blob: 32dee8bcb49a962f1390da479b359aea4dd0b686 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2014 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.IAssignment;
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.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;
private boolean isLocal = true;
public LocalDeclaration(char[] name, int sourceStart, int sourceEnd) {
this.name = name;
this.sourceStart = sourceStart;
this.sourceEnd = sourceEnd;
this.declarationEnd = sourceEnd;
}
public IAssignment getAssignment() {
if (this.initialization == null)
return null;
if (initialization instanceof FunctionExpression) {
return new Assignment(new SingleNameReference(this.name, this.sourceStart, this.sourceEnd), this.initialization, this.initialization.sourceEnd);
}
return null;
}
public LocalVariableBinding getBinding() {
return this.binding;
}
public void setBinding(LocalVariableBinding binding) {
this.binding = binding;
}
public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, FlowInfo flowInfo) {
// Do not analyseCode for the nextLocal local variables in recursion
// because it may cause StackOverflowError
AbstractVariableDeclaration currVarDecl = this;
while (currVarDecl != null) {
if (currVarDecl instanceof LocalDeclaration) {
flowInfo = ((LocalDeclaration)currVarDecl).analyseCodeLocal(currentScope, flowContext, flowInfo);
}
currVarDecl = currVarDecl.nextLocal;
}
return flowInfo;
}
public FlowInfo analyseCodeLocal(BlockScope currentScope, FlowContext flowContext, FlowInfo flowInfo) {
// record variable initialization if any
if ((flowInfo.tagBits & FlowInfo.UNREACHABLE) == 0) {
// only set if actually reached
bits |= ASTNode.IsLocalDeclarationReachable;
}
if (this.initialization != null) {
int nullStatus = this.initialization.nullStatus(flowInfo);
flowInfo = this.initialization.analyseCode(currentScope, flowContext, flowInfo).unconditionalInits();
// for local variable debug attributes
if (!flowInfo.isDefinitelyAssigned(this.binding)) {
this.bits |= FirstAssignmentToLocal;
}
else {
this.bits &= ~FirstAssignmentToLocal; // int i = (i = 0);
}
flowInfo.markAsDefinitelyAssigned(binding);
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
}
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) {
// Do not resolve the nextLocal local variables in recursion
// because it may cause StackOverflowError
AbstractVariableDeclaration currVarDecl = this;
while (currVarDecl != null) {
if (currVarDecl instanceof LocalDeclaration) {
((LocalDeclaration)currVarDecl).resolveLocal(scope);
}
currVarDecl = currVarDecl.nextLocal;
}
}
public void resolveLocal(BlockScope scope) {
// create a binding and add it to the scope
TypeBinding variableType = resolveVarType(scope);
if (type != null) {
variableType = type.resolveType(scope, true /* check bounds */);
} else {
if (inferredType != null) {
variableType = inferredType.resolveType(scope, this);
}
else {
variableType = TypeBinding.UNKNOWN;
}
}
checkModifiers();
Binding varBinding = null;
if (scope.enclosingMethodScope() == null) {
// do not resolve hidden field
varBinding = scope.getBinding(name, Binding.VARIABLE, this, false);
} 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);
}
}
// 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) {
// want to report all possible errors
initialization.resolveType(scope);
}
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;
}
}
else {
this.initialization.setExpectedType(variableType);
TypeBinding initializationType = this.initialization.resolveType(scope);
if (initializationType != null) {
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 {
TypeBinding reconcileAnonymous = initializationType.reconcileAnonymous(this.binding.type);
if (reconcileAnonymous != null) {
this.binding.type = variableType = reconcileAnonymous;
}
}
}
}
// check for assignment with no effect
if (this.binding == Assignment.getDirectBinding(this.initialization)) {
scope.problemReporter().assignmentHasNoEffect(this, this.name);
}
}
}
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) {
// Do not traverse the nextLocal local variables in recursion
// because it may cause StackOverflowError
AbstractVariableDeclaration currVarDecl = this;
while (currVarDecl != null) {
if (currVarDecl instanceof LocalDeclaration) {
((LocalDeclaration)currVarDecl).traverseLocal(visitor, scope);
}
currVarDecl = currVarDecl.nextLocal;
}
}
private void traverseLocal(ASTVisitor visitor, BlockScope scope) {
if (visitor.visit(this, scope)) {
if (type != null) {
type.traverse(visitor, scope);
}
IAssignment assignment = getAssignment();
if (assignment != null) {
((Assignment) assignment).traverse(visitor, scope);
}
else if (initialization != null) {
initialization.traverse(visitor, scope);
}
}
visitor.endVisit(this, 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;
}
/**
* <p>
* Set if declaration is a type on the declaration and the assignment.
* </p>
*
* @see org.eclipse.wst.jsdt.internal.compiler.ast.AbstractVariableDeclaration#setIsType(boolean)
*/
public void setIsType(boolean isType) {
super.setIsType(isType);
if(this.getAssignment() != null) {
this.getAssignment().setIsType(isType);
}
}
/**
* <p>
* Is type if declaration set as type or assignment is set as type.
* </p>
*
* @see org.eclipse.wst.jsdt.internal.compiler.ast.AbstractVariableDeclaration#isType()
*/
public boolean isType() {
return super.isType() || (this.getAssignment() != null && this.getAssignment().isType());
}
public void setIsLocal(boolean isLocal) {
this.isLocal = isLocal;
}
public boolean isLocal() {
return this.isLocal;
}
}