IDEA-146770 Suggest Math.toIntExact to convert long to int
authorDmitry Batkovich <dmitry.batkovich@jetbrains.com>
Wed, 28 Oct 2015 16:54:28 +0000 (19:54 +0300)
committerDmitry Batkovich <dmitry.batkovich@jetbrains.com>
Wed, 28 Oct 2015 16:55:14 +0000 (19:55 +0300)
17 files changed:
java/java-analysis-api/src/com/intellij/codeInsight/intention/QuickFixFactory.java
java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightMethodUtil.java
java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightUtil.java
java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/ConstructorParametersFixer.java
java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/WrapLongWithMathToIntExactFix.java [new file with mode: 0644]
java/java-analysis-impl/src/com/intellij/codeInsight/intention/EmptyQuickFixFactory.java
java/java-impl/src/com/intellij/codeInsight/intention/impl/config/QuickFixFactoryImpl.java
java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/wrapLongWithMathToIntExact/afterAssignment.java [new file with mode: 0644]
java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/wrapLongWithMathToIntExact/afterFewParameters.java [new file with mode: 0644]
java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/wrapLongWithMathToIntExact/afterSingleParameter.java [new file with mode: 0644]
java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/wrapLongWithMathToIntExact/afterVariableDeclaration.java [new file with mode: 0644]
java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/wrapLongWithMathToIntExact/beforeAssignment.java [new file with mode: 0644]
java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/wrapLongWithMathToIntExact/beforeFewParameters.java [new file with mode: 0644]
java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/wrapLongWithMathToIntExact/beforeSingleParameter.java [new file with mode: 0644]
java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/wrapLongWithMathToIntExact/beforeVariableDeclaration.java [new file with mode: 0644]
java/java-tests/testSrc/com/intellij/codeInsight/daemon/quickFix/WrapLongWithMathToIntExactFixTest.java [new file with mode: 0644]
resources-en/src/messages/QuickFixBundle.properties

index 5e1cb191ecddff0d1a8e7698821e44ed669f7553..6dd7cb6bf527ac5aec7586fa932eb1da4cf95ae7 100644 (file)
@@ -265,4 +265,7 @@ public abstract class QuickFixFactory {
 
   @NotNull
   public abstract IntentionAction addMethodQualifierFix(@NotNull PsiMethodCallExpression methodCall);
+
+  @NotNull
+  public abstract IntentionAction createWrapLongWithMathToIntExactFix(@NotNull PsiExpression expression);
 }
