зеркало из https://github.com/github/codeql.git
15 KiB
15 KiB
QL Style Guide
Introduction
This document describes how to format the QL code you contribute to this repository. It covers aspects such as layout, white-space, naming and documentation. Adhering to consistent standards makes code easier to read and maintain. Of course, these are only guidelines, and can be overridden as the need arises on a case-by-case basis. Where existing code deviates from these guidelines, prefer consistency with the surrounding code.
Words in italic are defined in the Glossary.
Indentation
- Always use 2 spaces for indentation.
- Always indent:
- The body of a module, newtype, class or predicate
- The second and subsequent lines after you use a line break to split a long line
- The body of a
from
,where
orselect
clause where it spans multiple lines - The body of a quantifier that spans multiple lines
Examples
module Helpers {
/** ... */
class X ... {
/** ... */
int getNumberOfChildren () {
result = count(int child |
exists(this.getChild(child))
)
}
}
}
from Call c, string reason
where isDeprecated(c, reason)
select c, "This call to '$@' is deprecated because " + reason + ".",
c.getTarget(), c.getTarget().getName()
Line breaks
- Use UNIX line endings.
- Lines must not exceed 100 characters.
- Long lines should be split with a line break, and the following lines must be indented one level until the next "regular" line break.
- There should be a single blank line:
- Between the file documentation and the first
import
- Before each declaration, except for the first declaration in a body
- Before the
from
-where
-select
section in a query file
- Between the file documentation and the first
- Avoid two or more adjacent blank lines.
- There must be a new line after the annotations
cached
,pragma
,language
andbindingset
. Other annotations do not have a new line. - There should not be additional blank lines within a predicate.
- There may be a new line:
- Immediately after the
from
,where
orselect
keywords in a query. - Immediately after
if
,then
, orelse
keywords.
- Immediately after the
- Avoid other line breaks in declarations, other than to break long lines.
- When operands of binary operators span two lines, the operator should be placed at the end of the first line.
- If the parameter list needs to be broken across multiple lines then there must be a line break after the opening
(
, the parameter declarations indented one level, and the) {
must be on its own line at the outer indentation.
Examples
cached
private int getNumberOfParameters() {
...
}
predicate methodStats(
string qualifiedName, string name, int numberOfParameters,
int numberOfStatements, int numberOfExpressions, int linesOfCode,
int nestingDepth, int numberOfBranches
) {
...
}
from Method main
where main.getName() = "Main"
select main, "This is the program entry point."
from Method main
where
main.getName() = "Main" and
main.getNumberOfParameters() = 0
select main, "Main method has no parameters."
if x.isPublic()
then result = "public"
else result = "private"
if
x.isPublic()
then
result = "public"
else
result = "private"
if x.isPublic()
then result = "public"
else
if x.isPrivate()
then result = "private"
else result = "protected"
Braces
- Braces follow Stroustrup style. The opening
{
must be placed at the end of the preceding line. - The closing
}
must be placed on its own line, indented to the outer level, or be on the same line as the opening{
. - Braces of empty blocks may be placed on a single line, with a single space separating the braces.
- Short predicates, not exceeding the maximum line width, may be placed on a single line, with a space following the opening brace and preceding the closing brace.
Examples
class ThrowException extends ThrowExpr {
Foo() {
this.getTarget() instanceof ExceptionClass
}
override string toString() { result = "Throw Exception" }
}
Spaces
- There must be a space or line break:
- Surrounding each
=
and|
- After each
,
- Surrounding each
- There should be a space or line break:
- Surrounding each binary operator, which must be balanced
- Surrounding
..
in a range - Exceptions to this may be made to save space or to improve readability.
- Avoid other spaces, for example:
- After a quantifier/aggregation keyword
- After the predicate name in a call
- Inside brackets used for calls, single-line quantifiers, and parenthesised formulas
- Surrounding a
.
- Inside the opening or closing
[ ]
in a range expression - Inside casts
a.(X)
- Avoid multiple spaces, except for indentation, and avoid additional indentation to align formulas, parameters or arguments.
- Do not put whitespace on blank lines, or trailing on the end of a line.
- Do not use tabs.
Examples
cached
private predicate foo(Expr e, Expr p) {
exists(int n |
n in [0 .. 1] |
e = p.getChild(n + 1)
)
}
Naming
- Use PascalCase for:
class
namesmodule
namesnewtype
names
- Use camelCase for:
- Predicate names
- Variable names
- Newtype predicate names should begin with
T
. - Predicates that have a result should be named
get...
- Predicates that can return multiple results should be named
getA...
orgetAn...
- Predicates that don't have a result or parameters should be named
is...
orhas...
- Avoid underscores in names.
- Avoid short or single-letter names for classes, predicates and fields.
- Short or single letter names for parameters and quantifiers may be used provided that they are sufficiently clear.
- Use names as they are used in the target-language specification.
- Use American English.
Examples
/** ... */
predicate calls(Callable caller, Callable callee) {
...
}
/** ... */
class Type extends ... {
/** ... */
string getName() { ... }
/** ... */
predicate declares(Member m) { ... }
/** ... */
predicate isGeneric() { ... }
/** ... */
Type getTypeParameter(int n) { ... }
/** ... */
Type getATypeParameter() { ... }
}
Documentation
General requirements:
- Documentation must adhere to the QLDoc specification.
- Use
/** ... */
for documentation, even for single line comments. - For single-line documentation, the
/**
and*/
are written on the same line as the comment. - For multi-line documentation, the
/**
and*/
are written on separate lines. There is a*
preceding each comment line, aligned on the first*
. - Use full sentences, with capital letters and full stops.
- Use American English.
- Documentation comments should be appropriate for users of the code.
- Documentation for maintainers of the code must use normal comments.
Documentation for specific items:
- Public declarations must be documented.
- Non-public declarations should be documented.
- Declarations in query files should be documented.
- Library files (
.qll
files) should be have a documentation comment at the top of the file. - Query files, except for tests, must have a QLDoc query documentation comment at the top of the file.
- Predicates that do not have a result should be documented
/** Holds if ... */
- Predicates that have a result should be documented
/** Gets ... */
- All predicate parameters should be referred to in the predicate documentation.
- Reference names, such as types and parameters, using backticks
`
. - Give examples of code in the target language, enclosed in
```
or`
. - Classes should be documented in the singular, for example
/* An expression. */
- Where a class denotes a generic concept with subclasses, list those subclasses.
- Declarations that are deprecated should be documented as
DEPRECATED: ...
- Declarations that are for internal use should be documented as
INTERNAL: Do not use
.
Examples
/** Provides logic for determining constant expressions. */
/**
* Holds if the qualifier of this call has type `qualifierType`.
* `isExactType` indicates whether the type is exact, that is, whether
* the qualifier is guaranteed not to be a subtype of `qualifierType`.
*/
/**
* A delegate declaration, for example
* ```
* delegate void Logger(string text);
* ```
*/
class Delegate extends ...
/**
* An element that can be called.
*
* Either a method (`Method`), a constructor (`Constructor`), a destructor
* (`Destructor`), an operator (`Operator`), an accessor (`Accessor`),
* an anonymous function (`AnonymousFunctionExpr`), or a local function
* (`LocalFunction`).
*/
class Callable extends ...
/** DEPRECATED: Use `getAnExpr()` instead. */
deprecated Expr getInitializer()
/**
* INTERNAL: Do not use.
*/
Formulas
- Prefer one conjunct per line.
- Write the
and
at the end of the line. This also applies inwhere
clauses. - Prefer to write the
or
keyword on its own line. - The
or
keyword may be written at the end of a line, or within a line, provided that it has noand
operands. - Single-line formulas may be used in order to save space or add clarity, particularly in the body of a quantifier/aggregation.
- Always use brackets to clarify the precedence of:
implies
if
-then
-else
- Avoid using brackets to clarify the precedence of:
not
and
or
- Parenthesised formulas can be written:
- Within a single line. There should not be an additional space following the opening parenthesis or preceding the closing parenthesis.
- Spanning multiple lines. The opening parenthesis should be placed at the end of the preceding line, the body should be indented one level, and the closing bracket should be placed on a new line at the outer indentation.
- Quantifiers/aggregations can be written:
- Within a single line. In this case, there is no space to the inside of the parentheses, or after the quantifier keyword.
- Across multiple lines. In this case, type declarations are on the same line as the quantifier with the first
|
at the same line as the quantifier, the second|
must be at the end of the same line as the quantifier or on its own line at the outer indentation, and the body of the quantifier must be indented one level. The closing)
is written on a new line, at the outer indentation. If the type declarations need to be broken across multiple lines then there must must be a line break after the opening(
, the type declarations indented one level, and the first|
on its own line at the outer indentation.
if
-then
-else
can be written:- On a single line
- With the body after the
if
/then
/else
keyword - With the body indented on the next line
- Always parenthesise the
else
part if it is a compound formula.
- If an
if
-then
-else
is broken across multiple lines then thethen
andelse
keywords should be at the start of lines aligned with theif
. - The
and
andelse
keywords may be placed on the same line as the closing parenthesis. - The
and
andelse
keywords may be "cuddled":) else (
- Always qualify calls to predicates of the same class with
this
. - Prefer postfix casts
a.(Expr)
to prefix casts(Expr)a
.
Examples
argumentType.isImplicitlyConvertibleTo(parameterType)
or
argumentType instanceof NullType and
result.getParameter(i).isOut() and
parameterType instanceof SimpleType
or
reflectionOrDynamicArg(argumentType, parameterType)
this.getName() = "Finalize" and not exists(this.getAParameter())
e1.getType() instanceof BoolType and (
b1 = true
or
b1 = false
) and (
b2 = true
or
b2 = false
)
if e1 instanceof BitwiseOrExpr or e1 instanceof LogicalOrExpr
then (
impliesSub(e1.(BinaryOperation).getAnOperand(), e2, b1, b2) and
b1 = false
) else (
e1.getType() instanceof BoolType and
e1 = e2 and
b1 = b2 and
(b1 = true or b1 = false)
)
(x instanceof Exception implies x.isPublic()) and y instanceof Exception
x instanceof Exception implies (x.isPublic() and y instanceof Exception)
exists(Type arg | arg = this.getAChild() | arg instanceof TypeParameter)
exists(Type qualifierType |
this.hasNonExactQualifierType(qualifierType)
|
result = getANonExactQualifierSubType(qualifierType)
)
methods = count(Method m | t = m.getDeclaringType() and not ilc(m))
if n = 0 then result = 1 else result = n * f(n - 1)
if n = 0
then result = 1
else result = n * f(n - 1)
if
n = 0
then
result = 1
else
result = n * f(n - 1)
if exists(this.getContainingType())
then (
result = "A nested class" and
parentName = this.getContainingType().getFullyQualifiedName()
) else (
result = parentName + "." + this.getName() and
parentName = this.getNamespace().getFullyQualifiedName()
)
Glossary
Phrase | Meaning |
---|---|
annotation | An additional specifier used to modify a declaration, such as private , override , deprecated , pragma , bindingset , or cached . |
body | The text inside { } , ( ) , or each section of an if -then -else or from -where -select . |
binary operator | An operator with two operands, such as comparison operators, and , or , implies , or arithmetic operators. |
call | A formula that invokes a predicate, e.g. this.isStatic() or calls(a,b) . |
conjunct | A formula that is an operand to an and . |
declaration | A class, module, predicate, field or newtype. |
disjunct | A formula that is an operand to an or . |
formula | A logical expression, such as A = B , a call, a quantifier, and , or , not , in or instanceof . |
should/should not/avoid/prefer | Adhere to this rule wherever possible, where it makes sense. |
may/can | This is a reasonable alternative, to be used with discretion. |
must/always/do not | Always adhere to this rule. |
quantifier/aggregation | exists , count , strictcount , any , forall , forex and so on. |
variable | A parameter to a predicate, a field, a from variable, or a variable introduced by a quantifier or aggregation. |