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

Java tutorial

Introduction

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

Source

/*******************************************************************************
 * Copyright (c) 2000, 2018 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 - Contribution for
 *                        bug 395002 - Self bound generic class doesn't resolve bounds properly for wildcards for certain parametrisation.
 *                        bug 331649 - [compiler][null] consider null annotations for fields
 *                        bug 400761 - [compiler][null] null may be return as boolean without a diagnostic
 *                        Bug 427438 - [1.8][compiler] NPE at org.eclipse.jdt.internal.compiler.ast.ConditionalExpression.generateCode(ConditionalExpression.java:280)
 *                        Bug 429403 - [1.8][null] null mismatch from type arguments is not reported at field initializer
 *                        Bug 453483 - [compiler][null][loop] Improve null analysis for loops
 *                        Bug 458396 - NPE in CodeStream.invoke()
 *        Andy Clement (GoPivotal, Inc) aclement@gopivotal.com - Contributions for
 *                        Bug 409250 - [1.8][compiler] Various loose ends in 308 code generation
 *******************************************************************************/
package org.eclipse.jdt.internal.compiler.ast;

import java.util.List;

import static org.eclipse.jdt.internal.compiler.ast.ExpressionContext.ASSIGNMENT_CONTEXT;

import org.eclipse.jdt.core.compiler.IProblem;
import org.eclipse.jdt.internal.compiler.ASTVisitor;
import org.eclipse.jdt.internal.compiler.impl.*;
import org.eclipse.jdt.internal.compiler.ast.TypeReference.AnnotationCollector;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
import org.eclipse.jdt.internal.compiler.codegen.*;
import org.eclipse.jdt.internal.compiler.flow.*;
import org.eclipse.jdt.internal.compiler.lookup.*;
import org.eclipse.jdt.internal.compiler.problem.ProblemReporter;
import org.eclipse.jdt.internal.compiler.problem.ProblemSeverities;
import org.eclipse.jdt.internal.compiler.util.Util;

@SuppressWarnings("rawtypes")
public class FieldDeclaration extends AbstractVariableDeclaration {

    public FieldBinding binding;
    public Javadoc javadoc;

    //allows to retrieve both the "type" part of the declaration (part1)
    //and also the part that decribe the name and the init and optionally
    //some other dimension ! ....
    //public int[] a, b[] = X, c ;
    //for b that would give for
    // - part1 : public int[]
    // - part2 : b[] = X,

    public int endPart1Position;
    public int endPart2Position;

    public FieldDeclaration() {
        // for subtypes or conversion
    }

    public FieldDeclaration(char[] name, int sourceStart, int sourceEnd) {
        this.name = name;
        //due to some declaration like
        // int x, y = 3, z , x ;
        //the sourceStart and the sourceEnd is ONLY on  the name
        this.sourceStart = sourceStart;
        this.sourceEnd = sourceEnd;
    }

    public FlowInfo analyseCode(MethodScope initializationScope, FlowContext flowContext, FlowInfo flowInfo) {
        if (this.binding != null && !this.binding.isUsed() && this.binding.isOrEnclosedByPrivateType()) {
            if (!initializationScope.referenceCompilationUnit().compilationResult.hasSyntaxError) {
                initializationScope.problemReporter().unusedPrivateField(this);
            }
        }
        // cannot define static non-constant field inside nested class
        if (this.binding != null && this.binding.isValidBinding() && this.binding.isStatic()
                && this.binding.constant(initializationScope) == Constant.NotAConstant
                && this.binding.declaringClass.isNestedType() && !this.binding.declaringClass.isStatic()) {
            initializationScope.problemReporter()
                    .unexpectedStaticModifierForField((SourceTypeBinding) this.binding.declaringClass, this);
        }

        if (this.initialization != null) {
            flowInfo = this.initialization.analyseCode(initializationScope, flowContext, flowInfo)
                    .unconditionalInits();
            flowInfo.markAsDefinitelyAssigned(this.binding);
        }
        if (this.initialization != null && this.binding != null) {
            CompilerOptions options = initializationScope.compilerOptions();
            if (options.isAnnotationBasedNullAnalysisEnabled) {
                if (this.binding.isNonNull() || options.sourceLevel >= ClassFileConstants.JDK1_8) {
                    int nullStatus = this.initialization.nullStatus(flowInfo, flowContext);
                    NullAnnotationMatching.checkAssignment(initializationScope, flowContext, this.binding, flowInfo,
                            nullStatus, this.initialization, this.initialization.resolvedType);
                }
            }
            this.initialization.checkNPEbyUnboxing(initializationScope, flowContext, flowInfo);
        }
        return flowInfo;
    }

    /**
     * Code generation for a field declaration:
     *      standard assignment to a field
     *
     * @param currentScope org.eclipse.jdt.internal.compiler.lookup.BlockScope
     * @param codeStream org.eclipse.jdt.internal.compiler.codegen.CodeStream
     */
    @Override
    public void generateCode(BlockScope currentScope, CodeStream codeStream) {
        if ((this.bits & IsReachable) == 0) {
            return;
        }
        // do not generate initialization code if final and static (constant is then
        // recorded inside the field itself).
        int pc = codeStream.position;
        boolean isStatic;
        if (this.initialization != null
                && !((isStatic = this.binding.isStatic()) && this.binding.constant() != Constant.NotAConstant)) {
            // non-static field, need receiver
            if (!isStatic)
                codeStream.aload_0();
            // generate initialization value
            this.initialization.generateCode(currentScope, codeStream, true);
            // store into field
            if (isStatic) {
                codeStream.fieldAccess(Opcodes.OPC_putstatic, this.binding, null /* default declaringClass */);
            } else {
                codeStream.fieldAccess(Opcodes.OPC_putfield, this.binding, null /* default declaringClass */);
            }
        }
        codeStream.recordPositionsFrom(pc, this.sourceStart);
    }

    public void getAllAnnotationContexts(int targetType, List allAnnotationContexts) {
        AnnotationCollector collector = new AnnotationCollector(this.type, targetType, allAnnotationContexts);
        for (int i = 0, max = this.annotations.length; i < max; i++) {
            Annotation annotation = this.annotations[i];
            annotation.traverse(collector, (BlockScope) null);
        }
    }

    /**
     * @see org.eclipse.jdt.internal.compiler.ast.AbstractVariableDeclaration#getKind()
     */
    @Override
    public int getKind() {
        return this.type == null ? ENUM_CONSTANT : FIELD;
    }

    public boolean isStatic() {
        if (this.binding != null)
            return this.binding.isStatic();
        return (this.modifiers & ClassFileConstants.AccStatic) != 0;
    }

    public boolean isFinal() {
        if (this.binding != null)
            return this.binding.isFinal();
        return (this.modifiers & ClassFileConstants.AccFinal) != 0;
    }

    @Override
    public StringBuffer printStatement(int indent, StringBuffer output) {
        if (this.javadoc != null) {
            this.javadoc.print(indent, output);
        }
        return super.printStatement(indent, output);
    }

    public void resolve(MethodScope initializationScope) {
        // the two <constant = Constant.NotAConstant> could be regrouped into
        // a single line but it is clearer to have two lines while the reason of their
        // existence is not at all the same. See comment for the second one.

        //--------------------------------------------------------
        if ((this.bits & ASTNode.HasBeenResolved) != 0)
            return;
        if (this.binding == null || !this.binding.isValidBinding())
            return;

        this.bits |= ASTNode.HasBeenResolved;

        // check if field is hiding some variable - issue is that field binding already got inserted in scope
        // thus must lookup separately in super type and outer context
        ClassScope classScope = initializationScope.enclosingClassScope();

        if (classScope != null) {
            checkHiding: {
                SourceTypeBinding declaringType = classScope.enclosingSourceType();
                checkHidingSuperField: {
                    if (declaringType.superclass == null)
                        break checkHidingSuperField;
                    // https://bugs.eclipse.org/bugs/show_bug.cgi?id=318171, find field skipping visibility checks
                    // we do the checks below ourselves, using the appropriate conditions for access check of
                    // protected members from superclasses.
                    FieldBinding existingVariable = classScope.findField(declaringType.superclass, this.name, this,
                            false /*do not resolve hidden field*/, true /* no visibility checks please */);
                    if (existingVariable == null)
                        break checkHidingSuperField; // keep checking outer scenario
                    if (!existingVariable.isValidBinding())
                        break checkHidingSuperField; // keep checking outer scenario
                    if (existingVariable.original() == this.binding)
                        break checkHidingSuperField; // keep checking outer scenario
                    if (!existingVariable.canBeSeenBy(declaringType, this, initializationScope))
                        break checkHidingSuperField; // keep checking outer scenario
                    // collision with supertype field
                    initializationScope.problemReporter().fieldHiding(this, existingVariable);
                    break checkHiding; // already found a matching field
                }
                // only corner case is: lookup of outer field through static declaringType, which isn't detected by #getBinding as lookup starts
                // from outer scope. Subsequent static contexts are detected for free.
                Scope outerScope = classScope.parent;
                if (outerScope.kind == Scope.COMPILATION_UNIT_SCOPE)
                    break checkHiding;
                Binding existingVariable = outerScope.getBinding(this.name, Binding.VARIABLE, this,
                        false /*do not resolve hidden field*/);
                if (existingVariable == null)
                    break checkHiding;
                if (!existingVariable.isValidBinding())
                    break checkHiding;
                if (existingVariable == this.binding)
                    break checkHiding;
                if (existingVariable instanceof FieldBinding) {
                    FieldBinding existingField = (FieldBinding) existingVariable;
                    if (existingField.original() == this.binding)
                        break checkHiding;
                    if (!existingField.isStatic() && declaringType.isStatic())
                        break checkHiding;
                }
                // collision with outer field or local variable
                initializationScope.problemReporter().fieldHiding(this, existingVariable);
            }
        }

        if (this.type != null) { // enum constants have no declared type
            this.type.resolvedType = this.binding.type; // update binding for type reference
        }

        FieldBinding previousField = initializationScope.initializedField;
        int previousFieldID = initializationScope.lastVisibleFieldID;
        try {
            initializationScope.initializedField = this.binding;
            initializationScope.lastVisibleFieldID = this.binding.id;

            resolveAnnotations(initializationScope, this.annotations, this.binding);
            // Check if this declaration should now have the type annotations bit set
            if (this.annotations != null) {
                for (int i = 0, max = this.annotations.length; i < max; i++) {
                    TypeBinding resolvedAnnotationType = this.annotations[i].resolvedType;
                    if (resolvedAnnotationType != null && (resolvedAnnotationType.getAnnotationTagBits()
                            & TagBits.AnnotationForTypeUse) != 0) {
                        this.bits |= ASTNode.HasTypeAnnotations;
                        break;
                    }
                }
            }

            // check @Deprecated annotation presence
            if ((this.binding.getAnnotationTagBits() & TagBits.AnnotationDeprecated) == 0
                    && (this.binding.modifiers & ClassFileConstants.AccDeprecated) != 0
                    && initializationScope.compilerOptions().sourceLevel >= ClassFileConstants.JDK1_5) {
                initializationScope.problemReporter().missingDeprecatedAnnotationForField(this);
            }
            // the resolution of the initialization hasn't been done
            if (this.initialization == null) {
                this.binding.setConstant(Constant.NotAConstant);
            } else {
                // break dead-lock cycles by forcing constant to NotAConstant
                this.binding.setConstant(Constant.NotAConstant);

                TypeBinding fieldType = this.binding.type;
                TypeBinding initializationType;
                this.initialization.setExpressionContext(ASSIGNMENT_CONTEXT);
                this.initialization.setExpectedType(fieldType); // needed in case of generic method invocation
                if (this.initialization instanceof ArrayInitializer) {

                    if ((initializationType = this.initialization.resolveTypeExpecting(initializationScope,
                            fieldType)) != null) {
                        ((ArrayInitializer) this.initialization).binding = (ArrayBinding) initializationType;
                        this.initialization.computeConversion(initializationScope, fieldType, initializationType);
                    }
                } else if ((initializationType = this.initialization.resolveType(initializationScope)) != null) {

                    if (TypeBinding.notEquals(fieldType, initializationType)) // must call before computeConversion() and typeMismatchError()
                        initializationScope.compilationUnitScope().recordTypeConversion(fieldType,
                                initializationType);
                    if (this.initialization.isConstantValueOfTypeAssignableToType(initializationType, fieldType)
                            || initializationType.isCompatibleWith(fieldType, classScope)) {
                        this.initialization.computeConversion(initializationScope, fieldType, initializationType);
                        if (initializationType.needsUncheckedConversion(fieldType)) {
                            initializationScope.problemReporter().unsafeTypeConversion(this.initialization,
                                    initializationType, fieldType);
                        }
                        if (this.initialization instanceof CastExpression
                                && (this.initialization.bits & ASTNode.UnnecessaryCast) == 0) {
                            CastExpression.checkNeedForAssignedCast(initializationScope, fieldType,
                                    (CastExpression) this.initialization);
                        }
                    } else if (isBoxingCompatible(initializationType, fieldType, this.initialization,
                            initializationScope)) {
                        this.initialization.computeConversion(initializationScope, fieldType, initializationType);
                        if (this.initialization instanceof CastExpression
                                && (this.initialization.bits & ASTNode.UnnecessaryCast) == 0) {
                            CastExpression.checkNeedForAssignedCast(initializationScope, fieldType,
                                    (CastExpression) this.initialization);
                        }
                    } else {
                        if (((fieldType.tagBits | initializationType.tagBits) & TagBits.HasMissingType) == 0) {
                            // if problem already got signaled on either type, do not report secondary problem
                            initializationScope.problemReporter().typeMismatchError(initializationType, fieldType,
                                    this.initialization, null);
                        }
                    }
                    if (this.binding.isFinal()) { // cast from constant actual type to variable type
                        this.binding.setConstant(this.initialization.constant
                                .castTo((this.binding.type.id << 4) + this.initialization.constant.typeID()));
                    }
                } else {
                    this.binding.setConstant(Constant.NotAConstant);
                }
                // check for assignment with no effect
                if (this.binding == Expression.getDirectBinding(this.initialization)) {
                    initializationScope.problemReporter().assignmentHasNoEffect(this, this.name);
                }
            }
        } finally {
            initializationScope.initializedField = previousField;
            initializationScope.lastVisibleFieldID = previousFieldID;
            if (this.binding.constant(initializationScope) == null)
                this.binding.setConstant(Constant.NotAConstant);
        }
    }

