IDEA-189985 - UseGrayInspection - rewrite to UAST

GitOrigin-RevId: c0b766b88a34cfccf4a349ffb0ae8cdd0d986ae8
This commit is contained in:
Karol Lewandowski
2022-10-12 13:18:05 +00:00
committed by intellij-monorepo-bot
parent 900f0bea95
commit eee32437ec
8 changed files with 142 additions and 43 deletions
@@ -104,7 +104,7 @@
enabledByDefault="true" level="WARNING"
implementationClass="org.jetbrains.idea.devkit.inspections.InspectionDescriptionNotFoundInspection"
key="inspection.inspection.description.not.found.inspection.display.name"/>
<localInspection language="JAVA" shortName="InspectionUsingGrayColors"
<localInspection language="UAST" shortName="InspectionUsingGrayColors"
groupPathKey="inspections.group.path" groupKey="inspections.group.code"
enabledByDefault="true" level="WARNING"
implementationClass="org.jetbrains.idea.devkit.inspections.UseGrayInspection"
@@ -3,78 +3,85 @@ package org.jetbrains.idea.devkit.inspections;
import com.intellij.codeInspection.ProblemsHolder;
import com.intellij.psi.*;
import com.intellij.psi.impl.JavaConstantExpressionEvaluator;
import com.intellij.uast.UastHintedVisitorAdapter;
import com.intellij.ui.Gray;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.idea.devkit.DevKitBundle;
import org.jetbrains.idea.devkit.inspections.quickfix.ConvertToGrayQuickFix;
import org.jetbrains.uast.UCallExpression;
import org.jetbrains.uast.UElement;
import org.jetbrains.uast.UExpression;
import org.jetbrains.uast.UastCallKind;
import org.jetbrains.uast.visitor.AbstractUastNonRecursiveVisitor;
import java.awt.*;
import java.util.List;
/**
* @author Konstantin Bulenkov
*/
public class UseGrayInspection extends DevKitInspectionBase {
public class UseGrayInspection extends DevKitUastInspectionBase {
private static final String AWT_COLOR_CLASS_NAME = Color.class.getName();
private static final String GRAY_CLASS_NAME = Gray.class.getName();
@SuppressWarnings("unchecked")
public static final Class<? extends UElement>[] 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<UExpression> 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<UExpression> 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;
}
@@ -7,10 +7,13 @@ class UseGrayConstantWhenAwtColorUsed {
private static final Color GRAY_CONSTANT = <warning descr="'java.awt.Color' used for gray">new Color(125, 125, 125)</warning>;
private static final Color NOT_GRAY_CONSTANT = new Color(12, 13, 14);
private static final int GRAY_VALUE = 125;
void any() {
Color myGray1 = <warning descr="'java.awt.Color' used for gray">new Color(25, 25, 25)</warning>;
Color myGray2 = <warning descr="'java.awt.Color' used for gray">new Color(GRAY_VALUE, 125, GRAY_VALUE)</warning>;
takeColor(<warning descr="'java.awt.Color' used for gray">new Color(125, 125, 125)</warning>);
Color myGray2 = new JBColor(<warning descr="'java.awt.Color' used for gray">new Color(25, 25, 25)</warning>, <warning descr="'java.awt.Color' used for gray">new Color(125, 125, 125)</warning>);
Color myGray3 = new JBColor(<warning descr="'java.awt.Color' used for gray">new Color(25, 25, 25)</warning>, <warning descr="'java.awt.Color' used for gray">new Color(125, 125, 125)</warning>);
takeColor(new JBColor(<warning descr="'java.awt.Color' used for gray">new Color(25, 25, 25)</warning>, <warning descr="'java.awt.Color' used for gray">new Color(125, 125, 125)</warning>));
// correct cases:
@@ -0,0 +1,11 @@
import java.awt.Color;
class UseGrayConstantFixWhenNumberConstantsReferenced {
private static final int GRAY_VALUE = 125;
void any() {
Color gray = <warning descr="'java.awt.Color' used for gray">new Col<caret>or(GRAY_VALUE, 125, GRAY_VALUE)</warning>;
}
}
@@ -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;
}
}
@@ -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));
}
}
@@ -0,0 +1,36 @@
import com.intellij.ui.JBColor
import java.awt.Color
class UseGrayConstantWhenAwtColorUsed {
companion object {
private val GRAY_CONSTANT = <warning descr="'java.awt.Color' used for gray">Color(125, 125, 125)</warning>
private val NOT_GRAY_CONSTANT = Color(12, 13, 14)
private const val GRAY_VALUE = 125
}
@Suppress("UNUSED_VARIABLE")
fun any() {
val myGray1 = <warning descr="'java.awt.Color' used for gray">Color(25, 25, 25)</warning>
val myGray2 = <warning descr="'java.awt.Color' used for gray">Color(GRAY_VALUE, 125, GRAY_VALUE)</warning>
takeColor(<warning descr="'java.awt.Color' used for gray">Color(125, 125, 125)</warning>)
val myGray3: Color = JBColor(<warning descr="'java.awt.Color' used for gray">Color(25, 25, 25)</warning>, <warning descr="'java.awt.Color' used for gray">Color(125, 125, 125)</warning>)
takeColor(JBColor(<warning descr="'java.awt.Color' used for gray">Color(25, 25, 25)</warning>, <warning descr="'java.awt.Color' used for gray">Color(125, 125, 125)</warning>))
// 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
}
}
@@ -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();
}
}