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+ }
0 commit comments