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

io.codemodder.codemods.Exceptions Maven / Gradle / Ivy

The newest version!
package io.codemodder.codemods;

import com.github.javaparser.ast.Node;
import com.github.javaparser.ast.NodeList;
import com.github.javaparser.ast.body.MethodDeclaration;
import com.github.javaparser.ast.expr.MethodCallExpr;
import com.github.javaparser.ast.expr.ObjectCreationExpr;
import com.github.javaparser.ast.stmt.BlockStmt;
import com.github.javaparser.ast.stmt.CatchClause;
import com.github.javaparser.ast.stmt.TryStmt;
import com.github.javaparser.ast.type.ReferenceType;
import com.github.javaparser.resolution.declarations.ResolvedConstructorDeclaration;
import com.github.javaparser.resolution.declarations.ResolvedMethodDeclaration;
import com.github.javaparser.resolution.types.ResolvedType;
import java.util.List;
import java.util.Optional;

/**
 * This method holds some candidate APIs for dealing with exception management, usually in light of
 * post-processing a codemod change. After some stability is achieved here, this should be made a
 * public API.
 */
final class Exceptions {

  private Exceptions() {
    // utility class
  }

  /**
   * This method attempts to "clean up" after a method/constructor which is known to throw an
   * exception is now gone.
   *
   * 

In this case, we may want to alter its existing try block, or maybe even if its method * signature. * *

This method is not guaranteed to be correct in situations in which there is limited type * resolution, like in the case of first party library information being missing. * * @param node the new or modified node * @param exceptionNoLongerThrownFqcn the fully qualified class name of the exception that is no * longer thrown after the change to the node */ static void cleanupExceptionHandling(final Node node, final String exceptionNoLongerThrownFqcn) { Optional tryStmt = node.findAncestor(TryStmt.class); String exceptionSimpleName = exceptionNoLongerThrownFqcn.substring(exceptionNoLongerThrownFqcn.lastIndexOf('.') + 1); if (tryStmt.isPresent()) { TryStmt tryStatement = tryStmt.get(); Node parent = tryStatement.getParentNode().get(); List allOtherMethodCallsInTry = tryStatement.getTryBlock().findAll(MethodCallExpr.class).stream().toList(); List allConstructorsInTry = tryStatement.getTryBlock().findAll(ObjectCreationExpr.class).stream().toList(); if (anyOtherMethodsAreKnownThrowThis( allOtherMethodCallsInTry, allConstructorsInTry, exceptionNoLongerThrownFqcn)) { // we can't touch it return; } // if there's no other methods we think are causing this exception, let's remove the clause NodeList catchClauses = tryStatement.getCatchClauses(); catchClauses.removeIf( cc -> cc.getParameter().getType().isClassOrInterfaceType() && cc.getParameter() .getType() .asClassOrInterfaceType() .getNameAsString() .equals(exceptionSimpleName)); if (catchClauses.isEmpty()) { BlockStmt tryBlockCode = tryStatement.getTryBlock(); parent.replace(tryStatement, tryBlockCode); } } else { // the method signature can have the throws clause Optional methodDeclaration = node.findAncestor(MethodDeclaration.class); if (methodDeclaration.isEmpty()) { return; } MethodDeclaration method = methodDeclaration.get(); List allOtherMethodCallsInTry = method.findAll(MethodCallExpr.class).stream().toList(); List allConstructorsInTry = method.findAll(ObjectCreationExpr.class).stream().toList(); if (anyOtherMethodsAreKnownThrowThis( allOtherMethodCallsInTry, allConstructorsInTry, exceptionNoLongerThrownFqcn)) { // we can't touch it return; } NodeList thrownExceptions = method.getThrownExceptions(); thrownExceptions.removeIf( referenceType -> referenceType.asClassOrInterfaceType().getNameAsString().equals(exceptionSimpleName)); } } private static boolean anyOtherMethodsAreKnownThrowThis( final List allOtherMethodCallsInTry, final List allConstructorsInTry, final String exceptionFqcn) { // detect from the methods for (MethodCallExpr method : allOtherMethodCallsInTry) { try { ResolvedMethodDeclaration resolve = method.resolve(); List methodExceptionTypes = resolve.getSpecifiedExceptions(); for (ResolvedType methodExceptionType : methodExceptionTypes) { if (methodExceptionType.asReferenceType().getQualifiedName().equals(exceptionFqcn)) { return true; } } } catch (Exception e) { // we can't resolve the method, so we can't tell if it throws this exception } } // detect from the constructors for (ObjectCreationExpr constructor : allConstructorsInTry) { try { ResolvedConstructorDeclaration resolve = constructor.resolve(); List constructorExceptionTypes = resolve.getSpecifiedExceptions(); for (ResolvedType constructorExceptionType : constructorExceptionTypes) { if (constructorExceptionType.asReferenceType().getQualifiedName().equals(exceptionFqcn)) { return true; } } } catch (Exception e) { // we can't resolve the constructor, so we can't tell if it throws this exception } } return false; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy