mirror of
https://github.com/github/codeql.git
synced 2026-04-26 17:25:19 +02:00
add query for detecting suspisous method names in TypeScript
This commit is contained in:
50
javascript/ql/src/Declarations/SuspiciousMethodName.qhelp
Normal file
50
javascript/ql/src/Declarations/SuspiciousMethodName.qhelp
Normal file
@@ -0,0 +1,50 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
<overview>
|
||||
<p>
|
||||
In TypeScript the keywords <code>constructor</code> and <code>new</code> are
|
||||
used to declare constructors in classes and interfaces respectively.
|
||||
However, by using the wrong keyword a programmer can accidentally declare e.g.
|
||||
a method called <code>constructor</code> inside an interface.
|
||||
Similarly the keyword <code>function</code> is used to declare functions in
|
||||
some contexts, however, using the keyword <code>function</code> inside a class
|
||||
or interface results in declaring a method called <code>function</code>.
|
||||
</p>
|
||||
|
||||
</overview>
|
||||
<recommendation>
|
||||
|
||||
<p>
|
||||
Declare classes as classes and not as interfaces.
|
||||
Use the keyword <code>constructor</code> to declare constructors in a class,
|
||||
use the keyword <code>new</code> to declare constructors inside interfaces,
|
||||
and don't use <code>function</code> when declaring an interface that is a
|
||||
function.
|
||||
</p>
|
||||
|
||||
</recommendation>
|
||||
<example>
|
||||
|
||||
<p>
|
||||
The below example declares an interface <code>Point</code> with 2 fields
|
||||
and a method called <code>constructor</code>. The interface does not declare
|
||||
a class <code>Point</code> with a constructor, which was likely what the
|
||||
developer meant to create.
|
||||
</p>
|
||||
<sample src="examples/SuspiciousMethodName.ts" />
|
||||
|
||||
<p>
|
||||
The below example is a fixed version of the above, where the interface is
|
||||
instead declared as a class, thereby describing the type the developer meant
|
||||
in the first place.
|
||||
</p>
|
||||
|
||||
<sample src="examples/SuspiciousMethodNameFixed.ts" />
|
||||
|
||||
</example>
|
||||
<references>
|
||||
|
||||
</references>
|
||||
</qhelp>
|
||||
80
javascript/ql/src/Declarations/SuspiciousMethodName.ql
Normal file
80
javascript/ql/src/Declarations/SuspiciousMethodName.ql
Normal file
@@ -0,0 +1,80 @@
|
||||
/**
|
||||
* @name Suspicious method name
|
||||
* @description A method having the name "function", "new", or "constructor"
|
||||
* is usually caused by a programmer being confused about the TypeScript syntax.
|
||||
* @kind problem
|
||||
* @problem.severity warning
|
||||
* @id js/suspicious-method-name
|
||||
* @precision high
|
||||
* @tags correctness
|
||||
* typescript
|
||||
* methods
|
||||
*/
|
||||
|
||||
import javascript
|
||||
|
||||
/**
|
||||
* Holds if the method name on the given container is likely to be a mistake.
|
||||
*/
|
||||
predicate isSuspisousMethodName(string name, ClassOrInterface container) {
|
||||
name = "function"
|
||||
or
|
||||
// "constructor" is only suspicious outside a class.
|
||||
name = "constructor" and not container instanceof ClassDefinition
|
||||
or
|
||||
// "new" is only suspicious inside a class.
|
||||
name = "new" and container instanceof ClassDefinition
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the beginning of the location is before the end.
|
||||
*/
|
||||
predicate isRealLocation(Location l) {
|
||||
l.getEndLine() > l.getStartLine() or
|
||||
(l.getStartLine() = l.getEndLine() and l.getEndColumn() > l.getStartColumn())
|
||||
}
|
||||
|
||||
from MethodDeclaration member, ClassOrInterface container, string suffixMsg
|
||||
where
|
||||
container.getLocation().getFile().getFileType().isTypeScript() and
|
||||
container.getAMember() = member and
|
||||
isSuspisousMethodName(member.getName(), container) and
|
||||
|
||||
// Assume that a "new" method is intentional if the class has an explicit constructor.
|
||||
not (
|
||||
member.getName() = "new" and
|
||||
container instanceof ClassDefinition and
|
||||
exists(MemberDeclaration constructor |
|
||||
container.getAMember() = constructor and
|
||||
constructor.getName() = "constructor" and
|
||||
// Test that it is not an implicitly declared constructor.
|
||||
isRealLocation(constructor.getLocation())
|
||||
)
|
||||
) and
|
||||
|
||||
// Explicitly declared static methods are fine.
|
||||
not (
|
||||
container instanceof ClassDefinition and
|
||||
member.isStatic()
|
||||
) and
|
||||
|
||||
// Only looking for declared methods. Methods with a body are OK.
|
||||
not exists(member.getBody().getBody()) and
|
||||
|
||||
// The developer was not confused about "function" when there are other methods in the interface.
|
||||
not (
|
||||
member.getName() = "function" and
|
||||
exists(MethodDeclaration other | other = container.getMethod(_) |
|
||||
other.getName() != "function" and
|
||||
isRealLocation(other.getLocation())
|
||||
)
|
||||
) and
|
||||
|
||||
(
|
||||
member.getName() = "constructor" and suffixMsg = "Did you mean to write a class instead of an interface?"
|
||||
or
|
||||
member.getName() = "new" and suffixMsg = "Did you mean \"constructor\"?"
|
||||
or
|
||||
member.getName() = "function" and suffixMsg = "Did you mean to omit \"function\"?"
|
||||
)
|
||||
select member, "Declares a suspiciously named method \"" + member.getName() + "\". " + suffixMsg
|
||||
@@ -0,0 +1,6 @@
|
||||
declare class Point {
|
||||
x: number;
|
||||
y: number;
|
||||
constructor(x : number, y: number);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
interface Point {
|
||||
x: number;
|
||||
y: number;
|
||||
}
|
||||
Reference in New Issue
Block a user