Skip to content

Commit 2ce4ea5

Browse files
authored
Merge pull request #263 from julia-vscode/type-inf-improvements
Type inf improvements
2 parents 397ce84 + 842ab88 commit 2ce4ea5

File tree

13 files changed

+648
-113
lines changed

13 files changed

+648
-113
lines changed

src/StaticLint.jl

Lines changed: 5 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -11,32 +11,11 @@ using SymbolServer: VarRef
1111

1212
const noname = EXPR(:noname, nothing, nothing, 0, 0, nothing, nothing, nothing)
1313

14-
baremodule CoreTypes # Convenience
15-
using ..SymbolServer
16-
using Base: ==, @static
17-
18-
const DataType = SymbolServer.stdlibs[:Core][:DataType]
19-
const Function = SymbolServer.stdlibs[:Core][:Function]
20-
const Module = SymbolServer.stdlibs[:Core][:Module]
21-
const String = SymbolServer.stdlibs[:Core][:String]
22-
const Symbol = SymbolServer.stdlibs[:Core][:Symbol]
23-
const Int = SymbolServer.stdlibs[:Core][:Int]
24-
const Float64 = SymbolServer.stdlibs[:Core][:Float64]
25-
const Vararg = SymbolServer.FakeTypeName(Core.Vararg)
26-
27-
isva(x::SymbolServer.FakeUnionAll) = isva(x.body)
28-
@static if Core.Vararg isa Core.Type
29-
function isva(x)
30-
return (x isa SymbolServer.FakeTypeName && x.name.name == :Vararg &&
31-
x.name.parent isa SymbolServer.VarRef && x.name.parent.name == :Core)
32-
end
33-
else
34-
isva(x) = x isa SymbolServer.FakeTypeofVararg
35-
end
36-
end
37-
14+
include("coretypes.jl")
3815
include("bindings.jl")
3916
include("scope.jl")
17+
include("subtypes.jl")
18+
include("methodmatching.jl")
4019

4120
mutable struct Meta
4221
binding::Union{Nothing,Binding}
@@ -115,7 +94,7 @@ function (state::Delayed)(x::EXPR)
11594

11695
traverse(x, state)
11796
if state.scope != s0
118-
for (k, b) in state.scope.names
97+
for b in values(state.scope.names)
11998
infer_type_by_use(b, state.server)
12099
check_unused_binding(b, state.scope)
121100
end
@@ -159,7 +138,7 @@ function semantic_pass(file, modified_expr=nothing)
159138
for x in state.delayed
160139
if hasscope(x)
161140
traverse(x, Delayed(scopeof(x), server))
162-
for (k, b) in scopeof(x).names
141+
for b in values(scopeof(x).names)
163142
infer_type_by_use(b, state.server)
164143
check_unused_binding(b, scopeof(x))
165144
end

src/bindings.jl

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -318,21 +318,22 @@ function add_binding(x, state, scope=state.scope)
318318
# Mark as overloaded so that calls to `M.f()` resolve properly.
319319
overload_method(tls, b, VarRef(lhs_ref.name, Symbol(name))) # Add to overloaded list but not scope.
320320
end
321-
elseif lhs_ref isa Binding && lhs_ref.type == CoreTypes.Module
321+
elseif lhs_ref isa Binding && CoreTypes.ismodule(lhs_ref.type)
322322
if hasscope(lhs_ref.val) && haskey(scopeof(lhs_ref.val).names, name)
323323
# Don't need to do anything, name will resolve
324324
end
325325
end
326326
else
327327
if scopehasbinding(tls, name)
328+
328329
existing_binding = tls.names[name]
329330
if existing_binding isa Binding && (existing_binding.val isa Binding || existing_binding.val isa SymbolServer.FunctionStore || existing_binding.val isa SymbolServer.DataTypeStore)
330331
# Should possibly be a while statement
331332
# If the .val is as above the Binding likely won't have a proper type attached
332333
# so lets use the .val instead.
333334
existing_binding = existing_binding.val
334335
end
335-
if (existing_binding isa Binding && ((existing_binding.type == CoreTypes.Function || existing_binding.type == CoreTypes.DataType)) || existing_binding isa SymbolServer.FunctionStore || existing_binding isa SymbolServer.DataTypeStore)
336+
if (existing_binding isa Binding && ((CoreTypes.isfunction(existing_binding.type) || CoreTypes.isdatatype(existing_binding.type))) || existing_binding isa SymbolServer.FunctionStore || existing_binding isa SymbolServer.DataTypeStore)
336337
# do nothing name of `x` will resolve to the root method
337338
else
338339
seterror!(x, CannotDefineFuncAlreadyHasValue)
@@ -353,7 +354,7 @@ function add_binding(x, state, scope=state.scope)
353354
check_const_decl(name, b, scope)
354355

