/** * BSD-style license; for more info see http://pmd.sourceforge.net/license.html */ package net.sourceforge.pmd.lang.java.symboltable; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import java.util.Iterator; import java.util.List; import java.util.Map; import org.junit.Test; import net.sourceforge.pmd.PMD; import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclaratorId; import net.sourceforge.pmd.lang.java.ast.DummyJavaNode; import net.sourceforge.pmd.lang.java.ast.JavaNode; import net.sourceforge.pmd.lang.java.symboltable.testdata.InnerClass; import net.sourceforge.pmd.lang.java.symboltable.testdata.InnerClass.TheInnerClass; import net.sourceforge.pmd.lang.java.symboltable.testdata.InnerClass.TheInnerClass.EnumTest; import net.sourceforge.pmd.lang.symboltable.NameDeclaration; import net.sourceforge.pmd.lang.symboltable.NameOccurrence; public class ClassScopeTest extends STBBaseTst { @Test public void testEnumsClassScope() { parseCode15(ENUM_SCOPE); } @Test public void testEnumTypeParameter() { parseCode15(ENUM_TYPE_PARAMETER); } @Test public void testVarArgsEmpty() { parseCode15("public class Foo {\n" + " public void bar1(String s, Integer... i) {}\n" + " public void bar1() {}\n" + " public void c() {\n" + " bar1();\n" + " }\n" + "}\n"); } // FIXME - these will break when this goes from Anonymous$1 to Foo$1 @Test public void testAnonymousInnerClassName() { ClassNameDeclaration classDeclaration = new ClassNameDeclaration(null); ClassScope s = new ClassScope("Foo", classDeclaration); s = new ClassScope(classDeclaration); assertEquals("Anonymous$1", s.getClassName()); s = new ClassScope(classDeclaration); assertEquals("Anonymous$2", s.getClassName()); } @Test public void testContains() { ClassNameDeclaration classDeclaration = new ClassNameDeclaration(null); ClassScope s = new ClassScope("Foo", classDeclaration); ASTVariableDeclaratorId node = new ASTVariableDeclaratorId(1); node.setImage("bar"); s.addDeclaration(new VariableNameDeclaration(node)); assertTrue(s.getDeclarations().keySet().iterator().hasNext()); } @Test public void testCantContainsSuperToString() { ClassNameDeclaration classDeclaration = new ClassNameDeclaration(null); ClassScope s = new ClassScope("Foo", classDeclaration); JavaNode node = new DummyJavaNode(1); node.setImage("super.toString"); assertFalse(s.contains(new JavaNameOccurrence(node, node.getImage()))); } @Test public void testContainsStaticVariablePrefixedWithClassName() { ClassNameDeclaration classDeclaration = new ClassNameDeclaration(null); ClassScope s = new ClassScope("Foo", classDeclaration); ASTVariableDeclaratorId node = new ASTVariableDeclaratorId(1); node.setImage("X"); s.addDeclaration(new VariableNameDeclaration(node)); JavaNode node2 = new DummyJavaNode(2); node2.setImage("Foo.X"); assertTrue(s.contains(new JavaNameOccurrence(node2, node2.getImage()))); } @Test public void testClassName() { parseCode(CLASS_NAME); ASTClassOrInterfaceDeclaration n = acu.findDescendantsOfType(ASTClassOrInterfaceDeclaration.class).get(0); assertEquals("Foo", n.getScope().getEnclosingScope(ClassScope.class).getClassName()); } @Test public void testMethodDeclarationRecorded() { parseCode(METHOD_DECLARATIONS_RECORDED); ASTClassOrInterfaceDeclaration n = acu.findDescendantsOfType(ASTClassOrInterfaceDeclaration.class).get(0); ClassScope s = (ClassScope) n.getScope(); Map<NameDeclaration, List<NameOccurrence>> m = s.getDeclarations(); assertEquals(1, m.size()); MethodNameDeclaration mnd = (MethodNameDeclaration) m.keySet().iterator().next(); assertEquals("bar", mnd.getImage()); ASTMethodDeclaration node = (ASTMethodDeclaration) mnd.getNode().jjtGetParent(); assertTrue(node.isPrivate()); } @Test public void testTwoMethodsSameNameDiffArgs() { // TODO this won't work with String and java.lang.String parseCode(METHODS_WITH_DIFF_ARG); ASTClassOrInterfaceDeclaration n = acu.findDescendantsOfType(ASTClassOrInterfaceDeclaration.class).get(0); Map<NameDeclaration, List<NameOccurrence>> m = ((ClassScope) n.getScope()).getDeclarations(); assertEquals(2, m.size()); Iterator<NameDeclaration> i = m.keySet().iterator(); MethodNameDeclaration mnd = (MethodNameDeclaration) i.next(); assertEquals("bar", mnd.getImage()); assertEquals("bar", ((MethodNameDeclaration) i.next()).getImage()); } @Test public final void testOneParam() { parseCode(ONE_PARAM); ASTClassOrInterfaceDeclaration n = acu.findDescendantsOfType(ASTClassOrInterfaceDeclaration.class).get(0); Map<NameDeclaration, List<NameOccurrence>> m = ((ClassScope) n.getScope()).getDeclarations(); MethodNameDeclaration mnd = (MethodNameDeclaration) m.keySet().iterator().next(); assertEquals("(String)", mnd.getParameterDisplaySignature()); } @Test public final void testTwoParams() { parseCode(TWO_PARAMS); ASTClassOrInterfaceDeclaration n = acu.findDescendantsOfType(ASTClassOrInterfaceDeclaration.class).get(0); Map<NameDeclaration, List<NameOccurrence>> m = ((ClassScope) n.getScope()).getDeclarations(); MethodNameDeclaration mnd = (MethodNameDeclaration) m.keySet().iterator().next(); assertEquals("(String,int)", mnd.getParameterDisplaySignature()); } @Test public final void testNoParams() { parseCode(NO_PARAMS); ASTClassOrInterfaceDeclaration n = acu.findDescendantsOfType(ASTClassOrInterfaceDeclaration.class).get(0); Map<NameDeclaration, List<NameOccurrence>> m = ((ClassScope) n.getScope()).getDeclarations(); MethodNameDeclaration mnd = (MethodNameDeclaration) m.keySet().iterator().next(); assertEquals("()", mnd.getParameterDisplaySignature()); } @Test public final void testOneParamVararg() { parseCode15(ONE_PARAM_VARARG); ASTClassOrInterfaceDeclaration n = acu.findDescendantsOfType(ASTClassOrInterfaceDeclaration.class).get(0); Map<NameDeclaration, List<NameOccurrence>> m = ((ClassScope) n.getScope()).getDeclarations(); MethodNameDeclaration mnd = (MethodNameDeclaration) m.keySet().iterator().next(); assertEquals("(String...)", mnd.getParameterDisplaySignature()); } @Test public final void testTwoParamsVararg() { parseCode15(TWO_PARAMS_VARARG); ASTClassOrInterfaceDeclaration n = acu.findDescendantsOfType(ASTClassOrInterfaceDeclaration.class).get(0); Map<NameDeclaration, List<NameOccurrence>> m = ((ClassScope) n.getScope()).getDeclarations(); MethodNameDeclaration mnd = (MethodNameDeclaration) m.keySet().iterator().next(); assertEquals("(String,String...)", mnd.getParameterDisplaySignature()); } @Test public void testNestedClassesOfImportResolution() { parseCode(NESTED_CLASSES_OF_IMPORT); final ASTClassOrInterfaceDeclaration n = acu.findDescendantsOfType(ASTClassOrInterfaceDeclaration.class).get(0); final ClassScope c = (ClassScope) n.getScope(); assertEquals(EnumTest.class, c.resolveType("TheInnerClass.EnumTest")); } @Test public void testNestedClassesResolution() { parseForClass(InnerClass.class); final ASTClassOrInterfaceDeclaration n = acu.findDescendantsOfType(ASTClassOrInterfaceDeclaration.class).get(0); final ClassScope c = (ClassScope) n.getScope(); assertEquals(InnerClass.class, c.resolveType("InnerClass")); assertEquals(TheInnerClass.class, c.resolveType("InnerClass.TheInnerClass")); assertEquals(TheInnerClass.class, c.resolveType("TheInnerClass")); // Within this scope, we can access it directly } @Test public void testImportNestedClassesResolution() { parseCode(IMPORT_NESTED_CLASSES); final ASTClassOrInterfaceDeclaration n = acu.findDescendantsOfType(ASTClassOrInterfaceDeclaration.class).get(0); final ClassScope c = (ClassScope) n.getScope(); assertEquals(EnumTest.class, c.resolveType("EnumTest")); } @Test public final void testNestedClassDeclFound() { parseCode(NESTED_CLASS_FOUND); ASTClassOrInterfaceDeclaration n = acu.findDescendantsOfType(ASTClassOrInterfaceDeclaration.class).get(0); ClassScope c = (ClassScope) n.getScope(); Map<NameDeclaration, List<NameOccurrence>> m = c.getDeclarations(); ClassNameDeclaration cnd = (ClassNameDeclaration) m.keySet().iterator().next(); assertEquals("Buz", cnd.getImage()); } @Test public final void testbuz() { parseCode(METH); // SymbolTableViewer st = new SymbolTableViewer(); // acu.jjtAccept(st, null); } @Test public void testMethodUsageSeen() { parseCode(METHOD_USAGE_SEEN); ASTClassOrInterfaceDeclaration n = acu.findDescendantsOfType(ASTClassOrInterfaceDeclaration.class).get(0); Map<NameDeclaration, List<NameOccurrence>> m = ((ClassScope) n.getScope()).getDeclarations(); Iterator<Map.Entry<NameDeclaration, List<NameOccurrence>>> i = m.entrySet().iterator(); MethodNameDeclaration mnd; Map.Entry<NameDeclaration, List<NameOccurrence>> entry; do { entry = i.next(); mnd = (MethodNameDeclaration) entry.getKey(); } while (!mnd.getImage().equals("bar")); List<NameOccurrence> usages = entry.getValue(); assertEquals(1, usages.size()); assertEquals("bar", ((JavaNameOccurrence) usages.get(0)).getImage()); } @Test public void testMethodUsageSeenWithThis() { parseCode(METHOD_USAGE_SEEN_WITH_THIS); ASTClassOrInterfaceDeclaration n = acu.findDescendantsOfType(ASTClassOrInterfaceDeclaration.class).get(0); Map<NameDeclaration, List<NameOccurrence>> m = ((ClassScope) n.getScope()).getDeclarations(); Iterator<Map.Entry<NameDeclaration, List<NameOccurrence>>> i = m.entrySet().iterator(); MethodNameDeclaration mnd; Map.Entry<NameDeclaration, List<NameOccurrence>> entry; do { entry = i.next(); mnd = (MethodNameDeclaration) entry.getKey(); } while (!mnd.getImage().equals("bar")); List<NameOccurrence> usages = entry.getValue(); assertEquals(1, usages.size()); assertEquals("bar", ((JavaNameOccurrence) usages.get(0)).getImage()); } @Test public void testMethodUsageSeen2() { parseCode(METHOD_USAGE_SEEN2); ASTClassOrInterfaceDeclaration n = acu.findDescendantsOfType(ASTClassOrInterfaceDeclaration.class).get(0); Map<NameDeclaration, List<NameOccurrence>> m = ((ClassScope) n.getScope()).getDeclarations(); assertEquals(2, m.size()); for (Map.Entry<NameDeclaration, List<NameOccurrence>> entry : m.entrySet()) { assertEquals("baz", entry.getKey().getImage()); if (entry.getKey().getNode().getBeginLine() == 2) { // this is the public method declaration - it is not used // anywhere assertEquals(0, entry.getValue().size()); } else if (entry.getKey().getNode().getBeginLine() == 5) { // this is the private (overloaded) method assertEquals(1, entry.getValue().size()); // it's used once in line 3 assertEquals(3, entry.getValue().get(0).getLocation().getBeginLine()); } else { fail("unexpected name declaration"); } } } /** * Test case for bug report #2410201 */ @Test public void testNestedClassFieldAndParameter() { parseCode(NESTED_CLASS_FIELD_AND_PARAM); ASTMethodDeclaration node = acu.getFirstDescendantOfType(ASTMethodDeclaration.class); Map<NameDeclaration, List<NameOccurrence>> vd = node.getScope().getDeclarations(); assertEquals(1, vd.size()); for (Map.Entry<NameDeclaration, List<NameOccurrence>> entry : vd.entrySet()) { assertEquals("field", entry.getKey().getImage()); List<NameOccurrence> occurrences = entry.getValue(); assertEquals(2, occurrences.size()); NameOccurrence no1 = occurrences.get(0); assertEquals(8, no1.getLocation().getBeginLine()); NameOccurrence no2 = occurrences.get(1); assertEquals(9, no2.getLocation().getBeginLine()); } } @Test public void testNullType() { parseCode(TEST_NULL_TYPE); } private static final String NESTED_CLASS_FIELD_AND_PARAM = "public class Foo {" + PMD.EOL + " class Test {" + PMD.EOL + " public String field;" + PMD.EOL + " public Test t;" + PMD.EOL + " }" + PMD.EOL + " public void foo(String field) {" + PMD.EOL + " Test t = new Test();" + PMD.EOL + " t.field = field;" + PMD.EOL + " t.t.field = field;" + PMD.EOL + " }" + PMD.EOL + "}"; private static final String METHOD_USAGE_SEEN2 = "public class Foo {" + PMD.EOL + " public void baz() {" + PMD.EOL + " baz(x, y);" + PMD.EOL + " }" + PMD.EOL + " private void baz(int x, int y) {}" + PMD.EOL + "}"; private static final String METHOD_USAGE_SEEN = "public class Foo {" + PMD.EOL + " private void bar() {}" + PMD.EOL + " public void buz() {" + PMD.EOL + " bar();" + PMD.EOL + " }" + PMD.EOL + "}"; private static final String METHOD_USAGE_SEEN_WITH_THIS = "public class Foo {" + PMD.EOL + " private void bar() {}" + PMD.EOL + " public void buz() {" + PMD.EOL + " this.bar();" + PMD.EOL + " }" + PMD.EOL + "}"; private static final String METH = "public class Test {" + PMD.EOL + " static { " + PMD.EOL + " int y; " + PMD.EOL + " } " + PMD.EOL + " void bar(int x) {} " + PMD.EOL + " void baz(int x) {} " + PMD.EOL + "}"; private static final String NESTED_CLASS_FOUND = "public class Test {" + PMD.EOL + " private class Buz {} " + PMD.EOL + "}"; private static final String ONE_PARAM = "public class Test {" + PMD.EOL + " void bar(String x) {" + PMD.EOL + " }" + PMD.EOL + "}"; private static final String TWO_PARAMS = "public class Test {" + PMD.EOL + " void bar(String x, int y) {" + PMD.EOL + " }" + PMD.EOL + "}"; private static final String NO_PARAMS = "public class Test {" + PMD.EOL + " void bar() {" + PMD.EOL + " }" + PMD.EOL + "}"; private static final String ONE_PARAM_VARARG = "public class Test {" + PMD.EOL + " void bar(String... s) {" + PMD.EOL + " }" + PMD.EOL + "}"; private static final String TWO_PARAMS_VARARG = "public class Test {" + PMD.EOL + " void bar(String s1, String... s2) {" + PMD.EOL + " }" + PMD.EOL + "}"; private static final String CLASS_NAME = "public class Foo {}"; private static final String METHOD_DECLARATIONS_RECORDED = "public class Foo {" + PMD.EOL + " private void bar() {}" + PMD.EOL + "}"; private static final String METHODS_WITH_DIFF_ARG = "public class Foo {" + PMD.EOL + " private void bar(String x) {}" + PMD.EOL + " private void bar() {}" + PMD.EOL + "}"; private static final String ENUM_SCOPE = "public enum Foo {" + PMD.EOL + " HEAP(\"foo\");" + PMD.EOL + " private final String fuz;" + PMD.EOL + " public String getFuz() {" + PMD.EOL + " return fuz;" + PMD.EOL + " }" + PMD.EOL + "}"; public static final String TEST_NULL_TYPE = "public abstract class NullTypeTest {" + PMD.EOL + " protected Comparator<TreeNode> nodesComparator = (o1, o2) -> StringHelper.saveCompare(getFilterableString(o1), getFilterableString(o2));" + PMD.EOL + " public abstract String getFilterableString(TreeNode node);" + PMD.EOL + "}"; private static final String ENUM_TYPE_PARAMETER = "public enum Foo {" + PMD.EOL + " BAR(isCustomer(BazEnum.FOO_BAR));" + PMD.EOL + " Foo(boolean isCustomer) { }" + PMD.EOL + " private static boolean isCustomer(BazEnum baz) {" + PMD.EOL + " return false;" + PMD.EOL + " }" + PMD.EOL + "}"; private static final String IMPORT_NESTED_CLASSES = "import net.sourceforge.pmd.lang.java.symboltable.testdata.InnerClass.TheInnerClass.EnumTest;" + PMD.EOL + "public class Foo {" + PMD.EOL + " public EnumTest e;" + PMD.EOL + "}" + PMD.EOL; private static final String NESTED_CLASSES_OF_IMPORT = "import net.sourceforge.pmd.lang.java.symboltable.testdata.InnerClass.TheInnerClass;" + PMD.EOL + "public class Foo {" + PMD.EOL + " public TheInnerClass.EnumTest e;" + PMD.EOL + "}" + PMD.EOL; }