From 56d56ab842d7eac38475439d62e12d448da037ad Mon Sep 17 00:00:00 2001 From: Nikolay Krasko Date: Fri, 25 Jan 2019 19:57:26 +0300 Subject: [PATCH] Make package name inspection global (KT-29549) Report same package only once. #KT-29549 Fixed --- .idea/inspectionProfiles/idea_default.xml | 1 + idea/resources/META-INF/plugin-common.xml | 2 +- .../NamingConventionInspections.kt | 165 ++++++++++++++---- .../kotlin/idea/util/compat/refElements.kt | 14 ++ .../idea/util/compat/refElements.kt.182 | 14 ++ 5 files changed, 165 insertions(+), 31 deletions(-) create mode 100644 idea/src/org/jetbrains/kotlin/idea/util/compat/refElements.kt create mode 100644 idea/src/org/jetbrains/kotlin/idea/util/compat/refElements.kt.182 diff --git a/.idea/inspectionProfiles/idea_default.xml b/.idea/inspectionProfiles/idea_default.xml index c76db8aa8e3..334e052ec9c 100644 --- a/.idea/inspectionProfiles/idea_default.xml +++ b/.idea/inspectionProfiles/idea_default.xml @@ -117,6 +117,7 @@ + diff --git a/idea/resources/META-INF/plugin-common.xml b/idea/resources/META-INF/plugin-common.xml index bc772b6624a..2d1cf735aea 100644 --- a/idea/resources/META-INF/plugin-common.xml +++ b/idea/resources/META-INF/plugin-common.xml @@ -2650,7 +2650,7 @@ displayName="Local variable naming convention" level="WEAK WARNING"/> - namePattern = value @@ -297,37 +306,17 @@ class LocalVariableNameInspection : START_LOWER, NO_UNDERSCORES, NO_BAD_CHARACTERS ) -class PackageNameInspection : - NamingConventionInspection("Package", "[a-z_][a-zA-Z\\d_]*(\\.[a-z_][a-zA-Z\\d_]*)*") { - - companion object { - val PART_RULES = arrayOf(NO_BAD_CHARACTERS_OR_UNDERSCORE, NO_START_UPPER) - } - +private class PackageNameInspectionLocal( + val parentInspection: InspectionProfileEntry, + val namingSettings: NamingConventionInspectionSettings +) : AbstractKotlinInspection() { override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean, session: LocalInspectionToolSession): PsiElementVisitor { return packageDirectiveVisitor { directive -> val packageNameExpression = directive.packageNameExpression ?: return@packageDirectiveVisitor - val qualifiedName = directive.qualifiedName - if (qualifiedName.isEmpty() || namingSettings.nameRegex?.matches(qualifiedName) != false) { - return@packageDirectiveVisitor - } - val partErrorMessage = if (namePattern == namingSettings.defaultNamePattern) { - directive.packageNames.asSequence() - .mapNotNull { simpleName -> - val referencedName = simpleName.getReferencedName() - findRuleMessage(referencedName, PackageNameInspection.PART_RULES) - } - .firstOrNull() - } else { - null - } + val checkResult = checkPackageDirective(directive, namingSettings) ?: return@packageDirectiveVisitor - val descriptionTemplate = if (partErrorMessage != null) { - "Package name #ref part $partErrorMessage #loc" - } else { - "Package name #ref ${namingSettings.getDefaultErrorMessage()} #loc" - } + val descriptionTemplate = checkResult.toProblemTemplateString() holder.registerProblem( packageNameExpression, @@ -337,10 +326,126 @@ class PackageNameInspection : } } + companion object { + data class CheckResult(val errorMessage: String, val isForPart: Boolean) + + fun CheckResult.toProblemTemplateString(): String { + return if (isForPart) { + "Package name #ref part $errorMessage #loc" + } else { + "Package name #ref $errorMessage #loc" + } + } + + fun checkPackageDirective(directive: KtPackageDirective, namingSettings: NamingConventionInspectionSettings): CheckResult? { + return checkQualifiedName(directive.qualifiedName, namingSettings) + } + + fun checkQualifiedName(qualifiedName: String, namingSettings: NamingConventionInspectionSettings): CheckResult? { + if (qualifiedName.isEmpty() || namingSettings.nameRegex?.matches(qualifiedName) != false) { + return null + } + + val partErrorMessage = if (namingSettings.namePattern == namingSettings.defaultNamePattern) { + qualifiedName.split('.').asSequence() + .mapNotNull { part -> findRuleMessage(part, PackageNameInspection.PART_RULES) } + .firstOrNull() + } else { + null + } + + return if (partErrorMessage != null) { + CheckResult(partErrorMessage, true) + } else { + CheckResult(namingSettings.getDefaultErrorMessage(), false) + } + } + } + private class RenamePackageFix : RenameIdentifierFix() { override fun getElementToRename(element: PsiElement): PsiElement? { val packageDirective = element as? KtPackageDirective ?: return null return JavaPsiFacade.getInstance(element.project).findPackage(packageDirective.qualifiedName) } } + + override fun getShortName(): String = parentInspection.shortName + override fun getDisplayName(): String = parentInspection.displayName +} + +class PackageNameInspection : BaseGlobalInspection() { + companion object { + const val DEFAULT_PACKAGE_NAME_PATTERN = "[a-z_][a-zA-Z\\d_]*(\\.[a-z_][a-zA-Z\\d_]*)*" + val PART_RULES = arrayOf(NO_BAD_CHARACTERS_OR_UNDERSCORE, NO_START_UPPER) + + private fun PackageNameInspectionLocal.Companion.CheckResult.toErrorMessage(qualifiedName: String): String { + return if (isForPart) { + "Package name $qualifiedName part $errorMessage" + } else { + "Package name $qualifiedName $errorMessage" + } + } + } + + var namePattern: String = DEFAULT_PACKAGE_NAME_PATTERN + + private val namingSettings = NamingConventionInspectionSettings( + "Package", + DEFAULT_PACKAGE_NAME_PATTERN, + setNamePatternCallback = { value -> + namePattern = value + } + ) + + override fun checkElement( + refEntity: RefEntity, + analysisScope: AnalysisScope, + inspectionManager: InspectionManager, + globalInspectionContext: GlobalInspectionContext + ): Array? { + when (refEntity) { + is RefFile -> { + val psiFile = refEntity.psiFile + if (psiFile is KtFile && !psiFile.isInjectedFragment && !psiFile.packageMatchesDirectoryOrImplicit()) { + val packageDirective = psiFile.packageDirective + if (packageDirective != null) { + val qualifiedName = packageDirective.qualifiedName + val checkResult = PackageNameInspectionLocal.checkPackageDirective(packageDirective, namingSettings) + if (checkResult != null) { + return arrayOf(inspectionManager.createProblemDescriptor(checkResult.toErrorMessage(qualifiedName))) + } + } + } + } + + is RefPackage -> { + @NonNls val name = StringUtil.getShortName(refEntity.getQualifiedName()) + if (name.isEmpty() || InspectionsBundle.message("inspection.reference.default.package") == name) { + return null + } + + val checkResult = PackageNameInspectionLocal.checkQualifiedName(name, namingSettings) + if (checkResult != null) { + return arrayOf(inspectionManager.createProblemDescriptor(checkResult.toErrorMessage(name))) + } + } + + else -> { + return null + } + } + + return null + } + + override fun readSettings(element: Element) { + super.readSettings(element) + namingSettings.namePattern = namePattern + } + + override fun createOptionsPanel() = namingSettings.createOptionsPanel() + + override fun getSharedLocalInspectionTool(): LocalInspectionTool { + return PackageNameInspectionLocal(this, namingSettings) + } } \ No newline at end of file diff --git a/idea/src/org/jetbrains/kotlin/idea/util/compat/refElements.kt b/idea/src/org/jetbrains/kotlin/idea/util/compat/refElements.kt new file mode 100644 index 00000000000..0b379a4ef06 --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/util/compat/refElements.kt @@ -0,0 +1,14 @@ +/* + * Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.idea.util.compat + +import com.intellij.codeInspection.reference.RefFile +import com.intellij.psi.PsiFile + +// BUNCH: 182 +@Suppress("IncompatibleAPI") +val RefFile.psiFile: PsiFile? + get() = psiElement \ No newline at end of file diff --git a/idea/src/org/jetbrains/kotlin/idea/util/compat/refElements.kt.182 b/idea/src/org/jetbrains/kotlin/idea/util/compat/refElements.kt.182 new file mode 100644 index 00000000000..dacd12701a3 --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/util/compat/refElements.kt.182 @@ -0,0 +1,14 @@ +/* + * Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.idea.util.compat + +import com.intellij.codeInspection.reference.RefFile +import com.intellij.psi.PsiFile + +// BUNCH: 182 +@Suppress("IncompatibleAPI") +val RefFile.psiFile: PsiFile? + get() = element \ No newline at end of file