Skip to content

Commit a5f4152

Browse files
committed
Merge branch 'master' into envs
2 parents 168eafe + 2ce4ea5 commit a5f4152

20 files changed

+1228
-260
lines changed

.github/pull_request_template.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
Fixes #.
2+
3+
For every PR, please check the following:
4+
- [ ] End-user documentation check. If this PR requires end-user documentation in the Julia VS Code extension docs, please add that at https://github.com/julia-vscode/docs.
5+
- [ ] Changelog mention. If this PR should be mentioned in the CHANGELOG for the Julia VS Code extension, please open a PR against https://github.com/julia-vscode/julia-vscode/blob/master/CHANGELOG.md with those changes.

.github/workflows/jlpkgbutler-ci-master-workflow.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ jobs:
1010
runs-on: ${{ matrix.os }}
1111
strategy:
1212
matrix:
13-
julia-version: ['1.0', '1.1', '1.2', '1.3', '1.4', '1.5']
13+
julia-version: ['1.0', '1.1', '1.2', '1.3', '1.4', '1.5', '1.6']
1414
julia-arch: [x64, x86]
1515
os: [ubuntu-latest, windows-latest, macOS-latest]
1616
exclude:

.github/workflows/jlpkgbutler-ci-pr-workflow.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ jobs:
99
runs-on: ${{ matrix.os }}
1010
strategy:
1111
matrix:
12-
julia-version: ['1.0', '1.1', '1.2', '1.3', '1.4', '1.5']
12+
julia-version: ['1.0', '1.1', '1.2', '1.3', '1.4', '1.5', '1.6']
1313
julia-arch: [x64, x86]
1414
os: [ubuntu-latest, windows-latest, macOS-latest]
1515
exclude:

.github/workflows/jlpkgbutler-codeformat-pr-workflow.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ jobs:
1212
- uses: actions/checkout@v2
1313
- uses: julia-actions/julia-codeformat@releases/v1
1414
- name: Create Pull Request
15-
uses: peter-evans/create-pull-request@v2
15+
uses: peter-evans/create-pull-request@v3
1616
with:
1717
token: ${{ secrets.GITHUB_TOKEN }}
1818
commit-message: Format files using DocumentFormat

src/StaticLint.jl

Lines changed: 24 additions & 30 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}
@@ -63,7 +42,7 @@ bindingof(m::Meta) = m.binding
6342
"""
6443
ExternalEnv
6544
66-
Holds a representation of an environment cached by SymbolServer.
45+
Holds a representation of an environment cached by SymbolServer.
6746
"""
6847
mutable struct ExternalEnv
6948
symbols::SymbolServer.EnvStore
@@ -93,7 +72,7 @@ function (state::Toplevel)(x::EXPR)
9372
s0 = scopes(x, state)
9473
resolve_ref(x, state)
9574
followinclude(x, state)
96-
75+
9776
old_in_modified_expr = state.in_modified_expr
9877
if state.modified_exprs !== nothing && x in state.modified_exprs
9978
state.in_modified_expr = true
@@ -107,7 +86,7 @@ function (state::Toplevel)(x::EXPR)
10786
else
10887
traverse(x, state)
10988
end
110-
89+
11190
state.in_modified_expr = old_in_modified_expr
11291
state.scope != s0 && (state.scope = s0)
11392
return state.scope
@@ -129,8 +108,9 @@ function (state::Delayed)(x::EXPR)
129108

