Skip to content

Commit 61f1606

Browse files
committed
lint stages wip
1 parent b9768c8 commit 61f1606

File tree

3 files changed

+186
-70
lines changed

3 files changed

+186
-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: 86 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
@@ -130,6 +137,7 @@ do
130137
_with_0.block = self.block
131138
_with_0.render = self.render
132139
_with_0.get_root_block = self.get_root_block
140+
_with_0.stages = self.stages
133141
_with_0.lint_check_unused = self.lint_check_unused
134142
_with_0.lint_mark_used = self.lint_mark_used
135143
_with_0.value_compilers = self.value_compilers
@@ -141,26 +149,34 @@ do
141149
_base_0.__index = _base_0
142150
setmetatable(_base_0, _parent_0.__base)
143151
_class_0 = setmetatable({
144-
__init = function(self, whitelist_globals, ...)
152+
__init = function(self, whitelist_globals, stages, ...)
145153
if whitelist_globals == nil then
146154
whitelist_globals = default_whitelist
147155
end
156+
if stages == nil then
157+
stages = default_stages
158+
end
148159
_class_0.__parent.__init(self, ...)
149160
self.get_root_block = function()
150161
return self
151162
end
163+
self.stages = stages
152164
self.lint_errors = { }
153165
local vc = self.value_compilers
154166
self.value_compilers = setmetatable({
155167
ref = function(block, val)
156168
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-
})
169+
if self.stages.globals then
170+
if not (block:has_name(name) or whitelist_globals[name] or name:match("%.")) then
171+
insert(self.lint_errors, {
172+
"accessing global `" .. tostring(name) .. "`",
173+
val[-1]
174+
})
175+
end
176+
end
177+
if self.stages.unused then
178+
block:lint_mark_used(name)
162179
end
163-
block:lint_mark_used(name)
164180
return vc.ref(block, val)
165181
end
166182
}, {
@@ -169,30 +185,32 @@ do
169185
local sc = self.statement_compilers
170186
self.statement_compilers = setmetatable({
171187
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
188+
if self.stages.unused then
189+
local names = node[2]
190+
for _index_0 = 1, #names do
191+
local _continue_0 = false
192+
repeat
193+
local name = names[_index_0]
194+
if type(name) == "table" and name[1] == "temp_name" then
195+
_continue_0 = true
196+
break
197+
end
198+
local real_name, is_local = block:extract_assign_name(name)
199+
if not (is_local or real_name and not block:has_name(real_name, true)) then
200+
_continue_0 = true
201+
break
202+
end
203+
if real_name == "_" then
204+
_continue_0 = true
205+
break
206+
end
207+
block.lint_unused_names = block.lint_unused_names or { }
208+
block.lint_unused_names[real_name] = node[-1] or 0
187209
_continue_0 = true
210+
until true
211+
if not _continue_0 then
188212
break
189213
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
196214
end
197215
end
198216
return sc.assign(block, node)
@@ -270,14 +288,18 @@ format_lint = function(errors, code, header)
270288
end
271289
local whitelist_for_file
272290
do
273-
local lint_config
274-
whitelist_for_file = function(fname)
275-
if not (lint_config) then
276-
lint_config = { }
291+
local loaded_configs = { }
292+
whitelist_for_file = function(fname, config_module)
293+
if config_module == nil then
294+
config_module = "lint_config"
295+
end
296+
if not (loaded_configs[config_module]) then
297+
loaded_configs[config_module] = { }
277298
pcall(function()
278-
lint_config = require("lint_config")
299+
loaded_configs[config_module] = require(config_module)
279300
end)
280301
end
302+
local lint_config = loaded_configs[config_module]
281303
if not (lint_config.whitelist_globals) then
282304
return default_whitelist
283305
end
@@ -295,28 +317,55 @@ do
295317
})
296318
end
297319
end
320+
local normalize_stages
321+
normalize_stages = function(stages)
322+
if not (stages) then
323+
return default_stages
324+
end
325+
if stages.globals ~= nil or stages.unused ~= nil then
326+
return stages
327+
end
328+
local out = { }
329+
for _index_0 = 1, #stages do
330+
local stage = stages[_index_0]
331+
out[stage] = true
332+
end
333+
return out
334+
end
298335
local lint_code
299-
lint_code = function(code, name, whitelist_globals)
336+
lint_code = function(code, name, opts)
300337
if name == nil then
301338
name = "string input"
302339
end
340+
if opts == nil then
341+
opts = { }
342+
end
303343
local parse = require("moonscript.parse")
304344
local tree, err = parse.string(code)
305345
if not (tree) then
306346
return nil, err
307347
end
308-
local scope = LinterBlock(whitelist_globals)
348+
local whitelist_globals = opts.whitelist_globals or default_whitelist
349+
local stages = normalize_stages(opts.stages)
350+
local scope = LinterBlock(whitelist_globals, stages)
309351
scope:stms(tree)
310352
scope:lint_check_unused()
311353
return format_lint(scope.lint_errors, code, name)
312354
end
313355
local lint_file
314-
lint_file = function(fname)
356+
lint_file = function(fname, opts)
357+
if opts == nil then
358+
opts = { }
359+
end
315360
local f, err = io.open(fname)
316361
if not (f) then
317362
return nil, err
318363
end
319-
return lint_code(f:read("*a"), fname, whitelist_for_file(fname))
364+
local config_module = opts.config_module or "lint_config"
365+
return lint_code(f:read("*a"), fname, {
366+
whitelist_globals = whitelist_for_file(fname, config_module),
367+
stages = opts.stages
368+
})
320369
end
321370
return {
322371
lint_code = lint_code,

0 commit comments

Comments
 (0)