From 69cb208e0b2f0f67120ae9267a4a515c2d3b7486 Mon Sep 17 00:00:00 2001 From: just-d-a Date: Wed, 13 Jan 2021 12:10:37 +0300 Subject: [PATCH 01/20] fix ComponentPositionDetector + refactor --- .../name/class/NameFileSufixDetector.kt | 4 +-- .../ComponentPositionDetector.kt | 25 +++++++++---------- .../class/NameFileUpperCamelCaseDetector.kt | 2 +- 3 files changed, 15 insertions(+), 16 deletions(-) diff --git a/checks/src/main/java/com/omegar/lint/checks/detector/code_guidelines/kotlin_style/name/class/NameFileSufixDetector.kt b/checks/src/main/java/com/omegar/lint/checks/detector/code_guidelines/kotlin_style/name/class/NameFileSufixDetector.kt index e3d7b65..ca7c770 100644 --- a/checks/src/main/java/com/omegar/lint/checks/detector/code_guidelines/kotlin_style/name/class/NameFileSufixDetector.kt +++ b/checks/src/main/java/com/omegar/lint/checks/detector/code_guidelines/kotlin_style/name/class/NameFileSufixDetector.kt @@ -106,11 +106,11 @@ class NameFileSufixDetector : Detector(), Detector.UastScanner { node, context.getNameLocation(node), "$REPORT_MESSAGE_BEGIN $value.\n${ISSUE.getExplanation(TextFormat.TEXT)}", - createContextReport(node.name, value) + createLintFix(node.name, value) ) } - private fun createContextReport(part: String?, value: String): LintFix { + private fun createLintFix(part: String?, value: String): LintFix { return fix() .replace() .text(part) diff --git a/checks/src/main/java/com/omegar/lint/checks/detector/code_guidelines/kotlin_style/order/file_class_interface/ComponentPositionDetector.kt b/checks/src/main/java/com/omegar/lint/checks/detector/code_guidelines/kotlin_style/order/file_class_interface/ComponentPositionDetector.kt index 442b5e8..cff3793 100644 --- a/checks/src/main/java/com/omegar/lint/checks/detector/code_guidelines/kotlin_style/order/file_class_interface/ComponentPositionDetector.kt +++ b/checks/src/main/java/com/omegar/lint/checks/detector/code_guidelines/kotlin_style/order/file_class_interface/ComponentPositionDetector.kt @@ -113,26 +113,26 @@ class ComponentPositionDetector : Detector(), Detector.UastScanner { var currentRank = 0 sortedDeclarationList.forEach { declaration -> val text = declaration.text ?: return - val currentParams = CurrentParams(context, text, currentRank, node, declaration) + val currentParams = CurrentParams(context, text, node, declaration) /** 1) it's can find companion object*/ - currentRank = checkOrder(currentParams, COMPANION_OBJECT_PARAMS) + currentRank = checkOrder(currentParams, COMPANION_OBJECT_PARAMS, currentRank) /** 2) Variables*/ - currentRank = checkOrder(currentParams, VAL_PARAMS) - currentRank = checkOrder(currentParams, VAR_PARAMS) + currentRank = checkOrder(currentParams, VAL_PARAMS, currentRank) + currentRank = checkOrder(currentParams, VAR_PARAMS, currentRank) /** 3) Constructor */ - currentRank = checkOrder(currentParams, CONSTRUCTOR_PARAMS) + currentRank = checkOrder(currentParams, CONSTRUCTOR_PARAMS, currentRank) /** 4 Function */ - currentRank = checkOrder(currentParams, FUNCTION_PARAMS) + currentRank = checkOrder(currentParams, FUNCTION_PARAMS, currentRank) /** 5 Enum */ - currentRank = checkOrder(currentParams, ENUM_PARAMS) + currentRank = checkOrder(currentParams, ENUM_PARAMS, currentRank) /** 6) Interface */ - currentRank = checkOrder(currentParams, INTERFACE_PARAMS) + currentRank = checkOrder(currentParams, INTERFACE_PARAMS, currentRank) /** 7) Class */ val classRegex = CLASS_REGEX_LIST.firstOrNull { text.contains(it) } @@ -169,16 +169,16 @@ class ComponentPositionDetector : Detector(), Detector.UastScanner { return list.distinctBy { it.text } } - private fun checkOrder(currentParams: CurrentParams, staticParams: StaticParams): Int { + private fun checkOrder(currentParams: CurrentParams, staticParams: StaticParams, currentRank: Int): Int { val isVariable = staticParams.regexList.firstOrNull { currentParams.text.contains(it) } if (isVariable != null) { - if (currentParams.currentRank <= staticParams.rank) { + if (currentRank <= staticParams.rank) { return staticParams.rank } else { makeContextReport(currentParams.context, currentParams.node, currentParams.declaration, staticParams.message) } } - return currentParams.currentRank + return currentRank } private fun makeContextReport(context: JavaContext, node: UClass, declaration: UDeclaration, message: String) { @@ -186,7 +186,7 @@ class ComponentPositionDetector : Detector(), Detector.UastScanner { ISSUE, node, context.getNameLocation(declaration), - "$message ${ISSUE.getExplanation(TextFormat.TEXT)}" + "$message. ${ISSUE.getExplanation(TextFormat.TEXT)}" ) } @@ -199,7 +199,6 @@ class ComponentPositionDetector : Detector(), Detector.UastScanner { private class CurrentParams( val context: JavaContext, val text: String, - val currentRank: Int, val node: UClass, val declaration: UDeclaration ) diff --git a/checks/src/main/java/com/omegar/lint/checks/detector/project_guidelines/file_name/class/NameFileUpperCamelCaseDetector.kt b/checks/src/main/java/com/omegar/lint/checks/detector/project_guidelines/file_name/class/NameFileUpperCamelCaseDetector.kt index 35edfa1..3c72e43 100644 --- a/checks/src/main/java/com/omegar/lint/checks/detector/project_guidelines/file_name/class/NameFileUpperCamelCaseDetector.kt +++ b/checks/src/main/java/com/omegar/lint/checks/detector/project_guidelines/file_name/class/NameFileUpperCamelCaseDetector.kt @@ -73,7 +73,7 @@ class NameFileUpperCamelCaseDetector : Detector(), Detector.UastScanner { } } - return resultName + return "${charArray[0]}$resultName" } } From 5b677519970414369d8a4b80fd4ce2fc3abcea72 Mon Sep 17 00:00:00 2001 From: just-d-a Date: Wed, 13 Jan 2021 19:00:15 +0300 Subject: [PATCH 02/20] rewriting SpaceMethodDetector --- .../around_operands/SpaceMethodDetector.kt | 106 +++++++++--------- 1 file changed, 53 insertions(+), 53 deletions(-) diff --git a/checks/src/main/java/com/omegar/lint/checks/detector/code_guidelines/kotlin_style/use_spaces/around_operands/SpaceMethodDetector.kt b/checks/src/main/java/com/omegar/lint/checks/detector/code_guidelines/kotlin_style/use_spaces/around_operands/SpaceMethodDetector.kt index c1b49bd..8e834be 100644 --- a/checks/src/main/java/com/omegar/lint/checks/detector/code_guidelines/kotlin_style/use_spaces/around_operands/SpaceMethodDetector.kt +++ b/checks/src/main/java/com/omegar/lint/checks/detector/code_guidelines/kotlin_style/use_spaces/around_operands/SpaceMethodDetector.kt @@ -5,7 +5,7 @@ import com.android.tools.lint.detector.api.* import org.jetbrains.uast.UClass import org.jetbrains.uast.UElement -class SpaceMethodDetector : Detector(), Detector.UastScanner { +open class SpaceMethodDetector : Detector(), Detector.UastScanner { companion object { /** Issue describing the problem and pointing to the detector implementation */ @JvmField @@ -30,17 +30,17 @@ class SpaceMethodDetector : Detector(), Detector.UastScanner { Regex("""([a-z]|[A-Z]|"|'|\)|=|>|\?)\s\{$""") private val RIGHT_FUNCTIONS_OPEN_SCOPE_REGEX = Regex("""([a-z]|[A-Z]|\d)\s*\(""") - private val POINT_BEGIN_REGEX = Regex("""^\s*\.""") - private val QUESTION_MARK_POINT_BEGIN_REGEX = Regex("""^\s*\?\.""") - private const val DELETE_SPACES_MESSAGE = "Remove extra spaces." private const val FUNCTION_VALUE = "fun" private const val OPEN_SCOPE_VALUE = "(" private const val QUOTE_VALUE = "\"" + private val OPEN_BRACE_REGEX = Regex("""\s*\{""") + private val OPEN_SCOPE_REGEX = Regex("""\s*\(""") + + private val KEY_SYMBOLS_ARRAY = arrayOf(".", "::", "?.") + private val REGEXPS = KEY_SYMBOLS_ARRAY.map { it to Regex("""\s*$it\s*""") }.toMap() - private val CHAR_ARRAY = arrayOf(".", "::", "?.") - private val REGEXPS = CHAR_ARRAY.map { it to Regex("""\s*$it\s""") }.toMap() private val COMMENTS_REGEX = Regex("""([//]|[/\*])""") } @@ -55,51 +55,39 @@ class SpaceMethodDetector : Detector(), Detector.UastScanner { var beginPosition = 0 lines.forEach { line -> val length = line.length + REGEXPS.forEach { pair -> if (line.contains(pair.value) && !line.contains(COMMENTS_REGEX)) { val beforeIndex = line.indexOf(" ${pair.key}") val afterIndex = line.indexOf("${pair.key} ") + val match = pair.value.find(line) - when { - beforeIndex > 0 - && afterIndex <= 0 - && !line.contains(POINT_BEGIN_REGEX) - && !line.contains(QUESTION_MARK_POINT_BEGIN_REGEX) -> { - makeContextReport( - Params( - context, - node, - beginPosition + beforeIndex, - pair.key.length + 1, - line, - beforeIndex - ) - ) - } + if (match != null && (beforeIndex > 0 || afterIndex > 0)) { - afterIndex > 0 && beforeIndex <= 0 -> { - makeContextReport( - Params( - context, - node, - beginPosition + afterIndex, - pair.key.length + 1, - line, - afterIndex - ) + val index = if(beforeIndex > 0) {beforeIndex} else {afterIndex} + + makeContextReport( + Params( + context, + node, + beginPosition + 1, + line.length - 1, + line, + 0, + "${pair.key} $beforeIndex $afterIndex ${match.value} \n $line \n $index" ) - } + ) - afterIndex > 0 && beforeIndex > 0 -> { + if (index > 0) { makeContextReport( Params( context, node, - beginPosition + beforeIndex, - pair.key.length + 2, + beginPosition + index, + match.value.length, line, - beforeIndex + index ) ) } @@ -116,6 +104,7 @@ class SpaceMethodDetector : Detector(), Detector.UastScanner { } } + private fun checkSpaceForScopes( context: JavaContext, line: String, @@ -125,9 +114,13 @@ class SpaceMethodDetector : Detector(), Detector.UastScanner { val length = line.length if (line.contains(RIGHT_FUNCTIONS_OPEN_SCOPE_REGEX) && line.contains(FUNCTION_VALUE)) { val functionLine = line.substring(0, line.indexOf(OPEN_SCOPE_VALUE) + 1) - val index = functionLine.indexOf(" (") - if (index > 0) { - makeContextReport(Params(context, node, beginPosition + index, 2, line, index)) + val spaceIndex = functionLine.indexOf(" $OPEN_SCOPE_VALUE") + if (spaceIndex > 0) { + val match = OPEN_SCOPE_REGEX.find(line) + if (match != null) { + val index = spaceIndex - match.value.length + 2 + makeContextReport(Params(context, node, beginPosition + index, match.value.length, line, index)) + } } } @@ -135,22 +128,17 @@ class SpaceMethodDetector : Detector(), Detector.UastScanner { && line.contains(END_FUNCTION_DECLARATION_REGEX) && !line.matches(END_FUNCTION_DECLARATION_REGEX) ) { - if (line[line.indexOf("{") - 1].toString() != OPEN_SCOPE_VALUE) - makeContextReport( - Params( - context, - node, - beginPosition + length - 1, - 1, - line, - length - 1 - ) - ) + val match = OPEN_BRACE_REGEX.find(line) + if (match != null) { + val index = length - match.value.length + makeContextReport(Params(context, node, beginPosition + index, match.value.length, line, index, " ")) + } } } private fun makeContextReport(params: Params) { if (!isInQuote(params.line, params.index)) { + params.context.report( ISSUE, params.node, @@ -159,7 +147,8 @@ class SpaceMethodDetector : Detector(), Detector.UastScanner { params.beginPosition, params.length ), - ISSUE.getExplanation(TextFormat.TEXT) + params.toReplaceString/*ISSUE.getExplanation(TextFormat.TEXT)*/, + createLintFix(params.line, params.index, params.length, params.toReplaceString) ) } } @@ -177,15 +166,26 @@ class SpaceMethodDetector : Detector(), Detector.UastScanner { return inside } } + return false } + private fun createLintFix(line: String, index: Int, length: Int, toReplaceString: String): LintFix { + val substringWithSpace = line.substring(index, index + length) + return LintFix.create() + .replace() + .text(substringWithSpace) + .with(substringWithSpace.trim() + toReplaceString) + .build() + } + class Params( val context: JavaContext, val node: UClass, val beginPosition: Int, val length: Int, val line: String, - val index: Int + val index: Int, + val toReplaceString: String = "" ) } \ No newline at end of file From a179a7a94df991e05977e2d34ac7978a49b5bbbd Mon Sep 17 00:00:00 2001 From: Just-D-A Date: Mon, 18 Jan 2021 02:47:21 +0300 Subject: [PATCH 03/20] correct comments fix SimplificationsFunctionDetector add quick fix NameResourceLayoutDetector --- .../name/abbreviation/AbbreviationDetector.kt | 34 ++++- .../SimplificationsFunctionDetector.kt | 118 ++++++++++-------- .../layout/NameResourceLayoutDetector.kt | 14 ++- 3 files changed, 112 insertions(+), 54 deletions(-) diff --git a/checks/src/main/java/com/omegar/lint/checks/detector/code_guidelines/kotlin_style/name/abbreviation/AbbreviationDetector.kt b/checks/src/main/java/com/omegar/lint/checks/detector/code_guidelines/kotlin_style/name/abbreviation/AbbreviationDetector.kt index 3f1345e..47e5369 100644 --- a/checks/src/main/java/com/omegar/lint/checks/detector/code_guidelines/kotlin_style/name/abbreviation/AbbreviationDetector.kt +++ b/checks/src/main/java/com/omegar/lint/checks/detector/code_guidelines/kotlin_style/name/abbreviation/AbbreviationDetector.kt @@ -33,7 +33,7 @@ class AbbreviationDetector : Detector(), Detector.UastScanner { //exclusion private const val MILLISECONDS_LABEL = "MSec" - private const val UELEMENT_LABEL = "UElement" + private const val U_ELEMENT_LABEL = "UElement" private const val TODO_LABEL = "TODO" private const val CLASS_LABEL = "class" private const val ENUM_LABEL = "enum class" @@ -41,7 +41,7 @@ class AbbreviationDetector : Detector(), Detector.UastScanner { val exclusionsList = listOf( MILLISECONDS_LABEL, TODO_LABEL, - UELEMENT_LABEL + U_ELEMENT_LABEL ) } @@ -73,7 +73,8 @@ class AbbreviationDetector : Detector(), Detector.UastScanner { ISSUE, node as UElement, context.getNameLocation(node), - checkText + "\n" + ISSUE.getExplanation(TextFormat.TEXT) + checkText + "\n" + ISSUE.getExplanation(TextFormat.TEXT), + createLintFix(checkText) ) } } @@ -105,4 +106,31 @@ class AbbreviationDetector : Detector(), Detector.UastScanner { } return resultText } + + private fun createLintFix(oldName: String): LintFix { + return LintFix.create() + .replace() + .text(oldName) + .with(getNewName(oldName)) + .build() + } + + + private fun getNewName(oldName: String): String { + var resultName = "" + val charArray = oldName.toCharArray() + + for (i in 1 until oldName.length) { + val currentChar = charArray[i] + val previousChar = charArray[i - 1] + + resultName += if (currentChar.isUpperCase() && previousChar.isUpperCase()) { + currentChar.toLowerCase() + } else { + currentChar + } + } + + return resultName + } } diff --git a/checks/src/main/java/com/omegar/lint/checks/detector/code_guidelines/kotlin_style/simplifications/function/SimplificationsFunctionDetector.kt b/checks/src/main/java/com/omegar/lint/checks/detector/code_guidelines/kotlin_style/simplifications/function/SimplificationsFunctionDetector.kt index d6c75d1..eb4f935 100644 --- a/checks/src/main/java/com/omegar/lint/checks/detector/code_guidelines/kotlin_style/simplifications/function/SimplificationsFunctionDetector.kt +++ b/checks/src/main/java/com/omegar/lint/checks/detector/code_guidelines/kotlin_style/simplifications/function/SimplificationsFunctionDetector.kt @@ -2,67 +2,87 @@ package com.omegar.lint.checks.detector.code_guidelines.kotlin_style.simplificat import com.android.tools.lint.client.api.UElementHandler import com.android.tools.lint.detector.api.* +import com.omegar.lint.checks.detector.code_guidelines.kotlin_style.restrictions.line_length.MaxLineLengthDetector.Companion.MAX_LENGTH import org.jetbrains.uast.UElement import org.jetbrains.uast.UMethod class SimplificationsFunctionDetector : Detector(), Detector.UastScanner { - companion object { - /** Issue describing the problem and pointing to the detector implementation */ - @JvmField - val ISSUE: Issue = Issue.create( - "OMEGA_CAN_USE_EXPRESSION_FUNCTION", - "When a function contains only one expression, it can be represented as an \"expression function\".", - """ + companion object { + /** Issue describing the problem and pointing to the detector implementation */ + @JvmField + val ISSUE: Issue = Issue.create( + "OMEGA_CAN_USE_EXPRESSION_FUNCTION", + "When a function contains only one expression, it can be represented as an \"expression function\".", + """ You can change it to "expression function" http://wiki.omega-r.club/dev-android-code#rec228389255 """, - Category.CORRECTNESS, - 7, - Severity.INFORMATIONAL, - Implementation( - SimplificationsFunctionDetector::class.java, - Scope.JAVA_FILE_SCOPE - ) - ) + Category.CORRECTNESS, + 7, + Severity.INFORMATIONAL, + Implementation( + SimplificationsFunctionDetector::class.java, + Scope.JAVA_FILE_SCOPE + ) + ) - private val ONE_EXPRESSION_REGEX = Regex("""\{\s*return\s*.*""") - private val RETURN_REGEX = Regex("""\s*return""") - private const val MAX_LINE_COUNT_IN_EXPRESSION_FUNCTION = 3 - } + private val ONE_EXPRESSION_REGEX = Regex("""\{\s*return\s*.*""") + private val RETURN_REGEX = Regex("""\s*return""") + private const val MAX_LINE_COUNT_IN_EXPRESSION_FUNCTION = 3 - override fun getApplicableUastTypes(): List> = listOf(UMethod::class.java) + private const val OPEN_SCOPE_SYMBOL = "{" + private const val CLOSE_SCOPE_SYMBOL = "}" + private const val EQUALS_SYMBOL = "=" + private const val NEW_LINE_SYMBOL = "\n" + private const val SPACE_SYMBOL = " " + + private val END_SPACE_REGEX = Regex("""\s*$""") + private val MORE_THAN_ONE_SPACE_REGEX = Regex("""\s+""") + } + + override fun getApplicableUastTypes(): List> = listOf(UMethod::class.java) + + override fun createUastHandler(context: JavaContext): UElementHandler { + return object : UElementHandler() { + override fun visitMethod(node: UMethod) { + val text = node.text ?: return + val linesCount = text.count { it == '\n' } + 1 + if (linesCount <= MAX_LINE_COUNT_IN_EXPRESSION_FUNCTION && text.contains( + ONE_EXPRESSION_REGEX + ) + ) { + val newText = getSimpleFunctionString(text) + + if (newText.length > MAX_LENGTH) return - override fun createUastHandler(context: JavaContext): UElementHandler { - return object : UElementHandler() { - override fun visitMethod(node: UMethod) { - val text = node.text ?: return - val linesCount = text.count { it == '\n' } + 1 - if (linesCount <= MAX_LINE_COUNT_IN_EXPRESSION_FUNCTION && text.contains(ONE_EXPRESSION_REGEX)) { context.report( - ISSUE, - node, - context.getLocation(node), - ISSUE.getExplanation(TextFormat.TEXT), - createFix(text) - ) - } - } - } - } + ISSUE, + node, + context.getLocation(node), + ISSUE.getExplanation(TextFormat.TEXT), + createFix(text, newText) + ) + } + } + } + } - private fun createFix(text: String): LintFix { - val newText = text - .replace("{", "=") - .replace(RETURN_REGEX, "") - .replace("\n", "") - .replace("}", "") - .replace(Regex("""\s*$"""), "") + private fun createFix(text: String, newText: String): LintFix? { + return fix() + .replace() + .text(text) + .with(newText) + .build() + } - return fix() - .replace() - .text(text) - .with(newText) - .build() + private fun getSimpleFunctionString(text: String): String { + return text + .replace(OPEN_SCOPE_SYMBOL, EQUALS_SYMBOL) + .replace(RETURN_REGEX, "") + .replace(NEW_LINE_SYMBOL, "") + .replace(CLOSE_SCOPE_SYMBOL, "") + .replace(END_SPACE_REGEX, "") + .replace(MORE_THAN_ONE_SPACE_REGEX, SPACE_SYMBOL) + } - } } \ No newline at end of file diff --git a/checks/src/main/java/com/omegar/lint/checks/detector/project_guidelines/file_name/resource/layout/NameResourceLayoutDetector.kt b/checks/src/main/java/com/omegar/lint/checks/detector/project_guidelines/file_name/resource/layout/NameResourceLayoutDetector.kt index fd26e30..cb8284e 100644 --- a/checks/src/main/java/com/omegar/lint/checks/detector/project_guidelines/file_name/resource/layout/NameResourceLayoutDetector.kt +++ b/checks/src/main/java/com/omegar/lint/checks/detector/project_guidelines/file_name/resource/layout/NameResourceLayoutDetector.kt @@ -117,15 +117,25 @@ class NameResourceLayoutDetector : Detector(), Detector.UastScanner { private fun checkLayoutName(arguments: List, newClassName: String, node: UCallExpression, context: JavaContext) { arguments.forEach { argument -> val argumentName = argument.asRenderString() - if (argumentName.contains(LAYOUT_PREFIX_VAL) && LAYOUT_PREFIX_VAL + newClassName != argumentName) { + val layoutName = LAYOUT_PREFIX_VAL + newClassName + if (argumentName.contains(LAYOUT_PREFIX_VAL) && layoutName != argumentName) { context.report( ISSUE, node, context.getNameLocation(argument), - "$LAYOUT_PREFIX_VAL$newClassName\n${ISSUE.getExplanation(TextFormat.TEXT)}" + "$LAYOUT_PREFIX_VAL$newClassName\n${ISSUE.getExplanation(TextFormat.TEXT)}", + createLintFix(argumentName, layoutName) ) } } } + + private fun createLintFix(argumentName: String, layoutName: String): LintFix? { + return LintFix.create() + .replace() + .text(argumentName) + .with(layoutName) + .build() + } } From cb64c45ee432c6adda5c83b0628651960f1fcbae Mon Sep 17 00:00:00 2001 From: just-d-a Date: Mon, 18 Jan 2021 12:00:29 +0300 Subject: [PATCH 04/20] refactoring --- .../field/CompanionObjectFieldsDetector.kt | 23 +++++--- .../emptiness/EmptyBodyFunctionDetector.kt | 20 ++++++- .../around_operands/SpaceMethodDetector.kt | 53 +++++++++++-------- 3 files changed, 65 insertions(+), 31 deletions(-) diff --git a/checks/src/main/java/com/omegar/lint/checks/detector/code_guidelines/kotlin_style/name/field/CompanionObjectFieldsDetector.kt b/checks/src/main/java/com/omegar/lint/checks/detector/code_guidelines/kotlin_style/name/field/CompanionObjectFieldsDetector.kt index cb7f73f..654c9d9 100644 --- a/checks/src/main/java/com/omegar/lint/checks/detector/code_guidelines/kotlin_style/name/field/CompanionObjectFieldsDetector.kt +++ b/checks/src/main/java/com/omegar/lint/checks/detector/code_guidelines/kotlin_style/name/field/CompanionObjectFieldsDetector.kt @@ -53,12 +53,14 @@ class CompanionObjectFieldsDetector : Detector(), Detector.UastScanner { val text = declaration.text ?: return val lines = text.lines() lines.forEach { line -> - if (isIncorrectConstantName(line)) { + val identifierName = getName(line) + if (identifierName.isNotEmpty() && !identifierName.matches(UPPER_REGEX)) { context.report( ISSUE, node, context.getNameLocation(declaration), - "$line\n${ISSUE.getExplanation(TextFormat.TEXT)}" + "$line\n${ISSUE.getExplanation(TextFormat.TEXT)}", + createLintFix(identifierName) ) } } @@ -67,20 +69,27 @@ class CompanionObjectFieldsDetector : Detector(), Detector.UastScanner { } } - private fun isIncorrectConstantName(line: String): Boolean { + private fun getName(line: String): String { if (!line.contains(CONST_VAL_LABEL)) { - return false + return "" } val substrings = line.split(" ") val valIndex = substrings.indexOf(VAL_LABEL) if (valIndex == substrings.size - 1 || valIndex <= 0) { - return false + return "" } - val identifierName = substrings[valIndex + 1] + return substrings[valIndex + 1] + } - return !identifierName.matches(UPPER_REGEX) + private fun createLintFix(name: String): LintFix { + return LintFix.create() + .replace() + .text(name) + .with(name.toUpperCase()) + .build() } } + diff --git a/checks/src/main/java/com/omegar/lint/checks/detector/code_guidelines/kotlin_style/restrictions/emptiness/EmptyBodyFunctionDetector.kt b/checks/src/main/java/com/omegar/lint/checks/detector/code_guidelines/kotlin_style/restrictions/emptiness/EmptyBodyFunctionDetector.kt index 8bea675..31924b3 100644 --- a/checks/src/main/java/com/omegar/lint/checks/detector/code_guidelines/kotlin_style/restrictions/emptiness/EmptyBodyFunctionDetector.kt +++ b/checks/src/main/java/com/omegar/lint/checks/detector/code_guidelines/kotlin_style/restrictions/emptiness/EmptyBodyFunctionDetector.kt @@ -45,7 +45,7 @@ class EmptyBodyFunctionDetector : Detector(), Detector.UastScanner { val text = node.text ?: return if (text.contains(EMPTY_BODY_REGEX) && (body.asRenderString().matches(EMPTY_BODY_REGEX))) { - context.report(ISSUE, node, context.getLocation(body), ISSUE.getExplanation(TextFormat.TEXT)) + context.report(ISSUE, node, context.getLocation(body), text, createFix(text)) } } @@ -53,10 +53,26 @@ class EmptyBodyFunctionDetector : Detector(), Detector.UastScanner { val renderText = node.asRenderString() if (renderText.contains(EMPTY_BRANCH_REGEX)) { - context.report(ISSUE, node, context.getLocation(node), ISSUE.getExplanation(TextFormat.TEXT)) + context.report( + ISSUE, + node, + context.getLocation(node), + ISSUE.getExplanation(TextFormat.TEXT), + createFix(renderText) + ) } } } } + + private fun createFix(body: String): LintFix? { + val indexOfScope = body.indexOf("{") +// val newBody = "${body.removeRange(indexOfScope, body.length)}\n//nothing\n}}" + return LintFix.create() + .replace() + .text(body) + .with("newBody") + .build() + } } diff --git a/checks/src/main/java/com/omegar/lint/checks/detector/code_guidelines/kotlin_style/use_spaces/around_operands/SpaceMethodDetector.kt b/checks/src/main/java/com/omegar/lint/checks/detector/code_guidelines/kotlin_style/use_spaces/around_operands/SpaceMethodDetector.kt index 8e834be..8927035 100644 --- a/checks/src/main/java/com/omegar/lint/checks/detector/code_guidelines/kotlin_style/use_spaces/around_operands/SpaceMethodDetector.kt +++ b/checks/src/main/java/com/omegar/lint/checks/detector/code_guidelines/kotlin_style/use_spaces/around_operands/SpaceMethodDetector.kt @@ -38,8 +38,17 @@ open class SpaceMethodDetector : Detector(), Detector.UastScanner { private val OPEN_BRACE_REGEX = Regex("""\s*\{""") private val OPEN_SCOPE_REGEX = Regex("""\s*\(""") - private val KEY_SYMBOLS_ARRAY = arrayOf(".", "::", "?.") - private val REGEXPS = KEY_SYMBOLS_ARRAY.map { it to Regex("""\s*$it\s*""") }.toMap() + private val KEY_SYMBOLS_MAP = mapOf( + "." to Regex("""\s*\.\s*"""), + "::" to Regex("""\s*::\s*"""), + "?." to Regex("""\s*\?\.\s*""") + ) + + private val KEY_BEGIN_SYMBOLS_MAP = mapOf( + "." to Regex("""^\s*\."""), + "::" to Regex("""^\s*::"""), + "?." to Regex("""^\s*\?\.""") + ) private val COMMENTS_REGEX = Regex("""([//]|[/\*])""") } @@ -56,36 +65,32 @@ open class SpaceMethodDetector : Detector(), Detector.UastScanner { lines.forEach { line -> val length = line.length - REGEXPS.forEach { pair -> + KEY_SYMBOLS_MAP.forEach { pair -> if (line.contains(pair.value) && !line.contains(COMMENTS_REGEX)) { val beforeIndex = line.indexOf(" ${pair.key}") val afterIndex = line.indexOf("${pair.key} ") val match = pair.value.find(line) - if (match != null && (beforeIndex > 0 || afterIndex > 0)) { - - val index = if(beforeIndex > 0) {beforeIndex} else {afterIndex} - - makeContextReport( - Params( - context, - node, - beginPosition + 1, - line.length - 1, - line, - 0, - "${pair.key} $beforeIndex $afterIndex ${match.value} \n $line \n $index" - ) - ) - - if (index > 0) { + if (match != null && (beforeIndex > 0 || afterIndex > 0)) { + var index = line.indexOf(match.value) + if (index >= 0 && !(pair.key == "." && line[line.indexOf(pair.key) - 1] == '?')) { + val selectedText = if (checkIsSymbolFirst(line, pair.key)) { + index = if (beforeIndex > 0) { + beforeIndex - 1 + } else { + afterIndex + } + match.value.substring(match.value.indexOf(pair.key) - 1, match.value.length - 1) + } else { + match.value + } makeContextReport( Params( context, node, beginPosition + index, - match.value.length, + selectedText.length, line, index ) @@ -104,6 +109,10 @@ open class SpaceMethodDetector : Detector(), Detector.UastScanner { } } + private fun checkIsSymbolFirst(line: String, key: String): Boolean { + val beginSymbolRegex = KEY_BEGIN_SYMBOLS_MAP[key] ?: return false + return line.contains(beginSymbolRegex) + } private fun checkSpaceForScopes( context: JavaContext, @@ -147,7 +156,7 @@ open class SpaceMethodDetector : Detector(), Detector.UastScanner { params.beginPosition, params.length ), - params.toReplaceString/*ISSUE.getExplanation(TextFormat.TEXT)*/, + ISSUE.getExplanation(TextFormat.TEXT), createLintFix(params.line, params.index, params.length, params.toReplaceString) ) } From b3b6ecaf2492484b702785176e23e84db017db12 Mon Sep 17 00:00:00 2001 From: just-d-a Date: Mon, 18 Jan 2021 12:15:12 +0300 Subject: [PATCH 05/20] fix space detector --- .../use_spaces/around_operands/SpaceMethodDetector.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/checks/src/main/java/com/omegar/lint/checks/detector/code_guidelines/kotlin_style/use_spaces/around_operands/SpaceMethodDetector.kt b/checks/src/main/java/com/omegar/lint/checks/detector/code_guidelines/kotlin_style/use_spaces/around_operands/SpaceMethodDetector.kt index 8927035..1e1dca9 100644 --- a/checks/src/main/java/com/omegar/lint/checks/detector/code_guidelines/kotlin_style/use_spaces/around_operands/SpaceMethodDetector.kt +++ b/checks/src/main/java/com/omegar/lint/checks/detector/code_guidelines/kotlin_style/use_spaces/around_operands/SpaceMethodDetector.kt @@ -39,9 +39,9 @@ open class SpaceMethodDetector : Detector(), Detector.UastScanner { private val OPEN_SCOPE_REGEX = Regex("""\s*\(""") private val KEY_SYMBOLS_MAP = mapOf( - "." to Regex("""\s*\.\s*"""), - "::" to Regex("""\s*::\s*"""), - "?." to Regex("""\s*\?\.\s*""") + "." to Regex("""(\s+\.\s*|\s*\.\s+)"""), + "::" to Regex("""(\s+::\s*|\s*::\s+)"""), + "?." to Regex("""(\s+\?\.\s*|\s*\?\.\s+)""") ) private val KEY_BEGIN_SYMBOLS_MAP = mapOf( From fc8ffc948f8bb07b50904ee4bf08a05a2a043351 Mon Sep 17 00:00:00 2001 From: just-d-a Date: Tue, 16 Feb 2021 17:24:10 +0300 Subject: [PATCH 06/20] add fixes --- .idea/markdown-navigator-enh.xml | 10 +++ .idea/markdown-navigator.xml | 62 +++++++++++++ .../omegar/lint/checks/LintIssueRegistry.kt | 30 +++---- .../exception/ExceptionCatchDetector.kt | 2 +- .../MaxFunctionsArgumentsDetector.kt | 4 +- ...plificationsControlInstructionsDetector.kt | 28 +++++- .../SimplificationsFunctionDetector.kt | 89 +++++++++---------- .../identifier/NameIdentifierXmlDetector.kt | 24 ++++- 8 files changed, 183 insertions(+), 66 deletions(-) create mode 100644 .idea/markdown-navigator-enh.xml create mode 100644 .idea/markdown-navigator.xml diff --git a/.idea/markdown-navigator-enh.xml b/.idea/markdown-navigator-enh.xml new file mode 100644 index 0000000..a8fcc84 --- /dev/null +++ b/.idea/markdown-navigator-enh.xml @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/.idea/markdown-navigator.xml b/.idea/markdown-navigator.xml new file mode 100644 index 0000000..a2fc086 --- /dev/null +++ b/.idea/markdown-navigator.xml @@ -0,0 +1,62 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/checks/src/main/java/com/omegar/lint/checks/LintIssueRegistry.kt b/checks/src/main/java/com/omegar/lint/checks/LintIssueRegistry.kt index 27b8cdc..d181bba 100644 --- a/checks/src/main/java/com/omegar/lint/checks/LintIssueRegistry.kt +++ b/checks/src/main/java/com/omegar/lint/checks/LintIssueRegistry.kt @@ -35,32 +35,32 @@ class LintIssueRegistry : IssueRegistry() { override val issues: List get() = listOf( NameFileUpperCamelCaseDetector.ISSUE, - AbbreviationDetector.ISSUE, // TODO need testing - PositionArgumentDetector.ISSUE, - MaxFunctionsArgumentsDetector.ISSUE, + AbbreviationDetector.ISSUE, // TODO need rewriting & lint quick fix + PositionArgumentDetector.ISSUE, // TODO add lint quick fix + MaxFunctionsArgumentsDetector.ISSUE, // TODO ??lint quick fix?? ExceptionCatchDetector.ISSUE, - ComponentPositionDetector.ISSUE, + ComponentPositionDetector.ISSUE, //TODO add lint quick fix for NameIdentifierXmlDetector.ISSUE, NameResourceStringXmlDetector.ISSUE, - NameResourceStyleXmlDetector.ISSUE, - MaxMethodCountDetector.ISSUE, - EmptyBodyFunctionDetector.ISSUE, + NameResourceStyleXmlDetector.ISSUE, //TODO change lint quick fix for + MaxMethodCountDetector.ISSUE, // TODO ??lint quick fix?? + EmptyBodyFunctionDetector.ISSUE, //TODO add lint quick fix for CompanionObjectFieldsDetector.ISSUE, - MaxFunctionLengthDetector.ISSUE, - MaxClassLengthDetector.ISSUE, + MaxFunctionLengthDetector.ISSUE, // TODO ??lint quick fix?? + MaxClassLengthDetector.ISSUE, // TODO ??lint quick fix?? SimplificationsFunctionDetector.ISSUE, - MaxLineLengthDetector.ISSUE, + MaxLineLengthDetector.ISSUE, // TODO ??lint quick fix?? AnnotationDetector.ISSUE, SpaceMethodDetector.ISSUE, NameFileSufixDetector.ISSUE, AttributesPositionXmlDetector.ISSUE, - MaxClassInPackageDetector.ISSUE, // TODO need testing - MaxPackageCountDetector.ISSUE, // TODO need testing - SimplificationsControlInstructionsDetector.ISSUE, // TODO need testing + MaxClassInPackageDetector.ISSUE, // TODO ??lint quick fix?? + MaxPackageCountDetector.ISSUE, // TODO ??lint quick fix?? + SimplificationsControlInstructionsDetector.ISSUE, //TODO add lint quick fix for IntentExtraParametersDetector.ISSUE, ArgumentsBundleKeyPrefixDetector.ISSUE, - LambdaDetector.ISSUE, - NameResourceLayoutDetector.ISSUE + LambdaDetector.ISSUE, //TODO add lint quick fix for + NameResourceLayoutDetector.ISSUE //TODO add lint quick fix for ) override val api: Int diff --git a/checks/src/main/java/com/omegar/lint/checks/detector/code_guidelines/kotlin_rules/exception/ExceptionCatchDetector.kt b/checks/src/main/java/com/omegar/lint/checks/detector/code_guidelines/kotlin_rules/exception/ExceptionCatchDetector.kt index 00b3061..b3c1fcb 100644 --- a/checks/src/main/java/com/omegar/lint/checks/detector/code_guidelines/kotlin_rules/exception/ExceptionCatchDetector.kt +++ b/checks/src/main/java/com/omegar/lint/checks/detector/code_guidelines/kotlin_rules/exception/ExceptionCatchDetector.kt @@ -61,7 +61,7 @@ class ExceptionCatchDetector : Detector(), Detector.UastScanner { context.report( ISSUE, body, - context.getLocation(it as UElement), + context.getLocation(body), GENERALIZED_EXCEPTION_MESSAGE, createEmptyBodyFix() ) diff --git a/checks/src/main/java/com/omegar/lint/checks/detector/code_guidelines/kotlin_style/restrictions/params_count/MaxFunctionsArgumentsDetector.kt b/checks/src/main/java/com/omegar/lint/checks/detector/code_guidelines/kotlin_style/restrictions/params_count/MaxFunctionsArgumentsDetector.kt index 92660d8..8b32a5d 100644 --- a/checks/src/main/java/com/omegar/lint/checks/detector/code_guidelines/kotlin_style/restrictions/params_count/MaxFunctionsArgumentsDetector.kt +++ b/checks/src/main/java/com/omegar/lint/checks/detector/code_guidelines/kotlin_style/restrictions/params_count/MaxFunctionsArgumentsDetector.kt @@ -25,6 +25,8 @@ class MaxFunctionsArgumentsDetector : Detector(), Detector.UastScanner { ) ) + // lint can't understand data class constructors because it understand them as fun with name "copy" + private const val DATA_CLASS_FUNCTION_VALUE = "public final fun copy" private const val MAX_COUNT_OF_ARGUMENTS = 5 } @@ -33,7 +35,7 @@ class MaxFunctionsArgumentsDetector : Detector(), Detector.UastScanner { override fun createUastHandler(context: JavaContext): UElementHandler { return object : UElementHandler() { override fun visitMethod(node: UMethod) { - if (node.isConstructor) { + if (node.isConstructor || node.asRenderString().contains(DATA_CLASS_FUNCTION_VALUE)) { return } diff --git a/checks/src/main/java/com/omegar/lint/checks/detector/code_guidelines/kotlin_style/simplifications/control_instructions/SimplificationsControlInstructionsDetector.kt b/checks/src/main/java/com/omegar/lint/checks/detector/code_guidelines/kotlin_style/simplifications/control_instructions/SimplificationsControlInstructionsDetector.kt index 5492323..599390b 100644 --- a/checks/src/main/java/com/omegar/lint/checks/detector/code_guidelines/kotlin_style/simplifications/control_instructions/SimplificationsControlInstructionsDetector.kt +++ b/checks/src/main/java/com/omegar/lint/checks/detector/code_guidelines/kotlin_style/simplifications/control_instructions/SimplificationsControlInstructionsDetector.kt @@ -26,10 +26,18 @@ class SimplificationsControlInstructionsDetector : Detector(), Detector.UastScan ) private val EMPTY_BRANCH_REGEX = Regex("""\{\s*\}""") + private val END_SPACE_REGEX = Regex("""\s*$""") + private val MORE_THAN_ONE_SPACE_REGEX = Regex("""\s+""") private const val ELSE_LABEL = "-> {" private const val ELSE_TEXT = "else -> {" private const val DELTA = 2 + private const val OPEN_SCOPE_SYMBOL = "{" + private const val CLOSE_SCOPE_SYMBOL = "}" + private const val EQUALS_SYMBOL = "=" + private const val NEW_LINE_SYMBOL = "\n" + private const val SPACE_SYMBOL = " " + } override fun getApplicableUastTypes(): List> = listOf(USwitchClauseExpression::class.java) @@ -65,9 +73,27 @@ class SimplificationsControlInstructionsDetector : Detector(), Detector.UastScan private fun exceedMaxLineLength(text: String): Boolean { val lines = text.lines() - if(lines.size > 1) { + if (lines.size > 1) { return lines[0].length + lines[1].trim().length - DELTA < MAX_LENGTH } return false } + + private fun createFix(text: String): LintFix? { + return fix() + .replace() + .text(text) + .with(getSimpleString(text)) + .build() + } + + private fun getSimpleString(text: String): String { + return text + .replace(OPEN_SCOPE_SYMBOL, EQUALS_SYMBOL) + .replace(NEW_LINE_SYMBOL, "") + .replace(CLOSE_SCOPE_SYMBOL, "") + .replace(END_SPACE_REGEX, "") + .replace(MORE_THAN_ONE_SPACE_REGEX, SPACE_SYMBOL) + } + } diff --git a/checks/src/main/java/com/omegar/lint/checks/detector/code_guidelines/kotlin_style/simplifications/function/SimplificationsFunctionDetector.kt b/checks/src/main/java/com/omegar/lint/checks/detector/code_guidelines/kotlin_style/simplifications/function/SimplificationsFunctionDetector.kt index eb4f935..4e3879e 100644 --- a/checks/src/main/java/com/omegar/lint/checks/detector/code_guidelines/kotlin_style/simplifications/function/SimplificationsFunctionDetector.kt +++ b/checks/src/main/java/com/omegar/lint/checks/detector/code_guidelines/kotlin_style/simplifications/function/SimplificationsFunctionDetector.kt @@ -7,10 +7,10 @@ import org.jetbrains.uast.UElement import org.jetbrains.uast.UMethod class SimplificationsFunctionDetector : Detector(), Detector.UastScanner { - companion object { - /** Issue describing the problem and pointing to the detector implementation */ - @JvmField - val ISSUE: Issue = Issue.create( + companion object { + /** Issue describing the problem and pointing to the detector implementation */ + @JvmField + val ISSUE: Issue = Issue.create( "OMEGA_CAN_USE_EXPRESSION_FUNCTION", "When a function contains only one expression, it can be represented as an \"expression function\".", """ @@ -26,31 +26,30 @@ class SimplificationsFunctionDetector : Detector(), Detector.UastScanner { ) ) - private val ONE_EXPRESSION_REGEX = Regex("""\{\s*return\s*.*""") - private val RETURN_REGEX = Regex("""\s*return""") - private const val MAX_LINE_COUNT_IN_EXPRESSION_FUNCTION = 3 + private val ONE_EXPRESSION_REGEX = Regex("""\{\s*return\s*.*""") + private val RETURN_REGEX = Regex("""\s*return""") + private const val MAX_LINE_COUNT_IN_EXPRESSION_FUNCTION = 3 - private const val OPEN_SCOPE_SYMBOL = "{" - private const val CLOSE_SCOPE_SYMBOL = "}" - private const val EQUALS_SYMBOL = "=" - private const val NEW_LINE_SYMBOL = "\n" - private const val SPACE_SYMBOL = " " + private const val OPEN_SCOPE_SYMBOL = "{" + private const val CLOSE_SCOPE_SYMBOL = "}" + private const val EQUALS_SYMBOL = "=" + private const val NEW_LINE_SYMBOL = "\n" + private const val SPACE_SYMBOL = " " - private val END_SPACE_REGEX = Regex("""\s*$""") - private val MORE_THAN_ONE_SPACE_REGEX = Regex("""\s+""") - } + private val END_SPACE_REGEX = Regex("""\s*$""") + private val MORE_THAN_ONE_SPACE_REGEX = Regex("""\s+""") + } - override fun getApplicableUastTypes(): List> = listOf(UMethod::class.java) + override fun getApplicableUastTypes(): List> = listOf(UMethod::class.java) - override fun createUastHandler(context: JavaContext): UElementHandler { - return object : UElementHandler() { - override fun visitMethod(node: UMethod) { - val text = node.text ?: return - val linesCount = text.count { it == '\n' } + 1 - if (linesCount <= MAX_LINE_COUNT_IN_EXPRESSION_FUNCTION && text.contains( - ONE_EXPRESSION_REGEX - ) - ) { + override fun createUastHandler(context: JavaContext): UElementHandler { + return object : UElementHandler() { + override fun visitMethod(node: UMethod) { + val text = node.text ?: return + val linesCount = text.count { it == '\n' } + 1 + if (linesCount <= MAX_LINE_COUNT_IN_EXPRESSION_FUNCTION + && text.contains(ONE_EXPRESSION_REGEX) + ) { val newText = getSimpleFunctionString(text) if (newText.length > MAX_LENGTH) return @@ -62,27 +61,27 @@ class SimplificationsFunctionDetector : Detector(), Detector.UastScanner { ISSUE.getExplanation(TextFormat.TEXT), createFix(text, newText) ) - } - } - } - } + } + } + } + } - private fun createFix(text: String, newText: String): LintFix? { - return fix() - .replace() - .text(text) - .with(newText) - .build() - } + private fun createFix(text: String, newText: String): LintFix? { + return fix() + .replace() + .text(text) + .with(newText) + .build() + } - private fun getSimpleFunctionString(text: String): String { - return text - .replace(OPEN_SCOPE_SYMBOL, EQUALS_SYMBOL) - .replace(RETURN_REGEX, "") - .replace(NEW_LINE_SYMBOL, "") - .replace(CLOSE_SCOPE_SYMBOL, "") - .replace(END_SPACE_REGEX, "") - .replace(MORE_THAN_ONE_SPACE_REGEX, SPACE_SYMBOL) - } + private fun getSimpleFunctionString(text: String): String { + return text + .replace(OPEN_SCOPE_SYMBOL, EQUALS_SYMBOL) + .replace(RETURN_REGEX, "") + .replace(NEW_LINE_SYMBOL, "") + .replace(CLOSE_SCOPE_SYMBOL, "") + .replace(END_SPACE_REGEX, "") + .replace(MORE_THAN_ONE_SPACE_REGEX, SPACE_SYMBOL) + } } \ No newline at end of file diff --git a/checks/src/main/java/com/omegar/lint/checks/detector/code_guidelines/xml_style/name_resource/identifier/NameIdentifierXmlDetector.kt b/checks/src/main/java/com/omegar/lint/checks/detector/code_guidelines/xml_style/name_resource/identifier/NameIdentifierXmlDetector.kt index 6c2d08e..cbe26a4 100644 --- a/checks/src/main/java/com/omegar/lint/checks/detector/code_guidelines/xml_style/name_resource/identifier/NameIdentifierXmlDetector.kt +++ b/checks/src/main/java/com/omegar/lint/checks/detector/code_guidelines/xml_style/name_resource/identifier/NameIdentifierXmlDetector.kt @@ -2,6 +2,7 @@ package com.omegar.lint.checks.detector.code_guidelines.xml_style.name_resource. import com.android.resources.ResourceFolderType import com.android.tools.lint.detector.api.* +import com.omegar.lint.checks.detector.project_guidelines.file_name.resource.layout.NameResourceLayoutDetector import org.w3c.dom.Attr class NameIdentifierXmlDetector : ResourceXmlDetector() { @@ -23,7 +24,8 @@ class NameIdentifierXmlDetector : ResourceXmlDetector() { ) ) - const val REPORT_MESSAGE = "Wrong prefix of identifier name. Should begin with: " + private val CAMEL_REGEX = Regex("(?<=[a-zA-Z])[A-Z]") + private const val REPORT_MESSAGE = "Wrong prefix of identifier name. Should begin with: " //elements private const val TEXT_VIEW_ELEMENT = "TextView" @@ -44,6 +46,7 @@ class NameIdentifierXmlDetector : ResourceXmlDetector() { private const val LAYOUT_PREFIX = "@+id/layout" private const val FLOATING_ACTION_BUTTON_PREFIX = "@+id/fab" private const val IMAGE_BUTTON_PREFIX = "@+id/button" + private const val IDENTIFIER_PREFIX = "@+id/" } @@ -97,10 +100,25 @@ class NameIdentifierXmlDetector : ResourceXmlDetector() { makeContextReport(context, attribute, IMAGE_BUTTON_PREFIX) } } + + else -> { + val tagName = IDENTIFIER_PREFIX + attribute.ownerElement.tagName.split(".").last().replace("View", "") + val prefix = tagName.convertCamelToSnakeCase() + if (!attributeValue.contains(prefix)) { + makeContextReport(context, attribute, prefix) + } + } } } } + // String extensions + private fun String.convertCamelToSnakeCase(): String { + return CAMEL_REGEX.replace(this) { + it.value + }.toLowerCase() + } + private fun makeContextReport(context: XmlContext, attribute: Attr, message: String) { context.report( issue = ISSUE, @@ -112,11 +130,11 @@ class NameIdentifierXmlDetector : ResourceXmlDetector() { } private fun createFix(attributeValue: String, correctPrefix: String): LintFix { - val oldText = attributeValue.replace("@+id/", "") + val attributeValueWithoutPrefix = attributeValue.replace(IDENTIFIER_PREFIX, "") return fix() .replace() .text(attributeValue) - .with("${correctPrefix}_$oldText") + .with("${correctPrefix}_$attributeValueWithoutPrefix") .build() } } \ No newline at end of file From 83eb2927b5903110a9a39393a3271cc28592cd24 Mon Sep 17 00:00:00 2001 From: just-d-a Date: Tue, 16 Feb 2021 17:30:26 +0300 Subject: [PATCH 07/20] fix Abbreviation Detector --- .../name/abbreviation/AbbreviationDetector.kt | 2 ++ .../class_methods_count/MaxMethodCountDetector.kt | 13 +++++++++---- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/checks/src/main/java/com/omegar/lint/checks/detector/code_guidelines/kotlin_style/name/abbreviation/AbbreviationDetector.kt b/checks/src/main/java/com/omegar/lint/checks/detector/code_guidelines/kotlin_style/name/abbreviation/AbbreviationDetector.kt index 47e5369..e81d6d1 100644 --- a/checks/src/main/java/com/omegar/lint/checks/detector/code_guidelines/kotlin_style/name/abbreviation/AbbreviationDetector.kt +++ b/checks/src/main/java/com/omegar/lint/checks/detector/code_guidelines/kotlin_style/name/abbreviation/AbbreviationDetector.kt @@ -28,6 +28,7 @@ class AbbreviationDetector : Detector(), Detector.UastScanner { private val ABBREVIATION_REGEX = Regex("""[A-Z][A-Z]""") private val ANNOTATION_REGEX = Regex("""^@""") private const val OPEN_SCOPE_LABEL = "(" + private const val OPEN_TAG_LABEL = "<" private const val EQUAL_LABEL = "=" private const val SPACE_LABEL = " " @@ -65,6 +66,7 @@ class AbbreviationDetector : Detector(), Detector.UastScanner { checkText = deleteAfterSymbol(checkText, EQUAL_LABEL) checkText = deleteAfterSymbol(checkText, OPEN_SCOPE_LABEL) + checkText = deleteAfterSymbol(checkText, OPEN_TAG_LABEL) checkText = deleteExclusions(checkText) diff --git a/checks/src/main/java/com/omegar/lint/checks/detector/code_guidelines/kotlin_style/restrictions/class_methods_count/MaxMethodCountDetector.kt b/checks/src/main/java/com/omegar/lint/checks/detector/code_guidelines/kotlin_style/restrictions/class_methods_count/MaxMethodCountDetector.kt index f717906..d43b9cf 100644 --- a/checks/src/main/java/com/omegar/lint/checks/detector/code_guidelines/kotlin_style/restrictions/class_methods_count/MaxMethodCountDetector.kt +++ b/checks/src/main/java/com/omegar/lint/checks/detector/code_guidelines/kotlin_style/restrictions/class_methods_count/MaxMethodCountDetector.kt @@ -5,6 +5,7 @@ import com.android.tools.lint.detector.api.* import org.jetbrains.uast.UClass import org.jetbrains.uast.UElement +import org.jetbrains.uast.UMethod class MaxMethodCountDetector : Detector(), Detector.UastScanner { companion object { @@ -31,13 +32,17 @@ class MaxMethodCountDetector : Detector(), Detector.UastScanner { override fun getApplicableUastTypes(): List> = listOf(UClass::class.java) - override fun createUastHandler(context: JavaContext): UElementHandler { return object : UElementHandler() { override fun visitClass(node: UClass) { - val methods = node.methods - if (methods.size > MAX_METHOD_COUNT) { - context.report(ISSUE, node, context.getNameLocation(node), ISSUE.getExplanation(TextFormat.TEXT)) + val resultMethods = mutableListOf() + node.methods.forEach { + if(!it.isVarArgs && !it.isConstructor) { + resultMethods.add(it) + } + } + if (resultMethods.size > MAX_METHOD_COUNT) { + context.report(ISSUE, node, context.getNameLocation(node), /*ISSUE.getExplanation(TextFormat.TEXT)*/resultMethods.size.toString()) } } } From 5adf4e61c28134e91e54b283c5490d55bee2697b Mon Sep 17 00:00:00 2001 From: just-d-a Date: Tue, 16 Feb 2021 17:38:30 +0300 Subject: [PATCH 08/20] change MaxMethodCountDetector report message --- .../class_methods_count/MaxMethodCountDetector.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/checks/src/main/java/com/omegar/lint/checks/detector/code_guidelines/kotlin_style/restrictions/class_methods_count/MaxMethodCountDetector.kt b/checks/src/main/java/com/omegar/lint/checks/detector/code_guidelines/kotlin_style/restrictions/class_methods_count/MaxMethodCountDetector.kt index d43b9cf..757cbe6 100644 --- a/checks/src/main/java/com/omegar/lint/checks/detector/code_guidelines/kotlin_style/restrictions/class_methods_count/MaxMethodCountDetector.kt +++ b/checks/src/main/java/com/omegar/lint/checks/detector/code_guidelines/kotlin_style/restrictions/class_methods_count/MaxMethodCountDetector.kt @@ -37,12 +37,12 @@ class MaxMethodCountDetector : Detector(), Detector.UastScanner { override fun visitClass(node: UClass) { val resultMethods = mutableListOf() node.methods.forEach { - if(!it.isVarArgs && !it.isConstructor) { + if (!it.isVarArgs && !it.isConstructor) { resultMethods.add(it) } } if (resultMethods.size > MAX_METHOD_COUNT) { - context.report(ISSUE, node, context.getNameLocation(node), /*ISSUE.getExplanation(TextFormat.TEXT)*/resultMethods.size.toString()) + context.report(ISSUE, node, context.getNameLocation(node), ISSUE.getExplanation(TextFormat.TEXT)) } } } From 195f3540213952aa65cff11ffe38fa84fe3394c2 Mon Sep 17 00:00:00 2001 From: just-d-a Date: Tue, 16 Feb 2021 17:40:30 +0300 Subject: [PATCH 09/20] add app files --- .../java/com/example/lint/MainActivity.kt | 148 ++++++++++++------ app/src/main/res/layout/activity_main.xml | 8 +- .../MaxMethodCountDetector.kt | 3 +- 3 files changed, 106 insertions(+), 53 deletions(-) diff --git a/app/src/main/java/com/example/lint/MainActivity.kt b/app/src/main/java/com/example/lint/MainActivity.kt index d100c8e..53048e4 100644 --- a/app/src/main/java/com/example/lint/MainActivity.kt +++ b/app/src/main/java/com/example/lint/MainActivity.kt @@ -1,89 +1,137 @@ package com.example.lint +import android.annotation.SuppressLint import android.content.Context import android.content.Intent import android.os.Bundle +import android.util.Log import androidx.appcompat.app.AppCompatActivity +import androidx.recyclerview.widget.RecyclerView class MainActivity : AppCompatActivity() { - companion object { + private val someaaa = "photo11111111111111111111111111111111111111111111111111111111111" - const val SS_S_SFDAS = 2 + companion object { - val some = 2 + const val ASAS = 2 * 1 - private const val EXTRA_PHOTO = "photo" + val some = 2 - fun createIntent(context: Context, photo: String): Intent { - return Intent(context, MainActivity ::class.java).putExtra(EXTRA_PHOTO, photo) - } - fun someFunction() {} + private val FREQUENCIES = listOf(50, 60) - fun createLauncher() = { someFunction() } - } + private const val EXTRA_PHOTO = + "photo1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111" - val SSs = 2 + fun ppp() { + String.toString()?.toString() + } - fun spme() { - String?.toString() + } - } - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - setContentView(R.layout.activity_main) + fun someFunction() { + } - val cTX: Context = this - String - .toString() + fun some(): Int = 1 + val s = 2 - val list = listOf() + fun createIntent(context: Context, photo: String): Intent = Intent() - list.forEach { line -> - val s = line - } - } + fun spme() { + String.toString() + val tag = emptyList() + tag.forEach { + Log.d("some", it) + } - private fun SSsome ( ) { - MainActivity .createIntent(this, "") - } - class SomeClass() { - val soURL = 2 - } + } + override fun onCreate(savedInstanceState: Bundle?) { - fun emptyFun(): Int { - try { - val s = 2 - } catch (e: Exception) { - throw e - } + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_main) - return when(1) { - 1 -> 2 - else -> 0 - } + val cTX: Context = this - } + var croppingBlock: (() -> Int)? = null - protected val s2: String = "" + try { + } catch (e: Exception) { + val some = 2 + } - fun Get() { - val s = 2 - s.toString() - //nothing - } + val list = listOf() - fun aAA() { + list.forEach { line -> + val s = line + } + } - } + private fun asome() { + createIntent(this, "") + emptyURL({ val s = 2 }, { val s = 2 }) + } + private fun emptyURL(function: () -> Unit, function2: () -> Unit) { + //nothing + } + + @SuppressLint("OMEGA_ABBREVIATION_AS_WORD") + val soURL = 2 + + class SomeClass() { + + } + + + fun em(): Int { + try { + val s = 2 + } catch (e: Exception) { + throw e + } + + return when (1) { + 1 -> 2 + else -> 0 + } + + } + + protected val s2: String = "" + + + fun agset() { + val s = 2 + s.toString() + //nothing + } + + fun aAa() { + //nothing + } + + data class GeneralSettings( + var doorStatus: Int = 1, + var keepDoorOpen: Boolean = false, + var keepDoorOpenSip: Boolean = false, + var doorOpenDuration: Int = 5, + var callDuration: Int = 5, + var talkDuration: Int = 5, + var conciergeApartment: Int = 100, + val conciergeApartment1: Int = 100, + var conciergeApartment2: Int = 100, + val conciergeApartment3: Int = 100, + var conciergeApartment5: Int = 100 + ) + + class BasePresenter } diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index 6757ae8..3e3f8f2 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -8,7 +8,7 @@ tools:context=".MainActivity"> + + \ No newline at end of file diff --git a/checks/src/main/java/com/omegar/lint/checks/detector/code_guidelines/kotlin_style/restrictions/class_methods_count/MaxMethodCountDetector.kt b/checks/src/main/java/com/omegar/lint/checks/detector/code_guidelines/kotlin_style/restrictions/class_methods_count/MaxMethodCountDetector.kt index 757cbe6..487df81 100644 --- a/checks/src/main/java/com/omegar/lint/checks/detector/code_guidelines/kotlin_style/restrictions/class_methods_count/MaxMethodCountDetector.kt +++ b/checks/src/main/java/com/omegar/lint/checks/detector/code_guidelines/kotlin_style/restrictions/class_methods_count/MaxMethodCountDetector.kt @@ -47,5 +47,4 @@ class MaxMethodCountDetector : Detector(), Detector.UastScanner { } } } -} - +} \ No newline at end of file From 99a088ba6086461d145e9784f021b14a47cda4c2 Mon Sep 17 00:00:00 2001 From: Dmitriy Sidukov Date: Wed, 13 Apr 2022 11:00:46 +0300 Subject: [PATCH 10/20] underscore_check_added --- .../identifier/NameIdentifierXmlDetector.kt | 93 +++++++++---------- 1 file changed, 43 insertions(+), 50 deletions(-) diff --git a/checks/src/main/java/com/omegar/lint/checks/detector/code_guidelines/xml_style/name_resource/identifier/NameIdentifierXmlDetector.kt b/checks/src/main/java/com/omegar/lint/checks/detector/code_guidelines/xml_style/name_resource/identifier/NameIdentifierXmlDetector.kt index cbe26a4..9eff89a 100644 --- a/checks/src/main/java/com/omegar/lint/checks/detector/code_guidelines/xml_style/name_resource/identifier/NameIdentifierXmlDetector.kt +++ b/checks/src/main/java/com/omegar/lint/checks/detector/code_guidelines/xml_style/name_resource/identifier/NameIdentifierXmlDetector.kt @@ -2,15 +2,16 @@ package com.omegar.lint.checks.detector.code_guidelines.xml_style.name_resource. import com.android.resources.ResourceFolderType import com.android.tools.lint.detector.api.* -import com.omegar.lint.checks.detector.project_guidelines.file_name.resource.layout.NameResourceLayoutDetector import org.w3c.dom.Attr +import java.util.Locale +@Suppress("UnstableApiUsage") class NameIdentifierXmlDetector : ResourceXmlDetector() { companion object { val ISSUE = Issue.create( id = "OMEGA_NAME_VARIABLES_CORRECTLY", - briefDescription = "Detects wrongs name of view's identifier", + briefDescription = "Detects wrong name of view's identifier", explanation = """ Name of identifier should begin with prefix, which depends of view name. http://wiki.omega-r.club/dev-android-code#rec228390320 @@ -59,54 +60,19 @@ class NameIdentifierXmlDetector : ResourceXmlDetector() { val attributeValue = attribute.nodeValue ?: return if (attribute.name == "android:id") { when (attribute.ownerElement.tagName) { - TEXT_VIEW_ELEMENT -> { - if (!attributeValue.contains(TEXT_VIEW_PREFIX)) { - makeContextReport(context, attribute, TEXT_VIEW_PREFIX) - } - } - - IMAGE_VIEW_ELEMENT -> { - if (!attributeValue.contains(IMAGE_VIEW_PREFIX)) { - makeContextReport(context, attribute, IMAGE_VIEW_PREFIX) - } - } - - BUTTON_ELEMENT -> { - if (!attributeValue.contains(BUTTON_PREFIX)) { - makeContextReport(context, attribute, BUTTON_PREFIX) - } - } - - EDIT_TEXT_ELEMENT -> { - if (!attributeValue.contains(EDIT_TEXT_PREFIX)) { - makeContextReport(context, attribute, EDIT_TEXT_PREFIX) - } - } - + TEXT_VIEW_ELEMENT -> checkPrefix(context, attributeValue, attribute, TEXT_VIEW_PREFIX) + IMAGE_VIEW_ELEMENT -> checkPrefix(context, attributeValue, attribute, IMAGE_VIEW_PREFIX) + BUTTON_ELEMENT -> checkPrefix(context, attributeValue, attribute, BUTTON_PREFIX) + EDIT_TEXT_ELEMENT -> checkPrefix(context, attributeValue, attribute, EDIT_TEXT_PREFIX) + FLOATING_ACTION_BUTTON_ELEMENT -> checkPrefix(context, attributeValue, attribute, FLOATING_ACTION_BUTTON_PREFIX) + IMAGE_BUTTON_ELEMENT -> checkPrefix(context, attributeValue, attribute, IMAGE_BUTTON_PREFIX) LAYOUT_ELEMENT, TABLE_LAYOUT_ELEMENT, LINEAR_LAYOUT_ELEMENT -> { - if (!attributeValue.contains(LAYOUT_PREFIX)) { - makeContextReport(context, attribute, LAYOUT_PREFIX) - } - } - - FLOATING_ACTION_BUTTON_ELEMENT -> { - if (!attributeValue.contains(FLOATING_ACTION_BUTTON_PREFIX)) { - makeContextReport(context, attribute, FLOATING_ACTION_BUTTON_PREFIX) - } - } - - IMAGE_BUTTON_ELEMENT -> { - if (!attributeValue.contains(IMAGE_BUTTON_PREFIX)) { - makeContextReport(context, attribute, IMAGE_BUTTON_PREFIX) - } + checkPrefix(context, attributeValue, attribute, LAYOUT_PREFIX) } - else -> { val tagName = IDENTIFIER_PREFIX + attribute.ownerElement.tagName.split(".").last().replace("View", "") val prefix = tagName.convertCamelToSnakeCase() - if (!attributeValue.contains(prefix)) { - makeContextReport(context, attribute, prefix) - } + checkPrefix(context, attributeValue, attribute, prefix) } } } @@ -116,25 +82,52 @@ class NameIdentifierXmlDetector : ResourceXmlDetector() { private fun String.convertCamelToSnakeCase(): String { return CAMEL_REGEX.replace(this) { it.value - }.toLowerCase() + }.toLowerCase(Locale.ROOT) + } + + private fun checkPrefix(context: XmlContext, attributeValue: String, attribute: Attr, prefix: String) { + if (!attributeValue.contains(prefix) && attributeValue.replace("_", "").contains(prefix)) { + makeContextReport(context, attribute, prefix) { + createFixForUnderscore(attributeValue, prefix) + } + } + else if (!attributeValue.contains(prefix)) { + makeContextReport(context, attribute, prefix) { + createFix(attributeValue, prefix) + } + } } - private fun makeContextReport(context: XmlContext, attribute: Attr, message: String) { + private fun makeContextReport(context: XmlContext, attribute: Attr, message: String, fix:() -> LintFix) { context.report( issue = ISSUE, scope = attribute, location = context.getValueLocation(attribute), message = "$REPORT_MESSAGE $message\n${ISSUE.getExplanation(TextFormat.TEXT)}", - quickfixData = createFix(attribute.nodeValue, message) + quickfixData = fix() ) } - private fun createFix(attributeValue: String, correctPrefix: String): LintFix { + private fun createFixForUnderscore(attributeValue: String, prefix: String): LintFix { + var finalValue = "" + attributeValue.forEach { char -> + if (!finalValue.contains(prefix) && char != '_' || finalValue.contains(prefix)) { + finalValue += char + } + } + return fix() + .replace() + .text(attributeValue) + .with(finalValue) + .build() + } + + private fun createFix(attributeValue: String, prefix: String): LintFix { val attributeValueWithoutPrefix = attributeValue.replace(IDENTIFIER_PREFIX, "") return fix() .replace() .text(attributeValue) - .with("${correctPrefix}_$attributeValueWithoutPrefix") + .with("${prefix}_$attributeValueWithoutPrefix") .build() } } \ No newline at end of file From a414dae2a7d90fb87220db1eecd534db485134a1 Mon Sep 17 00:00:00 2001 From: Dmitriy Sidukov Date: Thu, 14 Apr 2022 09:19:54 +0300 Subject: [PATCH 11/20] data class check added --- .../MaxMethodCountDetector.kt | 24 ++++++++++--------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/checks/src/main/java/com/omegar/lint/checks/detector/code_guidelines/kotlin_style/restrictions/class_methods_count/MaxMethodCountDetector.kt b/checks/src/main/java/com/omegar/lint/checks/detector/code_guidelines/kotlin_style/restrictions/class_methods_count/MaxMethodCountDetector.kt index 487df81..cd4e7e1 100644 --- a/checks/src/main/java/com/omegar/lint/checks/detector/code_guidelines/kotlin_style/restrictions/class_methods_count/MaxMethodCountDetector.kt +++ b/checks/src/main/java/com/omegar/lint/checks/detector/code_guidelines/kotlin_style/restrictions/class_methods_count/MaxMethodCountDetector.kt @@ -2,11 +2,11 @@ package com.omegar.lint.checks.detector.code_guidelines.kotlin_style.restriction import com.android.tools.lint.client.api.UElementHandler import com.android.tools.lint.detector.api.* - import org.jetbrains.uast.UClass import org.jetbrains.uast.UElement import org.jetbrains.uast.UMethod +@Suppress("UnstableApiUsage") class MaxMethodCountDetector : Detector(), Detector.UastScanner { companion object { /** Issue describing the problem and pointing to the detector implementation */ @@ -27,24 +27,26 @@ class MaxMethodCountDetector : Detector(), Detector.UastScanner { ) ) + private const val DATA_CLASS_FUNCTION_VALUE = "public final fun copy" private const val MAX_METHOD_COUNT = 30 } override fun getApplicableUastTypes(): List> = listOf(UClass::class.java) - override fun createUastHandler(context: JavaContext): UElementHandler { - return object : UElementHandler() { - override fun visitClass(node: UClass) { - val resultMethods = mutableListOf() - node.methods.forEach { - if (!it.isVarArgs && !it.isConstructor) { - resultMethods.add(it) - } + override fun createUastHandler(context: JavaContext) = object : UElementHandler() { + override fun visitClass(node: UClass) { + val resultMethods = mutableListOf() + node.methods.forEach { + if (it.asRenderString().contains(DATA_CLASS_FUNCTION_VALUE)) { + return@visitClass } - if (resultMethods.size > MAX_METHOD_COUNT) { - context.report(ISSUE, node, context.getNameLocation(node), ISSUE.getExplanation(TextFormat.TEXT)) + if (!it.isVarArgs && !it.isConstructor) { + resultMethods.add(it) } } + if (resultMethods.size > MAX_METHOD_COUNT) { + context.report(ISSUE, node, context.getNameLocation(node), ISSUE.getExplanation(TextFormat.TEXT)) + } } } } \ No newline at end of file From 68dba4a4f6cc0b44e138b93b2caef1354ccf0e69 Mon Sep 17 00:00:00 2001 From: Dmitriy Sidukov Date: Thu, 14 Apr 2022 09:56:09 +0300 Subject: [PATCH 12/20] regex updated --- .../field/CompanionObjectFieldsDetector.kt | 56 +++++++++---------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/checks/src/main/java/com/omegar/lint/checks/detector/code_guidelines/kotlin_style/name/field/CompanionObjectFieldsDetector.kt b/checks/src/main/java/com/omegar/lint/checks/detector/code_guidelines/kotlin_style/name/field/CompanionObjectFieldsDetector.kt index 654c9d9..e41a86b 100644 --- a/checks/src/main/java/com/omegar/lint/checks/detector/code_guidelines/kotlin_style/name/field/CompanionObjectFieldsDetector.kt +++ b/checks/src/main/java/com/omegar/lint/checks/detector/code_guidelines/kotlin_style/name/field/CompanionObjectFieldsDetector.kt @@ -5,7 +5,9 @@ import com.android.tools.lint.detector.api.* import org.jetbrains.uast.UClass import org.jetbrains.uast.UElement +import java.util.Locale +@Suppress("UnstableApiUsage") class CompanionObjectFieldsDetector : Detector(), Detector.UastScanner { companion object { /** Issue describing the problem and pointing to the detector implementation */ @@ -14,8 +16,8 @@ class CompanionObjectFieldsDetector : Detector(), Detector.UastScanner { id = "OMEGA_NAME_CONSTANTS_SCREAMING_SNAKE_CASE", briefDescription = "The line size does not match the coding convention", explanation = """ - The immutable fields in the Companion Object and compile-time constants are named in the - SCREAMING_SNAKE_CASE style. + The immutable fields in the Companion Object and compile-time constants + should be named in the SCREAMING_SNAKE_CASE style. http://wiki.omega-r.club/dev-android-code#rec226457239 """, category = Category.CORRECTNESS, @@ -31,38 +33,36 @@ class CompanionObjectFieldsDetector : Detector(), Detector.UastScanner { private const val VAL_LABEL = "val" private const val COMPANION_NAME_LABEL = "Companion" - private val UPPER_REGEX = Regex("""^([A-Z]*_*)*$""") + private val UPPER_REGEX = Regex("^([A-Z0-9]*_*)*$") } override fun getApplicableUastTypes(): List> = listOf(UClass::class.java) - override fun createUastHandler(context: JavaContext): UElementHandler { - return object : UElementHandler() { - override fun visitClass(node: UClass) { - val innerClass = node.innerClasses.firstOrNull() ?: return - val name = innerClass.name ?: return + override fun createUastHandler(context: JavaContext) = object : UElementHandler() { + override fun visitClass(node: UClass) { + val innerClass = node.innerClasses.firstOrNull() ?: return + val name = innerClass.name ?: return - if (name != COMPANION_NAME_LABEL) { - return - } + if (name != COMPANION_NAME_LABEL) { + return + } - val declarations = innerClass.uastDeclarations.distinctBy { it.text } - - declarations.forEach { declaration -> - val text = declaration.text ?: return - val lines = text.lines() - lines.forEach { line -> - val identifierName = getName(line) - if (identifierName.isNotEmpty() && !identifierName.matches(UPPER_REGEX)) { - context.report( - ISSUE, - node, - context.getNameLocation(declaration), - "$line\n${ISSUE.getExplanation(TextFormat.TEXT)}", - createLintFix(identifierName) - ) - } + val declarations = innerClass.uastDeclarations.distinctBy { it.text } + + declarations.forEach { declaration -> + val text = declaration.text ?: return + val lines = text.lines() + lines.forEach { line -> + val identifierName = getName(line) + if (identifierName.isNotEmpty() && !identifierName.matches(UPPER_REGEX)) { + context.report( + ISSUE, + node, + context.getNameLocation(declaration), + "$line\n${ISSUE.getExplanation(TextFormat.TEXT)}", + createLintFix(identifierName) + ) } } } @@ -87,7 +87,7 @@ class CompanionObjectFieldsDetector : Detector(), Detector.UastScanner { return LintFix.create() .replace() .text(name) - .with(name.toUpperCase()) + .with(name.toUpperCase(Locale.ROOT)) .build() } } From 5b1feb1fd7515f001d7dcfe4668cfd99ce6bfcb1 Mon Sep 17 00:00:00 2001 From: Dmitriy Sidukov Date: Thu, 14 Apr 2022 10:57:26 +0300 Subject: [PATCH 13/20] format field added --- .../name_resource/resource/NameResourceStringXmlDetector.kt | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/checks/src/main/java/com/omegar/lint/checks/detector/code_guidelines/xml_style/name_resource/resource/NameResourceStringXmlDetector.kt b/checks/src/main/java/com/omegar/lint/checks/detector/code_guidelines/xml_style/name_resource/resource/NameResourceStringXmlDetector.kt index 7e6dc09..34a6599 100644 --- a/checks/src/main/java/com/omegar/lint/checks/detector/code_guidelines/xml_style/name_resource/resource/NameResourceStringXmlDetector.kt +++ b/checks/src/main/java/com/omegar/lint/checks/detector/code_guidelines/xml_style/name_resource/resource/NameResourceStringXmlDetector.kt @@ -5,6 +5,7 @@ import com.android.tools.lint.detector.api.* import org.w3c.dom.Element import org.w3c.dom.Node +@Suppress("UnstableApiUsage") class NameResourceStringXmlDetector : ResourceXmlDetector() { companion object { @@ -32,7 +33,8 @@ class NameResourceStringXmlDetector : ResourceXmlDetector() { "label", "button", "action", - "hint" + "hint", + "format" ) private const val ATTRIBUTE_NAME_VAL = "name" //Attribute @@ -54,7 +56,7 @@ class NameResourceStringXmlDetector : ResourceXmlDetector() { val stringText = element.getAttribute(ATTRIBUTE_NAME_VAL) ?: return - if ((stringText == APP_NAME) || CORRECT_PREFIXES_LIST.firstOrNull() { stringText.contains(it) } != null) { + if ((stringText == APP_NAME) || CORRECT_PREFIXES_LIST.firstOrNull { stringText.contains(it) } != null) { return } From b74aff568220f6857de3af227247314d6d6a6242 Mon Sep 17 00:00:00 2001 From: Dmitriy Sidukov Date: Fri, 15 Apr 2022 09:51:42 +0300 Subject: [PATCH 14/20] mask field added --- .../name_resource/resource/NameResourceStringXmlDetector.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/checks/src/main/java/com/omegar/lint/checks/detector/code_guidelines/xml_style/name_resource/resource/NameResourceStringXmlDetector.kt b/checks/src/main/java/com/omegar/lint/checks/detector/code_guidelines/xml_style/name_resource/resource/NameResourceStringXmlDetector.kt index 34a6599..45959fa 100644 --- a/checks/src/main/java/com/omegar/lint/checks/detector/code_guidelines/xml_style/name_resource/resource/NameResourceStringXmlDetector.kt +++ b/checks/src/main/java/com/omegar/lint/checks/detector/code_guidelines/xml_style/name_resource/resource/NameResourceStringXmlDetector.kt @@ -34,7 +34,8 @@ class NameResourceStringXmlDetector : ResourceXmlDetector() { "button", "action", "hint", - "format" + "format", + "mask" ) private const val ATTRIBUTE_NAME_VAL = "name" //Attribute From 418264683c09bc20b71c4705b5416c4c67f230ba Mon Sep 17 00:00:00 2001 From: Dmitriy Sidukov Date: Tue, 19 Apr 2022 10:08:24 +0300 Subject: [PATCH 15/20] field get set method excluded --- .../MaxMethodCountDetector.kt | 21 +++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/checks/src/main/java/com/omegar/lint/checks/detector/code_guidelines/kotlin_style/restrictions/class_methods_count/MaxMethodCountDetector.kt b/checks/src/main/java/com/omegar/lint/checks/detector/code_guidelines/kotlin_style/restrictions/class_methods_count/MaxMethodCountDetector.kt index cd4e7e1..f2f6023 100644 --- a/checks/src/main/java/com/omegar/lint/checks/detector/code_guidelines/kotlin_style/restrictions/class_methods_count/MaxMethodCountDetector.kt +++ b/checks/src/main/java/com/omegar/lint/checks/detector/code_guidelines/kotlin_style/restrictions/class_methods_count/MaxMethodCountDetector.kt @@ -2,9 +2,7 @@ package com.omegar.lint.checks.detector.code_guidelines.kotlin_style.restriction import com.android.tools.lint.client.api.UElementHandler import com.android.tools.lint.detector.api.* -import org.jetbrains.uast.UClass -import org.jetbrains.uast.UElement -import org.jetbrains.uast.UMethod +import org.jetbrains.uast.* @Suppress("UnstableApiUsage") class MaxMethodCountDetector : Detector(), Detector.UastScanner { @@ -28,6 +26,10 @@ class MaxMethodCountDetector : Detector(), Detector.UastScanner { ) private const val DATA_CLASS_FUNCTION_VALUE = "public final fun copy" + private const val VAR_LABEL = "var" + private const val VAL_LABEL = "val" + private const val METHOD_GET = "get" + private const val METHOD_SET = "set" private const val MAX_METHOD_COUNT = 30 } @@ -36,6 +38,17 @@ class MaxMethodCountDetector : Detector(), Detector.UastScanner { override fun createUastHandler(context: JavaContext) = object : UElementHandler() { override fun visitClass(node: UClass) { val resultMethods = mutableListOf() + val text = node.uastDeclarations.distinctBy { it.text } + var getSetCount = 0 + + text.forEachIndexed { _, uDeclaration -> + if (uDeclaration.text.contains(VAL_LABEL) && uDeclaration.text.contains(METHOD_GET)) { + getSetCount++ + } else if (uDeclaration.text.contains(VAR_LABEL)) { + if (uDeclaration.text.contains(METHOD_GET)) getSetCount++ + if (uDeclaration.text.contains(METHOD_SET)) getSetCount++ + } + } node.methods.forEach { if (it.asRenderString().contains(DATA_CLASS_FUNCTION_VALUE)) { return@visitClass @@ -44,7 +57,7 @@ class MaxMethodCountDetector : Detector(), Detector.UastScanner { resultMethods.add(it) } } - if (resultMethods.size > MAX_METHOD_COUNT) { + if (resultMethods.size - getSetCount > MAX_METHOD_COUNT) { context.report(ISSUE, node, context.getNameLocation(node), ISSUE.getExplanation(TextFormat.TEXT)) } } From 617c83afb9701d68ec44cd495fd8a6fde208f8fd Mon Sep 17 00:00:00 2001 From: Dmitriy Sidukov Date: Tue, 19 Apr 2022 10:40:01 +0300 Subject: [PATCH 16/20] lexical changes --- .../MaxMethodCountDetector.kt | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/checks/src/main/java/com/omegar/lint/checks/detector/code_guidelines/kotlin_style/restrictions/class_methods_count/MaxMethodCountDetector.kt b/checks/src/main/java/com/omegar/lint/checks/detector/code_guidelines/kotlin_style/restrictions/class_methods_count/MaxMethodCountDetector.kt index f2f6023..36fd3b6 100644 --- a/checks/src/main/java/com/omegar/lint/checks/detector/code_guidelines/kotlin_style/restrictions/class_methods_count/MaxMethodCountDetector.kt +++ b/checks/src/main/java/com/omegar/lint/checks/detector/code_guidelines/kotlin_style/restrictions/class_methods_count/MaxMethodCountDetector.kt @@ -26,8 +26,8 @@ class MaxMethodCountDetector : Detector(), Detector.UastScanner { ) private const val DATA_CLASS_FUNCTION_VALUE = "public final fun copy" - private const val VAR_LABEL = "var" - private const val VAL_LABEL = "val" + private const val KEYWORD_VAR = "var" + private const val KEYWORD_VAL = "val" private const val METHOD_GET = "get" private const val METHOD_SET = "set" private const val MAX_METHOD_COUNT = 30 @@ -39,14 +39,14 @@ class MaxMethodCountDetector : Detector(), Detector.UastScanner { override fun visitClass(node: UClass) { val resultMethods = mutableListOf() val text = node.uastDeclarations.distinctBy { it.text } - var getSetCount = 0 + var propertyCount = 0 text.forEachIndexed { _, uDeclaration -> - if (uDeclaration.text.contains(VAL_LABEL) && uDeclaration.text.contains(METHOD_GET)) { - getSetCount++ - } else if (uDeclaration.text.contains(VAR_LABEL)) { - if (uDeclaration.text.contains(METHOD_GET)) getSetCount++ - if (uDeclaration.text.contains(METHOD_SET)) getSetCount++ + if (uDeclaration.text.contains(KEYWORD_VAL) && uDeclaration.text.contains(METHOD_GET)) { + propertyCount++ + } else if (uDeclaration.text.contains(KEYWORD_VAR)) { + if (uDeclaration.text.contains(METHOD_GET)) propertyCount++ + if (uDeclaration.text.contains(METHOD_SET)) propertyCount++ } } node.methods.forEach { @@ -57,7 +57,7 @@ class MaxMethodCountDetector : Detector(), Detector.UastScanner { resultMethods.add(it) } } - if (resultMethods.size - getSetCount > MAX_METHOD_COUNT) { + if (resultMethods.size - propertyCount > MAX_METHOD_COUNT) { context.report(ISSUE, node, context.getNameLocation(node), ISSUE.getExplanation(TextFormat.TEXT)) } } From bac80f0fd397022139346754e2f82f3fe1e28bfd Mon Sep 17 00:00:00 2001 From: Dmitriy Sidukov Date: Wed, 20 Apr 2022 10:48:38 +0300 Subject: [PATCH 17/20] colon added --- .../name/abbreviation/AbbreviationDetector.kt | 82 +++++++++---------- 1 file changed, 38 insertions(+), 44 deletions(-) diff --git a/checks/src/main/java/com/omegar/lint/checks/detector/code_guidelines/kotlin_style/name/abbreviation/AbbreviationDetector.kt b/checks/src/main/java/com/omegar/lint/checks/detector/code_guidelines/kotlin_style/name/abbreviation/AbbreviationDetector.kt index e81d6d1..b0c2778 100644 --- a/checks/src/main/java/com/omegar/lint/checks/detector/code_guidelines/kotlin_style/name/abbreviation/AbbreviationDetector.kt +++ b/checks/src/main/java/com/omegar/lint/checks/detector/code_guidelines/kotlin_style/name/abbreviation/AbbreviationDetector.kt @@ -12,7 +12,7 @@ class AbbreviationDetector : Detector(), Detector.UastScanner { @JvmField val ISSUE: Issue = Issue.create( id = "OMEGA_ABBREVIATION_AS_WORD", - briefDescription = "Use this abbreviation does not match the coding convention", + briefDescription = "Use of this abbreviation does not match the coding convention", explanation = """ Don't use abbreviations. http://wiki.omega-r.club/dev-android-code#rec228153340 @@ -25,69 +25,63 @@ class AbbreviationDetector : Detector(), Detector.UastScanner { ) ) - private val ABBREVIATION_REGEX = Regex("""[A-Z][A-Z]""") - private val ANNOTATION_REGEX = Regex("""^@""") - private const val OPEN_SCOPE_LABEL = "(" - private const val OPEN_TAG_LABEL = "<" - private const val EQUAL_LABEL = "=" + private val ABBREVIATION_REGEX = Regex("[A-Z][A-Z]") + private val ANNOTATION_REGEX = Regex("^@") private const val SPACE_LABEL = " " //exclusion - private const val MILLISECONDS_LABEL = "MSec" - private const val U_ELEMENT_LABEL = "UElement" - private const val TODO_LABEL = "TODO" private const val CLASS_LABEL = "class" private const val ENUM_LABEL = "enum class" val exclusionsList = listOf( - MILLISECONDS_LABEL, - TODO_LABEL, - U_ELEMENT_LABEL + "MSec", + "TODO", + "UElement" + ) + val specialSymbolList = listOf( + "(", + "<", + "=", + ":" ) - } override fun getApplicableUastTypes(): List> = listOf(UDeclaration::class.java) - override fun createUastHandler(context: JavaContext): UElementHandler { - return object : UElementHandler() { - override fun visitDeclaration(node: UDeclaration) { - val parent = node.parent ?: return - val fileLines = parent.text.lines() - val nameLine = fileLines.firstOrNull { it.contains(CLASS_LABEL) } + override fun createUastHandler(context: JavaContext) = object : UElementHandler() { + override fun visitDeclaration(node: UDeclaration) { + val parent = node.parent ?: return + val nameLine = parent.text.lines().firstOrNull { it.contains(CLASS_LABEL) } - if (nameLine != null && nameLine.contains(ENUM_LABEL)) { - return - } + if (nameLine != null && nameLine.contains(ENUM_LABEL)) { + return + } - val lines = node.text?.lines() ?: return + val lines = node.text?.lines() ?: return - var checkText = getNameString(lines) ?: return + var checkText = getNameString(lines) ?: return - checkText = deleteAfterSymbol(checkText, EQUAL_LABEL) - checkText = deleteAfterSymbol(checkText, OPEN_SCOPE_LABEL) - checkText = deleteAfterSymbol(checkText, OPEN_TAG_LABEL) - - checkText = deleteExclusions(checkText) - - if (checkText.contains(ABBREVIATION_REGEX) && !node.isStatic) { - context.report( - ISSUE, - node as UElement, - context.getNameLocation(node), - checkText + "\n" + ISSUE.getExplanation(TextFormat.TEXT), - createLintFix(checkText) - ) - } - } - } + checkText = deleteSpecialSymbols(checkText) + checkText = deleteExclusions(checkText) + if (checkText.contains(ABBREVIATION_REGEX) && !node.isStatic) { + context.report( + ISSUE, + node as UElement, + context.getNameLocation(node), + checkText + "\n" + ISSUE.getExplanation(TextFormat.TEXT), + createLintFix(checkText) + ) + } + } } - private fun deleteAfterSymbol(checkText: String, symbol: String): String { + private fun deleteSpecialSymbols(checkText: String): String { var text = checkText - if (text.indexOf(symbol) > 0) { - text = checkText.substring(0, checkText.indexOf(symbol)) + specialSymbolList.forEach { symbol -> + if (text.contains(symbol) && text.indexOf(symbol) > 0) { + text = checkText.substring(0, checkText.indexOf(symbol)) + } } return text } From 0eaac4979102cecb601120bad87ef67487281724 Mon Sep 17 00:00:00 2001 From: Dmitriy Sidukov Date: Tue, 26 Apr 2022 09:47:35 +0300 Subject: [PATCH 18/20] file order detection and refactoring --- .../omegar/lint/checks/LintIssueRegistry.kt | 9 +- .../ComponentPositionDetector.kt | 217 ++++++++++++++++++ .../ComponentPositionDetector.kt | 207 ----------------- .../file_package/PackageComponentDetector.kt | 79 +++++++ ...etector.kt => ArgumentPositionDetector.kt} | 5 +- 5 files changed, 304 insertions(+), 213 deletions(-) create mode 100644 checks/src/main/java/com/omegar/lint/checks/detector/code_guidelines/kotlin_style/order/class_interface/ComponentPositionDetector.kt delete mode 100644 checks/src/main/java/com/omegar/lint/checks/detector/code_guidelines/kotlin_style/order/file_class_interface/ComponentPositionDetector.kt create mode 100644 checks/src/main/java/com/omegar/lint/checks/detector/code_guidelines/kotlin_style/order/file_package/PackageComponentDetector.kt rename checks/src/main/java/com/omegar/lint/checks/detector/code_guidelines/kotlin_style/order/function_params/{PositionArgumentDetector.kt => ArgumentPositionDetector.kt} (94%) diff --git a/checks/src/main/java/com/omegar/lint/checks/LintIssueRegistry.kt b/checks/src/main/java/com/omegar/lint/checks/LintIssueRegistry.kt index d181bba..2400a86 100644 --- a/checks/src/main/java/com/omegar/lint/checks/LintIssueRegistry.kt +++ b/checks/src/main/java/com/omegar/lint/checks/LintIssueRegistry.kt @@ -11,8 +11,9 @@ import com.omegar.lint.checks.detector.code_guidelines.kotlin_style.elements_for import com.omegar.lint.checks.detector.code_guidelines.kotlin_style.name.`class`.NameFileSufixDetector import com.omegar.lint.checks.detector.code_guidelines.kotlin_style.name.abbreviation.AbbreviationDetector import com.omegar.lint.checks.detector.code_guidelines.kotlin_style.name.field.CompanionObjectFieldsDetector -import com.omegar.lint.checks.detector.code_guidelines.kotlin_style.order.file_class_interface.ComponentPositionDetector -import com.omegar.lint.checks.detector.code_guidelines.kotlin_style.order.function_params.PositionArgumentDetector +import com.omegar.lint.checks.detector.code_guidelines.kotlin_style.order.class_interface.ComponentPositionDetector +import com.omegar.lint.checks.detector.code_guidelines.kotlin_style.order.file_package.PackageComponentDetector +import com.omegar.lint.checks.detector.code_guidelines.kotlin_style.order.function_params.ArgumentPositionDetector import com.omegar.lint.checks.detector.code_guidelines.kotlin_style.restrictions.class_length.MaxClassLengthDetector import com.omegar.lint.checks.detector.code_guidelines.kotlin_style.restrictions.class_methods_count.MaxMethodCountDetector import com.omegar.lint.checks.detector.code_guidelines.kotlin_style.restrictions.classes_in_package_count.MaxClassInPackageDetector @@ -31,15 +32,17 @@ import com.omegar.lint.checks.detector.code_guidelines.xml_style.name_resource.t import com.omegar.lint.checks.detector.project_guidelines.file_name.`class`.NameFileUpperCamelCaseDetector import com.omegar.lint.checks.detector.project_guidelines.file_name.resource.layout.NameResourceLayoutDetector +@Suppress("UnstableApiUsage") class LintIssueRegistry : IssueRegistry() { override val issues: List get() = listOf( NameFileUpperCamelCaseDetector.ISSUE, AbbreviationDetector.ISSUE, // TODO need rewriting & lint quick fix - PositionArgumentDetector.ISSUE, // TODO add lint quick fix + ArgumentPositionDetector.ISSUE, // TODO add lint quick fix MaxFunctionsArgumentsDetector.ISSUE, // TODO ??lint quick fix?? ExceptionCatchDetector.ISSUE, ComponentPositionDetector.ISSUE, //TODO add lint quick fix for + PackageComponentDetector.ISSUE, NameIdentifierXmlDetector.ISSUE, NameResourceStringXmlDetector.ISSUE, NameResourceStyleXmlDetector.ISSUE, //TODO change lint quick fix for diff --git a/checks/src/main/java/com/omegar/lint/checks/detector/code_guidelines/kotlin_style/order/class_interface/ComponentPositionDetector.kt b/checks/src/main/java/com/omegar/lint/checks/detector/code_guidelines/kotlin_style/order/class_interface/ComponentPositionDetector.kt new file mode 100644 index 0000000..85491d5 --- /dev/null +++ b/checks/src/main/java/com/omegar/lint/checks/detector/code_guidelines/kotlin_style/order/class_interface/ComponentPositionDetector.kt @@ -0,0 +1,217 @@ +package com.omegar.lint.checks.detector.code_guidelines.kotlin_style.order.class_interface + +import com.android.tools.lint.client.api.UElementHandler +import com.android.tools.lint.detector.api.* +import org.jetbrains.uast.* +import java.lang.Integer.min + +@Suppress("UnstableApiUsage") +class ComponentPositionDetector : Detector(), Detector.UastScanner { + companion object { + /** Issue describing the problem and pointing to the detector implementation */ + @JvmField + val ISSUE: Issue = Issue.create( + id = "OMEGA_USE_CLASS_COMPONENTS_IN_CORRECT_ORDER", + briefDescription = "Place class members in correct order", + explanation = """ + Order warning. + http://wiki.omega-r.club/dev-android-code#rec228155171 + """, + category = Category.CORRECTNESS, + priority = 7, + severity = Severity.WARNING, + implementation = Implementation( + ComponentPositionDetector::class.java, + Scope.JAVA_FILE_SCOPE + ) + ) + + private const val COMPANION_NAME = "Companion" + private const val MODIFIERS_RANK_MESSAGE = + "Class members with access modifiers should be positioned in the following order:\n" + + "abstract, override, public, internal, protected, private.\n" + + // Access modifiers rank + private val modifiers = arrayOf( + Pair("abstract", 1), + Pair("override", 2), + Pair("public", 3), + Pair("internal", 4), + Pair("protected", 5), + Pair("private", 6), + Pair("", 3), + ) + + // 1. companion object members + private const val COMPANION_OBJECT = "const val" + private const val COMPANION_OBJECT_RANK = 1 + private const val COMPANION_OBJECT_MESSAGE = "Companion object should be the first" + private val COMPANION_OBJECT_REGEX_LIST = makeRegexList(COMPANION_OBJECT) + private val COMPANION_OBJECT_PARAMS = + StaticParams(COMPANION_OBJECT_REGEX_LIST, COMPANION_OBJECT_RANK, modifiers.last().second,COMPANION_OBJECT_MESSAGE) + + // 2. val + var variables + private const val VAL = "val" + private const val VAR = "var" + private const val VARIABLES_RANK = 2 + private const val VARIABLES_MESSAGE = + "Variables should be positioned earlier than constructors, functions, enums, interfaces and classes" + private val VAL_REGEX_LIST = makeRegexList(VAL) + private val VAR_REGEX_LIST = makeRegexList(VAR) + private val VAL_PARAMS = StaticParams(VAL_REGEX_LIST, VARIABLES_RANK, modifiers.last().second, VARIABLES_MESSAGE) + private val VAR_PARAMS = StaticParams(VAR_REGEX_LIST, VARIABLES_RANK, modifiers.last().second, VARIABLES_MESSAGE) + + // 3. constructors and inits + private const val CONSTRUCTOR = "constructor" + private const val CONSTRUCTOR_RANK = 3 + private const val CONSTRUCTOR_MESSAGE = "Constructors should be positioned earlier than functions, enums, interfaces and classes" + private val CONSTRUCTOR_REGEX_LIST = makeRegexList(CONSTRUCTOR) + private val CONSTRUCTOR_PARAMS = StaticParams(CONSTRUCTOR_REGEX_LIST, CONSTRUCTOR_RANK, modifiers.last().second, CONSTRUCTOR_MESSAGE) + + // 4. functions + private const val FUNCTION = "fun" + private const val FUNCTION_RANK = 4 + private const val FUNCTION_MESSAGE = "Functions should be positioned earlier than enums, interfaces and classes" + private val FUNCTION_REGEX_LIST = makeRegexList(FUNCTION) + private val FUNCTION_PARAMS = StaticParams(FUNCTION_REGEX_LIST, FUNCTION_RANK, modifiers.last().second, FUNCTION_MESSAGE) + + // 5. enums + private const val ENUM = "enum" + private const val ENUM_RANK = 5 + private const val ENUM_MESSAGE = "Enums should be positioned earlier than interfaces and classes" + private val ENUM_REGEX_LIST = makeRegexList(ENUM) + private val ENUM_PARAMS = StaticParams(ENUM_REGEX_LIST, ENUM_RANK, modifiers.last().second, ENUM_MESSAGE) + + // 6. interfaces + private const val INTERFACE = "interface" + private const val INTERFACE_RANK = 6 + private const val INTERFACE_MESSAGE = "Interfaces should be positioned earlier than classes" + private val INTERFACE_REGEX_LIST = makeRegexList(INTERFACE) + private val INTERFACE_PARAMS = StaticParams(INTERFACE_REGEX_LIST, INTERFACE_RANK, modifiers.last().second, INTERFACE_MESSAGE) + + // 7. classes + private const val CLASS = "class" + private const val CLASS_RANK = 7 + private val CLASS_REGEX_LIST = makeRegexList(CLASS) + + private fun makeRegexList(value: String): List { + return listOf( + Regex("^${modifiers[0].first} $value"), + Regex("^${modifiers[1].first} $value"), + Regex("^${modifiers[2].first} $value"), + Regex("^${modifiers[3].first} $value"), + Regex("^${modifiers[4].first} $value"), + Regex("^${modifiers[5].first} $value"), + Regex("^${modifiers[6].first}${value}") + ) + } + + } + + /** + * Sorting declarations by file's lines + * node.uastDeclarations gives elements in wrong order + * https://github.com/JetBrains/intellij-community/blob/master/uast/uast-java/src/org/jetbrains/uast/java/declarations/JavaUClass.kt + */ + + override fun getApplicableUastTypes(): List> = listOf(UClass::class.java) + + override fun createUastHandler(context: JavaContext) = object: UElementHandler() { + override fun visitClass(node: UClass) { + val name = node.name ?: return + val lines = node.text.lines().toMutableList().apply { + removeFirst() + removeLast() + removeIf { it == "" } + } + val sortedDeclarationList = getSortedDeclarationList(lines, node.uastDeclarations) + + if (name != COMPANION_NAME) { + var currentRank = Pair(0, 0) + sortedDeclarationList.forEach { declaration -> + val text = declaration.text ?: return + val currentParams = CurrentParams(context, text, node, declaration) + + /** 1) It cannot find companion object*/ + currentRank = checkOrder(currentParams, COMPANION_OBJECT_PARAMS, currentRank) + + /** 2) Variables */ + currentRank = checkOrder(currentParams, VAL_PARAMS, currentRank) + currentRank = checkOrder(currentParams, VAR_PARAMS, currentRank) + + /** 3) Constructors */ + currentRank = checkOrder(currentParams, CONSTRUCTOR_PARAMS, currentRank) + + /** 4 Functions */ + currentRank = checkOrder(currentParams, FUNCTION_PARAMS, currentRank) + + /** 5 Enums */ + currentRank = checkOrder(currentParams, ENUM_PARAMS, currentRank) + + /** 6) Interfaces */ + currentRank = checkOrder(currentParams, INTERFACE_PARAMS, currentRank) + + /** 7) Classes */ + val classRegex = CLASS_REGEX_LIST.firstOrNull { text.contains(it) } + if (classRegex != null) { + currentRank = Pair(CLASS_RANK, modifiers.last().second) + } + } + } + } + } + + private fun getSortedDeclarationList( + lines: List, + listUDeclaration: List, + ): List { + val list = mutableListOf() + lines.forEach { line -> + listUDeclaration.forEach { declaration -> + val text = declaration.text + if (text.substring(0, min(text.length, line.length)).trim() == line.trim()) { + list.add(declaration) + } + } + } + return list + } + + private fun checkOrder(currentParams: CurrentParams, staticParams: StaticParams, currentRank: Pair): Pair { + val isVariable = staticParams.regexList.firstOrNull { currentParams.text.contains(it) } + if (isVariable != null) { + staticParams.modifierRank = modifiers.first { currentParams.text.contains(it.first) }.second + if (currentRank.first <= staticParams.rank && currentRank.second <= staticParams.modifierRank) { + return Pair(staticParams.rank, staticParams.modifierRank) + } else if (currentRank.second > staticParams.modifierRank) { + makeContextReport(currentParams.context, currentParams.node, currentParams.declaration, MODIFIERS_RANK_MESSAGE) + } else { + makeContextReport(currentParams.context, currentParams.node, currentParams.declaration, staticParams.message) + } + } + return currentRank + } + + private fun makeContextReport(context: JavaContext, node: UClass, declaration: UDeclaration, message: String) { + context.report( + ISSUE, + node, + context.getNameLocation(declaration), + "$message. ${ISSUE.getExplanation(TextFormat.TEXT)}" + ) + } + + private class StaticParams( + val regexList: List, + val rank: Int, + var modifierRank: Int, + val message: String + ) + + private class CurrentParams( + val context: JavaContext, + val text: String, + val node: UClass, + val declaration: UDeclaration + ) +} diff --git a/checks/src/main/java/com/omegar/lint/checks/detector/code_guidelines/kotlin_style/order/file_class_interface/ComponentPositionDetector.kt b/checks/src/main/java/com/omegar/lint/checks/detector/code_guidelines/kotlin_style/order/file_class_interface/ComponentPositionDetector.kt deleted file mode 100644 index cff3793..0000000 --- a/checks/src/main/java/com/omegar/lint/checks/detector/code_guidelines/kotlin_style/order/file_class_interface/ComponentPositionDetector.kt +++ /dev/null @@ -1,207 +0,0 @@ -package com.omegar.lint.checks.detector.code_guidelines.kotlin_style.order.file_class_interface - -import com.android.tools.lint.client.api.UElementHandler -import com.android.tools.lint.detector.api.* -import org.jetbrains.uast.* -import java.lang.Integer.min - -class ComponentPositionDetector : Detector(), Detector.UastScanner { - companion object { - /** Issue describing the problem and pointing to the detector implementation */ - @JvmField - val ISSUE: Issue = Issue.create( - id = "OMEGA_USE_COMPONENTS_IN_CORRECT_ORDER", - briefDescription = "The line size does not match the coding convention", - explanation = """ - Order warning. - http://wiki.omega-r.club/dev-android-code#rec228155171 - """, - category = Category.CORRECTNESS, - priority = 7, - severity = Severity.WARNING, - implementation = Implementation( - ComponentPositionDetector::class.java, - Scope.JAVA_FILE_SCOPE - ) - ) - - private const val COMPANION_NAME = "Companion" - - // 1. companion object - private const val COMPANION_OBJECT = "companion object" - private const val COMPANION_OBJECT_RANK = 1 - private const val COMPANION_OBJECT_MESSAGE = "companion object should be the first" - private val COMPANION_OBJECT_REGEX_LIST = makeRegexList(COMPANION_OBJECT) - private val COMPANION_OBJECT_PARAMS = - StaticParams(COMPANION_OBJECT_REGEX_LIST, COMPANION_OBJECT_RANK, COMPANION_OBJECT_MESSAGE) - - // 2. val + var variables - private const val VAL = "val" - private const val VAR = "var" - private const val VARIABLES_RANK = 2 - private const val VARIABLES_MESSAGE = - "Variables should be earlier than constructors, functions, enums, interfaces and classes" - private val VAL_REGEX_LIST = makeRegexList(VAL) - private val VAR_REGEX_LIST = makeRegexList(VAR) - private val VAL_PARAMS = StaticParams(VAL_REGEX_LIST, VARIABLES_RANK, VARIABLES_MESSAGE) - private val VAR_PARAMS = StaticParams(VAR_REGEX_LIST, VARIABLES_RANK, VARIABLES_MESSAGE) - - // 3. constructors and inits - private const val CONSTRUCTOR = "constructor" - private const val CONSTRUCTOR_RANK = 3 - private const val CONSTRUCTOR_MESSAGE = "Constructor should be earlier than functions, enums, interfaces and classes" - private val CONSTRUCTOR_REGEX_LIST = makeRegexList(CONSTRUCTOR) - private val CONSTRUCTOR_PARAMS = StaticParams(CONSTRUCTOR_REGEX_LIST, CONSTRUCTOR_RANK, CONSTRUCTOR_MESSAGE) - - // 4. functions - private const val FUNCTION = "fun" - private const val FUNCTION_RANK = 4 - private const val FUNCTION_MESSAGE = "Functions should be earlier than enums, interfaces and classes" - private val FUNCTION_REGEX_LIST = makeRegexList(FUNCTION) - private val FUNCTION_PARAMS = StaticParams(FUNCTION_REGEX_LIST, FUNCTION_RANK, FUNCTION_MESSAGE) - - // 5. enums - private const val ENUM = "enum" - private const val ENUM_RANK = 5 - private const val ENUM_MESSAGE = "Enum should be earlier than interfaces and classes" - private val ENUM_REGEX_LIST = makeRegexList(ENUM) - private val ENUM_PARAMS = StaticParams(ENUM_REGEX_LIST, ENUM_RANK, ENUM_MESSAGE) - - // 6. interfaces - private const val INTERFACE = "interface" - private const val INTERFACE_RANK = 6 - private const val INTERFACE_MESSAGE = "Enum should be earlier than classes" - private val INTERFACE_REGEX_LIST = makeRegexList(INTERFACE) - private val INTERFACE_PARAMS = StaticParams(INTERFACE_REGEX_LIST, INTERFACE_RANK, INTERFACE_MESSAGE) - - // 7. classes - private const val CLASS = "class" - private const val CLASS_RANK = 7 - private val CLASS_REGEX_LIST = makeRegexList(CLASS) - private val CLASS_PARAMS = StaticParams(CLASS_REGEX_LIST, CLASS_RANK, "") - - private fun makeRegexList(value: String): List { - return listOf( - Regex("^abstract $value"), - Regex("^override $value"), - Regex("^public $value"), - Regex("^internal $value"), - Regex("^protected $value"), - Regex("^private $value"), - Regex("^${value}") - ) - } - - } - - /** - * Sorting declarations by file's lines - * node.uastDeclarations give elements in wrong order - * https://github.com/JetBrains/intellij-community/blob/master/uast/uast-java/src/org/jetbrains/uast/java/declarations/JavaUClass.kt - */ - - override fun getApplicableUastTypes(): List> = listOf(UClass::class.java) - - override fun createUastHandler(context: JavaContext): UElementHandler { - return object : UElementHandler() { - override fun visitClass(node: UClass) { - val name = node.name ?: return - val lines = node.parent?.text?.lines() ?: return - val sortedDeclarationList = getSortedDeclarationList(lines, node.uastDeclarations, name) - - if (name != COMPANION_NAME) { - var currentRank = 0 - sortedDeclarationList.forEach { declaration -> - val text = declaration.text ?: return - val currentParams = CurrentParams(context, text, node, declaration) - - /** 1) it's can find companion object*/ - currentRank = checkOrder(currentParams, COMPANION_OBJECT_PARAMS, currentRank) - - /** 2) Variables*/ - currentRank = checkOrder(currentParams, VAL_PARAMS, currentRank) - currentRank = checkOrder(currentParams, VAR_PARAMS, currentRank) - - /** 3) Constructor */ - currentRank = checkOrder(currentParams, CONSTRUCTOR_PARAMS, currentRank) - - /** 4 Function */ - currentRank = checkOrder(currentParams, FUNCTION_PARAMS, currentRank) - - /** 5 Enum */ - currentRank = checkOrder(currentParams, ENUM_PARAMS, currentRank) - - /** 6) Interface */ - currentRank = checkOrder(currentParams, INTERFACE_PARAMS, currentRank) - - /** 7) Class */ - val classRegex = CLASS_REGEX_LIST.firstOrNull { text.contains(it) } - if (classRegex != null) { - currentRank = CLASS_RANK - } - } - } - } - } - } - - private fun getSortedDeclarationList( - lines: List, - listUDeclaration: List, - name: String - ): List { - val list = mutableListOf() - lines.forEach { line -> - if (line != "") { - listUDeclaration.forEach { declaration -> - val dt = declaration.text - if (dt != null) { - if ((dt.substring(0, min(dt.length, line.length)).trim() == line.trim()) && - !(line.contains("class $name")) - ) { - list.add(declaration) - } - } - } - } - } - - return list.distinctBy { it.text } - } - - private fun checkOrder(currentParams: CurrentParams, staticParams: StaticParams, currentRank: Int): Int { - val isVariable = staticParams.regexList.firstOrNull { currentParams.text.contains(it) } - if (isVariable != null) { - if (currentRank <= staticParams.rank) { - return staticParams.rank - } else { - makeContextReport(currentParams.context, currentParams.node, currentParams.declaration, staticParams.message) - } - } - return currentRank - } - - private fun makeContextReport(context: JavaContext, node: UClass, declaration: UDeclaration, message: String) { - context.report( - ISSUE, - node, - context.getNameLocation(declaration), - "$message. ${ISSUE.getExplanation(TextFormat.TEXT)}" - ) - } - - private class StaticParams( - val regexList: List, - val rank: Int, - val message: String - ) - - private class CurrentParams( - val context: JavaContext, - val text: String, - val node: UClass, - val declaration: UDeclaration - ) -} - - diff --git a/checks/src/main/java/com/omegar/lint/checks/detector/code_guidelines/kotlin_style/order/file_package/PackageComponentDetector.kt b/checks/src/main/java/com/omegar/lint/checks/detector/code_guidelines/kotlin_style/order/file_package/PackageComponentDetector.kt new file mode 100644 index 0000000..8302555 --- /dev/null +++ b/checks/src/main/java/com/omegar/lint/checks/detector/code_guidelines/kotlin_style/order/file_package/PackageComponentDetector.kt @@ -0,0 +1,79 @@ +package com.omegar.lint.checks.detector.code_guidelines.kotlin_style.order.file_package + +import com.android.tools.lint.client.api.UElementHandler +import com.android.tools.lint.detector.api.* +import com.intellij.psi.PsiClass +import com.intellij.psi.PsiField +import org.jetbrains.uast.* + +@Suppress("UnstableApiUsage") +class PackageComponentDetector : Detector(), Detector.UastScanner { + + companion object { + @JvmField + val ISSUE = Issue.create( + id = "OMEGA_USE_FILE_COMPONENTS_IN_CORRECT_ORDER", + briefDescription = "Place file components in correct order", + explanation = """ + Order warning. + http://wiki.omega-r.club/dev-android-code#rec228155171 + """, + category = Category.CORRECTNESS, + priority = 7, + severity = Severity.WARNING, + implementation = Implementation( + PackageComponentDetector::class.java, + Scope.JAVA_FILE_SCOPE + ) + ) + + private const val CLASS_LABEL = "class" + private const val CONSTANT_MESSAGE = "Constants should be positioned before classes, interfaces or any public elements" + private const val ELEMENTS_MESSAGE = "All public elements should be positioned after classes and interfaces" + } + + override fun getApplicableUastTypes(): List> = listOf(UFile::class.java) + + override fun createUastHandler(context: JavaContext) = object : UElementHandler() { + override fun visitFile(node: UFile) { + val publicElements = node.classes.drop(1) + val classes = node.classes.filter { !it.isInterface }.filter { !it.isEnum } + val fields = node.classes[0].fields + + fields.forEach fieldScope@ { uField -> + publicElements.forEach { uElement -> + if (uField.getStartPosition() > uElement.getStartPosition()) { + makeContextReport(context, node, uField, CONSTANT_MESSAGE) + return@fieldScope + } + } + } + + publicElements.forEach elementsScope@ { uElement -> + classes.drop(1).forEach classesScope@ { uClass -> + if (!uClass.text.startsWith(CLASS_LABEL) && !uElement.text.startsWith(CLASS_LABEL)) { + return@classesScope + } + if (!uElement.isInterface && !uElement.text.startsWith(CLASS_LABEL) && uClass.getStartPosition() > uElement.getStartPosition()) { + makeContextReport(context, node, uElement, ELEMENTS_MESSAGE) + return@elementsScope + } + } + } + } + + private fun PsiField.getStartPosition() = context.getNameLocation(this).start?.line ?: 0 + + private fun PsiClass.getStartPosition() = context.getNameLocation(this).start?.line ?: 0 + + } + + private fun makeContextReport(context: JavaContext, node: UFile, declaration: UElement, message: String) { + context.report( + ISSUE, + node, + context.getLocation(declaration), + "$message ${ISSUE.getExplanation(TextFormat.TEXT)}" + ) + } +} \ No newline at end of file diff --git a/checks/src/main/java/com/omegar/lint/checks/detector/code_guidelines/kotlin_style/order/function_params/PositionArgumentDetector.kt b/checks/src/main/java/com/omegar/lint/checks/detector/code_guidelines/kotlin_style/order/function_params/ArgumentPositionDetector.kt similarity index 94% rename from checks/src/main/java/com/omegar/lint/checks/detector/code_guidelines/kotlin_style/order/function_params/PositionArgumentDetector.kt rename to checks/src/main/java/com/omegar/lint/checks/detector/code_guidelines/kotlin_style/order/function_params/ArgumentPositionDetector.kt index 23cefc5..14a0e55 100644 --- a/checks/src/main/java/com/omegar/lint/checks/detector/code_guidelines/kotlin_style/order/function_params/PositionArgumentDetector.kt +++ b/checks/src/main/java/com/omegar/lint/checks/detector/code_guidelines/kotlin_style/order/function_params/ArgumentPositionDetector.kt @@ -2,13 +2,12 @@ package com.omegar.lint.checks.detector.code_guidelines.kotlin_style.order.funct import com.android.tools.lint.client.api.UElementHandler import com.android.tools.lint.detector.api.* -import org.jetbrains.uast.UCallExpression import org.jetbrains.uast.UElement import org.jetbrains.uast.UMethod import org.jetbrains.uast.UParameter @Suppress("UnstableApiUsage") -class PositionArgumentDetector : Detector(), Detector.UastScanner { +class ArgumentPositionDetector : Detector(), Detector.UastScanner { companion object { /** Issue describing the problem and pointing to the detector implementation */ @JvmField @@ -23,7 +22,7 @@ class PositionArgumentDetector : Detector(), Detector.UastScanner { priority = 6, severity = Severity.WARNING, implementation = Implementation( - PositionArgumentDetector::class.java, + ArgumentPositionDetector::class.java, Scope.JAVA_FILE_SCOPE ) ) From 8cb69dde411df96c969683be9eaa4e10044cf2a9 Mon Sep 17 00:00:00 2001 From: Dmitriy Sidukov Date: Mon, 12 Sep 2022 09:17:24 +0300 Subject: [PATCH 19/20] extensions recognition included --- .../name/abbreviation/AbbreviationDetector.kt | 32 ++++++++++++------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/checks/src/main/java/com/omegar/lint/checks/detector/code_guidelines/kotlin_style/name/abbreviation/AbbreviationDetector.kt b/checks/src/main/java/com/omegar/lint/checks/detector/code_guidelines/kotlin_style/name/abbreviation/AbbreviationDetector.kt index b0c2778..58e5ffc 100644 --- a/checks/src/main/java/com/omegar/lint/checks/detector/code_guidelines/kotlin_style/name/abbreviation/AbbreviationDetector.kt +++ b/checks/src/main/java/com/omegar/lint/checks/detector/code_guidelines/kotlin_style/name/abbreviation/AbbreviationDetector.kt @@ -25,9 +25,14 @@ class AbbreviationDetector : Detector(), Detector.UastScanner { ) ) - private val ABBREVIATION_REGEX = Regex("[A-Z][A-Z]") - private val ANNOTATION_REGEX = Regex("^@") - private const val SPACE_LABEL = " " + private val ABBREVIATION_REGEX = Regex("[A-Z][A-Z]") + private val EXTENSIONS_REGEX = listOf( + Regex("fun.*\\."), + Regex("val.*?\\."), + Regex("var.*?\\.") + ) + private val ANNOTATION_REGEX = Regex("^@") + private const val SPACE_LABEL = " " //exclusion private const val CLASS_LABEL = "class" @@ -61,6 +66,7 @@ class AbbreviationDetector : Detector(), Detector.UastScanner { var checkText = getNameString(lines) ?: return + checkText = checkForExtension(checkText) checkText = deleteSpecialSymbols(checkText) checkText = deleteExclusions(checkText) @@ -103,14 +109,10 @@ class AbbreviationDetector : Detector(), Detector.UastScanner { return resultText } - private fun createLintFix(oldName: String): LintFix { - return LintFix.create() - .replace() - .text(oldName) - .with(getNewName(oldName)) - .build() - } - + private fun checkForExtension(checkText: String): String = + EXTENSIONS_REGEX.firstOrNull { checkText.contains(it) }?.let { + checkText.substring(checkText.indexOf('.') + 1) + } ?: run { checkText } private fun getNewName(oldName: String): String { var resultName = "" @@ -129,4 +131,12 @@ class AbbreviationDetector : Detector(), Detector.UastScanner { return resultName } + + private fun createLintFix(oldName: String): LintFix { + return LintFix.create() + .replace() + .text(oldName) + .with(getNewName(oldName)) + .build() + } } From 3cb5250d0c663c94a4e37563b0c77a535f5a25ec Mon Sep 17 00:00:00 2001 From: Dmitriy Sidukov Date: Mon, 12 Sep 2022 09:52:39 +0300 Subject: [PATCH 20/20] code refactored --- .../name/abbreviation/AbbreviationDetector.kt | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/checks/src/main/java/com/omegar/lint/checks/detector/code_guidelines/kotlin_style/name/abbreviation/AbbreviationDetector.kt b/checks/src/main/java/com/omegar/lint/checks/detector/code_guidelines/kotlin_style/name/abbreviation/AbbreviationDetector.kt index 58e5ffc..0db925e 100644 --- a/checks/src/main/java/com/omegar/lint/checks/detector/code_guidelines/kotlin_style/name/abbreviation/AbbreviationDetector.kt +++ b/checks/src/main/java/com/omegar/lint/checks/detector/code_guidelines/kotlin_style/name/abbreviation/AbbreviationDetector.kt @@ -26,11 +26,7 @@ class AbbreviationDetector : Detector(), Detector.UastScanner { ) private val ABBREVIATION_REGEX = Regex("[A-Z][A-Z]") - private val EXTENSIONS_REGEX = listOf( - Regex("fun.*\\."), - Regex("val.*?\\."), - Regex("var.*?\\.") - ) + private val EXTENSION_REGEX = Regex("fun.*\\.|val.*?\\.|var.*?\\.") private val ANNOTATION_REGEX = Regex("^@") private const val SPACE_LABEL = " " @@ -110,9 +106,8 @@ class AbbreviationDetector : Detector(), Detector.UastScanner { } private fun checkForExtension(checkText: String): String = - EXTENSIONS_REGEX.firstOrNull { checkText.contains(it) }?.let { - checkText.substring(checkText.indexOf('.') + 1) - } ?: run { checkText } + if (checkText.contains(EXTENSION_REGEX)) checkText.substring(checkText.indexOf('.') + 1) + else checkText private fun getNewName(oldName: String): String { var resultName = ""