Repository: tree-sitter/tree-sitter-go (language: python)

Issue:
Function call parsed as a Type conversion
From https://github.com/returntocorp/semgrep/issues/3993

this program:
```
_ := os.GetEnv("Test")
```
without the ending newline is parsed as a type conversion instead of a regular function call.



Discussion / hints:
Perhaps it is a type conversion: you can't tell without seeing whether package `os` defines `type GetEnv string` or `func GetEnv(string) string`, and the parser is not privy to that information.
Still it's weird that a newline has an impact on how this thing is parsed.

And in any case, because tree-sitter does not have the information, it should parse it as a function call, not a cast.
It should return a Cast node when we know for sure the thing is a type, as in  _ := int(3)


Fix patch:
diff --git a/.eslintrc.js b/.eslintrc.js
new file mode 100644
index 000000000..de1cc0506
--- /dev/null
+++ b/.eslintrc.js
@@ -0,0 +1,21 @@
+module.exports = {
+  'env': {
+    'commonjs': true,
+    'es2021': true,
+  },
+  'extends': 'google',
+  'overrides': [
+  ],
+  'parserOptions': {
+    'ecmaVersion': 'latest',
+    'sourceType': 'module',
+  },
+  'rules': {
+    'indent': ['error', 2, {'SwitchCase': 1}],
+    'max-len': [
+      'error',
+      {'code': 160, 'ignoreComments': true, 'ignoreUrls': true, 'ignoreStrings': true},
+    ],
+    'one-var': ['error', 'consecutive'],
+  },
+};
diff --git a/.gitattributes b/.gitattributes
index f60d7b977..c647c2bf4 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -1,2 +1,6 @@
 /src/** linguist-vendored
 /examples/* linguist-vendored
+
+src/grammar.json -diff
+src/node-types.json -diff
+src/parser.c -diff
diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml
new file mode 100644
index 000000000..d94f7f39d
--- /dev/null
+++ b/.github/workflows/lint.yml
@@ -0,0 +1,19 @@
+name: Lint
+
+on:
+  push:
+    branches:
+      - master
+  pull_request:
+    branches:
+      - "**"
+
+jobs:
+  lint:
+    runs-on: ubuntu-latest
+    steps:
+      - uses: actions/checkout@v3
+      - name: Install modules
+        run: npm install
+      - name: Run ESLint
+        run: npm run lint
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
new file mode 100644
index 000000000..c2020e92f
--- /dev/null
+++ b/.github/workflows/release.yml
@@ -0,0 +1,103 @@
+name: Release
+
+on:
+  workflow_run:
+    workflows: ["CI"]
+    branches:
+      - master
+    types:
+      - completed
+
+jobs:
+  release:
+    runs-on: ubuntu-latest
+    steps:
+      - name: Checkout code
+        uses: actions/checkout@v3
+        with:
+          fetch-depth: 0
+
+      - name: Get previous commit SHA
+        id: get_previous_commit
+        run: |
+          LATEST_TAG=$(git describe --tags --abbrev=0)
+          if [[ -z "$LATEST_TAG" ]]; then
+            echo "No tag found. Failing..."
+            exit 1
+          fi
+          echo "latest_tag=${LATEST_TAG#v}" >> "$GITHUB_ENV" # Remove 'v' prefix from the tag
+
+      - name: Check if version changed and is greater than the previous
+        id: version_check
+        run: |
+          # Compare the current version with the version from the previous commit
+          PREVIOUS_NPM_VERSION=${{ env.latest_tag }}
+          CURRENT_NPM_VERSION=$(jq -r '.version' package.json)
+          CURRENT_CARGO_VERSION=$(awk -F '"' '/^version/ {print $2}' Cargo.toml)
+          if [[ "$CURRENT_NPM_VERSION" != "$CURRENT_CARGO_VERSION" ]]; then # Cargo.toml and package.json versions must match
+            echo "Mismatch: NPM version ($CURRENT_NPM_VERSION) and Cargo.toml version ($CURRENT_CARGO_VERSION)"
+            echo "version_changed=false" >> "$GITHUB_ENV"
+          else
+            if [[ "$PREVIOUS_NPM_VERSION" ==  "$CURRENT_NPM_VERSION" ]]; then
+              echo "version_changed=" >> "$GITHUB_ENV"
+            else
+              IFS='.' read -ra PREVIOUS_VERSION_PARTS <<< "$PREVIOUS_NPM_VERSION"
+              IFS='.' read -ra CURRENT_VERSION_PARTS <<< "$CURRENT_NPM_VERSION"
+              VERSION_CHANGED=false
+              for i in "${!PREVIOUS_VERSION_PARTS[@]}"; do
+                if [[ ${CURRENT_VERSION_PARTS[i]} -gt ${PREVIOUS_VERSION_PARTS[i]} ]]; then
+                  VERSION_CHANGED=true
+                  break
+                elif [[ ${CURRENT_VERSION_PARTS[i]} -lt ${PREVIOUS_VERSION_PARTS[i]} ]]; then
+                  break
+                fi
+              done
+
+              echo "version_changed=$VERSION_CHANGED" >> "$GITHUB_ENV"
+              echo "current_version=${CURRENT_NPM_VERSION}" >> "$GITHUB_ENV"
+            fi
+          fi
+
+      - name: Display result
+        run: |
+          echo "Version bump detected: ${{ env.version_changed }}"
+
+      - name: Fail if version is lower
+        if: env.version_changed == 'false'
+        run: exit 1
+
+      - name: Setup Node
+        if: env.version_changed == 'true'
+        uses: actions/setup-node@v3
+        with:
+          node-version: 18
+          registry-url: "https://registry.npmjs.org"
+      - name: Publish to NPM
+        if: env.version_changed == 'true'
+        env:
+          NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}}
+        run: npm publish
+
+      - name: Setup Rust
+        if: env.version_changed == 'true'
+        uses: actions-rs/toolchain@v1
+        with:
+          profile: minimal
+          toolchain: stable
+          override: true
+      - name: Publish to Crates.io
+        if: env.version_changed == 'true'
+        uses: katyo/publish-crates@v2
+        with:
+          registry-token: ${{ secrets.CARGO_REGISTRY_TOKEN }}
+
+      - name: Tag versions
+        if: env.version_changed == 'true'
+        run: |
+          git checkout master
+          git config user.name github-actions[bot]
+          git config user.email github-actions[bot]@users.noreply.github.com
+          git tag -d "v${{ env.current_version }}" || true
+          git push origin --delete "v${{ env.current_version }}" || true
+          git tag -a "v${{ env.current_version }}" -m "Version ${{ env.current_version }}"
+          git push origin "v${{ env.current_version }}"
diff --git a/.gitmodules b/.gitmodules
deleted file mode 100644
index e69de29bb..000000000
diff --git a/Cargo.toml b/Cargo.toml
index 4de26496d..3af34c1ab 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -3,8 +3,8 @@ name = "tree-sitter-go"
 description = "Go grammar for the tree-sitter parsing library"
 version = "0.19.1"
 authors = [
-    "Max Brunsfeld <maxbrunsfeld@gmail.com>",
-    "Douglas Creager <dcreager@dcreager.net>",
+	"Max Brunsfeld <maxbrunsfeld@gmail.com>",
+	"Douglas Creager <dcreager@dcreager.net>",
 ]
 license = "MIT"
 readme = "bindings/rust/README.md"
@@ -14,12 +14,7 @@ repository = "https://github.com/tree-sitter/tree-sitter-go"
 edition = "2018"
 
 build = "bindings/rust/build.rs"
-include = [
-  "bindings/rust/*",
-  "grammar.js",
-  "queries/*",
-  "src/*",
-]
+include = ["bindings/rust/*", "grammar.js", "queries/*", "src/*"]
 
 [lib]
 path = "bindings/rust/lib.rs"
diff --git a/README.md b/README.md
index b8002673a..74dde3f06 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,4 @@
-tree-sitter-go
-===========================
+# tree-sitter-go
 
 [![Build/test](https://github.com/tree-sitter/tree-sitter-go/actions/workflows/ci.yml/badge.svg)](https://github.com/tree-sitter/tree-sitter-go/actions/workflows/ci.yml)
 
diff --git a/bindings/node/binding.cc b/bindings/node/binding.cc
index 914b6779f..784fd1c1e 100644
--- a/bindings/node/binding.cc
+++ b/bindings/node/binding.cc
@@ -1,28 +1,30 @@
+#include "nan.h"
 #include "tree_sitter/parser.h"
 #include <node.h>
-#include "nan.h"
 
 using namespace v8;
 
-extern "C" TSLanguage * tree_sitter_go();
+extern "C" TSLanguage *tree_sitter_go();
 
 namespace {
 
 NAN_METHOD(New) {}
 
 void Init(Local<Object> exports, Local<Object> module) {
-  Local<FunctionTemplate> tpl = Nan::New<FunctionTemplate>(New);
-  tpl->SetClassName(Nan::New("Language").ToLocalChecked());
-  tpl->InstanceTemplate()->SetInternalFieldCount(1);
-
-  Local<Function> constructor = Nan::GetFunction(tpl).ToLocalChecked();
-  Local<Object> instance = constructor->NewInstance(Nan::GetCurrentContext()).ToLocalChecked();
-  Nan::SetInternalFieldPointer(instance, 0, tree_sitter_go());
-
-  Nan::Set(instance, Nan::New("name").ToLocalChecked(), Nan::New("go").ToLocalChecked());
-  Nan::Set(module, Nan::New("exports").ToLocalChecked(), instance);
+    Local<FunctionTemplate> tpl = Nan::New<FunctionTemplate>(New);
+    tpl->SetClassName(Nan::New("Language").ToLocalChecked());
+    tpl->InstanceTemplate()->SetInternalFieldCount(1);
+
+    Local<Function> constructor = Nan::GetFunction(tpl).ToLocalChecked();
+    Local<Object> instance =
+        constructor->NewInstance(Nan::GetCurrentContext()).ToLocalChecked();
+    Nan::SetInternalFieldPointer(instance, 0, tree_sitter_go());
+
+    Nan::Set(instance, Nan::New("name").ToLocalChecked(),
+             Nan::New("go").ToLocalChecked());
+    Nan::Set(module, Nan::New("exports").ToLocalChecked(), instance);
 }
 
 NODE_MODULE(tree_sitter_go_binding, Init)
 
-}  // namespace
+} // namespace
diff --git a/bindings/node/index.js b/bindings/node/index.js
index f8080700c..2ce1c1e05 100644
--- a/bindings/node/index.js
+++ b/bindings/node/index.js
@@ -1,19 +1,19 @@
 try {
-  module.exports = require("../../build/Release/tree_sitter_go_binding");
+  module.exports = require('../../build/Release/tree_sitter_go_binding');
 } catch (error1) {
   if (error1.code !== 'MODULE_NOT_FOUND') {
     throw error1;
   }
   try {
-    module.exports = require("../../build/Debug/tree_sitter_go_binding");
+    module.exports = require('../../build/Debug/tree_sitter_go_binding');
   } catch (error2) {
     if (error2.code !== 'MODULE_NOT_FOUND') {
       throw error2;
     }
-    throw error1
+    throw error1;
   }
 }
 
 try {
-  module.exports.nodeTypeInfo = require("../../src/node-types.json");
+  module.exports.nodeTypeInfo = require('../../src/node-types.json');
 } catch (_) {}
diff --git a/bindings/rust/README.md b/bindings/rust/README.md
index 776c2b805..0eeaf6c16 100644
--- a/bindings/rust/README.md
+++ b/bindings/rust/README.md
@@ -1,24 +1,24 @@
 # tree-sitter-go
 
-This crate provides a Go grammar for the [tree-sitter][] parsing library.  To
+This crate provides a Go grammar for the [tree-sitter][] parsing library. To
 use this crate, add it to the `[dependencies]` section of your `Cargo.toml`
-file.  (Note that you will probably also need to depend on the
+file. (Note that you will probably also need to depend on the
 [`tree-sitter`][tree-sitter crate] crate to use the parsed result in any useful
 way.)
 
-``` toml
+```toml
 [dependencies]
 tree-sitter = "0.20"
 tree-sitter-go = "0.19"
 ```
 
-Typically, you will use the [language][language func] function to add this
+Typically, you will use the [language][language] function to add this
 grammar to a tree-sitter [Parser][], and then use the parser to parse some code:
 
-``` rust
+```rust
 let code = r#"
     func double(x int) int {
-    	return x * 2
+        return x * 2
     }
 "#;
 let mut parser = Parser::new();
@@ -29,8 +29,7 @@ let parsed = parser.parse(code, None);
 If you have any questions, please reach out to us in the [tree-sitter
 discussions] page.
 
-[Language]: https://docs.rs/tree-sitter/*/tree_sitter/struct.Language.html
-[language func]: https://docs.rs/tree-sitter-go/*/tree_sitter_go/fn.language.html
+[language]: https://docs.rs/tree-sitter-go/*/tree_sitter_go/fn.language.html
 [Parser]: https://docs.rs/tree-sitter/*/tree_sitter/struct.Parser.html
 [tree-sitter]: https://tree-sitter.github.io/
 [tree-sitter crate]: https://crates.io/crates/tree-sitter
