Skip to content

Commit 0a96266

Browse files
committed
go back to doing some structural evaluation, but more carefully using types
Even though that means there are cases that will be impossible to exclude, the author of Aqua confirmed that is intentional.
1 parent 35028cb commit 0a96266

File tree

3 files changed

+42
-39
lines changed

3 files changed

+42
-39
lines changed

src/ambiguities.jl

Lines changed: 30 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -29,30 +29,31 @@ test_ambiguities(packages; kwargs...) = _test_ambiguities(aspkgids(packages); kw
2929

3030
const ExcludeSpec = Pair{Base.PkgId,String}
3131

32-
strnameof(x) = string(x)
33-
strnameof(x::Type) = string(nameof(x))
34-
35-
rootmodule(x) = rootmodule(parentmodule(x))
32+
rootmodule(x::Type) = rootmodule(parentmodule(x))
3633
rootmodule(m::Module) = Base.require(PkgId(m)) # this handles Base/Core well
3734

38-
normalize_exclude(x::Union{Type,Function}) =
39-
Base.PkgId(rootmodule(x)) => join((fullname(parentmodule(x))..., strnameof(x)), ".")
40-
normalize_exclude(::Any) = error("Only a function and type can be excluded.")
35+
# get the Type associated with x
36+
normalize_exclude_obj(@nospecialize x) = x isa Type ? x : typeof(x)
37+
38+
function normalize_exclude(@nospecialize x)
39+
x = normalize_exclude_obj(x)
40+
return Base.PkgId(rootmodule(x)) => join((fullname(parentmodule(x))..., string(nameof(x))), ".")
41+
end
4142

42-
function getobj((pkgid, name)::ExcludeSpec)
43+
function getexclude((pkgid, name)::ExcludeSpec)
4344
nameparts = Symbol.(split(name, "."))
4445
m = Base.require(pkgid)
4546
for name in nameparts
4647
m = getproperty(m, name)
4748
end
48-
return m
49+
return normalize_exclude_obj(m)
4950
end
5051

5152
function normalize_and_check_exclude(exclude::AbstractVector)
52-
exspecs = mapfoldl(normalize_exclude, push!, exclude, init = ExcludeSpec[])
53-
for (spec, obj) in zip(exspecs, exclude)
54-
if getobj(spec) !== obj
55-
error("Name `$(spec[2])` is resolved to a different object.")
53+
exspecs = ExcludeSpec[normalize_exclude(exspec) for exspec in exclude]
54+
for (i, exspec) in enumerate(exspecs)
55+
if getexclude(exspec) !== normalize_exclude_obj(exclude[i])
56+
error("Name `$(exspec[2])` is resolved to a different object.")
5657
end
5758
end
5859
return exspecs::Vector{ExcludeSpec}
@@ -154,6 +155,20 @@ function reprpkgid(pkg::PkgId)
154155
return "Base.PkgId(Base.UUID($(repr(uuid.value))), $(repr(name)))"
155156
end
156157

158+
# try to extract the called function, or nothing if it is hard to analyze
159+
function trygetft(m::Method)
160+
sig = Base.unwrap_unionall(m.sig)::DataType
161+
ft = sig.parameters[is_kwcall(sig) ? 3 : 1]
162+
ft = Base.unwrap_unionall(ft)
163+
if ft isa DataType && ft.name === Type.body.name
164+
ft = Base.unwrap_unionall(ft.parameters[1])
165+
end
166+
if ft isa DataType
167+
return ft.name.wrapper
168+
end
169+
return nothing # cannot exclude signatures with Union
170+
end
171+
157172
function test_ambiguities_impl(
158173
packages::Vector{PkgId},
159174
options::NamedTuple,
@@ -165,26 +180,9 @@ function test_ambiguities_impl(
165180
ambiguities = detect_ambiguities(modules...; options...)
166181

167182
if !isempty(exspecs)
168-
exclude_ft = Any[getobj(spec) for spec in exspecs]
169-
exclude_sig = Any[]
170-
for ft in exclude_ft
171-
if ft isa Type
172-
push!(exclude_sig, Tuple{ft, Vararg})
173-
push!(exclude_sig, Tuple{Core.kwftype(ft), Any, ft, Vararg})
174-
ft = Type{<:ft} # alternatively, Type{ft}
175-
else
176-
ft = typeof(ft)
177-
end
178-
push!(exclude_sig, Tuple{ft, Vararg})
179-
push!(exclude_sig, Tuple{Core.kwftype(ft), Any, ft, Vararg})
180-
end
183+
exclude_ft = Any[getexclude(spec) for spec in exspecs] # vector of Type objects
181184
ambiguities = filter(ambiguities) do (m1, m2)
182-
for excl in exclude_sig
183-
if m1.sig <: excl || m2.sig <: excl
184-
return false
185-
end
186-
end
187-
return true
185+
trygetft(m1) exclude_ft && trygetft(m2) exclude_ft
188186
end
189187
end
190188

test/test_ambiguities.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ include("preamble.jl")
4949
check_testcase([PkgWithAmbiguities.ConcreteType], total - num_ambs_ConcreteType)
5050

5151
# exclude abstract supertype without callables and constructors
52-
check_testcase([PkgWithAmbiguities.AbstractType], total - num_ambs_SingletonType - num_ambs_ConcreteType)
52+
check_testcase([PkgWithAmbiguities.AbstractType], total)
5353

5454
# for ambiguities between abstract and concrete type callables, only one needs to be excluded
5555
check_testcase(

test/test_exclude.jl

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ module TestExclude
22

33
include("preamble.jl")
44
using Base: PkgId
5-
using Aqua: getobj, normalize_exclude, normalize_and_check_exclude, rootmodule, reprexclude
5+
using Aqua: getexclude, normalize_exclude, normalize_exclude_obj, normalize_and_check_exclude, rootmodule, reprexclude
66

77
@assert parentmodule(Tuple) === Core
88
@assert parentmodule(foldl) === Base
@@ -11,27 +11,32 @@ using Aqua: getobj, normalize_exclude, normalize_and_check_exclude, rootmodule,
1111
@assert rootmodule(Broadcast.Broadcasted) === Base
1212

1313
@testset "roundtrip" begin
14-
@testset for x in [
14+
@testset for x in Any[
1515
foldl
1616
Some
1717
Tuple
1818
Broadcast.Broadcasted
19+
nothing
20+
Any
1921
]
20-
@test getobj(normalize_exclude(x)) == x
22+
@test getexclude(normalize_exclude(x)) === normalize_exclude_obj(x)
2123
end
24+
@test_throws ErrorException normalize_and_check_exclude(Any[Pair{Int}])
2225

2326
@testset "$(repr(last(spec)))" for spec in [
24-
(PkgId(Base) => "Base.foldl")
27+
(PkgId(Base) => "Base.#foldl")
2528
(PkgId(Base) => "Base.Some")
2629
(PkgId(Core) => "Core.Tuple")
2730
(PkgId(Base) => "Base.Broadcast.Broadcasted")
31+
(PkgId(Core) => "Core.Nothing")
32+
(PkgId(Core) => "Core.Any")
2833
]
29-
@test normalize_exclude(getobj(spec)) === spec
34+
@test normalize_exclude(getexclude(spec)) === spec
3035
end
3136
end
3237

3338
@testset "normalize_and_check_exclude" begin
34-
@testset "$i" for (i, exclude) in enumerate([[foldl], [foldl, Some], [foldl, Tuple]])
39+
@testset "$i" for (i, exclude) in enumerate([Any[foldl], Any[foldl, Some], Any[foldl, Tuple]])
3540
local specs
3641
@test (specs = normalize_and_check_exclude(exclude)) isa Vector
3742
@test Base.include_string(@__MODULE__, reprexclude(specs)) == specs

0 commit comments

Comments
 (0)