@@ -5,9 +5,11 @@ import com.android.tools.lint.detector.api.*
55import com.android.tools.lint.detector.api.Category.Companion.CORRECTNESS
66import com.android.tools.lint.detector.api.Severity.WARNING
77import com.intellij.psi.PsiElement
8+ import com.intellij.psi.PsiType
89import org.jetbrains.uast.*
910import java.util.*
1011import kotlin.collections.ArrayList
12+ import kotlin.collections.HashMap
1113import kotlin.collections.HashSet
1214
1315
@@ -36,23 +38,31 @@ class CheckedExceptionsDetector : Detector(), Detector.UastScanner {
3638 return list
3739 }
3840
39-
40- fun findExceptionClassName (catchClause : UCatchClause ): String {
41- return catchClause.parameters[0 ].psi.type.canonicalText
41+ fun findClassParents (psiClass : PsiType ):HashSet <String > {
42+ val classes = HashSet <String >()
43+ classes.add(psiClass.canonicalText)
44+ psiClass.superTypes.forEach {
45+ classes.addAll(findClassParents(it))
46+ }
47+ return classes
4248 }
4349
44- fun findNamedExpressionsInAnnotation (uAnnotation : UAnnotation ): HashSet <String ? > {
45- val namedExpressions = HashSet <String ? >()
50+ fun findNamedExpressionsInAnnotation (uAnnotation : UAnnotation ): HashSet <PsiType > {
51+ val namedExpressions = HashSet <PsiType >()
4652 for (uNamedExpression in uAnnotation.attributeValues) {
4753 if (uNamedExpression.expression is UClassLiteralExpression ) {
4854 // If UClassLiteralExpression.type.canonicalText is null then maybe no import of Exception
49- namedExpressions.add((uNamedExpression.expression as UClassLiteralExpression ).type?.canonicalText)
55+ (uNamedExpression.expression as UClassLiteralExpression ).type?.let {
56+ namedExpressions.add(it)
57+ }
5058 continue
5159 }
5260 if (uNamedExpression.expression is UCallExpression ) {
5361 for (argument in (uNamedExpression.expression as UCallExpression ).valueArguments) {
5462 if (argument is UClassLiteralExpression ) {
55- namedExpressions.add(argument.type?.canonicalText)
63+ argument.type?.let {
64+ namedExpressions.add(it)
65+ }
5666 continue
5767 }
5868 }
@@ -61,7 +71,6 @@ class CheckedExceptionsDetector : Detector(), Detector.UastScanner {
6171 return namedExpressions
6272 }
6373
64-
6574 override fun createUastHandler (context : JavaContext ) = object : UElementHandler () {
6675
6776 init {
@@ -74,42 +83,49 @@ class CheckedExceptionsDetector : Detector(), Detector.UastScanner {
7483 val method = call.resolve() ? : return
7584 val uMethod = context.uastContext.getMethod(method)
7685
77- val throwsExceptions = HashSet <String >()
86+ var throwsExceptions = HashMap < String , HashSet <String > >()
7887
7988 // Find @Throws in annotation expression
8089 for (annotation in uMethod.annotations) {
8190 if (annotation.qualifiedName != " kotlin.jvm.Throws" ) continue
8291 for (throwsException in findNamedExpressionsInAnnotation(annotation)) {
83- throwsException ? : continue
84- if (throwsExceptions.contains(throwsException)) continue
85- throwsExceptions.add(throwsException)
92+ throwsExceptions[throwsException.canonicalText] = findClassParents(throwsException)
8693 }
8794 }
8895
96+ // Find throws in constructor
97+ val throwsList = method.throwsList
98+ throwsList.referencedTypes.forEach {
99+ throwsExceptions[it.canonicalText] = findClassParents(it)
100+ }
101+
89102 // Find throws in method
90103 for (child in uMethod.uastBody.getQualifiedChain()) {
91104 if (child is UBlockExpression ) {
92105 child.sourcePsi ? : continue
93106 for (psi in findChildsInPsi(child.sourcePsi!! )) {
94107 val uElement = psi.toUElement()
95108 if (uElement == null || uElement !is UThrowExpression ) continue
96- uElement.thrownExpression as UCallExpression
97109 val clazz = (uElement.thrownExpression as UCallExpression ).resolve()
98- val clazzName = clazz?.containingClass?.qualifiedName
99- clazzName ? : continue
100- throwsExceptions.add(clazzName)
110+ val mainClassName = clazz?.containingClass?.qualifiedName
111+ mainClassName ? : continue
112+ clazz.containingClass?.superTypes?.forEach {
113+ throwsExceptions[mainClassName] = (findClassParents(it).apply { add(mainClassName) })
114+ }
115+
101116 }
102117 }
103118 }
104119
105120
106- val ignoreExceptions = HashSet < String >()
121+ // Remove catched
107122 for (element in call.withContainingElements) {
108123 if (element !is UMethod ) continue
109124 for (child in element.annotations) {
110125 for (classInAnnotation in findNamedExpressionsInAnnotation(child)) {
111- classInAnnotation ? : continue
112- ignoreExceptions.add(classInAnnotation)
126+ throwsExceptions = throwsExceptions.filterTo(HashMap ()) {
127+ ! it.value.contains(classInAnnotation.canonicalText)
128+ }
113129 }
114130 }
115131 break
@@ -118,19 +134,20 @@ class CheckedExceptionsDetector : Detector(), Detector.UastScanner {
118134 for (element in call.withContainingElements) {
119135 if (element !is UTryExpression ) continue
120136 for (catchCause in element.catchClauses) {
121- val clazzName = findExceptionClassName(catchCause)
122- ignoreExceptions.add(clazzName)
137+ catchCause.types.forEach { catch ->
138+ throwsExceptions = throwsExceptions.filterTo(HashMap ()) {
139+ ! it.value.contains(catch .canonicalText)
140+ }
141+ }
123142 }
124143 break
125144 }
126145
127146 for (exceptions in throwsExceptions) {
128- if (! ignoreExceptions.contains(exceptions)) {
129- context.report(
130- ISSUE_PATTERN , call, context.getNameLocation(call),
131- " Unhandled exception: $exceptions "
132- )
133- }
147+ context.report(
148+ ISSUE_PATTERN , call, context.getNameLocation(call),
149+ " Unhandled exception: ${exceptions.key} "
150+ )
134151 }
135152
136153 }
0 commit comments