    public void resolveJavadoc(MethodScope initializationScope) {
        if (this.javadoc != null) {
            FieldBinding previousField = initializationScope.initializedField;
            int previousFieldID = initializationScope.lastVisibleFieldID;
            try {
                initializationScope.initializedField = this.binding;
                if (this.binding != null)
                    initializationScope.lastVisibleFieldID = this.binding.id;
                this.javadoc.resolve(initializationScope);
            } finally {
                initializationScope.initializedField = previousField;
                initializationScope.lastVisibleFieldID = previousFieldID;
            }
        } else if (this.binding != null && this.binding.declaringClass != null
                && !this.binding.declaringClass.isLocalType()) {
            // Set javadoc visibility
            int javadocVisibility = this.binding.modifiers & ExtraCompilerModifiers.AccVisibilityMASK;
            ProblemReporter reporter = initializationScope.problemReporter();
            int severity = reporter.computeSeverity(IProblem.JavadocMissing);
            if (severity != ProblemSeverities.Ignore) {
                ClassScope classScope = initializationScope.enclosingClassScope();
                if (classScope != null) {
                    javadocVisibility = Util.computeOuterMostVisibility(classScope.referenceType(),
                            javadocVisibility);
                }
                int javadocModifiers = (this.binding.modifiers & ~ExtraCompilerModifiers.AccVisibilityMASK)
                        | javadocVisibility;
                reporter.javadocMissing(this.sourceStart, this.sourceEnd, severity, javadocModifiers);
            }
        }
    }

    public void traverse(ASTVisitor visitor, MethodScope scope) {
        if (visitor.visit(this, scope)) {
            if (this.javadoc != null) {
                this.javadoc.traverse(visitor, scope);
            }
            if (this.annotations != null) {
                int annotationsLength = this.annotations.length;
                for (int i = 0; i < annotationsLength; i++)
                    this.annotations[i].traverse(visitor, scope);
            }
            if (this.type != null) {
                this.type.traverse(visitor, scope);
            }
            if (this.initialization != null)
                this.initialization.traverse(visitor, scope);
        }
        visitor.endVisit(this, scope);
    }
}