From 9c596579a4f08f6e0314f41e74b3d45b3f0d4445 Mon Sep 17 00:00:00 2001 From: Tim Holy Date: Thu, 1 Aug 2024 06:17:43 -0500 Subject: [PATCH 01/11] Defer unwrapping internal types until show --- TypedSyntax/src/node.jl | 35 ++++++++++++++--------- TypedSyntax/src/show.jl | 6 ++++ TypedSyntax/test/runtests.jl | 50 ++++++++++++++++----------------- TypedSyntax/test/test_module.jl | 4 +-- 4 files changed, 54 insertions(+), 41 deletions(-) diff --git a/TypedSyntax/src/node.jl b/TypedSyntax/src/node.jl index 40b7cbf7..8362ac5e 100644 --- a/TypedSyntax/src/node.jl +++ b/TypedSyntax/src/node.jl @@ -191,12 +191,12 @@ function map_signature!(sig::TypedSyntaxNode, slotnames::Vector{Symbol}, slottyp kwdivider = 1 if havekws && slotnames[1] !== Symbol("#self#") kwdivider = findfirst(1:length(slotnames)) do i - slotnames[i] == Symbol("") && unwrapinternal(slottypes[i]) <: Function # this should be the parent function as an argument + slotnames[i] == Symbol("") && isa(unwrapinternal(slottypes[i]), Function) # this should be the parent function as an argument end if kwdivider === nothing kwdivider = 1 end - if length(slottypes) >= 2 && slotnames[2] == Symbol("") && (nt = unwrapinternal(slottypes[2])) <: NamedTuple + if length(slottypes) >= 2 && slotnames[2] == Symbol("") && (nt = unwrapinternal(slottypes[2]); isa(nt, Type)) && nt <: NamedTuple # Match kwargs argcontainer = children(last(children(sig))) offset = length(children(sig)) - 1 @@ -244,7 +244,7 @@ function map_signature!(sig::TypedSyntaxNode, slotnames::Vector{Symbol}, slottyp if kind(arg) == K"::" && length(children(arg)) == 2 arg = child(arg, 1) end - arg.typ = unwrapinternal(slottypes[idx]) + arg.typ = slottypes[idx] end # It's annoying to print the signature as `foo::typeof(foo)(a::Int)` @@ -276,7 +276,7 @@ function striparg(arg) end function unwrapinternal(@nospecialize(T)) - isa(T, Core.Const) && return Core.Typeof(T.val) + isa(T, Core.Const) && return T.val isa(T, Core.PartialStruct) && return T.typ return T end @@ -287,10 +287,19 @@ function gettyp(node2ssa, node, src) ssavaluetypes = src.ssavaluetypes::Vector{Any} if isa(stmt, Core.ReturnNode) arg = stmt.val - isa(arg, SSAValue) && return unwrapinternal(ssavaluetypes[arg.id]) - is_slot(arg) && return unwrapinternal((src.slottypes::Vector{Any})[arg.id]) + isa(arg, SSAValue) && return ssavaluetypes[arg.id] + is_slot(arg) && return (src.slottypes::Vector{Any})[arg.id] end - return unwrapinternal(ssavaluetypes[i]) + # isa(stmt, GlobalRef) && return getglobal(stmt.mod, stmt.name) + v = ssavaluetypes[i] + # p = node.parent + # if isa(p, MaybeTypedSyntaxNode) && kind(p) == K"call" + # # for `f(x)`, we want to return just `f` rather than `typeof(f)` + # if node == p.children[1] && isa(v, Core.Const) + # return v.val + # end + # end + return v end Base.copy(tsd::TypedSyntaxData) = TypedSyntaxData(tsd.source, tsd.typedsource, tsd.raw, tsd.position, tsd.val, tsd.typ, tsd.runtime) @@ -608,7 +617,7 @@ function map_ssas_to_source(src::CodeInfo, mi::MethodInstance, rootnode::SyntaxN argmapping = typeof(rootnode)[] # temporary storage for (i, mapped, stmt) in zip(eachindex(mappings), mappings, src.code) empty!(argmapping) - if is_slot(stmt) || isa(stmt, SSAValue) + if is_slot(stmt) || isa(stmt, SSAValue) || isa(stmt, GlobalRef) append_targets_for_arg!(mapped, i, stmt) elseif isa(stmt, Core.ReturnNode) append_targets_for_line!(mapped, i, append_targets_for_arg!(argmapping, i, stmt.val)) @@ -626,16 +635,14 @@ function map_ssas_to_source(src::CodeInfo, mi::MethodInstance, rootnode::SyntaxN append_targets_for_arg!(mapped, i, stmt) filter_assignment_targets!(mapped, true) # match the RHS of assignments if length(mapped) == 1 - symtyps[only(mapped)] = unwrapinternal( - (is_slot(stmt) & have_slottypes) ? slottypes[(stmt::SlotType).id] : + symtyps[only(mapped)] = (is_slot(stmt) & have_slottypes) ? slottypes[(stmt::SlotType).id] : isa(stmt, SSAValue) ? ssavaluetypes[stmt.id] : #=literal=#typeof(stmt) - ) end # Now try to assign types to the LHS of the assignment append_targets_for_arg!(argmapping, i, lhs) filter_assignment_targets!(argmapping, false) # match the LHS of assignments if length(argmapping) == 1 - T = unwrapinternal(ssavaluetypes[i]) + T = ssavaluetypes[i] symtyps[only(argmapping)] = T end empty!(argmapping) @@ -793,14 +800,14 @@ function map_ssas_to_source(src::CodeInfo, mi::MethodInstance, rootnode::SyntaxN haskey(symtyps, t) && continue if skipped_parent(t) == node is_prec_assignment(node) && t == child(node, 1) && continue - symtyps[t] = unwrapinternal(if j > 0 + symtyps[t] = if j > 0 ssavaluetypes[j] elseif have_slottypes # We failed to find it as an SSAValue, it must have type assigned at function entry slottypes[arg.id] else nothing - end) + end break end end diff --git a/TypedSyntax/src/show.jl b/TypedSyntax/src/show.jl index f3bfbe05..4fc1d42f 100644 --- a/TypedSyntax/src/show.jl +++ b/TypedSyntax/src/show.jl @@ -96,6 +96,12 @@ end function type_annotation_mode(node, @nospecialize(T); type_annotations::Bool, hide_type_stable::Bool) kind(node) == K"return" && return false, "", "", "" + if isa(T, Core.Const) + val = T.val + if isa(val, Type) || isa(val, Function) + occursin(replace(string(val), isspace => ""), replace(node.source[node.position:last_byte(node)], isspace => "")) && return false, "", "", "" + end + end type_annotate = is_show_annotation(T; type_annotations, hide_type_stable) pre = pre2 = post = "" if type_annotate diff --git a/TypedSyntax/test/runtests.jl b/TypedSyntax/test/runtests.jl index d0115587..28aac33a 100644 --- a/TypedSyntax/test/runtests.jl +++ b/TypedSyntax/test/runtests.jl @@ -49,7 +49,7 @@ include("test_module.jl") @test has_name_typ(child(body, 1), :x, Int) @test has_name_typ(child(body, 3, 2, 1), :x, Int) pi4 = child(body, 3, 2, 3) - @test kind(pi4) == K"call" && pi4.typ == typeof(π / 4) + @test kind(pi4) == K"call" && pi4.typ === Core.Const(π / 4) tsn = TypedSyntaxNode(TSN.has2xa, (Real,)) @test tsn.typ === Any sig, body = children(tsn) @@ -213,18 +213,18 @@ include("test_module.jl") tsn = TypedSyntaxNode(TSN.nestedgenerators, (Int, Int)) sig, body = children(tsn) @test kind(body) == K"generator" - @test body.typ <: Base.Iterators.Flatten + @test TypedSyntax.unwrapinternal(body.typ) <: Base.Iterators.Flatten tsn = TypedSyntaxNode(TSN.nestedgenerators, (Int,)) sig, body = children(tsn) @test kind(body) == K"generator" - @test body.typ <: Base.Iterators.Flatten + @test TypedSyntax.unwrapinternal(body.typ) <: Base.Iterators.Flatten tsn = TypedSyntaxNode(TSN.nestedexplicit, (Int,)) sig, body = children(tsn) @test kind(body) == K"comprehension" @test body.typ <: Vector node = child(body, 1) @test kind(node) == K"generator" - @test node.typ <: Base.Generator + @test TypedSyntax.unwrapinternal(node.typ) <: Base.Generator # Broadcasting tsn = TypedSyntaxNode(TSN.fbroadcast, (Vector{Int},)) @@ -237,9 +237,9 @@ include("test_module.jl") sig, body = children(tsn) @test body.typ === Float64 cnode = child(body, 2) + @test cnode.typ === Vector{Float64} cnodef = child(cnode, 1, 2, 1) @test kind(cnodef) == K"Identifier" && cnodef.val == :materialize - @test cnode.typ === Vector{Float64} cnode = child(body, 2, 2) cnodef = child(cnode, 1, 2, 1) @test kind(cnodef) == K"Identifier" && cnodef.val == :broadcasted @@ -248,7 +248,7 @@ include("test_module.jl") sig, body = children(tsn) node = child(body, 2) src = tsn.typedsource - if isa(src.code[1], GlobalRef) + if isa(src.code[1], GlobalRef) # FIXME: not sure what this is really testing @test kind(node) == K"dotcall" && node.typ === Vector{String} else # We aren't quite handling this properly yet @@ -289,7 +289,7 @@ include("test_module.jl") isz = child(body, 2, 1, 1) @test kind(isz) == K"call" && child(isz, 1).val == :iszero @test isz.typ === Bool - @test child(body, 2, 1, 2).typ == Float64 + @test child(body, 2, 1, 2).typ === Core.Const(NaN) # default positional arguments tsn = TypedSyntaxNode(TSN.defaultarg, (Float32,)) @@ -307,7 +307,7 @@ include("test_module.jl") tsn = TypedSyntaxNode(TSN.hasdefaulttypearg, (Type{Float32},)) sig, body = children(tsn) arg = child(sig, 1, 2, 1) - @test kind(arg) == K"::" && arg.typ === Type{Float32} + @test kind(arg) == K"::" && arg.typ === Core.Const(Float32) tsn = TypedSyntaxNode(TSN.hasdefaulttypearg, ()) sig, body = children(tsn) arg = child(sig, 1, 2, 1) @@ -332,7 +332,7 @@ include("test_module.jl") @test tsn.typ == Union{Int,Float64} sig, body = children(tsn) @test has_name_typ(child(sig, 2), :list, Vector{Float64}) - @test has_name_typ(child(body, 1, 1), :s, Int) + @test has_name_typ(child(body, 1, 1), :s, Core.Const(0)) @test has_name_typ(child(body, 2, 1, 1), :x, Float64) node = child(body, 2, 2, 1) @test kind(node) == K"+=" @@ -350,8 +350,8 @@ include("test_module.jl") tsn = TypedSyntaxNode(TSN.zerowhere, (Vector{Int16},)) sig, body = children(tsn) @test child(sig, 1, 2).typ === Vector{Int16} - @test body.typ === Int16 - @test has_name_typ(child(body, 2), :T, Type{Int16}) + @test body.typ === Core.Const(Int16(0)) + @test has_name_typ(child(body, 2), :T, Core.Const(Int16)) # tsn = TypedSyntaxNode(TSN.vaparam, (Matrix{Float32}, (String, Bool))) # fails on `which` m = @which TSN.vaparam(rand(3,3), ("hello", false)) mi = first(specializations(m)) @@ -371,10 +371,10 @@ include("test_module.jl") @test has_name_typ(child(body, 2), :Bool, Type{Bool}) tsn = TypedSyntaxNode(TSN.unnamedargs, (Type{Matrix{Float32}}, Type{Int})) sig, body = children(tsn) + @test child(sig, 1, 2).typ === Core.Const(Matrix{Float32}) + @test child(sig, 1, 3).typ === Core.Const(Int) m = @which TSN.unnamedargs(Matrix{Float32}, Int, Int) fbody = Base.bodyfunction(m) - @test child(sig, 1, 2).typ === Type{Matrix{Float32}} - @test child(sig, 1, 3).typ === Type{Int} m = @which TSN.unnamedargs(Matrix{Float32}, Int; a="hello") mi = nothing for _mi in specializations(m) @@ -388,8 +388,8 @@ include("test_module.jl") end tsn = TypedSyntaxNode(mi) sig, body = children(tsn) - @test child(sig, 1, 2).typ === Type{Matrix{Float32}} - @test child(sig, 1, 3).typ === Type{Int} + @test child(sig, 1, 2).typ === Core.Const(Matrix{Float32}) + @test child(sig, 1, 3).typ === Core.Const(Int) @test has_name_notyp(child(sig, 1, 4, 1), :c) @test has_name_typ(child(sig, 1, 5, 1, 1), :a, String) m = @which TSN.unnamedargs(Matrix{Float32}, Int, :c; a="hello") @@ -403,8 +403,8 @@ include("test_module.jl") end tsn = TypedSyntaxNode(mi) sig, body = children(tsn) - @test child(sig, 1, 2).typ === Type{Matrix{Float32}} - @test child(sig, 1, 3).typ === Type{Int} + @test child(sig, 1, 2).typ === Core.Const(Matrix{Float32}) + @test child(sig, 1, 3).typ === Core.Const(Int) @test child(sig, 1, 4, 1).typ === Symbol @test child(sig, 1, 5, 1, 1).typ === String mbody = only(methods(fbody)) @@ -418,8 +418,8 @@ include("test_module.jl") end tsn = TypedSyntaxNode(mi) sig, body = children(tsn) - @test child(sig, 1, 2).typ === Type{Matrix{Float32}} - @test child(sig, 1, 3).typ === Type{Int} + @test child(sig, 1, 2).typ === Core.Const(Matrix{Float32}) + @test child(sig, 1, 3).typ === Core.Const(Int) @test child(sig, 1, 4, 1).typ === Symbol @test child(sig, 1, 5, 1, 1).typ === String tsn = TypedSyntaxNode(TSN.unnamedargs2, (Type{Matrix}, Symbol)) @@ -458,7 +458,7 @@ include("test_module.jl") src = tsn.typedsource @test Symbol("kwargs...") ∈ src.slotnames sig, body = children(tsn) - @test child(body, 2, 1).typ <: Base.Iterators.Pairs + @test TypedSyntax.unwrapinternal(child(body, 2, 1).typ) <: Base.Iterators.Pairs # quoted symbols that could be confused for function definition tsn = TypedSyntaxNode(TSN.isexpreq, (Expr,)) @@ -471,10 +471,10 @@ include("test_module.jl") sig, body = children(tsn) errnode = child(body, 1, 2) errf = child(errnode, 1) - @test errnode.typ === nothing && errf.typ === typeof(Base.throw_boundserror) + @test errnode.typ === nothing && errf.typ === Core.Const(Base.throw_boundserror) retnode = child(body, 2) @test kind(retnode) == K"return" - @test retnode.typ === nothing || retnode.typ === Nothing + @test retnode.typ === Core.Const(nothing) # Globals & scoped assignment tsn = TypedSyntaxNode(TSN.setglobal, (Char,)) @@ -486,7 +486,7 @@ include("test_module.jl") tsn = TypedSyntaxNode(TSN.myoftype, (Float64, Int)) sig, body = children(tsn) node = child(body, 1) - @test node.typ === Type{Float64} + @test node.typ === Core.Const(Float64) tsn = TypedSyntaxNode(TSN.DefaultArray{Float32}, (Vector{Int}, Int)) sig, body = children(tsn) @test kind(sig) == K"where" @@ -517,7 +517,7 @@ include("test_module.jl") tsn = TypedSyntaxNode(TSN.myoftype, (Float64, Int)) sig, body = children(tsn) node = child(body, 1) - @test node.typ === Type{Float64} + @test node.typ === Core.Const(Float64) # UnionAll in signature (issue #409) tsn = TypedSyntaxNode(Core.kwcall, (NamedTuple, typeof(issorted), Vector{Int})) @@ -573,7 +573,7 @@ include("test_module.jl") end @test occursin("s::$Int = 0::$Int", str) @test !occursin("(s::$Int = 0::$Int)", str) - @test occursin("(s::Float64 += x::Float64)::Float64", str) + @test occursin("(s::Float64 += x::Float64)::Float64", str) || occursin("(s::Union{Float64, Int64} += x::Float64)::Float64", str) tsn = TypedSyntaxNode(TSN.zerowhere, (Vector{Int16},)) str = sprint(tsn; context=:color=>true) do io, obj printstyled(io, obj; iswarn=true, hide_type_stable=false) diff --git a/TypedSyntax/test/test_module.jl b/TypedSyntax/test/test_module.jl index 1b7b7dfe..0d68444d 100644 --- a/TypedSyntax/test/test_module.jl +++ b/TypedSyntax/test/test_module.jl @@ -50,10 +50,10 @@ zerowhere(::AbstractArray{T}) where T<:Real = zero(T) vaparam(a::AbstractArray{T,N}, I::NTuple{N,Any}) where {T,N} = N @inline function myplustv(x::T, y::Integer) where {T<:AbstractChar} # vendored copy of +(::T, ::Integer) where T<:AbstractChar if x isa Char - u = Int32((bitcast(UInt32, x) >> 24) % Int8) + u = Int32((Base.bitcast(UInt32, x) >> 24) % Int8) if u >= 0 # inline the runtime fast path z = u + y - return 0 <= z < 0x80 ? bitcast(Char, (z % UInt32) << 24) : Char(UInt32(z)) + return 0 <= z < 0x80 ? Base.bitcast(Char, (z % UInt32) << 24) : Char(UInt32(z)) end end return T(Int32(x) + Int32(y)) From d34c1259324ca355d8b5721114b37abff5eba30a Mon Sep 17 00:00:00 2001 From: Tim Holy Date: Fri, 2 Aug 2024 06:22:22 -0500 Subject: [PATCH 02/11] More show fixes --- TypedSyntax/src/show.jl | 43 +++++++++++++++++++++++++-------- TypedSyntax/test/runtests.jl | 7 +++++- TypedSyntax/test/test_module.jl | 5 ++++ 3 files changed, 44 insertions(+), 11 deletions(-) diff --git a/TypedSyntax/src/show.jl b/TypedSyntax/src/show.jl index 4fc1d42f..39e76f36 100644 --- a/TypedSyntax/src/show.jl +++ b/TypedSyntax/src/show.jl @@ -87,28 +87,48 @@ end function is_show_annotation(@nospecialize(T); type_annotations::Bool, hide_type_stable::Bool) type_annotations || return false if isa(T, Core.Const) - T = typeof(T.val) + T = Core.Typeof(T.val) end isa(T, Type) || return false hide_type_stable || return true return isa(T, Type) && is_type_unstable(T) end -function type_annotation_mode(node, @nospecialize(T); type_annotations::Bool, hide_type_stable::Bool) - kind(node) == K"return" && return false, "", "", "" - if isa(T, Core.Const) - val = T.val - if isa(val, Type) || isa(val, Function) - occursin(replace(string(val), isspace => ""), replace(node.source[node.position:last_byte(node)], isspace => "")) && return false, "", "", "" +# Is the type equivalent to the source-text? +is_type_transparent(node, @nospecialize(T)) = replace(sourcetext(node), r"\s" => "") == replace(sprint(show, T), r"\s" => "") + +function is_callfunc(node::TypedSyntaxNode, @nospecialize(T)) + pnode = node.parent + if pnode !== nothing && kind(pnode) ∈ (K"call", K"curly") && ((is_infix_op_call(pnode) && is_operator(node)) || node === pnode.children[1]) + if isa(T, Core.Const) + T = T.val + end + if isa(T, Type) || isa(T, Function) + return is_type_transparent(node, T) end end + return false +end + +function type_annotation_mode(node, @nospecialize(T); type_annotations::Bool, hide_type_stable::Bool) + kind(node) == K"return" && return false, "", "", "" + is_callfunc(node, T) && return false, "", "", "" type_annotate = is_show_annotation(T; type_annotations, hide_type_stable) pre = pre2 = post = "" if type_annotate - if T isa DataType && T <: Type && isassigned(T.parameters, 1) - if replace(sourcetext(node), r"\s" => "") == replace(sprint(show, T.parameters[1]), r"\s" => "") - return false, pre, pre2, post + # Try stripping Core.Const and Type{T} wrappers to check if we need to avoid `String::Type{String}` + # or `String::Core.Const(String)` annotations + S = nothing + if isa(T, Core.Const) + val = T.val + if isa(val, DataType) + S = val end + elseif isa(T, DataType) && T <: Type && isassigned(T.parameters, 1) + S = T.parameters[1] + end + if S !== nothing && is_type_transparent(node, S) + return false, pre, pre2, post end if kind(node) ∈ KSet":: where" || is_infix_op_call(node) || (is_prec_assignment(node) && kind(node) != K"=") pre, post = "(", ")" @@ -124,6 +144,9 @@ function show_annotation(io, @nospecialize(T), post, node, position; iswarn::Boo inlay_hints = get(io, :inlay_hints, nothing) print(io, post) + if isa(T, Core.Const) && isa(T.val, Type) + T = Type{T.val} + end T_str = string(T) if iswarn && is_type_unstable(T) color = is_small_union_or_tunion(T) ? :yellow : :red diff --git a/TypedSyntax/test/runtests.jl b/TypedSyntax/test/runtests.jl index 28aac33a..d86b0b99 100644 --- a/TypedSyntax/test/runtests.jl +++ b/TypedSyntax/test/runtests.jl @@ -571,7 +571,7 @@ include("test_module.jl") str = sprint(tsn; context=:color=>false) do io, obj printstyled(io, obj; hide_type_stable=false) end - @test occursin("s::$Int = 0::$Int", str) + @test occursin("s::$Int = 0::$Int", str) || occursin("s::Core.Const(0) = 0::Core.Const(0)", str) @test !occursin("(s::$Int = 0::$Int)", str) @test occursin("(s::Float64 += x::Float64)::Float64", str) || occursin("(s::Union{Float64, Int64} += x::Float64)::Float64", str) tsn = TypedSyntaxNode(TSN.zerowhere, (Vector{Int16},)) @@ -619,6 +619,11 @@ include("test_module.jl") printstyled(io, obj; hide_type_stable=false) end @test !occursin("::Type{Dict{String, Any}}", str) + tsn = TypedSyntaxNode(TSN.obfuscated, (Float64,)) + str = sprint(tsn; context=:color=>false) do io, obj + printstyled(io, obj; hide_type_stable=false) + end + @test occursin("::Core.Const(sin)", str) || occursin("::typeof(sin)", str) # issue #413 @test TypedSyntax.is_small_union_or_tunion(Union{}) diff --git a/TypedSyntax/test/test_module.jl b/TypedSyntax/test/test_module.jl index 0d68444d..50877df3 100644 --- a/TypedSyntax/test/test_module.jl +++ b/TypedSyntax/test/test_module.jl @@ -242,4 +242,9 @@ function f493() sum(rand(T, 100)) end +function obfuscated(x) + f = sin + return f(x) +end + end From 921a12eae46cebd975665628adafcf93a039488e Mon Sep 17 00:00:00 2001 From: Tim Holy Date: Fri, 2 Aug 2024 06:56:05 -0500 Subject: [PATCH 03/11] Re-fix 1.10 --- TypedSyntax/test/runtests.jl | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/TypedSyntax/test/runtests.jl b/TypedSyntax/test/runtests.jl index d86b0b99..41c4047c 100644 --- a/TypedSyntax/test/runtests.jl +++ b/TypedSyntax/test/runtests.jl @@ -2,6 +2,7 @@ using JuliaSyntax: JuliaSyntax, SyntaxNode, children, child, sourcetext, kind, @ using TypedSyntax: TypedSyntax, TypedSyntaxNode using Dates, InteractiveUtils, Test +has_name_typ(node, name::Symbol, @nospecialize(Ts::Tuple)) = kind(node) == K"Identifier" && node.val === name && node.typ in Ts has_name_typ(node, name::Symbol, @nospecialize(T)) = kind(node) == K"Identifier" && node.val === name && node.typ === T has_name_notyp(node, name::Symbol) = has_name_typ(node, name, nothing) @@ -351,7 +352,7 @@ include("test_module.jl") sig, body = children(tsn) @test child(sig, 1, 2).typ === Vector{Int16} @test body.typ === Core.Const(Int16(0)) - @test has_name_typ(child(body, 2), :T, Core.Const(Int16)) + @test has_name_typ(child(body, 2), :T, (Core.Const(Int16), Type{Int16})) # tsn = TypedSyntaxNode(TSN.vaparam, (Matrix{Float32}, (String, Bool))) # fails on `which` m = @which TSN.vaparam(rand(3,3), ("hello", false)) mi = first(specializations(m)) @@ -474,7 +475,7 @@ include("test_module.jl") @test errnode.typ === nothing && errf.typ === Core.Const(Base.throw_boundserror) retnode = child(body, 2) @test kind(retnode) == K"return" - @test retnode.typ === Core.Const(nothing) + @test retnode.typ === Core.Const(nothing) || retnode.typ === nothing # julia 1.10 doesn't assign a type to the Core.ReturnNode # Globals & scoped assignment tsn = TypedSyntaxNode(TSN.setglobal, (Char,)) From f6591ad94500c15aa19d63419b60302bc0acc925 Mon Sep 17 00:00:00 2001 From: Tim Holy Date: Sun, 4 Aug 2024 13:52:40 -0500 Subject: [PATCH 04/11] More special handling for `materialize` --- TypedSyntax/src/node.jl | 3 +++ 1 file changed, 3 insertions(+) diff --git a/TypedSyntax/src/node.jl b/TypedSyntax/src/node.jl index 8362ac5e..459c5303 100644 --- a/TypedSyntax/src/node.jl +++ b/TypedSyntax/src/node.jl @@ -911,6 +911,9 @@ function skipped_parent(node::SyntaxNode) pnode === nothing && return node ppnode = pnode.parent if ppnode !== nothing && kind(pnode) ∈ KSet"... quote" # might need to add more things here + if kind(node) == K"Identifier" && kind(pnode) == K"quote" && kind(ppnode) == K"." && sourcetext(node) == "materialize" + return ppnode.parent + end return ppnode end return pnode From 54606b3951a0b78b1a641b78080d1909cdaaafd0 Mon Sep 17 00:00:00 2001 From: Tim Holy Date: Sun, 4 Aug 2024 14:01:03 -0500 Subject: [PATCH 05/11] Fix last broken test --- TypedSyntax/src/node.jl | 2 +- TypedSyntax/test/runtests.jl | 7 +------ 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/TypedSyntax/src/node.jl b/TypedSyntax/src/node.jl index 459c5303..1f384d0e 100644 --- a/TypedSyntax/src/node.jl +++ b/TypedSyntax/src/node.jl @@ -745,7 +745,7 @@ function map_ssas_to_source(src::CodeInfo, mi::MethodInstance, rootnode::SyntaxN if isexpr(nextstmt, :call) f = nextstmt.args[1] if isa(f, GlobalRef) && f.mod == Base && f.name == :broadcasted - empty!(mapped) + # empty!(mapped) break elseif isa(f, GlobalRef) && f.mod == Base && f.name == :materialize && nextstmt.args[2] === SSAValue(i) push!(mappings[inext], node) diff --git a/TypedSyntax/test/runtests.jl b/TypedSyntax/test/runtests.jl index 41c4047c..bb74caff 100644 --- a/TypedSyntax/test/runtests.jl +++ b/TypedSyntax/test/runtests.jl @@ -249,12 +249,7 @@ include("test_module.jl") sig, body = children(tsn) node = child(body, 2) src = tsn.typedsource - if isa(src.code[1], GlobalRef) # FIXME: not sure what this is really testing - @test kind(node) == K"dotcall" && node.typ === Vector{String} - else - # We aren't quite handling this properly yet - @test_broken kind(node) == K"dotcall" && node.typ === Vector{String} - end + @test kind(node) == K"dotcall" && node.typ === Vector{String} tsn = TypedSyntaxNode(TSN.bcast415, (TSN.B415, Float64)) sig, body = children(tsn) @test child(body, 1).typ === Float64 From 646b241abec28ba55fc51b513ba2666b67c914a3 Mon Sep 17 00:00:00 2001 From: Tim Holy Date: Sun, 4 Aug 2024 14:11:59 -0500 Subject: [PATCH 06/11] Delete debugging text --- TypedSyntax/src/node.jl | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/TypedSyntax/src/node.jl b/TypedSyntax/src/node.jl index 1f384d0e..c83f07e0 100644 --- a/TypedSyntax/src/node.jl +++ b/TypedSyntax/src/node.jl @@ -290,16 +290,7 @@ function gettyp(node2ssa, node, src) isa(arg, SSAValue) && return ssavaluetypes[arg.id] is_slot(arg) && return (src.slottypes::Vector{Any})[arg.id] end - # isa(stmt, GlobalRef) && return getglobal(stmt.mod, stmt.name) - v = ssavaluetypes[i] - # p = node.parent - # if isa(p, MaybeTypedSyntaxNode) && kind(p) == K"call" - # # for `f(x)`, we want to return just `f` rather than `typeof(f)` - # if node == p.children[1] && isa(v, Core.Const) - # return v.val - # end - # end - return v + return ssavaluetypes[i] end Base.copy(tsd::TypedSyntaxData) = TypedSyntaxData(tsd.source, tsd.typedsource, tsd.raw, tsd.position, tsd.val, tsd.typ, tsd.runtime) From 3038576c8da23246c72ac0b26c018def55d31ec4 Mon Sep 17 00:00:00 2001 From: Tim Holy Date: Sun, 4 Aug 2024 14:29:21 -0500 Subject: [PATCH 07/11] Change VSCode test --- TypedSyntax/test/runtests.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/TypedSyntax/test/runtests.jl b/TypedSyntax/test/runtests.jl index bb74caff..65a4e2e3 100644 --- a/TypedSyntax/test/runtests.jl +++ b/TypedSyntax/test/runtests.jl @@ -711,8 +711,8 @@ using TypedSyntax: InlayHint, Diagnostic, InlayHintKinds "(" "::$Int" ")::Bool" - "::$Int" - "::Float64" + "::Core.Const(-1)" + "::Core.Const(1.0)" ")::Union{Float64, $Int}"] @test length(io[:diagnostics]) == 2 end From ee3b8c48560b82d0c37d9838147ccc33da66c230 Mon Sep 17 00:00:00 2001 From: Tim Holy Date: Mon, 5 Aug 2024 05:04:51 -0500 Subject: [PATCH 08/11] Fix 32-bit failure --- TypedSyntax/test/runtests.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TypedSyntax/test/runtests.jl b/TypedSyntax/test/runtests.jl index 65a4e2e3..28f216b4 100644 --- a/TypedSyntax/test/runtests.jl +++ b/TypedSyntax/test/runtests.jl @@ -569,7 +569,7 @@ include("test_module.jl") end @test occursin("s::$Int = 0::$Int", str) || occursin("s::Core.Const(0) = 0::Core.Const(0)", str) @test !occursin("(s::$Int = 0::$Int)", str) - @test occursin("(s::Float64 += x::Float64)::Float64", str) || occursin("(s::Union{Float64, Int64} += x::Float64)::Float64", str) + @test occursin("(s::Float64 += x::Float64)::Float64", str) || occursin("(s::Union{Float64, $Int} += x::Float64)::Float64", str) tsn = TypedSyntaxNode(TSN.zerowhere, (Vector{Int16},)) str = sprint(tsn; context=:color=>true) do io, obj printstyled(io, obj; iswarn=true, hide_type_stable=false) From ab10c8946c4fc7d3fbcda32c9b640a0c7e22ffa1 Mon Sep 17 00:00:00 2001 From: Tim Holy Date: Mon, 5 Aug 2024 05:59:29 -0500 Subject: [PATCH 09/11] Relax to handle qualification --- TypedSyntax/src/show.jl | 3 ++- TypedSyntax/test/runtests.jl | 5 +++++ TypedSyntax/test/test_module.jl | 7 +++++++ 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/TypedSyntax/src/show.jl b/TypedSyntax/src/show.jl index 39e76f36..83422be7 100644 --- a/TypedSyntax/src/show.jl +++ b/TypedSyntax/src/show.jl @@ -95,7 +95,8 @@ function is_show_annotation(@nospecialize(T); type_annotations::Bool, hide_type_ end # Is the type equivalent to the source-text? -is_type_transparent(node, @nospecialize(T)) = replace(sourcetext(node), r"\s" => "") == replace(sprint(show, T), r"\s" => "") +# We use `endswith` to handle module qualification +is_type_transparent(node, @nospecialize(T)) = endswith(replace(sprint(show, T), r"\s" => ""), replace(sourcetext(node), r"\s" => "")) function is_callfunc(node::TypedSyntaxNode, @nospecialize(T)) pnode = node.parent diff --git a/TypedSyntax/test/runtests.jl b/TypedSyntax/test/runtests.jl index 28f216b4..91c25cc7 100644 --- a/TypedSyntax/test/runtests.jl +++ b/TypedSyntax/test/runtests.jl @@ -620,6 +620,11 @@ include("test_module.jl") printstyled(io, obj; hide_type_stable=false) end @test occursin("::Core.Const(sin)", str) || occursin("::typeof(sin)", str) + tsn = TypedSyntaxNode(TSN.calls_helper, (Float32,)) + str = sprint(tsn; context=:color=>false) do io, obj + printstyled(io, obj; hide_type_stable=false) + end + @test !occursin("Core.Const", str) # issue #413 @test TypedSyntax.is_small_union_or_tunion(Union{}) diff --git a/TypedSyntax/test/test_module.jl b/TypedSyntax/test/test_module.jl index 50877df3..650f18f7 100644 --- a/TypedSyntax/test/test_module.jl +++ b/TypedSyntax/test/test_module.jl @@ -247,4 +247,11 @@ function obfuscated(x) return f(x) end +module Internal +export helper +helper(x) = x+1 +end +using .Internal +calls_helper(x) = helper(x) + end From 07e1356e9a1c8619749f8ea6db6b33ac3dfde494 Mon Sep 17 00:00:00 2001 From: Tim Holy Date: Thu, 8 Aug 2024 05:42:02 -0500 Subject: [PATCH 10/11] Modules & scoping fixes Modules probably don't need `Core.Const` annotations --- TypedSyntax/src/show.jl | 10 ++++++++-- TypedSyntax/test/runtests.jl | 10 ++++++++++ TypedSyntax/test/test_module.jl | 5 +++++ 3 files changed, 23 insertions(+), 2 deletions(-) diff --git a/TypedSyntax/src/show.jl b/TypedSyntax/src/show.jl index 83422be7..7ed99d7d 100644 --- a/TypedSyntax/src/show.jl +++ b/TypedSyntax/src/show.jl @@ -87,6 +87,7 @@ end function is_show_annotation(@nospecialize(T); type_annotations::Bool, hide_type_stable::Bool) type_annotations || return false if isa(T, Core.Const) + isa(T.val, Module) && return false T = Core.Typeof(T.val) end isa(T, Type) || return false @@ -98,9 +99,14 @@ end # We use `endswith` to handle module qualification is_type_transparent(node, @nospecialize(T)) = endswith(replace(sprint(show, T), r"\s" => ""), replace(sourcetext(node), r"\s" => "")) -function is_callfunc(node::TypedSyntaxNode, @nospecialize(T)) +function is_callfunc(node::MaybeTypedSyntaxNode, @nospecialize(T)) + thisnode = node pnode = node.parent - if pnode !== nothing && kind(pnode) ∈ (K"call", K"curly") && ((is_infix_op_call(pnode) && is_operator(node)) || node === pnode.children[1]) + while pnode !== nothing && kind(pnode) ∈ KSet"quote ." && pnode.parent !== nothing + thisnode = pnode + pnode = pnode.parent + end + if pnode !== nothing && kind(pnode) ∈ (K"call", K"curly") && ((is_infix_op_call(pnode) && is_operator(thisnode)) || thisnode === pnode.children[1]) if isa(T, Core.Const) T = T.val end diff --git a/TypedSyntax/test/runtests.jl b/TypedSyntax/test/runtests.jl index 91c25cc7..ec523a2f 100644 --- a/TypedSyntax/test/runtests.jl +++ b/TypedSyntax/test/runtests.jl @@ -625,6 +625,16 @@ include("test_module.jl") printstyled(io, obj; hide_type_stable=false) end @test !occursin("Core.Const", str) + tsn = TypedSyntaxNode(TSN.calls_helper1, (Float32,)) + str = sprint(tsn; context=:color=>false) do io, obj + printstyled(io, obj; hide_type_stable=false) + end + @test !occursin("Core.Const", str) + tsn = TypedSyntaxNode(TSN.calls_helper2, (Float32,)) + str = sprint(tsn; context=:color=>false) do io, obj + printstyled(io, obj; hide_type_stable=false) + end + @test !occursin("Core.Const", str) # issue #413 @test TypedSyntax.is_small_union_or_tunion(Union{}) diff --git a/TypedSyntax/test/test_module.jl b/TypedSyntax/test/test_module.jl index 650f18f7..01394912 100644 --- a/TypedSyntax/test/test_module.jl +++ b/TypedSyntax/test/test_module.jl @@ -250,8 +250,13 @@ end module Internal export helper helper(x) = x+1 +module MoreInternal +helper2(x) = x+2 +end end using .Internal calls_helper(x) = helper(x) +calls_helper1(x) = Internal.helper(x) +calls_helper2(x) = Internal.MoreInternal.helper2(x) end From 0f54116330ca27e2a313c2930e90da2ece7a2dbd Mon Sep 17 00:00:00 2001 From: Tim Holy Date: Thu, 8 Aug 2024 06:04:44 -0500 Subject: [PATCH 11/11] Don't annotate Colon() --- TypedSyntax/src/show.jl | 1 + TypedSyntax/test/runtests.jl | 6 ++++++ TypedSyntax/test/test_module.jl | 2 ++ 3 files changed, 9 insertions(+) diff --git a/TypedSyntax/src/show.jl b/TypedSyntax/src/show.jl index 7ed99d7d..b4197b3e 100644 --- a/TypedSyntax/src/show.jl +++ b/TypedSyntax/src/show.jl @@ -111,6 +111,7 @@ function is_callfunc(node::MaybeTypedSyntaxNode, @nospecialize(T)) T = T.val end if isa(T, Type) || isa(T, Function) + T === Colon() && sourcetext(node) == ":" && return true return is_type_transparent(node, T) end end diff --git a/TypedSyntax/test/runtests.jl b/TypedSyntax/test/runtests.jl index ec523a2f..b1677ea9 100644 --- a/TypedSyntax/test/runtests.jl +++ b/TypedSyntax/test/runtests.jl @@ -635,6 +635,12 @@ include("test_module.jl") printstyled(io, obj; hide_type_stable=false) end @test !occursin("Core.Const", str) + tsn = TypedSyntaxNode(TSN.allbutfirst, (Vector{Bool},)) + str = sprint(tsn; context=:color=>false) do io, obj + printstyled(io, obj; hide_type_stable=false) + end + @test occursin("2:end", str) + # issue #413 @test TypedSyntax.is_small_union_or_tunion(Union{}) diff --git a/TypedSyntax/test/test_module.jl b/TypedSyntax/test/test_module.jl index 01394912..64fe596a 100644 --- a/TypedSyntax/test/test_module.jl +++ b/TypedSyntax/test/test_module.jl @@ -259,4 +259,6 @@ calls_helper(x) = helper(x) calls_helper1(x) = Internal.helper(x) calls_helper2(x) = Internal.MoreInternal.helper2(x) +allbutfirst(list) = list[2:end] + end