From 6bf103e7b8b9f002db977d6e8939872e96aa6005 Mon Sep 17 00:00:00 2001 From: David DE CARVALHO Date: Sat, 5 Aug 2023 23:33:43 +0200 Subject: [PATCH 1/9] [ISSUE 142] new python rule : AvoidMultipleIfElseStatement --- CHANGELOG.md | 1 + .../python/PythonRuleRepository.java | 3 +- .../AvoidMultipleIfElseStatementCheck.java | 401 ++++++++++++++++++ .../python/PythonRuleRepositoryTest.java | 2 +- ...AvoidMultipleIfElseStatementCheckTest.java | 36 ++ .../avoidMultipleIfElseStatementCompliant.py | 138 ++++++ ...voidMultipleIfElseStatementNonCompliant.py | 188 ++++++++ 7 files changed, 767 insertions(+), 2 deletions(-) create mode 100644 src/main/java/fr/greencodeinitiative/python/checks/AvoidMultipleIfElseStatementCheck.java create mode 100644 src/test/java/fr/greencodeinitiative/python/checks/AvoidMultipleIfElseStatementCheckTest.java create mode 100644 src/test/resources/checks/avoidMultipleIfElseStatementCompliant.py create mode 100644 src/test/resources/checks/avoidMultipleIfElseStatementNonCompliant.py diff --git a/CHANGELOG.md b/CHANGELOG.md index e38d70c..e6ff4c0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Python rules moved from `ecoCode` repository to current repository +- [#142](https://github.com/green-code-initiative/ecoCode/issues/142) new Python rule : Multiple if-else statement + refactoring implementation - [#205](https://github.com/green-code-initiative/ecoCode/issues/205) compatibility with SonarQube 10.1 ### Changed diff --git a/src/main/java/fr/greencodeinitiative/python/PythonRuleRepository.java b/src/main/java/fr/greencodeinitiative/python/PythonRuleRepository.java index 23ff4f5..ce96d81 100644 --- a/src/main/java/fr/greencodeinitiative/python/PythonRuleRepository.java +++ b/src/main/java/fr/greencodeinitiative/python/PythonRuleRepository.java @@ -63,7 +63,8 @@ public List checkClasses() { NoFunctionCallWhenDeclaringForLoop.class, AvoidFullSQLRequest.class, AvoidListComprehensionInIterations.class, - DetectUnoptimizedImageFormat.class + DetectUnoptimizedImageFormat.class, + AvoidMultipleIfElseStatementCheck.class ); } } diff --git a/src/main/java/fr/greencodeinitiative/python/checks/AvoidMultipleIfElseStatementCheck.java b/src/main/java/fr/greencodeinitiative/python/checks/AvoidMultipleIfElseStatementCheck.java new file mode 100644 index 0000000..fa300c8 --- /dev/null +++ b/src/main/java/fr/greencodeinitiative/python/checks/AvoidMultipleIfElseStatementCheck.java @@ -0,0 +1,401 @@ +/* + * SonarQube PHP Custom Rules Example + * Copyright (C) 2016-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package fr.greencodeinitiative.python.checks; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.sonar.check.Rule; +import org.sonar.plugins.python.api.PythonSubscriptionCheck; +import org.sonar.plugins.python.api.SubscriptionContext; +import org.sonar.plugins.python.api.tree.BinaryExpression; +import org.sonar.plugins.python.api.tree.ElseClause; +import org.sonar.plugins.python.api.tree.Expression; +import org.sonar.plugins.python.api.tree.FunctionDef; +import org.sonar.plugins.python.api.tree.IfStatement; +import org.sonar.plugins.python.api.tree.Statement; +import org.sonar.plugins.python.api.tree.Tree; +import static org.sonar.plugins.python.api.tree.Tree.Kind.FUNCDEF; + +/** + * FUNCTIONAL DESCRIPTION : please see ASCIIDOC description file of this rule (inside `ecocode-rules-spcifications`) + * TECHNICAL CHOICES : + * - Kind.IF_STATEMENT, Kind.ELSE_STATEMENT, Kind.ELSEIF_STATEMENT not used because it isn't possible + * to keep parent references to check later if variables already used or not in parent tree + * - only one way to keep parent history : manually go throw the all tree and thus, start at method declaration + * - an "ELSE" statement is considered as a second IF statement using the same variables used on previous + * - IF and ELSEIF statements are considered as an IF statement + */ +@Rule(key = "EC2") +public class AvoidMultipleIfElseStatementCheck extends PythonSubscriptionCheck { + + public static final String ERROR_MESSAGE = "Use a switch statement instead of multiple if-else if possible"; + + // data structure for following usage of variable inside all the AST tree + private VariablesPerLevelDataStructure variablesStruct = new VariablesPerLevelDataStructure(); + + // only visit each method to keep data of all conditional tree + // with IF, ELSE or ELSEIF statements, we can't keep all data of conditional tree + @Override + public void initialize(Context context) { + context.registerSyntaxNodeConsumer(FUNCDEF, this::visitFuncDef); + } + + private void visitFuncDef(SubscriptionContext ctx) { + visitNode(ctx, ctx.syntaxNode()); + } + + public void visitNode(SubscriptionContext context, Tree pTree) { + + FunctionDef method = (FunctionDef)pTree; + +// if (!method.body().is(Tree.Kind.)) { +// return; +// } + + // reinit data structure before each method analysis + variablesStruct = new VariablesPerLevelDataStructure(); + + // starting visit + visitNodeContent(context, method.body().statements(), 0); + + } + + /** + * Visit all content of a node for one level (with its statements list) + * + * @param pLstStatements statements list of current node + * @param pLevel level of current node + */ + private void visitNodeContent(SubscriptionContext context, List pLstStatements, int pLevel) { + if (pLstStatements == null || pLstStatements.isEmpty()) { + return; + } + + for (Statement statement : pLstStatements) { +// if (statement.is(Kind.BLOCK)) { +// // the cirrent node is a block : visit block content +// visitNodeContent(((BlockTree)statement).statements(), pLevel); +// } else + if (statement.is(Tree.Kind.IF_STMT)) { + visitIfNode(context, (IfStatement)statement, pLevel); + } + } + } + + /** + * Visit an IF type node + * @param pIfTree the current node (Tree type) + * @param pLevel the level of node + */ + private void visitIfNode(SubscriptionContext context, IfStatement pIfTree, int pLevel) { + + if (pIfTree == null) return; + + // init current if structure with cleaning child levels + variablesStruct.reinitVariableUsageForLevel(pLevel + 1); + // init current if structure with cleaning for ELSE process checking + variablesStruct.reinitVariableUsageForLevelForCurrentIfStruct(pLevel); + + // analyze condition variables and raise error if needed + computeIfVariables(context, pIfTree, pLevel); + + // visit the content of if block + visitNodeContent(context, pIfTree.body().statements(), pLevel + 1); + + // analyze ELSEIF clauses + if (pIfTree.elifBranches() != null && !pIfTree.elifBranches().isEmpty()) { + for (IfStatement elseifClause : pIfTree.elifBranches()) { + visitElseIfNode(context, elseifClause, pLevel); + } + } + + // analyze ELSE clause + visitElseNode(context, pIfTree.elseBranch(), pLevel); + + } + + /** + * Analyze and compute variables usage for IF AST structure + * @param pIfTree IF node + * @param pLevel the level of IF node + */ + private void computeIfVariables(SubscriptionContext context, IfStatement pIfTree, int pLevel) { + + if (pIfTree.condition() == null) return; + + // analysing content of conditions of IF node + Expression expr = pIfTree.condition(); + if (expr instanceof BinaryExpression) { + computeConditionVariables(context, (BinaryExpression) expr, pLevel); + } + + } + + /** + * Analyze and compute variables usage for Expression structure + * @param pBinExprTree binary expression to analyze + * @param pLevel The level of binary expression + */ + private void computeConditionVariables(SubscriptionContext context, BinaryExpression pBinExprTree, int pLevel) { + + // if multiple conditions, continue with each part of complex expression + if (pBinExprTree.is(Tree.Kind.AND) || pBinExprTree.is(Tree.Kind.OR)) { + if (pBinExprTree.leftOperand() instanceof BinaryExpression) { + computeConditionVariables(context, (BinaryExpression) pBinExprTree.leftOperand(), pLevel); + } + if (pBinExprTree.rightOperand() instanceof BinaryExpression) { + computeConditionVariables(context, (BinaryExpression) pBinExprTree.rightOperand(), pLevel); + } + } else if (pBinExprTree.is(Tree.Kind.COMPARISON)) { +// else if (pBinExprTree.is(Tree.Kind.EQUAL_TO) +// || pBinExprTree.is(Tree.Kind.NOT_EQUAL_TO) +// || pBinExprTree.is(Tree.Kind.GREATER_THAN) +// || pBinExprTree.is(Tree.Kind.GREATER_THAN_OR_EQUAL_TO) +// || pBinExprTree.is(Tree.Kind.LESS_THAN_OR_EQUAL_TO) +// || pBinExprTree.is(Tree.Kind.LESS_THAN) +// ) { + + // continue analyze with variables if some key-words are found +// if (pBinExprTree.leftOperand().is(Tree.Kind.VARIABLE_IDENTIFIER)) { + if ("IDENTIFIER".equals(pBinExprTree.leftOperand().firstToken().type().getName())) { +// computeVariables(context, (VariableIdentifierTree) pBinExprTree.leftOperand(), pLevel); + computeVariables(context, pBinExprTree.leftOperand(), pLevel); + } + if ("IDENTIFIER".equals(pBinExprTree.rightOperand().firstToken().type().getName())) { +// if (pBinExprTree.rightOperand().is(Tree.Kind.VARIABLE_IDENTIFIER)) { +// computeVariables(context, (VariableIdentifierTree) pBinExprTree.rightOperand(), pLevel); + computeVariables(context, pBinExprTree.rightOperand(), pLevel); + } + } + } + + /** + * Analyze and compute variables usage for Variable AST structure + * @param pVarIdTree The Variable AST structure + * @param pLevel the level of structure + */ +// private void computeVariables(SubscriptionContext context, VariableIdentifierTree pVarIdTree, int pLevel) { + private void computeVariables(SubscriptionContext context, Expression pVarIdTree, int pLevel) { +// if (pVarIdTree.variableExpression().is(Kind.VARIABLE_IDENTIFIER)) { + // increment the variable counter to list of all variables +// int nbUsed = variablesStruct.incrementVariableUsageForLevel(pVarIdTree.text(), pLevel); + int nbUsed = variablesStruct.incrementVariableUsageForLevel(pVarIdTree.firstToken().value(), pLevel); + + // increment variable counter to list of variables already declared for current if or elseif struture +// variablesStruct.incrementVariableUsageForLevelForCurrentIfStruct(pVarIdTree.text(), pLevel); + variablesStruct.incrementVariableUsageForLevelForCurrentIfStruct(pVarIdTree.firstToken().value(), pLevel); + + // raise an error if maximum + if (nbUsed > 2) { + context.addIssue(pVarIdTree.firstToken(), ERROR_MESSAGE); + } +// } + } + + /** + * Analyze and compute variables usage for ELSEIF AST structure + * @param pElseIfTree ELSEIF node + * @param pLevel the level of ELSEIF node + */ + private void visitElseIfNode(SubscriptionContext context, IfStatement pElseIfTree, int pLevel) { + + if (pElseIfTree == null) { return; } + + // init current if structure with cleaning child levels + variablesStruct.reinitVariableUsageForLevel(pLevel + 1); + + // init current if structure with cleaning for else verification + variablesStruct.reinitVariableUsageForLevelForCurrentIfStruct(pLevel); + + // analyze variables and raise error if needed + computeElseIfVariables(context, pElseIfTree, pLevel); + + // go to next child level + visitNodeContent(context, pElseIfTree.body().statements(), pLevel + 1); + } + + /** + * Analyze and compute variables usage for ELSEIF AST structure + * @param pElseIfTree ELSEIF node + * @param pLevel the level of ELSEIF node + */ + private void computeElseIfVariables(SubscriptionContext context, IfStatement pElseIfTree, int pLevel) { + + if (pElseIfTree.condition() == null) return; + + Expression expr = pElseIfTree.condition(); + if (expr instanceof BinaryExpression) { + computeConditionVariables(context, (BinaryExpression) expr, pLevel); + } + + } + + /** + * Analyze and compute variables usage for ELSE AST structure + * @param pElseTree ELSE node + * @param pLevel the level of ELSE node + */ + private void visitElseNode(SubscriptionContext context, ElseClause pElseTree, int pLevel) { + + if (pElseTree == null) { return; } + + // analyze variables and raise error if needed + computeElseVariables(context, pElseTree, pLevel); + + // go to next child level + visitNodeContent(context, pElseTree.body().statements(), pLevel + 1); + } + + /** + * Analyze and compute variables usage for ELSE AST structure + * @param pElseTree ELSE node + * @param pLevel the level of ELSE node + */ + private void computeElseVariables(SubscriptionContext context, ElseClause pElseTree, int pLevel) { + + for (Map.Entry entry : variablesStruct.getVariablesForCurrentIfStruct(pLevel).entrySet()) { + String variableName = entry.getKey(); + + // increment usage of all variables in the same level of ELSE staetement + int nbUsed = variablesStruct.incrementVariableUsageForLevel(variableName, pLevel); + + // increment variable counter to list of variables already declared for current if or elseif struture + variablesStruct.incrementVariableUsageForLevelForCurrentIfStruct(variableName, pLevel); + + // raise an error if maximum + if (nbUsed > 2) { + context.addIssue(pElseTree.firstToken(), ERROR_MESSAGE); + } + } + } + + /** + * Complex data structure representing variables counters per AST level (cumulative counts with parent levels) + * Map> ==> + * - Key : index of Level (0 = first level) + * - Value : Map + * - Key : name of variable in the current or parent level + * - Value : number of usage of this variable in an IF statement in current level or one of parent levels + * + */ + private static class VariablesPerLevelDataStructure { + + // global map variable counters per level + private final Map> mapVariablesPerLevel; + + // map variable counters per level for current If / ElseIf structure + // purpose : used by compute variables Else process (because Else structure is particular : + // we don't know previous variables and we need previous If / ElseIf structure to know variables) + private final Map> mapVariablesPerLevelForCurrentIfStruct; + + public VariablesPerLevelDataStructure() { + mapVariablesPerLevel = new HashMap<>(10); + mapVariablesPerLevelForCurrentIfStruct = new HashMap<>(10); + } + + /** + * increment variable counters on global map + */ + public int incrementVariableUsageForLevel(String variableName, int pLevel) { + return internalIncrementVariableUsage(mapVariablesPerLevel, variableName, pLevel); + } + + /** + * increment variable counters on input map + */ + private int internalIncrementVariableUsage(Map> pDataMap, String variableName, int pLevel) { + + // get variable usage map for current level and init if null + Map variablesMap = pDataMap.computeIfAbsent(pLevel, k -> new HashMap<>(5)); + + // get usage from parent if needed + Integer nbUsed = variablesMap.get(variableName); + if (nbUsed == null) { + Integer nbParentUsed = internalGetVariableUsageOfNearestParent(pDataMap, variableName, pLevel - 1); + nbUsed = nbParentUsed == null ? 0 : nbParentUsed; + } + + // increment usage for current level + nbUsed++; + variablesMap.put(variableName, nbUsed); + + return nbUsed; + } + + /** + * get usage of a variable in top tree (nearest top parent) + */ + private Integer internalGetVariableUsageOfNearestParent(Map> pDataMap, String variableName, int pLevel) { + + Integer nbParentUsed = null; + for (int i = pLevel; i >= 0 && nbParentUsed == null; i--) { + Map variablesParentLevelMap = pDataMap.get(i); + nbParentUsed = variablesParentLevelMap.get(variableName); + } + + return nbParentUsed; + } + + /** + * reinitialization of variable usages for input level and global map + */ + public void reinitVariableUsageForLevel(int pLevel) { + internalReinitVariableUsageForLevelForCurrentIfStruct(mapVariablesPerLevel, pLevel); + } + + /** + * reinitialization of variable usages in input level in input map + */ + private void internalReinitVariableUsageForLevelForCurrentIfStruct(Map> pDataMap, int pLevel) { + if (pDataMap.get(pLevel) == null) { return; } + + // cleaning of current If Structure beginning at level specified + for (int i = pLevel; i < pDataMap.size(); i++) { + pDataMap.remove(i); + } + + } + + /** + * reinitialization of variable usages for input level on if/elseif map + */ + public void reinitVariableUsageForLevelForCurrentIfStruct(int pLevel) { + internalReinitVariableUsageForLevelForCurrentIfStruct(mapVariablesPerLevelForCurrentIfStruct, pLevel); + } + + /** + * increment variable counters on if/elseif map + */ + public void incrementVariableUsageForLevelForCurrentIfStruct(String variableName, int pLevel) { + internalIncrementVariableUsage(mapVariablesPerLevelForCurrentIfStruct, variableName, pLevel); + } + + /** + * get usage of a variable in a level on if/elseif map + */ + public Map getVariablesForCurrentIfStruct(int pLevel) { + return mapVariablesPerLevelForCurrentIfStruct.get(pLevel); + } + + } + +} diff --git a/src/test/java/fr/greencodeinitiative/python/PythonRuleRepositoryTest.java b/src/test/java/fr/greencodeinitiative/python/PythonRuleRepositoryTest.java index 714ef27..eab6da2 100644 --- a/src/test/java/fr/greencodeinitiative/python/PythonRuleRepositoryTest.java +++ b/src/test/java/fr/greencodeinitiative/python/PythonRuleRepositoryTest.java @@ -77,7 +77,7 @@ void testMetadata() { @Test void testRegistredRules() { - assertThat(repository.rules()).hasSize(10); + assertThat(repository.rules()).hasSize(11); } @Test diff --git a/src/test/java/fr/greencodeinitiative/python/checks/AvoidMultipleIfElseStatementCheckTest.java b/src/test/java/fr/greencodeinitiative/python/checks/AvoidMultipleIfElseStatementCheckTest.java new file mode 100644 index 0000000..58da782 --- /dev/null +++ b/src/test/java/fr/greencodeinitiative/python/checks/AvoidMultipleIfElseStatementCheckTest.java @@ -0,0 +1,36 @@ +/* + * SonarQube PHP Custom Rules Example + * Copyright (C) 2016-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package fr.greencodeinitiative.python.checks; + +import org.junit.Test; +import org.sonar.python.checks.utils.PythonCheckVerifier; + +/** + * Test class to test the check implementation. + */ +public class AvoidMultipleIfElseStatementCheckTest { + + @Test + public void test() throws Exception { + PythonCheckVerifier.verifyNoIssue("src/test/resources/checks/avoidMultipleIfElseStatementCompliant.py", new AvoidMultipleIfElseStatementCheck()); + PythonCheckVerifier.verify("src/test/resources/checks/avoidMultipleIfElseStatementNonCompliant.py", new AvoidMultipleIfElseStatementCheck()); + } + +} diff --git a/src/test/resources/checks/avoidMultipleIfElseStatementCompliant.py b/src/test/resources/checks/avoidMultipleIfElseStatementCompliant.py new file mode 100644 index 0000000..7733cbc --- /dev/null +++ b/src/test/resources/checks/avoidMultipleIfElseStatementCompliant.py @@ -0,0 +1,138 @@ +# functional RULES : please see HTML description file of this rule (resources directory) + +# /////////////////////////////////////////////////////////////////////////////////////////////////////////////// +# /////////////////////////////////////////////////////////////////////////////////////////////////////////////// +# +# COMPLIANT use cases +# +# /////////////////////////////////////////////////////////////////////////////////////////////////////////////// +# /////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +# COMPLIANT +# USE CASE : compliant use case to check if a variable is used maximum twice on several IF / ELSE statements +# at the same level AND no problem with several IF staments at the same level using different variables +def shouldBeCompliantBecauseVariablesUsedMaximumTwiceAndDifferentsVariablesUsed(): + nb1 = 0 + nb2 = 0 + nb3 = 0 + if nb3 != 1 and nb1 > 1 : + nb1 = 1 + else : + nb2 = 2 + if nb2 == 2 : + nb1 = 3 + else : + nb1 = 4 + return nb1 + +# COMPLIANT +# USE CASE : compliant use case to check if a variable is used maximum twice on several IF / ELSE statements +# at the same level AND no problem with several IF staments at the same level using different variables +def shouldBeCompliantBecauseVariablesUsedMaximumTwiceAndDifferentsVariablesUsedAtDiffLevels(): + nb1 = 0 + nb2 = 0 + nb3 = 0 + if nb1 < 1: + if nb2 == 2: + nb3 = 3 + else: + nb3 = 4 + else: + nb2 = 2 + if nb3 >= 1: + if nb2 == 2: + nb1 = 3 + else: + nb1 = 4 + else: + nb1 = 2 + return nb1 + +# COMPLIANT +# USE CASE : compliant use case to check if a variable is used maximum twice on several IF / ELSE statements +# at the same level AND no problem with several IF staments at the same level using different variables +def shouldBeCompliantBecauseVariablesUsedMaximumTwiceAndDiffVariablesUsedAtDiffLevelsScenario2(): + nb1 = 0 + nb2 = 0 + nb3 = 0 + if nb1 <= 1: + if nb2 == 2: + if nb3 == 2: + nb3 = 3 + else: + nb3 = 4 + else: + nb3 = 4 + else: + nb2 = 2 + if nb3 == 1: + if nb2 == 2: + nb1 = 3 + else: + nb1 = 4 + else: + nb1 = 2 + return nb1 + +# COMPLIANT +# USE CASE : compliant use case to check if one variable is used maximum twice in different IF statements +def shouldBeCompliantBecauseVariableUsedMaximumTwiceInIfStatements(): + nb1 = 0 + if nb1 == 1: + nb1 = 1 + if nb1 == 2: + nb1 = 3 + return nb1 + +# COMPLIANT +# USE CASE : compliant use case to check if following is OK : +# - two uses of the same variable +# - usage of the same variable on different levels of IF statements +def shouldBeCompliantBecauseSereralVariablesUsedMaximumTwiceInComposedElseStatements(): + nb1 = 0 + nb2 = 0 + nb3 = 0 + if nb1 == 1: + nb1 = 2 + else: + if nb2 == 2: + nb1 = 1 + else: + if nb3 == 4: + nb1 = 3 + else: + nb1 = 6 + return nb1 + +# COMPLIANT +# USE CASE : compliant use case to check if following is OK : +# - two uses of the same variable +# - usage of the same variable on different kind of test statements (IF and elif) +def shouldBeCompliantBecauseVariableUsedMaximumTwiceInIfOrelifStatements(): + nb1 = 0 + nb2 = 10 + if nb1 == 1: + nb2 = 1 + elif nb1 == nb2: + nb2 = 2 + return nb2 + +# COMPLIANT +# USE CASE : compliant use case to check if following is OK : +# - two uses of the same variable +# - usage of the same variable on different kind of test statements (IF and elif) +def shouldBeCompliantBecauseSeveralVariablesUsedMaximumTwiceInIfOrelifStatements(): + nb1 = 0 + nb2 = 10 + nb3 = 3 + nb4 = 1 + nb5 = 2 + if nb1 == 1: + nb2 = 1 + elif nb3 == nb2: + nb2 = 2 + elif nb4 == nb5: + nb2 = 4 + else: + nb2 = 3 + return nb2 diff --git a/src/test/resources/checks/avoidMultipleIfElseStatementNonCompliant.py b/src/test/resources/checks/avoidMultipleIfElseStatementNonCompliant.py new file mode 100644 index 0000000..61aa001 --- /dev/null +++ b/src/test/resources/checks/avoidMultipleIfElseStatementNonCompliant.py @@ -0,0 +1,188 @@ +# functional RULES : please see HTML description file of this rule (resources directory) + +# /////////////////////////////////////////////////////////////////////////////////////////////////////////////// +# /////////////////////////////////////////////////////////////////////////////////////////////////////////////// +# +# NON COMPLIANT use cases +# +# /////////////////////////////////////////////////////////////////////////////////////////////////////////////// +# /////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +# NON COMPLIANT +# USE CASE : Non compliant use case to check if following is NON OK : +# - two uses of the same variable +# - usage of the same variable on different levels of IF statements +def shouldBeCompliantBecauseVariableUsedMaximumTwiceInComposedElseStatements(): + nb1 = 0 + if nb1 == 1: + nb1 = 2 + else: + if nb1 == 2: # Noncompliant {{Use a switch statement instead of multiple if-else if possible}} + nb1 = 1 + return nb1 + +# NON COMPLIANT +# USE CASE : non compliant use case to check if a variable is not used max twice on several IF / ELSE statements +# at the same level +def shouldBeNotCompliantBecauseVariablesUsedMaximumTwiceAndDifferentsVariablesUsed(): + nb1 = 0 + nb2 = 0 + nb3 = 0 + if nb3 == 1 and nb3 == 2 and nb3 == 3: # Noncompliant {{Use a switch statement instead of multiple if-else if possible}} + nb1 = 1 + else: # Noncompliant {{Use a switch statement instead of multiple if-else if possible}} + nb2 = 2 + if nb2 == 2: + nb1 = 3 + else: + nb1 = 4 + return nb1 + +# NON COMPLIANT +# USE CASE : NON compliant use case to check if following is NOT COMPLIANT : +# one variable is used maximum in two IF / ELSE / elif statements +def shouldBeNotCompliantBecauseVariablesIsUsedMoreThanTwice(): + nb1 = 0 + if nb1 == 1: + nb1 = 2 + else: + nb1 = 3 + if nb1 == 2: # Noncompliant {{Use a switch statement instead of multiple if-else if possible}} + nb1 = 4 + return nb1 + +# NON COMPLIANT +# USE CASE : NON compliant use case to check if following is NOT OK : +# - same variable used maximum twice : no compliant because 2 IFs and 1 ELSE +def shouldBeNotCompliantBecauseVariableUsedMoreThanTwiceInIfStatementsAtDifferentsLevels(): + nb1 = 0 + if nb1 == 1: + if nb1 == 2: + nb1 = 1 + else: # Noncompliant {{Use a switch statement instead of multiple if-else if possible}} + nb1 = 3 + else: + nb1 = 2 + return nb1 + +# NON COMPLIANT +# USE CASE : non compliant use case to check if following is NOT OK : +# - two uses of the same variable : use thre times with 2 IFs and 1 ELSE +# - usage of the same variable on different levels of IF statements +def shouldBeNotCompliantBecauseVariableUsedMoreThanTwiceInComposedElseStatements(): + nb1 = 0 + if nb1 == 1: + nb1 = 2 + else: + if nb1 == 2: # Noncompliant {{Use a switch statement instead of multiple if-else if possible}} + nb1 = 1 + else: # Noncompliant {{Use a switch statement instead of multiple if-else if possible}} + nb1 = 3 + return nb1 + +# NON COMPLIANT +# USE CASE : non compliant use case to check if following is NOT OK : +# - two uses of the same variable : use thre times with 2 IFs and 1 ELSE +# - usage of the same variable on different levels of IF statements +def shouldBeNotCompliantBecauseVariableUsedMoreThanTwiceInComposedElseStatementsScenario2(): + nb1 = 0 + if nb1 == 1: + if nb1 == 3: + nb1 = 4 + else: # Noncompliant {{Use a switch statement instead of multiple if-else if possible}} + nb1 = 5 + else: + if nb1 == 2: # Noncompliant {{Use a switch statement instead of multiple if-else if possible}} + nb1 = 1 + else: # Noncompliant {{Use a switch statement instead of multiple if-else if possible}} + nb1 = 3 + return nb1 + +# NON COMPLIANT +# USE CASE : non compliant use case to check if following is NOT OK : +# - two uses of the same variable : use thre times with 2 IFs and 1 ELSE +# - usage of the same variable on different levels of IF statements +def shouldBeNotCompliantBecauseVariableUsedMoreThanTwiceInComposedElseStatementsScenario3(): + nb1 = 0 + nb2 = 0 + if nb1 == 1: + if nb1 == 3: + nb1 = 4 + else: # Noncompliant {{Use a switch statement instead of multiple if-else if possible}} + nb1 = 5 + elif nb2 == 2: + if nb1 == 4: + nb1 = 5 + else: # Noncompliant {{Use a switch statement instead of multiple if-else if possible}} + nb1 = 6 + return nb1 + +# NON COMPLIANT +# USE CASE : non compliant use case to check if following is NOT OK : +# - two uses of the same variable : use thre times with 2 IFs and 1 ELSE +# - usage of the same variable on different levels of IF statements +def shouldBeNotCompliantBecauseVariableUsedMoreThanTwiceInComposedElseStatementsScenario4(): + nb1 = 0 + nb2 = 0 + if nb1 == 1: + if nb2 == 3: + nb1 = 4 + else: + nb1 = 5 + elif nb2 == 2: + if nb1 == 3: + nb1 = 4 + else: # Noncompliant {{Use a switch statement instead of multiple if-else if possible}} + nb1 = 5 + return nb1 + +# NON COMPLIANT +# USE CASE : NON compliant use case to check if following is NOT OK : +# - the same variable must used maximum twice +# - usage of the same variable on different levels of IF / ELSE statements +def shouldBeNotCompliantBecauseVariableUsedMaximumTwiceInComposedElseStatements(): + nb1 = 0 + if nb1 == 1: + nb1 = 2 + else: + if nb1 == 2: # Noncompliant {{Use a switch statement instead of multiple if-else if possible}} + nb1 = 1 + else: # Noncompliant {{Use a switch statement instead of multiple if-else if possible}} + if nb1 == 3: # Noncompliant {{Use a switch statement instead of multiple if-else if possible}} + nb1 = 4 + else: # Noncompliant {{Use a switch statement instead of multiple if-else if possible}} + nb1 = 5 + return nb1 + +# NON COMPLIANT +# USE CASE : NON compliant use case to check if following is NOT OK : +# - more than twice uses of the same variable +# - usage of the same variable on different kind of test statements (IF and elif) +def shouldBeNotCompliantBecauseTheSameVariableIsUsedMoreThanTwice(): + nb1 = 0 + nb2 = 10 + if nb1 == 1: + nb2 = 1 + elif nb1 == nb2: + nb2 = 2 + else: # Noncompliant {{Use a switch statement instead of multiple if-else if possible}} + nb2 = 4 + return nb2 + +# NON COMPLIANT +# USE CASE : NON compliant use case to check if following is NOT OK : +# - more than twice uses of the same variable +# - usage of the same variable on different kind of test statements (IF and elif) +def shouldBeNotCompliantBecauseTheSameVariableIsUsedManyTimes(): + nb1 = 0 + nb2 = 10 + nb3 = 11 + if nb1 == 1: + nb2 = 1 + elif nb1 == nb2: + nb2 = 2 + elif nb3 == nb1: # Noncompliant {{Use a switch statement instead of multiple if-else if possible}} + nb2 = 3 + else: # Noncompliant {{Use a switch statement instead of multiple if-else if possible}} + nb2 = 4 + return nb2 From 8dd9fda63127c0ba882a0c6bf8d28cfc4a04691a Mon Sep 17 00:00:00 2001 From: David DE CARVALHO Date: Sat, 5 Aug 2023 23:37:43 +0200 Subject: [PATCH 2/9] [ISSUE 142] optim enum values --- .../python/checks/AvoidMultipleIfElseStatementCheck.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/fr/greencodeinitiative/python/checks/AvoidMultipleIfElseStatementCheck.java b/src/main/java/fr/greencodeinitiative/python/checks/AvoidMultipleIfElseStatementCheck.java index fa300c8..e33f495 100644 --- a/src/main/java/fr/greencodeinitiative/python/checks/AvoidMultipleIfElseStatementCheck.java +++ b/src/main/java/fr/greencodeinitiative/python/checks/AvoidMultipleIfElseStatementCheck.java @@ -33,7 +33,7 @@ import org.sonar.plugins.python.api.tree.IfStatement; import org.sonar.plugins.python.api.tree.Statement; import org.sonar.plugins.python.api.tree.Tree; -import static org.sonar.plugins.python.api.tree.Tree.Kind.FUNCDEF; +import static org.sonar.plugins.python.api.tree.Tree.Kind.*; /** * FUNCTIONAL DESCRIPTION : please see ASCIIDOC description file of this rule (inside `ecocode-rules-spcifications`) @@ -95,7 +95,7 @@ private void visitNodeContent(SubscriptionContext context, List pLstS // // the cirrent node is a block : visit block content // visitNodeContent(((BlockTree)statement).statements(), pLevel); // } else - if (statement.is(Tree.Kind.IF_STMT)) { + if (statement.is(IF_STMT)) { visitIfNode(context, (IfStatement)statement, pLevel); } } @@ -158,14 +158,14 @@ private void computeIfVariables(SubscriptionContext context, IfStatement pIfTree private void computeConditionVariables(SubscriptionContext context, BinaryExpression pBinExprTree, int pLevel) { // if multiple conditions, continue with each part of complex expression - if (pBinExprTree.is(Tree.Kind.AND) || pBinExprTree.is(Tree.Kind.OR)) { + if (pBinExprTree.is(AND) || pBinExprTree.is(OR)) { if (pBinExprTree.leftOperand() instanceof BinaryExpression) { computeConditionVariables(context, (BinaryExpression) pBinExprTree.leftOperand(), pLevel); } if (pBinExprTree.rightOperand() instanceof BinaryExpression) { computeConditionVariables(context, (BinaryExpression) pBinExprTree.rightOperand(), pLevel); } - } else if (pBinExprTree.is(Tree.Kind.COMPARISON)) { + } else if (pBinExprTree.is(COMPARISON)) { // else if (pBinExprTree.is(Tree.Kind.EQUAL_TO) // || pBinExprTree.is(Tree.Kind.NOT_EQUAL_TO) // || pBinExprTree.is(Tree.Kind.GREATER_THAN) From cef81972a4fed028cb5e088260ddc730bca4c1dc Mon Sep 17 00:00:00 2001 From: David DE CARVALHO Date: Sat, 5 Aug 2023 23:58:03 +0200 Subject: [PATCH 3/9] [ISSUE 142] update error message --- .../python/checks/AvoidMultipleIfElseStatementCheck.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/fr/greencodeinitiative/python/checks/AvoidMultipleIfElseStatementCheck.java b/src/main/java/fr/greencodeinitiative/python/checks/AvoidMultipleIfElseStatementCheck.java index e33f495..a5af0bd 100644 --- a/src/main/java/fr/greencodeinitiative/python/checks/AvoidMultipleIfElseStatementCheck.java +++ b/src/main/java/fr/greencodeinitiative/python/checks/AvoidMultipleIfElseStatementCheck.java @@ -47,7 +47,7 @@ @Rule(key = "EC2") public class AvoidMultipleIfElseStatementCheck extends PythonSubscriptionCheck { - public static final String ERROR_MESSAGE = "Use a switch statement instead of multiple if-else if possible"; + public static final String ERROR_MESSAGE = "Use a match-case statement instead of multiple if-else if possible"; // data structure for following usage of variable inside all the AST tree private VariablesPerLevelDataStructure variablesStruct = new VariablesPerLevelDataStructure(); From 0bd552d880db52d4350a2f242478551bb5789c5e Mon Sep 17 00:00:00 2001 From: David DE CARVALHO Date: Sun, 6 Aug 2023 00:00:39 +0200 Subject: [PATCH 4/9] [ISSUE 142] correction tests + change version dep in pom.xml --- pom.xml | 3 +- ...voidMultipleIfElseStatementNonCompliant.py | 40 +++++++++---------- 2 files changed, 22 insertions(+), 21 deletions(-) diff --git a/pom.xml b/pom.xml index fbff633..4f0a67b 100644 --- a/pom.xml +++ b/pom.xml @@ -52,7 +52,8 @@ 5.3.1 - 0.0.2 + + 1.3.2-SNAPSHOT 2.5.0.1358 diff --git a/src/test/resources/checks/avoidMultipleIfElseStatementNonCompliant.py b/src/test/resources/checks/avoidMultipleIfElseStatementNonCompliant.py index 61aa001..a22689e 100644 --- a/src/test/resources/checks/avoidMultipleIfElseStatementNonCompliant.py +++ b/src/test/resources/checks/avoidMultipleIfElseStatementNonCompliant.py @@ -17,7 +17,7 @@ def shouldBeCompliantBecauseVariableUsedMaximumTwiceInComposedElseStatements(): if nb1 == 1: nb1 = 2 else: - if nb1 == 2: # Noncompliant {{Use a switch statement instead of multiple if-else if possible}} + if nb1 == 2: # Noncompliant {{Use a match-case statement instead of multiple if-else if possible}} nb1 = 1 return nb1 @@ -28,9 +28,9 @@ def shouldBeNotCompliantBecauseVariablesUsedMaximumTwiceAndDifferentsVariablesUs nb1 = 0 nb2 = 0 nb3 = 0 - if nb3 == 1 and nb3 == 2 and nb3 == 3: # Noncompliant {{Use a switch statement instead of multiple if-else if possible}} + if nb3 == 1 and nb3 == 2 and nb3 == 3: # Noncompliant {{Use a match-case statement instead of multiple if-else if possible}} nb1 = 1 - else: # Noncompliant {{Use a switch statement instead of multiple if-else if possible}} + else: # Noncompliant {{Use a match-case statement instead of multiple if-else if possible}} nb2 = 2 if nb2 == 2: nb1 = 3 @@ -47,7 +47,7 @@ def shouldBeNotCompliantBecauseVariablesIsUsedMoreThanTwice(): nb1 = 2 else: nb1 = 3 - if nb1 == 2: # Noncompliant {{Use a switch statement instead of multiple if-else if possible}} + if nb1 == 2: # Noncompliant {{Use a match-case statement instead of multiple if-else if possible}} nb1 = 4 return nb1 @@ -59,7 +59,7 @@ def shouldBeNotCompliantBecauseVariableUsedMoreThanTwiceInIfStatementsAtDifferen if nb1 == 1: if nb1 == 2: nb1 = 1 - else: # Noncompliant {{Use a switch statement instead of multiple if-else if possible}} + else: # Noncompliant {{Use a match-case statement instead of multiple if-else if possible}} nb1 = 3 else: nb1 = 2 @@ -74,9 +74,9 @@ def shouldBeNotCompliantBecauseVariableUsedMoreThanTwiceInComposedElseStatements if nb1 == 1: nb1 = 2 else: - if nb1 == 2: # Noncompliant {{Use a switch statement instead of multiple if-else if possible}} + if nb1 == 2: # Noncompliant {{Use a match-case statement instead of multiple if-else if possible}} nb1 = 1 - else: # Noncompliant {{Use a switch statement instead of multiple if-else if possible}} + else: # Noncompliant {{Use a match-case statement instead of multiple if-else if possible}} nb1 = 3 return nb1 @@ -89,12 +89,12 @@ def shouldBeNotCompliantBecauseVariableUsedMoreThanTwiceInComposedElseStatements if nb1 == 1: if nb1 == 3: nb1 = 4 - else: # Noncompliant {{Use a switch statement instead of multiple if-else if possible}} + else: # Noncompliant {{Use a match-case statement instead of multiple if-else if possible}} nb1 = 5 else: - if nb1 == 2: # Noncompliant {{Use a switch statement instead of multiple if-else if possible}} + if nb1 == 2: # Noncompliant {{Use a match-case statement instead of multiple if-else if possible}} nb1 = 1 - else: # Noncompliant {{Use a switch statement instead of multiple if-else if possible}} + else: # Noncompliant {{Use a match-case statement instead of multiple if-else if possible}} nb1 = 3 return nb1 @@ -108,12 +108,12 @@ def shouldBeNotCompliantBecauseVariableUsedMoreThanTwiceInComposedElseStatements if nb1 == 1: if nb1 == 3: nb1 = 4 - else: # Noncompliant {{Use a switch statement instead of multiple if-else if possible}} + else: # Noncompliant {{Use a match-case statement instead of multiple if-else if possible}} nb1 = 5 elif nb2 == 2: if nb1 == 4: nb1 = 5 - else: # Noncompliant {{Use a switch statement instead of multiple if-else if possible}} + else: # Noncompliant {{Use a match-case statement instead of multiple if-else if possible}} nb1 = 6 return nb1 @@ -132,7 +132,7 @@ def shouldBeNotCompliantBecauseVariableUsedMoreThanTwiceInComposedElseStatements elif nb2 == 2: if nb1 == 3: nb1 = 4 - else: # Noncompliant {{Use a switch statement instead of multiple if-else if possible}} + else: # Noncompliant {{Use a match-case statement instead of multiple if-else if possible}} nb1 = 5 return nb1 @@ -145,12 +145,12 @@ def shouldBeNotCompliantBecauseVariableUsedMaximumTwiceInComposedElseStatements( if nb1 == 1: nb1 = 2 else: - if nb1 == 2: # Noncompliant {{Use a switch statement instead of multiple if-else if possible}} + if nb1 == 2: # Noncompliant {{Use a match-case statement instead of multiple if-else if possible}} nb1 = 1 - else: # Noncompliant {{Use a switch statement instead of multiple if-else if possible}} - if nb1 == 3: # Noncompliant {{Use a switch statement instead of multiple if-else if possible}} + else: # Noncompliant {{Use a match-case statement instead of multiple if-else if possible}} + if nb1 == 3: # Noncompliant {{Use a match-case statement instead of multiple if-else if possible}} nb1 = 4 - else: # Noncompliant {{Use a switch statement instead of multiple if-else if possible}} + else: # Noncompliant {{Use a match-case statement instead of multiple if-else if possible}} nb1 = 5 return nb1 @@ -165,7 +165,7 @@ def shouldBeNotCompliantBecauseTheSameVariableIsUsedMoreThanTwice(): nb2 = 1 elif nb1 == nb2: nb2 = 2 - else: # Noncompliant {{Use a switch statement instead of multiple if-else if possible}} + else: # Noncompliant {{Use a match-case statement instead of multiple if-else if possible}} nb2 = 4 return nb2 @@ -181,8 +181,8 @@ def shouldBeNotCompliantBecauseTheSameVariableIsUsedManyTimes(): nb2 = 1 elif nb1 == nb2: nb2 = 2 - elif nb3 == nb1: # Noncompliant {{Use a switch statement instead of multiple if-else if possible}} + elif nb3 == nb1: # Noncompliant {{Use a match-case statement instead of multiple if-else if possible}} nb2 = 3 - else: # Noncompliant {{Use a switch statement instead of multiple if-else if possible}} + else: # Noncompliant {{Use a match-case statement instead of multiple if-else if possible}} nb2 = 4 return nb2 From e249f92f7b8d1d69f44bead29cb690aad9e59338 Mon Sep 17 00:00:00 2001 From: David DE CARVALHO Date: Sun, 6 Aug 2023 16:23:19 +0200 Subject: [PATCH 5/9] upgrade pom.xml --- pom.xml | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index 4f0a67b..3988951 100644 --- a/pom.xml +++ b/pom.xml @@ -41,14 +41,11 @@ https://sonarcloud.io 9.4.0.54424 - 7.19.0.31550 4.3.0.11660 1.21.0.505 true - 5.9.1 - 3.23.1 5.3.1 @@ -82,6 +79,14 @@ provided + + + org.sonarsource.analyzer-commons + sonar-analyzer-commons + ${sonar-analyzer-commons.version} + + + org.sonarsource.python python-checks-testkit @@ -174,6 +179,7 @@ ${java.version} + org.apache.maven.plugins maven-shade-plugin From 32b62c9f214dcfc2070aef42a6f1372aa85b0cf0 Mon Sep 17 00:00:00 2001 From: David DE CARVALHO Date: Sun, 6 Aug 2023 23:37:21 +0200 Subject: [PATCH 6/9] [ISSUE 142] correction pb deploy --- pom.xml | 5 ++++- .../python/checks/AvoidMultipleIfElseStatementCheck.java | 6 ++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index 3988951..a204efa 100644 --- a/pom.xml +++ b/pom.xml @@ -40,8 +40,11 @@ green-code-initiative https://sonarcloud.io + 9.4.0.54424 - 4.3.0.11660 + + + 4.1.0.11333 1.21.0.505 true diff --git a/src/main/java/fr/greencodeinitiative/python/checks/AvoidMultipleIfElseStatementCheck.java b/src/main/java/fr/greencodeinitiative/python/checks/AvoidMultipleIfElseStatementCheck.java index a5af0bd..2e4b9b6 100644 --- a/src/main/java/fr/greencodeinitiative/python/checks/AvoidMultipleIfElseStatementCheck.java +++ b/src/main/java/fr/greencodeinitiative/python/checks/AvoidMultipleIfElseStatementCheck.java @@ -175,12 +175,14 @@ private void computeConditionVariables(SubscriptionContext context, BinaryExpres // ) { // continue analyze with variables if some key-words are found + if (pBinExprTree.leftOperand().is(NAME)) { // if (pBinExprTree.leftOperand().is(Tree.Kind.VARIABLE_IDENTIFIER)) { - if ("IDENTIFIER".equals(pBinExprTree.leftOperand().firstToken().type().getName())) { +// if ("IDENTIFIER".equals(pBinExprTree.leftOperand().firstToken().type().getName())) { // computeVariables(context, (VariableIdentifierTree) pBinExprTree.leftOperand(), pLevel); computeVariables(context, pBinExprTree.leftOperand(), pLevel); } - if ("IDENTIFIER".equals(pBinExprTree.rightOperand().firstToken().type().getName())) { + if (pBinExprTree.rightOperand().is(NAME)) { +// if ("IDENTIFIER".equals(pBinExprTree.rightOperand().firstToken().type().getName())) { // if (pBinExprTree.rightOperand().is(Tree.Kind.VARIABLE_IDENTIFIER)) { // computeVariables(context, (VariableIdentifierTree) pBinExprTree.rightOperand(), pLevel); computeVariables(context, pBinExprTree.rightOperand(), pLevel); From 5fcbb3a4877abbd4f6c72015075b0b0e9ef6a17e Mon Sep 17 00:00:00 2001 From: David DE CARVALHO Date: Mon, 7 Aug 2023 10:34:28 +0200 Subject: [PATCH 7/9] [ISSUE 142] cleaning + tests minor corrections --- .../AvoidMultipleIfElseStatementCheck.java | 44 ++++--------------- .../avoidMultipleIfElseStatementCompliant.py | 14 +++--- ...voidMultipleIfElseStatementNonCompliant.py | 22 +++++----- 3 files changed, 27 insertions(+), 53 deletions(-) diff --git a/src/main/java/fr/greencodeinitiative/python/checks/AvoidMultipleIfElseStatementCheck.java b/src/main/java/fr/greencodeinitiative/python/checks/AvoidMultipleIfElseStatementCheck.java index 2e4b9b6..1b70bab 100644 --- a/src/main/java/fr/greencodeinitiative/python/checks/AvoidMultipleIfElseStatementCheck.java +++ b/src/main/java/fr/greencodeinitiative/python/checks/AvoidMultipleIfElseStatementCheck.java @@ -67,10 +67,6 @@ public void visitNode(SubscriptionContext context, Tree pTree) { FunctionDef method = (FunctionDef)pTree; -// if (!method.body().is(Tree.Kind.)) { -// return; -// } - // reinit data structure before each method analysis variablesStruct = new VariablesPerLevelDataStructure(); @@ -91,10 +87,6 @@ private void visitNodeContent(SubscriptionContext context, List pLstS } for (Statement statement : pLstStatements) { -// if (statement.is(Kind.BLOCK)) { -// // the cirrent node is a block : visit block content -// visitNodeContent(((BlockTree)statement).statements(), pLevel); -// } else if (statement.is(IF_STMT)) { visitIfNode(context, (IfStatement)statement, pLevel); } @@ -166,25 +158,12 @@ private void computeConditionVariables(SubscriptionContext context, BinaryExpres computeConditionVariables(context, (BinaryExpression) pBinExprTree.rightOperand(), pLevel); } } else if (pBinExprTree.is(COMPARISON)) { -// else if (pBinExprTree.is(Tree.Kind.EQUAL_TO) -// || pBinExprTree.is(Tree.Kind.NOT_EQUAL_TO) -// || pBinExprTree.is(Tree.Kind.GREATER_THAN) -// || pBinExprTree.is(Tree.Kind.GREATER_THAN_OR_EQUAL_TO) -// || pBinExprTree.is(Tree.Kind.LESS_THAN_OR_EQUAL_TO) -// || pBinExprTree.is(Tree.Kind.LESS_THAN) -// ) { - - // continue analyze with variables if some key-words are found + + // continue to analyze with variables if some key-words are found if (pBinExprTree.leftOperand().is(NAME)) { -// if (pBinExprTree.leftOperand().is(Tree.Kind.VARIABLE_IDENTIFIER)) { -// if ("IDENTIFIER".equals(pBinExprTree.leftOperand().firstToken().type().getName())) { -// computeVariables(context, (VariableIdentifierTree) pBinExprTree.leftOperand(), pLevel); computeVariables(context, pBinExprTree.leftOperand(), pLevel); } if (pBinExprTree.rightOperand().is(NAME)) { -// if ("IDENTIFIER".equals(pBinExprTree.rightOperand().firstToken().type().getName())) { -// if (pBinExprTree.rightOperand().is(Tree.Kind.VARIABLE_IDENTIFIER)) { -// computeVariables(context, (VariableIdentifierTree) pBinExprTree.rightOperand(), pLevel); computeVariables(context, pBinExprTree.rightOperand(), pLevel); } } @@ -195,22 +174,17 @@ private void computeConditionVariables(SubscriptionContext context, BinaryExpres * @param pVarIdTree The Variable AST structure * @param pLevel the level of structure */ -// private void computeVariables(SubscriptionContext context, VariableIdentifierTree pVarIdTree, int pLevel) { private void computeVariables(SubscriptionContext context, Expression pVarIdTree, int pLevel) { -// if (pVarIdTree.variableExpression().is(Kind.VARIABLE_IDENTIFIER)) { - // increment the variable counter to list of all variables -// int nbUsed = variablesStruct.incrementVariableUsageForLevel(pVarIdTree.text(), pLevel); + // increment the variable counter to list of all variables int nbUsed = variablesStruct.incrementVariableUsageForLevel(pVarIdTree.firstToken().value(), pLevel); - // increment variable counter to list of variables already declared for current if or elseif struture -// variablesStruct.incrementVariableUsageForLevelForCurrentIfStruct(pVarIdTree.text(), pLevel); + // increment variable counter to list of variables already declared for current if or elseif struture variablesStruct.incrementVariableUsageForLevelForCurrentIfStruct(pVarIdTree.firstToken().value(), pLevel); - // raise an error if maximum - if (nbUsed > 2) { - context.addIssue(pVarIdTree.firstToken(), ERROR_MESSAGE); - } -// } + // raise an error if maximum + if (nbUsed > 2) { + context.addIssue(pVarIdTree.firstToken(), ERROR_MESSAGE); + } } /** @@ -306,7 +280,7 @@ private static class VariablesPerLevelDataStructure { // map variable counters per level for current If / ElseIf structure // purpose : used by compute variables Else process (because Else structure is particular : - // we don't know previous variables and we need previous If / ElseIf structure to know variables) + // we don't know previous variables, and we need previous If / ElseIf structure to know variables) private final Map> mapVariablesPerLevelForCurrentIfStruct; public VariablesPerLevelDataStructure() { diff --git a/src/test/resources/checks/avoidMultipleIfElseStatementCompliant.py b/src/test/resources/checks/avoidMultipleIfElseStatementCompliant.py index 7733cbc..9a2c613 100644 --- a/src/test/resources/checks/avoidMultipleIfElseStatementCompliant.py +++ b/src/test/resources/checks/avoidMultipleIfElseStatementCompliant.py @@ -11,7 +11,7 @@ # COMPLIANT # USE CASE : compliant use case to check if a variable is used maximum twice on several IF / ELSE statements # at the same level AND no problem with several IF staments at the same level using different variables -def shouldBeCompliantBecauseVariablesUsedMaximumTwiceAndDifferentsVariablesUsed(): +def compliant_variables_used_max_twice_and_differents_variables(): nb1 = 0 nb2 = 0 nb3 = 0 @@ -28,7 +28,7 @@ def shouldBeCompliantBecauseVariablesUsedMaximumTwiceAndDifferentsVariablesUsed( # COMPLIANT # USE CASE : compliant use case to check if a variable is used maximum twice on several IF / ELSE statements # at the same level AND no problem with several IF staments at the same level using different variables -def shouldBeCompliantBecauseVariablesUsedMaximumTwiceAndDifferentsVariablesUsedAtDiffLevels(): +def compliant_variables_used_max_twice_and_differents_variables_at_diff_levels(): nb1 = 0 nb2 = 0 nb3 = 0 @@ -51,7 +51,7 @@ def shouldBeCompliantBecauseVariablesUsedMaximumTwiceAndDifferentsVariablesUsedA # COMPLIANT # USE CASE : compliant use case to check if a variable is used maximum twice on several IF / ELSE statements # at the same level AND no problem with several IF staments at the same level using different variables -def shouldBeCompliantBecauseVariablesUsedMaximumTwiceAndDiffVariablesUsedAtDiffLevelsScenario2(): +def compliant_variables_used_max_twice_and_diff_variables_used_at_diff_levels_scenario_2(): nb1 = 0 nb2 = 0 nb3 = 0 @@ -76,7 +76,7 @@ def shouldBeCompliantBecauseVariablesUsedMaximumTwiceAndDiffVariablesUsedAtDiffL # COMPLIANT # USE CASE : compliant use case to check if one variable is used maximum twice in different IF statements -def shouldBeCompliantBecauseVariableUsedMaximumTwiceInIfStatements(): +def compliant_variables_used_maximum_twice_in_if_statements(): nb1 = 0 if nb1 == 1: nb1 = 1 @@ -88,7 +88,7 @@ def shouldBeCompliantBecauseVariableUsedMaximumTwiceInIfStatements(): # USE CASE : compliant use case to check if following is OK : # - two uses of the same variable # - usage of the same variable on different levels of IF statements -def shouldBeCompliantBecauseSereralVariablesUsedMaximumTwiceInComposedElseStatements(): +def compliant_variables_used_max_twice_in_composed_else_statements(): nb1 = 0 nb2 = 0 nb3 = 0 @@ -108,7 +108,7 @@ def shouldBeCompliantBecauseSereralVariablesUsedMaximumTwiceInComposedElseStatem # USE CASE : compliant use case to check if following is OK : # - two uses of the same variable # - usage of the same variable on different kind of test statements (IF and elif) -def shouldBeCompliantBecauseVariableUsedMaximumTwiceInIfOrelifStatements(): +def compliant_variables_used_max_twice_in_if_orelif_statements(): nb1 = 0 nb2 = 10 if nb1 == 1: @@ -121,7 +121,7 @@ def shouldBeCompliantBecauseVariableUsedMaximumTwiceInIfOrelifStatements(): # USE CASE : compliant use case to check if following is OK : # - two uses of the same variable # - usage of the same variable on different kind of test statements (IF and elif) -def shouldBeCompliantBecauseSeveralVariablesUsedMaximumTwiceInIfOrelifStatements(): +def compliant_variables_used_max_twice_in_if_orelif_statements_scenario_2(): nb1 = 0 nb2 = 10 nb3 = 3 diff --git a/src/test/resources/checks/avoidMultipleIfElseStatementNonCompliant.py b/src/test/resources/checks/avoidMultipleIfElseStatementNonCompliant.py index a22689e..caca962 100644 --- a/src/test/resources/checks/avoidMultipleIfElseStatementNonCompliant.py +++ b/src/test/resources/checks/avoidMultipleIfElseStatementNonCompliant.py @@ -12,7 +12,7 @@ # USE CASE : Non compliant use case to check if following is NON OK : # - two uses of the same variable # - usage of the same variable on different levels of IF statements -def shouldBeCompliantBecauseVariableUsedMaximumTwiceInComposedElseStatements(): +def not_compliant_variables_used_max_twice_in_composed_else_statements(): nb1 = 0 if nb1 == 1: nb1 = 2 @@ -24,7 +24,7 @@ def shouldBeCompliantBecauseVariableUsedMaximumTwiceInComposedElseStatements(): # NON COMPLIANT # USE CASE : non compliant use case to check if a variable is not used max twice on several IF / ELSE statements # at the same level -def shouldBeNotCompliantBecauseVariablesUsedMaximumTwiceAndDifferentsVariablesUsed(): +def not_compliant_variables_used_max_twice_and_differents_variables_used(): nb1 = 0 nb2 = 0 nb3 = 0 @@ -41,7 +41,7 @@ def shouldBeNotCompliantBecauseVariablesUsedMaximumTwiceAndDifferentsVariablesUs # NON COMPLIANT # USE CASE : NON compliant use case to check if following is NOT COMPLIANT : # one variable is used maximum in two IF / ELSE / elif statements -def shouldBeNotCompliantBecauseVariablesIsUsedMoreThanTwice(): +def not_compliant_variable_used_more_than_twice(): nb1 = 0 if nb1 == 1: nb1 = 2 @@ -54,7 +54,7 @@ def shouldBeNotCompliantBecauseVariablesIsUsedMoreThanTwice(): # NON COMPLIANT # USE CASE : NON compliant use case to check if following is NOT OK : # - same variable used maximum twice : no compliant because 2 IFs and 1 ELSE -def shouldBeNotCompliantBecauseVariableUsedMoreThanTwiceInIfStatementsAtDifferentsLevels(): +def not_compliant_variable_used_more_than_twice_in_if_statements_at_differents_levels(): nb1 = 0 if nb1 == 1: if nb1 == 2: @@ -69,7 +69,7 @@ def shouldBeNotCompliantBecauseVariableUsedMoreThanTwiceInIfStatementsAtDifferen # USE CASE : non compliant use case to check if following is NOT OK : # - two uses of the same variable : use thre times with 2 IFs and 1 ELSE # - usage of the same variable on different levels of IF statements -def shouldBeNotCompliantBecauseVariableUsedMoreThanTwiceInComposedElseStatements(): +def not_compliant_variable_used_more_than_twice_in_composed_else_statements(): nb1 = 0 if nb1 == 1: nb1 = 2 @@ -84,7 +84,7 @@ def shouldBeNotCompliantBecauseVariableUsedMoreThanTwiceInComposedElseStatements # USE CASE : non compliant use case to check if following is NOT OK : # - two uses of the same variable : use thre times with 2 IFs and 1 ELSE # - usage of the same variable on different levels of IF statements -def shouldBeNotCompliantBecauseVariableUsedMoreThanTwiceInComposedElseStatementsScenario2(): +def not_compliant_variable_used_more_than_twice_in_composed_else_statements_scenario_2(): nb1 = 0 if nb1 == 1: if nb1 == 3: @@ -102,7 +102,7 @@ def shouldBeNotCompliantBecauseVariableUsedMoreThanTwiceInComposedElseStatements # USE CASE : non compliant use case to check if following is NOT OK : # - two uses of the same variable : use thre times with 2 IFs and 1 ELSE # - usage of the same variable on different levels of IF statements -def shouldBeNotCompliantBecauseVariableUsedMoreThanTwiceInComposedElseStatementsScenario3(): +def not_compliant_variable_used_more_than_twice_in_composed_else_statements_scenario_3(): nb1 = 0 nb2 = 0 if nb1 == 1: @@ -121,7 +121,7 @@ def shouldBeNotCompliantBecauseVariableUsedMoreThanTwiceInComposedElseStatements # USE CASE : non compliant use case to check if following is NOT OK : # - two uses of the same variable : use thre times with 2 IFs and 1 ELSE # - usage of the same variable on different levels of IF statements -def shouldBeNotCompliantBecauseVariableUsedMoreThanTwiceInComposedElseStatementsScenario4(): +def not_compliant_variable_used_more_than_twice_in_composed_else_statements_scenario_4(): nb1 = 0 nb2 = 0 if nb1 == 1: @@ -140,7 +140,7 @@ def shouldBeNotCompliantBecauseVariableUsedMoreThanTwiceInComposedElseStatements # USE CASE : NON compliant use case to check if following is NOT OK : # - the same variable must used maximum twice # - usage of the same variable on different levels of IF / ELSE statements -def shouldBeNotCompliantBecauseVariableUsedMaximumTwiceInComposedElseStatements(): +def not_compliant_variable_used_max_twice_in_composed_else_statements(): nb1 = 0 if nb1 == 1: nb1 = 2 @@ -158,7 +158,7 @@ def shouldBeNotCompliantBecauseVariableUsedMaximumTwiceInComposedElseStatements( # USE CASE : NON compliant use case to check if following is NOT OK : # - more than twice uses of the same variable # - usage of the same variable on different kind of test statements (IF and elif) -def shouldBeNotCompliantBecauseTheSameVariableIsUsedMoreThanTwice(): +def not_compliant_the_same_variable_is_used_more_than_twice(): nb1 = 0 nb2 = 10 if nb1 == 1: @@ -173,7 +173,7 @@ def shouldBeNotCompliantBecauseTheSameVariableIsUsedMoreThanTwice(): # USE CASE : NON compliant use case to check if following is NOT OK : # - more than twice uses of the same variable # - usage of the same variable on different kind of test statements (IF and elif) -def shouldBeNotCompliantBecauseTheSameVariableIsUsedManyTimes(): +def not_compliant_the_same_variable_is_used_many_times(): nb1 = 0 nb2 = 10 nb3 = 11 From e8ed05be3ca99057dc7b2df3376f216627faae6b Mon Sep 17 00:00:00 2001 From: David DE CARVALHO Date: Tue, 8 Aug 2023 17:41:07 +0200 Subject: [PATCH 8/9] [ISSUE 142] upgrade ecocode-rules-specifications --- pom.xml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index a204efa..f0ee623 100644 --- a/pom.xml +++ b/pom.xml @@ -52,8 +52,7 @@ 5.3.1 - - 1.3.2-SNAPSHOT + 0.0.3 2.5.0.1358 From 0c0155ab22e8e58154349f6fe9764217f34270d0 Mon Sep 17 00:00:00 2001 From: David DE CARVALHO Date: Tue, 8 Aug 2023 17:48:00 +0200 Subject: [PATCH 9/9] corrections for release process --- .github/workflows/tag_release.yml | 6 +++--- CHANGELOG.md | 9 +++++++++ pom.xml | 6 ++++++ 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/.github/workflows/tag_release.yml b/.github/workflows/tag_release.yml index e63b337..0d245eb 100644 --- a/.github/workflows/tag_release.yml +++ b/.github/workflows/tag_release.yml @@ -44,7 +44,7 @@ jobs: uses: actions/upload-artifact@v3 with: name: ecocode-plugins - path: lib + path: target - name: Export UPLOAD_URL id: export_upload_url run: echo "upload_url=${{ steps.create_release.outputs.upload_url }}" >> $GITHUB_OUTPUT @@ -58,7 +58,7 @@ jobs: uses: actions/download-artifact@v3 with: name: ecocode-plugins - path: lib + path: target - name: Upload Release Asset - Python Plugin id: upload-release-asset uses: actions/upload-release-asset@v1 @@ -66,6 +66,6 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: upload_url: ${{needs.build.outputs.upload_url}} - asset_path: lib/ecocode-python-plugin-${{ github.ref_name }}.jar + asset_path: target/ecocode-python-plugin-${{ github.ref_name }}.jar asset_name: ecocode-python-plugin-${{ github.ref_name }}.jar asset_content_type: application/zip diff --git a/CHANGELOG.md b/CHANGELOG.md index e6ff4c0..19802dc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,4 +17,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Deleted +## [0.0.0] + +### Added + +### Changed + +### Deleted + [unreleased]: https://github.com/green-code-initiative/ecoCode-python/compare/v0.0.1...HEAD +[0.0.1]: https://github.com/green-code-initiative/ecoCode-python/compare/v0.0.0...0.0.1 diff --git a/pom.xml b/pom.xml index f0ee623..cfb51b8 100644 --- a/pom.xml +++ b/pom.xml @@ -22,6 +22,12 @@ repo + + scm:git:https://github.com/green-code-initiative/ecocode-python + scm:git:https://github.com/green-code-initiative/ecocode-python + https://github.com/green-code-initiative/ecocode-python + HEAD + GitHub https://github.com/green-code-initiative/ecoCode-python/issues