Skip to content

Commit e625d17

Browse files
authored
Merge pull request #8374 from erik-krogh/nonDocBlock
QL: add query detecting block comments in a position where a QLDoc should be
2 parents f006cd0 + 122ab6e commit e625d17

File tree

16 files changed

+83
-18
lines changed

16 files changed

+83
-18
lines changed

cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowUtil.qll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -431,7 +431,7 @@ class SsaPhiNode extends Node, TSsaPhiNode {
431431

432432
SsaPhiNode() { this = TSsaPhiNode(phi) }
433433

434-
/* Get the phi node associated with this node. */
434+
/** Gets the phi node associated with this node. */
435435
Ssa::PhiNode getPhiNode() { result = phi }
436436

437437
override Declaration getEnclosingCallable() { result = this.getFunction() }

csharp/ql/lib/semmle/code/csharp/exprs/Creation.qll

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -435,10 +435,10 @@ class AnonymousFunctionExpr extends Expr, Callable, Modifiable, @anonymous_funct
435435
* A lambda expression, for example `(int x) => x + 1`.
436436
*/
437437
class LambdaExpr extends AnonymousFunctionExpr, @lambda_expr {
438-
/* Holds if this lambda expression has explicit return type. */
438+
/** Holds if this lambda expression has explicit return type. */
439439
predicate hasExplicitReturnType() { lambda_expr_return_type(this, _) }
440440

441-
/* Gets the explicit return type of this lambda expression, if any. */
441+
/** Gets the explicit return type of this lambda expression, if any. */
442442
Type getExplicitReturnType() { lambda_expr_return_type(this, getTypeRef(result)) }
443443

444444
override string toString() { result = "(...) => ..." }

python/ql/lib/semmle/python/Flow.qll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -299,7 +299,7 @@ class ControlFlowNode extends @py_flow_node {
299299
exists(BasicBlock b, int i, int j | this = b.getNode(i) and other = b.getNode(j) and i < j)
300300
}
301301

302-
/* Holds if this CFG node is a branch */
302+
/** Holds if this CFG node is a branch */
303303
predicate isBranch() { py_true_successors(this, _) or py_false_successors(this, _) }
304304

305305
ControlFlowNode getAChild() { result = this.getExprChild(this.getBasicBlock()) }

python/ql/lib/semmle/python/dataflow/old/Implementation.qll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -991,7 +991,7 @@ int iterable_unpacking_descent(SequenceNode left_parent, ControlFlowNode left_de
991991
}
992992

993993
module Implementation {
994-
/* A call that returns a copy (or similar) of the argument */
994+
/** Holds if `tonode` is a call that returns a copy (or similar) of the argument `fromnode` */
995995
predicate copyCall(ControlFlowNode fromnode, CallNode tonode) {
996996
tonode.getFunction().(AttrNode).getObject("copy") = fromnode
997997
or

python/ql/lib/semmle/python/dataflow/old/Legacy.qll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import semmle.python.dataflow.TaintTracking
22
private import semmle.python.objects.ObjectInternal
33
import semmle.python.dataflow.Implementation
44

5-
/* Backwards compatibility with config-less taint-tracking */
5+
/** A configuration that provides backwards compatibility with config-less taint-tracking */
66
private class LegacyConfiguration extends TaintTracking::Configuration {
77
LegacyConfiguration() {
88
/* A name that won't be accidentally chosen by users */

python/ql/lib/semmle/python/dependencies/Dependencies.qll

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -158,13 +158,13 @@ private predicate defn_of_instance_attribute(Assign asgn, Class c, string name)
158158
)
159159
}
160160

161-
/* Whether asgn defines an attribute of a class */
161+
/** Holds if asgn defines an attribute of a class */
162162
private predicate defn_of_class_attribute(Assign asgn, Class c, string name) {
163163
asgn.getScope() = c and
164164
asgn.getATarget().(Name).getId() = name
165165
}
166166

167-
/* Holds if `value` is a value assigned to the `name`d attribute of module `m`. */
167+
/** Holds if `value` is a value assigned to the `name`d attribute of module `m`. */
168168
private predicate defn_of_module_attribute(ControlFlowNode value, Module m, string name) {
169169
exists(DefinitionNode def |
170170
def.getScope() = m and

python/ql/lib/semmle/python/objects/ObjectAPI.qll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ private import semmle.python.types.Builtins
1818

1919
class ObjectSource = Object;
2020

21-
/* Aliases for scopes */
21+
/** An alias for Function used for scopes */
2222
class FunctionScope = Function;
2323

2424
class ClassScope = Class;

python/ql/lib/semmle/python/pointsto/Base.qll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ int version_tuple_compare(Object t) {
120120
version_tuple_value(t) > major_minor() and result = 1
121121
}
122122

123-
/* Holds if `cls` is a new-style class if it were to have no explicit base classes */
123+
/** Holds if `cls` is a new-style class if it were to have no explicit base classes */
124124
predicate baseless_is_new_style(ClassObject cls) {
125125
cls.isBuiltin()
126126
or

python/ql/lib/semmle/python/pointsto/PointsTo.qll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2395,7 +2395,7 @@ module Types {
23952395
)
23962396
}
23972397

2398-
/* Holds if type inference failed to compute the full class hierarchy for this class for the reason given. */
2398+
/** Holds if type inference failed to compute the full class hierarchy for this class for the reason given. */
23992399
private predicate failedInference(ClassObjectInternal cls, string reason, int priority) {
24002400
strictcount(cls.(PythonClassObjectInternal).getScope().getADecorator()) > 1 and
24012401
reason = "Multiple decorators" and

python/ql/lib/semmle/python/security/strings/Common.qll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import python
22

3-
/* A call that returns a copy (or similar) of the argument */
3+
/** A call that returns a copy (or similar) of the argument */
44
deprecated predicate copy_call(ControlFlowNode fromnode, CallNode tonode) {
55
tonode.getFunction().(AttrNode).getObject("copy") = fromnode
66
or

python/ql/lib/semmle/python/types/ClassObject.qll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,7 @@ class ClassObject extends Object {
166166
not this.failedInference()
167167
}
168168

169-
/* Whether this class is abstract. */
169+
/** Holds if this class is abstract. */
170170
predicate isAbstract() {
171171
this.getMetaClass() = theAbcMetaClassObject()
172172
or

python/ql/src/Lexical/CommentedOutCode.qll

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -158,11 +158,11 @@ private predicate commented_out_code_block(Comment start, Comment end) {
158158
not commented_out_code(non_empty_following(end))
159159
}
160160

161-
/* A single line comment that appears to be commented out code */
161+
/** A single line comment that appears to be commented out code */
162162
class CommentedOutCodeLine extends Comment {
163163
CommentedOutCodeLine() { exists(CommentedOutCodeBlock b | b.contains(this)) }
164164

165-
/* Whether this commented-out code line is likely to be example code embedded in a larger comment. */
165+
/** Holds if this commented-out code line is likely to be example code embedded in a larger comment. */
166166
predicate maybeExampleCode() {
167167
exists(CommentedOutCodeBlock block |
168168
block.contains(this) and

python/ql/src/Resources/FileOpen.qll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ predicate passes_open_files(Variable v, ControlFlowNode test, boolean sense) {
6666
)
6767
}
6868

69-
/* Helper for `def_is_open` to give better join order */
69+
// Helper for `def_is_open` to give better join order
7070
private predicate passes_open_files(PyEdgeRefinement refinement) {
7171
passes_open_files(refinement.getSourceVariable(), refinement.getPredecessor().getLastNode(),
7272
refinement.getSense())

ql/ql/src/codeql_ql/ast/Ast.qll

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,16 @@ class QLDoc extends TQLDoc, AstNode {
156156
override string getAPrimaryQlClass() { result = "QLDoc" }
157157
}
158158

159+
class BlockComment extends TBlockComment, AstNode {
160+
QL::BlockComment comment;
161+
162+
BlockComment() { this = TBlockComment(comment) }
163+
164+
string getContents() { result = comment.getValue() }
165+
166+
override string getAPrimaryQlClass() { result = "BlockComment" }
167+
}
168+
159169
/**
160170
* The `from, where, select` part of a QL query.
161171
*/
@@ -1867,13 +1877,13 @@ class BinOpExpr extends TBinOpExpr, Expr {
18671877
/** Gets the left operand of the binary expression. */
18681878
Expr getLeftOperand() { none() } // overriden in subclasses
18691879

1870-
/* Gets the right operand of the binary expression. */
1880+
/** Gets the right operand of the binary expression. */
18711881
Expr getRightOperand() { none() } // overriden in subclasses
18721882

18731883
/** Gets the operator of the binary expression. */
18741884
FunctionSymbol getOperator() { none() } // overriden in subclasses
18751885

1876-
/* Gets an operand of the binary expression. */
1886+
/** Gets an operand of the binary expression. */
18771887
final Expr getAnOperand() { result = this.getLeftOperand() or result = this.getRightOperand() }
18781888
}
18791889

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ cached
66
newtype TAstNode =
77
TTopLevel(QL::Ql file) or
88
TQLDoc(QL::Qldoc qldoc) or
9+
TBlockComment(QL::BlockComment comment) or
910
TClasslessPredicate(QL::ClasslessPredicate pred) or
1011
TVarDecl(QL::VarDecl decl) or
1112
TFieldDecl(QL::Field field) or
@@ -146,6 +147,8 @@ QL::AstNode toQL(AST::AstNode n) {
146147
or
147148
n = TQLDoc(result)
148149
or
150+
n = TBlockComment(result)
151+
or
149152
n = TClasslessPredicate(result)
150153
or
151154
n = TVarDecl(result)
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/**
2+
* @name Block comment that is not QLDoc
3+
* @description Placing a block comment that could have been a QLDoc comment is an indication that it should have been a QLDoc comment.
4+
* @kind problem
5+
* @problem.severity warning
6+
* @id ql/non-doc-block
7+
* @tags maintainability
8+
* @precision very-high
9+
*/
10+
11+
import ql
12+
13+
predicate canHaveQLDoc(AstNode node) {
14+
node instanceof Class
15+
or
16+
node instanceof Module
17+
or
18+
node instanceof ClasslessPredicate
19+
or
20+
node instanceof ClassPredicate
21+
}
22+
23+
pragma[noinline]
24+
int getLineAboveNodeThatCouldHaveDoc(File file) {
25+
exists(AstNode node | canHaveQLDoc(node) |
26+
result = node.getLocation().getStartLine() - 1 and file = node.getLocation().getFile()
27+
)
28+
}
29+
30+
pragma[noinline]
31+
BlockComment getACommentThatCouldBeQLDoc(File file) {
32+
file = result.getLocation().getFile() and
33+
result.getLocation().getEndLine() = getLineAboveNodeThatCouldHaveDoc(file) and
34+
result.getLocation().getFile().getExtension() = "qll" and
35+
not result.getContents().matches("/**%")
36+
}
37+
38+
pragma[noinline]
39+
BlockComment getCommentAt(File file, int endLine) {
40+
result = getACommentThatCouldBeQLDoc(file) and
41+
result.getLocation().getEndLine() = endLine
42+
}
43+
44+
from AstNode node, BlockComment comment
45+
where
46+
canHaveQLDoc(node) and
47+
not exists(node.getQLDoc()) and
48+
not node.(ClassPredicate).isOverride() and // ignore override predicates
49+
not node.hasAnnotation("deprecated") and // ignore deprecated
50+
not node.hasAnnotation("private") and // ignore private
51+
comment = getCommentAt(node.getLocation().getFile(), node.getLocation().getStartLine() - 1)
52+
select comment, "Block comment could be QLDoc for $@.", node, "the below code"

0 commit comments

Comments
 (0)