Skip to content

Commit 890bf65

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

File tree

5 files changed

+147
-1
lines changed

5 files changed

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