diff options
Diffstat (limited to 'java/java-analysis-impl/src/com/intellij/codeInspection')
14 files changed, 717 insertions, 538 deletions
diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/AnonymousCanBeLambdaInspection.java b/java/java-analysis-impl/src/com/intellij/codeInspection/AnonymousCanBeLambdaInspection.java index c1f127d3a1df..621ce3fce2af 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/AnonymousCanBeLambdaInspection.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/AnonymousCanBeLambdaInspection.java @@ -141,7 +141,19 @@ public class AnonymousCanBeLambdaInspection extends BaseJavaBatchLocalInspection .inferTypeArguments(method.getTypeParameters(), parameters, expressions, ((MethodCandidateInfo)result).getSiteSubstitutor(), callExpr.getParent(), DefaultParameterTypeInferencePolicy.INSTANCE); - return substitutor.substitute(parameters[i].getType()); + PsiType paramType; + if (i < parameters.length) { + paramType = parameters[i].getType(); + } + else { + paramType = parameters[parameters.length - 1].getType(); + if (!(paramType instanceof PsiEllipsisType)) { + return null; + } + paramType = ((PsiEllipsisType)paramType).getComponentType(); + } + + return substitutor.substitute(paramType); } } } diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/Analysis.java b/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/Analysis.java index 44e493c63f0a..47ef448c7484 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/Analysis.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/Analysis.java @@ -208,7 +208,8 @@ class MakeResult<Res> implements PendingAction<Res> { } abstract class Analysis<Res> { - private static final int STEPS_LIMIT = 30000; + public static final int STEPS_LIMIT = 30000; + public static final int EQUATION_SIZE_LIMIT = 30; final RichControlFlow richControlFlow; final Direction direction; final ControlFlowGraph controlFlow; @@ -225,7 +226,7 @@ abstract class Analysis<Res> { Res earlyResult = null; abstract Res identity(); - abstract Res combineResults(Res delta, List<Res> subResults); + abstract Res combineResults(Res delta, List<Res> subResults) throws AnalyzerException; abstract boolean isEarlyResult(Res res); abstract Equation<Key, Value> mkEquation(Res result); abstract void processState(State state) throws AnalyzerException; diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/BytecodeAnalysisConverter.java b/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/BytecodeAnalysisConverter.java index f29dd7f6cf0c..0239a661c4ee 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/BytecodeAnalysisConverter.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/BytecodeAnalysisConverter.java @@ -15,112 +15,68 @@ */ package com.intellij.codeInspection.bytecodeAnalysis; -import com.intellij.ide.util.PropertiesComponent; import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.application.PathManager; import com.intellij.openapi.components.ApplicationComponent; import com.intellij.openapi.progress.ProgressManager; -import com.intellij.openapi.util.ThrowableComputable; import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.*; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.util.TypeConversionUtil; -import com.intellij.util.io.*; -import gnu.trove.TIntHashSet; -import gnu.trove.TIntObjectHashMap; -import gnu.trove.TIntObjectIterator; +import gnu.trove.TLongArrayList; +import gnu.trove.TLongHashSet; +import gnu.trove.TLongObjectHashMap; +import gnu.trove.TLongObjectIterator; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; import org.jetbrains.org.objectweb.asm.Type; -import java.io.DataInput; -import java.io.DataOutput; -import java.io.File; import java.io.IOException; -import java.util.*; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Set; import static com.intellij.codeInspection.bytecodeAnalysis.ProjectBytecodeAnalysis.LOG; /** * @author lambdamix */ -public class BytecodeAnalysisConverter implements ApplicationComponent { +public abstract class BytecodeAnalysisConverter implements ApplicationComponent { - private static final String VERSION = "BytecodeAnalysisConverter.Enumerators"; + public static final int SHIFT = 4096; public static BytecodeAnalysisConverter getInstance() { return ApplicationManager.getApplication().getComponent(BytecodeAnalysisConverter.class); } - private PersistentStringEnumerator myNamesEnumerator; - private PersistentEnumeratorDelegate<int[]> myCompoundKeyEnumerator; - private int version; - - @Override - public void initComponent() { - version = PropertiesComponent.getInstance().getOrInitInt(VERSION, 0); - final File keysDir = new File(PathManager.getIndexRoot(), "bytecodekeys"); - final File namesFile = new File(keysDir, "names"); - final File compoundKeysFile = new File(keysDir, "compound"); - - try { - IOUtil.openCleanOrResetBroken(new ThrowableComputable<Void, IOException>() { - @Override - public Void compute() throws IOException { - myNamesEnumerator = new PersistentStringEnumerator(namesFile, true); - myCompoundKeyEnumerator = new IntArrayPersistentEnumerator(compoundKeysFile, new IntArrayKeyDescriptor()); - return null; - } - }, new Runnable() { - @Override - public void run() { - LOG.info("Error during initialization of enumerators in bytecode analysis. Re-initializing."); - IOUtil.deleteAllFilesStartingWith(keysDir); - version ++; - } - }); - } - catch (IOException e) { - LOG.error("Re-initialization of enumerators in bytecode analysis failed.", e); - } - // TODO: is it enough for rebuilding indices? - PropertiesComponent.getInstance().setValue(VERSION, String.valueOf(version)); - } - - @Override - public void disposeComponent() { - try { - myNamesEnumerator.close(); - myCompoundKeyEnumerator.close(); - } - catch (IOException e) { - LOG.debug(e); - } - } - @NotNull @Override public String getComponentName() { return "BytecodeAnalysisConverter"; } - IntIdEquation convert(Equation<Key, Value> equation) throws IOException { + public abstract int getVersion(); + + protected abstract int enumerateString(@NotNull String s) throws IOException; + + protected abstract int enumerateCompoundKey(@NotNull int[] key) throws IOException; + + IdEquation convert(Equation<Key, Value> equation) throws IOException { ProgressManager.checkCanceled(); Result<Key, Value> rhs = equation.rhs; - IntIdResult result; + IdResult result; if (rhs instanceof Final) { - result = new IntIdFinal(((Final<Key, Value>)rhs).value); + result = new IdFinal(((Final<Key, Value>)rhs).value); } else { Pending<Key, Value> pending = (Pending<Key, Value>)rhs; Set<Product<Key, Value>> sumOrigin = pending.sum; IntIdComponent[] components = new IntIdComponent[sumOrigin.size()]; int componentI = 0; for (Product<Key, Value> prod : sumOrigin) { - int[] intProd = new int[prod.ids.size()]; + long[] intProd = new long[prod.ids.size()]; int idI = 0; for (Key id : prod.ids) { - int rawId = mkAsmKey(id); + long rawId = mkAsmKey(id); if (rawId <= 0) { LOG.error("raw key should be positive. rawId = " + rawId); } @@ -131,24 +87,51 @@ public class BytecodeAnalysisConverter implements ApplicationComponent { components[componentI] = intIdComponent; componentI++; } - result = new IntIdPending(components); + result = new IdPending(components); } - int rawKey = mkAsmKey(equation.id); + long rawKey = mkAsmKey(equation.id); if (rawKey <= 0) { LOG.error("raw key should be positive. rawKey = " + rawKey); } - int key = equation.id.stable ? rawKey : -rawKey; - return new IntIdEquation(key, result); + long key = equation.id.stable ? rawKey : -rawKey; + return new IdEquation(key, result); } - public int mkAsmKey(@NotNull Key key) throws IOException { - return myCompoundKeyEnumerator.enumerate(new int[]{mkDirectionKey(key.direction), mkAsmSignatureKey(key.method)}); + public long mkAsmKey(@NotNull Key key) throws IOException { + long baseKey = mkAsmSignatureKey(key.method); + long directionKey = mkDirectionKey(key.direction); + return baseKey * SHIFT + directionKey; } - private int mkDirectionKey(Direction dir) throws IOException { - return myCompoundKeyEnumerator.enumerate(new int[]{dir.directionId(), dir.paramId(), dir.valueId()}); + private static int mkDirectionKey(Direction dir) throws IOException { + if (dir instanceof Out) { + return 0; + } else if (dir instanceof In) { + In in = (In)dir; + return 8 * in.paramId() + 1; + } else { + InOut inOut = (InOut)dir; + return 8 * inOut.paramId() + 2 + inOut.valueId(); + } + } + + @NotNull + private static Direction extractDirection(int directionKey) { + if (directionKey == 0) { + return new Out(); + } + else { + int paramId = directionKey / 8; + int subDirection = directionKey % 8; + if (subDirection == 1) { + return new In(paramId); + } + else { + return new InOut(paramId, Value.values()[subDirection - 2]); + } + } } // class + short signature @@ -156,33 +139,19 @@ public class BytecodeAnalysisConverter implements ApplicationComponent { int[] sigKey = new int[2]; sigKey[0] = mkAsmTypeKey(Type.getObjectType(method.internalClassName)); sigKey[1] = mkAsmShortSignatureKey(method); - return myCompoundKeyEnumerator.enumerate(sigKey); + return enumerateCompoundKey(sigKey); } private int mkAsmShortSignatureKey(@NotNull Method method) throws IOException { Type[] argTypes = Type.getArgumentTypes(method.methodDesc); int arity = argTypes.length; - int[] sigKey = new int[3 + arity]; + int[] sigKey = new int[2 + arity]; sigKey[0] = mkAsmTypeKey(Type.getReturnType(method.methodDesc)); - sigKey[1] = myNamesEnumerator.enumerate(method.methodName); - sigKey[2] = argTypes.length; + sigKey[1] = enumerateString(method.methodName); for (int i = 0; i < argTypes.length; i++) { - sigKey[3 + i] = mkAsmTypeKey(argTypes[i]); + sigKey[2 + i] = mkAsmTypeKey(argTypes[i]); } - return myCompoundKeyEnumerator.enumerate(sigKey); - } - - @Nullable - private static Direction extractDirection(int[] directionKey) { - switch (directionKey[0]) { - case Direction.OUT_DIRECTION: - return new Out(); - case Direction.IN_DIRECTION: - return new In(directionKey[1]); - case Direction.INOUT_DIRECTION: - return new InOut(directionKey[1], Value.values()[directionKey[2]]); - } - return null; + return enumerateCompoundKey(sigKey); } private int mkAsmTypeKey(Type type) throws IOException { @@ -197,22 +166,22 @@ public class BytecodeAnalysisConverter implements ApplicationComponent { packageName = ""; simpleName = className; } - int[] classKey = new int[]{myNamesEnumerator.enumerate(packageName), myNamesEnumerator.enumerate(simpleName)}; - return myCompoundKeyEnumerator.enumerate(classKey); + int[] classKey = new int[]{enumerateString(packageName), enumerateString(simpleName)}; + return enumerateCompoundKey(classKey); } - public int mkPsiKey(@NotNull PsiMethod psiMethod, Direction direction) throws IOException { + public long mkPsiKey(@NotNull PsiMethod psiMethod, Direction direction) throws IOException { final PsiClass psiClass = PsiTreeUtil.getParentOfType(psiMethod, PsiClass.class, false); if (psiClass == null) { LOG.debug("PsiClass was null for " + psiMethod.getName()); return -1; } - int sigKey = mkPsiSignatureKey(psiMethod); + long sigKey = mkPsiSignatureKey(psiMethod); if (sigKey == -1) { return -1; } - return myCompoundKeyEnumerator.enumerate(new int[]{mkDirectionKey(direction), sigKey}); - + long directionKey = mkDirectionKey(direction); + return sigKey * SHIFT + directionKey; } private int mkPsiSignatureKey(@NotNull PsiMethod psiMethod) throws IOException { @@ -228,21 +197,20 @@ public class BytecodeAnalysisConverter implements ApplicationComponent { final int shift = isInnerClassConstructor ? 1 : 0; final int arity = parameters.length + shift; - int[] shortSigKey = new int[3 + arity]; + int[] shortSigKey = new int[2 + arity]; if (returnType == null) { shortSigKey[0] = mkPsiTypeKey(PsiType.VOID); - shortSigKey[1] = myNamesEnumerator.enumerate("<init>"); + shortSigKey[1] = enumerateString("<init>"); } else { shortSigKey[0] = mkPsiTypeKey(returnType); - shortSigKey[1] = myNamesEnumerator.enumerate(psiMethod.getName()); + shortSigKey[1] = enumerateString(psiMethod.getName()); } - shortSigKey[2] = arity; if (isInnerClassConstructor) { - shortSigKey[3] = mkPsiClassKey(outerClass, 0); + shortSigKey[2] = mkPsiClassKey(outerClass, 0); } for (int i = 0; i < parameters.length; i++) { PsiParameter parameter = parameters[i]; - shortSigKey[3 + i + shift] = mkPsiTypeKey(parameter.getType()); + shortSigKey[2 + i + shift] = mkPsiTypeKey(parameter.getType()); } for (int aShortSigKey : shortSigKey) { if (aShortSigKey == -1) { @@ -256,9 +224,28 @@ public class BytecodeAnalysisConverter implements ApplicationComponent { return -1; } sigKey[0] = classKey; - sigKey[1] = myCompoundKeyEnumerator.enumerate(shortSigKey); + sigKey[1] = enumerateCompoundKey(shortSigKey); - return myCompoundKeyEnumerator.enumerate(sigKey); + return enumerateCompoundKey(sigKey); + } + + public TLongArrayList mkInOutKeys(@NotNull PsiMethod psiMethod, long primaryKey) throws IOException { + PsiParameter[] parameters = psiMethod.getParameterList().getParameters(); + TLongArrayList keys = new TLongArrayList(parameters.length * 2 + 1); + for (int i = 0; i < parameters.length; i++) { + PsiParameter parameter = parameters[i]; + PsiType parameterType = parameter.getType(); + if (parameterType instanceof PsiPrimitiveType) { + if (PsiType.BOOLEAN.equals(parameterType)) { + keys.add(primaryKey + mkDirectionKey(new InOut(i, Value.False))); + keys.add(primaryKey + mkDirectionKey(new InOut(i, Value.True))); + } + } else { + keys.add(primaryKey + mkDirectionKey(new InOut(i, Value.NotNull))); + keys.add(primaryKey + mkDirectionKey(new InOut(i, Value.Null))); + } + } + return keys; } @@ -279,17 +266,17 @@ public class BytecodeAnalysisConverter implements ApplicationComponent { className = qname.substring(packageName.length() + 1).replace('.', '$'); } int[] classKey = new int[2]; - classKey[0] = myNamesEnumerator.enumerate(packageName); + classKey[0] = enumerateString(packageName); if (dimensions == 0) { - classKey[1] = myNamesEnumerator.enumerate(className); + classKey[1] = enumerateString(className); } else { StringBuilder sb = new StringBuilder(className); for (int j = 0; j < dimensions; j++) { sb.append("[]"); } - classKey[1] = myNamesEnumerator.enumerate(sb.toString()); + classKey[1] = enumerateString(sb.toString()); } - return myCompoundKeyEnumerator.enumerate(classKey); + return enumerateCompoundKey(classKey); } private int mkPsiTypeKey(PsiType psiType) throws IOException { @@ -316,77 +303,69 @@ public class BytecodeAnalysisConverter implements ApplicationComponent { String packageName = ""; String className = psiType.getPresentableText(); int[] classKey = new int[2]; - classKey[0] = myNamesEnumerator.enumerate(packageName); + classKey[0] = enumerateString(packageName); if (dimensions == 0) { - classKey[1] = myNamesEnumerator.enumerate(className); + classKey[1] = enumerateString(className); } else { StringBuilder sb = new StringBuilder(className); for (int j = 0; j < dimensions; j++) { sb.append("[]"); } - classKey[1] = myNamesEnumerator.enumerate(sb.toString()); + classKey[1] = enumerateString(sb.toString()); } - return myCompoundKeyEnumerator.enumerate(classKey); + return enumerateCompoundKey(classKey); } return -1; } - public void addAnnotations(TIntObjectHashMap<Value> internalIdSolutions, Annotations annotations) { - - TIntObjectHashMap<List<String>> contractClauses = new TIntObjectHashMap<List<String>>(); - TIntObjectIterator<Value> solutionsIterator = internalIdSolutions.iterator(); + public void addMethodAnnotations(TLongObjectHashMap<Value> internalIdSolutions, Annotations annotations, long methodKey, int arity) { - TIntHashSet notNulls = annotations.notNulls; - TIntObjectHashMap<String> contracts = annotations.contracts; + List<String> clauses = new ArrayList<String>(); + TLongObjectIterator<Value> solutionsIterator = internalIdSolutions.iterator(); + TLongHashSet notNulls = annotations.notNulls; + TLongObjectHashMap<String> contracts = annotations.contracts; for (int i = internalIdSolutions.size(); i-- > 0;) { solutionsIterator.advance(); - int key = Math.abs(solutionsIterator.key()); + long key = Math.abs(solutionsIterator.key()); Value value = solutionsIterator.value(); if (value == Value.Top || value == Value.Bot) { continue; } - try { - int[] compoundKey = myCompoundKeyEnumerator.valueOf(key); - Direction direction = extractDirection(myCompoundKeyEnumerator.valueOf(compoundKey[0])); - if (value == Value.NotNull && (direction instanceof In || direction instanceof Out)) { - notNulls.add(key); - } - else if (direction instanceof InOut) { - compoundKey = new int[]{mkDirectionKey(new Out()), compoundKey[1]}; - try { - int baseKey = myCompoundKeyEnumerator.enumerate(compoundKey); - List<String> clauses = contractClauses.get(baseKey); - if (clauses == null) { - clauses = new ArrayList<String>(); - contractClauses.put(baseKey, clauses); - } - int[] sig = myCompoundKeyEnumerator.valueOf(compoundKey[1]); - int[] shortSig = myCompoundKeyEnumerator.valueOf(sig[1]); - int arity = shortSig[2]; - clauses.add(contractElement(arity, (InOut)direction, value)); - } - catch (IOException e) { - LOG.debug(e); - } - } + Direction direction = extractDirection((int)(key % SHIFT)); + if (value == Value.NotNull && direction instanceof Out && key == methodKey) { + notNulls.add(key); } - catch (IOException e) { - LOG.debug(e); + else if (direction instanceof InOut) { + long baseKey = key - (key % SHIFT); + if (baseKey == methodKey) { + clauses.add(contractElement(arity, (InOut)direction, value)); + } } } - TIntObjectIterator<List<String>> buildersIterator = contractClauses.iterator(); - for (int i = contractClauses.size(); i-- > 0;) { - buildersIterator.advance(); - int key = buildersIterator.key(); - if (!notNulls.contains(key)) { - List<String> clauses = buildersIterator.value(); - Collections.sort(clauses); - StringBuilder sb = new StringBuilder("\""); - StringUtil.join(clauses, ";", sb); - sb.append('"'); - contracts.put(key, sb.toString().intern()); + if (!notNulls.contains(methodKey) && !clauses.isEmpty()) { + Collections.sort(clauses); + StringBuilder sb = new StringBuilder("\""); + StringUtil.join(clauses, ";", sb); + sb.append('"'); + contracts.put(methodKey, sb.toString().intern()); + } + } + + public void addParameterAnnotations(TLongObjectHashMap<Value> internalIdSolutions, Annotations annotations) { + TLongObjectIterator<Value> solutionsIterator = internalIdSolutions.iterator(); + TLongHashSet notNulls = annotations.notNulls; + for (int i = internalIdSolutions.size(); i-- > 0;) { + solutionsIterator.advance(); + long key = Math.abs(solutionsIterator.key()); + Value value = solutionsIterator.value(); + if (value == Value.Top || value == Value.Bot) { + continue; + } + Direction direction = extractDirection((int)(key % SHIFT)); + if (value == Value.NotNull && (direction instanceof In || direction instanceof Out)) { + notNulls.add(key); } } } @@ -418,68 +397,4 @@ public class BytecodeAnalysisConverter implements ApplicationComponent { return sb.toString(); } - public int getVersion() { - return version; - } - - private static class IntArrayKeyDescriptor implements KeyDescriptor<int[]> { - - @Override - public void save(@NotNull DataOutput out, int[] value) throws IOException { - DataInputOutputUtil.writeINT(out, value.length); - for (int i : value) { - DataInputOutputUtil.writeINT(out, i); - } - } - - @Override - public int[] read(@NotNull DataInput in) throws IOException { - int[] value = new int[DataInputOutputUtil.readINT(in)]; - for (int i = 0; i < value.length; i++) { - value[i] = DataInputOutputUtil.readINT(in); - } - return value; - } - - @Override - public int getHashCode(int[] value) { - return Arrays.hashCode(value); - } - - @Override - public boolean isEqual(int[] val1, int[] val2) { - return Arrays.equals(val1, val2); - } - } - - private static class IntArrayPersistentEnumerator extends PersistentEnumeratorDelegate<int[]> { - private final CachingEnumerator<int[]> myCache; - - public IntArrayPersistentEnumerator(File compoundKeysFile, IntArrayKeyDescriptor descriptor) throws IOException { - super(compoundKeysFile, descriptor, 1024 * 4); - myCache = new CachingEnumerator<int[]>(new DataEnumerator<int[]>() { - @Override - public int enumerate(@Nullable int[] value) throws IOException { - return IntArrayPersistentEnumerator.super.enumerate(value); - } - - @Nullable - @Override - public int[] valueOf(int idx) throws IOException { - return IntArrayPersistentEnumerator.super.valueOf(idx); - } - }, descriptor); - } - - @Override - public int enumerate(@Nullable int[] value) throws IOException { - return myCache.enumerate(value); - } - - @Nullable - @Override - public int[] valueOf(int idx) throws IOException { - return myCache.valueOf(idx); - } - } } diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/BytecodeAnalysisConverterImpl.java b/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/BytecodeAnalysisConverterImpl.java new file mode 100644 index 000000000000..067cc743679e --- /dev/null +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/BytecodeAnalysisConverterImpl.java @@ -0,0 +1,206 @@ +/* + * Copyright 2000-2014 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.bytecodeAnalysis; + +import com.intellij.ide.util.PropertiesComponent; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.application.PathManager; +import com.intellij.openapi.util.ThrowableComputable; +import com.intellij.openapi.util.io.FileUtil; +import com.intellij.util.io.*; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.io.*; +import java.io.DataOutputStream; +import java.util.Arrays; + +import static com.intellij.codeInspection.bytecodeAnalysis.ProjectBytecodeAnalysis.LOG; + +/** + * @author lambdamix + */ +public class BytecodeAnalysisConverterImpl extends BytecodeAnalysisConverter { + private static final int LOGIC_VERSION = 1; + private static final String ENUMERATORS_VERSION_KEY = "BytecodeAnalysisConverter.Enumerators"; + + private File myVersionFile; + private PersistentStringEnumerator myNamesEnumerator; + private PersistentEnumeratorDelegate<int[]> myCompoundKeyEnumerator; + private int version; + + @Override + public void initComponent() { + + // suffix as an indicator of version + final File keysDir = new File(PathManager.getIndexRoot(), "bytecodekeys"); + final File namesFile = new File(keysDir, "names" + LOGIC_VERSION); + final File compoundKeysFile = new File(keysDir, "compound" + LOGIC_VERSION); + myVersionFile = new File(keysDir, "version" + LOGIC_VERSION); + + version = PropertiesComponent.getInstance().getOrInitInt(ENUMERATORS_VERSION_KEY, 0); + if (ApplicationManager.getApplication().isUnitTestMode()) { + version = _readVersion(); + } + + if (!namesFile.exists() || !compoundKeysFile.exists() || !myVersionFile.exists()) { + LOG.info("No enumerators detected, re-initialization of enumerators."); + IOUtil.deleteAllFilesStartingWith(keysDir); + version++; + } + + try { + IOUtil.openCleanOrResetBroken(new ThrowableComputable<Void, IOException>() { + @Override + public Void compute() throws IOException { + myNamesEnumerator = new PersistentStringEnumerator(namesFile, true); + myCompoundKeyEnumerator = new IntArrayPersistentEnumerator(compoundKeysFile, new IntArrayKeyDescriptor()); + return null; + } + }, new Runnable() { + @Override + public void run() { + LOG.info("Error during initialization of enumerators in bytecode analysis. Re-initializing."); + IOUtil.deleteAllFilesStartingWith(keysDir); + version++; + } + }); + } + catch (IOException e) { + LOG.error("Re-initialization of enumerators in bytecode analysis failed.", e); + } + PropertiesComponent.getInstance().setValue(ENUMERATORS_VERSION_KEY, String.valueOf(version)); + _saveVersion(); + } + + @Override + public void disposeComponent() { + try { + myNamesEnumerator.close(); + myCompoundKeyEnumerator.close(); + } + catch (IOException e) { + LOG.debug(e); + } + } + + public int _readVersion() { + try { + final DataInputStream is = new DataInputStream(new FileInputStream(myVersionFile)); + try { + return is.readInt(); + } + finally { + is.close(); + } + } + catch (FileNotFoundException ignored) { + } + catch (IOException ignored) { + } + return 0; + } + + private void _saveVersion() { + try { + FileUtil.createIfDoesntExist(myVersionFile); + final DataOutputStream os = new DataOutputStream(new FileOutputStream(myVersionFile)); + try { + os.writeInt(version); + } + finally { + os.close(); + } + } + catch (IOException ignored) { + } + } + + public int getVersion() { + return version; + } + + @Override + protected int enumerateString(@NotNull String s) throws IOException { + return myNamesEnumerator.enumerate(s); + } + + @Override + protected int enumerateCompoundKey(@NotNull int[] key) throws IOException { + return myCompoundKeyEnumerator.enumerate(key); + } + + private static class IntArrayKeyDescriptor implements KeyDescriptor<int[]>, DifferentSerializableBytesImplyNonEqualityPolicy { + + @Override + public void save(@NotNull DataOutput out, int[] value) throws IOException { + DataInputOutputUtil.writeINT(out, value.length); + for (int i : value) { + DataInputOutputUtil.writeINT(out, i); + } + } + + @Override + public int[] read(@NotNull DataInput in) throws IOException { + int[] value = new int[DataInputOutputUtil.readINT(in)]; + for (int i = 0; i < value.length; i++) { + value[i] = DataInputOutputUtil.readINT(in); + } + return value; + } + + @Override + public int getHashCode(int[] value) { + return Arrays.hashCode(value); + } + + @Override + public boolean isEqual(int[] val1, int[] val2) { + return Arrays.equals(val1, val2); + } + } + + private static class IntArrayPersistentEnumerator extends PersistentEnumeratorDelegate<int[]> { + private final CachingEnumerator<int[]> myCache; + + public IntArrayPersistentEnumerator(File compoundKeysFile, IntArrayKeyDescriptor descriptor) throws IOException { + super(compoundKeysFile, descriptor, 1024 * 4); + myCache = new CachingEnumerator<int[]>(new DataEnumerator<int[]>() { + @Override + public int enumerate(@Nullable int[] value) throws IOException { + return IntArrayPersistentEnumerator.super.enumerate(value); + } + + @Nullable + @Override + public int[] valueOf(int idx) throws IOException { + return IntArrayPersistentEnumerator.super.valueOf(idx); + } + }, descriptor); + } + + @Override + public int enumerate(@Nullable int[] value) throws IOException { + return myCache.enumerate(value); + } + + @Nullable + @Override + public int[] valueOf(int idx) throws IOException { + return myCache.valueOf(idx); + } + } +} diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/BytecodeAnalysisIndex.java b/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/BytecodeAnalysisIndex.java index 6a4b32783c95..297b2b694c62 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/BytecodeAnalysisIndex.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/BytecodeAnalysisIndex.java @@ -19,31 +19,29 @@ import com.intellij.ide.highlighter.JavaClassFileType; import com.intellij.openapi.application.Application; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.openapi.vfs.VirtualFileWithId; import com.intellij.util.SystemProperties; import com.intellij.util.indexing.*; import com.intellij.util.io.DataExternalizer; import com.intellij.util.io.DataInputOutputUtil; -import com.intellij.util.io.EnumeratorIntegerDescriptor; +import com.intellij.util.io.DifferentSerializableBytesImplyNonEqualityPolicy; import com.intellij.util.io.KeyDescriptor; import org.jetbrains.annotations.NotNull; import java.io.DataInput; import java.io.DataOutput; import java.io.IOException; -import java.util.ArrayList; -import java.util.Collection; /** * @author lambdamix */ -public class BytecodeAnalysisIndex extends FileBasedIndexExtension<Integer, Collection<IntIdEquation>> { - public static final ID<Integer, Collection<IntIdEquation>> NAME = ID.create("bytecodeAnalysis"); +public class BytecodeAnalysisIndex extends FileBasedIndexExtension<Long, IdEquation> { + public static final ID<Long, IdEquation> NAME = ID.create("bytecodeAnalysis"); private final EquationExternalizer myExternalizer = new EquationExternalizer(); - private static final DataIndexer<Integer, Collection<IntIdEquation>, FileContent> INDEXER = + private static final DataIndexer<Long, IdEquation, FileContent> INDEXER = new ClassDataIndexer(BytecodeAnalysisConverter.getInstance()); + private static final SmartLongKeyDescriptor KEY_DESCRIPTOR = new SmartLongKeyDescriptor(); - private static final int ourInternalVersion = 2; + private static final int ourInternalVersion = 3; private static boolean ourEnabled = SystemProperties.getBooleanProperty("idea.enable.bytecode.contract.inference", isEnabledByDefault()); private static boolean isEnabledByDefault() { @@ -51,31 +49,27 @@ public class BytecodeAnalysisIndex extends FileBasedIndexExtension<Integer, Coll return application.isInternal() || application.isUnitTestMode(); } - public static int indexKey(VirtualFile file, boolean parameters) { - return (file instanceof VirtualFileWithId ? ((VirtualFileWithId)file).getId() * 2 : -2) + (parameters ? 1 : 0); - } - @NotNull @Override - public ID<Integer, Collection<IntIdEquation>> getName() { + public ID<Long, IdEquation> getName() { return NAME; } @NotNull @Override - public DataIndexer<Integer, Collection<IntIdEquation>, FileContent> getIndexer() { + public DataIndexer<Long, IdEquation, FileContent> getIndexer() { return INDEXER; } @NotNull @Override - public KeyDescriptor<Integer> getKeyDescriptor() { - return EnumeratorIntegerDescriptor.INSTANCE; + public KeyDescriptor<Long> getKeyDescriptor() { + return KEY_DESCRIPTOR; } @NotNull @Override - public DataExternalizer<Collection<IntIdEquation>> getValueExternalizer() { + public DataExternalizer<IdEquation> getValueExternalizer() { return myExternalizer; } @@ -100,68 +94,108 @@ public class BytecodeAnalysisIndex extends FileBasedIndexExtension<Integer, Coll return ourInternalVersion + BytecodeAnalysisConverter.getInstance().getVersion() + (ourEnabled ? 0xFF : 0); } - public static class EquationExternalizer implements DataExternalizer<Collection<IntIdEquation>> { + public static class EquationExternalizer implements DataExternalizer<IdEquation>, DifferentSerializableBytesImplyNonEqualityPolicy { @Override - public void save(@NotNull DataOutput out, Collection<IntIdEquation> equations) throws IOException { - DataInputOutputUtil.writeINT(out, equations.size()); - - for (IntIdEquation equation : equations) { - out.writeInt(equation.id); - IntIdResult rhs = equation.rhs; - if (rhs instanceof IntIdFinal) { - IntIdFinal finalResult = (IntIdFinal)rhs; - out.writeBoolean(true); // final flag - DataInputOutputUtil.writeINT(out, finalResult.value.ordinal()); - } else { - IntIdPending pendResult = (IntIdPending)rhs; - out.writeBoolean(false); // pending flag - DataInputOutputUtil.writeINT(out, pendResult.delta.length); - - for (IntIdComponent component : pendResult.delta) { - DataInputOutputUtil.writeINT(out, component.value.ordinal()); - int[] ids = component.ids; - DataInputOutputUtil.writeINT(out, ids.length); - for (int id : ids) { - out.writeInt(id); - } + public void save(@NotNull DataOutput out, IdEquation equation) throws IOException { + long id = equation.id; + int sign = id > 0 ? 1 : -1; + id = Math.abs(id); + int primaryId = (int)(id / BytecodeAnalysisConverter.SHIFT); + int secondaryId = (int)(id % BytecodeAnalysisConverter.SHIFT); + out.writeInt(sign * primaryId); + DataInputOutputUtil.writeINT(out, secondaryId); + IdResult rhs = equation.rhs; + if (rhs instanceof IdFinal) { + IdFinal finalResult = (IdFinal)rhs; + out.writeBoolean(true); // final flag + DataInputOutputUtil.writeINT(out, finalResult.value.ordinal()); + } else { + IdPending pendResult = (IdPending)rhs; + out.writeBoolean(false); // pending flag + DataInputOutputUtil.writeINT(out, pendResult.delta.length); + + for (IntIdComponent component : pendResult.delta) { + DataInputOutputUtil.writeINT(out, component.value.ordinal()); + long[] ids = component.ids; + DataInputOutputUtil.writeINT(out, ids.length); + for (long id1 : ids) { + sign = id1 > 0 ? 1 : -1; + id = Math.abs(id1); + primaryId = (int)(id / BytecodeAnalysisConverter.SHIFT); + secondaryId = (int)(id % BytecodeAnalysisConverter.SHIFT); + out.writeInt(sign * primaryId); + DataInputOutputUtil.writeINT(out, secondaryId); } } } } @Override - public Collection<IntIdEquation> read(@NotNull DataInput in) throws IOException { - - int size = DataInputOutputUtil.readINT(in); - ArrayList<IntIdEquation> result = new ArrayList<IntIdEquation>(size); - - for (int x = 0; x < size; x++) { - int equationId = in.readInt(); - boolean isFinal = in.readBoolean(); // flag - if (isFinal) { + public IdEquation read(@NotNull DataInput in) throws IOException { + long primaryId = in.readInt(); + int sign = primaryId > 0 ? 1 : -1; + primaryId = Math.abs(primaryId); + int secondaryId = DataInputOutputUtil.readINT(in); + long equationId = sign * (primaryId * BytecodeAnalysisConverter.SHIFT + secondaryId); + boolean isFinal = in.readBoolean(); // flag + if (isFinal) { + int ordinal = DataInputOutputUtil.readINT(in); + Value value = Value.values()[ordinal]; + return new IdEquation(equationId, new IdFinal(value)); + } else { + + int sumLength = DataInputOutputUtil.readINT(in); + IntIdComponent[] components = new IntIdComponent[sumLength]; + + for (int i = 0; i < sumLength; i++) { int ordinal = DataInputOutputUtil.readINT(in); Value value = Value.values()[ordinal]; - result.add(new IntIdEquation(equationId, new IntIdFinal(value))); - } else { - - int sumLength = DataInputOutputUtil.readINT(in); - IntIdComponent[] components = new IntIdComponent[sumLength]; - - for (int i = 0; i < sumLength; i++) { - int ordinal = DataInputOutputUtil.readINT(in); - Value value = Value.values()[ordinal]; - int componentSize = DataInputOutputUtil.readINT(in); - int[] ids = new int[componentSize]; - for (int j = 0; j < componentSize; j++) { - ids[j] = in.readInt(); - } - components[i] = new IntIdComponent(value, ids); + int componentSize = DataInputOutputUtil.readINT(in); + long[] ids = new long[componentSize]; + for (int j = 0; j < componentSize; j++) { + primaryId = in.readInt(); + sign = primaryId > 0 ? 1 : -1; + primaryId = Math.abs(primaryId); + secondaryId = DataInputOutputUtil.readINT(in); + long id = sign * (primaryId * BytecodeAnalysisConverter.SHIFT + secondaryId); + ids[j] = id; } - result.add(new IntIdEquation(equationId, new IntIdPending(components))); + components[i] = new IntIdComponent(value, ids); } + return new IdEquation(equationId, new IdPending(components)); } + } + } + + private static class SmartLongKeyDescriptor implements KeyDescriptor<Long>, DifferentSerializableBytesImplyNonEqualityPolicy { + @Override + public void save(@NotNull DataOutput out, Long value) throws IOException { + long id = value.longValue(); + int sign = id > 0 ? 1 : -1; + id = Math.abs(id); + int primaryId = (int)(id / BytecodeAnalysisConverter.SHIFT); + int secondaryId = (int)(id % BytecodeAnalysisConverter.SHIFT); + out.writeInt(primaryId * sign); + DataInputOutputUtil.writeINT(out, secondaryId); + } + + @Override + public Long read(@NotNull DataInput in) throws IOException { + long primaryId = in.readInt(); + int sign = primaryId > 0 ? 1 : -1; + primaryId = Math.abs(primaryId); + int secondaryId = DataInputOutputUtil.readINT(in); + return sign * (primaryId * BytecodeAnalysisConverter.SHIFT + secondaryId); + } - return result; + @Override + public int getHashCode(Long value) { + return value.hashCode(); + } + + @Override + public boolean isEqual(Long val1, Long val2) { + return val1.longValue() == val2.longValue(); } } } diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/ClassDataIndexer.java b/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/ClassDataIndexer.java index 5e74a8b5dbb3..9e656b98a7fb 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/ClassDataIndexer.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/ClassDataIndexer.java @@ -33,7 +33,7 @@ import static com.intellij.codeInspection.bytecodeAnalysis.ProjectBytecodeAnalys /** * @author lambdamix */ -public class ClassDataIndexer implements DataIndexer<Integer, Collection<IntIdEquation>, FileContent> { +public class ClassDataIndexer implements DataIndexer<Long, IdEquation, FileContent> { final BytecodeAnalysisConverter myConverter; public ClassDataIndexer(BytecodeAnalysisConverter converter) { @@ -42,25 +42,20 @@ public class ClassDataIndexer implements DataIndexer<Integer, Collection<IntIdEq @NotNull @Override - public Map<Integer, Collection<IntIdEquation>> map(@NotNull FileContent inputData) { - HashMap<Integer, Collection<IntIdEquation>> map = new HashMap<Integer, Collection<IntIdEquation>>(2); + public Map<Long, IdEquation> map(@NotNull FileContent inputData) { + HashMap<Long, IdEquation> map = new HashMap<Long, IdEquation>(); try { ClassEquations rawEquations = processClass(new ClassReader(inputData.getContent())); List<Equation<Key, Value>> rawParameterEquations = rawEquations.parameterEquations; List<Equation<Key, Value>> rawContractEquations = rawEquations.contractEquations; - Collection<IntIdEquation> idParameterEquations = new ArrayList<IntIdEquation>(rawParameterEquations.size()); - Collection<IntIdEquation> idContractEquations = new ArrayList<IntIdEquation>(rawContractEquations.size()); - - map.put(BytecodeAnalysisIndex.indexKey(inputData.getFile(), true), idParameterEquations); - map.put(BytecodeAnalysisIndex.indexKey(inputData.getFile(), false), idContractEquations); - - for (Equation<Key, Value> rawParameterEquation: rawParameterEquations) { - idParameterEquations.add(myConverter.convert(rawParameterEquation)); + IdEquation equation = myConverter.convert(rawParameterEquation); + map.put(equation.id, equation); } for (Equation<Key, Value> rawContractEquation: rawContractEquations) { - idContractEquations.add(myConverter.convert(rawContractEquation)); + IdEquation equation = myConverter.convert(rawContractEquation); + map.put(equation.id, equation); } } catch (ProcessCanceledException e) { diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/Contracts.java b/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/Contracts.java index c837b127b74b..7b6c52e75a63 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/Contracts.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/Contracts.java @@ -49,7 +49,7 @@ class InOutAnalysis extends Analysis<Result<Key, Value>> { } @Override - Result<Key, Value> combineResults(Result<Key, Value> delta, List<Result<Key, Value>> subResults) { + Result<Key, Value> combineResults(Result<Key, Value> delta, List<Result<Key, Value>> subResults) throws AnalyzerException { Result<Key, Value> result = null; for (Result<Key, Value> subResult : subResults) { if (result == null) { diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/Parameters.java b/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/Parameters.java index 08c52c4d49b0..625eb8eed977 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/Parameters.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/Parameters.java @@ -78,8 +78,9 @@ abstract class PResults { }; static final class ConditionalNPE implements PResult { final Set<Set<Key>> sop; - public ConditionalNPE(Set<Set<Key>> sop) { + public ConditionalNPE(Set<Set<Key>> sop) throws AnalyzerException { this.sop = sop; + checkLimit(sop); } public ConditionalNPE(Key key) { @@ -88,9 +89,19 @@ abstract class PResults { prod.add(key); sop.add(prod); } + + static void checkLimit(Set<Set<Key>> sop) throws AnalyzerException { + int size = 0; + for (Set<Key> keys : sop) { + size += keys.size(); + } + if (size > Analysis.EQUATION_SIZE_LIMIT) { + throw new AnalyzerException(null, "Equation size is too big"); + } + } } - static PResult join(PResult r1, PResult r2) { + static PResult join(PResult r1, PResult r2) throws AnalyzerException { if (Identity == r1) return r2; if (Identity == r2) return r1; if (Return == r1) return Return; @@ -102,7 +113,7 @@ abstract class PResults { return new ConditionalNPE(join(cnpe1.sop, cnpe2.sop)); } - static PResult meet(PResult r1, PResult r2) { + static PResult meet(PResult r1, PResult r2) throws AnalyzerException { if (Identity == r1) return r2; if (Return == r1) return r2; if (Return == r2) return r1; @@ -130,7 +141,7 @@ class NonNullInAnalysis extends Analysis<PResult> { } @Override - PResult combineResults(PResult delta, List<PResult> subResults) { + PResult combineResults(PResult delta, List<PResult> subResults) throws AnalyzerException { PResult subResult = Identity; for (PResult sr : subResults) { subResult = join(subResult, sr); diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/ProjectBytecodeAnalysis.java b/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/ProjectBytecodeAnalysis.java index 86b9dd101fd9..23d9d5a9e1c5 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/ProjectBytecodeAnalysis.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/ProjectBytecodeAnalysis.java @@ -15,34 +15,31 @@ */ package com.intellij.codeInspection.bytecodeAnalysis; -import com.intellij.ProjectTopics; import com.intellij.codeInsight.AnnotationUtil; import com.intellij.codeInspection.dataFlow.ControlFlowAnalyzer; import com.intellij.openapi.components.ServiceManager; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.project.Project; -import com.intellij.openapi.roots.ContentIterator; -import com.intellij.openapi.roots.ModuleRootAdapter; -import com.intellij.openapi.roots.ModuleRootEvent; import com.intellij.openapi.util.Key; import com.intellij.openapi.util.ModificationTracker; -import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.*; import com.intellij.psi.search.GlobalSearchScope; import com.intellij.psi.search.ProjectScope; import com.intellij.psi.util.CachedValueProvider; import com.intellij.psi.util.CachedValuesManager; +import com.intellij.psi.util.PsiFormatUtil; import com.intellij.util.IncorrectOperationException; +import com.intellij.util.containers.LongStack; import com.intellij.util.indexing.FileBasedIndex; -import com.intellij.util.messages.MessageBusConnection; -import gnu.trove.TIntHashSet; -import gnu.trove.TIntObjectHashMap; +import gnu.trove.TLongArrayList; +import gnu.trove.TLongHashSet; +import gnu.trove.TLongObjectHashMap; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.io.IOException; -import java.util.Collection; +import java.util.List; /** * @author lambdamix @@ -50,93 +47,15 @@ import java.util.Collection; public class ProjectBytecodeAnalysis { public static final Logger LOG = Logger.getInstance("#com.intellij.codeInspection.bytecodeAnalysis"); public static final Key<Boolean> INFERRED_ANNOTATION = Key.create("INFERRED_ANNOTATION"); + public static final int EQUATIONS_LIMIT = 1000; private final Project myProject; - private volatile Annotations myAnnotations = null; - public static ProjectBytecodeAnalysis getInstance(@NotNull Project project) { return ServiceManager.getService(project, ProjectBytecodeAnalysis.class); } public ProjectBytecodeAnalysis(Project project) { myProject = project; - final MessageBusConnection connection = myProject.getMessageBus().connect(); - connection.subscribe(ProjectTopics.PROJECT_ROOTS, new ModuleRootAdapter() { - @Override - public void rootsChanged(ModuleRootEvent event) { - unloadAnnotations(); - } - }); - } - - private void loadAnnotations() { - Annotations annotations = new Annotations(); - loadParameterAnnotations(annotations); - loadContractAnnotations(annotations); - myAnnotations = annotations; - LOG.debug("NotNull annotations: " + myAnnotations.notNulls.size()); - LOG.debug("Contract annotations: " + myAnnotations.contracts.size()); - } - - private void unloadAnnotations() { - myAnnotations = null; - LOG.debug("unloaded"); - } - - private void loadParameterAnnotations(Annotations annotations) { - LOG.debug("initializing parameter annotations"); - final IntIdSolver solver = new IntIdSolver(new ELattice<Value>(Value.NotNull, Value.Top)); - - processValues(true, new FileBasedIndex.ValueProcessor<Collection<IntIdEquation>>() { - @Override - public boolean process(VirtualFile file, Collection<IntIdEquation> value) { - for (IntIdEquation intIdEquation : value) { - solver.addEquation(intIdEquation); - } - return true; - } - }); - - LOG.debug("parameter equations are constructed"); - LOG.debug("equations: " + solver.getSize()); - TIntObjectHashMap<Value> solutions = solver.solve(); - LOG.debug("parameter equations are solved"); - BytecodeAnalysisConverter.getInstance().addAnnotations(solutions, annotations); - } - - private void processValues(final boolean parameters, final FileBasedIndex.ValueProcessor<Collection<IntIdEquation>> processor) { - final GlobalSearchScope libScope = ProjectScope.getLibrariesScope(myProject); - final FileBasedIndex index = FileBasedIndex.getInstance(); - index.iterateIndexableFiles(new ContentIterator() { - @Override - public boolean processFile(VirtualFile fileOrDir) { - ProgressManager.checkCanceled(); - if (!fileOrDir.isDirectory() && libScope.contains(fileOrDir)) { - index.processValues(BytecodeAnalysisIndex.NAME, BytecodeAnalysisIndex.indexKey(fileOrDir, parameters), - fileOrDir, processor, GlobalSearchScope.fileScope(myProject, fileOrDir)); - } - return false; - } - }, myProject, null); - } - - private void loadContractAnnotations(Annotations annotations) { - LOG.debug("initializing contract annotations"); - final IntIdSolver solver = new IntIdSolver(new ELattice<Value>(Value.Bot, Value.Top)); - processValues(false, new FileBasedIndex.ValueProcessor<Collection<IntIdEquation>>() { - @Override - public boolean process(VirtualFile file, Collection<IntIdEquation> value) { - for (IntIdEquation intIdEquation : value) { - solver.addEquation(intIdEquation); - } - return true; - } - }); - LOG.debug("contract equations are constructed"); - LOG.debug("equations: " + solver.getSize()); - TIntObjectHashMap<Value> solutions = solver.solve(); - LOG.debug("contract equations are solved"); - BytecodeAnalysisConverter.getInstance().addAnnotations(solutions, annotations); } @Nullable @@ -144,11 +63,14 @@ public class ProjectBytecodeAnalysis { if (!(listOwner instanceof PsiCompiledElement)) { return null; } - if (annotationFQN.equals("org.jetbrains.annotations.NotNull")) { - return findNotNullAnnotation(listOwner); - } - else if (annotationFQN.equals("org.jetbrains.annotations.Contract")) { - return findContractAnnotation(listOwner); + if (annotationFQN.equals(AnnotationUtil.NOT_NULL) || annotationFQN.equals(ControlFlowAnalyzer.ORG_JETBRAINS_ANNOTATIONS_CONTRACT)) { + PsiAnnotation[] annotations = findInferredAnnotations(listOwner); + for (PsiAnnotation annotation : annotations) { + if (annotationFQN.equals(annotation.getQualifiedName())) { + return annotation; + } + } + return null; } else { return null; @@ -156,26 +78,30 @@ public class ProjectBytecodeAnalysis { } @NotNull - public PsiAnnotation[] findInferredAnnotations(@NotNull PsiModifierListOwner listOwner) { + public PsiAnnotation[] findInferredAnnotations(@NotNull final PsiModifierListOwner listOwner) { if (!(listOwner instanceof PsiCompiledElement)) { return PsiAnnotation.EMPTY_ARRAY; } - return collectInferredAnnotations(listOwner); + return CachedValuesManager.getCachedValue(listOwner, new CachedValueProvider<PsiAnnotation[]>() { + @Nullable + @Override + public Result<PsiAnnotation[]> compute() { + return Result.create(collectInferredAnnotations(listOwner), listOwner); + } + }); } - // TODO the best way to synchronize? @NotNull - private synchronized PsiAnnotation[] collectInferredAnnotations(PsiModifierListOwner listOwner) { - if (myAnnotations == null) { - loadAnnotations(); - } + private PsiAnnotation[] collectInferredAnnotations(PsiModifierListOwner listOwner) { try { - int key = getKey(listOwner); - if (key == -1) { + long ownerKey = getKey(listOwner); + if (ownerKey == -1) { return PsiAnnotation.EMPTY_ARRAY; } - boolean notNull = myAnnotations.notNulls.contains(key); - String contractValue = myAnnotations.contracts.get(key); + TLongArrayList allKeys = contractKeys(listOwner, ownerKey); + Annotations annotations = loadAnnotations(listOwner, ownerKey, allKeys); + boolean notNull = annotations.notNulls.contains(ownerKey); + String contractValue = annotations.contracts.get(ownerKey); if (notNull && contractValue != null) { return new PsiAnnotation[]{ @@ -201,6 +127,11 @@ public class ProjectBytecodeAnalysis { LOG.debug(e); return PsiAnnotation.EMPTY_ARRAY; } + catch (EquationsLimitException e) { + String externalName = PsiFormatUtil.getExternalName(listOwner, false, Integer.MAX_VALUE); + LOG.info("Too many equations for " + externalName); + return PsiAnnotation.EMPTY_ARRAY; + } } private PsiAnnotation getNotNullAnnotation() { @@ -213,54 +144,15 @@ public class ProjectBytecodeAnalysis { }); } - @Nullable - private synchronized PsiAnnotation findNotNullAnnotation(PsiModifierListOwner listOwner) { - if (myAnnotations == null) { - loadAnnotations(); - } - try { - int key = getKey(listOwner); - if (key == -1) { - return null; - } - return myAnnotations.notNulls.contains(key) ? getNotNullAnnotation() : null; - } - catch (IOException e) { - LOG.debug(e); - return null; - } - } - - @Nullable - private synchronized PsiAnnotation findContractAnnotation(PsiModifierListOwner listOwner) { - if (myAnnotations == null) { - loadAnnotations(); - } - try { - int key = getKey(listOwner); - if (key == -1) { - return null; - } - String contractValue = myAnnotations.contracts.get(key); - return contractValue != null ? createContractAnnotation(contractValue) : null; - } - catch (IOException e) { - LOG.debug(e); - return null; - } - } - public PsiAnnotation createContractAnnotation(String contractValue) { return createAnnotationFromText("@org.jetbrains.annotations.Contract(" + contractValue + ")"); } - public static int getKey(@NotNull PsiModifierListOwner owner) throws IOException { + public static long getKey(@NotNull PsiModifierListOwner owner) throws IOException { LOG.assertTrue(owner instanceof PsiCompiledElement, owner); - if (owner instanceof PsiMethod) { return BytecodeAnalysisConverter.getInstance().mkPsiKey((PsiMethod)owner, new Out()); } - if (owner instanceof PsiParameter) { PsiElement parent = owner.getParent(); if (parent instanceof PsiParameterList) { @@ -275,6 +167,80 @@ public class ProjectBytecodeAnalysis { return -1; } + public static TLongArrayList contractKeys(@NotNull PsiModifierListOwner owner, long primaryKey) throws IOException { + if (owner instanceof PsiMethod) { + TLongArrayList result = BytecodeAnalysisConverter.getInstance().mkInOutKeys((PsiMethod)owner, primaryKey); + result.add(primaryKey); + return result; + } + TLongArrayList result = new TLongArrayList(1); + result.add(primaryKey); + return result; + } + + private Annotations loadAnnotations(@NotNull PsiModifierListOwner owner, long key, TLongArrayList allKeys) + throws IOException, EquationsLimitException { + Annotations result = new Annotations(); + if (owner instanceof PsiParameter) { + final Solver solver = new Solver(new ELattice<Value>(Value.NotNull, Value.Top)); + collectEquations(allKeys, solver); + TLongObjectHashMap<Value> solutions = solver.solve(); + BytecodeAnalysisConverter.getInstance().addParameterAnnotations(solutions, result); + } else if (owner instanceof PsiMethod) { + final Solver solver = new Solver(new ELattice<Value>(Value.Bot, Value.Top)); + collectEquations(allKeys, solver); + TLongObjectHashMap<Value> solutions = solver.solve(); + BytecodeAnalysisConverter.getInstance().addMethodAnnotations(solutions, result, key, + ((PsiMethod)owner).getParameterList().getParameters().length); + } + return result; + } + + private void collectEquations(TLongArrayList keys, Solver solver) throws EquationsLimitException { + GlobalSearchScope librariesScope = ProjectScope.getLibrariesScope(myProject); + TLongHashSet queued = new TLongHashSet(); + LongStack queue = new LongStack(); + + for (int i = 0; i < keys.size(); i++) { + long key = keys.get(i); + queue.push(key); + queued.add(key); + // stable/unstable + queue.push(-key); + queued.add(-key); + } + + FileBasedIndex index = FileBasedIndex.getInstance(); + while (!queue.empty()) { + if (queued.size() > EQUATIONS_LIMIT) { + throw new EquationsLimitException(); + } + ProgressManager.checkCanceled(); + List<IdEquation> equations = index.getValues(BytecodeAnalysisIndex.NAME, queue.pop(), librariesScope); + for (IdEquation equation : equations) { + IdResult rhs = equation.rhs; + solver.addEquation(equation); + if (rhs instanceof IdPending) { + IdPending intIdPending = (IdPending)rhs; + for (IntIdComponent component : intIdPending.delta) { + for (long depKey : component.ids) { + if (!queued.contains(depKey)) { + queue.push(depKey); + queued.add(depKey); + } + // stable/unstable + long swapped = -depKey; + if (!queued.contains(swapped)) { + queue.push(swapped); + queued.add(swapped); + } + } + } + } + } + } + } + @NotNull private PsiAnnotation createAnnotationFromText(@NotNull final String text) throws IncorrectOperationException { PsiAnnotation annotation = JavaPsiFacade.getElementFactory(myProject).createAnnotationFromText(text, null); @@ -285,7 +251,9 @@ public class ProjectBytecodeAnalysis { class Annotations { // @NotNull keys - final TIntHashSet notNulls = new TIntHashSet(); + final TLongHashSet notNulls = new TLongHashSet(); // @Contracts - final TIntObjectHashMap<String> contracts = new TIntObjectHashMap<String>(); + final TLongObjectHashMap<String> contracts = new TLongObjectHashMap<String>(); } + +class EquationsLimitException extends Exception {} diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/Solver.java b/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/Solver.java index 47c97790d102..1dec1de8a606 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/Solver.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/Solver.java @@ -15,10 +15,12 @@ */ package com.intellij.codeInspection.bytecodeAnalysis; -import com.intellij.util.containers.IntStack; -import com.intellij.util.containers.IntToIntSetMap; -import gnu.trove.TIntObjectHashMap; +import com.intellij.util.containers.LongStack; +import gnu.trove.TLongHashSet; +import gnu.trove.TLongIterator; +import gnu.trove.TLongObjectHashMap; import org.jetbrains.annotations.NotNull; +import org.jetbrains.org.objectweb.asm.tree.analysis.AnalyzerException; import java.util.*; @@ -49,9 +51,9 @@ final class ELattice<T extends Enum<T>> { // component specialized for ints final class IntIdComponent { Value value; - final int[] ids; + final long[] ids; - IntIdComponent(Value value, int[] ids) { + IntIdComponent(Value value, long[] ids) { this.value = value; this.ids = ids; } @@ -74,7 +76,7 @@ final class IntIdComponent { return value.ordinal() + Arrays.hashCode(ids); } - public boolean remove(int id) { + public boolean remove(long id) { return IdUtils.remove(ids, id); } @@ -89,18 +91,18 @@ final class IntIdComponent { class IdUtils { // removed value - static final int nullId = 0; + static final long nullId = 0; - static boolean contains(int[] ids, int id) { - for (int id1 : ids) { + static boolean contains(long[] ids, int id) { + for (long id1 : ids) { if (id1 == id) return true; } return false; } - static boolean isEmpty(int[] ids) { - for (int id : ids) { + static boolean isEmpty(long[] ids) { + for (long id : ids) { if (id != nullId) return false; } return true; @@ -117,7 +119,7 @@ class IdUtils { return result; } - static boolean remove(int[] ids, int id) { + static boolean remove(long[] ids, long id) { boolean removed = false; for (int i = 0; i < ids.length; i++) { if (ids[i] == id) { @@ -137,7 +139,7 @@ class ResultUtil<Id, T extends Enum<T>> { top = lattice.top; } - Result<Id, T> join(Result<Id, T> r1, Result<Id, T> r2) { + Result<Id, T> join(Result<Id, T> r1, Result<Id, T> r2) throws AnalyzerException { if (r1 instanceof Final && ((Final) r1).value == top) { return r1; } @@ -166,8 +168,19 @@ class ResultUtil<Id, T extends Enum<T>> { Set<Product<Id, T>> sum = new HashSet<Product<Id, T>>(); sum.addAll(pending1.sum); sum.addAll(pending2.sum); + checkLimit(sum); return new Pending<Id, T>(sum); } + + private void checkLimit(Set<Product<Id, T>> sum) throws AnalyzerException { + int size = 0; + for (Product<Id, T> prod : sum) { + size += prod.ids.size(); + } + if (size > Analysis.EQUATION_SIZE_LIMIT) { + throw new AnalyzerException(null, "Equation size is too big"); + } + } } final class Product<K, V> { @@ -222,11 +235,11 @@ final class Pending<Id, T> implements Result<Id, T> { } -interface IntIdResult {} +interface IdResult {} // this just wrapper, no need for this really -final class IntIdFinal implements IntIdResult { +final class IdFinal implements IdResult { final Value value; - public IntIdFinal(Value value) { + public IdFinal(Value value) { this.value = value; } @@ -235,7 +248,7 @@ final class IntIdFinal implements IntIdResult { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; - IntIdFinal that = (IntIdFinal)o; + IdFinal that = (IdFinal)o; if (value != that.value) return false; @@ -253,19 +266,19 @@ final class IntIdFinal implements IntIdResult { } } -final class IntIdPending implements IntIdResult { +final class IdPending implements IdResult { final IntIdComponent[] delta; - IntIdPending(IntIdComponent[] delta) { + IdPending(IntIdComponent[] delta) { this.delta = delta; } @Override public boolean equals(Object o) { if (this == o) return true; - if (!(o instanceof IntIdPending)) return false; - IntIdPending pending = (IntIdPending)o; - return !Arrays.equals(delta, pending.delta); + if (!(o instanceof IdPending)) return false; + IdPending pending = (IdPending)o; + return Arrays.equals(delta, pending.delta); } @Override @@ -273,20 +286,20 @@ final class IntIdPending implements IntIdResult { return Arrays.hashCode(delta); } - IntIdPending copy() { + IdPending copy() { IntIdComponent[] delta1 = new IntIdComponent[delta.length]; for (int i = 0; i < delta.length; i++) { delta1[i] = delta[i].copy(); } - return new IntIdPending(delta1); + return new IdPending(delta1); } } -final class IntIdEquation { - final int id; - final IntIdResult rhs; +final class IdEquation { + final long id; + final IdResult rhs; - IntIdEquation(int id, IntIdResult rhs) { + IdEquation(long id, IdResult rhs) { this.id = id; this.rhs = rhs; } @@ -294,9 +307,9 @@ final class IntIdEquation { @Override public boolean equals(Object o) { if (this == o) return true; - if (!(o instanceof IntIdEquation)) return false; + if (!(o instanceof IdEquation)) return false; - IntIdEquation equation = (IntIdEquation)o; + IdEquation equation = (IdEquation)o; if (id != equation.id) return false; if (!rhs.equals(equation.rhs)) return false; @@ -306,9 +319,7 @@ final class IntIdEquation { @Override public int hashCode() { - int result = id; - result = 31 * result + rhs.hashCode(); - return result; + return 31 * ((int)(id ^ (id >>> 32))) + rhs.hashCode(); } } @@ -337,41 +348,46 @@ final class Equation<Id, T> { } } -final class IntIdSolver { +final class Solver { private int size = 0; private final ELattice<Value> lattice; - private final IntToIntSetMap dependencies = new IntToIntSetMap(10000, 0.5f); - private final TIntObjectHashMap<IntIdPending> pending = new TIntObjectHashMap<IntIdPending>(); - private final TIntObjectHashMap<Value> solved = new TIntObjectHashMap<Value>(); - private final IntStack moving = new IntStack(); + private final TLongObjectHashMap<TLongHashSet> dependencies = new TLongObjectHashMap<TLongHashSet>(); + private final TLongObjectHashMap<IdPending> pending = new TLongObjectHashMap<IdPending>(); + private final TLongObjectHashMap<Value> solved = new TLongObjectHashMap<Value>(); + private final LongStack moving = new LongStack(); int getSize() { return size; } - IntIdSolver(ELattice<Value> lattice) { + Solver(ELattice<Value> lattice) { this.lattice = lattice; } - void addEquation(IntIdEquation equation) { + void addEquation(IdEquation equation) { size ++; - IntIdResult rhs = equation.rhs; - if (rhs instanceof IntIdFinal) { - solved.put(equation.id, ((IntIdFinal) rhs).value); + IdResult rhs = equation.rhs; + if (rhs instanceof IdFinal) { + solved.put(equation.id, ((IdFinal) rhs).value); moving.push(equation.id); - } else if (rhs instanceof IntIdPending) { - IntIdPending pendResult = ((IntIdPending)rhs).copy(); - IntIdResult norm = normalize(pendResult.delta); - if (norm instanceof IntIdFinal) { - solved.put(equation.id, ((IntIdFinal) norm).value); + } else if (rhs instanceof IdPending) { + IdPending pendResult = ((IdPending)rhs).copy(); + IdResult norm = normalize(pendResult.delta); + if (norm instanceof IdFinal) { + solved.put(equation.id, ((IdFinal) norm).value); moving.push(equation.id); } else { - IntIdPending pendResult1 = ((IntIdPending)rhs).copy(); + IdPending pendResult1 = ((IdPending)rhs).copy(); for (IntIdComponent component : pendResult1.delta) { - for (int trigger : component.ids) { - dependencies.addOccurence(trigger, equation.id); + for (long trigger : component.ids) { + TLongHashSet set = dependencies.get(trigger); + if (set == null) { + set = new TLongHashSet(); + dependencies.put(trigger, set); + } + set.add(equation.id); } pending.put(equation.id, pendResult1); } @@ -379,31 +395,35 @@ final class IntIdSolver { } } - TIntObjectHashMap<Value> solve() { + TLongObjectHashMap<Value> solve() { while (!moving.empty()) { - int id = moving.pop(); + long id = moving.pop(); Value value = solved.get(id); boolean stable = id > 0; - int[] pIds = stable ? new int[]{id, -id} : new int[]{-id, id}; + long[] pIds = stable ? new long[]{id, -id} : new long[]{-id, id}; Value[] pVals = stable ? new Value[]{value, value} : new Value[]{value, lattice.top}; for (int i = 0; i < pIds.length; i++) { - int pId = pIds[i]; + long pId = pIds[i]; Value pVal = pVals[i]; - // todo - remove - int[] dIds = dependencies.get(pId); - for (int dId : dIds) { - IntIdPending pend = pending.remove(dId); + TLongHashSet dIds = dependencies.get(pId); + if (dIds == null) { + continue; + } + TLongIterator dIdsIterator = dIds.iterator(); + while (dIdsIterator.hasNext()) { + long dId = dIdsIterator.next(); + IdPending pend = pending.remove(dId); if (pend != null) { - IntIdResult pend1 = substitute(pend, pId, pVal); - if (pend1 instanceof IntIdFinal) { - IntIdFinal fi = (IntIdFinal)pend1; + IdResult pend1 = substitute(pend, pId, pVal); + if (pend1 instanceof IdFinal) { + IdFinal fi = (IdFinal)pend1; solved.put(dId, fi.value); moving.push(dId); } else { - pending.put(dId, (IntIdPending)pend1); + pending.put(dId, (IdPending)pend1); } } } @@ -414,7 +434,7 @@ final class IntIdSolver { } // substitute id -> value into pending - IntIdResult substitute(IntIdPending pending, int id, Value value) { + IdResult substitute(IdPending pending, long id, Value value) { IntIdComponent[] sum = pending.delta; for (IntIdComponent intIdComponent : sum) { if (intIdComponent.remove(id)) { @@ -424,7 +444,7 @@ final class IntIdSolver { return normalize(sum); } - IntIdResult normalize(IntIdComponent[] sum) { + IdResult normalize(IntIdComponent[] sum) { Value acc = lattice.bot; boolean computableNow = true; for (IntIdComponent prod : sum) { @@ -434,7 +454,7 @@ final class IntIdSolver { computableNow = false; } } - return (acc == lattice.top || computableNow) ? new IntIdFinal(acc) : new IntIdPending(sum); + return (acc == lattice.top || computableNow) ? new IdFinal(acc) : new IdPending(sum); } } diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/ContractInference.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/ContractInference.java index a1c908837ad0..bd207e58a0fe 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/ContractInference.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/ContractInference.java @@ -71,7 +71,23 @@ class ContractInferenceInterpreter { if (statements.length == 1) { if (statements[0] instanceof PsiReturnStatement) { List<MethodContract> result = handleDelegation(((PsiReturnStatement)statements[0]).getReturnValue(), false); - if (result != null) return result; + if (result != null) { + PsiTypeElement typeElement = myMethod.getReturnTypeElement(); + final boolean returningObject = typeElement == null || !(typeElement.getType() instanceof PsiClassType); + return ContainerUtil.findAll(result, new Condition<MethodContract>() { + @Override + public boolean value(MethodContract contract) { + if ((contract.returnValue == NULL_VALUE || contract.returnValue == NOT_NULL_VALUE) && returningObject) { + return false; + } + if ((contract.returnValue == TRUE_VALUE || contract.returnValue == FALSE_VALUE) && !returningObject) { + return false; + } + + return true; + } + }); + } } else if (statements[0] instanceof PsiExpressionStatement && ((PsiExpressionStatement)statements[0]).getExpression() instanceof PsiMethodCallExpression) { List<MethodContract> result = handleDelegation(((PsiExpressionStatement)statements[0]).getExpression(), false); @@ -132,6 +148,9 @@ class ContractInferenceInterpreter { } else { answer = withConstraint(answer, paramIndex, argConstraint); + if (answer == null) { + return null; + } } } } diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/MethodContract.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/MethodContract.java index 691c2f00d985..1167210f917e 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/MethodContract.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/MethodContract.java @@ -89,10 +89,16 @@ public class MethodContract { throw new ParseException("A contract clause must be in form arg1, ..., argN -> return-value"); } - String[] argStrings = clause.substring(0, arrowIndex).split(","); - ValueConstraint[] args = new ValueConstraint[argStrings.length]; - for (int i = 0; i < args.length; i++) { - args[i] = parseConstraint(argStrings[i]); + String beforeArrow = clause.substring(0, arrowIndex); + ValueConstraint[] args; + if (StringUtil.isNotEmpty(beforeArrow)) { + String[] argStrings = beforeArrow.split(","); + args = new ValueConstraint[argStrings.length]; + for (int i = 0; i < args.length; i++) { + args[i] = parseConstraint(argStrings[i]); + } + } else { + args = new ValueConstraint[0]; } result.add(new MethodContract(args, parseConstraint(clause.substring(arrowIndex + arrow.length())))); } diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/StandardInstructionVisitor.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/StandardInstructionVisitor.java index 013e24cbe07a..36449128b494 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/StandardInstructionVisitor.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/StandardInstructionVisitor.java @@ -500,21 +500,19 @@ public class StandardInstructionVisitor extends InstructionVisitor { PsiType varType = var.getVariableType(); if (!(varType instanceof PsiPrimitiveType)) return null; + + if (varType == PsiType.FLOAT || varType == PsiType.DOUBLE) return null; double minValue = varType == PsiType.BYTE ? Byte.MIN_VALUE : varType == PsiType.SHORT ? Short.MIN_VALUE : varType == PsiType.INT ? Integer.MIN_VALUE : varType == PsiType.CHAR ? Character.MIN_VALUE : - varType == PsiType.LONG ? Long.MIN_VALUE : - varType == PsiType.FLOAT ? Float.MIN_VALUE : - Double.MIN_VALUE; + Long.MIN_VALUE; double maxValue = varType == PsiType.BYTE ? Byte.MAX_VALUE : varType == PsiType.SHORT ? Short.MAX_VALUE : varType == PsiType.INT ? Integer.MAX_VALUE : varType == PsiType.CHAR ? Character.MAX_VALUE : - varType == PsiType.LONG ? Long.MAX_VALUE : - varType == PsiType.FLOAT ? Float.MAX_VALUE : - Double.MAX_VALUE; + Long.MAX_VALUE; return checkComparisonWithKnownRange(instruction, runner, memState, opSign, comparedWith, minValue, maxValue); } diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/xml/DeprecatedClassUsageInspection.java b/java/java-analysis-impl/src/com/intellij/codeInspection/xml/DeprecatedClassUsageInspection.java index 0a914c0d4477..8c9e4a72dfef 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/xml/DeprecatedClassUsageInspection.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/xml/DeprecatedClassUsageInspection.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2013 JetBrains s.r.o. + * Copyright 2000-2014 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. @@ -20,7 +20,6 @@ import com.intellij.codeInspection.ProblemsHolder; import com.intellij.codeInspection.XmlSuppressableInspectionTool; import com.intellij.codeInspection.deprecation.DeprecationInspection; import com.intellij.psi.*; -import com.intellij.psi.xml.XmlAttribute; import com.intellij.psi.xml.XmlAttributeValue; import com.intellij.psi.xml.XmlTag; import com.intellij.util.ArrayUtil; @@ -46,11 +45,6 @@ public class DeprecatedClassUsageInspection extends XmlSuppressableInspectionToo } @Override - public void visitXmlAttribute(XmlAttribute attribute) { - checkReferences(attribute, holder); - } - - @Override public void visitXmlAttributeValue(XmlAttributeValue value) { checkReferences(value, holder); } |