mirror of
https://github.com/github/codeql.git
synced 2025-12-16 16:53:25 +01:00
QL code and tests for C#/C++/JavaScript.
This commit is contained in:
10
.gitignore
vendored
Normal file
10
.gitignore
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
# editor and OS artifacts
|
||||
*~
|
||||
.DS_STORE
|
||||
|
||||
# query compilation caches
|
||||
.cache
|
||||
|
||||
# qltest projects and artifacts
|
||||
*/ql/test/**/*.testproj
|
||||
*/ql/test/**/*.actual
|
||||
46
CONTRIBUTING.md
Normal file
46
CONTRIBUTING.md
Normal file
@@ -0,0 +1,46 @@
|
||||
# Contributing to QL
|
||||
|
||||
We welcome contributions to our standard library and standard checks, got an idea for a new check, or how to improve an existing query? Then please go ahead an open a Pull Request!
|
||||
|
||||
Before we accept your pull request, we will require that you have agreed to our Contributor License Agreement, this is not something that you need to do before you submit your pull request, but until you've done so, we will be unable to accept your contribution.
|
||||
|
||||
## Using your personal data
|
||||
|
||||
If you contribute to this project, we will record your name and email
|
||||
address (as provided by you with your contributions) as part of the code
|
||||
repositories, which might be made public. We might also use this information
|
||||
to contact you in relation to your contributions, as well as in the
|
||||
normal course of software development. We also store records of your
|
||||
CLA agreements. Under GDPR legislation, we do this
|
||||
on the basis of our legitimate interest in creating the QL product.
|
||||
|
||||
Please do get in touch (privacy@semmle.com) if you have any questions about
|
||||
this or our data protection policies.
|
||||
|
||||
## Contributor License Agreement
|
||||
|
||||
This Contributor License Agreement (“Agreement”) is entered into between Semmle Limited (“Semmle,” “we” or “us” etc.), and You (as defined and further identified below).
|
||||
|
||||
Accordingly, You hereby agree to the following terms for Your present and future Contributions submitted to Semmle:
|
||||
|
||||
1. **Definitions**.
|
||||
|
||||
* "You" (or "Your") shall mean the Contribution copyright owner (whether an individual or organization) or legal entity authorized by the copyright owner that is making this Agreement with Semmle. For legal entities, the entity making a Contribution and all other entities that control, are controlled by, or are under common control with that entity are considered to be a single Contributor. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
* "Contribution(s)" shall mean the code, documentation or other original works of authorship, including any modifications or additions to an existing work, submitted by You to Semmle for inclusion in, or documentation of, any of the products or projects owned or managed by Semmle (the "Work(s)"). For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to Semmle or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, Semmle for the purpose of discussing and/or improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by You as "Not a Contribution."
|
||||
|
||||
2. **Grant of Copyright License**. You hereby grant to Semmle and to recipients of software distributed by Semmle a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare derivative works of, publicly display, publicly perform, sublicense, and distribute Your Contributions and such derivative works.
|
||||
|
||||
3. **Grant of Patent License**. You hereby grant to Semmle and to recipients of software distributed by Semmle a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by You that are necessarily infringed by Your Contribution(s) alone or by combination of Your Contribution(s) with the Work to which such Contribution(s) was submitted. If any entity institutes patent litigation against You or any other entity (including a cross-claim or counterclaim in a lawsuit) alleging that Your Contribution, or the Work to which You have contributed, constitutes direct or contributory patent infringement, then any patent licenses granted to that entity under this Agreement for that Contribution or Work shall terminate as of the date such litigation is filed.
|
||||
|
||||
4. **Ownership**. Except as set out above, You keep all right, title, and interest in Your Contribution. The rights that You grant to us under this Agreement are effective on the date You first submitted a Contribution to us, even if Your submission took place before the date You entered this Agreement.
|
||||
|
||||
5. **Representations**. You represent and warrant that: (i) the Contributions are an original work and that You can legally grant the rights set out in this Agreement; (ii) the Contributions and Semmle’s exercise of any license rights granted hereunder, does not and will not, infringe the rights of any third party; (iii) You are not aware of any pending or threatened claims, suits, actions, or charges pertaining to the Contributions, including without limitation any claims or allegations that any or all of the Contributions infringes, violates, or misappropriate the intellectual property rights of any third party (You further agree that You will notify Semmle immediately if You become aware of any such actual or potential claims, suits, actions, allegations or charges).
|
||||
|
||||
6. **Employer**. If Your employer(s) has rights to intellectual property that You create that includes Your Contributions, You represent and warrant that Your employer has waived such rights for Your Contributions to Semmle, or that You have received permission to make Contributions on behalf of that employer and that You are authorized to execute this Agreement on behalf of Your employer.
|
||||
|
||||
7. **Inclusion of Code**. We determine the code that is in our Works. You understand that the decision to include the Contribution in any project or source repository is entirely that of Semmle, and this agreement does not guarantee that the Contributions will be included in any product.
|
||||
|
||||
8. **Disclaimer**. You are not expected to provide support for Your Contributions, except to the extent You desire to provide support. You may provide support for free, for a fee, or not at all. Except as set forth herein, and unless required by applicable law or agreed to in writing, You provide Your Contributions on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND.
|
||||
|
||||
9. **General**. The failure of either party to enforce its rights under this Agreement for any period shall not be construed as a waiver of such rights. No changes or modifications or waivers to this Agreement will be effective unless in writing and signed by both parties. In the event that any provision of this Agreement shall be determined to be illegal or unenforceable, that provision will be limited or eliminated to the minimum extent necessary so that this Agreement shall otherwise remain in full force and effect and enforceable. This Agreement shall be governed by and construed in accordance with the laws of the State of California in the United States without regard to the conflicts of laws provisions thereof. In any action or proceeding to enforce rights under this Agreement, the prevailing party will be entitled to recover costs and attorneys’ fees.
|
||||
13
COPYRIGHT
Normal file
13
COPYRIGHT
Normal file
@@ -0,0 +1,13 @@
|
||||
Copyright (c) Semmle Inc and other contributors. All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License"); you may not use
|
||||
this file except in compliance with the License. You may obtain a copy of the
|
||||
License at http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
|
||||
WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
|
||||
MERCHANTABLITY OR NON-INFRINGEMENT.
|
||||
|
||||
See the Apache Version 2.0 License for specific language governing permissions
|
||||
and limitations under the License.
|
||||
176
LICENSE
Normal file
176
LICENSE
Normal file
@@ -0,0 +1,176 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
15
README.md
Normal file
15
README.md
Normal file
@@ -0,0 +1,15 @@
|
||||
# Semmle QL
|
||||
|
||||
This open source repository contains the standard QL libraries and queries that power [LGTM](https://lgtm.com), and the other products that [Semmle](https://semmle.com) makes available to its customers worldwide.
|
||||
|
||||
## How do I learn QL and run queries?
|
||||
|
||||
LGTM has [extensive documentation](https://lgtm.com/help/ql/introduction-to-ql) on getting started writing QL, and also has an [interactive query console](https://lgtm.com/help/lgtm/using-query-console) that allows you to try out your queries on any open-source project that's currently being analysed.
|
||||
|
||||
## Contributing
|
||||
|
||||
We welcome contributions to our standard library and standard checks, got an idea for a new check, or how to improve an existing query? Then please go ahead an open a Pull Request! Before you do though, please take the time to read our [contributing guidelines](CONTRIBUTING.md).
|
||||
|
||||
## License
|
||||
|
||||
The lgtm queries are licensed under [Apache License 2.0](LICENSE) by [Semmle](https://semmle.com).
|
||||
35
change-notes/1.18/analysis-csharp.md
Normal file
35
change-notes/1.18/analysis-csharp.md
Normal file
@@ -0,0 +1,35 @@
|
||||
# Improvements to C# analysis
|
||||
|
||||
> NOTES
|
||||
>
|
||||
> Please describe your changes in terms that are suitable for
|
||||
> customers to read. These notes will have only minor tidying up
|
||||
> before they are published as part of the release notes.
|
||||
|
||||
## General improvements
|
||||
|
||||
> Changes that affect alerts in many files or from many queries
|
||||
> For example, changes to file classification
|
||||
|
||||
## New queries
|
||||
|
||||
| **Query** | **Tags** | **Purpose** |
|
||||
|-----------------------------|-----------|--------------------------------------------------------------------|
|
||||
| Local scope variable shadows member (cs/local-shadows-member) | maintainability, readability | Replaces the existing queries [Local variable shadows class member (cs/local-shadows-class-member)](https://help.semmle.com/wiki/display/CSHARP/Local+variable+shadows+class+member), [Local variable shadows struct member (cs/local-shadows-struct-member)](https://help.semmle.com/wiki/display/CSHARP/Local+variable+shadows+struct+member), [Parameter shadows class member (cs/parameter-shadows-class-member)](https://help.semmle.com/wiki/display/CSHARP/Parameter+shadows+class+member), and [Parameter shadows struct member (cs/parameter-shadows-struct-member)](https://help.semmle.com/wiki/display/CSHARP/Parameter+shadows+struct+member). |
|
||||
|
||||
## Changes to existing queries
|
||||
|
||||
| **Query** | **Expected impact** | **Change** |
|
||||
|----------------------------|------------------------|------------------------------------------------------------------|
|
||||
| [Missing Dispose call on local IDisposable (cs/local-not-disposed)](https://help.semmle.com/wiki/display/CSHARP/Missing+Dispose+call+on+local+IDisposable) | Fewer results | The query identifies more cases where the local variable may be disposed by a library call. |
|
||||
| [Nested loops with same variable (cs/nested-loops-with-same-variable)](https://help.semmle.com/wiki/display/CSHARP/Nested+loops+with+same+variable) | Fewer results | Results are no longer highlighted in nested loops that share the same condition, and do not use the variable after the inner loop. |
|
||||
| [Potentially incorrect CompareTo(...) signature (cs/wrong-compareto-signature)](https://help.semmle.com/wiki/display/CSHARP/Potentially+incorrect+CompareTo%28...%29+signature) | Fewer results | Results are no longer highlighted in constructed types. |
|
||||
| [Useless upcast (cs/useless-upcast)](https://help.semmle.com/wiki/display/CSHARP/Useless+upcast) | Fewer results | The query has been improved to cover more cases where upcasts may be needed. |
|
||||
|
||||
## Changes to code extraction
|
||||
|
||||
* *Series of bullet points*
|
||||
|
||||
## Changes to QL libraries
|
||||
|
||||
* A new non-member predicate `mayBeDisposed()` can be used to determine if a variable is potentially disposed inside a library. It will analyse the CIL code in the library to determine this.
|
||||
45
change-notes/1.18/analysis-javascript.md
Normal file
45
change-notes/1.18/analysis-javascript.md
Normal file
@@ -0,0 +1,45 @@
|
||||
# Improvements to JavaScript analysis
|
||||
|
||||
## General improvements
|
||||
|
||||
* Additional heuristics have been added to `semmle.javascript.heuristics`. Add `import semmle.javascript.heuristics.all` to a query in order to activate all of the heuristics at once.
|
||||
|
||||
* Modelling of data flow through destructuring assignments has been improved. This may give additional results for the security queries and other queries that rely on data flow.
|
||||
|
||||
* Support for popular libraries has been improved. Consequently, queries may produce more results on code bases that use the following libraries:
|
||||
- [bluebird](http://bluebirdjs.com)
|
||||
- [browserid-crypto](https://github.com/mozilla/browserid-crypto)
|
||||
- [cookie-parser](https://github.com/expressjs/cookie-parser)
|
||||
- [cookie-session](https://github.com/expressjs/cookie-session)
|
||||
- [crypto-js](https://github.com/https://github.com/brix/crypto-js)
|
||||
- [express-jwt](https://github.com/auth0/express-jwt)
|
||||
- [express-session](https://github.com/expressjs/session)
|
||||
- [forge](https://github.com/digitalbazaar/forge)
|
||||
- [MySQL2](https://github.com/sidorares/node-mysql2)
|
||||
- [q](http://documentup.com/kriskowal/q/)
|
||||
|
||||
## New queries
|
||||
|
||||
| **Query** | **Tags** | **Purpose** |
|
||||
|-----------------------------|-----------|--------------------------------------------------------------------|
|
||||
| Disabling Electron webSecurity (`js/disabling-electron-websecurity`) | security, frameworks/electron | Highlights Electron browser objects that are created with the `webSecurity` property set to false. Results shown on LGTM by default. |
|
||||
| Enabling Electron allowRunningInsecureContent (`js/enabling-electron-insecure-content`) | security, frameworks/electron | Highlights Electron browser objects that are created with the `allowRunningInsecureContent` property set to true. Results shown on LGTM by default. |
|
||||
| Use of externally-controlled format string (`js/tainted-format-string`) | security, external/cwe/cwe-134 | Highlights format strings containing user-provided data, indicating a violation of [CWE-134](https://cwe.mitre.org/data/definitions/134.html). Results shown on LGTM by default. |
|
||||
|
||||
## Changes to existing queries
|
||||
|
||||
| **Query** | **Expected impact** | **Change** |
|
||||
|----------------------------|------------------------|------------------------------------------------------------------|
|
||||
| Arguments redefined | Fewer results | This rule previously also flagged redefinitions of `eval`. This was an oversight that is now fixed. |
|
||||
| CORS misconfiguration for credentials transfer | More true-positive results | This rule now treats header names case-insensitively. |
|
||||
| Hard-coded credentials | More true-positive results | This rule now recognizes secret cryptographic keys. |
|
||||
| Insecure randomness | More true-positive results | This rule now recognizes secret cryptographic keys. |
|
||||
| Missing X-Frame-Options HTTP header | Fewer false-positive results | This rule now treats header names case-insensitively. |
|
||||
| Reflected cross-site scripting | Fewer false-positive results | This rule now treats header names case-insensitively. |
|
||||
| Server-side URL redirect | More true-positive results | This rule now treats header names case-insensitively. |
|
||||
| Uncontrolled command line | More true-positive results | This rule now recognizes indirect command injection through `sh -c` and similar. |
|
||||
| Unused variable | Fewer results | This rule no longer flags class expressions that could be made anonymous. While technically true, these results are not interesting. |
|
||||
|
||||
## Changes to QL libraries
|
||||
|
||||
* HTTP header names are now always normalized to lower case to reflect the fact that they are case insensitive. In particular, the result of `HeaderDefinition.getAHeaderName`, and the first parameter of `HeaderDefinition.defines`, `ExplicitHeaderDefinition.definesExplicitly` and `RouteHandler.getAResponseHeader` is now always a lower-case string.
|
||||
12
cpp/ql/src/.project
Normal file
12
cpp/ql/src/.project
Normal file
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<projectDescription>
|
||||
<name>semmlecode-cpp-queries</name>
|
||||
<comment></comment>
|
||||
<projects>
|
||||
</projects>
|
||||
<buildSpec>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
<nature>com.semmle.plugin.qdt.core.qlnature</nature>
|
||||
</natures>
|
||||
</projectDescription>
|
||||
10
cpp/ql/src/.qlpath
Normal file
10
cpp/ql/src/.qlpath
Normal file
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<ns2:qlpath xmlns:ns2="https://semmle.com/schemas/qlpath">
|
||||
<librarypath>
|
||||
<path>/semmlecode-cpp-queries</path>
|
||||
</librarypath>
|
||||
<dbscheme>/semmlecode-cpp-queries/semmlecode.cpp.dbscheme</dbscheme>
|
||||
<defaultImports>
|
||||
<defaultImport>cpp</defaultImport>
|
||||
</defaultImports>
|
||||
</ns2:qlpath>
|
||||
8
cpp/ql/src/.vs/VSWorkspaceSettings.json
Normal file
8
cpp/ql/src/.vs/VSWorkspaceSettings.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"ql.projects" : {
|
||||
"." : {
|
||||
"dbScheme" : "semmlecode.cpp.dbscheme",
|
||||
"libraryPath" : []
|
||||
}
|
||||
}
|
||||
}
|
||||
89
cpp/ql/src/AlertSuppression.ql
Normal file
89
cpp/ql/src/AlertSuppression.ql
Normal file
@@ -0,0 +1,89 @@
|
||||
/**
|
||||
* @name Alert suppression
|
||||
* @description Generates information about alert suppressions.
|
||||
* @kind alert-suppression
|
||||
* @id cpp/alert-suppression
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
/**
|
||||
* An alert suppression comment.
|
||||
*/
|
||||
class SuppressionComment extends CppStyleComment {
|
||||
string annotation;
|
||||
string text;
|
||||
|
||||
SuppressionComment() {
|
||||
text = getContents().suffix(2) and
|
||||
( // match `lgtm[...]` anywhere in the comment
|
||||
annotation = text.regexpFind("(?i)\\blgtm\\s*\\[[^\\]]*\\]", _, _)
|
||||
or
|
||||
// match `lgtm` at the start of the comment and after semicolon
|
||||
annotation = text.regexpFind("(?i)(?<=^|;)\\s*lgtm(?!\\B|\\s*\\[)", _, _)
|
||||
.trim()
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
/** Gets the text in this comment, excluding the leading //. */
|
||||
string getText() {
|
||||
result = text
|
||||
}
|
||||
|
||||
/** Gets the suppression annotation in this comment. */
|
||||
string getAnnotation() {
|
||||
result = annotation
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this comment applies to the range from column `startcolumn` of line `startline`
|
||||
* to column `endcolumn` of line `endline` in file `filepath`.
|
||||
*/
|
||||
predicate covers(string filepath, int startline, int startcolumn, int endline, int endcolumn) {
|
||||
this.getLocation().hasLocationInfo(filepath, startline, _, endline, endcolumn) and
|
||||
startcolumn = 1
|
||||
}
|
||||
|
||||
/** Gets the scope of this suppression. */
|
||||
SuppressionScope getScope() {
|
||||
this = result.getSuppressionComment()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The scope of an alert suppression comment.
|
||||
*/
|
||||
class SuppressionScope extends @comment {
|
||||
SuppressionScope() {
|
||||
this instanceof SuppressionComment
|
||||
}
|
||||
|
||||
/** Gets a suppression comment with this scope. */
|
||||
SuppressionComment getSuppressionComment() {
|
||||
result = this
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this element is at the specified location.
|
||||
* The location spans column `startcolumn` of line `startline` to
|
||||
* column `endcolumn` of line `endline` in file `filepath`.
|
||||
* For more information, see
|
||||
* [LGTM locations](https://lgtm.com/help/ql/locations).
|
||||
*/
|
||||
predicate hasLocationInfo(string filepath, int startline, int startcolumn, int endline, int endcolumn) {
|
||||
this.(SuppressionComment).covers(filepath, startline, startcolumn, endline, endcolumn)
|
||||
}
|
||||
|
||||
/** Gets a textual representation of this element. */
|
||||
string toString() {
|
||||
result = "suppression range"
|
||||
}
|
||||
}
|
||||
|
||||
from SuppressionComment c
|
||||
select c, // suppression comment
|
||||
c.getText(), // text of suppression comment (excluding delimiters)
|
||||
c.getAnnotation(), // text of suppression annotation
|
||||
c.getScope() // scope of suppression
|
||||
|
||||
32
cpp/ql/src/Architecture/FeatureEnvy.qhelp
Normal file
32
cpp/ql/src/Architecture/FeatureEnvy.qhelp
Normal file
@@ -0,0 +1,32 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
|
||||
|
||||
<overview>
|
||||
<p>
|
||||
This rule finds functions that use more functions and variables from another file than functions and variables from its own file.
|
||||
This function may be better placed in the other file, to avoid exposing internals of the file it depends on.
|
||||
</p>
|
||||
|
||||
</overview>
|
||||
<recommendation>
|
||||
<p>See if the function can be moved to the file which contains most of its dependencies.</p>
|
||||
|
||||
</recommendation>
|
||||
<references>
|
||||
|
||||
<li>W. C. Wake, <em>Refactoring Workbook</em>, pp. 95 – 96. Addison-Wesley Professional, 2004.</li>
|
||||
<li>E. Gamma, R. Helm, R. Johnson, J. Vlissides,
|
||||
<em>Design patterns: elements of reusable object-oriented software</em>.
|
||||
Addison-Wesley Longman Publishing Co., Inc. Boston, MA, 1995.</li>
|
||||
<li>
|
||||
MSDN Magazine: <a href="http://msdn.microsoft.com/en-us/magazine/cc947917.aspx">Patterns in Practice: Cohesion And Coupling</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
</references>
|
||||
</qhelp>
|
||||
65
cpp/ql/src/Architecture/FeatureEnvy.ql
Normal file
65
cpp/ql/src/Architecture/FeatureEnvy.ql
Normal file
@@ -0,0 +1,65 @@
|
||||
/**
|
||||
* @name Feature envy
|
||||
* @description A function that uses more functions and variables from another file than functions and variables from its own file. This function might be better placed in the other file, to avoid exposing internals of the file it depends on.
|
||||
* @kind problem
|
||||
* @problem.severity recommendation
|
||||
* @precision high
|
||||
* @id cpp/feature-envy
|
||||
* @tags maintainability
|
||||
* modularity
|
||||
* statistical
|
||||
* non-attributable
|
||||
*/
|
||||
import cpp
|
||||
|
||||
predicate functionUsesVariable(Function source, Variable v, File target) {
|
||||
v.getAnAccess().getEnclosingFunction() = source and
|
||||
not (v.(LocalScopeVariable).getFunction() = source) and
|
||||
v.getFile() = target
|
||||
}
|
||||
|
||||
predicate functionUsesFunction(Function source, Function f, File target) {
|
||||
exists(FunctionCall fc | fc.getEnclosingFunction() = source and fc.getTarget() = f) and
|
||||
f.getFile() = target
|
||||
}
|
||||
|
||||
predicate dependencyCount(Function source, File target, int res) {
|
||||
res = strictcount(Declaration d |
|
||||
functionUsesVariable(source, d, target) or
|
||||
functionUsesFunction(source, d, target)
|
||||
)
|
||||
}
|
||||
|
||||
predicate selfDependencyCountOrZero(Function source, int res) {
|
||||
exists(File target
|
||||
| target = source.getFile() and onlyInFile(source, target)
|
||||
| res = max(int i | dependencyCount(source, target, i) or i = 0))
|
||||
}
|
||||
|
||||
predicate dependsHighlyOn(Function source, File target, int res) {
|
||||
dependencyCount(source, target, res) and
|
||||
target.fromSource() and
|
||||
exists(int selfCount |
|
||||
selfDependencyCountOrZero(source, selfCount) and
|
||||
res > 2*selfCount and
|
||||
res > 4
|
||||
)
|
||||
}
|
||||
|
||||
predicate onlyInFile(Function f, File file) {
|
||||
file = f.getFile() and
|
||||
not exists(File file2 | file2 = f.getFile() and file2 != file)
|
||||
}
|
||||
|
||||
from Function f, File other, int selfCount, int depCount, string selfDeps
|
||||
where dependsHighlyOn(f, other, depCount) and
|
||||
selfDependencyCountOrZero(f, selfCount) and
|
||||
not exists(File yetAnother | dependsHighlyOn(f, yetAnother, _) and yetAnother != other) and
|
||||
not other instanceof HeaderFile and
|
||||
not f instanceof MemberFunction
|
||||
and if selfCount = 0 then selfDeps = "0 dependencies"
|
||||
else if selfCount = 1 then selfDeps = "only 1 dependency"
|
||||
else selfDeps = "only " + selfCount.toString() + " dependencies"
|
||||
select f, "Function " + f.getName() + " could be moved to file $@" +
|
||||
" since it has " + depCount.toString() + " dependencies to that file, but " +
|
||||
selfDeps + " to its own file.", other, other.getBaseName()
|
||||
@@ -0,0 +1,24 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
|
||||
|
||||
<overview>
|
||||
<p>This query shows graph of class inheritance hierarchy</p>
|
||||
|
||||
<p />
|
||||
|
||||
</overview>
|
||||
<section title="How to Address the Query Results">
|
||||
<p />
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</section>
|
||||
<references>
|
||||
|
||||
</references>
|
||||
</qhelp>
|
||||
@@ -0,0 +1,15 @@
|
||||
/**
|
||||
* @name Class hierarchies
|
||||
* @description Shows classes and their base classes.
|
||||
* @kind graph
|
||||
* @id cpp/architecture/class-hierarchies
|
||||
* @graph.layout organic
|
||||
* @workingset jhotdraw
|
||||
* @result succeed 48
|
||||
* @result_ondemand succeed 48
|
||||
*/
|
||||
import cpp
|
||||
|
||||
from Class s
|
||||
where s.fromSource()
|
||||
select s, s.getABaseClass()
|
||||
@@ -0,0 +1,25 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
|
||||
|
||||
<overview>
|
||||
<p>This query shows coupling between classes.</p>
|
||||
|
||||
<p>Red, large boxes are hub types that depend on many other classes
|
||||
and are depended on by many other classes.</p>
|
||||
|
||||
</overview>
|
||||
<section title="How to Address the Query Results">
|
||||
<p />
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</section>
|
||||
<references>
|
||||
|
||||
</references>
|
||||
</qhelp>
|
||||
@@ -0,0 +1,16 @@
|
||||
/**
|
||||
* @name Hub classes
|
||||
* @description Shows coupling between classes; red, large boxes are hub types that depend on many other classes
|
||||
* and are depended on by many other classes.
|
||||
* @kind treemap
|
||||
* @id cpp/architecture/hub-classes
|
||||
* @treemap.warnOn highValues
|
||||
*/
|
||||
import cpp
|
||||
|
||||
from Class c
|
||||
where c.fromSource()
|
||||
select c as Class,
|
||||
c.getMetrics().getAfferentCoupling() as AfferentCoupling,
|
||||
c.getMetrics().getEfferentSourceCoupling() as EfferentCoupling
|
||||
order by AfferentCoupling desc
|
||||
@@ -0,0 +1,48 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
|
||||
|
||||
<overview>
|
||||
<p>This query shows the distribution of inheritance depth across all types, i.e. classes. Library types are ignored.</p>
|
||||
|
||||
<p>The result of this query is a line graph showing, for each number <i>n</i>, how many types have an inheritance depth of <i>n</i>, where
|
||||
the inheritance depth of a type is the length of a longest path in the inheritance hierarchy from top class to the type.</p>
|
||||
|
||||
<p>When hovering the mouse pointer over a specific depth value, the number of types having this inheritance depth is displayed.</p>
|
||||
|
||||
</overview>
|
||||
<section title="How to Address the Query Results">
|
||||
<p>The depth of a type is an indication of how deeply nested a type is in a given design.
|
||||
Very deep types can be an indication of over-engineering, whereas a system with predominantly shallow types
|
||||
may not be exploiting object-orientation to the full.</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</section>
|
||||
<references>
|
||||
<li>
|
||||
Shyam R. Chidamber and Chris F. Kemerer.
|
||||
<a href="http://www.pitt.edu/~ckemerer/CK%20research%20papers/MetricForOOD_ChidamberKemerer94.pdf">A Metrics Suite for Object Oriented Design
|
||||
</a>.
|
||||
IEEE Transactions on Software Engineering,
|
||||
20(6), pages 476-493, June 1994.
|
||||
|
||||
|
||||
|
||||
<a href="http://www.dmst.aueb.gr/dds/index.en.html">Diomides D. Spinnelis</a>.
|
||||
<a href="http://www.spinellis.gr/codequality/">Code Quality: The Open Source Perspective</a>.
|
||||
Addison-Wesley 2007.
|
||||
|
||||
|
||||
|
||||
<a href="http://www.dmst.aueb.gr/dds/index.en.html">Diomides D. Spinnelis</a>.
|
||||
<a href="http://www.spinellis.gr/sw/ckjm/">ckjm - Chidamber and Kemerer Java Metrics</a>.
|
||||
(implementation of CK metrics), 2006.
|
||||
|
||||
|
||||
|
||||
</li></references></qhelp>
|
||||
@@ -0,0 +1,22 @@
|
||||
/**
|
||||
* @name Inheritance depth distribution
|
||||
* @description Shows distribution of inheritance depth across all classes.
|
||||
* @kind chart
|
||||
* @id cpp/architecture/inheritance-depth-distribution
|
||||
* @chart.type line
|
||||
* @workingset jhotdraw
|
||||
* @result succeed 48
|
||||
* @result_ondemand succeed 48
|
||||
*/
|
||||
import cpp
|
||||
|
||||
/** does source class c have inheritance depth d? */
|
||||
predicate hasInheritanceDepth(Class c, int d) {
|
||||
c.fromSource() and d = c.getMetrics().getInheritanceDepth()
|
||||
}
|
||||
|
||||
from int depth
|
||||
where hasInheritanceDepth(_, depth)
|
||||
select depth as InheritanceDepth,
|
||||
count(Class c | hasInheritanceDepth(c, depth)) as NumberOfClasses
|
||||
order by InheritanceDepth
|
||||
@@ -0,0 +1,13 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
|
||||
|
||||
<overview>
|
||||
<p>about General Class-Level Information</p>
|
||||
|
||||
<!--TOC-->
|
||||
|
||||
</overview>
|
||||
</qhelp>
|
||||
@@ -0,0 +1,25 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
|
||||
|
||||
<overview>
|
||||
<p>This query shows namespaces that cyclically depend
|
||||
on one another.</p>
|
||||
|
||||
<p />
|
||||
|
||||
</overview>
|
||||
<section title="How to Address the Query Results">
|
||||
<p>If there are cyclic dependencies between packages, they cannot be developed and tested independently. It is thus preferable to
|
||||
eliminate such cycles from the program.</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</section>
|
||||
<references>
|
||||
<li>Robert Martin's <a href="https://drive.google.com/file/d/0BwhCYaYDn8EgOGM2ZGFhNmYtNmE4ZS00OGY5LWFkZTYtMjE0ZGNjODQ0MjEx/view">Acyclic Dependencies Principle</a>.
|
||||
</li></references></qhelp>
|
||||
@@ -0,0 +1,13 @@
|
||||
/**
|
||||
* @name Cyclic namespaces
|
||||
* @description Shows namespaces that cyclically depend on one another.
|
||||
* @kind graph
|
||||
* @id cpp/architecture/cyclic-namespaces
|
||||
* @graph.layout hierarchical
|
||||
* @tags maintainability
|
||||
*/
|
||||
import cpp
|
||||
|
||||
from MetricNamespace a, MetricNamespace b
|
||||
where a.getANamespaceDependency() = b and b.getANamespaceDependency*() = a
|
||||
select a, b
|
||||
@@ -0,0 +1,22 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
|
||||
|
||||
<overview>
|
||||
<p>This query finds classes that belong to no namespace</p>
|
||||
|
||||
</overview>
|
||||
<section title="How to Address the Query Results">
|
||||
<p>If there are too many classes that belong to no namespace, consider creating namespaces to get a better project structure.</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</section>
|
||||
<references>
|
||||
|
||||
</references>
|
||||
</qhelp>
|
||||
@@ -0,0 +1,13 @@
|
||||
/**
|
||||
* @name Global namespace classes
|
||||
* @description Finds classes that belong to no namespace
|
||||
* @kind table
|
||||
* @id cpp/architecture/global-namespace-classes
|
||||
*/
|
||||
import cpp
|
||||
|
||||
from Class c
|
||||
where c.fromSource()
|
||||
and c.isTopLevel()
|
||||
and c.getParentScope() instanceof GlobalNamespace
|
||||
select c, "This class is not declared in any namespace"
|
||||
@@ -0,0 +1,24 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
|
||||
|
||||
<overview>
|
||||
<p>This query finds namespace dependencies and draws hierarchical graph.</p>
|
||||
|
||||
<p />
|
||||
|
||||
</overview>
|
||||
<section title="How to Address the Query Results">
|
||||
<p />
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</section>
|
||||
<references>
|
||||
|
||||
</references>
|
||||
</qhelp>
|
||||
@@ -0,0 +1,12 @@
|
||||
/**
|
||||
* @name Namespace dependencies
|
||||
* @description Shows dependencies between namespaces.
|
||||
* @kind graph
|
||||
* @id cpp/architecture/namespace-dependencies
|
||||
* @graph.layout hierarchical
|
||||
*/
|
||||
import cpp
|
||||
|
||||
from MetricNamespace a, MetricNamespace b
|
||||
where a.getANamespaceDependency() = b
|
||||
select a, b
|
||||
@@ -0,0 +1,34 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
|
||||
|
||||
<overview>
|
||||
<p>This query shows general statistics about the application, organized as a table.</p>
|
||||
|
||||
|
||||
<p>The query shows the number of namespaces making up the application, the number of files, header files, C files, CPP files, classes, structures, unions, and
|
||||
functions, and the total number of source code resp. comment lines.</p>
|
||||
|
||||
<p>The self-containedness measure indicates how much the application
|
||||
depends on third-party libraries: low self-containedness means that many dependencies
|
||||
are to library classes (as opposed to source classes within the same application).</p>
|
||||
|
||||
</overview>
|
||||
<section title="How to Address the Query Results">
|
||||
|
||||
<p>The results of this query are purely informative and more useful for getting an overall impression of the application than for
|
||||
identifying particular defects.</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</section>
|
||||
<references>
|
||||
|
||||
|
||||
|
||||
</references>
|
||||
</qhelp>
|
||||
@@ -0,0 +1,34 @@
|
||||
/**
|
||||
* @name General statistics
|
||||
* @description Shows general statistics about the application.
|
||||
* @kind table
|
||||
* @id cpp/architecture/general-statistics
|
||||
*/
|
||||
import cpp
|
||||
|
||||
from string l, string n
|
||||
where (l = "Number of Namespaces" and
|
||||
n = count(Namespace p | p.fromSource()).toString())
|
||||
or (l = "Number of Files" and
|
||||
n = count(File f | f.fromSource()).toString())
|
||||
or (l = "Number of Header Files" and
|
||||
n = count(HeaderFile f | f.fromSource()).toString())
|
||||
or (l = "Number of C Files" and
|
||||
n = count(CFile f | f.fromSource()).toString())
|
||||
or (l = "Number of C++ Files" and
|
||||
n = count(CppFile f | f.fromSource()).toString())
|
||||
or (l = "Number of Classes" and
|
||||
n = count(Class c | c.fromSource() and not c instanceof Struct).toString())
|
||||
or (l = "Number of Structs" and
|
||||
n = count(Struct s | s.fromSource()and not s instanceof Union).toString())
|
||||
or (l = "Number of Unions" and
|
||||
n = count(Union u | u.fromSource()).toString())
|
||||
or (l = "Number of Functions" and
|
||||
n = count(Function f | f.fromSource()).toString())
|
||||
or (l = "Number of Lines Of Code" and
|
||||
n = sum(File f, int toSum | (f.fromSource()) and (toSum = f.getMetrics().getNumberOfLinesOfCode()) | toSum).toString())
|
||||
or (l = "Self-Containedness" and
|
||||
n = (100 * sum(Class c, int toSum | (c.fromSource()) and (toSum = c.getMetrics().getEfferentSourceCoupling()) | toSum)
|
||||
/ sum(Class c, int toSum | (c.fromSource()) and (toSum = c.getMetrics().getEfferentCoupling()) | toSum)).toString()
|
||||
+ "%")
|
||||
select l as Title, n as Value
|
||||
0
cpp/ql/src/Architecture/InappropriateIntimacy.cpp
Normal file
0
cpp/ql/src/Architecture/InappropriateIntimacy.cpp
Normal file
38
cpp/ql/src/Architecture/InappropriateIntimacy.qhelp
Normal file
38
cpp/ql/src/Architecture/InappropriateIntimacy.qhelp
Normal file
@@ -0,0 +1,38 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
|
||||
|
||||
<overview>
|
||||
<p>
|
||||
This rule looks for two files that share too much information about each other (accessing many operations or variables in both directions).
|
||||
It would be better to invert some of the dependencies to reduce the coupling between the two files.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Having two files have too many dependencies on each other makes it difficult to modify one file without requiring modifications to the
|
||||
other. This could lead to defects when the programmer forgets to put in the necessary changes to one file when he makes a change to the other.
|
||||
</p>
|
||||
|
||||
</overview>
|
||||
<recommendation>
|
||||
<p>Move some of the methods and variables from one file to another, so that most of the dependencies go only in one direction. If possible,
|
||||
try to make all the dependencies go in one direction.</p>
|
||||
|
||||
</recommendation>
|
||||
<references>
|
||||
|
||||
<li>W. C. Wake, <em>Refactoring Workbook</em>, pp. 95 – 96. Addison-Wesley Professional, 2004.</li>
|
||||
<li>E. Gamma, R. Helm, R. Johnson, J. Vlissides,
|
||||
<em>Design patterns: elements of reusable object-oriented software</em>.
|
||||
Addison-Wesley Longman Publishing Co., Inc. Boston, MA, 1995.</li>
|
||||
<li>
|
||||
MSDN Magazine: <a href="http://msdn.microsoft.com/en-us/magazine/cc947917.aspx">Patterns in Practice: Cohesion And Coupling</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
</references>
|
||||
</qhelp>
|
||||
63
cpp/ql/src/Architecture/InappropriateIntimacy.ql
Normal file
63
cpp/ql/src/Architecture/InappropriateIntimacy.ql
Normal file
@@ -0,0 +1,63 @@
|
||||
/**
|
||||
* @name Inappropriate Intimacy
|
||||
* @description Two files share too much information about each other (accessing many operations or variables in both directions). It would be better to invert some of the dependencies to reduce the coupling between the two files.
|
||||
* @kind problem
|
||||
* @problem.severity recommendation
|
||||
* @precision high
|
||||
* @id cpp/file-intimacy
|
||||
* @tags maintainability
|
||||
* modularity
|
||||
* statistical
|
||||
* non-attributable
|
||||
*/
|
||||
import cpp
|
||||
|
||||
predicate remoteVarAccess(File source, File target, VariableAccess va) {
|
||||
va.getFile() = source and
|
||||
va.getTarget().getFile() = target and
|
||||
// Ignore variables with locations in multiple files
|
||||
strictcount(File f | f = va.getTarget().getFile()) = 1 and
|
||||
source != target
|
||||
}
|
||||
|
||||
predicate remoteFunAccess(File source, File target, FunctionCall fc) {
|
||||
fc.getFile() = source and
|
||||
fc.getTarget().getFile() = target and
|
||||
// Ignore functions with locations in multiple files
|
||||
strictcount(File f | f = fc.getTarget().getFile()) = 1 and
|
||||
source != target
|
||||
}
|
||||
|
||||
predicate candidateFilePair(File source, File target) {
|
||||
remoteVarAccess(source, target, _) or
|
||||
remoteFunAccess(source, target, _)
|
||||
}
|
||||
|
||||
predicate variableDependencyCount(File source, File target, int res) {
|
||||
candidateFilePair(source, target) and
|
||||
res = count(VariableAccess va | remoteVarAccess(source, target, va))
|
||||
}
|
||||
|
||||
predicate functionDependencyCount(File source, File target, int res) {
|
||||
candidateFilePair(source, target) and
|
||||
res = count(FunctionCall fc | remoteFunAccess(source, target, fc))
|
||||
}
|
||||
|
||||
predicate highDependencyCount(File source, File target, int res) {
|
||||
exists(int varCount, int funCount |
|
||||
variableDependencyCount(source, target, varCount) and
|
||||
functionDependencyCount(source, target, funCount) and
|
||||
res = varCount + funCount and
|
||||
res > 20)
|
||||
}
|
||||
|
||||
from File a, File b, int ca, int cb
|
||||
where highDependencyCount(a, b, ca) and
|
||||
highDependencyCount(b, a, cb) and
|
||||
ca >= cb and
|
||||
a != b and
|
||||
not a instanceof HeaderFile and
|
||||
not b instanceof HeaderFile and
|
||||
b.getShortName().trim().length() > 0
|
||||
select a, "File is too closely tied to $@ (" + ca.toString() + " dependencies one way and " + cb.toString() + " the other).",
|
||||
b, b.getBaseName()
|
||||
@@ -0,0 +1,17 @@
|
||||
// an include declaration just adds one source dependency, it does not automatically
|
||||
// add a dependency from this file to all the declarations in stdio.h
|
||||
#include <stdio.h>
|
||||
#include <myfile.h> // contains non-static global myfile_err
|
||||
|
||||
extern int myfile_err; // this external declaration adds a dependency on myfile.h
|
||||
|
||||
class C {
|
||||
public:
|
||||
C() {
|
||||
// one dependency for printf:
|
||||
printf("Hello world!");
|
||||
// one dependency for FILE type, and one for NULL macro:
|
||||
FILE fp = NULL;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
|
||||
|
||||
<overview>
|
||||
<p>This rule finds classes that depend on many other types. Depending on too many other classes makes a class vulnerable to changes
|
||||
and defects in its dependencies, and is also a sign that the class lacks cohesion (i.e. a single purpose). These classes
|
||||
could be refactored into smaller, more cohesive classes.</p>
|
||||
|
||||
</overview>
|
||||
<recommendation>
|
||||
<p>These classes could probably be refactored into smaller classes with fewer dependencies.</p>
|
||||
|
||||
</recommendation>
|
||||
<example>
|
||||
<sample src="ClassesWithManyDependencies.cpp" />
|
||||
|
||||
|
||||
</example>
|
||||
<references>
|
||||
|
||||
<li>W. Stevens, G. Myers, L. Constantine, <em>Structured Design</em>, IBM Systems Journal, 13 (2), 115-139, 1974.</li>
|
||||
<li>
|
||||
Microsoft Patterns & Practices Team. <a href="http://msdn.microsoft.com/en-us/library/ee658117.aspx">Architectural Patterns and Styles</a> <em>Microsoft Application Architecture Guide, 2nd Edition.</em> Microsoft Press, 2009.
|
||||
</li>
|
||||
<li>
|
||||
<a href="en.wikipedia.org/wiki/Code_refactoring">Wikipedia: Code refactoring</a>
|
||||
</li>
|
||||
|
||||
|
||||
</references>
|
||||
</qhelp>
|
||||
@@ -0,0 +1,20 @@
|
||||
/**
|
||||
* @name Classes with too many source dependencies
|
||||
* @description Finds classes that depend on many other types; they could probably be refactored into smaller classes with fewer dependencies.
|
||||
* @kind problem
|
||||
* @id cpp/architecture/classes-with-many-dependencies
|
||||
* @problem.severity recommendation
|
||||
* @workingset jhotdraw
|
||||
* @result succeed 20
|
||||
* @result_ondemand succeed 20
|
||||
* @tags maintainability
|
||||
* statistical
|
||||
* non-attributable
|
||||
*/
|
||||
import cpp
|
||||
|
||||
from Class t, int n
|
||||
where t.fromSource() and
|
||||
n = t.getMetrics().getEfferentSourceCoupling() and
|
||||
n > 10
|
||||
select t as Class, "This class has too many dependencies (" + n.toString() + ")"
|
||||
@@ -0,0 +1,20 @@
|
||||
//This struct contains 30 fields.
|
||||
struct MyParticle {
|
||||
bool isActive;
|
||||
int priority;
|
||||
|
||||
float x, y, z;
|
||||
float dx, dy, dz;
|
||||
float ddx, ddy, ddz;
|
||||
bool isCollider;
|
||||
|
||||
int age, maxAge;
|
||||
float size1, size2;
|
||||
|
||||
bool hasColor;
|
||||
unsigned char r1, g1, b1, a1;
|
||||
unsigned char r2, g2, b2, a2;
|
||||
|
||||
class texture *tex;
|
||||
float u1, v1, u2, v2;
|
||||
};
|
||||
@@ -0,0 +1,36 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
|
||||
|
||||
<overview>
|
||||
<p>This rule finds classes with more than 15 instance (i.e., non-<code>static</code>) fields. Library classes
|
||||
are not shown. Having too many fields in one class is a sign that the class lacks cohesion (i.e. lacks a single purpose).
|
||||
These classes can be split into smaller, more cohesive classes. Alternatively, the related fields can be grouped
|
||||
into <code>struct</code>s.</p>
|
||||
|
||||
</overview>
|
||||
<recommendation>
|
||||
<p>Classes with many fields may be hard to maintain. They could probably be refactored by breaking them down into smaller classes
|
||||
and using composition.</p>
|
||||
|
||||
</recommendation>
|
||||
<example>
|
||||
<sample src="ClassesWithManyFields.cpp" />
|
||||
|
||||
|
||||
</example>
|
||||
<references>
|
||||
|
||||
<li>W. Stevens, G. Myers, L. Constantine, <em>Structured Design</em>. IBM Systems Journal, 13 (2), 115-139, 1974.</li>
|
||||
<li>
|
||||
Microsoft Patterns & Practices Team, <em>Microsoft Application Architecture Guide (2nd Edition), Chapter 3: Architectural Patterns and Styles.</em> Microsoft Press, 2009 (<a href="http://msdn.microsoft.com/en-us/library/ee658117.aspx">available online</a>).
|
||||
</li>
|
||||
<li>
|
||||
Wikipedia: <a href="en.wikipedia.org/wiki/Code_refactoring">Code refactoring</a>
|
||||
</li>
|
||||
|
||||
|
||||
</references>
|
||||
</qhelp>
|
||||
@@ -0,0 +1,113 @@
|
||||
/**
|
||||
* @name Classes with too many fields
|
||||
* @description Finds classes with many fields; they could probably be refactored by breaking them down into smaller classes, and using composition.
|
||||
* @kind problem
|
||||
* @problem.severity recommendation
|
||||
* @precision high
|
||||
* @id cpp/class-many-fields
|
||||
* @tags maintainability
|
||||
* statistical
|
||||
* non-attributable
|
||||
*/
|
||||
import cpp
|
||||
|
||||
string kindstr(Class c)
|
||||
{
|
||||
exists(int kind | usertypes(c, _, kind) |
|
||||
(kind = 1 and result = "Struct") or
|
||||
(kind = 2 and result = "Class") or
|
||||
(kind = 6 and result = "Template class")
|
||||
)
|
||||
}
|
||||
|
||||
predicate vdeInfo(VariableDeclarationEntry vde, Class c, File f, int line)
|
||||
{
|
||||
c = vde.getVariable().getDeclaringType() and
|
||||
f = vde.getLocation().getFile() and
|
||||
line = vde.getLocation().getStartLine()
|
||||
}
|
||||
|
||||
predicate previousVde(VariableDeclarationEntry previous, VariableDeclarationEntry vde)
|
||||
{
|
||||
exists(Class c, File f, int line | vdeInfo(vde, c, f, line) |
|
||||
vdeInfo(previous, c, f, line - 3) or
|
||||
vdeInfo(previous, c, f, line - 2) or
|
||||
vdeInfo(previous, c, f, line - 1) or
|
||||
(vdeInfo(previous, c, f, line) and exists(int prevCol, int vdeCol |
|
||||
prevCol = previous.getLocation().getStartColumn() and vdeCol = vde.getLocation().getStartColumn() |
|
||||
prevCol < vdeCol or (prevCol = vdeCol and previous.getName() < vde.getName())
|
||||
))
|
||||
)
|
||||
}
|
||||
|
||||
predicate masterVde(VariableDeclarationEntry master, VariableDeclarationEntry vde)
|
||||
{
|
||||
(not previousVde(_, vde) and master = vde) or
|
||||
exists(VariableDeclarationEntry previous | previousVde(previous, vde) and masterVde(master, previous))
|
||||
}
|
||||
|
||||
class VariableDeclarationGroup extends @var_decl {
|
||||
VariableDeclarationGroup() {
|
||||
not previousVde(_, this)
|
||||
}
|
||||
Class getClass() {
|
||||
vdeInfo(this, result, _, _)
|
||||
}
|
||||
|
||||
// pragma[noopt] since otherwise the two locationInfo relations get join-ordered
|
||||
// after each other
|
||||
pragma[noopt]
|
||||
predicate hasLocationInfo(string path, int startline, int startcol, int endline, int endcol) {
|
||||
exists(VariableDeclarationEntry last, Location lstart, Location lend |
|
||||
masterVde(this, last) and
|
||||
this instanceof VariableDeclarationGroup and
|
||||
not previousVde(last, _) and
|
||||
exists(VariableDeclarationEntry vde | vde=this and vde instanceof VariableDeclarationEntry and vde.getLocation() = lstart) and
|
||||
last.getLocation() = lend and
|
||||
lstart.hasLocationInfo(path, startline, startcol, _, _) and
|
||||
lend.hasLocationInfo(path, _, _, endline, endcol)
|
||||
)
|
||||
}
|
||||
|
||||
string toString() {
|
||||
if previousVde(this, _) then
|
||||
result = "group of "
|
||||
+ strictcount(string name
|
||||
| exists(VariableDeclarationEntry vde
|
||||
| masterVde(this, vde) and
|
||||
name = vde.getName()))
|
||||
+ " fields here"
|
||||
else
|
||||
result = "declaration of " + this.(VariableDeclarationEntry).getVariable().getName()
|
||||
}
|
||||
}
|
||||
|
||||
class ExtClass extends Class {
|
||||
predicate hasOneVariableGroup() {
|
||||
strictcount(VariableDeclarationGroup vdg | vdg.getClass() = this) = 1
|
||||
}
|
||||
|
||||
predicate hasLocationInfo(string path, int startline, int startcol, int endline, int endcol) {
|
||||
if hasOneVariableGroup() then
|
||||
exists(VariableDeclarationGroup vdg | vdg.getClass() = this | vdg.hasLocationInfo(path, startline, startcol, endline, endcol))
|
||||
else
|
||||
getLocation().hasLocationInfo(path, startline, startcol, endline, endcol)
|
||||
}
|
||||
}
|
||||
|
||||
from ExtClass c, int n, VariableDeclarationGroup vdg, string suffix
|
||||
where n = strictcount(string fieldName
|
||||
| exists(Field f
|
||||
| f.getDeclaringType() = c and
|
||||
fieldName = f.getName() and
|
||||
// IBOutlet's are a way of building GUIs
|
||||
// automatically out of ObjC properties.
|
||||
// We don't want to count those for the
|
||||
// purposes of this query.
|
||||
not (f.getType().getAnAttribute().hasName("iboutlet")))) and
|
||||
n > 15 and
|
||||
not c.isConstructedFrom(_) and
|
||||
c = vdg.getClass() and
|
||||
if c.hasOneVariableGroup() then suffix = "" else suffix = " - see $@"
|
||||
select c, kindstr(c) + " " + c.getName() + " has " + n + " fields, which is too many" + suffix + ".",
|
||||
vdg, vdg.toString()
|
||||
@@ -0,0 +1,22 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
|
||||
|
||||
<overview>
|
||||
<p>This rule finds functions that make too many calls. Having too many dependencies makes a function vulnerable to
|
||||
changes and defects in those dependencies, and is also a sign that the function lacks cohesion (i.e. lacks a single purpose).
|
||||
These functions can be split into smaller, more cohesive functions.</p>
|
||||
|
||||
</overview>
|
||||
<recommendation>
|
||||
<p>Splitting these functions would increase maintainability and readability.</p>
|
||||
|
||||
</recommendation>
|
||||
|
||||
|
||||
<references>
|
||||
<li>W. Stevens, G. Myers, L. Constantine, <em>Structured Design</em>, IBM Systems Journal, 13 (2), 115-139, 1974.
|
||||
|
||||
</li></references></qhelp>
|
||||
@@ -0,0 +1,18 @@
|
||||
/**
|
||||
* @name Complex functions
|
||||
* @description Finds functions which call too many other functions. Splitting these functions would increase maintainability and readability.
|
||||
* @kind problem
|
||||
* @id cpp/architecture/complex-functions
|
||||
* @problem.severity recommendation
|
||||
* @tags maintainability
|
||||
* statistical
|
||||
* non-attributable
|
||||
*/
|
||||
import cpp
|
||||
|
||||
from Function f, int n
|
||||
where f.fromSource() and
|
||||
n = f.getMetrics().getNumberOfCalls() and
|
||||
n > 99 and
|
||||
not f.isMultiplyDefined()
|
||||
select f as Function, "This function makes too many calls (" + n.toString() + ")"
|
||||
@@ -0,0 +1,30 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
|
||||
|
||||
<overview>
|
||||
<p>This rule computes cyclomatic complexity per function. Cyclomatic complexity is a measure of how complex a function
|
||||
is, and is derived from the number of branching statements in the function. Complex functions can be difficult to understand
|
||||
and maintain, and usually can be split into smaller, less complex functions.</p>
|
||||
|
||||
<p>The rule finds functions with high (e.g. >50) cyclomatic complexity.</p>
|
||||
|
||||
</overview>
|
||||
<recommendation>
|
||||
<p>With increasing cyclomatic complexity there need to be more test cases that are necessary to achieve a complete branch coverage when testing the function.
|
||||
Try to reduce the function's complexity by splitting it into several more cohesive functions.
|
||||
A particularly effective way of reducing complexity is to put code that handles a particular case in an large <code>if</code> or
|
||||
<code>switch</code> statement into a separate function with a descriptive name. This not only reduces the complexity of the function,
|
||||
but makes it considerably more readable as the function's descriptive name gives an idea of its purpose without the developer
|
||||
analyzing each line of code.
|
||||
</p>
|
||||
|
||||
</recommendation>
|
||||
|
||||
<references>
|
||||
<li>Wikipedia: <a href="https://en.wikipedia.org/wiki/Cyclomatic_complexity">Cyclomatic complexity</a>.</li>
|
||||
<li>T. McCabe. <em>A Complexity Measure</em>. ICSE '76: Proceedings of the 2nd international conference on Software engineering, 1976.</li>
|
||||
</references>
|
||||
</qhelp>
|
||||
@@ -0,0 +1,16 @@
|
||||
/**
|
||||
* @name Cyclomatic Complexity
|
||||
* @description Functions with high cyclomatic complexity. With increasing cyclomatic complexity there need to be more test cases that are necessary to achieve a complete branch coverage when testing this function.
|
||||
* @kind problem
|
||||
* @id cpp/architecture/cyclomatic-complexity
|
||||
* @problem.severity warning
|
||||
* @tags testability
|
||||
* statistical
|
||||
* non-attributable
|
||||
*/
|
||||
import cpp
|
||||
|
||||
from Function f, int complexity
|
||||
where complexity = f.getMetrics().getCyclomaticComplexity()
|
||||
and complexity > 250
|
||||
select f, "Function has high cyclomatic complexity: " + complexity.toString()
|
||||
@@ -0,0 +1,8 @@
|
||||
// this example has 15 parameters.
|
||||
void fillRect(int x, int y, int w, int h,
|
||||
int r1, int g1, int b1, int a1,
|
||||
int r2, int g2, int b2, int a2,
|
||||
gradient_type grad, unsigned int flags, bool border)
|
||||
{
|
||||
// ...
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
|
||||
|
||||
<overview>
|
||||
<p>This rule finds functions with many parameters. Passing too many parameters to a function is a sign that the function
|
||||
is not cohesive (i.e. lacks a single purpose). These functions could be split into smaller, more cohesive functions.</p>
|
||||
|
||||
</overview>
|
||||
<recommendation>
|
||||
<p>These functions could probably be refactored by wrapping related parameters into <code>struct</code>s.</p>
|
||||
|
||||
</recommendation>
|
||||
<example><sample src="FunctionsWithManyParameters.cpp" />
|
||||
|
||||
|
||||
|
||||
|
||||
</example>
|
||||
<references>
|
||||
<li>S. McConnell. <em>Code Complete, 2d ed</em>. Microsoft Press, 2004.</li>
|
||||
</references>
|
||||
</qhelp>
|
||||
@@ -0,0 +1,18 @@
|
||||
/**
|
||||
* @name Functions with too many parameters
|
||||
* @description Finds functions with many parameters;
|
||||
* they could probably be refactored by wrapping parameters into a struct.
|
||||
* @kind problem
|
||||
* @id cpp/architecture/functions-with-many-parameters
|
||||
* @problem.severity recommendation
|
||||
* @tags testability
|
||||
* statistical
|
||||
* non-attributable
|
||||
*/
|
||||
import cpp
|
||||
|
||||
from Function f
|
||||
where f.fromSource() and
|
||||
f.getMetrics().getNumberOfParameters() > 15
|
||||
select f, "This function has too many parameters ("
|
||||
+ f.getMetrics().getNumberOfParameters().toString() + ")"
|
||||
13
cpp/ql/src/Architecture/index.qhelp
Normal file
13
cpp/ql/src/Architecture/index.qhelp
Normal file
@@ -0,0 +1,13 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
|
||||
|
||||
<overview>
|
||||
<p>about Architecture</p>
|
||||
|
||||
<!--TOC-->
|
||||
|
||||
</overview>
|
||||
</qhelp>
|
||||
42
cpp/ql/src/Best Practices/BlockWithTooManyStatements.qhelp
Normal file
42
cpp/ql/src/Best Practices/BlockWithTooManyStatements.qhelp
Normal file
@@ -0,0 +1,42 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
|
||||
|
||||
<overview>
|
||||
<p>
|
||||
This rule finds blocks of code that have too many complex statements,
|
||||
such as branching statements (<code>if</code>, <code>switch</code>), and loops (<code>for</code>, <code>while</code>).
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Blocks with too many consecutive statements are candidates for refactoring.
|
||||
Only complex statements are counted here (eg. for, while, switch ...).
|
||||
The top-level logic will be clearer if each complex statement is extracted to a function.
|
||||
</p>
|
||||
|
||||
</overview>
|
||||
<recommendation>
|
||||
<p>It is often the case that each consecutive complex statement performs a dedicated separate task. It is a very common case that each complex statement is actually commented with a description of the task. Extract each such task into its own function for improved readability and to promote reuse.</p>
|
||||
|
||||
</recommendation>
|
||||
<references>
|
||||
|
||||
<li>
|
||||
M. Fowler. <em>Refactoring</em> Addison-Wesley, 1999.
|
||||
</li>
|
||||
<li>
|
||||
<a href="en.wikipedia.org/wiki/Code_refactoring">Wikipedia: Code refactoring</a>
|
||||
</li>
|
||||
<li>
|
||||
Microsoft Patterns & Practices Team. <a href="http://msdn.microsoft.com/en-us/library/ee658117.aspx">Architectural Patterns and Styles</a> <em>Microsoft Application Architecture Guide, 2nd Edition.</em> Microsoft Press, 2009.
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</references>
|
||||
</qhelp>
|
||||
27
cpp/ql/src/Best Practices/BlockWithTooManyStatements.ql
Normal file
27
cpp/ql/src/Best Practices/BlockWithTooManyStatements.ql
Normal file
@@ -0,0 +1,27 @@
|
||||
/**
|
||||
* @name Block with too many statements
|
||||
* @description Blocks with too many consecutive statements are candidates for refactoring. Only complex statements are counted here (eg. for, while, switch ...). The top-level logic will be clearer if each complex statement is extracted to a function.
|
||||
* @kind problem
|
||||
* @problem.severity recommendation
|
||||
* @precision high
|
||||
* @id cpp/complex-block
|
||||
* @tags testability
|
||||
* readability
|
||||
* maintainability
|
||||
*/
|
||||
import cpp
|
||||
|
||||
class ComplexStmt extends Stmt {
|
||||
ComplexStmt() {
|
||||
exists(Block body | body = this.(Loop ).getStmt() or
|
||||
body = this.(SwitchStmt).getStmt()
|
||||
| strictcount(body.getAStmt+()) > 6)
|
||||
and not exists (this.getGeneratingMacro())
|
||||
}
|
||||
}
|
||||
|
||||
from Block b, int n, ComplexStmt complexStmt
|
||||
where n = strictcount(ComplexStmt s | s = b.getAStmt()) and n > 3
|
||||
and complexStmt = b.getAStmt()
|
||||
select b, "Block with too many statements (" + n.toString() + " complex statements in the block). Complex statements at: $@", complexStmt, complexStmt.toString()
|
||||
|
||||
13
cpp/ql/src/Best Practices/ComplexCondition.cpp
Normal file
13
cpp/ql/src/Best Practices/ComplexCondition.cpp
Normal file
@@ -0,0 +1,13 @@
|
||||
//This condition is too complex and can be improved by using local variables
|
||||
bool accept_message =
|
||||
(message_type == CONNECT && _state != CONNECTED) ||
|
||||
(message_type == DISCONNECT && _state == CONNECTED) ||
|
||||
(message_type == DATA && _state == CONNECTED);
|
||||
|
||||
//This condition is acceptable, as all the logical operators are of the same type (&&)
|
||||
bool valid_connect =
|
||||
message_type == CONNECT &&
|
||||
_state != CONNECTED &&
|
||||
time_since_prev_connect > MAX_CONNECT_INTERVAL &&
|
||||
message_length <= MAX_PACKET_SIZE &&
|
||||
checksum(message) == get_checksum_field(message);
|
||||
43
cpp/ql/src/Best Practices/ComplexCondition.qhelp
Normal file
43
cpp/ql/src/Best Practices/ComplexCondition.qhelp
Normal file
@@ -0,0 +1,43 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
|
||||
|
||||
<overview>
|
||||
<p>
|
||||
This rule finds boolean expressions that have more than 5 consecutive operators that are not of the same type (e.g. alternating <code>&&</code> and <code>||</code> operators).
|
||||
Long chains of operators of the same type are not flagged as violations of this rule.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Complex boolean expressions are hard to read. Consequently, when modifying such expressions
|
||||
there is an increased risk of introducing defects.
|
||||
Naming intermediate results as local variables will make the logic easier to read and understand.
|
||||
</p>
|
||||
|
||||
</overview>
|
||||
<recommendation>
|
||||
<p>Use local variables or macros to represent intermediate values to make the condition easier to understand.</p>
|
||||
|
||||
</recommendation>
|
||||
<example><sample src="ComplexCondition.cpp" />
|
||||
|
||||
|
||||
</example>
|
||||
<references>
|
||||
|
||||
<li>
|
||||
<a href="http://www.cplusplus.com/doc/tutorial/operators/">Operators</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="http://geosoft.no/development/cppstyle.html#Conditionals">Conditionals</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</references>
|
||||
</qhelp>
|
||||
33
cpp/ql/src/Best Practices/ComplexCondition.ql
Normal file
33
cpp/ql/src/Best Practices/ComplexCondition.ql
Normal file
@@ -0,0 +1,33 @@
|
||||
/**
|
||||
* @name Complex condition
|
||||
* @description Boolean expressions that are too deeply nested are hard to read and understand. Consider naming intermediate results as local variables.
|
||||
* @kind problem
|
||||
* @problem.severity recommendation
|
||||
* @precision high
|
||||
* @id cpp/complex-condition
|
||||
* @tags testability
|
||||
* readability
|
||||
* maintainability
|
||||
* statistical
|
||||
* non-attributable
|
||||
*/
|
||||
import cpp
|
||||
|
||||
predicate logicalOp(string op) {
|
||||
op = "&&" or op = "||"
|
||||
}
|
||||
|
||||
predicate nontrivialLogicalOperator(Operation e) {
|
||||
exists(string op |
|
||||
op = e.getOperator() and
|
||||
logicalOp(op) and
|
||||
not (op = e.getParent().(Operation).getOperator())
|
||||
)
|
||||
and not e.isInMacroExpansion()
|
||||
}
|
||||
|
||||
from Expr e, int operators
|
||||
where not (e.getParent() instanceof Expr)
|
||||
and operators = count(Operation op | op.getParent*() = e and nontrivialLogicalOperator(op))
|
||||
and operators > 5
|
||||
select e, "Complex condition: too many logical operations in this expression."
|
||||
40
cpp/ql/src/Best Practices/Exceptions/AccidentalRethrow.qhelp
Normal file
40
cpp/ql/src/Best Practices/Exceptions/AccidentalRethrow.qhelp
Normal file
@@ -0,0 +1,40 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
|
||||
|
||||
<overview>
|
||||
<p>The C++ <code>throw</code> expression can take several forms. One form throws a new exception, whereas the
|
||||
other re-throws the current exception. In the latter case, if there is no current exception, then the program
|
||||
will be terminated. Presence of a re-throw outside of an exception handling context is often caused by the
|
||||
programmer not knowing what kind of exception to throw.</p>
|
||||
|
||||
</overview>
|
||||
<recommendation>
|
||||
<p>The <code>throw</code> expression should be changed to throw a particular type of exception.</p>
|
||||
|
||||
</recommendation>
|
||||
<example>
|
||||
<sample language="cpp">
|
||||
void bad() {
|
||||
/* ... */
|
||||
if(error_condition)
|
||||
throw;
|
||||
}
|
||||
|
||||
void good() {
|
||||
/* ... */
|
||||
if(error_condition)
|
||||
throw std::exception("Something went wrong.");
|
||||
}
|
||||
</sample>
|
||||
|
||||
</example>
|
||||
<references>
|
||||
|
||||
<li>Open Standards: <a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3337.pdf">Standard for Programming Language C++, draft n3337</a> [except.throw], clause 9, page 380.</li>
|
||||
|
||||
|
||||
</references>
|
||||
</qhelp>
|
||||
25
cpp/ql/src/Best Practices/Exceptions/AccidentalRethrow.ql
Normal file
25
cpp/ql/src/Best Practices/Exceptions/AccidentalRethrow.ql
Normal file
@@ -0,0 +1,25 @@
|
||||
/**
|
||||
* @name Accidental rethrow
|
||||
* @description When there is nothing to rethrow, attempting to rethrow an exception will terminate the program.
|
||||
* @kind problem
|
||||
* @problem.severity warning
|
||||
* @precision high
|
||||
* @id cpp/rethrow-no-exception
|
||||
* @tags reliability
|
||||
* correctness
|
||||
* exceptions
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
predicate isInCatch(Expr e) {
|
||||
e.getEnclosingStmt().getParent*() instanceof CatchBlock or // Lexically enclosing catch blocks will cause there to be a current exception,
|
||||
exists(Function f | f = e.getEnclosingFunction() |
|
||||
isInCatch(f.getACallToThisFunction()) or // as will dynamically enclosing catch blocks.
|
||||
f.getName().toLowerCase().matches("%exception%") // We assume that rethrows are intended when the function is called *exception*.
|
||||
)
|
||||
}
|
||||
|
||||
from ReThrowExpr e
|
||||
where not isInCatch(e)
|
||||
select e, "As there is no current exception, this rethrow expression will terminate the program."
|
||||
51
cpp/ql/src/Best Practices/Exceptions/CatchingByValue.qhelp
Normal file
51
cpp/ql/src/Best Practices/Exceptions/CatchingByValue.qhelp
Normal file
@@ -0,0 +1,51 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
|
||||
|
||||
<overview>
|
||||
<p>Catching an exception by value will create a new local variable which is a copy of the originally thrown object.
|
||||
Creating the copy is slightly wasteful, but not catastrophic. More worrisome is the fact that if the type being
|
||||
caught is a strict supertype of the originally thrown type, then the copy might not contain as much information
|
||||
as the original exception.</p>
|
||||
|
||||
</overview>
|
||||
<recommendation>
|
||||
<p>The parameter to the <code>catch</code> block should have its type changed from <code>T</code> to <code>T&</code>
|
||||
or <code>const T&</code>.</p>
|
||||
|
||||
</recommendation>
|
||||
<example>
|
||||
<sample language="cpp">
|
||||
void bad() {
|
||||
try {
|
||||
/* ... */
|
||||
}
|
||||
catch(std::exception a_copy_of_the_thrown_exception) {
|
||||
// Do something with a_copy_of_the_thrown_exception
|
||||
}
|
||||
}
|
||||
|
||||
void good() {
|
||||
try {
|
||||
/* ... */
|
||||
}
|
||||
catch(const std::exception& the_thrown_exception) {
|
||||
// Do something with the_thrown_exception
|
||||
}
|
||||
}
|
||||
</sample>
|
||||
|
||||
</example>
|
||||
<references>
|
||||
|
||||
<li>C++ FAQ: <a href="https://isocpp.org/wiki/faq/exceptions#what-to-throw">
|
||||
What should I throw?</a>, <a href="https://isocpp.org/wiki/faq/exceptions#what-to-catch">
|
||||
What should I catch?</a>.</li>
|
||||
<li>Wikibooks: <a href="http://en.wikibooks.org/wiki/C%2B%2B_Programming/Exception_Handling#Throwing_objects">
|
||||
Throwing objects</a>.</li>
|
||||
|
||||
|
||||
</references>
|
||||
</qhelp>
|
||||
17
cpp/ql/src/Best Practices/Exceptions/CatchingByValue.ql
Normal file
17
cpp/ql/src/Best Practices/Exceptions/CatchingByValue.ql
Normal file
@@ -0,0 +1,17 @@
|
||||
/**
|
||||
* @name Catching by value
|
||||
* @description Catching an exception by value will create a copy of the thrown exception, thereby potentially slicing the original exception object.
|
||||
* @kind problem
|
||||
* @problem.severity warning
|
||||
* @precision very-high
|
||||
* @id cpp/catch-by-value
|
||||
* @tags efficiency
|
||||
* correctness
|
||||
* exceptions
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
from CatchBlock cb, Class caughtType
|
||||
where caughtType = cb.getParameter().getType().getUnderlyingType().getUnspecifiedType()
|
||||
select cb, "This should catch a " + caughtType.getName() + " by (const) reference rather than by value."
|
||||
46
cpp/ql/src/Best Practices/Exceptions/LeakyCatch.qhelp
Normal file
46
cpp/ql/src/Best Practices/Exceptions/LeakyCatch.qhelp
Normal file
@@ -0,0 +1,46 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
|
||||
|
||||
<overview>
|
||||
<p>Modern C++ code and frameworks should not throw or catch pointers. Older frameworks, such as Microsoft's MFC,
|
||||
do throw and catch pointers. Said pointers will generally point to an exception object allocated on the heap,
|
||||
and therefore need to be freed when they are caught. Failure to free them will result in a memory leak.</p>
|
||||
|
||||
</overview>
|
||||
<recommendation>
|
||||
<p>The <code>catch</code> block should be augmented to delete the exception pointer.</p>
|
||||
|
||||
</recommendation>
|
||||
<example>
|
||||
<sample language="cpp">
|
||||
void bad() {
|
||||
try {
|
||||
/* ... */
|
||||
}
|
||||
catch(CException* e) {
|
||||
e->ReportError();
|
||||
}
|
||||
}
|
||||
|
||||
void good() {
|
||||
try {
|
||||
/* ... */
|
||||
}
|
||||
catch(CException* e) {
|
||||
e->ReportError();
|
||||
e->Delete();
|
||||
}
|
||||
}
|
||||
</sample>
|
||||
|
||||
</example>
|
||||
<references>
|
||||
|
||||
<li>MSDN Library for MFC: <a href="http://msdn.microsoft.com/en-us/library/0e5twxsh(v=vs.110).aspx">Exceptions: Catching and Deleting Exceptions</a>.</li>
|
||||
|
||||
|
||||
</references>
|
||||
</qhelp>
|
||||
48
cpp/ql/src/Best Practices/Exceptions/LeakyCatch.ql
Normal file
48
cpp/ql/src/Best Practices/Exceptions/LeakyCatch.ql
Normal file
@@ -0,0 +1,48 @@
|
||||
/**
|
||||
* @name Leaky catch
|
||||
* @description If an exception is allocated on the heap, then it should be deleted when caught.
|
||||
* @kind problem
|
||||
* @problem.severity warning
|
||||
* @precision high
|
||||
* @id cpp/catch-missing-free
|
||||
* @tags efficiency
|
||||
* correctness
|
||||
* exceptions
|
||||
* external/cwe/cwe-401
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
predicate doesRethrow(Function f) {
|
||||
exists(ReThrowExpr e | e.getEnclosingFunction() = f |
|
||||
not e.getEnclosingStmt().getParent*() instanceof CatchBlock
|
||||
)
|
||||
or
|
||||
exists(FunctionCall fc | fc.getEnclosingFunction() = f |
|
||||
doesRethrow(fc.getTarget())
|
||||
)
|
||||
}
|
||||
|
||||
predicate deletesException(Expr expr, Parameter exception) {
|
||||
expr.getEnclosingBlock().getParent*().(CatchBlock).getParameter() = exception and (
|
||||
exists(FunctionCall fc | fc = expr |
|
||||
// Calling a delete function on the exception will free it (MFC's CException has a Delete function).
|
||||
(fc.getQualifier() = exception.getAnAccess() and fc.getTarget().getName().toLowerCase().matches("%delete%")) or
|
||||
// Passing the exception to a function might free it.
|
||||
(fc.getAnArgument() = exception.getAnAccess()) or
|
||||
// Calling a function which rethrows the current exception might cause the exception to be freed.
|
||||
doesRethrow(fc.getTarget())
|
||||
) or
|
||||
// Calling operator delete on the exception will free it.
|
||||
exists(DeleteExpr d | d = expr |
|
||||
d.getExpr() = exception.getAnAccess()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
from CatchBlock cb
|
||||
where cb.getParameter().getType().getUnderlyingType() instanceof PointerType
|
||||
and not exists(Expr e | e.getEnclosingBlock().getParent*() = cb |
|
||||
deletesException(e, cb.getParameter())
|
||||
)
|
||||
select cb, "This catch block does not free the caught exception, thereby leaking memory."
|
||||
44
cpp/ql/src/Best Practices/Exceptions/ThrowingPointers.qhelp
Normal file
44
cpp/ql/src/Best Practices/Exceptions/ThrowingPointers.qhelp
Normal file
@@ -0,0 +1,44 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
|
||||
|
||||
<overview>
|
||||
<p>As C++ is not a garbage collected language, exceptions should not be dynamically allocated. Dynamically
|
||||
allocating an exception puts an onus on every <code>catch</code> site to ensure that the memory is freed.</p>
|
||||
|
||||
<p>As a special case, it is permissible to throw anything derived from Microsoft MFC's <code>CException</code>
|
||||
class as a pointer. This is for historical reasons; modern code and modern frameworks should not throw
|
||||
pointer values.</p>
|
||||
|
||||
</overview>
|
||||
<recommendation>
|
||||
<p>The <code>new</code> keyword immediately following the <code>throw</code> keyword should be removed. Any
|
||||
<code>catch</code> sites which previously caught the pointer should be changed to catch by reference or
|
||||
<code>const</code> reference.</p>
|
||||
|
||||
</recommendation>
|
||||
<example>
|
||||
<sample language="cpp">
|
||||
void bad() {
|
||||
throw new std::exception("This is how not to throw an exception");
|
||||
}
|
||||
|
||||
void good() {
|
||||
throw std::exception("This is how to throw an exception");
|
||||
}
|
||||
</sample>
|
||||
|
||||
</example>
|
||||
<references>
|
||||
|
||||
<li>C++ FAQ: <a href="https://isocpp.org/wiki/faq/exceptions#what-to-throw">
|
||||
What should I throw?</a>, <a href="https://isocpp.org/wiki/faq/exceptions#what-to-catch">
|
||||
What should I catch?</a>.</li>
|
||||
<li>Wikibooks: <a href="http://en.wikibooks.org/wiki/C%2B%2B_Programming/Exception_Handling#Throwing_objects">
|
||||
Throwing objects</a>.</li>
|
||||
|
||||
|
||||
</references>
|
||||
</qhelp>
|
||||
20
cpp/ql/src/Best Practices/Exceptions/ThrowingPointers.ql
Normal file
20
cpp/ql/src/Best Practices/Exceptions/ThrowingPointers.ql
Normal file
@@ -0,0 +1,20 @@
|
||||
/**
|
||||
* @name Throwing pointers
|
||||
* @description Exceptions should be objects rather than pointers to objects.
|
||||
* @kind problem
|
||||
* @problem.severity warning
|
||||
* @precision high
|
||||
* @id cpp/throwing-pointer
|
||||
* @tags efficiency
|
||||
* correctness
|
||||
* exceptions
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
from ThrowExpr throw, NewExpr new, Type t
|
||||
where new.getParent() = throw
|
||||
// Microsoft MFC's CException hierarchy should be thrown (and caught) as pointers
|
||||
and t = new.getAllocatedType()
|
||||
and not t.getUnderlyingType().(Class).getABaseClass*().hasName("CException")
|
||||
select throw, "This should throw a " + t.toString() + " rather than a pointer to one."
|
||||
@@ -0,0 +1,6 @@
|
||||
void f(int i) {
|
||||
for (int i = 0; i < 10; ++i) { //the loop variable hides the parameter to f()
|
||||
...
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
|
||||
|
||||
<overview>
|
||||
<p>This rule finds declarations of local variables that hide parameters of the surrounding function. Such declarations
|
||||
create variables with the same name but different scopes. This makes it hard to understand which variable is actually
|
||||
being used in an expression.</p>
|
||||
|
||||
</overview>
|
||||
<recommendation>
|
||||
<p>Consider changing the name of either the variable or the parameter to keep them distinct.</p>
|
||||
|
||||
</recommendation>
|
||||
<example>
|
||||
<sample src="DeclarationHidesParameter.cpp" />
|
||||
|
||||
|
||||
</example>
|
||||
<references>
|
||||
|
||||
<li>
|
||||
B. Stroustrup. <em>The C++ Programming Language Special Edition</em> p 82. Addison Wesley. 2000.
|
||||
</li>
|
||||
|
||||
|
||||
</references>
|
||||
</qhelp>
|
||||
@@ -0,0 +1,36 @@
|
||||
/**
|
||||
* @name Declaration hides parameter
|
||||
* @description A local variable hides a parameter. This may be confusing. Consider renaming one of them.
|
||||
* @kind problem
|
||||
* @problem.severity recommendation
|
||||
* @precision very-high
|
||||
* @id cpp/declaration-hides-parameter
|
||||
* @tags maintainability
|
||||
* readability
|
||||
*/
|
||||
import cpp
|
||||
|
||||
|
||||
/* Names of parameters in the implementation of a function.
|
||||
Notice that we need to exclude parameter names used in prototype
|
||||
declarations and only include the ones from the actual definition.
|
||||
We also exclude names from functions that have multiple definitions.
|
||||
This should not happen in a single application but since we
|
||||
have a system wide view it is likely to happen for instance for
|
||||
the main function. */
|
||||
ParameterDeclarationEntry functionParameterNames(Function f, string name) {
|
||||
exists(FunctionDeclarationEntry fe |
|
||||
result.getFunctionDeclarationEntry() = fe
|
||||
and fe.getFunction() = f
|
||||
and fe.getLocation() = f.getDefinitionLocation()
|
||||
and strictcount(f.getDefinitionLocation()) = 1
|
||||
and result.getName() = name
|
||||
)
|
||||
}
|
||||
|
||||
from Function f, LocalVariable lv, ParameterDeclarationEntry pde
|
||||
where f = lv.getFunction() and
|
||||
pde = functionParameterNames(f, lv.getName()) and
|
||||
not lv.isInMacroExpansion()
|
||||
select lv, "Local variable '"+ lv.getName() +"' hides a $@.",
|
||||
pde, "parameter of the same name"
|
||||
@@ -0,0 +1,12 @@
|
||||
void f() {
|
||||
int i = 10;
|
||||
|
||||
for (int i = 0; i < 10; i++) { //the loop counter hides the variable
|
||||
...
|
||||
}
|
||||
|
||||
{
|
||||
int i = 12; //this variable hides the variable in the outer block
|
||||
...
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
|
||||
|
||||
<overview>
|
||||
<p>This rule finds declarations of local variables that hide a local variable from a surrounding scope. Such declarations
|
||||
create variables with the same name but different scopes. This makes it difficult to know which variable is actually
|
||||
used in an expression.</p>
|
||||
|
||||
</overview>
|
||||
<recommendation>
|
||||
<p>Consider changing the name of either variable to keep them distinct.</p>
|
||||
|
||||
</recommendation>
|
||||
<example>
|
||||
<sample src="DeclarationHidesVariable.cpp" />
|
||||
|
||||
|
||||
</example>
|
||||
<references>
|
||||
|
||||
<li>
|
||||
B. Stroustrup. <em>The C++ Programming Language Special Edition</em> p 82. Addison Wesley. 2000.
|
||||
</li>
|
||||
|
||||
|
||||
</references>
|
||||
</qhelp>
|
||||
21
cpp/ql/src/Best Practices/Hiding/DeclarationHidesVariable.ql
Normal file
21
cpp/ql/src/Best Practices/Hiding/DeclarationHidesVariable.ql
Normal file
@@ -0,0 +1,21 @@
|
||||
/**
|
||||
* @name Declaration hides variable
|
||||
* @description A local variable hides another local variable from a surrounding scope. This may be confusing. Consider renaming one of the variables.
|
||||
* @kind problem
|
||||
* @problem.severity recommendation
|
||||
* @precision high
|
||||
* @id cpp/declaration-hides-variable
|
||||
* @tags maintainability
|
||||
* readability
|
||||
*/
|
||||
import cpp
|
||||
|
||||
import Best_Practices.Hiding.Shadowing
|
||||
|
||||
from LocalVariable lv1, LocalVariable lv2
|
||||
where shadowing(lv1, lv2) and
|
||||
not lv1.getParentScope().(Block).isInMacroExpansion() and
|
||||
not lv2.getParentScope().(Block).isInMacroExpansion()
|
||||
select lv1, "Variable " + lv1.getName() +
|
||||
" hides another variable of the same name (on $@).",
|
||||
lv2, "line " + lv2.getLocation().getStartLine().toString()
|
||||
@@ -0,0 +1,12 @@
|
||||
int i = 10;
|
||||
|
||||
void f() {
|
||||
for (int i = 0; i < 10; i++) { //the loop counter hides the global variable i
|
||||
...
|
||||
}
|
||||
|
||||
{
|
||||
int i = 12; //this variable hides the global variable i
|
||||
...
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
|
||||
|
||||
<overview>
|
||||
<p>This rule finds declarations of local variables or parameters that hide a global variable.
|
||||
Such declarations create variables with the same name but different scopes. This makes it
|
||||
difficult to know which variable is actually used in an expression.</p>
|
||||
|
||||
</overview>
|
||||
<recommendation>
|
||||
<p>Consider changing the name of either variable to keep them distinct.</p>
|
||||
|
||||
</recommendation>
|
||||
<example>
|
||||
<sample src="LocalVariableHidesGlobalVariable.cpp" />
|
||||
|
||||
</example>
|
||||
<references>
|
||||
|
||||
<li>
|
||||
B. Stroustrup. <em>The C++ Programming Language Special Edition</em> p 82. Addison Wesley. 2000.
|
||||
</li>
|
||||
|
||||
|
||||
</references>
|
||||
</qhelp>
|
||||
@@ -0,0 +1,36 @@
|
||||
/**
|
||||
* @name Local variable hides global variable
|
||||
* @description A local variable or parameter that hides a global variable of the same name. This may be confusing. Consider renaming one of the variables.
|
||||
* @kind problem
|
||||
* @problem.severity warning
|
||||
* @precision very-high
|
||||
* @id cpp/local-variable-hides-global-variable
|
||||
* @tags maintainability
|
||||
* readability
|
||||
*/
|
||||
import cpp
|
||||
|
||||
class LocalVariableOrParameter extends VariableDeclarationEntry {
|
||||
LocalVariableOrParameter() {
|
||||
this.getVariable() instanceof LocalScopeVariable and
|
||||
(
|
||||
// we only need to report parameters hiding globals when the clash is with the parameter
|
||||
// name as used in the function definition. The parameter name used in any other function
|
||||
// declaration is harmless.
|
||||
this instanceof ParameterDeclarationEntry
|
||||
implies
|
||||
exists(this.(ParameterDeclarationEntry).getFunctionDeclarationEntry().getBlock())
|
||||
)
|
||||
}
|
||||
|
||||
string type() {
|
||||
if this.getVariable() instanceof Parameter
|
||||
then result = "Parameter "
|
||||
else result = "Local variable "
|
||||
}
|
||||
}
|
||||
|
||||
from LocalVariableOrParameter lv, GlobalVariable gv
|
||||
where lv.getName() = gv.getName() and
|
||||
lv.getFile() = gv.getFile()
|
||||
select lv, lv.type() + gv.getName() + " hides $@ with the same name.", gv, "a global variable"
|
||||
33
cpp/ql/src/Best Practices/Hiding/Shadowing.qll
Normal file
33
cpp/ql/src/Best Practices/Hiding/Shadowing.qll
Normal file
@@ -0,0 +1,33 @@
|
||||
import cpp
|
||||
|
||||
predicate ancestorScope(Element b1, Element b2) {
|
||||
b1.getParentScope+() = b2
|
||||
}
|
||||
|
||||
pragma[noopt]
|
||||
predicate localVariablesSameNameInNestedScopes(LocalVariable lv1, LocalVariable lv2) {
|
||||
exists(Element b1, Element b2
|
||||
| b1 = lv1.getParentScope() and
|
||||
not b1 instanceof Namespace and
|
||||
lv1 instanceof LocalVariable and
|
||||
ancestorScope(b1, b2) and
|
||||
not b2 instanceof Namespace and
|
||||
b2 = lv2.getParentScope() and
|
||||
lv2 instanceof LocalVariable and
|
||||
lv1.getName() = lv2.getName())
|
||||
}
|
||||
|
||||
predicate shadowing(LocalVariable lv1, LocalVariable lv2) {
|
||||
localVariablesSameNameInNestedScopes(lv1, lv2) and
|
||||
exists(Location l1, Location l2 |
|
||||
l1 = lv1.getLocation() and
|
||||
l2 = lv2.getLocation() and
|
||||
(
|
||||
// variables declared later in parent scope are not shadowed
|
||||
l2.getEndLine() < l1.getStartLine()
|
||||
or (l2.getEndLine() = l1.getStartLine() and
|
||||
l2.getEndColumn() <= l1.getStartColumn())
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
9
cpp/ql/src/Best Practices/Likely Errors/EmptyBlock.cpp
Normal file
9
cpp/ql/src/Best Practices/Likely Errors/EmptyBlock.cpp
Normal file
@@ -0,0 +1,9 @@
|
||||
void f(int i) {
|
||||
if (i == 10); //empty then block
|
||||
... //won't be part of the if statement
|
||||
|
||||
if (i == 12) {
|
||||
...
|
||||
} else { //empty else block, most likely a mistake
|
||||
}
|
||||
}
|
||||
26
cpp/ql/src/Best Practices/Likely Errors/EmptyBlock.qhelp
Normal file
26
cpp/ql/src/Best Practices/Likely Errors/EmptyBlock.qhelp
Normal file
@@ -0,0 +1,26 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
|
||||
|
||||
<overview>
|
||||
<p>This rule finds empty blocks that occur as a branch of a conditional or as a loop body.
|
||||
This may indicate badly maintained code or a defect due to an unhandled case. It is common to find commented-out code in the empty body. Commented-out code is discouraged and is a source of defects and maintainability issues.</p>
|
||||
|
||||
</overview>
|
||||
<recommendation>
|
||||
<p>If the conditional or loop is useless, remove it.</p>
|
||||
<p>If only the else-branch of an <code>if</code> statement is empty, omit it. If the then-branch is empty, invert the sense of the condition.</p>
|
||||
|
||||
</recommendation>
|
||||
<example>
|
||||
<sample src="EmptyBlock.cpp" />
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</example>
|
||||
</qhelp>
|
||||
84
cpp/ql/src/Best Practices/Likely Errors/EmptyBlock.ql
Normal file
84
cpp/ql/src/Best Practices/Likely Errors/EmptyBlock.ql
Normal file
@@ -0,0 +1,84 @@
|
||||
/**
|
||||
* @name Empty branch of conditional
|
||||
* @description An empty block after a conditional can be a sign of an omission
|
||||
* and can decrease maintainability of the code. Such blocks
|
||||
* should contain an explanatory comment to aid future
|
||||
* maintainers.
|
||||
* @kind problem
|
||||
* @problem.severity recommendation
|
||||
* @precision very-high
|
||||
* @id cpp/empty-block
|
||||
* @tags reliability
|
||||
* readability
|
||||
*/
|
||||
import cpp
|
||||
|
||||
predicate emptyBlock(ControlStructure s, Block b) {
|
||||
b = s.getAChild() and
|
||||
not exists(b.getAChild()) and
|
||||
not b.isInMacroExpansion() and
|
||||
not s instanceof Loop
|
||||
}
|
||||
|
||||
class AffectedFile extends File {
|
||||
AffectedFile() {
|
||||
exists(Block b |
|
||||
emptyBlock(_, b) and
|
||||
this = b.getFile()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class BlockOrNonChild extends Element {
|
||||
BlockOrNonChild() {
|
||||
( this instanceof Block
|
||||
or
|
||||
this instanceof Comment
|
||||
or
|
||||
this instanceof PreprocessorDirective
|
||||
or
|
||||
this instanceof MacroInvocation
|
||||
) and
|
||||
this.getFile() instanceof AffectedFile
|
||||
}
|
||||
|
||||
private int getNonContiguousStartRankIn(AffectedFile file) {
|
||||
// When using `rank` with `order by`, the ranks may not be contiguous.
|
||||
this = rank[result](BlockOrNonChild boc, int startLine, int startCol |
|
||||
boc.getLocation()
|
||||
.hasLocationInfo(file.getAbsolutePath(), startLine, startCol, _, _)
|
||||
| boc
|
||||
order by startLine, startCol
|
||||
)
|
||||
}
|
||||
|
||||
int getStartRankIn(AffectedFile file) {
|
||||
this.getNonContiguousStartRankIn(file) = rank[result](int rnk |
|
||||
exists(BlockOrNonChild boc | boc.getNonContiguousStartRankIn(file) = rnk)
|
||||
)
|
||||
}
|
||||
|
||||
int getNonContiguousEndRankIn(AffectedFile file) {
|
||||
this = rank[result](BlockOrNonChild boc, int endLine, int endCol |
|
||||
boc.getLocation()
|
||||
.hasLocationInfo(file.getAbsolutePath(), _, _, endLine, endCol)
|
||||
| boc
|
||||
order by endLine, endCol
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
predicate emptyBlockContainsNonchild(Block b) {
|
||||
emptyBlock(_, b) and
|
||||
exists(BlockOrNonChild c, AffectedFile file |
|
||||
c.(BlockOrNonChild).getStartRankIn(file) =
|
||||
1 + b.(BlockOrNonChild).getStartRankIn(file) and
|
||||
c.(BlockOrNonChild).getNonContiguousEndRankIn(file) <
|
||||
b.(BlockOrNonChild).getNonContiguousEndRankIn(file)
|
||||
)
|
||||
}
|
||||
|
||||
from ControlStructure s, Block eb
|
||||
where emptyBlock(s, eb)
|
||||
and not emptyBlockContainsNonchild(eb)
|
||||
select eb, "Empty block without comment"
|
||||
@@ -0,0 +1,24 @@
|
||||
int find(int start, char *str, char goal)
|
||||
{
|
||||
int len = strlen(str);
|
||||
//Potential buffer overflow
|
||||
for (int i = start; str[i] != 0 && i < len; i++) {
|
||||
if (str[i] == goal)
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int findRangeCheck(int start, char *str, char goal)
|
||||
{
|
||||
int len = strlen(str);
|
||||
//Range check protects against buffer overflow
|
||||
for (int i = start; i < len && str[i] != 0 ; i++) {
|
||||
if (str[i] == goal)
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
<overview>
|
||||
<p>The program contains an and-expression where the array access is defined before the range check. Consequently the array is accessed without any bounds checking. The range check does not protect the program from segmentation faults caused by attempts to read beyond the end of a buffer.</p>
|
||||
|
||||
</overview>
|
||||
<recommendation>
|
||||
<p>Update the and-expression so that the range check precedes the array offset. This will ensure that the bounds are checked before the array is accessed.</p>
|
||||
|
||||
</recommendation>
|
||||
<example>
|
||||
<p>The <code>find</code> function can read past the end of the buffer pointed to by <code>str</code> if <code>start</code> is longer than or equal to the length of the buffer (or longer than <code>len</code>, depending on the contents of the buffer).</p>
|
||||
|
||||
<sample src="OffsetUseBeforeRangeCheck.c" />
|
||||
<p>Update the and-expression so that the range check precedes the array offset (for example, the <code>findRangeCheck</code> function).</p>
|
||||
|
||||
</example>
|
||||
<references>
|
||||
|
||||
<li>cplusplus.com: <a href="http://www.cplusplus.com/reference/array/array/">
|
||||
C++: array</a>.</li>
|
||||
<li>Wikipedia: <a href="http://en.wikipedia.org/wiki/Bounds_checking">
|
||||
Bounds checking</a>.</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</references>
|
||||
</qhelp>
|
||||
@@ -0,0 +1,22 @@
|
||||
/**
|
||||
* @name Array offset used before range check
|
||||
* @description Accessing an array offset before checking the range means that
|
||||
* the program may attempt to read beyond the end of a buffer
|
||||
* @kind problem
|
||||
* @id cpp/offset-use-before-range-check
|
||||
* @problem.severity warning
|
||||
* @tags reliability
|
||||
* security
|
||||
* external/cwe/cwe-120
|
||||
* external/cwe/cwe-125
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
from Variable v, LogicalAndExpr andexpr, ArrayExpr access, LTExpr rangecheck
|
||||
where access.getArrayOffset() = v.getAnAccess()
|
||||
and andexpr.getLeftOperand().getAChild() = access
|
||||
and andexpr.getRightOperand() = rangecheck
|
||||
and rangecheck.getLeftOperand() = v.getAnAccess()
|
||||
and not access.isInMacroExpansion()
|
||||
select access, "This use of offset '" + v.getName() + "' should follow the $@.", rangecheck, "range check"
|
||||
43
cpp/ql/src/Best Practices/Likely Errors/Slicing.cpp
Normal file
43
cpp/ql/src/Best Practices/Likely Errors/Slicing.cpp
Normal file
@@ -0,0 +1,43 @@
|
||||
static int idctr = 0;
|
||||
//Basic connection with id
|
||||
class Connection {
|
||||
public:
|
||||
int connId;
|
||||
virtual void print_info() {
|
||||
cout << "id: " << connId << "\n";
|
||||
}
|
||||
Connection() {
|
||||
connId = idctr++;
|
||||
}
|
||||
};
|
||||
|
||||
//Adds counters, and an overriding print_info
|
||||
class MeteredConnection : public Connection {
|
||||
public:
|
||||
int txCtr;
|
||||
int rxCtr;
|
||||
MeteredConnection() {
|
||||
txCtr = 0;
|
||||
rxCtr = 0;
|
||||
}
|
||||
virtual void print_info() {
|
||||
cout << "id: " << connId << "\n" << "tx/rx: " << txCtr << "/" << rxCtr << "\n";
|
||||
}
|
||||
};
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
Connection conn;
|
||||
MeteredConnection m_conn;
|
||||
|
||||
Connection curr_conn = conn;
|
||||
curr_conn.print_info();
|
||||
curr_conn = m_conn; //Wrong: Derived MetricConnection assigned to Connection
|
||||
//variable, will slice off the counters and the overriding print_info
|
||||
curr_conn.print_info(); //Will not print the counters.
|
||||
|
||||
Connection* curr_pconn = &conn;
|
||||
curr_pconn->print_info();
|
||||
curr_pconn = &m_conn; //Correct: Pointer assigned to address of the MetricConnection.
|
||||
//Counters and virtual functions remain intact.
|
||||
curr_pconn->print_info(); //Will call the correct method MeteredConnection::print_info
|
||||
}
|
||||
31
cpp/ql/src/Best Practices/Likely Errors/Slicing.qhelp
Normal file
31
cpp/ql/src/Best Practices/Likely Errors/Slicing.qhelp
Normal file
@@ -0,0 +1,31 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
|
||||
|
||||
<overview>
|
||||
<p>This query finds assignments of a non-reference instance of a derived type to a variable of the base type where the derived type has more fields than the base.
|
||||
These assignments slice off all the fields added by the derived type, and can cause unexpected state when accessed as the derived type.</p>
|
||||
|
||||
</overview>
|
||||
<recommendation>
|
||||
<p>Change the type of the variable at the left-hand side of the assignment to the subclass.</p>
|
||||
|
||||
</recommendation>
|
||||
<example>
|
||||
<sample src="Slicing.cpp" />
|
||||
|
||||
</example>
|
||||
<references>
|
||||
|
||||
<li>
|
||||
Wikipedia: <a href="http://en.wikipedia.org/wiki/Object_slicing">Object slicing</a>.
|
||||
</li>
|
||||
<li>
|
||||
DevX.com: <a href="http://www.devx.com/tips/Tip/14570">Slicing in C++</a>.
|
||||
</li>
|
||||
|
||||
|
||||
</references>
|
||||
</qhelp>
|
||||
24
cpp/ql/src/Best Practices/Likely Errors/Slicing.ql
Normal file
24
cpp/ql/src/Best Practices/Likely Errors/Slicing.ql
Normal file
@@ -0,0 +1,24 @@
|
||||
/**
|
||||
* @name Slicing
|
||||
* @description Assigning a non-reference instance of a derived type to a variable of the base type slices off all members added by the derived class, and can cause an unexpected state.
|
||||
* @kind problem
|
||||
* @problem.severity warning
|
||||
* @precision high
|
||||
* @id cpp/slicing
|
||||
* @tags reliability
|
||||
* correctness
|
||||
* types
|
||||
*/
|
||||
import cpp
|
||||
|
||||
//see http://www.cs.ualberta.ca/~hoover/Courses/201/201-New-Notes/lectures/section/slice.htm
|
||||
//Does not find anything in rivers (unfortunately)
|
||||
from AssignExpr e, Class lhsType, Class rhsType
|
||||
where e.getLValue().getType() = lhsType
|
||||
and e.getRValue().getType() = rhsType
|
||||
and rhsType.getABaseClass+() = lhsType
|
||||
and exists(Declaration m | rhsType.getAMember() = m and
|
||||
not m.(VirtualFunction).isPure()) //add additional checks for concrete members in in-between supertypes
|
||||
select e, "This assignment expression slices from type $@ to $@",
|
||||
rhsType, rhsType.getName(),
|
||||
lhsType, lhsType.getName()
|
||||
327
cpp/ql/src/Best Practices/Magic Constants/MagicConstants.qll
Normal file
327
cpp/ql/src/Best Practices/Magic Constants/MagicConstants.qll
Normal file
@@ -0,0 +1,327 @@
|
||||
import cpp
|
||||
import semmle.code.cpp.AutogeneratedFile
|
||||
|
||||
/*
|
||||
*
|
||||
* Counting nontrivial literal occurrences
|
||||
*
|
||||
*/
|
||||
|
||||
predicate trivialPositiveIntValue(string s) {
|
||||
s="0" or s="1" or s="2" or s="3" or s="4" or s="5" or s="6" or s="7" or s="8" or
|
||||
s="9" or s="10" or s="11" or s="12" or s="13" or s="14" or s="15" or s="16" or s="17" or
|
||||
s="18" or s="19" or s="20"
|
||||
|
||||
or
|
||||
|
||||
s="16" or s="24" or s="32" or s="64" or s="128" or s="256" or s="512" or s="1024" or
|
||||
s="2048" or s="4096" or s="16384" or s="32768" or s="65536" or
|
||||
s="1048576" or s="2147483648" or s="4294967296"
|
||||
|
||||
or
|
||||
|
||||
s="15" or s="31" or s="63" or s="127" or s="255" or s="511" or s="1023" or
|
||||
s="2047" or s="4095" or s="16383" or s="32767" or s="65535" or
|
||||
s="1048577" or s="2147483647" or s="4294967295"
|
||||
|
||||
or
|
||||
|
||||
s = "0x00000001" or s = "0x00000002" or s = "0x00000004" or s = "0x00000008" or s = "0x00000010" or s = "0x00000020" or s = "0x00000040" or s = "0x00000080" or s = "0x00000100" or s = "0x00000200" or s = "0x00000400" or s = "0x00000800" or s = "0x00001000" or s = "0x00002000" or s = "0x00004000" or s = "0x00008000" or s = "0x00010000" or s = "0x00020000" or s = "0x00040000" or s = "0x00080000" or s = "0x00100000" or s = "0x00200000" or s = "0x00400000" or s = "0x00800000" or s = "0x01000000" or s = "0x02000000" or s = "0x04000000" or s = "0x08000000" or s = "0x10000000" or s = "0x20000000" or s = "0x40000000" or s = "0x80000000" or
|
||||
s = "0x00000001" or s = "0x00000003" or s = "0x00000007" or s = "0x0000000f" or s = "0x0000001f" or s = "0x0000003f" or s = "0x0000007f" or s = "0x000000ff" or s = "0x000001ff" or s = "0x000003ff" or s = "0x000007ff" or s = "0x00000fff" or s = "0x00001fff" or s = "0x00003fff" or s = "0x00007fff" or s = "0x0000ffff" or s = "0x0001ffff" or s = "0x0003ffff" or s = "0x0007ffff" or s = "0x000fffff" or s = "0x001fffff" or s = "0x003fffff" or s = "0x007fffff" or s = "0x00ffffff" or s = "0x01ffffff" or s = "0x03ffffff" or s = "0x07ffffff" or s = "0x0fffffff" or s = "0x1fffffff" or s = "0x3fffffff" or s = "0x7fffffff" or s = "0xffffffff" or
|
||||
s = "0x0001" or s = "0x0002" or s = "0x0004" or s = "0x0008" or s = "0x0010" or s = "0x0020" or s = "0x0040" or s = "0x0080" or s = "0x0100" or s = "0x0200" or s = "0x0400" or s = "0x0800" or s = "0x1000" or s = "0x2000" or s = "0x4000" or s = "0x8000" or
|
||||
s = "0x0001" or s = "0x0003" or s = "0x0007" or s = "0x000f" or s = "0x001f" or s = "0x003f" or s = "0x007f" or s = "0x00ff" or s = "0x01ff" or s = "0x03ff" or s = "0x07ff" or s = "0x0fff" or s = "0x1fff" or s = "0x3fff" or s = "0x7fff" or s = "0xffff" or
|
||||
s = "0x01" or s = "0x02" or s = "0x04" or s = "0x08" or s = "0x10" or s = "0x20" or s = "0x40" or s = "0x80" or
|
||||
s = "0x01" or s = "0x03" or s = "0x07" or s = "0x0f" or s = "0x1f" or s = "0x3f" or s = "0x7f" or s = "0xff" or
|
||||
s = "0x00"
|
||||
|
||||
or
|
||||
|
||||
s = "10" or s = "100" or s = "1000" or s = "10000" or s = "100000" or s = "1000000" or s = "10000000" or s = "100000000" or s = "1000000000"
|
||||
}
|
||||
|
||||
predicate trivialIntValue(string s) {
|
||||
trivialPositiveIntValue(s) or
|
||||
exists(string pos | trivialPositiveIntValue(pos) and s = "-" + pos)
|
||||
}
|
||||
|
||||
predicate trivialLongValue(string s) {
|
||||
exists(string v | trivialIntValue(v) and s = v + "L")
|
||||
}
|
||||
|
||||
predicate intTrivial(Literal lit) {
|
||||
exists(string v | trivialIntValue(v) and v = lit.getValue())
|
||||
}
|
||||
|
||||
predicate longTrivial(Literal lit) {
|
||||
exists(string v | trivialLongValue(v) and v = lit.getValue())
|
||||
}
|
||||
|
||||
predicate powerOfTen(float f) {
|
||||
f = 10 or f = 100 or f = 1000 or f = 10000 or f = 100000 or f = 1000000 or f = 10000000 or f = 100000000 or f = 1000000000
|
||||
}
|
||||
|
||||
predicate floatTrivial(Literal lit) {
|
||||
lit.getType() instanceof FloatingPointType and
|
||||
exists(string value, float f |
|
||||
lit.getValue() = value and
|
||||
f = value.toFloat() and
|
||||
(f.abs() <= 20.0 or powerOfTen(f))
|
||||
)
|
||||
}
|
||||
|
||||
predicate charLiteral(Literal lit) {
|
||||
lit instanceof CharLiteral
|
||||
}
|
||||
|
||||
|
||||
Type literalType(Literal literal) {
|
||||
result = literal.getType()
|
||||
}
|
||||
|
||||
predicate stringType(DerivedType t) {
|
||||
t.getBaseType() instanceof CharType
|
||||
or
|
||||
exists(SpecifiedType constCharType |
|
||||
t.getBaseType() = constCharType and
|
||||
constCharType.isConst() and
|
||||
constCharType.getBaseType() instanceof CharType
|
||||
)
|
||||
}
|
||||
|
||||
predicate numberType(Type t) {
|
||||
t instanceof FloatingPointType or t instanceof IntegralType
|
||||
}
|
||||
|
||||
predicate stringLiteral(Literal literal) {
|
||||
literal instanceof StringLiteral
|
||||
}
|
||||
|
||||
predicate stringTrivial(Literal lit) {
|
||||
stringLiteral(lit) and
|
||||
lit.getValue().length() < 8
|
||||
}
|
||||
|
||||
predicate joiningStringTrivial(Literal lit) {
|
||||
// We want to be more lenient with string literals that are being
|
||||
// joined together, because replacing sentence fragments with named
|
||||
// constants could actually result in code that is harder to
|
||||
// understand (which is against the spirit of these queries).
|
||||
stringLiteral(lit) and
|
||||
exists(FunctionCall fc |
|
||||
(
|
||||
fc.getTarget().getName() = "operator+" or
|
||||
fc.getTarget().getName() = "operator<<"
|
||||
) and
|
||||
fc.getAnArgument().getAChild*() = lit
|
||||
) and
|
||||
lit.getValue().length() < 16
|
||||
}
|
||||
|
||||
predicate small(Literal lit) {
|
||||
lit.getValue().length() <= 1
|
||||
}
|
||||
|
||||
predicate trivial(Literal lit) {
|
||||
charLiteral(lit) or
|
||||
intTrivial(lit) or
|
||||
floatTrivial(lit) or
|
||||
stringTrivial(lit) or
|
||||
joiningStringTrivial(lit) or
|
||||
longTrivial(lit) or
|
||||
small(lit)
|
||||
}
|
||||
|
||||
private predicate isReferenceTo(Variable ref, Variable to) {
|
||||
exists(VariableAccess a |
|
||||
ref.getInitializer().getExpr().getConversion().(ReferenceToExpr).getExpr() = a and a.getTarget() = to)
|
||||
}
|
||||
|
||||
private predicate variableNotModifiedAfterInitializer(Variable v) {
|
||||
not exists(VariableAccess a | a.getTarget() = v and a.isModified()) and
|
||||
not exists(AddressOfExpr e | e.getAddressable() = v) and
|
||||
forall(Variable v2 |
|
||||
isReferenceTo(v2, v) |
|
||||
variableNotModifiedAfterInitializer(v2))
|
||||
}
|
||||
|
||||
predicate literalIsConstantInitializer(Literal literal, Variable f) {
|
||||
f.getInitializer().getExpr() = literal and
|
||||
variableNotModifiedAfterInitializer(f) and
|
||||
not f instanceof Parameter
|
||||
}
|
||||
|
||||
predicate literalIsEnumInitializer(Literal literal) {
|
||||
exists(EnumConstant ec | ec.getInitializer().getExpr() = literal)
|
||||
}
|
||||
|
||||
predicate literalInArrayInitializer(Literal literal) {
|
||||
exists(AggregateLiteral arrayInit |
|
||||
arrayInitializerChild(arrayInit, literal)
|
||||
)
|
||||
}
|
||||
|
||||
predicate arrayInitializerChild(AggregateLiteral parent, Expr e) {
|
||||
e = parent
|
||||
or
|
||||
exists (Expr mid | arrayInitializerChild(parent, mid) and e.getParent() = mid)
|
||||
}
|
||||
|
||||
// i.e. not a constant folded expression
|
||||
predicate literallyLiteral(Literal lit) {
|
||||
lit.getValueText().regexpMatch(".*\".*|\\s*+[-+]?+\\s*+(0[xob][0-9a-fA-F]|[0-9])[0-9a-fA-F,._]*+([eE][-+]?+[0-9,._]*+)?+\\s*+[a-zA-Z]*+\\s*+")
|
||||
}
|
||||
|
||||
predicate nonTrivialValue(string value, Literal literal) {
|
||||
value = literal.getValue() and
|
||||
not trivial(literal) and
|
||||
not literalIsConstantInitializer(literal, _) and
|
||||
not literalIsEnumInitializer(literal) and
|
||||
not literalInArrayInitializer(literal) and
|
||||
not literal.isAffectedByMacro() and
|
||||
literallyLiteral(literal)
|
||||
}
|
||||
|
||||
predicate valueOccurrenceCount(string value, int n) {
|
||||
n = strictcount(Location loc |
|
||||
exists(Literal lit |
|
||||
lit.getLocation() = loc |
|
||||
nonTrivialValue(value, lit)
|
||||
) and
|
||||
|
||||
// Exclude generated files (they do not have the same maintainability
|
||||
// concerns as ordinary source files)
|
||||
not loc.getFile() instanceof AutogeneratedFile
|
||||
) and
|
||||
n > 20
|
||||
}
|
||||
|
||||
predicate occurenceCount(Literal lit, string value, int n) {
|
||||
valueOccurrenceCount(value, n) and
|
||||
value = lit.getValue() and
|
||||
nonTrivialValue(_, lit)
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
*
|
||||
* Literals repeated frequently
|
||||
*
|
||||
*/
|
||||
|
||||
predicate check(Literal lit, string value, int n, File f) {
|
||||
// Check that the literal is nontrivial
|
||||
not trivial(lit) and
|
||||
// Check that it is repeated a number of times
|
||||
occurenceCount(lit, value, n) and n > 20 and
|
||||
f = lit.getFile() and
|
||||
|
||||
// Exclude generated files
|
||||
not f instanceof AutogeneratedFile
|
||||
}
|
||||
|
||||
predicate checkWithFileCount(string value, int overallCount, int fileCount, File f) {
|
||||
fileCount = strictcount(Location loc |
|
||||
exists(Literal lit |
|
||||
lit.getLocation() = loc |
|
||||
check(lit, value, overallCount, f)))
|
||||
}
|
||||
|
||||
predicate start(Literal lit, int startLine) {
|
||||
exists(Location l | l = lit.getLocation() and startLine = l.getStartLine())
|
||||
}
|
||||
|
||||
predicate firstOccurrence(Literal lit, string value, int n) {
|
||||
exists(File f, int fileCount |
|
||||
checkWithFileCount(value, n, fileCount, f) and
|
||||
fileCount < 100 and
|
||||
check(lit, value, n, f) and
|
||||
not exists(Literal lit2, int start1, int start2 |
|
||||
check(lit2, value, n, f) and
|
||||
start(lit, start1) and
|
||||
start(lit2, start2) and
|
||||
start2 < start1)
|
||||
)
|
||||
}
|
||||
|
||||
predicate magicConstant(Literal e, string msg) {
|
||||
exists(string value, int n | firstOccurrence(e, value, n)
|
||||
and msg = "Magic constant: literal '" + value + "' is repeated " + n.toString() + " times and should be encapsulated in a constant.")
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
* Literals where there is a defined constant with the same value
|
||||
*
|
||||
*/
|
||||
|
||||
predicate relevantVariable(Variable f, string value) {
|
||||
exists(Literal lit | not trivial(lit) and value = lit.getValue() and literalIsConstantInitializer(lit, f))
|
||||
}
|
||||
|
||||
predicate relevantCallable(Function f, string value) {
|
||||
exists(Literal lit | not trivial(lit) and value = lit.getValue() and lit.getEnclosingFunction() = f)
|
||||
}
|
||||
|
||||
|
||||
predicate isVisible(Variable field, Function fromCallable) {
|
||||
exists(string value |
|
||||
//public fields
|
||||
(
|
||||
relevantVariable(field, value) and
|
||||
field.(MemberVariable).isPublic() and
|
||||
relevantCallable(fromCallable, value)
|
||||
)
|
||||
or
|
||||
//in same class
|
||||
(
|
||||
relevantVariable(field, value) and
|
||||
exists(Type t |
|
||||
t = field.getDeclaringType() and
|
||||
t = fromCallable.getDeclaringType())
|
||||
and relevantCallable(fromCallable, value)
|
||||
)
|
||||
or
|
||||
//in subclass and not private
|
||||
(
|
||||
relevantVariable(field, value) and
|
||||
not field.(MemberVariable).isPrivate() and
|
||||
exists(Class sup, Class sub |
|
||||
sup = field.getDeclaringType() and
|
||||
sub.getABaseClass+() = sup and
|
||||
sub = fromCallable.getDeclaringType()
|
||||
) and
|
||||
relevantCallable(fromCallable, value)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
predicate canUseFieldInsteadOfLiteral(Variable constField, Literal magicLiteral) {
|
||||
exists(Literal initLiteral |
|
||||
literalIsConstantInitializer(initLiteral, constField) and
|
||||
not trivial(initLiteral) and
|
||||
not constField.getType().hasName("boolean") and
|
||||
exists(string value |
|
||||
value = initLiteral.getValue() and
|
||||
magicLiteral.getValue() = value
|
||||
)
|
||||
and constField.getType() = magicLiteral.getType()
|
||||
and not literalIsConstantInitializer(magicLiteral, _)
|
||||
and exists(Function c |
|
||||
c = magicLiteral.getEnclosingFunction() and
|
||||
(
|
||||
(
|
||||
constField.isTopLevel() and (not constField.isStatic() or constField.getFile() = c.getFile())
|
||||
)
|
||||
or
|
||||
isVisible(constField,c)
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
predicate literalInsteadOfConstant(Literal magicLiteral, string message, Variable constField, string linkText) {
|
||||
canUseFieldInsteadOfLiteral(constField, magicLiteral) and
|
||||
message =
|
||||
"Literal value '" + magicLiteral.getValue() + "' used instead of constant $@." and
|
||||
linkText = constField.getName()
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
void sanitize(Fields[] record) {
|
||||
//The number of fields here can be put in a const
|
||||
for (fieldCtr = 0; field < 7; field++) {
|
||||
sanitize(fields[fieldCtr]);
|
||||
}
|
||||
}
|
||||
|
||||
#define NUM_FIELDS 7
|
||||
|
||||
void process(Fields[] record) {
|
||||
//This avoids using a magic constant by using the macro instead
|
||||
for (fieldCtr = 0; field < NUM_FIELDS; field++) {
|
||||
process(fields[fieldCtr]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
|
||||
|
||||
<overview>
|
||||
<p>A <i>magic number</i> is a numeric literal (for example, <code>8080</code>,
|
||||
<code>2048</code>) that is used in the middle of a block of code without
|
||||
explanation. It is considered good practice to avoid magic numbers by assigning
|
||||
the numbers to named constants and using the named constants instead. The
|
||||
reasons for this are twofold:</p>
|
||||
|
||||
<ol>
|
||||
<li>A number in isolation can be inexplicable to later programmers, whereas a
|
||||
named constant (such as <code>MAX_GUESTS</code>) is more readily understood.
|
||||
</li>
|
||||
<li>Using the same named constant in many places, makes the code much easier to
|
||||
update if the requirements change (for example, one more guest is permitted).
|
||||
</li>
|
||||
</ol>
|
||||
|
||||
<p>This rule finds magic numbers for which there is no pre-existing named
|
||||
constant (for example, the line marked (4) below).</p>
|
||||
|
||||
</overview>
|
||||
<recommendation>
|
||||
<p>Consider creating a <code>const</code> or a macro to encapsulate the literal,
|
||||
then replace all the relevant occurrences in the code.</p>
|
||||
|
||||
</recommendation>
|
||||
<example><sample src="MagicConstantsNumbers.cpp" />
|
||||
|
||||
</example>
|
||||
<references>
|
||||
|
||||
<li>
|
||||
<a href="http://en.wikipedia.org/wiki/Magic_number_%28programming%29#Unnamed_numerical_constants">Magic number (Wikipedia)</a>
|
||||
</li>
|
||||
<li>
|
||||
Mats Henricson and Erik Nyquist, <i>Industrial Strength C++</i>, published by Prentice Hall PTR (1997).
|
||||
Chapter 5: Object Life Cycle, Rec 5.4 (<a href="http://mongers.org/industrial-c++/">PDF</a>).
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://www.securecoding.cert.org/confluence/display/c/DCL06-C.+Use+meaningful+symbolic+constants+to+represent+literal+values">DCL06-C. Use meaningful symbolic constants to represent literal values</a>
|
||||
</li>
|
||||
|
||||
|
||||
</references>
|
||||
</qhelp>
|
||||
@@ -0,0 +1,22 @@
|
||||
/**
|
||||
* @name Magic numbers
|
||||
* @description 'Magic constants' should be avoided: if a nontrivial constant is used repeatedly, it should be encapsulated into a const variable or macro definition.
|
||||
* @kind problem
|
||||
* @id cpp/magic-number
|
||||
* @problem.severity recommendation
|
||||
* @precision medium
|
||||
* @tags maintainability
|
||||
* statistical
|
||||
* non-attributable
|
||||
*/
|
||||
import cpp
|
||||
import MagicConstants
|
||||
|
||||
pragma[noopt]
|
||||
predicate selection(Element e, string msg) {
|
||||
magicConstant(e, msg) and exists(Literal l, Type t | l=e and t = l.getType() and numberType(t) and l instanceof Literal)
|
||||
}
|
||||
|
||||
from Literal e, string msg
|
||||
where selection(e, msg)
|
||||
select e, msg
|
||||
@@ -0,0 +1,49 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
|
||||
|
||||
<overview>
|
||||
<p>A <i>magic string</i> is a string literal (for example, <code>"SELECT"</code>,
|
||||
<code>"127.0.0.1"</code>) that is used in the middle of a block of code without
|
||||
explanation. It is considered good practice to avoid magic strings by assigning
|
||||
the strings to named constants and using the named constants instead. The
|
||||
reasons for this are twofold:</p>
|
||||
|
||||
<ol>
|
||||
<li>A string in isolation can be inexplicable to later programmers, whereas a
|
||||
named constant (such as <code>SMTP_HELO</code>) is more readily understood.
|
||||
</li>
|
||||
<li>Using the same named constant in many places, makes the code much easier to
|
||||
update if the requirements change (for example, a protocol is updated).
|
||||
</li>
|
||||
</ol>
|
||||
|
||||
<p>This rule finds magic strings for which there is no pre-existing named
|
||||
constant.</p>
|
||||
|
||||
|
||||
</overview>
|
||||
<recommendation>
|
||||
|
||||
<p>Consider replacing the magic string with a new named constant.</p>
|
||||
|
||||
</recommendation>
|
||||
<references>
|
||||
|
||||
|
||||
<li>
|
||||
<a href="http://en.wikipedia.org/wiki/Magic_string">Magic string (Wikipedia)</a>
|
||||
</li>
|
||||
<li>
|
||||
Mats Henricson and Erik Nyquist, <i>Industrial Strength C++</i>, published by Prentice Hall PTR (1997).
|
||||
Chapter 5: Object Life Cycle, Rec 5.4 (<a href="http://mongers.org/industrial-c++/">PDF</a>).
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://www.securecoding.cert.org/confluence/display/c/DCL06-C.+Use+meaningful+symbolic+constants+to+represent+literal+values">DCL06-C. Use meaningful symbolic constants to represent literal values</a>
|
||||
</li>
|
||||
|
||||
|
||||
</references>
|
||||
</qhelp>
|
||||
@@ -0,0 +1,22 @@
|
||||
/**
|
||||
* @name Magic strings
|
||||
* @description 'Magic constants' should be avoided: if a nontrivial constant is used repeatedly, it should be encapsulated into a const variable or macro definition.
|
||||
* @kind problem
|
||||
* @id cpp/magic-string
|
||||
* @problem.severity recommendation
|
||||
* @precision medium
|
||||
* @tags maintainability
|
||||
* statistical
|
||||
* non-attributable
|
||||
*/
|
||||
import cpp
|
||||
import MagicConstants
|
||||
|
||||
pragma[noopt]
|
||||
predicate selection(Element e, string msg) {
|
||||
magicConstant(e, msg) and stringLiteral(e)
|
||||
}
|
||||
|
||||
from Literal e, string msg
|
||||
where selection(e, msg)
|
||||
select e, msg
|
||||
@@ -0,0 +1,14 @@
|
||||
/**
|
||||
* @name Magic numbers: use defined constant
|
||||
* @description A numeric literal that matches the initializer of a constant variable was found. Consider using the constant variable instead of the numeric literal.
|
||||
* @kind problem
|
||||
* @id cpp/use-number-constant
|
||||
* @problem.severity recommendation
|
||||
*/
|
||||
import cpp
|
||||
import MagicConstants
|
||||
|
||||
from Literal magicLiteral, string message, Variable constant, string linkText
|
||||
where numberType(magicLiteral.getType())
|
||||
and literalInsteadOfConstant(magicLiteral, message, constant, linkText)
|
||||
select magicLiteral, message, constant, linkText
|
||||
@@ -0,0 +1,14 @@
|
||||
/**
|
||||
* @name Magic strings: use defined constant
|
||||
* @description A string literal that matches the initializer of a constant variable was found. Consider using the constant variable instead of the string literal.
|
||||
* @kind problem
|
||||
* @id cpp/use-string-constant
|
||||
* @problem.severity recommendation
|
||||
*/
|
||||
import cpp
|
||||
import MagicConstants
|
||||
|
||||
from Literal magicLiteral, string message, Variable constant, string linkText
|
||||
where stringLiteral(magicLiteral)
|
||||
and literalInsteadOfConstant(magicLiteral, message, constant, linkText)
|
||||
select magicLiteral, message, constant, linkText
|
||||
18
cpp/ql/src/Best Practices/NVI.ql
Normal file
18
cpp/ql/src/Best Practices/NVI.ql
Normal file
@@ -0,0 +1,18 @@
|
||||
/**
|
||||
* @name Public virtual method
|
||||
* @description When public methods can be overridden, base classes are unable
|
||||
* to enforce invariants that should hold for the whole hierarchy.
|
||||
* @kind problem
|
||||
* @id cpp/nvi
|
||||
* @problem.severity warning
|
||||
*/
|
||||
import cpp
|
||||
|
||||
//see http://www.gotw.ca/publications/mill18.htm
|
||||
|
||||
from MemberFunction f
|
||||
where f.hasSpecifier("public") and
|
||||
f.hasSpecifier("virtual") and
|
||||
f.getFile().fromSource() and
|
||||
not (f instanceof Destructor)
|
||||
select f, "Avoid having public virtual methods (NVI idiom)"
|
||||
22
cpp/ql/src/Best Practices/NVIHub.ql
Normal file
22
cpp/ql/src/Best Practices/NVIHub.ql
Normal file
@@ -0,0 +1,22 @@
|
||||
/**
|
||||
* @name Public virtual method in Hub Class
|
||||
* @description When public methods can be overridden, base classes are unable
|
||||
* to enforce invariants that should hold for the whole hierarchy.
|
||||
* This is especially problematic in classes with many
|
||||
* dependencies or dependents.
|
||||
* @kind table
|
||||
* @id cpp/nvi-hub
|
||||
*/
|
||||
import cpp
|
||||
|
||||
//see http://www.gotw.ca/publications/mill18.htm
|
||||
|
||||
from MemberFunction f, int hubIndex, Class fclass
|
||||
where f.hasSpecifier("public") and
|
||||
f.hasSpecifier("virtual") and
|
||||
f.getFile().fromSource() and
|
||||
not (f instanceof Destructor) and
|
||||
fclass = f.getDeclaringType() and
|
||||
hubIndex = fclass.getMetrics().getAfferentCoupling() * fclass.getMetrics().getEfferentCoupling() and
|
||||
hubIndex > 100
|
||||
select f.getFile(), f, "Avoid having public virtual methods (NVI idiom)"
|
||||
24
cpp/ql/src/Best Practices/RuleOfThree.qhelp
Normal file
24
cpp/ql/src/Best Practices/RuleOfThree.qhelp
Normal file
@@ -0,0 +1,24 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
|
||||
|
||||
<overview>
|
||||
<p>This query finds classes that define a destructor, a copy constructor, or a copy assignment operator, but not all three of them. The compiler generates default implementations for these functions, and since they deal with similar concerns it is likely that if the default implementation of one of them is not satisfactory, then neither are those of the others.</p>
|
||||
|
||||
<p>The query flags any such class with a warning, and also display the list of generated warnings in the result view.</p>
|
||||
|
||||
</overview>
|
||||
<section title="How to Address the Query Results">
|
||||
<p>Explicitly define the missing functions.</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</section>
|
||||
<references>
|
||||
<li><a href="http://en.wikipedia.org/wiki/Rule_of_three_(C%2B%2B_programming)">Wikipedia article</a>
|
||||
|
||||
</li></references></qhelp>
|
||||
26
cpp/ql/src/Best Practices/RuleOfThree.ql
Normal file
26
cpp/ql/src/Best Practices/RuleOfThree.ql
Normal file
@@ -0,0 +1,26 @@
|
||||
/**
|
||||
* @name Rule of three
|
||||
* @description Classes that have an explicit destructor, copy constructor, or
|
||||
* copy assignment operator may behave inconsistently if they do
|
||||
* not have all three.
|
||||
* @kind problem
|
||||
* @id cpp/rule-of-three
|
||||
* @problem.severity warning
|
||||
* @tags reliability
|
||||
*/
|
||||
import cpp
|
||||
|
||||
class BigThree extends MemberFunction {
|
||||
BigThree() {
|
||||
this instanceof Destructor
|
||||
or this instanceof CopyConstructor
|
||||
or this instanceof CopyAssignmentOperator
|
||||
}
|
||||
}
|
||||
|
||||
from Class c, BigThree b
|
||||
where b.getDeclaringType() = c and
|
||||
not (c.hasDestructor() and
|
||||
c.getAMemberFunction() instanceof CopyConstructor and
|
||||
c.getAMemberFunction() instanceof CopyAssignmentOperator)
|
||||
select c, "Class defines a destructor, copy constructor, or copy assignment operator, but not all three."
|
||||
26
cpp/ql/src/Best Practices/RuleOfTwo.cpp
Normal file
26
cpp/ql/src/Best Practices/RuleOfTwo.cpp
Normal file
@@ -0,0 +1,26 @@
|
||||
class C {
|
||||
private:
|
||||
Other* other = NULL;
|
||||
public:
|
||||
C(const C& copyFrom) {
|
||||
Other* newOther = new Other();
|
||||
*newOther = copyFrom.other;
|
||||
this->other = newOther;
|
||||
}
|
||||
|
||||
//No operator=, by default will just copy the pointer other, will not create a new object
|
||||
};
|
||||
|
||||
class D {
|
||||
Other* other = NULL;
|
||||
public:
|
||||
D& operator=(D& rhs) {
|
||||
Other* newOther = new Other();
|
||||
*newOther = rhs.other;
|
||||
this->other = newOther;
|
||||
return *this;
|
||||
}
|
||||
|
||||
//No copy constructor, will just copy the pointer other and not create a new object
|
||||
};
|
||||
|
||||
53
cpp/ql/src/Best Practices/RuleOfTwo.qhelp
Normal file
53
cpp/ql/src/Best Practices/RuleOfTwo.qhelp
Normal file
@@ -0,0 +1,53 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
|
||||
|
||||
<overview>
|
||||
<p>This rule finds classes that define a copy constructor or a copy assignment operator, but not both of them. The compiler generates default implementations for these functions, and since they deal with similar concerns it is likely that if the default implementation of one of them is not satisfactory, then neither is that of the other.</p>
|
||||
|
||||
<p>When a class defines a copy constructor or a copy assignment operator, but not both, this can cause
|
||||
unexpected behavior. The object initialization (that is, <code>Class c1 = c2</code>) may behave differently
|
||||
from object assignment (that is, <code>c1 = c2</code>).</p>
|
||||
</overview>
|
||||
|
||||
<recommendation>
|
||||
<p>First, consider whether the user-defined member needs to be explicitly
|
||||
defined at all. If no user-defined copy constructor is provided for a class,
|
||||
the compiler will always attempt to generate a public copy constructor that
|
||||
recursively invokes the copy constructor of each field. If the existing
|
||||
user-defined copy constructor does exactly the same, it is most likely
|
||||
beneficial to delete it. The compiler-generated version may be more efficient,
|
||||
and it does not need to be manually maintained as fields are added and deleted.
|
||||
</p>
|
||||
|
||||
<p>If the user-defined member <em>does</em> need to exist, the other
|
||||
corresponding member should be defined too. It can be defined as defaulted
|
||||
(using <code>= default</code>) if the compiler-generated implementation is
|
||||
acceptable, or it can be defined as deleted (using <code>= delete</code>) if it
|
||||
should never be called.
|
||||
</p>
|
||||
|
||||
</recommendation>
|
||||
<example><sample src="RuleOfTwo.cpp" />
|
||||
|
||||
|
||||
|
||||
</example>
|
||||
<references>
|
||||
|
||||
<li>
|
||||
<a href="http://en.wikipedia.org/wiki/Rule_of_three_(C%2B%2B_programming)">Rule of Three [Wikipedia]</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="http://www.artima.com/cppsource/bigtwo.html">The Law of The Big Two</a>
|
||||
</li>
|
||||
<li>
|
||||
cppreference.com: <a href="http://en.cppreference.com/w/cpp/language/copy_constructor">copy constructor</a>
|
||||
and <a href="http://en.cppreference.com/w/cpp/language/copy_assignment">copy assignment</a>
|
||||
</li>
|
||||
|
||||
|
||||
</references>
|
||||
</qhelp>
|
||||
65
cpp/ql/src/Best Practices/RuleOfTwo.ql
Normal file
65
cpp/ql/src/Best Practices/RuleOfTwo.ql
Normal file
@@ -0,0 +1,65 @@
|
||||
/**
|
||||
* @name Inconsistent definition of copy constructor and assignment ('Rule of Two')
|
||||
* @description Classes that have an explicit copy constructor or copy
|
||||
* assignment operator may behave inconsistently if they do
|
||||
* not have both.
|
||||
* @kind problem
|
||||
* @problem.severity warning
|
||||
* @precision high
|
||||
* @id cpp/rule-of-two
|
||||
* @tags reliability
|
||||
* readability
|
||||
* language-features
|
||||
*/
|
||||
import cpp
|
||||
|
||||
// This query enforces the Rule of Two, which is a conservative variation of
|
||||
// the more well-known Rule of Three.
|
||||
//
|
||||
// The Rule of Two is usually phrased informally, ignoring the distinction
|
||||
// between whether a member is missing because it's auto-generated (missing
|
||||
// from the source) or missing because it can't be called (missing from the
|
||||
// generated code).
|
||||
//
|
||||
// This query checks if one member is explicitly defined while the other is
|
||||
// auto-generated. This can lead to memory safety issues. It's a separate issue
|
||||
// whether one is callable while the other is not callable; that is an API
|
||||
// design question and carries has no safety risk.
|
||||
|
||||
predicate generatedCopyAssignment(CopyConstructor cc, string msg) {
|
||||
cc.getDeclaringType().hasImplicitCopyAssignmentOperator() and
|
||||
msg = "No matching copy assignment operator in class " +
|
||||
cc.getDeclaringType().getName() +
|
||||
". It is good practice to match a copy constructor with a " +
|
||||
"copy assignment operator."
|
||||
}
|
||||
|
||||
predicate generatedCopyConstructor(CopyAssignmentOperator ca, string msg) {
|
||||
ca.getDeclaringType().hasImplicitCopyConstructor() and
|
||||
msg = "No matching copy constructor in class " +
|
||||
ca.getDeclaringType().getName() +
|
||||
". It is good practice to match a copy assignment operator with a " +
|
||||
"copy constructor."
|
||||
}
|
||||
|
||||
from MemberFunction f, string msg
|
||||
where (generatedCopyAssignment(f, msg) or
|
||||
generatedCopyConstructor(f, msg))
|
||||
// Ignore template instantiations to prevent an explosion of alerts
|
||||
and not f.getDeclaringType().isConstructedFrom(_)
|
||||
// Ignore private members since a private constructor or assignment operator
|
||||
// is a common idiom that simulates suppressing the default-generated
|
||||
// members. It would be better to use C++11's "delete" facility or use
|
||||
// appropriate Boost helper classes, but it is too common to report as a
|
||||
// violation.
|
||||
and not f.isPrivate()
|
||||
// If it is truly user-defined then it must have a body. This leaves out
|
||||
// C++11 members that use `= delete` or `= default`.
|
||||
and exists(f.getBlock())
|
||||
// In rare cases, the extractor pretends that an auto-generated copy
|
||||
// constructor has a block that is one character long and is located on top
|
||||
// of the first character of the class name. Checking for
|
||||
// `isCompilerGenerated` will remove those results.
|
||||
and not f.isCompilerGenerated()
|
||||
and not f.isDeleted()
|
||||
select f, msg
|
||||
32
cpp/ql/src/Best Practices/SloppyGlobal.qhelp
Normal file
32
cpp/ql/src/Best Practices/SloppyGlobal.qhelp
Normal file
@@ -0,0 +1,32 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
|
||||
|
||||
<overview>
|
||||
<p>
|
||||
This rule finds global variables which have a name of length three characters or less. It is particularly important to use descriptive names for global variables. Use of a clear naming convention for global variables helps document their use, avoids pollution of the namespace and reduces the risk of shadowing with local variables.
|
||||
</p>
|
||||
|
||||
|
||||
</overview>
|
||||
<recommendation>
|
||||
<p>
|
||||
Review the purpose of the each global variable flagged by this rule and update each name to reflect the purpose of the variable.
|
||||
</p>
|
||||
|
||||
</recommendation>
|
||||
<references>
|
||||
|
||||
<li>
|
||||
Mats Henricson and Erik Nyquist, <i>Industrial Strength C++</i>, published by Prentice Hall PTR (1997).
|
||||
Chapter 1: Naming, Rec 1.1 (<a href="http://mongers.org/industrial-c++/">PDF</a>).
|
||||
</li>
|
||||
<li>
|
||||
<a href="http://www.learncpp.com/cpp-tutorial/42-global-variables/">Global variables</a>.
|
||||
</li>
|
||||
|
||||
|
||||
</references>
|
||||
</qhelp>
|
||||
15
cpp/ql/src/Best Practices/SloppyGlobal.ql
Normal file
15
cpp/ql/src/Best Practices/SloppyGlobal.ql
Normal file
@@ -0,0 +1,15 @@
|
||||
/**
|
||||
* @name Short global name
|
||||
* @description Global variables should have descriptive names, to help document their use, avoid namespace pollution and reduce the risk of shadowing with local variables.
|
||||
* @kind problem
|
||||
* @problem.severity warning
|
||||
* @precision high
|
||||
* @id cpp/short-global-name
|
||||
* @tags maintainability
|
||||
*/
|
||||
import cpp
|
||||
|
||||
from GlobalVariable gv
|
||||
where gv.getName().length() <= 3
|
||||
and not gv.isStatic()
|
||||
select gv, "Poor global variable name '" + gv.getName() + "'. Prefer longer, descriptive names for globals (eg. kMyGlobalConstant, not foo)."
|
||||
32
cpp/ql/src/Best Practices/SwitchLongCase.cpp
Normal file
32
cpp/ql/src/Best Practices/SwitchLongCase.cpp
Normal file
@@ -0,0 +1,32 @@
|
||||
//This switch statement has long case statements, and can become difficult to
|
||||
//read as the processing for each message type becomes more complex
|
||||
switch (message_type) {
|
||||
case CONNECT:
|
||||
_state = CONNECTING;
|
||||
int message_id = message_get_id(message);
|
||||
int source = connect_get_source(message);
|
||||
//More code here...
|
||||
send(connect_response);
|
||||
break;
|
||||
case DISCONNECT:
|
||||
_state = DISCONNECTING;
|
||||
int message_id = message_get_id(message);
|
||||
int source = disconnect_get_source(message);
|
||||
//More code here...
|
||||
send(disconnect_response);
|
||||
break;
|
||||
default:
|
||||
log("Invalid message, id : %d", message_get_id(message));
|
||||
}
|
||||
|
||||
//This is better, as each case is split out to a separate function
|
||||
switch (packet_type) {
|
||||
case STREAM:
|
||||
process_stream_packet(packet);
|
||||
break;
|
||||
case DATAGRAM:
|
||||
process_datagram_packet(packet);
|
||||
break;
|
||||
default:
|
||||
log("Invalid packet type: %d", packet_type);
|
||||
}
|
||||
32
cpp/ql/src/Best Practices/SwitchLongCase.qhelp
Normal file
32
cpp/ql/src/Best Practices/SwitchLongCase.qhelp
Normal file
@@ -0,0 +1,32 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
|
||||
|
||||
<overview>
|
||||
<p>
|
||||
This rule finds switch statements that have too much code in their cases.
|
||||
Long case statements often lead to large amounts of nesting, adding to the difficulty of understanding what the code actually does.
|
||||
Consider wrapping the code for each case in a function and just using the switch statement to invoke the appropriate function in each case.
|
||||
</p>
|
||||
|
||||
<p>The indicated switch statement has a case that is more than 30 lines long.</p>
|
||||
|
||||
</overview>
|
||||
<recommendation>
|
||||
<p>Consider creating a separate function for the code in the long case statement.</p>
|
||||
|
||||
</recommendation>
|
||||
<example><sample src="SwitchLongCase.cpp" />
|
||||
|
||||
</example>
|
||||
<references>
|
||||
|
||||
<li>
|
||||
<a href="http://www.learncpp.com/cpp-tutorial/53-switch-statements/">Switch statements</a>
|
||||
</li>
|
||||
|
||||
|
||||
</references>
|
||||
</qhelp>
|
||||
45
cpp/ql/src/Best Practices/SwitchLongCase.ql
Normal file
45
cpp/ql/src/Best Practices/SwitchLongCase.ql
Normal file
@@ -0,0 +1,45 @@
|
||||
/**
|
||||
* @name Long switch case
|
||||
* @description A switch statement with too much code in its cases can make the control flow hard to follow. Consider wrapping the code for each case in a function and just using the switch statement to invoke the appropriate function in each case.
|
||||
* @kind problem
|
||||
* @problem.severity recommendation
|
||||
* @precision high
|
||||
* @id cpp/long-switch
|
||||
* @tags maintainability
|
||||
* readability
|
||||
*/
|
||||
import cpp
|
||||
|
||||
predicate switchCaseStartLine(SwitchCase sc, int start) {
|
||||
sc.getLocation().getStartLine() = start
|
||||
}
|
||||
predicate switchStmtEndLine(SwitchStmt s, int start) {
|
||||
s.getLocation().getEndLine() = start
|
||||
}
|
||||
|
||||
predicate switchCaseLength(SwitchCase sc, int length) {
|
||||
exists(SwitchCase next, int l1, int l2 |
|
||||
next = sc.getNextSwitchCase() and
|
||||
switchCaseStartLine(next, l1) and
|
||||
switchCaseStartLine(sc, l2) and
|
||||
length = l1 - l2 - 1
|
||||
)
|
||||
or
|
||||
(
|
||||
not exists(sc.getNextSwitchCase()) and
|
||||
exists(int l1, int l2 |
|
||||
switchStmtEndLine(sc.getSwitchStmt(), l1) and
|
||||
switchCaseStartLine(sc, l2) and
|
||||
length = l1 - l2 - 1
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
predicate tooLong(SwitchCase sc) {
|
||||
exists(int n | switchCaseLength(sc, n) and n > 30)
|
||||
}
|
||||
|
||||
from SwitchStmt switch, SwitchCase sc, int lines
|
||||
where sc = switch.getASwitchCase() and tooLong(sc)
|
||||
and switchCaseLength(sc, lines)
|
||||
select switch, "Switch has at least one case that is too long: $@", sc, sc.getExpr().toString() + " (" + lines.toString() + " lines)"
|
||||
24
cpp/ql/src/Best Practices/Unused Entities/UnusedIncludes.ql
Normal file
24
cpp/ql/src/Best Practices/Unused Entities/UnusedIncludes.ql
Normal file
@@ -0,0 +1,24 @@
|
||||
/**
|
||||
* @name Unused include
|
||||
* @description Finds #include directives that are not needed because none of
|
||||
* the included elements are used.
|
||||
* @kind problem
|
||||
* @id cpp/unused-includes
|
||||
* @problem.severity warning
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
File sourceFile() {
|
||||
result instanceof CFile or
|
||||
result instanceof CppFile
|
||||
}
|
||||
|
||||
from Include include, File source, File unneeded
|
||||
where include.getFile() = source
|
||||
and source = sourceFile()
|
||||
and unneeded = include.getIncludedFile()
|
||||
and not unneeded.getAnIncludedFile*() = source.getMetrics().getAFileDependency()
|
||||
and unneeded.fromSource()
|
||||
and not unneeded.getBaseName().matches("%Debug.h")
|
||||
select include, "Redundant include, this file does not require $@.", unneeded, unneeded.getAbsolutePath()
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
int x = 0; //x is unused
|
||||
int y = 0;
|
||||
cout << y;
|
||||
}
|
||||
34
cpp/ql/src/Best Practices/Unused Entities/UnusedLocals.qhelp
Normal file
34
cpp/ql/src/Best Practices/Unused Entities/UnusedLocals.qhelp
Normal file
@@ -0,0 +1,34 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
|
||||
|
||||
<overview>
|
||||
<p>This rule finds local variables that are never accessed after declaration.
|
||||
Unused variables should be removed to increase readability and avoid misuse.</p>
|
||||
|
||||
</overview>
|
||||
<recommendation>
|
||||
<p>Removing these unused local variables will make code more readable.</p>
|
||||
|
||||
</recommendation>
|
||||
<example>
|
||||
<sample src="UnusedLocals.cpp" />
|
||||
|
||||
</example>
|
||||
<references>
|
||||
|
||||
<li>
|
||||
<a href="http://www.tutorialspoint.com/cplusplus/cpp_variable_scope.htm">Variable scope</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://wiki.sei.cmu.edu/confluence/display/c/MSC12-C.+Detect+and+remove+code+that+has+no+effect+or+is+never+executed">MSC12-C. Detect and remove code that has no effect</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</references>
|
||||
</qhelp>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user