Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SymbolSolver is unable to .resolve() #1943

Closed
un0btanium opened this issue Dec 1, 2018 · 30 comments
Closed

SymbolSolver is unable to .resolve() #1943

un0btanium opened this issue Dec 1, 2018 · 30 comments
Assignees
Milestone

Comments

@un0btanium
Copy link
Contributor

Hello,

i am trying to find the fully qualified name of interface main.MyInterface<E> that is being implemented in class main.MyClass<E>.

package main;
public class MyClass<E> implements MyInterface<E> { }
package main;

public class MyInterface<E> { }
for (ClassOrInterfaceType type : classDeclaration.getImplementedTypes()) {
	ResolvedReferenceType rrt = type.resolve(); // exception gets thrown here
	if (rrt != null) {
		String interfaceName = rrt.getQualifiedName();
		// do other stuff with it
	}
}

Sadly, this yields me an exception:

Symbol resolution not configured: to configure consider setting a SymbolResolver in the ParserConfiguration
	at com.github.javaparser.ast.Node.lambda$getSymbolResolver$5(Node.java:682)
	at java.base/java.util.Optional.map(Optional.java:254)
	at com.github.javaparser.ast.Node.getSymbolResolver(Node.java:679)
	at com.github.javaparser.ast.type.ClassOrInterfaceType.resolve(ClassOrInterfaceType.java:292)

I am setting a SymbolSolver before I start running .resolve():

CombinedTypeSolver typeSolver = new CombinedTypeSolver(new ReflectionTypeSolver());
// optionally some more external libraries added (required for what I am doing)
CombinedTypeSolver finalTypeSolver = new CombinedTypeSolver(typeSolver);
if (path.endsWith(".jar")) {
	finalTypeSolver.add(new JarTypeSolver(path));
} else if (path.endsWith("src")){
	finalTypeSolver.add(new JavaParserTypeSolver(path));
}

JavaSymbolSolver symbolSolver = new JavaSymbolSolver(finalTypeSolver);
JavaParser.getStaticConfiguration().setSymbolResolver(symbolSolver);

I also double checked without the generic types and it always yields the same exception. Same issue with extending from classes.
Am I doing something wrong or why does this not work as intended?

I just updated to the 3.7.1 (from 3.6.23) and now the exception is now this:

java.lang.IllegalStateException: No data of this type found. Use containsData to check for this first.
	at com.github.javaparser.ast.Node.getData(Node.java:457)
	at com.github.javaparser.ast.Node.lambda$getSymbolResolver$5(Node.java:699)
	at java.base/java.util.Optional.map(Optional.java:254)
	at com.github.javaparser.ast.Node.getSymbolResolver(Node.java:698)
	at com.github.javaparser.ast.type.ClassOrInterfaceType.resolve(ClassOrInterfaceType.java:292)

Since this did work before and only started to fail recently, I double checked what I did differently. And it turns out that I previously was using a path to a source folder (JavaParserTypeSolver). Now I am trying to read a JAR file and therefore use the JarTypeSolver, but it does not seem to work with that one. Whats going on here?

@un0btanium
Copy link
Contributor Author

un0btanium commented Dec 1, 2018

So apparently .resolve() is the problem.

The old fashioned way works:

ResolvedType rt = JavaParserFacade.get(typeSolver).convertToUsage(type);
if (rt.isReferenceType()) {
	System.out.println(rt.asReferenceType().getQualifiedName());
}

It is still weird that it works when checking raw source files and it does not work when checking a JAR file. Is this intended behaviour?

@matozoid matozoid added Question (JP usage) How to use JP to inspect/modify the AST - please close your question once answered! Symbol Solver labels Dec 1, 2018
@matozoid
Copy link
Contributor

matozoid commented Dec 1, 2018

Maybe @ftomassetti knows.

@un0btanium un0btanium changed the title SymbolSolver is unable to find inheritanced interface type SymbolSolver is unable to .resolve() when reading from a JAR file Dec 1, 2018
@un0btanium
Copy link
Contributor Author

I am using the com.github.javaparser.utils.SourceZip class to read the JAR file.

@ftomassetti
Copy link
Member

Are you setting the symbol solver before parsing?

