Skip to content

Commit

Permalink
WIP: Started fixing check for incomplete implementations
Browse files Browse the repository at this point in the history
# Conflicts:
#	Shared/src/main/java/org/openzen/zencode/shared/CompileExceptionCode.java
  • Loading branch information
kindlich authored and stanhebben committed Oct 11, 2024
1 parent beaae44 commit 45cad76
Show file tree
Hide file tree
Showing 5 changed files with 150 additions and 22 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import org.openzen.zenscript.codemodel.OperatorType;
import org.openzen.zenscript.codemodel.identifiers.MethodID;
import org.openzen.zenscript.codemodel.identifiers.instances.MethodInstance;
import org.openzen.zenscript.codemodel.member.IDefinitionMember;
import org.openzen.zenscript.codemodel.type.BasicTypeID;
import org.openzen.zenscript.codemodel.type.TypeID;

Expand Down Expand Up @@ -579,4 +580,22 @@ public static CompileError invalidLambdaHeader(FunctionHeader header) {
public static CompileError unreachableStatement() {
return new CompileError(CompileExceptionCode.UNREACHABLE_STATEMENT, "Unreachable statement");
}

public static CompileError definitionNotAllowedHere(String text) {
return new CompileError(CompileExceptionCode.DEFINITION_NOT_ALLOWED_HERE, text);
}

public static CompileError incompleteImplementation(List<IDefinitionMember> unimplementedMembers) {

String text;
if(unimplementedMembers.size() == 1) {
text = unimplementedMembers.get(0).describe() + " is not implemented";
} else {
text = unimplementedMembers.stream()
.map(IDefinitionMember::describe)
.collect(Collectors.joining("\n - ", "Implementation incomplete: " + unimplementedMembers.size() + " members not yet implemented:\n", ""));
}

return new CompileError(CompileExceptionCode.INCOMPLETE_IMPLEMENTATION, text);
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#error: 7:INCOMPLETE_IMPLEMENTATION
#error: 8:INCOMPLETE_IMPLEMENTATION

interface Test {
print(): void;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -142,5 +142,6 @@ public enum CompileExceptionCode {
INVALID_PROPERTY_GETTER,
INVALID_PROPERTY_SETTER,
INVALID_PROPERTY_PAIR,
MISSING_RETURN_TYPE_IN_HEADER
MISSING_RETURN_TYPE_IN_HEADER,
DEFINITION_NOT_ALLOWED_HERE,
}
Original file line number Diff line number Diff line change
Expand Up @@ -224,26 +224,13 @@ public Void visitImplementation(ImplementationMember implementation) {
}

private void checkImplementationComplete(ImplementationMember implementation) {
// TODO
/*Set<IDefinitionMember> implemented = new HashSet<>();
for (IDefinitionMember member : implementation.members)
if (member.getOverrides() != null)
implemented.add(member.getOverrides().getTarget());
for (DefinitionMemberRef member : implementation.definitionBorrowedMembers.keySet())
implemented.add(member.getTarget());
TypeMembers members = scope.getTypeMembers(implementation.type);
List<IDefinitionMember> unimplemented = members.getUnimplementedMembers(implemented);
if (unimplemented.size() == 1) {
validator.logError(ValidationLogEntry.Code.INCOMPLETE_IMPLEMENTATION, implementation.position, unimplemented.get(0).describe() + " not implemented");
} else if (unimplemented.size() > 1) {
StringBuilder message = new StringBuilder();
message.append("Implementation incomplete: ").append(unimplemented.size()).append(" members not yet implemented:");
for (IDefinitionMember member : unimplemented) {
message.append("\n").append(" - ").append(member.describe());
}
validator.logError(ValidationLogEntry.Code.INCOMPLETE_IMPLEMENTATION, implementation.position, message.toString());
}*/
ImplementationCheckValidator implementationCheckValidator = new ImplementationCheckValidator(validator, implementation);
implementation.members.forEach(member -> member.accept(implementationCheckValidator));

List<IDefinitionMember> unimplementedMembers = implementationCheckValidator.getUnimplementedMembers();
if (!unimplementedMembers.isEmpty()) {
validator.logError(implementation.position, CompileErrors.incompleteImplementation(unimplementedMembers));
}
}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
package org.openzen.zenscript.validator.visitors;

import org.openzen.zenscript.codemodel.compilation.CompileErrors;
import org.openzen.zenscript.codemodel.definition.InterfaceDefinition;
import org.openzen.zenscript.codemodel.identifiers.MethodSymbol;
import org.openzen.zenscript.codemodel.member.*;
import org.openzen.zenscript.codemodel.type.TypeID;
import org.openzen.zenscript.validator.Validator;

import java.util.HashSet;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.stream.Collectors;

public class ImplementationCheckValidator implements MemberVisitor<Void> {

private final Validator validator;
private final Set<MethodSymbol> implementedMethods = new HashSet<>();
private final InterfaceDefinition interfaceDefinition;

public ImplementationCheckValidator(Validator validator, ImplementationMember implementationMember) {
this.validator = validator;

this.interfaceDefinition = implementationMember.asImplementation()
.flatMap(TypeID::asDefinition)
.map(d -> d.definition)
.filter(InterfaceDefinition.class::isInstance)
.map(InterfaceDefinition.class::cast)
.orElseThrow(() -> new NoSuchElementException("Must be an implementation!"));
}

@Override
public Void visitField(FieldMember member) {
validator.logError(member.position, CompileErrors.definitionNotAllowedHere("Field members not allowed inside an interface implementation"));
return null;
}

@Override
public Void visitConstructor(ConstructorMember member) {
validator.logError(member.position, CompileErrors.definitionNotAllowedHere("Constructor members not allowed inside an interface implementation"));
return null;
}

@Override
public Void visitMethod(MethodMember member) {
visitFunctional(member);
return null;
}

@Override
public Void visitGetter(GetterMember member) {
visitFunctional(member);
return null;
}

@Override
public Void visitSetter(SetterMember member) {
visitFunctional(member);
return null;
}

@Override
public Void visitOperator(OperatorMember member) {
visitFunctional(member);
return null;
}

@Override
public Void visitCaster(CasterMember member) {
visitFunctional(member);
return null;
}

@Override
public Void visitCustomIterator(IteratorMember member) {
visitFunctional(member);
return null;
}

private void visitFunctional(FunctionalMember member) {
member.getOverrides().ifPresent(e -> {
if (interfaceDefinition.equals(e.method.getDefiningType())) {
if (implementedMethods.contains(e.method)) {
validator.logError(member.position, CompileErrors.duplicateMember(member.toString()));
return;
}

implementedMethods.add(e.method);
} else {
validator.logError(member.position, CompileErrors.invalidOverride("override not part of interface"));
}
});
}

@Override
public Void visitImplementation(ImplementationMember member) {
validator.logError(member.position, CompileErrors.cannotNestImplementations());
return null;
}

@Override
public Void visitInnerDefinition(InnerDefinitionMember member) {
validator.logError(member.position, CompileErrors.definitionNotAllowedHere("Inner definitions are not allowed inside an interface implementation"));
return null;
}

@Override
public Void visitStaticInitializer(StaticInitializerMember member) {
validator.logError(member.position, CompileErrors.definitionNotAllowedHere("Static initializer members not allowed inside an interface implementation"));
return null;
}

public List<IDefinitionMember> getUnimplementedMembers() {
return interfaceDefinition.members.stream()
.filter(IDefinitionMember::isAbstract)
.filter(o -> !implementedMethods.contains(o))
.collect(Collectors.toList());
}

}

0 comments on commit 45cad76

Please sign in to comment.