Skip to content

Commit

Permalink
doCheckFunctionOverrides in JvmGenericTypeValidator
Browse files Browse the repository at this point in the history
  • Loading branch information
LorenzoBettini committed Feb 5, 2024
1 parent 001292c commit cb65c6b
Show file tree
Hide file tree
Showing 2 changed files with 113 additions and 70 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
package org.eclipse.xtend.core.validation;

import static com.google.common.collect.Iterables.*;
import static com.google.common.collect.Iterables.filter;
import static com.google.common.collect.Lists.*;
import static org.eclipse.xtend.core.validation.IssueCodes.*;
import static org.eclipse.xtend.core.xtend.XtendPackage.Literals.*;
Expand All @@ -21,7 +20,6 @@
import java.lang.annotation.ElementType;
import java.util.Arrays;
import java.util.Collection;
import java.util.EnumSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map.Entry;
Expand All @@ -47,7 +45,6 @@
import org.eclipse.xtend.core.xtend.XtendClass;
import org.eclipse.xtend.core.xtend.XtendConstructor;
import org.eclipse.xtend.core.xtend.XtendEnum;
import org.eclipse.xtend.core.xtend.XtendExecutable;
import org.eclipse.xtend.core.xtend.XtendField;
import org.eclipse.xtend.core.xtend.XtendFile;
import org.eclipse.xtend.core.xtend.XtendFormalParameter;
Expand Down Expand Up @@ -114,7 +111,6 @@
import org.eclipse.xtext.xbase.scoping.featurecalls.OperatorMapping;
import org.eclipse.xtext.xbase.typesystem.IBatchTypeResolver;
import org.eclipse.xtext.xbase.typesystem.IResolvedTypes;
import org.eclipse.xtext.xbase.typesystem.override.IOverrideCheckResult.OverrideCheckDetails;
import org.eclipse.xtext.xbase.typesystem.override.IResolvedOperation;
import org.eclipse.xtext.xbase.typesystem.override.OverrideHelper;
import org.eclipse.xtext.xbase.typesystem.override.ResolvedFeatures;
Expand All @@ -137,7 +133,6 @@
import com.google.common.collect.ImmutableMultimap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Multimap;
import com.google.common.collect.Multimaps;
import com.google.common.collect.Sets;
Expand Down Expand Up @@ -604,15 +599,6 @@ else if(member instanceof XtendField)
return null;
}

protected EStructuralFeature returnTypeFeature(EObject member) {
if (member instanceof XtendFunction)
return XTEND_FUNCTION__RETURN_TYPE;
else if (member instanceof XtendField) // e.g., for an active annotation like @Accessors
return XTEND_FIELD__TYPE;
else
return null;
}

protected void doCheckFunctionOverrides(ResolvedFeatures resolvedFeatures, Set<EObject> flaggedOperations) {
for(IResolvedOperation operation: resolvedFeatures.getDeclaredOperations()) {
doCheckFunctionOverrides(operation, flaggedOperations);
Expand Down Expand Up @@ -646,33 +632,11 @@ protected void doCheckFunctionOverrides(IResolvedOperation operation, Set<EObjec
protected void doCheckFunctionOverrides(EObject sourceElement, IResolvedOperation resolved,
List<IResolvedOperation> allInherited) {
boolean overrideProblems = false;
List<IResolvedOperation> exceptionMismatch = null;
for(IResolvedOperation inherited: allInherited) {
if (inherited.getOverrideCheckResult().hasProblems()) {
overrideProblems = true;
EnumSet<OverrideCheckDetails> details = inherited.getOverrideCheckResult().getDetails();
if (details.contains(OverrideCheckDetails.IS_FINAL)) {
error("Attempt to override final method " + inherited.getSimpleSignature(), sourceElement,
nameFeature(sourceElement), OVERRIDDEN_FINAL);
} else if (details.contains(OverrideCheckDetails.REDUCED_VISIBILITY)) {
error("Cannot reduce the visibility of the overridden method " + inherited.getSimpleSignature(),
sourceElement, nameFeature(sourceElement), OVERRIDE_REDUCES_VISIBILITY);
} else if (details.contains(OverrideCheckDetails.EXCEPTION_MISMATCH)) {
if (exceptionMismatch == null)
exceptionMismatch = Lists.newArrayListWithCapacity(allInherited.size());
exceptionMismatch.add(inherited);
} else if (details.contains(OverrideCheckDetails.RETURN_MISMATCH)) {
error("The return type is incompatible with " + inherited.getSimpleSignature(), sourceElement,
returnTypeFeature(sourceElement), INCOMPATIBLE_RETURN_TYPE);
} else if (details.contains(OverrideCheckDetails.SYNCHRONIZED_MISMATCH)) {
warning("The overridden method is synchronized, the current one is not synchronized.", sourceElement,
nameFeature(sourceElement), MISSING_SYNCHRONIZED);
}
}
}
if (exceptionMismatch != null) {
createExceptionMismatchError(resolved, sourceElement, exceptionMismatch);
}
if (sourceElement instanceof XtendFunction) {
XtendFunction function = (XtendFunction) sourceElement;
if (!overrideProblems && !function.isOverride() && !function.isStatic()) {
Expand Down Expand Up @@ -700,40 +664,6 @@ protected void doCheckFunctionOverrides(EObject sourceElement, IResolvedOperatio
protected String getDeclaratorName(IResolvedOperation resolved) {
return getDeclaratorName(resolved.getDeclaration());
}

protected void createExceptionMismatchError(IResolvedOperation operation, EObject sourceElement,
List<IResolvedOperation> exceptionMismatch) {
List<LightweightTypeReference> exceptions = operation.getIllegallyDeclaredExceptions();

var suffixMessage = new StringBuilder();
suffixMessage.append(" not compatible with the throws clause in ");

for (int i = 0; i < exceptionMismatch.size(); i++) {
if (i != 0) {
if (i != exceptionMismatch.size() - 1)
suffixMessage.append(", ");
else
suffixMessage.append(" and ");
}
IResolvedOperation resolvedOperation = exceptionMismatch.get(i);
suffixMessage.append(getDeclaratorName(resolvedOperation));
suffixMessage.append('.');
suffixMessage.append(exceptionMismatch.get(i).getSimpleSignature());
}

List<LightweightTypeReference> resolvedExceptions = operation.getResolvedExceptions();
for (LightweightTypeReference exception : exceptions) {
var message = new StringBuilder(100);
message.append("The declared exception ");
message.append(exception.getHumanReadableName());
message.append(" is");
message.append(suffixMessage);
var exceptionIndex = resolvedExceptions.indexOf(exception);
error(message.toString(),
sourceElement, XTEND_EXECUTABLE__EXCEPTIONS,
exceptionIndex, INCOMPATIBLE_THROWS_CLAUSE);
}
}

protected EObject findPrimarySourceElement(IResolvedOperation operation) {
return associations.getPrimarySourceElement(operation.getDeclaration());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.EnumSet;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
Expand Down Expand Up @@ -57,6 +58,7 @@
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.IOverrideCheckResult.OverrideCheckDetails;
import org.eclipse.xtext.xbase.typesystem.override.IResolvedConstructor;
import org.eclipse.xtext.xbase.typesystem.override.IResolvedExecutable;
import org.eclipse.xtext.xbase.typesystem.override.IResolvedOperation;
Expand Down Expand Up @@ -377,6 +379,7 @@ protected void checkDuplicateAndOverriddenFunctions(EObject sourceType, JvmGener
Set<EObject> flaggedOperations = Sets.newHashSet();
doCheckDuplicateExecutables(type, resolvedFeatures, flaggedOperations);
doCheckOverriddenMethods(sourceType, type, resolvedFeatures, flaggedOperations);
doCheckFunctionOverrides(resolvedFeatures, flaggedOperations);
}

private <T1 extends JvmMember, T2 extends T1> Map<String, List<T2>> groupMembersByName(
Expand Down Expand Up @@ -747,6 +750,93 @@ protected void reportMissingImplementations(
}
}

protected void doCheckFunctionOverrides(ResolvedFeatures resolvedFeatures, Set<EObject> flaggedOperations) {
for(IResolvedOperation operation: resolvedFeatures.getDeclaredOperations()) {
doCheckFunctionOverrides(operation, flaggedOperations);
}
}

protected void doCheckFunctionOverrides(IResolvedOperation operation, Set<EObject> flaggedOperations) {
EObject sourceElement = findPrimarySourceElement(operation);
if (sourceElement != null) {
List<IResolvedOperation> allInherited = operation.getOverriddenAndImplementedMethods();
if (!allInherited.isEmpty() && flaggedOperations.add(sourceElement)) {
doCheckFunctionOverrides(sourceElement, operation, allInherited);
}
}
}

protected void doCheckFunctionOverrides(EObject sourceElement, IResolvedOperation resolved,
List<IResolvedOperation> allInherited) {
List<IResolvedOperation> exceptionMismatch = null;
for(IResolvedOperation inherited: allInherited) {
if (inherited.getOverrideCheckResult().hasProblems()) {
EnumSet<OverrideCheckDetails> details = inherited.getOverrideCheckResult().getDetails();
if (details.contains(OverrideCheckDetails.IS_FINAL)) {
error("Attempt to override final method " + inherited.getSimpleSignature(), sourceElement,
getFeatureForIssue(sourceElement), OVERRIDDEN_FINAL);
} else if (details.contains(OverrideCheckDetails.REDUCED_VISIBILITY)) {
error("Cannot reduce the visibility of the overridden method " + inherited.getSimpleSignature(),
sourceElement, getFeatureForIssue(sourceElement), OVERRIDE_REDUCES_VISIBILITY);
} else if (details.contains(OverrideCheckDetails.EXCEPTION_MISMATCH)) {
if (exceptionMismatch == null)
exceptionMismatch = Lists.newArrayListWithCapacity(allInherited.size());
exceptionMismatch.add(inherited);
} else if (details.contains(OverrideCheckDetails.RETURN_MISMATCH)) {
error("The return type is incompatible with " + inherited.getSimpleSignature(), sourceElement,
returnTypeFeature(sourceElement, resolved), INCOMPATIBLE_RETURN_TYPE);
} else if (details.contains(OverrideCheckDetails.SYNCHRONIZED_MISMATCH)) {
warning("The overridden method is synchronized, the current one is not synchronized.", sourceElement,
getFeatureForIssue(sourceElement), MISSING_SYNCHRONIZED);
}
}
}
if (exceptionMismatch != null) {
createExceptionMismatchError(resolved, sourceElement, exceptionMismatch);
}
}

protected void createExceptionMismatchError(IResolvedOperation operation, EObject sourceElement,
List<IResolvedOperation> exceptionMismatch) {
List<LightweightTypeReference> exceptions = operation.getIllegallyDeclaredExceptions();
StringBuilder message = new StringBuilder(100);
message.append("The declared exception");
if (exceptions.size() > 1) {
message.append('s');
}
message.append(' ');
for(int i = 0; i < exceptions.size(); i++) {
if (i != 0) {
if (i != exceptions.size() - 1)
message.append(", ");
else
message.append(" and ");
}
message.append(exceptions.get(i).getHumanReadableName());
}
if (exceptions.size() > 1) {
message.append(" are");
} else {
message.append(" is");
}
message.append(" not compatible with throws clause in ");
for(int i = 0; i < exceptionMismatch.size(); i++) {
if (i != 0) {
if (i != exceptionMismatch.size() - 1)
message.append(", ");
else
message.append(" and ");
}
IResolvedOperation resolvedOperation = exceptionMismatch.get(i);
message.append(getDeclaratorName(resolvedOperation));
message.append('.');
message.append(exceptionMismatch.get(i).getSimpleSignature());
}
// TODO: maybe put an error on each mismatching exception?
// TODO: currently we mark the first exception as in Xtend
error(message.toString(), sourceElement, exceptionsFeature(sourceElement, operation), INCOMPATIBLE_THROWS_CLAUSE);
}

protected String typeLabel(JvmExecutable executable) {
if (executable instanceof JvmOperation)
return "method";
Expand All @@ -765,6 +855,29 @@ protected String getDeclaratorName(JvmFeature feature) {
}
}

protected String getDeclaratorName(IResolvedOperation resolved) {
return getDeclaratorName(resolved.getDeclaration());
}

protected EStructuralFeature returnTypeFeature(EObject member, IResolvedOperation resolved) {
JvmOperation operation = resolved.getDeclaration();
JvmTypeReference returnType = operation.getReturnType();
EObject sourceReturnType = associations.getPrimarySourceElement(returnType);
if (sourceReturnType != null)
return sourceReturnType.eContainingFeature();
return null;
}

protected EStructuralFeature exceptionsFeature(EObject member, IResolvedOperation resolved) {
// This mimics the current Xtend that marks the first exception
JvmOperation operation = resolved.getDeclaration();
JvmTypeReference exceptionType = operation.getExceptions().get(0);
EObject sourceExceptionType = associations.getPrimarySourceElement(exceptionType);
if (sourceExceptionType != null)
return sourceExceptionType.eContainingFeature();
return null;
}

protected GeneratorConfig getGeneratorConfig(EObject element) {
GeneratorConfig result = (GeneratorConfig) getContext().get(GeneratorConfig.class);
if (result == null) {
Expand Down

0 comments on commit cb65c6b

Please sign in to comment.