ensure inserted single-static-import when conflicting on-demand imports are presented (IDEA-155031)

This commit is contained in:
Anna.Kozlova
2016-11-24 19:10:52 +01:00
parent 3e13378341
commit 83bb640734
11 changed files with 147 additions and 19 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2000-2014 JetBrains s.r.o.
* 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.
@@ -25,21 +25,18 @@ import com.intellij.codeInsight.intention.BaseElementAtCaretIntentionAction;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.Key;
import com.intellij.psi.*;
import com.intellij.psi.impl.source.codeStyle.ImportHelper;
import com.intellij.psi.impl.source.tree.java.PsiReferenceExpressionImpl;
import com.intellij.psi.util.InheritanceUtil;
import com.intellij.psi.util.MethodSignatureUtil;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.refactoring.util.RefactoringUtil;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Set;
public class AddSingleMemberStaticImportAction extends BaseElementAtCaretIntentionAction {
private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.intention.impl.AddSingleMemberStaticImportAction");
private static final Key<PsiElement> TEMP_REFERENT_USER_DATA = new Key<>("TEMP_REFERENT_USER_DATA");
@@ -100,8 +97,7 @@ public class AddSingleMemberStaticImportAction extends BaseElementAtCaretIntenti
final PsiElement currentFileResolveScope = resolveResult.getCurrentFileResolveScope();
if (currentFileResolveScope instanceof PsiImportStaticStatement) {
//don't hide another on-demand import and don't create ambiguity
if (((PsiImportStaticStatement)currentFileResolveScope).isOnDemand() ||
MethodSignatureUtil.areSignaturesEqual((PsiMethod)method, (PsiMethod)resolved)) {
if (MethodSignatureUtil.areSignaturesEqual((PsiMethod)method, (PsiMethod)resolved)) {
return null;
}
}
@@ -216,12 +212,12 @@ public class AddSingleMemberStaticImportAction extends BaseElementAtCaretIntenti
}
});
if (findExistingImport(file, resolvedClass, referenceName) == null) {
if (resolved instanceof PsiClass) {
((PsiImportHolder) file).importClass((PsiClass) resolved);
} else {
PsiReferenceExpressionImpl.bindToElementViaStaticImport(resolvedClass, referenceName, ((PsiJavaFile)file).getImportList());
}
PsiImportStatementBase existingImport = findExistingImport(file, resolvedClass, referenceName);
if (existingImport == null && resolved instanceof PsiClass) {
((PsiImportHolder) file).importClass((PsiClass) resolved);
}
else if (existingImport == null || existingImport.isOnDemand() && resolvedClass != null && ImportHelper.hasConflictingOnDemandImport((PsiJavaFile)file, resolvedClass, referenceName)) {
PsiReferenceExpressionImpl.bindToElementViaStaticImport(resolvedClass, referenceName, ((PsiJavaFile)file).getImportList());
}
file.accept(new JavaRecursiveElementVisitor() {

View File

@@ -759,6 +759,19 @@ public class ImportHelper{
return findEntryIndex(packageName, statement instanceof PsiImportStaticStatement, mySettings.IMPORT_LAYOUT_TABLE.getEntries());
}
public static boolean hasConflictingOnDemandImport(@NotNull PsiJavaFile file, @NotNull PsiClass psiClass, @NotNull String referenceName) {
Collection<Pair<String, Boolean>> resultList = collectNamesToImport(file, new ArrayList<>());
for (Pair<String, Boolean> pair : resultList) {
if (pair.second &&
referenceName.equals(StringUtil.getShortName(pair.first)) &&
!StringUtil.getPackageName(pair.first).equals(psiClass.getQualifiedName())) {
return true;
}
}
return false;
}
@NotNull
// returns list of (name, isImportStatic) pairs
private static Collection<Pair<String,Boolean>> collectNamesToImport(@NotNull PsiJavaFile file, List<PsiElement> comments){

View File

@@ -32,7 +32,6 @@ import com.intellij.psi.util.*;
import com.intellij.util.ArrayUtil;
import com.intellij.util.BitUtil;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.Processor;
import com.intellij.util.containers.ContainerUtil;
import gnu.trove.THashSet;
import org.jetbrains.annotations.NonNls;
@@ -127,6 +126,11 @@ public class JavaCodeStyleManagerImpl extends JavaCodeStyleManager {
return new ImportHelper(getSettings()).prepareOptimizeImportsResult(file);
}
@Override
public boolean hasConflictingOnDemandImport(@NotNull PsiJavaFile file, @NotNull PsiClass psiClass, @NotNull String referenceName) {
return ImportHelper.hasConflictingOnDemandImport(file, psiClass, referenceName);
}
@Override
public boolean addImport(@NotNull PsiJavaFile file, @NotNull PsiClass refClass) {
return new ImportHelper(getSettings()).addImport(file, refClass);

View File

@@ -91,6 +91,19 @@ public abstract class JavaCodeStyleManager {
*/
public abstract PsiImportList prepareOptimizeImportsResult(@NotNull PsiJavaFile file);
/**
* Single-static-import <code>import static classFQN.referenceName;</code> shadows on-demand static imports, like described
* JLS 6.4.1
* A single-static-import declaration d in a compilation unit c of package p that imports a {member} named n
* shadows the declaration of any static {member} named n imported by a static-import-on-demand declaration in c, throughout c.
*
* @return true if file contains import which would be shadowed
* false otherwise
*/
public boolean hasConflictingOnDemandImport(@NotNull PsiJavaFile file, @NotNull PsiClass psiClass, @NotNull String referenceName) {
return false;
}
/**
* Returns the kind of the specified variable (local, parameter, field, static field or static final field).
*

View File

@@ -121,9 +121,9 @@ public class PsiReferenceExpressionImpl extends PsiReferenceExpressionBase imple
assert importList != null;
final String qualifiedName = qualifierClass.getQualifiedName();
final List<PsiJavaCodeReferenceElement> refs = getImportsFromClass(importList, qualifiedName);
if (refs.size() < JavaCodeStyleSettingsFacade.getInstance(qualifierClass.getProject()).getNamesCountToUseImportOnDemand()) {
importList.add(JavaPsiFacade.getInstance(qualifierClass.getProject()).getElementFactory().createImportStaticStatement(qualifierClass,
staticName));
if (refs.size() < JavaCodeStyleSettingsFacade.getInstance(qualifierClass.getProject()).getNamesCountToUseImportOnDemand() ||
JavaCodeStyleManager.getInstance(qualifierClass.getProject()).hasConflictingOnDemandImport((PsiJavaFile)importList.getContainingFile(), qualifierClass, staticName)) {
importList.add(JavaPsiFacade.getInstance(qualifierClass.getProject()).getElementFactory().createImportStaticStatement(qualifierClass, staticName));
} else {
for (PsiJavaCodeReferenceElement ref : refs) {
final PsiImportStaticStatement importStatement = PsiTreeUtil.getParentOfType(ref, PsiImportStaticStatement.class);

View File

@@ -0,0 +1,12 @@
package use;
import foo.Bar;
import static foo.Foo.*;
public class Test {
void test() {
foo(1);
Bar.<caret>foo("1");
}
}

View File

@@ -0,0 +1,14 @@
package use;
import foo.Bar;
import foo.Foo;
import static foo.Bar.foo;
import static foo.Foo.*;
public class Test {
void test() {
Foo.foo(1);
foo("1");
}
}

View File

@@ -0,0 +1,12 @@
package use;
import foo.Bar;
import static foo.Foo.foo;
public class Test {
void test() {
foo(1);
Bar.<caret>foo("1");
}
}

View File

@@ -0,0 +1,11 @@
import foo.Foo;
import static foo.Bar.foo;
import static foo.Foo.*;
class Test {
void test() {
Foo.f<caret>oo(1);
foo("1");
}
}

View File

@@ -0,0 +1,13 @@
import foo.Foo;
import static foo.Bar.foo;
import static foo.Foo.*;
import static foo.Foo.foo;
class Test {
void test() {
foo(1);
foo("1");
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2000-2011 JetBrains s.r.o.
* 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.
@@ -16,6 +16,8 @@
package com.intellij.codeInsight.intention;
import com.intellij.JavaTestUtil;
import com.intellij.psi.codeStyle.CodeStyleSettings;
import com.intellij.psi.codeStyle.CodeStyleSettingsManager;
import com.intellij.testFramework.fixtures.JavaCodeInsightFixtureTestCase;
public class AddSingleStaticImportActionTest extends JavaCodeInsightFixtureTestCase {
@@ -85,7 +87,6 @@ public class AddSingleStaticImportActionTest extends JavaCodeInsightFixtureTestC
}
public void testSkipSameNamedNonStaticReferences() throws Exception {
myFixture.addClass("package foo;" +
"public class Clazz {" +
" public void print(String s) {}" +
@@ -99,6 +100,45 @@ public class AddSingleStaticImportActionTest extends JavaCodeInsightFixtureTestC
myFixture.checkResultByFile(getTestName(false) + "_after.java");
}
public void testAllowSingleStaticImportWhenOnDemandImportOverloadedMethod() throws Exception {
myFixture.addClass("package foo; class Foo {public static void foo(int i){}}");
myFixture.addClass("package foo; class Bar {public static void foo(String s){}}");
myFixture.configureByFile(getTestName(false) + ".java");
IntentionAction intention = myFixture.findSingleIntention("Add static import for 'foo.Bar.foo'");
assertNotNull(intention);
myFixture.launchAction(intention);
myFixture.checkResultByFile(getTestName(false) + "_after.java");
}
public void testSingleImportWhenConflictingWithOnDemand() throws Exception {
myFixture.addClass("package foo; class Foo {public static void foo(int i){}}");
myFixture.addClass("package foo; class Bar {public static void foo(String s){}}");
myFixture.configureByFile(getTestName(false) + ".java");
CodeStyleSettings settings = CodeStyleSettingsManager.getInstance(getProject()).getCurrentSettings();
int old = settings.NAMES_COUNT_TO_USE_IMPORT_ON_DEMAND;
settings.NAMES_COUNT_TO_USE_IMPORT_ON_DEMAND = 1;
try {
IntentionAction intention = myFixture.findSingleIntention("Add static import for 'foo.Foo.foo'");
assertNotNull(intention);
myFixture.launchAction(intention);
myFixture.checkResultByFile(getTestName(false) + "_after.java");
}
finally {
settings.NAMES_COUNT_TO_USE_IMPORT_ON_DEMAND = old;
}
}
public void testProhibitWhenMethodWithIdenticalSignatureAlreadyImportedFromAnotherClass() throws Exception {
myFixture.addClass("package foo; class Foo {public static void foo(int i){}}");
myFixture.addClass("package foo; class Bar {public static void foo(int i){}}");
myFixture.configureByFile(getTestName(false) + ".java");
IntentionAction intention = myFixture.getAvailableIntention("Add static import for 'foo.Bar.foo'");
assertNull(intention);
}
@Override
protected String getTestDataPath() {
return JavaTestUtil.getJavaTestDataPath() + "/codeInsight/daemonCodeAnalyzer/quickFix/addSingleStaticImport";