Skip to content

Commit

Permalink
Merge pull request #179 from TotalVerb/fw/code-simplify
Browse files Browse the repository at this point in the history
Simplify code thanks to migration from v0.4
  • Loading branch information
Michael-Klassen authored Mar 6, 2017
2 parents 28d02ec + 7cc7696 commit 564469e
Show file tree
Hide file tree
Showing 21 changed files with 407 additions and 432 deletions.
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.11.0
Compat 0.15.0
4 changes: 2 additions & 2 deletions docs/messages.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ Every error code starts with letter for the severity `E`:`ERROR`, `W`:`WARN` or
| E421 | (no longer used)
| E422 | string uses * to concatenate
| E423 | named keyword argument must have a default
| E424 | nested vect is treated as a 1-dimensional array. Use [a;b] instead
| E424 | (no longer used)
| E425 | use lintpragma macro inside type declaration
| E431 | use of length() in a Boolean context, use isempty()
| E432 | though valid in 0.4, use x() instead of y()
Expand All @@ -105,7 +105,7 @@ Every error code starts with letter for the severity `E`:`ERROR`, `W`:`WARN` or
| E534 | introducing a new name for an implicit argument to the function, use {T<:X}
| E535 | introducing a new name for an algebric data type, use {T<:X}
| E536 | use {T<:...} instead of a known type
| E537 | String constructor does not exist in v0.4; use string() instead
| E537 | (removed)
| E538 | known type in parametric data type, use {T<:...}
| |
| **E6** | *Structure Error*
Expand Down
11 changes: 7 additions & 4 deletions src/Lint.jl
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,13 @@ module Lint

using Base.Meta
using Compat
using Compat.TypeUtils

if isdefined(Base, :unwrap_unionall)
using Base: unwrap_unionall
else
unwrap_unionall(x) = x
end

export LintMessage, LintContext, LintStack
export lintfile, lintstr, lintpkg, lintserver, @lintpragma
Expand Down Expand Up @@ -267,10 +274,6 @@ function lintexpr(ex::Expr, ctx::LintContext)
lintref(ex, ctx)
elseif ex.head == :typed_vcat# it could be a ref a[b], or an array Int[1,2]
linttyped_vcat(ex, ctx)
elseif ex.head == :dict # homogeneous dictionary
lintdict(ex, ctx; typed=false)
elseif ex.head == :typed_dict # mixed type dictionary
lintdict(ex, ctx; typed=true)
elseif ex.head == :vcat
lintvcat(ex, ctx)
elseif ex.head == :vect # 0.4
Expand Down
39 changes: 17 additions & 22 deletions src/dict.jl
Original file line number Diff line number Diff line change
@@ -1,32 +1,27 @@
function lintdict(::Expr, ::LintContext; typed::Bool = false)
@lintpragma "Ignore unused typed"
# TODO: Complain about v0.4-style [a => b for x in y] Dict comprehensions,
# deprecated in v0.5.
end

function lintdict4(ex::Expr, ctx::LintContext)
function lintdict(ex::Expr, ctx::LintContext)
typed = isexpr(ex.args[1], :curly)
st = 2
ks = Set{Any}()
ktypes = Set{Any}()
vtypes = Set{Any}()
for i in st:length(ex.args)
a = ex.args[i]
if typeof(a) == Expr && a.head == :(=>)
if typeof(a.args[1]) != Expr
if in(a.args[1], ks)
msg(ctx, :E334, a.args[1], "duplicate key in Dict")
for a in ex.args[2:end]
if ispairexpr(a)
keyexpr = lexicalfirst(a)
lit = lexicalvalue(keyexpr)
if !isnull(lit)
if keyexpr in ks
msg(ctx, :E334, keyexpr, "duplicate key in Dict")
end
push!(ks, a.args[1])
push!(ks, keyexpr)
end
for (j,s) in [(1,ktypes), (2,vtypes)]
if typeof(a.args[j]) <: QuoteNode && typeof(a.args[j].value) <: Symbol
push!(s, Symbol)
elseif typeof(a.args[j]) <: Number || typeof(a.args[j]) <: AbstractString
push!(s, typeof(a.args[j]))
for (j,s) in [(lexicalfirst,ktypes), (lexicallast,vtypes)]
kvexpr = j(a)
typeguess = lexicaltypeof(kvexpr)
if isleaftype(typeguess)
push!(s, typeguess)
elseif isexpr(kvexpr, :call) && in(kvexpr.args[1], [:Date, :DateTime])
# TODO: use the existing guesstype infrastructure
# we want to add more immutable types such as Date, DateTime, etc.
elseif isexpr(a.args[j], :call) && in(a.args[j].args[1], [:Date, :DateTime])
push!(s, a.args[j].args[1])
push!(s, kvexpr.args[1])
else
if typed
push!(s, Any)
Expand Down
75 changes: 74 additions & 1 deletion src/exprutils.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,17 @@ module ExpressionUtils

