1 /* 2 * Copyright 2023 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 @file:Suppress("UnstableApiUsage") 18 19 package androidx.compose.lint 20 21 import androidx.collection.MutableObjectList 22 import androidx.collection.MutableScatterMap 23 import androidx.collection.MutableScatterSet 24 import androidx.collection.ObjectList 25 import androidx.collection.ScatterMap 26 import androidx.collection.ScatterSet 27 import androidx.collection.scatterSetOf 28 import com.android.tools.lint.client.api.UElementHandler 29 import com.android.tools.lint.detector.api.Category 30 import com.android.tools.lint.detector.api.Detector 31 import com.android.tools.lint.detector.api.Implementation 32 import com.android.tools.lint.detector.api.Issue 33 import com.android.tools.lint.detector.api.JavaContext 34 import com.android.tools.lint.detector.api.Scope 35 import com.android.tools.lint.detector.api.Severity 36 import com.android.tools.lint.detector.api.SourceCodeScanner 37 import com.intellij.psi.impl.source.PsiClassReferenceType 38 import java.util.EnumSet 39 import org.jetbrains.uast.UCallExpression 40 import org.jetbrains.uast.UElement 41 42 /** 43 * Using [ScatterMap.asMap], [ScatterSet.asSet], [ObjectList.asList], or their mutable counterparts 44 * indicates that the developer may be using the collection incorrectly. Using the interfaces is 45 * slower access. It is best to use those only for when it touches public API. 46 */ 47 class AsCollectionDetector : Detector(), SourceCodeScanner { getApplicableUastTypesnull48 override fun getApplicableUastTypes() = listOf<Class<out UElement>>(UCallExpression::class.java) 49 50 override fun createUastHandler(context: JavaContext) = 51 object : UElementHandler() { 52 override fun visitCallExpression(node: UCallExpression) { 53 val methodName = node.methodName ?: return 54 if (methodName in MethodNames) { 55 val receiverType = node.receiverType as? PsiClassReferenceType ?: return 56 val qualifiedName = receiverType.canonicalText 57 val indexOfAngleBracket = qualifiedName.indexOf('<') 58 if ( 59 indexOfAngleBracket > 0 && 60 qualifiedName.substring(0, indexOfAngleBracket) in CollectionClasses 61 ) { 62 context.report( 63 ISSUE, 64 node, 65 context.getLocation(node), 66 "Use method $methodName() only for public API usage" 67 ) 68 } 69 } 70 } 71 } 72 73 companion object { 74 private val MethodNames = 75 scatterSetOf( 76 "asMap", 77 "asMutableMap", 78 "asSet", 79 "asMutableSet", 80 "asList", 81 "asMutableList" 82 ) 83 private val CollectionClasses = 84 scatterSetOf( 85 ScatterMap::class.qualifiedName, 86 MutableScatterMap::class.qualifiedName, 87 ScatterSet::class.qualifiedName, 88 MutableScatterSet::class.qualifiedName, 89 ObjectList::class.qualifiedName, 90 MutableObjectList::class.qualifiedName, 91 ) 92 93 private val AsCollectionDetectorId = "AsCollectionCall" 94 95 val ISSUE = 96 Issue.create( 97 id = AsCollectionDetectorId, 98 briefDescription = 99 "High performance collections don't implement standard collection " + 100 "interfaces so that they can remain high performance. Converting to standard " + 101 "collections wraps the classes with another object. Use these interface " + 102 "wrappers only for exposing to public API.", 103 explanation = 104 "ScatterMap, ScatterSet, and AnyList are written for high " + 105 "performance access. Using the standard collection interfaces for these classes " + 106 "forces slower performance access to these collections. The methods returning " + 107 "these interfaces should be limited to public API, where standard collection " + 108 "interfaces are expected.", 109 category = Category.PERFORMANCE, 110 priority = 3, 111 severity = Severity.ERROR, 112 implementation = 113 Implementation(AsCollectionDetector::class.java, EnumSet.of(Scope.JAVA_FILE)) 114 ) 115 } 116 } 117