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

nested classes in java sources cannot be resolved #105

Open
yigit opened this issue Jan 21, 2021 · 12 comments
Open

nested classes in java sources cannot be resolved #105

yigit opened this issue Jan 21, 2021 · 12 comments

Comments

@yigit
Copy link
Contributor

yigit commented Jan 21, 2021

looks like i broke something here:

0a0af2b

for some reason, KSP fails to resolve a nested class's type when it is in sources.
repro:
yigit@2e35c09

I initially thought this was a KSP problem but cannot reproduce it neither there nor in a test project.
google/ksp#263

@yigit
Copy link
Contributor Author

yigit commented Jan 21, 2021

btw i'm sure it is because of that change because reverting it fixes the test.

@yigit
Copy link
Contributor Author

yigit commented Jan 22, 2021

i put more investigation notes into google/ksp#263 (comment).

This is about java files' location not matching the project structure.
Seems like java compilation works fine but kotlin does not allow you to access java files if they are misplaced (package not matching file location wrt source root).

As far as I can see, we don't set source root, instead we send files to kotlin compile / java compile.
Maybe we need to change that in kotlin compile testing, i'm not sure yet.

@yigit
Copy link
Contributor Author

yigit commented Jan 22, 2021

so it works fine if i pass the sources folder into the kotlin compiler as freeArgs instead of the actual files.
in theory this should reproduce without KSP, i'll try to see if i can reproduce that.

@yigit
Copy link
Contributor Author

yigit commented Jan 22, 2021

here is a repro w/o KSP:
yigit@6267f67

i'm afraid we may need an API change to specify java file locations or allow paths in names.

@yigit
Copy link
Contributor Author

yigit commented Jan 22, 2021

nevermind, repro had bad code :(

@yigit
Copy link
Contributor Author

yigit commented Jan 22, 2021

seems like this is happening because we give workingdir/ksp as the projectBaseDir to KSP which means the java file is outside the project.
i'm not yet sure what the proper solution is but at least i can workaround it in room by using SourceFile.fromPath and building the project structure myself in tests.

@yigit
Copy link
Contributor Author

yigit commented Jan 22, 2021

Finally figured it out (i think :) )
KCT is not setting java source roots directory, which is why KSP cannot find java files properly.

https://github.com/JetBrains/kotlin/blob/master/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/arguments/K2JVMCompilerArguments.kt#L236

I can "fix" that by giving the sources directory but it is not necessarily correct for source files that are created from other existing files:

https://github.com/tschuchortdev/kotlin-compile-testing/compare/master...yigit:find-nested-class-repro?expand=1

return compileKotlin(sources, K2JVMCompiler(), commonK2JVMArgs().also {
			it.javaSourceRoots = arrayOf(sourcesDir.canonicalPath)
		})

Now the problem in the KCT side is that we allow passing arbitrary java files so their root directory does not really exist.
@tschuchortdev what do you think?

@tschuchortdev
Copy link
Owner

If we set javaSourceRoots to sourcesDir.canonicalPath then I suppose any sources outside of that directory wouldn't be found. Using source files from the host project's resources directory instead of creating them inline is a valid use case, so that's problematic. We could also copy all source files into the sourcesDir but that may lead to naming collisions, thus the file names would have to be changed to be unique. Of course this makes compiler errors involving those files nonsensical, so we also need to keep a mapping between the original file path and unique file name and replace all occurrences in the output message stream. Very messy.

Is it not possible to add every Java file as a separate source root like we are doing here?

I wonder why this javaSourceRoots option is needed at all when it seems to work fine without, for the most part.

copybara-service bot pushed a commit to androidx/androidx that referenced this issue Jan 26, 2021
This version includes a fix where java sources were not
provided to KSP, which means getSymbolsAnnotatedWith didn't
work.

Unfortunately, this update hit a bug in KSP/KCT (not sure yet) where KSP
cannot find java sources. After a long investigation (see github
issues), I figured it is the lack of javaSourceRoots argument being
passed into KotlinCompilation that confuses KSP (but regular kotlin
compilation works fine). KCT does not set it and cannot easily set it
since its public API receives any file as an argument (so it cannot
calculate a root). For this reason, now Room's abstraction passes that
argument directly.

As part of that change, I also changed how SourceFile's are converted.
Now we always build a "proper" folder structure before calling KCT. I've
refactored all 3 places where we use KCT to use this common path.

tschuchortdev/kotlin-compile-testing#105
google/ksp#263

Test: existing tests
Change-Id: Ibd0d6fc46849c2a0489b2bb632a2c19862e31c9e
@tschuchortdev
Copy link
Owner

tschuchortdev commented Jan 26, 2021

Is it not possible to add every Java file as a separate source root like we are doing here?

Apparently, this doesn't work. But I haven't figured out yet why.

This is weird. javaSourceRoots and putting paths into freeArgs seems to result in basically the same code path: https://sourcegraph.com/github.com/JetBrains/kotlin@a4dd47daab6ded30ffcc19d5be61b50a256615b6/-/blob/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/K2JVMCompiler.kt#L186

@tschuchortdev
Copy link
Owner

By the way, I've talked to someone in the compiler team about this who had this to say:

[...] accepting single .java files in the compiler was implemented as a kind of a hack, where we are running the Java lexer manually beforehand and are recording fully qualified names of all classes that we find:
https://github.com/JetBrains/kotlin/blob/master/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/index/SingleJavaFileRootsIndex.kt
IIRC running the Java parser was not feasible at this point because of performance reasons.
There might very well be some bug in that code, that doesn’t catch some nested classes in some cases.

With this in mind, I think it would be best to change the API to no longer accept paths to Java files and only to Java source roots.

@yigit
Copy link
Contributor Author

yigit commented Feb 16, 2021

FYI this is what we do in room:
https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:room/compiler-processing-testing/src/main/java/androidx/room/compiler/processing/util/Source.kt;l=1?q=Source.kt&sq=&ss=androidx%2Fplatform%2Fframeworks%2Fsupport

For java, it expects a qualified name and content;
For kotlin, it expects a file name and content.

In room, we actually write those files into proper places wrt their qualified names to avoid this issue. (and we can find the correct place from the qName).

@rossbacher
Copy link

rossbacher commented Jun 29, 2021

FYI this is what we do in room:
https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:room/compiler-processing-testing/src/main/java/androidx/room/compiler/processing/util/Source.kt;l=1?q=Source.kt&sq=&ss=androidx%2Fplatform%2Fframeworks%2Fsupport

For java, it expects a qualified name and content;
For kotlin, it expects a file name and content.

In room, we actually write those files into proper places wrt their qualified names to avoid this issue. (and we can find the correct place from the qName).

We tried a workaround like this for DeepLinkDispatch (https://github.com/airbnb/DeepLinkDispatch/blob/supportKsp/deeplinkdispatch-processor/src/test/java/com/airbnb/deeplinkdispatch/test/Source.kt) to be able to run this test(https://github.com/airbnb/DeepLinkDispatch/blob/supportKsp/deeplinkdispatch-processor/src/test/java/com/airbnb/deeplinkdispatch/DeepLinkProcessorIncrementalTest.kt#L388) without the NPE on getting the type of the inner class and it still fails exactly the same way. Just FYI.

rossbacher added a commit to airbnb/DeepLinkDispatch that referenced this issue Jun 29, 2021
* Rename .java to .kt

* Convert processor ot Kotlin

* Rename .java to .kt

* Convert all processor tests to Kotlin and use com.github.tschuchortdev:kotlin-compile-testing for testing.
Moved all tests over and make them more meaningful by testing what ends up in the index instad of just 1:1 ow which code they generate) which with the index bing binary is unverifiable.

* Cleanup

* Kotlin conversion cleanup and segmentation.

* First step of ksp conversion

* First step of ksp conversion

* Fix delegate and index generation

* Cleanup

* Moved all tests to mockk
Removed old unused dependencies
Check generated source on top of functionality
Split method and class elements into two sets for processing
Fixed index generation for inner classes
Added processor test with inner class
Enabled ksp for sample project

* Fix originating elements for Delegate generation.
Fix originalting elment for Registry generation.
Made unit test when running via ksp not to compiled code check.

* Use | for separator of options

* Fix argument and disable kapt plugin

* Remove no unused jcenter
Fix warning
Updated gradle to 7.1

* Add Kapt test library to show setup for kapt.
Clean up gralde plugin dependency for ksp.
Updated documentation.

* Use public version of xProcessorVersion
Remove airbnb repo dependency

* Add ktlint plugin and clean up the processor.
Also add ktlint to ci checks.

* Better error message match.

* When running ksp allow the incremental flag to not be set and still get the custom processors from config.
Use testKsp to test that behavior.

* Cleanup.

* Typos

* Cleanup and Pr comments.

* Rename .java to .kt

* Made DeepLinkAnnotatedElement a sealed class
Adde Kotlin source base tests.
Cleanup/fixes

* Enable failing tests

* Kotlin conversion

* Make sure to generate java source files in correct package like it is done for room (mentioned here tschuchortdev/kotlin-compile-testing#105 (comment)) but this also does not work.

Disabled ksp test for inner classes in java because of tschuchortdev/kotlin-compile-testing#105

* Add missing import
Add Kotlin based module

* Add note about Kotlin source files when using KSP.

* Remove note about kapt/ksp coexistance.

Co-authored-by: Andreas Rossbacher <andreas.rossbacher@airbnb.com>
rauljurado620 added a commit to rauljurado620/Deep-Dispatch that referenced this issue Mar 24, 2022
* Rename .java to .kt

* Convert processor ot Kotlin

* Rename .java to .kt

* Convert all processor tests to Kotlin and use com.github.tschuchortdev:kotlin-compile-testing for testing.
Moved all tests over and make them more meaningful by testing what ends up in the index instad of just 1:1 ow which code they generate) which with the index bing binary is unverifiable.

* Cleanup

* Kotlin conversion cleanup and segmentation.

* First step of ksp conversion

* First step of ksp conversion

* Fix delegate and index generation

* Cleanup

* Moved all tests to mockk
Removed old unused dependencies
Check generated source on top of functionality
Split method and class elements into two sets for processing
Fixed index generation for inner classes
Added processor test with inner class
Enabled ksp for sample project

* Fix originating elements for Delegate generation.
Fix originalting elment for Registry generation.
Made unit test when running via ksp not to compiled code check.

* Use | for separator of options

* Fix argument and disable kapt plugin

* Remove no unused jcenter
Fix warning
Updated gradle to 7.1

* Add Kapt test library to show setup for kapt.
Clean up gralde plugin dependency for ksp.
Updated documentation.

* Use public version of xProcessorVersion
Remove airbnb repo dependency

* Add ktlint plugin and clean up the processor.
Also add ktlint to ci checks.

* Better error message match.

* When running ksp allow the incremental flag to not be set and still get the custom processors from config.
Use testKsp to test that behavior.

* Cleanup.

* Typos

* Cleanup and Pr comments.

* Rename .java to .kt

* Made DeepLinkAnnotatedElement a sealed class
Adde Kotlin source base tests.
Cleanup/fixes

* Enable failing tests

* Kotlin conversion

* Make sure to generate java source files in correct package like it is done for room (mentioned here tschuchortdev/kotlin-compile-testing#105 (comment)) but this also does not work.

Disabled ksp test for inner classes in java because of tschuchortdev/kotlin-compile-testing#105

* Add missing import
Add Kotlin based module

* Add note about Kotlin source files when using KSP.

* Remove note about kapt/ksp coexistance.

Co-authored-by: Andreas Rossbacher <andreas.rossbacher@airbnb.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants