Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Sign up for free
Menu
Search
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Pricing
Search
Sign in
Sign up for free
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
930
jetpack_benchmark.pdf
takakofire
2
150
livedata
takakofire
0
270
Other Decks in Technology
See All in Technology
Kiro IDE 入門 (画面の各部から、頼み方、任せ方、戻し方まで) | 目指せ Kiro マスター 02
inariku
PRO
4
180
基本から分かる!AWSセキュリティ運用の考え方と任せ方
cmusudakeisuke
1
480
レガシーコードに対する loop engineering的なAI開発プロセス導入について
kubell_hr
0
310
内製アプリとClaudeスキルのセキュリティチェック、 Backlogの課題で回しています。
ayaka_egaaa
0
160
Kiro を使うコツ (頼み方、覚えさせ方、任せ方) | 目指せ Kiro マスター 09
inariku
PRO
3
120
超⼩型・多⾔語検索モデル Bekko Embedding
hotchpotch
1
140
AI Agent 時代のデータ基盤: ClickHouse で実現する 超高速分析とコスト削減の両立
jozono
0
220
お客様の「やりたい」の先を見通す合意形成プロセス ~裏にある課題と不安を解きほぐすコツ~
yukakoseki
0
140
AI時代に求められる優秀なTechLeadの振る舞い
pospome
11
16k
Context Ontology Accelerator 入門 (業務データの意味を、AI エージェントに渡す)
inariku
PRO
3
610
re:Invent2026 事前勉強会 歴史と愉しみ方10分LT編
toshi_atsumi
0
180
生成AI時代のIAMのマニアックな話
takuros
6
2.9k
Featured
See All Featured
Documentation Writing (for coders)
carmenintech
77
5.6k
Primal Persuasion: How to Engage the Brain for Learning That Lasts
tmiket
0
520
ラッコキーワード サービス紹介資料
rakko
5
5.8M
16th Malabo Montpellier Forum Presentation
akademiya2063
PRO
0
460
DevOps and Value Stream Thinking: Enabling flow, efficiency and business value
helenjbeal
2
440
How To Speak Unicorn (iThemes Webinar)
marktimemedia
1
630
Visual Storytelling: How to be a Superhuman Communicator
reverentgeek
2
750
So, you think you're a good person
axbom
PRO
2
2.3k
How to Create Impact in a Changing Tech Landscape [PerfNow 2023]
tammyeverts
56
3.5k
Applied NLP in the Age of Generative AI
inesmontani
PRO
4
2.6k
Writing Fast Ruby
sferik
630
63k
Ethics towards AI in product and experience design
skipperchong
2
460
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
͝੩ௌ ͋Γ͕ͱ͏͍͟͝·ͨ͠ʂ