diff --git a/java/typeMigration/src/com/intellij/refactoring/typeMigration/intentions/ConvertFieldToAtomicIntention.java b/java/typeMigration/src/com/intellij/refactoring/typeMigration/intentions/ConvertFieldToAtomicIntention.java index c1b36db3267a..bf2f17d838d6 100644 --- a/java/typeMigration/src/com/intellij/refactoring/typeMigration/intentions/ConvertFieldToAtomicIntention.java +++ b/java/typeMigration/src/com/intellij/refactoring/typeMigration/intentions/ConvertFieldToAtomicIntention.java @@ -7,7 +7,6 @@ import com.intellij.lang.java.JavaLanguage; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.Pair; import com.intellij.psi.*; import com.intellij.psi.codeStyle.CodeStyleSettingsManager; import com.intellij.psi.codeStyle.JavaCodeStyleManager; @@ -21,15 +20,14 @@ import com.intellij.refactoring.typeMigration.TypeConversionDescriptor; import com.intellij.refactoring.typeMigration.TypeEvaluator; import com.intellij.refactoring.typeMigration.TypeMigrationReplacementUtil; import com.intellij.refactoring.typeMigration.rules.AtomicConversionRule; -import com.intellij.refactoring.typeMigration.usageInfo.TypeMigrationUsageInfo; import com.intellij.refactoring.util.RefactoringUtil; import com.intellij.util.IncorrectOperationException; import com.intellij.util.Query; import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.util.HashSet; -import java.util.LinkedList; import java.util.Map; import java.util.Set; import java.util.concurrent.atomic.*; @@ -160,7 +158,8 @@ public class ConvertFieldToAtomicIntention extends PsiElementBaseIntentionAction try { for (PsiReference reference : refs) { - PsiElement psiElement = reference.getElement(); + PsiElement refElement = reference.getElement(); + PsiElement psiElement = refElement; if (psiElement instanceof PsiExpression) { final PsiElement parent = psiElement.getParent(); if (parent instanceof PsiExpression && !(parent instanceof PsiReferenceExpression || parent instanceof PsiPolyadicExpression)) { @@ -168,14 +167,14 @@ public class ConvertFieldToAtomicIntention extends PsiElementBaseIntentionAction } if (psiElement instanceof PsiBinaryExpression) { PsiBinaryExpression binary = (PsiBinaryExpression)psiElement; - if (isBinaryOperatorApplicable(binary.getOperationTokenType(), binary.getLOperand(), binary.getROperand(), true)) { + if (isBinaryOpApplicable(binary.getOperationTokenType(), binary.getLOperand(), binary.getROperand(), refElement, toType)) { continue; } } else if (psiElement instanceof PsiAssignmentExpression) { final PsiAssignmentExpression assignment = (PsiAssignmentExpression)psiElement; final IElementType opSign = TypeConversionUtil.convertEQtoOperation(assignment.getOperationTokenType()); - if (opSign != null && isBinaryOperatorApplicable(opSign, assignment.getLExpression(), assignment.getRExpression(), true)) { + if (isBinaryOpApplicable(opSign, assignment.getLExpression(), assignment.getRExpression(), refElement, toType)) { continue; } } @@ -227,4 +226,15 @@ public class ConvertFieldToAtomicIntention extends PsiElementBaseIntentionAction public boolean startInWriteAction() { return true; } + + private static boolean isBinaryOpApplicable(@Nullable IElementType opSign, + @NotNull PsiExpression lExpr, + @Nullable PsiExpression rExpr, + @NotNull PsiElement varElement, + @NotNull PsiType migrationType) { + if (opSign == null || rExpr == null) return false; + PsiType lType = lExpr == varElement ? migrationType : lExpr.getType(); + PsiType rType = rExpr == varElement ? migrationType : rExpr.getType(); + return isBinaryOperatorApplicable(opSign, lType, rType, true); + } } diff --git a/java/typeMigration/src/com/intellij/refactoring/typeMigration/rules/AtomicConversionRule.java b/java/typeMigration/src/com/intellij/refactoring/typeMigration/rules/AtomicConversionRule.java index a6275b650266..21195048f5e8 100644 --- a/java/typeMigration/src/com/intellij/refactoring/typeMigration/rules/AtomicConversionRule.java +++ b/java/typeMigration/src/com/intellij/refactoring/typeMigration/rules/AtomicConversionRule.java @@ -7,13 +7,16 @@ package com.intellij.refactoring.typeMigration.rules; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.util.Comparing; import com.intellij.psi.*; +import com.intellij.psi.codeStyle.JavaCodeStyleManager; import com.intellij.psi.impl.PsiDiamondTypeUtil; import com.intellij.psi.tree.IElementType; import com.intellij.psi.util.PsiUtil; import com.intellij.psi.util.TypeConversionUtil; import com.intellij.refactoring.typeMigration.TypeConversionDescriptor; import com.intellij.refactoring.typeMigration.TypeConversionDescriptorBase; +import com.intellij.refactoring.typeMigration.TypeEvaluator; import com.intellij.refactoring.typeMigration.TypeMigrationLabeler; +import com.siyeh.ig.psiutils.ParenthesesUtils; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -114,9 +117,22 @@ public class AtomicConversionRule extends TypeConversionRule { final IElementType operationSign = signToken.getTokenType(); final String sign = signToken.getText(); if (operationSign == JavaTokenType.PLUSEQ || operationSign == JavaTokenType.MINUSEQ) { - return new TypeConversionDescriptor("$qualifier$ " + sign + " $val$", "$qualifier$.getAndAdd(" + - (operationSign == JavaTokenType.MINUSEQ ? "-" : "") + - "($val$))"); + return new TypeConversionDescriptor("$qualifier$ " + sign + " $val$", + "$qualifier$.addAndGet(" + (operationSign == JavaTokenType.MINUSEQ ? "-($val$))" : "$val$)")) { + @Override + public PsiExpression replace(PsiExpression expression, TypeEvaluator evaluator) { + final PsiMethodCallExpression result = (PsiMethodCallExpression)super.replace(expression, evaluator); + final PsiExpression argument = result.getArgumentList().getExpressions()[0]; + if (argument instanceof PsiPrefixExpression) { + final PsiExpression operand = ((PsiPrefixExpression)argument).getOperand(); + final PsiExpression striped = ParenthesesUtils.stripParentheses(operand); + if (striped != null && operand != striped) { + operand.replace(striped); + } + } + return result; + } + }; } } } @@ -198,11 +214,22 @@ public class AtomicConversionRule extends TypeConversionRule { return new TypeConversionDescriptor("$qualifier$ = $val$", "$qualifier$.set($val$)"); } else { - return new TypeConversionDescriptor("$qualifier$" + sign + "$val$", "$qualifier$.set(" + - getBoxedWrapper(from, to, "$qualifier$.get() " + - sign.charAt(0) + - " $val$") + - ")"); + if (PsiUtil.isLanguageLevel8OrHigher(context)) { + final String name = + JavaCodeStyleManager.getInstance(context.getProject()).suggestUniqueVariableName("v", context, false); + return new TypeConversionDescriptor("$qualifier$" + sign + "$val$", "$qualifier$.updateAndGet(" + + name + " -> " + name + " " + sign.charAt(0) + " $val$)"); } + else { + if (context.getParent() instanceof PsiStatement) { + return new TypeConversionDescriptor("$qualifier$" + sign + "$val$", "$qualifier$.set(" + + getBoxedWrapper(from, to, "$qualifier$.get() " + + sign.charAt(0) + + " $val$") + + ")"); + } else { + return null; + } + } } } //else should be a conflict } diff --git a/java/typeMigration/testData/intentions/atomic/afterAssignmentOp.java b/java/typeMigration/testData/intentions/atomic/afterAssignmentOp.java new file mode 100644 index 000000000000..0bb7d49f37e1 --- /dev/null +++ b/java/typeMigration/testData/intentions/atomic/afterAssignmentOp.java @@ -0,0 +1,18 @@ +import java.util.concurrent.atomic.AtomicLong; + +// "Convert to atomic" "true" +class A { + final AtomicLong x = new AtomicLong(0); + + public void testAtomicLong() { + x.getAndIncrement(); + x.getAndDecrement(); + x.addAndGet(2); + x.addAndGet(-2); + x.updateAndGet(v -> v * 3); + x.updateAndGet(v -> v / 3); + x.updateAndGet(v -> v % 3); + x.updateAndGet(v -> v & 3); + x.updateAndGet(v -> v | 3); + } +} \ No newline at end of file diff --git a/java/typeMigration/testData/intentions/atomic/beforeAssignmentOp.java b/java/typeMigration/testData/intentions/atomic/beforeAssignmentOp.java new file mode 100644 index 000000000000..6b6d59d7f3ca --- /dev/null +++ b/java/typeMigration/testData/intentions/atomic/beforeAssignmentOp.java @@ -0,0 +1,16 @@ +// "Convert to atomic" "true" +class A { + long x = 0; + + public void testAtomicLong() { + x++; + x--; + x+=2; + x-=2; + x*=3; + x/=3; + x%=3; + x&=3; + x|=3; + } +} \ No newline at end of file