diff --git a/.idea/artifacts/KotlinPlugin.xml b/.idea/artifacts/KotlinPlugin.xml index 29eb9de1c44..05f464d6ccd 100644 --- a/.idea/artifacts/KotlinPlugin.xml +++ b/.idea/artifacts/KotlinPlugin.xml @@ -60,6 +60,16 @@ + + + + + + + + + + diff --git a/.idea/modules.xml b/.idea/modules.xml index d7791229684..6ea0e38c8be 100644 --- a/.idea/modules.xml +++ b/.idea/modules.xml @@ -3,6 +3,7 @@ + @@ -59,6 +60,9 @@ + + + @@ -66,6 +70,10 @@ + + + + diff --git a/ChangeLog.md b/ChangeLog.md index 67fb950c5f5..fbee7d64bfe 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -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 diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/KotlinBundle.properties b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/KotlinBundle.properties index 6ac8970ee3c..2c68b6bc7fe 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/KotlinBundle.properties +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/KotlinBundle.properties @@ -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 \ No newline at end of file diff --git a/idea/src/META-INF/android-lint.xml b/idea/src/META-INF/android-lint.xml new file mode 100644 index 00000000000..40465d8b035 --- /dev/null +++ b/idea/src/META-INF/android-lint.xml @@ -0,0 +1,212 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/idea/src/META-INF/android.xml b/idea/src/META-INF/android.xml index 1a6150e70bb..64a80fb1e3f 100644 --- a/idea/src/META-INF/android.xml +++ b/idea/src/META-INF/android.xml @@ -1,4 +1,6 @@ - + + + diff --git a/plugins/lint/android-annotations/android-annotations.iml b/plugins/lint/android-annotations/android-annotations.iml new file mode 100644 index 00000000000..c90834f2d60 --- /dev/null +++ b/plugins/lint/android-annotations/android-annotations.iml @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/plugins/lint/android-annotations/src/com/android/annotations/NonNull.java b/plugins/lint/android-annotations/src/com/android/annotations/NonNull.java new file mode 100644 index 00000000000..d16451b60a9 --- /dev/null +++ b/plugins/lint/android-annotations/src/com/android/annotations/NonNull.java @@ -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. + *

+ * This is a marker annotation and it has no specific attributes. + */ +@Documented +@Retention(RetentionPolicy.CLASS) +@Target({METHOD,PARAMETER,LOCAL_VARIABLE,FIELD}) +public @interface NonNull { +} diff --git a/plugins/lint/android-annotations/src/com/android/annotations/NonNullByDefault.java b/plugins/lint/android-annotations/src/com/android/annotations/NonNullByDefault.java new file mode 100644 index 00000000000..9ce54d86833 --- /dev/null +++ b/plugins/lint/android-annotations/src/com/android/annotations/NonNullByDefault.java @@ -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. + *

+ * 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. + *

+ * This is a marker annotation and it has no specific attributes. + */ +@Documented +@Retention(RetentionPolicy.CLASS) +@Target({PACKAGE, TYPE}) +public @interface NonNullByDefault { +} diff --git a/plugins/lint/android-annotations/src/com/android/annotations/Nullable.java b/plugins/lint/android-annotations/src/com/android/annotations/Nullable.java new file mode 100755 index 00000000000..a0377cb6704 --- /dev/null +++ b/plugins/lint/android-annotations/src/com/android/annotations/Nullable.java @@ -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. + * Note: 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}. + *

+ * 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. + *

+ * When decorating a method, this denotes the method might legitimately return + * null. + *

+ * This is a marker annotation and it has no specific attributes. + */ +@Documented +@Retention(RetentionPolicy.CLASS) +@Target({METHOD, PARAMETER, LOCAL_VARIABLE, FIELD}) +public @interface Nullable { +} diff --git a/plugins/lint/android-annotations/src/com/android/annotations/VisibleForTesting.java b/plugins/lint/android-annotations/src/com/android/annotations/VisibleForTesting.java new file mode 100755 index 00000000000..7f41d70e0f5 --- /dev/null +++ b/plugins/lint/android-annotations/src/com/android/annotations/VisibleForTesting.java @@ -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. + *

+ * The visibility 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; +} diff --git a/plugins/lint/android-annotations/src/com/android/annotations/concurrency/GuardedBy.java b/plugins/lint/android-annotations/src/com/android/annotations/concurrency/GuardedBy.java new file mode 100644 index 00000000000..5a151acc8b8 --- /dev/null +++ b/plugins/lint/android-annotations/src/com/android/annotations/concurrency/GuardedBy.java @@ -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(); +} diff --git a/plugins/lint/android-annotations/src/com/android/annotations/concurrency/Immutable.java b/plugins/lint/android-annotations/src/com/android/annotations/concurrency/Immutable.java new file mode 100644 index 00000000000..c83f9f0ffc9 --- /dev/null +++ b/plugins/lint/android-annotations/src/com/android/annotations/concurrency/Immutable.java @@ -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 { +} diff --git a/plugins/lint/lint-api/lint-api.iml b/plugins/lint/lint-api/lint-api.iml new file mode 100644 index 00000000000..9158eb89551 --- /dev/null +++ b/plugins/lint/lint-api/lint-api.iml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/plugins/lint/lint-api/src/com/android/tools/klint/client/api/AsmVisitor.java b/plugins/lint/lint-api/src/com/android/tools/klint/client/api/AsmVisitor.java index e8a4bb5c25b..4053c6c54ff 100644 --- a/plugins/lint/lint-api/src/com/android/tools/klint/client/api/AsmVisitor.java +++ b/plugins/lint/lint-api/src/com/android/tools/klint/client/api/AsmVisitor.java @@ -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 list = mMethodNameToChecks.get(element); + List list = mMethodNameToChecks.get(element); if (list == null) { - list = new ArrayList(); + list = new ArrayList(); mMethodNameToChecks.put(element, list); } list.add(scanner); @@ -103,9 +94,9 @@ class AsmVisitor { if (owners != null) { checkFullClass = false; for (String element : owners) { - List list = mMethodOwnerToChecks.get(element); + List list = mMethodOwnerToChecks.get(element); if (list == null) { - list = new ArrayList(); + list = new ArrayList(); mMethodOwnerToChecks.put(element, list); } list.add(scanner); diff --git a/plugins/lint/lint-api/src/com/android/tools/klint/client/api/CircularDependencyException.java b/plugins/lint/lint-api/src/com/android/tools/klint/client/api/CircularDependencyException.java index 337eb279dfe..94020e080df 100644 --- a/plugins/lint/lint-api/src/com/android/tools/klint/client/api/CircularDependencyException.java +++ b/plugins/lint/lint-api/src/com/android/tools/klint/client/api/CircularDependencyException.java @@ -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; /** diff --git a/plugins/lint/lint-api/src/com/android/tools/klint/client/api/ClassEntry.java b/plugins/lint/lint-api/src/com/android/tools/klint/client/api/ClassEntry.java index c4d3eb5eb5d..5126d1b5b12 100644 --- a/plugins/lint/lint-api/src/com/android/tools/klint/client/api/ClassEntry.java +++ b/plugins/lint/lint-api/src/com/android/tools/klint/client/api/ClassEntry.java @@ -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 { diff --git a/plugins/lint/lint-api/src/com/android/tools/klint/client/api/CompositeIssueRegistry.java b/plugins/lint/lint-api/src/com/android/tools/klint/client/api/CompositeIssueRegistry.java index 70b6a09b74a..b66a8418d6f 100644 --- a/plugins/lint/lint-api/src/com/android/tools/klint/client/api/CompositeIssueRegistry.java +++ b/plugins/lint/lint-api/src/com/android/tools/klint/client/api/CompositeIssueRegistry.java @@ -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; diff --git a/plugins/lint/lint-api/src/com/android/tools/klint/client/api/Configuration.java b/plugins/lint/lint-api/src/com/android/tools/klint/client/api/Configuration.java index 64334a4eba5..8d2a60314fe 100644 --- a/plugins/lint/lint-api/src/com/android/tools/klint/client/api/Configuration.java +++ b/plugins/lint/lint-api/src/com/android/tools/klint/client/api/Configuration.java @@ -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; /** diff --git a/plugins/lint/lint-api/src/com/android/tools/klint/client/api/DefaultConfiguration.java b/plugins/lint/lint-api/src/com/android/tools/klint/client/api/DefaultConfiguration.java index ca7e76e7ccc..0a2278d0937 100644 --- a/plugins/lint/lint-api/src/com/android/tools/klint/client/api/DefaultConfiguration.java +++ b/plugins/lint/lint-api/src/com/android/tools/klint/client/api/DefaultConfiguration.java @@ -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. diff --git a/plugins/lint/lint-api/src/com/android/tools/klint/client/api/DefaultSdkInfo.java b/plugins/lint/lint-api/src/com/android/tools/klint/client/api/DefaultSdkInfo.java index 2f546594ce6..5097822ad59 100644 --- a/plugins/lint/lint-api/src/com/android/tools/klint/client/api/DefaultSdkInfo.java +++ b/plugins/lint/lint-api/src/com/android/tools/klint/client/api/DefaultSdkInfo.java @@ -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} *

diff --git a/plugins/lint/lint-api/src/com/android/tools/klint/client/api/IssueRegistry.java b/plugins/lint/lint-api/src/com/android/tools/klint/client/api/IssueRegistry.java index 09e70dc7e6b..552ea3e2cea 100644 --- a/plugins/lint/lint-api/src/com/android/tools/klint/client/api/IssueRegistry.java +++ b/plugins/lint/lint-api/src/com/android/tools/klint/client/api/IssueRegistry.java @@ -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 diff --git a/plugins/lint/lint-api/src/com/android/tools/klint/client/api/JarFileIssueRegistry.java b/plugins/lint/lint-api/src/com/android/tools/klint/client/api/JarFileIssueRegistry.java index 0262691fc7e..bcafb754a1c 100644 --- a/plugins/lint/lint-api/src/com/android/tools/klint/client/api/JarFileIssueRegistry.java +++ b/plugins/lint/lint-api/src/com/android/tools/klint/client/api/JarFileIssueRegistry.java @@ -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; diff --git a/plugins/lint/lint-api/src/com/android/tools/klint/client/api/JavaLintLanguageExtension.java b/plugins/lint/lint-api/src/com/android/tools/klint/client/api/JavaLintLanguageExtension.java new file mode 100644 index 00000000000..06ccbf6c619 --- /dev/null +++ b/plugins/lint/lint-api/src/com/android/tools/klint/client/api/JavaLintLanguageExtension.java @@ -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; + } +} diff --git a/plugins/lint/lint-api/src/com/android/tools/klint/client/api/JavaParser.java b/plugins/lint/lint-api/src/com/android/tools/klint/client/api/JavaParser.java index 3aa55d42868..4fd83c82036 100644 --- a/plugins/lint/lint-api/src/com/android/tools/klint/client/api/JavaParser.java +++ b/plugins/lint/lint-api/src/com/android/tools/klint/client/api/JavaParser.java @@ -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 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 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 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 getConstructors(); - - /** Returns the methods defined in this class, and optionally any methods inherited from any superclasses as well */ - @NonNull - public abstract Iterable 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 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 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 reference. 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 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 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) { } } diff --git a/plugins/lint/lint-api/src/com/android/tools/klint/client/api/LintClient.java b/plugins/lint/lint-api/src/com/android/tools/klint/client/api/LintClient.java index fcb4dfe14d3..d34c89a6f51 100755 --- a/plugins/lint/lint-api/src/com/android/tools/klint/client/api/LintClient.java +++ b/plugins/lint/lint-api/src/com/android/tools/klint/client/api/LintClient.java @@ -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 getConverters() { + return Collections.emptyList(); + } + /** * Information about class paths (sources, class files and libraries) * usually associated with a project. diff --git a/plugins/lint/lint-api/src/com/android/tools/klint/client/api/LintDriver.java b/plugins/lint/lint-api/src/com/android/tools/klint/client/api/LintDriver.java index d2482360145..cdc9c00b15b 100644 --- a/plugins/lint/lint-api/src/com/android/tools/klint/client/api/LintDriver.java +++ b/plugins/lint/lint-api/src/com/android/tools/klint/client/api/LintDriver.java @@ -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 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 javaCodeDetectors = mScopeDetectors.get(Scope.ALL_JAVA_FILES); + List 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 javaFileDetectors = mScopeDetectors.get(Scope.JAVA_FILE); + List 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 allLibraries = project.getAllLibraries(); Set allProjects = new HashSet(allLibraries.size() + 1); @@ -885,7 +836,7 @@ public class LintDriver { List 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 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 checks = union(mScopeDetectors.get(Scope.SOURCE_FILE), + mScopeDetectors.get(Scope.ALL_SOURCE_FILES)); if (checks != null && !checks.isEmpty()) { List files = project.getSubset(); if (files != null) { @@ -1111,7 +1062,7 @@ public class LintDriver { List 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 sourceFolders, @NonNull List 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 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 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 checks, @NonNull List files) { + List uastDetectors = new ArrayList(); - 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 detectors, + @NotNull List files) { + com.intellij.openapi.project.Project intellijProject = mClient.getProject(); + if (intellijProject == null) { return; } - JavaVisitor visitor = new JavaVisitor(javaParser, checks); + UastChecker checker = UastChecker.INSTANCE; + List converters = project.getClient().getConverters(); - List 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 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 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 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 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 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 annotations) { + for (UAnnotation annotation : annotations) { + if (annotation.matchesName(SUPPRESS_LINT) + || annotation.matchesName("SuppressWarnings")) { //$NON-NLS-1$ + List 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 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 expressions = array.getValueArguments(); + for (UExpression arrayElement : expressions) { + String elementValue = UastLiteralUtils.getValueIfStringLiteral(arrayElement); + if (elementValue != null) { + if (matches(issue, elementValue)) { + return true; } } } diff --git a/plugins/lint/lint-api/src/com/android/tools/klint/client/api/LintLanguageExtension.java b/plugins/lint/lint-api/src/com/android/tools/klint/client/api/LintLanguageExtension.java new file mode 100644 index 00000000000..e6245026482 --- /dev/null +++ b/plugins/lint/lint-api/src/com/android/tools/klint/client/api/LintLanguageExtension.java @@ -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 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 getConverters(@Nullable Project project) { + if (project == null) { + return Collections.emptyList(); + } + + LintLanguageExtension[] languageExtensions = project.getExtensions(EP_NAME); + List converters = new ArrayList(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); + } +} diff --git a/plugins/lint/lint-api/src/com/android/tools/klint/client/api/LintListener.java b/plugins/lint/lint-api/src/com/android/tools/klint/client/api/LintListener.java index 8195d36e379..7ef4ba0a305 100644 --- a/plugins/lint/lint-api/src/com/android/tools/klint/client/api/LintListener.java +++ b/plugins/lint/lint-api/src/com/android/tools/klint/client/api/LintListener.java @@ -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; /** diff --git a/plugins/lint/lint-api/src/com/android/tools/klint/client/api/LintRequest.java b/plugins/lint/lint-api/src/com/android/tools/klint/client/api/LintRequest.java index 5b51fd999ed..28b4ad22d89 100644 --- a/plugins/lint/lint-api/src/com/android/tools/klint/client/api/LintRequest.java +++ b/plugins/lint/lint-api/src/com/android/tools/klint/client/api/LintRequest.java @@ -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; diff --git a/plugins/lint/lint-api/src/com/android/tools/klint/client/api/OtherFileVisitor.java b/plugins/lint/lint-api/src/com/android/tools/klint/client/api/OtherFileVisitor.java index 0d3becd8573..7b64e46323f 100644 --- a/plugins/lint/lint-api/src/com/android/tools/klint/client/api/OtherFileVisitor.java +++ b/plugins/lint/lint-api/src/com/android/tools/klint/client/api/OtherFileVisitor.java @@ -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 files = new ArrayList(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 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); } } } diff --git a/plugins/lint/lint-api/src/com/android/tools/klint/client/api/ResourceVisitor.java b/plugins/lint/lint-api/src/com/android/tools/klint/client/api/ResourceVisitor.java index 25f5e15784c..7e257f9090c 100644 --- a/plugins/lint/lint-api/src/com/android/tools/klint/client/api/ResourceVisitor.java +++ b/plugins/lint/lint-api/src/com/android/tools/klint/client/api/ResourceVisitor.java @@ -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, diff --git a/plugins/lint/lint-api/src/com/android/tools/klint/client/api/SdkInfo.java b/plugins/lint/lint-api/src/com/android/tools/klint/client/api/SdkInfo.java index 8b3d1e97e89..e109d00ca6c 100644 --- a/plugins/lint/lint-api/src/com/android/tools/klint/client/api/SdkInfo.java +++ b/plugins/lint/lint-api/src/com/android/tools/klint/client/api/SdkInfo.java @@ -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; diff --git a/plugins/lint/lint-api/src/com/android/tools/klint/client/api/XmlParser.java b/plugins/lint/lint-api/src/com/android/tools/klint/client/api/XmlParser.java index 2660b0a8fea..b1ec10e57f6 100644 --- a/plugins/lint/lint-api/src/com/android/tools/klint/client/api/XmlParser.java +++ b/plugins/lint/lint-api/src/com/android/tools/klint/client/api/XmlParser.java @@ -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; diff --git a/plugins/lint/lint-api/src/com/android/tools/klint/detector/api/Category.java b/plugins/lint/lint-api/src/com/android/tools/klint/detector/api/Category.java index a950634b4f3..df780538370 100644 --- a/plugins/lint/lint-api/src/com/android/tools/klint/detector/api/Category.java +++ b/plugins/lint/lint-api/src/com/android/tools/klint/detector/api/Category.java @@ -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; diff --git a/plugins/lint/lint-api/src/com/android/tools/klint/detector/api/ClassContext.java b/plugins/lint/lint-api/src/com/android/tools/klint/detector/api/ClassContext.java index 2d08ed99380..15c1f0de690 100644 --- a/plugins/lint/lint-api/src/com/android/tools/klint/detector/api/ClassContext.java +++ b/plugins/lint/lint-api/src/com/android/tools/klint/detector/api/ClassContext.java @@ -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. *

@@ -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; diff --git a/plugins/lint/lint-api/src/com/android/tools/klint/detector/api/Context.java b/plugins/lint/lint-api/src/com/android/tools/klint/detector/api/Context.java index 3a04d71d05c..b977415d60d 100644 --- a/plugins/lint/lint-api/src/com/android/tools/klint/detector/api/Context.java +++ b/plugins/lint/lint-api/src/com/android/tools/klint/detector/api/Context.java @@ -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 diff --git a/plugins/lint/lint-api/src/com/android/tools/klint/detector/api/DefaultPosition.java b/plugins/lint/lint-api/src/com/android/tools/klint/detector/api/DefaultPosition.java index 72c8ee7bc28..fd370cc4d22 100644 --- a/plugins/lint/lint-api/src/com/android/tools/klint/detector/api/DefaultPosition.java +++ b/plugins/lint/lint-api/src/com/android/tools/klint/detector/api/DefaultPosition.java @@ -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; diff --git a/plugins/lint/lint-api/src/com/android/tools/klint/detector/api/Detector.java b/plugins/lint/lint-api/src/com/android/tools/klint/detector/api/Detector.java index 04e1937ee47..47ad4701343 100644 --- a/plugins/lint/lint-api/src/com/android/tools/klint/detector/api/Detector.java +++ b/plugins/lint/lint-api/src/com/android/tools/klint/detector/api/Detector.java @@ -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()}. - *

- * If you return specific AST node types from - * {@link #getApplicableNodeTypes()}, then the visitor will only - * 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. - *

- * If you return null from {@link #getApplicableNodeTypes()}, then your - * visitor will be called from the top and all node types visited. - *

- * 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. - *

- * If you return null from this method, then the visitor will process - * the full tree instead. - *

- * Note that for the shared visitor, the return codes from the visit - * methods are ignored: returning true will not 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> 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. - *

- * 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 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. - *

- * 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 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 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> 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 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 getApplicableConstructorTypes() { return null; } - @SuppressWarnings("javadoc") - public void visitConstructor( - @NonNull JavaContext context, - @Nullable AstVisitor visitor, - @NonNull ConstructorInvocation node, - @NonNull ResolvedMethod constructor) { + public List getApplicableSuperClasses() { + return null; + } + + public void visitClass(UastAndroidContext context, UClass node) { + + } + + public List 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: ---- diff --git a/plugins/lint/lint-api/src/com/android/tools/klint/detector/api/Implementation.java b/plugins/lint/lint-api/src/com/android/tools/klint/detector/api/Implementation.java index 9a4b70315a1..d312bb1ea4a 100644 --- a/plugins/lint/lint-api/src/com/android/tools/klint/detector/api/Implementation.java +++ b/plugins/lint/lint-api/src/com/android/tools/klint/detector/api/Implementation.java @@ -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; diff --git a/plugins/lint/lint-api/src/com/android/tools/klint/detector/api/Issue.java b/plugins/lint/lint-api/src/com/android/tools/klint/detector/api/Issue.java index eb2f15e03a0..b3f6abce962 100644 --- a/plugins/lint/lint-api/src/com/android/tools/klint/detector/api/Issue.java +++ b/plugins/lint/lint-api/src/com/android/tools/klint/detector/api/Issue.java @@ -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 { */ @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 { */ @NonNull public String getExplanation(@NonNull TextFormat format) { - return RAW.convertTo(mExplanation, format); + return TextFormat.RAW.convertTo(mExplanation, format); } /** diff --git a/plugins/lint/lint-api/src/com/android/tools/klint/detector/api/JavaContext.java b/plugins/lint/lint-api/src/com/android/tools/klint/detector/api/JavaContext.java index 5000c26e67c..f3796632fe3 100644 --- a/plugins/lint/lint-api/src/com/android/tools/klint/detector/api/JavaContext.java +++ b/plugins/lint/lint-api/src/com/android/tools/klint/detector/api/JavaContext.java @@ -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; * 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. */ -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 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 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 the target node type - * @return the nearest ancestor node in the parent chain, or null if not found - */ - @Nullable - public static T getParentOfType( - @Nullable Node element, - @NonNull Class 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 the target node type - * @return the nearest ancestor node in the parent chain, or null if not found - */ - @Nullable - public static T getParentOfType( - @Nullable Node element, - @NonNull Class 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 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 the target node type - * @return the nearest ancestor node in the parent chain, or null if not found - */ - @Nullable - public static T getParentOfType(@Nullable Node element, - @NonNull Class clz, - boolean strict, - @NonNull Class... 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 the type - * @return the first sibling of the given type, or null - */ - @Nullable - public static T getNextSiblingOfType(@Nullable Node sibling, - @NonNull Class clz) { - if (sibling == null) { - return null; - } - Node parent = sibling.getParent(); - if (parent == null) { - return null; - } - - Iterator 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); } } diff --git a/plugins/lint/lint-api/src/com/android/tools/klint/detector/api/LayoutDetector.java b/plugins/lint/lint-api/src/com/android/tools/klint/detector/api/LayoutDetector.java index b24c1a9d7fb..83fd6e7fdfb 100644 --- a/plugins/lint/lint-api/src/com/android/tools/klint/detector/api/LayoutDetector.java +++ b/plugins/lint/lint-api/src/com/android/tools/klint/detector/api/LayoutDetector.java @@ -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. diff --git a/plugins/lint/lint-api/src/com/android/tools/klint/detector/api/LintUtils.java b/plugins/lint/lint-api/src/com/android/tools/klint/detector/api/LintUtils.java index b233546fe03..f74876e4848 100755 --- a/plugins/lint/lint-api/src/com/android/tools/klint/detector/api/LintUtils.java +++ b/plugins/lint/lint-api/src/com/android/tools/klint/detector/api/LintUtils.java @@ -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; } } diff --git a/plugins/lint/lint-api/src/com/android/tools/klint/detector/api/Location.java b/plugins/lint/lint-api/src/com/android/tools/klint/detector/api/Location.java index 284e41b0968..dcb63d825ab 100644 --- a/plugins/lint/lint-api/src/com/android/tools/klint/detector/api/Location.java +++ b/plugins/lint/lint-api/src/com/android/tools/klint/detector/api/Location.java @@ -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; diff --git a/plugins/lint/lint-api/src/com/android/tools/klint/detector/api/Position.java b/plugins/lint/lint-api/src/com/android/tools/klint/detector/api/Position.java index 6407cc9df5d..749f6b41051 100644 --- a/plugins/lint/lint-api/src/com/android/tools/klint/detector/api/Position.java +++ b/plugins/lint/lint-api/src/com/android/tools/klint/detector/api/Position.java @@ -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; diff --git a/plugins/lint/lint-api/src/com/android/tools/klint/detector/api/Project.java b/plugins/lint/lint-api/src/com/android/tools/klint/detector/api/Project.java index 9141b9feaaa..569ff7434ea 100755 --- a/plugins/lint/lint-api/src/com/android/tools/klint/detector/api/Project.java +++ b/plugins/lint/lint-api/src/com/android/tools/klint/detector/api/Project.java @@ -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; diff --git a/plugins/lint/lint-api/src/com/android/tools/klint/detector/api/ResourceContext.java b/plugins/lint/lint-api/src/com/android/tools/klint/detector/api/ResourceContext.java index 3fdb6b786da..3bc6d49d316 100644 --- a/plugins/lint/lint-api/src/com/android/tools/klint/detector/api/ResourceContext.java +++ b/plugins/lint/lint-api/src/com/android/tools/klint/detector/api/ResourceContext.java @@ -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; diff --git a/plugins/lint/lint-api/src/com/android/tools/klint/detector/api/ResourceXmlDetector.java b/plugins/lint/lint-api/src/com/android/tools/klint/detector/api/ResourceXmlDetector.java index bd574071701..61d67f9d5fa 100644 --- a/plugins/lint/lint-api/src/com/android/tools/klint/detector/api/ResourceXmlDetector.java +++ b/plugins/lint/lint-api/src/com/android/tools/klint/detector/api/ResourceXmlDetector.java @@ -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; diff --git a/plugins/lint/lint-api/src/com/android/tools/klint/detector/api/Scope.java b/plugins/lint/lint-api/src/com/android/tools/klint/detector/api/Scope.java index 55dd7783644..c2c0795f336 100644 --- a/plugins/lint/lint-api/src/com/android/tools/klint/detector/api/Scope.java +++ b/plugins/lint/lint-api/src/com/android/tools/klint/detector/api/Scope.java @@ -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 all the Java source files together. *

- * 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 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 JAVA_FILE_SCOPE = EnumSet.of(JAVA_FILE); + public static final EnumSet 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 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 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 JAVA_AND_RESOURCE_FILES = - EnumSet.of(RESOURCE_FILE, JAVA_FILE); + public static final EnumSet 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 CLASS_AND_ALL_RESOURCE_FILES = EnumSet.of(ALL_RESOURCE_FILES, CLASS_FILE); diff --git a/plugins/lint/lint-api/src/com/android/tools/klint/detector/api/Severity.java b/plugins/lint/lint-api/src/com/android/tools/klint/detector/api/Severity.java index e0f90f0857c..a2d3c56b945 100644 --- a/plugins/lint/lint-api/src/com/android/tools/klint/detector/api/Severity.java +++ b/plugins/lint/lint-api/src/com/android/tools/klint/detector/api/Severity.java @@ -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; diff --git a/plugins/lint/lint-api/src/com/android/tools/klint/detector/api/Speed.java b/plugins/lint/lint-api/src/com/android/tools/klint/detector/api/Speed.java index 4ef1eb1702e..b790a1a683b 100644 --- a/plugins/lint/lint-api/src/com/android/tools/klint/detector/api/Speed.java +++ b/plugins/lint/lint-api/src/com/android/tools/klint/detector/api/Speed.java @@ -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; diff --git a/plugins/lint/lint-api/src/com/android/tools/klint/detector/api/TextFormat.java b/plugins/lint/lint-api/src/com/android/tools/klint/detector/api/TextFormat.java index ed1bd3ca90d..0855b10f817 100644 --- a/plugins/lint/lint-api/src/com/android/tools/klint/detector/api/TextFormat.java +++ b/plugins/lint/lint-api/src/com/android/tools/klint/detector/api/TextFormat.java @@ -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; diff --git a/plugins/lint/lint-api/src/com/android/tools/klint/detector/api/XmlContext.java b/plugins/lint/lint-api/src/com/android/tools/klint/detector/api/XmlContext.java index 891eced0f72..7dd2e6bf9d9 100644 --- a/plugins/lint/lint-api/src/com/android/tools/klint/detector/api/XmlContext.java +++ b/plugins/lint/lint-api/src/com/android/tools/klint/detector/api/XmlContext.java @@ -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; diff --git a/plugins/lint/lint-checks/lint-checks.iml b/plugins/lint/lint-checks/lint-checks.iml new file mode 100644 index 00000000000..358e50b450b --- /dev/null +++ b/plugins/lint/lint-checks/lint-checks.iml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/AccessibilityDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/AccessibilityDetector.java index 26e5a2c02d4..2fc267c59b5 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/AccessibilityDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/AccessibilityDetector.java @@ -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; diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/AddJavascriptInterfaceDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/AddJavascriptInterfaceDetector.java index e80b9f24e57..52367921a96 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/AddJavascriptInterfaceDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/AddJavascriptInterfaceDetector.java @@ -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 getApplicableMethodNames() { + public List 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 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); } } diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/AlarmDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/AlarmDetector.java index 6b46fadb381..27a3dfdeca3 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/AlarmDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/AlarmDetector.java @@ -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 getApplicableMethodNames() { + public List 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 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(); } diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/AlwaysShowActionDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/AlwaysShowActionDetector.java index 91fe2b6d051..33956b1f387 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/AlwaysShowActionDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/AlwaysShowActionDetector.java @@ -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> getApplicableNodeTypes() { - return Collections.>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(); } - mAlwaysFields.add(mContext.getLocation(node)); + mAlwaysFields.add(UastAndroidUtils.getLocation(node)); } else { mHasIfRoomRefs = true; } } - return super.visitSelect(node); + return false; } } } diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/AnnotationDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/AnnotationDetector.java index 66ece7e1b48..7666136e7c1 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/AnnotationDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/AnnotationDetector.java @@ -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> getApplicableNodeTypes() { - return Collections.>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 expressions = - array.astExpressions(); - if (expressions == null) { - continue; - } - Iterator 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)); diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/Api.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/Api.java index ca84b27c993..fee8695bcb7 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/Api.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/Api.java @@ -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; diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ApiClass.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ApiClass.java index c5946b92c0b..b23c5b466d0 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ApiClass.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ApiClass.java @@ -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; diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ApiDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ApiDetector.java index 1af53ce4fe8..11799d332ea 100755 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ApiDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ApiDetector.java @@ -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 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 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); } - } + }*/ } diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ApiLookup.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ApiLookup.java index 4097a3fb95f..b7341afbaad 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ApiLookup.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ApiLookup.java @@ -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; diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ApiParser.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ApiParser.java index b3c2f2aec8a..26ba6413277 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ApiParser.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ApiParser.java @@ -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; diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/AppCompatCallDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/AppCompatCallDetector.java index 8a8483e2b73..99754d2224d 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/AppCompatCallDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/AppCompatCallDetector.java @@ -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 getApplicableMethodNames() { + public List 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; diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/AppCompatResourceDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/AppCompatResourceDetector.java index 11222ac5e1e..bd713d3a57d 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/AppCompatResourceDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/AppCompatResourceDetector.java @@ -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$ diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/AppIndexingApiDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/AppIndexingApiDetector.java index 1a8dd676529..5ccd6c98963 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/AppIndexingApiDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/AppIndexingApiDetector.java @@ -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; diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ArraySizeDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ArraySizeDetector.java index 1045cf63dab..83ccf7ae3a5 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ArraySizeDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ArraySizeDetector.java @@ -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; diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/AssertDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/AssertDetector.java index 309dff9e8d9..128fe9315ab 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/AssertDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/AssertDetector.java @@ -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> getApplicableNodeTypes() { - return Collections.>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; diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/BuiltinIssueRegistry.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/BuiltinIssueRegistry.java index 290c23bf492..c9a23040926 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/BuiltinIssueRegistry.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/BuiltinIssueRegistry.java @@ -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; diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ButtonDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ButtonDetector.java index a0e5b77f32f..43774708c1d 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ButtonDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ButtonDetector.java @@ -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; diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ByteOrderMarkDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ByteOrderMarkDetector.java index 311a987a6cf..8bf6710d4b2 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ByteOrderMarkDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ByteOrderMarkDetector.java @@ -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; diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/CallSuperDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/CallSuperDetector.java index 51d9c325bb0..602f07fb427 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/CallSuperDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/CallSuperDetector.java @@ -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> getApplicableNodeTypes() { - return Collections.>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 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 superFunctions = method.getSuperFunctions(context); + return superFunctions.isEmpty() ? null : superFunctions.get(0); } // Look up annotations metadata - ResolvedMethod directSuper = method.getSuperMethod(); - ResolvedMethod superMethod = directSuper; + List superFunctions = method.getSuperFunctions(context); + UFunction directSuper = superFunctions.isEmpty() ? null : superFunctions.get(0); + UFunction superMethod = directSuper; while (superMethod != null) { - Iterable 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); + } } } } diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ChildCountDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ChildCountDetector.java index b618d0c25ce..ffe69eaa5c2 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ChildCountDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ChildCountDetector.java @@ -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; diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/CipherGetInstanceDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/CipherGetInstanceDetector.java index 3c53650355a..39a44036002 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/CipherGetInstanceDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/CipherGetInstanceDetector.java @@ -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 getApplicableMethodNames() { + public List 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 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 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); } } } diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/CleanupDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/CleanupDetector.java index b28df292e71..b1adeea5c4e 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/CleanupDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/CleanupDetector.java @@ -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 getApplicableMethodNames() { + public List 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 arguments = - call.astArguments(); + List 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 mVariables; + private abstract static class FinishVisitor extends UastVisitor { + protected final UastAndroidContext mContext; + protected final List 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; } } } diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ClickableViewAccessibilityDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ClickableViewAccessibilityDetector.java index 80b4bd73b10..79ce7082080 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ClickableViewAccessibilityDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ClickableViewAccessibilityDetector.java @@ -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; diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/CommentDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/CommentDetector.java index 24b174ef917..4a381f8bd60 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/CommentDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/CommentDetector.java @@ -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> getApplicableNodeTypes() { if (USE_AST) { return Collections.>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"); } } - } + }*/ } diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ControlFlowGraph.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ControlFlowGraph.java index 6c11ec18589..a48d4d0936d 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ControlFlowGraph.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ControlFlowGraph.java @@ -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; diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/CustomViewDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/CustomViewDetector.java index a243e40f186..93992646060 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/CustomViewDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/CustomViewDetector.java @@ -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 getApplicableMethodNames() { + public List 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 expressions = node.astArguments(); + List 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); } } } diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/CutPasteDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/CutPasteDetector.java index 07dbcd3dcf0..f4ff7aef4e9 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/CutPasteDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/CutPasteDetector.java @@ -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 mIds; + private UFunction mLastMethod; + private Map mIds; private Map mLhs; private Map mCallOperands; @@ -87,22 +79,22 @@ public class CutPasteDetector extends Detector implements Detector.JavaScanner { return true; } - // ---- Implements JavaScanner ---- + // ---- Implements UastScanner ---- + @Override - public List getApplicableMethodNames() { + public List 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); + } } } } diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/DateFormatDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/DateFormatDetector.java index 1ad78533cd2..f621b15b76f 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/DateFormatDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/DateFormatDetector.java @@ -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; } } diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/DeprecationDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/DeprecationDetector.java index fec87c8b29e..6d3c4e79e9e 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/DeprecationDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/DeprecationDetector.java @@ -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; diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/DetectMissingPrefix.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/DetectMissingPrefix.java index c7271fc2a23..99f00a5e573 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/DetectMissingPrefix.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/DetectMissingPrefix.java @@ -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; diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/DosLineEndingDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/DosLineEndingDetector.java index 7fbc3f3bb30..59e86d0f2b0 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/DosLineEndingDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/DosLineEndingDetector.java @@ -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; diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/DuplicateIdDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/DuplicateIdDetector.java index d93f7b42ccd..1d6de3cb4cb 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/DuplicateIdDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/DuplicateIdDetector.java @@ -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; diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/DuplicateResourceDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/DuplicateResourceDetector.java index ee3ca8bb406..d40b946ef23 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/DuplicateResourceDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/DuplicateResourceDetector.java @@ -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; diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ExtraTextDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ExtraTextDetector.java index f9b9e85658d..2ce4efcda46 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ExtraTextDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ExtraTextDetector.java @@ -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; diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/FieldGetterDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/FieldGetterDetector.java index 4d93230b376..edd65a5a403 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/FieldGetterDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/FieldGetterDetector.java @@ -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; diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/FragmentDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/FragmentDetector.java index 40e0a73a783..0e724cd1dce 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/FragmentDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/FragmentDetector.java @@ -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 applicableSuperClasses() { + public List 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); } } } diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/FullBackupContentDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/FullBackupContentDetector.java index 3b055897519..3ae668bcfc8 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/FullBackupContentDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/FullBackupContentDetector.java @@ -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 } XML elements */ diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/GetSignaturesDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/GetSignaturesDetector.java index 76aec22cc5d..776f78c8f74 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/GetSignaturesDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/GetSignaturesDetector.java @@ -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 getApplicableMethodNames() { + public List 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 argumentList = node.astArguments(); + + List 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; - } } diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/GradleDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/GradleDetector.java index b40f2b62688..0509961609c 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/GradleDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/GradleDetector.java @@ -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; diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/GridLayoutDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/GridLayoutDetector.java index 08fbaeb2a83..142e22bb3cd 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/GridLayoutDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/GridLayoutDetector.java @@ -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; diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/HandlerDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/HandlerDetector.java index 1d7ff8ed8a1..944ce0abb36 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/HandlerDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/HandlerDetector.java @@ -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 applicableSuperClasses() { + public List 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; } } diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/HardcodedDebugModeDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/HardcodedDebugModeDetector.java index 82981c4db19..6251abe352b 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/HardcodedDebugModeDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/HardcodedDebugModeDetector.java @@ -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; diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/HardcodedValuesDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/HardcodedValuesDetector.java index 288b3b1d08b..a1f2759d34a 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/HardcodedValuesDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/HardcodedValuesDetector.java @@ -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; diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/IconDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/IconDetector.java index 22d562ec498..116891d7b6a 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/IconDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/IconDetector.java @@ -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 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> getApplicableNodeTypes() { - List> types = new ArrayList>(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 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 args = node.astArguments(); + List 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 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 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 icons = mMenuToIcons.get(name); @@ -2101,7 +2097,7 @@ public class IconDetector extends ResourceXmlDetector implements Detector.JavaSc } } - return super.visitSelect(node); + return super.visitQualifiedExpression(node); } } } diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/IncludeDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/IncludeDetector.java index bf87d12c0ce..08c03bf6083 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/IncludeDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/IncludeDetector.java @@ -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; diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/InefficientWeightDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/InefficientWeightDetector.java index 36ac6f2b62b..300f8d78b72 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/InefficientWeightDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/InefficientWeightDetector.java @@ -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_BASELINE_ALIGNED; @@ -36,16 +36,16 @@ import static com.android.SdkConstants.VIEW_TAG; import com.android.annotations.NonNull; import com.android.ide.common.rendering.api.ResourceValue; -import com.android.tools.lint.client.api.SdkInfo; -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.client.api.SdkInfo; +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.Attr; import org.w3c.dom.Element; diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/InvalidPackageDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/InvalidPackageDetector.java index 027cdf8a647..26ce642ac2b 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/InvalidPackageDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/InvalidPackageDetector.java @@ -14,19 +14,19 @@ * 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.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.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.Lists; import com.google.common.collect.Sets; diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/JavaPerformanceDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/JavaPerformanceDetector.java index 52c6b6a091e..c527591586d 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/JavaPerformanceDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/JavaPerformanceDetector.java @@ -14,24 +14,23 @@ * limitations under the License. */ -package com.android.tools.lint.checks; +package com.android.tools.klint.checks; import static com.android.SdkConstants.SUPPORT_LIB_ARTIFACT; -import static com.android.tools.lint.client.api.JavaParser.TYPE_BOOLEAN; -import static com.android.tools.lint.client.api.JavaParser.TYPE_INT; 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.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.JavaContext; +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.Speed; +import com.android.tools.klint.detector.api.TextFormat; import com.google.common.collect.Sets; import com.google.common.collect.Sets.SetView; @@ -42,33 +41,20 @@ import java.util.HashSet; import java.util.Iterator; 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.If; -import lombok.ast.MethodDeclaration; -import lombok.ast.MethodInvocation; -import lombok.ast.Node; -import lombok.ast.Select; -import lombok.ast.StrictListAccessor; -import lombok.ast.This; -import lombok.ast.Throw; -import lombok.ast.TypeReference; -import lombok.ast.TypeReferencePart; -import lombok.ast.UnaryExpression; -import lombok.ast.VariableDefinition; -import lombok.ast.VariableReference; +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; /** * Looks for performance issues in Java files, such as memory allocations during * drawing operations and using HashMap instead of SparseArray. */ -public class JavaPerformanceDetector extends Detector implements Detector.JavaScanner { +public class JavaPerformanceDetector extends Detector implements UastScanner { - private static final Implementation IMPLEMENTATION = new Implementation( + private static final Implementation + IMPLEMENTATION = new Implementation( JavaPerformanceDetector.class, Scope.JAVA_FILE_SCOPE); @@ -159,65 +145,73 @@ public class JavaPerformanceDetector extends Detector implements Detector.JavaSc return Speed.FAST; } - // ---- Implements JavaScanner ---- + // ---- Implements UastScanner ---- @Override - public List> getApplicableNodeTypes() { - List> types = new ArrayList>(3); - types.add(ConstructorInvocation.class); - types.add(MethodDeclaration.class); - types.add(MethodInvocation.class); - return types; - } - - @Override - public AstVisitor createJavaVisitor(@NonNull JavaContext context) { + public UastVisitor createUastVisitor(UastAndroidContext context) { return new PerformanceVisitor(context); } - private static class PerformanceVisitor extends ForwardingAstVisitor { - private final JavaContext mContext; + private static class PerformanceVisitor extends UastVisitor { + private final UastAndroidContext mContext; private final boolean mCheckMaps; private final boolean mCheckAllocations; private final boolean mCheckValueOf; /** Whether allocations should be "flagged" in the current method */ private boolean mFlagAllocations; - public PerformanceVisitor(JavaContext context) { + public PerformanceVisitor(UastAndroidContext context) { mContext = context; - mCheckAllocations = context.isEnabled(PAINT_ALLOC); - mCheckMaps = context.isEnabled(USE_SPARSE_ARRAY); - mCheckValueOf = context.isEnabled(USE_VALUE_OF); + JavaContext lintContext = context.getLintContext(); + mCheckAllocations = lintContext.isEnabled(PAINT_ALLOC); + mCheckMaps = lintContext.isEnabled(USE_SPARSE_ARRAY); + mCheckValueOf = lintContext.isEnabled(USE_VALUE_OF); } @Override - public boolean visitMethodDeclaration(MethodDeclaration node) { + public boolean visitFunction(@NotNull UFunction node) { mFlagAllocations = isBlockedAllocationMethod(node); - - return super.visitMethodDeclaration(node); + return super.visitFunction(node); } @Override - public boolean visitConstructorInvocation(ConstructorInvocation node) { + public boolean visitCallExpression(@NotNull UCallExpression node) { + UastCallKind kind = node.getKind(); + if (kind == UastCallKind.CONSTRUCTOR_CALL) { + visitConstructorInvocation(node); + } else if (kind == UastCallKind.FUNCTION_CALL) { + visitFunctionInvocation(node); + } + + return super.visitCallExpression(node); + } + + private void visitConstructorInvocation(UCallExpression node) { + USimpleReferenceExpression classReference = node.getClassReference(); + if (classReference == null) { + return; + } + String typeName = null; if (mCheckMaps) { - TypeReference reference = node.astTypeReference(); - typeName = reference.astParts().last().astIdentifier().astValue(); - // TODO: Should we handle factory method constructions of HashMaps as well, - // e.g. via Guava? This is a bit trickier since we need to infer the type - // arguments from the calling context. - if (typeName.equals(HASH_MAP)) { - checkHashMap(node, reference); - } else if (typeName.equals(SPARSE_ARRAY)) { - checkSparseArray(node, reference); + UClass clazz = classReference.resolveClass(mContext); + if (clazz != null) { + typeName = clazz.getName(); + // TODO: Should we handle factory method constructions of HashMaps as well, + // e.g. via Guava? This is a bit trickier since we need to infer the type + // arguments from the calling context. + if (clazz.matchesFqName(HASH_MAP)) { + checkHashMap(node); + } else if (clazz.matchesFqName(SPARSE_ARRAY)) { + checkSparseArray(node); + } } } if (mCheckValueOf) { if (typeName == null) { - TypeReference reference = node.astTypeReference(); - typeName = reference.astParts().last().astIdentifier().astValue(); + typeName = classReference.getIdentifier(); } if ((typeName.equals(INTEGER) || typeName.equals(BOOLEAN) @@ -226,65 +220,63 @@ public class JavaPerformanceDetector extends Detector implements Detector.JavaSc || typeName.equals(LONG) || typeName.equals(DOUBLE) || typeName.equals(BYTE)) - && node.astTypeReference().astParts().size() == 1 - && node.astArguments().size() == 1) { - String argument = node.astArguments().first().toString(); + && node.getValueArgumentCount() == 1) { + String argument = node.getValueArguments().get(0).renderString(); mContext.report(USE_VALUE_OF, node, mContext.getLocation(node), getUseValueOfErrorMessage( typeName, argument)); } } - if (mFlagAllocations && !(node.getParent() instanceof Throw) && mCheckAllocations) { + if (mFlagAllocations && !(UastUtils.isThrow(node.getParent())) && mCheckAllocations) { // Make sure we're still inside the method declaration that marked // mInDraw as true, in case we've left it and we're in a static // block or something: - Node method = node; + UElement method = node; while (method != null) { - if (method instanceof MethodDeclaration) { + if (method instanceof UFunction) { break; } method = method.getParent(); } - if (method != null && isBlockedAllocationMethod(((MethodDeclaration) method)) + if (method != null && isBlockedAllocationMethod(((UFunction) method)) && !isLazilyInitialized(node)) { reportAllocation(node); } } - - return super.visitConstructorInvocation(node); } - private void reportAllocation(Node node) { + private void reportAllocation(UElement node) { mContext.report(PAINT_ALLOC, node, mContext.getLocation(node), "Avoid object allocations during draw/layout operations (preallocate and " + "reuse instead)"); } - @Override - public boolean visitMethodInvocation(MethodInvocation node) { - if (mFlagAllocations && node.astOperand() != null) { + private void visitFunctionInvocation(UCallExpression node) { + UExpression operand = UastUtils.getReceiver(node); + + if (mFlagAllocations && operand != null) { // Look for forbidden methods - String methodName = node.astName().astValue(); - if (methodName.equals("createBitmap") //$NON-NLS-1$ - || methodName.equals("createScaledBitmap")) { //$NON-NLS-1$ - String operand = node.astOperand().toString(); - if (operand.equals("Bitmap") //$NON-NLS-1$ - || operand.equals("android.graphics.Bitmap")) { //$NON-NLS-1$ + String methodName = node.getFunctionName(); + if ("createBitmap".equals(methodName) //$NON-NLS-1$ + || "createScaledBitmap".equals(methodName)) { //$NON-NLS-1$ + String operandText = operand.renderString(); + if (operandText.equals("Bitmap") //$NON-NLS-1$ + || operandText.equals("android.graphics.Bitmap")) { //$NON-NLS-1$ if (!isLazilyInitialized(node)) { reportAllocation(node); } } - } else if (methodName.startsWith("decode")) { //$NON-NLS-1$ + } else if (methodName != null && methodName.startsWith("decode")) { //$NON-NLS-1$ // decodeFile, decodeByteArray, ... - String operand = node.astOperand().toString(); - if (operand.equals("BitmapFactory") //$NON-NLS-1$ - || operand.equals("android.graphics.BitmapFactory")) { //$NON-NLS-1$ + String operandText = operand.renderString(); + if (operandText.equals("BitmapFactory") //$NON-NLS-1$ + || operandText.equals("android.graphics.BitmapFactory")) { //$NON-NLS-1$ if (!isLazilyInitialized(node)) { reportAllocation(node); } } - } else if (methodName.equals("getClipBounds")) { //$NON-NLS-1$ - if (node.astArguments().isEmpty()) { + } else if ("getClipBounds".equals(methodName)) { //$NON-NLS-1$ + if (node.getValueArgumentCount() == 0) { mContext.report(PAINT_ALLOC, node, mContext.getLocation(node), "Avoid object allocations during draw operations: Use " + "`Canvas.getClipBounds(Rect)` instead of `Canvas.getClipBounds()` " + @@ -292,8 +284,6 @@ public class JavaPerformanceDetector extends Detector implements Detector.JavaSc } } } - - return super.visitMethodInvocation(node); } /** @@ -316,13 +306,13 @@ public class JavaPerformanceDetector extends Detector implements Detector.JavaSc * } * */ - private static boolean isLazilyInitialized(Node node) { - Node curr = node.getParent(); + private static boolean isLazilyInitialized(UElement node) { + UElement curr = node.getParent(); while (curr != null) { - if (curr instanceof MethodDeclaration) { + if (curr instanceof UFunction) { return false; - } else if (curr instanceof If) { - If ifNode = (If) curr; + } else if (curr instanceof UIfExpression) { + UIfExpression ifNode = (UIfExpression) curr; // See if the if block represents a lazy initialization: // compute all variable names seen in the condition // (e.g. for "if (foo == null || bar != foo)" the result is "foo,bar"), @@ -331,11 +321,14 @@ public class JavaPerformanceDetector extends Detector implements Detector.JavaSc // guarded (so lazily initialized and an allocation we won't complain // about.) List assignments = new ArrayList(); - AssignmentTracker visitor = new AssignmentTracker(assignments); - ifNode.astStatement().accept(visitor); + UExpression thenBranch = ifNode.getThenBranch(); + if (thenBranch != null) { + AssignmentTracker visitor = new AssignmentTracker(assignments); + visitor.process(thenBranch); + } if (!assignments.isEmpty()) { List references = new ArrayList(); - addReferencedVariables(references, ifNode.astCondition()); + addReferencedVariables(references, ifNode.getCondition()); if (!references.isEmpty()) { SetView intersection = Sets.intersection( new HashSet(assignments), @@ -354,21 +347,21 @@ public class JavaPerformanceDetector extends Detector implements Detector.JavaSc /** Adds any variables referenced in the given expression into the given list */ private static void addReferencedVariables(Collection variables, - Expression expression) { - if (expression instanceof BinaryExpression) { - BinaryExpression binary = (BinaryExpression) expression; - addReferencedVariables(variables, binary.astLeft()); - addReferencedVariables(variables, binary.astRight()); - } else if (expression instanceof UnaryExpression) { - UnaryExpression unary = (UnaryExpression) expression; - addReferencedVariables(variables, unary.astOperand()); - } else if (expression instanceof VariableReference) { - VariableReference reference = (VariableReference) expression; - variables.add(reference.astIdentifier().astValue()); - } else if (expression instanceof Select) { - Select select = (Select) expression; - if (select.astOperand() instanceof This) { - variables.add(select.astIdentifier().astValue()); + UExpression expression) { + if (expression instanceof UBinaryExpression) { + UBinaryExpression binary = (UBinaryExpression) expression; + addReferencedVariables(variables, binary.getLeftOperand()); + addReferencedVariables(variables, binary.getRightOperand()); + } else if (expression instanceof UUnaryExpression) { + UUnaryExpression unary = (UUnaryExpression) expression; + addReferencedVariables(variables, unary.getOperand()); + } else if (expression instanceof USimpleReferenceExpression) { + USimpleReferenceExpression reference = (USimpleReferenceExpression) expression; + variables.add(reference.getIdentifier()); + } else if (expression instanceof UQualifiedExpression) { + UQualifiedExpression select = (UQualifiedExpression) expression; + if (select.getReceiver() instanceof UThisExpression && select.getSelector() instanceof USimpleReferenceExpression) { + variables.add(((USimpleReferenceExpression) select.getSelector()).getIdentifier()); } } } @@ -377,7 +370,7 @@ public class JavaPerformanceDetector extends Detector implements Detector.JavaSc * Returns whether the given method declaration represents a method * where allocating objects is not allowed for performance reasons */ - private static boolean isBlockedAllocationMethod(MethodDeclaration node) { + private static boolean isBlockedAllocationMethod(UFunction node) { return isOnDrawMethod(node) || isOnMeasureMethod(node) || isOnLayoutMethod(node) || isLayoutMethod(node); } @@ -386,15 +379,12 @@ public class JavaPerformanceDetector extends Detector implements Detector.JavaSc * Returns true if this method looks like it's overriding android.view.View's * {@code protected void onDraw(Canvas canvas)} */ - private static boolean isOnDrawMethod(MethodDeclaration node) { - if (ON_DRAW.equals(node.astMethodName().astValue())) { - StrictListAccessor parameters = - node.astParameters(); - if (parameters != null && parameters.size() == 1) { - VariableDefinition arg0 = parameters.first(); - TypeReferencePart type = arg0.astTypeReference().astParts().last(); - String typeName = type.getTypeName(); - if (typeName.equals(CANVAS)) { + private static boolean isOnDrawMethod(UFunction node) { + if (ON_DRAW.equals(node.getName())) { + List parameters = node.getValueParameters(); + if (parameters.size() == 1) { + UVariable arg0 = parameters.get(0); + if (arg0.getType().matchesName(CANVAS)) { return true; } } @@ -409,24 +399,23 @@ public class JavaPerformanceDetector extends Detector implements Detector.JavaSc * {@code protected void onLayout(boolean changed, int left, int top, * int right, int bottom)} */ - private static boolean isOnLayoutMethod(MethodDeclaration node) { - if (ON_LAYOUT.equals(node.astMethodName().astValue())) { - StrictListAccessor parameters = - node.astParameters(); - if (parameters != null && parameters.size() == 5) { - Iterator iterator = parameters.iterator(); + private static boolean isOnLayoutMethod(UFunction node) { + if (ON_LAYOUT.equals(node.getName())) { + List parameters = node.getValueParameters(); + if (parameters.size() == 5) { + Iterator iterator = parameters.iterator(); if (!iterator.hasNext()) { return false; } // Ensure that the argument list matches boolean, int, int, int, int - TypeReferencePart type = iterator.next().astTypeReference().astParts().last(); - if (!type.getTypeName().equals(TYPE_BOOLEAN) || !iterator.hasNext()) { + UType type = iterator.next().getType(); + if (!type.isBoolean() || !iterator.hasNext()) { return false; } for (int i = 0; i < 4; i++) { - type = iterator.next().astTypeReference().astParts().last(); - if (!type.getTypeName().equals(TYPE_INT)) { + type = iterator.next().getType(); + if (!type.isInt()) { return false; } if (!iterator.hasNext()) { @@ -443,17 +432,13 @@ public class JavaPerformanceDetector extends Detector implements Detector.JavaSc * Returns true if this method looks like it's overriding android.view.View's * {@code protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)} */ - private static boolean isOnMeasureMethod(MethodDeclaration node) { - if (ON_MEASURE.equals(node.astMethodName().astValue())) { - StrictListAccessor parameters = - node.astParameters(); - if (parameters != null && parameters.size() == 2) { - VariableDefinition arg0 = parameters.first(); - VariableDefinition arg1 = parameters.last(); - TypeReferencePart type1 = arg0.astTypeReference().astParts().last(); - TypeReferencePart type2 = arg1.astTypeReference().astParts().last(); - return TYPE_INT.equals(type1.getTypeName()) - && TYPE_INT.equals(type2.getTypeName()); + private static boolean isOnMeasureMethod(UFunction node) { + if (ON_MEASURE.equals(node.getName())) { + List parameters = node.getValueParameters(); + if (parameters.size() == 2) { + UVariable arg0 = parameters.get(0); + UVariable arg1 = parameters.get(parameters.size() - 1); + return arg0.getType().isInt() && arg1.getType().isInt(); } } @@ -464,19 +449,17 @@ public class JavaPerformanceDetector extends Detector implements Detector.JavaSc * Returns true if this method looks like it's overriding android.view.View's * {@code public void layout(int l, int t, int r, int b)} */ - private static boolean isLayoutMethod(MethodDeclaration node) { - if (LAYOUT.equals(node.astMethodName().astValue())) { - StrictListAccessor parameters = - node.astParameters(); - if (parameters != null && parameters.size() == 4) { - Iterator iterator = parameters.iterator(); + private static boolean isLayoutMethod(UFunction node) { + if (LAYOUT.equals(node.getName())) { + List parameters = node.getValueParameters(); + if (parameters.size() == 4) { + Iterator iterator = parameters.iterator(); for (int i = 0; i < 4; i++) { if (!iterator.hasNext()) { return false; } - VariableDefinition next = iterator.next(); - TypeReferencePart type = next.astTypeReference().astParts().last(); - if (!TYPE_INT.equals(type.getTypeName())) { + UVariable next = iterator.next(); + if (!next.getType().isInt()) { return false; } } @@ -493,15 +476,18 @@ public class JavaPerformanceDetector extends Detector implements Detector.JavaSc * to a HashMap constructor call that is eligible for replacement by a * SparseArray call instead */ - private void checkHashMap(ConstructorInvocation node, TypeReference reference) { + private void checkHashMap(UCallExpression node) { // reference.hasTypeArguments returns false where it should not - StrictListAccessor types = reference.getTypeArguments(); - if (types != null && types.size() == 2) { - TypeReference first = types.first(); - String typeName = first.getTypeName(); - int minSdk = mContext.getMainProject().getMinSdk(); + List types = node.getTypeArguments(); + if (types.size() == 2) { + UType first = types.get(0); + String typeName = first.getName(); + + Project mainProject = mContext.getLintContext().getMainProject(); + int minSdk = mainProject.getMinSdk(); + if (typeName.equals(INTEGER) || typeName.equals(BYTE)) { - String valueType = types.last().getTypeName(); + String valueType = types.get(1).getName(); if (valueType.equals(INTEGER)) { mContext.report(USE_SPARSE_ARRAY, node, mContext.getLocation(node), "Use new `SparseIntArray(...)` instead for better performance"); @@ -518,7 +504,7 @@ public class JavaPerformanceDetector extends Detector implements Detector.JavaSc valueType)); } } else if (typeName.equals(LONG) && (minSdk >= 16 || - Boolean.TRUE == mContext.getMainProject().dependsOn( + Boolean.TRUE == mainProject.dependsOn( SUPPORT_LIB_ARTIFACT))) { boolean useBuiltin = minSdk >= 16; String message = useBuiltin ? @@ -530,12 +516,12 @@ public class JavaPerformanceDetector extends Detector implements Detector.JavaSc } } - private void checkSparseArray(ConstructorInvocation node, TypeReference reference) { + private void checkSparseArray(UCallExpression node) { // reference.hasTypeArguments returns false where it should not - StrictListAccessor types = reference.getTypeArguments(); - if (types != null && types.size() == 1) { - TypeReference first = types.first(); - String valueType = first.getTypeName(); + List types = node.getTypeArguments(); + if (types.size() == 1) { + UType first = types.get(0); + String valueType = first.getName(); if (valueType.equals(INTEGER)) { mContext.report(USE_SPARSE_ARRAY, node, mContext.getLocation(node), "Use `new SparseIntArray(...)` instead for better performance"); @@ -567,7 +553,7 @@ public class JavaPerformanceDetector extends Detector implements Detector.JavaSc } /** Visitor which records variable names assigned into */ - private static class AssignmentTracker extends ForwardingAstVisitor { + private static class AssignmentTracker extends UastVisitor { private final Collection mVariables; public AssignmentTracker(Collection variables) { @@ -575,17 +561,22 @@ public class JavaPerformanceDetector extends Detector implements Detector.JavaSc } @Override - public boolean visitBinaryExpression(BinaryExpression node) { - BinaryOperator operator = node.astOperator(); - if (operator == BinaryOperator.ASSIGN || operator == BinaryOperator.OR_ASSIGN) { - Expression left = node.astLeft(); - String variable; - if (left instanceof Select && ((Select) left).astOperand() instanceof This) { - variable = ((Select) left).astIdentifier().astValue(); - } else { - variable = left.toString(); + public boolean visitBinaryExpression(@NotNull UBinaryExpression node) { + UastBinaryOperator operator = node.getOperator(); + if (operator == UastBinaryOperator.ASSIGN || operator == UastBinaryOperator.OR_ASSIGN) { + UExpression left = node.getLeftOperand(); + String variable = null; + if (left instanceof UQualifiedExpression && ((UQualifiedExpression) left).getReceiver() instanceof UThisExpression) { + UExpression selector = ((UQualifiedExpression) left).getSelector(); + if (selector instanceof USimpleReferenceExpression) { + variable = ((USimpleReferenceExpression) selector).getIdentifier(); + } + } else if (left instanceof USimpleReferenceExpression) { + variable = ((USimpleReferenceExpression) left).getIdentifier(); + } + if (variable != null) { + mVariables.add(variable); } - mVariables.add(variable); } return super.visitBinaryExpression(node); diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/JavaScriptInterfaceDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/JavaScriptInterfaceDetector.java index 9e4a4079d8e..b55cefe508b 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/JavaScriptInterfaceDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/JavaScriptInterfaceDetector.java @@ -14,49 +14,35 @@ * 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.ResolvedAnnotation; -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.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.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 com.google.common.collect.Maps; import java.util.Collections; import java.util.List; import java.util.Map; -import lombok.ast.AstVisitor; -import lombok.ast.BinaryExpression; -import lombok.ast.BinaryOperator; -import lombok.ast.Cast; -import lombok.ast.ConstructorInvocation; -import lombok.ast.Expression; -import lombok.ast.ForwardingAstVisitor; -import lombok.ast.InlineIfExpression; -import lombok.ast.MethodInvocation; -import lombok.ast.Node; -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; /** * Looks for addJavascriptInterface calls on interfaces have been properly annotated * with {@code @JavaScriptInterface} */ -public class JavaScriptInterfaceDetector extends Detector implements Detector.JavaScanner { +public class JavaScriptInterfaceDetector extends Detector implements UastScanner { /** The main issue discovered by this detector */ public static final Issue ISSUE = Issue.create( "JavascriptInterface", //$NON-NLS-1$ @@ -88,34 +74,31 @@ public class JavaScriptInterfaceDetector extends Detector implements Detector.Ja return Speed.SLOW; // because it relies on class loading referenced javascript interface } - // ---- Implements JavaScanner ---- + // ---- Implements UastScanner ---- + - @Nullable @Override - public List getApplicableMethodNames() { + public List getApplicableFunctionNames() { return Collections.singletonList(ADD_JAVASCRIPT_INTERFACE); } @Override - public void visitMethod( - @NonNull JavaContext context, - @Nullable AstVisitor visitor, - @NonNull MethodInvocation call) { - if (context.getMainProject().getTargetSdk() < 17) { + public void visitFunctionCall(UastAndroidContext context, UCallExpression node) { + if (context.getLintContext().getMainProject().getTargetSdk() < 17) { return; } - if (call.astArguments().size() != 2) { + if (node.getValueArgumentCount() != 2) { return; } - if (!isCallOnWebView(context, call)) { + if (!isCallOnWebView(context, node)) { return; } - Expression first = call.astArguments().first(); - ResolvedNode resolved = context.resolve(first); - if (resolved instanceof ResolvedVariable) { + UExpression first = node.getValueArguments().get(0); + UElement resolved = node.resolve(context); + if (resolved instanceof UVariable) { // We're passing in a variable to the addJavaScriptInterface method; // the variable may be of a more generic type than the actual // value assigned to it. For example, we may have a scenario like this: @@ -123,10 +106,10 @@ public class JavaScriptInterfaceDetector extends Detector implements Detector.Ja // addJavaScriptInterface(object, ...) // Here the type of the variable is Object, but we know that it can // contain objects of type SpecificType, so we should check that type instead. - Node method = JavaContext.findSurroundingMethod(call); + UFunction method = UastUtils.getContainingFunction(node); if (method != null) { - ConcreteTypeVisitor v = new ConcreteTypeVisitor(context, call); - method.accept(v); + ConcreteTypeVisitor v = new ConcreteTypeVisitor(context, node); + v.process(method); resolved = v.getType(); if (resolved == null) { return; @@ -134,91 +117,93 @@ public class JavaScriptInterfaceDetector extends Detector implements Detector.Ja } else { return; } - } else if (resolved instanceof ResolvedMethod) { - ResolvedMethod method = (ResolvedMethod) resolved; - if (method.isConstructor()) { - resolved = method.getContainingClass(); + } else if (resolved instanceof UFunction) { + UFunction method = (UFunction) resolved; + if (method.getKind() == UastFunctionKind.CONSTRUCTOR) { + resolved = UastUtils.getContainingClass(method); } else { - TypeDescriptor returnType = method.getReturnType(); + UType returnType = method.getReturnType(); if (returnType != null) { - resolved = returnType.getTypeClass(); + UClass resolvedClass = returnType.resolve(context); + if (resolvedClass != null) { + resolved = resolvedClass; + } } } } else { - TypeDescriptor type = context.getType(first); + UType type = first.getExpressionType(); if (type != null) { - resolved = type.getTypeClass(); + UClass resolvedClass = type.resolve(context); + if (resolvedClass != null) { + resolved = resolvedClass; + } } } - if (resolved instanceof ResolvedClass) { - ResolvedClass cls = (ResolvedClass) resolved; - if (isJavaScriptAnnotated(cls)) { + if (resolved instanceof UClass) { + UClass cls = (UClass) resolved; + if (isJavaScriptAnnotated(context, cls)) { return; } - Location location = context.getLocation(call.astName()); + Location location = UastAndroidUtils.getLocation(node.getFunctionNameElement()); String message = String.format( - "None of the methods in the added interface (%1$s) have been annotated " + - "with `@android.webkit.JavascriptInterface`; they will not " + - "be visible in API 17", cls.getSimpleName()); - context.report(ISSUE, call, location, message); + "None of the methods in the added interface (%1$s) have been annotated " + + "with `@android.webkit.JavascriptInterface`; they will not " + + "be visible in API 17", cls.getName()); + context.report(ISSUE, node, location, message); } } - private static boolean isCallOnWebView(JavaContext context, MethodInvocation call) { - ResolvedNode resolved = context.resolve(call); - if (!(resolved instanceof ResolvedMethod)) { - return false; - } - ResolvedMethod method = (ResolvedMethod) resolved; - return method.getContainingClass().matches(WEB_VIEW_CLS); + private static boolean isCallOnWebView(UastAndroidContext context, UCallExpression call) { + UFunction resolved = call.resolve(context); + return UastUtils.getContainingClassOrEmpty(resolved).matchesFqName(WEB_VIEW_CLS); } - private static boolean isJavaScriptAnnotated(ResolvedClass clz) { + private static boolean isJavaScriptAnnotated(UastAndroidContext context, UClass clz) { while (clz != null) { - for (ResolvedAnnotation annotation : clz.getAnnotations()) { - if (annotation.getType().matchesSignature(JAVASCRIPT_INTERFACE_CLS)) { + for (UAnnotation annotation : clz.getAnnotations()) { + if (JAVASCRIPT_INTERFACE_CLS.equals(annotation.getFqName())) { return true; } } - for (ResolvedMethod method : clz.getMethods(false)) { - for (ResolvedAnnotation annotation : method.getAnnotations()) { - if (annotation.getType().matchesSignature(JAVASCRIPT_INTERFACE_CLS)) { + for (UFunction method : clz.getFunctions()) { + for (UAnnotation annotation : method.getAnnotations()) { + if (JAVASCRIPT_INTERFACE_CLS.equals(annotation.getFqName())) { return true; } } } - clz = clz.getSuperClass(); + clz = clz.getSuperClass(context); } return false; } - private static class ConcreteTypeVisitor extends ForwardingAstVisitor { - private final JavaContext mContext; - private final MethodInvocation mTargetCall; + private static class ConcreteTypeVisitor extends UastVisitor { + private final UastAndroidContext mContext; + private final UCallExpression mTargetCall; private boolean mFoundCall; - private Map mTypes = Maps.newIdentityHashMap(); - private Map mVariableTypes = Maps.newHashMap(); + private Map mTypes = Maps.newIdentityHashMap(); + private Map mVariableTypes = Maps.newHashMap(); - public ConcreteTypeVisitor(JavaContext context, MethodInvocation call) { + public ConcreteTypeVisitor(UastAndroidContext context, UCallExpression call) { mContext = context; mTargetCall = call; } - public ResolvedClass getType() { - Expression first = mTargetCall.astArguments().first(); - ResolvedClass resolvedClass = mTypes.get(first); - if (resolvedClass == null) { - ResolvedNode resolved = mContext.resolve(first); - if (resolved instanceof ResolvedVariable) { + public UClass getType() { + UExpression first = mTargetCall.getValueArguments().get(0); + UClass resolvedClass = mTypes.get(first); + if (resolvedClass == null && first instanceof UResolvable) { + UElement resolved = ((UResolvable) first).resolve(mContext); + if (resolved instanceof UVariable) { resolvedClass = mVariableTypes.get(resolved); if (resolvedClass == null) { - return ((ResolvedVariable)resolved).getType().getTypeClass(); + return ((UVariable) resolved).getType().resolve(mContext); } } } @@ -226,96 +211,101 @@ public class JavaScriptInterfaceDetector extends Detector implements Detector.Ja } @Override - public boolean visitNode(Node node) { - return mFoundCall || super.visitNode(node); - } - - @Override - public void afterVisitMethodInvocation(MethodInvocation node) { - if (node == mTargetCall) { - mFoundCall = true; + public void process(@NotNull UElement element) { + if (!mFoundCall) { + super.process(element); } } @Override - public void afterVisitConstructorInvocation(@NonNull ConstructorInvocation node) { - ResolvedNode resolved = mContext.resolve(node); - if (resolved instanceof ResolvedMethod) { - ResolvedMethod method = (ResolvedMethod) resolved; - mTypes.put(node, method.getContainingClass()); - } else { - // Implicit constructor? - TypeDescriptor type = mContext.getType(node); - if (type != null) { - ResolvedClass typeClass = type.getTypeClass(); - if (typeClass != null) { - mTypes.put(node, typeClass); + public boolean visitCallExpression(@NotNull UCallExpression node) { + if (node.getKind() == UastCallKind.FUNCTION_CALL && node == mTargetCall) { + mFoundCall = true; + } else if (node.getKind() == UastCallKind.CONSTRUCTOR_CALL) { + UFunction resolved = node.resolve(mContext); + if (resolved != null) { + mTypes.put(node, UastUtils.getContainingClass(resolved)); + } else { + // Implicit constructor? + UType type = node.getExpressionType(); + if (type != null) { + UClass typeClass = type.resolve(mContext); + if (typeClass != null) { + mTypes.put(node, typeClass); + } } } } + return false; } @Override - public void afterVisitVariableReference(VariableReference node) { + public boolean visitSimpleReferenceExpression(@NotNull USimpleReferenceExpression node) { if (mTypes.get(node) == null) { - ResolvedNode resolved = mContext.resolve(node); - if (resolved instanceof ResolvedVariable) { - ResolvedClass resolvedClass = mVariableTypes.get(resolved); + UElement resolved = node.resolve(mContext); + if (resolved instanceof UVariable) { + UClass resolvedClass = mVariableTypes.get(resolved); if (resolvedClass != null) { mTypes.put(node, resolvedClass); } } } + return false; } @Override - public void afterVisitBinaryExpression(BinaryExpression node) { - if (node.astOperator() == BinaryOperator.ASSIGN) { - Expression rhs = node.astRight(); - ResolvedClass resolvedClass = mTypes.get(rhs); + public boolean visitBinaryExpression(@NotNull UBinaryExpression node) { + if (node.getOperator() == UastBinaryOperator.ASSIGN) { + UExpression rhs = node.getRightOperand(); + UClass resolvedClass = mTypes.get(rhs); if (resolvedClass != null) { - Expression lhs = node.astLeft(); + UExpression lhs = node.getLeftOperand(); mTypes.put(lhs, resolvedClass); - ResolvedNode variable = mContext.resolve(lhs); - if (variable instanceof ResolvedVariable) { - mVariableTypes.put((ResolvedVariable) variable, resolvedClass); + if (lhs instanceof UResolvable) { + UDeclaration variable = ((UResolvable) lhs).resolve(mContext); + if (variable instanceof UVariable) { + mVariableTypes.put((UVariable) variable, resolvedClass); + } } } } + return false; } @Override - public void afterVisitInlineIfExpression(InlineIfExpression node) { - ResolvedClass resolvedClass = mTypes.get(node.astIfTrue()); - if (resolvedClass == null) { - resolvedClass = mTypes.get(node.astIfFalse()); - } - if (resolvedClass != null) { - mTypes.put(node, resolvedClass); - } - } - - @Override - public void afterVisitVariableDefinitionEntry(VariableDefinitionEntry node) { - Expression initializer = node.astInitializer(); - if (initializer != null) { - ResolvedClass resolvedClass = mTypes.get(initializer); + public boolean visitIfExpression(@NotNull UIfExpression node) { + if (node.isTernary()) { + UClass resolvedClass = mTypes.get(node.getThenBranch()); + if (resolvedClass == null) { + resolvedClass = mTypes.get(node.getElseBranch()); + } if (resolvedClass != null) { mTypes.put(node, resolvedClass); - ResolvedNode variable = mContext.resolve(node); - if (variable instanceof ResolvedVariable) { - mVariableTypes.put((ResolvedVariable) variable, resolvedClass); - } } } + return false; } @Override - public void afterVisitCast(Cast node) { - ResolvedClass resolvedClass = mTypes.get(node); + public boolean visitVariable(@NotNull UVariable node) { + UExpression initializer = node.getInitializer(); + if (initializer != null) { + UClass resolvedClass = mTypes.get(initializer); + if (resolvedClass != null) { + mTypes.put(node, resolvedClass); + mVariableTypes.put(node, resolvedClass); + } + } + return false; + } + + @Override + public boolean visitBinaryExpressionWithType(@NotNull UBinaryExpressionWithType node) { + UClass resolvedClass = mTypes.get(node); if (resolvedClass != null) { mTypes.put(node, resolvedClass); } + return false; } } } diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/LabelForDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/LabelForDetector.java index acb7dec704d..33a48175429 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/LabelForDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/LabelForDetector.java @@ -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_HINT; @@ -25,20 +25,20 @@ import static com.android.SdkConstants.EDIT_TEXT; import static com.android.SdkConstants.ID_PREFIX; import static com.android.SdkConstants.MULTI_AUTO_COMPLETE_TEXT_VIEW; import static com.android.SdkConstants.NEW_ID_PREFIX; -import static com.android.tools.lint.detector.api.LintUtils.stripIdPrefix; +import static com.android.tools.klint.detector.api.LintUtils.stripIdPrefix; 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.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.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.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.Sets; import org.w3c.dom.Attr; diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/LayoutConsistencyDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/LayoutConsistencyDetector.java index f67af16d14a..af414bdfb68 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/LayoutConsistencyDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/LayoutConsistencyDetector.java @@ -14,36 +14,37 @@ * 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_URI; import static com.android.SdkConstants.ATTR_ID; -import static com.android.tools.lint.detector.api.LintUtils.stripIdPrefix; +import static com.android.tools.klint.detector.api.LintUtils.stripIdPrefix; import com.android.annotations.NonNull; import com.android.annotations.Nullable; import com.android.resources.ResourceFolderType; import com.android.resources.ResourceType; -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.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.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.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.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.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.UElement; +import org.jetbrains.uast.check.UastAndroidContext; +import org.jetbrains.uast.check.UastScanner; import org.w3c.dom.Attr; import org.w3c.dom.Document; import org.w3c.dom.Element; @@ -61,12 +62,10 @@ import java.util.List; import java.util.Map; import java.util.Set; -import lombok.ast.AstVisitor; - /** * Checks for consistency in layouts across different resource folders */ -public class LayoutConsistencyDetector extends LayoutDetector implements Detector.JavaScanner { +public class LayoutConsistencyDetector extends LayoutDetector implements UastScanner { /** Map from layout resource names to a list of files defining that resource, * and within each file the value is a map from string ids to the widget type @@ -113,7 +112,7 @@ public class LayoutConsistencyDetector extends LayoutDetector implements Detecto Severity.WARNING, new Implementation( LayoutConsistencyDetector.class, - Scope.JAVA_AND_RESOURCE_FILES)); + Scope.SOURCE_AND_RESOURCE_FILES)); /** Constructs a consistency check */ public LayoutConsistencyDetector() { @@ -436,9 +435,7 @@ public class LayoutConsistencyDetector extends LayoutDetector implements Detecto } @Override - public void visitResourceReference(@NonNull JavaContext context, @Nullable AstVisitor visitor, - @NonNull lombok.ast.Node node, @NonNull String type, @NonNull String name, - boolean isFramework) { + public void visitResourceReference(UastAndroidContext context, UElement element, String type, String name, boolean isFramework) { if (!isFramework && type.equals(ResourceType.ID.getName())) { mRelevantIds.add(name); } diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/LayoutInflationDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/LayoutInflationDetector.java index 3b4ea125f3a..55fea073cda 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/LayoutInflationDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/LayoutInflationDetector.java @@ -14,39 +14,41 @@ * 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_RESOURCE_PREFIX; -import static com.android.tools.lint.checks.ViewHolderDetector.INFLATE; +import static org.jetbrains.uast.UastLiteralUtils.*; import com.android.SdkConstants; import com.android.annotations.NonNull; -import com.android.annotations.Nullable; import com.android.annotations.VisibleForTesting; import com.android.ide.common.res2.AbstractResourceRepository; import com.android.ide.common.res2.ResourceFile; import com.android.ide.common.res2.ResourceItem; 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.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.LayoutDetector; -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.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.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.Implementation; +import com.android.tools.klint.detector.api.Issue; +import com.android.tools.klint.detector.api.JavaContext; +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.Project; +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.android.utils.Pair; import com.google.common.collect.Lists; import com.google.common.collect.Sets; +import org.jetbrains.uast.*; +import org.jetbrains.uast.check.UastAndroidUtils; +import org.jetbrains.uast.check.UastAndroidContext; +import org.jetbrains.uast.check.UastScanner; import org.kxml2.io.KXmlParser; import org.w3c.dom.Attr; import org.w3c.dom.Document; @@ -64,22 +66,15 @@ import java.util.Iterator; import java.util.List; import java.util.Set; -import lombok.ast.AstVisitor; -import lombok.ast.Expression; -import lombok.ast.MethodInvocation; -import lombok.ast.NullLiteral; -import lombok.ast.Select; -import lombok.ast.StrictListAccessor; - /** * Looks for layout inflation calls passing null as the view root */ -public class LayoutInflationDetector extends LayoutDetector implements Detector.JavaScanner { +public class LayoutInflationDetector extends LayoutDetector implements UastScanner { @SuppressWarnings("unchecked") private static final Implementation IMPLEMENTATION = new Implementation( LayoutInflationDetector.class, - Scope.JAVA_AND_RESOURCE_FILES, + Scope.SOURCE_AND_RESOURCE_FILES, Scope.JAVA_FILE_SCOPE); /** Passing in a null parent to a layout inflater */ @@ -150,55 +145,55 @@ public class LayoutInflationDetector extends LayoutDetector implements Detector. } } - // ---- Implements JavaScanner ---- + // ---- Implements UastScanner ---- - @Nullable @Override - public List getApplicableMethodNames() { - return Collections.singletonList(INFLATE); + public List getApplicableFunctionNames() { + return Collections.singletonList(ViewHolderDetector.INFLATE); } @Override - public void visitMethod(@NonNull JavaContext context, @Nullable AstVisitor visitor, - @NonNull MethodInvocation node) { - assert node.astName().astValue().equals(INFLATE); - if (node.astOperand() == null) { + public void visitFunctionCall(UastAndroidContext context, UCallExpression node) { + assert ViewHolderDetector.INFLATE.equals(node.getFunctionName()); + if (node instanceof USimpleReferenceExpression) { return; } - StrictListAccessor arguments = node.astArguments(); + List arguments = node.getValueArguments(); if (arguments.size() < 2) { return; } - Iterator iterator = arguments.iterator(); - Expression first = iterator.next(); - Expression second = iterator.next(); - if (!(second instanceof NullLiteral) || !(first instanceof Select)) { + Iterator iterator = arguments.iterator(); + UExpression first = iterator.next(); + UExpression second = iterator.next(); + if (!isNullLiteral(second) || !(first instanceof UQualifiedExpression)) { return; } - Select select = (Select) first; - Expression operand = select.astOperand(); - if (operand instanceof Select) { - Select rLayout = (Select) operand; - if (rLayout.astIdentifier().astValue().equals(ResourceType.LAYOUT.getName()) && - rLayout.astOperand().toString().endsWith(SdkConstants.R_CLASS)) { - String layoutName = select.astIdentifier().astValue(); - if (context.getScope().contains(Scope.RESOURCE_FILE)) { + UQualifiedExpression select = (UQualifiedExpression) first; + UExpression selector = select.getSelector(); + UExpression receiver = select.getReceiver(); + if (receiver instanceof UQualifiedExpression && selector instanceof USimpleReferenceExpression) { + UQualifiedExpression rLayout = (UQualifiedExpression) receiver; + if (rLayout.selectorMatches(ResourceType.LAYOUT.getName()) && + rLayout.getReceiver().renderString().endsWith(SdkConstants.R_CLASS)) { + String layoutName = ((USimpleReferenceExpression)selector).getIdentifier(); + + JavaContext lintContext = context.getLintContext(); + + if (lintContext.getScope().contains(Scope.RESOURCE_FILE)) { // We're doing a full analysis run: we can gather this information // incrementally - if (!context.getDriver().isSuppressed(context, ISSUE, node)) { + if (!lintContext.getDriver().isSuppressed(lintContext, ISSUE, node)) { if (mPendingErrors == null) { mPendingErrors = Lists.newArrayList(); } - Location location = context.getLocation(second); + Location location = UastAndroidUtils.getLocation(second); mPendingErrors.add(Pair.of(layoutName, location)); } - } else if (hasLayoutParams(context, layoutName)) { - context.report(ISSUE, node, context.getLocation(second), ERROR_MESSAGE); + } else if (hasLayoutParams(lintContext, layoutName)) { + context.report(ISSUE, node, UastAndroidUtils.getLocation(second), ERROR_MESSAGE); } } } - - super.visitMethod(context, visitor, node); } private static boolean hasLayoutParams(@NonNull JavaContext context, String name) { diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/LocaleDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/LocaleDetector.java index 59998eaadbb..6b8c6cd0a4b 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/LocaleDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/LocaleDetector.java @@ -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.FORMAT_METHOD; 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.Detector.ClassScanner; -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.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.Detector.ClassScanner; +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 org.objectweb.asm.Opcodes; import org.objectweb.asm.tree.AbstractInsnNode; diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/LocaleFolderDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/LocaleFolderDetector.java index 9581682eae5..b493627774d 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/LocaleFolderDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/LocaleFolderDetector.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.tools.lint.checks; +package com.android.tools.klint.checks; import static com.android.ide.common.resources.configuration.FolderConfiguration.QUALIFIER_SPLITTER; import static com.android.ide.common.resources.configuration.LocaleQualifier.BCP_47_PREFIX; @@ -27,17 +27,17 @@ import com.android.ide.common.resources.configuration.FolderConfiguration; import com.android.ide.common.resources.configuration.LocaleQualifier; import com.android.ide.common.resources.configuration.ResourceQualifier; 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.LintUtils; -import com.android.tools.lint.detector.api.Location; -import com.android.tools.lint.detector.api.ResourceContext; -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.LintUtils; +import com.android.tools.klint.detector.api.Location; +import com.android.tools.klint.detector.api.ResourceContext; +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.base.Joiner; import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.Lists; diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/LogDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/LogDetector.java index e8fd0e943cf..6f09a0f3bc6 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/LogDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/LogDetector.java @@ -14,43 +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.TYPE_STRING; +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.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.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.Detector; +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.Scope; +import com.android.tools.klint.detector.api.Severity; import java.util.Arrays; import java.util.Iterator; import java.util.List; -import lombok.ast.AstVisitor; -import lombok.ast.BinaryExpression; -import lombok.ast.ClassDeclaration; -import lombok.ast.Expression; -import lombok.ast.If; -import lombok.ast.MethodInvocation; -import lombok.ast.Node; -import lombok.ast.Select; -import lombok.ast.StringLiteral; -import lombok.ast.VariableReference; +import org.jetbrains.uast.*; +import org.jetbrains.uast.check.UastAndroidContext; +import org.jetbrains.uast.check.UastAndroidUtils; +import org.jetbrains.uast.check.UastScanner; /** * Detector for finding inefficiencies and errors in logging calls. */ -public class LogDetector extends Detector implements Detector.JavaScanner { +public class LogDetector extends Detector implements UastScanner { private static final Implementation IMPLEMENTATION = new Implementation( LogDetector.class, Scope.JAVA_FILE_SCOPE); @@ -106,96 +97,96 @@ public class LogDetector extends Detector implements Detector.JavaScanner { private static final String LOG_CLS = "android.util.Log"; //$NON-NLS-1$ private static final String PRINTLN = "println"; //$NON-NLS-1$ - // ---- Implements Detector.JavaScanner ---- + // ---- Implements UastScanner ---- + @Override - public List getApplicableMethodNames() { + public List getApplicableFunctionNames() { return Arrays.asList( - "d", //$NON-NLS-1$ - "e", //$NON-NLS-1$ - "i", //$NON-NLS-1$ - "v", //$NON-NLS-1$ - "w", //$NON-NLS-1$ - PRINTLN, - IS_LOGGABLE); + "d", //$NON-NLS-1$ + "e", //$NON-NLS-1$ + "i", //$NON-NLS-1$ + "v", //$NON-NLS-1$ + "w", //$NON-NLS-1$ + PRINTLN, + IS_LOGGABLE); } @Override - public void visitMethod(@NonNull JavaContext context, @Nullable AstVisitor visitor, @NonNull MethodInvocation node) { - ResolvedNode resolved = context.resolve(node); - if (!(resolved instanceof ResolvedMethod)) { + public void visitFunctionCall(UastAndroidContext context, UCallExpression node) { + UFunction method = node.resolve(context); + if (method == null) { return; } - ResolvedMethod method = (ResolvedMethod) resolved; - if (!method.getContainingClass().matches(LOG_CLS)) { + if (!UastUtils.getContainingClassOrEmpty(method).matchesFqName(LOG_CLS)) { return; } - String name = node.astName().astValue(); + String name = node.getFunctionName(); boolean withinConditional = IS_LOGGABLE.equals(name) || - checkWithinConditional(context, node.getParent(), node); + checkWithinConditional(context, node.getParent(), node); // See if it's surrounded by an if statement (and it's one of the non-error, spammy // log methods (info, verbose, etc)) if (("i".equals(name) || "d".equals(name) || "v".equals(name) || PRINTLN.equals(name)) - && !withinConditional - && performsWork(context, node) - && context.isEnabled(CONDITIONAL)) { + && !withinConditional + && performsWork(node) + && context.getLintContext().isEnabled(CONDITIONAL)) { String message = String.format("The log call Log.%1$s(...) should be " + - "conditional: surround with `if (Log.isLoggable(...))` or " + - "`if (BuildConfig.DEBUG) { ... }`", - node.astName().toString()); - context.report(CONDITIONAL, node, context.getLocation(node), message); + "conditional: surround with `if (Log.isLoggable(...))` or " + + "`if (BuildConfig.DEBUG) { ... }`", + node.getFunctionName()); + context.report(CONDITIONAL, node, UastAndroidUtils.getLocation(node), message); } // Check tag length - if (context.isEnabled(LONG_TAG)) { + if (context.getLintContext().isEnabled(LONG_TAG)) { int tagArgumentIndex = PRINTLN.equals(name) ? 1 : 0; - if (method.getArgumentCount() > tagArgumentIndex - && method.getArgumentType(tagArgumentIndex).matchesSignature(TYPE_STRING) - && node.astArguments().size() == method.getArgumentCount()) { - Iterator iterator = node.astArguments().iterator(); + if (method.getValueParameterCount() > tagArgumentIndex + && method.getValueParameters().get(tagArgumentIndex).getType().matchesFqName(TYPE_STRING) + && node.getValueArgumentCount() == method.getValueParameterCount()) { + Iterator iterator = node.getValueArguments().iterator(); if (tagArgumentIndex == 1) { iterator.next(); } - Node argument = iterator.next(); - String tag = ConstantEvaluator.evaluateString(context, argument, true); + UExpression argument = iterator.next(); + String tag = argument.evaluateString(); if (tag != null && tag.length() > 23) { String message = String.format( - "The logging tag can be at most 23 characters, was %1$d (%2$s)", - tag.length(), tag); - context.report(LONG_TAG, node, context.getLocation(node), message); + "The logging tag can be at most 23 characters, was %1$d (%2$s)", + tag.length(), tag); + context.report(LONG_TAG, node, UastAndroidUtils.getLocation(node), message); } } } + } /** Returns true if the given logging call performs "work" to compute the message */ private static boolean performsWork( - @NonNull JavaContext context, - @NonNull MethodInvocation node) { - int messageArgumentIndex = PRINTLN.equals(node.astName().astValue()) ? 2 : 1; - if (node.astArguments().size() >= messageArgumentIndex) { - Iterator iterator = node.astArguments().iterator(); - Node argument = null; + @NonNull UCallExpression node) { + int messageArgumentIndex = PRINTLN.equals(node.getFunctionName()) ? 2 : 1; + if (node.getValueArgumentCount() >= messageArgumentIndex) { + Iterator iterator = node.getValueArguments().iterator(); + UExpression argument = null; for (int i = 0; i <= messageArgumentIndex; i++) { argument = iterator.next(); } if (argument == null) { return false; } - if (argument instanceof StringLiteral || argument instanceof VariableReference) { + if (UastLiteralUtils.isStringLiteral(argument) || argument instanceof USimpleReferenceExpression) { return false; } - if (argument instanceof BinaryExpression) { - String string = ConstantEvaluator.evaluateString(context, argument, false); + if (argument instanceof UBinaryExpression) { + String string = argument.evaluateString(); //noinspection VariableNotUsedInsideIf if (string != null) { // does it resolve to a constant? return false; } - } else if (argument instanceof Select) { - String string = ConstantEvaluator.evaluateString(context, argument, false); + } else if (argument instanceof UQualifiedExpression) { + String string = argument.evaluateString(); //noinspection VariableNotUsedInsideIf if (string != null) { return false; @@ -210,22 +201,22 @@ public class LogDetector extends Detector implements Detector.JavaScanner { } private static boolean checkWithinConditional( - @NonNull JavaContext context, - @Nullable Node curr, - @NonNull MethodInvocation logCall) { + @NonNull UastAndroidContext context, + @Nullable UElement curr, + @NonNull UCallExpression logCall) { while (curr != null) { - if (curr instanceof If) { - If ifNode = (If) curr; - if (ifNode.astCondition() instanceof MethodInvocation) { - MethodInvocation call = (MethodInvocation) ifNode.astCondition(); - if (IS_LOGGABLE.equals(call.astName().astValue())) { + if (curr instanceof UIfExpression) { + UIfExpression ifNode = (UIfExpression) curr; + if (ifNode.getCondition() instanceof UCallExpression) { + UCallExpression call = (UCallExpression) ifNode.getCondition(); + if (IS_LOGGABLE.equals(call.getFunctionName())) { checkTagConsistent(context, logCall, call); } } return true; - } else if (curr instanceof MethodInvocation - || curr instanceof ClassDeclaration) { // static block + } else if (curr instanceof UCallExpression + || curr instanceof UClass) { // static block break; } curr = curr.getParent(); @@ -234,18 +225,21 @@ public class LogDetector extends Detector implements Detector.JavaScanner { } /** Checks that the tag passed to Log.s and Log.isLoggable match */ - private static void checkTagConsistent(JavaContext context, MethodInvocation logCall, - MethodInvocation call) { - Iterator isLogIterator = call.astArguments().iterator(); - Iterator logIterator = logCall.astArguments().iterator(); + private static void checkTagConsistent(UastAndroidContext context, UCallExpression logCall, + UCallExpression call) { + Iterator isLogIterator = call.getValueArguments().iterator(); + Iterator logIterator = logCall.getValueArguments().iterator(); if (!isLogIterator.hasNext() || !logIterator.hasNext()) { return; } - Expression isLoggableTag = isLogIterator.next(); - Expression logTag = logIterator.next(); + UExpression isLoggableTag = isLogIterator.next(); + UExpression logTag = logIterator.next(); + + String logCallName = logCall.getFunctionName(); + if (logCallName == null) { + return; + } - //String callName = logCall.astName().astValue(); - String logCallName = logCall.astName().astValue(); boolean isPrintln = PRINTLN.equals(logCallName); if (isPrintln) { if (!logIterator.hasNext()) { @@ -254,28 +248,33 @@ public class LogDetector extends Detector implements Detector.JavaScanner { logTag = logIterator.next(); } + JavaContext lintContext = context.getLintContext(); if (logTag != null) { - if (!isLoggableTag.toString().equals(logTag.toString())) { - ResolvedNode resolved1 = context.resolve(isLoggableTag); - ResolvedNode resolved2 = context.resolve(logTag); + if (!isLoggableTag.toString().equals(logTag.toString()) && + isLoggableTag instanceof UResolvable && + logTag instanceof UResolvable) { + UDeclaration resolved1 = ((UResolvable) isLoggableTag).resolve(context); + UDeclaration resolved2 = ((UResolvable) logTag).resolve(context); if ((resolved1 == null || resolved2 == null || !resolved1.equals(resolved2)) - && context.isEnabled(WRONG_TAG)) { - Location location = context.getLocation(logTag); - Location alternate = context.getLocation(isLoggableTag); - alternate.setMessage("Conflicting tag"); - location.setSecondary(alternate); - String isLoggableDescription = resolved1 != null ? resolved1 - .getName() - : isLoggableTag.toString(); - String logCallDescription = resolved2 != null ? resolved2.getName() - : logTag.toString(); - String message = String.format( - "Mismatched tags: the `%1$s()` and `isLoggable()` calls typically " + - "should pass the same tag: `%2$s` versus `%3$s`", - logCallName, - isLoggableDescription, - logCallDescription); - context.report(WRONG_TAG, call, location, message); + && lintContext.isEnabled(WRONG_TAG)) { + Location location = UastAndroidUtils.getLocation(logTag); + Location alternate = UastAndroidUtils.getLocation(isLoggableTag); + if (location != null && alternate != null) { + alternate.setMessage("Conflicting tag"); + location.setSecondary(alternate); + String isLoggableDescription = resolved1 != null ? resolved1 + .getName() + : isLoggableTag.toString(); + String logCallDescription = resolved2 != null ? resolved2.getName() + : logTag.toString(); + String message = String.format( + "Mismatched tags: the `%1$s()` and `isLoggable()` calls typically " + + "should pass the same tag: `%2$s` versus `%3$s`", + logCallName, + isLoggableDescription, + logCallDescription); + context.report(WRONG_TAG, call, location, message); + } } } } @@ -286,19 +285,19 @@ public class LogDetector extends Detector implements Detector.JavaScanner { if (logCallName.length() != 1 || !isLogIterator.hasNext()) { // e.g. println return; } - Expression isLoggableLevel = isLogIterator.next(); + UExpression isLoggableLevel = isLogIterator.next(); if (isLoggableLevel == null) { return; } String levelString = isLoggableLevel.toString(); - if (isLoggableLevel instanceof Select) { - levelString = ((Select)isLoggableLevel).astIdentifier().astValue(); + if (isLoggableLevel instanceof UQualifiedExpression) { + levelString = ((UQualifiedExpression)isLoggableLevel).getSelector().renderString(); } if (levelString.isEmpty()) { return; } char levelChar = Character.toLowerCase(levelString.charAt(0)); - if (logCallName.charAt(0) == levelChar || !context.isEnabled(WRONG_TAG)) { + if (logCallName.charAt(0) == levelChar || !lintContext.isEnabled(WRONG_TAG)) { return; } switch (levelChar) { @@ -318,10 +317,12 @@ public class LogDetector extends Detector implements Detector.JavaScanner { "Mismatched logging levels: when checking `isLoggable` level `%1$s`, the " + "corresponding log call should be `Log.%2$s`, not `Log.%3$s`", levelString, expectedCall, logCallName); - Location location = context.getLocation(logCall.astName()); - Location alternate = context.getLocation(isLoggableLevel); - alternate.setMessage("Conflicting tag"); - location.setSecondary(alternate); - context.report(WRONG_TAG, call, location, message); + Location location = UastAndroidUtils.getLocation(logCall.getFunctionNameElement()); + Location alternate = UastAndroidUtils.getLocation(isLoggableLevel); + if (location != null && alternate != null) { + alternate.setMessage("Conflicting tag"); + location.setSecondary(alternate); + context.report(WRONG_TAG, call, location, message); + } } } diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ManifestDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ManifestDetector.java index 2e2ac6eeb22..920c55f298a 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ManifestDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ManifestDetector.java @@ -14,14 +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.builder.model.*; import com.android.ide.common.res2.AbstractResourceRepository; import com.android.ide.common.resources.ResourceUrl; -import com.android.tools.lint.detector.api.*; +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.Project; +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.Maps; import org.w3c.dom.Attr; import org.w3c.dom.Element; @@ -38,8 +49,10 @@ import static com.android.xml.AndroidManifest.*; * Checks for issues in AndroidManifest files such as declaring elements in the * wrong order. */ -public class ManifestDetector extends Detector implements Detector.XmlScanner { - private static final Implementation IMPLEMENTATION = new Implementation( +public class ManifestDetector extends Detector + implements Detector.XmlScanner { + private static final Implementation + IMPLEMENTATION = new Implementation( ManifestDetector.class, Scope.MANIFEST_SCOPE ); diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ManifestTypoDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ManifestTypoDetector.java index 2d16e981df1..529b7d2b865 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ManifestTypoDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ManifestTypoDetector.java @@ -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.xml.AndroidManifest.NODE_ACTION; @@ -45,16 +45,16 @@ import static com.android.xml.AndroidManifest.NODE_USES_PERMISSION; import static com.android.xml.AndroidManifest.NODE_USES_SDK; 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.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.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.XmlContext; import com.google.common.collect.Lists; import com.google.common.collect.Sets; diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/MathDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/MathDetector.java index e9bb3a5bc7e..e9c26a4f9d7 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/MathDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/MathDetector.java @@ -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.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.tree.ClassNode; import org.objectweb.asm.tree.MethodInsnNode; diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/MergeRootFrameLayoutDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/MergeRootFrameLayoutDetector.java index ffc617beaf0..f5e3f02e2c8 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/MergeRootFrameLayoutDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/MergeRootFrameLayoutDetector.java @@ -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_BACKGROUND; @@ -28,23 +28,25 @@ import static com.android.SdkConstants.R_LAYOUT_RESOURCE_PREFIX; 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.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.LayoutDetector; -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.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.Location.Handle; +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.android.utils.Pair; +import org.jetbrains.uast.UExpression; +import org.jetbrains.uast.UCallExpression; +import org.jetbrains.uast.UQualifiedExpression; +import org.jetbrains.uast.check.UastAndroidContext; +import org.jetbrains.uast.check.UastScanner; import org.w3c.dom.Element; import org.w3c.dom.Node; @@ -58,16 +60,10 @@ import java.util.HashSet; import java.util.List; import java.util.Set; -import lombok.ast.AstVisitor; -import lombok.ast.Expression; -import lombok.ast.MethodInvocation; -import lombok.ast.Select; -import lombok.ast.StrictListAccessor; - /** * Checks whether a root FrameLayout can be replaced with a {@code } tag. */ -public class MergeRootFrameLayoutDetector extends LayoutDetector implements Detector.JavaScanner { +public class MergeRootFrameLayoutDetector extends LayoutDetector implements UastScanner { /** * Set of layouts that we want to enable the warning for. We only warn for * {@code }'s that are the root of a layout included from @@ -98,7 +94,7 @@ public class MergeRootFrameLayoutDetector extends LayoutDetector implements Dete Severity.WARNING, new Implementation( MergeRootFrameLayoutDetector.class, - EnumSet.of(Scope.ALL_RESOURCE_FILES, Scope.JAVA_FILE))) + EnumSet.of(Scope.ALL_RESOURCE_FILES, Scope.SOURCE_FILE))) .addMoreInfo( "http://android-developers.blogspot.com/2009/03/android-layout-tricks-3-optimize-by.html"); //$NON-NLS-1$ @@ -191,26 +187,26 @@ public class MergeRootFrameLayoutDetector extends LayoutDetector implements Dete mWhitelistedLayouts.add(layout); } - // Implements JavaScanner + // Implements UastScanner + @Override - public List getApplicableMethodNames() { + public List getApplicableFunctionNames() { return Collections.singletonList("setContentView"); //$NON-NLS-1$ } @Override - public void visitMethod( - @NonNull JavaContext context, - @Nullable AstVisitor visitor, - @NonNull MethodInvocation node) { - StrictListAccessor argumentList = node.astArguments(); - if (argumentList != null && argumentList.size() == 1) { - Expression argument = argumentList.first(); - if (argument instanceof Select) { - String expression = argument.toString(); - if (expression.startsWith(R_LAYOUT_RESOURCE_PREFIX)) { - whiteListLayout(expression.substring(R_LAYOUT_RESOURCE_PREFIX.length())); - } + public void visitFunctionCall(UastAndroidContext context, UCallExpression node) { + if (node.getValueArgumentCount() != 1) { + return; + } + + List argumentList = node.getValueArguments(); + UExpression argument = argumentList.get(0); + if (argument instanceof UQualifiedExpression) { + String expression = argument.renderString(); + if (expression.startsWith(R_LAYOUT_RESOURCE_PREFIX)) { + whiteListLayout(expression.substring(R_LAYOUT_RESOURCE_PREFIX.length())); } } } diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/MissingClassDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/MissingClassDetector.java index 0ad7a7eb9ba..ea9c1f7d2c7 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/MissingClassDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/MissingClassDetector.java @@ -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_URI; @@ -38,21 +38,21 @@ import static com.android.resources.ResourceFolderType.XML; 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.ClassContext; -import com.android.tools.lint.detector.api.Context; -import com.android.tools.lint.detector.api.Detector.ClassScanner; -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.Location.Handle; -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.ClassContext; +import com.android.tools.klint.detector.api.Context; +import com.android.tools.klint.detector.api.Detector.ClassScanner; +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.Location.Handle; +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.SdkUtils; import com.google.common.collect.Maps; import com.google.common.collect.Sets; diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/MissingIdDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/MissingIdDetector.java index 6970e86da15..6d4ed31e113 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/MissingIdDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/MissingIdDetector.java @@ -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; @@ -22,14 +22,14 @@ import static com.android.SdkConstants.ATTR_TAG; import static com.android.SdkConstants.VIEW_FRAGMENT; 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.Element; diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/NamespaceDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/NamespaceDetector.java index 78690faa851..1c90ac16b15 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/NamespaceDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/NamespaceDetector.java @@ -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.AUTO_URI; @@ -22,16 +22,16 @@ import static com.android.SdkConstants.URI_PREFIX; import static com.android.SdkConstants.XMLNS_PREFIX; 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.Project; -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.Project; +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.Document; diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/NegativeMarginDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/NegativeMarginDetector.java index 52f0ed05f42..fcc5e8d94bc 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/NegativeMarginDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/NegativeMarginDetector.java @@ -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_LAYOUT_MARGIN; import static com.android.SdkConstants.ATTR_LAYOUT_MARGIN_BOTTOM; @@ -38,17 +38,17 @@ import com.android.ide.common.res2.ResourceFile; import com.android.ide.common.res2.ResourceItem; import com.android.ide.common.resources.ResourceUrl; import com.android.resources.ResourceFolderType; -import com.android.tools.lint.client.api.LintClient; -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.Project; -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.client.api.LintClient; +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.Project; +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; diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/NestedScrollingWidgetDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/NestedScrollingWidgetDetector.java index a664483ef29..1bf9a966c77 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/NestedScrollingWidgetDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/NestedScrollingWidgetDetector.java @@ -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.GALLERY; import static com.android.SdkConstants.GRID_VIEW; @@ -23,15 +23,15 @@ import static com.android.SdkConstants.LIST_VIEW; 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.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.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.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; diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/NfcTechListDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/NfcTechListDetector.java index 28a98c81fab..beb01fb8140 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/NfcTechListDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/NfcTechListDetector.java @@ -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.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.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.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.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; @@ -40,7 +40,7 @@ import java.util.Collections; * Check which makes sure NFC tech lists do not include spaces around {@code } values * since that's not handled correctly by the inflater */ -public class NfcTechListDetector extends ResourceXmlDetector implements JavaScanner { +public class NfcTechListDetector extends ResourceXmlDetector implements UastScanner { /** The main issue discovered by this detector */ public static final Issue ISSUE = Issue.create( "NfcTechWhitespace", //$NON-NLS-1$ diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/NonInternationalizedSmsDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/NonInternationalizedSmsDetector.java index 0ad022b59da..4f613d675a2 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/NonInternationalizedSmsDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/NonInternationalizedSmsDetector.java @@ -14,31 +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.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.ArrayList; import java.util.List; -import lombok.ast.AstVisitor; -import lombok.ast.Expression; -import lombok.ast.MethodInvocation; -import lombok.ast.StrictListAccessor; -import lombok.ast.StringLiteral; +import org.jetbrains.uast.UExpression; +import org.jetbrains.uast.UCallExpression; +import org.jetbrains.uast.ULiteralExpression; +import org.jetbrains.uast.USimpleReferenceExpression; +import org.jetbrains.uast.check.UastAndroidUtils; +import org.jetbrains.uast.check.UastAndroidContext; +import org.jetbrains.uast.check.UastScanner; /** Detector looking for text messages sent to an unlocalized phone number. */ -public class NonInternationalizedSmsDetector extends Detector implements Detector.JavaScanner { +public class NonInternationalizedSmsDetector extends Detector implements UastScanner { /** The main issue discovered by this detector */ public static final Issue ISSUE = Issue.create( "UnlocalizedSms", //$NON-NLS-1$ @@ -66,37 +66,39 @@ public class NonInternationalizedSmsDetector extends Detector implements Detecto } - // ---- Implements JavaScanner ---- + // ---- Implements UastScanner ---- @Override - public List getApplicableMethodNames() { - List methodNames = new ArrayList(2); - methodNames.add("sendTextMessage"); //$NON-NLS-1$ - methodNames.add("sendMultipartTextMessage"); //$NON-NLS-1$ - return methodNames; + public List getApplicableFunctionNames() { + List methodNames = new ArrayList(2); + methodNames.add("sendTextMessage"); //$NON-NLS-1$ + methodNames.add("sendMultipartTextMessage"); //$NON-NLS-1$ + return methodNames; } @Override - public void visitMethod(@NonNull JavaContext context, @Nullable AstVisitor visitor, - @NonNull MethodInvocation node) { - assert node.astName().astValue().equals("sendTextMessage") || //$NON-NLS-1$ - node.astName().astValue().equals("sendMultipartTextMessage"); //$NON-NLS-1$ - if (node.astOperand() == null) { + public void visitFunctionCall(UastAndroidContext context, UCallExpression node) { + String functionName = node.getFunctionName(); + + assert "sendTextMessage".equals(functionName) || //$NON-NLS-1$ + "sendMultipartTextMessage".equals(functionName); //$NON-NLS-1$ + if (node instanceof USimpleReferenceExpression) { // "sendTextMessage"/"sendMultipartTextMessage" in the code with no operand return; } - StrictListAccessor args = node.astArguments(); + List args = node.getValueArguments(); if (args.size() == 5) { - Expression destinationAddress = args.first(); - if (destinationAddress instanceof StringLiteral) { - String number = ((StringLiteral) destinationAddress).astValue(); + UExpression destinationAddress = args.get(0); + if (destinationAddress instanceof ULiteralExpression + && ((ULiteralExpression)destinationAddress).isString()) { + String number = (String) ((ULiteralExpression) destinationAddress).getValue(); - if (!number.startsWith("+")) { //$NON-NLS-1$ - context.report(ISSUE, node, context.getLocation(destinationAddress), - "To make sure the SMS can be sent by all users, please start the SMS number " + - "with a + and a country code or restrict the code invocation to people in the country " + - "you are targeting."); + if (number != null && !number.startsWith("+")) { //$NON-NLS-1$ + context.report(ISSUE, node, UastAndroidUtils.getLocation(destinationAddress), + "To make sure the SMS can be sent by all users, please start the SMS number " + + "with a + and a country code or restrict the code invocation to people in the country " + + "you are targeting."); } } } diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ObsoleteLayoutParamsDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ObsoleteLayoutParamsDetector.java index 913fb3de572..630d191bf00 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ObsoleteLayoutParamsDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ObsoleteLayoutParamsDetector.java @@ -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; @@ -72,18 +72,18 @@ import static com.android.SdkConstants.VIEW_MERGE; import static com.android.SdkConstants.VIEW_TAG; import com.android.annotations.NonNull; -import com.android.tools.lint.client.api.SdkInfo; -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.Location; -import com.android.tools.lint.detector.api.Location.Handle; -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.client.api.SdkInfo; +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.Location; +import com.android.tools.klint.detector.api.Location.Handle; +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.android.utils.Pair; import org.w3c.dom.Attr; diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/OnClickDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/OnClickDetector.java index f633c6cf531..45664161dfc 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/OnClickDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/OnClickDetector.java @@ -14,27 +14,27 @@ * limitations under the License. */ -package com.android.tools.lint.checks; +package com.android.tools.klint.checks; import static com.android.SdkConstants.ATTR_ON_CLICK; import static com.android.SdkConstants.PREFIX_RESOURCE_REF; import com.android.annotations.NonNull; -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.Detector.ClassScanner; -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.Location.Handle; -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.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.ClassScanner; +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.Location.Handle; +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.base.Joiner; import org.objectweb.asm.Opcodes; diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/OverdrawDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/OverdrawDetector.java index 0603d875a05..a7ad37ab5a3 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/OverdrawDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/OverdrawDetector.java @@ -14,48 +14,33 @@ * 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_BACKGROUND; -import static com.android.SdkConstants.ATTR_CONTEXT; -import static com.android.SdkConstants.ATTR_NAME; -import static com.android.SdkConstants.ATTR_PARENT; -import static com.android.SdkConstants.ATTR_THEME; -import static com.android.SdkConstants.ATTR_TILE_MODE; -import static com.android.SdkConstants.DOT_JAVA; -import static com.android.SdkConstants.DOT_XML; -import static com.android.SdkConstants.DRAWABLE_PREFIX; -import static com.android.SdkConstants.NULL_RESOURCE; -import static com.android.SdkConstants.STYLE_RESOURCE_PREFIX; -import static com.android.SdkConstants.TAG_ACTIVITY; -import static com.android.SdkConstants.TAG_APPLICATION; -import static com.android.SdkConstants.TAG_BITMAP; -import static com.android.SdkConstants.TAG_STYLE; -import static com.android.SdkConstants.TOOLS_URI; -import static com.android.SdkConstants.TRANSPARENT_COLOR; -import static com.android.SdkConstants.VALUE_DISABLED; -import static com.android.tools.lint.detector.api.LintUtils.endsWith; +import static com.android.SdkConstants.*; +import static com.android.tools.klint.detector.api.LintUtils.endsWith; import static com.android.utils.SdkUtils.getResourceFieldName; 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; -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.LayoutDetector; -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.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.Project; +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.android.utils.Pair; +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.Attr; import org.w3c.dom.Element; import org.w3c.dom.Node; @@ -73,22 +58,11 @@ import java.util.List; import java.util.Map; import java.util.Set; -import lombok.ast.AstVisitor; -import lombok.ast.ClassDeclaration; -import lombok.ast.CompilationUnit; -import lombok.ast.Expression; -import lombok.ast.ForwardingAstVisitor; -import lombok.ast.MethodInvocation; -import lombok.ast.PackageDeclaration; -import lombok.ast.Select; -import lombok.ast.StrictListAccessor; -import lombok.ast.VariableReference; - /** * Check which looks for overdraw problems where view areas are painted and then * painted over, meaning that the bottom paint operation is a waste of time. */ -public class OverdrawDetector extends LayoutDetector implements Detector.JavaScanner { +public class OverdrawDetector extends LayoutDetector implements UastScanner { private static final String R_STYLE_PREFIX = "R.style."; //$NON-NLS-1$ private static final String SET_THEME = "setTheme"; //$NON-NLS-1$ @@ -122,7 +96,7 @@ public class OverdrawDetector extends LayoutDetector implements Detector.JavaSca Severity.WARNING, new Implementation( OverdrawDetector.class, - EnumSet.of(Scope.MANIFEST, Scope.JAVA_FILE, Scope.ALL_RESOURCE_FILES))); + EnumSet.of(Scope.MANIFEST, Scope.SOURCE_FILE, Scope.ALL_RESOURCE_FILES))); /** Mapping from FQN activity names to theme names registered in the manifest */ private Map mActivityToTheme; @@ -493,45 +467,32 @@ public class OverdrawDetector extends LayoutDetector implements Detector.JavaSca list.add(classFqn); } - // ---- Implements JavaScanner ---- + // ---- Implements UastScanner ---- @Override - public List> getApplicableNodeTypes() { - // This detector does not specify specific node types; this means - // that the infrastructure will run the full visitor on the compilation - // unit rather than on individual nodes. This is important since this - // detector relies on pruning (if it gets to a class declaration that is - // not an activity, it skips everything inside). - return null; - } - - @Override - public AstVisitor createJavaVisitor(@NonNull JavaContext context) { - if (!context.getProject().getReportIssues()) { + public UastVisitor createUastVisitor(@NonNull UastAndroidContext context) { + if (!context.getLintContext().getProject().getReportIssues()) { return null; } return new OverdrawVisitor(); } - private class OverdrawVisitor extends ForwardingAstVisitor { + private class OverdrawVisitor extends UastVisitor { private static final String ACTIVITY = "Activity"; //$NON-NLS-1$ private String mClassFqn; @Override - public boolean visitClassDeclaration(ClassDeclaration node) { - String name = node.astName().astValue(); + public boolean visitClass(@NotNull UClass node) { + String name = node.getName(); if (mActivities != null && mActivities.contains(mClassFqn) || name.endsWith(ACTIVITY) - || node.astExtending() != null && - node.astExtending().getTypeName().endsWith(ACTIVITY)) { + || node.isSubclassOf(CLASS_ACTIVITY)) { String packageName = ""; - if (node.getParent() instanceof CompilationUnit) { - CompilationUnit compilationUnit = (CompilationUnit) node.getParent(); - PackageDeclaration packageDeclaration = compilationUnit.astPackageDeclaration(); - if (packageDeclaration == null) { - // No package declaration: ignore this one + if (node.getParent() instanceof UFile) { + UFile file = (UFile) node.getParent(); + if (file.getPackageFqName() == null) { return true; } - packageName = packageDeclaration.getPackageName(); + packageName = file.getPackageFqName(); } mClassFqn = (!packageName.isEmpty() ? (packageName + '.') : "") + name; @@ -544,13 +505,13 @@ public class OverdrawDetector extends LayoutDetector implements Detector.JavaSca // Store R.layout references in activity classes in a map mapping back layouts // to activities @Override - public boolean visitSelect(Select node) { - if (node.astIdentifier().astValue().equals("layout") //$NON-NLS-1$ - && node.astOperand() instanceof VariableReference - && ((VariableReference) node.astOperand()).astIdentifier().astValue() - .equals("R") //$NON-NLS-1$ - && node.getParent() instanceof Select) { - String layout = ((Select) node.getParent()).astIdentifier().astValue(); + public boolean visitQualifiedExpression(@NotNull UQualifiedExpression node) { + if (node.getSelector().renderString().equals("layout") //$NON-NLS-1$ + && node.getReceiver() instanceof USimpleReferenceExpression + && ((USimpleReferenceExpression) node.getReceiver()).getIdentifier() + .equals("R") //$NON-NLS-1$ + && node.getParent() instanceof UQualifiedExpression) { + String layout = ((UQualifiedExpression) node.getParent()).getSelector().renderString(); registerLayoutActivity(layout, mClassFqn); } @@ -560,20 +521,22 @@ public class OverdrawDetector extends LayoutDetector implements Detector.JavaSca // Look for setTheme(R.style.whatever) and register as a theme registration // for the current activity + + @Override - public boolean visitMethodInvocation(MethodInvocation node) { - if (node.astName().astValue().equals(SET_THEME)) { + public boolean visitCallExpression(@NotNull UCallExpression node) { + if (SET_THEME.equals(node.getFunctionName())) { // Look at argument - StrictListAccessor args = node.astArguments(); + List args = node.getValueArguments(); if (args.size() == 1) { - Expression arg = args.first(); - if (arg instanceof Select) { - String resource = arg.toString(); + UExpression arg = args.get(0); + if (arg instanceof UQualifiedExpression) { + String resource = arg.renderString(); if (resource.startsWith(R_STYLE_PREFIX)) { if (mActivityToTheme == null) { mActivityToTheme = new HashMap(); } - String name = ((Select) arg).astIdentifier().astValue(); + String name = ((UQualifiedExpression) arg).getSelector().renderString(); mActivityToTheme.put(mClassFqn, STYLE_RESOURCE_PREFIX + name); } } diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/OverrideConcreteDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/OverrideConcreteDetector.java index eb95cc554b7..ea1ac70b0d9 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/OverrideConcreteDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/OverrideConcreteDetector.java @@ -14,35 +14,28 @@ * 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.ResolvedClass; +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.annotations.NonNull; -import com.android.annotations.Nullable; -import com.android.tools.lint.client.api.JavaParser.ResolvedMethod; -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.Scope; -import com.android.tools.lint.detector.api.Severity; - -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.UastScanner; /** * Checks that subclasses of certain APIs are overriding all methods that were abstract * in one or more earlier API levels that are still targeted by the minSdkVersion * of this project. */ -public class OverrideConcreteDetector extends Detector implements JavaScanner { +public class OverrideConcreteDetector extends Detector implements UastScanner { /** Are previously-abstract methods all overridden? */ public static final Issue ISSUE = Issue.create( "OverrideAbstract", //$NON-NLS-1$ @@ -83,26 +76,24 @@ public class OverrideConcreteDetector extends Detector implements JavaScanner { public OverrideConcreteDetector() { } - // ---- Implements JavaScanner ---- + // ---- Implements UastScanner ---- + - @Nullable @Override - public List applicableSuperClasses() { + public List getApplicableSuperClasses() { return Collections.singletonList(NOTIFICATION_LISTENER_SERVICE_FQN); } @Override - public void checkClass(@NonNull JavaContext context, @Nullable ClassDeclaration node, - @NonNull Node declarationOrAnonymous, @NonNull ResolvedClass resolvedClass) { + public void visitClass(UastAndroidContext context, UClass node) { if (node == null) { return; } - int flags = node.astModifiers().getEffectiveModifierFlags(); - if ((flags & Modifier.ABSTRACT) != 0) { + if (node.hasModifier(UastModifier.ABSTRACT)) { return; } - int minSdk = Math.max(context.getProject().getMinSdk(), getTargetApi(node)); + int minSdk = Math.max(context.getLintContext().getProject().getMinSdk(), getTargetApi(node)); if (minSdk >= CONCRETE_IN) { return; } @@ -110,23 +101,28 @@ public class OverrideConcreteDetector extends Detector implements JavaScanner { String[] methodNames = {ON_NOTIFICATION_POSTED, ON_NOTIFICATION_REMOVED}; for (String methodName : methodNames) { boolean found = false; - for (ResolvedMethod method : resolvedClass.getMethods(methodName, true)) { + List allFunctions = UastUtils.getAllFunctions(node, context); + for (UFunction method : allFunctions) { + if (!method.matchesName(methodName)) { + continue; + } + // Make sure it's not the base method, but that it's been defined // in a subclass, concretely - ResolvedClass containingClass = method.getContainingClass(); - if (containingClass.matches(NOTIFICATION_LISTENER_SERVICE_FQN)) { + UClass containingClass = UastUtils.getContainingClassOrEmpty(method); + if (containingClass.matchesFqName(NOTIFICATION_LISTENER_SERVICE_FQN)) { continue; } // Make sure subclass isn't just defining another abstract definition // of the method - if ((method.getModifiers() & Modifier.ABSTRACT) != 0) { + if (method.hasModifier(UastModifier.ABSTRACT)) { continue; } // Make sure it has the exact right signature - if (method.getArgumentCount() != 1) { + if (method.getValueParameterCount() != 1) { continue; // Wrong signature } - if (!method.getArgumentType(0).matchesName(STATUS_BAR_NOTIFICATION_FQN)) { + if (!method.getValueParameters().get(0).getType().matchesFqName(STATUS_BAR_NOTIFICATION_FQN)) { continue; } @@ -136,26 +132,25 @@ public class OverrideConcreteDetector extends Detector implements JavaScanner { if (!found) { String message = String.format( - "Must override `%1$s.%2$s(%3$s)`: Method was abstract until %4$d, and your `minSdkVersion` is %5$d", - NOTIFICATION_LISTENER_SERVICE_FQN, methodName, - STATUS_BAR_NOTIFICATION_FQN, CONCRETE_IN, minSdk); - Node nameNode = node.astName(); - context.report(ISSUE, node, context.getLocation(nameNode), - message); + "Must override `%1$s.%2$s(%3$s)`: Method was abstract until %4$d, and your `minSdkVersion` is %5$d", + NOTIFICATION_LISTENER_SERVICE_FQN, methodName, + STATUS_BAR_NOTIFICATION_FQN, CONCRETE_IN, minSdk); + context.report(ISSUE, node, context.getLocation(node.getNameElement()), + message); break; } } } - private static int getTargetApi(ClassDeclaration node) { + private static int getTargetApi(UClass node) { while (node != null) { - int targetApi = ApiDetector.getTargetApi(node.astModifiers()); + int targetApi = ApiDetector.getTargetApi(node.getAnnotations()); if (targetApi != -1) { return targetApi; } - node = JavaContext.findSurroundingClass(node.getParent()); + node = UastUtils.getContainingClass(node); } return -1; diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/OverrideDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/OverrideDetector.java index 5aa3ec3d5a0..00a05dcd5d2 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/OverrideDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/OverrideDetector.java @@ -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; import static org.objectweb.asm.Opcodes.ACC_PRIVATE; @@ -23,18 +23,18 @@ import static org.objectweb.asm.Opcodes.ACC_PUBLIC; import static org.objectweb.asm.Opcodes.ACC_STATIC; import com.android.annotations.NonNull; -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.Detector; -import com.android.tools.lint.detector.api.Detector.ClassScanner; -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.Scope; -import com.android.tools.lint.detector.api.Severity; -import com.android.tools.lint.detector.api.Speed; +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.Detector.ClassScanner; +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 com.google.common.collect.Maps; import com.google.common.collect.Sets; import com.google.common.collect.Sets.SetView; diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ParcelDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ParcelDetector.java index 1fe3afbcef3..15600ddfa61 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ParcelDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ParcelDetector.java @@ -13,39 +13,29 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.tools.lint.checks; - -import static com.android.tools.lint.client.api.JavaParser.ResolvedClass; -import static com.android.tools.lint.client.api.JavaParser.ResolvedField; +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.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 org.objectweb.asm.Opcodes; - -import java.util.Collections; -import java.util.List; - -import lombok.ast.AstVisitor; -import lombok.ast.ClassDeclaration; -import lombok.ast.ForwardingAstVisitor; -import lombok.ast.Node; -import lombok.ast.TypeReference; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.uast.*; +import org.jetbrains.uast.check.UastAndroidUtils; +import org.jetbrains.uast.check.UastAndroidContext; +import org.jetbrains.uast.check.UastScanner; +import org.jetbrains.uast.visitor.UastVisitor; /** * Looks for Parcelable classes that are missing a CREATOR field */ -public class ParcelDetector extends Detector implements Detector.JavaScanner { +public class ParcelDetector extends Detector implements UastScanner { /** The main issue discovered by this detector */ public static final Issue ISSUE = Issue.create( @@ -65,7 +55,7 @@ public class ParcelDetector extends Detector implements Detector.JavaScanner { Scope.JAVA_FILE_SCOPE)) .addMoreInfo("http://developer.android.com/reference/android/os/Parcelable.html"); - /** Constructs a new {@link com.android.tools.lint.checks.ParcelDetector} check */ + /** Constructs a new {@link ParcelDetector} check */ public ParcelDetector() { } @@ -75,64 +65,50 @@ public class ParcelDetector extends Detector implements Detector.JavaScanner { return Speed.FAST; } - // ---- Implements JavaScanner ---- + // ---- Implements UastScanner ---- - @Nullable @Override - public List> getApplicableNodeTypes() { - return Collections.>singletonList(ClassDeclaration.class); - } - - @Nullable - @Override - public AstVisitor createJavaVisitor(@NonNull final JavaContext context) { + public UastVisitor createUastVisitor(UastAndroidContext context) { return new ParcelVisitor(context); } - private static class ParcelVisitor extends ForwardingAstVisitor { - private final JavaContext mContext; + private static class ParcelVisitor extends UastVisitor { + private final UastAndroidContext mContext; - public ParcelVisitor(JavaContext context) { + public ParcelVisitor(UastAndroidContext context) { mContext = context; } @Override - public boolean visitClassDeclaration(ClassDeclaration node) { + public boolean visitClass(@NotNull UClass node) { // Only applies to concrete classes - int flags = node.astModifiers().getExplicitModifierFlags(); - if ((flags & (Opcodes.ACC_INTERFACE | Opcodes.ACC_ABSTRACT)) != 0) { + if (node.isInterface() || node.hasModifier(UastModifier.ABSTRACT)) { return true; } - if (node.astImplementing() != null) - for (TypeReference reference : node.astImplementing()) { - String name = reference.astParts().last().astIdentifier().astValue(); - if (name.equals("Parcelable")) { - JavaParser.ResolvedNode resolved = mContext.resolve(node); - if (resolved instanceof ResolvedClass) { - ResolvedClass cls = (ResolvedClass) resolved; - ResolvedField field = cls.getField("CREATOR", false); - if (field == null) { - // Make doubly sure that we're really implementing - // android.os.Parcelable - JavaParser.ResolvedNode r = mContext.resolve(reference); - if (r instanceof ResolvedClass) { - ResolvedClass parcelable = (ResolvedClass) r; - if (!parcelable.isSubclassOf("android.os.Parcelable", false)) { - return true; - } - } - Location location = mContext.getLocation(node.astName()); - mContext.report(ISSUE, node, location, - "This class implements `Parcelable` but does not " - + "provide a `CREATOR` field"); + for (UType reference : node.getSuperTypes()) { + String name = reference.getName(); + if (name.equals("Parcelable")) { + UVariable field = UastUtils.findProperty(node, "CREATOR"); + if (field == null) { + // Make doubly sure that we're really implementing + // android.os.Parcelable + UClass parcelable = reference.resolve(mContext); + if (parcelable != null) { + if (!parcelable.isSubclassOf("android.os.Parcelable")) { + return true; } } - break; + Location location = UastAndroidUtils.getLocation(node.getNameElement()); + mContext.report(ISSUE, node, location, + "This class implements `Parcelable` but does not " + + "provide a `CREATOR` field"); } } + } return true; } + } } diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/PermissionHolder.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/PermissionHolder.java index 3bde65229ee..b145aa5086b 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/PermissionHolder.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/PermissionHolder.java @@ -14,11 +14,10 @@ * 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.google.common.collect.Sets; import java.util.Collections; import java.util.Set; diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/PermissionRequirement.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/PermissionRequirement.java index 19cdc0ed8bc..1025f2f5a27 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/PermissionRequirement.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/PermissionRequirement.java @@ -13,21 +13,18 @@ * 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.ATTR_VALUE; -import static com.android.tools.lint.checks.SupportAnnotationDetector.ATTR_ALL_OF; -import static com.android.tools.lint.checks.SupportAnnotationDetector.ATTR_ANY_OF; -import static com.android.tools.lint.checks.SupportAnnotationDetector.ATTR_CONDITIONAL; +import static com.android.tools.klint.checks.SupportAnnotationDetector.ATTR_ALL_OF; +import static com.android.tools.klint.checks.SupportAnnotationDetector.ATTR_ANY_OF; +import static com.android.tools.klint.checks.SupportAnnotationDetector.ATTR_CONDITIONAL; import com.android.annotations.NonNull; import com.android.annotations.Nullable; import com.android.annotations.VisibleForTesting; -import com.android.tools.lint.client.api.JavaParser; -import com.android.tools.lint.client.api.JavaParser.ResolvedAnnotation; -import com.android.tools.lint.detector.api.Context; -import com.android.tools.lint.detector.api.JavaContext; +import com.android.tools.klint.detector.api.Context; import com.google.common.collect.Lists; import com.google.common.collect.Sets; @@ -37,13 +34,13 @@ import java.util.List; import java.util.Set; import java.util.concurrent.atomic.AtomicReference; -import lombok.ast.BinaryExpression; -import lombok.ast.BinaryOperator; -import lombok.ast.Expression; -import lombok.ast.ForwardingAstVisitor; -import lombok.ast.Node; -import lombok.ast.Select; -import lombok.ast.VariableDefinitionEntry; +import com.intellij.psi.JavaPsiFacade; +import com.intellij.psi.PsiElementFactory; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.uast.*; +import org.jetbrains.uast.check.UastAndroidContext; +import org.jetbrains.uast.java.JavaConverter; +import org.jetbrains.uast.visitor.UastVisitor; /** * A permission requirement is a boolean expression of permission names that a @@ -53,7 +50,7 @@ public abstract class PermissionRequirement { public static final String ATTR_PROTECTION_LEVEL = "protectionLevel"; //$NON-NLS-1$ public static final String VALUE_DANGEROUS = "dangerous"; //$NON-NLS-1$ - private final ResolvedAnnotation annotation; + private final UAnnotation annotation; @SuppressWarnings("ConstantConditions") public static final PermissionRequirement NONE = new PermissionRequirement(null) { @@ -89,7 +86,7 @@ public abstract class PermissionRequirement { @Nullable @Override - public BinaryOperator getOperator() { + public UastBinaryOperator getOperator() { return null; } @@ -100,14 +97,14 @@ public abstract class PermissionRequirement { } }; - private PermissionRequirement(@NonNull ResolvedAnnotation annotation) { + private PermissionRequirement(@NonNull UAnnotation annotation) { this.annotation = annotation; } @NonNull public static PermissionRequirement create( - @Nullable Context context, - @NonNull ResolvedAnnotation annotation) { + @Nullable UastAndroidContext context, + @NonNull UAnnotation annotation) { String value = (String)annotation.getValue(ATTR_VALUE); if (value != null && !value.isEmpty()) { for (int i = 0, n = value.length(); i < n; i++) { @@ -126,11 +123,11 @@ public abstract class PermissionRequirement { if (v instanceof String[]) { String[] anyOf = (String[])v; if (anyOf.length > 0) { - return new Many(annotation, BinaryOperator.LOGICAL_OR, anyOf); + return new Many(annotation, UastBinaryOperator.LOGICAL_OR, anyOf); } } else if (v instanceof String) { String[] anyOf = new String[] { (String)v }; - return new Many(annotation, BinaryOperator.LOGICAL_OR, anyOf); + return new Many(annotation, UastBinaryOperator.LOGICAL_OR, anyOf); } } @@ -139,11 +136,11 @@ public abstract class PermissionRequirement { if (v instanceof String[]) { String[] allOf = (String[])v; if (allOf.length > 0) { - return new Many(annotation, BinaryOperator.LOGICAL_AND, allOf); + return new Many(annotation, UastBinaryOperator.LOGICAL_AND, allOf); } } else if (v instanceof String) { String[] allOf = new String[] { (String)v }; - return new Many(annotation, BinaryOperator.LOGICAL_AND, allOf); + return new Many(annotation, UastBinaryOperator.LOGICAL_AND, allOf); } } @@ -231,7 +228,7 @@ public abstract class PermissionRequirement { * for leaf nodes */ @Nullable - public abstract BinaryOperator getOperator(); + public abstract UastBinaryOperator getOperator(); /** * Returns nested requirements, combined via {@link #getOperator()} @@ -243,7 +240,7 @@ public abstract class PermissionRequirement { private static class Single extends PermissionRequirement { public final String name; - public Single(@NonNull ResolvedAnnotation annotation, @NonNull String name) { + public Single(@NonNull UAnnotation annotation, @NonNull String name) { super(annotation); this.name = name; } @@ -255,7 +252,7 @@ public abstract class PermissionRequirement { @Nullable @Override - public BinaryOperator getOperator() { + public UastBinaryOperator getOperator() { return null; } @@ -302,14 +299,14 @@ public abstract class PermissionRequirement { } } - protected static void appendOperator(StringBuilder sb, BinaryOperator operator) { + protected static void appendOperator(StringBuilder sb, UastBinaryOperator operator) { sb.append(' '); - if (operator == BinaryOperator.LOGICAL_AND) { + if (operator == UastBinaryOperator.LOGICAL_AND) { sb.append("and"); - } else if (operator == BinaryOperator.LOGICAL_OR) { + } else if (operator == UastBinaryOperator.LOGICAL_OR) { sb.append("or"); } else { - assert operator == BinaryOperator.BITWISE_XOR : operator; + assert operator == UastBinaryOperator.BITWISE_XOR : operator; sb.append("xor"); } sb.append(' '); @@ -319,16 +316,16 @@ public abstract class PermissionRequirement { * Require a series of permissions, all with the same operator. */ private static class Many extends PermissionRequirement { - public final BinaryOperator operator; + public final UastBinaryOperator operator; public final List permissions; public Many( - @NonNull ResolvedAnnotation annotation, - BinaryOperator operator, + @NonNull UAnnotation annotation, + UastBinaryOperator operator, String[] names) { super(annotation); - assert operator == BinaryOperator.LOGICAL_OR - || operator == BinaryOperator.LOGICAL_AND : operator; + assert operator == UastBinaryOperator.LOGICAL_OR + || operator == UastBinaryOperator.LOGICAL_AND : operator; assert names.length >= 2; this.operator = operator; this.permissions = Lists.newArrayListWithExpectedSize(names.length); @@ -358,7 +355,7 @@ public abstract class PermissionRequirement { @Override public boolean isSatisfied(@NonNull PermissionHolder available) { - if (operator == BinaryOperator.LOGICAL_AND) { + if (operator == UastBinaryOperator.LOGICAL_AND) { for (PermissionRequirement requirement : permissions) { if (!requirement.isSatisfied(available)) { return false; @@ -366,7 +363,7 @@ public abstract class PermissionRequirement { } return true; } else { - assert operator == BinaryOperator.LOGICAL_OR : operator; + assert operator == UastBinaryOperator.LOGICAL_OR : operator; for (PermissionRequirement requirement : permissions) { if (requirement.isSatisfied(available)) { return true; @@ -428,7 +425,7 @@ public abstract class PermissionRequirement { @Nullable @Override - public BinaryOperator getOperator() { + public UastBinaryOperator getOperator() { return operator; } @@ -444,13 +441,13 @@ public abstract class PermissionRequirement { * associated boolean logic, such as "B or (C and (D or E))". */ private static class Complex extends PermissionRequirement { - public final BinaryOperator operator; + public final UastBinaryOperator operator; public final PermissionRequirement left; public final PermissionRequirement right; public Complex( - @NonNull ResolvedAnnotation annotation, - BinaryOperator operator, + @NonNull UAnnotation annotation, + UastBinaryOperator operator, PermissionRequirement left, PermissionRequirement right) { super(annotation); @@ -469,7 +466,7 @@ public abstract class PermissionRequirement { StringBuilder sb = new StringBuilder(); boolean needsParentheses = left instanceof Complex && - ((Complex) left).operator != BinaryOperator.LOGICAL_AND; + ((Complex) left).operator != UastBinaryOperator.LOGICAL_AND; if (needsParentheses) { sb.append('('); } @@ -481,7 +478,7 @@ public abstract class PermissionRequirement { appendOperator(sb, operator); needsParentheses = right instanceof Complex && - ((Complex) right).operator != BinaryOperator.LOGICAL_AND; + ((Complex) right).operator != UastBinaryOperator.LOGICAL_AND; if (needsParentheses) { sb.append('('); } @@ -497,12 +494,12 @@ public abstract class PermissionRequirement { public boolean isSatisfied(@NonNull PermissionHolder available) { boolean satisfiedLeft = left.isSatisfied(available); boolean satisfiedRight = right.isSatisfied(available); - if (operator == BinaryOperator.LOGICAL_AND) { + if (operator == UastBinaryOperator.LOGICAL_AND) { return satisfiedLeft && satisfiedRight; - } else if (operator == BinaryOperator.LOGICAL_OR) { + } else if (operator == UastBinaryOperator.LOGICAL_OR) { return satisfiedLeft || satisfiedRight; } else { - assert operator == BinaryOperator.BITWISE_XOR : operator; + assert operator == UastBinaryOperator.BITWISE_XOR : operator; return satisfiedLeft ^ satisfiedRight; } } @@ -511,7 +508,7 @@ public abstract class PermissionRequirement { public String describeMissingPermissions(@NonNull PermissionHolder available) { boolean satisfiedLeft = left.isSatisfied(available); boolean satisfiedRight = right.isSatisfied(available); - if (operator == BinaryOperator.LOGICAL_AND || operator == BinaryOperator.LOGICAL_OR) { + if (operator == UastBinaryOperator.LOGICAL_AND || operator == UastBinaryOperator.LOGICAL_OR) { if (satisfiedLeft) { if (satisfiedRight) { return ""; @@ -527,7 +524,7 @@ public abstract class PermissionRequirement { return sb.toString(); } } else { - assert operator == BinaryOperator.BITWISE_XOR : operator; + assert operator == UastBinaryOperator.BITWISE_XOR : operator; return toString(); } } @@ -537,7 +534,7 @@ public abstract class PermissionRequirement { @NonNull Set missing) { boolean satisfiedLeft = left.isSatisfied(available); boolean satisfiedRight = right.isSatisfied(available); - if (operator == BinaryOperator.LOGICAL_AND || operator == BinaryOperator.LOGICAL_OR) { + if (operator == UastBinaryOperator.LOGICAL_AND || operator == UastBinaryOperator.LOGICAL_OR) { if (satisfiedLeft) { if (satisfiedRight) { return; @@ -550,7 +547,7 @@ public abstract class PermissionRequirement { right.addMissingPermissions(available, missing); } } else { - assert operator == BinaryOperator.BITWISE_XOR : operator; + assert operator == UastBinaryOperator.BITWISE_XOR : operator; left.addMissingPermissions(available, missing); right.addMissingPermissions(available, missing); } @@ -571,66 +568,56 @@ public abstract class PermissionRequirement { } @NonNull - public static PermissionRequirement parse(@NonNull ResolvedAnnotation annotation, - @Nullable Context context, @NonNull final String value) { + public static PermissionRequirement parse(@NonNull UAnnotation annotation, + @Nullable UastAndroidContext uastContext, @NonNull final String value) { // Parse an expression of the form (A op1 B op2 C) op3 (D op4 E) etc. // We'll just use the Java parser to handle this to ensure that operator // precedence etc is correct. - if (context == null) { + if (uastContext == null) { return NONE; } - JavaParser javaParser = context.getClient().getJavaParser(null); - if (javaParser == null) { - return NONE; - } - try { - JavaContext javaContext = new JavaContext(context.getDriver(), - context.getProject(), context.getMainProject(), context.file, - javaParser) { - @Nullable - @Override - public String getContents() { - return "" - + "class Test { void test() {\n" - + "boolean result=" + value - + ";\n}\n}"; - } - }; - Node node = javaParser.parseJava(javaContext); - if (node != null) { - final AtomicReference reference = new AtomicReference(); - node.accept(new ForwardingAstVisitor() { - @Override - public boolean visitVariableDefinitionEntry(VariableDefinitionEntry node) { - reference.set(node.astInitializer()); - return true; - } - }); - Expression expression = reference.get(); - if (expression != null) { - return parse(annotation, expression); - } - } + Context context = uastContext.getLintContext(); - return NONE; - } finally { - javaParser.dispose(); + PsiElementFactory factory = JavaPsiFacade.getInstance( + context.getClient().getProject()).getElementFactory(); + + UElement node = JavaConverter.INSTANCE.convertWithParent(factory.createClassFromText( + "class Test { void test() {\n" + + "boolean result = " + value + + ";\n}\n}" + , null).getContainingFile()); + + if (node != null) { + final AtomicReference reference = new AtomicReference(); + new UastVisitor() { + @Override + public boolean visitVariable(@NotNull UVariable node) { + reference.set(node.getInitializer()); + return true; + } + }.process(node); + UExpression expression = reference.get(); + if (expression != null) { + return parse(annotation, expression); + } } + + return NONE; } private static PermissionRequirement parse( - @NonNull ResolvedAnnotation annotation, - @NonNull Expression expression) { - if (expression instanceof Select) { - return new Single(annotation, expression.toString()); - } else if (expression instanceof BinaryExpression) { - BinaryExpression binaryExpression = (BinaryExpression) expression; - BinaryOperator operator = binaryExpression.astOperator(); - if (operator == BinaryOperator.LOGICAL_AND - || operator == BinaryOperator.LOGICAL_OR - || operator == BinaryOperator.BITWISE_XOR) { - PermissionRequirement left = parse(annotation, binaryExpression.astLeft()); - PermissionRequirement right = parse(annotation, binaryExpression.astRight()); + @NonNull UAnnotation annotation, + @NonNull UExpression expression) { + if (expression instanceof UQualifiedExpression) { + return new Single(annotation, expression.renderString()); + } else if (expression instanceof UBinaryExpression) { + UBinaryExpression binaryExpression = (UBinaryExpression) expression; + UastBinaryOperator operator = binaryExpression.getOperator(); + if (operator == UastBinaryOperator.LOGICAL_AND + || operator == UastBinaryOperator.LOGICAL_OR + || operator == UastBinaryOperator.BITWISE_XOR) { + PermissionRequirement left = parse(annotation, binaryExpression.getLeftOperand()); + PermissionRequirement right = parse(annotation, binaryExpression.getRightOperand()); return new Complex(annotation, operator, left, right); } } @@ -639,7 +626,7 @@ public abstract class PermissionRequirement { @Nullable @Override - public BinaryOperator getOperator() { + public UastBinaryOperator getOperator() { return operator; } diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/PluralsDatabase.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/PluralsDatabase.java index f9d1fc4b3e6..f80394c8700 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/PluralsDatabase.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/PluralsDatabase.java @@ -13,17 +13,17 @@ * 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.tools.lint.checks.PluralsDatabase.Quantity.few; -import static com.android.tools.lint.checks.PluralsDatabase.Quantity.many; -import static com.android.tools.lint.checks.PluralsDatabase.Quantity.one; -import static com.android.tools.lint.checks.PluralsDatabase.Quantity.two; -import static com.android.tools.lint.checks.PluralsDatabase.Quantity.zero; +import static com.android.tools.klint.checks.PluralsDatabase.Quantity.few; +import static com.android.tools.klint.checks.PluralsDatabase.Quantity.many; +import static com.android.tools.klint.checks.PluralsDatabase.Quantity.one; +import static com.android.tools.klint.checks.PluralsDatabase.Quantity.two; +import static com.android.tools.klint.checks.PluralsDatabase.Quantity.zero; import com.android.annotations.NonNull; import com.android.annotations.Nullable; -import com.android.tools.lint.detector.api.LintUtils; +import com.android.tools.klint.detector.api.LintUtils; import com.google.common.collect.Maps; import java.util.ArrayList; @@ -33,7 +33,7 @@ import java.util.List; import java.util.Map; /** - * Database used by the {@link com.android.tools.lint.checks.PluralsDetector} to get information + * Database used by the {@link PluralsDetector} to get information * about plural forms for a given language */ public class PluralsDatabase { diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/PluralsDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/PluralsDetector.java index 5cb6a44df26..5da9774afa6 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/PluralsDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/PluralsDetector.java @@ -14,13 +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.ide.common.resources.configuration.LocaleQualifier; import com.android.resources.ResourceFolderType; -import com.android.tools.lint.checks.PluralsDatabase.Quantity; -import com.android.tools.lint.detector.api.*; +import com.android.tools.klint.checks.PluralsDatabase.Quantity; +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.LintUtils; +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 org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; @@ -38,7 +45,8 @@ import static com.android.SdkConstants.*; * 53015: lint could report incorrect usage of Resource.getQuantityString */ public class PluralsDetector extends ResourceXmlDetector { - private static final Implementation IMPLEMENTATION = new Implementation( + private static final Implementation + IMPLEMENTATION = new Implementation( PluralsDetector.class, Scope.RESOURCE_FILE_SCOPE); diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/PreferenceActivityDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/PreferenceActivityDetector.java index b3bb0e15457..1d3bb238bc7 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/PreferenceActivityDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/PreferenceActivityDetector.java @@ -14,29 +14,30 @@ * 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_NAME; import static com.android.SdkConstants.ATTR_PACKAGE; import static com.android.SdkConstants.TAG_ACTIVITY; -import static com.android.tools.lint.client.api.JavaParser.TYPE_STRING; +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.client.api.JavaParser.ResolvedClass; -import com.android.tools.lint.client.api.JavaParser.ResolvedMethod; -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.lint.detector.api.XmlContext; +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 com.android.tools.klint.detector.api.XmlContext; +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.UastScanner; import org.w3c.dom.Element; import java.util.Collection; @@ -46,14 +47,11 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import lombok.ast.ClassDeclaration; -import lombok.ast.Node; - /** * Ensures that PreferenceActivity and its subclasses are never exported. */ public class PreferenceActivityDetector extends Detector - implements Detector.XmlScanner, Detector.JavaScanner { + implements Detector.XmlScanner, UastScanner { public static final Issue ISSUE = Issue.create( "ExportedPreferenceActivity", //$NON-NLS-1$ "PreferenceActivity should not be exported", @@ -64,7 +62,7 @@ public class PreferenceActivityDetector extends Detector Severity.WARNING, new Implementation( PreferenceActivityDetector.class, - EnumSet.of(Scope.MANIFEST, Scope.JAVA_FILE))) + EnumSet.of(Scope.MANIFEST, Scope.SOURCE_FILE))) .addMoreInfo("http://securityintelligence.com/" + "new-vulnerability-android-framework-fragment-injection"); private static final String PREFERENCE_ACTIVITY = "android.preference.PreferenceActivity"; //$NON-NLS-1$ @@ -122,45 +120,43 @@ public class PreferenceActivityDetector extends Detector return activityClassName; } - // ---- Implements JavaScanner ---- + // ---- Implements UastScanner ---- + - @Nullable @Override - public List applicableSuperClasses() { + public List getApplicableSuperClasses() { return Collections.singletonList(PREFERENCE_ACTIVITY); } @Override - public void checkClass(@NonNull JavaContext context, @Nullable ClassDeclaration node, - @NonNull Node declarationOrAnonymous, @NonNull ResolvedClass resolvedClass) { - if (!context.getProject().getReportIssues()) { + public void visitClass(UastAndroidContext context, UClass node) { + if (!context.getLintContext().getProject().getReportIssues()) { return; } - String className = resolvedClass.getName(); - if (resolvedClass.isSubclassOf(PREFERENCE_ACTIVITY, false) - && mExportedActivities.containsKey(className)) { + String className = node.getName(); + if (node.isSubclassOf(PREFERENCE_ACTIVITY) + && mExportedActivities.containsKey(className)) { // Ignore the issue if we target an API greater than 19 and the class in // question specifically overrides isValidFragment() and thus knowingly white-lists // valid fragments. - if (context.getMainProject().getTargetSdk() >= 19 - && overridesIsValidFragment(resolvedClass)) { + if (context.getLintContext().getMainProject().getTargetSdk() >= 19 + && overridesIsValidFragment(node)) { return; } String message = String.format( - "`PreferenceActivity` subclass `%1$s` should not be exported", - className); - context.report(ISSUE, mExportedActivities.get(className).resolve(), message); + "`PreferenceActivity` subclass `%1$s` should not be exported", + className); + context.report(ISSUE, node, mExportedActivities.get(className).resolve(), message); } } - private static boolean overridesIsValidFragment(ResolvedClass resolvedClass) { - Iterable resolvedMethods = resolvedClass.getMethods(IS_VALID_FRAGMENT, - false); - for (ResolvedMethod resolvedMethod : resolvedMethods) { - if (resolvedMethod.getArgumentCount() == 1 - && resolvedMethod.getArgumentType(0).getName().equals(TYPE_STRING)) { + private static boolean overridesIsValidFragment(UClass resolvedClass) { + List functions = UastUtils.findFunctions(resolvedClass, IS_VALID_FRAGMENT); + for (UFunction func : functions) { + if (func.getValueParameterCount() == 1 + && func.getValueParameters().get(0).getType().matchesFqName(TYPE_STRING)) { return true; } } diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/PrivateKeyDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/PrivateKeyDetector.java index 8265338b014..3755d3b143b 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/PrivateKeyDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/PrivateKeyDetector.java @@ -14,19 +14,19 @@ * 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.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.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.base.Charsets; import com.google.common.io.Files; diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/PrivateResourceDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/PrivateResourceDetector.java index a3e04006a98..a8ca82cd828 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/PrivateResourceDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/PrivateResourceDetector.java @@ -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; import static com.android.SdkConstants.ATTR_REF_PREFIX; @@ -32,7 +32,7 @@ import static com.android.SdkConstants.TAG_STRING_ARRAY; import static com.android.SdkConstants.TAG_STYLE; import static com.android.SdkConstants.TOOLS_URI; import static com.android.SdkConstants.VALUE_TRUE; -import static com.android.tools.lint.detector.api.LintUtils.getBaseName; +import static com.android.tools.klint.detector.api.LintUtils.getBaseName; import static com.android.utils.SdkUtils.getResourceFieldName; import com.android.annotations.NonNull; @@ -44,23 +44,25 @@ import com.android.ide.common.resources.ResourceUrl; import com.android.resources.FolderTypeRelationship; 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.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.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 org.jetbrains.uast.UElement; +import org.jetbrains.uast.check.UastAndroidContext; +import org.jetbrains.uast.check.UastScanner; import org.w3c.dom.Attr; import org.w3c.dom.Element; +import org.w3c.dom.Node; import org.w3c.dom.NodeList; import java.io.File; @@ -68,20 +70,17 @@ import java.util.Arrays; import java.util.Collection; import java.util.List; -import lombok.ast.AstVisitor; -import lombok.ast.Node; - /** * Check which looks for access of private resources. */ -public class PrivateResourceDetector extends ResourceXmlDetector implements JavaScanner { +public class PrivateResourceDetector extends ResourceXmlDetector implements UastScanner { /** Attribute for overriding a resource */ private static final String ATTR_OVERRIDE = "override"; @SuppressWarnings("unchecked") private static final Implementation IMPLEMENTATION = new Implementation( PrivateResourceDetector.class, - Scope.JAVA_AND_RESOURCE_FILES, + Scope.SOURCE_AND_RESOURCE_FILES, Scope.JAVA_FILE_SCOPE, Scope.RESOURCE_FILE_SCOPE); @@ -110,7 +109,7 @@ public class PrivateResourceDetector extends ResourceXmlDetector implements Java return Speed.FAST; } - // ---- Implements JavaScanner ---- + // ---- Implements UastScanner ---- @Override public boolean appliesToResourceRefs() { @@ -119,19 +118,20 @@ public class PrivateResourceDetector extends ResourceXmlDetector implements Java @Override public void visitResourceReference( - @NonNull JavaContext context, - @Nullable AstVisitor visitor, - @NonNull Node node, - @NonNull String type, - @NonNull String name, - boolean isFramework) { - if (context.getProject().isGradleProject() && !isFramework) { - Project project = context.getProject(); + UastAndroidContext uastContext, + UElement element, + String type, + String name, + boolean isFramework + ) { + Context context = uastContext.getLintContext(); + Project project = context.getProject(); + if (project.isGradleProject() && !isFramework) { if (project.getGradleProjectModel() != null && project.getCurrentVariant() != null) { ResourceType resourceType = ResourceType.getEnum(type); if (resourceType != null && isPrivate(context, resourceType, name)) { String message = createUsageErrorMessage(context, resourceType, name); - context.report(ISSUE, node, context.getLocation(node), message); + context.report(ISSUE, uastContext.getLocation(element), message); } } } @@ -227,8 +227,8 @@ public class PrivateResourceDetector extends ResourceXmlDetector implements Java // Look for ?attr/ and @dimen/foo etc references in the item children NodeList childNodes = item.getChildNodes(); for (int i = 0, n = childNodes.getLength(); i < n; i++) { - org.w3c.dom.Node child = childNodes.item(i); - if (child.getNodeType() == org.w3c.dom.Node.TEXT_NODE) { + Node child = childNodes.item(i); + if (child.getNodeType() == Node.TEXT_NODE) { String text = child.getNodeValue(); int index = text.indexOf(ATTR_REF_PREFIX); @@ -259,7 +259,7 @@ public class PrivateResourceDetector extends ResourceXmlDetector implements Java } @Override - public void beforeCheckFile(@NonNull Context context) { + public void beforeCheckProject(@NonNull Context context) { File file = context.file; boolean isXmlFile = LintUtils.isXmlFile(file); if (!isXmlFile && !LintUtils.isBitmapFile(file)) { @@ -288,7 +288,7 @@ public class PrivateResourceDetector extends ResourceXmlDetector implements Java } private static String createOverrideErrorMessage(@NonNull Context context, - @NonNull ResourceType type, @NonNull String name) { + @NonNull ResourceType type, @NonNull String name) { String libraryName = getLibraryName(context, type, name); return String.format("Overriding `@%1$s/%2$s` which is marked as private in %3$s. If " + "deliberate, use tools:override=\"true\", otherwise pick a " diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ProguardDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ProguardDetector.java index 5fb9aa187fd..8c46d688241 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ProguardDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ProguardDetector.java @@ -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.PROGUARD_CONFIG; import static com.android.SdkConstants.PROJECT_PROPERTIES; 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.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; diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/PropertyFileDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/PropertyFileDetector.java index 744e5a35916..3715fae4644 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/PropertyFileDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/PropertyFileDetector.java @@ -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.DOT_PROPERTIES; 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.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.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.Location; +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.google.common.base.Splitter; import java.io.File; diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/PxUsageDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/PxUsageDetector.java index 650e6e7816c..ba0b6b674f6 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/PxUsageDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/PxUsageDetector.java @@ -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_LAYOUT_HEIGHT; import static com.android.SdkConstants.ATTR_NAME; @@ -38,17 +38,17 @@ import com.android.ide.common.res2.ResourceFile; import com.android.ide.common.res2.ResourceItem; import com.android.ide.common.resources.ResourceUrl; import com.android.resources.ResourceFolderType; -import com.android.tools.lint.client.api.LintClient; -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.Project; -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.client.api.LintClient; +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.Project; +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; diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/RegistrationDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/RegistrationDetector.java index de262a02933..e1670621b1e 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/RegistrationDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/RegistrationDetector.java @@ -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_APP_ACTIVITY; import static com.android.SdkConstants.ANDROID_APP_SERVICE; @@ -28,17 +28,17 @@ import static com.android.SdkConstants.TAG_RECEIVER; import static com.android.SdkConstants.TAG_SERVICE; 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.Detector.ClassScanner; -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.ClassContext; +import com.android.tools.klint.detector.api.Detector.ClassScanner; +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 com.google.common.collect.ArrayListMultimap; import com.google.common.collect.Multimap; diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/RelativeOverlapDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/RelativeOverlapDetector.java index 1fae9e6a980..b448d606dcf 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/RelativeOverlapDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/RelativeOverlapDetector.java @@ -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; @@ -50,13 +50,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 com.google.common.base.Joiner; import com.google.common.collect.Maps; diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/RequiredAttributeDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/RequiredAttributeDetector.java index a78ca032e46..08f10995213 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/RequiredAttributeDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/RequiredAttributeDetector.java @@ -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_PREFIX; import static com.android.SdkConstants.ANDROID_STYLE_RESOURCE_PREFIX; @@ -40,26 +40,27 @@ import static com.android.SdkConstants.VIEW_INCLUDE; import static com.android.SdkConstants.VIEW_MERGE; import static com.android.resources.ResourceFolderType.LAYOUT; import static com.android.resources.ResourceFolderType.VALUES; -import static com.android.tools.lint.detector.api.LintUtils.getLayoutName; +import static com.android.tools.klint.detector.api.LintUtils.getLayoutName; import com.android.annotations.NonNull; import com.android.annotations.Nullable; import com.android.annotations.VisibleForTesting; 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.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.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.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.Maps; import com.google.common.collect.Sets; +import org.jetbrains.uast.*; +import org.jetbrains.uast.check.UastAndroidContext; +import org.jetbrains.uast.check.UastScanner; import org.w3c.dom.Element; import org.w3c.dom.Node; @@ -72,18 +73,10 @@ import java.util.List; import java.util.Map; import java.util.Set; -import lombok.ast.AstVisitor; -import lombok.ast.Expression; -import lombok.ast.MethodInvocation; -import lombok.ast.NullLiteral; -import lombok.ast.Select; -import lombok.ast.StrictListAccessor; -import lombok.ast.VariableReference; - /** * Ensures that layout width and height attributes are specified */ -public class RequiredAttributeDetector extends LayoutDetector implements Detector.JavaScanner { +public class RequiredAttributeDetector extends LayoutDetector implements UastScanner { /** The main issue discovered by this detector */ public static final Issue ISSUE = Issue.create( "RequiredSize", //$NON-NLS-1$ @@ -100,7 +93,7 @@ public class RequiredAttributeDetector extends LayoutDetector implements Detecto Severity.ERROR, new Implementation( RequiredAttributeDetector.class, - EnumSet.of(Scope.JAVA_FILE, Scope.ALL_RESOURCE_FILES))); + EnumSet.of(Scope.SOURCE_FILE, Scope.ALL_RESOURCE_FILES))); /** Map from each style name to parent style */ @Nullable private Map mStyleParents; @@ -538,38 +531,43 @@ public class RequiredAttributeDetector extends LayoutDetector implements Detecto // ---- Implements JavaScanner ---- + @Override - @Nullable - public List getApplicableMethodNames() { + public List getApplicableFunctionNames() { return Collections.singletonList("inflate"); //$NON-NLS-1$ } @Override - public void visitMethod( - @NonNull JavaContext context, - @Nullable AstVisitor visitor, - @NonNull MethodInvocation call) { + public void visitFunctionCall(UastAndroidContext context, UCallExpression node) { // Handle // View#inflate(Context context, int resource, ViewGroup root) // LayoutInflater#inflate(int resource, ViewGroup root) // LayoutInflater#inflate(int resource, ViewGroup root, boolean attachToRoot) - StrictListAccessor args = call.astArguments(); + List args = node.getValueArguments(); String layout = null; int index = 0; - for (Iterator iterator = args.iterator(); iterator.hasNext(); index++) { - Expression expression = iterator.next(); - if (expression instanceof Select) { - Select outer = (Select) expression; - Expression operand = outer.astOperand(); - if (operand instanceof Select) { - Select inner = (Select) operand; - if (inner.astOperand() instanceof VariableReference) { - VariableReference reference = (VariableReference) inner.astOperand(); - if (FN_RESOURCE_BASE.equals(reference.astIdentifier().astValue()) - // TODO: constant - && "layout".equals(inner.astIdentifier().astValue())) { - layout = LAYOUT_RESOURCE_PREFIX + outer.astIdentifier().astValue(); + for (Iterator iterator = args.iterator(); iterator.hasNext(); index++) { + UExpression expression = iterator.next(); + if (expression instanceof UQualifiedExpression) { + UQualifiedExpression outer = (UQualifiedExpression) expression; + UExpression operand = outer.getReceiver(); + if (operand instanceof UQualifiedExpression) { + UQualifiedExpression inner = (UQualifiedExpression) operand; + if (inner.getReceiver() instanceof USimpleReferenceExpression) { + USimpleReferenceExpression reference = (USimpleReferenceExpression) inner.getReceiver(); + if (FN_RESOURCE_BASE.equals(reference.getIdentifier()) + // TODO: constant + && inner.selectorMatches("layout")) { + layout = LAYOUT_RESOURCE_PREFIX + outer.getSelector().renderString(); + break; + } + } else if (inner.getReceiver() instanceof UQualifiedExpression) { + UQualifiedExpression reference = (UQualifiedExpression) inner.getReceiver(); + if (reference.selectorMatches(FN_RESOURCE_BASE) + // TODO: constant + && inner.selectorMatches("layout")) { + layout = LAYOUT_RESOURCE_PREFIX + outer.getSelector().renderString(); break; } } @@ -578,14 +576,14 @@ public class RequiredAttributeDetector extends LayoutDetector implements Detecto } if (layout == null) { - lombok.ast.Node method = StringFormatDetector.getParentMethod(call); - if (method != null) { + UFunction containingFunction = UastUtils.getContainingFunction(node); + if (containingFunction != null) { // Must track local types index = 0; - String name = StringFormatDetector.getResourceArg(method, call, index); + String name = StringFormatDetector.getResourceArg(containingFunction, node, index); if (name == null) { index = 1; - name = StringFormatDetector.getResourceArg(method, call, index); + name = StringFormatDetector.getResourceArg(containingFunction, node, index); } if (name != null) { layout = LAYOUT_RESOURCE_PREFIX + name; @@ -602,14 +600,15 @@ public class RequiredAttributeDetector extends LayoutDetector implements Detecto int viewRootPos = index + 1; if (viewRootPos < args.size()) { int i = 0; - Iterator iterator = args.iterator(); + Iterator iterator = args.iterator(); while (iterator.hasNext() && i < viewRootPos) { iterator.next(); i++; } if (iterator.hasNext()) { - Expression viewRoot = iterator.next(); - if (viewRoot instanceof NullLiteral) { + UExpression viewRoot = iterator.next(); + if (viewRoot instanceof ULiteralExpression && + ((ULiteralExpression)viewRoot).isNull()) { // Yep, this one inflates the given view with a null parent: // Tag it as such. For now just use the include data structure since // it has the same net effect diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ResourceCycleDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ResourceCycleDetector.java index 1f0ca92ce73..5e4e38e077c 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ResourceCycleDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ResourceCycleDetector.java @@ -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_URI; @@ -38,17 +38,17 @@ import com.android.annotations.NonNull; import com.android.annotations.Nullable; 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.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 com.google.common.base.Joiner; import com.google.common.base.Supplier; import com.google.common.collect.ArrayListMultimap; diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ResourcePrefixDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ResourcePrefixDetector.java index 7250d782e7b..254f050dde0 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ResourcePrefixDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ResourcePrefixDetector.java @@ -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; import static com.android.SdkConstants.TAG_DECLARE_STYLEABLE; @@ -23,20 +23,20 @@ import static com.android.SdkConstants.TAG_RESOURCES; 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.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.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.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.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.Speed; +import com.android.tools.klint.detector.api.XmlContext; import org.w3c.dom.Attr; import org.w3c.dom.Element; @@ -73,7 +73,7 @@ public class ResourcePrefixDetector extends ResourceXmlDetector implements Scope.RESOURCE_FILE_SCOPE, Scope.BINARY_RESOURCE_FILE_SCOPE)); - /** Constructs a new {@link com.android.tools.lint.checks.ResourcePrefixDetector} */ + /** Constructs a new {@link ResourcePrefixDetector} */ public ResourcePrefixDetector() { } diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/RtlDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/RtlDetector.java index bc9812987bd..0a169c82bb8 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/RtlDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/RtlDetector.java @@ -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_DRAWABLE_END; @@ -58,22 +58,26 @@ import static com.android.SdkConstants.TAG_APPLICATION; import com.android.annotations.NonNull; import com.android.annotations.Nullable; import com.android.annotations.VisibleForTesting; -import com.android.tools.lint.client.api.JavaParser; -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.LayoutDetector; -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.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.Project; +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.*; +import org.jetbrains.uast.check.UastAndroidUtils; +import org.jetbrains.uast.check.UastAndroidContext; +import org.jetbrains.uast.check.UastScanner; +import org.jetbrains.uast.visitor.EmptyUastVisitor; +import org.jetbrains.uast.visitor.UastVisitor; import org.w3c.dom.Attr; import org.w3c.dom.Element; @@ -85,25 +89,15 @@ import java.util.EnumSet; import java.util.List; import java.util.Locale; -import lombok.ast.AstVisitor; -import lombok.ast.EnumConstant; -import lombok.ast.ForwardingAstVisitor; -import lombok.ast.Identifier; -import lombok.ast.ImportDeclaration; -import lombok.ast.Node; -import lombok.ast.Select; -import lombok.ast.VariableDefinitionEntry; -import lombok.ast.VariableReference; - /** * Check which looks for RTL issues (right-to-left support) in layouts */ -public class RtlDetector extends LayoutDetector implements Detector.JavaScanner { +public class RtlDetector extends LayoutDetector implements UastScanner { @SuppressWarnings("unchecked") private static final Implementation IMPLEMENTATION = new Implementation( RtlDetector.class, - EnumSet.of(Scope.RESOURCE_FILE, Scope.JAVA_FILE, Scope.MANIFEST), + EnumSet.of(Scope.RESOURCE_FILE, Scope.SOURCE_FILE, Scope.MANIFEST), Scope.RESOURCE_FILE_SCOPE, Scope.JAVA_FILE_SCOPE, Scope.MANIFEST_SCOPE @@ -562,7 +556,7 @@ public class RtlDetector extends LayoutDetector implements Detector.JavaScanner return name.startsWith(ATTR_PADDING); } - // ---- Implements JavaScanner ---- + // ---- Implements UastScanner ---- @Override public boolean appliesTo(@NonNull Context context, @NonNull File file) { @@ -570,73 +564,67 @@ public class RtlDetector extends LayoutDetector implements Detector.JavaScanner } @Override - public List> getApplicableNodeTypes() { - return Collections.>singletonList(Identifier.class); - } - - @Override - public AstVisitor createJavaVisitor(@NonNull JavaContext context) { - if (rtlApplies(context)) { + public UastVisitor createUastVisitor(UastAndroidContext context) { + if (rtlApplies(context.getLintContext())) { return new IdentifierChecker(context); } - return new ForwardingAstVisitor() { }; + return EmptyUastVisitor.INSTANCE; } - private static class IdentifierChecker extends ForwardingAstVisitor { - private final JavaContext mContext; + private static class IdentifierChecker extends UastVisitor { + private final UastAndroidContext mContext; - public IdentifierChecker(JavaContext context) { + public IdentifierChecker(UastAndroidContext context) { mContext = context; } @Override - public boolean visitIdentifier(Identifier node) { - String identifier = node.astValue(); + public boolean visitSimpleReferenceExpression(@NotNull USimpleReferenceExpression node) { + String identifier = node.getIdentifier(); boolean isLeft = LEFT_FIELD.equals(identifier); boolean isRight = RIGHT_FIELD.equals(identifier); if (!isLeft && !isRight) { return false; } - Node parent = node.getParent(); - if (parent instanceof ImportDeclaration || parent instanceof EnumConstant - || parent instanceof VariableDefinitionEntry) { + UElement parent = node.getParent(); + if (parent instanceof UImportStatement || parent instanceof UVariable) { return false; } - JavaParser.ResolvedNode resolved = mContext.resolve(node); + UElement resolved = node.resolve(mContext); if (resolved != null) { - if (!(resolved instanceof JavaParser.ResolvedField)) { + if (!(resolved instanceof UVariable)) { return false; } else { - JavaParser.ResolvedField field = (JavaParser.ResolvedField) resolved; - if (!field.getContainingClass().matches(FQCN_GRAVITY)) { + UClass containingClass = UastUtils.getContainingClass(resolved); + if (containingClass == null || !containingClass.matchesFqName(FQCN_GRAVITY)) { return false; } } } else { // Can't resolve types (for example while editing code with errors): // rely on heuristics like import statements and class qualifiers - if (parent instanceof Select && - !(GRAVITY_CLASS.equals(((Select) parent).astOperand().toString()))) { + if (parent instanceof UQualifiedExpression && + !(GRAVITY_CLASS.equals(((UQualifiedExpression) parent).getReceiver().renderString()))) { return false; } - if (parent instanceof VariableReference) { + if (parent instanceof USimpleReferenceExpression) { // No operand: make sure it's statically imported - if (!LintUtils.isImported(mContext.getCompilationUnit(), - FQCN_GRAVITY_PREFIX + identifier)) { + if (!LintUtils.isImported(mContext.getLintContext().getCompilationUnit(), + FQCN_GRAVITY_PREFIX + identifier)) { return false; } } } String message = String.format( - "Use \"`Gravity.%1$s`\" instead of \"`Gravity.%2$s`\" to ensure correct " - + "behavior in right-to-left locales", - (isLeft ? GRAVITY_VALUE_START : GRAVITY_VALUE_END).toUpperCase(Locale.US), - (isLeft ? GRAVITY_VALUE_LEFT : GRAVITY_VALUE_RIGHT).toUpperCase(Locale.US)); - Location location = mContext.getLocation(node); + "Use \"`Gravity.%1$s`\" instead of \"`Gravity.%2$s`\" to ensure correct " + + "behavior in right-to-left locales", + (isLeft ? GRAVITY_VALUE_START : GRAVITY_VALUE_END).toUpperCase(Locale.US), + (isLeft ? GRAVITY_VALUE_LEFT : GRAVITY_VALUE_RIGHT).toUpperCase(Locale.US)); + Location location = UastAndroidUtils.getLocation(node); mContext.report(USE_START, node, location, message); return true; diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/SQLiteDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/SQLiteDetector.java index 4197a88c6f4..70dca758002 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/SQLiteDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/SQLiteDetector.java @@ -14,36 +14,29 @@ * 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.TYPE_STRING; +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.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.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.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.Iterator; import java.util.List; -import lombok.ast.AstVisitor; -import lombok.ast.Expression; -import lombok.ast.MethodInvocation; -import lombok.ast.Node; +import org.jetbrains.uast.*; +import org.jetbrains.uast.check.UastAndroidUtils; +import org.jetbrains.uast.check.UastAndroidContext; +import org.jetbrains.uast.check.UastScanner; /** * Detector which looks for problems related to SQLite usage */ -public class SQLiteDetector extends Detector implements Detector.JavaScanner { +public class SQLiteDetector extends Detector implements UastScanner { private static final Implementation IMPLEMENTATION = new Implementation( SQLiteDetector.class, Scope.JAVA_FILE_SCOPE); @@ -79,37 +72,35 @@ public class SQLiteDetector extends Detector implements Detector.JavaScanner { // ---- Implements Detector.JavaScanner ---- @Override - public List getApplicableMethodNames() { + public List getApplicableFunctionNames() { return Collections.singletonList("execSQL"); //$NON-NLS-1$ } @Override - public void visitMethod(@NonNull JavaContext context, @Nullable AstVisitor visitor, - @NonNull MethodInvocation node) { - ResolvedNode resolved = context.resolve(node); - if (!(resolved instanceof ResolvedMethod)) { + public void visitFunctionCall(UastAndroidContext context, UCallExpression node) { + UFunction resolvedFunction = node.resolve(context); + UClass containingClass = UastUtils.getContainingClass(resolvedFunction); + if (resolvedFunction == null || containingClass == null) { return; } - ResolvedMethod method = (ResolvedMethod) resolved; - if (!method.getContainingClass().matches("android.database.sqlite.SQLiteDatabase")) { + if (!containingClass.matchesFqName("android.database.sqlite.SQLiteDatabase")) { return; } // Try to resolve the String and look for STRING keys - if (method.getArgumentCount() > 0 - && method.getArgumentType(0).matchesSignature(TYPE_STRING) - && node.astArguments().size() == method.getArgumentCount()) { - Iterator iterator = node.astArguments().iterator(); - Node argument = iterator.next(); - String sql = ConstantEvaluator.evaluateString(context, argument, true); + if (resolvedFunction.getValueParameterCount() > 0 + && resolvedFunction.getValueParameters().get(0).getType().matchesFqName(TYPE_STRING) + && node.getValueArgumentCount() == resolvedFunction.getValueParameterCount()) { + UExpression argument = node.getValueArguments().get(0); + String sql = argument.evaluateString(); if (sql != null && (sql.startsWith("CREATE TABLE") || sql.startsWith("ALTER TABLE")) - && sql.matches(".*\\bSTRING\\b.*")) { + && sql.matches(".*\\bSTRING\\b.*")) { String message = "Using column type STRING; did you mean to use TEXT? " - + "(STRING is a numeric type and its value can be adjusted; for example," - + "strings that look like integers can drop leading zeroes. See issue " - + "explanation for details.)"; - context.report(ISSUE, node, context.getLocation(node), message); + + "(STRING is a numeric type and its value can be adjusted; for example," + + "strings that look like integers can drop leading zeroes. See issue " + + "explanation for details.)"; + context.report(ISSUE, node, UastAndroidUtils.getLocation(node), message); } } } diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ScrollViewChildDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ScrollViewChildDetector.java index 8b4b29e0963..4a974f79f0c 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ScrollViewChildDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ScrollViewChildDetector.java @@ -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_LAYOUT_HEIGHT; @@ -25,15 +25,15 @@ import static com.android.SdkConstants.VALUE_FILL_PARENT; import static com.android.SdkConstants.VALUE_MATCH_PARENT; 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.Attr; import org.w3c.dom.Element; diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/SdCardDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/SdCardDetector.java index 5cd803f8783..9dbe622b9da 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/SdCardDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/SdCardDetector.java @@ -14,33 +14,32 @@ * 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.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.Node; -import lombok.ast.StringLiteral; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.uast.ULiteralExpression; +import org.jetbrains.uast.check.UastAndroidUtils; +import org.jetbrains.uast.check.UastAndroidContext; +import org.jetbrains.uast.check.UastScanner; +import org.jetbrains.uast.visitor.UastVisitor; /** * Looks for hardcoded references to /sdcard/. */ -public class SdCardDetector extends Detector implements Detector.JavaScanner { +public class SdCardDetector extends Detector implements UastScanner { /** Hardcoded /sdcard/ references */ public static final Issue ISSUE = Issue.create( "SdCardPath", //$NON-NLS-1$ @@ -77,29 +76,26 @@ public class SdCardDetector extends Detector implements Detector.JavaScanner { return Speed.FAST; } - // ---- Implements JavaScanner ---- + // ---- Implements UastScanner ---- @Override - public List> getApplicableNodeTypes() { - return Collections.>singletonList(StringLiteral.class); - } - - @Override - public AstVisitor createJavaVisitor(@NonNull JavaContext context) { + public UastVisitor createUastVisitor(UastAndroidContext context) { return new StringChecker(context); } - private static class StringChecker extends ForwardingAstVisitor { - private final JavaContext mContext; + private static class StringChecker extends UastVisitor { + private final UastAndroidContext mContext; - public StringChecker(JavaContext context) { + public StringChecker(UastAndroidContext context) { mContext = context; } @Override - public boolean visitStringLiteral(StringLiteral node) { - String s = node.astValue(); - if (s.isEmpty()) { + public boolean visitLiteralExpression(@NotNull ULiteralExpression node) { + if (!node.isString()) return false; + + String s = (String) node.getValue(); + if (s == null || s.isEmpty()) { return false; } char c = s.charAt(0); @@ -108,20 +104,20 @@ public class SdCardDetector extends Detector implements Detector.JavaScanner { } if (s.startsWith("/sdcard") //$NON-NLS-1$ - || s.startsWith("/mnt/sdcard/") //$NON-NLS-1$ - || s.startsWith("/system/media/sdcard") //$NON-NLS-1$ - || s.startsWith("file://sdcard/") //$NON-NLS-1$ - || s.startsWith("file:///sdcard/")) { //$NON-NLS-1$ + || s.startsWith("/mnt/sdcard/") //$NON-NLS-1$ + || s.startsWith("/system/media/sdcard") //$NON-NLS-1$ + || s.startsWith("file://sdcard/") //$NON-NLS-1$ + || s.startsWith("file:///sdcard/")) { //$NON-NLS-1$ String message = "Do not hardcode \"/sdcard/\"; " + - "use `Environment.getExternalStorageDirectory().getPath()` instead"; - Location location = mContext.getLocation(node); + "use `Environment.getExternalStorageDirectory().getPath()` instead"; + Location location = UastAndroidUtils.getLocation(node); mContext.report(ISSUE, node, location, message); } else if (s.startsWith("/data/data/") //$NON-NLS-1$ - || s.startsWith("/data/user/")) { //$NON-NLS-1$ + || s.startsWith("/data/user/")) { //$NON-NLS-1$ String message = "Do not hardcode \"`/data/`\"; " + - "use `Context.getFilesDir().getPath()` instead"; - Location location = mContext.getLocation(node); - mContext.report(ISSUE, node, location, message); + "use `Context.getFilesDir().getPath()` instead"; + Location location = UastAndroidUtils.getLocation(node); + mContext.report(ISSUE, node, location, message); } return false; diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/SecureRandomDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/SecureRandomDetector.java index 48b1a357f4e..b706ed10e6e 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/SecureRandomDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/SecureRandomDetector.java @@ -14,19 +14,19 @@ * 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.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.Detector.ClassScanner; -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.Scope; -import com.android.tools.lint.detector.api.Severity; +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.Detector.ClassScanner; +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 org.objectweb.asm.Opcodes; import org.objectweb.asm.Type; diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/SecureRandomGeneratorDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/SecureRandomGeneratorDetector.java index fc3bd2f436a..9a5cce30f5b 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/SecureRandomGeneratorDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/SecureRandomGeneratorDetector.java @@ -14,25 +14,24 @@ * limitations under the License. */ -package com.android.tools.lint.checks; +package com.android.tools.klint.checks; import static com.android.SdkConstants.CONSTRUCTOR_NAME; -import static com.android.tools.lint.checks.SecureRandomDetector.OWNER_SECURE_RANDOM; 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.ClassContext; -import com.android.tools.lint.detector.api.Context; -import com.android.tools.lint.detector.api.Detector; -import com.android.tools.lint.detector.api.Detector.ClassScanner; -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.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.Detector.ClassScanner; +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 org.objectweb.asm.Opcodes; import org.objectweb.asm.tree.AbstractInsnNode; @@ -106,7 +105,7 @@ public class SecureRandomGeneratorDetector extends Detector implements ClassScan JAVAX_CRYPTO_KEY_GENERATOR, JAVA_SECURITY_KEY_PAIR_GENERATOR, JAVAX_CRYPTO_KEY_AGREEMENT, - OWNER_SECURE_RANDOM, + SecureRandomDetector.OWNER_SECURE_RANDOM, JAVAX_NET_SSL_SSLENGINE, JAVAX_CRYPTO_SIGNATURE, JAVAX_CRYPTO_CIPHER @@ -160,7 +159,7 @@ public class SecureRandomGeneratorDetector extends Detector implements ClassScan assert owner.equals(JAVAX_CRYPTO_KEY_GENERATOR) || owner.equals(JAVA_SECURITY_KEY_PAIR_GENERATOR) || owner.equals(JAVAX_CRYPTO_KEY_AGREEMENT) - || owner.equals(OWNER_SECURE_RANDOM) + || owner.equals(SecureRandomDetector.OWNER_SECURE_RANDOM) || owner.equals(JAVAX_CRYPTO_CIPHER) || owner.equals(JAVAX_CRYPTO_SIGNATURE) || owner.equals(JAVAX_NET_SSL_SSLENGINE) : owner; diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/SecurityDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/SecurityDetector.java index 9071724ce95..4172cad1087 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/SecurityDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/SecurityDetector.java @@ -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; @@ -37,42 +37,37 @@ import static com.android.SdkConstants.TAG_SERVICE; import static com.android.xml.AndroidManifest.NODE_ACTION; 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.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.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.XmlContext; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.uast.UCallExpression; +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 org.w3c.dom.Element; import org.w3c.dom.Node; import java.io.File; -import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; -import java.util.List; - -import lombok.ast.AstVisitor; -import lombok.ast.Expression; -import lombok.ast.ForwardingAstVisitor; -import lombok.ast.Identifier; -import lombok.ast.MethodInvocation; -import lombok.ast.StrictListAccessor; /** * Checks that exported services request a permission. */ -public class SecurityDetector extends Detector implements Detector.XmlScanner, - Detector.JavaScanner { +public class SecurityDetector extends Detector implements Detector.XmlScanner, UastScanner { private static final Implementation IMPLEMENTATION_MANIFEST = new Implementation( SecurityDetector.class, @@ -357,52 +352,44 @@ public class SecurityDetector extends Detector implements Detector.XmlScanner, } } - // ---- Implements Detector.JavaScanner ---- + // ---- Implements UastScanner ---- @Override - public List getApplicableMethodNames() { - // These are the API calls that can accept a MODE_WORLD_READABLE/MODE_WORLD_WRITABLE - // argument. - List values = new ArrayList(2); - values.add("openFileOutput"); //$NON-NLS-1$ - values.add("getSharedPreferences"); //$NON-NLS-1$ - return values; - } - - @Override - public void visitMethod(@NonNull JavaContext context, @Nullable AstVisitor visitor, - @NonNull MethodInvocation node) { - StrictListAccessor args = node.astArguments(); - for (Expression arg : args) { - arg.accept(visitor); - } - } - - @Override - public AstVisitor createJavaVisitor(@NonNull JavaContext context) { + public UastVisitor createUastVisitor(UastAndroidContext context) { return new IdentifierVisitor(context); } - private static class IdentifierVisitor extends ForwardingAstVisitor { - private final JavaContext mContext; + private static class IdentifierVisitor extends UastVisitor { + private final UastAndroidContext mContext; - public IdentifierVisitor(JavaContext context) { - super(); + private final UastVisitor identifierHandler = new UastVisitor() { + @Override + public boolean visitSimpleReferenceExpression(@NotNull USimpleReferenceExpression node) { + if ("MODE_WORLD_WRITEABLE".equals(node.getIdentifier())) { //$NON-NLS-1$ + Location location = UastAndroidUtils.getLocation(node); + mContext.report(WORLD_WRITEABLE, node, location, + "Using `MODE_WORLD_WRITEABLE` when creating files can be " + + "risky, review carefully"); + } else if ("MODE_WORLD_READABLE".equals(node.getIdentifier())) { //$NON-NLS-1$ + Location location = UastAndroidUtils.getLocation(node); + mContext.report(WORLD_READABLE, node, location, + "Using `MODE_WORLD_READABLE` when creating files can be " + + "risky, review carefully"); + } + + return false; + } + }; + + public IdentifierVisitor(UastAndroidContext context) { mContext = context; } @Override - public boolean visitIdentifier(Identifier node) { - if ("MODE_WORLD_WRITEABLE".equals(node.astValue())) { //$NON-NLS-1$ - Location location = mContext.getLocation(node); - mContext.report(WORLD_WRITEABLE, node, location, - "Using `MODE_WORLD_WRITEABLE` when creating files can be " + - "risky, review carefully"); - } else if ("MODE_WORLD_READABLE".equals(node.astValue())) { //$NON-NLS-1$ - Location location = mContext.getLocation(node); - mContext.report(WORLD_READABLE, node, location, - "Using `MODE_WORLD_READABLE` when creating files can be " + - "risky, review carefully"); + public boolean visitCallExpression(@NotNull UCallExpression node) { + String name = node.getFunctionName(); + if ("openFileOutput".equals(name) || "getSharedPreferences".equals(name)) { + identifierHandler.processChildren(node); } return false; diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ServiceCastDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ServiceCastDetector.java index f11eaf428a1..9b92117ad33 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ServiceCastDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ServiceCastDetector.java @@ -14,18 +14,17 @@ * 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.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 com.google.common.collect.Maps; import java.io.File; @@ -33,16 +32,15 @@ import java.util.Collections; import java.util.List; import java.util.Map; -import lombok.ast.AstVisitor; -import lombok.ast.Cast; -import lombok.ast.Expression; -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; /** * Detector looking for casts on th result of context.getSystemService which are suspect */ -public class ServiceCastDetector extends Detector implements Detector.JavaScanner { +public class ServiceCastDetector extends Detector implements UastScanner { /** The main issue discovered by this detector */ public static final Issue ISSUE = Issue.create( "ServiceCast", //$NON-NLS-1$ @@ -68,41 +66,45 @@ public class ServiceCastDetector extends Detector implements Detector.JavaScanne return true; } - // ---- Implements JavaScanner ---- + // ---- Implements UastScanner ---- + @Override - public List getApplicableMethodNames() { + public List getApplicableFunctionNames() { return Collections.singletonList("getSystemService"); //$NON-NLS-1$ } @Override - public void visitMethod(@NonNull JavaContext context, @Nullable AstVisitor visitor, - @NonNull MethodInvocation node) { - if (node.getParent() instanceof Cast) { - Cast cast = (Cast) node.getParent(); - StrictListAccessor args = node.astArguments(); - if (args.size() == 1) { - String name = stripPackage(args.first().toString()); - String expectedClass = getExpectedType(name); - if (expectedClass != null) { - String castType = cast.astTypeReference().getTypeName(); - if (castType.indexOf('.') == -1) { - expectedClass = stripPackage(expectedClass); - } - if (!castType.equals(expectedClass)) { - // It's okay to mix and match - // android.content.ClipboardManager and android.text.ClipboardManager - if (isClipboard(castType) && isClipboard(expectedClass)) { - return; - } + public void visitFunctionCall(UastAndroidContext context, UCallExpression node) { + UExpression receiver = UastUtils.getQualifiedCallElement(node); + UElement parent = receiver.getParent(); + if (!(parent instanceof UBinaryExpressionWithType) + || ((UBinaryExpressionWithType)parent).getOperationKind() != UastBinaryExpressionWithTypeKind.TYPE_CAST) { + return; + } - String message = String.format( - "Suspicious cast to `%1$s` for a `%2$s`: expected `%3$s`", - stripPackage(castType), name, stripPackage(expectedClass)); - context.report(ISSUE, node, context.getLocation(cast), message); - } + UBinaryExpressionWithType cast = (UBinaryExpressionWithType) parent; + List args = node.getValueArguments(); + if (args.size() == 1) { + String name = stripPackage(args.get(0).renderString()); + String expectedClass = getExpectedType(name); + if (expectedClass != null) { + String castType = cast.getType().getName(); + if (castType.indexOf('.') == -1) { + expectedClass = stripPackage(expectedClass); } + if (!castType.equals(expectedClass)) { + // It's okay to mix and match + // android.content.ClipboardManager and android.text.ClipboardManager + if (isClipboard(castType) && isClipboard(expectedClass)) { + return; + } + String message = String.format( + "Suspicious cast to `%1$s` for a `%2$s`: expected `%3$s`", + stripPackage(castType), name, stripPackage(expectedClass)); + context.report(ISSUE, node, UastAndroidUtils.getLocation(cast), message); + } } } } diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/SetJavaScriptEnabledDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/SetJavaScriptEnabledDetector.java index f1533bea315..a8f62ddcf7f 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/SetJavaScriptEnabledDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/SetJavaScriptEnabledDetector.java @@ -14,28 +14,27 @@ * 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.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.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.MethodInvocation; +import org.jetbrains.uast.UCallExpression; +import org.jetbrains.uast.check.UastAndroidUtils; +import org.jetbrains.uast.check.UastAndroidContext; +import org.jetbrains.uast.check.UastScanner; /** * Looks for invocations of android.webkit.WebSettings.setJavaScriptEnabled. */ -public class SetJavaScriptEnabledDetector extends Detector implements Detector.JavaScanner { +public class SetJavaScriptEnabledDetector extends Detector implements UastScanner { /** Invocations of setJavaScriptEnabled */ public static final Issue ISSUE = Issue.create("SetJavaScriptEnabled", //$NON-NLS-1$ "Using `setJavaScriptEnabled`", @@ -56,21 +55,24 @@ public class SetJavaScriptEnabledDetector extends Detector implements Detector.J public SetJavaScriptEnabledDetector() { } - // ---- Implements JavaScanner ---- + // ---- Implements UastScanner ---- @Override - public void visitMethod(@NonNull JavaContext context, @Nullable AstVisitor visitor, - @NonNull MethodInvocation node) { - if (node.astArguments().size() == 1 - && !node.astArguments().first().toString().equals("false")) { //$NON-NLS-1$ - context.report(ISSUE, node, context.getLocation(node), - "Using `setJavaScriptEnabled` can introduce XSS vulnerabilities " + - "into you application, review carefully."); + public void visitFunctionCall(UastAndroidContext context, UCallExpression node) { + if (node.getValueArgumentCount() != 1) { + return; + } + + Object value = node.getValueArguments().get(0).evaluate(); + if (value instanceof Boolean && (Boolean) value) { + context.report(ISSUE, node, UastAndroidUtils.getLocation(node), + "Using `setJavaScriptEnabled` can introduce XSS vulnerabilities " + + "into you application, review carefully."); } } @Override - public List getApplicableMethodNames() { + public List getApplicableFunctionNames() { return Collections.singletonList("setJavaScriptEnabled"); } } diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/SharedPrefsDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/SharedPrefsDetector.java index 2310d230b7d..0dc32eee7fd 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/SharedPrefsDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/SharedPrefsDetector.java @@ -14,57 +14,36 @@ * limitations under the License. */ -package com.android.tools.lint.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.TypeDescriptor; +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.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.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 java.io.File; import java.util.Collections; import java.util.List; -import lombok.ast.Assert; -import lombok.ast.AstVisitor; -import lombok.ast.Block; -import lombok.ast.Case; -import lombok.ast.ClassDeclaration; -import lombok.ast.ConstructorDeclaration; -import lombok.ast.DoWhile; -import lombok.ast.Expression; -import lombok.ast.ExpressionStatement; -import lombok.ast.For; -import lombok.ast.ForwardingAstVisitor; -import lombok.ast.If; -import lombok.ast.MethodDeclaration; -import lombok.ast.MethodInvocation; -import lombok.ast.Node; -import lombok.ast.NormalTypeBody; -import lombok.ast.Return; -import lombok.ast.Statement; -import lombok.ast.VariableDeclaration; -import lombok.ast.VariableDefinition; -import lombok.ast.VariableReference; -import lombok.ast.While; +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; /** * Detector looking for SharedPreferences.edit() calls without a corresponding * commit() or apply() call */ -public class SharedPrefsDetector extends Detector implements Detector.JavaScanner { +public class SharedPrefsDetector extends Detector implements UastScanner { /** The main issue discovered by this detector */ public static final Issue ISSUE = Issue.create( "CommitPrefEdits", //$NON-NLS-1$ @@ -95,70 +74,47 @@ public class SharedPrefsDetector extends Detector implements Detector.JavaScanne } - // ---- Implements JavaScanner ---- + // ---- Implements UastScanner ---- @Override - public List getApplicableMethodNames() { + public List getApplicableFunctionNames() { return Collections.singletonList("edit"); //$NON-NLS-1$ } - @Nullable - private static NormalTypeBody findSurroundingTypeBody(Node scope) { - while (scope != null) { - Class 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 == NormalTypeBody.class) { - return (NormalTypeBody) scope; - } - - scope = scope.getParent(); - } - - return null; - } - - @Override - public void visitMethod(@NonNull JavaContext context, @Nullable AstVisitor visitor, - @NonNull MethodInvocation node) { - assert node.astName().astValue().equals("edit"); + public void visitFunctionCall(UastAndroidContext context, UCallExpression node) { + assert "edit".equals(node.getFunctionName()); boolean verifiedType = false; - ResolvedNode resolve = context.resolve(node); - if (resolve instanceof ResolvedMethod) { - ResolvedMethod method = (ResolvedMethod) resolve; - TypeDescriptor returnType = method.getReturnType(); + UFunction resolve = node.resolve(context); + if (resolve != null) { + UType returnType = resolve.getReturnType(); if (returnType == null || - !returnType.matchesName(ANDROID_CONTENT_SHARED_PREFERENCES_EDITOR)) { + !returnType.matchesFqName(ANDROID_CONTENT_SHARED_PREFERENCES_EDITOR)) { return; } verifiedType = true; } - Expression operand = node.astOperand(); - if (operand == null) { + UElement parent = node.getParent(); + if (!(parent instanceof UQualifiedExpression)) { return; } + UElement operand = ((UQualifiedExpression)parent).getReceiver(); // Looking for the specific pattern where you assign the edit() result // to a local variable; this means we won't recognize some other usages // of the API (e.g. assigning it to a previously declared variable) but // is needed until we have type attribution in the AST itself. - Node parent = node.getParent(); - VariableDefinition definition = getLhs(parent); + UVariable definition = getLhs(parent); boolean allowCommitBeforeTarget; if (definition == null) { - if (operand instanceof VariableReference) { - if (!verifiedType) { - NormalTypeBody body = findSurroundingTypeBody(parent); - if (body == null) { - return; - } - String variableName = ((VariableReference) operand).astIdentifier().astValue(); - String type = getFieldType(body, variableName); - if (type == null || !type.equals("SharedPreferences")) { //$NON-NLS-1$ + if (operand instanceof USimpleReferenceExpression) { + UDeclaration resolvedDeclaration = (((USimpleReferenceExpression)operand).resolve(context)); + if (resolvedDeclaration instanceof UVariable) { + String type = ((UVariable)resolvedDeclaration).getType().getName(); + if (!type.equals("SharedPreferences")) { //$NON-NLS-1$ return; } } @@ -168,11 +124,12 @@ public class SharedPrefsDetector extends Detector implements Detector.JavaScanne } } else { if (!verifiedType) { - String type = definition.astTypeReference().toString(); - if (!type.endsWith("SharedPreferences.Editor")) { //$NON-NLS-1$ - if (!type.equals("Editor") || //$NON-NLS-1$ - !LintUtils.isImported(context.getCompilationUnit(), - ANDROID_CONTENT_SHARED_PREFERENCES_EDITOR)) { + UType type = definition.getType(); + String possiblefqName = type.resolveOrEmpty(context).getFqNameOrName(); + if (possiblefqName.endsWith("SharedPreferences.Editor")) { //$NON-NLS-1$ + if (!type.matchesFqName("Editor") || //$NON-NLS-1$ + !LintUtils.isImported(context.getLintContext().getCompilationUnit(), + ANDROID_CONTENT_SHARED_PREFERENCES_EDITOR)) { return; } } @@ -180,44 +137,27 @@ public class SharedPrefsDetector extends Detector implements Detector.JavaScanne allowCommitBeforeTarget = false; } - Node method = JavaContext.findSurroundingMethod(parent); + UFunction method = UastUtils.getContainingFunction(parent); if (method == null) { return; } CommitFinder finder = new CommitFinder(context, node, allowCommitBeforeTarget); - method.accept(finder); + finder.process(method); if (!finder.isCommitCalled()) { - context.report(ISSUE, method, context.getLocation(node), - "`SharedPreferences.edit()` without a corresponding `commit()` or `apply()` call"); + context.report(ISSUE, method, UastAndroidUtils.getLocation(node), + "`SharedPreferences.edit()` without a corresponding `commit()` or `apply()` call"); } } @Nullable - private static String getFieldType(@NonNull NormalTypeBody cls, @NonNull String name) { - List children = cls.getChildren(); - for (Node child : children) { - if (child.getClass() == VariableDeclaration.class) { - VariableDeclaration declaration = (VariableDeclaration) child; - VariableDefinition definition = declaration.astDefinition(); - return definition.astTypeReference().toString(); - } - } - - return null; - } - - @Nullable - private static VariableDefinition getLhs(@NonNull Node node) { + private static UVariable getLhs(@NonNull UElement node) { while (node != null) { - Class type = node.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) { + if (node instanceof UFunction) { return null; } - if (type == VariableDefinition.class) { - return (VariableDefinition) node; + if (node instanceof UVariable) { + return ((UVariable)node); } node = node.getParent(); @@ -226,96 +166,98 @@ public class SharedPrefsDetector extends Detector implements Detector.JavaScanne return null; } - private static class CommitFinder extends ForwardingAstVisitor { + private static class CommitFinder extends UastVisitor { /** The target edit call */ - private final MethodInvocation mTarget; + private final UCallExpression mTarget; /** whether it allows the commit call to be seen before the target node */ private final boolean mAllowCommitBeforeTarget; - private final JavaContext mContext; + private final UastAndroidContext mContext; /** Whether we've found one of the commit/cancel methods */ private boolean mFound; /** Whether we've seen the target edit node yet */ private boolean mSeenTarget; - private CommitFinder(JavaContext context, MethodInvocation target, - boolean allowCommitBeforeTarget) { + private CommitFinder(UastAndroidContext context, UCallExpression target, + boolean allowCommitBeforeTarget) { mContext = context; mTarget = target; mAllowCommitBeforeTarget = allowCommitBeforeTarget; } @Override - public boolean visitMethodInvocation(MethodInvocation node) { + public boolean visitCallExpression(@NotNull UCallExpression node) { + UElement qualifiedElement = UastUtils.getQualifiedCallElement(node); + if (node == mTarget) { mSeenTarget = true; - } else if (mAllowCommitBeforeTarget || mSeenTarget || node.astOperand() == mTarget) { - String name = node.astName().astValue(); + } else if (mAllowCommitBeforeTarget || mSeenTarget || + (qualifiedElement instanceof UQualifiedExpression && + ((UQualifiedExpression)qualifiedElement).getReceiver() == mTarget)) { + String name = node.getFunctionName(); boolean isCommit = "commit".equals(name); if (isCommit || "apply".equals(name)) { //$NON-NLS-1$ //$NON-NLS-2$ // TODO: Do more flow analysis to see whether we're really calling commit/apply // on the right type of object? mFound = true; - ResolvedNode resolved = mContext.resolve(node); - if (resolved instanceof JavaParser.ResolvedMethod) { - ResolvedMethod method = (ResolvedMethod) resolved; - JavaParser.ResolvedClass clz = method.getContainingClass(); - if (clz.isSubclassOf("android.content.SharedPreferences.Editor", false) - && mContext.getProject().getMinSdkVersion().getApiLevel() >= 9) { + UFunction method = node.resolve(mContext); + if (method != null) { + UClass clz = UastUtils.getContainingClassOrEmpty(method); + if (clz.isSubclassOf("android.content.SharedPreferences.Editor") + && mContext.getLintContext().getProject().getMinSdkVersion().getApiLevel() >= 9) { // See if the return value is read: can only replace commit with // apply if the return value is not considered - Node parent = node.getParent(); boolean returnValueIgnored = false; - if (parent instanceof MethodDeclaration || - parent instanceof ConstructorDeclaration || - parent instanceof ClassDeclaration || - parent instanceof Block || - parent instanceof ExpressionStatement) { + if (qualifiedElement instanceof UFunction || + qualifiedElement instanceof UClass || + qualifiedElement instanceof UBlockExpression) { + returnValueIgnored = true; + } else if (qualifiedElement instanceof UIfExpression) { + returnValueIgnored = ((UIfExpression) qualifiedElement).getCondition() != node; + } else if (qualifiedElement instanceof USpecialExpressionList && + ((USpecialExpressionList)qualifiedElement).getKind() == UastSpecialExpressionKind.RETURN) { + returnValueIgnored = false; + } else if (qualifiedElement instanceof UVariable) { + returnValueIgnored = false; + } else if (qualifiedElement instanceof UForExpression) { + returnValueIgnored = ((UForExpression) qualifiedElement).getCondition() != node; + } else if (qualifiedElement instanceof UWhileExpression) { + returnValueIgnored = ((UWhileExpression) qualifiedElement).getCondition() != node; + } else if (qualifiedElement instanceof UDoWhileExpression) { + returnValueIgnored = ((UDoWhileExpression) qualifiedElement).getCondition() != node; + } else if (qualifiedElement instanceof UExpressionSwitchClauseExpression) { + returnValueIgnored = ((UExpressionSwitchClauseExpression) qualifiedElement).getCaseValue() != node; + } else if (qualifiedElement instanceof USpecialExpressionList && + ((USpecialExpressionList)qualifiedElement).getKind() == JavaSpecialExpressionKinds.ASSERT) { + returnValueIgnored = !((USpecialExpressionList) qualifiedElement).getExpressions().contains(node); + } else { returnValueIgnored = true; - } else if (parent instanceof Statement) { - if (parent instanceof If) { - returnValueIgnored = ((If) parent).astCondition() != node; - } else if (parent instanceof Return) { - returnValueIgnored = false; - } else if (parent instanceof VariableDeclaration) { - returnValueIgnored = false; - } else if (parent instanceof For) { - returnValueIgnored = ((For) parent).astCondition() != node; - } else if (parent instanceof While) { - returnValueIgnored = ((While) parent).astCondition() != node; - } else if (parent instanceof DoWhile) { - returnValueIgnored = ((DoWhile) parent).astCondition() != node; - } else if (parent instanceof Case) { - returnValueIgnored = ((Case) parent).astCondition() != node; - } else if (parent instanceof Assert) { - returnValueIgnored = ((Assert) parent).astAssertion() != node; - } else { - returnValueIgnored = true; - } } if (returnValueIgnored && isCommit) { String message = "Consider using `apply()` instead; `commit` writes " - + "its data to persistent storage immediately, whereas " - + "`apply` will handle it in the background"; - mContext.report(ISSUE, node, mContext.getLocation(node), message); + + "its data to persistent storage immediately, whereas " + + "`apply` will handle it in the background"; + mContext.report(ISSUE, node, UastAndroidUtils.getLocation(node), message); } } } } } - return super.visitMethodInvocation(node); + return false; } @Override - public boolean visitReturn(Return node) { - if (node.astValue() == mTarget) { - // If you just do "return editor.commit() don't warn - mFound = true; + public boolean visitSpecialExpressionList(@NotNull USpecialExpressionList node) { + if (node.getKind() == UastSpecialExpressionKind.RETURN) { + if (node.getExpressions().contains(mTarget)) { + mFound = true; + } } - return super.visitReturn(node); + + return false; } boolean isCommitCalled() { diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/SignatureOrSystemDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/SignatureOrSystemDetector.java index 62ffd15e159..d5cab0ba7ee 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/SignatureOrSystemDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/SignatureOrSystemDetector.java @@ -14,19 +14,19 @@ * limitations under the License. */ -package com.android.tools.lint.checks; +package com.android.tools.klint.checks; -import static com.android.tools.lint.checks.PermissionRequirement.ATTR_PROTECTION_LEVEL; +import static com.android.tools.klint.checks.PermissionRequirement.ATTR_PROTECTION_LEVEL; import com.android.annotations.NonNull; -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.lint.detector.api.XmlContext; +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.XmlContext; import org.w3c.dom.Attr; diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/StateListDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/StateListDetector.java index 36f53c0b0e0..a323065621d 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/StateListDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/StateListDetector.java @@ -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_URI; 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.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.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.Document; diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/StringFormatDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/StringFormatDetector.java index 6b902a5dbf1..98e6dcc79df 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/StringFormatDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/StringFormatDetector.java @@ -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; import static com.android.SdkConstants.DOT_JAVA; @@ -23,19 +23,17 @@ import static com.android.SdkConstants.GET_STRING_METHOD; import static com.android.SdkConstants.R_CLASS; import static com.android.SdkConstants.R_PREFIX; import static com.android.SdkConstants.TAG_STRING; -import static com.android.tools.lint.checks.SharedPrefsDetector.ANDROID_CONTENT_SHARED_PREFERENCES; -import static com.android.tools.lint.client.api.JavaParser.TYPE_BOOLEAN; -import static com.android.tools.lint.client.api.JavaParser.TYPE_BYTE; -import static com.android.tools.lint.client.api.JavaParser.TYPE_CHAR; -import static com.android.tools.lint.client.api.JavaParser.TYPE_DOUBLE; -import static com.android.tools.lint.client.api.JavaParser.TYPE_FLOAT; -import static com.android.tools.lint.client.api.JavaParser.TYPE_INT; -import static com.android.tools.lint.client.api.JavaParser.TYPE_LONG; -import static com.android.tools.lint.client.api.JavaParser.TYPE_NULL; -import static com.android.tools.lint.client.api.JavaParser.TYPE_OBJECT; -import static com.android.tools.lint.client.api.JavaParser.TYPE_SHORT; -import static com.android.tools.lint.client.api.JavaParser.TYPE_STRING; -import static com.android.tools.lint.client.api.JavaParser.TypeDescriptor; +import static com.android.tools.klint.client.api.JavaParser.TYPE_BOOLEAN; +import static com.android.tools.klint.client.api.JavaParser.TYPE_BYTE; +import static com.android.tools.klint.client.api.JavaParser.TYPE_CHAR; +import static com.android.tools.klint.client.api.JavaParser.TYPE_DOUBLE; +import static com.android.tools.klint.client.api.JavaParser.TYPE_FLOAT; +import static com.android.tools.klint.client.api.JavaParser.TYPE_INT; +import static com.android.tools.klint.client.api.JavaParser.TYPE_LONG; +import static com.android.tools.klint.client.api.JavaParser.TYPE_NULL; +import static com.android.tools.klint.client.api.JavaParser.TYPE_OBJECT; +import static com.android.tools.klint.client.api.JavaParser.TYPE_SHORT; +import static com.android.tools.klint.client.api.JavaParser.TYPE_STRING; import com.android.annotations.NonNull; import com.android.annotations.Nullable; @@ -45,27 +43,30 @@ import com.android.ide.common.res2.AbstractResourceRepository; import com.android.ide.common.res2.ResourceItem; import com.android.resources.ResourceFolderType; import com.android.resources.ResourceType; -import com.android.tools.lint.client.api.JavaParser; -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.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.Handle; -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.XmlContext; +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.Implementation; +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.Location.Handle; +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.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.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 org.w3c.dom.Node; import org.w3c.dom.NodeList; @@ -85,24 +86,6 @@ import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; -import lombok.ast.AstVisitor; -import lombok.ast.BooleanLiteral; -import lombok.ast.CharLiteral; -import lombok.ast.ConstructorDeclaration; -import lombok.ast.ConstructorInvocation; -import lombok.ast.Expression; -import lombok.ast.FloatingPointLiteral; -import lombok.ast.ForwardingAstVisitor; -import lombok.ast.IntegralLiteral; -import lombok.ast.MethodDeclaration; -import lombok.ast.MethodInvocation; -import lombok.ast.NullLiteral; -import lombok.ast.Select; -import lombok.ast.StrictListAccessor; -import lombok.ast.StringLiteral; -import lombok.ast.VariableDefinitionEntry; -import lombok.ast.VariableReference; - /** * Check which looks for problems with formatting strings such as inconsistencies between * translations or between string declaration and string usage in Java. @@ -110,7 +93,7 @@ import lombok.ast.VariableReference; * TODO: Verify booleans! * TODO: Handle Resources.getQuantityString as well */ -public class StringFormatDetector extends ResourceXmlDetector implements Detector.JavaScanner { +public class StringFormatDetector extends ResourceXmlDetector implements UastScanner { private static final Implementation IMPLEMENTATION_XML = new Implementation( StringFormatDetector.class, Scope.ALL_RESOURCES_SCOPE); @@ -118,7 +101,7 @@ public class StringFormatDetector extends ResourceXmlDetector implements Detecto @SuppressWarnings("unchecked") private static final Implementation IMPLEMENTATION_XML_AND_JAVA = new Implementation( StringFormatDetector.class, - EnumSet.of(Scope.ALL_RESOURCE_FILES, Scope.JAVA_FILE), + EnumSet.of(Scope.ALL_RESOURCE_FILES, Scope.SOURCE_FILE), Scope.JAVA_FILE_SCOPE); @@ -995,25 +978,29 @@ public class StringFormatDetector extends ResourceXmlDetector implements Detecto return false; } + // ---- Implements UastScanner ---- + + @Override - public List getApplicableMethodNames() { + public List getApplicableFunctionNames() { return Arrays.asList(FORMAT_METHOD, GET_STRING_METHOD); } @Override - public void visitMethod(@NonNull JavaContext context, @Nullable AstVisitor visitor, - @NonNull MethodInvocation node) { - if (mFormatStrings == null && !context.getClient().supportsProjectResources()) { + public void visitFunctionCall(UastAndroidContext context, UCallExpression node) { + if (mFormatStrings == null && !context.getLintContext().getClient().supportsProjectResources()) { return; } - String methodName = node.astName().astValue(); - if (methodName.equals(FORMAT_METHOD)) { + UExpression receiver = UastUtils.getReceiver(node); + + String methodName = node.getFunctionName(); + if (FORMAT_METHOD.equals(methodName)) { // String.format(getResources().getString(R.string.foo), arg1, arg2, ...) // Check that the arguments in R.string.foo match arg1, arg2, ... - if (node.astOperand() instanceof VariableReference) { - VariableReference ref = (VariableReference) node.astOperand(); - if ("String".equals(ref.astIdentifier().astValue())) { //$NON-NLS-1$ + if (receiver instanceof USimpleReferenceExpression) { + USimpleReferenceExpression ref = (USimpleReferenceExpression) receiver; + if ("String".equals(ref.getIdentifier())) { //$NON-NLS-1$ // Found a String.format call // Look inside to see if we can find an R string // Find surrounding method @@ -1023,14 +1010,14 @@ public class StringFormatDetector extends ResourceXmlDetector implements Detecto } else { // getResources().getString(R.string.foo, arg1, arg2, ...) // Check that the arguments in R.string.foo match arg1, arg2, ... - if (node.astArguments().size() > 1 && node.astOperand() != null ) { + if (node.getValueArgumentCount() > 1 && receiver != null ) { checkFormatCall(context, node); } } } - private void checkFormatCall(JavaContext context, MethodInvocation node) { - lombok.ast.Node current = getParentMethod(node); + private void checkFormatCall(UastAndroidContext context, UCallExpression node) { + UFunction current = UastUtils.getContainingFunction(node); if (current != null) { checkStringFormatCall(context, current, node); } @@ -1045,17 +1032,18 @@ public class StringFormatDetector extends ResourceXmlDetector implements Detecto * @param call the AST node for the {@link String#format} */ private void checkStringFormatCall( - JavaContext context, - lombok.ast.Node method, - MethodInvocation call) { + UastAndroidContext context, + UFunction method, + UCallExpression call) { - StrictListAccessor args = call.astArguments(); + List args = call.getValueArguments(); if (args.isEmpty()) { return; } - StringTracker tracker = new StringTracker(context, method, call, 0); - method.accept(tracker); + JavaContext lintContext = context.getLintContext(); + UastStringTracker tracker = new UastStringTracker(lintContext, method, call, 0); + tracker.process(method); String name = tracker.getFormatStringName(); if (name == null) { return; @@ -1069,7 +1057,7 @@ public class StringFormatDetector extends ResourceXmlDetector implements Detecto Handle handle = mNotFormatStrings.get(name); Object clientData = handle.getClientData(); if (clientData instanceof Node) { - if (context.getDriver().isSuppressed(null, INVALID, (Node) clientData)) { + if (lintContext.getDriver().isSuppressed(null, INVALID, (Node) clientData)) { return; } } @@ -1082,36 +1070,36 @@ public class StringFormatDetector extends ResourceXmlDetector implements Detecto return; } - Iterator argIterator = args.iterator(); - Expression first = argIterator.next(); - Expression second = argIterator.hasNext() ? argIterator.next() : null; + Iterator argIterator = args.iterator(); + UExpression first = argIterator.next(); + UExpression second = argIterator.hasNext() ? argIterator.next() : null; boolean specifiesLocale; - TypeDescriptor parameterType = context.getType(first); + UType parameterType = first.getExpressionType(); if (parameterType != null) { specifiesLocale = isLocaleReference(parameterType.getName()); - } else if (!call.astName().astValue().equals(FORMAT_METHOD)) { + } else if (!FORMAT_METHOD.equals(call.getFunctionName())) { specifiesLocale = false; } else { // No type information with this AST; use string patterns instead to make // an educated guess - String firstName = first.toString(); + String firstName = first.renderString(); specifiesLocale = firstName.startsWith("Locale.") //$NON-NLS-1$ || firstName.contains("locale") //$NON-NLS-1$ || firstName.equals("null") //$NON-NLS-1$ - || (second != null && second.toString().contains("getString") //$NON-NLS-1$ + || (second != null && second.renderString().contains("getString") //$NON-NLS-1$ && !firstName.contains("getString") //$NON-NLS-1$ && !firstName.contains(R_PREFIX) - && !(first instanceof StringLiteral)); + && !(UastLiteralUtils.isStringLiteral(first))); } List> list = mFormatStrings != null ? mFormatStrings.get(name) : null; if (list == null) { - LintClient client = context.getClient(); + LintClient client = lintContext.getClient(); if (client.supportsProjectResources() && - !context.getScope().contains(Scope.RESOURCE_FILE)) { + !lintContext.getScope().contains(Scope.RESOURCE_FILE)) { AbstractResourceRepository resources = client - .getProjectResources(context.getMainProject(), true); + .getProjectResources(lintContext.getMainProject(), true); List items = resources .getResourceItem(ResourceType.STRING, name); if (items != null) { @@ -1184,18 +1172,21 @@ public class StringFormatDetector extends ResourceXmlDetector implements Detecto Location location = context.getLocation(call); Location secondary = handle.resolve(); - secondary.setMessage(String.format("This definition requires %1$d arguments", - count)); - location.setSecondary(secondary); - String message = String.format( - "Wrong argument count, format string `%1$s` requires `%2$d` but format " + - "call supplies `%3$d`", - name, count, args.size() - 1 - (specifiesLocale ? 1 : 0)); - context.report(ARG_TYPES, method, location, message); - if (reported == null) { - reported = Sets.newHashSet(); + + if (location != null && secondary != null) { + secondary.setMessage(String.format("This definition requires %1$d arguments", + count)); + location.setSecondary(secondary); + String message = String.format( + "Wrong argument count, format string `%1$s` requires `%2$d` but format " + + "call supplies `%3$d`", + name, count, args.size() - 1 - (specifiesLocale ? 1 : 0)); + context.report(ARG_TYPES, method, location, message); + if (reported == null) { + reported = Sets.newHashSet(); + } + reported.add(s); } - reported.add(s); } else { for (int i = 1; i <= count; i++) { int argumentIndex = i + (specifiesLocale ? 1 : 0); @@ -1265,23 +1256,25 @@ public class StringFormatDetector extends ResourceXmlDetector implements Detecto continue; } - Expression argument = tracker.getArgument(argumentIndex); + UExpression argument = tracker.getArgument(argumentIndex); Location location = context.getLocation(argument); Location secondary = handle.resolve(); - secondary.setMessage("Conflicting argument declaration here"); - location.setSecondary(secondary); + if (location != null && secondary != null) { + secondary.setMessage("Conflicting argument declaration here"); + location.setSecondary(secondary); - String message = String.format( - "Wrong argument type for formatting argument '#%1$d' " + - "in `%2$s`: conversion is '`%3$s`', received `%4$s` " + - "(argument #%5$d in method call)", - i, name, formatType, type.getSimpleName(), - argumentIndex + 1); - context.report(ARG_TYPES, method, location, message); - if (reported == null) { - reported = Sets.newHashSet(); + String message = String.format( + "Wrong argument type for formatting argument '#%1$d' " + + "in `%2$s`: conversion is '`%3$s`', received `%4$s` " + + "(argument #%5$d in method call)", + i, name, formatType, type.getSimpleName(), + argumentIndex + 1); + context.report(ARG_TYPES, method, location, message); + if (reported == null) { + reported = Sets.newHashSet(); + } + reported.add(s); } - reported.add(s); } } } @@ -1290,23 +1283,22 @@ public class StringFormatDetector extends ResourceXmlDetector implements Detecto } } - private static boolean isSharedPreferenceGetString(@NonNull JavaContext context, - @NonNull MethodInvocation call) { - if (!GET_STRING_METHOD.equals(call.astName().astValue())) { + private static boolean isSharedPreferenceGetString(@NonNull UastAndroidContext context, + @NonNull UCallExpression call) { + if (!GET_STRING_METHOD.equals(call.getFunctionName())) { return false; } - JavaParser.ResolvedNode resolved = context.resolve(call); - if (resolved instanceof JavaParser.ResolvedMethod) { - JavaParser.ResolvedMethod resolvedMethod = (JavaParser.ResolvedMethod) resolved; - JavaParser.ResolvedClass containingClass = resolvedMethod.getContainingClass(); - return containingClass.isSubclassOf(ANDROID_CONTENT_SHARED_PREFERENCES, false); + UFunction resolvedMethod = call.resolve(context); + if (resolvedMethod != null) { + UClass containingClass = UastUtils.getContainingClassOrEmpty(resolvedMethod); + return containingClass.isSubclassOf(SharedPrefsDetector.ANDROID_CONTENT_SHARED_PREFERENCES); } return false; // not certain } - private static boolean isLocaleReference(@Nullable TypeDescriptor reference) { + private static boolean isLocaleReference(@Nullable UType reference) { return reference != null && isLocaleReference(reference.getName()); } @@ -1315,27 +1307,14 @@ public class StringFormatDetector extends ResourceXmlDetector implements Detecto || typeName.equals("java.util.Locale")); //$NON-NLS-1$ } - /** Returns the parent method of the given AST node */ - @Nullable - public static lombok.ast.Node getParentMethod(@NonNull lombok.ast.Node node) { - lombok.ast.Node current = node.getParent(); - while (current != null - && !(current instanceof MethodDeclaration) - && !(current instanceof ConstructorDeclaration)) { - current = current.getParent(); - } - - return current; - } - /** Returns the resource name corresponding to the first argument in the given call */ @Nullable public static String getResourceForFirstArg( - @NonNull lombok.ast.Node method, - @NonNull lombok.ast.Node call) { - assert call instanceof MethodInvocation || call instanceof ConstructorInvocation; - StringTracker tracker = new StringTracker(null, method, call, 0); - method.accept(tracker); + @NonNull UElement method, + @NonNull UElement call) { + assert call instanceof UCallExpression; + UastStringTracker tracker = new UastStringTracker(null, method, call, 0); + tracker.process(method); return tracker.getFormatStringName(); } @@ -1343,12 +1322,12 @@ public class StringFormatDetector extends ResourceXmlDetector implements Detecto /** Returns the resource name corresponding to the given argument in the given call */ @Nullable public static String getResourceArg( - @NonNull lombok.ast.Node method, - @NonNull lombok.ast.Node call, - int argIndex) { - assert call instanceof MethodInvocation || call instanceof ConstructorInvocation; - StringTracker tracker = new StringTracker(null, method, call, argIndex); - method.accept(tracker); + @NonNull UElement method, + @NonNull UElement call, + int argIndex) { + assert call instanceof UCallExpression; + UastStringTracker tracker = new UastStringTracker(null, method, call, argIndex); + tracker.process(method); return tracker.getFormatStringName(); } @@ -1374,9 +1353,9 @@ public class StringFormatDetector extends ResourceXmlDetector implements Detecto * certain argument types are valid. Note however that it does not do full-blown * type analysis by checking method call signatures and so on. */ - private static class StringTracker extends ForwardingAstVisitor { + private static class UastStringTracker extends UastVisitor { /** Method we're searching within */ - private final lombok.ast.Node mTop; + private final UElement mTop; /** The argument index in the method we're targeting */ private final int mArgIndex; /** Map from variable name to corresponding string resource name */ @@ -1384,7 +1363,7 @@ public class StringFormatDetector extends ResourceXmlDetector implements Detecto /** Map from variable name to corresponding type */ private final Map> mTypes = new HashMap>(); /** The AST node for the String.format we're interested in */ - private final lombok.ast.Node mTargetNode; + private final UElement mTargetNode; private boolean mDone; @Nullable private JavaContext mContext; @@ -1395,7 +1374,7 @@ public class StringFormatDetector extends ResourceXmlDetector implements Detecto */ private String mName; - public StringTracker(@Nullable JavaContext context, lombok.ast.Node top, lombok.ast.Node targetNode, int argIndex) { + public UastStringTracker(@Nullable JavaContext context, UElement top, UElement targetNode, int argIndex) { mContext = context; mTop = top; mArgIndex = argIndex; @@ -1415,7 +1394,7 @@ public class StringFormatDetector extends ResourceXmlDetector implements Detecto * @return the class (such as {@link Integer#TYPE} etc) or null if not known */ public Class getArgumentType(int argument) { - Expression arg = getArgument(argument); + UExpression arg = getArgument(argument); if (arg != null) { // Look up type based on the source code literals Class type = getType(arg); @@ -1426,14 +1405,14 @@ public class StringFormatDetector extends ResourceXmlDetector implements Detecto // If the AST supports type resolution, use that for other types // of expressions if (mContext != null) { - return getTypeClass(mContext.getType(arg)); + return getTypeClass(arg.getExpressionType()); } } return null; } - private static Class getTypeClass(@Nullable TypeDescriptor type) { + private static Class getTypeClass(@Nullable UType type) { if (type != null) { return getTypeClass(type.getName()); } @@ -1460,27 +1439,27 @@ public class StringFormatDetector extends ResourceXmlDetector implements Detecto } else if (fqcn.equals(TYPE_CHAR)) { return Character.TYPE; } else if (fqcn.equals("BigDecimal") //$NON-NLS-1$ - || fqcn.equals("java.math.BigDecimal")) { //$NON-NLS-1$ + || fqcn.equals("java.math.BigDecimal")) { //$NON-NLS-1$ return Float.TYPE; } else if (fqcn.equals("BigInteger") //$NON-NLS-1$ - || fqcn.equals("java.math.BigInteger")) { //$NON-NLS-1$ + || fqcn.equals("java.math.BigInteger")) { //$NON-NLS-1$ return Integer.TYPE; } else if (fqcn.equals(TYPE_OBJECT)) { - return null; - } else if (fqcn.startsWith("java.lang.")) { - if (fqcn.equals("java.lang.Integer") - || fqcn.equals("java.lang.Short") - || fqcn.equals("java.lang.Byte") - || fqcn.equals("java.lang.Long")) { - return Integer.TYPE; - } else if (fqcn.equals("java.lang.Float") - || fqcn.equals("java.lang.Double")) { - return Float.TYPE; - } else { return null; - } + } else if (fqcn.startsWith("java.lang.")) { + if (fqcn.equals("java.lang.Integer") + || fqcn.equals("java.lang.Short") + || fqcn.equals("java.lang.Byte") + || fqcn.equals("java.lang.Long")) { + return Integer.TYPE; + } else if (fqcn.equals("java.lang.Float") + || fqcn.equals("java.lang.Double")) { + return Float.TYPE; + } else { + return null; + } } else if (fqcn.equals(TYPE_BYTE)) { - return Byte.TYPE; + return Byte.TYPE; } else if (fqcn.equals(TYPE_SHORT)) { return Short.TYPE; } else { @@ -1488,45 +1467,37 @@ public class StringFormatDetector extends ResourceXmlDetector implements Detecto } } - public Expression getArgument(int argument) { - if (!(mTargetNode instanceof MethodInvocation)) { + public UExpression getArgument(int argument) { + if (!(mTargetNode instanceof UCallExpression)) { return null; } - MethodInvocation call = (MethodInvocation) mTargetNode; - StrictListAccessor args = call.astArguments(); - if (argument >= args.size()) { + UCallExpression call = (UCallExpression) mTargetNode; + List args = call.getValueArguments(); + if (argument < 0 || argument >= args.size()) { return null; } - Iterator iterator = args.iterator(); - int index = 0; - while (iterator.hasNext()) { - Expression arg = iterator.next(); - if (index++ == argument) { - return arg; - } + return args.get(argument); + } + + @Override + public void process(@NotNull UElement element) { + if (!mDone) { + super.process(element); } - - return null; } @Override - public boolean visitNode(lombok.ast.Node node) { - return mDone || super.visitNode(node); - - } - - @Override - public boolean visitVariableReference(VariableReference node) { - if (node.astIdentifier().astValue().equals(R_CLASS) && //$NON-NLS-1$ - node.getParent() instanceof Select && - node.getParent().getParent() instanceof Select) { + public boolean visitQualifiedExpression(@NotNull UQualifiedExpression node) { + if (node.selectorMatches(R_CLASS) && //$NON-NLS-1$ + node.getParent() instanceof UQualifiedExpression && + node.getParent().getParent() instanceof UQualifiedExpression) { // See if we're on the right hand side of an assignment - lombok.ast.Node current = node.getParent().getParent(); - String reference = ((Select) current).astIdentifier().astValue(); + UElement current = node.getParent().getParent(); + String reference = ((UQualifiedExpression) current).getSelector().renderString(); - while (current != mTop && !(current instanceof VariableDefinitionEntry)) { + while (current != null && current != mTop && !(current instanceof UVariable)) { if (current == mTargetNode) { mName = reference; mDone = true; @@ -1534,9 +1505,9 @@ public class StringFormatDetector extends ResourceXmlDetector implements Detecto } current = current.getParent(); } - if (current instanceof VariableDefinitionEntry) { - VariableDefinitionEntry entry = (VariableDefinitionEntry) current; - String variable = entry.astName().astValue(); + if (current instanceof UVariable) { + UVariable entry = (UVariable) current; + String variable = entry.getName(); mMap.put(variable, reference); } } @@ -1545,12 +1516,10 @@ public class StringFormatDetector extends ResourceXmlDetector implements Detecto } @Nullable - private Expression getTargetArgument() { - Iterator iterator; - if (mTargetNode instanceof MethodInvocation) { - iterator = ((MethodInvocation) mTargetNode).astArguments().iterator(); - } else if (mTargetNode instanceof ConstructorInvocation) { - iterator = ((ConstructorInvocation) mTargetNode).astArguments().iterator(); + private UExpression getTargetArgument() { + Iterator iterator; + if (mTargetNode instanceof UCallExpression) { + iterator = ((UCallExpression) mTargetNode).getValueArguments().iterator(); } else { return null; } @@ -1560,13 +1529,13 @@ public class StringFormatDetector extends ResourceXmlDetector implements Detecto i++; } if (iterator.hasNext()) { - Expression next = iterator.next(); + UExpression next = iterator.next(); if (next != null && mContext != null && iterator.hasNext()) { - TypeDescriptor type = mContext.getType(next); + UType type = next.getExpressionType(); if (isLocaleReference(type)) { next = iterator.next(); } else if (type == null - && next.toString().startsWith("Locale.")) { //$NON-NLS-1$ + && next.renderString().startsWith("Locale.")) { //$NON-NLS-1$ next = iterator.next(); } } @@ -1577,45 +1546,27 @@ public class StringFormatDetector extends ResourceXmlDetector implements Detecto } @Override - public boolean visitMethodInvocation(MethodInvocation node) { + public boolean visitCallExpression(@NotNull UCallExpression node) { if (node == mTargetNode) { - Expression arg = getTargetArgument(); - if (arg instanceof VariableReference) { - VariableReference reference = (VariableReference) arg; - String variable = reference.astIdentifier().astValue(); - mName = mMap.get(variable); - mDone = true; - return true; + UExpression arg = getTargetArgument(); + if (arg instanceof USimpleReferenceExpression) { + USimpleReferenceExpression reference = (USimpleReferenceExpression) arg; + String variable = reference.getIdentifier(); + mName = mMap.get(variable); + mDone = true; + return true; } } // Is this a getString() call? On a resource object? If so, // promote the resource argument up to the left hand side - return super.visitMethodInvocation(node); + return false; } @Override - public boolean visitConstructorInvocation(ConstructorInvocation node) { - if (node == mTargetNode) { - Expression arg = getTargetArgument(); - if (arg instanceof VariableReference) { - VariableReference reference = (VariableReference) arg; - String variable = reference.astIdentifier().astValue(); - mName = mMap.get(variable); - mDone = true; - return true; - } - } - - // Is this a getString() call? On a resource object? If so, - // promote the resource argument up to the left hand side - return super.visitConstructorInvocation(node); - } - - @Override - public boolean visitVariableDefinitionEntry(VariableDefinitionEntry node) { - String name = node.astName().astValue(); - Expression rhs = node.astInitializer(); + public boolean visitVariable(@NotNull UVariable node) { + String name = node.getName(); + UExpression rhs = node.getInitializer(); Class type = getType(rhs); if (type != null) { mTypes.put(name, type); @@ -1629,50 +1580,59 @@ public class StringFormatDetector extends ResourceXmlDetector implements Detecto } } - return super.visitVariableDefinitionEntry(node); + return false; } - private Class getType(Expression expression) { + private Class getType(UExpression expression) { if (expression == null) { - return null; + return null; } - if (expression instanceof VariableReference) { - VariableReference reference = (VariableReference) expression; - String variable = reference.astIdentifier().astValue(); + if (expression instanceof USimpleReferenceExpression) { + USimpleReferenceExpression reference = (USimpleReferenceExpression)expression; + String variable = reference.getIdentifier(); Class type = mTypes.get(variable); if (type != null) { return type; } - } else if (expression instanceof MethodInvocation) { - MethodInvocation method = (MethodInvocation) expression; - String methodName = method.astName().astValue(); - if (methodName.equals(GET_STRING_METHOD)) { + } else if (expression instanceof UCallExpression) { + UCallExpression method = (UCallExpression)expression; + String methodName = method.getFunctionName(); + if (GET_STRING_METHOD.equals(methodName)) { return String.class; } - } else if (expression instanceof StringLiteral) { - return String.class; - } else if (expression instanceof IntegralLiteral) { - return Integer.TYPE; - } else if (expression instanceof FloatingPointLiteral) { - return Float.TYPE; - } else if (expression instanceof CharLiteral) { - return Character.TYPE; - } else if (expression instanceof BooleanLiteral) { - return Boolean.TYPE; - } else if (expression instanceof NullLiteral) { - return Object.class; + } else if (expression instanceof ULiteralExpression) { + Object value = ((ULiteralExpression)expression).getValue(); + if (value == null) { + return Object.class; + } else if (value instanceof String) { + return String.class; + } else if (value instanceof Integer) { + return Integer.TYPE; + } else if (value instanceof Long) { + return Long.TYPE; + } else if (value instanceof Byte) { + return Byte.TYPE; + } else if (value instanceof Short) { + return Short.TYPE; + } else if (value instanceof Float) { + return Float.TYPE; + } else if (value instanceof Double) { + return Double.TYPE; + } else if (value instanceof Character) { + return Character.TYPE; + } else if (value instanceof Boolean) { + return Boolean.TYPE; + } } - if (mContext != null) { - TypeDescriptor type = mContext.getType(expression); - if (type != null) { - Class typeClass = getTypeClass(type); - if (typeClass != null) { - return typeClass; - } else { - return Object.class; - } + UType type = expression.getExpressionType(); + if (type != null) { + Class typeClass = getTypeClass(type); + if (typeClass != null) { + return typeClass; + } else { + return Object.class; } } diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/SupportAnnotationDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/SupportAnnotationDetector.java index 04eadb3a5e0..68c1bc18e32 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/SupportAnnotationDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/SupportAnnotationDetector.java @@ -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_NAME; @@ -29,75 +29,43 @@ import static com.android.SdkConstants.TYPE_DEF_FLAG_ATTRIBUTE; import static com.android.resources.ResourceType.COLOR; import static com.android.resources.ResourceType.DRAWABLE; import static com.android.resources.ResourceType.MIPMAP; -import static com.android.tools.lint.checks.PermissionRequirement.ATTR_PROTECTION_LEVEL; -import static com.android.tools.lint.checks.PermissionRequirement.VALUE_DANGEROUS; -import static com.android.tools.lint.detector.api.JavaContext.findSurroundingMethod; -import static com.android.tools.lint.detector.api.JavaContext.getParentOfType; import com.android.annotations.NonNull; import com.android.annotations.Nullable; import com.android.resources.ResourceType; -import com.android.tools.lint.checks.PermissionHolder.SetPermissionLookup; -import com.android.tools.lint.client.api.JavaParser.ResolvedAnnotation; -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.TypeDescriptor; -import com.android.tools.lint.client.api.LintClient; -import com.android.tools.lint.detector.api.Category; -import com.android.tools.lint.detector.api.ConstantEvaluator; -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.Project; -import com.android.tools.lint.detector.api.Scope; -import com.android.tools.lint.detector.api.Severity; +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.JavaContext; +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.utils.XmlUtils; import com.google.common.collect.Lists; 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.java.JavaUFunction; +import org.jetbrains.uast.java.JavaUastCallKinds; +import org.jetbrains.uast.kinds.UastOperator; +import org.jetbrains.uast.visitor.UastVisitor; import org.w3c.dom.Document; import org.w3c.dom.Element; +import org.w3c.dom.Node; import org.w3c.dom.NodeList; import java.io.File; import java.util.Collections; import java.util.Iterator; import java.util.List; -import java.util.ListIterator; import java.util.Locale; import java.util.Set; -import lombok.ast.ArrayCreation; -import lombok.ast.ArrayInitializer; -import lombok.ast.AstVisitor; -import lombok.ast.BinaryExpression; -import lombok.ast.BinaryOperator; -import lombok.ast.Catch; -import lombok.ast.Expression; -import lombok.ast.ExpressionStatement; -import lombok.ast.FloatingPointLiteral; -import lombok.ast.ForwardingAstVisitor; -import lombok.ast.InlineIfExpression; -import lombok.ast.IntegralLiteral; -import lombok.ast.MethodDeclaration; -import lombok.ast.MethodInvocation; -import lombok.ast.Node; -import lombok.ast.NullLiteral; -import lombok.ast.Select; -import lombok.ast.Statement; -import lombok.ast.StringLiteral; -import lombok.ast.Try; -import lombok.ast.TypeReference; -import lombok.ast.UnaryExpression; -import lombok.ast.UnaryOperator; -import lombok.ast.VariableDeclaration; -import lombok.ast.VariableDefinition; -import lombok.ast.VariableDefinitionEntry; -import lombok.ast.VariableReference; - /** * Looks up annotations on method calls and enforces the various things they * express, e.g. for {@code @CheckReturn} it makes sure the return value is used, @@ -107,7 +75,7 @@ import lombok.ast.VariableReference; * specifying toInclusive without setting to, combining @ColorInt with any @ResourceTypeRes, * using @CheckResult on a void method, etc. */ -public class SupportAnnotationDetector extends Detector implements Detector.JavaScanner { +public class SupportAnnotationDetector extends Detector implements UastScanner { public static final Implementation IMPLEMENTATION = new Implementation(SupportAnnotationDetector.class, Scope.JAVA_FILE_SCOPE); @@ -271,11 +239,15 @@ public class SupportAnnotationDetector extends Detector implements Detector.Java } private void checkMethodAnnotation( - @NonNull JavaContext context, - @NonNull ResolvedMethod method, - @NonNull MethodInvocation node, - @NonNull ResolvedAnnotation annotation) { - String signature = annotation.getSignature(); + @NonNull UastAndroidContext context, + @NonNull UFunction method, + @NonNull UCallExpression node, + @NonNull UAnnotation annotation) { + String signature = annotation.getFqName(); + if (signature == null) { + return; + } + if (CHECK_RESULT_ANNOTATION.equals(signature) || signature.endsWith(".CheckReturnValue")) { // support findbugs annotation too checkResult(context, node, annotation); @@ -288,10 +260,13 @@ public class SupportAnnotationDetector extends Detector implements Detector.Java } private static void checkParameterAnnotation( - @NonNull JavaContext context, - @NonNull Node argument, - @NonNull ResolvedAnnotation annotation) { - String signature = annotation.getSignature(); + @NonNull UastAndroidContext context, + @NonNull UElement argument, + @NonNull UAnnotation annotation) { + String signature = annotation.getFqName(); + if (signature == null) { + return; + } if (COLOR_INT_ANNOTATION.equals(signature)) { checkColor(context, argument); @@ -325,11 +300,13 @@ public class SupportAnnotationDetector extends Detector implements Detector.Java } } - private static void checkColor(@NonNull JavaContext context, @NonNull Node argument) { - if (argument instanceof InlineIfExpression) { - InlineIfExpression expression = (InlineIfExpression) argument; - checkColor(context, expression.astIfTrue()); - checkColor(context, expression.astIfFalse()); + private static void checkColor(@NonNull UastAndroidContext context, @NonNull UElement argument) { + if (argument instanceof UIfExpression) { + UIfExpression expression = (UIfExpression) argument; + if (expression.isTernary()) { + checkColor(context, expression.getThenBranch()); + checkColor(context, expression.getElseBranch()); + } return; } @@ -344,43 +321,45 @@ public class SupportAnnotationDetector extends Detector implements Detector.Java } private void checkPermission( - @NonNull JavaContext context, - @NonNull MethodInvocation node, - @NonNull ResolvedMethod method, - @NonNull ResolvedAnnotation annotation) { + @NonNull UastAndroidContext context, + @NonNull UCallExpression node, + @NonNull UFunction method, + @NonNull UAnnotation annotation) { PermissionRequirement requirement = PermissionRequirement.create(context, annotation); if (requirement.isConditional()) { return; } - PermissionHolder permissions = getPermissions(context); + PermissionHolder permissions = getPermissions(context.getLintContext()); if (!requirement.isSatisfied(permissions)) { // See if it looks like we're holding the permission implicitly by @RequirePermission // annotations in the surrounding context permissions = addLocalPermissions(context, permissions, node); if (!requirement.isSatisfied(permissions)) { - String name = method.getContainingClass().getSimpleName() + "." + method.getName(); - String message = getMissingPermissionMessage(requirement, name, permissions); - context.report(MISSING_PERMISSION, node, context.getLocation(node), message); + UClass containingClass = UastUtils.getContainingClass(method); + if (containingClass != null) { + String name = containingClass.getName() + "." + method.getName(); + String message = getMissingPermissionMessage(requirement, name, permissions); + context.report(MISSING_PERMISSION, node, context.getLocation(node), message); + } } } else if (requirement.isRevocable(permissions) && - context.getMainProject().getTargetSdkVersion().getFeatureLevel() >= 23) { + context.getLintContext().getMainProject().getTargetSdkVersion().getFeatureLevel() >= 23) { // Ensure that the caller is handling a security exception // First check to see if we're inside a try/catch which catches a SecurityException // (or some wider exception than that). Check for nested try/catches too. boolean handlesMissingPermission = false; - Node parent = node; + UElement parent = node; while (true) { - Try tryCatch = getParentOfType(parent, Try.class); + UTryExpression tryCatch = UastUtils.getParentOfType(parent, UTryExpression.class); if (tryCatch == null) { break; } else { - for (Catch aCatch : tryCatch.astCatches()) { - TypeReference catchType = aCatch.astExceptionDeclaration(). - astTypeReference(); - if (isSecurityException(context, - catchType)) { - handlesMissingPermission = true; - break; + for (UCatchClause aCatch : tryCatch.getCatchClauses()) { + for (UType catchType : aCatch.getTypes()) { + if (isSecurityException(catchType)) { + handlesMissingPermission = true; + break; + } } } parent = tryCatch; @@ -390,10 +369,11 @@ public class SupportAnnotationDetector extends Detector implements Detector.Java // If not, check to see if the method itself declares that it throws a // SecurityException or something wider. if (!handlesMissingPermission) { - MethodDeclaration declaration = getParentOfType(parent, MethodDeclaration.class); - if (declaration != null) { - for (TypeReference typeReference : declaration.astThrownTypeReferences()) { - if (isSecurityException(context, typeReference)) { + UFunction declaration = UastUtils.getParentOfType(parent, UFunction.class); + if (declaration instanceof JavaUFunction) { + List thrownExceptions = ((JavaUFunction)declaration).getThrownExceptions(); + for (UType typeReference : thrownExceptions) { + if (isSecurityException(typeReference)) { handlesMissingPermission = true; break; } @@ -404,10 +384,10 @@ public class SupportAnnotationDetector extends Detector implements Detector.Java // If not, check to see if the code is deliberately checking to see if the // given permission is available. if (!handlesMissingPermission) { - Node methodNode = JavaContext.findSurroundingMethod(node); + UFunction methodNode = UastUtils.getContainingFunction(node); if (methodNode != null) { CheckPermissionVisitor visitor = new CheckPermissionVisitor(node); - methodNode.accept(visitor); + visitor.process(methodNode); handlesMissingPermission = visitor.checksPermission(); } } @@ -421,34 +401,29 @@ public class SupportAnnotationDetector extends Detector implements Detector.Java @NonNull private static PermissionHolder addLocalPermissions( - @NonNull JavaContext context, + @NonNull UastAndroidContext context, @NonNull PermissionHolder permissions, - @NonNull Node node) { + @NonNull UElement node) { // Accumulate @RequirePermissions available in the local context - Node methodNode = JavaContext.findSurroundingMethod(node); - if (methodNode == null) { + UFunction method = UastUtils.getContainingFunction(node); + if (method == null) { return permissions; } - ResolvedNode resolved = context.resolve(methodNode); - if (!(resolved instanceof ResolvedMethod)) { - return permissions; - } - ResolvedMethod method = (ResolvedMethod) resolved; - ResolvedAnnotation annotation = method.getAnnotation(PERMISSION_ANNOTATION); + UAnnotation annotation = UastUtils.findAnnotation(method, PERMISSION_ANNOTATION); permissions = mergeAnnotationPermissions(context, permissions, annotation); - annotation = method.getContainingClass().getAnnotation(PERMISSION_ANNOTATION); + annotation = UastUtils.findAnnotation(UastUtils.getContainingClassOrEmpty(method), PERMISSION_ANNOTATION); permissions = mergeAnnotationPermissions(context, permissions, annotation); return permissions; } @NonNull private static PermissionHolder mergeAnnotationPermissions( - @NonNull JavaContext context, + @NonNull UastAndroidContext context, @NonNull PermissionHolder permissions, - @Nullable ResolvedAnnotation annotation) { + @Nullable UAnnotation annotation) { if (annotation != null) { PermissionRequirement requirement = PermissionRequirement.create(context, annotation); - permissions = SetPermissionLookup.join(permissions, requirement); + permissions = PermissionHolder.SetPermissionLookup.join(permissions, requirement); } return permissions; @@ -480,33 +455,39 @@ public class SupportAnnotationDetector extends Detector implements Detector.Java * or whether the check return value (== PERMISSION_GRANTED vs != PERMISSION_GRANTED) * is handled correctly, etc. */ - private static class CheckPermissionVisitor extends ForwardingAstVisitor { + private static class CheckPermissionVisitor extends UastVisitor { private boolean mChecksPermission; private boolean mDone; - private final Node mTarget; + private final UElement mTarget; - public CheckPermissionVisitor(@NonNull Node target) { + public CheckPermissionVisitor(@NonNull UElement target) { mTarget = target; } @Override - public boolean visitNode(Node node) { - return mDone; + public void process(@NotNull UElement element) { + if (!mDone) { + super.process(element); + } } @Override - public boolean visitMethodInvocation(MethodInvocation node) { - if (node == mTarget) { - mDone = true; + public boolean visitCallExpression(@NotNull UCallExpression node) { + if (node.getKind() == UastCallKind.FUNCTION_CALL) { + if (node == mTarget) { + mDone = true; + } + + String name = node.getFunctionName(); + if (name != null && + (name.startsWith("check") || name.startsWith("enforce")) + && name.endsWith("Permission")) { + mChecksPermission = true; + mDone = true; + } } - String name = node.astName().astValue(); - if ((name.startsWith("check") || name.startsWith("enforce")) - && name.endsWith("Permission")) { - mChecksPermission = true; - mDone = true; - } - return super.visitMethodInvocation(node); + return super.visitCallExpression(node); } public boolean checksPermission() { @@ -514,27 +495,25 @@ public class SupportAnnotationDetector extends Detector implements Detector.Java } } - private static boolean isSecurityException( - @NonNull JavaContext context, - @NonNull TypeReference typeReference) { - TypeDescriptor type = context.getType(typeReference); - return type != null && (type.matchesSignature("java.lang.SecurityException") || - type.matchesSignature("java.lang.RuntimeException") || - type.matchesSignature("java.lang.Exception") || - type.matchesSignature("java.lang.Throwable")); + private static boolean isSecurityException(@NonNull UType type) { + return type != null && (type.matchesFqName("java.lang.SecurityException") || + type.matchesFqName("java.lang.RuntimeException") || + type.matchesFqName("java.lang.Exception") || + type.matchesFqName("java.lang.Throwable")); } private PermissionHolder mPermissions; private PermissionHolder getPermissions( - @NonNull JavaContext context) { + @NonNull UastAndroidContext context) { if (mPermissions == null) { Set permissions = Sets.newHashSetWithExpectedSize(30); Set revocable = Sets.newHashSetWithExpectedSize(4); - LintClient client = context.getClient(); + JavaContext lintContext = context.getLintContext(); + LintClient client = lintContext.getClient(); // Gather permissions from all projects that contribute to the // main project. - Project mainProject = context.getMainProject(); + Project mainProject = lintContext.getMainProject(); for (File manifest : mainProject.getManifestFiles()) { addPermissions(client, permissions, revocable, manifest); } @@ -544,7 +523,7 @@ public class SupportAnnotationDetector extends Detector implements Detector.Java } } - mPermissions = new SetPermissionLookup(permissions, revocable); + mPermissions = new PermissionHolder.SetPermissionLookup(permissions, revocable); } return mPermissions; @@ -564,8 +543,8 @@ public class SupportAnnotationDetector extends Detector implements Detector.Java } NodeList children = root.getChildNodes(); for (int i = 0, n = children.getLength(); i < n; i++) { - org.w3c.dom.Node item = children.item(i); - if (item.getNodeType() != org.w3c.dom.Node.ELEMENT_NODE) { + Node item = children.item(i); + if (item.getNodeType() != Node.ELEMENT_NODE) { continue; } String nodeName = item.getNodeName(); @@ -578,8 +557,8 @@ public class SupportAnnotationDetector extends Detector implements Detector.Java } else if (nodeName.equals(TAG_PERMISSION)) { Element element = (Element)item; String protectionLevel = element.getAttributeNS(ANDROID_URI, - ATTR_PROTECTION_LEVEL); - if (VALUE_DANGEROUS.equals(protectionLevel)) { + PermissionRequirement.ATTR_PROTECTION_LEVEL); + if (PermissionRequirement.VALUE_DANGEROUS.equals(protectionLevel)) { String name = element.getAttributeNS(ANDROID_URI, ATTR_NAME); if (!name.isEmpty()) { revocable.add(name); @@ -589,10 +568,13 @@ public class SupportAnnotationDetector extends Detector implements Detector.Java } } - private static void checkResult(@NonNull JavaContext context, @NonNull MethodInvocation node, - @NonNull ResolvedAnnotation annotation) { - if (node.getParent() instanceof ExpressionStatement) { - String methodName = node.astName().astValue(); + private static void checkResult( + @NonNull UastAndroidContext context, + @NonNull UCallExpression node, + @NonNull UAnnotation annotation) { + if (node.getParent() instanceof UExpression) { + String methodName = node.getFunctionName(); + assert methodName != null; Object suggested = annotation.getValue(ATTR_SUGGEST); // Failing to check permissions is a potential security issue (and had an existing @@ -618,9 +600,9 @@ public class SupportAnnotationDetector extends Detector implements Detector.Java } private static void checkThreading( - @NonNull JavaContext context, - @NonNull MethodInvocation node, - @NonNull ResolvedMethod method, + @NonNull UastAndroidContext context, + @NonNull UCallExpression node, + @NonNull UFunction method, @NonNull String annotation) { String threadContext = getThreadContext(context, node); if (threadContext != null && !isCompatibleThread(threadContext, annotation)) { @@ -670,37 +652,40 @@ public class SupportAnnotationDetector extends Detector implements Detector.Java /** Attempts to infer the current thread context at the site of the given method call */ @Nullable - private static String getThreadContext(@NonNull JavaContext context, - @NonNull MethodInvocation methodCall) { - Node node = findSurroundingMethod(methodCall); - if (node != null) { - ResolvedNode resolved = context.resolve(node); - if (resolved instanceof ResolvedMethod) { - ResolvedMethod method = (ResolvedMethod) resolved; - ResolvedClass cls = method.getContainingClass(); + private static String getThreadContext(@NonNull UastAndroidContext context, + @NonNull UCallExpression methodCall) { + UFunction method = UastUtils.getContainingFunction(methodCall); + if (method != null) { + UClass cls = UastUtils.getContainingClass(method); - while (method != null) { - for (ResolvedAnnotation annotation : method.getAnnotations()) { - String name = annotation.getSignature(); - if (name.startsWith(SUPPORT_ANNOTATIONS_PREFIX) - && name.endsWith(THREAD_SUFFIX)) { - return name; - } + while (method != null) { + for (UAnnotation annotation : method.getAnnotations()) { + String name = annotation.getFqName(); + if (name != null + && name.startsWith(SUPPORT_ANNOTATIONS_PREFIX) + && name.endsWith(THREAD_SUFFIX)) { + return name; } - method = method.getSuperMethod(); } + List superFunctions = method.getSuperFunctions(context); + if (superFunctions.isEmpty()) { + method = null; + } else { + method = superFunctions.get(0); + } + } - // See if we're extending a class with a known threading context - while (cls != null) { - for (ResolvedAnnotation annotation : cls.getAnnotations()) { - String name = annotation.getSignature(); - if (name.startsWith(SUPPORT_ANNOTATIONS_PREFIX) - && name.endsWith(THREAD_SUFFIX)) { - return name; - } + // See if we're extending a class with a known threading context + while (cls != null) { + for (UAnnotation annotation : cls.getAnnotations()) { + String name = annotation.getFqName(); + if (name != null + && name.startsWith(SUPPORT_ANNOTATIONS_PREFIX) + && name.endsWith(THREAD_SUFFIX)) { + return name; } - cls = cls.getSuperClass(); } + cls = cls.getSuperClass(context); } } @@ -713,28 +698,28 @@ public class SupportAnnotationDetector extends Detector implements Detector.Java return null; } - private static boolean isNumber(@NonNull Node argument) { - return argument instanceof IntegralLiteral || argument instanceof UnaryExpression - && ((UnaryExpression) argument).astOperator() == UnaryOperator.UNARY_MINUS - && ((UnaryExpression) argument).astOperand() instanceof IntegralLiteral; + private static boolean isNumber(@NonNull UElement argument) { + return UastLiteralUtils.isIntegralLiteral(argument) || argument instanceof UPrefixExpression + && ((UPrefixExpression) argument).getOperator() == UastPrefixOperator.UNARY_MINUS + && UastLiteralUtils.isIntegralLiteral(((UPrefixExpression) argument).getOperand()); } - private static boolean isZero(@NonNull Node argument) { - return argument instanceof IntegralLiteral - && ((IntegralLiteral) argument).astIntValue() == 0; + private static boolean isZero(@NonNull UElement argument) { + return UastLiteralUtils.isIntegralLiteral(argument) + && UastLiteralUtils.getLongValue((ULiteralExpression) argument) == 0; } - private static boolean isMinusOne(@NonNull Node argument) { - return argument instanceof UnaryExpression - && ((UnaryExpression) argument).astOperator() == UnaryOperator.UNARY_MINUS - && ((UnaryExpression) argument).astOperand() instanceof IntegralLiteral - && ((IntegralLiteral) ((UnaryExpression) argument).astOperand()).astIntValue() - == 1; + private static boolean isMinusOne(@NonNull UElement argument) { + return argument instanceof UUnaryExpression + && ((UUnaryExpression) argument).getOperator() == UastPrefixOperator.UNARY_MINUS + && UastLiteralUtils.isIntegralLiteral(((UUnaryExpression) argument).getOperand()) + && UastLiteralUtils.getLongValue((ULiteralExpression) ((UUnaryExpression) argument).getOperand()) + == 1; } private static void checkResourceType( - @NonNull JavaContext context, - @NonNull Node argument, + @NonNull UastAndroidContext context, + @NonNull UElement argument, @Nullable ResourceType expectedType) { List actual = getResourceTypes(context, argument); if (actual == null && (!isNumber(argument) || isZero(argument) || isMinusOne(argument)) ) { @@ -761,24 +746,24 @@ public class SupportAnnotationDetector extends Detector implements Detector.Java } @Nullable - private static List getResourceTypes(@NonNull JavaContext context, - @NonNull Node argument) { - if (argument instanceof Select) { - Select node = (Select) argument; - if (node.astOperand() instanceof Select) { - Select select = (Select) node.astOperand(); - if (select.astOperand() instanceof Select) { // android.R.... - Select innerSelect = (Select) select.astOperand(); - if (innerSelect.astIdentifier().astValue().equals(R_CLASS)) { - String typeName = select.astIdentifier().astValue(); + private static List getResourceTypes(@NonNull UastAndroidContext context, + @NonNull UElement argument) { + if (argument instanceof UQualifiedExpression) { + UQualifiedExpression node = (UQualifiedExpression) argument; + if (node.getReceiver() instanceof UQualifiedExpression) { + UQualifiedExpression select = (UQualifiedExpression) node.getReceiver(); + if (select.getReceiver() instanceof UQualifiedExpression) { // android.R.... + UQualifiedExpression innerSelect = (UQualifiedExpression) select.getReceiver(); + if (innerSelect.getSelector().renderString().equals(R_CLASS)) { + String typeName = select.getSelector().renderString(); ResourceType type = ResourceType.getEnum(typeName); return type != null ? Collections.singletonList(type) : null; } } - if (select.astOperand() instanceof VariableReference) { - VariableReference reference = (VariableReference) select.astOperand(); - if (reference.astIdentifier().astValue().equals(R_CLASS)) { - String typeName = select.astIdentifier().astValue(); + if (select.getReceiver() instanceof USimpleReferenceExpression) { + USimpleReferenceExpression reference = (USimpleReferenceExpression) select.getReceiver(); + if (reference.getIdentifier().equals(R_CLASS)) { + String typeName = select.getSelector().renderString(); ResourceType type = ResourceType.getEnum(typeName); return type != null ? Collections.singletonList(type) : null; } @@ -786,71 +771,34 @@ public class SupportAnnotationDetector extends Detector implements Detector.Java } // Arbitrary packages -- android.R.type.name, foo.bar.R.type.name - if (node.astIdentifier().astValue().equals(R_CLASS)) { - Node parent = node.getParent(); - if (parent instanceof Select) { - Node grandParent = parent.getParent(); - if (grandParent instanceof Select) { - Select select = (Select) grandParent; - Expression typeOperand = select.astOperand(); - if (typeOperand instanceof Select) { - Select typeSelect = (Select) typeOperand; - String typeName = typeSelect.astIdentifier().astValue(); + if (node.getSelector().renderString().equals(R_CLASS)) { + UElement parent = node.getParent(); + if (parent instanceof UQualifiedExpression) { + UElement grandParent = parent.getParent(); + if (grandParent instanceof UQualifiedExpression) { + UQualifiedExpression select = (UQualifiedExpression) grandParent; + UExpression typeOperand = select.getReceiver(); + if (typeOperand instanceof UQualifiedExpression) { + UQualifiedExpression typeSelect = (UQualifiedExpression) typeOperand; + String typeName = typeSelect.getSelector().renderString(); ResourceType type = ResourceType.getEnum(typeName); return type != null ? Collections.singletonList(type) : null; } } } } - } else if (argument instanceof VariableReference) { - Statement statement = getParentOfType(argument, Statement.class, false); - if (statement != null) { - ListIterator iterator = statement.getParent().getChildren().listIterator(); - while (iterator.hasNext()) { - if (iterator.next() == statement) { - if (iterator.hasPrevious()) { // should always be true - iterator.previous(); - } - break; - } - } - - String targetName = ((VariableReference)argument).astIdentifier().astValue(); - while (iterator.hasPrevious()) { - Node previous = iterator.previous(); - if (previous instanceof VariableDeclaration) { - VariableDeclaration declaration = (VariableDeclaration) previous; - VariableDefinition definition = declaration.astDefinition(); - for (VariableDefinitionEntry entry : definition - .astVariables()) { - if (entry.astInitializer() != null - && entry.astName().astValue().equals(targetName)) { - return getResourceTypes(context, entry.astInitializer()); - } - } - } else if (previous instanceof ExpressionStatement) { - ExpressionStatement expressionStatement = (ExpressionStatement) previous; - Expression expression = expressionStatement.astExpression(); - if (expression instanceof BinaryExpression && - ((BinaryExpression) expression).astOperator() - == BinaryOperator.ASSIGN) { - BinaryExpression binaryExpression = (BinaryExpression) expression; - if (targetName.equals(binaryExpression.astLeft().toString())) { - return getResourceTypes(context, binaryExpression.astRight()); - } - } - } - } - } - } else if (argument instanceof MethodInvocation) { - ResolvedNode resolved = context.resolve(argument); - if (resolved != null) { - for (ResolvedAnnotation annotation : resolved.getAnnotations()) { - String signature = annotation.getSignature(); - if (signature.equals(COLOR_INT_ANNOTATION)) { + } else if (argument instanceof UCallExpression) { + UDeclaration resolved = ((UCallExpression)argument).resolve(context); + //noinspection ConstantConditions + if (resolved instanceof UAnnotated) { + List annotations = ((UAnnotated) resolved).getAnnotations(); + for (UAnnotation annotation : annotations) { + String signature = annotation.getFqName(); + if (COLOR_INT_ANNOTATION.equals(signature)) { return Collections.singletonList(COLOR_INT_MARKER_TYPE); } - if (signature.endsWith(RES_SUFFIX) + if (signature != null + && signature.endsWith(RES_SUFFIX) && signature.startsWith(SUPPORT_ANNOTATIONS_PREFIX)) { String typeString = signature.substring(SUPPORT_ANNOTATIONS_PREFIX.length(), signature.length() - RES_SUFFIX.length()).toLowerCase(Locale.US); @@ -878,10 +826,13 @@ public class SupportAnnotationDetector extends Detector implements Detector.Java } private static void checkIntRange( - @NonNull JavaContext context, - @NonNull ResolvedAnnotation annotation, - @NonNull Node argument) { - Object object = ConstantEvaluator.evaluate(context, argument); + @NonNull UastAndroidContext context, + @NonNull UAnnotation annotation, + @NonNull UElement argument) { + Object object = null; + if (argument instanceof UExpression) { + object = ((UExpression) argument).evaluate(); + } if (!(object instanceof Number)) { return; } @@ -918,10 +869,13 @@ public class SupportAnnotationDetector extends Detector implements Detector.Java } private static void checkFloatRange( - @NonNull JavaContext context, - @NonNull ResolvedAnnotation annotation, - @NonNull Node argument) { - Object object = ConstantEvaluator.evaluate(context, argument); + @NonNull UastAndroidContext context, + @NonNull UAnnotation annotation, + @NonNull UElement argument) { + Object object = null; + if (argument instanceof UExpression) { + object = ((UExpression) argument).evaluate(); + } if (!(object instanceof Number)) { return; } @@ -943,7 +897,7 @@ public class SupportAnnotationDetector extends Detector implements Detector.Java */ @Nullable private static String getFloatRangeError(double value, double from, double to, - boolean fromInclusive, boolean toInclusive, @NonNull Node node) { + boolean fromInclusive, boolean toInclusive, @NonNull UElement node) { if (!((fromInclusive && value >= from || !fromInclusive && value > from) && (toInclusive && value <= to || !toInclusive && value < to))) { StringBuilder sb = new StringBuilder(20); @@ -990,15 +944,13 @@ public class SupportAnnotationDetector extends Detector implements Detector.Java sb.append(Double.toString(to)); } sb.append(" (was "); - if (node instanceof FloatingPointLiteral || node instanceof IntegralLiteral) { + if (UastLiteralUtils.isNumberLiteral(node)) { // Use source text instead to avoid rounding errors involved in conversion, e.g // Error: Value must be > 2.5 (was 2.490000009536743) [Range] // printAtLeastExclusive(2.49f); // ERROR // ~~~~~ - String str = node.toString(); - if (str.endsWith("f") || str.endsWith("F")) { - str = str.substring(0, str.length() - 1); - } + //noinspection ConstantConditions + String str = ((ULiteralExpression)node).getValue().toString(); sb.append(str); } else { sb.append(value); @@ -1010,22 +962,20 @@ public class SupportAnnotationDetector extends Detector implements Detector.Java } private static void checkSize( - @NonNull JavaContext context, - @NonNull ResolvedAnnotation annotation, - @NonNull Node argument) { + @NonNull UastAndroidContext context, + @NonNull UAnnotation annotation, + @NonNull UElement argument) { int actual; - if (argument instanceof StringLiteral) { + if (UastLiteralUtils.isStringLiteral(argument)) { // Check string length - StringLiteral literal = (StringLiteral) argument; - String s = literal.astValue(); + ULiteralExpression literal = (ULiteralExpression) argument; + String s = (String) literal.getValue(); + assert s != null; actual = s.length(); - } else if (argument instanceof ArrayCreation) { - ArrayCreation literal = (ArrayCreation) argument; - ArrayInitializer initializer = literal.astInitializer(); - if (initializer == null) { - return; - } - actual = initializer.astExpressions().size(); + } else if (argument instanceof UCallExpression + && ((UCallExpression)argument).getKind() == JavaUastCallKinds.ARRAY_INITIALIZER) { + UCallExpression initializer = (UCallExpression) argument; + actual = initializer.getValueArgumentCount(); } else { // TODO: Collections syntax, e.g. Arrays.asList => param count, emptyList=0, singleton=1, etc // TODO: Flow analysis @@ -1038,7 +988,7 @@ public class SupportAnnotationDetector extends Detector implements Detector.Java long multiple = getLongAttribute(annotation, ATTR_MULTIPLE, 1); String unit; - boolean isString = argument instanceof StringLiteral; + boolean isString = argument instanceof ULiteralExpression; if (isString) { unit = "length"; } else { @@ -1084,145 +1034,93 @@ public class SupportAnnotationDetector extends Detector implements Detector.Java } private static void checkTypeDefConstant( - @NonNull JavaContext context, - @NonNull ResolvedAnnotation annotation, - @NonNull Node argument, - @Nullable Node errorNode, + @NonNull UastAndroidContext context, + @NonNull UAnnotation annotation, + @NonNull UElement argument, + @Nullable UElement errorNode, boolean flag) { - if (argument instanceof NullLiteral) { + if (UastLiteralUtils.isNullLiteral(argument)) { // Accepted for @StringDef return; } - if (argument instanceof StringLiteral) { - StringLiteral string = (StringLiteral) argument; - checkTypeDefConstant(context, annotation, argument, errorNode, false, string.astValue()); - } else if (argument instanceof IntegralLiteral) { - IntegralLiteral literal = (IntegralLiteral) argument; - int value = literal.astIntValue(); + if (UastLiteralUtils.isStringLiteral(argument)) { + ULiteralExpression string = (ULiteralExpression) argument; + checkTypeDefConstant(context, annotation, argument, errorNode, false, string.getValue()); + } else if (UastLiteralUtils.isIntegralLiteral(argument)) { + ULiteralExpression literal = (ULiteralExpression) argument; + long value = UastLiteralUtils.getLongValue(literal); if (flag && value == 0) { // Accepted for a flag @IntDef return; } - checkTypeDefConstant(context, annotation, argument, errorNode, flag, value); + checkTypeDefConstant(context, annotation, argument, errorNode, flag, (int) value); } else if (isMinusOne(argument)) { // -1 is accepted unconditionally for flags if (!flag) { reportTypeDef(context, annotation, argument, errorNode); } - } else if (argument instanceof InlineIfExpression) { - InlineIfExpression expression = (InlineIfExpression) argument; - if (expression.astIfTrue() != null) { - checkTypeDefConstant(context, annotation, expression.astIfTrue(), errorNode, flag); + } else if (argument instanceof UIfExpression && ((UIfExpression) argument).isTernary()) { + UIfExpression expression = (UIfExpression) argument; + if (expression.getThenBranch() != null) { + checkTypeDefConstant(context, annotation, expression.getThenBranch(), errorNode, flag); } - if (expression.astIfFalse() != null) { - checkTypeDefConstant(context, annotation, expression.astIfFalse(), errorNode, flag); + if (expression.getElseBranch() != null) { + checkTypeDefConstant(context, annotation, expression.getElseBranch(), errorNode, flag); } - } else if (argument instanceof UnaryExpression) { - UnaryExpression expression = (UnaryExpression) argument; - UnaryOperator operator = expression.astOperator(); + } else if (argument instanceof UUnaryExpression) { + UUnaryExpression expression = (UUnaryExpression) argument; + UastOperator operator = expression.getOperator(); if (flag) { - checkTypeDefConstant(context, annotation, expression.astOperand(), errorNode, true); - } else if (operator == UnaryOperator.BINARY_NOT) { + checkTypeDefConstant(context, annotation, expression.getOperand(), errorNode, true); + } else if (operator == UastPrefixOperator.BITWISE_NOT) { context.report(TYPE_DEF, expression, context.getLocation(expression), "Flag not allowed here"); } - } else if (argument instanceof BinaryExpression) { + } else if (argument instanceof UBinaryExpression) { // If it's ?: then check both the if and else clauses - BinaryExpression expression = (BinaryExpression) argument; + UBinaryExpression expression = (UBinaryExpression) argument; if (flag) { - checkTypeDefConstant(context, annotation, expression.astLeft(), errorNode, true); - checkTypeDefConstant(context, annotation, expression.astRight(), errorNode, true); + checkTypeDefConstant(context, annotation, expression.getLeftOperand(), errorNode, true); + checkTypeDefConstant(context, annotation, expression.getRightOperand(), errorNode, true); } else { - BinaryOperator operator = expression.astOperator(); - if (operator == BinaryOperator.BITWISE_AND - || operator == BinaryOperator.BITWISE_OR - || operator == BinaryOperator.BITWISE_XOR) { + UastOperator operator = expression.getOperator(); + if (operator == UastBinaryOperator.BITWISE_AND + || operator == UastBinaryOperator.BITWISE_OR + || operator == UastBinaryOperator.BITWISE_XOR) { context.report(TYPE_DEF, expression, context.getLocation(expression), "Flag not allowed here"); } } - } else { - ResolvedNode resolved = context.resolve(argument); - if (resolved instanceof ResolvedField) { + } else if (argument instanceof UResolvable) { + UDeclaration resolved = ((UResolvable)argument).resolve(context); + if (resolved instanceof UVariable) { checkTypeDefConstant(context, annotation, argument, errorNode, flag, resolved); - } else if (argument instanceof VariableReference) { - Statement statement = getParentOfType(argument, Statement.class, false); - if (statement != null) { - ListIterator iterator = statement.getParent().getChildren().listIterator(); - while (iterator.hasNext()) { - if (iterator.next() == statement) { - if (iterator.hasPrevious()) { // should always be true - iterator.previous(); - } - break; - } - } - - String targetName = ((VariableReference)argument).astIdentifier().astValue(); - while (iterator.hasPrevious()) { - Node previous = iterator.previous(); - if (previous instanceof VariableDeclaration) { - VariableDeclaration declaration = (VariableDeclaration) previous; - VariableDefinition definition = declaration.astDefinition(); - for (VariableDefinitionEntry entry : definition - .astVariables()) { - if (entry.astInitializer() != null - && entry.astName().astValue().equals(targetName)) { - checkTypeDefConstant(context, annotation, - entry.astInitializer(), - errorNode != null ? errorNode : argument, flag); - return; - } - } - } else if (previous instanceof ExpressionStatement) { - ExpressionStatement expressionStatement = (ExpressionStatement) previous; - Expression expression = expressionStatement.astExpression(); - if (expression instanceof BinaryExpression && - ((BinaryExpression) expression).astOperator() - == BinaryOperator.ASSIGN) { - BinaryExpression binaryExpression = (BinaryExpression) expression; - if (targetName.equals(binaryExpression.astLeft().toString())) { - checkTypeDefConstant(context, annotation, - binaryExpression.astRight(), - errorNode != null ? errorNode : argument, flag); - return; - } - } - } - } - } } } } - private static void checkTypeDefConstant(@NonNull JavaContext context, - @NonNull ResolvedAnnotation annotation, @NonNull Node argument, - @Nullable Node errorNode, boolean flag, Object value) { - Object allowed = annotation.getValue(); - if (allowed instanceof Object[]) { - Object[] allowedValues = (Object[]) allowed; - for (Object o : allowedValues) { - if (o.equals(value)) { - return; - } + private static void checkTypeDefConstant(@NonNull UastAndroidContext context, + @NonNull UAnnotation annotation, @NonNull UElement argument, + @Nullable UElement errorNode, boolean flag, Object value) { + List valueArguments = annotation.getValues(); + for (Object o : valueArguments) { + if (o.equals(value)) { + return; } - reportTypeDef(context, argument, errorNode, flag, allowedValues); } + reportTypeDef(context, argument, errorNode, flag, valueArguments); } - private static void reportTypeDef(@NonNull JavaContext context, - @NonNull ResolvedAnnotation annotation, @NonNull Node argument, - @Nullable Node errorNode) { - Object allowed = annotation.getValue(); - if (allowed instanceof Object[]) { - Object[] allowedValues = (Object[]) allowed; - reportTypeDef(context, argument, errorNode, false, allowedValues); - } + private static void reportTypeDef(@NonNull UastAndroidContext context, + @NonNull UAnnotation annotation, @NonNull UElement argument, + @Nullable UElement errorNode) { + List allowed = annotation.getValues(); + reportTypeDef(context, argument, errorNode, false, allowed); } - private static void reportTypeDef(@NonNull JavaContext context, @NonNull Node node, - @Nullable Node errorNode, boolean flag, @NonNull Object[] allowedValues) { + private static void reportTypeDef(@NonNull UastAndroidContext context, @NonNull UElement node, + @Nullable UElement errorNode, boolean flag, @NonNull List allowedValues) { String values = listAllowedValues(allowedValues); String message; if (flag) { @@ -1236,21 +1134,27 @@ public class SupportAnnotationDetector extends Detector implements Detector.Java context.report(TYPE_DEF, errorNode, context.getLocation(errorNode), message); } - private static String listAllowedValues(@NonNull Object[] allowedValues) { + private static String listAllowedValues(@NonNull List allowedValues) { StringBuilder sb = new StringBuilder(); for (Object allowedValue : allowedValues) { String s; if (allowedValue instanceof Integer) { s = allowedValue.toString(); - } else if (allowedValue instanceof ResolvedNode) { - ResolvedNode node = (ResolvedNode) allowedValue; - if (node instanceof ResolvedField) { - ResolvedField field = (ResolvedField) node; - String containingClassName = field.getContainingClassName(); - containingClassName = containingClassName.substring(containingClassName.lastIndexOf('.') + 1); - s = containingClassName + "." + field.getName(); + } else if (allowedValue instanceof UVariable) { + UVariable variable = (UVariable) allowedValue; + UClass containingClass = UastUtils.getContainingClassOrEmpty(variable); + String containingClassName = containingClass.getFqName(); + if (containingClassName == null) { + continue; + } + containingClassName = containingClassName.substring(containingClassName.lastIndexOf('.') + 1); + s = containingClassName + "." + variable.getName(); + } else if (allowedValue instanceof UFqNamed) { + String fqName = ((UFqNamed)allowedValue).getFqName(); + if (fqName != null) { + s = fqName; } else { - s = node.getSignature(); + continue; } } else { continue; @@ -1263,7 +1167,7 @@ public class SupportAnnotationDetector extends Detector implements Detector.Java return sb.toString(); } - private static double getDoubleAttribute(@NonNull ResolvedAnnotation annotation, + private static double getDoubleAttribute(@NonNull UAnnotation annotation, @NonNull String name, double defaultValue) { Object value = annotation.getValue(name); if (value instanceof Number) { @@ -1273,7 +1177,7 @@ public class SupportAnnotationDetector extends Detector implements Detector.Java return defaultValue; } - private static long getLongAttribute(@NonNull ResolvedAnnotation annotation, + private static long getLongAttribute(@NonNull UAnnotation annotation, @NonNull String name, long defaultValue) { Object value = annotation.getValue(name); if (value instanceof Number) { @@ -1283,7 +1187,7 @@ public class SupportAnnotationDetector extends Detector implements Detector.Java return defaultValue; } - private static boolean getBoolean(@NonNull ResolvedAnnotation annotation, + private static boolean getBoolean(@NonNull UAnnotation annotation, @NonNull String name, boolean defaultValue) { Object value = annotation.getValue(name); if (value instanceof Boolean) { @@ -1294,8 +1198,15 @@ public class SupportAnnotationDetector extends Detector implements Detector.Java } @Nullable - static ResolvedAnnotation getRelevantAnnotation(@NonNull ResolvedAnnotation annotation) { - String signature = annotation.getSignature(); + static UAnnotation getRelevantAnnotation( + @NonNull UAnnotation annotation, + @NonNull UastAndroidContext context + ) { + String signature = annotation.getFqName(); + if (signature == null) { + return null; + } + if (signature.startsWith(SUPPORT_ANNOTATIONS_PREFIX)) { // Bail on the nullness annotations early since they're the most commonly // defined ones. They're not analyzed in lint yet. @@ -1317,12 +1228,12 @@ public class SupportAnnotationDetector extends Detector implements Detector.Java // annotate it with @IntDef, and then use @foo.bar.Baz in your signatures. // Here we want to map from @foo.bar.Baz to the corresponding int def. // Don't need to compute this if performing @IntDef or @StringDef lookup - ResolvedClass type = annotation.getClassType(); + UClass type = annotation.resolve(context); if (type != null) { - for (ResolvedAnnotation inner : type.getAnnotations()) { - if (inner.matches(INT_DEF_ANNOTATION) - || inner.matches(STRING_DEF_ANNOTATION) - || inner.matches(PERMISSION_ANNOTATION)) { + for (UAnnotation inner : type.getAnnotations()) { + if (inner.matchesFqName(INT_DEF_ANNOTATION) + || inner.matchesFqName(STRING_DEF_ANNOTATION) + || inner.matchesFqName(PERMISSION_ANNOTATION)) { return inner; } } @@ -1331,35 +1242,76 @@ public class SupportAnnotationDetector extends Detector implements Detector.Java return null; } - // ---- Implements JavaScanner ---- + @Nullable + static UAnnotation getRelevantAnnotation( + @NonNull UastAndroidContext context, + @NonNull UAnnotation annotation + ) { + String signature = annotation.getFqName(); + if (signature != null && signature.startsWith(SUPPORT_ANNOTATIONS_PREFIX)) { + // Bail on the nullness annotations early since they're the most commonly + // defined ones. They're not analyzed in lint yet. + if (signature.endsWith(".Nullable") || signature.endsWith(".NonNull")) { + return null; + } - @Override - public - List> getApplicableNodeTypes() { - return Collections.>singletonList(MethodInvocation.class); + + return annotation; + } + + if (signature != null && signature.startsWith("java.")) { + // @Override, @SuppressWarnings etc. Ignore + return null; + } + + // Special case @IntDef and @StringDef: These are used on annotations + // themselves. For example, you create a new annotation named @foo.bar.Baz, + // annotate it with @IntDef, and then use @foo.bar.Baz in your signatures. + // Here we want to map from @foo.bar.Baz to the corresponding int def. + // Don't need to compute this if performing @IntDef or @StringDef lookup + UClass type = annotation.resolve(context); + if (type != null) { + for (UAnnotation inner : type.getAnnotations()) { + if (INT_DEF_ANNOTATION.equals(inner.getFqName()) + || STRING_DEF_ANNOTATION.equals(inner.getFqName()) + || PERMISSION_ANNOTATION.equals(inner.getFqName())) { + return inner; + } + } + } + + return null; } - @Nullable + // ---- Implements UastScanner ---- + @Override - public AstVisitor createJavaVisitor(@NonNull JavaContext context) { + public UastVisitor createUastVisitor(UastAndroidContext context) { return new CallVisitor(context); } - private class CallVisitor extends ForwardingAstVisitor { - private final JavaContext mContext; + private class CallVisitor extends UastVisitor { + private final UastAndroidContext mContext; - public CallVisitor(JavaContext context) { + public CallVisitor(UastAndroidContext context) { mContext = context; } @Override - public boolean visitMethodInvocation(@NonNull MethodInvocation call) { - ResolvedNode resolved = mContext.resolve(call); - if (resolved instanceof ResolvedMethod) { - ResolvedMethod method = (ResolvedMethod) resolved; - Iterable annotations = method.getAnnotations(); - for (ResolvedAnnotation annotation : annotations) { - annotation = getRelevantAnnotation(annotation); + public boolean visitCallExpression(@NotNull UCallExpression node) { + if (node.getKind() == UastCallKind.FUNCTION_CALL) { + visitFunctionInvocation(node); + } + + return super.visitCallExpression(node); + } + + private boolean visitFunctionInvocation(@NonNull UCallExpression call) { + UFunction method = call.resolve(mContext); + if (method != null) { + List annotations = method.getAnnotations(); + for (UAnnotation annotation : annotations) { + annotation = getRelevantAnnotation(annotation, mContext); if (annotation != null) { checkMethodAnnotation(mContext, method, call, annotation); } @@ -1367,24 +1319,26 @@ public class SupportAnnotationDetector extends Detector implements Detector.Java // Look for annotations on the class as well: these trickle // down to all the methods in the class - ResolvedClass containingClass = method.getContainingClass(); - annotations = containingClass.getAnnotations(); - for (ResolvedAnnotation annotation : annotations) { - annotation = getRelevantAnnotation(annotation); - if (annotation != null) { - checkMethodAnnotation(mContext, method, call, annotation); + UClass containingClass = UastUtils.getContainingClass(method); + if (containingClass != null) { + annotations = containingClass.getAnnotations(); + for (UAnnotation annotation : annotations) { + annotation = getRelevantAnnotation(annotation, mContext); + if (annotation != null) { + checkMethodAnnotation(mContext, method, call, annotation); + } } } - Iterator arguments = call.astArguments().iterator(); - for (int i = 0, n = method.getArgumentCount(); + Iterator arguments = call.getValueArguments().iterator(); + for (int i = 0, n = method.getValueParameterCount(); i < n && arguments.hasNext(); i++) { - Expression argument = arguments.next(); + UExpression argument = arguments.next(); - annotations = method.getParameterAnnotations(i); - for (ResolvedAnnotation annotation : annotations) { - annotation = getRelevantAnnotation(annotation); + annotations = method.getValueParameters().get(i).getAnnotations(); + for (UAnnotation annotation : annotations) { + annotation = getRelevantAnnotation(annotation, mContext); if (annotation != null) { checkParameterAnnotation(mContext, argument, annotation); } diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/SystemPermissionsDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/SystemPermissionsDetector.java index 5afdd874721..365eeafa030 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/SystemPermissionsDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/SystemPermissionsDetector.java @@ -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; @@ -22,15 +22,15 @@ import static com.android.SdkConstants.ATTR_NAME; import static com.android.SdkConstants.TAG_USES_PERMISSION; 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; import org.w3c.dom.Element; diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/TextFieldDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/TextFieldDetector.java index 80532118975..6a66b64502f 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/TextFieldDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/TextFieldDetector.java @@ -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_HINT; @@ -31,18 +31,18 @@ import static com.android.SdkConstants.NEW_ID_PREFIX; import com.android.annotations.NonNull; import com.android.annotations.VisibleForTesting; import com.android.ide.common.rendering.api.ResourceValue; -import com.android.tools.lint.client.api.LintClient; -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.Location; -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.Speed; -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.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.Project; +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; diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/TextViewDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/TextViewDetector.java index 84728c1c798..dbb728e715a 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/TextViewDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/TextViewDetector.java @@ -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_AUTO_TEXT; @@ -50,15 +50,15 @@ import static com.android.SdkConstants.VALUE_NONE; 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.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.Attr; import org.w3c.dom.Element; diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/TitleDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/TitleDetector.java index a8d6a5e9244..a409c084206 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/TitleDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/TitleDetector.java @@ -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_TITLE; @@ -25,16 +25,16 @@ import static com.android.SdkConstants.VALUE_FALSE; 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.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.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.Element; import java.util.Collection; @@ -43,7 +43,7 @@ import java.util.Collections; /** * Check which makes sure menu items specify a title */ -public class TitleDetector extends ResourceXmlDetector implements JavaScanner { +public class TitleDetector extends ResourceXmlDetector implements UastScanner { /** The main issue discovered by this detector */ public static final Issue ISSUE = Issue.create( "MenuTitle", //$NON-NLS-1$ diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ToastDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ToastDetector.java index 40c01c2d6b2..8f49d911800 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ToastDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ToastDetector.java @@ -14,34 +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.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.AstVisitor; -import lombok.ast.Expression; -import lombok.ast.ForwardingAstVisitor; -import lombok.ast.IntegralLiteral; -import lombok.ast.MethodInvocation; -import lombok.ast.Node; -import lombok.ast.Return; -import lombok.ast.StrictListAccessor; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.uast.*; +import org.jetbrains.uast.check.UastAndroidUtils; +import org.jetbrains.uast.check.UastAndroidContext; +import org.jetbrains.uast.check.UastScanner; +import org.jetbrains.uast.visitor.UastVisitor; /** Detector looking for Toast.makeText() without a corresponding show() call */ -public class ToastDetector extends Detector implements Detector.JavaScanner { +public class ToastDetector extends Detector implements UastScanner { /** The main issue discovered by this detector */ public static final Issue ISSUE = Issue.create( "ShowToast", //$NON-NLS-1$ @@ -68,23 +64,23 @@ public class ToastDetector extends Detector implements Detector.JavaScanner { } - // ---- Implements JavaScanner ---- + // ---- Implements UastScanner ---- @Override - public List getApplicableMethodNames() { + public List getApplicableFunctionNames() { return Collections.singletonList("makeText"); //$NON-NLS-1$ } @Override - public void visitMethod(@NonNull JavaContext context, @Nullable AstVisitor visitor, - @NonNull MethodInvocation node) { - assert node.astName().astValue().equals("makeText"); - if (node.astOperand() == null) { - // "makeText()" in the code with no operand + public void visitFunctionCall(UastAndroidContext context, UCallExpression node) { + assert "makeText".equals(node.getFunctionName()); + + UElement qualifiedExpression = node.getParent(); + if (!(qualifiedExpression instanceof UQualifiedExpression)) { return; } - String operand = node.astOperand().toString(); + String operand = ((UQualifiedExpression)qualifiedExpression).getReceiver().renderString(); if (!(operand.equals("Toast") || operand.endsWith(".Toast"))) { return; } @@ -92,47 +88,47 @@ public class ToastDetector extends Detector implements Detector.JavaScanner { // Make sure you pass the right kind of duration: it's not a delay, it's // LENGTH_SHORT or LENGTH_LONG // (see http://code.google.com/p/android/issues/detail?id=3655) - StrictListAccessor args = node.astArguments(); + List args = node.getValueArguments(); if (args.size() == 3) { - Expression duration = args.last(); - if (duration instanceof IntegralLiteral) { + UExpression duration = args.get(2); + if (duration instanceof ULiteralExpression && ((ULiteralExpression)duration).getValue() instanceof Number) { context.report(ISSUE, duration, context.getLocation(duration), - "Expected duration `Toast.LENGTH_SHORT` or `Toast.LENGTH_LONG`, a custom " + - "duration value is not supported"); + "Expected duration `Toast.LENGTH_SHORT` or `Toast.LENGTH_LONG`, a custom " + + "duration value is not supported"); } } - Node method = JavaContext.findSurroundingMethod(node.getParent()); + UFunction method = UastUtils.getContainingFunction(node.getParent()); if (method == null) { return; } - ShowFinder finder = new ShowFinder(node); - method.accept(finder); + UExpression nodeWithPossibleQualifier = UastUtils.getQualifiedCallElement(node); + ShowFinder finder = new ShowFinder(nodeWithPossibleQualifier); + finder.process(method); if (!finder.isShowCalled()) { - context.report(ISSUE, node, context.getLocation(node), - "Toast created but not shown: did you forget to call `show()` ?"); + context.report(ISSUE, node, UastAndroidUtils.getLocation(node), + "Toast created but not shown: did you forget to call `show()` ?"); } } - private static class ShowFinder extends ForwardingAstVisitor { + private static class ShowFinder extends UastVisitor { /** The target makeText call */ - private final MethodInvocation mTarget; + private final UExpression mTarget; /** Whether we've found the show method */ private boolean mFound; /** Whether we've seen the target makeText node yet */ private boolean mSeenTarget; - private ShowFinder(MethodInvocation target) { + private ShowFinder(UExpression target) { mTarget = target; } @Override - public boolean visitMethodInvocation(MethodInvocation node) { + public boolean visitCallExpression(@NotNull UCallExpression node) { if (node == mTarget) { mSeenTarget = true; - } else if ((mSeenTarget || node.astOperand() == mTarget) - && "show".equals(node.astName().astValue())) { //$NON-NLS-1$ + } else if (mSeenTarget && node.functionNameMatches("show")) { //$NON-NLS-1$ // TODO: Do more flow analysis to see whether we're really calling show // on the right type of object? mFound = true; @@ -142,16 +138,26 @@ public class ToastDetector extends Detector implements Detector.JavaScanner { } @Override - public boolean visitReturn(Return node) { - if (node.astValue() == mTarget) { + public boolean visitQualifiedExpression(@NotNull UQualifiedExpression node) { + if (node == mTarget) { + mSeenTarget = true; + } + + return false; + } + + @Override + public boolean visitSpecialExpressionList(@NotNull USpecialExpressionList node) { + if (node.getKind() == UastSpecialExpressionKind.RETURN && node.firstOrNull() == mTarget) { // If you just do "return Toast.makeText(...) don't warn mFound = true; } - return super.visitReturn(node); + + return false; } boolean isShowCalled() { - return mFound; + return mFound && mSeenTarget; } } } diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/TooManyViewsDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/TooManyViewsDetector.java index c059fd69a7f..88479aee52a 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/TooManyViewsDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/TooManyViewsDetector.java @@ -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.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.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.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; diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/TranslationDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/TranslationDetector.java index 2eaab73e218..8eb147990d2 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/TranslationDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/TranslationDetector.java @@ -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.ATTR_LOCALE; @@ -38,17 +38,17 @@ import com.android.ide.common.resources.LocaleManager; import com.android.ide.common.resources.configuration.FolderConfiguration; import com.android.ide.common.resources.configuration.LocaleQualifier; 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.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.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.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Sets; @@ -174,7 +174,7 @@ public class TranslationDetector extends ResourceXmlDetector { // Convention seen in various projects mIgnoreFile = context.file.getName().startsWith("donottranslate") //$NON-NLS-1$ - || UnusedResourceDetector.isAnalyticsFile(context); + || UnusedResourceDetector.isAnalyticsFile(context); if (!context.getProject().getReportIssues()) { mIgnoreFile = true; diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/TypoDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/TypoDetector.java index 5d76e845886..23d88fd3d1e 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/TypoDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/TypoDetector.java @@ -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_LOCALE; import static com.android.SdkConstants.ATTR_TRANSLATABLE; @@ -23,25 +23,25 @@ import static com.android.SdkConstants.TAG_PLURALS; import static com.android.SdkConstants.TAG_STRING; import static com.android.SdkConstants.TAG_STRING_ARRAY; import static com.android.SdkConstants.TOOLS_URI; -import static com.android.tools.lint.checks.TypoLookup.isLetter; +import static com.android.tools.klint.checks.TypoLookup.isLetter; import static com.google.common.base.Objects.equal; import com.android.annotations.NonNull; import com.android.annotations.Nullable; import com.android.ide.common.resources.configuration.LocaleQualifier; 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.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.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.google.common.base.Charsets; import org.w3c.dom.Attr; diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/TypoLookup.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/TypoLookup.java index 56ede7e175d..a580155082a 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/TypoLookup.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/TypoLookup.java @@ -14,16 +14,16 @@ * limitations under the License. */ -package com.android.tools.lint.checks; +package com.android.tools.klint.checks; import static com.android.SdkConstants.DOT_XML; -import static com.android.tools.lint.detector.api.LintUtils.assertionsEnabled; +import static com.android.tools.klint.detector.api.LintUtils.assertionsEnabled; import com.android.annotations.NonNull; import com.android.annotations.Nullable; import com.android.annotations.VisibleForTesting; -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.base.Splitter; import com.google.common.io.Files; diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/TypographyDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/TypographyDetector.java index 420d315dd74..5cd9445d423 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/TypographyDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/TypographyDetector.java @@ -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; import static com.android.SdkConstants.TAG_PLURALS; @@ -24,15 +24,15 @@ import static com.android.SdkConstants.TAG_STRING_ARRAY; import com.android.annotations.NonNull; import com.android.annotations.VisibleForTesting; 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.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.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.Element; import org.w3c.dom.Node; diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/UnusedResourceDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/UnusedResourceDetector.java index 4117067dc5d..e4ec4db5756 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/UnusedResourceDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/UnusedResourceDetector.java @@ -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_NAME; @@ -38,24 +38,29 @@ import static com.android.SdkConstants.TAG_STYLE; import static com.android.utils.SdkUtils.getResourceFieldName; import com.android.annotations.NonNull; -import com.android.annotations.Nullable; 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.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.Lists; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.uast.UClass; +import org.jetbrains.uast.UDeclaration; +import org.jetbrains.uast.UElement; +import org.jetbrains.uast.UVariable; +import org.jetbrains.uast.check.UastAndroidContext; +import org.jetbrains.uast.check.UastScanner; +import org.jetbrains.uast.visitor.UastVisitor; import org.w3c.dom.Attr; import org.w3c.dom.Element; import org.w3c.dom.Node; @@ -74,13 +79,6 @@ import java.util.List; import java.util.Map; import java.util.Set; -import lombok.ast.AstVisitor; -import lombok.ast.ClassDeclaration; -import lombok.ast.ForwardingAstVisitor; -import lombok.ast.NormalTypeBody; -import lombok.ast.VariableDeclaration; -import lombok.ast.VariableDefinition; - /** * Finds unused resources. *

@@ -89,11 +87,11 @@ import lombok.ast.VariableDefinition; * BCEL for bytecode analysis etc) and once it does this should be updated to * use it. */ -public class UnusedResourceDetector extends ResourceXmlDetector implements Detector.JavaScanner { +public class UnusedResourceDetector extends ResourceXmlDetector implements UastScanner { private static final Implementation IMPLEMENTATION = new Implementation( UnusedResourceDetector.class, - EnumSet.of(Scope.MANIFEST, Scope.ALL_RESOURCE_FILES, Scope.ALL_JAVA_FILES, + EnumSet.of(Scope.MANIFEST, Scope.ALL_RESOURCE_FILES, Scope.ALL_SOURCE_FILES, Scope.TEST_SOURCES)); /** Unused resources (other than ids). */ @@ -148,7 +146,7 @@ public class UnusedResourceDetector extends ResourceXmlDetector implements Detec } } - // ---- Implements JavaScanner ---- + // ---- Implements UastScanner ---- @Override public void beforeCheckFile(@NonNull Context context) { @@ -518,20 +516,13 @@ public class UnusedResourceDetector extends ResourceXmlDetector implements Detec return Speed.SLOW; } - @Override - public List> getApplicableNodeTypes() { - return Collections.>singletonList(ClassDeclaration.class); - } - @Override public boolean appliesToResourceRefs() { return true; } @Override - public void visitResourceReference(@NonNull JavaContext context, @Nullable AstVisitor visitor, - @NonNull lombok.ast.Node node, @NonNull String type, @NonNull String name, - boolean isFramework) { + public void visitResourceReference(UastAndroidContext context, UElement element, String type, String name, boolean isFramework) { if (mReferences != null && !isFramework) { String reference = R_PREFIX + type + '.' + name; mReferences.add(reference); @@ -539,7 +530,7 @@ public class UnusedResourceDetector extends ResourceXmlDetector implements Detec } @Override - public AstVisitor createJavaVisitor(@NonNull JavaContext context) { + public UastVisitor createUastVisitor(@NonNull UastAndroidContext context) { if (mReferences != null) { return new UnusedResourceVisitor(); } else { @@ -549,47 +540,17 @@ public class UnusedResourceDetector extends ResourceXmlDetector implements Detec } // Look for references and declarations - private class UnusedResourceVisitor extends ForwardingAstVisitor { + private class UnusedResourceVisitor extends UastVisitor { @Override - public boolean visitClassDeclaration(ClassDeclaration node) { + public boolean visitClass(@NotNull UClass node) { // Look for declarations of R class fields and store them in // mDeclarations - String description = node.astName().astValue(); + String description = node.getName(); if (description.equals(R_CLASS)) { - // This is an R class. We can process this class very deliberately. - // The R class has a very specific AST format: - // ClassDeclaration ("R") - // NormalTypeBody - // ClassDeclaration (e.g. "drawable") - // NormalTypeBody - // VariableDeclaration - // VariableDefinition (e.g. "ic_launcher") - for (lombok.ast.Node body : node.getChildren()) { - if (body instanceof NormalTypeBody) { - for (lombok.ast.Node subclass : body.getChildren()) { - if (subclass instanceof ClassDeclaration) { - String className = ((ClassDeclaration) subclass).astName().astValue(); - for (lombok.ast.Node innerBody : subclass.getChildren()) { - if (innerBody instanceof NormalTypeBody) { - for (lombok.ast.Node field : innerBody.getChildren()) { - if (field instanceof VariableDeclaration) { - for (lombok.ast.Node child : field.getChildren()) { - if (child instanceof VariableDefinition) { - VariableDefinition def = - (VariableDefinition) child; - String name = def.astVariables().first() - .astName().astValue(); - String resource = R_PREFIX + className - + '.' + name; - mDeclarations.add(resource); - } // Else: It could be a comment node - } - } - } - } - } - } - } + for (UDeclaration subclass : node.getNestedClasses()) { + for (UVariable variable : node.getProperties()) { + String resource = R_PREFIX + subclass.getName() + '.' + variable.getName(); + mDeclarations.add(resource); } } diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/UseCompoundDrawableDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/UseCompoundDrawableDetector.java index 7d2df3bb071..4c05d936763 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/UseCompoundDrawableDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/UseCompoundDrawableDetector.java @@ -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_BACKGROUND; @@ -25,15 +25,15 @@ import static com.android.SdkConstants.LINEAR_LAYOUT; import static com.android.SdkConstants.TEXT_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; diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/UselessViewDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/UselessViewDetector.java index d82478bb79c..80a62858ece 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/UselessViewDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/UselessViewDetector.java @@ -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; @@ -41,16 +41,16 @@ import static com.android.SdkConstants.TABLE_ROW; import static com.android.SdkConstants.VIEW_MERGE; 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.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.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 org.w3c.dom.Element; import org.w3c.dom.Node; diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/Utf8Detector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/Utf8Detector.java index 57a13d7a131..30b71c4e5b1 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/Utf8Detector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/Utf8Detector.java @@ -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.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.Document; diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ViewConstructorDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ViewConstructorDetector.java index 2ab6857da9f..02425b140ad 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ViewConstructorDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ViewConstructorDetector.java @@ -14,38 +14,35 @@ * limitations under the License. */ -package com.android.tools.lint.checks; +package com.android.tools.klint.checks; import static com.android.SdkConstants.CLASS_ATTRIBUTE_SET; import static com.android.SdkConstants.CLASS_CONTEXT; -import static com.android.tools.lint.client.api.JavaParser.ResolvedClass; -import static com.android.tools.lint.client.api.JavaParser.ResolvedMethod; import com.android.SdkConstants; 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.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 lombok.ast.NormalTypeBody; +import org.jetbrains.uast.*; +import org.jetbrains.uast.check.UastAndroidUtils; +import org.jetbrains.uast.check.UastAndroidContext; +import org.jetbrains.uast.check.UastScanner; /** * Looks for custom views that do not define the view constructors needed by UI builders */ -public class ViewConstructorDetector extends Detector implements Detector.JavaScanner { +public class ViewConstructorDetector extends Detector implements UastScanner { /** The main issue discovered by this detector */ public static final Issue ISSUE = Issue.create( "ViewConstructor", //$NON-NLS-1$ @@ -79,62 +76,56 @@ public class ViewConstructorDetector extends Detector implements Detector.JavaSc return Speed.FAST; } - // ---- Implements JavaScanner ---- + // ---- Implements UastScanner ---- - private static boolean isXmlConstructor(ResolvedMethod method) { + private static boolean isXmlConstructor(UFunction method) { // Accept // android.content.Context // android.content.Context,android.util.AttributeSet // android.content.Context,android.util.AttributeSet,int - int argumentCount = method.getArgumentCount(); - if (argumentCount == 0 || argumentCount > 3) { + List valueParameters = method.getValueParameters(); + int valueParameterCount = valueParameters.size(); + if (valueParameterCount == 0 || valueParameterCount > 3) { return false; } - if (!method.getArgumentType(0).matchesName(CLASS_CONTEXT)) { + + if (!valueParameters.get(0).getType().matchesFqName(CLASS_CONTEXT)) { return false; } - if (argumentCount == 1) { + if (valueParameterCount == 1) { return true; } - if (!method.getArgumentType(1).matchesName(CLASS_ATTRIBUTE_SET)) { + if (!valueParameters.get(1).getType().matchesFqName(CLASS_ATTRIBUTE_SET)) { return false; } //noinspection SimplifiableIfStatement - if (argumentCount == 2) { + if (valueParameterCount == 2) { return true; } - return method.getArgumentType(2).matchesName("int"); + return valueParameters.get(2).getType().isInt(); } @Nullable @Override - public List applicableSuperClasses() { + public List getApplicableSuperClasses() { return Collections.singletonList(SdkConstants.CLASS_VIEW); } @Override - public void checkClass(@NonNull JavaContext context, @Nullable ClassDeclaration node, - @NonNull Node declarationOrAnonymous, @NonNull ResolvedClass resolvedClass) { - if (node == null) { + public void visitClass(UastAndroidContext context, UClass node) { + // Only applies to concrete and not abstract classes + if (node.isObject() || node.isInterface() || node.isEnum() || node.hasModifier(UastModifier.ABSTRACT)) { return; } - // Only applies to concrete classes - int flags = node.astModifiers().getEffectiveModifierFlags(); - // Ignore abstract classes - if ((flags & Modifier.ABSTRACT) != 0) { - return; - } - - if (node.getParent() instanceof NormalTypeBody - && ((flags & Modifier.STATIC) == 0)) { + if (UastUtils.getContainingClass(node) != null && node.hasModifier(UastModifier.INNER)) { // Ignore inner classes that aren't static: we can't create these // anyway since we'd need the outer instance return; } boolean found = false; - for (ResolvedMethod constructor : resolvedClass.getConstructors()) { + for (UFunction constructor : node.getConstructors()) { if (isXmlConstructor(constructor)) { found = true; break; @@ -143,11 +134,11 @@ public class ViewConstructorDetector extends Detector implements Detector.JavaSc if (!found) { String message = String.format( - "Custom view `%1$s` is missing constructor used by tools: " - + "`(Context)` or `(Context,AttributeSet)` " - + "or `(Context,AttributeSet,int)`", - node.astName().astValue()); - Location location = context.getLocation(node.astName()); + "Custom view `%1$s` is missing constructor used by tools: " + + "`(Context)` or `(Context,AttributeSet)` " + + "or `(Context,AttributeSet,int)`", + node.getFqName()); + Location location = UastAndroidUtils.getLocation(node.getNameElement()); context.report(ISSUE, node, location, message /*data*/); } } diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ViewHolderDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ViewHolderDetector.java index 841f7270aff..63d8d257e29 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ViewHolderDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ViewHolderDetector.java @@ -14,42 +14,34 @@ * limitations under the License. */ -package com.android.tools.lint.checks; +package com.android.tools.klint.checks; -import static com.android.SdkConstants.VIEW; -import static com.android.SdkConstants.VIEW_GROUP; -import static com.android.tools.lint.client.api.JavaParser.TYPE_INT; +import static com.android.SdkConstants.*; import com.android.annotations.NonNull; -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.Lists; -import java.util.Collections; import java.util.Iterator; import java.util.List; -import lombok.ast.AstVisitor; -import lombok.ast.ForwardingAstVisitor; -import lombok.ast.If; -import lombok.ast.InlineIfExpression; -import lombok.ast.MethodDeclaration; -import lombok.ast.MethodInvocation; -import lombok.ast.Node; -import lombok.ast.StrictListAccessor; -import lombok.ast.Switch; -import lombok.ast.VariableDefinition; +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; /** * Looks for ListView scrolling performance: should use view holder pattern */ -public class ViewHolderDetector extends Detector implements Detector.JavaScanner { +public class ViewHolderDetector extends Detector implements UastScanner { private static final Implementation IMPLEMENTATION = new Implementation( ViewHolderDetector.class, @@ -85,52 +77,45 @@ public class ViewHolderDetector extends Detector implements Detector.JavaScanner return Speed.NORMAL; } - // ---- Implements JavaScanner ---- + // ---- Implements UastScanner ---- @Override - public List> getApplicableNodeTypes() { - return Collections.>singletonList(MethodDeclaration.class); - } - - @Override - public AstVisitor createJavaVisitor(@NonNull JavaContext context) { + public UastVisitor createUastVisitor(UastAndroidContext context) { return new ViewAdapterVisitor(context); } - private static class ViewAdapterVisitor extends ForwardingAstVisitor { - private final JavaContext mContext; + private static class ViewAdapterVisitor extends UastVisitor { + private final UastAndroidContext mContext; - public ViewAdapterVisitor(JavaContext context) { + public ViewAdapterVisitor(UastAndroidContext context) { mContext = context; } @Override - public boolean visitMethodDeclaration(MethodDeclaration node) { + public boolean visitFunction(@NotNull UFunction node) { if (isViewAdapterMethod(node)) { InflationVisitor visitor = new InflationVisitor(mContext); - node.accept(visitor); + visitor.process(node); visitor.finish(); } - return super.visitMethodDeclaration(node); + return false; } /** * Returns true if this method looks like it's overriding android.widget.Adapter's getView * method: getView(int position, View convertView, ViewGroup parent) */ - private static boolean isViewAdapterMethod(MethodDeclaration node) { - if (GET_VIEW.equals(node.astMethodName().astValue())) { - StrictListAccessor parameters = - node.astParameters(); - if (parameters != null && parameters.size() == 3) { - Iterator iterator = parameters.iterator(); + private static boolean isViewAdapterMethod(UFunction node) { + if (GET_VIEW.equals(node.getName())) { + List parameters = node.getValueParameters(); + if (parameters.size() == 3) { + Iterator iterator = parameters.iterator(); if (!iterator.hasNext()) { return false; } - VariableDefinition first = iterator.next(); - if (!first.astTypeReference().astParts().last().getTypeName().equals( - TYPE_INT)) { + UVariable first = iterator.next(); + if (!first.getType().isInt()) { return false; } @@ -138,9 +123,8 @@ public class ViewHolderDetector extends Detector implements Detector.JavaScanner return false; } - VariableDefinition second = iterator.next(); - if (!second.astTypeReference().astParts().last().getTypeName().equals( - VIEW)) { + UVariable second = iterator.next(); + if (!second.getType().matchesFqName(CLASS_VIEW)) { return false; } @@ -148,10 +132,9 @@ public class ViewHolderDetector extends Detector implements Detector.JavaScanner return false; } - VariableDefinition third = iterator.next(); + UVariable third = iterator.next(); //noinspection RedundantIfStatement - if (!third.astTypeReference().astParts().last().getTypeName().equals( - VIEW_GROUP)) { + if (!third.getType().matchesFqName(CLASS_VIEWGROUP)) { return false; } @@ -163,26 +146,26 @@ public class ViewHolderDetector extends Detector implements Detector.JavaScanner } } - private static class InflationVisitor extends ForwardingAstVisitor { - private final JavaContext mContext; - private List mNodes; + private static class InflationVisitor extends UastVisitor { + private final UastAndroidContext mContext; + private List mNodes; private boolean mHaveConditional; - public InflationVisitor(JavaContext context) { + public InflationVisitor(UastAndroidContext context) { mContext = context; } @Override - public boolean visitMethodInvocation(MethodInvocation node) { - if (node.astOperand() != null) { - String methodName = node.astName().astValue(); - if (methodName.equals(INFLATE) && node.astArguments().size() >= 1) { + public boolean visitCallExpression(@NotNull UCallExpression node) { + UElement parent = node.getParent(); + if (parent instanceof UQualifiedExpression) { + String methodName = node.getFunctionName(); + if (INFLATE.equals(methodName) && node.getValueArgumentCount() >= 1) { // See if we're inside a conditional boolean insideIf = false; - Node p = node.getParent(); + UElement p = parent.getParent(); while (p != null) { - if (p instanceof If || p instanceof InlineIfExpression - || p instanceof Switch) { + if (p instanceof UIfExpression || p instanceof USwitchExpression) { insideIf = true; mHaveConditional = true; break; @@ -209,17 +192,17 @@ public class ViewHolderDetector extends Detector implements Detector.JavaScanner } } - return super.visitMethodInvocation(node); + return false; } public void finish() { if (!mHaveConditional && mNodes != null) { - for (Node node : mNodes) { + for (UElement node : mNodes) { String message = "Unconditional layout inflation from view adapter: " + "Should use View Holder pattern (use recycled view passed " + "into this method as the second parameter) for smoother " + "scrolling"; - mContext.report(ISSUE, node, mContext.getLocation(node), message); + mContext.report(ISSUE, node, UastAndroidUtils.getLocation(node), message); } } } diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ViewTagDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ViewTagDetector.java index 77f5fe7660a..7019aeaa74b 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ViewTagDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ViewTagDetector.java @@ -14,22 +14,22 @@ * 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.LintDriver; -import com.android.tools.lint.client.api.SdkInfo; -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.Detector.ClassScanner; -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.Scope; -import com.android.tools.lint.detector.api.Severity; -import com.android.tools.lint.detector.api.Speed; +import com.android.tools.klint.client.api.LintDriver; +import com.android.tools.klint.client.api.SdkInfo; +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.Detector.ClassScanner; +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 org.objectweb.asm.Type; import org.objectweb.asm.tree.ClassNode; diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ViewTypeDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ViewTypeDetector.java index ec8edd342e2..40c7a22835b 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ViewTypeDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ViewTypeDetector.java @@ -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_CLASS; @@ -23,6 +23,7 @@ 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.VIEW_TAG; +import static org.jetbrains.uast.UastBinaryExpressionWithTypeUtils.*; import com.android.annotations.NonNull; import com.android.annotations.Nullable; @@ -31,19 +32,18 @@ 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.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.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.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.Implementation; +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.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.android.utils.XmlUtils; import com.google.common.base.Joiner; import com.google.common.collect.ArrayListMultimap; @@ -52,6 +52,10 @@ import com.google.common.collect.Maps; import com.google.common.collect.Multimap; import com.google.common.collect.Sets; +import org.jetbrains.uast.*; +import org.jetbrains.uast.check.UastAndroidContext; +import org.jetbrains.uast.check.UastAndroidUtils; +import org.jetbrains.uast.check.UastScanner; import org.w3c.dom.Attr; import org.w3c.dom.Document; import org.w3c.dom.Element; @@ -68,13 +72,6 @@ import java.util.List; import java.util.Map; import java.util.Set; -import lombok.ast.AstVisitor; -import lombok.ast.Cast; -import lombok.ast.Expression; -import lombok.ast.MethodInvocation; -import lombok.ast.Select; -import lombok.ast.StrictListAccessor; - /** Detector for finding inconsistent usage of views and casts *

* TODO: Check findFragmentById @@ -87,7 +84,7 @@ import lombok.ast.StrictListAccessor; * check its name or class attributes to make sure the cast is compatible with * the named fragment class! */ -public class ViewTypeDetector extends ResourceXmlDetector implements Detector.JavaScanner { +public class ViewTypeDetector extends ResourceXmlDetector implements UastScanner { /** Mismatched view types */ @SuppressWarnings("unchecked") public static final Issue ISSUE = Issue.create( @@ -100,7 +97,7 @@ public class ViewTypeDetector extends ResourceXmlDetector implements Detector.Ja Severity.FATAL, new Implementation( ViewTypeDetector.class, - EnumSet.of(Scope.ALL_RESOURCE_FILES, Scope.ALL_JAVA_FILES), + EnumSet.of(Scope.ALL_RESOURCE_FILES, Scope.ALL_SOURCE_FILES), Scope.JAVA_FILE_SCOPE)); /** Flag used to do no work if we're running in incremental mode in a .java file without @@ -163,79 +160,91 @@ public class ViewTypeDetector extends ResourceXmlDetector implements Detector.Ja } } - // ---- Implements Detector.JavaScanner ---- + // ---- Implements Detector.UastScanner ---- @Override - public List getApplicableMethodNames() { + public List getApplicableFunctionNames() { return Collections.singletonList("findViewById"); //$NON-NLS-1$ } @Override - public void visitMethod(@NonNull JavaContext context, @Nullable AstVisitor visitor, - @NonNull MethodInvocation node) { - LintClient client = context.getClient(); + public void visitFunctionCall(UastAndroidContext context, UCallExpression node) { + JavaContext lintContext = context.getLintContext(); + LintClient client = lintContext.getClient(); if (mIgnore == Boolean.TRUE) { return; } else if (mIgnore == null) { - mIgnore = !context.getScope().contains(Scope.ALL_RESOURCE_FILES) && - !client.supportsProjectResources(); + mIgnore = !lintContext.getScope().contains(Scope.ALL_RESOURCE_FILES) && + !client.supportsProjectResources(); if (mIgnore) { return; } } - assert node.astName().astValue().equals("findViewById"); - if (node.getParent() instanceof Cast) { - Cast cast = (Cast) node.getParent(); - String castType = cast.astTypeReference().getTypeName(); - StrictListAccessor args = node.astArguments(); - if (args.size() == 1) { - Expression first = args.first(); - // TODO: Do flow analysis as in the StringFormatDetector in order - // to handle variable references too - if (first instanceof Select) { - String resource = first.toString(); - if (resource.startsWith("R.id.")) { //$NON-NLS-1$ - String id = ((Select) first).astIdentifier().astValue(); + assert "findViewById".equals(node.getFunctionName()); + UBinaryExpressionWithType cast = findContainingTypeCast(node.getParent()); + if (cast == null) { + return; + } - if (client.supportsProjectResources()) { - AbstractResourceRepository resources = client - .getProjectResources(context.getMainProject(), true); - if (resources == null) { - return; - } + String castType = cast.getType().getFqName(); + List args = node.getValueArguments(); + if (args.size() == 1) { + UExpression first = args.get(0); + // TODO: Do flow analysis as in the StringFormatDetector in order + // to handle variable references too + if (first instanceof UQualifiedExpression) { + String resource = first.renderString(); + if (resource.startsWith("R.id.")) { //$NON-NLS-1$ + String id = ((UQualifiedExpression) first).getSelector().renderString(); - List items = resources.getResourceItem(ResourceType.ID, - id); - if (items != null && !items.isEmpty()) { - Set compatible = Sets.newHashSet(); - for (ResourceItem item : items) { - Collection tags = getViewTags(context, item); - if (tags != null) { - compatible.addAll(tags); - } - } - if (!compatible.isEmpty()) { - ArrayList layoutTypes = Lists.newArrayList(compatible); - checkCompatible(context, castType, null, layoutTypes, cast); + if (client.supportsProjectResources()) { + AbstractResourceRepository resources = client + .getProjectResources(lintContext.getMainProject(), true); + if (resources == null) { + return; + } + + List items = resources.getResourceItem(ResourceType.ID, + id); + if (items != null && !items.isEmpty()) { + Set compatible = Sets.newHashSet(); + for (ResourceItem item : items) { + Collection tags = getViewTags(lintContext, item); + if (tags != null) { + compatible.addAll(tags); } } - } else { - Object types = mIdToViewTag.get(id); - if (types instanceof String) { - String layoutType = (String) types; - checkCompatible(context, castType, layoutType, null, cast); - } else if (types instanceof List) { - @SuppressWarnings("unchecked") - List layoutTypes = (List) types; - checkCompatible(context, castType, null, layoutTypes, cast); + if (!compatible.isEmpty()) { + ArrayList layoutTypes = Lists.newArrayList(compatible); + checkCompatible(lintContext, castType, null, layoutTypes, cast); } } + } else { + Object types = mIdToViewTag.get(id); + if (types instanceof String) { + String layoutType = (String) types; + checkCompatible(lintContext, castType, layoutType, null, cast); + } else if (types instanceof List) { + @SuppressWarnings("unchecked") + List layoutTypes = (List) types; + checkCompatible(lintContext, castType, null, layoutTypes, cast); + } } } } } } + private UBinaryExpressionWithType findContainingTypeCast(UElement expression) { + if (isTypeCast(expression)) { + return (UBinaryExpressionWithType) expression; + } else if (expression instanceof UQualifiedExpression) { + return findContainingTypeCast(expression.getParent()); + } else { + return null; + } + } + @Nullable protected Collection getViewTags( @NonNull Context context, @@ -301,7 +310,7 @@ public class ViewTypeDetector extends ResourceXmlDetector implements Detector.Ja /** Check if the view and cast type are compatible */ private static void checkCompatible(JavaContext context, String castType, String layoutType, - List layoutTypes, Cast node) { + List layoutTypes, UBinaryExpressionWithType node) { assert layoutType == null || layoutTypes == null; // Should only specify one or the other boolean compatible = true; if (layoutType != null) { @@ -328,7 +337,7 @@ public class ViewTypeDetector extends ResourceXmlDetector implements Detector.Ja String message = String.format( "Unexpected cast to `%1$s`: layout tag was `%2$s`", castType, layoutType); - context.report(ISSUE, node, context.getLocation(node), message); + context.report(ISSUE, node, UastAndroidUtils.getLocation(node), message); } } } diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/WakelockDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/WakelockDetector.java index 7ec48e1f8f2..6b8f45e1f19 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/WakelockDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/WakelockDetector.java @@ -14,23 +14,23 @@ * limitations under the License. */ -package com.android.tools.lint.checks; +package com.android.tools.klint.checks; import static com.android.SdkConstants.ANDROID_APP_ACTIVITY; import com.android.annotations.NonNull; import com.android.annotations.Nullable; -import com.android.tools.lint.checks.ControlFlowGraph.Node; -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.Detector.ClassScanner; -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.Scope; -import com.android.tools.lint.detector.api.Severity; +import com.android.tools.klint.checks.ControlFlowGraph.Node; +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.Detector.ClassScanner; +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 org.objectweb.asm.Opcodes; import org.objectweb.asm.tree.AbstractInsnNode; @@ -305,7 +305,7 @@ public class WakelockDetector extends Detector implements ClassScanner { * @return true if the target was reached * XXX RETURN VALUES ARE WRONG AS OF RIGHT NOW */ - protected static int dfs(ControlFlowGraph.Node node) { + protected static int dfs(Node node) { AbstractInsnNode instruction = node.instruction; if (instruction.getType() == AbstractInsnNode.JUMP_INSN) { int opcode = instruction.getOpcode(); diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/WebViewDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/WebViewDetector.java index a0e74de5f5a..66aa35ba530 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/WebViewDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/WebViewDetector.java @@ -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; @@ -22,15 +22,15 @@ import static com.android.SdkConstants.VALUE_WRAP_CONTENT; import static com.android.SdkConstants.WEB_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.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.Attr; import org.w3c.dom.Element; diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/WrongCallDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/WrongCallDetector.java index 5a7c1d71bfb..29f38397482 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/WrongCallDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/WrongCallDetector.java @@ -14,41 +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.tools.lint.checks.JavaPerformanceDetector.ON_DRAW; -import static com.android.tools.lint.checks.JavaPerformanceDetector.ON_LAYOUT; -import static com.android.tools.lint.checks.JavaPerformanceDetector.ON_MEASURE; +import static com.android.tools.klint.checks.JavaPerformanceDetector.ON_DRAW; +import static com.android.tools.klint.checks.JavaPerformanceDetector.ON_LAYOUT; +import static com.android.tools.klint.checks.JavaPerformanceDetector.ON_MEASURE; 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.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.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.Expression; -import lombok.ast.MethodDeclaration; -import lombok.ast.MethodInvocation; -import lombok.ast.Node; -import lombok.ast.Super; +import org.jetbrains.uast.*; +import org.jetbrains.uast.check.UastAndroidUtils; +import org.jetbrains.uast.check.UastAndroidContext; +import org.jetbrains.uast.check.UastScanner; /** * Checks for cases where the wrong call is being made */ -public class WrongCallDetector extends Detector implements Detector.JavaScanner { +public class WrongCallDetector extends Detector implements UastScanner { /** Calling the wrong method */ public static final Issue ISSUE = Issue.create( "WrongCall", //$NON-NLS-1$ @@ -74,57 +70,67 @@ public class WrongCallDetector extends Detector implements Detector.JavaScanner return Speed.FAST; } - // ---- Implements JavaScanner ---- + // ---- Implements UastScanner ---- + @Override - @Nullable - public List getApplicableMethodNames() { + public List getApplicableFunctionNames() { return Arrays.asList( - ON_DRAW, - ON_MEASURE, - ON_LAYOUT + ON_DRAW, + ON_MEASURE, + ON_LAYOUT ); } @Override - public void visitMethod(@NonNull JavaContext context, @Nullable AstVisitor visitor, - @NonNull MethodInvocation node) { - + public void visitFunctionCall(UastAndroidContext context, UCallExpression node) { // Call is only allowed if it is both only called on the super class (invoke special) // as well as within the same overriding method (e.g. you can't call super.onLayout // from the onMeasure method) - Expression operand = node.astOperand(); - if (!(operand instanceof Super)) { - report(context, node); + if (node.getKind() != UastCallKind.FUNCTION_CALL) { return; } - Node method = StringFormatDetector.getParentMethod(node); - if (!(method instanceof MethodDeclaration) || - !((MethodDeclaration)method).astMethodName().astValue().equals( - node.astName().astValue())) { - report(context, node); - } - } - - private static void report(JavaContext context, MethodInvocation node) { - // Make sure the call is on a view - JavaParser.ResolvedNode resolved = context.resolve(node); - if (resolved instanceof JavaParser.ResolvedMethod) { - JavaParser.ResolvedMethod method = (JavaParser.ResolvedMethod) resolved; - JavaParser.ResolvedClass containingClass = method.getContainingClass(); - if (!containingClass.isSubclassOf(CLASS_VIEW, false)) { + UElement referenceExpression = node.getParent(); + if (referenceExpression instanceof UQualifiedExpression) { + UExpression operand = ((UQualifiedExpression)referenceExpression).getReceiver(); + if (!(operand instanceof USuperExpression)) { + report(context, node); return; } } - String name = node.astName().astValue(); + UFunction containingFunction = UastUtils.getContainingFunction(node); + if (containingFunction == null) { + return; + } + + if (containingFunction.getKind() == UastFunctionKind.CONSTRUCTOR || !containingFunction.matchesName(node.getFunctionName())) { + report(context, node); + } + } + + private static void report(UastAndroidContext context, UCallExpression node) { + // Make sure the call is on a view + UFunction resolved = node.resolve(context); + if (resolved != null && resolved.getKind() != UastFunctionKind.CONSTRUCTOR) { + UClass containingClass = UastUtils.getContainingClass(resolved); + if (containingClass == null || !containingClass.isSubclassOf(CLASS_VIEW)) { + return; + } + } + + String name = node.getFunctionName(); + if (name == null) { + return; + } + String suggestion = Character.toLowerCase(name.charAt(2)) + name.substring(3); String message = String.format( // Keep in sync with {@link #getOldValue} and {@link #getNewValue} below! "Suspicious method call; should probably call \"`%1$s`\" rather than \"`%2$s`\"", suggestion, name); - context.report(ISSUE, node, context.getLocation(node.astName()), message); + context.report(ISSUE, node, UastAndroidUtils.getLocation(node.getFunctionReference()), message); } /** diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/WrongCaseDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/WrongCaseDetector.java index 4eb5bef3bec..c07c523a079 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/WrongCaseDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/WrongCaseDetector.java @@ -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.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.Element; diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/WrongIdDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/WrongIdDetector.java index 6041f630062..942fdd3a142 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/WrongIdDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/WrongIdDetector.java @@ -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,10 +27,10 @@ import static com.android.SdkConstants.NEW_ID_PREFIX; import static com.android.SdkConstants.RELATIVE_LAYOUT; import static com.android.SdkConstants.TAG_ITEM; import static com.android.SdkConstants.VALUE_ID; -import static com.android.tools.lint.detector.api.LintUtils.editDistance; -import static com.android.tools.lint.detector.api.LintUtils.getChildren; -import static com.android.tools.lint.detector.api.LintUtils.isSameResourceFile; -import static com.android.tools.lint.detector.api.LintUtils.stripIdPrefix; +import static com.android.tools.klint.detector.api.LintUtils.editDistance; +import static com.android.tools.klint.detector.api.LintUtils.getChildren; +import static com.android.tools.klint.detector.api.LintUtils.isSameResourceFile; +import static com.android.tools.klint.detector.api.LintUtils.stripIdPrefix; import com.android.annotations.NonNull; import com.android.annotations.Nullable; @@ -39,18 +39,18 @@ 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.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.Location; -import com.android.tools.lint.detector.api.Location.Handle; -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.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.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.Location.Handle; +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.android.utils.Pair; import com.google.common.base.Joiner; import com.google.common.collect.ArrayListMultimap; diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/WrongImportDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/WrongImportDetector.java index 2c17555f59b..6d91d9e1fee 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/WrongImportDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/WrongImportDetector.java @@ -14,26 +14,24 @@ * 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.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.util.Collections; -import java.util.List; - -import lombok.ast.AstVisitor; -import lombok.ast.ForwardingAstVisitor; -import lombok.ast.ImportDeclaration; -import lombok.ast.Node; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.uast.UImportStatement; +import org.jetbrains.uast.check.UastAndroidUtils; +import org.jetbrains.uast.check.UastAndroidContext; +import org.jetbrains.uast.check.UastScanner; +import org.jetbrains.uast.visitor.UastVisitor; /** * Checks for "import android.R", which seems to be a common source of confusion @@ -47,7 +45,7 @@ import lombok.ast.Node; * break. Look out for these erroneous import statements and delete them. * */ -public class WrongImportDetector extends Detector implements Detector.JavaScanner { +public class WrongImportDetector extends Detector implements UastScanner { /** Is android.R being imported? */ public static final Issue ISSUE = Issue.create( "SuspiciousImport", //$NON-NLS-1$ @@ -76,35 +74,28 @@ public class WrongImportDetector extends Detector implements Detector.JavaScanne return Speed.FAST; } - // ---- Implements Detector.JavaScanner ---- + // ---- Implements Detector.UastScanner ---- @Override - public List> getApplicableNodeTypes() { - return Collections.> singletonList( - ImportDeclaration.class); - } - - @Override - public AstVisitor createJavaVisitor(@NonNull JavaContext context) { + public UastVisitor createUastVisitor(UastAndroidContext context) { return new ImportVisitor(context); } - private static class ImportVisitor extends ForwardingAstVisitor { - private final JavaContext mContext; + private static class ImportVisitor extends UastVisitor { + private final UastAndroidContext mContext; - public ImportVisitor(JavaContext context) { - super(); + public ImportVisitor(UastAndroidContext context) { mContext = context; } @Override - public boolean visitImportDeclaration(ImportDeclaration node) { - String fqn = node.asFullyQualifiedName(); - if (fqn.equals("android.R")) { //$NON-NLS-1$ - Location location = mContext.getLocation(node); + public boolean visitImportStatement(@NotNull UImportStatement node) { + String fqn = node.getNameToImport(); + if (fqn != null && fqn.equals("android.R")) { //$NON-NLS-1$ + Location location = UastAndroidUtils.getLocation(node); mContext.report(ISSUE, node, location, - "Don't include `android.R` here; use a fully qualified name for " - + "each usage instead"); + "Don't include `android.R` here; use a fully qualified name for " + + "each usage instead"); } return false; } diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/WrongLocationDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/WrongLocationDetector.java index 401cc987f6a..373d083b70d 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/WrongLocationDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/WrongLocationDetector.java @@ -14,19 +14,19 @@ * limitations under the License. */ -package com.android.tools.lint.checks; +package com.android.tools.klint.checks; import static com.android.SdkConstants.TAG_RESOURCES; 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.Document; import org.w3c.dom.Element; diff --git a/plugins/lint/lint-idea/lint-idea.iml b/plugins/lint/lint-idea/lint-idea.iml new file mode 100644 index 00000000000..9041e5bc8e8 --- /dev/null +++ b/plugins/lint/lint-idea/lint-idea.iml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/AndroidInspectionExtensionsFactory.java b/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/AndroidInspectionExtensionsFactory.java index da53d158520..8b73ffe7842 100644 --- a/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/AndroidInspectionExtensionsFactory.java +++ b/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/AndroidInspectionExtensionsFactory.java @@ -1,4 +1,20 @@ -package org.jetbrains.android.inspections.lint; +/* + * 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 org.jetbrains.android.inspections.klint; import com.intellij.codeInspection.HTMLComposer; import com.intellij.codeInspection.lang.GlobalInspectionContextExtension; diff --git a/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/AndroidLintExternalAnnotator.java b/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/AndroidLintExternalAnnotator.java index e7157f147de..23b804a404f 100644 --- a/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/AndroidLintExternalAnnotator.java +++ b/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/AndroidLintExternalAnnotator.java @@ -1,14 +1,28 @@ -package org.jetbrains.android.inspections.lint; +/* + * 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 org.jetbrains.android.inspections.klint; import com.android.SdkConstants; -import com.android.tools.idea.gradle.util.Projects; -import com.android.tools.idea.rendering.PsiProjectListener; -import com.android.tools.lint.client.api.IssueRegistry; -import com.android.tools.lint.client.api.LintDriver; -import com.android.tools.lint.client.api.LintRequest; -import com.android.tools.lint.detector.api.Issue; -import com.android.tools.lint.detector.api.Scope; -import com.android.utils.SdkUtils; +import com.android.tools.klint.client.api.IssueRegistry; +import com.android.tools.klint.client.api.LintDriver; +import com.android.tools.klint.client.api.LintLanguageExtension; +import com.android.tools.klint.client.api.LintRequest; +import com.android.tools.klint.detector.api.Issue; +import com.android.tools.klint.detector.api.Scope; import com.intellij.codeHighlighting.HighlightDisplayLevel; import com.intellij.codeInsight.FileModificationService; import com.intellij.codeInsight.daemon.DaemonBundle; @@ -47,7 +61,7 @@ import org.jetbrains.android.util.AndroidBundle; import org.jetbrains.android.util.AndroidCommonUtils; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.jetbrains.plugins.groovy.GroovyFileType; +import org.jetbrains.uast.UastConverterUtils; import javax.swing.*; import java.lang.reflect.InvocationTargetException; @@ -58,8 +72,8 @@ import java.util.EnumSet; import java.util.List; import static com.android.SdkConstants.*; -import static com.android.tools.lint.detector.api.TextFormat.HTML; -import static com.android.tools.lint.detector.api.TextFormat.RAW; +import static com.android.tools.klint.detector.api.TextFormat.HTML; +import static com.android.tools.klint.detector.api.TextFormat.RAW; /** * @author Eugene.Kudelevsky @@ -98,18 +112,10 @@ public class AndroidLintExternalAnnotator extends ExternalAnnotator issues = getIssuesFromInspections(file.getProject(), file); @@ -139,14 +145,16 @@ public class AndroidLintExternalAnnotator extends ExternalAnnotator pair = + final Pair pair = AndroidLintUtil.getHighlighLevelAndInspection(project, issue, file); if (pair == null) { continue; } - final AndroidLintInspectionBase inspection = pair.getFirst(); + final org.jetbrains.android.inspections.klint.AndroidLintInspectionBase inspection = pair.getFirst(); HighlightDisplayLevel displayLevel = pair.getSecond(); if (inspection != null) { @@ -228,7 +237,7 @@ public class AndroidLintExternalAnnotator extends ExternalAnnotator" + DaemonBundle.message("inspection.extended.description") - +" " + getShowMoreShortCut(); + + "href=\"#lint/" + issue.getId() + "\"" + + (UIUtil.isUnderDarcula() ? " color=\"7AB4C9\" " : "") + + ">" + DaemonBundle.message("inspection.extended.description") + + " " + getShowMoreShortCut(); String tooltip = XmlStringUtil.wrapInHtml(RAW.convertTo(message, HTML) + link); try { @@ -328,7 +337,7 @@ public class AndroidLintExternalAnnotator extends ExternalAnnotator() { @Override public String compute() { diff --git a/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/AndroidLintGlobalInspectionContext.java b/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/AndroidLintGlobalInspectionContext.java index fe7443c521a..a4aee5c1cee 100644 --- a/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/AndroidLintGlobalInspectionContext.java +++ b/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/AndroidLintGlobalInspectionContext.java @@ -1,9 +1,25 @@ -package org.jetbrains.android.inspections.lint; +/* + * 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. + */ -import com.android.tools.lint.client.api.LintDriver; -import com.android.tools.lint.client.api.LintRequest; -import com.android.tools.lint.detector.api.Issue; -import com.android.tools.lint.detector.api.Scope; +package org.jetbrains.android.inspections.klint; + +import com.android.tools.klint.client.api.LintDriver; +import com.android.tools.klint.client.api.LintRequest; +import com.android.tools.klint.detector.api.Issue; +import com.android.tools.klint.detector.api.Scope; import com.google.common.collect.Lists; import com.intellij.analysis.AnalysisScope; import com.intellij.codeInspection.GlobalInspectionContext; @@ -34,7 +50,10 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.io.File; -import java.util.*; +import java.util.Arrays; +import java.util.EnumSet; +import java.util.List; +import java.util.Map; /** * @author Eugene.Kudelevsky diff --git a/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/AndroidLintInspectionBase.java b/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/AndroidLintInspectionBase.java index dd3e7392806..ab5c15130e8 100644 --- a/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/AndroidLintInspectionBase.java +++ b/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/AndroidLintInspectionBase.java @@ -1,7 +1,27 @@ -package org.jetbrains.android.inspections.lint; +/* + * 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 org.jetbrains.android.inspections.klint; import com.android.annotations.concurrency.GuardedBy; -import com.android.tools.lint.detector.api.*; +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.Scope; +import com.android.tools.klint.detector.api.Severity; import com.intellij.analysis.AnalysisScope; import com.intellij.codeHighlighting.HighlightDisplayLevel; import com.intellij.codeInsight.daemon.HighlightDisplayKey; @@ -21,7 +41,6 @@ import com.intellij.psi.*; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.util.ArrayUtil; import com.intellij.util.containers.HashMap; -import com.intellij.xml.CommonXmlStrings; import org.jetbrains.android.util.AndroidBundle; import org.jetbrains.annotations.Nls; import org.jetbrains.annotations.NotNull; @@ -31,8 +50,8 @@ import org.jetbrains.annotations.TestOnly; import java.io.File; import java.util.*; -import static com.android.tools.lint.detector.api.TextFormat.HTML; -import static com.android.tools.lint.detector.api.TextFormat.RAW; +import static com.android.tools.klint.detector.api.TextFormat.HTML; +import static com.android.tools.klint.detector.api.TextFormat.RAW; import static com.intellij.xml.CommonXmlStrings.HTML_END; import static com.intellij.xml.CommonXmlStrings.HTML_START; @@ -40,7 +59,7 @@ import static com.intellij.xml.CommonXmlStrings.HTML_START; * @author Eugene.Kudelevsky */ public abstract class AndroidLintInspectionBase extends GlobalInspectionTool { - private static final Logger LOG = Logger.getInstance("#org.jetbrains.android.inspections.lint.AndroidLintInspectionBase"); + private static final Logger LOG = Logger.getInstance("#org.jetbrains.android.inspections.klint.AndroidLintInspectionBase"); private static final Object ISSUE_MAP_LOCK = new Object(); @@ -199,7 +218,8 @@ public abstract class AndroidLintInspectionBase extends GlobalInspectionTool { if (AndroidLintExternalAnnotator.INCLUDE_IDEA_SUPPRESS_ACTIONS) { final List result = new ArrayList(); result.add(suppressLintQuickFix); - result.addAll(Arrays.asList(BatchSuppressManager.SERVICE.getInstance().createBatchSuppressActions(HighlightDisplayKey.find(getShortName())))); + result.addAll(Arrays.asList( + BatchSuppressManager.SERVICE.getInstance().createBatchSuppressActions(HighlightDisplayKey.find(getShortName())))); result.addAll(Arrays.asList(new XmlSuppressableInspectionTool.SuppressTagStatic(getShortName()), new XmlSuppressableInspectionTool.SuppressForFile(getShortName()))); return result.toArray(new SuppressQuickFix[result.size()]); @@ -257,7 +277,7 @@ public abstract class AndroidLintInspectionBase extends GlobalInspectionTool { for (InspectionToolWrapper e : profile.getInspectionTools(null)) { final String shortName = e.getShortName(); - if (shortName.startsWith("AndroidLint")) { + if (shortName.startsWith("AndroidKLint")) { final InspectionProfileEntry entry = e.getTool(); if (entry instanceof AndroidLintInspectionBase) { final Issue s = ((AndroidLintInspectionBase)entry).getIssue(); @@ -387,7 +407,7 @@ public abstract class AndroidLintInspectionBase extends GlobalInspectionTool { return false; } final Scope scope = scopes.iterator().next(); - return scope == Scope.JAVA_FILE || scope == Scope.RESOURCE_FILE || scope == Scope.MANIFEST + return scope == Scope.SOURCE_FILE || scope == Scope.RESOURCE_FILE || scope == Scope.MANIFEST || scope == Scope.PROGUARD_FILE || scope == Scope.OTHER; } @@ -437,9 +457,9 @@ public abstract class AndroidLintInspectionBase extends GlobalInspectionTool { } /** - * A {@link com.intellij.codeInspection.ProblemDescriptor} for image and directory files. This is + * A {@link ProblemDescriptor} for image and directory files. This is * necessary because the {@link InspectionManager}'s createProblemDescriptor methods - * all use {@link com.intellij.codeInspection.ProblemDescriptorBase} where in the constructor + * all use {@link ProblemDescriptorBase} where in the constructor * it insists that the start and end {@link PsiElement} instances must have a valid * text range, which does not apply for images. *

diff --git a/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/AndroidLintInspectionToolProvider.java b/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/AndroidLintInspectionToolProvider.java index dbcd1ae6534..dc88ca3f1ae 100644 --- a/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/AndroidLintInspectionToolProvider.java +++ b/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/AndroidLintInspectionToolProvider.java @@ -1,42 +1,33 @@ -package org.jetbrains.android.inspections.lint; +/* + * 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 org.jetbrains.android.inspections.klint; -import com.android.SdkConstants; -import com.android.ide.common.repository.GradleCoordinate; -import com.android.ide.common.resources.ResourceUrl; -import com.android.ide.common.resources.configuration.FolderConfiguration; -import com.android.ide.common.resources.configuration.VersionQualifier; -import com.android.resources.ResourceFolderType; import com.android.sdklib.AndroidVersion; import com.android.sdklib.IAndroidTarget; import com.android.sdklib.SdkVersionInfo; -import com.android.tools.idea.actions.OverrideResourceAction; -import com.android.tools.idea.gradle.util.GradleUtil; -import com.android.tools.idea.rendering.ResourceHelper; -import com.android.tools.idea.templates.RepositoryUrlManager; -import com.android.tools.lint.checks.*; -import com.android.tools.lint.detector.api.Issue; -import com.google.common.collect.Lists; -import com.intellij.codeInsight.intention.IntentionAction; -import com.intellij.openapi.editor.Document; -import com.intellij.openapi.fileEditor.FileDocumentManager; -import com.intellij.openapi.project.Project; -import com.intellij.psi.*; -import com.intellij.psi.util.PsiTreeUtil; -import com.intellij.psi.xml.XmlAttribute; -import com.intellij.psi.xml.XmlFile; +import com.android.tools.klint.checks.*; +import com.android.tools.klint.detector.api.Issue; +import com.intellij.psi.PsiElement; import org.jetbrains.android.facet.AndroidFacet; import org.jetbrains.android.sdk.AndroidSdkData; import org.jetbrains.android.util.AndroidBundle; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.jetbrains.plugins.groovy.lang.psi.GroovyFile; -import java.util.List; - -import static com.android.SdkConstants.*; -import static com.android.tools.lint.checks.FragmentDetector.ISSUE; -import static com.android.tools.lint.checks.PluralsDetector.IMPLIED_QUANTITY; -import static com.android.tools.lint.detector.api.TextFormat.RAW; +import static com.android.tools.klint.checks.FragmentDetector.ISSUE; +import static com.android.tools.klint.checks.PluralsDetector.IMPLIED_QUANTITY; /** * Registrations for all the various Lint rules as local IDE inspections, along with quickfixes for many of them @@ -183,29 +174,12 @@ public class AndroidLintInspectionToolProvider { public AndroidLintAlwaysShowActionInspection() { super(AndroidBundle.message("android.lint.inspections.always.show.action"), AlwaysShowActionDetector.ISSUE); } - - @NotNull - @Override - public AndroidLintQuickFix[] getQuickFixes(@NotNull String message) { - return new AndroidLintQuickFix[] { new ReplaceStringQuickFix("Replace with ifRoom", "(always)", "ifRoom") }; - } } public static class AndroidLintAppCompatMethodInspection extends AndroidLintInspectionBase { public AndroidLintAppCompatMethodInspection() { super(AndroidBundle.message("android.lint.inspections.app.compat.method"), AppCompatCallDetector.ISSUE); } - - @NotNull - @Override - public AndroidLintQuickFix[] getQuickFixes(@NotNull String message) { - String oldCall = AppCompatCallDetector.getOldCall(message, RAW); - String newCall = AppCompatCallDetector.getNewCall(message, RAW); - if (oldCall != null && newCall != null) { - return new AndroidLintQuickFix[]{ new ReplaceStringQuickFix("Replace with " + newCall + "()", oldCall, newCall) }; - } - return AndroidLintQuickFix.EMPTY_ARRAY; - } } public static class AndroidLintAppCompatResourceInspection extends AndroidLintInspectionBase { @@ -281,15 +255,6 @@ public class AndroidLintInspectionToolProvider { public AndroidLintContentDescriptionInspection() { super(AndroidBundle.message("android.lint.inspections.content.description"), AccessibilityDetector.ISSUE); } - - @Override - @NotNull - public AndroidLintQuickFix[] getQuickFixes(@NotNull String message) { - return new AndroidLintQuickFix[]{ - new SetAttributeQuickFix(AndroidBundle.message("android.lint.fix.add.content.description"), - SdkConstants.ATTR_CONTENT_DESCRIPTION, null) - }; - } } public static class AndroidLintButtonOrderInspection extends AndroidLintInspectionBase { @@ -344,17 +309,6 @@ public class AndroidLintInspectionToolProvider { public AndroidLintUseValueOfInspection() { super(AndroidBundle.message("android.lint.inspections.use.value.of"), JavaPerformanceDetector.USE_VALUE_OF); } - - @NotNull - @Override - public AndroidLintQuickFix[] getQuickFixes(@NotNull String message) { - String replacedType = JavaPerformanceDetector.getReplacedType(message, RAW); - if (replacedType != null) { - return new AndroidLintQuickFix[]{new ReplaceStringQuickFix("Replace with valueOf()", "(new\\s+" + replacedType + ")", - replacedType + ".valueOf")}; - } - return AndroidLintQuickFix.EMPTY_ARRAY; - } } public static class AndroidLintLibraryCustomViewInspection extends AndroidLintInspectionBase { @@ -374,14 +328,6 @@ public class AndroidLintInspectionToolProvider { public AndroidLintParcelCreatorInspection() { super(AndroidBundle.message("android.lint.inspections.parcel.creator"), ParcelDetector.ISSUE); } - - @NotNull - @Override - public AndroidLintQuickFix[] getQuickFixes(@NotNull String message) { - return new AndroidLintQuickFix[]{ - new ParcelableQuickFix(AndroidBundle.message("implement.parcelable.intention.text"), ParcelableQuickFix.Operation.IMPLEMENT), - }; - } } public static class AndroidLintPluralsCandidateInspection extends AndroidLintInspectionBase { @@ -412,12 +358,6 @@ public class AndroidLintInspectionToolProvider { public AndroidLintEnforceUTF8Inspection() { super(AndroidBundle.message("android.lint.inspections.enforce.utf8"), Utf8Detector.ISSUE); } - - @NotNull - @Override - public AndroidLintQuickFix[] getQuickFixes(@NotNull String message) { - return new AndroidLintQuickFix[]{new ReplaceStringQuickFix(null, null, "utf-8") }; - } } public static class AndroidLintUnknownIdInLayoutInspection extends AndroidLintInspectionBase { @@ -460,12 +400,6 @@ public class AndroidLintInspectionToolProvider { public AndroidLintMissingPrefixInspection() { super(AndroidBundle.message("android.lint.inspections.missing.prefix"), DetectMissingPrefix.MISSING_NAMESPACE); } - - @NotNull - @Override - public AndroidLintQuickFix[] getQuickFixes(@NotNull String message) { - return new AndroidLintQuickFix[]{new AddMissingPrefixQuickFix()}; - } } public static class AndroidLintMissingQuantityInspection extends AndroidLintInspectionBase { @@ -492,53 +426,18 @@ public class AndroidLintInspectionToolProvider { public AndroidLintGridLayoutInspection() { super(AndroidBundle.message("android.lint.inspections.grid.layout"), GridLayoutDetector.ISSUE); } - - @NotNull - @Override - public AndroidLintQuickFix[] getQuickFixes(@NotNull final PsiElement startElement, @NotNull PsiElement endElement, @NotNull String message) { - String obsolete = GridLayoutDetector.getOldValue(message, RAW); - String available = GridLayoutDetector.getNewValue(message, RAW); - if (obsolete != null && available != null) { - return new AndroidLintQuickFix[]{new ReplaceStringQuickFix("Update to " + available, obsolete, available) { - @Override - protected void editBefore(@NotNull Document document) { - Project project = startElement.getProject(); - final XmlFile file = PsiTreeUtil.getParentOfType(startElement, XmlFile.class); - if (file != null) { - SuppressLintIntentionAction.ensureNamespaceImported(project, file, AUTO_URI); - PsiDocumentManager.getInstance(project).doPostponedOperationsAndUnblockDocument(document); - } - } - }}; - } - return AndroidLintQuickFix.EMPTY_ARRAY; - } } public static class AndroidLintHardcodedTextInspection extends AndroidLintInspectionBase { public AndroidLintHardcodedTextInspection() { super(AndroidBundle.message("android.lint.inspections.hardcoded.text"), HardcodedValuesDetector.ISSUE); } - - @NotNull - @Override - public IntentionAction[] getIntentions(@NotNull final PsiElement startElement, @NotNull PsiElement endElement) { - return new IntentionAction[]{new AndroidAddStringResourceQuickFix(startElement)}; - } } public static class AndroidLintInefficientWeightInspection extends AndroidLintInspectionBase { public AndroidLintInefficientWeightInspection() { super(AndroidBundle.message("android.lint.inspections.inefficient.weight"), InefficientWeightDetector.INEFFICIENT_WEIGHT); } - - @NotNull - @Override - public AndroidLintQuickFix[] getQuickFixes(@NotNull String message) { - return new AndroidLintQuickFix[]{ - new InefficientWeightQuickFix() - }; - } } public static class AndroidLintNestedWeightsInspection extends AndroidLintInspectionBase { @@ -564,15 +463,6 @@ public class AndroidLintInspectionToolProvider { public AndroidLintDisableBaselineAlignmentInspection() { super(AndroidBundle.message("android.lint.inspections.disable.baseline.alignment"), InefficientWeightDetector.BASELINE_WEIGHTS); } - - @NotNull - @Override - public AndroidLintQuickFix[] getQuickFixes(@NotNull String message) { - return new AndroidLintQuickFix[]{ - new SetAttributeQuickFix(AndroidBundle.message("android.lint.fix.set.baseline.attribute"), - SdkConstants.ATTR_BASELINE_ALIGNED, "false") - }; - } } public static class AndroidLintManifestOrderInspection extends AndroidLintInspectionBase { @@ -585,12 +475,6 @@ public class AndroidLintInspectionToolProvider { public AndroidLintMockLocationInspection() { super(AndroidBundle.message("android.lint.inspections.mock.location"), ManifestDetector.MOCK_LOCATION); } - - @NotNull - @Override - public AndroidLintQuickFix[] getQuickFixes(@NotNull String message) { - return new AndroidLintQuickFix[]{new MoveToDebugManifestQuickFix()}; - } } public static class AndroidLintMultipleUsesSdkInspection extends AndroidLintInspectionBase { @@ -609,16 +493,6 @@ public class AndroidLintInspectionToolProvider { public AndroidLintUsingHttpInspection() { super(AndroidBundle.message("android.lint.inspections.using.http"), PropertyFileDetector.HTTP); } - - @Override - @NotNull - public AndroidLintQuickFix[] getQuickFixes(@NotNull String message) { - String escaped = PropertyFileDetector.getSuggestedEscape(message, RAW); - if (escaped != null) { - return new AndroidLintQuickFix[]{new ReplaceStringQuickFix(null, null, escaped)}; - } - return AndroidLintQuickFix.EMPTY_ARRAY; - } } public static class AndroidLintValidFragmentInspection extends AndroidLintInspectionBase { @@ -667,17 +541,6 @@ public class AndroidLintInspectionToolProvider { public AndroidLintNewerVersionAvailableInspection() { super(AndroidBundle.message("android.lint.inspections.newer.version.available"), GradleDetector.REMOTE_VERSION); } - - @NotNull - @Override - public AndroidLintQuickFix[] getQuickFixes(@NotNull String message) { - String obsolete = GradleDetector.getOldValue(GradleDetector.DEPENDENCY, message, RAW); - String available = GradleDetector.getNewValue(GradleDetector.DEPENDENCY, message, RAW); - if (obsolete != null && available != null) { - return new AndroidLintQuickFix[]{new ReplaceStringQuickFix("Update to " + available, obsolete, available)}; - } - return AndroidLintQuickFix.EMPTY_ARRAY; - } } public static class AndroidLintNfcTechWhitespaceInspection extends AndroidLintInspectionBase { @@ -696,12 +559,6 @@ public class AndroidLintInspectionToolProvider { public AndroidLintObsoleteLayoutParamInspection() { super(AndroidBundle.message("android.lint.inspections.obsolete.layout.param"), ObsoleteLayoutParamsDetector.ISSUE); } - - @NotNull - @Override - public AndroidLintQuickFix[] getQuickFixes(@NotNull String message) { - return new AndroidLintQuickFix[]{new RemoveAttributeQuickFix()}; - } } public static class AndroidLintProguardInspection extends AndroidLintInspectionBase { @@ -720,57 +577,24 @@ public class AndroidLintInspectionToolProvider { public AndroidLintPxUsageInspection() { super(AndroidBundle.message("android.lint.inspections.px.usage"), PxUsageDetector.PX_ISSUE); } - - @NotNull - @Override - public AndroidLintQuickFix[] getQuickFixes(@NotNull String message) { - return new AndroidLintQuickFix[]{new ConvertToDpQuickFix()}; - } } public static class AndroidLintScrollViewSizeInspection extends AndroidLintInspectionBase { public AndroidLintScrollViewSizeInspection() { super(AndroidBundle.message("android.lint.inspections.scroll.view.size"), ScrollViewChildDetector.ISSUE); } - - @NotNull - @Override - public AndroidLintQuickFix[] getQuickFixes(@NotNull String message) { - return new AndroidLintQuickFix[]{new SetScrollViewSizeQuickFix()}; - } } public static class AndroidLintExportedServiceInspection extends AndroidLintInspectionBase { public AndroidLintExportedServiceInspection() { super(AndroidBundle.message("android.lint.inspections.exported.service"), SecurityDetector.EXPORTED_SERVICE); } - - @NotNull - @Override - public AndroidLintQuickFix[] getQuickFixes(@NotNull String message) { - return new AndroidLintQuickFix[]{ - new SetAttributeQuickFix(AndroidBundle.message("android.lint.fix.add.permission.attribute"), - SdkConstants.ATTR_PERMISSION, null) - }; - } } public static class AndroidLintGradleCompatibleInspection extends AndroidLintInspectionBase { public AndroidLintGradleCompatibleInspection() { super(AndroidBundle.message("android.lint.inspections.gradle.compatible"), GradleDetector.COMPATIBILITY); } - - @NotNull - @Override - public AndroidLintQuickFix[] getQuickFixes(@NotNull String message) { - String before = GradleDetector.getOldValue(GradleDetector.COMPATIBILITY, message, RAW); - String after = GradleDetector.getNewValue(GradleDetector.COMPATIBILITY, message, RAW); - if (before != null && after != null) { - return new AndroidLintQuickFix[]{new ReplaceStringQuickFix("Change to " + after, before, after)}; - } - - return AndroidLintQuickFix.EMPTY_ARRAY; - } } public static class AndroidLintGradleCompatiblePluginInspection extends AndroidLintInspectionBase { @@ -783,94 +607,18 @@ public class AndroidLintInspectionToolProvider { public AndroidLintGradleDependencyInspection() { super(AndroidBundle.message("android.lint.inspections.gradle.dependency"), GradleDetector.DEPENDENCY); } - - @NotNull - @Override - public AndroidLintQuickFix[] getQuickFixes(@NotNull String message) { - String before = GradleDetector.getOldValue(GradleDetector.DEPENDENCY, message, RAW); - String after = GradleDetector.getNewValue(GradleDetector.DEPENDENCY, message, RAW); - if (before != null && after != null) { - return new AndroidLintQuickFix[]{new ReplaceStringQuickFix("Change to " + after, before, after)}; - } - - return AndroidLintQuickFix.EMPTY_ARRAY; - } } public static class AndroidLintGradleDeprecatedInspection extends AndroidLintInspectionBase { public AndroidLintGradleDeprecatedInspection() { super(AndroidBundle.message("android.lint.inspections.gradle.deprecated"), GradleDetector.DEPRECATED); } - - @NotNull - @Override - public AndroidLintQuickFix[] getQuickFixes(@NotNull final String message) { - String before = GradleDetector.getOldValue(GradleDetector.DEPRECATED, message, RAW); - String after = GradleDetector.getNewValue(GradleDetector.DEPRECATED, message, RAW); - if (before != null && after != null) { - return new AndroidLintQuickFix[]{new ReplaceStringQuickFix(null, before, after)}; - } - return AndroidLintQuickFix.EMPTY_ARRAY; - } } public static class AndroidLintGradleDynamicVersionInspection extends AndroidLintInspectionBase { public AndroidLintGradleDynamicVersionInspection() { super(AndroidBundle.message("android.lint.inspections.gradle.dynamic.version"), GradleDetector.PLUS); } - - @NotNull - @Override - public AndroidLintQuickFix[] getQuickFixes(@NotNull final PsiElement startElement, @NotNull PsiElement endElement, @NotNull String message) { - String before = GradleDetector.getOldValue(GradleDetector.PLUS, message, RAW); - if (before != null && before.endsWith("+")) { - final GradleCoordinate plus = GradleCoordinate.parseCoordinateString(before); - if (plus != null && plus.getArtifactId() != null) { - return new AndroidLintQuickFix[]{new ReplaceStringQuickFix("Replace with specific version", plus.getFullRevision(), "specific version") { - @Nullable - @Override - protected String getNewValue() { - String filter = plus.getFullRevision(); - assert filter.endsWith("+") : filter; - filter = filter.substring(0, filter.length() - 1); - - // If this coordinate points to an artifact in one of our repositories, mark it will a comment if they don't - // have that repository available. - String artifactId = plus.getArtifactId(); - if (RepositoryUrlManager.supports(plus.getArtifactId())) { - // First look for matches, where we don't allow preview versions - String libraryCoordinate = RepositoryUrlManager.get().getLibraryCoordinate(artifactId, filter, false); - if (libraryCoordinate != null) { - GradleCoordinate available = GradleCoordinate.parseCoordinateString(libraryCoordinate); - if (available != null) { - return available.getFullRevision(); - } - } - // If that didn't yield any matches, try again, this time allowing preview platforms. - // This is necessary if the artifact filter includes enough of a version where there are - // only preview matches. - libraryCoordinate = RepositoryUrlManager.get().getLibraryCoordinate(artifactId, filter, true); - if (libraryCoordinate != null) { - GradleCoordinate available = GradleCoordinate.parseCoordinateString(libraryCoordinate); - if (available != null) { - return available.toString(); - } - } - } - - // Regular Gradle dependency? Look in Gradle cache - GradleCoordinate found = GradleUtil.findLatestVersionInGradleCache(plus, filter, startElement.getProject()); - if (found != null) { - return found.getFullRevision(); - } - - return null; - } - }}; - } - } - return AndroidLintQuickFix.EMPTY_ARRAY; - } } public static class AndroidLintGradleGetterInspection extends AndroidLintInspectionBase { @@ -920,15 +668,6 @@ public class AndroidLintInspectionToolProvider { public AndroidLintTextFieldsInspection() { super(AndroidBundle.message("android.lint.inspections.text.fields"), TextFieldDetector.ISSUE); } - - @NotNull - @Override - public AndroidLintQuickFix[] getQuickFixes(@NotNull String message) { - return new AndroidLintQuickFix[]{ - new SetAttributeQuickFix(AndroidBundle.message("android.lint.fix.add.input.type.attribute"), - SdkConstants.ATTR_INPUT_TYPE, null) - }; - } } public static class AndroidLintTooManyViewsInspection extends AndroidLintInspectionBase { @@ -947,60 +686,30 @@ public class AndroidLintInspectionToolProvider { public AndroidLintTypographyDashesInspection() { super(AndroidBundle.message("android.lint.inspections.typography.dashes"), TypographyDetector.DASHES); } - - @NotNull - @Override - public AndroidLintQuickFix[] getQuickFixes(@NotNull String message) { - return new AndroidLintQuickFix[] {new TypographyQuickFix(myIssue, message)}; - } } public static class AndroidLintTypographyQuotesInspection extends AndroidLintTypographyInspectionBase { public AndroidLintTypographyQuotesInspection() { super(AndroidBundle.message("android.lint.inspections.typography.quotes"), TypographyDetector.QUOTES); } - - @NotNull - @Override - public AndroidLintQuickFix[] getQuickFixes(@NotNull String message) { - return new AndroidLintQuickFix[] {new TypographyQuickFix(myIssue, message)}; - } } public static class AndroidLintTypographyFractionsInspection extends AndroidLintTypographyInspectionBase { public AndroidLintTypographyFractionsInspection() { super(AndroidBundle.message("android.lint.inspections.typography.fractions"), TypographyDetector.FRACTIONS); } - - @NotNull - @Override - public AndroidLintQuickFix[] getQuickFixes(@NotNull String message) { - return new AndroidLintQuickFix[] {new TypographyQuickFix(myIssue, message)}; - } } public static class AndroidLintTypographyEllipsisInspection extends AndroidLintTypographyInspectionBase { public AndroidLintTypographyEllipsisInspection() { super(AndroidBundle.message("android.lint.inspections.typography.ellipsis"), TypographyDetector.ELLIPSIS); } - - @NotNull - @Override - public AndroidLintQuickFix[] getQuickFixes(@NotNull String message) { - return new AndroidLintQuickFix[] {new TypographyQuickFix(myIssue, message)}; - } } public static class AndroidLintTypographyOtherInspection extends AndroidLintTypographyInspectionBase { public AndroidLintTypographyOtherInspection() { super(AndroidBundle.message("android.lint.inspections.typography.other"), TypographyDetector.OTHER); } - - @NotNull - @Override - public AndroidLintQuickFix[] getQuickFixes(@NotNull String message) { - return new AndroidLintQuickFix[] {new TypographyQuickFix(myIssue, message)}; - } } public static class AndroidLintUseAlpha2Inspection extends AndroidLintInspectionBase { @@ -1029,76 +738,24 @@ public class AndroidLintInspectionToolProvider { public AndroidLintUselessLeafInspection() { super(AndroidBundle.message("android.lint.inspections.useless.leaf"), UselessViewDetector.USELESS_LEAF); } - - @NotNull - @Override - public AndroidLintQuickFix[] getQuickFixes(@NotNull String message) { - return new AndroidLintQuickFix[]{new RemoveUselessViewQuickFix(myIssue)}; - } } private abstract static class AndroidLintTypographyInspectionBase extends AndroidLintInspectionBase { public AndroidLintTypographyInspectionBase(String displayName, Issue issue) { super(displayName, issue); } - - @NotNull - @Override - public AndroidLintQuickFix[] getQuickFixes(@NotNull String message) { - return new AndroidLintQuickFix[] {new TypographyQuickFix(myIssue, message)}; - } } public static class AndroidLintNewApiInspection extends AndroidLintInspectionBase { public AndroidLintNewApiInspection() { super(AndroidBundle.message("android.lint.inspections.new.api"), ApiDetector.UNSUPPORTED); } - - @NotNull - @Override - public AndroidLintQuickFix[] getQuickFixes(@NotNull PsiElement startElement, @NotNull PsiElement endElement, @NotNull String message) { - return getApiDetectorFixes(ApiDetector.UNSUPPORTED, startElement, endElement, message); - } } public static class AndroidLintInlinedApiInspection extends AndroidLintInspectionBase { public AndroidLintInlinedApiInspection() { super(AndroidBundle.message("android.lint.inspections.inlined.api"), ApiDetector.INLINED); } - - @NotNull - @Override - public AndroidLintQuickFix[] getQuickFixes(@NotNull PsiElement startElement, @NotNull PsiElement endElement, @NotNull String message) { - return getApiDetectorFixes(ApiDetector.INLINED, startElement, endElement, message); - } - } - - private static AndroidLintQuickFix[] getApiDetectorFixes(@NotNull Issue issue, - @NotNull PsiElement startElement, - @SuppressWarnings("UnusedParameters") @NotNull PsiElement endElement, - @NotNull String message) { - // TODO: Return one for each parent context (declaration, method, class, outer class(es) - int api = ApiDetector.getRequiredVersion(issue, message, RAW); - if (api != -1) { - List list = Lists.newArrayList(); - PsiFile file = startElement.getContainingFile(); - if (file instanceof XmlFile) { - ResourceFolderType folderType = ResourceHelper.getFolderType(file); - if (folderType != null) { - FolderConfiguration config = ResourceHelper.getFolderConfiguration(file); - if (config != null) { - config.setVersionQualifier(new VersionQualifier(api)); - String folder = config.getFolderName(folderType); - list.add(OverrideResourceAction.createFix(folder)); - } - } - } - - list.add(new AddTargetApiQuickFix(api, startElement)); - - return list.toArray(new AndroidLintQuickFix[list.size()]); - } - return AndroidLintQuickFix.EMPTY_ARRAY; } public static class AndroidLintOverrideInspection extends AndroidLintInspectionBase { @@ -1117,35 +774,12 @@ public class AndroidLintInspectionToolProvider { public AndroidLintMipmapIconsInspection() { super(AndroidBundle.message("android.lint.inspections.mipmap.icons"), ManifestDetector.MIPMAP); } - - @NotNull - @Override - public AndroidLintQuickFix[] getQuickFixes(@NotNull PsiElement startElement, @NotNull PsiElement endElement, @NotNull String message) { - PsiElement parent = startElement.getParent(); - if (parent instanceof XmlAttribute) { - XmlAttribute attribute = (XmlAttribute)parent; - String value = attribute.getValue(); - if (value != null) { - ResourceUrl url = ResourceUrl.parse(value); - if (url != null && !url.framework) { - return new AndroidLintQuickFix[]{new MigrateDrawableToMipmapFix(url)}; - } - } - } - return AndroidLintQuickFix.EMPTY_ARRAY; - } } public static class AndroidLintMissingApplicationIconInspection extends AndroidLintInspectionBase { public AndroidLintMissingApplicationIconInspection() { super(AndroidBundle.message("android.lint.inspections.missing.application.icon"), ManifestDetector.APPLICATION_ICON); } - - @NotNull - @Override - public AndroidLintQuickFix[] getQuickFixes(@NotNull PsiElement startElement, @NotNull PsiElement endElement, @NotNull String message) { - return new AndroidLintQuickFix[] { new SetAttributeQuickFix("Set application icon", ATTR_ICON, null) }; - } } public static class AndroidLintResourceCycleInspection extends AndroidLintInspectionBase { @@ -1196,12 +830,6 @@ public class AndroidLintInspectionToolProvider { public AndroidLintAllowBackupInspection() { super(AndroidBundle.message("android.lint.inspections.allow.backup"), ManifestDetector.ALLOW_BACKUP); } - - @NotNull - @Override - public AndroidLintQuickFix[] getQuickFixes(@NotNull PsiElement startElement, @NotNull PsiElement endElement, @NotNull String message) { - return new AndroidLintQuickFix[] { new SetAttributeQuickFix("Set backup attribute", ATTR_ALLOW_BACKUP, null) }; - } } public static class AndroidLintButtonStyleInspection extends AndroidLintInspectionBase { @@ -1214,45 +842,12 @@ public class AndroidLintInspectionToolProvider { public AndroidLintByteOrderMarkInspection() { super(AndroidBundle.message("android.lint.inspections.byte.order.mark"), ByteOrderMarkDetector.BOM); } - - @NotNull - @Override - public AndroidLintQuickFix[] getQuickFixes(@NotNull PsiElement startElement, @NotNull PsiElement endElement, @NotNull String message) { - return new AndroidLintQuickFix[] { - new DefaultLintQuickFix("Remove byte order marks") { - @Override - public void apply(@NotNull PsiElement startElement, - @NotNull PsiElement endElement, - @NotNull AndroidQuickfixContexts.Context context) { - Document document = FileDocumentManager.getInstance().getDocument(startElement.getContainingFile().getVirtualFile()); - if (document != null) { - String text = document.getText(); - for (int i = text.length() - 1; i >= 0; i--) { - char c = text.charAt(i); - if (c == '\uFEFF') { - document.deleteString(i, i + 1); - } - } - } - } - } - }; - } } public static class AndroidLintCommitPrefEditsInspection extends AndroidLintInspectionBase { public AndroidLintCommitPrefEditsInspection() { super(AndroidBundle.message("android.lint.inspections.commit.pref.edits"), SharedPrefsDetector.ISSUE); } - - @NotNull - @Override - public AndroidLintQuickFix[] getQuickFixes(@NotNull String message) { - if (message.contains("commit") && message.contains("apply")) { - return new AndroidLintQuickFix[] { new ReplaceStringQuickFix("Replace commit() with apply()", "(commit)\\s*\\(", "apply") }; - } - return AndroidLintQuickFix.EMPTY_ARRAY; - } } public static class AndroidLintCustomViewStyleableInspection extends AndroidLintInspectionBase { @@ -1332,19 +927,6 @@ public class AndroidLintInspectionToolProvider { public AndroidLintIncludeLayoutParamInspection() { super(AndroidBundle.message("android.lint.inspections.include.layout.param"), IncludeDetector.ISSUE); } - - @NotNull - @Override - public AndroidLintQuickFix[] getQuickFixes(@NotNull String message) { - List fixes = Lists.newArrayListWithExpectedSize(2); - if (IncludeDetector.requestsWidth(message)) { - fixes.add(new SetAttributeQuickFix("Set layout_width", ATTR_LAYOUT_WIDTH, null)); - } - if (IncludeDetector.requestsHeight(message)) { - fixes.add(new SetAttributeQuickFix("Set layout_height", ATTR_LAYOUT_HEIGHT, null)); - } - return fixes.toArray(new AndroidLintQuickFix[fixes.size()]); - } } public static class AndroidLintInflateParamsInspection extends AndroidLintInspectionBase { @@ -1361,18 +943,6 @@ public class AndroidLintInspectionToolProvider { public AndroidLintInnerclassSeparatorInspection() { super(AndroidBundle.message("android.lint.inspections.innerclass.separator"), MissingClassDetector.INNERCLASS); } - - @Override - @NotNull - public AndroidLintQuickFix[] getQuickFixes(@NotNull String message) { - String current = MissingClassDetector.getOldValue(MissingClassDetector.INNERCLASS, message, RAW); - String proposed = MissingClassDetector.getNewValue(MissingClassDetector.INNERCLASS, message, RAW); - if (proposed != null && current != null) { - return new AndroidLintQuickFix[]{new ReplaceStringQuickFix(null, current, proposed)}; - } - - return AndroidLintQuickFix.EMPTY_ARRAY; - } } public static class AndroidLintInvalidIdInspection extends AndroidLintInspectionBase { @@ -1437,24 +1007,12 @@ public class AndroidLintInspectionToolProvider { public AndroidLintMenuTitleInspection() { super(AndroidBundle.message("android.lint.inspections.menu.title"), TitleDetector.ISSUE); } - - @Override - @NotNull - public AndroidLintQuickFix[] getQuickFixes(@NotNull String message) { - return new AndroidLintQuickFix[] { new SetAttributeQuickFix("Set title", ATTR_TITLE, null) }; - } } public static class AndroidLintMissingIdInspection extends AndroidLintInspectionBase { public AndroidLintMissingIdInspection() { super(AndroidBundle.message("android.lint.inspections.missing.id"), MissingIdDetector.ISSUE); } - - @Override - @NotNull - public AndroidLintQuickFix[] getQuickFixes(@NotNull String message) { - return new AndroidLintQuickFix[] { new SetAttributeQuickFix("Set id", ATTR_ID, null) }; - } } public static class AndroidLintMissingVersionInspection extends AndroidLintInspectionBase { @@ -1467,20 +1025,6 @@ public class AndroidLintInspectionToolProvider { super(AndroidBundle.message("android.lint.inspections.old.target.api"), ManifestDetector.TARGET_NEWER); } - @NotNull - @Override - public AndroidLintQuickFix[] getQuickFixes(@NotNull PsiElement startElement, @NotNull PsiElement endElement, @NotNull String message) { - String highest = Integer.toString(getHighestApi(startElement)); // TODO: preview platform?? - String label = "Update targetSdkVersion to " + highest; - if (startElement.getContainingFile() instanceof XmlFile) { - return new AndroidLintQuickFix[]{new ReplaceStringQuickFix(label, "targetSdkVersion\\s*=\\s*[\"'](.*)[\"']", highest)}; - } else if (startElement.getContainingFile() instanceof GroovyFile) { - return new AndroidLintQuickFix[]{new ReplaceStringQuickFix(label, null, highest)}; - } else{ - return AndroidLintQuickFix.EMPTY_ARRAY; - } - } - private static int getHighestApi(PsiElement element) { int max = SdkVersionInfo.HIGHEST_KNOWN_STABLE_API; AndroidFacet instance = AndroidFacet.getInstance(element); @@ -1505,15 +1049,6 @@ public class AndroidLintInspectionToolProvider { public AndroidLintOrientationInspection() { super(AndroidBundle.message("android.lint.inspections.orientation"), InefficientWeightDetector.ORIENTATION); } - - @Override - @NotNull - public AndroidLintQuickFix[] getQuickFixes(@NotNull String message) { - return new AndroidLintQuickFix[] { - new SetAttributeQuickFix("Set orientation=\"horizontal\" (default)", ATTR_ORIENTATION, VALUE_HORIZONTAL), - new SetAttributeQuickFix("Set orientation=\"false\" (changes layout)", ATTR_ORIENTATION, VALUE_VERTICAL) - }; - } } public static class AndroidLintOverrideAbstractInspection extends AndroidLintInspectionBase { @@ -1531,16 +1066,6 @@ public class AndroidLintInspectionToolProvider { public AndroidLintPropertyEscapeInspection() { super(AndroidBundle.message("android.lint.inspections.property.escape"), PropertyFileDetector.ESCAPE); } - - @Override - @NotNull - public AndroidLintQuickFix[] getQuickFixes(@NotNull String message) { - String escaped = PropertyFileDetector.getSuggestedEscape(message, RAW); - if (escaped != null) { - return new AndroidLintQuickFix[]{new ReplaceStringQuickFix(null, null, escaped)}; - } - return AndroidLintQuickFix.EMPTY_ARRAY; - } } public static class AndroidLintProtectedPermissionsInspection extends AndroidLintInspectionBase { @@ -1559,16 +1084,6 @@ public class AndroidLintInspectionToolProvider { public AndroidLintReferenceTypeInspection() { super(AndroidBundle.message("android.lint.inspections.reference.type"), DuplicateResourceDetector.TYPE_MISMATCH); } - - @Override - @NotNull - public AndroidLintQuickFix[] getQuickFixes(@NotNull String message) { - String expected = DuplicateResourceDetector.getExpectedType(message, RAW); - if (expected != null) { - return new AndroidLintQuickFix[]{new ReplaceStringQuickFix(null, "(@.*/)", "@" + expected + "/")}; - } - return AndroidLintQuickFix.EMPTY_ARRAY; - } } public static class AndroidLintRegisteredInspection extends AndroidLintInspectionBase { @@ -1592,25 +1107,11 @@ public class AndroidLintInspectionToolProvider { public AndroidLintResAutoInspection() { super(AndroidBundle.message("android.lint.inspections.res.auto"), NamespaceDetector.RES_AUTO); } - - @Override - @NotNull - public AndroidLintQuickFix[] getQuickFixes(@NotNull String message) { - return new AndroidLintQuickFix[] { new ConvertNamespaceQuickFix() }; - } } public static class AndroidLintSelectableTextInspection extends AndroidLintInspectionBase { public AndroidLintSelectableTextInspection() { super(AndroidBundle.message("android.lint.inspections.selectable.text"), TextViewDetector.SELECTABLE); } - - @Override - @NotNull - public AndroidLintQuickFix[] getQuickFixes(@NotNull String message) { - return new AndroidLintQuickFix[] { - new SetAttributeQuickFix("Set android:textIsSelectable=true", ATTR_TEXT_IS_SELECTABLE, VALUE_TRUE) - }; - } } public static class AndroidLintServiceCastInspection extends AndroidLintInspectionBase { @@ -1639,12 +1140,6 @@ public class AndroidLintInspectionToolProvider { public AndroidLintSignatureOrSystemPermissionsInspection() { super(AndroidBundle.message("android.lint.inspections.signature.or.system.permissions"), SignatureOrSystemDetector.ISSUE); } - - @Override - @NotNull - public AndroidLintQuickFix[] getQuickFixes(@NotNull String message) { - return new AndroidLintQuickFix[] { new ReplaceStringQuickFix(null, "signatureOrSystem", "signature") }; - } } public static class AndroidLintSimpleDateFormatInspection extends AndroidLintInspectionBase { @@ -1663,12 +1158,6 @@ public class AndroidLintInspectionToolProvider { public AndroidLintSpUsageInspection() { super(AndroidBundle.message("android.lint.inspections.sp.usage"), PxUsageDetector.DP_ISSUE); } - - @NotNull - @Override - public AndroidLintQuickFix[] getQuickFixes(@NotNull String message) { - return new AndroidLintQuickFix[]{new ReplaceStringQuickFix(null, "\\d+(di?p)", "sp")}; - } } // Maybe not relevant @@ -1676,31 +1165,12 @@ public class AndroidLintInspectionToolProvider { public AndroidLintStopShipInspection() { super(AndroidBundle.message("android.lint.inspections.stop.ship"), CommentDetector.STOP_SHIP); } - - @NotNull - @Override - public AndroidLintQuickFix[] getQuickFixes(@NotNull String message) { - // TODO: Remove comment if that's all that remains - return new AndroidLintQuickFix[]{new ReplaceStringQuickFix("Remove STOPSHIP", "(\\s*STOPSHIP)", "")}; - } } public static class AndroidLintStringShouldBeIntInspection extends AndroidLintInspectionBase { public AndroidLintStringShouldBeIntInspection() { super(AndroidBundle.message("android.lint.inspections.string.should.be.int"), GradleDetector.STRING_INTEGER); } - - @NotNull - @Override - public AndroidLintQuickFix[] getQuickFixes(@NotNull String message) { - String current = GradleDetector.getOldValue(GradleDetector.STRING_INTEGER, message, RAW); - String proposed = GradleDetector.getNewValue(GradleDetector.STRING_INTEGER, message, RAW); - if (proposed != null && current != null) { - return new AndroidLintQuickFix[]{new ReplaceStringQuickFix("Replace with integer", current, proposed)}; - } - - return AndroidLintQuickFix.EMPTY_ARRAY; - } } public static class AndroidLintSuspicious0dpInspection extends AndroidLintInspectionBase { @@ -1713,21 +1183,6 @@ public class AndroidLintInspectionToolProvider { public AndroidLintTyposInspection() { super(AndroidBundle.message("android.lint.inspections.typos"), TypoDetector.ISSUE); } - - @NotNull - @Override - public AndroidLintQuickFix[] getQuickFixes(@NotNull String message) { - List suggestions = TypoDetector.getSuggestions(message, RAW); - if (suggestions != null && !suggestions.isEmpty()) { - List fixes = Lists.newArrayListWithExpectedSize(suggestions.size()); - for (String suggestion : suggestions) { - fixes.add(new ReplaceStringQuickFix("Replace with \"" + suggestion + "\"", null, suggestion)); - } - return fixes.toArray(new AndroidLintQuickFix[fixes.size()]); - } - - return AndroidLintQuickFix.EMPTY_ARRAY; - } } public static class AndroidLintUniquePermissionInspection extends AndroidLintInspectionBase { @@ -1749,45 +1204,12 @@ public class AndroidLintInspectionToolProvider { public AndroidLintWrongCallInspection() { super(AndroidBundle.message("android.lint.inspections.wrong.call"), WrongCallDetector.ISSUE); } - - @NotNull - @Override - public AndroidLintQuickFix[] getQuickFixes(@NotNull String message) { - String current = WrongCallDetector.getOldValue(message, RAW); - String proposed = WrongCallDetector.getNewValue(message, RAW); - if (proposed != null && current != null) { - return new AndroidLintQuickFix[]{new ReplaceStringQuickFix("Replace call with " + proposed + "()", current, proposed)}; - } - - return AndroidLintQuickFix.EMPTY_ARRAY; - } } public static class AndroidLintWrongCaseInspection extends AndroidLintInspectionBase { public AndroidLintWrongCaseInspection() { super(AndroidBundle.message("android.lint.inspections.wrong.case"), WrongCaseDetector.WRONG_CASE); } - - @NotNull - @Override - public AndroidLintQuickFix[] getQuickFixes(@NotNull String message) { - final String current = WrongCaseDetector.getOldValue(message, RAW); - final String proposed = WrongCaseDetector.getNewValue(message, RAW); - if (proposed != null && current != null) { - return new AndroidLintQuickFix[]{new ReplaceStringQuickFix(null, current, proposed) { - @Override - protected void editAfter(@SuppressWarnings("UnusedParameters") @NotNull Document document) { - String text = document.getText(); - int index = text.indexOf(""); - if (index != -1) { - document.replaceString(index + 2, index + 2 + current.length(), proposed); - } - } - }}; - } - - return AndroidLintQuickFix.EMPTY_ARRAY; - } } public static class AndroidLintWrongFolderInspection extends AndroidLintInspectionBase { diff --git a/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/AndroidLintQuickFix.java b/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/AndroidLintQuickFix.java index 6ba34a6d937..29c44f17e74 100644 --- a/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/AndroidLintQuickFix.java +++ b/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/AndroidLintQuickFix.java @@ -1,18 +1,32 @@ -package org.jetbrains.android.inspections.lint; +/* + * 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 org.jetbrains.android.inspections.klint; import com.intellij.psi.PsiElement; import org.jetbrains.annotations.NotNull; -/** - * @author Eugene.Kudelevsky - */ public interface AndroidLintQuickFix { - AndroidLintQuickFix[] EMPTY_ARRAY = new AndroidLintQuickFix[0]; - - void apply(@NotNull PsiElement startElement, @NotNull PsiElement endElement, @NotNull AndroidQuickfixContexts.Context context); + AndroidLintQuickFix[] EMPTY_ARRAY = new AndroidLintQuickFix[0]; - boolean isApplicable(@NotNull PsiElement startElement, @NotNull PsiElement endElement, @NotNull AndroidQuickfixContexts.ContextType contextType); + void apply(@NotNull PsiElement startElement, @NotNull PsiElement endElement, @NotNull AndroidQuickfixContexts.Context context); - @NotNull - String getName(); + boolean isApplicable(@NotNull PsiElement startElement, @NotNull PsiElement endElement, @NotNull AndroidQuickfixContexts.ContextType contextType); + + @NotNull + String getName(); } + diff --git a/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/AndroidLintUtil.java b/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/AndroidLintUtil.java index 9e065bb1424..0eed06f4d1f 100644 --- a/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/AndroidLintUtil.java +++ b/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/AndroidLintUtil.java @@ -1,6 +1,22 @@ -package org.jetbrains.android.inspections.lint; +/* + * 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. + */ -import com.android.tools.lint.detector.api.Issue; +package org.jetbrains.android.inspections.klint; + +import com.android.tools.klint.detector.api.Issue; import com.intellij.codeHighlighting.HighlightDisplayLevel; import com.intellij.codeInsight.daemon.HighlightDisplayKey; import com.intellij.codeInspection.InspectionProfile; diff --git a/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/AndroidQuickfixContexts.java b/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/AndroidQuickfixContexts.java index 3f9217b728b..c1e5827318a 100644 --- a/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/AndroidQuickfixContexts.java +++ b/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/AndroidQuickfixContexts.java @@ -1,75 +1,88 @@ -package org.jetbrains.android.inspections.lint; +/* + * 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 org.jetbrains.android.inspections.klint; import com.intellij.openapi.editor.Editor; import org.jetbrains.annotations.NotNull; -/** - * @author Eugene.Kudelevsky - */ public class AndroidQuickfixContexts { - public static abstract class Context { - private final ContextType myType; + public static abstract class Context { + private final ContextType myType; - private Context(@NotNull ContextType type) { - myType = type; + private Context(@NotNull ContextType type) { + myType = type; + } + + @NotNull + public ContextType getType() { + return myType; + } } - @NotNull - public ContextType getType() { - return myType; - } - } - - public static class ContextType { - private ContextType() { - } - } - - public static class BatchContext extends Context { - public static final ContextType TYPE = new ContextType(); - private static final BatchContext INSTANCE = new BatchContext(); - - private BatchContext() { - super(TYPE); + public static class ContextType { + private ContextType() { + } } - @NotNull - public static BatchContext getInstance() { - return INSTANCE; - } - } + public static class BatchContext extends Context { + public static final ContextType TYPE = new ContextType(); + private static final BatchContext INSTANCE = new BatchContext(); - public static class EditorContext extends Context { - public static final ContextType TYPE = new ContextType(); - private final Editor myEditor; + private BatchContext() { + super(TYPE); + } - private EditorContext(@NotNull Editor editor) { - super(TYPE); - myEditor = editor; + @NotNull + public static BatchContext getInstance() { + return INSTANCE; + } } - @NotNull - public Editor getEditor() { - return myEditor; + public static class EditorContext extends Context { + public static final ContextType TYPE = new ContextType(); + private final Editor myEditor; + + private EditorContext(@NotNull Editor editor) { + super(TYPE); + myEditor = editor; + } + + @NotNull + public Editor getEditor() { + return myEditor; + } + + @NotNull + public static EditorContext getInstance(@NotNull Editor editor) { + return new EditorContext(editor); + } } - @NotNull - public static EditorContext getInstance(@NotNull Editor editor) { - return new EditorContext(editor); - } - } + public static class DesignerContext extends Context { + public static final ContextType TYPE = new ContextType(); + private static final DesignerContext INSTANCE = new DesignerContext(); - public static class DesignerContext extends Context { - public static final ContextType TYPE = new ContextType(); - private static final DesignerContext INSTANCE = new DesignerContext(); + private DesignerContext() { + super(TYPE); + } - private DesignerContext() { - super(TYPE); + @NotNull + public static DesignerContext getInstance() { + return INSTANCE; + } } - - @NotNull - public static DesignerContext getInstance() { - return INSTANCE; - } - } -} +} \ No newline at end of file diff --git a/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/DomPsiConverter.java b/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/DomPsiConverter.java index caa75413bb8..d0b29f003a3 100644 --- a/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/DomPsiConverter.java +++ b/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/DomPsiConverter.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.jetbrains.android.inspections.lint; +package org.jetbrains.android.inspections.klint; import com.intellij.openapi.application.Application; import com.intellij.openapi.application.ApplicationManager; diff --git a/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/DomPsiParser.java b/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/DomPsiParser.java index 985e0aa3209..edbbe97d166 100644 --- a/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/DomPsiParser.java +++ b/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/DomPsiParser.java @@ -13,16 +13,16 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.jetbrains.android.inspections.lint; +package org.jetbrains.android.inspections.klint; import com.android.annotations.NonNull; import com.android.annotations.Nullable; -import com.android.tools.lint.client.api.LintClient; -import com.android.tools.lint.client.api.XmlParser; -import com.android.tools.lint.detector.api.DefaultPosition; -import com.android.tools.lint.detector.api.Location; -import com.android.tools.lint.detector.api.Position; -import com.android.tools.lint.detector.api.XmlContext; +import com.android.tools.klint.client.api.LintClient; +import com.android.tools.klint.client.api.XmlParser; +import com.android.tools.klint.detector.api.DefaultPosition; +import com.android.tools.klint.detector.api.Location; +import com.android.tools.klint.detector.api.Position; +import com.android.tools.klint.detector.api.XmlContext; import com.intellij.openapi.application.AccessToken; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.util.Computable; @@ -57,13 +57,13 @@ class DomPsiParser extends XmlParser { @Override public int getNodeStartOffset(@NonNull XmlContext context, @NonNull Node node) { - TextRange textRange = DomPsiConverter.getTextRange(node); + TextRange textRange = org.jetbrains.android.inspections.klint.DomPsiConverter.getTextRange(node); return textRange.getStartOffset(); } @Override public int getNodeEndOffset(@NonNull XmlContext context, @NonNull Node node) { - TextRange textRange = DomPsiConverter.getTextRange(node); + TextRange textRange = org.jetbrains.android.inspections.klint.DomPsiConverter.getTextRange(node); return textRange.getEndOffset(); } @@ -92,7 +92,7 @@ class DomPsiParser extends XmlParser { XmlFile xmlFile = (XmlFile)psiFile; try { - return DomPsiConverter.convert(xmlFile); + return org.jetbrains.android.inspections.klint.DomPsiConverter.convert(xmlFile); } catch (Throwable t) { myClient.log(t, "Failed converting PSI parse tree to DOM for file %1$s", context.file.getPath()); @@ -103,7 +103,7 @@ class DomPsiParser extends XmlParser { @NonNull @Override public Location getLocation(@NonNull XmlContext context, @NonNull Node node) { - TextRange textRange = DomPsiConverter.getTextRange(node); + TextRange textRange = org.jetbrains.android.inspections.klint.DomPsiConverter.getTextRange(node); Position start = new DefaultPosition(-1, -1, textRange.getStartOffset()); Position end = new DefaultPosition(-1, -1, textRange.getEndOffset()); return Location.create(context.file, start, end); @@ -112,7 +112,7 @@ class DomPsiParser extends XmlParser { @NonNull @Override public Location getLocation(@NonNull XmlContext context, @NonNull Node node, int startDelta, int endDelta) { - TextRange textRange = DomPsiConverter.getTextRange(node); + TextRange textRange = org.jetbrains.android.inspections.klint.DomPsiConverter.getTextRange(node); Position start = new DefaultPosition(-1, -1, textRange.getStartOffset() + startDelta); Position end = new DefaultPosition(-1, -1, textRange.getStartOffset() + endDelta); return Location.create(context.file, start, end); @@ -121,7 +121,7 @@ class DomPsiParser extends XmlParser { @NonNull @Override public Location getNameLocation(@NonNull XmlContext context, @NonNull Node node) { - TextRange textRange = DomPsiConverter.getTextNameRange(node); + TextRange textRange = org.jetbrains.android.inspections.klint.DomPsiConverter.getTextNameRange(node); Position start = new DefaultPosition(-1, -1, textRange.getStartOffset()); Position end = new DefaultPosition(-1, -1, textRange.getEndOffset()); return Location.create(context.file, start, end); @@ -130,7 +130,7 @@ class DomPsiParser extends XmlParser { @NonNull @Override public Location getValueLocation(@NonNull XmlContext context, @NonNull Attr node) { - TextRange textRange = DomPsiConverter.getTextValueRange(node); + TextRange textRange = org.jetbrains.android.inspections.klint.DomPsiConverter.getTextValueRange(node); Position start = new DefaultPosition(-1, -1, textRange.getStartOffset()); Position end = new DefaultPosition(-1, -1, textRange.getEndOffset()); return Location.create(context.file, start, end); @@ -163,7 +163,7 @@ class DomPsiParser extends XmlParser { } }); } - TextRange textRange = DomPsiConverter.getTextRange(myNode); + TextRange textRange = org.jetbrains.android.inspections.klint.DomPsiConverter.getTextRange(myNode); Position start = new DefaultPosition(-1, -1, textRange.getStartOffset()); Position end = new DefaultPosition(-1, -1, textRange.getEndOffset()); return Location.create(myFile, start, end); diff --git a/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/IntellijApiDetector.java b/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/IntellijApiDetector.java index 0c524d698fa..5dec5140022 100755 --- a/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/IntellijApiDetector.java +++ b/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/IntellijApiDetector.java @@ -13,33 +13,29 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.jetbrains.android.inspections.lint; +package org.jetbrains.android.inspections.klint; import com.android.annotations.NonNull; import com.android.annotations.Nullable; import com.android.sdklib.SdkVersionInfo; -import com.android.tools.lint.checks.ApiDetector; -import com.android.tools.lint.checks.ApiLookup; -import com.android.tools.lint.detector.api.*; -import com.intellij.codeInsight.ExceptionUtil; +import com.android.tools.klint.checks.ApiDetector; +import com.android.tools.klint.checks.ApiLookup; import com.intellij.openapi.application.ApplicationManager; import com.intellij.psi.*; import com.intellij.psi.impl.source.PsiClassReferenceType; import com.intellij.psi.tree.IElementType; import com.intellij.psi.util.MethodSignatureUtil; import com.intellij.psi.util.PsiTreeUtil; -import lombok.ast.AstVisitor; -import lombok.ast.CompilationUnit; -import lombok.ast.ForwardingAstVisitor; -import lombok.ast.Node; import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.uast.UFile; +import org.jetbrains.uast.check.UastAndroidContext; +import org.jetbrains.uast.visitor.UastVisitor; -import java.util.Collections; import java.util.EnumSet; -import java.util.List; -import static org.jetbrains.android.inspections.lint.IntellijLintUtils.SUPPRESS_LINT_FQCN; -import static org.jetbrains.android.inspections.lint.IntellijLintUtils.SUPPRESS_WARNINGS_FQCN; +import static org.jetbrains.android.inspections.klint.IntellijLintUtils.SUPPRESS_LINT_FQCN; +import static org.jetbrains.android.inspections.klint.IntellijLintUtils.SUPPRESS_WARNINGS_FQCN; /** * Intellij-specific version of the {@link ApiDetector} which uses the PSI structure @@ -52,40 +48,36 @@ import static org.jetbrains.android.inspections.lint.IntellijLintUtils.SUPPRESS_ */ public class IntellijApiDetector extends ApiDetector { @SuppressWarnings("unchecked") - static final Implementation IMPLEMENTATION = new Implementation( - IntellijApiDetector.class, - EnumSet.of(Scope.RESOURCE_FILE, Scope.MANIFEST, Scope.JAVA_FILE), - Scope.MANIFEST_SCOPE, - Scope.RESOURCE_FILE_SCOPE, - Scope.JAVA_FILE_SCOPE + static final com.android.tools.klint.detector.api.Implementation IMPLEMENTATION = new com.android.tools.klint.detector.api.Implementation( + IntellijApiDetector.class, + EnumSet.of(com.android.tools.klint.detector.api.Scope.RESOURCE_FILE, com.android.tools.klint.detector.api.Scope.MANIFEST, com.android.tools.klint.detector.api.Scope.SOURCE_FILE), + com.android.tools.klint.detector.api.Scope.MANIFEST_SCOPE, + com.android.tools.klint.detector.api.Scope.RESOURCE_FILE_SCOPE, + com.android.tools.klint.detector.api.Scope.JAVA_FILE_SCOPE ); @NonNls private static final String TARGET_API_FQCN = "android.annotation.TargetApi"; private static final String SDK_INT = "SDK_INT"; - @Nullable @Override - public List> getApplicableNodeTypes() { - return Collections.>singletonList(CompilationUnit.class); + public UastVisitor createUastVisitor(final UastAndroidContext context) { + return new UastVisitor() { + @Override + public boolean visitFile(@NotNull UFile node) { + check(context); + return true; + } + }; } - @Nullable - @Override - public AstVisitor createJavaVisitor(@NonNull final JavaContext context) { - return new ForwardingAstVisitor() { - @Override - public boolean visitCompilationUnit(CompilationUnit node) { - check(context); - return true; - } - }; - } - - private void check(final JavaContext context) { + private void check(final UastAndroidContext uastContext) { if (mApiDatabase == null) { return; } + + final com.android.tools.klint.detector.api.JavaContext context = uastContext.getLintContext(); + ApplicationManager.getApplication().runReadAction(new Runnable() { @Override public void run() { @@ -166,7 +158,7 @@ public class IntellijApiDetector extends ApiDetector { } private class ApiCheckVisitor extends JavaRecursiveElementVisitor { - private final Context myContext; + private final com.android.tools.klint.detector.api.Context myContext; private boolean mySeenSuppress; private boolean mySeenTargetApi; private final PsiClass myClass; @@ -175,7 +167,7 @@ public class IntellijApiDetector extends ApiDetector { private boolean myCheckOverride; private String myFrameworkParent; - public ApiCheckVisitor(Context context, PsiClass clz, PsiFile file) { + public ApiCheckVisitor(com.android.tools.klint.detector.api.Context context, PsiClass clz, PsiFile file) { myContext = context; myClass = clz; myFile = file; @@ -194,7 +186,7 @@ public class IntellijApiDetector extends ApiDetector { || fqcn.startsWith("java.") //$NON-NLS-1$ || fqcn.startsWith("javax.")) { //$NON-NLS-1$ if (!fqcn.equals(CommonClassNames.JAVA_LANG_OBJECT)) { - myFrameworkParent = ClassContext.getInternalName(fqcn); + myFrameworkParent = com.android.tools.klint.detector.api.ClassContext.getInternalName(fqcn); } break; } @@ -268,7 +260,7 @@ public class IntellijApiDetector extends ApiDetector { if (locationNode == null) { locationNode = method; } - Location location = IntellijLintUtils.getLocation(myContext.file, locationNode); + com.android.tools.klint.detector.api.Location location = IntellijLintUtils.getLocation(myContext.file, locationNode); myContext.report(OVERRIDE, location, message); } } @@ -307,7 +299,7 @@ public class IntellijApiDetector extends ApiDetector { continue; } - Location location; + com.android.tools.klint.detector.api.Location location; if (type instanceof PsiClassReferenceType) { PsiReference reference = ((PsiClassReferenceType)type).getReference(); PsiElement element = reference.getElement(); @@ -359,13 +351,13 @@ public class IntellijApiDetector extends ApiDetector { return; } - Location location = IntellijLintUtils.getLocation(myContext.file, expression); + com.android.tools.klint.detector.api.Location location = IntellijLintUtils.getLocation(myContext.file, expression); String fqcn = containingClass.getQualifiedName(); String message = String.format( "Field requires API level %1$d (current min is %2$d): %3$s", api, minSdk, fqcn + '#' + name); - Issue issue = UNSUPPORTED; + com.android.tools.klint.detector.api.Issue issue = UNSUPPORTED; // When accessing primitive types or Strings, the values get copied into // the class files (e.g. get inlined) which has a separate issue type: // INLINED. @@ -398,7 +390,7 @@ public class IntellijApiDetector extends ApiDetector { if (isSuppressed(api, statement, minSdk)) { return; } - Location location = IntellijLintUtils.getLocation(myContext.file, resourceList); + com.android.tools.klint.detector.api.Location location = IntellijLintUtils.getLocation(myContext.file, resourceList); String message = String.format("Try-with-resources requires API level %1$d (current min is %2$d)", api, minSdk); myContext.report(UNSUPPORTED, location, message); } @@ -447,7 +439,7 @@ public class IntellijApiDetector extends ApiDetector { continue; } - Location location; + com.android.tools.klint.detector.api.Location location; location = IntellijLintUtils.getLocation(myContext.file, reference); String fqcn = resolved.getName(); String message = String.format("Class requires API level %1$d (current min is %2$d): %3$s", api, minSdk, fqcn); @@ -669,7 +661,7 @@ public class IntellijApiDetector extends ApiDetector { if (locationNode == null) { locationNode = expression; } - Location location = IntellijLintUtils.getLocation(myContext.file, locationNode); + com.android.tools.klint.detector.api.Location location = IntellijLintUtils.getLocation(myContext.file, locationNode); String message = String.format("Call requires API level %1$d (current min is %2$d): %3$s", api, minSdk, fqcn + '#' + method.getName()); @@ -772,7 +764,7 @@ public class IntellijApiDetector extends ApiDetector { return isAndedWithConditional(inner.getLOperand(), api, before) || inner.getROperand() != before && isAndedWithConditional(inner.getROperand(), api, before); } else if (inner.getLOperand() instanceof PsiReferenceExpression && - SDK_INT.equals(((PsiReferenceExpression)inner.getLOperand()).getReferenceName())) { + SDK_INT.equals(((PsiReferenceExpression)inner.getLOperand()).getReferenceName())) { int level = -1; IElementType tokenType = inner.getOperationTokenType(); PsiExpression right = inner.getROperand(); diff --git a/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/IntellijLintClient.java b/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/IntellijLintClient.java index a71db3ecda9..85b2c54a784 100644 --- a/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/IntellijLintClient.java +++ b/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/IntellijLintClient.java @@ -1,4 +1,20 @@ -package org.jetbrains.android.inspections.lint; +/* + * 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 org.jetbrains.android.inspections.klint; import com.android.annotations.NonNull; import com.android.builder.model.AndroidProject; @@ -12,9 +28,8 @@ import com.android.tools.idea.gradle.util.Projects; import com.android.tools.idea.rendering.AppResourceRepository; import com.android.tools.idea.rendering.LocalResourceRepository; import com.android.tools.idea.sdk.IdeSdks; -import com.android.tools.lint.checks.ApiLookup; -import com.android.tools.lint.client.api.*; -import com.android.tools.lint.detector.api.*; +import com.android.tools.klint.client.api.*; +import com.android.tools.klint.checks.ApiLookup; import com.intellij.analysis.AnalysisScope; import com.intellij.openapi.Disposable; import com.intellij.openapi.application.ApplicationManager; @@ -48,6 +63,7 @@ import org.jetbrains.android.sdk.AndroidSdkData; import org.jetbrains.android.sdk.AndroidSdkType; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.jetbrains.uast.UastConverter; import java.io.File; import java.io.IOException; @@ -58,27 +74,31 @@ import java.util.Collections; import java.util.List; import java.util.Map; -import static com.android.tools.lint.detector.api.TextFormat.RAW; +import static com.android.tools.klint.detector.api.TextFormat.RAW; /** - * Implementation of the {@linkplain LintClient} API for executing lint within the IDE: + * Implementation of the {@linkplain com.android.tools.klint.client.api.LintClient} API for executing lint within the IDE: * reading files, reporting issues, logging errors, etc. */ -public class IntellijLintClient extends LintClient implements Disposable { +public class IntellijLintClient extends com.android.tools.klint.client.api.LintClient implements Disposable { protected static final Logger LOG = Logger.getInstance("#org.jetbrains.android.inspections.IntellijLintClient"); @NonNull protected Project myProject; - @Nullable protected Map myModuleMap; + @Nullable protected Map myModuleMap; + + @NonNull + private List myConverters; protected IntellijLintClient(@NonNull Project project) { myProject = project; + myConverters = LintLanguageExtension.getConverters(project); } /** Creates a lint client for batch inspections */ public static IntellijLintClient forBatch(@NotNull Project project, - @NotNull Map>> problemMap, + @NotNull Map>> problemMap, @NotNull AnalysisScope scope, - @NotNull List issues) { + @NotNull List issues) { return new BatchLintClient(project, problemMap, scope, issues); } @@ -102,7 +122,7 @@ public class IntellijLintClient extends LintClient implements Disposable { @Nullable protected Module findModuleForLintProject(@NotNull Project project, - @NotNull com.android.tools.lint.detector.api.Project lintProject) { + @NotNull com.android.tools.klint.detector.api.Project lintProject) { if (myModuleMap != null) { Module module = myModuleMap.get(lintProject); if (module != null) { @@ -114,12 +134,12 @@ public class IntellijLintClient extends LintClient implements Disposable { return vDir != null ? ModuleUtilCore.findModuleForFile(vDir, project) : null; } - void setModuleMap(@Nullable Map moduleMap) { + void setModuleMap(@Nullable Map moduleMap) { myModuleMap = moduleMap; } @Override - public Configuration getConfiguration(@NonNull com.android.tools.lint.detector.api.Project project) { + public com.android.tools.klint.client.api.Configuration getConfiguration(@NonNull com.android.tools.klint.detector.api.Project project) { if (project.isGradleProject() && project.isAndroidProject() && !project.isLibrary()) { AndroidProject model = project.getGradleProjectModel(); if (model != null) { @@ -127,30 +147,30 @@ public class IntellijLintClient extends LintClient implements Disposable { LintOptions lintOptions = model.getLintOptions(); final Map overrides = lintOptions.getSeverityOverrides(); if (overrides != null && !overrides.isEmpty()) { - return new DefaultConfiguration(this, project, null) { + return new com.android.tools.klint.client.api.DefaultConfiguration(this, project, null) { @NonNull @Override - public Severity getSeverity(@NonNull Issue issue) { + public com.android.tools.klint.detector.api.Severity getSeverity(@NonNull com.android.tools.klint.detector.api.Issue issue) { Integer severity = overrides.get(issue.getId()); if (severity != null) { switch (severity.intValue()) { case LintOptions.SEVERITY_FATAL: - return Severity.FATAL; + return com.android.tools.klint.detector.api.Severity.FATAL; case LintOptions.SEVERITY_ERROR: - return Severity.ERROR; + return com.android.tools.klint.detector.api.Severity.ERROR; case LintOptions.SEVERITY_WARNING: - return Severity.WARNING; + return com.android.tools.klint.detector.api.Severity.WARNING; case LintOptions.SEVERITY_INFORMATIONAL: - return Severity.INFORMATIONAL; + return com.android.tools.klint.detector.api.Severity.INFORMATIONAL; case LintOptions.SEVERITY_IGNORE: default: - return Severity.IGNORE; + return com.android.tools.klint.detector.api.Severity.IGNORE; } } // This is a LIST lookup. I should make this faster! if (!getIssues().contains(issue)) { - return Severity.IGNORE; + return com.android.tools.klint.detector.api.Severity.IGNORE; } return super.getSeverity(issue); @@ -162,25 +182,25 @@ public class IntellijLintClient extends LintClient implements Disposable { } } } - return new DefaultConfiguration(this, project, null) { + return new com.android.tools.klint.client.api.DefaultConfiguration(this, project, null) { @Override - public boolean isEnabled(@NonNull Issue issue) { + public boolean isEnabled(@NonNull com.android.tools.klint.detector.api.Issue issue) { return getIssues().contains(issue) && super.isEnabled(issue); } }; } @Override - public void report(@NonNull Context context, - @NonNull Issue issue, - @NonNull Severity severity, - @Nullable Location location, + public void report(@NonNull com.android.tools.klint.detector.api.Context context, + @NonNull com.android.tools.klint.detector.api.Issue issue, + @NonNull com.android.tools.klint.detector.api.Severity severity, + @Nullable com.android.tools.klint.detector.api.Location location, @NonNull String message, - @NonNull TextFormat format) { + @NonNull com.android.tools.klint.detector.api.TextFormat format) { assert false : message; } - @NonNull protected List getIssues() { + @NonNull protected List getIssues() { return Collections.emptyList(); } @@ -194,9 +214,9 @@ public class IntellijLintClient extends LintClient implements Disposable { * linked location, and so on.This is necessary since IntelliJ problems don't have secondary locations; instead, we create one * problem for each location associated with the lint error. */ - protected void reportSecondary(@NonNull Context context, @NonNull Issue issue, @NonNull Severity severity, @NonNull Location location, - @NonNull String message, @NonNull TextFormat format) { - Location secondary = location.getSecondary(); + protected void reportSecondary(@NonNull com.android.tools.klint.detector.api.Context context, @NonNull com.android.tools.klint.detector.api.Issue issue, @NonNull com.android.tools.klint.detector.api.Severity severity, @NonNull com.android.tools.klint.detector.api.Location location, + @NonNull String message, @NonNull com.android.tools.klint.detector.api.TextFormat format) { + com.android.tools.klint.detector.api.Location secondary = location.getSecondary(); if (secondary != null) { if (secondary.getMessage() != null) { message = message + " (" + secondary.getMessage() + ")"; @@ -206,14 +226,14 @@ public class IntellijLintClient extends LintClient implements Disposable { } @Override - public void log(@NonNull Severity severity, @Nullable Throwable exception, @Nullable String format, @Nullable Object... args) { - if (severity == Severity.ERROR || severity == Severity.FATAL) { + public void log(@NonNull com.android.tools.klint.detector.api.Severity severity, @Nullable Throwable exception, @Nullable String format, @Nullable Object... args) { + if (severity == com.android.tools.klint.detector.api.Severity.ERROR || severity == com.android.tools.klint.detector.api.Severity.FATAL) { if (format != null) { LOG.error(String.format(format, args), exception); } else if (exception != null) { LOG.error(exception); } - } else if (severity == Severity.WARNING) { + } else if (severity == com.android.tools.klint.detector.api.Severity.WARNING) { if (format != null) { LOG.warn(String.format(format, args), exception); } else if (exception != null) { @@ -229,26 +249,20 @@ public class IntellijLintClient extends LintClient implements Disposable { } @Override - public XmlParser getXmlParser() { + public com.android.tools.klint.client.api.XmlParser getXmlParser() { return new DomPsiParser(this); } - @Nullable - @Override - public JavaParser getJavaParser(@Nullable com.android.tools.lint.detector.api.Project project) { - return new LombokPsiParser(this); - } - @NonNull @Override - public List getJavaClassFolders(@NonNull com.android.tools.lint.detector.api.Project project) { + public List getJavaClassFolders(@NonNull com.android.tools.klint.detector.api.Project project) { // todo: implement when class files checking detectors will be available return Collections.emptyList(); } @NonNull @Override - public List getJavaLibraries(@NonNull com.android.tools.lint.detector.api.Project project) { + public List getJavaLibraries(@NonNull com.android.tools.klint.detector.api.Project project) { // todo: implement return Collections.emptyList(); } @@ -364,7 +378,7 @@ public class IntellijLintClient extends LintClient implements Disposable { } @Override - public boolean isGradleProject(com.android.tools.lint.detector.api.Project project) { + public boolean isGradleProject(com.android.tools.klint.detector.api.Project project) { Module module = getModule(); if (module != null) { AndroidFacet facet = AndroidFacet.getInstance(module); @@ -373,6 +387,16 @@ public class IntellijLintClient extends LintClient implements Disposable { return Projects.isGradleProject(myProject); } + @Override + public Project getProject() { + return myProject; + } + + @Override + public List getConverters() { + return myConverters; + } + // Overridden such that lint doesn't complain about missing a bin dir property in the event // that no SDK is configured @Override @@ -429,35 +453,35 @@ public class IntellijLintClient extends LintClient implements Disposable { @NonNull @Override - protected List getIssues() { + protected List getIssues() { return myState.getIssues(); } @Override - public void report(@NonNull Context context, - @NonNull Issue issue, - @NonNull Severity severity, - @Nullable Location location, + public void report(@NonNull com.android.tools.klint.detector.api.Context context, + @NonNull com.android.tools.klint.detector.api.Issue issue, + @NonNull com.android.tools.klint.detector.api.Severity severity, + @Nullable com.android.tools.klint.detector.api.Location location, @NonNull String message, - @NonNull TextFormat format) { + @NonNull com.android.tools.klint.detector.api.TextFormat format) { if (location != null) { final File file = location.getFile(); final VirtualFile vFile = LocalFileSystem.getInstance().findFileByIoFile(file); if (myState.getMainFile().equals(vFile)) { - final Position start = location.getStart(); - final Position end = location.getEnd(); + final com.android.tools.klint.detector.api.Position start = location.getStart(); + final com.android.tools.klint.detector.api.Position end = location.getEnd(); final TextRange textRange = start != null && end != null && start.getOffset() <= end.getOffset() ? new TextRange(start.getOffset(), end.getOffset()) : TextRange.EMPTY_RANGE; - Severity configuredSeverity = severity != issue.getDefaultSeverity() ? severity : null; + com.android.tools.klint.detector.api.Severity configuredSeverity = severity != issue.getDefaultSeverity() ? severity : null; message = format.convertTo(message, RAW); - myState.getProblems().add(new ProblemData(issue, message, textRange, configuredSeverity)); + myState.getProblems().add(new org.jetbrains.android.inspections.klint.ProblemData(issue, message, textRange, configuredSeverity)); } - Location secondary = location.getSecondary(); + com.android.tools.klint.detector.api.Location secondary = location.getSecondary(); if (secondary != null && myState.getMainFile().equals(LocalFileSystem.getInstance().findFileByIoFile(secondary.getFile()))) { reportSecondary(context, issue, severity, location, message, format); } @@ -521,7 +545,7 @@ public class IntellijLintClient extends LintClient implements Disposable { @NonNull @Override - public List getJavaSourceFolders(@NonNull com.android.tools.lint.detector.api.Project project) { + public List getJavaSourceFolders(@NonNull com.android.tools.klint.detector.api.Project project) { final VirtualFile[] sourceRoots = ModuleRootManager.getInstance(myState.getModule()).getSourceRoots(false); final List result = new ArrayList(sourceRoots.length); @@ -533,10 +557,10 @@ public class IntellijLintClient extends LintClient implements Disposable { @NonNull @Override - public List getResourceFolders(@NonNull com.android.tools.lint.detector.api.Project project) { + public List getResourceFolders(@NonNull com.android.tools.klint.detector.api.Project project) { AndroidFacet facet = AndroidFacet.getInstance(myState.getModule()); if (facet != null) { - return IntellijLintUtils.getResourceDirectories(facet); + return org.jetbrains.android.inspections.klint.IntellijLintUtils.getResourceDirectories(facet); } return super.getResourceFolders(project); } @@ -544,14 +568,14 @@ public class IntellijLintClient extends LintClient implements Disposable { /** Lint client used for batch operations */ private static class BatchLintClient extends IntellijLintClient { - private final Map>> myProblemMap; + private final Map>> myProblemMap; private final AnalysisScope myScope; - private final List myIssues; + private final List myIssues; public BatchLintClient(@NotNull Project project, - @NotNull Map>> problemMap, + @NotNull Map>> problemMap, @NotNull AnalysisScope scope, - @NotNull List issues) { + @NotNull List issues) { super(project); myProblemMap = problemMap; myScope = scope; @@ -567,17 +591,17 @@ public class IntellijLintClient extends LintClient implements Disposable { @NonNull @Override - protected List getIssues() { + protected List getIssues() { return myIssues; } @Override - public void report(@NonNull Context context, - @NonNull Issue issue, - @NonNull Severity severity, - @Nullable Location location, + public void report(@NonNull com.android.tools.klint.detector.api.Context context, + @NonNull com.android.tools.klint.detector.api.Issue issue, + @NonNull com.android.tools.klint.detector.api.Severity severity, + @Nullable com.android.tools.klint.detector.api.Location location, @NonNull String message, - @NonNull TextFormat format) { + @NonNull com.android.tools.klint.detector.api.TextFormat format) { VirtualFile vFile = null; File file = null; @@ -607,7 +631,7 @@ public class IntellijLintClient extends LintClient implements Disposable { if (myScope.getScopeType() == AnalysisScope.PROJECT) { inScope = true; } else if (myScope.getScopeType() == AnalysisScope.MODULE || - myScope.getScopeType() == AnalysisScope.MODULES) { + myScope.getScopeType() == AnalysisScope.MODULES) { final Module module = findModuleForLintProject(myProject, context.getProject()); if (module != null && myScope.containsModule(module)) { inScope = true; @@ -618,31 +642,31 @@ public class IntellijLintClient extends LintClient implements Disposable { if (inScope) { file = new File(PathUtil.getCanonicalPath(file.getPath())); - Map> file2ProblemList = myProblemMap.get(issue); + Map> file2ProblemList = myProblemMap.get(issue); if (file2ProblemList == null) { - file2ProblemList = new HashMap>(); + file2ProblemList = new HashMap>(); myProblemMap.put(issue, file2ProblemList); } - List problemList = file2ProblemList.get(file); + List problemList = file2ProblemList.get(file); if (problemList == null) { - problemList = new ArrayList(); + problemList = new ArrayList(); file2ProblemList.put(file, problemList); } TextRange textRange = TextRange.EMPTY_RANGE; if (location != null) { - final Position start = location.getStart(); - final Position end = location.getEnd(); + final com.android.tools.klint.detector.api.Position start = location.getStart(); + final com.android.tools.klint.detector.api.Position end = location.getEnd(); if (start != null && end != null && start.getOffset() <= end.getOffset()) { textRange = new TextRange(start.getOffset(), end.getOffset()); } } - Severity configuredSeverity = severity != issue.getDefaultSeverity() ? severity : null; + com.android.tools.klint.detector.api.Severity configuredSeverity = severity != issue.getDefaultSeverity() ? severity : null; message = format.convertTo(message, RAW); - problemList.add(new ProblemData(issue, message, textRange, configuredSeverity)); + problemList.add(new org.jetbrains.android.inspections.klint.ProblemData(issue, message, textRange, configuredSeverity)); if (location != null && location.getSecondary() != null) { reportSecondary(context, issue, severity, location, message, format); @@ -652,7 +676,7 @@ public class IntellijLintClient extends LintClient implements Disposable { @NonNull @Override - public List getJavaSourceFolders(@NonNull com.android.tools.lint.detector.api.Project project) { + public List getJavaSourceFolders(@NonNull com.android.tools.klint.detector.api.Project project) { final Module module = findModuleForLintProject(myProject, project); if (module == null) { return Collections.emptyList(); @@ -668,12 +692,12 @@ public class IntellijLintClient extends LintClient implements Disposable { @NonNull @Override - public List getResourceFolders(@NonNull com.android.tools.lint.detector.api.Project project) { + public List getResourceFolders(@NonNull com.android.tools.klint.detector.api.Project project) { final Module module = findModuleForLintProject(myProject, project); if (module != null) { AndroidFacet facet = AndroidFacet.getInstance(module); if (facet != null) { - return IntellijLintUtils.getResourceDirectories(facet); + return org.jetbrains.android.inspections.klint.IntellijLintUtils.getResourceDirectories(facet); } } return super.getResourceFolders(project); @@ -692,7 +716,7 @@ public class IntellijLintClient extends LintClient implements Disposable { @Nullable @Override - public AbstractResourceRepository getProjectResources(com.android.tools.lint.detector.api.Project project, boolean includeDependencies) { + public AbstractResourceRepository getProjectResources(com.android.tools.klint.detector.api.Project project, boolean includeDependencies) { final Module module = findModuleForLintProject(myProject, project); if (module != null) { AndroidFacet facet = AndroidFacet.getInstance(module); @@ -712,7 +736,7 @@ public class IntellijLintClient extends LintClient implements Disposable { @NonNull @Override - public Location.Handle createResourceItemHandle(@NonNull ResourceItem item) { + public com.android.tools.klint.detector.api.Location.Handle createResourceItemHandle(@NonNull ResourceItem item) { XmlTag tag = LocalResourceRepository.getItemTag(myProject, item); if (tag != null) { ResourceFile source = item.getSource(); @@ -738,7 +762,7 @@ public class IntellijLintClient extends LintClient implements Disposable { return super.getResourceVisibilityProvider(); } - private static class LocationHandle implements Location.Handle, Computable { + private static class LocationHandle implements com.android.tools.klint.detector.api.Location.Handle, Computable { private final File myFile; private final XmlElement myNode; private Object myClientData; @@ -750,7 +774,7 @@ public class IntellijLintClient extends LintClient implements Disposable { @NonNull @Override - public Location resolve() { + public com.android.tools.klint.detector.api.Location resolve() { if (!ApplicationManager.getApplication().isReadAccessAllowed()) { return ApplicationManager.getApplication().runReadAction(this); } @@ -767,13 +791,13 @@ public class IntellijLintClient extends LintClient implements Disposable { } } - Position start = new DefaultPosition(-1, -1, textRange.getStartOffset()); - Position end = new DefaultPosition(-1, -1, textRange.getEndOffset()); - return Location.create(myFile, start, end); + com.android.tools.klint.detector.api.Position start = new com.android.tools.klint.detector.api.DefaultPosition(-1, -1, textRange.getStartOffset()); + com.android.tools.klint.detector.api.Position end = new com.android.tools.klint.detector.api.DefaultPosition(-1, -1, textRange.getEndOffset()); + return com.android.tools.klint.detector.api.Location.create(myFile, start, end); } @Override - public Location compute() { + public com.android.tools.klint.detector.api.Location compute() { return resolve(); } diff --git a/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/IntellijLintIssueRegistry.java b/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/IntellijLintIssueRegistry.java index ccdaf5b5447..636a6928db9 100644 --- a/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/IntellijLintIssueRegistry.java +++ b/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/IntellijLintIssueRegistry.java @@ -13,18 +13,19 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.jetbrains.android.inspections.lint; +package org.jetbrains.android.inspections.klint; import com.android.annotations.NonNull; -import com.android.tools.lint.checks.*; -import com.android.tools.lint.detector.api.*; +import com.android.tools.klint.checks.*; +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 java.util.ArrayList; import java.util.EnumSet; import java.util.List; -import static org.jetbrains.android.inspections.lint.IntellijLintProject.*; - /** * Custom version of the {@link BuiltinIssueRegistry}. This * variation will filter the default issues and remove @@ -52,18 +53,16 @@ public class IntellijLintIssueRegistry extends BuiltinIssueRegistry { issue.setImplementation(IntellijApiDetector.IMPLEMENTATION); } else if (detectorClass == RegistrationDetector.class) { issue.setImplementation(IntellijRegistrationDetector.IMPLEMENTATION); - } else if (detectorClass == GradleDetector.class) { - issue.setImplementation(IntellijGradleDetector.IMPLEMENTATION); } else if (detectorClass == ViewTypeDetector.class) { issue.setImplementation(IntellijViewTypeDetector.IMPLEMENTATION); } else if (detectorClass == SupportAnnotationDetector.class) { // Handled by the ResourceTypeInspection continue; } else if (scope.contains(Scope.CLASS_FILE) || - scope.contains(Scope.ALL_CLASS_FILES) || - scope.contains(Scope.JAVA_LIBRARIES)) { + scope.contains(Scope.ALL_CLASS_FILES) || + scope.contains(Scope.JAVA_LIBRARIES)) { //noinspection ConstantConditions - assert !SUPPORT_CLASS_FILES; // When enabled, adjust this to include class detector based issues + assert !IntellijLintProject.SUPPORT_CLASS_FILES; // When enabled, adjust this to include class detector based issues boolean isOk = false; for (EnumSet analysisScope : implementation.getAnalysisScopes()) { diff --git a/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/IntellijLintProject.java b/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/IntellijLintProject.java index 7cba65ba3a4..a724c0773ad 100644 --- a/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/IntellijLintProject.java +++ b/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/IntellijLintProject.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.jetbrains.android.inspections.lint; +package org.jetbrains.android.inspections.klint; import com.android.annotations.NonNull; import com.android.builder.model.*; @@ -23,8 +23,8 @@ import com.android.tools.idea.gradle.IdeaAndroidProject; import com.android.tools.idea.gradle.util.GradleUtil; import com.android.tools.idea.model.AndroidModuleInfo; import com.android.tools.idea.model.ManifestInfo; -import com.android.tools.lint.client.api.LintClient; -import com.android.tools.lint.detector.api.Project; +import com.android.tools.klint.client.api.LintClient; +import com.android.tools.klint.detector.api.Project; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Sets; @@ -146,7 +146,7 @@ class IntellijLintProject extends Project { client.setModuleMap(projectMap); //noinspection ConstantConditions - return Pair.create(project,main); + return Pair.create(project, main); } /** Find an Android module that depends on this module; prefer app modules over library modules */ @@ -898,7 +898,7 @@ class IntellijLintProject extends Project { } else { // Some other (not yet directly cached result) if (myFacet.isGradleProject() && myFacet.getIdeaAndroidProject() != null - && GradleUtil.dependsOn(myFacet.getIdeaAndroidProject(), artifact)) { + && GradleUtil.dependsOn(myFacet.getIdeaAndroidProject(), artifact)) { return true; } diff --git a/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/IntellijLintRequest.java b/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/IntellijLintRequest.java index a8e5ac14180..859e7f97f99 100644 --- a/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/IntellijLintRequest.java +++ b/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/IntellijLintRequest.java @@ -13,12 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.jetbrains.android.inspections.lint; +package org.jetbrains.android.inspections.klint; import com.android.annotations.NonNull; import com.android.annotations.Nullable; -import com.android.tools.lint.client.api.LintRequest; -import com.android.tools.lint.detector.api.Scope; +import com.android.tools.klint.client.api.LintRequest; +import com.android.tools.klint.detector.api.Scope; import com.intellij.openapi.module.Module; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Pair; @@ -35,7 +35,7 @@ public class IntellijLintRequest extends LintRequest { @NonNull private final List myModules; @NonNull private final IntellijLintClient mLintClient; @Nullable private final List myFileList; - @Nullable private com.android.tools.lint.detector.api.Project myMainProject; + @Nullable private com.android.tools.klint.detector.api.Project myMainProject; private final boolean myIncremental; /** @@ -68,13 +68,13 @@ public class IntellijLintRequest extends LintRequest { @Override public EnumSet getScope() { if (mScope == null) { - Collection projects = getProjects(); + Collection projects = getProjects(); if (projects != null) { mScope = Scope.infer(projects); //noinspection ConstantConditions - if (!IntellijLintProject.SUPPORT_CLASS_FILES && (mScope.contains(Scope.CLASS_FILE) || mScope.contains(Scope.ALL_CLASS_FILES) - || mScope.contains(Scope.JAVA_LIBRARIES))) { + if (!org.jetbrains.android.inspections.klint.IntellijLintProject.SUPPORT_CLASS_FILES && (mScope.contains(Scope.CLASS_FILE) || mScope.contains(Scope.ALL_CLASS_FILES) + || mScope.contains(Scope.JAVA_LIBRARIES))) { mScope = EnumSet.copyOf(mScope); // make mutable // Can't run class file based checks mScope.remove(Scope.CLASS_FILE); @@ -89,20 +89,20 @@ public class IntellijLintRequest extends LintRequest { @Nullable @Override - public Collection getProjects() { + public Collection getProjects() { if (mProjects == null) { if (myIncremental && myFileList != null && myFileList.size() == 1 && myModules.size() == 1) { - Pair pair = - IntellijLintProject.createForSingleFile(mLintClient, myFileList.get(0), myModules.get(0)); + Pair pair = + org.jetbrains.android.inspections.klint.IntellijLintProject.createForSingleFile(mLintClient, myFileList.get(0), myModules.get(0)); mProjects = pair.first != null ? Collections.singletonList(pair.first) - : Collections.emptyList(); + : Collections.emptyList(); myMainProject = pair.second; } else if (!myModules.isEmpty()) { // Make one project for each module, mark each one as a library, // and add projects for the gradle libraries and set error reporting to // false on those //mProjects = computeProjects() - mProjects = IntellijLintProject.create(mLintClient, myFileList, myModules.toArray(new Module[myModules.size()])); + mProjects = org.jetbrains.android.inspections.klint.IntellijLintProject.create(mLintClient, myFileList, myModules.toArray(new Module[myModules.size()])); } else { mProjects = super.getProjects(); } @@ -113,7 +113,7 @@ public class IntellijLintRequest extends LintRequest { @NonNull @Override - public com.android.tools.lint.detector.api.Project getMainProject(@NonNull com.android.tools.lint.detector.api.Project project) { + public com.android.tools.klint.detector.api.Project getMainProject(@NonNull com.android.tools.klint.detector.api.Project project) { if (myMainProject != null) { return myMainProject; } diff --git a/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/IntellijLintUtils.java b/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/IntellijLintUtils.java index 7f2479384ca..9eca40210f7 100644 --- a/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/IntellijLintUtils.java +++ b/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/IntellijLintUtils.java @@ -13,14 +13,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.jetbrains.android.inspections.lint; +package org.jetbrains.android.inspections.klint; import com.android.SdkConstants; import com.android.annotations.NonNull; import com.android.annotations.Nullable; import com.android.builder.model.SourceProvider; -import com.android.tools.lint.client.api.LintRequest; -import com.android.tools.lint.detector.api.*; +import com.android.tools.klint.client.api.LintRequest; import com.google.common.base.Splitter; import com.intellij.debugger.engine.JVMNameUtil; import com.intellij.ide.util.JavaAnonymousClassesHelper; @@ -37,6 +36,10 @@ import com.intellij.psi.util.TypeConversionUtil; import org.jetbrains.android.facet.AndroidFacet; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; +import org.jetbrains.uast.UClass; +import org.jetbrains.uast.UElement; +import org.jetbrains.uast.UFile; +import org.jetbrains.uast.java.JavaUClass; import java.io.File; import java.util.ArrayList; @@ -66,7 +69,7 @@ public class IntellijLintUtils { * @return the location of the given element */ @NonNull - public static Location getLocation(@NonNull File file, @NonNull PsiElement element) { + public static com.android.tools.klint.detector.api.Location getLocation(@NonNull File file, @NonNull PsiElement element) { //noinspection ConstantConditions assert element.getContainingFile().getVirtualFile() == null || FileUtil.filesEqual(VfsUtilCore.virtualToIoFile(element.getContainingFile().getVirtualFile()), file); @@ -81,19 +84,19 @@ public class IntellijLintUtils { } TextRange textRange = element.getTextRange(); - Position start = new DefaultPosition(-1, -1, textRange.getStartOffset()); - Position end = new DefaultPosition(-1, -1, textRange.getEndOffset()); - return Location.create(file, start, end); + com.android.tools.klint.detector.api.Position start = new com.android.tools.klint.detector.api.DefaultPosition(-1, -1, textRange.getStartOffset()); + com.android.tools.klint.detector.api.Position end = new com.android.tools.klint.detector.api.DefaultPosition(-1, -1, textRange.getEndOffset()); + return com.android.tools.klint.detector.api.Location.create(file, start, end); } /** - * Returns the {@link PsiFile} associated with a given lint {@link Context} + * Returns the {@link PsiFile} associated with a given lint {@link com.android.tools.klint.detector.api.Context} * * @param context the context to look up the file for * @return the corresponding {@link PsiFile}, or null */ @Nullable - public static PsiFile getPsiFile(@NonNull Context context) { + public static PsiFile getPsiFile(@NonNull com.android.tools.klint.detector.api.Context context) { VirtualFile file = VfsUtil.findFileByIoFile(context.file, false); if (file == null) { return null; @@ -106,6 +109,11 @@ public class IntellijLintUtils { return PsiManager.getInstance(project).findFile(file); } + public static boolean isSuppressed(@NonNull UElement element, @NonNull UFile file, @NonNull com.android.tools.klint.detector.api.Issue issue) { + //TODO + return true; + } + /** * Returns true if the given issue is suppressed at the given element within the given file * @@ -114,7 +122,7 @@ public class IntellijLintUtils { * @param issue the issue to check * @return true if the given issue is suppressed */ - public static boolean isSuppressed(@NonNull PsiElement element, @NonNull PsiFile file, @NonNull Issue issue) { + public static boolean isSuppressed(@NonNull PsiElement element, @NonNull PsiFile file, @NonNull com.android.tools.klint.detector.api.Issue issue) { // Search upwards for suppress lint and suppress warnings annotations // Search upwards for target api annotations while (element != null && element != file) { // otherwise it will keep going into directories! @@ -200,7 +208,7 @@ public class IntellijLintUtils { * * @param psiClass the class to look up the internal name for * @return the internal class name - * @see ClassContext#getInternalName(String) + * @see com.android.tools.klint.detector.api.ClassContext#getInternalName(String) */ @Nullable public static String getInternalName(@NonNull PsiClass psiClass) { @@ -218,24 +226,32 @@ public class IntellijLintUtils { if (sig == null) { String qualifiedName = psiClass.getQualifiedName(); if (qualifiedName != null) { - return ClassContext.getInternalName(qualifiedName); + return com.android.tools.klint.detector.api.ClassContext.getInternalName(qualifiedName); } return null; } else if (sig.indexOf('.') != -1) { // Workaround -- ClassUtil doesn't treat this correctly! // .replace('.', '/'); - sig = ClassContext.getInternalName(sig); + sig = com.android.tools.klint.detector.api.ClassContext.getInternalName(sig); } return sig; } + public static String getInternalName(@NotNull UClass clazz) { + if (clazz instanceof JavaUClass) { + return getInternalName(((JavaUClass) clazz).getPsi()); + } else { + return null; + } + } + /** * Computes the internal class name of the given class type. * For example, for PsiClassType foo.bar.Foo.Bar it returns foo/bar/Foo$Bar. * * @param psiClassType the class type to look up the internal name for * @return the internal class name - * @see ClassContext#getInternalName(String) + * @see com.android.tools.klint.detector.api.ClassContext#getInternalName(String) */ @Nullable public static String getInternalName(@NonNull PsiClassType psiClassType) { @@ -246,7 +262,7 @@ public class IntellijLintUtils { String className = psiClassType.getClassName(); if (className != null) { - return ClassContext.getInternalName(className); + return com.android.tools.klint.detector.api.ClassContext.getInternalName(className); } return null; diff --git a/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/IntellijRegistrationDetector.java b/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/IntellijRegistrationDetector.java index c57405bdfe0..2c79c4ced33 100644 --- a/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/IntellijRegistrationDetector.java +++ b/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/IntellijRegistrationDetector.java @@ -13,80 +13,71 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.jetbrains.android.inspections.lint; +package org.jetbrains.android.inspections.klint; -import com.android.annotations.NonNull; -import com.android.annotations.Nullable; -import com.android.tools.lint.checks.RegistrationDetector; -import com.android.tools.lint.detector.api.*; +import com.android.tools.klint.checks.RegistrationDetector; +import com.android.tools.klint.detector.api.ClassContext; +import com.android.tools.klint.detector.api.Implementation; +import com.android.tools.klint.detector.api.Location; +import com.android.tools.klint.detector.api.Scope; import com.intellij.openapi.application.ApplicationManager; -import com.intellij.psi.*; -import lombok.ast.AstVisitor; -import lombok.ast.CompilationUnit; -import lombok.ast.ForwardingAstVisitor; -import lombok.ast.Node; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.uast.UClass; +import org.jetbrains.uast.UFile; +import org.jetbrains.uast.UastModifier; +import org.jetbrains.uast.UastUtils; +import org.jetbrains.uast.check.UastAndroidContext; +import org.jetbrains.uast.check.UastAndroidUtils; +import org.jetbrains.uast.check.UastScanner; +import org.jetbrains.uast.visitor.UastVisitor; import java.util.Collection; -import java.util.Collections; import java.util.EnumSet; -import java.util.List; import static com.android.SdkConstants.*; /** - * Intellij-specific version of the {@link com.android.tools.lint.checks.RegistrationDetector} which uses the PSI structure + * Intellij-specific version of the {@link RegistrationDetector} which uses the PSI structure * to check classes. *

*

    *
  • Unit tests, and compare to the bytecode based results
  • *
*/ -public class IntellijRegistrationDetector extends RegistrationDetector implements Detector.JavaScanner { +public class IntellijRegistrationDetector extends RegistrationDetector implements UastScanner { static final Implementation IMPLEMENTATION = new Implementation( IntellijRegistrationDetector.class, - EnumSet.of(Scope.MANIFEST, Scope.JAVA_FILE)); + EnumSet.of(Scope.MANIFEST, Scope.SOURCE_FILE)); - @Nullable @Override - public List> getApplicableNodeTypes() { - return Collections.>singletonList(CompilationUnit.class); - } - - @Nullable - @Override - public AstVisitor createJavaVisitor(@NonNull final JavaContext context) { - return new ForwardingAstVisitor() { + public UastVisitor createUastVisitor(final UastAndroidContext context) { + return new UastVisitor() { @Override - public boolean visitCompilationUnit(CompilationUnit node) { - check(context); + public boolean visitFile(@NotNull UFile file) { + check(context, file); return true; } }; } - private void check(final JavaContext context) { + private void check(final UastAndroidContext context, final UFile file) { ApplicationManager.getApplication().runReadAction(new Runnable() { @Override public void run() { - final PsiFile psiFile = IntellijLintUtils.getPsiFile(context); - if (!(psiFile instanceof PsiJavaFile)) { - return; - } - PsiJavaFile javaFile = (PsiJavaFile)psiFile; - for (PsiClass clz : javaFile.getClasses()) { + for (UClass clz : file.getClasses()) { check(context, clz); } } }); } - private void check(JavaContext context, PsiClass clz) { - for (PsiClass current = clz.getSuperClass(); current != null; current = current.getSuperClass()) { + private void check(UastAndroidContext context, UClass clz) { + for (UClass current = clz.getSuperClass(context); current != null; current = current.getSuperClass(context)) { // Ignore abstract classes - if (clz.hasModifierProperty(PsiModifier.ABSTRACT) || clz instanceof PsiAnonymousClass) { + if (clz.hasModifier(UastModifier.ABSTRACT) || clz.isObject() || clz.isInterface() || clz.isEnum() || clz.isAnnotation()) { continue; } - String fqcn = current.getQualifiedName(); + String fqcn = current.getFqName(); if (fqcn == null) { continue; } @@ -94,7 +85,10 @@ public class IntellijRegistrationDetector extends RegistrationDetector implement || CLASS_SERVICE.equals(fqcn) || CLASS_CONTENTPROVIDER.equals(fqcn)) { - String internalName = IntellijLintUtils.getInternalName(clz); + String internalName = clz.getInternalName(); + if (internalName == null) { + internalName = IntellijLintUtils.getInternalName(clz); + } if (internalName == null) { continue; } @@ -107,24 +101,24 @@ public class IntellijRegistrationDetector extends RegistrationDetector implement } } - for (PsiClass innerClass : clz.getInnerClasses()) { + for (UClass innerClass : clz.getNestedClasses()) { check(context, innerClass); } } - private static void report(JavaContext context, PsiClass clz, String internalName) { + private static void report(UastAndroidContext context, UClass clz, String internalName) { // Unlike the superclass, we don't have to check that the tags are compatible; // IDEA already checks that as part of its XML validation - if (IntellijLintUtils.isSuppressed(clz, clz.getContainingFile(), ISSUE)) { + if (IntellijLintUtils.isSuppressed(clz, UastUtils.getContainingFile(clz), ISSUE)) { return; } String tag = classToTag(internalName); - Location location = IntellijLintUtils.getLocation(context.file, clz); - String fqcn = clz.getQualifiedName(); + Location location = UastAndroidUtils.getLocation(clz); + String fqcn = clz.getFqName(); if (fqcn == null) { fqcn = clz.getName(); } - context.report(ISSUE, location, String.format("The <%1$s> %2$s is not registered in the manifest", tag, fqcn), null); + context.report(ISSUE, clz, location, String.format("The <%1$s> %2$s is not registered in the manifest", tag, fqcn)); } } diff --git a/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/IntellijViewTypeDetector.java b/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/IntellijViewTypeDetector.java index a550ff66468..4c54a1910bb 100644 --- a/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/IntellijViewTypeDetector.java +++ b/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/IntellijViewTypeDetector.java @@ -13,25 +13,25 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.jetbrains.android.inspections.lint; +package org.jetbrains.android.inspections.klint; import com.android.annotations.NonNull; import com.android.annotations.Nullable; import com.android.ide.common.res2.AbstractResourceRepository; import com.android.ide.common.res2.ResourceItem; import com.android.tools.idea.rendering.LocalResourceRepository; -import com.android.tools.lint.checks.ViewTypeDetector; -import com.android.tools.lint.detector.api.Context; -import com.android.tools.lint.detector.api.Implementation; -import com.android.tools.lint.detector.api.Scope; +import com.android.tools.klint.checks.ViewTypeDetector; +import com.android.tools.klint.detector.api.Context; +import com.android.tools.klint.detector.api.Implementation; +import com.android.tools.klint.detector.api.Scope; import java.util.Collection; import java.util.Collections; public class IntellijViewTypeDetector extends ViewTypeDetector { static final Implementation IMPLEMENTATION = new Implementation( - IntellijViewTypeDetector.class, - Scope.JAVA_FILE_SCOPE); + IntellijViewTypeDetector.class, + Scope.JAVA_FILE_SCOPE); @Nullable @Override diff --git a/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/KotlinLintLanguageExtension.kt b/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/KotlinLintLanguageExtension.kt new file mode 100644 index 00000000000..bd0f61299bf --- /dev/null +++ b/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/KotlinLintLanguageExtension.kt @@ -0,0 +1,24 @@ +/* + * 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 org.jetbrains.android.inspections.klint + +import com.android.tools.klint.client.api.LintLanguageExtension +import org.jetbrains.kotlin.uast.KotlinConverter + +class KotlinLintLanguageExtension : LintLanguageExtension() { + override fun getConverter() = KotlinConverter +} \ No newline at end of file diff --git a/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/LintInspectionDescriptionLinkHandler.java b/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/LintInspectionDescriptionLinkHandler.java index 861c78ee6e4..a9782bd7fcf 100644 --- a/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/LintInspectionDescriptionLinkHandler.java +++ b/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/LintInspectionDescriptionLinkHandler.java @@ -13,25 +13,22 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.jetbrains.android.inspections.lint; +package org.jetbrains.android.inspections.klint; -import com.android.tools.lint.checks.BuiltinIssueRegistry; -import com.android.tools.lint.detector.api.Issue; -import com.android.tools.lint.detector.api.TextFormat; +import com.android.tools.klint.checks.BuiltinIssueRegistry; +import com.android.tools.klint.detector.api.Issue; +import com.android.tools.klint.detector.api.TextFormat; import com.intellij.codeInsight.highlighting.TooltipLinkHandler; -import com.intellij.codeInspection.InspectionProfile; import com.intellij.codeInspection.InspectionsBundle; -import com.intellij.codeInspection.ex.InspectionToolWrapper; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.project.Project; -import com.intellij.profile.codeInspection.InspectionProfileManager; import com.intellij.psi.PsiDocumentManager; import com.intellij.psi.PsiFile; import org.jetbrains.annotations.NotNull; public class LintInspectionDescriptionLinkHandler extends TooltipLinkHandler { - private static final Logger LOG = Logger.getInstance("#org.jetbrains.android.inspections.lint.LintInspectionDescriptionLinkHandler"); + private static final Logger LOG = Logger.getInstance("#org.jetbrains.android.inspections.klint.LintInspectionDescriptionLinkHandler"); @Override public String getDescription(@NotNull final String refSuffix, @NotNull final Editor editor) { diff --git a/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/ProblemData.java b/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/ProblemData.java index 8743592eac1..933912ebe1b 100644 --- a/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/ProblemData.java +++ b/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/ProblemData.java @@ -1,7 +1,23 @@ -package org.jetbrains.android.inspections.lint; +/* + * 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. + */ -import com.android.tools.lint.detector.api.Issue; -import com.android.tools.lint.detector.api.Severity; +package org.jetbrains.android.inspections.klint; + +import com.android.tools.klint.detector.api.Issue; +import com.android.tools.klint.detector.api.Severity; import com.intellij.openapi.util.TextRange; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; diff --git a/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/State.java b/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/State.java new file mode 100644 index 00000000000..7a49b2dcee0 --- /dev/null +++ b/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/State.java @@ -0,0 +1,82 @@ +/* + * 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 org.jetbrains.android.inspections.klint; + +import com.android.tools.klint.detector.api.Issue; +import com.intellij.openapi.module.Module; +import com.intellij.openapi.vfs.VirtualFile; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.List; + +/** +* @author Eugene.Kudelevsky +*/ +public class State { + private final Module myModule; + private final VirtualFile myMainFile; + + private final String myMainFileContent; + private final List myProblems = new ArrayList(); + private final List myIssues; + + private volatile boolean myDirty; + + State(@NotNull Module module, + @NotNull VirtualFile mainFile, + @NotNull String mainFileContent, + @NotNull List issues) { + myModule = module; + myMainFile = mainFile; + myMainFileContent = mainFileContent; + myIssues = issues; + } + + @NotNull + public VirtualFile getMainFile() { + return myMainFile; + } + + @NotNull + public String getMainFileContent() { + return myMainFileContent; + } + + public void markDirty() { + myDirty = true; + } + + public boolean isDirty() { + return myDirty; + } + + @NotNull + public Module getModule() { + return myModule; + } + + @NotNull + public List getProblems() { + return myProblems; + } + + @NotNull + public List getIssues() { + return myIssues; + } +} diff --git a/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/SuppressLintIntentionAction.java b/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/SuppressLintIntentionAction.java new file mode 100644 index 00000000000..ed85df873aa --- /dev/null +++ b/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/SuppressLintIntentionAction.java @@ -0,0 +1,276 @@ +/* + * 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 org.jetbrains.android.inspections.klint; + +import com.google.common.base.Joiner; +import com.google.common.base.Splitter; +import com.intellij.codeInsight.AnnotationUtil; +import com.intellij.codeInsight.FileModificationService; +import com.intellij.codeInsight.intention.AddAnnotationFix; +import com.intellij.codeInsight.intention.IntentionAction; +import com.intellij.icons.AllIcons; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.editor.Document; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Iconable; +import com.intellij.openapi.util.TextRange; +import com.intellij.psi.*; +import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.psi.xml.XmlAttribute; +import com.intellij.psi.xml.XmlFile; +import com.intellij.psi.xml.XmlTag; +import com.intellij.util.IncorrectOperationException; +import org.jetbrains.android.util.AndroidBundle; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import static com.android.SdkConstants.*; + +/** Intention for adding a {@code @SuppressLint} annotation on the given element for the given id */ +public class SuppressLintIntentionAction implements IntentionAction, Iconable { + private static final String NO_INSPECTION_PREFIX = "//noinspection "; + private final String myId; + private final PsiElement myElement; + + SuppressLintIntentionAction(String id, PsiElement element) { + myId = id; + myElement = element; + } + + @Override + public Icon getIcon(@IconFlags int flags) { + return AllIcons.Actions.Cancel; + } + + @NotNull + @Override + public String getText() { + String id = getLintId(myId); + final PsiFile file = PsiTreeUtil.getParentOfType(myElement, PsiFile.class); + if (file == null) { + return ""; + } else if (file instanceof XmlFile) { + return AndroidBundle.message("android.lint.fix.suppress.lint.api.attr", id); + } else if (file instanceof PsiJavaFile) { + return AndroidBundle.message("android.lint.fix.suppress.lint.api.annotation", id); + } else { + return ""; + } + } + + @NotNull + @Override + public String getFamilyName() { + return getText(); + } + + @Override + public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) { + return true; + } + + @Override + public void invoke(@NotNull Project project, @Nullable Editor editor, @NotNull PsiFile file) throws IncorrectOperationException { + if (file instanceof XmlFile) { + final XmlTag element = PsiTreeUtil.getParentOfType(myElement, XmlTag.class); + if (element == null) { + return; + } + + if (!FileModificationService.getInstance().preparePsiElementForWrite(element)) { + return; + } + String lintId = getLintId(myId); + addSuppressAttribute(project, (XmlFile) file, element, lintId); + } else if (file instanceof PsiJavaFile) { + final PsiModifierListOwner container = + PsiTreeUtil.getParentOfType(myElement, PsiModifierListOwner.class); + if (container == null) { + return; + } + + if (!FileModificationService.getInstance().preparePsiElementForWrite(container)) { + return; + } + final PsiModifierList modifierList = container.getModifierList(); + if (modifierList != null) { + + String lintId = getLintId(myId); + addSuppressAnnotation(project, container, container, lintId); + } + } + } + + /** + * TODO: There is probably an existing utility method somewhere in IntelliJ for this; + * find it and inline. Possible candidate: {@link com.intellij.xml.XmlNamespaceHelper#insertNamespaceDeclaration}. + * See also code in {@link com.intellij.codeInsight.completion.XmlAttributeInsertHandler} for additional useful + * code such as code to pick a unique prefix, look up the prefix from the schema provider etc (which presumably would + * consult {@link org.jetbrains.android.AndroidXmlSchemaProvider}). + * + */ + @NotNull + public static String ensureNamespaceImported(@NotNull Project project, @NotNull XmlFile file, @NotNull String namespaceUri) { + ApplicationManager.getApplication().assertWriteAccessAllowed(); + + final XmlTag rootTag = file.getRootTag(); + + assert rootTag != null; + final XmlElementFactory elementFactory = XmlElementFactory.getInstance(project); + + String prefix = rootTag.getPrefixByNamespace(namespaceUri); + if (prefix != null) { + return prefix; + } + + if (TOOLS_URI.equals(namespaceUri)) { + prefix = TOOLS_PREFIX; + } else if (ANDROID_URI.equals(namespaceUri)) { + prefix = ANDROID_NS_NAME; + } else { + prefix = APP_PREFIX; + } + if (rootTag.getAttribute(XMLNS_PREFIX + prefix) != null) { + String base = prefix; + for (int i = 2; ; i++) { + prefix = base + Integer.toString(i); + if (rootTag.getAttribute(XMLNS_PREFIX + prefix) == null) { + break; + } + } + } + String name = XMLNS_PREFIX + prefix; + final XmlAttribute xmlnsAttr = elementFactory.createXmlAttribute(name, namespaceUri); + final XmlAttribute[] attributes = rootTag.getAttributes(); + XmlAttribute next = attributes.length > 0 ? attributes[0] : null; + for (XmlAttribute attribute : attributes) { + String attributeName = attribute.getName(); + if (!attributeName.startsWith(XMLNS_PREFIX) || attributeName.compareTo(name) > 0) { + next = attribute; + break; + } + } + if (next != null) { + rootTag.addBefore(xmlnsAttr, next); + } + else { + rootTag.add(xmlnsAttr); + } + + return prefix; + } + + static String getLintId(String intentionId) { + String lintId = intentionId; + if (lintId.startsWith("AndroidLint")) { + lintId = lintId.substring("AndroidLint".length()); + } + + return lintId; + } + + @Override + public boolean startInWriteAction() { + return true; + } + + private static void addSuppressAttribute(final Project project, + final XmlFile file, + final XmlTag element, + final String id) throws IncorrectOperationException { + XmlAttribute attribute = element.getAttribute(ATTR_IGNORE, TOOLS_URI); + String value; + if (attribute == null) { + value = id; + } else { + List ids = new ArrayList(); + for (String existing : Splitter.on(',').trimResults().split(attribute.getValue())) { + if (!existing.equals(id)) { + ids.add(existing); + } + } + ids.add(id); + Collections.sort(ids); + value = Joiner.on(',').join(ids); + } + ensureNamespaceImported(project, file, TOOLS_URI); + element.setAttribute(ATTR_IGNORE, TOOLS_URI, value); + } + + // Based on the equivalent code in com.intellij.codeInsight.daemon.impl.actions.SuppressFix + // to add @SuppressWarnings annotations + + private static void addSuppressAnnotation(final Project project, + final PsiElement container, + final PsiModifierListOwner modifierOwner, + final String id) throws IncorrectOperationException { + PsiAnnotation annotation = AnnotationUtil.findAnnotation(modifierOwner, FQCN_SUPPRESS_LINT); + final PsiAnnotation newAnnotation = createNewAnnotation(project, container, annotation, id); + if (newAnnotation != null) { + if (annotation != null && annotation.isPhysical()) { + annotation.replace(newAnnotation); + } + else { + final PsiNameValuePair[] attributes = newAnnotation.getParameterList().getAttributes(); + //noinspection ConstantConditions + new AddAnnotationFix(FQCN_SUPPRESS_LINT, modifierOwner, attributes).invoke(project, null /*editor*/, + container.getContainingFile()); + } + } + } + + @Nullable + private static PsiAnnotation createNewAnnotation(@NotNull final Project project, + @NotNull final PsiElement container, + @Nullable final PsiAnnotation annotation, + @NotNull final String id) { + if (annotation != null) { + final String currentSuppressedId = "\"" + id + "\""; + String annotationText = annotation.getText(); + if (!annotationText.contains("{")) { + final PsiNameValuePair[] attributes = annotation.getParameterList().getAttributes(); + if (attributes.length == 1) { + final String suppressedWarnings = attributes[0].getText(); + if (suppressedWarnings.contains(currentSuppressedId)) return null; + return JavaPsiFacade.getInstance(project).getElementFactory().createAnnotationFromText( + "@" + FQCN_SUPPRESS_LINT + "({" + suppressedWarnings + ", " + currentSuppressedId + "})", container); + + } + } + else { + final int curlyBraceIndex = annotationText.lastIndexOf("}"); + if (curlyBraceIndex > 0) { + final String oldSuppressWarning = annotationText.substring(0, curlyBraceIndex); + if (oldSuppressWarning.contains(currentSuppressedId)) return null; + return JavaPsiFacade.getInstance(project).getElementFactory().createAnnotationFromText( + oldSuppressWarning + ", " + currentSuppressedId + "})", container); + } + } + } + else { + return JavaPsiFacade.getInstance(project).getElementFactory() + .createAnnotationFromText("@" + FQCN_SUPPRESS_LINT + "(\"" + id + "\")", container); + } + return null; + } +} diff --git a/plugins/android-lint/src/org/jetbrains/kotlin/lint/KotlinLintLanguageExtension.kt b/plugins/lint/lint-idea/src/org/jetbrains/kotlin/lint/KotlinLintLanguageExtension.kt similarity index 92% rename from plugins/android-lint/src/org/jetbrains/kotlin/lint/KotlinLintLanguageExtension.kt rename to plugins/lint/lint-idea/src/org/jetbrains/kotlin/lint/KotlinLintLanguageExtension.kt index a1059ae3a4f..a72fcdebdaf 100644 --- a/plugins/android-lint/src/org/jetbrains/kotlin/lint/KotlinLintLanguageExtension.kt +++ b/plugins/lint/lint-idea/src/org/jetbrains/kotlin/lint/KotlinLintLanguageExtension.kt @@ -16,7 +16,7 @@ package org.jetbrains.kotlin.lint -import com.android.tools.lint.client.api.LintLanguageExtension +import com.android.tools.klint.client.api.LintLanguageExtension import org.jetbrains.kotlin.uast.KotlinConverter class KotlinLintLanguageExtension : LintLanguageExtension() { diff --git a/plugins/lint/uast-android/src/org/jetbrains/uast/check/UastChecker.kt b/plugins/lint/uast-android/src/org/jetbrains/uast/check/UastChecker.kt new file mode 100644 index 00000000000..cba09ee03ea --- /dev/null +++ b/plugins/lint/uast-android/src/org/jetbrains/uast/check/UastChecker.kt @@ -0,0 +1,105 @@ +/* + * 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 org.jetbrains.uast.check + +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.intellij.openapi.application.ApplicationManager +import com.intellij.openapi.project.Project +import com.intellij.openapi.vfs.VirtualFileManager +import com.intellij.psi.PsiJavaFile +import com.intellij.psi.PsiManager +import org.jetbrains.uast.* +import org.jetbrains.uast.UastCallKind.Companion.CONSTRUCTOR_CALL +import org.jetbrains.uast.UastCallKind.Companion.FUNCTION_CALL +import org.jetbrains.uast.java.JavaConverter +import org.jetbrains.uast.visitor.UastVisitor +import java.io.File + +interface UastAndroidContext : UastContext { + val lintContext: JavaContext + fun report(issue: Issue, element: UElement, location: Location?, message: String) + + fun getLocation(element: UElement?) = element?.getLocation() +} + +object UastChecker { + fun checkWithCustomHandler( + project: Project, + file: File, + converters: List, + visitor: UastVisitor) { + check(project, file, converters, UastHandler { visitor.handle(it) }) + } + + fun check(project: Project, file: File, converters: List, handler: UastHandler) { + val vfile = VirtualFileManager.getInstance().findFileByUrl("file://" + file.absolutePath) ?: return + ApplicationManager.getApplication().runReadAction { + val psiFile = PsiManager.getInstance(project).findFile(vfile) + + if (psiFile != null) { + when (psiFile) { + is PsiJavaFile -> { + val ufile = JavaConverter.convert(psiFile) + ufile.handleTraverse(handler) + } + else -> for (converter in converters) { + val ufile = converter.convertWithParent(psiFile) + if (ufile != null) { + ufile.handleTraverse(handler) + break + } + } + } + } + } + } + + fun check(project: Project, file: File, scanner: UastScanner, converters: List, context: UastAndroidContext) { + val applicableFunctionNames = scanner.applicableFunctionNames ?: emptyList() + val applicableSuperClasses = scanner.applicableSuperClasses ?: emptyList() + val applicableConstructorTypes = scanner.applicableConstructorTypes ?: emptyList() + + check(project, file, converters, UastHandler { element -> + when (element) { + is UCallExpression -> { + if (applicableFunctionNames.isNotEmpty()) { + if (element.kind == FUNCTION_CALL && element.functionName in applicableFunctionNames) { + scanner.visitFunctionCall(context, element) + } + } + + if (applicableConstructorTypes.isNotEmpty()) { + if (element.kind == CONSTRUCTOR_CALL) { + element.resolve(context)?.let { constructor -> + if (constructor.getContainingClass()?.fqName in applicableConstructorTypes) { + scanner.visitConstructor(context, element, constructor) + } + } + } + } + } + is UClass -> if (applicableSuperClasses.isNotEmpty()) { + if (applicableSuperClasses.any { element.isSubclassOf(it) }) { + scanner.visitClass(context, element) + } + } + } + }) + } + +} \ No newline at end of file diff --git a/plugins/lint/uast-android/src/org/jetbrains/uast/check/UastScanner.java b/plugins/lint/uast-android/src/org/jetbrains/uast/check/UastScanner.java new file mode 100644 index 00000000000..061b9bb73de --- /dev/null +++ b/plugins/lint/uast-android/src/org/jetbrains/uast/check/UastScanner.java @@ -0,0 +1,39 @@ +/* + * Copyright 2000-2015 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 org.jetbrains.uast.check; + +import org.jetbrains.uast.UClass; +import org.jetbrains.uast.UElement; +import org.jetbrains.uast.UFunction; +import org.jetbrains.uast.UCallExpression; +import org.jetbrains.uast.visitor.UastVisitor; + +import java.util.List; + +public interface UastScanner { + UastVisitor createUastVisitor(UastAndroidContext context); + + List getApplicableFunctionNames(); + List getApplicableSuperClasses(); + List getApplicableConstructorTypes(); + + void visitFunctionCall(UastAndroidContext context, UCallExpression node); + void visitClass(UastAndroidContext context, UClass node); + void visitConstructor(UastAndroidContext context, UCallExpression functionCall, UFunction constructor); + + boolean appliesToResourceRefs(); + void visitResourceReference(UastAndroidContext context, UElement element, String type, String name, boolean isFramework); +} diff --git a/plugins/lint/uast-android/src/org/jetbrains/uast/check/androidUtils.kt b/plugins/lint/uast-android/src/org/jetbrains/uast/check/androidUtils.kt new file mode 100644 index 00000000000..820ab6de340 --- /dev/null +++ b/plugins/lint/uast-android/src/org/jetbrains/uast/check/androidUtils.kt @@ -0,0 +1,32 @@ +/* + * 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. + */ +@file:JvmName("UastAndroidUtils") +package org.jetbrains.uast.check + +import com.android.tools.klint.detector.api.Location +import com.intellij.openapi.vfs.VfsUtilCore +import org.jetbrains.uast.UElement +import org.jetbrains.uast.psi.PsiElementBacked + +fun UElement?.getLocation(): Location? { + val psiElementBacked = this as? PsiElementBacked ?: return null + val psiElement = psiElementBacked.psi + val psiFile = psiElement?.containingFile ?: return null + val vfile = psiFile.virtualFile + val file = VfsUtilCore.virtualToIoFile(vfile) + val range = psiElement?.textRange ?: return null + return Location.create(file, psiFile.text, range.startOffset, range.endOffset) +} \ No newline at end of file diff --git a/plugins/lint/uast-android/uast-android.iml b/plugins/lint/uast-android/uast-android.iml new file mode 100644 index 00000000000..9bd119efcd1 --- /dev/null +++ b/plugins/lint/uast-android/uast-android.iml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + \ No newline at end of file