Skip to content

Commit 8d55013

Browse files
authored
Fix false positives caused by import checks in some rules (#103)
Co-authored-by: utarwyn <utarwyn@users.noreply.github.com>
1 parent b458bdf commit 8d55013

8 files changed

Lines changed: 155 additions & 75 deletions

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1010
### Fixed
1111

1212
- [#101](https://github.com/green-code-initiative/creedengo-javascript/pull/101) Fix spread style attributes in GCI26 and GCI29 rules
13+
- [#103](https://github.com/green-code-initiative/creedengo-javascript/pull/103) Fix false positives caused by import checks in GCI505, GCI522 and GCI523 rules
1314

1415
## [3.0.0] - 2026-02-03
1516

eslint-plugin/lib/rules/avoid-brightness-override.js

Lines changed: 10 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818

1919
"use strict";
2020

21+
const { createDefaultImportsTracker } = require("../utils/import-tracker");
22+
2123
const brightnessLibrariesMethods = {
2224
"expo-brightness": [
2325
"setBrightnessAsync",
@@ -45,30 +47,18 @@ module.exports = {
4547
schema: [],
4648
},
4749
create: function (context) {
48-
const librariesFoundInImports = [];
50+
const { importedObjects, ImportDeclaration } = createDefaultImportsTracker(
51+
brightnessLibrariesMethods,
52+
);
4953

5054
return {
51-
ImportDeclaration(node) {
52-
const currentLibrary = node.source.value;
53-
54-
if (brightnessLibrariesMethods[currentLibrary]) {
55-
librariesFoundInImports.push(currentLibrary);
56-
}
57-
},
55+
ImportDeclaration,
5856
MemberExpression(node) {
59-
if (librariesFoundInImports.length === 0) {
60-
return;
61-
}
57+
if (importedObjects.size === 0) return;
6258

63-
if (
64-
librariesFoundInImports.some((library) =>
65-
brightnessLibrariesMethods[library].includes(node.property.name),
66-
)
67-
) {
68-
context.report({
69-
node,
70-
messageId: "ShouldAvoidOverrideBrightness",
71-
});
59+
const methods = importedObjects.get(node.object?.name);
60+
if (methods?.includes(node.property.name)) {
61+
context.report({ node, messageId: "ShouldAvoidOverrideBrightness" });
7262
}
7363
},
7464
};

eslint-plugin/lib/rules/avoid-high-accuracy-geolocation.js

Lines changed: 9 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818

1919
"use strict";
2020

21+
const { createDefaultImportsTracker } = require("../utils/import-tracker");
22+
2123
const geolocationLibrariesMethods = {
2224
"expo-location": ["enableNetworkProviderAsync"],
2325
};
@@ -37,26 +39,17 @@ module.exports = {
3739
schema: [],
3840
},
3941
create: function (context) {
40-
const librariesFoundInImports = [];
42+
const { importedObjects, ImportDeclaration } = createDefaultImportsTracker(
43+
geolocationLibrariesMethods,
44+
);
4145

4246
return {
43-
ImportDeclaration(node) {
44-
const currentLibrary = node.source.value;
45-
46-
if (geolocationLibrariesMethods[currentLibrary]) {
47-
librariesFoundInImports.push(currentLibrary);
48-
}
49-
},
47+
ImportDeclaration,
5048
MemberExpression(node) {
51-
if (librariesFoundInImports.length === 0) {
52-
return;
53-
}
49+
if (importedObjects.size === 0) return;
5450

55-
if (
56-
librariesFoundInImports.some((library) =>
57-
geolocationLibrariesMethods[library].includes(node.property.name),
58-
)
59-
) {
51+
const methods = importedObjects.get(node.object?.name);
52+
if (methods?.includes(node.property.name)) {
6053
reportAvoidUsingAccurateGeolocation(context, node);
6154
}
6255
},

eslint-plugin/lib/rules/avoid-keep-awake.js

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -37,26 +37,31 @@ module.exports = {
3737
schema: [],
3838
},
3939
create: function (context) {
40-
const librariesFoundInImports = [];
40+
// Maps the local identifier name to true when it was imported from a keep-awake library.
41+
// Using local names handles aliased imports: import { activateKeepAwake as ka } from '…'
42+
const importedMethods = new Set();
4143

4244
return {
4345
ImportDeclaration(node) {
4446
const currentLibrary = node.source.value;
47+
const methods = keepAwakeLibrariesMethods[currentLibrary];
4548

46-
if (keepAwakeLibrariesMethods[currentLibrary]) {
47-
librariesFoundInImports.push(currentLibrary);
49+
if (!methods) return;
50+
51+
for (const specifier of node.specifiers) {
52+
if (specifier.type === "ImportSpecifier") {
53+
const importedName = specifier.imported.name;
54+
if (methods.includes(importedName)) {
55+
// Track the local alias so renamed imports are also caught
56+
importedMethods.add(specifier.local.name);
57+
}
58+
}
4859
}
4960
},
5061
CallExpression(node) {
51-
if (librariesFoundInImports.length === 0) {
52-
return;
53-
}
62+
if (importedMethods.size === 0) return;
5463

55-
if (
56-
librariesFoundInImports.some((library) =>
57-
keepAwakeLibrariesMethods[library].includes(node.callee.name),
58-
)
59-
) {
64+
if (importedMethods.has(node.callee.name)) {
6065
context.report({ node, messageId: "AvoidKeepAwake" });
6166
}
6267
},
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
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+
* Creates a shared import tracker used by rules that need to fire on
23+
* `importedObj.method()` only when `importedObj` was actually imported
24+
* from a known library.
25+
*
26+
* Returns the shared `importedObjects` map (localName → forbidden methods)
27+
* and a ready-to-use `ImportDeclaration` AST visitor that populates it.
28+
* Rules can spread the returned object directly into their visitor map.
29+
*
30+
* @param {Record<string, string[]>} libraryMethods
31+
* Map of library name → list of forbidden method names.
32+
* @returns {{ importedObjects: Map<string, string[]>, ImportDeclaration: Function }}
33+
*/
34+
function createDefaultImportsTracker(libraryMethods) {
35+
/** @type {Map<string, string[]>} */
36+
const importedObjects = new Map();
37+
38+
function ImportDeclaration(node) {
39+
const methods = libraryMethods[node.source.value];
40+
if (!methods) return;
41+
42+
for (const specifier of node.specifiers) {
43+
const localName = specifier.local.name;
44+
if (!importedObjects.has(localName)) {
45+
importedObjects.set(localName, []);
46+
}
47+
importedObjects.get(localName).push(...methods);
48+
}
49+
}
50+
51+
return { importedObjects, ImportDeclaration };
52+
}
53+
54+
module.exports = { createDefaultImportsTracker };

eslint-plugin/tests/lib/rules/avoid-brightness-override.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,22 @@ ruleTester.run("avoid-brightness-override", rule, {
7979
console.log('brightness', brightness);
8080
});
8181
`,
82+
// False positive guard: a brightness library is imported but the flagged method is called
83+
// on an unrelated object – it must NOT be reported.
84+
`
85+
import * as Brightness from 'expo-brightness';
86+
87+
const someOtherObj = {};
88+
someOtherObj.setBrightnessAsync(0.5);
89+
`,
90+
// False positive guard: two brightness libraries are imported; calling a method belonging
91+
// to library B on an object that was imported from library A must NOT be reported.
92+
`
93+
import * as Brightness from 'expo-brightness';
94+
import DeviceBrightness from 'react-native-device-brightness';
95+
96+
Brightness.setBrightnessLevel(0.5);
97+
`,
8298
],
8399

84100
invalid: [

eslint-plugin/tests/lib/rules/avoid-high-accuracy-geolocation.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,14 @@ ruleTester.run("avoid-high-accuracy-geolocation", rule, {
7878
7979
Location.requestPermissionsAsync();
8080
`,
81+
// False positive guard: expo-location is imported but the flagged method is called on an
82+
// unrelated object – it must NOT be reported.
83+
`
84+
import * as Location from 'expo-location';
85+
86+
const someOtherObj = {};
87+
someOtherObj.enableNetworkProviderAsync();
88+
`,
8189
],
8290

8391
invalid: [

eslint-plugin/tests/lib/rules/avoid-keep-awake.js

Lines changed: 41 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -53,37 +53,50 @@ const expectedErrorFunction = {
5353

5454
ruleTester.run("avoid-keep-awake", rule, {
5555
valid: [
56-
{
57-
code: `
58-
import React from 'react';
59-
import { Text, View } from 'react-native';
60-
61-
export default function ValidExample() {
56+
`
57+
import React from 'react';
58+
import { Text, View } from 'react-native';
59+
60+
export default function ValidExample() {
61+
return (
62+
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
63+
<Text>This screen will sleep!</Text>
64+
</View>
65+
);
66+
}
67+
`,
68+
`
69+
import React from 'react';
70+
import { useKeepAwake } from 'other-library';
71+
import { Button, View } from 'react-native';
72+
73+
export default class ValidExample extends React.Component {
74+
render() {
75+
useKeepAwake();
6276
return (
63-
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
64-
<Text>This screen will sleep!</Text>
65-
</View>
77+
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}></View>
6678
);
6779
}
68-
`,
69-
},
70-
{
71-
code: `
72-
import React from 'react';
73-
import { useKeepAwake } from 'other-library';
74-
import { Button, View } from 'react-native';
75-
76-
export default class ValidExample extends React.Component {
77-
render() {
78-
useKeepAwake();
79-
return (
80-
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
81-
</View>
82-
);
83-
}
84-
}
85-
`,
86-
},
80+
}
81+
`,
82+
// False positive guard: expo-keep-awake is imported for a side-effect but the
83+
// function with the same name is defined locally – it must NOT be flagged.
84+
`
85+
import 'expo-keep-awake';
86+
87+
function activateKeepAwake() {
88+
// local helper, unrelated to the library
89+
}
90+
activateKeepAwake();
91+
`,
92+
// False positive guard: a different export from expo-keep-awake is imported, while
93+
// activateKeepAwake comes from another library – it must NOT be flagged.
94+
`
95+
import { deactivateKeepAwake } from 'expo-keep-awake';
96+
import { activateKeepAwake } from 'some-other-library';
97+
98+
activateKeepAwake();
99+
`,
87100
],
88101
invalid: [
89102
{

0 commit comments

Comments
 (0)