Skip to content

Commit

Permalink
Merge branch 'master' into feature/runBlocking-async
Browse files Browse the repository at this point in the history
  • Loading branch information
kentr0w authored Jan 18, 2021
2 parents 90d30af + 526bc52 commit fcc5ad2
Show file tree
Hide file tree
Showing 36 changed files with 886 additions and 455 deletions.
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,9 @@ Now diKTat was already added to the lists of [static analysis tools](https://git

## See first

| | | | |
| --- | --- | --- | --- |
|[DiKTat codestyle](info/guide/diktat-coding-convention.md)|[Supported Rules](info/available-rules.md) | [Examples of Usage](https://github.com/akuleshov7/diktat-examples) | [Online Demo](https://ktlint-demo.herokuapp.com) | [Whitepaper](wp/wp.pdf) |
| | | | | |
| --- | --- | --- | --- | --- |
|[DiKTat codestyle](info/guide/diktat-coding-convention.md)|[Supported Rules](info/available-rules.md) | [Examples of Usage](https://github.com/akuleshov7/diktat-examples) | [Online Demo](https://ktlint-demo.herokuapp.com) | [White Paper](wp/wp.pdf) |

## Why should I use diktat in my CI/CD?

Expand Down
3 changes: 3 additions & 0 deletions diktat-analysis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,9 @@
maxParametersInOneLine: 2
# 3 by default.
# maxCallsInOneLine: 3
# Inspection that checks if a long dot qualified expression is used in condition or as an argument
- name: COMPLEX_EXPRESSION
enabled: true
# Checks that there are not too many consecutive spaces in line
- name: TOO_MANY_CONSECUTIVE_SPACES
enabled: true
Expand Down
2 changes: 2 additions & 0 deletions diktat-rules/src/main/kotlin/generated/WarningNames.kt
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,8 @@ public object WarningNames {

public const val WRONG_NEWLINES: String = "WRONG_NEWLINES"

public const val COMPLEX_EXPRESSION: String = "COMPLEX_EXPRESSION"

public const val STRING_CONCATENATION: String = "STRING_CONCATENATION"

public const val TOO_MANY_BLANK_LINES: String = "TOO_MANY_BLANK_LINES"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ enum class Warnings(
LONG_LINE(true, "3.5.1", "this line is longer than allowed"),
REDUNDANT_SEMICOLON(true, "3.6.2", "there should be no redundant semicolon at the end of lines"),
WRONG_NEWLINES(true, "3.6.2", "incorrect line breaking"),
COMPLEX_EXPRESSION(false, "3.6.3", "complex dot qualified expression should be replaced with variable"),

// FixMe: autofixing will be added for this rule
STRING_CONCATENATION(false, "3.15.1", "strings should not be concatenated using plus operator - use string templates instead if the statement fits one line"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,12 @@ class AnnotationNewLineRule(private val configRules: List<RulesConfig>) : Rule("
if (node == node.treeParent.getFirstChildWithType(node.elementType)) {
// Current node is ANNOTATION_ENTRY. treeParent(ModifierList) -> treeParent(PRIMARY_CONSTRUCTOR)
// Checks if there is a white space before grandparent node
if (node.treeParent.treeParent.treePrev.isWhiteSpace()) {
val hasSpaceBeforeGrandparent = node
.treeParent
.treeParent
.treePrev
.isWhiteSpace()
if (hasSpaceBeforeGrandparent) {
(node.treeParent.treeParent.treePrev as LeafPsiElement).replaceWithText("\n")
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -160,9 +160,8 @@ class NullChecksRule(private val configRules: List<RulesConfig>) : Rule("null-ch
}
}

@Suppress("WRONG_INDENTATION")
private fun ASTNode.extractLinesFromBlock(type: IElementType): List<String>? =
treeParent
treeParent
.getFirstChildWithType(type)
?.text
?.trim('{', '}')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,10 @@ class NullableTypeRule(private val configRules: List<RulesConfig>) : Rule("nulla

@Suppress("UnsafeCallOnNullableType")
private fun findFixableParam(node: ASTNode): FixedParam? {
val reference = node.findChildByType(TYPE_REFERENCE)!!.findChildByType(NULLABLE_TYPE)!!.findChildByType(USER_TYPE)?.findChildByType(REFERENCE_EXPRESSION)
val reference = node.findChildByType(TYPE_REFERENCE)!!
.findChildByType(NULLABLE_TYPE)!!
.findChildByType(USER_TYPE)
?.findChildByType(REFERENCE_EXPRESSION)
?: return null
return when (reference.text) {
"Boolean" -> FixedParam(BOOLEAN_CONSTANT, TRUE_KEYWORD, "true")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,13 +91,14 @@ class SmartCastRule(private val configRules: List<RulesConfig>) : Rule("smart-ca
@Suppress("NestedBlockDepth", "TYPE_ALIAS")
private fun handleGroups(groups: Map<KtNameReferenceExpression, List<KtNameReferenceExpression>>) {
groups.keys.forEach {
if (it.node.treeParent.text.contains(" is ")) {
val parentText = it.node.treeParent.text
if (parentText.contains(" is ")) {
groups.getValue(it).forEach { asCall ->
if (asCall.node.hasParent(THEN)) {
raiseWarning(asCall.node)
}
}
} else if (it.node.treeParent.text.contains(" !is ")) {
} else if (parentText.contains(" !is ")) {
groups.getValue(it).forEach { asCall ->
if (asCall.node.hasParent(ELSE)) {
raiseWarning(asCall.node)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import org.cqfn.diktat.common.config.rules.RulesConfig
import org.cqfn.diktat.common.config.rules.getRuleConfig
import org.cqfn.diktat.ruleset.constants.EmitType
import org.cqfn.diktat.ruleset.constants.ListOfList
import org.cqfn.diktat.ruleset.constants.Warnings.COMPLEX_EXPRESSION
import org.cqfn.diktat.ruleset.constants.Warnings.REDUNDANT_SEMICOLON
import org.cqfn.diktat.ruleset.constants.Warnings.WRONG_NEWLINES
import org.cqfn.diktat.ruleset.utils.*
Expand All @@ -18,10 +19,10 @@ import com.pinterest.ktlint.core.ast.ElementType.BLOCK_COMMENT
import com.pinterest.ktlint.core.ast.ElementType.CALLABLE_REFERENCE_EXPRESSION
import com.pinterest.ktlint.core.ast.ElementType.CALL_EXPRESSION
import com.pinterest.ktlint.core.ast.ElementType.CLASS
import com.pinterest.ktlint.core.ast.ElementType.CLASS_BODY
import com.pinterest.ktlint.core.ast.ElementType.COLON
import com.pinterest.ktlint.core.ast.ElementType.COLONCOLON
import com.pinterest.ktlint.core.ast.ElementType.COMMA
import com.pinterest.ktlint.core.ast.ElementType.CONDITION
import com.pinterest.ktlint.core.ast.ElementType.DIV
import com.pinterest.ktlint.core.ast.ElementType.DIVEQ
import com.pinterest.ktlint.core.ast.ElementType.DOT
Expand Down Expand Up @@ -51,19 +52,19 @@ import com.pinterest.ktlint.core.ast.ElementType.PLUS
import com.pinterest.ktlint.core.ast.ElementType.PLUSEQ
import com.pinterest.ktlint.core.ast.ElementType.POSTFIX_EXPRESSION
import com.pinterest.ktlint.core.ast.ElementType.PRIMARY_CONSTRUCTOR
import com.pinterest.ktlint.core.ast.ElementType.RBRACE
import com.pinterest.ktlint.core.ast.ElementType.RETURN
import com.pinterest.ktlint.core.ast.ElementType.RETURN_KEYWORD
import com.pinterest.ktlint.core.ast.ElementType.SAFE_ACCESS
import com.pinterest.ktlint.core.ast.ElementType.SAFE_ACCESS_EXPRESSION
import com.pinterest.ktlint.core.ast.ElementType.SECONDARY_CONSTRUCTOR
import com.pinterest.ktlint.core.ast.ElementType.SEMICOLON
import com.pinterest.ktlint.core.ast.ElementType.SUPER_TYPE_LIST
import com.pinterest.ktlint.core.ast.ElementType.VALUE_ARGUMENT
import com.pinterest.ktlint.core.ast.ElementType.VALUE_ARGUMENT_LIST
import com.pinterest.ktlint.core.ast.ElementType.VALUE_PARAMETER
import com.pinterest.ktlint.core.ast.ElementType.VALUE_PARAMETER_LIST
import com.pinterest.ktlint.core.ast.ElementType.WHEN
import com.pinterest.ktlint.core.ast.ElementType.WHITE_SPACE
import com.pinterest.ktlint.core.ast.isWhiteSpace
import com.pinterest.ktlint.core.ast.isWhiteSpaceWithNewline
import com.pinterest.ktlint.core.ast.nextCodeSibling
import com.pinterest.ktlint.core.ast.parent
Expand Down Expand Up @@ -92,6 +93,7 @@ import org.jetbrains.kotlin.psi.psiUtil.siblings
* 7. Ensures that in multiline lambda newline follows arrow or, in case of lambda without explicit parameters, opening brace
* 8. Checks that functions with single `return` are simplified to functions with expression body
* 9. parameter or argument lists and supertype lists that have more than 2 elements should be separated by newlines
* 10. Complex expression inside condition replaced with new variable
*/
@Suppress("ForbiddenComment")
class NewlinesRule(private val configRules: List<RulesConfig>) : Rule("newlines") {
Expand Down Expand Up @@ -154,18 +156,24 @@ class NewlinesRule(private val configRules: List<RulesConfig>) : Rule("newlines"
}
}

@Suppress("ComplexMethod")
@Suppress("ComplexMethod", "TOO_LONG_FUNCTION")
private fun handleOperatorWithLineBreakBefore(node: ASTNode) {
if (node.isDotFromPackageOrImport()) {
return
}
val isIncorrect = (if (node.elementType == ELVIS) node.treeParent else node).run {
if (isCallsChain()) {
if (node.isInParentheses()) {
COMPLEX_EXPRESSION.warn(configRules, emitWarn, isFixMode, node.text, node.startOffset, node)
}
val isSingleLineIfElse = parent({ it.elementType == IF }, true)?.isSingleLineIfElse() ?: false
// to follow functional style these operators should be started by newline
(isFollowedByNewline() || !isBeginByNewline()) && !isSingleLineIfElse &&
(!isFirstCall() || !isMultilineLambda(treeParent))
} else {
if (isCallsChain(false) && node.isInParentheses()) {
COMPLEX_EXPRESSION.warn(configRules, emitWarn, isFixMode, node.text, node.startOffset, node)
}
// unless statement is simple and on single line, these operators cannot have newline after
isFollowedByNewline() && !isSingleDotStatementOnSingleLine()
}
Expand Down Expand Up @@ -375,11 +383,11 @@ class NewlinesRule(private val configRules: List<RulesConfig>) : Rule("newlines"
if (psi.children.isNotEmpty() && (!psi.isFirstChildElementType(DOT_QUALIFIED_EXPRESSION) &&
!psi.isFirstChildElementType(SAFE_ACCESS_EXPRESSION))) {
val firstChild = psi.firstChild
if (firstChild.isFirstChildElementType(DOT_QUALIFIED_EXPRESSION) ||
firstChild.isFirstChildElementType(SAFE_ACCESS_EXPRESSION)) {
getOrderedCallExpressions(firstChild.firstChild, result)
}
if (firstChild.isFirstChildElementType(POSTFIX_EXPRESSION)) {
if (firstChild.isFirstChildElementType(DOT_QUALIFIED_EXPRESSION) ||
firstChild.isFirstChildElementType(SAFE_ACCESS_EXPRESSION)) {
getOrderedCallExpressions(firstChild.firstChild, result)
}
result.add(firstChild.node)
}
result.add(firstChild.node
Expand Down Expand Up @@ -439,17 +447,23 @@ class NewlinesRule(private val configRules: List<RulesConfig>) : Rule("newlines"
*
* @return true - if there is error, and false if there is no error
*/
private fun ASTNode.isCallsChain() = getCallChain()?.isNotValidCalls(this) ?: false

private fun ASTNode.getCallChain() = getParentExpressions()
.lastOrNull()
?.run {
mutableListOf<ASTNode>().also {
getOrderedCallExpressions(psi, it)
private fun ASTNode.isCallsChain(dropLeadingProperties: Boolean = true) = getCallChain(dropLeadingProperties)?.isNotValidCalls(this) ?: false

private fun ASTNode.getCallChain(dropLeadingProperties: Boolean = true): List<ASTNode>? {
val parentExpressionList = getParentExpressions()
.lastOrNull()
?.run {
mutableListOf<ASTNode>().also {
getOrderedCallExpressions(psi, it)
}
}
return if (dropLeadingProperties) {
// fixme: we can't distinguish fully qualified names (like java.lang) from chain of property accesses (like list.size) for now
parentExpressionList?.dropWhile { !it.treeParent.textContains('(') && !it.treeParent.textContains('{') }
} else {
parentExpressionList
}
// fixme: we can't distinguish fully qualified names (like java.lang) from chain of property accesses (like list.size) for now
?.dropWhile { !it.treeParent.textContains('(') && !it.treeParent.textContains('{') }
}

private fun List<ASTNode>.isNotValidCalls(node: ASTNode): Boolean {
if (this.size == 1) {
Expand Down Expand Up @@ -504,6 +518,15 @@ class NewlinesRule(private val configRules: List<RulesConfig>) : Rule("newlines"
firstChildNode.elementType == IDENTIFIER &&
treeParent.elementType == BINARY_EXPRESSION

/**
* This method checks that complex expression should be replace with new variable
*/
private fun ASTNode.isInParentheses() = parent({it.elementType == DOT_QUALIFIED_EXPRESSION || it.elementType == SAFE_ACCESS_EXPRESSION})
?.treeParent
?.elementType
?.let { it in parenthesesTypes }
?: false

/**
* [RuleConfiguration] for newlines placement
*/
Expand All @@ -527,5 +550,6 @@ class NewlinesRule(private val configRules: List<RulesConfig>) : Rule("newlines"
private val expressionTypes = TokenSet.create(DOT_QUALIFIED_EXPRESSION, SAFE_ACCESS_EXPRESSION, CALLABLE_REFERENCE_EXPRESSION, BINARY_EXPRESSION)
private val chainExpressionTypes = TokenSet.create(DOT_QUALIFIED_EXPRESSION, SAFE_ACCESS_EXPRESSION)
private val dropChainValues = TokenSet.create(EOL_COMMENT, WHITE_SPACE, BLOCK_COMMENT, KDOC)
private val parenthesesTypes = TokenSet.create(CONDITION, WHEN, VALUE_ARGUMENT)
}
}
3 changes: 3 additions & 0 deletions diktat-rules/src/main/resources/diktat-analysis-huawei.yml
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,9 @@
maxSpaces: '1'
# Whether formatting for enums should be kept without checking
saveInitialFormattingForEnums: false
# Inspection that checks if a long dot qualified expression is used in condition or as an argument
- name: COMPLEX_EXPRESSION
enabled: true
# Checks that blank lines are used correctly.
# For example: triggers when there are too many blank lines between function declaration
- name: TOO_MANY_BLANK_LINES
Expand Down
3 changes: 3 additions & 0 deletions diktat-rules/src/main/resources/diktat-analysis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,9 @@
maxSpaces: '1'
# Whether formatting for enums should be kept without checking
saveInitialFormattingForEnums: false
# Inspection that checks if a long dot qualified expression is used in condition or as an argument
- name: COMPLEX_EXPRESSION
enabled: true
# Checks that blank lines are used correctly.
# For example: triggers when there are too many blank lines between function declaration
- name: TOO_MANY_BLANK_LINES
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package org.cqfn.diktat.ruleset.chapter3.files

import org.cqfn.diktat.common.config.rules.RulesConfig
import org.cqfn.diktat.ruleset.constants.Warnings.COMPLEX_EXPRESSION
import org.cqfn.diktat.ruleset.constants.Warnings.REDUNDANT_SEMICOLON
import org.cqfn.diktat.ruleset.constants.Warnings.WRONG_NEWLINES
import org.cqfn.diktat.ruleset.rules.DIKTAT_RULE_SET_ID
Expand All @@ -11,6 +12,7 @@ import com.pinterest.ktlint.core.LintError
import generated.WarningNames
import org.junit.jupiter.api.Disabled
import org.junit.jupiter.api.Tag
import org.junit.jupiter.api.Tags
import org.junit.jupiter.api.Test

@Suppress("LargeClass")
Expand Down Expand Up @@ -905,6 +907,22 @@ class NewlinesRuleWarnTest : LintTestBase(::NewlinesRule) {
)
}

@Test
@Tag(WarningNames.WRONG_NEWLINES)
fun `test for not first prefix`() {
lintMethod(
"""
|fun foo() {
| a().b!!.c()
| a().b.c()!!
|}
""".trimMargin(),
LintError(2, 11, ruleId, "$functionalStyleWarn .", true),
LintError(3, 9, ruleId, "$functionalStyleWarn .", true),
rulesConfigList = rulesConfigListShort
)
}

@Test
@Tag(WarningNames.WRONG_NEWLINES)
fun `test for null safety several lines`() {
Expand Down Expand Up @@ -955,4 +973,39 @@ class NewlinesRuleWarnTest : LintTestBase(::NewlinesRule) {
rulesConfigList = rulesConfigListShort
)
}

@Test
@Tags(Tag(WarningNames.WRONG_NEWLINES), Tag(WarningNames.COMPLEX_EXPRESSION))
fun `complex expression in condition`() {
lintMethod(
"""
|fun foo() {
| if(a.b.c) {}
| while(a?.b?.c) {}
| when(a.b!!.c) {}
| goo(a?.b.c)
|}
""".trimMargin(),
LintError(2, 10, ruleId, "${COMPLEX_EXPRESSION.warnText()} .", false),
LintError(3, 14, ruleId, "${COMPLEX_EXPRESSION.warnText()} ?.", false),
LintError(4, 14, ruleId, "${COMPLEX_EXPRESSION.warnText()} .", false),
LintError(5, 12, ruleId, "${COMPLEX_EXPRESSION.warnText()} .", false),
rulesConfigList = rulesConfigListShort
)
}

@Test
@Tags(Tag(WarningNames.WRONG_NEWLINES), Tag(WarningNames.COMPLEX_EXPRESSION))
fun `complex expression in condition with double warnings`() {
lintMethod(
"""
|fun foo() {
| if(a().b().c()) {}
|}
""".trimMargin(),
LintError(2, 14, ruleId, "${COMPLEX_EXPRESSION.warnText()} .", false),
LintError(2, 14, ruleId, "$functionalStyleWarn .", true),
rulesConfigList = rulesConfigListShort
)
}
}
1 change: 1 addition & 0 deletions info/available-rules.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@
| 3 | 3.5.1 | LONG_LINE | Check: warns if length doesn't exceed the specified length | no | lineLength | handle json method in KDoc |
| 3 | 3.6.2 | REDUNDANT_SEMICOLON | Check: warns if semicolons are used at the end of line.<br>Fix: removes semicolon. | yes | no | - |
| 3 | 3.6.2 | WRONG_NEWLINES | Check: warns if line breaks do not follow code style guid.<br>Fix: fixes incorrect line breaks. | yes | no | - |
| 3 | 3.6.2 | COMPLEX_EXPRESSION | Check: warns if a long dot qualified expression is used in condition or as an argument | no | no | - |
| 3 | 3.15.1 | STRING_CONCATENATION | Check: warns if in a single line concatenation of strings is used | yes | no | - |
| 3 | 3.7.1 | TOO_MANY_BLANK_LINES | Check: warns if blank lines are used placed incorrectly.<br>Fix: removes redundant blank lines. | yes | no | |
| 3 | 3.8.1 | WRONG_WHITESPACE | Check: warns if usage of horizontal spaces violates code style guide.<br>Fix: fixes incorrect whitespaces. | yes | no | - |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ fun generateAvailableRules(rootDir: File, wpDir: File) {
.map { it[2].replace("\\s+".toRegex(), "") to it[5] }
.forEach { ruleMap[it.first]!!.config = it.second}
val newText = File(wpDir, "sections/appendix.tex").readLines().toMutableList()
newText.removeAll(newText.subList(newText.indexOf("\\section*{Available Rules}") + 1, newText.indexOf("\\lstMakeShortInline[basicstyle=\\ttfamily\\bfseries]`")))
newText.removeAll(newText.subList(newText.indexOf("\\section*{Available Rules}") + 1, newText.indexOf("%CodeStyle")))
var index = newText.indexOf("\\section*{Available Rules}") + 1
AUTO_TABLE.trimIndent().lines().forEach { newText.add(index++, it) }
ruleMap.map { it.value }
Expand Down
Loading

0 comments on commit fcc5ad2

Please sign in to comment.