This repository has been archived by the owner on Aug 17, 2020. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 417
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Question marks in a query don't match number of arguments lint check (#…
…220)
- Loading branch information
1 parent
7abcba4
commit 9ba06a9
Showing
7 changed files
with
335 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,6 @@ | ||
include ':sqlbrite' | ||
include ':sqlbrite-kotlin' | ||
include ':sqlbrite-lint' | ||
include ':sample' | ||
|
||
rootProject.name = 'sqlbrite-root' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
apply plugin: 'kotlin' | ||
|
||
dependencies { | ||
compileOnly rootProject.ext.kotlinStdLib | ||
compileOnly rootProject.ext.lintApi | ||
|
||
testImplementation rootProject.ext.junit | ||
testImplementation rootProject.ext.lint | ||
testImplementation rootProject.ext.lintTests | ||
} | ||
|
||
jar { | ||
manifest { | ||
attributes("Lint-Registry-v2": "com.squareup.sqlbrite3.BriteIssueRegistry") | ||
} | ||
} |
23 changes: 23 additions & 0 deletions
23
sqlbrite-lint/src/main/java/com/squareup/sqlbrite3/BriteIssueRegistry.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
/* | ||
* Copyright (C) 2017 Square, Inc. | ||
* | ||
* 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.squareup.sqlbrite3 | ||
|
||
import com.android.tools.lint.client.api.IssueRegistry | ||
|
||
class BriteIssueRegistry : IssueRegistry() { | ||
|
||
override fun getIssues() = listOf(SqlBriteArgCountDetector.ISSUE) | ||
} |
79 changes: 79 additions & 0 deletions
79
sqlbrite-lint/src/main/java/com/squareup/sqlbrite3/SqlBriteArgCountDetector.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
/* | ||
* Copyright (C) 2017 Square, Inc. | ||
* | ||
* 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.squareup.sqlbrite3 | ||
|
||
import com.android.tools.lint.detector.api.Category | ||
import com.android.tools.lint.detector.api.ConstantEvaluator.evaluateString | ||
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.JAVA_FILE | ||
import com.android.tools.lint.detector.api.Scope.TEST_SOURCES | ||
import com.android.tools.lint.detector.api.Severity | ||
import com.intellij.psi.PsiMethod | ||
import org.jetbrains.uast.UCallExpression | ||
import java.util.EnumSet | ||
|
||
private const val BRITE_DATABASE = "com.squareup.sqlbrite3.BriteDatabase" | ||
private const val QUERY_METHOD_NAME = "query" | ||
private const val CREATE_QUERY_METHOD_NAME = "createQuery" | ||
|
||
class SqlBriteArgCountDetector : Detector(), Detector.UastScanner { | ||
|
||
companion object { | ||
|
||
val ISSUE: Issue = Issue.create( | ||
"SqlBriteArgCount", | ||
"Number of provided arguments doesn't match number " + | ||
"of arguments specified in query", | ||
"When providing arguments to query you need to provide the same amount of " + | ||
"arguments that is specified in query.", | ||
Category.MESSAGES, | ||
9, | ||
Severity.ERROR, | ||
Implementation(SqlBriteArgCountDetector::class.java, EnumSet.of(JAVA_FILE, TEST_SOURCES))) | ||
} | ||
|
||
override fun getApplicableMethodNames() = listOf(CREATE_QUERY_METHOD_NAME, QUERY_METHOD_NAME) | ||
|
||
override fun visitMethod(context: JavaContext, call: UCallExpression, method: PsiMethod) { | ||
val evaluator = context.evaluator | ||
|
||
if (evaluator.isMemberInClass(method, BRITE_DATABASE)) { | ||
// Skip non varargs overloads. | ||
if (!method.isVarArgs) return | ||
|
||
// Position of sql parameter depends on method. | ||
val sql = evaluateString(context, | ||
call.valueArguments[if (call.isQueryMethod()) 0 else 1], true) ?: return | ||
|
||
// Count only vararg arguments. | ||
val argumentsCount = call.valueArgumentCount - if (call.isQueryMethod()) 1 else 2 | ||
val questionMarksCount = sql.count { it == '?' } | ||
if (argumentsCount != questionMarksCount) { | ||
val requiredArguments = "$questionMarksCount ${"argument".pluralize(questionMarksCount)}" | ||
val actualArguments = "$argumentsCount ${"argument".pluralize(argumentsCount)}" | ||
context.report(ISSUE, call, context.getLocation(call), "Wrong argument count, " + | ||
"query $sql requires $requiredArguments, but was provided $actualArguments") | ||
} | ||
} | ||
} | ||
|
||
private fun UCallExpression.isQueryMethod() = methodName == QUERY_METHOD_NAME | ||
|
||
private fun String.pluralize(count: Int) = if (count == 1) this else this + "s" | ||
} |
207 changes: 207 additions & 0 deletions
207
sqlbrite-lint/src/test/java/com/squareup/sqlbrite3/SqlBriteArgCountDetectorTest.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,207 @@ | ||
/* | ||
* Copyright (C) 2017 Square, Inc. | ||
* | ||
* 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.squareup.sqlbrite3 | ||
|
||
import com.android.tools.lint.checks.infrastructure.TestFiles.java | ||
import com.android.tools.lint.checks.infrastructure.TestLintTask.lint | ||
import org.junit.Test | ||
|
||
class SqlBriteArgCountDetectorTest { | ||
|
||
companion object { | ||
private val BRITE_DATABASE_STUB = java( | ||
""" | ||
package com.squareup.sqlbrite3; | ||
public final class BriteDatabase { | ||
public void query(String sql, Object... args) { | ||
} | ||
public void createQuery(String table, String sql, Object... args) { | ||
} | ||
// simulate createQuery with SupportSQLiteQuery query parameter | ||
public void createQuery(String table, int something) { | ||
} | ||
} | ||
""".trimIndent() | ||
) | ||
} | ||
|
||
@Test | ||
fun cleanCaseWithWithQueryAsLiteral() { | ||
lint().files( | ||
BRITE_DATABASE_STUB, | ||
java( | ||
""" | ||
package test.pkg; | ||
import com.squareup.sqlbrite3.BriteDatabase; | ||
public class Test { | ||
private static final String QUERY = "SELECT name FROM table WHERE id = ?"; | ||
public void test() { | ||
BriteDatabase db = new BriteDatabase(); | ||
db.query(QUERY, "id"); | ||
} | ||
} | ||
""".trimIndent())) | ||
.issues(SqlBriteArgCountDetector.ISSUE) | ||
.run() | ||
.expectClean() | ||
} | ||
|
||
@Test | ||
fun cleanCaseWithQueryAsBinaryExpression() { | ||
lint().files( | ||
BRITE_DATABASE_STUB, | ||
java( | ||
""" | ||
package test.pkg; | ||
import com.squareup.sqlbrite3.BriteDatabase; | ||
public class Test { | ||
private static final String QUERY = "SELECT name FROM table WHERE "; | ||
public void test() { | ||
BriteDatabase db = new BriteDatabase(); | ||
db.query(QUERY + "id = ?", "id"); | ||
} | ||
} | ||
""".trimIndent())) | ||
.issues(SqlBriteArgCountDetector.ISSUE) | ||
.run() | ||
.expectClean() | ||
} | ||
|
||
@Test | ||
fun cleanCaseWithQueryThatCantBeEvaluated() { | ||
lint().files( | ||
BRITE_DATABASE_STUB, | ||
java( | ||
""" | ||
package test.pkg; | ||
import com.squareup.sqlbrite3.BriteDatabase; | ||
public class Test { | ||
private static final String QUERY = "SELECT name FROM table WHERE id = ?"; | ||
public void test() { | ||
BriteDatabase db = new BriteDatabase(); | ||
db.query(query(), "id"); | ||
} | ||
private String query() { | ||
return QUERY + " age = ?"; | ||
} | ||
} | ||
""".trimIndent())) | ||
.issues(SqlBriteArgCountDetector.ISSUE) | ||
.run() | ||
.expectClean() | ||
} | ||
|
||
@Test | ||
fun cleanCaseWithNonVarargMethodCall() { | ||
lint().files( | ||
BRITE_DATABASE_STUB, | ||
java( | ||
""" | ||
package test.pkg; | ||
import com.squareup.sqlbrite3.BriteDatabase; | ||
public class Test { | ||
public void test() { | ||
BriteDatabase db = new BriteDatabase(); | ||
db.createQuery("table", 42); | ||
} | ||
} | ||
""".trimIndent())) | ||
.issues(SqlBriteArgCountDetector.ISSUE) | ||
.run() | ||
.expectClean() | ||
} | ||
|
||
@Test | ||
fun queryMethodWithWrongNumberOfArguments() { | ||
lint().files( | ||
BRITE_DATABASE_STUB, | ||
java( | ||
""" | ||
package test.pkg; | ||
import com.squareup.sqlbrite3.BriteDatabase; | ||
public class Test { | ||
private static final String QUERY = "SELECT name FROM table WHERE id = ?"; | ||
public void test() { | ||
BriteDatabase db = new BriteDatabase(); | ||
db.query(QUERY); | ||
} | ||
} | ||
""".trimIndent())) | ||
.issues(SqlBriteArgCountDetector.ISSUE) | ||
.run() | ||
.expect("src/test/pkg/Test.java:10: " + | ||
"Error: Wrong argument count, query SELECT name FROM table WHERE id = ?" + | ||
" requires 1 argument, but was provided 0 arguments [SqlBriteArgCount]\n" + | ||
" db.query(QUERY);\n" + | ||
" ~~~~~~~~~~~~~~~\n" + | ||
"1 errors, 0 warnings") | ||
} | ||
|
||
@Test | ||
fun createQueryMethodWithWrongNumberOfArguments() { | ||
lint().files( | ||
BRITE_DATABASE_STUB, | ||
java( | ||
""" | ||
package test.pkg; | ||
import com.squareup.sqlbrite3.BriteDatabase; | ||
public class Test { | ||
private static final String QUERY = "SELECT name FROM table WHERE id = ?"; | ||
public void test() { | ||
BriteDatabase db = new BriteDatabase(); | ||
db.createQuery("table", QUERY); | ||
} | ||
} | ||
""".trimIndent())) | ||
.issues(SqlBriteArgCountDetector.ISSUE) | ||
.run() | ||
.expect("src/test/pkg/Test.java:10: " + | ||
"Error: Wrong argument count, query SELECT name FROM table WHERE id = ?" + | ||
" requires 1 argument, but was provided 0 arguments [SqlBriteArgCount]\n" + | ||
" db.createQuery(\"table\", QUERY);\n" + | ||
" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" + | ||
"1 errors, 0 warnings") | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters