mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-03-22 15:19:59 +07:00
[java, import-module] resolve "import module" jep 476 IDEA-355535
GitOrigin-RevId: 4a4ee8cfafdba657d062317d3e014061af69a3e4
This commit is contained in:
committed by
intellij-monorepo-bot
parent
e9a6246df3
commit
c96bc2d64f
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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"/>
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)));
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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]");
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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, () -> {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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));
|
||||
});
|
||||
}
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user