IDEA-225778 Inline object with the subsequent call

GitOrigin-RevId: 122e10e69f93c31e289a024381c7315f1a62203b
This commit is contained in:
Tagir Valeev
2019-11-07 02:40:58 +00:00
committed by intellij-monorepo-bot
parent 7b5dcb3d80
commit c57a3e9f2a
18 changed files with 1259 additions and 472 deletions
@@ -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}