355356
scope.names[name] = b
356-
elseif is_soft_scope(scope) && parentof(scope) isa Scope && isidentifier(b.name) && scopehasbinding(parentof(scope), valofid(b.name))
357+
elseif is_soft_scope(scope) && parentof(scope) isa Scope && isidentifier(b.name) && scopehasbinding(parentof(scope), valofid(b.name)) && !enforce_hard_scope(x, scope)
357358
add_binding(x, state, scope.parent)
358359
else
359360
scope.names[name] = b
@@ -364,6 +365,10 @@ function add_binding(x, state, scope=state.scope)
364365
end
365366
end
366367

368+
function enforce_hard_scope(x::EXPR, scope)
369+
scope.expr.head === :for && is_in_fexpr(x, x-> x == scope.expr.args[1])
370+
end
371+
367372
name_is_getfield(x) = parentof(x) isa EXPR && parentof(parentof(x)) isa EXPR && CSTParser.is_getfield_w_quotenode(parentof(parentof(x)))
368373

369374

@@ -393,7 +398,7 @@ function mark_globals(x::EXPR, state)
393398
end
394399

395400
function name_extends_imported_method(b::Binding)
396-
if b.type == CoreTypes.Function && CSTParser.hasparent(b.name) && CSTParser.is_getfield(parentof(b.name))
401+
if CoreTypes.isfunction(b.type) && CSTParser.hasparent(b.name) && CSTParser.is_getfield(parentof(b.name))
397402
if refof_maybe_getfield(parentof(b.name)[1]) !== nothing
398403

399404
end

src/coretypes.jl

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
baremodule CoreTypes # Convenience
2+
using ..SymbolServer
3+
using Base: ==, @static
4+
5+
const Any = SymbolServer.stdlibs[:Core][:Any]
6+
const DataType = SymbolServer.stdlibs[:Core][:DataType]
7+
const Function = SymbolServer.stdlibs[:Core][:Function]
8+
const Module = SymbolServer.stdlibs[:Core][:Module]
9+
const String = SymbolServer.stdlibs[:Core][:String]
10+
const Char = SymbolServer.stdlibs[:Core][:Char]
11+
const Symbol = SymbolServer.stdlibs[:Core][:Symbol]
12+
const Bool = SymbolServer.stdlibs[:Core][:Bool]
13+
const Int = SymbolServer.stdlibs[:Core][:Int]
14+
const UInt8 = SymbolServer.stdlibs[:Core][:UInt8]
15+
const UInt16 = SymbolServer.stdlibs[:Core][:UInt16]
16+
const UInt32 = SymbolServer.stdlibs[:Core][:UInt32]
17+
const UInt64 = SymbolServer.stdlibs[:Core][:UInt64]
18+
const Float64 = SymbolServer.stdlibs[:Core][:Float64]
19+
const Vararg = SymbolServer.FakeTypeName(Core.Vararg)
20+
21+
iscoretype(x, name) = false
22+
iscoretype(x::SymbolServer.VarRef, name) = x isa SymbolServer.DataTypeStore && x.name.name == name && x.name isa SymbolServer.VarRef && x.name.parent.name == :Core
23+
iscoretype(x::SymbolServer.DataTypeStore, name) = x isa SymbolServer.DataTypeStore && x.name.name.name == name && x.name.name isa SymbolServer.VarRef && x.name.name.parent.name == :Core
24+
isdatatype(x) = iscoretype(x, :DataType)
25+
isfunction(x) = iscoretype(x, :Function)
26+
ismodule(x) = iscoretype(x, :Module)
27+
isstring(x) = iscoretype(x, :String)
28+
ischar(x) = iscoretype(x, :Char)
29+
issymbol(x) = iscoretype(x, :Symbol)
30+
@static if Core.Int == Core.Int64
31+
isint(x) = iscoretype(x, :Int64)
32+
else
33+
isint(x) = iscoretype(x, :Int32)
34+
end
35+
isfloat(x) = iscoretype(x, :Float64)
36+
isvector(x) = iscoretype(x, :Vector)
37+
isarray(x) = iscoretype(x, :Array)
38+
isva(x::SymbolServer.FakeUnionAll) = isva(x.body)
39+
@static if Core.Vararg isa Core.Type
40+
function isva(x)
41+
return (x isa SymbolServer.FakeTypeName && x.name.name == :Vararg &&
42+
x.name.parent isa SymbolServer.VarRef && x.name.parent.name == :Core)
43+
end
44+
else
45+
isva(x) = x isa SymbolServer.FakeTypeofVararg
46+
end
47+
end

