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

org.sonar.php.checks.GenericExceptionCheck Maven / Gradle / Ivy

/*
 * SonarQube PHP Plugin
 * Copyright (C) 2010-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 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.php.checks;

import java.util.HashSet;
import java.util.Set;
import javax.annotation.Nullable;
import org.sonar.check.Rule;
import org.sonar.plugins.php.api.tree.ScriptTree;
import org.sonar.plugins.php.api.tree.Tree.Kind;
import org.sonar.plugins.php.api.tree.declaration.NamespaceNameTree;
import org.sonar.plugins.php.api.tree.expression.FunctionCallTree;
import org.sonar.plugins.php.api.tree.expression.NewExpressionTree;
import org.sonar.plugins.php.api.tree.expression.ThrowExpressionTree;
import org.sonar.plugins.php.api.tree.statement.NamespaceStatementTree;
import org.sonar.plugins.php.api.tree.statement.UseClauseTree;
import org.sonar.plugins.php.api.visitors.PHPVisitorCheck;

@Rule(key = GenericExceptionCheck.KEY)
public class GenericExceptionCheck extends PHPVisitorCheck {

  public static final String KEY = "S112";
  public static final String MESSAGE = "Define and throw a dedicated exception instead of using a generic one.";

  private static final Set RAW_EXCEPTIONS = Set.of("ErrorException", "RuntimeException", "Exception");
  private Set importedGenericExceptions = new HashSet<>();
  private boolean inGlobalNamespace = true;

  @Override
  public void visitScript(ScriptTree tree) {
    importedGenericExceptions.clear();
    inGlobalNamespace = true;
    super.visitScript(tree);
  }

  @Override
  public void visitThrowExpression(ThrowExpressionTree tree) {
    NamespaceNameTree namespaceName = getThrownClassName(tree);

    if (namespaceName != null && isGenericException(namespaceName)) {
      context().newIssue(this, namespaceName, MESSAGE);
    }

    super.visitThrowExpression(tree);
  }

  @Override
  public void visitUseClause(UseClauseTree tree) {
    String qualifiedName = tree.namespaceName().qualifiedName();

    if (RAW_EXCEPTIONS.contains(qualifiedName)) {
      importedGenericExceptions.add(tree.alias() != null ? tree.alias().text() : qualifiedName);
    }

    super.visitUseClause(tree);
  }

  @Override
  public void visitNamespaceStatement(NamespaceStatementTree tree) {
    if (tree.namespaceName() != null) {
      inGlobalNamespace = false;
    }

    super.visitNamespaceStatement(tree);

    // Delimited namespace with curly braces
    if (tree.openCurlyBrace() != null) {
      inGlobalNamespace = true;
    }
  }

  private boolean isGenericException(NamespaceNameTree namespaceName) {
    return isImportedGenericException(namespaceName) || isGlobalNamespaceGenericException(namespaceName);

  }

  private boolean isImportedGenericException(NamespaceNameTree namespaceName) {
    return importedGenericExceptions.contains(namespaceName.fullName());
  }

  private boolean isGlobalNamespaceGenericException(NamespaceNameTree namespaceName) {
    return isFromGlobalNamespace(namespaceName) && RAW_EXCEPTIONS.contains(namespaceName.name().text());
  }

  private boolean isFromGlobalNamespace(NamespaceNameTree namespaceName) {
    return !namespaceName.hasQualifiers()
      && (namespaceName.isFullyQualified() || inGlobalNamespace);
  }

  @Nullable
  private static NamespaceNameTree getThrownClassName(ThrowExpressionTree tree) {
    if (tree.expression().is(Kind.NEW_EXPRESSION)) {
      NewExpressionTree newExpression = (NewExpressionTree) tree.expression();

      if (newExpression.expression().is(Kind.FUNCTION_CALL)) {
        FunctionCallTree functionCall = (FunctionCallTree) newExpression.expression();

        if (functionCall.callee().is(Kind.NAMESPACE_NAME)) {
          return (NamespaceNameTree) functionCall.callee();
        }
      }
    }
    return null;
  }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy