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
900
jetpack_benchmark.pdf
takakofire
2
150
livedata
takakofire
0
260
Other Decks in Technology
See All in Technology
2026/09/10 Spring_Bootから_Jakarta_EE_MicroProfileへの移行
megascus
0
240
KPIだけでは評価できないプロダクトが考えるべき Evalsという第二の評価系 / Beyond KPIs: Evals as a Second Evaluation Framework for Products #PdEConf
aki_iinuma
4
3.8k
GuardDuty 検知対応を DevOps Agent で効率化しようとしている話 / GuardDuty Investigations with DevOps Agent
masahirokawahara
1
340
なぜSRE・セキュリティは評価されないのか?守りの組織を事業成長エンジンに変えた実践
cscengineer
PRO
3
2.4k
Podは生きているのにGoだけが落ちる:GOGCとGOMEMLIMITで追うInvisible OOM Killの謎
tkc66buzz
1
210
Where Is JetBrains AI Heading- — Central CLI, Air Alpha, and the Agentic Development Stack
x5gtrn
PRO
0
150
AIとペアプロを始める。人とのペアプロをやめる。ペアプロの良さを改めて知る。もっと好きになった。 / Rediscovering Pair Programming
honyanya
1
290
Azure Cost Management の FOCUS コストデータを迷わず読むための“3つの軸”
tetsuyaooooo
0
160
薬剤師(ドメインエキスパート)と一緒に育てる薬局向けAIアシスタント
kakehashi
PRO
2
120
コスト最適化の「めんどくさい」を AWS FinOps Agent でチョット楽にする
classmethod_kaz
0
280
Jetpack Compose で挑む新聞紙面UI ─ 複合ジェスチャー・ポリゴン記事領域・適応的ページ構成という3つの壁/droidkaigi2026
nikkei_engineer_recruiting
0
240
V8コントリビュート超入門
riyaamemiya
0
150
Featured
See All Featured
Principles of Awesome APIs and How to Build Them.
keavy
128
18k
The Power of CSS Pseudo Elements
geoffreycrofte
82
6.5k
Rails Girls Zürich Keynote
gr2m
96
14k
Build The Right Thing And Hit Your Dates
maggiecrowley
39
3.4k
Designing for Timeless Needs
cassininazir
1
470
Dealing with People You Can't Stand - Big Design 2015
cassininazir
367
27k
The AI Search Optimization Roadmap by Aleyda Solis
aleyda
1
6.2k
Collaborative Software Design: How to facilitate domain modelling decisions
baasie
1
310
Creating an realtime collaboration tool: Agile Flush - .NET Oxford
marcduiker
35
2.6k
Embracing the Ebb and Flow
colly
88
5.2k
Side Projects
sachag
455
43k
Statistics for Hackers
jakevdp
799
230k
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
͝੩ௌ ͋Γ͕ͱ͏͍͟͝·ͨ͠ʂ