concatenation) {
if (concatenation == myConcatenation) return this;
return new ValuableDfaVariableState(myInstanceofValues, myNotInstanceofValues, myNullability, myValue, concatenation,
- myOptionalPresence);
+ myOptionalPresence, myRange);
}
@Override
diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/instructions/BinopInstruction.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/instructions/BinopInstruction.java
index 6e64a6b86416..66c13658febb 100644
--- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/instructions/BinopInstruction.java
+++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/instructions/BinopInstruction.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2000-2009 JetBrains s.r.o.
+ * 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.
@@ -28,7 +28,10 @@ import com.intellij.codeInspection.dataFlow.*;
import com.intellij.codeInspection.dataFlow.value.DfaValue;
import com.intellij.codeInspection.dataFlow.value.DfaValueFactory;
import com.intellij.openapi.project.Project;
-import com.intellij.psi.*;
+import com.intellij.psi.PsiClassType;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiManager;
+import com.intellij.psi.PsiType;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.tree.TokenSet;
@@ -38,7 +41,7 @@ import org.jetbrains.annotations.Nullable;
import static com.intellij.psi.JavaTokenType.*;
public class BinopInstruction extends BranchingInstruction {
- private static final TokenSet ourSignificantOperations = TokenSet.create(EQEQ, NE, LT, GT, LE, GE, INSTANCEOF_KEYWORD, PLUS);
+ private static final TokenSet ourSignificantOperations = TokenSet.create(EQEQ, NE, LT, GT, LE, GE, INSTANCEOF_KEYWORD, PLUS, AND);
private final IElementType myOperationSign;
private final Project myProject;
diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/rangeSet/LongRangeSet.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/rangeSet/LongRangeSet.java
new file mode 100644
index 000000000000..306c91c01bd2
--- /dev/null
+++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/rangeSet/LongRangeSet.java
@@ -0,0 +1,853 @@
+/*
+ * 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.dataFlow.rangeSet;
+
+import com.intellij.psi.JavaTokenType;
+import com.intellij.psi.PsiPrimitiveType;
+import com.intellij.psi.PsiType;
+import com.intellij.psi.tree.IElementType;
+import com.intellij.util.ThreeState;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.Arrays;
+import java.util.NoSuchElementException;
+import java.util.stream.IntStream;
+import java.util.stream.LongStream;
+
+/**
+ * An immutable set of long values optimized for small number of ranges.
+ *
+ * @author Tagir Valeev
+ */
+public abstract class LongRangeSet {
+ LongRangeSet() {}
+
+ /**
+ * Subtracts given set from the current
+ *
+ * @param other set to subtract
+ * @return a new set
+ */
+ public abstract LongRangeSet subtract(LongRangeSet other);
+
+ public LongRangeSet without(long value) {
+ return subtract(point(value));
+ }
+
+ /**
+ * @return true if set is empty
+ */
+ public boolean isEmpty() {
+ return this == Empty.EMPTY;
+ }
+
+ /**
+ * Intersects current set with other
+ *
+ * @param other other set to intersect with
+ * @return a new set
+ */
+ public abstract LongRangeSet intersect(LongRangeSet other);
+
+ /**
+ * Merge current set with other
+ *
+ * @param other other set to merge with
+ * @return a new set
+ */
+ public LongRangeSet union(LongRangeSet other) {
+ if(other.isEmpty() || other == this) return this;
+ if(other.contains(this)) return other;
+ // TODO: optimize
+ return Range.LONG_RANGE.subtract(Range.LONG_RANGE.subtract(this).intersect(Range.LONG_RANGE.subtract(other)));
+ }
+
+ /**
+ * @return a minimal value contained in the set
+ * @throws NoSuchElementException if set is empty
+ */
+ public abstract long min();
+
+ /**
+ * @return a maximal value contained in the set
+ * @throws NoSuchElementException if set is empty
+ */
+ public abstract long max();
+
+ /**
+ * Checks if current set and other set have at least one common element
+ *
+ * @param other other set to check whether intersection exists
+ * @return true if this set intersects other set
+ */
+ public abstract boolean intersects(LongRangeSet other);
+
+ /**
+ * Checks whether current set contains given value
+ *
+ * @param value value to find
+ * @return true if current set contains given value
+ */
+ public abstract boolean contains(long value);
+
+ /**
+ * Checks whether current set contains all the values from other set
+ *
+ * @param other a sub-set candidate
+ * @return true if current set contains all the values from other
+ */
+ public abstract boolean contains(LongRangeSet other);
+
+ /**
+ * Creates a new set which contains all possible values satisfying given predicate regarding the current set.
+ *
+ * E.g. if current set is {0..10} and relation is "GT", then result will be {1..Long.MAX_VALUE} (values which can be greater than
+ * some value from the current set)
+ *
+ * @param relation relation to be applied to current set (JavaTokenType.EQEQ/NE/GT/GE/LT/LE)
+ * @return new set or null if relation is unsupported
+ */
+ public LongRangeSet fromRelation(IElementType relation) {
+ if (isEmpty()) return null;
+ if (JavaTokenType.EQEQ.equals(relation)) {
+ return this;
+ }
+ if (JavaTokenType.NE.equals(relation)) {
+ long min = min();
+ if (min == max()) return all().without(min);
+ return all();
+ }
+ if (JavaTokenType.GT.equals(relation)) {
+ long min = min();
+ return min == Long.MAX_VALUE ? empty() : range(min + 1, Long.MAX_VALUE);
+ }
+ if (JavaTokenType.GE.equals(relation)) {
+ return range(min(), Long.MAX_VALUE);
+ }
+ if (JavaTokenType.LE.equals(relation)) {
+ return range(Long.MIN_VALUE, max());
+ }
+ if (JavaTokenType.LT.equals(relation)) {
+ long max = max();
+ return max == Long.MIN_VALUE ? empty() : range(Long.MIN_VALUE, max - 1);
+ }
+ return null;
+ }
+
+ /**
+ * Returns a range which represents all the possible values after applying {@link Math#abs(int)} or {@link Math#abs(long)}
+ * to the values from this set
+ *
+ * @param isLong whether {@link Math#abs(long)} is applied
+ * @return a new range
+ */
+ public abstract LongRangeSet abs(boolean isLong);
+
+ /**
+ * Returns a range which represents all the possible values after applying {@code x & y} operation for
+ * all {@code x} from this set and for all {@code y} from the other set. The resulting set may contain
+ * some more values.
+ *
+ * @param other other set to perform bitwise-and with
+ * @return a new range
+ */
+ public LongRangeSet bitwiseAnd(LongRangeSet other) {
+ if (this.isEmpty() || other.isEmpty()) return empty();
+ long[] left = splitAtZero(asRanges());
+ long[] right = splitAtZero(other.asRanges());
+ // More than three intervals --> convert to single interval to make result more compact (though probably less precise)
+ if (left.length > 6) {
+ left = splitAtZero(new long[]{left[0], left[left.length - 1]});
+ }
+ if (right.length > 6) {
+ right = splitAtZero(new long[]{right[0], right[right.length - 1]});
+ }
+ LongRangeSet result = all();
+ for (int i = 0; i < left.length; i += 2) {
+ for (int j = 0; j < right.length; j += 2) {
+ result = result.subtract(bitwiseAnd(left[i], left[i + 1], right[j], right[j + 1]));
+ }
+ }
+ return all().subtract(result);
+ }
+
+ private static long[] splitAtZero(long[] ranges) {
+ for (int i = 0; i < ranges.length; i += 2) {
+ if (ranges[i] < 0 && ranges[i + 1] >= 0) {
+ long[] result = new long[ranges.length + 2];
+ System.arraycopy(ranges, 0, result, 0, i + 1);
+ result[i + 1] = -1;
+ System.arraycopy(ranges, i + 1, result, i + 3, ranges.length - i - 1);
+ return result;
+ }
+ }
+ return ranges;
+ }
+
+ private static LongRangeSet bitwiseAnd(long leftFrom, long leftTo, long rightFrom, long rightTo) {
+ if (leftFrom == leftTo && rightFrom == rightTo) {
+ return point(leftFrom & rightFrom);
+ }
+ ThreeState[] leftBits = bits(leftFrom, leftTo);
+ ThreeState[] rightBits = bits(rightFrom, rightTo);
+ ThreeState[] resultBits = new ThreeState[Long.SIZE];
+ for (int i = 0; i < Long.SIZE; i++) {
+ if (leftBits[i] == ThreeState.NO || rightBits[i] == ThreeState.NO) {
+ resultBits[i] = ThreeState.NO;
+ }
+ else if (leftBits[i] == ThreeState.UNSURE || rightBits[i] == ThreeState.UNSURE) {
+ resultBits[i] = ThreeState.UNSURE;
+ }
+ else {
+ resultBits[i] = ThreeState.YES;
+ }
+ }
+ return fromBits(resultBits);
+ }
+
+ /**
+ * Creates a set which contains all the numbers satisfying the supplied bit vector.
+ * Vector format is the same as returned by {@link #bits(long, long)}. The resulting set may
+ * contain more values than necessary.
+ *
+ * @param bits a bit vector
+ * @return a new LongRangeSet
+ */
+ private static LongRangeSet fromBits(ThreeState[] bits) {
+ long from = 0;
+ int i = 0;
+ while (i < Long.SIZE && bits[i] != ThreeState.UNSURE) {
+ if (bits[i] == ThreeState.YES) {
+ from |= (1L << (Long.SIZE - 1 - i));
+ }
+ i++;
+ }
+ long to = ((1L << (Long.SIZE - i)) - 1) | from;
+ int j = Long.SIZE - 1;
+ while(j > i && bits[j] != ThreeState.UNSURE) {
+ if (bits[j] == ThreeState.NO) {
+ to &= ~(1L << Long.SIZE - 1 - j);
+ }
+ j--;
+ }
+ if(i == j) {
+ return point(from).union(point(to));
+ }
+ return from < to ? range(from, to) : range(to, from);
+ }
+
+ /**
+ * Returns a bit vector for values between from and to.
+ *
+ * @param from lower bound
+ * @param to upper bound
+ * @return an array of 64 ThreeState values (NO = zero bit for all values, YES = one bit for all values,
+ * UNSURE = both one and zero possible)
+ */
+ private static ThreeState[] bits(long from, long to) {
+ ThreeState[] bits = new ThreeState[Long.SIZE];
+ Arrays.setAll(bits, idx -> ThreeState.NO);
+ while (true) {
+ int fromBit = Long.numberOfLeadingZeros(from);
+ int toBit = Long.numberOfLeadingZeros(to);
+ if (fromBit != toBit) {
+ for (int i = Math.min(fromBit, toBit); i < Long.SIZE; i++) {
+ bits[i] = ThreeState.UNSURE;
+ }
+ break;
+ }
+ if (fromBit == 64) break;
+ bits[fromBit] = ThreeState.YES;
+ long clearMask = ~(1L << (Long.SIZE - 1 - fromBit));
+ from &= clearMask;
+ to &= clearMask;
+ }
+ return bits;
+ }
+
+ /**
+ * Returns a stream of all values from this range. Be careful: could be huge
+ *
+ * @return a new stream
+ */
+ public abstract LongStream stream();
+
+ /**
+ * @return an empty set
+ */
+ public static LongRangeSet empty() {
+ return Empty.EMPTY;
+ }
+
+ /**
+ * @return a set containing all possible long values
+ */
+ public static LongRangeSet all() {
+ return Range.LONG_RANGE;
+ }
+
+ /**
+ * Creates a set containing single given value
+ *
+ * @param value a value to be included into the set
+ * @return a new set
+ */
+ public static LongRangeSet point(long value) {
+ return new Point(value);
+ }
+
+ /**
+ * Creates a set containing single value which is equivalent to supplied boxed constant (if its type is supported)
+ *
+ * @param val constant to create a set from
+ * @return new LongRangeSet or null if constant type is unsupported
+ */
+ @Nullable
+ public static LongRangeSet fromConstant(Object val) {
+ if (val instanceof Byte || val instanceof Short || val instanceof Integer || val instanceof Long) {
+ return point(((Number)val).longValue());
+ }
+ else if (val instanceof Character) {
+ return point(((Character)val).charValue());
+ }
+ return null;
+ }
+
+ /**
+ * Creates a new set which contains all the numbers between from (inclusive) and to (inclusive)
+ *
+ * @param from lower bound
+ * @param to upper bound (must be greater or equal to {@code from})
+ * @return a new LongRangeSet
+ */
+ public static LongRangeSet range(long from, long to) {
+ return from == to ? new Point(from) : new Range(from, to);
+ }
+
+ abstract long[] asRanges();
+
+ static String toString(long from, long to) {
+ return from == to ? String.valueOf(from) : from + (to - from == 1 ? ", " : "..") + to;
+ }
+
+ static long minValue(boolean isLong) {
+ return isLong ? Long.MIN_VALUE : Integer.MIN_VALUE;
+ }
+
+ /**
+ * @return LongRangeSet describing possible array or string indices (from 0 to Integer.MAX_VALUE)
+ */
+ public static LongRangeSet indexRange() {
+ return Range.INDEX_RANGE;
+ }
+
+ /**
+ * Creates a range for given type (for primitives and boxed: values range)
+ *
+ * @param type type to create a range for
+ * @return a range or null if type is not supported
+ */
+ @Nullable
+ public static LongRangeSet fromType(PsiType type) {
+ if (type == null) {
+ return null;
+ }
+ type = PsiPrimitiveType.getOptionallyUnboxedType(type);
+ if (type != null) {
+ if (type.equals(PsiType.BYTE)) {
+ return Range.BYTE_RANGE;
+ }
+ if (type.equals(PsiType.CHAR)) {
+ return Range.CHAR_RANGE;
+ }
+ if (type.equals(PsiType.SHORT)) {
+ return Range.SHORT_RANGE;
+ }
+ if (type.equals(PsiType.INT)) {
+ return Range.INT_RANGE;
+ }
+ if (type.equals(PsiType.LONG)) {
+ return all();
+ }
+ }
+ return null;
+ }
+
+ static LongRangeSet fromRanges(long[] ranges, int bound) {
+ if (bound == 0) {
+ return Empty.EMPTY;
+ }
+ else if (bound == 2) {
+ return range(ranges[0], ranges[1]);
+ }
+ else {
+ return new RangeSet(Arrays.copyOfRange(ranges, 0, bound));
+ }
+ }
+
+ static final class Empty extends LongRangeSet {
+ static final LongRangeSet EMPTY = new Empty();
+
+ @Override
+ public LongRangeSet subtract(LongRangeSet other) {
+ return this;
+ }
+
+ @Override
+ public LongRangeSet intersect(LongRangeSet other) {
+ return this;
+ }
+
+ @Override
+ public LongRangeSet union(LongRangeSet other) {
+ return other;
+ }
+
+ @Override
+ public long min() {
+ throw new NoSuchElementException();
+ }
+
+ @Override
+ public long max() {
+ throw new NoSuchElementException();
+ }
+
+ @Override
+ public boolean intersects(LongRangeSet other) {
+ return false;
+ }
+
+ @Override
+ public boolean contains(long value) {
+ return false;
+ }
+
+ @Override
+ public boolean contains(LongRangeSet other) {
+ return other.isEmpty();
+ }
+
+ @Override
+ public LongRangeSet abs(boolean isLong) {
+ return this;
+ }
+
+ @Override
+ public LongStream stream() {
+ return LongStream.empty();
+ }
+
+ @Override
+ long[] asRanges() {
+ return new long[0];
+ }
+
+ @Override
+ public int hashCode() {
+ return 2154231;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return obj == this;
+ }
+
+ @Override
+ public String toString() {
+ return "{}";
+ }
+ }
+
+ static final class Point extends LongRangeSet {
+ final long myValue;
+
+ Point(long value) {
+ myValue = value;
+ }
+
+ @Override
+ public LongRangeSet subtract(LongRangeSet other) {
+ return other.contains(myValue) ? Empty.EMPTY : this;
+ }
+
+ @Override
+ public LongRangeSet intersect(LongRangeSet other) {
+ return other.contains(myValue) ? this : Empty.EMPTY;
+ }
+
+ @Override
+ public long min() {
+ return myValue;
+ }
+
+ @Override
+ public long max() {
+ return myValue;
+ }
+
+ @Override
+ public boolean intersects(LongRangeSet other) {
+ return other.contains(myValue);
+ }
+
+ @Override
+ public boolean contains(long value) {
+ return myValue == value;
+ }
+
+ @Override
+ public boolean contains(LongRangeSet other) {
+ return other.isEmpty() || equals(other);
+ }
+
+ @Override
+ public LongRangeSet abs(boolean isLong) {
+ return myValue >= 0 || myValue == minValue(isLong) ? this : point(-myValue);
+ }
+
+ @Override
+ public LongStream stream() {
+ return LongStream.of(myValue);
+ }
+
+ @Override
+ long[] asRanges() {
+ return new long[] {myValue, myValue};
+ }
+
+ @Override
+ public int hashCode() {
+ return Long.hashCode(myValue);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o == this) return true;
+ return o != null && o instanceof Point && myValue == ((Point)o).myValue;
+ }
+
+ @Override
+ public String toString() {
+ return "{" + myValue + "}";
+ }
+ }
+
+ static final class Range extends LongRangeSet {
+ static final Range BYTE_RANGE = new Range(Byte.MIN_VALUE, Byte.MAX_VALUE);
+ static final Range CHAR_RANGE = new Range(Character.MIN_VALUE, Character.MAX_VALUE);
+ static final Range SHORT_RANGE = new Range(Short.MIN_VALUE, Short.MAX_VALUE);
+ static final Range INT_RANGE = new Range(Integer.MIN_VALUE, Integer.MAX_VALUE);
+ static final Range LONG_RANGE = new Range(Long.MIN_VALUE, Long.MAX_VALUE);
+ static final Range INDEX_RANGE = new Range(0, Integer.MAX_VALUE);
+
+ final long myFrom; // inclusive
+ final long myTo; // inclusive
+
+ Range(long from, long to) {
+ if (to <= from) { // to == from => must be Point
+ throw new IllegalArgumentException(to + "<=" + from);
+ }
+ myFrom = from;
+ myTo = to;
+ }
+
+ @Override
+ public LongRangeSet subtract(LongRangeSet other) {
+ if (other.isEmpty()) return this;
+ if (other == this) return Empty.EMPTY;
+ if (other instanceof Point) {
+ long value = ((Point)other).myValue;
+ if (value < myFrom || value > myTo) return this;
+ if (value == myFrom) return range(myFrom + 1, myTo);
+ if (value == myTo) return range(myFrom, myTo - 1);
+ return new RangeSet(new long[]{myFrom, value - 1, value + 1, myTo});
+ }
+ if (other instanceof Range) {
+ long from = ((Range)other).myFrom;
+ long to = ((Range)other).myTo;
+ if (to < myFrom || from > myTo) return this;
+ if (from <= myFrom && to >= myTo) return Empty.EMPTY;
+ if (from > myFrom && to < myTo) {
+ return new RangeSet(new long[]{myFrom, from - 1, to + 1, myTo});
+ }
+ if (from <= myFrom) {
+ return range(to + 1, myTo);
+ }
+ if (to >= myTo) {
+ return range(myFrom, from - 1);
+ }
+ throw new InternalError("Impossible: " + this + ":" + other);
+ }
+ long[] ranges = ((RangeSet)other).myRanges;
+ LongRangeSet result = this;
+ for (int i = 0; i < ranges.length; i += 2) {
+ result = result.subtract(range(ranges[i], ranges[i + 1]));
+ if (result.isEmpty()) return result;
+ }
+ return result;
+ }
+
+ @Override
+ public LongRangeSet intersect(LongRangeSet other) {
+ if (other == this) return this;
+ if (other.isEmpty()) return other;
+ if (other instanceof Point) {
+ return other.intersect(this);
+ }
+ if (other instanceof Range) {
+ long from = ((Range)other).myFrom;
+ long to = ((Range)other).myTo;
+ if (from <= myFrom && to >= myTo) return this;
+ if (from >= myFrom && to <= myTo) return other;
+ if (from < myFrom) {
+ from = myFrom;
+ }
+ if (to > myTo) {
+ to = myTo;
+ }
+ return from <= to ? range(from, to) : Empty.EMPTY;
+ }
+ long[] ranges = ((RangeSet)other).myRanges;
+ long[] result = new long[ranges.length];
+ int index = 0;
+ for (int i = 0; i < ranges.length; i += 2) {
+ long[] res = intersect(range(ranges[i], ranges[i + 1])).asRanges();
+ System.arraycopy(res, 0, result, index, res.length);
+ index += res.length;
+ }
+ return fromRanges(result, index);
+ }
+
+ @Override
+ public long min() {
+ return myFrom;
+ }
+
+ @Override
+ public long max() {
+ return myTo;
+ }
+
+ @Override
+ public boolean intersects(LongRangeSet other) {
+ if (other.isEmpty()) return false;
+ if (other instanceof RangeSet) {
+ return other.intersects(this);
+ }
+ return myTo >= other.min() && myFrom <= other.max();
+ }
+
+ @Override
+ public boolean contains(long value) {
+ return myFrom <= value && myTo >= value;
+ }
+
+ @Override
+ public boolean contains(LongRangeSet other) {
+ return other.isEmpty() || other.min() >= myFrom && other.max() <= myTo;
+ }
+
+ @Override
+ public LongRangeSet abs(boolean isLong) {
+ if (myFrom >= 0) return this;
+ long minValue = minValue(isLong);
+ long low = myFrom, hi = myTo;
+ if (low <= minValue) {
+ low = minValue + 1;
+ }
+ if (myTo <= 0) {
+ hi = -low;
+ low = -myTo;
+ }
+ else {
+ hi = Math.max(-low, hi);
+ low = 0;
+ }
+ if (myFrom <= minValue) {
+ return new RangeSet(new long[]{minValue, minValue, low, hi});
+ }
+ else {
+ return new Range(low, hi);
+ }
+ }
+
+ @Override
+ public LongStream stream() {
+ return LongStream.rangeClosed(myFrom, myTo);
+ }
+
+ @Override
+ long[] asRanges() {
+ return new long[] {myFrom, myTo};
+ }
+
+ @Override
+ public int hashCode() {
+ return Long.hashCode(myFrom) * 1337 + Long.hashCode(myTo);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o == this) return true;
+ return o != null && o instanceof Range && myFrom == ((Range)o).myFrom && myTo == ((Range)o).myTo;
+ }
+
+ @Override
+ public String toString() {
+ return "{" + toString(myFrom, myTo) + "}";
+ }
+ }
+
+ static final class RangeSet extends LongRangeSet {
+ final long[] myRanges;
+
+ RangeSet(long[] ranges) {
+ if (ranges.length < 4 || ranges.length % 2 != 0) {
+ // 0 ranges = Empty; 1 range = Range
+ throw new IllegalArgumentException("Bad length: " + ranges.length + " " + Arrays.toString(ranges));
+ }
+ for (int i = 0; i < ranges.length; i += 2) {
+ if (ranges[i + 1] < ranges[i]) {
+ throw new IllegalArgumentException("Bad sub-range #" + (i / 2) + " " + Arrays.toString(ranges));
+ }
+ if (i > 0 && (ranges[i - 1] == Long.MAX_VALUE || 1 + ranges[i - 1] > ranges[i])) {
+ throw new IllegalArgumentException("Bad sub-ranges #" + (i / 2 - 1) + " and #" + (i / 2) + " " + Arrays.toString(ranges));
+ }
+ }
+ myRanges = ranges;
+ }
+
+ @Override
+ public LongRangeSet subtract(LongRangeSet other) {
+ if (other.isEmpty()) return this;
+ if (other == this) return Empty.EMPTY;
+ long[] result = new long[myRanges.length + other.asRanges().length];
+ int index = 0;
+ for (int i = 0; i < myRanges.length; i += 2) {
+ LongRangeSet res = range(myRanges[i], myRanges[i + 1]).subtract(other);
+ long[] ranges = res.asRanges();
+ System.arraycopy(ranges, 0, result, index, ranges.length);
+ index += ranges.length;
+ }
+ return fromRanges(result, index);
+ }
+
+ @Override
+ public LongRangeSet intersect(LongRangeSet other) {
+ if (other == this) return this;
+ if (other.isEmpty()) return other;
+ if (other instanceof Point || other instanceof Range) {
+ return other.intersect(this);
+ }
+ return subtract(all().subtract(other));
+ }
+
+ @Override
+ public long min() {
+ return myRanges[0];
+ }
+
+ @Override
+ public long max() {
+ return myRanges[myRanges.length - 1];
+ }
+
+ @Override
+ public boolean intersects(LongRangeSet other) {
+ if (other.isEmpty()) return false;
+ if (other instanceof Point) {
+ return contains(((Point)other).myValue);
+ }
+ long[] otherRanges = other.asRanges();
+ int a = 0, b = 0;
+ while (true) {
+ long aFrom = myRanges[a];
+ long aTo = myRanges[a + 1];
+ long bFrom = otherRanges[b];
+ long bTo = otherRanges[b + 1];
+ if (aFrom <= bTo && bFrom <= aTo) return true;
+ if (aFrom > bTo) {
+ b += 2;
+ if (b >= otherRanges.length) return false;
+ }
+ else {
+ a += 2;
+ if (a >= myRanges.length) return false;
+ }
+ }
+ }
+
+ @Override
+ public boolean contains(long value) {
+ for (int i = 0; i < myRanges.length; i += 2) {
+ if (value >= myRanges[i] && value <= myRanges[i + 1]) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public boolean contains(LongRangeSet other) {
+ if (other.isEmpty() || other == this) return true;
+ return other.subtract(this).isEmpty();
+ }
+
+ @Override
+ public LongRangeSet abs(boolean isLong) {
+ LongRangeSet result = all();
+ for (int i = 0; i < myRanges.length; i += 2) {
+ result = result.subtract(range(myRanges[i], myRanges[i + 1]).abs(isLong));
+ }
+ return all().subtract(result);
+ }
+
+ @Override
+ public LongStream stream() {
+ return IntStream.range(0, myRanges.length / 2)
+ .mapToObj(idx -> LongStream.rangeClosed(myRanges[idx * 2], myRanges[idx * 2 + 1]))
+ .reduce(LongStream::concat).orElseGet(LongStream::empty);
+ }
+
+ @Override
+ long[] asRanges() {
+ return myRanges;
+ }
+
+ @Override
+ public int hashCode() {
+ return Arrays.hashCode(myRanges);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o == this) return true;
+ return o != null && o instanceof RangeSet && Arrays.equals(myRanges, ((RangeSet)o).myRanges);
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder("{");
+ for (int i = 0; i < myRanges.length; i += 2) {
+ if (i > 0) sb.append(", ");
+ sb.append(LongRangeSet.toString(myRanges[i], myRanges[i + 1]));
+ }
+ sb.append("}");
+ return sb.toString();
+ }
+ }
+}
diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaExpressionFactory.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaExpressionFactory.java
index 5960f87b6933..c27408783297 100644
--- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaExpressionFactory.java
+++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaExpressionFactory.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2000-2015 JetBrains s.r.o.
+ * 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.
@@ -29,6 +29,7 @@ import com.intellij.psi.util.PropertyUtil;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.util.containers.ContainerUtil;
+import com.siyeh.ig.psiutils.MethodUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -174,6 +175,9 @@ public class DfaExpressionFactory {
return method;
}
}
+ if (MethodUtils.isStringLength(method)) {
+ return method;
+ }
if (AnnotationUtil.findAnnotation(method.getContainingClass(), "javax.annotation.concurrent.Immutable") != null) {
return method;
}
diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaRangeValue.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaRangeValue.java
new file mode 100644
index 000000000000..47825a92cf30
--- /dev/null
+++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaRangeValue.java
@@ -0,0 +1,70 @@
+/*
+ * 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.dataFlow.value;
+
+import com.intellij.codeInspection.dataFlow.rangeSet.LongRangeSet;
+import com.intellij.psi.PsiType;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @author Tagir Valeev
+ */
+public class DfaRangeValue extends DfaValue {
+ private final LongRangeSet myValue;
+
+ DfaRangeValue(DfaValueFactory factory, @NotNull LongRangeSet value) {
+ super(factory);
+ myValue = value;
+ }
+
+ public DfaRangeValue intersect(LongRangeSet value) {
+ return myFactory.getRangeFactory().create(myValue.intersect(value));
+ }
+
+ public LongRangeSet getValue() {
+ return myValue;
+ }
+
+ public static class Factory {
+ private Map myValues = new HashMap<>();
+ private DfaValueFactory myFactory;
+
+ Factory(DfaValueFactory factory) {
+ myFactory = factory;
+ }
+
+ /**
+ * Any value of given type (if type is supported)
+ *
+ * @param type type to create a range-value from
+ * @return DfaRangeValue representing range of given type
+ */
+ @Nullable
+ public DfaRangeValue create(PsiType type) {
+ LongRangeSet domain = LongRangeSet.fromType(type);
+ return domain == null ? null : create(domain);
+ }
+
+ @NotNull
+ public DfaRangeValue create(LongRangeSet value) {
+ return myValues.computeIfAbsent(value, val -> new DfaRangeValue(myFactory, val));
+ }
+ }
+}
diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaRelationValue.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaRelationValue.java
index cb2d4f1689ce..c77b320603ec 100644
--- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaRelationValue.java
+++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaRelationValue.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2000-2009 JetBrains s.r.o.
+ * 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.
@@ -29,6 +29,7 @@ import com.intellij.psi.tree.IElementType;
import com.intellij.util.containers.HashMap;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
@@ -168,6 +169,23 @@ public class DfaRelationValue extends DfaValue {
return myRelation == EQEQ && myIsNegated || myRelation == GT && !myIsNegated || myRelation == GE && myIsNegated;
}
+ /**
+ * @return comparison operation (GT, GE, LE, LT, EQEQ, NE) if this relation represents comparison, null otherwise
+ */
+ @Nullable
+ public IElementType getComparisonOperation() {
+ if(myRelation == GT) {
+ return myIsNegated ? LE : GT;
+ }
+ if(myRelation == GE) {
+ return myIsNegated ? LT : GE;
+ }
+ if(myRelation == EQEQ) {
+ return myIsNegated ? NE : EQEQ;
+ }
+ return null;
+ }
+
public boolean isInstanceOf() {
return myRelation == INSTANCEOF_KEYWORD;
}
diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaValueFactory.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaValueFactory.java
index dc5ccaf45280..fd29e0c5b37a 100644
--- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaValueFactory.java
+++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaValueFactory.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2000-2015 JetBrains s.r.o.
+ * 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.
@@ -60,6 +60,7 @@ public class DfaValueFactory {
myRelationFactory = new DfaRelationValue.Factory(this);
myExpressionFactory = new DfaExpressionFactory(this);
myOptionalFactory = new DfaOptionalValue.Factory(this);
+ myRangeFactory = new DfaRangeValue.Factory(this);
}
public boolean isHonorFieldInitializers() {
@@ -101,12 +102,13 @@ public class DfaValueFactory {
@Nullable
public DfaValue createLiteralValue(PsiLiteralExpression literal) {
- if (literal.getValue() instanceof String) {
- return createTypeValue(literal.getType(), Nullness.NOT_NULL); // Non-null string literal.
- }
return getConstFactory().create(literal);
}
+ public DfaConstValue getBoolean(boolean value) {
+ return value ? getConstFactory().getTrue() : getConstFactory().getFalse();
+ }
+
public static boolean isEffectivelyUnqualified(PsiReferenceExpression refExpression) {
PsiExpression qualifier = refExpression.getQualifierExpression();
if (qualifier == null) {
@@ -137,6 +139,7 @@ public class DfaValueFactory {
private final DfaRelationValue.Factory myRelationFactory;
private final DfaExpressionFactory myExpressionFactory;
private final DfaOptionalValue.Factory myOptionalFactory;
+ private final DfaRangeValue.Factory myRangeFactory;
@NotNull
public DfaVariableValue.Factory getVarFactory() {
@@ -166,4 +169,9 @@ public class DfaValueFactory {
public DfaOptionalValue.Factory getOptionalFactory() {
return myOptionalFactory;
}
+
+ @NotNull
+ public DfaRangeValue.Factory getRangeFactory() {
+ return myRangeFactory;
+ }
}
diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaVariableValue.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaVariableValue.java
index f8265fdaabbd..5ac12eaa3dbb 100644
--- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaVariableValue.java
+++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaVariableValue.java
@@ -39,6 +39,7 @@ import com.intellij.psi.search.searches.ReferencesSearch;
import com.intellij.psi.util.*;
import com.intellij.util.SmartList;
import com.intellij.util.containers.MultiMap;
+import com.siyeh.ig.psiutils.MethodUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -270,6 +271,7 @@ public class DfaVariableValue extends DfaValue {
if (myVariable instanceof PsiVariable && myVariable.hasModifierProperty(PsiModifier.FINAL)) {
return myQualifier != null && myQualifier.isFlushableByCalls();
}
+ if (myVariable instanceof PsiMethod && MethodUtils.isStringLength((PsiMethod)myVariable)) return false;
return true;
}
diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/deprecation/DeprecationInspection.java b/java/java-analysis-impl/src/com/intellij/codeInspection/deprecation/DeprecationInspection.java
index 5c14b72ded54..41a6140e9333 100644
--- a/java/java-analysis-impl/src/com/intellij/codeInspection/deprecation/DeprecationInspection.java
+++ b/java/java-analysis-impl/src/com/intellij/codeInspection/deprecation/DeprecationInspection.java
@@ -44,7 +44,7 @@ public class DeprecationInspection extends BaseJavaBatchLocalInspectionTool {
public static final String DISPLAY_NAME = DeprecationUtil.DEPRECATION_DISPLAY_NAME;
public static final String IGNORE_METHODS_OF_DEPRECATED_NAME = "IGNORE_METHODS_OF_DEPRECATED";
- public boolean IGNORE_INSIDE_DEPRECATED;
+ public boolean IGNORE_INSIDE_DEPRECATED = true;
public boolean IGNORE_ABSTRACT_DEPRECATED_OVERRIDES = true;
public boolean IGNORE_IMPORT_STATEMENTS = true;
public boolean IGNORE_METHODS_OF_DEPRECATED = true;
diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/java15api/Java15APIUsageInspectionBase.java b/java/java-analysis-impl/src/com/intellij/codeInspection/java15api/Java15APIUsageInspectionBase.java
index ce22eee0149c..1e1edf8938d8 100644
--- a/java/java-analysis-impl/src/com/intellij/codeInspection/java15api/Java15APIUsageInspectionBase.java
+++ b/java/java-analysis-impl/src/com/intellij/codeInspection/java15api/Java15APIUsageInspectionBase.java
@@ -15,16 +15,15 @@
*/
package com.intellij.codeInspection.java15api;
-import com.intellij.ToolExtensionPoints;
import com.intellij.codeHighlighting.HighlightDisplayLevel;
import com.intellij.codeInsight.AnnotationUtil;
import com.intellij.codeInsight.daemon.GroupNames;
import com.intellij.codeInsight.intention.QuickFixFactory;
-import com.intellij.codeInspection.*;
+import com.intellij.codeInspection.BaseJavaBatchLocalInspectionTool;
+import com.intellij.codeInspection.InspectionsBundle;
+import com.intellij.codeInspection.LocalQuickFix;
+import com.intellij.codeInspection.ProblemsHolder;
import com.intellij.openapi.diagnostic.Logger;
-import com.intellij.openapi.extensions.ExtensionPoint;
-import com.intellij.openapi.extensions.ExtensionPointName;
-import com.intellij.openapi.extensions.Extensions;
import com.intellij.openapi.module.EffectiveLanguageLevelUtil;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleUtilCore;
@@ -63,8 +62,6 @@ import java.util.Set;
*/
public class Java15APIUsageInspectionBase extends BaseJavaBatchLocalInspectionTool {
public static final String SHORT_NAME = "Since15";
- public static final ExtensionPointName EP_NAME =
- ExtensionPointName.create(ToolExtensionPoints.JAVA15_INSPECTION_TOOL);
private static final String EFFECTIVE_LL = "effectiveLL";
@@ -184,7 +181,6 @@ public class Java15APIUsageInspectionBase extends BaseJavaBatchLocalInspectionTo
private class MyVisitor extends JavaElementVisitor {
private final ProblemsHolder myHolder;
private final boolean myOnTheFly;
- private final ExtensionPoint point = Extensions.getRootArea().getExtensionPoint(EP_NAME);
public MyVisitor(final ProblemsHolder holder, boolean onTheFly) {
myHolder = holder;
@@ -238,7 +234,8 @@ public class Java15APIUsageInspectionBase extends BaseJavaBatchLocalInspectionTo
final Module module = ModuleUtilCore.findModuleForPsiElement(reference.getElement());
if (module != null) {
final LanguageLevel languageLevel = getEffectiveLanguageLevel(module);
- if (isForbiddenApiUsage((PsiMember)resolved, languageLevel)) {
+ LanguageLevel sinceLanguageLevel = getLastIncompatibleLanguageLevel((PsiMember)resolved, languageLevel);
+ if (sinceLanguageLevel != null) {
PsiClass psiClass = null;
final PsiElement qualifier = reference.getQualifier();
if (qualifier != null) {
@@ -255,7 +252,7 @@ public class Java15APIUsageInspectionBase extends BaseJavaBatchLocalInspectionTo
if (isIgnored(superClass)) return;
}
}
- registerError(reference, languageLevel);
+ registerError(reference, sinceLanguageLevel);
} else if (resolved instanceof PsiClass && isInProject(reference)&& !languageLevel.isAtLeast(LanguageLevel.JDK_1_7)) {
final PsiReferenceParameterList parameterList = reference.getParameterList();
if (parameterList != null && parameterList.getTypeParameterElements().length > 0) {
@@ -301,8 +298,9 @@ public class Java15APIUsageInspectionBase extends BaseJavaBatchLocalInspectionTo
if (module != null) {
final LanguageLevel languageLevel = getEffectiveLanguageLevel(module);
if (constructor instanceof PsiCompiledElement) {
- if (isForbiddenApiUsage(constructor, languageLevel)) {
- registerError(expression.getClassReference(), languageLevel);
+ LanguageLevel sinceLanguageLevel = getLastIncompatibleLanguageLevel(constructor, languageLevel);
+ if (sinceLanguageLevel != null) {
+ registerError(expression.getClassReference(), sinceLanguageLevel);
}
}
}
@@ -314,12 +312,14 @@ public class Java15APIUsageInspectionBase extends BaseJavaBatchLocalInspectionTo
PsiAnnotation annotation = !method.isConstructor() ? AnnotationUtil.findAnnotation(method, CommonClassNames.JAVA_LANG_OVERRIDE) : null;
if (annotation != null) {
final Module module = ModuleUtilCore.findModuleForPsiElement(annotation);
+ LanguageLevel sinceLanguageLevel = null;
if (module != null) {
final LanguageLevel languageLevel = getEffectiveLanguageLevel(module);
final PsiMethod[] methods = method.findSuperMethods();
for (PsiMethod superMethod : methods) {
if (superMethod instanceof PsiCompiledElement) {
- if (!isForbiddenApiUsage(superMethod, languageLevel)) {
+ sinceLanguageLevel = getLastIncompatibleLanguageLevel(superMethod, languageLevel);
+ if (sinceLanguageLevel == null) {
return;
}
}
@@ -328,7 +328,7 @@ public class Java15APIUsageInspectionBase extends BaseJavaBatchLocalInspectionTo
}
}
if (methods.length > 0) {
- registerError(annotation.getNameReferenceElement(), languageLevel);
+ registerError(annotation.getNameReferenceElement(), sinceLanguageLevel);
}
}
}
@@ -342,19 +342,9 @@ public class Java15APIUsageInspectionBase extends BaseJavaBatchLocalInspectionTo
private void registerError(PsiJavaCodeReferenceElement reference, LanguageLevel api) {
if (reference != null && isInProject(reference)) {
//noinspection DialogTitleCapitalization
- myHolder.registerProblem(reference, InspectionsBundle.message("inspection.1.5.problem.descriptor", getShortName(api)));
- }
- }
-
- @Override
- public void visitFile(PsiFile file) {
- for (FileCheckingInspection inspection : point.getExtensions()) {
- ProblemDescriptor[] descriptors = inspection.checkFile(file, InspectionManager.getInstance(file.getProject()), myOnTheFly);
- if (descriptors != null) {
- for (ProblemDescriptor descriptor : descriptors) {
- myHolder.registerProblem(descriptor);
- }
- }
+ myHolder.registerProblem(reference,
+ InspectionsBundle.message("inspection.1.5.problem.descriptor", getShortName(api)),
+ myOnTheFly ? new LocalQuickFix[] {(LocalQuickFix)QuickFixFactory.getInstance().createIncreaseLanguageLevelFix(LanguageLevel.values()[api.ordinal() + 1])} : null);
}
}
}
@@ -364,33 +354,32 @@ public class Java15APIUsageInspectionBase extends BaseJavaBatchLocalInspectionTo
return presentableText.substring(0, presentableText.indexOf(' '));
}
- public static boolean isForbiddenApiUsage(@NotNull PsiMember member, @NotNull LanguageLevel languageLevel) {
- if (member instanceof PsiAnonymousClass) return false;
+ public static LanguageLevel getLastIncompatibleLanguageLevel(@NotNull PsiMember member, @NotNull LanguageLevel languageLevel) {
+ if (member instanceof PsiAnonymousClass) return null;
PsiClass containingClass = member.getContainingClass();
- if (containingClass instanceof PsiAnonymousClass) return false;
- if (member instanceof PsiClass && !(member.getParent() instanceof PsiClass || member.getParent() instanceof PsiFile)) return false;
+ if (containingClass instanceof PsiAnonymousClass) return null;
+ if (member instanceof PsiClass && !(member.getParent() instanceof PsiClass || member.getParent() instanceof PsiFile)) return null;
- return isForbiddenSignature(member, languageLevel) ||
- containingClass != null && isForbiddenApiUsage(containingClass, languageLevel);
-
- }
-
- private static boolean isForbiddenSignature(@NotNull PsiMember member, @NotNull LanguageLevel languageLevel) {
Set forbiddenApi = getForbiddenApi(languageLevel);
String signature = getSignature(member);
- return forbiddenApi != null && signature != null && isForbiddenSignature(signature, languageLevel, forbiddenApi);
+ if (forbiddenApi != null && signature != null) {
+ LanguageLevel lastIncompatibleLanguageLevel = getLastIncompatibleLanguageLevelForSignature(signature, languageLevel, forbiddenApi);
+ if (lastIncompatibleLanguageLevel != null) return lastIncompatibleLanguageLevel;
+ }
+ return containingClass != null ? getLastIncompatibleLanguageLevel(containingClass, languageLevel) : null;
+
}
- private static boolean isForbiddenSignature(@NotNull String signature, @NotNull LanguageLevel languageLevel, @NotNull Set forbiddenApi) {
+ private static LanguageLevel getLastIncompatibleLanguageLevelForSignature(@NotNull String signature, @NotNull LanguageLevel languageLevel, @NotNull Set forbiddenApi) {
if (forbiddenApi.contains(signature)) {
- return true;
+ return languageLevel;
}
if (languageLevel.compareTo(ourHighestKnownLanguage) == 0) {
- return false;
+ return null;
}
LanguageLevel nextLanguageLevel = LanguageLevel.values()[languageLevel.ordinal() + 1];
Set nextForbiddenApi = getForbiddenApi(nextLanguageLevel);
- return nextForbiddenApi != null && isForbiddenSignature(signature, nextLanguageLevel, nextForbiddenApi);
+ return nextForbiddenApi != null ? getLastIncompatibleLanguageLevelForSignature(signature, nextLanguageLevel, nextForbiddenApi) : null;
}
/**
diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/javaDoc/JavaDocReferenceInspectionBase.java b/java/java-analysis-impl/src/com/intellij/codeInspection/javaDoc/JavaDocReferenceInspectionBase.java
index 593f2ce0c61c..f7bf4529e25b 100644
--- a/java/java-analysis-impl/src/com/intellij/codeInspection/javaDoc/JavaDocReferenceInspectionBase.java
+++ b/java/java-analysis-impl/src/com/intellij/codeInspection/javaDoc/JavaDocReferenceInspectionBase.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2000-2016 JetBrains s.r.o.
+ * 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.
@@ -106,14 +106,18 @@ public class JavaDocReferenceInspectionBase extends BaseJavaBatchLocalInspectio
@Nullable
@Override
public ProblemDescriptor[] checkFile(@NotNull PsiFile file, @NotNull InspectionManager manager, boolean isOnTheFly) {
- if (!PsiPackage.PACKAGE_INFO_FILE.equals(file.getName()) || !(file instanceof PsiJavaFile)) {
- return null;
+ final String fileName = file.getName();
+ if (PsiPackage.PACKAGE_INFO_FILE.equals(fileName)) {
+ final PsiDocComment docComment = PsiTreeUtil.getChildOfType(file, PsiDocComment.class);
+ return checkComment(docComment, file, manager, isOnTheFly);
}
- final PsiDocComment docComment = PsiTreeUtil.getChildOfType(file, PsiDocComment.class);
- final PsiJavaFile javaFile = (PsiJavaFile)file;
- final String packageName = javaFile.getPackageName();
- final PsiPackage aPackage = JavaPsiFacade.getInstance(file.getProject()).findPackage(packageName);
- return checkComment(docComment, aPackage, manager, isOnTheFly);
+ else if (PsiJavaModule.MODULE_INFO_FILE.equals(fileName)) {
+ final PsiJavaModule module = PsiTreeUtil.getChildOfType(file, PsiJavaModule.class);
+ if (module != null) {
+ return checkComment(module.getDocComment(), file, manager, isOnTheFly);
+ }
+ }
+ return null;
}
@Override
@@ -139,7 +143,7 @@ public class JavaDocReferenceInspectionBase extends BaseJavaBatchLocalInspectio
return checkComment(docCommentOwner.getDocComment(), docCommentOwner, manager, isOnTheFly);
}
- private ProblemDescriptor[] checkComment(PsiDocComment docComment, PsiElement context, InspectionManager manager, boolean isOnTheFly) {
+ private ProblemDescriptor[] checkComment(@Nullable PsiDocComment docComment, PsiElement context, InspectionManager manager, boolean isOnTheFly) {
if (docComment == null) return null;
final List problems = new ArrayList<>();
diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/nullable/NullableStuffInspectionBase.java b/java/java-analysis-impl/src/com/intellij/codeInspection/nullable/NullableStuffInspectionBase.java
index 9dced73577b8..f82c948ee070 100644
--- a/java/java-analysis-impl/src/com/intellij/codeInspection/nullable/NullableStuffInspectionBase.java
+++ b/java/java-analysis-impl/src/com/intellij/codeInspection/nullable/NullableStuffInspectionBase.java
@@ -477,6 +477,7 @@ public class NullableStuffInspectionBase extends BaseJavaBatchLocalInspectionToo
private boolean isNonAnnotatedOverridingNotNull(PsiMethod method, PsiMethod superMethod) {
return REPORT_NOT_ANNOTATED_METHOD_OVERRIDES_NOTNULL &&
!(method.getReturnType() instanceof PsiPrimitiveType) &&
+ !method.isConstructor() &&
!getNullityManager(method).hasNullability(method) &&
isNotNullNotInferred(superMethod, true, IGNORE_EXTERNAL_SUPER_NOTNULL);
}
diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/uncheckedWarnings/UncheckedWarningLocalInspectionBase.java b/java/java-analysis-impl/src/com/intellij/codeInspection/uncheckedWarnings/UncheckedWarningLocalInspectionBase.java
index 37078c49a367..5f6cee2b7d22 100644
--- a/java/java-analysis-impl/src/com/intellij/codeInspection/uncheckedWarnings/UncheckedWarningLocalInspectionBase.java
+++ b/java/java-analysis-impl/src/com/intellij/codeInspection/uncheckedWarnings/UncheckedWarningLocalInspectionBase.java
@@ -61,6 +61,7 @@ public class UncheckedWarningLocalInspectionBase extends BaseJavaBatchLocalInspe
return uncheckedCb;
}
+ @NotNull
private static LocalQuickFix[] getChangeVariableTypeFixes(@NotNull PsiVariable parameter, @Nullable PsiType itemType, LocalQuickFix[] generifyFixes) {
if (itemType instanceof PsiMethodReferenceType) return generifyFixes;
LOG.assertTrue(parameter.isValid());
@@ -148,8 +149,9 @@ public class UncheckedWarningLocalInspectionBase extends BaseJavaBatchLocalInspe
};
}
+ @NotNull
protected LocalQuickFix[] createFixes() {
- return null;
+ return LocalQuickFix.EMPTY_ARRAY;
}
private static String isMethodCalledOnRawType(PsiElement expression) {
diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/visibility/VisibilityInspection.java b/java/java-analysis-impl/src/com/intellij/codeInspection/visibility/VisibilityInspection.java
index 9263824d1015..58ca12a62866 100644
--- a/java/java-analysis-impl/src/com/intellij/codeInspection/visibility/VisibilityInspection.java
+++ b/java/java-analysis-impl/src/com/intellij/codeInspection/visibility/VisibilityInspection.java
@@ -223,7 +223,8 @@ public class VisibilityInspection extends GlobalJavaBatchInspectionTool {
return null;
}
- private static CommonProblemDescriptor[] createDescriptions(RefElement refElement, String access,
+ @NotNull
+ private static CommonProblemDescriptor[] createDescriptions(RefElement refElement, String access,
@NotNull InspectionManager manager,
@NotNull GlobalInspectionContext globalContext) {
final PsiElement element = refElement.getElement();
@@ -250,7 +251,7 @@ public class VisibilityInspection extends GlobalJavaBatchInspectionTool {
new AcceptSuggestedAccess(globalContext.getRefManager(), access, quickFixName),
ProblemHighlightType.GENERIC_ERROR_OR_WARNING, false)};
}
- return null;
+ return CommonProblemDescriptor.EMPTY_ARRAY;
}
int getMinVisibilityLevel(PsiMember member) {
diff --git a/java/java-analysis-impl/src/com/intellij/refactoring/util/duplicates/DuplicatesFinder.java b/java/java-analysis-impl/src/com/intellij/refactoring/util/duplicates/DuplicatesFinder.java
index 26b37eb61212..a9a632fedb00 100644
--- a/java/java-analysis-impl/src/com/intellij/refactoring/util/duplicates/DuplicatesFinder.java
+++ b/java/java-analysis-impl/src/com/intellij/refactoring/util/duplicates/DuplicatesFinder.java
@@ -43,14 +43,14 @@ import java.util.*;
public class DuplicatesFinder {
private static final Logger LOG = Logger.getInstance("#com.intellij.refactoring.util.duplicates.DuplicatesFinder");
public static final Key> PARAMETER = Key.create("PARAMETER");
- private final PsiElement[] myPattern;
+ @NotNull private final PsiElement[] myPattern;
private final InputVariables myParameters;
private final List extends PsiVariable> myOutputParameters;
private final List myPatternAsList;
private boolean myMultipleExitPoints;
@Nullable private final ReturnValue myReturnValue;
- public DuplicatesFinder(PsiElement[] pattern,
+ public DuplicatesFinder(@NotNull PsiElement[] pattern,
InputVariables parameters,
@Nullable ReturnValue returnValue,
@NotNull List extends PsiVariable> outputParameters
@@ -102,6 +102,7 @@ public class DuplicatesFinder {
return myParameters;
}
+ @NotNull
public PsiElement[] getPattern() {
return myPattern;
}
@@ -674,6 +675,7 @@ public class DuplicatesFinder {
return false;
}
+ @NotNull
public static PsiElement[] getFilteredChildren(PsiElement element1) {
PsiElement[] children1 = element1.getChildren();
ArrayList array = new ArrayList<>();
diff --git a/java/java-analysis-impl/src/org/jetbrains/java/generate/GenerateToStringUtils.java b/java/java-analysis-impl/src/org/jetbrains/java/generate/GenerateToStringUtils.java
index dcbf9d18d290..188ad1a334a9 100644
--- a/java/java-analysis-impl/src/org/jetbrains/java/generate/GenerateToStringUtils.java
+++ b/java/java-analysis-impl/src/org/jetbrains/java/generate/GenerateToStringUtils.java
@@ -24,6 +24,7 @@ import org.jetbrains.java.generate.config.FilterPattern;
import org.jetbrains.java.generate.psi.PsiAdapter;
import java.util.ArrayList;
+import java.util.HashSet;
import java.util.List;
/**
@@ -60,9 +61,27 @@ public class GenerateToStringUtils {
FilterPattern pattern) {
if (log.isDebugEnabled()) log.debug("Filtering fields using the pattern: " + pattern);
List availableFields = new ArrayList<>();
+ collectAvailableFields(clazz, includeSuperClass, pattern, availableFields, new HashSet<>());
+ return availableFields.toArray(new PsiField[availableFields.size()]);
+ }
+
+ private static void collectAvailableFields(PsiClass clazz,
+ boolean includeSuperClass,
+ FilterPattern pattern,
+ List availableFields,
+ HashSet visited) {
+
+ int sortElements = GenerateToStringContext.getConfig().getSortElements();
+
+ if (includeSuperClass && sortElements == 3) {
+ PsiClass superClass = clazz.getSuperClass();
+ if (superClass != null && visited.add(superClass)) {
+ collectAvailableFields(superClass, true, pattern, availableFields, visited);
+ }
+ }
// performs til filtering process
- PsiField[] fields = includeSuperClass ? clazz.getAllFields() : clazz.getFields();
+ PsiField[] fields = includeSuperClass && sortElements != 3 ? clazz.getAllFields() : clazz.getFields();
for (PsiField field : fields) {
if (!JavaResolveUtil.isAccessible(field, field.getContainingClass(), field.getModifierList(), clazz, null, null)) {
continue;
@@ -72,8 +91,6 @@ public class GenerateToStringUtils {
availableFields.add(field);
}
}
-
- return availableFields.toArray(new PsiField[availableFields.size()]);
}
/**
@@ -93,7 +110,21 @@ public class GenerateToStringUtils {
public static PsiMethod[] filterAvailableMethods(PsiClass clazz, @NotNull FilterPattern pattern) {
if (log.isDebugEnabled()) log.debug("Filtering methods using the pattern: " + pattern);
List availableMethods = new ArrayList<>();
- PsiMethod[] methods = clazz.getAllMethods();
+ collectAvailableMethods(clazz, pattern, availableMethods, new HashSet<>());
+ return availableMethods.toArray(new PsiMethod[availableMethods.size()]);
+ }
+
+ private static void collectAvailableMethods(PsiClass clazz, @NotNull FilterPattern pattern,
+ List availableMethods,
+ HashSet visited) {
+ int sortElements = GenerateToStringContext.getConfig().getSortElements();
+ if (sortElements == 3) {
+ PsiClass superClass = clazz.getSuperClass();
+ if (superClass != null && visited.add(superClass)) {
+ collectAvailableMethods(superClass, pattern, availableMethods, visited);
+ }
+ }
+ PsiMethod[] methods = sortElements != 3 ? clazz.getAllMethods() : clazz.getMethods();
for (PsiMethod method : methods) {
// the method should be a getter
if (!PsiAdapter.isGetterMethod(method)) {
@@ -133,6 +164,5 @@ public class GenerateToStringUtils {
log.debug("Adding the method " + methodName + " as there is not a field for this getter");
availableMethods.add(method);
}
- return availableMethods.toArray(new PsiMethod[availableMethods.size()]);
}
}
diff --git a/java/java-analysis-impl/src/org/jetbrains/java/generate/config/PolicyOptions.java b/java/java-analysis-impl/src/org/jetbrains/java/generate/config/PolicyOptions.java
index 53381892e3cf..0dcb61bd199b 100644
--- a/java/java-analysis-impl/src/org/jetbrains/java/generate/config/PolicyOptions.java
+++ b/java/java-analysis-impl/src/org/jetbrains/java/generate/config/PolicyOptions.java
@@ -15,6 +15,8 @@
*/
package org.jetbrains.java.generate.config;
+import org.jetbrains.annotations.NotNull;
+
/**
* Options for the various policies.
*/
@@ -30,6 +32,7 @@ public class PolicyOptions {
* Gets the options for the the insert new method policy.
* @return the options for the the insert new method policy.
*/
+ @NotNull
public static InsertWhere[] getNewMethodOptions() {
return newMethodOptions;
}
@@ -38,6 +41,7 @@ public class PolicyOptions {
* Gets the options for the the conflict resolution policy.
* @return the options for the the conflict resolution policy.
*/
+ @NotNull
public static DuplicationPolicy[] getConflictOptions() {
return conflictOptions;
}
diff --git a/java/java-analysis-impl/src/org/jetbrains/java/generate/psi/PsiAdapter.java b/java/java-analysis-impl/src/org/jetbrains/java/generate/psi/PsiAdapter.java
index f4b3ddbfe205..3c923deb54f5 100644
--- a/java/java-analysis-impl/src/org/jetbrains/java/generate/psi/PsiAdapter.java
+++ b/java/java-analysis-impl/src/org/jetbrains/java/generate/psi/PsiAdapter.java
@@ -18,7 +18,6 @@ package org.jetbrains.java.generate.psi;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.projectRoots.JavaSdkVersion;
import com.intellij.openapi.projectRoots.JavaVersionService;
-import com.intellij.openapi.projectRoots.JdkVersionUtil;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.*;
import com.intellij.psi.codeStyle.CodeStyleManager;
@@ -643,6 +642,7 @@ public class PsiAdapter {
* @param clazz the class
* @return the names.
*/
+ @NotNull
public static String[] getImplementsClassnames(PsiClass clazz) {
PsiClass[] interfaces = clazz.getInterfaces();
diff --git a/java/java-impl/java-impl.iml b/java/java-impl/java-impl.iml
index 1f9fd8301eef..85c83c42d0d0 100644
--- a/java/java-impl/java-impl.iml
+++ b/java/java-impl/java-impl.iml
@@ -14,7 +14,6 @@