atomic type migration: remove default argument for atomic constructor call

This commit is contained in:
Dmitry Batkovich
2017-09-08 16:09:48 +03:00
parent cd2a92875c
commit 816f4f2b0e
21 changed files with 311 additions and 92 deletions
@@ -463,6 +463,13 @@ public class TypeMigrationLabeler {
if (originalType.equals(PsiType.NULL)) {
if (migrationType instanceof PsiPrimitiveType) {
markFailedConversion(Pair.create(originalType, migrationType), expr);
return;
}
if (place instanceof PsiVariable) {
PsiType type = ((PsiVariable)place).getType();
if (((PsiVariable)place).getInitializer() == expr && myRules.shouldConvertNull(type, migrationType, expr)) {
convertExpression(expr, migrationType, type, isCovariant);
}
}
return;
}
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2011 JetBrains s.r.o.
* 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.
@@ -89,6 +89,10 @@ public class TypeMigrationRules {
return null;
}
public boolean shouldConvertNull(final PsiType from, final PsiType to, PsiExpression context) {
return myConversionRules.stream().anyMatch(rule -> rule.shouldConvertNullInitializer(from, to, context));
}
public void setBoundScope(final SearchScope searchScope) {
mySearchScope = searchScope;
}
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2011 JetBrains s.r.o.
* 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.
@@ -44,4 +44,8 @@ public abstract class TypeConversionRule {
final TypeMigrationLabeler labeler) {
return null;
}
public boolean shouldConvertNullInitializer(PsiType from, PsiType to, PsiExpression context) {
return false;
}
}
@@ -0,0 +1,46 @@
/*
* 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.refactoring.typeMigration.rules;
import com.intellij.psi.JavaPsiFacade;
import com.intellij.psi.PsiArrayInitializerExpression;
import com.intellij.psi.PsiElementFactory;
import com.intellij.psi.PsiExpression;
import com.intellij.psi.util.TypeConversionUtil;
import com.intellij.refactoring.typeMigration.TypeConversionDescriptor;
import org.jetbrains.annotations.NotNull;
class ArrayInitializerAwareConversionDescriptor extends TypeConversionDescriptor {
public ArrayInitializerAwareConversionDescriptor(String stringToReplace,
String replaceByString,
PsiExpression expression) {
super(stringToReplace, replaceByString, expression);
}
@NotNull
@Override
protected PsiExpression adjustExpressionBeforeReplacement(@NotNull PsiExpression expression) {
if (expression instanceof PsiArrayInitializerExpression) {
PsiElementFactory elementFactory = JavaPsiFacade.getInstance(expression.getProject()).getElementFactory();
return (PsiExpression)expression.replace(elementFactory.createExpressionFromText("new " +
TypeConversionUtil
.erasure(expression.getType()).getCanonicalText() +
expression.getText(),
expression));
}
return expression;
}
}
@@ -0,0 +1,46 @@
/*
* 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.refactoring.typeMigration.rules;
import com.intellij.psi.PsiExpression;
import com.intellij.psi.PsiNewExpression;
import com.intellij.refactoring.typeMigration.TypeEvaluator;
import org.jetbrains.annotations.NotNull;
import java.util.Objects;
class AtomicConstructorConversionDescriptor extends ArrayInitializerAwareConversionDescriptor {
@NotNull
private final AtomicConversionType myType;
public AtomicConstructorConversionDescriptor(String stringToReplace,
String replaceByString,
PsiExpression expression,
@NotNull AtomicConversionType type) {
super(stringToReplace, replaceByString, expression);
myType = type;
}
@Override
public PsiExpression replace(PsiExpression expression, @NotNull TypeEvaluator evaluator) {
PsiNewExpression constructorCall = (PsiNewExpression)super.replace(expression, evaluator);
PsiExpression argument = Objects.requireNonNull(constructorCall.getArgumentList()).getExpressions()[0];
if (myType.checkDefaultValue(argument)) {
argument.delete();
}
return constructorCall;
}
}
@@ -1,3 +1,18 @@
/*
* 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.refactoring.typeMigration.rules;
import com.intellij.openapi.diagnostic.Logger;
@@ -30,60 +45,28 @@ public class AtomicConversionRule extends TypeConversionRule {
PsiMember member,
PsiExpression context,
TypeMigrationLabeler labeler) {
if (to instanceof PsiClassType && isAtomicTypeMigration(from, (PsiClassType)to, context)) {
return findDirectConversion(context, to, from);
if (to instanceof PsiClassType) {
AtomicConversionType type = AtomicConversionType.getConversionType(from, (PsiClassType)to, context);
if (type != null) {
return findDirectConversion(context, to, from, type);
}
}
else if (from instanceof PsiClassType && isAtomicTypeMigration(to, (PsiClassType)from, context)) {
if (from instanceof PsiClassType && AtomicConversionType.getConversionType(to, (PsiClassType)from, context) != null) {
return findReverseConversion(context);
}
return null;
}
private static boolean isAtomicTypeMigration(PsiType from, PsiClassType to, PsiExpression context) {
if (PsiType.INT.isAssignableFrom(from) && to.getCanonicalText().equals(AtomicInteger.class.getName())) {
return true;
}
if (PsiType.LONG.isAssignableFrom(from) && to.getCanonicalText().equals(AtomicLong.class.getName())) {
return true;
}
if (from.equals(PsiType.INT.createArrayType()) && to.getCanonicalText().equals(AtomicIntegerArray.class.getName())) {
return true;
}
if (from.equals(PsiType.LONG.createArrayType()) && to.getCanonicalText().equals(AtomicLongArray.class.getName())) {
return true;
}
if (PsiType.BOOLEAN.equals(from) && to.getCanonicalText().equals(AtomicBoolean.class.getName())) {
return true;
}
final PsiClassType.ClassResolveResult resolveResult = PsiUtil.resolveGenericsClassInType(to);
final PsiClass atomicClass = resolveResult.getElement();
if (atomicClass != null) {
final String typeQualifiedName = atomicClass.getQualifiedName();
if (!Comparing.strEqual(typeQualifiedName, AtomicReference.class.getName()) &&
!Comparing.strEqual(typeQualifiedName, AtomicReferenceArray.class.getName())) {
return false;
}
final PsiTypeParameter[] typeParameters = atomicClass.getTypeParameters();
if (typeParameters.length != 1) return false;
final PsiType toTypeParameterValue = resolveResult.getSubstitutor().substitute(typeParameters[0]);
if (toTypeParameterValue != null) {
if (from.getDeepComponentType() instanceof PsiPrimitiveType) {
final PsiPrimitiveType unboxedInitialType = PsiPrimitiveType.getUnboxedType(toTypeParameterValue);
if (unboxedInitialType != null) {
return TypeConversionUtil.areTypesConvertible(from.getDeepComponentType(), unboxedInitialType);
}
}
else {
return TypeConversionUtil.isAssignable(from.getDeepComponentType(), PsiUtil.captureToplevelWildcards(toTypeParameterValue, context));
}
}
}
return false;
@Override
public boolean shouldConvertNullInitializer(PsiType from, PsiType to, PsiExpression context) {
return to instanceof PsiClassType && AtomicConversionType.getConversionType(from, (PsiClassType)to, context) != null;
}
@Nullable
public static TypeConversionDescriptor findDirectConversion(PsiElement context, PsiType to, PsiType from) {
public static TypeConversionDescriptor findDirectConversion(PsiElement context,
PsiType to,
PsiType from,
AtomicConversionType type) {
final PsiClass toTypeClass = PsiUtil.resolveClassInType(to);
LOG.assertTrue(toTypeClass != null);
final String qualifiedName = toTypeClass.getQualifiedName();
@@ -137,7 +120,7 @@ public class AtomicConversionRule extends TypeConversionRule {
}
}
else if (context instanceof PsiLiteralExpression && !(context.getParent() instanceof PsiAssignmentExpression)) {
return wrapWithNewExpression(to, from, (PsiExpression)context, context);
return wrapWithNewExpression(to, from, (PsiExpression)context, context, type);
}
}
else if (qualifiedName.equals(AtomicIntegerArray.class.getName()) || qualifiedName.equals(AtomicLongArray.class.getName())) {
@@ -181,12 +164,12 @@ public class AtomicConversionRule extends TypeConversionRule {
}
}
return from instanceof PsiArrayType
? findDirectConversionForAtomicReferenceArray(context, to, from)
: findDirectConversionForAtomicReference(context, to, from);
? findDirectConversionForAtomicReferenceArray(context, to, from, type)
: findDirectConversionForAtomicReference(context, to, from, type);
}
@Nullable
private static TypeConversionDescriptor findDirectConversionForAtomicReference(PsiElement context, PsiType to, PsiType from) {
private static TypeConversionDescriptor findDirectConversionForAtomicReference(PsiElement context, PsiType to, PsiType from, AtomicConversionType type) {
final PsiElement parent = context.getParent();
if (parent instanceof PsiAssignmentExpression) {
final IElementType operationSign = ((PsiAssignmentExpression)parent).getOperationTokenType();
@@ -215,7 +198,7 @@ public class AtomicConversionRule extends TypeConversionRule {
if (lExpression instanceof PsiReferenceExpression) {
final PsiElement element = ((PsiReferenceExpression)lExpression).resolve();
if (element instanceof PsiVariable && ((PsiVariable)element).hasModifierProperty(PsiModifier.FINAL)) {
return wrapWithNewExpression(to, from, ((PsiAssignmentExpression)context).getRExpression(), element);
return wrapWithNewExpression(to, from, ((PsiAssignmentExpression)context).getRExpression(), element, type);
}
}
return new TypeConversionDescriptor("$qualifier$ = $val$", "$qualifier$.set($val$)");
@@ -254,12 +237,16 @@ public class AtomicConversionRule extends TypeConversionRule {
}
if (parent instanceof PsiVariable) {
return wrapWithNewExpression(to, from, null, parent);
return wrapWithNewExpression(to, from, null, parent, type);
}
return null;
}
public static TypeConversionDescriptor wrapWithNewExpression(PsiType to, PsiType from, @Nullable PsiExpression expression, PsiElement context) {
public static TypeConversionDescriptor wrapWithNewExpression(PsiType to,
PsiType from,
@Nullable PsiExpression expression,
PsiElement context,
@NotNull AtomicConversionType type) {
final String typeText = PsiDiamondTypeUtil.getCollapsedType(to, context);
final PsiClassType.ClassResolveResult resolveResult = PsiUtil.resolveGenericsClassInType(to);
final PsiClass atomicClass = resolveResult.getElement();
@@ -273,37 +260,22 @@ public class AtomicConversionRule extends TypeConversionRule {
final PsiClassType boxedFromType = ((PsiPrimitiveType)from).getBoxedType(atomicClass);
LOG.assertTrue(boxedFromType != null);
if (!TypeConversionUtil.isAssignable(initial, boxedFromType)) {
return new ArrayInitializerAwareConversionDescriptor("$val$", "new " + typeText + "((" + unboxedInitialType.getCanonicalText() + ")$val$)", expression);
return new AtomicConstructorConversionDescriptor("$val$",
"new " + typeText + "((" + unboxedInitialType.getCanonicalText() + ")$val$)",
expression,
type);
}
}
}
}
return new ArrayInitializerAwareConversionDescriptor("$val$", "new " + typeText + "($val$)", expression);
}
static class ArrayInitializerAwareConversionDescriptor extends TypeConversionDescriptor {
public ArrayInitializerAwareConversionDescriptor(String stringToReplace,
String replaceByString,
PsiExpression expression) {
super(stringToReplace, replaceByString, expression);
}
@NotNull
@Override
protected PsiExpression adjustExpressionBeforeReplacement(@NotNull PsiExpression expression) {
if (expression instanceof PsiArrayInitializerExpression) {
PsiElementFactory elementFactory = JavaPsiFacade.getInstance(expression.getProject()).getElementFactory();
return (PsiExpression)expression.replace(elementFactory.createExpressionFromText("new " +
TypeConversionUtil.erasure(expression.getType()).getCanonicalText() +
expression.getText(),
expression));
}
return expression;
}
return new AtomicConstructorConversionDescriptor("$val$", "new " + typeText + "($val$)", expression, type);
}
@Nullable
private static TypeConversionDescriptor findDirectConversionForAtomicReferenceArray(PsiElement context, PsiType to, PsiType from) {
private static TypeConversionDescriptor findDirectConversionForAtomicReferenceArray(PsiElement context,
PsiType to,
PsiType from,
AtomicConversionType type) {
LOG.assertTrue(from instanceof PsiArrayType);
from = ((PsiArrayType)from).getComponentType();
final PsiElement parent = context.getParent();
@@ -333,12 +305,12 @@ public class AtomicConversionRule extends TypeConversionRule {
else {
final PsiExpression rExpression = assignmentExpression.getRExpression();
if (rExpression == context && operationSign == JavaTokenType.EQ) { //array = new T[l];
return wrapWithNewExpression(to, from, rExpression, context);
return wrapWithNewExpression(to, from, rExpression, context, type);
}
}
} else if (parent instanceof PsiVariable) {
if (((PsiVariable)parent).getInitializer() == context) {
return wrapWithNewExpression(to, from, (PsiExpression)context, context);
return wrapWithNewExpression(to, from, (PsiExpression)context, context, type);
}
}
@@ -0,0 +1,113 @@
/*
* 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.refactoring.typeMigration.rules;
import com.intellij.openapi.util.Comparing;
import com.intellij.psi.*;
import com.intellij.psi.impl.JavaConstantExpressionEvaluator;
import com.intellij.psi.util.PsiUtil;
import com.intellij.psi.util.TypeConversionUtil;
import org.jetbrains.annotations.Nullable;
import java.util.Arrays;
import java.util.Objects;
import java.util.concurrent.atomic.*;
enum AtomicConversionType {
ATOMIC_INTEGER {
@Override
protected boolean accept(PsiType from, PsiClassType to, PsiExpression context) {
return PsiType.INT.isAssignableFrom(from) && to.getCanonicalText().equals(AtomicInteger.class.getName());
}
@Override
protected boolean checkDefaultValue(PsiExpression expr) {
return Objects.equals(JavaConstantExpressionEvaluator.computeConstantExpression(expr, false), 0);
}
},
ATOMIC_LONG {
@Override
protected boolean accept(PsiType from, PsiClassType to, PsiExpression context) {
return PsiType.LONG.isAssignableFrom(from) && to.getCanonicalText().equals(AtomicLong.class.getName());
}
@Override
protected boolean checkDefaultValue(PsiExpression expr) {
return Objects.equals(JavaConstantExpressionEvaluator.computeConstantExpression(expr, false), 0);
}
},
ATOMIC_BOOLEAN {
@Override
protected boolean accept(PsiType from, PsiClassType to, PsiExpression context) {
return PsiType.BOOLEAN.equals(from) && to.getCanonicalText().equals(AtomicBoolean.class.getName());
}
@Override
protected boolean checkDefaultValue(PsiExpression expr) {
return false;
}
},
ATOMIC_REFERENCE_OR_ARRAY {
@Override
protected boolean accept(PsiType from, PsiClassType to, PsiExpression context) {
if (from.equals(PsiType.INT.createArrayType()) && to.getCanonicalText().equals(AtomicIntegerArray.class.getName())) {
return true;
}
if (from.equals(PsiType.LONG.createArrayType()) && to.getCanonicalText().equals(AtomicLongArray.class.getName())) {
return true;
}
final PsiClassType.ClassResolveResult resolveResult = PsiUtil.resolveGenericsClassInType(to);
final PsiClass atomicClass = resolveResult.getElement();
if (atomicClass != null) {
final String typeQualifiedName = atomicClass.getQualifiedName();
if (!Comparing.strEqual(typeQualifiedName, AtomicReference.class.getName()) &&
!Comparing.strEqual(typeQualifiedName, AtomicReferenceArray.class.getName())) {
return false;
}
final PsiTypeParameter[] typeParameters = atomicClass.getTypeParameters();
if (typeParameters.length != 1) return false;
final PsiType toTypeParameterValue = resolveResult.getSubstitutor().substitute(typeParameters[0]);
if (toTypeParameterValue != null) {
if (from.getDeepComponentType() instanceof PsiPrimitiveType) {
final PsiPrimitiveType unboxedInitialType = PsiPrimitiveType.getUnboxedType(toTypeParameterValue);
if (unboxedInitialType != null) {
return TypeConversionUtil.areTypesConvertible(from.getDeepComponentType(), unboxedInitialType);
}
}
else {
return TypeConversionUtil.isAssignable(from.getDeepComponentType(), PsiUtil.captureToplevelWildcards(toTypeParameterValue, context));
}
}
}
return false;
}
@Override
protected boolean checkDefaultValue(PsiExpression expr) {
return PsiType.NULL.equals(expr.getType());
}
};
protected abstract boolean accept(PsiType from, PsiClassType to, PsiExpression context);
protected abstract boolean checkDefaultValue(PsiExpression expr);
@Nullable
static AtomicConversionType getConversionType(PsiType from, PsiClassType to, PsiExpression context) {
return Arrays.stream(values()).filter(type -> type.accept(from, to, context)).findFirst().orElse(null);
}
}
@@ -1,3 +1,18 @@
/*
* 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.refactoring.typeMigration.rules;
import com.intellij.codeInsight.daemon.impl.analysis.HighlightControlFlowUtil;
@@ -244,7 +259,7 @@ public class ThreadLocalConversionRule extends TypeConversionRule {
return toBoxed(arg, from, context);
}
private static class WrappingWithInnerClassOrLambdaDescriptor extends AtomicConversionRule.ArrayInitializerAwareConversionDescriptor {
private static class WrappingWithInnerClassOrLambdaDescriptor extends ArrayInitializerAwareConversionDescriptor {
private final List<PsiVariable> myVariablesToMakeFinal;
private WrappingWithInnerClassOrLambdaDescriptor(@NonNls final String stringToReplace,
@@ -2,7 +2,7 @@ import java.util.concurrent.atomic.AtomicInteger;
// "Convert to atomic" "true"
class Test {
final AtomicInteger o = new AtomicInteger(0);
final AtomicInteger o = new AtomicInteger();
void foo() {
boolean b = this.o.get() == 1;
@@ -4,7 +4,7 @@ import java.util.concurrent.atomic.AtomicInteger;
class Test {
{
AtomicInteger i = new AtomicInteger(0);
AtomicInteger i = new AtomicInteger();
Integer j = 0;
assert j == i.get();
@@ -2,5 +2,5 @@ import java.util.concurrent.atomic.AtomicInteger;
// "Convert to atomic" "true"
class Test {
final AtomicInteger i = new AtomicInteger(0);
final AtomicInteger i = new AtomicInteger();
}
@@ -2,7 +2,7 @@ import java.util.concurrent.atomic.AtomicInteger;
// "Convert to atomic" "true"
class Test {
final AtomicInteger i = new AtomicInteger(0);
final AtomicInteger i = new AtomicInteger();
int j = i.get() + 5;
String s = "i = " + i;
@@ -2,7 +2,7 @@ import java.util.concurrent.atomic.AtomicInteger;
// "Convert to atomic" "true"
class Test {
final AtomicInteger o = new AtomicInteger(0);
final AtomicInteger o = new AtomicInteger();
int j = o.get();
void foo() {
@@ -2,7 +2,7 @@ import java.util.concurrent.atomic.AtomicInteger;
// "Convert to atomic" "true"
class Test {
final AtomicInteger o = new AtomicInteger(0);
final AtomicInteger o = new AtomicInteger();
int j = o.get();
void foo() {
@@ -2,7 +2,7 @@ import java.util.concurrent.atomic.AtomicInteger;
// "Convert to atomic" "true"
class Test {
final AtomicInteger o = new AtomicInteger(0);
final AtomicInteger o = new AtomicInteger();
int j = o.get();
Test(int o) {
@@ -2,7 +2,7 @@ import java.util.concurrent.atomic.AtomicLong;
// "Convert to atomic" "true"
class A {
final AtomicLong x = new AtomicLong(0);
final AtomicLong x = new AtomicLong();
public void testAtomicLong() {
x.getAndIncrement();
@@ -7,7 +7,7 @@ public class InLambdas
{
public void test()
{
AtomicInteger x = new AtomicInteger(0);
AtomicInteger x = new AtomicInteger();
Runnable r1 = () -> x.getAndIncrement();
Runnable r2 = () -> x.addAndGet(2);
Runnable r3 = () -> x.updateAndGet(v -> v * 2);
@@ -0,0 +1,7 @@
import java.util.concurrent.atomic.AtomicReference;
// "Convert to atomic" "true"
class Test {
final AtomicReference<Object> o = new AtomicReference<>();
}
@@ -0,0 +1,5 @@
// "Convert to atomic" "true"
class Test {
Object <caret>o = null;
}
@@ -1,7 +1,7 @@
import java.util.concurrent.atomic.AtomicInteger;
class Test {
AtomicInteger a = new AtomicInteger(0);
AtomicInteger a = new AtomicInteger();
int b = a.get() + 10;
}
@@ -1,7 +1,7 @@
import java.util.concurrent.atomic.AtomicInteger;
class Test {
AtomicInteger i = new AtomicInteger(0);
AtomicInteger i = new AtomicInteger();
void foo() {
i.addAndGet(2);