For such a simple diagnostic i would probably write relatively simple type inference code just to match for Boolean type.
However, do you know, how it is better to get resolved type during the compilation?
I am not familiar with detekt, if it bootstraps the whole kotlin compiler then i would use provided IR, otherwise, the only better way i now is to write a compiler plugin using IR, which contains comprehensive information about all types.
Plugin code would look like this:
val KEY_ENABLED = CompilerConfigurationKey<Boolean>("whether the plugin is enabled")
class MyComponentRegistrar : ComponentRegistrar {
override fun registerProjectComponents(
project: MockProject,
configuration: CompilerConfiguration
) {
if (configuration[KEY_ENABLED] == false) return
IrGenerationExtension.registerExtension(project, MyExtension())
}
}
class MyExtension : IrGenerationExtension {
override fun generate(moduleFragment: IrModuleFragment, pluginContext: IrPluginContext) {
val classes = moduleFragment.files
.flatMap { it.declarations }
.filterIsInstance<IrClass>()
for (clazz in classes) {
for (property in clazz.properties) {
val type = property.getter?.returnType ?: continue
val namedAsBoolProperty = property.name.asString().let {
it.startsWith("is") && (it.getOrNull(3)?.isUpperCase() == true)
}
if (namedAsBoolProperty && !type.isBoolean()) {
reportCodeSmell()
}
}
}
}
}
Dependency used: org.jetbrains.kotlin:kotlin-compiler-embeddable
.
If the plugin is not an option, but you need accurate information about the types, then perhaps it makes sense to bootstrap the compiler by yourself.
Here is an example code, however I don’t think it will work, since the API is very unstable and changes even in minor versions of kotlinc:
val psi = ktParser.parse(code)
class FirJvmModuleInfo : ModuleInfo {
override val name = Name.special("<dependencies>")
val dependencies: MutableList<ModuleInfo> = mutableListOf()
override val platform get() = JvmPlatforms.jvm18
override val analyzerServices get() = JvmPlatformAnalyzerServices
override fun dependencies(): List<ModuleInfo> = dependencies
}
val moduleInfo = FirJvmModuleInfo()
val scope = GlobalSearchScope.filesScope(project, listOf(psi.virtualFile))
.uniteWith(TopDownAnalyzerFacadeForJVM.AllJavaSourcesInProjectScope(project))
val firSession: FirSession = FirJavaModuleBasedSession(moduleInfo, provider, scope).also {
val dependenciesInfo = FirJvmModuleInfo()
moduleInfo.dependencies.add(dependenciesInfo)
val librariesScope = ProjectScope.getLibrariesScope(project)
FirLibrarySession.create(
dependenciesInfo, provider, librariesScope,
project, env.createPackagePartProvider(librariesScope)
)
}
val firFile = RawFirBuilder(firSession, stubMode = false).buildFirFile(psi)
firFile.runResolve(toPhase = FirResolvePhase.TYPES)