Repository: JuliaLang/julia
File: stdlib/Test/src/Test.jl (Julia)

Code before:
            end
            f = Base.unwrap_unionall(getfield(mod, n))
            if isa(f, Module) && f !== mod && parentmodule(f) === mod && nameof(f) === n
                push!(work, f)
            elseif isa(f, DataType) && isdefined(f.name, :mt) && parentmodule(f) === mod && nameof(f) === n && f.name.mt !== Symbol.name.mt && f.name.mt !== DataType.name.mt
                examine(f.name.mt)
            end
        end
    end
    examine(Symbol.name.mt)
    examine(DataType.name.mt)
    return collect(ambs)
end

"""
    detect_unbound_args(mod1, mod2...; recursive=false, allowed_undefineds=nothing)

Return a vector of `Method`s which may have unbound type parameters.
Use `recursive=true` to test in all submodules.

By default, any undefined symbols trigger a warning. This warning can
be suppressed by supplying a collection of `GlobalRef`s for which
the warning can be skipped. For example, setting

```
allowed_undefineds = Set([GlobalRef(Base, :active_repl),
                          GlobalRef(Base, :active_repl_backend)])
```

would suppress warnings about `Base.active_repl` and
`Base.active_repl_backend`.

!!! compat "Julia 1.8"
    `allowed_undefineds` requires at least Julia 1.8.
"""
function detect_unbound_args(mods...;
                             recursive::Bool = false,
                             allowed_undefineds=nothing)
    @nospecialize mods
    ambs = Set{Method}()
    mods = collect(mods)::Vector{Module}
    function examine(mt::Core.MethodTable)
        for m in Base.MethodList(mt)
            is_in_mods(parentmodule(m), recursive, mods) || continue
            has_unbound_vars(m.sig) || continue
            tuple_sig = Base.unwrap_unionall(m.sig)::DataType
            if Base.isvatuple(tuple_sig)
                params = tuple_sig.parameters[1:(end - 1)]
                tuple_sig = Base.rewrap_unionall(Tuple{params...}, m.sig)
                world = Base.get_world_counter()
                mf = ccall(:jl_gf_invoke_lookup, Any, (Any, Any, UInt), tuple_sig, nothing, world)

Review comment (suggestion):
Apparently some (testing) packages were using this: https://juliahub.com/ui/Search?type=symbols&q=push_testset&u=use.  Presumably it's ok to ask them to adapt their use of internal functions?  [These aren't documented](https://docs.julialang.org/en/v1/stdlib/Test/).

Code after:
_args_and_call((args..., f)...; kwargs...) = (args, kwargs, f(args...; kwargs...))
_materialize_broadcasted(f, args...) = Broadcast.materialize(Broadcast.broadcasted(f, args...))

"""
    @inferred [AllowedType] f(x)

Tests that the call expression `f(x)` returns a value of the same type inferred by the
compiler. It is useful to check for type stability.

`f(x)` can be any call expression. Returns the result of `f(x)` if the types match, and an
`Error` `Result` if it finds different types.

Optionally, `AllowedType` relaxes the test, by making it pass when either the type of `f(x)`
matches the inferred type modulo `AllowedType`, or when the return type is a subtype of
`AllowedType`. This is useful when testing type stability of functions returning a small
union such as `Union{Nothing, T}` or `Union{Missing, T}`.

```jldoctest; setup = :(using InteractiveUtils; using Base: >), filter = r"begin\\n(.|\\n)*end"
julia> f(a) = a > 1 ? 1 : 1.0
f (generic function with 1 method)

julia> typeof(f(2))
Int64

julia> @code_warntype f(2)
MethodInstance for f(::Int64)
  from f(a) @ Main none:1
Arguments
  #self#::Core.Const(f)
  a::Int64
Body::UNION{FLOAT64, INT64}
1 ─ %1 = :>::Core.Const(>)
│   %2 = (%1)(a, 1)::Bool
└──      goto #3 if not %2
2 ─      return 1
3 ─      return 1.0

julia> @inferred f(2)
ERROR: return type Int64 does not match inferred return type Union{Float64, Int64}
[...]

julia> @inferred max(1, 2)
2

julia> g(a) = a < 10 ? missing : 1.0
g (generic function with 1 method)

julia> @inferred g(20)
ERROR: return type Float64 does not match inferred return type Union{Missing, Float64}
[...]