diff --git a/CodeModel/src/main/java/org/openzen/zenscript/codemodel/compilation/CompileErrors.java b/CodeModel/src/main/java/org/openzen/zenscript/codemodel/compilation/CompileErrors.java index e23dbf64..d55c9d4b 100644 --- a/CodeModel/src/main/java/org/openzen/zenscript/codemodel/compilation/CompileErrors.java +++ b/CodeModel/src/main/java/org/openzen/zenscript/codemodel/compilation/CompileErrors.java @@ -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; @@ -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 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); + } } diff --git a/ScriptingEngineTester/src/main/resources/zencode_tests/interfaces/incomplete_implementation_1.zc b/ScriptingEngineTester/src/main/resources/zencode_tests/interfaces/incomplete_implementation_1.zc index 9368f7e0..a7a17397 100644 --- a/ScriptingEngineTester/src/main/resources/zencode_tests/interfaces/incomplete_implementation_1.zc +++ b/ScriptingEngineTester/src/main/resources/zencode_tests/interfaces/incomplete_implementation_1.zc @@ -1,4 +1,4 @@ -#error: 7:INCOMPLETE_IMPLEMENTATION +#error: 8:INCOMPLETE_IMPLEMENTATION interface Test { print(): void; diff --git a/Shared/src/main/java/org/openzen/zencode/shared/CompileExceptionCode.java b/Shared/src/main/java/org/openzen/zencode/shared/CompileExceptionCode.java index c8ba51e9..6174e979 100644 --- a/Shared/src/main/java/org/openzen/zencode/shared/CompileExceptionCode.java +++ b/Shared/src/main/java/org/openzen/zencode/shared/CompileExceptionCode.java @@ -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, } diff --git a/Validator/src/main/java/org/openzen/zenscript/validator/visitors/DefinitionMemberValidator.java b/Validator/src/main/java/org/openzen/zenscript/validator/visitors/DefinitionMemberValidator.java index fb3e18ad..df7913e5 100644 --- a/Validator/src/main/java/org/openzen/zenscript/validator/visitors/DefinitionMemberValidator.java +++ b/Validator/src/main/java/org/openzen/zenscript/validator/visitors/DefinitionMemberValidator.java @@ -224,26 +224,13 @@ public Void visitImplementation(ImplementationMember implementation) { } private void checkImplementationComplete(ImplementationMember implementation) { - // TODO - /*Set 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 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 unimplementedMembers = implementationCheckValidator.getUnimplementedMembers(); + if (!unimplementedMembers.isEmpty()) { + validator.logError(implementation.position, CompileErrors.incompleteImplementation(unimplementedMembers)); + } } @Override diff --git a/Validator/src/main/java/org/openzen/zenscript/validator/visitors/ImplementationCheckValidator.java b/Validator/src/main/java/org/openzen/zenscript/validator/visitors/ImplementationCheckValidator.java new file mode 100644 index 00000000..21cc70d0 --- /dev/null +++ b/Validator/src/main/java/org/openzen/zenscript/validator/visitors/ImplementationCheckValidator.java @@ -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 { + + private final Validator validator; + private final Set 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 getUnimplementedMembers() { + return interfaceDefinition.members.stream() + .filter(IDefinitionMember::isAbstract) + .filter(o -> !implementedMethods.contains(o)) + .collect(Collectors.toList()); + } + +}