mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
IDEA-225778 Inline object with the subsequent call
GitOrigin-RevId: 122e10e69f93c31e289a024381c7315f1a62203b
This commit is contained in:
committed by
intellij-monorepo-bot
parent
7b5dcb3d80
commit
c57a3e9f2a
@@ -6,6 +6,7 @@ import com.intellij.codeInsight.TargetElementUtil;
|
||||
import com.intellij.lang.java.JavaLanguage;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.ui.Messages;
|
||||
import com.intellij.openapi.vfs.ReadonlyStatusHandler;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.psi.*;
|
||||
@@ -101,6 +102,22 @@ public class InlineMethodHandler extends JavaInlineActionHandler {
|
||||
}
|
||||
final boolean chainingConstructor = InlineUtil.isChainingConstructor(method);
|
||||
if (!chainingConstructor) {
|
||||
InlineObjectProcessor processor = InlineObjectProcessor.create(reference, method);
|
||||
if (processor != null) {
|
||||
if (Messages.showOkCancelDialog("Do you want to inline the object and the subsequent call?", "Inline Object", "Inline", "Cancel",
|
||||
Messages.getQuestionIcon()) == Messages.OK) {
|
||||
processor = InlineObjectProcessor.create(reference, method);
|
||||
if (processor == null) {
|
||||
// Code changed while dialog was displayed?
|
||||
String message = RefactoringBundle.message("refactoring.cannot.be.applied", REFACTORING_NAME);
|
||||
CommonRefactoringUtil.showErrorHint(project, editor, message, REFACTORING_NAME, HelpID.INLINE_CONSTRUCTOR);
|
||||
} else {
|
||||
processor.setPrepareSuccessfulSwingThreadCallback(() -> {});
|
||||
processor.run();
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (!isThisReference(reference)) {
|
||||
String message = RefactoringBundle.message("refactoring.cannot.be.applied.to.inline.non.chaining.constructors", REFACTORING_NAME);
|
||||
CommonRefactoringUtil.showErrorHint(project, editor, message, REFACTORING_NAME, HelpID.INLINE_CONSTRUCTOR);
|
||||
|
||||
@@ -0,0 +1,164 @@
|
||||
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.intellij.refactoring.inline;
|
||||
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.codeStyle.JavaCodeStyleManager;
|
||||
import com.intellij.psi.codeStyle.VariableKind;
|
||||
import com.intellij.psi.infos.MethodCandidateInfo;
|
||||
import com.intellij.psi.search.GlobalSearchScope;
|
||||
import com.intellij.psi.search.LocalSearchScope;
|
||||
import com.intellij.psi.search.searches.ReferencesSearch;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.psi.util.PsiTypesUtil;
|
||||
import com.intellij.psi.util.PsiUtil;
|
||||
import com.intellij.psi.util.TypeConversionUtil;
|
||||
import com.intellij.refactoring.util.InlineUtil;
|
||||
import com.intellij.refactoring.util.RefactoringUtil;
|
||||
import com.intellij.util.ObjectUtils;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* A helper class to perform the parameter substitution during the Inline method refactoring.
|
||||
* It helps to declare parameters as locals, passing arguments from the call site and then tries to inline the parameters when possible.
|
||||
*/
|
||||
class InlineMethodHelper {
|
||||
private static final Logger LOG = Logger.getInstance("#com.intellij.refactoring.inline.InlineMethodHelper");
|
||||
|
||||
private final @NotNull Project myProject;
|
||||
private final @NotNull PsiManager myManager;
|
||||
private final @NotNull PsiMethod myMethod;
|
||||
private final @NotNull PsiMethod myMethodCopy;
|
||||
private final @NotNull PsiElementFactory myFactory;
|
||||
private final @NotNull JavaCodeStyleManager myJavaCodeStyle;
|
||||
private final @NotNull PsiCallExpression myCall;
|
||||
private final @NotNull PsiExpressionList myCallArguments;
|
||||
private final @NotNull PsiSubstitutor mySubstitutor;
|
||||
|
||||
InlineMethodHelper(@NotNull Project project, @NotNull PsiMethod method, @NotNull PsiMethod methodCopy, @NotNull PsiCallExpression call) {
|
||||
myProject = project;
|
||||
myManager = method.getManager();
|
||||
myMethod = method;
|
||||
myMethodCopy = methodCopy;
|
||||
myCall = call;
|
||||
myCallArguments = Objects.requireNonNull(call.getArgumentList());
|
||||
myFactory = JavaPsiFacade.getElementFactory(myProject);
|
||||
myJavaCodeStyle = JavaCodeStyleManager.getInstance(myProject);
|
||||
mySubstitutor = createSubstitutor();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
PsiSubstitutor getSubstitutor() {
|
||||
return mySubstitutor;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private PsiSubstitutor createSubstitutor() {
|
||||
JavaResolveResult resolveResult = myCall.resolveMethodGenerics();
|
||||
if (myMethod.isPhysical()) {
|
||||
// Could be specialized
|
||||
LOG.assertTrue(myManager.areElementsEquivalent(resolveResult.getElement(), myMethod));
|
||||
}
|
||||
if (resolveResult.getSubstitutor() != PsiSubstitutor.EMPTY) {
|
||||
Iterator<PsiTypeParameter> oldTypeParameters = PsiUtil.typeParametersIterator(myMethod);
|
||||
Iterator<PsiTypeParameter> newTypeParameters = PsiUtil.typeParametersIterator(myMethodCopy);
|
||||
PsiSubstitutor substitutor = resolveResult.getSubstitutor();
|
||||
while (newTypeParameters.hasNext()) {
|
||||
final PsiTypeParameter newTypeParameter = newTypeParameters.next();
|
||||
final PsiTypeParameter oldTypeParameter = oldTypeParameters.next();
|
||||
substitutor = substitutor.put(newTypeParameter, resolveResult.getSubstitutor().substitute(oldTypeParameter));
|
||||
}
|
||||
return substitutor;
|
||||
}
|
||||
|
||||
return PsiSubstitutor.EMPTY;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
PsiLocalVariable[] declareParameters() {
|
||||
PsiCodeBlock block = Objects.requireNonNull(myMethodCopy.getBody());
|
||||
final int applicabilityLevel = PsiUtil.getApplicabilityLevel(myMethod, mySubstitutor, myCallArguments);
|
||||
PsiParameter[] parameters = myMethodCopy.getParameterList().getParameters();
|
||||
PsiLocalVariable[] parameterVars = new PsiLocalVariable[parameters.length];
|
||||
for (int i = parameters.length - 1; i >= 0; i--) {
|
||||
PsiParameter parameter = parameters[i];
|
||||
String parameterName = parameter.getName();
|
||||
String name = parameterName;
|
||||
name = myJavaCodeStyle.variableNameToPropertyName(name, VariableKind.PARAMETER);
|
||||
name = myJavaCodeStyle.propertyNameToVariableName(name, VariableKind.LOCAL_VARIABLE);
|
||||
if (!name.equals(parameterName)) {
|
||||
name = myJavaCodeStyle.suggestUniqueVariableName(name, block.getFirstChild(), true);
|
||||
}
|
||||
RefactoringUtil.renameVariableReferences(parameter, name, new LocalSearchScope(block), true);
|
||||
PsiType paramType = parameter.getType();
|
||||
@NonNls String defaultValue;
|
||||
if (paramType instanceof PsiEllipsisType) {
|
||||
final PsiEllipsisType ellipsisType = (PsiEllipsisType)paramType;
|
||||
paramType = mySubstitutor.substitute(ellipsisType.toArrayType());
|
||||
if (applicabilityLevel == MethodCandidateInfo.ApplicabilityLevel.VARARGS) {
|
||||
PsiType componentType = ((PsiArrayType)paramType).getComponentType();
|
||||
defaultValue = "new " + ObjectUtils.notNull(TypeConversionUtil.erasure(componentType), componentType).getCanonicalText() + "[]{}";
|
||||
}
|
||||
else {
|
||||
defaultValue = PsiTypesUtil.getDefaultValueOfType(paramType);
|
||||
}
|
||||
}
|
||||
else {
|
||||
defaultValue = PsiTypesUtil.getDefaultValueOfType(paramType);
|
||||
}
|
||||
|
||||
PsiExpression initializer = myFactory.createExpressionFromText(defaultValue, null);
|
||||
PsiType varType = GenericsUtil.getVariableTypeByExpressionType(mySubstitutor.substitute(paramType));
|
||||
PsiDeclarationStatement declaration = myFactory.createVariableDeclarationStatement(name, varType, initializer);
|
||||
declaration = (PsiDeclarationStatement)block.addAfter(declaration, null);
|
||||
parameterVars[i] = (PsiLocalVariable)declaration.getDeclaredElements()[0];
|
||||
PsiUtil.setModifierProperty(parameterVars[i], PsiModifier.FINAL, parameter.hasModifierProperty(PsiModifier.FINAL));
|
||||
}
|
||||
return parameterVars;
|
||||
}
|
||||
|
||||
void initializeParameters(PsiLocalVariable[] vars) {
|
||||
PsiExpression[] args = myCallArguments.getExpressions();
|
||||
if (vars.length > 0) {
|
||||
for (int i = 0; i < args.length; i++) {
|
||||
int j = Math.min(i, vars.length - 1);
|
||||
final PsiExpression initializer = vars[j].getInitializer();
|
||||
LOG.assertTrue(initializer != null);
|
||||
if (initializer instanceof PsiNewExpression) {
|
||||
PsiArrayInitializerExpression arrayInitializer = ((PsiNewExpression)initializer).getArrayInitializer();
|
||||
if (arrayInitializer != null) { //varargs initializer
|
||||
arrayInitializer.add(args[i]);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
initializer.replace(args[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void inlineParameters(PsiLocalVariable[] parmVars) {
|
||||
final PsiParameter[] parameters = myMethod.getParameterList().getParameters();
|
||||
for (int i = 0; i < parmVars.length; i++) {
|
||||
final PsiParameter parameter = parameters[i];
|
||||
final boolean strictlyFinal = parameter.hasModifierProperty(PsiModifier.FINAL) && isStrictlyFinal(parameter);
|
||||
InlineUtil.tryInlineGeneratedLocal(parmVars[i], strictlyFinal);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isStrictlyFinal(PsiParameter parameter) {
|
||||
for (PsiReference reference : ReferencesSearch.search(parameter, GlobalSearchScope.projectScope(myProject), false)) {
|
||||
final PsiElement refElement = reference.getElement();
|
||||
final PsiElement anonymousClass = PsiTreeUtil.getParentOfType(refElement, PsiAnonymousClass.class);
|
||||
if (anonymousClass != null && PsiTreeUtil.isAncestor(myMethod, anonymousClass, true)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -4,8 +4,6 @@ package com.intellij.refactoring.inline;
|
||||
import com.intellij.codeInsight.AnnotationUtil;
|
||||
import com.intellij.codeInsight.ChangeContextUtil;
|
||||
import com.intellij.codeInsight.ExpressionUtil;
|
||||
import com.intellij.codeInsight.daemon.impl.analysis.HighlightControlFlowUtil;
|
||||
import com.intellij.codeInsight.daemon.impl.quickfix.SimplifyBooleanExpressionFix;
|
||||
import com.intellij.history.LocalHistory;
|
||||
import com.intellij.history.LocalHistoryAction;
|
||||
import com.intellij.lang.Language;
|
||||
@@ -27,13 +25,12 @@ import com.intellij.psi.controlFlow.*;
|
||||
import com.intellij.psi.impl.source.codeStyle.CodeEditUtil;
|
||||
import com.intellij.psi.impl.source.javadoc.PsiDocMethodOrFieldRef;
|
||||
import com.intellij.psi.impl.source.resolve.reference.impl.JavaLangClassMemberReference;
|
||||
import com.intellij.psi.infos.MethodCandidateInfo;
|
||||
import com.intellij.psi.search.GlobalSearchScope;
|
||||
import com.intellij.psi.search.LocalSearchScope;
|
||||
import com.intellij.psi.search.searches.OverridingMethodsSearch;
|
||||
import com.intellij.psi.search.searches.ReferencesSearch;
|
||||
import com.intellij.psi.util.*;
|
||||
import com.intellij.psi.util.InheritanceUtil;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.psi.util.PsiUtil;
|
||||
import com.intellij.refactoring.BaseRefactoringProcessor;
|
||||
import com.intellij.refactoring.RefactoringBundle;
|
||||
import com.intellij.refactoring.introduceParameter.Util;
|
||||
@@ -46,9 +43,9 @@ import com.intellij.usageView.UsageViewDescriptor;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import com.intellij.util.IncorrectOperationException;
|
||||
import com.intellij.util.JavaPsiConstructorUtil;
|
||||
import com.intellij.util.ObjectUtils;
|
||||
import com.intellij.util.containers.MultiMap;
|
||||
import com.siyeh.ig.psiutils.*;
|
||||
import com.siyeh.ig.psiutils.CommentTracker;
|
||||
import com.siyeh.ig.psiutils.SideEffectChecker;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@@ -337,9 +334,9 @@ public class InlineMethodProcessor extends BaseRefactoringProcessor {
|
||||
* @param usages
|
||||
* @param elementToInline
|
||||
*/
|
||||
private static Map<PsiMember, Set<PsiMember>> getInaccessible(HashSet<? extends PsiMember> referencedElements,
|
||||
UsageInfo[] usages,
|
||||
PsiElement elementToInline) {
|
||||
static Map<PsiMember, Set<PsiMember>> getInaccessible(HashSet<? extends PsiMember> referencedElements,
|
||||
UsageInfo[] usages,
|
||||
PsiElement elementToInline) {
|
||||
final Map<PsiMember, Set<PsiMember>> result = new HashMap<>();
|
||||
final PsiResolveHelper resolveHelper = JavaPsiFacade.getInstance(elementToInline.getProject()).getResolveHelper();
|
||||
for (UsageInfo usage : usages) {
|
||||
@@ -613,11 +610,12 @@ public class InlineMethodProcessor extends BaseRefactoringProcessor {
|
||||
|
||||
PsiMethodCallExpression methodCall = (PsiMethodCallExpression)ref.getParent();
|
||||
|
||||
PsiSubstitutor callSubstitutor = getCallSubstitutor(methodCall);
|
||||
BlockData blockData = prepareBlock(ref, callSubstitutor, methodCall.getArgumentList());
|
||||
InlineMethodHelper helper = new InlineMethodHelper(myProject, myMethod, myMethodCopy, methodCall);
|
||||
BlockData blockData = prepareBlock(ref, helper);
|
||||
InlineUtil.solveVariableNameConflicts(blockData.block, ref, myMethodCopy.getBody());
|
||||
addParmAndThisVarInitializers(blockData, methodCall);
|
||||
|
||||
helper.initializeParameters(blockData.parmVars);
|
||||
addThisInitializer(methodCall, blockData.thisVar);
|
||||
|
||||
PsiElement anchor = RefactoringUtil.getParentStatement(methodCall, true);
|
||||
if (anchor == null) {
|
||||
PsiEnumConstant enumConstant = PsiTreeUtil.getParentOfType(methodCall, PsiEnumConstant.class);
|
||||
@@ -693,7 +691,7 @@ public class InlineMethodProcessor extends BaseRefactoringProcessor {
|
||||
PsiClass thisClass = myMethod.getContainingClass();
|
||||
PsiExpression thisAccessExpr;
|
||||
if (thisVar != null) {
|
||||
if (!canInlineParmOrThisVariable(thisVar)) {
|
||||
if (!InlineUtil.canInlineParameterOrThisVariable(thisVar)) {
|
||||
thisAccessExpr = myFactory.createExpressionFromText(thisVar.getName(), null);
|
||||
}
|
||||
else {
|
||||
@@ -705,39 +703,42 @@ public class InlineMethodProcessor extends BaseRefactoringProcessor {
|
||||
}
|
||||
ChangeContextUtil.decodeContextInfo(anchorParent, thisClass, thisAccessExpr);
|
||||
|
||||
PsiReferenceExpression resultUsage = null;
|
||||
if (blockData.resultVar != null) {
|
||||
PsiExpression expr = myFactory.createExpressionFromText(blockData.resultVar.getName(), null);
|
||||
resultUsage = (PsiReferenceExpression)new CommentTracker().replaceAndRestoreComments(methodCall, expr);
|
||||
}
|
||||
else {
|
||||
// If return var is not specified, we trust that InlineTransformer fully processed the original anchor statement,
|
||||
// and we can delete it.
|
||||
CommentTracker tracker = new CommentTracker();
|
||||
if (firstAdded != null) {
|
||||
tracker.delete(anchor);
|
||||
tracker.insertCommentsBefore(firstAdded);
|
||||
} else {
|
||||
tracker.deleteAndRestoreComments(anchor);
|
||||
}
|
||||
}
|
||||
PsiReferenceExpression resultUsage = replaceCall(myFactory, methodCall, firstAdded, blockData.resultVar);
|
||||
|
||||
if (thisVar != null) {
|
||||
inlineParmOrThisVariable(thisVar, false);
|
||||
}
|
||||
final PsiParameter[] parameters = myMethod.getParameterList().getParameters();
|
||||
for (int i = 0; i < parmVars.length; i++) {
|
||||
final PsiParameter parameter = parameters[i];
|
||||
final boolean strictlyFinal = parameter.hasModifierProperty(PsiModifier.FINAL) && isStrictlyFinal(parameter);
|
||||
inlineParmOrThisVariable(parmVars[i], strictlyFinal);
|
||||
InlineUtil.tryInlineGeneratedLocal(thisVar, false);
|
||||
}
|
||||
helper.inlineParameters(parmVars);
|
||||
if (resultVar != null && resultUsage != null) {
|
||||
inlineResultVariable(resultVar, resultUsage);
|
||||
InlineUtil.tryInlineResultVariable(resultVar, resultUsage);
|
||||
}
|
||||
|
||||
ChangeContextUtil.clearContextInfo(anchorParent);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
static PsiReferenceExpression replaceCall(@NotNull PsiElementFactory factory,
|
||||
@NotNull PsiMethodCallExpression methodCall,
|
||||
@Nullable PsiElement firstAdded,
|
||||
@Nullable PsiLocalVariable resultVar) {
|
||||
if (resultVar != null) {
|
||||
PsiExpression expr = factory.createExpressionFromText(resultVar.getName(), null);
|
||||
return (PsiReferenceExpression)new CommentTracker().replaceAndRestoreComments(methodCall, expr);
|
||||
}
|
||||
// If return var is not specified, we trust that InlineTransformer fully processed the original anchor statement,
|
||||
// and we can delete it.
|
||||
CommentTracker tracker = new CommentTracker();
|
||||
PsiElement anchor = RefactoringUtil.getParentStatement(methodCall, true);
|
||||
assert anchor != null;
|
||||
if (firstAdded != null) {
|
||||
tracker.delete(anchor);
|
||||
tracker.insertCommentsBefore(firstAdded);
|
||||
} else {
|
||||
tracker.deleteAndRestoreComments(anchor);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private PsiExpression inlineParameterReference(@NotNull PsiReferenceExpression expression, BlockData blockData) {
|
||||
if (expression.getQualifierExpression() != null) return expression;
|
||||
@@ -747,46 +748,13 @@ public class InlineMethodProcessor extends BaseRefactoringProcessor {
|
||||
if (paramIdx < 0) return expression;
|
||||
PsiExpression initializer = blockData.parmVars[paramIdx].getInitializer();
|
||||
if (initializer == null) return expression;
|
||||
return inlineInitializer((PsiVariable)resolve, initializer, expression);
|
||||
}
|
||||
|
||||
private PsiSubstitutor getCallSubstitutor(PsiMethodCallExpression methodCall) {
|
||||
JavaResolveResult resolveResult = methodCall.getMethodExpression().advancedResolve(false);
|
||||
if (myMethod.isPhysical()) {
|
||||
// Could be specialized
|
||||
LOG.assertTrue(myManager.areElementsEquivalent(resolveResult.getElement(), myMethod));
|
||||
}
|
||||
if (resolveResult.getSubstitutor() != PsiSubstitutor.EMPTY) {
|
||||
Iterator<PsiTypeParameter> oldTypeParameters = PsiUtil.typeParametersIterator(myMethod);
|
||||
Iterator<PsiTypeParameter> newTypeParameters = PsiUtil.typeParametersIterator(myMethodCopy);
|
||||
PsiSubstitutor substitutor = resolveResult.getSubstitutor();
|
||||
while (newTypeParameters.hasNext()) {
|
||||
final PsiTypeParameter newTypeParameter = newTypeParameters.next();
|
||||
final PsiTypeParameter oldTypeParameter = oldTypeParameters.next();
|
||||
substitutor = substitutor.put(newTypeParameter, resolveResult.getSubstitutor().substitute(oldTypeParameter));
|
||||
}
|
||||
return substitutor;
|
||||
}
|
||||
|
||||
return PsiSubstitutor.EMPTY;
|
||||
return InlineUtil.inlineInitializer((PsiVariable)resolve, initializer, expression);
|
||||
}
|
||||
|
||||
private void substituteMethodTypeParams(PsiElement scope, final PsiSubstitutor substitutor) {
|
||||
InlineUtil.substituteTypeParams(scope, substitutor, myFactory);
|
||||
}
|
||||
|
||||
private boolean isStrictlyFinal(PsiParameter parameter) {
|
||||
for (PsiReference reference : ReferencesSearch.search(parameter, myRefactoringScope, false)) {
|
||||
final PsiElement refElement = reference.getElement();
|
||||
final PsiElement anonymousClass = PsiTreeUtil.getParentOfType(refElement, PsiAnonymousClass.class);
|
||||
if (anonymousClass != null && PsiTreeUtil.isAncestor(myMethod, anonymousClass, true)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
private boolean syncNeeded(final PsiReferenceExpression ref) {
|
||||
if (!myMethod.hasModifierProperty(PsiModifier.SYNCHRONIZED)) return false;
|
||||
final PsiMethod containingMethod = Util.getContainingMethod(ref);
|
||||
@@ -797,9 +765,10 @@ public class InlineMethodProcessor extends BaseRefactoringProcessor {
|
||||
return !sourceContainingClass.equals(targetContainingClass);
|
||||
}
|
||||
|
||||
private BlockData prepareBlock(PsiReferenceExpression ref, PsiSubstitutor callSubstitutor, PsiExpressionList argumentList)
|
||||
private BlockData prepareBlock(PsiReferenceExpression ref, InlineMethodHelper helper)
|
||||
throws IncorrectOperationException {
|
||||
final PsiCodeBlock block = Objects.requireNonNull(myMethodCopy.getBody());
|
||||
PsiSubstitutor callSubstitutor = helper.getSubstitutor();
|
||||
if (callSubstitutor != PsiSubstitutor.EMPTY) {
|
||||
substituteMethodTypeParams(block, callSubstitutor);
|
||||
}
|
||||
@@ -808,7 +777,7 @@ public class InlineMethodProcessor extends BaseRefactoringProcessor {
|
||||
PsiType returnType = callSubstitutor.substitute(myMethod.getReturnType());
|
||||
InlineTransformer transformer = myTransformerChooser.apply(ref);
|
||||
|
||||
PsiLocalVariable[] parmVars = declareParameters(block, argumentList, callSubstitutor);
|
||||
PsiLocalVariable[] parmVars = helper.declareParameters();
|
||||
|
||||
PsiLocalVariable thisVar = declareThis(callSubstitutor, block);
|
||||
|
||||
@@ -819,48 +788,6 @@ public class InlineMethodProcessor extends BaseRefactoringProcessor {
|
||||
return new BlockData(block, thisVar, parmVars, resultVar);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private PsiLocalVariable[] declareParameters(PsiCodeBlock block, PsiExpressionList argumentList, PsiSubstitutor callSubstitutor) {
|
||||
final int applicabilityLevel = PsiUtil.getApplicabilityLevel(myMethod, callSubstitutor, argumentList);
|
||||
PsiParameter[] parms = myMethodCopy.getParameterList().getParameters();
|
||||
PsiLocalVariable[] parmVars = new PsiLocalVariable[parms.length];
|
||||
for (int i = parms.length - 1; i >= 0; i--) {
|
||||
PsiParameter parm = parms[i];
|
||||
String parmName = parm.getName();
|
||||
String name = parmName;
|
||||
name = myJavaCodeStyle.variableNameToPropertyName(name, VariableKind.PARAMETER);
|
||||
name = myJavaCodeStyle.propertyNameToVariableName(name, VariableKind.LOCAL_VARIABLE);
|
||||
if (!name.equals(parmName)) {
|
||||
name = myJavaCodeStyle.suggestUniqueVariableName(name, block.getFirstChild(), true);
|
||||
}
|
||||
RefactoringUtil.renameVariableReferences(parm, name, new LocalSearchScope(myMethodCopy.getBody()), true);
|
||||
PsiType paramType = parm.getType();
|
||||
@NonNls String defaultValue;
|
||||
if (paramType instanceof PsiEllipsisType) {
|
||||
final PsiEllipsisType ellipsisType = (PsiEllipsisType)paramType;
|
||||
paramType = callSubstitutor.substitute(ellipsisType.toArrayType());
|
||||
if (applicabilityLevel == MethodCandidateInfo.ApplicabilityLevel.VARARGS) {
|
||||
PsiType componentType = ((PsiArrayType)paramType).getComponentType();
|
||||
defaultValue = "new " + ObjectUtils.notNull(TypeConversionUtil.erasure(componentType), componentType).getCanonicalText() + "[]{}";
|
||||
}
|
||||
else {
|
||||
defaultValue = PsiTypesUtil.getDefaultValueOfType(paramType);
|
||||
}
|
||||
}
|
||||
else {
|
||||
defaultValue = PsiTypesUtil.getDefaultValueOfType(paramType);
|
||||
}
|
||||
|
||||
PsiExpression initializer = myFactory.createExpressionFromText(defaultValue, null);
|
||||
PsiType varType = GenericsUtil.getVariableTypeByExpressionType(callSubstitutor.substitute(paramType));
|
||||
PsiDeclarationStatement declaration = myFactory.createVariableDeclarationStatement(name, varType, initializer);
|
||||
declaration = (PsiDeclarationStatement)block.addAfter(declaration, null);
|
||||
parmVars[i] = (PsiLocalVariable)declaration.getDeclaredElements()[0];
|
||||
PsiUtil.setModifierProperty(parmVars[i], PsiModifier.FINAL, parm.hasModifierProperty(PsiModifier.FINAL));
|
||||
}
|
||||
return parmVars;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private PsiLocalVariable declareThis(PsiSubstitutor callSubstitutor, PsiCodeBlock block) {
|
||||
PsiClass containingClass = myMethod.getContainingClass();
|
||||
@@ -901,24 +828,8 @@ public class InlineMethodProcessor extends BaseRefactoringProcessor {
|
||||
}
|
||||
}
|
||||
|
||||
private void addParmAndThisVarInitializers(BlockData blockData, PsiMethodCallExpression methodCall) throws IncorrectOperationException {
|
||||
PsiExpression[] args = methodCall.getArgumentList().getExpressions();
|
||||
if (blockData.parmVars.length > 0) {
|
||||
for (int i = 0; i < args.length; i++) {
|
||||
int j = Math.min(i, blockData.parmVars.length - 1);
|
||||
final PsiExpression initializer = blockData.parmVars[j].getInitializer();
|
||||
LOG.assertTrue(initializer != null);
|
||||
if (initializer instanceof PsiNewExpression && ((PsiNewExpression)initializer).getArrayInitializer() != null) { //varargs initializer
|
||||
final PsiArrayInitializerExpression arrayInitializer = ((PsiNewExpression)initializer).getArrayInitializer();
|
||||
arrayInitializer.add(args[i]);
|
||||
continue;
|
||||
}
|
||||
|
||||
initializer.replace(args[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if (blockData.thisVar != null) {
|
||||
private void addThisInitializer(PsiMethodCallExpression methodCall, PsiLocalVariable thisVar) throws IncorrectOperationException {
|
||||
if (thisVar != null) {
|
||||
PsiExpression qualifier = methodCall.getMethodExpression().getQualifierExpression();
|
||||
if (qualifier == null) {
|
||||
PsiElement parent = methodCall.getContext();
|
||||
@@ -966,337 +877,7 @@ public class InlineMethodProcessor extends BaseRefactoringProcessor {
|
||||
else if (qualifier instanceof PsiSuperExpression) {
|
||||
qualifier = myFactory.createExpressionFromText("this", null);
|
||||
}
|
||||
blockData.thisVar.getInitializer().replace(qualifier);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean canInlineParmOrThisVariable(PsiLocalVariable variable) {
|
||||
boolean isAccessedForWriting = false;
|
||||
for (PsiReference ref : ReferencesSearch.search(variable)) {
|
||||
PsiElement refElement = ref.getElement();
|
||||
if (refElement instanceof PsiExpression) {
|
||||
if (PsiUtil.isAccessedForWriting((PsiExpression)refElement)) {
|
||||
isAccessedForWriting = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PsiExpression initializer = variable.getInitializer();
|
||||
boolean shouldBeFinal = variable.hasModifierProperty(PsiModifier.FINAL) && false;
|
||||
return canInlineParmOrThisVariable(initializer, shouldBeFinal, false, ReferencesSearch.search(variable).findAll().size(), isAccessedForWriting);
|
||||
}
|
||||
|
||||
private void inlineParmOrThisVariable(PsiLocalVariable variable, boolean strictlyFinal) throws IncorrectOperationException {
|
||||
PsiReference firstRef = ReferencesSearch.search(variable).findFirst();
|
||||
|
||||
PsiExpression initializer = variable.getInitializer();
|
||||
if (firstRef == null) {
|
||||
PsiDeclarationStatement declaration = (PsiDeclarationStatement)variable.getParent();
|
||||
if (initializer != null) {
|
||||
List<PsiExpression> sideEffects = SideEffectChecker.extractSideEffectExpressions(initializer);
|
||||
for (PsiStatement statement : StatementExtractor.generateStatements(sideEffects, initializer)) {
|
||||
declaration.getParent().addBefore(statement, declaration);
|
||||
}
|
||||
}
|
||||
declaration.delete();
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
boolean isAccessedForWriting = false;
|
||||
final Collection<PsiReference> refs = ReferencesSearch.search(variable).findAll();
|
||||
for (PsiReference ref : refs) {
|
||||
PsiElement refElement = ref.getElement();
|
||||
if (refElement instanceof PsiExpression) {
|
||||
if (PsiUtil.isAccessedForWriting((PsiExpression)refElement)) {
|
||||
isAccessedForWriting = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
boolean shouldBeFinal = variable.hasModifierProperty(PsiModifier.FINAL) && strictlyFinal;
|
||||
if (canInlineParmOrThisVariable(initializer, shouldBeFinal, strictlyFinal, refs.size(), isAccessedForWriting)) {
|
||||
if (shouldBeFinal) {
|
||||
declareUsedLocalsFinal(initializer, strictlyFinal);
|
||||
}
|
||||
for (PsiReference ref : refs) {
|
||||
initializer = inlineInitializer(variable, initializer, (PsiJavaCodeReferenceElement)ref);
|
||||
}
|
||||
variable.getParent().delete();
|
||||
}
|
||||
}
|
||||
|
||||
private PsiExpression inlineInitializer(PsiVariable variable, PsiExpression initializer, PsiJavaCodeReferenceElement ref) {
|
||||
if (initializer instanceof PsiThisExpression && ((PsiThisExpression)initializer).getQualifier() == null) {
|
||||
final PsiClass varThisClass = RefactoringChangeUtil.getThisClass(variable);
|
||||
if (RefactoringChangeUtil.getThisClass(ref) != varThisClass) {
|
||||
initializer = JavaPsiFacade.getElementFactory(myManager.getProject()).createExpressionFromText(varThisClass.getName() + ".this", variable);
|
||||
}
|
||||
}
|
||||
|
||||
PsiExpression expr = InlineUtil.inlineVariable(variable, initializer, ref);
|
||||
|
||||
InlineUtil.tryToInlineArrayCreationForVarargs(expr);
|
||||
|
||||
//Q: move the following code to some util? (addition to inline?)
|
||||
if (expr instanceof PsiThisExpression) {
|
||||
if (expr.getParent() instanceof PsiReferenceExpression) {
|
||||
PsiReferenceExpression refExpr = (PsiReferenceExpression)expr.getParent();
|
||||
PsiElement refElement = refExpr.resolve();
|
||||
PsiExpression exprCopy = (PsiExpression)refExpr.copy();
|
||||
refExpr = (PsiReferenceExpression)refExpr.replace(myFactory.createExpressionFromText(refExpr.getReferenceName(), null));
|
||||
if (refElement != null) {
|
||||
PsiElement newRefElement = refExpr.resolve();
|
||||
if (!refElement.equals(newRefElement)) {
|
||||
// change back
|
||||
refExpr.replace(exprCopy);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (expr instanceof PsiLiteralExpression && PsiType.BOOLEAN.equals(expr.getType())) {
|
||||
Boolean value = tryCast(((PsiLiteralExpression)expr).getValue(), Boolean.class);
|
||||
if (value != null) {
|
||||
SimplifyBooleanExpressionFix fix = new SimplifyBooleanExpressionFix(expr, value);
|
||||
if (fix.isAvailable()) {
|
||||
fix.invoke(myProject, expr.getContainingFile(), expr, expr);
|
||||
}
|
||||
}
|
||||
}
|
||||
return initializer;
|
||||
}
|
||||
|
||||
private boolean canInlineParmOrThisVariable(PsiExpression initializer,
|
||||
boolean shouldBeFinal,
|
||||
boolean strictlyFinal,
|
||||
int accessCount,
|
||||
boolean isAccessedForWriting) {
|
||||
if (strictlyFinal) {
|
||||
class CanAllLocalsBeDeclaredFinal extends JavaRecursiveElementWalkingVisitor {
|
||||
boolean success = true;
|
||||
|
||||
@Override public void visitReferenceExpression(PsiReferenceExpression expression) {
|
||||
final PsiElement psiElement = expression.resolve();
|
||||
if (psiElement instanceof PsiLocalVariable || psiElement instanceof PsiParameter) {
|
||||
if (!RefactoringUtil.canBeDeclaredFinal((PsiVariable)psiElement)) {
|
||||
success = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override public void visitElement(PsiElement element) {
|
||||
if (success) {
|
||||
super.visitElement(element);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final CanAllLocalsBeDeclaredFinal canAllLocalsBeDeclaredFinal = new CanAllLocalsBeDeclaredFinal();
|
||||
initializer.accept(canAllLocalsBeDeclaredFinal);
|
||||
if (!canAllLocalsBeDeclaredFinal.success) return false;
|
||||
}
|
||||
if (initializer instanceof PsiFunctionalExpression) return accessCount <= 1;
|
||||
if (initializer instanceof PsiReferenceExpression) {
|
||||
PsiVariable refVar = (PsiVariable)((PsiReferenceExpression)initializer).resolve();
|
||||
if (refVar == null) {
|
||||
return !isAccessedForWriting;
|
||||
}
|
||||
if (refVar instanceof PsiField) {
|
||||
if (isAccessedForWriting) return false;
|
||||
if (refVar.hasModifierProperty(PsiModifier.VOLATILE)) return accessCount <= 1;
|
||||
/*
|
||||
PsiField field = (PsiField)refVar;
|
||||
if (isFieldNonModifiable(field)){
|
||||
return true;
|
||||
}
|
||||
//TODO: other cases
|
||||
return false;
|
||||
*/
|
||||
return true; //TODO: "suspicious" places to review by user!
|
||||
}
|
||||
else {
|
||||
if (isAccessedForWriting) {
|
||||
if (refVar.hasModifierProperty(PsiModifier.FINAL) || shouldBeFinal) return false;
|
||||
PsiReference[] refs = ReferencesSearch.search(refVar, myRefactoringScope, false).toArray(PsiReference.EMPTY_ARRAY);
|
||||
return refs.length == 1; //TODO: control flow
|
||||
}
|
||||
else {
|
||||
if (shouldBeFinal) {
|
||||
return refVar.hasModifierProperty(PsiModifier.FINAL) || RefactoringUtil.canBeDeclaredFinal(refVar);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (isAccessedForWriting) {
|
||||
return false;
|
||||
}
|
||||
else if (initializer instanceof PsiCallExpression) {
|
||||
if (accessCount != 1) return false;//don't allow deleting probable side effects or multiply those side effects
|
||||
if (initializer instanceof PsiNewExpression) {
|
||||
final PsiArrayInitializerExpression arrayInitializer = ((PsiNewExpression)initializer).getArrayInitializer();
|
||||
if (arrayInitializer != null) {
|
||||
for (PsiExpression expression : arrayInitializer.getInitializers()) {
|
||||
if (!canInlineParmOrThisVariable(expression, shouldBeFinal, strictlyFinal, accessCount, false)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
final PsiExpressionList argumentList = ((PsiCallExpression)initializer).getArgumentList();
|
||||
if (argumentList == null) return false;
|
||||
final PsiExpression[] expressions = argumentList.getExpressions();
|
||||
for (PsiExpression expression : expressions) {
|
||||
if (!canInlineParmOrThisVariable(expression, shouldBeFinal, strictlyFinal, accessCount, false)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true; //TODO: "suspicious" places to review by user!
|
||||
}
|
||||
else if (initializer instanceof PsiLiteralExpression) {
|
||||
return true;
|
||||
}
|
||||
else if (initializer instanceof PsiPrefixExpression && ((PsiPrefixExpression)initializer).getOperand() instanceof PsiLiteralExpression) {
|
||||
return true;
|
||||
}
|
||||
else if (initializer instanceof PsiArrayAccessExpression) {
|
||||
final PsiExpression arrayExpression = ((PsiArrayAccessExpression)initializer).getArrayExpression();
|
||||
final PsiExpression indexExpression = ((PsiArrayAccessExpression)initializer).getIndexExpression();
|
||||
return canInlineParmOrThisVariable(arrayExpression, shouldBeFinal, strictlyFinal, accessCount, false) &&
|
||||
canInlineParmOrThisVariable(indexExpression, shouldBeFinal, strictlyFinal, accessCount, false);
|
||||
}
|
||||
else if (initializer instanceof PsiParenthesizedExpression) {
|
||||
PsiExpression expr = ((PsiParenthesizedExpression)initializer).getExpression();
|
||||
return expr == null || canInlineParmOrThisVariable(expr, shouldBeFinal, strictlyFinal, accessCount, false);
|
||||
}
|
||||
else if (initializer instanceof PsiTypeCastExpression) {
|
||||
PsiExpression operand = ((PsiTypeCastExpression)initializer).getOperand();
|
||||
return operand != null && canInlineParmOrThisVariable(operand, shouldBeFinal, strictlyFinal, accessCount, false);
|
||||
}
|
||||
else if (initializer instanceof PsiPolyadicExpression) {
|
||||
PsiPolyadicExpression binExpr = (PsiPolyadicExpression)initializer;
|
||||
for (PsiExpression op : binExpr.getOperands()) {
|
||||
if (!canInlineParmOrThisVariable(op, shouldBeFinal, strictlyFinal, accessCount, false)) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
else if (initializer instanceof PsiClassObjectAccessExpression) {
|
||||
return true;
|
||||
}
|
||||
else if (initializer instanceof PsiThisExpression) {
|
||||
return true;
|
||||
}
|
||||
else if (initializer instanceof PsiSuperExpression) {
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static void declareUsedLocalsFinal(PsiElement expr, boolean strictlyFinal) throws IncorrectOperationException {
|
||||
if (expr instanceof PsiReferenceExpression) {
|
||||
PsiElement refElement = ((PsiReferenceExpression)expr).resolve();
|
||||
if (refElement instanceof PsiLocalVariable || refElement instanceof PsiParameter) {
|
||||
if (strictlyFinal || RefactoringUtil.canBeDeclaredFinal((PsiVariable)refElement)) {
|
||||
PsiUtil.setModifierProperty(((PsiVariable)refElement), PsiModifier.FINAL, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
PsiElement[] children = expr.getChildren();
|
||||
for (PsiElement child : children) {
|
||||
declareUsedLocalsFinal(child, strictlyFinal);
|
||||
}
|
||||
}
|
||||
|
||||
private void inlineResultVariable(@NotNull PsiLocalVariable resultVar, @NotNull PsiReferenceExpression resultUsage) throws IncorrectOperationException {
|
||||
PsiElement context = PsiUtil.getVariableCodeBlock(resultVar, null);
|
||||
if (context == null) return;
|
||||
List<PsiReferenceExpression> references = VariableAccessUtils.getVariableReferences(resultVar, context);
|
||||
if (resultVar.getInitializer() == null) {
|
||||
PsiAssignmentExpression assignment = null;
|
||||
for (PsiReferenceExpression ref : references) {
|
||||
if (ref.getParent() instanceof PsiAssignmentExpression && ((PsiAssignmentExpression)ref.getParent()).getLExpression().equals(ref)) {
|
||||
if (assignment != null) {
|
||||
assignment = null;
|
||||
break;
|
||||
}
|
||||
else {
|
||||
assignment = (PsiAssignmentExpression)ref.getParent();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (assignment != null) {
|
||||
inlineSingleAssignment(resultVar, assignment, resultUsage);
|
||||
return;
|
||||
}
|
||||
}
|
||||
tryReplaceWithTarget(resultVar, resultUsage, context, references);
|
||||
}
|
||||
|
||||
/**
|
||||
* If result of the method is an initializer of another var, try to reuse that var to store the result.
|
||||
*/
|
||||
private static void tryReplaceWithTarget(@NotNull PsiLocalVariable variable,
|
||||
@NotNull PsiReferenceExpression usage,
|
||||
PsiElement context,
|
||||
List<PsiReferenceExpression> references) {
|
||||
PsiLocalVariable target = tryCast(PsiUtil.skipParenthesizedExprUp(usage.getParent()), PsiLocalVariable.class);
|
||||
if (target == null) return;
|
||||
String name = target.getName();
|
||||
if (!target.getType().equals(variable.getType())) return;
|
||||
PsiDeclarationStatement declaration = tryCast(target.getParent(), PsiDeclarationStatement.class);
|
||||
if (declaration == null || declaration.getDeclaredElements().length != 1) return;
|
||||
PsiModifierList modifiers = target.getModifierList();
|
||||
if (modifiers != null && modifiers.getAnnotations().length != 0) return;
|
||||
boolean effectivelyFinal = HighlightControlFlowUtil.isEffectivelyFinal(variable, context, null);
|
||||
if (!effectivelyFinal && !VariableAccessUtils.canUseAsNonFinal(target)) return;
|
||||
|
||||
for (PsiReferenceExpression reference : references) {
|
||||
ExpressionUtils.bindReferenceTo(reference, name);
|
||||
}
|
||||
if (effectivelyFinal && target.hasModifierProperty(PsiModifier.FINAL)) {
|
||||
PsiModifierList modifierList = variable.getModifierList();
|
||||
if (modifierList != null) {
|
||||
modifierList.setModifierProperty(PsiModifier.FINAL, true);
|
||||
}
|
||||
}
|
||||
variable.setName(name);
|
||||
new CommentTracker().deleteAndRestoreComments(declaration);
|
||||
}
|
||||
|
||||
private void inlineSingleAssignment(@NotNull PsiVariable resultVar,
|
||||
@NotNull PsiAssignmentExpression assignment,
|
||||
@NotNull PsiReferenceExpression resultUsage) {
|
||||
LOG.assertTrue(assignment.getParent() instanceof PsiExpressionStatement);
|
||||
// SCR3175 fixed: inline only if declaration and assignment is in the same code block.
|
||||
if (!(assignment.getParent().getParent() == resultVar.getParent().getParent())) return;
|
||||
String name = Objects.requireNonNull(resultVar.getName());
|
||||
PsiDeclarationStatement declaration =
|
||||
myFactory.createVariableDeclarationStatement(name, resultVar.getType(), assignment.getRExpression());
|
||||
declaration = (PsiDeclarationStatement)assignment.getParent().replace(declaration);
|
||||
resultVar.getParent().delete();
|
||||
resultVar = (PsiVariable)declaration.getDeclaredElements()[0];
|
||||
|
||||
PsiElement parentStatement = RefactoringUtil.getParentStatement(resultUsage, true);
|
||||
PsiElement next = declaration.getNextSibling();
|
||||
boolean canInline = false;
|
||||
while (true) {
|
||||
if (next == null) break;
|
||||
if (next.equals(parentStatement)) {
|
||||
canInline = true;
|
||||
break;
|
||||
}
|
||||
if (next instanceof PsiStatement) break;
|
||||
next = next.getNextSibling();
|
||||
}
|
||||
|
||||
if (canInline) {
|
||||
InlineUtil.inlineVariable(resultVar, resultVar.getInitializer(), resultUsage);
|
||||
declaration.delete();
|
||||
thisVar.getInitializer().replace(qualifier);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,393 @@
|
||||
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.intellij.refactoring.inline;
|
||||
|
||||
import com.intellij.codeInsight.BlockUtils;
|
||||
import com.intellij.codeInsight.ChangeContextUtil;
|
||||
import com.intellij.codeInsight.editorActions.DeclarationJoinLinesHandler;
|
||||
import com.intellij.openapi.util.Ref;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.psi.util.PsiUtil;
|
||||
import com.intellij.refactoring.BaseRefactoringProcessor;
|
||||
import com.intellij.refactoring.RefactoringBundle;
|
||||
import com.intellij.refactoring.util.CommonRefactoringUtil;
|
||||
import com.intellij.refactoring.util.InlineUtil;
|
||||
import com.intellij.refactoring.util.RefactoringUIUtil;
|
||||
import com.intellij.refactoring.util.RefactoringUtil;
|
||||
import com.intellij.usageView.UsageInfo;
|
||||
import com.intellij.usageView.UsageViewDescriptor;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import com.intellij.util.containers.MultiMap;
|
||||
import com.siyeh.ig.psiutils.*;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import static com.intellij.util.ObjectUtils.tryCast;
|
||||
|
||||
/**
|
||||
* Performs inlining of object construction together with a subsequent call.
|
||||
* E.g. {@code new Point(12, 34).getX()} could be inlined to {@code 12}.
|
||||
*/
|
||||
public class InlineObjectProcessor extends BaseRefactoringProcessor {
|
||||
private final PsiMethod myMethod;
|
||||
private final PsiReference myReference;
|
||||
private final PsiNewExpression myNewExpression;
|
||||
private final PsiMethodCallExpression myNextCall;
|
||||
private final PsiMethod myNextMethod;
|
||||
|
||||
private InlineObjectProcessor(PsiMethod method, PsiReference reference) {
|
||||
super(method.getProject());
|
||||
myMethod = method;
|
||||
myReference = reference;
|
||||
PsiElement element = myReference.getElement();
|
||||
myNewExpression = tryCast(element.getParent(), PsiNewExpression.class);
|
||||
assert myNewExpression != null;
|
||||
myNextCall = ExpressionUtils.getCallForQualifier(myNewExpression);
|
||||
assert myNextCall != null;
|
||||
PsiMethod nextMethod = myNextCall.resolveMethod();
|
||||
assert nextMethod != null;
|
||||
PsiElement nav = nextMethod.getNavigationElement();
|
||||
if (nav instanceof PsiMethod) {
|
||||
nextMethod = (PsiMethod)nav;
|
||||
}
|
||||
myNextMethod = nextMethod;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
protected UsageViewDescriptor createUsageViewDescriptor(@NotNull UsageInfo[] usages) {
|
||||
return new InlineViewDescriptor(myMethod);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
protected Collection<? extends PsiElement> getElementsToWrite(@NotNull UsageViewDescriptor descriptor) {
|
||||
return Collections.singletonList(myReference.getElement());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
protected UsageInfo[] findUsages() {
|
||||
return new UsageInfo[]{new UsageInfo(myReference)};
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void performRefactoring(@NotNull UsageInfo[] usages) {
|
||||
ChangeContextUtil.encodeContextInfo(myMethod, false);
|
||||
PsiMethod ctorCopy = (PsiMethod)myMethod.copy();
|
||||
ChangeContextUtil.clearContextInfo(myMethod);
|
||||
ChangeContextUtil.encodeContextInfo(myNextMethod, false);
|
||||
PsiMethod nextCopy = (PsiMethod)myNextMethod.copy();
|
||||
ChangeContextUtil.clearContextInfo(myNextMethod);
|
||||
InlineMethodHelper ctorHelper = new InlineMethodHelper(myProject, myMethod, ctorCopy, myNewExpression);
|
||||
InlineMethodHelper nextHelper = new InlineMethodHelper(myProject, myNextMethod, nextCopy, myNextCall);
|
||||
PsiClass aClass = myMethod.getContainingClass();
|
||||
assert aClass != null;
|
||||
PsiElementFactory factory = JavaPsiFacade.getElementFactory(myProject);
|
||||
PsiCodeBlock target = factory.createCodeBlock();
|
||||
List<PsiLocalVariable> fieldLocals = new ArrayList<>();
|
||||
for (PsiField field : aClass.getFields()) {
|
||||
if (!field.hasModifierProperty(PsiModifier.STATIC)) {
|
||||
PsiDeclarationStatement declaration =
|
||||
factory.createVariableDeclarationStatement(field.getName(), field.getType(), field.getInitializer(), aClass);
|
||||
fieldLocals.add((PsiLocalVariable)((PsiDeclarationStatement)target.add(declaration)).getDeclaredElements()[0]);
|
||||
}
|
||||
}
|
||||
PsiLocalVariable[] ctorParameters = ctorHelper.declareParameters();
|
||||
InlineTransformer ctorTransformer = InlineTransformer.getSuitableTransformer(myMethod).apply(myReference);
|
||||
ctorTransformer.transformBody(ctorCopy, myReference, PsiType.VOID);
|
||||
PsiCodeBlock ctorBody = Objects.requireNonNull(ctorCopy.getBody());
|
||||
InlineUtil.solveVariableNameConflicts(ctorBody, target, ctorBody);
|
||||
updateFieldRefs(ctorCopy, aClass);
|
||||
ctorParameters = addRange(target, ctorBody, ctorParameters);
|
||||
|
||||
PsiLocalVariable[] nextParameters = nextHelper.declareParameters();
|
||||
InlineTransformer nextTransformer = InlineTransformer.getSuitableTransformer(myNextMethod).apply(myNextCall.getMethodExpression());
|
||||
PsiLocalVariable result = nextTransformer.transformBody(nextCopy, myNextCall.getMethodExpression(), myNextCall.getType());
|
||||
PsiCodeBlock nextBody = Objects.requireNonNull(nextCopy.getBody());
|
||||
InlineUtil.solveVariableNameConflicts(nextBody, target, nextBody);
|
||||
updateFieldRefs(nextCopy, aClass);
|
||||
if (result != null) {
|
||||
PsiLocalVariable[] resultAndParameters = ArrayUtil.prepend(result, nextParameters);
|
||||
resultAndParameters = addRange(target, nextBody, resultAndParameters);
|
||||
result = resultAndParameters[0];
|
||||
nextParameters = Arrays.copyOfRange(resultAndParameters, 1, resultAndParameters.length);
|
||||
}
|
||||
else {
|
||||
nextParameters = addRange(target, nextBody, nextParameters);
|
||||
}
|
||||
|
||||
InlineUtil.solveVariableNameConflicts(target, myReference.getElement(), target);
|
||||
ctorHelper.initializeParameters(ctorParameters);
|
||||
nextHelper.initializeParameters(nextParameters);
|
||||
|
||||
removeRedundantFieldVars(fieldLocals, target);
|
||||
ctorHelper.inlineParameters(ctorParameters);
|
||||
nextHelper.inlineParameters(nextParameters);
|
||||
|
||||
PsiElement anchor = RefactoringUtil.getParentStatement(myNextCall, true);
|
||||
assert anchor != null;
|
||||
PsiElement anchorParent = anchor.getParent();
|
||||
PsiStatement[] statements = target.getStatements();
|
||||
PsiElement firstBodyElement = target.getFirstBodyElement();
|
||||
if (firstBodyElement instanceof PsiWhiteSpace) firstBodyElement = PsiTreeUtil.skipWhitespacesForward(firstBodyElement);
|
||||
PsiElement firstAdded = null;
|
||||
if (firstBodyElement != null && firstBodyElement != target.getRBrace()) {
|
||||
int last = statements.length - 1;
|
||||
|
||||
final PsiElement rBraceOrReturnStatement =
|
||||
last >= 0 ? PsiTreeUtil.skipWhitespacesAndCommentsForward(statements[last]) : target.getLastBodyElement();
|
||||
assert rBraceOrReturnStatement != null;
|
||||
final PsiElement beforeRBraceStatement = rBraceOrReturnStatement.getPrevSibling();
|
||||
assert beforeRBraceStatement != null;
|
||||
|
||||
firstAdded = anchorParent.addRangeBefore(firstBodyElement, beforeRBraceStatement, anchor);
|
||||
ChangeContextUtil.decodeContextInfo(anchorParent, null, null);
|
||||
}
|
||||
|
||||
PsiReferenceExpression resultUsage = InlineMethodProcessor.replaceCall(factory, myNextCall, firstAdded, result);
|
||||
if (resultUsage != null) {
|
||||
PsiLocalVariable resultVar = ExpressionUtils.resolveLocalVariable(resultUsage);
|
||||
if (resultVar != null) {
|
||||
InlineUtil.tryInlineResultVariable(resultVar, resultUsage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void removeRedundantFieldVars(List<PsiLocalVariable> vars, PsiCodeBlock block) {
|
||||
for (PsiLocalVariable var : vars) {
|
||||
List<PsiReferenceExpression> references = VariableAccessUtils.getVariableReferences(var, block);
|
||||
PsiAssignmentExpression firstAssignment = null;
|
||||
List<PsiAssignmentExpression> assignments = new ArrayList<>();
|
||||
for (PsiReferenceExpression reference : references) {
|
||||
PsiAssignmentExpression assignment = tryCast(PsiUtil.skipParenthesizedExprUp(reference.getParent()), PsiAssignmentExpression.class);
|
||||
if (assignment != null && assignment.getOperationTokenType().equals(JavaTokenType.EQ) &&
|
||||
PsiUtil.skipParenthesizedExprDown(assignment.getLExpression()) == reference &&
|
||||
assignment.getParent() instanceof PsiExpressionStatement) {
|
||||
assignments.add(assignment);
|
||||
if (firstAssignment == null && assignment.getParent().getParent() == block) {
|
||||
firstAssignment = assignment;
|
||||
}
|
||||
}
|
||||
else {
|
||||
assignments = null;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (assignments != null) {
|
||||
for (PsiAssignmentExpression assignment : assignments) {
|
||||
PsiExpressionStatement statement = (PsiExpressionStatement)assignment.getParent();
|
||||
PsiExpression expression = assignment.getRExpression();
|
||||
CommentTracker ct = new CommentTracker();
|
||||
if (expression != null) {
|
||||
List<PsiExpression> sideEffects = SideEffectChecker.extractSideEffectExpressions(expression);
|
||||
sideEffects.forEach(ct::markUnchanged);
|
||||
PsiStatement[] statements = StatementExtractor.generateStatements(sideEffects, expression);
|
||||
if (statements.length > 0) {
|
||||
BlockUtils.addBefore(statement, statements);
|
||||
}
|
||||
}
|
||||
ct.deleteAndRestoreComments(statement);
|
||||
}
|
||||
new CommentTracker().deleteAndRestoreComments(var);
|
||||
}
|
||||
else if (firstAssignment != null) {
|
||||
var = DeclarationJoinLinesHandler.joinDeclarationAndAssignment(var, firstAssignment);
|
||||
InlineUtil.tryInlineGeneratedLocal(var, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static PsiLocalVariable[] addRange(PsiCodeBlock target, PsiCodeBlock body, PsiLocalVariable[] declaredVars) {
|
||||
PsiElement firstBodyElement = body.getFirstBodyElement();
|
||||
PsiElement lastBodyElement = body.getLastBodyElement();
|
||||
if (firstBodyElement == null || lastBodyElement == null) return declaredVars;
|
||||
PsiElement firstAdded = target.addRange(firstBodyElement, lastBodyElement);
|
||||
PsiLocalVariable[] updatedVars = new PsiLocalVariable[declaredVars.length];
|
||||
int index = 0;
|
||||
for (PsiElement e = firstAdded; index < updatedVars.length && e != null; e = e.getNextSibling()) {
|
||||
if (e instanceof PsiDeclarationStatement) {
|
||||
PsiElement[] elements = ((PsiDeclarationStatement)e).getDeclaredElements();
|
||||
if (elements.length == 1) {
|
||||
PsiLocalVariable var = tryCast(elements[0], PsiLocalVariable.class);
|
||||
if (var != null) {
|
||||
if (var.getName().equals(declaredVars[index].getName())) {
|
||||
updatedVars[index++] = var;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
assert index == updatedVars.length;
|
||||
return updatedVars;
|
||||
}
|
||||
|
||||
private static void updateFieldRefs(PsiMethod method, PsiClass aClass) {
|
||||
PsiCodeBlock body = method.getBody();
|
||||
assert body != null;
|
||||
for (PsiThisExpression thisExpression : PsiTreeUtil.findChildrenOfType(body, PsiThisExpression.class)) {
|
||||
PsiElement parent = PsiUtil.skipParenthesizedExprUp(thisExpression.getParent());
|
||||
if (parent instanceof PsiReferenceExpression) {
|
||||
PsiField field = tryCast(((PsiReferenceExpression)parent).resolve(), PsiField.class);
|
||||
if (field != null && field.getContainingClass() == aClass) {
|
||||
thisExpression.delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean preprocessUsages(@NotNull Ref<UsageInfo[]> refUsages) {
|
||||
final UsageInfo[] usagesIn = refUsages.get();
|
||||
final MultiMap<PsiElement, String> conflicts = new MultiMap<>();
|
||||
final ReferencedElementsCollector collector = new ReferencedElementsCollector();
|
||||
myMethod.accept(collector);
|
||||
myNextMethod.accept(collector);
|
||||
|
||||
final Map<PsiMember, Set<PsiMember>> containersToReferenced = InlineMethodProcessor
|
||||
.getInaccessible(collector.myReferencedMembers, usagesIn, myMethod);
|
||||
|
||||
containersToReferenced.forEach((container, referencedInaccessible) -> {
|
||||
for (PsiMember referenced : referencedInaccessible) {
|
||||
if (referenced instanceof PsiField && !referenced.hasModifierProperty(PsiModifier.STATIC) &&
|
||||
referenced.getContainingClass() == myMethod.getContainingClass()) {
|
||||
// Instance fields will be inlined
|
||||
continue;
|
||||
}
|
||||
final String referencedDescription = RefactoringUIUtil.getDescription(referenced, true);
|
||||
final String containerDescription = RefactoringUIUtil.getDescription(container, true);
|
||||
String message = RefactoringBundle.message("0.that.is.used.in.inlined.method.is.not.accessible.from.call.site.s.in.1",
|
||||
referencedDescription, containerDescription);
|
||||
conflicts.putValue(container, CommonRefactoringUtil.capitalize(message));
|
||||
}
|
||||
});
|
||||
return showConflicts(conflicts, usagesIn);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
protected String getCommandName() {
|
||||
return "Inline Object";
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static InlineObjectProcessor create(PsiReference reference, PsiMethod method) {
|
||||
if (!canInlineConstructorAndChainCall(reference, method)) {
|
||||
return null;
|
||||
}
|
||||
return new InlineObjectProcessor(method, reference);
|
||||
}
|
||||
|
||||
@Contract("null, _ -> false")
|
||||
private static boolean canInlineConstructorAndChainCall(PsiReference reference, PsiMethod method) {
|
||||
if (reference == null) return false;
|
||||
PsiElement element = reference.getElement();
|
||||
if (!(element instanceof PsiJavaCodeReferenceElement)) return false;
|
||||
PsiNewExpression expression = tryCast(element.getParent(), PsiNewExpression.class);
|
||||
if (expression == null) return false;
|
||||
PsiMethodCallExpression call = ExpressionUtils.getCallForQualifier(expression);
|
||||
if (call == null) return false;
|
||||
if (RefactoringUtil.getParentStatement(call, true) == null) return false;
|
||||
PsiMethod nextMethod = call.resolveMethod();
|
||||
if (nextMethod == null) return false;
|
||||
PsiElement nav = nextMethod.getNavigationElement();
|
||||
if (nav instanceof PsiMethod) {
|
||||
nextMethod = (PsiMethod)nav;
|
||||
}
|
||||
PsiClass aClass = method.getContainingClass();
|
||||
if (aClass == null) return false;
|
||||
if (aClass.getContainingClass() != null && !aClass.hasModifierProperty(PsiModifier.STATIC)) return false;
|
||||
|
||||
PsiClassType[] supers = aClass.getExtendsListTypes();
|
||||
if (supers.length > 1) return false;
|
||||
if (supers.length == 1 && !isStatelessSuperClass(supers[0], new HashSet<>())) return false;
|
||||
for (PsiField field : aClass.getFields()) {
|
||||
if (!field.hasModifierProperty(PsiModifier.STATIC)) {
|
||||
PsiExpression initializer = field.getInitializer();
|
||||
if (initializer != null && mayLeakThis(initializer)) return false;
|
||||
}
|
||||
}
|
||||
for (PsiClassInitializer initializer : aClass.getInitializers()) {
|
||||
if (!initializer.hasModifierProperty(PsiModifier.STATIC)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return !mayLeakThis(method) && !mayLeakThis(nextMethod);
|
||||
}
|
||||
|
||||
private static boolean isStatelessSuperClass(PsiClassType psiType, Set<PsiClass> checked) {
|
||||
if (TypeUtils.isJavaLangObject(psiType)) return true;
|
||||
PsiClass psiClass = psiType.resolve();
|
||||
if (psiClass == null || !checked.add(psiClass)) return false;
|
||||
PsiMethod[] constructors = psiClass.getConstructors();
|
||||
for (PsiMethod constructor : constructors) {
|
||||
if (constructor.getParameterList().isEmpty()) {
|
||||
PsiElement nav = constructor.getNavigationElement();
|
||||
if (nav instanceof PsiMethod) {
|
||||
constructor = (PsiMethod)nav;
|
||||
}
|
||||
PsiCodeBlock body = constructor.getBody();
|
||||
if (body == null || !ControlFlowUtils.isEmptyCodeBlock(body)) return false;
|
||||
}
|
||||
}
|
||||
for (PsiField field : psiClass.getFields()) {
|
||||
if (!field.hasModifierProperty(PsiModifier.STATIC)) return false;
|
||||
}
|
||||
PsiClassType[] supers = psiClass.getExtendsListTypes();
|
||||
return supers.length == 0 || supers.length == 1 && isStatelessSuperClass(supers[0], checked);
|
||||
}
|
||||
|
||||
private static boolean mayLeakThis(PsiMethod method) {
|
||||
if (method == null) return true;
|
||||
PsiCodeBlock body = method.getBody();
|
||||
if (body == null) return true;
|
||||
return mayLeakThis(body);
|
||||
}
|
||||
|
||||
private static boolean mayLeakThis(PsiElement body) {
|
||||
class Visitor extends JavaRecursiveElementWalkingVisitor {
|
||||
boolean leak = false;
|
||||
|
||||
@Override
|
||||
public void visitMethodCallExpression(PsiMethodCallExpression call) {
|
||||
super.visitMethodCallExpression(call);
|
||||
PsiExpression qualifier = ExpressionUtils.getEffectiveQualifier(call.getMethodExpression());
|
||||
if (qualifier instanceof PsiThisExpression || qualifier instanceof PsiSuperExpression) {
|
||||
leak = true;
|
||||
stopWalking();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitNewExpression(PsiNewExpression expression) {
|
||||
super.visitNewExpression(expression);
|
||||
if (expression.getQualifier() == null) {
|
||||
PsiJavaCodeReferenceElement reference = expression.getClassReference();
|
||||
if (reference != null) {
|
||||
PsiClass target = tryCast(reference.resolve(), PsiClass.class);
|
||||
if (target != null && target.getContainingClass() != null && !target.hasModifierProperty(PsiModifier.STATIC)) {
|
||||
leak = true;
|
||||
stopWalking();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitThisExpression(PsiThisExpression expression) {
|
||||
super.visitThisExpression(expression);
|
||||
PsiElement parent = PsiUtil.skipParenthesizedExprUp(expression.getParent());
|
||||
if (!(parent instanceof PsiReferenceExpression)) {
|
||||
leak = true;
|
||||
stopWalking();
|
||||
}
|
||||
}
|
||||
}
|
||||
Visitor visitor = new Visitor();
|
||||
body.accept(visitor);
|
||||
return visitor.leak;
|
||||
}
|
||||
}
|
||||
@@ -31,7 +31,7 @@ public interface InlineTransformer {
|
||||
* @param returnType substituted method return type
|
||||
* @return result variable or null if unnecessary
|
||||
*/
|
||||
PsiLocalVariable transformBody(PsiMethod methodCopy, PsiReferenceExpression callSite, PsiType returnType);
|
||||
PsiLocalVariable transformBody(PsiMethod methodCopy, PsiReference callSite, PsiType returnType);
|
||||
|
||||
/**
|
||||
* @return true if this transformer is a fallback transformer which may significantly rewrite the method body
|
||||
@@ -43,9 +43,11 @@ public interface InlineTransformer {
|
||||
class NormalTransformer implements InlineTransformer {
|
||||
|
||||
@Override
|
||||
public PsiLocalVariable transformBody(PsiMethod methodCopy, PsiReferenceExpression callSite, PsiType returnType) {
|
||||
public PsiLocalVariable transformBody(PsiMethod methodCopy, PsiReference callSite, PsiType returnType) {
|
||||
if (returnType == null || PsiType.VOID.equals(returnType) ||
|
||||
callSite.getParent() instanceof PsiMethodCallExpression && ExpressionUtils.isVoidContext((PsiExpression)callSite.getParent())) {
|
||||
callSite.getElement().getParent() instanceof PsiMethodCallExpression &&
|
||||
ExpressionUtils.isVoidContext((PsiExpression)callSite.getElement().getParent())) {
|
||||
|
||||
InlineUtil.extractReturnValues(methodCopy, false);
|
||||
return null;
|
||||
}
|
||||
@@ -78,8 +80,10 @@ public interface InlineTransformer {
|
||||
}
|
||||
|
||||
@Override
|
||||
public PsiLocalVariable transformBody(PsiMethod methodCopy, PsiReferenceExpression callSite, PsiType returnType) {
|
||||
if (callSite.getParent() instanceof PsiMethodCallExpression && ExpressionUtils.isVoidContext((PsiExpression)callSite.getParent())) {
|
||||
public PsiLocalVariable transformBody(PsiMethod methodCopy, PsiReference callSite, PsiType returnType) {
|
||||
if (callSite.getElement().getParent() instanceof PsiMethodCallExpression &&
|
||||
ExpressionUtils.isVoidContext((PsiExpression)callSite.getElement().getParent())) {
|
||||
|
||||
InlineUtil.extractReturnValues(methodCopy, false);
|
||||
returnType = PsiType.VOID;
|
||||
}
|
||||
|
||||
@@ -3,15 +3,19 @@ package com.intellij.refactoring.util;
|
||||
|
||||
import com.intellij.codeInsight.BlockUtils;
|
||||
import com.intellij.codeInsight.ChangeContextUtil;
|
||||
import com.intellij.codeInsight.daemon.impl.analysis.HighlightControlFlowUtil;
|
||||
import com.intellij.codeInsight.daemon.impl.quickfix.SimplifyBooleanExpressionFix;
|
||||
import com.intellij.codeInspection.redundantCast.RemoveRedundantCastUtil;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.progress.ProgressManager;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.Comparing;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.codeStyle.JavaCodeStyleManager;
|
||||
import com.intellij.psi.controlFlow.*;
|
||||
import com.intellij.psi.impl.source.resolve.graphInference.PsiPolyExpressionUtil;
|
||||
import com.intellij.psi.search.GlobalSearchScope;
|
||||
import com.intellij.psi.search.LocalSearchScope;
|
||||
import com.intellij.psi.search.searches.ReferencesSearch;
|
||||
import com.intellij.psi.tree.IElementType;
|
||||
@@ -21,6 +25,7 @@ import com.intellij.psi.util.RedundantCastUtil;
|
||||
import com.intellij.refactoring.RefactoringBundle;
|
||||
import com.intellij.refactoring.inline.InlineTransformer;
|
||||
import com.intellij.util.IncorrectOperationException;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.intellij.util.containers.MultiMap;
|
||||
import com.siyeh.ig.psiutils.*;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -28,6 +33,8 @@ import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import static com.intellij.util.ObjectUtils.tryCast;
|
||||
|
||||
/**
|
||||
* @author ven
|
||||
*/
|
||||
@@ -532,12 +539,368 @@ public class InlineUtil {
|
||||
}
|
||||
if (ControlFlowUtils.blockCompletesWithStatement(block, returnStatement)) {
|
||||
new CommentTracker().deleteAndRestoreComments(returnStatement);
|
||||
} else if (replaceWithContinue) {
|
||||
}
|
||||
else if (replaceWithContinue) {
|
||||
new CommentTracker().replaceAndRestoreComments(returnStatement, "continue;");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static PsiExpression inlineInitializer(PsiVariable variable, PsiExpression initializer, PsiJavaCodeReferenceElement ref) {
|
||||
Project project = variable.getProject();
|
||||
PsiElementFactory factory = JavaPsiFacade.getElementFactory(project);
|
||||
if (initializer instanceof PsiThisExpression && ((PsiThisExpression)initializer).getQualifier() == null) {
|
||||
final PsiClass varThisClass = RefactoringChangeUtil.getThisClass(variable);
|
||||
if (varThisClass != null && varThisClass != RefactoringChangeUtil.getThisClass(ref)) {
|
||||
initializer = factory.createExpressionFromText(varThisClass.getName() + ".this", variable);
|
||||
}
|
||||
}
|
||||
|
||||
PsiExpression expr = inlineVariable(variable, initializer, ref);
|
||||
|
||||
tryToInlineArrayCreationForVarargs(expr);
|
||||
|
||||
//Q: move the following code to some util? (addition to inline?)
|
||||
if (expr instanceof PsiThisExpression) {
|
||||
if (expr.getParent() instanceof PsiReferenceExpression) {
|
||||
PsiReferenceExpression refExpr = (PsiReferenceExpression)expr.getParent();
|
||||
PsiElement refElement = refExpr.resolve();
|
||||
PsiExpression exprCopy = (PsiExpression)refExpr.copy();
|
||||
refExpr = (PsiReferenceExpression)refExpr.replace(factory.createExpressionFromText(
|
||||
Objects.requireNonNull(refExpr.getReferenceName()), null));
|
||||
if (refElement != null) {
|
||||
PsiElement newRefElement = refExpr.resolve();
|
||||
if (!refElement.equals(newRefElement)) {
|
||||
// change back
|
||||
refExpr.replace(exprCopy);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (expr instanceof PsiLiteralExpression && PsiType.BOOLEAN.equals(expr.getType())) {
|
||||
Boolean value = tryCast(((PsiLiteralExpression)expr).getValue(), Boolean.class);
|
||||
if (value != null) {
|
||||
SimplifyBooleanExpressionFix fix = new SimplifyBooleanExpressionFix(expr, value);
|
||||
if (fix.isAvailable()) {
|
||||
fix.invoke(project, expr.getContainingFile(), expr, expr);
|
||||
}
|
||||
}
|
||||
}
|
||||
return initializer;
|
||||
}
|
||||
|
||||
public static boolean canInlineParameterOrThisVariable(PsiLocalVariable variable) {
|
||||
PsiElement block = PsiUtil.getVariableCodeBlock(variable, null);
|
||||
if (block == null) return false;
|
||||
List<PsiReferenceExpression> refs = VariableAccessUtils.getVariableReferences(variable, block);
|
||||
boolean isAccessedForWriting = false;
|
||||
for (PsiReferenceExpression refElement : refs) {
|
||||
if (PsiUtil.isAccessedForWriting(refElement)) {
|
||||
isAccessedForWriting = true;
|
||||
}
|
||||
}
|
||||
|
||||
PsiExpression initializer = variable.getInitializer();
|
||||
boolean shouldBeFinal = variable.hasModifierProperty(PsiModifier.FINAL) && false;
|
||||
return canInlineParameterOrThisVariable(variable.getProject(), initializer, shouldBeFinal, false,
|
||||
refs.size(), isAccessedForWriting);
|
||||
}
|
||||
|
||||
private static boolean canInlineParameterOrThisVariable(Project project,
|
||||
PsiExpression initializer,
|
||||
boolean shouldBeFinal,
|
||||
boolean strictlyFinal,
|
||||
int accessCount,
|
||||
boolean isAccessedForWriting) {
|
||||
if (strictlyFinal) {
|
||||
class CanAllLocalsBeDeclaredFinal extends JavaRecursiveElementWalkingVisitor {
|
||||
boolean success = true;
|
||||
|
||||
@Override
|
||||
public void visitReferenceExpression(PsiReferenceExpression expression) {
|
||||
final PsiElement psiElement = expression.resolve();
|
||||
if (psiElement instanceof PsiLocalVariable || psiElement instanceof PsiParameter) {
|
||||
if (!RefactoringUtil.canBeDeclaredFinal((PsiVariable)psiElement)) {
|
||||
success = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitElement(PsiElement element) {
|
||||
if (success) {
|
||||
super.visitElement(element);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final CanAllLocalsBeDeclaredFinal canAllLocalsBeDeclaredFinal = new CanAllLocalsBeDeclaredFinal();
|
||||
initializer.accept(canAllLocalsBeDeclaredFinal);
|
||||
if (!canAllLocalsBeDeclaredFinal.success) return false;
|
||||
}
|
||||
if (initializer instanceof PsiFunctionalExpression) return accessCount <= 1;
|
||||
if (initializer instanceof PsiReferenceExpression) {
|
||||
PsiVariable refVar = (PsiVariable)((PsiReferenceExpression)initializer).resolve();
|
||||
if (refVar == null) {
|
||||
return !isAccessedForWriting;
|
||||
}
|
||||
if (refVar instanceof PsiField) {
|
||||
if (isAccessedForWriting) return false;
|
||||
if (refVar.hasModifierProperty(PsiModifier.VOLATILE)) return accessCount <= 1;
|
||||
/*
|
||||
PsiField field = (PsiField)refVar;
|
||||
if (isFieldNonModifiable(field)){
|
||||
return true;
|
||||
}
|
||||
//TODO: other cases
|
||||
return false;
|
||||
*/
|
||||
return true; //TODO: "suspicious" places to review by user!
|
||||
}
|
||||
else {
|
||||
if (isAccessedForWriting) {
|
||||
if (refVar.hasModifierProperty(PsiModifier.FINAL) || shouldBeFinal) return false;
|
||||
PsiReference[] refs = ReferencesSearch.search(refVar, GlobalSearchScope.projectScope(project), false)
|
||||
.toArray(PsiReference.EMPTY_ARRAY);
|
||||
return refs.length == 1; //TODO: control flow
|
||||
}
|
||||
else {
|
||||
if (shouldBeFinal) {
|
||||
return refVar.hasModifierProperty(PsiModifier.FINAL) || RefactoringUtil.canBeDeclaredFinal(refVar);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (isAccessedForWriting) {
|
||||
return false;
|
||||
}
|
||||
else if (initializer instanceof PsiCallExpression) {
|
||||
if (accessCount != 1) return false;//don't allow deleting probable side effects or multiply those side effects
|
||||
if (initializer instanceof PsiNewExpression) {
|
||||
final PsiArrayInitializerExpression arrayInitializer = ((PsiNewExpression)initializer).getArrayInitializer();
|
||||
if (arrayInitializer != null) {
|
||||
for (PsiExpression expression : arrayInitializer.getInitializers()) {
|
||||
if (!canInlineParameterOrThisVariable(project, expression, shouldBeFinal, strictlyFinal, accessCount, false)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
final PsiExpressionList argumentList = ((PsiCallExpression)initializer).getArgumentList();
|
||||
if (argumentList == null) return false;
|
||||
final PsiExpression[] expressions = argumentList.getExpressions();
|
||||
for (PsiExpression expression : expressions) {
|
||||
if (!canInlineParameterOrThisVariable(project, expression, shouldBeFinal, strictlyFinal, accessCount, false)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true; //TODO: "suspicious" places to review by user!
|
||||
}
|
||||
else if (initializer instanceof PsiLiteralExpression) {
|
||||
return true;
|
||||
}
|
||||
else if (initializer instanceof PsiPrefixExpression &&
|
||||
((PsiPrefixExpression)initializer).getOperand() instanceof PsiLiteralExpression) {
|
||||
return true;
|
||||
}
|
||||
else if (initializer instanceof PsiArrayAccessExpression) {
|
||||
final PsiExpression arrayExpression = ((PsiArrayAccessExpression)initializer).getArrayExpression();
|
||||
final PsiExpression indexExpression = ((PsiArrayAccessExpression)initializer).getIndexExpression();
|
||||
return canInlineParameterOrThisVariable(project, arrayExpression, shouldBeFinal, strictlyFinal, accessCount, false) &&
|
||||
canInlineParameterOrThisVariable(project, indexExpression, shouldBeFinal, strictlyFinal, accessCount, false);
|
||||
}
|
||||
else if (initializer instanceof PsiParenthesizedExpression) {
|
||||
PsiExpression expr = ((PsiParenthesizedExpression)initializer).getExpression();
|
||||
return expr == null || canInlineParameterOrThisVariable(project, expr, shouldBeFinal, strictlyFinal, accessCount, false);
|
||||
}
|
||||
else if (initializer instanceof PsiTypeCastExpression) {
|
||||
PsiExpression operand = ((PsiTypeCastExpression)initializer).getOperand();
|
||||
return operand != null && canInlineParameterOrThisVariable(project, operand, shouldBeFinal, strictlyFinal, accessCount, false);
|
||||
}
|
||||
else if (initializer instanceof PsiPolyadicExpression) {
|
||||
PsiPolyadicExpression binExpr = (PsiPolyadicExpression)initializer;
|
||||
for (PsiExpression op : binExpr.getOperands()) {
|
||||
if (!canInlineParameterOrThisVariable(project, op, shouldBeFinal, strictlyFinal, accessCount, false)) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
else if (initializer instanceof PsiClassObjectAccessExpression) {
|
||||
return true;
|
||||
}
|
||||
else if (initializer instanceof PsiThisExpression) {
|
||||
return true;
|
||||
}
|
||||
else if (initializer instanceof PsiSuperExpression) {
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to inline local variable which was generated during method inlining (e.g. to hold a parameter or this reference)
|
||||
*
|
||||
* @param variable variable to inline
|
||||
* @param strictlyFinal whether the variable is referenced in the places where final variable is required
|
||||
* @throws IncorrectOperationException
|
||||
*/
|
||||
public static void tryInlineGeneratedLocal(PsiLocalVariable variable, boolean strictlyFinal)
|
||||
throws IncorrectOperationException {
|
||||
PsiElement scope = PsiUtil.getVariableCodeBlock(variable, null);
|
||||
if (scope == null) return;
|
||||
List<PsiReferenceExpression> refs = VariableAccessUtils.getVariableReferences(variable, scope);
|
||||
PsiReferenceExpression firstRef = ContainerUtil.getFirstItem(refs);
|
||||
|
||||
PsiExpression initializer = variable.getInitializer();
|
||||
if (firstRef == null) {
|
||||
PsiDeclarationStatement declaration = (PsiDeclarationStatement)variable.getParent();
|
||||
if (initializer != null) {
|
||||
List<PsiExpression> sideEffects = SideEffectChecker.extractSideEffectExpressions(initializer);
|
||||
for (PsiStatement statement : StatementExtractor.generateStatements(sideEffects, initializer)) {
|
||||
declaration.getParent().addBefore(statement, declaration);
|
||||
}
|
||||
}
|
||||
declaration.delete();
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
boolean isAccessedForWriting = false;
|
||||
for (PsiReferenceExpression refElement : refs) {
|
||||
if (PsiUtil.isAccessedForWriting(refElement)) {
|
||||
isAccessedForWriting = true;
|
||||
}
|
||||
}
|
||||
|
||||
boolean shouldBeFinal = variable.hasModifierProperty(PsiModifier.FINAL) && strictlyFinal;
|
||||
Project project = variable.getProject();
|
||||
if (canInlineParameterOrThisVariable(project, initializer, shouldBeFinal, strictlyFinal, refs.size(), isAccessedForWriting)) {
|
||||
if (shouldBeFinal) {
|
||||
declareUsedLocalsFinal(initializer, true);
|
||||
}
|
||||
for (PsiReference ref : refs) {
|
||||
initializer = inlineInitializer(variable, initializer, (PsiJavaCodeReferenceElement)ref);
|
||||
}
|
||||
variable.getParent().delete();
|
||||
}
|
||||
}
|
||||
|
||||
private static void declareUsedLocalsFinal(PsiElement expr, boolean strictlyFinal) throws IncorrectOperationException {
|
||||
if (expr instanceof PsiReferenceExpression) {
|
||||
PsiElement refElement = ((PsiReferenceExpression)expr).resolve();
|
||||
if (refElement instanceof PsiLocalVariable || refElement instanceof PsiParameter) {
|
||||
if (strictlyFinal || RefactoringUtil.canBeDeclaredFinal((PsiVariable)refElement)) {
|
||||
PsiUtil.setModifierProperty(((PsiVariable)refElement), PsiModifier.FINAL, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
PsiElement[] children = expr.getChildren();
|
||||
for (PsiElement child : children) {
|
||||
declareUsedLocalsFinal(child, strictlyFinal);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to inline the result variable after method inlining
|
||||
*
|
||||
* @param resultVar variable to inline
|
||||
* @param resultUsage variable usage
|
||||
* @throws IncorrectOperationException
|
||||
*/
|
||||
public static void tryInlineResultVariable(@NotNull PsiLocalVariable resultVar, @NotNull PsiReferenceExpression resultUsage)
|
||||
throws IncorrectOperationException {
|
||||
PsiElement context = PsiUtil.getVariableCodeBlock(resultVar, null);
|
||||
if (context == null) return;
|
||||
List<PsiReferenceExpression> references = VariableAccessUtils.getVariableReferences(resultVar, context);
|
||||
if (resultVar.getInitializer() == null) {
|
||||
PsiAssignmentExpression assignment = null;
|
||||
for (PsiReferenceExpression ref : references) {
|
||||
if (ref.getParent() instanceof PsiAssignmentExpression && ((PsiAssignmentExpression)ref.getParent()).getLExpression().equals(ref)) {
|
||||
if (assignment != null) {
|
||||
assignment = null;
|
||||
break;
|
||||
}
|
||||
else {
|
||||
assignment = (PsiAssignmentExpression)ref.getParent();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (assignment != null) {
|
||||
inlineSingleAssignment(resultVar, assignment, resultUsage);
|
||||
return;
|
||||
}
|
||||
}
|
||||
tryReplaceWithTarget(resultVar, resultUsage, context, references);
|
||||
}
|
||||
|
||||
/**
|
||||
* If result of the method is an initializer of another var, try to reuse that var to store the result.
|
||||
*/
|
||||
private static void tryReplaceWithTarget(@NotNull PsiLocalVariable variable,
|
||||
@NotNull PsiReferenceExpression usage,
|
||||
PsiElement context,
|
||||
List<PsiReferenceExpression> references) {
|
||||
PsiLocalVariable target = tryCast(PsiUtil.skipParenthesizedExprUp(usage.getParent()), PsiLocalVariable.class);
|
||||
if (target == null) return;
|
||||
String name = target.getName();
|
||||
if (!target.getType().equals(variable.getType())) return;
|
||||
PsiDeclarationStatement declaration = tryCast(target.getParent(), PsiDeclarationStatement.class);
|
||||
if (declaration == null || declaration.getDeclaredElements().length != 1) return;
|
||||
PsiModifierList modifiers = target.getModifierList();
|
||||
if (modifiers != null && modifiers.getAnnotations().length != 0) return;
|
||||
boolean effectivelyFinal = HighlightControlFlowUtil.isEffectivelyFinal(variable, context, null);
|
||||
if (!effectivelyFinal && !VariableAccessUtils.canUseAsNonFinal(target)) return;
|
||||
|
||||
for (PsiReferenceExpression reference : references) {
|
||||
ExpressionUtils.bindReferenceTo(reference, name);
|
||||
}
|
||||
if (effectivelyFinal && target.hasModifierProperty(PsiModifier.FINAL)) {
|
||||
PsiModifierList modifierList = variable.getModifierList();
|
||||
if (modifierList != null) {
|
||||
modifierList.setModifierProperty(PsiModifier.FINAL, true);
|
||||
}
|
||||
}
|
||||
variable.setName(name);
|
||||
new CommentTracker().deleteAndRestoreComments(declaration);
|
||||
}
|
||||
|
||||
private static void inlineSingleAssignment(@NotNull PsiVariable resultVar,
|
||||
@NotNull PsiAssignmentExpression assignment,
|
||||
@NotNull PsiReferenceExpression resultUsage) {
|
||||
LOG.assertTrue(assignment.getParent() instanceof PsiExpressionStatement);
|
||||
// SCR3175 fixed: inline only if declaration and assignment is in the same code block.
|
||||
if (!(assignment.getParent().getParent() == resultVar.getParent().getParent())) return;
|
||||
String name = Objects.requireNonNull(resultVar.getName());
|
||||
PsiDeclarationStatement declaration = JavaPsiFacade.getElementFactory(resultVar.getProject())
|
||||
.createVariableDeclarationStatement(name, resultVar.getType(), assignment.getRExpression());
|
||||
declaration = (PsiDeclarationStatement)assignment.getParent().replace(declaration);
|
||||
resultVar.getParent().delete();
|
||||
resultVar = (PsiVariable)declaration.getDeclaredElements()[0];
|
||||
|
||||
PsiElement parentStatement = RefactoringUtil.getParentStatement(resultUsage, true);
|
||||
PsiElement next = declaration.getNextSibling();
|
||||
boolean canInline = false;
|
||||
while (true) {
|
||||
if (next == null) break;
|
||||
if (next.equals(parentStatement)) {
|
||||
canInline = true;
|
||||
break;
|
||||
}
|
||||
if (next instanceof PsiStatement) break;
|
||||
next = next.getNextSibling();
|
||||
}
|
||||
|
||||
if (canInline) {
|
||||
inlineVariable(resultVar, resultVar.getInitializer(), resultUsage);
|
||||
declaration.delete();
|
||||
}
|
||||
}
|
||||
|
||||
public enum TailCallType {
|
||||
None(null),
|
||||
Simple((methodCopy, callSite, returnType) -> {
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
class Main {
|
||||
BitString test(long result, long mask) {
|
||||
BitString intersection = new <caret>BitString(result, mask).intersect(super.getBitwiseMask());
|
||||
assert intersection != null;
|
||||
return intersection;
|
||||
}
|
||||
}
|
||||
|
||||
class BitString {
|
||||
final long myBits;
|
||||
final long myMask;
|
||||
|
||||
BitString(long bits, long mask) {
|
||||
myBits = bits & mask;
|
||||
myMask = mask;
|
||||
}
|
||||
BitString intersect(BitString other) {
|
||||
long intersectMask = myMask & other.myMask;
|
||||
if ((myBits & intersectMask) != (other.myBits & intersectMask)) return null;
|
||||
return new BitString(myBits | other.myBits, myMask | other.myMask);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
class Main {
|
||||
BitString test(long result, long mask) {
|
||||
BitString intersection = null;
|
||||
BitString other = super.getBitwiseMask();
|
||||
long intersectMask = mask & other.myMask;
|
||||
if ((result & mask & intersectMask) == (other.myBits & intersectMask)) {
|
||||
intersection = new BitString(result & mask | other.myBits, mask | other.myMask);
|
||||
}
|
||||
assert intersection != null;
|
||||
return intersection;
|
||||
}
|
||||
}
|
||||
|
||||
class BitString {
|
||||
final long myBits;
|
||||
final long myMask;
|
||||
|
||||
BitString(long bits, long mask) {
|
||||
myBits = bits & mask;
|
||||
myMask = mask;
|
||||
}
|
||||
BitString intersect(BitString other) {
|
||||
long intersectMask = myMask & other.myMask;
|
||||
if ((myBits & intersectMask) != (other.myBits & intersectMask)) return null;
|
||||
return new BitString(myBits | other.myBits, myMask | other.myMask);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
import java.io.File;
|
||||
|
||||
class Main {
|
||||
String getParent(String path) {
|
||||
return new <caret>File(path).getParent();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
import java.io.File;
|
||||
|
||||
class Main {
|
||||
String getParent(String path) {
|
||||
if (path == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
String path1 = fs.normalize(path);
|
||||
int prefixLength = fs.prefixLength(path1);
|
||||
int index = path1.lastIndexOf(File.separatorChar);
|
||||
if (index < prefixLength) {
|
||||
if ((prefixLength > 0) && (path1.length() > prefixLength))
|
||||
return path1.substring(0, prefixLength);
|
||||
return null;
|
||||
}
|
||||
return path1.substring(0, index);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
class Main {
|
||||
void test() {
|
||||
System.out.println(new <caret>Point(1, 2).getX());
|
||||
}
|
||||
}
|
||||
|
||||
class Point {
|
||||
private int x, y;
|
||||
|
||||
public Point(int x, int y) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
|
||||
public int getX() {
|
||||
return x;
|
||||
}
|
||||
|
||||
public int getY() {
|
||||
return y;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
class Main {
|
||||
void test() {
|
||||
System.out.println(1);
|
||||
}
|
||||
}
|
||||
|
||||
class Point {
|
||||
private int x, y;
|
||||
|
||||
public Point(int x, int y) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
|
||||
public int getX() {
|
||||
return x;
|
||||
}
|
||||
|
||||
public int getY() {
|
||||
return y;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
class Main {
|
||||
void test() {
|
||||
System.out.println(new <caret>Point(1, 2).toString());
|
||||
}
|
||||
}
|
||||
|
||||
class Point {
|
||||
private int x, y;
|
||||
|
||||
public Point(int _x, int _y) {
|
||||
x = _x;
|
||||
y = _y;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "["+x+", "+y+"]";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
class Main {
|
||||
void test() {
|
||||
System.out.println("[" + 1 + ", " + 2 + "]");
|
||||
}
|
||||
}
|
||||
|
||||
class Point {
|
||||
private int x, y;
|
||||
|
||||
public Point(int _x, int _y) {
|
||||
x = _x;
|
||||
y = _y;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "["+x+", "+y+"]";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
import java.io.*;
|
||||
|
||||
class Main {
|
||||
void test() {
|
||||
new <caret>Logger(System.out).log("foo");
|
||||
}
|
||||
}
|
||||
|
||||
class Logger {
|
||||
private final PrintStream ps;
|
||||
|
||||
Logger(PrintStream ps) {
|
||||
this.ps = ps;
|
||||
}
|
||||
|
||||
void log(Object obj) {
|
||||
ps.println(obj);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
import java.io.*;
|
||||
|
||||
class Main {
|
||||
void test() {
|
||||
System.out.println((Object) "foo");
|
||||
}
|
||||
}
|
||||
|
||||
class Logger {
|
||||
private final PrintStream ps;
|
||||
|
||||
Logger(PrintStream ps) {
|
||||
this.ps = ps;
|
||||
}
|
||||
|
||||
void log(Object obj) {
|
||||
ps.println(obj);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright 2000-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.intellij.java.refactoring.inline;
|
||||
|
||||
import com.intellij.JavaTestUtil;
|
||||
import com.intellij.java.refactoring.LightRefactoringTestCase;
|
||||
import com.intellij.openapi.projectRoots.Sdk;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.refactoring.BaseRefactoringProcessor;
|
||||
import com.intellij.refactoring.inline.InlineObjectProcessor;
|
||||
import com.intellij.testFramework.IdeaTestUtil;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class InlineObjectTest extends LightRefactoringTestCase {
|
||||
@NotNull
|
||||
@Override
|
||||
protected String getTestDataPath() {
|
||||
return JavaTestUtil.getJavaTestDataPath();
|
||||
}
|
||||
|
||||
public void testInlinePoint() { doTest(); }
|
||||
public void testInlinePointToString() { doTest(); }
|
||||
public void testInlineBitString() { doTest(); }
|
||||
public void testInlineSideEffect() { doTest(); }
|
||||
public void testInlineFileParentSrc() {
|
||||
BaseRefactoringProcessor.ConflictsInTestsException.withIgnoredConflicts(this::doTest);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Sdk getProjectJDK() {
|
||||
return getTestName(false).contains("Src") ? IdeaTestUtil.getMockJdk17() : super.getProjectJDK();
|
||||
}
|
||||
|
||||
private void doTest() {
|
||||
@NonNls String fileName = configure();
|
||||
performAction();
|
||||
checkResultByFile(fileName + ".after");
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private String configure() {
|
||||
@NonNls String fileName = "/refactoring/inlineObject/" + getTestName(false) + ".java";
|
||||
configureByFile(fileName);
|
||||
return fileName;
|
||||
}
|
||||
|
||||
private void performAction() {
|
||||
final PsiReference ref = getFile().findReferenceAt(getEditor().getCaretModel().getOffset());
|
||||
assertInstanceOf(ref, PsiJavaCodeReferenceElement.class);
|
||||
final PsiElement parent = ((PsiJavaCodeReferenceElement)ref).getParent();
|
||||
assertInstanceOf(parent, PsiNewExpression.class);
|
||||
PsiMethod method = ((PsiNewExpression)parent).resolveConstructor();
|
||||
method = (PsiMethod)method.getNavigationElement();
|
||||
InlineObjectProcessor processor = InlineObjectProcessor.create(ref, method);
|
||||
assertNotNull(processor);
|
||||
processor.run();
|
||||
}
|
||||
}
|
||||
@@ -469,6 +469,7 @@ refactoring.is.not.supported.when.return.statement.interrupts.the.execution.flow
|
||||
refactoring.is.not.supported.for.recursive.methods={0} refactoring may not be applied to remove recursive methods.\nYou can inline only individual method calls.
|
||||
refactoring.cannot.be.applied.to.vararg.constructors={0} refactoring cannot be applied to vararg constructors
|
||||
refactoring.cannot.be.applied.to.inline.non.chaining.constructors={0} refactoring cannot be applied to inline non-chaining constructors
|
||||
refactoring.cannot.be.applied={0} refactoring cannot be applied
|
||||
inline.method.command=Inlining method {0}
|
||||
inlined.method.implements.method.from.0=Inlined method implements method from {0}
|
||||
inlined.method.overrides.method.from.0=Inlined method overrides method from {0}
|
||||
|
||||
Reference in New Issue
Block a user