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;
                }
        }
}
_______________________________________________
pdt-dev mailing list
[email protected]
To change your delivery options, retrieve your password, or unsubscribe from 
this list, visit
https://dev.eclipse.org/mailman/listinfo/pdt-dev

Reply via email to