diff --git a/plugins/devkit/devkit-core/resources/intellij.devkit.core.xml b/plugins/devkit/devkit-core/resources/intellij.devkit.core.xml index 6de682c00093..e6a51f53e615 100644 --- a/plugins/devkit/devkit-core/resources/intellij.devkit.core.xml +++ b/plugins/devkit/devkit-core/resources/intellij.devkit.core.xml @@ -104,7 +104,7 @@ enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.idea.devkit.inspections.InspectionDescriptionNotFoundInspection" key="inspection.inspection.description.not.found.inspection.display.name"/> - [] HINTS = new Class[]{UCallExpression.class}; + @Override - protected PsiElementVisitor buildInternalVisitor(@NotNull ProblemsHolder holder, boolean isOnTheFly) { - return new JavaElementVisitor() { + @NotNull + public PsiElementVisitor buildInternalVisitor(@NotNull final ProblemsHolder holder, boolean isOnTheFly) { + return UastHintedVisitorAdapter.create(holder.getFile().getLanguage(), new AbstractUastNonRecursiveVisitor() { @Override - public void visitNewExpression(@NotNull PsiNewExpression expression) { - if (isAwtRgbColorConstructor(expression)) { - Integer grayValue = getGrayValue(getConstructorParams(expression)); - if (grayValue != null && grayClassAccessible(expression)) { - holder.registerProblem(expression, - DevKitBundle.message("inspections.use.gray.awt.color.used.name"), - new ConvertToGrayQuickFix(grayValue)); + public boolean visitCallExpression(@NotNull UCallExpression expression) { + if (expression.getKind() == UastCallKind.CONSTRUCTOR_CALL) { + if (isAwtRgbColorConstructor(expression)) { + Integer grayValue = getGrayValue(expression); + if (grayValue != null) { + PsiElement sourcePsi = expression.getSourcePsi(); + if (sourcePsi != null && grayClassAccessible(sourcePsi)) { + holder.registerProblem(sourcePsi, + DevKitBundle.message("inspections.use.gray.awt.color.used.name"), + new ConvertToGrayQuickFix(grayValue)); + } + } } } + return super.visitCallExpression(expression); } - }; + }, HINTS); } - private static boolean isAwtRgbColorConstructor(PsiNewExpression expression) { - PsiExpression[] constructorParams = getConstructorParams(expression); - if (constructorParams == null) return false; - PsiType type = expression.getType(); - if (type == null) return false; - return constructorParams.length == 3 && AWT_COLOR_CLASS_NAME.equals(type.getCanonicalText()); - } - - @NotNull - private static PsiExpression @Nullable [] getConstructorParams(@NotNull PsiNewExpression expression) { - PsiExpressionList arguments = expression.getArgumentList(); - if (arguments == null) return null; - return arguments.getExpressions(); + private static boolean isAwtRgbColorConstructor(@NotNull UCallExpression constructorCall) { + List constructorParams = constructorCall.getValueArguments(); + if (constructorParams.size() != 3) return false; + PsiMethod constructor = constructorCall.resolve(); + if (constructor != null) { + PsiClass constructorClass = constructor.getContainingClass(); + if (constructorClass != null) { + return AWT_COLOR_CLASS_NAME.equals(constructorClass.getQualifiedName()); + } + } + return false; } @Nullable - private static Integer getGrayValue(@NotNull PsiExpression @Nullable [] constructorParams) { - if (constructorParams == null) return null; - PsiExpression redParam = constructorParams[0]; + private static Integer getGrayValue(@NotNull UCallExpression constructorCall) { + List constructorParams = constructorCall.getValueArguments(); + UExpression redParam = constructorParams.get(0); Integer red = evaluateColorValue(redParam); if (red == null) return null; - PsiExpression greenParam = constructorParams[1]; - PsiExpression blueParam = constructorParams[2]; + UExpression greenParam = constructorParams.get(1); + UExpression blueParam = constructorParams.get(2); return 0 <= red && red < 256 && red.equals(evaluateColorValue(greenParam)) && red.equals(evaluateColorValue(blueParam)) ? red : null; } @Nullable - private static Integer evaluateColorValue(@NotNull PsiExpression expression) { - if (expression instanceof PsiLiteralExpression) { - Object evaluatedColorValue = JavaConstantExpressionEvaluator.computeConstantExpression(expression, false); - if (evaluatedColorValue != null) { - try { - return Integer.parseInt(evaluatedColorValue.toString()); - } - catch (Exception e) { - // ignore - } - } + private static Integer evaluateColorValue(@NotNull UExpression expression) { + Object evaluatedExpression = expression.evaluate(); + if (evaluatedExpression instanceof Integer value) { + return value; } return null; } diff --git a/plugins/devkit/devkit-java-tests/testData/inspections/useGray/UseGrayConstantWhenAwtColorUsed.java b/plugins/devkit/devkit-java-tests/testData/inspections/useGray/UseGrayConstantWhenAwtColorUsed.java index 569218fe9157..696cd822c2fa 100644 --- a/plugins/devkit/devkit-java-tests/testData/inspections/useGray/UseGrayConstantWhenAwtColorUsed.java +++ b/plugins/devkit/devkit-java-tests/testData/inspections/useGray/UseGrayConstantWhenAwtColorUsed.java @@ -7,10 +7,13 @@ class UseGrayConstantWhenAwtColorUsed { private static final Color GRAY_CONSTANT = new Color(125, 125, 125); private static final Color NOT_GRAY_CONSTANT = new Color(12, 13, 14); + private static final int GRAY_VALUE = 125; + void any() { Color myGray1 = new Color(25, 25, 25); + Color myGray2 = new Color(GRAY_VALUE, 125, GRAY_VALUE); takeColor(new Color(125, 125, 125)); - Color myGray2 = new JBColor(new Color(25, 25, 25), new Color(125, 125, 125)); + Color myGray3 = new JBColor(new Color(25, 25, 25), new Color(125, 125, 125)); takeColor(new JBColor(new Color(25, 25, 25), new Color(125, 125, 125))); // correct cases: diff --git a/plugins/devkit/devkit-java-tests/testData/inspections/useGrayFix/UseGrayConstantFixWhenNumberConstantsReferenced.java b/plugins/devkit/devkit-java-tests/testData/inspections/useGrayFix/UseGrayConstantFixWhenNumberConstantsReferenced.java new file mode 100644 index 000000000000..5e5074e053b8 --- /dev/null +++ b/plugins/devkit/devkit-java-tests/testData/inspections/useGrayFix/UseGrayConstantFixWhenNumberConstantsReferenced.java @@ -0,0 +1,11 @@ +import java.awt.Color; + +class UseGrayConstantFixWhenNumberConstantsReferenced { + + private static final int GRAY_VALUE = 125; + + void any() { + Color gray = new Color(GRAY_VALUE, 125, GRAY_VALUE); + } + +} diff --git a/plugins/devkit/devkit-java-tests/testData/inspections/useGrayFix/UseGrayConstantFixWhenNumberConstantsReferenced_after.java b/plugins/devkit/devkit-java-tests/testData/inspections/useGrayFix/UseGrayConstantFixWhenNumberConstantsReferenced_after.java new file mode 100644 index 000000000000..0597d3243b56 --- /dev/null +++ b/plugins/devkit/devkit-java-tests/testData/inspections/useGrayFix/UseGrayConstantFixWhenNumberConstantsReferenced_after.java @@ -0,0 +1,13 @@ +import com.intellij.ui.Gray; + +import java.awt.Color; + +class UseGrayConstantFixWhenNumberConstantsReferenced { + + private static final int GRAY_VALUE = 125; + + void any() { + Color gray = Gray._125; + } + +} diff --git a/plugins/devkit/devkit-java-tests/testSrc/org/jetbrains/idea/devkit/inspections/quickfix/ConvertToGrayQuickFixTest.java b/plugins/devkit/devkit-java-tests/testSrc/org/jetbrains/idea/devkit/inspections/quickfix/ConvertToGrayQuickFixTest.java index 7894628cbbf2..73968f055be0 100644 --- a/plugins/devkit/devkit-java-tests/testSrc/org/jetbrains/idea/devkit/inspections/quickfix/ConvertToGrayQuickFixTest.java +++ b/plugins/devkit/devkit-java-tests/testSrc/org/jetbrains/idea/devkit/inspections/quickfix/ConvertToGrayQuickFixTest.java @@ -33,4 +33,8 @@ public class ConvertToGrayQuickFixTest extends ConvertToGrayQuickFixTestBase { public void testUseGrayConstantFixInJBColorParam() { doTest(CONVERT_TO_GRAY_FIX_NAME_PATTERN.formatted(25)); } + + public void testUseGrayConstantFixWhenNumberConstantsReferenced() { + doTest(CONVERT_TO_GRAY_FIX_NAME_PATTERN.formatted(125)); + } } diff --git a/plugins/devkit/devkit-kotlin-tests/testData/inspections/useGray/UseGrayConstantWhenAwtColorUsed.kt b/plugins/devkit/devkit-kotlin-tests/testData/inspections/useGray/UseGrayConstantWhenAwtColorUsed.kt new file mode 100644 index 000000000000..c8b55b17cbd7 --- /dev/null +++ b/plugins/devkit/devkit-kotlin-tests/testData/inspections/useGray/UseGrayConstantWhenAwtColorUsed.kt @@ -0,0 +1,36 @@ +import com.intellij.ui.JBColor +import java.awt.Color + +class UseGrayConstantWhenAwtColorUsed { + + companion object { + private val GRAY_CONSTANT = Color(125, 125, 125) + private val NOT_GRAY_CONSTANT = Color(12, 13, 14) + private const val GRAY_VALUE = 125 + } + + @Suppress("UNUSED_VARIABLE") + fun any() { + val myGray1 = Color(25, 25, 25) + val myGray2 = Color(GRAY_VALUE, 125, GRAY_VALUE) + takeColor(Color(125, 125, 125)) + val myGray3: Color = JBColor(Color(25, 25, 25), Color(125, 125, 125)) + takeColor(JBColor(Color(25, 25, 25), Color(125, 125, 125))) + + // correct cases: + val notGray1 = Color(15, 15, 1) + val notGray2 = Color(15, 1, 15) + val notGray3 = Color(1, 15, 15) + val grayWithAlpha1 = Color(15, 16, 17, 100) + val unsupportedGray = Color(0x000000) + val invalidGrayValue1 = Color(-15, -15, -15) + val invalidGrayValue2 = Color(2500, 2500, 2500) + val constantGray1 = Color.LIGHT_GRAY + val constantGray2 = Color.GRAY + } + + @Suppress("UNUSED_PARAMETER") + fun takeColor(color: Color?) { + // do nothing + } +} \ No newline at end of file diff --git a/plugins/devkit/devkit-kotlin-tests/testSrc/org/jetbrains/idea/devkit/kotlin/inspections/KtUseGrayInspectionTest.java b/plugins/devkit/devkit-kotlin-tests/testSrc/org/jetbrains/idea/devkit/kotlin/inspections/KtUseGrayInspectionTest.java new file mode 100644 index 000000000000..46498356d206 --- /dev/null +++ b/plugins/devkit/devkit-kotlin-tests/testSrc/org/jetbrains/idea/devkit/kotlin/inspections/KtUseGrayInspectionTest.java @@ -0,0 +1,25 @@ +// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +package org.jetbrains.idea.devkit.kotlin.inspections; + +import com.intellij.testFramework.TestDataPath; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.idea.devkit.inspections.UseGrayInspectionTestBase; +import org.jetbrains.idea.devkit.kotlin.DevkitKtTestsUtil; + +@TestDataPath("$CONTENT_ROOT/testData/inspections/useGray") +public class KtUseGrayInspectionTest extends UseGrayInspectionTestBase { + + @Override + protected String getBasePath() { + return DevkitKtTestsUtil.TESTDATA_PATH + "inspections/useGray"; + } + + @Override + protected @NotNull String getFileExtension() { + return "kt"; + } + + public void testUseGrayConstantWhenAwtColorUsed() { + doTest(); + } +}