/******************************************************************************* * Copyright (c) 2009, 2016 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.context; import java.util.LinkedList; import java.util.List; import java.util.Stack; import org.eclipse.dltk.ast.ASTVisitor; import org.eclipse.dltk.ast.declarations.Argument; import org.eclipse.dltk.ast.declarations.MethodDeclaration; 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.TypeReference; import org.eclipse.dltk.core.ISourceModule; import org.eclipse.dltk.core.IType; import org.eclipse.dltk.evaluation.types.MultiTypeType; import org.eclipse.dltk.evaluation.types.UnknownType; import org.eclipse.dltk.ti.IContext; import org.eclipse.dltk.ti.ISourceModuleContext; import org.eclipse.dltk.ti.types.IEvaluatedType; import org.eclipse.php.core.compiler.ast.nodes.AnonymousClassDeclaration; import org.eclipse.php.core.compiler.ast.nodes.NamespaceDeclaration; import org.eclipse.php.core.compiler.ast.nodes.TraitDeclaration; import org.eclipse.php.internal.core.typeinference.PHPClassType; import org.eclipse.php.internal.core.typeinference.PHPThisClassType; import org.eclipse.php.internal.core.typeinference.evaluators.PHPTraitType; /** * This abstract AST visitor finds type inference context * * @author michael */ public abstract class ContextFinder extends ASTVisitor { protected Stack<IContext> contextStack = new Stack<IContext>(); private ISourceModule sourceModule; IType declaringType; IType realType; public ContextFinder(ISourceModule sourceModule) { this.sourceModule = sourceModule; } public ContextFinder(ISourceModule sourceModule, IType realType, IType declaringType) { this.sourceModule = sourceModule; this.declaringType = declaringType; this.realType = realType; } /** * This method must return found context * * @return */ public IContext getContext() { return null; } public boolean visit(ModuleDeclaration node) throws Exception { contextStack.push(new FileContext(sourceModule, node)); boolean visitGeneral = visitGeneral(node); if (!visitGeneral) { contextStack.pop(); } return visitGeneral; } public boolean visit(TypeDeclaration node) throws Exception { if (node instanceof NamespaceDeclaration) { FileContext fileContext = (FileContext) contextStack.peek(); if (((NamespaceDeclaration) node).isGlobal()) { fileContext.setNamespace(null); } else { fileContext.setNamespace(node.getName()); } } else { ISourceModuleContext parentContext = (ISourceModuleContext) contextStack.peek(); PHPClassType instanceType; if (parentContext instanceof INamespaceContext && ((INamespaceContext) parentContext).getNamespace() != null) { if (node instanceof TraitDeclaration) { instanceType = new PHPTraitType(((INamespaceContext) parentContext).getNamespace(), node.getName()); } else { instanceType = new PHPClassType(((INamespaceContext) parentContext).getNamespace(), node.getName()); } } else { if (node instanceof TraitDeclaration) { if (declaringType != null && realType != null && declaringType.getElementName().equals(node.getName())) { if (realType.getParent() instanceof IType) { IType ns = (IType) realType.getParent(); instanceType = new PHPThisClassType(ns.getElementName(), realType.getElementName(), realType); } else { instanceType = new PHPThisClassType(realType.getElementName(), realType); } } else { instanceType = new PHPTraitType(node.getName()); } } else { instanceType = new PHPClassType(node.getName()); } } contextStack.push(new TypeContext(parentContext, instanceType)); boolean visitGeneral = visitGeneral(node); if (!visitGeneral) { contextStack.pop(); } return visitGeneral; } return visitGeneral(node); } public boolean visit(AnonymousClassDeclaration node) throws Exception { ISourceModuleContext parentContext = (ISourceModuleContext) contextStack.peek(); MultiTypeType multiTypeType = new MultiTypeType(); if (node.getSuperClass() != null) { multiTypeType.addType(PHPClassType.fromSimpleReference(node.getSuperClass())); } if (node.getInterfaceList() != null) { for (TypeReference typeReference : node.getInterfaceList()) { multiTypeType.addType(PHPClassType.fromSimpleReference(typeReference)); } } contextStack.push(new TypeContext(parentContext, multiTypeType)); return visitGeneral(node); } @SuppressWarnings("unchecked") public boolean visit(MethodDeclaration node) throws Exception { List<String> argumentsList = new LinkedList<String>(); List<IEvaluatedType> argTypes = new LinkedList<IEvaluatedType>(); List<Argument> args = node.getArguments(); for (Argument a : args) { argumentsList.add(a.getName()); argTypes.add(UnknownType.INSTANCE); } IContext parent = contextStack.peek(); ModuleDeclaration rootNode = ((ISourceModuleContext) parent).getRootNode(); contextStack.push(new MethodContext(parent, sourceModule, rootNode, node, argumentsList.toArray(new String[argumentsList.size()]), argTypes.toArray(new IEvaluatedType[argTypes.size()]))); boolean visitGeneral = visitGeneral(node); if (!visitGeneral) { contextStack.pop(); } return visitGeneral; } public boolean endvisit(ModuleDeclaration node) throws Exception { contextStack.pop(); endvisitGeneral(node); return true; } public boolean endvisit(TypeDeclaration node) throws Exception { if (!(node instanceof NamespaceDeclaration)) { contextStack.pop(); } endvisitGeneral(node); return true; } public boolean endvisit(MethodDeclaration node) throws Exception { contextStack.pop(); endvisitGeneral(node); return true; } public boolean endvisit(AnonymousClassDeclaration node) throws Exception { contextStack.pop(); endvisitGeneral(node); return true; } @Override public boolean visit(Expression s) throws Exception { if (s instanceof AnonymousClassDeclaration) { return visit((AnonymousClassDeclaration) s); } return super.visit(s); } @Override public boolean endvisit(Expression s) throws Exception { if (s instanceof AnonymousClassDeclaration) { return endvisit((AnonymousClassDeclaration) s); } return super.endvisit(s); } }