[lombok] IDEA-352727 Support incomplete mode

- change tests
- extract everything connected to dumb and incomplete mode into utils

GitOrigin-RevId: 979f4333917f68d947c9d28c04d7e6dfef8400ae
This commit is contained in:
Mikhail Pyltsin
2024-05-31 14:12:56 +02:00
committed by intellij-monorepo-bot
parent 8a33c22212
commit 1ac9d8ac4e
16 changed files with 257 additions and 249 deletions

View File

@@ -5,7 +5,7 @@ import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiElementFinder;
import com.intellij.psi.impl.file.impl.JavaFileManager;
import com.intellij.psi.search.GlobalSearchScope;
import de.plushnikov.intellij.plugin.util.IncompleteModeUtil;
import de.plushnikov.intellij.plugin.util.DumbIncompleteModeUtil;
import de.plushnikov.intellij.plugin.util.LombokLibraryUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -24,7 +24,7 @@ public final class LombokElementFinder extends PsiElementFinder {
@Override
public PsiClass findClass(@NotNull String qualifiedName, @NotNull GlobalSearchScope scope) {
if (!LombokLibraryUtil.hasLombokLibrary(myProject) &&
!IncompleteModeUtil.isIncompleteMode(myProject)) {
!DumbIncompleteModeUtil.isIncompleteMode(myProject)) {
return null;
}

View File

@@ -22,7 +22,7 @@ import de.plushnikov.intellij.plugin.processor.method.BuilderClassMethodProcesso
import de.plushnikov.intellij.plugin.processor.method.BuilderMethodProcessor;
import de.plushnikov.intellij.plugin.processor.method.DelegateMethodProcessor;
import de.plushnikov.intellij.plugin.processor.modifier.*;
import de.plushnikov.intellij.plugin.util.PsiAnnotationSearchUtil;
import de.plushnikov.intellij.plugin.util.DumbIncompleteModeUtil;
import org.jetbrains.annotations.NotNull;
import java.util.Arrays;
@@ -368,8 +368,8 @@ public final class LombokProcessorManager {
return Collections.emptyList();
}
String qualifiedName = psiAnnotation.getQualifiedName();
if (PsiAnnotationSearchUtil.isDumbOrIncompleteMode(psiAnnotation)) {
qualifiedName = PsiAnnotationSearchUtil.findLombokAnnotationQualifiedNameInIncompleteMode(psiAnnotation);
if (DumbIncompleteModeUtil.isDumbOrIncompleteMode(psiAnnotation)) {
qualifiedName = DumbIncompleteModeUtil.findLombokAnnotationQualifiedNameInDumbIncompleteMode(psiAnnotation);
}
if (StringUtil.isEmpty(qualifiedName) || !qualifiedName.contains("lombok")) {
return Collections.emptyList();

View File

@@ -8,9 +8,6 @@ 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.intellij.psi.util.CachedValuesManager;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.containers.ContainerUtil;
import com.siyeh.ig.psiutils.InitializationUtils;
import de.plushnikov.intellij.plugin.LombokClassNames;
import de.plushnikov.intellij.plugin.processor.LombokProcessorManager;
@@ -19,7 +16,7 @@ import de.plushnikov.intellij.plugin.processor.ValProcessor;
import de.plushnikov.intellij.plugin.processor.lombok.LombokAnnotationProcessor;
import de.plushnikov.intellij.plugin.processor.method.ExtensionMethodsHelper;
import de.plushnikov.intellij.plugin.processor.modifier.ModifierProcessor;
import de.plushnikov.intellij.plugin.util.IncompleteModeUtil;
import de.plushnikov.intellij.plugin.util.DumbIncompleteModeUtil;
import de.plushnikov.intellij.plugin.util.PsiAnnotationSearchUtil;
import de.plushnikov.intellij.plugin.util.PsiAnnotationUtil;
import org.jetbrains.annotations.NotNull;
@@ -51,7 +48,7 @@ public final class LombokAugmentProvider extends PsiAugmentProvider implements P
@Override
protected Set<String> transformModifiers(@NotNull PsiModifierList modifierList, @NotNull final Set<String> modifiers) {
// skip if no lombok library is present
if (!hasLombokLibrary(modifierList.getProject()) && !isIncompleteModeWithLombokAnnotation(modifierList)) {
if (!hasLombokLibrary(modifierList.getProject()) && !DumbIncompleteModeUtil.isIncompleteModeWithLombokAnnotation(modifierList)) {
return modifiers;
}
@@ -157,7 +154,7 @@ public final class LombokAugmentProvider extends PsiAugmentProvider implements P
}
// skip processing if disabled, or no lombok library is present
if (!hasLombokLibrary(element.getProject()) && !isIncompleteModeWithLombokAnnotation(psiClass)) {
if (!hasLombokLibrary(element.getProject()) && !DumbIncompleteModeUtil.isIncompleteModeWithLombokAnnotation(psiClass)) {
return emptyResult;
}
if (psiClass.isAnnotationType() && type == PsiMethod.class) {
@@ -204,79 +201,4 @@ public final class LombokAugmentProvider extends PsiAugmentProvider implements P
}
return ExtensionMethodsHelper.getExtensionMethods(aClass, nameHint, context);
}
/**
* Checks if the project is in incomplete mode and the class contains Lombok annotation.
* Incomplete mode means that the project contains incomplete dependencies.
* There is no purpose to be absolutely accurate, but it can help to reduce false red-code highlighting and help with some completion
* This method can be quite slow, but it calls only in incomplete mode and cache value for file
* @param context The PsiElement to check for Lombok annotations.
* @return true if the project is in incomplete mode and the class has any Lombok annotation or if any of the class fields have any Lombok annotation;
* otherwise, false.
*/
private static boolean isIncompleteModeWithLombokAnnotation(@NotNull PsiElement context) {
if (!IncompleteModeUtil.isIncompleteMode(context)) {
return false;
}
if (context.getLanguage() != JavaLanguage.INSTANCE) {
return false;
}
if (context instanceof PsiModifierList modifierList && hasAnyLombokAnnotation(modifierList.getAnnotations())) {
return true;
}
PsiClass psiClass = context instanceof PsiClass castedClass ? castedClass :
PsiTreeUtil.getParentOfType(context, PsiClass.class);
if (psiClass == null) return false;
return CachedValuesManager.getProjectPsiDependentCache(psiClass, psiElement -> {
if (!(psiElement.getContainingFile() instanceof PsiJavaFile file)) {
return false;
}
if (file.getImportList() != null && ContainerUtil.exists(file.getImportList().getAllImportStatements(), statement -> {
PsiJavaCodeReferenceElement reference = statement.getImportReference();
return reference != null && reference.getText().startsWith("lombok");
})) {
return true;
}
while (psiElement != null) {
if (psiElement instanceof PsiExtensibleClass extensibleClass &&
(hasAnyLombokAnnotation(extensibleClass.getAnnotations()) ||
ContainerUtil.exists(extensibleClass.getOwnFields(), field -> hasAnyLombokAnnotation(field.getAnnotations())) ||
ContainerUtil.exists(extensibleClass.getOwnMethods(), method -> hasAnyLombokAnnotation(method.getAnnotations())) ||
(file.getImportList() != null && ContainerUtil.exists(file.getImportList().getAllImportStatements(), statement -> {
PsiJavaCodeReferenceElement reference = statement.getImportReference();
return reference != null && reference.getText().startsWith("lombok");
})))) {
return true;
}
psiElement = PsiTreeUtil.getParentOfType(psiElement, PsiClass.class);
}
return false;
});
}
/**
* 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.getText();
if (qualifiedName == null) {
return false;
}
return qualifiedName.startsWith("@lombok.");
});
}
}

View File

@@ -0,0 +1,181 @@
package de.plushnikov.intellij.plugin.util;
import com.intellij.lang.java.JavaLanguage;
import com.intellij.openapi.project.DumbService;
import com.intellij.openapi.project.IncompleteDependenciesService;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.registry.Registry;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.*;
import com.intellij.psi.impl.source.PsiExtensibleClass;
import com.intellij.psi.util.CachedValuesManager;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.MultiMap;
import de.plushnikov.intellij.plugin.processor.LombokProcessorManager;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
public final class DumbIncompleteModeUtil {
private DumbIncompleteModeUtil() {
}
public static boolean isIncompleteMode(@NotNull Project project) {
return Registry.is("lombok.incomplete.mode.enabled", false) &&
!project.getService(IncompleteDependenciesService.class).getState().isComplete();
}
public static boolean isDumbOrIncompleteMode(@NotNull PsiElement context) {
Project project = context.getProject();
return (DumbService.isDumb(project) && Registry.is("lombok.dumb.mode.enabled", false)) ||
isIncompleteMode(context.getProject());
}
/**
* Searches for a specific annotation in the list of annotations of a PsiModifierListOwner in dumb mode.
*
* @param owner the PsiModifierListOwner whose annotations to search.
* @param annotationFQN the fully qualified name of the annotation to search for.
* @return the found PsiAnnotation object if the annotation is found, or null if not found.
*/
static @Nullable PsiAnnotation findAnnotationInDumbOrIncompleteMode(@NotNull PsiModifierListOwner owner, @NotNull String annotationFQN) {
for (PsiAnnotation annotation : owner.getAnnotations()) {
if (hasQualifiedNameInDumbOrIncompleteMode(annotation, annotationFQN)) {
return annotation;
}
}
return null;
}
/**
* Finds the fully qualified name of a Lombok annotation based only on psi structure, without resolving.
* Only annotations which have processors are supported
*
* @param psiAnnotation the PsiAnnotation object representing the Lombok annotation
* @return the fully qualified name of the Lombok annotation, or null if the annotation is unresolved or not a Lombok annotation
*/
public static @Nullable String findLombokAnnotationQualifiedNameInDumbIncompleteMode(@NotNull PsiAnnotation psiAnnotation) {
String qualifiedName = psiAnnotation.getQualifiedName();
if (StringUtil.isEmpty(qualifiedName)) return null;
if (qualifiedName.startsWith("lombok")) return qualifiedName;
LombokProcessorManager instance = LombokProcessorManager.getInstance();
MultiMap<String, String> names = instance.getOurSupportedShortNames();
Collection<String> fullQualifiedNames = names.get(qualifiedName);
for (String fullQualifiedName : fullQualifiedNames) {
if (hasQualifiedNameInDumbOrIncompleteMode(psiAnnotation, fullQualifiedName)) {
return fullQualifiedName;
}
}
return qualifiedName;
}
/**
* Checks if the given annotation has a qualified name in dumb mode.
* It is not fully accurate because it can't do resolving
*
* @param annotation the PsiAnnotation object to check.
* @param fqn the fully qualified name to check against.
* @return true if the annotation has the specified qualified name in dumb mode, otherwise false.
*/
static boolean hasQualifiedNameInDumbOrIncompleteMode(PsiAnnotation annotation, @NotNull String fqn) {
PsiJavaCodeReferenceElement referenceElement = annotation.getNameReferenceElement();
if (referenceElement == null) return false;
String qualifiedName = referenceElement.getReferenceName();
if (qualifiedName == null) return false;
if (qualifiedName.equals(fqn) || ("java.lang." + qualifiedName).equals(fqn)) return true;
String referenceElementText = referenceElement.getText();
if (referenceElementText != null && referenceElementText.equals(fqn)) return true;
if (!fqn.endsWith(qualifiedName)) return false;
PsiFile containingFile = annotation.getContainingFile();
if (!(containingFile instanceof PsiJavaFile javaFile)) {
return false;
}
String packageName = StringUtil.getPackageName(fqn);
PsiImportList importList = javaFile.getImportList();
if (importList == null) return false;
int indexMayByOuterClass = fqn.length() - qualifiedName.length() - 1;
String mayBeOuterClass = indexMayByOuterClass > 0 ? fqn.substring(0, indexMayByOuterClass) : null;
return importList.findOnDemandImportStatement(packageName) != null ||
importList.findSingleClassImportStatement(fqn) != null ||
(mayBeOuterClass!=null && importList.findSingleClassImportStatement(mayBeOuterClass) != null);
}
/**
* Checks if the project is in incomplete mode and the class contains Lombok annotation.
* Incomplete mode means that the project contains incomplete dependencies.
* There is no purpose to be absolutely accurate, but it can help to reduce false red-code highlighting and help with some completion
* This method can be quite slow, but it calls only in incomplete mode and cache value for file
* @param context The PsiElement to check for Lombok annotations.
* @return true if the project is in incomplete mode and the class has any Lombok annotation or if any of the class fields have any Lombok annotation;
* otherwise, false.
*/
public static boolean isIncompleteModeWithLombokAnnotation(@NotNull PsiElement context) {
if (!isIncompleteMode(context.getProject())) {
return false;
}
if (context.getLanguage() != JavaLanguage.INSTANCE) {
return false;
}
if (context instanceof PsiModifierList modifierList && hasAnyLombokAnnotation(modifierList.getAnnotations())) {
return true;
}
PsiClass psiClass = context instanceof PsiClass castedClass ? castedClass :
PsiTreeUtil.getParentOfType(context, PsiClass.class);
if (psiClass == null) return false;
return CachedValuesManager.getProjectPsiDependentCache(psiClass, psiElement -> {
if (!(psiElement.getContainingFile() instanceof PsiJavaFile file)) {
return false;
}
if (file.getImportList() != null && ContainerUtil.exists(file.getImportList().getAllImportStatements(), statement -> {
return canBeLombokImport(statement);
})) {
return true;
}
while (psiElement != null) {
if (psiElement instanceof PsiExtensibleClass extensibleClass &&
(hasAnyLombokAnnotation(extensibleClass.getAnnotations()) ||
ContainerUtil.exists(extensibleClass.getOwnFields(), field -> hasAnyLombokAnnotation(field.getAnnotations())) ||
ContainerUtil.exists(extensibleClass.getOwnMethods(), method -> hasAnyLombokAnnotation(method.getAnnotations())) ||
(file.getImportList() != null && ContainerUtil.exists(file.getImportList().getAllImportStatements(), statement -> {
return canBeLombokImport(statement);
})))) {
return true;
}
psiElement = PsiTreeUtil.getParentOfType(psiElement, PsiClass.class);
}
return false;
});
}
private static boolean canBeLombokImport(@NotNull PsiImportStatementBase 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.getText();
if (qualifiedName == null) {
return false;
}
return qualifiedName.startsWith("@lombok.");
});
}
}

View File

@@ -1,23 +0,0 @@
package de.plushnikov.intellij.plugin.util;
import com.intellij.openapi.project.IncompleteDependenciesService;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.registry.Registry;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiJavaFile;
import org.jetbrains.annotations.NotNull;
public final class IncompleteModeUtil {
private IncompleteModeUtil() {
}
public static boolean isIncompleteMode(@NotNull PsiElement context) {
if (!(context.getContainingFile() instanceof PsiJavaFile)) return false;
return isIncompleteMode(context.getProject());
}
public static boolean isIncompleteMode(@NotNull Project project) {
return Registry.is("lombok.incomplete.mode.enabled", false) &&
!project.getService(IncompleteDependenciesService.class).getState().isComplete();
}
}

View File

@@ -85,7 +85,7 @@ public final class LombokProcessorUtil {
public static Collection<String> getOldOnX(@NotNull PsiAnnotation psiAnnotation, @NotNull String parameterName) {
PsiAnnotationMemberValue onXValue;
if (PsiAnnotationSearchUtil.isDumbOrIncompleteMode(psiAnnotation)) {
if (DumbIncompleteModeUtil.isDumbOrIncompleteMode(psiAnnotation)) {
onXValue = psiAnnotation.findDeclaredAttributeValue(parameterName);
}
else {
@@ -99,7 +99,7 @@ public final class LombokProcessorUtil {
}
public static Collection<String> getNewOnX(@NotNull PsiAnnotation psiAnnotation, @NotNull String parameterName) {
if (PsiAnnotationSearchUtil.isDumbOrIncompleteMode(psiAnnotation) || psiAnnotation.hasAttribute(parameterName)) {
if (DumbIncompleteModeUtil.isDumbOrIncompleteMode(psiAnnotation) || psiAnnotation.hasAttribute(parameterName)) {
final Collection<PsiAnnotation> annotations =
PsiAnnotationUtil.getAnnotationValues(psiAnnotation, parameterName, PsiAnnotation.class, List.of());
return collectAnnotationStrings(annotations);

View File

@@ -1,12 +1,10 @@
package de.plushnikov.intellij.plugin.util;
import com.intellij.openapi.project.DumbService;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.*;
import com.intellij.psi.PsiAnnotation;
import com.intellij.psi.PsiJavaCodeReferenceElement;
import com.intellij.psi.PsiModifierListOwner;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.MultiMap;
import de.plushnikov.intellij.plugin.processor.LombokProcessorManager;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -17,57 +15,19 @@ public final class PsiAnnotationSearchUtil {
@Nullable
public static PsiAnnotation findAnnotation(@NotNull PsiModifierListOwner psiModifierListOwner, @NotNull String annotationFQN) {
if (isDumbOrIncompleteMode(psiModifierListOwner)) {
return findAnnotationInDumbMode(psiModifierListOwner, annotationFQN);
if (DumbIncompleteModeUtil.isDumbOrIncompleteMode(psiModifierListOwner)) {
return DumbIncompleteModeUtil.findAnnotationInDumbOrIncompleteMode(psiModifierListOwner, annotationFQN);
}
return psiModifierListOwner.getAnnotation(annotationFQN);
}
private static @Nullable PsiAnnotation findAnnotationInDumbMode(@NotNull PsiModifierListOwner owner, @NotNull String annotationFQN) {
for (PsiAnnotation annotation : owner.getAnnotations()) {
if (hasQualifiedNameInDumbMode(annotation, annotationFQN)) {
return annotation;
}
}
return null;
}
private static boolean hasQualifiedNameInDumbMode(PsiAnnotation annotation, @NotNull String fqn) {
PsiJavaCodeReferenceElement referenceElement = annotation.getNameReferenceElement();
if (referenceElement == null) return false;
String qualifiedName = referenceElement.getReferenceName();
if (qualifiedName == null) return false;
if (qualifiedName.equals(fqn)) return true;
String referenceElementText = referenceElement.getText();
if (referenceElementText != null && referenceElementText.equals(fqn)) return true;
if (!fqn.endsWith(qualifiedName)) return false;
PsiFile containingFile = annotation.getContainingFile();
if (!(containingFile instanceof PsiJavaFile javaFile)) {
return false;
}
String packageName = StringUtil.getPackageName(fqn);
PsiImportList importList = javaFile.getImportList();
if (importList == null) return false;
int indexMayByOuterClass = fqn.length() - qualifiedName.length() - 1;
String mayBeOuterClass = indexMayByOuterClass > 0 ? fqn.substring(0, indexMayByOuterClass) : null;
return importList.findOnDemandImportStatement(packageName) != null ||
importList.findSingleClassImportStatement(fqn) != null ||
(mayBeOuterClass!=null && importList.findSingleClassImportStatement(mayBeOuterClass) != null);
}
public static boolean isDumbOrIncompleteMode(@NotNull PsiElement context) {
Project project = context.getProject();
return DumbService.isDumb(project) ||
IncompleteModeUtil.isIncompleteMode(context);
}
@Nullable
public static PsiAnnotation findAnnotation(@NotNull PsiModifierListOwner psiModifierListOwner, String @NotNull ... annotationFQNs) {
boolean isDumbMode = isDumbOrIncompleteMode(psiModifierListOwner);
boolean isDumbMode = DumbIncompleteModeUtil.isDumbOrIncompleteMode(psiModifierListOwner);
for (String annotationFQN : annotationFQNs) {
PsiAnnotation annotation;
if (isDumbMode) {
annotation = findAnnotationInDumbMode(psiModifierListOwner, annotationFQN);
annotation = DumbIncompleteModeUtil.findAnnotationInDumbOrIncompleteMode(psiModifierListOwner, annotationFQN);
}
else {
annotation = psiModifierListOwner.getAnnotation(annotationFQN);
@@ -80,8 +40,8 @@ public final class PsiAnnotationSearchUtil {
}
public static boolean isAnnotatedWith(@NotNull PsiModifierListOwner psiModifierListOwner, @NotNull String annotationFQN) {
if (isDumbOrIncompleteMode(psiModifierListOwner)) {
return findAnnotationInDumbMode(psiModifierListOwner, annotationFQN) != null;
if (DumbIncompleteModeUtil.isDumbOrIncompleteMode(psiModifierListOwner)) {
return DumbIncompleteModeUtil.findAnnotationInDumbOrIncompleteMode(psiModifierListOwner, annotationFQN) != null;
}
return psiModifierListOwner.hasAnnotation(annotationFQN);
}
@@ -133,49 +93,17 @@ public final class PsiAnnotationSearchUtil {
public static boolean checkAnnotationHasOneOfFQNs(@NotNull PsiAnnotation psiAnnotation,
String @NotNull ... annotationFQNs) {
if (isDumbOrIncompleteMode(psiAnnotation)) {
return ContainerUtil.or(annotationFQNs, fqn-> hasQualifiedNameInDumbMode(psiAnnotation, fqn));
if (DumbIncompleteModeUtil.isDumbOrIncompleteMode(psiAnnotation)) {
return ContainerUtil.or(annotationFQNs, fqn-> DumbIncompleteModeUtil.hasQualifiedNameInDumbOrIncompleteMode(psiAnnotation, fqn));
}
return ContainerUtil.or(annotationFQNs, psiAnnotation::hasQualifiedName);
}
public static boolean checkAnnotationHasOneOfFQNs(@NotNull PsiAnnotation psiAnnotation,
@NotNull Set<String> annotationFQNs) {
if (isDumbOrIncompleteMode(psiAnnotation)) {
return ContainerUtil.or(annotationFQNs, fqn -> hasQualifiedNameInDumbMode(psiAnnotation, fqn));
if (DumbIncompleteModeUtil.isDumbOrIncompleteMode(psiAnnotation)) {
return ContainerUtil.or(annotationFQNs, fqn -> DumbIncompleteModeUtil.hasQualifiedNameInDumbOrIncompleteMode(psiAnnotation, fqn));
}
return ContainerUtil.or(annotationFQNs, psiAnnotation::hasQualifiedName);
}
/**
* Finds the fully qualified name of a Lombok annotation based only on psi structure, without resolving.
* Only annotations which have processors are supported
*
* @param psiAnnotation the PsiAnnotation object representing the Lombok annotation
* @return the fully qualified name of the Lombok annotation, or null if the annotation is unresolved or not a Lombok annotation
*/
public static @Nullable String findLombokAnnotationQualifiedNameInIncompleteMode(@NotNull PsiAnnotation psiAnnotation) {
String qualifiedName = psiAnnotation.getQualifiedName();
if (StringUtil.isEmpty(qualifiedName)) return null;
if (qualifiedName.startsWith("lombok")) return qualifiedName;
LombokProcessorManager instance = LombokProcessorManager.getInstance();
MultiMap<String, String> names = instance.getOurSupportedShortNames();
Collection<String> fullQualifiedNames = names.get(qualifiedName);
for (String fullQualifiedName : fullQualifiedNames) {
if (hasQualifiedNameInDumbMode(psiAnnotation, fullQualifiedName)) {
return fullQualifiedName;
}
}
String proposedQualifiedName = "lombok." + qualifiedName;
if (hasQualifiedNameInDumbMode(psiAnnotation, proposedQualifiedName)) {
qualifiedName = proposedQualifiedName;
}
else {
proposedQualifiedName = "lombok.experimental." + qualifiedName;
if (hasQualifiedNameInDumbMode(psiAnnotation, proposedQualifiedName)) {
qualifiedName = proposedQualifiedName;
}
}
return qualifiedName;
}
}

View File

@@ -42,7 +42,7 @@ public final class PsiAnnotationUtil {
@NotNull List<T> defaultDumbValue) {
Collection<T> result = Collections.emptyList();
PsiAnnotationMemberValue attributeValue;
if (PsiAnnotationSearchUtil.isDumbOrIncompleteMode(psiAnnotation)) {
if (DumbIncompleteModeUtil.isDumbOrIncompleteMode(psiAnnotation)) {
attributeValue = psiAnnotation.findDeclaredAttributeValue(parameter);
if (attributeValue == null) return defaultDumbValue;
}
@@ -84,7 +84,7 @@ public final class PsiAnnotationUtil {
public static String getEnumAnnotationValue(@NotNull PsiAnnotation psiAnnotation, @NotNull String attributeName, @NotNull String defaultValue) {
PsiAnnotationMemberValue attrValue = psiAnnotation.findDeclaredAttributeValue(attributeName);
if (IncompleteModeUtil.isIncompleteMode(psiAnnotation) && attrValue instanceof PsiReferenceExpression referenceExpression) {
if (DumbIncompleteModeUtil.isIncompleteMode(psiAnnotation.getProject()) && attrValue instanceof PsiReferenceExpression referenceExpression) {
//more or less good approximation if it is a complete mode
return referenceExpression.getReferenceName();
}

View File

@@ -5,6 +5,7 @@ import com.intellij.openapi.application.WriteAction;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.DumbService;
import com.intellij.openapi.project.IncompleteDependenciesService;
import com.intellij.openapi.util.registry.Registry;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.pom.PomNamedTarget;
import com.intellij.psi.*;
@@ -13,7 +14,7 @@ import com.intellij.testFramework.DumbModeTestUtils;
import com.intellij.testFramework.LightProjectDescriptor;
import com.intellij.util.ThrowableRunnable;
import com.intellij.util.containers.ContainerUtil;
import de.plushnikov.intellij.plugin.util.IncompleteModeUtil;
import de.plushnikov.intellij.plugin.util.DumbIncompleteModeUtil;
import de.plushnikov.intellij.plugin.util.PsiAnnotationSearchUtil;
import de.plushnikov.intellij.plugin.util.PsiElementUtil;
import org.jetbrains.annotations.NotNull;
@@ -39,7 +40,15 @@ public abstract class AbstractLombokParsingTestCase extends AbstractLombokLightC
@NotNull
protected List<ModeRunnerType> modes() {
return List.of(ModeRunnerType.INCOMPLETE, ModeRunnerType.DUMB, ModeRunnerType.NORMAL);
ArrayList<ModeRunnerType> types = new ArrayList<>();
if (Registry.is("lombok.incomplete.mode.enabled", false)) {
types.add(ModeRunnerType.INCOMPLETE);
}
if (Registry.is("lombok.dumb.mode.enabled", false)) {
types.add(ModeRunnerType.DUMB);
}
types.add(ModeRunnerType.NORMAL);
return types;
}
/**
@@ -267,10 +276,21 @@ public abstract class AbstractLombokParsingTestCase extends AbstractLombokLightC
.filter(Predicate.not(annotationsToIgnoreList()::contains))
.toList();
assertTrue("Annotations are different for " + afterModifierList.getParent() + ": " + beforeAnnotations + "/" + afterAnnotations,
beforeAnnotations.size() == afterAnnotations.size()
&& beforeAnnotations.containsAll(afterAnnotations)
&& afterAnnotations.containsAll(beforeAnnotations));
if (DumbIncompleteModeUtil.isIncompleteMode(beforeModifierList.getProject())) {
//In this case, it is impossible to resolve, and even if there are annotations we can't guess their fqn correctly.
//Let's check one by one considering import statements
for (String annotation : afterAnnotations) {
assertTrue("For " + afterModifierList.getParent() + " " + beforeAnnotations + " doesn't contain the annotation: " + annotation,
ContainerUtil.or(beforeModifierList.getAnnotations(),
an -> PsiAnnotationSearchUtil.checkAnnotationHasOneOfFQNs(an, Set.of(annotation))));
}
}
else {
assertTrue("Annotations are different for " + afterModifierList.getParent() + ": " + beforeAnnotations + "/" + afterAnnotations,
beforeAnnotations.size() == afterAnnotations.size()
&& beforeAnnotations.containsAll(afterAnnotations)
&& afterAnnotations.containsAll(beforeAnnotations));
}
// compare annotations parameter list
for (PsiAnnotation beforeAnnotation : beforeModifierList.getAnnotations()) {
@@ -293,16 +313,8 @@ public abstract class AbstractLombokParsingTestCase extends AbstractLombokLightC
}
private static @NotNull String getAnnotationQualifiedName(@NotNull PsiAnnotation annotation) {
String qualifiedName = DumbService.getInstance(annotation.getProject())
return DumbService.getInstance(annotation.getProject())
.computeWithAlternativeResolveEnabled(() -> annotation.getQualifiedName());
if (!qualifiedName.contains(".") && IncompleteModeUtil.isIncompleteMode(annotation)) {
String lombokAnnotation = PsiAnnotationSearchUtil.findLombokAnnotationQualifiedNameInIncompleteMode(annotation);
if (lombokAnnotation.startsWith("lombok")) {
qualifiedName = lombokAnnotation;
}
}
return qualifiedName;
}
private void compareMethods(PsiClass beforeClass, PsiClass afterClass) {

View File

@@ -1,19 +0,0 @@
package de.plushnikov.intellij.plugin;
import org.junit.runners.BlockJUnit4ClassRunner;
import org.junit.runners.model.InitializationError;
public class IncompleteAndDumbModeRunner extends BlockJUnit4ClassRunner {
public @interface InsertModeType {
}
public IncompleteAndDumbModeRunner(Class<?> testClass) throws InitializationError {
super(testClass);
}
}

View File

@@ -28,6 +28,8 @@ public class FieldDefaultsWithoutLombokImportTest extends AbstractLombokConfigSy
//incomplete mode is not supported, because files don't contain any lombok annotations,
//checking lombok.config is expensive, so skip such cases
//after returning to normal mode, caches will be dropped
return List.of(ModeRunnerType.NORMAL, ModeRunnerType.DUMB);
return super.modes()
.stream().filter(t -> t != ModeRunnerType.INCOMPLETE)
.toList();
}
}

View File

@@ -23,6 +23,8 @@ public class FieldNameConstantsTest extends AbstractLombokConfigSystemTestCase {
protected @NotNull List<ModeRunnerType> modes() {
//now incomplete mode is not supported for this processor, because it depends on the lombok version
//after returning to normal mode, caches will be dropped
return List.of(ModeRunnerType.NORMAL, ModeRunnerType.DUMB);
return super.modes()
.stream().filter(t -> t != ModeRunnerType.INCOMPLETE)
.toList();
}
}

View File

@@ -26,6 +26,8 @@ public class FieldNameConstantsOldTest extends AbstractLombokParsingTestCase {
protected @NotNull List<ModeRunnerType> modes() {
//now incomplete mode is not supported for this processor, because it depends on the lombok version
//after returning to normal mode, caches will be dropped
return List.of(ModeRunnerType.NORMAL, ModeRunnerType.DUMB);
return super.modes()
.stream().filter(t -> t != ModeRunnerType.INCOMPLETE)
.toList();
}
}

View File

@@ -38,6 +38,8 @@ public class FieldNameConstantsTest extends AbstractLombokParsingTestCase {
protected @NotNull List<ModeRunnerType> modes() {
//now incomplete mode is not supported for this processor, because it depends on the lombok version
//after returning to normal mode, caches will be dropped
return List.of(ModeRunnerType.NORMAL, ModeRunnerType.DUMB);
return super.modes()
.stream().filter(t -> t != ModeRunnerType.INCOMPLETE)
.toList();
}
}

View File

@@ -6,14 +6,14 @@ import javax.validation.Valid;
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import javax.validation.constraints.Size;
import org.jetbrains.annotations.NotNull;
import lombok.NonNull;
public class TestOnX {
@NotNull
@NonNull
private final Integer someIntField;
/** @deprecated */
@Deprecated
@NotNull
@NonNull
private String someStringField;
private float someFloatField;
@@ -30,7 +30,7 @@ public class TestOnX {
@Inject
@Named("myName1")
public TestOnX(@NotNull Integer someIntField, @NotNull String someStringField) {
public TestOnX(@NonNull Integer someIntField, @NonNull String someStringField) {
if (someIntField == null) {
throw new NullPointerException("someIntField is marked non-null but is null");
} else if (someStringField == null) {
@@ -43,7 +43,7 @@ public class TestOnX {
@Inject
@Named("myName2")
public TestOnX(@NotNull Integer someIntField, @NotNull String someStringField, float someFloatField) {
public TestOnX(@NonNull Integer someIntField, @NonNull String someStringField, float someFloatField) {
if (someIntField == null) {
throw new NullPointerException("someIntField is marked non-null but is null");
} else if (someStringField == null) {
@@ -108,7 +108,7 @@ public class TestOnX {
}
@Max(100)
@NotNull
@NonNull
public Integer getSomeIntField() {
return this.someIntField;
}
@@ -118,7 +118,7 @@ public class TestOnX {
@Size(
max = 20
)
@NotNull
@NonNull
public String getSomeStringField() {
return this.someStringField;
}
@@ -128,7 +128,7 @@ public class TestOnX {
@Size(
min = 10
)
public void setSomeStringField(@Size(min = 15) @NotNull String someStringField) {
public void setSomeStringField(@Size(min = 15) @NonNull String someStringField) {
if (someStringField == null) {
throw new NullPointerException("someStringField is marked non-null but is null");
} else {
@@ -136,7 +136,7 @@ public class TestOnX {
}
}
@NotNull
@NonNull
public TestOnX withSomeFloatField(@Min(1) float someFloatField) {
return this.someFloatField == someFloatField ? this : new TestOnX(this.someIntField, this.someStringField, someFloatField);
}

View File

@@ -8,7 +8,6 @@ import javax.validation.Valid;
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import javax.validation.constraints.Size;
import org.jetbrains.annotations.NotNull;
@ToString
@RequiredArgsConstructor(onConstructor_ = {@Inject, @Named("myName1")})
@@ -16,16 +15,16 @@ import org.jetbrains.annotations.NotNull;
@EqualsAndHashCode(onParam_ = @Valid)
public class TestOnX {
@Getter(onMethod_ = @Max(100))
@NotNull
@NonNull
private final Integer someIntField;
@NotNull
@NonNull
@Deprecated
@Getter(onMethod_ = @Size(max = 20))
@Setter(onMethod_ = @Size(min = 10), onParam_ = @Size(min = 15))
private String someStringField;
@With(onMethod_ = @NotNull, onParam_ = @Min(1))
@With(onMethod_ = @NonNull, onParam_ = @Min(1))
private float someFloatField;
public static void main(String[] args) {