diff --git a/bindings/rust/build.rs b/bindings/rust/build.rs
index 45b4151cb..6a41cd05c 100644
--- a/bindings/rust/build.rs
+++ b/bindings/rust/build.rs
@@ -1,7 +1,7 @@
 fn main() {
     let src_dir = std::path::Path::new("src");
     let mut c_config = cc::Build::new();
-    c_config.include(&src_dir);
+    c_config.include(src_dir);
     c_config
         .flag_if_supported("-Wno-unused-parameter")
         .flag_if_supported("-Wno-unused-but-set-variable")
diff --git a/bindings/rust/lib.rs b/bindings/rust/lib.rs
index 57d9c2d05..d2212211c 100644
--- a/bindings/rust/lib.rs
+++ b/bindings/rust/lib.rs
@@ -14,15 +14,14 @@
 //!
 //! let code = r#"
 //!     func double(x int) int {
-//!     	return x * 2
+//!         return x * 2
 //!     }
 //! "#;
 //! let mut parser = Parser::new();
 //! parser.set_language(tree_sitter_go::language()).expect("Error loading Go grammar");
-//! let parsed = parser.parse(code, None);
-//! # let parsed = parsed.unwrap();
-//! # let root = parsed.root_node();
-//! # assert!(!root.has_error());
+//! let parsed = parser.parse(code, None).unwrap();
+//! let root = parsed.root_node();
+//! assert!(!root.has_error());
 //! ```
 //!
 //! [Language]: https://docs.rs/tree-sitter/*/tree_sitter/struct.Language.html
@@ -44,18 +43,18 @@ pub fn language() -> Language {
 }
 
 /// The source of the Go tree-sitter grammar description.
-pub const GRAMMAR: &'static str = include_str!("../../grammar.js");
+pub const GRAMMAR: &str = include_str!("../../grammar.js");
 
 /// The syntax highlighting query for this language.
-pub const HIGHLIGHT_QUERY: &'static str = include_str!("../../queries/highlights.scm");
+pub const HIGHLIGHT_QUERY: &str = include_str!("../../queries/highlights.scm");
 
 /// The content of the [`node-types.json`][] file for this grammar.
 ///
 /// [`node-types.json`]: https://tree-sitter.github.io/tree-sitter/using-parsers#static-node-types
-pub const NODE_TYPES: &'static str = include_str!("../../src/node-types.json");
+pub const NODE_TYPES: &str = include_str!("../../src/node-types.json");
 
 /// The symbol tagging query for this language.
-pub const TAGGING_QUERY: &'static str = include_str!("../../queries/tags.scm");
+pub const TAGGING_QUERY: &str = include_str!("../../queries/tags.scm");
 
 #[cfg(test)]
 mod tests {
diff --git a/grammar.js b/grammar.js
index a8b952ee1..1817066ef 100644
--- a/grammar.js
+++ b/grammar.js
@@ -1,3 +1,15 @@
+/**
+ * @file Go grammar for tree-sitter
+ * @author Max Brunsfeld
+ * @license MIT
+ */
+
+/* eslint-disable arrow-parens */
+/* eslint-disable camelcase */
+/* eslint-disable-next-line spaced-comment */
+/// <reference types="tree-sitter-cli/dsl" />
+// @ts-check
+
 const
   PREC = {
     primary: 7,
@@ -17,7 +29,7 @@ const
 
 
   newline = '\n',
-  terminator = choice(newline, ';'),
+  terminator = choice(newline, ';', '\0'),
 
   hexDigit = /[0-9a-fA-F]/,
   octalDigit = /[0-7]/,
@@ -53,14 +65,14 @@ const
 
   floatLiteral = choice(decimalFloatLiteral, hexFloatLiteral),
 
-  imaginaryLiteral = seq(choice(decimalDigits, intLiteral, floatLiteral), 'i')
+  imaginaryLiteral = seq(choice(decimalDigits, intLiteral, floatLiteral), 'i');
 
 module.exports = grammar({
   name: 'go',
 
   extras: $ => [
     $.comment,
-    /\s/
+    /\s/,
   ],
 
   inline: $ => [
@@ -96,10 +108,10 @@ module.exports = grammar({
   rules: {
     source_file: $ => seq(
       repeat(choice(
-      // Unlike a Go compiler, we accept statements at top-level to enable
-      // parsing of partial code snippets in documentation (see #63).
-      seq($._statement, terminator),
-      seq($._top_level_declaration, terminator),
+        // Unlike a Go compiler, we accept statements at top-level to enable
+        // parsing of partial code snippets in documentation (see #63).
+        seq($._statement, terminator),
+        seq($._top_level_declaration, terminator),
       )),
       optional($._top_level_declaration),
     ),
@@ -108,32 +120,32 @@ module.exports = grammar({
       $.package_clause,
       $.function_declaration,
       $.method_declaration,
-      $.import_declaration
+      $.import_declaration,
     ),
 
     package_clause: $ => seq(
       'package',
-      $._package_identifier
+      $._package_identifier,
     ),
 
     import_declaration: $ => seq(
       'import',
       choice(
         $.import_spec,
-        $.import_spec_list
-      )
+        $.import_spec_list,
+      ),
     ),
 
     import_spec: $ => seq(
       optional(field('name', choice(
         $.dot,
         $.blank_identifier,
-        $._package_identifier
+        $._package_identifier,
       ))),
-      field('path', $._string_literal)
+      field('path', $._string_literal),
     ),
-    dot: $ => '.',
-    blank_identifier: $ => '_',
+    dot: _ => '.',
+    blank_identifier: _ => '_',
 
     import_spec_list: $ => seq(
       '(',
@@ -148,7 +160,7 @@ module.exports = grammar({
     _declaration: $ => choice(
       $.const_declaration,
       $.type_declaration,
-      $.var_declaration
+      $.var_declaration,
     ),
 
     const_declaration: $ => seq(
@@ -158,9 +170,9 @@ module.exports = grammar({
         seq(
           '(',
           repeat(seq($.const_spec, terminator)),
-          ')'
-        )
-      )
+          ')',
+        ),
+      ),
     ),
 
     const_spec: $ => prec.left(seq(
@@ -168,8 +180,8 @@ module.exports = grammar({
       optional(seq(
         optional(field('type', $._type)),
         '=',
-        field('value', $.expression_list)
-      ))
+        field('value', $.expression_list),
+      )),
     )),
 
     var_declaration: $ => seq(
@@ -179,9 +191,9 @@ module.exports = grammar({
         seq(
           '(',
           repeat(seq($.var_spec, terminator)),
-          ')'
-        )
-      )
+          ')',
+        ),
+      ),
     ),
 
     var_spec: $ => seq(
@@ -189,10 +201,10 @@ module.exports = grammar({
       choice(
         seq(
           field('type', $._type),
-          optional(seq('=', field('value', $.expression_list)))
+          optional(seq('=', field('value', $.expression_list))),
         ),
-        seq('=', field('value', $.expression_list))
-      )
+        seq('=', field('value', $.expression_list)),
+      ),
     ),
 
     function_declaration: $ => prec.right(1, seq(
@@ -201,7 +213,7 @@ module.exports = grammar({
       field('type_parameters', optional($.type_parameter_list)),
       field('parameters', $.parameter_list),
       field('result', optional(choice($.parameter_list, $._simple_type))),
-      field('body', optional($.block))
+      field('body', optional($.block)),
     )),
 
     method_declaration: $ => prec.right(1, seq(
@@ -210,23 +222,23 @@ module.exports = grammar({
       field('name', $._field_identifier),
       field('parameters', $.parameter_list),
       field('result', optional(choice($.parameter_list, $._simple_type))),
-      field('body', optional($.block))
+      field('body', optional($.block)),
     )),
 
     type_parameter_list: $ => seq(
       '[',
       commaSep1($.parameter_declaration),
       optional(','),
-      ']'
+      ']',
     ),
 
     parameter_list: $ => seq(
       '(',
       optional(seq(
         commaSep(choice($.parameter_declaration, $.variadic_parameter_declaration)),
-        optional(',')
+        optional(','),
       )),
-      ')'
+      ')',
     ),
 
     parameter_declaration: $ => prec.left(seq(
@@ -237,13 +249,13 @@ module.exports = grammar({
     variadic_parameter_declaration: $ => seq(
       field('name', optional($.identifier)),
       '...',
-      field('type', $._type)
+      field('type', $._type),
     ),
 
     type_alias: $ => seq(
       field('name', $._type_identifier),
       '=',
-      field('type', $._type)
+      field('type', $._type),
     ),
 
     type_declaration: $ => seq(
@@ -254,15 +266,15 @@ module.exports = grammar({
         seq(
           '(',
           repeat(seq(choice($.type_spec, $.type_alias), terminator)),
-          ')'
-        )
-      )
+          ')',
+        ),
+      ),
     ),
 
     type_spec: $ => seq(
       field('name', $._type_identifier),
       field('type_parameters', optional($.type_parameter_list)),
-      field('type', $._type)
+      field('type', $._type),
     ),
 
     field_name_list: $ => commaSep1($._field_identifier),
@@ -271,7 +283,7 @@ module.exports = grammar({
 
     _type: $ => choice(
       $._simple_type,
-      $.parenthesized_type
+      $.parenthesized_type,
     ),
 
     parenthesized_type: $ => seq('(', $._type, ')'),
@@ -299,9 +311,9 @@ module.exports = grammar({
 
     type_arguments: $ => prec.dynamic(2, seq(
       '[',
-       commaSep1($._type),
-       optional(','),
-      ']'
+      commaSep1($._type),
+      optional(','),
+      ']',
     )),
 
     pointer_type: $ => prec(PREC.unary, seq('*', $._type)),
@@ -310,25 +322,25 @@ module.exports = grammar({
       '[',
       field('length', $._expression),
       ']',
-      field('element', $._type)
+      field('element', $._type),
     )),
 
     implicit_length_array_type: $ => seq(
       '[',
       '...',
       ']',
-      field('element', $._type)
+      field('element', $._type),
     ),
 
     slice_type: $ => prec.right(seq(
       '[',
       ']',
-      field('element', $._type)
+      field('element', $._type),
     )),
 
     struct_type: $ => seq(
       'struct',
-      $.field_declaration_list
+      $.field_declaration_list,
     ),
 
     union_type: $ => prec.left(seq(
@@ -347,16 +359,16 @@ module.exports = grammar({
       optional(seq(
         $.field_declaration,
         repeat(seq(terminator, $.field_declaration)),
-        optional(terminator)
+        optional(terminator),
       )),
-      '}'
+      '}',
     ),
 
     field_declaration: $ => seq(
       choice(
         seq(
           commaSep1(field('name', $._field_identifier)),
-          field('type', $._type)
+          field('type', $._type),
         ),
         seq(
           optional('*'),
@@ -364,10 +376,10 @@ module.exports = grammar({
             $._type_identifier,
             $.qualified_type,
             $.generic_type,
-          ))
-        )
+          )),
+        ),
       ),
-      field('tag', optional($._string_literal))
+      field('tag', optional($._string_literal)),
     ),
 
     interface_type: $ => seq(
@@ -376,9 +388,9 @@ module.exports = grammar({
       optional(seq(
         $._interface_body,
         repeat(seq(terminator, $._interface_body)),
-        optional(terminator)
+        optional(terminator),
       )),
-      '}'
+      '}',
     ),
 
     _interface_body: $ => choice(
@@ -389,18 +401,18 @@ module.exports = grammar({
 
     struct_elem: $ => seq(
       $.struct_term,
-      repeat(seq('|', $.struct_term))
+      repeat(seq('|', $.struct_term)),
     ),
 
     struct_term: $ => prec(1, seq(
       optional(choice('~', '*')),
-      $.struct_type
+      $.struct_type,
     )),
 
     method_spec: $ => seq(
       field('name', $._field_identifier),
       field('parameters', $.parameter_list),
-      field('result', optional(choice($.parameter_list, $._simple_type)))
+      field('result', optional(choice($.parameter_list, $._simple_type))),
     ),
 
     map_type: $ => prec.right(seq(
@@ -408,25 +420,25 @@ module.exports = grammar({
       '[',
       field('key', $._type),
       ']',
-      field('value', $._type)
+      field('value', $._type),
     )),
 
     channel_type: $ => prec.left(choice(
       seq('chan', field('value', $._type)),
       seq('chan', '<-', field('value', $._type)),
-      prec(PREC.unary, seq('<-', 'chan', field('value', $._type)))
+      prec(PREC.unary, seq('<-', 'chan', field('value', $._type))),
     )),
 
     function_type: $ => prec.right(seq(
       'func',
       field('parameters', $.parameter_list),
-      field('result', optional(choice($.parameter_list, $._simple_type)))
+      field('result', optional(choice($.parameter_list, $._simple_type))),
     )),
 
     block: $ => seq(
       '{',
       optional($._statement_list),
-      '}'
+      '}',
     ),
 
     _statement_list: $ => choice(
@@ -435,10 +447,10 @@ module.exports = grammar({
         repeat(seq(terminator, $._statement)),
         optional(seq(
           terminator,
-          optional(alias($.empty_labeled_statement, $.labeled_statement))
-        ))
+          optional(alias($.empty_labeled_statement, $.labeled_statement)),
+        )),
       ),
-      alias($.empty_labeled_statement, $.labeled_statement)
+      alias($.empty_labeled_statement, $.labeled_statement),
     ),
 
     _statement: $ => choice(
@@ -458,18 +470,18 @@ module.exports = grammar({
       $.continue_statement,
       $.goto_statement,
       $.block,
-      $.empty_statement
+      $.empty_statement,
     ),
 
-    empty_statement: $ => ';',
+    empty_statement: _ => ';',
 
     _simple_statement: $ => choice(
-      $.expression_statement, 
+      $.expression_statement,
       $.send_statement,
       $.inc_statement,
       $.dec_statement,
       $.assignment_statement,
-      $.short_var_declaration
+      $.short_var_declaration,
     ),
 
     expression_statement: $ => $._expression,
@@ -477,31 +489,31 @@ module.exports = grammar({
     send_statement: $ => seq(
       field('channel', $._expression),
       '<-',
-      field('value', $._expression)
+      field('value', $._expression),
     ),
 
     receive_statement: $ => seq(
       optional(seq(
         field('left', $.expression_list),
-        choice('=', ':=')
+        choice('=', ':='),
       )),
-      field('right', $._expression)
+      field('right', $._expression),
     ),
 
     inc_statement: $ => seq(
       $._expression,
-      '++'
+      '++',
     ),
 
     dec_statement: $ => seq(
       $._expression,
-      '--'
+      '--',
     ),
 
     assignment_statement: $ => seq(
       field('left', $.expression_list),
       field('operator', choice(...assignment_operators)),
-      field('right', $.expression_list)
+      field('right', $.expression_list),
     ),
 
     short_var_declaration: $ => seq(
@@ -509,24 +521,24 @@ module.exports = grammar({
       // conflicts between identifiers as expressions vs identifiers here.
       field('left', $.expression_list),
       ':=',
-      field('right', $.expression_list)
+      field('right', $.expression_list),
     ),
 
     labeled_statement: $ => seq(
       field('label', alias($.identifier, $.label_name)),
       ':',
-      $._statement
+      $._statement,
     ),
 
     empty_labeled_statement: $ => seq(
       field('label', alias($.identifier, $.label_name)),
-      ':'
+      ':',
     ),
 
     // This is a hack to prevent `fallthrough_statement` from being parsed as
     // a single token. For consistency with `break_statement` etc it should
     // be parsed as a parent node that *contains* a `fallthrough` token.
-    fallthrough_statement: $ => prec.left('fallthrough'),
+    fallthrough_statement: _ => prec.left('fallthrough'),
 
     break_statement: $ => seq('break', optional(alias($.identifier, $.label_name))),
 
@@ -544,20 +556,20 @@ module.exports = grammar({
       'if',
       optional(seq(
         field('initializer', $._simple_statement),
-        ';'
+        ';',
       )),
       field('condition', $._expression),
       field('consequence', $.block),
       optional(seq(
         'else',
-        field('alternative', choice($.block, $.if_statement))
-      ))
+        field('alternative', choice($.block, $.if_statement)),
+      )),
     ),
 
     for_statement: $ => seq(
       'for',
       optional(choice($._expression, $.for_clause, $.range_clause)),
-      field('body', $.block)
+      field('body', $.block),
     ),
 
     for_clause: $ => seq(
@@ -565,41 +577,41 @@ module.exports = grammar({
       ';',
       field('condition', optional($._expression)),
       ';',
-      field('update', optional($._simple_statement))
+      field('update', optional($._simple_statement)),
     ),
 
     range_clause: $ => seq(
       optional(seq(
         field('left', $.expression_list),
-        choice('=', ':=')
+        choice('=', ':='),
       )),
       'range',
-      field('right', $._expression)
+      field('right', $._expression),
     ),
 
     expression_switch_statement: $ => seq(
       'switch',
       optional(seq(
         field('initializer', $._simple_statement),
-        ';'
+        ';',
       )),
       field('value', optional($._expression)),
       '{',
       repeat(choice($.expression_case, $.default_case)),
-      '}'
+      '}',
     ),
 
     expression_case: $ => seq(
       'case',
       field('value', $.expression_list),
       ':',
-      optional($._statement_list)
+      optional($._statement_list),
     ),
 
     default_case: $ => seq(
       'default',
       ':',
-      optional($._statement_list)
+      optional($._statement_list),
     ),
 
     type_switch_statement: $ => seq(
@@ -607,41 +619,41 @@ module.exports = grammar({
       $._type_switch_header,
       '{',
       repeat(choice($.type_case, $.default_case)),
-      '}'
+      '}',
     ),
 
     _type_switch_header: $ => seq(
       optional(seq(
         field('initializer', $._simple_statement),
-        ';'
+        ';',
       )),
       optional(seq(field('alias', $.expression_list), ':=')),
       field('value', $._expression),
       '.',
       '(',
       'type',
-      ')'
+      ')',
     ),
 
     type_case: $ => seq(
       'case',
       field('type', commaSep1($._type)),
       ':',
-      optional($._statement_list)
+      optional($._statement_list),
     ),
 
     select_statement: $ => seq(
       'select',
       '{',
       repeat(choice($.communication_case, $.default_case)),
-      '}'
+      '}',
     ),
 
     communication_case: $ => seq(
       'case',
       field('communication', choice($.send_statement, $.receive_statement)),
       ':',
-      optional($._statement_list)
+      optional($._statement_list),
     ),
 
     _expression: $ => choice(
@@ -666,30 +678,30 @@ module.exports = grammar({
       $.true,
       $.false,
       $.iota,
-      $.parenthesized_expression
+      $.parenthesized_expression,
     ),
 
     parenthesized_expression: $ => seq(
       '(',
       $._expression,
-      ')'
+      ')',
     ),
 
     call_expression: $ => prec(PREC.primary, choice(
       seq(
         field('function', alias(choice('new', 'make'), $.identifier)),
-        field('arguments', alias($.special_argument_list, $.argument_list))
+        field('arguments', alias($.special_argument_list, $.argument_list)),
       ),
       seq(
         field('function', $._expression),
         field('type_arguments', optional($.type_arguments)),
-        field('arguments', $.argument_list)
-      )
+        field('arguments', $.argument_list),
+      ),
     )),
 
     variadic_argument: $ => prec.right(seq(
       $._expression,
-      '...'
+      '...',
     )),
 
     special_argument_list: $ => seq(
@@ -697,7 +709,7 @@ module.exports = grammar({
       $._type,
       repeat(seq(',', $._expression)),
       optional(','),
-      ')'
+      ')',
     ),
 
     argument_list: $ => seq(
@@ -705,22 +717,22 @@ module.exports = grammar({
       optional(seq(
         choice($._expression, $.variadic_argument),
         repeat(seq(',', choice($._expression, $.variadic_argument))),
-        optional(',')
+        optional(','),
       )),
-      ')'
+      ')',
     ),
 
     selector_expression: $ => prec(PREC.primary, seq(
       field('operand', $._expression),
       '.',
-      field('field', $._field_identifier)
+      field('field', $._field_identifier),
     )),
 
     index_expression: $ => prec(PREC.primary, seq(
       field('operand', $._expression),
       '[',
       field('index', $._expression),
-      ']'
+      ']',
     )),
 
     slice_expression: $ => prec(PREC.primary, seq(
@@ -730,17 +742,17 @@ module.exports = grammar({
         seq(
           field('start', optional($._expression)),
           ':',
-          field('end', optional($._expression))
+          field('end', optional($._expression)),
         ),
         seq(
           field('start', optional($._expression)),
           ':',
           field('end', $._expression),
           ':',
-          field('capacity', $._expression)
-        )
+          field('capacity', $._expression),
+        ),
       ),
-      ']'
+      ']',
     )),
 
     type_assertion_expression: $ => prec(PREC.primary, seq(
@@ -748,7 +760,7 @@ module.exports = grammar({
       '.',
       '(',
       field('type', $._type),
-      ')'
+      ')',
     )),
 
     type_conversion_expression: $ => prec.dynamic(-1, seq(
@@ -756,7 +768,7 @@ module.exports = grammar({
       '(',
       field('operand', $._expression),
       optional(','),
-      ')'
+      ')',
     )),
 
     composite_literal: $ => prec(PREC.composite_literal, seq(
@@ -768,18 +780,18 @@ module.exports = grammar({
         $.struct_type,
         $._type_identifier,
         $.generic_type,
-        $.qualified_type
+        $.qualified_type,
       )),
-      field('body', $.literal_value)
+      field('body', $.literal_value),
     )),
 
     literal_value: $ => seq(
       '{',
       optional(
-	seq(
-	  commaSep(choice($.literal_element, $.keyed_element)),
+        seq(
+          commaSep(choice($.literal_element, $.keyed_element)),
           optional(','))),
-      '}'
+      '}',
     ),
 
     literal_element: $ => choice($._expression, $.literal_value),
@@ -795,12 +807,12 @@ module.exports = grammar({
       'func',
       field('parameters', $.parameter_list),
       field('result', optional(choice($.parameter_list, $._simple_type))),
-      field('body', $.block)
+      field('body', $.block),
     ),
 
     unary_expression: $ => prec(PREC.unary, seq(
       field('operator', choice('+', '-', '!', '^', '*', '&', '<-')),
-      field('operand', $._expression)
+      field('operand', $._expression),
     )),
 
     binary_expression: $ => {
@@ -813,18 +825,20 @@ module.exports = grammar({
       ];
 
       return choice(...table.map(([precedence, operator]) =>
+        // @ts-ignore
         prec.left(precedence, seq(
           field('left', $._expression),
+          // @ts-ignore
           field('operator', operator),
-          field('right', $._expression)
-        ))
+          field('right', $._expression),
+        )),
       ));
     },
 
     qualified_type: $ => seq(
       field('package', $._package_identifier),
       '.',
-      field('name', $._type_identifier)
+      field('name', $._type_identifier),
     ),
 
     identifier: _ => /[_\p{XID_Start}][_\p{XID_Continue}]*/,
@@ -835,44 +849,44 @@ module.exports = grammar({
 
     _string_literal: $ => choice(
       $.raw_string_literal,
-      $.interpreted_string_literal
+      $.interpreted_string_literal,
     ),
 
-    raw_string_literal: $ => token(seq(
+    raw_string_literal: _ => token(seq(
       '`',
       repeat(/[^`]/),
-      '`'
+      '`',
     )),
 
     interpreted_string_literal: $ => seq(
       '"',
       repeat(choice(
         $._interpreted_string_literal_basic_content,
-        $.escape_sequence
+        $.escape_sequence,
       )),
-      token.immediate('"')
+      token.immediate('"'),
     ),
-    _interpreted_string_literal_basic_content: $ => token.immediate(prec(1, /[^"\n\\]+/)),
+    _interpreted_string_literal_basic_content: _ => token.immediate(prec(1, /[^"\n\\]+/)),
 
-    escape_sequence: $ => token.immediate(seq(
+    escape_sequence: _ => token.immediate(seq(
       '\\',
       choice(
         /[^xuU]/,
         /\d{2,3}/,
         /x[0-9a-fA-F]{2,}/,
         /u[0-9a-fA-F]{4}/,
-        /U[0-9a-fA-F]{8}/
-      )
+        /U[0-9a-fA-F]{8}/,
+      ),
     )),
 
-    int_literal: $ => token(intLiteral),
+    int_literal: _ => token(intLiteral),
 
-    float_literal: $ => token(floatLiteral),
+    float_literal: _ => token(floatLiteral),
 
-    imaginary_literal: $ => token(imaginaryLiteral),
+    imaginary_literal: _ => token(imaginaryLiteral),
 
-    rune_literal: $ => token(seq(
-      "'",
+    rune_literal: _ => token(seq(
+      '\'',
       choice(
         /[^'\\]/,
         seq(
@@ -882,34 +896,50 @@ module.exports = grammar({
             seq(octalDigit, octalDigit, octalDigit),
             seq('u', hexDigit, hexDigit, hexDigit, hexDigit),
             seq('U', hexDigit, hexDigit, hexDigit, hexDigit, hexDigit, hexDigit, hexDigit, hexDigit),
-            seq(choice('a', 'b', 'f', 'n', 'r', 't', 'v', '\\', "'", '"'))
-          )
-        )
+            seq(choice('a', 'b', 'f', 'n', 'r', 't', 'v', '\\', '\'', '"')),
+          ),
+        ),
       ),
-      "'"
+      '\'',
     )),
 
-    nil: $ => 'nil',
-    true: $ => 'true',
-    false: $ => 'false',
-    iota: $ => 'iota',
+    nil: _ => 'nil',
+    true: _ => 'true',
+    false: _ => 'false',
+    iota: _ => 'iota',
 
     // http://stackoverflow.com/questions/13014947/regex-to-match-a-c-style-multiline-comment/36328890#36328890
-    comment: $ => token(choice(
+    comment: _ => token(choice(
       seq('//', /.*/),
       seq(
         '/*',
         /[^*]*\*+([^/*][^*]*\*+)*/,
-        '/'
-      )
-    ))
-  }
-})
-
+        '/',
+      ),
+    )),
+  },
+});
+
+/**
+ * Creates a rule to match one or more of the rules separated by a comma
+ *
+ * @param {Rule} rule
+ *
+ * @return {SeqRule}
+ *
+ */
 function commaSep1(rule) {
-  return seq(rule, repeat(seq(',', rule)))
+  return seq(rule, repeat(seq(',', rule)));
 }
 
+/**
+ * Creates a rule to optionally match one or more of the rules separated by a comma
+ *
+ * @param {Rule} rule
+ *
+ * @return {ChoiceRule}
+ *
+ */
 function commaSep(rule) {
-  return optional(commaSep1(rule))
+  return optional(commaSep1(rule));
 }
diff --git a/package.json b/package.json
index ffa60c50d..28083b191 100644
--- a/package.json
+++ b/package.json
@@ -17,10 +17,13 @@
     "nan": "^2.14.0"
   },
   "devDependencies": {
+    "eslint": "^8.45.0",
+    "eslint-config-google": "^0.14.0",
     "tree-sitter-cli": "^0.20.6"
   },
   "scripts": {
     "build": "tree-sitter generate && node-gyp build",
+    "lint": "eslint grammar.js",
     "test": "tree-sitter test && script/parse-examples",
     "test-windows": "tree-sitter test"
   },
diff --git a/src/grammar.json b/src/grammar.json
index 3554824ca..55e81d067 100644
--- a/src/grammar.json
+++ b/src/grammar.json
@@ -27,6 +27,10 @@
                       {
                         "type": "STRING",
                         "value": ";"
+                      },
+                      {
+                        "type": "STRING",
+                        "value": "\u0000"
                       }
                     ]
                   }
@@ -49,6 +53,10 @@
                       {
                         "type": "STRING",
                         "value": ";"
+                      },
+                      {
+                        "type": "STRING",
+                        "value": "\u0000"
                       }
                     ]
                   }
@@ -209,6 +217,10 @@
                           {
                             "type": "STRING",
                             "value": ";"
+                          },
+                          {
+                            "type": "STRING",
+                            "value": "\u0000"
                           }
                         ]
                       },
@@ -232,6 +244,10 @@
                         {
                           "type": "STRING",
                           "value": ";"
+                        },
+                        {
+                          "type": "STRING",
+                          "value": "\u0000"
                         }
                       ]
                     },
@@ -310,6 +326,10 @@
                           {
                             "type": "STRING",
                             "value": ";"
+                          },
+                          {
+                            "type": "STRING",
+                            "value": "\u0000"
                           }
                         ]
                       }
@@ -445,6 +465,10 @@
                           {
                             "type": "STRING",
                             "value": ";"
+                          },
+                          {
+                            "type": "STRING",
+                            "value": "\u0000"
                           }
                         ]
                       }
@@ -1027,6 +1051,10 @@
                           {
                             "type": "STRING",
                             "value": ";"
+                          },
+                          {
+                            "type": "STRING",
+                            "value": "\u0000"
                           }
                         ]
                       }
@@ -1496,6 +1524,10 @@
                           {
                             "type": "STRING",
                             "value": ";"
+                          },
+                          {
+                            "type": "STRING",
+                            "value": "\u0000"
                           }
                         ]
                       },
@@ -1519,6 +1551,10 @@
                         {
                           "type": "STRING",
                           "value": ";"
+                        },
+                        {
+                          "type": "STRING",
+                          "value": "\u0000"
                         }
                       ]
                     },
@@ -1686,6 +1722,10 @@
                           {
                             "type": "STRING",
                             "value": ";"
+                          },
+                          {
+                            "type": "STRING",
+                            "value": "\u0000"
                           }
                         ]
                       },
@@ -1709,6 +1749,10 @@
                         {
                           "type": "STRING",
                           "value": ";"
+                        },
+                        {
+                          "type": "STRING",
+                          "value": "\u0000"
                         }
                       ]
                     },
@@ -2062,6 +2106,10 @@
                       {
                         "type": "STRING",
                         "value": ";"
+                      },
+                      {
+                        "type": "STRING",
+                        "value": "\u0000"
                       }
                     ]
                   },
@@ -2088,6 +2136,10 @@
                         {
                           "type": "STRING",
                           "value": ";"
+                        },
+                        {
+                          "type": "STRING",
+                          "value": "\u0000"
                         }
                       ]
                     },
diff --git a/src/node-types.json b/src/node-types.json
index ac700bba1..60621cb7d 100644
--- a/src/node-types.json
+++ b/src/node-types.json
@@ -2515,6 +2515,10 @@
       }
     }
   },
+  {
+    "type": "\u0000",
+    "named": false
+  },
   {
     "type": "\n",
     "named": false
diff --git a/src/parser.c b/src/parser.c
index ba00b3fc0..cdcad118b 100644
Binary files a/src/parser.c and b/src/parser.c differ


Test patch:
diff --git a/corpus/declarations.txt b/test/corpus/declarations.txt
similarity index 100%
rename from corpus/declarations.txt
rename to test/corpus/declarations.txt
diff --git a/corpus/expressions.txt b/test/corpus/expressions.txt
similarity index 100%
rename from corpus/expressions.txt
rename to test/corpus/expressions.txt
diff --git a/corpus/literals.txt b/test/corpus/literals.txt
similarity index 100%
rename from corpus/literals.txt
rename to test/corpus/literals.txt
diff --git a/corpus/source_files.txt b/test/corpus/source_files.txt
similarity index 100%
rename from corpus/source_files.txt
rename to test/corpus/source_files.txt
diff --git a/corpus/statements.txt b/test/corpus/statements.txt
similarity index 100%
rename from corpus/statements.txt
rename to test/corpus/statements.txt
diff --git a/corpus/types.txt b/test/corpus/types.txt
similarity index 100%
rename from corpus/types.txt
rename to test/corpus/types.txt