Skip to content

Commit 6e56d5a

Browse files
GCI106 AvoidSqrtInLoop #Python #DLG #Build
Co-authored-by: DataLabGroupe-CreditAgricole <[email protected]>
1 parent c74f723 commit 6e56d5a

File tree

5 files changed

+148
-1
lines changed

5 files changed

+148
-1
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
99

1010
### Added
1111

12+
- add rule GCI106 Avoid SQRT in a loop
13+
1214
### Changed
1315

1416
- compatibility updates for SonarQube 25.5.0

src/main/java/org/greencodeinitiative/creedengo/python/PythonRuleRepository.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,8 @@ public class PythonRuleRepository implements RulesDefinition, PythonCustomRuleRe
4040
AvoidFullSQLRequest.class,
4141
AvoidListComprehensionInIterations.class,
4242
DetectUnoptimizedImageFormat.class,
43-
AvoidMultipleIfElseStatementCheck.class
43+
AvoidMultipleIfElseStatementCheck.class,
44+
AvoidSqrtInLoop.class
4445
);
4546

4647
public static final String LANGUAGE = "py";
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
/*
2+
* creedengo - Python language - Provides rules to reduce the environmental footprint of your Python programs
3+
* Copyright © 2024 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.python.checks;
19+
20+
import org.sonar.check.Rule;
21+
import org.sonar.plugins.python.api.PythonSubscriptionCheck;
22+
import org.sonar.plugins.python.api.SubscriptionContext;
23+
import org.sonar.plugins.python.api.tree.CallExpression;
24+
import org.sonar.plugins.python.api.tree.Expression;
25+
import org.sonar.plugins.python.api.tree.QualifiedExpression;
26+
import org.sonar.plugins.python.api.tree.Tree;
27+
28+
@Rule(key = "GCI106")
29+
public class AvoidSqrtInLoop extends PythonSubscriptionCheck {
30+
31+
public static final String DESCRIPTION = "Avoid using scalar sqrt functions in loops. Apply vectorized sqrt operations on arrays directly.";
32+
33+
@Override
34+
public void initialize(Context context) {
35+
context.registerSyntaxNodeConsumer(Tree.Kind.CALL_EXPR, this::checkCallExpression);
36+
}
37+
38+
private void checkCallExpression(SubscriptionContext context) {
39+
CallExpression callExpression = (CallExpression) context.syntaxNode();
40+
41+
if (isSqrtCall(callExpression) && hasLoopParent(callExpression)) {
42+
context.addIssue(callExpression, DESCRIPTION);
43+
}
44+
}
45+
46+
private boolean isSqrtCall(CallExpression callExpression) {
47+
Expression callee = callExpression.callee();
48+
49+
// Check for direct calls to math.sqrt
50+
if (callee.is(Tree.Kind.QUALIFIED_EXPR)) {
51+
QualifiedExpression qualifiedExpression = (QualifiedExpression) callee;
52+
String methodName = qualifiedExpression.name().name();
53+
54+
if ("sqrt".equals(methodName)) {
55+
Expression qualifier = qualifiedExpression.qualifier();
56+
if (qualifier != null && qualifier.is(Tree.Kind.NAME)) {
57+
String qualifierName = qualifier.firstToken().value();
58+
return "math".equals(qualifierName) || "np".equals(qualifierName) || "numpy".equals(qualifierName);
59+
}
60+
}
61+
}
62+
63+
return false;
64+
}
65+
66+
private boolean hasLoopParent(Tree tree) {
67+
for (Tree parent = tree.parent(); parent != null; parent = parent.parent()) {
68+
Tree.Kind kind = parent.getKind();
69+
if (kind == Tree.Kind.FOR_STMT || kind == Tree.Kind.WHILE_STMT) {
70+
return true;
71+
}
72+
}
73+
return false;
74+
}
75+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/*
2+
* creedengo - Python language - Provides rules to reduce the environmental footprint of your Python programs
3+
* Copyright © 2024 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.python.checks;
19+
20+
import org.junit.Test;
21+
import org.sonar.python.checks.utils.PythonCheckVerifier;
22+
23+
public class AvoidSqrtInLoopTest {
24+
@Test
25+
public void test() {
26+
PythonCheckVerifier.verify("src/test/resources/checks/avoidSqrtInLoop.py", new AvoidSqrtInLoop());
27+
}
28+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import math
2+
import numpy as np
3+
4+
def non_compliant_math_sqrt_in_for_loop():
5+
numbers = [1, 2, 3, 4, 5]
6+
results = []
7+
for num in numbers:
8+
results.append(math.sqrt(num)) # Noncompliant {{Avoid using scalar sqrt functions in loops. Apply vectorized sqrt operations on arrays directly.}}
9+
return results
10+
11+
def non_compliant_numpy_scalar_sqrt_in_for_loop():
12+
numbers = [1, 2, 3, 4, 5]
13+
results = []
14+
for num in numbers:
15+
results.append(np.sqrt(num)) # Noncompliant {{Avoid using scalar sqrt functions in loops. Apply vectorized sqrt operations on arrays directly.}}
16+
return results
17+
18+
def non_compliant_math_sqrt_in_while_loop():
19+
numbers = [1, 2, 3, 4, 5]
20+
results = []
21+
i = 0
22+
while i < len(numbers):
23+
results.append(math.sqrt(numbers[i])) # Noncompliant {{Avoid using scalar sqrt functions in loops. Apply vectorized sqrt operations on arrays directly.}}
24+
i += 1
25+
return results
26+
27+
def compliant_numpy_vectorized_sqrt():
28+
numbers = np.array([1, 2, 3, 4, 5])
29+
# Vectorized sqrt directly on the array - efficient
30+
results = np.sqrt(numbers)
31+
return results
32+
33+
def compliant_list_comprehension_with_vectorized_operation():
34+
numbers = [1, 2, 3, 4, 5]
35+
# Using numpy's vectorized sqrt on the entire array at once
36+
return np.sqrt(np.array(numbers))
37+
38+
def compliant_math_sqrt_outside_loop():
39+
# Using math.sqrt outside of a loop is OK
40+
value = 16
41+
return math.sqrt(value)

0 commit comments

Comments
 (0)