Merge remote-tracking branch 'origin/codeql-cli-2.11.5' into smowton/admin/merge-2.11.5-into-rc38

This commit is contained in:
Chris Smowton
2022-12-07 20:06:08 +00:00
57 changed files with 1083 additions and 418 deletions

View File

@@ -3,7 +3,9 @@
Basic query for C and C++ code
==============================
Learn to write and run a simple CodeQL query using LGTM.
Learn to write and run a simple CodeQL query using Visual Studio Code with the CodeQL extension.
.. include:: ../reusables/vs-code-basic-instructions/setup-to-run-queries.rst
About the query
---------------
@@ -14,62 +16,33 @@ The query we're going to run performs a basic search of the code for ``if`` stat
if (error) { }
Running the query
-----------------
.. include:: ../reusables/vs-code-basic-instructions/find-database.rst
#. In the main search box on LGTM.com, search for the project you want to query. For tips, see `Searching <https://lgtm.com/help/lgtm/searching>`__.
Running a quick query
---------------------
#. Click the project in the search results.
.. include:: ../reusables/vs-code-basic-instructions/run-quick-query-1.rst
#. Click **Query this project**.
This opens the query console. (For information about using this, see `Using the query console <https://lgtm.com/help/lgtm/using-query-console>`__.)
.. pull-quote::
Note
Alternatively, you can go straight to the query console by clicking **Query console** (at the top of any page), selecting **C/C++** from the **Language** drop-down list, then choosing one or more projects to query from those displayed in the **Project** drop-down list.
#. Copy the following query into the text box in the query console:
#. In the quick query tab, delete ``select ""`` and paste the following query beneath the import statement ``import cpp``.
.. code-block:: ql
import cpp
from IfStmt ifstmt, BlockStmt block
where ifstmt.getThen() = block and
block.getNumStmt() = 0
select ifstmt, "This 'if' statement is redundant."
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.
.. include:: ../reusables/vs-code-basic-instructions/run-quick-query-2.rst
#. Click **Run**.
.. image:: ../images/codeql-for-visual-studio-code/basic-cpp-query-results-1.png
:align: center
The name of the project you are querying, and the ID of the most recently analyzed commit to the project, are listed below the query box. To the right of this is an icon that indicates the progress of the query operation:
If any matching code is found, click a link in the ``ifstmt`` column to open the file and highlight the matching ``if`` statement.
.. image:: ../images/query-progress.png
:align: center
.. image:: ../images/codeql-for-visual-studio-code/basic-cpp-query-results-2.png
:align: center
.. pull-quote::
Note
Your query is always run against the most recently analyzed commit to the selected project.
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.
`Example query results <https://lgtm.com/query/4242591143131494898/>`__
.. pull-quote::
Note
An ellipsis (…) at the bottom of the table indicates that the entire list is not displayed—click it to show more results.
#. If any matching code is found, click a link in the ``ifstmt`` column to view the ``if`` statement in the code viewer.
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.
.. include:: ../reusables/vs-code-basic-instructions/note-store-quick-query.rst
About the query structure
~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -120,7 +93,7 @@ In this case, identifying the ``if`` statement with the empty ``then`` branch as
To exclude ``if`` statements that have an ``else`` branch:
#. Extend the ``where`` clause to include the following extra condition:
#. Edit your query and extend the ``where`` clause to include the following extra condition:
.. code-block:: ql
@@ -134,14 +107,24 @@ To exclude ``if`` statements that have an ``else`` branch:
block.getNumStmt() = 0 and
not ifstmt.hasElse()
#. Click **Run**.
#. Re-run the query.
There are now fewer results because ``if`` statements with an ``else`` branch are no longer reported.
`See this in the query console <https://lgtm.com/query/1899933116489579248/>`__
Further reading
---------------
.. include:: ../reusables/cpp-further-reading.rst
.. include:: ../reusables/codeql-ref-tools-further-reading.rst
.. Article-specific substitutions for the reusables used in docs/codeql/reusables/vs-code-basic-instructions
.. |language-text| replace:: C/C++
.. |language-code| replace:: ``cpp``
.. |example-url| replace:: https://github.com/protocolbuffers/protobuf
.. |image-quick-query| image:: ../images/codeql-for-visual-studio-code/quick-query-tab-cpp.png
.. |result-col-1| replace:: The first column corresponds to the expression ``ifstmt`` and is linked to the location in the source code of the project where ``ifstmt`` occurs.

View File

@@ -3,7 +3,9 @@
Basic query for C# code
=======================
Learn to write and run a simple CodeQL query using LGTM.
Learn to write and run a simple CodeQL query using Visual Studio Code with the CodeQL extension.
.. include:: ../reusables/vs-code-basic-instructions/setup-to-run-queries.rst
About the query
---------------
@@ -14,62 +16,33 @@ The query we're going to run performs a basic search of the code for ``if`` stat
if (error) { }
Running the query
-----------------
.. include:: ../reusables/vs-code-basic-instructions/find-database.rst
#. In the main search box on LGTM.com, search for the project you want to query. For tips, see `Searching <https://lgtm.com/help/lgtm/searching>`__.
Running a quick query
---------------------
#. Click the project in the search results.
.. include:: ../reusables/vs-code-basic-instructions/run-quick-query-1.rst
#. Click **Query this project**.
This opens the query console. (For information about using this, see `Using the query console <https://lgtm.com/help/lgtm/using-query-console>`__.)
.. pull-quote::
Note
Alternatively, you can go straight to the query console by clicking **Query console** (at the top of any page), selecting **C#** from the **Language** drop-down list, then choosing one or more projects to query from those displayed in the **Project** drop-down list.
#. Copy the following query into the text box in the query console:
#. In the quick query tab, delete ``select ""`` and paste the following query beneath the import statement ``import csharp``.
.. code-block:: ql
import csharp
from IfStmt ifstmt, BlockStmt block
where ifstmt.getThen() = block and
block.isEmpty()
select ifstmt, "This 'if' statement is redundant."
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.
.. include:: ../reusables/vs-code-basic-instructions/run-quick-query-2.rst
#. Click **Run**.
.. image:: ../images/codeql-for-visual-studio-code/basic-csharp-query-results-1.png
:align: center
The name of the project you are querying, and the ID of the most recently analyzed commit to the project, are listed below the query box. To the right of this is an icon that indicates the progress of the query operation:
If any matching code is found, click a link in the ``ifstmt`` column to open the file and highlight the matching ``if`` statement.
.. image:: ../images/query-progress.png
:align: center
.. image:: ../images/codeql-for-visual-studio-code/basic-csharp-query-results-2.png
:align: center
.. pull-quote::
Note
Your query is always run against the most recently analyzed commit to the selected project.
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.
`Example query results <https://lgtm.com/query/1214010107827821393/>`__
.. pull-quote::
Note
An ellipsis (…) at the bottom of the table indicates that the entire list is not displayed—click it to show more results.
#. If any matching code is found, click a link in the ``ifstmt`` column to view the ``if`` statement in the code viewer.
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.
.. include:: ../reusables/vs-code-basic-instructions/note-store-quick-query.rst
About the query structure
~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -139,14 +112,23 @@ To exclude ``if`` statements that have an ``else`` branch:
block.isEmpty() and
not exists(ifstmt.getElse())
#. Click **Run**.
#. Re-run the query.
There are now fewer results because ``if`` statements with an ``else`` branch are no longer included.
`See this in the query console <https://lgtm.com/query/6233102733683510530/>`__
Further reading
---------------
.. include:: ../reusables/csharp-further-reading.rst
.. include:: ../reusables/codeql-ref-tools-further-reading.rst
.. Article-specific substitutions for the reusables used in docs/codeql/reusables/vs-code-basic-instructions
.. |language-text| replace:: C#
.. |language-code| replace:: ``csharp``
.. |example-url| replace:: https://github.com/PowerShell/PowerShell
.. |image-quick-query| image:: ../images/codeql-for-visual-studio-code/quick-query-tab-csharp.png
.. |result-col-1| replace:: The first column corresponds to the expression ``ifstmt`` and is linked to the location in the source code of the project where ``ifstmt`` occurs.

View File

@@ -3,7 +3,9 @@
Basic query for Go code
=======================
Learn to write and run a simple CodeQL query using LGTM.
Learn to write and run a simple CodeQL query using Visual Studio Code with the CodeQL extension.
.. include:: ../reusables/vs-code-basic-instructions/setup-to-run-queries.rst
About the query
---------------
@@ -22,29 +24,17 @@ This is problematic because the receiver argument is passed by value, not by ref
For further information on using methods on values or pointers in Go, see the `Go FAQ <https://golang.org/doc/faq#methods_on_values_or_pointers>`__.
Running the query
-----------------
.. include:: ../reusables/vs-code-basic-instructions/find-database.rst
#. In the main search box on LGTM.com, search for the project you want to query. For tips, see `Searching <https://lgtm.com/help/lgtm/searching>`__.
Running a quick query
---------------------
#. Click the project in the search results.
.. include:: ../reusables/vs-code-basic-instructions/run-quick-query-1.rst
#. Click **Query this project**.
This opens the query console. (For information about using this, see `Using the query console <https://lgtm.com/help/lgtm/using-query-console>`__.)
.. pull-quote::
Note
Alternatively, you can go straight to the query console by clicking **Query console** (at the top of any page), selecting **Go** from the **Language** drop-down list, then choosing one or more projects to query from those displayed in the **Project** drop-down list.
#. Copy the following query into the text box in the query console:
#. In the quick query tab, delete ``select ""`` and paste the following query beneath the import statement ``import go``.
.. code-block:: ql
import go
from Method m, Variable recv, Write w, Field f
where
recv = m.getReceiver() and
@@ -52,34 +42,17 @@ Running the query
not recv.getType() instanceof PointerType
select w, "This update to " + f + " has no effect, because " + recv + " is not a pointer."
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.
.. include:: ../reusables/vs-code-basic-instructions/run-quick-query-2.rst
#. Click **Run**.
.. image:: ../images/codeql-for-visual-studio-code/basic-go-query-results-1.png
:align: center
The name of the project you are querying, and the ID of the most recently analyzed commit to the project, are listed below the query box. To the right of this is an icon that indicates the progress of the query operation:
If any matching code is found, click a link in the ``w`` column to open the file and highlight the matching location.
.. image:: ../images/query-progress.png
:align: center
.. image:: ../images/codeql-for-visual-studio-code/basic-go-query-results-2.png
:align: center
.. pull-quote::
Note
Your query is always run against the most recently analyzed commit to the selected project.
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 ``w``, which is the location in the source code where the receiver ``recv`` is modified. The second column is the alert message.
`Example query results <https://lgtm.com/query/6221190009056970603/>`__
.. pull-quote::
Note
An ellipsis (…) at the bottom of the table indicates that the entire list is not displayed—click it to show more results.
#. If any matching code is found, click a link in the ``w`` column to view it in the code viewer.
The matching ``w`` 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.
.. include:: ../reusables/vs-code-basic-instructions/note-store-quick-query.rst
About the query structure
~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -140,14 +113,24 @@ To exclude these values:
not recv.getType() instanceof PointerType and
not exists(ReturnStmt ret | ret.getExpr() = recv.getARead().asExpr())
#. Click **Run**.
#. Re-run the query.
There are now fewer results because value methods that return their receiver variable are no longer reported.
`See this in the query console <https://lgtm.com/query/9110448975027954322/>`__
Further reading
---------------
.. include:: ../reusables/go-further-reading.rst
.. include:: ../reusables/codeql-ref-tools-further-reading.rst
.. Article-specific substitutions for the reusables used in docs/codeql/reusables/vs-code-basic-instructions
.. |language-text| replace:: Go
.. |language-code| replace:: ``go``
.. |example-url| replace:: https://github.com/go-gorm/gorm
.. |image-quick-query| image:: ../images/codeql-for-visual-studio-code/quick-query-tab-go.png
.. |result-col-1| replace:: The first column corresponds to ``w``, which is the location in the source code where the receiver ``recv`` is modified.

View File

@@ -3,7 +3,9 @@
Basic query for Java and Kotlin code
====================================
Learn to write and run a simple CodeQL query using LGTM.
Learn to write and run a simple CodeQL query using Visual Studio Code with the CodeQL extension.
.. include:: ../reusables/vs-code-basic-instructions/setup-to-run-queries.rst
About the query
---------------
@@ -29,65 +31,36 @@ or Kotlin code such as:
In either case, replacing ``s.equals("")`` with ``s.isEmpty()``
would be more efficient.
Running the query
-----------------
.. include:: ../reusables/vs-code-basic-instructions/find-database.rst
#. In the main search box on LGTM.com, search for the project you want to query. For tips, see `Searching <https://lgtm.com/help/lgtm/searching>`__.
Running a quick query
---------------------
#. Click the project in the search results.
.. include:: ../reusables/vs-code-basic-instructions/run-quick-query-1.rst
#. Click **Query this project**.
This opens the query console. (For information about using this, see `Using the query console <https://lgtm.com/help/lgtm/using-query-console>`__.)
.. pull-quote::
Note
Alternatively, you can go straight to the query console by clicking **Query console** (at the top of any page), selecting **Java** from the **Language** drop-down list, then choosing one or more projects to query from those displayed in the **Project** drop-down list.
#. Copy the following query into the text box in the query console:
#. In the quick query tab, delete ``select ""`` and paste the following query beneath the import statement ``import java``.
.. code-block:: ql
import java
from MethodAccess ma
where
ma.getMethod().hasName("equals") and
ma.getArgument(0).(StringLiteral).getValue() = ""
select ma, "This comparison to empty string is inefficient, use isEmpty() instead."
from MethodAccess ma
where
ma.getMethod().hasName("equals") and
ma.getArgument(0).(StringLiteral).getValue() = ""
select ma, "This comparison to empty string is inefficient, use isEmpty() instead."
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.
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.
.. include:: ../reusables/vs-code-basic-instructions/run-quick-query-2.rst
#. Click **Run**.
.. image:: ../images/codeql-for-visual-studio-code/basic-java-query-results-1.png
:align: center
The name of the project you are querying, and the ID of the most recently analyzed commit to the project, are listed below the query box. To the right of this is an icon that indicates the progress of the query operation:
If any matching code is found, click a link in the ``ma`` column to view the ``.equals`` expression in the code viewer.
.. image:: ../images/query-progress.png
:align: center
.. image:: ../images/codeql-for-visual-studio-code/basic-java-query-results-2.png
:align: center
.. pull-quote::
Note
Your query is always run against the most recently analyzed commit to the selected project.
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.
`Example query results <https://lgtm.com/query/6863787472564633674/>`__
.. pull-quote::
Note
An ellipsis (…) at the bottom of the table indicates that the entire list is not displayed—click it to show more results.
#. If any matching code is found, click a link in the ``ma`` column to view the ``.equals`` expression in the code viewer.
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.
.. include:: ../reusables/vs-code-basic-instructions/note-store-quick-query.rst
About the query structure
~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -148,14 +121,24 @@ In this case, it is not possible to simply use ``o.isEmpty()`` instead, as ``o``
ma.getMethod().hasName("equals") and
ma.getArgument(0).(StringLiteral).getValue() = ""
#. Click **Run**.
#. Re-run the query.
There are now fewer results because ``.equals`` expressions with different types are no longer included.
`See this in the query console <https://lgtm.com/query/3716567543394265485/>`__
Further reading
---------------
.. include:: ../reusables/java-further-reading.rst
.. include:: ../reusables/codeql-ref-tools-further-reading.rst
.. Article-specific substitutions for the reusables used in docs/codeql/reusables/vs-code-basic-instructions
.. |language-text| replace:: Java
.. |language-code| replace:: ``java``
.. |example-url| replace:: https://github.com/apache/activemq
.. |image-quick-query| image:: ../images/codeql-for-visual-studio-code/quick-query-tab-java.png
.. |result-col-1| replace:: The first column corresponds to the expression ``ma`` and is linked to the location in the source code of the project where ``ma`` occurs.

View File

@@ -3,7 +3,9 @@
Basic query for JavaScript code
===============================
Learn to write and run a simple CodeQL query using LGTM.
Learn to write and run a simple CodeQL query using Visual Studio Code with the CodeQL extension.
.. include:: ../reusables/vs-code-basic-instructions/setup-to-run-queries.rst
About the query
---------------
@@ -12,62 +14,33 @@ In JavaScript, any expression can be turned into an expression statement. While
The query you will run finds instances of this problem. The query searches for expressions ``e`` that are pure—that is, their evaluation does not lead to any side effects—but appear as an expression statement.
Running the query
-----------------
.. include:: ../reusables/vs-code-basic-instructions/find-database.rst
#. In the main search box on LGTM.com, search for the project you want to query. For tips, see `Searching <https://lgtm.com/help/lgtm/searching>`__.
Running a quick query
---------------------
#. Click the project in the search results.
.. include:: ../reusables/vs-code-basic-instructions/run-quick-query-1.rst
#. Click **Query this project**.
This opens the query console. (For information about using this, see `Using the query console <https://lgtm.com/help/lgtm/using-query-console>`__.)
.. pull-quote::
Note
Alternatively, you can go straight to the query console by clicking **Query console** (at the top of any page), selecting **JavaScript** from the **Language** drop-down list, then choosing one or more projects to query from those displayed in the **Project** drop-down list.
#. Copy the following query into the text box in the query console:
#. In the quick query tab, delete ``select ""`` and paste the following query beneath the import statement ``import javascript``.
.. code-block:: ql
import javascript
from Expr e
where e.isPure() and
e.getParent() instanceof ExprStmt
select e, "This expression has no effect."
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.
.. include:: ../reusables/vs-code-basic-instructions/run-quick-query-2.rst
#. Click **Run**.
.. image:: ../images/codeql-for-visual-studio-code/basic-js-query-results-1.png
:align: center
The name of the project you are querying, and the ID of the most recently analyzed commit to the project, are listed below the query box. To the right of this is an icon that indicates the progress of the query operation:
If any matching code is found, click one of the links in the ``e`` column to open the file and highlight the matching expression.
.. image:: ../images/query-progress.png
:align: center
.. image:: ../images/codeql-for-visual-studio-code/basic-js-query-results-2.png
:align: center
.. pull-quote::
Note
Your query is always run against the most recently analyzed commit to the selected project.
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 ``e`` and is linked to the location in the source code of the project where ``e`` occurs. The second column is the alert message.
`Example query results <https://lgtm.com/query/5137013631828816943/>`__
.. pull-quote::
Note
An ellipsis (…) at the bottom of the table indicates that the entire list is not displayed—click it to show more results.
#. If any matching code is found, click one of the links in the ``e`` column to view the expression in the code viewer.
The matching 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.
.. include:: ../reusables/vs-code-basic-instructions/note-store-quick-query.rst
About the query structure
~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -119,11 +92,14 @@ To remove directives from the results:
e.getParent() instanceof ExprStmt and
not e.getParent() instanceof Directive
#. Click **Run**.
#. Re-run the query.
There are now fewer results as ``use strict`` directives are no longer reported.
The improved query finds several results on the example project including `this result <https://lgtm.com/projects/g/ajaxorg/ace/rev/ad50673d7137c09d1a5a6f0ef83633a149f9e3d1/files/lib/ace/keyboard/vim.js#L320>`__:
The improved query finds several results on the example project including the result below:
.. image:: ../images/codeql-for-visual-studio-code/basic-js-query-results-1.png
:align: center
.. code-block:: javascript
@@ -136,3 +112,15 @@ Further reading
.. include:: ../reusables/javascript-further-reading.rst
.. include:: ../reusables/codeql-ref-tools-further-reading.rst
.. Article-specific substitutions for the reusables used in docs/codeql/reusables/vs-code-basic-instructions
.. |language-text| replace:: JavaScript/TypeScript
.. |language-code| replace:: ``javascript``
.. |example-url| replace:: https://github.com/ajaxorg/ace
.. |image-quick-query| image:: ../images/codeql-for-visual-studio-code/quick-query-tab-js.png
.. |result-col-1| replace:: The first column corresponds to the expression ``e`` and is linked to the location in the source code of the project where ``e`` occurs.

View File

@@ -3,7 +3,9 @@
Basic query for Python code
===========================
Learn to write and run a simple CodeQL query using LGTM.
Learn to write and run a simple CodeQL query using Visual Studio Code with the CodeQL extension.
.. include:: ../reusables/vs-code-basic-instructions/setup-to-run-queries.rst
About the query
---------------
@@ -14,62 +16,33 @@ The query we're going to run performs a basic search of the code for ``if`` stat
if error: pass
Running the query
-----------------
.. include:: ../reusables/vs-code-basic-instructions/find-database.rst
#. In the main search box on LGTM.com, search for the project you want to query. For tips, see `Searching <https://lgtm.com/help/lgtm/searching>`__.
Running a quick query
---------------------
#. Click the project in the search results.
.. include:: ../reusables/vs-code-basic-instructions/run-quick-query-1.rst
#. Click **Query this project**.
This opens the query console. (For information about using this, see `Using the query console <https://lgtm.com/help/lgtm/using-query-console>`__.)
.. pull-quote::
Note
Alternatively, you can go straight to the query console by clicking **Query console** (at the top of any page), selecting **Python** from the **Language** drop-down list, then choosing one or more projects to query from those displayed in the **Project** drop-down list.
#. Copy the following query into the text box in the query console:
#. In the quick query tab, delete ``select ""`` and paste the following query beneath the import statement ``import python``.
.. code-block:: ql
import python
from If ifstmt, Stmt pass
where pass = ifstmt.getStmt(0) and
pass instanceof Pass
select ifstmt, "This 'if' statement is redundant."
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.
.. include:: ../reusables/vs-code-basic-instructions/run-quick-query-2.rst
#. Click **Run**.
.. image:: ../images/codeql-for-visual-studio-code/basic-python-query-results-1.png
:align: center
The name of the project you are querying, and the ID of the most recently analyzed commit to the project, are listed below the query box. To the right of this is an icon that indicates the progress of the query operation:
If any matching code is found, click a link in the ``ifstmt`` column to open the file and highlight the matching ``if`` statement.
.. image:: ../images/query-progress.png
:align: center
.. image:: ../images/codeql-for-visual-studio-code/basic-python-query-results-2.png
:align: center
.. pull-quote::
Note
Your query is always run against the most recently analyzed commit to the selected project.
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.
`Example query results <https://lgtm.com/query/3592297537117272922/>`__
.. pull-quote::
Note
An ellipsis (…) at the bottom of the table indicates that the entire list is not displayed—click it to show more results.
#. If any matching code is found, click a link in the ``ifstmt`` column to view the ``if`` statement in the code viewer.
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.
.. include:: ../reusables/vs-code-basic-instructions/note-store-quick-query.rst
About the query structure
~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -133,14 +106,24 @@ To exclude ``if`` statements that have an ``else`` branch:
pass instanceof Pass and
not exists(ifstmt.getOrelse())
#. Click **Run**.
#. Re-run the query.
There are now fewer results because ``if`` statements with an ``else`` branch are no longer included.
`See this in the query console <https://lgtm.com/query/3424727946018612474/>`__
Further reading
---------------
.. include:: ../reusables/python-further-reading.rst
.. include:: ../reusables/codeql-ref-tools-further-reading.rst
.. Article-specific substitutions for the reusables used in docs/codeql/reusables/vs-code-basic-instructions
.. |language-text| replace:: Python
.. |language-code| replace:: ``python``
.. |example-url| replace:: https://github.com/saltstack/salt
.. |image-quick-query| image:: ../images/codeql-for-visual-studio-code/quick-query-tab-python.png
.. |result-col-1| replace:: The first column corresponds to the expression ``ifstmt`` and is linked to the location in the source code of the project where ``ifstmt`` occurs.

View File

@@ -3,7 +3,9 @@
Basic query for Ruby code
=========================
Learn to write and run a simple CodeQL query.
Learn to write and run a simple CodeQL query using Visual Studio Code with the CodeQL extension.
.. include:: ../reusables/vs-code-basic-instructions/setup-to-run-queries.rst
About the query
---------------
@@ -15,24 +17,14 @@ The query we're going to run performs a basic search of the code for ``if`` expr
if error
# Handle the error
Running the query
-----------------
.. include:: ../reusables/vs-code-basic-instructions/find-database.rst
#. In the main search box on LGTM.com, search for the project you want to query. For tips, see `Searching <https://lgtm.com/help/lgtm/searching>`__.
Running a quick query
---------------------
#. Click the project in the search results.
.. include:: ../reusables/vs-code-basic-instructions/run-quick-query-1.rst
#. Click **Query this project**.
This opens the query console. (For information about using this, see `Using the query console <https://lgtm.com/help/lgtm/using-query-console>`__.)
.. pull-quote::
Note
Alternatively, you can go straight to the query console by clicking **Query console** (at the top of any page), selecting **Ruby** from the **Language** drop-down list, then choosing one or more projects to query from those displayed in the **Project** drop-down list.
#. Copy the following query into the text box in the query console:
#. In the quick query tab, delete the content and paste in the following query.
.. code-block:: ql
@@ -40,37 +32,20 @@ Running the query
from IfExpr ifexpr
where
not exists(ifexpr.getThen())
not exists(ifexpr.getThen())
select ifexpr, "This 'if' expression is redundant."
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.
.. include:: ../reusables/vs-code-basic-instructions/run-quick-query-2.rst
#. Click **Run**.
.. image:: ../images/codeql-for-visual-studio-code/basic-ruby-query-results-1.png
:align: center
The name of the project you are querying, and the ID of the most recently analyzed commit to the project, are listed below the query box. To the right of this is an icon that indicates the progress of the query operation:
If any matching code is found, click a link in the ``ifexpr`` column to open the file and highlight the matching ``if`` statement.
.. image:: ../images/query-progress.png
:align: center
.. image:: ../images/codeql-for-visual-studio-code/basic-ruby-query-results-2.png
:align: center
.. pull-quote::
Note
Your query is always run against the most recently analyzed commit to the selected project.
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 ``ifexpr`` and is linked to the location in the source code of the project where ``ifexpr`` occurs. The second column is the alert message.
`Example query results <https://lgtm.com/query/4416853782037269427/>`__
.. pull-quote::
Note
An ellipsis (…) at the bottom of the table indicates that the entire list is not displayed—click it to show more results.
#. If any matching code is found, click a link in the ``ifexpr`` column to view the ``if`` statement in the code viewer.
The matching ``if`` 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.
.. include:: ../reusables/vs-code-basic-instructions/note-store-quick-query.rst
About the query structure
~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -131,14 +106,24 @@ To exclude ``if`` statements that have an ``else`` branch:
not exists(ifexpr.getThen()) and
not exists(ifexpr.getElse())
#. Click **Run**.
#. Re-run the query.
There are now fewer results because ``if`` expressions with an ``else`` branch are no longer included.
`See this in the query console <https://lgtm.com/query/4694253275631320752/>`__
Further reading
---------------
.. include:: ../reusables/ruby-further-reading.rst
.. include:: ../reusables/codeql-ref-tools-further-reading.rst
.. Article-specific substitutions for the reusables used in docs/codeql/reusables/vs-code-basic-instructions
.. |language-text| replace:: Ruby
.. |language-code| replace:: ``ruby``
.. |example-url| replace:: https://github.com/discourse/discourse
.. |image-quick-query| image:: ../images/codeql-for-visual-studio-code/quick-query-tab-ruby.png
.. |result-col-1| replace:: The first column corresponds to the expression ``ifexpr`` and is linked to the location in the source code of the project where ``ifexpr`` occurs.

View File

@@ -123,8 +123,6 @@ Select expressions that cast a value to a type parameter:
where assertion.getTypeAnnotation() = param.getLocalTypeName().getAnAccess()
select assertion, "Cast to type parameter."
`See this in the query console on LGTM.com <https://lgtm.com/query/1505979606441/>`__.
Classes and interfaces
~~~~~~~~~~~~~~~~~~~~~~
@@ -179,7 +177,7 @@ Ambient nodes are mostly ignored by control flow and data flow analysis. The out
Static type information
-----------------------
Static type information and global name binding is available for projects with "full" TypeScript extraction enabled. This option is enabled by default for projects on LGTM.com and when you create databases with the :ref:`CodeQL CLI <codeql-cli>`.
Static type information and global name binding is available for projects with "full" TypeScript extraction enabled. This option is enabled by default when you create databases with the :ref:`CodeQL CLI <codeql-cli>`.
Basic usage
~~~~~~~~~~~
@@ -403,8 +401,6 @@ It is best to use `TypeName <https://codeql.github.com/codeql-standard-libraries
and not access.hasTypeArguments()
select access, "Type arguments are omitted"
`See this in the query console on LGTM.com <https://lgtm.com/query/1505985316500/>`__.
Find imported names that are used as both a type and a value:
.. code-block:: ql
@@ -416,8 +412,6 @@ Find imported names that are used as both a type and a value:
and exists (VarAccess access | access.getVariable().getADeclaration() = spec.getLocal())
select spec, "Used as both variable and type"
`See this in the query console on LGTM.com <https://lgtm.com/query/1505975787348/>`__.
Namespace names
~~~~~~~~~~~~~~~

View File

@@ -80,7 +80,7 @@ We can use the ``Callable`` class to write a query that finds methods that are n
where not exists(Callable caller | caller.polyCalls(callee))
select callee
`See this in the query console on LGTM.com <https://lgtm.com/query/8376915232270534450/>`__. This simple query typically returns a large number of results.
This simple query typically returns a large number of results.
.. pull-quote::
@@ -99,7 +99,7 @@ Running this query on a typical Java project results in lots of hits in the Java
callee.getCompilationUnit().fromSource()
select callee, "Not called."
`See this in the query console on LGTM.com <https://lgtm.com/query/8711624074465690976/>`__. This change reduces the number of results returned for most projects.
This change reduces the number of results returned for most codebases.
We might also notice several unused methods with the somewhat strange name ``<clinit>``: these are class initializers; while they are not explicitly called anywhere in the code, they are called implicitly whenever the surrounding class is loaded. Hence it makes sense to exclude them from our query. While we are at it, we can also exclude finalizers, which are similarly invoked implicitly:
@@ -113,7 +113,7 @@ We might also notice several unused methods with the somewhat strange name ``<cl
not callee.hasName("<clinit>") and not callee.hasName("finalize")
select callee, "Not called."
`See this in the query console on LGTM.com <https://lgtm.com/query/925473733866047471/>`__. This also reduces the number of results returned by most projects.
This also reduces the number of results returned by most codebases.
We may also want to exclude public methods from our query, since they may be external API entry points:
@@ -128,7 +128,7 @@ We may also want to exclude public methods from our query, since they may be ext
not callee.isPublic()
select callee, "Not called."
`See this in the query console on LGTM.com <https://lgtm.com/query/6284320987237954610/>`__. This should have a more noticeable effect on the number of results returned.
This should have a more noticeable effect on the number of results returned.
A further special case is non-public default constructors: in the singleton pattern, for example, a class is provided with private empty default constructor to prevent it from being instantiated. Since the very purpose of such constructors is their not being called, they should not be flagged up:
@@ -144,7 +144,7 @@ A further special case is non-public default constructors: in the singleton patt
not callee.(Constructor).getNumberOfParameters() = 0
select callee, "Not called."
`See this in the query console on LGTM.com <https://lgtm.com/query/2625028545869146918/>`__. This change has a large effect on the results for some projects but little effect on the results for others. Use of this pattern varies widely between different projects.
This change has a large effect on the results for some projects but little effect on the results for others. Use of this pattern varies widely between different projects.
Finally, on many Java projects there are methods that are invoked indirectly by reflection. So, while there are no calls invoking these methods, they are, in fact, used. It is in general very hard to identify such methods. A very common special case, however, is JUnit test methods, which are reflectively invoked by a test runner. The CodeQL library for Java has support for recognizing test classes of JUnit and other testing frameworks, which we can employ to filter out methods defined in such classes:
@@ -161,7 +161,7 @@ Finally, on many Java projects there are methods that are invoked indirectly by
not callee.getDeclaringType() instanceof TestClass
select callee, "Not called."
`See this in the query console on LGTM.com <https://lgtm.com/query/2055862421970264112/>`__. This should give a further reduction in the number of results returned.
This should give a further reduction in the number of results returned.
Further reading
---------------

View File

@@ -338,10 +338,9 @@ step by step in the UI:
where cfg.hasFlowPath(source, sink)
select sink, source, sink, "Property access on JSON value originating $@.", source, "here"
`Here <https://lgtm.com/query/5347702611074820306>`_ is a run of this query on the `plexus-interop
<https://lgtm.com/projects/g/finos-plexus/plexus-interop/>`_ project on LGTM.com. Many of the 19
results are false positives since we currently do not model many ways in which a value can be
checked for nullness. In particular, after a property reference ``x.p`` we implicitly know that
We ran this query on the https://github.com/finos/plexus-interop repository. Many of the
results were false positives since the query does not currently model many ways in which we can check
a value for nullness. In particular, after a property reference ``x.p`` we implicitly know that
``x`` cannot be null anymore, since otherwise the reference would have thrown an exception.
Modeling this would allow us to get rid of most of the false positives, but is beyond the scope of
this tutorial.
@@ -391,10 +390,10 @@ Some of our standard security queries use flow labels. You can look at their imp
to get a feeling for how to use flow labels in practice.
In particular, both of the examples mentioned in the section on limitations of basic data flow above
are from standard security queries that use flow labels. The `Prototype pollution
<https://lgtm.com/rules/1508857356317>`_ query uses two flow labels to distinguish completely
are from standard security queries that use flow labels. The `Prototype-polluting merge call
<https://codeql.github.com/codeql-query-help/javascript/js-prototype-pollution/>`_ query uses two flow labels to distinguish completely
tainted objects from partially tainted objects. The `Uncontrolled data used in path expression
<https://lgtm.com/rules/1971530250>`_ query uses four flow labels to track whether a user-controlled
<https://codeql.github.com/codeql-query-help/javascript/js-path-injection/>`_ query uses four flow labels to track whether a user-controlled
string may be an absolute path and whether it may contain ``..`` components.
Further reading

View File

