aboutsummaryrefslogtreecommitdiffhomepage
path: root/third_party/checker_framework_javacutil/java/org/checkerframework/javacutil/TypesUtils.java
blob: f024a60feb198106efeff914003c19e47b074235 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
package org.checkerframework.javacutil;

import javax.annotation.processing.ProcessingEnvironment;
import javax.lang.model.element.Name;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.ArrayType;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.type.TypeVariable;
import javax.lang.model.type.WildcardType;
import javax.lang.model.util.Elements;
import javax.lang.model.util.Types;

import com.sun.tools.javac.code.Symtab;
import com.sun.tools.javac.code.Type;
import com.sun.tools.javac.code.TypeTag;
import com.sun.tools.javac.model.JavacTypes;
import com.sun.tools.javac.processing.JavacProcessingEnvironment;
import com.sun.tools.javac.util.Context;

/**
 * A utility class that helps with {@link TypeMirror}s.
 *
 */
// TODO: This class needs significant restructuring
public final class TypesUtils {

    // Class cannot be instantiated
    private TypesUtils() { throw new AssertionError("Class TypesUtils cannot be instantiated."); }

    /**
     * Gets the fully qualified name for a provided type.  It returns an empty
     * name if type is an anonymous type.
     *
     * @param type the declared type
     * @return the name corresponding to that type
     */
    public static Name getQualifiedName(DeclaredType type) {
        TypeElement element = (TypeElement) type.asElement();
        return element.getQualifiedName();
    }

    /**
     * Checks if the type represents a java.lang.Object declared type.
     *
     * @param type  the type
     * @return true iff type represents java.lang.Object
     */
    public static boolean isObject(TypeMirror type) {
        return isDeclaredOfName(type, "java.lang.Object");
    }

    /**
     * Checks if the type represents a java.lang.Class declared type.
     *
     * @param type  the type
     * @return true iff type represents java.lang.Class
     */
    public static boolean isClass(TypeMirror type) {
        return isDeclaredOfName(type, "java.lang.Class");
    }

    /**
     * Checks if the type represents a java.lang.String declared type.
     * TODO: it would be cleaner to use String.class.getCanonicalName(), but
     *   the two existing methods above don't do that, I guess for performance reasons.
     *
     * @param type  the type
     * @return true iff type represents java.lang.String
     */
    public static boolean isString(TypeMirror type) {
        return isDeclaredOfName(type, "java.lang.String");
    }

    /**
     * Checks if the type represents a boolean type, that is either boolean
     * (primitive type) or java.lang.Boolean.
     *
     * @param type the type to test
     * @return true iff type represents a boolean type
     */
    public static boolean isBooleanType(TypeMirror type) {
        return isDeclaredOfName(type, "java.lang.Boolean")
                || type.getKind().equals(TypeKind.BOOLEAN);
    }

    /**
     * Check if the type represent a declared type of the given qualified name
     *
     * @param type the type
     * @return type iff type represents a declared type of the qualified name
     */
    public static boolean isDeclaredOfName(TypeMirror type, CharSequence qualifiedName) {
        return type.getKind() == TypeKind.DECLARED
            && getQualifiedName((DeclaredType)type).contentEquals(qualifiedName);
    }

    public static boolean isBoxedPrimitive(TypeMirror type) {
        if (type.getKind() != TypeKind.DECLARED)
            return false;

        String qualifiedName = getQualifiedName((DeclaredType)type).toString();

        return (qualifiedName.equals("java.lang.Boolean")
                || qualifiedName.equals("java.lang.Byte")
                || qualifiedName.equals("java.lang.Character")
                || qualifiedName.equals("java.lang.Short")
                || qualifiedName.equals("java.lang.Integer")
                || qualifiedName.equals("java.lang.Long")
                || qualifiedName.equals("java.lang.Double")
                || qualifiedName.equals("java.lang.Float"));
    }

    /** @return type represents a Throwable type (e.g. Exception, Error) **/
    public static boolean isThrowable(TypeMirror type) {
        while (type != null && type.getKind() == TypeKind.DECLARED) {
            DeclaredType dt = (DeclaredType) type;
            TypeElement elem = (TypeElement) dt.asElement();
            Name name = elem.getQualifiedName();
            if ("java.lang.Throwable".contentEquals(name))
                return true;
            type = elem.getSuperclass();
        }
        return false;
    }

    /**
     * Returns true iff the argument is a primitive type.
     *
     * @return  whether the argument is a primitive type
     */
    public static boolean isPrimitive(TypeMirror type) {
        switch (type.getKind()) {
        case BOOLEAN:
        case BYTE:
        case CHAR:
        case DOUBLE:
        case FLOAT:
        case INT:
        case LONG:
        case SHORT:
            return true;
        default:
            return false;
        }
    }

    /**
     * Returns true iff the arguments are both the same primitive types.
     *
     * @return  whether the arguments are the same primitive types
     */
    public static boolean areSamePrimitiveTypes(TypeMirror left, TypeMirror right) {
        if (!isPrimitive(left) || !isPrimitive(right)) {
            return false;
        }

        return (left.getKind() == right.getKind());
    }

