diff --git a/NEWS.md b/NEWS.md index 685572a3fa480..244c6f42f1066 100644 --- a/NEWS.md +++ b/NEWS.md @@ -29,6 +29,8 @@ New library functions Standard library changes ------------------------ +* Added `Base.hasproperty` and `Base.hasfield` ([#28850]). + #### LinearAlgebra * Added keyword arguments `rtol`, `atol` to `pinv` and `nullspace` ([#29998]). diff --git a/base/exports.jl b/base/exports.jl index 74ef52ea28e77..8a26eb8fdc73f 100644 --- a/base/exports.jl +++ b/base/exports.jl @@ -700,7 +700,9 @@ export fieldnames, fieldcount, fieldtypes, + hasfield, propertynames, + hasproperty, isabstracttype, isbitstype, isprimitivetype, diff --git a/base/reflection.jl b/base/reflection.jl index d201ecdff72c9..428aa3cae11b1 100644 --- a/base/reflection.jl +++ b/base/reflection.jl @@ -176,6 +176,17 @@ fieldnames(::Core.TypeofBottom) = throw(ArgumentError("The empty type does not have field names since it does not have instances.")) fieldnames(t::Type{<:Tuple}) = ntuple(identity, fieldcount(t)) +""" + hasfield(T::Type, name::Symbol) + +Returns a boolean indicating whether DataType has the specified field as one of +its own fields. +""" +function hasfield(::Type{T}, name::Symbol) where T + @_pure_meta + return fieldindex(T, name, false) > 0 +end + """ nameof(t::DataType) -> Symbol @@ -1204,3 +1215,11 @@ REPL tab completion on `x.` shows only the `private=false` properties. propertynames(x) = fieldnames(typeof(x)) propertynames(m::Module) = names(m) propertynames(x, private) = propertynames(x) # ignore private flag by default + +""" + hasproperty(x, s::Symbol) + +Returns a boolean indicating whether the object `x` has the specified property as one of +its own properties. +""" +hasproperty(x, s::Symbol) = s in propertynames(x) diff --git a/test/reflection.jl b/test/reflection.jl index a74e21f12a27a..25118419332c6 100644 --- a/test/reflection.jl +++ b/test/reflection.jl @@ -221,6 +221,10 @@ mutable struct TLayout end tlayout = TLayout(5,7,11) @test fieldnames(TLayout) == (:x, :y, :z) == Base.propertynames(tlayout) +@test hasfield(TLayout, :y) +@test !hasfield(TLayout, :a) +@test hasproperty(tlayout, :x) +@test !hasproperty(tlayout, :p) @test [(fieldoffset(TLayout,i), fieldname(TLayout,i), fieldtype(TLayout,i)) for i = 1:fieldcount(TLayout)] == [(0, :x, Int8), (2, :y, Int16), (4, :z, Int32)] @test fieldnames(Complex) === (:re, :im)