@@ -228,15 +228,23 @@ Here's see an example of what this can handle now:
Tracking in the whole model
---------------------------
We applied this pattern to ``firebaseDatabase()`` in the previous section, and it
can just as easily apply to the other predicates.
For reference, here's our simple Firebase model with type tracking on every predicate:
We applied this pattern to ``firebaseDatabase()`` in the previous section, and we can
apply the model just as easily to other predicates.
This example query uses the model to find `set` calls.
It's been modified slightly to handle a bit more of the API, which is beyond the scope of this tutorial.
.. code-block:: ql
import javascript
import DataFlow
SourceNode firebase(TypeTracker t) {
t.start() and
result = globalVarRef("firebase")
(
result = globalVarRef("firebase")
or
result = moduleImport("firebase/app")
)
or
exists(TypeTracker t2 |
result = firebase(t2).track(t2, t)
@@ -277,8 +285,7 @@ For reference, here's our simple Firebase model with type tracking on every pred
result = firebaseRef().getAMethodCall("set")
}
`Here <https://lgtm.com/query/1053770500827789481>`__ is a run of an example query using the model to find `set` calls on one of the Firebase sample projects.
It's been modified slightly to handle a bit more of the API, which is beyond the scope of this tutorial.
select firebaseSetterCall()
Tracking associated data
------------------------
@@ -392,7 +399,98 @@ Based on that we can track the ``snapshot`` value and find the ``val()`` call it
With this addition, ``firebaseDatabaseRead("forecast")`` finds the call to ``snapshot.val()`` that contains the value of the forecast.
`Here <https://lgtm.com/query/8761360814276109092>`__ is a run of an example query using the model to find `val` calls.
.. code-block:: ql
import javascript
import DataFlow
SourceNode firebase(TypeTracker t) {
t.start() and
(
result = globalVarRef("firebase")
or
result = moduleImport("firebase/app")
)
or
exists(TypeTracker t2 |
result = firebase(t2).track(t2, t)
)
}
SourceNode firebase() {
result = firebase(TypeTracker::end())
}
SourceNode firebaseDatabase(TypeTracker t) {
t.start() and
result = firebase().getAMethodCall("database")
or
exists(TypeTracker t2 |
result = firebaseDatabase(t2).track(t2, t)
)
}
SourceNode firebaseDatabase() {
result = firebaseDatabase(TypeTracker::end())
}
SourceNode firebaseRef(Node name, TypeTracker t) {
t.start() and
exists(CallNode call |
call = firebaseDatabase().getAMethodCall("ref") and
name = call.getArgument(0) and
result = call
)
or
exists(TypeTracker t2 |
result = firebaseRef(name, t2).track(t2, t)
)
}
SourceNode firebaseRef(Node name) {
result = firebaseRef(name, TypeTracker::end())
}
MethodCallNode firebaseSetterCall(Node name) {
result = firebaseRef(name).getAMethodCall("set")
}
SourceNode firebaseSnapshotCallback(Node refName, TypeBackTracker t) {
t.start() and
(
result = firebaseRef(refName).getAMethodCall("once").getArgument(1).getALocalSource()
or
result = firebaseRef(refName).getAMethodCall("once").getAMethodCall("then").getArgument(0).getALocalSource()
)
or
exists(TypeBackTracker t2 |
result = firebaseSnapshotCallback(refName, t2).backtrack(t2, t)
)
}
FunctionNode firebaseSnapshotCallback(Node refName) {
result = firebaseSnapshotCallback(refName, TypeBackTracker::end())
}
SourceNode firebaseSnapshot(Node refName, TypeTracker t) {
t.start() and
result = firebaseSnapshotCallback(refName).getParameter(0)
or
exists(TypeTracker t2 |
result = firebaseSnapshot(refName, t2).track(t2, t)
)
}
SourceNode firebaseSnapshot(Node refName) {
result = firebaseSnapshot(refName, TypeTracker::end())
}
MethodCallNode firebaseDatabaseRead(Node refName) {
result = firebaseSnapshot(refName).getAMethodCall("val")
}
from Node name
select name, firebaseDatabaseRead(name)
Summary
-------

View File

@@ -112,7 +112,7 @@ Here's a first version of our query:
wsinner > wsouter
select outer, "Whitespace around nested operators contradicts precedence."
`See this in the query console on LGTM.com <https://lgtm.com/query/8141155897270480914/>`__. This query is likely to find results on most projects.
This query is likely to find results on most codebases.
The first conjunct of the ``where`` clause restricts ``inner`` to be an operand of ``outer``, the second conjunct binds ``wsinner`` and ``wsouter``, while the last conjunct selects the suspicious cases.
@@ -143,7 +143,7 @@ Note that our predicate ``operatorWS`` computes the **total** amount of white sp
wsinner > wsouter
select outer, "Whitespace around nested operators contradicts precedence."
`See this in the query console on LGTM.com <https://lgtm.com/query/3151720037708691205/>`__. Any results will be refined by our changes to the query.
Any results will be refined by our changes to the query.
Another source of false positives are associative operators: in an expression of the form ``x + y+z``, the first plus is syntactically nested inside the second, since + in Java associates to the left; hence the expression is flagged as suspicious. But since + is associative to begin with, it does not matter which way around the operators are nested, so this is a false positive. To exclude these cases, let us define a new class identifying binary expressions with an associative operator:
@@ -175,8 +175,6 @@ Now we can extend our query to discard results where the outer and the inner exp
wsinner > wsouter
select outer, "Whitespace around nested operators contradicts precedence."
`See this in the query console on LGTM.com <https://lgtm.com/query/5714614966569401039/>`__.
Notice that we again use ``getOp``, this time to determine whether two binary expressions have the same operator. Running our improved query now finds the Java standard library bug described in the Overview. It also flags up the following suspicious code in `Hadoop HBase <https://hbase.apache.org/>`__:
.. code-block:: java

View File

@@ -55,7 +55,7 @@ def setup(sphinx):
sphinx.add_lexer("ql", QLLexer())
# The version of CodeQL for the current release you're documenting, acts as replacement for
# |version| and |release|. Not currently used except in LGTM Enterprise support info.
# |version| and |release|. Not currently used.
# The short X.Y version.
# version = u'3.0'

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 315 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 386 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 189 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 250 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 241 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 271 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 203 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 314 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 202 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 214 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 375 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 204 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 242 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 226 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 262 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 85 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 146 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 147 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 148 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

View File

@@ -0,0 +1 @@
For information about installing the CodeQL extension for Visual Studio code, see ":ref:`Setting up CodeQL in Visual Studio Code <setting-up-codeql-in-visual-studio-code>`."

View File

@@ -0,0 +1,23 @@
Finding a CodeQL database to experiment with
--------------------------------------------
Before you start writing queries for |language-text| code, you need a CodeQL database to run them against. The simplest way to do this is to download a database for a repository that uses |language-text| directly from GitHub.com.
#. In Visual Studio Code, click the **QL** icon |codeql-ext-icon| in the left sidebar to display the CodeQL extension.
#. Click **From GitHub** or the GitHub logo |github-db| at the top of the CodeQL extension to open an entry field.
#. Copy the URL for the repository into the field and press the keyboard **Enter** key. For example, |example-url|.
#. Optionally, if the repository has more than one CodeQL database available, select |language-code| to download the database created from the |language-text| code.
Information about the download progress for the database is shown in the bottom right corner of Visual Studio Code. When the download is complete, the database is shown with a check mark in the **Databases** section of the CodeQL extension (see screenshot below).
.. |codeql-ext-icon| image:: ../images/codeql-for-visual-studio-code/codeql-extension-icon.png
:width: 20
:alt: Icon for the CodeQL extension.
.. |github-db| image:: ../images/codeql-for-visual-studio-code/add-codeql-db-github.png
:width: 20
:alt: Icon for the CodeQL extension option to download a CodeQL database from GitHub.

View File

@@ -0,0 +1,5 @@
.. pull-quote::
Note
If you want to move your experimental query somewhere more permanent, you need to move the whole ``Quick Queries`` directory. The directory is a CodeQL pack with a ``qlpack.yml`` file that defines the content as queries for |language-text| CodeQL databases. For more information about CodeQL packs, see ":ref:`Working with CodeQL packs in Visual Studio Code <working-with-codeql-packs-in-visual-studio-code>`."

View File

@@ -0,0 +1,7 @@
The CodeQL extension for Visual Studio Code adds several **CodeQL:** commands to the command palette including **Quick Query**, which you can use to run a query without any set up.
#. From the command palette in Visual Studio Code, select **CodeQL: Quick Query**.
#. After a moment, a new tab *quick-query.ql* is opened, ready for you to write a query for your currently selected CodeQL database (here a |language-code| database). If you are prompted to reload your workspace as a multi-folder workspace to allow Quick queries, accept or create a new workspace using the starter workflow.
|image-quick-query|

View File

@@ -0,0 +1,7 @@
4. Save the query in its default location (a temporary "Quick Queries" directory under the workspace for ``GitHub.vscode-codeql/quick-queries``).
#. Right-click in the query tab and select **CodeQL: Run Query**. (Alternatively, run the command from the Command Palette.)
The query will take a few moments to return results. When the query completes, the results are displayed in a CodeQL Query Results view, next to the main editor view.
The query results are listed in two columns, corresponding to the expressions in the ``select`` clause of the query. |result-col-1| The second column is the alert message.

View File

@@ -0,0 +1 @@
For information about installing the CodeQL extension for Visual Studio code, see ":ref:`Setting up CodeQL in Visual Studio Code <setting-up-codeql-in-visual-studio-code>`."

View File

@@ -105,7 +105,7 @@ Now try applying ``isAllowedIn(string region)`` to a person ``p``. If ``p`` is n
You know that the fire starters live in the south *and* that they must have been able to travel to the north. Write a query to find the possible suspects. You could also extend the ``select`` clause to list the age of the suspects. That way you can clearly see that all the children have been excluded from the list.
`See the answer in the query console on LGTM.com <https://lgtm.com/query/2551838470440192723/>`__
`Check your answer <#exercise-1>`__
You can now continue to gather more clues and find out which of your suspects started the fire...
@@ -142,7 +142,7 @@ The predicate ``isBald`` is defined to take a ``Person``, so it can also take a
You can now write a query to select the bald southerners who are allowed into the north.
`See the answer in the query console on LGTM.com <https://lgtm.com/query/2572701606358725253/>`__
`Check your answer <#exercise-2>`__
You have found the two fire starters! They are arrested and the villagers are once again impressed with your work.
@@ -150,3 +150,65 @@ Further reading
---------------
.. include:: ../reusables/codeql-ref-tools-further-reading.rst
--------------
Answers
-------
In these answers, we use ``/*`` and ``*/`` to label the different parts of the query. Any text surrounded by ``/*`` and ``*/`` is not evaluated as part of the QL code, but is treated as a *comment*.
Exercise 1
~~~~~~~~~~
.. code-block:: ql
import tutorial
predicate isSouthern(Person p) { p.getLocation() = "south" }
class Southerner extends Person {
/* the characteristic predicate */
Southerner() { isSouthern(this) }
}
class Child extends Person {
/* the characteristic predicate */
Child() { this.getAge() < 10 }
/* a member predicate */
override predicate isAllowedIn(string region) { region = this.getLocation() }
}
from Southerner s
where s.isAllowedIn("north")
select s, s.getAge()
Exercise 2
~~~~~~~~~~
.. code-block:: ql
import tutorial
predicate isSouthern(Person p) { p.getLocation() = "south" }
class Southerner extends Person {
/* the characteristic predicate */
Southerner() { isSouthern(this) }
}
class Child extends Person {
/* the characteristic predicate */
Child() { this.getAge() < 10 }
/* a member predicate */
override predicate isAllowedIn(string region) { region = this.getLocation() }
}
predicate isBald(Person p) { not exists(string c | p.getHairColor() = c) }
from Southerner s
where s.isAllowedIn("north") and isBald(s)
select s

View File

@@ -260,21 +260,34 @@ Alternative solutions
Here are some more example queries that solve the river crossing puzzle:
#. This query uses a modified ``path`` variable to describe the resulting path in
more detail.
.. container:: toggle
`See solution in the query console on LGTM.com <https://lgtm.com/query/659603593702729237/>`__
.. container:: name
#. This query models the man and the cargo items in a different way, using an
:ref:`abstract <abstract>`
class and predicate. It also displays the resulting path in a more visual way.
*Show/hide example query - modified path*
`See solution in the query console on LGTM.com <https://lgtm.com/query/1025323464423811143/>`__
.. literalinclude:: river-answer-1-path.ql
:language: ql
#. This query introduces :ref:`algebraic datatypes <algebraic-datatypes>`
to model the situation, instead of defining everything as a subclass of ``string``.
`See solution in the query console on LGTM.com <https://lgtm.com/query/7260748307619718263/>`__
.. container:: toggle
.. container:: name
*Show/hide example query - abstract class*
.. literalinclude:: river-answer-2-abstract-class.ql
:language: ql
.. container:: toggle
.. container:: name
*Show/hide example query - datatypes*
.. literalinclude:: river-answer-3-datatypes.ql
:language: ql
Further reading
---------------

View File

@@ -129,7 +129,7 @@ Here is one way to define ``relativeOf()``:
Don't forget to use the predicate ``isDeceased()`` to find relatives that are still alive.
`See the answer in the query console on LGTM.com <https://lgtm.com/query/6710025057257064639/>`__
`Check your answer <#exercise-1>`__
Select the true heir
--------------------
@@ -142,7 +142,7 @@ To decide who should inherit the king's fortune, the villagers carefully read th
As your final challenge, define a predicate ``hasCriminalRecord`` so that ``hasCriminalRecord(p)`` holds if ``p`` is any of the criminals you unmasked earlier (in the ":doc:`Find the thief <find-the-thief>`" and ":doc:`Catch the fire starter <catch-the-fire-starter>`" tutorials).
`See the answer in the query console on LGTM.com <https://lgtm.com/query/1820692755164273290/>`__
`Check your answer <#exercise-2>`__
Experimental explorations
-------------------------
@@ -164,3 +164,47 @@ Further reading
---------------
.. include:: ../reusables/codeql-ref-tools-further-reading.rst
--------------
Answers
-------
In these answers, we use ``/*`` and ``*/`` to label the different parts of the query. Any text surrounded by ``/*`` and ``*/`` is not evaluated as part of the QL code, but is treated as a *comment*.
Exercise 1
~~~~~~~~~~
.. code-block:: ql
import tutorial
Person relativeOf(Person p) { parentOf*(result) = parentOf*(p) }
from Person p
where
not p.isDeceased() and
p = relativeOf("King Basil")
select p
Exercise 2
~~~~~~~~~~
.. code-block:: ql
import tutorial
Person relativeOf(Person p) { parentOf*(result) = parentOf*(p) }
predicate hasCriminalRecord(Person p) {
p = "Hester" or
p = "Hugh" or
p = "Charlie"
}
from Person p
where
not p.isDeceased() and
p = relativeOf("King Basil") and
not hasCriminalRecord(p)
select p

View File

@@ -9,8 +9,8 @@ About query results
-------------------
The information contained in the results of a query is controlled by the ``select`` statement. Part of the process of developing a useful query is to make the results clear and easy for other users to understand.
When you write your own queries in the query console or in the CodeQL :ref:`extension for VS Code <codeql-for-visual-studio-code>` there are no constraints on what can be selected.
However, if you want to use a query to create alerts in LGTM or generate valid analysis results using the :ref:`CodeQL CLI <codeql-cli>`, you'll need to make the ``select`` statement report results in the required format.
When you write your own queries in the CodeQL :ref:`extension for VS Code <codeql-for-visual-studio-code>` there are no constraints on what can be selected.
However, if you want to use a query to create alerts for code scanning or generate valid analysis results using the :ref:`CodeQL CLI <codeql-cli>`, you'll need to make the ``select`` statement report results in the required format.
You must also ensure that the query has the appropriate metadata properties defined.
This topic explains how to write your select statement to generate helpful analysis results.
@@ -23,7 +23,7 @@ In their most basic form, the ``select`` statement must select two 'columns':
- **Element**—a code element that's identified by the query. This defines the location of the alert.
- **String**—a message to display for this code element, describing why the alert was generated.
If you look at some of the LGTM queries, you'll see that they can select extra element/string pairs, which are combined with ``$@`` placeholder markers in the message to form links. For example, `Dereferenced variable may be null <https://lgtm.com/query/rule:1954750296/lang:java/>`__ (Java), or `Duplicate switch case <https://lgtm.com/query/rule:7890077/lang:javascript/>`__ (JavaScript).
If you look at some of the existing queries, you'll see that they can select extra element/string pairs, which are combined with ``$@`` placeholder markers in the message to form links. For example, `Dereferenced variable may be null <https://github.com/github/codeql/blob/95e65347cafe502bbd0d9f48d1175fd3d66e0459/java/ql/src/Likely%20Bugs/Nullness/NullMaybe.ql>`__ (Java), or `Duplicate switch case <https://github.com/github/codeql/blob/95e65347cafe502bbd0d9f48d1175fd3d66e0459/javascript/ql/src/Expressions/DuplicateSwitchCase.ql>`__ (JavaScript).
.. pull-quote::
@@ -34,78 +34,70 @@ If you look at some of the LGTM queries, you'll see that they can select extra e
Developing a select statement
-----------------------------
Here's a simple query that uses the standard CodeQL ``CodeDuplication.qll`` library to identify similar files.
Here's a simple query to find Java classes that extend other classes.
Basic select statement
~~~~~~~~~~~~~~~~~~~~~~
.. code-block:: ql
import java
import external.CodeDuplication
from File f, File other, int percent
where similarFiles(f, other, percent)
select f, "This file is similar to another file."
/**
* @kind problem
*/
import java
from Class c, Class superclass
where superclass = c.getASupertype()
select c, "This class extends another class."
This basic select statement has two columns:
#. Element to display the alert on: ``f`` corresponds to ``File``.
#. String message to display: ``"This file is similar to another file."``
#. An element with a location to display the alert on: ``c`` corresponds to ``Class``.
#. String message to display: ``"This class extends another class."``
.. image:: ../images/ql-select-statement-basic.png
:alt: Results of basic select statement
:class: border
Including the name of the similar file
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Including the name of the superclass
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The alert message defined by the basic select statement is constant and doesn't give users much information. Since the query identifies the similar file (``other``), it's easy to extend the ``select`` statement to report the name of the similar file. For example:
The alert message defined by the basic select statement is constant and doesn't give users much information. Since the query identifies the superclass, it's easy to include its name in the string message. For example:
.. code-block:: ql
select f, "This file is similar to " + other.getBaseName()
select c, "This class extends the class " + superclass.getName()
#. Element: ``f`` as before.
#. String message: ``"This file is similar to "``—the string text is combined with the file name for the ``other``, similar file, returned by ``getBaseName()``.
#. Element: ``c`` as before.
#. String message: ``"This class extends the class "``—the string text is combined with the class name for the ``superclass``, returned by ``getName()``.
.. image:: ../images/ql-select-statement-filename.png
.. image:: ../images/ql-select-statement-class-name.png
:alt: Results of extended select statement
:class: border
While this is more informative than the original select statement, the user still needs to find the other file manually.
While this is more informative than the original select statement, the user still needs to find the superclass manually.
Adding a link to the similar file
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Adding a link to the superclass
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
You can use placeholders in the text of alert messages to insert additional information, such as links to the similar file. Placeholders are defined using ``$@``, and filled using the information in the next two columns of the select statement. For example, this select statement returns four columns:
You can use placeholders in the text of alert messages to insert additional information, such as links to the superclass. Placeholders are defined using ``$@``, and filled using the information in the next two columns of the select statement. For example, this select statement returns four columns:
.. code-block:: ql
select f, "This file is similar to $@.", other, other.getBaseName()
select c, "This class extends the class $@.", superclass, superclass.getName()
#. Element: ``f`` as before.
#. String message: ``"This file is similar to $@."``—the string text now includes a placeholder, which will display the combined content of the next two columns.
#. Element for placeholder: ``other`` corresponds to the similar file.
#. String text for placeholder: the short file name returned by ``other.getBaseName()``.
#. Element: ``c`` as before.
#. String message: ``"This class extends the class $@."``—the string text now includes a placeholder, which will display the combined content of the next two columns.
#. Element for placeholder: the ``superclass``.
#. String text for placeholder: the class name returned by ``superclass.getBaseName()``.
When the alert message is displayed, the ``$@`` placeholder is replaced by a link created from the contents of the third and fourth columns defined by the ``select`` statement.
When the alert message is displayed, the ``$@`` placeholder is replaced by a link created from the contents of the third and fourth columns defined by the ``select`` statement. In this example, the link target will be the location of the superclass's definition, and the link text will be its name. Note that some superclasses, such as ``Object``, will not be in the database, since they are built in to the Java language. Clicking those links will have no effect.
If you use the ``$@`` placeholder marker multiple times in the description text, then the ``N``\ th use is replaced by a link formed from columns ``2N+2`` and ``2N+3``. If there are more pairs of additional columns than there are placeholder markers, then the trailing columns are ignored. Conversely, if there are fewer pairs of additional columns than there are placeholder markers, then the trailing markers are treated as normal text rather than placeholder markers.
Adding details of the extent of similarity
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
You could go further and change the ``select`` statement to report on the similarity of content in the two files, since this information is already available in the query. For example:
.. code-block:: ql
select f, percent + "% of the lines in " + f.getBaseName() + " are similar to lines in $@.", other, other.getBaseName()
The new elements added here don't need to be clickable, so we added them directly to the description string.
.. image:: ../images/ql-select-statement-similarity.png
:alt: Results showing the extent of similarity
.. image:: ../images/ql-select-statement-link.png
:alt: Results including links
:class: border
Further reading

View File

@@ -50,9 +50,7 @@ You start asking some creative questions and making notes of the answers so you
There is too much information to search through by hand, so you decide to use your newly acquired QL skills to help you with your investigation...
#. Open the `query console on LGTM.com <https://lgtm.com/query>`__ to get started.
#. Select a language and a demo project. For this tutorial, any language and project will do.
#. Delete the default code ``import <language> select "hello world"``.
.. include:: ../reusables/setup-to-run-tutorials.rst
QL libraries
------------
@@ -209,13 +207,7 @@ Hints
Once you have finished, you will have a list of possible suspects. One of those people must be the thief!
`See the answer in the query console on LGTM.com <https://lgtm.com/query/1505743955992/>`__
.. pull-quote::
Note
In the answer, we used ``/*`` and ``*/`` to label the different parts of the query. Any text surrounded by ``/*`` and ``*/`` is not evaluated as part of the QL code, but is just a *comment*.
`Check your answer <#exercise-1>`__
You are getting closer to solving the mystery! Unfortunately, you still have quite a long list of suspects... To find out which of your suspects is the thief, you must gather more information and refine your query in the next step.
@@ -291,9 +283,59 @@ You can now translate the remaining questions into QL:
Have you found the thief?
`See the answer in the query console on LGTM.com <https://lgtm.com/query/1505744186085/>`__
`Check your answer <#exercise-2>`__
Further reading
---------------
.. include:: ../reusables/codeql-ref-tools-further-reading.rst
--------------
Answers
-------
In these answers, we use ``/*`` and ``*/`` to label the different parts of the query. Any text surrounded by ``/*`` and ``*/`` is not evaluated as part of the QL code, but is treated as a *comment*.
Exercise 1
^^^^^^^^^^
.. code-block:: ql
import tutorial
from Person t
where
/* 1 */ t.getHeight() > 150 and
/* 2 */ not t.getHairColor() = "blond" and
/* 3 */ exists (string c | t.getHairColor() = c) and
/* 4 */ not t.getAge() < 30 and
/* 5 */ t.getLocation() = "east" and
/* 6 */ (t.getHairColor() = "black" or t.getHairColor() = "brown") and
/* 7 */ not (t.getHeight() > 180 and t.getHeight() < 190) and
/* 8 */ exists(Person p | p.getAge() > t.getAge())
select t
Exercise 2
^^^^^^^^^^
.. code-block:: ql
import tutorial
from Person t
where
/* 1 */ t.getHeight() > 150 and
/* 2 */ not t.getHairColor() = "blond" and
/* 3 */ exists (string c | t.getHairColor() = c) and
/* 4 */ not t.getAge() < 30 and
/* 5 */ t.getLocation() = "east" and
/* 6 */ (t.getHairColor() = "black" or t.getHairColor() = "brown") and
/* 7 */ not (t.getHeight() > 180 and t.getHeight() < 190) and
/* 8 */ exists(Person p | p.getAge() > t.getAge()) and
/* 9 */ not t = max(Person p | | p order by p.getHeight()) and
/* 10 */ t.getHeight() < avg(float i | exists(Person p | p.getHeight() = i) | i) and
/* 11 */ t = max(Person p | p.getLocation() = "east" | p order by p.getAge())
select "The thief is " + t + "!"

View File

@@ -0,0 +1,115 @@
/**
* A solution to the river crossing puzzle using a modified `path` variable
* to describe the resulting path in detail.
*/
/** A possible cargo item. */
class Cargo extends string {
Cargo() {
this = "Nothing" or
this = "Goat" or
this = "Cabbage" or
this = "Wolf"
}
}
/** One of two shores. */
class Shore extends string {
Shore() {
this = "Left" or
this = "Right"
}
/** Returns the other shore. */
Shore other() {
this = "Left" and result = "Right"
or
this = "Right" and result = "Left"
}
}
/** Renders the state as a string. */
string renderState(Shore manShore, Shore goatShore, Shore cabbageShore, Shore wolfShore) {
result = manShore + "," + goatShore + "," + cabbageShore + "," + wolfShore
}
/** A record of where everything is. */
class State extends string {
Shore manShore;
Shore goatShore;
Shore cabbageShore;
Shore wolfShore;
State() { this = renderState(manShore, goatShore, cabbageShore, wolfShore) }
/** Returns the state that is reached after ferrying a particular cargo item. */
State ferry(Cargo cargo) {
cargo = "Nothing" and
result = renderState(manShore.other(), goatShore, cabbageShore, wolfShore)
or
cargo = "Goat" and
result = renderState(manShore.other(), goatShore.other(), cabbageShore, wolfShore)
or
cargo = "Cabbage" and
result = renderState(manShore.other(), goatShore, cabbageShore.other(), wolfShore)
or
cargo = "Wolf" and
result = renderState(manShore.other(), goatShore, cabbageShore, wolfShore.other())
}
/**
* Holds if the state is safe. This occurs when neither the goat nor the cabbage
* can get eaten.
*/
predicate isSafe() {
// The goat can't eat the cabbage.
(goatShore != cabbageShore or goatShore = manShore) and
// The wolf can't eat the goat.
(wolfShore != goatShore or wolfShore = manShore)
}
/** Returns the state that is reached after safely ferrying a cargo item. */
State safeFerry(Cargo cargo) { result = this.ferry(cargo) and result.isSafe() }
string towards() {
manShore = "Left" and result = "to the left"
or
manShore = "Right" and result = "to the right"
}
/**
* Returns all states that are reachable via safe ferrying.
* `path` keeps track of how it is achieved.
* `visitedStates` keeps track of previously visited states and is used to avoid loops.
*/
State reachesVia(string path, string visitedStates) {
// Reachable in 1 step by ferrying a specific cargo
exists(Cargo cargo |
result = this.safeFerry(cargo) and
visitedStates = result and
path = "First " + cargo + " is ferried " + result.towards()
)
or
// Reachable by first following pathSoFar and then ferrying cargo
exists(string pathSoFar, string visitedStatesSoFar, Cargo cargo |
result = this.reachesVia(pathSoFar, visitedStatesSoFar).safeFerry(cargo) and
not exists(int i | i = visitedStatesSoFar.indexOf(result)) and // resulting state is not visited yet
visitedStates = visitedStatesSoFar + "_" + result and
path = pathSoFar + ",\nthen " + cargo + " is ferried " + result.towards()
)
}
}
/** The initial state, where everything is on the left shore. */
class InitialState extends State {
InitialState() { this = renderState("Left", "Left", "Left", "Left") }
}
/** The goal state, where everything is on the right shore. */
class GoalState extends State {
GoalState() { this = renderState("Right", "Right", "Right", "Right") }
}
from string path
where any(InitialState i).reachesVia(path, _) = any(GoalState g)
select path + "."

View File

@@ -0,0 +1,205 @@
/**
* A solution to the river crossing puzzle using abstract
* classes/predicates to model the situation and unicode
* symbols to display the answer.
*/
/** One of two shores. */
class Shore extends string {
Shore() { this = "left" or this = "right" }
}
/** Models the behavior of the man. */
class Man extends string {
Shore s;
Man() { this = "man " + s }
/** Holds if the man is on a particular shore. */
predicate isOn(Shore shore) { s = shore }
/** Returns the other shore, after the man crosses the river. */
Man cross() { result != this }
/** Returns a cargo and its position after being ferried. */
Cargo ferry(Cargo c) {
result = c.cross() and
c.isOn(s)
}
}
/** One of three possible cargo items, with their position. */
abstract class Cargo extends string {
Shore s;
bindingset[this]
Cargo() { any() }
/** Holds if the cargo is on a particular shore. */
predicate isOn(Shore shore) { s = shore }
/** Returns the other shore, after the cargo crosses the river. */
abstract Cargo cross();
}
/** Models the position of the goat. */
class Goat extends Cargo {
Goat() { this = "goat " + s }
override Goat cross() { result != this }
}
/** Models the position of the wolf. */
class Wolf extends Cargo {
Wolf() { this = "wolf " + s }
override Wolf cross() { result != this }
}
/** Models the position of the cabbage. */
class Cabbage extends Cargo {
Cabbage() { this = "cabbage " + s }
override Cabbage cross() { result != this }
}
/** Returns a unicode representation of everything on the left shore. */
string onLeft(Man man, Goat goat, Cabbage cabbage, Wolf wolf) {
exists(string manOnLeft, string goatOnLeft, string cabbageOnLeft, string wolfOnLeft |
(
man.isOn("left") and manOnLeft = "🕴"
or
man.isOn("right") and manOnLeft = "____"
) and
(
goat.isOn("left") and goatOnLeft = "🐐"
or
goat.isOn("right") and goatOnLeft = "___"
) and
(
cabbage.isOn("left") and cabbageOnLeft = "🥬"
or
cabbage.isOn("right") and cabbageOnLeft = "___"
) and
(
wolf.isOn("left") and wolfOnLeft = "🐺"
or
wolf.isOn("right") and wolfOnLeft = "___"
) and
result = manOnLeft + "__" + goatOnLeft + "__" + cabbageOnLeft + "__" + wolfOnLeft
)
}
/** Returns a unicode representation of everything on the right shore. */
string onRight(Man man, Goat goat, Cabbage cabbage, Wolf wolf) {
exists(string manOnLeft, string goatOnLeft, string cabbageOnLeft, string wolfOnLeft |
(
man.isOn("right") and manOnLeft = "🕴"
or
man.isOn("left") and manOnLeft = "_"
) and
(
goat.isOn("right") and goatOnLeft = "🐐"
or
goat.isOn("left") and goatOnLeft = "__"
) and
(
cabbage.isOn("right") and cabbageOnLeft = "🥬"
or
cabbage.isOn("left") and cabbageOnLeft = "__"
) and
(
wolf.isOn("right") and wolfOnLeft = "🐺"
or
wolf.isOn("left") and wolfOnLeft = "__"
) and
result = manOnLeft + "__" + goatOnLeft + "__" + cabbageOnLeft + "__" + wolfOnLeft
)
}
/** Renders the state as a string, using unicode symbols. */
string render(Man man, Goat goat, Cabbage cabbage, Wolf wolf) {
result = onLeft(man, goat, cabbage, wolf) + "___🌊🌊🌊🌊🌊🌊🌊🌊🌊🌊___" +
onRight(man, goat, cabbage, wolf)
}
/** A record of where everything is. */
class State extends string {
Man man;
Goat goat;
Cabbage cabbage;
Wolf wolf;
State() { this = render(man, goat, cabbage, wolf) }
/**
* Returns the possible states that you can transition to
* by ferrying one or zero cargo items.
*/
State transition() {
result = render(man.cross(), man.ferry(goat), cabbage, wolf) or
result = render(man.cross(), goat, man.ferry(cabbage), wolf) or
result = render(man.cross(), goat, cabbage, man.ferry(wolf)) or
result = render(man.cross(), goat, cabbage, wolf)
}
/**
* Returns all states that are reachable via a transition
* and have not yet been visited.
* `path` keeps track of how it is achieved.
*/
State reachesVia(string path) {
exists(string pathSoFar |
result = this.reachesVia(pathSoFar).transition() and
not exists(int i | i = pathSoFar.indexOf(result.toString())) and
path = pathSoFar + "\n↓\n" + result
)
}
}
/** The initial state, where everything is on the left shore. */
class InitialState extends State {
InitialState() {
exists(Shore left | left = "left" |
man.isOn(left) and goat.isOn(left) and cabbage.isOn(left) and wolf.isOn(left)
)
}
override State reachesVia(string path) {
path = this + "\n↓\n" + result and result = transition()
or
result = super.reachesVia(path)
}
}
/** The goal state, where everything is on the right shore. */
class GoalState extends State {
GoalState() {
exists(Shore right | right = "right" |
man.isOn(right) and goat.isOn(right) and cabbage.isOn(right) and wolf.isOn(right)
)
}
override State transition() { none() }
}
/** An unsafe state, where something gets eaten. */
class IllegalState extends State {
IllegalState() {
exists(Shore s |
goat.isOn(s) and cabbage.isOn(s) and not man.isOn(s)
or
wolf.isOn(s) and goat.isOn(s) and not man.isOn(s)
)
}
override State transition() { none() }
}
from string path
where any(InitialState i).reachesVia(path) = any(GoalState g)
select path

View File

@@ -0,0 +1,172 @@
/**
* "Typesafe" solution to the river crossing puzzle.
*/
/** Either the left shore or the right shore. */
newtype TShore =
Left() or
Right()
class Shore extends TShore {
Shore other() { result != this }
string toString() {
this = Left() and result = "left"
or
this = Right() and result = "right"
}
}
newtype TMan = TManOn(Shore s)
/** Models the behavior of the man. */
class Man extends TMan {
Shore s;
Man() { this = TManOn(s) }
/** Holds if the man is on a particular shore. */
predicate isOn(Shore shore) { s = shore }
/** Returns the other shore, after the man crosses the river. */
Man cross() { result.isOn(s.other()) }
/** Returns a cargo and its position after being ferried. */
Cargo ferry(Cargo c) {
result = c.cross() and
c.isOn(s)
}
string toString() { result = "man " + s }
}
newtype TCargo =
TGoat(Shore s) or
TCabbage(Shore s) or
TWolf(Shore s)
/** One of three possible cargo items, with their position. */
abstract class Cargo extends TCargo {
Shore s;
/** Holds if the cargo is on a particular shore. */
predicate isOn(Shore shore) { s = shore }
/** Returns the other shore, after the cargo crosses the river. */
abstract Cargo cross();
abstract string toString();
}
/** Models the position of the goat. */
class Goat extends Cargo, TGoat {
Goat() { this = TGoat(s) }
override Cargo cross() { result = TGoat(s.other()) }
override string toString() { result = "goat " + s }
}
/** Models the position of the wolf. */
class Wolf extends Cargo, TWolf {
Wolf() { this = TWolf(s) }
override Cargo cross() { result = TWolf(s.other()) }
override string toString() { result = "wolf " + s }
}
/** Models the position of the cabbage. */
class Cabbage extends Cargo, TCabbage {
Cabbage() { this = TCabbage(s) }
override Cargo cross() { result = TCabbage(s.other()) }
override string toString() { result = "cabbage " + s }
}
newtype TState = Currently(Man man, Goat goat, Cabbage cabbage, Wolf wolf)
/** A record of where everything is. */
class State extends TState {
Man man;
Goat goat;
Cabbage cabbage;
Wolf wolf;
State() { this = Currently(man, goat, cabbage, wolf) }
/**
* Returns the possible states that you can transition to
* by ferrying one or zero cargo items.
*/
State transition() {
result = Currently(man.cross(), man.ferry(goat), cabbage, wolf) or
result = Currently(man.cross(), goat, man.ferry(cabbage), wolf) or
result = Currently(man.cross(), goat, cabbage, man.ferry(wolf)) or
result = Currently(man.cross(), goat, cabbage, wolf)
}
/**
* Returns all states that are reachable via a transition
* and have not yet been visited.
* `path` keeps track of how it is achieved.
*/
State reachesVia(string path) {
exists(string pathSoFar |
result = this.reachesVia(pathSoFar).transition() and
not exists(int i | i = pathSoFar.indexOf(result.toString())) and
path = pathSoFar + "\n" + result
)
}
string toString() { result = man + "/" + goat + "/" + cabbage + "/" + wolf }
}
/** The initial state, where everything is on the left shore. */
class InitialState extends State {
InitialState() {
man.isOn(Left()) and goat.isOn(Left()) and cabbage.isOn(Left()) and wolf.isOn(Left())
}
override State reachesVia(string path) {
path = this + "\n" + result and result = transition()
or
result = super.reachesVia(path)
}
override string toString() { result = "Initial: " + super.toString() }
}
/** The goal state, where everything is on the right shore. */
class GoalState extends State {
GoalState() {
man.isOn(Right()) and goat.isOn(Right()) and cabbage.isOn(Right()) and wolf.isOn(Right())
}
override State transition() { none() }
override string toString() { result = "Goal: " + super.toString() }
}
/** An unsafe state, where something gets eaten. */
class IllegalState extends State {
IllegalState() {
exists(Shore s |
goat.isOn(s) and cabbage.isOn(s) and not man.isOn(s)
or
wolf.isOn(s) and goat.isOn(s) and not man.isOn(s)
)
}
override State transition() { none() }
override string toString() { result = "ILLEGAL: " + super.toString() }
}
from string path
where any(InitialState i).reachesVia(path) = any(GoalState g)
select path