Skip to content

Commit

Permalink
JvmGenericTypeValidator with cycle in hierarchy
Browse files Browse the repository at this point in the history
  • Loading branch information
LorenzoBettini committed Dec 18, 2023
1 parent 2b19d66 commit 7c9717b
Show file tree
Hide file tree
Showing 5 changed files with 99 additions and 30 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import static org.eclipse.xtend.core.xtend.XtendPackage.Literals.*;
import static org.eclipse.xtext.xbase.XbasePackage.Literals.*;
import static org.eclipse.xtext.xbase.validation.IssueCodes.*;
import static org.eclipse.xtext.xbase.validation.IssueCodes.CYCLIC_INHERITANCE;
import static org.eclipse.xtext.xtype.XtypePackage.Literals.*;

import java.util.Iterator;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,6 @@ private IssueCodes() {
public static final String OVERRIDE_REDUCES_VISIBILITY = ISSUE_CODE_PREFIX + "override_reduces_visibility";
public static final String MISSING_SYNCHRONIZED = ISSUE_CODE_PREFIX + "missing_synchronized";
public static final String INCOMPATIBLE_THROWS_CLAUSE = ISSUE_CODE_PREFIX + "incompatible_throws_clause";
public static final String CYCLIC_INHERITANCE = ISSUE_CODE_PREFIX + "cyclic_inheritance";

public static final String UNUSED_PRIVATE_MEMBER = ISSUE_CODE_PREFIX + "unused_private_member";
public static final String FIELD_NOT_INITIALIZED = ISSUE_CODE_PREFIX + "field_not_initialized";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@
import org.eclipse.xtext.xbase.typesystem.util.RecursionGuard;
import org.eclipse.xtext.xbase.validation.ImplicitReturnFinder;
import org.eclipse.xtext.xbase.validation.ImplicitReturnFinder.Acceptor;
import org.eclipse.xtext.xbase.validation.JvmGenericTypeValidator;
import org.eclipse.xtext.xbase.validation.ProxyAwareUIStrings;
import org.eclipse.xtext.xbase.validation.UIStrings;
import org.eclipse.xtext.xtype.XComputedTypeReference;
Expand All @@ -166,7 +167,7 @@
* @author Holger Schill
* @author Stephane Galland
*/
@ComposedChecks(validators = { AnnotationValidation.class })
@ComposedChecks(validators = { AnnotationValidation.class, JvmGenericTypeValidator.class })
public class XtendValidator extends XbaseWithAnnotationsValidator {

@Inject
Expand Down Expand Up @@ -667,11 +668,6 @@ public void checkSuperTypes(XtendClass xtendClass) {
}
checkWildcardSupertype(xtendClass, implementedType, XTEND_CLASS__IMPLEMENTS, i);
}
JvmGenericType inferredType = associations.getInferredType(xtendClass);
if (inferredType != null && hasCycleInHierarchy(inferredType, Sets.<JvmGenericType> newHashSet())) {
error("The inheritance hierarchy of " + notNull(xtendClass.getName()) + " contains cycles",
XTEND_TYPE_DECLARATION__NAME, CYCLIC_INHERITANCE);
}
}

@Check
Expand All @@ -683,11 +679,6 @@ public void checkSuperTypes(XtendInterface xtendInterface) {
}
checkWildcardSupertype(xtendInterface, extendedType, XTEND_INTERFACE__EXTENDS, i);
}
JvmGenericType inferredType = associations.getInferredType(xtendInterface);
if(inferredType != null && hasCycleInHierarchy(inferredType, Sets.<JvmGenericType> newHashSet())) {
error("The inheritance hierarchy of " + notNull(xtendInterface.getName()) + " contains cycles",
XTEND_TYPE_DECLARATION__NAME, CYCLIC_INHERITANCE);
}
}

