Skip to content

Commit d77bbf7

Browse files
committed
implement dim dead code
1 parent 1c04a1f commit d77bbf7

4 files changed

Lines changed: 175 additions & 3 deletions

File tree

language_server/editor_helper.lua

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -474,6 +474,78 @@ function META:Recompile(path, lol, diagnostics, on_save_path)
474474
end
475475
end
476476

477+
-- Add diagnostics for unreachable code (dimmed with Unnecessary tag)
478+
local file_data = self:IsLoaded(path) and self:GetFile(path)
479+
480+
if file_data and file_data.compiler and file_data.compiler.SyntaxTree then
481+
local code_obj = file_data.code
482+
local diag_name = code_obj and code_obj:GetName()
483+
484+
if diag_name then
485+
local function collect_unreachable(node)
486+
if not node then return end
487+
488+
if node.statements then
489+
for _, child in ipairs(node.statements) do
490+
if type(child) == "table" then
491+
if child.Type then
492+
-- It's a statement node
493+
if child.Unreachable == true then
494+
local start, stop = child:GetStartStop()
495+
496+
if start and stop then
497+
diagnostics[diag_name] = diagnostics[diag_name] or {}
498+
table.insert(
499+
diagnostics[diag_name],
500+
{
501+
severity = "hint",
502+
code = code_obj,
503+
start = start,
504+
stop = stop,
505+
message = "unreachable code",
506+
unreachable = true,
507+
}
508+
)
509+
end
510+
end
511+
512+
collect_unreachable(child)
513+
else
514+
-- It's an array of statements (e.g., if branch)
515+
for _, stmt in ipairs(child) do
516+
if type(stmt) == "table" and stmt.Type then
517+
if stmt.Unreachable == true then
518+
local start, stop = stmt:GetStartStop()
519+
520+
if start and stop then
521+
diagnostics[diag_name] = diagnostics[diag_name] or {}
522+
table.insert(
523+
diagnostics[diag_name],
524+
{
525+
severity = "hint",
526+
code = code_obj,
527+
start = start,
528+
stop = stop,
529+
message = "unreachable code",
530+
unreachable = true,
531+
}
532+
)
533+
end
534+
end
535+
536+
collect_unreachable(stmt)
537+
end
538+
end
539+
end
540+
end
541+
end
542+
end
543+
end
544+
545+
collect_unreachable(file_data.compiler.SyntaxTree)
546+
end
547+
end
548+
477549
for name, data in pairs(diagnostics) do
478550
if #data > 0 then
479551
self:OnDiagnostics(name, data)

language_server/lsp.lua

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ function editor_helper:OnDiagnostics(path, data)
131131
local range = get_range(v.code, v.start, v.stop)
132132
local tags
133133

134-
if v.message:find("is never used") then tags = {1} end
134+
if v.message:find("is never used") or v.unreachable then tags = {1} end
135135

136136
diagnostics[i] = {
137137
severity = DiagnosticSeverity[v.severity],
@@ -254,7 +254,6 @@ lsp.methods["textDocument/codeAction"] = function(params)
254254

255255
return actions
256256
end
257-
258257
lsp.methods["nattlua/format"] = function(params)
259258
local path = to_fs_path(params.textDocument.uri)
260259
local code = editor_helper:Format(params.code, path)
@@ -828,4 +827,4 @@ function lsp.Call(params)
828827
if lsp.methods[params.method] then lsp.methods[params.method](params) end
829828
end
830829

831-
return lsp
830+
return lsp

nattlua/analyzer/statements/if.lua

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,10 @@ return {
7373
self:ConstantIfExpressionWarning(nil, og_statement.tokens["if/else/elseif"][i])
7474
self:PopCurrentExpression()
7575
end
76+
77+
for _, stmt in ipairs(statements) do
78+
if stmt.Unreachable == nil then stmt:SetUnreachable(true) end
79+
end
7680
end
7781
end
7882
else

test/tests/language_server/integration.lua

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -298,3 +298,100 @@ do
298298
test_hover("local a = |(1)", "1")
299299
test_hover("local a = (|1)", "1")
300300
end
301+
302+
-- Test unreachable code is dimmed via Unnecessary diagnostic tag
303+
do
304+
local client = LSPClient.New()
305+
local root_uri = "file:///workspace"
306+
client:SetWorkingDirectory("/workspace")
307+
client:Initialize(lsp, root_uri)
308+
local file_uri = root_uri .. "/test.nlua"
309+
local code = [[
310+
if false then
311+
local www = "hello"
312+
end
313+
]]
314+
client:ClearNotifications()
315+
client:Notify(
316+
lsp,
317+
"textDocument/didOpen",
318+
{
319+
textDocument = {
320+
uri = file_uri,
321+
languageId = "nattlua",
322+
version = 1,
323+
text = code,
324+
},
325+
}
326+
)
327+
local diag_notifications = client:GetNotifications("textDocument/publishDiagnostics")
328+
assert(#diag_notifications > 0, "Should have publishDiagnostics notifications")
329+
local found_unnecessary = false
330+
331+
for _, notif in ipairs(diag_notifications) do
332+
for _, diag in ipairs(notif.params.diagnostics) do
333+
if diag.tags then
334+
for _, tag in ipairs(diag.tags) do
335+
if tag == 1 and diag.message:find("unreachable") then
336+
found_unnecessary = true
337+
end
338+
end
339+
end
340+
end
341+
end
342+
343+
assert(
344+
found_unnecessary,
345+
"Unreachable code inside 'if false' should produce a diagnostic with Unnecessary tag (1)"
346+
)
347+
end
348+
349+
-- Test unreachable code in else-branch when condition is always true
350+
do
351+
local client = LSPClient.New()
352+
local root_uri = "file:///workspace"
353+
client:SetWorkingDirectory("/workspace")
354+
client:Initialize(lsp, root_uri)
355+
local file_uri = root_uri .. "/test.nlua"
356+
local code = [[
357+
local x = true
358+
if x then
359+
local alive = "reachable"
360+
else
361+
local dead = "unreachable"
362+
end
363+
]]
364+
client:ClearNotifications()
365+
client:Notify(
366+
lsp,
367+
"textDocument/didOpen",
368+
{
369+
textDocument = {
370+
uri = file_uri,
371+
languageId = "nattlua",
372+
version = 1,
373+
text = code,
374+
},
375+
}
376+
)
377+
local diag_notifications = client:GetNotifications("textDocument/publishDiagnostics")
378+
assert(#diag_notifications > 0, "Should have publishDiagnostics notifications")
379+
local found_unnecessary = false
380+
381+
for _, notif in ipairs(diag_notifications) do
382+
for _, diag in ipairs(notif.params.diagnostics) do
383+
if diag.tags then
384+
for _, tag in ipairs(diag.tags) do
385+
if tag == 1 and diag.message:find("unreachable") then
386+
found_unnecessary = true
387+
end
388+
end
389+
end
390+
end
391+
end
392+
393+
assert(
394+
found_unnecessary,
395+
"Unreachable else-branch when condition is always true should produce a diagnostic with Unnecessary tag (1)"
396+
)
397+
end

0 commit comments

Comments
 (0)