Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Lint for Kotlin @R.kt#3

TakuSemba
March 15, 2018

Lint for Kotlin @R.kt#3

TakuSemba

March 15, 2018
Tweet

More Decks by TakuSemba

Other Decks in Programming

Transcript

  1. ktlint ɾStatic code analysis tool for kotlin ɾBased on official

    code style ɾBuilt-in formatter ɾCustomizable output
  2. ktlint with gradle configurations { ktlint } dependencies { ktlint

    "com.github.shyiko:ktlint:$KTLINT_VERSION" } check.dependsOn ktlint
  3. ktlint with gradle task ktlint(type: JavaExec, group: "kotlin verification") {

    description = 'Check Kotlin code style.' args 'src/**/*.kt' main = 'com.github.shyiko.ktlint.Main' classpath = configurations.ktlint + sourceSets.main.output } $ ./gradlew tasks
  4. ktlint with gradle task ktFormat(type: JavaExec, group: "kotlin Formatting") {

    description = 'Fix Kotlin code style deviations.' args '-F', 'src/**/*.kt' main = 'com.github.shyiko.ktlint.Main' classpath = configurations.ktlint + sourceSets.main.output } $ ./gradlew tasks
  5. Outputs task ktlint(type: JavaExec, dependsOn: classes) { description = 'Check

    Kotlin code style.' args 'src/**/*.kt', ‘--reporter=plain' main = 'com.github.shyiko.ktlint.Main' classpath = configurations.ktlint + sourceSets.main.output }
  6. Outputs task ktlint(type: JavaExec, dependsOn: classes) { description = 'Check

    Kotlin code style.' args 'src/**/*.kt', ‘--reporter=plain' main = 'com.github.shyiko.ktlint.Main' classpath = configurations.ktlint + sourceSets.main.output } '--reporter=plain'
  7. Outputs --reporter=plain takusemba/ktlint-sample/app/…/Hoge.kt:44:1: Unexpected blank line(s) before "}" takusemba/ktlint-sample/app/…/Fuga.kt:108:1: Needless

    blank line(s) takusemba/ktlint-sample/app/…/Foo.kt:25:7: Line break before assignment is not allowed takusemba/ktlint-sample/app/…/Bar.kt:47:1: Unexpected blank line(s) before “}" …
  8. Outputs --reporter=checkstyle <?xml version="1.0" encoding="utf-8"?> <checkstyle version=“8.0"> <file name="takusemba/ktlint-sample/app/…/Hoge.kt"> <error

    line="25" column="7" severity="error" message="Line break before assignment is not allowed" source="no-line-break-before-assignment" /> </file> … </checkstyle>
  9. Outputs --reporter=custom-ktlint-rule class CustomReporter(private val out: PrintStream) : Reporter {

    private val errors = ArrayList<LintError>() override fun onLintError( file: String, err: LintError, corrected: Boolean) { errors.add(err) } override fun afterAll() { out.println("hello custom reporter.") out.println("found ${errors.size} errors.") } }
  10. Outputs --reporter=custom-ktlint-rule class CustomReporter(private val out: PrintStream) : Reporter {

    private val errors = ArrayList<LintError>() override fun onLintError( file: String, err: LintError, corrected: Boolean) { errors.add(err) } override fun afterAll() { out.println("hello custom reporter.") out.println("found ${errors.size} errors.") } } override fun afterAll() { out.println("hello custom reporter.") out.println("found ${errors.size} errors.") }
  11. Outputs class CustomReporterProvider : ReporterProvider { override val id: String

    = “custom-ktlint-reporter" override fun get( out: PrintStream, opt: Map<String, String> ): Reporter = CustomReporter(out) } --reporter=custom-ktlint-rule
  12. Outputs class CustomReporterProvider : ReporterProvider { override val id: String

    = “custom-ktlint-reporter" override fun get( out: PrintStream, opt: Map<String, String> ): Reporter = CustomReporter(out) } --reporter=custom-ktlint-rule ): Reporter = CustomReporter(out)
  13. Disable rules import package.* // ktlint-disable no-wildcard-imports import package.* //

    ktlint-disable disable specific rule disable the whole rule
  14. Add rules class CustomRule : Rule("custom-rule") { override fun visit(

    node: ASTNode, autoCorrect: Boolean, emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit ) { if (isLintError) { emit(node.startOffset, “LINT ERROR FOUND!”, false) } } }
  15. Add rules class CustomRule : Rule("custom-rule") { override fun visit(

    node: ASTNode, autoCorrect: Boolean, emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit ) { if (isLintError) { emit(node.startOffset, “LINT ERROR FOUND!”, false) } } } node: ASTNode,
  16. AST { return a + b } KtKeywordToken.fun fun Int

    : (a: Int, b: Int) add KtToken.IDENTIFIER KtTypeReference KtSingleValueToken.COLON KtParameterList KtBlockExpression
  17. AST { return a + b } KtKeywordToken.fun fun Int

    : (a: Int, b: Int) add KtToken.IDENTIFIER KtTypeReference KtSingleValueToken.COLON KtParameterList KtBlockExpression
  18. Add rules class CustomRule : Rule("custom-rule") { override fun visit(

    node: ASTNode, autoCorrect: Boolean, emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit ) { if (isLintError) { emit(node.startOffset, “LINT ERROR FOUND!”, false) } } }
  19. Add rules class CustomRule : Rule("custom-rule") { override fun visit(

    node: ASTNode, autoCorrect: Boolean, emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit ) { if (isLintError) { emit(node.startOffset, “LINT ERROR FOUND!”, false) } } } node: ASTNode,
  20. Add rules fun add(a: Int, b: Int): Int = a

    + b // OK fun add(a: Int, b: Int) = a + b // NG
  21. Add rules fun add(a: Int, b: Int) = a +

    b // NG if (node.elementType == KtStubElementTypes.FUNCTION && !node.text.contains('\n')) { val child = node.findChildByType(KtNodeTypes.VALUE_PARAMETER_LIST) ?: return if (child.treeNext.elementType == KtTokens.WHITE_SPACE && child.treeNext.treeNext.elementType == KtTokens.EQ) { emit(node.startOffset, "need return type!!!", false) } }
  22. Add rules fun add(a: Int, b: Int) = a +

    b // NG if (node.elementType == KtStubElementTypes.FUNCTION && !node.text.contains('\n')) { val child = node.findChildByType(KtNodeTypes.VALUE_PARAMETER_LIST) ?: return if (child.treeNext.elementType == KtTokens.WHITE_SPACE && child.treeNext.treeNext.elementType == KtTokens.EQ) { emit(node.startOffset, "need return type!!!", false) } } if (node.elementType == KtStubElementTypes.FUNCTION && !node.text.contains('\n')) { }
  23. Add rules fun add(a: Int, b: Int) = a +

    b // NG (a: Int, b: Int) if (node.elementType == KtStubElementTypes.FUNCTION && !node.text.contains('\n')) { val child = node.findChildByType(KtNodeTypes.VALUE_PARAMETER_LIST) ?: return if (child.treeNext.elementType == KtTokens.WHITE_SPACE && child.treeNext.treeNext.elementType == KtTokens.EQ) { emit(node.startOffset, "need return type!!!”, false) } } val child = node.findChildByType(KtNodeTypes.VALUE_PARAMETER_LIST) ?: return
  24. Add rules fun add(a: Int, b: Int) = a +

    b // NG if (node.elementType == KtStubElementTypes.FUNCTION && !node.text.contains('\n')) { val child = node.findChildByType(KtNodeTypes.VALUE_PARAMETER_LIST) ?: return if (child.treeNext.elementType == KtTokens.WHITE_SPACE && child.treeNext.treeNext.elementType == KtTokens.EQ) { emit(node.startOffset, "need return type!!!", false) } } if (child.treeNext.elementType == KtTokens.WHITE_SPACE && child.treeNext.treeNext.elementType == KtTokens.EQ) { =
  25. Add rules fun add(a: Int, b: Int) = a +

    b // NG if (node.elementType == KtStubElementTypes.FUNCTION && !node.text.contains('\n')) { val child = node.findChildByType(KtNodeTypes.VALUE_PARAMETER_LIST) ?: return if (child.treeNext.elementType == KtTokens.WHITE_SPACE && child.treeNext.treeNext.elementType == KtTokens.EQ) { emit(node.startOffset, "need return type!!!", false) } } fun add(a: Int, b: Int) = a + b emit(node.startOffset, "need return type!!!", false)
  26. Add rules class ExpressionFunctionRule : Rule("one-line-function") { override fun visit(

    node: ASTNode, autoCorrect: Boolean, emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit ) { if (node.elementType == KtStubElementTypes.FUNCTION && !node.text.contains('\n')) { val child = node.findChildByType(KtNodeTypes.VALUE_PARAMETER_LIST) ?: return if (child.treeNext.elementType == KtTokens.WHITE_SPACE && child.treeNext.treeNext.elementType == KtTokens.EQ) { emit(node.startOffset, "need return type!!!", false) } } } }
  27. Add rules class CustomRuleSetProvider : RuleSetProvider { override fun get():

    RuleSet = RuleSet( “custom-rule", ExpressionFunctionRule() ) }
  28. Add rules class CustomRuleSetProvider : RuleSetProvider { override fun get():

    RuleSet = RuleSet( “custom-rule", ExpressionFunctionRule() ) } ExpressionFunctionRule()
  29. Add rules val rule = ExpressionFunctionRule() assertThat(rule.lint( """ fun add(a:

    Int, b: Int): Int = a + b """ .trimIndent()) ).isEqualTo(emptyList<LintError>()) assertThat(rule.lint( """ fun add(a: Int, b: Int) = a + b """ .trimIndent()) ).isEqualTo(listOf(LintError(1, 1, "one-line-function", "need return type!!!")))
  30. Add rules val rule = ExpressionFunctionRule() assertThat(rule.lint( """ fun add(a:

    Int, b: Int): Int = a + b """ .trimIndent()) ).isEqualTo(emptyList<LintError>()) assertThat(rule.lint( """ fun add(a: Int, b: Int) = a + b """ .trimIndent()) ).isEqualTo(listOf(LintError(1, 1, "one-line-function", "need return type!!!"))) val rule = ExpressionFunctionRule() assertThat(rule.lint( """ fun add(a: Int, b: Int): Int = a + b """ .trimIndent()) ).isEqualTo(emptyList<LintError>())
  31. Add rules val rule = ExpressionFunctionRule() assertThat(rule.lint( """ fun add(a:

    Int, b: Int): Int = a + b """ .trimIndent()) ).isEqualTo(emptyList<LintError>()) assertThat(rule.lint( """ fun add(a: Int, b: Int) = a + b """ .trimIndent()) ).isEqualTo(listOf(LintError(1, 1, "one-line-function", "need return type!!!"))) assertThat(rule.lint( """ fun add(a: Int, b: Int) = a + b """ .trimIndent()) ).isEqualTo(listOf(LintError(1, 1, "one-line-function", "need return type!!!")))