src/interface.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ function lint_file(rootpath, server = setup_server(); gethints = false)
4040
empty!(server.files)
4141
root = StaticLint.loadfile(server, rootpath)
4242
StaticLint.semantic_pass(root)
43-
for (p,f) in server.files
43+
for f in values(server.files)
4444
StaticLint.check_all(f.cst, StaticLint.LintOptions(), server)
4545
end
4646
if gethints

src/linting/checks.jl

Lines changed: 21 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ const LintCodeDescriptions = Dict{LintCodes,String}(IncorrectCallArgs => "Possib
5757
UnsupportedConstLocalVariable => "Unsupported `const` declaration on local variable.",
5858
UnassignedKeywordArgument => "Keyword argument not assigned.",
5959
CannotDefineFuncAlreadyHasValue => "Cannot define function ; it already has a value.",
60-
DuplicateFuncArgName => "Function argnument name not unique.",
60+
DuplicateFuncArgName => "Function argument name not unique.",
6161
IncludePathContainsNULL => "Cannot include file, path cotains NULL characters."
6262
)
6363

@@ -260,8 +260,8 @@ function compare_f_call(
260260
end
261261

262262
function is_something_with_methods(x::Binding)
263-
(x.type == CoreTypes.Function && x.val isa EXPR) ||
264-
(x.type == CoreTypes.DataType && x.val isa EXPR && CSTParser.defines_struct(x.val)) || # Todo: Could have abstract type with a constructor...
263+
(CoreTypes.isfunction(x.type) && x.val isa EXPR) ||
264+
(CoreTypes.isdatatype(x.type) && x.val isa EXPR && CSTParser.defines_struct(x.val)) ||
265265
(x.val isa SymbolServer.FunctionStore || x.val isa SymbolServer.DataTypeStore)
266266
end
267267
is_something_with_methods(x::T) where T <: Union{SymbolServer.FunctionStore,SymbolServer.DataTypeStore} = true
@@ -272,18 +272,10 @@ function check_call(x, server)
272272
parentof(x) isa EXPR && headof(parentof(x)) === :do && return # TODO: add number of args specified in do block.
273273
length(x.args) == 0 && return
274274
# find the function we're dealing with
275-
if isidentifier(first(x.args)) && hasref(first(x.args))
276-
func_ref = refof(first(x.args))
277-
elseif is_getfield_w_quotenode(x.args[1]) && (rhs = rhs_of_getfield(x.args[1])) !== nothing && hasref(rhs)
278-
func_ref = refof(rhs)
279-
else
280-
return
281-
end
275+
func_ref = refof_call_func(x)
276+
func_ref === nothing && return
282277

283-
# if func_ref isa Binding &&
284-
# end
285-
286-
if (func_ref isa Binding && (func_ref.type === CoreTypes.Function || func_ref.type === CoreTypes.DataType) && !(func_ref.val isa EXPR && func_ref.val.head === :macro)) || func_ref isa SymbolServer.FunctionStore || func_ref isa SymbolServer.DataTypeStore
278+
if is_something_with_methods(func_ref) && !(func_ref isa Binding && func_ref.val isa EXPR && func_ref.val.head === :macro)
287279
# intentionally empty
288280
if func_ref isa Binding && func_ref.val isa EXPR && isassignment(func_ref.val) && isidentifier(func_ref.val.args[1]) && isidentifier(func_ref.val.args[2])
289281
# if func_ref is a shadow binding (for these purposes, an assignment that just changes the name of a mehtod), redirect to the rhs of the assignment.
@@ -438,7 +430,7 @@ function is_never_datatype(b::Binding, server)
438430
return is_never_datatype(b.val, server)
439431
elseif b.val isa SymbolServer.FunctionStore
440432
return is_never_datatype(b.val, server)
441-
elseif b.type == CoreTypes.DataType
433+
elseif CoreTypes.isdatatype(b.type)
442434
return false
443435
elseif b.type !== nothing
444436
return true
@@ -502,6 +494,10 @@ function check_farg_unused_(arg, arg_names)
502494
return false
503495
end
504496
b = bindingof(arg)
497+
498+
# We don't care about these
499+
valof(b.name) isa String && all_underscore(valof(b.name)) && return false
500+
505501
if b === nothing ||
506502
# no refs:
507503
isempty(b.refs) ||
@@ -633,7 +629,7 @@ end
633629
function has_getproperty_method(b::Binding)
634630
if b.val isa Binding || b.val isa SymbolServer.DataTypeStore
635631
return has_getproperty_method(b.val)
636-
elseif b isa Binding && b.type === CoreTypes.DataType
632+
elseif b isa Binding && CoreTypes.isdatatype(b.type)
637633
for ref in b.refs
638634
if ref isa EXPR && is_type_of_call_to_getproperty(ref)
639635
return true
@@ -738,7 +734,10 @@ function check_const_decl(name::String, b::Binding, scope)
738734
seterror!(b.val, CannotDeclareConst)
739735
else
740736
prev = scope.names[name]
741-
if (prev.type === CoreTypes.DataType && !is_mask_binding_of_datatype(prev)) || is_const(prev)
737+
if (CoreTypes.isdatatype(prev.type) && !is_mask_binding_of_datatype(prev)) || is_const(prev)
738+
if b.val isa EXPR && prev.val isa EXPR && !in_same_if_branch(b.val, prev.val)
739+
return
740+
end
742741
if b.val isa EXPR
743742
seterror!(b.val, InvalidRedefofConst)
744743
else
@@ -752,12 +751,12 @@ end
752751
function is_mask_binding_of_datatype(b::Binding)
753752
b.val isa EXPR && CSTParser.isassignment(b.val) && (rhsref = refof(b.val.args[2])) !== nothing && (rhsref isa SymbolServer.DataTypeStore || (rhsref.val isa EXPR && rhsref.val isa SymbolServer.DataTypeStore) || (rhsref.val isa EXPR && CSTParser.defines_datatype(rhsref.val)))
754753
end
754+
755755
# check whether a and b are in all the same :if blocks and in the same branches
756-
function in_same_if_branch(a, b)
757-
a_branches = find_if_parents(a)
758-
b_branches = find_if_parents(b)
759-
760-
return length(a_branches) == length(b_branches) && all(k in keys(b_branches) for k in keys(a_branches)) && all(a_branches[k] == b_branches[k] for k in keys(a_branches))
756+
in_same_if_branch(a::EXPR, b::EXPR) = in_same_if_branch(find_if_parents(a), find_if_parents(b))
757+
in_same_if_branch(a::Dict, b::EXPR) = in_same_if_branch(a, find_if_parents(b))
758+
function in_same_if_branch(a::Dict, b::Dict)
759+
return length(a) == length(b) && all(k in keys(b) for k in keys(a)) && all(a[k] == b[k] for k in keys(a))
761760
end
762761

763762
# find any parent nodes that are :if blocks and a pseudo-index of which branch

0 commit comments

Comments
 (0)