130109
traverse(x, state)
131110
if state.scope != s0
132-
for (k, b) in state.scope.names
111+
for b in values(state.scope.names)
133112
infer_type_by_use(b, state.env)
113+
check_unused_binding(b, state.scope)
134114
end
135115
state.scope = s0
136116
end
@@ -173,9 +153,10 @@ function semantic_pass(file, modified_expr=nothing)
173153
state(getcst(file))
174154
for x in state.delayed
175155
if hasscope(x)
176-
traverse(x, Delayed(scopeof(x), env, server))
156+
traverse(x, Delayed(scopeof(x), env, server))
177157
for (k, b) in scopeof(x).names
178158
infer_type_by_use(b, env)
159+
check_unused_binding(b, scopeof(x))
179160
end
180161
else
181162
traverse(x, Delayed(retrieve_delayed_scope(x), env, server))
@@ -298,12 +279,21 @@ the path of the file to be included. Has limited support for `joinpath` calls.
298279
function get_path(x::EXPR, state)
299280
if CSTParser.iscall(x) && length(x.args) == 2
300281
parg = x.args[2]
282+
301283
if CSTParser.isstringliteral(parg)
284+
if occursin("\0", valof(parg))
285+
seterror!(parg, IncludePathContainsNULL)
286+
return ""
287+
end
302288
path = CSTParser.str_value(parg)
303289
path = normpath(path)
304290
Base.containsnul(path) && throw(SLInvalidPath("Couldn't convert '$x' into a valid path. Got '$path'"))
305291
return path
306292
elseif CSTParser.ismacrocall(parg) && valof(parg.args[1]) == "@raw_str" && CSTParser.isstringliteral(parg.args[3])
293+
if occursin("\0", valof(parg.args[3]))
294+
seterror!(parg.args[3], IncludePathContainsNULL)
295+
return ""
296+
end
307297
path = normpath(CSTParser.str_value(parg.args[3]))
308298
Base.containsnul(path) && throw(SLInvalidPath("Couldn't convert '$x' into a valid path. Got '$path'"))
309299
return path
@@ -315,7 +305,11 @@ function get_path(x::EXPR, state)
315305
if _is_macrocall_to_BaseDIR(arg) # Assumes @__DIR__ points to Base macro.
316306
push!(path_elements, dirname(getpath(state.file)))
317307
elseif CSTParser.isstringliteral(arg)
318-
push!(path_elements, string(valofid(arg)))
308+
if occursin("\0", valof(arg))
309+
seterror!(arg, IncludePathContainsNULL)
310+
return ""
311+
end
312+
push!(path_elements, string(valof(arg)))
319313
else
320314
return ""
321315
end

src/bindings.jl

Lines changed: 44 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -140,15 +140,29 @@ function mark_binding!(x::EXPR, val=x)
140140
return x
141141
end
142142

143-
144-
function mark_parameters(sig::EXPR)
145-
signame = CSTParser.rem_where_subtype(sig)
146-
if CSTParser.iscurly(signame)
147-
for i = 2:length(signame.args)
148-
mark_binding!(signame.args[i])
143+
function mark_parameters(sig::EXPR, params = String[])
144+
if CSTParser.issubtypedecl(sig)
145+
mark_parameters(sig.args[1], params)
146+
elseif iswhere(sig)
147+
for i = 2:length(sig.args)
148+
x = mark_binding!(sig.args[i])
149+
val = valof(bindingof(x).name)
150+
if val isa String
151+
push!(params, val)
152+
end
153+
end
154+
mark_parameters(sig.args[1], params)
155+
elseif CSTParser.iscurly(sig)
156+
for i = 2:length(sig.args)
157+
x = mark_binding!(sig.args[i])
158+
if bindingof(x) isa Binding && valof(bindingof(x).name) in params
159+
# Don't mark a new binding if a parameter has already been
160+
# introduced from a :where
161+
x.meta.binding = nothing
162+
end
149163
end
150164
end
151-
return sig
165+
sig
152166
end
153167

154168

@@ -231,18 +245,15 @@ function is_in_funcdef(x)
231245
end
232246
end
233247

234-
function _in_func_def(x::EXPR)
248+
rem_wheres_subs_decls(x::EXPR) = (iswhere(x) || isdeclaration(x) || CSTParser.issubtypedecl(x)) ? rem_wheres_subs_decls(x.args[1]) : x
249+
250+
function _in_func_or_struct_def(x::EXPR)
235251
# only called in :where
236252
# check 1st arg contains a call (or op call)
237-
ex = CSTParser.rem_wheres_decls(x.args[1])
238-
239-
!(CSTParser.iscall(ex) || CSTParser.is_getfield(ex) || CSTParser.isunarycall(ex)) && return false
240-
241-
# check parent is func def
242-
return is_in_funcdef(x)
253+
ex = rem_wheres_subs_decls(x.args[1])
254+
is_in_fexpr(x, CSTParser.defines_struct) || ((CSTParser.iscall(ex) || CSTParser.is_getfield(ex) || CSTParser.isunarycall(ex)) && is_in_funcdef(x))
243255
end
244256

