1
0
Fork 0

Closes #8944 - Adds custom lint checks

master
ekager 2020-03-03 10:44:54 -08:00 committed by liuche
parent 06e5efd03e
commit 414e905b76
8 changed files with 304 additions and 0 deletions

View File

@ -581,6 +581,8 @@ dependencies {
// For the initial release of Glean 19, we require consumer applications to
// depend on a separate library for unit tests. This will be removed in future releases.
testImplementation "org.mozilla.telemetry:glean-forUnitTests:${project.ext.glean_version}"
lintChecks project(":mozilla-lint-rules")
}
if (project.hasProperty("raptor")) {

1
mozilla-lint-rules/.gitignore vendored 100644
View File

@ -0,0 +1 @@
/build

View File

@ -0,0 +1,21 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
apply plugin: 'java-library'
apply plugin: 'kotlin'
targetCompatibility = JavaVersion.VERSION_1_8
sourceCompatibility = JavaVersion.VERSION_1_8
dependencies {
compileOnly "org.jetbrains.kotlin:kotlin-stdlib:1.3.61"
compileOnly "com.android.tools.lint:lint-api:26.6.1"
compileOnly "com.android.tools.lint:lint-checks:26.6.1"
}
jar {
manifest {
attributes('Lint-Registry-v2': 'org.mozilla.fenix.lintrules.LintIssueRegistry')
}
}

View File

@ -0,0 +1,80 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.fenix.lintrules
import com.android.SdkConstants.ATTR_SRC
import com.android.SdkConstants.FQCN_IMAGE_BUTTON
import com.android.SdkConstants.FQCN_IMAGE_VIEW
import com.android.SdkConstants.IMAGE_BUTTON
import com.android.SdkConstants.IMAGE_VIEW
import com.android.resources.ResourceFolderType
import com.android.tools.lint.detector.api.Category
import com.android.tools.lint.detector.api.Implementation
import com.android.tools.lint.detector.api.Issue
import com.android.tools.lint.detector.api.ResourceXmlDetector
import com.android.tools.lint.detector.api.Scope
import com.android.tools.lint.detector.api.Severity
import com.android.tools.lint.detector.api.XmlContext
import org.w3c.dom.Element
/**
* A custom lint check that prohibits not using the app:srcCompat for ImageViews
*/
class AndroidSrcXmlDetector : ResourceXmlDetector() {
companion object {
const val SCHEMA = "http://schemas.android.com/apk/res/android"
const val FULLY_QUALIFIED_APP_COMPAT_IMAGE_BUTTON =
"androidx.appcompat.widget.AppCompatImageButton"
const val FULLY_QUALIFIED_APP_COMPAT_VIEW_CLASS =
"androidx.appcompat.widget.AppCompatImageView"
const val APP_COMPAT_IMAGE_BUTTON = "AppCompatImageButton"
const val APP_COMPAT_IMAGE_VIEW = "AppCompatImageView"
const val ERROR_MESSAGE = "Using android:src to define resource instead of app:srcCompat"
val ISSUE_XML_SRC_USAGE = Issue.create(
id = "AndroidSrcXmlDetector",
briefDescription = "Prohibits using android:src in ImageViews and ImageButtons",
explanation = "ImageView (and descendants) images should be declared using app:srcCompat",
category = Category.CORRECTNESS,
severity = Severity.ERROR,
implementation = Implementation(
AndroidSrcXmlDetector::class.java,
Scope.RESOURCE_FILE_SCOPE
)
)
}
override fun appliesTo(folderType: ResourceFolderType): Boolean {
// Return true if we want to analyze resource files in the specified resource
// folder type. In this case we only need to analyze layout resource files.
return folderType == ResourceFolderType.LAYOUT
}
override fun getApplicableElements(): Collection<String>? {
return setOf(
FQCN_IMAGE_VIEW,
IMAGE_VIEW,
FQCN_IMAGE_BUTTON,
IMAGE_BUTTON,
FULLY_QUALIFIED_APP_COMPAT_IMAGE_BUTTON,
FULLY_QUALIFIED_APP_COMPAT_VIEW_CLASS,
APP_COMPAT_IMAGE_BUTTON,
APP_COMPAT_IMAGE_VIEW
)
}
override fun visitElement(context: XmlContext, element: Element) {
if (!element.hasAttributeNS(SCHEMA, ATTR_SRC)) return
val node = element.getAttributeNodeNS(SCHEMA, ATTR_SRC)
context.report(
issue = ISSUE_XML_SRC_USAGE,
scope = node,
location = context.getLocation(node),
message = ERROR_MESSAGE
)
}
}

View File

@ -0,0 +1,81 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.fenix.lintrules
import com.android.SdkConstants.ATTR_TINT
import com.android.SdkConstants.FQCN_IMAGE_BUTTON
import com.android.SdkConstants.FQCN_IMAGE_VIEW
import com.android.SdkConstants.IMAGE_BUTTON
import com.android.SdkConstants.IMAGE_VIEW
import com.android.resources.ResourceFolderType
import com.android.tools.lint.detector.api.Category
import com.android.tools.lint.detector.api.Implementation
import com.android.tools.lint.detector.api.Issue
import com.android.tools.lint.detector.api.ResourceXmlDetector
import com.android.tools.lint.detector.api.Scope
import com.android.tools.lint.detector.api.Severity
import com.android.tools.lint.detector.api.XmlContext
import org.w3c.dom.Element
/**
* A custom lint check that prohibits not using the app:tint for ImageViews
*/
class ImageViewAndroidTintXmlDetector : ResourceXmlDetector() {
companion object {
const val SCHEMA = "http://schemas.android.com/apk/res/android"
const val FULLY_QUALIFIED_APP_COMPAT_IMAGE_BUTTON =
"androidx.appcompat.widget.AppCompatImageButton"
const val FULLY_QUALIFIED_APP_COMPAT_VIEW_CLASS =
"androidx.appcompat.widget.AppCompatImageView"
const val APP_COMPAT_IMAGE_BUTTON = "AppCompatImageButton"
const val APP_COMPAT_IMAGE_VIEW = "AppCompatImageView"
const val ERROR_MESSAGE =
"Using android:tint to tint ImageView instead of app:tint with AppCompatImageView"
val ISSUE_XML_SRC_USAGE = Issue.create(
id = "AndroidSrcXmlDetector",
briefDescription = "Prohibits using android:tint in ImageViews and ImageButtons",
explanation = "ImageView (and descendants) should be tinted using app:tint",
category = Category.CORRECTNESS,
severity = Severity.ERROR,
implementation = Implementation(
ImageViewAndroidTintXmlDetector::class.java,
Scope.RESOURCE_FILE_SCOPE
)
)
}
override fun appliesTo(folderType: ResourceFolderType): Boolean {
// Return true if we want to analyze resource files in the specified resource
// folder type. In this case we only need to analyze layout resource files.
return folderType == ResourceFolderType.LAYOUT
}
override fun getApplicableElements(): Collection<String>? {
return setOf(
FQCN_IMAGE_VIEW,
IMAGE_VIEW,
FQCN_IMAGE_BUTTON,
IMAGE_BUTTON,
FULLY_QUALIFIED_APP_COMPAT_IMAGE_BUTTON,
FULLY_QUALIFIED_APP_COMPAT_VIEW_CLASS,
APP_COMPAT_IMAGE_BUTTON,
APP_COMPAT_IMAGE_VIEW
)
}
override fun visitElement(context: XmlContext, element: Element) {
if (!element.hasAttributeNS(SCHEMA, ATTR_TINT)) return
val node = element.getAttributeNodeNS(SCHEMA, ATTR_TINT)
context.report(
issue = ISSUE_XML_SRC_USAGE,
scope = node,
location = context.getLocation(node),
message = ERROR_MESSAGE
)
}
}

View File

@ -0,0 +1,21 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.fenix.lintrules
import com.android.tools.lint.client.api.IssueRegistry
import com.android.tools.lint.detector.api.Issue
/**
* Registry which provides a list of our custom lint checks to be performed on an Android project.
*/
@Suppress("unused")
class LintIssueRegistry : IssueRegistry() {
override val api: Int = com.android.tools.lint.detector.api.CURRENT_API
override val issues: List<Issue> = listOf(
AndroidSrcXmlDetector.ISSUE_XML_SRC_USAGE,
TextViewAndroidSrcXmlDetector.ISSUE_XML_SRC_USAGE,
ImageViewAndroidTintXmlDetector.ISSUE_XML_SRC_USAGE
)
}

View File

@ -0,0 +1,97 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.fenix.lintrules
import com.android.SdkConstants.ATTR_DRAWABLE_BOTTOM
import com.android.SdkConstants.ATTR_DRAWABLE_END
import com.android.SdkConstants.ATTR_DRAWABLE_LEFT
import com.android.SdkConstants.ATTR_DRAWABLE_RIGHT
import com.android.SdkConstants.ATTR_DRAWABLE_START
import com.android.SdkConstants.ATTR_DRAWABLE_TOP
import com.android.SdkConstants.FQCN_TEXT_VIEW
import com.android.SdkConstants.TEXT_VIEW
import com.android.resources.ResourceFolderType
import com.android.tools.lint.detector.api.Category
import com.android.tools.lint.detector.api.Implementation
import com.android.tools.lint.detector.api.Issue
import com.android.tools.lint.detector.api.ResourceXmlDetector
import com.android.tools.lint.detector.api.Scope
import com.android.tools.lint.detector.api.Severity
import com.android.tools.lint.detector.api.XmlContext
import org.w3c.dom.Element
/**
* A custom lint check that prohibits not using the app:srcCompat for ImageViews
*/
class TextViewAndroidSrcXmlDetector : ResourceXmlDetector() {
companion object {
const val SCHEMA = "http://schemas.android.com/apk/res/android"
const val ERROR_MESSAGE =
"Using android:drawableX to define resource instead of app:drawableXCompat"
val ISSUE_XML_SRC_USAGE = Issue.create(
id = "TextViewAndroidSrcXmlDetector",
briefDescription = "Prohibits using android namespace to define drawables in TextViews",
explanation = "TextView drawables should be declared using app:drawableXCompat",
category = Category.CORRECTNESS,
severity = Severity.ERROR,
implementation = Implementation(
TextViewAndroidSrcXmlDetector::class.java,
Scope.RESOURCE_FILE_SCOPE
)
)
}
override fun appliesTo(folderType: ResourceFolderType): Boolean {
// Return true if we want to analyze resource files in the specified resource
// folder type. In this case we only need to analyze layout resource files.
return folderType == ResourceFolderType.LAYOUT
}
override fun getApplicableElements(): Collection<String>? {
return setOf(
FQCN_TEXT_VIEW,
TEXT_VIEW
)
}
override fun visitElement(context: XmlContext, element: Element) {
val node = when {
element.hasAttributeNS(SCHEMA, ATTR_DRAWABLE_BOTTOM) -> element.getAttributeNodeNS(
SCHEMA,
ATTR_DRAWABLE_BOTTOM
)
element.hasAttributeNS(SCHEMA, ATTR_DRAWABLE_END) -> element.getAttributeNodeNS(
SCHEMA,
ATTR_DRAWABLE_END
)
element.hasAttributeNS(SCHEMA, ATTR_DRAWABLE_LEFT) -> element.getAttributeNodeNS(
SCHEMA,
ATTR_DRAWABLE_LEFT
)
element.hasAttributeNS(
SCHEMA,
ATTR_DRAWABLE_RIGHT
) -> element.getAttributeNodeNS(SCHEMA, ATTR_DRAWABLE_RIGHT)
element.hasAttributeNS(
SCHEMA,
ATTR_DRAWABLE_START
) -> element.getAttributeNodeNS(SCHEMA, ATTR_DRAWABLE_START)
element.hasAttributeNS(SCHEMA, ATTR_DRAWABLE_TOP) -> element.getAttributeNodeNS(
SCHEMA,
ATTR_DRAWABLE_TOP
)
else -> null
} ?: return
context.report(
issue = ISSUE_XML_SRC_USAGE,
scope = node,
location = context.getLocation(node),
message = ERROR_MESSAGE
)
}
}

View File

@ -1,6 +1,7 @@
include ':app'
include ':mozilla-detekt-rules'
include ':mozilla-lint-rules'
def log(message) {
logger.lifecycle("[settings] ${message}")
}