    /**
     * Returns true iff the argument is a primitive numeric type.
     *
     * @return  whether the argument is a primitive numeric type
     */
    public static boolean isNumeric(TypeMirror type) {
        switch (type.getKind()) {
        case BYTE:
        case CHAR:
        case DOUBLE:
        case FLOAT:
        case INT:
        case LONG:
        case SHORT:
            return true;
        default:
            return false;
        }
    }

    /**
     * Returns true iff the argument is an integral type.
     *
     * @return  whether the argument is an integral type
     */
    public static boolean isIntegral(TypeMirror type) {
        switch (type.getKind()) {
        case BYTE:
        case CHAR:
        case INT:
        case LONG:
        case SHORT:
            return true;
        default:
            return false;
        }
    }

    /**
     * Returns true iff the argument is a floating point type.
     *
     * @return  whether the argument is a floating point type
     */
    public static boolean isFloating(TypeMirror type) {
        switch (type.getKind()) {
        case DOUBLE:
        case FLOAT:
            return true;
        default:
            return false;
        }
    }

    /**
     * Returns the widened numeric type for an arithmetic operation
     * performed on a value of the left type and the right type.
     * Defined in JLS 5.6.2.  We return a {@link TypeKind} because
     * creating a {@link TypeMirror} requires a {@link Types} object
     * from the {@link javax.annotation.processing.ProcessingEnvironment}.
     *
     * @return  the result of widening numeric conversion, or NONE when
     *          the conversion cannot be performed
     */
    public static TypeKind widenedNumericType(TypeMirror left, TypeMirror right) {
        if (!isNumeric(left) || !isNumeric(right)) {
            return TypeKind.NONE;
        }

        TypeKind leftKind = left.getKind();
        TypeKind rightKind = right.getKind();

        if (leftKind == TypeKind.DOUBLE || rightKind == TypeKind.DOUBLE) {
            return TypeKind.DOUBLE;
        }

        if (leftKind == TypeKind.FLOAT || rightKind == TypeKind.FLOAT) {
            return TypeKind.FLOAT;
        }

        if (leftKind == TypeKind.LONG || rightKind == TypeKind.LONG) {
            return TypeKind.LONG;
        }

        return TypeKind.INT;
    }

    /**
     * If the argument is a bounded TypeVariable or WildcardType,
     * return its non-variable, non-wildcard upper bound.  Otherwise,
     * return the type itself.
     *
     * @param type  a type
     * @return  the non-variable, non-wildcard upper bound of a type,
     *    if it has one, or itself if it has no bounds
     */
    public static TypeMirror upperBound(TypeMirror type) {
        do {
            if (type instanceof TypeVariable) {
                TypeVariable tvar = (TypeVariable) type;
                if (tvar.getUpperBound() != null) {
                    type = tvar.getUpperBound();
                } else {
                    break;
                }
            } else if (type instanceof WildcardType) {
                WildcardType wc = (WildcardType) type;
                if (wc.getExtendsBound() != null) {
                    type = wc.getExtendsBound();
                } else {
                    break;
                }
            } else {
                break;
            }
        } while (true);
        return type;
    }

    // Version of com.sun.tools.javac.code.Types.wildUpperBound(Type)
    // that works with both jdk8 (called upperBound there) and jdk8u.
    // TODO: contrast to upperBound.
    public static Type wildUpperBound(ProcessingEnvironment env, TypeMirror tm) {
        Type t = (Type) tm;
        if (t.hasTag(TypeTag.WILDCARD)) {
            Context context = ((JavacProcessingEnvironment) env).getContext();
            Type.WildcardType w = (Type.WildcardType) TypeAnnotationUtils.unannotatedType(t);
            if (w.isSuperBound()) {
                Symtab syms = Symtab.instance(context);
                return w.bound == null ? syms.objectType : w.bound.bound;
            } else {
                return wildUpperBound(env, w.type);
            }
        }
        else {
            return TypeAnnotationUtils.unannotatedType(t);
        }
    }

    /**
     * Returns the {@link TypeMirror} for a given {@link Class}.
     */
    public static TypeMirror typeFromClass(Types types, Elements elements, Class<?> clazz) {
        if (clazz == void.class) {
            return types.getNoType(TypeKind.VOID);
        } else if (clazz.isPrimitive()) {
            String primitiveName = clazz.getName().toUpperCase();
            TypeKind primitiveKind = TypeKind.valueOf(primitiveName);
            return types.getPrimitiveType(primitiveKind);
        } else if (clazz.isArray()) {
            TypeMirror componentType = typeFromClass(types, elements, clazz.getComponentType());
            return types.getArrayType(componentType);
        } else {
            TypeElement element = elements.getTypeElement(clazz.getCanonicalName());
            if (element == null) {
                ErrorReporter.errorAbort("Unrecognized class: " + clazz);
                return null; // dead code
            }
            return element.asType();
        }
    }

    /**
     * Returns an {@link ArrayType} with elements of type {@code componentType}.
     */
    public static ArrayType createArrayType(Types types, TypeMirror componentType) {
        JavacTypes t = (JavacTypes) types;
        return t.getArrayType(componentType);
    }
}