From 45a85a81173630d3f1bb6d50d298f630617e6295 Mon Sep 17 00:00:00 2001 From: "Egor.Ushakov" Date: Fri, 28 Oct 2016 11:36:05 +0300 Subject: [PATCH 01/36] do not create requests for already deleted breakpoints --- .../intellij/xdebugger/impl/XDebugSessionImpl.java | 4 ++-- .../xdebugger/impl/breakpoints/XBreakpointBase.java | 12 +++++++++++- .../impl/breakpoints/XLineBreakpointImpl.java | 6 ++---- 3 files changed, 15 insertions(+), 7 deletions(-) diff --git a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/XDebugSessionImpl.java b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/XDebugSessionImpl.java index 34b1ef3d9ae2..ef7555abc4ba 100644 --- a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/XDebugSessionImpl.java +++ b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/XDebugSessionImpl.java @@ -444,9 +444,9 @@ public class XDebugSessionImpl implements XDebugSession { } } - public boolean isBreakpointActive(final XBreakpoint b) { + public boolean isBreakpointActive(@NotNull XBreakpoint b) { ApplicationManager.getApplication().assertReadAccessAllowed(); - return !areBreakpointsMuted() && b.isEnabled() && !isInactiveSlaveBreakpoint(b); + return !areBreakpointsMuted() && b.isEnabled() && !isInactiveSlaveBreakpoint(b) && !((XBreakpointBase)b).isDisposed(); } @Override diff --git a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/breakpoints/XBreakpointBase.java b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/breakpoints/XBreakpointBase.java index e16b3528cdcd..33c20ffa3642 100644 --- a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/breakpoints/XBreakpointBase.java +++ b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/breakpoints/XBreakpointBase.java @@ -75,6 +75,7 @@ public class XBreakpointBase, P extends XBreakpointP private XExpression myCondition; private boolean myLogExpressionEnabled = true; private XExpression myLogExpression; + private volatile boolean myDisposed; public XBreakpointBase(final XBreakpointType type, XBreakpointManagerImpl breakpointManager, final @Nullable P properties, final S state) { myState = state; @@ -311,7 +312,16 @@ public class XBreakpointBase, P extends XBreakpointP myState.setDescription(StringUtil.nullize(description)); } - public void dispose() { + public final void dispose() { + myDisposed = true; + doDispose(); + } + + protected void doDispose() { + } + + public boolean isDisposed() { + return myDisposed; } @Override diff --git a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/breakpoints/XLineBreakpointImpl.java b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/breakpoints/XLineBreakpointImpl.java index cd15f4d601d6..47d67064a56b 100644 --- a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/breakpoints/XLineBreakpointImpl.java +++ b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/breakpoints/XLineBreakpointImpl.java @@ -61,7 +61,6 @@ public class XLineBreakpointImpl

extends XBreak @Nullable private RangeHighlighter myHighlighter; private final XLineBreakpointType

myType; private XSourcePosition mySourcePosition; - private boolean myDisposed; public XLineBreakpointImpl(final XLineBreakpointType

type, XBreakpointManagerImpl breakpointManager, @@ -78,7 +77,7 @@ public class XLineBreakpointImpl

extends XBreak } public void updateUI() { - if (myDisposed || ApplicationManager.getApplication().isUnitTestMode()) { + if (isDisposed() || ApplicationManager.getApplication().isUnitTestMode()) { return; } @@ -209,9 +208,8 @@ public class XLineBreakpointImpl

extends XBreak } @Override - public void dispose() { + protected void doDispose() { removeHighlighter(); - myDisposed = true; } private void removeHighlighter() { From 252be38f2d0c15b0cf122a7ea5de4679e451340b Mon Sep 17 00:00:00 2001 From: Dmitry Batkovich Date: Fri, 28 Oct 2016 12:42:54 +0300 Subject: [PATCH 02/36] javac ast indices: fix find usages of class members: can't find usages in getter's method call --- .../compiler/backwardRefs/CompilerReferenceReader.java | 2 +- .../testMethodUsageOnGetter/Bar.java | 7 +++++++ .../testMethodUsageOnGetter/Foo.java | 5 +++++ .../testMethodUsageOnGetter/FooFactory.java | 5 +++++ .../compiler/CompilerReferencesFindUsagesTest.java | 8 ++++++++ 5 files changed, 26 insertions(+), 1 deletion(-) create mode 100644 java/java-tests/testData/compiler/compilerReferenceFindUsages/testMethodUsageOnGetter/Bar.java create mode 100644 java/java-tests/testData/compiler/compilerReferenceFindUsages/testMethodUsageOnGetter/Foo.java create mode 100644 java/java-tests/testData/compiler/compilerReferenceFindUsages/testMethodUsageOnGetter/FooFactory.java diff --git a/java/compiler/impl/src/com/intellij/compiler/backwardRefs/CompilerReferenceReader.java b/java/compiler/impl/src/com/intellij/compiler/backwardRefs/CompilerReferenceReader.java index 0accda716034..0b90cd79dde1 100644 --- a/java/compiler/impl/src/com/intellij/compiler/backwardRefs/CompilerReferenceReader.java +++ b/java/compiler/impl/src/com/intellij/compiler/backwardRefs/CompilerReferenceReader.java @@ -66,7 +66,7 @@ class CompilerReferenceReader { final LightRef.NamedLightRef[] hierarchy = getWholeHierarchy(hierarchyElement, checkBaseClassAmbiguity); if (hierarchy == null) return null; for (LightRef.NamedLightRef aClass : hierarchy) { - final LightRef overriderUsage = aClass.override(aClass.getName()); + final LightRef overriderUsage = ref.override(aClass.getName()); addUsages(overriderUsage, set); } return set; diff --git a/java/java-tests/testData/compiler/compilerReferenceFindUsages/testMethodUsageOnGetter/Bar.java b/java/java-tests/testData/compiler/compilerReferenceFindUsages/testMethodUsageOnGetter/Bar.java new file mode 100644 index 000000000000..cdda5ab9e03d --- /dev/null +++ b/java/java-tests/testData/compiler/compilerReferenceFindUsages/testMethodUsageOnGetter/Bar.java @@ -0,0 +1,7 @@ +class Bar { + + void m() { + FooFactory.getFoo().someMethod(); + } + +} \ No newline at end of file diff --git a/java/java-tests/testData/compiler/compilerReferenceFindUsages/testMethodUsageOnGetter/Foo.java b/java/java-tests/testData/compiler/compilerReferenceFindUsages/testMethodUsageOnGetter/Foo.java new file mode 100644 index 000000000000..77db046989bf --- /dev/null +++ b/java/java-tests/testData/compiler/compilerReferenceFindUsages/testMethodUsageOnGetter/Foo.java @@ -0,0 +1,5 @@ +class Foo { + void someMethod() { + + } +} \ No newline at end of file diff --git a/java/java-tests/testData/compiler/compilerReferenceFindUsages/testMethodUsageOnGetter/FooFactory.java b/java/java-tests/testData/compiler/compilerReferenceFindUsages/testMethodUsageOnGetter/FooFactory.java new file mode 100644 index 000000000000..54e801af779f --- /dev/null +++ b/java/java-tests/testData/compiler/compilerReferenceFindUsages/testMethodUsageOnGetter/FooFactory.java @@ -0,0 +1,5 @@ +class FooFactory { + public static Foo getFoo() { + return new Foo(); + } +} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/compiler/CompilerReferencesFindUsagesTest.java b/java/java-tests/testSrc/com/intellij/compiler/CompilerReferencesFindUsagesTest.java index 8977e1f40ca3..46b1776dd016 100644 --- a/java/java-tests/testSrc/com/intellij/compiler/CompilerReferencesFindUsagesTest.java +++ b/java/java-tests/testSrc/com/intellij/compiler/CompilerReferencesFindUsagesTest.java @@ -55,6 +55,14 @@ public class CompilerReferencesFindUsagesTest extends DaemonAnalyzerTestCase { return JavaTestUtil.getJavaTestDataPath() + "/compiler/compilerReferenceFindUsages/"; } + public void testMethodUsageOnGetter() throws Exception { + configureByFiles(getName(), getName() + "/Foo.java", getName() + "/FooFactory.java", getName() + "/Bar.java"); + PsiMethod methodToSearch = findClass("Foo").findMethodsByName("someMethod", false)[0]; + assertOneElement(MethodReferencesSearch.search(methodToSearch).findAll()); + myCompilerTester.rebuild(); + assertOneElement(MethodReferencesSearch.search(methodToSearch).findAll()); + } + public void testMethodUsageInClassHierarchy() throws Exception { configureByFiles(getName(), getName() + "/Bar.java", getName() + "/Foo.java"); PsiMethod methodToSearch = findClass("Foo").findMethodsByName("someMethod", false)[0]; From 99041414ab8ebfd6d77dcefa2012c9f4c31ba87c Mon Sep 17 00:00:00 2001 From: Vladimir Krivosheev Date: Fri, 28 Oct 2016 11:47:58 +0200 Subject: [PATCH 03/36] do not create keepass db as memory-only by default --- platform/credential-store/src/PasswordSafeImpl.kt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/platform/credential-store/src/PasswordSafeImpl.kt b/platform/credential-store/src/PasswordSafeImpl.kt index 103b288e4f5f..379cf77abf52 100644 --- a/platform/credential-store/src/PasswordSafeImpl.kt +++ b/platform/credential-store/src/PasswordSafeImpl.kt @@ -39,12 +39,11 @@ class PasswordSafeImpl(/* public - backward compatibility */val settings: Passwo currentProvider = KeePassCredentialStore(memoryOnly = true) } else if (settings.providerType == ProviderType.KEEPASS) { - val dbFile = settings.state.keepassDb?.let { LOG.catchAndLog { return@let Paths.get(it) } return@let null } - currentProvider = KeePassCredentialStore(memoryOnly = true, dbFile = dbFile) + currentProvider = KeePassCredentialStore(dbFile = dbFile) } else { currentProvider = createPersistentCredentialStore() From 83d6f2c59c8569327c960d10871bce6fef0e161a Mon Sep 17 00:00:00 2001 From: "Egor.Ushakov" Date: Fri, 28 Oct 2016 12:53:51 +0300 Subject: [PATCH 04/36] speedup emulated method breakpoints --- .../ui/breakpoints/MethodBreakpoint.java | 54 ++++++++++--------- 1 file changed, 28 insertions(+), 26 deletions(-) diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/MethodBreakpoint.java b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/MethodBreakpoint.java index 452c54a9f00f..07f418e52547 100644 --- a/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/MethodBreakpoint.java +++ b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/MethodBreakpoint.java @@ -156,33 +156,35 @@ public class MethodBreakpoint extends BreakpointWithHighlighter allLineLocations = method.allLineLocations(); - if (isWatchEntry()) { - createLocationBreakpointRequest(ContainerUtil.getFirstItem(allLineLocations), debugProcess); - } - if (isWatchExit()) { - MethodBytecodeUtil.visit(classType, method, new MethodVisitor(Opcodes.API_VERSION) { - int myLastLine = 0; - @Override - public void visitLineNumber(int line, Label start) { - myLastLine = line; - } - - @Override - public void visitInsn(int opcode) { - switch (opcode) { - case Opcodes.RETURN: - case Opcodes.IRETURN: - case Opcodes.FRETURN: - case Opcodes.ARETURN: - case Opcodes.LRETURN: - case Opcodes.DRETURN: - //case Opcodes.ATHROW: - allLineLocations.stream() - .filter(l -> l.lineNumber() == myLastLine) - .findFirst().ifPresent(location -> createLocationBreakpointRequest(location, debugProcess)); + if (!allLineLocations.isEmpty()) { + if (isWatchEntry()) { + createLocationBreakpointRequest(ContainerUtil.getFirstItem(allLineLocations), debugProcess); + } + if (isWatchExit()) { + MethodBytecodeUtil.visit(classType, method, new MethodVisitor(Opcodes.API_VERSION) { + int myLastLine = 0; + @Override + public void visitLineNumber(int line, Label start) { + myLastLine = line; } - } - }); + + @Override + public void visitInsn(int opcode) { + switch (opcode) { + case Opcodes.RETURN: + case Opcodes.IRETURN: + case Opcodes.FRETURN: + case Opcodes.ARETURN: + case Opcodes.LRETURN: + case Opcodes.DRETURN: + //case Opcodes.ATHROW: + allLineLocations.stream() + .filter(l -> l.lineNumber() == myLastLine) + .findFirst().ifPresent(location -> createLocationBreakpointRequest(location, debugProcess)); + } + } + }); + } } if (base) { // desired class found - now also track all new classes From 4cbfb0a4df190493e2ebda08f0b5a1d6c3eab2d7 Mon Sep 17 00:00:00 2001 From: peter Date: Fri, 28 Oct 2016 09:46:27 +0200 Subject: [PATCH 05/36] @NotNull FileASTNode.getLighterAST --- platform/core-api/src/com/intellij/lang/FileASTNode.java | 3 +-- .../src/com/intellij/psi/impl/source/tree/FileElement.java | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/platform/core-api/src/com/intellij/lang/FileASTNode.java b/platform/core-api/src/com/intellij/lang/FileASTNode.java index 193e2421ebd1..5ad21bdb4a68 100644 --- a/platform/core-api/src/com/intellij/lang/FileASTNode.java +++ b/platform/core-api/src/com/intellij/lang/FileASTNode.java @@ -17,7 +17,6 @@ package com.intellij.lang; import com.intellij.util.CharTable; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; /** * Top-level node in AST, corresponds to PsiFile. @@ -30,6 +29,6 @@ public interface FileASTNode extends ASTNode { boolean isParsed(); - @Nullable + @NotNull LighterAST getLighterAST(); } diff --git a/platform/core-impl/src/com/intellij/psi/impl/source/tree/FileElement.java b/platform/core-impl/src/com/intellij/psi/impl/source/tree/FileElement.java index 50f1be583953..87355e1b5822 100644 --- a/platform/core-impl/src/com/intellij/psi/impl/source/tree/FileElement.java +++ b/platform/core-impl/src/com/intellij/psi/impl/source/tree/FileElement.java @@ -27,7 +27,6 @@ import com.intellij.psi.tree.IFileElementType; import com.intellij.psi.tree.ILightStubFileElementType; import com.intellij.util.CharTable; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; public class FileElement extends LazyParseableElement implements FileASTNode, Getter { public static final FileElement[] EMPTY_ARRAY = new FileElement[0]; @@ -50,7 +49,7 @@ public class FileElement extends LazyParseableElement implements FileASTNode, Ge return myCharTable; } - @Nullable + @NotNull @Override public LighterAST getLighterAST() { final IFileElementType contentType = (IFileElementType)getElementType(); From 8bcf9ff59dd387ff59ef7e16533542cc3fe3bca6 Mon Sep 17 00:00:00 2001 From: peter Date: Fri, 28 Oct 2016 10:14:39 +0200 Subject: [PATCH 06/36] don't allow unexpected PSI modifications in copy action & MemberChooser (IDEA-163250) --- .../src/com/intellij/ide/actions/CopyElementAction.java | 6 ++++++ .../lang-impl/src/com/intellij/ide/util/MemberChooser.java | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/platform/lang-impl/src/com/intellij/ide/actions/CopyElementAction.java b/platform/lang-impl/src/com/intellij/ide/actions/CopyElementAction.java index fd4359ba2d90..d860328bbc42 100644 --- a/platform/lang-impl/src/com/intellij/ide/actions/CopyElementAction.java +++ b/platform/lang-impl/src/com/intellij/ide/actions/CopyElementAction.java @@ -28,6 +28,12 @@ import com.intellij.psi.PsiFile; import com.intellij.refactoring.copy.CopyHandler; public class CopyElementAction extends AnAction { + + @Override + public boolean startInTransaction() { + return true; + } + @Override public void actionPerformed(AnActionEvent e) { final DataContext dataContext = e.getDataContext(); diff --git a/platform/lang-impl/src/com/intellij/ide/util/MemberChooser.java b/platform/lang-impl/src/com/intellij/ide/util/MemberChooser.java index 5909ac72e027..2a9992bf37a0 100644 --- a/platform/lang-impl/src/com/intellij/ide/util/MemberChooser.java +++ b/platform/lang-impl/src/com/intellij/ide/util/MemberChooser.java @@ -21,6 +21,7 @@ import com.intellij.icons.AllIcons; import com.intellij.ide.IdeBundle; import com.intellij.openapi.actionSystem.*; import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.application.TransactionGuard; import com.intellij.openapi.keymap.KeymapManager; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.DialogWrapper; @@ -682,6 +683,11 @@ public class MemberChooser extends DialogWrapper implemen } } + @Override + public void show() { + TransactionGuard.getInstance().submitTransactionAndWait(super::show); + } + @Override public void dispose() { PropertiesComponent instance = PropertiesComponent.getInstance(); From 8359ff6bb352be81233e781b0ec2ff2f14f3c2d0 Mon Sep 17 00:00:00 2001 From: peter Date: Fri, 28 Oct 2016 11:08:01 +0200 Subject: [PATCH 07/36] IDEA-162397 Throwable at com.intellij.openapi.roots.ui.configuration.ProjectStructureConfigurable.apply --- .../openapi/wm/impl/welcomeScreen/FlatWelcomeFrame.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/welcomeScreen/FlatWelcomeFrame.java b/platform/platform-impl/src/com/intellij/openapi/wm/impl/welcomeScreen/FlatWelcomeFrame.java index 3dd984672bd3..ef0b5d91b4e9 100644 --- a/platform/platform-impl/src/com/intellij/openapi/wm/impl/welcomeScreen/FlatWelcomeFrame.java +++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/welcomeScreen/FlatWelcomeFrame.java @@ -683,6 +683,12 @@ public class FlatWelcomeFrame extends JFrame implements IdeFrame, Disposable, Ac return new AnAction(presentation.getText(), presentation.getDescription(), null) { + + @Override + public boolean startInTransaction() { + return child.startInTransaction(); + } + @Override public void actionPerformed(@NotNull AnActionEvent e) { child.actionPerformed(e); From cb66cc07a18dd3bd097735b2d86055259bd1c927 Mon Sep 17 00:00:00 2001 From: Dmitry Batkovich Date: Fri, 28 Oct 2016 13:35:16 +0300 Subject: [PATCH 08/36] javac ast indices: move from vfs listener to psi listener --- .../backwardRefs/DirtyModulesHolder.java | 66 +++++++++++-------- 1 file changed, 38 insertions(+), 28 deletions(-) diff --git a/java/compiler/impl/src/com/intellij/compiler/backwardRefs/DirtyModulesHolder.java b/java/compiler/impl/src/com/intellij/compiler/backwardRefs/DirtyModulesHolder.java index 4f335dba2c50..82a2dbbf051e 100644 --- a/java/compiler/impl/src/com/intellij/compiler/backwardRefs/DirtyModulesHolder.java +++ b/java/compiler/impl/src/com/intellij/compiler/backwardRefs/DirtyModulesHolder.java @@ -17,13 +17,15 @@ package com.intellij.compiler.backwardRefs; import com.intellij.openapi.module.Module; import com.intellij.openapi.util.UserDataHolderBase; -import com.intellij.openapi.vfs.*; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.psi.*; import com.intellij.psi.search.GlobalSearchScope; import com.intellij.psi.util.CachedValueProvider; import com.intellij.psi.util.CachedValuesManager; import com.intellij.psi.util.PsiModificationTracker; import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.util.Collections; import java.util.Set; @@ -74,63 +76,71 @@ class DirtyModulesHolder extends UserDataHolderBase { } void installVFSListener() { - VirtualFileManager.getInstance().addVirtualFileListener(new VirtualFileAdapter() { + PsiManager.getInstance(myService.getProject()).addPsiTreeChangeListener(new PsiTreeChangeAdapter() { @Override - public void fileCreated(@NotNull VirtualFileEvent event) { - processChange(event.getFile()); + public void beforeChildAddition(@NotNull PsiTreeChangeEvent event) { + psiChanged(event.getFile(), event.getParent()); } @Override - public void fileCopied(@NotNull VirtualFileCopyEvent event) { - processChange(event.getFile()); + public void beforeChildRemoval(@NotNull PsiTreeChangeEvent event) { + psiChanged(event.getFile(), event.getParent()); } @Override - public void fileMoved(@NotNull VirtualFileMoveEvent event) { - processChange(event.getFile()); + public void beforeChildReplacement(@NotNull PsiTreeChangeEvent event) { + psiChanged(event.getFile(), event.getParent()); } @Override - public void beforePropertyChange(@NotNull VirtualFilePropertyEvent event) { - if (VirtualFile.PROP_NAME.equals(event.getPropertyName()) || VirtualFile.PROP_SYMLINK_TARGET.equals(event.getPropertyName())) { - processChange(event.getFile()); + public void beforeChildMovement(@NotNull PsiTreeChangeEvent event) { + final PsiFile file = event.getFile(); + if (file != null) { + psiChanged(file, null); + } + else { + psiChanged(null, event.getOldParent()); + psiChanged(null, event.getNewParent()); } } @Override - public void beforeContentsChange(@NotNull VirtualFileEvent event) { - processChange(event.getFile()); + public void beforeChildrenChange(@NotNull PsiTreeChangeEvent event) { + psiChanged(event.getFile(), event.getParent()); } @Override - public void beforeFileDeletion(@NotNull VirtualFileEvent event) { - processChange(event.getFile()); + public void beforePropertyChange(@NotNull PsiTreeChangeEvent event) { + if (PsiTreeChangeEvent.PROP_UNLOADED_PSI.equals(event.getPropertyName()) || + PsiTreeChangeEvent.PROP_WRITABLE.equals(event.getPropertyName())) return; + psiChanged(event.getFile(), event.getParent()); } - @Override - public void beforeFileMovement(@NotNull VirtualFileMoveEvent event) { - processChange(event.getFile()); - } - - private void processChange(VirtualFile file) { - fileChanged(file); - } - - void fileChanged(VirtualFile file) { + private void psiChanged(@Nullable PsiFile psiFile, @Nullable PsiElement parent) { + final VirtualFile file; + if (psiFile != null) { + file = psiFile.getVirtualFile(); + } + else if (parent instanceof PsiFileSystemItem) { + file = ((PsiFileSystemItem)parent).getVirtualFile(); + } + else { + return; + } if (myService.getFileIndex().isInSourceContent(file) && myService.getFileTypes().contains(file.getFileType())) { final Module module = myService.getFileIndex().getModuleForFile(file); if (module != null) { synchronized (myLock) { if (myCompilationPhase) { myChangedModulesDuringCompilation.add(module); - } else { + } + else { myChangedModules.add(module); } } } } } - }, myService.getProject()); - + }); } } From 3f104634fe828558716df63125facd3aa7c9da11 Mon Sep 17 00:00:00 2001 From: peter Date: Fri, 28 Oct 2016 12:43:08 +0200 Subject: [PATCH 09/36] tolerate classes with no containing file, wherever they come from (EA-90345 - NPE: DefaultClassNavigationContributor$.process) --- .../gotoByName/DefaultClassNavigationContributor.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/java/java-impl/src/com/intellij/ide/util/gotoByName/DefaultClassNavigationContributor.java b/java/java-impl/src/com/intellij/ide/util/gotoByName/DefaultClassNavigationContributor.java index 8e79c54a351d..a38fc0d914c7 100644 --- a/java/java-impl/src/com/intellij/ide/util/gotoByName/DefaultClassNavigationContributor.java +++ b/java/java-impl/src/com/intellij/ide/util/gotoByName/DefaultClassNavigationContributor.java @@ -21,6 +21,7 @@ import com.intellij.navigation.NavigationItem; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.PsiClass; +import com.intellij.psi.PsiFile; import com.intellij.psi.codeStyle.MinusculeMatcher; import com.intellij.psi.codeStyle.NameUtil; import com.intellij.psi.presentation.java.SymbolPresentationUtil; @@ -113,7 +114,7 @@ public class DefaultClassNavigationContributor implements ChooseByNameContributo @Override public boolean process(PsiClass aClass) { - if (aClass.getContainingFile().getVirtualFile() == null || !aClass.isPhysical()) return true; + if (!isPhysical(aClass)) return true; if (isAnnotation && !aClass.isAnnotationType()) return true; if (innerMatcher != null) { if (aClass.getContainingClass() == null) return true; @@ -124,4 +125,9 @@ public class DefaultClassNavigationContributor implements ChooseByNameContributo } }, parameters.getSearchScope(), parameters.getIdFilter()); } + + private static boolean isPhysical(PsiClass aClass) { + PsiFile file = aClass.getContainingFile(); + return file != null && file.getVirtualFile() != null && aClass.isPhysical(); + } } \ No newline at end of file From f29d57c43644d18ab08b578705a2be16a68b2679 Mon Sep 17 00:00:00 2001 From: Vladimir Krivosheev Date: Fri, 28 Oct 2016 12:50:34 +0200 Subject: [PATCH 10/36] IDEA-161933 Command Line launcher always opens new window --- .../src/com/intellij/ide/CommandLineProcessor.java | 2 +- .../src/com/intellij/ide/impl/ProjectUtil.java | 2 +- .../src/com/intellij/project/project.kt | 9 ++------- 3 files changed, 4 insertions(+), 9 deletions(-) diff --git a/platform/platform-impl/src/com/intellij/ide/CommandLineProcessor.java b/platform/platform-impl/src/com/intellij/ide/CommandLineProcessor.java index c9dc1d45b207..0c019b45c04d 100644 --- a/platform/platform-impl/src/com/intellij/ide/CommandLineProcessor.java +++ b/platform/platform-impl/src/com/intellij/ide/CommandLineProcessor.java @@ -74,7 +74,7 @@ public class CommandLineProcessor { // HACK: PlatformProjectOpenProcessor agrees to open anything provider = null; } - if (provider != null || ProjectKt.isValidProjectPath(name, true)) { + if (provider != null || ProjectKt.isValidProjectPath(name)) { final Project result = ProjectUtil.openOrImport(name, null, true); if (result == null) { Messages.showErrorDialog("Cannot open project '" + name + "'", "Cannot Open Project"); diff --git a/platform/platform-impl/src/com/intellij/ide/impl/ProjectUtil.java b/platform/platform-impl/src/com/intellij/ide/impl/ProjectUtil.java index 0f10303c2ac5..4f42abb3004b 100644 --- a/platform/platform-impl/src/com/intellij/ide/impl/ProjectUtil.java +++ b/platform/platform-impl/src/com/intellij/ide/impl/ProjectUtil.java @@ -110,7 +110,7 @@ public class ProjectUtil { return strong.doOpenProject(virtualFile, projectToClose, forceOpenInNewFrame); } - if (ProjectKt.isValidProjectPath(path, true)) { + if (ProjectKt.isValidProjectPath(path)) { return openProject(path, projectToClose, forceOpenInNewFrame); } diff --git a/platform/projectModel-impl/src/com/intellij/project/project.kt b/platform/projectModel-impl/src/com/intellij/project/project.kt index ec432f5a4710..2b1f7111ed8b 100644 --- a/platform/projectModel-impl/src/com/intellij/project/project.kt +++ b/platform/projectModel-impl/src/com/intellij/project/project.kt @@ -44,12 +44,7 @@ fun getProjectStoreDirectory(file: VirtualFile): VirtualFile? { return if (file.isDirectory) file.findChild(Project.DIRECTORY_STORE_FOLDER) else null } -@JvmOverloads -fun isValidProjectPath(path: String, fastCheckIpr: Boolean = false): Boolean { - if (fastCheckIpr && path.endsWith(ProjectFileType.DOT_DEFAULT_EXTENSION)) { - return true - } - +fun isValidProjectPath(path: String): Boolean { val file = try { Paths.get(path) } @@ -58,7 +53,7 @@ fun isValidProjectPath(path: String, fastCheckIpr: Boolean = false): Boolean { } val attributes = file.basicAttributesIfExists() ?: return false - return !attributes.isDirectory /* ipr */ || file.resolve(Project.DIRECTORY_STORE_FOLDER).exists() + return if (attributes.isDirectory) file.resolve(Project.DIRECTORY_STORE_FOLDER).exists() else path.endsWith(ProjectFileType.DOT_DEFAULT_EXTENSION) } fun isProjectDirectoryExistsUsingIo(parent: VirtualFile): Boolean { From 1124e5c29c8915a4ab599c23952918f8f4eef499 Mon Sep 17 00:00:00 2001 From: Rustam Vishnyakov Date: Fri, 28 Oct 2016 13:54:23 +0300 Subject: [PATCH 11/36] EA-82793 Fix --- .../src/com/intellij/lang/XmlCodeFoldingBuilder.java | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/xml/xml-psi-impl/src/com/intellij/lang/XmlCodeFoldingBuilder.java b/xml/xml-psi-impl/src/com/intellij/lang/XmlCodeFoldingBuilder.java index 98af12fea500..b48106c8934f 100644 --- a/xml/xml-psi-impl/src/com/intellij/lang/XmlCodeFoldingBuilder.java +++ b/xml/xml-psi-impl/src/com/intellij/lang/XmlCodeFoldingBuilder.java @@ -244,10 +244,12 @@ public abstract class XmlCodeFoldingBuilder extends CustomFoldingBuilder impleme String text = psi.getText(); String fastPath = StringUtil.unescapeXml(text); if (!StringUtil.equals(fastPath, text)) return fastPath; - final XmlEntityDecl resolve = XmlEntityRefImpl.resolveEntity((XmlElement)psi, text, psi.getContainingFile()); - final XmlAttributeValue value = resolve != null ? resolve.getValueElement() : null; - if (value != null) { - return getEntityValue(value); + if (psi.isValid()) { + final XmlEntityDecl resolve = XmlEntityRefImpl.resolveEntity((XmlElement)psi, text, psi.getContainingFile()); + final XmlAttributeValue value = resolve != null ? resolve.getValueElement() : null; + if (value != null) { + return getEntityValue(value); + } } return null; } From d9ddc2c2ef9d817be14c698ebbe0797b59a3169d Mon Sep 17 00:00:00 2001 From: Alexander Zolotov Date: Fri, 28 Oct 2016 13:48:26 +0300 Subject: [PATCH 12/36] Nullable --- xml/xml-psi-api/src/com/intellij/xml/XmlElementDescriptor.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/xml/xml-psi-api/src/com/intellij/xml/XmlElementDescriptor.java b/xml/xml-psi-api/src/com/intellij/xml/XmlElementDescriptor.java index 6627ca0e7eea..9b07f2ad3ec2 100644 --- a/xml/xml-psi-api/src/com/intellij/xml/XmlElementDescriptor.java +++ b/xml/xml-psi-api/src/com/intellij/xml/XmlElementDescriptor.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2009 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. @@ -56,6 +56,7 @@ public interface XmlElementDescriptor extends PsiMetaData { @Nullable XmlAttributeDescriptor getAttributeDescriptor(XmlAttribute attribute); + @Nullable XmlNSDescriptor getNSDescriptor(); @Nullable From 21388332f6b4a2d6c4a8c54c83fcf1e3842c1a74 Mon Sep 17 00:00:00 2001 From: Vladimir Krivosheev Date: Fri, 28 Oct 2016 13:00:34 +0200 Subject: [PATCH 13/36] cleanup --- .../src/com/intellij/openapi/vcs/FileStatusListener.java | 7 +++++-- .../xml/breadcrumbs/BreadcrumbsInitializingActivity.java | 2 +- .../intellij/xml/breadcrumbs/BreadcrumbsXmlWrapper.java | 4 ---- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/platform/editor-ui-api/src/com/intellij/openapi/vcs/FileStatusListener.java b/platform/editor-ui-api/src/com/intellij/openapi/vcs/FileStatusListener.java index f53a2adc086a..59dc18f97394 100644 --- a/platform/editor-ui-api/src/com/intellij/openapi/vcs/FileStatusListener.java +++ b/platform/editor-ui-api/src/com/intellij/openapi/vcs/FileStatusListener.java @@ -26,6 +26,9 @@ public interface FileStatusListener { * Indicates that some file statuses were change. On this event client should recalculate all statuses * it's dependent on. */ - void fileStatusesChanged(); - void fileStatusChanged(@NotNull VirtualFile virtualFile); + default void fileStatusesChanged() { + } + + default void fileStatusChanged(@NotNull VirtualFile virtualFile) { + } } diff --git a/xml/impl/src/com/intellij/xml/breadcrumbs/BreadcrumbsInitializingActivity.java b/xml/impl/src/com/intellij/xml/breadcrumbs/BreadcrumbsInitializingActivity.java index e47515bdb588..26a2a583dd82 100644 --- a/xml/impl/src/com/intellij/xml/breadcrumbs/BreadcrumbsInitializingActivity.java +++ b/xml/impl/src/com/intellij/xml/breadcrumbs/BreadcrumbsInitializingActivity.java @@ -46,7 +46,7 @@ public class BreadcrumbsInitializingActivity implements StartupActivity, DumbAwa return; } - MessageBusConnection connection = project.getMessageBus().connect(project); + MessageBusConnection connection = project.getMessageBus().connect(); connection.subscribe(FileEditorManagerListener.FILE_EDITOR_MANAGER, new MyFileEditorManagerListener()); connection.subscribe(FileTypeManager.TOPIC, new MyFileTypeListener(project)); diff --git a/xml/impl/src/com/intellij/xml/breadcrumbs/BreadcrumbsXmlWrapper.java b/xml/impl/src/com/intellij/xml/breadcrumbs/BreadcrumbsXmlWrapper.java index a472ba06bbb2..afcdf58a8367 100644 --- a/xml/impl/src/com/intellij/xml/breadcrumbs/BreadcrumbsXmlWrapper.java +++ b/xml/impl/src/com/intellij/xml/breadcrumbs/BreadcrumbsXmlWrapper.java @@ -108,10 +108,6 @@ public class BreadcrumbsXmlWrapper implements BreadcrumbsItemListener Date: Fri, 28 Oct 2016 13:02:18 +0200 Subject: [PATCH 14/36] remove unused and deprecated getStatusText --- .../intellij/openapi/vcs/FileStatusManager.java | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) diff --git a/platform/editor-ui-api/src/com/intellij/openapi/vcs/FileStatusManager.java b/platform/editor-ui-api/src/com/intellij/openapi/vcs/FileStatusManager.java index 4c40f9120a46..4154d5e47165 100644 --- a/platform/editor-ui-api/src/com/intellij/openapi/vcs/FileStatusManager.java +++ b/platform/editor-ui-api/src/com/intellij/openapi/vcs/FileStatusManager.java @@ -33,25 +33,14 @@ public abstract class FileStatusManager { public abstract FileStatus getStatus(@NotNull VirtualFile virtualFile); public abstract void fileStatusesChanged(); + public abstract void fileStatusChanged(VirtualFile file); public abstract void addFileStatusListener(@NotNull FileStatusListener listener); + public abstract void addFileStatusListener(@NotNull FileStatusListener listener, @NotNull Disposable parentDisposable); + public abstract void removeFileStatusListener(@NotNull FileStatusListener listener); - /** - * @deprecated Use getStatus(file).getText()} instead - */ - public String getStatusText(@NotNull VirtualFile file){ - return getStatus(file).getText(); - } - - /** - * @deprecated Use getStatus(file).getColor()} instead - */ - public Color getStatusColor(@NotNull VirtualFile file){ - return getStatus(file).getColor(); - } - public abstract Color getNotChangedDirectoryColor(@NotNull VirtualFile vf); } From b73689aa4c6e2ecbeaf484cd8e15249d93cdcda9 Mon Sep 17 00:00:00 2001 From: Vladimir Krivosheev Date: Fri, 28 Oct 2016 13:22:18 +0200 Subject: [PATCH 15/36] BreadcrumbsXmlWrapper - do not add extra UISettingsListener because BreadcrumbsInitializingActivity can notify --- .../breadcrumbs/BreadcrumbsInitializingActivity.java | 10 +++++++--- .../xml/breadcrumbs/BreadcrumbsXmlWrapper.java | 11 ----------- 2 files changed, 7 insertions(+), 14 deletions(-) diff --git a/xml/impl/src/com/intellij/xml/breadcrumbs/BreadcrumbsInitializingActivity.java b/xml/impl/src/com/intellij/xml/breadcrumbs/BreadcrumbsInitializingActivity.java index 26a2a583dd82..ab8e3ae08b98 100644 --- a/xml/impl/src/com/intellij/xml/breadcrumbs/BreadcrumbsInitializingActivity.java +++ b/xml/impl/src/com/intellij/xml/breadcrumbs/BreadcrumbsInitializingActivity.java @@ -123,17 +123,21 @@ public class BreadcrumbsInitializingActivity implements StartupActivity, DumbAwa for (final FileEditor fileEditor : fileEditors) { if (fileEditor instanceof TextEditor) { Editor editor = ((TextEditor)fileEditor).getEditor(); - if (BreadcrumbsXmlWrapper.getBreadcrumbsComponent(editor) != null) { + BreadcrumbsXmlWrapper wrapper = BreadcrumbsXmlWrapper.getBreadcrumbsComponent(editor); + if (wrapper != null) { + wrapper.queueUpdate(); continue; } - final BreadcrumbsXmlWrapper wrapper = new BreadcrumbsXmlWrapper(editor); + + wrapper = new BreadcrumbsXmlWrapper(editor); final JComponent c = wrapper.getComponent(); fileEditorManager.addTopComponent(fileEditor, c); + BreadcrumbsXmlWrapper finalWrapper = wrapper; Disposer.register(fileEditor, new Disposable() { @Override public void dispose() { - disposeWrapper(fileEditorManager, fileEditor, wrapper); + disposeWrapper(fileEditorManager, fileEditor, finalWrapper); } }); } diff --git a/xml/impl/src/com/intellij/xml/breadcrumbs/BreadcrumbsXmlWrapper.java b/xml/impl/src/com/intellij/xml/breadcrumbs/BreadcrumbsXmlWrapper.java index afcdf58a8367..6aa3bcdd7d3f 100644 --- a/xml/impl/src/com/intellij/xml/breadcrumbs/BreadcrumbsXmlWrapper.java +++ b/xml/impl/src/com/intellij/xml/breadcrumbs/BreadcrumbsXmlWrapper.java @@ -17,8 +17,6 @@ package com.intellij.xml.breadcrumbs; import com.intellij.codeInsight.daemon.impl.tagTreeHighlighting.XmlTagTreeHighlightingUtil; import com.intellij.codeInsight.highlighting.HighlightManager; -import com.intellij.ide.ui.UISettings; -import com.intellij.ide.ui.UISettingsListener; import com.intellij.lang.Language; import com.intellij.openapi.Disposable; import com.intellij.openapi.editor.Document; @@ -101,7 +99,6 @@ public class BreadcrumbsXmlWrapper implements BreadcrumbsItemListener Date: Fri, 28 Oct 2016 15:03:39 +0300 Subject: [PATCH 16/36] IDEA-150047 Disable file centering in project view with "Autoscroll from source" enabled and double click --- .../src/com/intellij/ide/util/treeView/AbstractTreeUi.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/platform-api/src/com/intellij/ide/util/treeView/AbstractTreeUi.java b/platform/platform-api/src/com/intellij/ide/util/treeView/AbstractTreeUi.java index 3570167fdb19..326ca7ff470b 100644 --- a/platform/platform-api/src/com/intellij/ide/util/treeView/AbstractTreeUi.java +++ b/platform/platform-api/src/com/intellij/ide/util/treeView/AbstractTreeUi.java @@ -3801,7 +3801,7 @@ public class AbstractTreeUi { if (!runSelection) { if (elements.length > 0) { - selectVisible(elements[0], onDone, true, true, scrollToVisible); + selectVisible(elements[0], onDone, false, false, scrollToVisible); } return; } From b91c3fc586bcc87b2e00b60095865a8978072472 Mon Sep 17 00:00:00 2001 From: "Vladislav.Soroka" Date: Fri, 28 Oct 2016 15:06:28 +0300 Subject: [PATCH 17/36] gradle: check existing corrupted gradle dist zip before test --- .../plugins/gradle/importing/GradleImportingTestCase.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/plugins/gradle/testSources/org/jetbrains/plugins/gradle/importing/GradleImportingTestCase.java b/plugins/gradle/testSources/org/jetbrains/plugins/gradle/importing/GradleImportingTestCase.java index 5cc755e27074..b69ef51142e1 100644 --- a/plugins/gradle/testSources/org/jetbrains/plugins/gradle/importing/GradleImportingTestCase.java +++ b/plugins/gradle/testSources/org/jetbrains/plugins/gradle/importing/GradleImportingTestCase.java @@ -252,13 +252,17 @@ public abstract class GradleImportingTestCase extends ExternalSystemImportingTes File zip = localDistribution.getZipFile(); try { - ZipFile zipFile = new ZipFile(zip); - zipFile.close(); + if(zip.exists()) { + ZipFile zipFile = new ZipFile(zip); + zipFile.close(); + } } catch (ZipException e) { e.printStackTrace(); System.out.println("Corrupted file will be removed: " + zip.getPath()); FileUtil.delete(zip); + } catch (IOException e) { + e.printStackTrace(); } } From 07c840312f893c346dd0d1fc55cf30d904083728 Mon Sep 17 00:00:00 2001 From: "Vladislav.Soroka" Date: Fri, 28 Oct 2016 15:13:13 +0300 Subject: [PATCH 18/36] gradle: cleanup --- .../plugins/gradle/importing/GradleImportingTestCase.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/plugins/gradle/testSources/org/jetbrains/plugins/gradle/importing/GradleImportingTestCase.java b/plugins/gradle/testSources/org/jetbrains/plugins/gradle/importing/GradleImportingTestCase.java index b69ef51142e1..117507ba1f48 100644 --- a/plugins/gradle/testSources/org/jetbrains/plugins/gradle/importing/GradleImportingTestCase.java +++ b/plugins/gradle/testSources/org/jetbrains/plugins/gradle/importing/GradleImportingTestCase.java @@ -252,7 +252,7 @@ public abstract class GradleImportingTestCase extends ExternalSystemImportingTes File zip = localDistribution.getZipFile(); try { - if(zip.exists()) { + if (zip.exists()) { ZipFile zipFile = new ZipFile(zip); zipFile.close(); } @@ -261,7 +261,8 @@ public abstract class GradleImportingTestCase extends ExternalSystemImportingTes e.printStackTrace(); System.out.println("Corrupted file will be removed: " + zip.getPath()); FileUtil.delete(zip); - } catch (IOException e) { + } + catch (IOException e) { e.printStackTrace(); } } From 94fcbe55f736d106a8a4f6fcb76d41ddbad0bab9 Mon Sep 17 00:00:00 2001 From: Konstantin Bulenkov Date: Fri, 28 Oct 2016 14:17:49 +0200 Subject: [PATCH 19/36] fix progress on CE splash --- community-resources/src/idea/IdeaApplicationInfo.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/community-resources/src/idea/IdeaApplicationInfo.xml b/community-resources/src/idea/IdeaApplicationInfo.xml index 22c75f99867a..ac0c151efe77 100644 --- a/community-resources/src/idea/IdeaApplicationInfo.xml +++ b/community-resources/src/idea/IdeaApplicationInfo.xml @@ -2,8 +2,8 @@ - - + + From 7eebc85df8f3c2f29f2d55d7a36965a2fa65a690 Mon Sep 17 00:00:00 2001 From: "Egor.Ushakov" Date: Fri, 28 Oct 2016 15:24:09 +0300 Subject: [PATCH 20/36] do not show slowness warning for emulated breakpoints --- .../debugger/ui/breakpoints/BreakpointManager.java | 7 ++++++- .../intellij/debugger/ui/breakpoints/MethodBreakpoint.java | 2 +- resources-en/src/messages/DebuggerBundle.properties | 1 + 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/BreakpointManager.java b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/BreakpointManager.java index be97b24719cc..592b9f262973 100644 --- a/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/BreakpointManager.java +++ b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/BreakpointManager.java @@ -113,7 +113,12 @@ public class BreakpointManager { private static boolean checkAndNotifyPossiblySlowBreakpoint(XBreakpoint breakpoint) { if (breakpoint.isEnabled() && (breakpoint.getType() instanceof JavaMethodBreakpointType || breakpoint.getType() instanceof JavaWildcardMethodBreakpointType)) { - XDebugSessionImpl.NOTIFICATION_GROUP.createNotification("Method breakpoints may dramatically slow down debugging", MessageType.WARNING) + Breakpoint bpt = getJavaBreakpoint(breakpoint); + if (bpt instanceof MethodBreakpoint && ((MethodBreakpoint)bpt).isEmulated()) { + return false; + } + XDebugSessionImpl.NOTIFICATION_GROUP + .createNotification(DebuggerBundle.message("method.breakpoints.slowness.warning"), MessageType.WARNING) .notify(((XBreakpointBase)breakpoint).getProject()); return true; } diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/MethodBreakpoint.java b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/MethodBreakpoint.java index 07f418e52547..2032b41dc268 100644 --- a/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/MethodBreakpoint.java +++ b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/MethodBreakpoint.java @@ -455,7 +455,7 @@ public class MethodBreakpoint extends BreakpointWithHighlighter Date: Fri, 28 Oct 2016 15:39:20 +0300 Subject: [PATCH 21/36] javac ast indices: return vfs listener + process uncommitted changes --- .../CompilerReferenceServiceImpl.java | 6 +- .../backwardRefs/DirtyModulesHolder.java | 127 ++++++++++-------- .../psi/impl/PsiDocumentManagerBase.java | 2 +- 3 files changed, 79 insertions(+), 56 deletions(-) diff --git a/java/compiler/impl/src/com/intellij/compiler/backwardRefs/CompilerReferenceServiceImpl.java b/java/compiler/impl/src/com/intellij/compiler/backwardRefs/CompilerReferenceServiceImpl.java index 013add71a911..a092eabb4f4b 100644 --- a/java/compiler/impl/src/com/intellij/compiler/backwardRefs/CompilerReferenceServiceImpl.java +++ b/java/compiler/impl/src/com/intellij/compiler/backwardRefs/CompilerReferenceServiceImpl.java @@ -23,6 +23,7 @@ import com.intellij.lang.injection.InjectedLanguageManager; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.application.ReadAction; import com.intellij.openapi.compiler.*; +import com.intellij.openapi.fileEditor.FileDocumentManager; import com.intellij.openapi.fileTypes.FileType; import com.intellij.openapi.module.Module; import com.intellij.openapi.project.Project; @@ -33,6 +34,7 @@ import com.intellij.openapi.util.Couple; import com.intellij.openapi.util.ModificationTracker; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.VirtualFileWithId; +import com.intellij.psi.PsiDocumentManager; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; import com.intellij.psi.PsiNamedElement; @@ -71,12 +73,12 @@ public class CompilerReferenceServiceImpl extends CompilerReferenceService imple private volatile CompilerReferenceReader myReader; - public CompilerReferenceServiceImpl(Project project) { + public CompilerReferenceServiceImpl(Project project, FileDocumentManager fileDocumentManager, PsiDocumentManager psiDocumentManager) { super(project); myProjectFileIndex = ProjectRootManager.getInstance(project).getFileIndex(); myFileTypes = Stream.of(LanguageLightRefAdapter.INSTANCES).flatMap(a -> a.getFileTypes().stream()).collect(Collectors.toSet()); - myDirtyModulesHolder = new DirtyModulesHolder(this); + myDirtyModulesHolder = new DirtyModulesHolder(this, fileDocumentManager, psiDocumentManager); } @Override diff --git a/java/compiler/impl/src/com/intellij/compiler/backwardRefs/DirtyModulesHolder.java b/java/compiler/impl/src/com/intellij/compiler/backwardRefs/DirtyModulesHolder.java index 82a2dbbf051e..ed06b747dd04 100644 --- a/java/compiler/impl/src/com/intellij/compiler/backwardRefs/DirtyModulesHolder.java +++ b/java/compiler/impl/src/com/intellij/compiler/backwardRefs/DirtyModulesHolder.java @@ -15,18 +15,23 @@ */ package com.intellij.compiler.backwardRefs; +import com.intellij.openapi.application.ReadAction; +import com.intellij.openapi.editor.Document; +import com.intellij.openapi.fileEditor.FileDocumentManager; import com.intellij.openapi.module.Module; import com.intellij.openapi.util.UserDataHolderBase; -import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.psi.*; +import com.intellij.openapi.vfs.*; +import com.intellij.psi.PsiDocumentManager; import com.intellij.psi.search.GlobalSearchScope; import com.intellij.psi.util.CachedValueProvider; import com.intellij.psi.util.CachedValuesManager; import com.intellij.psi.util.PsiModificationTracker; +import com.intellij.util.ObjectUtils; import com.intellij.util.containers.ContainerUtil; +import com.intellij.util.containers.SmartHashSet; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; +import java.util.Collection; import java.util.Collections; import java.util.Set; @@ -34,14 +39,20 @@ import static com.intellij.psi.search.GlobalSearchScope.EMPTY_SCOPE; class DirtyModulesHolder extends UserDataHolderBase { private final CompilerReferenceServiceImpl myService; + private final FileDocumentManager myFileDocManager; + private final PsiDocumentManager myPsiDocManager; private final Set myChangedModules = ContainerUtil.newHashSet(); private final Set myChangedModulesDuringCompilation = ContainerUtil.newHashSet(); private final Object myLock = new Object(); private boolean myCompilationPhase; - public DirtyModulesHolder(@NotNull CompilerReferenceServiceImpl service){ + public DirtyModulesHolder(@NotNull CompilerReferenceServiceImpl service, + FileDocumentManager fileDocumentManager, + PsiDocumentManager psiDocumentManager){ myService = service; + myFileDocManager = fileDocumentManager; + myPsiDocManager = psiDocumentManager; } void compilerActivityStarted() { @@ -62,13 +73,23 @@ class DirtyModulesHolder extends UserDataHolderBase { } GlobalSearchScope getDirtyScope() { - return CachedValuesManager.getManager(myService.getProject()).getCachedValue(this, () -> { - synchronized (myLock) { - final GlobalSearchScope dirtyScope = - myChangedModules.stream().map(Module::getModuleWithDependentsScope).reduce(EMPTY_SCOPE, (s1, s2) -> s1.union(s2)); - return CachedValueProvider.Result.create(dirtyScope, PsiModificationTracker.MODIFICATION_COUNT, myService); + synchronized (myLock) { + final Set unCommittedModules = new SmartHashSet<>(0); + for (Document document : myFileDocManager.getUnsavedDocuments()) { + final Module m = getModuleForSourceContentFile(myFileDocManager.getFile(document)); + if (m != null && !myChangedModules.contains(m)) unCommittedModules.add(m); } - }); + for (Document document : ReadAction.compute(() -> myPsiDocManager.getUncommittedDocuments())) { + final Module m = getModuleForSourceContentFile(ObjectUtils.notNull(myPsiDocManager.getPsiFile(document)).getVirtualFile()); + if (m != null && !myChangedModules.contains(m)) unCommittedModules.add(m); + } + GlobalSearchScope dirtyCommittedScope = CachedValuesManager.getManager(myService.getProject()).getCachedValue(this, () -> + CachedValueProvider.Result.create(addModulesWithDependentToScope(myChangedModules, EMPTY_SCOPE), PsiModificationTracker.MODIFICATION_COUNT, myService)); + if (unCommittedModules.isEmpty()) { + return dirtyCommittedScope; + } + return addModulesWithDependentToScope(unCommittedModules, dirtyCommittedScope); + } } boolean contains(VirtualFile file) { @@ -76,71 +97,71 @@ class DirtyModulesHolder extends UserDataHolderBase { } void installVFSListener() { - PsiManager.getInstance(myService.getProject()).addPsiTreeChangeListener(new PsiTreeChangeAdapter() { + VirtualFileManager.getInstance().addVirtualFileListener(new VirtualFileAdapter() { @Override - public void beforeChildAddition(@NotNull PsiTreeChangeEvent event) { - psiChanged(event.getFile(), event.getParent()); + public void fileCreated(@NotNull VirtualFileEvent event) { + processChange(event.getFile()); } @Override - public void beforeChildRemoval(@NotNull PsiTreeChangeEvent event) { - psiChanged(event.getFile(), event.getParent()); + public void fileCopied(@NotNull VirtualFileCopyEvent event) { + processChange(event.getFile()); } @Override - public void beforeChildReplacement(@NotNull PsiTreeChangeEvent event) { - psiChanged(event.getFile(), event.getParent()); + public void fileMoved(@NotNull VirtualFileMoveEvent event) { + processChange(event.getFile()); } @Override - public void beforeChildMovement(@NotNull PsiTreeChangeEvent event) { - final PsiFile file = event.getFile(); - if (file != null) { - psiChanged(file, null); - } - else { - psiChanged(null, event.getOldParent()); - psiChanged(null, event.getNewParent()); + public void beforePropertyChange(@NotNull VirtualFilePropertyEvent event) { + if (VirtualFile.PROP_NAME.equals(event.getPropertyName()) || VirtualFile.PROP_SYMLINK_TARGET.equals(event.getPropertyName())) { + processChange(event.getFile()); } } @Override - public void beforeChildrenChange(@NotNull PsiTreeChangeEvent event) { - psiChanged(event.getFile(), event.getParent()); + public void beforeContentsChange(@NotNull VirtualFileEvent event) { + processChange(event.getFile()); } @Override - public void beforePropertyChange(@NotNull PsiTreeChangeEvent event) { - if (PsiTreeChangeEvent.PROP_UNLOADED_PSI.equals(event.getPropertyName()) || - PsiTreeChangeEvent.PROP_WRITABLE.equals(event.getPropertyName())) return; - psiChanged(event.getFile(), event.getParent()); + public void beforeFileDeletion(@NotNull VirtualFileEvent event) { + processChange(event.getFile()); } - private void psiChanged(@Nullable PsiFile psiFile, @Nullable PsiElement parent) { - final VirtualFile file; - if (psiFile != null) { - file = psiFile.getVirtualFile(); - } - else if (parent instanceof PsiFileSystemItem) { - file = ((PsiFileSystemItem)parent).getVirtualFile(); - } - else { - return; - } - if (myService.getFileIndex().isInSourceContent(file) && myService.getFileTypes().contains(file.getFileType())) { - final Module module = myService.getFileIndex().getModuleForFile(file); - if (module != null) { - synchronized (myLock) { - if (myCompilationPhase) { - myChangedModulesDuringCompilation.add(module); - } - else { - myChangedModules.add(module); - } + @Override + public void beforeFileMovement(@NotNull VirtualFileMoveEvent event) { + processChange(event.getFile()); + } + + private void processChange(VirtualFile file) { + fileChanged(file); + } + + void fileChanged(VirtualFile file) { + final Module module = getModuleForSourceContentFile(file); + if (module != null) { + synchronized (myLock) { + if (myCompilationPhase) { + myChangedModulesDuringCompilation.add(module); + } else { + myChangedModules.add(module); } } } } - }); + }, myService.getProject()); + } + + private Module getModuleForSourceContentFile(VirtualFile file) { + if (myService.getFileIndex().isInSourceContent(file) && myService.getFileTypes().contains(file.getFileType())) { + return myService.getFileIndex().getModuleForFile(file); + } + return null; + } + + private static GlobalSearchScope addModulesWithDependentToScope(Collection modules, GlobalSearchScope baseScope) { + return modules.stream().map(Module::getModuleWithDependentsScope).reduce(baseScope, (s1, s2) -> s1.union(s2)); } } diff --git a/platform/core-impl/src/com/intellij/psi/impl/PsiDocumentManagerBase.java b/platform/core-impl/src/com/intellij/psi/impl/PsiDocumentManagerBase.java index 1189fe00d901..2ac0a6c9194c 100644 --- a/platform/core-impl/src/com/intellij/psi/impl/PsiDocumentManagerBase.java +++ b/platform/core-impl/src/com/intellij/psi/impl/PsiDocumentManagerBase.java @@ -756,7 +756,7 @@ public abstract class PsiDocumentManagerBase extends PsiDocumentManager implemen @Override @NotNull public Document[] getUncommittedDocuments() { - ApplicationManager.getApplication().assertIsDispatchThread(); + ApplicationManager.getApplication().assertReadAccessAllowed(); Document[] documents = myUncommittedDocuments.toArray(new Document[myUncommittedDocuments.size()]); return ArrayUtil.stripTrailingNulls(documents); } From 94b4385ef1e16ac0f569dbedeb61ed764088cc4c Mon Sep 17 00:00:00 2001 From: Vladimir Krivosheev Date: Fri, 28 Oct 2016 14:36:35 +0200 Subject: [PATCH 22/36] IDEA-162844 more correct fix STR: https://youtrack.jetbrains.com/issue/IDEA-162774#comment=27-1678789) --- .../colors/impl/AbstractColorsScheme.java | 9 +++++- .../editor/colors/EditorColorSchemeTest.kt | 18 ++++++----- .../editor/colors/editorColorScheme.kt | 30 +++++++++++++++++++ 3 files changed, 48 insertions(+), 9 deletions(-) create mode 100644 platform/testFramework/testSrc/com/intellij/openapi/editor/colors/editorColorScheme.kt diff --git a/platform/editor-ui-ex/src/com/intellij/openapi/editor/colors/impl/AbstractColorsScheme.java b/platform/editor-ui-ex/src/com/intellij/openapi/editor/colors/impl/AbstractColorsScheme.java index 28fc81473e6e..60672f8bf92c 100644 --- a/platform/editor-ui-ex/src/com/intellij/openapi/editor/colors/impl/AbstractColorsScheme.java +++ b/platform/editor-ui-ex/src/com/intellij/openapi/editor/colors/impl/AbstractColorsScheme.java @@ -623,6 +623,12 @@ public abstract class AbstractColorsScheme implements EditorColorsScheme, Serial } } + private boolean isParentOverwritingInheritance(@NotNull TextAttributesKey key) { + TextAttributes parentAttributes = + myParentScheme instanceof AbstractColorsScheme ? ((AbstractColorsScheme)myParentScheme).getDirectlyDefinedAttributes(key) : null; + return parentAttributes != null && parentAttributes != USE_INHERITED_MARKER; + } + private void writeAttributes(@NotNull Element attrElements) throws WriteExternalException { List list = new ArrayList<>(myAttributesMap.keySet()); list.sort(null); @@ -630,7 +636,8 @@ public abstract class AbstractColorsScheme implements EditorColorsScheme, Serial TextAttributes attributes = myAttributesMap.get(key); TextAttributesKey baseKey = key.getFallbackAttributeKey(); if (attributes == USE_INHERITED_MARKER) { - if (baseKey != null) { + // do not store if inheritance = on in the parent scheme (https://youtrack.jetbrains.com/issue/IDEA-162774) + if (baseKey != null && isParentOverwritingInheritance(key)) { attrElements.addContent(new Element(OPTION_ELEMENT) .setAttribute(NAME_ATTR, key.getExternalName()) .setAttribute(BASE_ATTRIBUTES_ATTR, baseKey.getExternalName())); diff --git a/platform/platform-tests/testSrc/com/intellij/openapi/editor/colors/EditorColorSchemeTest.kt b/platform/platform-tests/testSrc/com/intellij/openapi/editor/colors/EditorColorSchemeTest.kt index 0a16a493729a..dacc78257b15 100644 --- a/platform/platform-tests/testSrc/com/intellij/openapi/editor/colors/EditorColorSchemeTest.kt +++ b/platform/platform-tests/testSrc/com/intellij/openapi/editor/colors/EditorColorSchemeTest.kt @@ -17,6 +17,7 @@ package com.intellij.openapi.editor.colors import com.intellij.configurationStore.SchemeManagerFactoryBase import com.intellij.openapi.editor.colors.ex.DefaultColorSchemesManager +import com.intellij.openapi.editor.colors.impl.AbstractColorsScheme import com.intellij.openapi.editor.colors.impl.EditorColorsManagerImpl import com.intellij.testFramework.InMemoryFsRule import com.intellij.testFramework.ProjectRule @@ -43,15 +44,9 @@ class EditorColorSchemeTest { val schemeFile = fsRule.fs.getPath("colors/Foo.icls") val schemeData = """ - - 2016-09-29T12:13:05 - idea - 2016.3.0.0 - 2016-09-29T12:14:54 - Default - """.trimIndent() schemeFile.write(schemeData) val schemeManagerFactory = SchemeManagerFactoryBase.TestSchemeManagerFactory(fsRule.fs.getPath("")) @@ -60,9 +55,16 @@ class EditorColorSchemeTest { val scheme = manager.getScheme("Foo") assertThat(scheme.name).isEqualTo("Foo") + (scheme as AbstractColorsScheme).isSaveNeeded = true + schemeManagerFactory.save() - assertThat(schemeFile.readText()).isEqualTo(schemeData) + // JAVA_NUMBER is removed - see isParentOverwritingInheritance + assertThat(removeSchemeMetaInfo(schemeFile.readText())).isEqualTo(""" + + """.trimIndent()) assertThat(schemeFile.parent).hasChildren("Foo.icls") } } diff --git a/platform/testFramework/testSrc/com/intellij/openapi/editor/colors/editorColorScheme.kt b/platform/testFramework/testSrc/com/intellij/openapi/editor/colors/editorColorScheme.kt new file mode 100644 index 000000000000..08b20e33c86e --- /dev/null +++ b/platform/testFramework/testSrc/com/intellij/openapi/editor/colors/editorColorScheme.kt @@ -0,0 +1,30 @@ +/* + * 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. + */ +package com.intellij.openapi.editor.colors + +import java.util.regex.Pattern + +fun removeSchemeMetaInfo(result: String): String { + val matcher = Pattern.compile("\\s+.*", Pattern.DOTALL).matcher(result) + if (!matcher.find()) { + return result + } + + val builder = StringBuffer() + matcher.appendReplacement(builder, "") + matcher.appendTail(builder) + return builder.toString() +} \ No newline at end of file From e76af49fc2417c6d78551ee96c72d865681f18f4 Mon Sep 17 00:00:00 2001 From: "Egor.Ushakov" Date: Fri, 28 Oct 2016 15:45:35 +0300 Subject: [PATCH 23/36] use StreamEx.ofTree --- .../intellij/debugger/ui/breakpoints/MethodBreakpoint.java | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/MethodBreakpoint.java b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/MethodBreakpoint.java index 2032b41dc268..8727d201f606 100644 --- a/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/MethodBreakpoint.java +++ b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/MethodBreakpoint.java @@ -510,10 +510,6 @@ public class MethodBreakpoint extends BreakpointWithHighlighter consumer) { MultiMap inheritance = new MultiMap<>(); classType.virtualMachine().allClasses().forEach(type -> supertypes(type).forEach(st -> inheritance.putValue(st, type))); - subtypes(classType, inheritance).forEach(consumer); - } - - private static Stream subtypes(ReferenceType type, MultiMap inheritance) { - return StreamEx.of(type).append(StreamEx.of(inheritance.get(type)).flatMap(t -> subtypes(t, inheritance))); + StreamEx.ofTree(classType, t -> StreamEx.of(inheritance.get(t))).forEach(consumer); } } From 170ca4768e37db13926fb4b79b4d8dcf9398f4d8 Mon Sep 17 00:00:00 2001 From: Sergey Malenkov Date: Fri, 28 Oct 2016 15:47:58 +0300 Subject: [PATCH 24/36] Use new way to register MnemonicHelper --- platform/util/resources/misc/registry.properties | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/platform/util/resources/misc/registry.properties b/platform/util/resources/misc/registry.properties index 7188840dd03a..aeed23650bd0 100644 --- a/platform/util/resources/misc/registry.properties +++ b/platform/util/resources/misc/registry.properties @@ -55,9 +55,10 @@ ide.popup.resizable.border.sensitivity.description=Amount of pixels from the edg ide.consumeKnownToolkitBugs=true ide.highlight.match.in.selected.only=true ide.lazyIconLoading=true -ide.mnemonic.helper.old=true + +ide.mnemonic.helper.old=false ide.mnemonic.helper.old.restartRequired=true -ide.mnemonic.helper.old.description=Use new algorithm of mnemonics processing +ide.mnemonic.helper.old.description=Use old algorithm of mnemonics processing ide.checkDuplicateMnemonics=false ide.checkDuplicateMnemonics.description=Check for duplicate mnemonics. ide.dnd.textHints=false From 3d425f56872225495dc08ceafa4c9acdc0b3ad3d Mon Sep 17 00:00:00 2001 From: Aleksey Pivovarov Date: Fri, 28 Oct 2016 15:03:51 +0300 Subject: [PATCH 25/36] ui: fix HiDPI scaling in Editor Search and Replace UI * do not scale `icon.getIconWidth()` * use JBDimension to avoid multiplicative scaling with `JBDimension.scale()` * do not set minimumSize depending on icon size - it's unnecessary, as actual size will be calculated as `Math.max(iconSize, minimumSize)` follow-up: 7aa6ce4c45f898fcf7aa37ea83c6ff03de348a9d --- .../lang-impl/src/com/intellij/find/SearchTextArea.java | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/platform/lang-impl/src/com/intellij/find/SearchTextArea.java b/platform/lang-impl/src/com/intellij/find/SearchTextArea.java index a9c2d0eb68d9..6f010e7e3deb 100644 --- a/platform/lang-impl/src/com/intellij/find/SearchTextArea.java +++ b/platform/lang-impl/src/com/intellij/find/SearchTextArea.java @@ -38,6 +38,7 @@ import com.intellij.ui.components.JBList; import com.intellij.ui.components.JBScrollPane; import com.intellij.ui.components.panels.NonOpaquePanel; import com.intellij.util.ArrayUtil; +import com.intellij.util.ui.JBDimension; import com.intellij.util.ui.JBInsets; import com.intellij.util.ui.JBUI; import com.intellij.util.ui.UIUtil; @@ -239,11 +240,7 @@ public class SearchTextArea extends NonOpaquePanel implements PropertyChangeList private static ActionButton createButton(AnAction action) { Presentation presentation = action.getTemplatePresentation(); - Icon icon = presentation.getIcon(); - Dimension d = new Dimension(JBUI.scale(16), JBUI.scale(16)); - if (icon != null) { - d = new Dimension(JBUI.scale(icon.getIconWidth()), JBUI.scale(icon.getIconHeight())); - } + Dimension d = new JBDimension(16, 16); ActionButton button = new ActionButton(action, presentation, ActionPlaces.UNKNOWN, d) { @Override protected DataContext getDataContext() { From a895bbe699eaae67c45b642a1d17c105b24b80f7 Mon Sep 17 00:00:00 2001 From: Aleksey Pivovarov Date: Fri, 28 Oct 2016 15:43:29 +0300 Subject: [PATCH 26/36] IDEA-161501 ui: fix indeterminate progress rendering under HiDPI --- .../laf/darcula/ui/DarculaProgressBarUI.java | 12 +-- .../status/ShowProgressTestDialogAction.java | 78 +++++++++++++++++++ .../src/idea/PlatformActions.xml | 1 + 3 files changed, 86 insertions(+), 5 deletions(-) create mode 100644 platform/platform-impl/src/com/intellij/openapi/wm/impl/status/ShowProgressTestDialogAction.java diff --git a/platform/platform-impl/src/com/intellij/ide/ui/laf/darcula/ui/DarculaProgressBarUI.java b/platform/platform-impl/src/com/intellij/ide/ui/laf/darcula/ui/DarculaProgressBarUI.java index 97644dd91fc0..e191f809509b 100644 --- a/platform/platform-impl/src/com/intellij/ide/ui/laf/darcula/ui/DarculaProgressBarUI.java +++ b/platform/platform-impl/src/com/intellij/ide/ui/laf/darcula/ui/DarculaProgressBarUI.java @@ -69,7 +69,9 @@ public class DarculaProgressBarUI extends BasicProgressBarUI { int x = -offset; final float R = JBUI.scale(8f); final float R2 = JBUI.scale(9f); - final Area containingRoundRect = new Area(new RoundRectangle2D.Float(1f, 1f, w - 2f, h - 2f, R, R)); + final float off = JBUI.scale(1f); + + final Area containingRoundRect = new Area(new RoundRectangle2D.Float(2f * off, 2f * off, w - 4f * off, h - 4f * off, R, R)); while (x < Math.max(c.getWidth(), c.getHeight())) { Path2D.Double path = new Path2D.Double(); float ww = getPeriodLength() / 2f; @@ -87,17 +89,17 @@ public class DarculaProgressBarUI extends BasicProgressBarUI { } offset = (offset + 1) % getPeriodLength(); Area area = new Area(new Rectangle2D.Float(0, 0, w, h)); - area.subtract(new Area(new RoundRectangle2D.Float(1f, 1f, w - 2f, h - 2f, R, R))); - g.setPaint(Gray._128); + area.subtract(new Area(new RoundRectangle2D.Float(off, off, w - 2f * off, h - 2f * off, R, R))); + g.setColor(Gray._128); if (c.isOpaque()) { g.fill(area); } area.subtract(new Area(new RoundRectangle2D.Float(0, 0, w, h, R2, R2))); - g.setPaint(c.getParent().getBackground()); + g.setColor(c.getParent().getBackground()); if (c.isOpaque()) { g.fill(area); } - g.draw(new RoundRectangle2D.Float(1f, 1f, w - 2f - 1f, h - 2f -1f, R, R)); + g.draw(new RoundRectangle2D.Float(off, off, w - 2f * off - off, h - 2f * off - off, R, R)); g.translate(0, -(c.getHeight() - h) / 2); // Deal with possible text painting diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/status/ShowProgressTestDialogAction.java b/platform/platform-impl/src/com/intellij/openapi/wm/impl/status/ShowProgressTestDialogAction.java new file mode 100644 index 000000000000..cb40fde82239 --- /dev/null +++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/status/ShowProgressTestDialogAction.java @@ -0,0 +1,78 @@ +/* + * 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. + */ +package com.intellij.openapi.wm.impl.status; + +import com.intellij.openapi.actionSystem.AnAction; +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.project.DumbAware; +import com.intellij.openapi.ui.DialogWrapper; +import com.intellij.ui.JBProgressBar; +import com.intellij.ui.LightColors; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; +import java.awt.*; + +@SuppressWarnings({"HardCodedStringLiteral"}) +public class ShowProgressTestDialogAction extends AnAction implements DumbAware { + public ShowProgressTestDialogAction() { + super("Show Progress Test Dialog"); + } + + public void actionPerformed(AnActionEvent e) { + new DialogWrapper(e.getProject()) { + { + init(); + } + + @Nullable + @Override + protected JComponent createCenterPanel() { + JPanel panel = new JPanel(); + panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS)); + + panel.add(createPanel(false, false)); + panel.add(createPanel(false, true)); + panel.add(createPanel(true, false)); + panel.add(createPanel(true, true)); + return panel; + } + }.show(); + } + + private static JComponent createPanel(boolean indeterminate, boolean opaque) { + String text = (indeterminate ? "indeterminate" : "determinate") + + (opaque ? "; opaque" : "; non opaque"); + JLabel label = new JLabel(text); + + JBProgressBar progress = new JBProgressBar(); + progress.setIndeterminate(indeterminate); + progress.setValue(30); + progress.setOpaque(opaque); + + JPanel wrapper = new JPanel(new BorderLayout()); + wrapper.add(progress); + wrapper.setBackground(LightColors.BLUE); + + JPanel panel = new JPanel(); + panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS)); + panel.add(label); + panel.add(wrapper); + panel.add(Box.createVerticalStrut(5)); + + return panel; + } +} diff --git a/platform/platform-resources/src/idea/PlatformActions.xml b/platform/platform-resources/src/idea/PlatformActions.xml index 1a01c39658b2..59352461aac1 100644 --- a/platform/platform-resources/src/idea/PlatformActions.xml +++ b/platform/platform-resources/src/idea/PlatformActions.xml @@ -740,6 +740,7 @@ + From a121d9b56d10aaf590d874174be109ec05e4897e Mon Sep 17 00:00:00 2001 From: Anton Makeev Date: Fri, 28 Oct 2016 15:23:46 +0200 Subject: [PATCH 27/36] wait for pending filters in cmake console --- .../execution/impl/AsyncFilterRunner.java | 16 +++++++++++++--- .../execution/impl/EditorHyperlinkSupport.java | 5 +++-- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/platform/platform-impl/src/com/intellij/execution/impl/AsyncFilterRunner.java b/platform/platform-impl/src/com/intellij/execution/impl/AsyncFilterRunner.java index 760b1f87d898..14af50403a23 100644 --- a/platform/platform-impl/src/com/intellij/execution/impl/AsyncFilterRunner.java +++ b/platform/platform-impl/src/com/intellij/execution/impl/AsyncFilterRunner.java @@ -94,25 +94,35 @@ class AsyncFilterRunner { return applyResults.get(); } - private void handleSynchronouslyIfQuick(AtomicBoolean handled, Future future, int timeout) { + private boolean handleSynchronouslyIfQuick(AtomicBoolean handled, Future future, long timeout) { try { future.get(timeout, TimeUnit.MILLISECONDS).applyHighlights(myHyperlinks); handled.set(true); myPendingFilterResults.remove(handled); + return true; } catch (TimeoutException ignored) { + return false; } catch (Exception e) { throw new RuntimeException(e); } } - public void waitForPendingFilters() { + public boolean waitForPendingFilters(long timeoutMs) { ApplicationManager.getApplication().assertIsDispatchThread(); + + long started = System.currentTimeMillis(); while(!myPendingFilterResults.isEmpty()) { Map.Entry> next = myPendingFilterResults.entrySet().iterator().next(); - handleSynchronouslyIfQuick(next.getKey(), next.getValue(), 1000); + + timeoutMs -= System.currentTimeMillis() - started; + if (timeoutMs < 1) return false; + + if (!handleSynchronouslyIfQuick(next.getKey(), next.getValue(), timeoutMs)) return false; } + + return true; } @NotNull diff --git a/platform/platform-impl/src/com/intellij/execution/impl/EditorHyperlinkSupport.java b/platform/platform-impl/src/com/intellij/execution/impl/EditorHyperlinkSupport.java index 87c56122c612..2af4e82824e1 100644 --- a/platform/platform-impl/src/com/intellij/execution/impl/EditorHyperlinkSupport.java +++ b/platform/platform-impl/src/com/intellij/execution/impl/EditorHyperlinkSupport.java @@ -110,8 +110,9 @@ public class EditorHyperlinkSupport { } } - public void waitForPendingFilters() { - myFilterRunner.waitForPendingFilters(); + @SuppressWarnings("SameParameterValue") + public void waitForPendingFilters(long timeoutMs) { + myFilterRunner.waitForPendingFilters(timeoutMs); } @Deprecated From 5249f199a304abca8299467af341d001bed8b608 Mon Sep 17 00:00:00 2001 From: peter Date: Fri, 28 Oct 2016 13:05:21 +0200 Subject: [PATCH 28/36] LookupPreview editor changes should be guarded (EA-90748 - Throwable: ObjectTree.rememberDisposedTrace) --- .../codeInsight/lookup/impl/LookupPreview.java | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/platform/lang-impl/src/com/intellij/codeInsight/lookup/impl/LookupPreview.java b/platform/lang-impl/src/com/intellij/codeInsight/lookup/impl/LookupPreview.java index 4c0e852b2289..3a03a9332a9a 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/lookup/impl/LookupPreview.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/lookup/impl/LookupPreview.java @@ -57,10 +57,12 @@ class LookupPreview { if (!suffix.isEmpty() && editor instanceof EditorImpl && !editor.getSelectionModel().hasSelection() && InplaceRefactoring.getActiveInplaceRenamer(editor) == null) { - for (Caret caret : editor.getCaretModel().getAllCarets()) { - ensureCaretBeforeInlays(caret); - addInlay(suffix, caret.getOffset()); - } + myLookup.performGuardedChange(() -> { + for (Caret caret : editor.getCaretModel().getAllCarets()) { + ensureCaretBeforeInlays(caret); + addInlay(suffix, caret.getOffset()); + } + }); } } From 303a3a845f88340a419f95b1c6cd67b0c69524c3 Mon Sep 17 00:00:00 2001 From: peter Date: Fri, 28 Oct 2016 13:09:25 +0200 Subject: [PATCH 29/36] contract inference index --- .../dataFlow/ContractInference.java | 18 +-- .../dataFlow/ContractInferenceIndex.kt | 111 +++++++++++++++++ .../ContractInferenceInterpreter.java | 5 +- .../dataFlow/InferenceFromSourceUtil.java | 3 +- .../dataFlow/MethodContract.java | 24 ++++ .../dataFlow/MethodDataExternalizer.kt | 114 ++++++++++++++++++ .../dataFlow/NullityInference.java | 16 +-- .../dataFlow/PurityInference.java | 20 ++- .../dataFlow/inferenceResults.kt | 53 +++++--- .../codeInspection/dataFlow/preContracts.kt | 30 ++--- .../psi/impl/source/JavaLightStubBuilder.java | 9 +- .../ContractInferenceFromSourceTest.groovy | 7 +- .../NullityInferenceFromSourceTestCase.groovy | 6 +- .../PurityInferenceFromSourceTest.groovy | 6 +- resources/src/idea/RichPlatformPlugin.xml | 2 + 15 files changed, 347 insertions(+), 77 deletions(-) create mode 100644 java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/ContractInferenceIndex.kt create mode 100644 java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/MethodDataExternalizer.kt diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/ContractInference.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/ContractInference.java index 4509e0aff2d2..dcc1f0da0ee8 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/ContractInference.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/ContractInference.java @@ -16,11 +16,8 @@ package com.intellij.codeInspection.dataFlow; import com.intellij.codeInsight.NullableNotNullManager; -import com.intellij.lang.ASTNode; -import com.intellij.lang.TreeBackedLighterAST; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.util.RecursionManager; -import com.intellij.psi.PsiCodeBlock; import com.intellij.psi.PsiMethod; import com.intellij.psi.PsiPrimitiveType; import com.intellij.psi.PsiType; @@ -50,22 +47,17 @@ public class ContractInference { } return CachedValuesManager.getCachedValue(method, () -> { - TreeBackedLighterAST tree = new TreeBackedLighterAST(method.getContainingFile().getNode()); - PsiCodeBlock body = method.getBody(); - assert body != null; - ASTNode methodNode = method.getNode(); - ASTNode bodyNode = body.getNode(); - List preContracts = methodNode == null || bodyNode == null ? Collections.emptyList() : - new ContractInferenceInterpreter(tree, TreeBackedLighterAST.wrap(methodNode), TreeBackedLighterAST.wrap(bodyNode)).inferContracts(); - List result = RecursionManager.doPreventingRecursion(method, true, () -> postProcessContracts(method, body, preContracts)); + MethodData data = ContractInferenceIndexKt.getIndexedData(method); + List preContracts = data == null ? Collections.emptyList() : data.getContracts(); + List result = RecursionManager.doPreventingRecursion(method, true, () -> postProcessContracts(method, data, preContracts)); if (result == null) result = Collections.emptyList(); return CachedValueProvider.Result.create(result, method, PsiModificationTracker.JAVA_STRUCTURE_MODIFICATION_COUNT); }); } @NotNull - private static List postProcessContracts(@NotNull PsiMethod method, @NotNull PsiCodeBlock body, List rawContracts) { - List contracts = ContainerUtil.concat(rawContracts, c -> c.toContracts(method, body)); + private static List postProcessContracts(@NotNull PsiMethod method, MethodData data, List rawContracts) { + List contracts = ContainerUtil.concat(rawContracts, c -> c.toContracts(method, data.methodBody(method))); if (contracts.isEmpty()) return Collections.emptyList(); final PsiType returnType = method.getReturnType(); diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/ContractInferenceIndex.kt b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/ContractInferenceIndex.kt new file mode 100644 index 000000000000..ee11a213b54f --- /dev/null +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/ContractInferenceIndex.kt @@ -0,0 +1,111 @@ +/* + * 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. + */ +package com.intellij.codeInspection.dataFlow + +import com.intellij.ide.highlighter.JavaFileType +import com.intellij.lang.LighterAST +import com.intellij.lang.LighterASTNode +import com.intellij.lang.TreeBackedLighterAST +import com.intellij.openapi.util.Ref +import com.intellij.psi.PsiMethod +import com.intellij.psi.impl.source.JavaFileElementType +import com.intellij.psi.impl.source.JavaLightStubBuilder +import com.intellij.psi.impl.source.PsiFileImpl +import com.intellij.psi.impl.source.PsiMethodImpl +import com.intellij.psi.impl.source.tree.JavaElementType +import com.intellij.psi.impl.source.tree.LightTreeUtil +import com.intellij.psi.impl.source.tree.RecursiveLighterASTNodeWalkingVisitor +import com.intellij.psi.search.GlobalSearchScope +import com.intellij.psi.util.CachedValueProvider +import com.intellij.psi.util.CachedValuesManager +import com.intellij.util.indexing.* +import com.intellij.util.io.DataExternalizer +import com.intellij.util.io.IntInlineKeyDescriptor +import java.util.* + +/** + * @author peter + */ + +private val INDEX_ID = ID.create("java.inferred.contracts") + +class ContractInferenceIndex : FileBasedIndexExtension(), PsiDependentIndex { + override fun getName() = INDEX_ID + override fun getVersion() = 0 + override fun dependsOnFileContent() = true + override fun getKeyDescriptor() = IntInlineKeyDescriptor() + override fun getValueExternalizer(): DataExternalizer = MethodDataExternalizer + + override fun getInputFilter() = FileBasedIndex.InputFilter { + it.fileType == JavaFileType.INSTANCE && JavaFileElementType.isInSourceContent(it) + } + + override fun getIndexer() = DataIndexer { fc -> + val result = HashMap() + + val tree = (fc as FileContentImpl).lighterASTForPsiDependentIndex + object : RecursiveLighterASTNodeWalkingVisitor(tree) { + var methodIndex = 0 + + override fun visitNode(element: LighterASTNode) { + if (element.tokenType === JavaElementType.METHOD) { + calcData(tree, element)?.let { data -> result[methodIndex] = data } + methodIndex++ + } + + if (JavaLightStubBuilder.isCodeBlockWithoutStubs(element)) return + + super.visitNode(element) + } + }.visitNode(tree.root) + + result + } + +} + +private fun calcData(tree: LighterAST, method: LighterASTNode): MethodData? { + val body = LightTreeUtil.firstChildOfType(tree, method, JavaElementType.CODE_BLOCK) ?: return null + + val nullity = NullityInference.doInferNullity(tree, body) + val purity = PurityInference.doInferPurity(body, tree) + val contracts = ContractInferenceInterpreter(tree, method, body).inferContracts() + if (nullity == null && purity == null && !contracts.isNotEmpty()) return null + + return MethodData(nullity, purity, contracts, body.startOffset, body.endOffset) +} + +fun getIndexedData(method: PsiMethod): MethodData? { + if (method !is PsiMethodImpl || !InferenceFromSourceUtil.shouldInferFromSource(method)) return null + val vFile = method.containingFile.virtualFile ?: return calcNonPhysicalMethodData(method) + + val ref = Ref() + val scope = GlobalSearchScope.fileScope(method.project, vFile) + FileBasedIndex.getInstance().processValues(INDEX_ID, methodIndex(method), vFile, { file, data -> ref.set(data); true }, scope) + return ref.get() +} + +private fun methodIndex(method: PsiMethodImpl): Int { + val file = method.containingFile as PsiFileImpl + val stubTree = file.stubTree ?: file.calcStubTree() + return stubTree.plainList.filter { it.stubType == JavaElementType.METHOD }.map { it.psi }.indexOf(method) +} + +private fun calcNonPhysicalMethodData(method: PsiMethodImpl): MethodData? { + return CachedValuesManager.getCachedValue(method) { + CachedValueProvider.Result(calcData(method.containingFile.node.lighterAST, TreeBackedLighterAST.wrap(method.node)), method) + } +} \ No newline at end of file diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/ContractInferenceInterpreter.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/ContractInferenceInterpreter.java index a56d2c0626f5..08cb3000cae3 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/ContractInferenceInterpreter.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/ContractInferenceInterpreter.java @@ -27,6 +27,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.List; @@ -54,6 +55,7 @@ class ContractInferenceInterpreter { return paramList != null ? getChildrenOfType(myTree, paramList, PARAMETER) : Collections.emptyList(); } + @NotNull List inferContracts() { LighterASTNode[] statements = getStatements(myBody); if (statements.length == 0) return Collections.emptyList(); @@ -153,7 +155,8 @@ class ContractInferenceInterpreter { return asPreContracts(toContracts(states, NOT_NULL_VALUE)); } if (type == METHOD_CALL_EXPRESSION) { - return Collections.singletonList(new MethodCallContract(ExpressionRange.create(expr, myBody.getStartOffset()), states)); + return Collections.singletonList(new MethodCallContract(ExpressionRange.create(expr, myBody.getStartOffset()), + ContainerUtil.map(states, Arrays::asList))); } final ValueConstraint constraint = getLiteralConstraint(expr); diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/InferenceFromSourceUtil.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/InferenceFromSourceUtil.java index dacd067718b5..aa1cf0896eb6 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/InferenceFromSourceUtil.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/InferenceFromSourceUtil.java @@ -39,8 +39,7 @@ public class InferenceFromSourceUtil { private static boolean calcShouldInferFromSource(@NotNull PsiMethod method) { if (isLibraryCode(method) || method.hasModifierProperty(PsiModifier.ABSTRACT) || - PsiUtil.canBeOverriden(method) || - method.getBody() == null) { + PsiUtil.canBeOverriden(method)) { return false; } diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/MethodContract.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/MethodContract.java index 3d92a10278ae..c962fdd6a156 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/MethodContract.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/MethodContract.java @@ -22,6 +22,7 @@ import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.Arrays; import java.util.List; /** @@ -45,6 +46,29 @@ public class MethodContract { return args; } + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof MethodContract)) return false; + + MethodContract contract = (MethodContract)o; + + if (!Arrays.equals(arguments, contract.arguments)) return false; + if (returnValue != contract.returnValue) return false; + + return true; + } + + @Override + public int hashCode() { + int result = 0; + for (ValueConstraint argument : arguments) { + result = 31 * result + argument.ordinal(); + } + result = 31 * result + returnValue.ordinal(); + return result; + } + @Override public String toString() { return StringUtil.join(arguments, constraint -> constraint.toString(), ", ") + " -> " + returnValue; diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/MethodDataExternalizer.kt b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/MethodDataExternalizer.kt new file mode 100644 index 000000000000..c094d311239c --- /dev/null +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/MethodDataExternalizer.kt @@ -0,0 +1,114 @@ +/* + * 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. + */ +package com.intellij.codeInspection.dataFlow + +import com.intellij.util.io.DataExternalizer +import com.intellij.util.io.DataInputOutputUtil +import com.intellij.util.io.IOUtil +import java.io.DataInput +import java.io.DataOutput + +/** + * @author peter + */ +internal object MethodDataExternalizer : DataExternalizer { + + override fun save(out: DataOutput, data: MethodData) { + writeNullable(out, data.nullity) { writeNullity(out, it) } + writeNullable(out, data.purity) { writePurity(out, it) } + writeList(out, data.contracts) { writeContract(out, it) } + DataInputOutputUtil.writeINT(out, data.bodyStart) + DataInputOutputUtil.writeINT(out, data.bodyEnd) + } + + override fun read(input: DataInput): MethodData { + val nullity = readNullable(input) { readNullity(input) } + val purity = readNullable(input) { readPurity(input) } + val contracts = readList(input) { readContract(input) } + return MethodData(nullity, purity, contracts, DataInputOutputUtil.readINT(input), DataInputOutputUtil.readINT(input)) + } + + private fun writeNullity(out: DataOutput, nullity: NullityInferenceResult) = when (nullity) { + is NullityInferenceResult.Predefined -> { out.writeByte(0); out.writeByte(nullity.value.ordinal) } + is NullityInferenceResult.FromDelegate -> { out.writeByte(1); writeRanges(out, nullity.delegateCalls) } + else -> throw IllegalArgumentException(nullity.toString()) + } + private fun readNullity(input: DataInput): NullityInferenceResult = when (input.readByte().toInt()) { + 0 -> NullityInferenceResult.Predefined(Nullness.values()[input.readByte().toInt()]) + else -> NullityInferenceResult.FromDelegate(readRanges(input)) + } + + private fun writeRanges(out: DataOutput, ranges: List) = writeList(out, ranges) { writeRange(out, it) } + private fun readRanges(input: DataInput) = readList(input) { readRange(input) } + + private fun writeRange(out: DataOutput, range: ExpressionRange) { + DataInputOutputUtil.writeINT(out, range.startOffset) + DataInputOutputUtil.writeINT(out, range.endOffset) + } + private fun readRange(input: DataInput) = ExpressionRange(DataInputOutputUtil.readINT(input), DataInputOutputUtil.readINT(input)) + + private fun writePurity(out: DataOutput, purity: PurityInferenceResult) { + writeRanges(out, purity.mutatedRefs) + writeNullable(out, purity.singleCall) { writeRange(out, it) } + } + private fun readPurity(input: DataInput) = PurityInferenceResult(readRanges(input), readNullable(input) { readRange(input) }) + + private fun writeContract(out: DataOutput, contract: PreContract): Unit = when (contract) { + is DelegationContract -> { out.writeByte(0); writeRange(out, contract.expression); out.writeBoolean(contract.negated) } + is KnownContract -> { out.writeByte(1); + writeContractArguments(out, contract.contract.arguments.toList()) + out.writeByte(contract.contract.returnValue.ordinal) + } + is MethodCallContract -> { out.writeByte(2); + writeRange(out, contract.call); + writeList(out, contract.states) { writeContractArguments(out, it) } + } + is NegatingContract -> { out.writeByte(3); writeContract(out, contract.negated) } + is SideEffectFilter -> { out.writeByte(4); + writeRanges(out, contract.expressionsToCheck) + writeList(out, contract.contracts) { writeContract(out, it) } + } + else -> throw IllegalArgumentException(contract.toString()) + } + private fun readContract(input: DataInput): PreContract = when (input.readByte().toInt()) { + 0 -> DelegationContract(readRange(input), input.readBoolean()) + 1 -> KnownContract(MethodContract(readContractArguments(input).toTypedArray(), readValueConstraint(input))) + 2 -> MethodCallContract(readRange(input), readList(input) { readContractArguments(input) }) + 3 -> NegatingContract(readContract(input)) + else -> SideEffectFilter(readRanges(input), readList(input) { readContract(input) }) + } + + private fun writeContractArguments(out: DataOutput, arguments: List) = + writeList(out, arguments) { out.writeByte(it.ordinal) } + private fun readContractArguments(input: DataInput) = readList(input, { readValueConstraint(input) }) + + private fun readValueConstraint(input: DataInput) = MethodContract.ValueConstraint.values()[input.readByte().toInt()] + +} + +// utils + +private fun writeNullable(out: DataOutput, value: T?, writeItem: (T) -> Unit) = when (value) { + null -> out.writeBoolean(false) + else -> { out.writeBoolean(true); writeItem(value) } +} +private fun readNullable(input: DataInput, readEach: () -> T): T? = if (input.readBoolean()) readEach() else null + +private fun writeList(out: DataOutput, list: List, writeEach: (T) -> Unit) { + DataInputOutputUtil.writeINT(out, list.size) + list.forEach(writeEach) +} +private fun readList(input: DataInput, readEach: () -> T) = (0 until DataInputOutputUtil.readINT(input)).map { readEach() } diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/NullityInference.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/NullityInference.java index 08493ec1a8f8..b6ac34569267 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/NullityInference.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/NullityInference.java @@ -15,10 +15,8 @@ */ package com.intellij.codeInspection.dataFlow; -import com.intellij.lang.ASTNode; import com.intellij.lang.LighterAST; import com.intellij.lang.LighterASTNode; -import com.intellij.lang.TreeBackedLighterAST; import com.intellij.openapi.util.RecursionManager; import com.intellij.psi.*; import com.intellij.psi.impl.source.JavaLightTreeUtil; @@ -27,7 +25,7 @@ import com.intellij.psi.tree.IElementType; import com.intellij.psi.util.CachedValueProvider; import com.intellij.psi.util.CachedValuesManager; import com.intellij.psi.util.PsiModificationTracker; -import com.intellij.util.ObjectUtils; +import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.MultiMap; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -53,18 +51,16 @@ public class NullityInference { } return CachedValuesManager.getCachedValue(method, () -> { - TreeBackedLighterAST tree = new TreeBackedLighterAST(method.getContainingFile().getNode()); - PsiCodeBlock body = ObjectUtils.assertNotNull(method.getBody()); - ASTNode node = body.getNode(); - NullityInferenceResult result = node == null ? null : doInferNullity(tree, TreeBackedLighterAST.wrap(node)); - Nullness nullness = result == null ? null : RecursionManager.doPreventingRecursion(method, true, () -> result.getNullness(method, body)); + MethodData data = ContractInferenceIndexKt.getIndexedData(method); + NullityInferenceResult result = data == null ? null : data.getNullity(); + Nullness nullness = result == null ? null : RecursionManager.doPreventingRecursion(method, true, () -> result.getNullness(method, data.methodBody(method))); if (nullness == null) nullness = Nullness.UNKNOWN; return CachedValueProvider.Result.create(nullness, method, PsiModificationTracker.JAVA_STRUCTURE_MODIFICATION_COUNT); }); } @Nullable - private static NullityInferenceResult doInferNullity(LighterAST tree, LighterASTNode body) { + static NullityInferenceResult doInferNullity(LighterAST tree, LighterASTNode body) { AtomicBoolean hasErrors = new AtomicBoolean(); AtomicBoolean hasNotNulls = new AtomicBoolean(); AtomicBoolean hasNulls = new AtomicBoolean(); @@ -134,7 +130,7 @@ public class NullityInference { return null; } if (delegates.size() == 1) { - return new NullityInferenceResult.FromDelegate(delegates.get(delegates.keySet().iterator().next())); + return new NullityInferenceResult.FromDelegate(ContainerUtil.newArrayList(delegates.get(delegates.keySet().iterator().next()))); } if (hasNotNulls.get()) { diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/PurityInference.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/PurityInference.java index 0b8602c287e0..0baaf1a29372 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/PurityInference.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/PurityInference.java @@ -15,14 +15,11 @@ */ package com.intellij.codeInspection.dataFlow; -import com.intellij.lang.ASTNode; import com.intellij.lang.LighterAST; import com.intellij.lang.LighterASTNode; -import com.intellij.lang.TreeBackedLighterAST; import com.intellij.openapi.util.RecursionManager; import com.intellij.openapi.util.Ref; import com.intellij.psi.JavaTokenType; -import com.intellij.psi.PsiCodeBlock; import com.intellij.psi.PsiMethod; import com.intellij.psi.PsiType; import com.intellij.psi.impl.source.JavaLightTreeUtil; @@ -31,7 +28,6 @@ import com.intellij.psi.impl.source.tree.RecursiveLighterASTNodeWalkingVisitor; import com.intellij.psi.tree.IElementType; import com.intellij.psi.util.CachedValueProvider; import com.intellij.psi.util.CachedValuesManager; -import com.intellij.psi.util.PropertyUtil; import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -49,24 +45,20 @@ public class PurityInference { public static boolean inferPurity(@NotNull final PsiMethod method) { if (!InferenceFromSourceUtil.shouldInferFromSource(method) || PsiType.VOID.equals(method.getReturnType()) || - method.getBody() == null || - method.isConstructor() || - PropertyUtil.isSimpleGetter(method)) { + method.isConstructor()) { return false; } return CachedValuesManager.getCachedValue(method, () -> { - TreeBackedLighterAST tree = new TreeBackedLighterAST(method.getContainingFile().getNode()); - PsiCodeBlock body = method.getBody(); - ASTNode node = body.getNode(); - PurityInferenceResult result = node == null ? null : doInferPurity(TreeBackedLighterAST.wrap(node), tree); - Boolean pure = RecursionManager.doPreventingRecursion(method, true, () -> result != null && result.isPure(method, body)); + MethodData data = ContractInferenceIndexKt.getIndexedData(method); + PurityInferenceResult result = data == null ? null : data.getPurity(); + Boolean pure = RecursionManager.doPreventingRecursion(method, true, () -> result != null && result.isPure(method, data.methodBody(method))); return CachedValueProvider.Result.create(pure == Boolean.TRUE, method); }); } @Nullable - private static PurityInferenceResult doInferPurity(LighterASTNode body, LighterAST tree) { + static PurityInferenceResult doInferPurity(LighterASTNode body, LighterAST tree) { List mutatedRefs = new ArrayList<>(); Ref hasReturns = Ref.create(false); List calls = new ArrayList<>(); @@ -74,6 +66,8 @@ public class PurityInference { @Override public void visitNode(@NotNull LighterASTNode element) { IElementType type = element.getTokenType(); + if (type == CLASS || type == ANONYMOUS_CLASS || type == LAMBDA_EXPRESSION) return; + if (type == ASSIGNMENT_EXPRESSION) { mutatedRefs.add(tree.getChildren(element).get(0)); } diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/inferenceResults.kt b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/inferenceResults.kt index fc8c78144ba8..72691488393b 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/inferenceResults.kt +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/inferenceResults.kt @@ -20,13 +20,15 @@ import com.intellij.lang.LighterASTNode import com.intellij.psi.* import com.intellij.psi.search.LocalSearchScope import com.intellij.psi.search.searches.ReferencesSearch +import com.intellij.psi.util.CachedValueProvider +import com.intellij.psi.util.CachedValuesManager import com.intellij.psi.util.PsiTreeUtil import com.intellij.psi.util.PsiUtil /** * @author peter */ -data class ExpressionRange private constructor (private val startOffset: Int, private val endOffset: Int) { +data class ExpressionRange internal constructor (internal val startOffset: Int, internal val endOffset: Int) { companion object { @JvmStatic @@ -41,18 +43,18 @@ data class ExpressionRange private constructor (private val startOffset: Int, pr } -data class PurityInferenceResult(private val mutatedRefs: List, private val singleCall: ExpressionRange?) { +data class PurityInferenceResult(internal val mutatedRefs: List, internal val singleCall: ExpressionRange?) { - fun isPure(method: PsiMethod, body: PsiCodeBlock) = !mutatesNonLocals(method, body) && callsOnlyPureMethods(body) + fun isPure(method: PsiMethod, body: () -> PsiCodeBlock) = !mutatesNonLocals(method, body) && callsOnlyPureMethods(body) - private fun mutatesNonLocals(method: PsiMethod, body: PsiCodeBlock): Boolean { - return mutatedRefs.any { range -> !isLocalVarReference(range.restoreExpression(body), method) } + private fun mutatesNonLocals(method: PsiMethod, body: () -> PsiCodeBlock): Boolean { + return mutatedRefs.any { range -> !isLocalVarReference(range.restoreExpression(body()), method) } } - private fun callsOnlyPureMethods(body: PsiCodeBlock): Boolean { + private fun callsOnlyPureMethods(body: () -> PsiCodeBlock): Boolean { if (singleCall == null) return true - val called = (singleCall.restoreExpression(body) as PsiCall).resolveMethod() + val called = (singleCall.restoreExpression(body()) as PsiCall).resolveMethod() return called != null && ControlFlowAnalyzer.isPure(called) } @@ -86,20 +88,20 @@ data class PurityInferenceResult(private val mutatedRefs: List, interface NullityInferenceResult { - fun getNullness(method: PsiMethod, body: PsiCodeBlock): Nullness + fun getNullness(method: PsiMethod, body: () -> PsiCodeBlock): Nullness - data class Predefined(private val value: Nullness) : NullityInferenceResult { - - override fun getNullness(method: PsiMethod, body: PsiCodeBlock) = when { + @Suppress("EqualsOrHashCode") + data class Predefined(internal val value: Nullness) : NullityInferenceResult { + override fun hashCode() = value.ordinal + override fun getNullness(method: PsiMethod, body: () -> PsiCodeBlock) = when { value == Nullness.NULLABLE && InferenceFromSourceUtil.suppressNullable(method) -> Nullness.UNKNOWN else -> value } } - data class FromDelegate (private val delegateCalls: Collection) : NullityInferenceResult { - - override fun getNullness(method: PsiMethod, body: PsiCodeBlock) = when { - delegateCalls.all { range -> isNotNullCall(range, body) } -> Nullness.NOT_NULL + data class FromDelegate(internal val delegateCalls: List) : NullityInferenceResult { + override fun getNullness(method: PsiMethod, body: () -> PsiCodeBlock) = when { + delegateCalls.all { range -> isNotNullCall(range, body()) } -> Nullness.NOT_NULL else -> Nullness.UNKNOWN } @@ -111,4 +113,25 @@ interface NullityInferenceResult { return target != null && NullableNotNullManager.isNotNull(target) } } +} + +data class MethodData( + val nullity: NullityInferenceResult?, + val purity: PurityInferenceResult?, + val contracts: List, + internal val bodyStart: Int, + internal val bodyEnd: Int +) { + fun methodBody(method: PsiMethod): () -> PsiCodeBlock = { + if ((method as StubBasedPsiElement<*>?)?.stub != null) + CachedValuesManager.getCachedValue(method) { CachedValueProvider.Result(getDetachedBody(method), method) } + else + method.body!! + } + + private fun getDetachedBody(method: PsiMethod): PsiCodeBlock { + val document = method.containingFile.viewProvider.document ?: return method.body!! + val bodyText = PsiDocumentManager.getInstance(method.project).getLastCommittedText(document).substring(bodyStart, bodyEnd) + return JavaPsiFacade.getElementFactory(method.project).createCodeBlockFromText(bodyText, method) + } } \ No newline at end of file diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/preContracts.kt b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/preContracts.kt index ed6b52164e61..2c3c7901f0c5 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/preContracts.kt +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/preContracts.kt @@ -27,19 +27,19 @@ import com.siyeh.ig.psiutils.SideEffectChecker * @author peter */ interface PreContract { - fun toContracts(method: PsiMethod, body: PsiCodeBlock): List + fun toContracts(method: PsiMethod, body: () -> PsiCodeBlock): List fun negate(): PreContract? = NegatingContract(this) } internal data class KnownContract(val contract: MethodContract) : PreContract { - override fun toContracts(method: PsiMethod, body: PsiCodeBlock) = listOf(contract) + override fun toContracts(method: PsiMethod, body: () -> PsiCodeBlock) = listOf(contract) override fun negate() = negateContract(contract)?.let(::KnownContract) } -internal data class DelegationContract(private val expression: ExpressionRange, private val negated: Boolean) : PreContract { +internal data class DelegationContract(internal val expression: ExpressionRange, internal val negated: Boolean) : PreContract { - override fun toContracts(method: PsiMethod, body: PsiCodeBlock): List { - val call = expression.restoreExpression(body) as PsiMethodCallExpression? ?: return emptyList() + override fun toContracts(method: PsiMethod, body: () -> PsiCodeBlock): List { + val call = expression.restoreExpression(body()) as PsiMethodCallExpression? ?: return emptyList() val result = call.resolveMethodGenerics() val targetMethod = result.element as PsiMethod? ?: return emptyList() @@ -103,10 +103,10 @@ internal data class DelegationContract(private val expression: ExpressionRange, } } -internal data class SideEffectFilter(private val expressionsToCheck: List, private val contracts: List) : PreContract { +internal data class SideEffectFilter(internal val expressionsToCheck: List, internal val contracts: List) : PreContract { - override fun toContracts(method: PsiMethod, body: PsiCodeBlock): List { - if (expressionsToCheck.any { d -> mayHaveSideEffects(body, d) }) { + override fun toContracts(method: PsiMethod, body: () -> PsiCodeBlock): List { + if (expressionsToCheck.any { d -> mayHaveSideEffects(body(), d) }) { return emptyList() } return contracts.flatMap { c -> c.toContracts(method, body) } @@ -116,8 +116,8 @@ internal data class SideEffectFilter(private val expressionsToCheck: List PsiCodeBlock) = negated.toContracts(method, body).mapNotNull(::negateContract) } private fun negateContract(c: MethodContract): MethodContract? { @@ -125,12 +125,14 @@ private fun negateContract(c: MethodContract): MethodContract? { return if (ret == TRUE_VALUE || ret == FALSE_VALUE) MethodContract(c.arguments, negateConstraint(ret)) else null } -internal data class MethodCallContract(private val call: ExpressionRange, private val states: List>) : PreContract { +@Suppress("EqualsOrHashCode") +internal data class MethodCallContract(internal val call: ExpressionRange, internal val states: List>) : PreContract { + override fun hashCode() = call.hashCode() * 31 + states.flatten().map { it.ordinal }.hashCode() - override fun toContracts(method: PsiMethod, body: PsiCodeBlock): List { - val target = (call.restoreExpression(body) as PsiMethodCallExpression?)?.resolveMethod() + override fun toContracts(method: PsiMethod, body: () -> PsiCodeBlock): List { + val target = (call.restoreExpression(body()) as PsiMethodCallExpression?)?.resolveMethod() if (target != null && NullableNotNullManager.isNotNull(target)) { - return ContractInferenceInterpreter.toContracts(states, NOT_NULL_VALUE) + return ContractInferenceInterpreter.toContracts(states.map { it.toTypedArray() }, NOT_NULL_VALUE) } return emptyList() } diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/JavaLightStubBuilder.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/JavaLightStubBuilder.java index b13629f0ac2c..3470ad3a1ca8 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/source/JavaLightStubBuilder.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/JavaLightStubBuilder.java @@ -67,12 +67,11 @@ public class JavaLightStubBuilder extends LightStubBuilder { @Override protected boolean skipChildProcessingWhenBuildingStubs(@NotNull LighterAST tree, @NotNull LighterASTNode parent, @NotNull LighterASTNode node) { - IElementType parentType = parent.getTokenType(); - IElementType nodeType = node.getTokenType(); + return checkByTypes(parent.getTokenType(), node.getTokenType()) || isCodeBlockWithoutStubs(node); + } - if (checkByTypes(parentType, nodeType)) return true; - - if (nodeType == JavaElementType.CODE_BLOCK) { + public static boolean isCodeBlockWithoutStubs(@NotNull LighterASTNode node) { + if (node.getTokenType() == JavaElementType.CODE_BLOCK && node instanceof LighterLazyParseableNode) { CodeBlockVisitor visitor = new CodeBlockVisitor(); ((LighterLazyParseableNode)node).accept(visitor); return visitor.result; diff --git a/java/java-tests/testSrc/com/intellij/codeInspection/ContractInferenceFromSourceTest.groovy b/java/java-tests/testSrc/com/intellij/codeInspection/ContractInferenceFromSourceTest.groovy index 9f1ec912fa93..f03845a25b19 100644 --- a/java/java-tests/testSrc/com/intellij/codeInspection/ContractInferenceFromSourceTest.groovy +++ b/java/java-tests/testSrc/com/intellij/codeInspection/ContractInferenceFromSourceTest.groovy @@ -17,9 +17,9 @@ package com.intellij.codeInspection import com.intellij.codeInspection.dataFlow.ContractInference import com.intellij.psi.PsiAnonymousClass +import com.intellij.psi.impl.source.PsiFileImpl import com.intellij.psi.util.PsiTreeUtil import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase - /** * @author peter */ @@ -569,6 +569,9 @@ class Foo {{ private List inferContracts(String method) { def clazz = myFixture.addClass("final class Foo { $method }") - return ContractInference.inferContracts(clazz.methods[0]).collect { it as String } + assert !((PsiFileImpl) clazz.containingFile).contentsLoaded + def contracts = ContractInference.inferContracts(clazz.methods[0]) + assert !((PsiFileImpl) clazz.containingFile).contentsLoaded + return contracts.collect { it as String } } } diff --git a/java/java-tests/testSrc/com/intellij/codeInspection/NullityInferenceFromSourceTestCase.groovy b/java/java-tests/testSrc/com/intellij/codeInspection/NullityInferenceFromSourceTestCase.groovy index dfa86716bf34..fdab395534ee 100644 --- a/java/java-tests/testSrc/com/intellij/codeInspection/NullityInferenceFromSourceTestCase.groovy +++ b/java/java-tests/testSrc/com/intellij/codeInspection/NullityInferenceFromSourceTestCase.groovy @@ -19,6 +19,7 @@ import com.intellij.codeInsight.NullableNotNullManager import com.intellij.codeInspection.dataFlow.DfaUtil import com.intellij.codeInspection.dataFlow.Nullness import com.intellij.psi.PsiMethod +import com.intellij.psi.impl.source.PsiFileImpl import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase import org.jetbrains.annotations.Contract @@ -124,7 +125,10 @@ Object foo(Object o) { if (o == null) return o.hashCode(); return 2; } static class LightInferenceTest extends NullityInferenceFromSourceTestCase { Nullness inferNullity(PsiMethod method) { - return NullableNotNullManager.isNotNull(method) ? NOT_NULL : NullableNotNullManager.isNullable(method) ? NULLABLE : UNKNOWN + assert !((PsiFileImpl) method.containingFile).contentsLoaded + def result = NullableNotNullManager.isNotNull(method) ? NOT_NULL : NullableNotNullManager.isNullable(method) ? NULLABLE : UNKNOWN + assert !((PsiFileImpl) method.containingFile).contentsLoaded + return result } void "test skip when errors"() { diff --git a/java/java-tests/testSrc/com/intellij/codeInspection/PurityInferenceFromSourceTest.groovy b/java/java-tests/testSrc/com/intellij/codeInspection/PurityInferenceFromSourceTest.groovy index 1cacdb4e0f4a..7d5846021f31 100644 --- a/java/java-tests/testSrc/com/intellij/codeInspection/PurityInferenceFromSourceTest.groovy +++ b/java/java-tests/testSrc/com/intellij/codeInspection/PurityInferenceFromSourceTest.groovy @@ -16,6 +16,7 @@ package com.intellij.codeInspection import com.intellij.codeInspection.dataFlow.PurityInference +import com.intellij.psi.impl.source.PsiFileImpl import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase /** * @author peter @@ -164,7 +165,10 @@ public Foo() { private void assertPure(boolean expected, String classBody) { def clazz = myFixture.addClass("final class Foo { $classBody }") - assert expected == PurityInference.inferPurity(clazz.methods[0]) + assert !((PsiFileImpl) clazz.containingFile).contentsLoaded + def purity = PurityInference.inferPurity(clazz.methods[0]) + assert !((PsiFileImpl) clazz.containingFile).contentsLoaded + assert expected == purity } } diff --git a/resources/src/idea/RichPlatformPlugin.xml b/resources/src/idea/RichPlatformPlugin.xml index 79f8d9d9e66a..d2a9d01ca2c1 100644 --- a/resources/src/idea/RichPlatformPlugin.xml +++ b/resources/src/idea/RichPlatformPlugin.xml @@ -172,6 +172,8 @@ + + From b84ac7f210047a71451e219d564f5374ff572583 Mon Sep 17 00:00:00 2001 From: peter Date: Fri, 28 Oct 2016 14:18:35 +0200 Subject: [PATCH 30/36] speedup contract inference index reduce the number of passes over light AST because it doesn't remember the contents of lazy-parseable tokens for long and thus might parse the same code block several times --- .../dataFlow/ContractInferenceIndex.kt | 34 +++- .../ContractInferenceInterpreter.java | 45 +++--- .../dataFlow/NullityInference.java | 149 +++++++++--------- .../dataFlow/PurityInference.java | 78 ++++----- 4 files changed, 165 insertions(+), 141 deletions(-) diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/ContractInferenceIndex.kt b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/ContractInferenceIndex.kt index ee11a213b54f..c4b2b3db1d69 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/ContractInferenceIndex.kt +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/ContractInferenceIndex.kt @@ -26,6 +26,7 @@ import com.intellij.psi.impl.source.JavaLightStubBuilder import com.intellij.psi.impl.source.PsiFileImpl import com.intellij.psi.impl.source.PsiMethodImpl import com.intellij.psi.impl.source.tree.JavaElementType +import com.intellij.psi.impl.source.tree.JavaElementType.* import com.intellij.psi.impl.source.tree.LightTreeUtil import com.intellij.psi.impl.source.tree.RecursiveLighterASTNodeWalkingVisitor import com.intellij.psi.search.GlobalSearchScope @@ -78,11 +79,36 @@ class ContractInferenceIndex : FileBasedIndexExtension(), PsiDe } private fun calcData(tree: LighterAST, method: LighterASTNode): MethodData? { - val body = LightTreeUtil.firstChildOfType(tree, method, JavaElementType.CODE_BLOCK) ?: return null + val body = LightTreeUtil.firstChildOfType(tree, method, CODE_BLOCK) ?: return null + val statements = ContractInferenceInterpreter.getStatements(body, tree) - val nullity = NullityInference.doInferNullity(tree, body) - val purity = PurityInference.doInferPurity(body, tree) - val contracts = ContractInferenceInterpreter(tree, method, body).inferContracts() + val contracts = ContractInferenceInterpreter(tree, method, body).inferContracts(statements) + + val nullityVisitor = NullityInference.NullityInferenceVisitor(tree, body) + val purityVisitor = PurityInference.PurityInferenceVisitor(tree, body) + for (statement in statements) { + walkMethodBody(tree, statement) { nullityVisitor.visitNode(it); purityVisitor.visitNode(it) } + } + + return createData(body, contracts, nullityVisitor.result, purityVisitor.result) +} + +private fun walkMethodBody(tree: LighterAST, root: LighterASTNode, processor: (LighterASTNode) -> Unit) { + object : RecursiveLighterASTNodeWalkingVisitor(tree) { + override fun visitNode(element: LighterASTNode) { + val type = element.tokenType + if (type === CLASS || type === ANONYMOUS_CLASS || type === LAMBDA_EXPRESSION) return + + processor(element) + super.visitNode(element) + } + }.visitNode(root) +} + +private fun createData(body: LighterASTNode, + contracts: List, + nullity: NullityInferenceResult?, + purity: PurityInferenceResult?): MethodData? { if (nullity == null && purity == null && !contracts.isNotEmpty()) return null return MethodData(nullity, purity, contracts, body.startOffset, body.endOffset) diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/ContractInferenceInterpreter.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/ContractInferenceInterpreter.java index 08cb3000cae3..353f053d2c80 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/ContractInferenceInterpreter.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/ContractInferenceInterpreter.java @@ -37,6 +37,7 @@ import static com.intellij.psi.impl.source.JavaLightTreeUtil.getExpressionChildr import static com.intellij.psi.impl.source.tree.JavaElementType.*; import static com.intellij.psi.impl.source.tree.LightTreeUtil.firstChildOfType; import static com.intellij.psi.impl.source.tree.LightTreeUtil.getChildrenOfType; +import static java.util.Collections.singletonList; class ContractInferenceInterpreter { private final LighterAST myTree; @@ -56,25 +57,25 @@ class ContractInferenceInterpreter { } @NotNull - List inferContracts() { - LighterASTNode[] statements = getStatements(myBody); - if (statements.length == 0) return Collections.emptyList(); + List inferContracts(List statements) { + if (statements.isEmpty()) return Collections.emptyList(); - if (statements.length == 1) { - if (statements[0].getTokenType() == RETURN_STATEMENT) { - List result = handleDelegation(findExpressionChild(myTree, statements[0]), false); + if (statements.size() == 1) { + LighterASTNode statement = statements.get(0); + if (statement.getTokenType() == RETURN_STATEMENT) { + List result = handleDelegation(findExpressionChild(myTree, statement), false); if (result != null) { return result; } } - else if (statements[0].getTokenType() == EXPRESSION_STATEMENT) { - LighterASTNode expr = findExpressionChild(myTree, statements[0]); + else if (statement.getTokenType() == EXPRESSION_STATEMENT) { + LighterASTNode expr = findExpressionChild(myTree, statement); List result = expr != null && expr.getTokenType() == METHOD_CALL_EXPRESSION ? handleDelegation(expr, false) : null; if (result != null) return result; } } - return visitStatements(Collections.singletonList(MethodContract.createConstraintArray(getParameters().size())), statements); + return visitStatements(singletonList(MethodContract.createConstraintArray(getParameters().size())), statements); } @Nullable @@ -83,10 +84,8 @@ class ContractInferenceInterpreter { } @NotNull - private LighterASTNode[] getStatements(@Nullable LighterASTNode codeBlock) { - return codeBlock == null - ? LighterASTNode.EMPTY_ARRAY - : getChildrenOfType(myTree, codeBlock, ElementType.JAVA_STATEMENT_BIT_SET).toArray(LighterASTNode.EMPTY_ARRAY); + static List getStatements(@Nullable LighterASTNode codeBlock, LighterAST tree) { + return codeBlock == null ? Collections.emptyList() : getChildrenOfType(tree, codeBlock, ElementType.JAVA_STATEMENT_BIT_SET); } @Nullable @@ -101,7 +100,7 @@ class ContractInferenceInterpreter { } if (expression.getTokenType() == METHOD_CALL_EXPRESSION) { - return Collections.singletonList(new DelegationContract(ExpressionRange.create(expression, myBody.getStartOffset()), negated)); + return singletonList(new DelegationContract(ExpressionRange.create(expression, myBody.getStartOffset()), negated)); } return null; @@ -155,7 +154,7 @@ class ContractInferenceInterpreter { return asPreContracts(toContracts(states, NOT_NULL_VALUE)); } if (type == METHOD_CALL_EXPRESSION) { - return Collections.singletonList(new MethodCallContract(ExpressionRange.create(expr, myBody.getStartOffset()), + return singletonList(new MethodCallContract(ExpressionRange.create(expr, myBody.getStartOffset()), ContainerUtil.map(states, Arrays::asList))); } @@ -300,24 +299,24 @@ class ContractInferenceInterpreter { } @NotNull - private List visitStatements(List states, LighterASTNode... statements) { + private List visitStatements(List states, List statements) { CodeBlockContracts result = new CodeBlockContracts(); for (LighterASTNode statement : statements) { IElementType type = statement.getTokenType(); if (type == BLOCK_STATEMENT) { - result.addAll(visitStatements(states, getStatements(getCodeBlock(statement)))); + result.addAll(visitStatements(states, getStatements(getCodeBlock(statement), myTree))); } else if (type == IF_STATEMENT) { List conditionResults = visitExpression(states, findExpressionChild(myTree, statement)); - LighterASTNode[] thenElse = getStatements(statement); - if (thenElse.length > 0) { - result.addAll(visitStatements(antecedentsReturning(conditionResults, TRUE_VALUE), thenElse[0])); + List thenElse = getStatements(statement, myTree); + if (thenElse.size() > 0) { + result.addAll(visitStatements(antecedentsReturning(conditionResults, TRUE_VALUE), singletonList(thenElse.get(0)))); } List falseStates = antecedentsReturning(conditionResults, FALSE_VALUE); - if (thenElse.length > 1) { - result.addAll(visitStatements(falseStates, thenElse[1])); + if (thenElse.size() > 1) { + result.addAll(visitStatements(falseStates, singletonList(thenElse.get(1)))); } else { states = falseStates; continue; @@ -342,7 +341,7 @@ class ContractInferenceInterpreter { continue; } else if (type == DO_WHILE_STATEMENT) { - result.addAll(visitStatements(states, getStatements(statement))); + result.addAll(visitStatements(states, getStatements(statement, myTree))); } break; // visit only the first statement unless it's 'if' whose 'then' always returns and the next statement is effectively 'else' diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/NullityInference.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/NullityInference.java index b6ac34569267..ef3a58c5e526 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/NullityInference.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/NullityInference.java @@ -20,7 +20,6 @@ import com.intellij.lang.LighterASTNode; import com.intellij.openapi.util.RecursionManager; import com.intellij.psi.*; import com.intellij.psi.impl.source.JavaLightTreeUtil; -import com.intellij.psi.impl.source.tree.RecursiveLighterASTNodeWalkingVisitor; import com.intellij.psi.tree.IElementType; import com.intellij.psi.util.CachedValueProvider; import com.intellij.psi.util.CachedValuesManager; @@ -31,7 +30,6 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.List; -import java.util.concurrent.atomic.AtomicBoolean; import static com.intellij.psi.impl.source.tree.JavaElementType.*; @@ -59,83 +57,84 @@ public class NullityInference { }); } - @Nullable - static NullityInferenceResult doInferNullity(LighterAST tree, LighterASTNode body) { - AtomicBoolean hasErrors = new AtomicBoolean(); - AtomicBoolean hasNotNulls = new AtomicBoolean(); - AtomicBoolean hasNulls = new AtomicBoolean(); - AtomicBoolean hasUnknowns = new AtomicBoolean(); + static class NullityInferenceVisitor { + private final LighterAST tree; + private final LighterASTNode body; + private boolean hasErrors; + private boolean hasNotNulls; + private boolean hasNulls; + private boolean hasUnknowns; MultiMap delegates = MultiMap.create(); - new RecursiveLighterASTNodeWalkingVisitor(tree) { - @Override - public void visitNode(@NotNull LighterASTNode element) { - IElementType type = element.getTokenType(); - if (type == CLASS || type == ANONYMOUS_CLASS || type == LAMBDA_EXPRESSION) return; - - if (type == TokenType.ERROR_ELEMENT) { - hasErrors.set(true); - } - else if (type == RETURN_STATEMENT) { - LighterASTNode value = JavaLightTreeUtil.findExpressionChild(tree, element); - if (value == null) { - hasErrors.set(true); - } else { - visitReturnedValue(value); - } - } - - super.visitNode(element); - } - - private void visitReturnedValue(LighterASTNode expr) { - IElementType type = expr.getTokenType(); - if (containsNulls(expr)) { - hasNulls.set(true); - } - else if (type == LAMBDA_EXPRESSION || type == NEW_EXPRESSION || - type == LITERAL_EXPRESSION || type == BINARY_EXPRESSION || type == POLYADIC_EXPRESSION) { - hasNotNulls.set(true); - } - else if (type == METHOD_CALL_EXPRESSION) { - String calledMethod = JavaLightTreeUtil.getNameIdentifierText(tree, tree.getChildren(expr).get(0)); - if (calledMethod != null) { - delegates.putValue(calledMethod, ExpressionRange.create(expr, body.getStartOffset())); - } - } - else { - hasUnknowns.set(true); - } - } - - private boolean containsNulls(@NotNull LighterASTNode value) { - if (value.getTokenType() == CONDITIONAL_EXPRESSION) { - List exprChildren = JavaLightTreeUtil.getExpressionChildren(tree, value); - return exprChildren.subList(1, exprChildren.size()).stream().anyMatch(e -> containsNulls(e)); - } - if (value.getTokenType() == PARENTH_EXPRESSION) { - LighterASTNode wrapped = JavaLightTreeUtil.findExpressionChild(tree, value); - return wrapped != null && containsNulls(wrapped); - } - return value.getTokenType() == LITERAL_EXPRESSION && tree.getChildren(value).get(0).getTokenType() == JavaTokenType.NULL_KEYWORD; - } - - }.visitNode(body); - - - if (hasNulls.get()) { - return new NullityInferenceResult.Predefined(Nullness.NULLABLE); + NullityInferenceVisitor(LighterAST tree, LighterASTNode body) { + this.tree = tree; + this.body = body; } - if (hasErrors.get() || hasUnknowns.get() || delegates.size() > 1) { + + void visitNode(LighterASTNode element) { + IElementType type = element.getTokenType(); + + if (type == TokenType.ERROR_ELEMENT) { + hasErrors = true; + } + else if (type == RETURN_STATEMENT) { + LighterASTNode value = JavaLightTreeUtil.findExpressionChild(tree, element); + if (value == null) { + hasErrors= true; + } else { + visitReturnedValue(value); + } + } + } + + private void visitReturnedValue(LighterASTNode expr) { + IElementType type = expr.getTokenType(); + if (containsNulls(expr)) { + hasNulls = true; + } + else if (type == LAMBDA_EXPRESSION || type == NEW_EXPRESSION || + type == LITERAL_EXPRESSION || type == BINARY_EXPRESSION || type == POLYADIC_EXPRESSION) { + hasNotNulls = true; + } + else if (type == METHOD_CALL_EXPRESSION) { + String calledMethod = JavaLightTreeUtil.getNameIdentifierText(tree, tree.getChildren(expr).get(0)); + if (calledMethod != null) { + delegates.putValue(calledMethod, ExpressionRange.create(expr, body.getStartOffset())); + } + } + else { + hasUnknowns = true; + } + } + + private boolean containsNulls(@NotNull LighterASTNode value) { + if (value.getTokenType() == CONDITIONAL_EXPRESSION) { + List exprChildren = JavaLightTreeUtil.getExpressionChildren(tree, value); + return exprChildren.subList(1, exprChildren.size()).stream().anyMatch(e -> containsNulls(e)); + } + if (value.getTokenType() == PARENTH_EXPRESSION) { + LighterASTNode wrapped = JavaLightTreeUtil.findExpressionChild(tree, value); + return wrapped != null && containsNulls(wrapped); + } + return value.getTokenType() == LITERAL_EXPRESSION && tree.getChildren(value).get(0).getTokenType() == JavaTokenType.NULL_KEYWORD; + } + + @Nullable + NullityInferenceResult getResult() { + if (hasNulls) { + return new NullityInferenceResult.Predefined(Nullness.NULLABLE); + } + if (hasErrors || hasUnknowns || delegates.size() > 1) { + return null; + } + if (delegates.size() == 1) { + return new NullityInferenceResult.FromDelegate(ContainerUtil.newArrayList(delegates.get(delegates.keySet().iterator().next()))); + } + + if (hasNotNulls) { + return new NullityInferenceResult.Predefined(Nullness.NOT_NULL); + } return null; } - if (delegates.size() == 1) { - return new NullityInferenceResult.FromDelegate(ContainerUtil.newArrayList(delegates.get(delegates.keySet().iterator().next()))); - } - - if (hasNotNulls.get()) { - return new NullityInferenceResult.Predefined(Nullness.NOT_NULL); - } - return null; } } diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/PurityInference.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/PurityInference.java index 0baaf1a29372..232400fa9bc4 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/PurityInference.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/PurityInference.java @@ -18,13 +18,11 @@ package com.intellij.codeInspection.dataFlow; import com.intellij.lang.LighterAST; import com.intellij.lang.LighterASTNode; import com.intellij.openapi.util.RecursionManager; -import com.intellij.openapi.util.Ref; import com.intellij.psi.JavaTokenType; import com.intellij.psi.PsiMethod; import com.intellij.psi.PsiType; import com.intellij.psi.impl.source.JavaLightTreeUtil; import com.intellij.psi.impl.source.tree.LightTreeUtil; -import com.intellij.psi.impl.source.tree.RecursiveLighterASTNodeWalkingVisitor; import com.intellij.psi.tree.IElementType; import com.intellij.psi.util.CachedValueProvider; import com.intellij.psi.util.CachedValuesManager; @@ -57,50 +55,52 @@ public class PurityInference { }); } - @Nullable - static PurityInferenceResult doInferPurity(LighterASTNode body, LighterAST tree) { - List mutatedRefs = new ArrayList<>(); - Ref hasReturns = Ref.create(false); - List calls = new ArrayList<>(); - new RecursiveLighterASTNodeWalkingVisitor(tree) { - @Override - public void visitNode(@NotNull LighterASTNode element) { - IElementType type = element.getTokenType(); - if (type == CLASS || type == ANONYMOUS_CLASS || type == LAMBDA_EXPRESSION) return; + static class PurityInferenceVisitor { + private final LighterAST tree; + private final LighterASTNode body; + private List mutatedRefs = new ArrayList<>(); + private boolean hasReturns; + private List calls = new ArrayList<>(); - if (type == ASSIGNMENT_EXPRESSION) { - mutatedRefs.add(tree.getChildren(element).get(0)); - } - else if (type == RETURN_STATEMENT && JavaLightTreeUtil.findExpressionChild(tree, element) != null) { - hasReturns.set(true); - } - else if ((type == PREFIX_EXPRESSION || type == POSTFIX_EXPRESSION) && isMutatingOperation(element)) { - ContainerUtil.addIfNotNull(mutatedRefs, JavaLightTreeUtil.findExpressionChild(tree, element)); - } - else if (isCall(element, type)) { - calls.add(element); - } + PurityInferenceVisitor(LighterAST tree, LighterASTNode body) { + this.tree = tree; + this.body = body; + } - super.visitNode(element); + void visitNode(LighterASTNode element) { + IElementType type = element.getTokenType(); + if (type == ASSIGNMENT_EXPRESSION) { + mutatedRefs.add(tree.getChildren(element).get(0)); } - - private boolean isCall(@NotNull LighterASTNode element, IElementType type) { - return type == NEW_EXPRESSION && LightTreeUtil.firstChildOfType(tree, element, EXPRESSION_LIST) != null || - type == METHOD_CALL_EXPRESSION; + else if (type == RETURN_STATEMENT && JavaLightTreeUtil.findExpressionChild(tree, element) != null) { + hasReturns = true; } - - private boolean isMutatingOperation(@NotNull LighterASTNode element) { - return LightTreeUtil.firstChildOfType(tree, element, JavaTokenType.PLUSPLUS) != null || - LightTreeUtil.firstChildOfType(tree, element, JavaTokenType.MINUSMINUS) != null; + else if ((type == PREFIX_EXPRESSION || type == POSTFIX_EXPRESSION) && isMutatingOperation(element)) { + ContainerUtil.addIfNotNull(mutatedRefs, JavaLightTreeUtil.findExpressionChild(tree, element)); } + else if (isCall(element, type)) { + calls.add(element); + } + } - }.visitNode(body); + private boolean isCall(@NotNull LighterASTNode element, IElementType type) { + return type == NEW_EXPRESSION && LightTreeUtil.firstChildOfType(tree, element, EXPRESSION_LIST) != null || + type == METHOD_CALL_EXPRESSION; + } - if (calls.size() > 1 || !hasReturns.get()) return null; - - int bodyStart = body.getStartOffset(); - return new PurityInferenceResult(ContainerUtil.map(mutatedRefs, node -> ExpressionRange.create(node, bodyStart)), - calls.isEmpty() ? null : ExpressionRange.create(calls.get(0), bodyStart)); + private boolean isMutatingOperation(@NotNull LighterASTNode element) { + return LightTreeUtil.firstChildOfType(tree, element, JavaTokenType.PLUSPLUS) != null || + LightTreeUtil.firstChildOfType(tree, element, JavaTokenType.MINUSMINUS) != null; + } + + @Nullable + PurityInferenceResult getResult() { + if (calls.size() > 1 || !hasReturns) return null; + + int bodyStart = body.getStartOffset(); + return new PurityInferenceResult(ContainerUtil.map(mutatedRefs, node -> ExpressionRange.create(node, bodyStart)), + calls.isEmpty() ? null : ExpressionRange.create(calls.get(0), bodyStart)); + } } } From 7a832bc13122a8c817f225813e4c21ed96d59921 Mon Sep 17 00:00:00 2001 From: "Anna.Kozlova" Date: Fri, 28 Oct 2016 11:41:57 +0200 Subject: [PATCH 31/36] fix quick fix name --- .../src/com/siyeh/InspectionGadgetsBundle.properties | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/InspectionGadgetsBundle.properties b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/InspectionGadgetsBundle.properties index 4913876518f6..6dea1edaf985 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/InspectionGadgetsBundle.properties +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/InspectionGadgetsBundle.properties @@ -1911,7 +1911,7 @@ usage.of.obsolete.assert.display.name=Usage of obsolete 'junit.framework.Assert' use.of.obsolete.assert.problem.descriptor=Call to #ref() from ''{0}'' should be replaced with call to method from ''org.junit.Assert'' #loc use.of.obsolete.assert.quickfix=Replace with 'org.junit.Assert' method call junit5.assertions.converter.problem.descriptor=Call to #ref() from ''{0}'' should be replaced with call to method from ''{1}'' #loc -junit5.assertions.converter.quickfix=Replace with ''0'' method call +junit5.assertions.converter.quickfix=Replace with ''{0}'' method call junit5.assertions.converter.familyName=Replace with JUnit 5 compatible call properties.object.as.hashtable.set.quickfix=Replace with call to 'setProperty()' properties.object.as.hashtable.get.quickfix=Replace with call to 'getProperty()' @@ -2203,3 +2203,4 @@ overly.long.lambda.problem.descriptor=Lambda expression is too long (# Non-comme wait.notify.not.in.synchronized.context.display.name='wait()' or 'notify()' while not synchronized wait.notify.while.not.synchronized.on.problem.descriptor=Call to #ref while not synchronized on ''{0}'' #loc junit5.assertions.converter.display.name=Obsolete assertions in JUnit 5 tests +junit5.converter.display.name=JUnit4 test can be JUnit5 From d17336c6db9a84288f74796a18ec1261b8ff02b3 Mon Sep 17 00:00:00 2001 From: "Anna.Kozlova" Date: Fri, 28 Oct 2016 15:25:16 +0200 Subject: [PATCH 32/36] junit 5 conversion: convert tests without @RunWith/@Rule/@ClassRule annotations --- .../migration/MigrationManager.java | 9 +- .../migration/MigrationMapSet.java | 16 +- .../migration/MigrationProcessor.java | 13 +- .../refactoring/migration/MigrationUtil.java | 30 +-- .../ex/LocalInspectionToolWrapper.java | 2 - .../src/META-INF/InspectionGadgets.xml | 4 + .../siyeh/InspectionGadgetsBundle.properties | 1 + .../siyeh/ig/junit/JUnitCommonClassNames.java | 3 + .../src/com/siyeh/ig/psiutils/TestUtils.java | 4 +- .../siyeh/ig/testFrameworks/AssertHint.java | 5 +- .../JUnit5AssertionsConverterInspection.java | 14 +- .../ig/junit/JUnit5ConverterInspection.java | 187 ++++++++++++++++++ .../JUnit5Converter.html | 7 + .../junit/junit5_converter/Simple.after.java | 35 ++++ .../junit/junit5_converter/Simple.java | 34 ++++ .../fixes/junit/Junit5ConverterFixTest.java | 64 ++++++ 16 files changed, 393 insertions(+), 35 deletions(-) create mode 100644 plugins/InspectionGadgets/src/com/siyeh/ig/junit/JUnit5ConverterInspection.java create mode 100644 plugins/InspectionGadgets/src/inspectionDescriptions/JUnit5Converter.html create mode 100644 plugins/InspectionGadgets/test/com/siyeh/igfixes/junit/junit5_converter/Simple.after.java create mode 100644 plugins/InspectionGadgets/test/com/siyeh/igfixes/junit/junit5_converter/Simple.java create mode 100644 plugins/InspectionGadgets/testsrc/com/siyeh/ig/fixes/junit/Junit5ConverterFixTest.java diff --git a/java/java-impl/src/com/intellij/refactoring/migration/MigrationManager.java b/java/java-impl/src/com/intellij/refactoring/migration/MigrationManager.java index 59cd10da7367..89afe193fbe6 100644 --- a/java/java-impl/src/com/intellij/refactoring/migration/MigrationManager.java +++ b/java/java-impl/src/com/intellij/refactoring/migration/MigrationManager.java @@ -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. @@ -16,6 +16,8 @@ package com.intellij.refactoring.migration; import com.intellij.openapi.project.Project; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; public class MigrationManager { private final Project myProject; @@ -35,4 +37,9 @@ public class MigrationManager { new MigrationProcessor(myProject, migrationMap).run(); } + + @Nullable + public MigrationMap findMigrationMap(@NotNull String name) { + return myMigrationMapSet.findMigrationMap(name); + } } diff --git a/java/java-impl/src/com/intellij/refactoring/migration/MigrationMapSet.java b/java/java-impl/src/com/intellij/refactoring/migration/MigrationMapSet.java index 8beeebf673c0..fc901c0830cb 100644 --- a/java/java-impl/src/com/intellij/refactoring/migration/MigrationMapSet.java +++ b/java/java-impl/src/com/intellij/refactoring/migration/MigrationMapSet.java @@ -29,13 +29,14 @@ import org.jdom.Document; import org.jdom.Element; import org.jdom.JDOMException; import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.net.URL; -import java.nio.file.Paths; import java.util.ArrayList; import java.util.Iterator; @@ -70,6 +71,19 @@ public class MigrationMapSet { // saveMaps(); } + @Nullable + public MigrationMap findMigrationMap(@NotNull String name) { + if (myMaps == null) { + loadMaps(); + } + for (MigrationMap map : myMaps) { + if (name.equals(map.getName())) { + return map; + } + } + return null; + } + public void replaceMap(MigrationMap oldMap, MigrationMap newMap) { for(int i = 0; i < myMaps.size(); i++){ if (myMaps.get(i) == oldMap){ diff --git a/java/java-impl/src/com/intellij/refactoring/migration/MigrationProcessor.java b/java/java-impl/src/com/intellij/refactoring/migration/MigrationProcessor.java index 83d651321513..e9254185a7ed 100644 --- a/java/java-impl/src/com/intellij/refactoring/migration/MigrationProcessor.java +++ b/java/java-impl/src/com/intellij/refactoring/migration/MigrationProcessor.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2015 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,6 +25,7 @@ import com.intellij.openapi.util.Ref; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiMigration; import com.intellij.psi.impl.migration.PsiMigrationManager; +import com.intellij.psi.search.GlobalSearchScope; import com.intellij.refactoring.BaseRefactoringProcessor; import com.intellij.refactoring.RefactoringBundle; import com.intellij.refactoring.RefactoringHelper; @@ -42,10 +43,16 @@ public class MigrationProcessor extends BaseRefactoringProcessor { private final MigrationMap myMigrationMap; private static final String REFACTORING_NAME = RefactoringBundle.message("migration.title"); private PsiMigration myPsiMigration; + private final GlobalSearchScope mySearchScope; public MigrationProcessor(Project project, MigrationMap migrationMap) { + this(project, migrationMap, GlobalSearchScope.projectScope(project)); + } + + public MigrationProcessor(Project project, MigrationMap migrationMap, GlobalSearchScope scope) { super(project); myMigrationMap = migrationMap; + mySearchScope = scope; myPsiMigration = startMigration(project); } @@ -88,10 +95,10 @@ public class MigrationProcessor extends BaseRefactoringProcessor { MigrationMapEntry entry = myMigrationMap.getEntryAt(i); UsageInfo[] usages; if (entry.getType() == MigrationMapEntry.PACKAGE) { - usages = MigrationUtil.findPackageUsages(myProject, myPsiMigration, entry.getOldName()); + usages = MigrationUtil.findPackageUsages(myProject, myPsiMigration, entry.getOldName(), mySearchScope); } else { - usages = MigrationUtil.findClassUsages(myProject, myPsiMigration, entry.getOldName()); + usages = MigrationUtil.findClassUsages(myProject, myPsiMigration, entry.getOldName(), mySearchScope); } for (UsageInfo usage : usages) { diff --git a/java/java-impl/src/com/intellij/refactoring/migration/MigrationUtil.java b/java/java-impl/src/com/intellij/refactoring/migration/MigrationUtil.java index 99b3ac84010e..60fcb612a103 100644 --- a/java/java-impl/src/com/intellij/refactoring/migration/MigrationUtil.java +++ b/java/java-impl/src/com/intellij/refactoring/migration/MigrationUtil.java @@ -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. @@ -15,11 +15,10 @@ */ package com.intellij.refactoring.migration; -import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.application.WriteAction; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Comparing; -import com.intellij.openapi.util.Computable; import com.intellij.openapi.util.TextRange; import com.intellij.psi.*; import com.intellij.psi.impl.source.resolve.reference.impl.providers.JavaClassReference; @@ -36,10 +35,10 @@ public class MigrationUtil { private MigrationUtil() { } - public static UsageInfo[] findPackageUsages(Project project, PsiMigration migration, String qName) { + public static UsageInfo[] findPackageUsages(Project project, PsiMigration migration, String qName, GlobalSearchScope searchScope) { PsiPackage aPackage = findOrCreatePackage(project, migration, qName); - return findRefs(project, aPackage); + return findRefs(project, aPackage, searchScope); } public static void doPackageMigration(Project project, PsiMigration migration, String newQName, UsageInfo[] usages) { @@ -82,16 +81,15 @@ public class MigrationUtil { } } - public static UsageInfo[] findClassUsages(Project project, PsiMigration migration, String qName) { + public static UsageInfo[] findClassUsages(Project project, PsiMigration migration, String qName, GlobalSearchScope searchScope) { PsiClass aClass = findOrCreateClass(project, migration, qName); - return findRefs(project, aClass); + return findRefs(project, aClass, searchScope); } - private static UsageInfo[] findRefs(final Project project, final PsiElement aClass) { + private static UsageInfo[] findRefs(final Project project, final PsiElement aClass, GlobalSearchScope searchScope) { final ArrayList results = new ArrayList<>(); - GlobalSearchScope projectScope = GlobalSearchScope.projectScope(project); - for (PsiReference usage : ReferencesSearch.search(aClass, projectScope, false)) { + for (PsiReference usage : ReferencesSearch.search(aClass, searchScope, false)) { results.add(new UsageInfo(usage)); } @@ -133,22 +131,14 @@ public class MigrationUtil { return aPackage; } else { - return ApplicationManager.getApplication().runWriteAction(new Computable() { - public PsiPackage compute() { - return migration.createPackage(qName); - } - }); + return WriteAction.compute(() -> migration.createPackage(qName)); } } static PsiClass findOrCreateClass(Project project, final PsiMigration migration, final String qName) { PsiClass aClass = JavaPsiFacade.getInstance(project).findClass(qName, GlobalSearchScope.allScope(project)); if (aClass == null) { - aClass = ApplicationManager.getApplication().runWriteAction(new Computable() { - public PsiClass compute() { - return migration.createClass(qName); - } - }); + aClass = WriteAction.compute(() -> migration.createClass(qName)); } return aClass; } diff --git a/platform/analysis-impl/src/com/intellij/codeInspection/ex/LocalInspectionToolWrapper.java b/platform/analysis-impl/src/com/intellij/codeInspection/ex/LocalInspectionToolWrapper.java index 1cca5efb7499..251317571574 100644 --- a/platform/analysis-impl/src/com/intellij/codeInspection/ex/LocalInspectionToolWrapper.java +++ b/platform/analysis-impl/src/com/intellij/codeInspection/ex/LocalInspectionToolWrapper.java @@ -27,7 +27,6 @@ import com.intellij.psi.PsiElement; import gnu.trove.THashMap; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.jetbrains.annotations.TestOnly; import java.util.Map; @@ -36,7 +35,6 @@ import java.util.Map; */ public class LocalInspectionToolWrapper extends InspectionToolWrapper { /** This should be used in tests primarily */ - @TestOnly public LocalInspectionToolWrapper(@NotNull LocalInspectionTool tool) { super(tool, ourEPMap.getValue().get(tool.getShortName())); } diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/META-INF/InspectionGadgets.xml b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/META-INF/InspectionGadgets.xml index ed0aaa93f018..afd0e1a4f322 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/META-INF/InspectionGadgets.xml +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/META-INF/InspectionGadgets.xml @@ -2815,6 +2815,10 @@ key="junit5.assertions.converter.display.name" groupBundle="messages.InspectionsBundle" groupKey="group.names.junit.issues" enabledByDefault="false" level="WARNING" implementationClass="com.siyeh.ig.junit.JUnit5AssertionsConverterInspection"/> + diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/InspectionGadgetsBundle.properties b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/InspectionGadgetsBundle.properties index 6dea1edaf985..d71660236fb2 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/InspectionGadgetsBundle.properties +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/InspectionGadgetsBundle.properties @@ -2204,3 +2204,4 @@ wait.notify.not.in.synchronized.context.display.name='wait()' or 'notify()' whil wait.notify.while.not.synchronized.on.problem.descriptor=Call to #ref while not synchronized on ''{0}'' #loc junit5.assertions.converter.display.name=Obsolete assertions in JUnit 5 tests junit5.converter.display.name=JUnit4 test can be JUnit5 +junit5.converter.fix.name=Migrate to JUnit5 diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/junit/JUnitCommonClassNames.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/junit/JUnitCommonClassNames.java index 9d50187406dd..fc10305facdf 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/junit/JUnitCommonClassNames.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/junit/JUnitCommonClassNames.java @@ -23,4 +23,7 @@ public class JUnitCommonClassNames { public static final String ORG_JUNIT_JUPITER_API_ASSUMPTIONS = "org.junit.jupiter.api.Assumptions"; public static final String JUNIT_FRAMEWORK_TEST_CASE = "junit.framework.TestCase"; public static final String ORG_HAMCREST_MATCHER_ASSERT = "org.hamcrest.MatcherAssert"; + public static final String ORG_JUNIT_TEST = "org.junit.Test"; + public static final String ORG_JUNIT_RULE = "org.junit.Rule"; + public static final String ORG_JUNIT_CLASS_RULE = "org.junit.ClassRule"; } diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/TestUtils.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/TestUtils.java index 68c85078e271..3353c198d078 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/TestUtils.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/TestUtils.java @@ -17,8 +17,6 @@ package com.siyeh.ig.psiutils; import com.intellij.codeInsight.AnnotationUtil; import com.intellij.codeInsight.TestFrameworks; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.roots.ProjectFileIndex; import com.intellij.openapi.roots.ProjectRootManager; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.*; @@ -101,7 +99,7 @@ public class TestUtils { } public static boolean isJUnit4TestMethod(@Nullable PsiMethod method) { - return method != null && AnnotationUtil.isAnnotated(method, "org.junit.Test", true); + return method != null && AnnotationUtil.isAnnotated(method, JUnitCommonClassNames.ORG_JUNIT_TEST, true); } public static boolean isAnnotatedTestMethod(@Nullable PsiMethod method) { diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/testFrameworks/AssertHint.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/testFrameworks/AssertHint.java index 7dc6817de776..78705947df52 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/testFrameworks/AssertHint.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/testFrameworks/AssertHint.java @@ -78,8 +78,9 @@ public class AssertHint { if (minimumParamCount == null) { return null; } - final PsiMethod method = expression.resolveMethod(); - if (method == null || method.hasModifierProperty(PsiModifier.PRIVATE)) { + JavaResolveResult resolveResult = expression.resolveMethodGenerics(); + final PsiMethod method = (PsiMethod)resolveResult.getElement(); + if (method == null || method.hasModifierProperty(PsiModifier.PRIVATE) || !resolveResult.isValidResult()) { return null; } final boolean messageOnLastPosition = isMessageOnLastPosition(method, checkTestNG); diff --git a/plugins/InspectionGadgets/src/com/siyeh/ig/junit/JUnit5AssertionsConverterInspection.java b/plugins/InspectionGadgets/src/com/siyeh/ig/junit/JUnit5AssertionsConverterInspection.java index ed6bbad3bdf2..4594f347e1d6 100644 --- a/plugins/InspectionGadgets/src/com/siyeh/ig/junit/JUnit5AssertionsConverterInspection.java +++ b/plugins/InspectionGadgets/src/com/siyeh/ig/junit/JUnit5AssertionsConverterInspection.java @@ -35,6 +35,14 @@ import org.jetbrains.annotations.Nls; import org.jetbrains.annotations.NotNull; public class JUnit5AssertionsConverterInspection extends BaseInspection { + private String myFrameworkName = "JUnit5";; + + JUnit5AssertionsConverterInspection(String frameworkName) { + myFrameworkName = frameworkName; + } + + public JUnit5AssertionsConverterInspection() { + } @Override @NotNull @@ -61,7 +69,7 @@ public class JUnit5AssertionsConverterInspection extends BaseInspection { return new UseOfObsoleteAssertVisitor(); } - private static class UseOfObsoleteAssertVisitor extends BaseInspectionVisitor { + private class UseOfObsoleteAssertVisitor extends BaseInspectionVisitor { @Override public void visitMethodCallExpression(PsiMethodCallExpression expression) { @@ -97,7 +105,7 @@ public class JUnit5AssertionsConverterInspection extends BaseInspection { if (file instanceof PsiClassOwner) { for (PsiClass psiClass : ((PsiClassOwner)file).getClasses()) { TestFramework testFramework = TestFrameworks.detectFramework(psiClass); - if (testFramework != null && "JUnit5".equals(testFramework.getName())) { + if (testFramework != null && myFrameworkName.equals(testFramework.getName())) { String methodName = psiMethod.getName(); registerMethodCallError(expression, name, getNewAssertClassName(methodName), @@ -123,7 +131,7 @@ public class JUnit5AssertionsConverterInspection extends BaseInspection { } } - private static class ReplaceObsoleteAssertsFix extends InspectionGadgetsFix { + static class ReplaceObsoleteAssertsFix extends InspectionGadgetsFix { private final String myBaseClassName; public ReplaceObsoleteAssertsFix(String baseClassName) { diff --git a/plugins/InspectionGadgets/src/com/siyeh/ig/junit/JUnit5ConverterInspection.java b/plugins/InspectionGadgets/src/com/siyeh/ig/junit/JUnit5ConverterInspection.java new file mode 100644 index 000000000000..884f25f80ebf --- /dev/null +++ b/plugins/InspectionGadgets/src/com/siyeh/ig/junit/JUnit5ConverterInspection.java @@ -0,0 +1,187 @@ +/* + * 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. + */ +package com.siyeh.ig.junit; + +import com.intellij.codeInsight.AnnotationUtil; +import com.intellij.codeInsight.TestFrameworks; +import com.intellij.codeInspection.GlobalInspectionContext; +import com.intellij.codeInspection.InspectionEngine; +import com.intellij.codeInspection.InspectionManager; +import com.intellij.codeInspection.ProblemDescriptor; +import com.intellij.codeInspection.actions.CleanupInspectionIntention; +import com.intellij.codeInspection.ex.LocalInspectionToolWrapper; +import com.intellij.openapi.project.Project; +import com.intellij.psi.PsiClass; +import com.intellij.psi.PsiField; +import com.intellij.psi.PsiFile; +import com.intellij.psi.PsiMethod; +import com.intellij.psi.search.GlobalSearchScope; +import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.psi.util.PsiUtil; +import com.intellij.refactoring.RefactoringManager; +import com.intellij.refactoring.migration.MigrationManager; +import com.intellij.refactoring.migration.MigrationMap; +import com.intellij.refactoring.migration.MigrationProcessor; +import com.intellij.testIntegration.TestFramework; +import com.intellij.usageView.UsageInfo; +import com.intellij.util.ArrayUtil; +import com.siyeh.InspectionGadgetsBundle; +import com.siyeh.ig.BaseInspection; +import com.siyeh.ig.BaseInspectionVisitor; +import com.siyeh.ig.InspectionGadgetsFix; +import com.siyeh.ig.psiutils.TestUtils; +import org.jetbrains.annotations.Nls; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class JUnit5ConverterInspection extends BaseInspection { + private static final List ruleAnnotations = Arrays.asList(JUnitCommonClassNames.ORG_JUNIT_RULE, JUnitCommonClassNames.ORG_JUNIT_CLASS_RULE); + + @Nls + @NotNull + @Override + public String getDisplayName() { + return InspectionGadgetsBundle.message("junit5.converter.display.name"); + } + + @NotNull + @Override + protected String buildErrorString(Object... infos) { + return "#ref can be JUnit 5 test"; + } + + @Override + public boolean shouldInspect(PsiFile file) { + if (!PsiUtil.isLanguageLevel8OrHigher(file)) return false; + return super.shouldInspect(file); + } + + @Nullable + @Override + protected InspectionGadgetsFix buildFix(Object... infos) { + return new MigrateToJUnit5(); + } + + @Override + public BaseInspectionVisitor buildVisitor() { + return new BaseInspectionVisitor() { + + @Override + public void visitClass(PsiClass aClass) { + TestFramework framework = TestFrameworks.detectFramework(aClass); + if (framework == null || !"JUnit4".equals(framework.getName())) { + return; + } + + if (AnnotationUtil.isAnnotated(aClass, TestUtils.RUN_WITH, true)) { + return; + } + + for (PsiField field : aClass.getAllFields()) { + if (AnnotationUtil.isAnnotated(field, ruleAnnotations)) { + return; + } + } + + for (PsiMethod method : aClass.getMethods()) { + if (AnnotationUtil.isAnnotated(method, ruleAnnotations)) { + return; + } + } + + registerClassError(aClass); + } + }; + } + + private static class MigrateToJUnit5 extends InspectionGadgetsFix { + @Nls + @NotNull + @Override + public String getFamilyName() { + return InspectionGadgetsBundle.message("junit5.converter.fix.name"); + } + + @Override + protected void doFix(Project project, ProblemDescriptor descriptor) { + PsiClass psiClass = PsiTreeUtil.getParentOfType(descriptor.getPsiElement(), PsiClass.class); + if (psiClass != null) { + MigrationManager manager = RefactoringManager.getInstance(project).getMigrateManager(); + MigrationMap migrationMap = manager.findMigrationMap("JUnit (4.x -> 5.0)"); + if (migrationMap != null) { + new MyJUnit5MigrationProcessor(project, migrationMap, psiClass.getContainingFile()).run(); + } + } + } + + @Override + public boolean startInWriteAction() { + return false; + } + + private static class MyJUnit5MigrationProcessor extends MigrationProcessor { + + private final Project myProject; + private final PsiFile myFile; + + public MyJUnit5MigrationProcessor(Project project, MigrationMap migrationMap, PsiFile file) { + super(project, migrationMap, GlobalSearchScope.fileScope(file)); + myProject = project; + myFile = file; + } + + @NotNull + @Override + protected UsageInfo[] findUsages() { + UsageInfo[] usages = super.findUsages(); + InspectionManager inspectionManager = InspectionManager.getInstance(myProject); + GlobalInspectionContext globalContext = inspectionManager.createNewGlobalContext(false); + LocalInspectionToolWrapper assertionsConverter = new LocalInspectionToolWrapper(new JUnit5AssertionsConverterInspection("JUnit4")); + UsageInfo[] descriptors = InspectionEngine.runInspectionOnFile(myFile, assertionsConverter, globalContext).stream().map(descriptor -> new MyDescriptionBasedUsageInfo(descriptor)).toArray(UsageInfo[]::new); + return ArrayUtil.mergeArrays(usages, descriptors); + } + + @Override + protected void performRefactoring(@NotNull UsageInfo[] usages) { + List migrateUsages = new ArrayList<>(); + List descriptions = new ArrayList<>(); + for (UsageInfo usage : usages) { + if (usage instanceof MyDescriptionBasedUsageInfo) { + descriptions.add (((MyDescriptionBasedUsageInfo)usage).myDescriptor); + } + else { + migrateUsages.add(usage); + } + } + super.performRefactoring(migrateUsages.toArray(new UsageInfo[migrateUsages.size()])); + CleanupInspectionIntention.applyFixes(myProject, "Convert Assertions", descriptions, JUnit5AssertionsConverterInspection.ReplaceObsoleteAssertsFix.class); + } + } + } + + private static class MyDescriptionBasedUsageInfo extends UsageInfo { + private final ProblemDescriptor myDescriptor; + + public MyDescriptionBasedUsageInfo(ProblemDescriptor descriptor) { + super(descriptor.getPsiElement()); + myDescriptor = descriptor; + } + } +} diff --git a/plugins/InspectionGadgets/src/inspectionDescriptions/JUnit5Converter.html b/plugins/InspectionGadgets/src/inspectionDescriptions/JUnit5Converter.html new file mode 100644 index 000000000000..36fa589f9405 --- /dev/null +++ b/plugins/InspectionGadgets/src/inspectionDescriptions/JUnit5Converter.html @@ -0,0 +1,7 @@ + + +Reports JUnit4 tests which can be automatically converted to JUnit5. Though default runners are automatically convertable, custom runners, method- and field- rules are not and require manual changes. +

+ New in 2017.1 + + \ No newline at end of file diff --git a/plugins/InspectionGadgets/test/com/siyeh/igfixes/junit/junit5_converter/Simple.after.java b/plugins/InspectionGadgets/test/com/siyeh/igfixes/junit/junit5_converter/Simple.after.java new file mode 100644 index 000000000000..566d8facf130 --- /dev/null +++ b/plugins/InspectionGadgets/test/com/siyeh/igfixes/junit/junit5_converter/Simple.after.java @@ -0,0 +1,35 @@ +/* + * 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. + */ +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import static org.junit.Assert.*; + + +public class Simple { + + @Test + public void test_first() throws Exception { + Assertions.assertArrayEquals(new Object[] {}, null); + Assertions.assertArrayEquals(new Object[] {}, null, "message"); + + Assertions.assertEquals("Expected", "actual"); + Assertions.assertEquals("Expected", "actual", "message"); + + fail(); + Assertions.fail(""); + } +} diff --git a/plugins/InspectionGadgets/test/com/siyeh/igfixes/junit/junit5_converter/Simple.java b/plugins/InspectionGadgets/test/com/siyeh/igfixes/junit/junit5_converter/Simple.java new file mode 100644 index 000000000000..4f4b2b1a2978 --- /dev/null +++ b/plugins/InspectionGadgets/test/com/siyeh/igfixes/junit/junit5_converter/Simple.java @@ -0,0 +1,34 @@ +/* + * 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. + */ +import org.junit.Test; + +import static org.junit.Assert.*; + + +public class Simple { + + @Test + public void test_first() throws Exception { + assertArrayEquals(new Object[] {}, null); + assertArrayEquals("message", new Object[] {}, null); + + assertEquals("Expected", "actual"); + assertEquals("message", "Expected", "actual"); + + fail(); + fail(""); + } +} diff --git a/plugins/InspectionGadgets/testsrc/com/siyeh/ig/fixes/junit/Junit5ConverterFixTest.java b/plugins/InspectionGadgets/testsrc/com/siyeh/ig/fixes/junit/Junit5ConverterFixTest.java new file mode 100644 index 000000000000..7cd269ac7f44 --- /dev/null +++ b/plugins/InspectionGadgets/testsrc/com/siyeh/ig/fixes/junit/Junit5ConverterFixTest.java @@ -0,0 +1,64 @@ +/* + * 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. + */ +package com.siyeh.ig.fixes.junit; + +import com.siyeh.InspectionGadgetsBundle; +import com.siyeh.ig.IGQuickFixesTestCase; +import com.siyeh.ig.junit.JUnit5ConverterInspection; + +public class Junit5ConverterFixTest extends IGQuickFixesTestCase { + + public void testSimple() { + doTest(); + } + + @Override + protected void setUp() throws Exception { + super.setUp(); + myFixture.enableInspections(new JUnit5ConverterInspection()); + myRelativePath = "junit/junit5_converter"; + myDefaultHint = InspectionGadgetsBundle.message("junit5.converter.fix.name"); + + myFixture.addClass("package org.junit;" + + "public class Assert {" + + " public static void assertArrayEquals(Object[] expecteds, Object[] actuals) {}" + + " public static void assertArrayEquals(String message, Object[] expecteds, Object[] actuals){}" + + " public static void assertTrue(String message, boolean condition) {}" + + " public static void assertTrue(boolean condition) {}" + + " public static void assertEquals(String message, Object expected, Object actual) {}" + + " public static void assertEquals(Object expected, Object actual) {}" + + " public static void fail(String message) {}" + + " public static void assertThat(T actual, Matcher matcher) {}" + + "}"); + + myFixture.addClass("package org.junit;" + + "public @interface Test {}"); + + myFixture.addClass("package org.junit.jupiter.api;" + + "public @interface Test {}"); + + myFixture.addClass("package org.junit.jupiter.api;\n" + + "public final class Assertions {\n" + + " public static void assertArrayEquals(Object[] expected, Object[] actual) {}\n" + + " public static void assertArrayEquals(Object[] expected, Object[] actual, String message) {}\n" + + " public static void assertEquals(Object expected, Object actual) {}\n" + + " public static void assertTrue(boolean expected) {}\n" + + " public static void assertEquals(Object expected, Object actual, String message) {}\n" + + " public static void assertTrue(Object expected, String message) {}\n" + + " public static void fail(String message) {}" + + "}"); + } +} From b36db6c25ddd4abd62bf4a7081468f924e04c4cb Mon Sep 17 00:00:00 2001 From: Sergey Malenkov Date: Fri, 28 Oct 2016 16:40:06 +0300 Subject: [PATCH 33/36] Registry key to control building a project tree --- .../src/com/intellij/ide/projectView/impl/ProjectViewPane.java | 2 +- platform/util/resources/misc/registry.properties | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/platform/lang-impl/src/com/intellij/ide/projectView/impl/ProjectViewPane.java b/platform/lang-impl/src/com/intellij/ide/projectView/impl/ProjectViewPane.java index 880a52185b5a..c59caab90180 100644 --- a/platform/lang-impl/src/com/intellij/ide/projectView/impl/ProjectViewPane.java +++ b/platform/lang-impl/src/com/intellij/ide/projectView/impl/ProjectViewPane.java @@ -194,7 +194,7 @@ public class ProjectViewPane extends AbstractProjectViewPSIPane { @Override public boolean isToBuildChildrenInBackground(Object element) { - return true; + return Registry.is("ide.projectView.ProjectViewPaneTreeStructure.BuildChildrenInBackground"); } } diff --git a/platform/util/resources/misc/registry.properties b/platform/util/resources/misc/registry.properties index aeed23650bd0..f6625480b3c9 100644 --- a/platform/util/resources/misc/registry.properties +++ b/platform/util/resources/misc/registry.properties @@ -888,6 +888,9 @@ show.configurables.ids.in.settings.description=Show configurables ids in setting ide.projectView.globalOptions=true ide.projectView.globalOptions.description=Make Project View options such as autoscroll to/from source, etc global per panes and per projects. For details see https://youtrack.jetbrains.com/issue/IDEA-85255 +ide.projectView.ProjectViewPaneTreeStructure.BuildChildrenInBackground=true +ide.projectView.ProjectViewPaneTreeStructure.BuildChildrenInBackground.description=Temporary ability to control a tree building for the Project View pane + compiler.ref.index=false compiler.ref.index.description=Enables find usages using references from compiler indices compiler.ref.index.restartRequired=true From 3a715162b841c1d331547b977be20b1835fa888e Mon Sep 17 00:00:00 2001 From: Vladimir Krivosheev Date: Fri, 28 Oct 2016 15:43:40 +0200 Subject: [PATCH 34/36] IDEA-CR-15134 use val --- .../breadcrumbs/BreadcrumbsInitializingActivity.java | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/xml/impl/src/com/intellij/xml/breadcrumbs/BreadcrumbsInitializingActivity.java b/xml/impl/src/com/intellij/xml/breadcrumbs/BreadcrumbsInitializingActivity.java index ab8e3ae08b98..eb6ce5d23a0c 100644 --- a/xml/impl/src/com/intellij/xml/breadcrumbs/BreadcrumbsInitializingActivity.java +++ b/xml/impl/src/com/intellij/xml/breadcrumbs/BreadcrumbsInitializingActivity.java @@ -123,21 +123,20 @@ public class BreadcrumbsInitializingActivity implements StartupActivity, DumbAwa for (final FileEditor fileEditor : fileEditors) { if (fileEditor instanceof TextEditor) { Editor editor = ((TextEditor)fileEditor).getEditor(); - BreadcrumbsXmlWrapper wrapper = BreadcrumbsXmlWrapper.getBreadcrumbsComponent(editor); - if (wrapper != null) { - wrapper.queueUpdate(); + final BreadcrumbsXmlWrapper existingWrapper = BreadcrumbsXmlWrapper.getBreadcrumbsComponent(editor); + if (existingWrapper != null) { + existingWrapper.queueUpdate(); continue; } - wrapper = new BreadcrumbsXmlWrapper(editor); + final BreadcrumbsXmlWrapper wrapper = new BreadcrumbsXmlWrapper(editor); final JComponent c = wrapper.getComponent(); fileEditorManager.addTopComponent(fileEditor, c); - BreadcrumbsXmlWrapper finalWrapper = wrapper; Disposer.register(fileEditor, new Disposable() { @Override public void dispose() { - disposeWrapper(fileEditorManager, fileEditor, finalWrapper); + disposeWrapper(fileEditorManager, fileEditor, wrapper); } }); } From 0340f0c9bfc69a50e59352e928ce993d29705910 Mon Sep 17 00:00:00 2001 From: Anton Tarasov Date: Fri, 28 Oct 2016 16:44:56 +0300 Subject: [PATCH 35/36] [followup] Add/refactor JBUI.JBIcon/ScalableJBIcon/ValidatingScalableJBIcon --- .../src/com/intellij/ui/LayeredIcon.java | 13 ++--- .../core-api/src/com/intellij/ui/RowIcon.java | 8 +-- .../src/com/intellij/ui/SizedIcon.java | 7 ++- .../com/intellij/openapi/util/IconLoader.java | 8 ++- .../intellij/openapi/util/ScalableIcon.java | 5 ++ .../src/com/intellij/util/ui/ColorIcon.java | 12 ++++ .../src/com/intellij/util/ui/EmptyIcon.java | 55 ++++++------------- .../util/src/com/intellij/util/ui/JBUI.java | 46 +++++++++++++--- .../com/intellij/util/ui/TwoColorsIcon.java | 10 ++++ .../filter/StructureFilterPopupComponent.java | 6 +- .../propertyTable/renderers/ColorIcon.java | 12 ++++ .../com/intellij/xml/util/ColorIconCache.java | 12 ++++ 12 files changed, 131 insertions(+), 63 deletions(-) diff --git a/platform/core-api/src/com/intellij/ui/LayeredIcon.java b/platform/core-api/src/com/intellij/ui/LayeredIcon.java index 1e73d2cc615e..7e6d84fc18e4 100644 --- a/platform/core-api/src/com/intellij/ui/LayeredIcon.java +++ b/platform/core-api/src/com/intellij/ui/LayeredIcon.java @@ -54,8 +54,8 @@ public class LayeredIcon extends JBUI.AuxScalableJBIcon { } @Override - public LayeredIcon withPreScaled(boolean preScaled) { - super.withPreScaled(preScaled); + public LayeredIcon withJBUIPreScaled(boolean preScaled) { + super.withJBUIPreScaled(preScaled); for (int i = 0; i < myIcons.length; i++) { myHShifts[i] = scaleVal(myHShifts[i], Scale.JBUI); myVShifts[i] = scaleVal(myVShifts[i], Scale.JBUI); @@ -271,11 +271,10 @@ public class LayeredIcon extends JBUI.AuxScalableJBIcon { @Override public Icon scale(float scale) { - if (getScale() != scale) { - super.scale(scale); - if (myScaledIcons!= null) Arrays.fill(myScaledIcons, null); - updateSize(); - } + if (scale == 1f) return this; + + setScale(scaleVal(scale, Scale.ARBITRARY)); + if (myScaledIcons != null) Arrays.fill(myScaledIcons, null); return this; } } diff --git a/platform/core-api/src/com/intellij/ui/RowIcon.java b/platform/core-api/src/com/intellij/ui/RowIcon.java index 0d9ebe1ca83c..173d3b847f3f 100644 --- a/platform/core-api/src/com/intellij/ui/RowIcon.java +++ b/platform/core-api/src/com/intellij/ui/RowIcon.java @@ -57,10 +57,10 @@ public class RowIcon extends JBUI.AuxScalableJBIcon { @Override public Icon scale(float scale) { - if (getScale() != scale || (getScale() != 1f && myScaledIcons == null)) { - super.scale(scale); - rescale(); - } + if (scale == 1f) return this; + + setScale(scaleVal(scale, Scale.ARBITRARY)); + rescale(); return this; } diff --git a/platform/platform-api/src/com/intellij/ui/SizedIcon.java b/platform/platform-api/src/com/intellij/ui/SizedIcon.java index 9547ab1c6a59..3d8e176a983b 100644 --- a/platform/platform-api/src/com/intellij/ui/SizedIcon.java +++ b/platform/platform-api/src/com/intellij/ui/SizedIcon.java @@ -58,9 +58,12 @@ public class SizedIcon extends JBUI.ScalableJBIcon { @Override public Icon scale(float scale) { + if (scale == 1f) return this; + if (myDelegate instanceof ScalableIcon) { - myScaledDelegate = ((ScalableIcon)myDelegate).scale(scale); + setScale(scaleVal(scale, Scale.ARBITRARY)); + myScaledDelegate = ((ScalableIcon)myDelegate).scale(getScale()); } - return super.scale(scale); + return this; } } diff --git a/platform/util/src/com/intellij/openapi/util/IconLoader.java b/platform/util/src/com/intellij/openapi/util/IconLoader.java index 40ac1fa8e905..2097900d4b02 100644 --- a/platform/util/src/com/intellij/openapi/util/IconLoader.java +++ b/platform/util/src/com/intellij/openapi/util/IconLoader.java @@ -457,6 +457,8 @@ public final class IconLoader { @Override public Icon scale(float scale) { + if (scale == 1f) return this; + if (!isValid()) getRealIcon(); // force state update & cache reset Icon icon = myScaledIconsCache.getScaledIcon(scale); @@ -578,10 +580,12 @@ public final class IconLoader { @Override public Icon scale(float scale) { + if (scale == 1f) return this; + getOrComputeIcon(); if (myIcon instanceof ScalableIcon) { - myIcon = ((ScalableIcon)myIcon).scale(scale); - return super.scale(scale); + setScale(scaleVal(scale, Scale.ARBITRARY)); + myIcon = ((ScalableIcon)myIcon).scale(getScale()); } return this; } diff --git a/platform/util/src/com/intellij/openapi/util/ScalableIcon.java b/platform/util/src/com/intellij/openapi/util/ScalableIcon.java index 93eea6d40c65..86e1e8ce53cb 100644 --- a/platform/util/src/com/intellij/openapi/util/ScalableIcon.java +++ b/platform/util/src/com/intellij/openapi/util/ScalableIcon.java @@ -23,6 +23,11 @@ import javax.swing.*; */ public interface ScalableIcon extends Icon { /** + * The method does either of the following: + * - creates/retrieves a scaled version of this icon and returns it. + * - scales this icon and returns it. + * The exact behavior is implementation dependant. + * * @param scaleFactor scale * @return scaled icon with width getIconWidth() * scaleFactor and height getIconHeight() * scaleFactor */ diff --git a/platform/util/src/com/intellij/util/ui/ColorIcon.java b/platform/util/src/com/intellij/util/ui/ColorIcon.java index 48f15a49a66d..a93581264859 100644 --- a/platform/util/src/com/intellij/util/ui/ColorIcon.java +++ b/platform/util/src/com/intellij/util/ui/ColorIcon.java @@ -43,6 +43,18 @@ public class ColorIcon extends EmptyIcon { this(size, color, false); } + protected ColorIcon(ColorIcon icon) { + super(icon); + myColor = icon.myColor; + myBorder = icon.myBorder; + myColorSize = icon.myColorSize; + } + + @Override + protected ColorIcon copy() { + return new ColorIcon(this); + } + public Color getIconColor() { return myColor; } diff --git a/platform/util/src/com/intellij/util/ui/EmptyIcon.java b/platform/util/src/com/intellij/util/ui/EmptyIcon.java index 211127b5b86c..29aaa9dd28fd 100644 --- a/platform/util/src/com/intellij/util/ui/EmptyIcon.java +++ b/platform/util/src/com/intellij/util/ui/EmptyIcon.java @@ -16,7 +16,6 @@ package com.intellij.util.ui; -import com.intellij.openapi.util.Pair; import org.jetbrains.annotations.NotNull; import javax.swing.*; @@ -32,9 +31,8 @@ import java.util.Map; * * @see ColorIcon */ -public class EmptyIcon extends JBUI.ScalableJBIcon { - private static final Map, EmptyIcon> cache = - new HashMap, EmptyIcon>(); // (size, preScaled) -> (icon) +public class EmptyIcon extends JBUI.CachingScalableJBIcon { + private static final Map cache = new HashMap(); public static final Icon ICON_16 = JBUI.scale(create(16)); public static final Icon ICON_18 = JBUI.scale(create(18)); @@ -43,13 +41,12 @@ public class EmptyIcon extends JBUI.ScalableJBIcon { protected final int width; protected final int height; - private EmptyIcon myScaledCache; private boolean myUseCache; /** * Creates an icon of the provided size. * - * Use {@link JBUI#scale(EmptyIcon)} to meet HiDPI. + * Use {@link JBUI#scale(JBUI.JBIcon)} to meet HiDPI. */ public static EmptyIcon create(int size) { return create(size, size); @@ -58,7 +55,7 @@ public class EmptyIcon extends JBUI.ScalableJBIcon { /** * Creates an icon of the provided size. * - * Use {@link JBUI#scale(EmptyIcon)} to meet HiDPI. + * Use {@link JBUI#scale(JBUI.JBIcon)} to meet HiDPI. */ public static EmptyIcon create(int width, int height) { return create(width, height, true); @@ -98,39 +95,34 @@ public class EmptyIcon extends JBUI.ScalableJBIcon { myUseCache = icon.myUseCache; } + @Override protected EmptyIcon copy() { return new EmptyIcon(this); } @Override - public EmptyIcon withPreScaled(boolean preScaled) { - if (myUseCache && isPreScaled() != preScaled) { - Pair key = key(width, height, preScaled); + public EmptyIcon withJBUIPreScaled(boolean preScaled) { + if (myUseCache && isJBUIPreScaled() != preScaled) { + Integer key = key(width, height, isJBUIPreScaled()); if (key != null) cache.remove(key); // rather useless to keep it in cache return create(width, height, preScaled); } - return (EmptyIcon)super.withPreScaled(preScaled); + return (EmptyIcon)super.withJBUIPreScaled(preScaled); } private static EmptyIcon create(int width, int height, boolean preScaled) { - Pair key = key(width, height, preScaled); + Integer key = key(width, height, preScaled); EmptyIcon icon = (key != null) ? cache.get(key) : null; if (icon == null) { icon = new EmptyIcon(width, height, true); - icon.setPreScaled(preScaled); + icon.setJBUIPreScaled(preScaled); if (key != null) cache.put(key, icon); } return icon; } - private static Pair key(int width, int height, boolean preScaled) { - return (width == height && width < 129) ? Pair.create(width, preScaled) : null; - } - - private boolean isCached() { - if (!myUseCache) return false; - Pair key = key(width, height, isPreScaled()); - return key != null && cache.containsKey(key); + private static Integer key(int width, int height, boolean preScaled) { + return (width == height && width < 129) ? preScaled ? width : JBUI.scale(width) : null; } @Override @@ -154,20 +146,18 @@ public class EmptyIcon extends JBUI.ScalableJBIcon { final EmptyIcon icon = (EmptyIcon)o; - if (height != icon.height) return false; - if (width != icon.width) return false; + if (scaleVal(height, Scale.JBUI) != icon.scaleVal(height, Scale.JBUI)) return false; + if (scaleVal(width, Scale.JBUI) != icon.scaleVal(width, Scale.JBUI)) return false; if (getScale() != icon.getScale()) return false; - if (isPreScaled() != icon.isPreScaled()) return false; return true; } @Override public int hashCode() { - int result = width; - result = 31 * result + height; + int result = scaleVal(width, Scale.JBUI); + result = 31 * result + scaleVal(height, Scale.JBUI); result = 31 * result + (getScale() != +0.0f ? Float.floatToIntBits(getScale()) : 0); - result = 31 * result + Boolean.valueOf(isPreScaled()).hashCode(); return result; } @@ -175,17 +165,6 @@ public class EmptyIcon extends JBUI.ScalableJBIcon { return new EmptyIconUIResource(this); } - @Override - public EmptyIcon scale(float scale) { - if (getScale() == scale || !isCached()) { - return (EmptyIcon)super.scale(scale); - } - if (myScaledCache == null || myScaledCache.getScale() != scale) { - myScaledCache = copy(); // never cached - } - return myScaledCache.scale(scale); - } - public static class EmptyIconUIResource extends EmptyIcon implements UIResource { protected EmptyIconUIResource(EmptyIcon icon) { super(icon); diff --git a/platform/util/src/com/intellij/util/ui/JBUI.java b/platform/util/src/com/intellij/util/ui/JBUI.java index e5ef636030ad..84b183b30221 100644 --- a/platform/util/src/com/intellij/util/ui/JBUI.java +++ b/platform/util/src/com/intellij/util/ui/JBUI.java @@ -166,7 +166,7 @@ public class JBUI { } public static T scale(T icon) { - return (T)icon.withPreScaled(false); + return (T)icon.withJBUIPreScaled(false); } public static JBDimension emptySize() { @@ -309,14 +309,14 @@ public class JBUI { /** * @return whether the icon size metrics are pre-scaled or not */ - protected boolean isPreScaled() { + protected boolean isJBUIPreScaled() { return myInitialJBUIScale != 1f; } /** * Sets the icon size metrics to {@code preScaled} */ - protected void setPreScaled(boolean preScaled) { + protected void setJBUIPreScaled(boolean preScaled) { myInitialJBUIScale = preScaled ? scale(1f) : 1f; } @@ -325,8 +325,8 @@ public class JBUI { * * @return the icon (this or new instance) with size metrics set to {@code preScaled} */ - public JBIcon withPreScaled(boolean preScaled) { - setPreScaled(preScaled); + public JBIcon withJBUIPreScaled(boolean preScaled) { + setJBUIPreScaled(preScaled); return this; } @@ -399,12 +399,44 @@ public class JBUI { return super.scaleVal(value * myScale); } } + } + /** + * A ScalableJBIcon providing an immutable caching implementation of the {@link #scale(float)} method. + * + * @author tav + * @author Aleksey Pivovarov + */ + public static abstract class CachingScalableJBIcon extends ScalableJBIcon { + private CachingScalableJBIcon myScaledIconCache; + + protected CachingScalableJBIcon() {} + + protected CachingScalableJBIcon(CachingScalableJBIcon icon) { + super(icon); + myScaledIconCache = null; + } + + /** + * @return a new scaled copy of this icon, or the cached instance of the provided scale + */ @Override public Icon scale(float scale) { - if (getScale() != scale) setScale(scale); - return this; + if (scale == 1f) return this; + + scale = scaleVal(scale, Scale.ARBITRARY); // accumulate scale + if (myScaledIconCache == null || myScaledIconCache.getScale() != scale) { + myScaledIconCache = copy(); + myScaledIconCache.setScale(scale); + } + return myScaledIconCache; } + + /** + * @return a deep copy of this icon instance + */ + @NotNull + protected abstract T copy(); } public interface AuxJBUIScale { diff --git a/platform/util/src/com/intellij/util/ui/TwoColorsIcon.java b/platform/util/src/com/intellij/util/ui/TwoColorsIcon.java index d1e3dd516f38..ca5ff1bc708e 100644 --- a/platform/util/src/com/intellij/util/ui/TwoColorsIcon.java +++ b/platform/util/src/com/intellij/util/ui/TwoColorsIcon.java @@ -51,6 +51,16 @@ public class TwoColorsIcon extends ColorIcon { mySecondColor = secondColor != null ? secondColor : Gray.TRANSPARENT; } + protected TwoColorsIcon(TwoColorsIcon icon) { + super(icon); + mySecondColor = icon.mySecondColor; + } + + @Override + protected TwoColorsIcon copy() { + return new TwoColorsIcon(this); + } + @Override public void paintIcon(final Component component, Graphics g, int x, int y) { Graphics2D g2d = (Graphics2D)g.create(); diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/filter/StructureFilterPopupComponent.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/filter/StructureFilterPopupComponent.java index 3682f1eb6b65..7ab4212cf0ab 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/filter/StructureFilterPopupComponent.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/filter/StructureFilterPopupComponent.java @@ -331,9 +331,9 @@ class StructureFilterPopupComponent extends FilterPopupComponent Date: Fri, 28 Oct 2016 16:50:10 +0300 Subject: [PATCH 36/36] IDEA-161501 ui: fix indeterminate progress rendering under HiDPI follow-up: a895bbe --- .../ide/ui/laf/darcula/ui/DarculaProgressBarUI.java | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/platform/platform-impl/src/com/intellij/ide/ui/laf/darcula/ui/DarculaProgressBarUI.java b/platform/platform-impl/src/com/intellij/ide/ui/laf/darcula/ui/DarculaProgressBarUI.java index e191f809509b..8c0c6b1931e5 100644 --- a/platform/platform-impl/src/com/intellij/ide/ui/laf/darcula/ui/DarculaProgressBarUI.java +++ b/platform/platform-impl/src/com/intellij/ide/ui/laf/darcula/ui/DarculaProgressBarUI.java @@ -71,7 +71,9 @@ public class DarculaProgressBarUI extends BasicProgressBarUI { final float R2 = JBUI.scale(9f); final float off = JBUI.scale(1f); + final Area innerBorderRoundRect = new Area(new RoundRectangle2D.Float(off, off, w - 2f * off, h - 2f * off, R, R)); final Area containingRoundRect = new Area(new RoundRectangle2D.Float(2f * off, 2f * off, w - 4f * off, h - 4f * off, R, R)); + while (x < Math.max(c.getWidth(), c.getHeight())) { Path2D.Double path = new Path2D.Double(); float ww = getPeriodLength() / 2f; @@ -89,7 +91,7 @@ public class DarculaProgressBarUI extends BasicProgressBarUI { } offset = (offset + 1) % getPeriodLength(); Area area = new Area(new Rectangle2D.Float(0, 0, w, h)); - area.subtract(new Area(new RoundRectangle2D.Float(off, off, w - 2f * off, h - 2f * off, R, R))); + area.subtract(innerBorderRoundRect); g.setColor(Gray._128); if (c.isOpaque()) { g.fill(area); @@ -99,7 +101,11 @@ public class DarculaProgressBarUI extends BasicProgressBarUI { if (c.isOpaque()) { g.fill(area); } - g.draw(new RoundRectangle2D.Float(off, off, w - 2f * off - off, h - 2f * off - off, R, R)); + + Area insetArea = new Area(innerBorderRoundRect); + insetArea.subtract(containingRoundRect); + g.fill(insetArea); + g.translate(0, -(c.getHeight() - h) / 2); // Deal with possible text painting