blob: b6ca27ec33d4a342268a18a829ea76833a4a93a2 [file] [log] [blame]
/*******************************************************************************
* 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.ILocalDeclaration;
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.Binding;
import org.eclipse.wst.jsdt.internal.compiler.lookup.BlockScope;
import org.eclipse.wst.jsdt.internal.compiler.lookup.CompilationUnitScope;
import org.eclipse.wst.jsdt.internal.compiler.lookup.ExtraCompilerModifiers;
import org.eclipse.wst.jsdt.internal.compiler.lookup.FunctionTypeBinding;
import org.eclipse.wst.jsdt.internal.compiler.lookup.LocalVariableBinding;
import org.eclipse.wst.jsdt.internal.compiler.lookup.MethodBinding;
import org.eclipse.wst.jsdt.internal.compiler.lookup.MethodScope;
import org.eclipse.wst.jsdt.internal.compiler.lookup.TypeBinding;
import org.eclipse.wst.jsdt.internal.compiler.lookup.VariableBinding;
public class LocalDeclaration extends AbstractVariableDeclaration implements ILocalDeclaration {
public LocalVariableBinding binding;
public LocalDeclaration(
char[] name,
int sourceStart,
int sourceEnd) {
this.name = name;
this.sourceStart = sourceStart;
this.sourceEnd = sourceEnd;
this.declarationEnd = sourceEnd;
}
public LocalVariableBinding getBinding() {
return this.binding;
}
public void setBinding(LocalVariableBinding binding) {
this.binding=binding;
}
public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, FlowInfo flowInfo) {
// record variable initialization if any
if ((flowInfo.tagBits & FlowInfo.UNREACHABLE) == 0) {
bits |= ASTNode.IsLocalDeclarationReachable; // only set if actually reached
}
if (this.initialization != null) {
int nullStatus = this.initialization.nullStatus(flowInfo);
flowInfo =
this.initialization
.analyseCode(currentScope, flowContext, flowInfo)
.unconditionalInits();
if (!flowInfo.isDefinitelyAssigned(this.binding)){// for local variable debug attributes
this.bits |= FirstAssignmentToLocal;
} else {
this.bits &= ~FirstAssignmentToLocal; // int i = (i = 0);
}
flowInfo.markAsDefinitelyAssigned(binding);
if ( true){//(this.binding.type.tagBits & TagBits.IsBaseType) == 0) {
switch(nullStatus) {
case FlowInfo.NULL :
flowInfo.markAsDefinitelyNull(this.binding);
break;
case FlowInfo.NON_NULL :
flowInfo.markAsDefinitelyNonNull(this.binding);
break;
default:
flowInfo.markAsDefinitelyUnknown(this.binding);
}
// no need to inform enclosing try block since its locals won't get
// known by the finally block
}
}
if (this.nextLocal!=null)
flowInfo=this.nextLocal.analyseCode(currentScope, flowContext, flowInfo);
return flowInfo;
}
public void checkModifiers() {
//only potential valid modifier is <<final>>
if (((modifiers & ExtraCompilerModifiers.AccJustFlag) & ~ClassFileConstants.AccFinal) != 0)
//AccModifierProblem -> other (non-visibility problem)
//AccAlternateModifierProblem -> duplicate modifier
//AccModifierProblem | AccAlternateModifierProblem -> visibility problem"
modifiers = (modifiers & ~ExtraCompilerModifiers.AccAlternateModifierProblem) | ExtraCompilerModifiers.AccModifierProblem;
}
/**
* @see org.eclipse.wst.jsdt.internal.compiler.ast.AbstractVariableDeclaration#getKind()
*/
public int getKind() {
return LOCAL_VARIABLE;
}
public TypeBinding resolveVarType(BlockScope scope)
{
TypeBinding variableType = null;
if (type!=null)
variableType=type.resolveType(scope, true /* check bounds*/);
else {
if (inferredType!=null)
variableType=inferredType.resolveType(scope,this);
else
variableType=TypeBinding.UNKNOWN;
}
checkModifiers();
return variableType;
}
public void resolve(BlockScope scope) {
// create a binding and add it to the scope
TypeBinding variableType = resolveVarType(scope);
if (this.nextLocal!=null)
this.nextLocal.resolve(scope);
if (variableType != null) {
if (variableType == TypeBinding.VOID) {
scope.problemReporter().variableTypeCannotBeVoid(this);
return ;
}
if (variableType.isArrayType() && ((ArrayBinding) variableType).leafComponentType == TypeBinding.VOID) {
scope.problemReporter().variableTypeCannotBeVoidArray(this);
return;
}
}
if (type!=null)
variableType=type.resolveType(scope, true /* check bounds*/);
else {
if (inferredType!=null)
{
variableType=inferredType.resolveType(scope,this);
}
else
variableType=TypeBinding.UNKNOWN;
}
checkModifiers();
if (variableType != null) {
if (variableType == TypeBinding.VOID) {
scope.problemReporter().variableTypeCannotBeVoid(this);
return;
}
if (variableType.isArrayType() && ((ArrayBinding) variableType).leafComponentType == TypeBinding.VOID) {
scope.problemReporter().variableTypeCannotBeVoidArray(this);
return;
}
}
Binding varBinding = null;
if (scope.enclosingMethodScope()==null)
varBinding=scope.getBinding(name, Binding.VARIABLE, this, false /*do not resolve hidden field*/);
else
varBinding=scope.getLocalBinding(name, Binding.VARIABLE, this, false);
boolean alreadyDefined=false;
if (varBinding != null && varBinding.isValidBinding()){
VariableBinding existingVariable=(VariableBinding)varBinding;
if (existingVariable.isFor(this))
{
if (variableType!=null)
existingVariable.type=variableType;
alreadyDefined=true;
}
else
{
if (existingVariable instanceof LocalVariableBinding && this.hiddenVariableDepth == 0) {
LocalVariableBinding localVariableBinding=(LocalVariableBinding)existingVariable;
if (localVariableBinding.declaringScope instanceof CompilationUnitScope && scope.enclosingMethodScope()!=null)
scope.problemReporter().localVariableHiding(this, existingVariable, false);
else
scope.problemReporter().redefineLocal(this);
} else {
scope.problemReporter().localVariableHiding(this, existingVariable, false);
}
}
}
if ((modifiers & ClassFileConstants.AccFinal)!= 0 && this.initialization == null) {
modifiers |= ExtraCompilerModifiers.AccBlankFinal;
}
if (!(this.binding!=null && alreadyDefined))
{
this.binding = new LocalVariableBinding(this, variableType, modifiers, false);
MethodScope methodScope = scope.enclosingMethodScope();
if (methodScope!=null)
methodScope.addLocalVariable(binding);
else
scope.compilationUnitScope().addLocalVariable(binding);
}
this.binding.setConstant(Constant.NotAConstant);
// allow to recursivelly target the binding....
// the correct constant is harmed if correctly computed at the end of this method
if (variableType == null) {
if (initialization != null)
initialization.resolveType(scope); // want to report all possible errors
return;
}
// store the constant for final locals
if (initialization != null) {
if (initialization instanceof ArrayInitializer) {
TypeBinding initializationType = initialization.resolveTypeExpecting(scope, variableType);
if (initializationType != null) {
((ArrayInitializer) initialization).binding = (ArrayBinding) initializationType;
initialization.computeConversion(scope, variableType, initializationType);
}
} else {
this.initialization.setExpectedType(variableType);
TypeBinding initializationType = this.initialization.resolveType(scope);
if (initializationType != null) {
// if (variableType != initializationType) // must call before computeConversion() and typeMismatchError()
// scope.compilationUnitScope().recordTypeConversion(variableType, initializationType);
if (initializationType.isFunctionType())
{
MethodBinding existingMethod = scope.findMethod(this.name, null,false);
if (existingMethod!=null)
{
MethodBinding functionBinding = ((FunctionTypeBinding)initializationType).functionBinding;
existingMethod.updateFrom(functionBinding);
}
}
if (variableType==TypeBinding.UNKNOWN && initializationType!=TypeBinding.NULL)
this.binding.type=initializationType;
else if (initialization.isConstantValueOfTypeAssignableToType(initializationType, variableType)
|| variableType.isBaseType() /* && BaseTypeBinding.isWidening(variableType.id, initializationType.id)) */
|| initializationType.isCompatibleWith(variableType)) {
// this.initialization.computeConversion(scope, variableType, initializationType);
// if (initializationType.needsUncheckedConversion(variableType)) {
// scope.problemReporter().unsafeTypeConversion(this.initialization, initializationType, variableType);
// }
// if (this.initialization instanceof CastExpression
// && (this.initialization.bits & ASTNode.UnnecessaryCast) == 0) {
// CastExpression.checkNeedForAssignedCast(scope, variableType, (CastExpression) this.initialization);
// }
// } else if (scope.isBoxingCompatibleWith(initializationType, variableType)
// || (initializationType.isBaseType() // narrowing then boxing ?
// && scope.compilerOptions().sourceLevel >= ClassFileConstants.JDK1_5 // autoboxing
// && !variableType.isBaseType()
// && initialization.isConstantValueOfTypeAssignableToType(initializationType, scope.environment().computeBoxingType(variableType)))) {
// this.initialization.computeConversion(scope, variableType, initializationType);
// if (this.initialization instanceof CastExpression
// && (this.initialization.bits & ASTNode.UnnecessaryCast) == 0) {
// CastExpression.checkNeedForAssignedCast(scope, variableType, (CastExpression) this.initialization);
// }
} else {
scope.problemReporter().typeMismatchError(initializationType, variableType, this.initialization);
}
}
}
// check for assignment with no effect
if (this.binding == Assignment.getDirectBinding(this.initialization)) {
scope.problemReporter().assignmentHasNoEffect(this, this.name);
}
// change the constant in the binding when it is final
// (the optimization of the constant propagation will be done later on)
// cast from constant actual type to variable type
binding.setConstant(
binding.isFinal()
? initialization.constant.castTo((variableType.id << 4) + initialization.constant.typeID())
: Constant.NotAConstant);
}
// Resolve Javadoc comment if one is present
if (this.javadoc != null) {
/*
if (classScope != null) {
this.javadoc.resolve(classScope);
}
*/
if (scope.enclosingMethodScope()!=null)
this.javadoc.resolve(scope.enclosingMethodScope());
else
this.javadoc.resolve(scope.compilationUnitScope());
}
// only resolve annotation at the end, for constant to be positionned before (96991)
// if (JavaScriptCore.IS_ECMASCRIPT4)
// resolveAnnotations(scope, this.annotations, this.binding);
}
public StringBuffer printStatement(int indent, StringBuffer output) {
if (this.javadoc != null) {
this.javadoc.print(indent, output);
}
return super.printStatement(indent, output);
}
public void traverse(ASTVisitor visitor, BlockScope scope) {
if (visitor.visit(this, scope)) {
if (this.annotations != null) {
int annotationsLength = this.annotations.length;
for (int i = 0; i < annotationsLength; i++)
this.annotations[i].traverse(visitor, scope);
}
if (type!=null)
type.traverse(visitor, scope);
if (initialization != null)
initialization.traverse(visitor, scope);
}
visitor.endVisit(this, scope);
if (this.nextLocal!=null)
this.nextLocal.traverse(visitor, scope);
}
public String getTypeName()
{
if (type!=null)
return type.toString();
if (inferredType!=null)
return new String(inferredType.getName());
return null;
}
public int getASTType() {
return IASTNode.LOCAL_DECLARATION;
}
}