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

org.sonar.flex.checks.ConditionalStructure Maven / Gradle / Ivy

There is a newer version: 2.14.0.5032
Show newest version
/*
 * SonarQube Flex Plugin
 * Copyright (C) 2010-2023 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.flex.checks;

import com.sonar.sslr.api.AstNode;
import com.sonar.sslr.api.Token;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.function.BiConsumer;
import org.sonar.flex.FlexGrammar;
import org.sonar.flex.FlexKeyword;

import static java.util.Collections.singletonList;

class ConditionalStructure {
  static class BranchAndContent {
    AstNode branch;
    List content;
    boolean oneLiner;
    BranchAndContent(AstNode branch, List content, boolean oneLiner) {
      this.branch = branch;
      this.content = content;
      this.oneLiner = oneLiner;
    }
  }
  static BranchAndContent branchAndContentIf(AstNode branch, AstNode subStatement) {
    return new BranchAndContent(branch, singletonList(subStatement), isOnelinerSubStatement(subStatement));
  }
  static BranchAndContent branchAndContentSwitch(AstNode branch, List directives) {
    return new BranchAndContent(branch, directives, isOnelinerDirectives(directives));
  }

  final List branches;
  final boolean allBranchesArePresent;
  final AstNode node;

  ConditionalStructure(AstNode node, List branches, boolean allBranchesArePresent) {
    this.node = node;
    this.branches = branches;
    this.allBranchesArePresent = allBranchesArePresent;
  }

  boolean areAllEquivalentBranches() {
    if (branches.isEmpty()) {
      return false;
    }
    BranchAndContent first = branches.get(0);
    return branches.stream().skip(1).allMatch(next -> SyntacticEquivalence.areEquivalent(first.content, next.content));
  }

  @FunctionalInterface
  interface DuplicatedBranchCallback extends BiConsumer {
    void accept(AstNode branchFirstNode1, AstNode branchFirstNode2);
  }

  void forEachBranchDuplication(DuplicatedBranchCallback callback) {
    boolean allEquivalentBranches = areAllEquivalentBranches();
    if (allBranchesArePresent && allEquivalentBranches) {
      return;
    }

    for (BranchAndContent branch1 : branches) {
      if (!branch1.oneLiner || allEquivalentBranches) {
        for (BranchAndContent branch2 : branches) {
          if (branch1 == branch2) {
            break;
          }
          if (SyntacticEquivalence.areEquivalent(branch1.content, branch2.content)) {
            callback.accept(branch1.branch, branch2.branch);
          }
        }
      }
    }
  }

  static boolean isOnelinerNonBlock(AstNode nonBlock) {
    List tokens = nonBlock.getTokens();
    if (!tokens.isEmpty()) {
      return tokens.get(0).isOnSameLineThan(tokens.get(tokens.size() - 1));
    }
    return false;
  }

  static boolean isOnelinerSubStatement(AstNode node) {
    AstNode child;
    AstNode grandchild;
    if ((child = node.getFirstChild(FlexGrammar.STATEMENT)) != null && (grandchild = child.getFirstChild(FlexGrammar.BLOCK)) != null) {
      return isOnelinerDirectives(grandchild.getFirstChild(FlexGrammar.DIRECTIVES).getChildren(FlexGrammar.DIRECTIVE));
    }
    return isOnelinerNonBlock(node);
  }

  static boolean isOnelinerDirectives(List directives) {
    if (directives.isEmpty()) {
      return true;
    }
    Token firstToken = directives.get(0).getTokens().get(0);
    Token lastToken = directives.get(directives.size() - 1).getLastToken();
    return firstToken.isOnSameLineThan(lastToken);
  }

  static ConditionalStructure ifStatement(AstNode node, Set visitedIfStatements) {
    List branches = new ArrayList<>();
    boolean allBranchesArePresent = false;

    branches.add(branchAndContentIf(node, node.getFirstChild(FlexGrammar.SUB_STATEMENT)));
    AstNode currentIfStatement = node;

    while (currentIfStatement.hasDirectChildren(FlexKeyword.ELSE)) {
      AstNode elseStatement = currentIfStatement.getLastChild(FlexGrammar.SUB_STATEMENT).getFirstChild(FlexGrammar.STATEMENT);
      if (elseStatement != null && elseStatement.hasDirectChildren(FlexGrammar.IF_STATEMENT)) {
        currentIfStatement = elseStatement.getFirstChild(FlexGrammar.IF_STATEMENT);
        visitedIfStatements.add(currentIfStatement);
        branches.add(branchAndContentIf(currentIfStatement, currentIfStatement.getFirstChild(FlexGrammar.SUB_STATEMENT)));
      } else {
        AstNode theElse = currentIfStatement.getFirstChild(FlexKeyword.ELSE);
        if (theElse != null) {
          branches.add(branchAndContentIf(theElse, currentIfStatement.getLastChild(FlexGrammar.SUB_STATEMENT)));
        }
        allBranchesArePresent = true;
        break;
      }
    }

    return new ConditionalStructure(node, branches, allBranchesArePresent);
  }

  static ConditionalStructure switchStatement(AstNode node) {
    List branches = new ArrayList<>();
    boolean allBranchesArePresent = false;

    for (AstNode caseElement : node.getChildren(FlexGrammar.CASE_ELEMENT)) {
      List directives = caseElement.getChildren(FlexGrammar.DIRECTIVE);
      if (!directives.isEmpty() && isBreakStatement(directives.get(directives.size() - 1).getFirstChild())) {
        directives = directives.subList(0, directives.size() - 1);
      }
      branches.add(branchAndContentSwitch(caseElement, directives));
      for (AstNode caseLabelNode : caseElement.getChildren(FlexGrammar.CASE_LABEL)) {
        if (caseLabelNode.hasDirectChildren(FlexKeyword.DEFAULT)) {
          allBranchesArePresent = true;
        }
      }
    }

    return new ConditionalStructure(node, branches, allBranchesArePresent);
  }

  private static boolean isBreakStatement(AstNode node) {
    return node.is(FlexGrammar.STATEMENT) && node.hasDirectChildren(FlexGrammar.BREAK_STATEMENT);
  }

  AstNode getNode() {
    return node;
  }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy