diff --git a/CHANGELOG.md b/CHANGELOG.md index d42549ee9d..7cabbd3e8b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -36,6 +36,20 @@ respectively (`--ruleset-*` will be removed in 1.0.0). - `ktlint-intellij-idea-integration` binary ([Intellij IDEA integration](https://github.com/shyiko/ktlint#option-1-recommended) task is now included in `ktlint` (as `ktlint --apply-to-idea`)). +## [0.8.3] - 2017-06-19 + +### Fixed + +- "Missing spacing after ";"" at the end of package declaration ([#59](https://github.com/shyiko/ktlint/issues/59)). +- "Unused import" false positive (`setValue`) ([#55](https://github.com/shyiko/ktlint/issues/55)). +- `get`/`set`ter spacing ([#56](https://github.com/shyiko/ktlint/pull/56)). + +## [0.8.2] - 2017-06-06 + +### Fixed + +- "Unused import" false positive (`getValue`) ([#54](https://github.com/shyiko/ktlint/issues/54)). + ## [0.8.1] - 2017-05-30 ### Fixed @@ -155,7 +169,9 @@ set in `[*{kt,kts}]` section). ## 0.1.0 - 2016-07-27 -[0.9.0]: https://github.com/shyiko/ktlint/compare/0.8.1...0.9.0 +[0.9.0]: https://github.com/shyiko/ktlint/compare/0.8.3...0.9.0 +[0.8.3]: https://github.com/shyiko/ktlint/compare/0.8.2...0.8.3 +[0.8.2]: https://github.com/shyiko/ktlint/compare/0.8.1...0.8.2 [0.8.1]: https://github.com/shyiko/ktlint/compare/0.8.0...0.8.1 [0.8.0]: https://github.com/shyiko/ktlint/compare/0.7.1...0.8.0 [0.7.1]: https://github.com/shyiko/ktlint/compare/0.7.0...0.7.1 diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/github/shyiko/ktlint/ruleset/standard/IndentationRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/github/shyiko/ktlint/ruleset/standard/IndentationRule.kt index a385dc2275..47a85aa4e4 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/github/shyiko/ktlint/ruleset/standard/IndentationRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/github/shyiko/ktlint/ruleset/standard/IndentationRule.kt @@ -50,11 +50,11 @@ class IndentationRule : Rule("indent") { if (node.isPartOf(KtParameterList::class) && firstParameterColumn.value != 0) { if (firstParameterColumn.value - 1 != it.length) { emit(offset, "Unexpected indentation (${it.length}) (" + - "parameters should be either vertically aligned or indented by the multiple of 4" + + "parameters should be either vertically aligned or indented by the multiple of $indent" + ")", false) } } else { - emit(offset, "Unexpected indentation (${it.length}) (it should be multiple of 4)", false) + emit(offset, "Unexpected indentation (${it.length}) (it should be multiple of $indent)", false) } } offset += it.length + 1 diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/github/shyiko/ktlint/ruleset/standard/NoSemicolonsRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/github/shyiko/ktlint/ruleset/standard/NoSemicolonsRule.kt index f08d500f13..804b37ce2c 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/github/shyiko/ktlint/ruleset/standard/NoSemicolonsRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/github/shyiko/ktlint/ruleset/standard/NoSemicolonsRule.kt @@ -14,10 +14,10 @@ class NoSemicolonsRule : Rule("no-semi") { emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit) { if (node is LeafPsiElement && node.textMatches(";") && !node.isPartOfString() && !node.isPartOf(KtEnumEntry::class)) { - val nextLeaf = PsiTreeUtil.nextLeaf(node) + val nextLeaf = PsiTreeUtil.nextLeaf(node, true) if (nextLeaf == null /* eof */ || (nextLeaf is PsiWhiteSpace && (nextLeaf.text.contains("\n") || - PsiTreeUtil.nextLeaf(nextLeaf) == null /* \s+ and then eof */)) + PsiTreeUtil.nextLeaf(nextLeaf, true) == null /* \s+ and then eof */)) ) { emit(node.startOffset, "Unnecessary semicolon", true) if (autoCorrect) { diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/github/shyiko/ktlint/ruleset/standard/NoUnusedImportsRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/github/shyiko/ktlint/ruleset/standard/NoUnusedImportsRule.kt index f0ec091aca..9ccbf00633 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/github/shyiko/ktlint/ruleset/standard/NoUnusedImportsRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/github/shyiko/ktlint/ruleset/standard/NoUnusedImportsRule.kt @@ -32,7 +32,9 @@ class NoUnusedImportsRule : Rule("no-unused-imports") { // comparison "compareTo", // iteration (https://github.com/shyiko/ktlint/issues/40) - "iterator" + "iterator", + // by (https://github.com/shyiko/ktlint/issues/54) + "getValue", "setValue" ) private val ref = mutableSetOf("*") private var packageName = "" diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/github/shyiko/ktlint/ruleset/standard/SpacingAroundCommaRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/github/shyiko/ktlint/ruleset/standard/SpacingAroundCommaRule.kt index f2a867c445..fa0f2f361c 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/github/shyiko/ktlint/ruleset/standard/SpacingAroundCommaRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/github/shyiko/ktlint/ruleset/standard/SpacingAroundCommaRule.kt @@ -11,7 +11,7 @@ class SpacingAroundCommaRule : Rule("comma-spacing") { override fun visit(node: ASTNode, autoCorrect: Boolean, emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit) { - if (node is LeafPsiElement && (node.textMatches(",") || node.textMatches(";")) && !node.isPartOfString() && + if (node is LeafPsiElement && node.textMatches(",") && !node.isPartOfString() && PsiTreeUtil.nextLeaf(node) !is PsiWhiteSpace) { emit(node.startOffset + 1, "Missing spacing after \"${node.text}\"", true) if (autoCorrect) { diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/github/shyiko/ktlint/ruleset/standard/SpacingAroundCurlyRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/github/shyiko/ktlint/ruleset/standard/SpacingAroundCurlyRule.kt index 3a1998e89c..ca7e8cd6f6 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/github/shyiko/ktlint/ruleset/standard/SpacingAroundCurlyRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/github/shyiko/ktlint/ruleset/standard/SpacingAroundCurlyRule.kt @@ -20,7 +20,7 @@ class SpacingAroundCurlyRule : Rule("curly-spacing") { val spacingBefore: Boolean val spacingAfter: Boolean if (node.textMatches("{")) { - spacingBefore = prevLeaf is PsiWhiteSpace || (prevLeaf?.node?.elementType == KtTokens.LPAR && + spacingBefore = prevLeaf is PsiWhiteSpace || prevLeaf?.node?.elementType == KtTokens.AT || (prevLeaf?.node?.elementType == KtTokens.LPAR && (node.parent is KtLambdaExpression || node.parent.parent is KtLambdaExpression)) spacingAfter = nextLeaf is PsiWhiteSpace || nextLeaf?.node?.elementType == KtTokens.RBRACE } else diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/github/shyiko/ktlint/ruleset/standard/SpacingAroundKeywordRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/github/shyiko/ktlint/ruleset/standard/SpacingAroundKeywordRule.kt index de579f225e..560e50d7c0 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/github/shyiko/ktlint/ruleset/standard/SpacingAroundKeywordRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/github/shyiko/ktlint/ruleset/standard/SpacingAroundKeywordRule.kt @@ -7,6 +7,7 @@ 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.com.intellij.psi.tree.TokenSet import org.jetbrains.kotlin.com.intellij.psi.util.PsiTreeUtil +import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.lexer.KtTokens.CATCH_KEYWORD import org.jetbrains.kotlin.lexer.KtTokens.DO_KEYWORD import org.jetbrains.kotlin.lexer.KtTokens.ELSE_KEYWORD @@ -16,32 +17,45 @@ import org.jetbrains.kotlin.lexer.KtTokens.IF_KEYWORD import org.jetbrains.kotlin.lexer.KtTokens.TRY_KEYWORD import org.jetbrains.kotlin.lexer.KtTokens.WHEN_KEYWORD import org.jetbrains.kotlin.lexer.KtTokens.WHILE_KEYWORD +import org.jetbrains.kotlin.psi.KtPropertyAccessor import org.jetbrains.kotlin.psi.KtWhenEntry +import org.jetbrains.kotlin.psi.psiUtil.nextLeaf class SpacingAroundKeywordRule : Rule("keyword-spacing") { private val noLFBeforeSet = TokenSet.create(ELSE_KEYWORD, CATCH_KEYWORD, FINALLY_KEYWORD) private val tokenSet = TokenSet.create(FOR_KEYWORD, IF_KEYWORD, ELSE_KEYWORD, WHILE_KEYWORD, DO_KEYWORD, TRY_KEYWORD, CATCH_KEYWORD, FINALLY_KEYWORD, WHEN_KEYWORD) - // todo: but not after fun(, get(, set( + + private val keywordsWithoutSpaces = TokenSet.create(KtTokens.GET_KEYWORD, KtTokens.SET_KEYWORD) override fun visit(node: ASTNode, autoCorrect: Boolean, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit) { - if (tokenSet.contains(node.elementType) && node is LeafPsiElement && - PsiTreeUtil.nextLeaf(node) !is PsiWhiteSpace) { - emit(node.startOffset + node.text.length, "Missing spacing after \"${node.text}\"", true) - if (autoCorrect) { - node.rawInsertAfterMe(PsiWhiteSpaceImpl(" ")) - } - } - if (noLFBeforeSet.contains(node.elementType) && node is LeafPsiElement) { - val prevLeaf = PsiTreeUtil.prevLeaf(node) - if (prevLeaf is PsiWhiteSpaceImpl && prevLeaf.textContains('\n') && - (node.elementType != ELSE_KEYWORD || node.parent !is KtWhenEntry) && - (PsiTreeUtil.prevLeaf(prevLeaf)?.textMatches("}") ?: false)) { - emit(node.startOffset, "Unexpected newline before \"${node.text}\"", true) + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit) { + + if (node is LeafPsiElement) { + if (tokenSet.contains(node.elementType) && node.nextLeaf() !is PsiWhiteSpace) { + emit(node.startOffset + node.text.length, "Missing spacing after \"${node.text}\"", true) if (autoCorrect) { - prevLeaf.rawReplaceWithText(" ") + node.rawInsertAfterMe(PsiWhiteSpaceImpl(" ")) + } + } else if (keywordsWithoutSpaces.contains(node.elementType) && node.nextLeaf() is PsiWhiteSpace) { + val parent = node.parent + if (parent is KtPropertyAccessor && parent.hasBody()) { + emit(node.startOffset, "Unexpected spacing after \"${node.text}\"", true) + if (autoCorrect) { + node.nextLeaf()?.delete() + } + } + } + if (noLFBeforeSet.contains(node.elementType)) { + val prevLeaf = PsiTreeUtil.prevLeaf(node) + if (prevLeaf is PsiWhiteSpaceImpl && prevLeaf.textContains('\n') && + (node.elementType != ELSE_KEYWORD || node.parent !is KtWhenEntry) && + (PsiTreeUtil.prevLeaf(prevLeaf)?.textMatches("}") ?: false)) { + emit(node.startOffset, "Unexpected newline before \"${node.text}\"", true) + if (autoCorrect) { + prevLeaf.rawReplaceWithText(" ") + } } } } diff --git a/ktlint-ruleset-standard/src/test/kotlin/com/github/shyiko/ktlint/ruleset/standard/IndentationRuleTest.kt b/ktlint-ruleset-standard/src/test/kotlin/com/github/shyiko/ktlint/ruleset/standard/IndentationRuleTest.kt index 06cad0c4c2..b3c80a636a 100644 --- a/ktlint-ruleset-standard/src/test/kotlin/com/github/shyiko/ktlint/ruleset/standard/IndentationRuleTest.kt +++ b/ktlint-ruleset-standard/src/test/kotlin/com/github/shyiko/ktlint/ruleset/standard/IndentationRuleTest.kt @@ -99,4 +99,18 @@ class IndentationRuleTest { )).isEmpty() } + @Test + fun testErrorWithCustomIndentSize() { + assertThat(IndentationRule().lint( + """ + fun main() { + val v = "" + println(v) + } + """.trimIndent(), + mapOf("indent_size" to "3") + )).isEqualTo(listOf( + LintError(3, 1, "indent", "Unexpected indentation (4) (it should be multiple of 3)") + )) + } } diff --git a/ktlint-ruleset-standard/src/test/kotlin/com/github/shyiko/ktlint/ruleset/standard/NoSemicolonsRuleTest.kt b/ktlint-ruleset-standard/src/test/kotlin/com/github/shyiko/ktlint/ruleset/standard/NoSemicolonsRuleTest.kt index 4d4ab29ea6..923320e926 100644 --- a/ktlint-ruleset-standard/src/test/kotlin/com/github/shyiko/ktlint/ruleset/standard/NoSemicolonsRuleTest.kt +++ b/ktlint-ruleset-standard/src/test/kotlin/com/github/shyiko/ktlint/ruleset/standard/NoSemicolonsRuleTest.kt @@ -1,8 +1,8 @@ package com.github.shyiko.ktlint.ruleset.standard import com.github.shyiko.ktlint.core.LintError -import com.github.shyiko.ktlint.test.lint import com.github.shyiko.ktlint.test.format +import com.github.shyiko.ktlint.test.lint import org.assertj.core.api.Assertions.assertThat import org.testng.annotations.Test @@ -12,6 +12,8 @@ class NoSemicolonsRuleTest { fun testLint() { assertThat(NoSemicolonsRule().lint( """ + package a.b.c; + fun main() { fun name() { a(); return b } println(";") @@ -19,7 +21,8 @@ class NoSemicolonsRuleTest { } """.trimIndent() )).isEqualTo(listOf( - LintError(4, 14, "no-semi", "Unnecessary semicolon") + LintError(1, 14, "no-semi", "Unnecessary semicolon"), + LintError(6, 14, "no-semi", "Unnecessary semicolon") )) } diff --git a/ktlint-ruleset-standard/src/test/kotlin/com/github/shyiko/ktlint/ruleset/standard/SpacingAroundCurlyRuleTest.kt b/ktlint-ruleset-standard/src/test/kotlin/com/github/shyiko/ktlint/ruleset/standard/SpacingAroundCurlyRuleTest.kt index bc7f6b405f..2dc3c6b56c 100644 --- a/ktlint-ruleset-standard/src/test/kotlin/com/github/shyiko/ktlint/ruleset/standard/SpacingAroundCurlyRuleTest.kt +++ b/ktlint-ruleset-standard/src/test/kotlin/com/github/shyiko/ktlint/ruleset/standard/SpacingAroundCurlyRuleTest.kt @@ -1,8 +1,8 @@ package com.github.shyiko.ktlint.ruleset.standard import com.github.shyiko.ktlint.core.LintError -import com.github.shyiko.ktlint.test.lint import com.github.shyiko.ktlint.test.format +import com.github.shyiko.ktlint.test.lint import org.assertj.core.api.Assertions.assertThat import org.testng.annotations.Test @@ -11,6 +11,7 @@ class SpacingAroundCurlyRuleTest { @Test fun testLint() { assertThat(SpacingAroundCurlyRule().lint("fun emit() { }")).isEmpty() + assertThat(SpacingAroundCurlyRule().lint("fun emit() { val a = a@{ } }")).isEmpty() assertThat(SpacingAroundCurlyRule().lint("fun emit() {}")).isEmpty() assertThat(SpacingAroundCurlyRule().lint("fun main() { val v = if (true){return 0} }")) .isEqualTo(listOf( diff --git a/ktlint-ruleset-standard/src/test/kotlin/com/github/shyiko/ktlint/ruleset/standard/SpacingAroundKeywordRuleTest.kt b/ktlint-ruleset-standard/src/test/kotlin/com/github/shyiko/ktlint/ruleset/standard/SpacingAroundKeywordRuleTest.kt index 073df23c88..232eeb3145 100644 --- a/ktlint-ruleset-standard/src/test/kotlin/com/github/shyiko/ktlint/ruleset/standard/SpacingAroundKeywordRuleTest.kt +++ b/ktlint-ruleset-standard/src/test/kotlin/com/github/shyiko/ktlint/ruleset/standard/SpacingAroundKeywordRuleTest.kt @@ -1,8 +1,8 @@ package com.github.shyiko.ktlint.ruleset.standard import com.github.shyiko.ktlint.core.LintError -import com.github.shyiko.ktlint.test.lint import com.github.shyiko.ktlint.test.format +import com.github.shyiko.ktlint.test.lint import org.assertj.core.api.Assertions.assertThat import org.testng.annotations.Test @@ -94,5 +94,47 @@ class SpacingAroundKeywordRuleTest { ) } + @Test + fun getterAndSetterFunction() { + assertThat(SpacingAroundKeywordRule().format( + """ + var x: String + get () { + return "" + } + private set (value) { + x = value + } + """.trimIndent() + )).isEqualTo( + """ + var x: String + get() { + return "" + } + private set(value) { + x = value + } + """.trimIndent() + ) + } + + @Test + fun visibilityOrInjectProperty() { + assertThat(SpacingAroundKeywordRule().lint( + """ + var setterVisibility: String = "abc" + private set + var setterWithAnnotation: Any? = null + @Inject set + var setterOnNextLine: String + private set + (value) { setterOnNextLine = value} + """ + )).isEqualTo(listOf( + LintError(7, 21, "keyword-spacing", "Unexpected spacing after \"set\"") + )) + } + }