mirror of
https://gitflic.ru/project/openide/openide.git
synced 2025-12-13 15:52:01 +07:00
[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:
committed by
intellij-monorepo-bot
parent
8a33c22212
commit
1ac9d8ac4e
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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.");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.");
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user