[java, import-module] resolve "import module" jep 476 IDEA-355535

GitOrigin-RevId: 4a4ee8cfafdba657d062317d3e014061af69a3e4
This commit is contained in:
Aleksey Dobrynin
2024-07-17 12:13:22 +02:00
committed by intellij-monorepo-bot
parent e9a6246df3
commit c96bc2d64f
17 changed files with 654 additions and 41 deletions

View File

@@ -0,0 +1,14 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.codeInsight.daemon.impl.analysis;
import com.intellij.psi.JavaModuleGraphHelper;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiJavaModule;
import org.jetbrains.annotations.Nullable;
public class JavaModuleGraphHelperImpl extends JavaModuleGraphHelper {
@Override
public @Nullable PsiJavaModule findDescriptorByElement(@Nullable PsiElement element) {
return JavaModuleGraphUtil.findDescriptorByElement(element);
}
}

View File

@@ -456,6 +456,8 @@
serviceImplementation="com.intellij.psi.impl.file.PsiPackageImplementationHelperImpl"/>
<applicationService serviceInterface="com.intellij.psi.codeStyle.JavaFileCodeStyleFacadeFactory"
serviceImplementation="com.intellij.psi.codeStyle.JavaFileCodeStyleFacadeImpl$Factory"/>
<applicationService serviceInterface="com.intellij.psi.JavaModuleGraphHelper"
serviceImplementation="com.intellij.codeInsight.daemon.impl.analysis.JavaModuleGraphHelperImpl"/>
<projectService serviceImplementation="com.intellij.jarRepository.RemoteRepositoriesConfiguration"/>
<projectService serviceImplementation="com.intellij.jarRepository.services.MavenRepositoryServicesManager"/>

View File

@@ -0,0 +1,15 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.psi;
import com.intellij.openapi.application.ApplicationManager;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.Nullable;
public abstract class JavaModuleGraphHelper {
@Contract("null->null")
public abstract @Nullable PsiJavaModule findDescriptorByElement(@Nullable PsiElement element);
public static JavaModuleGraphHelper getInstance() {
return ApplicationManager.getApplication().getService(JavaModuleGraphHelper.class);
}
}

View File

@@ -8,9 +8,11 @@ import com.intellij.psi.*;
import com.intellij.psi.impl.java.stubs.JavaStubElementTypes;
import com.intellij.psi.impl.java.stubs.PsiJavaModuleStub;
import com.intellij.psi.impl.source.SourceTreeToPsiMap;
import com.intellij.psi.impl.source.resolve.JavaResolveUtil;
import com.intellij.psi.impl.source.tree.JavaElementType;
import com.intellij.psi.impl.source.tree.TreeElement;
import com.intellij.psi.javadoc.PsiDocComment;
import com.intellij.psi.scope.PsiScopeProcessor;
import com.intellij.psi.stubs.StubElement;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.containers.ContainerUtil;
@@ -164,4 +166,12 @@ public class ClsJavaModuleImpl extends ClsRepositoryPsiElement<PsiJavaModuleStub
public String toString() {
return "PsiJavaModule:" + getName();
}
@Override
public boolean processDeclarations(@NotNull PsiScopeProcessor processor,
@NotNull ResolveState state,
@Nullable PsiElement lastParent,
@NotNull PsiElement place) {
return JavaResolveUtil.processJavaModuleExports(this, processor, state, lastParent, place);
}
}

View File

@@ -25,7 +25,7 @@ public final class ClsPackageAccessibilityStatementImpl extends ClsRepositoryPsi
super(stub);
myPackageReference = atomicLazyNullable(() -> {
String packageName = getPackageName();
return packageName != null ? new ClsJavaCodeReferenceElementImpl(this, packageName) : null;
return packageName != null ? new ClsPackageReferenceElementImpl(this, packageName) : null;
});
myModuleReferences = atomicLazy(() -> ContainerUtil.map(getStub().getTargets(), target -> new ClsJavaModuleReferenceElementImpl(this, target)));
}

View File

