mirror of
https://gitflic.ru/project/openide/openide.git
synced 2025-12-13 15:52:01 +07:00
[java-highlighting] IDEA-352727 Incomplete model with lombok
GitOrigin-RevId: ea0f6f9cf44d704d4ae7e45fa3ba262c1496ffa4
This commit is contained in:
committed by
intellij-monorepo-bot
parent
eb7c026662
commit
de02a19083
@@ -286,6 +286,9 @@ public final class HighlightControlFlowUtil {
|
|||||||
if (!field.hasModifierProperty(PsiModifier.FINAL)) return null;
|
if (!field.hasModifierProperty(PsiModifier.FINAL)) return null;
|
||||||
if (isFieldInitializedAfterObjectConstruction(field)) return null;
|
if (isFieldInitializedAfterObjectConstruction(field)) return null;
|
||||||
if (PsiUtilCore.hasErrorElementChild(field)) return null;
|
if (PsiUtilCore.hasErrorElementChild(field)) return null;
|
||||||
|
if (IncompleteModelUtil.isIncompleteModel(field) && IncompleteModelUtil.canBeAugmented(field.getContainingClass())) {
|
||||||
|
return IncompleteModelUtil.getPendingReferenceHighlightInfo(field);
|
||||||
|
}
|
||||||
String description = JavaErrorBundle.message("variable.not.initialized", field.getName());
|
String description = JavaErrorBundle.message("variable.not.initialized", field.getName());
|
||||||
TextRange range = HighlightNamesUtil.getFieldDeclarationTextRange(field);
|
TextRange range = HighlightNamesUtil.getFieldDeclarationTextRange(field);
|
||||||
HighlightInfo.Builder builder = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(range).descriptionAndTooltip(description);
|
HighlightInfo.Builder builder = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(range).descriptionAndTooltip(description);
|
||||||
|
|||||||
@@ -876,6 +876,12 @@ public final class HighlightMethodUtil {
|
|||||||
String description;
|
String description;
|
||||||
PsiElement elementToHighlight = ObjectUtils.notNull(referenceToMethod.getReferenceNameElement(), referenceToMethod);
|
PsiElement elementToHighlight = ObjectUtils.notNull(referenceToMethod.getReferenceNameElement(), referenceToMethod);
|
||||||
if (element != null && !resolveResult.isAccessible()) {
|
if (element != null && !resolveResult.isAccessible()) {
|
||||||
|
PsiClass qualifierClass = RefactoringChangeUtil.getQualifierClass(referenceToMethod);
|
||||||
|
if (qualifierClass!=null &&
|
||||||
|
IncompleteModelUtil.isIncompleteModel(file) &&
|
||||||
|
IncompleteModelUtil.canBeAugmented(qualifierClass)) {
|
||||||
|
return IncompleteModelUtil.getPendingReferenceHighlightInfo(elementToHighlight);
|
||||||
|
}
|
||||||
description = HighlightUtil.accessProblemDescription(referenceToMethod, element, resolveResult);
|
description = HighlightUtil.accessProblemDescription(referenceToMethod, element, resolveResult);
|
||||||
}
|
}
|
||||||
else if (element != null && !resolveResult.isStaticsScopeCorrect()) {
|
else if (element != null && !resolveResult.isStaticsScopeCorrect()) {
|
||||||
@@ -902,6 +908,9 @@ public final class HighlightMethodUtil {
|
|||||||
IncompleteModelUtil.canBePendingReference(referenceToMethod)) {
|
IncompleteModelUtil.canBePendingReference(referenceToMethod)) {
|
||||||
return IncompleteModelUtil.getPendingReferenceHighlightInfo(elementToHighlight);
|
return IncompleteModelUtil.getPendingReferenceHighlightInfo(elementToHighlight);
|
||||||
}
|
}
|
||||||
|
if (IncompleteModelUtil.isIncompleteModel(file) && IncompleteModelUtil.canBeAugmented(qualifierClass)) {
|
||||||
|
return IncompleteModelUtil.getPendingReferenceHighlightInfo(elementToHighlight);
|
||||||
|
}
|
||||||
description = JavaErrorBundle.message("ambiguous.method.call.no.match", referenceToMethod.getReferenceName(), className);
|
description = JavaErrorBundle.message("ambiguous.method.call.no.match", referenceToMethod.getReferenceName(), className);
|
||||||
}
|
}
|
||||||
else if (qualifierExpression != null &&
|
else if (qualifierExpression != null &&
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ import com.intellij.codeInsight.daemon.impl.HighlightInfo;
|
|||||||
import com.intellij.codeInsight.daemon.impl.HighlightInfoType;
|
import com.intellij.codeInsight.daemon.impl.HighlightInfoType;
|
||||||
import com.intellij.openapi.project.IncompleteDependenciesService;
|
import com.intellij.openapi.project.IncompleteDependenciesService;
|
||||||
import com.intellij.psi.*;
|
import com.intellij.psi.*;
|
||||||
|
import com.intellij.psi.augment.PsiAugmentProvider;
|
||||||
|
import com.intellij.psi.util.CachedValuesManager;
|
||||||
import com.intellij.psi.util.PsiUtil;
|
import com.intellij.psi.util.PsiUtil;
|
||||||
import com.siyeh.ig.psiutils.ClassUtils;
|
import com.siyeh.ig.psiutils.ClassUtils;
|
||||||
import org.jetbrains.annotations.Contract;
|
import org.jetbrains.annotations.Contract;
|
||||||
@@ -224,6 +226,9 @@ final class IncompleteModelUtil {
|
|||||||
if (qualifierTarget == null && canBePendingReference(qualifierRef)) {
|
if (qualifierTarget == null && canBePendingReference(qualifierRef)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
if (qualifierTarget instanceof PsiClass psiClass && canBeAugmented(psiClass)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
if (qualifierTarget instanceof PsiClass cls && isHierarchyResolved(cls)) {
|
if (qualifierTarget instanceof PsiClass cls && isHierarchyResolved(cls)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -247,4 +252,10 @@ final class IncompleteModelUtil {
|
|||||||
return HighlightInfo.newHighlightInfo(HighlightInfoType.PENDING_REFERENCE).range(elementToHighlight)
|
return HighlightInfo.newHighlightInfo(HighlightInfoType.PENDING_REFERENCE).range(elementToHighlight)
|
||||||
.descriptionAndTooltip(JavaErrorBundle.message("incomplete.project.state.pending.reference"));
|
.descriptionAndTooltip(JavaErrorBundle.message("incomplete.project.state.pending.reference"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static boolean canBeAugmented(@Nullable PsiClass targetClass) {
|
||||||
|
if (targetClass == null) return false;
|
||||||
|
return CachedValuesManager.getProjectPsiDependentCache(targetClass,
|
||||||
|
psiClass -> PsiAugmentProvider.canBeAugmentedForIncompleteMode(psiClass));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -267,5 +267,33 @@ public abstract class PsiAugmentProvider implements PossiblyDumbAware {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//</editor-fold>
|
|
||||||
|
/**
|
||||||
|
* Checks if the given PsiClass might be augmented.
|
||||||
|
* This method is usually called in incomplete mode.
|
||||||
|
*
|
||||||
|
* @param psiClass the PsiClass to be checked
|
||||||
|
* @return true if the PsiClass might be augmented for incomplete mode, false otherwise
|
||||||
|
*/
|
||||||
|
protected boolean mightBeAugmentedForIncompleteMode(@NotNull PsiClass psiClass) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the given PsiClass might be augmented.
|
||||||
|
* This method is usually called in incomplete mode.
|
||||||
|
*
|
||||||
|
* @param targetClass the PsiClass to be checked
|
||||||
|
* @return true if the PsiClass might be augmented for incomplete mode, false otherwise
|
||||||
|
*/
|
||||||
|
public static boolean canBeAugmentedForIncompleteMode(@NotNull PsiClass targetClass) {
|
||||||
|
Ref<Boolean> result = Ref.create();
|
||||||
|
|
||||||
|
forEach(targetClass.getProject(), provider -> {
|
||||||
|
boolean augmentedForIncompleteMode = provider.mightBeAugmentedForIncompleteMode(targetClass);
|
||||||
|
result.set(augmentedForIncompleteMode);
|
||||||
|
return !augmentedForIncompleteMode;
|
||||||
|
});
|
||||||
|
return result.get();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -5,7 +5,7 @@ import com.intellij.psi.*;
|
|||||||
import com.intellij.psi.augment.PsiAugmentProvider;
|
import com.intellij.psi.augment.PsiAugmentProvider;
|
||||||
import com.intellij.psi.augment.PsiExtensionMethod;
|
import com.intellij.psi.augment.PsiExtensionMethod;
|
||||||
import com.intellij.psi.impl.source.PsiExtensibleClass;
|
import com.intellij.psi.impl.source.PsiExtensibleClass;
|
||||||
import com.intellij.psi.search.GlobalSearchScope;
|
import com.intellij.util.containers.ContainerUtil;
|
||||||
import com.siyeh.ig.psiutils.InitializationUtils;
|
import com.siyeh.ig.psiutils.InitializationUtils;
|
||||||
import de.plushnikov.intellij.plugin.LombokClassNames;
|
import de.plushnikov.intellij.plugin.LombokClassNames;
|
||||||
import de.plushnikov.intellij.plugin.processor.LombokProcessorManager;
|
import de.plushnikov.intellij.plugin.processor.LombokProcessorManager;
|
||||||
@@ -14,9 +14,6 @@ import de.plushnikov.intellij.plugin.processor.ValProcessor;
|
|||||||
import de.plushnikov.intellij.plugin.processor.lombok.LombokAnnotationProcessor;
|
import de.plushnikov.intellij.plugin.processor.lombok.LombokAnnotationProcessor;
|
||||||
import de.plushnikov.intellij.plugin.processor.method.ExtensionMethodsHelper;
|
import de.plushnikov.intellij.plugin.processor.method.ExtensionMethodsHelper;
|
||||||
import de.plushnikov.intellij.plugin.processor.modifier.ModifierProcessor;
|
import de.plushnikov.intellij.plugin.processor.modifier.ModifierProcessor;
|
||||||
import de.plushnikov.intellij.plugin.psi.LombokLightAnnotationMethodBuilder;
|
|
||||||
import de.plushnikov.intellij.plugin.psi.LombokLightClassBuilder;
|
|
||||||
import de.plushnikov.intellij.plugin.psi.LombokLightMethodBuilder;
|
|
||||||
import de.plushnikov.intellij.plugin.util.PsiAnnotationSearchUtil;
|
import de.plushnikov.intellij.plugin.util.PsiAnnotationSearchUtil;
|
||||||
import de.plushnikov.intellij.plugin.util.PsiAnnotationUtil;
|
import de.plushnikov.intellij.plugin.util.PsiAnnotationUtil;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
@@ -173,4 +170,43 @@ public final class LombokAugmentProvider extends PsiAugmentProvider {
|
|||||||
}
|
}
|
||||||
return ExtensionMethodsHelper.getExtensionMethods(aClass, nameHint, context);
|
return ExtensionMethodsHelper.getExtensionMethods(aClass, nameHint, context);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean mightBeAugmentedForIncompleteMode(@NotNull PsiClass psiClass) {
|
||||||
|
if(!(psiClass instanceof PsiExtensibleClass extensibleClass)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if(!(extensibleClass.getContainingFile() instanceof PsiJavaFile file)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return hasAnyLombokAnnotation(extensibleClass.getAnnotations()) ||
|
||||||
|
ContainerUtil.exists(extensibleClass.getOwnFields(), field -> hasAnyLombokAnnotation(field.getAnnotations())) ||
|
||||||
|
(file.getImportList() != null &&
|
||||||
|
ContainerUtil.exists(file.getImportList().getAllImportStatements(), statement -> {
|
||||||
|
PsiJavaCodeReferenceElement reference = statement.getImportReference();
|
||||||
|
return reference != null && reference.getText().startsWith("lombok");
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the given PsiModifierListOwner has any Lombok annotation.
|
||||||
|
* It is used only for incomplete mode.
|
||||||
|
*
|
||||||
|
* @param annotations The annotations to check for Lombok annotations.
|
||||||
|
* @return true if the modifierListOwner has any Lombok annotation, otherwise false.
|
||||||
|
*/
|
||||||
|
|
||||||
|
private static boolean hasAnyLombokAnnotation(PsiAnnotation @NotNull [] annotations) {
|
||||||
|
return ContainerUtil.exists(annotations, annotation -> {
|
||||||
|
if (annotation == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
String qualifiedName = annotation.getQualifiedName();
|
||||||
|
if (qualifiedName == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return qualifiedName.startsWith("lombok.");
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,34 @@
|
|||||||
|
package com.intellij.java.lomboktest;
|
||||||
|
|
||||||
|
import com.intellij.codeInsight.daemon.LightDaemonAnalyzerTestCase;
|
||||||
|
import com.intellij.openapi.application.WriteAction;
|
||||||
|
import com.intellij.openapi.project.IncompleteDependenciesService;
|
||||||
|
import com.intellij.testFramework.PlatformTestUtil;
|
||||||
|
import org.jetbrains.annotations.NonNls;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
public class LombokIncompleteModeHighlightingTest extends LightDaemonAnalyzerTestCase {
|
||||||
|
|
||||||
|
public void testLombokBasics() { doTest(); }
|
||||||
|
|
||||||
|
public void testLombokBasicsWithExplicitImport() { doTest(); }
|
||||||
|
|
||||||
|
private void doTest() {
|
||||||
|
doTest(getTestName(false) + ".java");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void doTest(String fileName) {
|
||||||
|
var ignored = WriteAction.compute(() -> getProject().getService(IncompleteDependenciesService.class).enterIncompleteState());
|
||||||
|
try {
|
||||||
|
doTest("/plugins/lombok/testData/highlightingIncompleteMode/" + fileName, true, true);
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
WriteAction.run(ignored::close);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected @NonNls @NotNull String getTestDataPath() {
|
||||||
|
return PlatformTestUtil.getCommunityPath();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
import <info descr="Not resolved until the project is fully loaded">lombok</info>.*<EOLError descr="';' expected"></EOLError>
|
||||||
|
|
||||||
|
public final class LombokBasics {
|
||||||
|
public static void main(String[] args) {
|
||||||
|
UserDao userDao = UserDao.<info descr="Not resolved until the project is fully loaded">builder</info>()
|
||||||
|
.<info descr="Not resolved until the project is fully loaded">info</info>("1")
|
||||||
|
.<info descr="Not resolved until the project is fully loaded">build</info>();
|
||||||
|
String name = userDao.<info descr="Not resolved until the project is fully loaded">name</info>();
|
||||||
|
UserChain userChain = new UserChain();
|
||||||
|
String name1 = userChain.<info descr="Not resolved until the project is fully loaded">getName</info>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@<info descr="Not resolved until the project is fully loaded">Getter</info>
|
||||||
|
@<info descr="Not resolved until the project is fully loaded">SuperBuilder</info>
|
||||||
|
class UserDao extends UserId {
|
||||||
|
<info descr="Not resolved until the project is fully loaded">private final String name;</info>
|
||||||
|
<info descr="Not resolved until the project is fully loaded">private final String surname;</info>
|
||||||
|
<info descr="Not resolved until the project is fully loaded">private final String email;</info>
|
||||||
|
}
|
||||||
|
|
||||||
|
@<info descr="Not resolved until the project is fully loaded">SuperBuilder</info>
|
||||||
|
abstract class UserId {
|
||||||
|
<info descr="Not resolved until the project is fully loaded">private final long id;</info>
|
||||||
|
<info descr="Not resolved until the project is fully loaded">private final String info;</info>
|
||||||
|
}
|
||||||
|
|
||||||
|
class UserChain {
|
||||||
|
@<info descr="Not resolved until the project is fully loaded">Getter</info>
|
||||||
|
@<info descr="Not resolved until the project is fully loaded">Setter</info>
|
||||||
|
private String name;
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
import <info descr="Not resolved until the project is fully loaded">lombok</info>.<info descr="Not resolved until the project is fully loaded">Getter</info>;
|
||||||
|
import <info descr="Not resolved until the project is fully loaded">lombok</info>.<info descr="Not resolved until the project is fully loaded">Setter</info>;
|
||||||
|
import <info descr="Not resolved until the project is fully loaded">lombok</info>.<info descr="Not resolved until the project is fully loaded">SuperBuilder</info>;
|
||||||
|
|
||||||
|
public final class LombokBasicsWithExplicitImport {
|
||||||
|
public static void main(String[] args) {
|
||||||
|
UserDao userDao = UserDao.<info descr="Not resolved until the project is fully loaded">builder</info>()
|
||||||
|
.<info descr="Not resolved until the project is fully loaded">info</info>("1")
|
||||||
|
.<info descr="Not resolved until the project is fully loaded">build</info>();
|
||||||
|
String name = userDao.<info descr="Not resolved until the project is fully loaded">name</info>();
|
||||||
|
UserChain userChain = new UserChain();
|
||||||
|
String name1 = userChain.<info descr="Not resolved until the project is fully loaded">getName</info>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@<info descr="Not resolved until the project is fully loaded">Getter</info>
|
||||||
|
@<info descr="Not resolved until the project is fully loaded">SuperBuilder</info>
|
||||||
|
class UserDao extends UserId {
|
||||||
|
<info descr="Not resolved until the project is fully loaded">private final String name;</info>
|
||||||
|
<info descr="Not resolved until the project is fully loaded">private final String surname;</info>
|
||||||
|
<info descr="Not resolved until the project is fully loaded">private final String email;</info>
|
||||||
|
}
|
||||||
|
|
||||||
|
@<info descr="Not resolved until the project is fully loaded">SuperBuilder</info>
|
||||||
|
abstract class UserId {
|
||||||
|
<info descr="Not resolved until the project is fully loaded">private final long id;</info>
|
||||||
|
<info descr="Not resolved until the project is fully loaded">private final String info;</info>
|
||||||
|
}
|
||||||
|
|
||||||
|
class UserChain {
|
||||||
|
@<info descr="Not resolved until the project is fully loaded">Getter</info>
|
||||||
|
@<info descr="Not resolved until the project is fully loaded">Setter</info>
|
||||||
|
private String name;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user