Skip to content

Commit

Permalink
Merge branch 'master' into bugfix/complex-expression-kts#1148
Browse files Browse the repository at this point in the history
  • Loading branch information
petertrr authored Jan 31, 2022
2 parents 85d862a + 297417a commit 425665a
Show file tree
Hide file tree
Showing 18 changed files with 275 additions and 26 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,6 @@ class DiktatRuleSetProvider(private var diktatConfigFile: String = DIKTAT_ANALYS
::BracesInConditionalsAndLoopsRule,
::EmptyBlock,
::AvoidEmptyPrimaryConstructor,
::EnumsSeparated,
::TopLevelOrderRule,
::SingleLineStatementsRule,
::MultipleModifiersSequence,
Expand Down Expand Up @@ -187,6 +186,7 @@ class DiktatRuleSetProvider(private var diktatConfigFile: String = DIKTAT_ANALYS
::NestedFunctionBlock,
::AnnotationNewLineRule,
::SortRule,
::EnumsSeparated,
::StringConcatenationRule,
::StringTemplateFormatRule,
::AccurateCalculationsRule,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,15 @@ import org.cqfn.diktat.ruleset.utils.loopType

import com.pinterest.ktlint.core.ast.ElementType
import com.pinterest.ktlint.core.ast.ElementType.BLOCK
import com.pinterest.ktlint.core.ast.ElementType.CALL_EXPRESSION
import com.pinterest.ktlint.core.ast.ElementType.DO_KEYWORD
import com.pinterest.ktlint.core.ast.ElementType.ELSE_KEYWORD
import com.pinterest.ktlint.core.ast.ElementType.IF
import com.pinterest.ktlint.core.ast.ElementType.IF_KEYWORD
import com.pinterest.ktlint.core.ast.ElementType.LBRACE
import com.pinterest.ktlint.core.ast.ElementType.RBRACE
import com.pinterest.ktlint.core.ast.ElementType.REFERENCE_EXPRESSION
import com.pinterest.ktlint.core.ast.ElementType.SAFE_ACCESS_EXPRESSION
import com.pinterest.ktlint.core.ast.ElementType.WHEN
import com.pinterest.ktlint.core.ast.ElementType.WHILE_KEYWORD
import com.pinterest.ktlint.core.ast.ElementType.WHITE_SPACE
Expand All @@ -30,6 +33,7 @@ import org.jetbrains.kotlin.psi.KtIfExpression
import org.jetbrains.kotlin.psi.KtLoopExpression
import org.jetbrains.kotlin.psi.KtWhenExpression
import org.jetbrains.kotlin.psi.psiUtil.astReplace
import org.jetbrains.kotlin.psi.psiUtil.children

/**
* Rule that checks that all conditionals and loops have braces.
Expand Down Expand Up @@ -90,6 +94,24 @@ class BracesInConditionalsAndLoopsRule(configRules: List<RulesConfig>) : DiktatR
}

if (elseKeyword != null && elseNode?.elementType != IF && elseNode?.elementType != BLOCK) {
// Looking for scope functions, for which we won't trigger
val callAndSafeAccessExpressionChildren = elseNode?.findChildrenMatching {
it.elementType == CALL_EXPRESSION || it.elementType == SAFE_ACCESS_EXPRESSION
}

val scopeFunctionChildren = callAndSafeAccessExpressionChildren?.flatMap {
it.children()
}?.filter {
it.elementType == REFERENCE_EXPRESSION
}

val isNodeHaveScopeFunctionChildren = scopeFunctionChildren?.any {
it.text in scopeFunctions
}
if (isNodeHaveScopeFunctionChildren == true) {
return
}

NO_BRACES_IN_CONDITIONALS_AND_LOOPS.warnAndFix(configRules, emitWarn, isFixMode, "ELSE",
(elseNode?.treeParent?.prevSibling { it.elementType == ELSE_KEYWORD } ?: node).startOffset, node) {
elseNode?.run {
Expand Down Expand Up @@ -171,5 +193,6 @@ class BracesInConditionalsAndLoopsRule(configRules: List<RulesConfig>) : DiktatR
}
companion object {
private const val INDENT_STEP = 4
private val scopeFunctions = listOf("let", "run", "apply", "also")
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,9 @@ import org.jetbrains.kotlin.psi.KtBinaryExpression
import org.jetbrains.kotlin.psi.KtCallExpression
import org.jetbrains.kotlin.psi.KtConstantExpression
import org.jetbrains.kotlin.psi.KtDotQualifiedExpression
import org.jetbrains.kotlin.psi.KtOperationExpression
import org.jetbrains.kotlin.psi.KtExpression
import org.jetbrains.kotlin.psi.KtParenthesizedExpression
import org.jetbrains.kotlin.psi.KtReferenceExpression
import org.jetbrains.kotlin.psi.KtSafeQualifiedExpression
import org.jetbrains.kotlin.psi.KtStringTemplateExpression

/**
Expand Down Expand Up @@ -154,13 +153,14 @@ class StringConcatenationRule(configRules: List<RulesConfig>) : DiktatRule(
// =========== "a " + "b" -> "a b"
val rvalueTextNew = rvalueText?.trim('"')
return "$lvalueText$rvalueTextNew"
} else if (binaryExpressionPsi.isRvalueCallExpression() || binaryExpressionPsi.isRvalueDotQualifiedExpression() ||
binaryExpressionPsi.isRvalueOperation() || binaryExpressionPsi.isRvalueSafeQualifiedExpression()) {
} else if (binaryExpressionPsi.isRvalueCallExpression()) {
// =========== "a " + foo() -> "a ${foo()}}"
return "$lvalueText\${$rvalueText}"
} else if (binaryExpressionPsi.isRvalueReferenceExpression()) {
// =========== "a " + b -> "a $b"
return "$lvalueText$$rvalueText"
} else if (!binaryExpressionPsi.isRvalueParenthesized() && binaryExpressionPsi.isRvalueExpression()) {
return "$lvalueText\${$rvalueText}"
} else if (binaryExpressionPsi.isRvalueParenthesized()) {
val binExpression = binaryExpressionPsi.right?.children?.first()
if (binExpression is KtBinaryExpression) {
Expand Down Expand Up @@ -198,15 +198,9 @@ class StringConcatenationRule(configRules: List<RulesConfig>) : DiktatRule(
private fun KtBinaryExpression.isRvalueReferenceExpression() =
this.right is KtReferenceExpression

private fun KtBinaryExpression.isRvalueDotQualifiedExpression() =
this.right is KtDotQualifiedExpression

private fun KtBinaryExpression.isRvalueParenthesized() =
this.right is KtParenthesizedExpression

private fun KtBinaryExpression.isRvalueOperation() =
this.right is KtOperationExpression

private fun KtBinaryExpression.isLvalueDotQualifiedExpression() =
this.left is KtDotQualifiedExpression

Expand All @@ -219,6 +213,6 @@ class StringConcatenationRule(configRules: List<RulesConfig>) : DiktatRule(
private fun KtBinaryExpression.isLvalueConstantExpression() =
this.left is KtConstantExpression

private fun KtBinaryExpression.isRvalueSafeQualifiedExpression() =
this.right is KtSafeQualifiedExpression
private fun KtBinaryExpression.isRvalueExpression() =
this.right is KtExpression
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,10 @@ import org.jetbrains.kotlin.psi.KtCallExpression
import org.jetbrains.kotlin.psi.KtCallableReferenceExpression
import org.jetbrains.kotlin.psi.KtDotQualifiedExpression
import org.jetbrains.kotlin.psi.KtNameReferenceExpression
import org.jetbrains.kotlin.psi.KtParenthesizedExpression
import org.jetbrains.kotlin.psi.KtProperty
import org.jetbrains.kotlin.psi.KtQualifiedExpression
import org.jetbrains.kotlin.psi.KtReferenceExpression
import org.jetbrains.kotlin.psi.psiUtil.siblings
import org.jetbrains.kotlin.psi.psiUtil.startOffset
import org.slf4j.LoggerFactory
Expand Down Expand Up @@ -145,12 +148,33 @@ class CompactInitialization(configRules: List<RulesConfig>) : DiktatRule(
?: run {
// add apply block
property.node.run {
val newInitializerNode = kotlinParser.createNode("${property.initializer!!.text}.apply {}")
val newInitializerNodeText = buildInitializerNodeText(property)
val newInitializerNode = kotlinParser.createNode(newInitializerNodeText)
replaceChild(property.initializer!!.node, newInitializerNode)
}
(property.initializer as KtDotQualifiedExpression).selectorExpression!! as KtCallExpression
}

@Suppress("UnsafeCallOnNullableType")
private fun buildInitializerNodeText(property: KtProperty): String {
val isRequiresParentheses = property.initializer.let {
// list of expression types, that can be directly followed by a dot-qualified call
// e.g. val x = foo() -> val x = foo().apply {}
// e.g. val x = foo + bar -> val x = (foo + bar).apply {}
it is KtParenthesizedExpression || it is KtQualifiedExpression || it is KtReferenceExpression
}.not()
return buildString {
if (isRequiresParentheses) {
append("(")
}
append(property.initializer!!.text)
if (isRequiresParentheses) {
append(")")
}
append(".apply {}")
}
}

/**
* convert `apply(::foo)` to `apply { foo(this) }` if necessary
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,104 @@ class BracesRuleWarnTest : LintTestBase(::BracesInConditionalsAndLoopsRule) {
)
}

@Test
@Tag(WarningNames.NO_BRACES_IN_CONDITIONALS_AND_LOOPS)
fun `should check braces in if statements - exception for let`() {
lintMethod(
"""
|fun foo() {
| if (a) {
| bar()
| } else b?.let {
| baz()
| }
| ?: run {
| qux()
| }
|}
""".trimMargin()
)
}

@Test
@Tag(WarningNames.NO_BRACES_IN_CONDITIONALS_AND_LOOPS)
fun `should check braces in if statements - exception for let 2`() {
lintMethod(
"""
|fun foo() {
| if (a) {
| bar()
| } else b?.let {
| baz()
| }
|}
""".trimMargin()
)
}

@Test
@Tag(WarningNames.NO_BRACES_IN_CONDITIONALS_AND_LOOPS)
fun `should check braces in if statements - exception for run`() {
lintMethod(
"""
|fun foo() {
| if (a) {
| bar()
| } else b!!.run {
| baz()
| }
|}
""".trimMargin()
)
}

@Test
@Tag(WarningNames.NO_BRACES_IN_CONDITIONALS_AND_LOOPS)
fun `should check braces in if statements - exception for apply`() {
lintMethod(
"""
|fun foo() {
| if (a) {
| bar()
| } else b.apply {
| baz()
| }
|}
""".trimMargin()
)
}

@Test
@Tag(WarningNames.NO_BRACES_IN_CONDITIONALS_AND_LOOPS)
fun `should check braces in if statements, apply exists, but braces are needed`() {
lintMethod(
"""
|fun foo() {
| if (a) {
| bar()
| } else baz(b.apply { id = 5 })
|}
""".trimMargin(),
LintError(4, 7, ruleId, "${NO_BRACES_IN_CONDITIONALS_AND_LOOPS.warnText()} ELSE", true)
)
}

@Test
@Tag(WarningNames.NO_BRACES_IN_CONDITIONALS_AND_LOOPS)
fun `should check braces in if statements, apply exists, but braces are needed 2`() {
lintMethod(
"""
|fun foo() {
| if (a) {
| bar()
| } else
| c.baz(b.apply {id = 5})
|}
""".trimMargin(),
LintError(4, 7, ruleId, "${NO_BRACES_IN_CONDITIONALS_AND_LOOPS.warnText()} ELSE", true)
)
}

@Test
@Tag(WarningNames.NO_BRACES_IN_CONDITIONALS_AND_LOOPS)
fun `should correctly detect single line if`() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,10 @@ class CompactInitializationFixTest : FixTestBase("test/chapter6/compact_initiali
fun `should rename field in apply block to this keyword`() {
fixAndCompare("StatementUseFieldMultipleTimesExpected.kt", "StatementUseFieldMultipleTimesTest.kt")
}

@Test
@Tag(WarningNames.COMPACT_OBJECT_INITIALIZATION)
fun `should wrap receiver in parentheses if required`() {
fixAndCompare("ParenthesizedReceiverExpected.kt", "ParenthesizedReceiverTest.kt")
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
fun `translate text`() {
val res = translateText(text = "dummy")
(res is TranslationsSuccess) shouldBe true
val translationsSuccess = (res as TranslationsSuccess).apply {
translations shouldHaveSize 1}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
fun `translate text`() {
val res = translateText(text = "dummy")
(res is TranslationsSuccess) shouldBe true
val translationsSuccess = res as TranslationsSuccess
translationsSuccess.translations shouldHaveSize 1
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,31 @@ fun foo4() {
if (x > 0) {
} else {
} ;
}
}

fun foo() {
if (a) {
bar()
} else b?.let {
baz()
}
?: run {
qux()
}
}

fun foo() {
if (a) {
bar()
} else b.apply {
baz()
}
}

fun foo() {
if (a) {
bar()
} else {
baz(b.apply { id = 5 })
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,29 @@ fun foo3() {
fun foo4() {
if (x > 0)
else ;
}
}

fun foo() {
if (a) {
bar()
} else b?.let {
baz()
}
?: run {
qux()
}
}

fun foo() {
if (a) {
bar()
} else b.apply {
baz()
}
}

fun foo() {
if (a) {
bar()
} else baz(b.apply { id = 5 })
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,5 @@ val myTest21 = "${x} string"
val myTest22 = "string${foo()}"
val myTest23 = x.toString() + foo()
val myTest24 = foo() + "string"
val myTest25 = "String ${valueStr?.value}"
val myTest25 = "String ${valueStr?.value}"
val myTest26 = "my string ${if (true) "1" else "2"}"
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,4 @@ val myTest22 = "string" + foo()
val myTest23 = x.toString() + foo()
val myTest24 = foo() + "string"
val myTest25 = "String " + valueStr?.value
val myTest26 = "my string " + if (true) "1" else "2"
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,14 @@ enum class IssueType2 {
companion object
}

enum class IssueType3 {
A,
B,
C,
D,
;
}

class Foo {
/**
* @implNote lorem ipsum
Expand Down
Loading

0 comments on commit 425665a

Please sign in to comment.