@@ -0,0 +1,200 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.psi.impl.compiled;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.*;
import com.intellij.psi.impl.cache.TypeInfo;
import com.intellij.psi.impl.source.resolve.ResolveCache;
import com.intellij.psi.impl.source.tree.JavaElementType;
import com.intellij.psi.impl.source.tree.TreeElement;
import com.intellij.psi.infos.CandidateInfo;
import com.intellij.psi.scope.PsiScopeProcessor;
import com.intellij.psi.util.PsiUtilCore;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.NotNull;
import java.util.Objects;
public class ClsPackageReferenceElementImpl extends ClsElementImpl implements PsiJavaCodeReferenceElement, PsiQualifiedReferenceElement {
private final PsiElement myParent;
private final String myQualifiedName;
public ClsPackageReferenceElementImpl(@NotNull PsiElement parent,
@NotNull String canonicalText) {
myParent = parent;
myQualifiedName = TypeInfo.internFrequentType(canonicalText);
}
@Override
public PsiElement @NotNull [] getChildren() {
return PsiElement.EMPTY_ARRAY;
}
@Override
public PsiElement getParent() {
return myParent;
}
@Override
public PsiReference getReference() {
return this;
}
@Override
public @NotNull String getCanonicalText() {
return myQualifiedName;
}
private static class Resolver implements ResolveCache.PolyVariantContextResolver<ClsPackageReferenceElementImpl> {
public static final Resolver INSTANCE = new Resolver();
@Override
public JavaResolveResult @NotNull [] resolve(@NotNull ClsPackageReferenceElementImpl ref,
@NotNull PsiFile containingFile,
boolean incompleteCode) {
final JavaResolveResult resolveResult = ref.advancedResolveImpl(containingFile);
return resolveResult == null ? JavaResolveResult.EMPTY_ARRAY : new JavaResolveResult[]{resolveResult};
}
}
private JavaResolveResult advancedResolveImpl(@NotNull PsiFile containingFile) {
PsiElement element = getParent();
if (element instanceof PsiPackage) return new CandidateInfo(element, PsiSubstitutor.EMPTY);
PsiElement resolve = JavaPsiFacade.getInstance(containingFile.getProject()).findPackage(myQualifiedName);
if (resolve == null) return null;
return new CandidateInfo(resolve, PsiSubstitutor.EMPTY);
}
@Override
public @NotNull JavaResolveResult advancedResolve(boolean incompleteCode) {
final JavaResolveResult[] results = multiResolve(incompleteCode);
if (results.length == 1) return results[0];
return JavaResolveResult.EMPTY;
}
@Override
public JavaResolveResult @NotNull [] multiResolve(boolean incompleteCode) {
PsiFile file = getContainingFile();
if (file == null) {
PsiElement root = SyntaxTraverser.psiApi().parents(this).last();
PsiUtilCore.ensureValid(Objects.requireNonNull(root));
throw new PsiInvalidElementAccessException(this, "parent=" + myParent + ", root=" + root + ", canonicalText=" + myQualifiedName);
}
final ResolveCache resolveCache = ResolveCache.getInstance(file.getProject());
ResolveResult[] results = resolveCache.resolveWithCaching(this, Resolver.INSTANCE, true, incompleteCode, file);
if (results.length == 0) return JavaResolveResult.EMPTY_ARRAY;
return (JavaResolveResult[])results;
}
@Override
public PsiElement resolve() {
return advancedResolve(false).getElement();
}
@Override
public void processVariants(@NotNull PsiScopeProcessor processor) {
throw new RuntimeException("Variants are not available for light references");
}
@Override
public PsiElement getReferenceNameElement() {
return null;
}
@Override
public PsiReferenceParameterList getParameterList() {
return null;
}
@Override
public String getQualifiedName() {
return myQualifiedName;
}
@Override
public String getReferenceName() {
return myQualifiedName;
}
@Override
public PsiElement handleElementRename(@NotNull String newElementName) throws IncorrectOperationException {
throw cannotModifyException(this);
}
@Override
public PsiElement bindToElement(@NotNull PsiElement element) throws IncorrectOperationException {
throw cannotModifyException(this);
}
@Override
public boolean isReferenceTo(@NotNull PsiElement element) {
if (!(element instanceof PsiClass)) return false;
PsiClass aClass = (PsiClass)element;
return myQualifiedName.equals(aClass.getQualifiedName()) || getManager().areElementsEquivalent(resolve(), element);
}
@Override
public Object @NotNull [] getVariants() {
throw new RuntimeException("Variants are not available for references to compiled code");
}
@Override
public boolean isSoft() {
return false;
}
@Override
public void appendMirrorText(final int indentLevel, final @NotNull StringBuilder buffer) {
buffer.append(getCanonicalText());
}
@Override
protected void setMirror(@NotNull TreeElement element) throws InvalidMirrorException {
setMirrorCheckingType(element, JavaElementType.JAVA_CODE_REFERENCE);
}
@Override
public void accept(@NotNull PsiElementVisitor visitor) {
if (visitor instanceof JavaElementVisitor) {
((JavaElementVisitor)visitor).visitReferenceElement(this);
}
else {
visitor.visitElement(this);
}
}
@Override
public @NotNull TextRange getRangeInElement() {
return new TextRange(0, getTextLength());
}
@Override
public @NotNull PsiElement getElement() {
return this;
}
@Override
public PsiType @NotNull [] getTypeParameters() {
return PsiType.EMPTY_ARRAY;
}
@Override
public boolean isQualified() {
return false;
}
@Override
public PsiElement getQualifier() {
return null;
}
@Override
public String getText() {
return myQualifiedName;
}
@Override
public String toString() {
return "ClsPackageReferenceElement:" + getText();
}
}

