| /******************************************************************************* |
| * Copyright (c) 2005, 2016 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.ruby.typeinference.evaluators; |
| |
| import org.eclipse.core.resources.IResource; |
| import org.eclipse.dltk.ast.ASTNode; |
| import org.eclipse.dltk.ast.declarations.ModuleDeclaration; |
| import org.eclipse.dltk.ast.expressions.CallExpression; |
| import org.eclipse.dltk.core.DLTKCore; |
| import org.eclipse.dltk.core.IModelElement; |
| import org.eclipse.dltk.core.ISourceModule; |
| import org.eclipse.dltk.core.mixin.MixinModel; |
| import org.eclipse.dltk.ruby.internal.parser.mixin.RubyMixinClass; |
| import org.eclipse.dltk.ruby.internal.parser.mixin.RubyMixinMethod; |
| import org.eclipse.dltk.ruby.internal.parsers.jruby.ASTUtils; |
| import org.eclipse.dltk.ruby.typeinference.RubyClassType; |
| import org.eclipse.dltk.ruby.typeinference.RubyMethodReference; |
| import org.eclipse.dltk.ruby.typeinference.RubyTypeInferencingUtils; |
| import org.eclipse.dltk.ti.BasicContext; |
| import org.eclipse.dltk.ti.GoalState; |
| import org.eclipse.dltk.ti.goals.ExpressionTypeGoal; |
| import org.eclipse.dltk.ti.goals.IGoal; |
| import org.eclipse.dltk.ti.goals.MethodCallVerificationGoal; |
| import org.eclipse.dltk.ti.goals.PossiblePosition; |
| import org.eclipse.dltk.ti.types.IEvaluatedType; |
| |
| public class MethodCallVerificator extends RubyMixinGoalEvaluator { |
| |
| private static final int INIT = 0; |
| private static final int RECEIVER_WAIT = 1; |
| |
| private int state = INIT; |
| private RubyMethodReference result = null; |
| private IEvaluatedType receiverType; |
| private PossiblePosition position; |
| private boolean topLevelMethod; |
| |
| public MethodCallVerificator(IGoal goal) { |
| super(goal); |
| } |
| |
| private MethodCallVerificationGoal getTypedGoal() { |
| return (MethodCallVerificationGoal) this.getGoal(); |
| } |
| |
| @Override |
| public IGoal[] init() { |
| topLevelMethod = false; |
| MethodCallVerificationGoal goal2 = getTypedGoal(); |
| position = goal2.getPosition(); |
| IResource resource = position.getResource(); |
| |
| IModelElement element = DLTKCore.create(resource); |
| if (element instanceof ISourceModule) { |
| ModuleDeclaration decl = ASTUtils.getAST((ISourceModule) element); |
| ASTNode node = position.getNode(); |
| if (node == null) { |
| node = ASTUtils.findMinimalNode(decl, position.getOffset(), |
| position.getOffset() + position.getLength() - 1); |
| } |
| if (node instanceof CallExpression) { |
| receiverType = null; |
| CallExpression call = (CallExpression) node; |
| ASTNode receiver = call.getReceiver(); |
| if (receiver != null) { |
| ExpressionTypeGoal rgoal = new ExpressionTypeGoal( |
| new BasicContext((ISourceModule) element, decl), |
| receiver); |
| state = RECEIVER_WAIT; |
| return new IGoal[] { rgoal }; |
| } else { |
| // ASTNode[] nodes = |
| // RubyTypeInferencingUtils.getAllStaticScopes(decl, |
| // node.sourceStart()); |
| receiverType = RubyTypeInferencingUtils.determineSelfClass( |
| mixinModel, (ISourceModule) element, decl, node |
| .sourceStart()); |
| } |
| } |
| } |
| |
| // |
| return null; |
| } |
| |
| @Override |
| public Object produceResult() { // TODO: add partial results support |
| if (!(receiverType instanceof RubyClassType)) |
| return null; |
| |
| MethodCallVerificationGoal goal2 = getTypedGoal(); |
| RubyClassType type = (RubyClassType) receiverType; |
| String parentModelKey = goal2.getGoal().getParentModelKey(); |
| String name = goal2.getGoal().getName(); |
| String requiredKey = ((parentModelKey != null) ? (parentModelKey + MixinModel.SEPARATOR) |
| : "") + name; //$NON-NLS-1$ |
| RubyMixinClass rclass = mixinModel.createRubyClass(type); |
| RubyMixinMethod method = null; |
| if (topLevelMethod) { |
| method = (RubyMixinMethod) mixinModel.createRubyElement(name); |
| } else if (rclass != null) { |
| method = rclass.getMethod(name); |
| } |
| if (method != null) { |
| String key = method.getKey(); |
| if (key.equals(requiredKey) |
| || (parentModelKey.equals("Object") && key.equals(name))) { //$NON-NLS-1$ |
| result = new RubyMethodReference(name, parentModelKey, |
| position, RubyMethodReference.ACCURATE); |
| if (position.getNode() instanceof CallExpression) { |
| result.setNode((CallExpression) position.getNode()); |
| } |
| } |
| } |
| |
| return result; |
| } |
| |
| @Override |
| public IGoal[] subGoalDone(IGoal subgoal, Object result, GoalState state) { |
| if (this.state == RECEIVER_WAIT) { |
| receiverType = (IEvaluatedType) result; |
| } |
| return null; |
| } |
| |
| } |