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 index 96189835fe4d..2547db08515a 100644 --- 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 @@ -4,10 +4,11 @@ package com.intellij.codeInspection.dataFlow.rangeSet; import com.intellij.codeInsight.AnnotationUtil; import com.intellij.codeInspection.dataFlow.DfaFactType; import com.intellij.codeInspection.dataFlow.value.*; -import com.intellij.psi.*; +import com.intellij.psi.PsiAnnotation; +import com.intellij.psi.PsiModifierListOwner; +import com.intellij.psi.PsiPrimitiveType; +import com.intellij.psi.PsiType; import com.intellij.util.ThreeState; -import com.siyeh.ig.callMatcher.CallMapper; -import com.siyeh.ig.callMatcher.CallMatcher; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -24,16 +25,6 @@ import static com.intellij.codeInsight.AnnotationUtil.CHECK_TYPE; * @author Tagir Valeev */ public abstract class LongRangeSet { - // TODO: create an external annotation and use it - private static final CallMapper KNOWN_METHOD_RANGES = new CallMapper() - .register(CallMatcher.instanceCall("java.time.LocalDateTime", "getHour"), range(0, 23)) - .register(CallMatcher.instanceCall("java.time.LocalDateTime", "getMinute", "getSecond"), range(0, 59)) - .register(CallMatcher.staticCall(CommonClassNames.JAVA_LANG_LONG, "numberOfLeadingZeros", "numberOfTrailingZeros", "bitCount"), - range(0, Long.SIZE)) - .register(CallMatcher.staticCall(CommonClassNames.JAVA_LANG_INTEGER, "numberOfLeadingZeros", "numberOfTrailingZeros", "bitCount"), - range(0, Integer.SIZE)) - .register(CallMatcher.instanceCall(CommonClassNames.JAVA_LANG_ENUM, "ordinal").parameterCount(0), indexRange()); - LongRangeSet() {} /** @@ -446,10 +437,12 @@ public abstract class LongRangeSet { @NotNull public static LongRangeSet fromPsiElement(PsiModifierListOwner owner) { if (owner == null) return all(); - if (owner instanceof PsiMethod) { - LongRangeSet rangeSet = KNOWN_METHOD_RANGES.mapFirst((PsiMethod)owner); - if (rangeSet != null) { - return rangeSet; + PsiAnnotation rangeAnnotation = AnnotationUtil.findAnnotation(owner, "org.jetbrains.annotations.Range"); + if(rangeAnnotation != null) { + Long from = AnnotationUtil.getLongAttributeValue(rangeAnnotation, "from"); + Long to = AnnotationUtil.getLongAttributeValue(rangeAnnotation, "to"); + if(from != null && to != null && to >= from) { + return range(from, to); } } if (AnnotationUtil.isAnnotated(owner, "javax.annotation.Nonnegative", CHECK_TYPE)) { diff --git a/java/java-psi-api/src/com/intellij/codeInsight/AnnotationUtil.java b/java/java-psi-api/src/com/intellij/codeInsight/AnnotationUtil.java index 2bff3b83de0c..98656378f5ae 100644 --- a/java/java-psi-api/src/com/intellij/codeInsight/AnnotationUtil.java +++ b/java/java-psi-api/src/com/intellij/codeInsight/AnnotationUtil.java @@ -7,7 +7,10 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Comparing; import com.intellij.psi.*; import com.intellij.psi.util.*; -import com.intellij.util.*; +import com.intellij.util.ArrayUtil; +import com.intellij.util.Consumer; +import com.intellij.util.Processor; +import com.intellij.util.Processors; import com.intellij.util.containers.ConcurrentFactoryMap; import com.intellij.util.containers.ContainerUtil; import gnu.trove.THashMap; @@ -487,6 +490,13 @@ public class AnnotationUtil { return constValue instanceof Boolean ? (Boolean)constValue : null; } + @Nullable + public static Long getLongAttributeValue(@NotNull PsiAnnotation anno, @Nullable final String attributeName) { + PsiAnnotationMemberValue attrValue = anno.findAttributeValue(attributeName); + Object constValue = JavaPsiFacade.getInstance(anno.getProject()).getConstantEvaluationHelper().computeConstantExpression(attrValue); + return constValue instanceof Number ? ((Number)constValue).longValue() : null; + } + @Nullable public static String getDeclaredStringAttributeValue(@NotNull PsiAnnotation anno, @Nullable final String attributeName) { PsiAnnotationMemberValue attrValue = anno.findDeclaredAttributeValue(attributeName); diff --git a/java/jdkAnnotations/java/lang/annotations.xml b/java/jdkAnnotations/java/lang/annotations.xml index d96b472ea5d7..8464e1c7e57b 100644 --- a/java/jdkAnnotations/java/lang/annotations.xml +++ b/java/jdkAnnotations/java/lang/annotations.xml @@ -61,6 +61,12 @@ + + + + + + @@ -70,6 +76,24 @@ + + + + + + + + + + + + + + + + + + @@ -91,6 +115,24 @@ + + + + + + + + + + + + + + + + + + diff --git a/java/jdkAnnotations/java/time/annotations.xml b/java/jdkAnnotations/java/time/annotations.xml new file mode 100644 index 000000000000..f02bf8584a66 --- /dev/null +++ b/java/jdkAnnotations/java/time/annotations.xml @@ -0,0 +1,163 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/platform/core-api/src/org/jetbrains/annotations/Range.java b/platform/core-api/src/org/jetbrains/annotations/Range.java new file mode 100644 index 000000000000..1846875650fe --- /dev/null +++ b/platform/core-api/src/org/jetbrains/annotations/Range.java @@ -0,0 +1,27 @@ +/* + * Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. + */ +package org.jetbrains.annotations; + +import java.lang.annotation.*; + +/** + * An annotation which allows to specify for integral type (byte, char, short, int, long) an allowed values range. + *

+ * This is an experimental annotation which can be changed or removed without additional notice! + */ +@Documented +@Retention(RetentionPolicy.CLASS) +@Target({ElementType.TYPE_USE}) +@ApiStatus.Experimental +public @interface Range { + /** + * @return minimal allowed value (inclusive) + */ + long from(); + + /** + * @return maximal allowed value (inclusive) + */ + long to(); +} \ No newline at end of file