Skip to content

Commit aa88bd2

Browse files
committed
Added MAC OS sdk path in utils
Catch exceptions from java constructor Exceptions superclasses catch exceptions
1 parent f420b95 commit aa88bd2

File tree

4 files changed

+180
-27
lines changed

4 files changed

+180
-27
lines changed

lint-rules-android/src/main/java/com/thirdegg/lintrules/android/CheckedExceptionsDetector.kt

Lines changed: 44 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,11 @@ import com.android.tools.lint.detector.api.*
55
import com.android.tools.lint.detector.api.Category.Companion.CORRECTNESS
66
import com.android.tools.lint.detector.api.Severity.WARNING
77
import com.intellij.psi.PsiElement
8+
import com.intellij.psi.PsiType
89
import org.jetbrains.uast.*
910
import java.util.*
1011
import kotlin.collections.ArrayList
12+
import kotlin.collections.HashMap
1113
import 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
}

lint-rules-android/src/test/java/com/thirdegg/lintrules/android/Utils.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ object Utils {
88
// TestUtils.getSdk() not working
99
return if (System.getProperty("os.name").startsWith("Windows")) {
1010
File(System.getenv("LOCALAPPDATA")+"\\Android\\Sdk")
11+
} else if (System.getProperty("os.name").contains("Mac OS X")) {
12+
File(System.getProperty("user.home")+"/Library/Android/Sdk/")
1113
} else {
1214
File(System.getProperty("user.home")+"/Android/Sdk/")
1315
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
package com.thirdegg.lintrules.android.testcases
2+
3+
import com.android.tools.lint.checks.infrastructure.TestFiles
4+
import com.android.tools.lint.checks.infrastructure.TestLintTask
5+
import com.thirdegg.lintrules.android.ISSUE_PATTERN
6+
import com.thirdegg.lintrules.android.Utils
7+
import org.junit.Test
8+
9+
class StdJavaTestCase {
10+
11+
private val TestClassKotlin = TestFiles.kotlin("""
12+
13+
package com.thirdegg.lintrules.android
14+
15+
import java.util.Scanner
16+
import java.io.File
17+
import java.io.FileReader
18+
19+
class TestClassKotlin {
20+
fun test() {
21+
val file = File("file")
22+
val reader = FileReader(file)
23+
Scanner(File("test"))
24+
}
25+
}
26+
27+
28+
""").indented()
29+
30+
31+
32+
@Test
33+
fun check_kotlin() {
34+
35+
TestLintTask.lint()
36+
.sdkHome(Utils.getSdk())
37+
.issues(ISSUE_PATTERN)
38+
.files(TestClassKotlin)
39+
.run()
40+
.expect("""
41+
src/com/thirdegg/lintrules/android/TestClassKotlin.kt:11: Warning: Unhandled exception: java.io.FileNotFoundException [CheckedExceptions]
42+
val reader = FileReader(file)
43+
~~~~~~~~~~
44+
src/com/thirdegg/lintrules/android/TestClassKotlin.kt:12: Warning: Unhandled exception: java.io.FileNotFoundException [CheckedExceptions]
45+
Scanner(File("test"))
46+
~~~~~~~
47+
0 errors, 2 warnings
48+
""".trimIndent())
49+
50+
}
51+
52+
}
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
package com.thirdegg.lintrules.android.testcases
2+
3+
import com.android.tools.lint.checks.infrastructure.TestFiles
4+
import com.android.tools.lint.checks.infrastructure.TestLintTask
5+
import com.thirdegg.lintrules.android.ISSUE_PATTERN
6+
import com.thirdegg.lintrules.android.Utils
7+
import org.junit.Test
8+
9+
10+
class SuperCatchThrowsTestCase {
11+
12+
val SuperCatchThrowsClassKotlin = TestFiles.kotlin("""
13+
14+
package com.thirdegg.lintrules.android
15+
16+
import java.io.IOException
17+
import java.lang.Exception
18+
19+
class CheckLint {
20+
21+
@Throws(SuperPuperException::class)
22+
fun openFile() {
23+
throw SuperPuperException()
24+
}
25+
26+
@Throws(SuperException::class)
27+
fun doesIO() {
28+
openFile()
29+
}
30+
31+
@Throws(SuperPuperException::class)
32+
fun doesIOTwo() {
33+
doesIO()
34+
}
35+
36+
37+
fun test() {
38+
try {
39+
doesIO()
40+
} catch(e: IOException) {
41+
e.printStackTrace()
42+
}
43+
try {
44+
doesIO()
45+
} catch(e: SuperPuperException) {
46+
e.printStackTrace()
47+
}
48+
doesIO()
49+
}
50+
51+
class SuperException:IOException()
52+
53+
class SuperPuperException:SuperException()
54+
55+
}
56+
57+
""".trimIndent())
58+
59+
@Test
60+
fun check_kotlin() {
61+
62+
TestLintTask.lint()
63+
.sdkHome(Utils.getSdk())
64+
.issues(ISSUE_PATTERN)
65+
.files(SuperCatchThrowsClassKotlin)
66+
.run()
67+
.expect("""
68+
src/com/thirdegg/lintrules/android/CheckLint.kt:21: Warning: Unhandled exception: com.thirdegg.lintrules.android.CheckLint.SuperException [CheckedExceptions]
69+
doesIO()
70+
~~~~~~
71+
src/com/thirdegg/lintrules/android/CheckLint.kt:32: Warning: Unhandled exception: com.thirdegg.lintrules.android.CheckLint.SuperException [CheckedExceptions]
72+
doesIO()
73+
~~~~~~
74+
src/com/thirdegg/lintrules/android/CheckLint.kt:36: Warning: Unhandled exception: com.thirdegg.lintrules.android.CheckLint.SuperException [CheckedExceptions]
75+
doesIO()
76+
~~~~~~
77+
0 errors, 3 warnings
78+
""".trimIndent())
79+
80+
}
81+
82+
}

0 commit comments

Comments
 (0)