Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Get Lint working on 0.6 #172

Merged
merged 4 commits into from
Feb 7, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion REQUIRE
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
julia 0.4
Compat 0.8.2
Compat 0.11.0
5 changes: 5 additions & 0 deletions src/Lint.jl
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,11 @@ end

import Base: ==

include("exprutils.jl")
using .ExpressionUtils

include("statictype.jl")

include("linttypes.jl")
include("messages.jl")
include("knownsyms.jl")
Expand Down
29 changes: 12 additions & 17 deletions src/controls.jl
Original file line number Diff line number Diff line change
Expand Up @@ -56,25 +56,20 @@ end
# if none exists, return (nothing, nothing)
function versionconstraint(ex)
if isexpr(ex, :call) && ex.args[1] in COMPARISON_OPS
return versionconstraint(
Expr(:comparison, ex.args[2], ex.args[1], ex.args[3]))
elseif isexpr(ex, :comparison)
if in(:VERSION, ex.args)
for i = 1:2:length(ex.args)
a = ex.args[i]
if a == :VERSION
continue
end
if !isexpr(a, :macrocall) || a.args[1] != Symbol("@v_str") ||
!(typeof(a.args[2]) <: AbstractString)
return (nothing, nothing)
end
if length(ex.args) == 3
isversionfirst = :VERSION == ex.args[2]
constraint = simplify_literal(
isversionfirst ? ex.args[3] : ex.args[2])
if isa(constraint, VersionNumber)
pred = ver -> getfield(Base, ex.args[1])(
isversionfirst ? ver : constraint,
isversionfirst ? constraint : ver)
return (pred, !pred)
end
l = eval(Main, Expr(:(->), :VERSION, ex))
return (l, _ -> !(l(_)))
else
return (nothing, nothing)
end
return (nothing, nothing)
elseif isexpr(ex, :comparison)
return versionconstraint(split_comparison(ex))
elseif isexpr(ex, :(&&))
vc1 = versionconstraint(ex.args[1])
vc2 = versionconstraint(ex.args[2])
Expand Down
14 changes: 7 additions & 7 deletions src/curly.jl
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@
# contracts for common collections / type parametrized types
# TODO: Can we be more specific here? What about detecting contract?
const CURLY_CONTRACTS = Dict{Symbol, Any}(
:Array => (DataType, Integer),
:Dict => (DataType, DataType),
:Matrix => (DataType,),
:Set => (DataType,),
:Type => (DataType,),
:Array => (Type, Integer),
:Dict => (Type, Type),
:Matrix => (Type,),
:Set => (Type,),
:Type => (Type,),
:Val => (Any,),
:Vector => (DataType,))
:Vector => (Type,))

function lintcurly(ex::Expr, ctx::LintContext)
head = ex.args[1]
Expand All @@ -28,7 +28,7 @@ function lintcurly(ex::Expr, ctx::LintContext)
continue # grandfathered
else
t = guesstype(a, ctx)
if !(t == DataType || t == Symbol || isbits(t) || t == Any)
if !(t <: Type || t == Symbol || isbits(t) || t == Any)
msg(ctx, :W441, a, "probably illegal use inside curly")
elseif contract != nothing
if i - 1 > length(contract)
Expand Down
8 changes: 4 additions & 4 deletions src/dynamic.jl
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
"""
Determine the type of suspected imported binding `sym`. Return `:DataType` if
it is a likely type, `:var` if it likely exists but is likely not a type, and
`:Any` if it cannot be located, which may indicate.
Determine the type of suspected imported binding `sym`. Return `:Type` if it is
a likely type, `:var` if it likely exists but is likely not a type, and `:Any`
if it cannot be located, which may indicate.
"""
function dynamic_imported_binding_type(sym)
if isdefined(Main, sym)
if isupper(string(sym)[1])
try
if isa(eval(Main, sym), Type)
return :DataType
return :Type
end
end
end
Expand Down
47 changes: 47 additions & 0 deletions src/exprutils.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
module ExpressionUtils

using Base.Meta

