Skip to content

Commit

Permalink
More precise analysis of Signature string manipulation
Browse files Browse the repository at this point in the history
  • Loading branch information
mernst authored Oct 11, 2024
1 parent 6e8ed8e commit 69d703d
Show file tree
Hide file tree
Showing 5 changed files with 68 additions and 23 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,10 @@ public class SignatureAnnotatedTypeFactory extends BaseAnnotatedTypeFactory {
protected final AnnotationMirror PRIMITIVE_TYPE =
AnnotationBuilder.fromClass(elements, PrimitiveType.class);

/** The {@literal @}{@link Identifier} annotation. */
protected final AnnotationMirror IDENTIFIER =
AnnotationBuilder.fromClass(elements, Identifier.class);

/** The {@link String#replace(char, char)} method. */
private final ExecutableElement replaceCharChar =
TreeUtils.getMethod("java.lang.String", "replace", processingEnv, "char", "char");
Expand Down Expand Up @@ -209,9 +213,20 @@ public SignatureTreeAnnotator(AnnotatedTypeFactory atypeFactory) {

@Override
public Void visitBinary(BinaryTree tree, AnnotatedTypeMirror type) {

if (TreeUtils.isStringConcatenation(tree)) {
// This could be made more precise.
type.replaceAnnotation(SIGNATURE_UNKNOWN);
AnnotatedTypeMirror lType = getAnnotatedType(tree.getLeftOperand());
AnnotatedTypeMirror rType = getAnnotatedType(tree.getRightOperand());

// An identifier can end, but not start, with digits
if (lType.getPrimaryAnnotation(Identifier.class) != null
&& (rType.getPrimaryAnnotation(Identifier.class) != null
|| TypesUtils.isIntegralNumericOrBoxed(rType.getUnderlyingType()))) {
type.replaceAnnotation(IDENTIFIER);
} else {
// This could be made more precise.
type.replaceAnnotation(SIGNATURE_UNKNOWN);
}
}
return null; // super.visitBinary(tree, type);
}
Expand All @@ -231,10 +246,10 @@ public Void visitCompoundAssignment(CompoundAssignmentTree tree, AnnotatedTypeMi
*
* <pre><code>
* {@literal @}InternalForm String internalForm = binaryName.replace('.', '/');
* {@literal @}BinaryName String binaryName = internalForm.replace('/', '.');
* {@literal @}DotSeparatedIdentifiers String dsi = internalForm.replace('/', '.');
* </code></pre>
*
* Class.getName and Class.getCanonicalName(): Cwhen called on a primitive type ,the return a
* Class.getName and Class.getCanonicalName(): when called on a primitive type, they return a
* {@link PrimitiveType}. When called on a non-array, non-nested, non-primitive type, they
* return a {@link BinaryName}:
*
Expand Down Expand Up @@ -276,7 +291,7 @@ public Void visitMethodInvocation(MethodInvocationTree tree, AnnotatedTypeMirror
type.replaceAnnotation(INTERNAL_FORM);
} else if ((oldChar == '/' && newChar == '.')
&& receiverType.getPrimaryAnnotation(InternalForm.class) != null) {
type.replaceAnnotation(BINARY_NAME);
type.replaceAnnotation(DOT_SEPARATED_IDENTIFIERS);
}
} else {
boolean isClassGetName = TreeUtils.isMethodInvocation(tree, classGetName, processingEnv);
Expand Down
8 changes: 8 additions & 0 deletions checker/tests/signature/Concatenation.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import org.checkerframework.checker.signature.qual.*;

public class Concatenation {

@Identifier String m(@Identifier String s, int i) {
return s + i;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -641,7 +641,7 @@ public Void visitNewArray(NewArrayTree tree, AnnotatedTypeMirror mirror) {
/*package-private*/ AnnotationMirror convertSpecialIntRangeToStandardIntRange(
AnnotationMirror anm, TypeKind primitiveKind) {
long max = Long.MAX_VALUE;
if (TypesUtils.isIntegralPrimitive(primitiveKind)) {
if (TypeKindUtils.isIntegral(primitiveKind)) {
Range maxRange = Range.create(primitiveKind);
max = maxRange.to;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,25 @@ public static boolean isIntegral(TypeKind typeKind) {
}
}

/**
* Return true if the argument is one of INT, SHORT, BYTE, LONG.
*
* @param typeKind the TypeKind to inspect
* @return true if typeKind is a primitive integral type kind, excluding CHAR which does not print
* as an integer
*/
public static boolean isIntegralNumeric(TypeKind typeKind) {
switch (typeKind) {
case INT:
case SHORT:
case BYTE:
case LONG:
return true;
default:
return false;
}
}

/**
* Return true if the argument is one of FLOAT, DOUBLE.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -542,37 +542,40 @@ public static boolean isNumericBoxed(TypeMirror type) {
* @return whether the argument is an integral primitive type
*/
public static boolean isIntegralPrimitive(TypeMirror type) {
return isIntegralPrimitive(type.getKind());
return TypeKindUtils.isIntegral(type.getKind());
}

/**
* Return true if the argument TypeMirror is a (possibly boxed) integral type.
*
* @param type the type to inspect
* @return true if type is an integral type
*/
public static boolean isIntegralPrimitiveOrBoxed(TypeMirror type) {
TypeKind kind = TypeKindUtils.primitiveOrBoxedToTypeKind(type);
return kind != null && TypeKindUtils.isIntegral(kind);
}

/**
* Returns true iff the argument is an integral primitive type.
*
* @param typeKind a type kind
* @param type a type
* @return whether the argument is an integral primitive type
*/
public static boolean isIntegralPrimitive(TypeKind typeKind) {
switch (typeKind) {
case BYTE:
case CHAR:
case INT:
case LONG:
case SHORT:
return true;
default:
return false;
}
public static boolean isIntegralNumericPrimitive(TypeMirror type) {
return TypeKindUtils.isIntegralNumeric(type.getKind());
}

/**
* Return true if the argument TypeMirror is a (possibly boxed) integral type.
* Return true if the argument TypeMirror is a (possibly boxed) integral type, excluding char and
* Character which do not print as numbers.
*
* @param type the type to inspect
* @return true if type is an integral type
* @return true if type is an integral numeric type
*/
public static boolean isIntegralPrimitiveOrBoxed(TypeMirror type) {
public static boolean isIntegralNumericOrBoxed(TypeMirror type) {
TypeKind kind = TypeKindUtils.primitiveOrBoxedToTypeKind(type);
return kind != null && TypeKindUtils.isIntegral(kind);
return kind != null && TypeKindUtils.isIntegralNumeric(kind);
}

/**
Expand Down

0 comments on commit 69d703d

Please sign in to comment.