Skip to content

Commit b8857cf

Browse files
committed
feat: Add a rule to detect useless computed property key
1 parent 418f489 commit b8857cf

File tree

5 files changed

+149
-1
lines changed

5 files changed

+149
-1
lines changed

docsrc/warnings.rst

+22
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ Code Description
6262
614 Trailing whitespace in a comment.
6363
621 Inconsistent indentation (``SPACE`` followed by ``TAB``).
6464
631 Line is too long.
65+
701 Useless computed key
6566
==== =============================================================================
6667

6768
Global variables (1xx)
@@ -294,3 +295,24 @@ Additionally, separate limits can be set for three different type of lines:
294295

295296
These types of lines are limited using CLI options named ``--[no-]max-string-line-length``, ``--[no-]max-comment-line-length``,
296297
and ``--[no-]max-code-line-length``, with similar config and inline options.
298+
299+
Style issues (6xx)
300+
-----------------------
301+
302+
Useless computed key (701)
303+
^^^^^^^^^^^^^^^^^^^^^^^^^^
304+
305+
It’s unnecessary to use computed properties with literals such as:
306+
307+
.. code-block:: lua
308+
:linenos:
309+
310+
local foo = {
311+
["a"] = 0, -- bad
312+
}
313+
foo["a"] -- bad
314+
315+
local foo = {
316+
a = 0, -- good
317+
}
318+
foo.a -- good

spec/no_useless_computed_key_spec.lua

+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
local helper = require "spec.helper"
2+
3+
local function assert_warnings(warnings, src)
4+
assert.same(warnings, helper.get_stage_warnings("detect_useless_computed_key", src))
5+
end
6+
7+
describe("useless computed property key detection", function()
8+
it("does not detect anything wrong key is a keyword", function()
9+
assert_warnings({}, [[
10+
aTable["and"] = 0
11+
]])
12+
end)
13+
14+
it("does not detect anything wrong key is not a string", function()
15+
assert_warnings({}, [[
16+
aTable[{}] = 0
17+
]])
18+
end)
19+
20+
it("does not detect anything wrong key start with a number", function()
21+
assert_warnings({}, [[
22+
aTable["1key"] = 0
23+
]])
24+
end)
25+
26+
27+
it("detects useless computed key in table creation", function()
28+
assert_warnings({
29+
{code = "701", line = 2, column = 5, end_column = 11, name = "aKey1"},
30+
}, [[
31+
local aTable = {
32+
["aKey1"] = 0
33+
}
34+
]])
35+
end)
36+
37+
it("detects useless computed key when affecting a value", function()
38+
assert_warnings({
39+
{code = "701", line = 1, column = 8, end_column = 14, name = "aKey2"},
40+
}, [[
41+
aTable["aKey2"] = 0
42+
]])
43+
end)
44+
45+
it("detects useless computed key when accessing a value", function()
46+
assert_warnings({
47+
{code = "701", line = 1, column = 14, end_column = 20, name = "aKey3"},
48+
}, [[
49+
print(aTable["aKey3"])
50+
]])
51+
end)
52+
53+
end)

spec/samples/useless_computed_key.lua

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
-- warn, could be written simply with "aKey = 0"
2+
local aTable = {
3+
["aKey"] = 0
4+
}
5+
6+
-- warn, could be written simply with "aTable.aKey = 1"
7+
aTable["aKey"] = 1
8+
9+
-- no warn, "and" is a keyword
10+
aTable["and"] = 0
11+
12+
-- no warn, "1key" is not a valid name for key
13+
aTable["1key"] = 0
14+
15+
print(aTable)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
local core_utils = require "luacheck.core_utils"
2+
local utils = require "luacheck.utils"
3+
4+
local stage = {}
5+
6+
local function useless_computed_key_message_format()
7+
return "It's unnecessary to use computed properties with literals such as {name!}"
8+
end
9+
10+
stage.warnings = {
11+
["701"] = {message_format = useless_computed_key_message_format,
12+
fields = {"name"}}
13+
}
14+
15+
local function warn_useless_computed_key(chstate, node, symbol)
16+
chstate:warn_range("701", node, {
17+
name = symbol,
18+
})
19+
end
20+
21+
local keywords = utils.array_to_set({
22+
"and", "break", "do", "else", "elseif", "end", "false", "for", "function", "goto", "if", "in",
23+
"local", "nil", "not", "or", "repeat", "return", "then", "true", "until", "while"})
24+
25+
local function check_computed_key(chstate, key_node)
26+
if key_node.tag == "String" then
27+
local symbol = key_node[1]
28+
if (key_node.end_offset - key_node.offset + 1) > #symbol then
29+
if string.gmatch(symbol, "[%a_][%a%w_]*$")() == symbol and not keywords[symbol] then
30+
warn_useless_computed_key(chstate, key_node, symbol)
31+
end
32+
end
33+
end
34+
end
35+
36+
local function check_nodes(chstate, nodes)
37+
for _, node in ipairs(nodes) do
38+
if type(node) == "table" then
39+
if node.tag == "Pair" then
40+
local key_node = node[1]
41+
check_computed_key(chstate, key_node)
42+
elseif node.tag == "Index" then
43+
local key_node = node[2]
44+
check_computed_key(chstate, key_node)
45+
end
46+
47+
check_nodes(chstate, node)
48+
end
49+
end
50+
end
51+
52+
function stage.run(chstate)
53+
print("run")
54+
check_nodes(chstate, chstate.ast)
55+
end
56+
57+
return stage

src/luacheck/stages/init.lua

+2-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@ stages.names = {
2626
"detect_uninit_accesses",
2727
"detect_unreachable_code",
2828
"detect_unused_fields",
29-
"detect_unused_locals"
29+
"detect_unused_locals",
30+
"detect_useless_computed_key"
3031
}
3132

3233
stages.modules = {}

0 commit comments

Comments
 (0)