export split_comparison, simplify_literal

"""
split_comparison(::Expr)

Split a :comparison expression into an equivalent :&& expression, given that
each component of the comparison is pure.
"""
function split_comparison(ex)
if !isexpr(ex, :comparison)
throw(ArgumentError("expected a comparison expression, got a $(ex.head)"))
end
left = ex.args[1]
op = ex.args[2]
right = ex.args[3]
remainder = ex.args[3:end]
if length(remainder) == 1
:($op($left, $right))
else
:($op($left, $right) &&
$(split_comparison(Expr(:comparison, remainder...))))
end
end

"""
simplify_literal(::Expr)

Simplify certain macros into the literals they produce.

Simplifications performed include:

- v"x.y.z" literals are simplified into `VersionNumber` objects.
"""
function simplify_literal(ex)
if isexpr(ex, :macrocall) && ex.args[1] == Symbol("@v_str") &&
isa(ex.args[2], AbstractString)
VersionNumber(ex.args[2])
else
ex
end
end

end
64 changes: 32 additions & 32 deletions src/functions.jl
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
const commoncollections = DataType[
const commoncollections = [
Array, AbstractArray, BitArray, Set, Associative]
const commoncollmethods = Dict{Symbol, Set{DataType}}()
const commoncollmethods = Dict{Symbol, Set{Type}}()

# deprecation of specialized version of constructors
const deprecated_constructors =
Expand Down Expand Up @@ -39,7 +39,7 @@ function initcommoncollfuncs()
end
s = Symbol(mtch.match)
if !haskey(commoncollmethods, s)
commoncollmethods[s] = Set{DataType}()
commoncollmethods[s] = Set{Type}()
end
push!(commoncollmethods[s], t)
end
Expand All @@ -51,11 +51,11 @@ function initcommoncollfuncs()
end
end
# ADD COMMON FUNCTIONS WITH EASILY-MISTAKEN SIGNATURES HERE
commoncollmethods[:(append!)] = Set{DataType}()
commoncollmethods[:(append!)] = Set{Type}()
end

function lintfuncargtype(ex, ctx::LintContext)
if typeof(ex) <: Expr && ex.head == :curly
if isexpr(ex, :curly)
st = 2
en = 1
if ex.args[1] == :Array
Expand All @@ -75,7 +75,7 @@ end
# a constructor for a type. We would check
# * if the function name matches the type name
function lintfunction(ex::Expr, ctx::LintContext; ctorType = Symbol(""), isstaged=false)
if length(ex.args) == 1 && typeof(ex.args[1]) == Symbol
if length(ex.args) == 1 && isa(ex.args[1], Symbol)
# generic function without methods
return
end
Expand All @@ -97,7 +97,7 @@ function lintfunction(ex::Expr, ctx::LintContext; ctorType = Symbol(""), isstage
elseif isexpr(ex.args[1].args[1], :(.))
fname = ex.args[1].args[1]
push!(ctx.callstack[end].functions, fname.args[end])
elseif typeof(ex.args[1].args[1]) == Symbol
elseif isa(ex.args[1].args[1], Symbol)
fname = ex.args[1].args[1]
push!(ctx.callstack[end].functions, fname)
elseif !isa(ex.args[1].args[1], Expr)
Expand All @@ -108,7 +108,7 @@ function lintfunction(ex::Expr, ctx::LintContext; ctorType = Symbol(""), isstage
push!(ctx.callstack[end].functions, fname)
for i in 2:length(ex.args[1].args[1].args)
adt = ex.args[1].args[1].args[i]
if typeof(adt) == Symbol
if isa(adt, Symbol)
if in(adt, knowntypes)
msg(ctx, :E534, adt, "introducing a new name for an implicit " *
"argument to the function, use {T<:$(adt)}")
Expand All @@ -123,7 +123,7 @@ function lintfunction(ex::Expr, ctx::LintContext; ctorType = Symbol(""), isstage
end
if in(typeconstraint, knowntypes)
dt = eval(typeconstraint)
if typeof(dt) == DataType && isleaftype(dt)
if isa(dt, Type) && isleaftype(dt)
msg(ctx, :E513, adt, "leaf type as a type constraint makes no sense")
end
end
Expand Down Expand Up @@ -161,7 +161,7 @@ function lintfunction(ex::Expr, ctx::LintContext; ctorType = Symbol(""), isstage
assertions = Dict{Symbol, Any}() # e.g. x::Int

resolveArguments = (sube, position) -> begin # zero position means it's not called at the top level
if typeof(sube) == Symbol
if isa(sube, Symbol)
if in(sube, argsSeen)
msg(ctx, :E331, sube, "duplicate argument")
end
Expand All @@ -171,27 +171,27 @@ function lintfunction(ex::Expr, ctx::LintContext; ctorType = Symbol(""), isstage
push!(argsSeen, sube)
stacktop.localarguments[end][sube] = VarInfo(ctx.line)
if isstaged
assertions[sube] = DataType
assertions[sube] = Type
end
return sube
elseif sube.head == :parameters
for (j,kw) in enumerate(sube.args)
if typeof(kw)==Expr && kw.head == :(...)
if isexpr(kw, :(...))
if j != length(sube.args)
msg(ctx, :E412, kw, "named ellipsis ... can only be the last argument")
return
end
sym = resolveArguments(kw, 0)
if typeof(sym)== Symbol
if isa(sym, Symbol)
if isstaged
assertions[sym] = DataType
assertions[sym] = Type
else
# This may change to Array{(Symbol,Any), 1} in the future
assertions[sym] = Array{Any,1}
end
end
return
elseif typeof(kw) != Expr || (kw.head != :(=) && kw.head != :kw)
elseif !isexpr(kw, [:(=), :kw])
msg(ctx, :E423, kw, "named keyword argument must have a default")
return
else
Expand All @@ -206,15 +206,15 @@ function lintfunction(ex::Expr, ctx::LintContext; ctorType = Symbol(""), isstage
sym = resolveArguments(sube.args[1], 0)
if !isstaged
rhstype = guesstype(sube.args[2], ctx)
if typeof(sym) == Symbol
if isa(sym, Symbol)
typeRHShints[sym] = rhstype
end
end
elseif sube.head == :(::)
if length(sube.args) > 1
sym = resolveArguments(sube.args[1], 0)
if !isstaged
if typeof(sym) == Symbol
if isa(sym, Symbol)
dt = Any
try
dt = parsetype(sube.args[2])
Expand All @@ -234,9 +234,9 @@ function lintfunction(ex::Expr, ctx::LintContext; ctorType = Symbol(""), isstage
msg(ctx, :E413, sube, "positional ellipsis ... can only be the last argument")
end
sym = resolveArguments(sube.args[1], 0)
if typeof(sym) == Symbol
if isa(sym, Symbol)
if isstaged
assertions[sym] = Tuple{Vararg{DataType}}
assertions[sym] = Tuple{Vararg{Type}}
elseif haskey(assertions, sym)
assertions[sym] = Tuple{Vararg{assertions[sym]}}
else
Expand Down Expand Up @@ -268,7 +268,7 @@ function lintfunction(ex::Expr, ctx::LintContext; ctorType = Symbol(""), isstage
vi = stacktop.localarguments[end][s]
if haskey(assertions, s)
dt = eval(assertions[s])
if typeof(dt) == DataType || typeof(dt) == (DataType,)
if isa(dt, Type)
vi.typeactual = dt
if dt != Any && haskey(typeRHShints, s) && typeRHShints[s] != Any &&
!(typeRHShints[s] <: dt)
Expand All @@ -292,8 +292,8 @@ function lintfunction(ex::Expr, ctx::LintContext; ctorType = Symbol(""), isstage
end
if ctorType != Symbol("") && fname == ctorType
t = guesstype(ex.args[2], ctx)
if typeof(t) == DataType
if t.name.name != ctorType
if isa(t, Type)
if t ≠ Any && t.name.name != ctorType
msg(ctx, :E611, "constructor doesn't seem to return the constructed object")
end
elseif t != ctorType
Expand Down Expand Up @@ -343,7 +343,7 @@ function lintlambda(ex::Expr, ctx::LintContext)
end

resolveArguments = (sube) -> begin
if typeof(sube) == Symbol
if isa(sube, Symbol)
checklambdaarg(sube)
stacktop.localarguments[end][sube] = VarInfo(ctx.line)
#= # until lambda supports named args, keep this commented
Expand All @@ -367,7 +367,7 @@ function lintlambda(ex::Expr, ctx::LintContext)
end
end

if typeof(ex.args[1]) == Symbol
if isa(ex.args[1], Symbol)
resolveArguments(ex.args[1])
elseif isexpr(ex.args[1], :tuple)
for i = 1:length(ex.args[1].args)
Expand All @@ -385,7 +385,7 @@ end

function lintfunctioncall(ex::Expr, ctx::LintContext; inthrow::Bool=false)
if ex.args[1] == :include
if typeof(ex.args[2]) <: AbstractString
if isa(ex.args[2], AbstractString)
inclfile = string(ex.args[2])
else
inclfile = ""
Expand Down Expand Up @@ -441,7 +441,7 @@ function lintfunctioncall(ex::Expr, ctx::LintContext; inthrow::Bool=false)

skiplist = Int[]

if typeof(ex.args[1]) == Symbol && haskey(commoncollmethods, ex.args[1])
if isa(ex.args[1], Symbol) && haskey(commoncollmethods, ex.args[1])
known=true
s = ex.args[1]
typesig = Any[]
Expand All @@ -462,8 +462,8 @@ function lintfunctioncall(ex::Expr, ctx::LintContext; inthrow::Bool=false)
#splice! allows empty range such as 3:2, it means inserting an array
# between position 2 and 3, without taking out any value.
if ex.args[1] == Symbol("splice!") && Meta.isexpr(ex.args[3], :(:)) &&
length(ex.args[3].args) == 2 && typeof(ex.args[3].args[1]) <: Real &&
typeof(ex.args[3].args[2]) <: Real && ex.args[3].args[2] < ex.args[3].args[1]
length(ex.args[3].args) == 2 && isa(ex.args[3].args[1], Real) &&
isa(ex.args[3].args[2], Real) && ex.args[3].args[2] < ex.args[3].args[1]
push!(skiplist, 3)
end

Expand All @@ -486,7 +486,7 @@ function lintfunctioncall(ex::Expr, ctx::LintContext; inthrow::Bool=false)
end

st = 2
if ex.args[1] == :ifelse && typeof(ex.args[2]) == Expr
if ex.args[1] == :ifelse
lintboolean(ex.args[2], ctx)
st = 3
known = true
Expand All @@ -500,7 +500,7 @@ function lintfunctioncall(ex::Expr, ctx::LintContext; inthrow::Bool=false)

if isexpr(ex.args[1], :(.))
lintexpr(ex.args[1], ctx)
elseif typeof(ex.args[1]) == Symbol
elseif isa(ex.args[1], Symbol)
push!(ctx.callstack[end].calledfuncs, ex.args[1])
end

Expand All @@ -514,12 +514,12 @@ function lintfunctioncall(ex::Expr, ctx::LintContext; inthrow::Bool=false)
end
end

if !inthrow && typeof(ex.args[1]) == Symbol
if !inthrow && isa(ex.args[1], Symbol)
s = lowercase(string(ex.args[1]))
if contains(s,"error") || contains(s,"exception") || contains(s,"mismatch") || contains(s,"fault")
try
dt = eval(ex.args[1])
if typeof(dt) == DataType && dt <: Exception && !pragmaexists( "Ignore unthrown " * string(ex.args[1]), ctx)
if isa(dt, Type) && dt <: Exception && !pragmaexists( "Ignore unthrown " * string(ex.args[1]), ctx)
msg(ctx, :W448, string(ex.args[1]) * " is an Exception but it is not enclosed in a throw()")
end
end
Expand Down
Loading