using Base.Meta

export split_comparison, simplify_literal
export split_comparison, simplify_literal, ispairexpr, isliteral,
lexicaltypeof, lexicalfirst, lexicallast, lexicalvalue

# TODO: remove when 0.5 support dropped
function BROADCAST(f, x::Nullable)
if isnull(x)
Nullable()
else
Nullable(f(get(x)))
end
end

"""
split_comparison(::Expr)
Expand Down Expand Up @@ -44,4 +54,67 @@ function simplify_literal(ex)
end
end

"""
Return `true` if `x` is an expression of the form `:(k => v)`. Note that on
v0.5, this is `Expr(:(=>), ...)`, whereas on v0.6 it is `Expr(:call, :(=>),
...)`.
"""
ispairexpr(x) = isexpr(x, :(=>)) || isexpr(x, :call) && x.args[1] == :(=>)

"""
Return the first entry of the given pair expression, determined lexically.
Note that on v0.5, this is the first argument of the expression, whereas on
v0.6, this is the second argument of the expression.
"""
lexicalfirst(x) = VERSION < v"0.6-" ? x.args[1] : x.args[2]

"""
Return the last entry of the given pair expression, determined lexically. Note
that on v0.5, this is the second argument of the expression, whereas on v0.6,
this is the third argument of the expression.
"""
lexicallast(x) = VERSION < v"0.6-" ? x.args[2] : x.args[3]

"""
Return `true` if the value represented by expression `x` is exactly `x` itself;
that is, `x` is not `Expr`, `QuoteNode`, or `Symbol`.
"""
isliteral(x) = !isa(x, Expr) && !isa(x, QuoteNode) && !isa(x, Symbol)

"""
lexicalvalue(x) :: Nullable{Any}
If `x` is a literal, or a quoted literal, return that literal wrapped in a
`Nullable`. Otherwise, return `Nullable{Any}()`.
"""
function lexicalvalue(x)
if isliteral(x)
Nullable{Any}(x)
elseif isexpr(x, :quote)
if isexpr(x.args[1], :($))
lexicalvalue(x.args[1].args[1])
else
Nullable{Any}(x.args[1])
end
elseif isa(x, QuoteNode)
Nullable{Any}(x.value)
else
Nullable{Any}()
end
end

"""
Return the most specific known lexical type of the given expression. Lexical
type is defined as
- If the expression is a literal, the type of that literal;
- If the expression is a `QuoteNode`, or an `Expr(:quote, ...)`, the type of
whatever is quoted;
- Otherwise, `Any`.
That is, the maximal amount of information detectable from the lexical context
alone.
"""
lexicaltypeof(x) = get(BROADCAST(typeof, lexicalvalue(x)), Any)

end
51 changes: 16 additions & 35 deletions src/functions.jl
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,7 @@ const commoncollmethods = Dict{Symbol, Set{Type}}()

# deprecation of specialized version of constructors
const deprecated_constructors =
Dict(:symbol => :Symbol,
:uint => :UInt,
:uint8 => :UInt8,
:uint16 => :UInt16,
:uint32 => :UInt32,
:uint64 => :UInt64,
:uint128 => :UInt128,
:float16 => :Float16,
:float32 => :Float32,
:float64 => :Float64,
:int => :Int,
:int8 => :Int8,
:int16 => :Int16,
:int32 => :Int32,
:int64 => :Int64,
:int128 => :Int128)
Dict(:symbol => :Symbol)

const not_constructible = Set([:Union, :Tuple, :Type])

Expand Down Expand Up @@ -122,8 +107,8 @@ function lintfunction(ex::Expr, ctx::LintContext; ctorType = Symbol(""), isstage
msg(ctx, :E536, temptype, "use {T<:...} instead of a known type")
end
if in(typeconstraint, knowntypes)
dt = eval(typeconstraint)
if isa(dt, Type) && isleaftype(dt)
dt = parsetype(typeconstraint)
if isleaftype(dt)
msg(ctx, :E513, adt, "leaf type as a type constraint makes no sense")
end
end
Expand Down Expand Up @@ -267,13 +252,11 @@ function lintfunction(ex::Expr, ctx::LintContext; ctorType = Symbol(""), isstage
try
vi = stacktop.localarguments[end][s]
if haskey(assertions, s)
dt = eval(assertions[s])
if isa(dt, Type)
vi.typeactual = dt
if dt != Any && haskey(typeRHShints, s) && typeRHShints[s] != Any &&
!(typeRHShints[s] <: dt)
msg(ctx, :E516, s, "type assertion and default seem inconsistent")
end
dt = parsetype(assertions[s])
vi.typeactual = dt
if dt != Any && haskey(typeRHShints, s) && typeRHShints[s] != Any &&
!(typeRHShints[s] <: dt)
msg(ctx, :E516, s, "type assertion and default seem inconsistent")
end
elseif haskey(typeRHShints, s)
vi.typeactual = typeRHShints[s]
Expand Down Expand Up @@ -390,6 +373,7 @@ function lintfunctioncall(ex::Expr, ctx::LintContext; inthrow::Bool=false)
else
inclfile = ""
try
# TODO: not a good idea...
inclfile = eval(ex.args[2])
catch
inclfile = string(ex.args[2])
Expand All @@ -414,7 +398,7 @@ function lintfunctioncall(ex::Expr, ctx::LintContext; inthrow::Bool=false)
end

if ex.args[1] == :Dict || isexpr(ex.args[1], :curly) && ex.args[1].args[1] == :Dict
lintdict4(ex, ctx)
lintdict(ex, ctx)
return
end
known=false
Expand All @@ -429,10 +413,7 @@ function lintfunctioncall(ex::Expr, ctx::LintContext; inthrow::Bool=false)
end
msg(ctx, :I481, ex.args[1], "replace $(ex.args[1])() with $(repl)()$(suffix)")
end
if VERSION < v"0.5-" && ex.args[1] == :String
msg(ctx, :E537, ex.args[1],
"String constructor does not exist in v0.4; use string() instead")
elseif ex.args[1] in not_constructible
if ex.args[1] in not_constructible
msg(ctx, :W441, "type $(ex.args[1]) is not constructible like this")
elseif ex.args[1] == :(+)
lintplus(ex, ctx)
Expand Down Expand Up @@ -518,8 +499,8 @@ function lintfunctioncall(ex::Expr, ctx::LintContext; inthrow::Bool=false)
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 isa(dt, Type) && dt <: Exception && !pragmaexists( "Ignore unthrown " * string(ex.args[1]), ctx)
dt = parsetype(ex.args[1])
if 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 All @@ -536,9 +517,9 @@ function lintfunctioncall(ex::Expr, ctx::LintContext; inthrow::Bool=false)
for kw in ex.args[i].args
if isexpr(kw, :(...))
lintexpr(kw.args[1], ctx)
elseif isexpr(kw, :(=>))
lintexpr(kw.args[1], ctx)
lintexpr(kw.args[2], ctx)
elseif ispairexpr(kw)
lintexpr(lexicalfirst(kw), ctx)
lintexpr(lexicallast(kw), ctx)
elseif isa(kw, Expr) && length(kw.args) == 2
lintexpr(kw.args[2], ctx)
elseif isa(kw, Symbol)
Expand Down
Loading

0 comments on commit 564469e

Please sign in to comment.