diff --git a/docs/make.jl b/docs/make.jl index 77308142..f0476fa5 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -7,16 +7,16 @@ makedocs(; repo="https://github.com/julia-vscode/StaticLint.jl/blob/{commit}{path}#L{line}", sitename="StaticLint.jl", format=Documenter.HTML(; - prettyurls=prettyurls = get(ENV, "CI", nothing) == "true", + prettyurls=prettyurls = get(ENV, "CI", nothing) == "true" # canonical="https://www.julia-vscode.org/StaticLint.jl", # assets=String[], ), pages=[ "Home" => "index.md", "Syntax Reference" => "syntax.md", - ], + ] ) deploydocs(; - repo="github.com/julia-vscode/StaticLint.jl", + repo="github.com/julia-vscode/StaticLint.jl" ) diff --git a/src/StaticLint.jl b/src/StaticLint.jl index c13028e1..e4ff9b97 100644 --- a/src/StaticLint.jl +++ b/src/StaticLint.jl @@ -29,9 +29,9 @@ Meta() = Meta(nothing, nothing, nothing, nothing) function Base.show(io::IO, m::Meta) m.binding !== nothing && show(io, m.binding) - m.ref !== nothing && printstyled(io, " * ", color = :red) - m.scope !== nothing && printstyled(io, " new scope", color = :green) - m.error !== nothing && printstyled(io, " lint ", color = :red) + m.ref !== nothing && printstyled(io, " * ", color=:red) + m.scope !== nothing && printstyled(io, " new scope", color=:green) + m.error !== nothing && printstyled(io, " lint ", color=:red) end hasmeta(x::EXPR) = x.meta isa Meta hasbinding(m::Meta) = m.binding isa Binding @@ -168,7 +168,7 @@ end Performs a semantic pass across a project from the entry point `file`. A first pass traverses the top-level scope after which secondary passes handle delayed scopes (e.g. functions). These secondary passes can be, optionally, very light and only seek to resovle references (e.g. link symbols to bindings). This can be done by supplying a list of expressions on which the full secondary pass should be made (`modified_expr`), all others will receive the light-touch version. """ -function semantic_pass(file, modified_expr = nothing) +function semantic_pass(file, modified_expr=nothing) server = file.server env = getenv(file, server) setscope!(getcst(file), Scope(nothing, getcst(file), Dict(), Dict{Symbol,Any}(:Base => env.symbols[:Base], :Core => env.symbols[:Core]), nothing)) diff --git a/src/bindings.jl b/src/bindings.jl index f7a7dcc8..f49850a8 100644 --- a/src/bindings.jl +++ b/src/bindings.jl @@ -139,7 +139,7 @@ function mark_binding!(x::EXPR, val=x) return x end -function mark_parameters(sig::EXPR, params = String[]) +function mark_parameters(sig::EXPR, params=String[]) if CSTParser.issubtypedecl(sig) mark_parameters(sig.args[1], params) elseif iswhere(sig) @@ -365,7 +365,7 @@ function add_binding(x, state, scope=state.scope) end function enforce_hard_scope(x::EXPR, scope) - scope.expr.head === :for && is_in_fexpr(x, x-> x == scope.expr.args[1]) + scope.expr.head === :for && is_in_fexpr(x, x -> x == scope.expr.args[1]) end name_is_getfield(x) = parentof(x) isa EXPR && parentof(parentof(x)) isa EXPR && CSTParser.is_getfield_w_quotenode(parentof(parentof(x))) diff --git a/src/coretypes.jl b/src/coretypes.jl index 293f7a16..7bea49e3 100644 --- a/src/coretypes.jl +++ b/src/coretypes.jl @@ -39,7 +39,7 @@ isva(x::SymbolServer.FakeUnionAll) = isva(x.body) @static if Core.Vararg isa Core.Type function isva(x) return (x isa SymbolServer.FakeTypeName && x.name.name == :Vararg && - x.name.parent isa SymbolServer.VarRef && x.name.parent.name == :Core) + x.name.parent isa SymbolServer.VarRef && x.name.parent.name == :Core) end else isva(x) = x isa SymbolServer.FakeTypeofVararg diff --git a/src/exception_types.jl b/src/exception_types.jl index 6f1df07c..e274f571 100644 --- a/src/exception_types.jl +++ b/src/exception_types.jl @@ -4,4 +4,4 @@ end function Base.showerror(io::IO, ex::SLInvalidPath) print(io, ex.msg) -end \ No newline at end of file +end diff --git a/src/imports.jl b/src/imports.jl index a23c92e3..74a8e302 100644 --- a/src/imports.jl +++ b/src/imports.jl @@ -86,11 +86,11 @@ end function has_workspace_package(server, name) haskey(server.workspacepackages, name) && - hasscope(getcst(server.workspacepackages[name])) && - haskey(scopeof(getcst(server.workspacepackages[name])).names, name) && - scopeof(getcst(server.workspacepackages[name])).names[name] isa Binding && - scopeof(getcst(server.workspacepackages[name])).names[name].val isa EXPR && - CSTParser.defines_module(scopeof(getcst(server.workspacepackages[name])).names[name].val) + hasscope(getcst(server.workspacepackages[name])) && + haskey(scopeof(getcst(server.workspacepackages[name])).names, name) && + scopeof(getcst(server.workspacepackages[name])).names[name] isa Binding && + scopeof(getcst(server.workspacepackages[name])).names[name].val isa EXPR && + CSTParser.defines_module(scopeof(getcst(server.workspacepackages[name])).names[name].val) end function add_to_imported_modules(scope::Scope, name::Symbol, val) @@ -121,8 +121,8 @@ function _get_field(par, arg, state) if par isa SymbolServer.EnvStore if (arg_scope = retrieve_scope(arg)) !== nothing && (tlm = get_named_toplevel_module(arg_scope, arg_str_rep)) !== nothing && hasbinding(tlm) return bindingof(tlm) - # elseif has_workspace_package(state.server, arg_str_rep) - # return scopeof(getcst(state.server.workspacepackages[arg_str_rep])).names[arg_str_rep] + # elseif has_workspace_package(state.server, arg_str_rep) + # return scopeof(getcst(state.server.workspacepackages[arg_str_rep])).names[arg_str_rep] elseif haskey(par, Symbol(arg_str_rep)) if isempty(state.env.project_deps) || Symbol(arg_str_rep) in state.env.project_deps return par[Symbol(arg_str_rep)] diff --git a/src/interface.jl b/src/interface.jl index 94ff4ee1..55032295 100644 --- a/src/interface.jl +++ b/src/interface.jl @@ -1,4 +1,4 @@ -function setup_server(env = dirname(SymbolServer.Pkg.Types.Context().env.project_file), depot = first(SymbolServer.Pkg.depots()), cache = joinpath(dirname(pathof(SymbolServer)), "..", "store")) +function setup_server(env=dirname(SymbolServer.Pkg.Types.Context().env.project_file), depot=first(SymbolServer.Pkg.depots()), cache=joinpath(dirname(pathof(SymbolServer)), "..", "store")) server = StaticLint.FileServer() ssi = SymbolServerInstance(depot, cache) _, symbols = SymbolServer.getstore(ssi, env) @@ -14,7 +14,7 @@ Parse a string and run a semantic pass over it. This will mark scopes, bindings, references, and lint hints. An annotated `EXPR` is returned or, if `gethints = true`, it is paired with a collected list of errors/hints. """ -function lint_string(s::String, server = setup_server(); gethints = false) +function lint_string(s::String, server=setup_server(); gethints=false) empty!(server.files) f = File("", s, CSTParser.parse(s, true), nothing, server) env = getenv(f, server) @@ -38,7 +38,7 @@ in the project will be loaded automatically (calls to `include` with complicated are not handled, see `followinclude` for details). A `FileServer` will be returned containing the `File`s of the package. """ -function lint_file(rootpath, server = setup_server(); gethints = false) +function lint_file(rootpath, server=setup_server(); gethints=false) empty!(server.files) root = loadfile(server, rootpath) semantic_pass(root) @@ -47,7 +47,7 @@ function lint_file(rootpath, server = setup_server(); gethints = false) end if gethints hints = [] - for (p,f) in server.files + for (p, f) in server.files append!(hints, [(x, string(haserror(x) ? LintCodeDescriptions[x.meta.error] : "Missing reference", " at offset ", offset, " of ", p)) for (offset, x) in collect_hints(f.cst, getenv(f, server))]) end return root, hints diff --git a/src/linting/checks.jl b/src/linting/checks.jl index 75f07c19..65d70979 100644 --- a/src/linting/checks.jl +++ b/src/linting/checks.jl @@ -1,7 +1,5 @@ @enum( - LintCodes, - - MissingRef, + LintCodes, MissingRef, IncorrectCallArgs, IncorrectIterSpec, NothingEquality, @@ -181,9 +179,9 @@ function func_nargs(x::EXPR) maxargs !== typemax(Int) && (maxargs += 1) end elseif issplat(arg) || - (isdeclaration(arg) && - ((isidentifier(arg.args[2]) && valofid(arg.args[2]) == "Vararg") || - (iscurly(arg.args[2]) && isidentifier(arg.args[2].args[1]) && valofid(arg.args[2].args[1]) == "Vararg"))) + (isdeclaration(arg) && + ((isidentifier(arg.args[2]) && valofid(arg.args[2]) == "Vararg") || + (iscurly(arg.args[2]) && isidentifier(arg.args[2].args[1]) && valofid(arg.args[2].args[1]) == "Vararg"))) maxargs = typemax(Int) else minargs += 1 @@ -247,9 +245,9 @@ end # compare_f_call(m_counts, call_counts) = true # fallback method function compare_f_call( - (ref_minargs, ref_maxargs, ref_kws, kwsplat), - (act_minargs, act_maxargs, act_kws), - ) + (ref_minargs, ref_maxargs, ref_kws, kwsplat), + (act_minargs, act_maxargs, act_kws), +) # check matching on positional arguments if act_maxargs == typemax(Int) act_minargs <= act_maxargs < ref_minargs && return false @@ -269,10 +267,10 @@ end function is_something_with_methods(x::Binding) (CoreTypes.isfunction(x.type) && x.val isa EXPR) || - (CoreTypes.isdatatype(x.type) && x.val isa EXPR && CSTParser.defines_struct(x.val)) || - (x.val isa SymbolServer.FunctionStore || x.val isa SymbolServer.DataTypeStore) + (CoreTypes.isdatatype(x.type) && x.val isa EXPR && CSTParser.defines_struct(x.val)) || + (x.val isa SymbolServer.FunctionStore || x.val isa SymbolServer.DataTypeStore) end -is_something_with_methods(x::T) where T <: Union{SymbolServer.FunctionStore,SymbolServer.DataTypeStore} = true +is_something_with_methods(x::T) where T<:Union{SymbolServer.FunctionStore,SymbolServer.DataTypeStore} = true is_something_with_methods(x) = false function check_call(x, env::ExternalEnv) @@ -376,13 +374,13 @@ function check_incorrect_iter_spec(x, body, env) if headof(rng) === :FLOAT || headof(rng) === :INTEGER || (iscall(rng) && refof(rng.args[1]) === getsymbols(env)[:Base][:length]) seterror!(x, IncorrectIterSpec) elseif iscall(rng) && valof(rng.args[1]) == ":" && - length(rng.args) === 3 && - headof(rng.args[2]) === :INTEGER && - iscall(rng.args[3]) && - length(rng.args[3].args) > 1 && ( - refof(rng.args[3].args[1]) === getsymbols(env)[:Base][:length] || - refof(rng.args[3].args[1]) === getsymbols(env)[:Base][:size] - ) + length(rng.args) === 3 && + headof(rng.args[2]) === :INTEGER && + iscall(rng.args[3]) && + length(rng.args[3].args) > 1 && ( + refof(rng.args[3].args[1]) === getsymbols(env)[:Base][:length] || + refof(rng.args[3].args[1]) === getsymbols(env)[:Base][:size] + ) if length(x.args) >= 1 lhs = x.args[1] arr = rng.args[3].args[2] @@ -425,14 +423,14 @@ end function check_nothing_equality(x::EXPR, env::ExternalEnv) if isbinarycall(x) && length(x.args) == 3 if valof(x.args[1]) == "==" && ( - (valof(x.args[2]) == "nothing" && refof(x.args[2]) === getsymbols(env)[:Core][:nothing]) || - (valof(x.args[3]) == "nothing" && refof(x.args[3]) === getsymbols(env)[:Core][:nothing]) - ) + (valof(x.args[2]) == "nothing" && refof(x.args[2]) === getsymbols(env)[:Core][:nothing]) || + (valof(x.args[3]) == "nothing" && refof(x.args[3]) === getsymbols(env)[:Core][:nothing]) + ) seterror!(x.args[1], NothingEquality) elseif valof(x.args[1]) == "!=" && ( - (valof(x.args[2]) == "nothing" && refof(x.args[2]) === getsymbols(env)[:Core][:nothing]) || - (valof(x.args[3]) == "nothing" && refof(x.args[3]) === getsymbols(env)[:Core][:nothing]) - ) + (valof(x.args[2]) == "nothing" && refof(x.args[2]) === getsymbols(env)[:Core][:nothing]) || + (valof(x.args[3]) == "nothing" && refof(x.args[3]) === getsymbols(env)[:Core][:nothing]) + ) seterror!(x.args[1], NothingNotEq) end end @@ -524,9 +522,9 @@ end function check_modulename(x::EXPR) if CSTParser.defines_module(x) && # x is a module - scopeof(x) isa Scope && parentof(scopeof(x)) isa Scope && # it has a scope and a parent scope - CSTParser.defines_module(parentof(scopeof(x)).expr) && # the parent scope is a module - valof(CSTParser.get_name(x)) == valof(CSTParser.get_name(parentof(scopeof(x)).expr)) # their names match + scopeof(x) isa Scope && parentof(scopeof(x)) isa Scope && # it has a scope and a parent scope + CSTParser.defines_module(parentof(scopeof(x)).expr) && # the parent scope is a module + valof(CSTParser.get_name(x)) == valof(CSTParser.get_name(parentof(scopeof(x)).expr)) # their names match seterror!(CSTParser.get_name(x), InvalidModuleName) end end @@ -536,7 +534,7 @@ function check_farg_unused(x::EXPR) if CSTParser.defines_function(x) sig = CSTParser.rem_wheres_decls(CSTParser.get_sig(x)) if (headof(x) === :function && length(x.args) == 2 && x.args[2] isa EXPR && length(x.args[2].args) == 1 && CSTParser.isliteral(x.args[2].args[1])) || - (length(x.args) > 1 && headof(x.args[2]) === :block && length(x.args[2].args) == 1 && CSTParser.isliteral(x.args[2].args[1])) + (length(x.args) > 1 && headof(x.args[2]) === :block && length(x.args[2].args) == 1 && CSTParser.isliteral(x.args[2].args[1])) return # Allow functions that return constants end if iscall(sig) @@ -573,12 +571,12 @@ function check_farg_unused_(arg, arg_names) valof(b.name) isa String && all_underscore(valof(b.name)) && return false if b === nothing || - # no refs: + # no refs: isempty(b.refs) || - # only self ref: + # only self ref: (length(b.refs) == 1 && first(b.refs) == b.name) || - # first usage has binding: - (length(b.refs) > 1 && b.refs[2] isa EXPR && hasbinding(b.refs[2])) + # first usage has binding: + (length(b.refs) > 1 && b.refs[2] isa EXPR && hasbinding(b.refs[2])) seterror!(arg, UnusedFunctionArgument) end @@ -598,8 +596,8 @@ end function is_nospecialize_call(x) CSTParser.ismacrocall(x) && - CSTParser.ismacroname(x.args[1]) && - is_nospecialize(x.args[1]) + CSTParser.ismacroname(x.args[1]) && + is_nospecialize(x.args[1]) end """ @@ -619,8 +617,8 @@ function collect_hints(x::EXPR, env, missingrefs=:all, isquoted=false, errs=Tupl push!(errs, (pos, x)) elseif !isquoted if missingrefs != :none && isidentifier(x) && !hasref(x) && - !(valof(x) == "var" && parentof(x) isa EXPR && isnonstdid(parentof(x))) && - !((valof(x) == "stdcall" || valof(x) == "cdecl" || valof(x) == "fastcall" || valof(x) == "thiscall" || valof(x) == "llvmcall") && is_in_fexpr(x, x -> iscall(x) && isidentifier(x.args[1]) && valof(x.args[1]) == "ccall")) + !(valof(x) == "var" && parentof(x) isa EXPR && isnonstdid(parentof(x))) && + !((valof(x) == "stdcall" || valof(x) == "cdecl" || valof(x) == "fastcall" || valof(x) == "thiscall" || valof(x) == "llvmcall") && is_in_fexpr(x, x -> iscall(x) && isidentifier(x.args[1]) && valof(x.args[1]) == "ccall")) push!(errs, (pos, x)) elseif haserror(x) && errorof(x) isa StaticLint.LintCodes @@ -649,7 +647,7 @@ end function should_mark_missing_getfield_ref(x, env) if isidentifier(x) && !hasref(x) && # x has no ref - parentof(x) isa EXPR && headof(parentof(x)) === :quotenode && parentof(parentof(x)) isa EXPR && is_getfield(parentof(parentof(x))) # x is the rhs of a getproperty + parentof(x) isa EXPR && headof(parentof(x)) === :quotenode && parentof(parentof(x)) isa EXPR && is_getfield(parentof(parentof(x))) # x is the rhs of a getproperty lhsref = refof_maybe_getfield(parentof(parentof(x)).args[1]) hasref(x) && return false # We've resolved if lhsref isa SymbolServer.ModuleStore || (lhsref isa Binding && lhsref.val isa SymbolServer.ModuleStore) @@ -718,14 +716,14 @@ function is_type_of_call_to_getproperty(x::EXPR) if iscall(x) func_name = x.args[1] return (isidentifier(func_name) && valof(func_name) == "getproperty") || # getproperty() - (is_getfield_w_quotenode(func_name) && isidentifier(func_name.args[2].args[1]) && valof(func_name.args[2].args[1]) == "getproperty") # Base.getproperty() + (is_getfield_w_quotenode(func_name) && isidentifier(func_name.args[2].args[1]) && valof(func_name.args[2].args[1]) == "getproperty") # Base.getproperty() end return false end return parentof(x) isa EXPR && parentof(parentof(x)) isa EXPR && - ((isdeclaration(parentof(x)) && x === parentof(x).args[2] && is_call_to_getproperty(parentof(parentof(x)))) || - (iscurly(parentof(x)) && x === parentof(x).args[1] && isdeclaration(parentof(parentof(x))) && parentof(parentof(parentof(x))) isa EXPR && is_call_to_getproperty(parentof(parentof(parentof(x)))))) + ((isdeclaration(parentof(x)) && x === parentof(x).args[2] && is_call_to_getproperty(parentof(parentof(x)))) || + (iscurly(parentof(x)) && x === parentof(x).args[1] && isdeclaration(parentof(parentof(x))) && parentof(parentof(parentof(x))) isa EXPR && is_call_to_getproperty(parentof(parentof(parentof(x)))))) end isunionfaketype(t::SymbolServer.FakeTypeName) = t.name.name === :Union && t.name.parent isa SymbolServer.VarRef && t.name.parent.name === :Core @@ -958,8 +956,8 @@ function check_unused_binding(b::Binding, scope::Scope) if headof(scope.expr) !== :struct && headof(scope.expr) !== :tuple && !all_underscore(valof(b.name)) refs = loose_refs(b) if (isempty(refs) || length(refs) == 1 && refs[1] == b.name) && - !is_sig_arg(b.name) && !is_overwritten_in_loop(b.name) && - !is_overwritten_subsequently(b, scope) && !is_kw_of_macrocall(b) + !is_sig_arg(b.name) && !is_overwritten_in_loop(b.name) && + !is_overwritten_subsequently(b, scope) && !is_kw_of_macrocall(b) seterror!(b.name, UnusedBinding) end end diff --git a/src/macros.jl b/src/macros.jl index b340e5f2..3a913cf3 100644 --- a/src/macros.jl +++ b/src/macros.jl @@ -9,10 +9,10 @@ function handle_macro(x::EXPR, state) end elseif CSTParser.is_func_call(x.args[4]) sig = (x.args[4]) - if sig isa EXPR + if sig isa EXPR hasscope(sig) && return # We've already done this, don't repeat setscope!(sig, Scope(sig)) - mark_sig_args!(sig) + mark_sig_args!(sig) end if state isa Toplevel push!(state.resolveonly, x) @@ -85,35 +85,35 @@ function handle_macro(x::EXPR, state) end mark_binding!(x.args[i], x) end - # elseif _points_to_arbitrary_macro(x.args[1], :Turing, :model, state) && length(x) == 3 && - # isassignment(x.args[3]) && - # headof(x.args[3].args[2]) === CSTParser.Begin && length(x.args[3].args[2]) == 3 && headof(x.args[3].args[2].args[2]) === :block - # for i = 1:length(x.args[3].args[2].args[2]) - # ex = x.args[3].args[2].args[2].args[i] - # if isbinarycall(ex, "~") - # mark_binding!(ex) - # end - # end - # elseif _points_to_arbitrary_macro(x.args[1], :JuMP, :variable, state) - # if length(x.args) < 3 - # return - # elseif length(x) >= 5 && ispunctuation(x[2]) - # _mark_JuMP_binding(x[5]) - # else - # _mark_JuMP_binding(x[3]) - # end - # elseif (_points_to_arbitrary_macro(x[1], :JuMP, :expression, state) || - # _points_to_arbitrary_macro(x[1], :JuMP, :NLexpression, state) || - # _points_to_arbitrary_macro(x[1], :JuMP, :constraint, state) || _points_to_arbitrary_macro(x[1], :JuMP, :NLconstraint, state)) && length(x) > 1 - # if ispunctuation(x[2]) - # if length(x) == 8 - # _mark_JuMP_binding(x[5]) - # end - # else - # if length(x) == 4 - # _mark_JuMP_binding(x[3]) - # end - # end + # elseif _points_to_arbitrary_macro(x.args[1], :Turing, :model, state) && length(x) == 3 && + # isassignment(x.args[3]) && + # headof(x.args[3].args[2]) === CSTParser.Begin && length(x.args[3].args[2]) == 3 && headof(x.args[3].args[2].args[2]) === :block + # for i = 1:length(x.args[3].args[2].args[2]) + # ex = x.args[3].args[2].args[2].args[i] + # if isbinarycall(ex, "~") + # mark_binding!(ex) + # end + # end + # elseif _points_to_arbitrary_macro(x.args[1], :JuMP, :variable, state) + # if length(x.args) < 3 + # return + # elseif length(x) >= 5 && ispunctuation(x[2]) + # _mark_JuMP_binding(x[5]) + # else + # _mark_JuMP_binding(x[3]) + # end + # elseif (_points_to_arbitrary_macro(x[1], :JuMP, :expression, state) || + # _points_to_arbitrary_macro(x[1], :JuMP, :NLexpression, state) || + # _points_to_arbitrary_macro(x[1], :JuMP, :constraint, state) || _points_to_arbitrary_macro(x[1], :JuMP, :NLconstraint, state)) && length(x) > 1 + # if ispunctuation(x[2]) + # if length(x) == 8 + # _mark_JuMP_binding(x[5]) + # end + # else + # if length(x) == 4 + # _mark_JuMP_binding(x[3]) + # end + # end end end end @@ -130,7 +130,7 @@ is_nospecialize(x) = isidentifier(x) && valofid(x) == "@nospecialize" function _mark_JuMP_binding(arg) if isidentifier(arg) || headof(arg) === :ref mark_binding!(_rem_ref(arg)) - elseif isbinarycall(arg, "==") || isbinarycall(arg, "<=") || isbinarycall(arg, ">=") + elseif isbinarycall(arg, "==") || isbinarycall(arg, "<=") || isbinarycall(arg, ">=") if isidentifier(arg.args[1]) || headof(arg.args[1]) === :ref mark_binding!(_rem_ref(arg.args[1])) else @@ -144,14 +144,14 @@ end function _points_to_Base_macro(x::EXPR, name, state) CSTParser.is_getfield_w_quotenode(x) && return _points_to_Base_macro(x.args[2].args[1], name, state) haskey(getsymbols(state)[:Base], name) || return false - targetmacro = maybe_lookup(getsymbols(state)[:Base][name], state) + targetmacro = maybe_lookup(getsymbols(state)[:Base][name], state) isidentifier(x) && Symbol(valofid(x)) == name && (ref = refof(x)) !== nothing && - (ref == targetmacro || (ref isa Binding && ref.val == targetmacro)) + (ref == targetmacro || (ref isa Binding && ref.val == targetmacro)) end function _points_to_arbitrary_macro(x::EXPR, module_name, name, state) length(x.args) == 2 && isidentifier(x.args[2]) && valof(x.args[2]) == name && haskey(getsymbols(state), Symbol(module_name)) && haskey(getsymbols(state)[Symbol(module_name)], Symbol("@", name)) && (refof(x.args[2]) == maybe_lookup(getsymbols(state)[Symbol(module_name)][Symbol("@", name)], state) || - (refof(x.args[2]) isa Binding && refof(x.args[2]).val == maybe_lookup(getsymbols(state)[Symbol(module_name)][Symbol("@", name)], state))) + (refof(x.args[2]) isa Binding && refof(x.args[2]).val == maybe_lookup(getsymbols(state)[Symbol(module_name)][Symbol("@", name)], state))) end maybe_lookup(x, env::ExternalEnv) = x isa SymbolServer.VarRef ? SymbolServer._lookup(x, getsymbols(env), true) : x @@ -172,8 +172,8 @@ maybeget_quotedsymbol(x::EXPR) = isquoted(x) ? maybe_eventually_get_id(x.args[1] function is_loop_iterator(x::EXPR) CSTParser.is_range(x) && - ((parentof(x) isa EXPR && headof(parentof(x)) === :for) || - (parentof(x) isa EXPR && parentof(parentof(x)) isa EXPR && headof(parentof(parentof(x))) === :for)) + ((parentof(x) isa EXPR && headof(parentof(x)) === :for) || + (parentof(x) isa EXPR && parentof(parentof(x)) isa EXPR && headof(parentof(parentof(x))) === :for)) end """ diff --git a/src/methodmatching.jl b/src/methodmatching.jl index 341c73bc..4dccf612 100644 --- a/src/methodmatching.jl +++ b/src/methodmatching.jl @@ -100,7 +100,7 @@ function find_methods(x::EXPR, store) func_ref === nothing && return possibles args, kws = call_arg_types(x, false) if func_ref isa Binding && func_ref.val isa SymbolServer.FunctionStore || - func_ref isa Binding && func_ref.val isa SymbolServer.DataTypeStore + func_ref isa Binding && func_ref.val isa SymbolServer.DataTypeStore func_ref = func_ref.val end if func_ref isa SymbolServer.FunctionStore || func_ref isa SymbolServer.DataTypeStore @@ -155,7 +155,7 @@ function match_method(args::Vector{Any}, kws::Vector{Any}, method::SymbolServer. else !_issubtype(args[i], method.sig[i][2], store) && !_issubtype(method.sig[i][2], args[i], store) && return false end - + end return true end @@ -196,7 +196,7 @@ function match_method(args::Vector{Any}, kws::Vector{Any}, method::EXPR, store) push!(margs, mopts[i]) end if vararg - for _ in 1:length(args) - length(margs) + for _ in 1:length(args)-length(margs) push!(margs, CoreTypes.Any) end end @@ -221,7 +221,7 @@ function refof_call_func(x) end end -function is_sig_of_method(sig::EXPR, method = maybe_get_parent_fexpr(sig, defines_function)) +function is_sig_of_method(sig::EXPR, method=maybe_get_parent_fexpr(sig, defines_function)) method !== nothing && sig == CSTParser.get_sig(method) end diff --git a/src/scope.jl b/src/scope.jl index b096b304..3ed947f3 100644 --- a/src/scope.jl +++ b/src/scope.jl @@ -43,7 +43,7 @@ function addmoduletoscope!(s::Scope, m, mname::Symbol) s.modules[mname] = m end addmoduletoscope!(s::Scope, m::SymbolServer.ModuleStore) = addmoduletoscope!(s, m, m.name.name) -addmoduletoscope!(s::Scope, m::EXPR) = CSTParser.defines_module(m) && addmoduletoscope!(s, scopeof(m), Symbol(valof(CSTParser.get_name(m)))) +addmoduletoscope!(s::Scope, m::EXPR) = CSTParser.defines_module(m) && addmoduletoscope!(s, scopeof(m), Symbol(valof(CSTParser.get_name(m)))) addmoduletoscope!(s::Scope, s1::Scope) = CSTParser.defines_module(s1.expr) && addmoduletoscope!(s, s1, Symbol(valof(CSTParser.get_name(s1.expr)))) @@ -69,24 +69,24 @@ function introduces_scope(x::EXPR, state) return true elseif CSTParser.defines_anon_function(x) return true - elseif CSTParser.iswhere(x) + elseif CSTParser.iswhere(x) # unless in func def signature return !_in_func_or_struct_def(x) elseif CSTParser.istuple(x) && CSTParser.hastrivia(x) && ispunctuation(x.trivia[1]) && length(x.args) > 0 && isassignment(x.args[1]) # named tuple return true elseif headof(x) === :function || - headof(x) === :macro || - headof(x) === :for || - headof(x) === :while || - headof(x) === :let || - headof(x) === :generator || # and Flatten? - headof(x) === :try || - headof(x) === :do || - headof(x) === :module || - headof(x) === :abstract || - headof(x) === :primitive || - headof(x) === :struct + headof(x) === :macro || + headof(x) === :for || + headof(x) === :while || + headof(x) === :let || + headof(x) === :generator || # and Flatten? + headof(x) === :try || + headof(x) === :do || + headof(x) === :module || + headof(x) === :abstract || + headof(x) === :primitive || + headof(x) === :struct return true end return false @@ -138,8 +138,8 @@ function scopes(x::EXPR, state) # state.scope.names[bindingof(x).name] = bindingof(x) # TODO: move this to the binding stage add_binding(x, state) - # elseif headof(x) === :flatten && headof(x[1]) === CSTParser.Generator && length(x[1]) > 0 && headof(x[1][1]) === CSTParser.Generator - # setscope!(x[1][1], nothing) + # elseif headof(x) === :flatten && headof(x[1]) === CSTParser.Generator && length(x[1]) > 0 && headof(x[1][1]) === CSTParser.Generator + # setscope!(x[1][1], nothing) end end return s0 @@ -153,5 +153,5 @@ function add_eval_method(x, state) Symbol("top-level") end meth = SymbolServer.MethodStore(:eval, mod, "", 0, [:expr => SymbolServer.FakeTypeName(SymbolServer.VarRef(SymbolServer.VarRef(nothing, :Core), :Any), [])], [], Any) - state.scope.names["eval"] = Binding(x, SymbolServer.FunctionStore(SymbolServer.VarRef(nothing, :nothing), SymbolServer.MethodStore[meth],"", SymbolServer.VarRef(nothing, :nothing), false), getsymbols(state)[:Core][:DataType], []) + state.scope.names["eval"] = Binding(x, SymbolServer.FunctionStore(SymbolServer.VarRef(nothing, :nothing), SymbolServer.MethodStore[meth], "", SymbolServer.VarRef(nothing, :nothing), false), getsymbols(state)[:Core][:DataType], []) end diff --git a/src/subtypes.jl b/src/subtypes.jl index 1c2a478f..d18deb4b 100644 --- a/src/subtypes.jl +++ b/src/subtypes.jl @@ -2,7 +2,7 @@ function _issubtype(a, b, store) _isany(b) && return true _type_compare(a, b) && return true sup_a = _super(a, store) - _type_compare(sup_a, b) && return true + _type_compare(sup_a, b) && return true !_isany(sup_a) && return _issubtype(sup_a, b, store) return false end @@ -15,8 +15,8 @@ _type_compare(a::SymbolServer.DataTypeStore, b::SymbolServer.DataTypeStore) = a. _type_compare(a::SymbolServer.FakeTypeName, b::SymbolServer.FakeTypeName) = a == b _type_compare(a::SymbolServer.FakeTypeName, b::SymbolServer.DataTypeStore) = a == b.name _type_compare(a::SymbolServer.DataTypeStore, b::SymbolServer.FakeTypeName) = a.name == b -_type_compare(a::SymbolServer.DataTypeStore, b::SymbolServer.FakeUnion) = _type_compare(a, b.a) || -_type_compare(a, b.b) +_type_compare(a::SymbolServer.DataTypeStore, b::SymbolServer.FakeUnion) = _type_compare(a, b.a) || + _type_compare(a, b.b) function _type_compare(a::SymbolServer.DataTypeStore, b::SymbolServer.FakeTypeVar) if b.ub isa SymbolServer.FakeUnion diff --git a/src/type_inf.jl b/src/type_inf.jl index cd5c11e4..34c910a0 100644 --- a/src/type_inf.jl +++ b/src/type_inf.jl @@ -216,15 +216,15 @@ function check_ref_against_calls(x, visitedmethods, new_possibles, env::External if method isa EXPR if defines_function(method) get_arg_type_at_position(method, argi, new_possibles) - # elseif CSTParser.defines_struct(method) + # elseif CSTParser.defines_struct(method) # Can we ignore this? Default constructor gives us no type info? end else # elseif what? - iterate_over_ss_methods(method, tls, env, m -> (get_arg_type_at_position(m, argi, new_possibles);false)) + iterate_over_ss_methods(method, tls, env, m -> (get_arg_type_at_position(m, argi, new_possibles); false)) end end else - iterate_over_ss_methods(func, tls, env, m -> (get_arg_type_at_position(m, argi, new_possibles);false)) + iterate_over_ss_methods(func, tls, env, m -> (get_arg_type_at_position(m, argi, new_possibles); false)) end end end @@ -246,8 +246,8 @@ end function is_arg_of_resolved_call(x::EXPR) parentof(x) isa EXPR && headof(parentof(x)) === :call && # check we're in a call signature - (caller = parentof(x).args[1]) !== x && # and that x is not the caller - ((CSTParser.isidentifier(caller) && hasref(caller)) || (is_getfield(caller) && headof(caller.args[2]) === :quotenode && hasref(caller.args[2].args[1]))) + (caller = parentof(x).args[1]) !== x && # and that x is not the caller + ((CSTParser.isidentifier(caller) && hasref(caller)) || (is_getfield(caller) && headof(caller.args[2]) === :quotenode && hasref(caller.args[2].args[1]))) end function get_arg_position_in_call(sig::EXPR, arg) @@ -260,10 +260,10 @@ function get_arg_type_at_position(method, argi, types) if method isa EXPR sig = CSTParser.get_sig(method) if sig !== nothing && - sig.args !== nothing && argi <= length(sig.args) && - hasbinding(sig.args[argi]) && - (argb = bindingof(sig.args[argi]); argb isa Binding && argb.type !== nothing) && - !(argb.type in types) + sig.args !== nothing && argi <= length(sig.args) && + hasbinding(sig.args[argi]) && + (argb = bindingof(sig.args[argi]); argb isa Binding && argb.type !== nothing) && + !(argb.type in types) push!(types, argb.type) return end @@ -295,7 +295,7 @@ function infer_eltype(x::EXPR) elseif headof(x) === :ref && hasref(x.args[1]) r = refof(x.args[1]) if r isa SymbolServer.DataTypeStore || - r isa Binding && CoreTypes.isdatatype(r.type) + r isa Binding && CoreTypes.isdatatype(r.type) r end elseif headof(x) === :STRING diff --git a/test/runtests.jl b/test/runtests.jl index f752ea89..a4b309e1 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -31,48 +31,48 @@ end x x = 1 x -""") == [false, true, true] +""") == [false, true, true] @test check_resolved(""" x, y x = y = 1 x, y -""") == [false, false, true, true, true, true] +""") == [false, false, true, true, true, true] @test check_resolved(""" x, y x, y = 1, 1 x, y -""") == [false, false, true, true, true, true] +""") == [false, false, true, true, true, true] @test check_resolved(""" M module M end M -""") == [false, true, true] +""") == [false, true, true] @test check_resolved(""" f f() = 0 f -""") == [false, true, true] +""") == [false, true, true] @test check_resolved(""" f function f end f -""") == [false, true, true] +""") == [false, true, true] @test check_resolved(""" f function f() end f -""") == [false, true, true] +""") == [false, true, true] @test check_resolved(""" function f(a) end -""") == [true, true] +""") == [true, true] @test check_resolved(""" f, a @@ -80,7 +80,7 @@ function f(a) a end f, a -""") == [false, false, true, true, true, true, false] +""") == [false, false, true, true, true, true, false] @test check_resolved(""" @@ -89,7 +89,7 @@ let x = 1 x end x -""") == [false, true, true, false] +""") == [false, true, true, false] @test check_resolved(""" x,y @@ -97,36 +97,36 @@ let x = 1, y = 1 x, y end x, y -""") == [false, false, true, true, true, true, false, false] +""") == [false, false, true, true, true, true, false, false] @test check_resolved(""" function f(a...) f(a) end -""") == [true, true, true, true] +""") == [true, true, true, true] @test check_resolved(""" for i = 1:1 end -""") == [true] +""") == [true] @test check_resolved(""" [i for i in 1:1] -""") == [true, true] +""") == [true, true] @test check_resolved(""" [i for i in 1:1 if i] -""") == [true, true, true] +""") == [true, true, true] -# @test check_resolved(""" -# @deprecate f(a) sin(a) -# f -# """) == [true, true, true, true, true, true] + # @test check_resolved(""" + # @deprecate f(a) sin(a) + # f + # """) == [true, true, true, true, true, true] @test check_resolved(""" @deprecate f sin f -""") == [true, true, true, true] +""") == [true, true, true, true] @test check_resolved(""" module Mod @@ -240,7 +240,7 @@ f(arg) = arg function f(arg::T1) arg.field.x end - """); + """) @test refof(cst.args[3].args[2].args[1].args[1].args[1]) == bindingof(cst.args[3].args[1].args[2]) @test refof(cst.args[3].args[2].args[1].args[1].args[2].args[1]) == bindingof(cst.args[2].args[3].args[1]) @test refof(cst.args[3].args[2].args[1].args[2].args[1]) == bindingof(cst.args[1].args[3].args[1]) @@ -370,7 +370,7 @@ f(arg) = arg E a b - """) == [true, true, true, true, true, true, true] + """) == [true, true, true, true, true, true, true] end @test check_resolved(""" @@ -378,7 +378,7 @@ f(arg) = arg E a b - """) == [true, true, true, true, true, true, true] + """) == [true, true, true, true, true, true, true] @test check_resolved(""" @enum E begin @@ -388,7 +388,7 @@ f(arg) = arg E a b - """) == [true, true, true, true, true, true, true] + """) == [true, true, true, true, true, true, true] end @testset "tuple args" begin @@ -615,7 +615,7 @@ f(arg) = arg sin(a,b) = 1 sin(1) """) - # Checks that documented symbols are skipped + # Checks that documented symbols are skipped @test isempty(StaticLint.collect_hints(cst, StaticLint.getenv(server.files[""], server))) end let cst = parse_and_pass(""" @@ -623,7 +623,7 @@ f(arg) = arg sin(a,b) = 1 sin(1) """) - # Checks that documented symbols are skipped + # Checks that documented symbols are skipped @test isempty(StaticLint.collect_hints(cst, getenv(server.files[""], server))) end let cst = parse_and_pass(""" @@ -809,10 +809,10 @@ f(arg) = arg @test StaticLint.errorof(CSTParser.get_sig(cst[1])[3]) === StaticLint.UnusedFunctionArgument end let cst = parse_and_pass( - """function f(arg) - x = arg - arg = x - end""") + """function f(arg) + x = arg + arg = x + end""") StaticLint.check_farg_unused(cst[1]) @test StaticLint.errorof(CSTParser.get_sig(cst[1])[3]) === nothing end @@ -836,10 +836,10 @@ f(arg) = arg end end """) - StaticLint.check_farg_unused(cst[1]) - @test StaticLint.errorof(CSTParser.get_sig(cst[1])[3]) === nothing - @test StaticLint.errorof(CSTParser.get_sig(cst[1])[5]) === nothing - end + StaticLint.check_farg_unused(cst[1]) + @test StaticLint.errorof(CSTParser.get_sig(cst[1])[3]) === nothing + @test StaticLint.errorof(CSTParser.get_sig(cst[1])[5]) === nothing + end end @testset "check redefinition of const" begin @@ -1113,7 +1113,7 @@ f(arg) = arg end end @testset "overloading" begin - # overloading of a function that happens to be exported into the current scope. + # overloading of a function that happens to be exported into the current scope. let cst = parse_and_pass(""" Base.sin() = nothing sin() @@ -1122,7 +1122,7 @@ f(arg) = arg @test first(cst.meta.scope.names["sin"].refs) == server.external_env.symbols[:Base][:sin] @test isempty(StaticLint.collect_hints(cst[2], getenv(server.files[""], server))) end - # As above but for user defined function + # As above but for user defined function let cst = parse_and_pass(""" module M f(x) = nothing @@ -1142,7 +1142,7 @@ f(arg) = arg @test errorof(cst[1]) === errorof(cst[2]) === errorof(cst[3]) end end - # Non exported function is overloaded + # Non exported function is overloaded let cst = parse_and_pass(""" Base.argtail() = nothing Base.argtail() @@ -1150,7 +1150,7 @@ f(arg) = arg @test !haskey(cst.meta.scope.names, "argtail") # @test isempty(StaticLint.collect_hints(cst, getenv(server.files[""], server))) end - # As above but for user defined function + # As above but for user defined function let cst = parse_and_pass(""" module M ff(x) = nothing @@ -1781,7 +1781,7 @@ end end -if Meta.parse("import a as b", raise = false).head !== :error +if Meta.parse("import a as b", raise=false).head !== :error @testset "import as ..." begin cst = parse_and_pass("""import Base as base""") @test StaticLint.hasbinding(cst[1][2][3]) diff --git a/test/typeinf.jl b/test/typeinf.jl index bd5beadb..e90b94b2 100644 --- a/test/typeinf.jl +++ b/test/typeinf.jl @@ -1,109 +1,109 @@ -@testset "type inference by use" begin -cst = parse_and_pass(""" -struct T -end - -struct S -end - -f(x::T) = 1 -g(x::S) = 1 +@testset "type inference by use" begin + cst = parse_and_pass(""" + struct T + end -function ex1(x) - f(x) -end + struct S + end -function ex2(x) - f(x) - g(x) -end + f(x::T) = 1 + g(x::S) = 1 -function ex3(x) - if 1 - f(x) - else + function ex1(x) f(x) end -end -function ex4(x) - x - if 1 + function ex2(x) f(x) + g(x) end -end -function ex5(x) - if 1 - f(x) - else - g(x) + function ex3(x) + if 1 + f(x) + else + f(x) + end end -end -function ex6(x) - if 1 - y = x - f(y) - else - g(x) + function ex4(x) + x + if 1 + f(x) + end end -end -"""); -T = cst.meta.scope.names["T"] -S = cst.meta.scope.names["S"] + function ex5(x) + if 1 + f(x) + else + g(x) + end + end -@test cst.meta.scope.names["ex1"].val.meta.scope.names["x"].type == T -@test cst.meta.scope.names["ex2"].val.meta.scope.names["x"].type === nothing -@test cst.meta.scope.names["ex3"].val.meta.scope.names["x"].type === nothing -@test cst.meta.scope.names["ex4"].val.meta.scope.names["x"].type === nothing -@test cst.meta.scope.names["ex5"].val.meta.scope.names["x"].type === nothing -@test cst.meta.scope.names["ex6"].val.meta.scope.names["y"].type === T + function ex6(x) + if 1 + y = x + f(y) + else + g(x) + end + end + """) + + T = cst.meta.scope.names["T"] + S = cst.meta.scope.names["S"] + + @test cst.meta.scope.names["ex1"].val.meta.scope.names["x"].type == T + @test cst.meta.scope.names["ex2"].val.meta.scope.names["x"].type === nothing + @test cst.meta.scope.names["ex3"].val.meta.scope.names["x"].type === nothing + @test cst.meta.scope.names["ex4"].val.meta.scope.names["x"].type === nothing + @test cst.meta.scope.names["ex5"].val.meta.scope.names["x"].type === nothing + @test cst.meta.scope.names["ex6"].val.meta.scope.names["y"].type === T end @testset "loop iterator inference" begin -cst = parse_and_pass(""" -begin -abstract type T end -X = Int[] -Y = T[] -end + cst = parse_and_pass(""" + begin + abstract type T end + X = Int[] + Y = T[] + end -for x in 1 end -for x in "abc" end -for x in 1:10 end -for x in 1.0:10.0 end -for x in Int[1,2,3] end -for x in X end -for y in Y end -"""); - -@test cst.args[2].meta.scope.names["x"].type === nothing -@test StaticLint.CoreTypes.ischar(cst.args[3].meta.scope.names["x"].type) -@test StaticLint.CoreTypes.isint(cst.args[4].meta.scope.names["x"].type) -@test StaticLint.CoreTypes.isfloat(cst.args[5].meta.scope.names["x"].type) -@test StaticLint.CoreTypes.isint(cst.args[6].meta.scope.names["x"].type) -@test StaticLint.CoreTypes.isint(cst.args[7].meta.scope.names["x"].type) -@test StaticLint.CoreTypes.isint(cst.args[7].meta.scope.names["x"].type) -@test cst.args[8].meta.scope.names["y"].type == cst.meta.scope.names["T"] + for x in 1 end + for x in "abc" end + for x in 1:10 end + for x in 1.0:10.0 end + for x in Int[1,2,3] end + for x in X end + for y in Y end + """) + + @test cst.args[2].meta.scope.names["x"].type === nothing + @test StaticLint.CoreTypes.ischar(cst.args[3].meta.scope.names["x"].type) + @test StaticLint.CoreTypes.isint(cst.args[4].meta.scope.names["x"].type) + @test StaticLint.CoreTypes.isfloat(cst.args[5].meta.scope.names["x"].type) + @test StaticLint.CoreTypes.isint(cst.args[6].meta.scope.names["x"].type) + @test StaticLint.CoreTypes.isint(cst.args[7].meta.scope.names["x"].type) + @test StaticLint.CoreTypes.isint(cst.args[7].meta.scope.names["x"].type) + @test cst.args[8].meta.scope.names["y"].type == cst.meta.scope.names["T"] end @testset "Vector{T} infer" begin -cst = parse_and_pass(""" -struct T - t1 -end -struct S - s1::Vector{T} -end + cst = parse_and_pass(""" + struct T + t1 + end + struct S + s1::Vector{T} + end -function f(s::S) - t = s.s1[1] - t # This should be inferred as T -end -""") + function f(s::S) + t = s.s1[1] + t # This should be inferred as T + end + """) -@test cst[3].meta.scope.names["t"].type == cst.meta.scope.names["T"] + @test cst[3].meta.scope.names["t"].type == cst.meta.scope.names["T"] end