Skip to content

Commit 77670b1

Browse files
committed
lint stages wip
1 parent b9768c8 commit 77670b1

File tree

3 files changed

+184
-70
lines changed

3 files changed

+184
-70
lines changed

bin/moonc

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,26 @@ local lfs = require "lfs"
55

66
local parser = argparse()
77

8-
parser:flag("-l --lint", "Perform a lint on the file instead of compiling")
8+
parser:group("Lint mode",
9+
parser:flag("-l --lint", "Perform a lint on the file instead of compiling"),
10+
parser:option("--lint-config-module", "Module name used for lint configuration", "lint_config"),
11+
parser:option("--lint-stages", "Which stages of lint to perform", "globals,unused"):convert(function(stages_string)
12+
local valid_stages = { globals = true, unused = true }
13+
local stages = {}
14+
for stage in stages_string:gmatch("([^%s,]+)") do
15+
if not valid_stages[stage] then
16+
return nil, "--lint-stages: Invalid stage name `" .. stage .. "`"
17+
end
18+
table.insert(stages, stage)
19+
end
20+
21+
if not next(stages) then
22+
return nil, "--lint-stages: Can not be empty"
23+
end
24+
25+
return stages
26+
end)
27+
)
928

1029
parser:flag("-v --version", "Print version")
1130
parser:flag("-w --watch", "Watch file/directory for updates")
@@ -149,12 +168,20 @@ local function create_watcher(files)
149168
return watchers.SleepWatcher(files):each_update()
150169
end
151170

171+
-- lint options to pass through
172+
local lint_opts = {
173+
stages = opts.lint_stages,
174+
config_module = opts.lint_config_module
175+
}
176+
152177
if opts.watch then
153178
-- build function to check for lint or compile in watch
154179
local handle_file
155180
if opts.lint then
156181
local lint = require "moonscript.cmd.lint"
157-
handle_file = lint.lint_file
182+
handle_file = function(fname, target)
183+
return lint.lint_file(fname, lint_opts)
184+
end
158185
else
159186
handle_file = compile_and_write
160187
end
@@ -202,7 +229,7 @@ elseif opts.lint then
202229
local lint = require "moonscript.cmd.lint"
203230
for _, tuple in pairs(files) do
204231
local fname = tuple[1]
205-
local res, err = lint.lint_file(fname)
232+
local res, err = lint.lint_file(fname, lint_opts)
206233
if res then
207234
has_linted_with_error = true
208235
io.stderr:write(res .. "\n\n")

moonscript/cmd/lint.lua

Lines changed: 85 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,10 @@ local default_whitelist = Set({
5050
"true",
5151
"false"
5252
})
53+
local default_stages = {
54+
globals = true,
55+
unused = true
56+
}
5357
local LinterBlock
5458
do
5559
local _class_0
@@ -65,6 +69,9 @@ do
6569
end
6670
end,
6771
lint_check_unused = function(self)
72+
if not (self.stages.unused) then
73+
return
74+
end
6875
if not (self.lint_unused_names and next(self.lint_unused_names)) then
6976
return
7077
end
@@ -141,26 +148,34 @@ do
141148
_base_0.__index = _base_0
142149
setmetatable(_base_0, _parent_0.__base)
143150
_class_0 = setmetatable({
144-
__init = function(self, whitelist_globals, ...)
151+
__init = function(self, whitelist_globals, stages, ...)
145152
if whitelist_globals == nil then
146153
whitelist_globals = default_whitelist
147154
end
155+
if stages == nil then
156+
stages = default_stages
157+
end
148158
_class_0.__parent.__init(self, ...)
149159
self.get_root_block = function()
150160
return self
151161
end
162+
self.stages = stages
152163
self.lint_errors = { }
153164
local vc = self.value_compilers
154165
self.value_compilers = setmetatable({
155166
ref = function(block, val)
156167
local name = val[2]
157-
if not (block:has_name(name) or whitelist_globals[name] or name:match("%.")) then
158-
insert(self.lint_errors, {
159-
"accessing global `" .. tostring(name) .. "`",
160-
val[-1]
161-
})
168+
if self.stages.globals then
169+
if not (block:has_name(name) or whitelist_globals[name] or name:match("%.")) then
170+
insert(self.lint_errors, {
171+
"accessing global `" .. tostring(name) .. "`",
172+
val[-1]
173+
})
174+
end
175+
end
176+
if self.stages.unused then
177+
block:lint_mark_used(name)
162178
end
163-
block:lint_mark_used(name)
164179
return vc.ref(block, val)
165180
end
166181
}, {
@@ -169,30 +184,32 @@ do
169184
local sc = self.statement_compilers
170185
self.statement_compilers = setmetatable({
171186
assign = function(block, node)
172-
local names = node[2]
173-
for _index_0 = 1, #names do
174-
local _continue_0 = false
175-
repeat
176-
local name = names[_index_0]
177-
if type(name) == "table" and name[1] == "temp_name" then
178-
_continue_0 = true
179-
break
180-
end
181-
local real_name, is_local = block:extract_assign_name(name)
182-
if not (is_local or real_name and not block:has_name(real_name, true)) then
183-
_continue_0 = true
184-
break
185-
end
186-
if real_name == "_" then
187+
if self.stages.unused then
188+
local names = node[2]
189+
for _index_0 = 1, #names do
190+
local _continue_0 = false
191+
repeat
192+
local name = names[_index_0]
193+
if type(name) == "table" and name[1] == "temp_name" then
194+
_continue_0 = true
195+
break
196+
end
197+
local real_name, is_local = block:extract_assign_name(name)
198+
if not (is_local or real_name and not block:has_name(real_name, true)) then
199+
_continue_0 = true
200+
break
201+
end
202+
if real_name == "_" then
203+
_continue_0 = true
204+
break
205+
end
206+
block.lint_unused_names = block.lint_unused_names or { }
207+
block.lint_unused_names[real_name] = node[-1] or 0
187208
_continue_0 = true
209+
until true
210+
if not _continue_0 then
188211
break
189212
end
190-
block.lint_unused_names = block.lint_unused_names or { }
191-
block.lint_unused_names[real_name] = node[-1] or 0
192-
_continue_0 = true
193-
until true
194-
if not _continue_0 then
195-
break
196213
end
197214
end
198215
return sc.assign(block, node)
@@ -270,14 +287,18 @@ format_lint = function(errors, code, header)
270287
end
271288
local whitelist_for_file
272289
do
273-
local lint_config
274-
whitelist_for_file = function(fname)
275-
if not (lint_config) then
276-
lint_config = { }
290+
local loaded_configs = { }
291+
whitelist_for_file = function(fname, config_module)
292+
if config_module == nil then
293+
config_module = "lint_config"
294+
end
295+
if not (loaded_configs[config_module]) then
296+
loaded_configs[config_module] = { }
277297
pcall(function()
278-
lint_config = require("lint_config")
298+
loaded_configs[config_module] = require(config_module)
279299
end)
280300
end
301+
local lint_config = loaded_configs[config_module]
281302
if not (lint_config.whitelist_globals) then
282303
return default_whitelist
283304
end
@@ -295,28 +316,55 @@ do
295316
})
296317
end
297318
end
319+
local normalize_stages
320+
normalize_stages = function(stages)
321+
if not (stages) then
322+
return default_stages
323+
end
324+
if stages.globals ~= nil or stages.unused ~= nil then
325+
return stages
326+
end
327+
local out = { }
328+
for _index_0 = 1, #stages do
329+
local stage = stages[_index_0]
330+
out[stage] = true
331+
end
332+
return out
333+
end
298334
local lint_code
299-
lint_code = function(code, name, whitelist_globals)
335+
lint_code = function(code, name, opts)
300336
if name == nil then
301337
name = "string input"
302338
end
339+
if opts == nil then
340+
opts = { }
341+
end
303342
local parse = require("moonscript.parse")
304343
local tree, err = parse.string(code)
305344
if not (tree) then
306345
return nil, err
307346
end
308-
local scope = LinterBlock(whitelist_globals)
347+
local whitelist_globals = opts.whitelist_globals or default_whitelist
348+
local stages = normalize_stages(opts.stages)
349+
local scope = LinterBlock(whitelist_globals, stages)
309350
scope:stms(tree)
310351
scope:lint_check_unused()
311352
return format_lint(scope.lint_errors, code, name)
312353
end
313354
local lint_file
314-
lint_file = function(fname)
355+
lint_file = function(fname, opts)
356+
if opts == nil then
357+
opts = { }
358+
end
315359
local f, err = io.open(fname)
316360
if not (f) then
317361
return nil, err
318362
end
319-
return lint_code(f:read("*a"), fname, whitelist_for_file(fname))
363+
local config_module = opts.config_module or "lint_config"
364+
return lint_code(f:read("*a"), fname, {
365+
whitelist_globals = whitelist_for_file(fname, config_module),
366+
stages = opts.stages
367+
})
320368
end
321369
return {
322370
lint_code = lint_code,

0 commit comments

Comments
 (0)