#![allow(clippy::wildcard_imports, clippy::enum_glob_use)]
#![allow(
clippy::cast_precision_loss,
clippy::cast_possible_truncation,
clippy::cast_sign_loss
)]
use std::fmt;
use crate::checker::Checker;
use crate::macros::implement_metric_trait;
use crate::*;
#[derive(Debug, Clone, PartialEq)]
#[non_exhaustive]
pub struct Stats {
fn_nargs: usize,
closure_nargs: usize,
fn_nargs_sum: usize,
closure_nargs_sum: usize,
fn_nargs_min: usize,
closure_nargs_min: usize,
fn_nargs_max: usize,
closure_nargs_max: usize,
total_functions: usize,
total_closures: usize,
}
impl Default for Stats {
fn default() -> Self {
Self {
fn_nargs: 0,
closure_nargs: 0,
fn_nargs_sum: 0,
closure_nargs_sum: 0,
fn_nargs_min: usize::MAX,
closure_nargs_min: usize::MAX,
fn_nargs_max: 0,
closure_nargs_max: 0,
total_functions: 0,
total_closures: 0,
}
}
}
impl fmt::Display for Stats {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"function_args: {}, closure_args: {}, function_args_average: {}, closure_args_average: {}, total: {}, average: {}, function_args_min: {}, function_args_max: {}, closure_args_min: {}, closure_args_max: {}",
self.function_args_sum(),
self.closure_args_sum(),
self.function_args_average(),
self.closure_args_average(),
self.total(),
self.average(),
self.function_args_min(),
self.function_args_max(),
self.closure_args_min(),
self.closure_args_max()
)
}
}
impl Stats {
pub fn merge(&mut self, other: &Stats) {
self.closure_nargs_min = self.closure_nargs_min.min(other.closure_nargs_min);
self.closure_nargs_max = self.closure_nargs_max.max(other.closure_nargs_max);
self.fn_nargs_min = self.fn_nargs_min.min(other.fn_nargs_min);
self.fn_nargs_max = self.fn_nargs_max.max(other.fn_nargs_max);
self.fn_nargs_sum += other.fn_nargs_sum;
self.closure_nargs_sum += other.closure_nargs_sum;
}
#[inline]
#[must_use]
pub fn function_args(&self) -> u64 {
self.fn_nargs as u64
}
#[inline]
#[must_use]
pub fn closure_args(&self) -> u64 {
self.closure_nargs as u64
}
#[inline]
#[must_use]
pub fn function_args_sum(&self) -> u64 {
self.fn_nargs_sum as u64
}
#[inline]
#[must_use]
pub fn closure_args_sum(&self) -> u64 {
self.closure_nargs_sum as u64
}
#[inline]
#[must_use]
pub fn function_args_average(&self) -> f64 {
crate::metrics::average(self.fn_nargs_sum as f64, self.total_functions)
}
#[inline]
#[must_use]
pub fn closure_args_average(&self) -> f64 {
crate::metrics::average(self.closure_nargs_sum as f64, self.total_closures)
}
#[inline]
#[must_use]
pub fn total(&self) -> u64 {
self.function_args_sum() + self.closure_args_sum()
}
#[inline]
#[must_use]
pub fn average(&self) -> f64 {
crate::metrics::average(
self.total() as f64,
self.total_functions + self.total_closures,
)
}
#[inline]
#[must_use]
pub fn function_args_min(&self) -> u64 {
if self.fn_nargs_min == usize::MAX {
0
} else {
self.fn_nargs_min as u64
}
}
#[inline]
#[must_use]
pub fn function_args_max(&self) -> u64 {
self.fn_nargs_max as u64
}
#[inline]
#[must_use]
pub fn closure_args_min(&self) -> u64 {
if self.closure_nargs_min == usize::MAX {
0
} else {
self.closure_nargs_min as u64
}
}
#[inline]
#[must_use]
pub fn closure_args_max(&self) -> u64 {
self.closure_nargs_max as u64
}
#[inline]
pub(crate) fn compute_sum(&mut self) {
self.closure_nargs_sum += self.closure_nargs;
self.fn_nargs_sum += self.fn_nargs;
}
#[inline]
pub(crate) fn compute_minmax(&mut self) {
self.closure_nargs_min = self.closure_nargs_min.min(self.closure_nargs);
self.closure_nargs_max = self.closure_nargs_max.max(self.closure_nargs);
self.fn_nargs_min = self.fn_nargs_min.min(self.fn_nargs);
self.fn_nargs_max = self.fn_nargs_max.max(self.fn_nargs);
self.compute_sum();
}
pub(crate) fn finalize(&mut self, total_functions: usize, total_closures: usize) {
self.total_functions = total_functions;
self.total_closures = total_closures;
}
}
#[inline]
fn compute_args<T: Checker>(node: &Node, nargs: &mut usize) {
if let Some(params) = node.child_by_field_name("parameters") {
let node_params = params;
node_params.act_on_child(&mut |n| {
if !T::is_non_arg(n) {
*nargs += 1;
}
});
} else if node.child_by_field_name("parameter").is_some() {
*nargs += 1;
}
}
#[doc(hidden)]
pub(crate) trait NArgs
where
Self: Checker,
Self: std::marker::Sized,
{
fn compute(node: &Node, stats: &mut Stats) {
if Self::is_func(node) {
compute_args::<Self>(node, &mut stats.fn_nargs);
return;
}
if Self::is_closure(node) {
compute_args::<Self>(node, &mut stats.closure_nargs);
}
}
}
impl NArgs for CppCode {
fn compute(node: &Node, stats: &mut Stats) {
if Self::is_func(node) {
if let Some(declarator) = node.child_by_field_name("declarator") {
let new_node = declarator;
compute_args::<Self>(&new_node, &mut stats.fn_nargs);
}
return;
}
if Self::is_closure(node)
&& let Some(declarator) = node.child_by_field_name("declarator")
{
let new_node = declarator;
compute_args::<Self>(&new_node, &mut stats.closure_nargs);
}
}
}
impl NArgs for CCode {
fn compute(node: &Node, stats: &mut Stats) {
if Self::is_func(node) {
if let Some(declarator) = node.child_by_field_name("declarator") {
let new_node = declarator;
compute_args::<Self>(&new_node, &mut stats.fn_nargs);
}
return;
}
if Self::is_closure(node)
&& let Some(declarator) = node.child_by_field_name("declarator")
{
let new_node = declarator;
compute_args::<Self>(&new_node, &mut stats.closure_nargs);
}
}
}
impl NArgs for MozcppCode {
fn compute(node: &Node, stats: &mut Stats) {
if Self::is_func(node) {
if let Some(declarator) = node.child_by_field_name("declarator") {
let new_node = declarator;
compute_args::<Self>(&new_node, &mut stats.fn_nargs);
}
return;
}
if Self::is_closure(node)
&& let Some(declarator) = node.child_by_field_name("declarator")
{
let new_node = declarator;
compute_args::<Self>(&new_node, &mut stats.closure_nargs);
}
}
}
impl NArgs for ObjcCode {
fn compute(node: &Node, stats: &mut Stats) {
match node.kind_id().into() {
Objc::FunctionDefinition | Objc::FunctionDefinition2 => {
if let Some(declarator) = node.child_by_field_name("declarator") {
compute_args::<Self>(&declarator, &mut stats.fn_nargs);
}
}
Objc::MethodDefinition => {
node.act_on_child(&mut |n| {
if matches!(
n.kind_id().into(),
Objc::MethodParameter | Objc::MethodParameter2
) {
stats.fn_nargs += 1;
}
});
}
Objc::BlockLiteral => {
if let Some(params) = node.first_child(|id| Objc::ParameterList == id) {
params.act_on_child(&mut |n| {
if matches!(
n.kind_id().into(),
Objc::ParameterDeclaration | Objc::VariadicParameter
) {
stats.closure_nargs += 1;
}
});
}
}
_ => {}
}
}
}
fn compute_go_args(node: &Node, nargs: &mut usize) {
let Some(params) = node.child_by_field_name("parameters") else {
return;
};
*nargs += params
.children()
.map(|child| match child.kind_id().into() {
Go::ParameterDeclaration => child
.children()
.filter(|c| c.kind_id() == Go::Identifier)
.count()
.max(1),
Go::VariadicParameterDeclaration => 1,
_ => 0,
})
.sum::<usize>();
}
impl NArgs for GoCode {
fn compute(node: &Node, stats: &mut Stats) {
if Self::is_func(node) {
compute_go_args(node, &mut stats.fn_nargs);
return;
}
if Self::is_closure(node) {
compute_go_args(node, &mut stats.closure_nargs);
}
}
}
fn compute_kotlin_func_args(node: &Node, nargs: &mut usize) {
if let Some(params) = node
.children()
.find(|c| c.kind_id() == Kotlin::FunctionValueParameters)
{
params.act_on_child(&mut |n| {
if n.kind_id() == Kotlin::Parameter {
*nargs += 1;
}
});
}
}
fn compute_kotlin_lambda_args(node: &Node, nargs: &mut usize) {
if let Some(params) = node
.children()
.find(|c| c.kind_id() == Kotlin::LambdaParameters)
{
params.act_on_child(&mut |n| {
if n.kind_id() != Kotlin::COMMA {
*nargs += 1;
}
});
}
}
impl NArgs for KotlinCode {
fn compute(node: &Node, stats: &mut Stats) {
if Self::is_func(node) {
compute_kotlin_func_args(node, &mut stats.fn_nargs);
return;
}
if Self::is_closure(node) {
if node.kind_id() == Kotlin::LambdaLiteral {
compute_kotlin_lambda_args(node, &mut stats.closure_nargs);
} else {
compute_kotlin_func_args(node, &mut stats.closure_nargs);
}
}
}
}
fn compute_lua_args(node: &Node, nargs: &mut usize) {
let Some(params) = node.child_by_field_name("parameters") else {
return;
};
*nargs += params
.children()
.filter(|c| matches!(c.kind_id().into(), Lua::Identifier | Lua::VarargExpression))
.count();
}
impl NArgs for LuaCode {
fn compute(node: &Node, stats: &mut Stats) {
if Self::is_func(node) {
compute_lua_args(node, &mut stats.fn_nargs);
} else if Self::is_closure(node) {
compute_lua_args(node, &mut stats.closure_nargs);
}
}
}
fn compute_tcl_args(node: &Node, nargs: &mut usize) {
let Some(params) = node.child_by_field_name("arguments") else {
return;
};
*nargs += params
.children()
.filter(|c| c.kind_id() == Tcl::Argument)
.count();
}
impl NArgs for TclCode {
fn compute(node: &Node, stats: &mut Stats) {
if Self::is_func(node) {
compute_tcl_args(node, &mut stats.fn_nargs);
}
}
}
fn compute_irules_args(node: &Node, nargs: &mut usize) {
let Some(params) = node.child_by_field_name("arguments") else {
return;
};
*nargs += params
.children()
.filter(|c| c.kind_id() == Irules::Argument)
.count();
}
impl NArgs for IrulesCode {
fn compute(node: &Node, stats: &mut Stats) {
if Self::is_func(node) {
compute_irules_args(node, &mut stats.fn_nargs);
}
}
}
implement_metric_trait!(
[NArgs],
PythonCode,
MozjsCode,
JavascriptCode,
TypescriptCode,
TsxCode,
RustCode,
PreprocCode,
CcommentCode,
JavaCode,
PerlCode,
BashCode,
PhpCode,
CsharpCode,
ElixirCode,
RubyCode
);
impl NArgs for GroovyCode {
fn compute(node: &Node, stats: &mut Stats) {
use crate::languages::language_groovy::Groovy;
if Self::is_func(node) {
compute_args::<Self>(node, &mut stats.fn_nargs);
return;
}
if Self::is_closure(node)
&& let Some(params) = node.first_child(|id| id == Groovy::ClosureParameters)
{
params.act_on_child(&mut |n| {
if n.kind_id() == Groovy::ClosureParameter {
stats.closure_nargs += 1;
}
});
}
}
}
#[cfg(test)]
#[allow(
clippy::float_cmp,
clippy::cast_precision_loss,
clippy::cast_possible_truncation,
clippy::cast_sign_loss,
clippy::similar_names,
clippy::doc_markdown,
clippy::needless_raw_string_hashes,
clippy::too_many_lines
)]
mod tests {
use crate::tools::check_metrics;
use super::*;
#[test]
fn nargs_empty_file_min_is_zero() {
let stats = Stats::default();
assert_eq!(stats.function_args_min(), 0);
assert_eq!(stats.closure_args_min(), 0);
}
#[test]
fn python_no_functions_and_closures() {
check_metrics::<PythonParser>("a = 42", "foo.py", |metric| {
insta::assert_json_snapshot!(
metric.nargs,
@r#"
{
"function_args": 0,
"closure_args": 0,
"function_args_average": 0.0,
"closure_args_average": 0.0,
"total": 0,
"average": 0.0,
"function_args_min": 0,
"function_args_max": 0,
"closure_args_min": 0,
"closure_args_max": 0
}
"#
);
});
}
#[test]
fn rust_no_functions_and_closures() {
check_metrics::<RustParser>("let a = 42;", "foo.rs", |metric| {
insta::assert_json_snapshot!(
metric.nargs,
@r#"
{
"function_args": 0,
"closure_args": 0,
"function_args_average": 0.0,
"closure_args_average": 0.0,
"total": 0,
"average": 0.0,
"function_args_min": 0,
"function_args_max": 0,
"closure_args_min": 0,
"closure_args_max": 0
}
"#
);
});
}
#[test]
fn cpp_no_functions_and_closures() {
check_metrics::<CppParser>("int a = 42;", "foo.cpp", |metric| {
insta::assert_json_snapshot!(
metric.nargs,
@r#"
{
"function_args": 0,
"closure_args": 0,
"function_args_average": 0.0,
"closure_args_average": 0.0,
"total": 0,
"average": 0.0,
"function_args_min": 0,
"function_args_max": 0,
"closure_args_min": 0,
"closure_args_max": 0
}
"#
);
});
}
#[test]
fn javascript_no_functions_and_closures() {
check_metrics::<JavascriptParser>("var a = 42;", "foo.js", |metric| {
insta::assert_json_snapshot!(
metric.nargs,
@r#"
{
"function_args": 0,
"closure_args": 0,
"function_args_average": 0.0,
"closure_args_average": 0.0,
"total": 0,
"average": 0.0,
"function_args_min": 0,
"function_args_max": 0,
"closure_args_min": 0,
"closure_args_max": 0
}
"#
);
});
}
#[test]
fn python_single_function() {
check_metrics::<PythonParser>(
"def f(a, b):
if a:
return a",
"foo.py",
|metric| {
insta::assert_json_snapshot!(
metric.nargs,
@r#"
{
"function_args": 2,
"closure_args": 0,
"function_args_average": 2.0,
"closure_args_average": 0.0,
"total": 2,
"average": 2.0,
"function_args_min": 0,
"function_args_max": 2,
"closure_args_min": 0,
"closure_args_max": 0
}
"#
);
},
);
}
#[test]
fn rust_single_function() {
check_metrics::<RustParser>(
"fn f(a: bool, b: usize) {
if a {
return a;
}
}",
"foo.rs",
|metric| {
insta::assert_json_snapshot!(
metric.nargs,
@r#"
{
"function_args": 2,
"closure_args": 0,
"function_args_average": 2.0,
"closure_args_average": 0.0,
"total": 2,
"average": 2.0,
"function_args_min": 0,
"function_args_max": 2,
"closure_args_min": 0,
"closure_args_max": 0
}
"#
);
},
);
}
#[test]
fn c_single_function() {
check_metrics::<CParser>(
"int f(int a, int b) {
if (a) {
return a;
}
}",
"foo.c",
|metric| {
insta::assert_json_snapshot!(
metric.nargs,
@r#"
{
"function_args": 2,
"closure_args": 0,
"function_args_average": 2.0,
"closure_args_average": 0.0,
"total": 2,
"average": 2.0,
"function_args_min": 0,
"function_args_max": 2,
"closure_args_min": 0,
"closure_args_max": 0
}
"#
);
},
);
}
#[test]
fn javascript_single_function() {
check_metrics::<JavascriptParser>(
"function f(a, b) {
return a * b;
}",
"foo.js",
|metric| {
insta::assert_json_snapshot!(
metric.nargs,
@r#"
{
"function_args": 2,
"closure_args": 0,
"function_args_average": 2.0,
"closure_args_average": 0.0,
"total": 2,
"average": 2.0,
"function_args_min": 0,
"function_args_max": 2,
"closure_args_min": 0,
"closure_args_max": 0
}
"#
);
},
);
}
#[test]
fn python_single_lambda() {
check_metrics::<PythonParser>("bar = lambda a: True", "foo.py", |metric| {
insta::assert_json_snapshot!(
metric.nargs,
@r#"
{
"function_args": 0,
"closure_args": 1,
"function_args_average": 0.0,
"closure_args_average": 1.0,
"total": 1,
"average": 1.0,
"function_args_min": 0,
"function_args_max": 0,
"closure_args_min": 1,
"closure_args_max": 1
}
"#
);
});
}
#[test]
fn rust_single_closure() {
check_metrics::<RustParser>("let bar = |i: i32| -> i32 { i + 1 };", "foo.rs", |metric| {
insta::assert_json_snapshot!(
metric.nargs,
@r#"
{
"function_args": 0,
"closure_args": 1,
"function_args_average": 0.0,
"closure_args_average": 1.0,
"total": 1,
"average": 1.0,
"function_args_min": 0,
"function_args_max": 0,
"closure_args_min": 0,
"closure_args_max": 1
}
"#
);
});
}
#[test]
fn cpp_single_lambda() {
check_metrics::<CppParser>(
"auto bar = [](int x, int y) -> int { return x + y; };",
"foo.cpp",
|metric| {
insta::assert_json_snapshot!(
metric.nargs,
@r#"
{
"function_args": 0,
"closure_args": 2,
"function_args_average": 0.0,
"closure_args_average": 2.0,
"total": 2,
"average": 2.0,
"function_args_min": 0,
"function_args_max": 0,
"closure_args_min": 2,
"closure_args_max": 2
}
"#
);
},
);
}
#[test]
fn javascript_single_closure() {
check_metrics::<JavascriptParser>("function (a, b) {return a + b};", "foo.js", |metric| {
insta::assert_json_snapshot!(
metric.nargs,
@r#"
{
"function_args": 0,
"closure_args": 2,
"function_args_average": 0.0,
"closure_args_average": 2.0,
"total": 2,
"average": 2.0,
"function_args_min": 0,
"function_args_max": 0,
"closure_args_min": 0,
"closure_args_max": 2
}
"#
);
});
}
#[test]
fn python_functions() {
check_metrics::<PythonParser>(
"def f(a, b):
if a:
return a
def f(a, b):
if b:
return b",
"foo.py",
|metric| {
insta::assert_json_snapshot!(
metric.nargs,
@r#"
{
"function_args": 4,
"closure_args": 0,
"function_args_average": 2.0,
"closure_args_average": 0.0,
"total": 4,
"average": 2.0,
"function_args_min": 0,
"function_args_max": 2,
"closure_args_min": 0,
"closure_args_max": 0
}
"#
);
},
);
check_metrics::<PythonParser>(
"def f(a, b):
if a:
return a
def f(a, b, c):
if b:
return b",
"foo.py",
|metric| {
insta::assert_json_snapshot!(
metric.nargs,
@r#"
{
"function_args": 5,
"closure_args": 0,
"function_args_average": 2.5,
"closure_args_average": 0.0,
"total": 5,
"average": 2.5,
"function_args_min": 0,
"function_args_max": 3,
"closure_args_min": 0,
"closure_args_max": 0
}
"#
);
},
);
}
#[test]
fn rust_functions() {
check_metrics::<RustParser>(
"fn f(a: bool, b: usize) {
if a {
return a;
}
}
fn f1(a: bool, b: usize) {
if a {
return a;
}
}",
"foo.rs",
|metric| {
insta::assert_json_snapshot!(
metric.nargs,
@r#"
{
"function_args": 4,
"closure_args": 0,
"function_args_average": 2.0,
"closure_args_average": 0.0,
"total": 4,
"average": 2.0,
"function_args_min": 0,
"function_args_max": 2,
"closure_args_min": 0,
"closure_args_max": 0
}
"#
);
},
);
check_metrics::<RustParser>(
"fn f(a: bool, b: usize) {
if a {
return a;
}
}
fn f1(a: bool, b: usize, c: usize) {
if a {
return a;
}
}",
"foo.rs",
|metric| {
insta::assert_json_snapshot!(
metric.nargs,
@r#"
{
"function_args": 5,
"closure_args": 0,
"function_args_average": 2.5,
"closure_args_average": 0.0,
"total": 5,
"average": 2.5,
"function_args_min": 0,
"function_args_max": 3,
"closure_args_min": 0,
"closure_args_max": 0
}
"#
);
},
);
}
#[test]
fn rust_method_excludes_self_receiver() {
check_metrics::<RustParser>(
"struct S;
impl S {
fn a(self) {} // self -> 0 args
fn b(&self, x: i32) {} // &self + 1 -> 1 arg
fn c(&mut self, x: i32, y: i32) {} // &mut self + 2 -> 2 args
}",
"foo.rs",
|metric| {
let s = &metric.nargs;
assert_eq!(s.function_args_sum(), 3);
assert_eq!(s.function_args_max(), 2);
},
);
check_metrics::<RustParser>(
"use std::rc::Rc;
use std::pin::Pin;
struct S;
impl S {
fn a(self: Box<Self>, x: i32, y: i32) {} // receiver + 2 -> 2
fn b(self: Rc<Self>, x: i32) {} // receiver + 1 -> 1
fn c(self: Pin<&mut Self>) {} // receiver -> 0
}",
"foo.rs",
|metric| {
let s = &metric.nargs;
assert_eq!(s.function_args_sum(), 3);
assert_eq!(s.function_args_max(), 2);
},
);
}
#[test]
fn c_functions() {
check_metrics::<CParser>(
"int f(int a, int b) {
if (a) {
return a;
}
}
int f1(int a, int b) {
if (a) {
return a;
}
}",
"foo.c",
|metric| {
insta::assert_json_snapshot!(
metric.nargs,
@r#"
{
"function_args": 4,
"closure_args": 0,
"function_args_average": 2.0,
"closure_args_average": 0.0,
"total": 4,
"average": 2.0,
"function_args_min": 0,
"function_args_max": 2,
"closure_args_min": 0,
"closure_args_max": 0
}
"#
);
},
);
check_metrics::<CppParser>(
"int f(int a, int b) {
if (a) {
return a;
}
}
int f1(int a, int b, int c) {
if (a) {
return a;
}
}",
"foo.c",
|metric| {
insta::assert_json_snapshot!(
metric.nargs,
@r#"
{
"function_args": 5,
"closure_args": 0,
"function_args_average": 2.5,
"closure_args_average": 0.0,
"total": 5,
"average": 2.5,
"function_args_min": 0,
"function_args_max": 3,
"closure_args_min": 0,
"closure_args_max": 0
}
"#
);
},
);
}
#[test]
fn javascript_functions() {
check_metrics::<JavascriptParser>(
"function f(a, b) {
return a * b;
}
function f1(a, b) {
return a * b;
}",
"foo.js",
|metric| {
insta::assert_json_snapshot!(
metric.nargs,
@r#"
{
"function_args": 4,
"closure_args": 0,
"function_args_average": 2.0,
"closure_args_average": 0.0,
"total": 4,
"average": 2.0,
"function_args_min": 0,
"function_args_max": 2,
"closure_args_min": 0,
"closure_args_max": 0
}
"#
);
},
);
check_metrics::<JavascriptParser>(
"function f(a, b) {
return a * b;
}
function f1(a, b, c) {
return a * b;
}",
"foo.js",
|metric| {
insta::assert_json_snapshot!(
metric.nargs,
@r#"
{
"function_args": 5,
"closure_args": 0,
"function_args_average": 2.5,
"closure_args_average": 0.0,
"total": 5,
"average": 2.5,
"function_args_min": 0,
"function_args_max": 3,
"closure_args_min": 0,
"closure_args_max": 0
}
"#
);
},
);
}
#[test]
fn python_nested_functions() {
check_metrics::<PythonParser>(
"def f(a, b):
def foo(a):
if a:
return 1
bar = lambda a: lambda b: b or True or True
return bar(foo(a))(a)",
"foo.py",
|metric| {
insta::assert_json_snapshot!(
metric.nargs,
@r#"
{
"function_args": 3,
"closure_args": 2,
"function_args_average": 1.5,
"closure_args_average": 1.0,
"total": 5,
"average": 1.25,
"function_args_min": 0,
"function_args_max": 2,
"closure_args_min": 0,
"closure_args_max": 2
}
"#
);
},
);
}
#[test]
fn rust_nested_functions() {
check_metrics::<RustParser>(
"fn f(a: i32, b: i32) -> i32 {
fn foo(a: i32) -> i32 {
return a;
}
let bar = |a: i32, b: i32| -> i32 { a + 1 };
let bar1 = |b: i32| -> i32 { b + 1 };
return bar(foo(a), a);
}",
"foo.rs",
|metric| {
insta::assert_json_snapshot!(
metric.nargs,
@r#"
{
"function_args": 3,
"closure_args": 3,
"function_args_average": 1.5,
"closure_args_average": 1.5,
"total": 6,
"average": 1.5,
"function_args_min": 0,
"function_args_max": 2,
"closure_args_min": 0,
"closure_args_max": 2
}
"#
);
},
);
}
#[test]
fn cpp_nested_functions() {
check_metrics::<CppParser>(
"int f(int a, int b, int c) {
auto foo = [](int x) -> int { return x; };
auto bar = [](int x, int y) -> int { return x + y; };
return bar(foo(a), a);
}",
"foo.cpp",
|metric| {
insta::assert_json_snapshot!(
metric.nargs,
@r#"
{
"function_args": 3,
"closure_args": 3,
"function_args_average": 3.0,
"closure_args_average": 1.5,
"total": 6,
"average": 2.0,
"function_args_min": 0,
"function_args_max": 3,
"closure_args_min": 0,
"closure_args_max": 3
}
"#
);
},
);
}
#[test]
fn cpp_default_arguments() {
check_metrics::<CppParser>(
"int f(int a, int b, int c = 0) {
return a + b + c;
}",
"foo.cpp",
|metric| {
let s = &metric.nargs;
assert_eq!(s.function_args_sum(), 3);
assert_eq!(s.function_args_max(), 3);
insta::assert_json_snapshot!(
metric.nargs,
@r#"
{
"function_args": 3,
"closure_args": 0,
"function_args_average": 3.0,
"closure_args_average": 0.0,
"total": 3,
"average": 3.0,
"function_args_min": 0,
"function_args_max": 3,
"closure_args_min": 0,
"closure_args_max": 0
}
"#
);
},
);
}
#[test]
fn c_variadic_function() {
check_metrics::<CParser>(
"int printf(const char* fmt, ...) {
return 0;
}",
"foo.c",
|metric| {
let s = &metric.nargs;
assert_eq!(s.function_args_sum(), 2);
assert_eq!(s.function_args_max(), 2);
insta::assert_json_snapshot!(
metric.nargs,
@r#"
{
"function_args": 2,
"closure_args": 0,
"function_args_average": 2.0,
"closure_args_average": 0.0,
"total": 2,
"average": 2.0,
"function_args_min": 0,
"function_args_max": 2,
"closure_args_min": 0,
"closure_args_max": 0
}
"#
);
},
);
}
#[test]
fn cpp_template_parameter_pack() {
check_metrics::<CppParser>(
"template<typename... Args>
int sum(int seed, Args... args) {
return seed;
}",
"foo.cpp",
|metric| {
let s = &metric.nargs;
assert_eq!(s.function_args_sum(), 2);
assert_eq!(s.function_args_max(), 2);
insta::assert_json_snapshot!(
metric.nargs,
@r#"
{
"function_args": 2,
"closure_args": 0,
"function_args_average": 2.0,
"closure_args_average": 0.0,
"total": 2,
"average": 2.0,
"function_args_min": 0,
"function_args_max": 2,
"closure_args_min": 0,
"closure_args_max": 0
}
"#
);
},
);
}
#[test]
fn cpp_lambda_capture_not_counted() {
check_metrics::<CppParser>(
"int f() {
int x = 1;
int y = 2;
auto g = [=, &x](int a, int b) -> int { return a + b + x + y; };
return g(1, 2);
}",
"foo.cpp",
|metric| {
let s = &metric.nargs;
assert_eq!(s.function_args_sum(), 0);
assert_eq!(s.closure_args_sum(), 2);
assert_eq!(s.closure_args_max(), 2);
insta::assert_json_snapshot!(
metric.nargs,
@r#"
{
"function_args": 0,
"closure_args": 2,
"function_args_average": 0.0,
"closure_args_average": 2.0,
"total": 2,
"average": 1.0,
"function_args_min": 0,
"function_args_max": 0,
"closure_args_min": 0,
"closure_args_max": 2
}
"#
);
},
);
}
#[test]
fn cpp_member_function_this_not_counted() {
check_metrics::<CppParser>(
"struct S {
int x;
int set(int a) { // implicit `this` is NOT counted
this->x = a;
return a;
}
};",
"foo.cpp",
|metric| {
let s = &metric.nargs;
assert_eq!(s.function_args_sum(), 1);
assert_eq!(s.function_args_max(), 1);
insta::assert_json_snapshot!(
metric.nargs,
@r#"
{
"function_args": 1,
"closure_args": 0,
"function_args_average": 1.0,
"closure_args_average": 0.0,
"total": 1,
"average": 1.0,
"function_args_min": 0,
"function_args_max": 1,
"closure_args_min": 0,
"closure_args_max": 0
}
"#
);
},
);
}
#[test]
fn go_zero_args() {
check_metrics::<GoParser>(
"package main
func f() {}",
"foo.go",
|metric| {
insta::assert_json_snapshot!(
metric.nargs,
@r#"
{
"function_args": 0,
"closure_args": 0,
"function_args_average": 0.0,
"closure_args_average": 0.0,
"total": 0,
"average": 0.0,
"function_args_min": 0,
"function_args_max": 0,
"closure_args_min": 0,
"closure_args_max": 0
}
"#
);
},
);
}
#[test]
fn go_multiple_args() {
check_metrics::<GoParser>(
"package main
func f(a int, b string, c bool) {}",
"foo.go",
|metric| {
insta::assert_json_snapshot!(
metric.nargs,
@r#"
{
"function_args": 3,
"closure_args": 0,
"function_args_average": 3.0,
"closure_args_average": 0.0,
"total": 3,
"average": 3.0,
"function_args_min": 0,
"function_args_max": 3,
"closure_args_min": 0,
"closure_args_max": 0
}
"#
);
},
);
}
#[test]
fn go_method_excludes_receiver() {
check_metrics::<GoParser>(
"package main
type T struct{}
func (t *T) Greet(name string) string {
return name
}",
"foo.go",
|metric| {
insta::assert_json_snapshot!(
metric.nargs,
@r#"
{
"function_args": 1,
"closure_args": 0,
"function_args_average": 1.0,
"closure_args_average": 0.0,
"total": 1,
"average": 1.0,
"function_args_min": 0,
"function_args_max": 1,
"closure_args_min": 0,
"closure_args_max": 0
}
"#
);
},
);
}
#[test]
fn go_variadic() {
check_metrics::<GoParser>(
"package main
func f(args ...int) {}",
"foo.go",
|metric| {
insta::assert_json_snapshot!(
metric.nargs,
@r#"
{
"function_args": 1,
"closure_args": 0,
"function_args_average": 1.0,
"closure_args_average": 0.0,
"total": 1,
"average": 1.0,
"function_args_min": 0,
"function_args_max": 1,
"closure_args_min": 0,
"closure_args_max": 0
}
"#
);
},
);
}
#[test]
fn go_grouped_params() {
check_metrics::<GoParser>(
"package main
func f(a, b int, c string) {}",
"foo.go",
|metric| {
insta::assert_json_snapshot!(
metric.nargs,
@r#"
{
"function_args": 3,
"closure_args": 0,
"function_args_average": 3.0,
"closure_args_average": 0.0,
"total": 3,
"average": 3.0,
"function_args_min": 0,
"function_args_max": 3,
"closure_args_min": 0,
"closure_args_max": 0
}
"#
);
},
);
}
#[test]
fn go_func_literal_args() {
check_metrics::<GoParser>(
"package main
var f = func(x, y int) int { return x + y }",
"foo.go",
|metric| {
insta::assert_json_snapshot!(
metric.nargs,
@r#"
{
"function_args": 0,
"closure_args": 2,
"function_args_average": 0.0,
"closure_args_average": 2.0,
"total": 2,
"average": 2.0,
"function_args_min": 0,
"function_args_max": 0,
"closure_args_min": 0,
"closure_args_max": 2
}
"#
);
},
);
}
#[test]
fn javascript_nested_functions() {
check_metrics::<JavascriptParser>(
"function f(a, b) {
function foo(a, c) {
return a;
}
var bar = function (a, b) {return a + b};
function (a) {return a};
return bar(foo(a), a);
}",
"foo.js",
|metric| {
insta::assert_json_snapshot!(
metric.nargs,
@r#"
{
"function_args": 6,
"closure_args": 1,
"function_args_average": 2.0,
"closure_args_average": 1.0,
"total": 7,
"average": 1.75,
"function_args_min": 0,
"function_args_max": 2,
"closure_args_min": 0,
"closure_args_max": 1
}
"#
);
},
);
}
#[test]
fn perl_no_functions_and_closures() {
check_metrics::<PerlParser>(
"my $x = 1;
print $x;",
"foo.pl",
|metric| {
assert_eq!(metric.nom.functions_sum(), 0);
assert_eq!(metric.nom.closures_sum(), 0);
insta::assert_json_snapshot!(
metric.nargs,
@r#"
{
"function_args": 0,
"closure_args": 0,
"function_args_average": 0.0,
"closure_args_average": 0.0,
"total": 0,
"average": 0.0,
"function_args_min": 0,
"function_args_max": 0,
"closure_args_min": 0,
"closure_args_max": 0
}
"#
);
},
);
}
#[test]
fn perl_single_function() {
check_metrics::<PerlParser>(
"sub greet {
my ($name) = @_;
print \"hi $name\";
}",
"foo.pl",
|metric| {
assert_eq!(metric.nom.functions_sum(), 1);
assert_eq!(metric.nom.closures_sum(), 0);
insta::assert_json_snapshot!(
metric.nargs,
@r#"
{
"function_args": 0,
"closure_args": 0,
"function_args_average": 0.0,
"closure_args_average": 0.0,
"total": 0,
"average": 0.0,
"function_args_min": 0,
"function_args_max": 0,
"closure_args_min": 0,
"closure_args_max": 0
}
"#
);
},
);
}
#[test]
fn perl_single_closure() {
check_metrics::<PerlParser>(
"my $f = sub {
my ($x) = @_;
return $x + 1;
};",
"foo.pl",
|metric| {
assert_eq!(metric.nom.functions_sum(), 0);
assert_eq!(metric.nom.closures_sum(), 1);
insta::assert_json_snapshot!(
metric.nargs,
@r#"
{
"function_args": 0,
"closure_args": 0,
"function_args_average": 0.0,
"closure_args_average": 0.0,
"total": 0,
"average": 0.0,
"function_args_min": 0,
"function_args_max": 0,
"closure_args_min": 0,
"closure_args_max": 0
}
"#
);
},
);
}
#[test]
fn perl_multiple_functions() {
check_metrics::<PerlParser>(
"sub a { return 1; }
sub b {
my ($x, $y) = @_;
return $x + $y;
}",
"foo.pl",
|metric| {
assert_eq!(metric.nom.functions_sum(), 2);
assert_eq!(metric.nom.closures_sum(), 0);
insta::assert_json_snapshot!(
metric.nargs,
@r#"
{
"function_args": 0,
"closure_args": 0,
"function_args_average": 0.0,
"closure_args_average": 0.0,
"total": 0,
"average": 0.0,
"function_args_min": 0,
"function_args_max": 0,
"closure_args_min": 0,
"closure_args_max": 0
}
"#
);
},
);
}
#[test]
fn perl_nested_closure() {
check_metrics::<PerlParser>(
"sub outer {
my $inner = sub { return 42; };
return $inner->();
}",
"foo.pl",
|metric| {
assert_eq!(metric.nom.functions_sum(), 1);
assert_eq!(metric.nom.closures_sum(), 1);
insta::assert_json_snapshot!(
metric.nargs,
@r#"
{
"function_args": 0,
"closure_args": 0,
"function_args_average": 0.0,
"closure_args_average": 0.0,
"total": 0,
"average": 0.0,
"function_args_min": 0,
"function_args_max": 0,
"closure_args_min": 0,
"closure_args_max": 0
}
"#
);
},
);
}
#[test]
fn java_no_functions() {
check_metrics::<JavaParser>(
"class Foo {
int x = 42;
String name = \"hello\";
}",
"foo.java",
|metric| {
insta::assert_json_snapshot!(
metric.nargs,
@r#"
{
"function_args": 0,
"closure_args": 0,
"function_args_average": 0.0,
"closure_args_average": 0.0,
"total": 0,
"average": 0.0,
"function_args_min": 0,
"function_args_max": 0,
"closure_args_min": 0,
"closure_args_max": 0
}
"#
);
},
);
}
#[test]
fn java_single_method() {
check_metrics::<JavaParser>(
"class Foo {
void greet(String name, int count) {
return;
}
}",
"foo.java",
|metric| {
insta::assert_json_snapshot!(
metric.nargs,
@r#"
{
"function_args": 2,
"closure_args": 0,
"function_args_average": 2.0,
"closure_args_average": 0.0,
"total": 2,
"average": 2.0,
"function_args_min": 0,
"function_args_max": 2,
"closure_args_min": 0,
"closure_args_max": 0
}
"#
);
},
);
}
#[test]
fn java_multiple_methods() {
check_metrics::<JavaParser>(
"class Foo {
void a(int x) {
return;
}
void b(int x, int y, int z) {
return;
}
}",
"foo.java",
|metric| {
insta::assert_json_snapshot!(
metric.nargs,
@r#"
{
"function_args": 4,
"closure_args": 0,
"function_args_average": 2.0,
"closure_args_average": 0.0,
"total": 4,
"average": 2.0,
"function_args_min": 0,
"function_args_max": 3,
"closure_args_min": 0,
"closure_args_max": 0
}
"#
);
},
);
}
#[test]
fn java_constructor_args() {
check_metrics::<JavaParser>(
"class Foo {
Foo(String name, int age) {
return;
}
}",
"foo.java",
|metric| {
insta::assert_json_snapshot!(
metric.nargs,
@r#"
{
"function_args": 2,
"closure_args": 0,
"function_args_average": 2.0,
"closure_args_average": 0.0,
"total": 2,
"average": 2.0,
"function_args_min": 0,
"function_args_max": 2,
"closure_args_min": 0,
"closure_args_max": 0
}
"#
);
},
);
}
#[test]
fn java_method_excludes_explicit_receiver() {
check_metrics::<JavaParser>(
"class S {
void m(S this, int a) {} // receiver + 1 -> 1 arg
void n(int a, int b) {} // control: 2 real params
void r(S this) {} // receiver only -> 0 args
}",
"foo.java",
|metric| {
let s = &metric.nargs;
assert_eq!(s.function_args_sum(), 3);
assert_eq!(s.function_args_max(), 2);
},
);
}
#[test]
fn java_lambda_args() {
check_metrics::<JavaParser>(
"class Foo {
void run() {
Runnable r = (int a, int b) -> a + b;
}
}",
"foo.java",
|metric| {
insta::assert_json_snapshot!(
metric.nargs,
@r#"
{
"function_args": 0,
"closure_args": 2,
"function_args_average": 0.0,
"closure_args_average": 2.0,
"total": 2,
"average": 1.0,
"function_args_min": 0,
"function_args_max": 0,
"closure_args_min": 0,
"closure_args_max": 2
}
"#
);
},
);
}
#[test]
fn groovy_no_functions_and_closures() {
check_metrics::<GroovyParser>("int x = 1", "foo.groovy", |metric| {
assert_eq!(metric.nargs.total(), 0);
});
}
#[test]
fn groovy_single_method() {
check_metrics::<GroovyParser>(
"class A {
void greet(String name, int times) {
println(name)
}
}",
"foo.groovy",
|metric| {
assert_eq!(metric.nargs.function_args_sum(), 2);
assert_eq!(metric.nargs.closure_args_sum(), 0);
},
);
}
#[test]
fn groovy_multiple_methods() {
check_metrics::<GroovyParser>(
"class A {
int add(int x, int y) { x + y }
int sub(int x, int y, int z) { x - y - z }
}",
"foo.groovy",
|metric| {
assert_eq!(metric.nargs.function_args_sum(), 5);
},
);
}
#[test]
fn groovy_lambda_args() {
check_metrics::<GroovyParser>(
"class Foo {
void run() {
def f = { int a, int b -> a + b }
}
}",
"foo.groovy",
|metric| {
assert_eq!(metric.nargs.closure_args_sum(), 2);
},
);
}
#[test]
fn groovy_implicit_it_not_counted() {
check_metrics::<GroovyParser>(
"class A {
void apply() {
[1, 2, 3].each { println(it) }
}
}",
"foo.groovy",
|metric| {
assert_eq!(metric.nargs.closure_args_sum(), 0);
},
);
}
#[test]
fn csharp_no_functions() {
check_metrics::<CsharpParser>(
"class Foo {
int x = 42;
string Name = \"hello\";
}",
"foo.cs",
|metric| {
insta::assert_json_snapshot!(
metric.nargs,
@r#"
{
"function_args": 0,
"closure_args": 0,
"function_args_average": 0.0,
"closure_args_average": 0.0,
"total": 0,
"average": 0.0,
"function_args_min": 0,
"function_args_max": 0,
"closure_args_min": 0,
"closure_args_max": 0
}
"#
);
},
);
}
#[test]
fn csharp_single_method() {
check_metrics::<CsharpParser>(
"class Foo {
void Greet(string name, int count) {
return;
}
}",
"foo.cs",
|metric| {
insta::assert_json_snapshot!(
metric.nargs,
@r#"
{
"function_args": 2,
"closure_args": 0,
"function_args_average": 2.0,
"closure_args_average": 0.0,
"total": 2,
"average": 2.0,
"function_args_min": 0,
"function_args_max": 2,
"closure_args_min": 0,
"closure_args_max": 0
}
"#
);
},
);
}
#[test]
fn csharp_multiple_methods() {
check_metrics::<CsharpParser>(
"class Foo {
void A(int x) {
return;
}
void B(int x, int y, int z) {
return;
}
}",
"foo.cs",
|metric| {
insta::assert_json_snapshot!(
metric.nargs,
@r#"
{
"function_args": 4,
"closure_args": 0,
"function_args_average": 2.0,
"closure_args_average": 0.0,
"total": 4,
"average": 2.0,
"function_args_min": 0,
"function_args_max": 3,
"closure_args_min": 0,
"closure_args_max": 0
}
"#
);
},
);
}
#[test]
fn csharp_constructor_args() {
check_metrics::<CsharpParser>(
"class Foo {
public Foo(string name, int age) {
return;
}
}",
"foo.cs",
|metric| {
insta::assert_json_snapshot!(
metric.nargs,
@r#"
{
"function_args": 2,
"closure_args": 0,
"function_args_average": 2.0,
"closure_args_average": 0.0,
"total": 2,
"average": 2.0,
"function_args_min": 0,
"function_args_max": 2,
"closure_args_min": 0,
"closure_args_max": 0
}
"#
);
},
);
}
#[test]
fn csharp_lambda_args() {
check_metrics::<CsharpParser>(
"class Foo {
void Run() {
System.Func<int, int, int> f = (int a, int b) => a + b;
}
}",
"foo.cs",
|metric| {
insta::assert_json_snapshot!(
metric.nargs,
@r#"
{
"function_args": 0,
"closure_args": 2,
"function_args_average": 0.0,
"closure_args_average": 2.0,
"total": 2,
"average": 1.0,
"function_args_min": 0,
"function_args_max": 0,
"closure_args_min": 0,
"closure_args_max": 2
}
"#
);
},
);
}
#[test]
fn tsx_function_and_arrow() {
check_metrics::<TsxParser>(
"function add(a: number, b: number): number {
return a + b;
}
const multiply = (x: number, y: number) => x * y;",
"foo.tsx",
|metric| {
insta::assert_json_snapshot!(
metric.nargs,
@r#"
{
"function_args": 4,
"closure_args": 0,
"function_args_average": 2.0,
"closure_args_average": 0.0,
"total": 4,
"average": 2.0,
"function_args_min": 0,
"function_args_max": 2,
"closure_args_min": 0,
"closure_args_max": 0
}
"#
);
},
);
}
#[test]
fn typescript_typed_and_optional_params() {
check_metrics::<TypescriptParser>(
"function format(value: number, prefix?: string, suffix?: string): string {
return (prefix ?? '') + value.toString() + (suffix ?? '');
}
const identity = (x: number): number => x;",
"foo.ts",
|metric| {
insta::assert_json_snapshot!(
metric.nargs,
@r#"
{
"function_args": 4,
"closure_args": 0,
"function_args_average": 2.0,
"closure_args_average": 0.0,
"total": 4,
"average": 2.0,
"function_args_min": 0,
"function_args_max": 3,
"closure_args_min": 0,
"closure_args_max": 0
}
"#
);
},
);
}
#[test]
fn mozjs_single_function() {
check_metrics::<MozjsParser>(
"function f(a, b) {
return a * b;
}",
"foo.js",
|metric| {
insta::assert_json_snapshot!(
metric.nargs,
@r#"
{
"function_args": 2,
"closure_args": 0,
"function_args_average": 2.0,
"closure_args_average": 0.0,
"total": 2,
"average": 2.0,
"function_args_min": 0,
"function_args_max": 2,
"closure_args_min": 0,
"closure_args_max": 0
}
"#
);
},
);
}
#[test]
fn mozjs_closure_args() {
check_metrics::<MozjsParser>("function (a, b) {return a + b};", "foo.js", |metric| {
insta::assert_json_snapshot!(
metric.nargs,
@r#"
{
"function_args": 0,
"closure_args": 2,
"function_args_average": 0.0,
"closure_args_average": 2.0,
"total": 2,
"average": 2.0,
"function_args_min": 0,
"function_args_max": 0,
"closure_args_min": 0,
"closure_args_max": 2
}
"#
);
});
}
#[test]
fn javascript_bare_arrow_function() {
check_metrics::<JavascriptParser>("const f = x => x;", "foo.js", |metric| {
assert_eq!(metric.nargs.total(), 1);
});
}
#[test]
fn javascript_async_bare_arrow_function() {
check_metrics::<JavascriptParser>("const f = async x => x;", "foo.js", |metric| {
assert_eq!(metric.nargs.total(), 1);
});
}
#[test]
fn javascript_parenthesized_arrow_function() {
check_metrics::<JavascriptParser>("const f = (x) => x;", "foo.js", |metric| {
assert_eq!(metric.nargs.total(), 1);
});
}
#[test]
fn javascript_multi_parenthesized_arrow_function() {
check_metrics::<JavascriptParser>("const f = (x, y) => x + y;", "foo.js", |metric| {
assert_eq!(metric.nargs.total(), 2);
});
}
#[test]
fn typescript_bare_arrow_function() {
check_metrics::<TypescriptParser>("const f = x => x;", "foo.ts", |metric| {
assert_eq!(metric.nargs.total(), 1);
});
}
#[test]
fn typescript_async_bare_arrow_function() {
check_metrics::<TypescriptParser>("const f = async x => x;", "foo.ts", |metric| {
assert_eq!(metric.nargs.total(), 1);
});
}
#[test]
fn typescript_parenthesized_arrow_function() {
check_metrics::<TypescriptParser>("const f = (x: number) => x;", "foo.ts", |metric| {
assert_eq!(metric.nargs.total(), 1);
});
}
#[test]
fn typescript_multi_parenthesized_arrow_function() {
check_metrics::<TypescriptParser>(
"const f = (x: number, y: number) => x + y;",
"foo.ts",
|metric| {
assert_eq!(metric.nargs.total(), 2);
},
);
}
#[test]
fn tsx_bare_arrow_function() {
check_metrics::<TsxParser>("const f = x => x;", "foo.tsx", |metric| {
assert_eq!(metric.nargs.total(), 1);
});
}
#[test]
fn tsx_async_bare_arrow_function() {
check_metrics::<TsxParser>("const f = async x => x;", "foo.tsx", |metric| {
assert_eq!(metric.nargs.total(), 1);
});
}
#[test]
fn tsx_parenthesized_arrow_function() {
check_metrics::<TsxParser>("const f = (x: number) => x;", "foo.tsx", |metric| {
assert_eq!(metric.nargs.total(), 1);
});
}
#[test]
fn tsx_multi_parenthesized_arrow_function() {
check_metrics::<TsxParser>(
"const f = (x: number, y: number) => x + y;",
"foo.tsx",
|metric| {
assert_eq!(metric.nargs.total(), 2);
},
);
}
#[test]
fn mozjs_bare_arrow_function() {
check_metrics::<MozjsParser>("const f = x => x;", "foo.js", |metric| {
assert_eq!(metric.nargs.total(), 1);
});
}
#[test]
fn mozjs_async_bare_arrow_function() {
check_metrics::<MozjsParser>("const f = async x => x;", "foo.js", |metric| {
assert_eq!(metric.nargs.total(), 1);
});
}
#[test]
fn mozjs_parenthesized_arrow_function() {
check_metrics::<MozjsParser>("const f = (x) => x;", "foo.js", |metric| {
assert_eq!(metric.nargs.total(), 1);
});
}
#[test]
fn mozjs_multi_parenthesized_arrow_function() {
check_metrics::<MozjsParser>("const f = (x, y) => x + y;", "foo.js", |metric| {
assert_eq!(metric.nargs.total(), 2);
});
}
#[test]
fn kotlin_nargs_functions_and_closures() {
check_metrics::<KotlinParser>(
"fun add(a: Int, b: Int): Int {
val transform = { x: Int, y: Int -> x + y }
return transform(a, b)
}",
"foo.kt",
|metric| {
insta::assert_json_snapshot!(
metric.nargs,
@r#"
{
"function_args": 2,
"closure_args": 2,
"function_args_average": 2.0,
"closure_args_average": 2.0,
"total": 4,
"average": 2.0,
"function_args_min": 0,
"function_args_max": 2,
"closure_args_min": 0,
"closure_args_max": 2
}
"#
);
},
);
}
#[test]
fn lua_no_functions_and_closures() {
check_metrics::<LuaParser>("local x = 1", "foo.lua", |metric| {
assert_eq!(metric.nargs.function_args_sum(), 0);
assert_eq!(metric.nargs.closure_args_sum(), 0);
insta::assert_json_snapshot!(metric.nargs);
});
}
#[test]
fn lua_single_function() {
check_metrics::<LuaParser>("function f(a, b) return a + b end", "foo.lua", |metric| {
assert_eq!(metric.nargs.function_args_sum(), 2);
assert_eq!(metric.nargs.closure_args_sum(), 0);
insta::assert_json_snapshot!(metric.nargs);
});
}
#[test]
fn lua_single_closure() {
check_metrics::<LuaParser>(
"local f = function(a, b) return a + b end",
"foo.lua",
|metric| {
assert_eq!(metric.nargs.function_args_sum(), 0);
assert_eq!(metric.nargs.closure_args_sum(), 2);
insta::assert_json_snapshot!(metric.nargs);
},
);
}
#[test]
fn lua_functions() {
check_metrics::<LuaParser>(
"function f(a) return a end
function g(x, y, z) return x + y + z end",
"foo.lua",
|metric| {
assert_eq!(metric.nargs.function_args_sum(), 4);
assert_eq!(metric.nargs.closure_args_sum(), 0);
insta::assert_json_snapshot!(metric.nargs);
},
);
}
#[test]
fn lua_vararg_function() {
check_metrics::<LuaParser>("function f(a, ...) return a end", "foo.lua", |metric| {
assert_eq!(metric.nargs.function_args_sum(), 2);
assert_eq!(metric.nargs.closure_args_sum(), 0);
insta::assert_json_snapshot!(metric.nargs);
});
}
#[test]
fn lua_colon_method_nargs() {
check_metrics::<LuaParser>(
"function obj:method(a, b) return a + b end",
"foo.lua",
|metric| {
assert_eq!(metric.nargs.function_args_sum(), 2);
assert_eq!(metric.nargs.closure_args_sum(), 0);
insta::assert_json_snapshot!(metric.nargs);
},
);
}
#[test]
fn tcl_no_functions() {
check_metrics::<TclParser>("set x 1", "foo.tcl", |metric| {
assert_eq!(metric.nargs.function_args_sum(), 0);
assert_eq!(metric.nargs.closure_args_sum(), 0);
insta::assert_json_snapshot!(metric.nargs);
});
}
#[test]
fn tcl_single_function() {
check_metrics::<TclParser>("proc f {a b} { puts $a }", "foo.tcl", |metric| {
assert_eq!(metric.nargs.function_args_sum(), 2);
assert_eq!(metric.nargs.closure_args_sum(), 0);
insta::assert_json_snapshot!(metric.nargs);
});
}
#[test]
fn tcl_single_function_no_args() {
check_metrics::<TclParser>("proc f {} { puts hello }", "foo.tcl", |metric| {
assert_eq!(metric.nargs.function_args_sum(), 0);
assert_eq!(metric.nargs.closure_args_sum(), 0);
insta::assert_json_snapshot!(metric.nargs);
});
}
#[test]
fn tcl_functions() {
check_metrics::<TclParser>(
"proc f {a b} { puts $a }
proc g {x y z} { puts $x }",
"foo.tcl",
|metric| {
assert_eq!(metric.nargs.function_args_sum(), 5);
assert_eq!(metric.nargs.closure_args_sum(), 0);
insta::assert_json_snapshot!(metric.nargs);
},
);
}
#[test]
fn tcl_nested_functions() {
check_metrics::<TclParser>(
"proc outer {a} {
proc inner {x y} { puts $x }
inner $a $a
}",
"foo.tcl",
|metric| {
assert_eq!(metric.nargs.function_args_sum(), 3);
assert_eq!(metric.nargs.closure_args_sum(), 0);
insta::assert_json_snapshot!(metric.nargs);
},
);
}
#[test]
fn tcl_args_vararg() {
check_metrics::<TclParser>("proc f {a b args} { puts $a }", "foo.tcl", |metric| {
assert_eq!(metric.nargs.function_args_sum(), 3);
assert_eq!(metric.nargs.closure_args_sum(), 0);
insta::assert_json_snapshot!(metric.nargs);
});
}
#[test]
fn tcl_default_arg() {
check_metrics::<TclParser>(
"proc greet {{name World} greeting} {
puts \"$greeting, $name!\"
}",
"foo.tcl",
|metric| {
assert_eq!(metric.nargs.function_args_sum(), 2);
assert_eq!(metric.nargs.closure_args_sum(), 0);
insta::assert_json_snapshot!(metric.nargs);
},
);
}
#[test]
fn kotlin_zero_args() {
check_metrics::<KotlinParser>("fun f(): Int { return 42 }", "foo.kt", |metric| {
assert_eq!(metric.nargs.function_args_sum(), 0);
assert_eq!(metric.nargs.closure_args_sum(), 0);
insta::assert_json_snapshot!(metric.nargs);
});
}
#[test]
fn kotlin_single_arg() {
check_metrics::<KotlinParser>(
"fun double(x: Int): Int { return x * 2 }",
"foo.kt",
|metric| {
assert_eq!(metric.nargs.function_args_sum(), 1);
assert_eq!(metric.nargs.closure_args_sum(), 0);
insta::assert_json_snapshot!(metric.nargs);
},
);
}
#[test]
fn kotlin_multiple_args() {
check_metrics::<KotlinParser>(
"fun add(a: Int, b: Int, c: Int): Int { return a + b + c }",
"foo.kt",
|metric| {
assert_eq!(metric.nargs.function_args_sum(), 3);
assert_eq!(metric.nargs.closure_args_sum(), 0);
insta::assert_json_snapshot!(metric.nargs);
},
);
}
#[test]
fn kotlin_default_args() {
check_metrics::<KotlinParser>(
"fun greet(name: String = \"World\", greeting: String = \"Hello\"): String {
return \"$greeting, $name!\"
}",
"foo.kt",
|metric| {
assert_eq!(metric.nargs.function_args_sum(), 2);
assert_eq!(metric.nargs.closure_args_sum(), 0);
insta::assert_json_snapshot!(metric.nargs);
},
);
}
#[test]
fn kotlin_empty_lambda() {
check_metrics::<KotlinParser>(
"fun f() {
val two = { x: Int, y: Int -> x + y }
val zero = { -> 42 }
}",
"foo.kt",
|metric| {
assert_eq!(metric.nargs.function_args_sum(), 0);
assert_eq!(metric.nargs.closure_args_sum(), 2);
insta::assert_json_snapshot!(metric.nargs);
},
);
}
#[test]
fn kotlin_anonymous_function() {
check_metrics::<KotlinParser>(
"val add = fun(x: Int, y: Int): Int = x + y",
"foo.kt",
|metric| {
assert_eq!(metric.nargs.function_args_sum(), 0);
assert_eq!(metric.nargs.closure_args_sum(), 2);
insta::assert_json_snapshot!(metric.nargs);
},
);
}
#[test]
fn php_no_functions_and_closures() {
check_metrics::<PhpParser>("<?php $a = 42;", "foo.php", |metric| {
insta::assert_json_snapshot!(
metric.nargs,
@r#"
{
"function_args": 0,
"closure_args": 0,
"function_args_average": 0.0,
"closure_args_average": 0.0,
"total": 0,
"average": 0.0,
"function_args_min": 0,
"function_args_max": 0,
"closure_args_min": 0,
"closure_args_max": 0
}
"#
);
});
}
#[test]
fn php_single_function() {
check_metrics::<PhpParser>(
"<?php
function f(bool $a, int $b): bool {
if ($a) { return $a; }
return false;
}",
"foo.php",
|metric| {
insta::assert_json_snapshot!(
metric.nargs,
@r#"
{
"function_args": 2,
"closure_args": 0,
"function_args_average": 2.0,
"closure_args_average": 0.0,
"total": 2,
"average": 2.0,
"function_args_min": 0,
"function_args_max": 2,
"closure_args_min": 0,
"closure_args_max": 0
}
"#
);
},
);
}
#[test]
fn php_single_closure() {
check_metrics::<PhpParser>(
"<?php
$f = function (int $a, int $b) { return $a + $b; };
$g = fn (int $x) => $x * 2;",
"foo.php",
|metric| {
insta::assert_json_snapshot!(
metric.nargs,
@r#"
{
"function_args": 0,
"closure_args": 3,
"function_args_average": 0.0,
"closure_args_average": 1.5,
"total": 3,
"average": 1.5,
"function_args_min": 0,
"function_args_max": 0,
"closure_args_min": 0,
"closure_args_max": 2
}
"#
);
},
);
}
#[test]
fn php_functions() {
check_metrics::<PhpParser>(
"<?php
function a(int $x): int { return $x; }
function b(int $x, int $y): int { return $x + $y; }",
"foo.php",
|metric| {
insta::assert_json_snapshot!(
metric.nargs,
@r#"
{
"function_args": 3,
"closure_args": 0,
"function_args_average": 1.5,
"closure_args_average": 0.0,
"total": 3,
"average": 1.5,
"function_args_min": 0,
"function_args_max": 2,
"closure_args_min": 0,
"closure_args_max": 0
}
"#
);
},
);
}
#[test]
fn php_nested_functions() {
check_metrics::<PhpParser>(
"<?php
class A {
public function outer(int $a): int {
$f = function (int $b) use ($a) { return $a + $b; };
return $f($a);
}
}",
"foo.php",
|metric| {
insta::assert_json_snapshot!(
metric.nargs,
@r#"
{
"function_args": 1,
"closure_args": 1,
"function_args_average": 1.0,
"closure_args_average": 1.0,
"total": 2,
"average": 1.0,
"function_args_min": 0,
"function_args_max": 1,
"closure_args_min": 0,
"closure_args_max": 1
}
"#
);
},
);
}
#[test]
fn elixir_default_nargs_is_zero() {
check_metrics::<ElixirParser>(
"defmodule Foo do\n def add(a, b), do: a + b\n def use_anon do\n add2 = fn x, y -> x + y end\n add2.(1, 2)\n end\nend\n",
"foo.ex",
|metric| {
assert_eq!(metric.nargs.function_args_sum(), 0);
assert_eq!(metric.nargs.closure_args_sum(), 0);
},
);
}
#[test]
fn ruby_no_functions_and_closures() {
check_metrics::<RubyParser>("a = 42\n", "foo.rb", |metric| {
assert_eq!(metric.nargs.function_args_sum(), 0);
assert_eq!(metric.nargs.closure_args_sum(), 0);
});
}
#[test]
fn ruby_single_function() {
check_metrics::<RubyParser>("def foo(a, b, c)\n a + b + c\nend\n", "foo.rb", |metric| {
assert_eq!(metric.nargs.function_args_sum(), 3);
assert_eq!(metric.nargs.closure_args_sum(), 0);
});
}
#[test]
fn ruby_single_closure() {
check_metrics::<RubyParser>("[1, 2, 3].each { |x| puts x }\n", "foo.rb", |metric| {
assert_eq!(metric.nargs.function_args_sum(), 0);
assert_eq!(metric.nargs.closure_args_sum(), 1);
});
}
#[test]
fn ruby_functions() {
check_metrics::<RubyParser>(
"def add(a, b)\n a + b\nend\n\ndef neg(x)\n -x\nend\n\nf = ->(a, b) { a * b }\n",
"foo.rb",
|metric| {
assert_eq!(metric.nargs.function_args_sum(), 3);
assert_eq!(metric.nargs.closure_args_sum(), 2);
},
);
}
#[test]
fn ruby_nested_functions() {
check_metrics::<RubyParser>(
"def outer(a)\n def inner(b, c)\n b + c\n end\n inner(a, a)\nend\n",
"foo.rb",
|metric| {
assert_eq!(metric.nargs.function_args_sum(), 3);
assert_eq!(metric.nargs.closure_args_sum(), 0);
},
);
}
#[test]
fn python_both_parameter_separators() {
check_metrics::<PythonParser>(
"def f(pos_only, /, normal, *, kw_only): pass",
"foo.py",
|metric| {
assert_eq!(metric.nargs.function_args_sum(), 3);
assert_eq!(metric.nargs.closure_args_sum(), 0);
},
);
}
#[test]
fn python_positional_separator_only() {
check_metrics::<PythonParser>("def f(a, b, /): pass", "foo.py", |metric| {
assert_eq!(metric.nargs.function_args_sum(), 2);
assert_eq!(metric.nargs.closure_args_sum(), 0);
});
}
#[test]
fn python_keyword_separator_only() {
check_metrics::<PythonParser>("def f(*, a, b): pass", "foo.py", |metric| {
assert_eq!(metric.nargs.function_args_sum(), 2);
assert_eq!(metric.nargs.closure_args_sum(), 0);
});
}
#[test]
fn python_lambda_keyword_separator() {
check_metrics::<PythonParser>("g = lambda a, *, b: a", "foo.py", |metric| {
assert_eq!(metric.nargs.function_args_sum(), 0);
assert_eq!(metric.nargs.closure_args_sum(), 2);
});
}
#[test]
fn python_args_kwargs_still_counted() {
check_metrics::<PythonParser>("def f(a, *args, **kwargs): pass", "foo.py", |metric| {
assert_eq!(metric.nargs.function_args_sum(), 3);
assert_eq!(metric.nargs.closure_args_sum(), 0);
});
}
#[test]
fn irules_no_functions_and_closures() {
check_metrics::<IrulesParser>("set x 1\nlog local0. $x\n", "foo.irule", |metric| {
assert_eq!(metric.nargs.function_args_sum(), 0);
assert_eq!(metric.nargs.closure_args_sum(), 0);
});
}
#[test]
fn irules_handler_zero_args() {
check_metrics::<IrulesParser>(
"when HTTP_REQUEST { log local0. \"hit\" }\n",
"foo.irule",
|metric| {
assert_eq!(metric.nargs.function_args_sum(), 0);
assert_eq!(metric.nargs.closure_args_sum(), 0);
assert_eq!(metric.nom.functions_sum(), 1);
},
);
}
#[test]
fn irules_single_proc() {
check_metrics::<IrulesParser>("proc f { a b } { return $a }\n", "foo.irule", |metric| {
assert_eq!(metric.nargs.function_args_sum(), 2);
assert_eq!(metric.nargs.closure_args_sum(), 0);
});
}
#[test]
fn irules_proc_no_args() {
check_metrics::<IrulesParser>("proc f { } { return 1 }\n", "foo.irule", |metric| {
assert_eq!(metric.nargs.function_args_sum(), 0);
});
}
#[test]
fn irules_proc_arg_defaults() {
check_metrics::<IrulesParser>(
"proc f { a {b 5} c } { return $a }\n",
"foo.irule",
|metric| {
assert_eq!(metric.nargs.function_args_sum(), 3);
},
);
}
#[test]
fn irules_multiple_functions() {
check_metrics::<IrulesParser>(
"proc add { a b } { return [expr { $a + $b }] }
when HTTP_REQUEST { log local0. \"hit\" }
",
"foo.irule",
|metric| {
assert_eq!(metric.nargs.function_args_sum(), 2);
assert_eq!(metric.nom.functions_sum(), 2);
},
);
}
#[test]
fn objc_no_args() {
check_metrics::<ObjcParser>(
"@implementation Foo
- (void)foo {
[self doWork];
}
@end
",
"foo.m",
|metric| {
assert_eq!(metric.nargs.total(), 0);
insta::assert_json_snapshot!(metric.nargs, @r#"
{
"function_args": 0,
"closure_args": 0,
"function_args_average": 0.0,
"closure_args_average": 0.0,
"total": 0,
"average": 0.0,
"function_args_min": 0,
"function_args_max": 0,
"closure_args_min": 0,
"closure_args_max": 0
}
"#);
},
);
}
#[test]
fn objc_method_two_args() {
check_metrics::<ObjcParser>(
"@implementation Foo
- (void)foo:(int)a bar:(int)b {
[self use:a];
}
@end
",
"foo.m",
|metric| {
assert_eq!(metric.nargs.function_args_sum(), 2);
insta::assert_json_snapshot!(metric.nargs, @r#"
{
"function_args": 2,
"closure_args": 0,
"function_args_average": 2.0,
"closure_args_average": 0.0,
"total": 2,
"average": 2.0,
"function_args_min": 0,
"function_args_max": 2,
"closure_args_min": 0,
"closure_args_max": 0
}
"#);
},
);
}
#[test]
fn objc_function_args() {
check_metrics::<ObjcParser>(
"void f(int a, int b, int c) {
return;
}
",
"foo.m",
|metric| {
assert_eq!(metric.nargs.function_args_sum(), 3);
insta::assert_json_snapshot!(metric.nargs, @r#"
{
"function_args": 3,
"closure_args": 0,
"function_args_average": 3.0,
"closure_args_average": 0.0,
"total": 3,
"average": 3.0,
"function_args_min": 0,
"function_args_max": 3,
"closure_args_min": 0,
"closure_args_max": 0
}
"#);
},
);
}
#[test]
fn objc_block_args() {
check_metrics::<ObjcParser>(
"@implementation Foo
- (void)bar {
void (^blk)(int, int) = ^(int x, int y){
[self use:x];
};
blk(1, 2);
}
@end
",
"foo.m",
|metric| {
assert_eq!(metric.nargs.closure_args_sum(), 2);
insta::assert_json_snapshot!(metric.nargs, @r#"
{
"function_args": 0,
"closure_args": 2,
"function_args_average": 0.0,
"closure_args_average": 2.0,
"total": 2,
"average": 1.0,
"function_args_min": 0,
"function_args_max": 0,
"closure_args_min": 0,
"closure_args_max": 2
}
"#);
},
);
}
#[test]
fn display_headline_matches_sum_for_nested_functions() {
check_metrics::<PythonParser>(
"def f(a, b):
def foo(a):
if a:
return 1
bar = lambda a: lambda b: b or True or True
return bar(foo(a))(a)",
"foo.py",
|metric| {
let stats = &metric.nargs;
assert_eq!(stats.function_args_sum(), 3);
assert_eq!(stats.closure_args_sum(), 2);
let rendered = stats.to_string();
assert!(
rendered.starts_with(&format!(
"function_args: {}, closure_args: {},",
stats.function_args_sum(),
stats.closure_args_sum()
)),
"Display headline diverged from the summed accessors: {rendered}"
);
},
);
}
}