Fix "unresolved widget fqname" bug in Gradle

This commit is contained in:
Yan Zhulanow
2015-09-17 13:10:10 +03:00
parent 07b77432de
commit b73f91d16b
15 changed files with 58 additions and 46 deletions
@@ -29,8 +29,7 @@ import com.intellij.mock.MockApplication
import com.intellij.openapi.Disposable import com.intellij.openapi.Disposable
import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.components.ServiceManager import com.intellij.openapi.components.ServiceManager
import com.intellij.openapi.extensions.Extensions import com.intellij.openapi.extensions.*
import com.intellij.openapi.extensions.ExtensionsArea
import com.intellij.openapi.fileTypes.FileTypeExtensionPoint import com.intellij.openapi.fileTypes.FileTypeExtensionPoint
import com.intellij.openapi.fileTypes.PlainTextFileType import com.intellij.openapi.fileTypes.PlainTextFileType
import com.intellij.openapi.project.Project import com.intellij.openapi.project.Project
@@ -18,11 +18,13 @@ package org.jetbrains.kotlin.android.synthetic
import com.intellij.mock.MockProject import com.intellij.mock.MockProject
import com.intellij.openapi.components.ServiceManager import com.intellij.openapi.components.ServiceManager
import com.intellij.openapi.extensions.Extensions
import com.intellij.openapi.project.Project import com.intellij.openapi.project.Project
import org.jetbrains.kotlin.analyzer.ModuleInfo import org.jetbrains.kotlin.analyzer.ModuleInfo
import org.jetbrains.kotlin.android.synthetic.codegen.AndroidExpressionCodegenExtension import org.jetbrains.kotlin.android.synthetic.codegen.AndroidExpressionCodegenExtension
import org.jetbrains.kotlin.android.synthetic.codegen.AndroidOnDestroyClassBuilderInterceptorExtension import org.jetbrains.kotlin.android.synthetic.codegen.AndroidOnDestroyClassBuilderInterceptorExtension
import org.jetbrains.kotlin.android.synthetic.diagnostic.AndroidExtensionPropertiesCallChecker import org.jetbrains.kotlin.android.synthetic.diagnostic.AndroidExtensionPropertiesCallChecker
import org.jetbrains.kotlin.android.synthetic.diagnostic.DefaultErrorMessagesAndroid
import org.jetbrains.kotlin.android.synthetic.res.AndroidLayoutXmlFileManager import org.jetbrains.kotlin.android.synthetic.res.AndroidLayoutXmlFileManager
import org.jetbrains.kotlin.android.synthetic.res.CliAndroidLayoutXmlFileManager import org.jetbrains.kotlin.android.synthetic.res.CliAndroidLayoutXmlFileManager
import org.jetbrains.kotlin.android.synthetic.res.CliSyntheticFileGenerator import org.jetbrains.kotlin.android.synthetic.res.CliSyntheticFileGenerator
@@ -37,6 +39,7 @@ import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.config.CompilerConfigurationKey import org.jetbrains.kotlin.config.CompilerConfigurationKey
import org.jetbrains.kotlin.container.StorageComponentContainer import org.jetbrains.kotlin.container.StorageComponentContainer
import org.jetbrains.kotlin.container.useInstance import org.jetbrains.kotlin.container.useInstance
import org.jetbrains.kotlin.diagnostics.rendering.DefaultErrorMessages
import org.jetbrains.kotlin.extensions.ExternalDeclarationsProvider import org.jetbrains.kotlin.extensions.ExternalDeclarationsProvider
import org.jetbrains.kotlin.extensions.StorageComponentContainerContributor import org.jetbrains.kotlin.extensions.StorageComponentContainerContributor
import org.jetbrains.kotlin.psi.JetFile import org.jetbrains.kotlin.psi.JetFile
@@ -75,7 +78,7 @@ public class AndroidCommandLineProcessor : CommandLineProcessor {
public class CliAndroidDeclarationsProvider(private val project: Project) : ExternalDeclarationsProvider { public class CliAndroidDeclarationsProvider(private val project: Project) : ExternalDeclarationsProvider {
override fun getExternalDeclarations(moduleInfo: ModuleInfo?): Collection<JetFile> { override fun getExternalDeclarations(moduleInfo: ModuleInfo?): Collection<JetFile> {
val parser = ServiceManager.getService(project, javaClass<SyntheticFileGenerator>()) as? CliSyntheticFileGenerator val parser = ServiceManager.getService(project, SyntheticFileGenerator::class.java) as? CliSyntheticFileGenerator
return parser?.getSyntheticFiles() ?: listOf() return parser?.getSyntheticFiles() ?: listOf()
} }
} }
@@ -89,12 +92,13 @@ public class AndroidComponentRegistrar : ComponentRegistrar {
if (androidResPath != null && androidManifest != null) { if (androidResPath != null && androidManifest != null) {
val xmlProcessor = CliSyntheticFileGenerator(project, androidManifest, androidResPath) val xmlProcessor = CliSyntheticFileGenerator(project, androidManifest, androidResPath)
project.registerService(javaClass<SyntheticFileGenerator>(), xmlProcessor) project.registerService(SyntheticFileGenerator::class.java, xmlProcessor)
project.registerService(javaClass<AndroidLayoutXmlFileManager>(), CliAndroidLayoutXmlFileManager(project, androidManifest, androidResPath)) project.registerService(AndroidLayoutXmlFileManager::class.java, CliAndroidLayoutXmlFileManager(project, androidManifest, androidResPath))
ExternalDeclarationsProvider.registerExtension(project, CliAndroidDeclarationsProvider(project)) ExternalDeclarationsProvider.registerExtension(project, CliAndroidDeclarationsProvider(project))
ExpressionCodegenExtension.registerExtension(project, AndroidExpressionCodegenExtension()) ExpressionCodegenExtension.registerExtension(project, AndroidExpressionCodegenExtension())
StorageComponentContainerContributor.registerExtension(project, AndroidExtensionPropertiesComponentContainerContributor()) StorageComponentContainerContributor.registerExtension(project, AndroidExtensionPropertiesComponentContainerContributor())
Extensions.getRootArea().getExtensionPoint(DefaultErrorMessages.Extension.EP_NAME).registerExtension(DefaultErrorMessagesAndroid())
ClassBuilderInterceptorExtension.registerExtension(project, AndroidOnDestroyClassBuilderInterceptorExtension()) ClassBuilderInterceptorExtension.registerExtension(project, AndroidOnDestroyClassBuilderInterceptorExtension())
} }
} }
@@ -17,9 +17,6 @@
package org.jetbrains.kotlin.android.synthetic package org.jetbrains.kotlin.android.synthetic
import com.intellij.openapi.util.Key import com.intellij.openapi.util.Key
import org.jetbrains.kotlin.android.synthetic.res.AndroidFragment
import org.jetbrains.kotlin.android.synthetic.res.AndroidResource
import org.jetbrains.kotlin.android.synthetic.res.AndroidWidget
import org.jetbrains.kotlin.lexer.JetKeywordToken import org.jetbrains.kotlin.lexer.JetKeywordToken
import org.jetbrains.kotlin.lexer.JetTokens import org.jetbrains.kotlin.lexer.JetTokens
@@ -53,8 +50,8 @@ public object AndroidConst {
val IGNORED_XML_WIDGET_TYPES = setOf("requestFocus", "merge", "tag", "check", "blink") val IGNORED_XML_WIDGET_TYPES = setOf("requestFocus", "merge", "tag", "check", "blink")
val ESCAPED_IDENTIFIERS = (JetTokens.KEYWORDS.getTypes() + JetTokens.SOFT_KEYWORDS.getTypes()) val ESCAPED_IDENTIFIERS = (JetTokens.KEYWORDS.types + JetTokens.SOFT_KEYWORDS.types)
.map { it as? JetKeywordToken }.filterNotNull().map { it.getValue() }.toSet() .map { it as? JetKeywordToken }.filterNotNull().map { it.value }.toSet()
val FQNAME_RESOLVE_PACKAGES = listOf("android.widget", "android.webkit", "android.view") val FQNAME_RESOLVE_PACKAGES = listOf("android.widget", "android.webkit", "android.view")
} }
@@ -74,16 +71,4 @@ public fun isWidgetTypeIgnored(xmlType: String): Boolean {
fun escapeAndroidIdentifier(id: String): String { fun escapeAndroidIdentifier(id: String): String {
return if (id in AndroidConst.ESCAPED_IDENTIFIERS) "`$id`" else id return if (id in AndroidConst.ESCAPED_IDENTIFIERS) "`$id`" else id
}
public fun parseAndroidResource(id: String, tag: String, fqNameResolver: (String) -> String?): AndroidResource {
return when (tag) {
"fragment" -> AndroidFragment(id)
"include" -> AndroidWidget(id, AndroidConst.VIEW_FQNAME)
else -> {
val fqName = fqNameResolver(tag)
val invalidType = if (fqName != null) null else tag
AndroidWidget(id, fqName ?: AndroidConst.VIEW_FQNAME, invalidType)
}
}
} }
@@ -16,7 +16,9 @@
package org.jetbrains.kotlin.android.synthetic.diagnostic package org.jetbrains.kotlin.android.synthetic.diagnostic
import com.intellij.openapi.project.Project
import org.jetbrains.kotlin.android.synthetic.AndroidConst import org.jetbrains.kotlin.android.synthetic.AndroidConst
import org.jetbrains.kotlin.android.synthetic.CliAndroidDeclarationsProvider
import org.jetbrains.kotlin.android.synthetic.res.SyntheticFileGenerator import org.jetbrains.kotlin.android.synthetic.res.SyntheticFileGenerator
import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.resolve.calls.checkers.CallChecker import org.jetbrains.kotlin.resolve.calls.checkers.CallChecker
@@ -24,6 +26,7 @@ import org.jetbrains.kotlin.resolve.calls.context.BasicCallResolutionContext
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
import org.jetbrains.kotlin.resolve.constants.StringValue import org.jetbrains.kotlin.resolve.constants.StringValue
import org.jetbrains.kotlin.android.synthetic.diagnostic.ErrorsAndroid.* import org.jetbrains.kotlin.android.synthetic.diagnostic.ErrorsAndroid.*
import org.jetbrains.kotlin.extensions.ExternalDeclarationsProvider
public class AndroidExtensionPropertiesCallChecker : CallChecker { public class AndroidExtensionPropertiesCallChecker : CallChecker {
override fun <F : CallableDescriptor> check(resolvedCall: ResolvedCall<F>, context: BasicCallResolutionContext) { override fun <F : CallableDescriptor> check(resolvedCall: ResolvedCall<F>, context: BasicCallResolutionContext) {
@@ -44,5 +47,4 @@ public class AndroidExtensionPropertiesCallChecker : CallChecker {
val warning = if (erroneousType.contains('.')) SYNTHETIC_UNRESOLVED_WIDGET_TYPE else SYNTHETIC_INVALID_WIDGET_TYPE val warning = if (erroneousType.contains('.')) SYNTHETIC_UNRESOLVED_WIDGET_TYPE else SYNTHETIC_INVALID_WIDGET_TYPE
context.trace.report(warning.on(expression, erroneousType)) context.trace.report(warning.on(expression, erroneousType))
} }
} }
@@ -29,8 +29,9 @@ public class DefaultErrorMessagesAndroid : DefaultErrorMessages.Extension {
MAP.put(ErrorsAndroid.SYNTHETIC_INVALID_WIDGET_TYPE, MAP.put(ErrorsAndroid.SYNTHETIC_INVALID_WIDGET_TYPE,
"Widget has an invalid type ''{0}''. Please specify the fully qualified widget class name in XML", "Widget has an invalid type ''{0}''. Please specify the fully qualified widget class name in XML",
Renderers.TO_STRING) Renderers.TO_STRING)
MAP.put(ErrorsAndroid.SYNTHETIC_UNRESOLVED_WIDGET_TYPE, MAP.put(ErrorsAndroid.SYNTHETIC_UNRESOLVED_WIDGET_TYPE,
"Widget has an unresolved type ''{0}'', and thus it was upcasted to ''android.view.View''.", "Widget has an unresolved type ''{0}'', and thus it was upcasted to ''android.view.View''",
Renderers.TO_STRING) Renderers.TO_STRING)
} }
} }
@@ -23,9 +23,8 @@ import com.intellij.psi.PsiFile
import java.io.ByteArrayInputStream import java.io.ByteArrayInputStream
import com.intellij.psi.impl.PsiElementFinderImpl import com.intellij.psi.impl.PsiElementFinderImpl
import com.intellij.psi.search.GlobalSearchScope import com.intellij.psi.search.GlobalSearchScope
import org.jetbrains.kotlin.android.synthetic.AndroidConst
import org.jetbrains.kotlin.android.synthetic.AndroidXmlHandler import org.jetbrains.kotlin.android.synthetic.AndroidXmlHandler
import org.jetbrains.kotlin.android.synthetic.parseAndroidResource
import org.jetbrains.kotlin.load.java.JavaClassFinder
import org.jetbrains.kotlin.psi.JetFile import org.jetbrains.kotlin.psi.JetFile
public open class CliSyntheticFileGenerator( public open class CliSyntheticFileGenerator(
@@ -78,6 +77,13 @@ public open class CliSyntheticFileGenerator(
return false return false
} }
override fun parseAndroidWidget(id: String, tag: String, fqNameResolver: (String) -> String?): AndroidResource {
val fqName = fqNameResolver(tag)
val invalidType = if (fqName != null) null else tag
val type = fqName ?: (if ('.' in tag) tag else AndroidConst.VIEW_FQNAME)
return AndroidWidget(id, type, invalidType)
}
private companion object { private companion object {
private val LOG: Logger = Logger.getInstance(javaClass) private val LOG: Logger = Logger.getInstance(javaClass)
} }
@@ -17,7 +17,6 @@
package org.jetbrains.kotlin.android.synthetic.res package org.jetbrains.kotlin.android.synthetic.res
import com.intellij.openapi.project.Project import com.intellij.openapi.project.Project
import com.intellij.psi.PsiClass
import com.intellij.psi.PsiFile import com.intellij.psi.PsiFile
import com.intellij.psi.PsiManager import com.intellij.psi.PsiManager
import com.intellij.psi.util.CachedValue import com.intellij.psi.util.CachedValue
@@ -123,7 +122,7 @@ public abstract class SyntheticFileGenerator(protected val project: Project) {
private fun KotlinStringWriter.writeSyntheticProperty(receiver: String, resource: AndroidResource, stubCall: String) { private fun KotlinStringWriter.writeSyntheticProperty(receiver: String, resource: AndroidResource, stubCall: String) {
val className = if (isFromSupportV4Package(receiver)) resource.supportClassName else resource.className val className = if (isFromSupportV4Package(receiver)) resource.supportClassName else resource.className
val cast = if (resource.className != "View") " as? $className" else "" val cast = if (resource.className != AndroidConst.VIEW_FQNAME) " as? $className" else ""
val body = arrayListOf("return $stubCall$cast") val body = arrayListOf("return $stubCall$cast")
// Annotation on wrong widget type // Annotation on wrong widget type
@@ -165,6 +164,16 @@ public abstract class SyntheticFileGenerator(protected val project: Project) {
return null return null
} }
protected fun parseAndroidResource(id: String, tag: String, fqNameResolver: (String) -> String?): AndroidResource {
return when (tag) {
"fragment" -> AndroidFragment(id)
"include" -> AndroidWidget(id, AndroidConst.VIEW_FQNAME)
else -> parseAndroidWidget(id, tag, fqNameResolver)
}
}
protected abstract fun parseAndroidWidget(id: String, tag: String, fqNameResolver: (String) -> String?): AndroidResource
protected fun supportV4Available(): Boolean = checkIfClassExist(AndroidConst.SUPPORT_FRAGMENT_FQNAME) protected fun supportV4Available(): Boolean = checkIfClassExist(AndroidConst.SUPPORT_FRAGMENT_FQNAME)
protected fun filterDuplicates(resources: List<AndroidResource>): List<AndroidResource> { protected fun filterDuplicates(resources: List<AndroidResource>): List<AndroidResource> {
@@ -3,8 +3,8 @@ package kotlinx.android.synthetic.test
import kotlin.internal.flexible.ft import kotlin.internal.flexible.ft
val android.app.Activity.button: ft<android.view.View, android.view.View?> val android.app.Activity.button: ft<android.view.View, android.view.View?>
get() = findViewById(0) as? android.view.View get() = findViewById(0)
val android.app.Fragment.button: ft<android.view.View, android.view.View?> val android.app.Fragment.button: ft<android.view.View, android.view.View?>
get() = getView().findViewById(0) as? android.view.View get() = getView().findViewById(0)
@@ -3,10 +3,10 @@ package kotlinx.android.synthetic.test
import kotlin.internal.flexible.ft import kotlin.internal.flexible.ft
val android.app.Activity.textView1: ft<android.view.View, android.view.View?> val android.app.Activity.textView1: ft<android.view.View, android.view.View?>
get() = findViewById(0) as? android.view.View get() = findViewById(0)
val android.app.Fragment.textView1: ft<android.view.View, android.view.View?> val android.app.Fragment.textView1: ft<android.view.View, android.view.View?>
get() = getView().findViewById(0) as? android.view.View get() = getView().findViewById(0)
val android.app.Activity.textView2: ft<android.widget.TextView, android.widget.TextView?> val android.app.Activity.textView2: ft<android.widget.TextView, android.widget.TextView?>
get() = findViewById(0) as? android.widget.TextView get() = findViewById(0) as? android.widget.TextView
@@ -3,10 +3,10 @@ package kotlinx.android.synthetic.test
import kotlin.internal.flexible.ft import kotlin.internal.flexible.ft
val android.app.Activity.button: ft<android.view.View, android.view.View?> val android.app.Activity.button: ft<android.view.View, android.view.View?>
get() = findViewById(0) as? android.view.View get() = findViewById(0)
val android.app.Fragment.button: ft<android.view.View, android.view.View?> val android.app.Fragment.button: ft<android.view.View, android.view.View?>
get() = getView().findViewById(0) as? android.view.View get() = getView().findViewById(0)
val android.app.Activity.button2: ft<android.widget.Button, android.widget.Button?> val android.app.Activity.button2: ft<android.widget.Button, android.widget.Button?>
get() = findViewById(0) as? android.widget.Button get() = findViewById(0) as? android.widget.Button
@@ -3,10 +3,10 @@ package kotlinx.android.synthetic.test
import kotlin.internal.flexible.ft import kotlin.internal.flexible.ft
val android.app.Activity.includeTag: ft<android.view.View, android.view.View?> val android.app.Activity.includeTag: ft<android.view.View, android.view.View?>
get() = findViewById(0) as? android.view.View get() = findViewById(0)
val android.app.Fragment.includeTag: ft<android.view.View, android.view.View?> val android.app.Fragment.includeTag: ft<android.view.View, android.view.View?>
get() = getView().findViewById(0) as? android.view.View get() = getView().findViewById(0)
val android.app.Activity.fragmentTag: ft<android.app.Fragment, android.app.Fragment?> val android.app.Activity.fragmentTag: ft<android.app.Fragment, android.app.Fragment?>
get() = getFragmentManager().findFragmentById(0) as? android.app.Fragment get() = getFragmentManager().findFragmentById(0) as? android.app.Fragment
@@ -3,13 +3,13 @@ package kotlinx.android.synthetic.test
import kotlin.internal.flexible.ft import kotlin.internal.flexible.ft
val android.app.Activity.includeTag: ft<android.view.View, android.view.View?> val android.app.Activity.includeTag: ft<android.view.View, android.view.View?>
get() = findViewById(0) as? android.view.View get() = findViewById(0)
val android.app.Fragment.includeTag: ft<android.view.View, android.view.View?> val android.app.Fragment.includeTag: ft<android.view.View, android.view.View?>
get() = getView().findViewById(0) as? android.view.View get() = getView().findViewById(0)
val android.support.v4.app.Fragment.includeTag: ft<android.view.View, android.view.View?> val android.support.v4.app.Fragment.includeTag: ft<android.view.View, android.view.View?>
get() = getView().findViewById(0) as? android.view.View get() = getView().findViewById(0)
val android.app.Activity.fragmentTag: ft<android.app.Fragment, android.app.Fragment?> val android.app.Activity.fragmentTag: ft<android.app.Fragment, android.app.Fragment?>
get() = getFragmentManager().findFragmentById(0) as? android.app.Fragment get() = getFragmentManager().findFragmentById(0) as? android.app.Fragment
@@ -3,10 +3,10 @@ package kotlinx.android.synthetic.test
import kotlin.internal.flexible.ft import kotlin.internal.flexible.ft
@kotlin.internal.flexible.InvalidWidgetType("a.b.c") @kotlin.internal.flexible.InvalidWidgetType("a.b.c")
val android.app.Activity.MyView: ft<android.view.View, android.view.View?> val android.app.Activity.MyView: ft<a.b.c, a.b.c?>
get() = findViewById(0) as? android.view.View get() = findViewById(0) as? a.b.c
@kotlin.internal.flexible.InvalidWidgetType("a.b.c") @kotlin.internal.flexible.InvalidWidgetType("a.b.c")
val android.app.Fragment.MyView: ft<android.view.View, android.view.View?> val android.app.Fragment.MyView: ft<a.b.c, a.b.c?>
get() = getView().findViewById(0) as? android.view.View get() = getView().findViewById(0) as? a.b.c
@@ -4,9 +4,9 @@ import kotlin.internal.flexible.ft
@kotlin.internal.flexible.InvalidWidgetType("KeyboardView") @kotlin.internal.flexible.InvalidWidgetType("KeyboardView")
val android.app.Activity.MyKeyboardView: ft<android.view.View, android.view.View?> val android.app.Activity.MyKeyboardView: ft<android.view.View, android.view.View?>
get() = findViewById(0) as? android.view.View get() = findViewById(0)
@kotlin.internal.flexible.InvalidWidgetType("KeyboardView") @kotlin.internal.flexible.InvalidWidgetType("KeyboardView")
val android.app.Fragment.MyKeyboardView: ft<android.view.View, android.view.View?> val android.app.Fragment.MyKeyboardView: ft<android.view.View, android.view.View?>
get() = getView().findViewById(0) as? android.view.View get() = getView().findViewById(0)
@@ -25,8 +25,8 @@ import com.intellij.psi.util.CachedValueProvider.Result
import org.jetbrains.kotlin.android.synthetic.AndroidConst import org.jetbrains.kotlin.android.synthetic.AndroidConst
import org.jetbrains.kotlin.android.synthetic.idea.AndroidPsiTreeChangePreprocessor import org.jetbrains.kotlin.android.synthetic.idea.AndroidPsiTreeChangePreprocessor
import org.jetbrains.kotlin.android.synthetic.idea.AndroidXmlVisitor import org.jetbrains.kotlin.android.synthetic.idea.AndroidXmlVisitor
import org.jetbrains.kotlin.android.synthetic.parseAndroidResource
import org.jetbrains.kotlin.android.synthetic.res.AndroidResource import org.jetbrains.kotlin.android.synthetic.res.AndroidResource
import org.jetbrains.kotlin.android.synthetic.res.AndroidWidget
import org.jetbrains.kotlin.android.synthetic.res.SyntheticFileGenerator import org.jetbrains.kotlin.android.synthetic.res.SyntheticFileGenerator
import org.jetbrains.kotlin.psi.JetFile import org.jetbrains.kotlin.psi.JetFile
@@ -62,6 +62,12 @@ class IDESyntheticFileGenerator(val module: Module) : SyntheticFileGenerator(mod
return filterDuplicates(widgets) return filterDuplicates(widgets)
} }
override fun parseAndroidWidget(id: String, tag: String, fqNameResolver: (String) -> String?): AndroidResource {
val fqName = fqNameResolver(tag)
val invalidType = if (fqName != null) null else tag
return AndroidWidget(id, fqName ?: AndroidConst.VIEW_FQNAME, invalidType)
}
override fun checkIfClassExist(fqName: String): Boolean { override fun checkIfClassExist(fqName: String): Boolean {
val moduleScope = module.getModuleWithDependenciesAndLibrariesScope(false) val moduleScope = module.getModuleWithDependenciesAndLibrariesScope(false)
return JavaPsiFacade.getInstance(module.project).findClass(fqName, moduleScope) != null return JavaPsiFacade.getInstance(module.project).findClass(fqName, moduleScope) != null