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 c0aa6077129c779861bd24eb60b25765079a69b7..05b0ea10e0ac4f9eecbb2eff28c7c26ad21cbbb4 100644
--- a/scope4j/src/main/jastadd/ProgramToScopeTree.jrag
+++ b/scope4j/src/main/jastadd/ProgramToScopeTree.jrag
@@ -1,46 +1,72 @@
 aspect ProgramToScopeTree {
+  /** a relational nta collection attribute to compute the scope tree */
   syn lazy ScopeTree Program.scopeTree() {
-    ScopeTree tree = new ScopeTree();
-    tree.setProgram(this);
+    ScopeTree tree = asScopeTree();
+
+    // add all classes
+    for (CompilationUnit cu : getCompilationUnitList()) {
+      for (TypeDecl typeDecl : cu.getTypeDeclList()) {
+          tree.addElement(typeDecl.isClassDecl() ? ((ClassDecl)typeDecl).protectedScope() : typeDecl.scope());
+      }
+    }
 
-    for (CompilationUnit compilationUnit : getCompilationUnitList())
-      for (TypeDecl typeDecl : compilationUnit.getTypeDeclList())
-        tree.addElement(typeDecl.scope());
+    tree.updateInheritance(); // traverse the tree and add all inheritance relations
 
     return tree;
   }
 
-  /** a relational nta collection attribute to compute the scope 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;
 
+  /** a relational nta collection attribute to compute a special scope containing visible fields and subtypes */
+  coll TypeDeclScope ClassDecl.protectedScope() [asProtectedScope()] with addElement root Program;
+  coll TypeDeclScope ClassDecl.packageScope() [asPackageScope()] with addElement root Program;
+
   // collect all scopes
-  TypeDecl contributes scope() to ASTNode.scope() for containingScope();
+  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();
 
   // collect all elements
-  Declarator contributes asDeclaration() to ASTNode.scope() for containingScope();
+  Declarator contributes asDeclaration() when !isField() || isPrivate() to ASTNode.scope() for containingScope();
+  // field which is neither private, protected, nor public -> package-private scope
+  Declarator contributes asDeclaration() when isField() && !isPrivate() && !(isProtected() || isPublic()) to ClassDecl.packageScope() for containingScope();
+  // field which is either protected or public -> protected scope
+  Declarator contributes asDeclaration() when isField() && !isPrivate() && (isProtected() || isPublic()) to ClassDecl.protectedScope() for containingScope();
   ParameterDeclaration contributes asDeclaration() to ASTNode.scope() for containingScope();
+}
 
