Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
my_custom_lint
Search
takako-fire
March 27, 2019
Technology
410
2
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
my_custom_lint
takako-fire
March 27, 2019
More Decks by takako-fire
See All by takako-fire
mobile app development
takakofire
0
870
jetpack_benchmark.pdf
takakofire
2
150
livedata
takakofire
0
260
Other Decks in Technology
See All in Technology
AI時代における最適なQA組織の作り方
ymty
3
350
AI駆動開発におけるQAエンジニアの役割事例 〜AI駆動開発の現場から〜
kobayashiyorimitsu
0
290
ループエンジニアリングでE2Eテストを実践
noriyukitakei
0
250
Tech-Verse 2026_Keynote
lycorptech_jp
PRO
0
130
Claude Codeとハーネスについて考えてみる
oikon48
16
7.6k
飲食店もAIで。レジ締めやハンディシステムをつくってる話 / Using AI for restaurant management
vtryo
0
230
プライバシー保護の理論と実践
lycorptech_jp
PRO
1
230
攻撃者がいなくてもAIエージェントはインシデントを起こす
nomizone
0
190
打造你的 AI 工作流:Agent Skill + MCP 實戰工作坊
appleboy
0
490
Amazon EVS で VCF 9.0 / 9.1 のサポート開始まとめ
mtoyoda
0
170
From Prompt Engineering to Loop Engineering
shibuiwilliam
1
330
ご挨拶「10周年を迎える共創ラボのこれまでとこれから」
iotcomjpadmin
0
180
Featured
See All Featured
Distributed Sagas: A Protocol for Coordinating Microservices
caitiem20
333
23k
StorybookのUI Testing Handbookを読んだ
zakiyama
31
6.8k
The State of eCommerce SEO: How to Win in Today's Products SERPs - #SEOweek
aleyda
2
11k
The MySQL Ecosystem @ GitHub 2015
samlambert
251
13k
How to Build an AI Search Optimization Roadmap - Criteria and Steps to Take #SEOIRL
aleyda
1
2.1k
Test your architecture with Archunit
thirion
1
2.3k
職位にかかわらず全員がリーダーシップを発揮するチーム作り / Building a team where everyone can demonstrate leadership regardless of position
madoxten
62
55k
How to Grow Your eCommerce with AI & Automation
katarinadahlin
PRO
1
220
The World Runs on Bad Software
bkeepers
PRO
72
12k
The Success of Rails: Ensuring Growth for the Next 100 Years
eileencodes
47
8.2k
Code Review Best Practice
trishagee
74
20k
HU Berlin: Industrial-Strength Natural Language Processing with spaCy and Prodigy
inesmontani
PRO
0
430
Transcript
զ͕ՈͷCustom Lint @Takako
ࣗݾհ
ࣗݾհ ɾTakako ɹɾTwitter: Takako(@kotokoto_fire) ɾAndroidΤϯδχΞ ɾmenusͱ͍͏ݙཱΞϓϦΛ࡞ͬͯ·͢
ͱ͋Δͷίϛοτ
ͱ͋Δͷίϛοτ
ϨϏϡʔͰݟ͚ͭΔͷ ͘͠ͳ͍ʁ
ͦ͏ͩʂ CustomLintͩʂ
Androidʹ͓͚ΔLint
Androidʹ͓͚ΔLint LintΛར༻͢ΔͱΞϓϦΛ࣮ߦͨ͠Γɺςε τέʔεΛ࡞ͨ͠Γ͠ͳͯ͘ɺίʔυߏ ͷ࣭ʹؔ͢ΔΛಛఆͯ͠मਖ਼͢Δ͜ ͱ͕Ͱ͖·͢ IUUQTEFWFMPQFSBOESPJEDPNTUVEJPXSJUFMJOU IMKB
Androidʹ͓͚ΔLint
Androidʹ͓͚ΔLint
LintʹRuleΛՃʁ
Lintͷछྨ
Lintͷछྨ ɾktlint ɾandroid-lint ɾ(detekt)
Lintͷछྨ ɾktlint ɾandroid-lint ɾ(detekt)
LintΛCustom͢Δ
LintΛCustom͢Δ ~ಋೖ~ <ktlint> https://github.com/pinterest/ktlint <android-lint> https://github.com/googlesamples/android- custom-lint-rules
LintΛCustom͢Δ ~AST~ fun sampel() { val a = b +
c }
զ͕ՈͷRule
Check onError in subscribeBy
Check onError in subscribeBy <త> ɾsubscribe͢Δͱ͖ʹonErrorΛΩϟον͍ͯ͠ͳ ͚Εܯࠂ <લఏ> ɾݱঢ়subscribeΛ࣮ߦ͢Δࡍ΄ͱΜͱ subscribeByͰ࣮ߦ͢Δ
Check onError in subscribeBy <త> ɾsubscribe͢Δͱ͖ʹonErrorΛΩϟον͍ͯ͠ͳ ͚Εܯࠂ <લఏ> ɾݱঢ়subscribeΛ࣮ߦ͢Δࡍ΄ͱΜͱ subscribeByͰ࣮ߦ͢Δ
Check onError in subscribeBy package com.google.samples.apps.sunflower import io.reactivex.Single class Sample
{ val sample = Single.just("").subscribeBy {} } IUUQTHJUIVCDPNHPPHMFTBNQMFTBOESPJETVOqPXFS
Check onError in subscribeBy
Check onError in subscribeBy package com.google.samples.apps.sunflower import io.reactivex.Single class Sample
{ val sample = Single.just(“”).subscribeBy {} } 1"$,"(&@%*3&$5*0/
Check onError in subscribeBy package com.google.samples.apps.sunflower import io.reactivex.Single class Sample
{ val sample = Single.just(“").subscribeBy {} } ˡ1TJ8IJUF4QBDF
Check onError in subscribeBy package com.google.samples.apps.sunflower import io.reactivex.Single class Sample
{ val sample = Single.just(“”).subscribeBy {} } ˢ*.1035@-*45
Check onError in subscribeBy package com.google.samples.apps.sunflower import io.reactivex.Single class Sample
{ val sample = Single.just(“").subscribeBy {} } ˡ1TJ8IJUF4QBDF
Check onError in subscribeBy package com.google.samples.apps.sunflower import io.reactivex.Single class Sample
{ val sample = Single.just("").subscribeBy {} } ˡ$-"44
Check onError in subscribeBy package com.google.samples.apps.sunflower import io.reactivex.Single class Sample
{ val sample = Single.just(“").subscribeBy {} } ˡ1TJ8IJUF4QBDF
Check onError in subscribeBy ˢ461&3@5:1&@-*45ʹFYUFOETJNQMFNFOUT͕ฒͿ
Check onError in subscribeBy TVCTDSJCF#ZҎ߱ͷUFYU͕ཏྻ͞Ε͍ͯΔ
Check onError in subscribeBy class Sample { val sample =
Single.just("").subscribeBy( onError = { } ) }
Check onError in subscribeBy ͜ͷลΓո͍͠
Check onError in subscribeBy
Check onError in subscribeBy Detector ɾDetector ɾResourceXmlDetector ɾLayoutDetector …. Scanner
ɾUastScanner ɾClassScanner ɾXmlScanner ….
Check onError in subscribeBy Detector ɾDetector ɾResourceXmlDetector ɾLayoutDetector …. Scanner
ɾUastScanner ɾClassScanner ɾXmlScanner ….
Check onError in subscribeBy class NotHandledOnError : Detector(), Detector.UastScanner {
override fun getApplicableUastTypes(): List<Class<out UElement>>? { return Collections.singletonList( UCallExpression::class.java ) } }
Check onError in subscribeBy class NotHandledOnError : Detector(), Detector.UastScanner {
override fun createUastHandler(context: JavaContext): UElementHandler? { return object : UElementHandler() { override fun visitCallExpression(node: UCallExpression) { } } } }
Check onError in subscribeBy override fun createUastHandler(context: JavaContext): UElementHandler? {
return object : UElementHandler() { override fun visitCallExpression(node: UCallExpression) { if ( node.sourcePsi?.context.toString() == "DOT_QUALIFIED_EXPRESSION" && node.methodName?.startsWith("subscribeBy") == true && node.valueArgumentCount > 0 && node.sourcePsi?.text?.contains("onError") == false ) { context.report( NotHandledOnError.ISSUE, node, context.getLocation(node), "Should handle onError" ) } } } }
Check onError in subscribeBy override fun createUastHandler(context: JavaContext): UElementHandler? {
return object : UElementHandler() { override fun visitCallExpression(node: UCallExpression) { if ( node.sourcePsi?.context.toString() == "DOT_QUALIFIED_EXPRESSION" && node.methodName?.startsWith("subscribeBy") == true && node.valueArgumentCount > 0 && node.sourcePsi?.text?.contains("onError") == false ) { context.report( NotHandledOnError.ISSUE, node, context.getLocation(node), "Should handle onError" ) } } } }
Check onError in subscribeBy override fun createUastHandler(context: JavaContext): UElementHandler? {
return object : UElementHandler() { override fun visitCallExpression(node: UCallExpression) { if ( node.sourcePsi?.context.toString() == "DOT_QUALIFIED_EXPRESSION" && node.methodName?.startsWith("subscribeBy") == true && node.valueArgumentCount > 0 && node.sourcePsi?.text?.contains("onError") == false ) { context.report( NotHandledOnError.ISSUE, node, context.getLocation(node), "Should handle onError" ) } } } }
Check onError in subscribeBy override fun visitCallExpression(node: UCallExpression) { if
( node.sourcePsi?.context.toString() == "DOT_QUALIFIED_EXPRESSION" && node.methodName?.startsWith("subscribeBy") == true && node.valueArgumentCount > 0 && node.sourcePsi?.text?.contains("onError") == false ) { context.report( NotHandledOnError.ISSUE, node, context.getLocation(node), "Should handle onError" ) } }
Check onError in subscribeBy class NotHandledOnError : Detector(), Detector.UastScanner {
companion object { val ISSUE: Issue = Issue.create( "NotHandledOnError", "Not Handled onError", "Add onError to subscribeBy.", Category.CORRECTNESS, 6, Severity.ERROR, Implementation( NotHandledOnError::class.java, Scope.JAVA_FILE_SCOPE ) ) } }
Check onError in subscribeBy class CustomIssueRegistry : IssueRegistry() { override
val issues: List<Issue> get() { return Collections.singletonList( NotHandledOnError.ISSUE ) } }
Check onError in subscribeBy jar { manifest { attributes("Lint-Registry-v2": "com.example.custom_lint.CustomIssueRegistry")
} }
Is Drawable attr in TextView
Is Drawable attr in TextView <త> ɾTextViewʹdrawableXX͕͋Εwarning
Is Drawable attr in TextView class NotUseDrawableInTextView : LayoutDetector() {
override fun getApplicableElements(): Collection<String>? { return Collections.singletonList(SdkConstants.TEXT_VIEW) } override fun visitElement( context: XmlContext, element: Element ) { val attrNode = getDrawableAttribute(element) if (attrNode != null) { // ͜͜ʹ֘࣌ͷॲཧ } } private fun getDrawableAttribute(element: Element): Attr? { return element.getAttributeNodeNS(SdkConstants.ANDROID_URI, "drawableEnd") ?: ུ } }
Is Drawable attr in TextView class NotUseDrawableInTextView : LayoutDetector() {
override fun getApplicableElements(): Collection<String>? { return Collections.singletonList(SdkConstants.TEXT_VIEW) } override fun visitElement( context: XmlContext, element: Element ) { val attrNode = getDrawableAttribute(element) if (attrNode != null) { // ͜͜ʹ֘࣌ͷॲཧ } } private fun getDrawableAttribute(element: Element): Attr? { return element.getAttributeNodeNS(SdkConstants.ANDROID_URI, "drawableEnd") ?: ུ } }
Is Drawable attr in TextView class NotUseDrawableInTextView : LayoutDetector() {
override fun getApplicableElements(): Collection<String>? { return Collections.singletonList(SdkConstants.TEXT_VIEW) } override fun visitElement( context: XmlContext, element: Element ) { val attrNode = getDrawableAttribute(element) if (attrNode != null) { // ͜͜ʹ֘࣌ͷॲཧ } } private fun getDrawableAttribute(element: Element): Attr? { return element.getAttributeNodeNS(SdkConstants.ANDROID_URI, "drawableEnd") ?: ུ } }
Is Drawable attr in TextView class NotUseDrawableInTextView : LayoutDetector() {
override fun getApplicableElements(): Collection<String>? { return Collections.singletonList(SdkConstants.TEXT_VIEW) } override fun visitElement( context: XmlContext, element: Element ) { val attrNode = getDrawableAttribute(element) if (attrNode != null) { // ͜͜ʹ֘࣌ͷॲཧ } } private fun getDrawableAttribute(element: Element): Attr? { return element.getAttributeNodeNS(SdkConstants.ANDROID_URI, "drawableEnd") ?: ུ } }
Is Drawable attr in TextView class NotUseDrawableInTextView : LayoutDetector() {
override fun visitElement( context: XmlContext, element: Element ) { val attrNode = getDrawableAttribute(element) if (attrNode != null) { context.report( ISSUE, attrNode, context.getLocation(attrNode), "If you use vector resource, ུ" ) } } }
Is Drawable attr in TextView
Is Drawable attr in TextView class NotUseDrawableInTextView : LayoutDetector() {
companion object { val ISSUE: Issue = Issue.create( "NotUseDrawableInTextView", "Not use drawable in TextView", "Not use drawable in TextView", Category.LINT, 6, Severity.WARNING, Implementation( NotUseDrawableInTextView::class.java, Scope.RESOURCE_FILE_SCOPE ) ) } }
PRͷվળ
PR্Ͱ…
PR্Ͱ…
·ͱΊ
·ͱΊ ɾLintΧελϚΠζͰ͖Δ ɾScanner, DetectorྟػԠมʹ͍͚Α͏ ɾDangerͳͲͱ࿈ܞ͢ΔͱɺPRΛϚʔδ͢Δ લʹbot͔ΒࢦఠͰ͖Δ
Let’s improve your code with CustomLint
͝੩ௌ ͋Γ͕ͱ͏͍͟͝·ͨ͠ʂ