mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
Java: enable inlining on chaining vararg constructors (IDEA-376690, IDEA-376692)
GitOrigin-RevId: 2594b3ca241b7a4a0ca7e362482f5b606486f254
This commit is contained in:
committed by
intellij-monorepo-bot
parent
da71af67ec
commit
7d5bd541d5
+2
-9
@@ -1,5 +1,4 @@
|
||||
|
||||
// Copyright 2000-2020 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.
|
||||
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.refactoring.inline;
|
||||
|
||||
import com.intellij.CommonBundle;
|
||||
@@ -100,13 +99,7 @@ public final class InlineMethodHandler extends JavaInlineActionHandler {
|
||||
}
|
||||
|
||||
if (method.isConstructor()) {
|
||||
if (method.isVarArgs()) {
|
||||
String message = JavaRefactoringBundle.message("refactoring.cannot.be.applied.to.vararg.constructors", getRefactoringName());
|
||||
CommonRefactoringUtil.showErrorHint(project, editor, message, getRefactoringName(), HelpID.INLINE_CONSTRUCTOR);
|
||||
return;
|
||||
}
|
||||
final boolean chainingConstructor = InlineUtil.isChainingConstructor(method);
|
||||
if (!chainingConstructor) {
|
||||
if (!InlineUtil.isChainingConstructor(method)) {
|
||||
InlineObjectProcessor processor = InlineObjectProcessor.create(reference, method);
|
||||
if (processor != null) {
|
||||
if (Messages.showOkCancelDialog(JavaRefactoringBundle.message("inline.method.object.suggestion.message"),
|
||||
|
||||
+7
-25
@@ -565,28 +565,8 @@ public class InlineMethodProcessor extends BaseRefactoringProcessor {
|
||||
PsiMethod oldConstructor = constructorCall.resolveMethod();
|
||||
LOG.assertTrue(oldConstructor != null);
|
||||
oldConstructor = (PsiMethod)oldConstructor.getNavigationElement();
|
||||
PsiExpression[] instanceCreationArguments = constructorCall.getArgumentList().getExpressions();
|
||||
if (oldConstructor.isVarArgs()) { //wrap with explicit array
|
||||
final PsiParameter[] parameters = oldConstructor.getParameterList().getParameters();
|
||||
final PsiType varargType = parameters[parameters.length - 1].getType();
|
||||
if (varargType instanceof PsiEllipsisType) {
|
||||
final PsiType arrayType =
|
||||
constructorCall.resolveMethodGenerics().getSubstitutor().substitute(((PsiEllipsisType)varargType).getComponentType());
|
||||
final PsiExpression[] exprs = new PsiExpression[parameters.length];
|
||||
System.arraycopy(instanceCreationArguments, 0, exprs, 0, parameters.length - 1);
|
||||
StringBuilder varargs = new StringBuilder();
|
||||
for (int i = parameters.length - 1; i < instanceCreationArguments.length; i++) {
|
||||
if (!varargs.isEmpty()) varargs.append(", ");
|
||||
varargs.append(instanceCreationArguments[i].getText());
|
||||
}
|
||||
|
||||
exprs[parameters.length - 1] = JavaPsiFacade.getElementFactory(constructorCall.getProject())
|
||||
.createExpressionFromText("new " + arrayType.getCanonicalText() + "[]{" + varargs + "}", constructorCall);
|
||||
|
||||
instanceCreationArguments = exprs;
|
||||
}
|
||||
}
|
||||
|
||||
PsiExpression[] arguments = CommonJavaRefactoringUtil.getNonVarargArguments(constructorCall);
|
||||
PsiStatement[] statements = oldConstructor.getBody().getStatements();
|
||||
LOG.assertTrue(statements.length == 1 && statements[0] instanceof PsiExpressionStatement);
|
||||
PsiExpression expression = ((PsiExpressionStatement)statements[0]).getExpression();
|
||||
@@ -594,14 +574,16 @@ public class InlineMethodProcessor extends BaseRefactoringProcessor {
|
||||
ChangeContextUtil.encodeContextInfo(expression, true);
|
||||
|
||||
PsiMethodCallExpression methodCall = (PsiMethodCallExpression)expression.copy();
|
||||
final PsiExpression[] args = methodCall.getArgumentList().getExpressions();
|
||||
for (PsiExpression arg : args) {
|
||||
replaceParameterReferences(arg, oldConstructor, instanceCreationArguments);
|
||||
for (PsiExpression arg : methodCall.getArgumentList().getExpressions()) {
|
||||
replaceParameterReferences(arg, oldConstructor, arguments);
|
||||
}
|
||||
|
||||
try {
|
||||
final PsiExpressionList exprList = (PsiExpressionList) constructorCall.getArgumentList().replace(methodCall.getArgumentList());
|
||||
ChangeContextUtil.decodeContextInfo(exprList, PsiTreeUtil.getParentOfType(constructorCall, PsiClass.class), null);
|
||||
if (!exprList.isEmpty()) {
|
||||
PsiExpression[] expressions = exprList.getExpressions();
|
||||
CommonJavaRefactoringUtil.tryToInlineArrayCreationForVarargs(expressions[expressions.length - 1]);
|
||||
}
|
||||
}
|
||||
catch (IncorrectOperationException e) {
|
||||
LOG.error(e);
|
||||
|
||||
+3
-29
@@ -22,7 +22,6 @@ import com.intellij.refactoring.util.InlineUtil;
|
||||
import com.intellij.util.CommonJavaRefactoringUtil;
|
||||
import com.intellij.util.IncorrectOperationException;
|
||||
import com.intellij.util.ProcessingContext;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -296,40 +295,15 @@ class InlineToAnonymousConstructorProcessor {
|
||||
}
|
||||
|
||||
private PsiExpression[] initConstructorArguments() {
|
||||
PsiExpressionList argumentList = myNewExpression.getArgumentList();
|
||||
return argumentList != null ? argumentList.getExpressions() : PsiExpression.EMPTY_ARRAY;
|
||||
return CommonJavaRefactoringUtil.getNonVarargArguments(myNewExpression);
|
||||
}
|
||||
|
||||
private void generateLocalsForArguments() {
|
||||
for (int i = 0; i < myConstructorArguments.length; i++) {
|
||||
PsiExpression expr = myConstructorArguments[i];
|
||||
PsiParameter parameter = myConstructorParameters.getParameters()[i];
|
||||
if (parameter.isVarArgs()) {
|
||||
PsiEllipsisType ellipsisType = (PsiEllipsisType)parameter.getType();
|
||||
PsiType baseType = ellipsisType.getComponentType();
|
||||
@NonNls StringBuilder exprBuilder = new StringBuilder("new ");
|
||||
exprBuilder.append(baseType.getCanonicalText());
|
||||
exprBuilder.append("[] { }");
|
||||
try {
|
||||
PsiNewExpression newExpr = (PsiNewExpression) myElementFactory.createExpressionFromText(exprBuilder.toString(), myClass);
|
||||
PsiArrayInitializerExpression arrayInitializer = newExpr.getArrayInitializer();
|
||||
assert arrayInitializer != null;
|
||||
for(int j = i; j < myConstructorArguments.length; j++) {
|
||||
arrayInitializer.add(myConstructorArguments[j]);
|
||||
}
|
||||
|
||||
PsiLocalVariable variable = generateLocal(parameter.getName(), ellipsisType.toArrayType(), newExpr);
|
||||
myLocalsForParameters.put(parameter, variable);
|
||||
}
|
||||
catch (IncorrectOperationException e) {
|
||||
LOG.error(e);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
else if (!isConstant(expr)) {
|
||||
PsiLocalVariable variable = generateLocal(parameter.getName(), parameter.getType(), expr);
|
||||
myLocalsForParameters.put(parameter, variable);
|
||||
if (!isConstant(expr)) {
|
||||
myLocalsForParameters.put(parameter, generateLocal(parameter.getName(), parameter.getType(), expr));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.util;
|
||||
|
||||
import com.intellij.codeInsight.AnnotationUtil;
|
||||
@@ -856,6 +856,47 @@ public final class CommonJavaRefactoringUtil {
|
||||
(allowConjunctions && PsiUtil.isLanguageLevel8OrHigher(context)) ? JavaCodeFragmentFactory.ALLOW_INTERSECTION : 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the arguments of the call with any vararg arguments wrapped in an array.
|
||||
* Arguments to calls to non-vararg methods are returned as-is.
|
||||
*
|
||||
* @param call the call to return the arguments for.
|
||||
* @return the arguments to the call, with vararg arguments wrapped in an array when present
|
||||
*/
|
||||
public static PsiExpression[] getNonVarargArguments(PsiCall call) {
|
||||
final PsiExpressionList argumentList = call.getArgumentList();
|
||||
if (argumentList == null) return PsiExpression.EMPTY_ARRAY;
|
||||
final PsiExpression[] arguments = argumentList.getExpressions();
|
||||
final JavaResolveResult resolveResult = call.resolveMethodGenerics();
|
||||
final PsiMethod method = (PsiMethod)resolveResult.getElement();
|
||||
if (method == null || !method.isVarArgs()) {
|
||||
return arguments;
|
||||
}
|
||||
final PsiParameter[] parameters = method.getParameterList().getParameters();
|
||||
final PsiType varargType = parameters[parameters.length - 1].getType();
|
||||
if (!(varargType instanceof PsiEllipsisType ellipsis)) {
|
||||
return arguments;
|
||||
}
|
||||
final PsiType argumentType = arguments[parameters.length - 1].getType();
|
||||
boolean isCompatibleArray = arguments.length == parameters.length && argumentType != null && varargType.isAssignableFrom(argumentType);
|
||||
if (isCompatibleArray) {
|
||||
return arguments;
|
||||
}
|
||||
final PsiType arrayType = resolveResult.getSubstitutor().substitute(ellipsis.getComponentType());
|
||||
final PsiExpression[] result = new PsiExpression[parameters.length];
|
||||
System.arraycopy(arguments, 0, result, 0, parameters.length - 1);
|
||||
|
||||
final StringBuilder varargs = new StringBuilder();
|
||||
for (int i = parameters.length - 1; i < arguments.length; i++) {
|
||||
if (!varargs.isEmpty()) varargs.append(',');
|
||||
varargs.append(arguments[i].getText());
|
||||
}
|
||||
result[parameters.length - 1] = JavaPsiFacade.getElementFactory(call.getProject())
|
||||
.createExpressionFromText("new " + arrayType.getCanonicalText() + "[]{" + varargs + "}", call);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static void inlineArrayCreationForVarargs(final PsiNewExpression arrayCreation) {
|
||||
PsiExpressionList argumentList = (PsiExpressionList)PsiUtil.skipParenthesizedExprUp(arrayCreation.getParent());
|
||||
if (argumentList == null) return;
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
class Aucontraire {
|
||||
private Inner b = new <caret>Inner(1, 2); // inline this call
|
||||
|
||||
private class Inner {
|
||||
public Inner(String s, int... i) {
|
||||
}
|
||||
|
||||
public Inner(int... i) {
|
||||
this("", i);
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "A";
|
||||
}
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
class Aucontraire {
|
||||
private Inner b = new Inner("", 1, 2); // inline this call
|
||||
|
||||
private class Inner {
|
||||
public Inner(String s, int... i) {
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "A";
|
||||
}
|
||||
}
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
class Absolutely {
|
||||
private Inner b = new Inner(new int[]{1, 2});
|
||||
|
||||
private class Inner<caret> { // inline here
|
||||
public Inner(String s, int... i) {
|
||||
System.out.println(i);
|
||||
}
|
||||
|
||||
public Inner(int... i) {
|
||||
this("", i);
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "A";
|
||||
}
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
class Absolutely {
|
||||
private Object b = new Object() {
|
||||
{
|
||||
System.out.println(new int[]{1, 2});
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "A";
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
@@ -104,8 +104,10 @@ public class InlineMethodTest extends LightRefactoringTestCase {
|
||||
public void testChainingConstructor() { doTest(); }
|
||||
|
||||
public void testChainingConstructor1() {
|
||||
BaseRefactoringProcessor.ConflictsInTestsException.withIgnoredConflicts(()->doTest());
|
||||
BaseRefactoringProcessor.ConflictsInTestsException.withIgnoredConflicts(() -> doTest());
|
||||
}
|
||||
|
||||
public void testChainingVarargConstructor() { doTest(); }
|
||||
|
||||
public void testNestedCall() { doTest(); }
|
||||
|
||||
@@ -717,7 +719,7 @@ public class InlineMethodTest extends LightRefactoringTestCase {
|
||||
|
||||
private void performAction(final boolean inlineThisOnly, final boolean nonCode) {
|
||||
final PsiReference ref = getFile().findReferenceAt(getEditor().getCaretModel().getOffset());
|
||||
PsiReferenceExpression refExpr = ref instanceof PsiReferenceExpression ? (PsiReferenceExpression)ref : null;
|
||||
PsiReferenceExpression refExpr = ref instanceof PsiReferenceExpression expression ? expression : null;
|
||||
PsiMethod method = findMethod();
|
||||
final boolean condition = InlineMethodProcessor.checkBadReturns(method) && !InlineUtil.allUsagesAreTailCalls(method);
|
||||
assertFalse("Bad returns found", condition);
|
||||
|
||||
+4
@@ -157,6 +157,10 @@ public class InlineToAnonymousClassTest extends LightRefactoringTestCase {
|
||||
public void testChainedVarargConstructors() {
|
||||
doTest(false, false);
|
||||
}
|
||||
|
||||
public void testChainedVarargConstructorsCalledWithArray() {
|
||||
doTest(false, false);
|
||||
}
|
||||
|
||||
public void testInlineThisOnly() {
|
||||
doTest(true, false);
|
||||
|
||||
@@ -564,10 +564,9 @@ push.down.no.inheritors.final.class.warning.text=Final class {0} does not have i
|
||||
re.run.refactoring=Re-Run Refactoring
|
||||
record.description={1, choice, 0#|1#local }record {0}
|
||||
refactoring.cannot.be.applied.no.sources.attached={0} refactoring cannot be applied: no sources attached
|
||||
refactoring.cannot.be.applied.to.abstract.methods={0} refactoring cannot be applied to abstract methods
|
||||
refactoring.cannot.be.applied.to.inline.non.chaining.constructors={0} refactoring cannot be applied to inline non-chaining constructors
|
||||
refactoring.cannot.be.applied.to.native.methods={0} refactoring cannot be applied to native methods
|
||||
refactoring.cannot.be.applied.to.vararg.constructors={0} refactoring cannot be applied to vararg constructors
|
||||
refactoring.cannot.be.applied.to.abstract.methods={0} refactoring cannot inline abstract methods
|
||||
refactoring.cannot.be.applied.to.inline.non.chaining.constructors={0} refactoring cannot inline non-chaining constructors
|
||||
refactoring.cannot.be.applied.to.native.methods={0} refactoring cannot inline native methods
|
||||
refactoring.extract.method.dialog.duplicates.count={0,choice, 1#One|2#{0,number}} duplicate code {0,choice, 1#fragment|2#fragments} can be replaced with the extracted method call
|
||||
refactoring.extract.method.dialog.duplicates.pending=Searching for duplicates\u2026
|
||||
refactoring.extract.method.dialog.duplicates.progress=Searching for Duplicates
|
||||
|
||||
Reference in New Issue
Block a user