From a9f275a1dede3bd1e91c94c28eebdcc622975369 Mon Sep 17 00:00:00 2001 From: arnaud-ma Date: Wed, 8 Jan 2025 21:49:05 +0100 Subject: [PATCH 1/6] add ignore keyword for specific methods in test_unbound_args --- src/unbound_args.jl | 22 ++++++++++++++++++++-- test/pkgs/PkgUnboundArgs.jl | 5 +++++ test/test_unbound_args.jl | 8 ++++++++ 3 files changed, 33 insertions(+), 2 deletions(-) diff --git a/src/unbound_args.jl b/src/unbound_args.jl index a6413f5e..a2c65265 100644 --- a/src/unbound_args.jl +++ b/src/unbound_args.jl @@ -1,17 +1,35 @@ """ test_unbound_args(module::Module) + test_unbound_args(unbounds) Test that all methods in `module` and its submodules do not have unbound type parameters. An unbound type parameter is a type parameter with a `where`, that does not occur in the signature of some dispatch -of the method. +of the method. If unbounds methods are already known, they can be +passed directly to the function instead of the module. # Keyword Arguments - `broken::Bool = false`: If true, it uses `@test_broken` instead of `@test` and shortens the error message. +- `ignore::Vector{Tuple{Function, DataType...}}`: A list of functions and their + signatures to ignore. The signatures are given as tuples, where the + first element is the function and the rest are the types of the + arguments. For example, to ignore `foo(x::Int, y::Float64)`, pass + `(foo, Int, Float64)`. """ -function test_unbound_args(m::Module; broken::Bool = false) +function test_unbound_args(m::Module; broken::Bool = false, ignore = ()) unbounds = detect_unbound_args_recursively(m) + for i in ignore + # i[2:end] is empty if length(i) == 1 + ignore_signature = Tuple{typeof(i[1]),i[2:end]...} + filter!(unbounds) do method + method.sig != ignore_signature + end + end + test_unbound_args(unbounds; broken = broken) +end + +function test_unbound_args(unbounds; broken::Bool = false) if broken if !isempty(unbounds) printstyled( diff --git a/test/pkgs/PkgUnboundArgs.jl b/test/pkgs/PkgUnboundArgs.jl index 4eb5e27b..ee597f0b 100644 --- a/test/pkgs/PkgUnboundArgs.jl +++ b/test/pkgs/PkgUnboundArgs.jl @@ -7,4 +7,9 @@ end # `_totuple` is taken from # https://github.com/JuliaLang/julia/blob/48634f9f8669e1dc1be0a1589cd5be880c04055a/test/ambiguous.jl#L257-L259 +# taken from https://github.com/JuliaTesting/Aqua.jl/issues/86 +module Issue86 +f(::NTuple{N,T}) where {N,T} = (N, T) +f(::Tuple{}) = (0, Any) +end end # module diff --git a/test/test_unbound_args.jl b/test/test_unbound_args.jl index 97206032..6cc113a2 100644 --- a/test/test_unbound_args.jl +++ b/test/test_unbound_args.jl @@ -13,6 +13,14 @@ using PkgUnboundArgs @test length(results) == 1 @test results[1] isa Test.Fail + Aqua.test_unbound_args( + PkgUnboundArgs, + ignore = [ + (PkgUnboundArgs.M25341._totuple, Type{Tuple{Vararg{E}}} where {E}, Any, Vararg), + (PkgUnboundArgs.Issue86.f, NTuple), + ], + ) + # It works with other tests: Aqua.test_ambiguities(PkgUnboundArgs) Aqua.test_undefined_exports(PkgUnboundArgs) From cbe792650e9be046df487f9243f0afb177f36fdf Mon Sep 17 00:00:00 2001 From: arnaud-ma Date: Sun, 26 Jan 2025 18:21:01 +0100 Subject: [PATCH 2/6] rename 'ignore' keyword to 'exclude' in test_unbound_args to align with test_ambiguities --- src/unbound_args.jl | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/unbound_args.jl b/src/unbound_args.jl index a2c65265..81fcc1f0 100644 --- a/src/unbound_args.jl +++ b/src/unbound_args.jl @@ -11,19 +11,19 @@ passed directly to the function instead of the module. # Keyword Arguments - `broken::Bool = false`: If true, it uses `@test_broken` instead of `@test` and shortens the error message. -- `ignore::Vector{Tuple{Function, DataType...}}`: A list of functions and their - signatures to ignore. The signatures are given as tuples, where the - first element is the function and the rest are the types of the - arguments. For example, to ignore `foo(x::Int, y::Float64)`, pass - `(foo, Int, Float64)`. +- `exclude::AbstractVector{Tuple{Function, DataType...}} = []`: A list of + functions and their signatures to exclude. The signatures are given as + tuples, where the first element is the function and the rest are the types of + the arguments. For example, to ignore `foo(x::Int, y::Float64)`, + pass `(foo, Int, Float64)`. """ -function test_unbound_args(m::Module; broken::Bool = false, ignore = ()) +function test_unbound_args(m::Module; broken::Bool = false, exclude = []) unbounds = detect_unbound_args_recursively(m) - for i in ignore + for i in exclude # i[2:end] is empty if length(i) == 1 - ignore_signature = Tuple{typeof(i[1]),i[2:end]...} + exclude_signature = Tuple{typeof(i[1]),i[2:end]...} filter!(unbounds) do method - method.sig != ignore_signature + method.sig != exclude_signature end end test_unbound_args(unbounds; broken = broken) From 549620e9de42b22a2cd41d416265e03ac06b0f73 Mon Sep 17 00:00:00 2001 From: arnaud-ma Date: Sun, 26 Jan 2025 18:29:42 +0100 Subject: [PATCH 3/6] handle the case of callable objects in test_unbound_args --- src/unbound_args.jl | 7 +++++-- test/pkgs/PkgUnboundArgs.jl | 9 +++++++++ test/test_unbound_args.jl | 3 ++- 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/src/unbound_args.jl b/src/unbound_args.jl index 81fcc1f0..4eaee585 100644 --- a/src/unbound_args.jl +++ b/src/unbound_args.jl @@ -20,8 +20,11 @@ passed directly to the function instead of the module. function test_unbound_args(m::Module; broken::Bool = false, exclude = []) unbounds = detect_unbound_args_recursively(m) for i in exclude - # i[2:end] is empty if length(i) == 1 - exclude_signature = Tuple{typeof(i[1]),i[2:end]...} + callable, args = i[1], i[2:end] # i[2:end] is empty if length(i) == 1 + + # the type of the function is the function itself if it is a callable object + callable_t = callable isa Function ? typeof(callable) : callable + exclude_signature = Tuple{callable_t, args...} filter!(unbounds) do method method.sig != exclude_signature end diff --git a/test/pkgs/PkgUnboundArgs.jl b/test/pkgs/PkgUnboundArgs.jl index ee597f0b..8b3972f5 100644 --- a/test/pkgs/PkgUnboundArgs.jl +++ b/test/pkgs/PkgUnboundArgs.jl @@ -12,4 +12,13 @@ module Issue86 f(::NTuple{N,T}) where {N,T} = (N, T) f(::Tuple{}) = (0, Any) end + +module ExcludeCallableObject +struct Callable{U} + s::U +end + +(::Callable{U})(::NTuple{N,T}) where {N,T,U} = (N, T, U) +(::Callable{U})(::Tuple{}) where {U} = (0, Any, U) +end end # module diff --git a/test/test_unbound_args.jl b/test/test_unbound_args.jl index 6cc113a2..617c4064 100644 --- a/test/test_unbound_args.jl +++ b/test/test_unbound_args.jl @@ -15,9 +15,10 @@ using PkgUnboundArgs Aqua.test_unbound_args( PkgUnboundArgs, - ignore = [ + exclude = [ (PkgUnboundArgs.M25341._totuple, Type{Tuple{Vararg{E}}} where {E}, Any, Vararg), (PkgUnboundArgs.Issue86.f, NTuple), + (PkgUnboundArgs.ExcludeCallableObject.Callable, NTuple), ], ) From 35b2dc8cff7a63cca5a1ba9d1ea65a77f9a33aa2 Mon Sep 17 00:00:00 2001 From: arnaud-ma Date: Sun, 26 Jan 2025 18:45:33 +0100 Subject: [PATCH 4/6] remove the test_unbound_args(unbounds) method --- src/unbound_args.jl | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/src/unbound_args.jl b/src/unbound_args.jl index 4eaee585..ee853904 100644 --- a/src/unbound_args.jl +++ b/src/unbound_args.jl @@ -1,12 +1,10 @@ """ test_unbound_args(module::Module) - test_unbound_args(unbounds) Test that all methods in `module` and its submodules do not have unbound type parameters. An unbound type parameter is a type parameter with a `where`, that does not occur in the signature of some dispatch -of the method. If unbounds methods are already known, they can be -passed directly to the function instead of the module. +of the method. # Keyword Arguments - `broken::Bool = false`: If true, it uses `@test_broken` instead of @@ -25,14 +23,9 @@ function test_unbound_args(m::Module; broken::Bool = false, exclude = []) # the type of the function is the function itself if it is a callable object callable_t = callable isa Function ? typeof(callable) : callable exclude_signature = Tuple{callable_t, args...} - filter!(unbounds) do method - method.sig != exclude_signature - end + filter!(method -> (method.sig != exclude_signature), unbounds) end - test_unbound_args(unbounds; broken = broken) -end -function test_unbound_args(unbounds; broken::Bool = false) if broken if !isempty(unbounds) printstyled( From f30844d1ad1cae80b40925d50983df1e45e82c96 Mon Sep 17 00:00:00 2001 From: arnaud-ma Date: Sun, 26 Jan 2025 19:14:44 +0100 Subject: [PATCH 5/6] update the doc of test_unbound_args to align with test_ambiguities --- src/unbound_args.jl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/unbound_args.jl b/src/unbound_args.jl index ee853904..68daf441 100644 --- a/src/unbound_args.jl +++ b/src/unbound_args.jl @@ -9,10 +9,10 @@ of the method. # Keyword Arguments - `broken::Bool = false`: If true, it uses `@test_broken` instead of `@test` and shortens the error message. -- `exclude::AbstractVector{Tuple{Function, DataType...}} = []`: A list of - functions and their signatures to exclude. The signatures are given as - tuples, where the first element is the function and the rest are the types of - the arguments. For example, to ignore `foo(x::Int, y::Float64)`, +- `exclude::AbstractVector{Tuple{Base.Callable, DataType...}} = []`: A vector of + signatures of functions or callable to exclude from testing. A signature is given + as a tuple, where the first element is the callable and the rest are + the types of the arguments. For example, to exclude `foo(x::Int, y::Float64)`, pass `(foo, Int, Float64)`. """ function test_unbound_args(m::Module; broken::Bool = false, exclude = []) From df69299936c884f1c9d62b86d0709b8665eb0930 Mon Sep 17 00:00:00 2001 From: arnaud-ma Date: Tue, 4 Feb 2025 01:48:15 +0100 Subject: [PATCH 6/6] use exactly the same signature style as Julia to exclude methods in test_unbounds_args --- src/unbound_args.jl | 18 ++++++------------ test/test_unbound_args.jl | 11 ++++++++--- 2 files changed, 14 insertions(+), 15 deletions(-) diff --git a/src/unbound_args.jl b/src/unbound_args.jl index 68daf441..5338246a 100644 --- a/src/unbound_args.jl +++ b/src/unbound_args.jl @@ -9,21 +9,15 @@ of the method. # Keyword Arguments - `broken::Bool = false`: If true, it uses `@test_broken` instead of `@test` and shortens the error message. -- `exclude::AbstractVector{Tuple{Base.Callable, DataType...}} = []`: A vector of - signatures of functions or callable to exclude from testing. A signature is given - as a tuple, where the first element is the callable and the rest are - the types of the arguments. For example, to exclude `foo(x::Int, y::Float64)`, - pass `(foo, Int, Float64)`. +- `exclude::AbstractVector = []`: A vector of signatures of methods to exclude + from testing. A signature is usually of the form `Tuple{typeof(f), T1, T2, ...}`. + where `f` is the function and `T1, T2, ...` the type parameters. For example, the + signature of `f(x::Float64, y)` is `Tuple{typeof(f), Float64, Any}`. """ function test_unbound_args(m::Module; broken::Bool = false, exclude = []) unbounds = detect_unbound_args_recursively(m) - for i in exclude - callable, args = i[1], i[2:end] # i[2:end] is empty if length(i) == 1 - - # the type of the function is the function itself if it is a callable object - callable_t = callable isa Function ? typeof(callable) : callable - exclude_signature = Tuple{callable_t, args...} - filter!(method -> (method.sig != exclude_signature), unbounds) + for signature in exclude + filter!(method -> (method.sig != signature), unbounds) end if broken diff --git a/test/test_unbound_args.jl b/test/test_unbound_args.jl index 617c4064..84557ff2 100644 --- a/test/test_unbound_args.jl +++ b/test/test_unbound_args.jl @@ -16,9 +16,14 @@ using PkgUnboundArgs Aqua.test_unbound_args( PkgUnboundArgs, exclude = [ - (PkgUnboundArgs.M25341._totuple, Type{Tuple{Vararg{E}}} where {E}, Any, Vararg), - (PkgUnboundArgs.Issue86.f, NTuple), - (PkgUnboundArgs.ExcludeCallableObject.Callable, NTuple), + Tuple{ + typeof(PkgUnboundArgs.M25341._totuple), + Type{Tuple{Vararg{E}}} where E, + Any, + Vararg{Any}, + }, + Tuple{typeof(PkgUnboundArgs.Issue86.f),NTuple}, + Tuple{PkgUnboundArgs.ExcludeCallableObject.Callable,NTuple}, ], )