View File

@@ -11,7 +11,9 @@ import com.intellij.openapi.vfs.VfsUtilCore;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.VirtualFileVisitor;
import com.intellij.psi.*;
import com.intellij.psi.impl.source.resolve.JavaResolveUtil;
import com.intellij.psi.javadoc.PsiDocComment;
import com.intellij.psi.scope.PsiScopeProcessor;
import com.intellij.psi.util.PsiUtil;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.ApiStatus;
@@ -196,10 +198,12 @@ public final class LightJavaModule extends LightElement implements PsiJavaModule
private static class LightPackageAccessibilityStatement extends LightElement implements PsiPackageAccessibilityStatement {
private final String myPackageName;
private final PsiJavaCodeReferenceElement myPackageReference;
LightPackageAccessibilityStatement(@NotNull PsiManager manager, @NotNull String packageName) {
super(manager, JavaLanguage.INSTANCE);
myPackageName = packageName;
myPackageReference = new LightPackageReference(manager, packageName);
}
@Override
@@ -218,7 +222,7 @@ public final class LightJavaModule extends LightElement implements PsiJavaModule
@Override
public @Nullable PsiJavaCodeReferenceElement getPackageReference() {
return null;
return myPackageReference;
}
@Override
@@ -297,6 +301,14 @@ public final class LightJavaModule extends LightElement implements PsiJavaModule
return name;
}
@Override
public boolean processDeclarations(@NotNull PsiScopeProcessor processor,
@NotNull ResolveState state,
@Nullable PsiElement lastParent,
@NotNull PsiElement place) {
return JavaResolveUtil.processJavaModuleExports(this, processor, state, lastParent, place);
}
private static class Patterns {
private static final Pattern VERSION = Pattern.compile("-(\\d+(\\.|$))");
private static final Pattern NON_NAME = Pattern.compile("[^A-Za-z\\d]");

View File

@@ -54,7 +54,6 @@ public class PsiJavaCodeReferenceElementImpl extends CompositePsiElement impleme
CLASS_OR_PACKAGE_NAME_KIND,
CLASS_FQ_NAME_KIND,
CLASS_FQ_OR_PACKAGE_NAME_KIND,
MODULE_NAME_KIND,
CLASS_IN_QUALIFIED_NEW_KIND,
}
@@ -335,7 +334,6 @@ public class PsiJavaCodeReferenceElementImpl extends CompositePsiElement impleme
case PACKAGE_NAME_KIND:
case CLASS_FQ_NAME_KIND:
case CLASS_FQ_OR_PACKAGE_NAME_KIND:
case MODULE_NAME_KIND:
return getNormalizedText();
default:
@@ -530,9 +528,6 @@ public class PsiJavaCodeReferenceElementImpl extends CompositePsiElement impleme
}
return result;
case MODULE_NAME_KIND:
return JavaResolveResult.EMPTY_ARRAY;
}
throw new IllegalArgumentException("Unexpected kind: " + kind);
@@ -598,13 +593,6 @@ public class PsiJavaCodeReferenceElementImpl extends CompositePsiElement impleme
else {
throw cannotBindError(element, kind, element+ " is not a PsiClass but "+element.getClass());
}
case MODULE_NAME_KIND:
if (element instanceof PsiJavaModule) {
return bindToModule((PsiJavaModule)element, containingFile);
}
else {
throw cannotBindError(element, kind, element+ " is not a PsiJavaModule but "+element.getClass());
}
default:
throw new IllegalArgumentException("Unexpected kind: " + kind);
}
@@ -718,7 +706,6 @@ public class PsiJavaCodeReferenceElementImpl extends CompositePsiElement impleme
case PACKAGE_NAME_KIND:
case CLASS_FQ_NAME_KIND:
case CLASS_FQ_OR_PACKAGE_NAME_KIND:
case MODULE_NAME_KIND:
return true;
default:
@@ -747,13 +734,6 @@ public class PsiJavaCodeReferenceElementImpl extends CompositePsiElement impleme
return ref;
}
private @NotNull PsiElement bindToModule(@NotNull PsiJavaModule aModule, @NotNull PsiFile containingFile) throws IncorrectOperationException {
PsiJavaParserFacade parserFacade = JavaPsiFacade.getInstance(containingFile.getProject()).getParserFacade();
PsiJavaCodeReferenceElement ref = parserFacade.createReferenceFromText(aModule.getName(), getParent());
getTreeParent().replaceChildInternal(this, (TreeElement)ref.getNode());
return ref;
}
@Override
public boolean isReferenceTo(@NotNull PsiElement element) {
PsiFile containingFile = getContainingFile();
@@ -807,8 +787,6 @@ public class PsiJavaCodeReferenceElementImpl extends CompositePsiElement impleme
return qName.equals(getCanonicalText(false, null, containingFile));
}
return false;
case MODULE_NAME_KIND:
return false;
default:
throw new IllegalArgumentException("Unexpected kind: " + kind);
}
@@ -881,9 +859,6 @@ public class PsiJavaCodeReferenceElementImpl extends CompositePsiElement impleme
case CLASS_FQ_OR_PACKAGE_NAME_KIND:
filter = isQualified() ? new OrFilter(ElementClassFilter.CLASS, ElementClassFilter.PACKAGE) : ElementClassFilter.PACKAGE;
break;
case MODULE_NAME_KIND:
filter = ElementClassFilter.MODULE;
break;
default:
throw new RuntimeException("Unknown reference type");
}
@@ -939,8 +914,6 @@ public class PsiJavaCodeReferenceElementImpl extends CompositePsiElement impleme
}
}
return;
case MODULE_NAME_KIND:
return;
default:
throw new RuntimeException("Unknown reference type");
}
@@ -1035,7 +1008,6 @@ public class PsiJavaCodeReferenceElementImpl extends CompositePsiElement impleme
case PACKAGE_NAME_KIND:
case CLASS_FQ_NAME_KIND:
case CLASS_FQ_OR_PACKAGE_NAME_KIND:
case MODULE_NAME_KIND:
return getNormalizedText(); // there cannot be any <...>
default:

