diff --git a/scope/src/main/jastadd/ScopeTree.relast b/scope/src/main/jastadd/ScopeTree.relast index 8ed72413754ad3fc998ac9d0e6e0f6a0caa4e435..3da708098f1cdcc38ff9eb5e5470f2242883970c 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 3ca64528596533602746ca50763fdab9af416a3d..8bd777b431711d498044c13495e64978e5029b21 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 d472e0d0ad48cb60634988e101e6d5f60f314c5d..05b0ea10e0ac4f9eecbb2eff28c7c26ad21cbbb4 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 ef201525f26bee91ac385a13f75ff7e9ff1abe56..6d6cbf9ac7e4fd65a6df12cb675bda9a46fc1e73 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 0000000000000000000000000000000000000000..b983b028001dee0f0d863e3bcd09098746042885 --- /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 f07851f479bd3ca8711604597377be973c8a6f7e..d0ee15f843447579d5eb695ce68ed11f7150d9d9 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 0000000000000000000000000000000000000000..bc019b729a24c59a2ab1b747b04173e969e941cc --- /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 0000000000000000000000000000000000000000..7f6755e216ec6b0780045d210e71bee8655e4a41 --- /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; + } +}