index a3f78da67e05bb167e4b0b58ab5dd9d6a8dd26ce..0dc6c8fca8eb91164eaf7da241c9e2c372cfc1ea 100644 (file)
@@ -583,6 +583,7 @@ public class HighlightMethodUtil {
     TextRange fixRange = getFixRange(elementToHighlight);
     CastMethodArgumentFix.REGISTRAR.registerCastActions(candidates, methodCall, info, fixRange);
     WrapArrayToArraysAsListFix.REGISTAR.registerCastActions(candidates, methodCall, info, fixRange);
+    WrapLongWithMathToIntExactFix.REGISTAR.registerCastActions(candidates, methodCall, info, fixRange);
     PermuteArgumentsFix.registerFix(info, methodCall, candidates, fixRange);
     WrapExpressionFix.registerWrapAction(candidates, list.getExpressions(), info);
     registerChangeParameterClassFix(methodCall, list, info);
@@ -669,6 +670,7 @@ public class HighlightMethodUtil {
     TextRange fixRange = getFixRange(elementToHighlight);
     CastMethodArgumentFix.REGISTRAR.registerCastActions(candidates, methodCall, info, fixRange);
     WrapArrayToArraysAsListFix.REGISTAR.registerCastActions(candidates, methodCall, info, fixRange);
+    WrapLongWithMathToIntExactFix.REGISTAR.registerCastActions(candidates, methodCall, info, fixRange);
     PermuteArgumentsFix.registerFix(info, methodCall, candidates, fixRange);
     WrapExpressionFix.registerWrapAction(candidates, list.getExpressions(), info);
     registerChangeParameterClassFix(methodCall, list, info);
@@ -703,6 +705,7 @@ public class HighlightMethodUtil {
     PermuteArgumentsFix.registerFix(highlightInfo, methodCall, methodCandidates, fixRange);
     AddTypeArgumentsFix.REGISTRAR.registerCastActions(methodCandidates, methodCall, highlightInfo, fixRange);
     WrapArrayToArraysAsListFix.REGISTAR.registerCastActions(methodCandidates, methodCall, highlightInfo, fixRange);
+    WrapLongWithMathToIntExactFix.REGISTAR.registerCastActions(methodCandidates, methodCall, highlightInfo, fixRange);
     registerMethodAccessLevelIntentions(methodCandidates, methodCall, list, highlightInfo);
     registerChangeMethodSignatureFromUsageIntentions(methodCandidates, list, highlightInfo, fixRange);
     RemoveRedundantArgumentsFix.registerIntentions(methodCandidates, list, highlightInfo, fixRange);
index 25d4412ef13516de1c9c304cab2ea1677fc63c2d..32e2437c55cea6ba2edc8d7f899d7bf302eba44b 100644 (file)
@@ -564,6 +564,9 @@ public class HighlightUtil extends HighlightUtilBase {
       QuickFixAction.registerQuickFixAction(highlightInfo, QUICK_FIX_FACTORY.createAddTypeCastFix(lType, expression));
     }
     if (expression != null) {
+      if (PsiType.INT.equals(lType) && rType != null && (PsiType.LONG.equals(rType) || PsiType.LONG.getBoxedTypeName().equals(rType.getCanonicalText(false)))) {
+        QuickFixAction.registerQuickFixAction(highlightInfo, QUICK_FIX_FACTORY.createWrapLongWithMathToIntExactFix(expression));
+      }
       QuickFixAction.registerQuickFixAction(highlightInfo, QUICK_FIX_FACTORY.createWrapExpressionFix(lType, expression));
       AddTypeArgumentsConditionalFix.register(highlightInfo, expression, lType);
     }
@@ -1415,6 +1418,9 @@ public class HighlightUtil extends HighlightUtilBase {
       QuickFixAction.registerQuickFixAction(info, QUICK_FIX_FACTORY.createConvertSwitchToIfIntention(statement));
       if (PsiType.LONG.equals(type) || PsiType.FLOAT.equals(type) || PsiType.DOUBLE.equals(type)) {
         QuickFixAction.registerQuickFixAction(info, QUICK_FIX_FACTORY.createAddTypeCastFix(PsiType.INT, expression));
+        if (PsiType.LONG.equals(type)) {
+          QuickFixAction.registerQuickFixAction(info, QUICK_FIX_FACTORY.createWrapLongWithMathToIntExactFix(expression));
+        }
       }
       if (requiredLevel != null) {
         QuickFixAction.registerQuickFixAction(info, QUICK_FIX_FACTORY.createIncreaseLanguageLevelFix(requiredLevel));
index 44a4c761b18ac5f18cfdc8efb7c0ddffd7debc24..2feb7c42d94c7260c1db3ce7e6f38fb500c837f7 100644 (file)
@@ -43,5 +43,7 @@ public class ConstructorParametersFixer {
     }
     CastMethodArgumentFix.REGISTRAR.registerCastActions(candidates, constructorCall, highlightInfo, fixRange);
     AddTypeArgumentsFix.REGISTRAR.registerCastActions(candidates, constructorCall, highlightInfo, fixRange);
+    WrapArrayToArraysAsListFix.REGISTAR.registerCastActions(candidates, constructorCall, highlightInfo, fixRange);
+    WrapLongWithMathToIntExactFix.REGISTAR.registerCastActions(candidates, constructorCall, highlightInfo, fixRange);
   }
 }
diff --git a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/WrapLongWithMathToIntExactFix.java b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/WrapLongWithMathToIntExactFix.java
new file mode 100644 (file)
index 0000000..ebb0dbb
--- /dev/null
@@ -0,0 +1,122 @@
+/*
+ * Copyright 2000-2015 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.codeInsight.daemon.impl.quickfix;
+
+import com.intellij.codeInsight.daemon.QuickFixBundle;
+import com.intellij.codeInsight.daemon.impl.analysis.JavaHighlightUtil;
+import com.intellij.codeInsight.intention.HighPriorityAction;
+import com.intellij.codeInspection.LocalQuickFixAndIntentionActionOnPsiElement;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.editor.Editor;
+import com.intellij.openapi.project.Project;
+import com.intellij.psi.*;
+import com.intellij.psi.codeStyle.JavaCodeStyleManager;
+import com.intellij.psi.util.InheritanceUtil;
+import com.intellij.psi.util.PsiUtil;
+import com.intellij.util.IncorrectOperationException;
+import org.jetbrains.annotations.Nls;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * @author Dmitry Batkovich
+ */
+public class WrapLongWithMathToIntExactFix extends LocalQuickFixAndIntentionActionOnPsiElement implements HighPriorityAction {
+  private final static Logger LOG = Logger.getInstance(WrapLongWithMathToIntExactFix.class);
+
+  public final static MyMethodArgumentFixerFactory REGISTAR = new MyMethodArgumentFixerFactory();
+
+  public WrapLongWithMathToIntExactFix(final @NotNull PsiExpression expression) {
+    super(expression);
+  }
+
+  @NotNull
+  @Override
+  public String getText() {
+    return getFamilyName();
+  }
+
+  @Override
+  public void invoke(@NotNull Project project,
+                     @NotNull PsiFile file,
+                     @Nullable("is null when called from inspection") Editor editor,
+                     @NotNull PsiElement startElement,
+                     @NotNull PsiElement endElement) {
+    startElement.replace(getModifiedExpression(startElement));
+  }
+
+  @Override
+  public boolean isAvailable(@NotNull Project project,
+                             @NotNull PsiFile file,
+                             @NotNull PsiElement startElement,
+                             @NotNull PsiElement endElement) {
+    return startElement.isValid() && startElement.getManager().isInProject(startElement) && PsiUtil.isLanguageLevel8OrHigher(startElement);
+  }
+
+  @Nls
+  @NotNull
+  @Override
+  public String getFamilyName() {
+    return QuickFixBundle.message("wrap.long.with.math.to.int.text");
+  }
+
+  private static PsiElement getModifiedExpression(PsiElement expression) {
+    return JavaPsiFacade.getElementFactory(expression.getProject()).createExpressionFromText("java.lang.Math.toIntExact(" + expression.getText() + ")", expression);
+  }
+
+  private static class MyMethodArgumentFix extends MethodArgumentFix implements HighPriorityAction {
+
+    protected MyMethodArgumentFix(@NotNull PsiExpressionList list,
+                                  int i,
+                                  @NotNull PsiType toType,
+                                  @NotNull ArgumentFixerActionFactory fixerActionFactory) {
+      super(list, i, toType, fixerActionFactory);
+    }
+
+    @Nls
+    @NotNull
+    @Override
+    public String getText() {
+      return myArgList.getExpressions().length == 1
+             ? QuickFixBundle.message("wrap.long.with.math.to.int.parameter.single.text")
+             : QuickFixBundle.message("wrap.long.with.math.to.int.parameter.multiple.text", myIndex + 1);
+    }
+
+    @Override
+    public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) {
+      return PsiUtil.isLanguageLevel8OrHigher(file) && super.isAvailable(project, editor, file);
+    }
+  }
+
+  public static class MyMethodArgumentFixerFactory extends ArgumentFixerActionFactory {
+    @Nullable
+    @Override
+    protected PsiExpression getModifiedArgument(final PsiExpression expression, final PsiType toType) throws IncorrectOperationException {
+      LOG.assertTrue(PsiType.INT.equals(toType));
+      return (PsiExpression)getModifiedExpression(expression);
+    }
+
+    @Override
+    public boolean areTypesConvertible(final PsiType exprType, final PsiType parameterType, final PsiElement context) {
+      return parameterType.isConvertibleFrom(exprType) || (PsiType.INT.equals(parameterType) && PsiType.LONG.equals(exprType));
+    }
+
+    @Override
+    public MethodArgumentFix createFix(final PsiExpressionList list, final int i, final PsiType toType) {
+      return new MyMethodArgumentFix(list, i, toType, this);
+    }
+  }
+}
index e886be781eab44583d68f37114a720e00d431389..0e4142979c6a9f58403c22db32c10e8cdb5c4a26 100644 (file)
@@ -608,4 +608,10 @@ public class EmptyQuickFixFactory extends QuickFixFactory {
   public IntentionAction addMethodQualifierFix(@NotNull PsiMethodCallExpression methodCall) {
     return QuickFixes.EMPTY_FIX;
   }
+
+  @NotNull
+  @Override
+  public IntentionAction createWrapLongWithMathToIntExactFix(@NotNull PsiExpression expression) {
+    return QuickFixes.EMPTY_FIX;
+  }
 }
index 775dfcc27fce09c9ff66c6531ba76901baeae1f7..6d4ce79436b7289c5859c3eee1b715a69010c193 100644 (file)
@@ -768,6 +768,12 @@ public class QuickFixFactoryImpl extends QuickFixFactory {
     return new AddMethodQualifierFix(methodCall);
   }
 
+  @NotNull
+  @Override
+  public IntentionAction createWrapLongWithMathToIntExactFix(@NotNull PsiExpression expression) {
+    return new WrapLongWithMathToIntExactFix(expression);
+  }
+
   private static boolean timeToOptimizeImports(@NotNull PsiFile file) {
     if (!CodeInsightSettings.getInstance().OPTIMIZE_IMPORTS_ON_THE_FLY) return false;
 
diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/wrapLongWithMathToIntExact/afterAssignment.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/wrapLongWithMathToIntExact/afterAssignment.java
new file mode 100644 (file)
index 0000000..81d3580
--- /dev/null
@@ -0,0 +1,11 @@
+// "Wrap using 'Math.toIntExact()'" "true"
+public class Test {
+
+  void m(long l) {
+
+    int i = 10;
+    i = Math.toIntExact(l);
+
+  }
+
+}
\ No newline at end of file
diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/wrapLongWithMathToIntExact/afterFewParameters.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/wrapLongWithMathToIntExact/afterFewParameters.java
new file mode 100644 (file)
index 0000000..bac2a18
--- /dev/null
@@ -0,0 +1,12 @@
+// "Wrap 2nd parameter using 'Math.toIntExact()'" "true"
+public class Test {
+
+  void longMethod(int k, int thisIsInt) {
+
+  }
+
+  void m(long ll) {
+    longMethod(13, Math.toIntExact(ll));
+  }
+
+}
diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/wrapLongWithMathToIntExact/afterSingleParameter.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/wrapLongWithMathToIntExact/afterSingleParameter.java
new file mode 100644 (file)
index 0000000..a0d4877
--- /dev/null
@@ -0,0 +1,12 @@
+// "Wrap parameter using 'Math.toIntExact()'" "true"
+
+public class Test {
+  void m(int i) {
+
+  }
+
+  void method() {
+    m(Math.toIntExact(10L));
+  }
+
+}
diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/wrapLongWithMathToIntExact/afterVariableDeclaration.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/wrapLongWithMathToIntExact/afterVariableDeclaration.java
new file mode 100644 (file)
index 0000000..5a4e379
--- /dev/null
@@ -0,0 +1,10 @@
+// "Wrap using 'Math.toIntExact()'" "true"
+public class Test {
+
+    void m() {
+
+      int i = Math.toIntExact(Long.valueOf(123));
+
+    }
+
+}
diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/wrapLongWithMathToIntExact/beforeAssignment.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/wrapLongWithMathToIntExact/beforeAssignment.java
new file mode 100644 (file)
index 0000000..cdba30c
--- /dev/null
@@ -0,0 +1,11 @@
+// "Wrap using 'Math.toIntExact()'" "true"
+public class Test {
+
+  void m(long l) {
+
+    int i = 10;
+    i = l<caret>;
+
+  }
+
+}
\ No newline at end of file
diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/wrapLongWithMathToIntExact/beforeFewParameters.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/wrapLongWithMathToIntExact/beforeFewParameters.java
new file mode 100644 (file)
index 0000000..c940751
--- /dev/null
@@ -0,0 +1,12 @@
+// "Wrap 2nd parameter using 'Math.toIntExact()'" "true"
+public class Test {
+
+  void longMethod(int k, int thisIsInt) {
+
+  }
+
+  void m(long ll) {
+    longMethod(13, l<caret>l);
+  }
+
+}
diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/wrapLongWithMathToIntExact/beforeSingleParameter.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/wrapLongWithMathToIntExact/beforeSingleParameter.java
new file mode 100644 (file)
index 0000000..b8a630c
--- /dev/null
@@ -0,0 +1,12 @@
+// "Wrap parameter using 'Math.toIntExact()'" "true"
+
+public class Test {
+  void m(int i) {
+
+  }
+
+  void method() {
+    m(10<caret>L);
+  }
+
+}
diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/wrapLongWithMathToIntExact/beforeVariableDeclaration.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/wrapLongWithMathToIntExact/beforeVariableDeclaration.java
new file mode 100644 (file)
index 0000000..22fc1eb
--- /dev/null
@@ -0,0 +1,10 @@
+// "Wrap using 'Math.toIntExact()'" "true"
+public class Test {
+
+    void m() {
+
+      int i = Long.valu<caret>eOf(123);
+
+    }
+
+}
diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/quickFix/WrapLongWithMathToIntExactFixTest.java b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/quickFix/WrapLongWithMathToIntExactFixTest.java
new file mode 100644 (file)
index 0000000..a32d1f3
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2000-2015 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.codeInsight.daemon.quickFix;
+
+import com.intellij.openapi.projectRoots.Sdk;
+import com.intellij.testFramework.IdeaTestUtil;
+
+/**
+ * @author Dmitry Batkovich
+ */
+public class WrapLongWithMathToIntExactFixTest extends LightQuickFixParameterizedTestCase {
+  public void test() throws Exception {
+    doAllTests();
+  }
+
+  @Override
+  protected String getBasePath() {
+    return "/codeInsight/daemonCodeAnalyzer/quickFix/wrapLongWithMathToIntExact";
+  }
+
+  @Override
+  protected Sdk getProjectJDK() {
+    return IdeaTestUtil.getMockJdk18();
+  }
+}
index a61a5eaff41e958077b37bd5a3e99d3b3237ffe0..23c7786fc1a2fac2206c066dd45dc26bd35e7610 100644 (file)
@@ -270,4 +270,8 @@ add.method.qualifier.fix.text=Add qualifier {0} to method
 
 collection.addall.can.be.replaced.with.constructor.fix.options.title=Classes to check
 collection.addall.can.be.replaced.with.constructor.fix.description='addAll()' method can be replaced with parametrized constructor
-collection.addall.can.be.replaced.with.constructor.fix.title=Replace 'addAll()' method with parametrized constructor call
\ No newline at end of file
+collection.addall.can.be.replaced.with.constructor.fix.title=Replace 'addAll()' method with parametrized constructor call
+
+wrap.long.with.math.to.int.text=Wrap using 'Math.toIntExact()'
+wrap.long.with.math.to.int.parameter.single.text=Wrap parameter using 'Math.toIntExact()'
+wrap.long.with.math.to.int.parameter.multiple.text=Wrap {0, choice, 1#1st|2#2nd|3#3rd|4#{0,number}th} parameter using ''Math.toIntExact()''
\ No newline at end of file