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

T#<method declared in java.lang.Object>, where T is a Type Parameter, cannot be resolved. #2044

Closed
MarioIshac opened this issue Jan 27, 2019 · 6 comments
Labels
Question (JP usage) How to use JP to inspect/modify the AST - please close your question once answered! Symbol Solver
Milestone

Comments

@MarioIshac
Copy link

Let's look at this sample method I have:

private int getPartition(final K key) {
    return key.hashCode() / getHashes().length;
}

Based on what I've seen so far, the symbol solver cannot find a ResolvedMethodDeclaration from the MethodCallExpr key.hashCode() because key is of type K a type parameter. However, key.hashCode() is a valid MethodCallExpr, because K implicitly extends Object.

I may be way off here, and I may have not set up my resolver correctly (though I am using ReflectionTypeSolver). But is this what is happening, and if so, does this sound like a bug or intended behavior? I can get around it either way.

@matozoid
Copy link
Contributor

@ftomassetti and friends?

@matozoid matozoid added Question (JP usage) How to use JP to inspect/modify the AST - please close your question once answered! Symbol Solver labels Jan 27, 2019
@MysterAitch
Copy link
Member

MysterAitch commented Jan 27, 2019

How are you doing getting the resolved type?

Something similar came up as a problem for me recently where .resolve().getResolvedType() (or similar - I don't have the code in front of me) would seemingly return the declared generic type, while .calculateResolvedType() would return the instantiated type. This came up when trying to resolve entrysets and entries in a set or list for example.

NB: This comes with the disclaimer that I've not fully investigated/confirmed the difference, but it might be related to the above.

@MarioIshac
Copy link
Author

MarioIshac commented Jan 27, 2019

How are you doing getting the resolved type?

Something similar came up as a problem for me recently where .resolve().getResolvedType() (or similar - I don't have the code in front of me) would seemingly return the declared generic type, while .calculateResolvedType() would return the instantiated type. This came up when trying to resolve entrysets and entries in a set or list for example.

NB: This comes with the disclaimer that I've not fully investigated/confirmed the difference, but it might be related to the above.

I'm still using the old API, so I get the ResolvedMethodDeclaration through JavaParserFascade#solveMethodAsUsage(methodCallExpr).getDeclaration(); I haven't discovered a problem with resolving the type yet, getJavaParserFacade().getType(methodCallExpr.getScope().get()); works completely fine (assuming that the Optional contains a value.

Additional Info: This is the stacktrace:

Caused by: java.lang.RuntimeException: Method 'hashCode' cannot be resolved in context key.hashCode() (line: 40) MethodCallExprContext{wrapped=key.hashCode()}. Parameter types: []
    at com.github.javaparser.symbolsolver.javaparsermodel.JavaParserFacade.solveMethodAsUsage (JavaParserFacade.java:586)
    at me.theeninja.primitivespecializer.core.processor.SpecializingVisitor.visit (SpecializingVisitor.java:625)
    at me.theeninja.primitivespecializer.core.processor.SpecializingVisitor$VisitForwarder.visit (SpecializingVisitor.java:76)
    at me.theeninja.primitivespecializer.core.processor.SpecializingVisitor$VisitForwarder.access$000 (SpecializingVisitor.java:62)
    at me.theeninja.primitivespecializer.core.processor.SpecializingVisitor.visitOrderly (SpecializingVisitor.java:118)
    at me.theeninja.primitivespecializer.core.processor.SpecializationWriter.generateSpecializationPerCombination (SpecializationWriter.java:283)
    at me.theeninja.primitivespecializer.core.processor.SpecializationWriter.generateSpecializationPerCombination (SpecializationWriter.java:183)
    at me.theeninja.primitivespecializer.core.processor.SpecializingProcessor.processSingle (SpecializingProcessor.java:192)
    at me.theeninja.primitivespecializer.core.processor.SpecializingProcessor.process (SpecializingProcessor.java:208)
    at com.sun.tools.javac.processing.JavacProcessingEnvironment.callProcessor (JavacProcessingEnvironment.java:964)
    at com.sun.tools.javac.processing.JavacProcessingEnvironment.discoverAndRunProcs (JavacProcessingEnvironment.java:881)
    at com.sun.tools.javac.processing.JavacProcessingEnvironment.access$2100 (JavacProcessingEnvironment.java:110)
    at com.sun.tools.javac.processing.JavacProcessingEnvironment$Round.run (JavacProcessingEnvironment.java:1202)
    at com.sun.tools.javac.processing.JavacProcessingEnvironment.doProcessing (JavacProcessingEnvironment.java:1311)
    at com.sun.tools.javac.main.JavaCompiler.processAnnotations (JavaCompiler.java:1250)
    at com.sun.tools.javac.main.JavaCompiler.compile (JavaCompiler.java:928)
    at com.sun.tools.javac.api.JavacTaskImpl.lambda$doCall$0 (JavacTaskImpl.java:100)
    at com.sun.tools.javac.api.JavacTaskImpl.handleExceptions (JavacTaskImpl.java:142)
    at com.sun.tools.javac.api.JavacTaskImpl.doCall (JavacTaskImpl.java:96)
    at com.sun.tools.javac.api.JavacTaskImpl.call (JavacTaskImpl.java:90)
    at org.codehaus.plexus.compiler.javac.JavaxToolsCompiler.compileInProcess (JavaxToolsCompiler.java:126)
    at org.codehaus.plexus.compiler.javac.JavacCompiler.performCompile (JavacCompiler.java:174)
    at org.apache.maven.plugin.compiler.AbstractCompilerMojo.execute (AbstractCompilerMojo.java:1129)
    at org.apache.maven.plugin.compiler.CompilerMojo.execute (CompilerMojo.java:188)
    at org.apache.maven.plugin.DefaultBuildPluginManager.executeMojo (DefaultBuildPluginManager.java:134)
    at org.apache.maven.lifecycle.internal.MojoExecutor.execute (MojoExecutor.java:208)
    at org.apache.maven.lifecycle.internal.MojoExecutor.execute (MojoExecutor.java:154)
    at org.apache.maven.lifecycle.internal.MojoExecutor.execute (MojoExecutor.java:146)
    at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject (LifecycleModuleBuilder.java:117)
    at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject (LifecycleModuleBuilder.java:81)
    at org.apache.maven.lifecycle.internal.builder.singlethreaded.SingleThreadedBuilder.build (SingleThreadedBuilder.java:51)
    at org.apache.maven.lifecycle.internal.LifecycleStarter.execute (LifecycleStarter.java:128)
    at org.apache.maven.DefaultMaven.doExecute (DefaultMaven.java:309)
    at org.apache.maven.DefaultMaven.doExecute (DefaultMaven.java:194)
    at org.apache.maven.DefaultMaven.execute (DefaultMaven.java:107)
    at org.apache.maven.cli.MavenCli.execute (MavenCli.java:955)
    at org.apache.maven.cli.MavenCli.doMain (MavenCli.java:290)
    at org.apache.maven.cli.MavenCli.main (MavenCli.java:194)
    at jdk.internal.reflect.NativeMethodAccessorImpl.invoke0 (Native Method)
    at jdk.internal.reflect.NativeMethodAccessorImpl.invoke (NativeMethodAccessorImpl.java:62)
    at jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke (DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke (Method.java:564)
    at org.codehaus.plexus.classworlds.launcher.Launcher.launchEnhanced (Launcher.java:289)
    at org.codehaus.plexus.classworlds.launcher.Launcher.launch (Launcher.java:229)
    at org.codehaus.plexus.classworlds.launcher.Launcher.mainWithExitCode (Launcher.java:415)
    at org.codehaus.plexus.classworlds.launcher.Launcher.main (Launcher.java:356)

@MysterAitch
Copy link
Member

Been doing some digging and the issue above is easy to demonstrate using these test cases. Resolving key.hashCode() works fine when the type variable explicitly extends Object, and fails when it doesn't.

The stack trace and the exception thrown varies depending on how the resolution happens.

The "solution" seems simple in that where there are no bounds then the equivalent of extends Object needs to be put in place, but I'm not familiar enough to know how or where this should be done best.

    @Test
    public void issue2044_typeVariableExtendsObject() {

        String x = "public class X <K extends Object> {\n" +
                "    private int getPartition(final K key) {\n" +
                "        int x = (new Object()).hashCode();\n" +
                "        return key.hashCode() / getHashes().length;\n" +
                "    }\n" +
                "}";

        doTestSimple(x); // passes
    }


    @Test
    public void issue2044_simpleTypeVariable() {
        String x = "public class X <K> {\n" +
                "    private int getPartition(final K key) {\n" +
                "        int x = (new Object()).hashCode();\n" +
                "        return key.hashCode() / getHashes().length;\n" +
                "    }\n" +
                "}";

        doTestSimple(x); // fails
    }

    private void doTestSimple(String x) {
        TypeSolver typeSolver = new CombinedTypeSolver(new ReflectionTypeSolver());
        ParserConfiguration configuration = new ParserConfiguration().setSymbolResolver(new JavaSymbolSolver(typeSolver));
        JavaParser javaParser = new JavaParser(configuration);

        ParseResult<CompilationUnit> result = javaParser.parse(ParseStart.COMPILATION_UNIT, provider(x));
        assumeTrue(result.isSuccessful());

        result.ifSuccessful(compilationUnit -> {
            List<MethodCallExpr> methodCallExprs = compilationUnit.findAll(MethodCallExpr.class);
            assertEquals(3, methodCallExprs.size());

            MethodCallExpr methodCallExpr = methodCallExprs.get(1);

            //// Exception-triggering method calls
            
            // throws RuntimeException - stack trace below
            methodCallExpr.calculateResolvedType(); 
/*
java.lang.RuntimeException: Method 'hashCode' cannot be resolved in context key.hashCode() (line: 3) MethodCallExprContext{wrapped=key.hashCode()}. Parameter types: []

	at com.github.javaparser.symbolsolver.javaparsermodel.JavaParserFacade.solveMethodAsUsage(JavaParserFacade.java:586)
	at com.github.javaparser.symbolsolver.javaparsermodel.TypeExtractor.visit(TypeExtractor.java:267)
	at com.github.javaparser.symbolsolver.javaparsermodel.TypeExtractor.visit(TypeExtractor.java:44)
	at com.github.javaparser.ast.expr.MethodCallExpr.accept(MethodCallExpr.java:115)
	at com.github.javaparser.symbolsolver.javaparsermodel.JavaParserFacade.getTypeConcrete(JavaParserFacade.java:447)
	at com.github.javaparser.symbolsolver.javaparsermodel.JavaParserFacade.getType(JavaParserFacade.java:310)
	at com.github.javaparser.symbolsolver.javaparsermodel.JavaParserFacade.getType(JavaParserFacade.java:292)
	at com.github.javaparser.symbolsolver.JavaSymbolSolver.calculateType(JavaSymbolSolver.java:250)
	at com.github.javaparser.ast.expr.Expression.calculateResolvedType(Expression.java:564)
	at com.github.javaparser.symbolsolver.Issue2044.lambda$null$0(Issue2044.java:81)
	at java.util.ArrayList.forEach(ArrayList.java:1249)
	at com.github.javaparser.symbolsolver.Issue2044.lambda$issue2044$1(Issue2044.java:49)
*/
                            
                            
            // throws UnsolvedSymbolException - stack trace below
            methodCallExpr.resolve(); 
/*
Unsolved symbol : We are unable to find the method declaration corresponding to key.hashCode()
UnsolvedSymbolException{context='null', name='We are unable to find the method declaration corresponding to key.hashCode()', cause='null'}

	at com.github.javaparser.symbolsolver.JavaSymbolSolver.resolveDeclaration(JavaSymbolSolver.java:146)
	at com.github.javaparser.ast.expr.MethodCallExpr.resolve(MethodCallExpr.java:313)
	at com.github.javaparser.symbolsolver.Issue2044.lambda$null$0(Issue2044.java:101)
*/

        });
    }

Note that this is where the bound-checking takes place - returning Optional.empty() when not explicitly extending Object.

private Optional<MethodUsage> solveMethodAsUsage(ResolvedTypeVariable tp, String name, List<ResolvedType> argumentsTypes, Context invokationContext) {
for (ResolvedTypeParameterDeclaration.Bound bound : tp.asTypeParameter().getBounds()) {
Optional<MethodUsage> methodUsage = solveMethodAsUsage(bound.getType(), name, argumentsTypes, invokationContext);
if (methodUsage.isPresent()) {
return methodUsage;
}
}
return Optional.empty();
}

@MysterAitch
Copy link
Member

Note that this is sufficient to make the tests above pass, but has wider implications re: generating code etc.

    public TypeParameter(TokenRange tokenRange, SimpleName name, NodeList<ClassOrInterfaceType> typeBound, NodeList<AnnotationExpr> annotations) {
        super(tokenRange, annotations);
        setName(name);
        if(typeBound.isEmpty()) {
            ClassOrInterfaceType object = new ClassOrInterfaceType();
            object.setName("Object");
            typeBound.add(object);
        }
        setTypeBound(typeBound);
        customInitialization();
    }

@Generated("com.github.javaparser.generator.core.node.MainConstructorGenerator")
public TypeParameter(TokenRange tokenRange, SimpleName name, NodeList<ClassOrInterfaceType> typeBound, NodeList<AnnotationExpr> annotations) {
super(tokenRange, annotations);
setName(name);
setTypeBound(typeBound);
customInitialization();
}

@ftomassetti
Copy link
Member

I agree that the symbol solver should figure out that K is at least an Object. I am surprised this is not the case already

MysterAitch added a commit to MysterAitch/javaparser that referenced this issue Nov 17, 2019
tophyr added a commit to tophyr/javaparser that referenced this issue Nov 19, 2019
…ct" when resolving TypeVariables with an empty bounds list
@matozoid matozoid added this to the next release milestone Dec 15, 2019
@matozoid matozoid modified the milestones: 3.15.8, 3.15.7 Dec 29, 2019
MysterAitch added a commit to MysterAitch/javaparser that referenced this issue Jan 23, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Question (JP usage) How to use JP to inspect/modify the AST - please close your question once answered! Symbol Solver
Projects
None yet
Development

No branches or pull requests

4 participants