| /******************************************************************************* |
| * Copyright (c) 2000, 2008 IBM Corporation and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.wst.jsdt.internal.compiler.ast; |
| |
| import org.eclipse.wst.jsdt.core.ast.IASTNode; |
| import org.eclipse.wst.jsdt.core.ast.IExpression; |
| import org.eclipse.wst.jsdt.core.ast.IFieldDeclaration; |
| import org.eclipse.wst.jsdt.internal.compiler.ASTVisitor; |
| import org.eclipse.wst.jsdt.internal.compiler.classfmt.ClassFileConstants; |
| import org.eclipse.wst.jsdt.internal.compiler.flow.FlowContext; |
| import org.eclipse.wst.jsdt.internal.compiler.flow.FlowInfo; |
| import org.eclipse.wst.jsdt.internal.compiler.impl.Constant; |
| import org.eclipse.wst.jsdt.internal.compiler.lookup.ArrayBinding; |
| import org.eclipse.wst.jsdt.internal.compiler.lookup.BaseTypeBinding; |
| import org.eclipse.wst.jsdt.internal.compiler.lookup.Binding; |
| import org.eclipse.wst.jsdt.internal.compiler.lookup.ClassScope; |
| import org.eclipse.wst.jsdt.internal.compiler.lookup.FieldBinding; |
| import org.eclipse.wst.jsdt.internal.compiler.lookup.MethodScope; |
| import org.eclipse.wst.jsdt.internal.compiler.lookup.Scope; |
| import org.eclipse.wst.jsdt.internal.compiler.lookup.SourceTypeBinding; |
| import org.eclipse.wst.jsdt.internal.compiler.lookup.TagBits; |
| import org.eclipse.wst.jsdt.internal.compiler.lookup.TypeBinding; |
| |
| public class FieldDeclaration extends AbstractVariableDeclaration implements IFieldDeclaration { |
| |
| public FieldBinding binding; |
| // is in super 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()) { |
| if (this.binding.isPrivate() || (this.binding.declaringClass != null && this.binding.declaringClass.isLocalType())) { |
| 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() == 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); |
| } |
| return flowInfo; |
| } |
| |
| /** |
| * @see org.eclipse.wst.jsdt.internal.compiler.ast.AbstractVariableDeclaration#getKind() |
| */ |
| public int getKind() { |
| // return this.type == null ? ENUM_CONSTANT : FIELD; |
| return FIELD; |
| } |
| |
| public boolean isStatic() { |
| if (this.binding != null) |
| return this.binding.isStatic(); |
| return (this.modifiers & ClassFileConstants.AccStatic) != 0; |
| } |
| |
| 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; |
| Binding existingVariable = classScope.findField(declaringType.superclass, this.name, this, false /*do not resolve hidden field*/); |
| if (existingVariable == null) break checkHidingSuperField; // keep checking outer scenario |
| if (!existingVariable.isValidBinding()) break checkHidingSuperField; // keep checking outer scenario |
| if (existingVariable instanceof FieldBinding) { |
| FieldBinding existingField = (FieldBinding) existingVariable; |
| if (existingField.original() == this.binding) 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 @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.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 (fieldType != initializationType) // must call before computeConversion() and typeMismatchError() |
| initializationScope.compilationUnitScope().recordTypeConversion(fieldType, initializationType); |
| if (this.initialization.isConstantValueOfTypeAssignableToType(initializationType, fieldType) |
| || (fieldType.isBaseType() && BaseTypeBinding.isWidening(fieldType.id, initializationType.id)) |
| || initializationType.isCompatibleWith(fieldType)) { |
| 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 (initializationScope.isBoxingCompatibleWith(initializationType, fieldType) |
| || (initializationType.isBaseType() // narrowing then boxing ? |
| && initializationScope.compilerOptions().sourceLevel >= ClassFileConstants.JDK1_5 // autoboxing |
| && !fieldType.isBaseType() |
| && initialization.isConstantValueOfTypeAssignableToType(initializationType, initializationScope.environment().computeBoxingType(fieldType)))) { |
| 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 { |
| initializationScope.problemReporter().typeMismatchError(initializationType, fieldType, this); |
| } |
| 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 == Assignment.getDirectBinding(this.initialization)) { |
| initializationScope.problemReporter().assignmentHasNoEffect(this, this.name); |
| } |
| } |
| // Resolve Javadoc comment if one is present |
| if (this.javadoc != null) { |
| /* |
| if (classScope != null) { |
| this.javadoc.resolve(classScope); |
| } |
| */ |
| this.javadoc.resolve(initializationScope); |
| } else if (this.binding.declaringClass != null && !this.binding.declaringClass.isLocalType()) { |
| initializationScope.problemReporter().javadocMissing(this.sourceStart, this.sourceEnd, this.binding.modifiers); |
| } |
| } finally { |
| initializationScope.initializedField = previousField; |
| initializationScope.lastVisibleFieldID = previousFieldID; |
| if (this.binding.constant() == null) |
| this.binding.setConstant(Constant.NotAConstant); |
| } |
| } |
| |
| 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); |
| } |
| public int getASTType() { |
| return IASTNode.FIELD_DECLARATION; |
| |
| } |
| public IExpression getInitialization() |
| { |
| return this.initialization; |
| } |
| } |