-kotlin-dev-SNAPSHOT`.
-## Legal
+### Legal
This project is not affiliated with nor endorsed by JetBrains.
All code, unless specified otherwise, is licensed under the [MIT](https://opensource.org/licenses/MIT) license.
diff --git a/docs/extensions/badge.md b/docs/extensions/badge.md
index cfb1833e30..40b87210b4 100644
--- a/docs/extensions/badge.md
+++ b/docs/extensions/badge.md
@@ -1,6 +1,6 @@
If you want to display a badge to show that your project is linted and formatted using `'ktlint` than you can add the
-[![ktlint](https://img.shields.io/badge/code%20style-%E2%9D%A4-FF4081.svg)](https://ktlint.github.io/) badge:
+[![ktlint](https://img.shields.io/badge/ktlint%20code--style-%E2%9D%A4-FF4081)](https://pinterest.github.io/ktlint/) badge:
```md title="Ktlint code style badge"
-[![ktlint](https://img.shields.io/badge/code%20style-%E2%9D%A4-FF4081.svg)](https://ktlint.github.io/)
+[![ktlint](https://img.shields.io/badge/ktlint%20code--style-%E2%9D%A4-FF4081)](https://pinterest.github.io/ktlint/)
```
diff --git a/docs/index.md b/docs/index.md
index 02686d46dc..6807013165 100644
--- a/docs/index.md
+++ b/docs/index.md
@@ -1,7 +1,7 @@
# Welcome to Ktlint
@@ -9,7 +9,7 @@
-
+
Kotlin linter in spirit of feross/standard (JavaScript) and gofmt (Go).
diff --git a/docs/install/cli.md b/docs/install/cli.md
index cae9b9515f..b7f123141d 100644
--- a/docs/install/cli.md
+++ b/docs/install/cli.md
@@ -58,7 +58,7 @@ A good starting point is to read the help page:
ktlint --help
```
-When no arguments are specified, the style of all Kotlin files (ending with '.kt' or '.kts') inside the current dir (recursively) are validated with the rules from the [standard ruleset](https://ktlint.github.io/rules/standard/). Hidden folders will be skipped.
+When no arguments are specified, the style of all Kotlin files (ending with '.kt' or '.kts') inside the current dir (recursively) are validated with the rules from the [standard ruleset](https://pinterest.github.io/ktlint/rules/standard/). Hidden folders will be skipped.
```shell title="Default validation with standard ruleset"
ktlint
diff --git a/docs/install/integrations.md b/docs/install/integrations.md
index fb066eb444..c876fe3fb5 100644
--- a/docs/install/integrations.md
+++ b/docs/install/integrations.md
@@ -22,7 +22,7 @@ See [cli usage](../cli) for arguments that can be supplied to `ktlint`.
-
+
@@ -38,7 +38,7 @@ See [cli usage](../cli) for arguments that can be supplied to `ktlint`.
classpathref="maven.plugin.classpath" classname="com.pinterest.ktlint.Main">
-
+
@@ -125,7 +125,7 @@ task ktlint(type: JavaExec, group: "verification") {
classpath = configurations.ktlint
mainClass.set("com.pinterest.ktlint.Main")
args "src/**/*.kt"
- // see https://ktlint.github.io/install/cli/#command-line-usage for more information
+ // see https://pinterest.github.io/ktlint/install/cli/#command-line-usage for more information
}
check.dependsOn ktlint
@@ -134,7 +134,7 @@ task ktlintFormat(type: JavaExec, group: "formatting") {
classpath = configurations.ktlint
mainClass.set("com.pinterest.ktlint.Main")
args "-F", "src/**/*.kt"
- // see https://ktlint.github.io/install/cli/#command-line-usage for more information
+ // see https://pinterest.github.io/ktlint/install/cli/#command-line-usage for more information
}
```
@@ -172,7 +172,7 @@ val ktlintCheck by tasks.creating(JavaExec::class) {
description = "Check Kotlin code style."
classpath = ktlint
mainClass.set("com.pinterest.ktlint.Main")
- // see https://ktlint.github.io/install/cli/#command-line-usage for more information
+ // see https://pinterest.github.io/ktlint/install/cli/#command-line-usage for more information
args = listOf("src/**/*.kt")
}
@@ -183,7 +183,7 @@ val ktlintFormat by tasks.creating(JavaExec::class) {
description = "Fix Kotlin code style deviations."
classpath = ktlint
mainClass.set("com.pinterest.ktlint.Main")
- // see https://ktlint.github.io/install/cli/#command-line-usage for more information
+ // see https://pinterest.github.io/ktlint/install/cli/#command-line-usage for more information
args = listOf("-F", "src/**/*.kt")
}
```
From da28362796d666c7c16f19dc2d8ea05e2564fd50 Mon Sep 17 00:00:00 2001
From: paul-dingemans
Date: Wed, 3 Aug 2022 09:29:23 +0200
Subject: [PATCH 3/7] Extend CLI usage documentation
---
docs/install/cli.md | 111 ++++++++++++++++++++++++++++++++++++++++----
1 file changed, 101 insertions(+), 10 deletions(-)
diff --git a/docs/install/cli.md b/docs/install/cli.md
index b7f123141d..249ef3a5a2 100644
--- a/docs/install/cli.md
+++ b/docs/install/cli.md
@@ -52,11 +52,7 @@ On Arch Linux install package [ktlint AUR](https://aur.archlinux.org/
## Command line usage
-A good starting point is to read the help page:
-
-```shell title="Get help about all available commands"
-ktlint --help
-```
+### Rule set(s)
When no arguments are specified, the style of all Kotlin files (ending with '.kt' or '.kts') inside the current dir (recursively) are validated with the rules from the [standard ruleset](https://pinterest.github.io/ktlint/rules/standard/). Hidden folders will be skipped.
@@ -64,6 +60,32 @@ When no arguments are specified, the style of all Kotlin files (ending with '.kt
ktlint
```
+To validate with the [standard ruleset](https://pinterest.github.io/ktlint/rules/standard/) and the [experimental rulesset](https://pinterest.github.io/ktlint/rules/experimental/) run command below:
+
+```shell title="Validation with standard and experimental ruleset"
+ktlint --experimental
+```
+
+To validate with a [custom ruleset](https://pinterest.github.io/ktlint/extensions/custom-rule-set/) run command below:
+
+```shell title="Validation with standard and a custom ruleset"
+ktlint --ruleset=/path/to/custom-ruleset.jar
+# or
+ktlint -R /path/to/custom-ruleset.jar
+```
+
+### Format (autocorrect)
+
+Most style violations can be corrected automatically. Errors that can not be corrected, are printed to `stderr`.
+
+```shell title="Autocorrect style violations"
+ktlint --format
+# or
+ktlint -F
+```
+
+### Globs
+
Globs can be used to specify more exactly what files and directories are to be validated. `ktlint` uses the [`.gitignore` pattern style syntax for globs](https://git-scm.com/docs/gitignore). Globs are processed from left to right. Prepend a glob with `!` to negate it. Hidden folders will be skipped.
```shell title="Check only certain locations starting from the current directory"
@@ -74,11 +96,7 @@ ktlint "src/**/*.kt" "!src/**/*Test.kt"
ktlint "src/**/*.kt" "!src/**/generated/**"
```
-Most style violations can be corrected automatically. Errors that can not be corrected, are printed to `stderr`.
-
-```shell title="Auto-correct style violations"
-$ ktlint -F
-```
+### Error reporting
`ktlint` supports different type of reporters. When not specified the `plain` reporter is used. Optionally the `plain` reporter can group the violations per file.
@@ -86,6 +104,8 @@ $ ktlint -F
$ ktlint --reporter=plain?group_by_file
```
+Other built-in reporters are: `json`, `sarif`, `checkstyle`, and `html`
+
Style violations can be written to an output file which is convenient when multiple reporters are specified. In example below, the plain reporter is used to write to the console while the checkstyle reports is written to a file:
```shell title="Multiple reporters"
@@ -98,6 +118,51 @@ If resolving all existing errors in a project is unwanted, it is possible to cre
ktlint --baseline=ktlint-baseline.xml # Baseline is created when not existing
```
+### Rule configuration (`.editorconfig`)
+
+Some rules can be tweaked via the [`editorconfig file`](https://pinterest.github.io/ktlint/rules/configuration/).
+
+A scaffold of the `.editorconfig file` can be generated with command below. Note: that the generated file only contains configuration settings which are actively used by the [rules which are loaded](#rule-sets):
+
+```shell title="Generate .editorconfig"
+ktlint generateEditorConfig
+# or
+ktlint --experimental generateEditorConfig
+# or
+ktlint --experimental --ruleset=/path/to/custom-ruleset.jar generateEditorConfig
+```
+
+Normally this file is located in the root of your project directory. In case the file is located in a sub folder of the project, the settings of that file only applies to that subdirectory and its folders (recursively). Ktlint automatically detects and reads all `.editorconfig` files in your project.
+
+With command below, an `editorconfig` file of an alternative location can be used to configure ktlint:
+
+```shell title="Override '.editorconfig'"
+ktlint --editorconfig=/path/to/.editorconfig
+```
+
+!!! warning "Overrides '.editorconfig' in project directory"
+ When specifying this option, all `.editorconfig` files in the project directory are being ignored.
+
+### Stdin && stdout
+
+With command below, the input is read from `stdin` and the violations are printed to `stderr`.
+
+```shell title="Lint from stdin"
+ktlint --stdin
+```
+
+When combined with the `--format` option, the formatted code is written to `stdout` and the violations are printed to `stderr`:
+
+```shell title="Format from stdin and write to stdout"
+ktlint --stdin -F
+```
+
+!!! tip Suppress error output
+ Output printed to `stderr` can be suppressed in different ways. To ignore all error output, add `2> /dev/null` to the end of the command line. Otherwise, specify a [reporter](#error-reporting) to write the error output to a file.
+
+
+### Git hooks
+
Predefined git hooks can be installed, to automatically validate lint errors before commit or push.
```shell title="Install git pre-commit hook"
@@ -108,5 +173,31 @@ ktlint installGitPreCommitHook
ktlint installGitPrePushHook
```
+### Miscellaneous flags and commands
+
+`-a` or `--android`: Turn on Android Kotlin Style Guide compatibility. This flag is most likely to be removed in a future version. Use `.editorconfig ktlint_code_style`(https://pinterest.github.io/ktlint/rules/configuration/#code-style).
+
+`applyToIDEA` or `--apply-to-idea`: Update Intellij IDEA Kotlin codestyle settings (global)
+
+`applyToIDEAProject` or `--apply-to-idea-project`: Update Intellij IDEA project settings
+
+`--color` and `--color-name=`: Make output colorful and optionally set the color name to use.
+
+`--disabled_rules=`: A comma-separated list of rules to globally disable. To disable the standard ktlint rule-set use `--disabled_rules=standard`. This flag is most likely to be removed in a future version. Use `.editorconfig disabled_rules`(https://pinterest.github.io/ktlint/rules/configuration/#disabled-rules).
+
+`-h` or `--help`: Prints help information.
+
+`--limit=`: Maximum number of errors to show (default: show all)
+
+`printAST` or `--print-ast`: Prints AST (useful when writing/debugging rules)
+
+`--relative`: Print files relative to the working directory (e.g. dir/file.kt instead of /home/user/project/dir/file.kt)
+
+`-v`, `--verbose` or `--debug`: Turn on debug output. Also option `--trace` is available, but this is meant for ktlint library developers.
+
+`-V` or `--version`: Prints version information and exit.
+
+### Microsoft Windows users
+
!!! tip "Microsoft Windows"
On Microsoft Windows you'll have to use `java -jar ktlint ...`.
From ff8476f78769a4980b2715445083c690e3514eae Mon Sep 17 00:00:00 2001
From: paul-dingemans
Date: Wed, 3 Aug 2022 09:34:35 +0200
Subject: [PATCH 4/7] Update pull request template
---
.github/PULL_REQUEST_TEMPLATE.md | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
index 56ecc039e4..37743ea23c 100644
--- a/.github/PULL_REQUEST_TEMPLATE.md
+++ b/.github/PULL_REQUEST_TEMPLATE.md
@@ -9,8 +9,9 @@ If the PR solves an issue than provide a link to that issue. -->
- [ ] PR description added
- [ ] tests are added
+- [ ] KtLint has been applied on source code itself and violations are fixed
+- [ ] [documentation](https://pinterest.github.io/ktlint/) is updated
- [ ] `CHANGELOG.md` is updated
In case of adding a new rule:
-- [ ] `README.md` is updated
-- [ ] Rule has been applied on Ktlint itself and violations are fixed
+- [ ] Rule is added to [rules documentation](https://pinterest.github.io/ktlint/rules/standard/)
From 7bf39e5bf0b7c835f2a953e2015e88f8e313244f Mon Sep 17 00:00:00 2001
From: Emil Kantis
Date: Wed, 3 Aug 2022 11:16:46 +0200
Subject: [PATCH 5/7] Trailing commas in enums (#1542)
Co-authored-by: paul-dingemans
---
CHANGELOG.md | 1 +
.../ruleset/standard/TrailingCommaRule.kt | 122 +++++++--
.../ruleset/standard/TrailingCommaRuleTest.kt | 238 ++++++++++++++++++
3 files changed, 338 insertions(+), 23 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 51577e604b..df10aa67f0 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -126,6 +126,7 @@ The callback function provided as parameter to the format function is now called
* When a glob is specified then ensure that it matches files in the current directory and not only in subdirectories of the current directory ([#1533](https://github.com/pinterest/ktlint/issue/1533)).
* Execute `ktlint` cli on default kotlin extensions only when an (existing) path to a directory is given. ([#917](https://github.com/pinterest/ktlint/issue/917)).
* Invoke callback on `format` function for all errors including errors that are autocorrected ([#1491](https://github.com/pinterest/ktlint/issues/1491))
+* Handle trailing comma in enums `trailing-comma` ([#1542](https://github.com/pinterest/ktlint/pull/1542))
### Changed
diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/TrailingCommaRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/TrailingCommaRule.kt
index 54f51b5214..3bf276bf62 100644
--- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/TrailingCommaRule.kt
+++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/TrailingCommaRule.kt
@@ -4,8 +4,22 @@ import com.pinterest.ktlint.core.Rule
import com.pinterest.ktlint.core.api.EditorConfigProperties
import com.pinterest.ktlint.core.api.UsesEditorConfigProperties
import com.pinterest.ktlint.core.ast.ElementType
+import com.pinterest.ktlint.core.ast.ElementType.CLASS
+import com.pinterest.ktlint.core.ast.ElementType.CLASS_BODY
+import com.pinterest.ktlint.core.ast.ElementType.COLLECTION_LITERAL_EXPRESSION
+import com.pinterest.ktlint.core.ast.ElementType.DESTRUCTURING_DECLARATION
+import com.pinterest.ktlint.core.ast.ElementType.ENUM_ENTRY
+import com.pinterest.ktlint.core.ast.ElementType.FUNCTION_LITERAL
+import com.pinterest.ktlint.core.ast.ElementType.INDICES
+import com.pinterest.ktlint.core.ast.ElementType.SEMICOLON
+import com.pinterest.ktlint.core.ast.ElementType.TYPE_ARGUMENT_LIST
+import com.pinterest.ktlint.core.ast.ElementType.TYPE_PARAMETER_LIST
+import com.pinterest.ktlint.core.ast.ElementType.VALUE_ARGUMENT_LIST
+import com.pinterest.ktlint.core.ast.ElementType.VALUE_PARAMETER_LIST
+import com.pinterest.ktlint.core.ast.ElementType.WHEN_ENTRY
import com.pinterest.ktlint.core.ast.children
import com.pinterest.ktlint.core.ast.containsLineBreakInRange
+import com.pinterest.ktlint.core.ast.lineNumber
import com.pinterest.ktlint.core.ast.prevCodeLeaf
import com.pinterest.ktlint.core.ast.prevLeaf
import kotlin.properties.Delegates
@@ -15,8 +29,10 @@ import org.jetbrains.kotlin.com.intellij.lang.ASTNode
import org.jetbrains.kotlin.com.intellij.psi.PsiElement
import org.jetbrains.kotlin.com.intellij.psi.PsiWhiteSpace
import org.jetbrains.kotlin.com.intellij.psi.tree.TokenSet
+import org.jetbrains.kotlin.psi.KtClass
import org.jetbrains.kotlin.psi.KtCollectionLiteralExpression
import org.jetbrains.kotlin.psi.KtDestructuringDeclaration
+import org.jetbrains.kotlin.psi.KtEnumEntry
import org.jetbrains.kotlin.psi.KtFunctionLiteral
import org.jetbrains.kotlin.psi.KtPsiFactory
import org.jetbrains.kotlin.psi.KtValueArgumentList
@@ -47,7 +63,7 @@ private enum class TrailingCommaState {
/**
* The trailing comma isn't needed, but exists
*/
- REDUNDANT,
+ REDUNDANT
;
}
@@ -85,18 +101,16 @@ public class TrailingCommaRule :
// Keep processing of element types in sync with Intellij Kotlin formatting settings.
// https://github.com/JetBrains/intellij-kotlin/blob/master/formatter/src/org/jetbrains/kotlin/idea/formatter/trailingComma/util.kt
when (node.elementType) {
- ElementType.DESTRUCTURING_DECLARATION -> visitDestructuringDeclaration(node, emit, autoCorrect)
- ElementType.FUNCTION_LITERAL -> visitFunctionLiteral(node, emit, autoCorrect)
- ElementType.TYPE_PARAMETER_LIST -> visitTypeList(node, emit, autoCorrect)
- ElementType.VALUE_PARAMETER_LIST -> visitValueList(node, emit, autoCorrect)
- ElementType.WHEN_ENTRY -> visitWhenEntry(node, emit, autoCorrect)
- else -> Unit
- }
- when (node.elementType) {
- ElementType.COLLECTION_LITERAL_EXPRESSION -> visitCollectionLiteralExpression(node, emit, autoCorrect)
- ElementType.INDICES -> visitIndices(node, emit, autoCorrect)
- ElementType.TYPE_ARGUMENT_LIST -> visitTypeList(node, emit, autoCorrect)
- ElementType.VALUE_ARGUMENT_LIST -> visitValueList(node, emit, autoCorrect)
+ CLASS -> visitClass(node, emit, autoCorrect)
+ COLLECTION_LITERAL_EXPRESSION -> visitCollectionLiteralExpression(node, emit, autoCorrect)
+ DESTRUCTURING_DECLARATION -> visitDestructuringDeclaration(node, emit, autoCorrect)
+ FUNCTION_LITERAL -> visitFunctionLiteral(node, emit, autoCorrect)
+ INDICES -> visitIndices(node, emit, autoCorrect)
+ TYPE_ARGUMENT_LIST -> visitTypeList(node, emit, autoCorrect)
+ TYPE_PARAMETER_LIST -> visitTypeList(node, emit, autoCorrect)
+ VALUE_ARGUMENT_LIST -> visitValueList(node, emit, autoCorrect)
+ VALUE_PARAMETER_LIST -> visitValueList(node, emit, autoCorrect)
+ WHEN_ENTRY -> visitWhenEntry(node, emit, autoCorrect)
else -> Unit
}
}
@@ -152,7 +166,7 @@ public class TrailingCommaRule :
emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit,
autoCorrect: Boolean
) {
- if (node.treeParent.elementType != ElementType.FUNCTION_LITERAL) {
+ if (node.treeParent.elementType != FUNCTION_LITERAL) {
node
.children()
.lastOrNull { it.elementType == ElementType.RPAR }
@@ -191,6 +205,52 @@ public class TrailingCommaRule :
node.reportAndCorrectTrailingCommaNodeBefore(inspectNode, emit, autoCorrect)
}
+ private fun visitClass(
+ node: ASTNode,
+ emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit,
+ autoCorrect: Boolean
+ ) {
+ val psi = node.psi
+ require(psi is KtClass)
+
+ node
+ .takeIf { psi.isEnum() }
+ ?.findChildByType(CLASS_BODY)
+ ?.takeUnless {
+ // Do nothing when last two entries are on same line as no trailing comma should be inserted
+ it.lastTwoEnumEntriesAreOnSameLine()
+ }?.let { classBody ->
+ val nodeAfterTrailingCommaPosition = classBody.findNodeAfterTrailingCommaPosition()
+ node.reportAndCorrectTrailingCommaNodeBefore(nodeAfterTrailingCommaPosition, emit, autoCorrect)
+ }
+ }
+
+ /**
+ * Determines the [ASTNode] before which the trailing comma is allowed.
+ *
+ * If the list of enumeration entries is terminated by a semicolon, that semicolon will be returned. Otherwise, the
+ * last element of the class.
+ */
+ private fun ASTNode.findNodeAfterTrailingCommaPosition(): ASTNode {
+ val lastEnumEntry = children().last { it.psi is KtEnumEntry }
+
+ val semicolonAfterLastEnumEntry = lastEnumEntry
+ .children()
+ .singleOrNull { it.elementType == SEMICOLON }
+
+ return semicolonAfterLastEnumEntry ?: lastChildNode
+ }
+
+ private fun ASTNode.lastTwoEnumEntriesAreOnSameLine(): Boolean {
+ val lastTwoEnumEntries =
+ children()
+ .filter { it.psi is KtEnumEntry }
+ .toList()
+ .takeLast(2)
+
+ return lastTwoEnumEntries.count() == 2 && lastTwoEnumEntries[0].lineNumber() == lastTwoEnumEntries[1].lineNumber()
+ }
+
private fun ASTNode.reportAndCorrectTrailingCommaNodeBefore(
inspectNode: ASTNode,
emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit,
@@ -234,6 +294,7 @@ public class TrailingCommaRule :
true
)
}
+
if (autoCorrect) {
if (addNewLineBeforeArrowInWhenEntry) {
val parentIndent = (prevNode.psi.parent.prevLeaf() as? PsiWhiteSpace)?.text ?: "\n"
@@ -246,6 +307,20 @@ public class TrailingCommaRule :
prevNode.psi.parent.addAfter(newLine, prevNode.psi)
}
}
+
+ if (inspectNode.treeParent.elementType == ENUM_ENTRY) {
+ with(KtPsiFactory(prevNode.psi)) {
+ val parentIndent = (prevNode.psi.parent.prevLeaf() as? PsiWhiteSpace)?.text ?: "\n"
+ val newline = createWhiteSpace(parentIndent)
+ val enumEntry = inspectNode.treeParent.psi
+ enumEntry.apply {
+ add(newline)
+ removeChild(inspectNode)
+ parent.addAfter(createSemicolon(), this)
+ }
+ }
+ }
+
val comma = KtPsiFactory(prevNode.psi).createComma()
prevNode.psi.parent.addAfter(comma, prevNode.psi)
}
@@ -319,19 +394,20 @@ public class TrailingCommaRule :
public companion object {
private val TYPES_ON_DECLARATION_SITE = TokenSet.create(
- ElementType.DESTRUCTURING_DECLARATION,
- ElementType.FUNCTION_LITERAL,
+ DESTRUCTURING_DECLARATION,
+ FUNCTION_LITERAL,
ElementType.FUNCTION_TYPE,
- ElementType.TYPE_PARAMETER_LIST,
- ElementType.VALUE_PARAMETER_LIST,
- ElementType.WHEN_ENTRY
+ TYPE_PARAMETER_LIST,
+ VALUE_PARAMETER_LIST,
+ WHEN_ENTRY,
+ CLASS
)
private val TYPES_ON_CALL_SITE = TokenSet.create(
- ElementType.COLLECTION_LITERAL_EXPRESSION,
- ElementType.INDICES,
- ElementType.TYPE_ARGUMENT_LIST,
- ElementType.VALUE_ARGUMENT_LIST
+ COLLECTION_LITERAL_EXPRESSION,
+ INDICES,
+ TYPE_ARGUMENT_LIST,
+ VALUE_ARGUMENT_LIST
)
internal const val ALLOW_TRAILING_COMMA_NAME = "ij_kotlin_allow_trailing_comma"
diff --git a/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/TrailingCommaRuleTest.kt b/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/TrailingCommaRuleTest.kt
index 2af3517c9f..f7e69b015d 100644
--- a/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/TrailingCommaRuleTest.kt
+++ b/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/TrailingCommaRuleTest.kt
@@ -6,6 +6,7 @@ import com.pinterest.ktlint.ruleset.experimental.trailingcomma.TrailingCommaRule
import com.pinterest.ktlint.ruleset.experimental.trailingcomma.TrailingCommaRule.Companion.allowTrailingCommaProperty
import com.pinterest.ktlint.test.KtLintAssertThat.Companion.assertThatRule
import com.pinterest.ktlint.test.LintViolation
+import org.junit.jupiter.api.Nested
import org.junit.jupiter.api.Test
class TrailingCommaRuleTest {
@@ -1024,4 +1025,241 @@ class TrailingCommaRuleTest {
LintViolation(7, 6, "Missing trailing comma before \")\"")
).isFormattedAs(formattedCode)
}
+
+ @Test
+ fun `Given that a trailing comma is is not allowed then remove comma after last enum member`() {
+ val code =
+ """
+ enum class Shape {
+ SQUARE,
+ TRIANGLE,
+ }
+ """.trimIndent()
+ val formattedCode =
+ """
+ enum class Shape {
+ SQUARE,
+ TRIANGLE
+ }
+ """.trimIndent()
+ trailingCommaRuleAssertThat(code)
+ .withEditorConfigOverride(allowTrailingCommaProperty to false)
+ .hasLintViolation(3, 13, "Unnecessary trailing comma before \"}\"")
+ .isFormattedAs(formattedCode)
+ }
+
+ @Test
+ fun `Given that a trailing comma is not allowed then it is removed for enums terminated with semicolon`() {
+ val code =
+ """
+ enum class Shape {
+ SQUARE,
+ TRIANGLE,
+ ;
+
+ fun print() = name()
+ }
+ """.trimIndent()
+ val formattedCode =
+ """
+ enum class Shape {
+ SQUARE,
+ TRIANGLE
+ ;
+
+ fun print() = name()
+ }
+ """.trimIndent()
+ trailingCommaRuleAssertThat(code)
+ .withEditorConfigOverride(allowTrailingCommaProperty to false)
+ .hasLintViolation(3, 13, "Unnecessary trailing comma before \";\"")
+ .isFormattedAs(formattedCode)
+ }
+
+ @Test
+ fun `Given that a trailing comma is not allowed then it is not removed for enums where last two entries are on same line`() {
+ val code =
+ """
+ enum class Shape {
+ SQUARE, TRIANGLE,
+ }
+ """.trimIndent()
+ trailingCommaRuleAssertThat(code)
+ .withEditorConfigOverride(allowTrailingCommaProperty to false)
+ .hasNoLintViolations()
+ }
+
+ @Nested
+ inner class MissingRequiredTrailingComma {
+ @Test
+ fun `Given that last two enumeration entries are on same line, do not add a trailing comma`() {
+ val code =
+ """
+ enum class Shape {
+ SQUARE, TRIANGLE
+ }
+ """.trimIndent()
+ trailingCommaRuleAssertThat(code)
+ .withEditorConfigOverride(allowTrailingCommaProperty to true)
+ .hasNoLintViolations()
+ }
+
+ @Test
+ fun `Given an enum is terminated by a semicolon and EOL comment without a trailing comma, then it is added `() {
+ val code =
+ """
+ enum class Shape {
+ SQUARE,
+ TRIANGLE; // EOL Comment should be kept
+ }
+ """.trimIndent()
+ val formattedCode =
+ """
+ enum class Shape {
+ SQUARE,
+ TRIANGLE, // EOL Comment should be kept
+ ;
+ }
+ """.trimIndent()
+ trailingCommaRuleAssertThat(code)
+ .withEditorConfigOverride(allowTrailingCommaProperty to true)
+ .hasLintViolation(3, 13, "Missing trailing comma before \";\"")
+ .isFormattedAs(formattedCode)
+ }
+
+ @Test
+ fun `Given an enum is terminated by a semicolon and block comment, then it is added `() {
+ val code =
+ """
+ enum class Shape {
+ SQUARE,
+ TRIANGLE; /* block comment should be kept */
+ }
+ """.trimIndent()
+ val formattedCode =
+ """
+ enum class Shape {
+ SQUARE,
+ TRIANGLE, /* block comment should be kept */
+ ;
+ }
+ """.trimIndent()
+ trailingCommaRuleAssertThat(code)
+ .withEditorConfigOverride(allowTrailingCommaProperty to true)
+ .hasLintViolation(3, 13, "Missing trailing comma before \";\"")
+ .isFormattedAs(formattedCode)
+ }
+
+ @Test
+ fun `Given an enum terminated by semicolon without a trailing comma then it is added`() {
+ val code =
+ """
+ enum class Shape {
+ SQUARE,
+ TRIANGLE;
+ }
+ """.trimIndent()
+ val formattedCode =
+ """
+ enum class Shape {
+ SQUARE,
+ TRIANGLE,
+ ;
+ }
+ """.trimIndent()
+ trailingCommaRuleAssertThat(code)
+ .withEditorConfigOverride(allowTrailingCommaProperty to true)
+ .hasLintViolation(3, 13, "Missing trailing comma before \";\"")
+ .isFormattedAs(formattedCode)
+ }
+
+ @Test
+ fun `Given an enum without trailing-comma with other declarations following the enum entries then it is added`() {
+ val code =
+ """
+ enum class Shape {
+ SQUARE,
+ TRIANGLE;
+
+ fun print() = name()
+ }
+ """.trimIndent()
+ val formattedCode =
+ """
+ enum class Shape {
+ SQUARE,
+ TRIANGLE,
+ ;
+
+ fun print() = name()
+ }
+ """.trimIndent()
+ trailingCommaRuleAssertThat(code)
+ .withEditorConfigOverride(allowTrailingCommaProperty to true)
+ .hasLintViolation(3, 13, "Missing trailing comma before \";\"")
+ .isFormattedAs(formattedCode)
+ }
+
+ @Test
+ fun `Given that a trailing comma is required then it is added for complicated enums`() {
+ val code =
+ """
+ interface Printable {
+ fun print(): String
+ }
+
+ enum class Shape : Printable {
+ Square {
+ override fun print() = "■"
+ },
+
+ Triangle {
+ override fun print() = "▲"
+ }
+ }
+ """.trimIndent()
+ val formattedCode =
+ """
+ interface Printable {
+ fun print(): String
+ }
+
+ enum class Shape : Printable {
+ Square {
+ override fun print() = "■"
+ },
+
+ Triangle {
+ override fun print() = "▲"
+ },
+ }
+ """.trimIndent()
+ trailingCommaRuleAssertThat(code)
+ .withEditorConfigOverride(allowTrailingCommaProperty to true)
+ .hasLintViolation(12, 6, "Missing trailing comma before \"}\"")
+ .isFormattedAs(formattedCode)
+ }
+
+ @Test
+ fun `Given that a trailing comma is required then add trailing comma after last enum member`() {
+ val code =
+ """
+ enum class Shape {
+ SQUARE,
+ TRIANGLE
+ }
+ """.trimIndent()
+ val formattedCode =
+ """
+ enum class Shape {
+ SQUARE,
+ TRIANGLE,
+ }
+ """.trimIndent()
+ trailingCommaRuleAssertThat(code)
+ .withEditorConfigOverride(allowTrailingCommaProperty to true)
+ .hasLintViolation(3, 13, "Missing trailing comma before \"}\"")
+ .isFormattedAs(formattedCode)
+ }
+ }
}
From bf1443eb85c67ac33caa9d03b7fef292b2ee209d Mon Sep 17 00:00:00 2001
From: paul-dingemans
Date: Wed, 3 Aug 2022 14:59:29 +0200
Subject: [PATCH 6/7] Prevent class cast exception when retrieving
ktlint_code_style property from ".editorconfig" (#1564)
Closes #1559
---
CHANGELOG.md | 2 +-
.../core/api/UsesEditorConfigProperties.kt | 24 +++++++++++++++----
2 files changed, 21 insertions(+), 5 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index df10aa67f0..7c653a0e6f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -126,9 +126,9 @@ The callback function provided as parameter to the format function is now called
* When a glob is specified then ensure that it matches files in the current directory and not only in subdirectories of the current directory ([#1533](https://github.com/pinterest/ktlint/issue/1533)).
* Execute `ktlint` cli on default kotlin extensions only when an (existing) path to a directory is given. ([#917](https://github.com/pinterest/ktlint/issue/917)).
* Invoke callback on `format` function for all errors including errors that are autocorrected ([#1491](https://github.com/pinterest/ktlint/issues/1491))
+* Prevent class cast exception on ".editorconfig" property `ktlint_code_style` ([#1559](https://github.com/pinterest/ktlint/issues/1559))
* Handle trailing comma in enums `trailing-comma` ([#1542](https://github.com/pinterest/ktlint/pull/1542))
-
### Changed
* Print an error message and return with non-zero exit code when no files are found that match with the globs ([#629](https://github.com/pinterest/ktlint/issue/629)).
diff --git a/ktlint-core/src/main/kotlin/com/pinterest/ktlint/core/api/UsesEditorConfigProperties.kt b/ktlint-core/src/main/kotlin/com/pinterest/ktlint/core/api/UsesEditorConfigProperties.kt
index 2b87ef4053..8440757d73 100644
--- a/ktlint-core/src/main/kotlin/com/pinterest/ktlint/core/api/UsesEditorConfigProperties.kt
+++ b/ktlint-core/src/main/kotlin/com/pinterest/ktlint/core/api/UsesEditorConfigProperties.kt
@@ -47,10 +47,24 @@ public interface UsesEditorConfigProperties {
require(editorConfigProperties.contains(editorConfigProperty)) {
"EditorConfigProperty '${editorConfigProperty.type.name}' may only be retrieved when it is registered in the editorConfigProperties."
}
- val codeStyle = getEditorConfigValue(codeStyleSetProperty, official)
- return getEditorConfigValue(editorConfigProperty, codeStyle)
+
+ return getEditorConfigValue(editorConfigProperty, getEditorConfigCodeStyle())
}
+ /**
+ * The code style property does not need to be defined in the [editorConfigProperties] of the class that defines
+ * this interface. Those classed should not need to be aware of the different coding styles except when setting
+ * different default values. As the property is not defined in the [editorConfigProperties] the value needs to
+ * be parsed explicitly to prevent class cast exceptions.
+ */
+ private fun EditorConfigProperties.getEditorConfigCodeStyle() =
+ codeStyleSetProperty
+ .type
+ .parse(
+ get(codeStyleSetProperty.type.name)?.sourceValue
+ ).parsed
+ ?: official
+
/**
* Get the value of [EditorConfigProperty] based on loaded [EditorConfigProperties] content for the current
* [ASTNode].
@@ -61,8 +75,10 @@ public interface UsesEditorConfigProperties {
"EditorConfigProperty '${editorConfigProperty.type.name}' may only be retrieved when it is registered in the editorConfigProperties."
}
val editorConfigPropertyValues = getUserData(KtLint.EDITOR_CONFIG_PROPERTIES_USER_DATA_KEY)!!
- val codeStyle = editorConfigPropertyValues.getEditorConfigValue(codeStyleSetProperty, official)
- return editorConfigPropertyValues.getEditorConfigValue(editorConfigProperty, codeStyle)
+ return editorConfigPropertyValues.getEditorConfigValue(
+ editorConfigProperty,
+ editorConfigPropertyValues.getEditorConfigCodeStyle()
+ )
}
private fun EditorConfigProperties.getEditorConfigValue(
From 49efdba0893da99b3c2aaeec4ed4f65aa7ca6767 Mon Sep 17 00:00:00 2001
From: paul-dingemans
Date: Wed, 3 Aug 2022 20:56:40 +0200
Subject: [PATCH 7/7] Add missing whitespace when else is on same line as true
condition (#1565)
* Add missing whitespace when else is on same line as true condition `multiline-if-else`
Closes #1560
* Fix multiline if statement
Closes #828
---
CHANGELOG.md | 2 +
.../internal/SuppressionLocatorBuilder.kt | 9 +-
.../ruleset/standard/AnnotationSpacingRule.kt | 4 +-
.../ruleset/standard/MultiLineIfElseRule.kt | 95 +++++++++++++++----
.../standard/MultiLineIfElseRuleTest.kt | 83 +++++++++++++++-
5 files changed, 169 insertions(+), 24 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 7c653a0e6f..e9eb00d234 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -126,6 +126,8 @@ The callback function provided as parameter to the format function is now called
* When a glob is specified then ensure that it matches files in the current directory and not only in subdirectories of the current directory ([#1533](https://github.com/pinterest/ktlint/issue/1533)).
* Execute `ktlint` cli on default kotlin extensions only when an (existing) path to a directory is given. ([#917](https://github.com/pinterest/ktlint/issue/917)).
* Invoke callback on `format` function for all errors including errors that are autocorrected ([#1491](https://github.com/pinterest/ktlint/issues/1491))
+* Add missing whitespace when else is on same line as true condition `multiline-if-else` ([#1560](https://github.com/pinterest/ktlint/issues/1560))
+* Fix multiline if-statements `multiline-if-else` ([#828](https://github.com/pinterest/ktlint/issues/828))
* Prevent class cast exception on ".editorconfig" property `ktlint_code_style` ([#1559](https://github.com/pinterest/ktlint/issues/1559))
* Handle trailing comma in enums `trailing-comma` ([#1542](https://github.com/pinterest/ktlint/pull/1542))
diff --git a/ktlint-core/src/main/kotlin/com/pinterest/ktlint/core/internal/SuppressionLocatorBuilder.kt b/ktlint-core/src/main/kotlin/com/pinterest/ktlint/core/internal/SuppressionLocatorBuilder.kt
index 693e08a8c4..1491d1c7fc 100644
--- a/ktlint-core/src/main/kotlin/com/pinterest/ktlint/core/internal/SuppressionLocatorBuilder.kt
+++ b/ktlint-core/src/main/kotlin/com/pinterest/ktlint/core/internal/SuppressionLocatorBuilder.kt
@@ -38,12 +38,17 @@ internal object SuppressionLocatorBuilder {
val hintsList = collect(rootNode)
return if (hintsList.isEmpty()) {
noSuppression
- } else { offset, ruleId, isRoot ->
+ } else {
+ toSuppressedRegionsLocator(hintsList)
+ }
+ }
+
+ private fun toSuppressedRegionsLocator(hintsList: List): SuppressionLocator =
+ { offset, ruleId, isRoot ->
hintsList
.filter { offset in it.range }
.any { hint -> hint.disabledRules.isEmpty() || hint.disabledRules.contains(ruleId) }
}
- }
/**
* @param range zero-based range of lines where lint errors should be suppressed
diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/AnnotationSpacingRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/AnnotationSpacingRule.kt
index 5eda6f5311..ab17502d4b 100644
--- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/AnnotationSpacingRule.kt
+++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/AnnotationSpacingRule.kt
@@ -73,7 +73,9 @@ class AnnotationSpacingRule : Rule("annotation-spacing") {
val s = it.text
// Ensure at least one occurrence of two line breaks
s.indexOf("\n") != s.lastIndexOf("\n")
- } else it.isPartOfComment() && !it.isCommentOnSameLineAsPrevLeaf()
+ } else {
+ it.isPartOfComment() && !it.isCommentOnSameLineAsPrevLeaf()
+ }
}
)
if (next != null) {
diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/MultiLineIfElseRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/MultiLineIfElseRule.kt
index ac5c93f170..87ee66ffb6 100644
--- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/MultiLineIfElseRule.kt
+++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/MultiLineIfElseRule.kt
@@ -1,17 +1,26 @@
package com.pinterest.ktlint.ruleset.standard
+import com.pinterest.ktlint.core.IndentConfig
import com.pinterest.ktlint.core.Rule
+import com.pinterest.ktlint.core.api.DefaultEditorConfigProperties
+import com.pinterest.ktlint.core.api.EditorConfigProperties
+import com.pinterest.ktlint.core.api.UsesEditorConfigProperties
+import com.pinterest.ktlint.core.ast.ElementType.BLOCK
import com.pinterest.ktlint.core.ast.ElementType.ELSE
import com.pinterest.ktlint.core.ast.ElementType.ELSE_KEYWORD
+import com.pinterest.ktlint.core.ast.ElementType.IF
import com.pinterest.ktlint.core.ast.ElementType.LBRACE
import com.pinterest.ktlint.core.ast.ElementType.RBRACE
import com.pinterest.ktlint.core.ast.ElementType.RPAR
import com.pinterest.ktlint.core.ast.ElementType.THEN
+import com.pinterest.ktlint.core.ast.ElementType.WHITE_SPACE
import com.pinterest.ktlint.core.ast.isPartOfComment
+import com.pinterest.ktlint.core.ast.isWhiteSpace
import com.pinterest.ktlint.core.ast.isWhiteSpaceWithoutNewline
-import com.pinterest.ktlint.core.ast.prevLeaf
+import com.pinterest.ktlint.core.ast.lineIndent
+import com.pinterest.ktlint.core.ast.nextSibling
+import com.pinterest.ktlint.core.ast.upsertWhitespaceBeforeMe
import org.jetbrains.kotlin.com.intellij.lang.ASTNode
-import org.jetbrains.kotlin.com.intellij.psi.PsiWhiteSpace
import org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.LeafPsiElement
import org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.PsiWhiteSpaceImpl
import org.jetbrains.kotlin.psi.KtBlockExpression
@@ -20,50 +29,100 @@ import org.jetbrains.kotlin.psi.psiUtil.leaves
/**
* https://kotlinlang.org/docs/reference/coding-conventions.html#formatting-control-flow-statements
*/
-class MultiLineIfElseRule : Rule("multiline-if-else") {
+class MultiLineIfElseRule :
+ Rule("multiline-if-else"),
+ UsesEditorConfigProperties {
+ override val editorConfigProperties: List> =
+ listOf(
+ DefaultEditorConfigProperties.indentSizeProperty,
+ DefaultEditorConfigProperties.indentStyleProperty
+ )
+ private var indentConfig = IndentConfig.DEFAULT_INDENT_CONFIG
+
+ override fun beforeFirstNode(editorConfigProperties: EditorConfigProperties) {
+ indentConfig = IndentConfig(
+ indentStyle = editorConfigProperties.getEditorConfigValue(DefaultEditorConfigProperties.indentStyleProperty),
+ tabWidth = editorConfigProperties.getEditorConfigValue(DefaultEditorConfigProperties.indentSizeProperty)
+ )
+ }
+
override fun beforeVisitChildNodes(
node: ASTNode,
autoCorrect: Boolean,
emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit
) {
if (node.elementType == THEN || node.elementType == ELSE) {
- if (!node.treePrev.textContains('\n')) { // if (...)
+ if (node.firstChildNode?.elementType == BLOCK) {
return
}
- if (node.firstChildNode?.firstChildNode?.elementType != LBRACE) {
- emit(node.firstChildNode.startOffset, "Missing { ... }", true)
- if (autoCorrect) {
- autocorrect(node)
+ if (!node.treePrev.textContains('\n')) {
+ if (node.firstChildNode.elementType == IF) {
+ // Allow single line for:
+ // else if (...)
+ return
+ }
+ if (!node.treeParent.textContains('\n')) {
+ // Allow single line if statements as long as they are really simple (e.g. do not contain newlines)
+ // if (...) // no else statement
+ // if (...) else
+ return
}
}
+
+ emit(node.firstChildNode.startOffset, "Missing { ... }", true)
+ if (autoCorrect) {
+ autocorrect(node)
+ }
}
}
private fun autocorrect(node: ASTNode) {
val prevLeaves =
- node.leaves(forward = false).takeWhile { it.elementType !in listOf(RPAR, ELSE_KEYWORD) }.toList().reversed()
+ node
+ .leaves(forward = false)
+ .takeWhile { it.elementType !in listOf(RPAR, ELSE_KEYWORD) }
+ .toList()
+ .reversed()
val nextLeaves =
- node.leaves(forward = true).takeWhile { it.isWhiteSpaceWithoutNewline() || it.isPartOfComment() }.toList()
- val rightBraceIndent = node.treeParent
- .prevLeaf { it is PsiWhiteSpace && it.textContains('\n') }?.text.orEmpty()
- .let { "\n${it.substringAfterLast("\n")}" }
+ node
+ .leaves(forward = true)
+ .takeWhile { it.isWhiteSpaceWithoutNewline() || it.isPartOfComment() }
+ .toList()
+ .dropLastWhile { it.isWhiteSpaceWithoutNewline() }
- (node.treePrev as LeafPsiElement).rawReplaceWithText(" ")
+ prevLeaves
+ .firstOrNull()
+ .takeIf { it.isWhiteSpace() }
+ ?.let {
+ (it as LeafPsiElement).rawReplaceWithText(" ")
+ }
KtBlockExpression(null).apply {
val previousChild = node.firstChildNode
node.replaceChild(node.firstChildNode, this)
addChild(LeafPsiElement(LBRACE, "{"))
- prevLeaves.forEach(::addChild)
+ addChild(PsiWhiteSpaceImpl("\n" + node.lineIndent() + indentConfig.indent))
+ prevLeaves
+ .dropWhile { it.isWhiteSpace() }
+ .forEach(::addChild)
addChild(previousChild)
nextLeaves.forEach(::addChild)
- addChild(PsiWhiteSpaceImpl(rightBraceIndent))
+ addChild(PsiWhiteSpaceImpl("\n" + node.lineIndent()))
addChild(LeafPsiElement(RBRACE, "}"))
}
// Make sure else starts on same line as newly inserted right brace
- if (node.elementType == THEN && node.treeNext?.treeNext?.elementType == ELSE_KEYWORD) {
- node.treeParent.replaceChild(node.treeNext, PsiWhiteSpaceImpl(" "))
+ if (node.elementType == THEN) {
+ node
+ .nextSibling { !it.isPartOfComment() }
+ ?.let { nextSibling ->
+ if (nextSibling.elementType == ELSE_KEYWORD) {
+ (nextSibling as LeafPsiElement).upsertWhitespaceBeforeMe(" ")
+ }
+ if (nextSibling.elementType == WHITE_SPACE && nextSibling.text != " ") {
+ (nextSibling as LeafPsiElement).rawReplaceWithText(" ")
+ }
+ }
}
}
}
diff --git a/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/MultiLineIfElseRuleTest.kt b/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/MultiLineIfElseRuleTest.kt
index 2d2e8e0128..8ac7f2403d 100644
--- a/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/MultiLineIfElseRuleTest.kt
+++ b/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/MultiLineIfElseRuleTest.kt
@@ -480,7 +480,9 @@ class MultiLineIfElseRuleTest {
fun test(a: Int, b: Int, c: Int, d: Int, bar: Boolean) {
foo(
a,
- if (bar) b else {
+ if (bar) {
+ b
+ } else {
c
},
d
@@ -488,8 +490,83 @@ class MultiLineIfElseRuleTest {
}
""".trimIndent()
multiLineIfElseRuleAssertThat(code)
- // TODO: It is not consistent that argument "b" is not wrapped in a block while argument "c" is wrapped
- .hasLintViolation(6, 13, "Missing { ... }")
+ .hasLintViolations(
+ LintViolation(5, 18, "Missing { ... }"),
+ LintViolation(6, 13, "Missing { ... }")
+ ).isFormattedAs(formattedCode)
+ }
+
+ @Test
+ fun `Issue 1560 - Given an if statement with else keyword on same line as true branch`() {
+ val code =
+ """
+ fun foo() = if (bar())
+ "a" else
+ "b"
+ """.trimIndent()
+ val formattedCode =
+ """
+ fun foo() = if (bar()) {
+ "a"
+ } else {
+ "b"
+ }
+ """.trimIndent()
+ multiLineIfElseRuleAssertThat(code)
+ .hasLintViolations(
+ LintViolation(2, 5, "Missing { ... }"),
+ LintViolation(3, 5, "Missing { ... }")
+ ).isFormattedAs(formattedCode)
+ }
+
+ @Test
+ fun `Issue 828 - Given an if statement with multiline statement starting on same line as if`() {
+ val code =
+ """
+ fun foo() {
+ if (true) 50
+ .toString()
+ }
+ """.trimIndent()
+ val formattedCode =
+ """
+ fun foo() {
+ if (true) {
+ 50
+ .toString()
+ }
+ }
+ """.trimIndent()
+ multiLineIfElseRuleAssertThat(code)
+ .addAdditionalRuleProvider { IndentationRule() }
+ .hasLintViolation(2, 15, "Missing { ... }")
.isFormattedAs(formattedCode)
}
+
+ @Test
+ fun `Issue 828 - Given an if statement with simple branches but the else branch is on a separate line`() {
+ val code =
+ """
+ fun foo() {
+ if (true) 50
+ else 55
+ }
+ """.trimIndent()
+ val formattedCode =
+ """
+ fun foo() {
+ if (true) {
+ 50
+ } else {
+ 55
+ }
+ }
+ """.trimIndent()
+ multiLineIfElseRuleAssertThat(code)
+ .addAdditionalRuleProvider { IndentationRule() }
+ .hasLintViolations(
+ LintViolation(2, 15, "Missing { ... }"),
+ LintViolation(3, 10, "Missing { ... }")
+ ).isFormattedAs(formattedCode)
+ }
}