extends JElementSymbol {
- @Override
- @Nullable
- N getDeclaration();
}
diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/JAccessibleElementSymbol.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/JAccessibleElementSymbol.java
new file mode 100644
index 0000000000..ffc49a8293
--- /dev/null
+++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/JAccessibleElementSymbol.java
@@ -0,0 +1,60 @@
+/*
+ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
+ */
+
+
+package net.sourceforge.pmd.lang.java.symbols;
+
+import java.lang.reflect.Modifier;
+
+import org.checkerframework.checker.nullness.qual.NonNull;
+import org.checkerframework.checker.nullness.qual.Nullable;
+
+/**
+ * Represents declarations having access modifiers common to {@link JFieldSymbol},
+ * {@link JClassSymbol}, {@link JMethodSymbol}, and {@link JConstructorSymbol}.
+ *
+ * @author Clément Fournier
+ * @since 7.0.0
+ */
+public interface JAccessibleElementSymbol extends JElementSymbol {
+
+ /**
+ * Conventional return value of {@link #getPackageName()} for
+ * primitive types.
+ */
+ String PRIMITIVE_PACKAGE = "java.lang";
+
+
+ /**
+ * Returns the modifiers of the element represented by this symbol,
+ * as decodable by the standard {@link Modifier} API.
+ */
+ int getModifiers();
+
+
+ /**
+ * Returns the class that directly encloses this declaration.
+ * This is equivalent to {@link Class#getEnclosingClass()}.
+ * Returns null if this is a top-level type declaration.
+ *
+ * This is necessarily an already resolved symbol, because
+ * 1. if it's obtained from reflection, then the enclosing class is available
+ * 2. if it's obtained from an AST, then the enclosing class is in the same source file so we can
+ * know about it
+ */
+ @Nullable
+ JClassSymbol getEnclosingClass();
+
+
+ /**
+ * Returns the name of the package this element is declared in. This
+ * recurses into the enclosing elements if needed. If this is an array
+ * symbol, returns the package name of the element symbol. If this is
+ * a primitive type, returns {@value #PRIMITIVE_PACKAGE}.
+ *
+ *
This is consistent with Java 9's {@code getPackageName()}.
+ */
+ @NonNull
+ String getPackageName();
+}
diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/JClassSymbol.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/JClassSymbol.java
new file mode 100644
index 0000000000..0bf6d20ae8
--- /dev/null
+++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/JClassSymbol.java
@@ -0,0 +1,210 @@
+/*
+ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
+ */
+
+
+package net.sourceforge.pmd.lang.java.symbols;
+
+import java.lang.reflect.Modifier;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import org.apache.commons.lang3.NotImplementedException;
+import org.checkerframework.checker.nullness.qual.NonNull;
+import org.checkerframework.checker.nullness.qual.Nullable;
+
+import net.sourceforge.pmd.lang.java.ast.ASTAnyTypeDeclaration;
+
+
+/**
+ * Abstraction over a {@link Class} instance. This is not a type, it's
+ * the *declaration* of a type. For example, a class symbol representing
+ * a generic class can provide access to the formal type parameters, but
+ * the symbol does not represent a specific parametrization of a type.
+ *
+ *
Class symbols represent the full range of types represented by {@link Class}:
+ * classes, interfaces, arrays, and primitives. This excludes type variables,
+ * intersection types, parameterized types, wildcard types, etc., which are only
+ * compile-time constructs.
+ *
+ * @author Clément Fournier
+ * @since 7.0.0
+ */
+public interface JClassSymbol extends JTypeDeclSymbol,
+ JTypeParameterOwnerSymbol,
+ BoundToNode {
+
+ /**
+ * Returns the binary name of this type, as specified by the JLS:
+ * the JLS.
+ * For array types this returns the binary name of the component followed by "[]".
+ */
+ @NonNull
+ String getBinaryName();
+
+
+
+ /**
+ * Returns the simple name of this class, as specified by
+ * {@link Class#getCanonicalName()}.
+ */
+ @Nullable
+ String getCanonicalName();
+
+
+ /**
+ * Returns true if this class is a symbolic reference to an unresolved
+ * class. In that case no information about the symbol are known except
+ * its name, and the accessors of this class return default values.
+ *
+ * This kind of symbol is introduced to allow for some best-effort
+ * symbolic resolution. For example in:
+ *
{@code
+ * import org.Bar;
+ *
+ * Bar foo = new Bar();
+ * }
+ * and supposing {@code org.Bar} is not on the classpath. The type
+ * of {@code foo} is {@code Bar}, which we can qualify to {@code org.Bar} thanks to the
+ * import (via symbol tables, and without even querying the classpath).
+ * Even though we don't know what members {@code org.Bar} has, a
+ * test for {@code typeIs("org.Bar")} would succeed with certainty,
+ * so it makes sense to preserve the name information and not give
+ * up too early.
+ *
+ * Note that unresolved types are always created from an unresolved
+ * canonical name, so they can't be just any type. For example,
+ * they can't be array types, nor local classes (since those are lexically
+ * scoped, so always resolvable), nor anonymous classes (can only be referenced
+ * on their declaration site), etc.
+ */
+ boolean isUnresolved();
+
+
+ /**
+ * Returns the method or constructor this symbol is declared in, if
+ * it represents a {@linkplain #isLocalClass() local class declaration}.
+ *
+ *
Notice, that this returns null also if this class is local to
+ * a class or instance initializer.
+ */
+ @Nullable
+ default JExecutableSymbol getEnclosingMethod() {
+ throw new NotImplementedException("TODO, trickier than it appears");
+ }
+
+
+ @Override
+ default JTypeParameterOwnerSymbol getEnclosingTypeParameterOwner() {
+ JExecutableSymbol enclosingMethod = getEnclosingMethod();
+ return enclosingMethod != null ? enclosingMethod : getEnclosingClass();
+ }
+
+
+ /**
+ * Returns the member classes declared directly in this class.
+ *
+ * @see Class#getDeclaredClasses()
+ */
+ List getDeclaredClasses();
+
+
+ /**
+ * Returns the methods declared directly in this class.
+ * This excludes bridges and other synthetic methods.
+ *
+ * For an array type T[], to the difference of {@link Class},
+ * this method returns a one-element list with the {@link Cloneable#clone()}
+ * method, as if declared like so: {@code public final T[] clone() {...}}.
+ *
+ * @see Class#getDeclaredMethods()
+ */
+ List getDeclaredMethods();
+
+
+ /**
+ * Returns the constructors declared by this class.
+ * This excludes synthetic constructors.
+ *
+ * For an array type T[], and to the difference of {@link Class},
+ * this should return a one-element list with a constructor
+ * having the same modifiers as the array type, and a single
+ * {@code int} parameter.
+ *
+ * @see Class#getDeclaredConstructors()
+ */
+ List getConstructors();
+
+
+ /**
+ * Returns the fields declared directly in this class.
+ * This excludes synthetic fields.
+ *
+ * For arrays, and to the difference of {@link Class},
+ * this should return a one-element list with the
+ * {@code public final int length} field.
+ *
+ * @see Class#getDeclaredFields()
+ */
+ List getDeclaredFields();
+
+
+ /** Returns a field with the given name accessed defined in this class. */
+ @Nullable
+ default JFieldSymbol getDeclaredField(String name) {
+ for (JFieldSymbol field : getDeclaredFields()) {
+ if (field.getSimpleName().equals(name)) {
+ return field;
+ }
+ }
+ return null;
+ }
+
+
+ /** Returns all methods with the given name declared in this class. */
+ default List getDeclaredMethods(String name) {
+ return getDeclaredMethods().stream().filter(it -> it.getSimpleName().equals(name)).collect(Collectors.toList());
+ }
+
+
+ /**
+ * Returns the superclass symbol if it exists. Returns null if this
+ * class represents an interface or the class {@link Object}.
+ */
+ @Nullable
+ JClassSymbol getSuperclass();
+
+
+ /** Returns the direct super-interfaces of this class or interface symbol. */
+ List getSuperInterfaces();
+
+
+ default boolean isAbstract() {
+ return Modifier.isAbstract(getModifiers());
+ }
+
+
+ /** Returns the component symbol, returns null if this is not an array. */
+ @Nullable
+ JTypeDeclSymbol getArrayComponent();
+
+
+ boolean isArray();
+
+ boolean isPrimitive();
+
+ boolean isInterface();
+
+ boolean isEnum();
+
+ boolean isAnnotation();
+
+ boolean isLocalClass();
+
+ boolean isAnonymousClass();
+
+ default boolean isClass() {
+ return !isInterface() && !isArray() && !isPrimitive();
+ }
+
+}
diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/JConstructorSymbol.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/JConstructorSymbol.java
new file mode 100644
index 0000000000..b9d1dcb007
--- /dev/null
+++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/JConstructorSymbol.java
@@ -0,0 +1,28 @@
+/*
+ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
+ */
+
+
+package net.sourceforge.pmd.lang.java.symbols;
+
+import net.sourceforge.pmd.lang.java.ast.ASTConstructorDeclaration;
+
+
+/**
+ * Represents a constructor declaration.
+ *
+ * @author Clément Fournier
+ * @since 7.0.0
+ */
+public interface JConstructorSymbol extends JExecutableSymbol, BoundToNode {
+
+ /** Common dummy name for constructor symbols. */
+ String CTOR_NAME = "new";
+
+
+ /** For constructors, this returns the special name {@value CTOR_NAME}. */
+ @Override
+ default String getSimpleName() {
+ return CTOR_NAME;
+ }
+}
diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/JElementSymbol.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/JElementSymbol.java
new file mode 100644
index 0000000000..f00e2e7462
--- /dev/null
+++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/JElementSymbol.java
@@ -0,0 +1,85 @@
+/*
+ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
+ */
+
+
+package net.sourceforge.pmd.lang.java.symbols;
+
+import net.sourceforge.pmd.annotation.Experimental;
+import net.sourceforge.pmd.annotation.InternalApi;
+import net.sourceforge.pmd.lang.java.symbols.internal.impl.SymbolFactory;
+import net.sourceforge.pmd.lang.symboltable.NameDeclaration;
+
+
+/**
+ * Represents a named program element that can be referred to by simple name. Abstracts over
+ * whether the declaration is in the analysed file or not, using reflection when it's not.
+ *
+ * This type hierarchy is probably not directly relevant to users writing
+ * rules. It's mostly intended to unify the representation of type resolution
+ * and symbol analysis.
+ *
+ *
SymbolDeclarations have no reference to the scope they were found in, because
+ * that would tie the code reference to the analysed file, preventing the garbage
+ * collection of scopes and nodes. This is a major difference with {@link NameDeclaration}.
+ * The declaring scope would also vary from file to file. E.g.
+ *
+ *
+ * class Foo {
+ * public int foo;
+ * // here the declaring scope of Foo#foo would be the class scope of this file
+ * }
+ *
+ * class Bar extends Foo {
+ * // here the declaring scope of Foo#foo would be the inherited scope from Foo
+ * }
+ *
+ *
+ * By storing no reference, we ensure that code references can be shared across the
+ * analysed project, allowing reflective resolution to be only done once.
+ *
+ *
+ * @author Clément Fournier
+ * @since 7.0.0
+ */
+@Experimental
+@InternalApi
+public interface JElementSymbol {
+
+
+ /**
+ * Gets the name with which this declaration may be referred to,
+ * eg the name of the method, or the simple name of the class.
+ *
+ * @return the name
+ */
+ String getSimpleName();
+
+
+ /**
+ * Two symbols representing the same program element should be equal.
+ * So eg two {@link JClassSymbol}, even if their implementation class
+ * is different, should compare publicly observable properties (their
+ * binary name is enough). {@link #hashCode()} must of course be consistent
+ * with this contract.
+ *
+ *
Symbols should only be compared using this method, never with {@code ==},
+ * because their unicity is not guaranteed (even for the static ones
+ * declared in {@link SymbolFactory}).
+ *
+ * @param o Comparand
+ *
+ * @return True if the other is a symbol of the same type and
+ */
+ @Override
+ boolean equals(Object o);
+
+ // TODO access to annotations could be added to the API if we publish it
+
+ // TODO tests
+
+ // TODO add type information when TypeDefinitions are reviewed
+ // We should be able to create a type definition from a java.lang.reflect.Type,
+ // paying attention to type variables of enclosing methods and types.
+ // We should also be able to do so from an ASTType, with support from a JSymbolTable.
+}
diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/JExecutableSymbol.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/JExecutableSymbol.java
new file mode 100644
index 0000000000..0a769cb05b
--- /dev/null
+++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/JExecutableSymbol.java
@@ -0,0 +1,55 @@
+/*
+ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
+ */
+
+package net.sourceforge.pmd.lang.java.symbols;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+import java.util.List;
+
+import org.checkerframework.checker.nullness.qual.NonNull;
+
+/**
+ * Common supertype for {@linkplain JMethodSymbol method}
+ * and {@linkplain JConstructorSymbol constructor symbols}.
+ *
+ * @author Clément Fournier
+ */
+public interface JExecutableSymbol extends JAccessibleElementSymbol, JTypeParameterOwnerSymbol {
+
+
+ /** Returns the formal parameters this executable declares. */
+ List getFormalParameters();
+
+
+ /** Returns true if the last formal parameter is a varargs parameter. */
+ boolean isVarargs();
+
+
+ /**
+ * Returns the number of formal parameters expected. This must be the
+ * length of {@link #getFormalParameters()} but if it can be implemented
+ * without creating the formal parameters, it should.
+ *
+ * A varargs parameter counts as a single parameter.
+ */
+ int getArity();
+
+
+ /**
+ * Returns the class symbol declaring this method or constructor.
+ * This is similar to {@link Constructor#getDeclaringClass()}, resp.
+ * {@link Method#getDeclaringClass()}. Never null.
+ */
+ @Override
+ @NonNull
+ JClassSymbol getEnclosingClass();
+
+
+ @Override
+ @NonNull
+ default String getPackageName() {
+ return getEnclosingClass().getPackageName();
+ }
+}
diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/JFieldSymbol.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/JFieldSymbol.java
new file mode 100644
index 0000000000..d82aee49c7
--- /dev/null
+++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/JFieldSymbol.java
@@ -0,0 +1,41 @@
+/*
+ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
+ */
+
+
+package net.sourceforge.pmd.lang.java.symbols;
+
+import java.lang.reflect.Modifier;
+
+import org.checkerframework.checker.nullness.qual.NonNull;
+
+/**
+ * Represents a field declaration.
+ *
+ * @author Clément Fournier
+ * @since 7.0.0
+ */
+public interface JFieldSymbol extends JAccessibleElementSymbol, JValueSymbol {
+
+
+ /** Returns true if this field is an enum constant. */
+ boolean isEnumConstant();
+
+
+ @Override
+ default boolean isFinal() {
+ return Modifier.isFinal(getModifiers());
+ }
+
+
+ @Override
+ @NonNull JClassSymbol getEnclosingClass();
+
+
+ @Override
+ @NonNull
+ default String getPackageName() {
+ return getEnclosingClass().getPackageName();
+ }
+
+}
diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/JFormalParamSymbol.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/JFormalParamSymbol.java
new file mode 100644
index 0000000000..a0e179d22e
--- /dev/null
+++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/JFormalParamSymbol.java
@@ -0,0 +1,19 @@
+/*
+ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
+ */
+
+
+package net.sourceforge.pmd.lang.java.symbols;
+
+/**
+ * Represents a formal parameter of a {@link JExecutableSymbol}.
+ *
+ * @author Clément Fournier
+ * @since 7.0.0
+ */
+public interface JFormalParamSymbol extends JValueSymbol {
+
+ /** Returns the symbol declaring this parameter. */
+ JExecutableSymbol getDeclaringSymbol();
+
+}
diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/JLocalVariableSymbol.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/JLocalVariableSymbol.java
similarity index 62%
rename from pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/JLocalVariableSymbol.java
rename to pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/JLocalVariableSymbol.java
index 580463a8c2..f7e42ceec5 100644
--- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/JLocalVariableSymbol.java
+++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/JLocalVariableSymbol.java
@@ -1,16 +1,16 @@
-/**
+/*
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
-package net.sourceforge.pmd.lang.java.symbols.internal;
+
+package net.sourceforge.pmd.lang.java.symbols;
import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclaratorId;
/**
- * Represents all use cases of {@link ASTVariableDeclaratorId} except field declarations.
- * TODO do we need to split those into their own type of reference? This is e.g. done in INRIA/Spoon,
- * but for now doesn't appear to be an interesting tradeoff
+ * Represents all use cases of {@link ASTVariableDeclaratorId} except field declarations
+ * and method parameters.
*
* @author Clément Fournier
* @since 7.0.0
diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/JMethodSymbol.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/JMethodSymbol.java
new file mode 100644
index 0000000000..4859d09254
--- /dev/null
+++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/JMethodSymbol.java
@@ -0,0 +1,29 @@
+/*
+ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
+ */
+
+
+package net.sourceforge.pmd.lang.java.symbols;
+
+import java.lang.reflect.Modifier;
+
+import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration;
+
+
+/**
+ * Reference to a method.
+ *
+ * @author Clément Fournier
+ * @since 7.0.0
+ */
+public interface JMethodSymbol extends JExecutableSymbol, BoundToNode {
+
+ /** Returns true if this method is a default method of an interface. */
+ default boolean isDefault() {
+ // Default methods are public non-abstract instance methods
+ // declared in an interface.
+ return (getModifiers() & (Modifier.ABSTRACT | Modifier.PUBLIC | Modifier.STATIC)) == Modifier.PUBLIC
+ && getEnclosingClass().isInterface();
+ }
+
+}
diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/JTypeDeclSymbol.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/JTypeDeclSymbol.java
new file mode 100644
index 0000000000..aa748ce10f
--- /dev/null
+++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/JTypeDeclSymbol.java
@@ -0,0 +1,46 @@
+/*
+ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
+ */
+
+
+package net.sourceforge.pmd.lang.java.symbols;
+
+import org.checkerframework.checker.nullness.qual.NonNull;
+import org.checkerframework.checker.nullness.qual.Nullable;
+
+/**
+ * A symbol that declares a type. These include
+ *
+ * - {@linkplain JClassSymbol class, interface, array & primitive symbols}
+ * - {@linkplain JTypeParameterSymbol type parameters symbols}
+ *
+ *
+ * Note: type symbols are not types, they declare types.
+ *
+ * @author Clément Fournier
+ * @since 7.0.0
+ */
+public interface JTypeDeclSymbol extends JElementSymbol, JAccessibleElementSymbol {
+
+
+ /**
+ * Returns the reflected class this node represents, if it's on the auxclasspath.
+ * There's no guarantee that this is even exists (this symbol may be notional).
+ *
+ *
This is provided to optimize some stuff, but ideally the symbol
+ * API should reflect everything there is to know about classes,
+ * and this method shouldn't be used.
+ */
+ @Nullable
+ Class> getJvmRepr();
+
+
+ /**
+ * Returns the simple name of this class, as specified by
+ * {@link Class#getSimpleName()}.
+ */
+ @Override
+ @NonNull
+ String getSimpleName();
+
+}
diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/JTypeParameterOwnerSymbol.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/JTypeParameterOwnerSymbol.java
new file mode 100644
index 0000000000..d58528ae66
--- /dev/null
+++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/JTypeParameterOwnerSymbol.java
@@ -0,0 +1,36 @@
+/*
+ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
+ */
+
+
+package net.sourceforge.pmd.lang.java.symbols;
+
+import java.util.List;
+
+
+/**
+ * Represents a declaration that can declare type parameters,
+ * {@literal i.e.} {@link JClassSymbol} or {@link JMethodSymbol}.
+ *
+ * @author Clément Fournier
+ * @since 7.0.0
+ */
+public interface JTypeParameterOwnerSymbol extends JAccessibleElementSymbol {
+
+ List getTypeParameters();
+
+
+ default int getTypeParameterCount() {
+ return getTypeParameters().size();
+ }
+
+
+ /**
+ * Returns the {@link JClassSymbol#getEnclosingMethod() enclosing method} or
+ * the {@link #getEnclosingClass() enclosing class}, in that order
+ * of priority.
+ */
+ default JTypeParameterOwnerSymbol getEnclosingTypeParameterOwner() {
+ return getEnclosingClass();
+ }
+}
diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/JTypeParameterSymbol.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/JTypeParameterSymbol.java
new file mode 100644
index 0000000000..8cd87c5f9a
--- /dev/null
+++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/JTypeParameterSymbol.java
@@ -0,0 +1,53 @@
+/*
+ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
+ */
+
+
+package net.sourceforge.pmd.lang.java.symbols;
+
+import java.lang.reflect.Modifier;
+
+import org.checkerframework.checker.nullness.qual.NonNull;
+import org.checkerframework.checker.nullness.qual.Nullable;
+
+import net.sourceforge.pmd.lang.java.ast.ASTTypeParameter;
+
+
+/**
+ * Represents the declaration of a type variable, ie a type parameter. Type variables are reference
+ * types, but not class or interface types. They're also not declared with the same node. For those
+ * reasons this type of references is distinct from {@link JClassSymbol}.
+ *
+ * @author Clément Fournier
+ * @since 7.0.0
+ */
+public interface JTypeParameterSymbol extends JTypeDeclSymbol, BoundToNode {
+
+
+ /**
+ * Returns the {@link JClassSymbol} or {@link JMethodSymbol} which declared
+ * this type parameter.
+ */
+ JTypeParameterOwnerSymbol getDeclaringSymbol();
+
+
+ @Override
+ @NonNull
+ default String getPackageName() {
+ return getDeclaringSymbol().getPackageName();
+ }
+
+
+ @Override
+ default int getModifiers() {
+ return getDeclaringSymbol().getModifiers() | Modifier.ABSTRACT | Modifier.FINAL;
+ }
+
+
+ @Override
+ @Nullable
+ default JClassSymbol getEnclosingClass() {
+ JTypeParameterOwnerSymbol ownerSymbol = getDeclaringSymbol();
+ return ownerSymbol instanceof JClassSymbol ? (JClassSymbol) ownerSymbol : ownerSymbol.getEnclosingClass();
+ }
+}
diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/JValueSymbol.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/JValueSymbol.java
new file mode 100644
index 0000000000..2fa94363cd
--- /dev/null
+++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/JValueSymbol.java
@@ -0,0 +1,25 @@
+/*
+ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
+ */
+
+
+package net.sourceforge.pmd.lang.java.symbols;
+
+import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclaratorId;
+
+
+/**
+ * Reference to a value, ie {@linkplain JLocalVariableSymbol local variable},
+ * {@linkplain JFormalParamSymbol formal parameter}, or {@linkplain JFieldSymbol field}.
+ *
+ * @author Clément Fournier
+ * @since 7.0.0
+ */
+public interface JValueSymbol extends BoundToNode {
+
+ /**
+ * Returns true if this declaration is declared final.
+ * This takes implicit modifiers into account.
+ */
+ boolean isFinal();
+}
diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/SymbolResolver.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/SymbolResolver.java
new file mode 100644
index 0000000000..435f31e2ff
--- /dev/null
+++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/SymbolResolver.java
@@ -0,0 +1,33 @@
+/*
+ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
+ */
+
+package net.sourceforge.pmd.lang.java.symbols;
+
+import org.checkerframework.checker.nullness.qual.NonNull;
+import org.checkerframework.checker.nullness.qual.Nullable;
+
+/**
+ * Resolves symbols from their global name. This abstracts over whether
+ * we're looking on a classpath, in a file tree, in a serialized index, etc.
+ *
+ * @author Clément Fournier
+ */
+public interface SymbolResolver {
+
+ /**
+ * Resolves a class symbol from its canonical name. Periods ('.') may
+ * be interpreted as nested-class separators.
+ */
+ @Nullable
+ JClassSymbol resolveClassFromCanonicalName(@NonNull String canonicalName);
+
+
+ /**
+ * Loads the class like {@link #resolveClassFromCanonicalName(String)},
+ * but if this fails, returns an {@link JClassSymbol#isUnresolved() unresolved symbol}.
+ */
+ @NonNull
+ JClassSymbol resolveClassOrDefault(@NonNull String canonicalName);
+
+}
diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/JAccessibleElementSymbol.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/JAccessibleElementSymbol.java
deleted file mode 100644
index 455a5f3ef7..0000000000
--- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/JAccessibleElementSymbol.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/**
- * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
- */
-
-package net.sourceforge.pmd.lang.java.symbols.internal;
-
-/**
- * Represents declarations having access modifiers common to {@link JFieldSymbol},
- * {@link JClassSymbol}, {@link JMethodSymbol}, and {@link JConstructorSymbol}.
- *
- * @author Clément Fournier
- * @since 7.0.0
- */
-public interface JAccessibleElementSymbol extends JElementSymbol {
-
- /**
- * Returns the class that directly encloses this declaration.
- * This is equivalent to {@link Class#getEnclosingClass()}.
- * Returns null if this is a top-level type declaration.
- *
- * This is necessarily an already resolved symbol, because
- * 1. if it's obtained from reflection, then the enclosing class is available
- * 2. if it's obtained from an AST, then the enclosing class is in the same source file so we can
- * know about it
- */
- JClassSymbol getEnclosingClass();
-
- boolean isPublic();
-
-
- boolean isPrivate();
-
-
- boolean isProtected();
-
-
- boolean isPackagePrivate();
-}
diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/JClassSymbol.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/JClassSymbol.java
deleted file mode 100644
index 88631f9ede..0000000000
--- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/JClassSymbol.java
+++ /dev/null
@@ -1,107 +0,0 @@
-/**
- * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
- */
-
-package net.sourceforge.pmd.lang.java.symbols.internal;
-
-import java.util.List;
-
-import org.checkerframework.checker.nullness.qual.Nullable;
-
-import net.sourceforge.pmd.lang.java.ast.ASTAnyTypeDeclaration;
-import net.sourceforge.pmd.lang.java.ast.ASTAnyTypeDeclaration.TypeKind;
-
-
-/**
- * Represents a class or interface declaration. This is not a type! This corresponds
- * closely to a {@link Class} instance. It's the *declaration* of a type.
- *
- * Unlike {@link Class} this interface isn't used to represent either
- * array types or primitive types.
- *
- * @author Clément Fournier
- * @since 7.0.0
- */
-public interface JClassSymbol extends JSimpleTypeSymbol,
- JTypeParameterOwnerSymbol,
- BoundToNode {
-
- /**
- * Returns the fully qualified name of this class, as specified by
- * {@link Class#getName()}.
- */
- String getName();
-
- /**
- * Returns the simple name of this class, as specified by
- * {@link Class#getSimpleName()}.
- */
- @Override
- String getSimpleName();
-
- /**
- * Returns the simple name of this class, as specified by
- * {@link Class#getCanonicalName()}.
- */
- String getCanonicalName();
-
-
- /**
- * Returns the reflected class this node represents, if it's on the auxclasspath.
- * Ideally this shouldn't be used, and the symbol API should reflect everything
- * there is to know about a class.
- */
- @Nullable
- Class> getClassObject();
-
-
- List getDeclaredClasses();
-
-
- List getDeclaredMethods();
-
-
- List getConstructors();
-
-
- boolean isStrict();
-
-
- boolean isAbstract();
-
-
- default boolean isInterface() {
- return getTypeKind() == TypeKind.INTERFACE;
- }
-
-
- default boolean isEnum() {
- return getTypeKind() == TypeKind.ENUM;
- }
-
-
- default boolean isAnnotation() {
- return getTypeKind() == TypeKind.ANNOTATION;
- }
-
-
- default boolean isClass() {
- return getTypeKind() == TypeKind.CLASS;
- }
-
-
-
- ASTAnyTypeDeclaration.TypeKind getTypeKind();
-
-
- /**
- * Returns true if this declaration is declared final.
- */
- boolean isFinal();
-
-
- /**
- * Returns true if this declaration is static.
- */
- boolean isStatic();
-}
diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/JConstructorSymbol.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/JConstructorSymbol.java
deleted file mode 100644
index 845bc50ff3..0000000000
--- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/JConstructorSymbol.java
+++ /dev/null
@@ -1,18 +0,0 @@
-/**
- * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
- */
-
-package net.sourceforge.pmd.lang.java.symbols.internal;
-
-import net.sourceforge.pmd.lang.java.ast.ASTConstructorDeclaration;
-
-
-/**
- * Represents a constructor declaration.
- *
- * @author Clément Fournier
- * @since 7.0.0
- */
-public interface JConstructorSymbol extends JFormalParameterOwnerSymbol, BoundToNode {
-
-}
diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/JElementSymbol.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/JElementSymbol.java
deleted file mode 100644
index 6aeae7bb61..0000000000
--- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/JElementSymbol.java
+++ /dev/null
@@ -1,104 +0,0 @@
-/**
- * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
- */
-
-package net.sourceforge.pmd.lang.java.symbols.internal;
-
-import org.checkerframework.checker.nullness.qual.Nullable;
-
-import net.sourceforge.pmd.annotation.Experimental;
-import net.sourceforge.pmd.annotation.InternalApi;
-import net.sourceforge.pmd.lang.ast.Node;
-import net.sourceforge.pmd.lang.symboltable.NameDeclaration;
-
-
-/**
- * Represents a named program element that can be referred to by simple name. Abstracts over
- * whether the declaration is in the analysed file or not, using reflection when it's not.
- *
- * This type hierarchy is probably not directly relevant to users writing
- * rules. It's mostly intended to unify the representation of type resolution
- * and symbol analysis. At least for now it's internal.
- *
- *
SymbolDeclarations have no reference to the scope they were found in, because
- * that would tie the code reference to the analysed file, preventing the garbage
- * collection of scopes and nodes. This is a major difference with {@link NameDeclaration}.
- * The declaring scope would also vary from file to file. E.g.
- *
- *
- * class Foo {
- * public int foo;
- * // here the declaring scope of Foo#foo would be the class scope of this file
- * }
- *
- * class Bar extends Foo {
- * // here the declaring scope of Foo#foo would be the inherited scope from Foo
- * }
- *
- *
- * By storing no reference, we ensure that code references can be shared across the
- * analysed project, allowing reflective resolution to be only done once.
- *
- *
About global caching
- *
- * TODO implement sharing of reflectively found code references across the analysed project
- * It would be sufficient to cache JClassSymbols, since from there, all their members would
- * be cached too. {@link JLocalVariableSymbol} doesn't need to be cached since you can't refer to
- * it from another file.
- *
- *
That global cache could be used as a basis for multifile analysis, probably whose logic can probably
- * be merged with {@link net.sourceforge.pmd.lang.java.multifile.ProjectMirror}.
- *
- *
- *
TODO with global caching, use a (Weak|SoftReference) on the node to avoid memory leaks.
- * Also, {@link Lazy} may cause memory leaks by holding strong references to nodes in the lambdas.
- * This is no problem for now, because without global caching, symbols referring to the
- * same entity are duplicated across analysed classes, and only the symbols created in the
- * class where they're defined hold a node. So the symbols are garbage collected with the
- * AST anyway.
- *
- * A global caching will probably be enough to mitigate the cost of creating symbols,
- * and we can make those potential memory leaks strict.
- *
- * In the current state of affairs (no persistent analysis cache, incremental analysis),
- * a global cache would *heavily* use reflection. So analysis without auxclasspath will be
- * severely limited (like now tbh). There would be no access to classes that are in the
- * analysed project which lack compiled classes.
- *
- *
- * @author Clément Fournier
- * @since 7.0.0
- */
-@Experimental
-@InternalApi
-public interface JElementSymbol {
-
-
- /**
- * Returns the node declaring this program element, if it is available.
- * If the element was declared outside of the analysed sources (considering
- * incremental analysis), its AST is not available.
- *
- * @return the AST node representing the declaration, or null
- */
- @Nullable
- Node getDeclaration();
-
-
- /**
- * Gets the simple name with which this declaration may be referred to
- * when unqualified, eg the simple name of the class, or the name of the method.
- *
- * @return the simple name
- */
- String getSimpleName();
-
- // TODO access to annotations could be added to the API if we publish it
-
- // TODO tests
-
- // TODO add type information when TypeDefinitions are reviewed
- // We should be able to create a type definition from a java.lang.reflect.Type,
- // paying attention to type variables of enclosing methods and types.
- // We should also be able to do so from an ASTType, with support from a JSymbolTable.
-}
diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/JFieldSymbol.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/JFieldSymbol.java
deleted file mode 100644
index dcca59a1ba..0000000000
--- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/JFieldSymbol.java
+++ /dev/null
@@ -1,24 +0,0 @@
-/**
- * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
- */
-
-package net.sourceforge.pmd.lang.java.symbols.internal;
-
-/**
- * Represents a field declaration.
- *
- * @author Clément Fournier
- * @since 7.0.0
- */
-public interface JFieldSymbol extends JAccessibleElementSymbol, JValueSymbol {
- /** Returns true if this field is volatile. */
- boolean isVolatile();
-
-
- /** Returns true if this field is transient. */
- boolean isTransient();
-
-
- /** Returns true if this field is static. */
- boolean isStatic();
-}
diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/JFormalParameterOwnerSymbol.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/JFormalParameterOwnerSymbol.java
deleted file mode 100644
index 7dc3f41555..0000000000
--- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/JFormalParameterOwnerSymbol.java
+++ /dev/null
@@ -1,21 +0,0 @@
-/**
- * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
- */
-
-package net.sourceforge.pmd.lang.java.symbols.internal;
-
-import java.util.List;
-
-
-/**
- * Represents a declaration that can declare type parameters,
- * i.e. {@link JClassSymbol} or {@link JMethodSymbol}.
- *
- * @author Clément Fournier
- * @since 7.0.0
- */
-public interface JFormalParameterOwnerSymbol extends JAccessibleElementSymbol {
-
- List getFormalParameters();
-
-}
diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/JMethodSymbol.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/JMethodSymbol.java
deleted file mode 100644
index 14b8ec7f15..0000000000
--- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/JMethodSymbol.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/**
- * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
- */
-
-package net.sourceforge.pmd.lang.java.symbols.internal;
-
-import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration;
-
-
-/**
- * Reference to a method.
- *
- * @author Clément Fournier
- * @since 7.0.0
- */
-public interface JMethodSymbol extends JTypeParameterOwnerSymbol,
- JFormalParameterOwnerSymbol,
- BoundToNode {
-
-
- /** Returns true if this declaration is declared final. */
- boolean isFinal();
-
-
- boolean isStrict();
-
-
- boolean isAbstract();
-
-
- boolean isVarargs();
-
-
- /** Returns true if this declaration is static. */
- boolean isStatic();
-
-
- boolean isSynchronized();
-
-
- boolean isNative();
-
-
- boolean isDefault();
-
-}
diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/JResolvableClassSymbol.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/JResolvableClassSymbol.java
deleted file mode 100644
index a217f38e9c..0000000000
--- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/JResolvableClassSymbol.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/**
- * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
- */
-
-package net.sourceforge.pmd.lang.java.symbols.internal;
-
-import org.checkerframework.checker.nullness.qual.Nullable;
-
-import net.sourceforge.pmd.lang.java.ast.ASTAnyTypeDeclaration;
-
-
-/**
- * Symbolic version of {@link JClassSymbol}, which doesn't load a type
- * but provides access to its canonical name. It can try building a full type reference,
- * but this may fail. This kind of reference may be used by functions like typeIs() or
- * TypeHelper to test the type in the absence of a complete auxclasspath, but cannot
- * be used properly by type resolution since it needs access to eg supertypes and members.
- *
- * Naturally, anonymous and local classes may not be represented by
- * this symbol.
- *
- * @author Clément Fournier
- * @since 7.0.0
- */
-public interface JResolvableClassSymbol extends JSimpleTypeSymbol, BoundToNode {
-
-
- /**
- * Returns the qualified name representing this class. This is the
- * {@link JClassSymbol#getCanonicalName() canonical name} of the class.
- *
- * @return a qualified name
- */
- String getCanonicalName();
-
-
- /**
- * Attempts to convert this reference into the richer {@link JClassSymbol}
- * by loading the class. If the class can't be resolved (incomplete classpath),
- * returns null. Also, maybe the class is already loaded.
- */
- @Nullable
- JClassSymbol loadClass();
-}
diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/JSimpleTypeSymbol.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/JSimpleTypeSymbol.java
deleted file mode 100644
index 3218c72406..0000000000
--- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/JSimpleTypeSymbol.java
+++ /dev/null
@@ -1,50 +0,0 @@
-/**
- * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
- */
-
-package net.sourceforge.pmd.lang.java.symbols.internal;
-
-/**
- * A reference type that can be referred to using a simple name.
- * These include references to class or interfaces (be they
- * {@linkplain JClassSymbol resolved} or only {@link JResolvableClassSymbol symbolic}),
- * and references to {@linkplain JTypeParameterSymbol type parameters},
- * but not array types or parameterized types. Primitive types are
- * excluded as well because that wouldn't be useful.
- *
- * This can probably be unified with types symbols (including array types,
- * intersection types, wildcard types) in a later stage to make type
- * resolution depend only on this abstract representation.
- *
- * @author Clément Fournier
- * @since 7.0.0
- */
-public interface JSimpleTypeSymbol extends JElementSymbol {
-
-
- /**
- * Returns true if this is a resolved class reference, in
- * which case it can be safely downcast to {@link JClassSymbol}.
- */
- default boolean isResolvedClass() {
- return this instanceof JClassSymbol;
- }
-
-
- /**
- * Returns true if this is a class reference, in
- * which case it can be safely downcast to {@link JResolvableClassSymbol}.
- */
- default boolean isUnresolvedClass() {
- return this instanceof JResolvableClassSymbol;
- }
-
-
- /**
- * Returns true if this is a reference to a type variable, in
- * which case it can be safely downcast to {@link JTypeParameterSymbol}.
- */
- default boolean isTypeVariable() {
- return this instanceof JTypeParameterSymbol;
- }
-}
diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/JTypeParameterOwnerSymbol.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/JTypeParameterOwnerSymbol.java
deleted file mode 100644
index 3b828f9f6a..0000000000
--- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/JTypeParameterOwnerSymbol.java
+++ /dev/null
@@ -1,21 +0,0 @@
-/**
- * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
- */
-
-package net.sourceforge.pmd.lang.java.symbols.internal;
-
-import java.util.List;
-
-
-/**
- * Represents a declaration that can declare type parameters,
- * i.e. {@link JClassSymbol} or {@link JMethodSymbol}.
- *
- * @author Clément Fournier
- * @since 7.0.0
- */
-public interface JTypeParameterOwnerSymbol extends JAccessibleElementSymbol {
-
- List getTypeParameters();
-
-}
diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/JTypeParameterSymbol.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/JTypeParameterSymbol.java
deleted file mode 100644
index 43810f36e6..0000000000
--- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/JTypeParameterSymbol.java
+++ /dev/null
@@ -1,26 +0,0 @@
-/**
- * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
- */
-
-package net.sourceforge.pmd.lang.java.symbols.internal;
-
-import net.sourceforge.pmd.lang.java.ast.ASTTypeParameter;
-
-
-/**
- * Represents the declaration of a type variable, ie a type parameter. Type variables are reference
- * types, but not class or interface types. They're also not declared with the same node. For those
- * reasons this type of references is distinct from {@link JResolvableClassSymbol}.
- *
- * @author Clément Fournier
- * @since 7.0.0
- */
-public interface JTypeParameterSymbol extends JSimpleTypeSymbol, BoundToNode {
-
-
- /**
- * Returns the {@link JClassSymbol} or {@link JMethodSymbol} which declared
- * this type parameter.
- */
- JTypeParameterOwnerSymbol getDeclaringSymbol();
-}
diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/JValueSymbol.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/JValueSymbol.java
deleted file mode 100644
index 1cc0dac43f..0000000000
--- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/JValueSymbol.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/**
- * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
- */
-
-package net.sourceforge.pmd.lang.java.symbols.internal;
-
-import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclaratorId;
-
-
-/**
- * Reference to a value, ie {@linkplain JLocalVariableSymbol local variable} or {@linkplain JFieldSymbol field}.
- *
- * @author Clément Fournier
- * @since 7.0.0
- */
-public interface JValueSymbol extends BoundToNode {
-
-
- /**
- * Returns true if this is a field reference, in
- * which case it can be safely downcast to {@link JFieldSymbol}.
- */
- default boolean isField() {
- return this instanceof JFieldSymbol;
- }
-
-
- /**
- * Returns true if this is a reference to a local variable, in
- * which case it can be safely downcast to {@link JLocalVariableSymbol}.
- */
- default boolean isLocalVar() {
- return !isField();
- }
-
-
- /** Returns true if this declaration is declared final. */
- boolean isFinal();
-}
diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/impl/ArraySymbolImpl.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/impl/ArraySymbolImpl.java
new file mode 100644
index 0000000000..aa1fe51d1e
--- /dev/null
+++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/impl/ArraySymbolImpl.java
@@ -0,0 +1,386 @@
+/*
+ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
+ */
+
+package net.sourceforge.pmd.lang.java.symbols.internal.impl;
+
+import java.lang.reflect.Array;
+import java.lang.reflect.Modifier;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+
+import org.checkerframework.checker.nullness.qual.NonNull;
+import org.checkerframework.checker.nullness.qual.Nullable;
+
+import net.sourceforge.pmd.lang.java.symbols.JClassSymbol;
+import net.sourceforge.pmd.lang.java.symbols.JConstructorSymbol;
+import net.sourceforge.pmd.lang.java.symbols.JExecutableSymbol;
+import net.sourceforge.pmd.lang.java.symbols.JFieldSymbol;
+import net.sourceforge.pmd.lang.java.symbols.JFormalParamSymbol;
+import net.sourceforge.pmd.lang.java.symbols.JMethodSymbol;
+import net.sourceforge.pmd.lang.java.symbols.JTypeDeclSymbol;
+import net.sourceforge.pmd.lang.java.symbols.JTypeParameterSymbol;
+
+/**
+ * Generic implementation for array symbols, which does not rely on
+ * reflection.
+ */
+class ArraySymbolImpl implements JClassSymbol {
+
+ private final JTypeDeclSymbol component;
+
+ ArraySymbolImpl(@SuppressWarnings("PMD.UnusedFormalParameter") SymbolFactory> factory, JTypeDeclSymbol component) {
+ this.component = Objects.requireNonNull(component, "Array symbol requires component");
+ if (component instanceof JClassSymbol && ((JClassSymbol) component).isAnonymousClass()) {
+ throw new IllegalArgumentException("Anonymous classes cannot be array components: " + component);
+ }
+ }
+
+ @Override
+ public @NonNull String getBinaryName() {
+ if (component instanceof JClassSymbol) {
+ return ((JClassSymbol) component).getBinaryName() + "[]";
+ }
+ return component.getSimpleName() + "[]";
+ }
+
+ @Override
+ public String getCanonicalName() {
+ if (component instanceof JClassSymbol) {
+ String compName = ((JClassSymbol) component).getCanonicalName();
+ return compName == null ? null : compName + "[]";
+ }
+ return component.getSimpleName() + "[]";
+ }
+
+ @Override
+ public boolean isUnresolved() {
+ return false;
+ }
+
+ @Override
+ public @Nullable Class> getJvmRepr() {
+ JTypeDeclSymbol elt = this.getArrayComponent();
+ int depth = 0;
+ while (elt instanceof JClassSymbol && ((JClassSymbol) elt).isArray()) {
+ elt = ((JClassSymbol) elt).getArrayComponent();
+ depth++;
+ }
+
+ Class> eltType = elt.getJvmRepr();
+ if (eltType == null) {
+ return null;
+ }
+
+ return Array.newInstance(eltType, (int[]) Array.newInstance(int.class, depth)).getClass();
+ }
+
+ @Override
+ public @Nullable JExecutableSymbol getEnclosingMethod() {
+ return null;
+ }
+
+ @Override
+ public List getDeclaredMethods() {
+ return Collections.singletonList(new ArrayCloneMethod(this));
+ }
+
+ @Override
+ public List getDeclaredFields() {
+ return Collections.singletonList(new ArrayLengthField(this));
+ }
+
+ @Override
+ public @Nullable JClassSymbol getSuperclass() {
+ return SymbolFactory.OBJECT_SYM;
+ }
+
+ @Override
+ public List getSuperInterfaces() {
+ return SymbolFactory.ARRAY_SUPER_INTERFACES;
+ }
+
+ @Override
+ public @NonNull JTypeDeclSymbol getArrayComponent() {
+ return component;
+ }
+
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ ArraySymbolImpl that = (ArraySymbolImpl) o;
+ return Objects.equals(component, that.component);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(component);
+ }
+
+ @Override
+ public List getDeclaredClasses() {
+ return Collections.emptyList();
+ }
+
+ @Override
+ public List getConstructors() {
+ return Collections.singletonList(new ArrayConstructor(this));
+ }
+
+ @Override
+ @NonNull
+ public String getPackageName() {
+ return getArrayComponent().getPackageName();
+ }
+
+ @Override
+ @NonNull
+ public String getSimpleName() {
+ return getArrayComponent().getSimpleName() + "[]";
+ }
+
+ @Override
+ public int getModifiers() {
+ int comp = getArrayComponent().getModifiers();
+ return Modifier.FINAL | Modifier.ABSTRACT | (comp & ~Modifier.STATIC);
+ }
+
+ @Override
+ public List getTypeParameters() {
+ return Collections.emptyList();
+ }
+
+ @Override
+ @Nullable
+ public JClassSymbol getEnclosingClass() {
+ return null;
+ }
+
+ @Override
+ public boolean isArray() {
+ return true;
+ }
+
+ @Override
+ public boolean isAnnotation() {
+ return false;
+ }
+
+ @Override
+ public boolean isPrimitive() {
+ return false;
+ }
+
+ @Override
+ public boolean isInterface() {
+ return false;
+ }
+
+ @Override
+ public boolean isEnum() {
+ return false;
+ }
+
+ @Override
+ public boolean isLocalClass() {
+ return false;
+ }
+
+ @Override
+ public boolean isAnonymousClass() {
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ return "array(" + component.toString() + ")";
+ }
+
+ private static class ArrayLengthField implements JFieldSymbol {
+
+ private final JClassSymbol arraySymbol;
+
+ ArrayLengthField(JClassSymbol arraySymbol) {
+ this.arraySymbol = arraySymbol;
+ }
+
+ @Override
+ public String getSimpleName() {
+ return "length";
+ }
+
+ @Override
+ public int getModifiers() {
+ return Modifier.PUBLIC | Modifier.FINAL;
+ }
+
+ @Override
+ public boolean isEnumConstant() {
+ return false;
+ }
+
+ @Override
+ public @NonNull JClassSymbol getEnclosingClass() {
+ return arraySymbol;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ ArrayLengthField that = (ArrayLengthField) o;
+ return arraySymbol.equals(that.arraySymbol);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(arraySymbol);
+ }
+ }
+
+ private static class ArrayCloneMethod implements JMethodSymbol {
+
+ private final ArraySymbolImpl arraySymbol;
+
+ ArrayCloneMethod(ArraySymbolImpl arraySymbol) {
+ this.arraySymbol = arraySymbol;
+ }
+
+ @Override
+ public String getSimpleName() {
+ return "clone";
+ }
+
+ @Override
+ public List getFormalParameters() {
+ return Collections.emptyList();
+ }
+
+ @Override
+ public boolean isVarargs() {
+ return false;
+ }
+
+ @Override
+ public int getArity() {
+ return 0;
+ }
+
+ @Override
+ public int getModifiers() {
+ return Modifier.PUBLIC;
+ }
+
+ @Override
+ public @NonNull JClassSymbol getEnclosingClass() {
+ return arraySymbol;
+ }
+
+ @Override
+ public List getTypeParameters() {
+ return Collections.emptyList();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ ArrayCloneMethod that = (ArrayCloneMethod) o;
+ return Objects.equals(arraySymbol, that.arraySymbol);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(arraySymbol);
+ }
+ }
+
+ private static class ArrayConstructor implements JConstructorSymbol {
+
+ private final ArraySymbolImpl arraySymbol;
+
+ ArrayConstructor(ArraySymbolImpl arraySymbol) {
+ this.arraySymbol = arraySymbol;
+ }
+
+ @Override
+ public List getFormalParameters() {
+ return Collections.singletonList(new JFormalParamSymbol() {
+
+ @Override
+ public JExecutableSymbol getDeclaringSymbol() {
+ return ArrayConstructor.this;
+ }
+
+ @Override
+ public String getSimpleName() {
+ return "arg0";
+ }
+
+ @Override
+ public boolean isFinal() {
+ return false;
+ }
+
+ // TODO equals/hashcode?
+ });
+ }
+
+ @Override
+ public boolean isVarargs() {
+ return false;
+ }
+
+ @Override
+ public int getArity() {
+ return 1;
+ }
+
+ @Override
+ public int getModifiers() {
+ return Modifier.PUBLIC | Modifier.FINAL;
+ }
+
+ @Override
+ public @NonNull JClassSymbol getEnclosingClass() {
+ return arraySymbol;
+ }
+
+ @Override
+ public List getTypeParameters() {
+ return Collections.emptyList();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ ArrayConstructor that = (ArrayConstructor) o;
+ return Objects.equals(arraySymbol, that.arraySymbol);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(arraySymbol);
+ }
+ }
+}
diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/impl/SymbolEquality.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/impl/SymbolEquality.java
new file mode 100644
index 0000000000..5def8a0679
--- /dev/null
+++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/impl/SymbolEquality.java
@@ -0,0 +1,175 @@
+/*
+ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
+ */
+
+package net.sourceforge.pmd.lang.java.symbols.internal.impl;
+
+import java.util.Objects;
+
+import net.sourceforge.pmd.lang.java.symbols.JClassSymbol;
+import net.sourceforge.pmd.lang.java.symbols.JConstructorSymbol;
+import net.sourceforge.pmd.lang.java.symbols.JElementSymbol;
+import net.sourceforge.pmd.lang.java.symbols.JFieldSymbol;
+import net.sourceforge.pmd.lang.java.symbols.JFormalParamSymbol;
+import net.sourceforge.pmd.lang.java.symbols.JMethodSymbol;
+import net.sourceforge.pmd.lang.java.symbols.JTypeParameterSymbol;
+
+/**
+ * Routines to share logic for equality, respecting the contract of
+ * {@link JElementSymbol#equals(Object)}.
+ *
+ * Despite this two equal symbols may not hold the same amount of
+ * information... Reflection symbols are nice, but they also add some
+ * synthetic stuff (eg implicit formal parameters, bridge methods),
+ * which we must either filter-out or replicate in AST symbols. This is TODO
+ */
+@SuppressWarnings("PMD.CompareObjectsWithEquals")
+public final class SymbolEquality {
+
+ private SymbolEquality() {
+ // util class
+ }
+
+ public static final EqAndHash TYPE_PARAM = new EqAndHash() {
+ @Override
+ public int hash(JTypeParameterSymbol t1) {
+ return Objects.hash(t1.getDeclaringSymbol(), t1.getSimpleName());
+ }
+
+ @Override
+ public boolean equals(JTypeParameterSymbol m1, Object o) {
+ if (m1 == o) {
+ return true;
+ }
+ if (!(o instanceof JTypeParameterSymbol)) {
+ return false;
+ }
+ JTypeParameterSymbol m2 = (JTypeParameterSymbol) o;
+
+ // FIXME arity check is not enough for overloads
+ return m1.getSimpleName().equals(m2.getSimpleName())
+ && m1.getDeclaringSymbol().equals(m2.getDeclaringSymbol());
+ }
+ };
+
+ public static final EqAndHash METHOD = new EqAndHash() {
+ @Override
+ public int hash(JMethodSymbol t1) {
+ return 0;
+ }
+
+ @Override
+ public boolean equals(JMethodSymbol m1, Object o) {
+ if (m1 == o) {
+ return true;
+ }
+ if (!(o instanceof JMethodSymbol)) {
+ return false;
+ }
+ JMethodSymbol m2 = (JMethodSymbol) o;
+
+ // FIXME arity check is not enough for overloads
+ return m1.getModifiers() == m2.getModifiers()
+ && m1.getArity() == m2.getArity()
+ && Objects.equals(m1.getSimpleName(), m2.getSimpleName())
+ && m1.getEnclosingClass().equals(m2.getEnclosingClass());
+ }
+ };
+
+ public static final EqAndHash CONSTRUCTOR = new EqAndHash() {
+ @Override
+ public int hash(JConstructorSymbol t1) {
+ return 0;
+ }
+
+ @Override
+ public boolean equals(JConstructorSymbol m1, Object o) {
+ if (m1 == o) {
+ return true;
+ }
+ if (!(o instanceof JConstructorSymbol)) {
+ return false;
+ }
+ JConstructorSymbol m2 = (JConstructorSymbol) o;
+
+ // FIXME arity check is not enough for overloads
+ return m1.getModifiers() == m2.getModifiers()
+ && m1.getArity() == m2.getArity()
+ && Objects.equals(m1.getSimpleName(), m2.getSimpleName())
+ && m1.getEnclosingClass().equals(m2.getEnclosingClass());
+ }
+ };
+
+
+ public static final EqAndHash CLASS = new EqAndHash() {
+ @Override
+ public int hash(JClassSymbol t1) {
+ return t1.getBinaryName().hashCode();
+ }
+
+ @Override
+ public boolean equals(JClassSymbol m1, Object o) {
+ if (m1 == o) {
+ return true;
+ }
+ if (!(o instanceof JClassSymbol)) {
+ return false;
+ }
+ JClassSymbol m2 = (JClassSymbol) o;
+
+ return m1.getBinaryName().equals(m2.getBinaryName());
+ }
+ };
+
+ public static final EqAndHash FIELD = new EqAndHash() {
+ @Override
+ public int hash(JFieldSymbol t1) {
+ return Objects.hash(t1.getEnclosingClass(), t1.getSimpleName());
+ }
+
+ @Override
+ public boolean equals(JFieldSymbol f1, Object o) {
+ if (!(o instanceof JFieldSymbol)) {
+ return false;
+ }
+ JFieldSymbol f2 = (JFieldSymbol) o;
+ return f1.getSimpleName().equals(f2.getSimpleName())
+ && f1.getEnclosingClass().equals(f2.getEnclosingClass());
+
+ }
+ };
+
+ public static final EqAndHash FORMAL_PARAM = new EqAndHash() {
+ @Override
+ public int hash(JFormalParamSymbol t1) {
+ return Objects.hash(t1.getDeclaringSymbol(), t1.getSimpleName());
+ }
+
+ @Override
+ public boolean equals(JFormalParamSymbol f1, Object o) {
+ if (!(o instanceof JFormalParamSymbol)) {
+ return false;
+ }
+ JFormalParamSymbol f2 = (JFormalParamSymbol) o;
+ return f1.getSimpleName().equals(f2.getSimpleName())
+ && f1.getDeclaringSymbol().equals(f2.getDeclaringSymbol());
+
+ }
+ };
+
+ /**
+ * Strategy to perform equals/hashcode for a type T. There are libraries
+ * for that, whatever.
+ */
+ public abstract static class EqAndHash {
+
+ public abstract int hash(T t1);
+
+
+ public abstract boolean equals(T t1, Object t2);
+
+
+ }
+
+
+}
diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/impl/SymbolFactory.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/impl/SymbolFactory.java
new file mode 100644
index 0000000000..d7d4bec210
--- /dev/null
+++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/impl/SymbolFactory.java
@@ -0,0 +1,111 @@
+/*
+ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
+ */
+
+package net.sourceforge.pmd.lang.java.symbols.internal.impl;
+
+
+import java.io.Serializable;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import org.checkerframework.checker.nullness.qual.NonNull;
+import org.checkerframework.checker.nullness.qual.Nullable;
+
+import net.sourceforge.pmd.lang.java.ast.ASTAnyTypeDeclaration;
+import net.sourceforge.pmd.lang.java.symbols.JClassSymbol;
+import net.sourceforge.pmd.lang.java.symbols.JTypeDeclSymbol;
+import net.sourceforge.pmd.lang.java.symbols.internal.impl.reflect.ReflectSymInternals;
+
+/**
+ * Builds symbols.
+ *
+ * This may be improved later to eg cache and reuse the most recently
+ * accessed symbols (there may be a lot of cache hits in a typical java file).
+ *
+ * @param Type of stuff this factory can convert to symbols. We'll
+ * implement it for {@link Class} and {@link ASTAnyTypeDeclaration}.
+ */
+public interface SymbolFactory {
+
+
+ // object
+ JClassSymbol OBJECT_SYM = ReflectSymInternals.createSharedSym(Object.class);
+ // unresolved symbol
+ JClassSymbol UNRESOLVED_CLASS_SYM = new UnresolvedClassImpl("/*unresolved*/");
+
+ // primitives
+ JClassSymbol BOOLEAN_SYM = ReflectSymInternals.createSharedSym(boolean.class);
+ JClassSymbol BYTE_SYM = ReflectSymInternals.createSharedSym(byte.class);
+ JClassSymbol CHAR_SYM = ReflectSymInternals.createSharedSym(char.class);
+ JClassSymbol DOUBLE_SYM = ReflectSymInternals.createSharedSym(double.class);
+ JClassSymbol FLOAT_SYM = ReflectSymInternals.createSharedSym(float.class);
+ JClassSymbol INT_SYM = ReflectSymInternals.createSharedSym(int.class);
+ JClassSymbol LONG_SYM = ReflectSymInternals.createSharedSym(long.class);
+ JClassSymbol SHORT_SYM = ReflectSymInternals.createSharedSym(short.class);
+ JClassSymbol VOID_SYM = ReflectSymInternals.createSharedSym(void.class);
+
+ // primitive wrappers
+ JClassSymbol BOXED_BOOLEAN_SYM = ReflectSymInternals.createSharedSym(Boolean.class);
+ JClassSymbol BOXED_BYTE_SYM = ReflectSymInternals.createSharedSym(Byte.class);
+ JClassSymbol BOXED_CHAR_SYM = ReflectSymInternals.createSharedSym(Character.class);
+ JClassSymbol BOXED_DOUBLE_SYM = ReflectSymInternals.createSharedSym(Double.class);
+ JClassSymbol BOXED_FLOAT_SYM = ReflectSymInternals.createSharedSym(Float.class);
+ JClassSymbol BOXED_INT_SYM = ReflectSymInternals.createSharedSym(Integer.class);
+ JClassSymbol BOXED_LONG_SYM = ReflectSymInternals.createSharedSym(Long.class);
+ JClassSymbol BOXED_SHORT_SYM = ReflectSymInternals.createSharedSym(Short.class);
+ JClassSymbol BOXED_VOID_SYM = ReflectSymInternals.createSharedSym(Void.class);
+
+ // array supertypes
+ JClassSymbol CLONEABLE_SYM = ReflectSymInternals.createSharedSym(Cloneable.class);
+ JClassSymbol SERIALIZABLE_SYM = ReflectSymInternals.createSharedSym(Serializable.class);
+ List ARRAY_SUPER_INTERFACES = Collections.unmodifiableList(Arrays.asList(CLONEABLE_SYM, SERIALIZABLE_SYM));
+
+ // other important/common types
+ JClassSymbol CLASS_SYM = ReflectSymInternals.createSharedSym(Class.class);
+ JClassSymbol ITERABLE_SYM = ReflectSymInternals.createSharedSym(Iterable.class);
+ JClassSymbol ENUM_SYM = ReflectSymInternals.createSharedSym(Enum.class);
+ JClassSymbol STRING_SYM = ReflectSymInternals.createSharedSym(String.class);
+
+
+ /**
+ * Produces an unresolved class symbol from the given canonical name.
+ *
+ * @param canonicalName Canonical name of the returned symbol
+ *
+ * @throws NullPointerException If the name is null
+ * @throws IllegalArgumentException If the name is empty
+ */
+ @NonNull
+ default JClassSymbol makeUnresolvedReference(String canonicalName) {
+ return new UnresolvedClassImpl(canonicalName);
+ }
+
+
+ /**
+ * Produces an array symbol from the given component symbol (one dimension).
+ * The component can naturally be another array symbol, but cannot be an
+ * anonymous class.
+ *
+ * @param component Component symbol of the array
+ *
+ * @throws NullPointerException If the component is null
+ * @throws IllegalArgumentException If the component is the symbol for an anonymous class
+ */
+ @NonNull
+ default JClassSymbol makeArraySymbol(JTypeDeclSymbol component) {
+ return new ArraySymbolImpl(this, component);
+ }
+
+
+ /**
+ * Returns the symbol representing the given class. Returns null if
+ * the given class is itself null.
+ *
+ * @param klass Object representing a class
+ */
+ JClassSymbol getClassSymbol(@Nullable T klass);
+
+
+}
diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/impl/UnresolvedClassImpl.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/impl/UnresolvedClassImpl.java
new file mode 100644
index 0000000000..3d30d78996
--- /dev/null
+++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/impl/UnresolvedClassImpl.java
@@ -0,0 +1,202 @@
+/*
+ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
+ */
+
+package net.sourceforge.pmd.lang.java.symbols.internal.impl;
+
+import java.lang.reflect.Modifier;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+
+import org.checkerframework.checker.nullness.qual.NonNull;
+import org.checkerframework.checker.nullness.qual.Nullable;
+
+import net.sourceforge.pmd.lang.java.symbols.JClassSymbol;
+import net.sourceforge.pmd.lang.java.symbols.JConstructorSymbol;
+import net.sourceforge.pmd.lang.java.symbols.JExecutableSymbol;
+import net.sourceforge.pmd.lang.java.symbols.JFieldSymbol;
+import net.sourceforge.pmd.lang.java.symbols.JMethodSymbol;
+import net.sourceforge.pmd.lang.java.symbols.JTypeDeclSymbol;
+import net.sourceforge.pmd.lang.java.symbols.JTypeParameterSymbol;
+
+/**
+ * Unresolved external reference to a class.
+ *
+ * @see JClassSymbol#isUnresolved()
+ */
+class UnresolvedClassImpl implements JClassSymbol {
+
+ private final @Nullable JClassSymbol enclosing;
+ private final String canonicalName;
+
+ UnresolvedClassImpl(String canonicalName) {
+ this(null, canonicalName);
+ }
+
+ UnresolvedClassImpl(@Nullable JClassSymbol enclosing, String canonicalName) {
+ this.enclosing = enclosing;
+ this.canonicalName = canonicalName;
+ }
+
+ @Override
+ public boolean isUnresolved() {
+ return true;
+ }
+
+
+ @Override
+ public @Nullable JExecutableSymbol getEnclosingMethod() {
+ return null;
+ }
+
+ @Override
+ public boolean isPrimitive() {
+ return false;
+ }
+
+ @Override
+ public @NonNull String getBinaryName() {
+ return canonicalName;
+ }
+
+ @NonNull
+ @Override
+ public String getSimpleName() {
+ int idx = canonicalName.lastIndexOf('.');
+ if (idx < 0) {
+ return canonicalName;
+ } else {
+ return canonicalName.substring(idx + 1);
+ }
+ }
+
+ @Override
+ public String getCanonicalName() {
+ return canonicalName;
+ }
+
+ @Override
+ public @NonNull String getPackageName() {
+ int idx = canonicalName.lastIndexOf('.');
+ if (idx < 0) {
+ return canonicalName;
+ } else {
+ return canonicalName.substring(0, idx);
+ }
+ }
+
+ @Override
+ public @Nullable Class> getJvmRepr() {
+ return null;
+ }
+
+
+ @Nullable
+ @Override
+ public JClassSymbol getSuperclass() {
+ return SymbolFactory.OBJECT_SYM;
+ }
+
+
+ @Override
+ public List getSuperInterfaces() {
+ return Collections.emptyList();
+ }
+
+ @Override
+ public List getDeclaredClasses() {
+ return Collections.emptyList();
+ }
+
+ @Override
+ public boolean isInterface() {
+ return false;
+ }
+
+ @Override
+ public boolean isEnum() {
+ return false;
+ }
+
+ @Override
+ public boolean isAnnotation() {
+ return false;
+ }
+
+ @Override
+ public boolean isAnonymousClass() {
+ return false;
+ }
+
+ @Override
+ public boolean isLocalClass() {
+ return false;
+ }
+
+ @Override
+ public boolean isArray() {
+ return false;
+ }
+
+ @Override
+ public JClassSymbol getEnclosingClass() {
+ return enclosing;
+ }
+
+ @Override
+ public int getModifiers() {
+ return Modifier.PUBLIC;
+ }
+
+
+ @Nullable
+ @Override
+ public JTypeDeclSymbol getArrayComponent() {
+ return null;
+ }
+
+ @Override
+ public List getDeclaredMethods() {
+ return Collections.emptyList();
+ }
+
+ @Override
+ public List getConstructors() {
+ return Collections.emptyList();
+ }
+
+ @Override
+ public List getDeclaredFields() {
+ return Collections.emptyList();
+ }
+
+ @Override
+ public String toString() {
+ return "unresolved(" + canonicalName + ")";
+ }
+
+ @Override
+ public List getTypeParameters() {
+ return Collections.emptyList();
+ }
+
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (!(o instanceof JClassSymbol)) {
+ return false;
+ }
+ JClassSymbol that = (JClassSymbol) o;
+ return Objects.equals(getBinaryName(), that.getBinaryName());
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(this.getSimpleName());
+ }
+
+}
diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/impl/reflect/AbstractReflectedExecutableSymbol.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/impl/reflect/AbstractReflectedExecutableSymbol.java
new file mode 100644
index 0000000000..e52fb10483
--- /dev/null
+++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/impl/reflect/AbstractReflectedExecutableSymbol.java
@@ -0,0 +1,61 @@
+/*
+ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
+ */
+
+package net.sourceforge.pmd.lang.java.symbols.internal.impl.reflect;
+
+import java.lang.reflect.Executable;
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import org.checkerframework.checker.nullness.qual.NonNull;
+
+import net.sourceforge.pmd.lang.java.symbols.JClassSymbol;
+import net.sourceforge.pmd.lang.java.symbols.JExecutableSymbol;
+import net.sourceforge.pmd.lang.java.symbols.JFormalParamSymbol;
+
+abstract class AbstractReflectedExecutableSymbol extends AbstractTypeParamOwnerSymbol implements JExecutableSymbol {
+
+ private final @NonNull ReflectedClassImpl owner;
+ private List params;
+
+
+ AbstractReflectedExecutableSymbol(@NonNull ReflectedClassImpl owner, T executable) {
+ super(owner.symFactory, executable);
+ this.owner = owner;
+ }
+
+
+ @NonNull
+ @Override
+ public final JClassSymbol getEnclosingClass() {
+ return owner;
+ }
+
+ @Override
+ public boolean isVarargs() {
+ return reflected.isVarArgs();
+ }
+
+ @Override
+ public int getArity() {
+ return reflected.getParameterCount();
+ }
+
+ @Override
+ public int getModifiers() {
+ return reflected.getModifiers();
+ }
+
+ @Override
+ public final List getFormalParameters() {
+ if (params == null) {
+ this.params = Arrays.stream(reflected.getParameters())
+ .map(p -> new ReflectedMethodParamImpl(this, p))
+ .collect(Collectors.toList());
+ }
+
+ return params;
+ }
+}
diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/impl/reflect/AbstractReflectedSymbol.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/impl/reflect/AbstractReflectedSymbol.java
new file mode 100644
index 0000000000..453c26e566
--- /dev/null
+++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/impl/reflect/AbstractReflectedSymbol.java
@@ -0,0 +1,20 @@
+/*
+ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
+ */
+
+package net.sourceforge.pmd.lang.java.symbols.internal.impl.reflect;
+
+import net.sourceforge.pmd.lang.java.symbols.JElementSymbol;
+
+/**
+ * @author Clément Fournier
+ */
+abstract class AbstractReflectedSymbol implements JElementSymbol {
+
+ protected final ReflectionSymFactory symFactory;
+
+ AbstractReflectedSymbol(ReflectionSymFactory symFactory) {
+ this.symFactory = symFactory;
+ }
+
+}
diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/impl/reflect/AbstractTypeParamOwnerSymbol.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/impl/reflect/AbstractTypeParamOwnerSymbol.java
new file mode 100644
index 0000000000..488a6846bb
--- /dev/null
+++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/impl/reflect/AbstractTypeParamOwnerSymbol.java
@@ -0,0 +1,59 @@
+/*
+ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
+ */
+
+package net.sourceforge.pmd.lang.java.symbols.internal.impl.reflect;
+
+import static java.util.stream.Collectors.toList;
+
+import java.lang.reflect.GenericDeclaration;
+import java.lang.reflect.TypeVariable;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import net.sourceforge.pmd.lang.java.symbols.JTypeParameterOwnerSymbol;
+import net.sourceforge.pmd.lang.java.symbols.JTypeParameterSymbol;
+
+abstract class AbstractTypeParamOwnerSymbol extends AbstractReflectedSymbol implements JTypeParameterOwnerSymbol {
+
+ protected final T reflected;
+
+ private List typeParams;
+
+
+ AbstractTypeParamOwnerSymbol(ReflectionSymFactory factory, T tparamOwner) {
+ super(factory);
+ this.reflected = tparamOwner;
+ }
+
+
+ @Override
+ public final List getTypeParameters() {
+ if (typeParams == null) {
+ buildTypeParams(reflected.getTypeParameters());
+ }
+
+ return typeParams;
+ }
+
+ private void buildTypeParams(TypeVariable>[] tparams) {
+
+ List result =
+ tparams.length == 0
+ ? Collections.emptyList()
+ : Arrays.stream(tparams)
+ .map(tvar -> new ReflectedTypeParamImpl(symFactory, this, tvar))
+ .collect(toList());
+
+ // this needs to be set before calling computeBounds
+ this.typeParams = Collections.unmodifiableList(result);
+
+ }
+
+ @Override
+ public int getTypeParameterCount() {
+ return typeParams != null ? typeParams.size() : reflected.getTypeParameters().length;
+ }
+
+}
diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/impl/reflect/ClasspathSymbolResolver.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/impl/reflect/ClasspathSymbolResolver.java
new file mode 100644
index 0000000000..56a6ca001b
--- /dev/null
+++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/impl/reflect/ClasspathSymbolResolver.java
@@ -0,0 +1,57 @@
+/*
+ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
+ */
+
+package net.sourceforge.pmd.lang.java.symbols.internal.impl.reflect;
+
+
+import org.checkerframework.checker.nullness.qual.NonNull;
+
+import net.sourceforge.pmd.lang.java.symbols.JClassSymbol;
+import net.sourceforge.pmd.lang.java.symbols.SymbolResolver;
+
+/**
+ * Resolves symbols by asking a classloader.
+ */
+public class ClasspathSymbolResolver implements SymbolResolver {
+
+ private final ClassLoader classLoader;
+ private final ReflectionSymFactory factory;
+
+ public ClasspathSymbolResolver(ClassLoader classLoader, ReflectionSymFactory factory) {
+ this.classLoader = classLoader;
+ this.factory = factory;
+ }
+
+
+ @Override
+ public JClassSymbol resolveClassFromCanonicalName(@NonNull String canonicalName) {
+ try {
+ return factory.getClassSymbol(classLoader.loadClass(canonicalName));
+ } catch (ClassNotFoundException e) {
+ int lastDotIdx = canonicalName.lastIndexOf('.');
+ if (lastDotIdx < 0) {
+ return null;
+ } else {
+ JClassSymbol outer = resolveClassFromCanonicalName(canonicalName.substring(0, lastDotIdx));
+ if (outer != null) {
+ String innerName = canonicalName.substring(lastDotIdx + 1);
+ for (JClassSymbol inner : outer.getDeclaredClasses()) {
+ if (inner.getSimpleName().equals(innerName)) {
+ return inner;
+ }
+ }
+ }
+ }
+ }
+
+ return null;
+ }
+
+ @NonNull
+ @Override
+ public JClassSymbol resolveClassOrDefault(@NonNull String canonicalName) {
+ JClassSymbol symbol = resolveClassFromCanonicalName(canonicalName);
+ return symbol != null ? symbol : factory.makeUnresolvedReference(canonicalName);
+ }
+}
diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/impl/reflect/ReflectSymInternals.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/impl/reflect/ReflectSymInternals.java
new file mode 100644
index 0000000000..d619f62619
--- /dev/null
+++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/impl/reflect/ReflectSymInternals.java
@@ -0,0 +1,29 @@
+/*
+ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
+ */
+
+package net.sourceforge.pmd.lang.java.symbols.internal.impl.reflect;
+
+import net.sourceforge.pmd.lang.java.symbols.JClassSymbol;
+import net.sourceforge.pmd.lang.java.symbols.internal.impl.SymbolFactory;
+
+/**
+ * Bridge into the internal API of this package.
+ */
+public final class ReflectSymInternals {
+
+ private static final ReflectionSymFactory STATIC_FACTORY = new ReflectionSymFactory();
+
+ private ReflectSymInternals() {
+ // util class
+ }
+
+ /**
+ * {@link SymbolFactory} cannot use {@link ReflectionSymFactory}
+ * directly, because of class init cycle.
+ */
+ public static JClassSymbol createSharedSym(Class> klass) {
+ return ReflectedClassImpl.createOuterClass(STATIC_FACTORY, klass);
+ }
+
+}
diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/impl/reflect/ReflectedClassImpl.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/impl/reflect/ReflectedClassImpl.java
new file mode 100644
index 0000000000..1a4a8c1a28
--- /dev/null
+++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/impl/reflect/ReflectedClassImpl.java
@@ -0,0 +1,234 @@
+/*
+ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
+ */
+
+package net.sourceforge.pmd.lang.java.symbols.internal.impl.reflect;
+
+import static java.util.stream.Collectors.toList;
+
+import java.util.Arrays;
+import java.util.List;
+
+import org.apache.commons.lang3.ClassUtils;
+import org.checkerframework.checker.nullness.qual.NonNull;
+import org.checkerframework.checker.nullness.qual.Nullable;
+
+import net.sourceforge.pmd.lang.java.symbols.JClassSymbol;
+import net.sourceforge.pmd.lang.java.symbols.JConstructorSymbol;
+import net.sourceforge.pmd.lang.java.symbols.JFieldSymbol;
+import net.sourceforge.pmd.lang.java.symbols.JMethodSymbol;
+import net.sourceforge.pmd.lang.java.symbols.JTypeDeclSymbol;
+import net.sourceforge.pmd.lang.java.symbols.internal.impl.SymbolEquality;
+import net.sourceforge.pmd.lang.java.symbols.internal.impl.SymbolFactory;
+
+final class ReflectedClassImpl extends AbstractTypeParamOwnerSymbol> implements JClassSymbol {
+
+ private final Class> myClass;
+ private final @Nullable JClassSymbol enclosing;
+
+ private @Nullable JClassSymbol superclass;
+ private List superInterfaces;
+
+
+ private List declaredClasses;
+ private List declaredMethods;
+ private List declaredConstructors;
+ private List declaredFields;
+
+ private ReflectedClassImpl(ReflectionSymFactory symbolFactory, Class> myClass) {
+ this(symbolFactory, null, myClass);
+ }
+
+ /**
+ * This assumes that the enclosing symbol is correct and doesn't
+ * check it itself unless assertions are enabled.
+ */
+ private ReflectedClassImpl(ReflectionSymFactory symbolFactory, @Nullable JClassSymbol enclosing, Class> myClass) {
+ super(symbolFactory, myClass);
+
+ this.myClass = myClass;
+ this.enclosing = enclosing;
+
+ assert !myClass.isArray() : "This class cannot represent array types";
+
+ assert enclosing == null && myClass.getEnclosingClass() == null
+ || myClass.getEnclosingClass() != null && enclosing != null
+ && myClass.getEnclosingClass().getName().equals(enclosing.getBinaryName())
+ : "Wrong enclosing class " + enclosing + ", expecting " + myClass.getEnclosingClass();
+ }
+
+ @Override
+ public @NonNull String getBinaryName() {
+ return myClass.getName();
+ }
+
+ @NonNull
+ @Override
+ public String getSimpleName() {
+ return myClass.getSimpleName();
+ }
+
+ @Override
+ public String getCanonicalName() {
+ return myClass.getCanonicalName();
+ }
+
+ @Override
+ public boolean isUnresolved() {
+ return false;
+ }
+
+ @Override
+ public @NonNull String getPackageName() {
+ return myClass.isPrimitive() ? PRIMITIVE_PACKAGE
+ : ClassUtils.getPackageName(myClass);
+ }
+
+ @Override
+ public @Nullable Class> getJvmRepr() {
+ return myClass;
+ }
+
+ @Override
+ public boolean isPrimitive() {
+ return myClass.isPrimitive();
+ }
+
+ @Nullable
+ @Override
+ public JClassSymbol getSuperclass() {
+ if (superclass == null) {
+ superclass = symFactory.getClassSymbol(myClass.getSuperclass());
+ }
+ return superclass;
+ }
+
+
+ @Override
+ public List getSuperInterfaces() {
+ if (superInterfaces == null) {
+ superInterfaces = myClass.isArray() ? SymbolFactory.ARRAY_SUPER_INTERFACES
+ : Arrays.stream(myClass.getInterfaces()).map(symFactory::getClassSymbol).collect(toList());
+ }
+ return superInterfaces;
+ }
+
+ @Override
+ public List getDeclaredClasses() {
+ if (declaredClasses == null) {
+ declaredClasses = Arrays.stream(myClass.getDeclaredClasses())
+ .map(k -> createWithEnclosing(symFactory, this, k))
+ .collect(toList());
+ }
+ return declaredClasses;
+ }
+
+
+ @Override
+ public boolean isInterface() {
+ return myClass.isInterface();
+ }
+
+ @Override
+ public boolean isEnum() {
+ return myClass.isEnum();
+ }
+
+ @Override
+ public boolean isAnnotation() {
+ return myClass.isAnnotation();
+ }
+
+ @Override
+ public boolean isAnonymousClass() {
+ return myClass.isAnonymousClass();
+ }
+
+ @Override
+ public boolean isLocalClass() {
+ return myClass.isLocalClass();
+ }
+
+ @Override
+ public boolean isArray() {
+ return false;
+ }
+
+ @Override
+ public JClassSymbol getEnclosingClass() {
+ return enclosing;
+ }
+
+ @Override
+ public int getModifiers() {
+ return myClass.getModifiers();
+ }
+
+
+ @Nullable
+ @Override
+ public JTypeDeclSymbol getArrayComponent() {
+ return null;
+ }
+
+ @Override
+ public List getDeclaredMethods() {
+ if (declaredMethods == null) {
+ declaredMethods = Arrays.stream(myClass.getDeclaredMethods())
+ .filter(it -> !it.isBridge() && !it.isSynthetic())
+ .map(it -> new ReflectedMethodImpl(this, it))
+ .collect(toList());
+ }
+ return declaredMethods;
+ }
+
+ @Override
+ public List getConstructors() {
+ if (declaredConstructors == null) {
+ declaredConstructors = Arrays.stream(myClass.getDeclaredConstructors())
+ .filter(it -> !it.isSynthetic())
+ .map(it -> new ReflectedCtorImpl(this, it))
+ .collect(toList());
+ }
+ return declaredConstructors;
+ }
+
+ @Override
+ public List getDeclaredFields() {
+ if (declaredFields == null) {
+ declaredFields = Arrays.stream(myClass.getDeclaredFields())
+ .filter(it -> !it.isSynthetic())
+ .map(it -> new ReflectedFieldImpl(this, it))
+ .collect(toList());
+ }
+ return declaredFields;
+ }
+
+ @Override
+ public String toString() {
+ return getBinaryName();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ return SymbolEquality.CLASS.equals(this, o);
+ }
+
+ @Override
+ public int hashCode() {
+ return SymbolEquality.CLASS.hash(this);
+ }
+
+
+
+ static ReflectedClassImpl createWithEnclosing(ReflectionSymFactory symbolFactory,
+ @Nullable JClassSymbol enclosing,
+ Class> myClass) {
+ return new ReflectedClassImpl(symbolFactory, enclosing, myClass);
+ }
+
+ static ReflectedClassImpl createOuterClass(ReflectionSymFactory symbolFactory, Class> myClass) {
+ return new ReflectedClassImpl(symbolFactory, myClass);
+ }
+
+}
diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/impl/reflect/ReflectedCtorImpl.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/impl/reflect/ReflectedCtorImpl.java
new file mode 100644
index 0000000000..a8f83252e9
--- /dev/null
+++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/impl/reflect/ReflectedCtorImpl.java
@@ -0,0 +1,30 @@
+/*
+ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
+ */
+
+package net.sourceforge.pmd.lang.java.symbols.internal.impl.reflect;
+
+import java.lang.reflect.Constructor;
+
+import org.checkerframework.checker.nullness.qual.NonNull;
+
+import net.sourceforge.pmd.lang.java.symbols.JConstructorSymbol;
+import net.sourceforge.pmd.lang.java.symbols.internal.impl.SymbolEquality;
+
+
+class ReflectedCtorImpl extends AbstractReflectedExecutableSymbol> implements JConstructorSymbol {
+
+ ReflectedCtorImpl(@NonNull ReflectedClassImpl owner, Constructor> myConstructor) {
+ super(owner, myConstructor);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return SymbolEquality.CONSTRUCTOR.equals(this, obj);
+ }
+
+ @Override
+ public int hashCode() {
+ return SymbolEquality.CONSTRUCTOR.hash(this);
+ }
+}
diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/impl/reflect/ReflectedFieldImpl.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/impl/reflect/ReflectedFieldImpl.java
new file mode 100644
index 0000000000..58df439d81
--- /dev/null
+++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/impl/reflect/ReflectedFieldImpl.java
@@ -0,0 +1,57 @@
+/*
+ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
+ */
+
+package net.sourceforge.pmd.lang.java.symbols.internal.impl.reflect;
+
+import java.lang.reflect.Field;
+
+import org.checkerframework.checker.nullness.qual.NonNull;
+
+import net.sourceforge.pmd.lang.java.symbols.JClassSymbol;
+import net.sourceforge.pmd.lang.java.symbols.JFieldSymbol;
+import net.sourceforge.pmd.lang.java.symbols.internal.impl.SymbolEquality;
+
+class ReflectedFieldImpl extends AbstractReflectedSymbol implements JFieldSymbol {
+
+ private final ReflectedClassImpl owner;
+ private final Field myField;
+
+ ReflectedFieldImpl(ReflectedClassImpl owner, Field myField) {
+ super(owner.symFactory);
+ this.owner = owner;
+ this.myField = myField;
+ }
+
+ @Override
+ public String getSimpleName() {
+ return myField.getName();
+ }
+
+ @Override
+ public boolean isEnumConstant() {
+ return myField.isEnumConstant();
+ }
+
+ @NonNull
+ @Override
+ public JClassSymbol getEnclosingClass() {
+ return owner;
+ }
+
+ @Override
+ public int getModifiers() {
+ return myField.getModifiers();
+ }
+
+
+ @Override
+ public boolean equals(Object o) {
+ return SymbolEquality.FIELD.equals(this, o);
+ }
+
+ @Override
+ public int hashCode() {
+ return SymbolEquality.FIELD.hash(this);
+ }
+}
diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/impl/reflect/ReflectedMethodImpl.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/impl/reflect/ReflectedMethodImpl.java
new file mode 100644
index 0000000000..e415f9866a
--- /dev/null
+++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/impl/reflect/ReflectedMethodImpl.java
@@ -0,0 +1,37 @@
+/*
+ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
+ */
+
+package net.sourceforge.pmd.lang.java.symbols.internal.impl.reflect;
+
+import java.lang.reflect.Method;
+
+import org.checkerframework.checker.nullness.qual.NonNull;
+
+import net.sourceforge.pmd.lang.java.symbols.JMethodSymbol;
+import net.sourceforge.pmd.lang.java.symbols.internal.impl.SymbolEquality;
+
+
+class ReflectedMethodImpl extends AbstractReflectedExecutableSymbol implements JMethodSymbol {
+
+ ReflectedMethodImpl(@NonNull ReflectedClassImpl owner, Method method) {
+ super(owner, method);
+ }
+
+
+ @Override
+ public String getSimpleName() {
+ return reflected.getName();
+ }
+
+
+ @Override
+ public boolean equals(Object o) {
+ return SymbolEquality.METHOD.equals(this, o);
+ }
+
+ @Override
+ public int hashCode() {
+ return SymbolEquality.METHOD.hash(this);
+ }
+}
diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/impl/reflect/ReflectedMethodParamImpl.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/impl/reflect/ReflectedMethodParamImpl.java
new file mode 100644
index 0000000000..64c335d27f
--- /dev/null
+++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/impl/reflect/ReflectedMethodParamImpl.java
@@ -0,0 +1,50 @@
+/*
+ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
+ */
+
+package net.sourceforge.pmd.lang.java.symbols.internal.impl.reflect;
+
+import java.lang.reflect.Modifier;
+import java.lang.reflect.Parameter;
+
+import net.sourceforge.pmd.lang.java.symbols.JExecutableSymbol;
+import net.sourceforge.pmd.lang.java.symbols.JFormalParamSymbol;
+import net.sourceforge.pmd.lang.java.symbols.internal.impl.SymbolEquality;
+
+final class ReflectedMethodParamImpl extends AbstractReflectedSymbol implements JFormalParamSymbol {
+
+ private final AbstractReflectedExecutableSymbol> owner;
+ private final Parameter reflected;
+
+ /** Constructor for a reflected method or constructor parameter. */
+ ReflectedMethodParamImpl(AbstractReflectedExecutableSymbol> owner, Parameter reflected) {
+ super(owner.symFactory);
+ this.owner = owner;
+ this.reflected = reflected;
+ }
+
+ @Override
+ public JExecutableSymbol getDeclaringSymbol() {
+ return owner;
+ }
+
+ @Override
+ public boolean isFinal() {
+ return Modifier.isFinal(reflected.getModifiers());
+ }
+
+ @Override
+ public String getSimpleName() {
+ return reflected.getName();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ return SymbolEquality.FORMAL_PARAM.equals(this, o);
+ }
+
+ @Override
+ public int hashCode() {
+ return SymbolEquality.FORMAL_PARAM.hash(this);
+ }
+}
diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/impl/reflect/ReflectedTypeParamImpl.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/impl/reflect/ReflectedTypeParamImpl.java
new file mode 100644
index 0000000000..1eae5f6789
--- /dev/null
+++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/impl/reflect/ReflectedTypeParamImpl.java
@@ -0,0 +1,59 @@
+/*
+ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
+ */
+
+package net.sourceforge.pmd.lang.java.symbols.internal.impl.reflect;
+
+import java.lang.reflect.TypeVariable;
+
+import org.checkerframework.checker.nullness.qual.NonNull;
+import org.checkerframework.checker.nullness.qual.Nullable;
+
+import net.sourceforge.pmd.lang.java.symbols.JTypeParameterOwnerSymbol;
+import net.sourceforge.pmd.lang.java.symbols.JTypeParameterSymbol;
+import net.sourceforge.pmd.lang.java.symbols.internal.impl.SymbolEquality;
+
+@SuppressWarnings("PMD")
+// yeah this looks weird for now
+// but it will fall into place when we introduce type mirrors
+class ReflectedTypeParamImpl implements JTypeParameterSymbol {
+
+ private final ReflectionSymFactory factory;
+ private final JTypeParameterOwnerSymbol ownerSymbol;
+ private final String name;
+ private final TypeVariable> reflected;
+
+ ReflectedTypeParamImpl(ReflectionSymFactory factory, JTypeParameterOwnerSymbol ownerSymbol, TypeVariable> tvar) {
+ this.factory = factory;
+ this.ownerSymbol = ownerSymbol;
+ this.name = tvar.getName();
+ this.reflected = tvar;
+
+ }
+
+ @Override
+ public @Nullable Class> getJvmRepr() {
+ return Object.class; // TODO upper bound, when we have implemented types
+ }
+
+ @Override
+ public JTypeParameterOwnerSymbol getDeclaringSymbol() {
+ return ownerSymbol;
+ }
+
+ @NonNull
+ @Override
+ public String getSimpleName() {
+ return name;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ return SymbolEquality.TYPE_PARAM.equals(this, o);
+ }
+
+ @Override
+ public int hashCode() {
+ return SymbolEquality.TYPE_PARAM.hash(this);
+ }
+}
diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/impl/reflect/ReflectionSymFactory.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/impl/reflect/ReflectionSymFactory.java
new file mode 100644
index 0000000000..7402fdd362
--- /dev/null
+++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/impl/reflect/ReflectionSymFactory.java
@@ -0,0 +1,119 @@
+/*
+ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
+ */
+
+package net.sourceforge.pmd.lang.java.symbols.internal.impl.reflect;
+
+
+import static net.sourceforge.pmd.lang.java.symbols.internal.impl.reflect.ReflectedClassImpl.createOuterClass;
+import static net.sourceforge.pmd.lang.java.symbols.internal.impl.reflect.ReflectedClassImpl.createWithEnclosing;
+
+import java.io.Serializable;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.checkerframework.checker.nullness.qual.Nullable;
+
+import net.sourceforge.pmd.lang.java.symbols.JClassSymbol;
+import net.sourceforge.pmd.lang.java.symbols.internal.impl.SymbolFactory;
+
+/**
+ * Symbol factory building type symbols from {@link Class} instances.
+ * Reflected symbol implementations carry an instance of this around,
+ * so as to allow caching recently accessed symbols later on.
+ */
+public final class ReflectionSymFactory implements SymbolFactory> {
+
+ /**
+ * Lazy initialized to avoid class init cycle, because
+ * {@link SymbolFactory} creates reflected symbols.
+ */
+ private static Map, JClassSymbol> commonSymbols;
+
+
+ @Override
+ @Nullable
+ public JClassSymbol getClassSymbol(@Nullable Class> klass) {
+ if (klass == null) {
+ return null;
+ }
+
+ Map, JClassSymbol> shared = getCommonSyms();
+ if (shared.containsKey(klass)) {
+ return shared.get(klass);
+ }
+
+ if (klass.getEnclosingClass() != null) {
+ JClassSymbol enclosing = getClassSymbol(klass.getEnclosingClass());
+ assert enclosing != null;
+ return createWithEnclosing(this, enclosing, klass);
+ }
+
+ if (klass.isArray()) {
+ JClassSymbol component = getClassSymbol(klass.getComponentType());
+ return makeArraySymbol(component);
+ }
+
+ return createOuterClass(this, klass);
+ }
+
+ private static Map, JClassSymbol> getCommonSyms() {
+ Map, JClassSymbol> shared = commonSymbols;
+ if (shared == null) {
+ synchronized (ReflectionSymFactory.class) {
+ shared = commonSymbols;
+ if (shared == null) {
+ shared = initCommonSyms();
+ commonSymbols = shared;
+ }
+ }
+ }
+ return shared;
+ }
+
+ private static void putStr(Map, JClassSymbol> byClass,
+ Class> booleanClass,
+ JClassSymbol booleanSym) {
+ byClass.put(booleanClass, booleanSym);
+ }
+
+
+ private static Map, JClassSymbol> initCommonSyms() {
+ // consider putting whole java.lang + java.util in there ?
+
+ Map, JClassSymbol> specials = new HashMap<>();
+
+ putStr(specials, Object.class, OBJECT_SYM);
+
+ putStr(specials, boolean.class, BOOLEAN_SYM);
+ putStr(specials, byte.class, BYTE_SYM);
+ putStr(specials, char.class, CHAR_SYM);
+ putStr(specials, double.class, DOUBLE_SYM);
+ putStr(specials, float.class, FLOAT_SYM);
+ putStr(specials, int.class, INT_SYM);
+ putStr(specials, long.class, LONG_SYM);
+ putStr(specials, short.class, SHORT_SYM);
+ putStr(specials, void.class, VOID_SYM);
+
+ putStr(specials, Cloneable.class, CLONEABLE_SYM);
+ putStr(specials, Serializable.class, SERIALIZABLE_SYM);
+
+ putStr(specials, Boolean.class, BOXED_BOOLEAN_SYM);
+ putStr(specials, Byte.class, BOXED_BYTE_SYM);
+ putStr(specials, Character.class, BOXED_CHAR_SYM);
+ putStr(specials, Double.class, BOXED_DOUBLE_SYM);
+ putStr(specials, Float.class, BOXED_FLOAT_SYM);
+ putStr(specials, Integer.class, BOXED_INT_SYM);
+ putStr(specials, Long.class, BOXED_LONG_SYM);
+ putStr(specials, Short.class, BOXED_SHORT_SYM);
+ putStr(specials, Void.class, BOXED_VOID_SYM);
+
+ putStr(specials, Iterable.class, ITERABLE_SYM);
+ putStr(specials, Enum.class, ENUM_SYM);
+ putStr(specials, String.class, STRING_SYM);
+
+ return Collections.unmodifiableMap(specials);
+ }
+
+}
diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/package-info.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/package-info.java
index 48808224b7..c759d0e899 100644
--- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/package-info.java
+++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/package-info.java
@@ -1,4 +1,4 @@
-/**
+/*
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
@@ -6,8 +6,9 @@
* Prototype of a new symbol resolution framework
* that inter-operates cleanly with type resolution.
*
- * @see net.sourceforge.pmd.lang.java.symbols.internal.JElementSymbol
+ * @see net.sourceforge.pmd.lang.java.symbols.JElementSymbol
* @see net.sourceforge.pmd.lang.java.symbols.table.JSymbolTable
+ * @see net.sourceforge.pmd.lang.java.symbols.SymbolResolver
*/
@Experimental
diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/table/JSymbolTable.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/table/JSymbolTable.java
index 9825b49dfe..2e0819857b 100644
--- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/table/JSymbolTable.java
+++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/table/JSymbolTable.java
@@ -1,4 +1,4 @@
-/**
+/*
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
@@ -9,14 +9,10 @@ import java.util.stream.Stream;
import org.checkerframework.checker.nullness.qual.Nullable;
import net.sourceforge.pmd.annotation.Experimental;
-import net.sourceforge.pmd.lang.java.symbols.internal.JClassSymbol;
-import net.sourceforge.pmd.lang.java.symbols.internal.JElementSymbol;
-import net.sourceforge.pmd.lang.java.symbols.internal.JMethodSymbol;
-import net.sourceforge.pmd.lang.java.symbols.internal.JResolvableClassSymbol;
-import net.sourceforge.pmd.lang.java.symbols.internal.JSimpleTypeSymbol;
-import net.sourceforge.pmd.lang.java.symbols.internal.JTypeParameterSymbol;
-import net.sourceforge.pmd.lang.java.symbols.internal.JValueSymbol;
-import net.sourceforge.pmd.lang.java.typeresolution.ClassTypeResolver;
+import net.sourceforge.pmd.lang.java.symbols.JElementSymbol;
+import net.sourceforge.pmd.lang.java.symbols.JMethodSymbol;
+import net.sourceforge.pmd.lang.java.symbols.JTypeDeclSymbol;
+import net.sourceforge.pmd.lang.java.symbols.JValueSymbol;
// @formatter:off
/**
@@ -36,33 +32,6 @@ import net.sourceforge.pmd.lang.java.typeresolution.ClassTypeResolver;
* This allows directly encoding shadowing and hiding mechanisms in the parent-child
* relationships.
*
- * Why not keep the current symbol table
- *
- * The current symbol table framework was not built with the same goals in mind.
- * It indexes the AST to reduce it to a simpler representation, which is mostly
- * shortcuts to nodes. That representation hasn't proved very useful in rules,
- * which mostly only use it to resolve variable accesses.
- *
- *
The biggest issue is that it was not designed to abstract over whether we
- * have a node to represent a declaration or not. It can't work on reflection
- * data, and thus cannot really help type resolution, even if a good symbol table
- * would take the burden of resolving references off a type checker. The
- * shortcomings of the current symbol table make the current typeres duplicate
- * logic, and ultimately perform tasks that are not its responsibility,
- * which is probably why {@link ClassTypeResolver} is so huge and nasty.
- *
- *
Having an abstraction layer to unify them allows the AST analyses to
- * be complementary, and rely on each other, instead of being so self-reliant.
- * The abstraction provided by {@link JElementSymbol} may in the future be used
- * to build global indices of analysed projects to implement multifile analysis.
- *
- *
The goals of this rewrite should be:
- *
- * - To make our language analyses passes share information and be complementary
- *
- To have a symbol table that is precise and exhaustive enough that all rules can depend on it
- *
- To modularize the system so that it's more easily testable and documentable
- *
- *
* @author Clément Fournier
* @since 7.0.0
*/
@@ -78,23 +47,21 @@ public interface JSymbolTable {
*/
JSymbolTable getParent();
+ // note that types and value names can be obscured, but that depends on the syntactic
+ // context of the *usage* and is not relevant to the symbol table stack.
+
/**
* Resolves the type referred to by the given name. This must be a simple name,
* ie, parameterized types and array types are not available. Primitive types are
* also not considered because it's probably not useful.
*
- * The returned type reference may be a {@link JResolvableClassSymbol}, a
- * {@link JClassSymbol} if the type was already resolved, or a {@link JTypeParameterSymbol}.
- *
- *
Doesn't handle primitive types (there's no symbol for them).
- *
* @param simpleName Simple name of the type to look for
*
* @return The type reference if it can be found, otherwise {@code null}
*/
@Nullable
- JSimpleTypeSymbol resolveTypeName(String simpleName);
+ JTypeDeclSymbol resolveTypeName(String simpleName);
/**
diff --git a/pmd-java/src/test/java/javasymbols/testdata/impls/GenericClass.java b/pmd-java/src/test/java/javasymbols/testdata/impls/GenericClass.java
new file mode 100644
index 0000000000..37f3d7865a
--- /dev/null
+++ b/pmd-java/src/test/java/javasymbols/testdata/impls/GenericClass.java
@@ -0,0 +1,23 @@
+/*
+ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
+ */
+
+package javasymbols.testdata.impls;
+
+
+import net.sourceforge.pmd.lang.ast.Node;
+
+
+public class GenericClass {
+
+
+ public void anOverload(int bb) {
+
+ }
+
+
+ public void anOverload(int bb, String bachir) {
+
+ }
+
+}
diff --git a/pmd-java/src/test/java/javasymbols/testdata/impls/GenericClassCopy.java b/pmd-java/src/test/java/javasymbols/testdata/impls/GenericClassCopy.java
new file mode 100644
index 0000000000..da0a0ffee5
--- /dev/null
+++ b/pmd-java/src/test/java/javasymbols/testdata/impls/GenericClassCopy.java
@@ -0,0 +1,23 @@
+/*
+ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
+ */
+
+package javasymbols.testdata.impls;
+
+
+import net.sourceforge.pmd.lang.ast.Node;
+
+
+public class GenericClassCopy {
+
+
+ public void anOverload(int bb) {
+
+ }
+
+
+ public void anOverload(int bb, String bachir) {
+
+ }
+
+}
diff --git a/pmd-java/src/test/java/javasymbols/testdata/impls/IdenticalToSomeFields.java b/pmd-java/src/test/java/javasymbols/testdata/impls/IdenticalToSomeFields.java
new file mode 100644
index 0000000000..fa757526e9
--- /dev/null
+++ b/pmd-java/src/test/java/javasymbols/testdata/impls/IdenticalToSomeFields.java
@@ -0,0 +1,28 @@
+/*
+ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
+ */
+
+package javasymbols.testdata.impls;
+
+
+public class IdenticalToSomeFields {
+
+
+ public final String foo = "";
+ protected volatile int bb;
+ private int a;
+
+
+ public final String foo() {
+ return "";
+ }
+
+ public interface Other {
+
+ default int defaultMethod() {
+ return 1;
+ }
+
+ }
+
+}
diff --git a/pmd-java/src/test/java/javasymbols/testdata/impls/Overloads.java b/pmd-java/src/test/java/javasymbols/testdata/impls/Overloads.java
new file mode 100644
index 0000000000..05e10c0253
--- /dev/null
+++ b/pmd-java/src/test/java/javasymbols/testdata/impls/Overloads.java
@@ -0,0 +1,20 @@
+/*
+ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
+ */
+
+package javasymbols.testdata.impls;
+
+
+public class Overloads {
+
+
+ public void anOverload(int bb) {
+
+ }
+
+
+ public void anOverload(int bb, String bachir) {
+
+ }
+
+}
diff --git a/pmd-java/src/test/java/javasymbols/testdata/impls/SomeFields.java b/pmd-java/src/test/java/javasymbols/testdata/impls/SomeFields.java
new file mode 100644
index 0000000000..12424bf591
--- /dev/null
+++ b/pmd-java/src/test/java/javasymbols/testdata/impls/SomeFields.java
@@ -0,0 +1,15 @@
+/*
+ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
+ */
+
+package javasymbols.testdata.impls;
+
+
+public class SomeFields {
+
+ public final String foo = "";
+ private int a;
+ protected volatile int bb;
+
+
+}
diff --git a/pmd-java/src/test/java/javasymbols/testdata/impls/SomeInnerClasses.java b/pmd-java/src/test/java/javasymbols/testdata/impls/SomeInnerClasses.java
new file mode 100644
index 0000000000..79b76c8a4c
--- /dev/null
+++ b/pmd-java/src/test/java/javasymbols/testdata/impls/SomeInnerClasses.java
@@ -0,0 +1,18 @@
+/*
+ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
+ */
+
+package javasymbols.testdata.impls;
+
+
+public class SomeInnerClasses {
+
+ public static class StaticInner {
+
+ }
+
+ public class Inner {
+
+ }
+
+}
diff --git a/pmd-java/src/test/java/javasymbols/testdata/impls/SomeMethodsNoOverloads.java b/pmd-java/src/test/java/javasymbols/testdata/impls/SomeMethodsNoOverloads.java
new file mode 100644
index 0000000000..d18f300326
--- /dev/null
+++ b/pmd-java/src/test/java/javasymbols/testdata/impls/SomeMethodsNoOverloads.java
@@ -0,0 +1,22 @@
+/*
+ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
+ */
+
+package javasymbols.testdata.impls;
+
+
+public class SomeMethodsNoOverloads {
+
+ public final String foo() {
+ return "";
+ }
+
+
+ public interface Other {
+
+ default int defaultMethod() {
+ return 1;
+ }
+
+ }
+}
diff --git a/pmd-java/src/test/java/javasymbols/testdata/impls/WithSuperClass.java b/pmd-java/src/test/java/javasymbols/testdata/impls/WithSuperClass.java
new file mode 100644
index 0000000000..546a54454b
--- /dev/null
+++ b/pmd-java/src/test/java/javasymbols/testdata/impls/WithSuperClass.java
@@ -0,0 +1,23 @@
+/*
+ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
+ */
+
+package javasymbols.testdata.impls;
+
+
+import net.sourceforge.pmd.lang.ast.Node;
+
+public class WithSuperClass extends GenericClass {
+
+
+ @Override
+ public void anOverload(int bb) {
+
+ }
+
+
+ public void anOverload(int bb, String bachir, int other) {
+
+ }
+
+}
diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/JavaParsingHelper.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/JavaParsingHelper.java
new file mode 100644
index 0000000000..e9b67a934d
--- /dev/null
+++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/JavaParsingHelper.java
@@ -0,0 +1,38 @@
+/**
+ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
+ */
+
+package net.sourceforge.pmd.lang.java;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import net.sourceforge.pmd.lang.ast.Node;
+import net.sourceforge.pmd.lang.ast.test.BaseParsingHelper;
+import net.sourceforge.pmd.lang.java.ast.ASTCompilationUnit;
+
+public class JavaParsingHelper extends BaseParsingHelper {
+
+ /** This just runs the parser and no processing stages. */
+ public static final JavaParsingHelper JUST_PARSE = new JavaParsingHelper(Params.getDefaultNoProcess());
+ /** This runs all processing stages when parsing. */
+ public static final JavaParsingHelper WITH_PROCESSING = new JavaParsingHelper(Params.getDefaultProcess());
+
+ private JavaParsingHelper(Params params) {
+ super(JavaLanguageModule.NAME, ASTCompilationUnit.class, params);
+ }
+
+
+ @Override
+ protected JavaParsingHelper clone(Params params) {
+ return new JavaParsingHelper(params);
+ }
+
+ public static List convertList(List nodes, Class target) {
+ List converted = new ArrayList<>();
+ for (Node n : nodes) {
+ converted.add(target.cast(n));
+ }
+ return converted;
+ }
+}
diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ParserTstUtil.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ParserTstUtil.java
deleted file mode 100644
index ca9fb60996..0000000000
--- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ParserTstUtil.java
+++ /dev/null
@@ -1,316 +0,0 @@
-/**
- * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
- */
-
-package net.sourceforge.pmd.lang.java;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.StringReader;
-import java.lang.reflect.InvocationHandler;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import java.lang.reflect.Proxy;
-import java.nio.charset.StandardCharsets;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
-import org.apache.commons.io.IOUtils;
-import org.jaxen.JaxenException;
-
-import net.sourceforge.pmd.lang.LanguageRegistry;
-import net.sourceforge.pmd.lang.LanguageVersion;
-import net.sourceforge.pmd.lang.LanguageVersionHandler;
-import net.sourceforge.pmd.lang.ast.Node;
-import net.sourceforge.pmd.lang.java.ast.ASTCompilationUnit;
-import net.sourceforge.pmd.lang.java.ast.JavaParserVisitor;
-import net.sourceforge.pmd.lang.java.dfa.DataFlowFacade;
-import net.sourceforge.pmd.lang.java.internal.JavaLanguageHandler;
-import net.sourceforge.pmd.lang.java.qname.QualifiedNameResolver;
-import net.sourceforge.pmd.lang.java.symboltable.SymbolFacade;
-
-
-public class ParserTstUtil {
-
- private ParserTstUtil() {
-
- }
-
- private static class Collector implements InvocationHandler {
- private Class clazz = null;
- private Collection collection;
-
- Collector(Class clazz) {
- this(clazz, new HashSet());
- }
-
- Collector(Class clazz, Collection coll) {
- this.clazz = clazz;
- this.collection = coll;
- }
-
- public Collection getCollection() {
- return collection;
- }
-
- public Object invoke(Object proxy, Method method, Object[] params) throws NoSuchMethodException,
- SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
- if (method.getName().equals("visit")) {
- if (clazz.isInstance(params[0])) {
- collection.add(clazz.cast(params[0]));
- }
- }
-
- Method childrenAccept = params[0].getClass().getMethod("childrenAccept",
- JavaParserVisitor.class, Object.class);
- childrenAccept.invoke(params[0], (JavaParserVisitor) proxy, null);
- return null;
- }
- }
-
- // TODO provide a configurable api to choose which visitors to invoke
- // it makes no sense
-
- public static Set getNodes(Class clazz, String javaCode) {
- return getNodes(LanguageRegistry.getLanguage(JavaLanguageModule.NAME).getDefaultVersion(), clazz, javaCode);
- }
-
- public static Set getNodes(LanguageVersion languageVersion, Class clazz, String javaCode) {
- Collector coll = new Collector<>(clazz);
- LanguageVersionHandler languageVersionHandler = languageVersion.getLanguageVersionHandler();
- ASTCompilationUnit cu = (ASTCompilationUnit) languageVersionHandler
- .getParser(languageVersionHandler.getDefaultParserOptions()).parse(null, new StringReader(javaCode));
- JavaParserVisitor jpv = (JavaParserVisitor) Proxy.newProxyInstance(JavaParserVisitor.class.getClassLoader(),
- new Class[] { JavaParserVisitor.class }, coll);
- jpv.visit(cu, null);
- return (Set) coll.getCollection();
- }
-
- public static List getOrderedNodes(Class clazz, String javaCode) {
- Collector coll = new Collector<>(clazz, new ArrayList());
- LanguageVersionHandler languageVersionHandler = LanguageRegistry.getLanguage(JavaLanguageModule.NAME)
- .getDefaultVersion().getLanguageVersionHandler();
- ASTCompilationUnit cu = (ASTCompilationUnit) languageVersionHandler
- .getParser(languageVersionHandler.getDefaultParserOptions()).parse(null, new StringReader(javaCode));
- JavaParserVisitor jpv = (JavaParserVisitor) Proxy.newProxyInstance(JavaParserVisitor.class.getClassLoader(),
- new Class[] { JavaParserVisitor.class }, coll);
- jpv.visit(cu, null);
- new QualifiedNameResolver().initializeWith(ParserTstUtil.class.getClassLoader(), cu);
- new SymbolFacade().initializeWith(cu);
- new DataFlowFacade().initializeWith(languageVersionHandler.getDataFlowHandler(), cu);
-
- return (List) coll.getCollection();
- }
-
- public static String dumpNodes(List list) {
- StringBuilder sb = new StringBuilder();
- int index = 0;
- for (E item : list) {
- sb.append("\n node[").append(index).append(item.toString());
- index++;
- }
- return sb.toString();
- }
-
- public static ASTCompilationUnit buildDFA(String javaCode) {
- LanguageVersionHandler languageVersionHandler = LanguageRegistry.getLanguage(JavaLanguageModule.NAME)
- .getDefaultVersion().getLanguageVersionHandler();
- ASTCompilationUnit cu = (ASTCompilationUnit) languageVersionHandler
- .getParser(languageVersionHandler.getDefaultParserOptions()).parse(null, new StringReader(javaCode));
- JavaParserVisitor jpv = (JavaParserVisitor) Proxy.newProxyInstance(JavaParserVisitor.class.getClassLoader(),
- new Class[] { JavaParserVisitor.class }, new Collector<>(ASTCompilationUnit.class));
- jpv.visit(cu, null);
- new SymbolFacade().initializeWith(cu);
- new DataFlowFacade().initializeWith(languageVersionHandler.getDataFlowHandler(), cu);
- return cu;
- }
-
- /** @see #parseJava(LanguageVersionHandler, String) */
- public static ASTCompilationUnit parseJava13(String code) {
- return parseJava(getLanguageVersionHandler("1.3"), code);
- }
-
- /** @see #parseJava(LanguageVersionHandler, String) */
- public static ASTCompilationUnit parseJava14(String code) {
- return parseJava(getLanguageVersionHandler("1.4"), code);
- }
-
- /** @see #parseJava(LanguageVersionHandler, String) */
- public static ASTCompilationUnit parseJava15(String code) {
- return parseJava(getLanguageVersionHandler("1.5"), code);
- }
-
- /** @see #parseJava(LanguageVersionHandler, String) */
- public static ASTCompilationUnit parseJava17(String code) {
- return parseJava(getLanguageVersionHandler("1.7"), code);
- }
-
- /** @see #parseJava(LanguageVersionHandler, String) */
- public static ASTCompilationUnit parseJava18(String code) {
- return parseJava(getLanguageVersionHandler("1.8"), code);
- }
-
- /** @see #parseJava(LanguageVersionHandler, String) */
- public static ASTCompilationUnit parseJava9(String code) {
- return parseJava(getLanguageVersionHandler("9"), code);
- }
-
- /** @see #parseJava(LanguageVersionHandler, String) */
- public static ASTCompilationUnit parseJava10(String code) {
- return parseJava(getLanguageVersionHandler("10"), code);
- }
-
- /** @see #parseJava(LanguageVersionHandler, String) */
- public static ASTCompilationUnit parseJava13(Class> source) {
- return parseJava13(getSourceFromClass(source));
- }
-
- /** @see #parseJava(LanguageVersionHandler, String) */
- public static ASTCompilationUnit parseJava14(Class> source) {
- return parseJava14(getSourceFromClass(source));
- }
-
- /** @see #parseJava(LanguageVersionHandler, String) */
- public static ASTCompilationUnit parseJava15(Class> source) {
- return parseJava15(getSourceFromClass(source));
- }
-
- /** @see #parseJava(LanguageVersionHandler, String) */
- public static ASTCompilationUnit parseJava17(Class> source) {
- return parseJava17(getSourceFromClass(source));
- }
-
- /** @see #parseJava(LanguageVersionHandler, String) */
- public static ASTCompilationUnit parseJava18(Class> source) {
- return parseJava18(getSourceFromClass(source));
- }
-
- /** @see #parseJava(LanguageVersionHandler, String) */
- public static ASTCompilationUnit parseJava9(Class> source) {
- return parseJava9(getSourceFromClass(source));
- }
-
- /** @see #parseJava(LanguageVersionHandler, String) */
- public static ASTCompilationUnit parseJava10(Class> source) {
- return parseJava10(getSourceFromClass(source));
- }
-
- /** @see #parseJava(LanguageVersionHandler, String) */
- public static ASTCompilationUnit parseJavaDefaultVersion(String source) {
- return parseJava(getDefaultLanguageVersionHandler(), source);
- }
-
- /** @see #parseJava(LanguageVersionHandler, String) */
- public static ASTCompilationUnit parseJavaDefaultVersion(Class> source) {
- return parseJavaDefaultVersion(getSourceFromClass(source));
- }
-
-
- public static JavaLanguageHandler getLanguageVersionHandler(String version) {
- return (JavaLanguageHandler) LanguageRegistry.getLanguage(JavaLanguageModule.NAME).getVersion(version).getLanguageVersionHandler();
- }
-
- public static JavaLanguageHandler getDefaultLanguageVersionHandler() {
- return (JavaLanguageHandler) LanguageRegistry.getLanguage(JavaLanguageModule.NAME).getDefaultVersion().getLanguageVersionHandler();
- }
-
-
- /**
- * Parses Java code and executes the symbol table visitor.
- *
- * @param languageVersionHandler The version handler for the wanted version
- * @param code The source code
- *
- * @return The compilation unit
- */
- public static ASTCompilationUnit parseJava(LanguageVersionHandler languageVersionHandler, String code) {
- ASTCompilationUnit rootNode = (ASTCompilationUnit) languageVersionHandler
- .getParser(languageVersionHandler.getDefaultParserOptions()).parse(null, new StringReader(code));
- languageVersionHandler.getQualifiedNameResolutionFacade(ParserTstUtil.class.getClassLoader()).start(rootNode);
- languageVersionHandler.getSymbolFacade().start(rootNode);
- return rootNode;
- }
-
-
- public static List selectNodes(String source, Class resultType, String xpath) throws JaxenException {
- return selectNodes(source, "1.5", resultType, xpath);
- }
-
-
- // This is the master overload, others just default the parameters
- public static List selectNodes(String source, String version, Class resultType, String xpath) throws JaxenException {
- ASTCompilationUnit acu = parseAndTypeResolveJava(version, source);
- return convertList(acu.findChildNodesWithXPath(xpath), resultType);
- }
-
-
- public static List selectNodes(Class> source, Class resultType) {
- return parseAndTypeResolveJava("1.5", getSourceFromClass(source)).findDescendantsOfType(resultType);
- }
-
-
- public static List selectNodes(Class> source, Class resultType, String xpath) throws JaxenException {
- return selectNodes(source, "1.5", resultType, xpath);
- }
-
-
- public static List selectNodes(Class> source, String version, Class resultType, String xpath) throws JaxenException {
- return selectNodes(ParserTstUtil.getSourceFromClass(source), version, resultType, xpath);
- }
-
-
- private static List convertList(List nodes, Class target) {
- List converted = new ArrayList<>();
- for (Node n : nodes) {
- converted.add(target.cast(n));
- }
- return converted;
- }
-
-
- /**
- * Gets the source from the source file in which the class was declared.
- * Returns the source of the whole file even it it is not a top-level type.
- *
- * @param clazz Class to find the source for
- *
- * @return The source
- *
- * @throws IllegalArgumentException if the source file wasn't found
- */
- public static String getSourceFromClass(Class> clazz) {
- String sourceFile = clazz.getName().replace('.', '/') + ".java";
- // Consider nested classes
- if (clazz.getName().contains("$")) {
- sourceFile = sourceFile.substring(0, clazz.getName().indexOf('$')) + ".java";
- }
- InputStream is = ParserTstUtil.class.getClassLoader().getResourceAsStream(sourceFile);
- if (is == null) {
- throw new IllegalArgumentException(
- "Unable to find source file " + sourceFile + " for " + clazz);
- }
- String source;
- try {
- source = IOUtils.toString(is, StandardCharsets.UTF_8);
- } catch (IOException e) {
- throw new RuntimeException(e);
- }
- return source;
- }
-
- public static ASTCompilationUnit parseAndTypeResolveJava(String javaVersion, String sourceCode) {
- LanguageVersionHandler languageVersionHandler = getLanguageVersionHandler(javaVersion);
- ASTCompilationUnit rootNode = (ASTCompilationUnit) languageVersionHandler
- .getParser(languageVersionHandler.getDefaultParserOptions())
- .parse(null, new StringReader(sourceCode));
- languageVersionHandler.getQualifiedNameResolutionFacade(ParserTstUtil.class.getClassLoader()).start(rootNode);
- languageVersionHandler.getSymbolFacade().start(rootNode);
- languageVersionHandler.getDataFlowFacade().start(rootNode);
- languageVersionHandler.getTypeResolutionFacade(ParserTstUtil.class.getClassLoader()).start(rootNode);
- languageVersionHandler.getMultifileFacade().start(rootNode);
- return rootNode;
- }
-}
diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/SuppressWarningsTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/SuppressWarningsTest.java
index 71005ace71..6506f992b0 100644
--- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/SuppressWarningsTest.java
+++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/SuppressWarningsTest.java
@@ -9,7 +9,6 @@ import static org.junit.Assert.assertEquals;
import org.junit.Test;
import net.sourceforge.pmd.FooRule;
-import net.sourceforge.pmd.PMD;
import net.sourceforge.pmd.Report;
import net.sourceforge.pmd.lang.LanguageRegistry;
import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration;
@@ -177,59 +176,39 @@ public class SuppressWarningsTest extends RuleTst {
assertEquals(0, rpt.size());
}
- private static final String TEST1 = "@SuppressWarnings(\"PMD\")" + PMD.EOL + "public class Foo {}";
+ private static final String TEST1 = "@SuppressWarnings(\"PMD\")\npublic class Foo {}";
- private static final String TEST2 = "@SuppressWarnings(\"PMD\")" + PMD.EOL + "public class Foo {" + PMD.EOL
- + " void bar() {" + PMD.EOL + " int foo;" + PMD.EOL + " }" + PMD.EOL + "}";
+ private static final String TEST2 = "@SuppressWarnings(\"PMD\")\npublic class Foo {\n void bar() {\n int foo;\n }\n}";
- private static final String TEST3 = "public class Baz {" + PMD.EOL + " @SuppressWarnings(\"PMD\")" + PMD.EOL
- + " public class Bar {" + PMD.EOL + " void bar() {" + PMD.EOL + " int foo;" + PMD.EOL + " }" + PMD.EOL
- + " }" + PMD.EOL + "}";
+ private static final String TEST3 = "public class Baz {\n @SuppressWarnings(\"PMD\")\n public class Bar {\n void bar() {\n int foo;\n }\n }\n}";
- private static final String TEST4 = "public class Foo {" + PMD.EOL + " @SuppressWarnings(\"PMD\")" + PMD.EOL
- + " void bar() {" + PMD.EOL + " int foo;" + PMD.EOL + " }" + PMD.EOL + "}";
+ private static final String TEST4 = "public class Foo {\n @SuppressWarnings(\"PMD\")\n void bar() {\n int foo;\n }\n}";
- private static final String TEST5 = "public class Bar {" + PMD.EOL + " @SuppressWarnings(\"PMD\")" + PMD.EOL
- + " public Bar() {" + PMD.EOL + " int foo;" + PMD.EOL + " }" + PMD.EOL + "}";
+ private static final String TEST5 = "public class Bar {\n @SuppressWarnings(\"PMD\")\n public Bar() {\n int foo;\n }\n}";
- private static final String TEST6 = "public class Bar {" + PMD.EOL + " @SuppressWarnings(\"PMD\")" + PMD.EOL
- + " int foo;" + PMD.EOL + " void bar() {" + PMD.EOL + " int foo;" + PMD.EOL + " }" + PMD.EOL + "}";
+ private static final String TEST6 = "public class Bar {\n @SuppressWarnings(\"PMD\")\n int foo;\n void bar() {\n int foo;\n }\n}";
- private static final String TEST7 = "public class Bar {" + PMD.EOL + " int foo;" + PMD.EOL
- + " void bar(@SuppressWarnings(\"PMD\") int foo) {}" + PMD.EOL + "}";
+ private static final String TEST7 = "public class Bar {\n int foo;\n void bar(@SuppressWarnings(\"PMD\") int foo) {}\n}";
- private static final String TEST8 = "public class Bar {" + PMD.EOL + " int foo;" + PMD.EOL + " void bar() {"
- + PMD.EOL + " @SuppressWarnings(\"PMD\") int foo;" + PMD.EOL + " }" + PMD.EOL + "}";
+ private static final String TEST8 = "public class Bar {\n int foo;\n void bar() {\n @SuppressWarnings(\"PMD\") int foo;\n }\n}";
- private static final String TEST9 = "public class Bar {" + PMD.EOL + " int foo;" + PMD.EOL + " void bar() {"
- + PMD.EOL + " @SuppressWarnings(\"PMD.NoFoo\") int foo;" + PMD.EOL + " }" + PMD.EOL + "}";
+ private static final String TEST9 = "public class Bar {\n int foo;\n void bar() {\n @SuppressWarnings(\"PMD.NoFoo\") int foo;\n }\n}";
- private static final String TEST9_VALUE1 = "public class Bar {" + PMD.EOL + " int foo;" + PMD.EOL + " void bar() {"
- + PMD.EOL + " @SuppressWarnings(value = \"PMD.NoFoo\") int foo;" + PMD.EOL + " }" + PMD.EOL + "}";
+ private static final String TEST9_VALUE1 = "public class Bar {\n int foo;\n void bar() {\n @SuppressWarnings(value = \"PMD.NoFoo\") int foo;\n }\n}";
- private static final String TEST9_VALUE2 = "public class Bar {" + PMD.EOL + " int foo;" + PMD.EOL + " void bar() {"
- + PMD.EOL + " @SuppressWarnings({\"PMD.NoFoo\"}) int foo;" + PMD.EOL + " }" + PMD.EOL + "}";
+ private static final String TEST9_VALUE2 = "public class Bar {\n int foo;\n void bar() {\n @SuppressWarnings({\"PMD.NoFoo\"}) int foo;\n }\n}";
- private static final String TEST9_VALUE3 = "public class Bar {" + PMD.EOL + " int foo;" + PMD.EOL + " void bar() {"
- + PMD.EOL + " @SuppressWarnings(value = {\"PMD.NoFoo\"}) int foo;" + PMD.EOL + " }" + PMD.EOL + "}";
+ private static final String TEST9_VALUE3 = "public class Bar {\n int foo;\n void bar() {\n @SuppressWarnings(value = {\"PMD.NoFoo\"}) int foo;\n }\n}";
- private static final String TEST9_MULTIPLE_VALUES_1 = "@SuppressWarnings({\"PMD.NoFoo\", \"PMD.NoBar\"})" + PMD.EOL
- + "public class Bar {" + PMD.EOL + " int foo;" + PMD.EOL + " void bar() {" + PMD.EOL + " int foo;"
- + PMD.EOL + " }" + PMD.EOL + "}";
+ private static final String TEST9_MULTIPLE_VALUES_1 = "@SuppressWarnings({\"PMD.NoFoo\", \"PMD.NoBar\"})\npublic class Bar {\n int foo;\n void bar() {\n int foo;\n }\n}";
- private static final String TEST9_MULTIPLE_VALUES_2 = "@SuppressWarnings(value = {\"PMD.NoFoo\", \"PMD.NoBar\"})"
- + PMD.EOL + "public class Bar {" + PMD.EOL + " int foo;" + PMD.EOL + " void bar() {" + PMD.EOL
- + " int foo;" + PMD.EOL + " }" + PMD.EOL + "}";
+ private static final String TEST9_MULTIPLE_VALUES_2 = "@SuppressWarnings(value = {\"PMD.NoFoo\", \"PMD.NoBar\"})\npublic class Bar {\n int foo;\n void bar() {\n int foo;\n }\n}";
- private static final String TEST10 = "public class Bar {" + PMD.EOL + " int foo;" + PMD.EOL + " void bar() {"
- + PMD.EOL + " @SuppressWarnings(\"\") int foo;" + PMD.EOL + " }" + PMD.EOL + "}";
+ private static final String TEST10 = "public class Bar {\n int foo;\n void bar() {\n @SuppressWarnings(\"\") int foo;\n }\n}";
- private static final String TEST11 = "public class Bar {" + PMD.EOL + " int foo;" + PMD.EOL + " void bar() {"
- + PMD.EOL + " @SuppressWarnings(\"SomethingElse\") int foo;" + PMD.EOL + " }" + PMD.EOL + "}";
+ private static final String TEST11 = "public class Bar {\n int foo;\n void bar() {\n @SuppressWarnings(\"SomethingElse\") int foo;\n }\n}";
- private static final String TEST12 = "public class Bar {" + PMD.EOL + " @SuppressWarnings(\"all\") int foo;"
- + PMD.EOL + "}";
+ private static final String TEST12 = "public class Bar {\n @SuppressWarnings(\"all\") int foo;\n}";
- private static final String TEST13 = "@SuppressWarnings(\"PMD.NoBar\")" + PMD.EOL + "public class Bar {" + PMD.EOL
- + "}";
+ private static final String TEST13 = "@SuppressWarnings(\"PMD.NoBar\")\npublic class Bar {\n}";
}
diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/ASTBooleanLiteralTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/ASTBooleanLiteralTest.java
index 0394f2aa14..084684ec9e 100644
--- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/ASTBooleanLiteralTest.java
+++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/ASTBooleanLiteralTest.java
@@ -4,33 +4,30 @@
package net.sourceforge.pmd.lang.java.ast;
-import static net.sourceforge.pmd.lang.java.ParserTstUtil.getNodes;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
-import java.util.Set;
+import java.util.List;
import org.junit.Test;
-import net.sourceforge.pmd.PMD;
-
-public class ASTBooleanLiteralTest {
+public class ASTBooleanLiteralTest extends BaseParserTest {
@Test
public void testTrue() {
- Set ops = getNodes(ASTBooleanLiteral.class, TEST1);
- ASTBooleanLiteral b = ops.iterator().next();
+ List ops = java.getNodes(ASTBooleanLiteral.class, TEST1);
+ ASTBooleanLiteral b = ops.get(0);
assertTrue(b.isTrue());
}
@Test
public void testFalse() {
- Set ops = getNodes(ASTBooleanLiteral.class, TEST2);
- ASTBooleanLiteral b = ops.iterator().next();
+ List ops = java.getNodes(ASTBooleanLiteral.class, TEST2);
+ ASTBooleanLiteral b = ops.get(0);
assertFalse(b.isTrue());
}
- private static final String TEST1 = "class Foo { " + PMD.EOL + " boolean bar = true; " + PMD.EOL + "} ";
+ private static final String TEST1 = "class Foo { \n boolean bar = true; \n} ";
- private static final String TEST2 = "class Foo { " + PMD.EOL + " boolean bar = false; " + PMD.EOL + "} ";
+ private static final String TEST2 = "class Foo { \n boolean bar = false; \n} ";
}
diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/ASTClassOrInterfaceDeclarationTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/ASTClassOrInterfaceDeclarationTest.java
index 0390203714..1e00823936 100644
--- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/ASTClassOrInterfaceDeclarationTest.java
+++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/ASTClassOrInterfaceDeclarationTest.java
@@ -11,7 +11,7 @@ import java.util.List;
import org.junit.Test;
-import net.sourceforge.pmd.lang.java.ParserTstUtil;
+import net.sourceforge.pmd.lang.java.JavaParsingHelper;
/**
@@ -41,7 +41,7 @@ public class ASTClassOrInterfaceDeclarationTest {
@Test
public void testLocalInMethod() {
- List classes = ParserTstUtil.getOrderedNodes(ASTClassOrInterfaceDeclaration.class, LOCAL_CLASS_IN_METHOD);
+ List classes = getClassDecls(LOCAL_CLASS_IN_METHOD);
assertTrue(classes.size() == 2);
assertFalse("Local class false-positive", classes.get(0).isLocal());
@@ -51,7 +51,7 @@ public class ASTClassOrInterfaceDeclarationTest {
@Test
public void testLocalInInitializer() {
- List classes = ParserTstUtil.getOrderedNodes(ASTClassOrInterfaceDeclaration.class, LOCAL_CLASS_IN_INITIALIZER);
+ List classes = getClassDecls(LOCAL_CLASS_IN_INITIALIZER);
assertTrue(classes.size() == 2);
assertFalse("Local class false-positive", classes.get(0).isLocal());
@@ -62,7 +62,7 @@ public class ASTClassOrInterfaceDeclarationTest {
@Test
public void testLocalAbstractClass() {
- List classes = ParserTstUtil.getOrderedNodes(ASTClassOrInterfaceDeclaration.class, LOCAL_CLASS_WITH_MODIFIERS);
+ List classes = getClassDecls(LOCAL_CLASS_WITH_MODIFIERS);
assertTrue(classes.size() == 2);
assertFalse("Local class false-positive", classes.get(0).isLocal());
@@ -73,7 +73,7 @@ public class ASTClassOrInterfaceDeclarationTest {
@Test
public void testLocalClassWithMixedModifiers() {
- List classes = ParserTstUtil.getOrderedNodes(ASTClassOrInterfaceDeclaration.class, LOCAL_CLASS_WITH_MIXED_MODIFIER_ANNOTATIONS);
+ List classes = getClassDecls(LOCAL_CLASS_WITH_MIXED_MODIFIER_ANNOTATIONS);
assertTrue(classes.size() == 2);
assertFalse("Local class false-positive", classes.get(0).isLocal());
@@ -85,7 +85,7 @@ public class ASTClassOrInterfaceDeclarationTest {
@Test
public void testLocalClassVisibility() {
- List classes = ParserTstUtil.getOrderedNodes(ASTClassOrInterfaceDeclaration.class, LOCAL_CLASS_WITH_MODIFIERS);
+ List classes = getClassDecls(LOCAL_CLASS_WITH_MODIFIERS);
assertTrue(classes.size() == 2);
assertFalse("Local class false-positive", classes.get(0).isLocal());
@@ -99,7 +99,7 @@ public class ASTClassOrInterfaceDeclarationTest {
@Test
public void testNestedClassIsNotLocal() {
- List classes = ParserTstUtil.getOrderedNodes(ASTClassOrInterfaceDeclaration.class, NESTED_CLASS_IS_NOT_LOCAL);
+ List classes = getClassDecls(NESTED_CLASS_IS_NOT_LOCAL);
assertTrue(classes.size() == 2);
assertFalse("Local class false-positive", classes.get(0).isLocal());
@@ -109,7 +109,7 @@ public class ASTClassOrInterfaceDeclarationTest {
@Test
public void testLocalChildrenAreNotAlwaysLocal() {
- List classes = ParserTstUtil.getOrderedNodes(ASTClassOrInterfaceDeclaration.class, LOCAL_CHILDREN_ARE_NOT_ALWAYS_LOCAL);
+ List classes = getClassDecls(LOCAL_CHILDREN_ARE_NOT_ALWAYS_LOCAL);
assertTrue(classes.size() == 4);
assertFalse("Local class false-positive", classes.get(0).isLocal()); // class Foo
@@ -117,4 +117,9 @@ public class ASTClassOrInterfaceDeclarationTest {
assertFalse("Local class false-positive", classes.get(2).isLocal()); // class Nested
assertTrue("Local class false-negative", classes.get(3).isLocal()); // class Local2
}
+
+
+ private List getClassDecls(String code) {
+ return JavaParsingHelper.WITH_PROCESSING.getNodes(ASTClassOrInterfaceDeclaration.class, code);
+ }
}
diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/ASTFormalParameterTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/ASTFormalParameterTest.java
index fbfa65ae47..046dcc5b78 100644
--- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/ASTFormalParameterTest.java
+++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/ASTFormalParameterTest.java
@@ -4,31 +4,23 @@
package net.sourceforge.pmd.lang.java.ast;
-import static net.sourceforge.pmd.lang.java.ParserTstUtil.getNodes;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
-import java.util.Iterator;
-import java.util.Set;
+import java.util.List;
import org.junit.Test;
-import net.sourceforge.pmd.PMD;
-import net.sourceforge.pmd.lang.LanguageRegistry;
-import net.sourceforge.pmd.lang.java.JavaLanguageModule;
-
-public class ASTFormalParameterTest {
+public class ASTFormalParameterTest extends BaseParserTest {
@Test
public void testVarargs() {
int nrOfVarArgs = 0;
int nrOfNoVarArgs = 0;
- Set ops = getNodes(LanguageRegistry.getLanguage(JavaLanguageModule.NAME).getVersion("1.5"),
- ASTFormalParameter.class, TEST1);
- for (Iterator iter = ops.iterator(); iter.hasNext();) {
- ASTFormalParameter b = iter.next();
+ List ops = java.getNodes(ASTFormalParameter.class, TEST1, "1.5");
+ for (ASTFormalParameter b : ops) {
ASTVariableDeclaratorId variableDeclId = b.getFirstDescendantOfType(ASTVariableDeclaratorId.class);
if (!"x".equals(variableDeclId.getImage())) {
assertTrue(b.isVarargs());
@@ -44,5 +36,5 @@ public class ASTFormalParameterTest {
assertEquals(1, nrOfNoVarArgs);
}
- private static final String TEST1 = "class Foo {" + PMD.EOL + " void bar(int x, int... others) {}" + PMD.EOL + "}";
+ private static final String TEST1 = "class Foo {\n void bar(int x, int... others) {}\n}";
}
diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/ASTImportDeclarationTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/ASTImportDeclarationTest.java
index 2e85435124..ef2a96075f 100644
--- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/ASTImportDeclarationTest.java
+++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/ASTImportDeclarationTest.java
@@ -4,48 +4,42 @@
package net.sourceforge.pmd.lang.java.ast;
-import static net.sourceforge.pmd.lang.java.ParserTstUtil.getNodes;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
-import java.util.Set;
+import java.util.List;
import org.junit.Test;
-import net.sourceforge.pmd.PMD;
-import net.sourceforge.pmd.lang.LanguageRegistry;
-import net.sourceforge.pmd.lang.java.JavaLanguageModule;
-
-public class ASTImportDeclarationTest {
+public class ASTImportDeclarationTest extends BaseParserTest {
@Test
public void testImportOnDemand() {
- Set ops = getNodes(ASTImportDeclaration.class, TEST1);
- assertTrue(ops.iterator().next().isImportOnDemand());
+ List ops = java.getNodes(ASTImportDeclaration.class, TEST1);
+ assertTrue(ops.get(0).isImportOnDemand());
}
@Test
public void testGetImportedNameNode() {
- ASTImportDeclaration i = getNodes(ASTImportDeclaration.class, TEST2).iterator().next();
+ ASTImportDeclaration i = java.getNodes(ASTImportDeclaration.class, TEST2).get(0);
assertEquals("foo.bar.Baz", i.getImportedName());
}
@Test
public void testStaticImport() {
- Set ops = getNodes(ASTImportDeclaration.class, TEST3);
- ASTImportDeclaration i = ops.iterator().next();
+ List ops = java.getNodes(ASTImportDeclaration.class, TEST3);
+ ASTImportDeclaration i = ops.get(0);
assertTrue(i.isStatic());
}
@Test(expected = ParseException.class)
public void testStaticImportFailsWithJDK14() {
- getNodes(LanguageRegistry.getLanguage(JavaLanguageModule.NAME).getVersion("1.4"), ASTImportDeclaration.class,
- TEST3);
+ java.parse(TEST3, "1.4");
}
- private static final String TEST1 = "import foo.bar.*;" + PMD.EOL + "public class Foo {}";
+ private static final String TEST1 = "import foo.bar.*;\npublic class Foo {}";
- private static final String TEST2 = "import foo.bar.Baz;" + PMD.EOL + "public class Foo {}";
+ private static final String TEST2 = "import foo.bar.Baz;\npublic class Foo {}";
- private static final String TEST3 = "import static foo.bar.Baz;" + PMD.EOL + "public class Foo {}";
+ private static final String TEST3 = "import static foo.bar.Baz;\npublic class Foo {}";
}
diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/ASTInitializerTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/ASTInitializerTest.java
index 670ab82738..f333179d1b 100644
--- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/ASTInitializerTest.java
+++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/ASTInitializerTest.java
@@ -4,19 +4,14 @@
package net.sourceforge.pmd.lang.java.ast;
-import static net.sourceforge.pmd.lang.java.ParserTstUtil.getNodes;
-
import org.junit.Test;
-import net.sourceforge.pmd.PMD;
-
-public class ASTInitializerTest {
+public class ASTInitializerTest extends BaseParserTest {
@Test
public void testDontCrashOnBlockStatement() {
- getNodes(ASTInitializer.class, TEST1);
+ java.parse(TEST1);
}
- private static final String TEST1 = "public class Foo {" + PMD.EOL + " {" + PMD.EOL + " x = 5;" + PMD.EOL + " }"
- + PMD.EOL + "}";
+ private static final String TEST1 = "public class Foo {\n {\n x = 5;\n }\n}";
}
diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/ASTModuleDeclarationTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/ASTModuleDeclarationTest.java
index 60223d57d3..8031cd667b 100644
--- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/ASTModuleDeclarationTest.java
+++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/ASTModuleDeclarationTest.java
@@ -4,31 +4,19 @@
package net.sourceforge.pmd.lang.java.ast;
-import static net.sourceforge.pmd.lang.java.ParserTstUtil.parseJava9;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
-import java.io.IOException;
-import java.nio.charset.StandardCharsets;
import java.util.List;
-import org.apache.commons.io.IOUtils;
import org.junit.Test;
-public class ASTModuleDeclarationTest {
- private static String loadSource(String name) {
- try {
- return IOUtils.toString(ASTModuleDeclarationTest.class.getResourceAsStream("jdkversiontests/" + name),
- StandardCharsets.UTF_8);
- } catch (IOException e) {
- throw new RuntimeException(e);
- }
- }
+public class ASTModuleDeclarationTest extends BaseParserTest {
@Test
public final void jdk9ModuleInfo() {
- ASTCompilationUnit ast = parseJava9(loadSource("jdk9_module_info.java"));
+ ASTCompilationUnit ast = java9.parseResource("jdkversiontests/jdk9_module_info.java");
List modules = ast.findDescendantsOfType(ASTModuleDeclaration.class);
assertEquals(1, modules.size());
ASTModuleDeclaration module = modules.get(0);
@@ -37,7 +25,7 @@ public class ASTModuleDeclarationTest {
assertEquals(7, module.jjtGetNumChildren());
List directives = module.findChildrenOfType(ASTModuleDirective.class);
assertEquals(7, directives.size());
-
+
// requires com.example.foo.http;
assertEquals(ASTModuleDirective.DirectiveType.REQUIRES.name(), directives.get(0).getType());
assertNull(directives.get(0).getRequiresModifier());
diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/ASTPackageDeclarationTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/ASTPackageDeclarationTest.java
index d261a1c903..991ab72612 100644
--- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/ASTPackageDeclarationTest.java
+++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/ASTPackageDeclarationTest.java
@@ -4,29 +4,22 @@
package net.sourceforge.pmd.lang.java.ast;
-import static net.sourceforge.pmd.lang.java.ParserTstUtil.getNodes;
import static org.junit.Assert.assertEquals;
-import java.util.Set;
-
import org.junit.Test;
-import net.sourceforge.pmd.PMD;
+public class ASTPackageDeclarationTest extends BaseParserTest {
-public class ASTPackageDeclarationTest {
-
- private static final String PACKAGE_INFO_ANNOTATED = "@Deprecated" + PMD.EOL + "package net.sourceforge.pmd.foobar;"
- + PMD.EOL;
+ private static final String PACKAGE_INFO_ANNOTATED = "@Deprecated\npackage net.sourceforge.pmd.foobar;\n";
/**
* Regression test for bug 3524607.
*/
@Test
public void testPackageName() {
- Set nodes = getNodes(ASTPackageDeclaration.class, PACKAGE_INFO_ANNOTATED);
+ ASTCompilationUnit nodes = java.parse(PACKAGE_INFO_ANNOTATED);
- assertEquals(1, nodes.size());
- ASTPackageDeclaration packageNode = nodes.iterator().next();
- assertEquals("net.sourceforge.pmd.foobar", packageNode.getPackageNameImage());
+ assertEquals("net.sourceforge.pmd.foobar", nodes.getPackageDeclaration().getPackageNameImage());
+ assertEquals("net.sourceforge.pmd.foobar", nodes.getPackageName());
}
}
diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/ASTSwitchLabelTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/ASTSwitchLabelTest.java
index 1838d70eeb..abf5bb6c6c 100644
--- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/ASTSwitchLabelTest.java
+++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/ASTSwitchLabelTest.java
@@ -4,33 +4,28 @@
package net.sourceforge.pmd.lang.java.ast;
-import static net.sourceforge.pmd.lang.java.ParserTstUtil.getNodes;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
-import java.util.Set;
+import java.util.List;
import org.junit.Test;
-import net.sourceforge.pmd.PMD;
-
-public class ASTSwitchLabelTest {
+public class ASTSwitchLabelTest extends BaseParserTest {
@Test
public void testDefaultOff() {
- Set ops = getNodes(ASTSwitchLabel.class, TEST1);
- assertFalse(ops.iterator().next().isDefault());
+ List ops = java.getNodes(ASTSwitchLabel.class, TEST1);
+ assertFalse(ops.get(0).isDefault());
}
@Test
public void testDefaultSet() {
- Set ops = getNodes(ASTSwitchLabel.class, TEST2);
- assertTrue(ops.iterator().next().isDefault());
+ List ops = java.getNodes(ASTSwitchLabel.class, TEST2);
+ assertTrue(ops.get(0).isDefault());
}
- private static final String TEST1 = "public class Foo {" + PMD.EOL + " void bar() {" + PMD.EOL + " switch (x) {"
- + PMD.EOL + " case 1: y = 2;" + PMD.EOL + " }" + PMD.EOL + " }" + PMD.EOL + "}";
+ private static final String TEST1 = "public class Foo {\n void bar() {\n switch (x) {\n case 1: y = 2;\n }\n }\n}";
- private static final String TEST2 = "public class Foo {" + PMD.EOL + " void bar() {" + PMD.EOL + " switch (x) {"
- + PMD.EOL + " default: y = 2;" + PMD.EOL + " }" + PMD.EOL + " }" + PMD.EOL + "}";
+ private static final String TEST2 = "public class Foo {\n void bar() {\n switch (x) {\n default: y = 2;\n }\n }\n}";
}
diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/ASTTests.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/ASTTests.java
deleted file mode 100644
index b65be821e5..0000000000
--- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/ASTTests.java
+++ /dev/null
@@ -1,16 +0,0 @@
-/**
- * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
- */
-
-package net.sourceforge.pmd.lang.java.ast;
-
-import org.junit.runner.RunWith;
-import org.junit.runners.Suite;
-import org.junit.runners.Suite.SuiteClasses;
-
-@RunWith(Suite.class)
-@SuiteClasses({ ASTImportDeclarationTest.class, ASTVariableDeclaratorIdTest.class, AccessNodeTest.class,
- ClassDeclTest.class, FieldDeclTest.class, MethodDeclTest.class, SimpleNodeTest.class })
-public class ASTTests {
-
-}
diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/ASTThrowStatementTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/ASTThrowStatementTest.java
index 04a05d1b3a..c31912118a 100644
--- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/ASTThrowStatementTest.java
+++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/ASTThrowStatementTest.java
@@ -4,35 +4,30 @@
package net.sourceforge.pmd.lang.java.ast;
-import static net.sourceforge.pmd.lang.java.ParserTstUtil.getNodes;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import org.junit.Test;
-import net.sourceforge.pmd.PMD;
-
/**
- * Created on Jan 19, 2005
+ * Created on Jan 19, 2005
* @author mgriffa
*/
-public class ASTThrowStatementTest {
+public class ASTThrowStatementTest extends BaseParserTest {
@Test
public final void testGetFirstASTNameImageNull() {
- ASTThrowStatement t = getNodes(ASTThrowStatement.class, NULL_NAME).iterator().next();
+ ASTThrowStatement t = java.getNodes(ASTThrowStatement.class, NULL_NAME).get(0);
assertNull(t.getFirstClassOrInterfaceTypeImage());
}
@Test
public final void testGetFirstASTNameImageNew() {
- ASTThrowStatement t = getNodes(ASTThrowStatement.class, OK_NAME).iterator().next();
+ ASTThrowStatement t = java.getNodes(ASTThrowStatement.class, OK_NAME).get(0);
assertEquals("FooException", t.getFirstClassOrInterfaceTypeImage());
}
- private static final String NULL_NAME = "public class Test {" + PMD.EOL + " void bar() {" + PMD.EOL + " throw e;"
- + PMD.EOL + " }" + PMD.EOL + "}";
+ private static final String NULL_NAME = "public class Test {\n void bar() {\n throw e;\n }\n}";
- private static final String OK_NAME = "public class Test {" + PMD.EOL + " void bar() {" + PMD.EOL
- + " throw new FooException();" + PMD.EOL + " }" + PMD.EOL + "}";
+ private static final String OK_NAME = "public class Test {\n void bar() {\n throw new FooException();\n }\n}";
}
diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/ASTVariableDeclaratorIdTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/ASTVariableDeclaratorIdTest.java
index 64e8450015..d0230b626b 100644
--- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/ASTVariableDeclaratorIdTest.java
+++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/ASTVariableDeclaratorIdTest.java
@@ -4,31 +4,25 @@
package net.sourceforge.pmd.lang.java.ast;
-import static net.sourceforge.pmd.lang.java.ParserTstUtil.getNodes;
-import static net.sourceforge.pmd.lang.java.ParserTstUtil.parseJava18;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import org.junit.Test;
-import net.sourceforge.pmd.PMD;
-
-public class ASTVariableDeclaratorIdTest {
-
-
+public class ASTVariableDeclaratorIdTest extends BaseParserTest {
@Test
public void testIsExceptionBlockParameter() {
- ASTCompilationUnit acu = getNodes(ASTCompilationUnit.class, EXCEPTION_PARAMETER).iterator().next();
+ ASTCompilationUnit acu = java.parse(EXCEPTION_PARAMETER);
ASTVariableDeclaratorId id = acu.getFirstDescendantOfType(ASTVariableDeclaratorId.class);
assertTrue(id.isExceptionBlockParameter());
}
@Test
public void testTypeNameNode() {
- ASTCompilationUnit acu = getNodes(ASTCompilationUnit.class, TYPE_NAME_NODE).iterator().next();
+ ASTCompilationUnit acu = java.parse(TYPE_NAME_NODE);
ASTVariableDeclaratorId id = acu.findDescendantsOfType(ASTVariableDeclaratorId.class).get(0);
ASTClassOrInterfaceType name = (ASTClassOrInterfaceType) id.getTypeNameNode();
@@ -37,7 +31,7 @@ public class ASTVariableDeclaratorIdTest {
@Test
public void testAnnotations() {
- ASTCompilationUnit acu = getNodes(ASTCompilationUnit.class, TEST_ANNOTATIONS).iterator().next();
+ ASTCompilationUnit acu = java.parse(TEST_ANNOTATIONS);
ASTVariableDeclaratorId id = acu.findDescendantsOfType(ASTVariableDeclaratorId.class).get(0);
ASTClassOrInterfaceType name = (ASTClassOrInterfaceType) id.getTypeNode();
@@ -46,7 +40,7 @@ public class ASTVariableDeclaratorIdTest {
@Test
public void testLambdaWithType() throws Exception {
- ASTCompilationUnit acu = parseJava18(TEST_LAMBDA_WITH_TYPE);
+ ASTCompilationUnit acu = java8.parse(TEST_LAMBDA_WITH_TYPE);
ASTLambdaExpression lambda = acu.getFirstDescendantOfType(ASTLambdaExpression.class);
ASTVariableDeclaratorId f = lambda.getFirstDescendantOfType(ASTVariableDeclaratorId.class);
assertEquals("File", f.getTypeNode().getTypeImage());
@@ -54,21 +48,19 @@ public class ASTVariableDeclaratorIdTest {
@Test
public void testLambdaWithoutType() throws Exception {
- ASTCompilationUnit acu = parseJava18(TEST_LAMBDA_WITHOUT_TYPE);
+ ASTCompilationUnit acu = java8.parse(TEST_LAMBDA_WITHOUT_TYPE);
ASTLambdaExpression lambda = acu.getFirstDescendantOfType(ASTLambdaExpression.class);
ASTVariableDeclaratorId f = lambda.getFirstDescendantOfType(ASTVariableDeclaratorId.class);
assertNull(f.getTypeNode());
}
- private static final String TYPE_NAME_NODE = "public class Test {" + PMD.EOL + " private String bar;" + PMD.EOL
- + "}";
+ private static final String TYPE_NAME_NODE = "public class Test {\n private String bar;\n}";
private static final String EXCEPTION_PARAMETER = "public class Test { { try {} catch(Exception ie) {} } }";
- private static final String TEST_ANNOTATIONS = "public class Foo {" + PMD.EOL
- + " public void bar(@A1 @A2 String s) {}" + PMD.EOL + "}";
- private static final String TEST_LAMBDA_WITH_TYPE = "public class Foo {\n" + " public void bar() {\n"
- + " FileFilter java = (File f) -> f.getName().endsWith(\".java\");\n" + " }\n" + "}\n";
- private static final String TEST_LAMBDA_WITHOUT_TYPE = "public class Foo {\n" + " public void bar() {\n"
- + " FileFilter java2 = f -> f.getName().endsWith(\".java\");\n" + " }\n" + "}\n";
+ private static final String TEST_ANNOTATIONS = "public class Foo {\n public void bar(@A1 @A2 String s) {}\n}";
+ private static final String TEST_LAMBDA_WITH_TYPE =
+ "public class Foo {\n public void bar() {\n FileFilter java = (File f) -> f.getName().endsWith(\".java\");\n }\n}\n";
+ private static final String TEST_LAMBDA_WITHOUT_TYPE =
+ "public class Foo {\n public void bar() {\n FileFilter java2 = f -> f.getName().endsWith(\".java\");\n }\n}\n";
public static junit.framework.Test suite() {
return new junit.framework.JUnit4TestAdapter(ASTVariableDeclaratorIdTest.class);
diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/AccessNodeTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/AccessNodeTest.java
index 93e8657730..e430036fbf 100644
--- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/AccessNodeTest.java
+++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/AccessNodeTest.java
@@ -4,15 +4,18 @@
package net.sourceforge.pmd.lang.java.ast;
-import static net.sourceforge.pmd.lang.java.ParserTstUtil.getNodes;
+import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
-import java.util.Set;
+import java.util.List;
import org.junit.Test;
-public class AccessNodeTest {
+import net.sourceforge.pmd.lang.ast.Node;
+import net.sourceforge.pmd.lang.java.JavaParsingHelper;
+
+public class AccessNodeTest extends BaseParserTest {
public static class MyAccessNode extends AbstractJavaAccessNode {
public MyAccessNode(int i) {
@@ -37,8 +40,8 @@ public class AccessNodeTest {
@Test
public void testModifiersOnClassDecl() {
- Set ops = getNodes(ASTClassOrInterfaceDeclaration.class, TEST1);
- assertTrue(ops.iterator().next().isPublic());
+ List ops = java.getNodes(ASTClassOrInterfaceDeclaration.class, TEST1);
+ assertTrue(ops.get(0).isPublic());
}
private static final String TEST1 = "public class Foo {}";
@@ -144,4 +147,24 @@ public class AccessNodeTest {
InternalApiBridge.setModifier(node, AccessNode.PROTECTED);
assertFalse("Node set to protected, still package private.", node.isPackagePrivate());
}
+
+
+ private static String makeAccessJavaCode(String[] access, String declRest) {
+ String result = "public class Test { ";
+ for (String s : access) {
+ result += s + " ";
+ }
+ return result + " " + declRest + " }";
+ }
+
+
+ public static T getDeclWithModifiers(String[] access, Class