/* * $Id$ * * SARL is an general-purpose agent programming language. * More details on http://www.sarl.io * * Copyright (C) 2014-2017 the original authors or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package io.sarl.lang.bugfixes.bug621; import static org.eclipse.xtend.core.validation.IssueCodes.CONFLICTING_DEFAULT_METHODS; import static org.eclipse.xtend.core.validation.IssueCodes.DUPLICATE_METHOD; import java.util.List; import java.util.Set; import javax.inject.Inject; import com.google.common.collect.Lists; import com.google.common.collect.Sets; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.util.EcoreUtil; import org.eclipse.xtend.core.xtend.XtendPackage; import org.eclipse.xtend.core.xtend.XtendTypeDeclaration; import org.eclipse.xtext.common.types.JvmDeclaredType; import org.eclipse.xtext.common.types.JvmGenericType; import org.eclipse.xtext.common.types.JvmType; import org.eclipse.xtext.common.types.JvmTypeReference; import org.eclipse.xtext.xbase.typesystem.override.ConflictingDefaultOperation; import org.eclipse.xtext.xbase.typesystem.override.IResolvedOperation; import org.eclipse.xtext.xbase.typesystem.override.ResolvedFeatures; import org.eclipse.xtext.xbase.typesystem.util.ContextualVisibilityHelper; import org.eclipse.xtext.xbase.typesystem.util.IVisibilityHelper; import org.eclipse.xtext.xbase.typesystem.util.RecursionGuard; import io.sarl.lang.validation.SARLValidator; /** * Fixing the SARL issue 621: Error on multiple function inheritance. * * <p>Issue is due to Xtend issue 191 (https://github.com/eclipse/xtext-xtend/pull/191), * and the associated PR 192 (https://github.com/eclipse/xtext-xtend/pull/192) * * @author $Author: sgalland$ * @version $FullVersion$ * @mavengroupid $GroupId$ * @mavenartifactid $ArtifactId$ * @see "https://github.com/sarl/sarl/issues/621" * @see "https://github.com/eclipse/xtext-xtend/pull/191" * @see "https://github.com/eclipse/xtext-xtend/pull/192" */ @SuppressWarnings("all") public class Bug621SARLValidator extends SARLValidator { @Inject private IVisibilityHelper visibilityHelper; private boolean contributesToConflict(JvmGenericType rootType, ConflictingDefaultOperation conflictingDefaultOperation) { Set<JvmDeclaredType> involvedInterfaces = Sets.newHashSet(); involvedInterfaces.add(conflictingDefaultOperation.getDeclaration().getDeclaringType()); for (IResolvedOperation conflictingOperation : conflictingDefaultOperation.getConflictingOperations()) { involvedInterfaces.add(conflictingOperation.getDeclaration().getDeclaringType()); } RecursionGuard<JvmDeclaredType> recursionGuard = new RecursionGuard<JvmDeclaredType>(); if (rootType.isInterface()) { int contributingCount = 0; for (JvmTypeReference typeRef : rootType.getExtendedInterfaces()) { JvmType rawType = typeRef.getType(); if (rawType instanceof JvmDeclaredType && contributesToConflict((JvmDeclaredType) rawType, involvedInterfaces, recursionGuard)) { contributingCount++; } } return contributingCount >= 2; } else { return contributesToConflict(rootType, involvedInterfaces, recursionGuard); } } private boolean contributesToConflict(JvmDeclaredType type, Set<JvmDeclaredType> involvedInterfaces, RecursionGuard<JvmDeclaredType> guard) { if (!guard.tryNext(type)) { return false; } if (involvedInterfaces.contains(type)) { return true; } for (JvmTypeReference typeRef : type.getExtendedInterfaces()) { JvmType rawType = typeRef.getType(); if (rawType instanceof JvmDeclaredType && contributesToConflict((JvmDeclaredType) rawType, involvedInterfaces, guard)) { return true; } } return false; } @Override protected void doCheckOverriddenMethods(XtendTypeDeclaration xtendType, JvmGenericType inferredType, ResolvedFeatures resolvedFeatures, Set<EObject> flaggedOperations) { List<IResolvedOperation> operationsMissingImplementation = null; boolean doCheckAbstract = !inferredType.isAbstract(); if (doCheckAbstract) { operationsMissingImplementation = Lists.newArrayList(); } IVisibilityHelper visibilityHelper = new ContextualVisibilityHelper(this.visibilityHelper, resolvedFeatures.getType()); boolean flaggedType = false; for (IResolvedOperation operation : resolvedFeatures.getAllOperations()) { JvmDeclaredType operationDeclaringType = operation.getDeclaration().getDeclaringType(); if (operationDeclaringType != inferredType) { if (operationsMissingImplementation != null && operation.getDeclaration().isAbstract()) { operationsMissingImplementation.add(operation); } if (visibilityHelper.isVisible(operation.getDeclaration())) { String erasureSignature = operation.getResolvedErasureSignature(); List<IResolvedOperation> declaredOperationsWithSameErasure = resolvedFeatures.getDeclaredOperations(erasureSignature); for (IResolvedOperation localOperation: declaredOperationsWithSameErasure) { if (!localOperation.isOverridingOrImplementing(operation.getDeclaration()).isOverridingOrImplementing()) { EObject source = findPrimarySourceElement(localOperation); if (flaggedOperations.add(source)) { if (operation.getDeclaration().isStatic() && !localOperation.getDeclaration().isStatic()) { error("The instance method " + localOperation.getSimpleSignature() + " cannot override the static method " + operation.getSimpleSignature() + " of type " + getDeclaratorName(operation.getDeclaration()) + ".", source, nameFeature(source), DUPLICATE_METHOD); } else { error("Name clash: The method " + localOperation.getSimpleSignature() + " of type " + inferredType.getSimpleName() + " has the same erasure as " + // use source with other operations parameters to avoid confusion // due to name transformations in JVM model inference operation.getSimpleSignature() + " of type " + getDeclaratorName(operation.getDeclaration()) + " but does not override it.", source, nameFeature(source), DUPLICATE_METHOD); } } } } if (operation instanceof ConflictingDefaultOperation && contributesToConflict(inferredType, (ConflictingDefaultOperation) operation) && !flaggedType) { IResolvedOperation conflictingOperation = ((ConflictingDefaultOperation) operation).getConflictingOperations().get(0); // Include the declaring class in the issue code in order to give better quick fixes String[] uris = new String[] { getDeclaratorName(operation.getDeclaration()) + "|" + EcoreUtil.getURI(operation.getDeclaration()).toString(), getDeclaratorName(conflictingOperation.getDeclaration()) + "|" + EcoreUtil.getURI(conflictingOperation.getDeclaration()).toString() }; if (!operation.getDeclaration().isAbstract() && !operation.getDeclaration().isDefault() && !conflictingOperation.getDeclaration().isAbstract() && !conflictingOperation.getDeclaration().isDefault()) { error("The type " + inferredType.getSimpleName() + " inherits multiple implementations of the method " + conflictingOperation.getSimpleSignature() + " from " + getDeclaratorName(conflictingOperation.getDeclaration()) + " and " + getDeclaratorName(operation.getDeclaration()) + ".", xtendType, XtendPackage.Literals.XTEND_TYPE_DECLARATION__NAME, CONFLICTING_DEFAULT_METHODS, uris); } else if (!operation.getDeclaration().isDefault() && !conflictingOperation.getDeclaration().isDefault()) { // At least one of the operations is non-abstract IResolvedOperation abstractOp, nonabstractOp; if (operation.getDeclaration().isAbstract()) { abstractOp = operation; nonabstractOp = conflictingOperation; } else { abstractOp = conflictingOperation; nonabstractOp = operation; } error("The non-abstract method " + nonabstractOp.getSimpleSignature() + " inherited from " + getDeclaratorName(nonabstractOp.getDeclaration()) + " conflicts with the method " + abstractOp.getSimpleSignature() + " inherited from " + getDeclaratorName(abstractOp.getDeclaration()) + ".", xtendType, XtendPackage.Literals.XTEND_TYPE_DECLARATION__NAME, CONFLICTING_DEFAULT_METHODS, uris); } flaggedType = true; } } } } if (operationsMissingImplementation != null && !operationsMissingImplementation.isEmpty() && !flaggedType) { reportMissingImplementations(xtendType, inferredType, operationsMissingImplementation); } } }