Skip to content
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]

### Added
- [#32](https://github.com/green-code-initiative/creedengo-challenge/issues/32) [EC1442] @dataclass(slots=True) shoud be declared on data classes

### Changed

Expand Down
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@
<mockito.version>5.17.0</mockito.version>

<!-- temporary version waiting for a real automatic release in creedengo repository -->
<creedengo-rules-specifications.version>2.2.2</creedengo-rules-specifications.version>
<creedengo-rules-specifications.version>main-SNAPSHOT</creedengo-rules-specifications.version>

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should not be commited

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes thank you. Done


<!-- URL of the Maven repository where sonarqube will be downloaded -->
<test-it.orchestrator.artifactory.url>https://repo1.maven.org/maven2</test-it.orchestrator.artifactory.url>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -273,4 +273,30 @@ void testGCI203_compliant() {

}

@Test
void testGCIGCI1442_compliant() {

String filePath = "src/usingSlotsOnDataClassesCompliant.py";
String ruleId = "creedengo-python:GCI1442";
String ruleMsg = "Reduce memory footprint by using @dataclass(slots=True)";
int[] startLines = new int[]{};
int[] endLines = new int[]{};

checkIssuesForFile(filePath, ruleId, ruleMsg, startLines, endLines, SEVERITY, TYPE, EFFORT_1MIN);

}

@Test
void testGCIGCI1442_nonCompliant() {

String filePath = "src/usingSlotsOnDataClassesNonCompliant.py";
String ruleId = "creedengo-python:GCI1442";
String ruleMsg = "Reduce memory footprint by using @dataclass(slots=True)";
int[] startLines = new int[]{1, 7, 13};
int[] endLines = new int[]{1, 7, 13};

checkIssuesForFile(filePath, ruleId, ruleMsg, startLines, endLines, SEVERITY, TYPE, EFFORT_1MIN);

}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
@dataclass(slots=True)
class MyClass:
a: int
b: int
c: int
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
@dataclass
class MyClass1:
a: int
b: int
c: int

@dataclass()
class MyClass2:
a: int
b: int
c: int

@dataclass(frozen=True)
class MyClass3:
a: int
b: int
c: int
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@ public class PythonRuleRepository implements RulesDefinition, PythonCustomRuleRe
AvoidFullSQLRequest.class,
AvoidListComprehensionInIterations.class,
DetectUnoptimizedImageFormat.class,
AvoidMultipleIfElseStatementCheck.class
AvoidMultipleIfElseStatementCheck.class,
UsingSlotsOnDataClasses.class
);

public static final String LANGUAGE = "py";
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/*
* creedengo - Python language - Provides rules to reduce the environmental footprint of your Python programs
* Copyright © 2024 Green Code Initiative (https://green-code-initiative.org)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.greencodeinitiative.creedengo.python.checks;

import org.sonar.check.Rule;
import org.sonar.plugins.python.api.ProjectPythonVersion;
import org.sonar.plugins.python.api.PythonSubscriptionCheck;
import org.sonar.plugins.python.api.SubscriptionContext;
import org.sonar.plugins.python.api.tree.*;

@Rule(key = "GCI1442")
public class UsingSlotsOnDataClasses extends PythonSubscriptionCheck {

private static final String DECORATOR_DATA_CLASS = "dataclass";
private static final String SLOTS_ARG = "slots";

public static final String DESCRIPTION = "Reduce memory footprint by using @dataclass(slots=True)";

@Override
public void initialize(Context context) {
if(ProjectPythonVersion.currentVersions().stream().anyMatch(version -> version.compare(3, 10) >= 0)) {
context.registerSyntaxNodeConsumer(Tree.Kind.DECORATOR, this::isUsingSlots);
}
}

private void isUsingSlots(SubscriptionContext ctx) {
Decorator decorator = ((Decorator) ctx.syntaxNode());

if (!isDecoratorDataClass(decorator)) {
return;
}

if (decorator.arguments() != null
&& decorator.arguments().arguments() != null
&& decorator.arguments().arguments().stream()
.anyMatch(argument -> argument.firstToken().value().equals(SLOTS_ARG))) {
return;
}

ctx.addIssue(decorator, DESCRIPTION);
}

private boolean isDecoratorDataClass(Decorator decorator) {
Name name = null;
// Manage decorator detected as simple expression
if (decorator.expression().is(Tree.Kind.NAME)) {
name = (Name) decorator.expression();
// manage decorator detected as callable expression
} else if(decorator.expression().is(Tree.Kind.CALL_EXPR)) {
CallExpression callExpression = (CallExpression) decorator.expression();
if (callExpression.callee().is(Tree.Kind.NAME)) {
name = (Name) callExpression.callee();
}
}
return name != null && UsingSlotsOnDataClasses.DECORATOR_DATA_CLASS.equals(name.name());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
"GCI74",
"GCI89",
"GCI203",
"GCI404"
"GCI404",
"GCI1442"
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
* creedengo - Python language - Provides rules to reduce the environmental footprint of your Python programs
* Copyright © 2024 Green Code Initiative (https://green-code-initiative.org)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.greencodeinitiative.creedengo.python.checks;

import org.junit.Test;
import org.sonar.plugins.python.api.ProjectPythonVersion;
import org.sonar.plugins.python.api.PythonVersionUtils;
import org.sonar.python.checks.utils.PythonCheckVerifier;

import java.util.Set;

public class UsingSlotsOnDataClassesTest {

@Test
public void test_using_slots_on_data_classes() {
ProjectPythonVersion.setCurrentVersions(PythonVersionUtils.allVersions());
PythonCheckVerifier.verify("src/test/resources/checks/usingSlotsOnDataClassesNonCompliant.py", new UsingSlotsOnDataClasses());
PythonCheckVerifier.verifyNoIssue("src/test/resources/checks/usingSlotsOnDataClassesCompliant.py", new UsingSlotsOnDataClasses());
}

@Test
public void test_using_slots_on_data_classes_python_less_than_310() {
ProjectPythonVersion.setCurrentVersions(Set.of(PythonVersionUtils.Version.V_39));
PythonCheckVerifier.verifyNoIssue("src/test/resources/checks/usingSlotsOnDataClassesCompliantV39.py", new UsingSlotsOnDataClasses());
}

}
6 changes: 6 additions & 0 deletions src/test/resources/checks/usingSlotsOnDataClassesCompliant.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# COMPLIANT
@dataclass(slots=True)
class MyClass:
a: int
b: int
c: int
17 changes: 17 additions & 0 deletions src/test/resources/checks/usingSlotsOnDataClassesCompliantV39.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
@dataclass
class MyClass:
a: int
b: int
c: int

@dataclass()
class MyClass:
a: int
b: int
c: int

@dataclass(frozen=True)
class MyClass:
a: int
b: int
c: int
17 changes: 17 additions & 0 deletions src/test/resources/checks/usingSlotsOnDataClassesNonCompliant.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
@dataclass # Noncompliant {{Reduce memory footprint by using @dataclass(slots=True)}}
class MyClass:
a: int
b: int
c: int

@dataclass() # Noncompliant {{Reduce memory footprint by using @dataclass(slots=True)}}
class MyClass:
a: int
b: int
c: int

@dataclass(frozen=True) # Noncompliant {{Reduce memory footprint by using @dataclass(slots=True)}}
class MyClass:
a: int
b: int
c: int