diff --git a/idea/src/org/jetbrains/kotlin/idea/inspections/NamingConventionInspections.kt b/idea/src/org/jetbrains/kotlin/idea/inspections/NamingConventionInspections.kt
index db707c44d7e..bc817feb0e4 100644
--- a/idea/src/org/jetbrains/kotlin/idea/inspections/NamingConventionInspections.kt
+++ b/idea/src/org/jetbrains/kotlin/idea/inspections/NamingConventionInspections.kt
@@ -26,9 +26,28 @@ import java.awt.BorderLayout
import java.util.regex.PatternSyntaxException
import javax.swing.JPanel
+data class NamingRule(val message: String, val matcher: (String) -> Boolean)
+
+private val START_UPPER = NamingRule("should start with an uppercase letter") {
+ it.getOrNull(0)?.isUpperCase() == false
+}
+
+private val START_LOWER = NamingRule("should start with a lowercase letter") {
+ it.getOrNull(0)?.isLowerCase() == false
+}
+
+private val NO_UNDERSCORES = NamingRule("should not contain underscores") {
+ '_' in it
+}
+
+private val NO_MIDDLE_UNDERSCORES = NamingRule("should not contain underscores in the middle or the end") {
+ '_' in it.substring(1)
+}
+
abstract class NamingConventionInspection(
private val entityName: String,
- defaultNamePattern: String
+ private val defaultNamePattern: String,
+ private vararg val rules: NamingRule
) : AbstractKotlinInspection() {
protected var nameRegex: Regex? = defaultNamePattern.toRegex()
var namePattern: String = defaultNamePattern
@@ -45,18 +64,34 @@ abstract class NamingConventionInspection(
val name = element.name
val nameIdentifier = element.nameIdentifier
if (name != null && nameIdentifier != null && nameRegex?.matches(name) == false) {
+ val message = getNameMismatchMessage(name)
holder.registerProblem(
element.nameIdentifier!!,
- "$entityName name #ref doesn't match regex '$namePattern' #loc",
+ "$entityName name #ref $message #loc",
RenameIdentifierFix()
)
}
}
+ protected fun getNameMismatchMessage(name: String): String {
+ if (namePattern == defaultNamePattern) {
+ for (rule in rules) {
+ if (rule.matcher(name)) {
+ return rule.message
+ }
+ }
+ }
+ return "doesn't match regex '$namePattern'"
+ }
+
override fun createOptionsPanel() = NamingConventionOptionsPanel(this)
}
-class ClassNameInspection : NamingConventionInspection("Class", "[A-Z][A-Za-z\\d]*") {
+class ClassNameInspection : NamingConventionInspection(
+ "Class",
+ "[A-Z][A-Za-z\\d]*",
+ START_UPPER, NO_UNDERSCORES
+) {
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor {
return object : KtVisitorVoid() {
override fun visitClassOrObject(classOrObject: KtClassOrObject) {
@@ -70,7 +105,11 @@ class ClassNameInspection : NamingConventionInspection("Class", "[A-Z][A-Za-z\\d
}
}
-class EnumEntryNameInspection : NamingConventionInspection("Enum entry", "[A-Z]([A-Za-z\\d]*|[A-Z_\\d]*)") {
+class EnumEntryNameInspection : NamingConventionInspection(
+ "Enum entry",
+ "[A-Z]([A-Za-z\\d]*|[A-Z_\\d]*)",
+ START_UPPER
+) {
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor {
return object : KtVisitorVoid() {
override fun visitEnumEntry(enumEntry: KtEnumEntry) {
@@ -80,7 +119,11 @@ class EnumEntryNameInspection : NamingConventionInspection("Enum entry", "[A-Z](
}
}
-class FunctionNameInspection : NamingConventionInspection("Function", "[a-z][A-Za-z\\d]*") {
+class FunctionNameInspection : NamingConventionInspection(
+ "Function",
+ "[a-z][A-Za-z\\d]*",
+ START_LOWER, NO_UNDERSCORES
+) {
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor {
return object : KtVisitorVoid() {
override fun visitNamedFunction(function: KtNamedFunction) {
@@ -96,8 +139,9 @@ class FunctionNameInspection : NamingConventionInspection("Function", "[a-z][A-Z
abstract class PropertyNameInspectionBase protected constructor(
private val kind: PropertyKind,
entityName: String,
- defaultNamePattern: String
-) : NamingConventionInspection(entityName, defaultNamePattern) {
+ defaultNamePattern: String,
+ vararg rules: NamingRule
+) : NamingConventionInspection(entityName, defaultNamePattern, *rules) {
protected enum class PropertyKind { NORMAL, PRIVATE, OBJECT, CONST, LOCAL }
@@ -124,25 +168,32 @@ abstract class PropertyNameInspectionBase protected constructor(
}
}
-class PropertyNameInspection : PropertyNameInspectionBase(PropertyKind.NORMAL, "Property", "[a-z][A-Za-z\\d]*")
+class PropertyNameInspection :
+ PropertyNameInspectionBase(PropertyKind.NORMAL, "Property", "[a-z][A-Za-z\\d]*", START_LOWER, NO_UNDERSCORES)
-class ObjectPropertyNameInspection : PropertyNameInspectionBase(PropertyKind.OBJECT, "Object property", "[A-Za-z][_A-Za-z\\d]*")
+class ObjectPropertyNameInspection :
+ PropertyNameInspectionBase(PropertyKind.OBJECT, "Object property", "[A-Za-z][_A-Za-z\\d]*")
-class PrivatePropertyNameInspection : PropertyNameInspectionBase(PropertyKind.PRIVATE, "Private property", "_?[a-z][A-Za-z\\d]*")
+class PrivatePropertyNameInspection :
+ PropertyNameInspectionBase(PropertyKind.PRIVATE, "Private property", "_?[a-z][A-Za-z\\d]*", NO_MIDDLE_UNDERSCORES)
-class ConstPropertyNameInspection : PropertyNameInspectionBase(PropertyKind.CONST, "Const property", "[A-Z][_A-Z\\d]*")
+class ConstPropertyNameInspection :
+ PropertyNameInspectionBase(PropertyKind.CONST, "Const property", "[A-Z][_A-Z\\d]*")
-class LocalVariableNameInspection : PropertyNameInspectionBase(PropertyKind.LOCAL, "Local variable", "[a-z][A-Za-z\\d]*")
+class LocalVariableNameInspection :
+ PropertyNameInspectionBase(PropertyKind.LOCAL, "Local variable", "[a-z][A-Za-z\\d]*", START_LOWER, NO_UNDERSCORES)
-class PackageNameInspection : NamingConventionInspection("Package", "[a-z][A-Za-z\\d]*(\\.[a-z][A-Za-z\\d]*)*") {
+class PackageNameInspection :
+ NamingConventionInspection("Package", "[a-z][A-Za-z\\d]*(\\.[a-z][A-Za-z\\d]*)*", NO_UNDERSCORES) {
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean, session: LocalInspectionToolSession): PsiElementVisitor {
return object : KtVisitorVoid() {
override fun visitPackageDirective(directive: KtPackageDirective) {
val qualifiedName = directive.qualifiedName
if (qualifiedName.isNotEmpty() && nameRegex?.matches(qualifiedName) == false) {
+ val message = getNameMismatchMessage(qualifiedName)
holder.registerProblem(
directive.packageNameExpression!!,
- "Package name #ref doesn't match regex '$namePattern' #loc",
+ "Package name #ref $message #loc",
RenamePackageFix()
)
}
diff --git a/idea/testData/inspections/naming/class/inspectionData/expected.xml b/idea/testData/inspections/naming/class/inspectionData/expected.xml
index 26b463bc8c8..79b8108cf57 100644
--- a/idea/testData/inspections/naming/class/inspectionData/expected.xml
+++ b/idea/testData/inspections/naming/class/inspectionData/expected.xml
@@ -5,7 +5,7 @@
light_idea_test_case
Class naming convention
- Class name <code>a</code> doesn't match regex '[A-Z][A-Za-z\d]*' #loc
+ Class name <code>a</code> should start with an uppercase letter #loc
test.kt
@@ -13,7 +13,7 @@
light_idea_test_case
Class naming convention
- Class name <code>b</code> doesn''t match regex '[A-Z][A-Za-z\d]*' #loc
+ Class name <code>b</code> should start with an uppercase letter #loc
test.kt
diff --git a/idea/testData/inspections/naming/enumEntry/inspectionData/expected.xml b/idea/testData/inspections/naming/enumEntry/inspectionData/expected.xml
index 2fb137e8437..04575d07063 100644
--- a/idea/testData/inspections/naming/enumEntry/inspectionData/expected.xml
+++ b/idea/testData/inspections/naming/enumEntry/inspectionData/expected.xml
@@ -11,6 +11,6 @@
12
light_idea_test_case
- Enum entry name <code>quuxDoo</code> doesn't match regex '[A-Z]([A-Za-z\d]*|[A-Z_\d]*)' #loc
+ Enum entry name <code>quuxDoo</code> should start with an uppercase letter #loc
\ No newline at end of file
diff --git a/idea/testData/inspections/naming/function/inspectionData/expected.xml b/idea/testData/inspections/naming/function/inspectionData/expected.xml
index 0695fa7edd6..d297390225e 100644
--- a/idea/testData/inspections/naming/function/inspectionData/expected.xml
+++ b/idea/testData/inspections/naming/function/inspectionData/expected.xml
@@ -4,13 +4,13 @@
1
light_idea_test_case
- Function name <code>Foo</code> doesn't match regex '[a-z][A-Za-z\d]*' #loc
+ Function name <code>Foo</code> should start with a lowercase letter #loc
test.kt
3
light_idea_test_case
- Function name <code>FOO_BAR</code> doesn't match regex '[a-z][A-Za-z\d]*' #loc
+ Function name <code>FOO_BAR</code> should not contain underscores #loc
diff --git a/idea/testData/inspections/naming/property/inspectionData/expected.xml b/idea/testData/inspections/naming/property/inspectionData/expected.xml
index 208f68275dd..c83909d433a 100644
--- a/idea/testData/inspections/naming/property/inspectionData/expected.xml
+++ b/idea/testData/inspections/naming/property/inspectionData/expected.xml
@@ -4,13 +4,13 @@
1
light_idea_test_case
- Property name <code>Foo</code> doesn't match regex '[a-z][A-Za-z\d]*' #loc
+ Property name <code>Foo</code> should start with a lowercase letter #loc
test.kt
3
light_idea_test_case
- Property name <code>FOO_BAR</code> doesn't match regex '[a-z][A-Za-z\d]*' #loc
+ Property name <code>FOO_BAR</code> should not contain underscores #loc