[
Date Prev][
Date Next][
Thread Prev][
Thread Next][
Date Index][
Thread Index]
[
List Home]
[pdt-dev] PDT : Questions about type inference evaluators
|
Hello Michal how are you ? I've seen that you recently worked on some patch to correct problems with type inference evaluators (Bug 453737 - Tag @var doesn't work well with array types). I ran FindBugs some time ago and found some bugs related to the usage of String#split(). Maybe they are still some pending bugs concerning type inference evaluators, but I do not feel comfortable enough about these parts of code, so maybe you could help ;)
What I find strange is the way that some evaluators (from packages org.eclipse.php.internal.core.typeinference.evaluators.*) are splitting types using String#split("\\|") and handling brackets (for array type declarations). So my (stupid) questions are : - why do some evaluators look after brackets (MethodReturnTypeEvaluator, PHPDocMethodReturnTypeEvaluator, PHPDocClassVariableEvaluator) and others not (ClassVariableDeclarationEvaluator, VariableReferenceEvaluator) ? - why aren't splitted values (using String#split("\\|")) tested if they are empty or not ? - why are PHPDocClassVariableEvaluator#getArrayType(String type, IType currentNamespace, int offset) and PHPDocMethodReturnTypeEvaluator#getArrayType(String type, IType currentNamespace, int offset) different ? - so getEvaluatedType(String typeName, IType currentNamespace) and getArrayType(String type, IType currentNamespace, int offset) from classes PHPDocClassVariableEvaluator and PHPDocMethodReturnTypeEvaluator can probably be merged (into a class utility?). I made some (untested) changes and provide them "as is" as attachments, so you can see what I mean. I also didn't put them on gerrit because I don't plan to make a patch ;) Thierry.
|
/*******************************************************************************
* Copyright (c) 2009 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
* Zend Technologies
*******************************************************************************/
package org.eclipse.php.internal.core.typeinference.evaluators;
import java.util.*;
import java.util.Map.Entry;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.dltk.ast.ASTNode;
import org.eclipse.dltk.ast.declarations.ModuleDeclaration;
import org.eclipse.dltk.ast.declarations.TypeDeclaration;
import org.eclipse.dltk.ast.expressions.Expression;
import org.eclipse.dltk.ast.references.SimpleReference;
import org.eclipse.dltk.ast.references.TypeReference;
import org.eclipse.dltk.ast.references.VariableReference;
import org.eclipse.dltk.ast.statements.Statement;
import org.eclipse.dltk.core.*;
import org.eclipse.dltk.internal.core.SourceRefElement;
import org.eclipse.dltk.ti.GoalState;
import org.eclipse.dltk.ti.IContext;
import org.eclipse.dltk.ti.goals.ExpressionTypeGoal;
import org.eclipse.dltk.ti.goals.IGoal;
import org.eclipse.dltk.ti.types.IEvaluatedType;
import org.eclipse.php.internal.core.PHPVersion;
import org.eclipse.php.internal.core.compiler.ast.nodes.*;
import org.eclipse.php.internal.core.project.ProjectOptions;
import org.eclipse.php.internal.core.typeinference.*;
import org.eclipse.php.internal.core.typeinference.context.ContextFinder;
import org.eclipse.php.internal.core.typeinference.context.IModelCacheContext;
import org.eclipse.php.internal.core.typeinference.context.MethodContext;
import org.eclipse.php.internal.core.typeinference.context.TypeContext;
import org.eclipse.php.internal.core.typeinference.goals.ClassVariableDeclarationGoal;
/**
* This evaluator finds class field declaration either using : 1. @var hint 2.
* in method body using field access. 3. magic declaration using the @property,
*
* @property-read, @property-write
*/
public class ClassVariableDeclarationEvaluator extends AbstractPHPGoalEvaluator {
private List<IEvaluatedType> evaluated = new LinkedList<IEvaluatedType>();
public ClassVariableDeclarationEvaluator(IGoal goal) {
super(goal);
}
public IGoal[] init() {
ClassVariableDeclarationGoal typedGoal = (ClassVariableDeclarationGoal) goal;
IType[] types = typedGoal.getTypes();
if (types == null) {
TypeContext context = (TypeContext) typedGoal.getContext();
types = PHPTypeInferenceUtils.getModelElements(
context.getInstanceType(), context);
}
if (types == null) {
return null;
}
IContext context = typedGoal.getContext();
IModelAccessCache cache = null;
if (context instanceof IModelCacheContext) {
cache = ((IModelCacheContext) context).getCache();
}
String variableName = typedGoal.getVariableName();
final List<IGoal> subGoals = new LinkedList<IGoal>();
for (final IType type : types) {
try {
ITypeHierarchy hierarchy = null;
if (cache != null) {
hierarchy = cache.getSuperTypeHierarchy(type, null);
}
IField[] fields = PHPModelUtils.getTypeHierarchyField(type,
hierarchy, variableName, true, null);
Map<IType, IType> fieldDeclaringTypeSet = new HashMap<IType, IType>();
for (IField field : fields) {
IType declaringType = field.getDeclaringType();
if (declaringType != null) {
fieldDeclaringTypeSet.put(declaringType, type);
ISourceModule sourceModule = declaringType
.getSourceModule();
ModuleDeclaration moduleDeclaration = SourceParserUtil
.getModuleDeclaration(sourceModule);
TypeDeclaration typeDeclaration = PHPModelUtils
.getNodeByClass(moduleDeclaration,
declaringType);
if (typeDeclaration != null
&& field instanceof SourceRefElement) {
SourceRefElement sourceRefElement = (SourceRefElement) field;
ISourceRange sourceRange = sourceRefElement
.getSourceRange();
ClassDeclarationSearcher searcher = new ClassDeclarationSearcher(
sourceModule, typeDeclaration,
sourceRange.getOffset(),
sourceRange.getLength(), null, type,
declaringType);
try {
moduleDeclaration.traverse(searcher);
if (searcher.getResult() != null) {
subGoals.add(new ExpressionTypeGoal(
searcher.getContext(), searcher
.getResult()));
}
} catch (Exception e) {
if (DLTKCore.DEBUG) {
e.printStackTrace();
}
}
}
}
}
if (subGoals.size() == 0) {
getGoalFromStaticDeclaration(variableName, subGoals, type,
null);
}
fieldDeclaringTypeSet.remove(type);
if (subGoals.size() == 0 && !fieldDeclaringTypeSet.isEmpty()) {
for (Entry<IType, IType> entry : fieldDeclaringTypeSet
.entrySet()) {
getGoalFromStaticDeclaration(variableName, subGoals,
entry.getKey(), entry.getValue());
}
}
} catch (CoreException e) {
if (DLTKCore.DEBUG) {
e.printStackTrace();
}
}
}
resolveMagicClassVariableDeclaration(types, variableName, cache);
return subGoals.toArray(new IGoal[subGoals.size()]);
}
protected void getGoalFromStaticDeclaration(String variableName,
final List<IGoal> subGoals, final IType declaringType,
IType realType) throws ModelException {
ISourceModule sourceModule = declaringType.getSourceModule();
ModuleDeclaration moduleDeclaration = SourceParserUtil
.getModuleDeclaration(sourceModule);
TypeDeclaration typeDeclaration = PHPModelUtils.getNodeByClass(
moduleDeclaration, declaringType);
// try to search declarations of type "self::$var =" or
// "$this->var ="
ClassDeclarationSearcher searcher;
if (realType != null) {
searcher = new ClassDeclarationSearcher(sourceModule,
typeDeclaration, 0, 0, variableName, realType,
declaringType);
} else {
searcher = new ClassDeclarationSearcher(sourceModule,
typeDeclaration, 0, 0, variableName);
}
try {
moduleDeclaration.traverse(searcher);
for (Entry<ASTNode, IContext> entry : searcher
.getStaticDeclarations().entrySet()) {
final IContext context = entry.getValue();
if (context instanceof MethodContext) {
MethodContext methodContext = (MethodContext) context;
methodContext.setCurrentType(realType);
}
if (context instanceof IModelCacheContext
&& ClassVariableDeclarationEvaluator.this.goal
.getContext() instanceof IModelCacheContext) {
((IModelCacheContext) context)
.setCache(((IModelCacheContext) ClassVariableDeclarationEvaluator.this.goal
.getContext()).getCache());
}
subGoals.add(new ExpressionTypeGoal(context, entry.getKey()));
}
} catch (Exception e) {
if (DLTKCore.DEBUG) {
e.printStackTrace();
}
}
}
/**
* Search for magic variables using the @property tag
*
* @param types
* @param variableName
* @param cache
*/
private void resolveMagicClassVariableDeclaration(IType[] types,
String variableName, IModelAccessCache cache) {
for (IType type : types) {
resolveMagicClassVariableDeclaration(variableName, type, cache);
try {
if (evaluated.isEmpty() && type.getSuperClasses() != null
&& type.getSuperClasses().length > 0) {
ITypeHierarchy hierarchy = null;
if (cache != null) {
hierarchy = cache.getSuperTypeHierarchy(type, null);
}
IType[] superClasses = PHPModelUtils.getSuperClasses(type,
hierarchy);
for (int i = 0; i < superClasses.length
/* && evaluated.isEmpty() */; i++) {
IType superClass = superClasses[i];
resolveMagicClassVariableDeclaration(variableName,
superClass, cache);
}
}
} catch (ModelException e) {
e.printStackTrace();
}
}
}
protected void resolveMagicClassVariableDeclaration(String variableName,
IType type, IModelAccessCache cache) {
final PHPDocBlock docBlock = PHPModelUtils.getDocBlock(type);
if (docBlock == null) {
return;
}
for (PHPDocTag tag : docBlock.getTags()) {
final int tagKind = tag.getTagKind();
if (tagKind == PHPDocTag.PROPERTY
|| tagKind == PHPDocTag.PROPERTY_READ
|| tagKind == PHPDocTag.PROPERTY_WRITE) {
final String[] typeNames = getTypeBinding(variableName, tag);
if (typeNames != null) {
for (String typeName : typeNames) {
if (typeName.trim().length() == 0) {
continue;
}
IEvaluatedType resolved = PHPSimpleTypes
.fromString(typeName);
if (resolved == null) {
resolved = new PHPClassType(typeName);
}
evaluated.add(resolved);
}
}
}
}
}
/**
* Resolves the type from the @property tag
*
* @param variableName
* @param docTag
* @return the types of the given variable
*/
private String[] getTypeBinding(String variableName, PHPDocTag docTag) {
final String[] split = docTag.getValue().trim().split("\\s+"); //$NON-NLS-1$
if (split.length < 2) {
return null;
}
if (split[1].equals(variableName)) {
return split[0].split("\\|");//$NON-NLS-1$
}
return null;
}
public Object produceResult() {
return PHPTypeInferenceUtils.combineTypes(evaluated);
}
public IGoal[] subGoalDone(IGoal subgoal, Object result, GoalState state) {
if (state != GoalState.RECURSIVE && result != null) {
evaluated.add((IEvaluatedType) result);
}
return IGoal.NO_GOALS;
}
/**
* Searches for all class variable declarations using offset and length
* which is hold by model element
*
* @author michael
*/
class ClassDeclarationSearcher extends ContextFinder {
private static final String NULL = "null"; //$NON-NLS-1$
private TypeDeclaration typeDeclaration;
private ASTNode result;
private IContext context;
private int offset;
private int length;
private String variableName;
private ISourceModule sourceModule;
private Map<ASTNode, IContext> staticDeclarations;
public ClassDeclarationSearcher(ISourceModule sourceModule,
TypeDeclaration typeDeclaration, int offset, int length,
String variableName) {
super(sourceModule);
this.typeDeclaration = typeDeclaration;
this.offset = offset;
this.length = length;
this.sourceModule = sourceModule;
this.variableName = variableName;
this.staticDeclarations = new HashMap<ASTNode, IContext>();
}
public ClassDeclarationSearcher(ISourceModule sourceModule,
TypeDeclaration typeDeclaration, int offset, int length,
String variableName, IType realType, IType declaringType) {
// this(sourceModule, typeDeclaration2, offset2, length2,
// variableName);
super(sourceModule, realType, declaringType);
this.typeDeclaration = typeDeclaration;
this.offset = offset;
this.length = length;
this.sourceModule = sourceModule;
this.variableName = variableName;
this.staticDeclarations = new HashMap<ASTNode, IContext>();
}
public ASTNode getResult() {
return result;
}
public Map<ASTNode, IContext> getStaticDeclarations() {
return staticDeclarations;
}
public IContext getContext() {
if (context instanceof IModelCacheContext
&& ClassVariableDeclarationEvaluator.this.goal.getContext() instanceof IModelCacheContext) {
((IModelCacheContext) context)
.setCache(((IModelCacheContext) ClassVariableDeclarationEvaluator.this.goal
.getContext()).getCache());
}
return context;
}
public boolean visit(Statement e) throws Exception {
if (typeDeclaration.sourceStart() < e.sourceStart()
&& typeDeclaration.sourceEnd() > e.sourceEnd()) {
if (e instanceof PHPFieldDeclaration) {
PHPFieldDeclaration phpFieldDecl = (PHPFieldDeclaration) e;
if (phpFieldDecl.getDeclarationStart() == offset
&& phpFieldDecl.sourceEnd()
- phpFieldDecl.getDeclarationStart() == length) {
result = ((PHPFieldDeclaration) e).getVariableValue();
if (result instanceof Scalar) {
Scalar scalar = (Scalar) result;
if (scalar.getScalarType() == Scalar.TYPE_STRING
&& scalar.getValue().toLowerCase()
.equals(NULL)) {
result = null;
}
}
context = contextStack.peek();
}
}
}
return visitGeneral(e);
}
public boolean visit(Expression e) throws Exception {
if (typeDeclaration.sourceStart() < e.sourceStart()
&& typeDeclaration.sourceEnd() > e.sourceEnd()) {
if (e instanceof Assignment) {
if (e.sourceStart() == offset
&& e.sourceEnd() - e.sourceStart() == length) {
result = ((Assignment) e).getValue();
context = contextStack.peek();
} else if (variableName != null) {
Assignment assignment = (Assignment) e;
Expression left = assignment.getVariable();
Expression right = assignment.getValue();
if (left instanceof StaticFieldAccess) {
StaticFieldAccess fieldAccess = (StaticFieldAccess) left;
Expression dispatcher = fieldAccess.getDispatcher();
if (isSelf(dispatcher)) {
Expression field = fieldAccess.getField();
if (field instanceof VariableReference
&& variableName
.equals(((VariableReference) field)
.getName())) {
staticDeclarations.put(right,
contextStack.peek());
}
}
} else if (left instanceof FieldAccess) {
FieldAccess fieldAccess = (FieldAccess) left;
Expression dispatcher = fieldAccess.getDispatcher();
if (dispatcher instanceof VariableReference
&& "$this".equals(((VariableReference) dispatcher).getName())) { //$NON-NLS-1$
Expression field = fieldAccess.getField();
if (field instanceof SimpleReference
&& variableName
.equals('$' + ((SimpleReference) field)
.getName())) {
staticDeclarations.put(right,
contextStack.peek());
}
}
}
}
}
}
return visitGeneral(e);
}
public boolean visitGeneral(ASTNode e) throws Exception {
return e.sourceStart() <= offset || variableName != null;
}
private boolean isSelf(Expression dispatcher) {
if (!(dispatcher instanceof TypeReference)) {
return false;
}
if ("self".equals(((TypeReference) dispatcher).getName())) { //$NON-NLS-1$
return true;
} else if (PHPVersion.PHP5_4.isLessThan(ProjectOptions
.getPhpVersion(sourceModule))
&& "self".equals(((TypeReference) dispatcher).getName() //$NON-NLS-1$
.toLowerCase())) {
return true;
}
return false;
}
}
}
/*******************************************************************************
* Copyright (c) 2009 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
* Zend Technologies
*******************************************************************************/
package org.eclipse.php.internal.core.typeinference.evaluators;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.dltk.ast.references.SimpleReference;
import org.eclipse.dltk.core.IMethod;
import org.eclipse.dltk.core.IModelElement;
import org.eclipse.dltk.evaluation.types.MultiTypeType;
import org.eclipse.dltk.ti.GoalState;
import org.eclipse.dltk.ti.IContext;
import org.eclipse.dltk.ti.goals.ExpressionTypeGoal;
import org.eclipse.dltk.ti.goals.GoalEvaluator;
import org.eclipse.dltk.ti.goals.IGoal;
import org.eclipse.dltk.ti.types.IEvaluatedType;
import org.eclipse.php.internal.core.compiler.ast.nodes.*;
import org.eclipse.php.internal.core.compiler.ast.parser.php56.CompilerParserConstants;
import org.eclipse.php.internal.core.compiler.ast.parser.php56.PhpTokenNames;
import org.eclipse.php.internal.core.typeinference.PHPClassType;
import org.eclipse.php.internal.core.typeinference.PHPModelUtils;
import org.eclipse.php.internal.core.typeinference.PHPSimpleTypes;
import org.eclipse.php.internal.core.typeinference.context.MethodContext;
import org.eclipse.php.internal.core.typeinference.evaluators.phpdoc.PHPDocClassVariableEvaluator;
public class FormalParameterEvaluator extends GoalEvaluator {
private static final String ELLIPSIS = PhpTokenNames
.getName(CompilerParserConstants.T_ELLIPSIS);
private IEvaluatedType result;
public FormalParameterEvaluator(IGoal goal) {
super(goal);
}
public IGoal[] init() {
ExpressionTypeGoal typedGoal = (ExpressionTypeGoal) goal;
FormalParameter parameter = (FormalParameter) typedGoal.getExpression();
SimpleReference type = parameter.getParameterType();
if (type != null && "array".equals(type.getName()) == false) { //$NON-NLS-1$
result = PHPClassType.fromSimpleReference(type);
} else {
IContext context = typedGoal.getContext();
if (context instanceof MethodContext) {
MethodContext methodContext = (MethodContext) context;
PHPMethodDeclaration methodDeclaration = (PHPMethodDeclaration) methodContext
.getMethodNode();
PHPDocBlock[] docBlocks = new PHPDocBlock[0];
try {
IModelElement element = methodContext.getSourceModule()
.getElementAt(methodDeclaration.getNameStart());
if (element instanceof IMethod) {
IMethod method = (IMethod) element;
if (method.getDeclaringType() != null) {
docBlocks = PHPModelUtils
.getTypeHierarchyMethodDoc(
method.getDeclaringType(),
methodContext.getCache() != null ? methodContext
.getCache()
.getSuperTypeHierarchy(
method.getDeclaringType(),
null)
: null, method
.getElementName(), true,
null);
} else {
docBlocks = new PHPDocBlock[] { methodDeclaration
.getPHPDoc() };
}
} else {
docBlocks = new PHPDocBlock[] { methodDeclaration
.getPHPDoc() };
}
} catch (CoreException e) {
}
for (PHPDocBlock docBlock : docBlocks) {
if (result != null) {
break;
}
if (docBlock != null) {
for (PHPDocTag tag : docBlock.getTags()) {
if (tag.getTagKind() == PHPDocTag.PARAM) {
SimpleReference[] references = tag
.getReferences();
if (references.length == 2) {
String parameterName = parameter.getName();
if (parameter.isVariadic()) {
parameterName = ELLIPSIS
+ parameterName;
}
if (references[0].getName().equals(
parameterName)) {
// result = PHPClassType
// .fromSimpleReference(PHPModelUtils.getFullName(references[1].getName(),
// methodContext.getSourceModule(),
// references[1].sourceStart()));
// fix unit test testDoctag7.pdtt
String typeName = references[1]
.getName();
if (typeName.indexOf('|') >= 0) {
String[] typeNames = typeName
.split("\\|"); //$NON-NLS-1$
MultiTypeType arrayType = new MultiTypeType();
for (int i = 0; i < typeNames.length; i++) {
if (typeNames[i]
.endsWith(PHPDocClassVariableEvaluator.BRACKETS)) {
typeNames[i] = typeNames[i]
.substring(
0,
typeNames[i]
.length() - 2);
}
if (typeNames[i].trim()
.length() == 0) { //$NON-NLS-1$
continue;
}
arrayType
.addType(PHPClassType
.fromTypeName(
typeNames[i],
methodContext
.getSourceModule(),
references[1]
.sourceStart()));
}
result = arrayType;
} else if (typeName
.endsWith(PHPDocClassVariableEvaluator.BRACKETS)) {
typeName = typeName.substring(0,
typeName.length() - 2);
}
if (typeName.trim().length() == 0) { //$NON-NLS-1$
continue;
}
result = PHPClassType
.fromTypeName(
typeName,
methodContext
.getSourceModule(),
references[1]
.sourceStart());
}
}
}
}
}
}
if (result == null
&& parameter.getInitialization() instanceof Scalar) {
Scalar scalar = (Scalar) parameter.getInitialization();
result = PHPSimpleTypes.fromString(scalar.getType());
if (result == null) {
result = new PHPClassType(scalar.getType());
}
}
}
}
return IGoal.NO_GOALS;
}
public Object produceResult() {
return result;
}
public IGoal[] subGoalDone(IGoal subgoal, Object result, GoalState state) {
return IGoal.NO_GOALS;
}
}
/*******************************************************************************
* Copyright (c) 2009 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
* Zend Technologies
*******************************************************************************/
package org.eclipse.php.internal.core.typeinference.evaluators;
import java.util.LinkedList;
import java.util.List;
import java.util.regex.Matcher;
import org.eclipse.dltk.ast.ASTNode;
import org.eclipse.dltk.ast.ASTVisitor;
import org.eclipse.dltk.ast.declarations.MethodDeclaration;
import org.eclipse.dltk.ast.declarations.ModuleDeclaration;
import org.eclipse.dltk.ast.expressions.Expression;
import org.eclipse.dltk.core.*;
import org.eclipse.dltk.ti.GoalState;
import org.eclipse.dltk.ti.IContext;
import org.eclipse.dltk.ti.goals.ExpressionTypeGoal;
import org.eclipse.dltk.ti.goals.IGoal;
import org.eclipse.dltk.ti.types.IEvaluatedType;
import org.eclipse.php.internal.core.compiler.ast.nodes.PHPDocBlock;
import org.eclipse.php.internal.core.compiler.ast.nodes.PHPDocTag;
import org.eclipse.php.internal.core.compiler.ast.nodes.ReturnStatement;
import org.eclipse.php.internal.core.compiler.ast.nodes.YieldExpression;
import org.eclipse.php.internal.core.compiler.ast.parser.ASTUtils;
import org.eclipse.php.internal.core.typeinference.*;
import org.eclipse.php.internal.core.typeinference.context.IModelCacheContext;
import org.eclipse.php.internal.core.typeinference.context.MethodContext;
import org.eclipse.php.internal.core.typeinference.evaluators.phpdoc.PHPDocClassVariableEvaluator;
import org.eclipse.php.internal.core.typeinference.goals.MethodElementReturnTypeGoal;
public class MethodReturnTypeEvaluator extends
AbstractMethodReturnTypeEvaluator {
private final List<IEvaluatedType> evaluated = new LinkedList<IEvaluatedType>();
private final List<IEvaluatedType> yieldEvaluated = new LinkedList<IEvaluatedType>();
private final List<IGoal> yieldGoals = new LinkedList<IGoal>();
public MethodReturnTypeEvaluator(IGoal goal) {
super(goal);
}
public IGoal[] init() {
MethodElementReturnTypeGoal goal = (MethodElementReturnTypeGoal) getGoal();
String methodName = goal.getMethodName();
final List<IGoal> subGoals = new LinkedList<IGoal>();
MethodsAndTypes mat = getMethodsAndTypes();
for (int i = 0; i < mat.methods.length; i++) {
IMethod method = mat.methods[i];
ISourceModule sourceModule = method.getSourceModule();
ModuleDeclaration module = SourceParserUtil
.getModuleDeclaration(sourceModule);
MethodDeclaration decl = null;
try {
decl = PHPModelUtils.getNodeByMethod(module, method);
} catch (ModelException e) {
if (DLTKCore.DEBUG) {
e.printStackTrace();
}
}
// final boolean found[] = new boolean[1];
if (decl != null) {
final IContext innerContext = ASTUtils.findContext(
sourceModule, module, decl);
if (innerContext instanceof MethodContext) {
MethodContext mc = (MethodContext) innerContext;
mc.setCurrentType(mat.types[i]);
}
if (goal.getContext() instanceof IModelCacheContext
&& innerContext instanceof IModelCacheContext) {
((IModelCacheContext) innerContext)
.setCache(((IModelCacheContext) goal.getContext())
.getCache());
}
ASTVisitor visitor = new ASTVisitor() {
public boolean visitGeneral(ASTNode node) throws Exception {
if (node instanceof ReturnStatement) {
ReturnStatement statement = (ReturnStatement) node;
Expression expr = statement.getExpr();
if (expr == null) {
evaluated.add(PHPSimpleTypes.VOID);
} else {
subGoals.add(new ExpressionTypeGoal(
innerContext, expr));
}
} else if (node instanceof YieldExpression) {
YieldExpression statement = (YieldExpression) node;
Expression expr = statement.getExpr();
if (expr == null) {
yieldEvaluated.add(PHPSimpleTypes.NULL);
} else {
final ExpressionTypeGoal yg = new ExpressionTypeGoal(
innerContext, expr);
subGoals.add(yg);
yieldGoals.add(yg);
}
}
return super.visitGeneral(node);
}
};
try {
decl.traverse(visitor);
} catch (Exception e) {
if (DLTKCore.DEBUG) {
e.printStackTrace();
}
}
}
if (method != null) {
resolveMagicMethodDeclaration(method, methodName);
}
}
return subGoals.toArray(new IGoal[subGoals.size()]);
}
/**
* Resolve magic methods defined by the @method tag
*/
private void resolveMagicMethodDeclaration(IMethod method, String methodName) {
final IModelElement parent = method.getParent();
if (parent.getElementType() != IModelElement.TYPE) {
return;
}
IType type = (IType) parent;
final PHPDocBlock docBlock = PHPModelUtils.getDocBlock(type);
if (docBlock == null) {
return;
}
IType currentNamespace = PHPModelUtils.getCurrentNamespace(type);
for (PHPDocTag tag : docBlock.getTags()) {
final int tagKind = tag.getTagKind();
if (tagKind == PHPDocTag.METHOD) {
final String[] typeNames = getTypeBinding(methodName, tag);
if (typeNames != null) {
for (String typeName : typeNames) {
if (typeName.trim().length() == 0) {
continue;
}
Matcher m = PHPDocClassVariableEvaluator.ARRAY_TYPE_PATTERN
.matcher(typeName);
if (m.find()) {
evaluated.add(PHPDocClassVariableEvaluator
.getArrayType(m.group(), currentNamespace,
tag.sourceStart()));
} else if (typeName
.endsWith(PHPDocClassVariableEvaluator.BRACKETS)
&& typeName.length() > 2) {
int offset = tag.sourceStart();
evaluated.add(PHPDocClassVariableEvaluator
.getArrayType(
typeName.substring(0,
typeName.length() - 2),
currentNamespace, offset));
} else {
IEvaluatedType resolved = PHPSimpleTypes
.fromString(typeName);
if (resolved == null) {
resolved = new PHPClassType(typeName);
}
evaluated.add(resolved);
}
}
}
}
}
}
/**
* Resolves the type from the @property tag
*
* @param variableName
* @param docTag
* @return the types of the given variable
*/
private String[] getTypeBinding(String methodName, PHPDocTag docTag) {
final String[] split = docTag.getValue().trim().split("\\s+"); //$NON-NLS-1$
if (split.length < 2) {
return null;
}
if (split[1].equals(methodName)) {
return split[0].split("\\|");//$NON-NLS-1$
}
String substring = split[1];
int parenIndex = split[1].indexOf('('); //$NON-NLS-1$
if (parenIndex != -1) {
substring = substring.substring(0, parenIndex);
}
if (substring.equals(methodName)) {
return split[0].split("\\|");//$NON-NLS-1$
}
return null;
}
public IGoal[] subGoalDone(IGoal subgoal, Object result, GoalState state) {
if (state != GoalState.RECURSIVE && result != null) {
if (!yieldGoals.contains(subgoal)) {
evaluated.add((IEvaluatedType) result);
} else {
yieldEvaluated.add((IEvaluatedType) result);
}
}
return IGoal.NO_GOALS;
}
public Object produceResult() {
if (yieldEvaluated.size() > 0 || yieldGoals.size() > 0) {
GeneratorClassType generatorClassType = new GeneratorClassType();
generatorClassType.getTypes().addAll(yieldEvaluated);
evaluated.add(generatorClassType);
}
return PHPTypeInferenceUtils.combineTypes(evaluated);
}
}
/*******************************************************************************
* Copyright (c) 2009 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
* Zend Technologies
*******************************************************************************/
package org.eclipse.php.internal.core.typeinference.evaluators;
import java.util.*;
import org.eclipse.dltk.ast.ASTNode;
import org.eclipse.dltk.ast.expressions.Expression;
import org.eclipse.dltk.ast.references.TypeReference;
import org.eclipse.dltk.ast.references.VariableReference;
import org.eclipse.dltk.ast.statements.Statement;
import org.eclipse.dltk.core.DLTKCore;
import org.eclipse.dltk.core.ISourceModule;
import org.eclipse.dltk.evaluation.types.SimpleType;
import org.eclipse.dltk.ti.GoalState;
import org.eclipse.dltk.ti.IContext;
import org.eclipse.dltk.ti.ISourceModuleContext;
import org.eclipse.dltk.ti.goals.ExpressionTypeGoal;
import org.eclipse.dltk.ti.goals.GoalEvaluator;
import org.eclipse.dltk.ti.goals.IGoal;
import org.eclipse.dltk.ti.types.IEvaluatedType;
import org.eclipse.php.internal.core.PHPVersion;
import org.eclipse.php.internal.core.compiler.ast.nodes.*;
import org.eclipse.php.internal.core.project.ProjectOptions;
import org.eclipse.php.internal.core.typeinference.ArrayDeclaration;
import org.eclipse.php.internal.core.typeinference.Declaration;
import org.eclipse.php.internal.core.typeinference.IModelAccessCache;
import org.eclipse.php.internal.core.typeinference.PHPTypeInferenceUtils;
import org.eclipse.php.internal.core.typeinference.context.ContextFinder;
import org.eclipse.php.internal.core.typeinference.context.FileContext;
import org.eclipse.php.internal.core.typeinference.context.IModelCacheContext;
import org.eclipse.php.internal.core.typeinference.context.MethodContext;
import org.eclipse.php.internal.core.typeinference.goals.ArrayDeclarationGoal;
import org.eclipse.php.internal.core.typeinference.goals.ForeachStatementGoal;
import org.eclipse.php.internal.core.typeinference.goals.GlobalVariableReferencesGoal;
/**
* This evaluator finds all local variable declarations and produces the
* following sub-goals: {@link GlobalVariableReferencesGoal} or
* {@link VariableDeclarationGoal}
*/
public class VariableReferenceEvaluator extends GoalEvaluator {
private List<IEvaluatedType> results = new ArrayList<IEvaluatedType>();
public VariableReferenceEvaluator(IGoal goal) {
super(goal);
}
public IGoal[] init() {
final VariableReference variableReference = (VariableReference) ((ExpressionTypeGoal) goal)
.getExpression();
IContext context = goal.getContext();
IModelAccessCache cache = null;
if (context instanceof IModelCacheContext) {
cache = ((IModelCacheContext) context).getCache();
}
// Handle $this variable reference
if (variableReference.getName().equals("$this")) { //$NON-NLS-1$
if (context instanceof MethodContext) {
MethodContext methodContext = (MethodContext) context;
final LambdaFunctionDeclaration[] lambdas = new LambdaFunctionDeclaration[1];
ContextFinder contextFinder = new ContextFinder(
methodContext.getSourceModule()) {
@Override
public boolean visit(Expression s) throws Exception {
if (s instanceof LambdaFunctionDeclaration) {
LambdaFunctionDeclaration lambda = (LambdaFunctionDeclaration) s;
if (variableReference.sourceStart() > lambda
.sourceStart()
&& variableReference.sourceEnd() < lambda
.sourceEnd()) {
lambdas[0] = lambda;
}
}
return super.visit(s);
}
};
try {
methodContext.getRootNode().traverse(contextFinder);
} catch (Exception e) {
}
PHPVersion phpVersion = ProjectOptions
.getPhpVersion(methodContext.getSourceModule()
.getScriptProject().getProject());
if (lambdas[0] != null
&& (lambdas[0].isStatic() || phpVersion
.isLessThan(PHPVersion.PHP5_4))) {
this.results.add(new SimpleType(SimpleType.TYPE_NULL));
} else {
IEvaluatedType instanceType = methodContext
.getInstanceType();
if (instanceType != null) {
this.results.add(instanceType);
} else {
this.results.add(new SimpleType(SimpleType.TYPE_NULL));
}
}
return IGoal.NO_GOALS;
}
}
try {
if (context instanceof ISourceModuleContext) {
ISourceModuleContext typedContext = (ISourceModuleContext) context;
ASTNode rootNode = typedContext.getRootNode();
ASTNode localScopeNode = rootNode;
if (context instanceof MethodContext) {
localScopeNode = ((MethodContext) context).getMethodNode();
}
LocalReferenceDeclSearcher varDecSearcher = new LocalReferenceDeclSearcher(
typedContext.getSourceModule(), variableReference,
localScopeNode);
rootNode.traverse(varDecSearcher);
PHPModuleDeclaration phpModule = (PHPModuleDeclaration) rootNode;
List<IGoal> subGoals = new LinkedList<IGoal>();
List<VarComment> varComments = phpModule.getVarComments();
List<VarComment> newList = new ArrayList<VarComment>(phpModule
.getVarComments().size());
newList.addAll(varComments);
Collections.sort(newList, new Comparator<VarComment>() {
public int compare(VarComment o1, VarComment o2) {
return o2.sourceStart() - o1.sourceStart();
}
});
for (VarComment varComment : newList) {
if (varComment.sourceStart() > variableReference
.sourceStart()) {
continue;
}
if (varComment.getVariableReference().getName()
.equals(variableReference.getName())) {
List<IGoal> goals = new LinkedList<IGoal>();
for (TypeReference ref : varComment.getTypeReferences()) {
goals.add(new ExpressionTypeGoal(context, ref));
}
return (IGoal[]) goals.toArray(new IGoal[goals.size()]);
}
}
List<PHPDocBlock> docBlocks = new ArrayList<PHPDocBlock>(
phpModule.getPhpDocBlocks().size());
docBlocks.addAll(phpModule.getPhpDocBlocks());
Collections.sort(docBlocks, new Comparator<PHPDocBlock>() {
@Override
public int compare(PHPDocBlock o1, PHPDocBlock o2) {
return o1.sourceStart() - o1.sourceStart();
}
});
for (PHPDocBlock block : docBlocks) {
if (block.sourceStart() > variableReference.sourceStart()
|| localScopeNode.sourceStart() > block
.sourceStart()) {
continue;
}
for (PHPDocTag tag : block.getTags(PHPDocTagKinds.VAR)) {
String value = tag.getValue().trim();
if (value.charAt(0) != '$') {
continue;
}
String[] split = value.split("\\s+"); //$NON-NLS-1$
if (split.length > 1
&& split[0].equals(variableReference.getName())) {
List<IGoal> goals = new LinkedList<IGoal>();
for (String name : split[1].split("\\|")) { //$NON-NLS-1$
if (name.trim().length() > 0) {
goals.add(new ExpressionTypeGoal(context,
new TypeReference(
tag.sourceStart(), tag
.sourceEnd(), name
.trim())));
}
}
return (IGoal[]) goals.toArray(new IGoal[goals
.size()]);
}
}
}
Declaration[] decls = varDecSearcher.getDeclarations();
boolean mergeWithGlobalScope = false;
for (int i = 0; i < decls.length; ++i) {
Declaration decl = decls[i];
// TODO check ArrayCreation and its element type
if (decl instanceof ArrayDeclaration) {
ArrayDeclaration arrayDeclaration = (ArrayDeclaration) decl;
subGoals.add(new ArrayDeclarationGoal(context,
arrayDeclaration));
} else if (decl.getNode() instanceof GlobalStatement) {
mergeWithGlobalScope = true;
} else {
ASTNode declNode = decl.getNode();
if (declNode instanceof ForEachStatement) {
subGoals.add(new ForeachStatementGoal(context,
((ForEachStatement) declNode)
.getExpression()));
} else {
subGoals.add(new ExpressionTypeGoal(context,
declNode));
}
}
}
if (mergeWithGlobalScope
|| (decls.length == 0 && context.getClass() == FileContext.class)) {
// collect all global variables, and merge results with
// existing declarations
subGoals.add(new GlobalVariableReferencesGoal(context,
variableReference.getName()));
}
return subGoals.toArray(new IGoal[subGoals.size()]);
}
} catch (Exception e) {
if (DLTKCore.DEBUG) {
e.printStackTrace();
}
}
return IGoal.NO_GOALS;
}
public Object produceResult() {
return PHPTypeInferenceUtils.combineTypes(results);
}
public IGoal[] subGoalDone(IGoal subgoal, Object result, GoalState state) {
if (state != GoalState.RECURSIVE && result != null) {
results.add((IEvaluatedType) result);
}
return IGoal.NO_GOALS;
}
public static class LocalReferenceDeclSearcher
extends
org.eclipse.php.internal.core.typeinference.VariableDeclarationSearcher {
private final String variableName;
private final int variableOffset;
private final ASTNode localScopeNode;
private IContext variableContext;
private int variableLevel;
public LocalReferenceDeclSearcher(ISourceModule sourceModule,
VariableReference variableReference, ASTNode localScopeNode) {
super(sourceModule);
variableName = variableReference.getName();
variableOffset = variableReference.sourceStart();
this.localScopeNode = localScopeNode;
}
public Declaration[] getDeclarations() {
Declaration[] declarations = getScope(variableContext)
.getDeclarations(variableName);
if (variableLevel > 0 && variableLevel < declarations.length) {
Declaration[] newDecls = new Declaration[declarations.length
- variableLevel];
System.arraycopy(declarations, variableLevel, newDecls, 0,
newDecls.length);
declarations = newDecls;
}
List<Declaration> filteredDecls = new LinkedList<Declaration>();
for (Declaration decl : declarations) {
if (decl.getNode().sourceStart() > localScopeNode.sourceStart()) {
filteredDecls.add(decl);
}
}
return (Declaration[]) filteredDecls
.toArray(new Declaration[filteredDecls.size()]);
}
protected void postProcess(Expression node) {
if (node instanceof InstanceOfExpression) {
InstanceOfExpression expr = (InstanceOfExpression) node;
if (expr.getExpr() instanceof VariableReference) {
VariableReference varReference = (VariableReference) expr
.getExpr();
if (variableName.equals(varReference.getName())) {
getScope().addDeclaration(variableName,
expr.getClassName());
}
}
}
}
protected void postProcessGeneral(ASTNode node) {
if (node.sourceStart() <= variableOffset
&& node.sourceEnd() >= variableOffset) {
variableContext = contextStack.peek();
variableLevel = getScope(variableContext).getInnerBlockLevel();
}
}
protected void postProcess(Statement node) {
}
protected boolean isInteresting(ASTNode node) {
return node.sourceStart() <= variableOffset;
}
}
}