Skip to content

Commit e3a1579

Browse files
committed
QL: add query detecting upper-case acronyms
1 parent df9533f commit e3a1579

File tree

4 files changed

+77
-0
lines changed

4 files changed

+77
-0
lines changed

ql/ql/src/codeql/Locations.qll

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ class Location extends @location {
2828
int getNumLines() { result = getEndLine() - getStartLine() + 1 }
2929

3030
/** Gets a textual representation of this element. */
31+
cached
3132
string toString() {
3233
exists(string filepath, int startline, int startcolumn, int endline, int endcolumn |
3334
hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) and

ql/ql/src/codeql_ql/ast/internal/Module.qll

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,15 @@ private class ContainerOrModule extends TContainerOrModule {
1414
) {
1515
none()
1616
}
17+
18+
/** Gets the kind of this file/module/folder. */
19+
string getKind() {
20+
this = TFile(_) and result = "file"
21+
or
22+
this = TModule(_) and result = "module"
23+
or
24+
this = TFolder(_) and result = "folder"
25+
}
1726
}
1827

1928
private class TFileOrModule = TFile or TModule;
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import ql
2+
3+
/**
4+
* Gets the name for a `node` that defines something in a QL program.
5+
* E.g. a predicate, class, or module definition.
6+
*/
7+
string getName(AstNode node, string kind) {
8+
result = node.(Class).getName() and kind = "class"
9+
or
10+
// not including CharPreds or db relations. The remaining are: classlessPredicate, classPredicate, newTypeBranch.
11+
result = node.(ClasslessPredicate).getName() and
12+
kind = "classlessPredicate"
13+
or
14+
result = node.(ClassPredicate).getName() and
15+
kind = "classPredicate"
16+
or
17+
result = node.(NewTypeBranch).getName() and
18+
kind = "newtypeBranch"
19+
or
20+
result = node.(NewType).getName() and
21+
kind = "newtype"
22+
or
23+
result = node.(VarDecl).getName() and
24+
kind = "variable" and
25+
not node = any(FieldDecl f).getVarDecl()
26+
or
27+
result = node.(FieldDecl).getName() and kind = "field"
28+
or
29+
result = node.(Module).getName() and kind = "module"
30+
}
31+
32+
/**
33+
* Holds if `name` seems to contain an upper-cased acronym that could be pascal-cased.
34+
* `name` is the name of `node`, and `kind` describes what kind of definition `node` is.
35+
*/
36+
predicate shouldBePascalCased(string name, AstNode node, string kind) {
37+
name = getName(node, kind) and
38+
(
39+
// when the acronym is followed by something, then there must be 4 upper-case letters
40+
name.regexpMatch(".*[A-Z]{4,}([^A-Z]).*")
41+
or
42+
// when the acronym is the last part, then we only require 3 upper-case letters
43+
name.regexpMatch(".*[A-Z]{3,}$")
44+
) and
45+
not node.hasAnnotation("deprecated") and
46+
// allowed upper-case acronyms.
47+
not name.regexpMatch(".*(PEP|AES|DES|EOF).*") and
48+
not (name.regexpMatch("T[A-Z]{3}[^A-Z].*") and node instanceof NewTypeBranch) and
49+
not name.toUpperCase() = name // We are OK with fully-uppercase names.
50+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
/**
2+
* @name Acronyms should be PascalCase/camelCase.
3+
* @description Acronyms should be PascalCase/camelCase instead of upper-casing all the letters.
4+
* @kind problem
5+
* @problem.severity warning
6+
* @id ql/acronyms-should-be-pascal-case
7+
* @tags correctness
8+
* maintainability
9+
* @precision high
10+
*/
11+
12+
import ql
13+
import codeql_ql.style.AcronymsShouldBeCamelCaseQuery
14+
15+
from string name, AstNode node
16+
where shouldBePascalCased(name, node, _)
17+
select node, "Acronyms should be PascalCase/camelCase"

0 commit comments

Comments
 (0)