Lint: Rewrite Lint diagnostics using Uast.

This commit is contained in:
Yan Zhulanow
2016-04-06 16:05:50 +03:00
parent 126cc778e4
commit 4df9e9261e
210 changed files with 6176 additions and 7039 deletions
+10
View File
@@ -60,6 +60,16 @@
<element id="archive" name="kotlin-android-extensions-compiler-plugin.jar">
<element id="module-output" name="android-extensions-compiler" />
</element>
<element id="archive" name="android-lint.jar">
<element id="module-output" name="uast-kotlin" />
<element id="module-output" name="lint-idea" />
<element id="module-output" name="uast-java" />
<element id="module-output" name="uast-common" />
<element id="module-output" name="lint-checks" />
<element id="library" level="project" name="guava" />
<element id="module-output" name="lint-api" />
<element id="module-output" name="uast-android" />
</element>
</element>
<element id="directory" name="kotlinc">
<element id="dir-copy" path="$PROJECT_DIR$/dist/kotlinc" />
+8
View File
@@ -3,6 +3,7 @@
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/Kotlin.iml" filepath="$PROJECT_DIR$/Kotlin.iml" />
<module fileurl="file://$PROJECT_DIR$/plugins/lint/android-annotations/android-annotations.iml" filepath="$PROJECT_DIR$/plugins/lint/android-annotations/android-annotations.iml" />
<module fileurl="file://$PROJECT_DIR$/plugins/android-extensions/android-extensions-compiler/android-extensions-compiler.iml" filepath="$PROJECT_DIR$/plugins/android-extensions/android-extensions-compiler/android-extensions-compiler.iml" group="plugins/android-extensions" />
<module fileurl="file://$PROJECT_DIR$/plugins/android-extensions/android-extensions-idea/android-extensions-idea.iml" filepath="$PROJECT_DIR$/plugins/android-extensions/android-extensions-idea/android-extensions-idea.iml" group="plugins/android-extensions" />
<module fileurl="file://$PROJECT_DIR$/plugins/android-extensions/android-extensions-jps/android-extensions-jps.iml" filepath="$PROJECT_DIR$/plugins/android-extensions/android-extensions-jps/android-extensions-jps.iml" group="plugins/android-extensions" />
@@ -59,6 +60,9 @@
<module fileurl="file://$PROJECT_DIR$/js/js.translator/js.translator.iml" filepath="$PROJECT_DIR$/js/js.translator/js.translator.iml" group="compiler/js" />
<module fileurl="file://$PROJECT_DIR$/jps-plugin/kannotator-jps-plugin-test/kannotator-jps-plugin-test.iml" filepath="$PROJECT_DIR$/jps-plugin/kannotator-jps-plugin-test/kannotator-jps-plugin-test.iml" group="ide/jps" />
<module fileurl="file://$PROJECT_DIR$/compiler/light-classes/light-classes.iml" filepath="$PROJECT_DIR$/compiler/light-classes/light-classes.iml" group="compiler/java" />
<module fileurl="file://$PROJECT_DIR$/plugins/lint/lint-api/lint-api.iml" filepath="$PROJECT_DIR$/plugins/lint/lint-api/lint-api.iml" />
<module fileurl="file://$PROJECT_DIR$/plugins/lint/lint-checks/lint-checks.iml" filepath="$PROJECT_DIR$/plugins/lint/lint-checks/lint-checks.iml" />
<module fileurl="file://$PROJECT_DIR$/plugins/lint/lint-idea/lint-idea.iml" filepath="$PROJECT_DIR$/plugins/lint/lint-idea/lint-idea.iml" group="plugins" />
<module fileurl="file://$PROJECT_DIR$/compiler/plugin-api/plugin-api.iml" filepath="$PROJECT_DIR$/compiler/plugin-api/plugin-api.iml" group="compiler" />
<module fileurl="file://$PROJECT_DIR$/plugins/plugins-tests/plugins-tests.iml" filepath="$PROJECT_DIR$/plugins/plugins-tests/plugins-tests.iml" group="plugins" />
<module fileurl="file://$PROJECT_DIR$/compiler/preloader/preloader.iml" filepath="$PROJECT_DIR$/compiler/preloader/preloader.iml" group="compiler/cli" />
@@ -66,6 +70,10 @@
<module fileurl="file://$PROJECT_DIR$/compiler/resolution/resolution.iml" filepath="$PROJECT_DIR$/compiler/resolution/resolution.iml" group="compiler" />
<module fileurl="file://$PROJECT_DIR$/core/runtime.jvm/runtime.jvm.iml" filepath="$PROJECT_DIR$/core/runtime.jvm/runtime.jvm.iml" group="core" />
<module fileurl="file://$PROJECT_DIR$/compiler/serialization/serialization.iml" filepath="$PROJECT_DIR$/compiler/serialization/serialization.iml" group="compiler" />
<module fileurl="file://$PROJECT_DIR$/plugins/lint/uast-android/uast-android.iml" filepath="$PROJECT_DIR$/plugins/lint/uast-android/uast-android.iml" />
<module fileurl="file://$PROJECT_DIR$/plugins/uast-common/uast-common.iml" filepath="$PROJECT_DIR$/plugins/uast-common/uast-common.iml" />
<module fileurl="file://$PROJECT_DIR$/plugins/uast-java/uast-java.iml" filepath="$PROJECT_DIR$/plugins/uast-java/uast-java.iml" />
<module fileurl="file://$PROJECT_DIR$/plugins/uast-kotlin/uast-kotlin.iml" filepath="$PROJECT_DIR$/plugins/uast-kotlin/uast-kotlin.iml" group="plugins" />
<module fileurl="file://$PROJECT_DIR$/compiler/util/util.iml" filepath="$PROJECT_DIR$/compiler/util/util.iml" />
<module fileurl="file://$PROJECT_DIR$/core/util.runtime/util.runtime.iml" filepath="$PROJECT_DIR$/core/util.runtime/util.runtime.iml" group="core" />
</modules>
+1
View File
@@ -189,6 +189,7 @@ Issues fixed:
- [KT-11487](https://youtrack.jetbrains.com/issue/KT-11487) Fixed sequential build with kapt and stubs enabled when Kotlin source file was modified and no Java source files were modified
- [KT-11264](https://youtrack.jetbrains.com/issue/KT-11264) Action to create new activity in Kotlin
- [KT-11201](https://youtrack.jetbrains.com/issue/KT-11201) Do not ignore items with similar names in kapt
- [KT-7729](https://youtrack.jetbrains.com/issue/KT-7729) Add Android Lint checks for Kotlin (Android Studio 1.5)
### Maven
@@ -253,3 +253,6 @@ debugger.field.watchpoints.tab.title=Kotlin Field Watchpoints
debugger.field.watchpoints.properties.panel.field.access.label=Field &access
debugger.field.watchpoints.properties.panel.field.modification.label=Field &modification
debugger.field.watchpoints.properties.panel.field.initialization.label=Field &initialization
# Android Lint
android.klint.inspections.group.name=Android Lint for Kotlin
+212
View File
@@ -0,0 +1,212 @@
<idea-plugin>
<extensions defaultExtensionNs="com.intellij">
<externalAnnotator language="kotlin" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintExternalAnnotator"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintAaptCrash" displayName="Potential AAPT crash" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintAaptCrashInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintAccidentalOctal" displayName="Accidental Octal" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintAccidentalOctalInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintAdapterViewChildren" displayName="AdapterViews cannot have children in XML" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintAdapterViewChildrenInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintAddJavascriptInterface" displayName="addJavascriptInterface Called" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintAddJavascriptInterfaceInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintAllowBackup" displayName="Missing allowBackup attribute" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintAllowBackupInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintAlwaysShowAction" displayName="Usage of showAsAction=always" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintAlwaysShowActionInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintAppCompatMethod" displayName="Using Wrong AppCompat Method" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintAppCompatMethodInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintAppCompatResource" displayName="Menu namespace" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintAppCompatResourceInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintAppIndexingApiError" displayName="Wrong usage of AppIndexing api" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintAppIndexingApiErrorInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintAppIndexingApiWarning" displayName="Missing AppIndexing Support" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintAppIndexingApiWarningInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintAssert" displayName="Assertions" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintAssertInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintBackButton" displayName="Back button" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="false" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintBackButtonInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintButtonCase" displayName="Cancel/OK dialog button capitalization" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintButtonCaseInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintButtonOrder" displayName="Button order" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintButtonOrderInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintButtonStyle" displayName="Button should be borderless" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintButtonStyleInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintByteOrderMark" displayName="Byte order mark inside files" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintByteOrderMarkInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintCommitPrefEdits" displayName="Missing commit() on SharedPreference editor" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintCommitPrefEditsInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintCommitTransaction" displayName="Missing commit() calls" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintCommitTransactionInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintContentDescription" displayName="Image without contentDescription" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintContentDescriptionInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintCustomViewStyleable" displayName="Mismatched Styleable/Custom View Name" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintCustomViewStyleableInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintCutPasteId" displayName="Likely cut &amp; paste mistakes" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintCutPasteIdInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintDeprecated" displayName="Using deprecated resources" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintDeprecatedInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintDeviceAdmin" displayName="Malformed Device Admin" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintDeviceAdminInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintDisableBaselineAlignment" displayName="Missing baselineAligned attribute" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintDisableBaselineAlignmentInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintDrawAllocation" displayName="Memory allocations within drawing code" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintDrawAllocationInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintDuplicateActivity" displayName="Activity registered more than once" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintDuplicateActivityInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintDuplicateDefinition" displayName="Duplicate definitions of resources" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintDuplicateDefinitionInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintDuplicateIds" displayName="Duplicate ids within a single layout" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintDuplicateIdsInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintDuplicateIncludedIds" displayName="Duplicate ids across layouts combined with include tags" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintDuplicateIncludedIdsInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintDuplicateUsesFeature" displayName="Feature declared more than once" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintDuplicateUsesFeatureInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintEasterEgg" displayName="Code contains easter egg" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="false" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintEasterEggInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintEnforceUTF8" displayName="Encoding used in resource files is not UTF-8" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintEnforceUTF8Inspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintExportedContentProvider" displayName="Content provider does not require permission" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintExportedContentProviderInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintExportedPreferenceActivity" displayName="PreferenceActivity should not be exported" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintExportedPreferenceActivityInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintExportedReceiver" displayName="Receiver does not require permission" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintExportedReceiverInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintExportedService" displayName="Exported service does not require permission" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintExportedServiceInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintExtraText" displayName="Extraneous text in resource files" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintExtraTextInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintExtraTranslation" displayName="Extra translation" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintExtraTranslationInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintFullBackupContent" displayName="Valid Full Backup Content File" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintFullBackupContentInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintGetInstance" displayName="Cipher.getInstance with ECB" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintGetInstanceInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintGifUsage" displayName="Using .gif format for bitmaps is discouraged" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintGifUsageInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintGradleCompatible" displayName="Incompatible Gradle Versions" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintGradleCompatibleInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintGradleCompatiblePlugin" displayName="Incompatible Android Gradle Plugin Version" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintGradleCompatiblePluginInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintGradleDependency" displayName="Obsolete Gradle Dependency" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintGradleDependencyInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintGradleDeprecated" displayName="Deprecated Gradle Construct" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintGradleDeprecatedInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintGradleDynamicVersion" displayName="Gradle Dynamic Version" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintGradleDynamicVersionInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintGradleGetter" displayName="Gradle Implicit Getter Call" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintGradleGetterInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintGradleIdeError" displayName="Gradle IDE Support Issues" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintGradleIdeErrorInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintGradleOverrides" displayName="Value overridden by Gradle build script" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintGradleOverridesInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintGradlePath" displayName="Gradle Path Issues" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintGradlePathInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintGrantAllUris" displayName="Content provider shares everything" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintGrantAllUrisInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintGridLayout" displayName="GridLayout validation" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintGridLayoutInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintHandlerLeak" displayName="Handler reference leaks" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintHandlerLeakInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintHardcodedDebugMode" displayName="Hardcoded value of android:debuggable in the manifest" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintHardcodedDebugModeInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintHardcodedText" displayName="Hardcoded text" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintHardcodedTextInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintIconColors" displayName="Icon colors do not follow the recommended visual style" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintIconColorsInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintIconDensities" displayName="Icon densities validation" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintIconDensitiesInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintIconDipSize" displayName="Icon density-independent size validation" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintIconDipSizeInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintIconDuplicates" displayName="Duplicated icons under different names" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintIconDuplicatesInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintIconDuplicatesConfig" displayName="Identical bitmaps across various configurations" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintIconDuplicatesConfigInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintIconExpectedSize" displayName="Icon has incorrect size" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="false" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintIconExpectedSizeInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintIconExtension" displayName="Icon format does not match the file extension" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintIconExtensionInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintIconLauncherShape" displayName="The launcher icon shape should use a distinct silhouette" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintIconLauncherShapeInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintIconLocation" displayName="Image defined in density-independent drawable folder" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintIconLocationInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintIconMissingDensityFolder" displayName="Missing density folder" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintIconMissingDensityFolderInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintIconMixedNinePatch" displayName="Clashing PNG and 9-PNG files" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintIconMixedNinePatchInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintIconNoDpi" displayName="Icon appears in both -nodpi and dpi folders" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintIconNoDpiInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintIconXmlAndPng" displayName="Icon is specified both as .xml file and as a bitmap" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintIconXmlAndPngInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintIllegalResourceRef" displayName="Name and version must be integer or string, not resource" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintIllegalResourceRefInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintImpliedQuantity" displayName="Implied Quantities" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintImpliedQuantityInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintInOrMmUsage" displayName="Using mm or in dimensions" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintInOrMmUsageInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintIncludeLayoutParam" displayName="Ignored layout params on include" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintIncludeLayoutParamInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintInconsistentArrays" displayName="Inconsistencies in array element counts" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintInconsistentArraysInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintInconsistentLayout" displayName="Inconsistent Layouts" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintInconsistentLayoutInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintInefficientWeight" displayName="Inefficient layout weight" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintInefficientWeightInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintInflateParams" displayName="Layout Inflation without a Parent" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintInflateParamsInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintInlinedApi" displayName="Using inlined constants on older versions" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintInlinedApiInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintInnerclassSeparator" displayName="Inner classes should use $ rather than ." groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintInnerclassSeparatorInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintInvalidId" displayName="Invalid ID declaration" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintInvalidIdInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintInvalidResourceFolder" displayName="Invalid Resource Folder" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintInvalidResourceFolderInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintJavascriptInterface" displayName="Missing @JavascriptInterface on methods" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintJavascriptInterfaceInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintLabelFor" displayName="Missing labelFor attribute" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintLabelForInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintLibraryCustomView" displayName="Custom views in libraries should use res-auto-namespace" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintLibraryCustomViewInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintLocalSuppress" displayName="@SuppressLint on invalid element" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintLocalSuppressInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintLocaleFolder" displayName="Wrong locale name" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintLocaleFolderInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintLogConditional" displayName="Unconditional Logging Calls" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="false" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintLogConditionalInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintLogTagMismatch" displayName="Mismatched Log Tags" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintLogTagMismatchInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintLongLogTag" displayName="Too Long Log Tags" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintLongLogTagInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintMangledCRLF" displayName="Mangled file line endings" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintMangledCRLFInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintManifestOrder" displayName="Incorrect order of elements in manifest" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintManifestOrderInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintMenuTitle" displayName="Missing menu title" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintMenuTitleInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintMergeRootFrame" displayName="FrameLayout can be replaced with &lt;merge> tag" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintMergeRootFrameInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintMipmapIcons" displayName="Use Mipmap Launcher Icons" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintMipmapIconsInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintMissingApplicationIcon" displayName="Missing application icon" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintMissingApplicationIconInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintMissingId" displayName="Fragments should specify an id or tag" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintMissingIdInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintMissingPrefix" displayName="Missing Android XML namespace" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintMissingPrefixInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintMissingQuantity" displayName="Missing quantity translation" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintMissingQuantityInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintMissingSuperCall" displayName="Missing Super Call" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintMissingSuperCallInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintMissingTranslation" displayName="Incomplete translation" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintMissingTranslationInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintMissingVersion" displayName="Missing application name/version" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintMissingVersionInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintMockLocation" displayName="Using mock location provider in production" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintMockLocationInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintMultipleUsesSdk" displayName="Multiple &lt;uses-sdk> elements in the manifest" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintMultipleUsesSdkInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintNegativeMargin" displayName="Negative Margins" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="false" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintNegativeMarginInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintNestedScrolling" displayName="Nested scrolling widgets" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintNestedScrollingInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintNestedWeights" displayName="Nested layout weights" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintNestedWeightsInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintNewApi" displayName="Calling new methods on older versions" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintNewApiInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintNewerVersionAvailable" displayName="Newer Library Versions Available" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="false" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintNewerVersionAvailableInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintNfcTechWhitespace" displayName="Whitespace in NFC tech lists" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintNfcTechWhitespaceInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintNotSibling" displayName="RelativeLayout Invalid Constraints" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintNotSiblingInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintObsoleteLayoutParam" displayName="Obsolete layout params" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintObsoleteLayoutParamInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintOldTargetApi" displayName="Target SDK attribute is not targeting latest version" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintOldTargetApiInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintOrientation" displayName="Missing explicit orientation" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintOrientationInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintOverdraw" displayName="Overdraw: Painting regions more than once" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintOverdrawInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintOverride" displayName="Method conflicts with new inherited method" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintOverrideInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintOverrideAbstract" displayName="Not overriding abstract methods on older platforms" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintOverrideAbstractInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintPackagedPrivateKey" displayName="Packaged private key" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintPackagedPrivateKeyInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintPackageManagerGetSignatures" displayName="Potential Multiple Certificate Exploit" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintPackageManagerGetSignaturesInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintParcelCreator" displayName="Missing Parcelable CREATOR field" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintParcelCreatorInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintPluralsCandidate" displayName="Potential Plurals" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintPluralsCandidateInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintPrivateResource" displayName="Using private resources" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintPrivateResourceInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintProguard" displayName="Using obsolete ProGuard configuration" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintProguardInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintProguardSplit" displayName="Proguard.cfg file contains generic Android rules" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintProguardSplitInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintPropertyEscape" displayName="Incorrect property escapes" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintPropertyEscapeInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintProtectedPermissions" displayName="Using system app permission" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintProtectedPermissionsInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintPxUsage" displayName="Using &apos;px&apos; dimension" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintPxUsageInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintRecycle" displayName="Missing recycle() calls" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintRecycleInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintReferenceType" displayName="Incorrect reference types" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintReferenceTypeInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintRegistered" displayName="Class is not registered in the manifest" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintRegisteredInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintRelativeOverlap" displayName="Overlapping items in RelativeLayout" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintRelativeOverlapInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintRequiredSize" displayName="Missing layout_width or layout_height attributes" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintRequiredSizeInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintResAuto" displayName="Hardcoded Package in Namespace" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintResAutoInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintResourceCycle" displayName="Cycle in resource definitions" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintResourceCycleInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintResourceName" displayName="Resource with Wrong Prefix" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintResourceNameInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintRtlCompat" displayName="Right-to-left text compatibility issues" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintRtlCompatInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintRtlEnabled" displayName="Using RTL attributes without enabling RTL support" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintRtlEnabledInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintRtlHardcoded" displayName="Using left/right instead of start/end attributes" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintRtlHardcodedInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintRtlSymmetry" displayName="Padding and margin symmetry" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintRtlSymmetryInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintSQLiteString" displayName="Using STRING instead of TEXT" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintSQLiteStringInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintScrollViewCount" displayName="ScrollViews can have only one child" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintScrollViewCountInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintScrollViewSize" displayName="ScrollView size validation" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintScrollViewSizeInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintSdCardPath" displayName="Hardcoded reference to /sdcard" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintSdCardPathInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintSelectableText" displayName="Dynamic text should probably be selectable" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="false" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintSelectableTextInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintServiceCast" displayName="Wrong system service casts" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintServiceCastInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintSetJavaScriptEnabled" displayName="Using setJavaScriptEnabled" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintSetJavaScriptEnabledInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintShortAlarm" displayName="Short or Frequent Alarm" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintShortAlarmInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintShowToast" displayName="Toast created but not shown" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintShowToastInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintSignatureOrSystemPermissions" displayName="signatureOrSystem permissions declared" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintSignatureOrSystemPermissionsInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintSimpleDateFormat" displayName="Implied locale in date format" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintSimpleDateFormatInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintSmallSp" displayName="Text size is too small" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintSmallSpInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintSpUsage" displayName="Using dp instead of sp for text sizes" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintSpUsageInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintStateListReachable" displayName="Unreachable state in a &lt;selector>" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintStateListReachableInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintStopShip" displayName="Code contains STOPSHIP marker" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="false" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintStopShipInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintStringFormatCount" displayName="Formatting argument types incomplete or inconsistent" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintStringFormatCountInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintStringFormatInvalid" displayName="Invalid format string" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintStringFormatInvalidInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintStringFormatMatches" displayName="String.format string doesn&apos;t match the XML format string" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintStringFormatMatchesInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintStringShouldBeInt" displayName="String should be int" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintStringShouldBeIntInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintSuspicious0dp" displayName="Suspicious 0dp dimension" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintSuspicious0dpInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintSuspiciousImport" displayName="&apos;import android.R&apos; statement" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintSuspiciousImportInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintTextFields" displayName="Missing inputType or hint" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintTextFieldsInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintTextViewEdits" displayName="TextView should probably be an EditText instead" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintTextViewEditsInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintTooDeepLayout" displayName="Layout hierarchy is too deep" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintTooDeepLayoutInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintTooManyViews" displayName="Layout has too many views" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintTooManyViewsInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintTypographyDashes" displayName="Hyphen can be replaced with dash" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintTypographyDashesInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintTypographyEllipsis" displayName="Ellipsis string can be replaced with ellipsis character" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintTypographyEllipsisInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintTypographyFractions" displayName="Fraction string can be replaced with fraction character" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintTypographyFractionsInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintTypographyOther" displayName="Other typographical problems" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintTypographyOtherInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintTypographyQuotes" displayName="Straight quotes can be replaced with curvy quotes" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="false" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintTypographyQuotesInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintTypos" displayName="Spelling error" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintTyposInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintUniquePermission" displayName="Permission names are not unique" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintUniquePermissionInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintUnknownId" displayName="Reference to an unknown id" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintUnknownIdInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintUnknownIdInLayout" displayName="Reference to an id that is not in the current layout" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintUnknownIdInLayoutInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintUnlocalizedSms" displayName="SMS phone number missing country code" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintUnlocalizedSmsInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintUnusedAttribute" displayName="Attribute unused on older versions" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintUnusedAttributeInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintUnusedIds" displayName="Unused id" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="false" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintUnusedIdsInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintUnusedQuantity" displayName="Unused quantity translations" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintUnusedQuantityInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintUnusedResources" displayName="Unused resources" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintUnusedResourcesInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintUseAlpha2" displayName="Using 3-letter Codes" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintUseAlpha2Inspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintUseCompoundDrawables" displayName="Node can be replaced by a TextView with compound drawables" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintUseCompoundDrawablesInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintUseSparseArrays" displayName="HashMap can be replaced with SparseArray" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintUseSparseArraysInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintUseValueOf" displayName="Should use valueOf instead of new" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintUseValueOfInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintUselessLeaf" displayName="Useless leaf layout" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintUselessLeafInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintUselessParent" displayName="Useless parent layout" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintUselessParentInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintUsesMinSdkAttributes" displayName="Minimum SDK and target SDK attributes not defined" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintUsesMinSdkAttributesInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintUsingHttp" displayName="Using HTTP instead of HTTPS" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintUsingHttpInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintValidFragment" displayName="Fragment not instantiatable" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintValidFragmentInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintViewConstructor" displayName="Missing View constructors for XML inflation" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintViewConstructorInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintViewHolder" displayName="View Holder Candidates" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintViewHolderInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintWebViewLayout" displayName="WebViews in wrap_content parents" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintWebViewLayoutInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintWorldReadableFiles" displayName="openFileOutput() call passing MODE_WORLD_READABLE" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintWorldReadableFilesInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintWorldWriteableFiles" displayName="openFileOutput() call passing MODE_WORLD_WRITEABLE" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintWorldWriteableFilesInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintWrongCall" displayName="Using wrong draw/layout method" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintWrongCallInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintWrongCase" displayName="Wrong case for view tag" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintWrongCaseInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintWrongFolder" displayName="Resource file in the wrong res folder" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintWrongFolderInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintWrongRegion" displayName="Suspicious Language/Region Combination" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintWrongRegionInspection"/>
<globalInspection hasStaticDescription="true" shortName="AndroidLintWrongViewCast" displayName="Mismatched view type" groupKey="android.klint.inspections.group.name" bundle="org.jetbrains.kotlin.idea.KotlinBundle" enabledByDefault="true" level="ERROR" implementationClass="org.jetbrains.android.inspections.klint.AndroidLintInspectionToolProvider$AndroidLintWrongViewCastInspection"/>
</extensions>
<extensionPoints>
<extensionPoint qualifiedName="com.android.tools.lint.client.api.lintLanguageExtension"
interface="com.android.tools.klint.client.api.LintLanguageExtension"
area="IDEA_PROJECT"/>
</extensionPoints>
<extensions defaultExtensionNs="com.android.tools.lint.client.api">
<lintLanguageExtension implementation="org.jetbrains.android.inspections.klint.KotlinLintLanguageExtension"/>
<lintLanguageExtension implementation="com.android.tools.klint.client.api.JavaLintLanguageExtension"/>
</extensions>
</idea-plugin>
+3 -1
View File
@@ -1,4 +1,6 @@
<idea-plugin>
<idea-plugin xmlns:xi="http://www.w3.org/2001/XInclude">
<xi:include href="android-lint.xml" xpointer="xpointer(/idea-plugin/*)"/>
<extensions defaultExtensionNs="com.intellij">
<gotoDeclarationHandler implementation="org.jetbrains.kotlin.android.navigation.KotlinAndroidGotoDeclarationHandler"/>
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>
@@ -0,0 +1,35 @@
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.annotations;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.*;
/**
* Denotes that a parameter, field or method return value can never be null.
* <p/>
* This is a marker annotation and it has no specific attributes.
*/
@Documented
@Retention(RetentionPolicy.CLASS)
@Target({METHOD,PARAMETER,LOCAL_VARIABLE,FIELD})
public @interface NonNull {
}
@@ -0,0 +1,47 @@
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.annotations;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.PACKAGE;
import static java.lang.annotation.ElementType.TYPE;
/**
* Denotes that all parameters, fields or methods within a class or method by
* default can not be null. This can be overridden by adding specific
* {@link com.android.annotations.Nullable} annotations on fields, parameters or
* methods that should not use the default.
* <p/>
* NOTE: Eclipse does not yet handle defaults well (in particular, if
* you add this on a class which implements Comparable, then it will insist
* that your compare method is changing the nullness of the compare parameter,
* so you'll need to add @Nullable on it, which also is not right (since
* the method should have implied @NonNull and you do not need to check
* the parameter.). For now, it's best to individually annotate methods,
* parameters and fields.
* <p/>
* This is a marker annotation and it has no specific attributes.
*/
@Documented
@Retention(RetentionPolicy.CLASS)
@Target({PACKAGE, TYPE})
public @interface NonNullByDefault {
}
@@ -0,0 +1,46 @@
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.annotations;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.*;
/**
* Denotes that a parameter, field or method return value can be null.
* <b>Note</b>: this is the default assumption for most Java APIs and the
* default assumption made by most static code checking tools, so usually you
* don't need to use this annotation; its primary use is to override a default
* wider annotation like {@link NonNullByDefault}.
* <p/>
* When decorating a method call parameter, this denotes the parameter can
* legitimately be null and the method will gracefully deal with it. Typically
* used on optional parameters.
* <p/>
* When decorating a method, this denotes the method might legitimately return
* null.
* <p/>
* This is a marker annotation and it has no specific attributes.
*/
@Documented
@Retention(RetentionPolicy.CLASS)
@Target({METHOD, PARAMETER, LOCAL_VARIABLE, FIELD})
public @interface Nullable {
}
@@ -0,0 +1,50 @@
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.annotations;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/**
* Denotes that the class, method or field has its visibility relaxed so
* that unit tests can access it.
* <p/>
* The <code>visibility</code> argument can be used to specific what the original
* visibility should have been if it had not been made public or package-private for testing.
* The default is to consider the element private.
*/
@Retention(RetentionPolicy.SOURCE)
public @interface VisibleForTesting {
/**
* Intended visibility if the element had not been made public or package-private for
* testing.
*/
enum Visibility {
/** The element should be considered protected. */
PROTECTED,
/** The element should be considered package-private. */
PACKAGE,
/** The element should be considered private. */
PRIVATE
}
/**
* Intended visibility if the element had not been made public or package-private for testing.
* If not specified, one should assume the element originally intended to be private.
*/
Visibility visibility() default Visibility.PRIVATE;
}
@@ -0,0 +1,30 @@
/*
* Copyright (C) 2013 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.annotations.concurrency;
import java.lang.annotation.*;
/**
* Indicates that the target field or method should only be accessed
* with the specified lock being held.
*/
@Documented
@Retention(RetentionPolicy.CLASS)
@Target({ElementType.METHOD, ElementType.FIELD})
public @interface GuardedBy {
String value();
}
@@ -0,0 +1,29 @@
/*
* Copyright (C) 2013 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.annotations.concurrency;
import java.lang.annotation.*;
/**
* Indicates that the target class to which this annotation is applied
* is immutable.
*/
@Documented
@Retention(RetentionPolicy.CLASS)
@Target(ElementType.TYPE)
public @interface Immutable {
}
+18
View File
@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="guava" level="project" />
<orderEntry type="module" module-name="uast-common" />
<orderEntry type="module" module-name="uast-java" />
<orderEntry type="module" module-name="uast-android" />
<orderEntry type="library" name="android-plugin" level="project" />
<orderEntry type="library" name="intellij-core" level="project" />
<orderEntry type="module" module-name="android-annotations" />
</component>
</module>
@@ -14,25 +14,16 @@
* limitations under the License.
*/
package com.android.tools.lint.client.api;
package com.android.tools.klint.client.api;
import com.android.annotations.NonNull;
import com.android.tools.lint.detector.api.ClassContext;
import com.android.tools.lint.detector.api.Detector;
import com.android.tools.lint.detector.api.Detector.ClassScanner;
import com.android.tools.klint.detector.api.ClassContext;
import com.android.tools.klint.detector.api.Detector;
import com.android.tools.klint.detector.api.Detector.ClassScanner;
import com.google.common.annotations.Beta;
import org.objectweb.asm.tree.*;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.InsnList;
import org.objectweb.asm.tree.MethodInsnNode;
import org.objectweb.asm.tree.MethodNode;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.*;
/**
* Specialized visitor for running detectors on a class object model.
@@ -90,9 +81,9 @@ class AsmVisitor {
if (names != null) {
checkFullClass = false;
for (String element : names) {
List<Detector.ClassScanner> list = mMethodNameToChecks.get(element);
List<ClassScanner> list = mMethodNameToChecks.get(element);
if (list == null) {
list = new ArrayList<Detector.ClassScanner>();
list = new ArrayList<ClassScanner>();
mMethodNameToChecks.put(element, list);
}
list.add(scanner);
@@ -103,9 +94,9 @@ class AsmVisitor {
if (owners != null) {
checkFullClass = false;
for (String element : owners) {
List<Detector.ClassScanner> list = mMethodOwnerToChecks.get(element);
List<ClassScanner> list = mMethodOwnerToChecks.get(element);
if (list == null) {
list = new ArrayList<Detector.ClassScanner>();
list = new ArrayList<ClassScanner>();
mMethodOwnerToChecks.put(element, list);
}
list.add(scanner);
@@ -14,12 +14,12 @@
* limitations under the License.
*/
package com.android.tools.lint.client.api;
package com.android.tools.klint.client.api;
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
import com.android.tools.lint.detector.api.Location;
import com.android.tools.lint.detector.api.Project;
import com.android.tools.klint.detector.api.Location;
import com.android.tools.klint.detector.api.Project;
import com.google.common.annotations.Beta;
/**
@@ -14,11 +14,7 @@
* limitations under the License.
*/
package com.android.tools.lint.client.api;
import static com.android.SdkConstants.DOT_CLASS;
import static com.android.SdkConstants.DOT_JAR;
import static org.objectweb.asm.Opcodes.ASM5;
package com.android.tools.klint.client.api;
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
@@ -26,7 +22,6 @@ import com.android.annotations.VisibleForTesting;
import com.google.common.collect.Maps;
import com.google.common.io.ByteStreams;
import com.google.common.io.Closeables;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
@@ -40,6 +35,10 @@ import java.util.Map;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import static com.android.SdkConstants.DOT_CLASS;
import static com.android.SdkConstants.DOT_JAR;
import static org.objectweb.asm.Opcodes.ASM5;
/** A class, present either as a .class file on disk, or inside a .jar file. */
@VisibleForTesting
class ClassEntry implements Comparable<ClassEntry> {
@@ -13,10 +13,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.tools.lint.client.api;
package com.android.tools.klint.client.api;
import com.android.annotations.NonNull;
import com.android.tools.lint.detector.api.Issue;
import com.android.tools.klint.detector.api.Issue;
import com.google.common.collect.Lists;
import java.util.List;
@@ -14,14 +14,14 @@
* limitations under the License.
*/
package com.android.tools.lint.client.api;
package com.android.tools.klint.client.api;
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
import com.android.tools.lint.detector.api.Context;
import com.android.tools.lint.detector.api.Issue;
import com.android.tools.lint.detector.api.Location;
import com.android.tools.lint.detector.api.Severity;
import com.android.tools.klint.detector.api.Context;
import com.android.tools.klint.detector.api.Issue;
import com.android.tools.klint.detector.api.Location;
import com.android.tools.klint.detector.api.Severity;
import com.google.common.annotations.Beta;
/**
@@ -14,48 +14,32 @@
* limitations under the License.
*/
package com.android.tools.lint.client.api;
import static com.android.SdkConstants.CURRENT_PLATFORM;
import static com.android.SdkConstants.PLATFORM_WINDOWS;
package com.android.tools.klint.client.api;
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
import com.android.tools.lint.detector.api.Context;
import com.android.tools.lint.detector.api.Issue;
import com.android.tools.lint.detector.api.Location;
import com.android.tools.lint.detector.api.Project;
import com.android.tools.lint.detector.api.Severity;
import com.android.tools.lint.detector.api.TextFormat;
import com.android.tools.klint.detector.api.Context;
import com.android.tools.klint.detector.api.Issue;
import com.android.tools.klint.detector.api.Location;
import com.android.tools.klint.detector.api.Project;
import com.android.tools.klint.detector.api.Severity;
import com.android.tools.klint.detector.api.TextFormat;
import com.android.utils.XmlUtils;
import com.google.common.annotations.Beta;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Splitter;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.*;
import org.xml.sax.SAXParseException;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.io.*;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
import static com.android.SdkConstants.CURRENT_PLATFORM;
import static com.android.SdkConstants.PLATFORM_WINDOWS;
/**
* Default implementation of a {@link Configuration} which reads and writes
* configuration data into {@code lint.xml} in the project directory.
@@ -14,53 +14,7 @@
* limitations under the License.
*/
package com.android.tools.lint.client.api;
import static com.android.SdkConstants.ABSOLUTE_LAYOUT;
import static com.android.SdkConstants.ABS_LIST_VIEW;
import static com.android.SdkConstants.ABS_SEEK_BAR;
import static com.android.SdkConstants.ABS_SPINNER;
import static com.android.SdkConstants.ADAPTER_VIEW;
import static com.android.SdkConstants.AUTO_COMPLETE_TEXT_VIEW;
import static com.android.SdkConstants.BUTTON;
import static com.android.SdkConstants.CHECKABLE;
import static com.android.SdkConstants.CHECKED_TEXT_VIEW;
import static com.android.SdkConstants.CHECK_BOX;
import static com.android.SdkConstants.COMPOUND_BUTTON;
import static com.android.SdkConstants.EDIT_TEXT;
import static com.android.SdkConstants.EXPANDABLE_LIST_VIEW;
import static com.android.SdkConstants.FRAME_LAYOUT;
import static com.android.SdkConstants.GALLERY;
import static com.android.SdkConstants.GRID_VIEW;
import static com.android.SdkConstants.HORIZONTAL_SCROLL_VIEW;
import static com.android.SdkConstants.IMAGE_BUTTON;
import static com.android.SdkConstants.IMAGE_VIEW;
import static com.android.SdkConstants.LINEAR_LAYOUT;
import static com.android.SdkConstants.LIST_VIEW;
import static com.android.SdkConstants.MULTI_AUTO_COMPLETE_TEXT_VIEW;
import static com.android.SdkConstants.PROGRESS_BAR;
import static com.android.SdkConstants.RADIO_BUTTON;
import static com.android.SdkConstants.RADIO_GROUP;
import static com.android.SdkConstants.RELATIVE_LAYOUT;
import static com.android.SdkConstants.SCROLL_VIEW;
import static com.android.SdkConstants.SEEK_BAR;
import static com.android.SdkConstants.SPINNER;
import static com.android.SdkConstants.SURFACE_VIEW;
import static com.android.SdkConstants.SWITCH;
import static com.android.SdkConstants.TABLE_LAYOUT;
import static com.android.SdkConstants.TABLE_ROW;
import static com.android.SdkConstants.TAB_HOST;
import static com.android.SdkConstants.TAB_WIDGET;
import static com.android.SdkConstants.TEXT_VIEW;
import static com.android.SdkConstants.TOGGLE_BUTTON;
import static com.android.SdkConstants.VIEW;
import static com.android.SdkConstants.VIEW_ANIMATOR;
import static com.android.SdkConstants.VIEW_GROUP;
import static com.android.SdkConstants.VIEW_PKG_PREFIX;
import static com.android.SdkConstants.VIEW_STUB;
import static com.android.SdkConstants.VIEW_SWITCHER;
import static com.android.SdkConstants.WEB_VIEW;
import static com.android.SdkConstants.WIDGET_PKG_PREFIX;
package com.android.tools.klint.client.api;
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
@@ -72,6 +26,8 @@ import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import static com.android.SdkConstants.*;
/**
* Default simple implementation of an {@link SdkInfo}
* <p>
@@ -14,28 +14,21 @@
* limitations under the License.
*/
package com.android.tools.lint.client.api;
package com.android.tools.klint.client.api;
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
import com.android.annotations.VisibleForTesting;
import com.android.tools.lint.detector.api.Category;
import com.android.tools.lint.detector.api.Detector;
import com.android.tools.lint.detector.api.Implementation;
import com.android.tools.lint.detector.api.Issue;
import com.android.tools.lint.detector.api.Scope;
import com.android.tools.lint.detector.api.Severity;
import com.android.tools.klint.detector.api.Category;
import com.android.tools.klint.detector.api.Detector;
import com.android.tools.klint.detector.api.Implementation;
import com.android.tools.klint.detector.api.Issue;
import com.android.tools.klint.detector.api.Scope;
import com.android.tools.klint.detector.api.Severity;
import com.google.common.annotations.Beta;
import com.google.common.collect.Maps;
import java.util.ArrayList;
import java.util.Collections;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.*;
/**
* Registry which provides a list of checks to be performed on an Android project
@@ -13,11 +13,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.tools.lint.client.api;
package com.android.tools.klint.client.api;
import com.android.annotations.NonNull;
import com.android.tools.lint.detector.api.Issue;
import com.android.tools.lint.detector.api.Severity;
import com.android.tools.klint.detector.api.Issue;
import com.android.tools.klint.detector.api.Severity;
import com.android.utils.SdkUtils;
import com.google.common.collect.Lists;
@@ -0,0 +1,27 @@
/*
* Copyright 2010-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.tools.klint.client.api;
import org.jetbrains.uast.UastConverter;
import org.jetbrains.uast.java.JavaConverter;
public class JavaLintLanguageExtension extends LintLanguageExtension {
@Override
public UastConverter getConverter() {
return JavaConverter.INSTANCE;
}
}
@@ -14,26 +14,12 @@
* limitations under the License.
*/
package com.android.tools.lint.client.api;
import static com.android.SdkConstants.ATTR_VALUE;
package com.android.tools.klint.client.api;
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
import com.android.tools.lint.detector.api.Context;
import com.android.tools.lint.detector.api.JavaContext;
import com.android.tools.lint.detector.api.Location;
import com.android.tools.klint.detector.api.JavaContext;
import com.google.common.annotations.Beta;
import com.google.common.base.Splitter;
import java.util.Collections;
import java.util.List;
import lombok.ast.Identifier;
import lombok.ast.Node;
import lombok.ast.StrictListAccessor;
import lombok.ast.TypeReference;
import lombok.ast.TypeReferencePart;
import org.jetbrains.uast.UFile;
/**
* A wrapper for a Java parser. This allows tools integrating lint to map directly
@@ -56,475 +42,11 @@ public abstract class JavaParser {
public static final String TYPE_BYTE = "byte"; //$NON-NLS-1$
public static final String TYPE_NULL = "null"; //$NON-NLS-1$
/**
* Prepare to parse the given contexts. This method will be called before
* a series of {@link #parseJava(JavaContext)} calls, which allows some
* parsers to do up front global computation in case they want to more
* efficiently process multiple files at the same time. This allows a single
* type-attribution pass for example, which is a lot more efficient than
* performing global type analysis over and over again for each individual
* file
*
* @param contexts a list of contexts to be parsed
*/
public abstract void prepareJavaParse(@NonNull List<JavaContext> contexts);
/**
* Parse the file pointed to by the given context.
*
* @param context the context pointing to the file to be parsed, typically
* via {@link Context#getContents()} but the file handle (
* {@link Context#file} can also be used to map to an existing
* editor buffer in the surrounding tool, etc)
* @return the compilation unit node for the file
*/
@Nullable
public abstract Node parseJava(@NonNull JavaContext context);
/**
* Returns a {@link Location} for the given node
*
* @param context information about the file being parsed
* @param node the node to create a location for
* @return a location for the given node
*/
@NonNull
public abstract Location getLocation(@NonNull JavaContext context, @NonNull Node node);
/**
* Creates a light-weight handle to a location for the given node. It can be
* turned into a full fledged location by
* {@link com.android.tools.lint.detector.api.Location.Handle#resolve()}.
*
* @param context the context providing the node
* @param node the node (element or attribute) to create a location handle
* for
* @return a location handle
*/
@NonNull
public abstract Location.Handle createLocationHandle(@NonNull JavaContext context,
@NonNull Node node);
/**
* Dispose any data structures held for the given context.
* @param context information about the file previously parsed
* @param compilationUnit the compilation unit being disposed
*/
public void dispose(@NonNull JavaContext context, @NonNull Node compilationUnit) {
}
/**
* Dispose any remaining data structures held for all contexts.
* Typically frees up any resources allocated by
* {@link #prepareJavaParse(List)}
*/
public void dispose() {
}
/**
* Resolves the given expression node: computes the declaration for the given symbol
*
* @param context information about the file being parsed
* @param node the node to resolve
* @return a node representing the resolved fully type: class/interface/annotation,
* field, method or variable
*/
@Nullable
public abstract ResolvedNode resolve(@NonNull JavaContext context, @NonNull Node node);
/**
* Finds the given type, if possible (which should be reachable from the compilation
* patch of the given node.
*
* @param context information about the file being parsed
* @param fullyQualifiedName the fully qualified name of the class to look up
* @return the class, or null if not found
*/
@Nullable
public ResolvedClass findClass(
@NonNull JavaContext context,
@NonNull String fullyQualifiedName) {
return null;
}
/**
* Gets the type of the given node
*
* @param context information about the file being parsed
* @param node the node to look up the type for
* @return the type of the node, if known
*/
@Nullable
public abstract TypeDescriptor getType(@NonNull JavaContext context, @NonNull Node node);
/** A description of a type, such as a primitive int or the android.app.Activity class */
public abstract static class TypeDescriptor {
/**
* Returns the fully qualified name of the type, such as "int" or "android.app.Activity"
* */
@NonNull public abstract String getName();
/**
* Returns the full signature of the type, which is normally the same as {@link #getName()}
* but for arrays can include []'s, for generic methods can include generics parameters
* etc
*/
@NonNull public abstract String getSignature();
public abstract boolean matchesName(@NonNull String name);
public abstract boolean matchesSignature(@NonNull String signature);
@NonNull
public TypeReference getNode() {
TypeReference typeReference = new TypeReference();
StrictListAccessor<TypeReferencePart, TypeReference> parts = typeReference.astParts();
for (String part : Splitter.on('.').split(getName())) {
Identifier identifier = Identifier.of(part);
parts.addToEnd(new TypeReferencePart().astIdentifier(identifier));
}
return typeReference;
}
/** If the type is not primitive, returns the class of the type if known */
@Nullable
public abstract ResolvedClass getTypeClass();
@Override
public abstract boolean equals(Object o);
}
/** Convenience implementation of {@link TypeDescriptor} */
public static class DefaultTypeDescriptor extends TypeDescriptor {
private String mName;
public DefaultTypeDescriptor(String name) {
mName = name;
}
@NonNull
@Override
public String getName() {
return mName;
}
@NonNull
@Override
public String getSignature() {
return getName();
}
@Override
public boolean matchesName(@NonNull String name) {
return mName.equals(name);
}
@Override
public boolean matchesSignature(@NonNull String signature) {
return matchesName(signature);
}
@Override
public String toString() {
return getSignature();
}
@Override
@Nullable
public ResolvedClass getTypeClass() {
return null;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
DefaultTypeDescriptor that = (DefaultTypeDescriptor) o;
return !(mName != null ? !mName.equals(that.mName) : that.mName != null);
}
@Override
public int hashCode() {
return mName != null ? mName.hashCode() : 0;
}
}
/** A resolved declaration from an AST Node reference */
public abstract static class ResolvedNode {
@NonNull
public abstract String getName();
/** Returns the signature of the resolved node */
public abstract String getSignature();
public abstract int getModifiers();
@Override
public String toString() {
return getSignature();
}
/** Returns any annotations defined on this node */
@NonNull
public abstract Iterable<ResolvedAnnotation> getAnnotations();
/**
* Searches for the annotation of the given type on this node
*
* @param type the fully qualified name of the annotation to check
* @return the annotation, or null if not found
*/
@Nullable
public ResolvedAnnotation getAnnotation(@NonNull String type) {
for (ResolvedAnnotation annotation : getAnnotations()) {
if (annotation.getType().matchesSignature(type)) {
return annotation;
}
}
return null;
}
}
/** A resolved class declaration (class, interface, enumeration or annotation) */
public abstract static class ResolvedClass extends ResolvedNode {
/** Returns the fully qualified name of this class */
@Override
@NonNull
public abstract String getName();
/** Returns the simple of this class */
@NonNull
public abstract String getSimpleName();
/** Returns the package name of this class */
@NonNull
public String getPackageName() {
String name = getName();
String simpleName = getSimpleName();
if (name.length() > simpleName.length() + 1) {
return name.substring(0, name.length() - simpleName.length() - 1);
}
return name;
}
/** Returns whether this class' fully qualified name matches the given name */
public abstract boolean matches(@NonNull String name);
@Nullable
public abstract ResolvedClass getSuperClass();
@Nullable
public abstract ResolvedClass getContainingClass();
public TypeDescriptor getType() {
return new DefaultTypeDescriptor(getName());
}
/**
* Determines whether this class extends the given name. If strict is true,
* it will not consider C extends C true.
*
* @param name the fully qualified class name
* @param strict if true, do not consider a class to be extending itself
* @return true if this class extends the given class
*/
public abstract boolean isSubclassOf(@NonNull String name, boolean strict);
@NonNull
public abstract Iterable<ResolvedMethod> getConstructors();
/** Returns the methods defined in this class, and optionally any methods inherited from any superclasses as well */
@NonNull
public abstract Iterable<ResolvedMethod> getMethods(boolean includeInherited);
/** Returns the methods of a given name defined in this class, and optionally any methods inherited from any superclasses as well */
@NonNull
public abstract Iterable<ResolvedMethod> getMethods(@NonNull String name, boolean includeInherited);
/** Returns the named field defined in this class, or optionally inherited from a superclass */
@Nullable
public abstract ResolvedField getField(@NonNull String name, boolean includeInherited);
}
/** A method or constructor declaration */
public abstract static class ResolvedMethod extends ResolvedNode {
@Override
@NonNull
public abstract String getName();
/** Returns whether this method name matches the given name */
public abstract boolean matches(@NonNull String name);
@NonNull
public abstract ResolvedClass getContainingClass();
public abstract int getArgumentCount();
@NonNull
public abstract TypeDescriptor getArgumentType(int index);
/** Returns true if the parameter at the given index matches the given type signature */
public boolean argumentMatchesType(int index, @NonNull String signature) {
return getArgumentType(index).matchesSignature(signature);
}
@Nullable
public abstract TypeDescriptor getReturnType();
public boolean isConstructor() {
return getReturnType() == null;
}
/** Returns any annotations defined on the given parameter of this method */
@NonNull
public abstract Iterable<ResolvedAnnotation> getParameterAnnotations(int index);
/**
* Searches for the annotation of the given type on the method
*
* @param type the fully qualified name of the annotation to check
* @param parameterIndex the index of the parameter to look up
* @return the annotation, or null if not found
*/
@Nullable
public ResolvedAnnotation getParameterAnnotation(@NonNull String type,
int parameterIndex) {
for (ResolvedAnnotation annotation : getParameterAnnotations(parameterIndex)) {
if (annotation.getType().matchesSignature(type)) {
return annotation;
}
}
return null;
}
/** Returns the super implementation of the given method, if any */
@Nullable
public ResolvedMethod getSuperMethod() {
ResolvedClass cls = getContainingClass().getSuperClass();
if (cls != null) {
String methodName = getName();
int argCount = getArgumentCount();
for (ResolvedMethod method : cls.getMethods(methodName, true)) {
if (argCount != method.getArgumentCount()) {
continue;
}
boolean sameTypes = true;
for (int arg = 0; arg < argCount; arg++) {
if (!method.getArgumentType(arg).equals(getArgumentType(arg))) {
sameTypes = false;
break;
}
}
if (sameTypes) {
return method;
}
}
}
return null;
}
}
/** A field declaration */
public abstract static class ResolvedField extends ResolvedNode {
@Override
@NonNull
public abstract String getName();
/** Returns whether this field name matches the given name */
public abstract boolean matches(@NonNull String name);
@NonNull
public abstract TypeDescriptor getType();
@NonNull
public abstract ResolvedClass getContainingClass();
@Nullable
public abstract Object getValue();
public String getContainingClassName() {
return getContainingClass().getName();
}
}
/**
* An annotation <b>reference</b>. Note that this refers to a usage of an annotation,
* not a declaraton of an annotation. You can call {@link #getClassType()} to
* find the declaration for the annotation.
*/
public abstract static class ResolvedAnnotation extends ResolvedNode {
@Override
@NonNull
public abstract String getName();
/** Returns whether this field name matches the given name */
public abstract boolean matches(@NonNull String name);
@NonNull
public abstract TypeDescriptor getType();
/** Returns the {@link ResolvedClass} which defines the annotation */
@Nullable
public abstract ResolvedClass getClassType();
public static class Value {
@NonNull public final String name;
@Nullable public final Object value;
public Value(@NonNull String name, @Nullable Object value) {
this.name = name;
this.value = value;
}
}
@NonNull
public abstract List<Value> getValues();
@Nullable
public Object getValue(@NonNull String name) {
for (Value value : getValues()) {
if (name.equals(value.name)) {
return value.value;
}
}
return null;
}
@Nullable
public Object getValue() {
return getValue(ATTR_VALUE);
}
@NonNull
@Override
public Iterable<ResolvedAnnotation> getAnnotations() {
return Collections.emptyList();
}
}
/** A local variable or parameter declaration */
public abstract static class ResolvedVariable extends ResolvedNode {
@Override
@NonNull
public abstract String getName();
/** Returns whether this variable name matches the given name */
public abstract boolean matches(@NonNull String name);
@NonNull
public abstract TypeDescriptor getType();
public void dispose(@NonNull JavaContext context, @NonNull UFile compilationUnit) {
}
}
@@ -14,16 +14,7 @@
* limitations under the License.
*/
package com.android.tools.lint.client.api;
import static com.android.SdkConstants.CLASS_FOLDER;
import static com.android.SdkConstants.DOT_AAR;
import static com.android.SdkConstants.DOT_JAR;
import static com.android.SdkConstants.GEN_FOLDER;
import static com.android.SdkConstants.LIBS_FOLDER;
import static com.android.SdkConstants.RES_FOLDER;
import static com.android.SdkConstants.SRC_FOLDER;
import static com.android.tools.lint.detector.api.LintUtils.endsWith;
package com.android.tools.klint.client.api;
import com.android.SdkConstants;
import com.android.annotations.NonNull;
@@ -35,21 +26,21 @@ import com.android.prefs.AndroidLocation;
import com.android.sdklib.IAndroidTarget;
import com.android.sdklib.SdkVersionInfo;
import com.android.sdklib.repository.local.LocalSdk;
import com.android.tools.lint.detector.api.Context;
import com.android.tools.lint.detector.api.Detector;
import com.android.tools.lint.detector.api.Issue;
import com.android.tools.lint.detector.api.LintUtils;
import com.android.tools.lint.detector.api.Location;
import com.android.tools.lint.detector.api.Project;
import com.android.tools.lint.detector.api.Severity;
import com.android.tools.lint.detector.api.TextFormat;
import com.android.tools.klint.detector.api.Context;
import com.android.tools.klint.detector.api.Detector;
import com.android.tools.klint.detector.api.Issue;
import com.android.tools.klint.detector.api.LintUtils;
import com.android.tools.klint.detector.api.Location;
import com.android.tools.klint.detector.api.Project;
import com.android.tools.klint.detector.api.Severity;
import com.android.tools.klint.detector.api.TextFormat;
import com.android.utils.XmlUtils;
import com.google.common.annotations.Beta;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.google.common.io.Files;
import org.jetbrains.uast.UastConverter;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
@@ -59,13 +50,10 @@ import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.*;
import static com.android.SdkConstants.*;
import static com.android.tools.klint.detector.api.LintUtils.endsWith;
/**
* Information about the tool embedding the lint analyzer. IDEs and other tools
@@ -153,19 +141,6 @@ public abstract class LintClient {
@Nullable
public abstract XmlParser getXmlParser();
/**
* Returns a {@link JavaParser} to use to parse Java
*
* @param project the project to parse, if known (this can be used to look up
* the class path for type attribution etc, and it can also be used
* to more efficiently process a set of files, for example to
* perform type attribution for multiple units in a single pass)
* @return a new {@link JavaParser}, or null if this client does not
* support Java analysis
*/
@Nullable
public abstract JavaParser getJavaParser(@Nullable Project project);
/**
* Returns an optimal detector, if applicable. By default, just returns the
* original detector, but tools can replace detectors using this hook with a version
@@ -406,6 +381,15 @@ public abstract class LintClient {
return false;
}
@Nullable
public com.intellij.openapi.project.Project getProject() {
return null;
}
public List<UastConverter> getConverters() {
return Collections.emptyList();
}
/**
* Information about class paths (sources, class files and libraries)
* usually associated with a project.
@@ -14,24 +14,7 @@
* limitations under the License.
*/
package com.android.tools.lint.client.api;
import static com.android.SdkConstants.ATTR_IGNORE;
import static com.android.SdkConstants.CLASS_CONSTRUCTOR;
import static com.android.SdkConstants.CONSTRUCTOR_NAME;
import static com.android.SdkConstants.DOT_CLASS;
import static com.android.SdkConstants.DOT_JAR;
import static com.android.SdkConstants.DOT_JAVA;
import static com.android.SdkConstants.FD_GRADLE_WRAPPER;
import static com.android.SdkConstants.FN_GRADLE_WRAPPER_PROPERTIES;
import static com.android.SdkConstants.FN_LOCAL_PROPERTIES;
import static com.android.SdkConstants.RES_FOLDER;
import static com.android.SdkConstants.SUPPRESS_ALL;
import static com.android.SdkConstants.SUPPRESS_LINT;
import static com.android.SdkConstants.TOOLS_URI;
import static com.android.ide.common.resources.configuration.FolderConfiguration.QUALIFIER_SPLITTER;
import static com.android.tools.lint.detector.api.LintUtils.isAnonymousClass;
import static java.io.File.separator;
package com.android.tools.klint.client.api;
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
@@ -40,79 +23,47 @@ import com.android.ide.common.res2.ResourceItem;
import com.android.resources.ResourceFolderType;
import com.android.sdklib.IAndroidTarget;
import com.android.sdklib.repository.local.LocalSdk;
import com.android.tools.lint.client.api.LintListener.EventType;
import com.android.tools.lint.detector.api.ClassContext;
import com.android.tools.lint.detector.api.Context;
import com.android.tools.lint.detector.api.Detector;
import com.android.tools.lint.detector.api.Issue;
import com.android.tools.lint.detector.api.JavaContext;
import com.android.tools.lint.detector.api.LintUtils;
import com.android.tools.lint.detector.api.Location;
import com.android.tools.lint.detector.api.Project;
import com.android.tools.lint.detector.api.ResourceContext;
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.TextFormat;
import com.android.tools.lint.detector.api.XmlContext;
import com.android.tools.klint.detector.api.ClassContext;
import com.android.tools.klint.detector.api.Context;
import com.android.tools.klint.detector.api.Detector;
import com.android.tools.klint.detector.api.Issue;
import com.android.tools.klint.detector.api.JavaContext;
import com.android.tools.klint.detector.api.LintUtils;
import com.android.tools.klint.detector.api.Location;
import com.android.tools.klint.detector.api.Project;
import com.android.tools.klint.detector.api.ResourceContext;
import com.android.tools.klint.detector.api.ResourceXmlDetector;
import com.android.tools.klint.detector.api.Scope;
import com.android.tools.klint.detector.api.Severity;
import com.android.tools.klint.detector.api.TextFormat;
import com.android.tools.klint.detector.api.XmlContext;
import com.google.common.annotations.Beta;
import com.google.common.base.Objects;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Multimap;
import com.google.common.collect.Sets;
import com.google.common.collect.*;
import com.sun.istack.internal.NotNull;
import org.jetbrains.uast.*;
import org.jetbrains.uast.check.UastChecker;
import org.jetbrains.uast.check.UastScanner;
import org.jetbrains.uast.java.JavaUastCallKinds;
import org.jetbrains.uast.visitor.UastVisitor;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.AnnotationNode;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.FieldInsnNode;
import org.objectweb.asm.tree.FieldNode;
import org.objectweb.asm.tree.InsnList;
import org.objectweb.asm.tree.MethodInsnNode;
import org.objectweb.asm.tree.MethodNode;
import org.objectweb.asm.tree.*;
import org.w3c.dom.Attr;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Deque;
import java.util.EnumMap;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import lombok.ast.Annotation;
import lombok.ast.AnnotationElement;
import lombok.ast.AnnotationValue;
import lombok.ast.ArrayInitializer;
import lombok.ast.ClassDeclaration;
import lombok.ast.ConstructorDeclaration;
import lombok.ast.Expression;
import lombok.ast.MethodDeclaration;
import lombok.ast.Modifiers;
import lombok.ast.Node;
import lombok.ast.StrictListAccessor;
import lombok.ast.StringLiteral;
import lombok.ast.TypeReference;
import lombok.ast.VariableDefinition;
import static com.android.SdkConstants.*;
import static com.android.ide.common.resources.configuration.FolderConfiguration.QUALIFIER_SPLITTER;
import static java.io.File.separator;
/**
* Analyzes Android projects and files
@@ -415,7 +366,7 @@ public class LintDriver {
mScope = Scope.infer(projects);
}
fireEvent(EventType.STARTING, null);
fireEvent(LintListener.EventType.STARTING, null);
for (Project project : projects) {
mPhase = 1;
@@ -438,7 +389,7 @@ public class LintDriver {
runExtraPhases(project, main);
}
fireEvent(mCanceled ? EventType.CANCELED : EventType.COMPLETED, null);
fireEvent(mCanceled ? LintListener.EventType.CANCELED : LintListener.EventType.COMPLETED, null);
}
private void registerCustomRules(Collection<Project> projects) {
@@ -484,7 +435,7 @@ public class LintDriver {
do {
mPhase++;
fireEvent(EventType.NEW_PHASE,
fireEvent(LintListener.EventType.NEW_PHASE,
new Context(this, project, null, project.getDir()));
// Narrow the scope down to the set of scopes requested by
@@ -629,16 +580,16 @@ public class LintDriver {
assert detector instanceof Detector.XmlScanner : detector;
}
}
List<Detector> javaCodeDetectors = mScopeDetectors.get(Scope.ALL_JAVA_FILES);
List<Detector> javaCodeDetectors = mScopeDetectors.get(Scope.ALL_SOURCE_FILES);
if (javaCodeDetectors != null) {
for (Detector detector : javaCodeDetectors) {
assert detector instanceof Detector.JavaScanner : detector;
assert (detector instanceof UastScanner) : detector;
}
}
List<Detector> javaFileDetectors = mScopeDetectors.get(Scope.JAVA_FILE);
List<Detector> javaFileDetectors = mScopeDetectors.get(Scope.SOURCE_FILE);
if (javaFileDetectors != null) {
for (Detector detector : javaFileDetectors) {
assert detector instanceof Detector.JavaScanner : detector;
assert (detector instanceof UastScanner) : detector;
}
}
@@ -861,7 +812,7 @@ public class LintDriver {
File projectDir = project.getDir();
Context projectContext = new Context(this, project, null, projectDir);
fireEvent(EventType.SCANNING_PROJECT, projectContext);
fireEvent(LintListener.EventType.SCANNING_PROJECT, projectContext);
List<Project> allLibraries = project.getAllLibraries();
Set<Project> allProjects = new HashSet<Project>(allLibraries.size() + 1);
@@ -885,7 +836,7 @@ public class LintDriver {
List<Project> libraries = project.getAllLibraries();
for (Project library : libraries) {
Context libraryContext = new Context(this, library, project, projectDir);
fireEvent(EventType.SCANNING_LIBRARY_PROJECT, libraryContext);
fireEvent(LintListener.EventType.SCANNING_LIBRARY_PROJECT, libraryContext);
mCurrentProject = library;
for (Detector check : mApplicableDetectors) {
@@ -954,7 +905,7 @@ public class LintDriver {
if (detectors != null) {
ResourceVisitor v = new ResourceVisitor(parser, detectors,
null);
fireEvent(EventType.SCANNING_FILE, context);
fireEvent(LintListener.EventType.SCANNING_FILE, context);
v.visitFile(context, manifestFile);
}
}
@@ -1014,9 +965,9 @@ public class LintDriver {
}
}
if (mScope.contains(Scope.JAVA_FILE) || mScope.contains(Scope.ALL_JAVA_FILES)) {
List<Detector> checks = union(mScopeDetectors.get(Scope.JAVA_FILE),
mScopeDetectors.get(Scope.ALL_JAVA_FILES));
if (mScope.contains(Scope.SOURCE_FILE) || mScope.contains(Scope.ALL_SOURCE_FILES)) {
List<Detector> checks = union(mScopeDetectors.get(Scope.SOURCE_FILE),
mScopeDetectors.get(Scope.ALL_SOURCE_FILES));
if (checks != null && !checks.isEmpty()) {
List<File> files = project.getSubset();
if (files != null) {
@@ -1111,7 +1062,7 @@ public class LintDriver {
List<File> files = project.getProguardFiles();
for (File file : files) {
Context context = new Context(this, project, main, file);
fireEvent(EventType.SCANNING_FILE, context);
fireEvent(LintListener.EventType.SCANNING_FILE, context);
for (Detector detector : detectors) {
if (detector.appliesTo(context, file)) {
detector.beforeCheckFile(context);
@@ -1137,7 +1088,7 @@ public class LintDriver {
File file = new File(project.getDir(), relativePath);
if (file.exists()) {
Context context = new Context(this, project, main, file);
fireEvent(EventType.SCANNING_FILE, context);
fireEvent(LintListener.EventType.SCANNING_FILE, context);
for (Detector detector : detectors) {
if (detector.appliesTo(context, file)) {
detector.beforeCheckFile(context);
@@ -1476,12 +1427,6 @@ public class LintDriver {
@Nullable Project main,
@NonNull List<File> sourceFolders,
@NonNull List<Detector> checks) {
JavaParser javaParser = mClient.getJavaParser(project);
if (javaParser == null) {
mClient.log(null, "No java parser provided to lint: not running Java checks");
return;
}
assert !checks.isEmpty();
// Gather all Java source files in a single pass; more efficient.
@@ -1490,22 +1435,39 @@ public class LintDriver {
gatherJavaFiles(folder, sources);
}
if (!sources.isEmpty()) {
JavaVisitor visitor = new JavaVisitor(javaParser, checks);
List<JavaContext> contexts = Lists.newArrayListWithExpectedSize(sources.size());
for (File file : sources) {
JavaContext context = new JavaContext(this, project, main, file, javaParser);
contexts.add(context);
}
visitor.prepare(contexts);
com.intellij.openapi.project.Project ideaProject = mClient.getProject();
if (ideaProject == null) {
return;
}
List<UastConverter> converters = mClient.getConverters();
for (JavaContext context : contexts) {
fireEvent(EventType.SCANNING_FILE, context);
visitor.visitFile(context);
fireEvent(LintListener.EventType.SCANNING_FILE, context);
for (Detector check : checks) {
if (check instanceof UastScanner) {
UastScanner scanner = (UastScanner) check;
UastVisitor customVisitor = scanner.createUastVisitor(context);
if (customVisitor != null) {
UastChecker.INSTANCE.checkWithCustomHandler(
ideaProject, context.file, converters, customVisitor);
} else {
UastChecker.INSTANCE.check(
ideaProject, context.file, (UastScanner)check, converters, context);
}
}
}
if (mCanceled) {
return;
}
}
visitor.dispose();
}
}
@@ -1514,41 +1476,52 @@ public class LintDriver {
@Nullable Project main,
@NonNull List<Detector> checks,
@NonNull List<File> files) {
List<UastScanner> uastDetectors = new ArrayList<UastScanner>();
JavaParser javaParser = mClient.getJavaParser(project);
if (javaParser == null) {
mClient.log(null, "No java parser provided to lint: not running Java checks");
for (Detector check : checks) {
if (check instanceof UastScanner) {
uastDetectors.add((UastScanner) check);
}
}
checkWithUastDetectors(project, main, uastDetectors, files);
}
private void checkWithUastDetectors(
@NotNull Project project,
@Nullable Project main,
@NotNull List<UastScanner> detectors,
@NotNull List<File> files) {
com.intellij.openapi.project.Project intellijProject = mClient.getProject();
if (intellijProject == null) {
return;
}
JavaVisitor visitor = new JavaVisitor(javaParser, checks);
UastChecker checker = UastChecker.INSTANCE;
List<UastConverter> converters = project.getClient().getConverters();
List<JavaContext> contexts = Lists.newArrayListWithExpectedSize(files.size());
for (File file : files) {
if (file.isFile() && file.getPath().endsWith(DOT_JAVA)) {
contexts.add(new JavaContext(this, project, main, file, javaParser));
if (!file.isFile()) {
continue;
}
String path = file.getPath();
if (!path.endsWith(DOT_JAVA) && !UastConverterUtils.isFileSupported(converters, path)) {
continue;
}
JavaContext
context = new JavaContext(this, project, main, file);
for (UastScanner detector : detectors) {
UastVisitor customHandler = detector.createUastVisitor(context);
if (customHandler != null) {
checker.checkWithCustomHandler(intellijProject, file, converters, customHandler);
} else {
checker.check(intellijProject, file, detector, converters, context);
}
}
}
if (contexts.isEmpty()) {
return;
}
visitor.prepare(contexts);
if (mCanceled) {
return;
}
for (JavaContext context : contexts) {
fireEvent(EventType.SCANNING_FILE, context);
visitor.visitFile(context);
if (mCanceled) {
return;
}
}
visitor.dispose();
}
private static void gatherJavaFiles(@NonNull File dir, @NonNull List<File> result) {
@@ -1666,7 +1639,7 @@ public class LintDriver {
if (dirChecks != null && !dirChecks.isEmpty()) {
ResourceContext context = new ResourceContext(this, project, main, dir, type);
String folderName = dir.getName();
fireEvent(EventType.SCANNING_FILE, context);
fireEvent(LintListener.EventType.SCANNING_FILE, context);
for (Detector check : dirChecks) {
if (check.appliesTo(type)) {
check.beforeCheckFile(context);
@@ -1693,11 +1666,11 @@ public class LintDriver {
if (LintUtils.isXmlFile(file)) {
XmlContext context = new XmlContext(this, project, main, file, type,
visitor.getParser());
fireEvent(EventType.SCANNING_FILE, context);
fireEvent(LintListener.EventType.SCANNING_FILE, context);
visitor.visitFile(context, file);
} else if (binaryChecks != null && LintUtils.isBitmapFile(file)) {
ResourceContext context = new ResourceContext(this, project, main, file, type);
fireEvent(EventType.SCANNING_FILE, context);
fireEvent(LintListener.EventType.SCANNING_FILE, context);
visitor.visitBinaryResource(context);
}
if (mCanceled) {
@@ -1739,7 +1712,7 @@ public class LintDriver {
if (visitor != null) {
XmlContext context = new XmlContext(this, project, main, file, type,
visitor.getParser());
fireEvent(EventType.SCANNING_FILE, context);
fireEvent(LintListener.EventType.SCANNING_FILE, context);
visitor.visitFile(context, file);
}
}
@@ -1752,7 +1725,7 @@ public class LintDriver {
if (visitor != null) {
ResourceContext context = new ResourceContext(this, project, main, file,
type);
fireEvent(EventType.SCANNING_FILE, context);
fireEvent(LintListener.EventType.SCANNING_FILE, context);
visitor.visitBinaryResource(context);
if (mCanceled) {
return;
@@ -1920,12 +1893,6 @@ public class LintDriver {
return mDelegate.getProject(dir, referenceDir);
}
@Nullable
@Override
public JavaParser getJavaParser(@Nullable Project project) {
return mDelegate.getJavaParser(project);
}
@Override
public File findResource(@NonNull String relativePath) {
return mDelegate.findResource(relativePath);
@@ -1939,7 +1906,7 @@ public class LintDriver {
@Override
@NonNull
protected ClassPathInfo getClassPath(@NonNull Project project) {
protected LintClient.ClassPathInfo getClassPath(@NonNull Project project) {
return mDelegate.getClassPath(project);
}
@@ -2069,6 +2036,17 @@ public class LintDriver {
public void closeConnection(@NonNull URLConnection connection) throws IOException {
mDelegate.closeConnection(connection);
}
@Override
public com.intellij.openapi.project.Project getProject() {
return mDelegate.getProject();
}
@Override
public List<UastConverter> getConverters() {
return mDelegate.getConverters();
}
}
/**
@@ -2140,7 +2118,7 @@ public class LintDriver {
return true;
}
} else if (classNode.outerClass != null && classNode.outerMethod == null
&& isAnonymousClass(classNode)) {
&& LintUtils.isAnonymousClass(classNode)) {
if (isSuppressed(issue, classNode)) {
return true;
}
@@ -2251,7 +2229,7 @@ public class LintDriver {
}
if (classNode.outerClass != null && classNode.outerMethod == null
&& isAnonymousClass(classNode)) {
&& LintUtils.isAnonymousClass(classNode)) {
ClassNode outer = getOuterClassNode(classNode);
if (outer != null) {
MethodNode m = findMethod(outer, CONSTRUCTOR_NAME, false);
@@ -2347,44 +2325,31 @@ public class LintDriver {
* issue in this class
*/
public boolean isSuppressed(@Nullable JavaContext context, @NonNull Issue issue,
@Nullable Node scope) {
@Nullable UElement scope) {
boolean checkComments = mClient.checkForSuppressComments() &&
context != null && context.containsCommentSuppress();
context != null && context.containsCommentSuppress();
while (scope != null) {
Class<? extends Node> type = scope.getClass();
// The Lombok AST uses a flat hierarchy of node type implementation classes
// so no need to do instanceof stuff here.
if (type == VariableDefinition.class) {
// Variable
VariableDefinition declaration = (VariableDefinition) scope;
if (isSuppressed(issue, declaration.astModifiers())) {
if (scope instanceof UVariable) {
UVariable declaration = (UVariable) scope;
if (isUastSuppressed(issue, declaration.getAnnotations())) {
return true;
}
} else if (type == MethodDeclaration.class) {
// Method
// Look for annotations on the method
MethodDeclaration declaration = (MethodDeclaration) scope;
if (isSuppressed(issue, declaration.astModifiers())) {
} else if (scope instanceof UFunction) {
UFunction declaration = (UFunction) scope;
if (isUastSuppressed(issue, declaration.getAnnotations())) {
return true;
}
} else if (type == ConstructorDeclaration.class) {
// Constructor
// Look for annotations on the method
ConstructorDeclaration declaration = (ConstructorDeclaration) scope;
if (isSuppressed(issue, declaration.astModifiers())) {
return true;
}
} else if (type == ClassDeclaration.class) {
// Class
ClassDeclaration declaration = (ClassDeclaration) scope;
if (isSuppressed(issue, declaration.astModifiers())) {
} else if (scope instanceof UClass) {
UClass declaration = (UClass) scope;
if (isUastSuppressed(issue, declaration.getAnnotations())) {
return true;
}
}
if (checkComments && context.isSuppressedWithComment(scope, issue)) {
return true;
}
//TODO check comments
//if (checkComments && context.isSuppressedWithComment(scope, issue)) {
// return true;
//}
scope = scope.getParent();
}
@@ -2401,47 +2366,27 @@ public class LintDriver {
* @return true if the issue or all issues should be suppressed for this
* modifier
*/
private static boolean isSuppressed(@Nullable Issue issue, @Nullable Modifiers modifiers) {
if (modifiers == null) {
return false;
}
StrictListAccessor<Annotation, Modifiers> annotations = modifiers.astAnnotations();
if (annotations == null) {
return false;
}
for (Annotation annotation : annotations) {
TypeReference t = annotation.astAnnotationTypeReference();
String typeName = t.getTypeName();
if (typeName.endsWith(SUPPRESS_LINT)
|| typeName.endsWith("SuppressWarnings")) { //$NON-NLS-1$
StrictListAccessor<AnnotationElement, Annotation> values =
annotation.astElements();
if (values != null) {
for (AnnotationElement element : values) {
AnnotationValue valueNode = element.astValue();
if (valueNode == null) {
continue;
private static boolean isUastSuppressed(@Nullable Issue issue, @Nullable List<UAnnotation> annotations) {
for (UAnnotation annotation : annotations) {
if (annotation.matchesName(SUPPRESS_LINT)
|| annotation.matchesName("SuppressWarnings")) { //$NON-NLS-1$
List<UNamedExpression> values = annotation.getValueArguments();
for (UNamedExpression element : values) {
UExpression valueNode = element.getExpression();
String value = UastLiteralUtils.getValueIfStringLiteral(valueNode);
if (value != null) {
if (matches(issue, value)) {
return true;
}
if (valueNode instanceof StringLiteral) {
StringLiteral literal = (StringLiteral) valueNode;
String value = literal.astValue();
if (matches(issue, value)) {
return true;
}
} else if (valueNode instanceof ArrayInitializer) {
ArrayInitializer array = (ArrayInitializer) valueNode;
StrictListAccessor<Expression, ArrayInitializer> expressions =
array.astExpressions();
if (expressions == null) {
continue;
}
for (Expression arrayElement : expressions) {
if (arrayElement instanceof StringLiteral) {
String value = ((StringLiteral) arrayElement).astValue();
if (matches(issue, value)) {
return true;
}
} else if (valueNode instanceof UCallExpression
&& ((UCallExpression)valueNode).getKind() == JavaUastCallKinds.ARRAY_INITIALIZER) {
UCallExpression array = (UCallExpression) valueNode;
List<UExpression> expressions = array.getValueArguments();
for (UExpression arrayElement : expressions) {
String elementValue = UastLiteralUtils.getValueIfStringLiteral(arrayElement);
if (elementValue != null) {
if (matches(issue, elementValue)) {
return true;
}
}
}
@@ -0,0 +1,67 @@
/*
* Copyright 2010-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.tools.klint.client.api;
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
import com.intellij.openapi.extensions.ExtensionPointName;
import com.intellij.openapi.project.Project;
import org.jetbrains.uast.UastConverter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public abstract class LintLanguageExtension {
public static final ExtensionPointName<LintLanguageExtension> EP_NAME =
ExtensionPointName.create("com.android.tools.lint.client.api.lintLanguageExtension");
@NonNull
public abstract UastConverter getConverter();
public static boolean isFileSupported(@Nullable Project project, String path) {
LintLanguageExtension[] extensions = getExtensions(project);
for (LintLanguageExtension ext : extensions) {
if (ext.getConverter().isFileSupported(path)) {
return true;
}
}
return false;
}
public static List<UastConverter> getConverters(@Nullable Project project) {
if (project == null) {
return Collections.emptyList();
}
LintLanguageExtension[] languageExtensions = project.getExtensions(EP_NAME);
List<UastConverter> converters = new ArrayList<UastConverter>(languageExtensions.length);
for (LintLanguageExtension extension : languageExtensions) {
converters.add(extension.getConverter());
}
return converters;
}
public static LintLanguageExtension[] getExtensions(@Nullable Project project) {
if (project == null) {
return new LintLanguageExtension[0];
}
return project.getExtensions(EP_NAME);
}
}
@@ -14,11 +14,11 @@
* limitations under the License.
*/
package com.android.tools.lint.client.api;
package com.android.tools.klint.client.api;
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
import com.android.tools.lint.detector.api.Context;
import com.android.tools.klint.detector.api.Context;
import com.google.common.annotations.Beta;
/**
@@ -14,12 +14,12 @@
* limitations under the License.
*/
package com.android.tools.lint.client.api;
package com.android.tools.klint.client.api;
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
import com.android.tools.lint.detector.api.Project;
import com.android.tools.lint.detector.api.Scope;
import com.android.tools.klint.detector.api.Project;
import com.android.tools.klint.detector.api.Scope;
import com.google.common.annotations.Beta;
import java.io.File;
@@ -13,30 +13,22 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.tools.lint.client.api;
import static com.android.SdkConstants.ANDROID_MANIFEST_XML;
import static com.android.SdkConstants.DOT_CLASS;
import static com.android.SdkConstants.DOT_JAVA;
import static com.android.SdkConstants.DOT_XML;
import static com.android.SdkConstants.FD_ASSETS;
import static com.android.tools.lint.detector.api.Detector.OtherFileScanner;
package com.android.tools.klint.client.api;
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
import com.android.tools.lint.detector.api.Context;
import com.android.tools.lint.detector.api.Detector;
import com.android.tools.lint.detector.api.Project;
import com.android.tools.lint.detector.api.Scope;
import com.android.tools.klint.detector.api.Context;
import com.android.tools.klint.detector.api.Detector;
import com.android.tools.klint.detector.api.Project;
import com.android.tools.klint.detector.api.Scope;
import com.android.utils.SdkUtils;
import com.google.common.collect.Lists;
import java.io.File;
import java.util.ArrayList;
import java.util.EnumMap;
import java.util.EnumSet;
import java.util.List;
import java.util.Map;
import java.util.*;
import static com.android.SdkConstants.*;
import static com.android.tools.klint.detector.api.Detector.OtherFileScanner;
/**
* Visitor for "other" files: files that aren't java sources,
@@ -102,7 +94,7 @@ class OtherFileVisitor {
}
}
if (scopes.contains(Scope.JAVA_FILE)) {
if (scopes.contains(Scope.SOURCE_FILE)) {
if (subset != null && !subset.isEmpty()) {
List<File> files = new ArrayList<File>(subset.size());
for (File file : subset) {
@@ -111,7 +103,7 @@ class OtherFileVisitor {
}
}
if (!files.isEmpty()) {
mFiles.put(Scope.JAVA_FILE, files);
mFiles.put(Scope.SOURCE_FILE, files);
}
} else {
List<File> files = Lists.newArrayListWithExpectedSize(100);
@@ -119,7 +111,7 @@ class OtherFileVisitor {
collectFiles(files, srcFolder);
}
if (!files.isEmpty()) {
mFiles.put(Scope.JAVA_FILE, files);
mFiles.put(Scope.SOURCE_FILE, files);
}
}
}
@@ -14,30 +14,20 @@
* limitations under the License.
*/
package com.android.tools.lint.client.api;
package com.android.tools.klint.client.api;
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
import com.android.tools.lint.detector.api.Detector;
import com.android.tools.lint.detector.api.Detector.XmlScanner;
import com.android.tools.lint.detector.api.LintUtils;
import com.android.tools.lint.detector.api.ResourceContext;
import com.android.tools.lint.detector.api.XmlContext;
import com.android.tools.klint.detector.api.Detector;
import com.android.tools.klint.detector.api.Detector.XmlScanner;
import com.android.tools.klint.detector.api.LintUtils;
import com.android.tools.klint.detector.api.ResourceContext;
import com.android.tools.klint.detector.api.XmlContext;
import com.google.common.annotations.Beta;
import org.w3c.dom.Attr;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.*;
import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.RandomAccess;
import java.util.*;
/**
* Specialized visitor for running detectors on resources: typically XML documents,
@@ -14,7 +14,7 @@
* limitations under the License.
*/
package com.android.tools.lint.client.api;
package com.android.tools.klint.client.api;
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
@@ -14,15 +14,14 @@
* limitations under the License.
*/
package com.android.tools.lint.client.api;
package com.android.tools.klint.client.api;
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
import com.android.tools.lint.detector.api.Context;
import com.android.tools.lint.detector.api.Location;
import com.android.tools.lint.detector.api.XmlContext;
import com.android.tools.klint.detector.api.Context;
import com.android.tools.klint.detector.api.Location;
import com.android.tools.klint.detector.api.XmlContext;
import com.google.common.annotations.Beta;
import org.w3c.dom.Attr;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
@@ -14,7 +14,7 @@
* limitations under the License.
*/
package com.android.tools.lint.detector.api;
package com.android.tools.klint.detector.api;
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
@@ -14,34 +14,21 @@
* limitations under the License.
*/
package com.android.tools.lint.detector.api;
import static com.android.SdkConstants.CONSTRUCTOR_NAME;
import static com.android.SdkConstants.DOT_CLASS;
import static com.android.SdkConstants.DOT_JAVA;
import static com.android.tools.lint.detector.api.Location.SearchDirection.BACKWARD;
import static com.android.tools.lint.detector.api.Location.SearchDirection.EOL_BACKWARD;
import static com.android.tools.lint.detector.api.Location.SearchDirection.FORWARD;
package com.android.tools.klint.detector.api;
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
import com.android.tools.lint.client.api.LintDriver;
import com.android.tools.lint.detector.api.Location.SearchDirection;
import com.android.tools.lint.detector.api.Location.SearchHints;
import com.android.tools.klint.client.api.LintDriver;
import com.google.common.annotations.Beta;
import com.google.common.base.Splitter;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.FieldNode;
import org.objectweb.asm.tree.LineNumberNode;
import org.objectweb.asm.tree.MethodInsnNode;
import org.objectweb.asm.tree.MethodNode;
import org.objectweb.asm.tree.*;
import java.io.File;
import java.util.List;
import static com.android.SdkConstants.*;
/**
* A {@link Context} used when checking .class files.
* <p/>
@@ -261,7 +248,7 @@ public class ClassContext extends Context {
*/
@NonNull
public Location getLocationForLine(int line, @Nullable String patternStart,
@Nullable String patternEnd, @Nullable SearchHints hints) {
@Nullable String patternEnd, @Nullable Location.SearchHints hints) {
File sourceFile = getSourceFile();
if (sourceFile != null) {
// ASM line numbers are 1-based, and lint line numbers are 0-based
@@ -515,7 +502,7 @@ public class ClassContext extends Context {
}
return getLocationForLine(findLineNumber(classNode), pattern, null,
SearchHints.create(BACKWARD).matchJavaSymbol());
Location.SearchHints.create(Location.SearchDirection.BACKWARD).matchJavaSymbol());
}
@Nullable
@@ -556,21 +543,21 @@ public class ClassContext extends Context {
// to find a method, look up the corresponding line number then search
// around it for a suitable tag, such as the class name.
String pattern;
SearchDirection searchMode;
Location.SearchDirection searchMode;
if (methodNode.name.equals(CONSTRUCTOR_NAME)) {
searchMode = EOL_BACKWARD;
searchMode = Location.SearchDirection.EOL_BACKWARD;
if (isAnonymousClass(classNode.name)) {
pattern = classNode.superName.substring(classNode.superName.lastIndexOf('/') + 1);
} else {
pattern = classNode.name.substring(classNode.name.lastIndexOf('$') + 1);
}
} else {
searchMode = BACKWARD;
searchMode = Location.SearchDirection.BACKWARD;
pattern = methodNode.name;
}
return getLocationForLine(findLineNumber(methodNode), pattern, null,
SearchHints.create(searchMode).matchJavaSymbol());
Location.SearchHints.create(searchMode).matchJavaSymbol());
}
/**
@@ -582,7 +569,7 @@ public class ClassContext extends Context {
*/
@NonNull
public Location getLocation(@NonNull AbstractInsnNode instruction) {
SearchHints hints = SearchHints.create(FORWARD).matchJavaSymbol();
Location.SearchHints hints = Location.SearchHints.create(Location.SearchDirection.FORWARD).matchJavaSymbol();
String pattern = null;
if (instruction instanceof MethodInsnNode) {
MethodInsnNode call = (MethodInsnNode) instruction;
@@ -14,19 +14,14 @@
* limitations under the License.
*/
package com.android.tools.lint.detector.api;
import static com.android.SdkConstants.DOT_GRADLE;
import static com.android.SdkConstants.DOT_JAVA;
import static com.android.SdkConstants.DOT_XML;
import static com.android.SdkConstants.SUPPRESS_ALL;
package com.android.tools.klint.detector.api;
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
import com.android.tools.lint.client.api.Configuration;
import com.android.tools.lint.client.api.LintClient;
import com.android.tools.lint.client.api.LintDriver;
import com.android.tools.lint.client.api.SdkInfo;
import com.android.tools.klint.client.api.Configuration;
import com.android.tools.klint.client.api.LintClient;
import com.android.tools.klint.client.api.LintDriver;
import com.android.tools.klint.client.api.SdkInfo;
import com.google.common.annotations.Beta;
import java.io.File;
@@ -34,6 +29,8 @@ import java.util.EnumSet;
import java.util.HashMap;
import java.util.Map;
import static com.android.SdkConstants.*;
/**
* Context passed to the detectors during an analysis run. It provides
* information about the file being analyzed, it allows shared properties (so
@@ -14,7 +14,7 @@
* limitations under the License.
*/
package com.android.tools.lint.detector.api;
package com.android.tools.klint.detector.api;
import com.google.common.annotations.Beta;
@@ -14,16 +14,18 @@
* limitations under the License.
*/
package com.android.tools.lint.detector.api;
package com.android.tools.klint.detector.api;
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
import com.android.resources.ResourceFolderType;
import com.android.tools.lint.client.api.JavaParser.ResolvedClass;
import com.android.tools.lint.client.api.JavaParser.ResolvedMethod;
import com.android.tools.lint.client.api.LintDriver;
import com.google.common.annotations.Beta;
import org.jetbrains.uast.UCallExpression;
import org.jetbrains.uast.UClass;
import org.jetbrains.uast.UElement;
import org.jetbrains.uast.UFunction;
import org.jetbrains.uast.check.UastAndroidContext;
import org.jetbrains.uast.visitor.UastVisitor;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.MethodInsnNode;
@@ -33,17 +35,7 @@ import org.w3c.dom.Document;
import org.w3c.dom.Element;
import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.EnumSet;
import java.util.List;
import java.util.Map;
import lombok.ast.AstVisitor;
import lombok.ast.ClassDeclaration;
import lombok.ast.ConstructorInvocation;
import lombok.ast.MethodInvocation;
import lombok.ast.Node;
import java.util.*;
/**
* A detector is able to find a particular problem (or a set of related problems).
@@ -70,192 +62,6 @@ import lombok.ast.Node;
*/
@Beta
public abstract class Detector {
/** Specialized interface for detectors that scan Java source file parse trees */
public interface JavaScanner {
/**
* Create a parse tree visitor to process the parse tree. All
* {@link JavaScanner} detectors must provide a visitor, unless they
* either return true from {@link #appliesToResourceRefs()} or return
* non null from {@link #getApplicableMethodNames()}.
* <p>
* If you return specific AST node types from
* {@link #getApplicableNodeTypes()}, then the visitor will <b>only</b>
* be called for the specific requested node types. This is more
* efficient, since it allows many detectors that apply to only a small
* part of the AST (such as method call nodes) to share iteration of the
* majority of the parse tree.
* <p>
* If you return null from {@link #getApplicableNodeTypes()}, then your
* visitor will be called from the top and all node types visited.
* <p>
* Note that a new visitor is created for each separate compilation
* unit, so you can store per file state in the visitor.
*
* @param context the {@link Context} for the file being analyzed
* @return a visitor, or null.
*/
@Nullable
AstVisitor createJavaVisitor(@NonNull JavaContext context);
/**
* Return the types of AST nodes that the visitor returned from
* {@link #createJavaVisitor(JavaContext)} should visit. See the
* documentation for {@link #createJavaVisitor(JavaContext)} for details
* on how the shared visitor is used.
* <p>
* If you return null from this method, then the visitor will process
* the full tree instead.
* <p>
* Note that for the shared visitor, the return codes from the visit
* methods are ignored: returning true will <b>not</b> prune iteration
* of the subtree, since there may be other node types interested in the
* children. If you need to ensure that your visitor only processes a
* part of the tree, use a full visitor instead. See the
* OverdrawDetector implementation for an example of this.
*
* @return the list of applicable node types (AST node classes), or null
*/
@Nullable
List<Class<? extends Node>> getApplicableNodeTypes();
/**
* Return the list of method names this detector is interested in, or
* null. If this method returns non-null, then any AST nodes that match
* a method call in the list will be passed to the
* {@link #visitMethod(JavaContext, AstVisitor, MethodInvocation)}
* method for processing. The visitor created by
* {@link #createJavaVisitor(JavaContext)} is also passed to that
* method, although it can be null.
* <p>
* This makes it easy to write detectors that focus on some fixed calls.
* For example, the StringFormatDetector uses this mechanism to look for
* "format" calls, and when found it looks around (using the AST's
* {@link Node#getParent()} method) to see if it's called on
* a String class instance, and if so do its normal processing. Note
* that since it doesn't need to do any other AST processing, that
* detector does not actually supply a visitor.
*
* @return a set of applicable method names, or null.
*/
@Nullable
List<String> getApplicableMethodNames();
/**
* Method invoked for any method calls found that matches any names
* returned by {@link #getApplicableMethodNames()}. This also passes
* back the visitor that was created by
* {@link #createJavaVisitor(JavaContext)}, but a visitor is not
* required. It is intended for detectors that need to do additional AST
* processing, but also want the convenience of not having to look for
* method names on their own.
*
* @param context the context of the lint request
* @param visitor the visitor created from
* {@link #createJavaVisitor(JavaContext)}, or null
* @param node the {@link MethodInvocation} node for the invoked method
*/
void visitMethod(
@NonNull JavaContext context,
@Nullable AstVisitor visitor,
@NonNull MethodInvocation node);
/**
* Return the list of constructor types this detector is interested in, or
* null. If this method returns non-null, then any AST nodes that match
* a constructor call in the list will be passed to the
* {@link #visitConstructor(JavaContext, AstVisitor, ConstructorInvocation, ResolvedMethod)}
* method for processing. The visitor created by
* {@link #createJavaVisitor(JavaContext)} is also passed to that
* method, although it can be null.
* <p>
* This makes it easy to write detectors that focus on some fixed constructors.
*
* @return a set of applicable fully qualified types, or null.
*/
@Nullable
List<String> getApplicableConstructorTypes();
/**
* Method invoked for any constructor calls found that matches any names
* returned by {@link #getApplicableConstructorTypes()}. This also passes
* back the visitor that was created by
* {@link #createJavaVisitor(JavaContext)}, but a visitor is not
* required. It is intended for detectors that need to do additional AST
* processing, but also want the convenience of not having to look for
* method names on their own.
*
* @param context the context of the lint request
* @param visitor the visitor created from
* {@link #createJavaVisitor(JavaContext)}, or null
* @param node the {@link ConstructorInvocation} node for the invoked method
* @param constructor the resolved constructor method with type information
*/
void visitConstructor(
@NonNull JavaContext context,
@Nullable AstVisitor visitor,
@NonNull ConstructorInvocation node,
@NonNull ResolvedMethod constructor);
/**
* Returns whether this detector cares about Android resource references
* (such as {@code R.layout.main} or {@code R.string.app_name}). If it
* does, then the visitor will look for these patterns, and if found, it
* will invoke {@link #visitResourceReference} passing the resource type
* and resource name. It also passes the visitor, if any, that was
* created by {@link #createJavaVisitor(JavaContext)}, such that a
* detector can do more than just look for resources.
*
* @return true if this detector wants to be notified of R resource
* identifiers found in the code.
*/
boolean appliesToResourceRefs();
/**
* Called for any resource references (such as {@code R.layout.main}
* found in Java code, provided this detector returned {@code true} from
* {@link #appliesToResourceRefs()}.
*
* @param context the lint scanning context
* @param visitor the visitor created from
* {@link #createJavaVisitor(JavaContext)}, or null
* @param node the variable reference for the resource
* @param type the resource type, such as "layout" or "string"
* @param name the resource name, such as "main" from
* {@code R.layout.main}
* @param isFramework whether the resource is a framework resource
* (android.R) or a local project resource (R)
*/
void visitResourceReference(
@NonNull JavaContext context,
@Nullable AstVisitor visitor,
@NonNull Node node,
@NonNull String type,
@NonNull String name,
boolean isFramework);
/**
* Returns a list of fully qualified names for super classes that this
* detector cares about. If not null, this detector will *only* be called
* if the current class is a subclass of one of the specified superclasses.
*
* @return a list of fully qualified names
*/
@Nullable
List<String> applicableSuperClasses();
/**
* Called for each class that extends one of the super classes specified with
* {@link #applicableSuperClasses()}
*
* @param context the lint scanning context
* @param declaration the class declaration node, or null for anonymous classes
* @param node the class declaration node or the anonymous class construction node
* @param resolvedClass the resolved class
*/
void checkClass(@NonNull JavaContext context, @Nullable ClassDeclaration declaration,
@NonNull Node node, @NonNull ResolvedClass resolvedClass);
}
/** Specialized interface for detectors that scan Java class files */
public interface ClassScanner {
/**
@@ -664,52 +470,48 @@ public abstract class Detector {
return null;
}
@Nullable @SuppressWarnings("javadoc")
public AstVisitor createJavaVisitor(@NonNull JavaContext context) {
return null;
}
@Nullable @SuppressWarnings("javadoc")
public List<Class<? extends Node>> getApplicableNodeTypes() {
return null;
}
@SuppressWarnings("javadoc")
public void visitMethod(@NonNull JavaContext context, @Nullable AstVisitor visitor,
@NonNull MethodInvocation node) {
}
@SuppressWarnings("javadoc")
public boolean appliesToResourceRefs() {
return false;
}
@SuppressWarnings("javadoc")
public void visitResourceReference(@NonNull JavaContext context, @Nullable AstVisitor visitor,
@NonNull Node node, @NonNull String type, @NonNull String name,
boolean isFramework) {
}
@Nullable
public List<String> applicableSuperClasses() {
return null;
}
public void checkClass(@NonNull JavaContext context, @Nullable ClassDeclaration declaration,
@NonNull Node node, @NonNull ResolvedClass resolvedClass) {
}
public void visitResourceReference(
UastAndroidContext context,
UElement element,
String type,
String name,
boolean isFramework
) {}
@Nullable @SuppressWarnings("javadoc")
public List<String> getApplicableConstructorTypes() {
return null;
}
@SuppressWarnings("javadoc")
public void visitConstructor(
@NonNull JavaContext context,
@Nullable AstVisitor visitor,
@NonNull ConstructorInvocation node,
@NonNull ResolvedMethod constructor) {
public List<String> getApplicableSuperClasses() {
return null;
}
public void visitClass(UastAndroidContext context, UClass node) {
}
public List<String> getApplicableFunctionNames() {
return null;
}
public void visitConstructor(UastAndroidContext context, UCallExpression functionCall,
UFunction constructor) {
}
public UastVisitor createUastVisitor(UastAndroidContext context) {
return null;
}
public void visitFunctionCall(UastAndroidContext context, UCallExpression node) {
}
// ---- Dummy implementations to make implementing a ClassScanner easier: ----
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.tools.lint.detector.api;
package com.android.tools.klint.detector.api;
import com.android.annotations.NonNull;
import com.google.common.annotations.Beta;
@@ -14,12 +14,9 @@
* limitations under the License.
*/
package com.android.tools.lint.detector.api;
import static com.android.tools.lint.detector.api.TextFormat.RAW;
package com.android.tools.klint.detector.api;
import com.android.annotations.NonNull;
import com.android.tools.lint.client.api.Configuration;
import com.google.common.annotations.Beta;
import java.util.ArrayList;
@@ -142,7 +139,7 @@ public final class Issue implements Comparable<Issue> {
*/
@NonNull
public String getBriefDescription(@NonNull TextFormat format) {
return RAW.convertTo(mBriefDescription, format);
return TextFormat.RAW.convertTo(mBriefDescription, format);
}
/**
@@ -156,7 +153,7 @@ public final class Issue implements Comparable<Issue> {
*/
@NonNull
public String getExplanation(@NonNull TextFormat format) {
return RAW.convertTo(mExplanation, format);
return TextFormat.RAW.convertTo(mExplanation, format);
}
/**
@@ -14,28 +14,19 @@
* limitations under the License.
*/
package com.android.tools.lint.detector.api;
import static com.android.SdkConstants.CLASS_CONTEXT;
import static com.android.tools.lint.client.api.JavaParser.ResolvedNode;
import static com.android.tools.lint.client.api.JavaParser.TypeDescriptor;
package com.android.tools.klint.detector.api;
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
import com.android.tools.lint.client.api.JavaParser;
import com.android.tools.lint.client.api.JavaParser.ResolvedClass;
import com.android.tools.lint.client.api.LintDriver;
import com.android.tools.klint.client.api.LintDriver;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.uast.*;
import org.jetbrains.uast.check.UastAndroidContext;
import java.io.File;
import java.util.Iterator;
import java.util.List;
import lombok.ast.ClassDeclaration;
import lombok.ast.ConstructorDeclaration;
import lombok.ast.Expression;
import lombok.ast.MethodDeclaration;
import lombok.ast.MethodInvocation;
import lombok.ast.Node;
import lombok.ast.Position;
import static com.android.SdkConstants.CLASS_CONTEXT;
/**
* A {@link Context} used when checking Java files.
@@ -43,14 +34,11 @@ import lombok.ast.Position;
* <b>NOTE: This is not a public or final API; if you rely on this be prepared
* to adjust your code for the next tools release.</b>
*/
public class JavaContext extends Context {
public class JavaContext extends Context implements UastAndroidContext {
static final String SUPPRESS_COMMENT_PREFIX = "//noinspection "; //$NON-NLS-1$
/** The parse tree */
private Node mCompilationUnit;
/** The parser which produced the parse tree */
private final JavaParser mParser;
private UFile mCompilationUnit;
/**
* Constructs a {@link JavaContext} for running lint on the given file, with
@@ -70,10 +58,8 @@ public class JavaContext extends Context {
@NonNull LintDriver driver,
@NonNull Project project,
@Nullable Project main,
@NonNull File file,
@NonNull JavaParser parser) {
@NonNull File file) {
super(driver, project, main, file);
mParser = parser;
}
/**
@@ -82,18 +68,13 @@ public class JavaContext extends Context {
* @param node the AST node to get a location for
* @return a location for the given node
*/
@NonNull
public Location getLocation(@NonNull Node node) {
return mParser.getLocation(this, node);
}
@NonNull
public JavaParser getParser() {
return mParser;
@Override
public Location getLocation(@NotNull UElement element) {
return UastAndroidContext.DefaultImpls.getLocation(this, element);
}
@Nullable
public Node getCompilationUnit() {
public UFile getCompilationUnit() {
return mCompilationUnit;
}
@@ -103,7 +84,7 @@ public class JavaContext extends Context {
*
* @param compilationUnit the parse tree
*/
public void setCompilationUnit(@Nullable Node compilationUnit) {
public void setCompilationUnit(@Nullable UFile compilationUnit) {
mCompilationUnit = compilationUnit;
}
@@ -127,107 +108,24 @@ public class JavaContext extends Context {
* @param location the location of the issue, or null if not known
* @param message the message for this warning
*/
@Override
public void report(
@NonNull Issue issue,
@Nullable Node scope,
@Nullable Location location,
@NonNull String message) {
@NonNull Issue issue,
@Nullable UElement scope,
@Nullable Location location,
@NonNull String message) {
if (scope != null && mDriver.isSuppressed(this, issue, scope)) {
return;
}
super.report(issue, location, message);
}
/**
* Report an error.
* Like {@link #report(Issue, Node, Location, String)} but with
* a now-unused data parameter at the end.
*
* @deprecated Use {@link #report(Issue, Node, Location, String)} instead;
* this method is here for custom rule compatibility
*/
@SuppressWarnings("UnusedDeclaration") // Potentially used by external existing custom rules
@Deprecated
public void report(
@NonNull Issue issue,
@Nullable Node scope,
@Nullable Location location,
@NonNull String message,
@SuppressWarnings("UnusedParameters") @Nullable Object data) {
report(issue, scope, location, message);
}
@Nullable
public static Node findSurroundingMethod(Node scope) {
while (scope != null) {
Class<? extends Node> type = scope.getClass();
// The Lombok AST uses a flat hierarchy of node type implementation classes
// so no need to do instanceof stuff here.
if (type == MethodDeclaration.class || type == ConstructorDeclaration.class) {
return scope;
}
scope = scope.getParent();
}
return null;
}
@Nullable
public static ClassDeclaration findSurroundingClass(@Nullable Node scope) {
while (scope != null) {
Class<? extends Node> type = scope.getClass();
// The Lombok AST uses a flat hierarchy of node type implementation classes
// so no need to do instanceof stuff here.
if (type == ClassDeclaration.class) {
return (ClassDeclaration) scope;
}
scope = scope.getParent();
}
return null;
}
@Override
@Nullable
protected String getSuppressCommentPrefix() {
return SUPPRESS_COMMENT_PREFIX;
}
public boolean isSuppressedWithComment(@NonNull Node scope, @NonNull Issue issue) {
// Check whether there is a comment marker
String contents = getContents();
assert contents != null; // otherwise we wouldn't be here
Position position = scope.getPosition();
if (position == null) {
return false;
}
int start = position.getStart();
return isSuppressedWithComment(start, issue);
}
@NonNull
public Location.Handle createLocationHandle(@NonNull Node node) {
return mParser.createLocationHandle(this, node);
}
@Nullable
public ResolvedNode resolve(@NonNull Node node) {
return mParser.resolve(this, node);
}
@Nullable
public ResolvedClass findClass(@NonNull String fullyQualifiedName) {
return mParser.findClass(this, fullyQualifiedName);
}
@Nullable
public TypeDescriptor getType(@NonNull Node node) {
return mParser.getType(this, node);
}
/**
* Returns true if the given method invocation node corresponds to a call on a
* {@code android.content.Context}
@@ -235,160 +133,35 @@ public class JavaContext extends Context {
* @param node the method call node
* @return true iff the method call is on a class extending context
*/
public boolean isContextMethod(@NonNull MethodInvocation node) {
public boolean isContextMethod(@NonNull UCallExpression node) {
// Method name used in many other contexts where it doesn't have the
// same semantics; only use this one if we can resolve types
// and we're certain this is the Context method
ResolvedNode resolved = resolve(node);
if (resolved instanceof JavaParser.ResolvedMethod) {
JavaParser.ResolvedMethod method = (JavaParser.ResolvedMethod) resolved;
ResolvedClass containingClass = method.getContainingClass();
if (containingClass.isSubclassOf(CLASS_CONTEXT, false)) {
UFunction resolved = node.resolve(this);
UClass containingClass = UastUtils.getContainingClass(resolved);
if (resolved != null && containingClass != null) {
if (containingClass.isSubclassOf(CLASS_CONTEXT)) {
return true;
}
}
return false;
}
/**
* Returns the first ancestor node of the given type
*
* @param element the element to search from
* @param clz the target node type
* @param <T> the target node type
* @return the nearest ancestor node in the parent chain, or null if not found
*/
@Nullable
public static <T extends Node> T getParentOfType(
@Nullable Node element,
@NonNull Class<T> clz) {
return getParentOfType(element, clz, true);
@NotNull
@Override
public JavaContext getLintContext() {
return this;
}
/**
* Returns the first ancestor node of the given type
*
* @param element the element to search from
* @param clz the target node type
* @param strict if true, do not consider the element itself, only its parents
* @param <T> the target node type
* @return the nearest ancestor node in the parent chain, or null if not found
*/
@Nullable
public static <T extends Node> T getParentOfType(
@Nullable Node element,
@NonNull Class<T> clz,
boolean strict) {
if (element == null) {
return null;
}
if (strict) {
element = element.getParent();
}
while (element != null) {
if (clz.isInstance(element)) {
//noinspection unchecked
return (T) element;
}
element = element.getParent();
}
return null;
@NotNull
@Override
public List<UastConverter> getConverters() {
return getClient().getConverters();
}
/**
* Returns the first ancestor node of the given type, stopping at the given type
*
* @param element the element to search from
* @param clz the target node type
* @param strict if true, do not consider the element itself, only its parents
* @param terminators optional node types to terminate the search at
* @param <T> the target node type
* @return the nearest ancestor node in the parent chain, or null if not found
*/
@Nullable
public static <T extends Node> T getParentOfType(@Nullable Node element,
@NonNull Class<T> clz,
boolean strict,
@NonNull Class<? extends Node>... terminators) {
if (element == null) {
return null;
}
if (strict) {
element = element.getParent();
}
while (element != null && !clz.isInstance(element)) {
for (Class<?> terminator : terminators) {
if (terminator.isInstance(element)) {
return null;
}
}
element = element.getParent();
}
//noinspection unchecked
return (T) element;
}
/**
* Returns the first sibling of the given node that is of the given class
*
* @param sibling the sibling to search from
* @param clz the type to look for
* @param <T> the type
* @return the first sibling of the given type, or null
*/
@Nullable
public static <T extends Node> T getNextSiblingOfType(@Nullable Node sibling,
@NonNull Class<T> clz) {
if (sibling == null) {
return null;
}
Node parent = sibling.getParent();
if (parent == null) {
return null;
}
Iterator<Node> iterator = parent.getChildren().iterator();
while (iterator.hasNext()) {
if (iterator.next() == sibling) {
break;
}
}
while (iterator.hasNext()) {
Node child = iterator.next();
if (clz.isInstance(child)) {
//noinspection unchecked
return (T) child;
}
}
return null;
}
/**
* Returns the given argument of the given call
*
* @param call the call containing arguments
* @param index the index of the target argument
* @return the argument at the given index
* @throws IllegalArgumentException if index is outside the valid range
*/
@NonNull
public static Node getArgumentNode(@NonNull MethodInvocation call, int index) {
int i = 0;
for (Expression parameter : call.astArguments()) {
if (i == index) {
return parameter;
}
i++;
}
throw new IllegalArgumentException(Integer.toString(index));
@org.jetbrains.annotations.Nullable
@Override
public UElement convert(@Nullable Object element) {
return UastContext.DefaultImpls.convert(this, element);
}
}
@@ -14,25 +14,15 @@
* limitations under the License.
*/
package com.android.tools.lint.detector.api;
import static com.android.SdkConstants.ANDROID_URI;
import static com.android.SdkConstants.ATTR_LAYOUT_HEIGHT;
import static com.android.SdkConstants.ATTR_LAYOUT_WIDTH;
import static com.android.SdkConstants.ATTR_PADDING;
import static com.android.SdkConstants.ATTR_PADDING_BOTTOM;
import static com.android.SdkConstants.ATTR_PADDING_LEFT;
import static com.android.SdkConstants.ATTR_PADDING_RIGHT;
import static com.android.SdkConstants.ATTR_PADDING_TOP;
import static com.android.SdkConstants.VALUE_FILL_PARENT;
import static com.android.SdkConstants.VALUE_MATCH_PARENT;
package com.android.tools.klint.detector.api;
import com.android.annotations.NonNull;
import com.android.resources.ResourceFolderType;
import com.google.common.annotations.Beta;
import org.w3c.dom.Element;
import static com.android.SdkConstants.*;
/**
* Abstract class specifically intended for layout detectors which provides some
* common utility methods shared by layout detectors.
@@ -14,25 +14,7 @@
* limitations under the License.
*/
package com.android.tools.lint.detector.api;
import static com.android.SdkConstants.ANDROID_MANIFEST_XML;
import static com.android.SdkConstants.ANDROID_PREFIX;
import static com.android.SdkConstants.ANDROID_URI;
import static com.android.SdkConstants.ATTR_LOCALE;
import static com.android.SdkConstants.BIN_FOLDER;
import static com.android.SdkConstants.DOT_GIF;
import static com.android.SdkConstants.DOT_JPEG;
import static com.android.SdkConstants.DOT_JPG;
import static com.android.SdkConstants.DOT_PNG;
import static com.android.SdkConstants.DOT_WEBP;
import static com.android.SdkConstants.DOT_XML;
import static com.android.SdkConstants.ID_PREFIX;
import static com.android.SdkConstants.NEW_ID_PREFIX;
import static com.android.SdkConstants.TOOLS_URI;
import static com.android.SdkConstants.UTF_8;
import static com.android.ide.common.resources.configuration.FolderConfiguration.QUALIFIER_SPLITTER;
import static com.android.ide.common.resources.configuration.LocaleQualifier.BCP_47_PREFIX;
package com.android.tools.klint.detector.api;
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
@@ -53,7 +35,7 @@ import com.android.sdklib.AndroidVersion;
import com.android.sdklib.IAndroidTarget;
import com.android.sdklib.SdkVersionInfo;
import com.android.sdklib.repository.FullRevision;
import com.android.tools.lint.client.api.LintClient;
import com.android.tools.klint.client.api.LintClient;
import com.android.utils.PositionXmlParser;
import com.android.utils.SdkUtils;
import com.google.common.annotations.Beta;
@@ -61,7 +43,8 @@ import com.google.common.base.Splitter;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import org.jetbrains.uast.UFile;
import org.jetbrains.uast.UImportStatement;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.ClassNode;
@@ -74,19 +57,14 @@ import java.io.File;
import java.io.IOException;
import java.io.StringWriter;
import java.io.UnsupportedEncodingException;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Properties;
import java.util.Queue;
import java.util.Set;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
import lombok.ast.ImportDeclaration;
import static com.android.SdkConstants.*;
import static com.android.ide.common.resources.configuration.FolderConfiguration.QUALIFIER_SPLITTER;
import static com.android.ide.common.resources.configuration.LocaleQualifier.BCP_47_PREFIX;
/**
* Useful utility methods related to lint.
@@ -796,7 +774,7 @@ public class LintUtils {
* qualified name
*/
public static boolean isImported(
@Nullable lombok.ast.Node compilationUnit,
@Nullable UFile compilationUnit,
@NonNull String fullyQualifiedName) {
if (compilationUnit == null) {
return false;
@@ -805,26 +783,27 @@ public class LintUtils {
int dotLength = fullyQualifiedName.length() - dotIndex;
boolean imported = false;
for (lombok.ast.Node rootNode : compilationUnit.getChildren()) {
if (rootNode instanceof ImportDeclaration) {
ImportDeclaration importDeclaration = (ImportDeclaration) rootNode;
String fqn = importDeclaration.asFullyQualifiedName();
if (fqn.equals(fullyQualifiedName)) {
return true;
} else if (fullyQualifiedName.regionMatches(dotIndex, fqn,
fqn.length() - dotLength, dotLength)) {
// This import is importing the class name using some other prefix, so there
// fully qualified class name cannot be imported under that name
return false;
} else if (importDeclaration.astStarImport()
&& fqn.regionMatches(0, fqn, 0, dotIndex + 1)) {
imported = true;
// but don't break -- keep searching in case there's a non-wildcard
// import of the specific class name, e.g. if we're looking for
// android.content.SharedPreferences.Editor, don't match on the following:
// import android.content.SharedPreferences.*;
// import foo.bar.Editor;
}
for (UImportStatement importStatement : compilationUnit.getImportStatements()) {
String fqn = importStatement.getNameToImport();
if (fqn == null) {
continue;
}
if (fqn.equals(fullyQualifiedName)) {
return true;
} else if (fullyQualifiedName.regionMatches(dotIndex, fqn,
fqn.length() - dotLength, dotLength)) {
// This import is importing the class name using some other prefix, so there
// fully qualified class name cannot be imported under that name
return false;
} else if (importStatement.isStarImport()
&& fqn.regionMatches(0, fqn, 0, dotIndex + 1)) {
imported = true;
// but don't break -- keep searching in case there's a non-wildcard
// import of the specific class name, e.g. if we're looking for
// android.content.SharedPreferences.Editor, don't match on the following:
// import android.content.SharedPreferences.*;
// import foo.bar.Editor;
}
}
@@ -14,7 +14,7 @@
* limitations under the License.
*/
package com.android.tools.lint.detector.api;
package com.android.tools.klint.detector.api;
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
@@ -14,7 +14,7 @@
* limitations under the License.
*/
package com.android.tools.lint.detector.api;
package com.android.tools.klint.detector.api;
import com.google.common.annotations.Beta;
@@ -14,7 +14,7 @@
* limitations under the License.
*/
package com.android.tools.lint.detector.api;
package com.android.tools.klint.detector.api;
import com.android.SdkConstants;
import com.android.annotations.NonNull;
@@ -28,10 +28,10 @@ import com.android.resources.ResourceFolderType;
import com.android.sdklib.AndroidVersion;
import com.android.sdklib.IAndroidTarget;
import com.android.sdklib.SdkVersionInfo;
import com.android.tools.lint.client.api.CircularDependencyException;
import com.android.tools.lint.client.api.Configuration;
import com.android.tools.lint.client.api.LintClient;
import com.android.tools.lint.client.api.SdkInfo;
import com.android.tools.klint.client.api.CircularDependencyException;
import com.android.tools.klint.client.api.Configuration;
import com.android.tools.klint.client.api.LintClient;
import com.android.tools.klint.client.api.SdkInfo;
import com.google.common.annotations.Beta;
import com.google.common.base.CharMatcher;
import com.google.common.base.Charsets;
@@ -14,12 +14,12 @@
* limitations under the License.
*/
package com.android.tools.lint.detector.api;
package com.android.tools.klint.detector.api;
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
import com.android.resources.ResourceFolderType;
import com.android.tools.lint.client.api.LintDriver;
import com.android.tools.klint.client.api.LintDriver;
import com.google.common.annotations.Beta;
import java.io.File;
@@ -14,7 +14,7 @@
* limitations under the License.
*/
package com.android.tools.lint.detector.api;
package com.android.tools.klint.detector.api;
import com.android.annotations.NonNull;
import com.android.resources.ResourceFolderType;
@@ -14,27 +14,19 @@
* limitations under the License.
*/
package com.android.tools.lint.detector.api;
import static com.android.SdkConstants.ANDROID_MANIFEST_XML;
import static com.android.SdkConstants.DOT_CLASS;
import static com.android.SdkConstants.DOT_GRADLE;
import static com.android.SdkConstants.DOT_JAVA;
import static com.android.SdkConstants.DOT_PNG;
import static com.android.SdkConstants.DOT_PROPERTIES;
import static com.android.SdkConstants.DOT_XML;
import static com.android.SdkConstants.FN_PROJECT_PROGUARD_FILE;
import static com.android.SdkConstants.OLD_PROGUARD_FILE;
import static com.android.SdkConstants.RES_FOLDER;
package com.android.tools.klint.detector.api;
import com.android.annotations.NonNull;
import com.google.common.annotations.Beta;
import org.jetbrains.uast.UastConverterUtils;
import java.io.File;
import java.util.Collection;
import java.util.EnumSet;
import java.util.List;
import static com.android.SdkConstants.*;
/**
* The scope of a detector is the set of files a detector must consider when
* performing its analysis. This can be used to determine when issues are
@@ -80,14 +72,14 @@ public enum Scope {
* Issues which are only affected by a single Java source file can be
* checked for incrementally when a Java source file is edited.
*/
JAVA_FILE,
SOURCE_FILE,
/**
* The analysis considers <b>all</b> the Java source files together.
* <p>
* This flag is mutually exclusive with {@link #JAVA_FILE}.
* This flag is mutually exclusive with {@link #SOURCE_FILE}.
*/
ALL_JAVA_FILES,
ALL_SOURCE_FILES,
/**
* The analysis only considers a single Java class file at a time.
@@ -143,10 +135,10 @@ public enum Scope {
if (size == 2) {
// When single checking a Java source file, we check both its Java source
// and the associated class files
return scopes.contains(JAVA_FILE) && scopes.contains(CLASS_FILE);
return scopes.contains(SOURCE_FILE) && scopes.contains(CLASS_FILE);
} else {
return size == 1 &&
(scopes.contains(JAVA_FILE)
(scopes.contains(SOURCE_FILE)
|| scopes.contains(CLASS_FILE)
|| scopes.contains(RESOURCE_FILE)
|| scopes.contains(PROGUARD_FILE)
@@ -192,7 +184,7 @@ public enum Scope {
} else if (name.endsWith(DOT_XML)) {
scope.add(RESOURCE_FILE);
} else if (name.endsWith(DOT_JAVA)) {
scope.add(JAVA_FILE);
scope.add(SOURCE_FILE);
} else if (name.endsWith(DOT_CLASS)) {
scope.add(CLASS_FILE);
} else if (name.endsWith(DOT_GRADLE)) {
@@ -210,6 +202,9 @@ public enum Scope {
scope.add(RESOURCE_FILE);
scope.add(BINARY_RESOURCE_FILE);
scope.add(RESOURCE_FOLDER);
} else if (UastConverterUtils.isFileSupported(
project.getClient().getConverters(), name)) {
scope.add(SOURCE_FILE);
}
}
} else {
@@ -231,7 +226,7 @@ public enum Scope {
/** Scope-set used for detectors which scan all resources */
public static final EnumSet<Scope> ALL_RESOURCES_SCOPE = EnumSet.of(ALL_RESOURCE_FILES);
/** Scope-set used for detectors which are affected by a single Java source file */
public static final EnumSet<Scope> JAVA_FILE_SCOPE = EnumSet.of(JAVA_FILE);
public static final EnumSet<Scope> JAVA_FILE_SCOPE = EnumSet.of(SOURCE_FILE);
/** Scope-set used for detectors which are affected by a single Java class file */
public static final EnumSet<Scope> CLASS_FILE_SCOPE = EnumSet.of(CLASS_FILE);
/** Scope-set used for detectors which are affected by a single Gradle build file */
@@ -248,8 +243,8 @@ public enum Scope {
public static final EnumSet<Scope> MANIFEST_AND_RESOURCE_SCOPE =
EnumSet.of(Scope.MANIFEST, Scope.RESOURCE_FILE);
/** Scope-set used for detectors which are affected by single XML and Java source files */
public static final EnumSet<Scope> JAVA_AND_RESOURCE_FILES =
EnumSet.of(RESOURCE_FILE, JAVA_FILE);
public static final EnumSet<Scope> SOURCE_AND_RESOURCE_FILES =
EnumSet.of(RESOURCE_FILE, SOURCE_FILE);
/** Scope-set used for analyzing individual class files and all resource files */
public static final EnumSet<Scope> CLASS_AND_ALL_RESOURCE_FILES =
EnumSet.of(ALL_RESOURCE_FILES, CLASS_FILE);
@@ -14,7 +14,7 @@
* limitations under the License.
*/
package com.android.tools.lint.detector.api;
package com.android.tools.klint.detector.api;
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
@@ -14,7 +14,7 @@
* limitations under the License.
*/
package com.android.tools.lint.detector.api;
package com.android.tools.klint.detector.api;
import com.android.annotations.NonNull;
import com.google.common.annotations.Beta;
@@ -14,7 +14,7 @@
* limitations under the License.
*/
package com.android.tools.lint.detector.api;
package com.android.tools.klint.detector.api;
import com.android.annotations.NonNull;
import com.android.utils.SdkUtils;
@@ -14,15 +14,14 @@
* limitations under the License.
*/
package com.android.tools.lint.detector.api;
package com.android.tools.klint.detector.api;
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
import com.android.resources.ResourceFolderType;
import com.android.tools.lint.client.api.LintDriver;
import com.android.tools.lint.client.api.XmlParser;
import com.android.tools.klint.client.api.LintDriver;
import com.android.tools.klint.client.api.XmlParser;
import com.google.common.annotations.Beta;
import org.w3c.dom.Attr;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
+17
View File
@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="module" module-name="android-annotations" />
<orderEntry type="module" module-name="lint-api" />
<orderEntry type="library" name="android-plugin" level="project" />
<orderEntry type="library" name="guava" level="project" />
<orderEntry type="module" module-name="uast-android" />
<orderEntry type="library" name="intellij-core" level="project" />
</component>
</module>
@@ -14,7 +14,7 @@
* limitations under the License.
*/
package com.android.tools.lint.checks;
package com.android.tools.klint.checks;
import static com.android.SdkConstants.ANDROID_URI;
import static com.android.SdkConstants.ATTR_CONTENT_DESCRIPTION;
@@ -26,14 +26,14 @@ import static com.android.SdkConstants.VALUE_NO;
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
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.LayoutDetector;
import com.android.tools.lint.detector.api.Scope;
import com.android.tools.lint.detector.api.Severity;
import com.android.tools.lint.detector.api.Speed;
import com.android.tools.lint.detector.api.XmlContext;
import com.android.tools.klint.detector.api.Category;
import com.android.tools.klint.detector.api.Implementation;
import com.android.tools.klint.detector.api.Issue;
import com.android.tools.klint.detector.api.LayoutDetector;
import com.android.tools.klint.detector.api.Scope;
import com.android.tools.klint.detector.api.Severity;
import com.android.tools.klint.detector.api.Speed;
import com.android.tools.klint.detector.api.XmlContext;
import org.w3c.dom.Attr;
import org.w3c.dom.Element;
@@ -14,35 +14,34 @@
* limitations under the License.
*/
package com.android.tools.lint.checks;
package com.android.tools.klint.checks;
import static com.android.tools.lint.client.api.JavaParser.ResolvedMethod;
import static com.android.tools.lint.client.api.JavaParser.ResolvedNode;
import static com.android.tools.lint.client.api.JavaParser.TYPE_OBJECT;
import static com.android.tools.lint.client.api.JavaParser.TYPE_STRING;
import static com.android.tools.klint.client.api.JavaParser.TYPE_OBJECT;
import static com.android.tools.klint.client.api.JavaParser.TYPE_STRING;
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
import com.android.tools.lint.detector.api.Category;
import com.android.tools.lint.detector.api.Detector;
import com.android.tools.lint.detector.api.Implementation;
import com.android.tools.lint.detector.api.Issue;
import com.android.tools.lint.detector.api.JavaContext;
import com.android.tools.lint.detector.api.Scope;
import com.android.tools.lint.detector.api.Severity;
import com.android.tools.lint.detector.api.Speed;
import java.util.Collections;
import java.util.List;
import lombok.ast.AstVisitor;
import lombok.ast.MethodInvocation;
import com.android.tools.klint.detector.api.Category;
import com.android.tools.klint.detector.api.Detector;
import com.android.tools.klint.detector.api.Implementation;
import com.android.tools.klint.detector.api.Issue;
import com.android.tools.klint.detector.api.Scope;
import com.android.tools.klint.detector.api.Severity;
import com.android.tools.klint.detector.api.Speed;
import org.jetbrains.uast.*;
import org.jetbrains.uast.check.UastAndroidUtils;
import org.jetbrains.uast.check.UastAndroidContext;
import org.jetbrains.uast.check.UastScanner;
/**
* Ensures that addJavascriptInterface is not called for API levels below 17.
*/
public class AddJavascriptInterfaceDetector extends Detector implements Detector.JavaScanner {
public class AddJavascriptInterfaceDetector extends Detector implements UastScanner {
public static final Issue ISSUE = Issue.create(
"AddJavascriptInterface", //$NON-NLS-1$
"addJavascriptInterface Called",
@@ -62,45 +61,40 @@ public class AddJavascriptInterfaceDetector extends Detector implements Detector
private static final String WEB_VIEW = "android.webkit.WebView"; //$NON-NLS-1$
private static final String ADD_JAVASCRIPT_INTERFACE = "addJavascriptInterface"; //$NON-NLS-1$
// ---- Implements UastScanner ----
@NonNull
@Override
public Speed getSpeed() {
return Speed.FAST;
}
// ---- Implements JavaScanner ----
@Nullable
@Override
public List<String> getApplicableMethodNames() {
public List<String> getApplicableFunctionNames() {
return Collections.singletonList(ADD_JAVASCRIPT_INTERFACE);
}
@Override
public void visitMethod(@NonNull JavaContext context, @Nullable AstVisitor visitor,
@NonNull MethodInvocation node) {
// Ignore the issue if we never build for any API less than 17.
if (context.getMainProject().getMinSdk() >= 17) {
public void visitFunctionCall(UastAndroidContext context, UCallExpression node) {
if (context.getLintContext().getMainProject().getMinSdk() >= 17) {
return;
}
// Ignore if the method doesn't fit our description.
ResolvedNode resolved = context.resolve(node);
if (!(resolved instanceof ResolvedMethod)) {
UFunction resolvedFunction = node.resolveOrEmpty(context);
UClass containingClass = UastUtils.getContainingClassOrEmpty(resolvedFunction);
if (!containingClass.isSubclassOf(WEB_VIEW)) {
return;
}
ResolvedMethod method = (ResolvedMethod) resolved;
if (!method.getContainingClass().isSubclassOf(WEB_VIEW, false)) {
return;
}
if (method.getArgumentCount() != 2
|| !method.getArgumentType(0).matchesName(TYPE_OBJECT)
|| !method.getArgumentType(1).matchesName(TYPE_STRING)) {
List<UVariable> valueParameters = resolvedFunction.getValueParameters();
if (node.getValueArgumentCount() != 2
|| !valueParameters.get(0).getType().matchesFqName(TYPE_OBJECT)
|| !valueParameters.get(1).getType().matchesFqName(TYPE_STRING)) {
return;
}
String message = "`WebView.addJavascriptInterface` should not be called with minSdkVersion < 17 for security reasons: " +
"JavaScript can use reflection to manipulate application";
context.report(ISSUE, node, context.getLocation(node.astName()), message);
context.report(ISSUE, node, UastAndroidUtils.getLocation(node), message);
}
}
@@ -14,36 +14,30 @@
* limitations under the License.
*/
package com.android.tools.lint.checks;
package com.android.tools.klint.checks;
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
import com.android.tools.lint.client.api.JavaParser.ResolvedMethod;
import com.android.tools.lint.client.api.JavaParser.ResolvedNode;
import com.android.tools.lint.detector.api.Category;
import com.android.tools.lint.detector.api.ConstantEvaluator;
import com.android.tools.lint.detector.api.Context;
import com.android.tools.lint.detector.api.Detector;
import com.android.tools.lint.detector.api.Implementation;
import com.android.tools.lint.detector.api.Issue;
import com.android.tools.lint.detector.api.JavaContext;
import com.android.tools.lint.detector.api.Scope;
import com.android.tools.lint.detector.api.Severity;
import com.android.tools.lint.detector.api.Speed;
import com.android.tools.klint.detector.api.Category;
import com.android.tools.klint.detector.api.Context;
import com.android.tools.klint.detector.api.Detector;
import com.android.tools.klint.detector.api.Implementation;
import com.android.tools.klint.detector.api.Issue;
import com.android.tools.klint.detector.api.Scope;
import com.android.tools.klint.detector.api.Severity;
import com.android.tools.klint.detector.api.Speed;
import java.io.File;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import lombok.ast.AstVisitor;
import lombok.ast.Expression;
import lombok.ast.MethodInvocation;
import org.jetbrains.uast.*;
import org.jetbrains.uast.check.UastAndroidContext;
import org.jetbrains.uast.check.UastScanner;
/**
* Makes sure that alarms are handled correctly
*/
public class AlarmDetector extends Detector implements Detector.JavaScanner {
public class AlarmDetector extends Detector implements UastScanner {
private static final Implementation IMPLEMENTATION = new Implementation(
AlarmDetector.class,
@@ -82,51 +76,38 @@ public class AlarmDetector extends Detector implements Detector.JavaScanner {
return Speed.FAST;
}
// ---- Implements JavaScanner ----
// ---- Implements UastScanner ----
@Override
public List<String> getApplicableMethodNames() {
public List<String> getApplicableFunctionNames() {
return Collections.singletonList("setRepeating");
}
@Override
public void visitMethod(@NonNull JavaContext context, @Nullable AstVisitor visitor,
@NonNull MethodInvocation node) {
ResolvedNode resolved = context.resolve(node);
if (resolved instanceof ResolvedMethod) {
ResolvedMethod method = (ResolvedMethod) resolved;
if (method.getContainingClass().matches("android.app.AlarmManager")
&& method.getArgumentCount() == 4) {
ensureAtLeast(context, node, 1, 5000L);
ensureAtLeast(context, node, 2, 60000L);
}
public void visitFunctionCall(UastAndroidContext context, UCallExpression node) {
UFunction setRepeatingFunction = node.resolve(context);
UClass containingClass = UastUtils.getContainingClassOrEmpty(setRepeatingFunction);
if (containingClass.matchesFqName("android.app.AlarmManager")
&& node.getValueArgumentCount() == 4) {
ensureAtLeast(context, node, 1, 5000L);
ensureAtLeast(context, node, 2, 60000L);
}
}
private static void ensureAtLeast(@NonNull JavaContext context,
@NonNull MethodInvocation node, int parameter, long min) {
Iterator<Expression> iterator = node.astArguments().iterator();
Expression argument = null;
for (int i = 0; i <= parameter; i++) {
if (!iterator.hasNext()) {
return;
}
argument = iterator.next();
}
if (argument == null) {
return;
}
long value = getLongValue(context, argument);
private static void ensureAtLeast(@NonNull UastAndroidContext context,
@NonNull UCallExpression node, int parameter, long min) {
UExpression arg = node.getValueArguments().get(parameter);
long value = getLongValue(arg);
if (value < min) {
String message = String.format("Value will be forced up to %d as of Android 5.1; "
+ "don't rely on this to be exact", min);
context.report(ISSUE, argument, context.getLocation(argument), message);
context.report(ISSUE, arg, context.getLocation(arg), message);
}
}
private static long getLongValue(@NonNull JavaContext context, @NonNull Expression argument) {
Object value = ConstantEvaluator.evaluate(context, argument);
private static long getLongValue(@NonNull UExpression argument) {
Object value = argument.evaluate();
if (value instanceof Number) {
return ((Number)value).longValue();
}
@@ -14,7 +14,7 @@
* limitations under the License.
*/
package com.android.tools.lint.checks;
package com.android.tools.klint.checks;
import static com.android.SdkConstants.ATTR_SHOW_AS_ACTION;
import static com.android.SdkConstants.VALUE_ALWAYS;
@@ -22,19 +22,26 @@ import static com.android.SdkConstants.VALUE_IF_ROOM;
import com.android.annotations.NonNull;
import com.android.resources.ResourceFolderType;
import com.android.tools.lint.detector.api.Category;
import com.android.tools.lint.detector.api.Context;
import com.android.tools.lint.detector.api.Detector.JavaScanner;
import com.android.tools.lint.detector.api.Implementation;
import com.android.tools.lint.detector.api.Issue;
import com.android.tools.lint.detector.api.JavaContext;
import com.android.tools.lint.detector.api.Location;
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.Speed;
import com.android.tools.lint.detector.api.XmlContext;
import com.android.tools.klint.detector.api.Category;
import com.android.tools.klint.detector.api.Context;
import com.android.tools.klint.detector.api.Implementation;
import com.android.tools.klint.detector.api.Issue;
import com.android.tools.klint.detector.api.JavaContext;
import com.android.tools.klint.detector.api.Location;
import com.android.tools.klint.detector.api.ResourceXmlDetector;
import com.android.tools.klint.detector.api.Scope;
import com.android.tools.klint.detector.api.Severity;
import com.android.tools.klint.detector.api.Speed;
import com.android.tools.klint.detector.api.XmlContext;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.uast.UElement;
import org.jetbrains.uast.UQualifiedExpression;
import org.jetbrains.uast.USimpleReferenceExpression;
import org.jetbrains.uast.check.UastAndroidUtils;
import org.jetbrains.uast.check.UastAndroidContext;
import org.jetbrains.uast.check.UastScanner;
import org.jetbrains.uast.visitor.UastVisitor;
import org.w3c.dom.Attr;
import java.util.ArrayList;
@@ -42,17 +49,12 @@ import java.util.Collection;
import java.util.Collections;
import java.util.List;
import lombok.ast.AstVisitor;
import lombok.ast.ForwardingAstVisitor;
import lombok.ast.Node;
import lombok.ast.Select;
/**
* Check which looks for usage of showAsAction="always" in menus (or
* MenuItem.SHOW_AS_ACTION_ALWAYS in code), which is usually a style guide violation.
* (Use ifRoom instead).
*/
public class AlwaysShowActionDetector extends ResourceXmlDetector implements JavaScanner {
public class AlwaysShowActionDetector extends ResourceXmlDetector implements UastScanner {
/** The main issue discovered by this detector */
@SuppressWarnings("unchecked")
@@ -78,7 +80,7 @@ public class AlwaysShowActionDetector extends ResourceXmlDetector implements Jav
Severity.WARNING,
new Implementation(
AlwaysShowActionDetector.class,
Scope.JAVA_AND_RESOURCE_FILES,
Scope.SOURCE_AND_RESOURCE_FILES,
Scope.RESOURCE_FILE_SCOPE))
.addMoreInfo("http://developer.android.com/design/patterns/actionbar.html"); //$NON-NLS-1$
@@ -193,47 +195,47 @@ public class AlwaysShowActionDetector extends ResourceXmlDetector implements Jav
mFileAttributes.add(attribute);
}
// ---- Implements JavaScanner ----
// ---- Implements UastScanner ----
@Override
public
List<Class<? extends Node>> getApplicableNodeTypes() {
return Collections.<Class<? extends Node>>singletonList(Select.class);
}
@Override
public AstVisitor createJavaVisitor(@NonNull JavaContext context) {
public UastVisitor createUastVisitor(UastAndroidContext context) {
return new FieldAccessChecker(context);
}
private class FieldAccessChecker extends ForwardingAstVisitor {
private final JavaContext mContext;
private class FieldAccessChecker extends UastVisitor {
private final UastAndroidContext mContext;
public FieldAccessChecker(JavaContext context) {
public FieldAccessChecker(UastAndroidContext context) {
mContext = context;
}
@Override
public boolean visitSelect(Select node) {
String description = node.astIdentifier().astValue();
public boolean visitQualifiedExpression(@NotNull UQualifiedExpression node) {
UElement selector = node.getSelector();
if (!(selector instanceof USimpleReferenceExpression)) {
return false;
}
String description = ((USimpleReferenceExpression)selector).getIdentifier();
boolean isIfRoom = description.equals("SHOW_AS_ACTION_IF_ROOM"); //$NON-NLS-1$
boolean isAlways = description.equals("SHOW_AS_ACTION_ALWAYS"); //$NON-NLS-1$
if ((isIfRoom || isAlways)
&& node.astOperand().toString().equals("MenuItem")) { //$NON-NLS-1$
&& node.getReceiver().renderString().equals("MenuItem")) { //$NON-NLS-1$
if (isAlways) {
if (mContext.getDriver().isSuppressed(mContext, ISSUE, node)) {
return super.visitSelect(node);
JavaContext lintContext = mContext.getLintContext();
if (lintContext.getDriver().isSuppressed(lintContext, ISSUE, node)) {
return false;
}
if (mAlwaysFields == null) {
mAlwaysFields = new ArrayList<Location>();
}
mAlwaysFields.add(mContext.getLocation(node));
mAlwaysFields.add(UastAndroidUtils.getLocation(node));
} else {
mHasIfRoomRefs = true;
}
}
return super.visitSelect(node);
return false;
}
}
}
@@ -14,51 +14,36 @@
* limitations under the License.
*/
package com.android.tools.lint.checks;
package com.android.tools.klint.checks;
import static com.android.SdkConstants.FQCN_SUPPRESS_LINT;
import static com.android.SdkConstants.SUPPRESS_LINT;
import com.android.annotations.NonNull;
import com.android.tools.lint.client.api.IssueRegistry;
import com.android.tools.lint.detector.api.Category;
import com.android.tools.lint.detector.api.Context;
import com.android.tools.lint.detector.api.Detector;
import com.android.tools.lint.detector.api.Implementation;
import com.android.tools.lint.detector.api.Issue;
import com.android.tools.lint.detector.api.JavaContext;
import com.android.tools.lint.detector.api.Scope;
import com.android.tools.lint.detector.api.Severity;
import com.android.tools.lint.detector.api.Speed;
import com.android.tools.klint.client.api.IssueRegistry;
import com.android.tools.klint.detector.api.Category;
import com.android.tools.klint.detector.api.Context;
import com.android.tools.klint.detector.api.Detector;
import com.android.tools.klint.detector.api.Implementation;
import com.android.tools.klint.detector.api.Issue;
import com.android.tools.klint.detector.api.Scope;
import com.android.tools.klint.detector.api.Severity;
import com.android.tools.klint.detector.api.Speed;
import java.io.File;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import lombok.ast.Annotation;
import lombok.ast.AnnotationElement;
import lombok.ast.AnnotationValue;
import lombok.ast.ArrayInitializer;
import lombok.ast.AstVisitor;
import lombok.ast.Block;
import lombok.ast.ConstructorDeclaration;
import lombok.ast.Expression;
import lombok.ast.ForwardingAstVisitor;
import lombok.ast.MethodDeclaration;
import lombok.ast.Modifiers;
import lombok.ast.Node;
import lombok.ast.Select;
import lombok.ast.StrictListAccessor;
import lombok.ast.StringLiteral;
import lombok.ast.TypeBody;
import lombok.ast.VariableDefinition;
import lombok.ast.VariableDefinitionEntry;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.uast.*;
import org.jetbrains.uast.check.UastAndroidContext;
import org.jetbrains.uast.check.UastAndroidUtils;
import org.jetbrains.uast.check.UastScanner;
import org.jetbrains.uast.java.JavaUastCallKinds;
import org.jetbrains.uast.visitor.UastVisitor;
/**
* Checks annotations to make sure they are valid
*/
public class AnnotationDetector extends Detector implements Detector.JavaScanner {
public class AnnotationDetector extends Detector implements UastScanner {
/** Placing SuppressLint on a local variable doesn't work for class-file based checks */
public static final Issue ISSUE = Issue.create(
"LocalSuppress", //$NON-NLS-1$
@@ -94,59 +79,45 @@ public class AnnotationDetector extends Detector implements Detector.JavaScanner
return Speed.FAST;
}
// ---- Implements JavaScanner ----
// ---- Implements UastScanner ----
@Override
public List<Class<? extends Node>> getApplicableNodeTypes() {
return Collections.<Class<? extends Node>>singletonList(Annotation.class);
}
@Override
public AstVisitor createJavaVisitor(@NonNull JavaContext context) {
public UastVisitor createUastVisitor(UastAndroidContext context) {
return new AnnotationChecker(context);
}
private static class AnnotationChecker extends ForwardingAstVisitor {
private final JavaContext mContext;
private static class AnnotationChecker extends UastVisitor {
private final UastAndroidContext mContext;
public AnnotationChecker(JavaContext context) {
public AnnotationChecker(UastAndroidContext context) {
mContext = context;
}
@Override
public boolean visitAnnotation(Annotation node) {
String type = node.astAnnotationTypeReference().getTypeName();
public boolean visitAnnotation(@NotNull UAnnotation node) {
String type = node.getName();
if (SUPPRESS_LINT.equals(type) || FQCN_SUPPRESS_LINT.equals(type)) {
Node parent = node.getParent();
if (parent instanceof Modifiers) {
parent = parent.getParent();
if (parent instanceof VariableDefinition) {
for (AnnotationElement element : node.astElements()) {
AnnotationValue valueNode = element.astValue();
if (valueNode == null) {
UElement parent = node.getParent();
if (parent instanceof UVariable) {
for (UNamedExpression element : node.getValueArguments()) {
UExpression valueNode = element.getExpression();
if (UastLiteralUtils.isStringLiteral(valueNode)) {
ULiteralExpression literal = (ULiteralExpression) valueNode;
String id = ((String)literal.getValue());
if (!checkId(node, id)) {
return super.visitAnnotation(node);
}
} else if (valueNode instanceof UCallExpression &&
((UCallExpression) valueNode).getKind() == JavaUastCallKinds.ARRAY_INITIALIZER) {
UCallExpression array = (UCallExpression) valueNode;
if (array.getValueArgumentCount() == 0) {
continue;
}
if (valueNode instanceof StringLiteral) {
StringLiteral literal = (StringLiteral) valueNode;
String id = literal.astValue();
if (!checkId(node, id)) {
return super.visitAnnotation(node);
}
} else if (valueNode instanceof ArrayInitializer) {
ArrayInitializer array = (ArrayInitializer) valueNode;
StrictListAccessor<Expression, ArrayInitializer> expressions =
array.astExpressions();
if (expressions == null) {
continue;
}
Iterator<Expression> arrayIterator = expressions.iterator();
while (arrayIterator.hasNext()) {
Expression arrayElement = arrayIterator.next();
if (arrayElement instanceof StringLiteral) {
String id = ((StringLiteral) arrayElement).astValue();
if (!checkId(node, id)) {
return super.visitAnnotation(node);
}
for (UExpression arrayElement : array.getValueArguments()) {
if (UastLiteralUtils.isStringLiteral(arrayElement)) {
String id = ((String)((ULiteralExpression)arrayElement).getValue());
if (!checkId(node, id)) {
return super.visitAnnotation(node);
}
}
}
@@ -155,32 +126,26 @@ public class AnnotationDetector extends Detector implements Detector.JavaScanner
}
}
return super.visitAnnotation(node);
return false;
}
private boolean checkId(Annotation node, String id) {
IssueRegistry registry = mContext.getDriver().getRegistry();
private boolean checkId(UAnnotation node, String id) {
IssueRegistry registry = mContext.getLintContext().getDriver().getRegistry();
Issue issue = registry.getIssue(id);
// Special-case the ApiDetector issue, since it does both source file analysis
// only on field references, and class file analysis on the rest, so we allow
// annotations outside of methods only on fields
if (issue != null && !issue.getImplementation().getScope().contains(Scope.JAVA_FILE)
if (issue != null && !issue.getImplementation().getScope().contains(Scope.SOURCE_FILE)
|| issue == ApiDetector.UNSUPPORTED) {
// Ensure that this isn't a field
Node parent = node.getParent();
UElement parent = node.getParent();
while (parent != null) {
if (parent instanceof MethodDeclaration
|| parent instanceof ConstructorDeclaration
|| parent instanceof Block) {
if (parent instanceof UFunction || parent instanceof UBlockExpression) {
break;
} else if (parent instanceof TypeBody) { // It's a field
return true;
} else if (issue == ApiDetector.UNSUPPORTED
&& parent instanceof VariableDefinition) {
VariableDefinition definition = (VariableDefinition) parent;
for (VariableDefinitionEntry entry : definition.astVariables()) {
Expression initializer = entry.astInitializer();
if (initializer instanceof Select) {
} else if (issue == ApiDetector.UNSUPPORTED && parent instanceof UDeclarationsExpression) {
UDeclarationsExpression declarations = (UDeclarationsExpression)parent;
for (UVariable var : declarations.getVariables()) {
if (var.getKind() != UastVariableKind.MEMBER && var.getInitializer() instanceof UQualifiedExpression) {
return true;
}
}
@@ -193,7 +158,7 @@ public class AnnotationDetector extends Detector implements Detector.JavaScanner
// This issue doesn't have AST access: annotations are not
// available for local variables or parameters
mContext.report(ISSUE, node, mContext.getLocation(node), String.format(
mContext.report(ISSUE, node, UastAndroidUtils.getLocation(node), String.format(
"The `@SuppressLint` annotation cannot be used on a local " +
"variable with the lint check '%1$s': move out to the " +
"surrounding method", id));
@@ -14,7 +14,7 @@
* limitations under the License.
*/
package com.android.tools.lint.checks;
package com.android.tools.klint.checks;
import org.xml.sax.SAXException;
@@ -14,7 +14,7 @@
* limitations under the License.
*/
package com.android.tools.lint.checks;
package com.android.tools.klint.checks;
import static com.android.SdkConstants.CONSTRUCTOR_NAME;
@@ -14,7 +14,7 @@
* limitations under the License.
*/
package com.android.tools.lint.checks;
package com.android.tools.klint.checks;
import static com.android.SdkConstants.ANDROID_PREFIX;
import static com.android.SdkConstants.ANDROID_THEME_PREFIX;
@@ -34,7 +34,6 @@ import static com.android.SdkConstants.CHECK_BOX;
import static com.android.SdkConstants.CLASS_CONSTRUCTOR;
import static com.android.SdkConstants.CONSTRUCTOR_NAME;
import static com.android.SdkConstants.PREFIX_ANDROID;
import static com.android.SdkConstants.R_CLASS;
import static com.android.SdkConstants.SWITCH;
import static com.android.SdkConstants.TAG;
import static com.android.SdkConstants.TAG_ITEM;
@@ -42,12 +41,10 @@ import static com.android.SdkConstants.TAG_STYLE;
import static com.android.SdkConstants.TARGET_API;
import static com.android.SdkConstants.TOOLS_URI;
import static com.android.SdkConstants.VIEW_TAG;
import static com.android.tools.lint.detector.api.ClassContext.getFqcn;
import static com.android.tools.lint.detector.api.ClassContext.getInternalName;
import static com.android.tools.lint.detector.api.LintUtils.getNextInstruction;
import static com.android.tools.lint.detector.api.Location.SearchDirection.BACKWARD;
import static com.android.tools.lint.detector.api.Location.SearchDirection.FORWARD;
import static com.android.tools.lint.detector.api.Location.SearchDirection.NEAREST;
import static com.android.tools.klint.detector.api.LintUtils.getNextInstruction;
import static com.android.tools.klint.detector.api.Location.SearchDirection.BACKWARD;
import static com.android.tools.klint.detector.api.Location.SearchDirection.FORWARD;
import static com.android.tools.klint.detector.api.Location.SearchDirection.NEAREST;
import static com.android.utils.SdkUtils.getResourceFieldName;
import com.android.SdkConstants;
@@ -56,32 +53,27 @@ import com.android.annotations.Nullable;
import com.android.resources.ResourceFolderType;
import com.android.sdklib.AndroidVersion;
import com.android.sdklib.SdkVersionInfo;
import com.android.tools.lint.client.api.IssueRegistry;
import com.android.tools.lint.client.api.JavaParser;
import com.android.tools.lint.client.api.LintDriver;
import com.android.tools.lint.detector.api.Category;
import com.android.tools.lint.detector.api.ClassContext;
import com.android.tools.lint.detector.api.Context;
import com.android.tools.lint.detector.api.DefaultPosition;
import com.android.tools.lint.detector.api.Detector;
import com.android.tools.lint.detector.api.Implementation;
import com.android.tools.lint.detector.api.Issue;
import com.android.tools.lint.detector.api.JavaContext;
import com.android.tools.lint.detector.api.LintUtils;
import com.android.tools.lint.detector.api.Location;
import com.android.tools.lint.detector.api.Location.SearchHints;
import com.android.tools.lint.detector.api.Position;
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.Speed;
import com.android.tools.lint.detector.api.TextFormat;
import com.android.tools.lint.detector.api.XmlContext;
import com.android.tools.klint.client.api.IssueRegistry;
import com.android.tools.klint.client.api.LintDriver;
import com.android.tools.klint.detector.api.Category;
import com.android.tools.klint.detector.api.ClassContext;
import com.android.tools.klint.detector.api.Context;
import com.android.tools.klint.detector.api.Detector;
import com.android.tools.klint.detector.api.Implementation;
import com.android.tools.klint.detector.api.Issue;
import com.android.tools.klint.detector.api.LintUtils;
import com.android.tools.klint.detector.api.Location;
import com.android.tools.klint.detector.api.Location.SearchHints;
import com.android.tools.klint.detector.api.ResourceXmlDetector;
import com.android.tools.klint.detector.api.Scope;
import com.android.tools.klint.detector.api.Severity;
import com.android.tools.klint.detector.api.Speed;
import com.android.tools.klint.detector.api.TextFormat;
import com.android.tools.klint.detector.api.XmlContext;
import com.android.utils.Pair;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import org.jetbrains.uast.*;
import org.jetbrains.uast.check.UastScanner;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.AbstractInsnNode;
@@ -91,64 +83,29 @@ import org.objectweb.asm.tree.FieldInsnNode;
import org.objectweb.asm.tree.FieldNode;
import org.objectweb.asm.tree.InsnList;
import org.objectweb.asm.tree.IntInsnNode;
import org.objectweb.asm.tree.JumpInsnNode;
import org.objectweb.asm.tree.LdcInsnNode;
import org.objectweb.asm.tree.LocalVariableNode;
import org.objectweb.asm.tree.LookupSwitchInsnNode;
import org.objectweb.asm.tree.MethodInsnNode;
import org.objectweb.asm.tree.MethodNode;
import org.objectweb.asm.tree.analysis.AnalyzerException;
import org.w3c.dom.Attr;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collection;
import java.util.EnumSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import lombok.ast.Annotation;
import lombok.ast.AnnotationElement;
import lombok.ast.AnnotationValue;
import lombok.ast.AstVisitor;
import lombok.ast.BinaryExpression;
import lombok.ast.Case;
import lombok.ast.Catch;
import lombok.ast.ClassDeclaration;
import lombok.ast.ConstructorDeclaration;
import lombok.ast.ConstructorInvocation;
import lombok.ast.Expression;
import lombok.ast.ForwardingAstVisitor;
import lombok.ast.If;
import lombok.ast.ImportDeclaration;
import lombok.ast.InlineIfExpression;
import lombok.ast.IntegralLiteral;
import lombok.ast.MethodDeclaration;
import lombok.ast.MethodInvocation;
import lombok.ast.Modifiers;
import lombok.ast.Select;
import lombok.ast.StrictListAccessor;
import lombok.ast.StringLiteral;
import lombok.ast.SuperConstructorInvocation;
import lombok.ast.Switch;
import lombok.ast.Try;
import lombok.ast.TypeReference;
import lombok.ast.VariableDefinition;
import lombok.ast.VariableDefinitionEntry;
import lombok.ast.VariableReference;
/**
* Looks for usages of APIs that are not supported in all the versions targeted
* by this application (according to its minimum API requirement in the manifest).
*/
public class ApiDetector extends ResourceXmlDetector
implements Detector.ClassScanner, Detector.JavaScanner {
implements Detector.ClassScanner, UastScanner {
/**
* Whether we flag variable, field, parameter and return type declarations of a type
@@ -1386,9 +1343,9 @@ public class ApiDetector extends ResourceXmlDetector
super.afterCheckProject(context);
}
// ---- Implements JavaScanner ----
// ---- Implements UastScanner ----
@Nullable
/* @Nullable
@Override
public AstVisitor createJavaVisitor(@NonNull JavaContext context) {
if (mApiDatabase == null) {
@@ -1413,7 +1370,7 @@ public class ApiDetector extends ResourceXmlDetector
return types;
}
/**
*//**
* Checks whether the given instruction is a benign usage of a constant defined in
* a later version of Android than the application's {@code minSdkVersion}.
*
@@ -1422,7 +1379,18 @@ public class ApiDetector extends ResourceXmlDetector
* @param owner the field owner
* @return true if the given usage is safe on older versions than the introduction
* level of the constant
*/
*//*
*/
public static boolean isBenignConstantUsage(
@Nullable UElement node,
@NonNull String name,
@NonNull String owner) {
return true;
}
/*
public static boolean isBenignConstantUsage(
@Nullable lombok.ast.Node node,
@NonNull String name,
@@ -1811,10 +1779,10 @@ public class ApiDetector extends ResourceXmlDetector
super.endVisit(node);
}
/**
*//**
* Checks a Java source field reference. Returns true if the field is known
* regardless of whether it's an invalid field or not
*/
*//*
private boolean checkField(
@NonNull lombok.ast.Node node,
@NonNull String name,
@@ -1902,12 +1870,12 @@ public class ApiDetector extends ResourceXmlDetector
return false;
}
/**
*//**
* Returns the minimum SDK to use according to the given AST node, or null
* if no {@code TargetApi} annotations were found
*
* @return the API level to use for this node, or -1
*/
*//*
public int getLocalMinSdk(@Nullable lombok.ast.Node scope) {
while (scope != null) {
Class<? extends lombok.ast.Node> type = scope.getClass();
@@ -1952,13 +1920,13 @@ public class ApiDetector extends ResourceXmlDetector
}
}
/**
*//**
* Returns the API level for the given AST node if specified with
* an {@code @TargetApi} annotation.
*
* @param modifiers the modifier to check
* @return the target API level, or -1 if not specified
*/
*//*
public static int getTargetApi(@Nullable Modifiers modifiers) {
if (modifiers == null) {
return -1;
@@ -2000,6 +1968,36 @@ public class ApiDetector extends ResourceXmlDetector
}
}
return -1;
}*/
public static int getTargetApi(List<UAnnotation> annotations) {
if (annotations == null) {
return -1;
}
for (UAnnotation annotation : annotations) {
if (annotation.matchesName(TARGET_API)) {
for (UNamedExpression element : annotation.getValueArguments()) {
UExpression valueNode = element.getExpression();
if (UastLiteralUtils.isIntegralLiteral(valueNode)) {
return (int) UastLiteralUtils.getLongValue((ULiteralExpression) valueNode);
} else if (UastLiteralUtils.isStringLiteral(valueNode)) {
String value = (String) ((ULiteralExpression) valueNode).getValue();
return SdkVersionInfo.getApiByBuildCode(value, true);
} else if (valueNode instanceof UQualifiedExpression) {
UQualifiedExpression select = (UQualifiedExpression) valueNode;
String codename = select.getSelector().renderString();
return SdkVersionInfo.getApiByBuildCode(codename, true);
} else if (valueNode instanceof USimpleReferenceExpression) {
USimpleReferenceExpression reference = (USimpleReferenceExpression) valueNode;
String codename = reference.getIdentifier();
return SdkVersionInfo.getApiByBuildCode(codename, true);
}
}
}
}
return -1;
}
@@ -2023,7 +2021,7 @@ public class ApiDetector extends ResourceXmlDetector
@NonNull ClassNode classNode,
@NonNull MethodNode method,
@NonNull AbstractInsnNode call,
int requiredApi) {
int requiredApi) { return false; } /*
assert requiredApi != -1;
if (!containsSimpleSdkCheck(method)) {
@@ -2126,10 +2124,10 @@ public class ApiDetector extends ResourceXmlDetector
return false;
}
/**
*//**
* Control flow graph which skips control flow edges that check
* a given SDK_VERSION requirement that is not met by a given call
*/
*//*
private static class ApiCheckGraph extends ControlFlowGraph {
private final int mRequiredApi;
@@ -2177,5 +2175,5 @@ public class ApiDetector extends ResourceXmlDetector
super.add(from, to);
}
}
}*/
}
@@ -14,7 +14,7 @@
* limitations under the License.
*/
package com.android.tools.lint.checks;
package com.android.tools.klint.checks;
import static com.android.SdkConstants.ANDROID_PKG;
import static com.android.SdkConstants.DOT_XML;
@@ -26,8 +26,8 @@ import com.android.sdklib.repository.FullRevision;
import com.android.sdklib.repository.descriptors.PkgType;
import com.android.sdklib.repository.local.LocalPkgInfo;
import com.android.sdklib.repository.local.LocalSdk;
import com.android.tools.lint.client.api.LintClient;
import com.android.tools.lint.detector.api.LintUtils;
import com.android.tools.klint.client.api.LintClient;
import com.android.tools.klint.detector.api.LintUtils;
import com.google.common.base.Charsets;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
@@ -14,7 +14,7 @@
* limitations under the License.
*/
package com.android.tools.lint.checks;
package com.android.tools.klint.checks;
import org.xml.sax.Attributes;
@@ -13,37 +13,37 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.tools.lint.checks;
package com.android.tools.klint.checks;
import static com.android.SdkConstants.APPCOMPAT_LIB_ARTIFACT;
import static com.android.SdkConstants.CLASS_ACTIVITY;
import static com.android.tools.lint.detector.api.TextFormat.RAW;
import static com.android.tools.klint.detector.api.TextFormat.RAW;
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
import com.android.tools.lint.client.api.JavaParser.ResolvedClass;
import com.android.tools.lint.client.api.JavaParser.ResolvedMethod;
import com.android.tools.lint.client.api.JavaParser.ResolvedNode;
import com.android.tools.lint.detector.api.Category;
import com.android.tools.lint.detector.api.Context;
import com.android.tools.lint.detector.api.Detector;
import com.android.tools.lint.detector.api.Implementation;
import com.android.tools.lint.detector.api.Issue;
import com.android.tools.lint.detector.api.JavaContext;
import com.android.tools.lint.detector.api.LintUtils;
import com.android.tools.lint.detector.api.Scope;
import com.android.tools.lint.detector.api.Severity;
import com.android.tools.lint.detector.api.Speed;
import com.android.tools.lint.detector.api.TextFormat;
import com.android.tools.klint.detector.api.Category;
import com.android.tools.klint.detector.api.Context;
import com.android.tools.klint.detector.api.Detector;
import com.android.tools.klint.detector.api.Implementation;
import com.android.tools.klint.detector.api.Issue;
import com.android.tools.klint.detector.api.LintUtils;
import com.android.tools.klint.detector.api.Scope;
import com.android.tools.klint.detector.api.Severity;
import com.android.tools.klint.detector.api.Speed;
import com.android.tools.klint.detector.api.TextFormat;
import java.util.Arrays;
import java.util.List;
import lombok.ast.AstVisitor;
import lombok.ast.ClassDeclaration;
import lombok.ast.MethodInvocation;
import org.jetbrains.uast.UCallExpression;
import org.jetbrains.uast.UClass;
import org.jetbrains.uast.UFunction;
import org.jetbrains.uast.UastUtils;
import org.jetbrains.uast.check.UastAndroidContext;
import org.jetbrains.uast.check.UastAndroidUtils;
import org.jetbrains.uast.check.UastScanner;
public class AppCompatCallDetector extends Detector implements Detector.JavaScanner {
public class AppCompatCallDetector extends Detector implements UastScanner {
public static final Issue ISSUE = Issue.create(
"AppCompatMethod",
"Using Wrong AppCompat Method",
@@ -82,23 +82,21 @@ public class AppCompatCallDetector extends Detector implements Detector.JavaScan
mDependsOnAppCompat = dependsOnAppCompat != null && dependsOnAppCompat;
}
@Nullable
@Override
public List<String> getApplicableMethodNames() {
public List<String> getApplicableFunctionNames() {
return Arrays.asList(
GET_ACTION_BAR,
START_ACTION_MODE,
SET_PROGRESS_BAR_VIS,
SET_PROGRESS_BAR_IN_VIS,
SET_PROGRESS_BAR_INDETERMINATE,
REQUEST_WINDOW_FEATURE);
GET_ACTION_BAR,
START_ACTION_MODE,
SET_PROGRESS_BAR_VIS,
SET_PROGRESS_BAR_IN_VIS,
SET_PROGRESS_BAR_INDETERMINATE,
REQUEST_WINDOW_FEATURE);
}
@Override
public void visitMethod(@NonNull JavaContext context, @Nullable AstVisitor visitor,
@NonNull MethodInvocation node) {
public void visitFunctionCall(UastAndroidContext context, UCallExpression node) {
if (mDependsOnAppCompat && isAppBarActivityCall(context, node)) {
String name = node.astName().astValue();
String name = node.getFunctionName();
String replace = null;
if (GET_ACTION_BAR.equals(name)) {
replace = "getSupportActionBar";
@@ -116,29 +114,22 @@ public class AppCompatCallDetector extends Detector implements Detector.JavaScan
if (replace != null) {
String message = String.format(ERROR_MESSAGE_FORMAT, replace, name);
context.report(ISSUE, node, context.getLocation(node), message);
context.report(ISSUE, node, UastAndroidUtils.getLocation(node), message);
}
}
}
private static boolean isAppBarActivityCall(@NonNull JavaContext context,
@NonNull MethodInvocation node) {
ResolvedNode resolved = context.resolve(node);
if (resolved instanceof ResolvedMethod) {
ResolvedMethod method = (ResolvedMethod) resolved;
ResolvedClass containingClass = method.getContainingClass();
if (containingClass.isSubclassOf(CLASS_ACTIVITY, false)) {
private static boolean isAppBarActivityCall(@NonNull UastAndroidContext context,
@NonNull UCallExpression node) {
UFunction resolved = node.resolve(context);
if (resolved != null) {
UClass containingClass = UastUtils.getContainingClass(resolved);
if (containingClass != null && containingClass.isSubclassOf(CLASS_ACTIVITY)) {
// Make sure that the calling context is a subclass of ActionBarActivity;
// we don't want to flag these calls if they are in non-appcompat activities
// such as PreferenceActivity (see b.android.com/58512)
ClassDeclaration surroundingClass = JavaContext.findSurroundingClass(node);
if (surroundingClass != null) {
ResolvedNode clz = context.resolve(surroundingClass);
return clz instanceof ResolvedClass &&
((ResolvedClass)clz).isSubclassOf(
"android.support.v7.app.ActionBarActivity",
false);
}
return UastUtils.getContainingClassOrEmpty(node)
.isSubclassOf("android.support.v7.app.ActionBarActivity");
}
}
return false;
@@ -14,24 +14,24 @@
* limitations under the License.
*/
package com.android.tools.lint.checks;
package com.android.tools.klint.checks;
import static com.android.SdkConstants.ANDROID_URI;
import static com.android.SdkConstants.ATTR_SHOW_AS_ACTION;
import com.android.annotations.NonNull;
import com.android.resources.ResourceFolderType;
import com.android.tools.lint.detector.api.Category;
import com.android.tools.lint.detector.api.Detector.JavaScanner;
import com.android.tools.lint.detector.api.Implementation;
import com.android.tools.lint.detector.api.Issue;
import com.android.tools.lint.detector.api.Project;
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.Speed;
import com.android.tools.lint.detector.api.XmlContext;
import com.android.tools.klint.detector.api.Category;
import com.android.tools.klint.detector.api.Implementation;
import com.android.tools.klint.detector.api.Issue;
import com.android.tools.klint.detector.api.Project;
import com.android.tools.klint.detector.api.ResourceXmlDetector;
import com.android.tools.klint.detector.api.Scope;
import com.android.tools.klint.detector.api.Severity;
import com.android.tools.klint.detector.api.Speed;
import com.android.tools.klint.detector.api.XmlContext;
import org.jetbrains.uast.check.UastScanner;
import org.w3c.dom.Attr;
import java.util.Collection;
@@ -43,7 +43,7 @@ import java.util.Collections;
* Using app:showAsAction instead of android:showAsAction leads to problems, but
* isn't caught by the API Detector since it's not in the Android namespace.
*/
public class AppCompatResourceDetector extends ResourceXmlDetector implements JavaScanner {
public class AppCompatResourceDetector extends ResourceXmlDetector implements UastScanner {
/** The main issue discovered by this detector */
public static final Issue ISSUE = Issue.create(
"AppCompatResource", //$NON-NLS-1$
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.tools.lint.checks;
package com.android.tools.klint.checks;
import static com.android.SdkConstants.ANDROID_URI;
import static com.android.SdkConstants.ATTR_HOST;
@@ -38,15 +38,15 @@ import com.android.ide.common.res2.AbstractResourceRepository;
import com.android.ide.common.res2.ResourceItem;
import com.android.ide.common.resources.ResourceUrl;
import com.android.resources.ResourceType;
import com.android.tools.lint.client.api.LintClient;
import com.android.tools.lint.detector.api.Category;
import com.android.tools.lint.detector.api.Detector;
import com.android.tools.lint.detector.api.Implementation;
import com.android.tools.lint.detector.api.Issue;
import com.android.tools.lint.detector.api.Project;
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 com.android.tools.klint.client.api.LintClient;
import com.android.tools.klint.detector.api.Category;
import com.android.tools.klint.detector.api.Detector;
import com.android.tools.klint.detector.api.Implementation;
import com.android.tools.klint.detector.api.Issue;
import com.android.tools.klint.detector.api.Project;
import com.android.tools.klint.detector.api.Scope;
import com.android.tools.klint.detector.api.Severity;
import com.android.tools.klint.detector.api.XmlContext;
import org.w3c.dom.Attr;
import org.w3c.dom.Element;
@@ -14,7 +14,7 @@
* limitations under the License.
*/
package com.android.tools.lint.checks;
package com.android.tools.klint.checks;
import static com.android.SdkConstants.ATTR_NAME;
@@ -30,19 +30,19 @@ import com.android.ide.common.res2.ResourceFile;
import com.android.ide.common.res2.ResourceItem;
import com.android.resources.ResourceFolderType;
import com.android.resources.ResourceType;
import com.android.tools.lint.client.api.LintClient;
import com.android.tools.lint.client.api.LintDriver;
import com.android.tools.lint.detector.api.Category;
import com.android.tools.lint.detector.api.Context;
import com.android.tools.lint.detector.api.Implementation;
import com.android.tools.lint.detector.api.Issue;
import com.android.tools.lint.detector.api.LintUtils;
import com.android.tools.lint.detector.api.Location;
import com.android.tools.lint.detector.api.Project;
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 com.android.tools.klint.client.api.LintClient;
import com.android.tools.klint.client.api.LintDriver;
import com.android.tools.klint.detector.api.Category;
import com.android.tools.klint.detector.api.Context;
import com.android.tools.klint.detector.api.Implementation;
import com.android.tools.klint.detector.api.Issue;
import com.android.tools.klint.detector.api.LintUtils;
import com.android.tools.klint.detector.api.Location;
import com.android.tools.klint.detector.api.Project;
import com.android.tools.klint.detector.api.ResourceXmlDetector;
import com.android.tools.klint.detector.api.Scope;
import com.android.tools.klint.detector.api.Severity;
import com.android.tools.klint.detector.api.XmlContext;
import com.android.utils.Pair;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Multimap;
@@ -14,35 +14,33 @@
* limitations under the License.
*/
package com.android.tools.lint.checks;
package com.android.tools.klint.checks;
import com.android.annotations.NonNull;
import com.android.tools.lint.detector.api.Category;
import com.android.tools.lint.detector.api.Context;
import com.android.tools.lint.detector.api.Detector;
import com.android.tools.lint.detector.api.Implementation;
import com.android.tools.lint.detector.api.Issue;
import com.android.tools.lint.detector.api.JavaContext;
import com.android.tools.lint.detector.api.Scope;
import com.android.tools.lint.detector.api.Severity;
import com.android.tools.klint.detector.api.Category;
import com.android.tools.klint.detector.api.Context;
import com.android.tools.klint.detector.api.Detector;
import com.android.tools.klint.detector.api.Implementation;
import com.android.tools.klint.detector.api.Issue;
import com.android.tools.klint.detector.api.Scope;
import com.android.tools.klint.detector.api.Severity;
import java.io.File;
import java.util.Collections;
import java.util.List;
import lombok.ast.Assert;
import lombok.ast.AstVisitor;
import lombok.ast.BinaryExpression;
import lombok.ast.BooleanLiteral;
import lombok.ast.Expression;
import lombok.ast.ForwardingAstVisitor;
import lombok.ast.Node;
import lombok.ast.NullLiteral;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.uast.*;
import org.jetbrains.uast.check.UastAndroidContext;
import org.jetbrains.uast.check.UastAndroidUtils;
import org.jetbrains.uast.check.UastScanner;
import org.jetbrains.uast.java.JavaSpecialExpressionKinds;
import org.jetbrains.uast.visitor.UastVisitor;
import static org.jetbrains.uast.UastLiteralUtils.isNullLiteral;
/**
* Looks for assertion usages.
*/
public class AssertDetector extends Detector implements Detector.JavaScanner {
public class AssertDetector extends Detector implements UastScanner {
/** Using assertions */
public static final Issue ISSUE = Issue.create(
"Assert", //$NON-NLS-1$
@@ -80,28 +78,30 @@ public class AssertDetector extends Detector implements Detector.JavaScanner {
return true;
}
// ---- Implements JavaScanner ----
// ---- Implements UastScanner ----
@Override
public List<Class<? extends Node>> getApplicableNodeTypes() {
return Collections.<Class<? extends Node>>singletonList(Assert.class);
}
@Override
public AstVisitor createJavaVisitor(@NonNull final JavaContext context) {
return new ForwardingAstVisitor() {
public UastVisitor createUastVisitor(final UastAndroidContext context) {
return new UastVisitor() {
@Override
public boolean visitAssert(Assert node) {
if (!context.getMainProject().isAndroidProject()) {
public boolean visitSpecialExpressionList(@NotNull USpecialExpressionList node) {
if (node.getKind() != JavaSpecialExpressionKinds.ASSERT) {
return true;
}
Expression assertion = node.astAssertion();
if (!context.getLintContext().getMainProject().isAndroidProject()) {
return true;
}
UExpression assertion = node.firstOrNull();
// Allow "assert true"; it's basically a no-op
if (assertion instanceof BooleanLiteral) {
Boolean b = ((BooleanLiteral) assertion).astValue();
if (b != null && b) {
return false;
if (assertion instanceof ULiteralExpression) {
ULiteralExpression literal = (ULiteralExpression) assertion;
if (literal.isBoolean()) {
Boolean b = ((Boolean)literal.getValue());
if (b != null && b) {
return false;
}
}
} else {
// Allow assertions of the form "assert foo != null" because they are often used
@@ -114,8 +114,8 @@ public class AssertDetector extends Detector implements Detector.JavaScanner {
}
}
String message
= "Assertions are unreliable. Use `BuildConfig.DEBUG` conditional checks instead.";
context.report(ISSUE, node, context.getLocation(node), message);
= "Assertions are unreliable. Use `BuildConfig.DEBUG` conditional checks instead.";
context.report(ISSUE, node, UastAndroidUtils.getLocation(node), message);
return false;
}
};
@@ -126,14 +126,14 @@ public class AssertDetector extends Detector implements Detector.JavaScanner {
* true for expressions like "a != null" and "a != null && b != null" and
* "b == null || c != null".
*/
private static boolean isNullCheck(Expression expression) {
if (expression instanceof BinaryExpression) {
BinaryExpression binExp = (BinaryExpression) expression;
if (binExp.astLeft() instanceof NullLiteral ||
binExp.astRight() instanceof NullLiteral) {
private static boolean isNullCheck(UExpression expression) {
if (expression instanceof UBinaryExpression) {
UBinaryExpression binExp = (UBinaryExpression) expression;
if (isNullLiteral(binExp.getLeftOperand()) ||
isNullLiteral(binExp.getRightOperand())) {
return true;
} else {
return isNullCheck(binExp.astLeft()) && isNullCheck(binExp.astRight());
return isNullCheck(binExp.getLeftOperand()) && isNullCheck(binExp.getRightOperand());
}
} else {
return false;
@@ -14,13 +14,13 @@
* limitations under the License.
*/
package com.android.tools.lint.checks;
package com.android.tools.klint.checks;
import com.android.annotations.NonNull;
import com.android.annotations.VisibleForTesting;
import com.android.tools.lint.client.api.IssueRegistry;
import com.android.tools.lint.detector.api.Issue;
import com.android.tools.lint.detector.api.Scope;
import com.android.tools.klint.client.api.IssueRegistry;
import com.android.tools.klint.detector.api.Issue;
import com.android.tools.klint.detector.api.Scope;
import java.util.ArrayList;
import java.util.Collections;
@@ -283,7 +283,7 @@ public class BuiltinIssueRegistry extends IssueRegistry {
initialSize += 10;
}
if (scope.contains(Scope.JAVA_FILE)) {
if (scope.contains(Scope.SOURCE_FILE)) {
initialSize += 55;
} else if (scope.contains(Scope.CLASS_FILE)) {
initialSize += 15;
@@ -14,7 +14,7 @@
* limitations under the License.
*/
package com.android.tools.lint.checks;
package com.android.tools.klint.checks;
import static com.android.SdkConstants.ANDROID_STRING_PREFIX;
import static com.android.SdkConstants.ANDROID_URI;
@@ -41,17 +41,17 @@ import static com.android.SdkConstants.VALUE_VERTICAL;
import com.android.SdkConstants;
import com.android.annotations.NonNull;
import com.android.resources.ResourceFolderType;
import com.android.tools.lint.detector.api.Category;
import com.android.tools.lint.detector.api.Context;
import com.android.tools.lint.detector.api.Implementation;
import com.android.tools.lint.detector.api.Issue;
import com.android.tools.lint.detector.api.LintUtils;
import com.android.tools.lint.detector.api.Location;
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.Speed;
import com.android.tools.lint.detector.api.XmlContext;
import com.android.tools.klint.detector.api.Category;
import com.android.tools.klint.detector.api.Context;
import com.android.tools.klint.detector.api.Implementation;
import com.android.tools.klint.detector.api.Issue;
import com.android.tools.klint.detector.api.LintUtils;
import com.android.tools.klint.detector.api.Location;
import com.android.tools.klint.detector.api.ResourceXmlDetector;
import com.android.tools.klint.detector.api.Scope;
import com.android.tools.klint.detector.api.Severity;
import com.android.tools.klint.detector.api.Speed;
import com.android.tools.klint.detector.api.XmlContext;
import org.w3c.dom.Attr;
import org.w3c.dom.Element;
@@ -14,21 +14,21 @@
* limitations under the License.
*/
package com.android.tools.lint.checks;
package com.android.tools.klint.checks;
import static com.android.SdkConstants.ATTR_NAME;
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
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.Location;
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.Speed;
import com.android.tools.lint.detector.api.XmlContext;
import com.android.tools.klint.detector.api.Category;
import com.android.tools.klint.detector.api.Implementation;
import com.android.tools.klint.detector.api.Issue;
import com.android.tools.klint.detector.api.Location;
import com.android.tools.klint.detector.api.ResourceXmlDetector;
import com.android.tools.klint.detector.api.Scope;
import com.android.tools.klint.detector.api.Severity;
import com.android.tools.klint.detector.api.Speed;
import com.android.tools.klint.detector.api.XmlContext;
import org.w3c.dom.Attr;
@@ -14,42 +14,37 @@
* limitations under the License.
*/
package com.android.tools.lint.checks;
package com.android.tools.klint.checks;
import static com.android.SdkConstants.CLASS_VIEW;
import static com.android.SdkConstants.SUPPORT_ANNOTATIONS_PREFIX;
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
import com.android.tools.lint.client.api.JavaParser;
import com.android.tools.lint.client.api.JavaParser.ResolvedMethod;
import com.android.tools.lint.client.api.JavaParser.ResolvedNode;
import com.android.tools.lint.detector.api.Category;
import com.android.tools.lint.detector.api.Context;
import com.android.tools.lint.detector.api.Detector;
import com.android.tools.lint.detector.api.Implementation;
import com.android.tools.lint.detector.api.Issue;
import com.android.tools.lint.detector.api.JavaContext;
import com.android.tools.lint.detector.api.Location;
import com.android.tools.lint.detector.api.Scope;
import com.android.tools.lint.detector.api.Severity;
import com.android.tools.lint.detector.api.Speed;
import com.android.tools.klint.detector.api.Category;
import com.android.tools.klint.detector.api.Context;
import com.android.tools.klint.detector.api.Detector;
import com.android.tools.klint.detector.api.Implementation;
import com.android.tools.klint.detector.api.Issue;
import com.android.tools.klint.detector.api.Location;
import com.android.tools.klint.detector.api.Scope;
import com.android.tools.klint.detector.api.Severity;
import com.android.tools.klint.detector.api.Speed;
import java.io.File;
import java.util.Collections;
import java.util.List;
import lombok.ast.AstVisitor;
import lombok.ast.ForwardingAstVisitor;
import lombok.ast.MethodDeclaration;
import lombok.ast.MethodInvocation;
import lombok.ast.Node;
import lombok.ast.Super;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.uast.*;
import org.jetbrains.uast.check.UastAndroidContext;
import org.jetbrains.uast.check.UastAndroidUtils;
import org.jetbrains.uast.check.UastScanner;
import org.jetbrains.uast.visitor.UastVisitor;
/**
* Makes sure that methods call super when overriding methods.
*/
public class CallSuperDetector extends Detector implements Detector.JavaScanner {
public class CallSuperDetector extends Detector implements UastScanner {
private static final String CALL_SUPER_ANNOTATION = SUPPORT_ANNOTATIONS_PREFIX + "CallSuper"; //$NON-NLS-1$
private static final String ON_DETACHED_FROM_WINDOW = "onDetachedFromWindow"; //$NON-NLS-1$
private static final String ON_VISIBILITY_CHANGED = "onVisibilityChanged"; //$NON-NLS-1$
@@ -86,40 +81,29 @@ public class CallSuperDetector extends Detector implements Detector.JavaScanner
return Speed.FAST;
}
// ---- Implements JavaScanner ----
// ---- Implements UastScanner ----
@Override
public List<Class<? extends Node>> getApplicableNodeTypes() {
return Collections.<Class<? extends Node>>singletonList(MethodDeclaration.class);
}
@Override
public AstVisitor createJavaVisitor(@NonNull final JavaContext context) {
return new ForwardingAstVisitor() {
public UastVisitor createUastVisitor(final UastAndroidContext context) {
return new UastVisitor() {
@Override
public boolean visitMethodDeclaration(MethodDeclaration node) {
ResolvedNode resolved = context.resolve(node);
if (resolved instanceof ResolvedMethod) {
ResolvedMethod method = (ResolvedMethod) resolved;
checkCallSuper(context, node, method);
}
public boolean visitFunction(@NotNull UFunction node) {
checkCallSuper(context, node);
return false;
}
};
}
private static void checkCallSuper(@NonNull JavaContext context,
@NonNull MethodDeclaration declaration,
@NonNull ResolvedMethod method) {
private static void checkCallSuper(@NonNull UastAndroidContext context,
@NonNull UFunction declaration) {
ResolvedMethod superMethod = getRequiredSuperMethod(method);
UFunction superMethod = getRequiredSuperMethod(context, declaration);
if (superMethod != null) {
if (!SuperCallVisitor.callsSuper(context, declaration, superMethod)) {
String methodName = method.getName();
String methodName = declaration.getName();
String message = "Overriding method should call `super."
+ methodName + "`";
Location location = context.getLocation(declaration.astMethodName());
Location location = UastAndroidUtils.getLocation(declaration.getNameElement());
context.report(ISSUE, declaration, location, message);
}
}
@@ -130,8 +114,8 @@ public class CallSuperDetector extends Detector implements Detector.JavaScanner
* to be invoked, and if so, returns it (otherwise returns null)
*/
@Nullable
private static ResolvedMethod getRequiredSuperMethod(
@NonNull ResolvedMethod method) {
private static UFunction getRequiredSuperMethod(UastAndroidContext context,
@NonNull UFunction method) {
String name = method.getName();
if (ON_DETACHED_FROM_WINDOW.equals(name)) {
@@ -140,84 +124,89 @@ public class CallSuperDetector extends Detector implements Detector.JavaScanner
// is still dangerous if supporting older versions so flag
// this for now (should make annotation carry metadata like
// compileSdkVersion >= N).
if (!method.getContainingClass().isSubclassOf(CLASS_VIEW, false)) {
if (!UastUtils.getContainingClassOrEmpty(method).isSubclassOf(CLASS_VIEW)) {
return null;
}
return method.getSuperMethod();
List<UFunction> superFunctions = method.getSuperFunctions(context);
return superFunctions.isEmpty() ? null : superFunctions.get(0);
} else if (ON_VISIBILITY_CHANGED.equals(name)) {
// From Android Wear API; doesn't yet have an annotation
// but we want to enforce this right away until the AAR
// is updated to supply it once @CallSuper is available in
// the support library
if (!method.getContainingClass().isSubclassOf(
"android.support.wearable.watchface.WatchFaceService.Engine", false)) {
if (!UastUtils.getContainingClassOrEmpty(method).isSubclassOf(
"android.support.wearable.watchface.WatchFaceService.Engine")) {
return null;
}
return method.getSuperMethod();
List<UFunction> superFunctions = method.getSuperFunctions(context);
return superFunctions.isEmpty() ? null : superFunctions.get(0);
}
// Look up annotations metadata
ResolvedMethod directSuper = method.getSuperMethod();
ResolvedMethod superMethod = directSuper;
List<UFunction> superFunctions = method.getSuperFunctions(context);
UFunction directSuper = superFunctions.isEmpty() ? null : superFunctions.get(0);
UFunction superMethod = directSuper;
while (superMethod != null) {
Iterable<JavaParser.ResolvedAnnotation> annotations = superMethod.getAnnotations();
for (JavaParser.ResolvedAnnotation annotation : annotations) {
annotation = SupportAnnotationDetector.getRelevantAnnotation(annotation);
for (UAnnotation annotation : superMethod.getAnnotations()) {
annotation = SupportAnnotationDetector.getRelevantAnnotation(context, annotation);
if (annotation != null) {
String signature = annotation.getSignature();
String signature = annotation.getFqName();
if (CALL_SUPER_ANNOTATION.equals(signature)) {
return directSuper;
} else if (signature.endsWith(".OverrideMustInvoke")) {
} else if (signature != null && signature.endsWith(".OverrideMustInvoke")) {
// Handle findbugs annotation on the fly too
return directSuper;
}
}
}
superMethod = superMethod.getSuperMethod();
superFunctions = superMethod.getSuperFunctions(context);
superMethod = superFunctions.isEmpty() ? null : superFunctions.get(0);
}
return null;
}
/** Visits a method and determines whether the method calls its super method */
private static class SuperCallVisitor extends ForwardingAstVisitor {
private final JavaContext mContext;
private final ResolvedMethod mMethod;
private static class SuperCallVisitor extends UastVisitor {
private final UastAndroidContext mContext;
private final String mMethodFqName;
private boolean mCallsSuper;
public static boolean callsSuper(
@NonNull JavaContext context,
@NonNull MethodDeclaration methodDeclaration,
@NonNull ResolvedMethod method) {
SuperCallVisitor visitor = new SuperCallVisitor(context, method);
methodDeclaration.accept(visitor);
@NonNull UastAndroidContext context,
@NonNull UFunction methodDeclaration,
@NonNull UFunction superMethod) {
SuperCallVisitor visitor = new SuperCallVisitor(context, superMethod);
visitor.process(methodDeclaration);
return visitor.mCallsSuper;
}
private SuperCallVisitor(@NonNull JavaContext context, @NonNull ResolvedMethod method) {
private SuperCallVisitor(@NonNull UastAndroidContext context, @NonNull UFunction method) {
mContext = context;
mMethod = method;
mMethodFqName = UastUtils.getFqName(method);
}
@Override
public boolean visitSuper(Super node) {
ResolvedNode resolved = null;
if (node.getParent() instanceof MethodInvocation) {
resolved = mContext.resolve(node.getParent());
}
if (resolved == null) {
resolved = mContext.resolve(node);
}
if (mMethod.equals(resolved)) {
mCallsSuper = true;
return true;
public boolean visitQualifiedExpression(@NotNull UQualifiedExpression node) {
UExpression receiver = node.getReceiver();
UExpression selector = node.getSelector();
if (receiver instanceof USuperExpression && selector instanceof UCallExpression) {
UFunction resolvedFunction = ((UCallExpression) selector).resolve(mContext);
if (mMethodFqName.equals(UastUtils.getFqName(resolvedFunction))) {
mCallsSuper = true;
return true;
}
}
return false;
}
@Override
public boolean visitNode(Node node) {
return mCallsSuper || super.visitNode(node);
public void process(@NotNull UElement element) {
if (!mCallsSuper) {
super.process(element);
}
}
}
}
@@ -14,7 +14,7 @@
* limitations under the License.
*/
package com.android.tools.lint.checks;
package com.android.tools.klint.checks;
import static com.android.SdkConstants.GRID_VIEW;
import static com.android.SdkConstants.HORIZONTAL_SCROLL_VIEW;
@@ -23,15 +23,15 @@ import static com.android.SdkConstants.REQUEST_FOCUS;
import static com.android.SdkConstants.SCROLL_VIEW;
import com.android.annotations.NonNull;
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.LayoutDetector;
import com.android.tools.lint.detector.api.LintUtils;
import com.android.tools.lint.detector.api.Scope;
import com.android.tools.lint.detector.api.Severity;
import com.android.tools.lint.detector.api.Speed;
import com.android.tools.lint.detector.api.XmlContext;
import com.android.tools.klint.detector.api.Category;
import com.android.tools.klint.detector.api.Implementation;
import com.android.tools.klint.detector.api.Issue;
import com.android.tools.klint.detector.api.LayoutDetector;
import com.android.tools.klint.detector.api.LintUtils;
import com.android.tools.klint.detector.api.Scope;
import com.android.tools.klint.detector.api.Severity;
import com.android.tools.klint.detector.api.Speed;
import com.android.tools.klint.detector.api.XmlContext;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
@@ -14,36 +14,31 @@
* limitations under the License.
*/
package com.android.tools.lint.checks;
package com.android.tools.klint.checks;
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
import com.android.tools.lint.client.api.JavaParser;
import com.android.tools.lint.detector.api.Category;
import com.android.tools.lint.detector.api.Detector;
import com.android.tools.lint.detector.api.Implementation;
import com.android.tools.lint.detector.api.Issue;
import com.android.tools.lint.detector.api.JavaContext;
import com.android.tools.lint.detector.api.Scope;
import com.android.tools.lint.detector.api.Severity;
import com.android.tools.lint.detector.api.Speed;
import com.android.tools.klint.detector.api.Category;
import com.android.tools.klint.detector.api.Detector;
import com.android.tools.klint.detector.api.Implementation;
import com.android.tools.klint.detector.api.Issue;
import com.android.tools.klint.detector.api.Scope;
import com.android.tools.klint.detector.api.Severity;
import com.android.tools.klint.detector.api.Speed;
import com.google.common.collect.Sets;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import lombok.ast.AstVisitor;
import lombok.ast.Expression;
import lombok.ast.MethodInvocation;
import lombok.ast.Node;
import lombok.ast.StrictListAccessor;
import lombok.ast.StringLiteral;
import org.jetbrains.uast.*;
import org.jetbrains.uast.check.UastAndroidContext;
import org.jetbrains.uast.check.UastAndroidUtils;
import org.jetbrains.uast.check.UastScanner;
/**
* Ensures that Cipher.getInstance is not called with AES as the parameter.
*/
public class CipherGetInstanceDetector extends Detector implements Detector.JavaScanner {
public class CipherGetInstanceDetector extends Detector implements UastScanner {
public static final Issue ISSUE = Issue.create(
"GetInstance", //$NON-NLS-1$
"Cipher.getInstance with ECB",
@@ -69,59 +64,56 @@ public class CipherGetInstanceDetector extends Detector implements Detector.Java
return Speed.FAST;
}
// ---- Implements JavaScanner ----
// ---- Implements UastScanner ----
@Nullable
@Override
public List<String> getApplicableMethodNames() {
public List<String> getApplicableFunctionNames() {
return Collections.singletonList(GET_INSTANCE);
}
@Override
public void visitMethod(@NonNull JavaContext context, @Nullable AstVisitor visitor,
@NonNull MethodInvocation node) {
// Ignore if the method doesn't fit our description.
JavaParser.ResolvedNode resolved = context.resolve(node);
if (!(resolved instanceof JavaParser.ResolvedMethod)) {
public void visitFunctionCall(UastAndroidContext context, UCallExpression node) {
UClass containingClass = UastUtils.getContainingClass(node);
if (containingClass == null || !containingClass.isSubclassOf(CIPHER)) {
return;
}
JavaParser.ResolvedMethod method = (JavaParser.ResolvedMethod) resolved;
if (!method.getContainingClass().isSubclassOf(CIPHER, false)) {
return;
}
StrictListAccessor<Expression, MethodInvocation> argumentList = node.astArguments();
if (argumentList != null && argumentList.size() == 1) {
Expression expression = argumentList.first();
if (expression instanceof StringLiteral) {
StringLiteral argument = (StringLiteral)expression;
String parameter = argument.astValue();
List<UExpression> argumentList = node.getValueArguments();
if (argumentList.size() == 1) {
UExpression expression = argumentList.get(0);
if (expression instanceof ULiteralExpression) {
ULiteralExpression argument = (ULiteralExpression)expression;
String parameter = argument.getText();
checkParameter(context, node, argument, parameter, false);
} else {
JavaParser.ResolvedNode resolve = context.resolve(expression);
if (resolve instanceof JavaParser.ResolvedField) {
JavaParser.ResolvedField field = (JavaParser.ResolvedField) resolve;
Object value = field.getValue();
if (value instanceof String) {
checkParameter(context, node, expression, (String)value, true);
} else if (expression instanceof UResolvable) {
UDeclaration declaration = ((UResolvable)expression).resolve(context);
if (declaration instanceof UVariable) {
UVariable field = (UVariable) declaration;
UExpression initializer = field.getInitializer();
if (initializer != null) {
Object value = initializer.evaluate();
if (value instanceof String) {
checkParameter(context, node, expression, (String)value, true);
}
}
}
}
}
}
private static void checkParameter(@NonNull JavaContext context,
@NonNull MethodInvocation call, @NonNull Node node, @NonNull String value,
boolean includeValue) {
private static void checkParameter(@NonNull UastAndroidContext context,
@NonNull UCallExpression call, @NonNull UExpression arg, @NonNull String value,
boolean includeValue) {
if (ALGORITHM_ONLY.contains(value)) {
String message = "`Cipher.getInstance` should not be called without setting the"
+ " encryption mode and padding";
context.report(ISSUE, call, context.getLocation(node), message);
context.report(ISSUE, call, UastAndroidUtils.getLocation(arg), message);
} else if (value.contains(ECB)) {
String message = "ECB encryption mode should not be used";
if (includeValue) {
message += " (was \"" + value + "\")";
}
context.report(ISSUE, call, context.getLocation(node), message);
context.report(ISSUE, call, UastAndroidUtils.getLocation(arg), message);
}
}
}
@@ -14,50 +14,36 @@
* limitations under the License.
*/
package com.android.tools.lint.checks;
package com.android.tools.klint.checks;
import static com.android.SdkConstants.CLASS_CONTEXT;
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
import com.android.tools.lint.client.api.JavaParser.ResolvedClass;
import com.android.tools.lint.client.api.JavaParser.ResolvedField;
import com.android.tools.lint.client.api.JavaParser.ResolvedMethod;
import com.android.tools.lint.client.api.JavaParser.ResolvedNode;
import com.android.tools.lint.client.api.JavaParser.ResolvedVariable;
import com.android.tools.lint.client.api.JavaParser.TypeDescriptor;
import com.android.tools.lint.detector.api.Category;
import com.android.tools.lint.detector.api.Detector;
import com.android.tools.lint.detector.api.Detector.JavaScanner;
import com.android.tools.lint.detector.api.Implementation;
import com.android.tools.lint.detector.api.Issue;
import com.android.tools.lint.detector.api.JavaContext;
import com.android.tools.lint.detector.api.Location;
import com.android.tools.lint.detector.api.Scope;
import com.android.tools.lint.detector.api.Severity;
import com.android.tools.klint.detector.api.Category;
import com.android.tools.klint.detector.api.Detector;
import com.android.tools.klint.detector.api.Implementation;
import com.android.tools.klint.detector.api.Issue;
import com.android.tools.klint.detector.api.Location;
import com.android.tools.klint.detector.api.Scope;
import com.android.tools.klint.detector.api.Severity;
import com.google.common.collect.Lists;
import java.util.Arrays;
import java.util.List;
import lombok.ast.AstVisitor;
import lombok.ast.BinaryExpression;
import lombok.ast.BinaryOperator;
import lombok.ast.ConstructorInvocation;
import lombok.ast.Expression;
import lombok.ast.ForwardingAstVisitor;
import lombok.ast.MethodInvocation;
import lombok.ast.Node;
import lombok.ast.Return;
import lombok.ast.StrictListAccessor;
import lombok.ast.VariableDefinitionEntry;
import lombok.ast.VariableReference;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.uast.*;
import org.jetbrains.uast.check.UastAndroidContext;
import org.jetbrains.uast.check.UastAndroidUtils;
import org.jetbrains.uast.check.UastScanner;
import org.jetbrains.uast.visitor.UastVisitor;
/**
* Checks for missing {@code recycle} calls on resources that encourage it, and
* for missing {@code commit} calls on FragmentTransactions, etc.
*/
public class CleanupDetector extends Detector implements JavaScanner {
public class CleanupDetector extends Detector implements UastScanner {
private static final Implementation IMPLEMENTATION = new Implementation(
CleanupDetector.class,
@@ -140,26 +126,26 @@ public class CleanupDetector extends Detector implements JavaScanner {
public CleanupDetector() {
}
// ---- Implements JavaScanner ----
// ---- Implements UastScanner ----
@Nullable
@Override
public List<String> getApplicableMethodNames() {
public List<String> getApplicableFunctionNames() {
return Arrays.asList(
// FragmentManager commit check
BEGIN_TRANSACTION,
// FragmentManager commit check
BEGIN_TRANSACTION,
// Recycle check
OBTAIN, OBTAIN_NO_HISTORY,
OBTAIN_STYLED_ATTRIBUTES,
OBTAIN_ATTRIBUTES,
OBTAIN_TYPED_ARRAY,
// Recycle check
OBTAIN, OBTAIN_NO_HISTORY,
OBTAIN_STYLED_ATTRIBUTES,
OBTAIN_ATTRIBUTES,
OBTAIN_TYPED_ARRAY,
// Release check
ACQUIRE_CPC,
// Release check
ACQUIRE_CPC,
// Cursor close check
QUERY, RAW_QUERY, QUERY_WITH_FACTORY, RAW_QUERY_WITH_FACTORY
// Cursor close check
QUERY, RAW_QUERY, QUERY_WITH_FACTORY, RAW_QUERY_WITH_FACTORY
);
}
@@ -170,10 +156,8 @@ public class CleanupDetector extends Detector implements JavaScanner {
}
@Override
public void visitMethod(@NonNull JavaContext context, @Nullable AstVisitor visitor,
@NonNull MethodInvocation node) {
String name = node.astName().astValue();
public void visitFunctionCall(UastAndroidContext context, UCallExpression node) {
String name = node.getFunctionName();
if (BEGIN_TRANSACTION.equals(name)) {
checkTransactionCommits(context, node);
} else {
@@ -182,48 +166,53 @@ public class CleanupDetector extends Detector implements JavaScanner {
}
@Override
public void visitConstructor(@NonNull JavaContext context, @Nullable AstVisitor visitor,
@NonNull ConstructorInvocation node, @NonNull ResolvedMethod constructor) {
checkRecycled(context, node, constructor.getContainingClass().getSignature(), RELEASE);
public void visitConstructor(UastAndroidContext context, UCallExpression functionCall, UFunction constructor) {
UClass containingClass = UastUtils.getContainingClass(constructor);
if (containingClass != null) {
checkRecycled(context, functionCall, containingClass.getFqName(), RELEASE);
}
}
private static void checkResourceRecycled(@NonNull JavaContext context,
@NonNull MethodInvocation node, @NonNull String name) {
private static void checkResourceRecycled(@NonNull UastAndroidContext context,
@NonNull UCallExpression node, @NonNull String name) {
// Recycle detector
ResolvedNode resolved = context.resolve(node);
if (!(resolved instanceof ResolvedMethod)) {
UFunction method = node.resolve(context);
if (method == null) {
return;
}
ResolvedMethod method = (ResolvedMethod) resolved;
ResolvedClass containingClass = method.getContainingClass();
UClass containingClass = UastUtils.getContainingClass(method);
if (containingClass == null) {
return;
}
if ((OBTAIN.equals(name) || OBTAIN_NO_HISTORY.equals(name)) &&
containingClass.isSubclassOf(MOTION_EVENT_CLS, false)) {
containingClass.isSubclassOf(MOTION_EVENT_CLS)) {
checkRecycled(context, node, MOTION_EVENT_CLS, RECYCLE);
} else if (OBTAIN.equals(name) && containingClass.isSubclassOf(PARCEL_CLS, false)) {
} else if (OBTAIN.equals(name) && containingClass.isSubclassOf(PARCEL_CLS)) {
checkRecycled(context, node, PARCEL_CLS, RECYCLE);
} else if (OBTAIN.equals(name) &&
containingClass.isSubclassOf(VELOCITY_TRACKER_CLS, false)) {
containingClass.isSubclassOf(VELOCITY_TRACKER_CLS)) {
checkRecycled(context, node, VELOCITY_TRACKER_CLS, RECYCLE);
} else if ((OBTAIN_STYLED_ATTRIBUTES.equals(name)
|| OBTAIN_ATTRIBUTES.equals(name)
|| OBTAIN_TYPED_ARRAY.equals(name)) &&
(containingClass.isSubclassOf(CLASS_CONTEXT, false) ||
containingClass.isSubclassOf(RESOURCES_CLS, false))) {
TypeDescriptor returnType = method.getReturnType();
if (returnType != null && returnType.matchesSignature(TYPED_ARRAY_CLS)) {
(containingClass.isSubclassOf(CLASS_CONTEXT) ||
containingClass.isSubclassOf(RESOURCES_CLS))) {
UType returnType = method.getReturnType();
if (returnType != null && returnType.matchesFqName(TYPED_ARRAY_CLS)) {
checkRecycled(context, node, TYPED_ARRAY_CLS, RECYCLE);
}
} else if (ACQUIRE_CPC.equals(name) && containingClass.isSubclassOf(
CONTENT_RESOLVER_CLS, false)) {
CONTENT_RESOLVER_CLS)) {
checkRecycled(context, node, CONTENT_PROVIDER_CLIENT_CLS, RELEASE);
} else if ((QUERY.equals(name)
|| RAW_QUERY.equals(name)
|| QUERY_WITH_FACTORY.equals(name)
|| RAW_QUERY_WITH_FACTORY.equals(name))
&& (containingClass.isSubclassOf(SQLITE_DATABASE_CLS, false) ||
containingClass.isSubclassOf(CONTENT_RESOLVER_CLS, false) ||
containingClass.isSubclassOf(CONTENT_PROVIDER_CLS, false) ||
containingClass.isSubclassOf(CONTENT_PROVIDER_CLIENT_CLS, false))) {
&& (containingClass.isSubclassOf(SQLITE_DATABASE_CLS) ||
containingClass.isSubclassOf(CONTENT_RESOLVER_CLS) ||
containingClass.isSubclassOf(CONTENT_PROVIDER_CLS) ||
containingClass.isSubclassOf(CONTENT_PROVIDER_CLIENT_CLS))) {
// Other potential cursors-returning methods that should be tracked:
// android.app.DownloadManager#query
// android.content.ContentProviderClient#query
@@ -242,34 +231,35 @@ public class CleanupDetector extends Detector implements JavaScanner {
}
}
private static void checkRecycled(@NonNull final JavaContext context, @NonNull Node node,
private static void checkRecycled(@NonNull final UastAndroidContext context, @NonNull UElement node,
@NonNull final String recycleType, @NonNull final String recycleName) {
ResolvedVariable boundVariable = getVariable(context, node);
UVariable boundVariable = getVariable(context, node);
if (boundVariable == null) {
return;
}
Node method = JavaContext.findSurroundingMethod(node);
UFunction method = UastUtils.getContainingFunction(node);
if (method == null) {
return;
}
FinishVisitor visitor = new FinishVisitor(context, boundVariable) {
@Override
protected boolean isCleanupCall(@NonNull MethodInvocation call) {
String methodName = call.astName().astValue();
protected boolean isCleanupCall(@NonNull UCallExpression call) {
String methodName = call.getFunctionName();
if (!recycleName.equals(methodName)) {
return false;
}
ResolvedNode resolved = mContext.resolve(call);
if (resolved instanceof ResolvedMethod) {
ResolvedClass containingClass = ((ResolvedMethod) resolved).getContainingClass();
if (containingClass.isSubclassOf(recycleType, false)) {
UDeclaration resolved = call.resolve(mContext);
if (resolved != null) {
UClass containingClass = UastUtils.getContainingClassOrEmpty(resolved);
if (containingClass.isSubclassOf(recycleType)) {
// Yes, called the right recycle() method; now make sure
// we're calling it on the right variable
Expression operand = call.astOperand();
if (operand != null) {
resolved = mContext.resolve(operand);
UElement parent = call.getParent();
if (parent instanceof UQualifiedExpression) {
UExpression operand = ((UQualifiedExpression) parent).getReceiver();
resolved = UastUtils.resolveIfCan(operand, mContext);
//noinspection SuspiciousMethodCalls
if (resolved != null && mVariables.contains(resolved)) {
return true;
@@ -281,7 +271,7 @@ public class CleanupDetector extends Detector implements JavaScanner {
}
};
method.accept(visitor);
visitor.process(method);
if (visitor.isCleanedUp() || visitor.variableEscapes()) {
return;
}
@@ -296,46 +286,46 @@ public class CleanupDetector extends Detector implements JavaScanner {
"This `%1$s` should be freed up after use with `#%2$s()`", className,
recycleName);
}
Node locationNode = node instanceof MethodInvocation ?
((MethodInvocation) node).astName() : node;
Location location = context.getLocation(locationNode);
UElement locationNode = node instanceof UCallExpression ?
((UCallExpression) node).getFunctionNameElement() : node;
Location location = UastAndroidUtils.getLocation(locationNode);
context.report(RECYCLE_RESOURCE, node, location, message);
}
private static boolean checkTransactionCommits(@NonNull JavaContext context,
@NonNull MethodInvocation node) {
private static boolean checkTransactionCommits(@NonNull UastAndroidContext context,
@NonNull UCallExpression node) {
if (isBeginTransaction(context, node)) {
ResolvedVariable boundVariable = getVariable(context, node);
UVariable boundVariable = getVariable(context, node);
if (boundVariable == null && isCommittedInChainedCalls(context, node)) {
return true;
}
if (boundVariable != null) {
Node method = JavaContext.findSurroundingMethod(node);
UFunction method = UastUtils.getContainingFunction(node);
if (method == null) {
return true;
}
FinishVisitor commitVisitor = new FinishVisitor(context, boundVariable) {
@Override
protected boolean isCleanupCall(@NonNull MethodInvocation call) {
protected boolean isCleanupCall(@NonNull UCallExpression call) {
if (isTransactionCommitMethodCall(mContext, call)) {
Expression operand = call.astOperand();
if (operand != null) {
ResolvedNode resolved = mContext.resolve(operand);
UExpression operand = UastUtils.getReceiver(call);
if (operand instanceof UResolvable) {
UDeclaration resolved = ((UResolvable) operand).resolve(mContext);
//noinspection SuspiciousMethodCalls
if (resolved != null && mVariables.contains(resolved)) {
return true;
} else if (resolved instanceof ResolvedMethod
&& operand instanceof MethodInvocation
&& isCommittedInChainedCalls(mContext,(MethodInvocation) operand)) {
} else if (resolved instanceof UFunction
&& operand instanceof UCallExpression
&& isCommittedInChainedCalls(mContext, (UCallExpression) operand)) {
// Check that the target of the committed chains is the
// right variable!
while (operand instanceof MethodInvocation) {
operand = ((MethodInvocation)operand).astOperand();
while (operand instanceof UCallExpression) {
operand = UastUtils.getReceiver((UCallExpression)operand);
}
if (operand instanceof VariableReference) {
resolved = mContext.resolve(operand);
if (operand instanceof USimpleReferenceExpression) {
resolved = ((USimpleReferenceExpression)operand).resolve(mContext);
//noinspection SuspiciousMethodCalls
if (resolved != null && mVariables.contains(resolved)) {
return true;
@@ -344,11 +334,10 @@ public class CleanupDetector extends Detector implements JavaScanner {
}
}
} else if (isShowFragmentMethodCall(mContext, call)) {
StrictListAccessor<Expression, MethodInvocation> arguments =
call.astArguments();
List<UExpression> arguments = call.getValueArguments();
if (arguments.size() == 2) {
Expression first = arguments.first();
ResolvedNode resolved = mContext.resolve(first);
UExpression first = arguments.get(0);
UDeclaration resolved = UastUtils.resolveIfCan(first, mContext);
//noinspection SuspiciousMethodCalls
if (resolved != null && mVariables.contains(resolved)) {
return true;
@@ -359,29 +348,29 @@ public class CleanupDetector extends Detector implements JavaScanner {
}
};
method.accept(commitVisitor);
commitVisitor.handle(method);
if (commitVisitor.isCleanedUp() || commitVisitor.variableEscapes()) {
return true;
}
}
String message = "This transaction should be completed with a `commit()` call";
context.report(COMMIT_FRAGMENT, node, context.getLocation(node.astName()),
message);
context.report(COMMIT_FRAGMENT, node, UastAndroidUtils.getLocation(node.getFunctionReference()),
message);
}
return false;
}
private static boolean isCommittedInChainedCalls(@NonNull JavaContext context,
@NonNull MethodInvocation node) {
private static boolean isCommittedInChainedCalls(@NonNull UastAndroidContext context,
@NonNull UCallExpression node) {
// Look for chained calls since the FragmentManager methods all return "this"
// to allow constructor chaining, e.g.
// getFragmentManager().beginTransaction().addToBackStack("test")
// .disallowAddToBackStack().hide(mFragment2).setBreadCrumbShortTitle("test")
// .show(mFragment2).setCustomAnimations(0, 0).commit();
Node parent = node.getParent();
while (parent instanceof MethodInvocation) {
MethodInvocation methodInvocation = (MethodInvocation) parent;
UElement parent = node.getParent();
while (parent instanceof UCallExpression) {
UCallExpression methodInvocation = (UCallExpression) parent;
if (isTransactionCommitMethodCall(context, methodInvocation)
|| isShowFragmentMethodCall(context, methodInvocation)) {
return true;
@@ -393,74 +382,71 @@ public class CleanupDetector extends Detector implements JavaScanner {
return false;
}
private static boolean isTransactionCommitMethodCall(@NonNull JavaContext context,
@NonNull MethodInvocation call) {
private static boolean isTransactionCommitMethodCall(@NonNull UastAndroidContext context,
@NonNull UCallExpression call) {
String methodName = call.astName().astValue();
String methodName = call.getFunctionName();
return (COMMIT.equals(methodName) || COMMIT_ALLOWING_LOSS.equals(methodName)) &&
isMethodOnFragmentClass(context, call,
FRAGMENT_TRANSACTION_CLS,
FRAGMENT_TRANSACTION_V4_CLS);
}
private static boolean isShowFragmentMethodCall(@NonNull JavaContext context,
@NonNull MethodInvocation call) {
String methodName = call.astName().astValue();
private static boolean isShowFragmentMethodCall(@NonNull UastAndroidContext context,
@NonNull UCallExpression call) {
String methodName = call.getFunctionName();
return SHOW.equals(methodName)
&& isMethodOnFragmentClass(context, call,
DIALOG_FRAGMENT, DIALOG_V4_FRAGMENT);
}
private static boolean isMethodOnFragmentClass(
@NonNull JavaContext context,
@NonNull MethodInvocation call,
@NonNull UastAndroidContext context,
@NonNull UCallExpression call,
@NonNull String fragmentClass,
@NonNull String v4FragmentClass) {
ResolvedNode resolved = context.resolve(call);
if (resolved instanceof ResolvedMethod) {
ResolvedClass containingClass = ((ResolvedMethod) resolved).getContainingClass();
return containingClass.isSubclassOf(fragmentClass, false) ||
containingClass.isSubclassOf(v4FragmentClass, false);
UFunction resolved = call.resolve(context);
if (resolved != null) {
UClass containingClass = UastUtils.getContainingClassOrEmpty(resolved);
return containingClass.isSubclassOf(fragmentClass) ||
containingClass.isSubclassOf(v4FragmentClass);
}
return false;
}
@Nullable
public static ResolvedVariable getVariable(@NonNull JavaContext context,
@NonNull Node expression) {
Node parent = expression.getParent();
if (parent instanceof BinaryExpression) {
BinaryExpression binaryExpression = (BinaryExpression) parent;
if (binaryExpression.astOperator() == BinaryOperator.ASSIGN) {
Expression lhs = binaryExpression.astLeft();
ResolvedNode resolved = context.resolve(lhs);
if (resolved instanceof ResolvedVariable) {
return (ResolvedVariable) resolved;
public static UVariable getVariable(@NonNull UastAndroidContext context,
@NonNull UElement expression) {
UElement parent = expression.getParent();
if (parent instanceof UBinaryExpression) {
UBinaryExpression binaryExpression = (UBinaryExpression) parent;
if (binaryExpression.getOperator() == UastBinaryOperator.ASSIGN) {
UExpression lhs = binaryExpression.getLeftOperand();
if (lhs instanceof UResolvable) {
UDeclaration resolved = ((UResolvable) lhs).resolve(context);
if (resolved instanceof UVariable) {
return (UVariable) resolved;
}
}
}
} else if (parent instanceof VariableDefinitionEntry) {
ResolvedNode resolved = context.resolve(parent);
if (resolved instanceof ResolvedVariable) {
return (ResolvedVariable) resolved;
}
} else if (parent instanceof UVariable) {
return (UVariable) parent;
}
return null;
}
private static boolean isBeginTransaction(@NonNull JavaContext context,
@NonNull MethodInvocation node) {
String methodName = node.astName().astValue();
assert methodName.equals(BEGIN_TRANSACTION) : methodName;
private static boolean isBeginTransaction(@NonNull UastAndroidContext context,
@NonNull UCallExpression node) {
String methodName = node.getFunctionName();
assert BEGIN_TRANSACTION.equals(methodName) : methodName;
if (BEGIN_TRANSACTION.equals(methodName)) {
ResolvedNode resolved = context.resolve(node);
if (resolved instanceof ResolvedMethod) {
ResolvedMethod method = (ResolvedMethod) resolved;
ResolvedClass containingClass = method.getContainingClass();
if (containingClass.isSubclassOf(FRAGMENT_MANAGER_CLS, false)
|| containingClass.isSubclassOf(FRAGMENT_MANAGER_V4_CLS,
false)) {
UFunction method = node.resolve(context);
if (method != null) {
UClass containingClass = UastUtils.getContainingClassOrEmpty(method);
if (containingClass.isSubclassOf(FRAGMENT_MANAGER_CLS)
|| containingClass.isSubclassOf(FRAGMENT_MANAGER_V4_CLS)) {
return true;
}
}
@@ -475,13 +461,13 @@ public class CleanupDetector extends Detector implements JavaScanner {
* case of a TypedArray we're looking for a "recycle", call, in the
* case of a database cursor we're looking for a "close" call, etc.
*/
private abstract static class FinishVisitor extends ForwardingAstVisitor {
protected final JavaContext mContext;
protected final List<ResolvedVariable> mVariables;
private abstract static class FinishVisitor extends UastVisitor {
protected final UastAndroidContext mContext;
protected final List<UVariable> mVariables;
private boolean mContainsCleanup;
private boolean mEscapes;
public FinishVisitor(JavaContext context, @NonNull ResolvedVariable variable) {
public FinishVisitor(UastAndroidContext context, @NonNull UVariable variable) {
mContext = context;
mVariables = Lists.newArrayList(variable);
}
@@ -495,25 +481,25 @@ public class CleanupDetector extends Detector implements JavaScanner {
}
@Override
public boolean visitNode(Node node) {
return mContainsCleanup || super.visitNode(node);
public void process(@NotNull UElement element) {
if (!mContainsCleanup) {
super.process(element);
}
}
protected abstract boolean isCleanupCall(@NonNull MethodInvocation call);
protected abstract boolean isCleanupCall(@NonNull UCallExpression call);
@Override
public boolean visitMethodInvocation(MethodInvocation call) {
public boolean visitCallExpression(@NotNull UCallExpression call) {
if (mContainsCleanup) {
return true;
}
super.visitMethodInvocation(call);
// Look for escapes
if (!mEscapes) {
for (Expression expression : call.astArguments()) {
if (expression instanceof VariableReference) {
ResolvedNode resolved = mContext.resolve(expression);
for (UExpression expression : call.getValueArguments()) {
if (expression instanceof USimpleReferenceExpression) {
UDeclaration resolved = ((USimpleReferenceExpression) expression).resolve(mContext);
//noinspection SuspiciousMethodCalls
if (resolved != null && mVariables.contains(resolved)) {
mEscapes = true;
@@ -521,12 +507,11 @@ public class CleanupDetector extends Detector implements JavaScanner {
// Special case: MotionEvent.obtain(MotionEvent): passing in an
// event here does not recycle the event, and we also know it
// doesn't escape
if (OBTAIN.equals(call.astName().astValue())) {
ResolvedNode r = mContext.resolve(call);
if (r instanceof ResolvedMethod) {
ResolvedMethod method = (ResolvedMethod) r;
ResolvedClass cls = method.getContainingClass();
if (cls.matches(MOTION_EVENT_CLS)) {
if (OBTAIN.equals(call.getFunctionName())) {
UFunction method = call.resolve(mContext);
if (method != null) {
UClass cls = UastUtils.getContainingClassOrEmpty(method);
if (cls.matchesFqName(MOTION_EVENT_CLS)) {
mEscapes = false;
}
}
@@ -545,57 +530,61 @@ public class CleanupDetector extends Detector implements JavaScanner {
}
@Override
public boolean visitVariableDefinitionEntry(VariableDefinitionEntry node) {
Expression initializer = node.astInitializer();
if (initializer instanceof VariableReference) {
ResolvedNode resolved = mContext.resolve(initializer);
public boolean visitVariable(@NotNull UVariable node) {
UExpression initializer = node.getInitializer();
if (initializer instanceof USimpleReferenceExpression) {
UDeclaration resolved = ((USimpleReferenceExpression) initializer).resolve(mContext);
//noinspection SuspiciousMethodCalls
if (resolved != null && mVariables.contains(resolved)) {
ResolvedNode resolvedVariable = mContext.resolve(node);
if (resolvedVariable instanceof ResolvedVariable) {
ResolvedVariable variable = (ResolvedVariable) resolvedVariable;
mVariables.add(variable);
} else if (resolvedVariable instanceof ResolvedField) {
if (node.getKind() == UastVariableKind.LOCAL_VARIABLE) {
mVariables.add(node);
} else if (node.getKind() == UastVariableKind.MEMBER) {
mEscapes = true;
}
}
}
return super.visitVariableDefinitionEntry(node);
return false;
}
@Override
public boolean visitBinaryExpression(BinaryExpression node) {
if (node.astOperator() == BinaryOperator.ASSIGN) {
Expression rhs = node.astRight();
if (rhs instanceof VariableReference) {
ResolvedNode resolved = mContext.resolve(rhs);
public boolean visitBinaryExpression(@NotNull UBinaryExpression node) {
if (node.getOperator() == UastBinaryOperator.ASSIGN) {
UExpression rhs = node.getRightOperand();
if (rhs instanceof USimpleReferenceExpression) {
UDeclaration resolved = ((USimpleReferenceExpression) rhs).resolve(mContext);
UExpression leftOperand = node.getLeftOperand();
//noinspection SuspiciousMethodCalls
if (resolved != null && mVariables.contains(resolved)) {
ResolvedNode resolvedLhs = mContext.resolve(node.astLeft());
if (resolvedLhs instanceof ResolvedVariable) {
ResolvedVariable variable = (ResolvedVariable) resolvedLhs;
mVariables.add(variable);
} else if (resolvedLhs instanceof ResolvedField) {
mEscapes = true;
if (resolved != null && mVariables.contains(resolved) && leftOperand instanceof UResolvable) {
UDeclaration resolvedLhs = ((UResolvable) leftOperand).resolve(mContext);
if (resolvedLhs instanceof UVariable) {
UVariable variable = (UVariable) resolvedLhs;
if (variable.getKind() == UastVariableKind.LOCAL_VARIABLE) {
mVariables.add(variable);
} else if (variable.getKind() == UastVariableKind.MEMBER) {
mEscapes = true;
}
}
}
}
}
return super.visitBinaryExpression(node);
return false;
}
@Override
public boolean visitReturn(Return node) {
Expression value = node.astValue();
if (value instanceof VariableReference) {
ResolvedNode resolved = mContext.resolve(value);
//noinspection SuspiciousMethodCalls
if (resolved != null && mVariables.contains(resolved)) {
mEscapes = true;
public boolean visitSpecialExpressionList(@NotNull USpecialExpressionList node) {
if (node.getKind() == UastSpecialExpressionKind.RETURN) {
UExpression value = node.firstOrNull();
if (value instanceof USimpleReferenceExpression) {
UDeclaration resolved = ((USimpleReferenceExpression) value).resolve(mContext);
//noinspection SuspiciousMethodCalls
if (resolved != null && mVariables.contains(resolved)) {
mEscapes = true;
}
}
}
return super.visitReturn(node);
return false;
}
}
}
@@ -14,20 +14,20 @@
* limitations under the License.
*/
package com.android.tools.lint.checks;
package com.android.tools.klint.checks;
import static com.android.SdkConstants.ANDROID_VIEW_VIEW;
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
import com.android.tools.lint.detector.api.Category;
import com.android.tools.lint.detector.api.ClassContext;
import com.android.tools.lint.detector.api.Detector;
import com.android.tools.lint.detector.api.Implementation;
import com.android.tools.lint.detector.api.Issue;
import com.android.tools.lint.detector.api.Scope;
import com.android.tools.lint.detector.api.Severity;
import com.android.tools.lint.detector.api.Speed;
import com.android.tools.klint.detector.api.Category;
import com.android.tools.klint.detector.api.ClassContext;
import com.android.tools.klint.detector.api.Detector;
import com.android.tools.klint.detector.api.Implementation;
import com.android.tools.klint.detector.api.Issue;
import com.android.tools.klint.detector.api.Scope;
import com.android.tools.klint.detector.api.Severity;
import com.android.tools.klint.detector.api.Speed;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.tree.AbstractInsnNode;
@@ -14,33 +14,26 @@
* limitations under the License.
*/
package com.android.tools.lint.checks;
package com.android.tools.klint.checks;
import com.android.annotations.NonNull;
import com.android.tools.lint.detector.api.Category;
import com.android.tools.lint.detector.api.Context;
import com.android.tools.lint.detector.api.Detector;
import com.android.tools.lint.detector.api.Implementation;
import com.android.tools.lint.detector.api.Issue;
import com.android.tools.lint.detector.api.JavaContext;
import com.android.tools.lint.detector.api.Location;
import com.android.tools.lint.detector.api.Scope;
import com.android.tools.lint.detector.api.Severity;
import com.android.tools.lint.detector.api.Speed;
import com.android.tools.klint.detector.api.Category;
import com.android.tools.klint.detector.api.Context;
import com.android.tools.klint.detector.api.Detector;
import com.android.tools.klint.detector.api.Implementation;
import com.android.tools.klint.detector.api.Issue;
import com.android.tools.klint.detector.api.Scope;
import com.android.tools.klint.detector.api.Severity;
import com.android.tools.klint.detector.api.Speed;
import java.io.File;
import java.util.Collections;
import java.util.List;
import lombok.ast.AstVisitor;
import lombok.ast.Comment;
import lombok.ast.ForwardingAstVisitor;
import lombok.ast.Node;
import org.jetbrains.uast.check.UastScanner;
/**
* Looks for issues in Java comments
*/
public class CommentDetector extends Detector implements Detector.JavaScanner {
public class CommentDetector extends Detector implements UastScanner {
private static final String STOPSHIP_COMMENT = "STOPSHIP"; //$NON-NLS-1$
private static final Implementation IMPLEMENTATION = new Implementation(
@@ -96,7 +89,7 @@ public class CommentDetector extends Detector implements Detector.JavaScanner {
return Speed.NORMAL;
}
@Override
/*@Override
public List<Class<? extends Node>> getApplicableNodeTypes() {
if (USE_AST) {
return Collections.<Class<? extends Node>>singletonList(Comment.class);
@@ -135,7 +128,7 @@ public class CommentDetector extends Detector implements Detector.JavaScanner {
} else if (next == '*') {
// Block comment
int start = i + 2;
int end = source.indexOf("*/", start);
int end = source.indexOf("*//*", start);
if (end == -1) {
end = n;
}
@@ -195,5 +188,5 @@ public class CommentDetector extends Detector implements Detector.JavaScanner {
"to release");
}
}
}
}*/
}
@@ -14,7 +14,7 @@
* limitations under the License.
*/
package com.android.tools.lint.checks;
package com.android.tools.klint.checks;
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
@@ -14,7 +14,7 @@
* limitations under the License.
*/
package com.android.tools.lint.checks;
package com.android.tools.klint.checks;
import static com.android.SdkConstants.CLASS_VIEW;
import static com.android.SdkConstants.CLASS_VIEWGROUP;
@@ -22,36 +22,29 @@ import static com.android.SdkConstants.DOT_LAYOUT_PARAMS;
import static com.android.SdkConstants.R_STYLEABLE_PREFIX;
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
import com.android.tools.lint.client.api.JavaParser.ResolvedClass;
import com.android.tools.lint.client.api.JavaParser.ResolvedNode;
import com.android.tools.lint.detector.api.Category;
import com.android.tools.lint.detector.api.Context;
import com.android.tools.lint.detector.api.Detector;
import com.android.tools.lint.detector.api.Implementation;
import com.android.tools.lint.detector.api.Issue;
import com.android.tools.lint.detector.api.JavaContext;
import com.android.tools.lint.detector.api.Scope;
import com.android.tools.lint.detector.api.Severity;
import com.android.tools.lint.detector.api.Speed;
import com.google.common.collect.Iterators;
import com.android.tools.klint.detector.api.Category;
import com.android.tools.klint.detector.api.Context;
import com.android.tools.klint.detector.api.Detector;
import com.android.tools.klint.detector.api.Implementation;
import com.android.tools.klint.detector.api.Issue;
import com.android.tools.klint.detector.api.Scope;
import com.android.tools.klint.detector.api.Severity;
import com.android.tools.klint.detector.api.Speed;
import java.io.File;
import java.util.Collections;
import java.util.List;
import lombok.ast.AstVisitor;
import lombok.ast.ClassDeclaration;
import lombok.ast.Expression;
import lombok.ast.ExpressionStatement;
import lombok.ast.MethodInvocation;
import lombok.ast.StrictListAccessor;
import org.jetbrains.uast.*;
import org.jetbrains.uast.check.UastAndroidUtils;
import org.jetbrains.uast.check.UastAndroidContext;
import org.jetbrains.uast.check.UastScanner;
/**
* Makes sure that custom views use a declare styleable that matches
* the name of the custom view
*/
public class CustomViewDetector extends Detector implements Detector.JavaScanner {
public class CustomViewDetector extends Detector implements UastScanner {
private static final Implementation IMPLEMENTATION = new Implementation(
CustomViewDetector.class,
@@ -76,7 +69,7 @@ public class CustomViewDetector extends Detector implements Detector.JavaScanner
private static final String OBTAIN_STYLED_ATTRIBUTES = "obtainStyledAttributes"; //$NON-NLS-1$
/** Constructs a new {@link com.android.tools.lint.checks.CustomViewDetector} check */
/** Constructs a new {@link CustomViewDetector} check */
public CustomViewDetector() {
}
@@ -91,79 +84,76 @@ public class CustomViewDetector extends Detector implements Detector.JavaScanner
return Speed.FAST;
}
// ---- Implements JavaScanner ----
// ---- Implements UastScanner ----
@Override
public List<String> getApplicableMethodNames() {
public List<String> getApplicableFunctionNames() {
return Collections.singletonList(OBTAIN_STYLED_ATTRIBUTES);
}
@Override
public void visitMethod(@NonNull JavaContext context, @Nullable AstVisitor visitor,
@NonNull MethodInvocation node) {
if (node.getParent() instanceof ExpressionStatement) {
if (!context.isContextMethod(node)) {
public void visitFunctionCall(UastAndroidContext context, UCallExpression node) {
if (node.getParent() instanceof UExpression) {
if (!context.getLintContext().isContextMethod(node)) {
return;
}
StrictListAccessor<Expression, MethodInvocation> expressions = node.astArguments();
List<UExpression> expressions = node.getValueArguments();
int size = expressions.size();
// Which parameter contains the styleable (attrs) ?
int parameterIndex;
if (size == 1) {
// obtainStyledAttributes(int[] attrs)
parameterIndex = 0;
} else {
} else if (size > 1) {
// obtainStyledAttributes(int resid, int[] attrs)
// obtainStyledAttributes(AttributeSet set, int[] attrs)
// obtainStyledAttributes(AttributeSet set, int[] attrs, int defStyleAttr, int defStyleRes)
parameterIndex = 1;
}
Expression expression = Iterators.get(node.astArguments().iterator(), parameterIndex,
null);
if (expression == null) {
} else {
return;
}
String s = expression.toString();
UExpression expression = expressions.get(parameterIndex);
if (!(expression instanceof UQualifiedExpression)) {
return;
}
String s = expression.renderString();
if (!s.startsWith(R_STYLEABLE_PREFIX)) {
return;
}
String styleableName = s.substring(R_STYLEABLE_PREFIX.length());
ClassDeclaration cls = JavaContext.findSurroundingClass(node);
if (cls == null) {
UClass resolvedClass = UastUtils.getContainingClass(node);
if (resolvedClass == null) {
return;
}
ResolvedNode resolved = context.resolve(cls);
if (!(resolved instanceof ResolvedClass)) {
return;
}
String className = cls.astName().astValue();
ResolvedClass resolvedClass = (ResolvedClass) resolved;
if (resolvedClass.isSubclassOf(CLASS_VIEW, false)) {
String className = resolvedClass.getName();
if (resolvedClass.isSubclassOf(CLASS_VIEW)) {
if (!styleableName.equals(className)) {
String message = String.format(
"By convention, the custom view (`%1$s`) and the declare-styleable (`%2$s`) "
+ "should have the same name (various editor features rely on "
+ "this convention)",
className, styleableName);
context.report(ISSUE, node, context.getLocation(expression), message);
"By convention, the custom view (`%1$s`) and the declare-styleable (`%2$s`) "
+ "should have the same name (various editor features rely on "
+ "this convention)",
className, styleableName);
context.report(ISSUE, node, UastAndroidUtils.getLocation(expression), message);
}
} else if (resolvedClass.isSubclassOf(CLASS_VIEWGROUP + DOT_LAYOUT_PARAMS, false)) {
ClassDeclaration outer = JavaContext.findSurroundingClass(cls.getParent());
} else if (resolvedClass.isSubclassOf(CLASS_VIEWGROUP + DOT_LAYOUT_PARAMS)) {
UClass outer = UastUtils.getContainingClass(resolvedClass);
if (outer == null) {
return;
}
String layoutClassName = outer.astName().astValue();
String layoutClassName = outer.getName();
String expectedName = layoutClassName + "_Layout";
if (!styleableName.equals(expectedName)) {
String message = String.format(
"By convention, the declare-styleable (`%1$s`) for a layout parameter "
+ "class (`%2$s`) is expected to be the surrounding "
+ "class (`%3$s`) plus \"`_Layout`\", e.g. `%4$s`. "
+ "(Various editor features rely on this convention.)",
styleableName, className, layoutClassName, expectedName);
context.report(ISSUE, node, context.getLocation(expression), message);
"By convention, the declare-styleable (`%1$s`) for a layout parameter "
+ "class (`%2$s`) is expected to be the surrounding "
+ "class (`%3$s`) plus \"`_Layout`\", e.g. `%4$s`. "
+ "(Various editor features rely on this convention.)",
styleableName, className, layoutClassName, expectedName);
context.report(ISSUE, node, UastAndroidUtils.getLocation(expression), message);
}
}
}
@@ -14,21 +14,20 @@
* limitations under the License.
*/
package com.android.tools.lint.checks;
package com.android.tools.klint.checks;
import static com.android.SdkConstants.RESOURCE_CLZ_ID;
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
import com.android.tools.lint.detector.api.Category;
import com.android.tools.lint.detector.api.Context;
import com.android.tools.lint.detector.api.Detector;
import com.android.tools.lint.detector.api.Implementation;
import com.android.tools.lint.detector.api.Issue;
import com.android.tools.lint.detector.api.JavaContext;
import com.android.tools.lint.detector.api.Location;
import com.android.tools.lint.detector.api.Scope;
import com.android.tools.lint.detector.api.Severity;
import com.android.tools.klint.detector.api.Category;
import com.android.tools.klint.detector.api.Context;
import com.android.tools.klint.detector.api.Detector;
import com.android.tools.klint.detector.api.Implementation;
import com.android.tools.klint.detector.api.Issue;
import com.android.tools.klint.detector.api.Location;
import com.android.tools.klint.detector.api.Scope;
import com.android.tools.klint.detector.api.Severity;
import com.google.common.collect.Maps;
import java.io.File;
@@ -36,24 +35,17 @@ import java.util.Collections;
import java.util.List;
import java.util.Map;
import lombok.ast.ArrayAccess;
import lombok.ast.AstVisitor;
import lombok.ast.BinaryExpression;
import lombok.ast.Cast;
import lombok.ast.Expression;
import lombok.ast.ForwardingAstVisitor;
import lombok.ast.If;
import lombok.ast.MethodInvocation;
import lombok.ast.Node;
import lombok.ast.Select;
import lombok.ast.Statement;
import lombok.ast.VariableDefinitionEntry;
import lombok.ast.VariableReference;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.uast.*;
import org.jetbrains.uast.check.UastAndroidContext;
import org.jetbrains.uast.check.UastAndroidUtils;
import org.jetbrains.uast.check.UastScanner;
import org.jetbrains.uast.visitor.UastVisitor;
/**
* Detector looking for cut & paste issues
*/
public class CutPasteDetector extends Detector implements Detector.JavaScanner {
public class CutPasteDetector extends Detector implements UastScanner {
/** The main issue discovered by this detector */
public static final Issue ISSUE = Issue.create(
"CutPasteId", //$NON-NLS-1$
@@ -73,8 +65,8 @@ public class CutPasteDetector extends Detector implements Detector.JavaScanner {
CutPasteDetector.class,
Scope.JAVA_FILE_SCOPE));
private Node mLastMethod;
private Map<String, MethodInvocation> mIds;
private UFunction mLastMethod;
private Map<String, UCallExpression> mIds;
private Map<String, String> mLhs;
private Map<String, String> mCallOperands;
@@ -87,22 +79,22 @@ public class CutPasteDetector extends Detector implements Detector.JavaScanner {
return true;
}
// ---- Implements JavaScanner ----
// ---- Implements UastScanner ----
@Override
public List<String> getApplicableMethodNames() {
public List<String> getApplicableFunctionNames() {
return Collections.singletonList("findViewById"); //$NON-NLS-1$
}
@Override
public void visitMethod(@NonNull JavaContext context, @Nullable AstVisitor visitor,
@NonNull MethodInvocation call) {
String lhs = getLhs(call);
public void visitFunctionCall(UastAndroidContext context, UCallExpression node) {
String lhs = getLhs(node);
if (lhs == null) {
return;
}
Node method = JavaContext.findSurroundingMethod(call);
UFunction method = UastUtils.getContainingFunction(node);
if (method == null) {
return;
} else if (method != mLastMethod) {
@@ -112,16 +104,20 @@ public class CutPasteDetector extends Detector implements Detector.JavaScanner {
mLastMethod = method;
}
String callOperand = call.astOperand() != null ? call.astOperand().toString() : "";
UElement parent = node.getParent();
String callOperand = "";
if (parent instanceof UQualifiedExpression) {
callOperand = ((UQualifiedExpression)parent).getReceiver().renderString();
}
Expression first = call.astArguments().first();
if (first instanceof Select) {
Select select = (Select) first;
String id = select.astIdentifier().astValue();
Expression operand = select.astOperand();
if (operand instanceof Select) {
Select type = (Select) operand;
if (type.astIdentifier().astValue().equals(RESOURCE_CLZ_ID)) {
UExpression first = node.getValueArguments().get(0);
if (first instanceof UQualifiedExpression) {
UQualifiedExpression select = (UQualifiedExpression) first;
String id = select.getSelector().renderString();
UExpression operand = select.getReceiver();
if (operand instanceof UQualifiedExpression) {
UQualifiedExpression type = (UQualifiedExpression) operand;
if (type.getSelector().renderString().equals(RESOURCE_CLZ_ID)) {
if (mIds.containsKey(id)) {
if (lhs.equals(mLhs.get(id))) {
return;
@@ -129,45 +125,49 @@ public class CutPasteDetector extends Detector implements Detector.JavaScanner {
if (!callOperand.equals(mCallOperands.get(id))) {
return;
}
MethodInvocation earlierCall = mIds.get(id);
if (!isReachableFrom(method, earlierCall, call)) {
UCallExpression earlierCall = mIds.get(id);
if (!isReachableFrom(method, earlierCall, node)) {
return;
}
Location location = context.getLocation(call);
Location secondary = context.getLocation(earlierCall);
secondary.setMessage("First usage here");
location.setSecondary(secondary);
context.report(ISSUE, call, location, String.format(
"The id `%1$s` has already been looked up in this method; possible " +
"cut & paste error?", first.toString()));
Location location = UastAndroidUtils.getLocation(node);
Location secondary = UastAndroidUtils.getLocation(earlierCall);
if (location != null && secondary != null) {
secondary.setMessage("First usage here");
location.setSecondary(secondary);
context.report(ISSUE, node, location, String.format(
"The id `%1$s` has already been looked up in this method; possible " +
"cut & paste error?", first.toString()));
}
} else {
mIds.put(id, call);
mIds.put(id, node);
mLhs.put(id, lhs);
mCallOperands.put(id, callOperand);
}
}
}
}
}
}
@Nullable
private static String getLhs(@NonNull MethodInvocation call) {
Node parent = call.getParent();
if (parent instanceof Cast) {
private static String getLhs(@NonNull UCallExpression call) {
UElement parent = call.getParent();
if (UastBinaryExpressionWithTypeUtils.isTypeCast(parent)) {
assert parent != null;
parent = parent.getParent();
}
if (parent instanceof VariableDefinitionEntry) {
VariableDefinitionEntry vde = (VariableDefinitionEntry) parent;
return vde.astName().astValue();
} else if (parent instanceof BinaryExpression) {
BinaryExpression be = (BinaryExpression) parent;
Expression left = be.astLeft();
if (left instanceof VariableReference || left instanceof Select) {
return be.astLeft().toString();
} else if (left instanceof ArrayAccess) {
ArrayAccess aa = (ArrayAccess) left;
return aa.astOperand().toString();
if (parent instanceof UVariable) {
UVariable vde = (UVariable) parent;
return vde.getName();
} else if (parent instanceof UBinaryExpression) {
UBinaryExpression be = (UBinaryExpression) parent;
UExpression left = be.getLeftOperand();
if (left instanceof USimpleReferenceExpression || left instanceof UQualifiedExpression) {
return be.getLeftOperand().toString();
} else if (left instanceof UArrayAccessExpression) {
UArrayAccessExpression aa = (UArrayAccessExpression) left;
return aa.getReceiver().toString();
}
}
@@ -175,22 +175,21 @@ public class CutPasteDetector extends Detector implements Detector.JavaScanner {
}
private static boolean isReachableFrom(
@NonNull Node method,
@NonNull MethodInvocation from,
@NonNull MethodInvocation to) {
@NonNull UElement method,
@NonNull UCallExpression from,
@NonNull UCallExpression to) {
ReachableVisitor visitor = new ReachableVisitor(from, to);
method.accept(visitor);
visitor.process(method);
return visitor.isReachable();
}
private static class ReachableVisitor extends ForwardingAstVisitor {
@NonNull private final MethodInvocation mFrom;
@NonNull private final MethodInvocation mTo;
private static class ReachableVisitor extends UastVisitor {
@NonNull private final UCallExpression mFrom;
@NonNull private final UCallExpression mTo;
private boolean mReachable;
private boolean mSeenEnd;
public ReachableVisitor(@NonNull MethodInvocation from, @NonNull MethodInvocation to) {
public ReachableVisitor(@NonNull UCallExpression from, @NonNull UCallExpression to) {
mFrom = from;
mTo = to;
}
@@ -200,43 +199,42 @@ public class CutPasteDetector extends Detector implements Detector.JavaScanner {
}
@Override
public boolean visitMethodInvocation(MethodInvocation node) {
public boolean visitCallExpression(@NotNull UCallExpression node) {
if (node == mFrom) {
mReachable = true;
} else if (node == mTo) {
mSeenEnd = true;
}
return super.visitMethodInvocation(node);
return false;
}
@Override
public boolean visitIf(If node) {
Expression condition = node.astCondition();
Statement body = node.astStatement();
Statement elseBody = node.astElseStatement();
if (condition != null) {
condition.accept(this);
}
public boolean visitIfExpression(@NotNull UIfExpression node) {
UExpression condition = node.getCondition();
UExpression body = node.getThenBranch();
UElement elseBody = node.getElseBranch();
process(condition);
if (body != null) {
boolean wasReachable = mReachable;
body.accept(this);
process(body);
mReachable = wasReachable;
}
if (elseBody != null) {
boolean wasReachable = mReachable;
elseBody.accept(this);
process(elseBody);
mReachable = wasReachable;
}
endVisit(node);
return false;
}
@Override
public boolean visitNode(Node node) {
return mSeenEnd;
public void process(@NotNull UElement element) {
if (!mSeenEnd) {
super.process(element);
}
}
}
}
@@ -14,33 +14,33 @@
* limitations under the License.
*/
package com.android.tools.lint.checks;
package com.android.tools.klint.checks;
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
import com.android.tools.lint.client.api.JavaParser.ResolvedMethod;
import com.android.tools.lint.client.api.JavaParser.TypeDescriptor;
import com.android.tools.lint.detector.api.Category;
import com.android.tools.lint.detector.api.Detector;
import com.android.tools.lint.detector.api.Detector.JavaScanner;
import com.android.tools.lint.detector.api.Implementation;
import com.android.tools.lint.detector.api.Issue;
import com.android.tools.lint.detector.api.JavaContext;
import com.android.tools.lint.detector.api.Location;
import com.android.tools.lint.detector.api.Scope;
import com.android.tools.lint.detector.api.Severity;
import com.android.tools.lint.detector.api.Speed;
import com.android.tools.klint.detector.api.Category;
import com.android.tools.klint.detector.api.Detector;
import com.android.tools.klint.detector.api.Implementation;
import com.android.tools.klint.detector.api.Issue;
import com.android.tools.klint.detector.api.Location;
import com.android.tools.klint.detector.api.Scope;
import com.android.tools.klint.detector.api.Severity;
import com.android.tools.klint.detector.api.Speed;
import java.util.Collections;
import java.util.List;
import lombok.ast.AstVisitor;
import lombok.ast.ConstructorInvocation;
import org.jetbrains.uast.UFunction;
import org.jetbrains.uast.UCallExpression;
import org.jetbrains.uast.UType;
import org.jetbrains.uast.check.UastAndroidContext;
import org.jetbrains.uast.check.UastAndroidUtils;
import org.jetbrains.uast.check.UastScanner;
/**
* Checks for errors related to Date Formats
*/
public class DateFormatDetector extends Detector implements JavaScanner {
public class DateFormatDetector extends Detector implements UastScanner {
private static final Implementation IMPLEMENTATION = new Implementation(
DateFormatDetector.class,
@@ -82,7 +82,7 @@ public class DateFormatDetector extends Detector implements JavaScanner {
return Speed.FAST;
}
// ---- Implements JavaScanner ----
// ---- Implements UastScanner ----
@Nullable
@Override
@@ -91,22 +91,21 @@ public class DateFormatDetector extends Detector implements JavaScanner {
}
@Override
public void visitConstructor(@NonNull JavaContext context, @Nullable AstVisitor visitor,
@NonNull ConstructorInvocation node, @NonNull ResolvedMethod constructor) {
public void visitConstructor(UastAndroidContext context, UCallExpression functionCall, UFunction constructor) {
if (!specifiesLocale(constructor)) {
Location location = context.getLocation(node);
Location location = UastAndroidUtils.getLocation(functionCall);
String message =
"To get local formatting use `getDateInstance()`, `getDateTimeInstance()`, " +
"or `getTimeInstance()`, or use `new SimpleDateFormat(String template, " +
"Locale locale)` with for example `Locale.US` for ASCII dates.";
context.report(DATE_FORMAT, node, location, message);
"To get local formatting use `getDateInstance()`, `getDateTimeInstance()`, " +
"or `getTimeInstance()`, or use `new SimpleDateFormat(String template, " +
"Locale locale)` with for example `Locale.US` for ASCII dates.";
context.report(DATE_FORMAT, functionCall, location, message);
}
}
private static boolean specifiesLocale(@NonNull ResolvedMethod method) {
for (int i = 0, n = method.getArgumentCount(); i < n; i++) {
TypeDescriptor argumentType = method.getArgumentType(i);
if (argumentType.matchesSignature(LOCALE_CLS)) {
private static boolean specifiesLocale(@NonNull UFunction method) {
for (int i = 0, n = method.getValueParameterCount(); i < n; i++) {
UType parameterType = method.getValueParameters().get(i).getType();
if (parameterType.matchesFqName(LOCALE_CLS)) {
return true;
}
}
@@ -14,7 +14,7 @@
* limitations under the License.
*/
package com.android.tools.lint.checks;
package com.android.tools.klint.checks;
import static com.android.SdkConstants.ABSOLUTE_LAYOUT;
import static com.android.SdkConstants.ANDROID_URI;
@@ -31,14 +31,14 @@ import static com.android.SdkConstants.EDIT_TEXT;
import static com.android.SdkConstants.VALUE_TRUE;
import com.android.annotations.NonNull;
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.LayoutDetector;
import com.android.tools.lint.detector.api.Scope;
import com.android.tools.lint.detector.api.Severity;
import com.android.tools.lint.detector.api.Speed;
import com.android.tools.lint.detector.api.XmlContext;
import com.android.tools.klint.detector.api.Category;
import com.android.tools.klint.detector.api.Implementation;
import com.android.tools.klint.detector.api.Issue;
import com.android.tools.klint.detector.api.LayoutDetector;
import com.android.tools.klint.detector.api.Scope;
import com.android.tools.klint.detector.api.Severity;
import com.android.tools.klint.detector.api.Speed;
import com.android.tools.klint.detector.api.XmlContext;
import org.w3c.dom.Attr;
import org.w3c.dom.Element;
@@ -14,7 +14,7 @@
* limitations under the License.
*/
package com.android.tools.lint.checks;
package com.android.tools.klint.checks;
import static com.android.SdkConstants.ANDROID_PKG_PREFIX;
import static com.android.SdkConstants.ANDROID_SUPPORT_PKG_PREFIX;
@@ -39,14 +39,14 @@ import static com.android.resources.ResourceFolderType.MENU;
import com.android.annotations.NonNull;
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.LayoutDetector;
import com.android.tools.lint.detector.api.Scope;
import com.android.tools.lint.detector.api.Severity;
import com.android.tools.lint.detector.api.Speed;
import com.android.tools.lint.detector.api.XmlContext;
import com.android.tools.klint.detector.api.Category;
import com.android.tools.klint.detector.api.Implementation;
import com.android.tools.klint.detector.api.Issue;
import com.android.tools.klint.detector.api.LayoutDetector;
import com.android.tools.klint.detector.api.Scope;
import com.android.tools.klint.detector.api.Severity;
import com.android.tools.klint.detector.api.Speed;
import com.android.tools.klint.detector.api.XmlContext;
import org.w3c.dom.Attr;
import org.w3c.dom.Element;
@@ -14,18 +14,18 @@
* limitations under the License.
*/
package com.android.tools.lint.checks;
package com.android.tools.klint.checks;
import com.android.annotations.NonNull;
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.LayoutDetector;
import com.android.tools.lint.detector.api.Location;
import com.android.tools.lint.detector.api.Scope;
import com.android.tools.lint.detector.api.Severity;
import com.android.tools.lint.detector.api.Speed;
import com.android.tools.lint.detector.api.XmlContext;
import com.android.tools.klint.detector.api.Category;
import com.android.tools.klint.detector.api.Implementation;
import com.android.tools.klint.detector.api.Issue;
import com.android.tools.klint.detector.api.LayoutDetector;
import com.android.tools.klint.detector.api.Location;
import com.android.tools.klint.detector.api.Scope;
import com.android.tools.klint.detector.api.Severity;
import com.android.tools.klint.detector.api.Speed;
import com.android.tools.klint.detector.api.XmlContext;
import org.w3c.dom.Document;
@@ -14,7 +14,7 @@
* limitations under the License.
*/
package com.android.tools.lint.checks;
package com.android.tools.klint.checks;
import static com.android.SdkConstants.ANDROID_URI;
import static com.android.SdkConstants.ATTR_ID;
@@ -27,17 +27,17 @@ import static com.android.ide.common.resources.configuration.FolderConfiguration
import com.android.annotations.NonNull;
import com.android.resources.ResourceFolderType;
import com.android.tools.lint.detector.api.Category;
import com.android.tools.lint.detector.api.Context;
import com.android.tools.lint.detector.api.Implementation;
import com.android.tools.lint.detector.api.Issue;
import com.android.tools.lint.detector.api.LayoutDetector;
import com.android.tools.lint.detector.api.LintUtils;
import com.android.tools.lint.detector.api.Location;
import com.android.tools.lint.detector.api.Scope;
import com.android.tools.lint.detector.api.Severity;
import com.android.tools.lint.detector.api.Speed;
import com.android.tools.lint.detector.api.XmlContext;
import com.android.tools.klint.detector.api.Category;
import com.android.tools.klint.detector.api.Context;
import com.android.tools.klint.detector.api.Implementation;
import com.android.tools.klint.detector.api.Issue;
import com.android.tools.klint.detector.api.LayoutDetector;
import com.android.tools.klint.detector.api.LintUtils;
import com.android.tools.klint.detector.api.Location;
import com.android.tools.klint.detector.api.Scope;
import com.android.tools.klint.detector.api.Severity;
import com.android.tools.klint.detector.api.Speed;
import com.android.tools.klint.detector.api.XmlContext;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Multimap;
@@ -14,7 +14,7 @@
* limitations under the License.
*/
package com.android.tools.lint.checks;
package com.android.tools.klint.checks;
import static com.android.SdkConstants.ATTR_NAME;
@@ -27,19 +27,19 @@ import com.android.annotations.Nullable;
import com.android.ide.common.resources.ResourceUrl;
import com.android.resources.ResourceFolderType;
import com.android.resources.ResourceType;
import com.android.tools.lint.detector.api.Category;
import com.android.tools.lint.detector.api.Context;
import com.android.tools.lint.detector.api.Implementation;
import com.android.tools.lint.detector.api.Issue;
import com.android.tools.lint.detector.api.LintUtils;
import com.android.tools.lint.detector.api.Location;
import com.android.tools.lint.detector.api.Location.Handle;
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.Speed;
import com.android.tools.lint.detector.api.TextFormat;
import com.android.tools.lint.detector.api.XmlContext;
import com.android.tools.klint.detector.api.Category;
import com.android.tools.klint.detector.api.Context;
import com.android.tools.klint.detector.api.Implementation;
import com.android.tools.klint.detector.api.Issue;
import com.android.tools.klint.detector.api.LintUtils;
import com.android.tools.klint.detector.api.Location;
import com.android.tools.klint.detector.api.Location.Handle;
import com.android.tools.klint.detector.api.ResourceXmlDetector;
import com.android.tools.klint.detector.api.Scope;
import com.android.tools.klint.detector.api.Severity;
import com.android.tools.klint.detector.api.Speed;
import com.android.tools.klint.detector.api.TextFormat;
import com.android.tools.klint.detector.api.XmlContext;
import com.android.utils.Pair;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
@@ -14,21 +14,21 @@
* limitations under the License.
*/
package com.android.tools.lint.checks;
package com.android.tools.klint.checks;
import com.android.annotations.NonNull;
import com.android.resources.ResourceFolderType;
import com.android.tools.lint.detector.api.Category;
import com.android.tools.lint.detector.api.DefaultPosition;
import com.android.tools.lint.detector.api.Implementation;
import com.android.tools.lint.detector.api.Issue;
import com.android.tools.lint.detector.api.Location;
import com.android.tools.lint.detector.api.Position;
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.Speed;
import com.android.tools.lint.detector.api.XmlContext;
import com.android.tools.klint.detector.api.Category;
import com.android.tools.klint.detector.api.DefaultPosition;
import com.android.tools.klint.detector.api.Implementation;
import com.android.tools.klint.detector.api.Issue;
import com.android.tools.klint.detector.api.Location;
import com.android.tools.klint.detector.api.Position;
import com.android.tools.klint.detector.api.ResourceXmlDetector;
import com.android.tools.klint.detector.api.Scope;
import com.android.tools.klint.detector.api.Severity;
import com.android.tools.klint.detector.api.Speed;
import com.android.tools.klint.detector.api.XmlContext;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
@@ -14,20 +14,20 @@
* limitations under the License.
*/
package com.android.tools.lint.checks;
package com.android.tools.klint.checks;
import com.android.annotations.NonNull;
import com.android.tools.lint.detector.api.Category;
import com.android.tools.lint.detector.api.ClassContext;
import com.android.tools.lint.detector.api.Context;
import com.android.tools.lint.detector.api.Detector;
import com.android.tools.lint.detector.api.Implementation;
import com.android.tools.lint.detector.api.Issue;
import com.android.tools.lint.detector.api.LintUtils;
import com.android.tools.lint.detector.api.Location;
import com.android.tools.lint.detector.api.Scope;
import com.android.tools.lint.detector.api.Severity;
import com.android.tools.lint.detector.api.Speed;
import com.android.tools.klint.detector.api.Category;
import com.android.tools.klint.detector.api.ClassContext;
import com.android.tools.klint.detector.api.Context;
import com.android.tools.klint.detector.api.Detector;
import com.android.tools.klint.detector.api.Implementation;
import com.android.tools.klint.detector.api.Issue;
import com.android.tools.klint.detector.api.LintUtils;
import com.android.tools.klint.detector.api.Location;
import com.android.tools.klint.detector.api.Scope;
import com.android.tools.klint.detector.api.Severity;
import com.android.tools.klint.detector.api.Speed;
import com.google.common.collect.Maps;
import org.objectweb.asm.Opcodes;
@@ -14,34 +14,28 @@
* limitations under the License.
*/
package com.android.tools.lint.checks;
package com.android.tools.klint.checks;
import static com.android.SdkConstants.CLASS_FRAGMENT;
import static com.android.SdkConstants.CLASS_V4_FRAGMENT;
import static com.android.tools.lint.client.api.JavaParser.ResolvedClass;
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
import com.android.tools.lint.detector.api.Category;
import com.android.tools.lint.detector.api.Detector;
import com.android.tools.lint.detector.api.Detector.JavaScanner;
import com.android.tools.lint.detector.api.Implementation;
import com.android.tools.lint.detector.api.Issue;
import com.android.tools.lint.detector.api.JavaContext;
import com.android.tools.lint.detector.api.Location;
import com.android.tools.lint.detector.api.Scope;
import com.android.tools.lint.detector.api.Severity;
import com.android.tools.lint.detector.api.Speed;
import com.android.tools.klint.detector.api.Category;
import com.android.tools.klint.detector.api.Detector;
import com.android.tools.klint.detector.api.Implementation;
import com.android.tools.klint.detector.api.Issue;
import com.android.tools.klint.detector.api.Location;
import com.android.tools.klint.detector.api.Scope;
import com.android.tools.klint.detector.api.Severity;
import com.android.tools.klint.detector.api.Speed;
import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.List;
import lombok.ast.ClassDeclaration;
import lombok.ast.ConstructorDeclaration;
import lombok.ast.Node;
import lombok.ast.NormalTypeBody;
import lombok.ast.TypeMember;
import org.jetbrains.uast.*;
import org.jetbrains.uast.check.UastAndroidUtils;
import org.jetbrains.uast.check.UastAndroidContext;
import org.jetbrains.uast.check.UastScanner;
/**
* Checks that Fragment subclasses can be instantiated via
@@ -52,7 +46,7 @@ import lombok.ast.TypeMember;
* http://stackoverflow.com/questions/8058809/fragment-activity-crashes-on-screen-rotate
* (and countless duplicates)
*/
public class FragmentDetector extends Detector implements JavaScanner {
public class FragmentDetector extends Detector implements UastScanner {
/** Are fragment subclasses instantiatable? */
public static final Issue ISSUE = Issue.create(
"ValidFragment", //$NON-NLS-1$
@@ -86,80 +80,67 @@ public class FragmentDetector extends Detector implements JavaScanner {
return Speed.FAST;
}
// ---- Implements JavaScanner ----
// ---- Implements UastScanner ----
@Nullable
@Override
public List<String> applicableSuperClasses() {
public List<String> getApplicableSuperClasses() {
return Arrays.asList(CLASS_FRAGMENT, CLASS_V4_FRAGMENT);
}
@Override
public void checkClass(@NonNull JavaContext context, @Nullable ClassDeclaration node,
@NonNull Node declarationOrAnonymous, @NonNull ResolvedClass cls) {
if (node == null) {
public void visitClass(UastAndroidContext context, UClass cls) {
if (cls.hasModifier(UastModifier.ABSTRACT) || cls.isInterface()) {
return;
}
int flags = node.astModifiers().getEffectiveModifierFlags();
if ((flags & Modifier.ABSTRACT) != 0) {
return;
}
if ((flags & Modifier.PUBLIC) == 0) {
if (!cls.getVisibility().isPublic()) {
String message = String.format("This fragment class should be public (%1$s)",
cls.getName());
context.report(ISSUE, node, context.getLocation(node.astName()), message);
cls.getFqName());
context.report(ISSUE, cls, UastAndroidUtils.getLocation(cls.getNameElement()), message);
return;
}
if (cls.getContainingClass() != null && (flags & Modifier.STATIC) == 0) {
if (UastUtils.getContainingClass(cls) != null && cls.hasModifier(UastModifier.INNER)) {
String message = String.format(
"This fragment inner class should be static (%1$s)", cls.getName());
context.report(ISSUE, node, context.getLocation(node.astName()), message);
"This fragment inner class should be static (%1$s)", cls.getName());
context.report(ISSUE, cls, UastAndroidUtils.getLocation(cls.getNameElement()), message);
return;
}
boolean hasDefaultConstructor = false;
boolean hasConstructor = false;
NormalTypeBody body = node.astBody();
if (body != null) {
for (TypeMember member : body.astMembers()) {
if (member instanceof ConstructorDeclaration) {
hasConstructor = true;
ConstructorDeclaration constructor = (ConstructorDeclaration) member;
if (constructor.astParameters().isEmpty()) {
// The constructor must be public
if (constructor.astModifiers().isPublic()) {
hasDefaultConstructor = true;
} else {
Location location = context.getLocation(
constructor.astTypeName());
context.report(ISSUE, constructor, location,
"The default constructor must be public");
// Also mark that we have a constructor so we don't complain again
// below since we've already emitted a more specific error related
// to the default constructor
hasDefaultConstructor = true;
}
} else {
Location location = context.getLocation(constructor.astTypeName());
// TODO: Use separate issue for this which isn't an error
String message = "Avoid non-default constructors in fragments: "
+ "use a default constructor plus "
+ "`Fragment#setArguments(Bundle)` instead";
context.report(ISSUE, constructor, location, message);
}
for (UFunction constructor : cls.getConstructors()) {
hasConstructor = true;
if (constructor.getValueParameterCount() == 0) {
if (constructor.getVisibility().isPublic()) {
hasDefaultConstructor = true;
} else {
Location location = UastAndroidUtils.getLocation(constructor.getNameElement());
context.report(ISSUE, constructor, location,
"The default constructor must be public");
// Also mark that we have a constructor so we don't complain again
// below since we've already emitted a more specific error related
// to the default constructor
hasDefaultConstructor = true;
}
} else {
Location location = UastAndroidUtils.getLocation(constructor.getNameElement());
// TODO: Use separate issue for this which isn't an error
String message = "Avoid non-default constructors in fragments: "
+ "use a default constructor plus "
+ "`Fragment#setArguments(Bundle)` instead";
context.report(ISSUE, constructor, location, message);
}
}
if (!hasDefaultConstructor && hasConstructor) {
String message = String.format(
"This fragment should provide a default constructor (a public " +
"constructor with no arguments) (`%1$s`)",
cls.getName());
context.report(ISSUE, node, context.getLocation(node.astName()), message);
"This fragment should provide a default constructor (a public " +
"constructor with no arguments) (`%1$s`)",
cls.getName());
context.report(ISSUE, cls, UastAndroidUtils.getLocation(cls.getNameElement()), message);
}
}
}
@@ -14,25 +14,25 @@
* limitations under the License.
*/
package com.android.tools.lint.checks;
package com.android.tools.klint.checks;
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
import com.android.resources.ResourceFolderType;
import com.android.tools.lint.detector.api.Category;
import com.android.tools.lint.detector.api.Detector.JavaScanner;
import com.android.tools.lint.detector.api.Implementation;
import com.android.tools.lint.detector.api.Issue;
import com.android.tools.lint.detector.api.Location;
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 com.android.tools.klint.detector.api.Category;
import com.android.tools.klint.detector.api.Implementation;
import com.android.tools.klint.detector.api.Issue;
import com.android.tools.klint.detector.api.Location;
import com.android.tools.klint.detector.api.ResourceXmlDetector;
import com.android.tools.klint.detector.api.Scope;
import com.android.tools.klint.detector.api.Severity;
import com.android.tools.klint.detector.api.XmlContext;
import com.google.common.base.Joiner;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Lists;
import com.google.common.collect.Multimap;
import org.jetbrains.uast.check.UastScanner;
import org.w3c.dom.Attr;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
@@ -45,7 +45,7 @@ import java.util.List;
/**
* Check which makes sure that a full-backup-content descriptor file is valid and logical
*/
public class FullBackupContentDetector extends ResourceXmlDetector implements JavaScanner {
public class FullBackupContentDetector extends ResourceXmlDetector implements UastScanner {
/**
* Validation of {@code <full-backup-content>} XML elements
*/
@@ -14,36 +14,25 @@
* limitations under the License.
*/
package com.android.tools.lint.checks;
package com.android.tools.klint.checks;
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
import com.android.tools.lint.client.api.JavaParser;
import com.android.tools.lint.client.api.JavaParser.ResolvedField;
import com.android.tools.lint.client.api.JavaParser.ResolvedMethod;
import com.android.tools.lint.client.api.JavaParser.ResolvedNode;
import com.android.tools.lint.client.api.JavaParser.TypeDescriptor;
import com.android.tools.lint.detector.api.Category;
import com.android.tools.lint.detector.api.Detector;
import com.android.tools.lint.detector.api.Implementation;
import com.android.tools.lint.detector.api.Issue;
import com.android.tools.lint.detector.api.JavaContext;
import com.android.tools.lint.detector.api.Scope;
import com.android.tools.lint.detector.api.Severity;
import com.android.tools.klint.client.api.JavaParser;
import com.android.tools.klint.detector.api.Category;
import com.android.tools.klint.detector.api.Detector;
import com.android.tools.klint.detector.api.Implementation;
import com.android.tools.klint.detector.api.Issue;
import com.android.tools.klint.detector.api.Scope;
import com.android.tools.klint.detector.api.Severity;
import java.util.Collections;
import java.util.List;
import lombok.ast.AstVisitor;
import lombok.ast.BinaryExpression;
import lombok.ast.BinaryOperator;
import lombok.ast.Expression;
import lombok.ast.IntegralLiteral;
import lombok.ast.MethodInvocation;
import lombok.ast.StrictListAccessor;
import lombok.ast.StringLiteral;
import org.jetbrains.uast.*;
import org.jetbrains.uast.check.UastAndroidContext;
import org.jetbrains.uast.check.UastAndroidUtils;
import org.jetbrains.uast.check.UastScanner;
public class GetSignaturesDetector extends Detector implements Detector.JavaScanner {
public class GetSignaturesDetector extends Detector implements UastScanner {
public static final Issue ISSUE = Issue.create(
"PackageManagerGetSignatures", //$NON-NLS-1$
"Potential Multiple Certificate Exploit",
@@ -65,92 +54,83 @@ public class GetSignaturesDetector extends Detector implements Detector.JavaScan
private static final String GET_PACKAGE_INFO = "getPackageInfo"; //$NON-NLS-1$
private static final int GET_SIGNATURES_FLAG = 0x00000040; //$NON-NLS-1$
// ---- Implements JavaScanner ----
// ---- Implements UastScanner ----
@Override
@Nullable
public List<String> getApplicableMethodNames() {
public List<String> getApplicableFunctionNames() {
return Collections.singletonList(GET_PACKAGE_INFO);
}
@Override
public void visitMethod(@NonNull JavaContext context, @Nullable AstVisitor visitor,
@NonNull MethodInvocation node) {
ResolvedNode resolved = context.resolve(node);
public void visitFunctionCall(UastAndroidContext context, UCallExpression node) {
UFunction resolved = node.resolve(context);
if (!(resolved instanceof ResolvedMethod) ||
!((ResolvedMethod) resolved).getContainingClass()
.isSubclassOf(PACKAGE_MANAGER_CLASS, false)) {
if (resolved == null ||
!UastUtils.getContainingClassOrEmpty(resolved).isSubclassOf(PACKAGE_MANAGER_CLASS)) {
return;
}
StrictListAccessor<Expression, MethodInvocation> argumentList = node.astArguments();
List<UExpression> argumentList = node.getValueArguments();
// Ignore if the method doesn't fit our description.
if (argumentList != null && argumentList.size() == 2) {
TypeDescriptor firstParameterType = context.getType(argumentList.first());
if (argumentList.size() == 2) {
UType firstParameterType = argumentList.get(0).getExpressionType();
if (firstParameterType != null
&& firstParameterType.matchesSignature(JavaParser.TYPE_STRING)) {
maybeReportIssue(calculateValue(context, argumentList.last()), context, node);
&& firstParameterType.matchesFqName(JavaParser.TYPE_STRING)) {
maybeReportIssue(calculateValue(context, argumentList.get(1)), context, node);
}
}
}
private static void maybeReportIssue(
int flagValue, JavaContext context, MethodInvocation node) {
int flagValue, UastAndroidContext context, UCallExpression node) {
if ((flagValue & GET_SIGNATURES_FLAG) != 0) {
context.report(ISSUE, node, context.getLocation(node.astArguments().last()),
context.report(ISSUE, node, UastAndroidUtils.getLocation(node.getValueArguments().get(1)),
"Reading app signatures from getPackageInfo: The app signatures "
+ "could be exploited if not validated properly; "
+ "see issue explanation for details.");
}
}
private static int calculateValue(JavaContext context, Expression expression) {
private static int calculateValue(UastAndroidContext context, UExpression expression) {
// This function assumes that the only inputs to the expression are static integer
// flags that combined via bitwise operands.
if (expression instanceof IntegralLiteral) {
return ((IntegralLiteral) expression).astIntValue();
if (UastLiteralUtils.isIntegralLiteral(expression)) {
return (int) UastLiteralUtils.getLongValue((ULiteralExpression) expression);
}
ResolvedNode resolvedNode = context.resolve(expression);
if (resolvedNode instanceof ResolvedField) {
Object value = ((ResolvedField) resolvedNode).getValue();
if (value instanceof Integer) {
return (Integer) value;
if (expression instanceof UResolvable) {
UDeclaration resolvedNode = ((UResolvable) expression).resolve(context);
if (resolvedNode instanceof UVariable) {
UExpression initializer = ((UVariable)resolvedNode).getInitializer();
if (initializer != null) {
Object value = initializer.evaluate();
if (value instanceof Integer) {
return (Integer)value;
}
}
}
}
if (expression instanceof BinaryExpression) {
BinaryExpression binaryExpression = (BinaryExpression) expression;
BinaryOperator operator = binaryExpression.astOperator();
int leftValue = calculateValue(context, binaryExpression.astLeft());
int rightValue = calculateValue(context, binaryExpression.astRight());
if (operator == BinaryOperator.BITWISE_OR) {
if (expression instanceof UBinaryExpression) {
UBinaryExpression binaryExpression = (UBinaryExpression) expression;
UastBinaryOperator operator = binaryExpression.getOperator();
int leftValue = calculateValue(context, binaryExpression.getLeftOperand());
int rightValue = calculateValue(context, binaryExpression.getRightOperand());
if (operator == UastBinaryOperator.BITWISE_OR) {
return leftValue | rightValue;
}
if (operator == BinaryOperator.BITWISE_AND) {
if (operator == UastBinaryOperator.BITWISE_AND) {
return leftValue & rightValue;
}
if (operator == BinaryOperator.BITWISE_XOR) {
if (operator == UastBinaryOperator.BITWISE_XOR) {
return leftValue ^ rightValue;
}
}
return 0;
}
private static boolean isStringParameter(
@NonNull Expression expression, @NonNull JavaContext context) {
if (expression instanceof StringLiteral) {
return true;
} else {
ResolvedNode resolvedNode = context.resolve(expression);
if (resolvedNode instanceof ResolvedField) {
if (((ResolvedField) resolvedNode).getValue() instanceof String) {
return true;
}
}
}
return false;
}
}
@@ -13,14 +13,13 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.tools.lint.checks;
package com.android.tools.klint.checks;
import static com.android.SdkConstants.FD_BUILD_TOOLS;
import static com.android.SdkConstants.GRADLE_PLUGIN_MINIMUM_VERSION;
import static com.android.SdkConstants.GRADLE_PLUGIN_RECOMMENDED_VERSION;
import static com.android.ide.common.repository.GradleCoordinate.COMPARE_PLUS_HIGHER;
import static com.android.tools.lint.checks.ManifestDetector.TARGET_NEWER;
import static com.android.tools.lint.detector.api.LintUtils.findSubstring;
import static com.android.tools.klint.detector.api.LintUtils.findSubstring;
import static com.google.common.base.Charsets.UTF_8;
import com.android.SdkConstants;
@@ -35,18 +34,18 @@ import com.android.ide.common.repository.GradleCoordinate;
import com.android.ide.common.repository.GradleCoordinate.RevisionComponent;
import com.android.ide.common.repository.SdkMavenRepository;
import com.android.sdklib.repository.PreciseRevision;
import com.android.tools.lint.client.api.LintClient;
import com.android.tools.lint.detector.api.Category;
import com.android.tools.lint.detector.api.Context;
import com.android.tools.lint.detector.api.Detector;
import com.android.tools.lint.detector.api.Implementation;
import com.android.tools.lint.detector.api.Issue;
import com.android.tools.lint.detector.api.LintUtils;
import com.android.tools.lint.detector.api.Location;
import com.android.tools.lint.detector.api.Scope;
import com.android.tools.lint.detector.api.Severity;
import com.android.tools.lint.detector.api.Speed;
import com.android.tools.lint.detector.api.TextFormat;
import com.android.tools.klint.client.api.LintClient;
import com.android.tools.klint.detector.api.Category;
import com.android.tools.klint.detector.api.Context;
import com.android.tools.klint.detector.api.Detector;
import com.android.tools.klint.detector.api.Implementation;
import com.android.tools.klint.detector.api.Issue;
import com.android.tools.klint.detector.api.LintUtils;
import com.android.tools.klint.detector.api.Location;
import com.android.tools.klint.detector.api.Scope;
import com.android.tools.klint.detector.api.Severity;
import com.android.tools.klint.detector.api.Speed;
import com.android.tools.klint.detector.api.TextFormat;
import com.google.common.base.Splitter;
import com.google.common.collect.Lists;
@@ -336,7 +335,7 @@ public class GradleDetector extends Detector implements Detector.GradleScanner {
"Not targeting the latest versions of Android; compatibility " +
"modes apply. Consider testing and updating this version. " +
"Consult the android.os.Build.VERSION_CODES javadoc for details.";
report(context, valueCookie, TARGET_NEWER, message);
report(context, valueCookie, ManifestDetector.TARGET_NEWER, message);
}
if (version > 0) {
mTargetSdkVersion = version;
@@ -14,7 +14,7 @@
* limitations under the License.
*/
package com.android.tools.lint.checks;
package com.android.tools.klint.checks;
import static com.android.SdkConstants.ANDROID_NS_NAME;
import static com.android.SdkConstants.ANDROID_URI;
@@ -34,16 +34,16 @@ import static com.android.SdkConstants.XMLNS_PREFIX;
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
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.LayoutDetector;
import com.android.tools.lint.detector.api.LintUtils;
import com.android.tools.lint.detector.api.Scope;
import com.android.tools.lint.detector.api.Severity;
import com.android.tools.lint.detector.api.Speed;
import com.android.tools.lint.detector.api.TextFormat;
import com.android.tools.lint.detector.api.XmlContext;
import com.android.tools.klint.detector.api.Category;
import com.android.tools.klint.detector.api.Implementation;
import com.android.tools.klint.detector.api.Issue;
import com.android.tools.klint.detector.api.LayoutDetector;
import com.android.tools.klint.detector.api.LintUtils;
import com.android.tools.klint.detector.api.Scope;
import com.android.tools.klint.detector.api.Severity;
import com.android.tools.klint.detector.api.Speed;
import com.android.tools.klint.detector.api.TextFormat;
import com.android.tools.klint.detector.api.XmlContext;
import org.w3c.dom.Attr;
import org.w3c.dom.Document;
@@ -14,35 +14,31 @@
* limitations under the License.
*/
package com.android.tools.lint.checks;
package com.android.tools.klint.checks;
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
import com.android.tools.lint.client.api.JavaParser.ResolvedClass;
import com.android.tools.lint.client.api.JavaParser.ResolvedMethod;
import com.android.tools.lint.client.api.JavaParser.TypeDescriptor;
import com.android.tools.lint.detector.api.Category;
import com.android.tools.lint.detector.api.Detector;
import com.android.tools.lint.detector.api.Implementation;
import com.android.tools.lint.detector.api.Issue;
import com.android.tools.lint.detector.api.JavaContext;
import com.android.tools.lint.detector.api.Location;
import com.android.tools.lint.detector.api.Scope;
import com.android.tools.lint.detector.api.Severity;
import com.android.tools.lint.detector.api.Speed;
import com.android.tools.klint.detector.api.Category;
import com.android.tools.klint.detector.api.Detector;
import com.android.tools.klint.detector.api.Implementation;
import com.android.tools.klint.detector.api.Issue;
import com.android.tools.klint.detector.api.Location;
import com.android.tools.klint.detector.api.Scope;
import com.android.tools.klint.detector.api.Severity;
import com.android.tools.klint.detector.api.Speed;
import java.lang.reflect.Modifier;
import java.util.Collections;
import java.util.List;
import lombok.ast.ClassDeclaration;
import lombok.ast.Node;
import org.jetbrains.uast.*;
import org.jetbrains.uast.check.UastAndroidContext;
import org.jetbrains.uast.check.UastAndroidUtils;
import org.jetbrains.uast.check.UastScanner;
/**
* Checks that Handler implementations are top level classes or static.
* See the corresponding check in the android.os.Handler source code.
*/
public class HandlerDetector extends Detector implements Detector.JavaScanner {
public class HandlerDetector extends Detector implements UastScanner {
/** Potentially leaking handlers */
public static final Issue ISSUE = Issue.create(
@@ -78,58 +74,39 @@ public class HandlerDetector extends Detector implements Detector.JavaScanner {
return Speed.FAST;
}
// ---- Implements JavaScanner ----
// ---- Implements UastScanner ----
@Nullable
@Override
public List<String> applicableSuperClasses() {
public List<String> getApplicableSuperClasses() {
return Collections.singletonList(HANDLER_CLS);
}
@Override
public void checkClass(@NonNull JavaContext context, @Nullable ClassDeclaration declaration,
@NonNull Node node, @NonNull ResolvedClass cls) {
if (!isInnerClass(declaration)) {
return;
}
if (isStaticClass(declaration)) {
public void visitClass(UastAndroidContext context, UClass node) {
boolean x = node.hasModifier(UastModifier.INNER);
if (!node.hasModifier(UastModifier.INNER)) {
return;
}
// Only flag handlers using the default looper
if (hasLooperConstructorParameter(cls)) {
if (hasLooperConstructorParameter(node)) {
return;
}
Node locationNode = node instanceof ClassDeclaration
? ((ClassDeclaration) node).astName() : node;
Location location = context.getLocation(locationNode);
context.report(ISSUE, location, String.format(
"This Handler class should be static or leaks might occur (%1$s)",
cls.getName()));
UElement locationNode = node.getNameElement();
Location location = UastAndroidUtils.getLocation(locationNode);
context.report(ISSUE, node, location, String.format(
"This Handler class should be static or leaks might occur (%1$s)",
node.getName()));
}
private static boolean isInnerClass(@Nullable ClassDeclaration node) {
return node == null || // null class declarations means anonymous inner class
JavaContext.getParentOfType(node, ClassDeclaration.class, true) != null;
}
private static boolean isStaticClass(@Nullable ClassDeclaration node) {
if (node == null) {
// A null class declaration means anonymous inner class, and these can't be static
return false;
}
int flags = node.astModifiers().getEffectiveModifierFlags();
return (flags & Modifier.STATIC) != 0;
}
private static boolean hasLooperConstructorParameter(@NonNull ResolvedClass cls) {
for (ResolvedMethod constructor : cls.getConstructors()) {
for (int i = 0, n = constructor.getArgumentCount(); i < n; i++) {
TypeDescriptor type = constructor.getArgumentType(i);
if (type.matchesSignature(LOOPER_CLS)) {
private static boolean hasLooperConstructorParameter(@NonNull UClass cls) {
for (UFunction constructor : cls.getConstructors()) {
for (int i = 0, n = constructor.getValueParameterCount(); i < n; i++) {
UType type = constructor.getValueParameters().get(i).getType();
if (type.matchesFqName(LOOPER_CLS)) {
return true;
}
}
@@ -14,22 +14,22 @@
* limitations under the License.
*/
package com.android.tools.lint.checks;
package com.android.tools.klint.checks;
import static com.android.SdkConstants.ANDROID_MANIFEST_XML;
import static com.android.SdkConstants.ANDROID_URI;
import static com.android.SdkConstants.ATTR_DEBUGGABLE;
import com.android.annotations.NonNull;
import com.android.tools.lint.detector.api.Category;
import com.android.tools.lint.detector.api.Context;
import com.android.tools.lint.detector.api.Detector;
import com.android.tools.lint.detector.api.Implementation;
import com.android.tools.lint.detector.api.Issue;
import com.android.tools.lint.detector.api.Scope;
import com.android.tools.lint.detector.api.Severity;
import com.android.tools.lint.detector.api.Speed;
import com.android.tools.lint.detector.api.XmlContext;
import com.android.tools.klint.detector.api.Category;
import com.android.tools.klint.detector.api.Context;
import com.android.tools.klint.detector.api.Detector;
import com.android.tools.klint.detector.api.Implementation;
import com.android.tools.klint.detector.api.Issue;
import com.android.tools.klint.detector.api.Scope;
import com.android.tools.klint.detector.api.Severity;
import com.android.tools.klint.detector.api.Speed;
import com.android.tools.klint.detector.api.XmlContext;
import org.w3c.dom.Attr;
@@ -14,7 +14,7 @@
* limitations under the License.
*/
package com.android.tools.lint.checks;
package com.android.tools.klint.checks;
import static com.android.SdkConstants.ANDROID_URI;
import static com.android.SdkConstants.ATTR_CONTENT_DESCRIPTION;
@@ -26,14 +26,14 @@ import static com.android.SdkConstants.ATTR_TITLE;
import com.android.annotations.NonNull;
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.LayoutDetector;
import com.android.tools.lint.detector.api.Scope;
import com.android.tools.lint.detector.api.Severity;
import com.android.tools.lint.detector.api.Speed;
import com.android.tools.lint.detector.api.XmlContext;
import com.android.tools.klint.detector.api.Category;
import com.android.tools.klint.detector.api.Implementation;
import com.android.tools.klint.detector.api.Issue;
import com.android.tools.klint.detector.api.LayoutDetector;
import com.android.tools.klint.detector.api.Scope;
import com.android.tools.klint.detector.api.Severity;
import com.android.tools.klint.detector.api.Speed;
import com.android.tools.klint.detector.api.XmlContext;
import org.w3c.dom.Attr;
@@ -14,7 +14,7 @@
* limitations under the License.
*/
package com.android.tools.lint.checks;
package com.android.tools.klint.checks;
import static com.android.SdkConstants.ANDROID_MANIFEST_XML;
import static com.android.SdkConstants.ANDROID_URI;
@@ -43,7 +43,7 @@ import static com.android.SdkConstants.TAG_ITEM;
import static com.android.SdkConstants.TAG_PROVIDER;
import static com.android.SdkConstants.TAG_RECEIVER;
import static com.android.SdkConstants.TAG_SERVICE;
import static com.android.tools.lint.detector.api.LintUtils.endsWith;
import static com.android.tools.klint.detector.api.LintUtils.endsWith;
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
@@ -51,26 +51,29 @@ import com.android.builder.model.ProductFlavor;
import com.android.builder.model.ProductFlavorContainer;
import com.android.resources.Density;
import com.android.resources.ResourceFolderType;
import com.android.tools.lint.detector.api.Category;
import com.android.tools.lint.detector.api.Context;
import com.android.tools.lint.detector.api.Detector;
import com.android.tools.lint.detector.api.Implementation;
import com.android.tools.lint.detector.api.Issue;
import com.android.tools.lint.detector.api.JavaContext;
import com.android.tools.lint.detector.api.LintUtils;
import com.android.tools.lint.detector.api.Location;
import com.android.tools.lint.detector.api.Project;
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.Speed;
import com.android.tools.lint.detector.api.XmlContext;
import com.android.tools.klint.detector.api.Category;
import com.android.tools.klint.detector.api.Context;
import com.android.tools.klint.detector.api.Implementation;
import com.android.tools.klint.detector.api.Issue;
import com.android.tools.klint.detector.api.LintUtils;
import com.android.tools.klint.detector.api.Location;
import com.android.tools.klint.detector.api.Project;
import com.android.tools.klint.detector.api.ResourceXmlDetector;
import com.android.tools.klint.detector.api.Scope;
import com.android.tools.klint.detector.api.Severity;
import com.android.tools.klint.detector.api.Speed;
import com.android.tools.klint.detector.api.XmlContext;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Multimap;
import com.google.common.collect.Sets;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.uast.*;
import org.jetbrains.uast.check.UastAndroidContext;
import org.jetbrains.uast.check.UastScanner;
import org.jetbrains.uast.visitor.UastVisitor;
import org.w3c.dom.Element;
import java.awt.Dimension;
@@ -99,24 +102,11 @@ import javax.imageio.ImageIO;
import javax.imageio.ImageReader;
import javax.imageio.stream.ImageInputStream;
import lombok.ast.AstVisitor;
import lombok.ast.ConstructorInvocation;
import lombok.ast.Expression;
import lombok.ast.ForwardingAstVisitor;
import lombok.ast.MethodDeclaration;
import lombok.ast.MethodInvocation;
import lombok.ast.Node;
import lombok.ast.Select;
import lombok.ast.StrictListAccessor;
import lombok.ast.TypeReference;
import lombok.ast.TypeReferencePart;
import lombok.ast.VariableReference;
/**
* Checks for common icon problems, such as wrong icon sizes, placing icons in the
* density independent drawable folder, etc.
*/
public class IconDetector extends ResourceXmlDetector implements Detector.JavaScanner {
public class IconDetector extends ResourceXmlDetector implements UastScanner {
private static final boolean INCLUDE_LDPI;
static {
@@ -158,7 +148,7 @@ public class IconDetector extends ResourceXmlDetector implements Detector.JavaSc
* the manifest, menu files etc to see how icons are used
*/
private static final EnumSet<Scope> ICON_TYPE_SCOPE = EnumSet.of(Scope.ALL_RESOURCE_FILES,
Scope.JAVA_FILE, Scope.MANIFEST);
Scope.SOURCE_FILE, Scope.MANIFEST);
private static final Implementation IMPLEMENTATION_JAVA = new Implementation(
IconDetector.class,
@@ -1965,53 +1955,67 @@ public class IconDetector extends ResourceXmlDetector implements Detector.JavaSc
}
}
// ---- Implements JavaScanner ----
// ---- Implements UastScanner ----
private static final String NOTIFICATION_CLASS = "Notification"; //$NON-NLS-1$
private static final String NOTIFICATION_COMPAT_CLASS = "NotificationCompat"; //$NON-NLS-1$
private static final String NOTIFICATION_CLASS_FQNAME =
"android.app.Notification"; //$NON-NLS-1$
private static final String NOTIFICATION_COMPAT_CLASS_FQNAME =
"android.support.v4.app.NotificationCompat"; //$NON-NLS-1$
private static final String BUILDER_CLASS = "Builder"; //$NON-NLS-1$
private static final String SET_SMALL_ICON = "setSmallIcon"; //$NON-NLS-1$
private static final String ON_CREATE_OPTIONS_MENU = "onCreateOptionsMenu"; //$NON-NLS-1$
@Override
@Nullable
public AstVisitor createJavaVisitor(@NonNull JavaContext context) {
return new NotificationFinder();
public UastVisitor createUastVisitor(UastAndroidContext context) {
return new NotificationFinder(context);
}
@Override
@Nullable
public List<Class<? extends Node>> getApplicableNodeTypes() {
List<Class<? extends Node>> types = new ArrayList<Class<? extends Node>>(3);
types.add(MethodDeclaration.class);
types.add(ConstructorInvocation.class);
return types;
}
private final class NotificationFinder extends UastVisitor {
private UastAndroidContext mContext;
private final class NotificationFinder extends ForwardingAstVisitor {
@Override
public boolean visitMethodDeclaration(MethodDeclaration node) {
if (ON_CREATE_OPTIONS_MENU.equals(node.astMethodName().astValue())) {
// Gather any R.menu references found in this method
node.accept(new MenuFinder());
}
return super.visitMethodDeclaration(node);
public NotificationFinder(UastAndroidContext mContext) {
this.mContext = mContext;
}
@Override
public boolean visitConstructorInvocation(ConstructorInvocation node) {
TypeReference reference = node.astTypeReference();
StrictListAccessor<TypeReferencePart, TypeReference> parts = reference.astParts();
String typeName = parts.last().astIdentifier().astValue();
public boolean visitFunction(@NotNull UFunction node) {
if (node.matchesName(ON_CREATE_OPTIONS_MENU)) {
// Gather any R.menu references found in this method
new MenuFinder().process(node);
}
return super.visitFunction(node);
}
@Override
public boolean visitCallExpression(@NotNull UCallExpression node) {
if (node.getKind() == UastCallKind.CONSTRUCTOR_CALL) {
visitConstructorInvocation(node);
}
return super.visitCallExpression(node);
}
private void visitConstructorInvocation(UCallExpression node) {
USimpleReferenceExpression reference = node.getClassReference();
if (reference == null) {
return;
}
String typeName = reference.getIdentifier();
if (NOTIFICATION_CLASS.equals(typeName)) {
StrictListAccessor<Expression, ConstructorInvocation> args = node.astArguments();
List<UExpression> args = node.getValueArguments();
if (args.size() == 3) {
if (args.first() instanceof Select && handleSelect((Select) args.first())) {
return super.visitConstructorInvocation(node);
if (args.get(0) instanceof UQualifiedExpression
&& handleSelect((UQualifiedExpression) args.get(0))) {
return;
}
Node method = StringFormatDetector.getParentMethod(node);
UFunction method = UastUtils.getContainingFunction(node);
if (method != null) {
// Must track local types
String name = StringFormatDetector.getResourceForFirstArg(method, node);
@@ -2024,31 +2028,24 @@ public class IconDetector extends ResourceXmlDetector implements Detector.JavaSc
}
}
} else if (BUILDER_CLASS.equals(typeName)) {
boolean isBuilder = false;
if (parts.size() == 1) {
isBuilder = true;
} else if (parts.size() == 2) {
String clz = parts.first().astIdentifier().astValue();
if (NOTIFICATION_CLASS.equals(clz) || NOTIFICATION_COMPAT_CLASS.equals(clz)) {
isBuilder = true;
}
}
UClass resolvedClass = reference.resolveClass(mContext);
boolean isBuilder = resolvedClass != null
&& (resolvedClass.matchesFqName(NOTIFICATION_CLASS_FQNAME)
|| resolvedClass.matchesFqName(NOTIFICATION_COMPAT_CLASS_FQNAME));
if (isBuilder) {
Node method = StringFormatDetector.getParentMethod(node);
UFunction method = UastUtils.getContainingFunction(node);
if (method != null) {
SetIconFinder finder = new SetIconFinder();
method.accept(finder);
finder.process(method);
}
}
}
return super.visitConstructorInvocation(node);
}
}
private boolean handleSelect(Select select) {
if (select.toString().startsWith(R_DRAWABLE_PREFIX)) {
String name = select.astIdentifier().astValue();
private boolean handleSelect(UQualifiedExpression select) {
if (select.renderString().startsWith(R_DRAWABLE_PREFIX)) {
String name = select.getSelector().renderString();
if (mNotificationIcons == null) {
mNotificationIcons = Sets.newHashSet();
}
@@ -2060,32 +2057,31 @@ public class IconDetector extends ResourceXmlDetector implements Detector.JavaSc
return false;
}
private final class SetIconFinder extends ForwardingAstVisitor {
private final class SetIconFinder extends UastVisitor {
@Override
public boolean visitMethodInvocation(MethodInvocation node) {
if (SET_SMALL_ICON.equals(node.astName().astValue())) {
StrictListAccessor<Expression,MethodInvocation> arguments = node.astArguments();
if (arguments.size() == 1 && arguments.first() instanceof Select) {
handleSelect((Select) arguments.first());
public boolean visitCallExpression(@NotNull UCallExpression node) {
if (SET_SMALL_ICON.equals(node.getFunctionName())) {
List<UExpression> args = node.getValueArguments();
if (args.size() == 1 && args.get(0) instanceof UQualifiedExpression) {
handleSelect((UQualifiedExpression) args.get(0));
}
}
return super.visitMethodInvocation(node);
return super.visitCallExpression(node);
}
}
private final class MenuFinder extends ForwardingAstVisitor {
private final class MenuFinder extends UastVisitor {
@Override
public boolean visitSelect(Select node) {
public boolean visitQualifiedExpression(@NotNull UQualifiedExpression node) {
// R.type.name
if (node.astOperand() instanceof Select) {
Select select = (Select) node.astOperand();
if (select.astOperand() instanceof VariableReference) {
VariableReference reference = (VariableReference) select.astOperand();
if (reference.astIdentifier().astValue().equals(R_CLASS)) {
String type = select.astIdentifier().astValue();
if (type.equals(MENU_TYPE)) {
String name = node.astIdentifier().astValue();
if (node.getReceiver() instanceof UQualifiedExpression) {
UQualifiedExpression select = (UQualifiedExpression) node.getReceiver();
if (select.getReceiver() instanceof USimpleReferenceExpression) {
USimpleReferenceExpression reference = (USimpleReferenceExpression) select.getReceiver();
if (R_CLASS.equals(reference.getIdentifier())) {
if (select.selectorMatches(MENU_TYPE)) {
String name = node.getSelector().renderString();
// Reclassify icons in the given menu as action bar icons
if (mMenuToIcons != null) {
Collection<String> icons = mMenuToIcons.get(name);
@@ -2101,7 +2097,7 @@ public class IconDetector extends ResourceXmlDetector implements Detector.JavaSc
}
}
return super.visitSelect(node);
return super.visitQualifiedExpression(node);
}
}
}
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.tools.lint.checks;
package com.android.tools.klint.checks;
import static com.android.SdkConstants.ANDROID_URI;
import static com.android.SdkConstants.ATTR_LAYOUT_HEIGHT;
@@ -23,13 +23,13 @@ import static com.android.SdkConstants.VIEW_INCLUDE;
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
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.LayoutDetector;
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 com.android.tools.klint.detector.api.Category;
import com.android.tools.klint.detector.api.Implementation;
import com.android.tools.klint.detector.api.Issue;
import com.android.tools.klint.detector.api.LayoutDetector;
import com.android.tools.klint.detector.api.Scope;
import com.android.tools.klint.detector.api.Severity;
import com.android.tools.klint.detector.api.XmlContext;
import org.w3c.dom.Attr;
import org.w3c.dom.Element;

Some files were not shown because too many files have changed in this diff Show More