Skip to content

Document path statements #71

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 10 commits into
base: master
Choose a base branch
from
102 changes: 51 additions & 51 deletions docs/concepts/CLQL.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ To limit the above query to match classes with a particular name, add a "name" p

```
@review.comment
common.method(depth = any):
method(depth = any):

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it encouraged to use common lexicon facts when go lexicon facts would do? Am I currently able to entwine common lexicon facts with go lexicon facts?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah you can, but don't worry about it for now. The common lexicon isn't properly implemented yet. common.func_decl will match go.func_decl or any other kind of func_decl, but we haven't put any effort into making sure the different names for func_decl in various languages map to the same common fact.

name == "myFunc"
```

Expand All @@ -67,14 +67,14 @@ This query returns all methods with the name "myFunc". Note that the query decor
Properties can be of type string, float, and int. The following finds all int literals with the value 8:

```
common.int_lit(depth = any):
int_lit(depth = any):
value == 8
```

This query finds float literals with the value 8.7:

```
common.float_lit(depth = any):
float_lit(depth = any):
value: 8.7
```

Expand All @@ -84,7 +84,7 @@ common.float_lit(depth = any):

The comparison operators >, <, >=, and <= are available for floats and ints. The following finds all int literals above negative 3:
```
common.int_lit(depth = any):
int_lit(depth = any):
value: > -3
```

Expand All @@ -95,25 +95,25 @@ common.int_lit(depth = any):
Facts can take any number of facts and properties as children, forming a query with a tree struct of arbitrary depth. A parent-child fact pair will match any parent element even if the child is not a direct descendant. The following query finds all the if statements inside a method called "myMethod", even those nested inside intermediate scopes (for loops etc):

```
common.method(depth = any):
method(depth = any):
name == "myMethod"
common.if_stmt(depth = any)
if_stmt(depth = any)
```

Any fact in a query can be decorated. If `class` is decorated, this query returns all classes named "myClass", but only if it has at least one method:

```
common.class(depth = any):
class(depth = any):
name: “myClass”
common.method(depth = any)
method(depth = any)
```

Any fact in a query can have properties. The following query finds all methods named "myMethod" on the all classes named "myClass":

```
common.class(depth = any):
class(depth = any):
name: “myClass”
common.method(depth = any):
method(depth = any):
name: “myMethod”
```

Expand All @@ -122,31 +122,31 @@ common.class(depth = any):
Facts use depth ranges to specify the depth at which they can be found below their parent. Depth ranges have two zero based numbers, representing the minimum and maximum depth to find the result at, inclusive and exclusive respectively. The following query finds any if statements that are direct children of their parent method, in other words, if statements at depth zero from methods:

```
common.method(depth = any):
common.if_stmt(depth = 0:1)
method(depth = any):
if_stmt(depth = 0:1)
```

This query finds if statements at (zero based) depths 3, 4, and 5:

```
common.method(depth = any):
common.if_stmt(depth = 3:6)
method(depth = any):
if_stmt(depth = 3:6)
```

A depth range where the maximum is not greater than the minimum, i.e. `(depth = 5:5})` or `({depth: 6:0)`, will give an error.

Depth ranges specifying a single depth can be described with a single number. This query finds direct children at depth zero:

```
common.method(depth = any):
common.if_stmt(depth = 0)
method(depth = any):
if_stmt(depth = 0)
```

Indices in a depth range can range from 0 to positive infinity. Positive infinity is represented by leaving the second index empty. This query finds all methods, and all their descendant if_statements from depth 5 onwards:

```
common.method(depth = any):
common.if_stmt(depth = 5:)
method(depth = any):
if_stmt(depth = 5:)
```

Note: The depth range on top level facts, like `method` in the previous examples, determines the depth from the base context to that fact. In this case the base context contains a single program. However, it can be configured to refer to any context, typically a single repository or the root of the graph on which all queryable data hangs.
Expand All @@ -158,10 +158,10 @@ Note: The depth range on top level facts, like `method` in the previous examples
The following query will find a method with a foreach loop, a for loop, and a while loop in that order:

```
common.method(depth = any):
common.for_stmt
common.foreach_stmt
common.while_stmt
method(depth = any):
for_stmt
foreach_stmt
while_stmt
```

<!--TODO(blakemscurr): Explain the <lexicon>.element fact-->
Expand All @@ -173,26 +173,26 @@ common.method(depth = any):
Exlude allows queries to match children that *do not* have a given property or child fact. Excluded facts and properties are children of an `exclude` operator. The following query finds all classes except those named "classA":

```
common.class(depth = any):
class(depth = any):
exclude:
name == "classA"
```

This query finds all classes with a method that is not called String:

```
common.class(depth = any):
common.method:
class(depth = any):
method:
exclude:
name: “String”
```

The placement of the exclude operator has a significant effect on the query's meaning - this similar query finds all classes without String methods:

```
common.class(depth = any):
class(depth = any):
exlude:
common.method:
method:
name: “String”
```

Expand All @@ -201,11 +201,11 @@ The exclude operator in the above query can be read as excluding all methods wit
Excluding a fact does not affect its siblings. The following query finds all String methods that use an if statement, but don’t use a foreach statement:

```
common.method(depth = any):
method(depth = any):
name: “String”
common.if_stmt
if_stmt
exclude:
common.foreach_stmt
foreach_stmt
```

An excluded fact will not return a result and therefore cannot be decorated.
Expand All @@ -216,10 +216,10 @@ An excluded fact will not return a result and therefore cannot be decorated.
Exclusions can be arbitrarily nested. The following query finds methods which only return nil or return nothing, that is, it finds all methods except those with non-nil values in their return statements:

```
common.method:
method:
exclude:
common.return_stmt(depth = any):
common.literal:
return_stmt(depth = any):
literal:
exclude:
name == "nil"
```
Expand All @@ -232,12 +232,12 @@ Facts nested under multiple excludes still do not return results and cannot be d
Include allows queries to match patterns without a given parent. The following query is a simple attempt at finding infinitely recursing functions. It works by finding functions that call themselves without an if statement to halt recursion:

```
common.func:
func:
name as funcName
exclude:
common.if_stmt:
if_stmt:
include:
common.func_call:
func_call:
name as funcName
```

Expand All @@ -253,12 +253,12 @@ Results under include statements appear as children of the parent of the corresp
A fact with multiple children will match against elements of the code that have child1 *and* child2 *and* child3 etc. The `any_of` operator overrides the implicit "and". The following query finds all String methods that use basic loops:

```
common.method(depth = any):
method(depth = any):
name: “String”
any_of:
common.foreach_stmt
common.while_stmt
common.for_stmt
foreach_stmt
while_stmt
for_stmt
```
<!-- TODO(blakemscurr) n_of-->

Expand Down Expand Up @@ -418,13 +418,13 @@ Facts that do not have a parent-child relationship can be compared by assigning
The following query compares two classes (which do have a parent-child relationship) and returns the methods which both classes implement:

```
common.class(depth = any):
class(depth = any):
name: “classA”
common.method:
method:
name as methodName
common.class(depth = any):
class(depth = any):
name: “classB”
common.method:
method:
name as methodName
```

Expand All @@ -441,9 +441,9 @@ Functions allow users to execute arbitrary logic on variables. There are two typ
A resolver function is used on the right hand side of a property assertion. In the following example, we assert that the name property of the method fact is equal to the value returned from the concat function:

```
common.class(depth = any):
class(depth = any):
name as className
common.method:
method:
name == concat("New", className)
```

Expand All @@ -454,8 +454,8 @@ Asserter functions return a Boolean value and can only be called on their own li
The following query uses the inbuilt `regex` function to match methods with capitalised names:

```
common.class(depth = any):
common.method:
class(depth = any):
method:
name as methodName
regex(/^[A-Z]/, methodName) // pass in the methodName variable to the regex function and assert that the name is capitalised.
```
Expand All @@ -482,10 +482,10 @@ tenets:
This method appears to be a constructor
name: constructor-finder
query: |
common.class(depth = any):
class(depth = any):
name as className
@review.comment
common.method:
method:
name == newConcat("New", className)
```

Expand All @@ -506,7 +506,7 @@ tenets:
This method has a long name
name: long-method-name
query: |
common.method:
method:
name as methodName
stringLengthGreaterThan(methodName, 15)
```
Expand Down