View File

@@ -399,9 +399,22 @@ public abstract class PsiJavaFileBaseImpl extends PsiFileImpl implements PsiJava
if (shouldProcessClasses && !processOnDemandTypeImports(state, place, processor)) return false;
if (shouldProcessClasses && !processModules(state, place, processor)) return false;
return !shouldProcessClasses || processImplicitImports(state, place, processor);
}
private boolean processModules(@NotNull ResolveState state, @NotNull PsiElement place, @NotNull PsiScopeProcessor processor) {
for (PsiImportModuleStatement statement : getImportModuleStatements()) {
PsiElement resolved = statement.resolve();
if (resolved != null) {
processor.handleEvent(JavaScopeProcessorEvent.SET_CURRENT_FILE_CONTEXT, statement);
if (!processOnDemandTarget(resolved, state, place, processor)) return false;
}
}
return true;
}
private PsiImportStaticStatement @NotNull [] getImportStaticStatements() {
return getImportList() != null ? getImportList().getImportStaticStatements() : PsiImportStaticStatement.EMPTY_ARRAY;
}
@@ -410,6 +423,10 @@ public abstract class PsiJavaFileBaseImpl extends PsiFileImpl implements PsiJava
return getImportList() != null ? getImportList().getImportStatements() : PsiImportStatement.EMPTY_ARRAY;
}
private PsiImportModuleStatement @NotNull [] getImportModuleStatements() {
return getImportList() != null ? getImportList().getImportModuleStatements() : PsiImportModuleStatement.EMPTY_ARRAY;
}
private boolean processCurrentPackage(@NotNull ResolveState state, @NotNull PsiElement place, @NotNull PsiScopeProcessor processor) {
processor.handleEvent(JavaScopeProcessorEvent.SET_CURRENT_FILE_CONTEXT, null);
PsiPackage aPackage = JavaPsiFacade.getInstance(myManager.getProject()).findPackage(getPackageName());
@@ -489,12 +506,34 @@ public abstract class PsiJavaFileBaseImpl extends PsiFileImpl implements PsiJava
if (!processor.execute(inner, substitutor)) return false;
}
}
else if (target instanceof PsiJavaModule) {
return processModuleDeclaration(substitutor, place, (PsiJavaModule)target, processor);
}
else {
LOG.error("Unexpected target type: " + target);
}
return true;
}
private static boolean processModuleDeclaration(@NotNull ResolveState state,
@NotNull PsiElement place,
@NotNull PsiJavaModule target,
@NotNull PsiScopeProcessor processor) {
processor = new DelegatingScopeProcessor(processor) {
@Override
public @Nullable <T> T getHint(@NotNull Key<T> hintKey) {
if (hintKey == ElementClassHint.KEY) {
//noinspection unchecked
return (T)(ElementClassHint)kind -> kind == ElementClassHint.DeclarationKind.CLASS;
} else {
return super.getHint(hintKey);
}
}
};
return target.processDeclarations(processor, state, null, place);
}
@Override
public void accept(@NotNull PsiElementVisitor visitor){
if (visitor instanceof JavaElementVisitor) {

View File

@@ -8,8 +8,10 @@ import com.intellij.psi.*;
import com.intellij.psi.impl.JavaPsiImplementationHelper;
import com.intellij.psi.impl.java.stubs.JavaStubElementTypes;
import com.intellij.psi.impl.java.stubs.PsiJavaModuleStub;
import com.intellij.psi.impl.source.resolve.JavaResolveUtil;
import com.intellij.psi.impl.source.tree.JavaElementType;
import com.intellij.psi.javadoc.PsiDocComment;
import com.intellij.psi.scope.PsiScopeProcessor;
import com.intellij.psi.search.ProjectScope;
import com.intellij.psi.search.SearchScope;
import com.intellij.psi.util.CachedValueProvider;
@@ -140,6 +142,14 @@ public class PsiJavaModuleImpl extends JavaStubPsiElement<PsiJavaModuleStub> imp
return getNameIdentifier().getTextOffset();
}
@Override
public boolean processDeclarations(@NotNull PsiScopeProcessor processor,
@NotNull ResolveState state,
@Nullable PsiElement lastParent,
@NotNull PsiElement place) {
return JavaResolveUtil.processJavaModuleExports(this, processor, state, lastParent, place);
}
@Override
public PsiElement getOriginalElement() {
return CachedValuesManager.getCachedValue(this, () -> {

View File

@@ -260,6 +260,9 @@ public class ClassResolverProcessor implements PsiScopeProcessor, NameHint, Elem
if (myCurrentFileContext instanceof PsiImportStatementBase) {
return ((PsiImportStatementBase)myCurrentFileContext).isOnDemand();
}
if (myCurrentFileContext instanceof PsiImportModuleStatement) {
return true;
}
return false;
}

View File

@@ -4,8 +4,10 @@ package com.intellij.psi.impl.source.resolve;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.projectRoots.JavaSdkVersion;
import com.intellij.openapi.projectRoots.JavaVersionService;
import com.intellij.openapi.roots.ProjectRootModificationTracker;
import com.intellij.psi.*;
import com.intellij.psi.impl.PsiImplUtil;
import com.intellij.psi.impl.PsiJavaModuleModificationTracker;
import com.intellij.psi.impl.source.DummyHolder;
import com.intellij.psi.impl.source.DummyHolderFactory;
import com.intellij.psi.impl.source.resolve.graphInference.PatternInference;
@@ -14,12 +16,16 @@ import com.intellij.psi.impl.source.tree.TreeElement;
import com.intellij.psi.impl.source.tree.java.PsiExpressionListImpl;
import com.intellij.psi.infos.CandidateInfo;
import com.intellij.psi.javadoc.PsiDocComment;
import com.intellij.psi.scope.ElementClassHint;
import com.intellij.psi.scope.PsiScopeProcessor;
import com.intellij.psi.util.*;
import com.intellij.util.ObjectUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.List;
public final class JavaResolveUtil {
public static PsiClass getContextClass(@NotNull PsiElement element) {
PsiElement prev = element;
@@ -35,9 +41,9 @@ public final class JavaResolveUtil {
return null;
}
public static PsiElement findParentContextOfClass(PsiElement element, Class<?> aClass, boolean strict){
public static PsiElement findParentContextOfClass(PsiElement element, Class<?> aClass, boolean strict) {
PsiElement scope = strict ? element.getContext() : element;
while(scope != null && !aClass.isInstance(scope)){
while (scope != null && !aClass.isInstance(scope)) {
scope = scope.getContext();
}
return scope;
@@ -49,8 +55,8 @@ public final class JavaResolveUtil {
@NotNull PsiElement place,
@Nullable PsiClass accessObjectClass,
@Nullable PsiElement fileResolveScope) {
if (place instanceof PsiDirectory &&
modifierList != null &&
if (place instanceof PsiDirectory &&
modifierList != null &&
accessObjectClass == null &&
PsiUtil.getAccessLevel(modifierList) == PsiUtil.ACCESS_LEVEL_PACKAGE_LOCAL) {
PsiPackage aPackage = JavaDirectoryService.getInstance().getPackage((PsiDirectory)place);
@@ -118,7 +124,7 @@ public final class JavaResolveUtil {
else {
contextClass = PsiTreeUtil.getContextOfType(place, PsiClass.class, false);
if (isInClassAnnotationParameterList(place, contextClass)) return false;
if (contextClass instanceof PsiAnonymousClass &&
if (contextClass instanceof PsiAnonymousClass &&
PsiTreeUtil.isAncestor(((PsiAnonymousClass)contextClass).getArgumentList(), place, true)) {
contextClass = PsiTreeUtil.getContextOfType(contextClass, PsiClass.class, true);
}
@@ -177,8 +183,8 @@ public final class JavaResolveUtil {
}
public static boolean canAccessProtectedMember(@NotNull PsiMember member,
@NotNull PsiClass memberClass,
@Nullable PsiClass accessObjectClass, @Nullable PsiClass contextClass, boolean isStatic) {
@NotNull PsiClass memberClass,
@Nullable PsiClass accessObjectClass, @Nullable PsiClass contextClass, boolean isStatic) {
while (contextClass != null) {
if (InheritanceUtil.isInheritorOrSelf(contextClass, memberClass, true)) {
if (member instanceof PsiClass || isStatic || accessObjectClass == null
@@ -204,13 +210,13 @@ public final class JavaResolveUtil {
private static boolean ignoreReferencedElementAccessibility(PsiFile placeFile) {
return placeFile instanceof FileResolveScopeProvider &&
((FileResolveScopeProvider) placeFile).ignoreReferencedElementAccessibility() &&
((FileResolveScopeProvider)placeFile).ignoreReferencedElementAccessibility() &&
!PsiImplUtil.isInServerPage(placeFile);
}
public static boolean isInJavaDoc(@NotNull PsiElement place) {
PsiElement scope = place;
while(scope != null){
while (scope != null) {
if (scope instanceof PsiDocComment) return true;
if (scope instanceof PsiMember || scope instanceof PsiMethodCallExpression || scope instanceof PsiFile) return false;
scope = scope.getContext();
@@ -267,8 +273,9 @@ public final class JavaResolveUtil {
PsiClass resultClass = (PsiClass)resultElement;
if (resultClass.hasTypeParameters()) {
PsiSubstitutor substitutor = resolveResult.getSubstitutor();
result[i] = pattern != null && ref.getTypeParameterCount() == 0
? PatternInference.inferPatternGenerics(resolveResult, pattern, resultClass, JavaPsiPatternUtil.getContextType(pattern))
result[i] = pattern != null && ref.getTypeParameterCount() == 0
? PatternInference.inferPatternGenerics(resolveResult, pattern, resultClass,
JavaPsiPatternUtil.getContextType(pattern))
: new CandidateInfo(resolveResult, substitutor) {
@Override
public @NotNull PsiSubstitutor getSubstitutor() {
@@ -319,4 +326,49 @@ public final class JavaResolveUtil {
return JavaDirectoryService.getInstance().getPackage(directory);
}
public static boolean processJavaModuleExports(@NotNull PsiJavaModule module,
@NotNull PsiScopeProcessor processor,
@NotNull ResolveState state,
@Nullable PsiElement lastParent,
@NotNull PsiElement place) {
processor.handleEvent(PsiScopeProcessor.Event.SET_DECLARATION_HOLDER, module);
ElementClassHint classHint = processor.getHint(ElementClassHint.KEY);
if (classHint != null && !classHint.shouldProcess(ElementClassHint.DeclarationKind.CLASS)) return true;
PsiJavaModule currentModule = JavaModuleGraphHelper.getInstance().findDescriptorByElement(place);
List<PsiPackageAccessibilityStatement> exports = getAllDeclaredExports(module);
for (PsiPackageAccessibilityStatement export : exports) {
PsiJavaCodeReferenceElement aPackage = export.getPackageReference();
if (aPackage == null) continue;
List<String> accessibleModules = export.getModuleNames();
if (!accessibleModules.isEmpty()) {
if (currentModule == null || !accessibleModules.contains(currentModule.getName())) continue;
}
PsiElement resolvedPackage = aPackage.resolve();
if (!(resolvedPackage instanceof PsiPackage)) continue;
if (!resolvedPackage.processDeclarations(processor, state, lastParent, place)) return false;
}
return true;
}
private static List<PsiPackageAccessibilityStatement> getAllDeclaredExports(@NotNull PsiJavaModule module) {
Project project = module.getProject();
return CachedValuesManager.getCachedValue(module, () -> {
List<PsiPackageAccessibilityStatement> exports = new ArrayList<>();
for (PsiPackageAccessibilityStatement export : module.getExports()) {
exports.add(export);
}
for (PsiRequiresStatement require : module.getRequires()) {
if (!require.hasModifierProperty(PsiModifier.TRANSITIVE)) continue;
PsiJavaModule resolved = require.resolve();
if (resolved == null) continue;
exports.addAll(getAllDeclaredExports(resolved));
}
return CachedValueProvider.Result.create(exports,
PsiJavaModuleModificationTracker.getInstance(project),
ProjectRootModificationTracker.getInstance(project));
});
}
}

View File

@@ -0,0 +1,284 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.java.psi.resolve;
import com.intellij.JavaTestUtil;
import com.intellij.java.testFramework.fixtures.LightJava9ModulesCodeInsightFixtureTestCase;
import com.intellij.java.testFramework.fixtures.MultiModuleJava9ProjectDescriptor.ModuleDescriptor;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleManager;
import com.intellij.openapi.roots.DependencyScope;
import com.intellij.openapi.roots.ModuleRootModificationUtil;
import com.intellij.openapi.vfs.VfsUtil;
import com.intellij.psi.*;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtil;
import org.intellij.lang.annotations.Language;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.util.List;
import java.util.Set;
import static com.intellij.java.testFramework.fixtures.MultiModuleJava9ProjectDescriptor.ModuleDescriptor.*;
public class ResolveModuleImportTest extends LightJava9ModulesCodeInsightFixtureTestCase {
@NotNull
@Override
protected String getTestDataPath() {
return JavaTestUtil.getJavaTestDataPath() + "/importModule/resolve";
}
@Override
protected void setUp() throws Exception {
super.setUp();
ModuleRootModificationUtil.addModuleLibrary(getModule(), "moduleA", getJarRootUrls("lib/moduleA-1.0.jar"),
getJarRootUrls("lib/moduleA-1.0-sources.jar"));
ModuleRootModificationUtil.addModuleLibrary(getModule(), "moduleB", getJarRootUrls("lib/moduleB-1.0.jar"),
getJarRootUrls("lib/moduleB-1.0-sources.jar"));
}
private List<String> getJarRootUrls(String path) {
return List.of(VfsUtil.getUrlForLibraryRoot(new File(getTestDataPath(), path)));
}
public void testResolveModuleLibrary() {
addCode("Test.java", """
import module my.test.moduleA;
class Test {
ModuleA <caret>module;
}""");
PsiClass psiClass = getPsiClass();
assertNotNull(psiClass);
assertEquals("my.test.moduleA.ModuleA", psiClass.getQualifiedName());
}
public void testResolveModuleLibraryInternalPackage() {
addCode("Test.java", """
import module my.test.moduleA;
class Test {
ModuleAInternal <caret>internal;
}""");
assertNull(getPsiClass());
}
public void testResolveAutoModule() {
addCode("Test.java", """
import module my.test.moduleB;
class Test {
ModuleB <caret>module;
}""");
PsiClass psiClass = getPsiClass();
assertNotNull(psiClass);
assertEquals("my.test.moduleB.ModuleB", psiClass.getQualifiedName());
}
public void testResolveAutoModuleExternalPackage() {
addCode("Test.java", """
import module my.test.moduleB;
class Test {
ModuleBExternal <caret>module;
}""");
PsiClass psiClass = getPsiClass();
assertNotNull(psiClass);
assertEquals("my.test.moduleB.external.ModuleBExternal", psiClass.getQualifiedName());
}
public void testResolveSourceModule() {
addCode("module-info.java", """
module my.source.moduleB {
exports my.source.moduleB;
}
""", M2);
addCode("my/source/moduleB/SourceTestB.java", """
package my.source.moduleB;
class SourceTestB {}
""", M2);
addCode("Test.java", """
import module my.source.moduleB;
class Test {
SourceTestB <caret>module;
}
""");
PsiClass psiClass = getPsiClass();
assertNotNull(psiClass);
assertEquals("my.source.moduleB.SourceTestB", psiClass.getQualifiedName());
}
public void testResolveTransitiveDependency() {
Module m2 = ModuleManager.getInstance(getProject()).findModuleByName(M2.getModuleName$intellij_java_tests());
Module m3 = ModuleManager.getInstance(getProject()).findModuleByName(M3.getModuleName$intellij_java_tests());
ModuleRootModificationUtil.addDependency(m2, m3, DependencyScope.COMPILE, true);
addCode("module-info.java", """
module my.source.moduleB {
requires transitive my.source.moduleC;
}
""", M2);
addCode("module-info.java", """
module my.source.moduleC {
exports my.source.moduleC;
}
""", M3);
addCode("my/source/moduleC/SourceTestC.java", """
package my.source.moduleC;
class SourceTestC {}
""", M3);
addCode("Test.java", """
import module my.source.moduleB;
class Test {
SourceTestC <caret>module;
}
""");
PsiClass psiClass = getPsiClass();
assertNotNull(psiClass);
assertEquals("my.source.moduleC.SourceTestC", psiClass.getQualifiedName());
}
public void testMultiResolve() {
addCode("module-info.java", """
module my.source.moduleB {
exports my.source.moduleB;
}
""", M2);
addCode("my/source/moduleB/MyTest.java", """
package my.source.moduleB;
class MyTest {}
""", M2);
addCode("module-info.java", """
module my.source.moduleC {
exports my.source.moduleC;
}
""", M4);
addCode("my/source/moduleC/MyTest.java", """
package my.source.moduleC;
class MyTest {}
""", M4);
addCode("Test.java", """
import module my.source.moduleB;
import module my.source.moduleC;
class Test {
MyTest module = new <caret>MyTest();
}
""");
ResolveResult[] resolveResults = getResolveResults();
assertEquals(2, resolveResults.length);
assertEquals(Set.of("my.source.moduleC.MyTest",
"my.source.moduleB.MyTest"),
Set.of(((PsiClass)resolveResults[0].getElement()).getQualifiedName(),
((PsiClass)resolveResults[1].getElement()).getQualifiedName()));
}
public void testResolveToModuleAccessed() {
addCode("module-info.java", """
module my.source.moduleB {
exports my.source.moduleB to my.source.moduleA;
}
""", M2);
addCode("my/source/moduleB/SourceTestB.java", """
package my.source.moduleB;
class SourceTestB {}
""", M2);
addCode("module-info.java", """
module my.source.moduleA {
requires my.source.moduleB;
}
""");
addCode("Test.java", """
package my;
import module my.source.moduleB;
class Test {
SourceTestB <caret>module;
}
""");
PsiClass psiClass = getPsiClass();
assertNotNull(psiClass);
assertEquals("my.source.moduleB.SourceTestB", psiClass.getQualifiedName());
}
public void testResolveToModuleNotAccessed() {
addCode("module-info.java", """
module my.source.moduleB {
exports my.source.moduleB to my.source.moduleC;
}
""", M2);
addCode("my/source/moduleB/SourceTestB.java", """
package my.source.moduleB;
class SourceTestB {}
""", M2);
addCode("module-info.java", """
module my.source.moduleA {
requires my.source.moduleB;
}
""");
addCode("Test.java", """
package my;
import module my.source.moduleB;
class Test {
SourceTestB <caret>module;
}
""");
PsiClass psiClass = getPsiClass();
assertNull(psiClass);
}
public void testResolveToModuleAccessedViaManifest() {
addCode("module-info.java", """
module my.source.moduleB {
exports my.source.moduleB to my.source.moduleA;
}
""", M2);
addCode("my/source/moduleB/SourceTestB.java", """
package my.source.moduleB;
class SourceTestB {}
""", M2);
addResourceFile("META-INF/MANIFEST.MF", """
Manifest-Version: 1.0
Automatic-Module-Name: my.source.moduleA
""", MAIN);
addCode("Test.java", """
package my;
import module my.source.moduleB;
class Test {
SourceTestB <caret>module;
}
""");
PsiClass psiClass = getPsiClass();
assertNotNull(psiClass);
assertEquals("my.source.moduleB.SourceTestB", psiClass.getQualifiedName());
}
@Nullable
private PsiClass getPsiClass() {
PsiField field = PsiTreeUtil.getParentOfType(myFixture.getFile().findElementAt(myFixture.getCaretOffset()), PsiField.class);
assertNotNull(field);
return PsiUtil.resolveClassInClassTypeOnly(field.getType());
}
private ResolveResult[] getResolveResults() {
PsiReference reference = myFixture.getFile().findReferenceAt(myFixture.getEditor().getCaretModel().getOffset());
return ((PsiPolyVariantReference)reference).multiResolve(false);
}
private void addCode(@NotNull String name, @NotNull @Language("JAVA") String text, @NotNull ModuleDescriptor descriptor) {
addFile(name, text, descriptor);
}
private void addCode(@NotNull String name, @NotNull @Language("JAVA") String text) {
myFixture.configureByText(name, text);
}
}