Skip to content

Commit dd2ce4a

Browse files
author
Mathis Girault
committed
adding rule GCI7
1 parent e4ce6f1 commit dd2ce4a

File tree

7 files changed

+399
-0
lines changed

7 files changed

+399
-0
lines changed
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# Avoid rewriting native getter and setters (`@creedengo/avoid-getters-and-setters`)
2+
3+
⚠️ This rule _warns_ in the following configs: ✅ `flat/recommended`, ✅ `recommended`.
4+
5+
<!-- end auto-generated rule header -->
6+
7+
## Why is this an issue?
8+
9+
Overloading default getters and setters lengthens the compilation and execution times, which are usually much better optimized by the language than by the developer.
10+
11+
```js
12+
const circle = {
13+
_radius: 42,
14+
get radius() {
15+
return this._radius; // Non-compliant
16+
},
17+
};
18+
```
19+
20+
```js
21+
const circle = {
22+
_radius: 42,
23+
get diameter() {
24+
return this._radius * 2 * Math.PI; // Compliant
25+
}
26+
};
27+
```
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
/*
2+
* creedengo JavaScript plugin - Provides rules to reduce the environmental footprint of your JavaScript programs
3+
* Copyright © 2023 Green Code Initiative (https://green-code-initiative.org)
4+
*
5+
* This program is free software: you can redistribute it and/or modify
6+
* it under the terms of the GNU General Public License as published by
7+
* the Free Software Foundation, either version 3 of the License, or
8+
* (at your option) any later version.
9+
*
10+
* This program is distributed in the hope that it will be useful,
11+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
* GNU General Public License for more details.
14+
*
15+
* You should have received a copy of the GNU General Public License
16+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
17+
*/
18+
19+
"use strict";
20+
21+
/** @type {import("eslint").Rule.RuleModule} */
22+
module.exports = {
23+
meta: {
24+
type: "suggestion",
25+
docs: {
26+
description: "Avoid rewriting native getter and setters.",
27+
category: "eco-design",
28+
recommended: "warn",
29+
},
30+
messages: {
31+
avoidGettersAndSetters: "Avoid rewriting native getter and setters for direct property access.",
32+
},
33+
schema: [],
34+
},
35+
create: function (context) {
36+
/**
37+
* Checks if a getter simply returns an object property.
38+
*/
39+
function isSimpleGetter(node) {
40+
if (
41+
node.value &&
42+
node.value.body &&
43+
node.value.body.body.length === 1
44+
) {
45+
const stmt = node.value.body.body[0];
46+
if (
47+
stmt.type === "ReturnStatement" &&
48+
stmt.argument &&
49+
(
50+
// return this.foo;
51+
(stmt.argument.type === "MemberExpression" && stmt.argument.object.type === "ThisExpression") ||
52+
// return _foo;
53+
(stmt.argument.type === "Identifier")
54+
)
55+
) {
56+
return true;
57+
}
58+
}
59+
return false;
60+
}
61+
62+
/**
63+
* Checks if a setter simply assigns its parameter to a property (this.foo = value or foo = value).
64+
*/
65+
function isSimpleSetter(node) {
66+
if (
67+
node.value &&
68+
node.value.body &&
69+
node.value.body.body.length === 1
70+
) {
71+
const stmt = node.value.body.body[0];
72+
if (
73+
stmt.type === "ExpressionStatement" &&
74+
stmt.expression.type === "AssignmentExpression" &&
75+
(
76+
// this.foo = value;
77+
(stmt.expression.left.type === "MemberExpression" && stmt.expression.left.object.type === "ThisExpression") ||
78+
// foo = value;
79+
(stmt.expression.left.type === "Identifier")
80+
) &&
81+
stmt.expression.right.type === "Identifier" &&
82+
node.value.params.length === 1 &&
83+
stmt.expression.right.name === node.value.params[0].name
84+
) {
85+
return true;
86+
}
87+
}
88+
return false;
89+
}
90+
91+
return {
92+
// For object literals: { get foo() {...} } or { set foo(v) {...} }
93+
Property(node) {
94+
if ((node.kind === "get" && isSimpleGetter(node)) ||
95+
(node.kind === "set" && isSimpleSetter(node))) {
96+
context.report({ node, messageId: "avoidGettersAndSetters" });
97+
}
98+
},
99+
// For classes: class { get foo() {...} } or class { set foo(v) {...} }
100+
MethodDefinition(node) {
101+
if ((node.kind === "get" && isSimpleGetter(node)) ||
102+
(node.kind === "set" && isSimpleSetter(node))) {
103+
context.report({ node, messageId: "avoidGettersAndSetters" });
104+
}
105+
}
106+
};
107+
},
108+
};
Lines changed: 200 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
1+
/*
2+
* creedengo JavaScript plugin - Provides rules to reduce the environmental footprint of your JavaScript programs
3+
* Copyright © 2023 Green Code Initiative (https://green-code-initiative.org)
4+
*
5+
* This program is free software: you can redistribute it and/or modify
6+
* it under the terms of the GNU General Public License as published by
7+
* the Free Software Foundation, either version 3 of the License, or
8+
* (at your option) any later version.
9+
*
10+
* This program is distributed in the hope that it will be useful,
11+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
* GNU General Public License for more details.
14+
*
15+
* You should have received a copy of the GNU General Public License
16+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
17+
*/
18+
19+
"use strict";
20+
21+
//------------------------------------------------------------------------------
22+
// Requirements
23+
//------------------------------------------------------------------------------
24+
25+
const rule = require("../../../lib/rules/avoid-getters-and-setters");
26+
const RuleTester = require("eslint").RuleTester;
27+
28+
const ruleTester = new RuleTester({ parserOptions: { ecmaVersion: 2020 } });
29+
const expectedError = { messageId: "avoidGettersAndSetters" };
30+
31+
ruleTester.run("avoid-getters-and-setters", rule, {
32+
valid: [
33+
// Not a getter/setter
34+
`
35+
const obj = {
36+
foo: 42,
37+
bar() { return this.foo * 2; }
38+
};
39+
`,
40+
// Getter with computation (not simple property access)
41+
`
42+
const obj = {
43+
get diameter() {
44+
return this.radius * 2;
45+
}
46+
};
47+
`,
48+
// Setter with computation (not simple assignment)
49+
`
50+
const obj = {
51+
set radius(value) {
52+
this._radius = Math.abs(value);
53+
}
54+
};
55+
`,
56+
// Class with computed getter
57+
`
58+
class Circle {
59+
get diameter() {
60+
return this.radius * 2;
61+
}
62+
}
63+
`,
64+
// Class with computed setter
65+
`
66+
class Circle {
67+
set radius(value) {
68+
this._radius = Math.abs(value);
69+
}
70+
}
71+
`,
72+
// Getter/setter not returning/assigning a property
73+
`
74+
const obj = {
75+
get foo() {
76+
return 42;
77+
},
78+
set foo(value) {
79+
doSomething(value);
80+
}
81+
};
82+
`,
83+
],
84+
85+
invalid: [
86+
// Object literal simple getter
87+
{
88+
code: `
89+
const obj = {
90+
_length: 42,
91+
get length() {
92+
return this._length;
93+
},
94+
};
95+
`,
96+
errors: [expectedError],
97+
},
98+
// Object literal simple setter
99+
{
100+
code: `
101+
const obj = {
102+
set length(value) {
103+
this._length = value;
104+
},
105+
};
106+
`,
107+
errors: [expectedError],
108+
},
109+
// Object literal getter returning identifier
110+
{
111+
code: `
112+
const obj = {
113+
get foo() {
114+
return _foo;
115+
}
116+
};
117+
`,
118+
errors: [expectedError],
119+
},
120+
// Object literal setter assigning identifier
121+
{
122+
code: `
123+
const obj = {
124+
set foo(value) {
125+
_foo = value;
126+
}
127+
};
128+
`,
129+
errors: [expectedError],
130+
},
131+
// Class simple getter
132+
{
133+
code: `
134+
class MyClass {
135+
get foo() {
136+
return this._foo;
137+
}
138+
}
139+
`,
140+
errors: [expectedError],
141+
},
142+
// Class simple setter
143+
{
144+
code: `
145+
class MyClass {
146+
set foo(value) {
147+
this._foo = value;
148+
}
149+
}
150+
`,
151+
errors: [expectedError],
152+
},
153+
// Class getter returning identifier
154+
{
155+
code: `
156+
class MyClass {
157+
get bar() {
158+
return _bar;
159+
}
160+
}
161+
`,
162+
errors: [expectedError],
163+
},
164+
// Class setter assigning identifier
165+
{
166+
code: `
167+
class MyClass {
168+
set bar(value) {
169+
_bar = value;
170+
}
171+
}
172+
`,
173+
errors: [expectedError],
174+
},
175+
// Multiple simple getters/setters in one object
176+
{
177+
code: `
178+
const obj = {
179+
get foo() { return this._foo; },
180+
set foo(v) { this._foo = v; },
181+
get bar() { return _bar; },
182+
set bar(v) { _bar = v; }
183+
};
184+
`,
185+
errors: [expectedError, expectedError, expectedError, expectedError],
186+
},
187+
// Multiple simple getters/setters in one class
188+
{
189+
code: `
190+
class MyClass {
191+
get foo() { return this._foo; }
192+
set foo(v) { this._foo = v; }
193+
get bar() { return _bar; }
194+
set bar(v) { _bar = v; }
195+
}
196+
`,
197+
errors: [expectedError, expectedError, expectedError, expectedError],
198+
},
199+
],
200+
});

