Make package name inspection global (KT-29549)
Report same package only once. #KT-29549 Fixed
This commit is contained in:
+1
@@ -117,6 +117,7 @@
|
|||||||
<Problem reference="com.intellij.openapi.extensions.ExtensionPointName#findExtensionOrFail" reason="Absent in 182." />
|
<Problem reference="com.intellij.openapi.extensions.ExtensionPointName#findExtensionOrFail" reason="Absent in 182." />
|
||||||
<Problem reference="com.intellij.openapi.ui.popup.PopupChooserBuilder#PopupChooserBuilder(javax.swing.JList)" reason="Generified in 182. Use PopupChooserBuilderWrapper instead." />
|
<Problem reference="com.intellij.openapi.ui.popup.PopupChooserBuilder#PopupChooserBuilder(javax.swing.JList)" reason="Generified in 182. Use PopupChooserBuilderWrapper instead." />
|
||||||
<Problem reference="com.intellij.openapi.editor.event.EditorFactoryListener" reason="Default implementations were added in 183. Use EditorFactoryListenerWrapper for inheritance instead." />
|
<Problem reference="com.intellij.openapi.editor.event.EditorFactoryListener" reason="Default implementations were added in 183. Use EditorFactoryListenerWrapper for inheritance instead." />
|
||||||
|
<Problem reference="com.intellij.codeInspection.reference.RefFile#getPsiElement" reason="Absent in 182. Use psiFile extension instead." />
|
||||||
</list>
|
</list>
|
||||||
</option>
|
</option>
|
||||||
</inspection_tool>
|
</inspection_tool>
|
||||||
|
|||||||
@@ -2650,7 +2650,7 @@
|
|||||||
displayName="Local variable naming convention"
|
displayName="Local variable naming convention"
|
||||||
level="WEAK WARNING"/>
|
level="WEAK WARNING"/>
|
||||||
|
|
||||||
<localInspection implementationClass="org.jetbrains.kotlin.idea.inspections.PackageNameInspection"
|
<globalInspection implementationClass="org.jetbrains.kotlin.idea.inspections.PackageNameInspection"
|
||||||
language="kotlin"
|
language="kotlin"
|
||||||
groupPath="Kotlin"
|
groupPath="Kotlin"
|
||||||
groupName="Naming conventions"
|
groupName="Naming conventions"
|
||||||
|
|||||||
@@ -5,21 +5,30 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.idea.inspections
|
package org.jetbrains.kotlin.idea.inspections
|
||||||
|
|
||||||
import com.intellij.codeInspection.LocalInspectionToolSession
|
import com.intellij.analysis.AnalysisScope
|
||||||
import com.intellij.codeInspection.ProblemsHolder
|
import com.intellij.codeInspection.*
|
||||||
|
import com.intellij.codeInspection.reference.RefEntity
|
||||||
|
import com.intellij.codeInspection.reference.RefFile
|
||||||
|
import com.intellij.codeInspection.reference.RefPackage
|
||||||
import com.intellij.openapi.editor.event.DocumentAdapter
|
import com.intellij.openapi.editor.event.DocumentAdapter
|
||||||
import com.intellij.openapi.editor.event.DocumentEvent
|
import com.intellij.openapi.editor.event.DocumentEvent
|
||||||
import com.intellij.openapi.ui.LabeledComponent
|
import com.intellij.openapi.ui.LabeledComponent
|
||||||
|
import com.intellij.openapi.util.text.StringUtil
|
||||||
import com.intellij.psi.JavaPsiFacade
|
import com.intellij.psi.JavaPsiFacade
|
||||||
import com.intellij.psi.PsiElement
|
import com.intellij.psi.PsiElement
|
||||||
import com.intellij.psi.PsiElementVisitor
|
import com.intellij.psi.PsiElementVisitor
|
||||||
import com.intellij.psi.PsiNameIdentifierOwner
|
import com.intellij.psi.PsiNameIdentifierOwner
|
||||||
import com.intellij.ui.EditorTextField
|
import com.intellij.ui.EditorTextField
|
||||||
|
import com.siyeh.ig.BaseGlobalInspection
|
||||||
import com.siyeh.ig.psiutils.TestUtils
|
import com.siyeh.ig.psiutils.TestUtils
|
||||||
import org.intellij.lang.annotations.Language
|
import org.intellij.lang.annotations.Language
|
||||||
import org.intellij.lang.regexp.RegExpFileType
|
import org.intellij.lang.regexp.RegExpFileType
|
||||||
import org.jdom.Element
|
import org.jdom.Element
|
||||||
|
import org.jetbrains.annotations.NonNls
|
||||||
|
import org.jetbrains.kotlin.idea.core.packageMatchesDirectoryOrImplicit
|
||||||
import org.jetbrains.kotlin.idea.quickfix.RenameIdentifierFix
|
import org.jetbrains.kotlin.idea.quickfix.RenameIdentifierFix
|
||||||
|
import org.jetbrains.kotlin.idea.refactoring.isInjectedFragment
|
||||||
|
import org.jetbrains.kotlin.idea.util.compat.psiFile
|
||||||
import org.jetbrains.kotlin.lexer.KtTokens
|
import org.jetbrains.kotlin.lexer.KtTokens
|
||||||
import org.jetbrains.kotlin.psi.*
|
import org.jetbrains.kotlin.psi.*
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject
|
import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject
|
||||||
@@ -104,7 +113,7 @@ class NamingConventionInspectionSettings(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected fun getNameMismatchMessage(name: String): String {
|
fun getNameMismatchMessage(name: String): String {
|
||||||
if (namePattern != defaultNamePattern) {
|
if (namePattern != defaultNamePattern) {
|
||||||
return getDefaultErrorMessage()
|
return getDefaultErrorMessage()
|
||||||
}
|
}
|
||||||
@@ -145,7 +154,7 @@ sealed class NamingConventionInspection(
|
|||||||
@Suppress("MemberVisibilityCanBePrivate")
|
@Suppress("MemberVisibilityCanBePrivate")
|
||||||
var namePattern: String = defaultNamePattern
|
var namePattern: String = defaultNamePattern
|
||||||
|
|
||||||
protected val namingSettings = NamingConventionInspectionSettings(
|
private val namingSettings = NamingConventionInspectionSettings(
|
||||||
entityName, defaultNamePattern,
|
entityName, defaultNamePattern,
|
||||||
setNamePatternCallback = { value ->
|
setNamePatternCallback = { value ->
|
||||||
namePattern = value
|
namePattern = value
|
||||||
@@ -297,37 +306,17 @@ class LocalVariableNameInspection :
|
|||||||
START_LOWER, NO_UNDERSCORES, NO_BAD_CHARACTERS
|
START_LOWER, NO_UNDERSCORES, NO_BAD_CHARACTERS
|
||||||
)
|
)
|
||||||
|
|
||||||
class PackageNameInspection :
|
private class PackageNameInspectionLocal(
|
||||||
NamingConventionInspection("Package", "[a-z_][a-zA-Z\\d_]*(\\.[a-z_][a-zA-Z\\d_]*)*") {
|
val parentInspection: InspectionProfileEntry,
|
||||||
|
val namingSettings: NamingConventionInspectionSettings
|
||||||
companion object {
|
) : AbstractKotlinInspection() {
|
||||||
val PART_RULES = arrayOf(NO_BAD_CHARACTERS_OR_UNDERSCORE, NO_START_UPPER)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean, session: LocalInspectionToolSession): PsiElementVisitor {
|
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean, session: LocalInspectionToolSession): PsiElementVisitor {
|
||||||
return packageDirectiveVisitor { directive ->
|
return packageDirectiveVisitor { directive ->
|
||||||
val packageNameExpression = directive.packageNameExpression ?: return@packageDirectiveVisitor
|
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) {
|
val checkResult = checkPackageDirective(directive, namingSettings) ?: return@packageDirectiveVisitor
|
||||||
directive.packageNames.asSequence()
|
|
||||||
.mapNotNull { simpleName ->
|
|
||||||
val referencedName = simpleName.getReferencedName()
|
|
||||||
findRuleMessage(referencedName, PackageNameInspection.PART_RULES)
|
|
||||||
}
|
|
||||||
.firstOrNull()
|
|
||||||
} else {
|
|
||||||
null
|
|
||||||
}
|
|
||||||
|
|
||||||
val descriptionTemplate = if (partErrorMessage != null) {
|
val descriptionTemplate = checkResult.toProblemTemplateString()
|
||||||
"Package name <code>#ref</code> part $partErrorMessage #loc"
|
|
||||||
} else {
|
|
||||||
"Package name <code>#ref</code> ${namingSettings.getDefaultErrorMessage()} #loc"
|
|
||||||
}
|
|
||||||
|
|
||||||
holder.registerProblem(
|
holder.registerProblem(
|
||||||
packageNameExpression,
|
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 <code>#ref</code> part $errorMessage #loc"
|
||||||
|
} else {
|
||||||
|
"Package name <code>#ref</code> $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() {
|
private class RenamePackageFix : RenameIdentifierFix() {
|
||||||
override fun getElementToRename(element: PsiElement): PsiElement? {
|
override fun getElementToRename(element: PsiElement): PsiElement? {
|
||||||
val packageDirective = element as? KtPackageDirective ?: return null
|
val packageDirective = element as? KtPackageDirective ?: return null
|
||||||
return JavaPsiFacade.getInstance(element.project).findPackage(packageDirective.qualifiedName)
|
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 <code>$qualifiedName</code> part $errorMessage"
|
||||||
|
} else {
|
||||||
|
"Package name <code>$qualifiedName</code> $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<CommonProblemDescriptor>? {
|
||||||
|
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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
Reference in New Issue
Block a user