-
Notifications
You must be signed in to change notification settings - Fork 348
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Propagate length information from argument to return value of Arrays.copyOf() #3524
Changes from 7 commits
5624d27
3723b51
32a728f
2fea713
888a7a4
48ad98a
688ca4f
3966be6
35a6430
ecb48f5
e309153
221d3c0
5a02c56
881bbe9
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
// Test case for https://tinyurl.com/cfissue/3224 | ||
|
||
import java.util.Arrays; | ||
import org.checkerframework.common.value.qual.MinLen; | ||
|
||
public class Issue3224 { | ||
|
||
public static void m1(String @MinLen(1) [] args) { | ||
int i = 4; | ||
String @MinLen(1) [] args2 = Arrays.copyOf(args, i); | ||
} | ||
|
||
public static void m2(String @MinLen(1) [] args) { | ||
String @MinLen(1) [] args2 = Arrays.copyOf(args, args.length); | ||
} | ||
|
||
public static void m3(String @MinLen(1) ... args) { | ||
String @MinLen(1) [] args2 = Arrays.copyOf(args, args.length); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,12 +2,14 @@ | |
|
||
import com.sun.source.tree.AnnotationTree; | ||
import com.sun.source.tree.ExpressionTree; | ||
import com.sun.source.tree.MethodInvocationTree; | ||
import com.sun.source.tree.MethodTree; | ||
import com.sun.source.tree.Tree; | ||
import com.sun.source.tree.Tree.Kind; | ||
import com.sun.source.tree.TypeCastTree; | ||
import java.util.Collections; | ||
import java.util.List; | ||
import java.util.stream.Collectors; | ||
import javax.lang.model.element.AnnotationMirror; | ||
import javax.lang.model.type.TypeKind; | ||
import javax.lang.model.type.TypeMirror; | ||
|
@@ -17,6 +19,7 @@ | |
import org.checkerframework.common.value.qual.IntRangeFromGTENegativeOne; | ||
import org.checkerframework.common.value.qual.IntRangeFromNonNegative; | ||
import org.checkerframework.common.value.qual.IntRangeFromPositive; | ||
import org.checkerframework.common.value.qual.IntVal; | ||
import org.checkerframework.common.value.util.NumberUtils; | ||
import org.checkerframework.common.value.util.Range; | ||
import org.checkerframework.framework.type.AnnotatedTypeMirror; | ||
|
@@ -76,6 +79,26 @@ protected void commonAssignmentCheck( | |
getTypeFactory().createIntRangeAnnotation(Range.CHAR_EVERYTHING)); | ||
} | ||
|
||
if (valueTree.getKind() == Kind.METHOD_INVOCATION | ||
&& TreeUtils.isArrayscopyOfMethodInvocation((MethodInvocationTree) valueTree) | ||
&& valueType.getKind() == TypeKind.ARRAY) { | ||
List<? extends ExpressionTree> args = ((MethodInvocationTree) valueTree).getArguments(); | ||
AnnotatedTypeMirror arrType = atypeFactory.getAnnotatedType(args.get(0)); | ||
if (TreeUtils.isArrayLengthAccess(args.get(1))) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This only tests that the second argument is any array length access expression. The correct test needs to check that it is an array length access for the array that is the first argument. You can add a test case for this:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we want to check this now? As suggested above I have added to There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In the test case I suggested, that is a true positive: |
||
valueType = arrType; | ||
} else if (atypeFactory.getAnnotatedType(args.get(1)).getAnnotation(IntVal.class) | ||
!= null) { | ||
kelloggm marked this conversation as resolved.
Show resolved
Hide resolved
|
||
AnnotationMirror argType = | ||
atypeFactory.getAnnotatedType(args.get(1)).getAnnotation(IntVal.class); | ||
valueType.addAnnotation( | ||
getTypeFactory() | ||
.createArrayLenAnnotation( | ||
ValueAnnotatedTypeFactory.getIntValues(argType).stream() | ||
.map(Long::intValue) | ||
.collect(Collectors.toList()))); | ||
} | ||
} | ||
|
||
super.commonAssignmentCheck(varType, valueType, valueTree, errorKey, extraArgs); | ||
} | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1273,6 +1273,27 @@ public static boolean isAnonymousConstructor(final MethodTree method) { | |
return false; | ||
} | ||
|
||
/** | ||
* Determines if the tree is a call to Arrays.copyOf() | ||
* | ||
* @param tree tree to check | ||
* @return true if the given tree is a call to Arrays.copyOf() method | ||
*/ | ||
public static boolean isArrayscopyOfMethodInvocation(MethodInvocationTree tree) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: in camel-case, this should be |
||
if (tree.getMethodSelect().getKind() != Kind.MEMBER_SELECT) { | ||
return false; | ||
} | ||
|
||
MemberSelectTree memberSelectTree = (MemberSelectTree) tree.getMethodSelect(); | ||
|
||
if (memberSelectTree.getExpression().toString().equals("Arrays") | ||
&& getMethodName(memberSelectTree).equals("copyOf")) { | ||
return true; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This test isn't strong enough - it will return true on any method named You should look at how (It would probably also be worthwhile to add an inner static class named "Arrays" with a static "copyOf" method to the tests and then ensure that your code is not invoked when that method is called.) |
||
} | ||
|
||
return false; | ||
} | ||
|
||
/** | ||
* Converts the given AnnotationTrees to AnnotationMirrors. | ||
* | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please add a check here that the length of
args
is what you expect (i.e. 2) and throw aBugInCF
otherwise