mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
Merge remote-tracking branch 'origin/master'
This commit is contained in:
@@ -2,8 +2,8 @@
|
||||
<version codename="Community Edition" major="2017" minor="1" eap="true"/>
|
||||
<company name="JetBrains s.r.o." url="https://www.jetbrains.com/?fromIDE"/>
|
||||
<build number="__BUILD_NUMBER__" date="__BUILD_DATE__"/>
|
||||
<logo url="/idea_community_logo.png" textcolor="444444" progressColor="f87206" progressX="0" progressY="271" progressHeight="3" licenseOffsetY="30" />
|
||||
<about url="/idea_community_about.png" foreground="444444" copyrightForeground="7A858F" linkColor="9b5121" logoX="300" logoY="265" logoW="75" logoH="30"/>
|
||||
<logo url="/idea_community_logo.png" textcolor="dddddd" progressColor="f87206" progressX="117" progressY="280" progressHeight="3" />
|
||||
<about url="/idea_community_about.png" foreground="000000" copyrightForeground="7A858F" linkColor="9b5121" logoX="300" logoY="265" logoW="75" logoH="30"/>
|
||||
<icon size32="/icon_CE.png" size16="/icon_CEsmall.png" size128="/icon_CE_128.png" ico="idea_CE.ico"/>
|
||||
<package code="__PACKAGE_CODE__"/>
|
||||
<names product="IDEA" fullname="IntelliJ IDEA" script="idea"/>
|
||||
|
||||
+1
-1
@@ -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;
|
||||
|
||||
+4
-2
@@ -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
|
||||
|
||||
@@ -15,16 +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.*;
|
||||
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 java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
|
||||
@@ -32,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<Module> myChangedModules = ContainerUtil.newHashSet();
|
||||
private final Set<Module> 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() {
|
||||
@@ -60,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<Module> 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) {
|
||||
@@ -117,20 +140,28 @@ class DirtyModulesHolder extends UserDataHolderBase {
|
||||
}
|
||||
|
||||
void fileChanged(VirtualFile file) {
|
||||
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);
|
||||
}
|
||||
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<Module> modules, GlobalSearchScope baseScope) {
|
||||
return modules.stream().map(Module::getModuleWithDependentsScope).reduce(baseScope, (s1, s2) -> s1.union(s2));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -156,33 +156,35 @@ public class MethodBreakpoint extends BreakpointWithHighlighter<JavaMethodBreakp
|
||||
for (Method method : classType.methods()) {
|
||||
if (getMethodName().equals(method.name()) && mySignature.getName(debugProcess).equals(method.signature())) {
|
||||
List<Location> 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
|
||||
@@ -453,7 +455,7 @@ public class MethodBreakpoint extends BreakpointWithHighlighter<JavaMethodBreakp
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean isEmulated() {
|
||||
public boolean isEmulated() {
|
||||
return getProperties().EMULATED && Registry.is("debugger.emulate.method.breakpoints");
|
||||
}
|
||||
|
||||
@@ -508,10 +510,6 @@ public class MethodBreakpoint extends BreakpointWithHighlighter<JavaMethodBreakp
|
||||
private static void processSubTypes(ReferenceType classType, Consumer<ReferenceType> consumer) {
|
||||
MultiMap<ReferenceType, ReferenceType> inheritance = new MultiMap<>();
|
||||
classType.virtualMachine().allClasses().forEach(type -> supertypes(type).forEach(st -> inheritance.putValue(st, type)));
|
||||
subtypes(classType, inheritance).forEach(consumer);
|
||||
}
|
||||
|
||||
private static Stream<ReferenceType> subtypes(ReferenceType type, MultiMap<ReferenceType, ReferenceType> 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);
|
||||
}
|
||||
}
|
||||
|
||||
+5
-13
@@ -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<PreContract> preContracts = methodNode == null || bodyNode == null ? Collections.emptyList() :
|
||||
new ContractInferenceInterpreter(tree, TreeBackedLighterAST.wrap(methodNode), TreeBackedLighterAST.wrap(bodyNode)).inferContracts();
|
||||
List<MethodContract> result = RecursionManager.doPreventingRecursion(method, true, () -> postProcessContracts(method, body, preContracts));
|
||||
MethodData data = ContractInferenceIndexKt.getIndexedData(method);
|
||||
List<PreContract> preContracts = data == null ? Collections.emptyList() : data.getContracts();
|
||||
List<MethodContract> 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<MethodContract> postProcessContracts(@NotNull PsiMethod method, @NotNull PsiCodeBlock body, List<PreContract> rawContracts) {
|
||||
List<MethodContract> contracts = ContainerUtil.concat(rawContracts, c -> c.toContracts(method, body));
|
||||
private static List<MethodContract> postProcessContracts(@NotNull PsiMethod method, MethodData data, List<PreContract> rawContracts) {
|
||||
List<MethodContract> contracts = ContainerUtil.concat(rawContracts, c -> c.toContracts(method, data.methodBody(method)));
|
||||
if (contracts.isEmpty()) return Collections.emptyList();
|
||||
|
||||
final PsiType returnType = method.getReturnType();
|
||||
|
||||
+137
@@ -0,0 +1,137 @@
|
||||
/*
|
||||
* 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.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<Int, MethodData>("java.inferred.contracts")
|
||||
|
||||
class ContractInferenceIndex : FileBasedIndexExtension<Int, MethodData>(), PsiDependentIndex {
|
||||
override fun getName() = INDEX_ID
|
||||
override fun getVersion() = 0
|
||||
override fun dependsOnFileContent() = true
|
||||
override fun getKeyDescriptor() = IntInlineKeyDescriptor()
|
||||
override fun getValueExternalizer(): DataExternalizer<MethodData> = MethodDataExternalizer
|
||||
|
||||
override fun getInputFilter() = FileBasedIndex.InputFilter {
|
||||
it.fileType == JavaFileType.INSTANCE && JavaFileElementType.isInSourceContent(it)
|
||||
}
|
||||
|
||||
override fun getIndexer() = DataIndexer<Int, MethodData, FileContent> { fc ->
|
||||
val result = HashMap<Int, MethodData>()
|
||||
|
||||
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, CODE_BLOCK) ?: return null
|
||||
val statements = ContractInferenceInterpreter.getStatements(body, tree)
|
||||
|
||||
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<PreContract>,
|
||||
nullity: NullityInferenceResult?,
|
||||
purity: PurityInferenceResult?): MethodData? {
|
||||
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<MethodData>()
|
||||
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)
|
||||
}
|
||||
}
|
||||
+25
-23
@@ -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;
|
||||
|
||||
@@ -36,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;
|
||||
@@ -54,25 +56,26 @@ class ContractInferenceInterpreter {
|
||||
return paramList != null ? getChildrenOfType(myTree, paramList, PARAMETER) : Collections.emptyList();
|
||||
}
|
||||
|
||||
List<PreContract> inferContracts() {
|
||||
LighterASTNode[] statements = getStatements(myBody);
|
||||
if (statements.length == 0) return Collections.emptyList();
|
||||
@NotNull
|
||||
List<PreContract> inferContracts(List<LighterASTNode> statements) {
|
||||
if (statements.isEmpty()) return Collections.emptyList();
|
||||
|
||||
if (statements.length == 1) {
|
||||
if (statements[0].getTokenType() == RETURN_STATEMENT) {
|
||||
List<PreContract> result = handleDelegation(findExpressionChild(myTree, statements[0]), false);
|
||||
if (statements.size() == 1) {
|
||||
LighterASTNode statement = statements.get(0);
|
||||
if (statement.getTokenType() == RETURN_STATEMENT) {
|
||||
List<PreContract> 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<PreContract> 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
|
||||
@@ -81,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<LighterASTNode> getStatements(@Nullable LighterASTNode codeBlock, LighterAST tree) {
|
||||
return codeBlock == null ? Collections.emptyList() : getChildrenOfType(tree, codeBlock, ElementType.JAVA_STATEMENT_BIT_SET);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -99,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;
|
||||
@@ -153,7 +154,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 singletonList(new MethodCallContract(ExpressionRange.create(expr, myBody.getStartOffset()),
|
||||
ContainerUtil.map(states, Arrays::asList)));
|
||||
}
|
||||
|
||||
final ValueConstraint constraint = getLiteralConstraint(expr);
|
||||
@@ -297,24 +299,24 @@ class ContractInferenceInterpreter {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private List<PreContract> visitStatements(List<ValueConstraint[]> states, LighterASTNode... statements) {
|
||||
private List<PreContract> visitStatements(List<ValueConstraint[]> states, List<LighterASTNode> 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<PreContract> conditionResults = visitExpression(states, findExpressionChild(myTree, statement));
|
||||
|
||||
LighterASTNode[] thenElse = getStatements(statement);
|
||||
if (thenElse.length > 0) {
|
||||
result.addAll(visitStatements(antecedentsReturning(conditionResults, TRUE_VALUE), thenElse[0]));
|
||||
List<LighterASTNode> thenElse = getStatements(statement, myTree);
|
||||
if (thenElse.size() > 0) {
|
||||
result.addAll(visitStatements(antecedentsReturning(conditionResults, TRUE_VALUE), singletonList(thenElse.get(0))));
|
||||
}
|
||||
|
||||
List<ValueConstraint[]> 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;
|
||||
@@ -339,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'
|
||||
|
||||
+1
-2
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
+114
@@ -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<MethodData> {
|
||||
|
||||
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<ExpressionRange>) = 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<MethodContract.ValueConstraint>) =
|
||||
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 <T> writeNullable(out: DataOutput, value: T?, writeItem: (T) -> Unit) = when (value) {
|
||||
null -> out.writeBoolean(false)
|
||||
else -> { out.writeBoolean(true); writeItem(value) }
|
||||
}
|
||||
private fun <T> readNullable(input: DataInput, readEach: () -> T): T? = if (input.readBoolean()) readEach() else null
|
||||
|
||||
private fun <T> writeList(out: DataOutput, list: List<T>, writeEach: (T) -> Unit) {
|
||||
DataInputOutputUtil.writeINT(out, list.size)
|
||||
list.forEach(writeEach)
|
||||
}
|
||||
private fun <T> readList(input: DataInput, readEach: () -> T) = (0 until DataInputOutputUtil.readINT(input)).map { readEach() }
|
||||
+78
-83
@@ -15,25 +15,21 @@
|
||||
*/
|
||||
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;
|
||||
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.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;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import static com.intellij.psi.impl.source.tree.JavaElementType.*;
|
||||
|
||||
@@ -53,93 +49,92 @@ 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) {
|
||||
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<String, ExpressionRange> 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<LighterASTNode> 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<LighterASTNode> 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(delegates.get(delegates.keySet().iterator().next()));
|
||||
}
|
||||
|
||||
if (hasNotNulls.get()) {
|
||||
return new NullityInferenceResult.Predefined(Nullness.NOT_NULL);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
+44
-50
@@ -15,23 +15,17 @@
|
||||
*/
|
||||
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;
|
||||
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;
|
||||
import com.intellij.psi.util.PropertyUtil;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@@ -49,64 +43,64 @@ 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) {
|
||||
List<LighterASTNode> mutatedRefs = new ArrayList<>();
|
||||
Ref<Boolean> hasReturns = Ref.create(false);
|
||||
List<LighterASTNode> calls = new ArrayList<>();
|
||||
new RecursiveLighterASTNodeWalkingVisitor(tree) {
|
||||
@Override
|
||||
public void visitNode(@NotNull LighterASTNode element) {
|
||||
IElementType type = element.getTokenType();
|
||||
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);
|
||||
}
|
||||
static class PurityInferenceVisitor {
|
||||
private final LighterAST tree;
|
||||
private final LighterASTNode body;
|
||||
private List<LighterASTNode> mutatedRefs = new ArrayList<>();
|
||||
private boolean hasReturns;
|
||||
private List<LighterASTNode> calls = new ArrayList<>();
|
||||
|
||||
super.visitNode(element);
|
||||
PurityInferenceVisitor(LighterAST tree, LighterASTNode body) {
|
||||
this.tree = tree;
|
||||
this.body = body;
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+38
-15
@@ -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<ExpressionRange>, private val singleCall: ExpressionRange?) {
|
||||
data class PurityInferenceResult(internal val mutatedRefs: List<ExpressionRange>, 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<ExpressionRange>,
|
||||
|
||||
|
||||
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<ExpressionRange>) : 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<ExpressionRange>) : 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<PreContract>,
|
||||
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)
|
||||
}
|
||||
}
|
||||
@@ -27,19 +27,19 @@ import com.siyeh.ig.psiutils.SideEffectChecker
|
||||
* @author peter
|
||||
*/
|
||||
interface PreContract {
|
||||
fun toContracts(method: PsiMethod, body: PsiCodeBlock): List<MethodContract>
|
||||
fun toContracts(method: PsiMethod, body: () -> PsiCodeBlock): List<MethodContract>
|
||||
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<MethodContract> {
|
||||
val call = expression.restoreExpression(body) as PsiMethodCallExpression? ?: return emptyList()
|
||||
override fun toContracts(method: PsiMethod, body: () -> PsiCodeBlock): List<MethodContract> {
|
||||
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<ExpressionRange>, private val contracts: List<PreContract>) : PreContract {
|
||||
internal data class SideEffectFilter(internal val expressionsToCheck: List<ExpressionRange>, internal val contracts: List<PreContract>) : PreContract {
|
||||
|
||||
override fun toContracts(method: PsiMethod, body: PsiCodeBlock): List<MethodContract> {
|
||||
if (expressionsToCheck.any { d -> mayHaveSideEffects(body, d) }) {
|
||||
override fun toContracts(method: PsiMethod, body: () -> PsiCodeBlock): List<MethodContract> {
|
||||
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<Expres
|
||||
range.restoreExpression(body)?.let { SideEffectChecker.mayHaveSideEffects(it) } ?: false
|
||||
}
|
||||
|
||||
internal data class NegatingContract(private val negated: PreContract) : PreContract {
|
||||
override fun toContracts(method: PsiMethod, body: PsiCodeBlock) = negated.toContracts(method, body).mapNotNull(::negateContract)
|
||||
internal data class NegatingContract(internal val negated: PreContract) : PreContract {
|
||||
override fun toContracts(method: PsiMethod, body: () -> 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<Array<MethodContract.ValueConstraint>>) : PreContract {
|
||||
@Suppress("EqualsOrHashCode")
|
||||
internal data class MethodCallContract(internal val call: ExpressionRange, internal val states: List<List<MethodContract.ValueConstraint>>) : PreContract {
|
||||
override fun hashCode() = call.hashCode() * 31 + states.flatten().map { it.ordinal }.hashCode()
|
||||
|
||||
override fun toContracts(method: PsiMethod, body: PsiCodeBlock): List<MethodContract> {
|
||||
val target = (call.restoreExpression(body) as PsiMethodCallExpression?)?.resolveMethod()
|
||||
override fun toContracts(method: PsiMethod, body: () -> PsiCodeBlock): List<MethodContract> {
|
||||
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()
|
||||
}
|
||||
|
||||
+7
-1
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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){
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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<UsageInfo> 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<PsiPackage>() {
|
||||
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<PsiClass>() {
|
||||
public PsiClass compute() {
|
||||
return migration.createClass(qName);
|
||||
}
|
||||
});
|
||||
aClass = WriteAction.compute(() -> migration.createClass(qName));
|
||||
}
|
||||
return aClass;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
class Bar {
|
||||
|
||||
void m() {
|
||||
FooFactory.getFoo().someMethod();
|
||||
}
|
||||
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
class Foo {
|
||||
void someMethod() {
|
||||
|
||||
}
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
class FooFactory {
|
||||
public static Foo getFoo() {
|
||||
return new Foo();
|
||||
}
|
||||
}
|
||||
+5
-2
@@ -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<String> 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 }
|
||||
}
|
||||
}
|
||||
|
||||
+5
-1
@@ -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"() {
|
||||
|
||||
+5
-1
@@ -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
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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];
|
||||
|
||||
-2
@@ -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<LocalInspectionTool, LocalInspectionEP> {
|
||||
/** This should be used in tests primarily */
|
||||
@TestOnly
|
||||
public LocalInspectionToolWrapper(@NotNull LocalInspectionTool tool) {
|
||||
super(tool, ourEPMap.getValue().get(tool.getShortName()));
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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<FileElement> {
|
||||
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();
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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) {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
+8
-1
@@ -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<TextAttributesKey> 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()));
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -194,7 +194,7 @@ public class ProjectViewPane extends AbstractProjectViewPSIPane {
|
||||
|
||||
@Override
|
||||
public boolean isToBuildChildrenInBackground(Object element) {
|
||||
return true;
|
||||
return Registry.is("ide.projectView.ProjectViewPaneTreeStructure.BuildChildrenInBackground");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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<T extends ClassMember> extends DialogWrapper implemen
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void show() {
|
||||
TransactionGuard.getInstance().submitTransactionAndWait(super::show);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
PropertiesComponent instance = PropertiesComponent.getInstance();
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -94,25 +94,35 @@ class AsyncFilterRunner {
|
||||
return applyResults.get();
|
||||
}
|
||||
|
||||
private void handleSynchronouslyIfQuick(AtomicBoolean handled, Future<FilterResults> future, int timeout) {
|
||||
private boolean handleSynchronouslyIfQuick(AtomicBoolean handled, Future<FilterResults> 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<AtomicBoolean, Future<FilterResults>> 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
|
||||
|
||||
@@ -110,8 +110,9 @@ public class EditorHyperlinkSupport {
|
||||
}
|
||||
}
|
||||
|
||||
public void waitForPendingFilters() {
|
||||
myFilterRunner.waitForPendingFilters();
|
||||
@SuppressWarnings("SameParameterValue")
|
||||
public void waitForPendingFilters(long timeoutMs) {
|
||||
myFilterRunner.waitForPendingFilters(timeoutMs);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
+13
-5
@@ -69,7 +69,11 @@ 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 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;
|
||||
@@ -87,17 +91,21 @@ 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(innerBorderRoundRect);
|
||||
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));
|
||||
|
||||
Area insetArea = new Area(innerBorderRoundRect);
|
||||
insetArea.subtract(containingRoundRect);
|
||||
g.fill(insetArea);
|
||||
|
||||
g.translate(0, -(c.getHeight() - h) / 2);
|
||||
|
||||
// Deal with possible text painting
|
||||
|
||||
+78
@@ -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;
|
||||
}
|
||||
}
|
||||
+6
@@ -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);
|
||||
|
||||
@@ -740,6 +740,7 @@
|
||||
<action id="AddTestProcessAction" internal="true" class="com.intellij.openapi.wm.impl.status.AddTestProcessAction" text="Add Test Process"/>
|
||||
<action id="AddTestProcessActionIndefinte" internal="true" class="com.intellij.openapi.wm.impl.status.AddTestProcessActionIndefinite" text="Add Test Process Indefinite"/>
|
||||
<action id="AddManyTestProcesses" internal="true" class="com.intellij.openapi.wm.impl.status.AddManyTestProcesses" text="Add Many Test Processes"/>
|
||||
<action id="ShowProgressTestDialogAction" internal="true" class="com.intellij.openapi.wm.impl.status.ShowProgressTestDialogAction" text="Show Progress Test Dialog"/>
|
||||
<separator/>
|
||||
<action id="TestGestureAction" class="com.intellij.openapi.keymap.impl.ui.TestGestureAction" text="Test Gesture Action"/>
|
||||
<action id="TestDndAction" class="com.intellij.internal.validation.TestDnd" text="Test Dnd"/>
|
||||
|
||||
+10
-8
@@ -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 = """
|
||||
<scheme name="Foo" version="142" parent_scheme="Default">
|
||||
<metaInfo>
|
||||
<property name="created">2016-09-29T12:13:05</property>
|
||||
<property name="ide">idea</property>
|
||||
<property name="ideVersion">2016.3.0.0</property>
|
||||
<property name="modified">2016-09-29T12:14:54</property>
|
||||
<property name="originalScheme">Default</property>
|
||||
</metaInfo>
|
||||
<option name="EDITOR_FONT_SIZE" value="12" />
|
||||
<option name="EDITOR_FONT_NAME" value="Menlo" />
|
||||
<option name="JAVA_NUMBER" baseAttributes="DEFAULT_NUMBER" />
|
||||
</scheme>""".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("""
|
||||
<scheme name="Foo" version="142" parent_scheme="Default">
|
||||
<option name="EDITOR_FONT_SIZE" value="12" />
|
||||
<option name="EDITOR_FONT_NAME" value="Menlo" />
|
||||
</scheme>""".trimIndent())
|
||||
assertThat(schemeFile.parent).hasChildren("Foo.icls")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
+30
@@ -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+<metaInfo>.*</metaInfo>", Pattern.DOTALL).matcher(result)
|
||||
if (!matcher.find()) {
|
||||
return result
|
||||
}
|
||||
|
||||
val builder = StringBuffer()
|
||||
matcher.appendReplacement(builder, "")
|
||||
matcher.appendTail(builder)
|
||||
return builder.toString()
|
||||
}
|
||||
@@ -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
|
||||
@@ -887,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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
*/
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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<Pair<Integer, Boolean>, EmptyIcon> cache =
|
||||
new HashMap<Pair<Integer, Boolean>, EmptyIcon>(); // (size, preScaled) -> (icon)
|
||||
public class EmptyIcon extends JBUI.CachingScalableJBIcon<EmptyIcon> {
|
||||
private static final Map<Integer, EmptyIcon> cache = new HashMap<Integer, EmptyIcon>();
|
||||
|
||||
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<Integer, Boolean> 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<Integer, Boolean> 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<Integer, Boolean> 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<Integer, Boolean> 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);
|
||||
|
||||
@@ -166,7 +166,7 @@ public class JBUI {
|
||||
}
|
||||
|
||||
public static <T extends JBIcon> 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<T extends 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 {
|
||||
|
||||
@@ -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();
|
||||
|
||||
+3
-3
@@ -331,9 +331,9 @@ class StructureFilterPopupComponent extends FilterPopupComponent<VcsLogFileFilte
|
||||
}
|
||||
|
||||
@Override
|
||||
public CheckboxColorIcon withPreScaled(boolean preScaled) {
|
||||
mySizedIcon = JBUI.scale(mySizedIcon);
|
||||
return (CheckboxColorIcon)super.withPreScaled(preScaled);
|
||||
public CheckboxColorIcon withJBUIPreScaled(boolean preScaled) {
|
||||
mySizedIcon = (SizedIcon)mySizedIcon.withJBUIPreScaled(preScaled);
|
||||
return (CheckboxColorIcon)super.withJBUIPreScaled(preScaled);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -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
|
||||
|
||||
+11
-1
@@ -75,6 +75,7 @@ public class XBreakpointBase<Self extends XBreakpoint<P>, P extends XBreakpointP
|
||||
private XExpression myCondition;
|
||||
private boolean myLogExpressionEnabled = true;
|
||||
private XExpression myLogExpression;
|
||||
private volatile boolean myDisposed;
|
||||
|
||||
public XBreakpointBase(final XBreakpointType<Self, P> type, XBreakpointManagerImpl breakpointManager, final @Nullable P properties, final S state) {
|
||||
myState = state;
|
||||
@@ -311,7 +312,16 @@ public class XBreakpointBase<Self extends XBreakpoint<P>, 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
|
||||
|
||||
+2
-4
@@ -61,7 +61,6 @@ public class XLineBreakpointImpl<P extends XBreakpointProperties> extends XBreak
|
||||
@Nullable private RangeHighlighter myHighlighter;
|
||||
private final XLineBreakpointType<P> myType;
|
||||
private XSourcePosition mySourcePosition;
|
||||
private boolean myDisposed;
|
||||
|
||||
public XLineBreakpointImpl(final XLineBreakpointType<P> type,
|
||||
XBreakpointManagerImpl breakpointManager,
|
||||
@@ -78,7 +77,7 @@ public class XLineBreakpointImpl<P extends XBreakpointProperties> extends XBreak
|
||||
}
|
||||
|
||||
public void updateUI() {
|
||||
if (myDisposed || ApplicationManager.getApplication().isUnitTestMode()) {
|
||||
if (isDisposed() || ApplicationManager.getApplication().isUnitTestMode()) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -209,9 +208,8 @@ public class XLineBreakpointImpl<P extends XBreakpointProperties> extends XBreak
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
protected void doDispose() {
|
||||
removeHighlighter();
|
||||
myDisposed = true;
|
||||
}
|
||||
|
||||
private void removeHighlighter() {
|
||||
|
||||
@@ -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"/>
|
||||
<localInspection groupPath="Java" language="JAVA" shortName="JUnit5Converter" bundle="com.siyeh.InspectionGadgetsBundle"
|
||||
key="junit5.converter.display.name"
|
||||
groupBundle="messages.InspectionsBundle" groupKey="group.names.junit.issues" enabledByDefault="false" level="WARNING"
|
||||
implementationClass="com.siyeh.ig.junit.JUnit5ConverterInspection"/>
|
||||
</extensions>
|
||||
|
||||
</idea-plugin>
|
||||
|
||||
+3
-1
@@ -1911,7 +1911,7 @@ usage.of.obsolete.assert.display.name=Usage of obsolete 'junit.framework.Assert'
|
||||
use.of.obsolete.assert.problem.descriptor=Call to <code>#ref()</code> 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 <code>#ref()</code> 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,5 @@ 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 <code>#ref</code> 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
|
||||
|
||||
+3
@@ -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";
|
||||
}
|
||||
|
||||
+1
-3
@@ -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) {
|
||||
|
||||
+3
-2
@@ -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);
|
||||
|
||||
+11
-3
@@ -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) {
|
||||
|
||||
@@ -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<String> 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<UsageInfo> migrateUsages = new ArrayList<>();
|
||||
List<ProblemDescriptor> 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
<html>
|
||||
<body>
|
||||
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.
|
||||
<p>
|
||||
<small>New in 2017.1</small>
|
||||
</body>
|
||||
</html>
|
||||
+35
@@ -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("");
|
||||
}
|
||||
}
|
||||
@@ -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 Simp<caret>le {
|
||||
|
||||
@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("");
|
||||
}
|
||||
}
|
||||
+64
@@ -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 <T> void assertThat(T actual, Matcher<? super T> 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) {}" +
|
||||
"}");
|
||||
}
|
||||
}
|
||||
+7
-2
@@ -252,14 +252,19 @@ 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();
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
|
||||
+12
@@ -34,6 +34,18 @@ public final class ColorIcon extends EmptyIcon {
|
||||
myColorSize = colorSize;
|
||||
}
|
||||
|
||||
protected ColorIcon(ColorIcon icon) {
|
||||
super(icon);
|
||||
myColorSize = icon.myColorSize;
|
||||
myColor = icon.myColor;
|
||||
myShowRedLine = icon.myShowRedLine;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ColorIcon copy() {
|
||||
return new ColorIcon(this);
|
||||
}
|
||||
|
||||
public Color getColor() {
|
||||
return myShowRedLine ? null : myColor;
|
||||
}
|
||||
|
||||
@@ -343,6 +343,7 @@ exception.breakpoints.tab.title=Java Exception Breakpoints
|
||||
field.watchpoints.tab.title=Java Field Watchpoints
|
||||
line.breakpoints.tab.title=Java Line Breakpoints
|
||||
method.breakpoints.tab.title=Java Method Breakpoints
|
||||
method.breakpoints.slowness.warning=Method breakpoints may dramatically slow down debugging
|
||||
title.error.evaluating.breakpoint.condition=Breakpoint Condition Error
|
||||
title.error.evaluating.breakpoint.action=Breakpoint Action Error
|
||||
error.evaluating.breakpoint.condition.or.action=Problem processing VM event:\nBreakpoint: ''{0}''\nError: {1}\nWould you like to stop at the breakpoint?
|
||||
|
||||
@@ -172,6 +172,8 @@
|
||||
<projectService serviceInterface="com.intellij.codeInsight.InferredAnnotationsManager"
|
||||
serviceImplementation="com.intellij.codeInsight.InferredAnnotationsManagerImpl"/>
|
||||
|
||||
<fileBasedIndex implementation="com.intellij.codeInspection.dataFlow.ContractInferenceIndex"/>
|
||||
|
||||
<projectService serviceInterface="com.intellij.task.ProjectTaskManager"
|
||||
serviceImplementation="com.intellij.task.impl.ProjectTaskManagerImpl"/>
|
||||
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -123,9 +123,12 @@ 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) {
|
||||
final BreadcrumbsXmlWrapper existingWrapper = BreadcrumbsXmlWrapper.getBreadcrumbsComponent(editor);
|
||||
if (existingWrapper != null) {
|
||||
existingWrapper.queueUpdate();
|
||||
continue;
|
||||
}
|
||||
|
||||
final BreadcrumbsXmlWrapper wrapper = new BreadcrumbsXmlWrapper(editor);
|
||||
final JComponent c = wrapper.getComponent();
|
||||
fileEditorManager.addTopComponent(fileEditor, c);
|
||||
|
||||
@@ -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,27 +99,14 @@ public class BreadcrumbsXmlWrapper implements BreadcrumbsItemListener<Breadcrumb
|
||||
Document document = myEditor.getDocument();
|
||||
myFile = FileDocumentManager.getInstance().getFile(document);
|
||||
|
||||
|
||||
final FileStatusManager manager = FileStatusManager.getInstance(project);
|
||||
manager.addFileStatusListener(new FileStatusListener() {
|
||||
@Override
|
||||
public void fileStatusesChanged() {
|
||||
updateCrumbs();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fileStatusChanged(@NotNull final VirtualFile virtualFile) {
|
||||
}
|
||||
}, this);
|
||||
|
||||
project.getMessageBus().connect(this).subscribe(UISettingsListener.TOPIC, new UISettingsListener() {
|
||||
@Override
|
||||
public void uiSettingsChanged(UISettings uiSettings) {
|
||||
queueUpdate();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
myInfoProvider = findInfoProvider(findViewProvider(myFile, myProject));
|
||||
|
||||
final CaretListener caretListener = new CaretAdapter() {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.util.Arrays;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
@@ -67,6 +68,17 @@ public class ColorIconCache {
|
||||
myColours = colours;
|
||||
}
|
||||
|
||||
protected ColorIcon(ColorIcon icon) {
|
||||
super(icon);
|
||||
myColor = icon.myColor;
|
||||
if (icon.myColours != null) myColours = Arrays.copyOf(icon.myColours, icon.myColours.length);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ColorIcon copy() {
|
||||
return new ColorIcon(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void paintIcon(final Component component, final Graphics g, final int i, final int j) {
|
||||
final int iconWidth = getIconWidth();
|
||||
|
||||
Reference in New Issue
Block a user