package org.xtest.scoping; import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Set; import org.eclipse.emf.common.util.EList; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.EReference; import org.eclipse.emf.ecore.resource.Resource; import org.eclipse.xtend.core.scoping.StaticallyImportedFeaturesProvider; import org.eclipse.xtend.core.xtend.XtendFunction; import org.eclipse.xtend.core.xtend.XtendParameter; import org.eclipse.xtext.common.types.JvmDeclaredType; import org.eclipse.xtext.common.types.JvmEnumerationLiteral; import org.eclipse.xtext.common.types.JvmEnumerationType; import org.eclipse.xtext.common.types.JvmFormalParameter; import org.eclipse.xtext.common.types.JvmIdentifiableElement; import org.eclipse.xtext.common.types.JvmOperation; import org.eclipse.xtext.common.types.JvmTypeReference; import org.eclipse.xtext.common.types.util.TypeReferences; import org.eclipse.xtext.naming.QualifiedName; import org.eclipse.xtext.resource.EObjectDescription; import org.eclipse.xtext.resource.IEObjectDescription; import org.eclipse.xtext.scoping.IScope; import org.eclipse.xtext.scoping.impl.FilteringScope; import org.eclipse.xtext.scoping.impl.MapBasedScope; import org.eclipse.xtext.util.IAcceptor; import org.eclipse.xtext.xbase.XAssignment; import org.eclipse.xtext.xbase.XBlockExpression; import org.eclipse.xtext.xbase.XClosure; import org.eclipse.xtext.xbase.XExpression; import org.eclipse.xtext.xbase.jvmmodel.ILogicalContainerProvider; import org.eclipse.xtext.xbase.scoping.LocalVariableScopeContext; import org.eclipse.xtext.xbase.scoping.XbaseScopeProvider; import org.eclipse.xtext.xbase.scoping.featurecalls.IFeaturesForTypeProvider; import org.eclipse.xtext.xbase.scoping.featurecalls.IJvmFeatureDescriptionProvider; import org.eclipse.xtext.xbase.scoping.featurecalls.IValidatedEObjectDescription; import org.eclipse.xtext.xbase.scoping.featurecalls.JvmFeatureScope; import org.eclipse.xtext.xbase.scoping.featurecalls.LocalVarDescription; import org.xtest.jvmmodel.XtestJvmModelAssociator; import org.xtest.preferences.RuntimePref; import org.xtest.xTest.XMethodDef; import org.xtest.xTest.XMethodDefExpression; import org.xtest.xTest.XTestPackage; import com.google.common.base.Predicate; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.inject.Inject; import com.google.inject.Provider; /** * Custom scope provider for Xtest * * Portions borrowed from XtendScopeProvider * * @author Michael Barry */ @SuppressWarnings("restriction") public class XTestScopeProvider extends XbaseScopeProvider { private static final int IMPORTED_STATIC_FEATURE_PRIORITY = 50; private static final int STATIC_EXTENSION_PRIORITY_OFFSET = 220; @Inject private XtestJvmModelAssociator associations; @Inject private ILogicalContainerProvider logicalContainerProvider; @Inject private Provider<StaticallyImportedFeaturesProvider> staticallyImportedFeaturesProvider; @Inject private TypeReferences typeRefs; @Override public IScope createImplicitFeatureCallScope(EObject call, Resource resource, IScope parent, IScope localVariableScope) { // Same as XbaseScopeProvider.createImplicitFeatureCallScope... JvmFeatureScopeAcceptor featureScopeDescriptions = new JvmFeatureScopeAcceptor(); addFeatureCallScopes(call, localVariableScope, featureScopeDescriptions); JvmDeclaredType contextType = getContextType(call); IAcceptor<IJvmFeatureDescriptionProvider> acceptorWithoutContext = featureScopeDescriptions .curry(null, call); addStaticFeatureDescriptionProviders(resource, contextType, acceptorWithoutContext); if (contextType != null) { IAcceptor<IJvmFeatureDescriptionProvider> acceptorWithContext = featureScopeDescriptions .curry(typeRefs.createTypeRef(contextType), call); addFeatureDescriptionProviders(contextType, null, null, null, getImplicitStaticFeaturePriority(), false, acceptorWithContext); } // ... except adding in local method scoping addLocalMethodScope(contextType, localVariableScope, acceptorWithoutContext); IScope result = featureScopeDescriptions.createScope(parent); return result; } @Override public IScope createSimpleFeatureCallScope(EObject context, EReference reference, Resource resource, boolean includeCurrentBlock, int idx) { // Now that the actual feature call has been added to the scope, remove the local variable // holding the method signature for local scoping IScope createSimpleFeatureCallScope = super.createSimpleFeatureCallScope(context, reference, resource, includeCurrentBlock, idx); return filterOutLocalMethods(createSimpleFeatureCallScope); } @Override public IScope getScope(EObject context, EReference reference) { IScope scope; if (reference == XTestPackage.Literals.FILE_PARAM__FEATURE) { List<IEObjectDescription> descriptions = Lists.newArrayList(); Map<String, RuntimePref> map = Maps.newTreeMap(); for (RuntimePref pref : RuntimePref.values()) { map.put(pref.toString(), pref); } JvmTypeReference typeForName = typeRefs.getTypeForName(RuntimePref.class, context); if (typeForName != null && typeForName.getType() instanceof JvmEnumerationType) { EList<JvmEnumerationLiteral> literals = ((JvmEnumerationType) typeForName.getType()) .getLiterals(); for (JvmEnumerationLiteral literal : literals) { String simpleName = literal.getSimpleName(); RuntimePref runtimePref = map.get(simpleName); if (runtimePref != null) { String id = runtimePref.getId(); IEObjectDescription create = EObjectDescription.create(id, literal); descriptions.add(create); } } } scope = MapBasedScope.createScope(MapBasedScope.NULLSCOPE, descriptions); } else { scope = super.getScope(context, reference); } return scope; } @Override protected void addFeatureDescriptionProviders(Resource resource, JvmDeclaredType contextType, XExpression implicitReceiver, XExpression implicitArgument, int priority, IAcceptor<IJvmFeatureDescriptionProvider> acceptor) { addFeatureDescriptionProviders(resource, contextType, implicitReceiver, implicitArgument, priority, acceptor, null); } @Override protected void addFeatureDescriptionProvidersForAssignment(Resource resource, JvmDeclaredType contextType, XExpression implicitReceiver, XExpression implicitArgument, int priority, IAcceptor<IJvmFeatureDescriptionProvider> acceptor) { addFeatureDescriptionProvidersForAssignment(resource, contextType, implicitReceiver, implicitArgument, priority, acceptor, null); } @Override protected void addStaticFeatureDescriptionProviders(Resource resource, JvmDeclaredType contextType, IAcceptor<IJvmFeatureDescriptionProvider> acceptor) { super.addStaticFeatureDescriptionProviders(resource, contextType, acceptor); StaticallyImportedFeaturesProvider staticProvider = staticallyImportedFeaturesProvider .get(); staticProvider.setResourceContext(resource); staticProvider.setExtensionProvider(false); addFeatureDescriptionProviders(contextType, staticProvider, null, null, IMPORTED_STATIC_FEATURE_PRIORITY, true, acceptor); } @Override protected IScope createFeatureScopeForTypeRef(JvmTypeReference declaringType, EObject expression, XExpression implicitReceiver, IScope parent) { parent = super.createFeatureScopeForTypeRef(declaringType, expression, implicitReceiver, parent); // Add locally-defined extensions methods to feature scope JvmFeatureScopeAcceptor featureScopeDescriptions = new JvmFeatureScopeAcceptor(); addLocalMethodExtensionScope(declaringType, expression, implicitReceiver, parent, featureScopeDescriptions); parent = featureScopeDescriptions.createScope(parent); return parent; } @Override protected LocalVariableScopeContext createLocalVariableScopeContext(final EObject context, EReference reference, boolean includeCurrentBlock, int idx) { return new LocalVariableScopeContextAllowsMethods(context, reference, includeCurrentBlock, idx, false, logicalContainerProvider); } @Override protected IScope createLocalVarScope(IScope parentScope, LocalVariableScopeContext scopeContext) { if (scopeContext == null || scopeContext.getContext() == null) { return parentScope; } EObject context = scopeContext.getContext(); parentScope = super.createLocalVarScope(parentScope, scopeContext); if (context instanceof XMethodDef) { XMethodDef methDef = (XMethodDef) context; parentScope = createLocalVarScopeForMethodDef(methDef, parentScope); } return parentScope; } @Override protected IScope createLocalVarScopeForBlock(XBlockExpression block, int indexOfContextExpressionInBlock, boolean referredFromClosure, IScope parentScope) { parentScope = super.createLocalVarScopeForBlock(block, indexOfContextExpressionInBlock, referredFromClosure, parentScope); List<IValidatedEObjectDescription> descriptions = Lists.newArrayList(); for (int i = 0; i <= indexOfContextExpressionInBlock && i < block.getExpressions().size(); i++) { XExpression expression = block.getExpressions().get(i); if (expression instanceof XMethodDefExpression) { XtendFunction methDef = ((XMethodDefExpression) expression).getMethod(); Set<EObject> jvmElements2 = associations.getJvmElements(methDef); Iterable<JvmOperation> jvmElements = Iterables.filter(jvmElements2, JvmOperation.class); if (methDef.getName() != null) { for (JvmOperation op : jvmElements) { if (!op.isStatic()) { // Add non-static method calls to local var scope descriptions.add(new MethodScopingLocalVarDescription(op)); } } } } } if (descriptions.isEmpty()) { return parentScope; } return new ShadowedJvmFeatureScope(parentScope, "XMethodDef", descriptions); } /** * Adds function parameters to the scope of a function's body * * @param def * The method def * @param parentScope * The parent scope * @return Scope for the function's body containing the method's parameters */ protected IScope createLocalVarScopeForMethodDef(XMethodDef def, IScope parentScope) { List<IValidatedEObjectDescription> descriptions = Lists.newArrayList(); if (def.isStatic()) { parentScope = new FilteringScope(parentScope, new Predicate<IEObjectDescription>() { @Override public boolean apply(IEObjectDescription input) { return !(input instanceof LocalVarDescription); } }); } for (XtendParameter xtendParam : def.getParameters()) { Set<EObject> jvmElements = associations.getJvmElements(xtendParam); Iterable<JvmFormalParameter> params = Iterables.filter(jvmElements, JvmFormalParameter.class); for (JvmFormalParameter param : params) { String name = param.getName(); if (name != null) { QualifiedName create = QualifiedName.create(name); IValidatedEObjectDescription desc; desc = new LocalVarDescription(create, param); descriptions.add(desc); } } } return new JvmFeatureScope(parentScope, "XMethodDef", descriptions); } @Override protected IScope createTypeScope(EObject context, EReference reference) { if (context.eContainer() == null) { return super.createTypeScope(context, reference); } IScope parentScope = createTypeScope(context.eContainer(), reference); if (context instanceof XMethodDef && ((XMethodDef) context).isStatic()) { parentScope = filterOutParentInferredScope(parentScope); } if (context instanceof XMethodDef) { XMethodDef def = (XMethodDef) context; JvmOperation op = associations.getJvmOperation(def); parentScope = createTypeScope(op, reference, parentScope); } return parentScope; } @Override protected IScope createTypeScope(JvmIdentifiableElement context, EReference reference, IScope parentScope) { IScope result = parentScope; if (context instanceof JvmOperation || !associations.hasAssociation(context)) { result = super.createTypeScope(context, reference, parentScope); } return result; } private void addFeatureDescriptionProviders(Resource resource, JvmDeclaredType contextType, XExpression implicitReceiver, XExpression implicitArgument, int priority, IAcceptor<IJvmFeatureDescriptionProvider> acceptor, IScope parent) { // Adds local extension methods to member feature call scope super.addFeatureDescriptionProviders(resource, contextType, implicitReceiver, implicitArgument, priority, acceptor); if (implicitReceiver == null || implicitArgument != null) { final StaticallyImportedFeaturesProvider staticProvider = staticallyImportedFeaturesProvider .get(); staticProvider.setResourceContext(resource); staticProvider.setExtensionProvider(true); if (implicitArgument != null) { // use the implicit argument as implicit receiver SimpleAcceptor casted = (SimpleAcceptor) acceptor; JvmTypeReference implicitArgumentType = getTypeProvider().getType(implicitArgument, true); IAcceptor<IJvmFeatureDescriptionProvider> myAcceptor = casted.getParent().curry( implicitArgumentType, casted.getExpression()); addFeatureDescriptionProviders(contextType, staticProvider, implicitArgument, null, priority + STATIC_EXTENSION_PRIORITY_OFFSET, true, myAcceptor); if (parent != null) { IFeaturesForTypeProvider implicitMethodsProvider = new LocalMethodScopeFeaturesForTypeProvider( parent); addFeatureDescriptionProviders(contextType, implicitMethodsProvider, implicitArgument, null, priority + STATIC_EXTENSION_PRIORITY_OFFSET, false, myAcceptor); } } else { addFeatureDescriptionProviders(contextType, staticProvider, implicitReceiver, implicitArgument, priority + STATIC_EXTENSION_PRIORITY_OFFSET, true, acceptor); if (parent != null) { IFeaturesForTypeProvider implicitMethodsProvider = new LocalMethodScopeFeaturesForTypeProvider( parent); addFeatureDescriptionProviders(contextType, implicitMethodsProvider, implicitReceiver, implicitArgument, priority + STATIC_EXTENSION_PRIORITY_OFFSET, false, acceptor); } } } } private void addFeatureDescriptionProvidersForAssignment(Resource resource, JvmDeclaredType contextType, XExpression implicitReceiver, XExpression implicitArgument, int priority, IAcceptor<IJvmFeatureDescriptionProvider> acceptor, IScope parent) { // Adds local extension methods to member feature call scope super.addFeatureDescriptionProvidersForAssignment(resource, contextType, implicitReceiver, implicitArgument, priority, acceptor); if (implicitReceiver == null || implicitArgument != null) { final StaticallyImportedFeaturesProvider staticProvider = staticallyImportedFeaturesProvider .get(); staticProvider.setResourceContext(resource); staticProvider.setExtensionProvider(true); if (implicitArgument != null) { // use the implicit argument as implicit receiver SimpleAcceptor casted = (SimpleAcceptor) acceptor; JvmTypeReference implicitArgumentType = getTypeProvider().getType(implicitArgument, true); IAcceptor<IJvmFeatureDescriptionProvider> myAcceptor = casted.getParent().curry( implicitArgumentType, casted.getExpression()); addFeatureDescriptionProvidersForAssignment(contextType, staticProvider, implicitArgument, null, priority + STATIC_EXTENSION_PRIORITY_OFFSET, true, myAcceptor); if (parent != null) { IFeaturesForTypeProvider implicitMethodsProvider = new LocalMethodScopeFeaturesForTypeProvider( parent); addFeatureDescriptionProvidersForAssignment(contextType, implicitMethodsProvider, implicitArgument, null, priority + STATIC_EXTENSION_PRIORITY_OFFSET, false, myAcceptor); } } else { addFeatureDescriptionProvidersForAssignment(contextType, staticProvider, implicitReceiver, implicitArgument, priority + STATIC_EXTENSION_PRIORITY_OFFSET, true, acceptor); if (parent != null) { IFeaturesForTypeProvider implicitMethodsProvider = new LocalMethodScopeFeaturesForTypeProvider( parent); addFeatureDescriptionProvidersForAssignment(contextType, implicitMethodsProvider, implicitReceiver, implicitArgument, priority + STATIC_EXTENSION_PRIORITY_OFFSET, false, acceptor); } } } } private void addLocalMethodExtensionScope(JvmTypeReference receiverType, EObject expression, XExpression implicitReceiver, IScope parent, JvmFeatureScopeAcceptor acceptor) { JvmDeclaredType contextType = getContextType(expression); IAcceptor<IJvmFeatureDescriptionProvider> curried = acceptor .curry(receiverType, expression); LocalVariableScopeContext scopeContext = createLocalVariableScopeContext(expression, null, false, -1); IScope localVariableScope = createLocalVarScope(IScope.NULLSCOPE, scopeContext); if (expression instanceof XAssignment) { addFeatureDescriptionProvidersForAssignment(expression.eResource(), contextType, implicitReceiver, null, getDefaultPriority(), curried, localVariableScope); } else { addFeatureDescriptionProviders(expression.eResource(), contextType, implicitReceiver, null, getDefaultPriority(), curried, localVariableScope); } } private void addLocalMethodScope(JvmDeclaredType contextType, IScope localVariableScope, IAcceptor<IJvmFeatureDescriptionProvider> acceptor) { IFeaturesForTypeProvider implicitMethodsProvider = new LocalMethodScopeFeaturesForTypeProvider( localVariableScope); addFeatureDescriptionProviders(contextType, implicitMethodsProvider, null, null, getImplicitStaticFeaturePriority(), true, acceptor); } private FilteringScope filterOutLocalMethods(IScope parentScope) { return new FilteringScope(parentScope, new Predicate<IEObjectDescription>() { @Override public boolean apply(IEObjectDescription input) { return !(input instanceof MethodScopingLocalVarDescription); } }); } private FilteringScope filterOutParentInferredScope(IScope parentScope) { return new FilteringScope(parentScope, new Predicate<IEObjectDescription>() { @Override public boolean apply(IEObjectDescription input) { EObject eObjectOrProxy = input.getEObjectOrProxy(); return !associations.hasAssociation(eObjectOrProxy); } }); } private static class LocalVariableScopeContextAllowsMethods extends LocalVariableScopeContext { private final ILogicalContainerProvider expressionContext; protected LocalVariableScopeContextAllowsMethods(EObject context, EReference reference, boolean includeCurrentBlock, int idx, boolean referredFromClosure, ILogicalContainerProvider expressionContext) { super(context, reference, includeCurrentBlock, idx, referredFromClosure, expressionContext); this.expressionContext = expressionContext; } @Override public LocalVariableScopeContext spawnForContainer() { if (getLogicalOrRealContainer() instanceof XMethodDef || getContext() instanceof XClosure) { return new LocalVariableScopeContextAllowsMethods(getLogicalOrRealContainer(), getReference(), false, -1, true, expressionContext); } return new LocalVariableScopeContextAllowsMethods(getLogicalOrRealContainer(), getReference(), false, -1, isReferredFromClosure(), expressionContext); } } private static class MethodScopingLocalVarDescription extends LocalVarDescription { public MethodScopingLocalVarDescription(JvmOperation op) { super(QualifiedName.create(op.getSimpleName()), op); } } /** * JVM Feature scope that is shado * * @author Michael Barry */ private static class ShadowedJvmFeatureScope extends JvmFeatureScope { private ShadowedJvmFeatureScope(IScope parent, String scopeDescription, Collection<? extends IValidatedEObjectDescription> descriptions) { super(parent, scopeDescription, descriptions); } @Override protected boolean isShadowed(IEObjectDescription fromParent) { return false; } @Override protected boolean isShadowedBy(IEObjectDescription fromParent, Iterable<IEObjectDescription> localElements) { return false; } } }