org.eclipse.jdt.internal.compiler.ast.MemberValuePair.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.jdt.internal.compiler.ast.MemberValuePair.java

Source

/*******************************************************************************
 * Copyright (c) 2000, 2017 IBM Corporation and others.
 *
 * This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License 2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *     Stephan Herrmann - Contributions for
 *                        bug 186342 - [compiler][null] Using annotations for null checking
 *                        bug 365519 - editorial cleanup after bug 186342 and bug 365387
 *                        Bug 434570 - Generic type mismatch for parametrized class annotation attribute with inner class
 *******************************************************************************/
package org.eclipse.jdt.internal.compiler.ast;

import org.eclipse.jdt.internal.compiler.ASTVisitor;
import org.eclipse.jdt.internal.compiler.impl.Constant;
import org.eclipse.jdt.internal.compiler.lookup.Binding;
import org.eclipse.jdt.internal.compiler.lookup.BlockScope;
import org.eclipse.jdt.internal.compiler.lookup.ClassScope;
import org.eclipse.jdt.internal.compiler.lookup.ElementValuePair;
import org.eclipse.jdt.internal.compiler.lookup.FieldBinding;
import org.eclipse.jdt.internal.compiler.lookup.LocalVariableBinding;
import org.eclipse.jdt.internal.compiler.lookup.MethodBinding;
import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;

/**
 * MemberValuePair node
 */
public class MemberValuePair extends ASTNode {

    public char[] name;
    public Expression value;
    public MethodBinding binding;
    /**
     *  The representation of this pair in the type system.
     */
    public ElementValuePair compilerElementPair = null;

    public MemberValuePair(char[] token, int sourceStart, int sourceEnd, Expression value) {
        this.name = token;
        this.sourceStart = sourceStart;
        this.sourceEnd = sourceEnd;
        this.value = value;
        if (value instanceof ArrayInitializer) {
            value.bits |= IsAnnotationDefaultValue;
        }
    }

    @Override
    public StringBuffer print(int indent, StringBuffer output) {
        output.append(this.name).append(" = "); //$NON-NLS-1$
        this.value.print(0, output);
        return output;
    }

