how to make custom lint at shibuya apk 24

how to make custom lint at shibuya apk 24

android向けのcustom lintの作り方の資料です
shibuya.apk#24

3c4e24fd827c789cb67a9f759f057b06?s=128

Shinnosuke Kugimiya

April 13, 2018
Tweet

Transcript

  1. © DMM.com !LHNZTIJO TIJCVZBBQL LPUMJOͰ΋KBWBͰ΋ ݕग़Ͱ͖Δ $VTUPN-JOUͷ࡞Γํ

  2. © DMM.com X w LHNZTIJOఝٶ ͗͘Έ΍  w "OESPJEΤϯδχΞ w

    %..DPNϥϘ$50ࣨॴଐ w ೥݄͔Β ࣗݾ঺հ
  3. © DMM.com X BHFOEB

  4. © DMM.com X w LPUMJOΛ࢖ͬͨ"OESPJEϓϩδΣΫτͰMJOUΛ͔͚Δ ʹ͸ʁ w $VTUPN-JOUΛ࡞Δʹ͸ʁ w BOESPJEMJOUͰ$VTUPN-JOUΛ࡞ͬͯΈΔ

    w ࡞ͬͨBOESPJEMJOUͷ$VTUPN-JOUΛςετ͢Δ w ࡞ͬͨBOESPJEMJOUͷ$VTUPN-JOUΛ࢖͏ w ·ͱΊ BHFOEB
  5. © DMM.com X LPUMJOΛ࢖ͬͨ "OESPJEϓϩδΣΫτͰ MJOUΛ͔͚Δʹ͸ʁ

  6. © DMM.com X LPUMJOΛ࢖ͬͨ"OESPJEϓϩδΣΫτͰMJOUΛ͔͚Δʹ͸ʁ Ҏલ͸ ओʹ ͭͷํ๏͔͠ͳ͔ͬͨ w LUMJOU w

    IUUQTHJUIVCDPNTIZJLPLUMJOU w EFUFLU w IUUQTHJUIVCDPNBSUVSCPTDIEFUFLU
  7. © DMM.com X LPUMJOΛ࢖ͬͨ"OESPJEϓϩδΣΫτͰMJOUΛ͔͚Δʹ͸ʁ ͓ͦΒ͘ BOESPJEHSBMEFQMVHJO͔Β w LUMJOU w IUUQTHJUIVCDPNTIZJLPLUMJOU

    w EFUFLU w IUUQTHJUIVCDPNBSUVSCPTDIEFUFLU w BOESPJEMJOU ศٓͷͨΊɺBOESPJEMJOUͱݺͼ·͢  ‏/FX w IUUQTHJUIVCDPNHPPHMFTBNQMFTBOESPJE DVTUPNMJOUSVMFT αϯϓϧ  
  8. © DMM.com X w ެ͕ࣜ࡞ͬͯΔ w AHSBEMFXMJOUAͰ૸Δ w ΧελϜ-JOUΛ࡞Δࡍʹܕ৘ใΛ࢖ͬͨ-JOU΋࡞Ε Δ

    w ΧελϜ-JOUΛ࡞ΔࡍʹKBWB΋νΣοΫͰ͖Δ BOESPJEMJOUͷ͍͍ͱ͜Ζ
  9. © DMM.com X ॴײ ओ؍ LUMJOU 4UZMFدΓɻEFUFLUͱେ͖ͳࠩ͸ͳ͍ɻ ͨͩɺϧʔϧͷҰ෦ແࢹͱ͔͕Ͱ͖ͳ͍ɻ FEJUPSDPOpHͰҰ෦͸ม͑ΕΔ EFUFLU

    4UZMFدΓɻLUMJOUͱେ͖ͳࠩ͸ͳ͍ɻ BOESPJEMJOU 4UZMF΋ϩδοΫͷ֬ೝͳͲ΋Ͱ͖Δɻ ͨͩ͠ɺEFUFLU΍LUMJOUͰ༻ҙ͍ͯ͠Δ ඪ४ϧʔϧʹ֘౰͢Δ΋ͷ͸ɺ·ͩͳ͍໛༷  "4ͷJOTQFDUJPOʹ͸͋Δ
  10. © DMM.com X ͲΕ࢖͏ʁ ओ؍ ౰෼͸BOESPJEMJOUͱ LUMJOUPSEFUFLU ͷซ༻ w BOESPJEMJOU͸ඪ४ͰೖͬͯΔͷͰ֎͢ͱ͍͏બ୒ࢶ͸ͳ͍

    w LUMJOU΍EFUFLUͰελΠϧͷνΣοΫΛ͢Δ w ࣗ෼͸LUMJOUΛ࢖͍ͬͯΔ w ελΠϧدΓͷ$VTUPN-JOU͸LUMJOUͰ࡞͍ͬͯΔ w LUMJOU͸ݸผʹϧʔϧΛແޮʹͨ͠ΓͰ͖ͳ͍ͷͰɺEFUFLU ͷํ͕͍͍ͷͰ͸ʁͱࢥ͍࢝Ίͯ͸͍Δɻ  w LUMJOU΋EFUFLU΋$VTUPN-JOU͸खܰʹγϡοͱ࡞ΕΔ w BOESPJEMJOUͰ΋͠ࠓޙελΠϧपΓͷϧʔϧ͕ॆ࣮͖ͯͨ͠Βɺ ͦͷ࣌ʹLUMJOU΍EFUFLU͸֎͢͜ͱΛߟ͑Δ
  11. © DMM.com X $VTUPN-JOUΛ࡞Δʹ͸ʁ

  12. © 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 } πϦʔߏ଄Ͱղऍ͞ΕΔ
  13. © DMM.com X ͨͱ͑͹ɺҾ਺ͰA A͕͋ͬͨΒઈରվߦ͢Δ$VTUPN-JOUΛ࡞Δ࣌ fuga(1, 2) fuga( 1, 2

    ) /( 0,
  14. © DMM.com X ͨͱ͑͹ɺҾ਺ͰA A͕͋ͬͨΒઈରվߦ͢Δ$VTUPN-JOUΛ࡞Δ࣌ 7"-6& "3(6.&/5-*45 7"-6& "3(6.&/5 $0.."

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

    8)*5&41"$& 7"-6& "3(6.&/5     0, վߦίʔυؚ͕·Ε͍ͯΔ!
  16. © DMM.com X ͨͱ͑͹ɺҾ਺ͰA A͕͋ͬͨΒઈରվߦ͢Δ$VTUPN-JOUΛ࡞Δ࣌ 7"-6& "3(6.&/5-*45 7"-6& "3(6.&/5 $0.."

    8)*5&41"$& 7"-6& "3(6.&/5     /( վߦίʔυؚ͕·Ε͍ͯͳ͍"
  17. © DMM.com X ͨͱ͑͹ɺҾ਺ͰA A͕͋ͬͨΒઈରվߦ͢Δ$VTUPN-JOUΛ࡞Δ࣌ $VTUPN-JOUͷϩδοΫ͸ɺԼهΛ֬ೝ͢Δ w Լهͷߏ଄ʹͳ͍ͬͯΔͱ͖ʹ 7"-6& "3(6.&/5-*45

    7"-6& "3(6.&/5 $0.." 8)*5&41"$& 7"-6& "3(6.&/5 w ࡾͭ໨ͷ8)*5&@41"$&ʹվߦίʔυΛؚ·ΕΔ͜ͱ EFUFLUͰ΋LUMJOUͰ΋BOESPJEMJOUͰ΋ͲΕͰ΋ɺ ɹɹɹɹɹɹɹɹɹ$VTUPN-JOU͸͜ͷΑ͏ʹͯ͠࡞͍͖ͬͯ·͢
  18. © DMM.com X ͳΜͱͳ͘Θ͔͚ͬͨͲɺ ͦ΋ͦ΋πϦʔ͕Ͳ͏ͳͬͯΔͱ͔Θ͔ΒΜʜ

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

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

  21. © DMM.com X BOESPJEMJOUͰ $VTUPN-JOUΛ࡞ͬͯΈΔ

  22. © DMM.com X ͜͏͍͏ͷΛ࡞ͬͯݟ·͢ 3Y+BWBΛ࢖ͬͯͯɺ %JTQPTBCMFΛద੾ʹॲཧͯ͠ͳ͍ͱܯࠂग़͢$VTUPN-JOU

  23. © DMM.com X खॱ  ϓϩδΣΫτΛ࡞Δ  %FUFDUPSΛ࡞Δ  *TTVFΛ࡞Δ

     3FHJTUSZΛ࡞Δ  3FHJTUSZΛCVJMEHSBEMFʹهड़
  24. © DMM.com X ̍ϓϩδΣΫτΛ࡞Δ "OESPJE4UVEJP΍*OUFMMJK*EFBΛ։͍ͯ 'JMF/FX/FX1SPKFDU ͢Δ͚ͩɻ ͨͩɺHPPHMFTBNQMFTʹςϯϓϨʔτͷΑ͏ͳ΋ͷ͕ ͋ΔͷͰɺͦΕʹͳΒ͏ͷ͕ૣ͍ IUUQTHJUIVCDPNHPPHMFTBNQMFTBOESPJEDVTUPN

    MJOUSVMFTUSFFNBTUFSBOESPJETUVEJP
  25. © DMM.com X ̎%FUFDUPSΛ࡞Δ public class NotHandledDisposableDetector extends Detector implements

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

    UastScanner { @Override public List<Class<? extends UElement>> 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ͷུɻ
  27. © DMM.com X ̎%FUFDUPSΛ࡞Δ public class NotHandledDisposableDetector extends Detector implements

    UastScanner { @Override public List<Class<? extends UElement>> 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Λ࣮૷͢Δɻ
  28. © 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 WJTJU WJTJU WJTJU WJTJU WJTJU
  29. © DMM.com X ࠓճ͸ ࠓճ͸1TJ7JXFSΛݟΔݶΓɺ%05@26"-*'*&%@&913&44*0/ ͕དྷͨ࣌ʹ֬ೝ͢Δ͚ͩͰࡁΈͦ͏ͳͷͰɺ WJTJU2VBMJpFE3FGFSFODF&YQSFTTJPOͷΈΛPWFSJSEF͢Δɻ

  30. © DMM.com X ̎%FUFDUPSΛ࡞Δ public class NotHandledDisposableDetector extends Detector implements

    UastScanner { @Override public List<Class<? extends UElement>> 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ϝιου͕ݺ͹Ε·ͤΜ
  31. © 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ͷத਎Λઆ໌͠·͢ɻ
  32. © 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Ͱɺ
  33. © 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Ͱ
  34. © 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Ͱ͸ࠓͷॴͰ͖·ͤΜ
  35. © 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; } Ϩϙʔτ͢Δʂ ܯࠂ͕ग़Δ
  36. © 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*ͷܕ͕ҧ͏ͷͰผ్ॻ͘ඞཁ͕͋Γ·͢ɻ
  37. © 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Ϋϥεʹॻ͍ͯΔͱ͜Ζ͕΄ͱΜͲ Ͱ͢ɻ
  38. © DMM.com X ̐3FHJTUSZΛ࡞Δ public class CustomIssueRegistry extends IssueRegistry {

    @Override public List<Issue> getIssues() { return Collections.singletonList( NotHandledDisposableDetector.ISSUE ); } } ࡞ͬͨ*TTVFΛొ࿥͢Δ3FHJTUPSZΫϥεΛ࡞Γ·͢ɻ *TTVF͸ෳ਺ొ࿥Ͱ͖·͢ɻ
  39. © DMM.com X ̑3FHJTUSZΛCVJMEHSBEMFʹهड़ jar { manifest { attributes("Lint-Registry-v2": "com.kgmyshin.lint.CustomIssueRegistry")

    } } A-JOU3FHJTUSZ7Aʹઌ΄Ͳ࡞ͬͨ3FHJTUSZΛઃఆ͠·͢ɻ A7AΛ๨Εͳ͍Α͏ʹɻ
  40. © DMM.com X ͜ΕͰɺ$VTUPN-JOUͷ׬੒Ͱ͢ʂ

  41. © DMM.com X ࡞ͬͨBOESPJEMJOUͷ $VTUPN-JOUΛςετ͢Δ

  42. © DMM.com X ςετͷॻ͖ํ public void ςετϝιου໊() { @Language("kotlin") String

    content = "ʢର৅ίʔυʣ"; lint().files( kotlin(content) ).run().expect("Τϥʔݕग़࣌΋͘͠͸੒ޭ࣌ͷจݴ") } ςετ͸͜ͷΑ͏ʹॻ͖·͢ɻ
  43. © DMM.com X ςετͷॻ͖ํ public void ςετϝιου໊() { @Language("kotlin") String

    content = "ʢର৅ίʔυʣ"; lint().files( kotlin(content) ).run().expect("Τϥʔݕग़࣌΋͘͠͸੒ޭ࣌ͷจݴ") } ର৅ίʔυΛจࣈྻͰ༻ҙɻ ௕͘ͳΔ͜ͱ͕ଟ͍ͱࢥ͏ͷͰɺϑΝΠϧಡΈࠐΈʹ͢Δͷ͕٢ɻ
  44. © DMM.com X ςετͷॻ͖ํ public void ςετϝιου໊() { @Language("kotlin") String

    content = "ʢର৅ίʔυʣ"; lint().files( kotlin(content) ).run().expect("Τϥʔݕग़࣌΋͘͠͸੒ޭ࣌ͷจݴ") } LPUMJOϑΝΠϧͱͯ͠౉ͯ͠ɺΤϥʔจݴ͕͋ͬͯΔ͔Ͳ͏ ͔ΛAFYQFDUAϝιουͰ֬ೝ͢Δɻ
  45. © DMM.com X ςετͷॻ͖ํ public void ςετϝιου໊() { @Language(“JAVA") String

    content = "ʢର৅ίʔυʣ"; lint().files( java(content) ).run().expect("Τϥʔݕग़࣌΋͘͠͸੒ޭ࣌ͷจݴ") } KBWBͷͱ͖͸ɺԼهՕॴ͕KBWBʹͳΓ·͢ɻ
  46. © DMM.com X ࡞ͬͨBOESPJEMJOUͷ $VTUPN-JOUΛ࢖͏

  47. © DMM.com X ࡞ͬͨ$VTUPN-JOUͷ࢖͍ํ dependencies { implementation 'io.reactivex.rxjava2:rxjava:2.1.10' lintChecks project(":checks")

    } ಉҰϓϩδΣΫτʹ͋Δ৔߹͸ɺԼهͷΑ͏ʹEFQFOEFODJFTʹ MJOU$IFDLTQSPKFDU $VTUPN-JOUϞδϡʔϧ  Λ௥Ճ
  48. © DMM.com X ࡞ͬͨ$VTUPN-JOUͷ࢖͍ํ dependencies { implementation 'io.reactivex.rxjava2:rxjava:2.1.10' lintChecks files("lint/custom-lint.jar")

    } ଞͷϓϩδΣΫτͳͲͰͷ࠶ར༻໨తͰKBSͷΈ͔͠ͳ͍৔߹͸ MJOU$IFDLTpMFT KBSͷύε  Λ௥Ճ
  49. © 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() ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ : ແࣄಈ͖·ͨ͠ʂ
  50. © DMM.com X ·ͱΊ

  51. © DMM.com X ·ͱΊ w LPUMJOͰMJOUΛ͔͚Δʹ͸ͭͷํ๏͕͋Δ w LUMJOUͰ΋EFUFLUͰ΋BOESPJEMJOUͷ৔߹Ͱ΋ɺ $VTUPN-JOU࡞Δ࣌͸"45 ˺14*

    Λ஌Δඞཁ͕͋ Δ͜ͱΛઆ໌ w BOESPJEMJOUͷ$VTUPN-JOUͷ࡞ΓํΛઆ໌
  52. ͝੩ௌ͋Γ͕ͱ͏͍͟͝·ͨ͠ɻ