mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-04-19 13:02:30 +07:00
Introduced AggressiveHardCodedPurity under registry key (IDEA-CR-21434)
New key = java.annotations.inference.aggressive.hardcoded.purity (true by default). Implies purity for Object.toString(), Iterable.iterator(), Iterator.hasNext() and this-changing Iterator.next(). Parameter-changing mode supported for hardcoded purity.
This commit is contained in:
@@ -52,8 +52,14 @@ import static com.intellij.codeInspection.bytecodeAnalysis.ProjectBytecodeAnalys
|
||||
public class BytecodeAnalysisIndex extends ScalarIndexExtension<HMethod> {
|
||||
private static final ID<HMethod, Void> NAME = ID.create("bytecodeAnalysis");
|
||||
private static final HKeyDescriptor KEY_DESCRIPTOR = new HKeyDescriptor();
|
||||
|
||||
private static final int VERSION = 4; // change when inference algorithm changes
|
||||
private static final int VERSION_MODIFIER = HardCodedPurity.AGGRESSIVE_HARDCODED_PURITY ? 1 : 0;
|
||||
private static final int VERSION_MODIFIER_MAX = 2;
|
||||
private static final int FINAL_VERSION = VERSION * VERSION_MODIFIER_MAX + VERSION_MODIFIER;
|
||||
|
||||
private static final VirtualFileGist<Map<HMethod, Equations>> ourGist = GistManager.getInstance().newVirtualFileGist(
|
||||
"BytecodeAnalysisIndex", 7, new EquationsExternalizer(), new ClassDataIndexer());
|
||||
"BytecodeAnalysisIndex", FINAL_VERSION, new EquationsExternalizer(), new ClassDataIndexer());
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,120 @@
|
||||
/*
|
||||
* Copyright 2000-2017 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.openapi.util.Couple;
|
||||
import com.intellij.openapi.util.registry.Registry;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import org.jetbrains.org.objectweb.asm.tree.FieldInsnNode;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
class HardCodedPurity {
|
||||
static final boolean AGGRESSIVE_HARDCODED_PURITY = Registry.is("java.annotations.inference.aggressive.hardcoded.purity", true);
|
||||
|
||||
private static Set<Couple<String>> ownedFields = ContainerUtil.set(
|
||||
new Couple<>("java/lang/AbstractStringBuilder", "value")
|
||||
);
|
||||
private static Set<Method> thisChangingMethods = ContainerUtil.set(
|
||||
new Method("java/lang/Throwable", "fillInStackTrace", "()Ljava/lang/Throwable;")
|
||||
);
|
||||
// Assumed that all these methods are not only pure, but return object which could be safely modified
|
||||
private static Set<Method> pureMethods = ContainerUtil.set(
|
||||
// Maybe overloaded and be not pure, but this would be definitely bad code style
|
||||
// Used in Throwable(Throwable) ctor, so this helps to infer purity of many exception constructors
|
||||
new Method("java/lang/Throwable", "toString", "()Ljava/lang/String;"),
|
||||
// Declared in final class StringBuilder
|
||||
new Method("java/lang/StringBuilder", "toString", "()Ljava/lang/String;"),
|
||||
new Method("java/lang/StringBuffer", "toString", "()Ljava/lang/String;"),
|
||||
// Native
|
||||
new Method("java/lang/Object", "getClass", "()Ljava/lang/Class;"),
|
||||
new Method("java/lang/Class", "getComponentType", "()Ljava/lang/Class;"),
|
||||
new Method("java/lang/reflect/Array", "newInstance", "(Ljava/lang/Class;I)Ljava/lang/Object;"),
|
||||
new Method("java/lang/reflect/Array", "newInstance", "(Ljava/lang/Class;[I)Ljava/lang/Object;"),
|
||||
new Method("java/lang/Float", "floatToRawIntBits", "(F)I"),
|
||||
new Method("java/lang/Float", "intBitsToFloat", "(I)F"),
|
||||
new Method("java/lang/Double", "doubleToRawLongBits", "(D)J"),
|
||||
new Method("java/lang/Double", "longBitsToDouble", "(J)D")
|
||||
);
|
||||
private static Map<Method, Set<EffectQuantum>> solutions = new HashMap<>();
|
||||
private static Set<EffectQuantum> thisChange = Collections.singleton(EffectQuantum.ThisChangeQuantum);
|
||||
|
||||
static {
|
||||
// Native
|
||||
solutions.put(new Method("java/lang/System", "arraycopy", "(Ljava/lang/Object;ILjava/lang/Object;II)V"),
|
||||
Collections.singleton(new EffectQuantum.ParamChangeQuantum(2)));
|
||||
solutions.put(new Method("java/lang/Object", "hashCode", "()I"), Collections.emptySet());
|
||||
}
|
||||
|
||||
static HardCodedPurity getInstance() {
|
||||
return AGGRESSIVE_HARDCODED_PURITY ? new AggressiveHardCodedPurity() : new HardCodedPurity();
|
||||
}
|
||||
|
||||
Set<EffectQuantum> getHardCodedSolution(Method method) {
|
||||
return isThisChangingMethod(method) ? thisChange : isPureMethod(method) ? Collections.emptySet() : solutions.get(method);
|
||||
}
|
||||
|
||||
boolean isThisChangingMethod(Method method) {
|
||||
return isBuilderChainCall(method) || thisChangingMethods.contains(method);
|
||||
}
|
||||
|
||||
boolean isBuilderChainCall(Method method) {
|
||||
// Those methods are virtual, thus contracts cannot be inferred automatically,
|
||||
// but all possible implementations are controlled
|
||||
// (only final classes j.l.StringBuilder and j.l.StringBuffer extend package-private j.l.AbstractStringBuilder)
|
||||
return (method.internalClassName.equals("java/lang/StringBuilder") || method.internalClassName.equals("java/lang/StringBuffer")) &&
|
||||
method.methodName.startsWith("append");
|
||||
}
|
||||
|
||||
boolean isPureMethod(Method method) {
|
||||
return pureMethods.contains(method);
|
||||
}
|
||||
|
||||
boolean isOwnedField(FieldInsnNode fieldInsn) {
|
||||
return ownedFields.contains(new Couple<>(fieldInsn.owner, fieldInsn.name));
|
||||
}
|
||||
|
||||
static class AggressiveHardCodedPurity extends HardCodedPurity {
|
||||
static final Set<String> ITERABLES = ContainerUtil.set("java/lang/Iterable", "java/util/Collection",
|
||||
"java/util/List", "java/util/Set", "java/util/ArrayList",
|
||||
"java/util/HashSet", "java/util/AbstractList",
|
||||
"java/util/AbstractSet", "java/util/TreeSet");
|
||||
|
||||
@Override
|
||||
boolean isThisChangingMethod(Method method) {
|
||||
if (method.methodName.equals("next") && method.methodDesc.startsWith("()") && method.internalClassName.equals("java/util/Iterator")) {
|
||||
return true;
|
||||
}
|
||||
return super.isThisChangingMethod(method);
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean isPureMethod(Method method) {
|
||||
if (method.methodName.equals("toString") && method.methodDesc.equals("()Ljava/lang/String;")) return true;
|
||||
if (method.methodName.equals("iterator") && method.methodDesc.equals("()Ljava/util/Iterator;") &&
|
||||
ITERABLES.contains(method.internalClassName)) {
|
||||
return true;
|
||||
}
|
||||
if (method.methodName.equals("hasNext") && method.methodDesc.equals("()Z") && method.internalClassName.equals("java/util/Iterator")) {
|
||||
return true;
|
||||
}
|
||||
return super.isPureMethod(method);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,9 +16,7 @@
|
||||
package com.intellij.codeInspection.bytecodeAnalysis;
|
||||
|
||||
import com.intellij.codeInspection.bytecodeAnalysis.asm.ASMUtils;
|
||||
import com.intellij.openapi.util.Couple;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.org.objectweb.asm.Opcodes;
|
||||
import org.jetbrains.org.objectweb.asm.Type;
|
||||
@@ -43,7 +41,7 @@ public class PurityAnalysis {
|
||||
@NotNull
|
||||
public static Equation analyze(Method method, MethodNode methodNode, boolean stable) {
|
||||
EKey key = new EKey(method, Direction.Pure, stable);
|
||||
Set<EffectQuantum> hardCodedSolution = HardCodedPurity.getHardCodedSolution(method);
|
||||
Set<EffectQuantum> hardCodedSolution = HardCodedPurity.getInstance().getHardCodedSolution(method);
|
||||
if (hardCodedSolution != null) {
|
||||
return new Equation(key, new Effects(hardCodedSolution));
|
||||
}
|
||||
@@ -386,11 +384,11 @@ class DataInterpreter extends Interpreter<DataValue> {
|
||||
EKey key = new EKey(method, Direction.Pure, stable);
|
||||
EffectQuantum quantum = new EffectQuantum.CallQuantum(key, data, opCode == Opcodes.INVOKESTATIC);
|
||||
DataValue result = (ASMUtils.getReturnSizeFast(mNode.desc) == 1) ? DataValue.UnknownDataValue1 : DataValue.UnknownDataValue2;
|
||||
if (HardCodedPurity.isPureMethod(method)) {
|
||||
if (HardCodedPurity.getInstance().isPureMethod(method)) {
|
||||
quantum = null;
|
||||
result = DataValue.LocalDataValue;
|
||||
}
|
||||
else if (HardCodedPurity.isThisChangingMethod(method)) {
|
||||
else if (HardCodedPurity.getInstance().isThisChangingMethod(method)) {
|
||||
DataValue receiver = ArrayUtil.getFirstElement(data);
|
||||
if (receiver == DataValue.ThisDataValue) {
|
||||
quantum = EffectQuantum.ThisChangeQuantum;
|
||||
@@ -398,7 +396,10 @@ class DataInterpreter extends Interpreter<DataValue> {
|
||||
else if (receiver == DataValue.LocalDataValue || receiver == DataValue.OwnedDataValue) {
|
||||
quantum = null;
|
||||
}
|
||||
if (HardCodedPurity.isBuilderChainCall(method)) {
|
||||
else if (receiver instanceof DataValue.ParameterDataValue) {
|
||||
quantum = new EffectQuantum.ParamChangeQuantum(((DataValue.ParameterDataValue)receiver).n);
|
||||
}
|
||||
if (HardCodedPurity.getInstance().isBuilderChainCall(method)) {
|
||||
// mostly to support string concatenation
|
||||
result = receiver;
|
||||
}
|
||||
@@ -424,7 +425,7 @@ class DataInterpreter extends Interpreter<DataValue> {
|
||||
return DataValue.UnknownDataValue2;
|
||||
case Opcodes.GETFIELD:
|
||||
FieldInsnNode fieldInsn = ((FieldInsnNode)insn);
|
||||
if (value == DataValue.ThisDataValue && HardCodedPurity.isOwnedField(fieldInsn)) {
|
||||
if (value == DataValue.ThisDataValue && HardCodedPurity.getInstance().isOwnedField(fieldInsn)) {
|
||||
return DataValue.OwnedDataValue;
|
||||
} else {
|
||||
return ASMUtils.getSizeFast(fieldInsn.desc) == 1 ? DataValue.UnknownDataValue1 : DataValue.UnknownDataValue2;
|
||||
@@ -474,63 +475,6 @@ class DataInterpreter extends Interpreter<DataValue> {
|
||||
}
|
||||
}
|
||||
|
||||
final class HardCodedPurity {
|
||||
private static Set<Couple<String>> ownedFields = ContainerUtil.set(
|
||||
new Couple<>("java/lang/AbstractStringBuilder", "value")
|
||||
);
|
||||
private static Set<Method> thisChangingMethods = ContainerUtil.set(
|
||||
new Method("java/lang/Throwable", "fillInStackTrace", "()Ljava/lang/Throwable;")
|
||||
);
|
||||
// Assumed that all these methods are not only pure, but return object which could be safely modified
|
||||
private static Set<Method> pureMethods = ContainerUtil.set(
|
||||
// Maybe overloaded and be not pure, but this would be definitely bad code style
|
||||
// Used in Throwable(Throwable) ctor, so this helps to infer purity of many exception constructors
|
||||
new Method("java/lang/Throwable", "toString", "()Ljava/lang/String;"),
|
||||
// Native
|
||||
new Method("java/lang/Object", "getClass", "()Ljava/lang/Class;"),
|
||||
new Method("java/lang/Class", "getComponentType", "()Ljava/lang/Class;"),
|
||||
new Method("java/lang/reflect/Array", "newInstance", "(Ljava/lang/Class;I)Ljava/lang/Object;"),
|
||||
new Method("java/lang/reflect/Array", "newInstance", "(Ljava/lang/Class;[I)Ljava/lang/Object;"),
|
||||
new Method("java/lang/Float", "floatToRawIntBits", "(F)I"),
|
||||
new Method("java/lang/Float", "intBitsToFloat", "(I)F"),
|
||||
new Method("java/lang/Double", "doubleToRawLongBits", "(D)J"),
|
||||
new Method("java/lang/Double", "longBitsToDouble", "(J)D")
|
||||
);
|
||||
private static Map<Method, Set<EffectQuantum>> solutions = new HashMap<>();
|
||||
private static Set<EffectQuantum> thisChange = Collections.singleton(EffectQuantum.ThisChangeQuantum);
|
||||
static {
|
||||
// Native
|
||||
solutions.put(new Method("java/lang/System", "arraycopy", "(Ljava/lang/Object;ILjava/lang/Object;II)V"),
|
||||
Collections.singleton(new EffectQuantum.ParamChangeQuantum(2)));
|
||||
solutions.put(new Method("java/lang/Object", "hashCode", "()I"), Collections.emptySet());
|
||||
}
|
||||
|
||||
static Set<EffectQuantum> getHardCodedSolution(Method method) {
|
||||
return isThisChangingMethod(method) ? thisChange : isPureMethod(method) ? Collections.emptySet() : solutions.get(method);
|
||||
}
|
||||
|
||||
static boolean isThisChangingMethod(Method method) {
|
||||
return isBuilderChainCall(method) || thisChangingMethods.contains(method);
|
||||
}
|
||||
|
||||
static boolean isBuilderChainCall(Method method) {
|
||||
// Those methods are virtual, thus contracts cannot be inferred automatically,
|
||||
// but all possible implementations are controlled
|
||||
// (only final classes j.l.StringBuilder and j.l.StringBuffer extend package-private j.l.AbstractStringBuilder)
|
||||
return (method.internalClassName.equals("java/lang/StringBuilder") || method.internalClassName.equals("java/lang/StringBuffer")) &&
|
||||
method.methodName.startsWith("append");
|
||||
}
|
||||
|
||||
static boolean isPureMethod(Method method) {
|
||||
return method.methodName.equals("toString") && method.methodDesc.equals("()Ljava/lang/String;") ||
|
||||
pureMethods.contains(method);
|
||||
}
|
||||
|
||||
static boolean isOwnedField(FieldInsnNode fieldInsn) {
|
||||
return ownedFields.contains(new Couple<>(fieldInsn.owner, fieldInsn.name));
|
||||
}
|
||||
}
|
||||
|
||||
final class PuritySolver {
|
||||
private HashMap<EKey, Set<EffectQuantum>> solved = new HashMap<>();
|
||||
private HashMap<EKey, Set<EKey>> dependencies = new HashMap<>();
|
||||
|
||||
@@ -1278,6 +1278,11 @@
|
||||
<val name="pure" val="true"/>
|
||||
</annotation>
|
||||
</item>
|
||||
<item name='java.lang.Iterable java.util.Iterator<T> iterator()'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val name="pure" val="true"/>
|
||||
</annotation>
|
||||
</item>
|
||||
<item name='java.lang.Long Long(java.lang.String) 0'>
|
||||
<annotation name='org.jetbrains.annotations.NotNull'/>
|
||||
</item>
|
||||
|
||||
@@ -917,6 +917,11 @@
|
||||
<item name='java.util.Calendar void writeObject(java.io.ObjectOutputStream) 0'>
|
||||
<annotation name='org.jetbrains.annotations.NotNull'/>
|
||||
</item>
|
||||
<item name='java.util.Collection java.util.Iterator<E> iterator()'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val name="pure" val="true"/>
|
||||
</annotation>
|
||||
</item>
|
||||
<item name='java.util.Collections Collections()'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val name="pure" val="true"/>
|
||||
@@ -1063,6 +1068,9 @@
|
||||
</annotation>
|
||||
</item>
|
||||
<item name='java.util.Collections java.util.Enumeration<T> enumeration(java.util.Collection<T>)'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val name="pure" val="true"/>
|
||||
</annotation>
|
||||
<annotation name='org.jetbrains.annotations.NotNull'/>
|
||||
</item>
|
||||
<item name='java.util.Collections java.util.Iterator<E> singletonIterator(E)'>
|
||||
@@ -1378,6 +1386,9 @@
|
||||
<annotation name='org.jetbrains.annotations.NotNull'/>
|
||||
</item>
|
||||
<item name='java.util.Collections.CheckedCollection java.util.Iterator<E> iterator()'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val name="pure" val="true"/>
|
||||
</annotation>
|
||||
<annotation name='org.jetbrains.annotations.NotNull'/>
|
||||
</item>
|
||||
<item name='java.util.Collections.CheckedCollection void typeCheck(java.lang.Object) 0'>
|
||||
@@ -1503,6 +1514,9 @@
|
||||
<annotation name='org.jetbrains.annotations.NotNull'/>
|
||||
</item>
|
||||
<item name='java.util.Collections.CheckedMap.CheckedEntrySet java.util.Iterator<java.util.Map.Entry<K,V>> iterator()'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val name="pure" val="true"/>
|
||||
</annotation>
|
||||
<annotation name='org.jetbrains.annotations.NotNull'/>
|
||||
</item>
|
||||
<item name='java.util.Collections.CheckedMap.CheckedEntrySet.CheckedEntry CheckedEntry(java.util.Map.Entry<K,V>, java.lang.Class<T>)'>
|
||||
@@ -1926,6 +1940,11 @@
|
||||
<val name="pure" val="true"/>
|
||||
</annotation>
|
||||
</item>
|
||||
<item name='java.util.Collections.SetFromMap java.util.Iterator<E> iterator()'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val name="pure" val="true"/>
|
||||
</annotation>
|
||||
</item>
|
||||
<item name='java.util.Collections.SetFromMap void readObject(java.io.ObjectInputStream) 0'>
|
||||
<annotation name='org.jetbrains.annotations.NotNull'/>
|
||||
</item>
|
||||
@@ -2017,6 +2036,11 @@
|
||||
<val name="pure" val="true"/>
|
||||
</annotation>
|
||||
</item>
|
||||
<item name='java.util.Collections.SynchronizedCollection java.util.Iterator<E> iterator()'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val name="pure" val="true"/>
|
||||
</annotation>
|
||||
</item>
|
||||
<item name='java.util.Collections.SynchronizedCollection void writeObject(java.io.ObjectOutputStream) 0'>
|
||||
<annotation name='org.jetbrains.annotations.NotNull'/>
|
||||
</item>
|
||||
@@ -2201,6 +2225,9 @@
|
||||
</annotation>
|
||||
</item>
|
||||
<item name='java.util.Collections.UnmodifiableCollection java.util.Iterator<E> iterator()'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val name="pure" val="true"/>
|
||||
</annotation>
|
||||
<annotation name='org.jetbrains.annotations.NotNull'/>
|
||||
</item>
|
||||
<item name='java.util.Collections.UnmodifiableCollection void clear()'>
|
||||
@@ -2334,6 +2361,9 @@
|
||||
<annotation name='org.jetbrains.annotations.Nullable'/>
|
||||
</item>
|
||||
<item name='java.util.Collections.UnmodifiableMap.UnmodifiableEntrySet java.util.Iterator<java.util.Map.Entry<K,V>> iterator()'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val name="pure" val="true"/>
|
||||
</annotation>
|
||||
<annotation name='org.jetbrains.annotations.NotNull'/>
|
||||
</item>
|
||||
<item name='java.util.Collections.UnmodifiableMap.UnmodifiableEntrySet.UnmodifiableEntry UnmodifiableEntry(java.util.Map.Entry<? extends K,? extends V>)'>
|
||||
@@ -3076,6 +3106,11 @@
|
||||
</annotation>
|
||||
<annotation name='org.jetbrains.annotations.NotNull'/>
|
||||
</item>
|
||||
<item name='java.util.Iterator boolean hasNext()'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val name="pure" val="true"/>
|
||||
</annotation>
|
||||
</item>
|
||||
<item name='java.util.LinkedHashMap LinkedHashMap(java.util.Map<? extends K,? extends V>) 0'>
|
||||
<annotation name='org.jetbrains.annotations.NotNull'/>
|
||||
</item>
|
||||
@@ -3146,6 +3181,11 @@
|
||||
<val name="pure" val="true"/>
|
||||
</annotation>
|
||||
</item>
|
||||
<item name='java.util.List java.util.Iterator<E> iterator()'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val name="pure" val="true"/>
|
||||
</annotation>
|
||||
</item>
|
||||
<item name='java.util.Locale Locale(java.lang.String) 0'>
|
||||
<annotation name='org.jetbrains.annotations.NotNull'/>
|
||||
</item>
|
||||
@@ -3357,6 +3397,11 @@
|
||||
<item name='java.util.Random void writeObject(java.io.ObjectOutputStream) 0'>
|
||||
<annotation name='org.jetbrains.annotations.NotNull'/>
|
||||
</item>
|
||||
<item name='java.util.Set java.util.Iterator<E> iterator()'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val name="pure" val="true"/>
|
||||
</annotation>
|
||||
</item>
|
||||
<item name='java.util.TreeMap K firstKey()'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val name="pure" val="true"/>
|
||||
|
||||
@@ -51,6 +51,11 @@
|
||||
<item name='org.apache.commons.collections.ExtendedProperties java.lang.String unescape(java.lang.String) 0'>
|
||||
<annotation name='org.jetbrains.annotations.NotNull'/>
|
||||
</item>
|
||||
<item name='org.apache.commons.collections.ExtendedProperties java.util.Iterator getKeys()'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val name="pure" val="true"/>
|
||||
</annotation>
|
||||
</item>
|
||||
<item name='org.apache.commons.collections.ExtendedProperties org.apache.commons.collections.ExtendedProperties convertProperties(java.util.Properties) 0'>
|
||||
<annotation name='org.jetbrains.annotations.NotNull'/>
|
||||
</item>
|
||||
|
||||
@@ -1543,6 +1543,11 @@
|
||||
<item name='org.apache.commons.lang.CharSet CharSet(java.lang.String[]) 0'>
|
||||
<annotation name='org.jetbrains.annotations.NotNull'/>
|
||||
</item>
|
||||
<item name='org.apache.commons.lang.CharSet boolean contains(char)'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val name="pure" val="true"/>
|
||||
</annotation>
|
||||
</item>
|
||||
<item name='org.apache.commons.lang.CharSet boolean equals(java.lang.Object) 0'>
|
||||
<annotation name='org.jetbrains.annotations.Nullable'/>
|
||||
</item>
|
||||
@@ -3315,7 +3320,8 @@
|
||||
</item>
|
||||
<item name='org.apache.commons.lang.StringUtils java.lang.String join(java.util.Collection, char)'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val val=""null,_->null""/>
|
||||
<val name="value" val=""null,_->null""/>
|
||||
<val name="pure" val="true"/>
|
||||
</annotation>
|
||||
<annotation name='org.jetbrains.annotations.Nullable'/>
|
||||
</item>
|
||||
@@ -3324,7 +3330,8 @@
|
||||
</item>
|
||||
<item name='org.apache.commons.lang.StringUtils java.lang.String join(java.util.Collection, java.lang.String)'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val val=""null,_->null""/>
|
||||
<val name="value" val=""null,_->null""/>
|
||||
<val name="pure" val="true"/>
|
||||
</annotation>
|
||||
<annotation name='org.jetbrains.annotations.Nullable'/>
|
||||
</item>
|
||||
@@ -4137,9 +4144,19 @@
|
||||
<item name='org.apache.commons.lang.Validate void noNullElements(java.lang.Object[], java.lang.String) 0'>
|
||||
<annotation name='org.jetbrains.annotations.NotNull'/>
|
||||
</item>
|
||||
<item name='org.apache.commons.lang.Validate void noNullElements(java.util.Collection)'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val name="pure" val="true"/>
|
||||
</annotation>
|
||||
</item>
|
||||
<item name='org.apache.commons.lang.Validate void noNullElements(java.util.Collection) 0'>
|
||||
<annotation name='org.jetbrains.annotations.NotNull'/>
|
||||
</item>
|
||||
<item name='org.apache.commons.lang.Validate void noNullElements(java.util.Collection, java.lang.String)'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val name="pure" val="true"/>
|
||||
</annotation>
|
||||
</item>
|
||||
<item name='org.apache.commons.lang.Validate void noNullElements(java.util.Collection, java.lang.String) 0'>
|
||||
<annotation name='org.jetbrains.annotations.NotNull'/>
|
||||
</item>
|
||||
|
||||
@@ -90,6 +90,11 @@
|
||||
<val name="pure" val="true"/>
|
||||
</annotation>
|
||||
</item>
|
||||
<item name='org.apache.velocity.anakia.NodeList java.util.Iterator iterator()'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val name="pure" val="true"/>
|
||||
</annotation>
|
||||
</item>
|
||||
<item name='org.apache.velocity.anakia.NodeList java.util.List getList()'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val name="pure" val="true"/>
|
||||
|
||||
@@ -23,6 +23,26 @@
|
||||
<item name='org.apache.velocity.app.event.EventCartridge boolean removeEventHandler(org.apache.velocity.app.event.EventHandler) 0'>
|
||||
<annotation name='org.jetbrains.annotations.Nullable'/>
|
||||
</item>
|
||||
<item name='org.apache.velocity.app.event.EventCartridge java.util.Iterator getIncludeEventHandlers()'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val name="pure" val="true"/>
|
||||
</annotation>
|
||||
</item>
|
||||
<item name='org.apache.velocity.app.event.EventCartridge java.util.Iterator getInvalidReferenceEventHandlers()'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val name="pure" val="true"/>
|
||||
</annotation>
|
||||
</item>
|
||||
<item name='org.apache.velocity.app.event.EventCartridge java.util.Iterator getMethodExceptionEventHandlers()'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val name="pure" val="true"/>
|
||||
</annotation>
|
||||
</item>
|
||||
<item name='org.apache.velocity.app.event.EventCartridge java.util.Iterator getNullSetEventHandlers()'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val name="pure" val="true"/>
|
||||
</annotation>
|
||||
</item>
|
||||
<item name='org.apache.velocity.app.event.EventCartridge java.util.Iterator getReferenceInsertionEventHandlers()'>
|
||||
<annotation name='org.jetbrains.annotations.Nullable'/>
|
||||
</item>
|
||||
|
||||
@@ -407,6 +407,9 @@ java.annotations.inference.nullable.method.restartRequired=true
|
||||
java.annotations.inference.nullable.method.transitivity=true
|
||||
java.annotations.inference.nullable.method.transitivity.description=If a method result is a call to a @Nullable method, reports the caller as @Nullable as well
|
||||
java.annotations.inference.nullable.method.transitivity.restartRequired=true
|
||||
java.annotations.inference.aggressive.hardcoded.purity=true
|
||||
java.annotations.inference.aggressive.hardcoded.purity.description=Assume any implementation of methods like Object.toString() or Iterable.iterator() to be pure during bytecode inference. This assumption might lead to false-positives in some inspections, though it's believed to uncover more bugs.
|
||||
java.annotations.inference.aggressive.hardcoded.purity.restartRequired=true
|
||||
|
||||
java.hierarchy.service=false
|
||||
java.hierarchy.service.description=Enable special indexing for quick JVM class hierarchy calculation
|
||||
|
||||
Reference in New Issue
Block a user