Skip to content
Snippets Groups Projects
Commit 8a6ad345 authored by Johannes Mey's avatar Johannes Mey
Browse files

Merge branch 'visibilityscopes' into modelica

parents 8d3e2019 c31c3c87
No related branches found
No related tags found
No related merge requests found
Showing
with 538 additions and 121 deletions
...@@ -2,3 +2,5 @@ ScopeTree : Scope; ...@@ -2,3 +2,5 @@ ScopeTree : Scope;
abstract Element; abstract Element;
Declaration:Element ::= <Name:String>; Declaration:Element ::= <Name:String>;
Scope:Element ::= Element*; Scope:Element ::= Element*;
rel Scope.inheritedScope* -> Scope;
...@@ -4,25 +4,26 @@ aspect Shadowing { ...@@ -4,25 +4,26 @@ aspect Shadowing {
Declaration contributes new VariableShadowFinding(shadowed(), this) when isShadowing() to ScopeTree.variableShadowings(); Declaration contributes new VariableShadowFinding(shadowed(), this) when isShadowing() to ScopeTree.variableShadowings();
Declaration contributes new MultipleDeclarationFinding(this) when isShadowingInSameScope() to ScopeTree.variableShadowings(); Declaration contributes new MultipleDeclarationFinding(this) when isShadowingInSameScope() to ScopeTree.variableShadowings();
inh Declaration Declaration.shadowed(); syn Declaration Declaration.shadowed()= shadowed(asDeclaration());
eq Scope.getElement(int i).shadowed() = shadowed(getElement(i).asDeclaration());
inh Declaration Element.shadowed(Declaration shadower); 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()) { for (Declaration declaration : declarations()) {
if (declaration != shadower && declaration.getName().equals(shadower.getName())) { if (declaration != shadower && declaration.getName().equals(shadower.getName())) {
return declaration; return declaration;
} }
} }
return null; // the look in the inherited scopes
} for (Scope inherited : getInheritedScopeList()) {
eq Scope.getElement().shadowed(Declaration shadower) { Declaration shadowed = inherited.shadowedLocally(shadower);
for (Declaration declaration : declarations()) { if (shadowed != null) {
if (declaration != shadower && declaration.getName().equals(shadower.getName())) { return shadowed;
return declaration;
} }
} }
return shadowed(shadower); return (this instanceof ScopeTree) ? null : shadowed(shadower);
} }
inh Declaration Declaration.shadowedInSameScope(); inh Declaration Declaration.shadowedInSameScope();
...@@ -39,8 +40,6 @@ aspect Shadowing { ...@@ -39,8 +40,6 @@ aspect Shadowing {
syn boolean Declaration.isShadowing() = shadowed() != null; syn boolean Declaration.isShadowing() = shadowed() != null;
syn boolean Declaration.isShadowingInSameScope() = shadowedInSameScope() != null; syn boolean Declaration.isShadowingInSameScope() = shadowedInSameScope() != null;
} }
aspect Statictics { aspect Statictics {
......
aspect ProgramToScopeTree { aspect ProgramToScopeTree {
/** a relational nta collection attribute to compute the scope tree */
syn lazy ScopeTree Program.scopeTree() { syn lazy ScopeTree Program.scopeTree() {
ScopeTree tree = new ScopeTree(); ScopeTree tree = asScopeTree();
tree.setProgram(this);
for (CompilationUnit compilationUnit : getCompilationUnitList()) // add all classes
for (TypeDecl typeDecl : compilationUnit.getTypeDeclList()) for (CompilationUnit cu : getCompilationUnitList()) {
tree.addElement(typeDecl.scope()); 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; 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; 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 // 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(); Block contributes scope() to ASTNode.scope() for containingScope();
ForStmt contributes scope() when !(getStmt() instanceof Block) 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(); EnhancedForStmt contributes scope() when !(getStmt() instanceof Block) to ASTNode.scope() for containingScope();
// collect all elements // 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(); ParameterDeclaration contributes asDeclaration() to ASTNode.scope() for containingScope();
}
Collection<Declaration> ClassDecl.createSuperClassFieldDeclarators() { /**
ArrayList<Declaration> result = new ArrayList<>(); * aspect containing helper methods to construct (mostly empty) AST nodes of the scope tree
TypeDecl supertype = superclass(); * There are few parts added manually, but stubs could easily be generated from the mapping grammar
*/
aspect ScopeTreeConstructors {
System.out.println("supertype " + supertype.getID()); syn lazy ScopeTree Program.asScopeTree() {
while (supertype.isClassDecl() && supertype != unknownType()) { ScopeTree tree = new ScopeTree();
for (BodyDecl bodyDecl : supertype.getBodyDeclList()) { tree.setProgram(this);
if (bodyDecl instanceof FieldDecl) { return tree;
for (FieldDeclarator declarator : ((FieldDecl)bodyDecl).getDeclaratorList()) {
JavaDeclaration declaration = new JavaDeclaration(declarator.getID());
declaration.setDeclarator(declarator);
result.add(declaration);
}
}
}
supertype = ((ClassDecl)supertype).superclass();
}
return result;
} }
/** fallback attribute to ensure every AST element could pontentially be a scope */ /** fallback attribute to ensure every AST element could pontentially be a scope */
...@@ -51,11 +77,26 @@ aspect ProgramToScopeTree { ...@@ -51,11 +77,26 @@ aspect ProgramToScopeTree {
syn lazy TypeDeclScope TypeDecl.asScope() { syn lazy TypeDeclScope TypeDecl.asScope() {
TypeDeclScope scope = new TypeDeclScope(); TypeDeclScope scope = new TypeDeclScope();
scope.setTypeDecl(this); scope.setTypeDecl(this);
if (isClassDecl()) { return scope;
for (Declaration declaration : ((ClassDecl)this).createSuperClassFieldDeclarators()) {
scope.addElement(declaration);
} }
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; return scope;
} }
...@@ -77,8 +118,8 @@ aspect ProgramToScopeTree { ...@@ -77,8 +118,8 @@ aspect ProgramToScopeTree {
return scope; return scope;
} }
syn lazy JavaDeclaration Declarator.asDeclaration() { syn lazy SimpleJavaDeclaration Declarator.asDeclaration() {
JavaDeclaration decl = new JavaDeclaration(getID()); SimpleJavaDeclaration decl = new SimpleJavaDeclaration(getID());
decl.setDeclarator(this); decl.setDeclarator(this);
return decl; return decl;
} }
...@@ -113,4 +154,8 @@ aspect ScopeGenerationAttributes { ...@@ -113,4 +154,8 @@ aspect ScopeGenerationAttributes {
eq EnhancedForStmt.getVariableDecl().containingScope() = (getStmt() instanceof Block) ? getStmt() : this; eq EnhancedForStmt.getVariableDecl().containingScope() = (getStmt() instanceof Block) ? getStmt() : this;
eq ForStmt.getInitStmt().containingScope() = (getStmt() instanceof Block) ? getStmt() : this; eq ForStmt.getInitStmt().containingScope() = (getStmt() instanceof Block) ? getStmt() : this;
eq ForStmt.getUpdateStmt().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();
} }
// glue relation for the Java-based variable shadowing analysis // glue relation for the Java-based variable shadowing analysis
rel ScopeTree.Program -> Program; rel ScopeTree.Program -> Program;
TypeDeclScope : Scope; abstract JavaScope : Scope;
TypeDeclScope : JavaScope;
rel TypeDeclScope.typeDecl -> TypeDecl; rel TypeDeclScope.typeDecl -> TypeDecl;
BlockScope : Scope; ProtectedClassDeclScope : TypeDeclScope;
PackageClassDeclScope : TypeDeclScope;
PrivateClassDeclScope : TypeDeclScope;
BlockScope : JavaScope;
rel BlockScope.block -> Block; rel BlockScope.block -> Block;
ForStmtScope : Scope; ForStmtScope : JavaScope;
rel ForStmtScope.forStmt -> ForStmt; rel ForStmtScope.forStmt -> ForStmt;
EnhancedForStmtScope : Scope; EnhancedForStmtScope : JavaScope;
rel EnhancedForStmtScope.enhancedForStmt -> EnhancedForStmt; rel EnhancedForStmtScope.enhancedForStmt -> EnhancedForStmt;
JavaDeclaration : Declaration; abstract JavaDeclaration : Declaration ;
rel JavaDeclaration.declarator -> Declarator; SimpleJavaDeclaration : JavaDeclaration;
rel SimpleJavaDeclaration.declarator -> Declarator;
JavaParameterDeclaration : Declaration; JavaParameterDeclaration : JavaDeclaration;
rel JavaParameterDeclaration.parameterDeclaration -> ParameterDeclaration; rel JavaParameterDeclaration.parameterDeclaration -> ParameterDeclaration;
JavaInferredLambdaParameterDeclaration : Declaration; JavaInferredLambdaParameterDeclaration : JavaDeclaration;
rel JavaInferredLambdaParameterDeclaration.inferredParameterDeclaration -> InferredParameterDeclaration; rel JavaInferredLambdaParameterDeclaration.inferredParameterDeclaration -> InferredParameterDeclaration;
aspect Shadow{ aspect Shadow{
// --- toString ---
eq TypeDeclScope.toString() = getTypeDecl().getClass().getSimpleName() + "-Scope"; eq TypeDeclScope.toString() = getTypeDecl().getClass().getSimpleName() + "-Scope";
eq BlockScope.toString() = getBlock().getClass().getSimpleName() + "-Scope"; eq BlockScope.toString() = getBlock().getClass().getSimpleName() + "-Scope";
eq ForStmtScope.toString() = getForStmt().getClass().getSimpleName() + "-Scope"; eq ForStmtScope.toString() = getForStmt().getClass().getSimpleName() + "-Scope";
eq EnhancedForStmtScope.toString() = getEnhancedForStmt().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 JavaParameterDeclaration.toString() = getParameterDeclaration().getClass().getSimpleName() + ":" + super.toString();
eq JavaInferredLambdaParameterDeclaration.toString() = getInferredParameterDeclaration().getClass().getSimpleName() + ":" + super.toString(); eq JavaInferredLambdaParameterDeclaration.toString() = getInferredParameterDeclaration().getClass().getSimpleName() + ":" + super.toString();
public int TypeDeclScope.lineNumber(){ // --- lineNumber ---
return getTypeDecl().lineNumber(); syn int JavaScope.lineNumber();
} eq TypeDeclScope.lineNumber() = getTypeDecl().lineNumber();
public int BlockScope.lineNumber(){ eq BlockScope.lineNumber() = getBlock().lineNumber();
return getBlock().lineNumber(); eq ForStmtScope.lineNumber() = getForStmt().lineNumber();
} eq EnhancedForStmtScope.lineNumber() = getEnhancedForStmt().lineNumber();
public int ForStmtScope.lineNumber(){ syn int JavaDeclaration.lineNumber();
return getForStmt().lineNumber(); eq SimpleJavaDeclaration.lineNumber() = getDeclarator().lineNumber();
} eq JavaParameterDeclaration.lineNumber() = getParameterDeclaration().lineNumber();
public int EnhancedForStmtScope.lineNumber(){ eq JavaInferredLambdaParameterDeclaration.lineNumber() = getInferredParameterDeclaration().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();
}
public String TypeDeclScope.sourceFile() { // --- sourceFile ---
return getTypeDecl().sourceFile(); syn String JavaScope.sourceFile();
} eq TypeDeclScope.sourceFile() = getTypeDecl().sourceFile();
public String BlockScope.sourceFile() { eq BlockScope.sourceFile() = getBlock().sourceFile();
return getBlock().sourceFile(); eq ForStmtScope.sourceFile() = getForStmt().sourceFile();
} eq EnhancedForStmtScope.sourceFile() = getEnhancedForStmt().sourceFile();
public String ForStmtScope.sourceFile() { syn String JavaDeclaration.sourceFile();
return getForStmt().sourceFile(); eq SimpleJavaDeclaration.sourceFile() = getDeclarator().sourceFile();
} eq JavaParameterDeclaration.sourceFile() = getParameterDeclaration().sourceFile();
public String EnhancedForStmtScope.sourceFile() { eq JavaInferredLambdaParameterDeclaration.sourceFile() = getInferredParameterDeclaration().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();
}
// --- asJavaDeclaration ---
syn JavaDeclaration Declaration.asJavaDeclaration() = null;
eq JavaDeclaration.asJavaDeclaration() = this;
} }
...@@ -137,7 +137,7 @@ public class ScopeAnalysis extends Frontend { ...@@ -137,7 +137,7 @@ public class ScopeAnalysis extends Frontend {
} }
private Program readProgram(Collection<String> files) throws IOException { 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 program = new Program();
program.resetStatistics(); program.resetStatistics();
......
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);
}
}
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());
}
}
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());
}
}
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());
}
}
package org.extendj; package org.extendj;
import org.extendj.ast.AbstractFinding; import org.extendj.ast.*;
import org.extendj.ast.Declaration;
import org.extendj.ast.MultipleDeclarationFinding;
import org.extendj.ast.VariableShadowFinding;
import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Assertions;
import java.util.Set; import java.util.Set;
import java.util.function.BiFunction;
import java.util.function.Supplier;
public abstract class ScopeAnalysisTest { 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) { for (AbstractFinding finding : findings) {
if (finding instanceof VariableShadowFinding) { if (finding instanceof VariableShadowFinding) {
Declaration shadower = ((VariableShadowFinding)finding).getShadower(); JavaDeclaration shadower = ((VariableShadowFinding)finding).getShadower().asJavaDeclaration();
Declaration shadowed = ((VariableShadowFinding)finding).getShadowed(); JavaDeclaration shadowed = ((VariableShadowFinding)finding).getShadowed().asJavaDeclaration();
if (shadowed.getName().equals(name) && shadowed.lineNumber() == shadowedLine && shadower.lineNumber() == shadowerLine) { if (test.apply(shadower, shadowed)) {
return; 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) { static void assertRedefinition(Set<AbstractFinding> findings, String name, int declLine) {
...@@ -33,4 +45,13 @@ public abstract class ScopeAnalysisTest { ...@@ -33,4 +45,13 @@ public abstract class ScopeAnalysisTest {
} }
Assertions.fail("No multi-decl finding found for name '" + name + "' in line " + declLine); 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);
}
} }
...@@ -12,18 +12,15 @@ public class SimpleScopeTest extends ScopeAnalysisTest { ...@@ -12,18 +12,15 @@ public class SimpleScopeTest extends ScopeAnalysisTest {
void test() { void test() {
ScopeAnalysis scopeAnalysis = new ScopeAnalysis(); 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()); 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);
} }
} }
...@@ -13,15 +13,14 @@ public class SuperclassFieldsTest extends ScopeAnalysisTest { ...@@ -13,15 +13,14 @@ public class SuperclassFieldsTest extends ScopeAnalysisTest {
void test() { void test() {
ScopeAnalysis scopeAnalysis = new ScopeAnalysis(); 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, "fieldC", "ClassB", 19, "ClassB", 3);
assertShadow(findings, "fieldB", 21, 4); assertShadow(findings, "fieldB", "ClassB", 21, "ClassB", 2);
assertRedefinition(findings, "fieldB", 2); assertShadow(findings, "fieldB", "ClassB", 2, "ClassA", 4);
assertRedefinition(findings, "fieldB", 4);
Assertions.assertEquals(4, findings.size()); Assertions.assertEquals(3, findings.size());
} }
......
public abstract class ClassA {
public int publicField;
protected int protectedField;
int packageField;
private int privateField;
void n(int parameter) {
int member;
{
int memberInBlock;
}
}
}
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;
}
}
}
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;
}
}
}
public class ClassA {
public int fieldA;
}
public class ClassB {
int fieldB;
class ClassC extends ClassA {
int fieldA;
int fieldB;
}
}
/*
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
}
}
/*
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
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment