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

kotlin linter

kotlin linter

Kotlin Fest( https://kotlin.connpass.com/event/91666/ )で発表したkotlinのlintに関する発表資料です。

Shinnosuke Kugimiya

August 25, 2018
Tweet

More Decks by Shinnosuke Kugimiya

Other Decks in Technology

Transcript

  1. © DMM.com
    !LHNZTIJO
    ,PUMJO'FTU
    LPUMJOMJOUFS

    View Slide

  2. © DMM.com X
    w LHNZTIJOఝٶ ͗͘Έ΍

    w "OESPJEΤϯδχΞ
    w ߹ಉձࣾ%..DPN$50ࣨॴଐ
    ࣗݾ঺հ

    View Slide

  3. © DMM.com X
    ͜ͷηογϣϯͷΰʔϧ

    View Slide

  4. © DMM.com X
    w ηογϣϯΛݟͨํʑ͕ɺ,PUMJO༻ͷ֤छ-JOUFSͷ࢖
    ͍ํɺಛ௃Λཧղ͢Δ͜ͱ
    w ηογϣϯΛݟͨํʑ͕ɺ֤छ-JOUFSͷΧελϜ͠
    ͨϧʔϧͷ࡞ΕΔΑ͏ʹͳΔ͜ͱ
    w ηογϣϯΛݟͨํʑ͕ɺ-JOUFSΛٕज़બఆ͢Δ৔
    ߹ͷࡐྉʹͳΔ͜ͱ
    ͜ͷηογϣϯͷΰʔϧ

    View Slide

  5. © DMM.com X
    BHFOEB

    View Slide

  6. © DMM.com X
    w -JOUFSͱ͸
    w LUMJOU
    w Πϯετʔϧํ๏
    w ࢖͍ํ
    w ඪ४Ͱ༻ҙ͞Ε͍ͯΔϧʔϧ
    w ϧʔϧͷઃఆํ๏
    w ΧελϜϧʔϧͷ࡞Γํ
    w ࡞ͬͨΧελϜϧʔϧͷద༻ํ๏
    w ͦͷଞ
    w EFUFLU
    w BOESPJEMJOU
    w ·ͱΊ
    BHFOEB

    View Slide

  7. © DMM.com X
    -JOUFSͱ͸

    View Slide

  8. © DMM.com X
    –wikipedia
    l"MJOUFSPSMJOUSFGFSTUPUPPMTUIBUBOBMZ[FTPVSDF
    DPEFUPqBHQSPHSBNNJOHFSSPST CVHT TUZMJTUJD
    FSSPST BOETVTQJDJPVTDPOTUSVDUTz

    View Slide

  9. © DMM.com X
    –wikipedia
    l-JOUFS΋͘͠͸-JOU͸ɺϓϩάϥϛϯάΤϥʔɺ
    όάɺॻࣜΤϥʔɺ͓ΑͼٙΘ͍͠ߏ଄ʹϑϥά
    ΛཱͯΔͨΊͷιʔείʔυΛ෼ੳ͢Δϓϩάϥ
    ϜͰ͋Δz

    View Slide

  10. © DMM.com X
    LPUMJOͷMJOUFS
    LPUMJOͷMJOUFS͸ओʹ͸ೋͭɻͦͯ͠ɺBOESPJEͷඪ४MJOU
    ͕LPUMJOʹ΋ରԠ͍ͯ͠Δɻ
    w LUMJOU
    w EFUFLU
    w BOESPJEMJOU

    ͜ͷ̏ͭʹ͍ͭͯઆ໌͍͖ͯ͠·͢ɻ

    View Slide

  11. © DMM.com X
    LUMJOU

    View Slide

  12. © DMM.com
    Πϯετʔϧํ๏

    View Slide

  13. © DMM.com X
    Πϯετʔϧํ๏
    $ curl -sSLO https://github.com/shyiko/ktlint/releases/download/
    0.27.0/ktlint &&
    chmod a+x ktlint &&
    sudo mv ktlint /usr/local/bin/
    IUUQTHJUIVCDPNTIZJLPLUMJOUJOTUBMMBUJPO
    3&"%.&ͷJOTUBMMBUJPOʹै͏ɻ

    View Slide

  14. © DMM.com X
    HSBEMFΛ࢖͏৔߹
    QMVHJO͕ೋͭ͋ΔʢQMVHJOͳ͠Ͱ΋ɺઃఆͰ͖Δʣ
    w IUUQTHJUIVCDPNKMMFJUTDIVILUMJOUHSBEMF
    w IUUQTHJUIVCDPNKFSFNZNBJMFOLPUMJOUFSHSBEMF
    ࣗ෼͸ALPMJOUFSHSBEMFAΛ࢖͍ͬͯΔɻ
    ٕज़બఆ౰࣌ɺLUMJOUHSBEMFͷํͰ͸DVTUPNSVMFΛద
    ༻Ͱ͖ͳ͔ͬͨͷ͕ཧ༝ɻ
    ݱࡏ͸ͦͷ໰୊͸ղফ͞Ε͍ͯΔͷͰɺͲͪΒͰ΋ྑ͞
    ͦ͏ɻ

    View Slide

  15. © DMM.com
    ࢖͍ํ

    View Slide

  16. © DMM.com X
    ࢖͍ํ
    LUMJOU
    LU6OFYQFDUFETQBDJOHBGUFS
    LU6OFYQFDUFETQBDJOHBGUFS
    LU6OOFDFTTBSZTQBDF T

    LU/FFEMFTTCMBOLMJOF T

    LU6OFYQFDUFETQBDJOHBGUFS
    LU6OFYQFDUFETQBDJOHBGUFS
    LU.JTTJOHTQBDFBGUFS
    LU6OFYQFDUFETQBDJOHBGUFS

    ίϚϯυΛଧ͚ͭͩɻΤϥʔݸॴͷҰཡ͕දࣔ͞ΕΔɻ

    View Slide

  17. © DMM.com
    ඪ४Ͱ༻ҙ͞Ε͍ͯΔϧʔϧ

    View Slide

  18. © DMM.com X
    ඪ४Ͱ༻ҙ͞Ε͍ͯΔϧʔϧ
    TQBDFTGPSJOEFOUBUJPO
    /PTFNJDPMPOT VOMFTTVTFEUPTFQBSBUFNVMUJQMFTUBUFNFOUTPOUIFTBNFMJOF

    /PXJMEDBSEVOVTFEAJNQPSUAT
    /PDPOTFDVUJWFCMBOLMJOFT
    /PCMBOLMJOFTCFGPSFA^A
    /PUSBJMJOHXIJUFTQBDFT
    /PA6OJUASFUVSOT AGVOGO\^AJOTUFBEPGAGVOGO6OJU\^A

    /PFNQUZ A\^A
    DMBTTCPEJFT
    /PTQBDFTBSPVOESBOHF AA
    PQFSBUPS
    /POFXMJOFCFGPSF CJOBSZ
    AAAA AA AA AA AA AccA
    8IFOXSBQQJOHDIBJOFEDBMMTAA A ABOEA ATIPVMECFQMBDFEPOUIFOFYUMJOF
    8IFOBMJOFJTCSPLFOBUBOBTTJHONFOU AA
    PQFSBUPSUIFCSFBLDPNFTBGUFSUIFTZNCPM
    8IFODMBTTGVODUJPOTJHOBUVSFEPFTOUpUPOBTJOHMFMJOF FBDIQBSBNFUFSNVTUCFPOB
    TFQBSBUFMJOF
    $POTJTUFOUTUSJOHUFNQMBUFT AWAJOTUFBEPGA\W^A A\QW^AJOTUFBEPGA\QWUP4USJOH
    ^A

    $POTJTUFOUPSEFSPGNPEJpFST
    $POTJTUFOUTQBDJOHBGUFSLFZXPSET DPNNBTBSPVOEDPMPOT DVSMZCSBDFT QBSFOT JOpY
    PQFSBUPST DPNNFOUT FUD
    /FXMJOFBUUIFFOEPGFBDIpMF OPUFOBCMFECZEFGBVMU CVUSFDPNNFOEFE

    View Slide

  19. © DMM.com X
    ඪ४Ͱ༻ҙ͞Ε͍ͯΔϧʔϧ
    Πϯσϯτ͸ͭ
    ηϛίϩϯېࢭ
    ϫΠϧυΧʔυJNQPSUɺ࢖༻ͯ͠ͳ͍JNQPSUېࢭ
    ࿈ଓۭͨ͠ߦېࢭ
    A^Aͷ௚લͷۭߦېࢭ
    ຤ඌۭߦېࢭ
    6OJUͷSFUVSOېࢭ
    ۭͷΫϥεϘσΟA\^Aېࢭ
    SBOHFΦϖϨʔλʔAAͷલޙʹۭനېࢭ
    AA AA AA AA AA AA AccAͷΦϖϨʔλͷ௚લʹ
    վߦېࢭ

    View Slide

  20. © DMM.com X
    ඪ४Ͱ༻ҙ͞Ε͍ͯΔϧʔϧ
    AA A A A Aͷ௚ޙͰվߦېࢭ
    AAͷ௚લͰվߦېࢭ
    ෳ਺ύϥϝʔλͷ৔߹ɺશͯվߦ͢Δ͔ҰߦͰॻ͘
    Ұ؏ͨ͠TUSJOHUFNQMBUFʢA\W^AͰ͸ͳͯ͘AWAɻ
    A\QWUP4USJOH
    ^AͰͳͯ͘A\QW^Aɻ

    Ұ؏ͨ͠म০ࢠͷॱ൪
    Ұ؏ͨ͠εϖʔγϯά
    ֤ϑΝΠϧͷ຤ඌʹվߦΛೖΕΔ σϑΥϧτͰPOʹͳͬͯ
    ͸ͳ͍͕ɺਪ঑

    View Slide

  21. © DMM.com
    ϧʔϧͷઃఆํ๏

    View Slide

  22. © DMM.com X
    ඪ४ϧʔϧͷઃఆํ๏
    جຊతʹ֤ϧʔϧʹ͍ͭͯɺFOBCMFEJTBCMFͳͲͷ
    ઃఆ͸Ͱ͖ͳ͍ɻ
    ͨͩ͠ɺΠϯσϯταΠζͱϑΝΠϧ຤ඌվߦʹ͍ͭͯ
    ͸&EJUPS$POpHͰઃఆͰ͖Δɻ
    [*.{kt,kts}]
    indent_size=2
    continuation_indent_size=2
    insert_final_newline=true

    View Slide

  23. © DMM.com X
    ඪ४ϧʔϧͷઃఆํ๏
    ·ͨɺಛఆͷ৔ॴ͚ͩɺಛఆͷϧʔϧ΍શϧʔϧΛແࢹ
    ͢Δ͜ͱ͕Ͱ͖Δ
    import package.* // ktlint-disable no-wildcard-imports
    /* ktlint-disable parameter-list-wrapping */
    data class A(
    val a: String, val b: String,
    val c: String
    )
    /* ktlint-disable parameter-list-wrapping */
    /* ktlint-disable */
    : શ෦ແࢹ
    /* ktlint-disable */

    View Slide

  24. © DMM.com
    ΧελϜϧʔϧͷ࡞Γํ

    View Slide

  25. © DMM.com
    ·ͣ͸"45Λ஌Δ

    View Slide

  26. © DMM.com X
    ·ͣ͸"45 ˺14*
    Λ஌Δ
    package yourpackage
    class Hoge {
    fun fuga(): Int = 2 + 3
    }

    View Slide

  27. © DMM.com X
    ·ͣ͸"45 ˺14*
    Λ஌Δ
    'JMF
    1"$,"(&
    %*3&$5*7&
    $-"44
    FMFNFOU DMBTT
    8)*5&@41"$&
    FMFNFOU

    *%&/5*'*&3

    8)*5&@41"$& $-"44@#0%:
    package yourpackage
    class Hoge {
    fun fuga(): Int = 2 + 3
    }
    πϦʔߏ଄Ͱղऍ͞ΕΔ

    View Slide

  28. © DMM.com X
    ͨͱ͑͹ɺҾ਺ͰA A͕͋ͬͨΒઈରվߦ͢ΔΧελϜϧʔϧΛ࡞Δ࣌
    fuga(1, 2)
    fuga(
    1,
    2
    )
    /(
    0,

    View Slide

  29. © DMM.com X
    ͨͱ͑͹ɺҾ਺ͰA A͕͋ͬͨΒઈରվߦ͢ΔΧελϜϧʔϧΛ࡞Δ࣌
    7"-6&
    "3(6.&/5-*45
    7"-6&
    "3(6.&/5
    $0.." 8)*5&41"$&
    7"-6&
    "3(6.&/5
    /(ͷ࣌΋0,ͷ࣌΋
    "45ͷπϦʔ͸͜ͷΑ͏ʹͳΔ Ұ෦୺ંͬͯ·͢




    View Slide

  30. © DMM.com X
    ͨͱ͑͹ɺҾ਺ͰA A͕͋ͬͨΒઈରվߦ͢ΔΧελϜϧʔϧΛ࡞Δ࣌
    7"-6&
    "3(6.&/5-*45
    7"-6&
    "3(6.&/5
    $0.." 8)*5&41"$&
    7"-6&
    "3(6.&/5



    0,
    վߦίʔυؚ͕·Ε͍ͯΔ̋

    View Slide

  31. © DMM.com X
    ͨͱ͑͹ɺҾ਺ͰA A͕͋ͬͨΒઈରվߦ͢ΔΧελϜϧʔϧΛ࡞Δ࣌
    7"-6&
    "3(6.&/5-*45
    7"-6&
    "3(6.&/5
    $0.." 8)*5&41"$&
    7"-6&
    "3(6.&/5



    /(
    վߦίʔυؚ͕·Ε͍ͯͳ͍ʷ

    View Slide

  32. © DMM.com X
    ͨͱ͑͹ɺҾ਺ͰA A͕͋ͬͨΒઈରվߦ͢ΔΧελϜϧʔϧΛ࡞Δ࣌
    ΧελϜϧʔϧͷϩδοΫ͸ɺԼهΛ֬ೝ͢Δ
    w Լهͷߏ଄ʹͳ͍ͬͯΔͱ͖ʹ
    7"-6&
    "3(6.&/5-*45
    7"-6&
    "3(6.&/5
    $0.." 8)*5&41"$&
    7"-6&
    "3(6.&/5
    w $0.."ͷ࣍ͷ8)*5&@41"$&ʹվߦίʔυΛؚ·ΕΔ͜ͱ
    LUMJOUͰ΋EFUFLUͰ΋BOESPJEMJOUͰ΋ͲΕͰ΋ɺ
    ɹɹɹɹɹɹΧελϜϧʔϧ͸͜ͷΑ͏ʹͯ͠࡞͍͖ͬͯ·͢

    View Slide

  33. © DMM.com X
    ͳΜͱͳ͘Θ͔͚ͬͨͲɺ
    ͦ΋ͦ΋πϦʔߏ଄͕Ͳ͏ͳͬͯΔͱ͔Θ͔ΒΜʜ

    View Slide

  34. © DMM.com X
    େৎ෉ɺ๻΋Θ͔Γ·ͤΜ

    View Slide

  35. © DMM.com X
    1TJ7JFXFS࢖͍·͠ΐ͏
    IUUQTQMVHJOTKFUCSBJOTDPNQMVHJOQTJWJFXFS

    View Slide

  36. © DMM.com
    LUMJOUͰ$VTUPN-JOU

    View Slide

  37. © DMM.com X
    αϯϓϧͱͯ͠ɺ͜ͷΧελϜϧʔϧΛ࡞Δ
    fuga(1, 2)
    fuga(
    1,
    2
    )
    /(
    0,
    Ҿ਺ͰA A͕͋ͬͨΒվߦΛଅ͢

    View Slide

  38. © DMM.com X
    खॱ
    ςϯϓϨʔτΛDMPOFͯ͘͠Δ
    3VMFΛ࡞Δ
    3VMF4FU1SPWJEFSΛ࡞Δ
    SFTPVSDFT.&5"*/'TFSWJDFT
    DPNHJUIVCTIZJLPLUMJOUDPSF3VMF4FU1SPWJEFSʹ࡞ͬͨ
    3VMF4FU1SPWJEFSΛهड़
    KBSΛ࡞Δ

    View Slide

  39. © DMM.com X
    ςϯϓϨʔτΛDMPOFͯ͘͠Δ
    ςϯϓϨʔτ͕LUMJOUͷϦϙδτϦʹ༻ҙ͞Ε͍ͯΔͷͰɺ͜
    ΕΛམͱͯ͘͠Δɻ
    IUUQTHJUIVCDPNTIZJLPLUMJOUCMPCNBTUFSLUMJOUSVMFTFU
    UFNQMBUF
    ෆཁͳαϯϓϧͳͲΛ࡟আͯ͠ɺ࡞੒։࢝ɻ

    View Slide

  40. © DMM.com X
    3VMFΛ࡞Δ
    abstract class Rule(val id: String) {
    ...
    abstract fun visit(
    node: ASTNode,
    autoCorrect: Boolean,
    emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) ->
    Unit
    )
    }
    ࡞Γ͍ͨ3VMFΛ࡞ͬͯߦ͘ɻ
    ΍Δ͜ͱ͸Ұͭɻ
    3VMFΛܧঝͯ͠ɺWJTJUϝιουΛ࣮૷͢Δɻ

    View Slide

  41. © DMM.com X
    WJTJUϝιου
    'JMF
    1"$,"(&
    %*3&$5*7&
    $-"44
    FMFNFOU DMBTT
    8)*5&@41"$&
    FMFNFOU

    *%&/5*'*&3

    8)*5&@41"$& $-"44@#0%:
    WJTJU
    WJTJU
    WJTJU
    WJTJU WJTJU WJTJU WJTJU WJTJU
    "45ͷOPEFΛ্͔ΒॱʹḷͬͯWJTJUϝιου͕ݺ͹ΕΔ

    View Slide

  42. © DMM.com X
    WJTJUϝιου
    WJTJUϝιουͰԼهͷϩδοΫΛॻ͘
    7"-6&
    "3(6.&/5-*45
    7"-6&
    "3(6.&/5
    $0.." 8)*5&41"$&
    7"-6&
    "3(6.&/5
    w $0.."ͷ࣍ͷ8)*5&@41"$&ʹվߦίʔυ͕ͳ͍৔
    ߹͸ܯࠂΛग़͢
    w Լهͷߏ଄ʹͳ͍ͬͯΔ࣌ʹ

    View Slide

  43. © DMM.com X
    ྫ͑͹͜Μͳײ͡ʹ
    7"-6&
    "3(6.&/5-*45
    7"-6&
    "3(6.&/5
    $0.." 8)*5&41"$&
    7"-6&
    "3(6.&/5
    7"-6&
    "3(6.&/5
    -JTU
    ʹͨͲΓண͍ͨ࣌
    WJTJU

    View Slide

  44. © DMM.com X
    ྫ͑͹͜Μͳײ͡ʹ
    7"-6&
    "3(6.&/5-*45
    7"-6&
    "3(6.&/5
    $0.." 8)*5&41"$&
    7"-6&
    "3(6.&/5
    7"-6&
    "3(6.&/5
    -JTU
    ʹͨͲΓண͍ͨ࣌
    WJTJU
    DIJMESFOͷதʹ
    $0.."͕͍Δ৔߹ʹ

    View Slide

  45. © DMM.com X
    ྫ͑͹͜Μͳײ͡ʹ
    7"-6&
    "3(6.&/5-*45
    7"-6&
    "3(6.&/5
    $0.." 8)*5&41"$&
    7"-6&
    "3(6.&/5
    7"-6&
    "3(6.&/5
    -JTU
    ʹͨͲΓண͍ͨ࣌
    WJTJU
    DIJMESFOͷதʹ
    $0.."͕͍Δ৔߹ʹ ࣍ͷۭന͕
    վߦΛؚΉ͔ʁ

    View Slide

  46. © DMM.com X
    ίʔυʹ͢Δͱ
    override fun visit(
    node: ASTNode,
    autoCorrect: Boolean,
    emit: (
    offset: Int,
    errorMessage: String,
    canBeAutoCorrected: Boolean
    ) -> Unit
    ) {
    if (node.elementType == KtNodeTypes.VALUE_PARAMETER_LIST) {
    val isError = node.children().any {
    it.elementType == KtTokens.COMMA &&
    it.treeNext.elementType == KtTokens.WHITE_SPACE &&
    !it.treeNext.text.contains("\n")
    }
    if (isError) {
    emit(
    node.startOffset,
    "should line break",
    false
    )
    }
    }
    }

    View Slide

  47. © DMM.com X
    ίʔυʹ͢Δͱ
    override fun visit(
    node: ASTNode,
    autoCorrect: Boolean,
    emit: (
    offset: Int,
    errorMessage: String,
    canBeAutoCorrected: Boolean
    ) -> Unit
    ) {
    if (node.elementType == KtNodeTypes.VALUE_ARGUMENT_LIST) {
    val isError = node.children().any {
    it.elementType == KtTokens.COMMA &&
    it.treeNext.elementType == KtTokens.WHITE_SPACE &&
    !it.treeNext.text.contains("\n")
    }
    if (isError) {
    emit(
    node.startOffset,
    "should line break",
    false
    )
    }
    }
    }
    OPEF͕
    7"-6&@"3(6.&/5@-*45ͷ࣌ʹ

    View Slide

  48. © DMM.com X
    ίʔυʹ͢Δͱ
    override fun visit(
    node: ASTNode,
    autoCorrect: Boolean,
    emit: (
    offset: Int,
    errorMessage: String,
    canBeAutoCorrected: Boolean
    ) -> Unit
    ) {
    if (node.elementType == KtNodeTypes.VALUE_ARGUMENT_LIST) {
    val isError = node.children().any {
    it.elementType == KtTokens.COMMA &&
    it.treeNext.elementType == KtTokens.WHITE_SPACE &&
    !it.treeNext.text.contains("\n")
    }
    if (isError) {
    emit(
    node.startOffset,
    "should line break",
    false
    )
    }
    }
    }
    OPEF͕
    DIJMESFOͷதʹ$0.."͕͋Δ࣌ʹ

    View Slide

  49. © DMM.com X
    ίʔυʹ͢Δͱ
    override fun visit(
    node: ASTNode,
    autoCorrect: Boolean,
    emit: (
    offset: Int,
    errorMessage: String,
    canBeAutoCorrected: Boolean
    ) -> Unit
    ) {
    if (node.elementType == KtNodeTypes.VALUE_ARGUMENT_LIST) {
    val isError = node.children().any {
    it.elementType == KtTokens.COMMA &&
    it.treeNext.elementType == KtTokens.WHITE_SPACE &&
    !it.treeNext.text.contains("\n")
    }
    if (isError) {
    emit(
    node.startOffset,
    "should line break",
    false
    )
    }
    }
    }
    ࣍ͷۭനʹ
    վߦؚ͕·Ε͍ͯΔ͔ʁ

    View Slide

  50. © DMM.com X
    ίʔυʹ͢Δͱ
    override fun visit(
    node: ASTNode,
    autoCorrect: Boolean,
    emit: (
    offset: Int,
    errorMessage: String,
    canBeAutoCorrected: Boolean
    ) -> Unit
    ) {
    if (node.elementType == KtNodeTypes.VALUE_PARAMETER_LIST) {
    val isError = node.children().any {
    it.elementType == KtTokens.COMMA &&
    it.treeNext.elementType == KtTokens.WHITE_SPACE &&
    !it.treeNext.text.contains("\n")
    }
    if (isError) {
    emit(
    node.startOffset,
    "should line break",
    false
    )
    }
    }
    }
    վߦ͞Εͯ
    ͳ͔ͬͨΒ
    FNJUʂ

    View Slide

  51. © DMM.com X
    3VMF4FU1SPWJEFSΛ࡞Δ
    3VMF4FU1SPWJEFSΛܧঝ͠ɺHFUϝιουΛ࣮૷͢Δɻ
    ࡞ͬͨ3VMFΛొ࿥͢Δɻ
    class CustomRuleSetProvider : RuleSetProvider {
    override fun get(): RuleSet =
    RuleSet("custom", LineBreakInArgsListRule())
    }

    View Slide

  52. © DMM.com X
    DPNHJUIVCTIZJLPLUMJOUDPSF3VMF4FU1SPWJEFS
    SFTPVSDFT.&5"*/'TFSWJDFT
    DPNHJUIVCTIZJLPLUMJOUDPSF3VMF4FU1SPWJEFSʹ࡞ͬͨ
    3VMF4FU1SPWJEFSΛهड़
    com.kgmyshin.ktlint.CustomRuleSetProvider

    View Slide

  53. © DMM.com X
    KBSΛ࡞Δ
    KBSΛ࡞੒͢Δɻ
    ./gradlew jar
    CVJMEMJCT഑Լʹ࡞੒͞ΕΔɻ

    View Slide

  54. © DMM.com
    ࡞ͬͨΧελϜϧʔϧΛద༻͢Δ

    View Slide

  55. © DMM.com X
    ࡞ͬͨΧελϜϧʔϧΛద༻͢Δ
    $ ktlint -R ktlint-custom-rulseset.jar “src/main/**/*.kt"
    3ͰKBSͷύεΛ౉͢
    $ ktlint -R ktlint-custom-ruleset.jar “app/src/main/**/*.kt”
    ...
    Lint error > ...kt:43:17: should line break
    Lint error > ....kt:43:33: should line break
    ͦΕ͚ͩͰ0,ɻ

    View Slide

  56. © DMM.com X
    LPUMJOUFSHSBEMFΛ࢖͍ͬͯΔ৔߹
    buildscript {
    dependencies {
    classpath "gradle.plugin.org.jmailen.gradle:kotlinter-gradle:1.11.1"
    classpath files('lint/ktlint/ktlint-custom-ruleset.jar')
    }
    }
    DMBTTQBUIΛ௨͢
    $ ./gradlew lintKotlin
    ...
    > Task :app:lintKotlinMain
    Lint error > ...kt:43:17: should line break
    Lint error > ....kt:43:33: should line break
    ͦΕ͚ͩͰ0,ɻ

    View Slide

  57. © DMM.com
    ͦͷଞGPSNBUΛ͔͚Δ

    View Slide

  58. © DMM.com X
    GPSNBUΛ͔͚Δ
    LUMJOU͸ܯࠂΛग़͚ͩ͢Ͱͳ͘ɺ͋Δఔ౓GPSNBUΛ͔͚Δ͜ͱ
    ͕Ͱ͖Δɻ
    LPMJOUFSHSBEMFͷ৔߹
    $ ktlint -F “src/main/**/*.kt”
    $ ./gradlew formatKotlin

    View Slide

  59. © DMM.com X
    GPSNBUΛ͔͚Δ
    data class A(
    val bb: String,
    val cc: String,
    val dd: String
    )
    #FGPSF
    "GUFS data class A(
    val bb: String,
    val cc: String,
    val dd: String
    )
    $ ./gradlew formatKotlin

    View Slide

  60. © DMM.com X
    ΧελϜϧʔϧͷ৔߹
    ΧελϜϧʔϧͷ৔߹Ͱ΋ɺGPSNBUΛ͔͚Δ͜ͱ͸Ͱ͖Δɻ
    ͨͩ͠ʢ΋ͪΖΜ͚ͩͲʣɺGPSNBUͷϩδοΫΛ࣮૷͢Δඞ
    ཁ͕͋Δɻ

    View Slide

  61. © DMM.com X
    ઌ΄Ͳͷίʔυ
    override fun visit(
    node: ASTNode,
    autoCorrect: Boolean,
    emit: (
    offset: Int,
    errorMessage: String,
    canBeAutoCorrected: Boolean
    ) -> Unit
    ) {
    if (node.elementType == KtNodeTypes.VALUE_PARAMETER_LIST) {
    val isError = node.children().any {
    it.elementType == KtTokens.COMMA &&
    it.treeNext.elementType == KtTokens.WHITE_SPACE &&
    !it.treeNext.text.contains("\n")
    }
    if (isError) {
    emit(
    node.startOffset,
    "should line break",
    false
    )
    }
    }
    }
    GPSNBUΦϓγϣϯ͕͍ͭͯΔ࣌
    UVSFʹͳΔ

    View Slide

  62. © DMM.com X
    ઌ΄Ͳͷίʔυ
    override fun visit(
    node: ASTNode,
    autoCorrect: Boolean,
    emit: (
    offset: Int,
    errorMessage: String,
    canBeAutoCorrected: Boolean
    ) -> Unit
    ) {
    if (node.elementType == KtNodeTypes.VALUE_PARAMETER_LIST) {
    val isError = node.children().any {
    it.elementType == KtTokens.COMMA &&
    it.treeNext.elementType == KtTokens.WHITE_SPACE &&
    !it.treeNext.text.contains("\n")
    }
    if (isError) {
    emit(
    node.startOffset,
    "should line break",
    false
    )
    if (autoCorrect) {
    format(node)
    }
    }
    }
    }
    ௥Ճ

    View Slide

  63. © DMM.com X
    GPSNBUϝιουͷத਎
    private fun format(node: ASTNode) {
    val indent = node.indentSize()
    node.children().forEach {
    if (..লུ..) {
    (it.treeNext as LeafPsiElement).rawReplaceWithText(
    "\n" + " ".repeat(indent)
    )
    }
    }
    }
    GPSNBUϝιουͷத਎ͷྫɻ
    ࣮ࡍͷOPEFͷ಺༰Λ௚઀ฤू͢Ε͹0,ɻ

    View Slide

  64. © DMM.com X
    GPSNBUϝιουͷத਎
    private fun format(node: ASTNode) {
    val indent = node.indentSize()
    node.children().forEach {
    if (..লུ..) {
    (it.treeNext as LeafPsiElement).rawReplaceWithText(
    "\n" + " ".repeat(indent)
    )
    }
    }
    }
    GPSNBUϝιουͷத਎ͷྫɻ
    ࣮ࡍͷOPEFͷ಺༰Λ௚઀ฤू͢Ε͹0,ɻ

    View Slide

  65. © DMM.com X
    ΧελϜϧʔϧͷ৔߹
    data class A(
    val bb: String, val cc: String, val dd: String
    )
    #FGPSF
    "GUFS data class A(
    val bb: String,
    val cc: String,
    val dd: String
    )
    $ ./gradlew formatKotlin

    View Slide

  66. © DMM.com X
    EFUFLU

    View Slide

  67. © DMM.com
    Πϯετʔϧํ๏

    View Slide

  68. © DMM.com X
    HSBEMFQSPKFDUʹಋೖ͢Δ৔߹
    CVJMEHSBEMFʹԼهΛهड़ɻ
    buildscript {
    repositories {
    jcenter()
    }
    }
    plugins {
    id "io.gitlab.arturbosch.detekt" version "1.0.0.RC8"
    }
    detekt {
    version = "1.0.0.[version]"
    defaultProfile {
    input = file("src/main/kotlin")
    filters = ".*/resources/.*,.*/build/.*"
    }
    }

    View Slide

  69. © DMM.com
    ࢖͍ํ

    View Slide

  70. © DMM.com X
    ࢖͍ํ
    AHSBEMFXEFUFLU$IFDLAΛ࣮ߦ͢Δ͚ͩ

    View Slide

  71. © DMM.com X
    ࢖͍ํ
    $ ./gradlew detektCheck
    :
    Ruleset: comments
    Ruleset: complexity - 20min debt
    LongMethod - 29/20 - [onCreate] at ...kt:45:3
    Ruleset: detekt-custom-rules - 5min debt
    line-break-in-args-list - [A] at ...kt:42:13
    :
    Overall debt: 25min
    Complexity Report:
    - 489 lines of code (loc)
    - 416 source lines of code (sloc)
    - 263 logical lines of code (lloc)
    - 3 comment lines of code (cloc)
    - 46 McCabe complexity (mcc)
    - 2 number of total code smells
    - 0 % comment source ratio
    - 174 mcc per 1000 lloc
    - 7 code smells per 1000 lloc
    Project Statistics:
    - number of properties: 32
    - number of functions: 24
    - number of classes: 26
    - number of packages: 3
    - number of kt files: 12
    :
    detekt finished in 1804 ms.

    View Slide

  72. © DMM.com X
    HSBEMFQSPKFDUʹಋೖ͢Δ৔߹
    $ ./gradlew detektCheck
    :
    Ruleset: comments
    Ruleset: complexity - 20min debt
    LongMethod - 29/20 - [onCreate] at ...kt:45:3
    Ruleset: detekt-custom-rules - 5min debt
    line-break-in-args-list - [A] at ...kt:42:13
    :
    Overall debt: 25min
    Complexity Report:
    - 489 lines of code (loc)
    - 416 source lines of code (sloc)
    - 263 logical lines of code (lloc)
    - 3 comment lines of code (cloc)
    - 46 McCabe complexity (mcc)
    - 2 number of total code smells
    - 0 % comment source ratio
    - 174 mcc per 1000 lloc
    - 7 code smells per 1000 lloc
    Project Statistics:
    - number of properties: 32
    - number of functions: 24
    - number of classes: 26
    - number of packages: 3
    - number of kt files: 12
    :
    detekt finished in 1804 ms.
    -JOUͷ݁Ռ

    View Slide

  73. © DMM.com X
    HSBEMFQSPKFDUʹಋೖ͢Δ৔߹
    $ ./gradlew detektCheck
    :
    Ruleset: comments
    Ruleset: complexity - 20min debt
    LongMethod - 29/20 - [onCreate] at ...kt:45:3
    Ruleset: detekt-custom-rules - 5min debt
    line-break-in-args-list - [A] at ...kt:42:13
    :
    Overall debt: 25min
    Complexity Report:
    - 489 lines of code (loc)
    - 416 source lines of code (sloc)
    - 263 logical lines of code (lloc)
    - 3 comment lines of code (cloc)
    - 46 McCabe complexity (mcc)
    - 2 number of total code smells
    - 0 % comment source ratio
    - 174 mcc per 1000 lloc
    - 7 code smells per 1000 lloc
    Project Statistics:
    - number of properties: 32
    - number of functions: 24
    - number of classes: 26
    - number of packages: 3
    - number of kt files: 12
    :
    detekt finished in 1804 ms.
    -JOUͷमਖ਼ʹ͔͔Δ࣌ؒ

    View Slide

  74. © DMM.com X
    HSBEMFQSPKFDUʹಋೖ͢Δ৔߹
    $ ./gradlew detektCheck
    :
    Ruleset: comments
    Ruleset: complexity - 20min debt
    LongMethod - 29/20 - [onCreate] at ...kt:45:3
    Ruleset: detekt-custom-rules - 5min debt
    line-break-in-args-list - [A] at ...kt:42:13
    :
    Overall debt: 25min
    Complexity Report:
    - 489 lines of code (loc)
    - 416 source lines of code (sloc)
    - 263 logical lines of code (lloc)
    - 3 comment lines of code (cloc)
    - 46 McCabe complexity (mcc)
    - 2 number of total code smells
    - 0 % comment source ratio
    - 174 mcc per 1000 lloc
    - 7 code smells per 1000 lloc
    Project Statistics:
    - number of properties: 32
    - number of functions: 24
    - number of classes: 26
    - number of packages: 3
    - number of kt files: 12
    :
    detekt finished in 1804 ms.
    ෳࡶ౓

    View Slide

  75. © DMM.com X
    HSBEMFQSPKFDUʹಋೖ͢Δ৔߹
    $ ./gradlew detektCheck
    :
    Ruleset: comments
    Ruleset: complexity - 20min debt
    LongMethod - 29/20 - [onCreate] at ...kt:45:3
    Ruleset: detekt-custom-rules - 5min debt
    line-break-in-args-list - [A] at ...kt:42:13
    :
    Overall debt: 25min
    Complexity Report:
    - 489 lines of code (loc)
    - 416 source lines of code (sloc)
    - 263 logical lines of code (lloc)
    - 3 comment lines of code (cloc)
    - 46 McCabe complexity (mcc)
    - 2 number of total code smells
    - 0 % comment source ratio
    - 174 mcc per 1000 lloc
    - 7 code smells per 1000 lloc
    Project Statistics:
    - number of properties: 32
    - number of functions: 24
    - number of classes: 26
    - number of packages: 3
    - number of kt files: 12
    :
    detekt finished in 1804 ms.
    ϑΝΠϧ਺ͳͲͷϨϙʔτ

    View Slide

  76. © DMM.com
    ඪ४Ͱ༻ҙ͞Ε͍ͯΔϧʔϧ

    View Slide

  77. © DMM.com X
    ඪ४Ͱ༻ҙ͞Ε͍ͯΔϧʔϧ͸͍ͬͺ͍͋Δ
    $IBJO8SBQQJOH
    $MBTT/BNJOH
    $PNNFOU4QBDJOH
    $PNQMFY$POEJUJPO
    $PNQMFY.FUIPE
    %VQMJDBUF$BTF*O8IFO&YQSFTTJPO
    &NQUZ$BUDI#MPDL
    &NQUZ$MBTT#MPDL
    &NQUZ%FGBVMU$POTUSVDUPS
    &NQUZ%P8IJMF#MPDL
    &NQUZ&MTF#MPDL
    &NQUZ'JOBMMZ#MPDL
    &NQUZ'PS#MPDL
    &NQUZ'VODUJPO#MPDL
    &NQUZ*G#MPDL
    &NQUZ*OJU#MPDL
    &NQUZ,U'JMF
    &NQUZ4FDPOEBSZ$POTUSVDUPS
    &NQUZ8IFO#MPDL
    &NQUZ8IJMF#MPDL
    &OVN/BNJOH
    &RVBMT8JUI)BTI$PEF&YJTU
    &YQMJDJU(BSCBHF$PMMFDUJPO$BMM
    'JMFOBNF
    'JOBM/FXMJOF
    'PSCJEEFO$PNNFOU
    'PS&BDI0O3BOHF
    'VODUJPO/BNJOH
    *NQPSU0SEFSJOH
    *OEFOUBUJPO
    -BSHF$MBTT
    -POH.FUIPE
    -POH1BSBNFUFS-JTU
    .BHJD/VNCFS
    .BUDIJOH%FDMBSBUJPO/BNF
    .BYJNVN-JOF-FOHUI
    .BY-JOF-FOHUI
    .PEJpFS0SEFS
    .PEJpFS0SEF
    /FTUFE#MPDL%
    /FX-JOF"U&O
    /P#MBOL-JOF#
    /P$POTFDVUJW
    /P&NQUZ$MBT
    /P*U1BSBN*O.
    /P-JOF#SFBL"
    /P-JOF#SFBL#
    /P.VMUJQMF4Q
    /P4FNJDPMPO
    /P5SBJMJOH4QB
    /P6OJU3FUVSO
    /P6OVTFE*N
    /P8JMEDBSE*N
    0CKFDU1SPQFS
    0QUJPOBM"CTUS
    1BDLBHF/BN
    1BSBNFUFS-JT

    View Slide

  78. © DMM.com X
    ඪ४Ͱ༻ҙ͞Ε͍ͯΔϧʔϧ͸͍ͬͺ͍͋Δ
    $IBJO8SBQQJOH
    $MBTT/BNJOH
    $PNNFOU4QBDJOH
    $PNQMFY$POEJUJPO
    $PNQMFY.FUIPE
    %VQMJDBUF$BTF*O8IFO&YQSFTTJPO
    &NQUZ$BUDI#MPDL
    &NQUZ$MBTT#MPDL
    &NQUZ%FGBVMU$POTUSVDUPS
    &NQUZ%P8IJMF#MPDL
    &NQUZ&MTF#MPDL
    &NQUZ'JOBMMZ#MPDL
    &NQUZ'PS#MPDL
    &NQUZ'VODUJPO#MPDL
    &NQUZ*G#MPDL
    &NQUZ*OJU#MPDL
    &NQUZ,U'JMF
    &NQUZ4FDPOEBSZ$POTUSVDUPS
    &NQUZ8IFO#MPDL
    &NQUZ8IJMF#MPDL
    &OVN/BNJOH
    &RVBMT8JUI)BTI$PEF&YJTU
    &YQMJDJU(BSCBHF$PMMFDUJPO$BMM
    'JMFOBNF
    'JOBM/FXMJOF
    'PSCJEEFO$PNNFOU
    'PS&BDI0O3BOHF
    'VODUJPO/BNJOH
    *NQPSU0SEFSJOH
    *OEFOUBUJPO
    -BSHF$MBTT
    -POH.FUIPE
    -POH1BSBNFUFS-JTU
    .BHJD/VNCFS
    .BUDIJOH%FDMBSBUJPO/BNF
    .BYJNVN-JOF-FOHUI
    .BY-JOF-FOHUI
    .PEJpFS0SEFS
    .PEJpFS0SEF
    /FTUFE#MPDL%
    /FX-JOF"U&O
    /P#MBOL-JOF#
    /P$POTFDVUJW
    /P&NQUZ$MBT
    /P*U1BSBN*O.
    /P-JOF#SFBL"
    /P-JOF#SFBL#
    /P.VMUJQMF4Q
    /P4FNJDPMPO
    /P5SBJMJOH4QB
    /P6OJU3FUVSO
    /P6OVTFE*N
    /P8JMEDBSE*N
    0CKFDU1SPQFS
    0QUJPOBM"CTUS
    1BDLBHF/BN
    1BSBNFUFS-JT
    EFGBVMUͰBDUJWFʹͳ͍ͬͯΔ΋ͷΛ
    ਺͑Δͱݸʂ

    View Slide

  79. © DMM.com
    ϧʔϧͷઃఆํ๏

    View Slide

  80. © DMM.com X
    ·ͣ͸DPOpHϑΝΠϧΛ࡞Δ
    HFOFSBUFίϚϯυΛ࣮ߦ͢Δͱɺ
    ɹɹɹɹɹɹɹɹɹɹɹEFGBVMUEFUFLUDPOpHZNM͕Ͱ͖Δ
    HSBEMFXEFUFLU(FOFSBUF$POpH

    View Slide

  81. © DMM.com X
    ·ͣ͸DPOpHϑΝΠϧΛ࡞Δ
    …লུ…
    style:
    active: true
    CollapsibleIfStatements:
    active: false
    DataClassContainsFunctions:
    active: false
    conversionFunctionPrefix: 'to'
    EqualsNullCall:
    active: false
    ExpressionBodySyntax:
    active: false
    includeLineWrapping: false
    ForbiddenComment:
    active: true
    values: 'TODO:,FIXME:,STOPSHIP:'
    ForbiddenImport:
    active: false
    imports: ''
    FunctionOnlyReturningConstant:
    active: false
    ignoreOverridableFunction: true
    excludedFunctions: 'describeContents'

    View Slide

  82. © DMM.com X
    ·ͣ͸DPOpHϑΝΠϧΛ࡞Δ
    …লུ…
    style:
    active: true
    CollapsibleIfStatements:
    active: false
    DataClassContainsFunctions:
    active: false
    conversionFunctionPrefix: 'to'
    EqualsNullCall:
    active: false
    ExpressionBodySyntax:
    active: false
    includeLineWrapping: false
    ForbiddenComment:
    active: true
    values: 'TODO:,FIXME:,STOPSHIP:'
    ForbiddenImport:
    active: false
    imports: ''
    FunctionOnlyReturningConstant:
    active: false
    ignoreOverridableFunction: true
    excludedFunctions: 'describeContents'
    3VMF4FU

    View Slide

  83. © DMM.com X
    ·ͣ͸DPOpHϑΝΠϧΛ࡞Δ
    …লུ…
    style:
    active: true
    CollapsibleIfStatements:
    active: false
    DataClassContainsFunctions:
    active: false
    conversionFunctionPrefix: 'to'
    EqualsNullCall:
    active: false
    ExpressionBodySyntax:
    active: false
    includeLineWrapping: false
    ForbiddenComment:
    active: true
    values: 'TODO:,FIXME:,STOPSHIP:'
    ForbiddenImport:
    active: false
    imports: ''
    FunctionOnlyReturningConstant:
    active: false
    ignoreOverridableFunction: true
    excludedFunctions: 'describeContents'
    3VMF

    View Slide

  84. © DMM.com X
    ·ͣ͸DPOpHϑΝΠϧΛ࡞Δ
    …লུ…
    style:
    active: true
    CollapsibleIfStatements:
    active: false
    DataClassContainsFunctions:
    active: false
    conversionFunctionPrefix: 'to'
    EqualsNullCall:
    active: false
    ExpressionBodySyntax:
    active: false
    includeLineWrapping: false
    ForbiddenComment:
    active: true
    values: 'TODO:,FIXME:,STOPSHIP:'
    ForbiddenImport:
    active: false
    imports: ''
    FunctionOnlyReturningConstant:
    active: false
    ignoreOverridableFunction: true
    excludedFunctions: 'describeContents'
    GBMTF
    ݸผʹ
    ઃఆͰ͖Δ

    View Slide

  85. © DMM.com
    ΧελϜϧʔϧͷ࡞Γํ

    View Slide

  86. © DMM.com X
    αϯϓϧͱͯ͠ɺ͜ͷϧʔϧΛͭ͘Δ
    fuga(1, 2)
    fuga(
    1,
    2
    )
    /(
    0,
    Ҿ਺ͰA A͕͋ͬͨΒվߦΛଅ͢

    View Slide

  87. © DMM.com X
    खॱ
    ϓϩδΣΫτ࡞੒
    3VMFΛ࡞Δ
    3VMF4FU1SPWJEFSΛ࡞Δ
    SFTPVSDFT.&5"*/'TFSWJDFT
    JPHJUMBCBSUVSCPTDIEFUFLUBQJ3VMF4FU1SPWJEFSʹ࡞ͬͨ
    3VMF4FU1SPWJEFSΛهड़
    KBSΛ࡞Δ

    View Slide

  88. © DMM.com X
    ϓϩδΣΫτ࡞੒
    buildscript {
    ext.kotlin_version = '1.2.60'
    repositories {
    mavenCentral()
    }
    dependencies {
    classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
    }
    }
    group 'detekt-custom-lint'
    version '1.0-SNAPSHOT'
    apply plugin: 'kotlin'
    repositories {
    mavenCentral()
    }
    dependencies {
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
    implementation "io.gitlab.arturbosch.detekt:detekt-api:1.0.0.RC8"
    testCompile "junit:junit:4.12"
    testCompile "org.assertj:assertj-core:3.10.0"
    testCompile "io.gitlab.arturbosch.detekt:detekt-api:1.0.0.RC8"
    testCompile "io.gitlab.arturbosch.detekt:detekt-test:1.0.0.RC8"
    }

    View Slide

  89. © DMM.com X
    ϓϩδΣΫτ࡞੒
    buildscript {
    ext.kotlin_version = '1.2.60'
    repositories {
    mavenCentral()
    }
    dependencies {
    classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
    }
    }
    group 'detekt-custom-lint'
    version '1.0-SNAPSHOT'
    apply plugin: 'kotlin'
    repositories {
    mavenCentral()
    }
    dependencies {
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
    implementation "io.gitlab.arturbosch.detekt:detekt-api:1.0.0.RC8"
    testCompile "junit:junit:4.12"
    testCompile "org.assertj:assertj-core:3.10.0"
    testCompile "io.gitlab.arturbosch.detekt:detekt-api:1.0.0.RC8"
    testCompile "io.gitlab.arturbosch.detekt:detekt-test:1.0.0.RC8"
    }
    LPUMJOͷϓϩδΣΫτΛ࡞ͬͯ
    EFUFLUBQJΛ௥Ճ

    View Slide

  90. © DMM.com X
    3VMFΛ࡞Δ
    ࡞Γ͍ͨ3VMFΛ࡞ͬͯߦ͘ɻ
    3VMFΛܧঝͯ͠ɺJTTVFΛ࡞੒ͯ͠ɺద੾ͳWJTJUϝιουΛ࣮
    ૷͢ΔɻʢLUMJOUͱҟͳΓෳ਺ͷWJTJUϝιου͕͋Δʣ

    View Slide

  91. © DMM.com X
    ·ͣ͸JTTVFΛ࡞Δ
    class LineBreakInArgsListDetektRule : Rule() {
    override val issue: Issue = Issue(
    id = "line-break-in-args-list",
    severity = Severity.Style,
    debt = Debt.FIVE_MINS,
    description = "should line break"
    )
    }
    *E ΧςΰϦɺमਖ਼͔͔Δ࣌ؒɺઆ໌ΛҾ਺ʹ*TTVFͷΠϯελ
    ϯεΛ࡞Δɻ

    View Slide

  92. © DMM.com X
    3VMFΛ࡞Δ
    LUMJOUͱҟͳΓෳ਺ͷWJTJUϝιου͕͋Δɻ
    'JMF
    1"$,"(&
    %*3&$5*7&
    $-"44
    FMFNFOU DMBTT
    8)*5&@41"$&
    FMFNFOU

    *%&/5*'*&3

    8)*5&@41"$& $-"44@#0%:
    WJTJU'JMF
    WJTJU1BDLBHF%JSFDUJWF
    WJTJU$MBTT
    WJTJU$MBTT#PEZ

    View Slide

  93. © DMM.com X
    3VMFΛ࡞Δ
    ͜ΕΛ֬ೝͯ͠ߦ͘ɻ
    7"-6&
    "3(6.&/5-*45
    7"-6&
    "3(6.&/5
    $0.." 8)*5&41"$&
    7"-6&
    "3(6.&/5
    w $0.."ͷ࣍ͷ8)*5&@41"$&ʹվߦίʔυΛؚ·ΕΔ͜ͱ
    w Լهͷߏ଄ʹͳ͍ͬͯΔ࣌ʹ

    View Slide

  94. © DMM.com X
    3VMFΛ࡞Δ
    7"-6&
    "3(6.&/5-*45
    7"-6&
    "3(6.&/5
    $0.." 8)*5&41"$&
    7"-6&
    "3(6.&/5

    View Slide

  95. © DMM.com X
    3VMFΛ࡞Δ
    7"-6&
    "3(6.&/5-*45
    7"-6&
    "3(6.&/5
    $0.." 8)*5&41"$&
    7"-6&
    "3(6.&/5
    WJTJU7BMVF"SHVNFOU-JTU
    WJTJU7BMVF"SHVNFOU-JTUΛ࣮૷͢Δ

    View Slide

  96. © DMM.com X
    3VMFΛ࡞Δ
    7"-6&
    "3(6.&/5-*45
    7"-6&
    "3(6.&/5
    $0.." 8)*5&41"$&
    7"-6&
    "3(6.&/5
    WJTJU7BMVF"SHVNFOU-JTU
    $0.."ͷ࣍ͷ
    WJTJU7BMVF"SHVNFOU-JTUΛ࣮૷͢Δ

    View Slide

  97. © DMM.com X
    3VMFΛ࡞Δ
    7"-6&
    "3(6.&/5-*45
    7"-6&
    "3(6.&/5
    $0.." 8)*5&41"$&
    7"-6&
    "3(6.&/5
    WJTJU7BMVF"SHVNFOU-JTU
    $0.."ͷ࣍ͷ ۭന಺Ͱ
    վߦ͞ΕͯΔ͔ʁʁʁ
    WJTJU7BMVF"SHVNFOU-JTUΛ࣮૷͢Δ

    View Slide

  98. © DMM.com X
    3VMFΛ࡞Δ
    class LineBreakInArgsListDetektRule : Rule() {
    override val issue: Issue = Issue(
    id = "line-break-in-args-list",
    severity = Severity.Style,
    debt = Debt.FIVE_MINS,
    description = "should line break"
    )
    override fun visitValueArgumentList(list: KtValueArgumentList) {
    super.visitValueArgumentList(list)
    list.children.forEach {
    if (it.node.elementType == KtTokens.COMMA &&
    it.nextSibling is PsiWhiteSpace &&
    !it.nextSibling.text.contains("\n")
    ) {
    report(
    CodeSmell(
    issue,
    Entity.from(list),
    "should line break"
    )
    )
    return
    }
    }
    }
    }
    վߦ͕ͳ͍৔߹͸ɺSFQPSUϝιουΛݺͿɻ

    View Slide

  99. © DMM.com X
    3VMFΛ࡞Δ
    class LineBreakInArgsListDetektRule : Rule() {
    override val issue: Issue = Issue(
    id = "line-break-in-args-list",
    severity = Severity.Style,
    debt = Debt.FIVE_MINS,
    description = "should line break"
    )
    override fun visitValueArgumentList(list: KtValueArgumentList) {
    super.visitValueArgumentList(list)
    list.children.forEach {
    if (it.node.elementType == KtTokens.COMMA &&
    it.nextSibling is PsiWhiteSpace &&
    !it.nextSibling.text.contains("\n")
    ) {
    report(
    CodeSmell(
    issue,
    Entity.from(list),
    "should line break"
    )
    )
    return
    }
    }
    }
    }
    վߦ͕ͳ͍৔߹͸ɺSFQPSUϝιουΛݺͿɻ
    $0.."ͷ
    ࣍ͷ8)*5&@41"$&͕վߦΛ
    ؚ·ͳ͍৔߹

    View Slide

  100. © DMM.com X
    3VMFΛ࡞Δ
    class LineBreakInArgsListDetektRule : Rule() {
    override val issue: Issue = Issue(
    id = "line-break-in-args-list",
    severity = Severity.Style,
    debt = Debt.FIVE_MINS,
    description = "should line break"
    )
    override fun visitValueArgumentList(list: KtValueArgumentList) {
    super.visitValueArgumentList(list)
    list.children.forEach {
    if (it.node.elementType == KtTokens.COMMA &&
    it.nextSibling is PsiWhiteSpace &&
    !it.nextSibling.text.contains("\n")
    ) {
    report(
    CodeSmell(
    issue,
    Entity.from(list),
    "should line break"
    )
    )
    return
    }
    }
    }
    }
    վߦ͕ͳ͍৔߹͸ɺSFQPSUϝιουΛݺͿɻ
    SFQPSU͢Δ

    View Slide

  101. © DMM.com X
    3VMF4FU1SPWJEFSΛ࡞Δ
    3VMF4FU1SPWJEFSΛܧঝ͠ɺJOTUBODFϝιουΛ࣮૷͢Δɻ
    ࡞ͬͨ3VMFΛొ࿥͢Δɻ
    class CustomRuleSetProvider : RuleSetProvider {
    override val ruleSetId: String = "detekt-custom-rules"
    override fun instance(config: Config): RuleSet = RuleSet(
    ruleSetId,
    listOf(LineBreakInArgsListDetektRule())
    )
    }

    View Slide

  102. © DMM.com X
    DPNHJUIVCTIZJLPLUMJOUDPSF3VMF4FU1SPWJEFS
    SFTPVSDFT.&5"*/'TFSWJDFT
    JPHJUMBCBSUVSCPTDIEFUFLUBQJ3VMF4FU1SPWJEFSʹ࡞ͬͨ
    3VMF4FU1SPWJEFSΛهड़
    com.kgmyshin.ktlint.CustomRuleSetProvider

    View Slide

  103. © DMM.com X
    KBSΛ࡞Δ
    KBSΛ࡞੒͢Δɻ
    ./gradlew jar
    CVJMEMJCT഑Լʹ࡞੒͞ΕΔɻ

    View Slide

  104. © DMM.com
    ࡞ͬͨΧελϜϧʔϧΛద༻͢Δ

    View Slide

  105. © DMM.com X
    ࡞ͬͨΧελϜϧʔϧΛద༻͢Δ
    dependencies {

    detekt files("detekt-custom-lint.jar")
    }
    EFQFOEFODJFTʹEFUFLUDPOpHVSBUJPOʹKBSΛ௥Ճ͢Δ
    $ ./gradlew detektCheck
    :
    Ruleset: detekt-custom-rules - 5min debt
    line-break-in-args-list - [A] at ...kt:42:13
    ͦΕ͚ͩͰ0,ɻ

    View Slide

  106. © DMM.com
    ͦͷଞGPSNBUΛ͔͚Δ

    View Slide

  107. © DMM.com X
    BVUP$PSSFDU1SPQFSUZ
    l5IJTPQUJPOJTTUJMMQSFTFOUEVFUPMFHBDZ
    SFBTPOT*OUIFpSTUNJMFTUPOFSFMFBTFTEFUFLU
    BMTPGPSNBUUFELPUMJODPEF8JUIJOUIFEFUFLU
    UFBNXFEFDJEFEUPOPUNFTTXJUIVTFSDPEFBOE
    MFUPUIFSUPPMTEPUIFGPSNBUUJOHFHJOUFMMJKPS
    ,U-JOU4UJMMBTEFUFLUDBOCFFYUFOEFEXJUI
    DVTUPNSVMFT ZPVBSFGSFFUPXSJUFSVMFTXIJDI
    TVQQPSUBVUPDPSSFDUJPO0OMZXSJUFDPSSFDUJOH
    DPEFXJUIJOUIFXJUI"VUP$PSSFDU
    GVODUJPOz
    IUUQTHJUIVCDPNBSUVSCPTDIEFUFLUCMPCNBTUFSEPDTQBHFTDPOpHVSBUJPOTNEBVUPDPSSFDUQSPQFSUZ

    View Slide

  108. © DMM.com X
    BVUP$PSSFDU1SPQFSUZ
    w ࠷ॳͷϚΠϧετʔϯͰ͸ϑΥʔϚοτػೳ͸͋ͬͨ
    w EFUFLUνʔϜ͸ɺࠞཚΛ͚͞ΔͨΊϑΥʔϚοτػೳ
    Λͳͨ͘͠
    w ͨͩ͠ɺΧελϜϧʔϧͰϑΥʔϚοτΛ͢Δ͜ͱ͕
    Ͱ͖Δɻ
    w XJUI"VUP$PSSFDUϝιου಺ʹ࣮૷͍ͯͩ͘͠͞

    View Slide

  109. © DMM.com X
    ΧελϜϧʔϧͰXJUI"VUP$PSSFDUΛ࢖͏
    override fun visitValueArgumentList(list: KtValueArgumentList) {
    super.visitValueArgumentList(list)
    list.children.forEach {
    if (it.nextSibling.node.elementType == KtTokens.COMMA &&
    it.nextSibling.nextSibling is PsiWhiteSpace &&
    !it.nextSibling.nextSibling.text.contains("\n")
    ) {
    report(
    CodeSmell(
    issue,
    Entity.from(list),
    "should line break"
    )
    )
    withAutoCorrect {
    format(list)
    }
    return
    }
    }
    }
    }
    DPOJGHͰABVUP$PSSFDUUVSFAͱͳ͍ͬͯΔ͚࣌ͩ
    ϑΥʔϚοτ͞ΕΔ

    View Slide

  110. © DMM.com X
    ΧελϜϧʔϧͰXJUI"VUP$PSSFDUΛ࢖͏
    override fun visitValueArgumentList(list: KtValueArgumentList) {
    super.visitValueArgumentList(list)
    list.children.forEach {
    if (it.nextSibling.node.elementType == KtTokens.COMMA &&
    it.nextSibling.nextSibling is PsiWhiteSpace &&
    !it.nextSibling.nextSibling.text.contains("\n")
    ) {
    report(
    CodeSmell(
    issue,
    Entity.from(list),
    "should line break"
    )
    )
    withAutoCorrect {
    format(list)
    }
    return
    }
    }
    }
    }
    DPOJGHͰABVUP$PSSFDUUVSFAͱͳ͍ͬͯΔ͚࣌ͩ
    ϑΥʔϚοτ͞ΕΔ

    View Slide

  111. © DMM.com
    ͪͳΈʹ

    View Slide

  112. © DMM.com X
    EFUFDUGPSNBUUJOH
    EFUFLUGPSNBUUJOHΛCVJMEHSBEMF಺ͰEFUFLUDPOpHVSBUJPOʹࢦ
    ఆ͢Δ͜ͱͰ,UMJOUͷඪ४ϧʔϧΛ௥ՃͰ͖Δ
    dependencies {
    detekt “io.gitlab.arturbosch.detekt:detekt-formatting:1.0.0.RC8"
    }
    ˞GPSNBUͨ͠Γͱ͔͸ಛʹؔ܎ͳ͍Ͱ͢

    View Slide

  113. © DMM.com X
    BOESPJEMJOU

    View Slide

  114. © DMM.com
    ࢖͍ํ

    View Slide

  115. © DMM.com X
    ࢖͍ํ
    HSBEMFXMJOU
    ʜ
    3BOMJOUPOWBSJBOUEFCVHJTTVFTGPVOE
    3BOMJOUPOWBSJBOUSFMFBTFJTTVFTGPVOE
    8SPUF)5.-SFQPSUUPpMFCVJMESFQPSUTMJOUSFTVMUTIUNM
    8SPUF9.-SFQPSUUPpMFCVJMESFQPSUTMJOUSFTVMUTYNM
    ʜ
    BOESPJEϓϩδΣΫτͰMJOUλεΫΛ࣮ߦ͢Δɻ

    View Slide

  116. © DMM.com X
    ࢖͍ํ
    BOESPJEϓϩδΣΫτͰMJOUλεΫΛ࣮ߦ͢Δɻ

    View Slide

  117. © DMM.com
    ඪ४Ͱ༻ҙ͞Ε͍ͯΔϧʔϧ

    View Slide

  118. © DMM.com X
    ඪ४Ͱ༻ҙ͞Ε͍ͯΔϧʔϧ͸͍ͬͺ͍͋Δ
    "DDFTTJCJMJUZ%FUFDUPS
    "EE+BWBTDSJQU*OUFSGBDF%FUFDUPS
    "MBSN%FUFDUPS
    "MXBZT4IPX"DUJPO%FUFDUPS
    "OOPUBUJPO%FUFDUPS
    "QJ%FUFDUPS
    "QQ$PNQBU$BMM%FUFDUPS
    "QQ$PNQBU3FTPVSDF%FUFDUPS
    "QQ*OEFYJOH"QJ%FUFDUPS
    "SSBZ4J[F%FUFDUPS
    "TTFSU%FUFDUPS
    #VJMUJO*TTVF3FHJTUSZ
    #VUUPO%FUFDUPS
    #ZUF0SEFS.BSL%FUFDUPS
    $BMM4VQFS%FUFDUPS
    $IJME$PVOU%FUFDUPS
    $JQIFS(FU*OTUBODF%FUFDUPS
    $MFBOVQ%FUFDUPS
    $MJDLBCMF7JFX"DDFTTJCJMJUZ%FUFDUPS
    $PNNFOU%FUFDUPS
    $POUSPM'MPX(SBQI
    $VTUPN7JFX%FUFDUPS
    $VU1BTUF%FUFDUPS
    %BUF'PSNBU%FUFDUPS
    %FQSFDBUJPO%FUFDUPS
    %FUFDU.JTTJOH1SFpY
    %PT-JOF&OEJOH%FUFDUPS
    %VQMJDBUF*E%FUFDUPS
    %VQMJDBUF3FTPVSDF%FUFDUPS
    &YUSB5FYU%FUFDUPS
    'JFME(FUUFS%FUFDUPS
    'SBHNFOU%FUFDUPS
    'VMM#BDLVQ$POUFOU%FUFDUPS
    (FU4JHOBUVSFT%FUFDUPS
    (SBEMF%FUFDUPS
    (SJE-BZPVU%FUFDUPS
    )BOEMFS%FUFDUPS
    )BSEDPEFE%FCVH.PEF%FUFDUPS
    )BSEDPEFE7B
    *DPO%FUFDUPS
    *ODMVEF%FUFD
    *OF⒏DJFOU8F
    *OWBMJE1BDLBH
    +BWB1FSGPSNB
    +BWB4DSJQU*OUF
    -BCFM'PS%FUF
    -BZPVU$POTJT
    -BZPVU*OqBUJP
    -PDBMF%FUFDU
    -PDBMF'PMEFS%
    -PH%FUFDUPS
    .BOJGFTU%FUF
    .BOJGFTU5ZQP
    .BUI%FUFDUP
    .FSHF3PPU'SB
    .JTTJOH$MBTT
    .JTTJOH*E%FU

    View Slide

  119. © DMM.com X
    ඪ४Ͱ༻ҙ͞Ε͍ͯΔϧʔϧ͸͍ͬͺ͍͋Δ
    "DDFTTJCJMJUZ%FUFDUPS
    "EE+BWBTDSJQU*OUFSGBDF%FUFDUPS
    "MBSN%FUFDUPS
    "MXBZT4IPX"DUJPO%FUFDUPS
    "OOPUBUJPO%FUFDUPS
    "QJ%FUFDUPS
    "QQ$PNQBU$BMM%FUFDUPS
    "QQ$PNQBU3FTPVSDF%FUFDUPS
    "QQ*OEFYJOH"QJ%FUFDUPS
    "SSBZ4J[F%FUFDUPS
    "TTFSU%FUFDUPS
    #VJMUJO*TTVF3FHJTUSZ
    #VUUPO%FUFDUPS
    #ZUF0SEFS.BSL%FUFDUPS
    $BMM4VQFS%FUFDUPS
    $IJME$PVOU%FUFDUPS
    $JQIFS(FU*OTUBODF%FUFDUPS
    $MFBOVQ%FUFDUPS
    $MJDLBCMF7JFX"DDFTTJCJMJUZ%FUFDUPS
    $PNNFOU%FUFDUPS
    $POUSPM'MPX(SBQI
    $VTUPN7JFX%FUFDUPS
    $VU1BTUF%FUFDUPS
    %BUF'PSNBU%FUFDUPS
    %FQSFDBUJPO%FUFDUPS
    %FUFDU.JTTJOH1SFpY
    %PT-JOF&OEJOH%FUFDUPS
    %VQMJDBUF*E%FUFDUPS
    %VQMJDBUF3FTPVSDF%FUFDUPS
    &YUSB5FYU%FUFDUPS
    'JFME(FUUFS%FUFDUPS
    'SBHNFOU%FUFDUPS
    'VMM#BDLVQ$POUFOU%FUFDUPS
    (FU4JHOBUVSFT%FUFDUPS
    (SBEMF%FUFDUPS
    (SJE-BZPVU%FUFDUPS
    )BOEMFS%FUFDUPS
    )BSEDPEFE%FCVH.PEF%FUFDUPS
    )BSEDPEFE7B
    *DPO%FUFDUPS
    *ODMVEF%FUFD
    *OF⒏DJFOU8F
    *OWBMJE1BDLBH
    +BWB1FSGPSNB
    +BWB4DSJQU*OUF
    -BCFM'PS%FUF
    -BZPVU$POTJT
    -BZPVU*OqBUJP
    -PDBMF%FUFDU
    -PDBMF'PMEFS%
    -PH%FUFDUPS
    .BOJGFTU%FUF
    .BOJGFTU5ZQP
    .BUI%FUFDUP
    .FSHF3PPU'SB
    .JTTJOH$MBTT
    .JTTJOH*E%FU
    YNM KBWB LPUMJOͱ͍ΖΜͳSVMF͕
    ࠞࡏ͍ͯ͠Δ

    View Slide

  120. © DMM.com
    ϧʔϧͷઃఆํ๏

    View Slide

  121. © DMM.com X
    ઃఆํ๏ͷҰྫ





    id="GoogleAppIndexingWarning"
    severity="ignore"
    />

    DPOpHϑΝΠϧΛ࡞੒ͯ͠ɺ֤3VMFʢ͜͜Ͱ͸JTTVFʣ
    ͝ͱʹઃఆ͢Δɻ

    View Slide

  122. © DMM.com X
    ·ͣ͸DPOpHϑΝΠϧΛ࡞Δ
    android {
    ...
    lintOptions {
    abortOnError false
    lintConfig file("path/to/lint/config.xml")
    }
    }
    DPOpHϑΝΠϧΛMJOU$POpHʹઃఆ͢Ε͹0,ɻ

    View Slide

  123. © DMM.com
    ΧελϜϧʔϧͷ࡞Γํ

    View Slide

  124. © DMM.com X
    ͜͏͍͏ͷΛ࡞ͬͯݟ·͢
    3Y+BWBΛ࢖ͬͯͯɺ
    %JTQPTBCMFΛద੾ʹॲཧͯ͠ͳ͍ͱܯࠂग़͢ΧελϜϧʔϧ

    View Slide

  125. © DMM.com X
    खॱ
    ϓϩδΣΫτ࡞੒
    %FUFDUPSΛ࡞Δ
    *TTVFΛ࡞Δ
    3FHJTUSZΛ࡞Δ
    3FHJTUSZΛCVJMEHSBEMFʹهड़
    KBSΛ࡞Δ

    View Slide

  126. © DMM.com X
    ̍ϓϩδΣΫτΛ࡞Δ
    HPPHMFTBNQMFTʹςϯϓϨʔτͷΑ͏ͳ΋ͷ͕͋Δͷ
    ͰɺͦΕʹͳΒ͏ͷ͕ૣ͍
    IUUQTHJUIVCDPNHPPHMFTBNQMFTBOESPJEDVTUPN
    MJOUSVMFTUSFFNBTUFSBOESPJETUVEJP

    View Slide

  127. © DMM.com X
    ̎%FUFDUPSΛ࡞Δ
    public class NotHandledDisposableDetector extends Detector implements UastScanner {
    @Override
    public List> getApplicableUastTypes() {
    return Collections.singletonList(UQualifiedReferenceExpression.class);
    }
    @Override
    public UElementHandler createUastHandler(JavaContext context) {
    return new UElementHandler() {
    @Override
    public void visitQualifiedReferenceExpression(
    ɹɹɹɹɹɹɹ UQualifiedReferenceExpression node
    ɹɹɹɹɹɹɹ) {
    : ॲཧ
    }
    };
    }
    }
    ׬੒ܗ

    View Slide

  128. © DMM.com X
    ̎%FUFDUPSΛ࡞Δ
    public class NotHandledDisposableDetector extends Detector implements UastScanner {
    @Override
    public List> getApplicableUastTypes() {
    return Collections.singletonList(UQualifiedReferenceExpression.class);
    }
    @Override
    public UElementHandler createUastHandler(JavaContext context) {
    return new UElementHandler() {
    @Override
    public void visitQualifiedReferenceExpression(
    ɹɹɹɹɹɹɹ UQualifiedReferenceExpression node
    ɹɹɹɹɹɹɹ) {
    : ॲཧ
    }
    };
    }
    }
    %FUFDUPSΛFYUFOETɺ6BTU4DBOOFSΛJNQMFNFOUTɻ
    6BTU͸6OJpFE"45ͷུɻ

    View Slide

  129. © DMM.com X
    ̎%FUFDUPSΛ࡞Δ
    public class NotHandledDisposableDetector extends Detector implements UastScanner {
    @Override
    public List> getApplicableUastTypes() {
    return Collections.singletonList(UQualifiedReferenceExpression.class);
    }
    @Override
    public UElementHandler createUastHandler(JavaContext context) {
    return new UElementHandler() {
    @Override
    public void visitQualifiedReferenceExpression(
    ɹɹɹɹɹɹɹ UQualifiedReferenceExpression node
    ɹɹɹɹɹɹɹ) {
    : ॲཧ
    }
    };
    }
    }
    DSFBUF6BTU)BOEMFSΛPWFSSJEF͢Δɻ
    ͭ·Γ6&MFNFOU)BOEMFSΛ࣮૷͢Δɻ

    View Slide

  130. © DMM.com X
    6*&MFNFOU)BOEMFSͱ͸
    USFFͷ্͔ΒॱʹTDBO͍ͯͬͯ͠ɺ֤छOPEF͕ݟ͔ͭΔͨ
    ͼʹWJTJUϝιου͕ݺ͹ΕΔਓɻ
    ֤छWJTJUϝιουΛPWFSSJEFͯ͠࢖͏ɻ
    7"-6&
    "3(6.&/5-*45
    7"-6&
    "3(6.&/5
    $0.." 8)*5&41"$&
    7"-6&
    "3(6.&/5
    WJTJU"SHVNFOU-JTU
    WJTJU"SHVNFOU
    WJTJU"SHVNFOU

    View Slide

  131. © DMM.com X
    ࠓճ͸
    ࠓճ͸1TJ7JXFSΛݟΔݶΓɺ%05@26"-*'*&%@&913&44*0/
    ͕དྷͨ࣌ʹ֬ೝ͢Δ͚ͩͰࡁΈͦ͏ͳͷͰɺ
    WJTJU2VBMJpFE3FGFSFODF&YQSFTTJPOͷΈΛPWFSJSEF͢Δɻ

    View Slide

  132. © DMM.com X
    ̎%FUFDUPSΛ࡞Δ
    public class NotHandledDisposableDetector extends Detector implements UastScanner {
    @Override
    public List> getApplicableUastTypes() {
    return Collections.singletonList(UQualifiedReferenceExpression.class);
    }
    @Override
    public UElementHandler createUastHandler(JavaContext context) {
    return new UElementHandler() {
    @Override
    public void visitQualifiedReferenceExpression(
    ɹɹɹɹɹɹɹ UQualifiedReferenceExpression node
    ɹɹɹɹɹɹɹ) {
    : ॲཧ
    }
    };
    }
    }
    WJTJUର৅ͷOPEFͷΫϥεҰཡΛHFU"QQMJDBCMF6BTU5ZQFTͰ
    ฦ٫͢Δɻ͜ΕΛ͠ͳ͍ͱWJTJUϝιου͕ݺ͹Ε·ͤΜ

    View Slide

  133. © DMM.com X
    ̎%FUFDUPSΛ࡞Δ
    // kotlin
    if (node.getPsi() != null &&
    node.getPsi().getContext() != null &&
    node.getPsi().getContext().toString().equals("BLOCK") &&
    node.getSelector().asRenderString().startsWith("subscribe(") &&
    node.getSelector().getExpressionType() != null &&
    node.getSelector().getExpressionType()
    .getCanonicalText()
    .equals("io.reactivex.disposables.Disposable")) {
    context.report(
    ISSUE,
    node,
    context.getLocation(node),
    "Should handle Disposable”
    );
    return;
    }
    WJTJU2VBMJpFE3FGFSFODF&YQSFTTJPOͷத਎Λઆ໌͠·͢ɻ

    View Slide

  134. © DMM.com X
    ̎%FUFDUPSΛ࡞Δ
    // kotlin
    if (node.getPsi() != null &&
    node.getPsi().getContext() != null &&
    node.getPsi().getContext().toString().equals("BLOCK") &&
    node.getSelector().asRenderString().startsWith("subscribe(") &&
    node.getSelector().getExpressionType() != null &&
    node.getSelector().getExpressionType()
    .getCanonicalText()
    .equals("io.reactivex.disposables.Disposable")) {
    context.report(
    ISSUE,
    node,
    context.getLocation(node),
    "Should handle Disposable”
    );
    return;
    }
    ਌͕#MPDLA\^AͰɺ

    View Slide

  135. © DMM.com X
    ̎%FUFDUPSΛ࡞Δ
    // kotlin
    if (node.getPsi() != null &&
    node.getPsi().getContext() != null &&
    node.getPsi().getContext().toString().equals("BLOCK") &&
    node.getSelector().asRenderString().startsWith("subscribe(") &&
    node.getSelector().getExpressionType() != null &&
    node.getSelector().getExpressionType()
    .getCanonicalText()
    .equals("io.reactivex.disposables.Disposable")) {
    context.report(
    ISSUE,
    node,
    context.getLocation(node),
    "Should handle Disposable”
    );
    return;
    }
    ϝιουνΣʔϯͷ࠷ޙ͕ATVCTDSJCF AͰ

    View Slide

  136. © DMM.com X
    ̎%FUFDUPSΛ࡞Δ
    // kotlin
    if (node.getPsi() != null &&
    node.getPsi().getContext() != null &&
    node.getPsi().getContext().toString().equals("BLOCK") &&
    node.getSelector().asRenderString().startsWith("subscribe(") &&
    node.getSelector().getExpressionType() != null &&
    node.getSelector().getExpressionType()
    .getCanonicalText()
    .equals("io.reactivex.disposables.Disposable")) {
    context.report(
    ISSUE,
    node,
    context.getLocation(node),
    "Should handle Disposable”
    );
    return;
    }
    ͦͷTVCTDJSCFϝιουͷฦΓ஋͕JPSFBDUJWFYEJTQPTBCMFT%JTQPTBCMFͷ࣌
    ‐͜Ε͕LUMJOUͱEFUFLUͰ͸ࠓͷॴͰ͖·ͤΜ

    View Slide

  137. © DMM.com X
    ̎%FUFDUPSΛ࡞Δ
    // kotlin
    if (node.getPsi() != null &&
    node.getPsi().getContext() != null &&
    node.getPsi().getContext().toString().equals("BLOCK") &&
    node.getSelector().asRenderString().startsWith("subscribe(") &&
    node.getSelector().getExpressionType() != null &&
    node.getSelector().getExpressionType()
    .getCanonicalText()
    .equals("io.reactivex.disposables.Disposable")) {
    context.report(
    ISSUE,
    node,
    context.getLocation(node),
    "Should handle Disposable”
    );
    return;
    }
    Ϩϙʔτ͢Δʂ ܯࠂ͕ग़Δ

    View Slide

  138. © DMM.com X
    ̎%FUFDUPSΛ࡞Δ
    // java
    if (node.getPsi() != null &&
    node.getPsi().getContext() != null &&
    node.getPsi().getContext().getContext() != null &&
    node.getPsi().getContext().getContext().toString()
    .equals(“PsiCodeBlock") &&
    node.getSelector().asRenderString().startsWith("subscribe(") &&
    node.getSelector().getExpressionType() != null &&
    node.getSelector().getExpressionType().getCanonicalText()
    .equals("io.reactivex.disposables.Disposable")) {
    context.report(
    ISSUE,
    node,
    context.getLocation(node),
    "Should handle Disposable”
    );
    }
    KBWBͷ৔߹΋ॻ͖·͢ɻ6BTU4DBOOFS͸LPUMJOͱKBWBͰ۠ผͳ͘WJTJUͯ͘͠Ε·͢ɻ
    ͨͩ͠ɺLPUMJOͷ࣌ͱߏ଄͸େମಉ͡Ͱ͕͢ɺ14*ͷܕ͕ҧ͏ͷͰผ్ॻ͘ඞཁ͕͋Γ·͢ɻ

    View Slide

  139. © DMM.com X
    ̏*TTVFΛ࡞Δ
    public class NotHandledDisposableDetector extends Detector implements
    UastScanner {
    public static final Issue ISSUE = Issue.create(
    "NotHandledDisposable",
    "Not Handled Disposable",
    "Disposable should be called dispose.",
    Category.CORRECTNESS,
    6,
    Severity.ERROR,
    new Implementation(
    NotHandledDisposableDetector.class,
    Scope.JAVA_FILE_SCOPE
    )
    );
    *%΍આ໌ɺΧςΰϦ৘ใΛ࣋ͬͨTUBUJDͳ*TTVFΠϯελϯεΛ࡞Γ·͢ɻ
    ৔ॴ͸Ͳ͜Ͱ΋͍͍͚Ͳɺطଘͷ-JOUΛݟΔݶΓ%FUFDUPSΫϥεʹॻ͍ͯΔͱ͜Ζ͕΄ͱΜͲ
    Ͱ͢ɻ

    View Slide

  140. © DMM.com X
    ̐3FHJTUSZΛ࡞Δ
    public class CustomIssueRegistry extends IssueRegistry {
    @Override
    public List getIssues() {
    return Collections.singletonList(
    NotHandledDisposableDetector.ISSUE
    );
    }
    }
    ࡞ͬͨ*TTVFΛొ࿥͢Δ3FHJTUPSZΫϥεΛ࡞Γ·͢ɻ
    *TTVF͸ෳ਺ొ࿥Ͱ͖·͢ɻ

    View Slide

  141. © DMM.com X
    ̑3FHJTUSZΛCVJMEHSBEMFʹهड़
    jar {
    manifest {
    attributes("Lint-Registry-v2": "com.kgmyshin.lint.CustomIssueRegistry")
    }
    }
    A-JOU3FHJTUSZ7Aʹઌ΄Ͳ࡞ͬͨ3FHJTUSZΛઃఆ͠·͢ɻ
    A7AΛ๨Εͳ͍Α͏ʹɻ

    View Slide

  142. © DMM.com X
    KBSΛ࡞Δ
    KBSΛ࡞੒͢Δɻ
    ./gradlew jar
    CVJMEMJCT഑Լʹ࡞੒͞ΕΔɻ

    View Slide

  143. © DMM.com
    ࡞ͬͨΧελϜϧʔϧΛద༻͢Δ

    View Slide

  144. © DMM.com X
    ࡞ͬͨ$VTUPN-JOUͷ࢖͍ํ
    dependencies {
    implementation 'io.reactivex.rxjava2:rxjava:2.1.10'
    lintChecks files("lint/custom-lint.jar")
    }
    AMJOU$IFDLTpMFT KBSͷύε
    A
    Λ௥ՃͰ0,

    View Slide

  145. © DMM.com X
    ࡞ͬͨ$VTUPN-JOUͷ࢖͍ํ
    dependencies {
    implementation 'io.reactivex.rxjava2:rxjava:2.1.10'
    lintChecks project(":checks")
    }
    ಉҰϓϩδΣΫτʹ͋Δ৔߹͸ɺԼهͷΑ͏ʹEFQFOEFODJFTʹ
    MJOU$IFDLTQSPKFDU ΧελϜϧʔϧͷϞδϡʔϧ

    Λ௥Ճ

    View Slide

  146. © DMM.com X
    ࡞ͬͨ$VTUPN-JOUͷ࢖͍ํ
    ./gradlew lint
    :
    Errors found:
    /../custom-lint-rules/library/src/main/java/test/pkg/MainJava.java:8:
    Error: Should handle Disposable [NotHandledDisposable]
    Single.just("test").subscribe();
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    /../custom-lint-rules/library/src/main/java/test/pkg/MainKt.kt:8: Error:
    Should handle Disposable [NotHandledDisposable]
    Single.just("aa").subscribe()
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    :
    ແࣄಈ͖·ͨ͠ʂ

    View Slide

  147. © DMM.com X
    ·ͱΊ

    View Slide

  148. © DMM.com X
    ൺֱද
    ඪ४ϧʔϧ ݸผઃఆ $VTUPN-JOU ͦͷଞ
    LUMJOU ݸऑ΄Ͳ جຊͰ͖ͳ͍ ࡞ΕΔ GPSNBUͰ͖Δ
    EFUFLU ͨ͘͞Μ͋Δ Ͱ͖Δ ࡞ΕΔ
    ෳࡶ౓ͳͲ΋
    Θ͔Δ
    BOESPJEMJOU ͨ͘͞Μ͋Δ Ͱ͖Δ ࡞ΕΔ ܕΛݟΕΔ

    View Slide

  149. © DMM.com X
    ·ͱΊ
    w $VTUPN-JOUͷ࡞Γํ͸ͲΕ΋͍͍ͩͨڞ௨
    w LUMJOU͸جຊతʹϧʔϧͷݸผઃఆ͸ͮ͠Β͍
    w LUMJOU͸GPSNBU΋Ͱ͖Δ
    w EFUFLU͸ෳࡶ౓ͳͲ΋Ϩϙʔτ͞ΕΔ
    w EFUFLU͸GPSNBUػೳ͸΍Ίͨ
    w BOESPJEMJOU͸KBWBͱLPUMJOΛࠩผ͠ͳ͍VBTUΛ࢖ͬ
    ͍ͯΔ
    w BOESPJEMJOU͸ܕΛ֬ೝͰ͖Δ

    View Slide