    public void resolveTypeExpecting(final BlockScope scope, final TypeBinding requiredType) {
        if (this.compilerElementPair != null) {
            return;
        }

        if (this.value == null) {
            this.compilerElementPair = new ElementValuePair(this.name, this.value, this.binding);
            return;
        }
        if (requiredType == null) {
            // fault tolerance: keep resolving
            if (this.value instanceof ArrayInitializer) {
                this.value.resolveTypeExpecting(scope, null);
            } else {
                this.value.resolveType(scope);
            }
            this.compilerElementPair = new ElementValuePair(this.name, this.value, this.binding);
            return;
        }

        this.value.setExpectedType(requiredType); // needed in case of generic method invocation - looks suspect, generic method invocation here ???
        final TypeBinding valueType;
        if (this.value instanceof ArrayInitializer) {
            ArrayInitializer initializer = (ArrayInitializer) this.value;
            valueType = initializer.resolveTypeExpecting(scope, this.binding.returnType);
        } else if (this.value instanceof ArrayAllocationExpression) {
            scope.problemReporter().annotationValueMustBeArrayInitializer(this.binding.declaringClass, this.name,
                    this.value);
            this.value.resolveType(scope);
            valueType = null; // no need to pursue
        } else {
            valueType = this.value.resolveType(scope);
            // https://bugs.eclipse.org/bugs/show_bug.cgi?id=248897
            ASTVisitor visitor = new ASTVisitor() {
                @Override
                public boolean visit(SingleNameReference reference, BlockScope scop) {
                    if (reference.binding instanceof LocalVariableBinding) {
                        ((LocalVariableBinding) reference.binding).useFlag = LocalVariableBinding.USED;
                    }
                    return true;
                }
            };
            this.value.traverse(visitor, scope);
        }
        this.compilerElementPair = new ElementValuePair(this.name, this.value, this.binding);
        if (valueType == null)
            return;

        final TypeBinding leafType = requiredType.leafComponentType();
        // the next check may need deferring:
        final boolean[] shouldExit = { false };
        Runnable check = new Runnable() {
            @Override
            public void run() {
                if (!(MemberValuePair.this.value.isConstantValueOfTypeAssignableToType(valueType, requiredType)
                        || valueType.isCompatibleWith(requiredType))) {
                    if (!(requiredType.isArrayType() && requiredType.dimensions() == 1
                            && (MemberValuePair.this.value.isConstantValueOfTypeAssignableToType(valueType,
                                    leafType) || valueType.isCompatibleWith(leafType)))) {

                        if (leafType.isAnnotationType() && !valueType.isAnnotationType()) {
                            scope.problemReporter().annotationValueMustBeAnnotation(
                                    MemberValuePair.this.binding.declaringClass, MemberValuePair.this.name,
                                    MemberValuePair.this.value, leafType);
                        } else {
                            scope.problemReporter().typeMismatchError(valueType, requiredType,
                                    MemberValuePair.this.value, null);
                        }
                        shouldExit[0] = true; // TODO may allow to proceed to find more errors at once
                    }
                } else {
                    scope.compilationUnitScope().recordTypeConversion(requiredType.leafComponentType(),
                            valueType.leafComponentType());
                    MemberValuePair.this.value.computeConversion(scope, requiredType, valueType);
                }
            }
        };
        // ... now or later?
        if (!scope.deferCheck(check)) {
            check.run();
            if (shouldExit[0])
                return;
        }

        // annotation methods can only return base types, String, Class, enum type, annotation types and arrays of these
        checkAnnotationMethodType: {
            switch (leafType.erasure().id) {
            case T_byte:
            case T_short:
            case T_char:
            case T_int:
            case T_long:
            case T_float:
            case T_double:
            case T_boolean:
            case T_JavaLangString:
                if (this.value instanceof ArrayInitializer) {
                    ArrayInitializer initializer = (ArrayInitializer) this.value;
                    final Expression[] expressions = initializer.expressions;
                    if (expressions != null) {
                        for (int i = 0, max = expressions.length; i < max; i++) {
                            Expression expression = expressions[i];
                            if (expression.resolvedType == null)
                                continue; // fault-tolerance
                            if (expression.constant == Constant.NotAConstant) {
                                scope.problemReporter().annotationValueMustBeConstant(this.binding.declaringClass,
                                        this.name, expressions[i], false);
                            }
                        }
                    }
                } else if (this.value.constant == Constant.NotAConstant) {
                    if (valueType.isArrayType()) {
                        scope.problemReporter().annotationValueMustBeArrayInitializer(this.binding.declaringClass,
                                this.name, this.value);
                    } else {
                        scope.problemReporter().annotationValueMustBeConstant(this.binding.declaringClass,
                                this.name, this.value, false);
                    }
                }
                break checkAnnotationMethodType;
            case T_JavaLangClass:
                if (this.value instanceof ArrayInitializer) {
                    ArrayInitializer initializer = (ArrayInitializer) this.value;
                    final Expression[] expressions = initializer.expressions;
                    if (expressions != null) {
                        for (int i = 0, max = expressions.length; i < max; i++) {
                            Expression currentExpression = expressions[i];
                            if (!(currentExpression instanceof ClassLiteralAccess)) {
                                scope.problemReporter().annotationValueMustBeClassLiteral(
                                        this.binding.declaringClass, this.name, currentExpression);
                            }
                        }
                    }
                } else if (!(this.value instanceof ClassLiteralAccess)) {
                    scope.problemReporter().annotationValueMustBeClassLiteral(this.binding.declaringClass,
                            this.name, this.value);
                }
                break checkAnnotationMethodType;
            }
            if (leafType.isEnum()) {
                if (this.value instanceof NullLiteral) {
                    scope.problemReporter().annotationValueMustBeConstant(this.binding.declaringClass, this.name,
                            this.value, true);
                } else if (this.value instanceof ArrayInitializer) {
                    ArrayInitializer initializer = (ArrayInitializer) this.value;
                    final Expression[] expressions = initializer.expressions;
                    if (expressions != null) {
                        for (int i = 0, max = expressions.length; i < max; i++) {
                            Expression currentExpression = expressions[i];
                            if (currentExpression instanceof NullLiteral) {
                                scope.problemReporter().annotationValueMustBeConstant(this.binding.declaringClass,
                                        this.name, currentExpression, true);
                            } else if (currentExpression instanceof NameReference) {
                                NameReference nameReference = (NameReference) currentExpression;
                                final Binding nameReferenceBinding = nameReference.binding;
                                if (nameReferenceBinding.kind() == Binding.FIELD) {
                                    FieldBinding fieldBinding = (FieldBinding) nameReferenceBinding;
                                    if (!fieldBinding.declaringClass.isEnum()) {
                                        scope.problemReporter().annotationValueMustBeConstant(
                                                this.binding.declaringClass, this.name, currentExpression, true);
                                    }
                                }
                            }
                        }
                    }
                } else if (this.value instanceof NameReference) {
                    NameReference nameReference = (NameReference) this.value;
                    final Binding nameReferenceBinding = nameReference.binding;
                    if (nameReferenceBinding.kind() == Binding.FIELD) {
                        FieldBinding fieldBinding = (FieldBinding) nameReferenceBinding;
                        if (!fieldBinding.declaringClass.isEnum()) {
                            if (!fieldBinding.type.isArrayType()) {
                                scope.problemReporter().annotationValueMustBeConstant(this.binding.declaringClass,
                                        this.name, this.value, true);
                            } else {
                                scope.problemReporter().annotationValueMustBeArrayInitializer(
                                        this.binding.declaringClass, this.name, this.value);
                            }
                        }
                    }
                } else {
                    scope.problemReporter().annotationValueMustBeConstant(this.binding.declaringClass, this.name,
                            this.value, true);
                }
                break checkAnnotationMethodType;
            }
            if (leafType.isAnnotationType()) {
                if (!valueType.leafComponentType().isAnnotationType()) { // check annotation type and also reject null literal
                    scope.problemReporter().annotationValueMustBeAnnotation(this.binding.declaringClass, this.name,
                            this.value, leafType);
                } else if (this.value instanceof ArrayInitializer) {
                    ArrayInitializer initializer = (ArrayInitializer) this.value;
                    final Expression[] expressions = initializer.expressions;
                    if (expressions != null) {
                        for (int i = 0, max = expressions.length; i < max; i++) {
                            Expression currentExpression = expressions[i];
                            if (currentExpression instanceof NullLiteral
                                    || !(currentExpression instanceof Annotation)) {
                                scope.problemReporter().annotationValueMustBeAnnotation(this.binding.declaringClass,
                                        this.name, currentExpression, leafType);
                            }
                        }
                    }
                } else if (!(this.value instanceof Annotation)) {
                    scope.problemReporter().annotationValueMustBeAnnotation(this.binding.declaringClass, this.name,
                            this.value, leafType);
                }
                break checkAnnotationMethodType;
            }
        }
    }

    @Override
    public void traverse(ASTVisitor visitor, BlockScope scope) {
        if (visitor.visit(this, scope)) {
            if (this.value != null) {
                this.value.traverse(visitor, scope);
            }
        }
        visitor.endVisit(this, scope);
    }

    public void traverse(ASTVisitor visitor, ClassScope scope) {
        if (visitor.visit(this, scope)) {
            if (this.value != null) {
                this.value.traverse(visitor, scope);
            }
        }
        visitor.endVisit(this, scope);
    }
}