diff --git a/README.md b/README.md
index 0ce70e293fbd049e02b21de2a60784048e8e840b..b9ed10d230a515e4327d39f3273e6f60b81cae25 100644
--- a/README.md
+++ b/README.md
@@ -13,7 +13,7 @@ Warning: all method calls must resolve to a method declaration. If your Java pro
JavaSDGSlicer manages its dependencies through maven, so you need to have the JDK (≥11) and Maven installed, then run
```
-mvn package
+mvn package -Dmaven.test.skip
```
A fat jar containing all the project's dependencies can be then located at `./sdg-cli/target/sdg-cli-{version}-jar-with-dependencies.jar`.
@@ -57,7 +57,7 @@ java -jar sdg-cli.jar --help
Our slicer requires the input Java program to be compilable, so all libraries must be provided using the `-i` flag. For the cases where the source code is not available, you may include the required libraries in the Java classpath by using the following call:
```
-java -cp sdg-cli.jar:your-libraries.jar es.upv.slicing.cli.Slicer -c Example.java#11:sum
+java -cp your-libraries.jar -jar sdg-cli.jar -c Example.java#11:sum
```
This approach produces lower quality slices, as the contents of the library calls are unknown.
@@ -77,6 +77,4 @@ If the graph is of interest, it can be outputted in `dot` or PDF format via `SDG
## Missing Java features
-* Object-oriented features: abstract classes, interfaces, class, method and field inheritance, anonymous classes, lambdas.
* Parallel features: threads, shared memory, synchronized methods, etc.
-* Exception handling: `finally`, try with resources.
diff --git a/javaparser-symbol-solver-core/pom.xml b/javaparser-symbol-solver-core/pom.xml
new file mode 100644
index 0000000000000000000000000000000000000000..c07194d39a0050bcaaabba52f5b62d024be6b48f
--- /dev/null
+++ b/javaparser-symbol-solver-core/pom.xml
@@ -0,0 +1,69 @@
+
+
+
+ sdg
+ es.upv.mist.slicing
+ 1.3.0
+
+ 4.0.0
+
+ javaparser-symbol-solver-core
+ com.github.javaparser
+ 3.23.2
+ jar
+ A Symbol Solver for Java, built on top of JavaParser (core)
+
+
+
+ GNU Lesser General Public License
+ http://www.gnu.org/licenses/lgpl-3.0.html
+ repo
+
+
+ Apache License, Version 2.0
+ http://www.apache.org/licenses/LICENSE-2.0.txt
+ repo
+ A business-friendly OSS license
+
+
+
+
+ 1.8
+ ${maven.build.timestamp}
+
+
+
+
+ com.github.javaparser
+ javaparser-core
+ 3.23.1
+
+
+ org.javassist
+ javassist
+ 3.28.0-GA
+
+
+ com.google.guava
+ guava
+ 31.0.1-jre
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-jar-plugin
+
+
+
+ com.github.javaparser.symbolsolver.core
+
+
+
+
+
+
+
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/JavaSymbolSolver.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/JavaSymbolSolver.java
new file mode 100644
index 0000000000000000000000000000000000000000..d93a52cfd3f0f0c87db4666df1cb83982dbaa603
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/JavaSymbolSolver.java
@@ -0,0 +1,293 @@
+/*
+ * Copyright (C) 2015-2016 Federico Tomassetti
+ * Copyright (C) 2017-2020 The JavaParser Team.
+ *
+ * This file is part of JavaParser.
+ *
+ * JavaParser can be used either under the terms of
+ * a) the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * b) the terms of the Apache License
+ *
+ * You should have received a copy of both licenses in LICENCE.LGPL and
+ * LICENCE.APACHE. Please refer to those files for details.
+ *
+ * JavaParser is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ */
+
+package com.github.javaparser.symbolsolver;
+
+import com.github.javaparser.ast.CompilationUnit;
+import com.github.javaparser.ast.Node;
+import com.github.javaparser.ast.body.*;
+import com.github.javaparser.ast.expr.*;
+import com.github.javaparser.ast.stmt.ExplicitConstructorInvocationStmt;
+import com.github.javaparser.ast.type.Type;
+import com.github.javaparser.resolution.SymbolResolver;
+import com.github.javaparser.resolution.UnsolvedSymbolException;
+import com.github.javaparser.resolution.declarations.*;
+import com.github.javaparser.resolution.types.ResolvedPrimitiveType;
+import com.github.javaparser.resolution.types.ResolvedType;
+import com.github.javaparser.symbolsolver.javaparsermodel.JavaParserFacade;
+import com.github.javaparser.symbolsolver.javaparsermodel.JavaParserFactory;
+import com.github.javaparser.symbolsolver.javaparsermodel.declarations.*;
+import com.github.javaparser.symbolsolver.model.resolution.SymbolReference;
+import com.github.javaparser.symbolsolver.model.resolution.TypeSolver;
+
+/**
+ * This implementation of the SymbolResolver wraps the functionality of the library to make them easily usable
+ * from JavaParser nodes.
+ *
+ * An instance of this class should be created once and then injected in all the CompilationUnit for which we
+ * want to enable symbol resolution. To do so the method inject can be used, or you can use
+ * {@link com.github.javaparser.ParserConfiguration#setSymbolResolver(SymbolResolver)} and the parser will do the
+ * injection for you.
+ *
+ * @author Federico Tomassetti
+ */
+public class JavaSymbolSolver implements SymbolResolver {
+
+ private static class ArrayLengthValueDeclaration implements ResolvedValueDeclaration {
+
+ private static final ArrayLengthValueDeclaration INSTANCE = new ArrayLengthValueDeclaration();
+
+ private ArrayLengthValueDeclaration() {
+
+ }
+
+ @Override
+ public String getName() {
+ return "length";
+ }
+
+ @Override
+ public ResolvedType getType() {
+ return ResolvedPrimitiveType.INT;
+ }
+ }
+
+ private TypeSolver typeSolver;
+
+ public JavaSymbolSolver(TypeSolver typeSolver) {
+ this.typeSolver = typeSolver;
+ }
+
+ /**
+ * Register this SymbolResolver into a CompilationUnit, so that symbol resolution becomes available to
+ * all nodes part of the CompilationUnit.
+ */
+ public void inject(CompilationUnit destination) {
+ destination.setData(Node.SYMBOL_RESOLVER_KEY, this);
+ }
+
+ @Override
+ public T resolveDeclaration(Node node, Class resultClass) {
+ if (node instanceof MethodDeclaration) {
+ return resultClass.cast(new JavaParserMethodDeclaration((MethodDeclaration) node, typeSolver));
+ }
+ if (node instanceof ClassOrInterfaceDeclaration) {
+ ResolvedReferenceTypeDeclaration resolved = JavaParserFactory.toTypeDeclaration(node, typeSolver);
+ if (resultClass.isInstance(resolved)) {
+ return resultClass.cast(resolved);
+ }
+ }
+ if (node instanceof EnumDeclaration) {
+ ResolvedReferenceTypeDeclaration resolved = JavaParserFactory.toTypeDeclaration(node, typeSolver);
+ if (resultClass.isInstance(resolved)) {
+ return resultClass.cast(resolved);
+ }
+ }
+ if (node instanceof EnumConstantDeclaration) {
+ ResolvedEnumDeclaration enumDeclaration = node.findAncestor(EnumDeclaration.class).get().resolve().asEnum();
+ ResolvedEnumConstantDeclaration resolved = enumDeclaration.getEnumConstants().stream().filter(c -> ((JavaParserEnumConstantDeclaration) c).getWrappedNode() == node).findFirst().get();
+ if (resultClass.isInstance(resolved)) {
+ return resultClass.cast(resolved);
+ }
+ }
+ if (node instanceof ConstructorDeclaration) {
+ ConstructorDeclaration constructorDeclaration = (ConstructorDeclaration) node;
+ TypeDeclaration> typeDeclaration = (TypeDeclaration>) node.getParentNode().get();
+ ResolvedReferenceTypeDeclaration resolvedTypeDeclaration = resolveDeclaration(typeDeclaration, ResolvedReferenceTypeDeclaration.class);
+ ResolvedConstructorDeclaration resolved = resolvedTypeDeclaration.getConstructors().stream()
+ .filter(c -> c instanceof JavaParserConstructorDeclaration)
+ .filter(c -> ((JavaParserConstructorDeclaration>) c).getWrappedNode() == constructorDeclaration)
+ .findFirst()
+ .orElseThrow(() -> new RuntimeException("This constructor cannot be found in its parent. This seems wrong"));
+ if (resultClass.isInstance(resolved)) {
+ return resultClass.cast(resolved);
+ }
+ }
+ if (node instanceof AnnotationDeclaration) {
+ ResolvedReferenceTypeDeclaration resolved = JavaParserFactory.toTypeDeclaration(node, typeSolver);
+ if (resultClass.isInstance(resolved)) {
+ return resultClass.cast(resolved);
+ }
+ }
+ if (node instanceof AnnotationMemberDeclaration) {
+ ResolvedAnnotationDeclaration annotationDeclaration = node.findAncestor(AnnotationDeclaration.class).get().resolve();
+ ResolvedAnnotationMemberDeclaration resolved = annotationDeclaration.getAnnotationMembers().stream().filter(c -> ((JavaParserAnnotationMemberDeclaration) c).getWrappedNode() == node).findFirst().get();
+ if (resultClass.isInstance(resolved)) {
+ return resultClass.cast(resolved);
+ }
+ }
+ if (node instanceof FieldDeclaration) {
+ FieldDeclaration fieldDeclaration = (FieldDeclaration) node;
+ if (fieldDeclaration.getVariables().size() != 1) {
+ throw new RuntimeException("Cannot resolve a Field Declaration including multiple variable declarators. Resolve the single variable declarators");
+ }
+ ResolvedFieldDeclaration resolved = new JavaParserFieldDeclaration(fieldDeclaration.getVariable(0), typeSolver);
+ if (resultClass.isInstance(resolved)) {
+ return resultClass.cast(resolved);
+ }
+ }
+ if (node instanceof VariableDeclarator) {
+ ResolvedValueDeclaration resolved;
+ if (node.getParentNode().isPresent() && node.getParentNode().get() instanceof FieldDeclaration) {
+ resolved = new JavaParserFieldDeclaration((VariableDeclarator) node, typeSolver);
+ } else if (node.getParentNode().isPresent() && node.getParentNode().get() instanceof VariableDeclarationExpr) {
+ resolved = new JavaParserVariableDeclaration((VariableDeclarator) node, typeSolver);
+ } else {
+ throw new UnsupportedOperationException("Parent of VariableDeclarator is: " + node.getParentNode());
+ }
+ if (resultClass.isInstance(resolved)) {
+ return resultClass.cast(resolved);
+ }
+ }
+ if (node instanceof MethodCallExpr) {
+ SymbolReference result = JavaParserFacade.get(typeSolver).solve((MethodCallExpr) node);
+ if (result.isSolved()) {
+ if (resultClass.isInstance(result.getCorrespondingDeclaration())) {
+ return resultClass.cast(result.getCorrespondingDeclaration());
+ }
+ } else {
+ throw new UnsolvedSymbolException("We are unable to find the method declaration corresponding to " + node);
+ }
+ }
+ if (node instanceof ObjectCreationExpr) {
+ SymbolReference result = JavaParserFacade.get(typeSolver).solve((ObjectCreationExpr) node);
+ if (result.isSolved()) {
+ if (resultClass.isInstance(result.getCorrespondingDeclaration())) {
+ return resultClass.cast(result.getCorrespondingDeclaration());
+ }
+ } else {
+ throw new UnsolvedSymbolException("We are unable to find the constructor declaration corresponding to " + node);
+ }
+ }
+ if (node instanceof NameExpr) {
+ SymbolReference extends ResolvedValueDeclaration> result = JavaParserFacade.get(typeSolver).solve((NameExpr) node);
+ if (result.isSolved()) {
+ if (resultClass.isInstance(result.getCorrespondingDeclaration())) {
+ return resultClass.cast(result.getCorrespondingDeclaration());
+ }
+ } else {
+ throw new UnsolvedSymbolException("We are unable to find the value declaration corresponding to " + node);
+ }
+ }
+ if (node instanceof MethodReferenceExpr) {
+ SymbolReference result = JavaParserFacade.get(typeSolver).solve((MethodReferenceExpr) node);
+ if (result.isSolved()) {
+ if (resultClass.isInstance(result.getCorrespondingDeclaration())) {
+ return resultClass.cast(result.getCorrespondingDeclaration());
+ }
+ } else {
+ throw new UnsolvedSymbolException("We are unable to find the method declaration corresponding to " + node);
+ }
+ }
+ if (node instanceof FieldAccessExpr) {
+ SymbolReference extends ResolvedValueDeclaration> result = JavaParserFacade.get(typeSolver).solve((FieldAccessExpr) node);
+ if (result.isSolved()) {
+ if (resultClass.isInstance(result.getCorrespondingDeclaration())) {
+ return resultClass.cast(result.getCorrespondingDeclaration());
+ }
+ } else {
+ if (((FieldAccessExpr) node).getName().getId().equals("length")) {
+ ResolvedType scopeType = ((FieldAccessExpr) node).getScope().calculateResolvedType();
+ if (scopeType.isArray()) {
+ if (resultClass.isInstance(ArrayLengthValueDeclaration.INSTANCE)) {
+ return resultClass.cast(ArrayLengthValueDeclaration.INSTANCE);
+ }
+ }
+ }
+ throw new UnsolvedSymbolException("We are unable to find the value declaration corresponding to " + node);
+ }
+ }
+ if (node instanceof ThisExpr) {
+ SymbolReference result = JavaParserFacade.get(typeSolver).solve((ThisExpr) node);
+ if (result.isSolved()) {
+ if (resultClass.isInstance(result.getCorrespondingDeclaration())) {
+ return resultClass.cast(result.getCorrespondingDeclaration());
+ }
+ } else {
+ throw new UnsolvedSymbolException("We are unable to find the type declaration corresponding to " + node);
+ }
+ }
+ if (node instanceof ExplicitConstructorInvocationStmt) {
+ SymbolReference result = JavaParserFacade.get(typeSolver).solve((ExplicitConstructorInvocationStmt) node);
+ if (result.isSolved()) {
+ if (resultClass.isInstance(result.getCorrespondingDeclaration())) {
+ return resultClass.cast(result.getCorrespondingDeclaration());
+ }
+ } else {
+ throw new UnsolvedSymbolException("We are unable to find the constructor declaration corresponding to " + node);
+ }
+ }
+ if (node instanceof Parameter) {
+ if (ResolvedParameterDeclaration.class.equals(resultClass)) {
+ Parameter parameter = (Parameter) node;
+ CallableDeclaration callableDeclaration = node.findAncestor(CallableDeclaration.class).get();
+ ResolvedMethodLikeDeclaration resolvedMethodLikeDeclaration;
+ if (callableDeclaration.isConstructorDeclaration()) {
+ resolvedMethodLikeDeclaration = callableDeclaration.asConstructorDeclaration().resolve();
+ } else {
+ resolvedMethodLikeDeclaration = callableDeclaration.asMethodDeclaration().resolve();
+ }
+ for (int i = 0; i < resolvedMethodLikeDeclaration.getNumberOfParams(); i++) {
+ if (resolvedMethodLikeDeclaration.getParam(i).getName().equals(parameter.getNameAsString())) {
+ return resultClass.cast(resolvedMethodLikeDeclaration.getParam(i));
+ }
+ }
+ }
+ }
+ if (node instanceof AnnotationExpr) {
+ SymbolReference result = JavaParserFacade.get(typeSolver).solve((AnnotationExpr) node);
+ if (result.isSolved()) {
+ if (resultClass.isInstance(result.getCorrespondingDeclaration())) {
+ return resultClass.cast(result.getCorrespondingDeclaration());
+ }
+ } else {
+ throw new UnsolvedSymbolException("We are unable to find the annotation declaration corresponding to " + node);
+ }
+ }
+ if (node instanceof PatternExpr) {
+ SymbolReference extends ResolvedValueDeclaration> result = JavaParserFacade.get(typeSolver).solve((PatternExpr) node);
+ if (result.isSolved()) {
+ if (resultClass.isInstance(result.getCorrespondingDeclaration())) {
+ return resultClass.cast(result.getCorrespondingDeclaration());
+ }
+ } else {
+ throw new UnsolvedSymbolException("We are unable to find the method declaration corresponding to " + node);
+ }
+ }
+ throw new UnsupportedOperationException("Unable to find the declaration of type " + resultClass.getSimpleName()
+ + " from " + node.getClass().getSimpleName());
+ }
+
+ @Override
+ public T toResolvedType(Type javaparserType, Class resultClass) {
+ ResolvedType resolvedType = JavaParserFacade.get(typeSolver).convertToUsage(javaparserType, javaparserType);
+ if (resultClass.isInstance(resolvedType)) {
+ return resultClass.cast(resolvedType);
+ }
+ throw new UnsupportedOperationException("Unable to get the resolved type of class "
+ + resultClass.getSimpleName() + " from " + javaparserType);
+ }
+
+ @Override
+ public ResolvedType calculateType(Expression expression) {
+ return JavaParserFacade.get(typeSolver).getType(expression);
+ }
+}
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/SourceFileInfoExtractor.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/SourceFileInfoExtractor.java
new file mode 100644
index 0000000000000000000000000000000000000000..03f23c498ee817f4f18c1074e3301a59685029b6
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/SourceFileInfoExtractor.java
@@ -0,0 +1,224 @@
+/*
+ * Copyright (C) 2015-2016 Federico Tomassetti
+ * Copyright (C) 2017-2020 The JavaParser Team.
+ *
+ * This file is part of JavaParser.
+ *
+ * JavaParser can be used either under the terms of
+ * a) the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * b) the terms of the Apache License
+ *
+ * You should have received a copy of both licenses in LICENCE.LGPL and
+ * LICENCE.APACHE. Please refer to those files for details.
+ *
+ * JavaParser is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ */
+
+package com.github.javaparser.symbolsolver;
+
+import com.github.javaparser.ast.CompilationUnit;
+import com.github.javaparser.ast.ImportDeclaration;
+import com.github.javaparser.ast.Node;
+import com.github.javaparser.ast.PackageDeclaration;
+import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration;
+import com.github.javaparser.ast.body.MethodDeclaration;
+import com.github.javaparser.ast.body.VariableDeclarator;
+import com.github.javaparser.ast.expr.Expression;
+import com.github.javaparser.ast.expr.MethodCallExpr;
+import com.github.javaparser.ast.stmt.Statement;
+import com.github.javaparser.ast.stmt.SwitchEntry;
+import com.github.javaparser.resolution.declarations.ResolvedMethodDeclaration;
+import com.github.javaparser.resolution.declarations.ResolvedTypeDeclaration;
+import com.github.javaparser.resolution.types.ResolvedReferenceType;
+import com.github.javaparser.resolution.types.ResolvedType;
+import com.github.javaparser.symbolsolver.javaparsermodel.JavaParserFacade;
+import com.github.javaparser.symbolsolver.model.resolution.SymbolReference;
+import com.github.javaparser.symbolsolver.model.resolution.TypeSolver;
+
+import java.io.IOException;
+import java.io.PrintStream;
+import java.nio.file.FileVisitResult;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.SimpleFileVisitor;
+import java.nio.file.attribute.BasicFileAttributes;
+import java.util.ArrayList;
+import java.util.List;
+
+import static com.github.javaparser.StaticJavaParser.parse;
+import static com.github.javaparser.symbolsolver.javaparser.Navigator.demandParentNode;
+import static java.util.Comparator.comparing;
+
+/**
+ * Resolves resolvable nodes from one or more source files, and reports the results.
+ * It is mainly intended as an example usage of JavaSymbolSolver.
+ *
+ * @author Federico Tomassetti
+ */
+public class SourceFileInfoExtractor {
+
+ private final TypeSolver typeSolver;
+
+ private int successes = 0;
+ private int failures = 0;
+ private int unsupported = 0;
+ private boolean printFileName = true;
+ private PrintStream out = System.out;
+ private PrintStream err = System.err;
+ private boolean verbose = false;
+
+ public SourceFileInfoExtractor(TypeSolver typeSolver) {
+ this.typeSolver = typeSolver;
+ }
+
+ public void setVerbose(boolean verbose) {
+ this.verbose = verbose;
+ }
+
+ public void setPrintFileName(boolean printFileName) {
+ this.printFileName = printFileName;
+ }
+
+ public void setOut(PrintStream out) {
+ this.out = out;
+ }
+
+ public void setErr(PrintStream err) {
+ this.err = err;
+ }
+
+ public int getSuccesses() {
+ return successes;
+ }
+
+ public int getUnsupported() {
+ return unsupported;
+ }
+
+ public int getFailures() {
+ return failures;
+ }
+
+ private void solveTypeDecl(ClassOrInterfaceDeclaration node) {
+ ResolvedTypeDeclaration typeDeclaration = JavaParserFacade.get(typeSolver).getTypeDeclaration(node);
+ if (typeDeclaration.isClass()) {
+ out.println("\n[ Class " + typeDeclaration.getQualifiedName() + " ]");
+ for (ResolvedReferenceType sc : typeDeclaration.asClass().getAllSuperClasses()) {
+ out.println(" superclass: " + sc.getQualifiedName());
+ }
+ for (ResolvedReferenceType sc : typeDeclaration.asClass().getAllInterfaces()) {
+ out.println(" interface: " + sc.getQualifiedName());
+ }
+ }
+ }
+
+ private void solve(Node node) {
+ if (node instanceof ClassOrInterfaceDeclaration) {
+ solveTypeDecl((ClassOrInterfaceDeclaration) node);
+ } else if (node instanceof Expression) {
+ Node parentNode = demandParentNode(node);
+ if (parentNode instanceof ImportDeclaration ||
+ parentNode instanceof Expression ||
+ parentNode instanceof MethodDeclaration ||
+ parentNode instanceof PackageDeclaration) {
+ // skip
+ return;
+ }
+ if (parentNode instanceof Statement ||
+ parentNode instanceof VariableDeclarator ||
+ parentNode instanceof SwitchEntry) {
+ try {
+ ResolvedType ref = JavaParserFacade.get(typeSolver).getType(node);
+ out.println(" Line " + lineNr(node) + ") " + node + " ==> " + ref.describe());
+ successes++;
+ } catch (UnsupportedOperationException upe) {
+ unsupported++;
+ err.println(upe.getMessage());
+ throw upe;
+ } catch (RuntimeException re) {
+ failures++;
+ err.println(re.getMessage());
+ throw re;
+ }
+ }
+ }
+ }
+
+ private void solveMethodCalls(Node node) {
+ if (node instanceof MethodCallExpr) {
+ out.println(" Line " + lineNr(node) + ") " + node + " ==> " + toString((MethodCallExpr) node));
+ }
+ for (Node child : node.getChildNodes()) {
+ solveMethodCalls(child);
+ }
+ }
+
+ private String toString(MethodCallExpr node) {
+ try {
+ return toString(JavaParserFacade.get(typeSolver).solve(node));
+ } catch (Exception e) {
+ if (verbose) {
+ System.err.println("Error resolving call at L" + lineNr(node) + ": " + node);
+ e.printStackTrace();
+ }
+ return "ERROR";
+ }
+ }
+
+ private String toString(SymbolReference methodDeclarationSymbolReference) {
+ if (methodDeclarationSymbolReference.isSolved()) {
+ return methodDeclarationSymbolReference.getCorrespondingDeclaration().getQualifiedSignature();
+ } else {
+ return "UNSOLVED";
+ }
+ }
+
+ private List collectAllNodes(Node node) {
+ List nodes = new ArrayList<>();
+ node.walk(nodes::add);
+ nodes.sort(comparing(n -> n.getBegin().get()));
+ return nodes;
+ }
+
+ public void solve(Path path) throws IOException {
+ Files.walkFileTree(path, new SimpleFileVisitor() {
+ @Override
+ public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
+ if (file.toString().endsWith(".java")) {
+ if (printFileName) {
+ out.println("- parsing " + file.toAbsolutePath());
+ }
+ CompilationUnit cu = parse(file);
+ List nodes = collectAllNodes(cu);
+ nodes.forEach(n -> solve(n));
+ }
+ return FileVisitResult.CONTINUE;
+ }
+ });
+ }
+
+ public void solveMethodCalls(Path path) throws IOException {
+ Files.walkFileTree(path, new SimpleFileVisitor() {
+ @Override
+ public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
+ if (file.toString().endsWith(".java")) {
+ if (printFileName) {
+ out.println("- parsing " + file.toAbsolutePath());
+ }
+ CompilationUnit cu = parse(file);
+ solveMethodCalls(cu);
+ }
+ return FileVisitResult.CONTINUE;
+ }
+ });
+ }
+
+ private int lineNr(Node node) {
+ return node.getRange().map(range -> range.begin.line).orElseThrow(IllegalStateException::new);
+ }
+}
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/cache/Cache.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/cache/Cache.java
new file mode 100644
index 0000000000000000000000000000000000000000..d0846e239da2f5c95cf9cd7195526c49a686c889
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/cache/Cache.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2007-2010 Júlio Vilmar Gesser.
+ * Copyright (C) 2011, 2013-2021 The JavaParser Team.
+ *
+ * This file is part of JavaParser.
+ *
+ * JavaParser can be used either under the terms of
+ * a) the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * b) the terms of the Apache License
+ *
+ * You should have received a copy of both licenses in LICENCE.LGPL and
+ * LICENCE.APACHE. Please refer to those files for details.
+ *
+ * JavaParser is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ */
+
+package com.github.javaparser.symbolsolver.cache;
+
+import java.util.Optional;
+
+/**
+ * A contract that defines a semi-persistent mapping of keys and values.
+ *
+ * Cache entries are manually added using put({@link K}, {@link V}),
+ * and are stored in the cache until either evicted or manually removed.
+ * After storing a value, it can be accessed using get({@link K}).
+ *
+ * @param The type of the key.
+ * @param The type of the value.
+ */
+public interface Cache {
+
+ /**
+ * Associates value with key in this cache.
+ *
+ * If the cache previously contained a value associated with key,
+ * the old value is replaced by value.
+ *
+ * @param key The key to be used as index.
+ * @param value The value to be stored.
+ */
+ void put(K key, V value);
+
+ /**
+ * Returns the value associated with {@code key} in this cache,
+ * or empty if there is no cached value for {@code key}.
+ *
+ * @param key The key to look for.
+ *
+ * @return The value stored in cache if present.
+ */
+ Optional get(K key);
+
+ /**
+ * Discards any cached value for this key.
+ *
+ * @param key The key to be discarded.
+ */
+ void remove(K key);
+
+ /**
+ * Discards all entries in the cache.
+ */
+ void removeAll();
+
+ /**
+ * Returns {@code True} if the cache contains a entry with the key,
+ * or {@code False} if there is none.
+ *
+ * @param key The key to be verified.
+ *
+ * @return {@code True} if the key is present.
+ */
+ boolean contains(K key);
+
+ /**
+ * Returns the number of entries in this cache.
+ *
+ * @return The cache size.
+ */
+ long size();
+
+ /**
+ * Returns {@code True} if the cache is empty, or {@code False}
+ * if there's at least a entry stored in cache.
+ *
+ * @return {@code True} if is empty.
+ */
+ boolean isEmpty();
+
+}
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/cache/GuavaCache.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/cache/GuavaCache.java
new file mode 100644
index 0000000000000000000000000000000000000000..88943309a3b9f21898041ef79d866e909a7cab72
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/cache/GuavaCache.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2007-2010 Júlio Vilmar Gesser.
+ * Copyright (C) 2011, 2013-2021 The JavaParser Team.
+ *
+ * This file is part of JavaParser.
+ *
+ * JavaParser can be used either under the terms of
+ * a) the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * b) the terms of the Apache License
+ *
+ * You should have received a copy of both licenses in LICENCE.LGPL and
+ * LICENCE.APACHE. Please refer to those files for details.
+ *
+ * JavaParser is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ */
+
+package com.github.javaparser.symbolsolver.cache;
+
+import java.util.Objects;
+import java.util.Optional;
+
+/**
+ * This class is used to wrap a Guava {@link com.google.common.cache.Cache}.
+ *
+ * @param The type of the key.
+ * @param The type of the value.
+ */
+public class GuavaCache implements Cache {
+
+ /**
+ * Wrap a Guava cache with a custom cache.
+ *
+ * @param guavaCache The guava cache to be wrapped-
+ *
+ * @param The expected type for the key.
+ * @param The expected type for the value.
+ *
+ * @return A newly created instance of {@link NoCache}.
+ */
+ public static GuavaCache create(com.google.common.cache.Cache guavaCache) {
+ return new GuavaCache<>(guavaCache);
+ }
+
+ private final com.google.common.cache.Cache guavaCache;
+
+ public GuavaCache(com.google.common.cache.Cache guavaCache) {
+ this.guavaCache = Objects.requireNonNull(guavaCache, "The argument GuavaCache can't be null.");
+ }
+
+ @Override
+ public void put(K key, V value) {
+ guavaCache.put(key, value);
+ }
+
+ @Override
+ public Optional get(K key) {
+ return Optional.ofNullable(
+ guavaCache.getIfPresent(key)
+ );
+ }
+
+ @Override
+ public void remove(K key) {
+ guavaCache.invalidate(key);
+ }
+
+ @Override
+ public void removeAll() {
+ guavaCache.invalidateAll();
+ }
+
+ @Override
+ public boolean contains(K key) {
+ return get(key).isPresent();
+ }
+
+ @Override
+ public long size() {
+ return guavaCache.size();
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return size() == 0;
+ }
+
+}
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/cache/InMemoryCache.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/cache/InMemoryCache.java
new file mode 100644
index 0000000000000000000000000000000000000000..d0e4391895b533b180a7b748780ed965aab12d00
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/cache/InMemoryCache.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2007-2010 Júlio Vilmar Gesser.
+ * Copyright (C) 2011, 2013-2021 The JavaParser Team.
+ *
+ * This file is part of JavaParser.
+ *
+ * JavaParser can be used either under the terms of
+ * a) the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * b) the terms of the Apache License
+ *
+ * You should have received a copy of both licenses in LICENCE.LGPL and
+ * LICENCE.APACHE. Please refer to those files for details.
+ *
+ * JavaParser is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ */
+
+package com.github.javaparser.symbolsolver.cache;
+
+import java.util.Map;
+import java.util.Optional;
+import java.util.WeakHashMap;
+
+/**
+ * A cache implementation that stores the information in memory.
+ *
+ * The current implementation stores the values in memory in a {@link WeakHashMap}.
+ *
+ * @param The type of the key.
+ * @param The type of the value.
+ */
+public class InMemoryCache implements Cache {
+
+ /**
+ * Create a new instance for a cache in memory.
+ *
+ * @param The expected type for the key.
+ * @param The expected type for the value.
+ *
+ * @return A newly created instance of {@link InMemoryCache}.
+ */
+ public static InMemoryCache create() {
+ return new InMemoryCache<>();
+ }
+
+ private final Map mappedValues = new WeakHashMap<>();
+
+ @Override
+ public void put(K key, V value) {
+ mappedValues.put(key, value);
+ }
+
+ @Override
+ public Optional get(K key) {
+ return Optional.ofNullable(
+ mappedValues.get(key)
+ );
+ }
+
+ @Override
+ public void remove(K key) {
+ mappedValues.remove(key);
+ }
+
+ @Override
+ public void removeAll() {
+ mappedValues.clear();
+ }
+
+ @Override
+ public boolean contains(K key) {
+ return mappedValues.containsKey(key);
+ }
+
+ @Override
+ public long size() {
+ return mappedValues.size();
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return mappedValues.isEmpty();
+ }
+
+}
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/cache/NoCache.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/cache/NoCache.java
new file mode 100644
index 0000000000000000000000000000000000000000..3fb4931e0fd80352a68e0afab8cde0ef94c944d3
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/cache/NoCache.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2007-2010 Júlio Vilmar Gesser.
+ * Copyright (C) 2011, 2013-2021 The JavaParser Team.
+ *
+ * This file is part of JavaParser.
+ *
+ * JavaParser can be used either under the terms of
+ * a) the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * b) the terms of the Apache License
+ *
+ * You should have received a copy of both licenses in LICENCE.LGPL and
+ * LICENCE.APACHE. Please refer to those files for details.
+ *
+ * JavaParser is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ */
+
+package com.github.javaparser.symbolsolver.cache;
+
+import java.util.Optional;
+
+/**
+ * A cache implementation that does not store any information.
+ *
+ * @param The key type.
+ * @param The value type.
+ */
+public class NoCache implements Cache {
+
+ /**
+ * Create a new instance.
+ *
+ * @param The expected type for the key.
+ * @param The expected type for the value.
+ *
+ * @return A newly created instance of {@link NoCache}.
+ */
+ public static NoCache create() {
+ return new NoCache<>();
+ }
+
+ @Override
+ public void put(K key, V value) {
+ // Nothing to do here.
+ }
+
+ @Override
+ public Optional get(K key) {
+ return Optional.empty();
+ }
+
+ @Override
+ public void remove(K key) {
+ // Nothing to do here.
+ }
+
+ @Override
+ public void removeAll() {
+ // Nothing to do here.
+ }
+
+ @Override
+ public boolean contains(K key) {
+ return false;
+ }
+
+ @Override
+ public long size() {
+ return 0;
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return true;
+ }
+
+}
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/core/package-info.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/core/package-info.java
new file mode 100644
index 0000000000000000000000000000000000000000..2755f8f6630aa70630ad4952f0ebbf7ff34f85a1
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/core/package-info.java
@@ -0,0 +1 @@
+package com.github.javaparser.symbolsolver.core;
\ No newline at end of file
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/core/resolution/Context.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/core/resolution/Context.java
new file mode 100644
index 0000000000000000000000000000000000000000..821ec7d1cb8d4a591cb98cb01be54c26d84a225f
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/core/resolution/Context.java
@@ -0,0 +1,381 @@
+/*
+ * Copyright (C) 2015-2016 Federico Tomassetti
+ * Copyright (C) 2017-2020 The JavaParser Team.
+ *
+ * This file is part of JavaParser.
+ *
+ * JavaParser can be used either under the terms of
+ * a) the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * b) the terms of the Apache License
+ *
+ * You should have received a copy of both licenses in LICENCE.LGPL and
+ * LICENCE.APACHE. Please refer to those files for details.
+ *
+ * JavaParser is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ */
+
+package com.github.javaparser.symbolsolver.core.resolution;
+
+import com.github.javaparser.ast.Node;
+import com.github.javaparser.ast.body.Parameter;
+import com.github.javaparser.ast.body.VariableDeclarator;
+import com.github.javaparser.ast.expr.PatternExpr;
+import com.github.javaparser.resolution.MethodUsage;
+import com.github.javaparser.resolution.declarations.*;
+import com.github.javaparser.resolution.types.ResolvedType;
+import com.github.javaparser.symbolsolver.javaparsermodel.contexts.AbstractJavaParserContext;
+import com.github.javaparser.symbolsolver.model.resolution.SymbolReference;
+import com.github.javaparser.symbolsolver.model.resolution.Value;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Optional;
+
+/**
+ * Context is very similar to scope.
+ * In the context we look for solving symbols.
+ *
+ * @author Federico Tomassetti
+ */
+public interface Context {
+
+ /**
+ * @return The parent context, if there is one. For example, a method exists within a compilation unit.
+ */
+ Optional getParent();
+
+
+ /* Type resolution */
+
+ /**
+ * Default to no generics available in this context, delegating solving to the parent context.
+ * Contexts which have generics available to it will override this method.
+ * For example class and method declarations, and method calls.
+ *
+ * @param name For example, solving {@code T} within {@code class Foo {}} or
+ * @return The resolved generic type, if found.
+ */
+ default Optional solveGenericType(String name) {
+ // Default to solving within the parent context.
+ return solveGenericTypeInParentContext(name);
+ }
+
+ default Optional solveGenericTypeInParentContext(String name) {
+ Optional optionalParentContext = getParent();
+ if (!optionalParentContext.isPresent()) {
+ return Optional.empty();
+ }
+
+ // Delegate solving to the parent context.
+ return optionalParentContext.get().solveGenericType(name);
+ }
+
+ /**
+ * Default to being unable to solve any reference in this context, delegating solving to the parent context.
+ * Contexts which exist as the "parent" of a resolvable type will override this method.
+ * For example, a compilation unit can contain classes. A class declaration can also contain types (e.g. a subclass).
+ *
+ * @param name For example, solving {@code List} or {@code java.util.List}.
+ * @return The declaration associated with the given type name.
+ */
+ default SymbolReference solveType(String name) {
+ // Default to solving within the parent context.
+ return solveTypeInParentContext(name);
+ }
+
+ default SymbolReference solveTypeInParentContext(String name) {
+ Optional optionalParentContext = getParent();
+ if (!optionalParentContext.isPresent()) {
+ return SymbolReference.unsolved(ResolvedReferenceTypeDeclaration.class);
+ }
+
+ // Delegate solving to the parent context.
+ return optionalParentContext.get().solveType(name);
+ }
+
+ /* Symbol resolution */
+
+ /**
+ * Used where a symbol is being used (e.g. solving {@code x} when used as an argument {@code doubleThis(x)}, or calculation {@code return x * 2;}).
+ * @param name the variable / reference / identifier used.
+ * @return // FIXME: Better documentation on how this is different to solveSymbolAsValue()
+ */
+ default SymbolReference extends ResolvedValueDeclaration> solveSymbol(String name) {
+ // Default to solving within the parent context.
+ return solveSymbolInParentContext(name);
+ }
+
+ default SymbolReference extends ResolvedValueDeclaration> solveSymbolInParentContext(String name) {
+ Optional optionalParentContext = getParent();
+ if (!optionalParentContext.isPresent()) {
+ return SymbolReference.unsolved(ResolvedValueDeclaration.class);
+ }
+
+ // Delegate solving to the parent context.
+ return optionalParentContext.get().solveSymbol(name);
+ }
+
+ /**
+ * Used where a symbol is being used (e.g. solving {@code x} when used as an argument {@code doubleThis(x)}, or calculation {@code return x * 2;}).
+ * @param name the variable / reference / identifier used.
+ * @return // FIXME: Better documentation on how this is different to solveSymbol()
+ */
+ default Optional solveSymbolAsValue(String name) {
+ SymbolReference extends ResolvedValueDeclaration> ref = solveSymbol(name);
+ if (!ref.isSolved()) {
+ return Optional.empty();
+ }
+
+ return Optional.of(Value.from(ref.getCorrespondingDeclaration()));
+ }
+
+ default Optional solveSymbolAsValueInParentContext(String name) {
+ SymbolReference extends ResolvedValueDeclaration> ref = solveSymbolInParentContext(name);
+ if (!ref.isSolved()) {
+ return Optional.empty();
+ }
+
+ return Optional.of(Value.from(ref.getCorrespondingDeclaration()));
+ }
+
+
+ /**
+ * The fields that are declared and in this immediate context made visible to a given child.
+ * This list could include values which are shadowed.
+ */
+ default List fieldsExposedToChild(Node child) {
+ return Collections.emptyList();
+ }
+
+ /**
+ * The local variables that are declared in this immediate context and made visible to a given child.
+ * This list could include values which are shadowed.
+ */
+ default List localVariablesExposedToChild(Node child) {
+ return Collections.emptyList();
+ }
+
+ /**
+ * The parameters that are declared in this immediate context and made visible to a given child.
+ * This list could include values which are shadowed.
+ */
+ default List parametersExposedToChild(Node child) {
+ return Collections.emptyList();
+ }
+
+ /**
+ * The pattern expressions that are declared in this immediate context and made visible to a given child.
+ * This list could include values which are shadowed.
+ */
+ default List patternExprsExposedToChild(Node child) {
+ return Collections.emptyList();
+ }
+
+ /**
+ */
+ default List patternExprsExposedFromChildren() {
+ return Collections.emptyList();
+ }
+
+ /**
+ */
+ default List negatedPatternExprsExposedFromChildren() {
+ return Collections.emptyList();
+ }
+
+ /**
+ * Aim to resolve the given name by looking for a variable matching it.
+ *
+ * To do it consider local variables that are visible in a certain scope as defined in JLS 6.3. Scope of a
+ * Declaration.
+ *
+ * 1. The scope of a local variable declaration in a block (§14.4) is the rest of the block in which the
+ * declaration
+ * appears, starting with its own initializer and including any further declarators to the right in the local
+ * variable declaration statement.
+ *
+ * 2. The scope of a local variable declared in the ForInit part of a basic for statement (§14.14.1) includes all
+ * of the following:
+ * 2.1 Its own initializer
+ * 2.2 Any further declarators to the right in the ForInit part of the for statement
+ * 2.3 The Expression and ForUpdate parts of the for statement
+ * 2.4 The contained Statement
+ *
+ * 3. The scope of a local variable declared in the FormalParameter part of an enhanced for statement (§14.14.2) is
+ * the contained Statement.
+ * 4. The scope of a parameter of an exception handler that is declared in a catch clause of a try statement
+ * (§14.20) is the entire block associated with the catch.
+ *
+ * 5. The scope of a variable declared in the ResourceSpecification of a try-with-resources statement (§14.20.3) is
+ * from the declaration rightward over the remainder of the ResourceSpecification and the entire try block
+ * associated with the try-with-resources statement.
+ */
+ default Optional localVariableDeclarationInScope(String name) {
+ if (!getParent().isPresent()) {
+ return Optional.empty();
+ }
+
+ // First check if the variable is directly declared within this context.
+ Node wrappedNode = ((AbstractJavaParserContext) this).getWrappedNode();
+ Context parentContext = getParent().get();
+ Optional localResolutionResults = parentContext
+ .localVariablesExposedToChild(wrappedNode)
+ .stream()
+ .filter(vd -> vd.getNameAsString().equals(name))
+ .findFirst();
+
+ if (localResolutionResults.isPresent()) {
+ return localResolutionResults;
+ }
+
+
+ // If we don't find the variable locally, escalate up the scope hierarchy to see if it is declared there.
+ return parentContext.localVariableDeclarationInScope(name);
+ }
+
+ default Optional parameterDeclarationInScope(String name) {
+ if (!getParent().isPresent()) {
+ return Optional.empty();
+ }
+
+ // First check if the parameter is directly declared within this context.
+ Node wrappedNode = ((AbstractJavaParserContext) this).getWrappedNode();
+ Context parentContext = getParent().get();
+ Optional localResolutionResults = parentContext
+ .parametersExposedToChild(wrappedNode)
+ .stream()
+ .filter(vd -> vd.getNameAsString().equals(name))
+ .findFirst();
+
+ if (localResolutionResults.isPresent()) {
+ return localResolutionResults;
+ }
+
+ // If we don't find the parameter locally, escalate up the scope hierarchy to see if it is declared there.
+ return parentContext.parameterDeclarationInScope(name);
+ }
+
+
+ /**
+ * With respect to solving, the AST "parent" of a block statement is not necessarily the same as the scope parent.
+ *
Example:
+ *
+ * {@code
+ * public String x() {
+ * if(x) {
+ * // Parent node: the block attached to the method declaration
+ * // Scope-parent: the block attached to the method declaration
+ * } else if {
+ * // Parent node: the if
+ * // Scope-parent: the block attached to the method declaration
+ * } else {
+ * // Parent node: the elseif
+ * // Scope-parent: the block attached to the method declaration
+ * }
+ * }
+ * }
+ */
+ default Optional patternExprInScope(String name) {
+ if (!getParent().isPresent()) {
+ return Optional.empty();
+ }
+ Context parentContext = getParent().get();
+
+ // FIXME: "scroll backwards" from the wrapped node
+ // FIXME: If there are multiple patterns, throw an error?
+
+ // First check if the pattern is directly declared within this context.
+ Node wrappedNode = ((AbstractJavaParserContext) this).getWrappedNode();
+ Optional localResolutionResults = parentContext
+ .patternExprsExposedToChild(wrappedNode)
+ .stream()
+ .filter(vd -> vd.getNameAsString().equals(name))
+ .findFirst();
+
+ if (localResolutionResults.isPresent()) {
+ return localResolutionResults;
+ }
+
+ // If we don't find the parameter locally, escalate up the scope hierarchy to see if it is declared there.
+ return parentContext.patternExprInScope(name);
+ }
+
+ default Optional fieldDeclarationInScope(String name) {
+ if (!getParent().isPresent()) {
+ return Optional.empty();
+ }
+ Context parentContext = getParent().get();
+ // First check if the parameter is directly declared within this context.
+ Node wrappedNode = ((AbstractJavaParserContext) this).getWrappedNode();
+ Optional localResolutionResults = parentContext
+ .fieldsExposedToChild(wrappedNode)
+ .stream()
+ .filter(vd -> vd.getName().equals(name))
+ .findFirst();
+
+ if (localResolutionResults.isPresent()) {
+ return localResolutionResults;
+ }
+
+ // If we don't find the field locally, escalate up the scope hierarchy to see if it is declared there.
+ return parentContext.fieldDeclarationInScope(name);
+ }
+
+
+ /* Constructor resolution */
+
+ /**
+ * We find the method declaration which is the best match for the given name and list of typeParametersValues.
+ */
+ default SymbolReference solveConstructor(List argumentsTypes) {
+ throw new IllegalArgumentException("Constructor resolution is available only on Class Context");
+ }
+
+ /* Methods resolution */
+
+ /**
+ * We find the method declaration which is the best match for the given name and list of typeParametersValues.
+ */
+ default SymbolReference solveMethod(String name, List argumentsTypes, boolean staticOnly) {
+ // Default to solving within the parent context.
+ return solveMethodInParentContext(name, argumentsTypes, staticOnly);
+ }
+
+ default SymbolReference solveMethodInParentContext(String name, List argumentsTypes, boolean staticOnly) {
+ Optional optionalParentContext = getParent();
+ if (!optionalParentContext.isPresent()) {
+ return SymbolReference.unsolved(ResolvedMethodDeclaration.class);
+ }
+
+ // Delegate solving to the parent context.
+ return optionalParentContext.get().solveMethod(name, argumentsTypes, staticOnly);
+ }
+
+ /**
+ * Similar to solveMethod but we return a MethodUsage.
+ * A MethodUsage corresponds to a MethodDeclaration plus the resolved type variables.
+ */
+ default Optional solveMethodAsUsage(String name, List argumentsTypes) {
+ SymbolReference methodSolved = solveMethod(name, argumentsTypes, false);
+ if (methodSolved.isSolved()) {
+ ResolvedMethodDeclaration methodDeclaration = methodSolved.getCorrespondingDeclaration();
+ if (!(methodDeclaration instanceof TypeVariableResolutionCapability)) {
+ throw new UnsupportedOperationException(String.format(
+ "Resolved method declarations must implement %s.",
+ TypeVariableResolutionCapability.class.getName()
+ ));
+ }
+
+ MethodUsage methodUsage = ((TypeVariableResolutionCapability) methodDeclaration).resolveTypeVariables(this, argumentsTypes);
+ return Optional.of(methodUsage);
+ } else {
+ return Optional.empty();
+ }
+ }
+
+}
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/core/resolution/MethodUsageResolutionCapability.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/core/resolution/MethodUsageResolutionCapability.java
new file mode 100644
index 0000000000000000000000000000000000000000..c3ce36dbd0c4b8c1b95f3d44385b1ab4cdab24b3
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/core/resolution/MethodUsageResolutionCapability.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2015-2016 Federico Tomassetti
+ * Copyright (C) 2017-2020 The JavaParser Team.
+ *
+ * This file is part of JavaParser.
+ *
+ * JavaParser can be used either under the terms of
+ * a) the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * b) the terms of the Apache License
+ *
+ * You should have received a copy of both licenses in LICENCE.LGPL and
+ * LICENCE.APACHE. Please refer to those files for details.
+ *
+ * JavaParser is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ */
+
+package com.github.javaparser.symbolsolver.core.resolution;
+
+import com.github.javaparser.resolution.MethodUsage;
+import com.github.javaparser.resolution.types.ResolvedType;
+
+import java.util.List;
+import java.util.Optional;
+
+public interface MethodUsageResolutionCapability {
+ Optional solveMethodAsUsage(String name, List argumentTypes, Context invocationContext,
+ List typeParameters);
+}
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/core/resolution/TypeVariableResolutionCapability.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/core/resolution/TypeVariableResolutionCapability.java
new file mode 100644
index 0000000000000000000000000000000000000000..a7dab945067749529cbae4c8850bbf02b54de840
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/core/resolution/TypeVariableResolutionCapability.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2015-2016 Federico Tomassetti
+ * Copyright (C) 2017-2020 The JavaParser Team.
+ *
+ * This file is part of JavaParser.
+ *
+ * JavaParser can be used either under the terms of
+ * a) the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * b) the terms of the Apache License
+ *
+ * You should have received a copy of both licenses in LICENCE.LGPL and
+ * LICENCE.APACHE. Please refer to those files for details.
+ *
+ * JavaParser is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ */
+
+package com.github.javaparser.symbolsolver.core.resolution;
+
+import com.github.javaparser.resolution.MethodUsage;
+import com.github.javaparser.resolution.types.ResolvedType;
+
+import java.util.List;
+
+public interface TypeVariableResolutionCapability {
+ MethodUsage resolveTypeVariables(Context context, List parameterTypes);
+}
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/declarations/common/MethodDeclarationCommonLogic.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/declarations/common/MethodDeclarationCommonLogic.java
new file mode 100644
index 0000000000000000000000000000000000000000..e0d0d3bf745de79851659a90f7af004411d22372
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/declarations/common/MethodDeclarationCommonLogic.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2015-2016 Federico Tomassetti
+ * Copyright (C) 2017-2020 The JavaParser Team.
+ *
+ * This file is part of JavaParser.
+ *
+ * JavaParser can be used either under the terms of
+ * a) the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * b) the terms of the Apache License
+ *
+ * You should have received a copy of both licenses in LICENCE.LGPL and
+ * LICENCE.APACHE. Please refer to those files for details.
+ *
+ * JavaParser is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ */
+
+package com.github.javaparser.symbolsolver.declarations.common;
+
+import com.github.javaparser.resolution.MethodUsage;
+import com.github.javaparser.resolution.declarations.ResolvedMethodDeclaration;
+import com.github.javaparser.resolution.declarations.ResolvedParameterDeclaration;
+import com.github.javaparser.resolution.declarations.ResolvedTypeParameterDeclaration;
+import com.github.javaparser.resolution.types.ResolvedType;
+import com.github.javaparser.resolution.types.ResolvedTypeVariable;
+import com.github.javaparser.symbolsolver.core.resolution.Context;
+import com.github.javaparser.symbolsolver.logic.InferenceContext;
+import com.github.javaparser.symbolsolver.model.resolution.TypeSolver;
+import com.github.javaparser.symbolsolver.reflectionmodel.MyObjectProvider;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+
+/**
+ * @author Federico Tomassetti
+ */
+public class MethodDeclarationCommonLogic {
+
+ private ResolvedMethodDeclaration methodDeclaration;
+ private TypeSolver typeSolver;
+
+ public MethodDeclarationCommonLogic(ResolvedMethodDeclaration methodDeclaration, TypeSolver typeSolver) {
+ this.methodDeclaration = methodDeclaration;
+ this.typeSolver = typeSolver;
+ }
+
+ public MethodUsage resolveTypeVariables(Context context, List parameterTypes) {
+ ResolvedType returnType = replaceTypeParams(methodDeclaration.getReturnType(), context);
+ List params = new ArrayList<>();
+ for (int i = 0; i < methodDeclaration.getNumberOfParams(); i++) {
+ ResolvedType replaced = replaceTypeParams(methodDeclaration.getParam(i).getType(), context);
+ params.add(replaced);
+ }
+
+ // We now look at the type parameter for the method which we can derive from the parameter types
+ // and then we replace them in the return type
+ // Map determinedTypeParameters = new HashMap<>();
+ InferenceContext inferenceContext = new InferenceContext(MyObjectProvider.INSTANCE);
+ for (int i = 0; i < methodDeclaration.getNumberOfParams(); i++) {
+ ResolvedParameterDeclaration formalParamDecl = methodDeclaration.getParam(i);
+ ResolvedType formalParamType = formalParamDecl.getType();
+
+ // Don't continue if a vararg parameter is reached and there are no arguments left
+ if (formalParamDecl.isVariadic() && parameterTypes.size() < methodDeclaration.getNumberOfParams()) {
+ break;
+ }
+
+ ResolvedType actualParamType = parameterTypes.get(i);
+
+ if (formalParamDecl.isVariadic() && !actualParamType.isArray()) {
+ formalParamType = formalParamType.asArrayType().getComponentType();
+ }
+
+ inferenceContext.addPair(formalParamType, actualParamType);
+ }
+
+ returnType = inferenceContext.resolve(inferenceContext.addSingle(returnType));
+
+ return new MethodUsage(methodDeclaration, params, returnType);
+ }
+
+ private ResolvedType replaceTypeParams(ResolvedType type, Context context) {
+ if (type.isTypeVariable()) {
+ ResolvedTypeParameterDeclaration typeParameter = type.asTypeParameter();
+ if (typeParameter.declaredOnType()) {
+ Optional typeParam = typeParamByName(typeParameter.getName(), context);
+ if (typeParam.isPresent()) {
+ type = typeParam.get();
+ }
+ }
+ }
+
+ if (type.isReferenceType()) {
+ type.asReferenceType().transformTypeParameters(tp -> replaceTypeParams(tp, context));
+ }
+
+ return type;
+ }
+
+ protected Optional typeParamByName(String name, Context context) {
+ return methodDeclaration.getTypeParameters().stream().filter(tp -> tp.getName().equals(name)).map(tp -> toType(tp)).findFirst();
+ }
+
+ protected ResolvedType toType(ResolvedTypeParameterDeclaration typeParameterDeclaration) {
+ return new ResolvedTypeVariable(typeParameterDeclaration);
+ }
+}
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparser/Navigator.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparser/Navigator.java
new file mode 100644
index 0000000000000000000000000000000000000000..b0a46db2b2917731f2301357ab9c2eb120e88b61
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparser/Navigator.java
@@ -0,0 +1,269 @@
+/*
+ * Copyright (C) 2015-2016 Federico Tomassetti
+ * Copyright (C) 2017-2020 The JavaParser Team.
+ *
+ * This file is part of JavaParser.
+ *
+ * JavaParser can be used either under the terms of
+ * a) the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * b) the terms of the Apache License
+ *
+ * You should have received a copy of both licenses in LICENCE.LGPL and
+ * LICENCE.APACHE. Please refer to those files for details.
+ *
+ * JavaParser is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ */
+
+package com.github.javaparser.symbolsolver.javaparser;
+
+import com.github.javaparser.ast.CompilationUnit;
+import com.github.javaparser.ast.Node;
+import com.github.javaparser.ast.body.*;
+import com.github.javaparser.ast.expr.MethodCallExpr;
+import com.github.javaparser.ast.expr.NameExpr;
+import com.github.javaparser.ast.expr.SimpleName;
+import com.github.javaparser.ast.stmt.ReturnStmt;
+import com.github.javaparser.ast.stmt.SwitchStmt;
+
+import java.util.Optional;
+
+/**
+ * This class can be used to easily retrieve nodes from a JavaParser AST.
+ *
+ * Note that methods with the prefix `demand` indicate that if the search value is not found, an exception will be thrown.
+ *
+ * @author Federico Tomassetti
+ */
+public final class Navigator {
+
+ private Navigator() {
+ // prevent instantiation
+ }
+
+ public static ClassOrInterfaceDeclaration demandClass(CompilationUnit cu, String qualifiedName) {
+ ClassOrInterfaceDeclaration cd = demandClassOrInterface(cu, qualifiedName);
+ if (cd.isInterface()) {
+ throw new IllegalStateException("Type is not a class");
+ }
+ return cd;
+ }
+
+ public static ClassOrInterfaceDeclaration demandClassOrInterface(CompilationUnit compilationUnit, String qualifiedName) {
+ return findType(compilationUnit, qualifiedName)
+ .map(res -> res.toClassOrInterfaceDeclaration().orElseThrow(() -> new IllegalStateException("Type is not a class or an interface, it is " + res.getClass().getCanonicalName())))
+ .orElseThrow(() -> new IllegalStateException("No type named '" + qualifiedName + "'found"));
+ }
+
+ /**
+ * Returns the {@code (i+1)}'th constructor of the given type declaration, in textual order. The constructor that
+ * appears first has the index 0, the second one the index 1, and so on.
+ *
+ * @param td The type declaration to search in. Note that only classes and enums have constructors.
+ * @param index The index of the desired constructor.
+ * @return The desired ConstructorDeclaration if it was found, else an exception is thrown.
+ */
+ public static ConstructorDeclaration demandConstructor(TypeDeclaration> td, int index) {
+ // TODO: Refactor to use `td.findAll(ConstructorDeclaration.class);` - potential difference re: searching only immediate children?
+ ConstructorDeclaration found = null;
+ int i = 0;
+ for (BodyDeclaration> bd : td.getMembers()) {
+ if (bd instanceof ConstructorDeclaration) {
+ ConstructorDeclaration cd = (ConstructorDeclaration) bd;
+ if (i == index) {
+ found = cd;
+ break;
+ }
+ i++;
+ }
+ }
+ if (found == null) {
+ throw new IllegalStateException("No constructor with index " + index);
+ }
+ return found;
+ }
+
+ public static EnumDeclaration demandEnum(CompilationUnit cu, String qualifiedName) {
+ Optional> res = findType(cu, qualifiedName);
+ if (!res.isPresent()) {
+ throw new IllegalStateException("No type found");
+ }
+ if (!(res.get() instanceof EnumDeclaration)) {
+ throw new IllegalStateException("Type is not an enum");
+ }
+ return (EnumDeclaration) res.get();
+ }
+
+ public static VariableDeclarator demandField(ClassOrInterfaceDeclaration cd, String name) {
+ for (BodyDeclaration> bd : cd.getMembers()) {
+ if (bd instanceof FieldDeclaration) {
+ FieldDeclaration fd = (FieldDeclaration) bd;
+ for (VariableDeclarator vd : fd.getVariables()) {
+ if (vd.getName().getId().equals(name)) {
+ return vd;
+ }
+ }
+ }
+ }
+ throw new IllegalStateException("No field with given name");
+ }
+
+ public static ClassOrInterfaceDeclaration demandInterface(CompilationUnit cu, String qualifiedName) {
+ ClassOrInterfaceDeclaration cd = demandClassOrInterface(cu, qualifiedName);
+ if (!cd.isInterface()) {
+ throw new IllegalStateException("Type is not an interface");
+ }
+ return cd;
+ }
+
+ public static MethodDeclaration demandMethod(TypeDeclaration> cd, String name) {
+ MethodDeclaration found = null;
+ for (BodyDeclaration> bd : cd.getMembers()) {
+ if (bd instanceof MethodDeclaration) {
+ MethodDeclaration md = (MethodDeclaration) bd;
+ if (md.getNameAsString().equals(name)) {
+ if (found != null) {
+ throw new IllegalStateException("Ambiguous getName");
+ }
+ found = md;
+ }
+ }
+ }
+ if (found == null) {
+ throw new IllegalStateException("No method called " + name);
+ }
+ return found;
+ }
+
+ public static N demandNodeOfGivenClass(Node node, Class clazz) {
+ return node.findFirst(clazz).orElseThrow(IllegalArgumentException::new);
+ }
+
+ public static Node demandParentNode(Node node) {
+ return node.getParentNode().orElseThrow(() -> new IllegalStateException("Parent not found, the node does not appear to be inserted in a correct AST"));
+ }
+
+ public static ReturnStmt demandReturnStmt(MethodDeclaration method) {
+ return demandNodeOfGivenClass(method, ReturnStmt.class);
+ }
+
+ public static SwitchStmt demandSwitch(Node node) {
+ return findSwitchHelper(node).orElseThrow(IllegalArgumentException::new);
+ }
+
+ public static Optional demandVariableDeclaration(Node node, String name) {
+ return node.findFirst(VariableDeclarator.class, n -> n.getNameAsString().equals(name));
+ }
+
+ public static Optional findMethodCall(Node node, String methodName) {
+ return node.findFirst(MethodCallExpr.class, n -> n.getNameAsString().equals(methodName));
+ }
+
+ public static Optional findNameExpression(Node node, String name) {
+ return node.findFirst(NameExpr.class, n -> n.getNameAsString().equals(name));
+ }
+
+ /**
+ * @deprecated Use {@link #demandNodeOfGivenClass(Node, Class)}
+ */
+ @Deprecated
+ public static N findNodeOfGivenClass(Node node, Class clazz) {
+ return demandNodeOfGivenClass(node, clazz);
+ }
+
+ /**
+ * @deprecated Use {@link #demandReturnStmt(MethodDeclaration)}
+ */
+ @Deprecated
+ public static ReturnStmt findReturnStmt(MethodDeclaration method) {
+ return demandReturnStmt(method);
+ }
+
+ public static Optional findSimpleName(Node node, String name) {
+ return node.findFirst(SimpleName.class, n -> n.asString().equals(name));
+ }
+
+ /**
+ * @deprecated Use {@link #demandSwitch(Node)}
+ */
+ @Deprecated
+ public static SwitchStmt findSwitch(Node node) {
+ return demandSwitch(node);
+ }
+
+ private static Optional findSwitchHelper(Node node) {
+ if (node instanceof SwitchStmt) {
+ return Optional.of((SwitchStmt) node);
+ }
+
+ return node.findFirst(SwitchStmt.class);
+ }
+
+ /**
+ * Looks among the type declared in the Compilation Unit for one having the specified name.
+ * The name can be qualified with respect to the compilation unit. For example, if the compilation
+ * unit is in package a.b; and it contains two top level classes named C and D, with class E being defined inside D
+ * then the qualifiedName that can be resolved are "C", "D", and "D.E".
+ */
+ public static Optional> findType(CompilationUnit cu, String qualifiedName) {
+ if (cu.getTypes().isEmpty()) {
+ return Optional.empty();
+ }
+
+ final String typeName = getOuterTypeName(qualifiedName);
+ Optional> type = cu.getTypes().stream().filter((t) -> t.getName().getId().equals(typeName)).findFirst();
+
+ final String innerTypeName = getInnerTypeName(qualifiedName);
+ if (type.isPresent() && !innerTypeName.isEmpty()) {
+ return findType(type.get(), innerTypeName);
+ }
+ return type;
+ }
+
+ /**
+ * Looks among the type declared in the TypeDeclaration for one having the specified name.
+ * The name can be qualified with respect to the TypeDeclaration. For example, if the class declaration defines
+ * class D and class D contains an internal class named E then the qualifiedName that can be resolved are "D", and
+ * "D.E".
+ */
+ public static Optional> findType(TypeDeclaration> td, String qualifiedName) {
+ final String typeName = getOuterTypeName(qualifiedName);
+
+ Optional> type = Optional.empty();
+ for (Node n : td.getMembers()) {
+ if (n instanceof TypeDeclaration && ((TypeDeclaration>) n).getName().getId().equals(typeName)) {
+ type = Optional.of((TypeDeclaration>) n);
+ break;
+ }
+ }
+ final String innerTypeName = getInnerTypeName(qualifiedName);
+ if (type.isPresent() && !innerTypeName.isEmpty()) {
+ return findType(type.get(), innerTypeName);
+ }
+ return type;
+ }
+
+ private static String getInnerTypeName(String qualifiedName) {
+ if (qualifiedName.contains(".")) {
+ return qualifiedName.split("\\.", 2)[1];
+ }
+ return "";
+ }
+
+ private static String getOuterTypeName(String qualifiedName) {
+ return qualifiedName.split("\\.", 2)[0];
+ }
+
+ /**
+ * @deprecated Use {@link #demandParentNode(Node)}
+ */
+ @Deprecated
+ public static Node requireParentNode(Node node) {
+ return demandParentNode(node);
+ }
+
+}
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparser/package-info.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparser/package-info.java
new file mode 100644
index 0000000000000000000000000000000000000000..cbeea03613f0283699fcd00999a5bcf6c404954f
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparser/package-info.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2015-2016 Federico Tomassetti
+ * Copyright (C) 2017-2020 The JavaParser Team.
+ *
+ * This file is part of JavaParser.
+ *
+ * JavaParser can be used either under the terms of
+ * a) the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * b) the terms of the Apache License
+ *
+ * You should have received a copy of both licenses in LICENCE.LGPL and
+ * LICENCE.APACHE. Please refer to those files for details.
+ *
+ * JavaParser is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ */
+
+/**
+ * This package contains utility to use JavaParser.
+ */
+package com.github.javaparser.symbolsolver.javaparser;
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/DefaultVisitorAdapter.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/DefaultVisitorAdapter.java
new file mode 100644
index 0000000000000000000000000000000000000000..6975aab383777fb46efc98a8adae19994a298bbf
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/DefaultVisitorAdapter.java
@@ -0,0 +1,536 @@
+/*
+ * Copyright (C) 2015-2016 Federico Tomassetti
+ * Copyright (C) 2017-2020 The JavaParser Team.
+ *
+ * This file is part of JavaParser.
+ *
+ * JavaParser can be used either under the terms of
+ * a) the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * b) the terms of the Apache License
+ *
+ * You should have received a copy of both licenses in LICENCE.LGPL and
+ * LICENCE.APACHE. Please refer to those files for details.
+ *
+ * JavaParser is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ */
+
+package com.github.javaparser.symbolsolver.javaparsermodel;
+
+import com.github.javaparser.ast.*;
+import com.github.javaparser.ast.body.*;
+import com.github.javaparser.ast.comments.BlockComment;
+import com.github.javaparser.ast.comments.JavadocComment;
+import com.github.javaparser.ast.comments.LineComment;
+import com.github.javaparser.ast.expr.*;
+import com.github.javaparser.ast.modules.*;
+import com.github.javaparser.ast.stmt.*;
+import com.github.javaparser.ast.type.*;
+import com.github.javaparser.ast.visitor.GenericVisitor;
+import com.github.javaparser.resolution.types.ResolvedType;
+
+public class DefaultVisitorAdapter implements GenericVisitor {
+ @Override
+ public ResolvedType visit(CompilationUnit node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(PackageDeclaration node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(TypeParameter node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(LineComment node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(BlockComment node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(ClassOrInterfaceDeclaration node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(EnumDeclaration node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(EnumConstantDeclaration node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(AnnotationDeclaration node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(AnnotationMemberDeclaration node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(FieldDeclaration node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(VariableDeclarator node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(ConstructorDeclaration node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(MethodDeclaration node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(Parameter node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(InitializerDeclaration node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(JavadocComment node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(ClassOrInterfaceType node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(PrimitiveType node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(ArrayType node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(ArrayCreationLevel node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(IntersectionType node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(UnionType node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(VoidType node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(WildcardType node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(UnknownType node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(ArrayAccessExpr node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(ArrayCreationExpr node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(ArrayInitializerExpr node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(AssignExpr node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(BinaryExpr node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(CastExpr node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(ClassExpr node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(ConditionalExpr node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(EnclosedExpr node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(FieldAccessExpr node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(InstanceOfExpr node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(PatternExpr node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(StringLiteralExpr node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(IntegerLiteralExpr node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(LongLiteralExpr node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(CharLiteralExpr node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(DoubleLiteralExpr node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(BooleanLiteralExpr node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(NullLiteralExpr node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(MethodCallExpr node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(NameExpr node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(ObjectCreationExpr node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(ThisExpr node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(SuperExpr node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(UnaryExpr node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(VariableDeclarationExpr node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(MarkerAnnotationExpr node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(SingleMemberAnnotationExpr node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(NormalAnnotationExpr node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(MemberValuePair node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(ExplicitConstructorInvocationStmt node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(LocalClassDeclarationStmt node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(LocalRecordDeclarationStmt node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(AssertStmt node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(BlockStmt node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(LabeledStmt node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(EmptyStmt node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(ExpressionStmt node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(SwitchStmt node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(SwitchEntry node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(BreakStmt node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(ReturnStmt node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(IfStmt node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(WhileStmt node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(ContinueStmt node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(DoStmt node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(ForEachStmt node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(ForStmt node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(ThrowStmt node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(SynchronizedStmt node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(TryStmt node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(CatchClause node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(LambdaExpr node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(MethodReferenceExpr node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(TypeExpr node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(NodeList node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(Name node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(SimpleName node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(ImportDeclaration node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(ModuleDeclaration node, Boolean arg) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(ModuleRequiresDirective node, Boolean arg) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(ModuleExportsDirective node, Boolean arg) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(ModuleProvidesDirective node, Boolean arg) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(ModuleUsesDirective node, Boolean arg) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(ModuleOpensDirective node, Boolean arg) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(UnparsableStmt node, Boolean arg) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(ReceiverParameter node, Boolean arg) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(VarType node, Boolean arg) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(Modifier node, Boolean arg) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(SwitchExpr node, Boolean arg) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(YieldStmt node, Boolean arg) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(TextBlockLiteralExpr node, Boolean arg) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(RecordDeclaration node, Boolean arg) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(CompactConstructorDeclaration node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+}
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/JavaParserFacade.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/JavaParserFacade.java
new file mode 100644
index 0000000000000000000000000000000000000000..b07283fc38d600b19181be9f8323081f5796a07d
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/JavaParserFacade.java
@@ -0,0 +1,779 @@
+/*
+ * Copyright (C) 2015-2016 Federico Tomassetti
+ * Copyright (C) 2017-2020 The JavaParser Team.
+ *
+ * This file is part of JavaParser.
+ *
+ * JavaParser can be used either under the terms of
+ * a) the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * b) the terms of the Apache License
+ *
+ * You should have received a copy of both licenses in LICENCE.LGPL and
+ * LICENCE.APACHE. Please refer to those files for details.
+ *
+ * JavaParser is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ */
+
+package com.github.javaparser.symbolsolver.javaparsermodel;
+
+import com.github.javaparser.ast.CompilationUnit;
+import com.github.javaparser.ast.DataKey;
+import com.github.javaparser.ast.Node;
+import com.github.javaparser.ast.NodeList;
+import com.github.javaparser.ast.body.*;
+import com.github.javaparser.ast.expr.*;
+import com.github.javaparser.ast.stmt.ExplicitConstructorInvocationStmt;
+import com.github.javaparser.ast.type.*;
+import com.github.javaparser.resolution.MethodAmbiguityException;
+import com.github.javaparser.resolution.MethodUsage;
+import com.github.javaparser.resolution.UnsolvedSymbolException;
+import com.github.javaparser.resolution.declarations.*;
+import com.github.javaparser.resolution.types.*;
+import com.github.javaparser.symbolsolver.core.resolution.Context;
+import com.github.javaparser.symbolsolver.javaparsermodel.contexts.FieldAccessContext;
+import com.github.javaparser.symbolsolver.javaparsermodel.declarations.JavaParserAnonymousClassDeclaration;
+import com.github.javaparser.symbolsolver.javaparsermodel.declarations.JavaParserEnumDeclaration;
+import com.github.javaparser.symbolsolver.javaparsermodel.declarations.JavaParserTypeVariableDeclaration;
+import com.github.javaparser.symbolsolver.model.resolution.SymbolReference;
+import com.github.javaparser.symbolsolver.model.resolution.TypeSolver;
+import com.github.javaparser.symbolsolver.model.typesystem.ReferenceTypeImpl;
+import com.github.javaparser.symbolsolver.reflectionmodel.ReflectionAnnotationDeclaration;
+import com.github.javaparser.symbolsolver.reflectionmodel.ReflectionClassDeclaration;
+import com.github.javaparser.symbolsolver.reflectionmodel.ReflectionEnumDeclaration;
+import com.github.javaparser.symbolsolver.reflectionmodel.ReflectionInterfaceDeclaration;
+import com.github.javaparser.symbolsolver.resolution.ConstructorResolutionLogic;
+import com.github.javaparser.symbolsolver.resolution.MethodResolutionLogic;
+import com.github.javaparser.symbolsolver.resolution.SymbolSolver;
+import com.github.javaparser.utils.Log;
+
+import java.util.*;
+import java.util.stream.Collectors;
+
+import static com.github.javaparser.symbolsolver.javaparser.Navigator.demandParentNode;
+import static com.github.javaparser.symbolsolver.model.resolution.SymbolReference.solved;
+import static com.github.javaparser.symbolsolver.model.resolution.SymbolReference.unsolved;
+
+/**
+ * Class to be used by final users to solve symbols for JavaParser ASTs.
+ *
+ * @author Federico Tomassetti
+ */
+public class JavaParserFacade {
+
+ private static final DataKey TYPE_WITH_LAMBDAS_RESOLVED = new DataKey() {
+ };
+ private static final DataKey TYPE_WITHOUT_LAMBDAS_RESOLVED = new DataKey() {
+ };
+
+ private static final Map instances = new WeakHashMap<>();
+
+ private static String JAVA_LANG_STRING = String.class.getCanonicalName();
+
+ private final TypeSolver typeSolver;
+ private final TypeExtractor typeExtractor;
+ private final SymbolSolver symbolSolver;
+
+ private JavaParserFacade(TypeSolver typeSolver) {
+ this.typeSolver = typeSolver.getRoot();
+ this.symbolSolver = new SymbolSolver(typeSolver);
+ this.typeExtractor = new TypeExtractor(typeSolver, this);
+ }
+
+ public TypeSolver getTypeSolver() {
+ return typeSolver;
+ }
+
+ public SymbolSolver getSymbolSolver() {
+ return symbolSolver;
+ }
+
+ /**
+ * Note that the addition of the modifier {@code synchronized} is specific and directly in response to issue #2668.
+ *
This MUST NOT be misinterpreted as a signal that JavaParser is safe to use within a multi-threaded environment.
+ *
+ *
Additional discussion and context from a user attempting multithreading can be found within issue #2671 .
+ *
+ *
+ * @see https://github.com/javaparser/javaparser/issues/2668
+ * @see https://github.com/javaparser/javaparser/issues/2671
+ */
+ public synchronized static JavaParserFacade get(TypeSolver typeSolver) {
+ return instances.computeIfAbsent(typeSolver, JavaParserFacade::new);
+ }
+
+ /**
+ * This method is used to clear internal caches for the sake of releasing memory.
+ */
+ public static void clearInstances() {
+ instances.clear();
+ }
+
+ protected static ResolvedType solveGenericTypes(ResolvedType type, Context context) {
+ if (type.isTypeVariable()) {
+ return context.solveGenericType(type.describe()).orElse(type);
+ }
+ if (type.isWildcard()) {
+ if (type.asWildcard().isExtends() || type.asWildcard().isSuper()) {
+ ResolvedWildcard wildcardUsage = type.asWildcard();
+ ResolvedType boundResolved = solveGenericTypes(wildcardUsage.getBoundedType(), context);
+ if (wildcardUsage.isExtends()) {
+ return ResolvedWildcard.extendsBound(boundResolved);
+ } else {
+ return ResolvedWildcard.superBound(boundResolved);
+ }
+ }
+ }
+ return type;
+ }
+
+ public SymbolReference extends ResolvedValueDeclaration> solve(NameExpr nameExpr) {
+ return symbolSolver.solveSymbol(nameExpr.getName().getId(), nameExpr);
+ }
+
+ public SymbolReference extends ResolvedValueDeclaration> solve(SimpleName nameExpr) {
+ return symbolSolver.solveSymbol(nameExpr.getId(), nameExpr);
+ }
+
+ public SymbolReference extends ResolvedValueDeclaration> solve(Expression expr) {
+ return expr.toNameExpr().map(this::solve).orElseThrow(() -> new IllegalArgumentException(expr.getClass().getCanonicalName()));
+ }
+
+ public SymbolReference solve(MethodCallExpr methodCallExpr) {
+ return solve(methodCallExpr, true);
+ }
+
+ public SymbolReference solve(MethodReferenceExpr methodReferenceExpr) {
+ return solve(methodReferenceExpr, true);
+ }
+
+ public SymbolReference solve(ObjectCreationExpr objectCreationExpr) {
+ return solve(objectCreationExpr, true);
+ }
+
+ public SymbolReference solve(ExplicitConstructorInvocationStmt explicitConstructorInvocationStmt) {
+ return solve(explicitConstructorInvocationStmt, true);
+ }
+
+ public SymbolReference solve(ExplicitConstructorInvocationStmt explicitConstructorInvocationStmt, boolean solveLambdas) {
+ // Constructor invocation must exist within a class (not interface).
+ Optional optAncestorClassOrInterfaceNode = explicitConstructorInvocationStmt.findAncestor(ClassOrInterfaceDeclaration.class);
+ if (!optAncestorClassOrInterfaceNode.isPresent()) {
+ return unsolved(ResolvedConstructorDeclaration.class);
+ }
+
+ ClassOrInterfaceDeclaration classOrInterfaceNode = optAncestorClassOrInterfaceNode.get();
+ ResolvedReferenceTypeDeclaration resolvedClassNode = classOrInterfaceNode.resolve();
+ if (!resolvedClassNode.isClass()) {
+ throw new IllegalStateException("Expected to be a class -- cannot call this() or super() within an interface.");
+ }
+
+ ResolvedTypeDeclaration typeDecl = null;
+ if (explicitConstructorInvocationStmt.isThis()) {
+ // this()
+ typeDecl = resolvedClassNode.asReferenceType();
+ } else {
+ // super()
+ Optional superClass = resolvedClassNode.asClass().getSuperClass();
+ if (superClass.isPresent() && superClass.get().getTypeDeclaration().isPresent()) {
+ typeDecl = superClass.get().getTypeDeclaration().get();
+ }
+ }
+ if (typeDecl == null) {
+ return unsolved(ResolvedConstructorDeclaration.class);
+ }
+
+ // Solve each of the arguments being passed into this constructor invocation.
+ List argumentTypes = new LinkedList<>();
+ List placeholders = new LinkedList<>();
+ solveArguments(explicitConstructorInvocationStmt, explicitConstructorInvocationStmt.getArguments(), solveLambdas, argumentTypes, placeholders);
+
+ // Determine which constructor is referred to, and return it.
+ SymbolReference res = ConstructorResolutionLogic.findMostApplicable(((ResolvedClassDeclaration) typeDecl).getConstructors(), argumentTypes, typeSolver);
+ for (LambdaArgumentTypePlaceholder placeholder : placeholders) {
+ placeholder.setMethod(res);
+ }
+
+ return res;
+ }
+
+ public SymbolReference solve(ThisExpr node) {
+ // If 'this' is prefixed by a class eg. MyClass.this
+ if (node.getTypeName().isPresent()) {
+ // Get the class name
+ String className = node.getTypeName().get().asString();
+ // Attempt to resolve using a typeSolver
+ SymbolReference clazz = typeSolver.tryToSolveType(className);
+ if (clazz.isSolved()) {
+ return solved(clazz.getCorrespondingDeclaration());
+ }
+ // Attempt to resolve locally in Compilation unit
+ Optional cu = node.findAncestor(CompilationUnit.class);
+ if (cu.isPresent()) {
+ Optional classByName = cu.get().getClassByName(className);
+ if (classByName.isPresent()) {
+ return solved(getTypeDeclaration(classByName.get()));
+ }
+ }
+ }
+ return solved(getTypeDeclaration(findContainingTypeDeclOrObjectCreationExpr(node)));
+ }
+
+ /**
+ * Given a constructor call find out to which constructor declaration it corresponds.
+ */
+ public SymbolReference solve(ObjectCreationExpr objectCreationExpr, boolean solveLambdas) {
+ List argumentTypes = new LinkedList<>();
+ List placeholders = new LinkedList<>();
+
+ solveArguments(objectCreationExpr, objectCreationExpr.getArguments(), solveLambdas, argumentTypes, placeholders);
+
+ ResolvedReferenceTypeDeclaration typeDecl = null;
+ if (objectCreationExpr.getAnonymousClassBody().isPresent()) {
+ typeDecl = new JavaParserAnonymousClassDeclaration(objectCreationExpr, typeSolver);
+ } else {
+ ResolvedType classDecl = JavaParserFacade.get(typeSolver).convert(objectCreationExpr.getType(), objectCreationExpr);
+ if (classDecl.isReferenceType() && classDecl.asReferenceType().getTypeDeclaration().isPresent()) {
+ typeDecl = classDecl.asReferenceType().getTypeDeclaration().get();
+ }
+ }
+ if (typeDecl == null) {
+ return unsolved(ResolvedConstructorDeclaration.class);
+ }
+ SymbolReference res = ConstructorResolutionLogic.findMostApplicable(typeDecl.getConstructors(), argumentTypes, typeSolver);
+ for (LambdaArgumentTypePlaceholder placeholder : placeholders) {
+ placeholder.setMethod(res);
+ }
+ return res;
+ }
+
+ private void solveArguments(Node node, NodeList args, boolean solveLambdas, List argumentTypes,
+ List placeholders) {
+ int i = 0;
+ for (Expression parameterValue : args) {
+ if (parameterValue instanceof LambdaExpr || parameterValue instanceof MethodReferenceExpr) {
+ LambdaArgumentTypePlaceholder placeholder = new LambdaArgumentTypePlaceholder(i);
+ argumentTypes.add(placeholder);
+ placeholders.add(placeholder);
+ } else {
+ try {
+ argumentTypes.add(JavaParserFacade.get(typeSolver).getType(parameterValue, solveLambdas));
+ } catch (UnsolvedSymbolException e) {
+ throw e;
+ } catch (Exception e) {
+ throw new RuntimeException(String.format("Unable to calculate the type of a parameter of a method call. Method call: %s, Parameter: %s",
+ node, parameterValue), e);
+ }
+ }
+ i++;
+ }
+ }
+
+ /**
+ * Given a method call find out to which method declaration it corresponds.
+ */
+ public SymbolReference solve(MethodCallExpr methodCallExpr, boolean solveLambdas) {
+ List argumentTypes = new LinkedList<>();
+ List placeholders = new LinkedList<>();
+
+ solveArguments(methodCallExpr, methodCallExpr.getArguments(), solveLambdas, argumentTypes, placeholders);
+
+ SymbolReference res = JavaParserFactory.getContext(methodCallExpr, typeSolver).solveMethod(methodCallExpr.getName().getId(), argumentTypes, false);
+ for (LambdaArgumentTypePlaceholder placeholder : placeholders) {
+ placeholder.setMethod(res);
+ }
+ return res;
+ }
+
+ /**
+ * Given a method reference find out to which method declaration it corresponds.
+ */
+ public SymbolReference solve(MethodReferenceExpr methodReferenceExpr, boolean solveLambdas) {
+ // pass empty argument list to be populated
+ List argumentTypes = new LinkedList<>();
+ return JavaParserFactory.getContext(methodReferenceExpr, typeSolver).solveMethod(methodReferenceExpr.getIdentifier(), argumentTypes, false);
+ }
+
+ public SymbolReference solve(AnnotationExpr annotationExpr) {
+ Context context = JavaParserFactory.getContext(annotationExpr, typeSolver);
+ SymbolReference typeDeclarationSymbolReference = context.solveType(annotationExpr.getNameAsString());
+ if (typeDeclarationSymbolReference.isSolved()) {
+ ResolvedAnnotationDeclaration annotationDeclaration = (ResolvedAnnotationDeclaration) typeDeclarationSymbolReference.getCorrespondingDeclaration();
+ return solved(annotationDeclaration);
+ } else {
+ return unsolved(ResolvedAnnotationDeclaration.class);
+ }
+ }
+
+ public SymbolReference solve(FieldAccessExpr fieldAccessExpr) {
+ return ((FieldAccessContext) JavaParserFactory.getContext(fieldAccessExpr, typeSolver)).solveField(fieldAccessExpr.getName().getId());
+ }
+
+ /**
+ * Get the type associated with the node.
+ *
+ * This method was originally intended to get the type of a value: any value has a type.
+ *
+ * For example:
+ *
+ * int foo(int a) {
+ * return a; // when getType is invoked on "a" it returns the type "int"
+ * }
+ *
+ *
+ * Now, users started using also of names of types itself, which do not have a type.
+ *
+ * For example:
+ *
+ * class A {
+ * int foo(int a) {
+ * return A.someStaticField; // when getType is invoked on "A", which represents a class, it returns
+ * // the type "A" itself while it used to throw UnsolvedSymbolException
+ * }
+ *
+ *
+ * To accommodate this usage and avoid confusion this method return
+ * the type itself when used on the name of type.
+ */
+ public ResolvedType getType(Node node) {
+ try {
+ return getType(node, true);
+ } catch (UnsolvedSymbolException e) {
+ if (node instanceof NameExpr) {
+ NameExpr nameExpr = (NameExpr) node;
+ SymbolReference typeDeclaration = JavaParserFactory.getContext(node, typeSolver)
+ .solveType(nameExpr.getNameAsString());
+ if (typeDeclaration.isSolved() && typeDeclaration.getCorrespondingDeclaration() instanceof ResolvedReferenceTypeDeclaration) {
+ ResolvedReferenceTypeDeclaration resolvedReferenceTypeDeclaration = (ResolvedReferenceTypeDeclaration) typeDeclaration.getCorrespondingDeclaration();
+ return ReferenceTypeImpl.undeterminedParameters(resolvedReferenceTypeDeclaration, typeSolver);
+ }
+ }
+ throw e;
+ }
+ }
+
+ public ResolvedType getType(Node node, boolean solveLambdas) {
+ if (solveLambdas) {
+ if (!node.containsData(TYPE_WITH_LAMBDAS_RESOLVED)) {
+ ResolvedType res = getTypeConcrete(node, solveLambdas);
+
+ node.setData(TYPE_WITH_LAMBDAS_RESOLVED, res);
+
+ boolean secondPassNecessary = false;
+ if (node instanceof MethodCallExpr) {
+ MethodCallExpr methodCallExpr = (MethodCallExpr) node;
+ for (Node arg : methodCallExpr.getArguments()) {
+ if (!arg.containsData(TYPE_WITH_LAMBDAS_RESOLVED)) {
+ getType(arg, true);
+ secondPassNecessary = true;
+ }
+ }
+ }
+ if (secondPassNecessary) {
+ node.removeData(TYPE_WITH_LAMBDAS_RESOLVED);
+ ResolvedType type = getType(node, true);
+ node.setData(TYPE_WITH_LAMBDAS_RESOLVED, type);
+
+ }
+ Log.trace("getType on %s -> %s", () -> node, () -> res);
+ }
+ return node.getData(TYPE_WITH_LAMBDAS_RESOLVED);
+ } else {
+ Optional res = find(TYPE_WITH_LAMBDAS_RESOLVED, node);
+ if (res.isPresent()) {
+ return res.get();
+ }
+ res = find(TYPE_WITHOUT_LAMBDAS_RESOLVED, node);
+ if (!res.isPresent()) {
+ ResolvedType resType = getTypeConcrete(node, solveLambdas);
+ node.setData(TYPE_WITHOUT_LAMBDAS_RESOLVED, resType);
+ Optional finalRes = res;
+ Log.trace("getType on %s (no solveLambdas) -> %s", () -> node, () -> finalRes);
+ return resType;
+ }
+ return res.get();
+ }
+ }
+
+ private Optional find(DataKey dataKey, Node node) {
+ if (node.containsData(dataKey)) {
+ return Optional.of(node.getData(dataKey));
+ }
+ return Optional.empty();
+ }
+
+ protected MethodUsage toMethodUsage(MethodReferenceExpr methodReferenceExpr, List paramTypes) {
+ Expression scope = methodReferenceExpr.getScope();
+ ResolvedType typeOfScope = getType(methodReferenceExpr.getScope());
+ if (!typeOfScope.isReferenceType()) {
+ throw new UnsupportedOperationException(typeOfScope.getClass().getCanonicalName());
+ }
+
+ Optional result;
+ Set allMethods = typeOfScope.asReferenceType().getTypeDeclaration()
+ .orElseThrow(() -> new RuntimeException("TypeDeclaration unexpectedly empty."))
+ .getAllMethods();
+
+ if (scope instanceof TypeExpr) {
+ // static methods should match all params
+ List staticMethodUsages = allMethods.stream()
+ .filter(it -> it.getDeclaration().isStatic())
+ .collect(Collectors.toList());
+
+ result = MethodResolutionLogic.findMostApplicableUsage(staticMethodUsages, methodReferenceExpr.getIdentifier(), paramTypes, typeSolver);
+
+ if (!paramTypes.isEmpty()) {
+ // instance methods are called on the first param and should match all other params
+ List instanceMethodUsages = allMethods.stream()
+ .filter(it -> !it.getDeclaration().isStatic())
+ .collect(Collectors.toList());
+
+ List instanceMethodParamTypes = new ArrayList<>(paramTypes);
+ instanceMethodParamTypes.remove(0); // remove the first one
+
+ Optional instanceResult = MethodResolutionLogic.findMostApplicableUsage(
+ instanceMethodUsages, methodReferenceExpr.getIdentifier(), instanceMethodParamTypes, typeSolver);
+ if (result.isPresent() && instanceResult.isPresent()) {
+ throw new MethodAmbiguityException("Ambiguous method call: cannot find a most applicable method for " + methodReferenceExpr.getIdentifier());
+ }
+
+ if (instanceResult.isPresent()) {
+ result = instanceResult;
+ }
+ }
+ } else {
+ result = MethodResolutionLogic.findMostApplicableUsage(new ArrayList<>(allMethods), methodReferenceExpr.getIdentifier(), paramTypes, typeSolver);
+
+ if (result.isPresent() && result.get().getDeclaration().isStatic()) {
+ throw new RuntimeException("Invalid static method reference " + methodReferenceExpr.getIdentifier());
+ }
+ }
+
+ if (!result.isPresent()) {
+ throw new UnsupportedOperationException();
+ }
+
+ return result.get();
+ }
+
+ protected ResolvedType getBinaryTypeConcrete(Node left, Node right, boolean solveLambdas, BinaryExpr.Operator operator) {
+ ResolvedType leftType = getTypeConcrete(left, solveLambdas);
+ ResolvedType rightType = getTypeConcrete(right, solveLambdas);
+
+ // JLS 15.18.1. String Concatenation Operator +
+ // If only one operand expression is of type String, then string conversion (§5.1.11) is performed on the other
+ // operand to produce a string at run time.
+ //
+ // The result of string concatenation is a reference to a String object that is the concatenation of the two
+ // operand strings. The characters of the left-hand operand precede the characters of the right-hand operand in
+ // the newly created string.
+
+ if (operator == BinaryExpr.Operator.PLUS) {
+ boolean isLeftString = leftType.isReferenceType() && leftType.asReferenceType()
+ .getQualifiedName().equals(JAVA_LANG_STRING);
+ boolean isRightString = rightType.isReferenceType() && rightType.asReferenceType()
+ .getQualifiedName().equals(JAVA_LANG_STRING);
+ if (isLeftString || isRightString) {
+ return isLeftString ? leftType : rightType;
+ }
+ }
+
+ // JLS 5.6.2. Binary Numeric Promotion
+ //
+ // Widening primitive conversion (§5.1.2) is applied to convert either or both operands as specified by the
+ // following rules:
+ //
+ // * If either operand is of type double, the other is converted to double.
+ // * Otherwise, if either operand is of type float, the other is converted to float.
+ // * Otherwise, if either operand is of type long, the other is converted to long.
+ // * Otherwise, both operands are converted to type int.
+
+ boolean isLeftNumeric = leftType.isPrimitive() && leftType.asPrimitive().isNumeric();
+ boolean isRightNumeric = rightType.isPrimitive() && rightType.asPrimitive().isNumeric();
+
+ if (isLeftNumeric && isRightNumeric) {
+ return leftType.asPrimitive().bnp(rightType.asPrimitive());
+ }
+
+ if (rightType.isAssignableBy(leftType)) {
+ return rightType;
+ }
+ return leftType;
+ }
+
+
+ /**
+ * Should return more like a TypeApplication: a TypeDeclaration and possible typeParametersValues or array
+ * modifiers.
+ */
+ private ResolvedType getTypeConcrete(Node node, boolean solveLambdas) {
+ if (node == null) throw new IllegalArgumentException();
+ return node.accept(typeExtractor, solveLambdas);
+ }
+
+ /**
+ * Where a node has an interface/class/enum declaration as its ancestor, return the nearest one.
+ *
+ * NOTE: See {@link #findContainingTypeDeclOrObjectCreationExpr} if wanting to include anonymous inner classes.
+ *
+ * For example, these all return X:
+ * {@code public interface X { ... node here ... }}
+ * {@code public class X { ... node here ... }}
+ * {@code public enum X { ... node here ... }}
+ *
+ * @param node The Node whose ancestors will be traversed,
+ * @return The first class/interface/enum declaration in the Node's ancestry.
+ */
+ protected TypeDeclaration> findContainingTypeDecl(Node node) {
+ Node parent = node;
+ while (true) {
+ parent = demandParentNode(parent);
+ if (parent instanceof TypeDeclaration) {
+ return (TypeDeclaration>) parent;
+ }
+ }
+ }
+
+ /**
+ * Where a node has an interface/class/enum declaration -- or an object creation expression (anonymous inner class)
+ * -- as its ancestor, return the nearest one.
+ *
+ * NOTE: See {@link #findContainingTypeDecl} if wanting to not include anonymous inner classes.
+ *
+ * For example, these all return X:
+ *
+ *
+ *
+ * @param node The Node whose ancestors will be traversed,
+ * @return The first class/interface/enum declaration -- or object creation expression (anonymous inner class) -- in
+ * the Node's ancestry.
+ */
+ protected Node findContainingTypeDeclOrObjectCreationExpr(Node node) {
+ Node parent = node;
+ boolean detachFlag = false;
+ while (true) {
+ parent = demandParentNode(parent);
+ if (parent instanceof BodyDeclaration) {
+ if (parent instanceof TypeDeclaration) {
+ return parent;
+ } else {
+ detachFlag = true;
+ }
+ } else if (parent instanceof ObjectCreationExpr) {
+ if (detachFlag) {
+ return parent;
+ }
+ }
+ }
+ }
+
+ /**
+ * Where a node has an interface/class/enum declaration -- or an object creation expression in an inner class
+ * references an outer class -- as its ancestor, return the declaration corresponding to the class name specified.
+ */
+ protected Node findContainingTypeDeclOrObjectCreationExpr(Node node, String className) {
+ Node parent = node;
+ boolean detachFlag = false;
+ while (true) {
+ parent = demandParentNode(parent);
+ if (parent instanceof BodyDeclaration) {
+ if (parent instanceof TypeDeclaration && ((TypeDeclaration>) parent).getFullyQualifiedName().get().endsWith(className)) {
+ return parent;
+ } else {
+ detachFlag = true;
+ }
+ } else if (parent instanceof ObjectCreationExpr) {
+ if (detachFlag) {
+ return parent;
+ }
+ }
+ }
+ }
+
+
+ public ResolvedType convertToUsageVariableType(VariableDeclarator var) {
+ return get(typeSolver).convertToUsage(var.getType(), var);
+ }
+
+ public ResolvedType convertToUsage(Type type, Node context) {
+ if (type.isUnknownType()) {
+ throw new IllegalArgumentException("Inferred lambda parameter type");
+ }
+ return convertToUsage(type, JavaParserFactory.getContext(context, typeSolver));
+ }
+
+ public ResolvedType convertToUsage(Type type) {
+ return convertToUsage(type, type);
+ }
+
+ // This is an hack around an issue in JavaParser
+ private String qName(ClassOrInterfaceType classOrInterfaceType) {
+ String name = classOrInterfaceType.getName().getId();
+ if (classOrInterfaceType.getScope().isPresent()) {
+ return qName(classOrInterfaceType.getScope().get()) + "." + name;
+ }
+ return name;
+ }
+
+ protected ResolvedType convertToUsage(Type type, Context context) {
+ if (context == null) {
+ throw new NullPointerException("Context should not be null");
+ }
+ if (type instanceof ClassOrInterfaceType) {
+ ClassOrInterfaceType classOrInterfaceType = (ClassOrInterfaceType) type;
+ String name = qName(classOrInterfaceType);
+ SymbolReference ref = context.solveType(name);
+ if (!ref.isSolved()) {
+ throw new UnsolvedSymbolException(name);
+ }
+ ResolvedTypeDeclaration typeDeclaration = ref.getCorrespondingDeclaration();
+ List typeParameters = Collections.emptyList();
+ if (classOrInterfaceType.getTypeArguments().isPresent()) {
+ typeParameters = classOrInterfaceType.getTypeArguments().get().stream().map((pt) -> convertToUsage(pt, context)).collect(Collectors.toList());
+ }
+ if (typeDeclaration.isTypeParameter()) {
+ if (typeDeclaration instanceof ResolvedTypeParameterDeclaration) {
+ return new ResolvedTypeVariable((ResolvedTypeParameterDeclaration) typeDeclaration);
+ } else {
+ JavaParserTypeVariableDeclaration javaParserTypeVariableDeclaration = (JavaParserTypeVariableDeclaration) typeDeclaration;
+ return new ResolvedTypeVariable(javaParserTypeVariableDeclaration.asTypeParameter());
+ }
+ } else {
+ return new ReferenceTypeImpl((ResolvedReferenceTypeDeclaration) typeDeclaration, typeParameters, typeSolver);
+ }
+ } else if (type instanceof PrimitiveType) {
+ return ResolvedPrimitiveType.byName(((PrimitiveType) type).getType().name());
+ } else if (type instanceof WildcardType) {
+ WildcardType wildcardType = (WildcardType) type;
+ if (wildcardType.getExtendedType().isPresent() && !wildcardType.getSuperType().isPresent()) {
+ return ResolvedWildcard.extendsBound(convertToUsage(wildcardType.getExtendedType().get(), context)); // removed (ReferenceTypeImpl)
+ } else if (!wildcardType.getExtendedType().isPresent() && wildcardType.getSuperType().isPresent()) {
+ return ResolvedWildcard.superBound(convertToUsage(wildcardType.getSuperType().get(), context)); // removed (ReferenceTypeImpl)
+ } else if (!wildcardType.getExtendedType().isPresent() && !wildcardType.getSuperType().isPresent()) {
+ return ResolvedWildcard.UNBOUNDED;
+ } else {
+ throw new UnsupportedOperationException(wildcardType.toString());
+ }
+ } else if (type instanceof VoidType) {
+ return ResolvedVoidType.INSTANCE;
+ } else if (type instanceof ArrayType) {
+ ArrayType jpArrayType = (ArrayType) type;
+ return new ResolvedArrayType(convertToUsage(jpArrayType.getComponentType(), context));
+ } else if (type instanceof UnionType) {
+ UnionType unionType = (UnionType) type;
+ return new ResolvedUnionType(unionType.getElements().stream().map(el -> convertToUsage(el, context)).collect(Collectors.toList()));
+ } else if (type instanceof VarType) {
+ Node parent = type.getParentNode().get();
+ if (!(parent instanceof VariableDeclarator)) {
+ throw new IllegalStateException("Trying to resolve a `var` which is not in a variable declaration.");
+ }
+ final VariableDeclarator variableDeclarator = (VariableDeclarator) parent;
+ return variableDeclarator.getInitializer()
+ .map(Expression::calculateResolvedType)
+ .orElseThrow(() -> new IllegalStateException("Cannot resolve `var` which has no initializer."));
+ } else {
+ throw new UnsupportedOperationException(type.getClass().getCanonicalName());
+ }
+ }
+
+
+ public ResolvedType convert(Type type, Node node) {
+ return convert(type, JavaParserFactory.getContext(node, typeSolver));
+ }
+
+ public ResolvedType convert(Type type, Context context) {
+ return convertToUsage(type, context);
+ }
+
+ public MethodUsage solveMethodAsUsage(MethodCallExpr call) {
+ List params = new ArrayList<>();
+ if (call.getArguments() != null) {
+ for (Expression param : call.getArguments()) {
+ //getTypeConcrete(Node node, boolean solveLambdas)
+ try {
+ params.add(getType(param, false));
+ } catch (Exception e) {
+ throw new RuntimeException(String.format("Error calculating the type of parameter %s of method call %s", param, call), e);
+ }
+ //params.add(getTypeConcrete(param, false));
+ }
+ }
+ Context context = JavaParserFactory.getContext(call, typeSolver);
+ Optional methodUsage = context.solveMethodAsUsage(call.getName().getId(), params);
+ if (!methodUsage.isPresent()) {
+ throw new RuntimeException("Method '" + call.getName() + "' cannot be resolved in context "
+ + call + " (line: " + call.getRange().map(r -> "" + r.begin.line).orElse("??") + ") " + context + ". Parameter types: " + params);
+ }
+ return methodUsage.get();
+ }
+
+ public ResolvedReferenceTypeDeclaration getTypeDeclaration(Node node) {
+ if (node instanceof TypeDeclaration) {
+ return getTypeDeclaration((TypeDeclaration) node);
+ } else if (node instanceof ObjectCreationExpr) {
+ return new JavaParserAnonymousClassDeclaration((ObjectCreationExpr) node, typeSolver);
+ } else {
+ throw new IllegalArgumentException();
+ }
+ }
+
+ public ResolvedReferenceTypeDeclaration getTypeDeclaration(ClassOrInterfaceDeclaration classOrInterfaceDeclaration) {
+ return JavaParserFactory.toTypeDeclaration(classOrInterfaceDeclaration, typeSolver);
+ }
+
+ /**
+ * "this" inserted in the given point, which type would have?
+ */
+ public ResolvedType getTypeOfThisIn(Node node) {
+ // TODO consider static methods
+ if (node instanceof ClassOrInterfaceDeclaration) {
+ return new ReferenceTypeImpl(getTypeDeclaration((ClassOrInterfaceDeclaration) node), typeSolver);
+ } else if (node instanceof EnumDeclaration) {
+ JavaParserEnumDeclaration enumDeclaration = new JavaParserEnumDeclaration((EnumDeclaration) node, typeSolver);
+ return new ReferenceTypeImpl(enumDeclaration, typeSolver);
+ } else if (node instanceof ObjectCreationExpr && ((ObjectCreationExpr) node).getAnonymousClassBody().isPresent()) {
+ JavaParserAnonymousClassDeclaration anonymousDeclaration = new JavaParserAnonymousClassDeclaration((ObjectCreationExpr) node, typeSolver);
+ return new ReferenceTypeImpl(anonymousDeclaration, typeSolver);
+ }
+ return getTypeOfThisIn(demandParentNode(node));
+ }
+
+ public ResolvedReferenceTypeDeclaration getTypeDeclaration(TypeDeclaration> typeDeclaration) {
+ return JavaParserFactory.toTypeDeclaration(typeDeclaration, typeSolver);
+ }
+
+ public ResolvedType classToResolvedType(Class> clazz) {
+ if (clazz.isPrimitive()) {
+ return ResolvedPrimitiveType.byName(clazz.getName());
+ }
+
+ ResolvedReferenceTypeDeclaration declaration;
+ if (clazz.isAnnotation()) {
+ declaration = new ReflectionAnnotationDeclaration(clazz, typeSolver);
+ } else if (clazz.isEnum()) {
+ declaration = new ReflectionEnumDeclaration(clazz, typeSolver);
+ } else if (clazz.isInterface()) {
+ declaration = new ReflectionInterfaceDeclaration(clazz, typeSolver);
+ } else {
+ declaration = new ReflectionClassDeclaration(clazz, typeSolver);
+ }
+ return new ReferenceTypeImpl(declaration, typeSolver);
+ }
+
+}
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/JavaParserFactory.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/JavaParserFactory.java
new file mode 100644
index 0000000000000000000000000000000000000000..ea00fbaab4f48f07851f0af6f77b42fb9ec7c170
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/JavaParserFactory.java
@@ -0,0 +1,180 @@
+/*
+ * Copyright (C) 2015-2016 Federico Tomassetti
+ * Copyright (C) 2017-2020 The JavaParser Team.
+ *
+ * This file is part of JavaParser.
+ *
+ * JavaParser can be used either under the terms of
+ * a) the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * b) the terms of the Apache License
+ *
+ * You should have received a copy of both licenses in LICENCE.LGPL and
+ * LICENCE.APACHE. Please refer to those files for details.
+ *
+ * JavaParser is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ */
+
+package com.github.javaparser.symbolsolver.javaparsermodel;
+
+import com.github.javaparser.ast.CompilationUnit;
+import com.github.javaparser.ast.Node;
+import com.github.javaparser.ast.body.*;
+import com.github.javaparser.ast.expr.*;
+import com.github.javaparser.ast.stmt.*;
+import com.github.javaparser.ast.type.ClassOrInterfaceType;
+import com.github.javaparser.ast.type.TypeParameter;
+import com.github.javaparser.resolution.declarations.ResolvedReferenceTypeDeclaration;
+import com.github.javaparser.symbolsolver.core.resolution.Context;
+import com.github.javaparser.symbolsolver.javaparsermodel.contexts.*;
+import com.github.javaparser.symbolsolver.javaparsermodel.declarations.*;
+import com.github.javaparser.symbolsolver.javaparsermodel.declarators.*;
+import com.github.javaparser.symbolsolver.model.resolution.TypeSolver;
+import com.github.javaparser.symbolsolver.resolution.SymbolDeclarator;
+
+import static com.github.javaparser.symbolsolver.javaparser.Navigator.demandParentNode;
+
+/**
+ * @author Federico Tomassetti
+ */
+public class JavaParserFactory {
+
+ public static Context getContext(Node node, TypeSolver typeSolver) {
+ if (node == null) {
+ throw new NullPointerException("Node should not be null");
+ }
+
+ // TODO: Is order important here?
+ if (node instanceof ArrayAccessExpr) {
+ return new ArrayAccessExprContext((ArrayAccessExpr) node, typeSolver);
+ } else if (node instanceof AnnotationDeclaration) {
+ return new AnnotationDeclarationContext((AnnotationDeclaration) node, typeSolver);
+ } else if (node instanceof BinaryExpr) {
+ return new BinaryExprContext((BinaryExpr) node, typeSolver);
+ } else if (node instanceof BlockStmt) {
+ return new BlockStmtContext((BlockStmt) node, typeSolver);
+ } else if (node instanceof CompilationUnit) {
+ return new CompilationUnitContext((CompilationUnit) node, typeSolver);
+ } else if (node instanceof EnclosedExpr) {
+ return new EnclosedExprContext((EnclosedExpr) node, typeSolver);
+ } else if (node instanceof ForEachStmt) {
+ return new ForEachStatementContext((ForEachStmt) node, typeSolver);
+ } else if (node instanceof ForStmt) {
+ return new ForStatementContext((ForStmt) node, typeSolver);
+ } else if (node instanceof IfStmt) {
+ return new IfStatementContext((IfStmt) node, typeSolver);
+ } else if (node instanceof InstanceOfExpr) {
+ return new InstanceOfExprContext((InstanceOfExpr) node, typeSolver);
+ } else if (node instanceof LambdaExpr) {
+ return new LambdaExprContext((LambdaExpr) node, typeSolver);
+ } else if (node instanceof MethodDeclaration) {
+ return new MethodContext((MethodDeclaration) node, typeSolver);
+ } else if (node instanceof ConstructorDeclaration) {
+ return new ConstructorContext((ConstructorDeclaration) node, typeSolver);
+ } else if (node instanceof ClassOrInterfaceDeclaration) {
+ return new ClassOrInterfaceDeclarationContext((ClassOrInterfaceDeclaration) node, typeSolver);
+ } else if (node instanceof MethodCallExpr) {
+ return new MethodCallExprContext((MethodCallExpr) node, typeSolver);
+ } else if (node instanceof MethodReferenceExpr) {
+ return new MethodReferenceExprContext((MethodReferenceExpr) node, typeSolver);
+ } else if (node instanceof EnumDeclaration) {
+ return new EnumDeclarationContext((EnumDeclaration) node, typeSolver);
+ } else if (node instanceof FieldAccessExpr) {
+ return new FieldAccessContext((FieldAccessExpr) node, typeSolver);
+ } else if (node instanceof SwitchEntry) {
+ return new SwitchEntryContext((SwitchEntry) node, typeSolver);
+ } else if (node instanceof TryStmt) {
+ return new TryWithResourceContext((TryStmt) node, typeSolver);
+ } else if (node instanceof Statement) {
+ return new StatementContext<>((Statement) node, typeSolver);
+ } else if (node instanceof CatchClause) {
+ return new CatchClauseContext((CatchClause) node, typeSolver);
+ } else if (node instanceof UnaryExpr) {
+ return new UnaryExprContext((UnaryExpr) node, typeSolver);
+ } else if (node instanceof VariableDeclarator) {
+ return new VariableDeclaratorContext((VariableDeclarator) node, typeSolver);
+ } else if (node instanceof VariableDeclarationExpr) {
+ return new VariableDeclarationExprContext((VariableDeclarationExpr) node, typeSolver);
+ } else if (node instanceof ObjectCreationExpr &&
+ ((ObjectCreationExpr) node).getAnonymousClassBody().isPresent()) {
+ return new AnonymousClassDeclarationContext((ObjectCreationExpr) node, typeSolver);
+ } else if (node instanceof ObjectCreationExpr) {
+ return new ObjectCreationContext((ObjectCreationExpr)node, typeSolver);
+ } else {
+ if (node instanceof NameExpr) {
+ // to resolve a name when in a fieldAccess context, we can go up until we get a node other than FieldAccessExpr,
+ // in order to prevent a infinite loop if the name is the same as the field (ie x.x, x.y.x, or x.y.z.x)
+ if (node.getParentNode().isPresent() && node.getParentNode().get() instanceof FieldAccessExpr) {
+ Node ancestor = node.getParentNode().get();
+ while (ancestor.getParentNode().isPresent()) {
+ ancestor = ancestor.getParentNode().get();
+ if (!(ancestor instanceof FieldAccessExpr)) {
+ break;
+ }
+ }
+ return getContext(ancestor, typeSolver);
+ }
+ if (node.getParentNode().isPresent() && node.getParentNode().get() instanceof ObjectCreationExpr && node.getParentNode().get().getParentNode().isPresent()) {
+ return getContext(node.getParentNode().get().getParentNode().get(), typeSolver);
+ }
+ }
+ final Node parentNode = demandParentNode(node);
+ if (node instanceof ClassOrInterfaceType && parentNode instanceof ClassOrInterfaceDeclaration) {
+ ClassOrInterfaceDeclaration parentDeclaration = (ClassOrInterfaceDeclaration) parentNode;
+ if (parentDeclaration.getImplementedTypes().contains(node) ||
+ parentDeclaration.getExtendedTypes().contains(node)) {
+ // When resolving names in implements and extends the body of the declaration
+ // should not be searched so use limited context.
+ return new ClassOrInterfaceDeclarationExtendsContext(parentDeclaration, typeSolver);
+ }
+ }
+ return getContext(parentNode, typeSolver);
+ }
+ }
+
+ public static SymbolDeclarator getSymbolDeclarator(Node node, TypeSolver typeSolver) {
+ if (node instanceof FieldDeclaration) {
+ return new FieldSymbolDeclarator((FieldDeclaration) node, typeSolver);
+ } else if (node instanceof Parameter) {
+ return new ParameterSymbolDeclarator((Parameter) node, typeSolver);
+ } else if (node instanceof PatternExpr) {
+ return new PatternSymbolDeclarator((PatternExpr) node, typeSolver);
+ } else if (node instanceof ExpressionStmt) {
+ ExpressionStmt expressionStmt = (ExpressionStmt) node;
+ if (expressionStmt.getExpression() instanceof VariableDeclarationExpr) {
+ return new VariableSymbolDeclarator((VariableDeclarationExpr) (expressionStmt.getExpression()), typeSolver);
+ } else {
+ return new NoSymbolDeclarator<>(expressionStmt, typeSolver);
+ }
+ } else if (node instanceof ForEachStmt) {
+ ForEachStmt foreachStmt = (ForEachStmt) node;
+ return new VariableSymbolDeclarator(foreachStmt.getVariable(), typeSolver);
+ } else {
+ return new NoSymbolDeclarator<>(node, typeSolver);
+ }
+ }
+
+ public static ResolvedReferenceTypeDeclaration toTypeDeclaration(Node node, TypeSolver typeSolver) {
+ if (node instanceof ClassOrInterfaceDeclaration) {
+ if (((ClassOrInterfaceDeclaration) node).isInterface()) {
+ return new JavaParserInterfaceDeclaration((ClassOrInterfaceDeclaration) node, typeSolver);
+ } else {
+ return new JavaParserClassDeclaration((ClassOrInterfaceDeclaration) node, typeSolver);
+ }
+ } else if (node instanceof TypeParameter) {
+ return new JavaParserTypeParameter((TypeParameter) node, typeSolver);
+ } else if (node instanceof EnumDeclaration) {
+ return new JavaParserEnumDeclaration((EnumDeclaration) node, typeSolver);
+ } else if (node instanceof AnnotationDeclaration) {
+ return new JavaParserAnnotationDeclaration((AnnotationDeclaration) node, typeSolver);
+ } else if (node instanceof EnumConstantDeclaration) {
+ return new JavaParserEnumDeclaration((EnumDeclaration) demandParentNode((EnumConstantDeclaration) node), typeSolver);
+ } else {
+ throw new IllegalArgumentException(node.getClass().getCanonicalName());
+ }
+ }
+}
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/LambdaArgumentTypePlaceholder.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/LambdaArgumentTypePlaceholder.java
new file mode 100644
index 0000000000000000000000000000000000000000..ed8eebea68119625419c3a477c841671dbde9da3
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/LambdaArgumentTypePlaceholder.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2015-2016 Federico Tomassetti
+ * Copyright (C) 2017-2020 The JavaParser Team.
+ *
+ * This file is part of JavaParser.
+ *
+ * JavaParser can be used either under the terms of
+ * a) the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * b) the terms of the Apache License
+ *
+ * You should have received a copy of both licenses in LICENCE.LGPL and
+ * LICENCE.APACHE. Please refer to those files for details.
+ *
+ * JavaParser is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ */
+
+package com.github.javaparser.symbolsolver.javaparsermodel;
+
+import com.github.javaparser.resolution.declarations.ResolvedMethodLikeDeclaration;
+import com.github.javaparser.resolution.types.ResolvedType;
+import com.github.javaparser.symbolsolver.model.resolution.SymbolReference;
+
+/**
+ * Placeholder used to represent a lambda argument type while it is being
+ * calculated.
+ *
+ * @author Federico Tomassetti
+ */
+public class LambdaArgumentTypePlaceholder implements ResolvedType {
+
+ private int pos;
+ private SymbolReference extends ResolvedMethodLikeDeclaration> method;
+
+ public LambdaArgumentTypePlaceholder(int pos) {
+ this.pos = pos;
+ }
+
+ @Override
+ public boolean isArray() {
+ return false;
+ }
+
+ @Override
+ public boolean isPrimitive() {
+ return false;
+ }
+
+ @Override
+ public boolean isReferenceType() {
+ return false;
+ }
+
+ @Override
+ public String describe() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean isTypeVariable() {
+ return false;
+ }
+
+ public void setMethod(SymbolReference extends ResolvedMethodLikeDeclaration> method) {
+ this.method = method;
+ }
+
+ @Override
+ public boolean isAssignableBy(ResolvedType other) {
+ throw new UnsupportedOperationException();
+ }
+
+}
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/TypeExtractor.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/TypeExtractor.java
new file mode 100644
index 0000000000000000000000000000000000000000..88b8f727d93ec21083f513adec943d6948d77475
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/TypeExtractor.java
@@ -0,0 +1,794 @@
+/*
+ * Copyright (C) 2015-2016 Federico Tomassetti
+ * Copyright (C) 2017-2020 The JavaParser Team.
+ *
+ * This file is part of JavaParser.
+ *
+ * JavaParser can be used either under the terms of
+ * a) the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * b) the terms of the Apache License
+ *
+ * You should have received a copy of both licenses in LICENCE.LGPL and
+ * LICENCE.APACHE. Please refer to those files for details.
+ *
+ * JavaParser is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ */
+
+package com.github.javaparser.symbolsolver.javaparsermodel;
+
+import com.github.javaparser.ast.CompilationUnit;
+import com.github.javaparser.ast.Node;
+import com.github.javaparser.ast.body.FieldDeclaration;
+import com.github.javaparser.ast.body.Parameter;
+import com.github.javaparser.ast.body.VariableDeclarator;
+import com.github.javaparser.ast.expr.*;
+import com.github.javaparser.ast.stmt.BlockStmt;
+import com.github.javaparser.ast.stmt.ExpressionStmt;
+import com.github.javaparser.ast.stmt.ReturnStmt;
+import com.github.javaparser.ast.type.ClassOrInterfaceType;
+import com.github.javaparser.ast.type.Type;
+import com.github.javaparser.ast.type.UnknownType;
+import com.github.javaparser.resolution.MethodUsage;
+import com.github.javaparser.resolution.UnsolvedSymbolException;
+import com.github.javaparser.resolution.declarations.*;
+import com.github.javaparser.resolution.types.ResolvedArrayType;
+import com.github.javaparser.resolution.types.ResolvedPrimitiveType;
+import com.github.javaparser.resolution.types.ResolvedType;
+import com.github.javaparser.resolution.types.ResolvedVoidType;
+import com.github.javaparser.symbolsolver.core.resolution.Context;
+import com.github.javaparser.symbolsolver.javaparsermodel.declarations.JavaParserSymbolDeclaration;
+import com.github.javaparser.symbolsolver.logic.FunctionalInterfaceLogic;
+import com.github.javaparser.symbolsolver.logic.InferenceContext;
+import com.github.javaparser.symbolsolver.model.resolution.SymbolReference;
+import com.github.javaparser.symbolsolver.model.resolution.TypeSolver;
+import com.github.javaparser.symbolsolver.model.resolution.Value;
+import com.github.javaparser.symbolsolver.model.typesystem.NullType;
+import com.github.javaparser.symbolsolver.model.typesystem.ReferenceTypeImpl;
+import com.github.javaparser.symbolsolver.reflectionmodel.MyObjectProvider;
+import com.github.javaparser.symbolsolver.reflectionmodel.ReflectionClassDeclaration;
+import com.github.javaparser.symbolsolver.resolution.SymbolSolver;
+import com.github.javaparser.symbolsolver.resolution.typeinference.TypeHelper;
+import com.github.javaparser.symbolsolver.resolution.typesolvers.ReflectionTypeSolver;
+import com.github.javaparser.utils.Log;
+import com.github.javaparser.utils.Pair;
+import com.google.common.collect.ImmutableList;
+
+import java.util.List;
+import java.util.Optional;
+
+import static com.github.javaparser.symbolsolver.javaparser.Navigator.demandParentNode;
+import static com.github.javaparser.symbolsolver.javaparsermodel.JavaParserFacade.solveGenericTypes;
+
+public class TypeExtractor extends DefaultVisitorAdapter {
+
+ private static final String JAVA_LANG_STRING = String.class.getCanonicalName();
+
+ private TypeSolver typeSolver;
+ private JavaParserFacade facade;
+
+ private ReferenceTypeImpl StringReferenceType;
+
+ public TypeExtractor(TypeSolver typeSolver, JavaParserFacade facade) {
+ this.typeSolver = typeSolver;
+ this.facade = facade;
+ //pre-calculate the String reference (optimization)
+ StringReferenceType = new ReferenceTypeImpl(new ReflectionTypeSolver().solveType(JAVA_LANG_STRING), typeSolver);
+ }
+
+ @Override
+ public ResolvedType visit(VariableDeclarator node, Boolean solveLambdas) {
+ if (demandParentNode(node) instanceof FieldDeclaration) {
+ return facade.convertToUsageVariableType(node);
+ } else if (demandParentNode(node) instanceof VariableDeclarationExpr) {
+ return facade.convertToUsageVariableType(node);
+ }
+ throw new UnsupportedOperationException(demandParentNode(node).getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(Parameter node, Boolean solveLambdas) {
+ if (node.getType() instanceof UnknownType) {
+ throw new IllegalStateException("Parameter has unknown type: " + node);
+ }
+ return facade.convertToUsage(node.getType(), node);
+ }
+
+
+ @Override
+ public ResolvedType visit(ArrayAccessExpr node, Boolean solveLambdas) {
+ ResolvedType arrayUsageType = node.getName().accept(this, solveLambdas);
+ if (arrayUsageType.isArray()) {
+ return ((ResolvedArrayType) arrayUsageType).getComponentType();
+ }
+ return arrayUsageType;
+ }
+
+ @Override
+ public ResolvedType visit(ArrayCreationExpr node, Boolean solveLambdas) {
+ ResolvedType res = facade.convertToUsage(node.getElementType(), JavaParserFactory.getContext(node, typeSolver));
+ for (int i = 0; i < node.getLevels().size(); i++) {
+ res = new ResolvedArrayType(res);
+ }
+ return res;
+ }
+
+ @Override
+ public ResolvedType visit(ArrayInitializerExpr node, Boolean solveLambdas) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(AssignExpr node, Boolean solveLambdas) {
+ return node.getTarget().accept(this, solveLambdas);
+ }
+
+ @Override
+ public ResolvedType visit(BinaryExpr node, Boolean solveLambdas) {
+ switch (node.getOperator()) {
+ case PLUS:
+ case MINUS:
+ case DIVIDE:
+ case MULTIPLY:
+ case REMAINDER:
+ case BINARY_AND:
+ case BINARY_OR:
+ case XOR:
+ return facade.getBinaryTypeConcrete(node.getLeft(), node.getRight(), solveLambdas, node.getOperator());
+ case LESS_EQUALS:
+ case LESS:
+ case GREATER:
+ case GREATER_EQUALS:
+ case EQUALS:
+ case NOT_EQUALS:
+ case OR:
+ case AND:
+ return ResolvedPrimitiveType.BOOLEAN;
+ case SIGNED_RIGHT_SHIFT:
+ case UNSIGNED_RIGHT_SHIFT:
+ case LEFT_SHIFT:
+ ResolvedType rt = node.getLeft().accept(this, solveLambdas);
+ // apply unary primitive promotion
+ return ResolvedPrimitiveType.unp(rt);
+ default:
+ throw new UnsupportedOperationException("Operator " + node.getOperator().name());
+ }
+ }
+
+ @Override
+ public ResolvedType visit(CastExpr node, Boolean solveLambdas) {
+ return facade.convertToUsage(node.getType(), JavaParserFactory.getContext(node, typeSolver));
+ }
+
+ @Override
+ public ResolvedType visit(ClassExpr node, Boolean solveLambdas) {
+ // This implementation does not regard the actual type argument of the ClassExpr.
+ Type astType = node.getType();
+ ResolvedType jssType = facade.convertToUsage(astType, node.getType());
+ return new ReferenceTypeImpl(new ReflectionClassDeclaration(Class.class, typeSolver), ImmutableList.of(jssType), typeSolver);
+ }
+
+ /*
+ * The conditional operator has three operand expressions. ? appears between the first and second expressions, and
+ * : appears between the second and third expressions.
+ * There are three kinds of conditional expressions, classified according to the second and third operand
+ * expressions: boolean conditional expressions, numeric conditional expressions, and reference conditional
+ * expressions.
+ * The classification rules are as follows:
+ * 1/ If both the second and the third operand expressions are boolean expressions, the conditional expression is a
+ * boolean conditional expression.
+ * 2/ If both the second and the third operand expressions are numeric expressions, the conditional expression is a
+ * numeric conditional expression.
+ * 3/ Otherwise, the conditional expression is a reference conditional expression
+ */
+ @Override
+ public ResolvedType visit(ConditionalExpr node, Boolean solveLambdas) {
+ ResolvedType thenExpr = node.getThenExpr().accept(this, solveLambdas);
+ ResolvedType elseExpr = node.getElseExpr().accept(this, solveLambdas);
+
+ // manage null expression
+ if ( thenExpr.isNull()) {
+ return elseExpr;
+ }
+ if ( elseExpr.isNull()) {
+ return thenExpr;
+ }
+ /*
+ * Boolean conditional expressions are standalone expressions
+ * The type of a boolean conditional expression is determined as follows:
+ * If the second and third operands are both of type Boolean, the conditional expression has type Boolean.
+ * Otherwise, the conditional expression has type boolean.
+ */
+ if ( thenExpr.isAssignableBy(ResolvedPrimitiveType.BOOLEAN)
+ && elseExpr.isAssignableBy(ResolvedPrimitiveType.BOOLEAN)) {
+ if (thenExpr.isReferenceType() && elseExpr.isReferenceType()) {
+ return thenExpr.asReferenceType();
+ }
+ return thenExpr.isPrimitive() ? thenExpr : elseExpr;
+ }
+
+ /*
+ * Numeric conditional expressions are standalone expressions (§15.2).
+ * The type of a numeric conditional expression is determined as follows:
+ * If the second and third operands have the same type, then that is the type of the conditional expression.
+ * If one of the second and third operands is of primitive type T, and the type of the other is the result of
+ * applying boxing conversion (§5.1.7) to T, then the type of the conditional expression is T.
+ * If one of the operands is of type byte or Byte and the other is of type short or Short, then the type of the
+ * conditional expression is short.
+ * If one of the operands is of type T where T is byte, short, or char, and the other operand is a constant
+ * expression (§15.28) of type int whose value is representable in type T, then the type of the conditional
+ * expression is T.
+ * If one of the operands is of type T, where T is Byte, Short, or Character, and the other operand is a
+ * constant expression of type int whose value is representable in the type U which is the result of applying
+ * unboxing conversion to T, then the type of the conditional expression is U.
+ * Otherwise, binary numeric promotion (§5.6.2) is applied to the operand types, and the type of the
+ * conditional expression is the promoted type of the second and third operands.
+ */
+ if (thenExpr.isNumericType() && elseExpr.isNumericType()) {
+ ResolvedPrimitiveType[] resolvedPrimitiveTypeSubList = new ResolvedPrimitiveType[] {ResolvedPrimitiveType.BYTE, ResolvedPrimitiveType.SHORT, ResolvedPrimitiveType.CHAR};
+ /*
+ * If the second and third operands have the same type, then that is the type of the conditional expression.
+ */
+ String qnameTypeThenExpr = thenExpr.isPrimitive() ? thenExpr.asPrimitive().describe()
+ : thenExpr.asReferenceType().describe();
+ String qnameTypeElseExpr = elseExpr.isPrimitive() ? elseExpr.asPrimitive().describe()
+ : elseExpr.asReferenceType().describe();
+ if (qnameTypeThenExpr.equals(qnameTypeElseExpr)) {
+ return thenExpr;
+ }
+ /*
+ * If one of the second and third operands is of primitive type T, and the type of the other is the result of
+ * applying boxing conversion (§5.1.7) to T, then the type of the conditional expression is T.
+ */
+ else if ((thenExpr.isPrimitive() && elseExpr.isReferenceType()
+ && isCompatible(elseExpr.asReferenceType(), thenExpr.asPrimitive()))) {
+ return thenExpr;
+ } else if ((elseExpr.isPrimitive() && thenExpr.isReferenceType()
+ && isCompatible(thenExpr.asReferenceType(), elseExpr.asPrimitive()))) {
+ return elseExpr;
+ }
+ /*
+ * If one of the operands is of type byte or Byte and the other is of type short or Short, then the type of the
+ * conditional expression is short.
+ */
+ else if ((isCompatible(thenExpr, ResolvedPrimitiveType.BYTE) && isCompatible(elseExpr, ResolvedPrimitiveType.SHORT))
+ || (isCompatible(elseExpr, ResolvedPrimitiveType.BYTE) && isCompatible(thenExpr, ResolvedPrimitiveType.SHORT))) {
+ return ResolvedPrimitiveType.SHORT;
+ }
+ /*
+ * If one of the operands is of type T where T is byte, short, or char, and the
+ * other operand is a constant expression (§15.28) of type int whose value is
+ * representable in type T, then the type of the conditional expression is T
+ * How can we know if the constant expression of type int is representable in type T ?
+ * "The constant expression of type int is representable in type T" is a runtime decision!
+ */
+ else if (thenExpr.isPrimitive() && elseExpr.isPrimitive()) {
+ if (((ResolvedPrimitiveType)thenExpr).in(resolvedPrimitiveTypeSubList)
+ && ((ResolvedPrimitiveType)elseExpr).equals(ResolvedPrimitiveType.INT)) {
+ return thenExpr;
+ } else if (((ResolvedPrimitiveType)elseExpr).in(resolvedPrimitiveTypeSubList)
+ && ((ResolvedPrimitiveType)thenExpr).equals(ResolvedPrimitiveType.INT)) {
+ return elseExpr;
+ }
+ }
+ /* If one of the operands is of type T, where T is Byte, Short, or Character,
+ * and the other operand is a constant expression of type int whose value is
+ * representable in the type U which is the result of applying unboxing
+ * conversion to T, then the type of the conditional expression is U.
+ * A priori this is a runtime decision!
+ */
+ else if (thenExpr.isReference() && elseExpr.isPrimitive()
+ && thenExpr.asReferenceType().isUnboxable()
+ && thenExpr.asReferenceType().toUnboxedType().get().in(resolvedPrimitiveTypeSubList)
+ && ((ResolvedPrimitiveType)elseExpr).equals(ResolvedPrimitiveType.INT)) {
+ return thenExpr.asReferenceType().toUnboxedType().get();
+ } else if (elseExpr.isReference() && thenExpr.isPrimitive()
+ && elseExpr.asReferenceType().isUnboxable()
+ && elseExpr.asReferenceType().toUnboxedType().get().in(resolvedPrimitiveTypeSubList)
+ && ((ResolvedPrimitiveType)thenExpr).equals(ResolvedPrimitiveType.INT)) {
+ return elseExpr.asReferenceType().toUnboxedType().get();
+ }
+
+ /* Otherwise, binary numeric promotion (§5.6.2) is applied to the operand types,
+ * and the type of the conditional expression is the promoted type of the second
+ * and third operands.
+ */
+ ResolvedPrimitiveType PrimitiveThenExpr = thenExpr.isPrimitive() ? thenExpr.asPrimitive()
+ : thenExpr.asReferenceType().toUnboxedType().get();
+ ResolvedPrimitiveType PrimitiveElseExpr = elseExpr.isPrimitive() ? elseExpr.asPrimitive()
+ : elseExpr.asReferenceType().toUnboxedType().get();
+ return PrimitiveThenExpr.bnp(PrimitiveElseExpr);
+ }
+
+ /*
+ * Otherwise, the conditional expression is a reference conditional expression.
+ * A reference conditional expression is a poly expression if it appears in an assignment context or an
+ * invocation context (§5.2. §5.3).
+ * Otherwise, it is a standalone expression.
+ * The type of a poly reference conditional expression is the same as its target type.
+ * The type of a standalone reference conditional expression is determined as follows:
+ * If the second and third operands have the same type (which may be the null type), then that is the type of
+ * the conditional expression.
+ * If the type of one of the second and third operands is the null type, and the type of the other operand is a
+ * reference type, then the type of the conditional expression is that reference type.
+ * Otherwise, the second and third operands are of types S1 and S2 respectively. Let T1 be the type that
+ * results from applying boxing conversion to S1, and let T2 be the type that results from applying boxing
+ * conversion to S2. The type of the conditional expression is the result of applying capture conversion
+ * (§5.1.10) to lub(T1, T2).
+ * TODO : must be implemented
+ */
+ if (node.isPolyExpression()) {
+ // The type of a poly reference conditional expression is the same as its target type.
+ Optional parentNode = node.getParentNode();
+ if (parentNode.isPresent()) {
+ Node parent = parentNode.get();
+ if (parent instanceof AssignExpr) {
+ return visit((AssignExpr)parent, solveLambdas);
+ } else if (parent instanceof MethodCallExpr) {
+ // how to define the target type?
+ // a priori it is the type of the parameter of the method which takes the value of the conditional expression
+ // TODO for the moment we keep the original return type
+ return thenExpr;
+ }
+ throw new RuntimeException("Cannot resolve type of poly expression "+ node.toString());
+ } else {
+ throw new RuntimeException("Parent node unexpectedly empty");
+ }
+
+ }
+
+ // The type of a standalone reference conditional expression is determined as follows:
+
+ // If the second and third operands have the same type (which may be the null type), then that is the type of
+ // the conditional expression.
+ if (thenExpr.equals(elseExpr)) {
+ return thenExpr;
+ }
+ // If the type of one of the second and third operands is the null type, and the type of the other operand is a
+ // reference type, then the type of the conditional expression is that reference type.
+ // this case is already supported above
+
+ // Otherwise, the second and third operands are of types S1 and S2 respectively. Let T1 be the type that
+ // results from applying boxing conversion to S1, and let T2 be the type that results from applying boxing
+ // conversion to S2. The type of the conditional expression is the result of applying capture conversion
+ // (§5.1.10) to lub(T1, T2).
+ ResolvedType resolvedThenType = thenExpr.isPrimitive() ? TypeHelper.toBoxedType(thenExpr.asPrimitive(), typeSolver) : thenExpr;
+ ResolvedType resolvedElseType = elseExpr.isPrimitive() ? TypeHelper.toBoxedType(elseExpr.asPrimitive(), typeSolver) : elseExpr;
+
+ // TypeHelper.leastUpperBound method is not yet implemented so for the moment we keep the original return type of this method
+ // TODO implement TypeHelper.leastUpperBound method
+ // return TypeHelper.leastUpperBound(new HashSet(Arrays.asList(resolvedThenType, resolvedElseType)));
+ return node.getThenExpr().accept(this, solveLambdas);
+ }
+
+ private boolean isCompatible(ResolvedType resolvedType, ResolvedPrimitiveType primitiveType) {
+ return (resolvedType.isPrimitive() && resolvedType.asPrimitive().equals(primitiveType))
+ || (resolvedType.isReferenceType() && resolvedType.asReferenceType().isUnboxableTo(primitiveType));
+ }
+
+ @Override
+ public ResolvedType visit(EnclosedExpr node, Boolean solveLambdas) {
+ return node.getInner().accept(this, solveLambdas);
+ }
+
+ /**
+ * Java Parser can't differentiate between packages, internal types, and fields.
+ * All three are lumped together into FieldAccessExpr. We need to differentiate them.
+ */
+ private ResolvedType solveDotExpressionType(ResolvedReferenceTypeDeclaration parentType, FieldAccessExpr node) {
+ // Fields and internal type declarations cannot have the same name.
+ // Thus, these checks will always be mutually exclusive.
+ if (parentType.isEnum() && parentType.asEnum().hasEnumConstant(node.getName().getId())) {
+ return parentType.asEnum().getEnumConstant(node.getName().getId()).getType();
+ } else if (parentType.hasField(node.getName().getId())) {
+ return parentType.getField(node.getName().getId()).getType();
+ } else if (parentType.hasInternalType(node.getName().getId())) {
+ return new ReferenceTypeImpl(parentType.getInternalType(node.getName().getId()), typeSolver);
+ } else {
+ throw new UnsolvedSymbolException(node.getName().getId());
+ }
+ }
+
+ @Override
+ public ResolvedType visit(FieldAccessExpr node, Boolean solveLambdas) {
+ // We should understand if this is a static access
+ if (node.getScope() instanceof NameExpr ||
+ node.getScope() instanceof FieldAccessExpr) {
+ Expression staticValue = node.getScope();
+ SymbolReference typeAccessedStatically = JavaParserFactory.getContext(node, typeSolver).solveType(staticValue.toString());
+ if (typeAccessedStatically.isSolved()) {
+ // TODO here maybe we have to substitute type typeParametersValues
+ return solveDotExpressionType(
+ typeAccessedStatically.getCorrespondingDeclaration().asReferenceType(), node);
+ }
+ } else if (node.getScope() instanceof ThisExpr) {
+ // If we are accessing through a 'this' expression, first resolve the type
+ // corresponding to 'this'
+ SymbolReference solve = facade.solve((ThisExpr) node.getScope());
+ // If found get it's declaration and get the field in there
+ if (solve.isSolved()) {
+ ResolvedTypeDeclaration correspondingDeclaration = solve.getCorrespondingDeclaration();
+ if (correspondingDeclaration instanceof ResolvedReferenceTypeDeclaration) {
+ return solveDotExpressionType(correspondingDeclaration.asReferenceType(), node);
+ }
+ }
+
+ } else if (node.getScope().toString().indexOf('.') > 0) {
+ // try to find fully qualified name
+ SymbolReference sr = typeSolver.tryToSolveType(node.getScope().toString());
+ if (sr.isSolved()) {
+ return solveDotExpressionType(sr.getCorrespondingDeclaration(), node);
+ }
+ }
+ Optional value = Optional.empty();
+ try {
+ value = new SymbolSolver(typeSolver).solveSymbolAsValue(node.getName().getId(), node);
+ } catch (UnsolvedSymbolException use) {
+ // This node may have a package name as part of its fully qualified name.
+ // We should solve for the type declaration inside this package.
+ SymbolReference sref = typeSolver.tryToSolveType(node.toString());
+ if (sref.isSolved()) {
+ return new ReferenceTypeImpl(sref.getCorrespondingDeclaration(), typeSolver);
+ }
+ }
+ if (value.isPresent()) {
+ return value.get().getType();
+ }
+ throw new UnsolvedSymbolException(node.getName().getId());
+ }
+
+ @Override
+ public ResolvedType visit(InstanceOfExpr node, Boolean solveLambdas) {
+ return ResolvedPrimitiveType.BOOLEAN;
+ }
+
+ @Override
+ public ResolvedType visit(StringLiteralExpr node, Boolean solveLambdas) {
+ return StringReferenceType;
+ }
+
+ @Override
+ public ResolvedType visit(IntegerLiteralExpr node, Boolean solveLambdas) {
+ return ResolvedPrimitiveType.INT;
+ }
+
+ @Override
+ public ResolvedType visit(LongLiteralExpr node, Boolean solveLambdas) {
+ return ResolvedPrimitiveType.LONG;
+ }
+
+ @Override
+ public ResolvedType visit(CharLiteralExpr node, Boolean solveLambdas) {
+ return ResolvedPrimitiveType.CHAR;
+ }
+
+ @Override
+ public ResolvedType visit(DoubleLiteralExpr node, Boolean solveLambdas) {
+ if (node.getValue().toLowerCase().endsWith("f")) {
+ return ResolvedPrimitiveType.FLOAT;
+ }
+ return ResolvedPrimitiveType.DOUBLE;
+ }
+
+ @Override
+ public ResolvedType visit(BooleanLiteralExpr node, Boolean solveLambdas) {
+ return ResolvedPrimitiveType.BOOLEAN;
+ }
+
+ @Override
+ public ResolvedType visit(NullLiteralExpr node, Boolean solveLambdas) {
+ return NullType.INSTANCE;
+ }
+
+ @Override
+ public ResolvedType visit(MethodCallExpr node, Boolean solveLambdas) {
+ Log.trace("getType on method call %s", ()-> node);
+ // first solve the method
+ MethodUsage ref = facade.solveMethodAsUsage(node);
+ Log.trace("getType on method call %s resolved to %s", ()-> node, ()-> ref);
+ Log.trace("getType on method call %s return type is %s", ()-> node, ref::returnType);
+ return ref.returnType();
+ // the type is the return type of the method
+ }
+
+ @Override
+ public ResolvedType visit(NameExpr node, Boolean solveLambdas) {
+ Log.trace("getType on name expr %s", ()-> node);
+ Optional value = new SymbolSolver(typeSolver).solveSymbolAsValue(node.getName().getId(), node);
+ if (!value.isPresent()) {
+ throw new UnsolvedSymbolException("Solving " + node, node.getName().getId());
+ } else {
+ return value.get().getType();
+ }
+ }
+
+ @Override
+ public ResolvedType visit(TypeExpr node, Boolean solveLambdas) {
+ Log.trace("getType on type expr %s", ()-> node);
+ if (!(node.getType() instanceof ClassOrInterfaceType)) {
+ throw new UnsupportedOperationException(node.getType().getClass().getCanonicalName());
+ }
+
+ ClassOrInterfaceType classOrInterfaceType = (ClassOrInterfaceType) node.getType();
+ String nameWithScope = classOrInterfaceType.getNameWithScope();
+
+ // JLS 15.13 - ReferenceType :: [TypeArguments] Identifier
+ SymbolReference typeDeclarationSymbolReference = JavaParserFactory
+ .getContext(classOrInterfaceType, typeSolver)
+ .solveType(nameWithScope);
+ if (typeDeclarationSymbolReference.isSolved()) {
+ return new ReferenceTypeImpl(typeDeclarationSymbolReference.getCorrespondingDeclaration().asReferenceType(), typeSolver);
+ }
+
+ // JLS 15.13 - ExpressionName :: [TypeArguments] Identifier
+ Optional value = new SymbolSolver(typeSolver).solveSymbolAsValue(nameWithScope, node);
+ if (value.isPresent()) {
+ return value.get().getType();
+ }
+
+ throw new UnsolvedSymbolException("Solving " + node, classOrInterfaceType.getName().getId());
+ }
+
+ @Override
+ public ResolvedType visit(ObjectCreationExpr node, Boolean solveLambdas) {
+ return facade.convertToUsage(node.getType(), node);
+ }
+
+ @Override
+ public ResolvedType visit(ThisExpr node, Boolean solveLambdas) {
+ // If 'this' is prefixed by a class eg. MyClass.this
+ if (node.getTypeName().isPresent()) {
+ // Get the class name
+ String className = node.getTypeName().get().asString();
+ // Attempt to resolve locally in Compilation unit
+ // first try a buttom/up approach
+ try {
+ return new ReferenceTypeImpl(
+ facade.getTypeDeclaration(facade.findContainingTypeDeclOrObjectCreationExpr(node, className)),
+ typeSolver);
+ } catch (IllegalStateException e) {
+ // trying another approach from type solver
+ Optional cu = node.findAncestor(CompilationUnit.class);
+ SymbolReference clazz = typeSolver.tryToSolveType(className);
+ if (clazz.isSolved()) {
+ return new ReferenceTypeImpl(clazz.getCorrespondingDeclaration(), typeSolver);
+ }
+ }
+ }
+ return new ReferenceTypeImpl(facade.getTypeDeclaration(facade.findContainingTypeDeclOrObjectCreationExpr(node)), typeSolver);
+ }
+
+ @Override
+ public ResolvedType visit(SuperExpr node, Boolean solveLambdas) {
+ // If 'super' is prefixed by a class eg. MyClass.this
+ if (node.getTypeName().isPresent()) {
+ String className = node.getTypeName().get().asString();
+ SymbolReference resolvedTypeNameRef = JavaParserFactory.getContext(node, typeSolver).solveType(className);
+ if (resolvedTypeNameRef.isSolved()) {
+ // Cfr JLS $15.12.1
+ ResolvedTypeDeclaration resolvedTypeName = resolvedTypeNameRef.getCorrespondingDeclaration();
+ if (resolvedTypeName.isInterface()) {
+ return new ReferenceTypeImpl(resolvedTypeName.asInterface(), typeSolver);
+ } else if (resolvedTypeName.isClass()) {
+ // TODO: Maybe include a presence check? e.g. in the case of `java.lang.Object` there will be no superclass.
+ return resolvedTypeName.asClass().getSuperClass().orElseThrow(() -> new RuntimeException("super class unexpectedly empty"));
+ } else {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+ } else {
+ throw new UnsolvedSymbolException(className);
+ }
+ }
+
+ ResolvedTypeDeclaration typeOfNode = facade.getTypeDeclaration(facade.findContainingTypeDeclOrObjectCreationExpr(node));
+ if (typeOfNode instanceof ResolvedClassDeclaration) {
+ // TODO: Maybe include a presence check? e.g. in the case of `java.lang.Object` there will be no superclass.
+ return ((ResolvedClassDeclaration) typeOfNode).getSuperClass().orElseThrow(() -> new RuntimeException("super class unexpectedly empty"));
+ } else {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+ }
+
+ @Override
+ public ResolvedType visit(UnaryExpr node, Boolean solveLambdas) {
+ switch (node.getOperator()) {
+ case MINUS:
+ case PLUS:
+ return ResolvedPrimitiveType.unp(node.getExpression().accept(this, solveLambdas));
+ case LOGICAL_COMPLEMENT:
+ return ResolvedPrimitiveType.BOOLEAN;
+ case POSTFIX_DECREMENT:
+ case PREFIX_DECREMENT:
+ case POSTFIX_INCREMENT:
+ case PREFIX_INCREMENT:
+ case BITWISE_COMPLEMENT:
+ return node.getExpression().accept(this, solveLambdas);
+ default:
+ throw new UnsupportedOperationException(node.getOperator().name());
+ }
+ }
+
+ @Override
+ public ResolvedType visit(VariableDeclarationExpr node, Boolean solveLambdas) {
+ if (node.getVariables().size() != 1) {
+ throw new UnsupportedOperationException();
+ }
+ return facade.convertToUsageVariableType(node.getVariables().get(0));
+ }
+
+
+ @Override
+ public ResolvedType visit(LambdaExpr node, Boolean solveLambdas) {
+ if (demandParentNode(node) instanceof MethodCallExpr) {
+ MethodCallExpr callExpr = (MethodCallExpr) demandParentNode(node);
+ int pos = JavaParserSymbolDeclaration.getParamPos(node);
+ SymbolReference refMethod = facade.solve(callExpr);
+ if (!refMethod.isSolved()) {
+ throw new UnsolvedSymbolException(demandParentNode(node).toString(), callExpr.getName().getId());
+ }
+ Log.trace("getType on lambda expr %s", ()-> refMethod.getCorrespondingDeclaration().getName());
+ if (solveLambdas) {
+
+ // The type parameter referred here should be the java.util.stream.Stream.T
+ ResolvedType result = refMethod.getCorrespondingDeclaration().getParam(pos).getType();
+
+ if (callExpr.hasScope()) {
+ Expression scope = callExpr.getScope().get();
+
+ // If it is a static call we should not try to get the type of the scope
+ boolean staticCall = false;
+ if (scope instanceof NameExpr) {
+ NameExpr nameExpr = (NameExpr) scope;
+ try {
+ SymbolReference type = JavaParserFactory.getContext(nameExpr, typeSolver).solveType(nameExpr.getName().getId());
+ if (type.isSolved()) {
+ staticCall = true;
+ }
+ } catch (Exception e) {
+
+ }
+ }
+
+ if (!staticCall) {
+ ResolvedType scopeType = facade.getType(scope);
+ if (scopeType.isReferenceType()) {
+ result = scopeType.asReferenceType().useThisTypeParametersOnTheGivenType(result);
+ }
+ }
+ }
+
+ // We need to replace the type variables
+ Context ctx = JavaParserFactory.getContext(node, typeSolver);
+ result = solveGenericTypes(result, ctx);
+
+ //We should find out which is the functional method (e.g., apply) and replace the params of the
+ //solveLambdas with it, to derive so the values. We should also consider the value returned by the
+ //lambdas
+ Optional functionalMethod = FunctionalInterfaceLogic.getFunctionalMethod(result);
+ if (functionalMethod.isPresent()) {
+ LambdaExpr lambdaExpr = node;
+
+ InferenceContext lambdaCtx = new InferenceContext(MyObjectProvider.INSTANCE);
+ InferenceContext funcInterfaceCtx = new InferenceContext(MyObjectProvider.INSTANCE);
+
+ // At this point parameterType
+ // if Function
+ // we should replace Stream.T
+ ResolvedType functionalInterfaceType = ReferenceTypeImpl.undeterminedParameters(functionalMethod.get().getDeclaration().declaringType(), typeSolver);
+
+ lambdaCtx.addPair(result, functionalInterfaceType);
+
+ ResolvedType actualType;
+
+ if (lambdaExpr.getBody() instanceof ExpressionStmt) {
+ actualType = facade.getType(((ExpressionStmt) lambdaExpr.getBody()).getExpression());
+ } else if (lambdaExpr.getBody() instanceof BlockStmt) {
+ BlockStmt blockStmt = (BlockStmt) lambdaExpr.getBody();
+
+ // Get all the return statements in the lambda block
+ List returnStmts = blockStmt.findAll(ReturnStmt.class);
+
+ if (returnStmts.size() > 0) {
+ actualType = returnStmts.stream()
+ .map(returnStmt -> returnStmt.getExpression().map(e -> facade.getType(e)).orElse(ResolvedVoidType.INSTANCE))
+ .filter(x -> x != null && !x.isVoid() && !x.isNull())
+ .findFirst()
+ .orElse(ResolvedVoidType.INSTANCE);
+
+ } else {
+ actualType = ResolvedVoidType.INSTANCE;
+ }
+
+
+ } else {
+ throw new UnsupportedOperationException();
+ }
+
+ ResolvedType formalType = functionalMethod.get().returnType();
+
+ // Infer the functional interfaces' return vs actual type
+ funcInterfaceCtx.addPair(formalType, actualType);
+ // Substitute to obtain a new type
+ ResolvedType functionalTypeWithReturn = funcInterfaceCtx.resolve(funcInterfaceCtx.addSingle(functionalInterfaceType));
+
+ // if the functional method returns void anyway
+ // we don't need to bother inferring types
+ if (!(formalType instanceof ResolvedVoidType)) {
+ lambdaCtx.addPair(result, functionalTypeWithReturn);
+ result = lambdaCtx.resolve(lambdaCtx.addSingle(result));
+ }
+ }
+
+ return result;
+ } else {
+ return refMethod.getCorrespondingDeclaration().getParam(pos).getType();
+ }
+ } else {
+ throw new UnsupportedOperationException("The type of a lambda expr depends on the position and its return value");
+ }
+ }
+
+ @Override
+ public ResolvedType visit(MethodReferenceExpr node, Boolean solveLambdas) {
+ if (demandParentNode(node) instanceof MethodCallExpr) {
+ MethodCallExpr callExpr = (MethodCallExpr) demandParentNode(node);
+ int pos = JavaParserSymbolDeclaration.getParamPos(node);
+ SymbolReference refMethod = facade.solve(callExpr, false);
+ if (!refMethod.isSolved()) {
+ throw new UnsolvedSymbolException(demandParentNode(node).toString(), callExpr.getName().getId());
+ }
+ Log.trace("getType on method reference expr %s", ()-> refMethod.getCorrespondingDeclaration().getName());
+ if (solveLambdas) {
+ MethodUsage usage = facade.solveMethodAsUsage(callExpr);
+ ResolvedType result = usage.getParamType(pos);
+ // We need to replace the type variables
+ Context ctx = JavaParserFactory.getContext(node, typeSolver);
+ result = solveGenericTypes(result, ctx);
+
+ //We should find out which is the functional method (e.g., apply) and replace the params of the
+ //solveLambdas with it, to derive so the values. We should also consider the value returned by the
+ //lambdas
+ Optional functionalMethodOpt = FunctionalInterfaceLogic.getFunctionalMethod(result);
+ if (functionalMethodOpt.isPresent()) {
+ MethodUsage functionalMethod = functionalMethodOpt.get();
+
+ for (Pair typeParamDecl : result.asReferenceType().getTypeParametersMap()) {
+ functionalMethod = functionalMethod.replaceTypeParameter(typeParamDecl.a, typeParamDecl.b);
+ }
+
+ // replace wildcards
+ for (int i = 0; i < functionalMethod.getNoParams(); i++) {
+ ResolvedType type = functionalMethod.getParamType(i);
+ if (type.isWildcard()) {
+ ResolvedType boundedType = type.asWildcard().getBoundedType();
+ functionalMethod = functionalMethod.replaceParamType(i, boundedType);
+ }
+ }
+
+ ResolvedType actualType = facade.toMethodUsage(node, functionalMethod.getParamTypes()).returnType();
+ ResolvedType formalType = functionalMethod.returnType();
+
+ InferenceContext inferenceContext = new InferenceContext(MyObjectProvider.INSTANCE);
+ inferenceContext.addPair(formalType, actualType);
+ result = inferenceContext.resolve(inferenceContext.addSingle(result));
+ }
+
+ return result;
+ }
+ return refMethod.getCorrespondingDeclaration().getParam(pos).getType();
+ }
+ throw new UnsupportedOperationException("The type of a method reference expr depends on the position and its return value");
+ }
+
+ @Override
+ public ResolvedType visit(FieldDeclaration node, Boolean solveLambdas) {
+ if (node.getVariables().size() == 1) {
+ return node.getVariables().get(0).accept(this, solveLambdas);
+ }
+ throw new IllegalArgumentException("Cannot resolve the type of a field with multiple variable declarations. Pick one");
+ }
+}
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/contexts/AbstractJavaParserContext.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/contexts/AbstractJavaParserContext.java
new file mode 100644
index 0000000000000000000000000000000000000000..b71ea5c13f5cc501fba763baaa378d99247ebb1f
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/contexts/AbstractJavaParserContext.java
@@ -0,0 +1,281 @@
+/*
+ * Copyright (C) 2015-2016 Federico Tomassetti
+ * Copyright (C) 2017-2020 The JavaParser Team.
+ *
+ * This file is part of JavaParser.
+ *
+ * JavaParser can be used either under the terms of
+ * a) the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * b) the terms of the Apache License
+ *
+ * You should have received a copy of both licenses in LICENCE.LGPL and
+ * LICENCE.APACHE. Please refer to those files for details.
+ *
+ * JavaParser is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ */
+
+package com.github.javaparser.symbolsolver.javaparsermodel.contexts;
+
+import com.github.javaparser.ast.Node;
+import com.github.javaparser.ast.expr.*;
+import com.github.javaparser.ast.nodeTypes.NodeWithOptionalScope;
+import com.github.javaparser.resolution.UnsolvedSymbolException;
+import com.github.javaparser.resolution.declarations.ResolvedReferenceTypeDeclaration;
+import com.github.javaparser.resolution.declarations.ResolvedTypeDeclaration;
+import com.github.javaparser.resolution.declarations.ResolvedTypeParameterDeclaration;
+import com.github.javaparser.resolution.declarations.ResolvedValueDeclaration;
+import com.github.javaparser.resolution.types.ResolvedReferenceType;
+import com.github.javaparser.resolution.types.ResolvedType;
+import com.github.javaparser.symbolsolver.core.resolution.Context;
+import com.github.javaparser.symbolsolver.javaparsermodel.JavaParserFacade;
+import com.github.javaparser.symbolsolver.javaparsermodel.JavaParserFactory;
+import com.github.javaparser.symbolsolver.javaparsermodel.declarations.JavaParserPatternDeclaration;
+import com.github.javaparser.symbolsolver.javaparsermodel.declarations.JavaParserSymbolDeclaration;
+import com.github.javaparser.symbolsolver.model.resolution.SymbolReference;
+import com.github.javaparser.symbolsolver.model.resolution.TypeSolver;
+import com.github.javaparser.symbolsolver.model.resolution.Value;
+import com.github.javaparser.symbolsolver.reflectionmodel.ReflectionClassDeclaration;
+import com.github.javaparser.symbolsolver.resolution.SymbolDeclarator;
+
+import java.util.*;
+
+import static com.github.javaparser.symbolsolver.javaparser.Navigator.demandParentNode;
+import static java.util.Collections.singletonList;
+
+/**
+ * @author Federico Tomassetti
+ */
+public abstract class AbstractJavaParserContext implements Context {
+
+ protected N wrappedNode;
+ protected TypeSolver typeSolver;
+
+ ///
+ /// Static methods
+ ///
+
+ protected static boolean isQualifiedName(String name) {
+ return name.contains(".");
+ }
+
+ public static SymbolReference solveWith(SymbolDeclarator symbolDeclarator, String name) {
+ for (ResolvedValueDeclaration decl : symbolDeclarator.getSymbolDeclarations()) {
+ if (decl.getName().equals(name)) {
+ return SymbolReference.solved(decl);
+ }
+ }
+ return SymbolReference.unsolved(ResolvedValueDeclaration.class);
+ }
+
+ ///
+ /// Constructors
+ ///
+
+ public AbstractJavaParserContext(N wrappedNode, TypeSolver typeSolver) {
+ if (wrappedNode == null) {
+ throw new NullPointerException();
+ }
+ this.wrappedNode = wrappedNode;
+ this.typeSolver = typeSolver;
+ }
+
+ ///
+ /// Public methods
+ ///
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ AbstractJavaParserContext> that = (AbstractJavaParserContext>) o;
+
+ return wrappedNode != null ? wrappedNode.equals(that.wrappedNode) : that.wrappedNode == null;
+ }
+
+ @Override
+ public int hashCode() {
+ return wrappedNode == null ? 0 : wrappedNode.hashCode();
+ }
+
+ @Override
+ public final Optional getParent() {
+ Node parentNode = wrappedNode.getParentNode().orElse(null);
+
+ // TODO/FiXME: Document why the method call expression is treated differently.
+ if (parentNode instanceof MethodCallExpr) {
+ MethodCallExpr parentCall = (MethodCallExpr) parentNode;
+ // TODO: Can this be replaced with: boolean found = parentCall.getArguments().contains(wrappedNode);
+ boolean found = false;
+ for (Expression expression : parentCall.getArguments()) {
+ if (expression == wrappedNode) {
+ found = true;
+ break;
+ }
+ }
+ if (found) {
+ Node notMethod = parentNode;
+ while (notMethod instanceof MethodCallExpr) {
+ notMethod = demandParentNode(notMethod);
+ }
+ return Optional.of(JavaParserFactory.getContext(notMethod, typeSolver));
+ }
+ }
+ Node notMethodNode = parentNode;
+ // to avoid an infinite loop if parent scope is the same as wrapped node
+ while (notMethodNode instanceof MethodCallExpr || notMethodNode instanceof FieldAccessExpr
+ || (notMethodNode != null && notMethodNode.hasScope() && getScope(notMethodNode).equals(wrappedNode)) ) {
+ notMethodNode = notMethodNode.getParentNode().orElse(null);
+ }
+ if (notMethodNode == null) {
+ return Optional.empty();
+ }
+ Context parentContext = JavaParserFactory.getContext(notMethodNode, typeSolver);
+ return Optional.of(parentContext);
+ }
+
+ // before to call this method verify the node has a scope
+ protected Node getScope(Node node) {
+ return (Node) ((NodeWithOptionalScope)node).getScope().get();
+ }
+
+
+ @Override
+ public SymbolReference extends ResolvedValueDeclaration> solveSymbolInParentContext(String name) {
+ Optional optionalParentContext = getParent();
+ if (!optionalParentContext.isPresent()) {
+ return SymbolReference.unsolved(ResolvedValueDeclaration.class);
+ }
+
+ // First check if there are any pattern expressions available to this node.
+ Context parentContext = optionalParentContext.get();
+ if(parentContext instanceof BinaryExprContext || parentContext instanceof IfStatementContext) {
+ List patternExprs = parentContext.patternExprsExposedToChild(this.getWrappedNode());
+
+ Optional localResolutionResults = patternExprs
+ .stream()
+ .filter(vd -> vd.getNameAsString().equals(name))
+ .findFirst();
+
+ if (localResolutionResults.isPresent()) {
+ if(patternExprs.size() == 1) {
+ JavaParserPatternDeclaration decl = JavaParserSymbolDeclaration.patternVar(localResolutionResults.get(), typeSolver);
+ return SymbolReference.solved(decl);
+ } else if(patternExprs.size() > 1) {
+ throw new IllegalStateException("Unexpectedly more than one reference in scope");
+ }
+ }
+ }
+
+ // Delegate solving to the parent context.
+ return parentContext.solveSymbol(name);
+ }
+
+ ///
+ /// Protected methods
+ ///
+
+ protected Optional solveWithAsValue(SymbolDeclarator symbolDeclarator, String name) {
+ return symbolDeclarator.getSymbolDeclarations().stream()
+ .filter(d -> d.getName().equals(name))
+ .map(Value::from)
+ .findFirst();
+ }
+
+ protected Collection findTypeDeclarations(Optional optScope) {
+ if (optScope.isPresent()) {
+ Expression scope = optScope.get();
+
+ // consider static methods
+ if (scope instanceof NameExpr) {
+ NameExpr scopeAsName = scope.asNameExpr();
+ SymbolReference symbolReference = this.solveType(scopeAsName.getName().getId());
+ if (symbolReference.isSolved() && symbolReference.getCorrespondingDeclaration().isType()) {
+ return singletonList(symbolReference.getCorrespondingDeclaration().asReferenceType());
+ }
+ }
+
+ ResolvedType typeOfScope;
+ try {
+ typeOfScope = JavaParserFacade.get(typeSolver).getType(scope);
+ } catch (Exception e) {
+ // If the scope corresponds to a type we should treat it differently
+ if (scope instanceof FieldAccessExpr) {
+ FieldAccessExpr scopeName = (FieldAccessExpr) scope;
+ if (this.solveType(scopeName.toString()).isSolved()) {
+ return Collections.emptyList();
+ }
+ }
+ throw new UnsolvedSymbolException(scope.toString(), wrappedNode.toString(), e);
+ }
+ if (typeOfScope.isWildcard()) {
+ if (typeOfScope.asWildcard().isExtends() || typeOfScope.asWildcard().isSuper()) {
+ // TODO: Figure out if it is appropriate to remove the orElseThrow() -- if so, how...
+ return singletonList(
+ typeOfScope.asWildcard()
+ .getBoundedType()
+ .asReferenceType()
+ .getTypeDeclaration()
+ .orElseThrow(() -> new RuntimeException("TypeDeclaration unexpectedly empty."))
+ );
+ } else {
+ return singletonList(new ReflectionClassDeclaration(Object.class, typeSolver).asReferenceType());
+ }
+ } else if (typeOfScope.isArray()) {
+ // method call on array are Object methods
+ return singletonList(new ReflectionClassDeclaration(Object.class, typeSolver).asReferenceType());
+ } else if (typeOfScope.isTypeVariable()) {
+ Collection result = new ArrayList<>();
+ for (ResolvedTypeParameterDeclaration.Bound bound : typeOfScope.asTypeParameter().getBounds()) {
+ // TODO: Figure out if it is appropriate to remove the orElseThrow() -- if so, how...
+ result.add(
+ bound.getType()
+ .asReferenceType()
+ .getTypeDeclaration()
+ .orElseThrow(() -> new RuntimeException("TypeDeclaration unexpectedly empty."))
+ );
+ }
+ return result;
+ } else if (typeOfScope.isConstraint()) {
+ // TODO: Figure out if it is appropriate to remove the orElseThrow() -- if so, how...
+ return singletonList(
+ typeOfScope.asConstraintType()
+ .getBound()
+ .asReferenceType()
+ .getTypeDeclaration()
+ .orElseThrow(() -> new RuntimeException("TypeDeclaration unexpectedly empty."))
+ );
+ } else if (typeOfScope.isUnionType()) {
+ return typeOfScope.asUnionType().getCommonAncestor()
+ .flatMap(ResolvedReferenceType::getTypeDeclaration)
+ .map(Collections::singletonList)
+ .orElseThrow(() -> new UnsolvedSymbolException("No common ancestor available for UnionType" + typeOfScope.describe()));
+ }
+
+ // TODO: Figure out if it is appropriate to remove the orElseThrow() -- if so, how...
+ return singletonList(
+ typeOfScope.asReferenceType()
+ .getTypeDeclaration()
+ .orElseThrow(() -> new RuntimeException("TypeDeclaration unexpectedly empty."))
+ );
+ }
+
+ ResolvedType typeOfScope = JavaParserFacade.get(typeSolver).getTypeOfThisIn(wrappedNode);
+
+ // TODO: Figure out if it is appropriate to remove the orElseThrow() -- if so, how...
+ return singletonList(
+ typeOfScope.asReferenceType()
+ .getTypeDeclaration()
+ .orElseThrow(() -> new RuntimeException("TypeDeclaration unexpectedly empty."))
+ );
+ }
+
+ public N getWrappedNode() {
+ return wrappedNode;
+ }
+}
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/contexts/AbstractMethodLikeDeclarationContext.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/contexts/AbstractMethodLikeDeclarationContext.java
new file mode 100644
index 0000000000000000000000000000000000000000..6a3b8af92f3525f8603bb25fccae92a6631a21ce
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/contexts/AbstractMethodLikeDeclarationContext.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2015-2016 Federico Tomassetti
+ * Copyright (C) 2017-2020 The JavaParser Team.
+ *
+ * This file is part of JavaParser.
+ *
+ * JavaParser can be used either under the terms of
+ * a) the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * b) the terms of the Apache License
+ *
+ * You should have received a copy of both licenses in LICENCE.LGPL and
+ * LICENCE.APACHE. Please refer to those files for details.
+ *
+ * JavaParser is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ */
+
+package com.github.javaparser.symbolsolver.javaparsermodel.contexts;
+
+import com.github.javaparser.ast.Node;
+import com.github.javaparser.ast.body.Parameter;
+import com.github.javaparser.ast.body.TypeDeclaration;
+import com.github.javaparser.ast.nodeTypes.NodeWithParameters;
+import com.github.javaparser.ast.nodeTypes.NodeWithTypeParameters;
+import com.github.javaparser.ast.type.TypeParameter;
+import com.github.javaparser.resolution.declarations.ResolvedMethodDeclaration;
+import com.github.javaparser.resolution.declarations.ResolvedTypeDeclaration;
+import com.github.javaparser.resolution.declarations.ResolvedValueDeclaration;
+import com.github.javaparser.resolution.types.ResolvedType;
+import com.github.javaparser.resolution.types.ResolvedTypeVariable;
+import com.github.javaparser.symbolsolver.javaparsermodel.JavaParserFacade;
+import com.github.javaparser.symbolsolver.javaparsermodel.JavaParserFactory;
+import com.github.javaparser.symbolsolver.javaparsermodel.declarations.JavaParserTypeParameter;
+import com.github.javaparser.symbolsolver.model.resolution.SymbolReference;
+import com.github.javaparser.symbolsolver.model.resolution.TypeSolver;
+import com.github.javaparser.symbolsolver.model.resolution.Value;
+import com.github.javaparser.symbolsolver.resolution.SymbolDeclarator;
+
+import java.util.List;
+import java.util.Optional;
+
+/**
+ * @author Federico Tomassetti
+ */
+public abstract class AbstractMethodLikeDeclarationContext
+ & NodeWithTypeParameters> extends AbstractJavaParserContext {
+
+ public AbstractMethodLikeDeclarationContext(T wrappedNode, TypeSolver typeSolver) {
+ super(wrappedNode, typeSolver);
+ }
+
+ @Override
+ public final SymbolReference extends ResolvedValueDeclaration> solveSymbol(String name) {
+ for (Parameter parameter : wrappedNode.getParameters()) {
+ SymbolDeclarator sb = JavaParserFactory.getSymbolDeclarator(parameter, typeSolver);
+ SymbolReference extends ResolvedValueDeclaration> symbolReference = AbstractJavaParserContext.solveWith(sb, name);
+ if (symbolReference.isSolved()) {
+ return symbolReference;
+ }
+ }
+
+ // if nothing is found we should ask the parent context
+ return solveSymbolInParentContext(name);
+ }
+
+ @Override
+ public final Optional solveGenericType(String name) {
+ // First check if the method-like declaration has type parameters defined.
+ // For example: {@code public boolean containsAll(Collection c);}
+ for (TypeParameter tp : wrappedNode.getTypeParameters()) {
+ if (tp.getName().getId().equals(name)) {
+ return Optional.of(new ResolvedTypeVariable(new JavaParserTypeParameter(tp, typeSolver)));
+ }
+ }
+
+ // If no generic types on the method declaration, continue to solve elsewhere as usual.
+ return solveGenericTypeInParentContext(name);
+ }
+
+ @Override
+ public final Optional solveSymbolAsValue(String name) {
+ for (Parameter parameter : wrappedNode.getParameters()) {
+ SymbolDeclarator sb = JavaParserFactory.getSymbolDeclarator(parameter, typeSolver);
+ Optional symbolReference = solveWithAsValue(sb, name);
+ if (symbolReference.isPresent()) {
+ // Perform parameter type substitution as needed
+ return symbolReference;
+ }
+ }
+
+ // if nothing is found we should ask the parent context
+ return solveSymbolAsValueInParentContext(name);
+ }
+
+ @Override
+ public final SymbolReference solveType(String name) {
+ // TODO: Is null check required?
+ if (wrappedNode.getTypeParameters() != null) {
+ for (TypeParameter tp : wrappedNode.getTypeParameters()) {
+ if (tp.getName().getId().equals(name)) {
+ return SymbolReference.solved(new JavaParserTypeParameter(tp, typeSolver));
+ }
+ }
+ }
+
+ // Local types
+ List localTypes = wrappedNode.findAll(TypeDeclaration.class);
+ for (TypeDeclaration> localType : localTypes) {
+ if (localType.getName().getId().equals(name)) {
+ return SymbolReference.solved(JavaParserFacade.get(typeSolver)
+ .getTypeDeclaration(localType));
+ } else if (name.startsWith(String.format("%s.", localType.getName()))) {
+ return JavaParserFactory.getContext(localType, typeSolver)
+ .solveType(name.substring(localType.getName().getId().length() + 1));
+ }
+ }
+
+ return solveTypeInParentContext(name);
+ }
+
+ @Override
+ public final SymbolReference solveMethod(String name, List argumentsTypes, boolean staticOnly) {
+ // TODO: Document why staticOnly is forced to be false.
+ return solveMethodInParentContext(name, argumentsTypes, false);
+ }
+}
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/contexts/AnnotationDeclarationContext.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/contexts/AnnotationDeclarationContext.java
new file mode 100644
index 0000000000000000000000000000000000000000..d189976790d0a6da121aadbf9999f113fb9c6c8e
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/contexts/AnnotationDeclarationContext.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2015-2016 Federico Tomassetti
+ * Copyright (C) 2017-2020 The JavaParser Team.
+ *
+ * This file is part of JavaParser.
+ *
+ * JavaParser can be used either under the terms of
+ * a) the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * b) the terms of the Apache License
+ *
+ * You should have received a copy of both licenses in LICENCE.LGPL and
+ * LICENCE.APACHE. Please refer to those files for details.
+ *
+ * JavaParser is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ */
+
+package com.github.javaparser.symbolsolver.javaparsermodel.contexts;
+
+import com.github.javaparser.ast.body.AnnotationDeclaration;
+import com.github.javaparser.resolution.declarations.ResolvedMethodDeclaration;
+import com.github.javaparser.resolution.declarations.ResolvedReferenceTypeDeclaration;
+import com.github.javaparser.resolution.declarations.ResolvedTypeDeclaration;
+import com.github.javaparser.resolution.declarations.ResolvedValueDeclaration;
+import com.github.javaparser.resolution.types.ResolvedType;
+import com.github.javaparser.symbolsolver.javaparsermodel.declarations.JavaParserAnnotationDeclaration;
+import com.github.javaparser.symbolsolver.model.resolution.SymbolReference;
+import com.github.javaparser.symbolsolver.model.resolution.TypeSolver;
+
+import java.util.List;
+
+/**
+ * @author Takeshi D. Itoh
+ */
+public class AnnotationDeclarationContext extends AbstractJavaParserContext {
+
+ private JavaParserTypeDeclarationAdapter javaParserTypeDeclarationAdapter;
+
+ public AnnotationDeclarationContext(AnnotationDeclaration wrappedNode, TypeSolver typeSolver) {
+ super(wrappedNode, typeSolver);
+ this.javaParserTypeDeclarationAdapter = new JavaParserTypeDeclarationAdapter(wrappedNode, typeSolver,
+ getDeclaration(), this);
+ }
+
+ @Override
+ public SymbolReference extends ResolvedValueDeclaration> solveSymbol(String name) {
+ if (typeSolver == null) throw new IllegalArgumentException();
+
+ if (this.getDeclaration().hasField(name)) {
+ return SymbolReference.solved(this.getDeclaration().getField(name));
+ }
+
+ // then to parent
+ return solveSymbolInParentContext(name);
+ }
+
+ @Override
+ public SymbolReference solveType(String name) {
+ return javaParserTypeDeclarationAdapter.solveType(name);
+ }
+
+ @Override
+ public SymbolReference solveMethod(String name, List argumentsTypes, boolean staticOnly) {
+ return javaParserTypeDeclarationAdapter.solveMethod(name, argumentsTypes, staticOnly);
+ }
+
+ ///
+ /// Private methods
+ ///
+
+ private ResolvedReferenceTypeDeclaration getDeclaration() {
+ return new JavaParserAnnotationDeclaration(this.wrappedNode, typeSolver);
+ }
+}
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/contexts/AnonymousClassDeclarationContext.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/contexts/AnonymousClassDeclarationContext.java
new file mode 100644
index 0000000000000000000000000000000000000000..ca4ea87c768201302ad6c0e964b95115adf23868
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/contexts/AnonymousClassDeclarationContext.java
@@ -0,0 +1,210 @@
+/*
+ * Copyright (C) 2015-2016 Federico Tomassetti
+ * Copyright (C) 2017-2020 The JavaParser Team.
+ *
+ * This file is part of JavaParser.
+ *
+ * JavaParser can be used either under the terms of
+ * a) the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * b) the terms of the Apache License
+ *
+ * You should have received a copy of both licenses in LICENCE.LGPL and
+ * LICENCE.APACHE. Please refer to those files for details.
+ *
+ * JavaParser is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ */
+
+package com.github.javaparser.symbolsolver.javaparsermodel.contexts;
+
+import com.github.javaparser.ast.NodeList;
+import com.github.javaparser.ast.body.TypeDeclaration;
+import com.github.javaparser.ast.expr.ObjectCreationExpr;
+import com.github.javaparser.ast.nodeTypes.NodeWithTypeArguments;
+import com.github.javaparser.ast.type.TypeParameter;
+import com.github.javaparser.resolution.declarations.ResolvedMethodDeclaration;
+import com.github.javaparser.resolution.declarations.ResolvedReferenceTypeDeclaration;
+import com.github.javaparser.resolution.declarations.ResolvedTypeDeclaration;
+import com.github.javaparser.resolution.declarations.ResolvedValueDeclaration;
+import com.github.javaparser.resolution.types.ResolvedReferenceType;
+import com.github.javaparser.resolution.types.ResolvedType;
+import com.github.javaparser.symbolsolver.javaparsermodel.JavaParserFacade;
+import com.github.javaparser.symbolsolver.javaparsermodel.JavaParserFactory;
+import com.github.javaparser.symbolsolver.javaparsermodel.declarations.JavaParserAnonymousClassDeclaration;
+import com.github.javaparser.symbolsolver.javaparsermodel.declarations.JavaParserTypeParameter;
+import com.github.javaparser.symbolsolver.model.resolution.SymbolReference;
+import com.github.javaparser.symbolsolver.model.resolution.TypeSolver;
+import com.github.javaparser.symbolsolver.reflectionmodel.ReflectionClassDeclaration;
+import com.github.javaparser.symbolsolver.resolution.MethodResolutionLogic;
+import com.google.common.base.Preconditions;
+
+import java.util.List;
+import java.util.Optional;
+import java.util.stream.Collectors;
+
+/**
+ * A symbol resolution context for an object creation node.
+ */
+public class AnonymousClassDeclarationContext extends AbstractJavaParserContext {
+
+ private final JavaParserAnonymousClassDeclaration myDeclaration =
+ new JavaParserAnonymousClassDeclaration(wrappedNode, typeSolver);
+
+ public AnonymousClassDeclarationContext(ObjectCreationExpr node, TypeSolver typeSolver) {
+ super(node, typeSolver);
+ Preconditions.checkArgument(node.getAnonymousClassBody().isPresent(),
+ "An anonymous class must have a body");
+ }
+
+ @Override
+ public SymbolReference solveMethod(String name,
+ List argumentsTypes,
+ boolean staticOnly) {
+ List candidateMethods = myDeclaration
+ .getDeclaredMethods()
+ .stream()
+ .filter(m -> m.getName().equals(name) && (!staticOnly || m.isStatic()))
+ .collect(Collectors.toList());
+
+ if (!myDeclaration.isJavaLangObject()) {
+ for (ResolvedReferenceType ancestor : myDeclaration.getAncestors()) {
+ ancestor.getTypeDeclaration().ifPresent(ancestorTypeDeclaration -> {
+ SymbolReference res = MethodResolutionLogic.solveMethodInType(
+ ancestorTypeDeclaration,
+ name,
+ argumentsTypes,
+ staticOnly
+ );
+
+ // consider methods from superclasses and only default methods from interfaces :
+ // not true, we should keep abstract as a valid candidate
+ // abstract are removed in MethodResolutionLogic.isApplicable is necessary
+ if (res.isSolved()) {
+ candidateMethods.add(res.getCorrespondingDeclaration());
+ }
+ });
+ }
+ }
+
+ // We want to avoid infinite recursion when a class is using its own method
+ // see issue #75
+ if (candidateMethods.isEmpty()) {
+ SymbolReference parentSolution =
+ getParent()
+ .orElseThrow(() -> new RuntimeException("Parent context unexpectedly empty."))
+ .solveMethod(name, argumentsTypes, staticOnly);
+ if (parentSolution.isSolved()) {
+ candidateMethods.add(parentSolution.getCorrespondingDeclaration());
+ }
+ }
+
+ // if is interface and candidate method list is empty, we should check the Object Methods
+ if (candidateMethods.isEmpty() && myDeclaration.getSuperTypeDeclaration().isInterface()) {
+ SymbolReference res =
+ MethodResolutionLogic.solveMethodInType(new ReflectionClassDeclaration(Object.class,
+ typeSolver),
+ name,
+ argumentsTypes,
+ false);
+ if (res.isSolved()) {
+ candidateMethods.add(res.getCorrespondingDeclaration());
+ }
+ }
+
+ return MethodResolutionLogic.findMostApplicable(candidateMethods,
+ name,
+ argumentsTypes,
+ typeSolver);
+ }
+
+ @Override
+ public SymbolReference solveType(String name) {
+ List typeDeclarations = myDeclaration.findMembersOfKind(TypeDeclaration.class);
+
+ Optional> exactMatch =
+ typeDeclarations
+ .stream()
+ .filter(internalType -> internalType.getName().getId().equals(name))
+ .findFirst()
+ .map(internalType ->
+ SymbolReference.solved(
+ JavaParserFacade.get(typeSolver).getTypeDeclaration(internalType)));
+
+ if(exactMatch.isPresent()){
+ return exactMatch.get();
+ }
+
+ Optional> recursiveMatch =
+ typeDeclarations
+ .stream()
+ .filter(internalType -> name.startsWith(String.format("%s.", internalType.getName())))
+ .findFirst()
+ .map(internalType ->
+ JavaParserFactory
+ .getContext(internalType, typeSolver)
+ .solveType(name.substring(internalType.getName().getId().length() + 1)));
+
+ if (recursiveMatch.isPresent()) {
+ return recursiveMatch.get();
+ }
+
+ Optional> typeArgumentsMatch =
+ wrappedNode
+ .getTypeArguments()
+ .map(nodes ->
+ ((NodeWithTypeArguments>) nodes).getTypeArguments()
+ .orElse(new NodeList<>()))
+ .orElse(new NodeList<>())
+ .stream()
+ .filter(type -> type.toString().equals(name))
+ .findFirst()
+ .map(matchingType ->
+ SymbolReference.solved(
+ new JavaParserTypeParameter(new TypeParameter(matchingType.toString()),
+ typeSolver)));
+
+ if (typeArgumentsMatch.isPresent()) {
+ return typeArgumentsMatch.get();
+ }
+
+ // Look into extended classes and implemented interfaces
+ for (ResolvedReferenceType ancestor : myDeclaration.getAncestors()) {
+ // look at names of extended classes and implemented interfaces (this may not be important because they are checked in CompilationUnitContext)
+ Optional optionalTypeDeclaration = ancestor.getTypeDeclaration();
+ if (optionalTypeDeclaration.isPresent()) {
+ ResolvedReferenceTypeDeclaration typeDeclaration = optionalTypeDeclaration.get();
+ if (typeDeclaration.getName().equals(name)) {
+ return SymbolReference.solved(typeDeclaration);
+ }
+ // look into internal types of extended classes and implemented interfaces
+ try {
+ for (ResolvedTypeDeclaration internalTypeDeclaration : typeDeclaration.internalTypes()) {
+ if (internalTypeDeclaration.getName().equals(name)) {
+ return SymbolReference.solved(internalTypeDeclaration);
+ }
+ }
+ } catch (UnsupportedOperationException e) {
+ // just continue using the next ancestor
+ }
+ }
+ }
+
+ return solveTypeInParentContext(name);
+ }
+
+ @Override
+ public SymbolReference extends ResolvedValueDeclaration> solveSymbol(String name) {
+ Preconditions.checkArgument(typeSolver != null);
+
+ if (myDeclaration.hasField(name)) {
+ return SymbolReference.solved(myDeclaration.getField(name));
+ }
+
+ return solveSymbolInParentContext(name);
+ }
+
+}
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/contexts/ArrayAccessExprContext.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/contexts/ArrayAccessExprContext.java
new file mode 100644
index 0000000000000000000000000000000000000000..400b89ed4d648c7fac99b2fc2c35c51aab9fbf29
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/contexts/ArrayAccessExprContext.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2015-2016 Federico Tomassetti
+ * Copyright (C) 2017-2020 The JavaParser Team.
+ *
+ * This file is part of JavaParser.
+ *
+ * JavaParser can be used either under the terms of
+ * a) the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * b) the terms of the Apache License
+ *
+ * You should have received a copy of both licenses in LICENCE.LGPL and
+ * LICENCE.APACHE. Please refer to those files for details.
+ *
+ * JavaParser is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ */
+
+package com.github.javaparser.symbolsolver.javaparsermodel.contexts;
+
+import com.github.javaparser.ast.expr.ArrayAccessExpr;
+import com.github.javaparser.resolution.declarations.ResolvedValueDeclaration;
+import com.github.javaparser.symbolsolver.model.resolution.SymbolReference;
+import com.github.javaparser.symbolsolver.model.resolution.TypeSolver;
+
+/**
+ *
+ * Required to prevent recursive access to the "parent node" (not necessarily the same as the "parent context").
+ *
+ * Consider, for example, this code where the cursor is currently at the node of type {@code ArrayAccessExpr}:
+ *
+ * {@code
+ * var1.perPriority[index].recovered
+ * ^^^^^^^^^^^^^^^^^^^^^^^ - ArrayAccessExpr
+ * }
+ *
+ * The AST for this snippet:
+ *
+ * {@code
+ * FieldAccessExpr // This FieldAccessExpr is accessing the field `recovered`
+ * / \
+ * **ArrayAccessExpr** SimpleName(recovered)
+ * / \
+ * FieldAccessExpr NameExpr(index) // This FieldAccessExpr is accessing the field `perPriority`
+ * / \
+ * NameExpr(var1) SimpleName (perPriority)
+ * }
+ *
+ * In this example:
+ *
+ * -
+ * The parent node for {@code ArrayAccessExpr} is {@code FieldAccessExpr} ({@code variable1.perPriority[index].recovered}).
+ *
{@code
+ * // "Parent Node" of the ArrayAccessExpr
+ * var.perPriority[index].recovered
+ * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - FieldAccessExpr
+ * ^^^^^^^^^^^^^^^^^^^^^^ - ArrayAccessExpr
+ * ^^^^^^^^^ - SimpleName
+ * }
+ *
+ * -
+ * The parent context is the {@code FieldAccessExpr} to the left of the outer array-access, which is actually a child node.
+ *
{@code
+ *
+ * // "Parent Context" of the ArrayAccessExpr
+ * var1.perPriority[index].recovered
+ * ^^^^^^^^^^^^^^^^^^^^^^^ - ArrayAccessExpr
+ * ^^^^^^^^^^^^^^^^ - FieldAccessExpr
+ * ^^^^^ - NameExpr
+ * }
+ *
+ *
+ *
+ *
+ *
+ *
+ * @author Roger Howell
+ */
+public class ArrayAccessExprContext extends AbstractJavaParserContext {
+
+ public ArrayAccessExprContext(ArrayAccessExpr wrappedNode, TypeSolver typeSolver) {
+ super(wrappedNode, typeSolver);
+ }
+
+ public SymbolReference extends ResolvedValueDeclaration> solveSymbolInParentContext(String name) {
+ /*
+ * Simple implementation, included explicitly here for clarity:
+ * - Delegate to parent context per the documentation for ArrayAccessExprContext
+ * - Required to prevent recursive access to the "parent node" (not necessarily the same as the "parent context")
+ */
+ return super.solveSymbolInParentContext(name);
+ }
+
+}
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/contexts/BinaryExprContext.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/contexts/BinaryExprContext.java
new file mode 100644
index 0000000000000000000000000000000000000000..4f7e1e3398b196218f7fac0bf2becbbf2579b3b4
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/contexts/BinaryExprContext.java
@@ -0,0 +1,228 @@
+package com.github.javaparser.symbolsolver.javaparsermodel.contexts;
+
+import com.github.javaparser.ast.Node;
+import com.github.javaparser.ast.expr.BinaryExpr;
+import com.github.javaparser.ast.expr.Expression;
+import com.github.javaparser.ast.expr.PatternExpr;
+import com.github.javaparser.symbolsolver.core.resolution.Context;
+import com.github.javaparser.symbolsolver.javaparsermodel.JavaParserFactory;
+import com.github.javaparser.symbolsolver.model.resolution.TypeSolver;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+
+public class BinaryExprContext extends AbstractJavaParserContext {
+
+ public BinaryExprContext(BinaryExpr wrappedNode, TypeSolver typeSolver) {
+ super(wrappedNode, typeSolver);
+ }
+
+ @Override
+ public List patternExprsExposedFromChildren() {
+
+ BinaryExpr binaryExpr = wrappedNode;
+ Expression leftBranch = binaryExpr.getLeft();
+ Expression rightBranch = binaryExpr.getRight();
+
+ List results = new ArrayList<>();
+
+ if (binaryExpr.getOperator().equals(BinaryExpr.Operator.EQUALS)) {
+ if (rightBranch.isBooleanLiteralExpr()) {
+ if (rightBranch.asBooleanLiteralExpr().getValue() == true) {
+ // "x" instanceof String s == true
+ results.addAll(patternExprsExposedToDirectParentFromBranch(leftBranch));
+ } else {
+ // "x" instanceof String s == false
+ }
+ } else if (leftBranch.isBooleanLiteralExpr()) {
+ if (leftBranch.asBooleanLiteralExpr().getValue() == true) {
+ // true == "x" instanceof String s
+ results.addAll(patternExprsExposedToDirectParentFromBranch(rightBranch));
+ } else {
+ // false == "x" instanceof String s
+ }
+ }
+ } else if (binaryExpr.getOperator().equals(BinaryExpr.Operator.NOT_EQUALS)) {
+ if (rightBranch.isBooleanLiteralExpr()) {
+ if (rightBranch.asBooleanLiteralExpr().getValue() == true) {
+ // "x" instanceof String s != true
+ } else {
+ // "x" instanceof String s != false
+ results.addAll(patternExprsExposedToDirectParentFromBranch(leftBranch));
+ }
+ } else if (leftBranch.isBooleanLiteralExpr()) {
+ if (leftBranch.asBooleanLiteralExpr().getValue() == true) {
+ // true != "x" instanceof String s
+ } else {
+ // false != "x" instanceof String s
+ results.addAll(patternExprsExposedToDirectParentFromBranch(rightBranch));
+ }
+ }
+
+ // TODO/FIXME: There are other cases where it may be ambiguously true until runtime e.g. `"x" instanceof String s == (new Random().nextBoolean())`
+
+ } else if (binaryExpr.getOperator().equals(BinaryExpr.Operator.AND)) {
+ // "x" instanceof String s && s.length() > 0
+ // "x" instanceof String s && "x" instanceof String s2
+ results.addAll(patternExprsExposedToDirectParentFromBranch(leftBranch));
+ results.addAll(patternExprsExposedToDirectParentFromBranch(rightBranch));
+ } else {
+ return new ArrayList<>();
+ }
+
+ return results;
+ }
+
+ @Override
+ public List negatedPatternExprsExposedFromChildren() {
+
+ BinaryExpr binaryExpr = wrappedNode;
+ Expression leftBranch = binaryExpr.getLeft();
+ Expression rightBranch = binaryExpr.getRight();
+
+ List results = new ArrayList<>();
+
+ // FIXME: Redo the `.getValue() == true` to take more complex code into account when determining if definitively true (e.g. `
+ if (binaryExpr.getOperator().equals(BinaryExpr.Operator.EQUALS)) {
+ if (rightBranch.isBooleanLiteralExpr()) {
+ if (isDefinitivelyTrue(rightBranch)) {
+ // "x" instanceof String s == true
+ // "x" instanceof String s == !(false)
+ // No negations.
+ } else {
+ // "x" instanceof String s == false
+ // "x" instanceof String s == !(true)
+ results.addAll(patternExprsExposedToDirectParentFromBranch(leftBranch));
+ }
+ } else if (leftBranch.isBooleanLiteralExpr()) {
+ if (isDefinitivelyTrue(leftBranch)) {
+ // true == "x" instanceof String s
+ // !(false) == "x" instanceof String s
+ // No negations.
+ } else {
+ // false == "x" instanceof String s
+ // !(true) == "x" instanceof String s
+ results.addAll(patternExprsExposedToDirectParentFromBranch(rightBranch));
+ }
+ }
+ } else if (binaryExpr.getOperator().equals(BinaryExpr.Operator.NOT_EQUALS)) {
+ if (rightBranch.isBooleanLiteralExpr()) {
+ if (isDefinitivelyTrue(rightBranch)) {
+ // "x" instanceof String s != true
+ // "x" instanceof String s != !(false)
+ results.addAll(patternExprsExposedToDirectParentFromBranch(leftBranch));
+ } else {
+ // "x" instanceof String s != false
+ // "x" instanceof String s != !(true)
+ }
+ } else if (leftBranch.isBooleanLiteralExpr()) {
+ if (isDefinitivelyTrue(leftBranch)) {
+ // true != "x" instanceof String s
+ // !(false) != "x" instanceof String s
+ results.addAll(patternExprsExposedToDirectParentFromBranch(rightBranch));
+ } else {
+ // false != "x" instanceof String s
+ // !(true) != "x" instanceof String s
+ }
+ }
+
+ // TODO/FIXME: There are other cases where it may be ambiguously true until runtime e.g. `"x" instanceof String s == (new Random().nextBoolean())`
+
+ } else if (binaryExpr.getOperator().equals(BinaryExpr.Operator.AND)) {
+ // "x" instanceof String s && s.length() > 0
+ // "x" instanceof String s && "x" instanceof String s2
+ results.addAll(negatedPatternExprsExposedToDirectParentFromBranch(leftBranch));
+ results.addAll(negatedPatternExprsExposedToDirectParentFromBranch(rightBranch));
+ } else {
+ return new ArrayList<>();
+ }
+
+ return results;
+ }
+
+ private List patternExprsExposedToDirectParentFromBranch(Expression branch) {
+ if (branch.isEnclosedExpr() || branch.isBinaryExpr() || branch.isUnaryExpr() || branch.isInstanceOfExpr()) {
+ Context branchContext = JavaParserFactory.getContext(branch, typeSolver);
+ return branchContext.patternExprsExposedFromChildren();
+ }
+
+ return new ArrayList<>();
+ }
+
+ private List negatedPatternExprsExposedToDirectParentFromBranch(Expression branch) {
+ if (branch.isEnclosedExpr() || branch.isBinaryExpr() || branch.isUnaryExpr() || branch.isInstanceOfExpr()) {
+ Context branchContext = JavaParserFactory.getContext(branch, typeSolver);
+ return branchContext.negatedPatternExprsExposedFromChildren();
+ }
+
+ return new ArrayList<>();
+ }
+
+ public List patternExprsExposedToChild(Node child) {
+ BinaryExpr binaryExpr = wrappedNode;
+ Expression leftBranch = binaryExpr.getLeft();
+ Expression rightBranch = binaryExpr.getRight();
+
+ List results = new ArrayList<>();
+ if (child == leftBranch) {
+ results.addAll(patternExprsExposedToDirectParentFromBranch(leftBranch));
+ } else if (child == rightBranch) {
+ if (binaryExpr.getOperator().equals(BinaryExpr.Operator.AND)) {
+ // "" instanceof String s && "" instanceof String s2
+ results.addAll(patternExprsExposedToDirectParentFromBranch(leftBranch));
+ }
+ }
+// else if (binaryExpr.getOperator().equals(BinaryExpr.Operator.AND) && rightBranch.isAncestorOf(child)) {
+// // "" instanceof String s && "" instanceof String s2
+// results.addAll(patternExprsExposedToDirectParentFromBranch(leftBranch));
+// }
+
+ return results;
+ }
+
+
+ public Optional patternExprInScope(String name) {
+ BinaryExpr binaryExpr = wrappedNode;
+ Expression leftBranch = binaryExpr.getLeft();
+ Expression rightBranch = binaryExpr.getRight();
+
+ List patternExprs = patternExprsExposedToDirectParentFromBranch(leftBranch);
+ Optional localResolutionResults = patternExprs
+ .stream()
+ .filter(vd -> vd.getNameAsString().equals(name))
+ .findFirst();
+
+ if (localResolutionResults.isPresent()) {
+ return localResolutionResults;
+ }
+
+
+ // If we don't find the parameter locally, escalate up the scope hierarchy to see if it is declared there.
+ if (!getParent().isPresent()) {
+ return Optional.empty();
+ }
+ Context parentContext = getParent().get();
+ return parentContext.patternExprInScope(name);
+ }
+
+ private boolean isDefinitivelyTrue(Expression expression) {
+ // TODO: Consider combinations of literal true/false, enclosed expressions, and negations.
+ if (expression.isBooleanLiteralExpr()) {
+ if (expression.asBooleanLiteralExpr().getValue() == true) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private boolean isDefinitivelyFalse(Expression expression) {
+ // TODO: Consider combinations of literal true/false, enclosed expressions, and negations.
+ if (expression.isBooleanLiteralExpr()) {
+ if (expression.asBooleanLiteralExpr().getValue() == false) {
+ return true;
+ }
+ }
+ return false;
+ }
+}
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/contexts/BlockStmtContext.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/contexts/BlockStmtContext.java
new file mode 100644
index 0000000000000000000000000000000000000000..ccaed5ae609589144aec20417d1f5fb2d99d4906
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/contexts/BlockStmtContext.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2015-2016 Federico Tomassetti
+ * Copyright (C) 2017-2020 The JavaParser Team.
+ *
+ * This file is part of JavaParser.
+ *
+ * JavaParser can be used either under the terms of
+ * a) the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * b) the terms of the Apache License
+ *
+ * You should have received a copy of both licenses in LICENCE.LGPL and
+ * LICENCE.APACHE. Please refer to those files for details.
+ *
+ * JavaParser is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ */
+
+package com.github.javaparser.symbolsolver.javaparsermodel.contexts;
+
+import com.github.javaparser.ast.Node;
+import com.github.javaparser.ast.body.VariableDeclarator;
+import com.github.javaparser.ast.expr.VariableDeclarationExpr;
+import com.github.javaparser.ast.stmt.BlockStmt;
+import com.github.javaparser.ast.stmt.ExpressionStmt;
+import com.github.javaparser.ast.stmt.Statement;
+import com.github.javaparser.resolution.declarations.ResolvedValueDeclaration;
+import com.github.javaparser.symbolsolver.core.resolution.Context;
+import com.github.javaparser.symbolsolver.javaparsermodel.declarations.JavaParserSymbolDeclaration;
+import com.github.javaparser.symbolsolver.model.resolution.SymbolReference;
+import com.github.javaparser.symbolsolver.model.resolution.TypeSolver;
+
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Optional;
+
+public class BlockStmtContext extends AbstractJavaParserContext {
+
+ public BlockStmtContext(BlockStmt wrappedNode, TypeSolver typeSolver) {
+ super(wrappedNode, typeSolver);
+ }
+
+ @Override
+ public List localVariablesExposedToChild(Node child) {
+ int position = wrappedNode.getStatements().indexOf(child);
+ if (position == -1) {
+ throw new RuntimeException();
+ }
+ List variableDeclarators = new LinkedList<>();
+ for (int i = position - 1; i >= 0; i--) {
+ variableDeclarators.addAll(localVariablesDeclaredIn(wrappedNode.getStatement(i)));
+ }
+ return variableDeclarators;
+ }
+
+ private List localVariablesDeclaredIn(Statement statement) {
+ if (statement instanceof ExpressionStmt) {
+ ExpressionStmt expressionStmt = (ExpressionStmt) statement;
+ if (expressionStmt.getExpression() instanceof VariableDeclarationExpr) {
+ VariableDeclarationExpr variableDeclarationExpr = (VariableDeclarationExpr) expressionStmt.getExpression();
+ List variableDeclarators = new LinkedList<>();
+ variableDeclarators.addAll(variableDeclarationExpr.getVariables());
+ return variableDeclarators;
+ }
+ }
+ return Collections.emptyList();
+ }
+
+ @Override
+ public SymbolReference extends ResolvedValueDeclaration> solveSymbol(String name) {
+ Optional optionalParent = getParent();
+ if (!optionalParent.isPresent()) {
+ return SymbolReference.unsolved(ResolvedValueDeclaration.class);
+ }
+
+ if (wrappedNode.getStatements().size() > 0) {
+ // tries to resolve a declaration from local variables defined in child statements
+ // or from parent node context
+ // for example resolve declaration for the MethodCallExpr a.method() in
+ // A a = this;
+ // {
+ // a.method();
+ // }
+
+ List variableDeclarators = new LinkedList<>();
+ // find all variable declarators exposed to child
+ // given that we don't know the statement we are trying to resolve, we look for all variable declarations
+ // defined in the context of the wrapped node whether it is located before or after the statement that interests us
+ // because a variable cannot be (re)defined after having been used
+ wrappedNode.getStatements().getLast().ifPresent(stmt -> variableDeclarators.addAll(localVariablesExposedToChild(stmt)));
+ if (!variableDeclarators.isEmpty()) {
+ // FIXME: Work backwards from the current statement, to only consider declarations prior to this statement.
+ for (VariableDeclarator vd : variableDeclarators) {
+ if (vd.getNameAsString().equals(name)) {
+ return SymbolReference.solved(JavaParserSymbolDeclaration.localVar(vd, typeSolver));
+ }
+ }
+ }
+ }
+
+ // Otherwise continue as normal...
+ return solveSymbolInParentContext(name);
+ }
+}
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/contexts/CatchClauseContext.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/contexts/CatchClauseContext.java
new file mode 100644
index 0000000000000000000000000000000000000000..f909267c0cc1513b7d8e4df103e4692f3ca194d7
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/contexts/CatchClauseContext.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2015-2016 Federico Tomassetti
+ * Copyright (C) 2017-2020 The JavaParser Team.
+ *
+ * This file is part of JavaParser.
+ *
+ * JavaParser can be used either under the terms of
+ * a) the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * b) the terms of the Apache License
+ *
+ * You should have received a copy of both licenses in LICENCE.LGPL and
+ * LICENCE.APACHE. Please refer to those files for details.
+ *
+ * JavaParser is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ */
+
+package com.github.javaparser.symbolsolver.javaparsermodel.contexts;
+
+import com.github.javaparser.ast.Node;
+import com.github.javaparser.ast.body.Parameter;
+import com.github.javaparser.ast.body.VariableDeclarator;
+import com.github.javaparser.ast.stmt.CatchClause;
+import com.github.javaparser.resolution.declarations.ResolvedMethodDeclaration;
+import com.github.javaparser.resolution.declarations.ResolvedValueDeclaration;
+import com.github.javaparser.resolution.types.ResolvedType;
+import com.github.javaparser.symbolsolver.javaparsermodel.JavaParserFactory;
+import com.github.javaparser.symbolsolver.model.resolution.SymbolReference;
+import com.github.javaparser.symbolsolver.model.resolution.TypeSolver;
+import com.github.javaparser.symbolsolver.model.resolution.Value;
+import com.github.javaparser.symbolsolver.resolution.SymbolDeclarator;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Optional;
+
+/**
+ * @author Fred Lefévère-Laoide
+ */
+public class CatchClauseContext extends AbstractJavaParserContext {
+
+ public CatchClauseContext(CatchClause wrappedNode, TypeSolver typeSolver) {
+ super(wrappedNode, typeSolver);
+ }
+
+ @Override
+ public final SymbolReference extends ResolvedValueDeclaration> solveSymbol(String name) {
+ SymbolDeclarator sb = JavaParserFactory.getSymbolDeclarator(wrappedNode.getParameter(), typeSolver);
+ SymbolReference extends ResolvedValueDeclaration> symbolReference = AbstractJavaParserContext.solveWith(sb, name);
+ if (symbolReference.isSolved()) {
+ return symbolReference;
+ }
+
+ // if nothing is found we should ask the parent context
+ return solveSymbolInParentContext(name);
+ }
+
+ @Override
+ public final Optional solveSymbolAsValue(String name) {
+ SymbolDeclarator sb = JavaParserFactory.getSymbolDeclarator(wrappedNode.getParameter(), typeSolver);
+ Optional symbolReference = solveWithAsValue(sb, name);
+ if (symbolReference.isPresent()) {
+ // Perform parameter type substitution as needed
+ return symbolReference;
+ }
+
+ // if nothing is found we should ask the parent context
+ return solveSymbolAsValueInParentContext(name);
+ }
+
+ @Override
+ public final SymbolReference solveMethod(String name, List argumentsTypes, boolean staticOnly) {
+ // TODO: Document why staticOnly is forced to be false.
+ return solveMethodInParentContext(name, argumentsTypes, false);
+ }
+
+ @Override
+ public List localVariablesExposedToChild(Node child) {
+ return Collections.emptyList();
+ }
+
+ @Override
+ public List parametersExposedToChild(Node child) {
+ // TODO/FIXME: Presumably the parameters must be exposed to all children and their descendants, not just the direct child?
+ if (child == getWrappedNode().getBody()) {
+ return Collections.singletonList(getWrappedNode().getParameter());
+ }
+ return Collections.emptyList();
+ }
+}
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/contexts/ClassOrInterfaceDeclarationContext.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/contexts/ClassOrInterfaceDeclarationContext.java
new file mode 100644
index 0000000000000000000000000000000000000000..308fb7b0defa3e4a5ded3c6cae946964cc5f83cf
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/contexts/ClassOrInterfaceDeclarationContext.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2015-2016 Federico Tomassetti
+ * Copyright (C) 2017-2020 The JavaParser Team.
+ *
+ * This file is part of JavaParser.
+ *
+ * JavaParser can be used either under the terms of
+ * a) the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * b) the terms of the Apache License
+ *
+ * You should have received a copy of both licenses in LICENCE.LGPL and
+ * LICENCE.APACHE. Please refer to those files for details.
+ *
+ * JavaParser is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ */
+
+package com.github.javaparser.symbolsolver.javaparsermodel.contexts;
+
+import com.github.javaparser.ast.Node;
+import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration;
+import com.github.javaparser.ast.type.TypeParameter;
+import com.github.javaparser.resolution.declarations.*;
+import com.github.javaparser.resolution.types.ResolvedType;
+import com.github.javaparser.resolution.types.ResolvedTypeVariable;
+import com.github.javaparser.symbolsolver.javaparsermodel.JavaParserFacade;
+import com.github.javaparser.symbolsolver.javaparsermodel.declarations.JavaParserTypeParameter;
+import com.github.javaparser.symbolsolver.model.resolution.SymbolReference;
+import com.github.javaparser.symbolsolver.model.resolution.TypeSolver;
+import com.github.javaparser.symbolsolver.model.resolution.Value;
+
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Optional;
+
+/**
+ * @author Federico Tomassetti
+ */
+public class ClassOrInterfaceDeclarationContext extends AbstractJavaParserContext