The `symbols`

function is the key in our static analysis. It takes a s-expression, and returns a set of symbols defined by this s-expression.

For Constants, it returns an empty set.

```
(symbols 2) => #{}
(symbols true) => #{}
(symbols "foo") => #{}
(symbols :foo) => #{}
(symbols nil) => #{}
(symbols #"foobar") => #{}
(symbols *ns*) => #{}
```

For a symbol, `symbols`

returns a set with that symbol.

`(symbols 'x) => #{'x}`

`symbols`

goes into Clojure structures and aggergates symbols from there.

```
(symbols '(+ a b)) => #{'+ 'a 'b}
(symbols '[1 a 2 b]) => #{'a 'b}
(symbols {'x 'y}) => #{'x 'y}
(symbols #{'a 'b 3 4 5}) => #{'a 'b}
```

In a `let*`

special form, the bindings are removed from the returned set.

```
(symbols '(let* [x 1 y 2] (+ x y))) => #{'+}
(symbols '(let* [x a] x)) => #{'a}
```

`symbols`

expands macros, so it also handles the more familiar `let`

form

`(symbols '(let [x a] x a)) => #{'a}`

`loop`

is supported similarly.

`(symbols '(loop [x a y b] a x b y c)) => #{'a 'b 'c}`

In the `fn*`

special form (used by the `fn`

macro) `symbols`

removes the argument names from the set.

```
(symbols '(fn [x y] (+ x y a))) => #{'a '+}
(symbols '(fn foo [x y] (+ x (foo y a)))) => #{'a '+}
```

The function name (in named functions) is also removed.

`(symbols '(fn foo [x] (foo x))) => #{}`

Multi-arity functions are supported as well.

```
(symbols '(fn ([x] (+ a x))
([x y] (+ b x y)))) => #{'a 'b '+}
```

`def`

is not allowed inside an expression (it is allowed on the top-level, as described below).

`(symbols '(def x 2)) => (throws "def is not allowed")`

Similarly, reference to variables is not allowed.

`(symbols '#'var) => (throws "vars are not allowed")`

Quoted symbols are ignored.

`(symbols ''(a b c)) => #{}`

In special forms such as `if`

and `do`

, the form's name is ignored.

```
(symbols '(if a b c)) => #{'a 'b 'c}
(symbols '(do a b c)) => #{'a 'b 'c}
(symbols '(recur a b)) => #{'a 'b}
```

Exceptions are not supported because they use Java interop.

```
(symbols '(throw foo)) => (throws Exception "throw is not allowed. Use error instead")
(symbols '(try foo bar baz)) => (throws "try/catch is not allowed")
```

While `for`

is a macro and not a special form, its definition makes use of Java interop, which we disallow. We therefore make `for`

a special case.

`(symbols '(for [x foo] (* x 2))) => #{'foo '*}`

Reader macros annonimous functions are supported.

```
(symbols '#(+ 1 %)) => #{'+}
(symbols '(fn* [x] (+ 1 x))) => #{'+}
```