With Kotlin 2.2.0, in a `FirDeclarationGenerationE...
# compiler
h
With Kotlin 2.2.0, in a
FirDeclarationGenerationExtension
, I call
declarationSymbols
inside
generateFunctions
. Now this call is marked with
DirectDeclarationsAccess
. What is the alternative, beside optin (like the parcelize plugin does that I use as reference)?
d
If you are inspecting the declarations of a class for which you are generating declarations use
context.declaredScope
. In other cases it's better to use `classSymbol.declaredMemberScope()`/`classSymbol.unsubstitutedScope()` (unless it won't cause loop with a plugin)
h
I have the same use-case like the serialization/parcelize plugin: I want to generate a function to a class but only if the user did not write the same function manually. So in this case, I should use
context.declaredScope
?
d
Yep
h
Another question, why is the declarationScope optional, when using
MemberGenerationContext
?
d
It could be
null
in case of classes generated by plugins. These classes don't contain "real" declared scope, only generated one
h
Does the declaredScope also include "real" declared functions from super types?
d
No, declared scope contains only members declared directly inside the class. To get members of supertypes you need to take declared scopes of supertype classes.
h
Like you did here: https://github.com/JetBrains/kotlin/blob/382193f440412cb87427d2cfe0a4e8ab2c38d1a8/plugins/parcelize/parcelize-compiler/parcelize.k2/src/org/jetbrains/kotlin/parcelize/fir/FirParcelizeDeclarationGenerator.kt#L60
Copy code
val hasContentTypeFunction = context.declaredScope?.hasContentTypeFunction() == true || lookupSuperTypes(
                owner,
                lookupInterfaces = true,
                deep = true,
                session,
            ).any {
                it.fullyExpandedType(session).toRegularClassSymbol(session)?.hasContentTypeFunction() ?: false
            }
d
The most useful scope is a
FirUseSiteMemberScope
(`classSymbol.unsubsitutedScope()`/`type.useSiteScope()`), which consists of declared scope (both "real" and plugin-generated) and supertype scopes. This scope correctly matches overrides between members of the class and supertypes, but for that it's needed to know all declarations from the declared scope. So if you try to use it while generating functions for the class you will get a recursion: • to build function you need use-site scope • to operate use-site scope need to access declared scope • declared scope should ask plugins to generate functions That's the root of all of this inconvenience
thank you color 1
Like you did here:
Yep, it should do the trick
thank you color 1
h
Thank you for the help, but I still notice, the trick does use
FirRegularClassSymbol.declarationSymbols
in the superTypeLookup after calling
toRegularClassSymbol
. Should I still call
declarationSymbols
because they are resolved anyway?
d
I just remembered why we introduced `@DirectDeclarationsAccess`: to fix all of its usages in checkers. It's actually safe to call it during declarations generation
Like one of main dangerous consequence of using
declarationSymbols
instead of
declaredScope()
is the fact that the scope contains declarations from the plugins and the
declarationSymbols
doesn't, so it's easy to miss some declarations. But in your case you are explicitly want to check only declarations without plugin
h
Yeah, thank you very much! BTW you already use context parameters in the compiler. Maybe you could use context parameters too to scope/block unwanted access, like allow all variables with
@DirectDeclarationsAccess
from declarations generators (or a marker interface) only 🤔
👍 1
d
Yeah, we are considering this approach for the stable API layer over the FIR