From a171874b2f9efc19c2bda05dbba0621af32e84f8 Mon Sep 17 00:00:00 2001 From: Yan Zhulanow Date: Fri, 12 Jan 2018 15:45:41 +0900 Subject: [PATCH] Fix "Illegal Android Identifier" inspection reports non-instrumentation unit tests (KT-22168) --- .../inspection/IllegalIdentifierInspection.kt | 65 +++++++++++++++++-- 1 file changed, 61 insertions(+), 4 deletions(-) diff --git a/idea/idea-android/src/org/jetbrains/kotlin/android/inspection/IllegalIdentifierInspection.kt b/idea/idea-android/src/org/jetbrains/kotlin/android/inspection/IllegalIdentifierInspection.kt index 03b120903f3..cdc585590ab 100644 --- a/idea/idea-android/src/org/jetbrains/kotlin/android/inspection/IllegalIdentifierInspection.kt +++ b/idea/idea-android/src/org/jetbrains/kotlin/android/inspection/IllegalIdentifierInspection.kt @@ -16,20 +16,36 @@ package org.jetbrains.kotlin.android.inspection +import com.android.builder.model.AndroidProject.ARTIFACT_UNIT_TEST +import com.android.tools.idea.AndroidPsiUtils +import com.android.tools.idea.gradle.project.model.AndroidModuleModel import com.intellij.codeInspection.LocalInspectionToolSession import com.intellij.codeInspection.ProblemHighlightType import com.intellij.codeInspection.ProblemsHolder import com.intellij.openapi.application.ApplicationManager +import com.intellij.openapi.module.Module +import com.intellij.openapi.roots.ProjectRootModificationTracker +import com.intellij.openapi.util.Key +import com.intellij.openapi.util.SystemInfo +import com.intellij.openapi.util.text.StringUtil +import com.intellij.openapi.vfs.VirtualFile import com.intellij.psi.PsiElement import com.intellij.psi.PsiElementVisitor +import com.intellij.psi.util.CachedValuesManager +import org.jetbrains.android.facet.AndroidFacet import org.jetbrains.kotlin.android.getAndroidFacetForFile import org.jetbrains.kotlin.idea.inspections.AbstractKotlinInspection import org.jetbrains.kotlin.idea.quickfix.RenameIdentifierFix import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.psi.KtPsiUtil import org.jetbrains.kotlin.psi.KtVisitorVoid +import java.io.File class IllegalIdentifierInspection : AbstractKotlinInspection() { + private class JunitPaths(val paths: List, val generationId: Long) { + companion object : Key("AndroidModuleJunitPaths") + } + override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean, session: LocalInspectionToolSession): PsiElementVisitor { return object : KtVisitorVoid() { override fun visitElement(element: PsiElement) { @@ -44,13 +60,37 @@ class IllegalIdentifierInspection : AbstractKotlinInspection() { if (unquotedName.isEmpty()) return if (!unquotedName.all { isValidDalvikCharacter(it) } && checkAndroidFacet(element)) { - holder.registerProblem(element, - "Identifier not allowed in Android projects", - ProblemHighlightType.GENERIC_ERROR, - RenameIdentifierFix()) + if (element.isInUnitTests()) { + return + } + + holder.registerProblem( + element, + "Identifier not allowed in Android projects", + ProblemHighlightType.GENERIC_ERROR, + RenameIdentifierFix() + ) } } + private fun PsiElement.isInUnitTests(): Boolean { + val containingFile = containingFile?.virtualFile?.let { getIoFile(it) } + val module = AndroidPsiUtils.getModuleSafely(this) + + if (module != null && containingFile != null) { + val currentGenerationId = ProjectRootModificationTracker.getInstance(module.project).modificationCount + val junitTestPaths = module.getUserData(JunitPaths) + ?.takeIf { it.generationId == currentGenerationId } + ?: JunitPaths(getJunitTestPaths(module), currentGenerationId).also { module.putUserData(JunitPaths, it) } + + if (junitTestPaths.paths.any { containingFile.startsWith(it) }) { + return true + } + } + + return false + } + private fun checkAndroidFacet(element: PsiElement): Boolean { return element.getAndroidFacetForFile() != null || ApplicationManager.getApplication().isUnitTestMode } @@ -70,4 +110,21 @@ class IllegalIdentifierInspection : AbstractKotlinInspection() { } } + private fun getJunitTestPaths(module: Module): List { + val androidFacet = AndroidFacet.getInstance(module) ?: return emptyList() + val androidModuleModel = AndroidModuleModel.get(androidFacet) ?: return emptyList() + + return androidModuleModel.getTestSourceProviders(ARTIFACT_UNIT_TEST).flatMap { it.javaDirectories } + } + + private fun getIoFile(virtualFile: VirtualFile): File? { + var path = virtualFile.path + + // Taken from LocalFileSystemBase.convertToIOFile + if (StringUtil.endsWithChar(path, ':') && path.length == 2 && SystemInfo.isWindows) { + path += "/" + } + + return File(path).takeIf { it.exists() } + } } \ No newline at end of file