sonar-plugin/src/main/java/org/greencodeinitiative/creedengo/javascript/CheckList.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,10 @@ public static List<Class<? extends JavaScriptCheck>> getAllChecks() {
3737
AvoidAutoPlay.class,
3838
AvoidBrightnessOverride.class,
3939
AvoidCSSAnimations.class,
40+
<<<<<<< Updated upstream
41+
=======
42+
AvoidGettersAndSetters.class,
43+
>>>>>>> Stashed changes
4044
AvoidHighAccuracyGeolocation.class,
4145
AvoidKeepAwake.class,
4246
LimitDbQueryResult.class,
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/*
2+
* Creedengo JavaScript plugin - Provides rules to reduce the environmental footprint of your JavaScript programs
3+
* Copyright © 2023 Green Code Initiative (https://green-code-initiative.org)
4+
*
5+
* This program is free software: you can redistribute it and/or modify
6+
* it under the terms of the GNU General Public License as published by
7+
* the Free Software Foundation, either version 3 of the License, or
8+
* (at your option) any later version.
9+
*
10+
* This program is distributed in the hope that it will be useful,
11+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
* GNU General Public License for more details.
14+
*
15+
* You should have received a copy of the GNU General Public License
16+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
17+
*/
18+
package org.greencodeinitiative.creedengo.javascript.checks;
19+
20+
import org.greencodeinitiative.creedengo.javascript.DeprecatedEcoCodeRule;
21+
import org.sonar.check.Rule;
22+
import org.sonar.plugins.javascript.api.EslintBasedCheck;
23+
import org.sonar.plugins.javascript.api.JavaScriptRule;
24+
import org.sonar.plugins.javascript.api.TypeScriptRule;
25+
26+
@JavaScriptRule
27+
@TypeScriptRule
28+
@Rule(key = AvoidGettersAndSetters.RULE_KEY)
29+
public class AvoidGettersAndSetters implements EslintBasedCheck {
30+
31+
public static final String RULE_KEY = "GCI7";
32+
33+
@Override
34+
public String eslintKey() {
35+
return "@creedengo/avoid-getters-and-setters";
36+
}
37+
38+
}

sonar-plugin/src/main/resources/org/greencodeinitiative/creedengo/profiles/javascript_profile.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
{
22
"name": "Creedengo",
33
"ruleKeys": [
4+
<<<<<<< Updated upstream
5+
=======
6+
"GCI7",
7+
>>>>>>> Stashed changes
48
"GCI9",
59
"GCI11",
610
"GCI12",

0 commit comments

Comments
 (0)