[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:
Mikhail Pyltsin
2024-07-03 17:59:10 +02:00
committed by intellij-monorepo-bot
parent 6827bb2045
commit 07178b990f
23 changed files with 303 additions and 117 deletions

View File

@@ -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);

View File

@@ -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

View File

@@ -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;

View File

@@ -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);
}
}
}

View File

@@ -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) {}
}

View File

@@ -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.
*

View File

@@ -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);
}
}
}

View File

@@ -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;
}
}

View File

@@ -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();

View File

@@ -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);
}
}

View File

@@ -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

View File

@@ -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() {

View File

@@ -0,0 +1,3 @@
void main() {
println("Hello!");
}

View File

@@ -0,0 +1,3 @@
void main() {
println("Hello!");
}

View File

@@ -0,0 +1,3 @@
void main() {
println("Hello!");
}

View File

@@ -0,0 +1,5 @@
import static java.io.IO.*;
void main() {
println("Hello!");
}

View File

@@ -0,0 +1,3 @@
void main() {
println("Hello!");
}

View File

@@ -0,0 +1,5 @@
import static java.io.IO.println;
void main() {
println("Hello!");
}

View File

@@ -0,0 +1,3 @@
void main() {
println("Hello!");
}

View File

@@ -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>);
}""");
}
}

View File

@@ -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()

View File

@@ -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(); }

View File

@@ -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