All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.sonar.python.checks.AllBranchesAreIdenticalCheck Maven / Gradle / Ivy

/*
 * SonarQube Python Plugin
 * Copyright (C) 2011-2020 SonarSource SA
 * mailto:info 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 org.sonar.python.checks;

import java.util.ArrayList;
import java.util.List;
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.ConditionalExpression;
import org.sonar.plugins.python.api.tree.Expression;
import org.sonar.plugins.python.api.tree.IfStatement;
import org.sonar.plugins.python.api.tree.StatementList;
import org.sonar.plugins.python.api.tree.Token;
import org.sonar.plugins.python.api.tree.Tree;
import org.sonar.plugins.python.api.IssueLocation;
import org.sonar.python.tree.TreeUtils;

@Rule(key = "S3923")
public class AllBranchesAreIdenticalCheck extends PythonSubscriptionCheck {

  private static final List ignoreList = new ArrayList<>();

  @Override
  public void initialize(Context context) {
    context.registerSyntaxNodeConsumer(Tree.Kind.FILE_INPUT, ctx -> ignoreList.clear());
    context.registerSyntaxNodeConsumer(Tree.Kind.IF_STMT, ctx -> handleIfStatement((IfStatement) ctx.syntaxNode(), ctx));
    context.registerSyntaxNodeConsumer(Tree.Kind.CONDITIONAL_EXPR, ctx -> handleConditionalExpression((ConditionalExpression) ctx.syntaxNode(), ctx));
  }

  private static void handleIfStatement(IfStatement ifStmt, SubscriptionContext ctx) {
    if (ifStmt.elseBranch() == null) {
      return;
    }
    StatementList body = ifStmt.body();
    for (IfStatement elifBranch : ifStmt.elifBranches()) {
      StatementList elifBody = elifBranch.body();
      if (!CheckUtils.areEquivalent(body, elifBody)) {
        return;
      }
    }
    if (!CheckUtils.areEquivalent(body, ifStmt.elseBranch().body())) {
      return;
    }
    PreciseIssue issue = ctx.addIssue(ifStmt.keyword(), "Remove this if statement or edit its code blocks so that they're not all the same.");
    issue.secondary(issueLocation(ifStmt.body()));
    ifStmt.elifBranches().forEach(e -> issue.secondary(issueLocation(e.body())));
    issue.secondary(issueLocation(ifStmt.elseBranch().body()));
  }

  private static IssueLocation issueLocation(StatementList body) {
    List tokens = TreeUtils.nonWhitespaceTokens(body);
    return IssueLocation.preciseLocation(tokens.get(0), tokens.get(tokens.size() - 1), null);
  }

  private static void handleConditionalExpression(ConditionalExpression conditionalExpression, SubscriptionContext ctx) {
    if (ignoreList.contains(conditionalExpression)) {
      return;
    }
    if (areIdentical(conditionalExpression.trueExpression(), conditionalExpression.falseExpression())) {
      PreciseIssue issue = ctx.addIssue(conditionalExpression.ifKeyword(), "This conditional expression returns the same value whether the condition is \"true\" or \"false\".");
      addSecondaryLocations(issue, conditionalExpression.trueExpression());
      addSecondaryLocations(issue, conditionalExpression.falseExpression());
    }
  }

  private static void addSecondaryLocations(PreciseIssue issue, Expression expression) {
    Expression unwrappedExpression = Expressions.removeParentheses(expression);
    if (unwrappedExpression.is(Tree.Kind.CONDITIONAL_EXPR)) {
      ConditionalExpression conditionalExpression = (ConditionalExpression) unwrappedExpression;
      ignoreList.add(conditionalExpression);
      addSecondaryLocations(issue, conditionalExpression.trueExpression());
      addSecondaryLocations(issue, conditionalExpression.falseExpression());
    } else {
      issue.secondary(unwrappedExpression, null);
    }
  }

  private static boolean areIdentical(Expression trueExpression, Expression falseExpression) {
    Expression unwrappedTrueExpression = unwrapIdenticalExpressions(trueExpression);
    Expression unwrappedFalseExpression = unwrapIdenticalExpressions(falseExpression);
    return CheckUtils.areEquivalent(unwrappedTrueExpression, unwrappedFalseExpression);
  }

  private static Expression unwrapIdenticalExpressions(Expression expression) {
    Expression unwrappedExpression = Expressions.removeParentheses(expression);
    if (unwrappedExpression.is(Tree.Kind.CONDITIONAL_EXPR)) {
      boolean identicalExpressions = areIdentical(((ConditionalExpression) unwrappedExpression).trueExpression(), ((ConditionalExpression) unwrappedExpression).falseExpression());
      if (identicalExpressions) {
        while (unwrappedExpression.is(Tree.Kind.CONDITIONAL_EXPR)) {
          unwrappedExpression = Expressions.removeParentheses(((ConditionalExpression) unwrappedExpression).trueExpression());
        }
      }
    }
    return unwrappedExpression;
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy