From 212ffaac6cc1df150c5a6f70299aa42803f5287e Mon Sep 17 00:00:00 2001 From: Miklos Magyari Date: Fri, 23 Jul 2021 13:32:47 +0200 Subject: [PATCH] OOP: semantic check - a class cannot extend it itself directly/indirectly (issue #427) Signed-off-by: Miklos Magyari --- .../Basic_tests/OopNegativeSemanticTest.ttcn | 10 ++- .../AST/TTCN3/types/ClassTypeBody.java | 6 +- .../TTCN3/types/ClassTypeReferenceList.java | 78 ++++++++++++++++++- .../designer/AST/TTCN3/types/Class_Type.java | 4 - .../Basic_tests/OOP_Semantic_tests.java | 8 +- 5 files changed, 94 insertions(+), 12 deletions(-) diff --git a/Semantic_Analizer_Tests/src/Basic_tests/OopNegativeSemanticTest.ttcn b/Semantic_Analizer_Tests/src/Basic_tests/OopNegativeSemanticTest.ttcn index 10a66d267..d11fe3dec 100755 --- a/Semantic_Analizer_Tests/src/Basic_tests/OopNegativeSemanticTest.ttcn +++ b/Semantic_Analizer_Tests/src/Basic_tests/OopNegativeSemanticTest.ttcn @@ -51,6 +51,14 @@ type class RepeatedExtends extends MinimalClass, BaseClass, MinimalClass { } +// self extend +type class @trait TC {} +type class AA extends DD, TC { } +type class BB extends AA { } +type class CC extends BB { } +type class DD extends CC { } +type class EE extends EE { } + type class @trait BadTraitClass { public var integer m_int := 0; public timer Timer; @@ -213,7 +221,7 @@ type class ClassWithPrivate { } public function f_override4() { - + } } diff --git a/org.eclipse.titan.designer/src/org/eclipse/titan/designer/AST/TTCN3/types/ClassTypeBody.java b/org.eclipse.titan.designer/src/org/eclipse/titan/designer/AST/TTCN3/types/ClassTypeBody.java index 20f7d8829..2a33f62a0 100755 --- a/org.eclipse.titan.designer/src/org/eclipse/titan/designer/AST/TTCN3/types/ClassTypeBody.java +++ b/org.eclipse.titan.designer/src/org/eclipse/titan/designer/AST/TTCN3/types/ClassTypeBody.java @@ -322,7 +322,7 @@ public final class ClassTypeBody extends TTCN3Scope implements IReferenceChainEl final ClassTypeBody body = toBeChecked.removeFirst(); if(!result.contains(body)) { result.add(body); - if (body.extendsReferences != null) { + if (body.extendsReferences != null && body.extendsReferences.getClassBodies() != null) { for(final ClassTypeBody subBody : body.extendsReferences.getClassBodies()) { if(!result.contains(subBody) && !toBeChecked.contains(subBody)) { toBeChecked.add(subBody); @@ -406,6 +406,10 @@ public final class ClassTypeBody extends TTCN3Scope implements IReferenceChainEl return extendsReferences.hasExtendsReferences(); } + public ClassTypeReferenceList getExtendsReferences() { + return extendsReferences; + } + @Override public boolean accept(ASTVisitor v) { // TODO Auto-generated method stub diff --git a/org.eclipse.titan.designer/src/org/eclipse/titan/designer/AST/TTCN3/types/ClassTypeReferenceList.java b/org.eclipse.titan.designer/src/org/eclipse/titan/designer/AST/TTCN3/types/ClassTypeReferenceList.java index a5bbfc07d..1937e090e 100755 --- a/org.eclipse.titan.designer/src/org/eclipse/titan/designer/AST/TTCN3/types/ClassTypeReferenceList.java +++ b/org.eclipse.titan.designer/src/org/eclipse/titan/designer/AST/TTCN3/types/ClassTypeReferenceList.java @@ -9,7 +9,6 @@ package org.eclipse.titan.designer.AST.TTCN3.types; import java.text.MessageFormat; import java.util.ArrayList; -import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -19,7 +18,6 @@ import org.eclipse.titan.designer.AST.ASTNode; import org.eclipse.titan.designer.AST.ASTVisitor; import org.eclipse.titan.designer.AST.Assignment; import org.eclipse.titan.designer.AST.ILocateableNode; -import org.eclipse.titan.designer.AST.Identifier; import org.eclipse.titan.designer.AST.Location; import org.eclipse.titan.designer.AST.Reference; import org.eclipse.titan.designer.AST.Scope; @@ -39,6 +37,7 @@ public final class ClassTypeReferenceList extends ASTNode implements ILocateable private final String FINALCANNOTBEEXTENDED = "A final class cannot be extended"; private final String DUPLICATECOMPONENTREFERENCEFIRST = "Duplicate reference to class `{0}'' was first declared here"; private final String DUPLICATECOMPONENTREFERENCEREPEATED = "Duplicate reference to class `{0}'' was declared here again"; + private final String CLASSCANNOTEXTENDITSELF = "A class cannot extend itself neither directly nor indirectly"; private Class_Type parentClass; @@ -96,6 +95,9 @@ public final class ClassTypeReferenceList extends ASTNode implements ILocateable final Type type = deftype.getType(timestamp); if (type instanceof Class_Type) { final Class_Type extClass = (Class_Type)type; + if (extClass == parentClass) { + classRef.getLocation().reportSemanticError(CLASSCANNOTEXTENDITSELF); + } if (extClass.isFinal()) { classRef.getLocation().reportSemanticError(FINALCANNOTBEEXTENDED); parentClass.setIsErroneous(true); @@ -116,13 +118,14 @@ public final class ClassTypeReferenceList extends ASTNode implements ILocateable } } + checkSelfReference(timestamp, parentClass); checkUniqueness(timestamp); - lastCompilationTimeStamp = timestamp; - for (final ClassTypeBody body : orderedClassTypeBodies) { body.check(timestamp); } + + lastCompilationTimeStamp = timestamp; } /** @@ -168,6 +171,73 @@ public final class ClassTypeReferenceList extends ASTNode implements ILocateable return classReferences.size() > 0; } + /** + * Checks if the list of class references contains a given class + * + * @param timestamp + * @param refdClass + * @return + */ + public boolean checkExtendsReference(final CompilationTimeStamp timestamp, Class_Type refdClass) { + if (refdClass == null) { + return false; + } + for (Reference ref : classReferences) { + final Assignment refdAss = ref.getRefdAssignment(timestamp, false); + if (refdAss == null) { + return false; + } + if (refdAss instanceof Def_Type) { + final Def_Type deftype = (Def_Type)refdAss; + final Type type = deftype.getType(timestamp); + if (type instanceof Class_Type) { + if ((Class_Type)type == refdClass) { + ref.getLocation().reportSemanticError(CLASSCANNOTEXTENDITSELF); + return true; + } + } + } + } + return false; + } + + /** + * Walks the class extension hierarchy and checks if classes up in the 'extends' chain extend the given class + * + * @param timestamp + * @param refdClass + * @return + */ + public boolean checkSelfReference(final CompilationTimeStamp timestamp, Class_Type refdClass) { + if (refdClass == null) { + return false; + } + for (Reference ref : classReferences) { + final Assignment refdAss = ref.getRefdAssignment(timestamp, false); + if (refdAss == null) { + return false; + } + if (refdAss instanceof Def_Type) { + final Def_Type deftype = (Def_Type)refdAss; + final Type type = deftype.getType(timestamp); + if (type instanceof Class_Type) { + final Class_Type ctype = (Class_Type)type; + if (refdClass == ctype) { + ref.getLocation().reportSemanticError(CLASSCANNOTEXTENDITSELF); + return true; + } + if (checkExtendsReference(timestamp, refdClass)) { + return true; + } + if (ctype.getClassBody().getExtendsReferences().checkSelfReference(timestamp, refdClass)) { + return true; + } + } + } + } + return false; + } + @Override public void updateSyntax(TTCN3ReparseUpdater reparser, boolean isDamaged) throws ReParseException { // TODO Auto-generated method stub diff --git a/org.eclipse.titan.designer/src/org/eclipse/titan/designer/AST/TTCN3/types/Class_Type.java b/org.eclipse.titan.designer/src/org/eclipse/titan/designer/AST/TTCN3/types/Class_Type.java index 763717c04..a9e1472f4 100755 --- a/org.eclipse.titan.designer/src/org/eclipse/titan/designer/AST/TTCN3/types/Class_Type.java +++ b/org.eclipse.titan.designer/src/org/eclipse/titan/designer/AST/TTCN3/types/Class_Type.java @@ -14,14 +14,11 @@ package org.eclipse.titan.designer.AST.TTCN3.types; -import java.text.MessageFormat; import java.util.ArrayList; import java.util.List; -import org.eclipse.titan.designer.AST.ArraySubReference; import org.eclipse.titan.designer.AST.Assignment; import org.eclipse.titan.designer.AST.Assignment.Assignment_type; -import org.eclipse.titan.designer.AST.FieldSubReference; import org.eclipse.titan.designer.AST.IReferenceChain; import org.eclipse.titan.designer.AST.ISubReference; import org.eclipse.titan.designer.AST.ISubReference.Subreference_type; @@ -30,7 +27,6 @@ import org.eclipse.titan.designer.AST.ITypeWithComponents; import org.eclipse.titan.designer.AST.Identifier; import org.eclipse.titan.designer.AST.Identifier.Identifier_type; import org.eclipse.titan.designer.AST.Location; -import org.eclipse.titan.designer.AST.ParameterisedSubReference; import org.eclipse.titan.designer.AST.Reference; import org.eclipse.titan.designer.AST.Scope; import org.eclipse.titan.designer.AST.Type; diff --git a/org.eclipse.titan.regressiontests/src/org/eclipse/titan/regressiontests/designer/statictests/Basic_tests/OOP_Semantic_tests.java b/org.eclipse.titan.regressiontests/src/org/eclipse/titan/regressiontests/designer/statictests/Basic_tests/OOP_Semantic_tests.java index e08b27e41..094febb25 100755 --- a/org.eclipse.titan.regressiontests/src/org/eclipse/titan/regressiontests/designer/statictests/Basic_tests/OOP_Semantic_tests.java +++ b/org.eclipse.titan.regressiontests/src/org/eclipse/titan/regressiontests/designer/statictests/Basic_tests/OOP_Semantic_tests.java @@ -45,7 +45,7 @@ public class OOP_Semantic_tests { private ArrayList oopNegative_ttcn_initializer() { //oopNegativeSemanticTest.ttcn - ArrayList markersToCheck = new ArrayList(45); + ArrayList markersToCheck = new ArrayList(50); int lineNum = 28; markersToCheck.add(new MarkerToCheck("class type expected", lineNum, IMarker.SEVERITY_ERROR)); lineNum += 10; @@ -62,7 +62,11 @@ public class OOP_Semantic_tests { markersToCheck.add(new MarkerToCheck("A class can only extend one non-trait class", lineNum, IMarker.SEVERITY_ERROR)); markersToCheck.add(new MarkerToCheck("Duplicate reference to class `MinimalClass' was declared here again", lineNum, IMarker.SEVERITY_ERROR)); markersToCheck.add(new MarkerToCheck("Duplicate reference to class `MinimalClass' was first declared here", lineNum, IMarker.SEVERITY_ERROR)); - lineNum += 5; + lineNum += 6; + for (i = 0; i < 5; i++) { + markersToCheck.add(new MarkerToCheck("A class cannot extend itself neither directly nor indirectly", lineNum++, IMarker.SEVERITY_ERROR)); + } + lineNum += 2; for (i = 0; i < 2; i++) { markersToCheck.add(new MarkerToCheck("Trait classes can only declare methods", lineNum++, IMarker.SEVERITY_ERROR)); } -- GitLab