mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-04-19 13:02:30 +07:00
[java-highlighting] Finish moving module access errors
Part of IDEA-365344 Create a new Java error highlighter with minimal dependencies (PSI only) GitOrigin-RevId: 0f89a7cd9609aea2780e132118aae892a6b0bafd
This commit is contained in:
committed by
intellij-monorepo-bot
parent
f976c0b04c
commit
2250bb64ee
@@ -3,11 +3,11 @@ package com.intellij.java.codeserver.core
|
||||
|
||||
import com.intellij.openapi.module.Module
|
||||
import com.intellij.openapi.module.ModuleUtilCore
|
||||
import com.intellij.psi.JavaModuleSystem
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiFileSystemItem
|
||||
import com.intellij.psi.PsiJavaModule
|
||||
import com.intellij.openapi.roots.ProjectFileIndex
|
||||
import com.intellij.pom.java.JavaFeature
|
||||
import com.intellij.psi.*
|
||||
import com.intellij.psi.impl.light.LightJavaModule
|
||||
import com.intellij.psi.util.PsiUtil
|
||||
|
||||
/**
|
||||
* Represents a JPMS module and the corresponding module in IntelliJ project model
|
||||
@@ -55,4 +55,49 @@ sealed interface JpmsModuleInfo {
|
||||
return JpmsModuleAccessInfo(current, this)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
/**
|
||||
* Find module info structures when accessing a given location.
|
||||
*
|
||||
* @param targetPackageName package name which about to be accessed
|
||||
* @param targetFile concrete target file which is about to be accessed; null if not known (in this case,
|
||||
* multiple results could be returned, as multiple source roots may define a given package)
|
||||
* @param place source place from where the access is requested
|
||||
* @return list of TargetModuleInfo structures that describe the possible target; empty list if the target package is empty
|
||||
* (which is generally an error), null if not applicable (e.g., modules are not supported at place;
|
||||
* target does not belong to any module; etc.). In this case, no access problem should be reported.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun findTargetModuleInfos(targetPackageName: String, targetFile: PsiFile?, place: PsiFile): List<TargetModuleInfo>? {
|
||||
val originalTargetFile = targetFile?.originalFile
|
||||
if (!PsiUtil.isAvailable(JavaFeature.MODULES, place)) return null
|
||||
|
||||
val useVFile = place.virtualFile
|
||||
val project = place.project
|
||||
val index = ProjectFileIndex.getInstance(project)
|
||||
if (useVFile != null && index.isInLibrarySource(useVFile)) return null
|
||||
if (originalTargetFile != null && originalTargetFile.isPhysical) {
|
||||
return listOf(TargetModuleInfo(originalTargetFile, targetPackageName))
|
||||
}
|
||||
if (useVFile == null) return null
|
||||
|
||||
val target = JavaPsiFacade.getInstance(project).findPackage(targetPackageName) ?: return null
|
||||
val module = index.getModuleForFile(useVFile) ?: return null
|
||||
val test = index.isInTestSourceContent(useVFile)
|
||||
val moduleScope = module.getModuleWithDependenciesAndLibrariesScope(test)
|
||||
val dirs = target.getDirectories(moduleScope)
|
||||
val packageName = target.qualifiedName
|
||||
if (dirs.isEmpty()) {
|
||||
return if (target.getFiles(moduleScope).isEmpty()) {
|
||||
listOf()
|
||||
}
|
||||
else {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
return dirs.map { dir -> TargetModuleInfo(dir, packageName) }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -303,6 +303,7 @@ reference.class.in.default.package=Class ''{0}'' is in the default package
|
||||
reference.non.static.from.static.context=Non-static {0} ''{1}'' cannot be referenced from a static context
|
||||
reference.outer.type.parameter.from.static.context=''{0}'' cannot be referenced from a static context
|
||||
reference.select.from.type.parameter=Cannot select from a type parameter
|
||||
reference.package.not.found=Package not found: {0}
|
||||
|
||||
statement.case.outside.switch=Case statement outside switch
|
||||
statement.invalid=Invalid statement
|
||||
|
||||
@@ -923,6 +923,9 @@ final class ExpressionChecker {
|
||||
if (!result.isStaticsScopeCorrect()) {
|
||||
myVisitor.report(JavaErrorKinds.REFERENCE_NON_STATIC_FROM_STATIC_CONTEXT.create(ref, resolved));
|
||||
}
|
||||
if (resolved instanceof PsiModifierListOwner owner) {
|
||||
myVisitor.myModuleChecker.checkModuleAccess(owner, ref);
|
||||
}
|
||||
}
|
||||
|
||||
private void checkUnresolvedReference(@NotNull PsiJavaCodeReferenceElement ref, @NotNull JavaResolveResult result) {
|
||||
|
||||
@@ -8,6 +8,7 @@ import com.intellij.java.codeserver.highlighting.errors.JavaIncompatibleTypeErro
|
||||
import com.intellij.lang.jvm.JvmModifier;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.impl.IncompleteModelUtil;
|
||||
import com.intellij.psi.impl.PsiClassImplUtil;
|
||||
import com.intellij.psi.impl.source.resolve.graphInference.InferenceSession;
|
||||
import com.intellij.psi.infos.MethodCandidateInfo;
|
||||
import com.intellij.psi.util.*;
|
||||
@@ -389,4 +390,51 @@ final class FunctionChecker {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 15.13 | 15.27
|
||||
// It is a compile-time error if any class or interface mentioned by either U or the function type of U
|
||||
// is not accessible from the class or interface in which the method reference expression appears.
|
||||
void checkFunctionalInterfaceTypeAccessible(@NotNull PsiFunctionalExpression expression,
|
||||
@NotNull PsiType functionalInterfaceType) {
|
||||
checkFunctionalInterfaceTypeAccessible(expression, functionalInterfaceType, true);
|
||||
}
|
||||
|
||||
private void checkFunctionalInterfaceTypeAccessible(@NotNull PsiFunctionalExpression expression,
|
||||
@NotNull PsiType functionalInterfaceType,
|
||||
boolean checkFunctionalTypeSignature) {
|
||||
PsiClassType.ClassResolveResult resolveResult =
|
||||
PsiUtil.resolveGenericsClassInType(PsiClassImplUtil.correctType(functionalInterfaceType, expression.getResolveScope()));
|
||||
PsiClass psiClass = resolveResult.getElement();
|
||||
if (psiClass == null) return;
|
||||
if (!PsiUtil.isAccessible(myVisitor.project(), psiClass, expression, null)) {
|
||||
myVisitor.myModifierChecker.reportAccessProblem(expression, psiClass, resolveResult);
|
||||
return;
|
||||
}
|
||||
for (PsiType type : resolveResult.getSubstitutor().getSubstitutionMap().values()) {
|
||||
if (type != null) {
|
||||
checkFunctionalInterfaceTypeAccessible(expression, type, false);
|
||||
if (myVisitor.hasErrorResults()) return;
|
||||
}
|
||||
}
|
||||
|
||||
PsiMethod psiMethod = checkFunctionalTypeSignature ? LambdaUtil.getFunctionalInterfaceMethod(resolveResult) : null;
|
||||
if (psiMethod != null) {
|
||||
PsiSubstitutor substitutor = LambdaUtil.getSubstitutor(psiMethod, resolveResult);
|
||||
for (PsiParameter parameter : psiMethod.getParameterList().getParameters()) {
|
||||
PsiType substitute = substitutor.substitute(parameter.getType());
|
||||
if (substitute != null) {
|
||||
checkFunctionalInterfaceTypeAccessible(expression, substitute, false);
|
||||
if (myVisitor.hasErrorResults()) return;
|
||||
}
|
||||
}
|
||||
|
||||
PsiType substitute = substitutor.substitute(psiMethod.getReturnType());
|
||||
if (substitute != null) {
|
||||
checkFunctionalInterfaceTypeAccessible(expression, substitute, false);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
myVisitor.myModuleChecker.checkModuleAccess(psiClass, expression);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,7 +60,7 @@ final class JavaErrorVisitor extends JavaElementVisitor {
|
||||
final @NotNull ControlFlowChecker myControlFlowChecker = new ControlFlowChecker(this);
|
||||
private final @NotNull FunctionChecker myFunctionChecker = new FunctionChecker(this);
|
||||
final @NotNull PatternChecker myPatternChecker = new PatternChecker(this);
|
||||
private final @NotNull ModuleChecker myModuleChecker = new ModuleChecker(this);
|
||||
final @NotNull ModuleChecker myModuleChecker = new ModuleChecker(this);
|
||||
final @NotNull ModifierChecker myModifierChecker = new ModifierChecker(this);
|
||||
final @NotNull ExpressionChecker myExpressionChecker = new ExpressionChecker(this);
|
||||
private final @NotNull SwitchChecker mySwitchChecker = new SwitchChecker(this);
|
||||
@@ -426,6 +426,7 @@ final class JavaErrorVisitor extends JavaElementVisitor {
|
||||
report(JavaErrorKinds.LAMBDA_NOT_FUNCTIONAL_INTERFACE.create(expression, functionalInterfaceType));
|
||||
}
|
||||
if (!hasErrorResults()) myFunctionChecker.checkMethodReferenceContext(expression, functionalInterfaceType);
|
||||
if (!hasErrorResults()) myFunctionChecker.checkFunctionalInterfaceTypeAccessible(expression, functionalInterfaceType);
|
||||
}
|
||||
if (!hasErrorResults()) myFunctionChecker.checkMethodReferenceResolve(expression, results, functionalInterfaceType);
|
||||
if (!hasErrorResults()) myFunctionChecker.checkMethodReferenceReturnType(expression, result, functionalInterfaceType);
|
||||
@@ -472,7 +473,7 @@ final class JavaErrorVisitor extends JavaElementVisitor {
|
||||
else if (parent instanceof PsiClass aClass) {
|
||||
if (!hasErrorResults()) myClassChecker.checkDuplicateNestedClass(aClass);
|
||||
if (!hasErrorResults() && !(aClass instanceof PsiAnonymousClass)) {
|
||||
/* anonymous class is highlighted in HighlightClassUtil.checkAbstractInstantiation()*/
|
||||
/* an anonymous class is highlighted in HighlightClassUtil.checkAbstractInstantiation()*/
|
||||
myClassChecker.checkClassMustBeAbstract(aClass);
|
||||
}
|
||||
if (!hasErrorResults()) {
|
||||
@@ -933,6 +934,7 @@ final class JavaErrorVisitor extends JavaElementVisitor {
|
||||
if (functionalInterfaceType != null) {
|
||||
myFunctionChecker.checkExtendsSealedClass(expression, functionalInterfaceType);
|
||||
if (!hasErrorResults()) myFunctionChecker.checkInterfaceFunctional(expression, functionalInterfaceType);
|
||||
if (!hasErrorResults()) myFunctionChecker.checkFunctionalInterfaceTypeAccessible(expression, functionalInterfaceType);
|
||||
}
|
||||
else if (LambdaUtil.getFunctionalInterfaceType(expression, true) != null) {
|
||||
report(JavaErrorKinds.LAMBDA_TYPE_INFERENCE_FAILURE.create(expression));
|
||||
|
||||
@@ -178,9 +178,7 @@ final class ModifierChecker {
|
||||
}
|
||||
}
|
||||
|
||||
void reportAccessProblem(@NotNull PsiJavaCodeReferenceElement ref,
|
||||
@NotNull PsiModifierListOwner resolved,
|
||||
@NotNull JavaResolveResult result) {
|
||||
void reportAccessProblem(@NotNull PsiElement ref, @NotNull PsiModifierListOwner resolved, @NotNull JavaResolveResult result) {
|
||||
result = withElement(result, resolved);
|
||||
if (resolved.hasModifierProperty(PsiModifier.PRIVATE)) {
|
||||
myVisitor.report(JavaErrorKinds.ACCESS_PRIVATE.create(ref, result));
|
||||
@@ -202,15 +200,11 @@ final class ModifierChecker {
|
||||
return;
|
||||
}
|
||||
|
||||
checkModuleAccess(resolved, ref);
|
||||
myVisitor.myModuleChecker.checkModuleAccess(resolved, ref);
|
||||
if (myVisitor.hasErrorResults()) return;
|
||||
myVisitor.report(JavaErrorKinds.ACCESS_GENERIC_PROBLEM.create(ref, result));
|
||||
}
|
||||
|
||||
private void checkModuleAccess(@NotNull PsiModifierListOwner resolved, @NotNull PsiElement ref) {
|
||||
// TODO: JPMS
|
||||
}
|
||||
|
||||
private static @NotNull JavaResolveResult withElement(@NotNull JavaResolveResult original, @NotNull PsiElement newElement) {
|
||||
if (newElement == original.getElement()) return original;
|
||||
return new JavaResolveResult() {
|
||||
|
||||
@@ -4,7 +4,9 @@ package com.intellij.java.codeserver.highlighting;
|
||||
import com.intellij.java.codeserver.core.JavaPsiModuleUtil;
|
||||
import com.intellij.java.codeserver.core.JavaServiceProviderUtil;
|
||||
import com.intellij.java.codeserver.core.JpmsModuleAccessInfo;
|
||||
import com.intellij.java.codeserver.core.JpmsModuleAccessInfo.JpmsModuleAccessProblem;
|
||||
import com.intellij.java.codeserver.core.JpmsModuleInfo;
|
||||
import com.intellij.java.codeserver.highlighting.errors.JavaCompilationError;
|
||||
import com.intellij.java.codeserver.highlighting.errors.JavaErrorKind;
|
||||
import com.intellij.java.codeserver.highlighting.errors.JavaErrorKinds;
|
||||
import com.intellij.openapi.module.Module;
|
||||
@@ -24,6 +26,7 @@ import org.jetbrains.jps.model.module.JpsModuleSourceRootType;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
|
||||
@@ -299,7 +302,7 @@ final class ModuleChecker {
|
||||
myVisitor.report(kind.create(statement));
|
||||
}
|
||||
|
||||
JavaErrorKind.Parameterized<PsiElement, JpmsModuleAccessInfo> accessError(@NotNull JpmsModuleAccessInfo.JpmsModuleAccessProblem problem) {
|
||||
JavaErrorKind.Parameterized<PsiElement, JpmsModuleAccessInfo> accessError(@NotNull JpmsModuleAccessProblem problem) {
|
||||
return switch (problem) {
|
||||
case FROM_NAMED -> JavaErrorKinds.MODULE_ACCESS_FROM_NAMED;
|
||||
case FROM_UNNAMED -> JavaErrorKinds.MODULE_ACCESS_FROM_UNNAMED;
|
||||
@@ -325,10 +328,51 @@ final class ModuleChecker {
|
||||
return;
|
||||
}
|
||||
JpmsModuleAccessInfo moduleAccess = new JpmsModuleInfo.TargetModuleInfo(target, "").accessAt(myVisitor.file().getOriginalFile());
|
||||
JpmsModuleAccessInfo.JpmsModuleAccessProblem problem = moduleAccess.checkModuleAccess(statement);
|
||||
JpmsModuleAccessProblem problem = moduleAccess.checkModuleAccess(statement);
|
||||
if (problem != null) {
|
||||
myVisitor.report(accessError(problem).create(statement, moduleAccess));
|
||||
}
|
||||
}
|
||||
|
||||
private static @NotNull PsiElement findPackagePrefix(@NotNull PsiJavaCodeReferenceElement ref) {
|
||||
PsiElement candidate = ref;
|
||||
while (candidate instanceof PsiJavaCodeReferenceElement element) {
|
||||
if (element.resolve() instanceof PsiPackage) return candidate;
|
||||
candidate = element.getQualifier();
|
||||
}
|
||||
return ref;
|
||||
}
|
||||
|
||||
void checkModuleAccess(@NotNull PsiModifierListOwner target, @NotNull PsiElement ref) {
|
||||
if (target instanceof PsiClass targetClass && !(target instanceof PsiTypeParameter)) {
|
||||
String packageName = PsiUtil.getPackageName(targetClass);
|
||||
if (packageName != null) {
|
||||
checkAccess(packageName, target.getContainingFile(), ref);
|
||||
}
|
||||
}
|
||||
else if (target instanceof PsiPackage targetPackage) {
|
||||
checkAccess(targetPackage.getQualifiedName(), null, ref);
|
||||
}
|
||||
}
|
||||
|
||||
private void checkAccess(@NotNull String targetPackageName, @Nullable PsiFile targetFile, @NotNull PsiElement place) {
|
||||
PsiFile file = myVisitor.file().getOriginalFile();
|
||||
List<JpmsModuleInfo.@NotNull TargetModuleInfo> infos = JpmsModuleInfo.findTargetModuleInfos(targetPackageName, targetFile, file);
|
||||
if (infos == null) return;
|
||||
if (infos.isEmpty()) {
|
||||
myVisitor.report(JavaErrorKinds.REFERENCE_PACKAGE_NOT_FOUND.create(place, targetPackageName));
|
||||
return;
|
||||
}
|
||||
JavaCompilationError<PsiElement, JpmsModuleAccessInfo> error = null;
|
||||
for (JpmsModuleInfo.TargetModuleInfo info : infos) {
|
||||
JpmsModuleAccessInfo moduleAccessInfo = info.accessAt(file);
|
||||
JpmsModuleAccessProblem problem = moduleAccessInfo.checkAccess(file, JpmsModuleAccessInfo.JpmsModuleAccessMode.READ);
|
||||
if (problem == null) return;
|
||||
if (error == null) {
|
||||
PsiElement anchor = place instanceof PsiJavaCodeReferenceElement ref ? findPackagePrefix(ref) : place;
|
||||
error = accessError(problem).create(anchor, moduleAccessInfo);
|
||||
}
|
||||
}
|
||||
myVisitor.report(error);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -117,8 +117,8 @@ final class JavaErrorFormatUtil {
|
||||
return nameElement.getTextRangeInParent();
|
||||
}
|
||||
}
|
||||
if (element instanceof PsiReferenceExpression refExpression) {
|
||||
PsiElement nameElement = refExpression.getReferenceNameElement();
|
||||
if (element instanceof PsiJavaCodeReferenceElement ref) {
|
||||
PsiElement nameElement = ref.getReferenceNameElement();
|
||||
if (nameElement != null) {
|
||||
return nameElement.getTextRangeInParent();
|
||||
}
|
||||
|
||||
@@ -1157,6 +1157,9 @@ public final class JavaErrorKinds {
|
||||
.withRawDescription((ref, refElement) -> message("reference.outer.type.parameter.from.static.context", refElement.getName()));
|
||||
public static final Simple<PsiJavaCodeReferenceElement> REFERENCE_SELECT_FROM_TYPE_PARAMETER =
|
||||
error(PsiJavaCodeReferenceElement.class, "reference.select.from.type.parameter");
|
||||
public static final Parameterized<PsiElement, String> REFERENCE_PACKAGE_NOT_FOUND =
|
||||
parameterized(PsiElement.class, String.class, "reference.package.not.found")
|
||||
.withRawDescription((psi, packageName) -> message("reference.package.not.found", packageName));
|
||||
|
||||
public static final Simple<PsiSwitchLabelStatementBase> STATEMENT_CASE_OUTSIDE_SWITCH = error("statement.case.outside.switch");
|
||||
public static final Simple<PsiStatement> STATEMENT_INVALID = error("statement.invalid");
|
||||
@@ -1307,23 +1310,23 @@ public final class JavaErrorKinds {
|
||||
parameterized(PsiExpression.class, PsiType.class, "string.template.raw.processor")
|
||||
.withRawDescription((psi, type) -> message("string.template.raw.processor", type.getPresentableText()));
|
||||
|
||||
public static final Parameterized<PsiJavaCodeReferenceElement, JavaResolveResult> ACCESS_PRIVATE =
|
||||
parameterized(PsiJavaCodeReferenceElement.class, JavaResolveResult.class, "access.private")
|
||||
.withAnchor(psi -> requireNonNullElse(psi.getReferenceNameElement(), psi))
|
||||
public static final Parameterized<PsiElement, JavaResolveResult> ACCESS_PRIVATE =
|
||||
parameterized(PsiElement.class, JavaResolveResult.class, "access.private")
|
||||
.withRange((psi, cls) -> getRange(psi))
|
||||
.withRawDescription((psi, result) -> message("access.private", formatResolvedSymbol(result), formatResolvedSymbolContainer(result)));
|
||||
public static final Parameterized<PsiJavaCodeReferenceElement, JavaResolveResult> ACCESS_PROTECTED =
|
||||
parameterized(PsiJavaCodeReferenceElement.class, JavaResolveResult.class, "access.protected")
|
||||
.withAnchor(psi -> requireNonNullElse(psi.getReferenceNameElement(), psi))
|
||||
public static final Parameterized<PsiElement, JavaResolveResult> ACCESS_PROTECTED =
|
||||
parameterized(PsiElement.class, JavaResolveResult.class, "access.protected")
|
||||
.withRange((psi, cls) -> getRange(psi))
|
||||
.withRawDescription(
|
||||
(psi, result) -> message("access.protected", formatResolvedSymbol(result), formatResolvedSymbolContainer(result)));
|
||||
public static final Parameterized<PsiJavaCodeReferenceElement, JavaResolveResult> ACCESS_PACKAGE_LOCAL =
|
||||
parameterized(PsiJavaCodeReferenceElement.class, JavaResolveResult.class, "access.package.local")
|
||||
.withAnchor(psi -> requireNonNullElse(psi.getReferenceNameElement(), psi))
|
||||
public static final Parameterized<PsiElement, JavaResolveResult> ACCESS_PACKAGE_LOCAL =
|
||||
parameterized(PsiElement.class, JavaResolveResult.class, "access.package.local")
|
||||
.withRange((psi, cls) -> getRange(psi))
|
||||
.withRawDescription(
|
||||
(psi, result) -> message("access.package.local", formatResolvedSymbol(result), formatResolvedSymbolContainer(result)));
|
||||
public static final Parameterized<PsiJavaCodeReferenceElement, JavaResolveResult> ACCESS_GENERIC_PROBLEM =
|
||||
parameterized(PsiJavaCodeReferenceElement.class, JavaResolveResult.class, "access.generic.problem")
|
||||
.withAnchor(psi -> requireNonNullElse(psi.getReferenceNameElement(), psi))
|
||||
public static final Parameterized<PsiElement, JavaResolveResult> ACCESS_GENERIC_PROBLEM =
|
||||
parameterized(PsiElement.class, JavaResolveResult.class, "access.generic.problem")
|
||||
.withRange((psi, cls) -> getRange(psi))
|
||||
.withRawDescription(
|
||||
(psi, result) -> message("access.generic.problem", formatResolvedSymbol(result), formatResolvedSymbolContainer(result)));
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ import com.intellij.openapi.editor.colors.TextAttributesKey;
|
||||
import com.intellij.openapi.editor.colors.TextAttributesScheme;
|
||||
import com.intellij.openapi.editor.markup.TextAttributes;
|
||||
import com.intellij.openapi.project.IndexNotReadyException;
|
||||
import com.intellij.openapi.util.NlsSafe;
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.packageDependencies.DependencyValidationManager;
|
||||
@@ -25,6 +26,8 @@ import com.intellij.psi.impl.source.tree.TreeUtil;
|
||||
import com.intellij.psi.search.scope.packageSet.NamedScope;
|
||||
import com.intellij.psi.search.scope.packageSet.NamedScopesHolder;
|
||||
import com.intellij.psi.search.scope.packageSet.PackageSet;
|
||||
import com.intellij.psi.util.PsiFormatUtil;
|
||||
import com.intellij.psi.util.PsiFormatUtilBase;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.psi.util.PsiUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -380,4 +383,13 @@ public final class HighlightNamesUtil {
|
||||
static HighlightInfo highlightClassKeyword(@NotNull PsiKeyword keyword) {
|
||||
return nameBuilder(JavaHighlightInfoTypes.JAVA_KEYWORD_CLASS_FILE).range(keyword).create();
|
||||
}
|
||||
|
||||
public static @NotNull @NlsSafe String formatClass(@NotNull PsiClass aClass) {
|
||||
return formatClass(aClass, true);
|
||||
}
|
||||
|
||||
public static @NotNull String formatClass(@NotNull PsiClass aClass, boolean fqn) {
|
||||
return PsiFormatUtil.formatClass(aClass, PsiFormatUtilBase.SHOW_NAME |
|
||||
PsiFormatUtilBase.SHOW_ANONYMOUS_CLASS_VERBOSE | (fqn ? PsiFormatUtilBase.SHOW_FQ_NAME : 0));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,158 +1,34 @@
|
||||
// Copyright 2000-2025 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.codeInsight.ContainerProvider;
|
||||
import com.intellij.codeInsight.JavaModuleSystemEx;
|
||||
import com.intellij.codeInsight.JavaModuleSystemEx.ErrorWithFixes;
|
||||
import com.intellij.codeInsight.daemon.JavaErrorBundle;
|
||||
import com.intellij.codeInsight.daemon.impl.HighlightInfo;
|
||||
import com.intellij.codeInsight.daemon.impl.HighlightInfoType;
|
||||
import com.intellij.codeInsight.intention.IntentionAction;
|
||||
import com.intellij.java.codeserver.core.JavaPsiModifierUtil;
|
||||
import com.intellij.openapi.util.NlsSafe;
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.PsiClass;
|
||||
import com.intellij.psi.util.PsiFormatUtil;
|
||||
import com.intellij.psi.util.PsiFormatUtilBase;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.psi.util.PsiUtil;
|
||||
import org.jetbrains.annotations.Nls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
// generates HighlightInfoType.ERROR-like HighlightInfos
|
||||
/**
|
||||
* @deprecated all methods are deprecated
|
||||
*/
|
||||
@Deprecated
|
||||
public final class HighlightUtil {
|
||||
|
||||
private HighlightUtil() { }
|
||||
|
||||
/**
|
||||
* @deprecated use {@link HighlightNamesUtil#formatClass(PsiClass)} or
|
||||
* preferably {@link PsiFormatUtil#formatClass(PsiClass, int)} directly
|
||||
*/
|
||||
@Deprecated
|
||||
public static @NotNull @NlsSafe String formatClass(@NotNull PsiClass aClass) {
|
||||
return formatClass(aClass, true);
|
||||
return HighlightNamesUtil.formatClass(aClass);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated use {@link HighlightNamesUtil#formatClass(PsiClass)} or
|
||||
* preferably {@link PsiFormatUtil#formatClass(PsiClass, int)} directly
|
||||
*/
|
||||
@Deprecated
|
||||
public static @NotNull String formatClass(@NotNull PsiClass aClass, boolean fqn) {
|
||||
return PsiFormatUtil.formatClass(aClass, PsiFormatUtilBase.SHOW_NAME |
|
||||
PsiFormatUtilBase.SHOW_ANONYMOUS_CLASS_VERBOSE | (fqn ? PsiFormatUtilBase.SHOW_FQ_NAME : 0));
|
||||
}
|
||||
|
||||
static @NotNull Pair<@Nls String, List<IntentionAction>> accessProblemDescriptionAndFixes(@NotNull PsiElement ref,
|
||||
@NotNull PsiElement resolved,
|
||||
@NotNull JavaResolveResult result) {
|
||||
assert resolved instanceof PsiModifierListOwner : resolved;
|
||||
PsiModifierListOwner refElement = (PsiModifierListOwner)resolved;
|
||||
String symbolName = HighlightMessageUtil.getSymbolName(refElement, result.getSubstitutor());
|
||||
|
||||
if (refElement.hasModifierProperty(PsiModifier.PRIVATE)) {
|
||||
String containerName = getContainerName(refElement, result.getSubstitutor());
|
||||
return Pair.pair(JavaErrorBundle.message("private.symbol", symbolName, containerName), null);
|
||||
}
|
||||
|
||||
if (refElement.hasModifierProperty(PsiModifier.PROTECTED)) {
|
||||
String containerName = getContainerName(refElement, result.getSubstitutor());
|
||||
return Pair.pair(JavaErrorBundle.message("protected.symbol", symbolName, containerName), null);
|
||||
}
|
||||
|
||||
PsiClass packageLocalClass = JavaPsiModifierUtil.getPackageLocalClassInTheMiddle(ref);
|
||||
if (packageLocalClass != null) {
|
||||
refElement = packageLocalClass;
|
||||
symbolName = HighlightMessageUtil.getSymbolName(refElement, result.getSubstitutor());
|
||||
}
|
||||
|
||||
if (refElement.hasModifierProperty(PsiModifier.PACKAGE_LOCAL) || packageLocalClass != null) {
|
||||
String containerName = getContainerName(refElement, result.getSubstitutor());
|
||||
return Pair.pair(JavaErrorBundle.message("package.local.symbol", symbolName, containerName), null);
|
||||
}
|
||||
|
||||
String containerName = getContainerName(refElement, result.getSubstitutor());
|
||||
ErrorWithFixes problem = checkModuleAccess(resolved, ref, symbolName, containerName);
|
||||
if (problem != null) return Pair.pair(problem.message, problem.fixes);
|
||||
return Pair.pair(JavaErrorBundle.message("visibility.access.problem", symbolName, containerName), null);
|
||||
}
|
||||
|
||||
static @Nullable @Nls ErrorWithFixes checkModuleAccess(@NotNull PsiElement resolved, @NotNull PsiElement ref, @NotNull JavaResolveResult result) {
|
||||
PsiElement refElement = resolved;
|
||||
PsiClass packageLocalClass = JavaPsiModifierUtil.getPackageLocalClassInTheMiddle(ref);
|
||||
if (packageLocalClass != null) {
|
||||
refElement = packageLocalClass;
|
||||
}
|
||||
|
||||
String symbolName = HighlightMessageUtil.getSymbolName(refElement, result.getSubstitutor());
|
||||
String containerName = (resolved instanceof PsiModifierListOwner modifierListOwner)
|
||||
? getContainerName(modifierListOwner, result.getSubstitutor())
|
||||
: null;
|
||||
return checkModuleAccess(resolved, ref, symbolName, containerName);
|
||||
}
|
||||
|
||||
private static @Nullable @Nls ErrorWithFixes checkModuleAccess(@NotNull PsiElement target,
|
||||
@NotNull PsiElement place,
|
||||
@Nullable String symbolName,
|
||||
@Nullable String containerName) {
|
||||
for (JavaModuleSystem moduleSystem : JavaModuleSystem.EP_NAME.getExtensionList()) {
|
||||
if (moduleSystem instanceof JavaModuleSystemEx system) {
|
||||
if (target instanceof PsiClass targetClass) {
|
||||
final ErrorWithFixes problem = system.checkAccess(targetClass, place);
|
||||
if (problem != null) return problem;
|
||||
}
|
||||
if (target instanceof PsiPackage targetPackage) {
|
||||
final ErrorWithFixes problem = system.checkAccess(targetPackage.getQualifiedName(), null, place);
|
||||
if (problem != null) return problem;
|
||||
}
|
||||
}
|
||||
else if (!isAccessible(moduleSystem, target, place)) {
|
||||
return new ErrorWithFixes(JavaErrorBundle.message("visibility.module.access.problem", symbolName, containerName, moduleSystem.getName()));
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static boolean isAccessible(@NotNull JavaModuleSystem system, @NotNull PsiElement target, @NotNull PsiElement place) {
|
||||
if (target instanceof PsiClass psiClass) return system.isAccessible(psiClass, place);
|
||||
if (target instanceof PsiPackage psiPackage) return system.isAccessible(psiPackage.getQualifiedName(), null, place);
|
||||
return true;
|
||||
}
|
||||
|
||||
private static PsiElement getContainer(@NotNull PsiModifierListOwner refElement) {
|
||||
for (ContainerProvider provider : ContainerProvider.EP_NAME.getExtensionList()) {
|
||||
PsiElement container = provider.getContainer(refElement);
|
||||
if (container != null) return container;
|
||||
}
|
||||
return refElement.getParent();
|
||||
}
|
||||
|
||||
private static String getContainerName(@NotNull PsiModifierListOwner refElement, @NotNull PsiSubstitutor substitutor) {
|
||||
PsiElement container = getContainer(refElement);
|
||||
return container == null ? "?" : HighlightMessageUtil.getSymbolName(container, substitutor);
|
||||
}
|
||||
|
||||
static HighlightInfo.Builder checkReference(@NotNull PsiJavaCodeReferenceElement ref, @NotNull JavaResolveResult result) {
|
||||
PsiElement resolved = result.getElement();
|
||||
|
||||
boolean skipValidityChecks =
|
||||
ref.getParent() instanceof PsiMethodCallExpression || resolved == null ||
|
||||
PsiUtil.isInsideJavadocComment(ref) ||
|
||||
PsiTreeUtil.getParentOfType(ref, PsiPackageStatement.class, true) != null ||
|
||||
resolved instanceof PsiPackage && ref.getParent() instanceof PsiJavaCodeReferenceElement;
|
||||
|
||||
if (skipValidityChecks) return null;
|
||||
|
||||
final ErrorWithFixes moduleProblem = checkModuleAccess(resolved, ref, result);
|
||||
if (moduleProblem != null) {
|
||||
HighlightInfo.Builder info = HighlightInfo.newHighlightInfo(HighlightInfoType.WRONG_REF).range(findPackagePrefix(ref))
|
||||
.descriptionAndTooltip(moduleProblem.message);
|
||||
moduleProblem.fixes.forEach(fix -> info.registerFix(fix, List.of(), null, null, null));
|
||||
return info;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static @NotNull PsiElement findPackagePrefix(@NotNull PsiJavaCodeReferenceElement ref) {
|
||||
PsiElement candidate = ref;
|
||||
while (candidate instanceof PsiJavaCodeReferenceElement element) {
|
||||
if (element.resolve() instanceof PsiPackage) return candidate;
|
||||
candidate = element.getQualifier();
|
||||
}
|
||||
return ref;
|
||||
return HighlightNamesUtil.formatClass(aClass, fqn);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,15 +16,11 @@ import com.intellij.openapi.editor.DefaultLanguageHighlighterColors;
|
||||
import com.intellij.openapi.editor.Document;
|
||||
import com.intellij.openapi.editor.colors.EditorColorsUtil;
|
||||
import com.intellij.openapi.progress.ProgressManager;
|
||||
import com.intellij.openapi.project.IndexNotReadyException;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.openapi.util.text.HtmlChunk;
|
||||
import com.intellij.pom.java.LanguageLevel;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.impl.source.resolve.JavaResolveUtil;
|
||||
import com.intellij.psi.impl.source.tree.java.PsiReferenceExpressionImpl;
|
||||
import com.intellij.psi.util.PsiTypesUtil;
|
||||
import com.intellij.psi.util.PsiUtil;
|
||||
import com.intellij.ui.ColorUtil;
|
||||
import com.intellij.ui.NewUI;
|
||||
@@ -40,6 +36,7 @@ import java.util.Map;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import static com.intellij.java.codeserver.highlighting.errors.JavaErrorKinds.*;
|
||||
import static com.intellij.util.ObjectUtils.tryCast;
|
||||
|
||||
// java highlighting: problems in java code like unresolved/incompatible symbols/methods etc.
|
||||
public class HighlightVisitorImpl extends JavaElementVisitor implements HighlightVisitor {
|
||||
@@ -51,8 +48,6 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh
|
||||
private @NotNull PsiFile myFile;
|
||||
private JavaErrorCollector myCollector;
|
||||
|
||||
private final @NotNull Consumer<? super HighlightInfo.Builder> myErrorSink = builder -> add(builder);
|
||||
|
||||
private boolean myHasError; // true if myHolder.add() was called with HighlightInfo of >=ERROR severity. On each .visit(PsiElement) call this flag is reset. Useful to determine whether the error was already reported while visiting this PsiElement.
|
||||
|
||||
protected HighlightVisitorImpl() {
|
||||
@@ -183,7 +178,8 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh
|
||||
Consumer<@NotNull CommonIntentionAction> consumer = fix -> info.registerFix(fix.asIntention(), null, null, null, null);
|
||||
errorFixProvider.processFixes(error, consumer);
|
||||
ErrorFixExtensionPoint.registerFixes(consumer, error.psi(), error.kind().key());
|
||||
error.psiForKind(EXPRESSION_EXPECTED, REFERENCE_UNRESOLVED, REFERENCE_AMBIGUOUS, ACCESS_PRIVATE, ACCESS_PACKAGE_LOCAL, ACCESS_PROTECTED)
|
||||
error.psiForKind(EXPRESSION_EXPECTED, REFERENCE_UNRESOLVED, REFERENCE_AMBIGUOUS)
|
||||
.or(() -> error.psiForKind(ACCESS_PRIVATE, ACCESS_PACKAGE_LOCAL, ACCESS_PROTECTED).map(psi -> tryCast(psi, PsiJavaCodeReferenceElement.class)))
|
||||
.or(() -> error.psiForKind(TYPE_UNKNOWN_CLASS).map(PsiTypeElement::getInnermostComponentReferenceElement))
|
||||
.or(() -> error.psiForKind(CALL_AMBIGUOUS_NO_MATCH, CALL_UNRESOLVED).map(PsiMethodCallExpression::getMethodExpression))
|
||||
.ifPresent(ref -> UnresolvedReferenceQuickFixProvider.registerUnresolvedReferenceLazyQuickFixes(ref, info));
|
||||
@@ -206,76 +202,10 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitLambdaExpression(@NotNull PsiLambdaExpression expression) {
|
||||
visitElement(expression);
|
||||
PsiElement parent = PsiUtil.skipParenthesizedExprUp(expression.getParent());
|
||||
if (hasErrorResults() || toReportFunctionalExpressionProblemOnParent(parent)) return;
|
||||
|
||||
if (!hasErrorResults()) {
|
||||
PsiType functionalInterfaceType = expression.getFunctionalInterfaceType();
|
||||
if (functionalInterfaceType != null) {
|
||||
add(LambdaHighlightingUtil.checkFunctionalInterfaceTypeAccessible(myFile.getProject(), expression, functionalInterfaceType));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitImportStaticReferenceElement(@NotNull PsiImportStaticReferenceElement ref) {
|
||||
super.visitImportStaticReferenceElement(ref);
|
||||
JavaResolveResult[] results = ref.multiResolve(false);
|
||||
if (!hasErrorResults() && results.length == 1) {
|
||||
add(HighlightUtil.checkReference(ref, results[0]));
|
||||
}
|
||||
}
|
||||
|
||||
static @Nullable JavaResolveResult resolveOptimised(@NotNull PsiJavaCodeReferenceElement ref, @NotNull PsiFile containingFile) {
|
||||
try {
|
||||
if (ref instanceof PsiReferenceExpressionImpl) {
|
||||
PsiReferenceExpressionImpl.OurGenericsResolver resolver = PsiReferenceExpressionImpl.OurGenericsResolver.INSTANCE;
|
||||
JavaResolveResult[] results = JavaResolveUtil.resolveWithContainingFile(ref, resolver, true, true, containingFile);
|
||||
return results.length == 1 ? results[0] : JavaResolveResult.EMPTY;
|
||||
}
|
||||
return ref.advancedResolve(true);
|
||||
}
|
||||
catch (IndexNotReadyException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitReferenceExpression(@NotNull PsiReferenceExpression expression) {
|
||||
JavaResolveResult result = resolveOptimised(expression, myFile);
|
||||
if (result != null) {
|
||||
add(HighlightUtil.checkReference(expression, result));
|
||||
}
|
||||
|
||||
if (!hasErrorResults()) {
|
||||
visitElement(expression);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitMethodReferenceExpression(@NotNull PsiMethodReferenceExpression expression) {
|
||||
// Necessary to call visitElement, as super-implementation is empty
|
||||
visitElement(expression);
|
||||
PsiElement parent = PsiUtil.skipParenthesizedExprUp(expression.getParent());
|
||||
if (toReportFunctionalExpressionProblemOnParent(parent)) return;
|
||||
PsiType functionalInterfaceType = expression.getFunctionalInterfaceType();
|
||||
if (functionalInterfaceType != null) {
|
||||
if (!hasErrorResults() && PsiTypesUtil.allTypeParametersResolved(expression, functionalInterfaceType)) {
|
||||
add(LambdaHighlightingUtil.checkFunctionalInterfaceTypeAccessible(myFile.getProject(), expression, functionalInterfaceType));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true for {@code functional_expression;} or {@code var l = functional_expression;}
|
||||
*/
|
||||
private static boolean toReportFunctionalExpressionProblemOnParent(@Nullable PsiElement parent) {
|
||||
if (parent instanceof PsiLocalVariable variable) {
|
||||
return variable.getTypeElement().isInferredType();
|
||||
}
|
||||
return parent instanceof PsiExpressionStatement && !(parent.getParent() instanceof PsiSwitchLabeledRuleStatement);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -293,17 +223,7 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh
|
||||
private void checkSwitchBlock(@NotNull PsiSwitchBlock switchBlock) {
|
||||
SwitchBlockHighlightingModel model = SwitchBlockHighlightingModel.createInstance(myLanguageLevel, switchBlock, myFile);
|
||||
if (model == null) return;
|
||||
if (!hasErrorResults()) model.checkSwitchLabelValues(myErrorSink);
|
||||
if (!hasErrorResults()) model.checkSwitchLabelValues(builder -> add(builder));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitReferenceElement(@NotNull PsiJavaCodeReferenceElement ref) {
|
||||
if (!(ref instanceof PsiExpression)) {
|
||||
JavaResolveResult result = resolveOptimised(ref, myFile);
|
||||
if (result != null) {
|
||||
add(HighlightUtil.checkReference(ref, result));
|
||||
}
|
||||
}
|
||||
if (!hasErrorResults()) super.visitReferenceElement(ref);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -737,10 +737,11 @@ final class JavaErrorFixProvider {
|
||||
}
|
||||
|
||||
private void createAccessFixes() {
|
||||
JavaFixesPusher<PsiJavaCodeReferenceElement, JavaResolveResult> accessFix = (error, sink) -> {
|
||||
if (error.context().isStaticsScopeCorrect() && error.context().getElement() instanceof PsiJvmMember member) {
|
||||
HighlightFixUtil.registerAccessQuickFixAction(sink, member, error.psi(), null);
|
||||
if (error.psi() instanceof PsiReferenceExpression expression) {
|
||||
JavaFixesPusher<PsiElement, JavaResolveResult> accessFix = (error, sink) -> {
|
||||
if (error.psi() instanceof PsiJavaCodeReferenceElement ref &&
|
||||
error.context().isStaticsScopeCorrect() && error.context().getElement() instanceof PsiJvmMember member) {
|
||||
HighlightFixUtil.registerAccessQuickFixAction(sink, member, ref, null);
|
||||
if (ref instanceof PsiReferenceExpression expression) {
|
||||
sink.accept(myFactory.createRenameWrongRefFix(expression));
|
||||
}
|
||||
}
|
||||
@@ -865,7 +866,7 @@ final class JavaErrorFixProvider {
|
||||
}
|
||||
});
|
||||
fix(CLASS_REFERENCE_LIST_DUPLICATE,
|
||||
error -> myFactory.createRemoveDuplicateExtendsAction(HighlightUtil.formatClass(error.context())));
|
||||
error -> myFactory.createRemoveDuplicateExtendsAction(HighlightNamesUtil.formatClass(error.context())));
|
||||
multi(CLASS_REFERENCE_LIST_INNER_PRIVATE, error -> List.of(
|
||||
addModifierFix(error.context(), PsiModifier.PUBLIC),
|
||||
addModifierFix(error.context(), PsiModifier.PROTECTED)));
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
package com.intellij.codeInsight.daemon.impl.analysis;
|
||||
|
||||
import com.intellij.codeInsight.daemon.impl.HighlightVisitor;
|
||||
import com.intellij.java.codeserver.highlighting.JavaErrorCollector;
|
||||
import com.intellij.openapi.editor.colors.TextAttributesScheme;
|
||||
import com.intellij.openapi.project.DumbAware;
|
||||
import com.intellij.openapi.project.DumbService;
|
||||
@@ -23,7 +24,7 @@ import java.util.function.Supplier;
|
||||
* - color names, like "reassigned variables"/fields/statics etc.
|
||||
* - soft keywords
|
||||
* NO COMPILATION ERRORS
|
||||
* for other highlighting errors see {@link HighlightVisitorImpl}
|
||||
* for other highlighting errors see {@link JavaErrorCollector}
|
||||
*/
|
||||
final class JavaNamesHighlightVisitor extends JavaElementVisitor implements HighlightVisitor, DumbAware {
|
||||
private HighlightInfoHolder myHolder;
|
||||
@@ -178,7 +179,7 @@ final class JavaNamesHighlightVisitor extends JavaElementVisitor implements High
|
||||
}
|
||||
|
||||
private void doVisitReferenceElement(@NotNull PsiJavaCodeReferenceElement ref) {
|
||||
JavaResolveResult result = HighlightVisitorImpl.resolveOptimised(ref, myFile);
|
||||
JavaResolveResult result = LocalRefUseInfo.resolveOptimised(ref, myFile);
|
||||
PsiElement resolved = result != null ? result.getElement() : null;
|
||||
|
||||
if (resolved instanceof PsiVariable variable) {
|
||||
|
||||
@@ -1,88 +0,0 @@
|
||||
// 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.codeInsight.JavaModuleSystemEx.ErrorWithFixes;
|
||||
import com.intellij.codeInsight.daemon.impl.HighlightInfo;
|
||||
import com.intellij.codeInsight.daemon.impl.HighlightInfoType;
|
||||
import com.intellij.codeInsight.intention.IntentionAction;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.impl.PsiClassImplUtil;
|
||||
import com.intellij.psi.util.PsiUtil;
|
||||
import org.jetbrains.annotations.Nls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public final class LambdaHighlightingUtil {
|
||||
// 15.13 | 15.27
|
||||
// It is a compile-time error if any class or interface mentioned by either U or the function type of U
|
||||
// is not accessible from the class or interface in which the method reference expression appears.
|
||||
static HighlightInfo.Builder checkFunctionalInterfaceTypeAccessible(@NotNull Project project,
|
||||
@NotNull PsiFunctionalExpression expression,
|
||||
@NotNull PsiType functionalInterfaceType) {
|
||||
return checkFunctionalInterfaceTypeAccessible(project, expression, functionalInterfaceType, true);
|
||||
}
|
||||
|
||||
private static HighlightInfo.Builder checkFunctionalInterfaceTypeAccessible(@NotNull Project project, @NotNull PsiFunctionalExpression expression,
|
||||
@NotNull PsiType functionalInterfaceType,
|
||||
boolean checkFunctionalTypeSignature) {
|
||||
PsiClassType.ClassResolveResult resolveResult =
|
||||
PsiUtil.resolveGenericsClassInType(PsiClassImplUtil.correctType(functionalInterfaceType, expression.getResolveScope()));
|
||||
PsiClass psiClass = resolveResult.getElement();
|
||||
if (psiClass == null) {
|
||||
return null;
|
||||
}
|
||||
if (PsiUtil.isAccessible(project, psiClass, expression, null)) {
|
||||
for (PsiType type : resolveResult.getSubstitutor().getSubstitutionMap().values()) {
|
||||
if (type != null) {
|
||||
HighlightInfo.Builder info = checkFunctionalInterfaceTypeAccessible(project, expression, type, false);
|
||||
if (info != null) {
|
||||
return info;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PsiMethod psiMethod = checkFunctionalTypeSignature ? LambdaUtil.getFunctionalInterfaceMethod(resolveResult) : null;
|
||||
if (psiMethod != null) {
|
||||
PsiSubstitutor substitutor = LambdaUtil.getSubstitutor(psiMethod, resolveResult);
|
||||
for (PsiParameter parameter : psiMethod.getParameterList().getParameters()) {
|
||||
PsiType substitute = substitutor.substitute(parameter.getType());
|
||||
if (substitute != null) {
|
||||
HighlightInfo.Builder info = checkFunctionalInterfaceTypeAccessible(project, expression, substitute, false);
|
||||
if (info != null) {
|
||||
return info;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PsiType substitute = substitutor.substitute(psiMethod.getReturnType());
|
||||
if (substitute != null) {
|
||||
return checkFunctionalInterfaceTypeAccessible(project, expression, substitute, false);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
else {
|
||||
Pair<@Nls String, List<IntentionAction>> problem =
|
||||
HighlightUtil.accessProblemDescriptionAndFixes(expression, psiClass, resolveResult);
|
||||
HighlightInfo.Builder info =
|
||||
HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(expression).descriptionAndTooltip(problem.first);
|
||||
if (problem.second != null) {
|
||||
problem.second.forEach(fix -> info.registerFix(fix, List.of(), null, null, null));
|
||||
}
|
||||
return info;
|
||||
}
|
||||
|
||||
final ErrorWithFixes moduleProblem = HighlightUtil.checkModuleAccess(psiClass, expression, resolveResult);
|
||||
if (moduleProblem != null) {
|
||||
HighlightInfo.Builder info =
|
||||
HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(expression).descriptionAndTooltip(moduleProblem.message);
|
||||
moduleProblem.fixes.forEach(fix -> info.registerFix(fix, List.of(), null, null, null));
|
||||
return info;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -15,6 +15,7 @@ import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.impl.IncompleteModelUtil;
|
||||
import com.intellij.psi.impl.source.resolve.JavaResolveUtil;
|
||||
import com.intellij.psi.impl.source.tree.java.PsiReferenceExpressionImpl;
|
||||
import com.intellij.psi.infos.CandidateInfo;
|
||||
import com.intellij.psi.util.*;
|
||||
import com.intellij.util.ArrayUtilRt;
|
||||
@@ -246,6 +247,20 @@ public final class LocalRefUseInfo {
|
||||
return false;
|
||||
}
|
||||
|
||||
static @Nullable JavaResolveResult resolveOptimised(@NotNull PsiJavaCodeReferenceElement ref, @NotNull PsiFile containingFile) {
|
||||
try {
|
||||
if (ref instanceof PsiReferenceExpressionImpl) {
|
||||
PsiReferenceExpressionImpl.OurGenericsResolver resolver = PsiReferenceExpressionImpl.OurGenericsResolver.INSTANCE;
|
||||
JavaResolveResult[] results = JavaResolveUtil.resolveWithContainingFile(ref, resolver, true, true, containingFile);
|
||||
return results.length == 1 ? results[0] : JavaResolveResult.EMPTY;
|
||||
}
|
||||
return ref.advancedResolve(true);
|
||||
}
|
||||
catch (IndexNotReadyException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static class Builder extends JavaRecursiveElementWalkingVisitor {
|
||||
private final @NotNull PsiFile myFile;
|
||||
private final @NotNull Set<PsiAnchor> myDclsUsedMap;
|
||||
@@ -399,7 +414,7 @@ public final class LocalRefUseInfo {
|
||||
public void visitReferenceElement(@NotNull PsiJavaCodeReferenceElement ref) {
|
||||
super.visitReferenceElement(ref);
|
||||
if (!(ref instanceof PsiImportStaticReferenceElement)) {
|
||||
JavaResolveResult result = HighlightVisitorImpl.resolveOptimised(ref, myFile);
|
||||
JavaResolveResult result = resolveOptimised(ref, myFile);
|
||||
if (result != null) {
|
||||
registerReference(ref, result);
|
||||
}
|
||||
@@ -409,7 +424,7 @@ public final class LocalRefUseInfo {
|
||||
@Override
|
||||
public void visitReferenceExpression(@NotNull PsiReferenceExpression ref) {
|
||||
super.visitReferenceExpression(ref);
|
||||
JavaResolveResult result = HighlightVisitorImpl.resolveOptimised(ref, myFile);
|
||||
JavaResolveResult result = resolveOptimised(ref, myFile);
|
||||
if (result != null) {
|
||||
registerReference(ref, result);
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ package com.intellij.codeInsight.daemon.impl.quickfix;
|
||||
import com.intellij.codeInsight.BlockUtils;
|
||||
import com.intellij.codeInsight.daemon.QuickFixBundle;
|
||||
import com.intellij.codeInsight.daemon.impl.analysis.HighlightMessageUtil;
|
||||
import com.intellij.codeInsight.daemon.impl.analysis.HighlightUtil;
|
||||
import com.intellij.codeInsight.daemon.impl.analysis.HighlightNamesUtil;
|
||||
import com.intellij.codeInspection.util.IntentionName;
|
||||
import com.intellij.java.JavaBundle;
|
||||
import com.intellij.modcommand.ActionContext;
|
||||
@@ -75,7 +75,7 @@ public class AccessStaticViaInstanceFix extends PsiBasedModCommandAction<PsiRefe
|
||||
? "access.static.method.via.class.reference.text"
|
||||
: "access.static.field.via.class.reference.text",
|
||||
HighlightMessageUtil.getSymbolName(member, substitutor, PsiFormatUtilBase.SHOW_TYPE),
|
||||
HighlightUtil.formatClass(aClass, false));
|
||||
HighlightNamesUtil.formatClass(aClass, false));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
package com.intellij.codeInsight.daemon.impl.quickfix;
|
||||
|
||||
import com.intellij.codeInsight.daemon.QuickFixBundle;
|
||||
import com.intellij.codeInsight.daemon.impl.analysis.HighlightUtil;
|
||||
import com.intellij.codeInsight.daemon.impl.analysis.HighlightNamesUtil;
|
||||
import com.intellij.codeInspection.util.IntentionName;
|
||||
import com.intellij.modcommand.ActionContext;
|
||||
import com.intellij.modcommand.ModPsiUpdater;
|
||||
@@ -22,8 +22,8 @@ public class MoveBoundClassToFrontFix extends PsiUpdateModCommandAction<PsiTypeP
|
||||
PsiClass psiClass = classToExtendFrom.resolve();
|
||||
|
||||
myName = QuickFixBundle.message("move.bound.class.to.front.fix.text",
|
||||
psiClass == null ? "<null>" : HighlightUtil.formatClass(psiClass),
|
||||
HighlightUtil.formatClass(aClass));
|
||||
psiClass == null ? "<null>" : HighlightNamesUtil.formatClass(psiClass),
|
||||
HighlightNamesUtil.formatClass(aClass));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -14,11 +14,9 @@ import com.intellij.java.JavaBundle
|
||||
import com.intellij.java.codeserver.core.JpmsModuleAccessInfo
|
||||
import com.intellij.java.codeserver.core.JpmsModuleAccessInfo.JpmsModuleAccessMode
|
||||
import com.intellij.java.codeserver.core.JpmsModuleAccessInfo.JpmsModuleAccessProblem
|
||||
import com.intellij.java.codeserver.core.JpmsModuleInfo
|
||||
import com.intellij.java.codeserver.core.JpmsModuleInfo.TargetModuleInfo
|
||||
import com.intellij.openapi.roots.ProjectFileIndex
|
||||
import com.intellij.pom.java.JavaFeature
|
||||
import com.intellij.psi.*
|
||||
import com.intellij.psi.util.PsiUtil
|
||||
import org.jetbrains.annotations.Nls
|
||||
|
||||
/**
|
||||
@@ -32,13 +30,13 @@ internal class JavaPlatformModuleSystem : JavaModuleSystemEx {
|
||||
|
||||
override fun isAccessible(targetPackageName: String, targetFile: PsiFile?, place: PsiElement): Boolean {
|
||||
val useFile = place.containingFile?.originalFile ?: return true
|
||||
val infos = findTargetModuleInfos(targetPackageName, targetFile, useFile) ?: return true
|
||||
val infos = JpmsModuleInfo.findTargetModuleInfos(targetPackageName, targetFile, useFile) ?: return true
|
||||
return infos.isNotEmpty() && infos.any { info -> info.accessAt(useFile).checkAccess(useFile, JpmsModuleAccessMode.EXPORT) == null }
|
||||
}
|
||||
|
||||
override fun checkAccess(targetPackageName: String, targetFile: PsiFile?, place: PsiElement): ErrorWithFixes? {
|
||||
val useFile = place.containingFile?.originalFile ?: return null
|
||||
val infos = findTargetModuleInfos(targetPackageName, targetFile, useFile) ?: return null
|
||||
val infos = JpmsModuleInfo.findTargetModuleInfos(targetPackageName, targetFile, useFile) ?: return null
|
||||
if (infos.isEmpty()) {
|
||||
return ErrorWithFixes(JavaErrorBundle.message("package.not.found", targetPackageName))
|
||||
}
|
||||
@@ -59,36 +57,6 @@ internal class JavaPlatformModuleSystem : JavaModuleSystemEx {
|
||||
return TargetModuleInfo(targetModule, "").accessAt(useFile).checkModuleAccess(place) == null
|
||||
}
|
||||
|
||||
private fun findTargetModuleInfos(targetPackageName: String, targetFile: PsiFile?, useFile: PsiFile): List<TargetModuleInfo>? {
|
||||
val originalTargetFile = targetFile?.originalFile
|
||||
if (!PsiUtil.isAvailable(JavaFeature.MODULES, useFile)) return null
|
||||
|
||||
val useVFile = useFile.virtualFile
|
||||
val index = ProjectFileIndex.getInstance(useFile.project)
|
||||
if (useVFile != null && index.isInLibrarySource(useVFile)) return null
|
||||
if (originalTargetFile != null && originalTargetFile.isPhysical) {
|
||||
return listOf(TargetModuleInfo(originalTargetFile, targetPackageName))
|
||||
}
|
||||
if (useVFile == null) return null
|
||||
|
||||
val target = JavaPsiFacade.getInstance(useFile.project).findPackage(targetPackageName) ?: return null
|
||||
val module = index.getModuleForFile(useVFile) ?: return null
|
||||
val test = index.isInTestSourceContent(useVFile)
|
||||
val moduleScope = module.getModuleWithDependenciesAndLibrariesScope(test)
|
||||
val dirs = target.getDirectories(moduleScope)
|
||||
val packageName = target.qualifiedName
|
||||
if (dirs.isEmpty()) {
|
||||
return if (target.getFiles(moduleScope).isEmpty()) {
|
||||
listOf()
|
||||
}
|
||||
else {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
return dirs.map { dir -> TargetModuleInfo(dir, packageName) }
|
||||
}
|
||||
|
||||
fun JpmsModuleAccessInfo.toErrorWithFixes(problem: JpmsModuleAccessProblem, place: PsiElement): ErrorWithFixes {
|
||||
return ErrorWithFixes(getMessage(problem), getFixes(problem, place))
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ import test.*;
|
||||
|
||||
class Test {
|
||||
{
|
||||
I i = <error descr="'test.A' is not public in 'test'. Cannot be accessed from outside package">(a) -> {}</error>;
|
||||
J j = <error descr="'test.A' is not public in 'test'. Cannot be accessed from outside package">() -> null</error>;
|
||||
I i = <error descr="'test.A' is not public in 'test'. Cannot be accessed from outside package">(a) -> {};</error>
|
||||
J j = <error descr="'test.A' is not public in 'test'. Cannot be accessed from outside package">() -> null;</error>
|
||||
}
|
||||
}
|
||||
@@ -10,7 +10,7 @@ class Outer {
|
||||
class Usage {
|
||||
void test(Outer outer) {
|
||||
outer.m(<error descr="'Outer.Inner' has private access in 'Outer'">() -> {}</error>);
|
||||
outer.m(<error descr="'Outer.Inner' has private access in 'Outer'">this::foo</error>);
|
||||
outer.m(this::<error descr="'Outer.Inner' has private access in 'Outer'">foo</error>);
|
||||
}
|
||||
|
||||
void foo() {}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import static <error descr="Static method may only be called on its containing interface">p.Foo.FooEx.foo;</error>
|
||||
import static p.Foo.FooEx.<error descr="Static method may only be called on its containing interface">foo</error>;
|
||||
|
||||
class FooImpl {
|
||||
public void baz() {
|
||||
|
||||
@@ -4,7 +4,7 @@ import java.util.function.Consumer;
|
||||
class Test {
|
||||
{
|
||||
consume(<error descr="'Outer.A' has private access in 'Outer'">o -> {}</error>, new Outer.B(), new Outer.C());
|
||||
consume(<error descr="'Outer.A' has private access in 'Outer'">Test::foo</error>, new Outer.B(), new Outer.C());
|
||||
consume(Test::<error descr="'Outer.A' has private access in 'Outer'">foo</error>, new Outer.B(), new Outer.C());
|
||||
}
|
||||
|
||||
private static void foo(Object o) {}
|
||||
|
||||
@@ -3,7 +3,7 @@ package org.jetbrains.plugins.javaFX.fxml;
|
||||
|
||||
import com.intellij.codeInsight.AnnotationUtil;
|
||||
import com.intellij.codeInsight.daemon.Validator;
|
||||
import com.intellij.codeInsight.daemon.impl.analysis.HighlightUtil;
|
||||
import com.intellij.codeInsight.daemon.impl.analysis.HighlightNamesUtil;
|
||||
import com.intellij.codeInsight.daemon.impl.analysis.JavaGenericsUtil;
|
||||
import com.intellij.codeInspection.util.InspectionMessage;
|
||||
import com.intellij.ide.highlighter.JavaFileType;
|
||||
@@ -611,7 +611,7 @@ public final class JavaFxPsiUtil {
|
||||
private static boolean unableToCoerceError(@NotNull PsiType targetType, @NotNull PsiClass fromClass,
|
||||
@NotNull BiConsumer<? super String, ? super Validator.ValidationHost.ErrorType> messageConsumer) {
|
||||
messageConsumer.accept(
|
||||
JavaFXBundle.message("unable.to.coerce.error", HighlightUtil.formatClass(fromClass), targetType.getCanonicalText()),
|
||||
JavaFXBundle.message("unable.to.coerce.error", HighlightNamesUtil.formatClass(fromClass), targetType.getCanonicalText()),
|
||||
Validator.ValidationHost.ErrorType.ERROR);
|
||||
return false;
|
||||
}
|
||||
@@ -619,7 +619,7 @@ public final class JavaFxPsiUtil {
|
||||
private static boolean unrelatedTypesWarning(@NotNull PsiType targetType, @NotNull PsiClass fromClass,
|
||||
@NotNull BiConsumer<? super @InspectionMessage String, ? super Validator.ValidationHost.ErrorType> messageConsumer) {
|
||||
messageConsumer.accept(
|
||||
JavaFXBundle.message("conversion.between.unrelated.types.error", HighlightUtil.formatClass(fromClass), targetType.getCanonicalText()),
|
||||
JavaFXBundle.message("conversion.between.unrelated.types.error", HighlightNamesUtil.formatClass(fromClass), targetType.getCanonicalText()),
|
||||
Validator.ValidationHost.ErrorType.WARNING);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@ package org.jetbrains.kotlin.idea.java
|
||||
import com.intellij.codeInsight.ClassUtil.getAnyMethodToImplement
|
||||
import com.intellij.codeInsight.daemon.JavaErrorBundle
|
||||
import com.intellij.codeInsight.daemon.impl.analysis.HighlightNamesUtil
|
||||
import com.intellij.codeInsight.daemon.impl.analysis.HighlightUtil
|
||||
import com.intellij.codeInsight.daemon.impl.analysis.JavaHighlightUtil
|
||||
import com.intellij.codeInsight.intention.QuickFixFactory
|
||||
import com.intellij.lang.annotation.AnnotationHolder
|
||||
@@ -77,8 +76,8 @@ class UnimplementedKotlinInterfaceMemberAnnotator : Annotator {
|
||||
private fun report(method: KtLightMethod, holder: AnnotationHolder, psiClass: PsiClass) {
|
||||
val key = if (psiClass is PsiEnumConstantInitializer) "enum.constant.should.implement.method" else "class.must.be.abstract"
|
||||
val message = JavaErrorBundle.message(
|
||||
key, HighlightUtil.formatClass(psiClass, false), JavaHighlightUtil.formatMethod(method),
|
||||
HighlightUtil.formatClass(method.containingClass, false)
|
||||
key, HighlightNamesUtil.formatClass(psiClass, false), JavaHighlightUtil.formatMethod(method),
|
||||
HighlightNamesUtil.formatClass(method.containingClass, false)
|
||||
)
|
||||
val quickFixFactory = QuickFixFactory.getInstance()
|
||||
var error = holder.newAnnotation(HighlightSeverity.ERROR, message)
|
||||
|
||||
Reference in New Issue
Block a user