blob: 8a66b40126a74aba406667f1f7ad8ada8d2832d3 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2005, 2007 IBM Corporation and others.
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* SPDX-License-Identifier: EPL-2.0
*
*******************************************************************************/
package org.eclipse.dltk.python.internal.core.evaluation;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.eclipse.dltk.ast.ASTNode;
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.expressions.NumericLiteral;
import org.eclipse.dltk.ast.expressions.StringLiteral;
import org.eclipse.dltk.ast.references.SimpleReference;
import org.eclipse.dltk.ast.references.VariableReference;
import org.eclipse.dltk.ast.statements.Block;
import org.eclipse.dltk.ast.statements.Statement;
import org.eclipse.dltk.core.IField;
import org.eclipse.dltk.core.IMethod;
import org.eclipse.dltk.core.IModelElement;
import org.eclipse.dltk.core.IModule;
import org.eclipse.dltk.core.IParent;
import org.eclipse.dltk.core.IType;
import org.eclipse.dltk.core.ModelException;
import org.eclipse.dltk.evaluation.types.ClassInstanceType;
import org.eclipse.dltk.evaluation.types.FunctionType;
import org.eclipse.dltk.evaluation.types.IClassType;
import org.eclipse.dltk.evaluation.types.ModelClassType;
import org.eclipse.dltk.evaluation.types.ModelFunctionType;
import org.eclipse.dltk.evaluation.types.ModelModuleType;
import org.eclipse.dltk.evaluation.types.MultiTypeType;
import org.eclipse.dltk.evaluation.types.OldClassType;
import org.eclipse.dltk.evaluation.types.SimpleType;
import org.eclipse.dltk.evaluation.types.UnknownType;
import org.eclipse.dltk.python.internal.core.evaluation.types.ImportedCallType;
import org.eclipse.dltk.python.internal.core.evaluation.types.ImportedType;
import org.eclipse.dltk.python.parser.ast.PythonImportFromStatement;
import org.eclipse.dltk.python.parser.ast.PythonImportStatement;
import org.eclipse.dltk.python.parser.ast.expressions.Assignment;
import org.eclipse.dltk.python.parser.ast.expressions.BinaryExpression;
import org.eclipse.dltk.python.parser.ast.expressions.CallHolder;
import org.eclipse.dltk.python.parser.ast.expressions.ExtendedVariableReference;
import org.eclipse.dltk.python.parser.ast.expressions.PythonDictExpression;
import org.eclipse.dltk.python.parser.ast.expressions.PythonImportAsExpression;
import org.eclipse.dltk.python.parser.ast.expressions.PythonImportExpression;
import org.eclipse.dltk.python.parser.ast.expressions.PythonListExpression;
import org.eclipse.dltk.python.parser.ast.expressions.PythonTestListExpression;
import org.eclipse.dltk.python.parser.ast.expressions.PythonTupleExpression;
import org.eclipse.dltk.python.parser.ast.statements.ReturnStatement;
import org.eclipse.dltk.ti.types.IEvaluatedType;
import org.eclipse.dltk.ti.types.RecursionTypeCall;
public class PythonASTTypeEvaluator {
private static final String SELF_CONSTANT = "self";
private ModuleDeclaration fModule = null;
private Map fParents = null;
private IModule fModelModule = null;
private List fEvaluationList = new ArrayList();
private Map fLocalIndex = null;
/*
* way to remove recursion.
*/
public PythonASTTypeEvaluator(IModule modelModule,
ModuleDeclaration module, Map/*<ASTNode, ASTNode>*/ parents) {
this.fModule = module;
this.fParents = parents;
this.fModelModule = modelModule;
this.fLocalIndex = makeLocalIndex(this.fModelModule);
}
// Statistic information of unknown detections.
public static long totalSimpleReferenceDetections = 0;
public static long unknownSimpleReferenceBecauseOfArgument = 0;
public static long unknownSimpleReferenceNotInAST = 0;
public static long unknownSimpleReferenceNotFoundAtEnd = 0;
public static long unknownReturnBecauseOfArgument = 0;
public static long unknownSimpleReferenceEmptyParent = 0;
public static long unknownSimpleReferenceNotFoundAtEndBuiltin = 0;
public static long unknownBinaryExpressionCount = 0;
public static long unknownBinaryExpressionUnDetected = 0;
public static long unknownBinaryExpressionNotSimpleTypes = 0;
public static long unknownExtendedCount = 0;
public static long unknownExtendedUnDetectedIndex = 0;
/**
* Used to evaluate binaty expressiont in AST.
*
* @param node
* @return
*/
public IEvaluatedType evaluateASTBinaryExpression(ASTNode node) {
BinaryExpression expression = (BinaryExpression) node;
IEvaluatedType leftType = this.evaluateASTNode(expression.getLeft(),
null);
IEvaluatedType rightType = this.evaluateASTNode(expression.getRight(),
null);
unknownBinaryExpressionCount += 1;
// TODO: Add correct other types binary expression handling here. May be
// operator calls' or other stuff.
if (leftType instanceof UnknownType || rightType instanceof UnknownType) {
return UnknownType.INSTANCE;
}
if (!(leftType instanceof SimpleType && rightType instanceof SimpleType)) {
unknownBinaryExpressionNotSimpleTypes += 1;
return UnknownType.INSTANCE;
}
int left = ((SimpleType) leftType).getType();
int right = ((SimpleType) rightType).getType();
int kind = expression.getKind();
IEvaluatedType type = PythonBinaryExpressionOperations.makeType(left,
kind, right);
if (type instanceof UnknownType) {
unknownBinaryExpressionUnDetected += 1;
}
return type;
// binding.
}
/**
* Used to evaluate type of any AST node.
*
* @param module
* @param parents
* @param node
* @return
*/
public IEvaluatedType evaluateASTNode(ASTNode node, ASTNode scope) {
if (this.fEvaluationList.contains(node)) {
// CorePrinter printer = new CorePrinter( System.out );
// printer.formatPrintLn( "Recursion on field:" );
// node.printNode( printer );
// printer.flush( );
return RecursionTypeCall.INSTANCE;
// throw new RuntimeException("Recursion detected");
}
this.fEvaluationList.add(node);
if (node == null) {
return UnknownType.INSTANCE;
}
if (node instanceof Assignment) {
Assignment assign = (Assignment) node;
return evaluateASTNode(assign.getRight(), scope);
} else if (node instanceof MethodDeclaration) {
// return evaluateFunctionReturnValue( node, scope );
return new FunctionType(this.fModule, (MethodDeclaration) node);
} else if (node instanceof BinaryExpression) {
return evaluateASTBinaryExpression(node);
} else if (node instanceof NumericLiteral) {
return new SimpleType(SimpleType.TYPE_NUMBER);
} else if (node instanceof StringLiteral) {
return new SimpleType(SimpleType.TYPE_STRING);
} else if (node instanceof PythonTupleExpression) {
return new SimpleType(SimpleType.TYPE_TUPLE);
} else if (node instanceof PythonListExpression) {
return new SimpleType(SimpleType.TYPE_LIST);
} else if (node instanceof PythonDictExpression) {
return new SimpleType(SimpleType.TYPE_DICT);
} else if (node instanceof SimpleReference) { // Simple reference
return evaluateSimpleReferenceType((SimpleReference) node, scope);
} else if (node instanceof ExtendedVariableReference) {
return evaluateExtendedReferenceType(
(ExtendedVariableReference) node, node);
} else if (node instanceof TypeDeclaration) {
return new OldClassType(this.fModule, (TypeDeclaration) node);
}
return UnknownType.INSTANCE;
}
/**
* Evaluate call node. This may be only be functions or lambda expressions.
* Or call what return functions or lambda expressions.
*/
public IEvaluatedType evaluateCallASTNode(ASTNode node,
CallHolder callHolder, boolean isMethod, ASTNode scope) {
if (node == null) {
return UnknownType.INSTANCE;
} else if (node instanceof SimpleReference) { // Simple reference
IEvaluatedType type = evaluateSimpleReferenceType(
(SimpleReference) node, scope);
if (type instanceof FunctionType) {
ASTNode parent = (ASTNode)this.fParents.get(((FunctionType) type)
.getFunction());
if (isMethod && parent instanceof TypeDeclaration) {
return type;
} else if (!isMethod && !(parent instanceof TypeDeclaration)) {
return type;
} else {
// Not approppriate type.
return UnknownType.INSTANCE;
}
} else if (type instanceof OldClassType) {
return type;
}
// TODO: Add class methods here to check.
} else if (node instanceof ExtendedVariableReference) {
return evaluateExtendedReferenceType(
(ExtendedVariableReference) node, scope);
}
return UnknownType.INSTANCE;
}
private IEvaluatedType evaluateExtendedReferenceType(
ExtendedVariableReference reference, ASTNode scope) {
unknownExtendedCount += 1;
// TODO: Add correct multi extended variables handling here.
// Tonay only simple variables are handled.
List/*<Expression>*/ expressions = reference.getExpressions();
IEvaluatedType type = null;
Expression expr = null;
int index = 0;
while (index < expressions.size()) {
expr = (Expression)expressions.get(index);
// This is method call.
if (reference.isCall(index)) {
CallHolder callHolder = (CallHolder) expressions.get(index + 1);
if (type == null) { // This is first
type = evaluateFunctionCall(expr, callHolder, scope);
}
// Not first
else {
if (type instanceof IClassType
&& expr instanceof SimpleReference) {
type = evaluateMethodCall(type, (SimpleReference) expr,
callHolder, scope);
}
}
if (type == null) {
return UnknownType.INSTANCE;
}
// Need to evaluate in the selected class or variable.
index += 2;
} else if (reference.isDot(index) || reference.isLast(index)) {
if (type == null) { // first
// lets firstly check for dotted import names and then check
// for simple name.
type = checkDottedImports(expressions, expr, scope);
if (type == null) {
type = evaluateIdentifier(expr, scope);
}
if (type instanceof ImportedType) {
type = ((ImportedType) type).getType();
}
} else { // second and other.
if (type instanceof ModelModuleType) {
type = evaluateModuleSubIdentifier(
((ModelModuleType) type).getModule(), expr);
} else if (type instanceof ClassInstanceType) {
type = evaluateASTClassSubIdentifier(
((ClassInstanceType) type).getTypeDeclaration(),
expr, false);
} else if (type instanceof OldClassType) {
type = evaluateASTClassSubIdentifier(((OldClassType) type)
.getTypeDeclaration(), expr, true);
} else if (type instanceof ModelClassType) {
/*
* Model class evaluation.
*/
type = evaluateModelClassSubIdentifier(
(ModelClassType) type, expr);
}
if (type instanceof ImportedType) {
type = ((ImportedType) type).getType();
}
}
if (type == null) {
return UnknownType.INSTANCE;
} else if (type instanceof ModelModuleType) {
ModelModuleType modelModuleType = (ModelModuleType) type;
index += modelModuleType.getStepCount() - 1;
}
} else if (reference.isIndex(index)) {
unknownExtendedUnDetectedIndex += 1;
return UnknownType.INSTANCE;
}
index++;
}
return type;
}
private IEvaluatedType evaluateASTClassSubIdentifier(
TypeDeclaration typeDeclaration, Expression expr,
boolean bStaticsOnly) {
if (expr instanceof SimpleReference) {
SimpleReference reference = (SimpleReference) expr;
SimpleReference ref = (SimpleReference) expr;
PythonASTFindVisitor visitor = new PythonASTFindVisitor(ref
.getName(), bStaticsOnly);
try {
typeDeclaration.traverse(visitor);
} catch (Exception ex) {
ex.printStackTrace();
return null;
}
List/*<ASTNode>*/ nodes = visitor.getNodes();
IEvaluatedType type = this.checkListForReference(reference
.getName(), nodes, typeDeclaration);
if (type != null) {
return type;
}
IEvaluatedType imported = checkReferenceImports(
reference.getName(), nodes);
if (imported != null) {
return imported;
}
IEvaluatedType btype = evaluateReferenceInBaseClasses(
typeDeclaration, (SimpleReference) expr, bStaticsOnly);
if (btype != null) {
return btype;
}
}
return null;
}
private IEvaluatedType evaluateReferenceInBaseClasses(
TypeDeclaration typeDeclaration, SimpleReference expr,
boolean staticOnly) {
List/*<String>*/ baseClasses = typeDeclaration.getSuperClassNames();
Iterator i = baseClasses.iterator();
while( i.hasNext() ) {
String className = (String)i.next();
IEvaluatedType type = evaluateName(className, typeDeclaration);
if (type instanceof ImportedType) {
type = ((ImportedType) type).getType();
}
if (type instanceof OldClassType) {
OldClassType ctype = (OldClassType) type;
IEvaluatedType subIdentifier = this
.evaluateASTClassSubIdentifier(ctype
.getTypeDeclaration(), expr, staticOnly);
if (subIdentifier != null) {
return subIdentifier;
}
} else if (type instanceof ModelClassType) {
ModelClassType ctype = (ModelClassType) type;
IEvaluatedType subIdentifier = this
.evaluateModelClassSubIdentifier(ctype, expr);
if (subIdentifier != null) {
return subIdentifier;
}
}
}
return null;
}
/**
* Check for
*
* @param className
* @param scope
* @return
*/
private IEvaluatedType evaluateName(String name, ASTNode scope) {
VariableReference ref = new VariableReference(-2, -3, name);
this.fParents.put(ref, scope);
IEvaluatedType type = this.evaluateSimpleReferenceType(ref, null);
this.fParents.remove(ref);
return type;
}
private IEvaluatedType evaluateModelClassSubIdentifier(
ModelClassType modelType, Expression expr) {
if (expr instanceof SimpleReference) {
SimpleReference ref = (SimpleReference) expr;
IType classElement = modelType.getTypeDeclaration();
if (classElement != null) {
IModelElement[] elements = null;
try {
elements = classElement.getChildren();
} catch (ModelException ex) {
ex.printStackTrace();
return null;
}
for (int i = 0; i < elements.length; ++i ) {
IModelElement element = elements[i];
if (element instanceof IField) {
if (((IField) element).getElementName().equals(
ref.getName())) {
IEvaluatedType type = PythonTypeEvaluator
.evaluate(element);
if (type != null) {
return type;
}
} else if (element instanceof IMethod) {
if (((IMethod) element).getElementName().equals(
ref.getName())) {
return new ModelFunctionType((IMethod) element);
}
}
}
}
// check for base classes here.
try {
String[] superClassNames = classElement.getSuperClasses();
for ( int i = 0; i < superClassNames.length; ++i ) {
String superClass = superClassNames[i];
IModelElement baseElement = this
.findBaseClassForElement(classElement,
superClass);
if (baseElement instanceof IType) {
IEvaluatedType type = evaluateModelClassSubIdentifier(
new ModelClassType((IType) baseElement),
expr);
if (type != null) {
return type;
}
} else if (baseElement instanceof IModule) {
IEvaluatedType type = evaluateModuleSubIdentifier(
(IModule) baseElement, expr);
if (type != null) {
return type;
}
}
}
} catch (ModelException ex) {
ex.printStackTrace();
return UnknownType.INSTANCE;
}
}
}
return null;
}
private IModelElement findBaseClassForElement(IType classElement,
String baseClassName) {
// TODO:Need to be reimplemented on new model.
// IModelElement parent = classElement.getParent( );
// Collection< IImportStatement > imports = null;
// if( parent instanceof IType ) {
// IType classElement0 = ( IType )parent;
// try {
// imports = classElement0.getImports( );
// }
// catch( ModelException ex ) {
// ex.printStackTrace( );
// return null;
// }
// }
// else if( parent instanceof IMethod ) {
// IMethod methodElement = ( IMethod )parent;
// try {
// imports = methodElement.getImports( );
// }
// catch( ModelException ex ) {
// ex.printStackTrace( );
// return null;
// }
// }
// else if( parent instanceof IModule ) {
// IModule moduleElement = ( IModule )parent;
// try {
// imports = moduleElement.getImports( );
// }
// catch( ModelException ex ) {
// ex.printStackTrace( );
// return null;
// }
// }
// // lest filter all imports below our element.
// List< IImportStatement > neededImports = new ArrayList<
// IImportStatement >( );
//
// PositionInformation elementPosition = classElement.getPosition( );
//
// if( elementPosition != null ) {
// for( IImportStatement statement : imports ) {
// PositionInformation position = statement.getPosition( );
// if( position != null ) {
// if( position.isLess( elementPosition ) ) {
// neededImports.add( statement );
// }
// }
// }
// }
// // we can't check anything then position is null.
// for( IImportStatement statement : neededImports ) {
// IModelElement sElement = null;
// try {
// sElement = statement.isImported( baseClassName );
// }
// catch( ModelException ex ) {
// ex.printStackTrace( );
// continue;
// }
// if( sElement != null ) {
// return sElement;
// }
// }
return null;
}
private IEvaluatedType evaluateModuleSubIdentifier(IModule module,
Expression expr) {
if (expr instanceof SimpleReference) {
IModelElement[] childrens = null;
try {
childrens = module.getChildren();
} catch (ModelException ex) {
ex.printStackTrace();
}
// TODO: Add order corrections here.
if (childrens != null) {
for( int i = 0; i < childrens.length; ++i ) {
IModelElement element = childrens[i];
if (element instanceof IField) {
if (((IField) element).getElementName().equals(
((SimpleReference) expr).getName())) {
return PythonTypeEvaluator.evaluate(element);
}
} else if (element instanceof IMethod) {
if (((IMethod) element).getElementName().equals(
((SimpleReference) expr).getName())) {
return new ModelFunctionType((IMethod) element);
}
} else if (element instanceof IType) {
if (((IType) element).getElementName().equals(
((SimpleReference) expr).getName())) {
return new ModelClassType((IType) element);
}
}
}
}
}
return null;
}
/**
* Returns last possible dotted name module.
*
* @param expressions
* @param expr
* @param scope
* @return
*/
private IEvaluatedType checkDottedImports(List/*<Expression>*/ expressions,
Expression expr, ASTNode scope) {
/*
* lets check for possible element in module like a.b.c and has import
* such as a.b.c
*/
// check for imports with dots.
String pName = "";
boolean firstI = true;
int index = 1;
IEvaluatedType lastType = null;
Iterator i = expressions.iterator();
while( i.hasNext() ) {
Expression exp = (Expression)i.next();
if (exp instanceof SimpleReference) {
if (firstI) {
pName += ((SimpleReference) exp).getName();
firstI = false;
continue;
} else {
pName += "." + ((SimpleReference) exp).getName();
}
index += 1;
IEvaluatedType type = this.evaluateName(pName, scope);
if (type != null) {
if (type instanceof ModelModuleType) {
lastType = new ModelModuleType(((ModelModuleType) type)
.getModule(), index);
} else if (type instanceof UnknownType) {
continue;
}
}
}
}
return lastType;
}
private IEvaluatedType evaluateIdentifier(Expression expr, ASTNode scope) {
IEvaluatedType ctype = evaluateASTNode(expr, scope);
if (ctype instanceof ModelClassType) {
return ctype;
} else if (ctype instanceof OldClassType) {
return ctype;
} else if (ctype instanceof ClassInstanceType) {
return ctype;
} else if (ctype instanceof FunctionType) {
return ctype;
} else if (ctype instanceof ModelFunctionType) {
return ctype;
} else if (ctype instanceof ModelModuleType) {
return ctype;
}
return null;
}
private IEvaluatedType evaluateMethodCall(IEvaluatedType type,
SimpleReference reference, CallHolder callHolder, ASTNode scope) {
if (type instanceof OldClassType) {
// TODO: Implement static call
OldClassType classType = (OldClassType) type;
IEvaluatedType rtype = this.evaluateASTClassSubIdentifier(classType
.getTypeDeclaration(), reference, true);
if (rtype instanceof ImportedType) {
rtype = ((ImportedType) rtype).getType();
}
if (rtype instanceof FunctionType) {
IEvaluatedType ftype = evaluateMethodReturnValue(
((FunctionType) rtype).getFunction(), callHolder, scope);
if (ftype != null) {
return ftype;
}
}
// TODO: Add imported function for base function check.
return UnknownType.INSTANCE;
} else if (type instanceof ClassInstanceType) {
// TODO: Evaluate non static call.
IEvaluatedType rtype = this.evaluateASTClassSubIdentifier(
((ClassInstanceType) type).getTypeDeclaration(), reference,
false);
if (rtype instanceof FunctionType) {
IEvaluatedType ftype = evaluateMethodReturnValue(
((FunctionType) rtype).getFunction(), callHolder, scope);
if (ftype != null) {
return ftype;
}
}
// TODO: Add imported function for base function check.
return UnknownType.INSTANCE;
} else if (type instanceof ModelClassType) {
IType classElement = ((ModelClassType) type).getTypeDeclaration();
if (classElement != null) {
// if( this.fClassIndex != null ) {
// List< IModelElement > elements =
// this.fClassIndex.getElements( reference.getName( ) );
//
// for( IModelElement element : elements ) {
// if( element.getParent( ).equals( classElement ) ) {
// if( element instanceof IField ) {
// return TypeEvaluator.evaluate( element );
// }
// else if( element instanceof IMethod ) {
//
// /*
// * TODO: Add appropriate arguments here,
// */
// return TypeEvaluator.evaluateCall( element, null );
// }
// }
// }
// }
}
}
return null;
}
private IEvaluatedType evaluateFunctionCall(Expression expr,
CallHolder callHolder, ASTNode scope) {
IEvaluatedType ctype = evaluateCallASTNode(expr, callHolder, false,
scope);
if (ctype instanceof OldClassType) {
return new ClassInstanceType(this.fModule, ((OldClassType) ctype)
.getTypeDeclaration());
} else if (ctype instanceof FunctionType) {
return evaluateFunctionReturnValue(((FunctionType) ctype)
.getFunction(), callHolder, scope);
} else if (ctype instanceof ImportedCallType) {
ImportedCallType icType = (ImportedCallType) ctype;
IModelElement modelElement = icType.getElement();
if (modelElement != null) {
if (modelElement instanceof IType) {
/*
* this is class.
*/
return new ModelClassType((IType) modelElement);
} else if (modelElement instanceof IMethod) {
/*
* TODO: Add argumens type evaluation here before of call
* evaluation for correct type evaluation of method
* declaration.
*/
return PythonTypeEvaluator.evaluateCall(modelElement, null);
}
}
}
return null;
}
private List/*<ASTNode>*/ getUpperNodes(ASTNode parent, ASTNode nodeParent) {
List/*<ASTNode>*/ nodes = PythonTypeEvaluatorUtils
.getAllASTLevelChilds(parent);
// Here must be all childs of selected node.
if (nodes.contains(nodeParent)) {
// We are in first context. So remove current and
// all above.
List/*<ASTNode>*/ nnodes = new ArrayList/*<ASTNode>*/();
Iterator i = nodes.iterator();
while( i.hasNext() ) {
ASTNode nnde = (ASTNode)i.next();
if (nnde.equals(nodeParent)) {
break;
} else {
nnodes.add(nnde);
}
}
nodes = nnodes;
}
// lets find declaration of this reference from end.
Collections.reverse(nodes);
return nodes;
}
private List/*<ReturnStatement>*/ findReturns(ASTNode node) {
List/*<ASTNode>*/ nodes = PythonTypeEvaluatorUtils
.getAllASTLevelChilds(node);
// Filter return statements.
List/*<ReturnStatement>*/ returns = new ArrayList/*<ReturnStatement>*/();
if (nodes == null) {
return returns;
}
Iterator i = nodes.iterator();
while( i.hasNext() ) {
ASTNode nde = (ASTNode)i.next();
if (nde instanceof ReturnStatement) {
returns.add(nde);
}
}
return returns;
}
private IEvaluatedType makeMultiType(List/*<IEvaluatedType>*/ types) {
int size = types.size();
if (size == 1) {
return (IEvaluatedType)types.get(0);
} else if (size > 1) {
MultiTypeType multi = new MultiTypeType();
Iterator i = types.iterator();
while( i.hasNext()) {
IEvaluatedType type = (IEvaluatedType)i.next();
multi.addType(type);
}
if (multi.size() == 1) {
return multi.get(0);
}
return multi;
}
return new SimpleType(SimpleType.TYPE_NONE);
}
/**
* Used to evaluate method return value from correct ASTNode.
*/
private IEvaluatedType evaluateFunctionReturnValue(ASTNode node,
ASTNode scope) {
List/*<ReturnStatement>*/ returns = findReturns(node);
if (returns == null) {
return new SimpleType(SimpleType.TYPE_NONE);
}
// TODO: Add correct multiple types check.
int count = 0;
List/*<IEvaluatedType>*/ types = new ArrayList/*<IEvaluatedType>*/();
Iterator i = returns.iterator();
while( i.hasNext() ) {
ReturnStatement returnS = (ReturnStatement)i.next();
if (returnS.getExpression() != null) {
IEvaluatedType type = evaluateASTNode(returnS.getExpression(),
scope);
if (!(type instanceof RecursionTypeCall)) {
types.add(type);
}
} else {
types.add(new SimpleType(SimpleType.TYPE_NONE));
}
}
return makeMultiType(types);
}
private IEvaluatedType evaluateFunctionReturnValue(ASTNode node,
CallHolder arguments, ASTNode scope) {
// TODO: Add call dependent method return value implementation.
MethodDeclaration method = (MethodDeclaration) node;
List originalArgumetns = method.getArguments();
List/*<Expression>*/ argValues = PythonTypeEvaluatorUtils
.parseCallHolder(arguments);
// TODO: Add argument type checking. *args is array of all posible
// arguments.
// and **kwrargs is map with names and arguments.
List/*<Expression>*/ oldInitialValues = new ArrayList/*<Expression>*/();
int index = 0;
if (originalArgumetns.size() == argValues.size()) {
Iterator i = originalArgumetns.iterator();
while( i.hasNext()) {
Argument arg = (Argument)i.next();
oldInitialValues.add(arg.getInitialization());
arg.setInitializationExpression((Expression)argValues.get(index));
index += 1;
}
} else {
return evaluateFunctionReturnValue(node, scope);
}
IEvaluatedType type = evaluateFunctionReturnValue(node, scope);
index = 0;
Iterator i = originalArgumetns.iterator();
while( i.hasNext()) {
Argument arg = (Argument)i.next();
arg.setInitializationExpression((Expression)oldInitialValues.get(index));
index += 1;
}
return type;
}
private IEvaluatedType evaluateMethodReturnValue(ASTNode node,
CallHolder arguments, ASTNode scope) {
// TODO: Add call dependent method return value implementation.
MethodDeclaration method = (MethodDeclaration) node;
List originalArgumetns = method.getArguments();
List/*<Expression>*/ argValues = PythonTypeEvaluatorUtils
.parseCallHolder(arguments);
// TODO: Add argument type checking. *args is array of all posible
// arguments.
// and **kwrargs is map with names and arguments.
List/*<Expression>*/ oldInitialValues = new ArrayList/*<Expression>*/();
int index = 0;
if (originalArgumetns.size() - 1 == argValues.size()) {
boolean skipFirst = true;
Iterator i = originalArgumetns.iterator();
while( i.hasNext()) {
Argument arg = (Argument)i.next();
if (skipFirst) {
skipFirst = false;
continue;
}
oldInitialValues.add(arg.getInitialization());
arg.setInitializationExpression((Expression)argValues.get(index));
index += 1;
}
} else {
return evaluateFunctionReturnValue(node, scope);
}
IEvaluatedType type = evaluateFunctionReturnValue(node, scope);
index = 0;
boolean skipFirst = true;
Iterator i = originalArgumetns.iterator();
while( i.hasNext()) {
Argument arg = (Argument)i.next();
if (skipFirst) {
skipFirst = false;
continue;
}
arg.setInitializationExpression((Expression)oldInitialValues.get(index));
index += 1;
}
return type;
}
private IEvaluatedType checkArguments(MethodDeclaration method,
String reference) {
// / Checks for parameters and may be it has types.
List arguments = method.getArguments();
boolean first = true;
Iterator i = arguments.iterator();
while( i.hasNext()) {
Argument arg = (Argument)i.next();
if (arg.getName().equals(reference)) {
/*
* TODO: Add staticmethod and classmethods check here.
*/
if (first) {
// should return itselt if this is correct and upper if it
// is block possible.
ASTNode parent = (ASTNode)this.fParents.get(method);
ASTNode parentParent = PythonTypeEvaluatorUtils
.getModelLikeNode(fParents, parent);
if (parentParent instanceof TypeDeclaration) {
/*
* suppose this is self. If is call check may be this is
* call to __call__ method.
*/
// TODO: Add check of staticmethod and classmethod to
// return needed type here.
return new ClassInstanceType(this.fModule,
(TypeDeclaration) parentParent);
}
}
if (arg.getInitialization() != null) {
IEvaluatedType type = evaluateASTNode(arg
.getInitialization(), null);
if (type instanceof UnknownType) {
unknownSimpleReferenceBecauseOfArgument += 1;
}
return type;
} else {
unknownSimpleReferenceBecauseOfArgument += 1;
return UnknownType.INSTANCE;
}
}
first = false;
}
return null;
}
private IEvaluatedType checkMethodNode(MethodDeclaration method,
CallHolder callHolder, SimpleReference reference) {
if (method.getName().equals(reference.getName())) {
List arguments = method.getArguments();
List/*<Expression>*/ callHolderArguments = PythonTypeEvaluatorUtils
.parseCallHolder(callHolder);
// TODO: Add correct check for arguments.
if (callHolderArguments.size() == arguments.size()) {
return new FunctionType(this.fModule, method);
} else {
return new FunctionType(this.fModule, method, true);
}
}
return null;
}
/**
* Used to filter list of in one module model elements by call holder and
* some other information.
*
* @param types
* @param callHolder
* @return one desired element or null if it can't be found.
*/
private IModelElement filterCallType(List/*<IModelElement>*/ types,
CallHolder callHolder) {
// TODO: Add correct code here.
// Now return first with appropriate count of arguments if function and
// first class if class.
List/*<IModelElement>*/ fTypes = new ArrayList/*<IModelElement>*/();
fTypes.addAll(types);
Collections.reverse(fTypes);
Iterator i = fTypes.iterator();
while( i.hasNext() ) {
IModelElement element = (IModelElement)i.next();
if (element instanceof IMethod) {
IMethod method = (IMethod) element;
String[] args = null;
try {
args = method.getParameterNames();
List/*<Expression>*/ callHolderArgs = PythonTypeEvaluatorUtils
.parseCallHolder(callHolder);
if (args.length == callHolderArgs.size()) {
return element;
}
} catch (ModelException ex) {
ex.printStackTrace();
}
} else if (element instanceof IType) { // This is class
// instanciation possible
// action.
return element;
}
}
return null;
}
private IEvaluatedType evaluateSimpleReferenceType(
SimpleReference reference, ASTNode scope) {
// adding only for simple reference detections.
if (reference.getName().indexOf(".")==-1) {
totalSimpleReferenceDetections += 1;
}
ASTNode parent = null;
if (!this.fParents.containsKey(reference)) {
// throw new RuntimeException("node not in AST.");
if (reference.getName().indexOf(".")==-1) {
unknownSimpleReferenceNotInAST += 1;
}
return UnknownType.INSTANCE;
}
parent = PythonTypeEvaluatorUtils.getParentNode(this.fParents,
reference);
if (parent == null) {
if (reference.getName().indexOf(".")==-1) {
totalSimpleReferenceDetections -= 1;
/*
* this is not detection. FIXME: Possible this is some kind of
* not in ast nodes? Need to check.
*/
}
return UnknownType.INSTANCE;
}
/*
* This is parent that can' be found in this context.
*/
ASTNode nodeParent = PythonTypeEvaluatorUtils.getNodeParentBeforeModel(
this.fParents, reference);
if (nodeParent == null) {
if (reference.getName().indexOf(".")==-1) {
unknownSimpleReferenceEmptyParent += 1;
}
return UnknownType.INSTANCE;
}
ASTNode pnode = nodeParent;
/*
* Previous parent. node parent first not module, method or type will be
* fine. This is used to remove under declarations.
*/
boolean parentFromScope = false;
while (parent != null) {
// find from begining this node declaration.
// May be only assignments or methods.
List/*<ASTNode>*/ nodes = null;
try {
if (parent instanceof ModuleDeclaration && scope != null
&& !parentFromScope) {
pnode = PythonTypeEvaluatorUtils.getNodeParentBeforeModel(
this.fParents, scope);
nodeParent = pnode;
parent = PythonTypeEvaluatorUtils.getParentNode(
this.fParents, scope);
parentFromScope = true;
}
// This is for correcting filtration.
if (scope != null) {
if (parent instanceof ModuleDeclaration) {
ASTNode par = (ASTNode)this.fParents.get(nodeParent);
if (par != null) {
while (!par.equals(parent) && par != null) {
nodeParent = par;
par = (ASTNode)this.fParents.get(nodeParent);
}
}
}
}
nodes = getUpperNodes(parent, pnode);
// if this is parent then lets go to parent parent for correct
// checks.
if (parent instanceof Block) {
IEvaluatedType stype = checkListForReference(reference
.getName(), nodes, scope);
if (stype != null) {
return stype;
}
/*
* If we can't found here. Then check for this scope has
* contexts. and check may be we import needed element from
* this context.
*/
IEvaluatedType imported = checkReferenceImports(reference
.getName(), nodes);
if (imported != null) {
return imported;
}
if (!(parent instanceof ModuleDeclaration)) {
if (!this.fParents.containsKey(parent)) {
if (reference.getName().indexOf(".")==-1) {
unknownSimpleReferenceNotInAST += 1;
}
return UnknownType.INSTANCE;
}
} else {
parent = null;
}
} else if (parent instanceof MethodDeclaration) {
IEvaluatedType type = checkArguments(
(MethodDeclaration) parent, reference.getName());
if (type != null) {
return type;
}
} else if (parent instanceof TypeDeclaration) { // we can search
// for class or
// method name
// here also.
Iterator i = nodes.iterator();
while( i.hasNext() ) {
ASTNode node = (ASTNode)i.next();
if (node instanceof MethodDeclaration) {
MethodDeclaration method = (MethodDeclaration) node;
if (method.getName().equals(reference.getName())) {
return new FunctionType(this.fModule,
(MethodDeclaration) node);
}
}
if (node instanceof TypeDeclaration) {
TypeDeclaration method = (TypeDeclaration) node;
if (method.getName().equals(reference.getName())) {
return new OldClassType(this.fModule,
(TypeDeclaration) node);
}
}
}
}
pnode = parent;
parent = (ASTNode)this.fParents.get(parent);
} catch (Exception e) {
e.printStackTrace();
return UnknownType.INSTANCE;
}
}
// check in ClassIndex.
// check for buildin name.
IEvaluatedType buildinType = evaluateBuildin(reference.getName());
if (buildinType != null) {
return buildinType;
}
if (!PythonBuiltIns.isBuiltin(reference.getName())) {
if (reference.getName().indexOf(".")==-1) {
unknownSimpleReferenceNotFoundAtEnd += 1;
}
}
return UnknownType.INSTANCE;
}
private IEvaluatedType checkReferenceImports(String reference,
List/*<ASTNode>*/ nodes) {
Iterator i = nodes.iterator();
while( i.hasNext() ) {
ASTNode nde = (ASTNode)i.next();
if (nde instanceof PythonImportFromStatement) {
IModelElement element = null;// this.findImport( (
// PythonImportFromStatement
// )nde, reference );
if (element != null) {
if (element instanceof IField) {
return new ImportedType(PythonTypeEvaluator
.evaluateLast(element),
PythonTypeEvaluatorUtils
.getElementModule(element));
} else if (element instanceof IMethod) {
return new ImportedType(new ModelFunctionType(
(IMethod) element), PythonTypeEvaluatorUtils
.getElementModule(element));
} else if (element instanceof IType) {
return new ImportedType(new ModelClassType(
(IType) element), PythonTypeEvaluatorUtils
.getElementModule(element));
} else if (element instanceof IModule) {
return new ImportedType(new ModelModuleType(
(IModule) element), PythonTypeEvaluatorUtils
.getElementModule(element));
}
}
}
// This check for module by name returning.
else if (nde instanceof PythonImportStatement) {
PythonImportStatement importStatement = (PythonImportStatement) nde;
Expression expr = importStatement.getExpression();
List/*<Expression>*/ imports = new ArrayList/*<Expression>*/();
if (expr instanceof PythonTestListExpression) {
PythonTestListExpression testList = (PythonTestListExpression) expr;
imports = testList.getExpressions();
} else {
imports.add(expr);
}
Iterator j = imports.iterator();
while( j.hasNext() ) {
Expression imp = (Expression)j.next();
if (imp instanceof PythonImportExpression) {
PythonImportExpression impex = (PythonImportExpression) imp;
String impexName = impex.getName();
if (impexName.equals(reference)) {
IModule module = null;// this.findImport( impex
// );//findModuleFromImport(
// importStatement,
// impexName );
if (module != null) {
return new ModelModuleType(module);
}
}
// if it contains dots then check for sub name is
// equals.
if (impexName.indexOf(".")!=-1) {
if (impexName.startsWith(reference)) {
// cool lets return appropriate module.
IModule module = null;// findModuleFromImport(
// importStatement,
// reference );
if (module != null) {
return new ModelModuleType(module);
}
}
}
} else if (imp instanceof PythonImportAsExpression) {
IModule module = null;// findModuleFromImport(
// importStatement, ( (
// PythonImportAsExpression )imp
// ).getAsName( ) );
if (module != null) {
return new ModelModuleType(module);
}
}
}
}
}
return null;
}
// TODO: Need to be reimplemented.
// private Collection< IImportStatement > getImportsFromNode( ASTNode nde )
// {
//
// ASTNode parent = PythonTypeEvaluatorUtils.getModelLikeNode( fParents, nde
// );
// if( parent == null ) {
// return null;
// }
// IModelElement element = null;
// if( parent instanceof MethodDeclaration ) {
// MethodDeclaration methodParent = ( MethodDeclaration )parent;
// element = this.filerElementForAST( this.fLocalIndex.get(
// methodParent.getName( ) ) );
// }
// else if( parent instanceof TypeDeclaration ) {
// TypeDeclaration classParent = ( TypeDeclaration )parent;
// element = this.filerElementForAST( this.fLocalIndex.get(
// classParent.getName( ) ) );
// }
// else if( parent instanceof ModuleDeclaration ) {
// element = this.fModelModule;
// }
// if( element != null ) {
// Collection< IImportStatement > imports = null;
// if( element.getElementType( ) == IModelElement.Kind.Class ) {
// IType classElement = ( IType )element;
// try {
// imports = classElement.getImports( );
// }
// catch( ModelException ex ) {
// ex.printStackTrace( );
// return null;
// }
// }
// else if( element.getElementType( ) == IModelElement.Kind.Method ) {
// IMethod method = ( IMethod )element;
// try {
// imports = method.getImports( );
// }
// catch( ModelException ex ) {
// ex.printStackTrace( );
// return null;
// }
// }
// else if( element.getElementType( ) == IModelElement.SOURCE_MODULE ) {
// IModule module = ( IModule )element;
// try {
// imports = module.getImports( );
// }
// catch( ModelException ex ) {
// ex.printStackTrace( );
// return null;
// }
// }
// return imports;
// }
// return null;
// }
// TODO: Need tot be reimplemented.
// private IModule findModuleFromImport( PythonImportStatement statement,
// String name ) {
//
// Collection< IImportStatement > imports = getImportsFromNode( statement );
// if( imports != null ) {
// for( IImportStatement importStatement : imports ) {
// if( ! ( importStatement instanceof PythonModelImportStatement ) )
// continue;
//
// PythonModelImportStatement pythonModelImportStatement = (
// PythonModelImportStatement )importStatement;
//
// if( pythonModelImportStatement.getKind( ) ==
// PythonModelImportStatement.Kind.ModuleAs ) {
//
// if( !pythonModelImportStatement.getModuleName( ).equals( name ) ) {
// continue;
// }
//
// IModule module = null;
// try {
// module = pythonModelImportStatement.getModule( );
// }
// catch( ModelException ex ) {
// ex.printStackTrace( );
// }
// if( module != null ) {
// return module;
// }
// }
// }
// }
// return null;
// }
Map/*<String, List<IModelElement>>*/ makeLocalIndex(IModule module) {
Map/*<String, List<IModelElement>>*/ index = new HashMap/*<String, List<IModelElement>>*/();
List/*<IModelElement>*/ queue = new ArrayList/*<IModelElement>*/();
queue.add(module);
while (queue.size() > 0) {
IModelElement e = (IModelElement)queue.get(0);
queue.remove(0);
if (!index.containsKey(e.getElementName())) {
List/*<IModelElement>*/ list = new ArrayList/*<IModelElement>*/();
list.add(e);
index.put(e.getElementName(), list);
} else {
List/*<IModelElement>*/ list = (List)index.get(e.getElementName());
list.add(e);
}
// adding childs.
IModelElement[] childs = null;
try {
if (e instanceof IParent) {
childs = ((IParent) e).getChildren();
}
} catch (ModelException ex) {
ex.printStackTrace();
}
if (childs != null) {
// queue.addAll( childs. );
for( int i = 0; i < childs.length; ++i ) {
IModelElement element = childs[i];
queue.add(element);
}
}
}
return index;
}
/**
* Used to find model import for selected ast import.
*
* @param statement
* @return
*/
// TODO: Need to be reimplemented.
// private IModelElement findFromImport( PythonImportFromStatement
// statement, String name ) {
//
// Collection< IImportStatement > imports = getImportsFromNode( statement );
//
// if( imports != null ) {
// for( IImportStatement imp : imports ) {
//
// if( ! ( imp instanceof PythonModelImportStatement ) ) continue;
// PythonModelImportStatement importStatement = ( PythonModelImportStatement
// )imp;
//
// if( importStatement.getKind( ) ==
// PythonModelImportStatement.Kind.ElementsFrom || importStatement.getKind(
// ) == PythonModelImportStatement.Kind.WholeModule ) {
//
// PositionInformation position = statement.getPosition( );
// if( !statement.isAllImport( ) && ( importStatement.getKind( ) ==
// PythonModelImportStatement.Kind.WholeModule ) ) {
// continue;
// }
//
// if( !importStatement.getModuleName( ).equals(
// statement.getImportModuleName( ) ) ) {
// continue;
// }
// if( !importStatement.getPosition( ).equals( position ) ) {
// continue;
// }
//
// IModelElement modelElement = null;
// try {
// modelElement = importStatement.isImported( name );
// }
// catch( ModelException ex ) {
// ex.printStackTrace( );
// }
// if( modelElement != null ) {
// return modelElement;
// }
// }
// }
// }
//
// return null;
// }
/*
* private IModule findImport(PythonImportStatement statement ) { //we need
* to find selected module name in project fragment roots. IDylanProject
* project = this.fModelModule.getScriptProject(); IProjectFragment[]
* fragments; try { fragments = project.getProjectFragments(); for(
* IProjectFragment pf:fragments ) { IScriptFolder sf = pf.getScriptFolder(
* new Path( "" ) ); if( sf != null && sf.exists() ) { ISourceModule sm =
* sf.getSourceModule( statement.get + ".py" ); if( sm != null &&
* sm.exists() ) { //lets find element with selected name. IModelElement[]
* elements = sm.getChildren(); for( IModelElement element: elements ) { if(
* element.getElementName().equals( reference ) ) { return element; } } } } } }
* catch( ModelException me ) { me.printStackTrace(); }
*
* IResource ur; try { ur = this.fModelModule.getUnderlyingResource(); if(
* ur.getType() == IResource.FOLDER ) { IScriptFolder sf =
* (IScriptFolder)ur; ISourceModule sm = sf.getSourceModule(
* statement.getImportModuleName() + ".py" ); if( sm != null && sm.exists() ) { //
* lets find element with selected name. IModelElement[] elements =
* sm.getChildren(); for( IModelElement element: elements ) { if(
* element.getElementName().equals( reference ) ) { return element; } } } }
* } catch( ModelException me ) { me.printStackTrace(); }
*
* // Secondly lets search for module in current folder. return null; }
*/
private IModelElement filerElementForAST(List/*<IModelElement>*/ elements) {
if (elements.size() == 1) {
return (IModelElement)elements.get(0);
} else if (elements.size() > 0) { // TODO: Add filtration here.
return (IModelElement)elements.get(0);
}
return null;
}
/**
* TODO: Add building here.
*
* @param reference
* @return
*/
private IEvaluatedType evaluateBuildin(String reference) {
if (PythonBuiltIns.isBuiltin(reference)) {
unknownSimpleReferenceNotFoundAtEndBuiltin += 1;
}
return null;
}
private IEvaluatedType checkListForReference(String reference,
List/*<ASTNode>*/ nodes, ASTNode scope) {
Iterator i = nodes.iterator();
while( i.hasNext()) {
ASTNode node = (ASTNode)i.next();
if (node instanceof Assignment) {
Assignment assignment = (Assignment) node;
Statement left = assignment.getLeft();
Statement right = assignment.getRight();
if (left instanceof SimpleReference) {
if (((SimpleReference) left).getName().equals(reference)) {
if (right != null) {
return evaluateASTNode(right, scope);
} else {
return new SimpleType(SimpleType.TYPE_NONE);
}
}
} else if (left instanceof ExtendedVariableReference) {
ExtendedVariableReference ref = (ExtendedVariableReference) left;
if (ref.isDot(0) && ref.getExpressionCount() == 2) {
Expression first = ref.getExpression(0);
Expression second = ref.getExpression(1);
// TODO: Replace self with appropriate first argument of
// function.
if (first instanceof VariableReference
&& ((VariableReference) first).getName()
.equals(SELF_CONSTANT)) {
if (second instanceof VariableReference
&& ((VariableReference) second).getName()
.equals(reference)) {
if (right != null) {
return evaluateASTNode(right, scope);
} else {
return new SimpleType(SimpleType.TYPE_NONE);
}
}
}
}
}
}
if (node instanceof MethodDeclaration) {
MethodDeclaration method = (MethodDeclaration) node;
if (method.getName().equals(reference)) {
return new FunctionType(this.fModule,
(MethodDeclaration) node);
}
}
if (node instanceof TypeDeclaration) {
TypeDeclaration method = (TypeDeclaration) node;
if (method.getName().equals(reference)) {
return new OldClassType(this.fModule, (TypeDeclaration) node);
}
}
}
return null;
}
}