245-
246257
"""
247258
add_binding(x, state, scope=state.scope)
248259
@@ -307,14 +318,22 @@ function add_binding(x, state, scope=state.scope)
307318
# Mark as overloaded so that calls to `M.f()` resolve properly.
308319
overload_method(tls, b, VarRef(lhs_ref.name, Symbol(name))) # Add to overloaded list but not scope.
309320
end
310-
elseif lhs_ref isa Binding && lhs_ref.type == CoreTypes.Module
321+
elseif lhs_ref isa Binding && CoreTypes.ismodule(lhs_ref.type)
311322
if hasscope(lhs_ref.val) && haskey(scopeof(lhs_ref.val).names, name)
312323
# Don't need to do anything, name will resolve
313324
end
314325
end
315326
else
316327
if scopehasbinding(tls, name)
317-
if tls.names[name] isa Binding && ((tls.names[name].type == CoreTypes.Function || tls.names[name].type == CoreTypes.DataType) || tls.names[name] isa SymbolServer.FunctionStore || tls.names[name] isa SymbolServer.DataTypeStore)
328+
329+
existing_binding = tls.names[name]
330+
if existing_binding isa Binding && (existing_binding.val isa Binding || existing_binding.val isa SymbolServer.FunctionStore || existing_binding.val isa SymbolServer.DataTypeStore)
331+
# Should possibly be a while statement
332+
# If the .val is as above the Binding likely won't have a proper type attached
333+
# so lets use the .val instead.
334+
existing_binding = existing_binding.val
335+
end
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)
318337
# do nothing name of `x` will resolve to the root method
319338
else
320339
seterror!(x, CannotDefineFuncAlreadyHasValue)
@@ -333,7 +352,10 @@ function add_binding(x, state, scope=state.scope)
333352
elseif scopehasbinding(scope, name)
334353
# TODO: some checks about rebinding of consts
335354
check_const_decl(name, b, scope)
355+
336356
scope.names[name] = b
357+
elseif is_soft_scope(scope) && parentof(scope) isa Scope && isidentifier(b.name) && scopehasbinding(parentof(scope), valofid(b.name)) && !enforce_hard_scope(x, scope)
358+
add_binding(x, state, scope.parent)
337359
else
338360
scope.names[name] = b
339361
end
@@ -343,6 +365,10 @@ function add_binding(x, state, scope=state.scope)
343365
end
344366
end
345367

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+
346372
name_is_getfield(x) = parentof(x) isa EXPR && parentof(parentof(x)) isa EXPR && CSTParser.is_getfield_w_quotenode(parentof(parentof(x)))
347373

348374

@@ -372,7 +398,7 @@ function mark_globals(x::EXPR, state)
372398
end
373399

374400
function name_extends_imported_method(b::Binding)
375-
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))
376402
if refof_maybe_getfield(parentof(b.name)[1]) !== nothing
377403

378404
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/imports.jl

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,17 @@
11
function resolve_import_block(x::EXPR, state::State, root, usinged, markfinal=true)
2+
if x.head == :as
3+
resolve_import_block(x.args[1], state, root, usinged, markfinal)
4+
if x.args[2].meta === nothing
5+
x.args[2].meta = Meta()
6+
end
7+
if hasbinding(last(x.args[1].args))
8+
lhsbinding = bindingof(last(x.args[1].args))
9+
x.args[2].meta.binding = Binding(x.args[2], lhsbinding.val, lhsbinding.type, lhsbinding.refs)
10+
setref!(x.args[2], bindingof(x.args[2]))
11+
last(x.args[1].args).meta.binding = nothing
12+
end
13+
return
14+
end
215
n = length(x.args)
316
for i = 1:length(x.args)
417
arg = x.args[i]
@@ -75,10 +88,13 @@ function add_to_imported_modules(scope::Scope, name::Symbol, val)
7588
if scope.modules isa Dict
7689
scope.modules[name] = val
7790
else
78-
modules = Dict(name => val)
91+
Dict(name => val)
7992
end
8093
end
8194
no_modules_above(s::Scope) = !CSTParser.defines_module(s.expr) || s.parent === nothing || no_modules_above(s.parent)
95+
function get_named_toplevel_module(s, name)
96+
return nothing
97+
end
8298
function get_named_toplevel_module(s::Scope, name::String)
8399
if CSTParser.defines_module(s.expr)
84100
m_name = CSTParser.get_name(s.expr)

src/interface.jl

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,17 +31,17 @@ end
3131
"""
3232
lint_file(rootpath, server)
3333
34-
Read a file from disc, parse and run a semantic pass over it. The file should be the
34+
Read a file from disc, parse and run a semantic pass over it. The file should be the
3535
root of a project, e.g. for this package that file is `src/StaticLint.jl`. Other files
3636
in the project will be loaded automatically (calls to `include` with complicated arguments
37-
are not handled, see `followinclude` for details). A `FileServer` will be returned
37+
are not handled, see `followinclude` for details). A `FileServer` will be returned
3838
containing the `File`s of the package.
3939
"""
4040
function lint_file(rootpath, server = setup_server(); gethints = false)
4141
empty!(server.files)
4242
root = loadfile(server, rootpath)
4343
semantic_pass(root)
44-
for (p,f) in server.files
44+
for f in values(server.files)
4545
check_all(f.cst, LintOptions(), getenv(f, server))
4646
end
4747
if gethints

0 commit comments

Comments
 (0)