@ftomassetti
Copy link
Member

the problem seems to be that typesolver is not injected in the nodes

@un0btanium
Copy link
Contributor Author

Yes, since my file is ruff spaghetti code, I am very confident that I setup the SymbolSolver before reading any of the source code file. The file looks like this:

		TypeSolver combinedTypeSolver = null;
		if (pathToJar.endsWith(".jar")) {
			try {
				combinedTypeSolver = new CombinedTypeSolver(new ReflectionTypeSolver(), new JarTypeSolver(pathToJar));
			} catch (IOException e) {
				e.printStackTrace();
				return "Something went wrong while trying to read your JAR file!";
			}
		} else if (pathToJar.endsWith("src")){
			combinedTypeSolver = new CombinedTypeSolver(new ReflectionTypeSolver(), new JavaParserTypeSolver(pathToJar));
		}
		
		JavaSymbolSolver symbolSolver = new JavaSymbolSolver(combinedTypeSolver);
		JavaParser.getStaticConfiguration().setSymbolResolver(symbolSolver);

		Map<String, CompilationUnit> files = new HashMap<>();
		if (pathToJar.endsWith(".jar")) {
			SourceZip sourceZip = new SourceZip(Paths.get(pathToJar), new ParserConfiguration().setTabSize(4));
			List<Pair<Path, ParseResult<CompilationUnit>>> cuList;
			try {
				cuList = sourceZip.parse();
			} catch (IOException e) {
				e.printStackTrace();
				return "";
			}
			for (Pair<Path, ParseResult<CompilationUnit>> pair : cuList) {
				Optional<CompilationUnit> optional = pair.b.getResult();
				if (optional.isPresent()) {

					Optional<ClassOrInterfaceDeclaration> optionalNode = optional.get().findFirst(ClassOrInterfaceDeclaration.class);
					if (optionalNode.isPresent()) {
						String name = QualifiedNameGenerator.getFullyQualifiedName(optionalNode.get());
						files.put(name, optional.get());
					}
				}
			}
		} else {
			if (!pathToJar.endsWith("src")) {
				pathToJar = pathToJar + File.separator + "src";
			}
			if (!Files.exists(Paths.get(pathToJar))) {
				return "No folder '" + pathToJar + "' found!";
			}
			if (!Files.isDirectory(Paths.get(pathToJar))) {
				return "The path '" + pathToJar + "' is not a folder!";
			}

			List<File> sourceFiles = new ArrayList<>();
			findSourceFiles(pathToJar, sourceFiles);
			
			for (File file : sourceFiles) {
				CompilationUnit cu;
				try {
					cu = JavaParser.parse(file);
				} catch (FileNotFoundException e) {
					e.printStackTrace();
					return "Unable to parse file! Exiting...";
				}
				Optional<ClassOrInterfaceDeclaration> optionalNode = cu.findFirst(ClassOrInterfaceDeclaration.class);
				if (optionalNode.isPresent()) {
					String name = QualifiedNameGenerator.getFullyQualifiedName(optionalNode.get());
					files.put(name, cu);
				}
			}
		}

		 // MORE CODE INBETWEEN

		HashMap<String, ArrayList<String>> classAndTheirInterfaces = new HashMap<>();
		for (String key : files.keySet()) {
			CompilationUnit cu = files.get(key);
			for (ClassOrInterfaceDeclaration c : cu.findAll(ClassOrInterfaceDeclaration.class)) {
				if (c.getImplementedTypes().size() > 0) {
					ArrayList<String> interfacesOfThisClass = new ArrayList<>();
					for (ClassOrInterfaceType type : c.getImplementedTypes()) {
						ResolvedReferenceType rrt = type.resolve(); // THIS FAILS
						ResolvedType rt = JavaParserFacade.get(combinedTypeSolver).convertToUsage(type); // THIS WORKS
						if (rt.isReferenceType()) {
							interfacesOfThisClass.add("\"" + rt.asReferenceType().getQualifiedName() + "\"");
						}
					}
					classAndTheirInterfaces.put(QualifiedNameGenerator.getFullyQualifiedName(c), interfacesOfThisClass);
				}
			}
		}```

@un0btanium
Copy link
Contributor Author

I should really get into streams. The code is embarrassing :D

@un0btanium
Copy link
Contributor Author

un0btanium commented Dec 2, 2018

I probably should explain it a little bit, so that it might be more helpful when it comes to help figure out what went wrong.

The input is a path to a folder containing source files or a JAR file.
The first part is where I define the TypeSolver by adding the path to the JAR file or folder.
Then I have two ways to read the source files. One for the folder, where I just scan it for .java files and parse them one-by-one with JavaParser.parse(File). And one for the JAR file, where I use the SourceZip class to read the .java entires.
I save the ComputationUnits of each file in a Map for later use where the key is the qualified name of the file/first class in the file.
Once the source files are read and ASTs have been generated, I try to find the Interface class that is being implemented on another class. If I use ResolvedType rt = JavaParserFacade.get(combinedTypeSolver).convertToUsage(type); it always works.
If I use ResolvedReferenceType rrt = type.resolve(); it throws me the exception. But only if I read the source files from a JAR file with SourceZip.
So the issue is either SourceZip or JarTypeSolver.

@un0btanium
Copy link
Contributor Author

un0btanium commented Jan 16, 2019

I know this is a complex issue (and I probably could have made a better job at preseting it), but did you have the time to read into it yet? @ftomassetti

@matozoid
Copy link
Contributor

Sorry, I'm busy trying to get JDK12 support in, and @ftomassetti is drowning in work.

Can it be that SourceZip gets a configuration where you don't set the symbol solver?

@un0btanium
Copy link
Contributor Author

No problem.

Ah, indeed. SourceZip has its own ParserConfiguration and requires the TypeSolver to be set. I thought it would always just take the statically set TypeSolver, but I guess SourceZip is older than the newest .resolve() functionality.

new SourceZip(Paths.get(path), new ParserConfiguration().setSymbolResolver(symbolSolver));

@matozoid
Copy link
Contributor

Nah, the static configuration is for the static methods. Every instance gets its own configuration.

It's kind of confusing, so the plan is to remove all the static methods soon.

@MarioIshac
Copy link

MarioIshac commented Jan 24, 2019

I have the same problem, except I am not using SourceZip. Below is the method that sets the symbol resolver:

private void setSymbolSolver() {
        final String sourceDirectory = getAnnotatedClassSourceInformation().getSourceDirectory();
        
        final ReflectionTypeSolver standardLibraryTypeSolver = new ReflectionTypeSolver();
        final JavaParserTypeSolver processedProjectTypeSolver = new JavaParserTypeSolver(sourceDirectory);

        final CombinedTypeSolver combinedTypeSolver = new CombinedTypeSolver(
            standardLibraryTypeSolver,
            processedProjectTypeSolver
        );

        final ClassLoader processedProjectClassLoader = getClass().getClassLoader();
        final URLClassLoader processedProjectURLClassLoader = (URLClassLoader) processedProjectClassLoader;

        final URL[] processedProjectDependencyLocations = processedProjectURLClassLoader.getURLs();

        try {
            for (final URL processedProjectDependencyLocation : processedProjectDependencyLocations) {
                final Path dependencyLocationPath = Paths.get(processedProjectDependencyLocation.toURI());

                if (dependencyLocationPath.endsWith(".jar")) {
                    final JavaParserTypeSolver dependencyTypeSolver = new JavaParserTypeSolver(dependencyLocationPath);

                    combinedTypeSolver.add(dependencyTypeSolver);
                }
            }
        }
        catch (final URISyntaxException e) {
            throw new RuntimeException(e);
        }

        final JavaSymbolSolver javaSymbolSolver = new JavaSymbolSolver(combinedTypeSolver);

        JavaParser.getStaticConfiguration().setSymbolResolver(javaSymbolSolver);
    }

and here is the calling code:

@Override
public Visitable visit(
    final ClassOrInterfaceType classOrInterfaceType,
    final PrimitiveTypesCombination primitiveTypesCombination
) {
    super.visit(classOrInterfaceType, primitiveTypesCombination);
    ResolvedReferenceType resolvedClassOrInterfaceType = classOrInterfaceType.resolve();
    ...
}

I get the following exception:

Caused by: java.lang.IllegalStateException: No data of this type found. Use containsData to check for this first.

@matozoid
Copy link
Contributor

@TheeNinja the crucial part is how you do the parsing, which is not in your report.
All JavaParser.parse... methods use the static configuration.
new Javaparser(config).parse(..,..,..) uses the configuration from config.

The error simply means that the solver is not set in the configuration the parser is using.

@MarioIshac
Copy link

@TheeNinja the crucial part is how you do the parsing, which is not in your report.
All JavaParser.parse... methods use the static configuration.
new Javaparser(config).parse(..,..,..) uses the configuration from config.

The error simply means that the solver is not set in the configuration the parser is using.

I am getting my "generic" compilation unit through final CompilationUnit genericClassCompilationUnit = JavaParser.parse(annotatedClassSourceCode);. I then get my "specialized" compilation unit through cloning the generic compilation unit (this will happen multiple times). final CompilationUnit specializedClassCompilationUnit = genericClassCompilationUnit.clone(); I then perform all the visiting on the specializedClassCompilationUnit. setSymbolResolver() is called before the generic compilation unit is parsed. Does cloning throw away the resolutions?

@matozoid
Copy link
Contributor

I do think it does not clone all data (the CloneVisitor has the answer.) This will need some thinking...

@un0btanium un0btanium changed the title SymbolSolver is unable to .resolve() when reading from a JAR file SymbolSolver is unable to .resolve() Feb 12, 2019
@matozoid
Copy link
Contributor

matozoid commented Apr 5, 2019

Oh yeah! Let's fix this one.

@matozoid matozoid added Bug report and removed Question (JP usage) How to use JP to inspect/modify the AST - please close your question once answered! Symbol Solver labels Apr 5, 2019
@un0btanium un0btanium reopened this Apr 5, 2019
@matozoid matozoid modified the milestones: 3.13.5, next release Apr 7, 2019
@jwertherf
Copy link

Hi everybody,

I am having the same problem to solve symbols with sources that are in jar file. I am using the javaparser/symbolsolver v.3.13.3 and the problem still continues. When the program executes the ".resolve()", it yelds a UnsolvedSymbolException, even loading the sources previously using JarTypeSolver.

Does anybody already have a solution?

@un0btanium
Copy link
Contributor Author

un0btanium commented May 30, 2019

Well, the solution for me was to set the SymbolSolver on the ParserConfiguration and pass it on to SourceZip (or another instance that consumes ParserConfiguration). Did you set it?
new SourceZip(Paths.get(path), new ParserConfiguration().setSymbolResolver(symbolSolver));

@jwertherf
Copy link

jwertherf commented May 30, 2019

Sorry, but I think that I did not understand exactly how the SourceZip works.
Well, below there is a source code snippet:

	CombinedTypeSolver typeSolver = new CombinedTypeSolver();
	typeSolver.add(new ReflectionTypeSolver(false)); 
	typeSolver.add(new JavaParserTypeSolver(new File(SOURCE_PATH))); 
typeSolver.add(new JarTypeSolver(jarSourceFile));
	
	JavaSymbolSolver symbolSolver = new JavaSymbolSolver(typeSolver);
	StaticJavaParser.getConfiguration().setSymbolResolver(symbolSolver);    	

Where should I put the mentioned SourceZip code snippet in this code and how I use it later to ".resolve()"?

@un0btanium
Copy link
Contributor Author

un0btanium commented May 30, 2019

SourceZip is a class that allows you to read in all the source files from a JAR or ZIP file. I used it in my program to read in the source code. You probably use another class to help you parse the source code. So you have to provide the Configuration to that instead, not StaticJavaParser.

As far as I understood: The ParserConfiguration object gets passed into each Node, which then gets used by .resolve(). If you set the ParserConfiguration as static, then it probably wont be injected in your Node objects. Thus, you have to provide your SymbolSolver to the ParserConfiguration of the Class you use to parse your files. I dont know how you read in the source code to parse it, so you might want to provide that part.

@jwertherf
Copy link

jwertherf commented May 30, 2019

I have use the JarTypeSolver class to read source codes inside jar files. If I am not mistaken, I have worked with this class and it has worked fine - using also a static parser - but it is not working now. The code below shows the part that I need use the ".resolve()" to solve the method called to get information about its package, class, etc.

	new VoidVisitorAdapter<Object>() {
	    @Override
	    public void visit(MethodCallExpr methodCall, Object obj) {
			super.visit(methodCall, obj);
			// Identify the host class (source)
			Node node = (Node) methodCall.getParentNode().get();
			while(!(node instanceof ClassOrInterfaceDeclaration) && 
				  !(node instanceof EnumDeclaration)) {
				node = node.getParentNode().get();
			}
			ClassInfo cSource = null;
			if(node instanceof ClassOrInterfaceDeclaration) // its a class
				cSource = new ClassInfo((ClassOrInterfaceDeclaration) node);
			else // is a enum
				cSource = new ClassInfo((EnumDeclaration) node);
	                //
			// Identify the method called
			ResolvedMethodDeclaration resmd = null;
			// rejecting method call that can not have a ResolvedMethodDeclaration instance
			try {
				resmd = methodCall.resolve();
			} catch (UnsolvedSymbolException e) {
				if(log) toLog("WARNING: rejecting method call "+methodCall.getName()+" - UnsolvedSymbolException: "+e.getMessage());
                       }
                   ...
                   ... (etc.)
                   ...
              }  

@un0btanium
Copy link
Contributor Author

un0btanium commented May 30, 2019

Are you using JavaParser.parse(...) on the source code you are parsing? How do you parse your code is important, because you have to provide that class the Configuration. Doesnt matter which TypeSolver you use if you put it on the wrong class/instance.

@jwertherf
Copy link

Sorry, I forgot to tell you how I call the parser in the code. See it now.

	new VoidVisitorAdapter<Object>() {
	    @Override
	    public void visit(MethodCallExpr methodCall, Object obj) {
			super.visit(methodCall, obj);
			// Identify the host class (source)
			Node node = (Node) methodCall.getParentNode().get();
			while(!(node instanceof ClassOrInterfaceDeclaration) && 
				  !(node instanceof EnumDeclaration)) {
				node = node.getParentNode().get();
			}
			ClassInfo cSource = null;
			if(node instanceof ClassOrInterfaceDeclaration) // its a class
				cSource = new ClassInfo((ClassOrInterfaceDeclaration) node);
			else // is a enum
				cSource = new ClassInfo((EnumDeclaration) node);
	                //
			// Identify the method called
			ResolvedMethodDeclaration resmd = null;
			// rejecting method call that can not have a ResolvedMethodDeclaration instance
			try {
				resmd = methodCall.resolve();
			} catch (UnsolvedSymbolException e) {
				if(log) toLog("WARNING: rejecting method call "+methodCall.getName()+" - UnsolvedSymbolException: "+e.getMessage());
                       }
                   ...
                   ... (etc.)
                   ...
              }  
	}.visit(StaticJavaParser.parse(new File(completeFileName)), null);

@un0btanium
Copy link
Contributor Author

Did you try StaticJavaParser.setConfiguration(ParserConfiguration parserConfiguration) yet? Instead of doing getConfiguration() =

@jwertherf
Copy link

Yes. Unfortunately it did not work.

Did you try StaticJavaParser.setConfiguration(ParserConfiguration parserConfiguration) yet? Instead of doing getConfiguration() =

@un0btanium
Copy link
Contributor Author

Hm, then I have no idea. Maybe the developers can help you with this one. Maybe create a new issue since this seems to be a bit different from mine. And mine got already solved. Since then they changed some stuff, so maybe something broke along the way.

@jwertherf
Copy link

Ok, thank you very much.

Hm, then I have no idea. Maybe the developers can help you with this one. Maybe create a new issue since this seems to be a bit different from mine. And mine got already solved. Since then they changed some stuff, so maybe something broke along the way.

@un0btanium
Copy link
Contributor Author

No Problem :)

@wsqandgy
Copy link

I also meet this error。I resolved this problem finally。

because code has a complie error, when i fix this error, is work well。

if anyone meet this problem,review that

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

6 participants