Skip to content

Commit 950c4c8

Browse files
committed
Java/Kotlin: Make the basic query in docs work for both languages
1 parent 9ee3621 commit 950c4c8

File tree

1 file changed

+62
-48
lines changed

1 file changed

+62
-48
lines changed

docs/codeql/codeql-language-guides/basic-query-for-java-code.rst

Lines changed: 62 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,33 @@
11
.. _basic-query-for-java-code:
22

3-
Basic query for Java code
4-
=========================
3+
Basic query for Java and Kotlin code
4+
====================================
55

66
Learn to write and run a simple CodeQL query using LGTM.
77

88
About the query
99
---------------
1010

11-
The query we're going to run performs a basic search of the code for ``if`` statements that are redundant, in the sense that they have an empty then branch. For example, code such as:
11+
The query we're going to run searches for inefficient tests for empty strings. For example, Java code such as:
1212

1313
.. code-block:: java
1414
15-
if (error) { }
15+
public class TestJava {
16+
void myJavaFun(String s) {
17+
boolean b = s.equals("");
18+
}
19+
}
20+
21+
or Kotlin code such as:
22+
23+
.. code-block:: kotlin
24+
25+
void myKotlinFun(s: String) {
26+
var b = s.equals("")
27+
}
28+
29+
In either case, replacing ``s.equals("")`` with ``s.isEmpty()``
30+
would be more efficient.
1631

1732
Running the query
1833
-----------------
@@ -37,10 +52,13 @@ Running the query
3752
3853
import java
3954
40-
from IfStmt ifstmt, BlockStmt block
41-
where ifstmt.getThen() = block and
42-
block.getNumStmt() = 0
43-
select ifstmt, "This 'if' statement is redundant."
55+
from MethodAccess ma
56+
where
57+
ma.getMethod().hasName("equals") and
58+
ma.getArgument(0).(StringLiteral).getValue() = ""
59+
select ma, "This comparison to empty string is inefficient, use isEmpty() instead."
60+
61+
Note that CodeQL treats Java and Kotlin as part of the same language, so even though this query starts with ``import java``, it will work for both Java and Kotlin code.
4462

4563
LGTM checks whether your query compiles and, if all is well, the **Run** button changes to green to indicate that you can go ahead and run the query.
4664

@@ -57,44 +75,43 @@ Running the query
5775

5876
Your query is always run against the most recently analyzed commit to the selected project.
5977

60-
The query will take a few moments to return results. When the query completes, the results are displayed below the project name. The query results are listed in two columns, corresponding to the two expressions in the ``select`` clause of the query. The first column corresponds to the expression ``ifstmt`` and is linked to the location in the source code of the project where ``ifstmt`` occurs. The second column is the alert message.
78+
The query will take a few moments to return results. When the query completes, the results are displayed below the project name. The query results are listed in two columns, corresponding to the two expressions in the ``select`` clause of the query. The first column corresponds to the expression ``ma`` and is linked to the location in the source code of the project where ``ma`` occurs. The second column is the alert message.
6179

62-
➤ `Example query results <https://lgtm.com/query/8628882704378129297/>`__
80+
➤ `Example query results <https://lgtm.com/query/6863787472564633674/>`__
6381

6482
.. pull-quote::
6583

6684
Note
6785

6886
An ellipsis (…) at the bottom of the table indicates that the entire list is not displayed—click it to show more results.
6987

70-
#. If any matching code is found, click a link in the ``ifstmt`` column to view the ``if`` statement in the code viewer.
88+
#. If any matching code is found, click a link in the ``ma`` column to view the ``.equals`` expression in the code viewer.
7189

72-
The matching ``if`` statement is highlighted with a yellow background in the code viewer. If any code in the file also matches a query from the standard query library for that language, you will see a red alert message at the appropriate point within the code.
90+
The matching ``.equals`` expression is highlighted with a yellow background in the code viewer. If any code in the file also matches a query from the standard query library for that language, you will see a red alert message at the appropriate point within the code.
7391

7492
About the query structure
7593
~~~~~~~~~~~~~~~~~~~~~~~~~
7694

7795
After the initial ``import`` statement, this simple query comprises three parts that serve similar purposes to the FROM, WHERE, and SELECT parts of an SQL query.
7896

79-
+---------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------+------------------------------------------------------------------------------------------------------------------------+
80-
| Query part | Purpose | Details |
81-
+===============================================================+===================================================================================================================+========================================================================================================================+
82-
| ``import java`` | Imports the standard CodeQL libraries for Java. | Every query begins with one or more ``import`` statements. |
83-
+---------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------+------------------------------------------------------------------------------------------------------------------------+
84-
| ``from IfStmt ifstmt, BlockStmt block`` | Defines the variables for the query. | We use: |
85-
| | Declarations are of the form: | |
86-
| | ``<type> <variable name>`` | - an ``IfStmt`` variable for ``if`` statements |
87-
| | | - a ``BlockStmt`` variable for the ``then`` block |
88-
+---------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------+------------------------------------------------------------------------------------------------------------------------+
89-
| ``where ifstmt.getThen() = block and block.getNumStmt() = 0`` | Defines a condition on the variables. | ``ifstmt.getThen() = block`` relates the two variables. The block must be the ``then`` branch of the ``if`` statement. |
90-
| | | |
91-
| | | ``block.getNumStmt() = 0`` states that the block must be empty (that is, it contains no statements). |
92-
+---------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------+------------------------------------------------------------------------------------------------------------------------+
93-
| ``select ifstmt, "This 'if' statement is redundant."`` | Defines what to report for each match. | Reports the resulting ``if`` statement with a string that explains the problem. |
94-
| | | |
95-
| | ``select`` statements for queries that are used to find instances of poor coding practice are always in the form: | |
96-
| | ``select <program element>, "<alert message>"`` | |
97-
+---------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------+------------------------------------------------------------------------------------------------------------------------+
97+
+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------+
98+
| Query part | Purpose | Details |
99+
+==================================================================================================+===================================================================================================================+===================================================================================================+
100+
| ``import java`` | Imports the standard CodeQL libraries for Java. | Every query begins with one or more ``import`` statements. |
101+
+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------+
102+
| ``from MethodAccess ma`` | Defines the variables for the query. | We use: |
103+
| | Declarations are of the form: | |
104+
| | ``<type> <variable name>`` | - a ``MethodAccess`` variable for call expressions |
105+
+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------+
106+
| ``where ma.getMethod().hasName("equals") and ma.getArgument(0).(StringLiteral).getValue() = ""`` | Defines a condition on the variables. | ``ma.getMethod().hasName("equals")`` restricts ``ma`` to only calls to methods call ``equals``. |
107+
| | | |
108+
| | | ``ma.getArgument(0).(StringLiteral).getValue() = ""`` says the argument must be literal ``""``. |
109+
+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------+
110+
| ``select ma, "This comparison to empty string is inefficient, use isEmpty() instead."`` | Defines what to report for each match. | Reports the resulting ``.equals`` expression with a string that explains the problem. |
111+
| | | |
112+
| | ``select`` statements for queries that are used to find instances of poor coding practice are always in the form: | |
113+
| | ``select <program element>, "<alert message>"`` | |
114+
+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------+
98115

99116
Extend the query
100117
----------------
@@ -104,41 +121,38 @@ Query writing is an inherently iterative process. You write a simple query and t
104121
Remove false positive results
105122
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
106123

107-
Browsing the results of our basic query shows that it could be improved. Among the results you are likely to find examples of ``if`` statements with an ``else`` branch, where an empty ``then`` branch does serve a purpose. For example:
124+
Browsing the results of our basic query shows that it could be improved. For example, you may find results for code like:
108125

109126
.. code-block:: java
110127
111-
if (...) {
112-
...
113-
} else if ("-verbose".equals(option)) {
114-
// nothing to do - handled earlier
115-
} else {
116-
error("unrecognized option");
117-
}
118-
119-
In this case, identifying the ``if`` statement with the empty ``then`` branch as redundant is a false positive. One solution to this is to modify the query to ignore empty ``then`` branches if the ``if`` statement has an ``else`` branch.
128+
public class TestJava {
129+
void myJavaFun(Object o) {
130+
boolean b = o.equals("");
131+
}
132+
}
120133
121-
To exclude ``if`` statements that have an ``else`` branch:
134+
In this case, it is not possible to simply use ``o.isEmpty()`` instead, as ``o`` has type ``Object`` rather than ``String``. One solution to this is to modify the query to only return results where the expression being tested has type ``String``:
122135

123136
#. Extend the where clause to include the following extra condition:
124137

125138
.. code-block:: ql
126139
127-
and not exists(ifstmt.getElse())
140+
ma.getQualifier().getType() instanceof TypeString
128141
129142
The ``where`` clause is now:
130143

131144
.. code-block:: ql
132145
133-
where ifstmt.getThen() = block and
134-
block.getNumStmt() = 0 and
135-
not exists(ifstmt.getElse())
146+
where
147+
ma.getQualifier().getType() instanceof TypeString and
148+
ma.getMethod().hasName("equals") and
149+
ma.getArgument(0).(StringLiteral).getValue() = ""
136150
137151
#. Click **Run**.
138152

139-
There are now fewer results because ``if`` statements with an ``else`` branch are no longer included.
153+
There are now fewer results because ``.equals`` expressions with different types are no longer included.
140154

141-
➤ `See this in the query console <https://lgtm.com/query/2005778170075484819/>`__
155+
➤ `See this in the query console <https://lgtm.com/query/3716567543394265485/>`__
142156

143157
Further reading
144158
---------------

0 commit comments

Comments
 (0)