Merge remote-tracking branch 'origin/master'

This commit is contained in:
Roman Shevchenko
2016-10-28 16:13:58 +02:00
88 changed files with 1393 additions and 458 deletions
@@ -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"/>
@@ -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;
@@ -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);
}
}
@@ -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();
@@ -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)
}
}
@@ -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'
@@ -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;
@@ -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() }
@@ -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;
}
}
@@ -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));
}
}
}
@@ -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()
}
@@ -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;
@@ -0,0 +1,7 @@
class Bar {
void m() {
FooFactory.getFoo().someMethod();
}
}
@@ -0,0 +1,5 @@
class Foo {
void someMethod() {
}
}
@@ -0,0 +1,5 @@
class FooFactory {
public static Foo getFoo() {
return new Foo();
}
}
@@ -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 }
}
}
@@ -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"() {
@@ -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];
@@ -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);
}
@@ -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);
}
@@ -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
@@ -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;
}
}
@@ -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"/>
@@ -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 {
@@ -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();
@@ -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
@@ -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
@@ -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>
@@ -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
@@ -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";
}
@@ -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) {
@@ -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);
@@ -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>
@@ -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("");
}
}
@@ -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) {}" +
"}");
}
}
@@ -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
@@ -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();