-  Collection<Declaration> ClassDecl.createSuperClassFieldDeclarators() {
-    ArrayList<Declaration> result = new ArrayList<>();
-    TypeDecl supertype = superclass();
-
-    System.out.println("supertype " + supertype.getID());
-    while (supertype.isClassDecl() && supertype != unknownType()) {
-      for (BodyDecl bodyDecl : supertype.getBodyDeclList()) {
-        if (bodyDecl instanceof FieldDecl) {
-          for (FieldDeclarator declarator : ((FieldDecl)bodyDecl).getDeclaratorList()) {
-            JavaDeclaration declaration = new JavaDeclaration(declarator.getID());
-            declaration.setDeclarator(declarator);
-            result.add(declaration);
-          }
-        }
-      }
-      supertype = ((ClassDecl)supertype).superclass();
-    }
-    return result;
+/**
+ * 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 {
+
+  syn lazy ScopeTree Program.asScopeTree() {
+    ScopeTree tree = new ScopeTree();
+    tree.setProgram(this);
+    return tree;
   }
 
   /** fallback attribute to ensure every AST element could pontentially be a scope */
@@ -51,11 +77,26 @@ aspect ProgramToScopeTree {
   syn lazy TypeDeclScope TypeDecl.asScope() {
     TypeDeclScope scope = new TypeDeclScope();
     scope.setTypeDecl(this);
-    if (isClassDecl()) {
-      for (Declaration declaration : ((ClassDecl)this).createSuperClassFieldDeclarators()) {
-        scope.addElement(declaration);
-      }
-    }
+    return scope;
+  }
+
+  syn lazy TypeDeclScope ClassDecl.asScope() {
+    TypeDeclScope scope = new PrivateClassDeclScope();
+    scope.setTypeDecl(this);
+    return scope;
+  }
+
+  syn lazy TypeDeclScope ClassDecl.asProtectedScope() {
+    TypeDeclScope scope = new ProtectedClassDeclScope();
+    scope.setTypeDecl(this);
+    scope.addElement(packageScope());
+    return scope;
+  }
+
+  syn lazy TypeDeclScope ClassDecl.asPackageScope() {
+    TypeDeclScope scope = new PackageClassDeclScope();
+    scope.setTypeDecl(this);
+    scope.addElement(scope());
     return scope;
   }
 
@@ -77,8 +118,8 @@ aspect ProgramToScopeTree {
     return scope;
   }
 
-  syn lazy JavaDeclaration Declarator.asDeclaration() {
-    JavaDeclaration decl = new JavaDeclaration(getID());
+  syn lazy SimpleJavaDeclaration Declarator.asDeclaration() {
+    SimpleJavaDeclaration decl = new SimpleJavaDeclaration(getID());
     decl.setDeclarator(this);
     return decl;
   }
@@ -113,4 +154,8 @@ aspect ScopeGenerationAttributes {
   eq EnhancedForStmt.getVariableDecl().containingScope() = (getStmt() instanceof Block) ? getStmt() : this;
   eq ForStmt.getInitStmt().containingScope() = (getStmt() instanceof Block) ? getStmt() : this;
   eq ForStmt.getUpdateStmt().containingScope() = (getStmt() instanceof Block) ? getStmt() : this;
+
+  // allow host package to be called from all AST nodes
+  inh String ASTNode.hostPackage();
+  eq Program.getCompilationUnit(int i).hostPackage() = getCompilationUnit(i).getPackageDecl();
 }
diff --git a/scope4j/src/main/jastadd/ProgramToScopeTree.relast b/scope4j/src/main/jastadd/ProgramToScopeTree.relast
index a1d84e0153fe227e592e61c3a456e9553462c79e..6d6cbf9ac7e4fd65a6df12cb675bda9a46fc1e73 100644
--- a/scope4j/src/main/jastadd/ProgramToScopeTree.relast
+++ b/scope4j/src/main/jastadd/ProgramToScopeTree.relast
@@ -1,23 +1,29 @@
 // glue relation for the Java-based variable shadowing analysis
 rel ScopeTree.Program -> Program;
 
-TypeDeclScope : Scope;
+abstract JavaScope : Scope;
+TypeDeclScope : JavaScope;
 rel TypeDeclScope.typeDecl -> TypeDecl;
 
-BlockScope : Scope;
+ProtectedClassDeclScope : TypeDeclScope;
+PackageClassDeclScope : TypeDeclScope;
+PrivateClassDeclScope : TypeDeclScope;
+
+BlockScope : JavaScope;
 rel BlockScope.block -> Block;
 
-ForStmtScope : Scope;
+ForStmtScope : JavaScope;
 rel ForStmtScope.forStmt -> ForStmt;
 
-EnhancedForStmtScope : Scope;
+EnhancedForStmtScope : JavaScope;
 rel EnhancedForStmtScope.enhancedForStmt -> EnhancedForStmt;
 
-JavaDeclaration : Declaration;
-rel JavaDeclaration.declarator -> Declarator;
+abstract JavaDeclaration : Declaration ;
+SimpleJavaDeclaration : JavaDeclaration;
+rel SimpleJavaDeclaration.declarator -> Declarator;
 
-JavaParameterDeclaration : Declaration;
+JavaParameterDeclaration : JavaDeclaration;
 rel JavaParameterDeclaration.parameterDeclaration -> ParameterDeclaration;
 
-JavaInferredLambdaParameterDeclaration : Declaration;
+JavaInferredLambdaParameterDeclaration : JavaDeclaration;
 rel JavaInferredLambdaParameterDeclaration.inferredParameterDeclaration -> InferredParameterDeclaration;
diff --git a/scope4j/src/main/jastadd/Shadow.jadd b/scope4j/src/main/jastadd/Shadow.jadd
index 644a178f4dba9057f6ab1617f8360e82b6f8f493..0caf2f5f46964f4fa319576dbe5c50e2ca074fe3 100644
--- a/scope4j/src/main/jastadd/Shadow.jadd
+++ b/scope4j/src/main/jastadd/Shadow.jadd
@@ -1,55 +1,37 @@
 aspect Shadow{
+    // --- toString ---
     eq TypeDeclScope.toString() = getTypeDecl().getClass().getSimpleName() + "-Scope";
     eq BlockScope.toString() = getBlock().getClass().getSimpleName() + "-Scope";
     eq ForStmtScope.toString() = getForStmt().getClass().getSimpleName() + "-Scope";
     eq EnhancedForStmtScope.toString() = getEnhancedForStmt().getClass().getSimpleName() + "-Scope";
 
-    eq JavaDeclaration.toString() = getDeclarator().getClass().getSimpleName() + ":" + super.toString();
+    eq SimpleJavaDeclaration.toString() = getDeclarator().getClass().getSimpleName() + ":" + super.toString();
     eq JavaParameterDeclaration.toString() = getParameterDeclaration().getClass().getSimpleName() + ":" + super.toString();
     eq JavaInferredLambdaParameterDeclaration.toString() = getInferredParameterDeclaration().getClass().getSimpleName() + ":" + super.toString();
 
-    public int TypeDeclScope.lineNumber(){
-      return getTypeDecl().lineNumber();
-    }
-    public int BlockScope.lineNumber(){
-      return getBlock().lineNumber();
-    }
-    public int ForStmtScope.lineNumber(){
-      return getForStmt().lineNumber();
-    }
-    public int EnhancedForStmtScope.lineNumber(){
-      return getEnhancedForStmt().lineNumber();
-    }
-    public int JavaDeclaration.lineNumber() {
-      return getDeclarator().lineNumber();
-    }
-    public int JavaParameterDeclaration.lineNumber() {
-      return getParameterDeclaration().lineNumber();
-    }
-    public int JavaInferredLambdaParameterDeclaration.lineNumber() {
-      return getInferredParameterDeclaration().lineNumber();
-    }
+    // --- lineNumber ---
+    syn int JavaScope.lineNumber();
+    eq TypeDeclScope.lineNumber() = getTypeDecl().lineNumber();
+    eq BlockScope.lineNumber() = getBlock().lineNumber();
+    eq ForStmtScope.lineNumber() = getForStmt().lineNumber();
+    eq EnhancedForStmtScope.lineNumber() = getEnhancedForStmt().lineNumber();
+    syn int JavaDeclaration.lineNumber();
+    eq SimpleJavaDeclaration.lineNumber() = getDeclarator().lineNumber();
+    eq JavaParameterDeclaration.lineNumber() = getParameterDeclaration().lineNumber();
+    eq JavaInferredLambdaParameterDeclaration.lineNumber() = getInferredParameterDeclaration().lineNumber();
 
-    public String TypeDeclScope.sourceFile() {
-      return getTypeDecl().sourceFile();
-    }
-    public String BlockScope.sourceFile() {
-      return getBlock().sourceFile();
-    }
-    public String ForStmtScope.sourceFile() {
-      return getForStmt().sourceFile();
-    }
-    public String EnhancedForStmtScope.sourceFile() {
-      return getEnhancedForStmt().sourceFile();
-    }
-    public String JavaDeclaration.sourceFile() {
-      return getDeclarator().sourceFile();
-    }
-    public String JavaParameterDeclaration.sourceFile() {
-      return getParameterDeclaration().sourceFile();
-    }
-    public String JavaInferredLambdaParameterDeclaration.sourceFile() {
-      return getInferredParameterDeclaration().sourceFile();
-    }
+    // --- sourceFile ---
+    syn String JavaScope.sourceFile();
+    eq TypeDeclScope.sourceFile() = getTypeDecl().sourceFile();
+    eq BlockScope.sourceFile() = getBlock().sourceFile();
+    eq ForStmtScope.sourceFile() = getForStmt().sourceFile();
+    eq EnhancedForStmtScope.sourceFile() = getEnhancedForStmt().sourceFile();
+    syn String JavaDeclaration.sourceFile();
+    eq SimpleJavaDeclaration.sourceFile() = getDeclarator().sourceFile();
+    eq JavaParameterDeclaration.sourceFile() = getParameterDeclaration().sourceFile();
+    eq JavaInferredLambdaParameterDeclaration.sourceFile() = getInferredParameterDeclaration().sourceFile();
 
+    // --- asJavaDeclaration ---
+    syn JavaDeclaration Declaration.asJavaDeclaration() = null;
+    eq JavaDeclaration.asJavaDeclaration() = this;
 }
diff --git a/scope4j/src/main/java/org/extendj/ScopeAnalysis.java b/scope4j/src/main/java/org/extendj/ScopeAnalysis.java
index 4409ba34b48dd9fc47d1c27aaafd82619ebe8d15..c6802fa0af437d36c7e77341d28f5ed219fa67c6 100644
--- a/scope4j/src/main/java/org/extendj/ScopeAnalysis.java
+++ b/scope4j/src/main/java/org/extendj/ScopeAnalysis.java
@@ -137,7 +137,7 @@ public class ScopeAnalysis extends Frontend {
   }
 
   private Program readProgram(Collection<String> files) throws IOException {
-
+    System.out.println("Reading " + (files.size() > 10 ? files.size() + " files" : files.toString()));
 
     Program program = new Program();
     program.resetStatistics();
diff --git a/scope4j/src/test/java/org/extendj/FieldsTest.java b/scope4j/src/test/java/org/extendj/FieldsTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..f08b58fe3ea8e6eaf3fb972ee1ac81a3cd56e663
--- /dev/null
+++ b/scope4j/src/test/java/org/extendj/FieldsTest.java
@@ -0,0 +1,19 @@
+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 FieldsTest extends ScopeAnalysisTest {
+
+  @Test
+  void test() {
+
+    ScopeAnalysis scopeAnalysis = new ScopeAnalysis();
+    Set<AbstractFinding> findings = scopeAnalysis.analyze("src/test/resources/fields", true, false);
+
+  }
+
+}
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
new file mode 100644
index 0000000000000000000000000000000000000000..d0ee15f843447579d5eb695ce68ed11f7150d9d9
--- /dev/null
+++ b/scope4j/src/test/java/org/extendj/InnerTest.java
@@ -0,0 +1,46 @@
+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 InnerTest extends ScopeAnalysisTest {
+
+  @Test
+  void test() {
+
+    ScopeAnalysis scopeAnalysis = new ScopeAnalysis();
+    Set<AbstractFinding> findings = scopeAnalysis.analyze("src/test/resources/inner", true, true);
+
+    System.out.println(findings);
+
+    // anonymous class
+    assertShadow(findings, "fieldA", "ClassA", 11, "ClassA", 13);
+    assertShadow(findings, "fieldA", "ClassA", 13, "ClassA", 3);
+
+    // local inner class
+    assertShadow(findings, "fieldA", "ClassA", 27, "ClassA", 29);
+    assertShadow(findings, "fieldA", "ClassA", 29, "ClassA", 3);
+    assertShadow(findings, "changingVar", "ClassA", 25, "ClassA", 19);
+
+    // static member class
+    assertShadow(findings, "fieldA", "ClassA", 37, "ClassA", 35);
+    assertShadow(findings, "fieldA", "ClassA", 35, "ClassA", 3);
+
+    // member class
+    assertShadow(findings, "fieldA", "ClassA", 44, "ClassA", 42);
+    assertShadow(findings, "fieldA", "ClassA", 42, "ClassA", 3);
+
+    // anonymous class defined in other class
+    assertShadow(findings, "fieldB", "ClassB", 5, "ClassB", 10);
+
+    // the anonymous class inherited a field
+    assertShadow(findings, "fieldB", "ClassB", 10, "ClassA", 4);
+
+
+    Assertions.assertEquals(11, findings.size());
+  }
+
+}
diff --git a/scope4j/src/test/java/org/extendj/ProtectedTest.java b/scope4j/src/test/java/org/extendj/ProtectedTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..3d53bb46300f7188caf02a89e8bff8c4a775d975
--- /dev/null
+++ b/scope4j/src/test/java/org/extendj/ProtectedTest.java
@@ -0,0 +1,113 @@
+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 ProtectedTest extends ScopeAnalysisTest {
+
+  @Test
+  void test() {
+
+    ScopeAnalysis scopeAnalysis = new ScopeAnalysis();
+    Set<AbstractFinding> findings = scopeAnalysis.analyze("src/test/resources/protected_", true, true);
+
+    System.out.println(findings);
+
+    final String classA = "src/test/resources/protected_/one/ClassA.java";
+    final String classB = "src/test/resources/protected_/one/ClassB.java";
+    final String classC = "src/test/resources/protected_/one/ClassC.java";
+    final String classD = "src/test/resources/protected_/two/ClassD.java";
+    final String classE = "src/test/resources/protected_/two/ClassE.java";
+
+    // within class A
+    // A.A()
+    assertShadow(findings, "fPrivate", classA, 19, classA, 9);
+    assertShadow(findings, "fPackage", classA, 20, classA, 10);
+    assertShadow(findings, "fProtected", classA, 21, classA, 11);
+    assertShadow(findings, "fPublic", classA, 22, classA, 12);
+    // A.foo()
+    assertShadow(findings, "fPrivate", classA, 26, classA, 9);
+    assertShadow(findings, "fPackage", classA, 27, classA, 10);
+    assertShadow(findings, "fProtected", classA, 28, classA, 11);
+    assertShadow(findings, "fPublic", classA, 29, classA, 12);
+    // A.bar()
+    assertShadow(findings, "fPrivate", classA, 32, classA, 9);
+    assertShadow(findings, "fPackage", classA, 33, classA, 10);
+    assertShadow(findings, "fProtected", classA, 34, classA, 11);
+    assertShadow(findings, "fPublic", classA, 35, classA, 12);
+
+    // within class B
+    assertShadow(findings, "fPackage", classB, 14, classA, 10);
+    assertShadow(findings, "fProtected", classB, 15, classA, 11);
+    assertShadow(findings, "fPublic", classB, 16, classA, 12);
+    // B.B()
+    assertShadow(findings, "fPrivate", classB, 19, classB, 13);
+    assertShadow(findings, "fPackage", classB, 20, classB, 14);
+    assertShadow(findings, "fProtected", classB, 21, classB, 15);
+    assertShadow(findings, "fPublic", classB, 22, classB, 16);
+    // B.foo()
+    assertShadow(findings, "fPrivate", classB, 26, classB, 13);
+    assertShadow(findings, "fPackage", classB, 27, classB, 14);
+    assertShadow(findings, "fProtected", classB, 28, classB, 15);
+    assertShadow(findings, "fPublic", classB, 29, classB, 16);
+    // B.bar()
+    assertShadow(findings, "fPrivate", classB, 32, classB, 13);
+    assertShadow(findings, "fPackage", classB, 33, classB, 14);
+    assertShadow(findings, "fProtected", classB, 34, classB, 15);
+    assertShadow(findings, "fPublic", classB, 35, classB, 16);
+
+    // within class C
+    // C.C()
+    assertShadow(findings, "fPackage", classC, 20, classA, 10);
+    assertShadow(findings, "fProtected", classC, 21, classA, 11);
+    assertShadow(findings, "fPublic", classC, 22, classA, 12);
+    // C.foo()
+    assertShadow(findings, "fPackage", classC, 27, classA, 10);
+    assertShadow(findings, "fProtected", classC, 28, classA, 11);
+    assertShadow(findings, "fPublic", classC, 29, classA, 12);
+    // C.bar()
+    assertShadow(findings, "fPackage", classC, 33, classA, 10);
+    assertShadow(findings, "fProtected", classC, 34, classA, 11);
+    assertShadow(findings, "fPublic", classC, 35, classA, 12);
+
+    // within class D
+    assertNotShadow(findings, "fPackage", classD, 20, classA, 10);
+    assertShadow(findings, "fProtected", classD, 15, classA, 11);
+    assertShadow(findings, "fPublic", classD, 16, classA, 12);
+    // D.D()
+    assertNotShadow(findings, "fPrivate", classD, 19, classA, 9); // true, but not helpful
+    assertNotShadow(findings, "fPackage", classD, 20, classA, 10); // true, but not helpful
+    assertShadow(findings, "fProtected", classD, 21, classD, 15);
+    assertShadow(findings, "fPublic", classD, 22, classD, 16);
+    // D.foo()
+    assertNotShadow(findings, "fPrivate", classD, 26, classA, 9); // true, but not helpful
+    assertNotShadow(findings, "fPackage", classD, 27, classA, 10); // true, but not helpful
+    assertShadow(findings, "fProtected", classD, 28, classD, 15);
+    assertShadow(findings, "fPublic", classD, 29, classD, 16);
+    // D.bar()
+    assertNotShadow(findings, "fPrivate", classD, 32, classA, 9); // true, but not helpful
+    assertNotShadow(findings, "fPackage", classD, 33, classA, 10); // true, but not helpful
+    assertShadow(findings, "fProtected", classD, 34, classD, 15);
+    assertShadow(findings, "fPublic", classD, 35, classD, 16);
+
+    // within class E
+    // E.E()
+    assertNotShadow(findings, "fPackage", classE, 20, classA, 10);
+    assertShadow(findings, "fProtected", classE, 21, classA, 11);
+    assertShadow(findings, "fPublic", classE, 22, classA, 12);
+    // E.foo()
+    assertNotShadow(findings, "fPackage", classE, 27, classA, 10);
+    assertShadow(findings, "fProtected", classE, 28, classA, 11);
+    assertShadow(findings, "fPublic", classE, 29, classA, 12);
+    // E.bar()
+    assertNotShadow(findings, "fPackage", classE, 33, classA, 14);
+    assertShadow(findings, "fProtected", classE, 34, classA, 11);
+    assertShadow(findings, "fPublic", classE, 35, classA, 12);
+
+    Assertions.assertEquals(56, findings.size());
+  }
+
+}
diff --git a/scope4j/src/test/java/org/extendj/ScopeAnalysisTest.java b/scope4j/src/test/java/org/extendj/ScopeAnalysisTest.java
index 95d479ce7b90b3d7deb7684ea36c07c3dba1777a..f54ba0fa2060ac9b76c155824a91153a42e83c09 100644
--- a/scope4j/src/test/java/org/extendj/ScopeAnalysisTest.java
+++ b/scope4j/src/test/java/org/extendj/ScopeAnalysisTest.java
@@ -1,25 +1,37 @@
 package org.extendj;
 
-import org.extendj.ast.AbstractFinding;
-import org.extendj.ast.Declaration;
-import org.extendj.ast.MultipleDeclarationFinding;
-import org.extendj.ast.VariableShadowFinding;
+import org.extendj.ast.*;
 import org.junit.jupiter.api.Assertions;
 
 import java.util.Set;
+import java.util.function.BiFunction;
+import java.util.function.Supplier;
 
 public abstract class ScopeAnalysisTest {
-  static void assertShadow(Set<AbstractFinding> findings, String name, int shadowerLine, int shadowedLine) {
+  private static void assertShadow(Set<AbstractFinding> findings,
+                            BiFunction<JavaDeclaration, JavaDeclaration, Boolean> test,
+                            Supplier<String> failMessage) {
     for (AbstractFinding finding : findings) {
       if (finding instanceof VariableShadowFinding) {
-        Declaration shadower = ((VariableShadowFinding)finding).getShadower();
-        Declaration shadowed = ((VariableShadowFinding)finding).getShadowed();
-        if (shadowed.getName().equals(name) && shadowed.lineNumber() == shadowedLine && shadower.lineNumber() == shadowerLine) {
+        JavaDeclaration shadower = ((VariableShadowFinding)finding).getShadower().asJavaDeclaration();
+        JavaDeclaration shadowed = ((VariableShadowFinding)finding).getShadowed().asJavaDeclaration();
+        if (test.apply(shadower, shadowed)) {
           return;
         }
       }
     }
-    Assertions.fail("No shadow finding found for name '" + name + "' in lines " + shadowerLine + " > " + shadowedLine);
+    Assertions.fail(failMessage.get());
+  }
+
+  static void assertShadow(Set<AbstractFinding> findings, String name, String shadowerSourceFile, int shadowerLine, String shadowedSourceFile, int shadowedLine) {
+    assertShadow(findings,
+        (shadower, shadowed) ->
+            shadowed.getName().equals(name) &&
+                shadowed.sourceFile().contains(shadowedSourceFile) &&
+                shadowed.lineNumber() == shadowedLine &&
+                shadower.sourceFile().contains(shadowerSourceFile) &&
+                shadower.lineNumber() == shadowerLine,
+        () -> "No shadow finding found for name '" + name + "' in " +shadowerSourceFile + ":" + shadowerLine + " > " + shadowedSourceFile + ":" + shadowedLine);
   }
 
   static void assertRedefinition(Set<AbstractFinding> findings, String name, int declLine) {
@@ -33,4 +45,13 @@ public abstract class ScopeAnalysisTest {
     }
     Assertions.fail("No multi-decl finding found for name '" + name + "' in line " + declLine);
   }
+
+  static void assertNotShadow(Set<AbstractFinding> findings, String name, String shadowerSourceFile, int shadowerLine, String shadowedSourceFile, int shadowedLine) {
+    try {
+      assertShadow(findings, name, shadowerSourceFile, shadowerLine, shadowedSourceFile, shadowedLine);
+    } catch (AssertionError e) {
+      return;
+    }
+    Assertions.fail("There should not be a shadow finding for name '" + name + "' in " + shadowerSourceFile + ":" + shadowerLine + " > " + shadowedSourceFile + ":" + shadowedLine);
+  }
 }
diff --git a/scope4j/src/test/java/org/extendj/SimpleScopeTest.java b/scope4j/src/test/java/org/extendj/SimpleScopeTest.java
index 2bb7111bfc3b5b754ce4d9c1012226713f5a0e9d..ae16c02891af19193758b8400ce236aebdd34bf9 100644
--- a/scope4j/src/test/java/org/extendj/SimpleScopeTest.java
+++ b/scope4j/src/test/java/org/extendj/SimpleScopeTest.java
@@ -12,18 +12,15 @@ public class SimpleScopeTest extends ScopeAnalysisTest {
   void test() {
 
     ScopeAnalysis scopeAnalysis = new ScopeAnalysis();
-    Set<AbstractFinding> findings = scopeAnalysis.analyze("src/test/resources/simple", false, false);
+    Set<AbstractFinding> findings = scopeAnalysis.analyze("src/test/resources/simple", true, false);
 
+    assertShadow(findings, "localVarA", "ClassA", 19, "ClassA", 11);
+    assertShadow(findings, "localVarB", "ClassA", 33, "ClassA", 12);
+    assertShadow(findings, "localVarC", "ClassA", 24, "ClassA", 13);
+    assertShadow(findings, "fieldA", "ClassA", 29, "ClassA", 3);
+    assertShadow(findings, "fieldB", "ClassA", 36, "ClassA", 4);
 
     Assertions.assertEquals(5, findings.size());
-
-    assertShadow(findings, "localVarA", 19, 11);
-    assertShadow(findings, "localVarB", 33, 12);
-    assertShadow(findings, "localVarC", 24, 13);
-    assertShadow(findings, "fieldA", 29, 3);
-    assertShadow(findings, "fieldB", 36, 4);
-
-
   }
 
 }
diff --git a/scope4j/src/test/java/org/extendj/SuperclassFieldsTest.java b/scope4j/src/test/java/org/extendj/SuperclassFieldsTest.java
index e32199d181284dd4a6528ac93ae15d76e04180b2..6f01c7e2daf89de2df98953194d1d9e23d0c1b36 100644
--- a/scope4j/src/test/java/org/extendj/SuperclassFieldsTest.java
+++ b/scope4j/src/test/java/org/extendj/SuperclassFieldsTest.java
@@ -13,15 +13,14 @@ public class SuperclassFieldsTest extends ScopeAnalysisTest {
   void test() {
 
     ScopeAnalysis scopeAnalysis = new ScopeAnalysis();
-    Set<AbstractFinding> findings = scopeAnalysis.analyze("src/test/resources/superclassFields", true, false);
+    Set<AbstractFinding> findings = scopeAnalysis.analyze("src/test/resources/superclassFields", false, false);
 
 
-    assertShadow(findings, "fieldC", 19, 3);
-    assertShadow(findings, "fieldB", 21, 4);
-    assertRedefinition(findings, "fieldB", 2);
-    assertRedefinition(findings, "fieldB", 4);
+    assertShadow(findings, "fieldC", "ClassB", 19, "ClassB", 3);
+    assertShadow(findings, "fieldB", "ClassB", 21, "ClassB", 2);
+    assertShadow(findings, "fieldB", "ClassB", 2, "ClassA", 4);
 
-    Assertions.assertEquals(4, findings.size());
+    Assertions.assertEquals(3, findings.size());
 
   }
 
diff --git a/scope4j/src/test/resources/fields/ClassA.java b/scope4j/src/test/resources/fields/ClassA.java
new file mode 100644
index 0000000000000000000000000000000000000000..a94c5b76f0b5cc267a77ba1dc017dcd43c6ebe9f
--- /dev/null
+++ b/scope4j/src/test/resources/fields/ClassA.java
@@ -0,0 +1,15 @@
+public abstract class ClassA {
+​
+  public int publicField;
+  protected int protectedField;
+  int packageField;
+  private int privateField;
+
+  void n(int parameter) {
+    int member;
+    {
+      int memberInBlock;
+    }
+  }
+​
+}
diff --git a/scope4j/src/test/resources/inner/ClassA.java b/scope4j/src/test/resources/inner/ClassA.java
new file mode 100644
index 0000000000000000000000000000000000000000..c9dd1fa19fa804b33a9eb664ed58b6e400280727
--- /dev/null
+++ b/scope4j/src/test/resources/inner/ClassA.java
@@ -0,0 +1,47 @@
+public abstract class ClassA {
+
+  int fieldA;
+  int fieldB;
+
+  abstract void toBeDefined();
+
+  void method1() {
+    ClassA anonymous = new ClassA() {
+      void toBeDefined() {
+        int fieldA = 11;
+      }
+      int fieldA = 1;
+    };
+  }
+
+  void method2() {
+    final int finalVar = 1;
+    int changingVar = 0;
+    changingVar = 1;  // changingVar is not-final and not-effective-final, thus can not be used in InnerA
+    class InnerA extends ClassA {
+      /* This variable shares the name, but actually could never reference the outer scope
+         We include it anyway, because a) it would obscure analysis for this edge-case, and b) warns for potentially
+          unwanted effects (as all shadowing-warnings do) */
+      int changingVar = 4;
+      void toBeDefined() {
+        int fieldA = 21 + changingVar + finalVar;
+      }
+      int fieldA = 2;
+    }
+    ClassA inner = new InnerA();
+  }
+
+  static class StaticMemberClass extends ClassA {
+    int fieldA = 3;
+    void toBeDefined() {
+      int fieldA = 31;
+    }
+  }
+
+  class MemberClass extends ClassA {
+    int fieldA = 4;
+    void toBeDefined() {
+      int fieldA = 41;
+    }
+  }
+}
diff --git a/scope4j/src/test/resources/inner/ClassB.java b/scope4j/src/test/resources/inner/ClassB.java
new file mode 100644
index 0000000000000000000000000000000000000000..3213a0f7f5f67c18ce9a0385b94bfb08ca897b27
--- /dev/null
+++ b/scope4j/src/test/resources/inner/ClassB.java
@@ -0,0 +1,13 @@
+public class ClassB {
+  void anonymousClassFromOtherSourceFile() {
+    ClassA anonymous = new ClassA() {
+      void toBeDefined() {
+        int fieldB = 11;
+      }
+
+      /* false-negative. there should be two scopes: ClassA and this method of ClassB
+       * But there is only the method, thus, not shadowing of ClassA.fieldB is detected. */
+      int fieldB = 1;
+    }
+  }
+}
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;
+  }
+}
diff --git a/scope4j/src/test/resources/protected_/one/ClassA.java b/scope4j/src/test/resources/protected_/one/ClassA.java
new file mode 100644
index 0000000000000000000000000000000000000000..9929229498cee9696281fb62d9f28de648b53023
--- /dev/null
+++ b/scope4j/src/test/resources/protected_/one/ClassA.java
@@ -0,0 +1,38 @@
+/*
+ As of https://docs.oracle.com/javase/tutorial/java/package/namingpkgs.html, an underscore shall be added to
+ avoid conflicts with Java keywords.
+*/
+package protected_.one;
+
+public class ClassA {
+
+  private int fPrivate = 0;
+  int fPackage = 0;
+  protected int fProtected = 0;
+  public int fPublic = 0;
+
+
+
+
+
+  protected ClassA() {
+    int fPrivate = 1;
+    int fPackage = 1;
+    int fProtected = 1;
+    int fPublic = 1;
+  }
+
+  void foo() {
+    int fPrivate = 2;
+    int fPackage = 2;
+    int fProtected = 2;
+    int fPublic = 2;
+  }
+
+  void bar(int fPrivate,
+           int fPackage,
+           int fProtected,
+           int fPublic) {
+    // empty
+  }
+}
diff --git a/scope4j/src/test/resources/protected_/one/ClassB.java b/scope4j/src/test/resources/protected_/one/ClassB.java
new file mode 100644
index 0000000000000000000000000000000000000000..bde099846c3ce8b8fb4c17b60ba1b07c0c8ddde8
--- /dev/null
+++ b/scope4j/src/test/resources/protected_/one/ClassB.java
@@ -0,0 +1,38 @@
+/*
+ As of https://docs.oracle.com/javase/tutorial/java/package/namingpkgs.html, an underscore shall be added to
+ avoid conflicts with Java keywords.
+*/
+package protected_.one;
+
+class ClassB extends ClassA {
+
+
+
+
+
+  private int fPrivate = 0;
+  int fPackage = 0;
+  protected int fProtected = 0;
+  public int fPublic = 0;
+
+  ClassB() {
+    int fPrivate = 1;
+    int fPackage = 1;
+    int fProtected = 1;
+    int fPublic = 1;
+  }
+
+  void foo() {
+    int fPrivate = 2;
+    int fPackage = 2;
+    int fProtected = 2;
+    int fPublic = 2;
+  }
+
+  void bar(int fPrivate,
+           int fPackage,
+           int fProtected,
+           int fPublic) {
+    // empty
+  }
+}
diff --git a/scope4j/src/test/resources/protected_/one/ClassC.java b/scope4j/src/test/resources/protected_/one/ClassC.java
new file mode 100644
index 0000000000000000000000000000000000000000..5fc85fdd3e7390f533f3111ff9149c9e44299d70
--- /dev/null
+++ b/scope4j/src/test/resources/protected_/one/ClassC.java
@@ -0,0 +1,38 @@
+/*
+ As of https://docs.oracle.com/javase/tutorial/java/package/namingpkgs.html, an underscore shall be added to
+ avoid conflicts with Java keywords.
+*/
+package protected_.one;
+
+class ClassC extends ClassA {
+
+
+
+
+
+
+
+
+
+
+  ClassC() {
+    int fPrivate = 1;
+    int fPackage = 1;
+    int fProtected = 1;
+    int fPublic = 1;
+  }
+
+  void foo() {
+    int fPrivate = 2;
+    int fPackage = 2;
+    int fProtected = 2;
+    int fPublic = 2;
+  }
+
+  void bar(int fPrivate,
+           int fPackage,
+           int fProtected,
+           int fPublic) {
+    // empty
+  }
+}
diff --git a/scope4j/src/test/resources/protected_/two/ClassD.java b/scope4j/src/test/resources/protected_/two/ClassD.java
new file mode 100644
index 0000000000000000000000000000000000000000..b5fe8e114e989e3b165f6cc77f95f5aa77c52cd7
--- /dev/null
+++ b/scope4j/src/test/resources/protected_/two/ClassD.java
@@ -0,0 +1,38 @@
+/*
+ As of https://docs.oracle.com/javase/tutorial/java/package/namingpkgs.html, an underscore shall be added to
+ avoid conflicts with Java keywords.
+*/
+package protected_.two;
+import protected_.one.ClassA;
+class ClassD extends ClassA {
+
+
+
+
+
+  int fPrivate = 0;
+  int fPackage = 0;
+  int fProtected = 0;
+  int fPublic = 0;
+
+  ClassD() {
+    int fPrivate = 1;
+    int fPackage = 1;
+    int fProtected = 1;
+    int fPublic = 1;
+  }
+
+  void foo() {
+    int fPrivate = 2;
+    int fPackage = 2;
+    int fProtected = 2;
+    int fPublic = 2;
+  }
+
+  void bar(int fPrivate,
+           int fPackage,
+           int fProtected,
+           int fPublic) {
+    // empty
+  }
+}
diff --git a/scope4j/src/test/resources/protected_/two/ClassE.java b/scope4j/src/test/resources/protected_/two/ClassE.java
new file mode 100644
index 0000000000000000000000000000000000000000..84c0d0aba6f92f0b086edf9c76c491f2f7b7da90
--- /dev/null
+++ b/scope4j/src/test/resources/protected_/two/ClassE.java
@@ -0,0 +1,38 @@
+/*
+ As of https://docs.oracle.com/javase/tutorial/java/package/namingpkgs.html, an underscore shall be added to
+ avoid conflicts with Java keywords.
+*/
+package protected_.two;
+
+class ClassE extends protected_.one.ClassA {
+
+
+
+
+
+
+
+
+
+
+  ClassE() {
+    int fPrivate = 1;
+    int fPackage = 1;
+    int fProtected = 1;
+    int fPublic = 1;
+  }
+
+  void foo() {
+    int fPrivate = 2;
+    int fPackage = 2;
+    int fProtected = 2;
+    int fPublic = 2;
+  }
+
+  void bar(int fPrivate,
+           int fPackage,
+           int fProtected,
+           int fPublic) {
+    // empty
+  }
+}
diff --git a/scope4j/src/test/resources/simple/ClassA.java b/scope4j/src/test/resources/simple/ClassA.java
index a066a2aa6bdf14ea306e3c0c077723250ced13fb..4cd7ec11b7fb1f8f21cb133aec2ce25501ea5f4c 100644
--- a/scope4j/src/test/resources/simple/ClassA.java
+++ b/scope4j/src/test/resources/simple/ClassA.java
@@ -37,6 +37,7 @@ public abstract class ClassA {
     ) { /* do stuff */ } catch (java.io.IOException e) {/* do stuff */}
   }
 
-  // this does not appear as a scope (and, more importantly, the parameters are not added anywhere else)
+  // these do not appear as a scope (and, more importantly, the parameters are not added anywhere else)
   public abstract void methodNameB(int parameterForAbstractMethodB);
+  public abstract void methodNameC(int fieldA);
 }
diff --git a/scope4j/src/test/resources/superclassFields/ClassA.java b/scope4j/src/test/resources/superclassFields/ClassA.java
index 675b9360cbfcb0764893486d7dcc1946b2c10085..1bd25cd71c600406dc7af4bf5040a3def7753497 100644
--- a/scope4j/src/test/resources/superclassFields/ClassA.java
+++ b/scope4j/src/test/resources/superclassFields/ClassA.java
@@ -1,12 +1,12 @@
 public abstract class ClassA {
-​
+
   int fieldA;
   int fieldB;
 
-  void m();
+  abstract void m();
 
   void n() {
     //...
   }
-​
+
 }