diff --git a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightUtil.java b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightUtil.java index 8c66f5218709..29c0f8f3c505 100644 --- a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightUtil.java +++ b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightUtil.java @@ -1337,12 +1337,11 @@ public class HighlightUtil { } @Nullable - public static HighlightInfo checkMemberReferencedBeforeConstructorCalled(PsiElement expression) { + public static HighlightInfo checkMemberReferencedBeforeConstructorCalled(PsiElement expression, PsiElement resolved) { PsiClass referencedClass; @NonNls String resolvedName; PsiType type; if (expression instanceof PsiJavaCodeReferenceElement) { - PsiElement resolved = ((PsiJavaCodeReferenceElement)expression).advancedResolve(true).getElement(); // redirected ctr if (PsiKeyword.THIS.equals(((PsiJavaCodeReferenceElement)expression).getReferenceName()) && resolved instanceof PsiMethod @@ -1838,7 +1837,7 @@ public class HighlightUtil { PsiElement refName = ref.getReferenceNameElement(); if (!(refName instanceof PsiIdentifier) && !(refName instanceof PsiKeyword)) return null; - HighlightInfo highlightInfo = checkMemberReferencedBeforeConstructorCalled(ref); + HighlightInfo highlightInfo = checkMemberReferencedBeforeConstructorCalled(ref, resolved); if (highlightInfo != null) return highlightInfo; PsiElement refParent = ref.getParent(); diff --git a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightVisitorImpl.java b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightVisitorImpl.java index b6c8e95ee32c..21101a4a3474 100644 --- a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightVisitorImpl.java +++ b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightVisitorImpl.java @@ -418,9 +418,31 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh } myHolder.add(HighlightNamesUtil.highlightMethodName(method, identifier, true, colorsScheme)); } + else { + visitParentReference(parent); + } super.visitIdentifier(identifier); } + private void visitParentReference(PsiElement parent) { + if (parent instanceof PsiJavaCodeReferenceElement && !(parent.getParent() instanceof PsiJavaCodeReferenceElement) && + !((PsiJavaCodeReferenceElement)parent).isQualified()) { + PsiJavaCodeReferenceElement ref = (PsiJavaCodeReferenceElement)parent; + JavaResolveResult result; + try { + result = ref.advancedResolve(true); + } + catch (IndexNotReadyException e) { + return; + } + PsiElement resolved = result.getElement(); + myHolder.add(HighlightUtil.checkReference(ref, result, resolved)); + if (myRefCountHolder != null) { + myRefCountHolder.registerReference(ref, result); + } + } + } + @Override public void visitImportStatement(PsiImportStatement statement) { if (!myHolder.hasErrorResults()) myHolder.add(HighlightUtil.checkSingleImportClassConflict(statement, mySingleImportedClasses)); } @@ -480,28 +502,32 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh @Override public void visitKeyword(PsiKeyword keyword) { super.visitKeyword(keyword); PsiElement parent = keyword.getParent(); + String text = keyword.getText(); if (parent instanceof PsiModifierList) { PsiModifierList psiModifierList = (PsiModifierList)parent; if (!myHolder.hasErrorResults()) myHolder.add(HighlightUtil.checkNotAllowedModifier(keyword, psiModifierList)); if (!myHolder.hasErrorResults()) myHolder.add(HighlightUtil.checkIllegalModifierCombination(keyword, psiModifierList)); if (!myHolder.hasErrorResults()) myHolder.add(HighlightClassUtil.checkPublicClassInRightFile(keyword, psiModifierList)); - if (PsiModifier.ABSTRACT.equals(keyword.getText()) && psiModifierList.getParent() instanceof PsiMethod) { + if (PsiModifier.ABSTRACT.equals(text) && psiModifierList.getParent() instanceof PsiMethod) { if (!myHolder.hasErrorResults()) { myHolder.add(HighlightMethodUtil.checkAbstractMethodInConcreteClass((PsiMethod)psiModifierList.getParent(), keyword)); } } } - else if (keyword.getText().equals(PsiKeyword.CONTINUE) && parent instanceof PsiContinueStatement) { + else if (PsiKeyword.CONTINUE.equals(text) && parent instanceof PsiContinueStatement) { PsiContinueStatement statement = (PsiContinueStatement)parent; if (!myHolder.hasErrorResults()) myHolder.add(HighlightUtil.checkContinueOutsideLoop(statement)); } - else if (keyword.getText().equals(PsiKeyword.BREAK) && parent instanceof PsiBreakStatement) { + else if (PsiKeyword.BREAK.equals(text) && parent instanceof PsiBreakStatement) { PsiBreakStatement statement = (PsiBreakStatement)parent; if (!myHolder.hasErrorResults()) myHolder.add(HighlightUtil.checkBreakOutsideLoop(statement)); } - else if (PsiKeyword.INTERFACE.equals(keyword.getText()) && parent instanceof PsiClass) { + else if (PsiKeyword.INTERFACE.equals(text) && parent instanceof PsiClass) { if (!myHolder.hasErrorResults()) myHolder.add(HighlightClassUtil.checkInterfaceCannotBeLocal((PsiClass)parent)); } + else { + visitParentReference(parent); + } if (!myHolder.hasErrorResults()) myHolder.add(HighlightClassUtil.checkStaticDeclarationInInnerClass(keyword)); if (!myHolder.hasErrorResults()) myHolder.add(HighlightUtil.checkIllegalVoidType(keyword)); @@ -719,11 +745,13 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh } PsiElement resolved = result.getElement(); PsiElement parent = ref.getParent(); - if (myRefCountHolder != null) { - myRefCountHolder.registerReference(ref, result); - } - myHolder.add(HighlightUtil.checkReference(ref, result, resolved)); + if (parent instanceof PsiJavaCodeReferenceElement || ref.isQualified()) { + if (myRefCountHolder != null) { + myRefCountHolder.registerReference(ref, result); + } + myHolder.add(HighlightUtil.checkReference(ref, result, resolved)); + } if (!myHolder.hasErrorResults()) myHolder.add(HighlightClassUtil.checkAbstractInstantiation(ref)); if (!myHolder.hasErrorResults()) myHolder.add(HighlightClassUtil.checkExtendsDuplicate(ref, resolved)); if (!myHolder.hasErrorResults()) myHolder.add(HighlightUtil.checkExceptionAlreadyCaught(ref, resolved)); @@ -852,7 +880,9 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh @Override public void visitThisExpression(PsiThisExpression expr) { myHolder.add(HighlightUtil.checkThisOrSuperExpressionInIllegalContext(expr, expr.getQualifier())); - if (!myHolder.hasErrorResults()) myHolder.add(HighlightUtil.checkMemberReferencedBeforeConstructorCalled(expr)); + if (!myHolder.hasErrorResults()) { + myHolder.add(HighlightUtil.checkMemberReferencedBeforeConstructorCalled(expr, null)); + } if (!myHolder.hasErrorResults()) { visitExpression(expr); } diff --git a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/ImportClassFix.java b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/ImportClassFix.java index 8d2efb1cc111..a922a6544d67 100644 --- a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/ImportClassFix.java +++ b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/ImportClassFix.java @@ -31,7 +31,6 @@ import com.intellij.psi.util.ClassUtil; import org.jetbrains.annotations.NotNull; public class ImportClassFix extends ImportClassFixBase { - public ImportClassFix(@NotNull PsiJavaCodeReferenceElement element) { super(element); } @@ -42,7 +41,12 @@ public class ImportClassFix extends ImportClassFixBase im @Nullable protected abstract String getReferenceName(T reference); + protected abstract PsiElement getReferenceNameElement(T reference); protected abstract boolean hasTypeParameters(T reference); @@ -208,8 +209,10 @@ public abstract class ImportClassFixBase im } } - private static boolean isCaretNearRef(Editor editor, PsiElement ref) { - TextRange range = ref.getTextRange(); + private boolean isCaretNearRef(Editor editor, T ref) { + PsiElement nameElement = getReferenceNameElement(ref); + if (nameElement == null) return false; + TextRange range = nameElement.getTextRange(); int offset = editor.getCaretModel().getOffset(); return offset == range.getEndOffset(); @@ -230,7 +233,7 @@ public abstract class ImportClassFixBase im }); } - protected void bindReference(T reference, PsiClass targetClass) { + protected void bindReference(PsiReference reference, PsiClass targetClass) { reference.bindToElement(targetClass); } @@ -238,7 +241,7 @@ public abstract class ImportClassFixBase im return new AddImportAction(project, myRef, editor, classes) { @Override protected void bindReference(PsiReference ref, PsiClass targetClass) { - ImportClassFixBase.this.bindReference((T)ref, targetClass); + ImportClassFixBase.this.bindReference(ref, targetClass); } }; } diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting/ReferenceMemberBeforeCtrCalled.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting/ReferenceMemberBeforeCtrCalled.java index 149f62c06438..ebe7991169a4 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting/ReferenceMemberBeforeCtrCalled.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting/ReferenceMemberBeforeCtrCalled.java @@ -2,45 +2,49 @@ import java.io.*; import java.net.*; -class a { - a(int i) {} - a(a a) {} +class A { + A(int i) {} + A(A a) {} int f() { return 0; } int fi; } -class b extends a { +class B extends A { int bi; - b(int h) { - super(bi); + B(int h) { + super(bi); } - b() { - this(bi); + B() { + this(bi); } - b(String s) { - super(db(1) ); + B(String s) { + super(db(1) ); } - b(int i, int j) { - super(f()); + B(int i, int j) { + super(f()); } - b(int i, int j, int k) { - super(super.f()); + B(int i, int j, int k) { + super(super.f()); } - b(String s, int i) { + B(String s, int i) { super(s.length()); } - b(int s, int i, char j) { - super(super.fi ); + B(int s, int i, char j) { + super(super.fi ); } - b(double d) { - super(new inner() ); + B(double d) { + super(new Inner() ); + } + class Inner extends A { + Inner(){ + super(1); + } } - class inner extends a {inner(){super(1);}} int db(int j) { return 0; @@ -48,54 +52,51 @@ class b extends a { } -class enc { +class Enc { int ienc; - class bb extends a { + class Bb extends A { int ibb; - bb() { super(ienc); } - bb(int i) { + Bb() { super(ienc); } + Bb(int i) { super(i); } - bb(int i, int j) { - super(enc.bb.this.ibb ); + Bb(int i, int j) { + super(Enc.Bb.this.ibb ); } - bb(int i, String s) { - super(enc.this.ienc); + Bb(int i, String s) { + super(Enc.this.ienc); } - bb(int i, char j) { + Bb(int i, char j) { super(this ); } - - } - enc() { - this(new bb()); + Enc() { + this(new Bb()); } - enc(bb b) {} + Enc(Bb b) {} } // static are OK -class c2 extends a { +class C2 extends A { static final int fi = 4; - c2() { + C2() { super(fi); } - c2(int i) { + C2(int i) { super(sf()); } static int sf() { return 0; } - c2(int i, int j) { - super(new sc().i); + C2(int i, int j) { + super(new Sc().i); } - static class sc { + static class Sc { int i; } - } interface Callback { @@ -111,7 +112,6 @@ class Base { } class YellinBug extends Base { - public YellinBug() { super(new Callback() { diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/ImportHelperTest.java b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/ImportHelperTest.java index b9bc2d13da3c..9c16d28ee6d1 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/ImportHelperTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/ImportHelperTest.java @@ -12,6 +12,7 @@ import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.command.CommandProcessor; import com.intellij.openapi.command.WriteCommandAction; import com.intellij.openapi.command.impl.UndoManagerImpl; +import com.intellij.openapi.command.undo.UndoManager; import com.intellij.openapi.fileEditor.impl.LoadTextUtil; import com.intellij.openapi.fileTypes.StdFileTypes; import com.intellij.openapi.projectRoots.Sdk; @@ -99,44 +100,44 @@ public class ImportHelperTest extends DaemonAnalyzerTestCase { assertEmpty(filter(doHighlighting(), HighlightSeverity.ERROR)); CommandProcessor.getInstance().executeCommand( getProject(), new Runnable() { - @Override - public void run() { - ApplicationManager.getApplication().runWriteAction(new Runnable() { - @Override - public void run() { - try { - - CodeStyleSettings settings = CodeStyleSettingsManager.getSettings(getProject()).clone(); - settings.LAYOUT_STATIC_IMPORTS_SEPARATELY = true; - PackageEntryTable table = new PackageEntryTable(); - table.addEntry(PackageEntry.ALL_OTHER_IMPORTS_ENTRY); - table.addEntry(PackageEntry.BLANK_LINE_ENTRY); - table.addEntry(new PackageEntry(false, "javax", true)); - table.addEntry(new PackageEntry(false, "java", true)); - table.addEntry(PackageEntry.BLANK_LINE_ENTRY); - table.addEntry(new PackageEntry(true, "java", true)); - table.addEntry(PackageEntry.BLANK_LINE_ENTRY); - table.addEntry(PackageEntry.ALL_OTHER_STATIC_IMPORTS_ENTRY); - - settings.IMPORT_LAYOUT_TABLE.copyFrom(table); - CodeStyleSettingsManager.getInstance(getProject()).setTemporarySettings(settings); + @Override + public void run() { + ApplicationManager.getApplication().runWriteAction(new Runnable() { + @Override + public void run() { try { - JavaCodeStyleManager.getInstance(getProject()).optimizeImports(file); - } - finally { - CodeStyleSettingsManager.getInstance(getProject()).dropTemporarySettings(); - } - assertOrder(file, "java.awt.*", "java.util.Map", "static java.lang.Math.max", "static java.lang.Math.min", "static javax.swing.SwingConstants.CENTER"); + CodeStyleSettings settings = CodeStyleSettingsManager.getSettings(getProject()).clone(); + settings.LAYOUT_STATIC_IMPORTS_SEPARATELY = true; + PackageEntryTable table = new PackageEntryTable(); + table.addEntry(PackageEntry.ALL_OTHER_IMPORTS_ENTRY); + table.addEntry(PackageEntry.BLANK_LINE_ENTRY); + table.addEntry(new PackageEntry(false, "javax", true)); + table.addEntry(new PackageEntry(false, "java", true)); + table.addEntry(PackageEntry.BLANK_LINE_ENTRY); + table.addEntry(new PackageEntry(true, "java", true)); + table.addEntry(PackageEntry.BLANK_LINE_ENTRY); + table.addEntry(PackageEntry.ALL_OTHER_STATIC_IMPORTS_ENTRY); + settings.IMPORT_LAYOUT_TABLE.copyFrom(table); + CodeStyleSettingsManager.getInstance(getProject()).setTemporarySettings(settings); + try { + JavaCodeStyleManager.getInstance(getProject()).optimizeImports(file); + } + finally { + CodeStyleSettingsManager.getInstance(getProject()).dropTemporarySettings(); + } + + assertOrder(file, "java.awt.*", "java.util.Map", "static java.lang.Math.max", "static java.lang.Math.min", "static javax.swing.SwingConstants.CENTER"); + + } + catch (Throwable e) { + LOG.error(e); + } } - catch (Throwable e) { - LOG.error(e); - } - } - }); - } - }, "", ""); + }); + } + }, "", ""); } @Override @@ -149,8 +150,8 @@ public class ImportHelperTest extends DaemonAnalyzerTestCase { ImportHelper importHelper = new ImportHelper(settings); PsiClass psiClass = JavaPsiFacade.getInstance(getProject()).findClass(fqn, GlobalSearchScope.allScope(getProject())); - boolean b = importHelper.addImport(file, psiClass); - assertTrue(b); + boolean b = importHelper.addImport(file, psiClass); + assertTrue(b); assertOrder(file, expectedOrder); } @@ -199,8 +200,8 @@ public class ImportHelperTest extends DaemonAnalyzerTestCase { try { CodeInsightSettings.getInstance().ADD_UNAMBIGIOUS_IMPORTS_ON_THE_FLY = true; configureByText(StdFileTypes.JAVA, "class X { ArrayList c; }"); - ((UndoManagerImpl)UndoManagerImpl.getInstance(getProject())).flushCurrentCommandMerger(); - ((UndoManagerImpl)UndoManagerImpl.getInstance(getProject())).clearUndoRedoQueueInTests(getFile().getVirtualFile()); + ((UndoManagerImpl)UndoManager.getInstance(getProject())).flushCurrentCommandMerger(); + ((UndoManagerImpl)UndoManager.getInstance(getProject())).clearUndoRedoQueueInTests(getFile().getVirtualFile()); type(" "); backspace(); @@ -232,8 +233,8 @@ public class ImportHelperTest extends DaemonAnalyzerTestCase { try { CodeInsightSettings.getInstance().ADD_UNAMBIGIOUS_IMPORTS_ON_THE_FLY = true; configureByText(StdFileTypes.JAVA, "class X { ArrayList c = new ArrayList(); }"); - ((UndoManagerImpl)UndoManagerImpl.getInstance(getProject())).flushCurrentCommandMerger(); - ((UndoManagerImpl)UndoManagerImpl.getInstance(getProject())).clearUndoRedoQueueInTests(getFile().getVirtualFile()); + ((UndoManagerImpl)UndoManager.getInstance(getProject())).flushCurrentCommandMerger(); + ((UndoManagerImpl)UndoManager.getInstance(getProject())).clearUndoRedoQueueInTests(getFile().getVirtualFile()); type(" "); backspace(); @@ -290,8 +291,8 @@ public class ImportHelperTest extends DaemonAnalyzerTestCase { public void testAutoImportWorks() throws Throwable { @NonNls final String text = "class S { JFrame x; }"; configureByText(StdFileTypes.JAVA, text); - ((UndoManagerImpl)UndoManagerImpl.getInstance(getProject())).flushCurrentCommandMerger(); - ((UndoManagerImpl)UndoManagerImpl.getInstance(getProject())).clearUndoRedoQueueInTests(getFile().getVirtualFile()); + ((UndoManagerImpl)UndoManager.getInstance(getProject())).flushCurrentCommandMerger(); + ((UndoManagerImpl)UndoManager.getInstance(getProject())).clearUndoRedoQueueInTests(getFile().getVirtualFile()); assertFalse(((DaemonCodeAnalyzerImpl)DaemonCodeAnalyzer.getInstance(getProject())).canChangeFileSilently(getFile())); @@ -306,6 +307,36 @@ public class ImportHelperTest extends DaemonAnalyzerTestCase { assertFalse(((DaemonCodeAnalyzerImpl)DaemonCodeAnalyzer.getInstance(getProject())).canChangeFileSilently(getFile()));//CodeInsightSettings.getInstance().ADD_UNAMBIGIOUS_IMPORTS_ON_THE_FLY = old; } + @CanChangeDocumentDuringHighlighting + @DoNotWrapInCommand + public void testAutoImportOfGenericReference() throws Throwable { + @NonNls final String text = "class S {{ new ArrayList<> }}"; + configureByText(StdFileTypes.JAVA, text); + boolean old = CodeInsightSettings.getInstance().ADD_UNAMBIGIOUS_IMPORTS_ON_THE_FLY; + CodeInsightSettings.getInstance().ADD_UNAMBIGIOUS_IMPORTS_ON_THE_FLY = true; + DaemonCodeAnalyzerSettings.getInstance().setImportHintEnabled(true); + + ((UndoManagerImpl)UndoManager.getInstance(getProject())).flushCurrentCommandMerger(); + ((UndoManagerImpl)UndoManager.getInstance(getProject())).clearUndoRedoQueueInTests(getFile().getVirtualFile()); + type(" "); + backspace(); + + try { + doHighlighting(); + //caret is too close + assertEmpty(((PsiJavaFile)getFile()).getImportList().getAllImportStatements()); + + caretRight(); + + doHighlighting(); + + assertNotSame(0, ((PsiJavaFile)getFile()).getImportList().getAllImportStatements().length); + } + finally { + CodeInsightSettings.getInstance().ADD_UNAMBIGIOUS_IMPORTS_ON_THE_FLY = old; + } + } + @CanChangeDocumentDuringHighlighting @DoNotWrapInCommand public void testAutoOptimizeUnresolvedImports() throws Throwable { diff --git a/java/testFramework/src/com/intellij/codeInsight/CodeInsightTestCase.java b/java/testFramework/src/com/intellij/codeInsight/CodeInsightTestCase.java index e0ed7d687b54..e69bad860077 100644 --- a/java/testFramework/src/com/intellij/codeInsight/CodeInsightTestCase.java +++ b/java/testFramework/src/com/intellij/codeInsight/CodeInsightTestCase.java @@ -628,6 +628,12 @@ public abstract class CodeInsightTestCase extends PsiTestCase { action.actionPerformed(getEditor(), c, DataManager.getInstance().getDataContext()); } + protected void caretRight() { + EditorActionManager actionManager = EditorActionManager.getInstance(); + EditorActionHandler action = actionManager.getActionHandler(IdeActions.ACTION_EDITOR_MOVE_CARET_RIGHT); + action.execute(getEditor(), DataManager.getInstance().getDataContext()); + } + protected void type(@NonNls String s) { for (char c : s.toCharArray()) { type(c); diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/annotator/intentions/GroovyAddImportAction.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/annotator/intentions/GroovyAddImportAction.java index 051c29683cbe..30a4beeb17a2 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/annotator/intentions/GroovyAddImportAction.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/annotator/intentions/GroovyAddImportAction.java @@ -19,6 +19,7 @@ package org.jetbrains.plugins.groovy.annotator.intentions; import com.intellij.codeInsight.daemon.impl.quickfix.ImportClassFixBase; import com.intellij.openapi.util.Comparing; import com.intellij.psi.PsiClass; +import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; import org.jetbrains.plugins.groovy.lang.psi.GrReferenceElement; import org.jetbrains.plugins.groovy.lang.psi.GroovyFile; @@ -34,13 +35,13 @@ public class GroovyAddImportAction extends ImportClassFixBase