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

au.com.integradev.delphi.checks.IfThenShortCircuitCheck Maven / Gradle / Ivy

The newest version!
/*
 * Sonar Delphi Plugin
 * Copyright (C) 2019 Integrated Application Development
 *
 * 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  02
 */
package au.com.integradev.delphi.checks;

import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
import java.util.stream.Collectors;
import org.apache.commons.lang3.StringUtils;
import org.sonar.check.Rule;
import org.sonar.plugins.communitydelphi.api.ast.ArgumentListNode;
import org.sonar.plugins.communitydelphi.api.ast.ArgumentNode;
import org.sonar.plugins.communitydelphi.api.ast.BinaryExpressionNode;
import org.sonar.plugins.communitydelphi.api.ast.DelphiNode;
import org.sonar.plugins.communitydelphi.api.ast.ExpressionNode;
import org.sonar.plugins.communitydelphi.api.ast.NameReferenceNode;
import org.sonar.plugins.communitydelphi.api.ast.Node;
import org.sonar.plugins.communitydelphi.api.ast.PrimaryExpressionNode;
import org.sonar.plugins.communitydelphi.api.ast.utils.ExpressionNodeUtils;
import org.sonar.plugins.communitydelphi.api.check.DelphiCheck;
import org.sonar.plugins.communitydelphi.api.check.DelphiCheckContext;
import org.sonar.plugins.communitydelphi.api.operator.BinaryOperator;
import org.sonar.plugins.communitydelphi.api.symbol.declaration.NameDeclaration;
import org.sonar.plugins.communitydelphi.api.symbol.declaration.RoutineNameDeclaration;
import org.sonarsource.analyzer.commons.annotations.DeprecatedRuleKey;

@DeprecatedRuleKey(ruleKey = "IfThenShortCircuitRule", repositoryKey = "delph")
@Rule(key = "IfThenShortCircuit")
public class IfThenShortCircuitCheck extends DelphiCheck {
  private static final String MESSAGE = "Change this unsafe IfThen call to an 'if' statement.";

  @Override
  public DelphiCheckContext visit(NameReferenceNode nameReference, DelphiCheckContext context) {
    DelphiNode parent = nameReference.getParent();
    if (parent instanceof PrimaryExpressionNode
        && nameReference.getLastName().getIdentifier().getImage().equalsIgnoreCase("IfThen")) {
      DelphiNode argumentList = parent.getChild(nameReference.getChildIndex() + 1);
      if (argumentList instanceof ArgumentListNode) {
        List arguments =
            ((ArgumentListNode) argumentList)
                .getArgumentNodes().stream()
                    .map(ArgumentNode::getExpression)
                    .collect(Collectors.toUnmodifiableList());
        if (isViolation(arguments)) {
          reportIssue(context, nameReference, MESSAGE);
        }
      }
    }
    return super.visit(nameReference, context);
  }

  private static boolean isViolation(List arguments) {
    if (arguments.size() == 3) {
      for (String image : findImagesCheckedForAssignment(arguments.get(0))) {
        if (isAccessedBy(image, arguments.get(1)) || isAccessedBy(image, arguments.get(2))) {
          return true;
        }
      }
    }
    return false;
  }

  private static Set findImagesCheckedForAssignment(ExpressionNode expression) {
    Set images = new TreeSet<>(String.CASE_INSENSITIVE_ORDER);
    images.addAll(findImagesInPrimaryExpressions(expression));
    images.addAll(findImagesInBinaryExpressions(expression));
    return images;
  }

  private static Set findImagesInPrimaryExpressions(ExpressionNode expression) {
    Set images = new HashSet<>();
    for (var binaryExpression : findDescendentsOrSelf(expression, BinaryExpressionNode.class)) {
      if (binaryExpression.getOperator() == BinaryOperator.EQUAL
          || binaryExpression.getOperator() == BinaryOperator.NOT_EQUAL) {
        ExpressionNode left = binaryExpression.getLeft();
        ExpressionNode right = binaryExpression.getRight();
        if (ExpressionNodeUtils.isNilLiteral(left)) {
          images.add(right.getImage());
        } else if (ExpressionNodeUtils.isNilLiteral(right)) {
          images.add(left.getImage());
        }
      }
    }
    return images;
  }

  private static Set findImagesInBinaryExpressions(ExpressionNode expression) {
    Set images = new HashSet<>();
    for (var primaryExpression : findDescendentsOrSelf(expression, PrimaryExpressionNode.class)) {
      Node nameReference = primaryExpression.getChild(0);
      Node argumentList = primaryExpression.getChild(1);
      if (nameReference instanceof NameReferenceNode && argumentList instanceof ArgumentListNode) {
        NameDeclaration declaration =
            ((NameReferenceNode) nameReference).getLastName().getNameDeclaration();
        if (declaration instanceof RoutineNameDeclaration
            && ((RoutineNameDeclaration) declaration)
                .fullyQualifiedName()
                .equals("System.Assigned")) {
          images.add(
              ((ArgumentListNode) argumentList)
                  .getArgumentNodes()
                  .get(0)
                  .getExpression()
                  .getImage());
        }
      }
    }
    return images;
  }

  private static boolean isAccessedBy(String image, ExpressionNode expression) {
    return findDescendentsOrSelf(expression, PrimaryExpressionNode.class).stream()
        .map(Node::getImage)
        .filter(primaryImage -> primaryImage.length() > image.length())
        .anyMatch(
            primaryImage -> {
              char nextChar = primaryImage.charAt(image.length());
              return (nextChar == '.' || nextChar == '^')
                  && StringUtils.startsWithIgnoreCase(primaryImage, image);
            });
  }

  private static  List findDescendentsOrSelf(DelphiNode node, Class clazz) {
    List result = node.findDescendantsOfType(clazz);
    if (clazz.isInstance(node)) {
      result.add(clazz.cast(node));
    }
    return result;
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy