JavaScript: Remove deprecated queries.

These queries have all been deprecated since 1.17 (released in July 2018). I think it's time to say goodbye.
This commit is contained in:
Max Schaefer
2019-10-21 14:32:58 +01:00
parent 016c95a69c
commit 55fb86d618
67 changed files with 18 additions and 1615 deletions

View File

@@ -42,3 +42,21 @@
## Changes to QL libraries
* `Expr.getDocumentation()` now handles chain assignments.
## Removal of deprecated queries
The following queries (deprecated since 1.17) are no longer available in the distribution:
* Builtin redefined (js/builtin-redefinition)
* Inefficient method definition (js/method-definition-in-constructor)
* Bad parity check (js/incomplete-parity-check)
* Potentially misspelled property or variable name (js/wrong-capitalization)
* Unknown JSDoc tag (js/jsdoc/unknown-tag-type)
* Invalid JSLint directive (js/jslint/invalid-directive)
* Malformed JSLint directive (js/jslint/malformed-directive)
* Use of HTML comments (js/html-comment)
* Multi-line string literal (js/multi-line-string)
* Octal literal (js/octal-literal)
* Reserved word used as variable name (js/use-of-reserved-word)
* Trailing comma in array or object expressions (js/trailing-comma-in-array-or-object)
* Call to parseInt without radix (js/parseint-without-radix)

View File

@@ -1,13 +0,0 @@
+ semmlecode-javascript-queries/Declarations/BuiltinRedefined.ql: /Maintainability/Declarations
+ semmlecode-javascript-queries/Declarations/InefficientMethodDefinition.ql: /Maintainability/Declarations
+ semmlecode-javascript-queries/Expressions/BadParityCheck.ql: /Correctness/Expressions
+ semmlecode-javascript-queries/Expressions/HapaxLegomenon.ql: /Correctness/Expressions
+ semmlecode-javascript-queries/JSDoc/UnknownTagType.ql: /Readability/JSDoc
+ semmlecode-javascript-queries/JSLint/InvalidJSLintDirective.ql: /Frameworks/JSLint
+ semmlecode-javascript-queries/JSLint/MalformedJSLintDirective.ql: /Frameworks/JSLint
+ semmlecode-javascript-queries/LanguageFeatures/HTMLComments.ql: /Maintainability/Language Features
+ semmlecode-javascript-queries/LanguageFeatures/MultilineStringLiteral.ql: /Readability/Language Features
+ semmlecode-javascript-queries/LanguageFeatures/ReservedWords.ql: /Maintainability/Language Features
+ semmlecode-javascript-queries/LanguageFeatures/TrailingComma.ql: /Correctness/Language Features
+ semmlecode-javascript-queries/LanguageFeatures/OctalLiteral.ql: /Maintainability/Language Features
+ semmlecode-javascript-queries/StandardLibrary/ParseIntRadix.ql: /Maintainability/Standard Library

View File

@@ -1,37 +0,0 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>
Builtin functions and objects defined in the JavaScript standard library can be shadowed or redefined in user code.
This is confusing and makes code hard to understand, so it should be avoided.
</p>
</overview>
<recommendation>
<p>
Refactor the code to avoid shadowing or redefinition. For example, if a local variable has the same name as a standard
library builtin, it should be renamed.
</p>
</recommendation>
<example>
<p>
In the following example, the user-defined function <code>eval</code> shadows the builtin function <code>eval</code>
defined in the standard library. It could be renamed <code>evaluate</code> to avoid confusion.
</p>
<sample src="examples/BuiltinRedefined.js" />
</example>
<references>
<li>Ecma International, <i>ECMAScript Language Definition</i>, 5.1 Edition, Section 15. ECMA, 2011.</li>
</references>
</qhelp>

View File

@@ -1,50 +0,0 @@
/**
* @name Builtin redefined
* @description Standard library functions can be redefined, but this should be avoided
* since it makes code hard to read and maintain.
* @kind problem
* @problem.severity recommendation
* @id js/builtin-redefinition
* @tags maintainability
* @precision medium
* @deprecated This query is prone to false positives. Deprecated since 1.17.
*/
import javascript
import Definitions
/**
* Holds if `id` is a redefinition of a standard library function that is considered
* acceptable since it merely introduces a local alias to the standard function of
* the same name.
*/
predicate acceptableRedefinition(Identifier id) {
// function(x, y, undefined) { ... }(23, 42)
id.getName() = "undefined" and
exists(ImmediatelyInvokedFunctionExpr iife |
id = iife.getParameter(iife.getInvocation().getNumArgument())
)
or
// Date = global.Date
exists(AssignExpr assgn |
id = assgn.getTarget() and
id.getName() = assgn.getRhs().getUnderlyingValue().(PropAccess).getPropertyName()
)
or
// var Date = global.Date
exists(VariableDeclarator decl |
id = decl.getBindingPattern() and
id.getName() = decl.getInit().getUnderlyingValue().(PropAccess).getPropertyName()
)
}
from DefiningIdentifier id, string name
where
not id.inExternsFile() and
name = id.getName() and
name
.regexpMatch("Object|Function|Array|String|Boolean|Number|Math|Date|RegExp|Error|" +
"NaN|Infinity|undefined|eval|parseInt|parseFloat|isNaN|isFinite|" +
"decodeURI|decodeURIComponent|encodeURI|encodeURIComponent") and
not acceptableRedefinition(id)
select id, "Redefinition of " + name + "."

View File

@@ -1,46 +0,0 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>
Defining a method by assigning a closure to a property of the receiver object in the constructor
is inefficient, since a new closure is created for every instance. This wastes heap space and may
interfere with JIT compilation.
</p>
</overview>
<recommendation>
<p>
Assign the function to a property of the prototype object instead. That way, all instances share
the same closure.
</p>
</recommendation>
<example>
<p>
In the following example, constructor <code>Point</code> defines method <code>move</code> by creating
a new closure and storing it in the <code>move</code> property of each new instance. Consequently,
<code>p.move</code> and <code>q.move</code> are different methods.
</p>
<sample src="examples/InefficientMethodDefinition.js" />
<p>
It is better to instead define <code>move</code> on the prototype object <code>Point.prototype</code>
like this:
</p>
<sample src="examples/InefficientMethodDefinitionGood.js" />
</example>
<references>
<li>Mozilla Developer Network: <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Inheritance_and_the_prototype_chain">Inheritance and the prototype chain</a>.</li>
</references>
</qhelp>

View File

@@ -1,41 +0,0 @@
/**
* @name Inefficient method definition
* @description Defining methods in the constructor (as opposed to adding them to the
* prototype object) is inefficient.
* @kind problem
* @problem.severity recommendation
* @id js/method-definition-in-constructor
* @tags efficiency
* maintainability
* @precision medium
* @deprecated This query is prone to false positives. Deprecated since 1.17.
*/
import javascript
import semmle.javascript.RestrictedLocations
/**
* Holds if `stmt` is of the form `this.<name> = <method>;`.
*/
predicate methodDefinition(ExprStmt stmt, string name, Function method) {
exists(AssignExpr assgn, PropAccess pacc |
assgn = stmt.getExpr() and
pacc = assgn.getLhs() and
pacc.getBase() instanceof ThisExpr and
name = pacc.getPropertyName() and
method = assgn.getRhs()
)
}
from Function ctor, ExprStmt defn, string name, Function method
where
not ctor instanceof ImmediatelyInvokedFunctionExpr and
defn = ctor.getABodyStmt() and
methodDefinition(defn, name, method) and
// if the method captures a local variable of the constructor, it cannot
// easily be moved to the constructor object
not exists(Variable v | v.getScope() = ctor.getScope() |
v.getAnAccess().getContainer().getEnclosingContainer*() = method
)
select defn.(FirstLineOf),
name + " should be added to the prototype object rather than to each instance."

View File

@@ -1,45 +0,0 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>
Avoid using <code>x % 2 === 1</code> or <code>x % 2 &gt; 0</code> to check whether a number
<code>x</code> is odd, or <code>x % 2 !== 1</code> to check whether it is even.
Such code does not work for negative numbers: for example, <code>-5 % 2</code> equals
<code>-1</code>, not <code>1</code>.
</p>
</overview>
<recommendation>
<p>
Consider using <code>x % 2 !== 0</code> to check for odd parity and <code>x % 2 === 0</code>
to check for even parity.
</p>
</recommendation>
<example>
<p>
The following code snippet does not detect -9 as an odd number because <code>-9 % 2</code>
is <code>-1</code>, not <code>1</code>.</p>
<sample src="examples/BadParityCheck.js" />
<p>
The check should be rewritten as follows:
</p>
<sample src="examples/BadParityCheckGood.js" />
</example>
<references>
<li>J. Bloch and N. Gafter, <em>Java Puzzlers: Traps, Pitfalls, and Corner Cases</em>, Puzzle 1. Addison-Wesley, 2005.</li>
<li>Ecma International, <i>ECMAScript Language Definition</i>, 5.1 Edition, Section 11.5.3. ECMA, 2011.</li>
</references>
</qhelp>

View File

@@ -1,105 +0,0 @@
/**
* @name Bad parity check
* @description Ensure that parity checks take negative numbers into account.
* @kind problem
* @problem.severity recommendation
* @id js/incomplete-parity-check
* @tags reliability
* correctness
* external/cwe/cwe-480
* @precision low
* @deprecated This query is prone to false positives. Deprecated since 1.17.
*/
import javascript
/*
* The following predicates implement a simple analysis for identifying
* expressions that are guaranteed to only evaluate to non-negative numbers:
*
* - non-negative number literals
* - applications of (), ++, + to expressions known to be non-negative
* - references to local variables that are only assigned non-negative values,
* never decremented, and never subjected to any compound assignments except
* += where the rhs is known to be non-negative
*
* This is a greatest-fixpoint problem: if we have `x = 0`, `y = x`, `x = y`,
* we want to conclude that both `x` and `y` are non-negative. Hence we have
* to implement the analysis the other way around, as a conservative check
* for negativity.
*/
/**
* Holds if `e` is an expression that is relevant for the maybe-negative analysis.
*/
predicate relevant(Expr e) {
// base case: left operands of `%`
exists(ModExpr me | e = me.getLeftOperand())
or
// first inductive case: downward AST traversal
relevant(e.getParentExpr())
or
// second inductive case: following variable assignments
exists(Variable v | relevant(v.getAnAccess()) | e = v.getAnAssignedExpr())
}
/** Holds if `e` could evaluate to a negative number. */
predicate maybeNegative(Expr e) {
relevant(e) and
if exists(e.getIntValue())
then e.getIntValue() < 0
else
if e instanceof ParExpr
then maybeNegative(e.(ParExpr).getExpression())
else
if e instanceof IncExpr
then maybeNegative(e.(IncExpr).getOperand())
else
if e instanceof VarAccess
then maybeNegativeVar(e.(VarAccess).getVariable())
else
if e instanceof AddExpr
then maybeNegative(e.(AddExpr).getAnOperand())
else
// anything else is considered to possibly be negative
any()
}
/** Holds if `v` could be assigned a negative number. */
predicate maybeNegativeVar(Variable v) {
v.isGlobal()
or
v.isParameter()
or
// is v ever assigned a potentially negative value?
maybeNegative(v.getAnAssignedExpr())
or
// is v ever decremented?
exists(DecExpr dec | dec.getOperand().getUnderlyingReference() = v.getAnAccess())
or
// is v ever subject to a compound assignment other than +=, or to
// += with potentially negative rhs?
exists(CompoundAssignExpr assgn | assgn.getTarget() = v.getAnAccess() |
not assgn instanceof AssignAddExpr or
maybeNegative(assgn.getRhs())
)
}
from Comparison cmp, ModExpr me, int num, string parity
where
cmp.getAnOperand().stripParens() = me and
cmp.getAnOperand().getIntValue() = num and
me.getRightOperand().getIntValue() = 2 and
maybeNegative(me.getLeftOperand()) and
(
(cmp instanceof EqExpr or cmp instanceof StrictEqExpr) and
num = 1 and
parity = "oddness"
or
(cmp instanceof NEqExpr or cmp instanceof StrictNEqExpr) and
num = 1 and
parity = "evenness"
or
cmp instanceof GTExpr and num = 0 and parity = "oddness"
)
select cmp, "Test for " + parity + " does not take negative numbers into account."

View File

@@ -1,56 +0,0 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>
In JavaScript, properties of objects do not have to be declared and can be dynamically added
and removed at runtime. Thus, if a property name is misspelled, this is not detected by the
compiler, and may lead to an error at runtime. The same problem occurs with misspelled
global variables.
</p>
<p>
This rule flags property names and global variables that are mentioned only once, but where
a different capitalization of the same name is used in multiple other places, suggesting a typo.
</p>
</overview>
<recommendation>
<p>
Check whether the name has been misspelled. If the name is correct, consider using
a <a href="http://www.jslint.com/help.html#properties">JSLint-style</a>
<code>/*property ...*/</code> directive to document the existence of this property,
or provide an externs file declaring the property.
</p>
</recommendation>
<example>
<p>
The following code snippet contains two uses of the <code>log</code> method, but only
one use of the <code>Log</code> method. This suggests that <code>Log</code> may be a typo
for <code>log</code>.
</p>
<sample src="examples/HapaxLegomenon.js" />
<p>
If the use of <code>Log</code> is, in fact, a typo, it should be corrected. Otherwise, a
<code>properties</code> directive can be introduced to document the fact that both
<code>log</code> and <code>Log</code> properties exist:
</p>
<sample src="examples/HapaxLegomenonGood.js" />
</example>
<references>
<li>JSLint: <a href="http://www.jslint.com/help.html#properties">Property</a>.</li>
<li>Google Closure Tools: <a href="https://developers.google.com/closure/compiler/docs/api-tutorial3?csw=1#externs">Declaring externs</a>.</li>
</references>
</qhelp>

View File

@@ -1,91 +0,0 @@
/**
* @name Potentially misspelled property or variable name
* @description A property or variable is only mentioned once, but there is one with the same name
* in different capitalization that is mentioned more than once, suggesting that this
* may be a typo.
* @kind problem
* @problem.severity warning
* @id js/wrong-capitalization
* @tags reliability
* @precision low
* @deprecated This query is prone to false positives. Deprecated since 1.17.
*/
import javascript
/** Gets the number of identifiers and string literals that refer to `name`. */
int countOccurrences(string name) {
(
exists(PropAccess pacc | name = pacc.getPropertyName()) or
exists(VarAccess acc | name = acc.getName())
) and
result = strictcount(Expr id |
id.(Identifier).getName() = name
or
// count string literals as well to capture meta-programming
id.getStringValue() = name
)
}
/**
* An access to an undeclared variable or property that is only referenced
* once in the entire program.
*/
abstract class Hapax extends @expr {
/** Gets the name of the accessed variable or property. */
abstract string getName();
/** Gets a textual representation of this element. */
string toString() { result = this.(Expr).toString() }
}
/**
* An access to a property that is covered neither by a JSLint property declaration
* nor by an externs declaration, and that is only mentioned once in the entire program.
*/
class UndeclaredPropertyAccess extends Hapax, @dotexpr {
UndeclaredPropertyAccess() {
exists(string name | name = this.(DotExpr).getPropertyName() |
countOccurrences(name) = 1 and
not exists(JSLintProperties jslpd | jslpd.appliesTo(this) and jslpd.getAProperty() = name) and
not exists(ExternalMemberDecl emd | emd.getProperty() = this)
)
}
override string getName() { result = this.(DotExpr).getPropertyName() }
}
/**
* An access to a global variable that is neither declared nor covered by a linter
* directive, and that is only mentioned once in the entire program.
*/
class UndeclaredGlobal extends Hapax, @varaccess {
UndeclaredGlobal() {
exists(GlobalVariable gv, string name | this = gv.getAnAccess() and name = gv.getName() |
countOccurrences(name) = 1 and
not exists(Linting::GlobalDeclaration glob | glob.declaresGlobalForAccess(this)) and
not exists(gv.getADeclaration())
)
}
override string getName() { result = this.(VarAccess).getName() }
}
/**
* Gets the number of occurrences of `m`, which is the same as `hapax`
* except for capitalization, ensuring that it occurs at least twice.
*/
int candidateSpellingCount(Hapax hapax, string m) {
exists(string n | n = hapax.getName() |
m.toLowerCase() = n.toLowerCase() and
m != n and
result = countOccurrences(m) and
result > 1
)
}
from Hapax hapax, string n, string m
where
n = hapax.getName() and
candidateSpellingCount(hapax, m) = max(candidateSpellingCount(hapax, _))
select hapax.(Expr), "'" + n + "' is mentioned only once; it may be a typo for '" + m + "'."

View File

@@ -1,38 +0,0 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>
Non-standard JSDoc tags are undesirable, since JSDoc-processing tools will either ignore
them or treat them as plain text.
</p>
</overview>
<recommendation>
<p>
Check whether the tag name is misspelled, or consult the JSDoc documentation to find out
what standard tags are available.
</p>
</recommendation>
<example>
<p>
In the following example, the constructor <code>Message</code> has a JSDoc comment describing
its parameters, but the second <code>@param</code> tag has been misspelled as
<code>@parma</code>.
</p>
<sample src="examples/UnknownTagType.js" />
</example>
<references>
<li>Use JSDoc: <a href="http://usejsdoc.org/index.html">Tag Dictionary</a>.</li>
</references>
</qhelp>

View File

@@ -1,139 +0,0 @@
/**
* @name Unknown JSDoc tag
* @description A JSDoc tag with a non-standard tag type will either be ignored or treated as plain
* text by JSDoc-processing tools.
* @kind problem
* @problem.severity recommendation
* @id js/jsdoc/unknown-tag-type
* @tags maintainability
* readability
* documentation
* @precision low
* @deprecated This query is prone to false positives. Deprecated since 1.17.
*/
import javascript
/** Holds if `tp` is a standard tag type. */
predicate knownTagType(string tp) {
tp = "abstract" or
tp = "access" or
tp = "alias" or
tp = "api" or
tp = "arg" or
tp = "argument" or
tp = "augments" or
tp = "author" or
tp = "borrows" or
tp = "bug" or
tp = "callback" or
tp = "category" or
tp = "class" or
tp = "classdesc" or
tp = "const" or
tp = "constant" or
tp = "constructor" or
tp = "constructs" or
tp = "copyright" or
tp = "default" or
tp = "defaultvalue" or
tp = "define" or
tp = "depend" or
tp = "depends" or
tp = "deprecated" or
tp = "desc" or
tp = "description" or
tp = "dict" or
tp = "emits" or
tp = "enum" or
tp = "event" or
tp = "example" or
tp = "exception" or
tp = "export" or
tp = "exports" or
tp = "expose" or
tp = "extends" or
tp = "external" or
tp = "externs" or
tp = "field" or
tp = "file" or
tp = "fileoverview" or
tp = "final" or
tp = "fires" or
tp = "flow" or
tp = "func" or
tp = "function" or
tp = "global" or
tp = "host" or
tp = "ignore" or
tp = "implements" or
tp = "implicitCast" or
tp = "inheritDoc" or
tp = "inner" or
tp = "interface" or
tp = "internal" or
tp = "instance" or
tp = "kind" or
tp = "lends" or
tp = "license" or
tp = "link" or
tp = "member" or
tp = "memberof" or
tp = "memberOf" or
tp = "method" or
tp = "mixes" or
tp = "mixin" or
tp = "modifies" or
tp = "module" or
tp = "modName" or
tp = "mods" or
tp = "name" or
tp = "namespace" or
tp = "ngInject" or
tp = "noalias" or
tp = "nocompile" or
tp = "nosideeffects" or
tp = "note" or
tp = "override" or
tp = "overview" or
tp = "owner" or
tp = "package" or
tp = "param" or
tp = "preserve" or
tp = "preserveTry" or
tp = "private" or
tp = "prop" or
tp = "property" or
tp = "protected" or
tp = "providesModule" or
tp = "public" or
tp = "readonly" or
tp = "requires" or
tp = "returns" or
tp = "return" or
tp = "see" or
tp = "since" or
tp = "static" or
tp = "struct" or
tp = "summary" or
tp = "supported" or
tp = "suppress" or
tp = "template" or
tp = "this" or
tp = "throws" or
tp = "todo" or
tp = "tutorial" or
tp = "type" or
tp = "typedef" or
tp = "var" or
tp = "variation" or
tp = "version" or
tp = "virtual" or
tp = "visibility" or
tp = "wizaction" or
tp = "wizmodule"
}
from JSDocTag tag
where not knownTagType(tag.getTitle())
select tag, "Unknown tag type '" + tag.getTitle() + "'."

View File

@@ -1,26 +0,0 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>
JSLint directives must not start with a space. For example, <code>/* global window*/</code>
is not a valid directive, and will not be recognized by JSLint.
</p>
</overview>
<recommendation>
<p>
Remove the space: <code>/*global window*/</code>.
</p>
</recommendation>
<references>
<li>JSLint: <a href="http://www.jslint.com/help.html">JSLint Help</a>.</li>
</references>
</qhelp>

View File

@@ -1,21 +0,0 @@
/**
* @name Invalid JSLint directive
* @description A JSLint directive that has whitespace characters before the
* directive name is not recognized by JSLint.
* @kind problem
* @problem.severity recommendation
* @id js/jslint/invalid-directive
* @tags maintainability
* @precision medium
* @deprecated JSLint is rarely used any more. Deprecated since 1.17.
*/
import javascript
from SlashStarComment c
where
// use possessive quantifiers '*+' and '++' to avoid backtracking
c
.getText()
.regexpMatch("\\s+(global|properties|property|jslint)\\s(\\s*+[a-zA-Z$_][a-zA-Z0-9$_]*+(\\s*+:\\s*+\\w++)?\\s*+,?)++\\s*")
select c, "JSLint directives must not have whitespace characters before the directive name."

View File

@@ -1,49 +0,0 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>
JSLint directives must consist of a comma-separated list of flags, where each flag
can optionally be followed by a colon and a value. The value may either be a number
or a Boolean (that is, 'true' or 'false'). Directives must not contain other
characters such as '*', which some editors may automatically insert after every line
break when editing a block comment.
</p>
</overview>
<recommendation>
<p>
Insert commas where necessary and remove stray characters.
</p>
</recommendation>
<example>
<p>
For example, <code>/*jslint nomen:true vars:true*/</code> is not a well-formed
JSLint directive; it should be replaced by <code>/*jslint nomen:true, vars:true*/</code>.
</p>
<p>
This is another example of a malformed JSLint directive:
</p>
<sample src="examples/MalformedJSLintDirective.js" />
<p>
It should be fixed as follows:
</p>
<sample src="examples/MalformedJSLintDirectiveGood.js" />
</example>
<references>
<li>JSLint: <a href="http://www.jslint.com/help.html">JSLint Help</a>.</li>
</references>
</qhelp>

View File

