[lombok] IDEA-255688 Using existing functionality to skip checking expressions from field initializer

and to skip checking field initializer expression

GitOrigin-RevId: 67211ecbad65889dabe6385678cc1b7c47a96deb
This commit is contained in:
Michail Plushnikov
2023-12-05 19:35:04 +01:00
committed by intellij-monorepo-bot
parent 7b19a2dd37
commit 4222be044e
11 changed files with 47 additions and 136 deletions

View File

@@ -5,6 +5,7 @@ import com.intellij.psi.*;
import com.intellij.psi.augment.PsiAugmentProvider;
import com.intellij.psi.augment.PsiExtensionMethod;
import com.intellij.psi.impl.source.PsiExtensibleClass;
import com.siyeh.ig.psiutils.InitializationUtils;
import de.plushnikov.intellij.plugin.LombokClassNames;
import de.plushnikov.intellij.plugin.processor.LombokProcessorManager;
import de.plushnikov.intellij.plugin.processor.Processor;
@@ -12,6 +13,7 @@ import de.plushnikov.intellij.plugin.processor.ValProcessor;
import de.plushnikov.intellij.plugin.processor.method.ExtensionMethodsHelper;
import de.plushnikov.intellij.plugin.processor.modifier.ModifierProcessor;
import de.plushnikov.intellij.plugin.util.PsiAnnotationSearchUtil;
import de.plushnikov.intellij.plugin.util.PsiAnnotationUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -61,13 +63,40 @@ public class LombokAugmentProvider extends PsiAugmentProvider {
/*
* The final fields that are marked with Builder.Default contains only possible value
* because user can set another value during the creation of the object.
*
* The fields marked with Getter(lazy=true) contains a value that will be calculated only at first access to the getter-Method
*/
//see de.plushnikov.intellij.plugin.inspection.DataFlowInspectionTest.testDefaultBuilderFinalValueInspectionIsAlwaysThat
//see de.plushnikov.intellij.plugin.inspection.PointlessBooleanExpressionInspectionTest.testPointlessBooleanExpressionBuilderDefault
//see com.intellij.java.lomboktest.LombokHighlightingTest.testBuilderWithDefaultRedundantInitializer
//see com.intellij.java.lomboktest.LombokHighlightingTest.testGetterLazyInvocationProduceNPE
//see com.intellij.java.lomboktest.LombokHighlightingTest.testGetterLazyVariableNotInitialized
@Override
protected boolean fieldInitializerMightBeChanged(@NotNull PsiField field) {
return PsiAnnotationSearchUtil.isAnnotatedWith(field, LombokClassNames.BUILDER_DEFAULT);
if (field.hasAnnotation(LombokClassNames.BUILDER_DEFAULT)) {
return true;
}
final PsiAnnotation getterAnnotation = PsiAnnotationSearchUtil.findAnnotation(field, LombokClassNames.GETTER);
final boolean isLazyGetter = null != getterAnnotation &&
PsiAnnotationUtil.getBooleanAnnotationValue(getterAnnotation, "lazy", false);
if (isLazyGetter) {
final PsiExpression fieldInitializer = field.getInitializer();
if (fieldInitializer instanceof PsiMethodCallExpression methodCallExpression) {
final PsiExpression qualifierExpression = methodCallExpression.getMethodExpression().getQualifierExpression();
if (qualifierExpression instanceof PsiReferenceExpression qualifierReferenceExpression) {
final PsiElement referencedElement = qualifierReferenceExpression.resolve();
if (referencedElement instanceof PsiField referencedField) {
final PsiClass containingClass = referencedField.getContainingClass();
if (containingClass != null) {
return InitializationUtils.isInitializedInConstructors(referencedField, containingClass);
}
}
}
}
}
return isLazyGetter;
}
@Nullable
@@ -91,11 +120,11 @@ public class LombokAugmentProvider extends PsiAugmentProvider {
final List<Psi> emptyResult = Collections.emptyList();
if ((type != PsiClass.class && type != PsiField.class && type != PsiMethod.class) || !(element instanceof PsiExtensibleClass)
|| (element instanceof PsiCompiledElement) // skip compiled classes
) {
) {
return emptyResult;
}
final PsiClass psiClass = (PsiClass) element;
final PsiClass psiClass = (PsiClass)element;
if (!psiClass.getLanguage().isKindOf(JavaLanguage.INSTANCE)) {
return emptyResult;
}
@@ -120,7 +149,7 @@ public class LombokAugmentProvider extends PsiAugmentProvider {
for (Processor processor : LombokProcessorManager.getProcessors(type)) {
final List<? super PsiElement> generatedElements = processor.process(psiClass, nameHint);
for (Object psiElement : generatedElements) {
result.add((Psi) psiElement);
result.add((Psi)psiElement);
}
}
return result;

View File

@@ -1,39 +0,0 @@
package de.plushnikov.intellij.plugin.provider;
import com.intellij.codeInspection.dataFlow.MethodCallProduceNPESupport;
import com.intellij.psi.*;
import com.intellij.psi.util.PsiTreeUtil;
import com.siyeh.ig.psiutils.InitializationUtils;
import de.plushnikov.intellij.plugin.LombokClassNames;
import de.plushnikov.intellij.plugin.util.PsiAnnotationSearchUtil;
import de.plushnikov.intellij.plugin.util.PsiAnnotationUtil;
import org.jetbrains.annotations.NotNull;
public class LombokMethodCallExpressionNPESupport implements MethodCallProduceNPESupport {
@Override
public boolean ignoreMethodCallExpression(@NotNull PsiExpression psiExpression) {
final PsiField field = PsiTreeUtil.getParentOfType(psiExpression, PsiField.class);
if (field == null) {
return false;
}
final PsiAnnotation getterAnnotation = PsiAnnotationSearchUtil.findAnnotation(field, LombokClassNames.GETTER);
final boolean isLazyGetter = null != getterAnnotation && PsiAnnotationUtil.getBooleanAnnotationValue(getterAnnotation, "lazy", false);
if (isLazyGetter) {
final PsiReference reference = psiExpression.getReference();
if (reference != null) {
PsiElement referencedElement = reference.resolve();
if (!(referencedElement instanceof PsiField referencedField)) {
return false;
}
PsiClass containingClass = referencedField.getContainingClass();
if (containingClass != null) {
return InitializationUtils.isInitializedInConstructors(referencedField, containingClass);
}
}
}
return isLazyGetter;
}
}

View File

@@ -1,26 +0,0 @@
package de.plushnikov.intellij.plugin.provider;
import com.intellij.codeInsight.daemon.impl.analysis.VariableInitializedBeforeUsageSupport;
import com.intellij.psi.*;
import com.intellij.psi.util.PsiTreeUtil;
import de.plushnikov.intellij.plugin.LombokClassNames;
import de.plushnikov.intellij.plugin.util.PsiAnnotationUtil;
import org.jetbrains.annotations.NotNull;
/**
* A class that implements the VariableInitializedBeforeUsageSupport interface to provide support for Lombok annotated variables.
* It checks if a variable expression should be ignored based on Lombok annotations.
*/
public class LombokVariableInitializedBeforeUsageSupport implements VariableInitializedBeforeUsageSupport {
@Override
public boolean ignoreVariableExpression(@NotNull PsiReferenceExpression psiExpression, @NotNull PsiVariable psiVariable) {
final PsiField field = PsiTreeUtil.getParentOfType(psiExpression, PsiField.class);
if (field == null) {
return false;
}
final PsiAnnotation getterAnnotation = field.getAnnotation(LombokClassNames.GETTER);
return null != getterAnnotation && PsiAnnotationUtil.getBooleanAnnotationValue(getterAnnotation, "lazy", false);
}
}

View File

@@ -53,8 +53,6 @@
<lang.psiAugmentProvider implementation="de.plushnikov.intellij.plugin.provider.LombokAugmentProvider"/>
<lang.jvm.annotationPackageSupport implementation="de.plushnikov.intellij.plugin.provider.LombokAnnotationSupport"/>
<lang.jvm.ignoreAnnotationParamSupport implementation="de.plushnikov.intellij.plugin.provider.LombokDefaultAnnotationParamSupport"/>
<lang.jvm.ignoreVariableInitializedBeforeUsageSupport implementation="de.plushnikov.intellij.plugin.provider.LombokVariableInitializedBeforeUsageSupport"/>
<lang.jvm.ignoreMethodCallExpressionNPESupport implementation="de.plushnikov.intellij.plugin.provider.LombokMethodCallExpressionNPESupport"/>
<implicitUsageProvider implementation="de.plushnikov.intellij.plugin.provider.LombokImplicitUsageProvider"/>
<projectConfigurable groupId="language"
key="plugin.settings.title" bundle="messages.LombokBundle"

View File

@@ -14,6 +14,7 @@ public class GetterLazyInvocationProduceNPE {
}
private Bar <warning descr="Field 'bar' may be 'final'">bar</warning>;
private Bar <warning descr="Private field 'bar2' is never assigned">bar2</warning>;
private Car car;
public GetterLazyInvocationProduceNPE(Bar bar, Car car) {
@@ -21,11 +22,15 @@ public class GetterLazyInvocationProduceNPE {
this.car = car;
}
// without warning
// without warning, because of lazy getter and initialized in constructor
@Getter(lazy = true)
private final String barString = bar.sayHello();
//with warning!
// with warning, because of lazy getter and NOT initialized in constructor
@Getter(lazy = true)
private final String bar2String = bar2.<warning descr="Method invocation 'sayHello' will produce 'NullPointerException'">sayHello</warning>();
//with warning, because of NOT lazy getter
@Getter
private final String carString = car.<warning descr="Method invocation 'sayHello' will produce 'NullPointerException'">sayHello</warning>();