protected boolean isAnnotation(JvmType jvmType) {
Expand Down Expand Up @@ -750,24 +741,6 @@ else if (typeRef instanceof JvmParameterizedTypeReference) {
return false;
}

protected boolean hasCycleInHierarchy(JvmGenericType type, Set<JvmGenericType> processedSuperTypes) {
JvmDeclaredType container = type;
do {
if (processedSuperTypes.contains(container))
return true;
container = container.getDeclaringType();
} while (container != null);
processedSuperTypes.add(type);
for (JvmTypeReference superTypeRef : type.getSuperTypes()) {
if (superTypeRef.getType() instanceof JvmGenericType) {
if (hasCycleInHierarchy((JvmGenericType) superTypeRef.getType(), processedSuperTypes))
return true;
}
}
processedSuperTypes.remove(type);
return false;
}

@Check
public void checkDuplicateAndOverriddenFunctions(XtendTypeDeclaration xtendType) {
final JvmDeclaredType inferredType = associations.getInferredType(xtendType);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,11 @@ public class IssueCodes extends org.eclipse.xtext.validation.IssueCodes {
*/
public static final String INVALID_TRY_RESOURCE_TYPE = ISSUE_CODE_PREFIX + "invalid_try_resource_type";

/**
* @since 2.34
*/
public static final String CYCLIC_INHERITANCE = ISSUE_CODE_PREFIX + "cyclic_inheritance";

private IssueCodes() {
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
/*******************************************************************************
* Copyright (c) 2023 itemis AG (http://www.itemis.eu) and others.
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* SPDX-License-Identifier: EPL-2.0
*******************************************************************************/
package org.eclipse.xtext.xbase.validation;

import static org.eclipse.xtext.util.Strings.*;
import static org.eclipse.xtext.xbase.validation.IssueCodes.*;

import java.util.HashSet;
import java.util.Set;

import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.xtext.common.types.JvmDeclaredType;
import org.eclipse.xtext.common.types.JvmGenericType;
import org.eclipse.xtext.common.types.JvmTypeReference;
import org.eclipse.xtext.validation.AbstractDeclarativeValidator;
import org.eclipse.xtext.validation.Check;
import org.eclipse.xtext.validation.EValidatorRegistrar;
import org.eclipse.xtext.xbase.jvmmodel.IJvmModelAssociations;

import com.google.inject.Inject;

/**
* @author Lorenzo Bettini - Initial contribution and API
* @author Sebastian Zarnekow - Author of the original Java code in XtendValidator extracted here.
*/
public class JvmGenericTypeValidator extends AbstractDeclarativeValidator {
@Inject
private IJvmModelAssociations associations;

@Override
public void register(EValidatorRegistrar registrar) {
// library validator is not registered for a specific language
}

@Check
public void interceptRoot(EObject o) {
var contents = o.eResource().getContents();
if (contents.get(0) != o)
return;
contents.stream()
.filter(e -> !associations.getSourceElements(e).isEmpty())
.filter(JvmGenericType.class::isInstance)
.map(JvmGenericType.class::cast)
.forEach(this::checkJvmGenericType);
}

protected void checkJvmGenericType(JvmGenericType type) {
checkSuperTypes(type);
}

protected void checkSuperTypes(JvmGenericType type) {
var primarySourceElement = associations.getPrimarySourceElement(type);
if (primarySourceElement != null && hasCycleInHierarchy(type, new HashSet<>())) {
error("The inheritance hierarchy of " + notNull(type.getSimpleName()) + " contains cycles",
primarySourceElement,
getFeatureForIssue(primarySourceElement), CYCLIC_INHERITANCE);
}
}

private boolean hasCycleInHierarchy(JvmGenericType type, Set<JvmGenericType> processedSuperTypes) {
JvmDeclaredType container = type;
do {
if (processedSuperTypes.contains(container))
return true;
container = container.getDeclaringType();
} while (container != null);
processedSuperTypes.add(type);
for (JvmTypeReference superTypeRef : type.getSuperTypes()) {
if (superTypeRef.getType() instanceof JvmGenericType) {
if (hasCycleInHierarchy((JvmGenericType) superTypeRef.getType(), processedSuperTypes))
return true;
}
}
processedSuperTypes.remove(type);
return false;
}

/**
* The default implementation returns the feature "name".
*/
protected EStructuralFeature getFeatureForIssue(EObject object) {
return object.eClass().getEStructuralFeature("name");
}
}

0 comments on commit 7c9717b

Please sign in to comment.