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

org.sonar.java.checks.helpers.MethodTreeUtils Maven / Gradle / Ivy

There is a newer version: 8.10.0.38194
Show newest version
/*
 * SonarQube Java
 * Copyright (C) 2012-2024 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 Sonar Source-Available License Version 1, as published by SonarSource SA.
 *
 * 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 Sonar Source-Available License for more details.
 *
 * You should have received a copy of the Sonar Source-Available License
 * along with this program; if not, see https://sonarsource.com/license/ssal/
 */
package org.sonar.java.checks.helpers;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.function.Predicate;
import javax.annotation.Nullable;
import org.sonar.java.annotations.VisibleForTesting;
import org.sonar.java.model.ExpressionUtils;
import org.sonar.java.model.ModifiersUtils;
import org.sonar.plugins.java.api.semantic.MethodMatchers;
import org.sonar.plugins.java.api.semantic.Symbol;
import org.sonar.plugins.java.api.tree.ArrayTypeTree;
import org.sonar.plugins.java.api.tree.BaseTreeVisitor;
import org.sonar.plugins.java.api.tree.ClassTree;
import org.sonar.plugins.java.api.tree.LambdaExpressionTree;
import org.sonar.plugins.java.api.tree.MemberSelectExpressionTree;
import org.sonar.plugins.java.api.tree.MethodInvocationTree;
import org.sonar.plugins.java.api.tree.MethodTree;
import org.sonar.plugins.java.api.tree.Modifier;
import org.sonar.plugins.java.api.tree.NewClassTree;
import org.sonar.plugins.java.api.tree.PrimitiveTypeTree;
import org.sonar.plugins.java.api.tree.Tree;
import org.sonar.plugins.java.api.tree.TypeTree;
import org.sonar.plugins.java.api.tree.VariableTree;

public final class MethodTreeUtils {

  private MethodTreeUtils() {
  }

  public static boolean isMainMethod(MethodTree m) {
    return isPublic(m) && isStatic(m) && isNamed(m, "main") && returnsPrimitive(m, "void") && hasStringArrayParameter(m);
  }

  private static boolean hasStringArrayParameter(MethodTree m) {
    return m.parameters().size() == 1 && isParameterStringArray(m);
  }

  private static boolean isParameterStringArray(MethodTree m) {
    VariableTree variableTree = m.parameters().get(0);
    boolean result = false;
    if (variableTree.type().is(Tree.Kind.ARRAY_TYPE)) {
      ArrayTypeTree arrayTypeTree = (ArrayTypeTree) variableTree.type();
      result = arrayTypeTree.type().symbolType().isClass() && "String".equals(arrayTypeTree.type().symbolType().name());
    }
    return result;
  }

  public static boolean isEqualsMethod(MethodTree m) {
    boolean hasEqualsSignature = isNamed(m, "equals") && returnsPrimitive(m, "boolean") && hasObjectParameter(m);
    return isPublic(m) && !isStatic(m) && hasEqualsSignature;
  }

  private static boolean hasObjectParameter(MethodTree m) {
    return m.parameters().size() == 1 && m.parameters().get(0).type().symbolType().is("java.lang.Object");
  }

  public static boolean isHashCodeMethod(MethodTree m) {
    boolean hasHashCodeSignature = isNamed(m, "hashCode") && m.parameters().isEmpty() && returnsInt(m);
    return isPublic(m) && !isStatic(m) && hasHashCodeSignature;
  }

  public static boolean isSetterMethod(MethodTree m) {
    boolean publicNonStaticVoid = isPublic(m) && !isStatic(m) && returnsPrimitive(m, "void");
    return publicNonStaticVoid && isNamePrefix(m, "set") && m.parameters().size() == 1;
  }

  private static boolean isNamed(MethodTree m, String name) {
    return name.equals(m.simpleName().name());
  }

  private static boolean isNamePrefix(MethodTree m, String prefix) {
    return m.simpleName().name().startsWith(prefix);
  }

  private static boolean isStatic(MethodTree m) {
    return ModifiersUtils.hasModifier(m.modifiers(), Modifier.STATIC);
  }

  private static boolean isPublic(MethodTree m) {
    return ModifiersUtils.hasModifier(m.modifiers(), Modifier.PUBLIC);
  }

  private static boolean returnsInt(MethodTree m) {
    return returnsPrimitive(m, "int");
  }

  private static boolean returnsPrimitive(MethodTree m, String primitive) {
    TypeTree returnType = m.returnType();
    if (returnType == null) {
      return false;
    }
    return returnType.is(Tree.Kind.PRIMITIVE_TYPE)
      && primitive.equals(((PrimitiveTypeTree) returnType).keyword().text());
  }

  public static Optional consecutiveMethodInvocation(Tree tree) {
    Tree memberSelectExpression = tree;
    Tree memberSelectExpressionParent = memberSelectExpression.parent();
    while (hasKind(memberSelectExpressionParent, Tree.Kind.PARENTHESIZED_EXPRESSION)) {
      memberSelectExpression = memberSelectExpressionParent;
      memberSelectExpressionParent = memberSelectExpressionParent.parent();
    }
    if (hasKind(memberSelectExpressionParent, Tree.Kind.MEMBER_SELECT)) {
      if (((MemberSelectExpressionTree) memberSelectExpressionParent).identifier() == memberSelectExpression) {
        // In the case: A.B.M(), B is the identifier of another member select, we want to go one level above.
        memberSelectExpressionParent = memberSelectExpressionParent.parent();
      }

      Tree memberSelectParent = memberSelectExpressionParent.parent();
      if (hasKind(memberSelectParent, Tree.Kind.METHOD_INVOCATION)) {
        return Optional.of((MethodInvocationTree) memberSelectParent);
      }
    }
    return Optional.empty();
  }

  public static Optional subsequentMethodInvocation(Tree tree, MethodMatchers methodMatchers) {
    return consecutiveMethodInvocation(tree)
      .map(consecutiveMethod ->
        methodMatchers.matches(consecutiveMethod) ?
          consecutiveMethod : subsequentMethodInvocation(consecutiveMethod, methodMatchers).orElse(null));
  }

  @VisibleForTesting
  static boolean hasKind(@Nullable Tree tree, Tree.Kind kind) {
    return tree != null && tree.kind() == kind;
  }

  public static class MethodInvocationCollector extends BaseTreeVisitor {
    protected final List invocationTree = new ArrayList<>();
    private final Predicate collectPredicate;

    public MethodInvocationCollector(Predicate collectPredicate) {
      this.collectPredicate = collectPredicate;
    }

    public List getInvocationTree() {
      return invocationTree;
    }

    @Override
    public void visitMethodInvocation(MethodInvocationTree mit) {
      if (collectPredicate.test(mit.methodSymbol())) {
        invocationTree.add(ExpressionUtils.methodName(mit));
      }
      super.visitMethodInvocation(mit);
    }

    @Override
    public void visitNewClass(NewClassTree tree) {
      if (collectPredicate.test(tree.methodSymbol())) {
        invocationTree.add(tree.identifier());
      }
      super.visitNewClass(tree);
    }

    @Override
    public void visitClass(ClassTree tree) {
      // Skip class
    }

    @Override
    public void visitLambdaExpression(LambdaExpressionTree lambdaExpressionTree) {
      // Skip lambdas
    }
  }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy