Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,11 @@ private static FunctionSignature filterSigs(
return candidates.get(0);
}

candidates = filterPreferNonAbstract(candidates);
if (candidates.size() == 1) {
return candidates.get(0);
}


boolean b = true;
for (WurstType t : argTypes) {
Expand Down Expand Up @@ -99,6 +104,20 @@ private static List<FunctionSignature> filterByIfNotDefinedAnnotation(List<Funct
return list;
}

private static List<FunctionSignature> filterPreferNonAbstract(List<FunctionSignature> candidates) {
List<FunctionSignature> nonAbstract = new ArrayList<>();
for (FunctionSignature sig : candidates) {
FunctionDefinition def = sig.getDef();
if (def != null && !def.attrIsAbstract()) {
nonAbstract.add(sig);
}
}
if (!nonAbstract.isEmpty()) {
return nonAbstract;
}
return candidates;
}

@NotNull
private static List<FunctionSignature> filterByArgumentTypes(Collection<FunctionSignature> sigs, List<WurstType> argTypes, StmtCall location) {
List<FunctionSignature> candidates = new ArrayList<>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ static private class OverrideCheckResult {
// (for interfaces override modifier is optional)
boolean requiresOverrideMod = false;

// inherited functions used for override checks only, skip reporting
boolean skipErrorReporting = false;

// errors for functions with same name that it does not override
io.vavr.collection.List<String> overrideErrors = io.vavr.collection.List.empty();

Expand Down Expand Up @@ -71,6 +74,9 @@ private static void reportOverrideErrors(Map<String, Map<FuncLink, OverrideCheck
for (Entry<FuncLink, OverrideCheckResult> e : map.entrySet()) {
FunctionDefinition f = e.getKey().getDef();
OverrideCheckResult check = e.getValue();
if (check.skipErrorReporting) {
continue;
}
if (f.attrIsOverride() && !check.doesOverride) {
StringBuilder msg = new StringBuilder("Function " + f.getName() + " does not override anything.");
for (String overrideError : check.overrideErrors) {
Expand Down Expand Up @@ -101,30 +107,36 @@ public static ImmutableMultimap<String, DefLink> calculate(InterfaceDef i) {

private static void addNamesFromExtendedInterfaces(Multimap<String, DefLink> result, WurstTypeInterface iType, Map<String, Map<FuncLink, OverrideCheckResult>> overrideCheckResults) {
for (WurstTypeInterface superI : iType.extendedInterfaces()) {
addNewNameLinks(result, overrideCheckResults, superI.nameLinks(), false);
addNewNameLinks(result, overrideCheckResults, superI.nameLinks(), false, null);
}
}


private static void addNamesFromImplementedInterfaces(Multimap<String, DefLink> result, WurstTypeClass classDef, Map<String, Map<FuncLink, OverrideCheckResult>> overrideCheckResults) {
for (WurstTypeInterface interfaceType : classDef.implementedInterfaces()) {
addNewNameLinks(result, overrideCheckResults, interfaceType.nameLinks(), false);
addNewNameLinks(result, overrideCheckResults, interfaceType.nameLinks(), false, classDef.getClassDef());
}
}

private static void addNamesFromSuperClass(Multimap<String, DefLink> result, WurstTypeClass c, Map<String, Map<FuncLink, OverrideCheckResult>> overrideCheckResults) {
@Nullable WurstTypeClass superClass = c.extendedClass();
if (superClass != null) {
addNewNameLinks(result, overrideCheckResults, superClass.nameLinks(), false);
addNewNameLinks(result, overrideCheckResults, superClass.nameLinks(), false, c.getClassDef());
}
}

private static void addNewNameLinks(Multimap<String, DefLink> result, Map<String, Map<FuncLink, OverrideCheckResult>> overrideCheckResults, ImmutableMultimap<String, DefLink> newNameLinks, boolean allowStaticOverride) {
private static void addNewNameLinks(Multimap<String, DefLink> result, Map<String, Map<FuncLink, OverrideCheckResult>> overrideCheckResults, ImmutableMultimap<String, DefLink> newNameLinks, boolean allowStaticOverride, @Nullable ClassDef currentClass) {
for (Entry<String, DefLink> e : newNameLinks.entries()) {
DefLink def = e.getValue();
if (!def.getVisibility().isInherited()) {
continue;
}
if (currentClass != null && def instanceof FuncLink) {
DefLink adapted = ((FuncLink) def).adaptToReceiverType(currentClass.attrTyp());
if (adapted != null) {
def = adapted;
}
}
String name = e.getKey();
boolean isOverridden = false;
if (def instanceof FuncLink) {
Expand Down Expand Up @@ -153,8 +165,41 @@ private static void addNewNameLinks(Multimap<String, DefLink> result, Map<String
}
if (!isOverridden) {
result.put(name, def.hidingPrivate());
if (def instanceof FuncLink) {
registerOverrideCandidate(overrideCheckResults, name, (FuncLink) def, currentClass);
}
}
}
}

private static void registerOverrideCandidate(Map<String, Map<FuncLink, OverrideCheckResult>> overrideCheckResults,
String name, FuncLink funcLink, @Nullable ClassDef currentClass) {
if (currentClass == null) {
return;
}
FunctionDefinition def = funcLink.getDef();
if (!(def instanceof FuncDef)) {
return;
}
if (def.attrIsAbstract()) {
return;
}
if (def.attrNearestClassDef() == null) {
return;
}
if (currentClass != null) {
DefLink adapted = funcLink.adaptToReceiverType(currentClass.attrTyp());
if (adapted instanceof FuncLink) {
funcLink = (FuncLink) adapted;
}
}
Map<FuncLink, OverrideCheckResult> map = overrideCheckResults.computeIfAbsent(name, s -> new HashMap<>());
OverrideCheckResult check = map.get(funcLink);
if (check == null) {
check = new OverrideCheckResult();
check.skipErrorReporting = true;
map.put(funcLink, check);
}
}


Expand Down Expand Up @@ -319,7 +364,7 @@ private static void addNameDefDefLink(Multimap<String, DefLink> result, NameDef
private static void addNamesFromUsedModuleInstantiations(ClassOrModuleOrModuleInstanciation c,
Multimap<String, DefLink> result, Map<String, Map<FuncLink, OverrideCheckResult>> overrideCheckResults) {
for (ModuleInstanciation m : c.getModuleInstanciations()) {
addNewNameLinks(result, overrideCheckResults, m.attrNameLinks(), true);
addNewNameLinks(result, overrideCheckResults, m.attrNameLinks(), true, null);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1129,23 +1129,70 @@ public Map<ClassDef, FuncDef> getClassesWithImplementation(Collection<ClassDef>
+ " in "
+ Utils.printElementWithSource(Optional.of(c)));
}
for (NameLink nameLink : c.attrNameLinks().get(func.getName())) {
NameDef nameDef = nameLink.getDef();
if (nameLink.getDefinedIn() == c) {
if (nameLink instanceof FuncLink && nameLink.getDef() instanceof FuncDef) {
FuncLink funcLink = (FuncLink) nameLink;
FuncDef f = (FuncDef) funcLink.getDef();
// check if function f overrides func
if (WurstValidator.canOverride(funcLink, funcNameLink, false)) {
result.put(c, f);
}
}
}
FuncLink implementationLink = findImplementationLink(c, funcNameLink);
if (implementationLink != null && implementationLink.getDef() instanceof FuncDef) {
result.put(c, (FuncDef) implementationLink.getDef());
}
}
return result;
}

private @Nullable FuncLink findImplementationLink(ClassDef c, FuncLink target) {
FuncLink best = null;
int bestDistance = Integer.MAX_VALUE;
for (NameLink candidateLink : c.attrNameLinks().get(target.getName())) {
if (!(candidateLink instanceof FuncLink)) {
continue;
}
FuncLink candidate = (FuncLink) candidateLink;
if (!WurstValidator.canOverride(candidate, target, false)) {
continue;
}
FunctionDefinition candidateDef = candidate.getDef();
if (!(candidateDef instanceof FuncDef)) {
continue;
}
if (candidateDef.attrIsAbstract()) {
continue;
}
ClassDef owner = candidateDef.attrNearestClassDef();
if (owner == null) {
continue;
}
int distance = distanceToOwner(c, owner);
if (distance == Integer.MAX_VALUE) {
continue;
}
if (best == null || distance < bestDistance) {
best = candidate;
bestDistance = distance;
}
}
return best;
}

private int distanceToOwner(ClassDef start, ClassDef owner) {
int distance = 0;
ClassDef current = start;
Set<ClassDef> visited = new HashSet<>();
while (current != null && visited.add(current)) {
if (current == owner) {
return distance;
}
WurstTypeClass type = current.attrTypC();
if (type == null) {
break;
}
WurstTypeClass superType = type.extendedClass();
if (superType == null) {
break;
}
current = superType.getClassDef();
distance++;
}
return Integer.MAX_VALUE;
}


private final Map<ClassDef, List<Pair<ImVar, VarInitialization>>> classDynamicInitMap = Maps.newLinkedHashMap();
private final Map<ClassDef, List<WStatement>> classInitStatements = Maps.newLinkedHashMap();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,9 @@ public static void addOverride(
}

if (!needConversion) {
if (superMethodIm == subMethod) {
return;
}
superMethodIm.getSubMethods().add(subMethod);
return;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,13 @@ public WurstType getReturnType() {
return receiverType;
}

public @Nullable FunctionDefinition getDef() {
if (trace instanceof FunctionDefinition) {
return (FunctionDefinition) trace;
}
return null;
}

@CheckReturnValue
public FunctionSignature setTypeArgs(Element context, VariableBinding newMapping) {
if (newMapping.isEmpty()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -525,8 +525,11 @@ private void checkAbstractMethods(ClassDef c) {
}
loc.addError("Non-abstract class " + c.getName() + " cannot have abstract functions like " + f.getName());
} else if (link instanceof FuncLink) {
toImplement.append("\n ");
toImplement.append(((FuncLink) link).printFunctionTemplate());
FuncLink abstractLink = (FuncLink) link;
if (!hasImplementationInHierarchy(c, abstractLink)) {
toImplement.append("\n ");
toImplement.append(abstractLink.printFunctionTemplate());
}
}
}
}
Expand All @@ -536,6 +539,66 @@ private void checkAbstractMethods(ClassDef c) {
}
}

private boolean hasImplementationInHierarchy(ClassDef c, FuncLink abstractFunc) {
return findImplementationLink(c, abstractFunc) != null;
}

private @Nullable FuncLink findImplementationLink(ClassDef c, FuncLink abstractFunc) {
FuncLink best = null;
int bestDistance = Integer.MAX_VALUE;
for (NameLink nameLink : c.attrNameLinks().get(abstractFunc.getName())) {
if (!(nameLink instanceof FuncLink)) {
continue;
}
FuncLink candidate = (FuncLink) nameLink;
if (!WurstValidator.canOverride(candidate, abstractFunc, false)) {
continue;
}
FunctionDefinition candidateDef = candidate.getDef();
if (!(candidateDef instanceof FuncDef)) {
continue;
}
if (candidateDef.attrIsAbstract()) {
continue;
}
ClassDef owner = candidateDef.attrNearestClassDef();
if (owner == null) {
continue;
}
int distance = distanceToOwner(c, owner);
if (distance == Integer.MAX_VALUE) {
continue;
}
if (best == null || distance < bestDistance) {
best = candidate;
bestDistance = distance;
}
}
return best;
}

private int distanceToOwner(ClassDef start, ClassDef owner) {
int distance = 0;
ClassDef current = start;
Set<ClassDef> visited = new HashSet<>();
while (current != null && visited.add(current)) {
if (current == owner) {
return distance;
}
WurstTypeClass currentType = current.attrTypC();
if (currentType == null) {
break;
}
WurstTypeClass superType = currentType.extendedClass();
if (superType == null) {
break;
}
current = superType.getClassDef();
distance++;
}
return Integer.MAX_VALUE;
}

private void visit(StmtExitwhen exitwhen) {
Element parent = exitwhen.getParent();
while (!(parent instanceof FunctionDefinition)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -537,6 +537,26 @@ public void implGap() { // #676
);
}

@Test
public void interfaceImplementationFromSuperClass() {
testAssertOkLines(true,
"package test",
" native testSuccess()",
" interface Foo",
" function doSomething()",
" interface Bar",
" function doSomething()",
" class BaseFoo implements Foo",
" override function doSomething()",
" testSuccess()",
" class FooBar extends BaseFoo implements Bar",
" init",
" FooBar fb = new FooBar()",
" fb.doSomething()",
"endpackage"
);
}

@Test
public void testEmptyImplements() {
CompilationResult res = test().executeProg(false)
Expand Down
Loading