From c31c3c876465d72cb11ba55c47db584c9ecddaad Mon Sep 17 00:00:00 2001 From: Johannes Mey <johannes.mey@tu-dresden.de> Date: Sat, 4 Jan 2020 03:29:28 +0100 Subject: [PATCH] added proper support for (multiple) inheritance, but broke visibility again, should be easily fixable. --- scope/src/main/jastadd/ScopeTree.relast | 2 + scope/src/main/jastadd/Shadowing.jrag | 23 +++--- .../src/main/jastadd/ProgramToScopeTree.jrag | 72 ++++++++++++------- .../main/jastadd/ProgramToScopeTree.relast | 4 ++ .../org/extendj/InnerInheritanceTest.java | 23 ++++++ .../src/test/java/org/extendj/InnerTest.java | 8 ++- .../resources/innerInheritance/ClassA.java | 5 ++ .../resources/innerInheritance/ClassB.java | 9 +++ 8 files changed, 105 insertions(+), 41 deletions(-) create mode 100644 scope4j/src/test/java/org/extendj/InnerInheritanceTest.java create mode 100644 scope4j/src/test/resources/innerInheritance/ClassA.java create mode 100644 scope4j/src/test/resources/innerInheritance/ClassB.java diff --git a/scope/src/main/jastadd/ScopeTree.relast b/scope/src/main/jastadd/ScopeTree.relast index 8ed7241..3da7080 100644 --- a/scope/src/main/jastadd/ScopeTree.relast +++ b/scope/src/main/jastadd/ScopeTree.relast @@ -2,3 +2,5 @@ ScopeTree : Scope; abstract Element; Declaration:Element ::= <Name:String>; Scope:Element ::= Element*; + +rel Scope.inheritedScope* -> Scope; diff --git a/scope/src/main/jastadd/Shadowing.jrag b/scope/src/main/jastadd/Shadowing.jrag index 3ca6452..8bd777b 100644 --- a/scope/src/main/jastadd/Shadowing.jrag +++ b/scope/src/main/jastadd/Shadowing.jrag @@ -4,25 +4,26 @@ aspect Shadowing { Declaration contributes new VariableShadowFinding(shadowed(), this) when isShadowing() to ScopeTree.variableShadowings(); Declaration contributes new MultipleDeclarationFinding(this) when isShadowingInSameScope() to ScopeTree.variableShadowings(); - inh Declaration Declaration.shadowed(); - eq Scope.getElement(int i).shadowed() = shadowed(getElement(i).asDeclaration()); + syn Declaration Declaration.shadowed()= shadowed(asDeclaration()); inh Declaration Element.shadowed(Declaration shadower); - eq ScopeTree.getElement().shadowed(Declaration shadower) { + eq Scope.getElement().shadowed(Declaration shadower) = shadowedLocally(shadower); + + syn Declaration Scope.shadowedLocally(Declaration shadower) { + // first look in the current scope for (Declaration declaration : declarations()) { if (declaration != shadower && declaration.getName().equals(shadower.getName())) { return declaration; } } - return null; - } - eq Scope.getElement().shadowed(Declaration shadower) { - for (Declaration declaration : declarations()) { - if (declaration != shadower && declaration.getName().equals(shadower.getName())) { - return declaration; + // the look in the inherited scopes + for (Scope inherited : getInheritedScopeList()) { + Declaration shadowed = inherited.shadowedLocally(shadower); + if (shadowed != null) { + return shadowed; } } - return shadowed(shadower); + return (this instanceof ScopeTree) ? null : shadowed(shadower); } inh Declaration Declaration.shadowedInSameScope(); @@ -39,8 +40,6 @@ aspect Shadowing { syn boolean Declaration.isShadowing() = shadowed() != null; syn boolean Declaration.isShadowingInSameScope() = shadowedInSameScope() != null; - - } aspect Statictics { diff --git a/scope4j/src/main/jastadd/ProgramToScopeTree.jrag b/scope4j/src/main/jastadd/ProgramToScopeTree.jrag index d472e0d..05b0ea1 100644 --- a/scope4j/src/main/jastadd/ProgramToScopeTree.jrag +++ b/scope4j/src/main/jastadd/ProgramToScopeTree.jrag @@ -1,8 +1,38 @@ aspect ProgramToScopeTree { /** a relational nta collection attribute to compute the scope tree */ - coll ScopeTree Program.scopeTree() [asScopeTree()] with addElement root Program; - TypeDecl contributes scope() when !isClassDecl() to Program.scopeTree(); - ClassDecl contributes protectedScope() when !isInnerType() && !superclass().compilationUnit().fromSource() to Program.scopeTree(); + syn lazy ScopeTree Program.scopeTree() { + ScopeTree tree = asScopeTree(); + + // add all classes + for (CompilationUnit cu : getCompilationUnitList()) { + for (TypeDecl typeDecl : cu.getTypeDeclList()) { + tree.addElement(typeDecl.isClassDecl() ? ((ClassDecl)typeDecl).protectedScope() : typeDecl.scope()); + } + } + + tree.updateInheritance(); // traverse the tree and add all inheritance relations + + return tree; + } + + /** helper method to add inheritance relations */ + public void Scope.updateInheritance() { + for (Element element : getElementList()) { + if (element.isScope()) { + element.asScope().updateInheritance(); + } + } + } + + public void ProtectedClassDeclScope.updateInheritance() { + if (getTypeDecl().isClassDecl()) { + ClassDecl classDecl = (ClassDecl)getTypeDecl(); + if(classDecl.superclass().isClassDecl() && classDecl.superclass().compilationUnit().fromSource()) { + addInheritedScope(((ClassDecl)classDecl.superclass()).asPackageScope()); + } + } + super.updateInheritance(); + } /** a relational nta collection attribute to compute scopes */ coll Scope ASTNode.scope() [asScope()] with addElement root Program; @@ -12,19 +42,8 @@ aspect ProgramToScopeTree { coll TypeDeclScope ClassDecl.packageScope() [asPackageScope()] with addElement root Program; // collect all scopes - TypeDecl contributes scope() when !isClassDecl() to ASTNode.scope() for containingScope(); - ClassDecl contributes protectedScope() - when !(!isInnerClass() && superclass().isClassDecl() && superclass().compilationUnit().fromSource()) - to ASTNode.scope() - for containingScope(); - ClassDecl contributes protectedScope() - when !isInnerClass() && superclass().isClassDecl() && superclass().compilationUnit().fromSource() && !hostPackage().equals(superclass().hostPackage()) - to ClassDecl.protectedScope() - for superclass(); - ClassDecl contributes protectedScope() - when !isInnerClass() && superclass().isClassDecl() && superclass().compilationUnit().fromSource() && hostPackage().equals(superclass().hostPackage()) - to ClassDecl.packageScope() - for superclass(); + TypeDecl contributes scope() when !isNestedType() && !isClassDecl() to ASTNode.scope() for containingScope(); + ClassDecl contributes protectedScope() when isNestedType() to ASTNode.scope() for containingScope(); Block contributes scope() to ASTNode.scope() for containingScope(); ForStmt contributes scope() when !(getStmt() instanceof Block) to ASTNode.scope() for containingScope(); EnhancedForStmt contributes scope() when !(getStmt() instanceof Block) to ASTNode.scope() for containingScope(); @@ -39,9 +58,8 @@ aspect ProgramToScopeTree { } /** - * ascpect containing helper methods to construct (mostly empty) AST nodes of the scope tree - * If it was not for the single line in asProtectedScope(), the rest of this aspect could have been generated - * automatically, which would have been much nicer! + * aspect containing helper methods to construct (mostly empty) AST nodes of the scope tree + * There are few parts added manually, but stubs could easily be generated from the mapping grammar */ aspect ScopeTreeConstructors { @@ -62,19 +80,21 @@ aspect ScopeTreeConstructors { return scope; } + syn lazy TypeDeclScope ClassDecl.asScope() { + TypeDeclScope scope = new PrivateClassDeclScope(); + scope.setTypeDecl(this); + return scope; + } + syn lazy TypeDeclScope ClassDecl.asProtectedScope() { - TypeDeclScope scope = new TypeDeclScope(); + TypeDeclScope scope = new ProtectedClassDeclScope(); scope.setTypeDecl(this); - scope.addElement(packageScope()); // this irregular statement is necessary because of either a bug or a limitation in JastAdd collections + scope.addElement(packageScope()); return scope; } - // the following commented statement unfortunately does not work, because during the contribution phase, the information - // which target should be contributed to is lost if there are multiple targets. Maybe I got it wrong, but I tried many things - // and JastAdd always wants to also add the contribution made in line 17ff to this target as well. - // ClassDecl contributes scope() to ClassDecl.protectedScope() for this; syn lazy TypeDeclScope ClassDecl.asPackageScope() { - TypeDeclScope scope = new TypeDeclScope(); + TypeDeclScope scope = new PackageClassDeclScope(); scope.setTypeDecl(this); scope.addElement(scope()); return scope; diff --git a/scope4j/src/main/jastadd/ProgramToScopeTree.relast b/scope4j/src/main/jastadd/ProgramToScopeTree.relast index ef20152..6d6cbf9 100644 --- a/scope4j/src/main/jastadd/ProgramToScopeTree.relast +++ b/scope4j/src/main/jastadd/ProgramToScopeTree.relast @@ -5,6 +5,10 @@ abstract JavaScope : Scope; TypeDeclScope : JavaScope; rel TypeDeclScope.typeDecl -> TypeDecl; +ProtectedClassDeclScope : TypeDeclScope; +PackageClassDeclScope : TypeDeclScope; +PrivateClassDeclScope : TypeDeclScope; + BlockScope : JavaScope; rel BlockScope.block -> Block; diff --git a/scope4j/src/test/java/org/extendj/InnerInheritanceTest.java b/scope4j/src/test/java/org/extendj/InnerInheritanceTest.java new file mode 100644 index 0000000..b983b02 --- /dev/null +++ b/scope4j/src/test/java/org/extendj/InnerInheritanceTest.java @@ -0,0 +1,23 @@ +package org.extendj; + +import org.extendj.ast.AbstractFinding; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.util.Set; + +public class InnerInheritanceTest extends ScopeAnalysisTest { + + @Test + void test() { + + ScopeAnalysis scopeAnalysis = new ScopeAnalysis(); + Set<AbstractFinding> findings = scopeAnalysis.analyze("src/test/resources/innerInheritance", true, true); + + assertShadow(findings, "fieldA", "ClassB", 6, "ClassA", 3); + assertShadow(findings, "fieldB", "ClassB", 7, "ClassB", 3); + + Assertions.assertEquals(2, findings.size()); + } + +} diff --git a/scope4j/src/test/java/org/extendj/InnerTest.java b/scope4j/src/test/java/org/extendj/InnerTest.java index f07851f..d0ee15f 100644 --- a/scope4j/src/test/java/org/extendj/InnerTest.java +++ b/scope4j/src/test/java/org/extendj/InnerTest.java @@ -35,10 +35,12 @@ public class InnerTest extends ScopeAnalysisTest { // anonymous class defined in other class assertShadow(findings, "fieldB", "ClassB", 5, "ClassB", 10); - // this finding is currently not found -// assertShadow(findings, "fieldB", 10, 4); - Assertions.assertEquals(10, findings.size()); + // the anonymous class inherited a field + assertShadow(findings, "fieldB", "ClassB", 10, "ClassA", 4); + + + Assertions.assertEquals(11, findings.size()); } } diff --git a/scope4j/src/test/resources/innerInheritance/ClassA.java b/scope4j/src/test/resources/innerInheritance/ClassA.java new file mode 100644 index 0000000..bc019b7 --- /dev/null +++ b/scope4j/src/test/resources/innerInheritance/ClassA.java @@ -0,0 +1,5 @@ +public class ClassA { + + public int fieldA; + +} diff --git a/scope4j/src/test/resources/innerInheritance/ClassB.java b/scope4j/src/test/resources/innerInheritance/ClassB.java new file mode 100644 index 0000000..7f6755e --- /dev/null +++ b/scope4j/src/test/resources/innerInheritance/ClassB.java @@ -0,0 +1,9 @@ +public class ClassB { + + int fieldB; + + class ClassC extends ClassA { + int fieldA; + int fieldB; + } +} -- GitLab