Skip to content

Commit

Permalink
checkDefaultSuperConstructor in JvmGenericTypeValidator
Browse files Browse the repository at this point in the history
  • Loading branch information
LorenzoBettini committed Feb 5, 2024
1 parent 9f58a13 commit b30d3ef
Show file tree
Hide file tree
Showing 2 changed files with 87 additions and 95 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@
import org.eclipse.emf.ecore.EPackage;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.xtend.core.jvmmodel.DispatchHelper;
import org.eclipse.xtend.core.jvmmodel.IXtendJvmAssociations;
import org.eclipse.xtend.core.richstring.RichStringProcessor;
Expand Down Expand Up @@ -64,7 +63,6 @@
import org.eclipse.xtext.common.types.JvmAnnotationType;
import org.eclipse.xtext.common.types.JvmConstructor;
import org.eclipse.xtext.common.types.JvmDeclaredType;
import org.eclipse.xtext.common.types.JvmExecutable;
import org.eclipse.xtext.common.types.JvmFeature;
import org.eclipse.xtext.common.types.JvmField;
import org.eclipse.xtext.common.types.JvmFormalParameter;
Expand Down Expand Up @@ -97,11 +95,9 @@
import org.eclipse.xtext.validation.ComposedChecks;
import org.eclipse.xtext.validation.ValidationMessageAcceptor;
import org.eclipse.xtext.xbase.XAssignment;
import org.eclipse.xtext.xbase.XBlockExpression;
import org.eclipse.xtext.xbase.XCatchClause;
import org.eclipse.xtext.xbase.XClosure;
import org.eclipse.xtext.xbase.XExpression;
import org.eclipse.xtext.xbase.XFeatureCall;
import org.eclipse.xtext.xbase.XIfExpression;
import org.eclipse.xtext.xbase.XReturnExpression;
import org.eclipse.xtext.xbase.XbasePackage;
Expand All @@ -113,8 +109,6 @@
import org.eclipse.xtext.xbase.compiler.IGeneratorConfigProvider;
import org.eclipse.xtext.xbase.compiler.JavaKeywords;
import org.eclipse.xtext.xbase.jvmmodel.IJvmModelAssociations;
import org.eclipse.xtext.xbase.jvmmodel.ILogicalContainerProvider;
import org.eclipse.xtext.xbase.jvmmodel.JvmTypeExtensions;
import org.eclipse.xtext.xbase.lib.util.ToStringBuilder;
import org.eclipse.xtext.xbase.scoping.batch.IFeatureNames;
import org.eclipse.xtext.xbase.scoping.featurecalls.OperatorMapping;
Expand Down Expand Up @@ -180,12 +174,6 @@ public class XtendValidator extends XbaseWithAnnotationsValidator {
@Inject
private UIStrings uiStrings;

@Inject
private ILogicalContainerProvider containerProvider;

@Inject
private JvmTypeExtensions typeExtensions;

@Inject
private IJvmModelAssociations jvmModelAssociations;

Expand Down Expand Up @@ -791,56 +779,6 @@ protected boolean isMorePrivateThan(JvmVisibility o1, JvmVisibility o2) {
}
}

@Check
public void checkDefaultSuperConstructor(XtendClass xtendClass) {
JvmGenericType inferredType = associations.getInferredType(xtendClass);
if (inferredType == null)
return;
Iterable<JvmConstructor> constructors = filter(inferredType.getMembers(), JvmConstructor.class);
if(inferredType.getExtendedClass() != null) {
JvmType superType = inferredType.getExtendedClass().getType();
if(superType instanceof JvmGenericType) {
Iterable<JvmConstructor> superConstructors = ((JvmGenericType) superType).getDeclaredConstructors();
for(JvmConstructor superConstructor: superConstructors) {
if(superConstructor.getParameters().isEmpty())
// there is a default super constructor. nothing more to check
return;
}
if(size(constructors) == 1 && typeExtensions.isSingleSyntheticDefaultConstructor(constructors.iterator().next())) {
List<String> issueData = newArrayList();
for(JvmConstructor superConstructor:superConstructors) {
issueData.add(EcoreUtil.getURI(superConstructor).toString());
issueData.add(doGetReadableSignature(xtendClass.getName(), superConstructor.getParameters()));
}
error("No default constructor in super type " + superType.getSimpleName() + "." +
xtendClass.getName() + " must define an explicit constructor.",
xtendClass, XTEND_TYPE_DECLARATION__NAME, MISSING_CONSTRUCTOR, toArray(issueData, String.class));
} else {
for(JvmConstructor constructor: constructors) {
XExpression expression = containerProvider.getAssociatedExpression(constructor);
if (expression instanceof XBlockExpression) {
List<XExpression> expressions = ((XBlockExpression) expression).getExpressions();
if(expressions.isEmpty() || !isDelegateConstructorCall(expressions.get(0))) {
EObject source = associations.getPrimarySourceElement(constructor);
error("No default constructor in super type " + superType.getSimpleName()
+ ". Another constructor must be invoked explicitly.",
source, null, MUST_INVOKE_SUPER_CONSTRUCTOR);
}
}
}
}
}
}
}

protected boolean isDelegateConstructorCall(XExpression expression) {
if(expression instanceof XFeatureCall) {
JvmIdentifiableElement feature = ((XFeatureCall)expression).getFeature();
return (feature != null && !feature.eIsProxy() && feature instanceof JvmConstructor);
}
return false;
}

protected boolean isInterface(JvmDeclaredType type) {
return type instanceof JvmGenericType && ((JvmGenericType) type).isInterface();
}
Expand All @@ -849,39 +787,6 @@ protected String canonicalName(JvmIdentifiableElement element) {
return (element != null) ? notNull(element.getIdentifier()) : null;
}

protected String getReadableSignature(JvmExecutable executable) {
if (executable == null)
return "null";
return doGetReadableSignature(executable.getSimpleName(), executable.getParameters());
}

protected String doGetReadableSignature(String simpleName, List<JvmFormalParameter> parameters) {
return getReadableSignature(simpleName,
Lists.transform(parameters, new Function<JvmFormalParameter, JvmTypeReference>() {
@Override
public JvmTypeReference apply(JvmFormalParameter from) {
return from.getParameterType();
}
}));
}

protected String getReadableSignature(String elementName, List<JvmTypeReference> parameterTypes) {
StringBuilder result = new StringBuilder(elementName);
result.append('(');
for (int i = 0; i < parameterTypes.size(); i++) {
if (i != 0) {
result.append(", ");
}
JvmTypeReference parameterType = parameterTypes.get(i);
if (parameterType != null)
result.append(parameterType.getSimpleName());
else
result.append("null");
}
result.append(')');
return result.toString();
}

@Check
public void checkParameterNames(XtendFunction function) {
for (int i = 0; i < function.getParameters().size(); ++i) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
package org.eclipse.xtext.xbase.validation;

import static com.google.common.collect.Iterables.*;
import static com.google.common.collect.Lists.*;
import static org.eclipse.xtext.util.Strings.*;
import static org.eclipse.xtext.xbase.validation.IssueCodes.*;

Expand All @@ -32,7 +33,9 @@
import org.eclipse.xtext.common.types.JvmExecutable;
import org.eclipse.xtext.common.types.JvmFeature;
import org.eclipse.xtext.common.types.JvmField;
import org.eclipse.xtext.common.types.JvmFormalParameter;
import org.eclipse.xtext.common.types.JvmGenericType;
import org.eclipse.xtext.common.types.JvmIdentifiableElement;
import org.eclipse.xtext.common.types.JvmMember;
import org.eclipse.xtext.common.types.JvmOperation;
import org.eclipse.xtext.common.types.JvmParameterizedTypeReference;
Expand All @@ -45,9 +48,14 @@
import org.eclipse.xtext.validation.AbstractDeclarativeValidator;
import org.eclipse.xtext.validation.Check;
import org.eclipse.xtext.validation.EValidatorRegistrar;
import org.eclipse.xtext.xbase.XBlockExpression;
import org.eclipse.xtext.xbase.XExpression;
import org.eclipse.xtext.xbase.XFeatureCall;
import org.eclipse.xtext.xbase.compiler.GeneratorConfig;
import org.eclipse.xtext.xbase.compiler.IGeneratorConfigProvider;
import org.eclipse.xtext.xbase.jvmmodel.IJvmModelAssociations;
import org.eclipse.xtext.xbase.jvmmodel.ILogicalContainerProvider;
import org.eclipse.xtext.xbase.jvmmodel.JvmTypeExtensions;
import org.eclipse.xtext.xbase.typesystem.override.ConflictingDefaultOperation;
import org.eclipse.xtext.xbase.typesystem.override.IResolvedConstructor;
import org.eclipse.xtext.xbase.typesystem.override.IResolvedExecutable;
Expand Down Expand Up @@ -84,6 +92,12 @@ public class JvmGenericTypeValidator extends AbstractDeclarativeValidator {
@Inject
private IVisibilityHelper visibilityHelper;

@Inject
private JvmTypeExtensions typeExtensions;

@Inject
private ILogicalContainerProvider containerProvider;

@Override
public void register(EValidatorRegistrar registrar) {
// library validator is not registered for a specific language
Expand All @@ -110,6 +124,8 @@ private boolean isAssociatedToSource(EObject e) {
}

protected void checkJvmGenericType(JvmGenericType type) {
var sourceType = associations.getPrimarySourceElement(type);
handleExceptionDuringValidation(() -> checkDefaultSuperConstructor(sourceType, type));
handleExceptionDuringValidation(() -> checkSuperTypes(type));
type.getDeclaredFields().forEach(field -> {
if (isAssociatedToSource(field))
Expand Down Expand Up @@ -231,6 +247,77 @@ protected EObject getSuperTypeSourceElement(JvmTypeReference extendedType) {
return associated;
}

protected void checkDefaultSuperConstructor(EObject sourceType, JvmGenericType type) {
Iterable<JvmConstructor> constructors = filter(type.getMembers(), JvmConstructor.class);
if(type.getExtendedClass() != null) {
JvmType superType = type.getExtendedClass().getType();
if(superType instanceof JvmGenericType) {
Iterable<JvmConstructor> superConstructors = ((JvmGenericType) superType).getDeclaredConstructors();
for(JvmConstructor superConstructor: superConstructors) {
if(superConstructor.getParameters().isEmpty())
// there is a default super constructor. nothing more to check
return;
}
if(size(constructors) == 1 && typeExtensions.isSingleSyntheticDefaultConstructor(constructors.iterator().next())) {
List<String> issueData = newArrayList();
for(JvmConstructor superConstructor:superConstructors) {
issueData.add(EcoreUtil.getURI(superConstructor).toString());
issueData.add(doGetReadableSignature(type.getSimpleName(), superConstructor.getParameters()));
}
error("No default constructor in super type " + superType.getSimpleName() + "." +
type.getSimpleName() + " must define an explicit constructor.",
sourceType, getFeatureForIssue(sourceType), MISSING_CONSTRUCTOR,
toArray(issueData, String.class));
} else {
for(JvmConstructor constructor: constructors) {
XExpression expression = containerProvider.getAssociatedExpression(constructor);
if (expression instanceof XBlockExpression) {
List<XExpression> expressions = ((XBlockExpression) expression).getExpressions();
if(expressions.isEmpty() || !isDelegateConstructorCall(expressions.get(0))) {
EObject source = associations.getPrimarySourceElement(constructor);
error("No default constructor in super type " + superType.getSimpleName()
+ ". Another constructor must be invoked explicitly.",
source, null, MUST_INVOKE_SUPER_CONSTRUCTOR);
}
}
}
}
}
}
}

protected String doGetReadableSignature(String simpleName, List<JvmFormalParameter> parameters) {
return getReadableSignature(simpleName,
parameters.stream()
.map(JvmFormalParameter::getParameterType)
.collect(Collectors.toList()));
}

protected String getReadableSignature(String elementName, List<JvmTypeReference> parameterTypes) {
StringBuilder result = new StringBuilder(elementName);
result.append('(');
for (int i = 0; i < parameterTypes.size(); i++) {
if (i != 0) {
result.append(", ");
}
JvmTypeReference parameterType = parameterTypes.get(i);
if (parameterType != null)
result.append(parameterType.getSimpleName());
else
result.append("null");
}
result.append(')');
return result.toString();
}

protected boolean isDelegateConstructorCall(XExpression expression) {
if(expression instanceof XFeatureCall) {
JvmIdentifiableElement feature = ((XFeatureCall)expression).getFeature();
return (feature != null && !feature.eIsProxy() && feature instanceof JvmConstructor);
}
return false;
}

protected void checkMemberNamesAreUnique(JvmGenericType type) {
Map<String, List<JvmField>> fieldsByName = groupMembersByName(type.getMembers(),
JvmField.class);
Expand Down

0 comments on commit b30d3ef

Please sign in to comment.