mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-04-30 10:20:15 +07:00
[java-highlighting] IDEA-355777 Support JEP 477: implicit imports
- support implicit import for java.io.IO - reimplement implicit static imports GitOrigin-RevId: 3e2650128d43b1cff40c21c0539869070c6094d0
This commit is contained in:
committed by
intellij-monorepo-bot
parent
6827bb2045
commit
07178b990f
@@ -1,18 +1,4 @@
|
||||
/*
|
||||
* Copyright 2003-2023 Dave Griffith, Bas Leijdekkers
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.siyeh.ig.psiutils;
|
||||
|
||||
import com.intellij.openapi.project.Project;
|
||||
@@ -23,9 +9,7 @@ import com.intellij.psi.util.InheritanceUtil;
|
||||
import com.intellij.psi.util.*;
|
||||
import com.intellij.util.ObjectUtils;
|
||||
import com.siyeh.HardcodedMethodConstants;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.annotations.*;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
@@ -429,6 +413,7 @@ public final class ImportUtils {
|
||||
if (memberName == null) {
|
||||
return false;
|
||||
}
|
||||
if (hasImplicitStaticImport(javaFile, memberClass.getQualifiedName() + "." + memberName)) return true;
|
||||
final PsiImportStatementBase existingImportStatement = importList.findSingleImportStatement(memberName);
|
||||
if (existingImportStatement instanceof PsiImportStaticStatement importStaticStatement) {
|
||||
final PsiClass importClass = importStaticStatement.resolveTargetClass();
|
||||
@@ -449,6 +434,60 @@ public final class ImportUtils {
|
||||
return false;
|
||||
}
|
||||
|
||||
private static boolean hasImplicitStaticImport(@NotNull PsiJavaFile file, @NotNull String name) {
|
||||
return createImplicitImportChecker(file).isImplicitlyImported(new Import(name, true));
|
||||
}
|
||||
|
||||
|
||||
@Contract("_ -> new")
|
||||
public static @NotNull ImportUtils.ImplicitImportChecker createImplicitImportChecker(@NotNull PsiJavaFile file) {
|
||||
return new ImplicitImportChecker(file);
|
||||
}
|
||||
|
||||
/**
|
||||
* The ImplicitImportChecker class provides functionality to check if a given import is implicitly
|
||||
* imported in the context of a PsiJavaFile. It tracks both static member imports and package imports.
|
||||
*/
|
||||
public static class ImplicitImportChecker {
|
||||
|
||||
@NotNull
|
||||
private final Map<String, PsiJavaFile.StaticMember> myStaticImportStatements = new HashMap<>();
|
||||
|
||||
@NotNull
|
||||
private final Set<String> packages = new HashSet<>();
|
||||
|
||||
private ImplicitImportChecker(@NotNull PsiJavaFile file) {
|
||||
for (PsiJavaFile.StaticMember imp : file.getImplicitlyImportedStaticMembers()) {
|
||||
myStaticImportStatements.put(imp.getContainingClass(), imp);
|
||||
}
|
||||
packages.addAll(Arrays.asList(file.getImplicitlyImportedPackages()));
|
||||
}
|
||||
|
||||
public boolean isImplicitlyImported(@NotNull Import name) {
|
||||
String packageOrClassName = getPackageOrClassName(name.name);
|
||||
if (!name.isStatic && packages.contains(packageOrClassName)) return true;
|
||||
PsiJavaFile.StaticMember base = myStaticImportStatements.get(packageOrClassName);
|
||||
if (base != null) {
|
||||
if (!name.isStatic) return false;
|
||||
if (base.isOnDemand()) {
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
return name.name.equals(base.getContainingClass() + "." + base.getMemberName());
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static @NotNull String getPackageOrClassName(@NotNull String className){
|
||||
int dotIndex = className.lastIndexOf('.');
|
||||
return dotIndex < 0 ? "" : className.substring(0, dotIndex);
|
||||
}
|
||||
|
||||
@ApiStatus.Internal
|
||||
public record Import(String name, boolean isStatic) {}
|
||||
|
||||
private static boolean memberReferenced(PsiMember member, PsiElement context) {
|
||||
final MemberReferenceVisitor visitor = new MemberReferenceVisitor(member);
|
||||
context.accept(visitor);
|
||||
|
||||
@@ -106,6 +106,7 @@ feature.javadoc.snippets=@snippet in Javadoc
|
||||
feature.pattern.guard.and.record.patterns=Pattern guards and record patterns
|
||||
feature.record.patterns.in.for.each=Record patterns in for-each loops
|
||||
feature.primitive.types.in.patterns=Primitive types in patterns, instanceof and switch
|
||||
feature.implicit.import.in.implicit.classes=Implicit import in implicitly declared classes
|
||||
feature.enum.qualified.name.in.switch=Qualified enum as a constant in switch
|
||||
feature.string.templates=String templates
|
||||
feature.unnamed.vars=Unnamed patterns and variables
|
||||
|
||||
@@ -111,6 +111,7 @@ public enum JavaFeature {
|
||||
* Implementation for java 23
|
||||
*/
|
||||
PRIMITIVE_TYPES_IN_PATTERNS(LanguageLevel.JDK_X, "feature.primitive.types.in.patterns"),
|
||||
IMPLICIT_IMPORT_IN_IMPLICIT_CLASSES(LanguageLevel.JDK_X, "feature.implicit.import.in.implicit.classes"),
|
||||
;
|
||||
|
||||
private final @NotNull LanguageLevel myLevel;
|
||||
|
||||
@@ -6,7 +6,6 @@ import com.intellij.codeInsight.lookup.LookupElement;
|
||||
import com.intellij.codeInsight.lookup.VariableLookupItem;
|
||||
import com.intellij.featureStatistics.FeatureUsageTracker;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.util.ImportsUtil;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.psi.util.PsiUtil;
|
||||
import com.intellij.util.ObjectUtils;
|
||||
@@ -26,11 +25,9 @@ public class JavaStaticMemberProcessor extends StaticMemberProcessor {
|
||||
final PsiImportList importList = ((PsiJavaFile)file).getImportList();
|
||||
if (importList != null) {
|
||||
for (PsiImportStaticStatement statement : importList.getImportStaticStatements()) {
|
||||
if (!ImportsUtil.isImplicitImport(statement)) {
|
||||
PsiClass aClass = statement.resolveTargetClass();
|
||||
if (aClass != null) {
|
||||
importMembersOf(aClass);
|
||||
}
|
||||
PsiClass aClass = statement.resolveTargetClass();
|
||||
if (aClass != null) {
|
||||
importMembersOf(aClass);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,6 @@ import com.intellij.openapi.util.Comparing;
|
||||
import com.intellij.openapi.util.NlsSafe;
|
||||
import com.intellij.openapi.util.Predicates;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.pom.java.JavaFeature;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.codeStyle.CodeStyleSettings;
|
||||
import com.intellij.psi.codeStyle.JavaCodeStyleSettings;
|
||||
@@ -45,7 +44,6 @@ import com.intellij.util.containers.MultiMap;
|
||||
import com.intellij.util.containers.NotNullList;
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntMap;
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@@ -54,6 +52,7 @@ import java.util.*;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static com.siyeh.ig.psiutils.ImportUtils.*;
|
||||
import static java.util.stream.Collectors.toSet;
|
||||
|
||||
public final class ImportHelper{
|
||||
@@ -61,7 +60,6 @@ public final class ImportHelper{
|
||||
|
||||
private final JavaCodeStyleSettings mySettings;
|
||||
private static final @NonNls String JAVA_LANG_PACKAGE = "java.lang";
|
||||
private static final String STRING_TEMPLATE_STR = "java.lang.StringTemplate.STR";
|
||||
|
||||
public ImportHelper(@NotNull JavaCodeStyleSettings settings) {
|
||||
mySettings = settings;
|
||||
@@ -99,7 +97,7 @@ public final class ImportHelper{
|
||||
collectNamesToImport(file, nonImports)
|
||||
.stream()
|
||||
.filter(filter)
|
||||
.sorted(Comparator.comparing(o -> o.name))
|
||||
.sorted(Comparator.comparing(o -> o.name()))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
List<Import> resultList = sortItemsAccordingToSettings(imports, mySettings);
|
||||
@@ -109,8 +107,8 @@ public final class ImportHelper{
|
||||
|
||||
MultiMap<String, String> conflictingMemberNames = new MultiMap<>();
|
||||
for (Import anImport : resultList) {
|
||||
if (anImport.isStatic) {
|
||||
conflictingMemberNames.putValue(StringUtil.getShortName(anImport.name), StringUtil.getPackageName(anImport.name));
|
||||
if (anImport.isStatic()) {
|
||||
conflictingMemberNames.putValue(StringUtil.getShortName(anImport.name()), StringUtil.getPackageName(anImport.name()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -128,8 +126,7 @@ public final class ImportHelper{
|
||||
classesToUseSingle.addAll(toReimport);
|
||||
|
||||
try {
|
||||
boolean stringTemplates = PsiUtil.isAvailable(JavaFeature.STRING_TEMPLATES, file);
|
||||
StringBuilder text = buildImportListText(resultList, classesOrPackagesToImportOnDemand.keySet(), classesToUseSingle, stringTemplates);
|
||||
StringBuilder text = buildImportListText(resultList, classesOrPackagesToImportOnDemand.keySet(), classesToUseSingle, createImplicitImportChecker(file));
|
||||
for (PsiElement nonImport : nonImports) {
|
||||
text.append("\n").append(nonImport.getText());
|
||||
}
|
||||
@@ -150,16 +147,15 @@ public final class ImportHelper{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static void collectOnDemandImports(@NotNull List<Import> resultList,
|
||||
@NotNull JavaCodeStyleSettings settings,
|
||||
@NotNull Map<String, Boolean> outClassesOrPackagesToImportOnDemand) {
|
||||
Object2IntMap<String> packageToCountMap = new Object2IntOpenHashMap<>();
|
||||
Object2IntMap <String> classToCountMap = new Object2IntOpenHashMap<>();
|
||||
for (Import anImport : resultList) {
|
||||
String packageOrClassName = getPackageOrClassName(anImport.name);
|
||||
String packageOrClassName = getPackageOrClassName(anImport.name());
|
||||
if (packageOrClassName.isEmpty()) continue;
|
||||
Object2IntMap<String> map = anImport.isStatic ? classToCountMap : packageToCountMap;
|
||||
Object2IntMap<String> map = anImport.isStatic() ? classToCountMap : packageToCountMap;
|
||||
map.put(packageOrClassName, map.getOrDefault(packageOrClassName, 0) + 1);
|
||||
}
|
||||
|
||||
@@ -180,7 +176,7 @@ public final class ImportHelper{
|
||||
PackageEntry[] entries = settings.IMPORT_LAYOUT_TABLE.getEntries();
|
||||
for(int i = 0; i < imports.size(); i++){
|
||||
Import anImport = imports.get(i);
|
||||
entryForName[i] = findEntryIndex(anImport.name, anImport.isStatic, entries);
|
||||
entryForName[i] = findEntryIndex(anImport.name(), anImport.isStatic(), entries);
|
||||
}
|
||||
|
||||
List<Import> resultList = new ArrayList<>(imports.size());
|
||||
@@ -210,7 +206,7 @@ public final class ImportHelper{
|
||||
List<PsiClass> onDemandElements = ContainerUtil.map(onDemandImportsList, onDemandName -> facade.findClass(onDemandName, resolveScope));
|
||||
Set<String> namesToUseSingle = new HashSet<>();
|
||||
for (Import anImport : imports) {
|
||||
String name = anImport.name;
|
||||
String name = anImport.name();
|
||||
String prefix = getPackageOrClassName(name);
|
||||
if (prefix.isEmpty()) continue;
|
||||
boolean isImplicitlyImported = implicitlyImportedPackages.contains(prefix);
|
||||
@@ -234,7 +230,7 @@ public final class ImportHelper{
|
||||
for (int i = 0; i < onDemandImportsList.size(); i++) {
|
||||
String onDemandName = onDemandImportsList.get(i);
|
||||
if (prefix.equals(onDemandName)) continue;
|
||||
if (anImport.isStatic) {
|
||||
if (anImport.isStatic()) {
|
||||
PsiClass aClass = onDemandElements.get(i);
|
||||
if (aClass != null) {
|
||||
PsiField field = aClass.findFieldByName(shortName, true);
|
||||
@@ -294,7 +290,7 @@ public final class ImportHelper{
|
||||
GlobalSearchScope resolveScope = file.getResolveScope();
|
||||
Map<String, Set<String>> classNames = new HashMap<>();
|
||||
JavaPsiFacade facade = JavaPsiFacade.getInstance(file.getProject());
|
||||
for (int i = onDemands.size() - 1; i>= 0; i--) {
|
||||
for (int i = onDemands.size() - 1; i >= 0; i--) {
|
||||
String onDemand = onDemands.get(i);
|
||||
PsiPackage aPackage = facade.findPackage(onDemand);
|
||||
boolean isStatic = ObjectUtils.notNull(onDemandImports.get(onDemand), Boolean.FALSE);
|
||||
@@ -360,14 +356,14 @@ public final class ImportHelper{
|
||||
private static @NotNull StringBuilder buildImportListText(@NotNull List<Import> imports,
|
||||
@NotNull Set<String> packagesOrClassesToImportOnDemand,
|
||||
@NotNull Set<String> namesToUseSingle,
|
||||
boolean stringTemplates) {
|
||||
@NotNull ImplicitImportChecker implicitImportContext) {
|
||||
Set<Import> importedPackagesOrClasses = new HashSet<>();
|
||||
@NonNls StringBuilder buffer = new StringBuilder();
|
||||
for (Import importedName : imports) {
|
||||
String name = importedName.name;
|
||||
boolean isStatic = importedName.isStatic;
|
||||
String name = importedName.name();
|
||||
boolean isStatic = importedName.isStatic();
|
||||
String packageOrClassName = getPackageOrClassName(name);
|
||||
boolean implicitlyImported = JAVA_LANG_PACKAGE.equals(packageOrClassName) || stringTemplates && STRING_TEMPLATE_STR.equals(name);
|
||||
boolean implicitlyImported = implicitImportContext.isImplicitlyImported(importedName);
|
||||
boolean useOnDemand = implicitlyImported || packagesOrClassesToImportOnDemand.contains(packageOrClassName);
|
||||
Import current = new Import(packageOrClassName, isStatic);
|
||||
if (namesToUseSingle.remove(name)) {
|
||||
@@ -628,7 +624,7 @@ public final class ImportHelper{
|
||||
|
||||
@Override
|
||||
public void visitReferenceElement(@NotNull PsiJavaCodeReferenceElement reference) {
|
||||
if (shortClassName.equals(reference.getReferenceName()) &&
|
||||
if (shortClassName.equals(reference.getReferenceName()) &&
|
||||
file.getManager().areElementsEquivalent(reference.resolve(), aClass)) {
|
||||
foundRef[0] = true;
|
||||
}
|
||||
@@ -809,9 +805,9 @@ public final class ImportHelper{
|
||||
Collection<Import> resultList = collectNamesToImport(file, new ArrayList<>());
|
||||
String qualifiedName = psiClass.getQualifiedName();
|
||||
for (Import anImport : resultList) {
|
||||
if (anImport.isStatic &&
|
||||
referenceName.equals(StringUtil.getShortName(anImport.name)) &&
|
||||
!StringUtil.getPackageName(anImport.name).equals(qualifiedName)) {
|
||||
if (anImport.isStatic() &&
|
||||
referenceName.equals(StringUtil.getShortName(anImport.name())) &&
|
||||
!StringUtil.getPackageName(anImport.name()).equals(qualifiedName)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -1024,12 +1020,4 @@ public final class ImportHelper{
|
||||
if (!packageName.isEmpty() && className.charAt(packageName.length()) != '.') return false;
|
||||
return className.indexOf('.', packageName.length() + 1) < 0;
|
||||
}
|
||||
|
||||
private static @NotNull String getPackageOrClassName(@NotNull String className){
|
||||
int dotIndex = className.lastIndexOf('.');
|
||||
return dotIndex < 0 ? "" : className.substring(0, dotIndex);
|
||||
}
|
||||
|
||||
@ApiStatus.Internal
|
||||
public record Import(String name, boolean isStatic) {}
|
||||
}
|
||||
|
||||
@@ -420,6 +420,12 @@ public interface PsiElementFactory extends PsiJavaParserFacade, JVMElementFactor
|
||||
PsiImportStaticStatement createImportStaticStatement(@NotNull PsiClass aClass, @NotNull String memberName)
|
||||
throws IncorrectOperationException;
|
||||
|
||||
/**
|
||||
* Creates an {@code import static} statement for importing the specified member
|
||||
* from the specified class.
|
||||
*/
|
||||
@NotNull PsiImportStaticStatement createImportStaticStatementFromText(@NotNull String classFullyQualifiedName, @NotNull String memberName) throws IncorrectOperationException;
|
||||
|
||||
/**
|
||||
* Creates a parameter list from the specified parameter names and types.
|
||||
*
|
||||
|
||||
@@ -1,21 +1,8 @@
|
||||
/*
|
||||
* Copyright 2000-2016 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
// 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.pom.java.LanguageLevel;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -102,4 +89,48 @@ public interface PsiJavaFile extends PsiImportHolder, PsiClassOwner, AbstractBas
|
||||
*/
|
||||
@Nullable
|
||||
PsiJavaModule getModuleDeclaration();
|
||||
|
||||
/**
|
||||
* @return the array of implicitly imported static members.
|
||||
*/
|
||||
@ApiStatus.Experimental
|
||||
default @NotNull StaticMember @NotNull [] getImplicitlyImportedStaticMembers() {
|
||||
return StaticMember.EMPTY_ARRAY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Class representing a static member represented implicitly imported static members.
|
||||
* if memberName is `*`, it is on demand import
|
||||
*/
|
||||
@ApiStatus.Experimental
|
||||
class StaticMember {
|
||||
public static final @NotNull StaticMember @NotNull [] EMPTY_ARRAY = new StaticMember[0];
|
||||
|
||||
private final @NotNull String myContainingClass;
|
||||
private final @NotNull String myMemberName;
|
||||
|
||||
private StaticMember(@NotNull String containingClass, @NotNull String memberName) {
|
||||
myContainingClass = containingClass;
|
||||
myMemberName = memberName;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public String getContainingClass() {
|
||||
return myContainingClass;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public String getMemberName() {
|
||||
return myMemberName;
|
||||
}
|
||||
|
||||
public boolean isOnDemand() {
|
||||
return "*".equals(myMemberName);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static StaticMember create(@NotNull String containingClass, @NotNull String memberName) {
|
||||
return new StaticMember(containingClass, memberName);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,6 @@
|
||||
package com.intellij.psi.util;
|
||||
|
||||
import com.intellij.openapi.util.Comparing;
|
||||
import com.intellij.openapi.util.Key;
|
||||
import com.intellij.psi.*;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@@ -12,7 +11,6 @@ import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
public final class ImportsUtil {
|
||||
private static final Key<Boolean> IS_IMPLICIT = Key.create("IMPORT_IS_IMPLICIT");
|
||||
|
||||
private ImportsUtil() {
|
||||
}
|
||||
@@ -89,12 +87,4 @@ public final class ImportsUtil {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static void markAsImplicitImport(@NotNull PsiImportStatementBase importStatement) {
|
||||
importStatement.putUserData(IS_IMPLICIT, Boolean.TRUE);
|
||||
}
|
||||
|
||||
public static boolean isImplicitImport(@NotNull PsiImportStatementBase importStatement) {
|
||||
return importStatement.getUserData(IS_IMPLICIT) == Boolean.TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -442,6 +442,13 @@ public final class PsiElementFactoryImpl extends PsiJavaParserFacadeImpl impleme
|
||||
return (PsiImportStaticStatement)CodeStyleManager.getInstance(myManager.getProject()).reformat(statement);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull PsiImportStaticStatement createImportStaticStatementFromText(@NotNull String classFullyQualifiedName, @NotNull String memberName) throws IncorrectOperationException {
|
||||
PsiJavaFile aFile = createDummyJavaFile("import static " + classFullyQualifiedName + "." + memberName + ";");
|
||||
PsiImportStatementBase statement = extractImport(aFile, true);
|
||||
return (PsiImportStaticStatement)CodeStyleManager.getInstance(myManager.getProject()).reformat(statement);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull PsiParameterList createParameterList(String @NotNull [] names, PsiType @NotNull [] types) throws IncorrectOperationException {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
|
||||
@@ -44,6 +44,7 @@ import java.util.*;
|
||||
|
||||
public final class PsiImplUtil {
|
||||
private static final Logger LOG = Logger.getInstance(PsiImplUtil.class);
|
||||
private static final String JAVA_IO_IO = "java.io.IO";
|
||||
|
||||
private PsiImplUtil() { }
|
||||
|
||||
@@ -846,27 +847,28 @@ public final class PsiImplUtil {
|
||||
}
|
||||
}
|
||||
|
||||
public static PsiImportStaticStatement[] getImplicitStaticImports(@NotNull PsiFile file) {
|
||||
PsiImportStaticStatement[] staticImports = new PsiImportStaticStatement[1];
|
||||
int counter = 0;
|
||||
|
||||
/**
|
||||
* Retrieves the implicit static imports for the given file.
|
||||
*
|
||||
* @param file the file for which to retrieve implicit static imports
|
||||
* @return an array of static members representing the implicit static imports
|
||||
*/
|
||||
@ApiStatus.Experimental
|
||||
public static @NotNull PsiJavaFile.StaticMember @NotNull[] getImplicitStaticImports(@NotNull PsiFile file) {
|
||||
List<PsiJavaFile.StaticMember> staticImports = new ArrayList<>();
|
||||
// java.lang.StringTemplate.STR
|
||||
if (PsiUtil.isAvailable(JavaFeature.STRING_TEMPLATES, file)) {
|
||||
final JavaPsiFacade psiFacade = JavaPsiFacade.getInstance(file.getProject());
|
||||
final PsiClass aClass = psiFacade.findClass(CommonClassNames.JAVA_LANG_STRING_TEMPLATE, file.getResolveScope());
|
||||
if (aClass != null) {
|
||||
final PsiImportStaticStatement stringTemplate = psiFacade.getElementFactory().createImportStaticStatement(aClass, "STR");
|
||||
staticImports[counter++] = stringTemplate;
|
||||
staticImports.add(PsiJavaFile.StaticMember.create(CommonClassNames.JAVA_LANG_STRING_TEMPLATE, "STR"));
|
||||
}
|
||||
|
||||
// java.io.IO.* for implicit classes
|
||||
if (PsiUtil.isAvailable(JavaFeature.IMPLICIT_IMPORT_IN_IMPLICIT_CLASSES, file) && file instanceof PsiJavaFile) {
|
||||
PsiClass[] classes = ((PsiJavaFile)file).getClasses();
|
||||
if (classes.length == 1 && classes[0] instanceof PsiImplicitClass) {
|
||||
staticImports.add(PsiJavaFile.StaticMember.create(JAVA_IO_IO, "*"));
|
||||
}
|
||||
}
|
||||
|
||||
// preparation of results
|
||||
if (counter < staticImports.length) {
|
||||
staticImports = Arrays.copyOf(staticImports, counter);
|
||||
}
|
||||
for (PsiImportStaticStatement statement : staticImports) {
|
||||
ImportsUtil.markAsImplicitImport(statement);
|
||||
}
|
||||
return staticImports;
|
||||
return staticImports.toArray(PsiJavaFile.StaticMember.EMPTY_ARRAY);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,16 +3,15 @@ package com.intellij.psi.impl.source;
|
||||
|
||||
import com.intellij.lang.ASTNode;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.impl.PsiImplUtil;
|
||||
import com.intellij.psi.impl.java.stubs.JavaStubElementTypes;
|
||||
import com.intellij.psi.impl.java.stubs.PsiImportListStub;
|
||||
import com.intellij.psi.impl.source.tree.ElementType;
|
||||
import com.intellij.psi.impl.source.tree.JavaElementType;
|
||||
import com.intellij.psi.tree.TokenSet;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class PsiImportListImpl extends JavaStubPsiElement<PsiImportListStub> implements PsiImportList {
|
||||
private volatile Map<String,PsiImportStatement> myClassNameToImportMap;
|
||||
@@ -54,11 +53,7 @@ public class PsiImportListImpl extends JavaStubPsiElement<PsiImportListStub> imp
|
||||
|
||||
@Override
|
||||
public PsiImportStaticStatement @NotNull [] getImportStaticStatements() {
|
||||
final PsiImportStaticStatement[] explicitStaticImports =
|
||||
getStubOrPsiChildren(IMPORT_STATIC_STATEMENT_BIT_SET, PsiImportStaticStatementImpl.ARRAY_FACTORY);
|
||||
final PsiImportStaticStatement[] implicitStaticImports =
|
||||
PsiImplUtil.getImplicitStaticImports(getContainingFile());
|
||||
return ArrayUtil.mergeArrays(explicitStaticImports, implicitStaticImports);
|
||||
return getStubOrPsiChildren(IMPORT_STATIC_STATEMENT_BIT_SET, PsiImportStaticStatementImpl.ARRAY_FACTORY);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -333,17 +333,13 @@ public abstract class PsiJavaFileBaseImpl extends PsiFileImpl implements PsiJava
|
||||
}
|
||||
}
|
||||
}
|
||||
for (PsiImportStaticStatement staticImport : getImportStaticStatements()) {
|
||||
for (PsiImportStaticStatement staticImport : ContainerUtil.append(getImplicitlyImportedStaticStatements(), getImportStaticStatements())) {
|
||||
String name = staticImport.getReferenceName();
|
||||
if (name != null) {
|
||||
staticImports.putValue(name, staticImport);
|
||||
}
|
||||
}
|
||||
|
||||
for (PsiImportStaticStatement staticImport : PsiImplUtil.getImplicitStaticImports(this)) {
|
||||
staticImports.putValue(staticImport.getReferenceName(), staticImport);
|
||||
}
|
||||
|
||||
Map<String, Iterable<ResultWithContext>> result = new LinkedHashMap<>();
|
||||
for (String name : ContainerUtil.newLinkedHashSet(
|
||||
ContainerUtil.concat(ownClasses.keySet(), typeImports.keySet(), staticImports.keySet()))) {
|
||||
@@ -429,7 +425,7 @@ public abstract class PsiJavaFileBaseImpl extends PsiFileImpl implements PsiJava
|
||||
}
|
||||
|
||||
private boolean processOnDemandStaticImports(@NotNull ResolveState state, @NotNull StaticImportFilteringProcessor processor) {
|
||||
for (PsiImportStaticStatement importStaticStatement : getImportStaticStatements()) {
|
||||
for (PsiImportStaticStatement importStaticStatement : ContainerUtil.append(getImplicitlyImportedStaticStatements(), getImportStaticStatements())) {
|
||||
if (!importStaticStatement.isOnDemand()) continue;
|
||||
PsiClass targetElement = importStaticStatement.resolveTargetClass();
|
||||
if (targetElement != null) {
|
||||
@@ -527,6 +523,11 @@ public abstract class PsiJavaFileBaseImpl extends PsiFileImpl implements PsiJava
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull StaticMember @NotNull [] getImplicitlyImportedStaticMembers() {
|
||||
return PsiImplUtil.getImplicitStaticImports(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearCaches() {
|
||||
super.clearCaches();
|
||||
@@ -539,6 +540,12 @@ public abstract class PsiJavaFileBaseImpl extends PsiFileImpl implements PsiJava
|
||||
clearCaches();
|
||||
}
|
||||
|
||||
private List<PsiImportStaticStatement> getImplicitlyImportedStaticStatements() {
|
||||
PsiElementFactory factory = PsiElementFactory.getInstance(getProject());
|
||||
return ContainerUtil.map(getImplicitlyImportedStaticMembers(),
|
||||
member -> factory.createImportStaticStatementFromText(member.getContainingClass(), member.getMemberName()));
|
||||
}
|
||||
|
||||
private static final Key<String> SHEBANG_SOURCE_LEVEL = Key.create("SHEBANG_SOURCE_LEVEL");
|
||||
|
||||
private @NotNull LanguageLevel getLanguageLevelInner() {
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
void main() {
|
||||
println("Hello!");
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
void main() {
|
||||
println("Hello!");
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
void main() {
|
||||
println("Hello!");
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
import static java.io.IO.*;
|
||||
|
||||
void main() {
|
||||
println("Hello!");
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
void main() {
|
||||
println("Hello!");
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
import static java.io.IO.println;
|
||||
|
||||
void main() {
|
||||
println("Hello!");
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
void main() {
|
||||
println("Hello!");
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
// 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.codeInsight.completion;
|
||||
|
||||
import com.intellij.codeInsight.completion.CompletionType;
|
||||
import com.intellij.pom.java.JavaFeature;
|
||||
import com.intellij.testFramework.LightProjectDescriptor;
|
||||
import com.intellij.testFramework.NeedsIndex;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class NormalImplicitClassCompletionTest extends NormalCompletionTestCase {
|
||||
@NotNull
|
||||
@Override
|
||||
protected LightProjectDescriptor getProjectDescriptor() {
|
||||
return new ProjectDescriptor(JavaFeature.IMPLICIT_IMPORT_IN_IMPLICIT_CLASSES.getMinimumLevel());
|
||||
}
|
||||
|
||||
@NeedsIndex.SmartMode(reason = "out of standard library")
|
||||
public void testImplicitIoImport() {
|
||||
myFixture.addClass("""
|
||||
package java.io;
|
||||
|
||||
public final class IO {
|
||||
public static void println(Object obj) {}
|
||||
}
|
||||
""");
|
||||
|
||||
myFixture.configureByText("a.java", """
|
||||
void main(){
|
||||
prin<caret>
|
||||
}""");
|
||||
myFixture.complete(CompletionType.BASIC, 0);
|
||||
myFixture.checkResult("""
|
||||
void main(){
|
||||
println(<caret>);
|
||||
}""");
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,11 @@
|
||||
package com.intellij.java.codeInsight.daemon
|
||||
|
||||
import com.intellij.JavaTestUtil
|
||||
import com.intellij.pom.java.JavaFeature
|
||||
import com.intellij.pom.java.LanguageLevel
|
||||
import com.intellij.psi.PsiCallExpression
|
||||
import com.intellij.psi.PsiExpressionStatement
|
||||
import com.intellij.psi.PsiJavaFile
|
||||
import com.intellij.testFramework.IdeaTestUtil
|
||||
import com.intellij.testFramework.UsefulTestCase
|
||||
import com.intellij.testFramework.fixtures.LightJavaCodeInsightFixtureTestCase
|
||||
@@ -66,6 +70,24 @@ class ImplicitClassHighlightingTest : LightJavaCodeInsightFixtureTestCase() {
|
||||
UsefulTestCase.assertNotEmpty(highlightings)
|
||||
}
|
||||
|
||||
fun testImplicitIoImport() {
|
||||
IdeaTestUtil.withLevel(module, JavaFeature.IMPLICIT_IMPORT_IN_IMPLICIT_CLASSES.minimumLevel, Runnable {
|
||||
myFixture.addClass("""
|
||||
package java.io;
|
||||
|
||||
public final class IO {
|
||||
public static void println(Object obj) {}
|
||||
}
|
||||
|
||||
""".trimIndent())
|
||||
val psiFile = myFixture.configureByFile(getTestName(false) + ".java")
|
||||
myFixture.checkHighlighting()
|
||||
val statement = (psiFile as PsiJavaFile).classes[0].methods[0].body!!.statements[0] as PsiExpressionStatement
|
||||
val resolveMethod = (statement.expression as PsiCallExpression).resolveMethod()
|
||||
assertNotNull(resolveMethod)
|
||||
})
|
||||
}
|
||||
|
||||
private fun doTest() {
|
||||
myFixture.configureByFile(getTestName(false) + ".java")
|
||||
myFixture.checkHighlighting()
|
||||
|
||||
@@ -22,6 +22,7 @@ import com.intellij.openapi.application.ReadAction;
|
||||
import com.intellij.openapi.application.ex.PathManagerEx;
|
||||
import com.intellij.openapi.command.WriteCommandAction;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.pom.java.JavaFeature;
|
||||
import com.intellij.psi.PsiDocumentManager;
|
||||
import com.intellij.psi.PsiFile;
|
||||
import com.intellij.psi.codeStyle.CodeStyleSettings;
|
||||
@@ -29,6 +30,7 @@ import com.intellij.psi.codeStyle.JavaCodeStyleSettings;
|
||||
import com.intellij.psi.codeStyle.PackageEntry;
|
||||
import com.intellij.psi.codeStyle.PackageEntryTable;
|
||||
import com.intellij.psi.codeStyle.modifier.CodeStyleSettingsModifier;
|
||||
import com.intellij.testFramework.IdeaTestUtil;
|
||||
import com.intellij.testFramework.LightProjectDescriptor;
|
||||
import com.intellij.testFramework.ServiceContainerUtil;
|
||||
import com.intellij.util.PathUtil;
|
||||
@@ -88,8 +90,43 @@ public class OptimizeImportsTest extends OptimizeImportsTestCase {
|
||||
doTest();
|
||||
}
|
||||
public void testStringTemplates() {
|
||||
doTest();
|
||||
IdeaTestUtil.withLevel(getModule(), JavaFeature.STRING_TEMPLATES.getMinimumLevel(), () -> {
|
||||
myFixture.addClass("""
|
||||
package java.lang;
|
||||
|
||||
public interface StringTemplate {
|
||||
Processor<String, RuntimeException> STR = StringTemplate::interpolate;
|
||||
}
|
||||
""");
|
||||
doTest();
|
||||
});
|
||||
}
|
||||
|
||||
public void testImplicitIoImport1() {
|
||||
implicitIoImport();
|
||||
}
|
||||
|
||||
private void implicitIoImport() {
|
||||
IdeaTestUtil.withLevel(getModule(), JavaFeature.IMPLICIT_IMPORT_IN_IMPLICIT_CLASSES.getMinimumLevel(), () -> {
|
||||
myFixture.addClass("""
|
||||
package java.io;
|
||||
|
||||
public final class IO {
|
||||
public static void println(Object obj) {}
|
||||
}
|
||||
""");
|
||||
doTest();
|
||||
});
|
||||
}
|
||||
|
||||
public void testImplicitIoImport2() {
|
||||
implicitIoImport();
|
||||
}
|
||||
|
||||
public void testImplicitIoImport3() {
|
||||
implicitIoImport();
|
||||
}
|
||||
|
||||
public void testNewImportListIsEmptyAndCommentPreserved() { doTest(); }
|
||||
public void testNewImportListIsEmptyAndJavaDocWithInvalidCodePreserved() { doTest(); }
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.psi.xml.*;
|
||||
import com.intellij.xml.XmlAttributeDescriptor;
|
||||
import com.intellij.xml.XmlElementDescriptor;
|
||||
import com.siyeh.ig.psiutils.ImportUtils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.plugins.javaFX.fxml.JavaFxFileTypeFactory;
|
||||
@@ -42,12 +43,12 @@ public final class JavaFxImportsOptimizer implements ImportOptimizer {
|
||||
if (vFile == null || !ProjectRootManager.getInstance(project).getFileIndex().isInSourceContent(vFile)) {
|
||||
return EmptyRunnable.INSTANCE;
|
||||
}
|
||||
final @NotNull List<ImportHelper.Import> names = new ArrayList<>();
|
||||
final @NotNull List<ImportUtils.Import> names = new ArrayList<>();
|
||||
final Set<String> demandedForNested = new HashSet<>();
|
||||
collectNamesToImport(names, demandedForNested, (XmlFile)file);
|
||||
names.sort((o1, o2) -> StringUtil.compare(o1.name(), o2.name(), true));
|
||||
final JavaCodeStyleSettings settings = JavaCodeStyleSettings.getInstance(file);
|
||||
final @NotNull List<ImportHelper.Import> sortedNames = ImportHelper.sortItemsAccordingToSettings(names, settings);
|
||||
final @NotNull List<ImportUtils.Import> sortedNames = ImportHelper.sortItemsAccordingToSettings(names, settings);
|
||||
final Map<String, Boolean> onDemand = new HashMap<>();
|
||||
ImportHelper.collectOnDemandImports(sortedNames, settings, onDemand);
|
||||
for (String s : demandedForNested) {
|
||||
@@ -55,7 +56,7 @@ public final class JavaFxImportsOptimizer implements ImportOptimizer {
|
||||
}
|
||||
final Set<String> imported = new HashSet<>();
|
||||
final List<String> imports = new ArrayList<>();
|
||||
for (ImportHelper.Import anImport : sortedNames) {
|
||||
for (ImportUtils.Import anImport : sortedNames) {
|
||||
final String qName = anImport.name();
|
||||
final String packageName = StringUtil.getPackageName(qName);
|
||||
if (imported.contains(packageName) || imported.contains(qName)) {
|
||||
@@ -94,13 +95,13 @@ public final class JavaFxImportsOptimizer implements ImportOptimizer {
|
||||
};
|
||||
}
|
||||
|
||||
private static void collectNamesToImport(final @NotNull List<ImportHelper.Import> names,
|
||||
private static void collectNamesToImport(final @NotNull List<ImportUtils.Import> names,
|
||||
final @NotNull Collection<String> demandedForNested,
|
||||
@NotNull XmlFile file) {
|
||||
file.accept(new JavaFxUsedClassesVisitor() {
|
||||
@Override
|
||||
protected void appendClassName(String fqn) {
|
||||
names.add(new ImportHelper.Import(fqn, false));
|
||||
names.add(new ImportUtils.Import(fqn, false));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
Reference in New Issue
Block a user