Skip to content

[Query] Inspect Recursive Functions

github-actions[bot] edited this page Jul 21, 2026 · 1 revision

This document was generated from 'src/documentation/wiki-query.ts' on 2026-07-20, 17:48:26 UTC presenting an overview of flowR's query API (v2.12.3). Please do not edit this file/wiki page directly.

Inspect Recursive Functions Query [overview]

Determine whether functions are recursive
This query is requested with the type inspect-recursion.
Run in the REPL: :query @inspect-recursion [(<crit>;...)] <code | file://path>

With this query you can identify which functions in the code are recursive. Please note, that functions that may be recursive due to indirect calls are also considered recursive.

Using the example code fact <- function(n) { if(n <= 1) 1 else n * fact(n - 1) } the following query returns the information for all identified function definitions whether they are recursive:

[ { "type": "inspect-recursion" } ]

(This can be shortened to @inspect-recursion when used with the REPL command :query).

Results (prettified and summarized):

Query: inspect-recursion (7ms)
  - Function 21 (1.9-57) is recursive
All queries together required ≈7 ms (1ms accuracy, total 8 ms)

Show Detailed Results as Json

The analysis required 8.1 ms (including parsing and normalization and the query) within the generation environment.

In general, the JSON contains the Ids of the nodes in question as they are present in the normalized AST or the dataflow graph of flowR. Please consult the Interface wiki page for more information on how to get those.

{
  "inspect-recursion": {
    ".meta": {
      "timing": 7
    },
    "recursive": {
      "21": true
    }
  },
  ".meta": {
    "timing": 7
  }
}
Original Code
fact <- function(n) { if(n <= 1) 1 else n * fact(n - 1) }
Dataflow Graph of the R Code

The analysis required 4.8 ms (including parse and normalize, using the r-shell engine) within the generation environment. No signature database is mounted for these generated graphs, so library() calls attach no package exports; base-R names are still qualified via the generated base-package store (e.g. acf as stats::acf). We encountered no unknown side effects during the analysis.

flowchart LR
    21["`*#91;RFunctionDefinition#93;* **function**
      *1.9-57* (**id: 21**)`"]

subgraph "flow-21" [function 21]
    1["`*#91;RSymbol#93;* **n**
      *1.18* (**id: 1**, v: )`"]
    5(["`*#91;RSymbol#93;* **n**
      *1.26* (**id: 5**)`"])
    6{{"`*#91;RNumber#93;* **1**
      *1.31* (**id: 6**)`"}}
   %% No edges found for 6
    7[["`*#91;RBinaryOp#93;* base#58;#58;**#60;=**
      *1.26-31* (**id: 7**)
    arg: (5, 6)`"]]
    built-in:__["`Built-In:
#60;=`"]
    style built-in:__ stroke:gray,fill:gray,stroke-width:2px,opacity:.8;
    8{{"`*#91;RNumber#93;* **1**
      *1.34* (**id: 8**, 19+)`"}}
   %% No edges found for 8
    10(["`*#91;RSymbol#93;* **n**
      *1.41* (**id: 10**)`"])
    12(["`*#91;RSymbol#93;* **n**
      *1.50* (**id: 12**)`"])
    13{{"`*#91;RNumber#93;* **1**
      *1.54* (**id: 13**)`"}}
   %% No edges found for 13
    14[["`*#91;RBinaryOp#93;* base#58;#58;**#45;**
      *1.50-54* (**id: 14**, 19-)
    arg: (12, 13)`"]]
    built-in:-["`Built-In:
#45;`"]
    style built-in:- stroke:gray,fill:gray,stroke-width:2px,opacity:.8;
    %% Environment of 16 [level: 1]:
    %% Built-in
    %% 1----------------------------------------
    %% 2----------------------------------------
    %%   n: {**n** (id: 1, type: Parameter, def. @2)}
    16[["`*#91;RFunctionCall#93;* **fact**
      *1.45-55* (**id: 16**, 19-)
    arg: (14)`"]]
    17[["`*#91;RBinaryOp#93;* base#58;#58;**#42;**
      *1.41-55* (**id: 17**, 19-)
    arg: (10, 16)`"]]
    built-in:_["`Built-In:
#42;`"]
    style built-in:_ stroke:gray,fill:gray,stroke-width:2px,opacity:.8;
    19[["`*#91;RIfThenElse#93;* base#58;#58;**if**
      *1.23-55* (**id: 19**)
    arg: (7, 8, 17)`"]]
    built-in:if["`Built-In:
if`"]
    style built-in:if stroke:gray,fill:gray,stroke-width:2px,opacity:.8;
    20[["`*#91;RExpressionList#93;* base#58;#58;**#123;**
      *1.21* (**id: 20**)
    arg: (19)`"]]
    style 8 stroke:purple,stroke-width:4px; 
end
   %% No edges found for 21
    0["`*#91;RSymbol#93;* **fact**
      *1.1-4* (**id: 0**, v: 21)`"]
    22[["`*#91;RBinaryOp#93;* base#58;#58;**#60;#45;**
      *1.1-57* (**id: 22**)
    arg: (0, 21)`"]]
    built-in:_-["`Built-In:
#60;#45;`"]
    style built-in:_- stroke:gray,fill:gray,stroke-width:2px,opacity:.8;
    1 -->|"def-by-on-call"| 14
    5 -->|"reads"| 1
    7 -->|"reads, arg"| 5
    7 -->|"reads, arg"| 6
    7 -.->|"reads, calls"| built-in:__
    linkStyle 4 stroke:gray;
    10 -->|"reads"| 1
    12 -->|"reads"| 1
    14 -->|"reads, arg"| 12
    14 -->|"reads, arg"| 13
    14 -.->|"reads, calls"| built-in:-
    linkStyle 9 stroke:gray;
    14 -->|"def-on-call"| 1
    14 -->|"CD-False"| 19
    linkStyle 11 stroke:gray,color:gray;
    16 -->|"reads, arg"| 14
    16 -->|"calls"| 21
    16 -->|"returns"| 19
    16 -->|"def-on-call, def-by-on-call"| 0
    16 -->|"CD-False"| 19
    linkStyle 16 stroke:gray,color:gray;
    17 -->|"reads, arg"| 10
    17 -->|"reads, arg"| 16
    17 -.->|"reads, calls"| built-in:_
    linkStyle 19 stroke:gray;
    17 -->|"CD-False"| 19
    linkStyle 20 stroke:gray,color:gray;
    19 -->|"returns, arg"| 8
    19 -->|"returns, arg"| 17
    19 -->|"reads, arg"| 7
    19 -.->|"reads, calls"| built-in:if
    linkStyle 24 stroke:gray;
    20 -->|"returns, arg"| 19
    20 -.->|"reads, calls"| built-in:_
    linkStyle 26 stroke:gray;
21 -.-|function| flow-21

    0 -->|"defined-by"| 21
    0 -->|"defined-by"| 22
    22 -->|"reads, arg"| 21
    22 -->|"returns, arg"| 0
    22 -.->|"reads, calls"| built-in:_-
    linkStyle 32 stroke:gray;
Loading

This query also supports a slicing criterion based query mode that only returns information for functions matching the given criteria:

[
  {
    "type": "inspect-recursion",
    "filter": [
      "1@function"
    ]
  }
]

(This can be shortened to @inspect-recursion (1@function) "fact <- function(n) { if(n <= 1) 1 else n * fact(n - 1) }" when used with the REPL command :query).

Results (prettified and summarized):

Query: inspect-recursion (8ms)
  - Function 21 (1.9-57) is recursive
All queries together required ≈8 ms (1ms accuracy, total 10 ms)

Show Detailed Results as Json

The analysis required 9.6 ms (including parsing and normalization and the query) within the generation environment.

In general, the JSON contains the Ids of the nodes in question as they are present in the normalized AST or the dataflow graph of flowR. Please consult the Interface wiki page for more information on how to get those.

{
  "inspect-recursion": {
    ".meta": {
      "timing": 8
    },
    "recursive": {
      "21": true
    }
  },
  ".meta": {
    "timing": 8
  }
}
Original Code
fact <- function(n) { if(n <= 1) 1 else n * fact(n - 1) }
Dataflow Graph of the R Code

The analysis required 5.1 ms (including parse and normalize, using the r-shell engine) within the generation environment. No signature database is mounted for these generated graphs, so library() calls attach no package exports; base-R names are still qualified via the generated base-package store (e.g. acf as stats::acf). We encountered no unknown side effects during the analysis.

flowchart LR
    21["`*#91;RFunctionDefinition#93;* **function**
      *1.9-57* (**id: 21**)`"]

subgraph "flow-21" [function 21]
    1["`*#91;RSymbol#93;* **n**
      *1.18* (**id: 1**, v: )`"]
    5(["`*#91;RSymbol#93;* **n**
      *1.26* (**id: 5**)`"])
    6{{"`*#91;RNumber#93;* **1**
      *1.31* (**id: 6**)`"}}
   %% No edges found for 6
    7[["`*#91;RBinaryOp#93;* base#58;#58;**#60;=**
      *1.26-31* (**id: 7**)
    arg: (5, 6)`"]]
    built-in:__["`Built-In:
#60;=`"]
    style built-in:__ stroke:gray,fill:gray,stroke-width:2px,opacity:.8;
    8{{"`*#91;RNumber#93;* **1**
      *1.34* (**id: 8**, 19+)`"}}
   %% No edges found for 8
    10(["`*#91;RSymbol#93;* **n**
      *1.41* (**id: 10**)`"])
    12(["`*#91;RSymbol#93;* **n**
      *1.50* (**id: 12**)`"])
    13{{"`*#91;RNumber#93;* **1**
      *1.54* (**id: 13**)`"}}
   %% No edges found for 13
    14[["`*#91;RBinaryOp#93;* base#58;#58;**#45;**
      *1.50-54* (**id: 14**, 19-)
    arg: (12, 13)`"]]
    built-in:-["`Built-In:
#45;`"]
    style built-in:- stroke:gray,fill:gray,stroke-width:2px,opacity:.8;
    %% Environment of 16 [level: 1]:
    %% Built-in
    %% 1----------------------------------------
    %% 2----------------------------------------
    %%   n: {**n** (id: 1, type: Parameter, def. @2)}
    16[["`*#91;RFunctionCall#93;* **fact**
      *1.45-55* (**id: 16**, 19-)
    arg: (14)`"]]
    17[["`*#91;RBinaryOp#93;* base#58;#58;**#42;**
      *1.41-55* (**id: 17**, 19-)
    arg: (10, 16)`"]]
    built-in:_["`Built-In:
#42;`"]
    style built-in:_ stroke:gray,fill:gray,stroke-width:2px,opacity:.8;
    19[["`*#91;RIfThenElse#93;* base#58;#58;**if**
      *1.23-55* (**id: 19**)
    arg: (7, 8, 17)`"]]
    built-in:if["`Built-In:
if`"]
    style built-in:if stroke:gray,fill:gray,stroke-width:2px,opacity:.8;
    20[["`*#91;RExpressionList#93;* base#58;#58;**#123;**
      *1.21* (**id: 20**)
    arg: (19)`"]]
    style 8 stroke:purple,stroke-width:4px; 
end
   %% No edges found for 21
    0["`*#91;RSymbol#93;* **fact**
      *1.1-4* (**id: 0**, v: 21)`"]
    22[["`*#91;RBinaryOp#93;* base#58;#58;**#60;#45;**
      *1.1-57* (**id: 22**)
    arg: (0, 21)`"]]
    built-in:_-["`Built-In:
#60;#45;`"]
    style built-in:_- stroke:gray,fill:gray,stroke-width:2px,opacity:.8;
    1 -->|"def-by-on-call"| 14
    5 -->|"reads"| 1
    7 -->|"reads, arg"| 5
    7 -->|"reads, arg"| 6
    7 -.->|"reads, calls"| built-in:__
    linkStyle 4 stroke:gray;
    10 -->|"reads"| 1
    12 -->|"reads"| 1
    14 -->|"reads, arg"| 12
    14 -->|"reads, arg"| 13
    14 -.->|"reads, calls"| built-in:-
    linkStyle 9 stroke:gray;
    14 -->|"def-on-call"| 1
    14 -->|"CD-False"| 19
    linkStyle 11 stroke:gray,color:gray;
    16 -->|"reads, arg"| 14
    16 -->|"calls"| 21
    16 -->|"returns"| 19
    16 -->|"def-on-call, def-by-on-call"| 0
    16 -->|"CD-False"| 19
    linkStyle 16 stroke:gray,color:gray;
    17 -->|"reads, arg"| 10
    17 -->|"reads, arg"| 16
    17 -.->|"reads, calls"| built-in:_
    linkStyle 19 stroke:gray;
    17 -->|"CD-False"| 19
    linkStyle 20 stroke:gray,color:gray;
    19 -->|"returns, arg"| 8
    19 -->|"returns, arg"| 17
    19 -->|"reads, arg"| 7
    19 -.->|"reads, calls"| built-in:if
    linkStyle 24 stroke:gray;
    20 -->|"returns, arg"| 19
    20 -.->|"reads, calls"| built-in:_
    linkStyle 26 stroke:gray;
21 -.-|function| flow-21

    0 -->|"defined-by"| 21
    0 -->|"defined-by"| 22
    22 -->|"reads, arg"| 21
    22 -->|"returns, arg"| 0
    22 -.->|"reads, calls"| built-in:_-
    linkStyle 32 stroke:gray;
Loading
Implementation Details

Responsible for the execution of the Inspect Recursive Functions Query query is executeRecursionQuery in ./src/queries/catalog/inspect-recursion-query/inspect-recursion-query-executor.ts.

Clone this wiki locally