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

Lint for Kotlin @R.kt#3

Avatar for TakuSemba TakuSemba
March 15, 2018

Lint for Kotlin @R.kt#3

Avatar for TakuSemba

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!!!")))