@@ -1,26 +0,0 @@
/**
* @name Malformed JSLint directive
* @description A malformed JSLint directive will be rejected by JSLint, and may be either
* rejected or ignored by other tools.
* @kind problem
* @problem.severity recommendation
* @id js/jslint/malformed-directive
* @tags maintainability
* @precision medium
* @deprecated JSLint is rarely used any more. Deprecated since 1.17.
*/
import javascript
from JSLintDirective dir, string flag, string flags, string directive
where
// a flag, optionally followed by a colon and a value, where the value may be
// a Boolean or a number
flag = "[a-zA-Z$_][a-zA-Z0-9$_]*(\\s*:\\s*(true|false|\\d+))?" and
// a non-empty, comma-separated list of flags
flags = "(" + flag + "\\s*,\\s*)*" + flag and
// a word (which is the directive's name), followed by a possibly empty list of flags
// note that there may be trailing whitespace, but no leading whitespace
directive = "\\s*\\w+\\s+(" + flags + ")?\\s*" and
not dir.getText().regexpMatch(directive)
select dir, "Malformed JSLint directive."

View File

@@ -1,42 +0,0 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>
HTML comments are a technique for hiding JavaScript code from browsers that do not interpret <code>script</code>
tags. Since all popular browsers have supported <code>script</code> tags for many years, this precaution is
not needed any more.
</p>
</overview>
<recommendation>
<p>
Remove all HTML comments.
</p>
</recommendation>
<example>
<p>
The following code block uses HTML comments to hide the <code>script</code> block from ancient browsers.
</p>
<sample src="examples/HTMLComments.js" />
<p>
Since such browsers are no longer widely used, the comments should be removed:
</p>
<sample src="examples/HTMLCommentsGood.js" />
</example>
<references>
<li>JavaScript Toolbox: <a href="http://www.javascripttoolbox.com/bestpractices/#comments">JavaScript Best Practices</a>.</li>
</references>
</qhelp>

View File

@@ -1,18 +0,0 @@
/**
* @name Use of HTML comments
* @description HTML-style comments are not a standard ECMAScript feature and should be avoided.
* @kind problem
* @problem.severity recommendation
* @id js/html-comment
* @tags maintainability
* language-features
* external/cwe/cwe-758
* @precision low
* @deprecated HTML comments are recognized in the standard as an additional feature supported by
* web browsers. Deprecated since 1.17.
*/
import javascript
from HtmlLineComment c
select c, "Do not use HTML comments."

View File

@@ -1,40 +0,0 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>
Multi-line string literals are not supported on all platforms, and thus should be avoided.
</p>
</overview>
<recommendation>
<p>
Replace multi-line string literals by multiple strings concatenated with the <code>+</code> operator.
</p>
</recommendation>
<example>
<p>
The following example contains a string literal spanning three lines:
</p>
<sample src="examples/MultilineStringLiteral.js" />
<p>
It should be rewritten like this:
</p>
<sample src="examples/MultilineStringLiteralGood.js" />
</example>
<references>
<li>Ecma International, <i>ECMAScript Language Definition</i>, 5.1 Edition, Section 7.8.4. ECMA, 2011.</li>
</references>
</qhelp>

View File

@@ -1,19 +0,0 @@
/**
* @name Multi-line string literal
* @description Multi-line string literals are non-standard and hard to read, and should be avoided.
* @kind problem
* @problem.severity recommendation
* @id js/multi-line-string
* @tags maintainability
* external/cwe/cwe-758
* @precision low
* @deprecated Multi-line string literals are now a standard language feature. Deprecated since 1.17.
*/
import javascript
from StringLiteral sl, Location l
where
l = sl.getLocation() and
l.getStartLine() != l.getEndLine()
select sl, "Avoid multi-line string literals."

View File

@@ -1,40 +0,0 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>
Integer literals starting with the digit <code>0</code> may be interpreted as octal numbers by some platforms
but not others, and thus should be avoided. This does not make a difference for the literal <code>0</code>
itself.
</p>
</overview>
<recommendation>
<p>
If the literal was meant to be octal, convert it to a decimal or hexadecimal number. Otherwise, remove
the leading zero.
</p>
</recommendation>
<example>
<p>
The following example uses the literal <code>012</code>, which some platforms will interpret as an octal
encoding of the decimal number <code>10</code>, while others will interpret it as the decimal number
<code>12</code>. Depending on the desired interpretation, it should be replaced with either <code>10</code>
or <code>12</code>.
</p>
<sample src="examples/OctalLiteral.js" />
</example>
<references>
<li>Ecma International, <i>ECMAScript Language Definition</i>, 5.1 Edition, Annex B.1.1. ECMA, 2011.</li>
</references>
</qhelp>

View File

@@ -1,17 +0,0 @@
/**
* @name Octal literal
* @description Octal numeric literals are a platform-specific extension and should not be used.
* @kind problem
* @problem.severity recommendation
* @id js/octal-literal
* @tags portability
* external/cwe/cwe-758
* @precision low
* @deprecated This query is prone to false positives. Deprecated since 1.17.
*/
import javascript
from NumberLiteral nl
where nl.getRawValue().regexpMatch("0\\d+")
select nl, "Do not use octal literals."

View File

@@ -1,37 +0,0 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>
The ECMAScript standard defines a list of future keywords that should not be used as identifiers.
While they may be accepted by current implementations, they may no longer be supported in the future,
so it is best not to rely on them.
</p>
</overview>
<recommendation>
<p>
Rename the identifier in question.
</p>
</recommendation>
<example>
<p>
In the following code snippet, <code>package</code> is used as a variable name. Since <code>package</code>
is a future reserved word, the variable should be renamed, for instance to <code>pkg</code>.
</p>
<sample src="examples/ReservedWords.js" />
</example>
<references>
<li>Ecma International, <i>ECMAScript Language Definition</i>, 5.1 Edition, Section 7.6.1.2. ECMA, 2011.</li>
</references>
</qhelp>

View File

@@ -1,24 +0,0 @@
/**
* @name Reserved word used as variable name
* @description Future reserved words should not be used as variable names.
* @kind problem
* @problem.severity recommendation
* @id js/use-of-reserved-word
* @tags maintainability
* language-features
* @precision very-high
* @deprecated This is no longer a problem with modern browsers. Deprecated since 1.17.
*/
import javascript
from Identifier id
where
id
.getName()
.regexpMatch("class|const|enum|export|extends|import|super|implements|interface|let|package|private|protected|public|static|yield") and
not exists(DotExpr de | id = de.getProperty()) and
not exists(Property prop | id = prop.getNameExpr()) and
// exclude JSX attribute names
not exists(JSXElement e | id = e.getAnAttribute().getNameExpr())
select id, "Identifier name is a reserved word."

View File

@@ -1,37 +0,0 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>
The ECMAScript standard allows trailing commas in array and object literals which are ignored. However,
older versions of Internet Explorer do not recognize this syntax. Moreover, it can lead to confusion
when used in array literals, since spurious commas other than the last one are not ignored but give rise
to additional undefined array elements. For these reasons, trailing commas should always be avoided.
</p>
</overview>
<recommendation>
<p>
Remove the trailing comma.
</p>
</recommendation>
<example>
<p>
The following code snippet shows an object literal with a trailing comma, which should be removed.
</p>
<sample src="examples/TrailingComma.js" />
</example>
<references>
<li>Ecma International, <i>ECMAScript Language Definition</i>, 5.1 Edition, Sections 11.1.4 and 11.1.5. ECMA, 2011.</li>
</references>
</qhelp>

View File

@@ -1,37 +0,0 @@
/**
* @name Trailing comma in array or object expressions
* @description Trailing commas in array and object expressions are interpreted differently
* by different browsers and should be avoided.
* @kind problem
* @problem.severity recommendation
* @id js/trailing-comma-in-array-or-object
* @tags portability
* external/cwe/cwe-758
* @precision low
* @deprecated This is no longer a problem with modern browsers. Deprecated since 1.17.
*/
import javascript
/** An array or object expression. */
class ArrayOrObjectExpr extends Expr {
ArrayOrObjectExpr() {
this instanceof ArrayExpr or
this instanceof ObjectExpr
}
/** Holds if this array or object expression has a trailing comma. */
predicate hasTrailingComma() {
this.(ArrayExpr).hasTrailingComma() or
this.(ObjectExpr).hasTrailingComma()
}
/** Gets a short description of this expression. */
string getShortName() {
if this instanceof ArrayExpr then result = "array expression" else result = "object expression"
}
}
from ArrayOrObjectExpr e
where e.hasTrailingComma()
select e.getLastToken().getPreviousToken(), "Trailing comma in " + e.getShortName() + "."

View File

@@ -1,51 +0,0 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>
On some platforms, the builtin function <code>parseInt</code> parses strings starting with the digit
<code>0</code> as octal values (unless an explicit radix is provided). This can lead to unexpected
results when parsing decimal numbers that may be zero-padded, such as dates.
</p>
</overview>
<recommendation>
<p>
Provide an explicit radix as the second parameter to <code>parseInt</code>.
</p>
</recommendation>
<example>
<p>
In the following example, <code>parseInt</code> is used to convert the contents of a field in an HTML
form to a number:
</p>
<sample src="examples/ParseIntRadix.js" />
<p>
Now assume that a user has entered a zero-padded decimal number, say <code>09</code>, into the form.
Since the first digit is a zero, older versions of <code>parseInt</code> interpret this value as an
octal number. When they then encounter <code>9</code> (which is not an octal digit), they will stop
parsing and discard the rest of the string, returning the value <code>0</code>, which is probably not
what was expected.
</p>
<p>
To avoid this problem, an explicit radix parameter should be parsed as follows:
</p>
<sample src="examples/ParseIntRadixGood.js" />
</example>
<references>
<li>D. Crockford, <i>JavaScript: The Good Parts</i>, Appendix A.7. O'Reilly, 2008.</li>
</references>
</qhelp>

View File

@@ -1,21 +0,0 @@
/**
* @name Call to parseInt without radix
* @description Calls to the 'parseInt' function should always specify a radix to avoid accidentally
* parsing a number as octal.
* @kind problem
* @problem.severity recommendation
* @id js/parseint-without-radix
* @tags reliability
* maintainability
* external/cwe/cwe-676
* @precision very-high
* @deprecated This is no longer a problem with modern browsers. Deprecated since 1.17.
*/
import javascript
from DataFlow::CallNode parseInt
where
parseInt = DataFlow::globalVarRef("parseInt").getACall() and
parseInt.getNumArgument() = 1
select parseInt, "Missing radix parameter."

View File

@@ -1,7 +0,0 @@
| builtinRedefined.js:1:1:1:4 | eval | Redefinition of eval. |
| builtinRedefined.js:2:3:2:6 | eval | Redefinition of eval. |
| builtinRedefined.js:3:5:3:8 | eval | Redefinition of eval. |
| builtinRedefined.js:4:12:4:15 | eval | Redefinition of eval. |
| builtinRedefined.js:5:18:5:21 | eval | Redefinition of eval. |
| builtinRedefined.js:8:1:8:6 | Object | Redefinition of Object. |
| builtinRedefined.js:10:12:10:20 | undefined | Redefinition of undefined. |

View File

@@ -1 +0,0 @@
Declarations/BuiltinRedefined.ql

View File

@@ -1,14 +0,0 @@
eval = 42;
++eval;
var eval;
function x(eval) { }
var y = function eval() { };
eval("");
Object = function(){};
function f(undefined) {}
(function f(undefined){}());
Date = this.Date;
var Math = this.Math;

View File

@@ -1,3 +0,0 @@
Object = function(){};
//semmle-extractor-options: --externs

View File

@@ -1 +0,0 @@
| tst.js:6:5:6:28 | this.di ... \\n }; | dist should be added to the prototype object rather than to each instance. |

View File

@@ -1 +0,0 @@
Declarations/InefficientMethodDefinition.ql

View File

@@ -1,53 +0,0 @@
function Point(x, y) {
this.x = x;
this.y = y;
// NOT OK
this.dist = function() {
return Math.sqrt(this.x*this.x + this.y*this.y);
};
// OK
this.getOriginalX = function() {
return x;
};
// OK
this.getOriginalXGetter = function() {
return function() {
return x;
};
};
// OK
this.getOtherOriginalXGetter = function() {
function getter() {
return x;
}
return getter;
};
if (x === 0)
// OK
this.dist = function() {
return y;
};
var o = {};
// OK
o.f = function() { return 23; };
}
// OK
var o = new(function() {
this.f = function() { return 42 };
});
// OK
(function() {
this.move = function(dx, dy) {
this.x += dx;
this.y += dy;
};
}).call(Point.prototype);

View File

@@ -1,9 +0,0 @@
| tst.js:1:1:1:11 | x % 2 === 1 | Test for oddness does not take negative numbers into account. |
| tst.js:2:1:2:11 | x % 2 !== 1 | Test for evenness does not take negative numbers into account. |
| tst.js:3:1:3:10 | x % 2 == 1 | Test for oddness does not take negative numbers into account. |
| tst.js:4:1:4:10 | x % 2 != 1 | Test for evenness does not take negative numbers into account. |
| tst.js:25:1:25:9 | x % 2 > 0 | Test for oddness does not take negative numbers into account. |
| tst.js:29:1:29:17 | x % (2) === ((1)) | Test for oddness does not take negative numbers into account. |
| tst.js:31:1:31:11 | 1 === x % 2 | Test for oddness does not take negative numbers into account. |
| tst.js:43:1:43:11 | y % 2 === 1 | Test for oddness does not take negative numbers into account. |
| tst.js:47:9:47:17 | i % 2 > 0 | Test for oddness does not take negative numbers into account. |

View File

@@ -1 +0,0 @@
Expressions/BadParityCheck.ql

View File

@@ -1,48 +0,0 @@
x % 2 === 1;
x % 2 !== 1;
x % 2 == 1;
x % 2 != 1;
x % 2 >= 1;
x % 2 <= 1;
x % 2 > 1;
x % 2 < 1;
x % 2 === -1;
x % 2 !== -1;
x % 2 == -1;
x % 2 != -1;
x % 2 >= -1;
x % 2 <= -1;
x % 2 > -1;
x % 2 < -1;
x % 2 === 0;
x % 2 !== 0;
x % 2 == 0;
x % 2 != 0;
x % 2 >= 0;
x % 2 <= 0;
x % 2 > 0;
x % 2 < 0;
x % 3 === 1;
x % (2) === ((1));
2 % x == 1;
1 === x % 2;
x = Math.random() > 0.5 ? -1 : 1;
// OK
function printOdd(n) {
for (var i=0; i<n; ++i)
if (i % 2 == 1)
console.log(i);
}
// NOT OK
y % 2 === 1;
// NOT OK
function isEven(i) {
return i % 2 > 0;
}

View File

@@ -1,2 +0,0 @@
| tst.js:9:1:9:5 | o.Foo | 'Foo' is mentioned only once; it may be a typo for 'foo'. |
| tst.js:26:5:26:9 | iccID | 'iccID' is mentioned only once; it may be a typo for 'iccId'. |

View File

@@ -1 +0,0 @@
Expressions/HapaxLegomenon.ql

View File

@@ -1,7 +0,0 @@
var google;
google.maps.Marker;
google.maps.Bar = 23;
//semmle-extractor-options: --externs

View File

@@ -1,33 +0,0 @@
/*properties Map*/
var o = {
foo: function() {}
};
o.foo();
o.foo();
o.Foo();
new google.maps.Map(document.getElementById("map"), mapOptions);
[].map(function(){});
[1, 2, 3].map(function(){});
new google.maps.Marker();
google.maps.marker;
google.maps.marker;
var q = { bar: 42 };
q.bar;
q.bar;
function tst(iccId) {
iccId;
iccId;
iccID;
}
/*property startsWith*/
var o = { StartsWith: 42 };
o.StartsWith;
o.StartsWith;
"hi".startsWith("h");

View File

@@ -1 +0,0 @@
| tst.js:7:4:7:9 | @parma | Unknown tag type 'parma'. |

View File

@@ -1 +0,0 @@
JSDoc/UnknownTagType.ql

View File

@@ -1,17 +0,0 @@
/**
* A message.
*
* @constructor
*
* @param title
* @parma text
*/
function Message(title, body) {
this.title = title;
this.body = body;
}
/**
* @modifies something
*/
function f() {}

View File

@@ -1,3 +0,0 @@
| tst.js:2:1:2:13 | /* global x*/ | JSLint directives must not have whitespace characters before the directive name. |
| tst.js:3:1:3:18 | /* global x,y: z*/ | JSLint directives must not have whitespace characters before the directive name. |
| tst.js:16:1:98:2 | /* glob ... rue,\\n*/ | JSLint directives must not have whitespace characters before the directive name. |

View File

@@ -1 +0,0 @@
JSLint/InvalidJSLintDirective.ql

View File

@@ -1,187 +0,0 @@
// NOT OK
/* global x*/
/* global x,y: z*/
// OK: vacuous directives
/* jslint */
/* properties*/
// OK: not a directive
/* jslint is popular. */
// OK: not a directive
// global x
// NOT OK
/* global angular: true,
msie: true,
jqLite: true,
jQuery: true,
slice: true,
push: true,
toString: true,
ngMinErr: true,
angularModule: true,
nodeName_: true,
uid: true,
VALIDITY_STATE_PROPERTY: true,
lowercase: true,
uppercase: true,
manualLowercase: true,
manualUppercase: true,
nodeName_: true,
isArrayLike: true,
forEach: true,
sortedKeys: true,
forEachSorted: true,
reverseParams: true,
nextUid: true,
setHashKey: true,
extend: true,
int: true,
inherit: true,
noop: true,
identity: true,
valueFn: true,
isUndefined: true,
isDefined: true,
isObject: true,
isString: true,
isNumber: true,
isDate: true,
isArray: true,
isFunction: true,
isRegExp: true,
isWindow: true,
isScope: true,
isFile: true,
isBlob: true,
isBoolean: true,
isPromiseLike: true,
trim: true,
isElement: true,
makeMap: true,
map: true,
size: true,
includes: true,
indexOf: true,
arrayRemove: true,
isLeafNode: true,
copy: true,
shallowCopy: true,
equals: true,
csp: true,
concat: true,
sliceArgs: true,
bind: true,
toJsonReplacer: true,
toJson: true,
fromJson: true,
toBoolean: true,
startingTag: true,
tryDecodeURIComponent: true,
parseKeyValue: true,
toKeyValue: true,
encodeUriSegment: true,
encodeUriQuery: true,
angularInit: true,
bootstrap: true,
snake_case: true,
bindJQuery: true,
assertArg: true,
assertArgFn: true,
assertNotHasOwnProperty: true,
getter: true,
getBlockElements: true,
hasOwnProperty: true,
*/
// OK: not a directive
/*notadirective global x*/
// OK: not a directive
/* global angular: true,
msie: true,
jqLite: true,
jQuery: true,
slice: true,
push: true,
toString: true,
ngMinErr: true,
angularModule: true,
nodeName_: true,
uid: true,
VALIDITY_STATE_PROPERTY: true,
lowercase: true,
uppercase: true,
manualLowercase: true,
manualUppercase: true,
nodeName_: true,
isArrayLike: true,
forEach: true,
sortedKeys: true,
forEachSorted: true,
reverseParams: true,
nextUid: true,
setHashKey: true,
extend: true,
int: true,
inherit: true,
noop: true,
identity: true,
valueFn: true,
isUndefined: true,
isDefined: true,
isObject: true,
isString: true,
isNumber: true,
isDate: true,
isArray: true,
isFunction: true,
isRegExp: true,
isWindow: true,
isScope: true,
isFile: true,
isBlob: true,
isBoolean: true,
isPromiseLike: true,
trim: true,
isElement: true,
makeMap: true,
map: true,
size: true,
includes: true,
indexOf: true,
arrayRemove: true,
isLeafNode: true,
copy: true,
shallowCopy: true,
equals: true,
csp: true,
concat: true,
sliceArgs: true,
bind: true,
toJsonReplacer: true,
toJson: true,
fromJson: true,
toBoolean: true,
startingTag: true,
tryDecodeURIComponent: true,
parseKeyValue: true,
toKeyValue: true,
encodeUriSegment: true,
encodeUriQuery: true,
angularInit: true,
bootstrap: true,
snake_case: true,
bindJQuery: true,
assertArg: true,
assertArgFn: true,
assertNotHasOwnProperty: true,
getter: true,
getBlockElements: true,
hasOwnProperty: true,
@@@
*/

View File

@@ -1,3 +0,0 @@
| tst.js:1:1:1:38 | /*jslin ... true */ | Malformed JSLint directive. |
| tst.js:2:1:3:20 | /*jslin ... true */ | Malformed JSLint directive. |
| tst.js:5:1:5:17 | /*global a:treu*/ | Malformed JSLint directive. |

View File

@@ -1 +0,0 @@
JSLint/MalformedJSLintDirective.ql

View File

@@ -1,9 +0,0 @@
/*jslint bitwise:true plusplus:true */
/*jslint bitwise:true,
* plusplus:true */
/*jslint bitwise:true, plusplus:true */
/*global a:treu*/
/*properties */
/*jslint forin: true, maxlen: 350 */
/* global x*/
/* global $*/

View File

@@ -1 +0,0 @@
| tst.js:1:1:1:11 | <!-- NOT OK | Do not use HTML comments. |

View File

@@ -1 +0,0 @@
LanguageFeatures/HTMLComments.ql

View File

@@ -1,3 +0,0 @@
<!-- NOT OK
// OK
/* OK */

View File

@@ -1 +0,0 @@
| multilineStringLiteral.js:2:9:7:38 | 'A rath ... e day.' | Avoid multi-line string literals. |

View File

@@ -1 +0,0 @@
LanguageFeatures/MultilineStringLiteral.ql

View File

@@ -1,9 +0,0 @@
function sayHello() {
nok = 'A rather long string of English text, an error message \
actually that just keeps going and going -- an error \
message to make the Energizer bunny blush (right through \
those Schwarzenegger shades)! Where was I? Oh yes, \
you\'ve got an error and all the extraneous whitespace is \
just gravy. Have a nice day.';
ok = 'ok';
}

View File

@@ -1,7 +0,0 @@
| tst.js:1:5:1:14 | implements | Identifier name is a reserved word. |
| tst.js:1:17:1:25 | interface | Identifier name is a reserved word. |
| tst.js:1:28:1:34 | package | Identifier name is a reserved word. |
| tst.js:1:37:1:43 | private | Identifier name is a reserved word. |
| tst.js:1:46:1:54 | protected | Identifier name is a reserved word. |
| tst.js:1:57:1:62 | public | Identifier name is a reserved word. |
| tst.js:1:65:1:70 | static | Identifier name is a reserved word. |

View File

@@ -1 +0,0 @@
LanguageFeatures/ReservedWords.ql

View File

@@ -1 +0,0 @@
var div = <div class="highlight">Hi!</div>;

View File

@@ -1,5 +0,0 @@
var implements, interface, package, private, protected, public, static; // NOT OK
({
implements: true // OK
})

View File

@@ -1,2 +0,0 @@
| tst.js:1:9:1:9 | , | Trailing comma in object expression. |
| tst.js:2:6:2:6 | , | Trailing comma in array expression. |

View File

@@ -1 +0,0 @@
LanguageFeatures/TrailingComma.ql

View File

@@ -1,2 +0,0 @@
({ x: 42, }) // NOT OK
([ 23, ]); // NOT OK

View File

@@ -1 +0,0 @@
| tst.js:1:1:1:24 | parseIn ... .value) | Missing radix parameter. |

View File

@@ -1 +0,0 @@
StandardLibrary/ParseIntRadix.ql

View File

@@ -1,8 +0,0 @@
parseInt(form.day.value);
parseInt(form.day.value, 10);
o.parseInt(s);
function f(parseInt, s) {
parseInt();
parseInt(s, 10);
}