From 7d2389d71cfa89aa9c91b48985b361e59fd8dd37 Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Thu, 19 Oct 2023 17:15:14 +0200 Subject: [PATCH 001/291] Fix free cycle for index files --- .../src/stack-graphs.tsg | 21 +++++++++++++++++-- ...mport-reexports-from-subdirectory-index.ts | 12 +++++++++++ ...ort-reexports-from-superdirectory-index.ts | 12 +++++++++++ 3 files changed, 43 insertions(+), 2 deletions(-) create mode 100644 languages/tree-sitter-stack-graphs-typescript/test/modules/import-reexports-from-subdirectory-index.ts create mode 100644 languages/tree-sitter-stack-graphs-typescript/test/modules/import-reexports-from-superdirectory-index.ts diff --git a/languages/tree-sitter-stack-graphs-typescript/src/stack-graphs.tsg b/languages/tree-sitter-stack-graphs-typescript/src/stack-graphs.tsg index 0a2a0d0b5..499022284 100644 --- a/languages/tree-sitter-stack-graphs-typescript/src/stack-graphs.tsg +++ b/languages/tree-sitter-stack-graphs-typescript/src/stack-graphs.tsg @@ -687,8 +687,25 @@ if none @is_type { (export_statement "*" source:(_)@name )@export_stmt { - ; export everything from source module - edge @export_stmt.exports -> @name.mod_ref + node expr_pop + node expr_push + attr (expr_pop) pop_symbol = "%E" + attr (expr_push) push_symbol = "%E" + edge expr_pop -> expr_push + + node type_pop + node type_push + attr (type_pop) pop_symbol = "%E" + attr (type_push) push_symbol = "%E" + edge type_pop -> type_push + + ; export all expressions from source module + edge @export_stmt.exports -> expr_pop + edge expr_push -> @name.mod_ref + + ; export all types from source module + edge @export_stmt.exports -> type_pop + edge type_push -> @name.mod_ref } ; export CLAUSE from MODULE diff --git a/languages/tree-sitter-stack-graphs-typescript/test/modules/import-reexports-from-subdirectory-index.ts b/languages/tree-sitter-stack-graphs-typescript/test/modules/import-reexports-from-subdirectory-index.ts new file mode 100644 index 000000000..e1871de89 --- /dev/null +++ b/languages/tree-sitter-stack-graphs-typescript/test/modules/import-reexports-from-subdirectory-index.ts @@ -0,0 +1,12 @@ +/* --- path: foo/index.ts --- */ + +export * from './bar' + +/* --- path: foo/bar.ts --- */ + +export let baz = 42 + +/* --- path: qux.ts --- */ + +import { baz } from './foo' +// ^ defined: 7 diff --git a/languages/tree-sitter-stack-graphs-typescript/test/modules/import-reexports-from-superdirectory-index.ts b/languages/tree-sitter-stack-graphs-typescript/test/modules/import-reexports-from-superdirectory-index.ts new file mode 100644 index 000000000..c37ae3cf7 --- /dev/null +++ b/languages/tree-sitter-stack-graphs-typescript/test/modules/import-reexports-from-superdirectory-index.ts @@ -0,0 +1,12 @@ +/* --- path: index.ts --- */ + +export * from './bar' + +/* --- path: bar.ts --- */ + +export let baz = 42 + +/* --- path: foo/qux.ts --- */ + +import { baz } from '..' +// ^ defined: 7 From 3bca8673f5f10459fe540bd06731baeb0be90f2b Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Fri, 20 Oct 2023 14:21:30 +0200 Subject: [PATCH 002/291] Add syntax types --- .../src/stack-graphs.tsg | 37 +++++++++++-------- 1 file changed, 22 insertions(+), 15 deletions(-) diff --git a/languages/tree-sitter-stack-graphs-typescript/src/stack-graphs.tsg b/languages/tree-sitter-stack-graphs-typescript/src/stack-graphs.tsg index 499022284..d08f4805b 100644 --- a/languages/tree-sitter-stack-graphs-typescript/src/stack-graphs.tsg +++ b/languages/tree-sitter-stack-graphs-typescript/src/stack-graphs.tsg @@ -1276,7 +1276,7 @@ if none @is_type { attr (@name.expr_def__ns) pop_symbol = "%E" edge @name.expr_def__ns -> @name.expr_def ; - attr (@name.expr_def) node_definition = @name + attr (@name.expr_def) node_definition = @name, syntax_type = "function" ; function type edge @name.expr_def -> @name.expr_def__typeof @@ -1430,10 +1430,17 @@ if none @is_async { edge @class_decl.type_export -> @class_decl.generic_type } [ - (abstract_class_declaration name:(_)) - (class_declaration name:(_)) + (interface_declaration name:(_)@name) +] { + attr (@name.type_def) syntax_type = "interface" +} +[ + (abstract_class_declaration name:(_)@name) + (class_declaration name:(_)@name) ; NOTE not for interface_declaration as body is already a type with an endpoint of needed ]@class_decl { + attr (@name.type_def) syntax_type = "class" + ; mark type scope as endpoint attr (@class_decl.generic_inner_type) is_endpoint } @@ -1449,7 +1456,7 @@ if none @is_async { attr (@name.expr_def__ns) pop_symbol = "%E" edge @name.expr_def__ns -> @name.expr_def ; - attr (@name.expr_def) node_definition = @name + attr (@name.expr_def) node_definition = @name, syntax_type = "class" ; type of expression is .static_type edge @name.expr_def -> @name.expr_def__typeof @@ -2291,7 +2298,7 @@ if none @is_async { attr (@name.expr_def__ns) pop_symbol = "%E" edge @name.expr_def__ns -> @name.expr_def ; - attr (@name.expr_def) node_definition = @name + attr (@name.expr_def) node_definition = @name, syntax_type = "module" ; namespaces mimic objects with members to access exported expressions edge @name.expr_def -> @name.expr_def__typeof @@ -2311,7 +2318,7 @@ if none @is_async { attr (@name.type_def__ns) pop_symbol = "%T" edge @name.type_def__ns -> @name.type_def ; - attr (@name.type_def) node_definition = @name + attr (@name.type_def) node_definition = @name, syntax_type = "module" ; namespaces mimic types with member types to access exported expressions edge @name.type_def -> @mod.type_member @@ -2353,7 +2360,7 @@ if none @is_async { attr (@name.expr_def__ns) pop_symbol = "%E" edge @name.expr_def__ns -> @name.expr_def ; - attr (@name.expr_def) node_definition = @name + attr (@name.expr_def) node_definition = @name, syntax_type = "module" ; namespaces mimic objects with members to access exported expressions edge @name.expr_def -> @name.expr_def__typeof @@ -2373,7 +2380,7 @@ if none @is_async { attr (@name.type_def__ns) pop_symbol = "%T" edge @name.type_def__ns -> @name.type_def ; - attr (@name.type_def) node_definition = @name + attr (@name.type_def) node_definition = @name, syntax_type = "module" ; namespaces mimic types with member types to access exported expressions edge @name.type_def -> @mod.type_member @@ -2472,7 +2479,7 @@ if none @is_async { attr (@name.expr_def__ns) pop_symbol = "%E" edge @name.expr_def__ns -> @name.expr_def ; - attr (@name.expr_def) node_definition = @name + attr (@name.expr_def) node_definition = @name, syntax_type = "type" ; type of enum expression is members edge @name.expr_def -> @name.expr_def__typeof @@ -2494,7 +2501,7 @@ if none @is_async { attr (@name.type_def__ns) pop_symbol = "%T" edge @name.type_def__ns -> @name.type_def ; - attr (@name.type_def) node_definition = @name + attr (@name.type_def) node_definition = @name, syntax_type = "type" edge @name.type_def -> @enum_decl.type ; edge @enum_decl.type -> @body.type_members @@ -2584,7 +2591,7 @@ if none @is_async { attr (@name.type_def__ns) pop_symbol = "%T" edge @name.type_def__ns -> @name.type_def ; - attr (@name.type_def) node_definition = @name + attr (@name.type_def) node_definition = @name, syntax_type = "type" edge @name.type_def -> @decl.generic_type ; ; type parameters are handled by generics rules below @@ -4468,7 +4475,7 @@ if none @is_async { attr (@sig.member) pop_symbol = "." edge @sig.member -> @name.expr_def ; - attr (@name.expr_def) node_definition = @name + attr (@name.expr_def) node_definition = @name, syntax_type = "field" } (property_signature @@ -4506,7 +4513,7 @@ if none @is_async { attr (@def.member) pop_symbol = "." edge @def.member -> @name.expr_def ; - attr (@name.expr_def) node_definition = @name + attr (@name.expr_def) node_definition = @name, syntax_type = "field" } (public_field_definition @@ -4590,7 +4597,7 @@ if none @is_acc { attr (@mem.member) pop_symbol = "." edge @mem.member -> @name.expr_def ; - attr (@name.expr_def) node_definition = @name + attr (@name.expr_def) node_definition = @name, syntax_type = "method" ; type of method is callable generic type edge @name.expr_def -> @name.expr_def__typeof @@ -4675,7 +4682,7 @@ if none @is_acc { attr (@mem.member) pop_symbol = "." edge @mem.member -> @name.expr_def ; - attr (@name.expr_def) node_definition = @name + attr (@name.expr_def) node_definition = @name, syntax_type = "field" } [ From 7482452cf396d8151f663aafdb6c9edafe23333b Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Wed, 25 Oct 2023 14:58:21 +0100 Subject: [PATCH 003/291] Fix panic when error was already reported --- tree-sitter-stack-graphs/src/cli/util.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tree-sitter-stack-graphs/src/cli/util.rs b/tree-sitter-stack-graphs/src/cli/util.rs index 66181a1fd..7d50c8a44 100644 --- a/tree-sitter-stack-graphs/src/cli/util.rs +++ b/tree-sitter-stack-graphs/src/cli/util.rs @@ -437,7 +437,7 @@ impl<'a> CLIFileReporter<'a> { status: &str, details: Option<&dyn std::fmt::Display>, ) { - if !self.path_logged { + if !self.path_logged || self.status_logged { return; } self.failure(status, details); From d30ec74c946e2dbe0d4c64aff2146295f8c071a0 Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Sun, 22 Oct 2023 13:36:02 +0200 Subject: [PATCH 004/291] Reduce source span for async definitions --- .../tree-sitter-stack-graphs-typescript/src/stack-graphs.tsg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/languages/tree-sitter-stack-graphs-typescript/src/stack-graphs.tsg b/languages/tree-sitter-stack-graphs-typescript/src/stack-graphs.tsg index d08f4805b..2d05f5891 100644 --- a/languages/tree-sitter-stack-graphs-typescript/src/stack-graphs.tsg +++ b/languages/tree-sitter-stack-graphs-typescript/src/stack-graphs.tsg @@ -6146,7 +6146,7 @@ if none @is_acc { attr (@async.async_type__type_app) push_scoped_symbol = "<>", scope = @async.async_type__type_args edge @async.async_type__type_app -> @async.async_type__promise_ref ; - attr (@async.async_type__promise_ref) symbol_reference = "Promise", source_node = @async + attr (@async.async_type__promise_ref) symbol_reference = "Promise", source_node = @async, empty_source_span edge @async.async_type__promise_ref -> @async.async_type__promise_ref__ns ; attr (@async.async_type__promise_ref__ns) push_symbol = "%T" From e2151cc8c2e5a7aed04ee291f6f1d60ab9b26d44 Mon Sep 17 00:00:00 2001 From: jdonszelmann Date: Mon, 30 Oct 2023 17:07:02 +0100 Subject: [PATCH 005/291] make tree-sitter feature optional Co-authored-by: Terts Diepraam --- stack-graphs/Cargo.toml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/stack-graphs/Cargo.toml b/stack-graphs/Cargo.toml index 2e55e1768..bc08b1d82 100644 --- a/stack-graphs/Cargo.toml +++ b/stack-graphs/Cargo.toml @@ -13,11 +13,13 @@ authors = [ edition = "2018" [features] +default = ["tree-sitter"] bincode = ["dep:bincode", "lsp-positions/bincode"] copious-debugging = [] serde = ["dep:serde", "serde_with", "lsp-positions/serde"] storage = ["bincode", "rusqlite"] visualization = ["serde", "serde_json"] +tree-sitter = ["lsp-positions/tree-sitter"] [lib] # All of our tests are in the tests/it "integration" test executable. @@ -32,7 +34,7 @@ enumset = "1.1" fxhash = "0.2" itertools = "0.10" libc = "0.2" -lsp-positions = { version = "0.3", path = "../lsp-positions" } +lsp-positions = { version = "0.3", path = "../lsp-positions", default-features=false } rusqlite = { version = "0.28", optional = true, features = ["bundled", "functions"] } serde = { version = "1.0", optional = true, features = ["derive"] } serde_json = { version = "1.0", optional = true } From 2038d46d630faf389c864f07499caf409d008134 Mon Sep 17 00:00:00 2001 From: jdonszelmann Date: Mon, 30 Oct 2023 17:40:07 +0100 Subject: [PATCH 006/291] change to dep:tree-sitter Co-authored-by: Terts Diepraam --- lsp-positions/Cargo.toml | 2 +- tree-sitter-stack-graphs/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lsp-positions/Cargo.toml b/lsp-positions/Cargo.toml index 2b2d496b3..058b318f7 100644 --- a/lsp-positions/Cargo.toml +++ b/lsp-positions/Cargo.toml @@ -17,7 +17,7 @@ edition = "2018" test = false [features] -default = ["tree-sitter"] +default = ["dep:tree-sitter"] bincode = ["dep:bincode"] [dependencies] diff --git a/tree-sitter-stack-graphs/Cargo.toml b/tree-sitter-stack-graphs/Cargo.toml index 2d6dabbd1..2de26a86f 100644 --- a/tree-sitter-stack-graphs/Cargo.toml +++ b/tree-sitter-stack-graphs/Cargo.toml @@ -62,7 +62,7 @@ env_logger = { version = "0.9", optional = true } indoc = { version = "1.0", optional = true } itertools = "0.10" log = "0.4" -lsp-positions = { version="0.3", path="../lsp-positions" } +lsp-positions = { version="0.3", path="../lsp-positions", features=["tree-sitter"] } once_cell = "1" pathdiff = { version = "0.2.1", optional = true } regex = "1" From 0bbb98ab2fba6b3e97e1420d07e6384167c407b5 Mon Sep 17 00:00:00 2001 From: jdonszelmann Date: Mon, 30 Oct 2023 17:41:43 +0100 Subject: [PATCH 007/291] remove tree-sitter feature from stack-graphs Co-authored-by: Terts Diepraam --- stack-graphs/Cargo.toml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/stack-graphs/Cargo.toml b/stack-graphs/Cargo.toml index bc08b1d82..e7acaac06 100644 --- a/stack-graphs/Cargo.toml +++ b/stack-graphs/Cargo.toml @@ -13,13 +13,12 @@ authors = [ edition = "2018" [features] -default = ["tree-sitter"] +default = [] bincode = ["dep:bincode", "lsp-positions/bincode"] copious-debugging = [] serde = ["dep:serde", "serde_with", "lsp-positions/serde"] storage = ["bincode", "rusqlite"] visualization = ["serde", "serde_json"] -tree-sitter = ["lsp-positions/tree-sitter"] [lib] # All of our tests are in the tests/it "integration" test executable. From 25b77e9ed173f131d8ca4f2bcabe1f329252f939 Mon Sep 17 00:00:00 2001 From: jdonszelmann Date: Mon, 30 Oct 2023 17:49:49 +0100 Subject: [PATCH 008/291] remove default feature Co-authored-by: Terts Diepraam terts.diepraam@gmail.com --- stack-graphs/Cargo.toml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/stack-graphs/Cargo.toml b/stack-graphs/Cargo.toml index e7acaac06..d756a2c79 100644 --- a/stack-graphs/Cargo.toml +++ b/stack-graphs/Cargo.toml @@ -13,7 +13,6 @@ authors = [ edition = "2018" [features] -default = [] bincode = ["dep:bincode", "lsp-positions/bincode"] copious-debugging = [] serde = ["dep:serde", "serde_with", "lsp-positions/serde"] @@ -33,7 +32,7 @@ enumset = "1.1" fxhash = "0.2" itertools = "0.10" libc = "0.2" -lsp-positions = { version = "0.3", path = "../lsp-positions", default-features=false } +lsp-positions = { version = "0.3", path = "../lsp-positions"} rusqlite = { version = "0.28", optional = true, features = ["bundled", "functions"] } serde = { version = "1.0", optional = true, features = ["derive"] } serde_json = { version = "1.0", optional = true } From 76261f7c84bdb618511526ebd76bce4f93ac0004 Mon Sep 17 00:00:00 2001 From: jdonszelmann Date: Mon, 30 Oct 2023 17:59:34 +0100 Subject: [PATCH 009/291] final suggestions --- lsp-positions/Cargo.toml | 2 +- stack-graphs/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lsp-positions/Cargo.toml b/lsp-positions/Cargo.toml index 058b318f7..242a894ff 100644 --- a/lsp-positions/Cargo.toml +++ b/lsp-positions/Cargo.toml @@ -17,8 +17,8 @@ edition = "2018" test = false [features] -default = ["dep:tree-sitter"] bincode = ["dep:bincode"] +tree-sitter = ["dep:tree-sitter"] [dependencies] memchr = "2.4" diff --git a/stack-graphs/Cargo.toml b/stack-graphs/Cargo.toml index d756a2c79..2e55e1768 100644 --- a/stack-graphs/Cargo.toml +++ b/stack-graphs/Cargo.toml @@ -32,7 +32,7 @@ enumset = "1.1" fxhash = "0.2" itertools = "0.10" libc = "0.2" -lsp-positions = { version = "0.3", path = "../lsp-positions"} +lsp-positions = { version = "0.3", path = "../lsp-positions" } rusqlite = { version = "0.28", optional = true, features = ["bundled", "functions"] } serde = { version = "1.0", optional = true, features = ["derive"] } serde_json = { version = "1.0", optional = true } From 31c0523280bb8d7041b7b09ffce62bb4aa2c78dc Mon Sep 17 00:00:00 2001 From: Rebecca Valentine Date: Tue, 31 Oct 2023 15:28:42 -0400 Subject: [PATCH 010/291] Adds many jsx rules Co-Authored-By: Rob Rix --- .../src/stack-graphs.tsg | 249 ++++++++++++++++++ 1 file changed, 249 insertions(+) diff --git a/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg b/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg index 254445369..9d29db8b3 100644 --- a/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg +++ b/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg @@ -3476,6 +3476,255 @@ inherit .return_or_yield edge @class.after_scope -> @name.after_scope } +(jsx_element + open_tag:(_)@open_tag + close_tag:(_)@close_tag)@jsx_element { + + node @jsx_element.before_scope + node @jsx_element.after_scope + node @jsx_element.value + + edge @open_tag.before_scope -> @jsx_element.before_scope + edge @jsx_element.after_scope -> @close_tag.after_scope + + ; LATER-TODO scope propagation through empty JSX children + ; currently unsupported by tree sitter queries + ; THIS IS A HUGE HACK AND MUST BE FIXED + edge @close_tag.before_scope -> @open_tag.after_scope + +} + +(jsx_element + open_tag:(_)@open_tag + . + [ + (jsx_text) + (jsx_element) + (jsx_self_closing_element) + (jsx_fragment) + (jsx_expression) + ]@first_child +) { + edge @first_child.before_scope -> @open_tag.after_scope +} + +(jsx_element + [ + (jsx_text) + (jsx_element) + (jsx_self_closing_element) + (jsx_fragment) + (jsx_expression) + ]@left_child + . + [ + (jsx_text) + (jsx_element) + (jsx_self_closing_element) + (jsx_fragment) + (jsx_expression) + ]@right_child +) { + edge @right_child.before_scope -> @left_child.after_scope +} + +(jsx_element + [ + (jsx_text) + (jsx_element) + (jsx_self_closing_element) + (jsx_fragment) + (jsx_expression) + ]@last_child + . + close_tag:(_)@close_tag +) { + edge @close_tag.before_scope -> @last_child.after_scope +} + +(jsx_text)@jsx_text { + node @jsx_text.before_scope + node @jsx_text.after_scope + + edge @jsx_text.after_scope -> @jsx_text.before_scope +} + +(jsx_opening_element + name:(_)@element_name)@jsx_opening_element { + + node @jsx_opening_element.before_scope + node @jsx_opening_element.after_scope + + edge @element_name.before_scope -> @jsx_opening_element.before_scope + +} + +(jsx_opening_element + name:(_)@element_name + !attribute)@jsx_opening_element +{ + + edge @jsx_opening_element.after_scope -> @element_name.after_scope + +} + +(jsx_opening_element + name:(_)@element_name + . + attribute:(_)@first_attr +) { + + edge @first_attr.before_scope -> @element_name.after_scope + +} + +(jsx_opening_element + attribute:(_)@left_attr + . + attribute:(_)@right_attr +) { + + edge @right_attr.before_scope -> @left_attr.after_scope + +} + +(jsx_opening_element + attribute:(_)@last_attr + .)@jsx_opening_element +{ + + edge @jsx_opening_element.after_scope -> @last_attr.after_scope + +} + +(jsx_opening_element name:(identifier)@name) { + + node @name.before_scope + node @name.after_scope + + edge @name.after_scope -> @name.before_scope + +} + +(jsx_attribute)@jsx_attribute { + + node @jsx_attribute.before_scope + node @jsx_attribute.after_scope + + ; LATER-TODO scope propagation through empty child lists + ; currently unsupported by tree sitter queries + ; THIS IS A HUGE HACK AND MUST BE FIXED + edge @jsx_attribute.after_scope -> @jsx_attribute.before_scope + +} + +(jsx_attribute (_) . (_)@attr_value)@jsx_attribute { + + edge @attr_value.before_scope -> @jsx_attribute.before_scope + edge @jsx_attribute.after_scope -> @attr_value.after_scope + +} + +(jsx_self_closing_element + name:(_)@element_name)@jsx_self_closing_element { + + node @jsx_self_closing_element.before_scope + node @jsx_self_closing_element.after_scope + + edge @element_name.before_scope -> @jsx_self_closing_element.before_scope + +} + +(jsx_self_closing_element + name:(_)@element_name + !attribute)@jsx_self_closing_element +{ + + edge @jsx_self_closing_element.after_scope -> @element_name.after_scope + +} + +(jsx_self_closing_element + name:(_)@element_name + . + attribute:(_)@first_attr +) { + + edge @first_attr.before_scope -> @element_name.after_scope + +} + +(jsx_self_closing_element + attribute:(_)@left_attr + . + attribute:(_)@right_attr +) { + + edge @right_attr.before_scope -> @left_attr.after_scope + +} + +(jsx_self_closing_element + attribute:(_)@last_attr + .)@jsx_self_closing_element +{ + + edge @jsx_self_closing_element.after_scope -> @last_attr.after_scope + +} + +(jsx_self_closing_element name:(identifier)@name) { + + node @name.before_scope + node @name.after_scope + + edge @name.after_scope -> @name.before_scope + +} + +(jsx_expression)@jsx_expression { + + node @jsx_expression.before_scope + node @jsx_expression.after_scope + + ; LATER-TODO scope propagation through empty child lists + ; currently unsupported by tree sitter queries + ; THIS IS A HUGE HACK AND MUST BE FIXED + edge @jsx_expression.after_scope -> @jsx_expression.before_scope + +} + +(jsx_expression (_)@child)@jsx_expression { + + edge @child.before_scope -> @jsx_expression.before_scope + edge @jsx_expression.after_scope -> @child.after_scope + +} + +(jsx_closing_element + name:(_)@element_name)@jsx_closing_element +{ + + node @jsx_closing_element.before_scope + node @jsx_closing_element.after_scope + + edge @element_name.before_scope -> @jsx_closing_element.before_scope + edge @jsx_closing_element.after_scope -> @element_name.after_scope + +} + +(jsx_closing_element + name:(_)@element_name) +{ + + node @element_name.before_scope + node @element_name.after_scope + + edge @element_name.after_scope -> @element_name.before_scope + +} + + From a9b8b05ee42ff8dba65b49e378f2e0764beb6972 Mon Sep 17 00:00:00 2001 From: Rebecca Valentine Date: Tue, 31 Oct 2023 15:56:07 -0400 Subject: [PATCH 011/291] Adds missing self closing element value Co-Authored-By: Rob Rix --- .../tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg | 1 + 1 file changed, 1 insertion(+) diff --git a/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg b/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg index 9d29db8b3..1731fcd59 100644 --- a/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg +++ b/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg @@ -3630,6 +3630,7 @@ inherit .return_or_yield node @jsx_self_closing_element.before_scope node @jsx_self_closing_element.after_scope + node @jsx_self_closing_element.value edge @element_name.before_scope -> @jsx_self_closing_element.before_scope From 12ff224f4e73a6340bb99a1cbc032bc108f2abcb Mon Sep 17 00:00:00 2001 From: Rebecca Valentine Date: Tue, 31 Oct 2023 18:20:46 -0400 Subject: [PATCH 012/291] Adds jsx_fragment Co-Authored-By: Rob Rix --- .../src/stack-graphs.tsg | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg b/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg index 1731fcd59..812827e0c 100644 --- a/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg +++ b/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg @@ -3725,6 +3725,37 @@ inherit .return_or_yield } +(jsx_fragment)@jsx_fragment { + node @jsx_fragment.before_scope + node @jsx_fragment.after_scope + + ; LATER-TODO scope propagation through empty child lists + ; currently unsupported by tree sitter queries + ; THIS IS A HUGE HACK AND MUST BE FIXED + edge @jsx_fragment.after_scope -> @jsx_fragment.before_scope +} + +(jsx_fragment + . + (_)@first_child)@jsx_fragment +{ + edge @first_child.before_scope -> @jsx_fragment.before_scope +} + +(jsx_fragment + (_)@left_child + . + (_)@right_child) +{ + edge @right_child.before_scope -> @left_child.after_scope +} + +(jsx_fragment + (_)@last_child + .)@jsx_fragment +{ + edge @jsx_fragment.after_scope -> @last_child.after_scope +} From 58ee031c8e4b1a1cf303253cee8571ed82ebecc2 Mon Sep 17 00:00:00 2001 From: Rebecca Valentine Date: Wed, 1 Nov 2023 15:01:32 -0400 Subject: [PATCH 013/291] Removes hack notes and associated code --- .../src/stack-graphs.tsg | 57 ++++++++----------- 1 file changed, 23 insertions(+), 34 deletions(-) diff --git a/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg b/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg index 812827e0c..564fb0bb9 100644 --- a/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg +++ b/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg @@ -3478,7 +3478,8 @@ inherit .return_or_yield (jsx_element open_tag:(_)@open_tag - close_tag:(_)@close_tag)@jsx_element { + close_tag:(_)@close_tag + (_)*@children)@jsx_element { node @jsx_element.before_scope node @jsx_element.after_scope @@ -3487,10 +3488,9 @@ inherit .return_or_yield edge @open_tag.before_scope -> @jsx_element.before_scope edge @jsx_element.after_scope -> @close_tag.after_scope - ; LATER-TODO scope propagation through empty JSX children - ; currently unsupported by tree sitter queries - ; THIS IS A HUGE HACK AND MUST BE FIXED - edge @close_tag.before_scope -> @open_tag.after_scope + if (is-empty @children) { + edge @close_tag.before_scope -> @open_tag.after_scope + } } @@ -3606,22 +3606,17 @@ inherit .return_or_yield } -(jsx_attribute)@jsx_attribute { +(jsx_attribute (_) . (_)?@attr_value)@jsx_attribute { node @jsx_attribute.before_scope node @jsx_attribute.after_scope - ; LATER-TODO scope propagation through empty child lists - ; currently unsupported by tree sitter queries - ; THIS IS A HUGE HACK AND MUST BE FIXED - edge @jsx_attribute.after_scope -> @jsx_attribute.before_scope - -} - -(jsx_attribute (_) . (_)@attr_value)@jsx_attribute { - - edge @attr_value.before_scope -> @jsx_attribute.before_scope - edge @jsx_attribute.after_scope -> @attr_value.after_scope + if none @attr_value { + edge @jsx_attribute.after_scope -> @jsx_attribute.before_scope + } else { + edge @attr_value.before_scope -> @jsx_attribute.before_scope + edge @jsx_attribute.after_scope -> @attr_value.after_scope + } } @@ -3683,22 +3678,17 @@ inherit .return_or_yield } -(jsx_expression)@jsx_expression { +(jsx_expression (_)?@child)@jsx_expression { node @jsx_expression.before_scope node @jsx_expression.after_scope - ; LATER-TODO scope propagation through empty child lists - ; currently unsupported by tree sitter queries - ; THIS IS A HUGE HACK AND MUST BE FIXED - edge @jsx_expression.after_scope -> @jsx_expression.before_scope - -} - -(jsx_expression (_)@child)@jsx_expression { - - edge @child.before_scope -> @jsx_expression.before_scope - edge @jsx_expression.after_scope -> @child.after_scope + if none @child { + edge @jsx_expression.after_scope -> @jsx_expression.before_scope + } else { + edge @child.before_scope -> @jsx_expression.before_scope + edge @jsx_expression.after_scope -> @child.after_scope + } } @@ -3725,14 +3715,13 @@ inherit .return_or_yield } -(jsx_fragment)@jsx_fragment { +(jsx_fragment (_)*@children)@jsx_fragment { node @jsx_fragment.before_scope node @jsx_fragment.after_scope - ; LATER-TODO scope propagation through empty child lists - ; currently unsupported by tree sitter queries - ; THIS IS A HUGE HACK AND MUST BE FIXED - edge @jsx_fragment.after_scope -> @jsx_fragment.before_scope + if (is-empty @children) { + edge @jsx_fragment.after_scope -> @jsx_fragment.before_scope + } } (jsx_fragment From fd6efe57c18594c105fc208affbf20bceac18864 Mon Sep 17 00:00:00 2001 From: Rebecca Valentine Date: Wed, 1 Nov 2023 15:01:48 -0400 Subject: [PATCH 014/291] Removes remnant hack note --- .../tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg | 3 --- 1 file changed, 3 deletions(-) diff --git a/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg b/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg index 564fb0bb9..e23716717 100644 --- a/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg +++ b/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg @@ -4034,9 +4034,6 @@ inherit .return_or_yield ;; #### Object Patterns -; LATER-TODO scope propagation through empty object patterns -; currently unsupported by tree sitter queries -; THIS IS A HUGE HACK AND MUST BE FIXED (object_pattern (_)* @entries)@object_pat { if (is-empty @entries) { edge @object_pat.after_scope -> @object_pat.before_scope From 1959db1855ec869928539ff5cfd4134b11341ee7 Mon Sep 17 00:00:00 2001 From: Rebecca Valentine Date: Thu, 2 Nov 2023 10:16:06 -0700 Subject: [PATCH 015/291] Adds tests and makes them pass --- .../src/stack-graphs.tsg | 14 ++++++- .../test/base_syntax.js | 7 +++- .../test/jsx/jsx_core.js | 40 +++++++++++++++++++ .../test/jsx/jsx_fragment.js | 8 ++++ .../test/jsx/jsx_namespace_name.js | 8 ++++ .../test/jsx/jsx_text.js | 8 ++++ 6 files changed, 83 insertions(+), 2 deletions(-) create mode 100644 languages/tree-sitter-stack-graphs-javascript/test/jsx/jsx_core.js create mode 100644 languages/tree-sitter-stack-graphs-javascript/test/jsx/jsx_fragment.js create mode 100644 languages/tree-sitter-stack-graphs-javascript/test/jsx/jsx_namespace_name.js create mode 100644 languages/tree-sitter-stack-graphs-javascript/test/jsx/jsx_text.js diff --git a/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg b/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg index e23716717..1a1bff821 100644 --- a/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg +++ b/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg @@ -3597,7 +3597,7 @@ inherit .return_or_yield } -(jsx_opening_element name:(identifier)@name) { +(jsx_opening_element name:(_)@name) { node @name.before_scope node @name.after_scope @@ -3631,6 +3631,17 @@ inherit .return_or_yield } +(jsx_self_closing_element + name:(_)@element_name) +{ + + node @element_name.before_scope + node @element_name.after_scope + + edge @element_name.after_scope -> @element_name.before_scope + +} + (jsx_self_closing_element name:(_)@element_name !attribute)@jsx_self_closing_element @@ -3718,6 +3729,7 @@ inherit .return_or_yield (jsx_fragment (_)*@children)@jsx_fragment { node @jsx_fragment.before_scope node @jsx_fragment.after_scope + node @jsx_fragment.value if (is-empty @children) { edge @jsx_fragment.after_scope -> @jsx_fragment.before_scope diff --git a/languages/tree-sitter-stack-graphs-javascript/test/base_syntax.js b/languages/tree-sitter-stack-graphs-javascript/test/base_syntax.js index 858f854cd..e732ced2a 100644 --- a/languages/tree-sitter-stack-graphs-javascript/test/base_syntax.js +++ b/languages/tree-sitter-stack-graphs-javascript/test/base_syntax.js @@ -62,4 +62,9 @@ x++; x += 1; (1, 2); 1 ? 2 : 3; -class { }; \ No newline at end of file +class { }; + + + <>doo + {garply} + \ No newline at end of file diff --git a/languages/tree-sitter-stack-graphs-javascript/test/jsx/jsx_core.js b/languages/tree-sitter-stack-graphs-javascript/test/jsx/jsx_core.js new file mode 100644 index 000000000..64e5c3b3b --- /dev/null +++ b/languages/tree-sitter-stack-graphs-javascript/test/jsx/jsx_core.js @@ -0,0 +1,40 @@ +// The core of JSX tests here verify the behavior of the following node types: +// jsx_element +// jsx_identifier +// jsx_attribute +// jsx_expression +// jsx_opening_element +// jsx_closing_element +// There is no real way to avoid testing all of these at once, +// and so we don't even try to. + +let x = 1; + +// Flow In + +const el = {x}; +// ^ defined: 11 +// ^ defined: 11 + +// Flow Out + +const el = + {z = 3} +; + +/**/ y; +// ^ defined: 21 + +/**/ z; +// ^ defined: 22 + +// Flow Across + +const el = + {w}; +// ^ defined: 33 + +// Flow Around + +/**/ x; +// ^ defined: 11 \ No newline at end of file diff --git a/languages/tree-sitter-stack-graphs-javascript/test/jsx/jsx_fragment.js b/languages/tree-sitter-stack-graphs-javascript/test/jsx/jsx_fragment.js new file mode 100644 index 000000000..02d5f5d68 --- /dev/null +++ b/languages/tree-sitter-stack-graphs-javascript/test/jsx/jsx_fragment.js @@ -0,0 +1,8 @@ +let x = 1; + +// Flow Around + +const el = <>; + +/**/ x; +// ^ defined: 1 \ No newline at end of file diff --git a/languages/tree-sitter-stack-graphs-javascript/test/jsx/jsx_namespace_name.js b/languages/tree-sitter-stack-graphs-javascript/test/jsx/jsx_namespace_name.js new file mode 100644 index 000000000..2c325ed51 --- /dev/null +++ b/languages/tree-sitter-stack-graphs-javascript/test/jsx/jsx_namespace_name.js @@ -0,0 +1,8 @@ +let x = 1; + +// Flow Around + +const el = ; + +/**/ x; +// ^ defined: 1 \ No newline at end of file diff --git a/languages/tree-sitter-stack-graphs-javascript/test/jsx/jsx_text.js b/languages/tree-sitter-stack-graphs-javascript/test/jsx/jsx_text.js new file mode 100644 index 000000000..0cc359765 --- /dev/null +++ b/languages/tree-sitter-stack-graphs-javascript/test/jsx/jsx_text.js @@ -0,0 +1,8 @@ +let x = 1; + +// Flow Around + +const el = bar; + +/**/ x; +// ^ defined: 1 \ No newline at end of file From 073ed25e67089b7b956991192a18bfdd7b581df7 Mon Sep 17 00:00:00 2001 From: Rebecca Valentine Date: Thu, 2 Nov 2023 17:05:21 -0700 Subject: [PATCH 016/291] Cleans up element names and makes them reference correctly --- .../src/stack-graphs.tsg | 95 +++++++++++++------ .../test/jsx/jsx_core.js | 14 ++- .../test/jsx/jsx_namespace_name.js | 15 ++- .../test/jsx/jsx_self_closing_element.js | 37 ++++++++ 4 files changed, 126 insertions(+), 35 deletions(-) create mode 100644 languages/tree-sitter-stack-graphs-javascript/test/jsx/jsx_self_closing_element.js diff --git a/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg b/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg index 1a1bff821..b8a220f41 100644 --- a/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg +++ b/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg @@ -3597,15 +3597,6 @@ inherit .return_or_yield } -(jsx_opening_element name:(_)@name) { - - node @name.before_scope - node @name.after_scope - - edge @name.after_scope -> @name.before_scope - -} - (jsx_attribute (_) . (_)?@attr_value)@jsx_attribute { node @jsx_attribute.before_scope @@ -3631,17 +3622,6 @@ inherit .return_or_yield } -(jsx_self_closing_element - name:(_)@element_name) -{ - - node @element_name.before_scope - node @element_name.after_scope - - edge @element_name.after_scope -> @element_name.before_scope - -} - (jsx_self_closing_element name:(_)@element_name !attribute)@jsx_self_closing_element @@ -3680,15 +3660,6 @@ inherit .return_or_yield } -(jsx_self_closing_element name:(identifier)@name) { - - node @name.before_scope - node @name.after_scope - - edge @name.after_scope -> @name.before_scope - -} - (jsx_expression (_)?@child)@jsx_expression { node @jsx_expression.before_scope @@ -3715,8 +3686,14 @@ inherit .return_or_yield } -(jsx_closing_element - name:(_)@element_name) +[ + (jsx_opening_element + name:(identifier)@element_name) + (jsx_self_closing_element + name:(identifier)@element_name) + (jsx_closing_element + name:(identifier)@element_name) +] { node @element_name.before_scope @@ -3724,6 +3701,62 @@ inherit .return_or_yield edge @element_name.after_scope -> @element_name.before_scope + scan (source-text @element_name) { + ; standard HTML elements + "^(a|abbr|acronym|address|applet|area|article|aside|audio|b|base|basefont|bdi|bdo|big|blockquote|body|br|button|canvas|caption|center|cite|code|col|colgroup|data|datalist|dd|del|details|dfn|dialog|dir|div|dl|dt|em|embed|fieldset|figcaption|figure|font|footer|form|frame|frameset|h1|h2|h3|h4|h5|h6|head|header|hgroup|hr|html|i|iframe|input|ins|kbd|label|legend|li|link|main|map|mark|menu|meta|meter|nav|noframes|noscript|object|ol|optgroup|option|output|p|param|picture|pre|progress|q|rp|rt|ruby|s|samp|script|search|section|select|small|source|span|strike|strong|style|sub|summary|sup|svg|table|tbody|td|template|textarea|tfoot|th|thead|time|title|tr|track|tt|u|ul|var|video|wbr)$" { + ; do nothing! + } + + ; everything else + "^.+$" { + node element_name_pop + attr (element_name_pop) node_reference = @element_name + edge element_name_pop -> @element_name.before_scope + } + } + +} + +(nested_identifier)@nested_identifier { + node @nested_identifier.before_scope + node @nested_identifier.after_scope + node @nested_identifier.value + + edge @nested_identifier.after_scope -> @nested_identifier.before_scope +} + +(nested_identifier + (identifier)@first_part + (identifier)@second_part)@nested_identifier +{ + node @first_part.value + node @second_part.value + node guard_member + + attr (@first_part.value) node_reference = @first_part + attr (@second_part.value) node_reference = @second_part + attr (guard_member) symbol_reference = "GUARD:MEMBER" + + edge @first_part.value -> @nested_identifier.before_scope + edge guard_member -> @first_part.value + edge @second_part.value -> guard_member + edge @nested_identifier.value -> @second_part.value +} + +(nested_identifier + (nested_identifier)@first_part + (identifier)@second_part)@nested_identifier +{ + node @second_part.value + node guard_member + + attr (@second_part.value) node_reference = @second_part + attr (guard_member) symbol_reference = "GUARD:MEMBER" + + edge @first_part.before_scope -> @nested_identifier.before_scope + edge guard_member -> @first_part.value + edge @second_part.value -> guard_member + edge @nested_identifier.value -> @second_part.value } (jsx_fragment (_)*@children)@jsx_fragment { diff --git a/languages/tree-sitter-stack-graphs-javascript/test/jsx/jsx_core.js b/languages/tree-sitter-stack-graphs-javascript/test/jsx/jsx_core.js index 64e5c3b3b..fc0674833 100644 --- a/languages/tree-sitter-stack-graphs-javascript/test/jsx/jsx_core.js +++ b/languages/tree-sitter-stack-graphs-javascript/test/jsx/jsx_core.js @@ -16,6 +16,10 @@ const el = {x}; // ^ defined: 11 // ^ defined: 11 +const el2 = +// ^ defined: 11 +// ^ defined: 11 + // Flow Out const el = @@ -23,16 +27,20 @@ const el = ; /**/ y; -// ^ defined: 21 +// ^ defined: 25 /**/ z; -// ^ defined: 22 +// ^ defined: 26 // Flow Across const el = {w}; -// ^ defined: 33 +// ^ defined: 37 + +const el = ; +// ^ defined: 41 // Flow Around diff --git a/languages/tree-sitter-stack-graphs-javascript/test/jsx/jsx_namespace_name.js b/languages/tree-sitter-stack-graphs-javascript/test/jsx/jsx_namespace_name.js index 2c325ed51..ec59c3dca 100644 --- a/languages/tree-sitter-stack-graphs-javascript/test/jsx/jsx_namespace_name.js +++ b/languages/tree-sitter-stack-graphs-javascript/test/jsx/jsx_namespace_name.js @@ -5,4 +5,17 @@ let x = 1; const el = ; /**/ x; -// ^ defined: 1 \ No newline at end of file +// ^ defined: 1 + +// Flow In + +let foo = { + bar: { + baz: 1 + } +}; + +const el2 = ; +// ^ defined: 12 +// ^ defined: 13 +// ^ defined: 14 \ No newline at end of file diff --git a/languages/tree-sitter-stack-graphs-javascript/test/jsx/jsx_self_closing_element.js b/languages/tree-sitter-stack-graphs-javascript/test/jsx/jsx_self_closing_element.js new file mode 100644 index 000000000..937ca0970 --- /dev/null +++ b/languages/tree-sitter-stack-graphs-javascript/test/jsx/jsx_self_closing_element.js @@ -0,0 +1,37 @@ +// The core of JSX tests here verify the behavior of the following node types: +// jsx_element +// jsx_identifier +// jsx_attribute +// jsx_expression +// jsx_opening_element +// jsx_closing_element +// There is no real way to avoid testing all of these at once, +// and so we don't even try to. + +let x = 1; + +// Flow In + +const el = ; +// ^ defined: 11 + +const el2 = +// ^ defined: 11 + +// Flow Out + +const el = ; + +/**/ y; +// ^ defined: 23 + +// Flow Across + +const el = ; +// ^ defined: 30 + +// Flow Around + +/**/ x; +// ^ defined: 11 \ No newline at end of file From 9ffe369f72bfe7b9b30a0cf3b301e4e2573d8a95 Mon Sep 17 00:00:00 2001 From: Rebecca Valentine Date: Fri, 3 Nov 2023 15:30:50 -0700 Subject: [PATCH 017/291] Adds definiens for module name --- .../tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg | 1 + 1 file changed, 1 insertion(+) diff --git a/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg b/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg index b8a220f41..6b644d02f 100644 --- a/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg +++ b/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg @@ -327,6 +327,7 @@ inherit .return_or_yield edge @prog.before_scope -> @prog.hoist_point attr (prog_module_scope) pop_symbol = "GUARD:MODULE" + attr (prog_module_pop_start) definiens = @prog edge @prog.pkg_pop -> prog_module_pop_start edge module_pop_end -> prog_module_scope edge prog_module_scope -> @prog.after_scope From b2fc0434435b2a9ec6e783a5dccea25130856abb Mon Sep 17 00:00:00 2001 From: Rebecca Valentine Date: Fri, 3 Nov 2023 15:40:13 -0700 Subject: [PATCH 018/291] Ibid. --- .../tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg b/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg index 6b644d02f..bdc5d3f22 100644 --- a/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg +++ b/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg @@ -320,6 +320,7 @@ inherit .return_or_yield "([^/]+)$" { attr (module_pop_end) symbol_definition = $1, source_node = @prog attr (module_pop_end) empty_source_span + attr (module_pop_end) definiens = @prog } } @@ -327,7 +328,6 @@ inherit .return_or_yield edge @prog.before_scope -> @prog.hoist_point attr (prog_module_scope) pop_symbol = "GUARD:MODULE" - attr (prog_module_pop_start) definiens = @prog edge @prog.pkg_pop -> prog_module_pop_start edge module_pop_end -> prog_module_scope edge prog_module_scope -> @prog.after_scope From 67014222d1522e769508b6a3595f60ce895f9594 Mon Sep 17 00:00:00 2001 From: Rebecca Valentine Date: Fri, 3 Nov 2023 17:07:28 -0700 Subject: [PATCH 019/291] Removes empty source span for prog node --- .../tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg b/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg index bdc5d3f22..895099776 100644 --- a/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg +++ b/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg @@ -319,7 +319,7 @@ inherit .return_or_yield } "([^/]+)$" { attr (module_pop_end) symbol_definition = $1, source_node = @prog - attr (module_pop_end) empty_source_span + ;; attr (module_pop_end) empty_source_span attr (module_pop_end) definiens = @prog } } From 4a480e938f2457a789133b219b2c9722e3afce1b Mon Sep 17 00:00:00 2001 From: Rebecca Valentine Date: Fri, 3 Nov 2023 18:42:36 -0700 Subject: [PATCH 020/291] Revert "Removes empty source span for prog node" This reverts commit 67014222d1522e769508b6a3595f60ce895f9594. --- .../tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg b/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg index 895099776..bdc5d3f22 100644 --- a/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg +++ b/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg @@ -319,7 +319,7 @@ inherit .return_or_yield } "([^/]+)$" { attr (module_pop_end) symbol_definition = $1, source_node = @prog - ;; attr (module_pop_end) empty_source_span + attr (module_pop_end) empty_source_span attr (module_pop_end) definiens = @prog } } From d4fae16c72e9920bfffac1e65a85ec4653c86d29 Mon Sep 17 00:00:00 2001 From: Rebecca Valentine Date: Mon, 6 Nov 2023 12:24:31 -0800 Subject: [PATCH 021/291] Fixes definiens attr name Co-Authored-By: Rob Rix --- .../tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg b/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg index bdc5d3f22..ecdc9bf30 100644 --- a/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg +++ b/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg @@ -320,7 +320,7 @@ inherit .return_or_yield "([^/]+)$" { attr (module_pop_end) symbol_definition = $1, source_node = @prog attr (module_pop_end) empty_source_span - attr (module_pop_end) definiens = @prog + attr (module_pop_end) definiens_node = @prog } } From 40e1c3def1c965cf6bd29e9faafd67153cfc5ede Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Tue, 7 Nov 2023 14:01:16 +0100 Subject: [PATCH 022/291] Fix rustc warnings --- tree-sitter-stack-graphs/src/loader.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tree-sitter-stack-graphs/src/loader.rs b/tree-sitter-stack-graphs/src/loader.rs index 1620334ce..0feb6a28c 100644 --- a/tree-sitter-stack-graphs/src/loader.rs +++ b/tree-sitter-stack-graphs/src/loader.rs @@ -700,11 +700,11 @@ impl PathLoader { graph: &mut StackGraph, cancellation_flag: &dyn CancellationFlag, ) -> Result<(), LoadError<'static>> { - let source = std::fs::read_to_string(builtins_path.clone())?; + let source = std::fs::read_to_string(builtins_path)?; let mut config_path = builtins_path.to_path_buf(); config_path.set_extension("cfg"); let config = if config_path.exists() { - std::fs::read_to_string(builtins_path.clone())? + std::fs::read_to_string(builtins_path)? } else { "".into() }; From df0b8c088f94c6df4372af7d9387181678a9a7a6 Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Tue, 7 Nov 2023 18:47:09 +0100 Subject: [PATCH 023/291] Support main module configuration in `package.json` --- .../rust/npm_package.rs | 37 ++++++++++---- .../rust/tsconfig.rs | 12 ++--- .../can-access-package-non-main-explicilty.ts | 51 +++++++++++++++++++ .../package-does-not-export-non-main.ts | 44 ++++++++++++++++ .../packages/package-exports-explicit-main.ts | 51 +++++++++++++++++++ .../package-invisible-without-dependency.ts | 12 +++-- .../package-main-defaults-to-index.ts | 50 ++++++++++++++++++ 7 files changed, 237 insertions(+), 20 deletions(-) create mode 100644 languages/tree-sitter-stack-graphs-typescript/test/packages/can-access-package-non-main-explicilty.ts create mode 100644 languages/tree-sitter-stack-graphs-typescript/test/packages/package-does-not-export-non-main.ts create mode 100644 languages/tree-sitter-stack-graphs-typescript/test/packages/package-exports-explicit-main.ts rename languages/tree-sitter-stack-graphs-typescript/test/{projects => packages}/package-invisible-without-dependency.ts (98%) create mode 100644 languages/tree-sitter-stack-graphs-typescript/test/packages/package-main-defaults-to-index.ts diff --git a/languages/tree-sitter-stack-graphs-typescript/rust/npm_package.rs b/languages/tree-sitter-stack-graphs-typescript/rust/npm_package.rs index 9b67db598..b7351454a 100644 --- a/languages/tree-sitter-stack-graphs-typescript/rust/npm_package.rs +++ b/languages/tree-sitter-stack-graphs-typescript/rust/npm_package.rs @@ -8,6 +8,7 @@ use serde::Deserialize; use std::collections::HashMap; use std::path::Path; +use std::path::PathBuf; use stack_graphs::arena::Handle; use stack_graphs::graph::File; @@ -15,6 +16,7 @@ use stack_graphs::graph::StackGraph; use tree_sitter_stack_graphs::BuildError; use tree_sitter_stack_graphs::FileAnalyzer; +use crate::tsconfig::NormalizedRelativePath; use crate::util::*; pub struct NpmPackageAnalyzer {} @@ -74,16 +76,28 @@ impl FileAnalyzer for NpmPackageAnalyzer { }; // package definition - let pkg_def = add_module_pops( - graph, - file, - NON_REL_M_NS, - Path::new(&npm_pkg.name), - root, - "npm_package.pkg_def", - ); - let pkg_ref = add_push(graph, file, proj_scope, PKG_M_NS, "npm_package.pkg_ref"); - add_edge(graph, pkg_def, pkg_ref, 0); + if !npm_pkg.name.is_empty() { + let pkg_def = add_module_pops( + graph, + file, + NON_REL_M_NS, + Path::new(&npm_pkg.name), + root, + "npm_package.pkg_def", + ); + let pkg_ref = add_push(graph, file, proj_scope, PKG_M_NS, "npm_package.pkg_ref"); + add_edge(graph, pkg_def, pkg_ref, 0); + + let main = Some(npm_pkg.main) + .filter(|main| !main.is_empty()) + .and_then(|main| NormalizedRelativePath::from_str(&main)) + .map(|p| p.into_path_buf()) + .unwrap_or(PathBuf::from("index")) + .with_extension(""); + let main_ref = + add_module_pushes(graph, file, M_NS, &main, proj_scope, "npm_package.main_ref"); + add_edge(graph, pkg_def, main_ref, 0); + } // dependencies (package references) for (i, (pkg_name, _)) in npm_pkg.dependencies.iter().enumerate() { @@ -113,7 +127,10 @@ impl FileAnalyzer for NpmPackageAnalyzer { #[derive(Default, Debug, Clone, PartialEq, Deserialize)] #[serde(rename_all = "camelCase")] pub struct NpmPackage { + #[serde(default)] pub name: String, #[serde(default)] + pub main: String, + #[serde(default)] pub dependencies: HashMap, } diff --git a/languages/tree-sitter-stack-graphs-typescript/rust/tsconfig.rs b/languages/tree-sitter-stack-graphs-typescript/rust/tsconfig.rs index c885c298f..5c9ecae28 100644 --- a/languages/tree-sitter-stack-graphs-typescript/rust/tsconfig.rs +++ b/languages/tree-sitter-stack-graphs-typescript/rust/tsconfig.rs @@ -498,15 +498,15 @@ fn longest_common_prefix(left: &Path, right: &Path) -> Option { Some(prefix) } -struct NormalizedRelativePath(PathBuf); +pub(crate) struct NormalizedRelativePath(PathBuf); impl NormalizedRelativePath { - pub(self) fn from_str(path: &str) -> Option { + pub(crate) fn from_str(path: &str) -> Option { Self::from_path(Path::new(path)) } /// Creates a new normalized, relative path from a path. - pub(self) fn from_path(path: &Path) -> Option { + pub(crate) fn from_path(path: &Path) -> Option { let mut np = PathBuf::new(); let mut normal_components = 0usize; for c in path.components() { @@ -538,18 +538,18 @@ impl NormalizedRelativePath { } /// Returns if the relative path escapes to the parent. - pub(self) fn escapes(&self) -> bool { + pub(crate) fn escapes(&self) -> bool { self.0 .components() .next() .map_or(false, |c| c == Component::ParentDir) } - pub(self) fn as_path(&self) -> &Path { + pub(crate) fn as_path(&self) -> &Path { &self.0 } - pub(self) fn into_path_buf(self) -> PathBuf { + pub(crate) fn into_path_buf(self) -> PathBuf { self.0 } } diff --git a/languages/tree-sitter-stack-graphs-typescript/test/packages/can-access-package-non-main-explicilty.ts b/languages/tree-sitter-stack-graphs-typescript/test/packages/can-access-package-non-main-explicilty.ts new file mode 100644 index 000000000..dda91f57b --- /dev/null +++ b/languages/tree-sitter-stack-graphs-typescript/test/packages/can-access-package-non-main-explicilty.ts @@ -0,0 +1,51 @@ +/* --- path: acme_foo/tsconfig.json --- */ +/* --- global: FILE_PATH=tsconfig.json --- */ +/* --- global: PROJECT_NAME=acme_foo --- */ + +{} + +/* --- path: acme_foo/package.json --- */ +/* --- global: FILE_PATH=package.json --- */ +/* --- global: PROJECT_NAME=acme_foo --- */ + +{ + "name": "@acme/foo", + "version": "1.0", + "main": "./api" +} + +/* --- path: acme_foo/api.ts --- */ +/* --- global: FILE_PATH=api.ts --- */ +/* --- global: PROJECT_NAME=acme_foo --- */ + +export let x; + +/* --- path: acme_foo/core.ts --- */ +/* --- global: FILE_PATH=core.ts --- */ +/* --- global: PROJECT_NAME=acme_foo --- */ + +export let x; + +/* --- path: bar/tsconfig.json --- */ +/* --- global: FILE_PATH=tsconfig.json --- */ +/* --- global: PROJECT_NAME=bar --- */ + +{} + +/* --- path: bar/package.json --- */ +/* --- global: FILE_PATH=package.json --- */ +/* --- global: PROJECT_NAME=bar --- */ + +{ + "name": "bar", + "dependencies": { + "@acme/foo": "1" + } +} + +/* --- path: bar/app.ts --- */ +/* --- global: FILE_PATH=app.ts --- */ +/* --- global: PROJECT_NAME=bar --- */ + +import { x } from "@acme/foo/core" +// ^ defined: 27 diff --git a/languages/tree-sitter-stack-graphs-typescript/test/packages/package-does-not-export-non-main.ts b/languages/tree-sitter-stack-graphs-typescript/test/packages/package-does-not-export-non-main.ts new file mode 100644 index 000000000..d39b0d79f --- /dev/null +++ b/languages/tree-sitter-stack-graphs-typescript/test/packages/package-does-not-export-non-main.ts @@ -0,0 +1,44 @@ +/* --- path: foo/tsconfig.json --- */ +/* --- global: FILE_PATH=tsconfig.json --- */ +/* --- global: PROJECT_NAME=foo --- */ + +{} + +/* --- path: foo/package.json --- */ +/* --- global: FILE_PATH=package.json --- */ +/* --- global: PROJECT_NAME=foo --- */ + +{ + "name": "foo", + "version": "1.0" +} + +/* --- path: foo/impl.ts --- */ +/* --- global: FILE_PATH=impl.ts --- */ +/* --- global: PROJECT_NAME=foo --- */ + +export let x; + +/* --- path: bar/tsconfig.json --- */ +/* --- global: FILE_PATH=tsconfig.json --- */ +/* --- global: PROJECT_NAME=bar --- */ + +{} + +/* --- path: bar/package.json --- */ +/* --- global: FILE_PATH=package.json --- */ +/* --- global: PROJECT_NAME=bar --- */ + +{ + "name": "bar", + "dependencies": { + "foo": "1" + } +} + +/* --- path: bar/app.ts --- */ +/* --- global: FILE_PATH=app.ts --- */ +/* --- global: PROJECT_NAME=bar --- */ + +import { x } from "foo" +// ^ defined: diff --git a/languages/tree-sitter-stack-graphs-typescript/test/packages/package-exports-explicit-main.ts b/languages/tree-sitter-stack-graphs-typescript/test/packages/package-exports-explicit-main.ts new file mode 100644 index 000000000..f6a4f5e45 --- /dev/null +++ b/languages/tree-sitter-stack-graphs-typescript/test/packages/package-exports-explicit-main.ts @@ -0,0 +1,51 @@ +/* --- path: acme_foo/tsconfig.json --- */ +/* --- global: FILE_PATH=tsconfig.json --- */ +/* --- global: PROJECT_NAME=acme_foo --- */ + +{} + +/* --- path: acme_foo/package.json --- */ +/* --- global: FILE_PATH=package.json --- */ +/* --- global: PROJECT_NAME=acme_foo --- */ + +{ + "name": "@acme/foo", + "version": "1.0", + "main": "./api" +} + +/* --- path: acme_foo/api.ts --- */ +/* --- global: FILE_PATH=api.ts --- */ +/* --- global: PROJECT_NAME=acme_foo --- */ + +export let x; + +/* --- path: acme_foo/core.ts --- */ +/* --- global: FILE_PATH=core.ts --- */ +/* --- global: PROJECT_NAME=acme_foo --- */ + +export let x; + +/* --- path: bar/tsconfig.json --- */ +/* --- global: FILE_PATH=tsconfig.json --- */ +/* --- global: PROJECT_NAME=bar --- */ + +{} + +/* --- path: bar/package.json --- */ +/* --- global: FILE_PATH=package.json --- */ +/* --- global: PROJECT_NAME=bar --- */ + +{ + "name": "bar", + "dependencies": { + "@acme/foo": "1" + } +} + +/* --- path: bar/app.ts --- */ +/* --- global: FILE_PATH=app.ts --- */ +/* --- global: PROJECT_NAME=bar --- */ + +import { x } from "@acme/foo" +// ^ defined: 21 diff --git a/languages/tree-sitter-stack-graphs-typescript/test/projects/package-invisible-without-dependency.ts b/languages/tree-sitter-stack-graphs-typescript/test/packages/package-invisible-without-dependency.ts similarity index 98% rename from languages/tree-sitter-stack-graphs-typescript/test/projects/package-invisible-without-dependency.ts rename to languages/tree-sitter-stack-graphs-typescript/test/packages/package-invisible-without-dependency.ts index ca9852a68..c7c0458ff 100644 --- a/languages/tree-sitter-stack-graphs-typescript/test/projects/package-invisible-without-dependency.ts +++ b/languages/tree-sitter-stack-graphs-typescript/test/packages/package-invisible-without-dependency.ts @@ -1,6 +1,7 @@ /* --- path: ./my_lib/package.json --- */ /* --- global: FILE_PATH=package.json --- */ /* --- global: PROJECT_NAME=my_lib --- */ + { "name": "@my/lib" } @@ -8,17 +9,19 @@ /* --- path: ./my_lib/tsconfig.json --- */ /* --- global: FILE_PATH=tsconfig.json --- */ /* --- global: PROJECT_NAME=my_lib --- */ -{ -} + +{} /* --- path: ./my_lib/src/foo.ts --- */ /* --- global: FILE_PATH=src/foo.ts --- */ /* --- global: PROJECT_NAME=my_lib --- */ + export const bar = 42; /* --- path: ./my_app/package.json --- */ /* --- global: FILE_PATH=package.json --- */ /* --- global: PROJECT_NAME=my_app --- */ + { "name": "@my/app" } @@ -26,11 +29,12 @@ export const bar = 42; /* --- path: ./my_app/tsconfig.json --- */ /* --- global: FILE_PATH=tsconfig.json --- */ /* --- global: PROJECT_NAME=my_app --- */ -{ -} + +{} /* --- path: ./my_app/src/index.ts --- */ /* --- global: FILE_PATH=src/index.ts --- */ /* --- global: PROJECT_NAME=my_app --- */ + import { bar } from "@my/lib/foo"; // ^ defined: diff --git a/languages/tree-sitter-stack-graphs-typescript/test/packages/package-main-defaults-to-index.ts b/languages/tree-sitter-stack-graphs-typescript/test/packages/package-main-defaults-to-index.ts new file mode 100644 index 000000000..b7983551d --- /dev/null +++ b/languages/tree-sitter-stack-graphs-typescript/test/packages/package-main-defaults-to-index.ts @@ -0,0 +1,50 @@ +/* --- path: foo/tsconfig.json --- */ +/* --- global: FILE_PATH=tsconfig.json --- */ +/* --- global: PROJECT_NAME=foo --- */ + +{} + +/* --- path: foo/package.json --- */ +/* --- global: FILE_PATH=package.json --- */ +/* --- global: PROJECT_NAME=foo --- */ + +{ + "name": "foo", + "version": "1.0" +} + +/* --- path: foo/index.ts --- */ +/* --- global: FILE_PATH=index.ts --- */ +/* --- global: PROJECT_NAME=foo --- */ + +export let x; + +/* --- path: foo/impl.ts --- */ +/* --- global: FILE_PATH=impl.ts --- */ +/* --- global: PROJECT_NAME=foo --- */ + +export let x; + +/* --- path: bar/tsconfig.json --- */ +/* --- global: FILE_PATH=tsconfig.json --- */ +/* --- global: PROJECT_NAME=bar --- */ + +{} + +/* --- path: bar/package.json --- */ +/* --- global: FILE_PATH=package.json --- */ +/* --- global: PROJECT_NAME=bar --- */ + +{ + "name": "bar", + "dependencies": { + "foo": "1" + } +} + +/* --- path: bar/app.ts --- */ +/* --- global: FILE_PATH=app.ts --- */ +/* --- global: PROJECT_NAME=bar --- */ + +import { x } from "foo" +// ^ defined: 20 From 8ab41844775d8803c602cefc2d4d477cdb28a2fb Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Thu, 9 Nov 2023 12:43:24 +0100 Subject: [PATCH 024/291] Color nodes by file --- .../src/visualization/visualization.css | 233 ++++++++++++++---- .../src/visualization/visualization.js | 45 +++- 2 files changed, 221 insertions(+), 57 deletions(-) diff --git a/stack-graphs/src/visualization/visualization.css b/stack-graphs/src/visualization/visualization.css index 360594182..c5cc80151 100644 --- a/stack-graphs/src/visualization/visualization.css +++ b/stack-graphs/src/visualization/visualization.css @@ -9,15 +9,33 @@ /* Paul Tol's Colorblind Friendly Color Scheme (vibrant) * Source: https://personal.sron.nl/~pault/ * + * In default order: + * + * orange #ee7733 * blue #0077bb * cyan #33bbee - * teal #009988 - * orange #ee7733 - * red #cc3311 * magenta #ee3377 + * red #cc3311 + * teal #009988 * grey #bbbbbb */ +/* Paul Tol's Colorblind Friendly Color Scheme (light) + * Source: https://personal.sron.nl/~pault/ + * + * In default order: + * + * light blue #77aadd + * orange #ee8866 + * light yellow #eedd88 + * pink #ffaabb + * light cyan #99ddff + * mint #44bb99 + * pear #bbcc33 + * olive #aaaa00 + * pale grey #dddddd + */ + .sg { width: 100%; height: 100%; @@ -50,52 +68,25 @@ /* --- drop scopes --- */ .sg .node.drop_scopes .background { - fill: #cc3311; r: 6px; } /* --- jump to scope --- */ .sg .node.jump_to_scope .background { - fill: #ee7733; r: 6px; - stroke: black; } /* --- pop symbol --- */ -.sg .node.pop_symbol .background, -.sg .node.pop_scoped_symbol .background { - fill: #009988; -} - -.sg .node.pop_symbol .arrow, -.sg .node.pop_scoped_symbol .arrow { - fill: #005b51; -} - .sg .node.pop_scoped_symbol .pop_scope { fill: #ee7733; r: 6px; stroke: black; } -.sg .node.definition .background { - stroke: black; -} - /* --- push symbol --- */ -.sg .node.push_symbol .background, -.sg .node.push_scoped_symbol .background { - fill: #33bbee; -} - -.sg .node.push_symbol .arrow, -.sg .node.push_scoped_symbol .arrow { - fill: #006e96; -} - .sg .node.push_scoped_symbol .push_scope { fill: #bbbbbb; r: 6px; @@ -111,16 +102,10 @@ fill: black; } -.sg .node.reference .background { - stroke: black; -} - /* --- root --- */ .sg .node.root .background { - fill: #0077bb; r: 6px; - stroke: black; } /* --- scope --- */ @@ -131,14 +116,9 @@ } .sg .node.scope .background { - fill: #bbbbbb; r: 6px; } -.sg .node.scope.exported .background { - stroke: black; -} - .sg .node.scope .focus-point { r: 3px; fill: none; @@ -156,7 +136,6 @@ } .sg .node.scope.plain_labeled_node .background { - fill: #bbbbbb; rx: 6px; } @@ -167,7 +146,6 @@ /* --- path highlight --- */ .sg .node.path-node .border { - stroke: #ee3377; stroke-width: 4px; stroke-dasharray: 5, 5; } @@ -185,30 +163,21 @@ } .sg .edge path { - stroke: black; stroke-width: 1px; fill: none; } .sg .edge text { font-size: 11pt; - stroke: black; stroke-width: 1px; dominant-baseline: central; } .sg .edge.path-edge path { - stroke: #ee3377; stroke-width: 3px; } -.sg .edge.path-edge text -{ - stroke: #ee3377; - fill: #ee3377; -} - /* ------------------------------------------------------------------------------------------------ * Jumps */ @@ -442,3 +411,163 @@ margin: 0px 6px; cursor: pointer; } + +/* ------------------------------------------------------------------------------------------------ + * Colors + */ + +.sg .node.global .background { + fill: #0077bb; /* blue */ +} +.sg .edge.global path, +.sg .edge.global text { + stroke: #0077bb; /* blue */ +} + +.sg .node.file-0 .background { + fill: #77aadd; /* light blue */ +} + +.sg .node.file-0 .arrow, +.sg .edge.file-0 text +{ + fill: #0e2236; /* light blue (darkened) */ +} +.sg .node.file-0.reference .background, +.sg .node.file-0.definition .background, +.sg .node.file-0.scope.exported .background, +.sg .edge.file-0 path, +.sg .edge.file-0 text +{ + stroke: #0e2236; /* light blue (darkened) */ +} + +.sg .node.file-1 .background, +.sg .edge.file-1 text +{ + fill: #ee8866; /* orange */ +} +.sg .node.file-1 .arrow { + fill: #3d1407; /* orange (darkened) */ +} +.sg .node.file-1.reference .background, +.sg .node.file-1.definition .background, +.sg .node.file-1.scope.exported .background, +.sg .edge.file-1 path, +.sg .edge.file-1 text +{ + stroke: #3d1407; /* orange (darkened) */ +} + +.sg .node.file-2 .background, +.sg .edge.file-2 text +{ + fill: #eedd88; /* light yellow */ +} +.sg .node.file-2 .arrow { + fill: #413809; /* light yellow (darkened) */ +} +.sg .node.file-2.reference .background, +.sg .node.file-2.definition .background, +.sg .node.file-2.scope.exported .background, +.sg .edge.file-2 path, +.sg .edge.file-2 text +{ + stroke: #413809; /* light yellow (darkened) */ +} + +.sg .node.file-3 .background, +.sg .edge.file-3 text +{ + fill: #ffaabb; /* pink */ +} +.sg .node.file-3 .arrow { + fill: #550011; /* pink (darkened) */ +} +.sg .node.file-3.reference .background, +.sg .node.file-3.definition .background, +.sg .node.file-3.scope.exported .background, +.sg .edge.file-3 path, +.sg .edge.file-3 text +{ + stroke: #550011; /* pink (darkened) */ +} + +.sg .node.file-4 .background, +.sg .edge.file-4 text +{ + fill: #99ddff; /* light cyan */ +} +.sg .node.file-4 .arrow { + fill: #003652; /* light cyan (darkened) */ +} +.sg .node.file-4.reference .background, +.sg .node.file-4.definition .background, +.sg .node.file-4.scope.exported .background, +.sg .edge.file-4 path, +.sg .edge.file-4 text +{ + stroke: #003652; /* light cyan (darkened) */ +} + +.sg .node.file-5 .background, +.sg .edge.file-5 text +{ + fill: #44bb99; /* mint */ +} +.sg .node.file-5 .arrow { + fill: #0e251f; /* mint (darkened) */ +} +.sg .node.file-5.reference .background, +.sg .node.file-5.definition .background, +.sg .node.file-5.scope.exported .background, +.sg .edge.file-5 path, +.sg .edge.file-5 text +{ + stroke: #0e251f; /* mint (darkened) */ +} + +.sg .node.file-6 .background, +.sg .edge.file-6 text +{ + fill: #bbcc33; /* pear */ +} +.sg .node.file-6 .arrow { + fill: #25290a; /* pear (darkened) */ +} +.sg .node.file-6.reference .background, +.sg .node.file-6.definition .background, +.sg .node.file-6.scope.exported .background, +.sg .edge.file-6 path, +.sg .edge.file-6 text +{ + stroke: #25290a; /* pear (darkened) */ +} + +.sg .node.file-7 .background, +.sg .edge.file-7 text +{ + fill: #aaaa00; /* olive */ +} +.sg .node.file-7 .arrow { + fill: #222200; /* olive (darkened) */ +} +.sg .node.file-7.reference .background, +.sg .node.file-7.definition .background, +.sg .node.file-7.scope.exported .background, +.sg .edge.file-7 path, +.sg .edge.file-7 text +{ + stroke: #222200; /* olive (darkened) */ +} + +.sg .node.path-node .border, +.sg .edge.path-edge path, +.sg .edge.path-edge text +{ + stroke: #ee7733; +} + +.sg .edge.path-edge text { + fill: #ee7733; +} diff --git a/stack-graphs/src/visualization/visualization.js b/stack-graphs/src/visualization/visualization.js index a1d9bbaac..7e5183973 100644 --- a/stack-graphs/src/visualization/visualization.js +++ b/stack-graphs/src/visualization/visualization.js @@ -17,6 +17,8 @@ class StackGraph { static arrow_head_w = 16; static arrow_head_h = 8; + static number_of_file_colors = 8; + constructor(container, graph, paths, metadata) { this.metadata = metadata; @@ -32,12 +34,22 @@ class StackGraph { } compute_data() { + this.F = {}; this.ID = {}; this.N = []; + this.compute_file_data(); this.compute_node_data(); this.compute_path_data(); } + compute_file_data() { + for (let i in graph.files) { + const file = graph.files[i]; + this.F[file] = i; + } + console.log(this.F); + } + compute_node_data() { for (let i in graph.nodes) { const node = graph.nodes[i]; @@ -225,7 +237,7 @@ class StackGraph { .data(dag.links()) .enter() .append("g") - .attr("class", (d) => d.data.is_jump ? "jump" : "edge") + .attr("class", (d) => `${d.data.is_jump ? "jump" : "edge"} ${this.edge_to_file_class(d.data)}`) .attr("id", (d) => this.edge_to_id_str(d.data)); edges.append("path") .attr("id", (d) => this.edge_to_id_str(d.data) + ":path") @@ -286,14 +298,14 @@ class StackGraph { render_node(node, g) { g.attr('id', this.node_to_id_str(node)); - g.attr('class', `node ${node.type}`); + g.attr('class', `node ${node.type} ${this.node_to_file_class(node)}`); switch (node.type) { case "drop_scopes": - this.render_scope(g); + this.render_symbol_node(g, "[drop]", null, ""); break; case "jump_to_scope": - this.render_scope(g); + this.render_symbol_node(g, "[jump]", null, ""); break; case "pop_symbol": this.render_symbol_node(g, node.symbol, null, "pop"); @@ -322,7 +334,7 @@ class StackGraph { } break; case "root": - this.render_scope(g); + this.render_symbol_node(g, "[root]", null, ""); break; case "scope": if (this.show_all_node_labels()) { @@ -347,6 +359,7 @@ class StackGraph { break; } } + render_symbol_node(g, text, scope, shape) { let content = g.append("g"); content.append('text').text(text); @@ -869,10 +882,24 @@ class StackGraph { return this.node_id_to_str(node.id); } + node_to_file_class(node) { + return this.node_id_to_file_class(node.id); + } + edge_to_id_str(edge) { return this.node_id_to_str(edge.source) + "->" + this.node_id_to_str(edge.sink); } + edge_to_file_class(edge) { + if (edge.source.hasOwnProperty('file')) { + return "file-" + (this.F[edge.source.file] % StackGraph.number_of_file_colors); + } else if (edge.sink.hasOwnProperty('file')) { + return "file-" + (this.F[edge.sink.file] % StackGraph.number_of_file_colors); + } else { + return "global"; + } + } + node_id_to_str(id) { if (id.hasOwnProperty('file')) { return id.file + "#" + id.local_id; @@ -881,6 +908,14 @@ class StackGraph { } } + node_id_to_file_class(id) { + if (id.hasOwnProperty('file')) { + return "file-" + (this.F[id.file] % StackGraph.number_of_file_colors); + } else { + return "global"; + } + } + id_selector(id) { const sel = "#" + id.replaceAll(/[^a-zA-Z0-9]/g, '\\$&'); return sel; From 13dce02b433660f94b892df0dbf805c290855335 Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Thu, 9 Nov 2023 18:17:17 +0100 Subject: [PATCH 025/291] Add file legend --- .../src/visualization/visualization.css | 62 ++++++++++++++++++- .../src/visualization/visualization.js | 45 ++++++++++++-- 2 files changed, 102 insertions(+), 5 deletions(-) diff --git a/stack-graphs/src/visualization/visualization.css b/stack-graphs/src/visualization/visualization.css index c5cc80151..177b56cef 100644 --- a/stack-graphs/src/visualization/visualization.css +++ b/stack-graphs/src/visualization/visualization.css @@ -314,6 +314,40 @@ margin: 0px 1px; } +/* ------------------------------------------------------------------------------------------------ + * Legend + */ + +#sg-legend { + position: absolute; + left: 10px; + top: 10px; + background-color: #bbbbbb; + padding: 6px; + border-radius: 6px; + z-index: 1; +} + +#sg-legend h1 { + font-variant: small-caps; + font-weight: bold; + font-size: inherit; + border-bottom: solid 1px #777777; + margin: 0px; +} + +#sg-legend ul { + list-style: none; + padding: 0px; + margin: 0px; +} + +#sg-legend li { + padding: 3px 6px; + margin: 3px 0px; + border: 1px solid white; +} + /* ------------------------------------------------------------------------------------------------ * Help */ @@ -419,6 +453,9 @@ .sg .node.global .background { fill: #0077bb; /* blue */ } +#sg-legend .global { + background-color: #0077bb; /* blue */ +} .sg .edge.global path, .sg .edge.global text { stroke: #0077bb; /* blue */ @@ -427,7 +464,9 @@ .sg .node.file-0 .background { fill: #77aadd; /* light blue */ } - +#sg-legend .file-0 { + background-color: #77aadd; /* light blue */ +} .sg .node.file-0 .arrow, .sg .edge.file-0 text { @@ -447,6 +486,9 @@ { fill: #ee8866; /* orange */ } +#sg-legend .file-1 { + background-color: #ee8866; /* orange */ +} .sg .node.file-1 .arrow { fill: #3d1407; /* orange (darkened) */ } @@ -464,6 +506,9 @@ { fill: #eedd88; /* light yellow */ } +#sg-legend .file-2 { + background-color: #eedd88; /* light yellow */ +} .sg .node.file-2 .arrow { fill: #413809; /* light yellow (darkened) */ } @@ -481,6 +526,9 @@ { fill: #ffaabb; /* pink */ } +#sg-legend .file-3 { + background-color: #ffaabb; /* pink */ +} .sg .node.file-3 .arrow { fill: #550011; /* pink (darkened) */ } @@ -498,6 +546,9 @@ { fill: #99ddff; /* light cyan */ } +#sg-legend .file-4 { + background-color: #99ddff; /* light cyan */ +} .sg .node.file-4 .arrow { fill: #003652; /* light cyan (darkened) */ } @@ -515,6 +566,9 @@ { fill: #44bb99; /* mint */ } +#sg-legend .file-5 { + background-color: #44bb99; /* mint */ +} .sg .node.file-5 .arrow { fill: #0e251f; /* mint (darkened) */ } @@ -532,6 +586,9 @@ { fill: #bbcc33; /* pear */ } +#sg-legend .file-6 { + background-color: #bbcc33; /* pear */ +} .sg .node.file-6 .arrow { fill: #25290a; /* pear (darkened) */ } @@ -549,6 +606,9 @@ { fill: #aaaa00; /* olive */ } +#sg-legend .file-7 { + background-color: #aaaa00; /* olive */ +} .sg .node.file-7 .arrow { fill: #222200; /* olive (darkened) */ } diff --git a/stack-graphs/src/visualization/visualization.js b/stack-graphs/src/visualization/visualization.js index 7e5183973..116850791 100644 --- a/stack-graphs/src/visualization/visualization.js +++ b/stack-graphs/src/visualization/visualization.js @@ -163,7 +163,8 @@ class StackGraph { // render UI this.render_help(); this.render_tooltip(); - this.render_graph() + this.render_legend(); + this.render_graph(); // pan & zoom let zoom = d3.zoom() @@ -471,9 +472,9 @@ class StackGraph { } } - /* ------------------------------------------------------------------------------------------------ - * Path Highlighting - */ + // ------------------------------------------------------------------------------------------------ + // Path Highlighting + // paths_mouseover(e, node) { if (this.paths_lock !== null) { @@ -781,6 +782,30 @@ class StackGraph { || (this.current_edge !== null && path.derived.edges.hasOwnProperty(this.edge_to_id_str(this.current_edge))); } + // ------------------------------------------------------------------------------------------------ + // Legend + // + + render_legend() { + const legend = d3.select('body').append('div') + .attr('id', 'sg-legend') + legend.append("h1").text("Files"); + const items = legend.append("ul"); + items.append("li") + .classed("global", true) + .text("[global]"); + for (const file in this.F) { + items.append("li") + .classed('file-' + this.F[file], true) + .text(file); + } + } + + legend_update() { + const legend = d3.select('#sg-legend'); + legend.style('visibility', this.show_file_legend() ? null : 'hidden'); + } + // ------------------------------------------------------------------------------------------------ // Help // @@ -802,6 +827,10 @@ class StackGraph { Pan by dragging the background with the mouse. Zoom using the scroll wheel. `); + this.show_files_legend_toggle = this.new_setting(help_content, "sg-files-legend", "Show files legend (f)", true); + this.show_files_legend_toggle.on("change", (e => { + this.legend_update(); + })); this.show_all_node_labels_toggle = this.new_setting(help_content, "sg-scope-labels", "Show all node labels (l)", false); this.show_all_node_labels_toggle.on("change", (e => { this.render_graph(); @@ -838,6 +867,10 @@ class StackGraph { help_keypress(e) { switch (e.keyCode) { + case 70: // f + this.show_files_legend_toggle.property("checked", !this.show_files_legend_toggle.property("checked")); + this.legend_update(); + break; case 72: // h this.help_toggle.property("checked", !this.help_toggle.property("checked")); break; @@ -860,6 +893,10 @@ class StackGraph { return this.show_all_node_labels_toggle.property("checked"); } + show_file_legend() { + return this.show_files_legend_toggle.property("checked"); + } + new_setting(element, id, html, initial) { const toggle = element.append("div"); const toggle_input = toggle.append('input') From 1acebc7a89c312e8119e3f8e6bd15e7c622ed6ab Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Thu, 9 Nov 2023 18:36:59 +0100 Subject: [PATCH 026/291] Make border of clickable nodes heavier --- stack-graphs/src/visualization/visualization.css | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/stack-graphs/src/visualization/visualization.css b/stack-graphs/src/visualization/visualization.css index 177b56cef..c3dfc091b 100644 --- a/stack-graphs/src/visualization/visualization.css +++ b/stack-graphs/src/visualization/visualization.css @@ -85,6 +85,10 @@ stroke: black; } +.sg .node.definition .background { + stroke-width: 2px; +} + /* --- push symbol --- */ .sg .node.push_scoped_symbol .push_scope { @@ -102,6 +106,10 @@ fill: black; } +.sg .node.reference .background { + stroke-width: 2px; +} + /* --- root --- */ .sg .node.root .background { @@ -128,6 +136,10 @@ fill: black; } +.sg .node.scope.exported .background { + stroke-width: 2px; +} + /* --- plain labeled node --- */ .sg .node.scope.plain_labeled_node .border { @@ -139,10 +151,6 @@ rx: 6px; } -.sg .node.scope.plain_labeled_node.exported .background { - stroke: black; -} - /* --- path highlight --- */ .sg .node.path-node .border { From 11c42030b7396644c878a71fb1e449b43c672137 Mon Sep 17 00:00:00 2001 From: Rebecca Valentine Date: Thu, 9 Nov 2023 10:21:53 -0800 Subject: [PATCH 027/291] Adds closure points and tests --- .../src/stack-graphs.tsg | 26 ++++++++++--- .../function_closure_mutation.js | 38 +++++++++++++++++++ 2 files changed, 58 insertions(+), 6 deletions(-) create mode 100644 languages/tree-sitter-stack-graphs-javascript/test/computation_flow/function_closure_mutation.js diff --git a/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg b/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg index b8a220f41..253ea216c 100644 --- a/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg +++ b/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg @@ -252,6 +252,7 @@ inherit .constructor inherit .export_statement inherit .exports inherit .hoist_point +inherit .closure_point inherit .import_statement inherit .pkg_pop inherit .pkg_push @@ -280,6 +281,7 @@ inherit .return_or_yield (program)@prog { node @prog.after_scope node @prog.before_scope + let @prog.closure_point = @prog.after_scope } ;; ### Program Queries @@ -1267,6 +1269,7 @@ inherit .return_or_yield node fun_decl_value_return node fun_decl_value_this node fun_decl_value_this_guard + let @body.closure_point = @body.after_scope attr (@name.pop) syntax_type = "function" @@ -1281,7 +1284,7 @@ inherit .return_or_yield ; function values have drop nodes that handle closures, that points to the ; before scope for the function attr (fun_decl_value_drop) type = "drop_scopes" - edge fun_decl_value_drop -> @fun_decl.before_scope + edge fun_decl_value_drop -> @fun_decl.closure_point ; the call sig's before scope comes from the drop node then flows into the body edge @call_sig.before_scope -> fun_decl_value_drop @@ -1351,6 +1354,7 @@ inherit .return_or_yield node fun_decl_value_return node fun_decl_value_this node fun_decl_value_this_guard + let @body.closure_point = @body.after_scope attr (@name.pop) syntax_type = "function" @@ -1365,7 +1369,7 @@ inherit .return_or_yield ; function values have drop nodes that handle closures, that points to the ; before scope of the declaration attr (fun_decl_value_drop) type = "drop_scopes" - edge fun_decl_value_drop -> @fun_decl.before_scope + edge fun_decl_value_drop -> @fun_decl.closure_point ; the call sig's before scope comes from the drop node then flows into the body @@ -1549,7 +1553,7 @@ inherit .return_or_yield ; method values have drop nodes that handle closures, that points to the ; before scope from method def attr (method_def_method_value_drop) type = "drop_scopes" - edge method_def_method_value_drop -> @method_def.before_scope + edge method_def_method_value_drop -> @method_def.closure_point ; the call sig's before scope comes from the drop node then flows into the body edge @call_sig.before_scope -> method_def_method_value_drop @@ -2246,6 +2250,16 @@ inherit .return_or_yield } +[ + (function body:(_)@body) + (arrow_function body:(_)@body) + (generator_function body:(_)@body) +] { + + let @body.closure_point = @body.after_scope + +} + ;; ### Expression Queries @@ -2713,7 +2727,7 @@ inherit .return_or_yield ; function values have drop nodes that handle closures, that points to the ; before scope from the function attr (fun_value_drop) type = "drop_scopes" - edge fun_value_drop -> @fun.before_scope + edge fun_value_drop -> @fun.closure_point ; the call sig's before scope comes from the drop node, ; then flows into the body, and includes a variable binding for "this" @@ -2791,7 +2805,7 @@ inherit .return_or_yield ; function values have drop nodes that handle closures, that points to the ; before scope from the function attr (fun_value_drop) type = "drop_scopes" - edge fun_value_drop -> @fun.before_scope + edge fun_value_drop -> @fun.closure_point ; the call sig's before scope comes from the drop node then flows into the body edge @call_sig.before_scope -> fun_value_drop @@ -2900,7 +2914,7 @@ inherit .return_or_yield ; function values have drop nodes that handle closures, that points to the ; before scope from the function attr (fun_value_drop) type = "drop_scopes" - edge fun_value_drop -> @fun.before_scope + edge fun_value_drop -> @fun.closure_point ; the call sig's before scope comes from the drop node, ; then flows into the body, and includes a variable binding for "this" diff --git a/languages/tree-sitter-stack-graphs-javascript/test/computation_flow/function_closure_mutation.js b/languages/tree-sitter-stack-graphs-javascript/test/computation_flow/function_closure_mutation.js new file mode 100644 index 000000000..68cd9117b --- /dev/null +++ b/languages/tree-sitter-stack-graphs-javascript/test/computation_flow/function_closure_mutation.js @@ -0,0 +1,38 @@ +let x = 1; + +function foo() { + /**/ x; + // ^ defined: 1, 38 +} + +function* foo() { + /**/ x; + // ^ defined: 1, 38 +} + +function () { + /**/ x; + // ^ defined: 1, 38 +}; + +function* () { + /**/ x; + // ^ defined: 1, 38 +}; + +() => x; +// ^ defined: 1, 38 + +() => { + /**/ x; + // ^ defined: 1, 38 +}; + +class C { + foo() { + /**/ x; + // ^ defined: 1, 38 + } +} + +x = 2; \ No newline at end of file From 5ac451d5d101766c9dfe246fea98114e1412bc5b Mon Sep 17 00:00:00 2001 From: Rebecca Valentine Date: Thu, 9 Nov 2023 11:52:40 -0800 Subject: [PATCH 028/291] Fixes minore bugs around method closure scopes --- .../src/stack-graphs.tsg | 6 +++--- .../test/computation_flow/function_closure_mutation.js | 7 ++++++- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg b/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg index 253ea216c..b864404a9 100644 --- a/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg +++ b/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg @@ -1441,7 +1441,7 @@ inherit .return_or_yield edge @class_decl.class_value -> guard_prototype edge guard_prototype -> @class_decl.prototype - edge @body.before_scope -> @class_decl.before_scope + edge @body.before_scope -> @class_decl.closure_point edge @class_decl.prototype -> @body.after_scope edge @class_decl.after_scope -> @class_decl.before_scope } @@ -1553,7 +1553,7 @@ inherit .return_or_yield ; method values have drop nodes that handle closures, that points to the ; before scope from method def attr (method_def_method_value_drop) type = "drop_scopes" - edge method_def_method_value_drop -> @method_def.closure_point + edge method_def_method_value_drop -> @method_def.before_scope ; the call sig's before scope comes from the drop node then flows into the body edge @call_sig.before_scope -> method_def_method_value_drop @@ -3454,7 +3454,7 @@ inherit .return_or_yield node @class.constructor attr (guard_prototype) pop_symbol = "GUARD:PROTOTYPE" - edge @body.before_scope -> @class.before_scope + edge @body.before_scope -> @class.closure_point edge @class.value -> @class.class_value edge @class.class_value -> guard_prototype edge guard_prototype -> @class.prototype diff --git a/languages/tree-sitter-stack-graphs-javascript/test/computation_flow/function_closure_mutation.js b/languages/tree-sitter-stack-graphs-javascript/test/computation_flow/function_closure_mutation.js index 68cd9117b..4b8c1a200 100644 --- a/languages/tree-sitter-stack-graphs-javascript/test/computation_flow/function_closure_mutation.js +++ b/languages/tree-sitter-stack-graphs-javascript/test/computation_flow/function_closure_mutation.js @@ -35,4 +35,9 @@ class C { } } -x = 2; \ No newline at end of file +x = 2; + +(function bar() { + /**/ bar; + // ^ defined: 40 +}); \ No newline at end of file From 81c9cf8d4cef4b1f585f09f0e837819db816827f Mon Sep 17 00:00:00 2001 From: Rebecca Valentine Date: Thu, 9 Nov 2023 12:50:31 -0800 Subject: [PATCH 029/291] Improves definiens --- .../src/stack-graphs.tsg | 22 +++++-------------- 1 file changed, 6 insertions(+), 16 deletions(-) diff --git a/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg b/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg index b864404a9..3aa12eabd 100644 --- a/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg +++ b/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg @@ -4699,12 +4699,7 @@ inherit .return_or_yield (assignment_expression left: (identifier)@left - right: [ - (function) - (generator_function) - (arrow_function) - (class) - ])@assignment_expr { + right: (_))@assignment_expr { attr (@left.pop) definiens_node = @assignment_expr @@ -4716,7 +4711,7 @@ inherit .return_or_yield object:(identifier)@_object property:(_)@left ) - right: (_)@right)@assignment_expr + right: (_))@assignment_expr (#not-eq @_object "module") (#not-eq @left "exports") ) { @@ -4726,7 +4721,7 @@ inherit .return_or_yield attr (left_ignore_guard) pop_symbol = "GUARD:GANDALF" attr (left_definiens_hook) node_definition = @left - attr (left_definiens_hook) definiens_node = @right + attr (left_definiens_hook) definiens_node = @assignment_expr edge @assignment_expr.pkg_pop -> left_ignore_guard edge left_ignore_guard -> left_definiens_hook } @@ -4737,7 +4732,7 @@ inherit .return_or_yield object:(identifier)@_object property:(_)@left ) - right: (_)@right)@assignment_expr + right: (_))@assignment_expr (#eq @_object "module") (#eq @left "exports") ) { @@ -4747,7 +4742,7 @@ inherit .return_or_yield attr (left_ignore_guard) pop_symbol = "GUARD:GANDALF" attr (left_definiens_hook) node_definition = @left - attr (left_definiens_hook) definiens_node = @right + attr (left_definiens_hook) definiens_node = @assignment_expr edge @assignment_expr.pkg_pop -> left_ignore_guard edge left_ignore_guard -> left_definiens_hook @@ -4846,12 +4841,7 @@ inherit .return_or_yield (pair key: (_)@name - value: [ - (function) - (generator_function) - (arrow_function) - (class) - ])@pair_expr { + value: (_))@pair_expr { node @name.definiens_hook node name_ignore_guard From c41dc0e657231df026c79724a135576a43ebb0ed Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Thu, 9 Nov 2023 20:52:28 +0100 Subject: [PATCH 030/291] Only keep the best path in similar path detection --- stack-graphs/src/cycles.rs | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/stack-graphs/src/cycles.rs b/stack-graphs/src/cycles.rs index eda31e171..4ffe3148b 100644 --- a/stack-graphs/src/cycles.rs +++ b/stack-graphs/src/cycles.rs @@ -111,10 +111,22 @@ where let key = path.key(); let possibly_similar_paths = self.paths.entry(key).or_default(); - for other_path in possibly_similar_paths.iter() { - match cmp(arena, path, other_path) { - Some(ord) if ord != Ordering::Less => return true, - _ => continue, + let mut idx = 0; + while idx < possibly_similar_paths.len() { + match cmp(arena, path, &possibly_similar_paths[idx]) { + Some(Ordering::Less) => { + // the new path is betetr, remove the old one + possibly_similar_paths.remove(idx); + // keep `idx` which now points to the next element + continue; + } + Some(_) => { + // the new path is equal or worse, and ignored + return true; + } + None => { + idx += 1; + } } } From 397942bdc42cbadef99f4ae6622d716c0c86435a Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Fri, 10 Nov 2023 11:09:59 +0100 Subject: [PATCH 031/291] Use much smaller buckets --- stack-graphs/src/cycles.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stack-graphs/src/cycles.rs b/stack-graphs/src/cycles.rs index 4ffe3148b..2fc125b6c 100644 --- a/stack-graphs/src/cycles.rs +++ b/stack-graphs/src/cycles.rs @@ -49,7 +49,7 @@ use crate::stitching::ToAppendable; /// Helps detect similar paths in the path-finding algorithm. pub struct SimilarPathDetector

{ - paths: HashMap>, + paths: HashMap>, } #[doc(hidden)] From 84694154d7d099c6113a401db67e1f1e0bc2e24b Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Sat, 11 Nov 2023 18:17:45 +0100 Subject: [PATCH 032/291] Restrict similar path detection to split paths --- stack-graphs/src/stitching.rs | 79 +++++++++++++++++++++++------------ 1 file changed, 53 insertions(+), 26 deletions(-) diff --git a/stack-graphs/src/stitching.rs b/stack-graphs/src/stitching.rs index 4872afe91..5bea52134 100644 --- a/stack-graphs/src/stitching.rs +++ b/stack-graphs/src/stitching.rs @@ -41,6 +41,9 @@ use std::collections::VecDeque; #[cfg(feature = "copious-debugging")] use std::fmt::Display; +use itertools::izip; +use itertools::Itertools; + use crate::arena::Arena; use crate::arena::Handle; use crate::arena::HandleSet; @@ -60,6 +63,7 @@ use crate::partial::Cyclicity; use crate::partial::PartialPath; use crate::partial::PartialPaths; use crate::partial::PartialSymbolStack; +use crate::paths::Extend; use crate::paths::PathResolutionError; use crate::CancellationError; use crate::CancellationFlag; @@ -736,13 +740,18 @@ impl<'a> Display for DisplaySymbolStackKey<'a> { /// [`find_all_complete_partial_paths`]: #method.find_all_complete_partial_paths pub struct ForwardPartialPathStitcher { candidates: Vec, - queue: VecDeque<(PartialPath, AppendingCycleDetector)>, + extensions: Vec<(PartialPath, AppendingCycleDetector)>, + queue: VecDeque<(PartialPath, AppendingCycleDetector, bool)>, // tracks the number of initial paths in the queue because we do not want call // extend_until on those initial_paths: usize, // next_iteration is a tuple of queues instead of an queue of tuples so that the path queue // can be cheaply exposed through the C API as a continuous memory block - next_iteration: (VecDeque, VecDeque>), + next_iteration: ( + VecDeque, + VecDeque>, + VecDeque, + ), appended_paths: Appendables, similar_path_detector: Option>, max_work_per_phase: usize, @@ -763,15 +772,16 @@ impl ForwardPartialPathStitcher { I: IntoIterator, { let mut appended_paths = Appendables::new(); - let next_iteration: (VecDeque<_>, VecDeque<_>) = initial_partial_paths + let next_iteration: (VecDeque<_>, VecDeque<_>, VecDeque<_>) = initial_partial_paths .into_iter() .map(|p| { let c = AppendingCycleDetector::from(&mut appended_paths, p.clone().into()); - (p, c) + (p, c, false) }) - .unzip(); + .multiunzip(); Self { candidates: Vec::new(), + extensions: Vec::new(), queue: VecDeque::new(), initial_paths: next_iteration.0.len(), next_iteration, @@ -835,6 +845,7 @@ impl ForwardPartialPathStitcher { candidates: &mut C, partial_path: &PartialPath, cycle_detector: AppendingCycleDetector, + has_split: bool, ) -> usize where A: Appendable, @@ -871,15 +882,15 @@ impl ForwardPartialPathStitcher { // find candidates to append self.candidates.clear(); candidates.get_forward_candidates(partial_path, &mut self.candidates); + let (graph, partials, db) = candidates.get_graph_partials_and_db(); // try to extend path with candidates - let extension_count = self.candidates.len(); - self.next_iteration.0.reserve(extension_count); - self.next_iteration.1.reserve(extension_count); - for extension in &self.candidates { - let (graph, partials, db) = candidates.get_graph_partials_and_db(); - let extension_path = db.get_appendable(extension); - copious_debugging!(" with {}", extension_path.display(graph, partials)); + let candidate_count = self.candidates.len(); + self.extensions.clear(); + self.extensions.reserve(candidate_count); + for candidate in &self.candidates { + let appendable = db.get_appendable(candidate); + copious_debugging!(" with {}", appendable.display(graph, partials)); let mut new_partial_path = partial_path.clone(); let mut new_cycle_detector = cycle_detector.clone(); @@ -887,12 +898,23 @@ impl ForwardPartialPathStitcher { // partial path, just skip the extension — it's not a fatal error. #[cfg_attr(not(feature = "copious-debugging"), allow(unused_variables))] { - if let Err(err) = extension_path.append_to(graph, partials, &mut new_partial_path) { + if let Err(err) = appendable.append_to(graph, partials, &mut new_partial_path) { copious_debugging!(" is invalid: {:?}", err); continue; } - copious_debugging!(" is {}", new_partial_path.display(graph, partials)); - new_cycle_detector.append(&mut self.appended_paths, extension.clone()); + } + new_cycle_detector.append(&mut self.appended_paths, candidate.clone()); + copious_debugging!(" is {}", new_partial_path.display(graph, partials)); + self.extensions.push((new_partial_path, new_cycle_detector)); + } + + let extension_count = self.extensions.len(); + let new_has_split = has_split || self.extensions.len() > 1; + self.next_iteration.0.reserve(extension_count); + self.next_iteration.1.reserve(extension_count); + self.next_iteration.2.reserve(extension_count); + for (new_partial_path, new_cycle_detector) in self.extensions.drain(..) { + if new_has_split { if let Some(similar_path_detector) = &mut self.similar_path_detector { if similar_path_detector.has_similar_path( graph, @@ -912,16 +934,22 @@ impl ForwardPartialPathStitcher { } }, ) { + copious_debugging!( + " extension {}", + new_partial_path.display(graph, partials) + ); copious_debugging!(" is rejected: too many similar"); continue; } } } - self.next_iteration.0.push_back(new_partial_path); - self.next_iteration.1.push_back(new_cycle_detector); + + self.next_iteration.0.push(new_partial_path); + self.next_iteration.1.push(new_cycle_detector); + self.next_iteration.2.push(new_has_split); } - extension_count + candidate_count } /// Returns whether the algorithm has completed. @@ -949,14 +977,13 @@ impl ForwardPartialPathStitcher { E: Fn(&StackGraph, &mut PartialPaths, &PartialPath) -> bool, { copious_debugging!("==> Start phase {}", self.phase_number); - self.queue.extend( - self.next_iteration - .0 - .drain(..) - .zip(self.next_iteration.1.drain(..)), - ); + self.queue.extend(izip!( + self.next_iteration.0.drain(..), + self.next_iteration.1.drain(..), + self.next_iteration.2.drain(..), + )); let mut work_performed = 0; - while let Some((partial_path, cycle_detector)) = self.queue.pop_front() { + while let Some((partial_path, cycle_detector, has_split)) = self.queue.pop_front() { let (graph, partials, _) = candidates.get_graph_partials_and_db(); copious_debugging!( "--> Candidate partial path {}", @@ -971,7 +998,7 @@ impl ForwardPartialPathStitcher { ); continue; } - work_performed += self.extend(candidates, &partial_path, cycle_detector); + work_performed += self.extend(candidates, &partial_path, cycle_detector, has_split); if work_performed >= self.max_work_per_phase { break; } From 52d7bd55b38302ee2f5bb9bffea687d2678b71d5 Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Sat, 11 Nov 2023 18:23:25 +0100 Subject: [PATCH 033/291] Restrict similar path detection to paths that can converge --- stack-graphs/src/graph.rs | 9 +++++++++ stack-graphs/src/stitching.rs | 3 ++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/stack-graphs/src/graph.rs b/stack-graphs/src/graph.rs index ae2b9aa62..8c0de1f74 100644 --- a/stack-graphs/src/graph.rs +++ b/stack-graphs/src/graph.rs @@ -1303,6 +1303,7 @@ impl StackGraph { let edges = &mut self.outgoing_edges[source]; if let Err(index) = edges.binary_search_by_key(&sink, |o| o.sink) { edges.insert(index, OutgoingEdge { sink, precedence }); + self.incoming_edges[sink] += 1; } } @@ -1311,6 +1312,7 @@ impl StackGraph { let edges = &mut self.outgoing_edges[source]; if let Ok(index) = edges.binary_search_by_key(&sink, |o| o.sink) { edges.remove(index); + self.incoming_edges[sink] -= 1; } } @@ -1325,6 +1327,11 @@ impl StackGraph { None => Either::Left(std::iter::empty()), } } + + /// Returns the number of edges that end at a particular sink node. + pub fn incoming_edge_count(&self, sink: Handle) -> u32 { + self.incoming_edges[sink] + } } //------------------------------------------------------------------------------------------------- @@ -1443,6 +1450,7 @@ pub struct StackGraph { pub(crate) source_info: SupplementalArena, node_id_handles: NodeIDHandles, outgoing_edges: SupplementalArena>, + incoming_edges: SupplementalArena, pub(crate) node_debug_info: SupplementalArena, pub(crate) edge_debug_info: SupplementalArena, DebugInfo); 8]>>, } @@ -1625,6 +1633,7 @@ impl Default for StackGraph { source_info: SupplementalArena::new(), node_id_handles: NodeIDHandles::new(), outgoing_edges: SupplementalArena::new(), + incoming_edges: SupplementalArena::new(), node_debug_info: SupplementalArena::new(), edge_debug_info: SupplementalArena::new(), } diff --git a/stack-graphs/src/stitching.rs b/stack-graphs/src/stitching.rs index 5bea52134..1c0a29f27 100644 --- a/stack-graphs/src/stitching.rs +++ b/stack-graphs/src/stitching.rs @@ -914,7 +914,8 @@ impl ForwardPartialPathStitcher { self.next_iteration.1.reserve(extension_count); self.next_iteration.2.reserve(extension_count); for (new_partial_path, new_cycle_detector) in self.extensions.drain(..) { - if new_has_split { + let can_converge = graph.incoming_edge_count(new_partial_path.end_node) > 1; + if new_has_split && can_converge { if let Some(similar_path_detector) = &mut self.similar_path_detector { if similar_path_detector.has_similar_path( graph, From 2596ad29c4dd87bca73d132d8701c17f078594fa Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Sun, 12 Nov 2023 21:41:50 +0100 Subject: [PATCH 034/291] Only check for cycles if loops can actually occur --- stack-graphs/src/stitching.rs | 66 +++++++++++++++++++++++------------ stack-graphs/src/storage.rs | 4 +++ 2 files changed, 48 insertions(+), 22 deletions(-) diff --git a/stack-graphs/src/stitching.rs b/stack-graphs/src/stitching.rs index 1c0a29f27..d348dac9b 100644 --- a/stack-graphs/src/stitching.rs +++ b/stack-graphs/src/stitching.rs @@ -202,6 +202,9 @@ where where R: std::iter::Extend; + /// Get the number of available candidates that share the given path's end node. + fn get_joining_candidate_count(&self, path: &PartialPath) -> u32; + /// Get the graph, partial path arena, and database backing this candidates instance. fn get_graph_partials_and_db(&mut self) -> (&StackGraph, &mut PartialPaths, &Db); } @@ -243,6 +246,10 @@ impl ForwardCandidates for GraphEdgeC })); } + fn get_joining_candidate_count(&self, path: &PartialPath) -> u32 { + self.graph.incoming_edge_count(path.end_node) + } + fn get_graph_partials_and_db(&mut self) -> (&StackGraph, &mut PartialPaths, &GraphEdges) { (self.graph, self.partials, &self.edges) } @@ -277,6 +284,7 @@ pub struct Database { symbol_stack_key_cache: HashMap, paths_by_start_node: SupplementalArena>>, root_paths_by_precondition: SupplementalArena>>, + incoming_paths: SupplementalArena, } impl Database { @@ -289,6 +297,7 @@ impl Database { symbol_stack_key_cache: HashMap::new(), paths_by_start_node: SupplementalArena::new(), root_paths_by_precondition: SupplementalArena::new(), + incoming_paths: SupplementalArena::new(), } } @@ -302,6 +311,7 @@ impl Database { self.symbol_stack_key_cache.clear(); self.paths_by_start_node.clear(); self.root_paths_by_precondition.clear(); + self.incoming_paths.clear(); } /// Adds a partial path to this database. We do not deduplicate partial paths in any way; it's @@ -313,6 +323,7 @@ impl Database { path: PartialPath, ) -> Handle { let start_node = path.start_node; + let end_node = path.end_node; copious_debugging!( " Add {} path to database {}", if graph[start_node].is_root() { @@ -343,6 +354,7 @@ impl Database { self.paths_by_start_node[start_node].push(handle); } + self.incoming_paths[end_node] += 1; handle } @@ -600,6 +612,10 @@ impl ForwardCandidates, PartialPath, Database, CancellationE .find_candidate_partial_paths(self.graph, self.partials, path, result); } + fn get_joining_candidate_count(&self, path: &PartialPath) -> u32 { + self.database.incoming_paths[path.end_node] + } + fn get_graph_partials_and_db(&mut self) -> (&StackGraph, &mut PartialPaths, &Database) { (self.graph, self.partials, self.database) } @@ -852,31 +868,37 @@ impl ForwardPartialPathStitcher { Db: ToAppendable, C: ForwardCandidates, { + let check_cycle = partial_path.start_node == partial_path.end_node + || candidates.get_joining_candidate_count(partial_path) > 1; + let (graph, partials, db) = candidates.get_graph_partials_and_db(); copious_debugging!(" Extend {}", partial_path.display(graph, partials)); - // check is path is cyclic, in which case we do not extend it - let has_precondition_variables = partial_path.symbol_stack_precondition.has_variable() - || partial_path.scope_stack_precondition.has_variable(); - let cycles = cycle_detector - .is_cyclic(graph, partials, db, &mut self.appended_paths) - .expect("cyclic test failed when stitching partial paths"); - let cyclic = match has_precondition_variables { - // If the precondition has no variables, we allow cycles that strengthen the - // precondition, because we know they cannot strengthen the precondition of - // the overall path. - false => !cycles - .into_iter() - .all(|c| c == Cyclicity::StrengthensPrecondition), - // If the precondition has variables, do not allow any cycles, not even those - // that strengthen the precondition. This is more strict than necessary. Better - // might be to disallow precondition strengthening cycles only if they would - // strengthen the overall path precondition. - true => !cycles.is_empty(), - }; - if cyclic { - copious_debugging!(" is discontinued: cyclic"); - return 0; + if check_cycle { + // Check is path is cyclic, in which case we do not extend it. We only do this if the start and end nodes are the same, + // or the current end node has multiple incoming edges. If neither of these hold, the path cannot end in a cycle. + let has_precondition_variables = partial_path.symbol_stack_precondition.has_variable() + || partial_path.scope_stack_precondition.has_variable(); + let cycles = cycle_detector + .is_cyclic(graph, partials, db, &mut self.appended_paths) + .expect("cyclic test failed when stitching partial paths"); + let cyclic = match has_precondition_variables { + // If the precondition has no variables, we allow cycles that strengthen the + // precondition, because we know they cannot strengthen the precondition of + // the overall path. + false => !cycles + .into_iter() + .all(|c| c == Cyclicity::StrengthensPrecondition), + // If the precondition has variables, do not allow any cycles, not even those + // that strengthen the precondition. This is more strict than necessary. Better + // might be to disallow precondition strengthening cycles only if they would + // strengthen the overall path precondition. + true => !cycles.is_empty(), + }; + if cyclic { + copious_debugging!(" is discontinued: cyclic"); + return 0; + } } // find candidates to append diff --git a/stack-graphs/src/storage.rs b/stack-graphs/src/storage.rs index 3cd89d797..42e7234a5 100644 --- a/stack-graphs/src/storage.rs +++ b/stack-graphs/src/storage.rs @@ -736,6 +736,10 @@ impl ForwardCandidates, PartialPath, Database, StorageError> .find_candidate_partial_paths(&self.graph, &mut self.partials, path, result); } + fn get_joining_candidate_count(&self, _path: &PartialPath) -> u32 { + u32::MAX // err on the safe side + } + fn get_graph_partials_and_db(&mut self) -> (&StackGraph, &mut PartialPaths, &Database) { (&self.graph, &mut self.partials, &self.db) } From d7bb4ad92f07bb301e06afddca8510b7eaa9336b Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Mon, 13 Nov 2023 11:16:47 +0100 Subject: [PATCH 035/291] Only apply join count optimization if explicitly enabled --- stack-graphs/src/stitching.rs | 30 ++++++++++++++++++++++++++---- stack-graphs/src/storage.rs | 4 ++-- 2 files changed, 28 insertions(+), 6 deletions(-) diff --git a/stack-graphs/src/stitching.rs b/stack-graphs/src/stitching.rs index d348dac9b..169f1e00a 100644 --- a/stack-graphs/src/stitching.rs +++ b/stack-graphs/src/stitching.rs @@ -469,6 +469,11 @@ impl Database { } } + /// Returns the number of paths in this database that share the given end node. + pub fn get_incoming_path_count(&self, end_node: Handle) -> u32 { + self.incoming_paths[end_node] + } + /// Determines which nodes in the stack graph are “local”, taking into account the partial /// paths in this database. /// @@ -613,7 +618,7 @@ impl ForwardCandidates, PartialPath, Database, CancellationE } fn get_joining_candidate_count(&self, path: &PartialPath) -> u32 { - self.database.incoming_paths[path.end_node] + self.database.get_incoming_path_count(path.end_node) } fn get_graph_partials_and_db(&mut self) -> (&StackGraph, &mut PartialPaths, &Database) { @@ -770,6 +775,7 @@ pub struct ForwardPartialPathStitcher { ), appended_paths: Appendables, similar_path_detector: Option>, + check_only_join_nodes: bool, max_work_per_phase: usize, #[cfg(feature = "copious-debugging")] phase_number: usize, @@ -803,6 +809,8 @@ impl ForwardPartialPathStitcher { next_iteration, appended_paths, similar_path_detector: Some(SimilarPathDetector::new()), + // By default, all nodes are checked for cycles and (if enabled) similarity + check_only_join_nodes: false, // By default, there's no artificial bound on the amount of work done per phase max_work_per_phase: usize::MAX, #[cfg(feature = "copious-debugging")] @@ -844,6 +852,14 @@ impl ForwardPartialPathStitcher { } } + /// Sets whether all nodes are checked for cycles and (if enabled) similar paths, or only nodes with multiple + /// incoming candidates. Checking only join nodes is **unsafe** unless the database of candidates is stable + /// between all stitching phases. If paths are added to the database from one phase to another, for example if + /// paths are dynamically loaded from storage, setting this to true is incorrect and might lead to non-termination! + pub fn set_check_only_join_nodes(&mut self, check_only_join_nodes: bool) { + self.check_only_join_nodes = check_only_join_nodes; + } + /// Sets the maximum amount of work that can be performed during each phase of the algorithm. /// By bounding our work this way, you can ensure that it's not possible for our CPU-bound /// algorithm to starve any worker threads or processes that you might be using. If you don't @@ -868,7 +884,8 @@ impl ForwardPartialPathStitcher { Db: ToAppendable, C: ForwardCandidates, { - let check_cycle = partial_path.start_node == partial_path.end_node + let check_cycle = !self.check_only_join_nodes + || partial_path.start_node == partial_path.end_node || candidates.get_joining_candidate_count(partial_path) > 1; let (graph, partials, db) = candidates.get_graph_partials_and_db(); @@ -936,8 +953,11 @@ impl ForwardPartialPathStitcher { self.next_iteration.1.reserve(extension_count); self.next_iteration.2.reserve(extension_count); for (new_partial_path, new_cycle_detector) in self.extensions.drain(..) { - let can_converge = graph.incoming_edge_count(new_partial_path.end_node) > 1; - if new_has_split && can_converge { + let check_similar_path = new_has_split + && (!self.check_only_join_nodes + || candidates.get_joining_candidate_count(&new_partial_path) > 1); + let (graph, partials, _) = candidates.get_graph_partials_and_db(); + if check_similar_path { if let Some(similar_path_detector) = &mut self.similar_path_detector { if similar_path_detector.has_similar_path( graph, @@ -1081,6 +1101,7 @@ impl ForwardPartialPathStitcher { .collect::>(); let mut stitcher = ForwardPartialPathStitcher::from_partial_paths(graph, partials, initial_paths); + stitcher.set_check_only_join_nodes(true); while !stitcher.is_complete() { cancellation_flag.check("finding complete partial paths")?; stitcher.process_next_phase( @@ -1136,6 +1157,7 @@ impl ForwardPartialPathStitcher { .collect::>(); ForwardPartialPathStitcher::from_partial_paths(graph, partials, initial_paths) }; + stitcher.set_check_only_join_nodes(true); while !stitcher.is_complete() { cancellation_flag.check("finding complete partial paths")?; for path in stitcher.previous_phase_partial_paths() { diff --git a/stack-graphs/src/storage.rs b/stack-graphs/src/storage.rs index 42e7234a5..78481dc71 100644 --- a/stack-graphs/src/storage.rs +++ b/stack-graphs/src/storage.rs @@ -736,8 +736,8 @@ impl ForwardCandidates, PartialPath, Database, StorageError> .find_candidate_partial_paths(&self.graph, &mut self.partials, path, result); } - fn get_joining_candidate_count(&self, _path: &PartialPath) -> u32 { - u32::MAX // err on the safe side + fn get_joining_candidate_count(&self, path: &PartialPath) -> u32 { + self.db.get_incoming_path_count(path.end_node) } fn get_graph_partials_and_db(&mut self) -> (&StackGraph, &mut PartialPaths, &Database) { From e535492484356f599ca751873b2ee9615cf7c2bb Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Mon, 13 Nov 2023 14:20:58 +0100 Subject: [PATCH 036/291] Reduce preallocated buffers --- stack-graphs/src/cycles.rs | 2 +- stack-graphs/src/graph.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/stack-graphs/src/cycles.rs b/stack-graphs/src/cycles.rs index 2fc125b6c..6038770fd 100644 --- a/stack-graphs/src/cycles.rs +++ b/stack-graphs/src/cycles.rs @@ -49,7 +49,7 @@ use crate::stitching::ToAppendable; /// Helps detect similar paths in the path-finding algorithm. pub struct SimilarPathDetector

{ - paths: HashMap>, + paths: HashMap>, } #[doc(hidden)] diff --git a/stack-graphs/src/graph.rs b/stack-graphs/src/graph.rs index 8c0de1f74..50eb1ab03 100644 --- a/stack-graphs/src/graph.rs +++ b/stack-graphs/src/graph.rs @@ -1449,10 +1449,10 @@ pub struct StackGraph { pub(crate) nodes: Arena, pub(crate) source_info: SupplementalArena, node_id_handles: NodeIDHandles, - outgoing_edges: SupplementalArena>, + outgoing_edges: SupplementalArena>, incoming_edges: SupplementalArena, pub(crate) node_debug_info: SupplementalArena, - pub(crate) edge_debug_info: SupplementalArena, DebugInfo); 8]>>, + pub(crate) edge_debug_info: SupplementalArena, DebugInfo); 4]>>, } impl StackGraph { From 07ca85d6bd593c26a03f34d577618e1b5d342337 Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Mon, 13 Nov 2023 17:01:09 +0100 Subject: [PATCH 037/291] Add comment warning for in-loop modification --- stack-graphs/src/cycles.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/stack-graphs/src/cycles.rs b/stack-graphs/src/cycles.rs index 6038770fd..d10679385 100644 --- a/stack-graphs/src/cycles.rs +++ b/stack-graphs/src/cycles.rs @@ -110,12 +110,15 @@ where { let key = path.key(); + // Iterate through the bucket to determine if this paths is better than any already known + // path. Note that the bucket might be modified during the loop if a path is removed which + // is shadowed by the new path! let possibly_similar_paths = self.paths.entry(key).or_default(); let mut idx = 0; while idx < possibly_similar_paths.len() { match cmp(arena, path, &possibly_similar_paths[idx]) { Some(Ordering::Less) => { - // the new path is betetr, remove the old one + // the new path is better, remove the old one possibly_similar_paths.remove(idx); // keep `idx` which now points to the next element continue; From cd58abbff3d128fc480f7f016c3ac1ed9d01bf04 Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Mon, 13 Nov 2023 17:27:12 +0100 Subject: [PATCH 038/291] Replace incoming count with degree --- .../rust/npm_package.rs | 2 +- .../rust/util.rs | 10 ++-- stack-graphs/src/graph.rs | 46 ++++++++++++++++--- stack-graphs/src/stitching.rs | 22 +++++---- stack-graphs/src/storage.rs | 5 +- stack-graphs/tests/it/graph.rs | 8 ---- 6 files changed, 63 insertions(+), 30 deletions(-) diff --git a/languages/tree-sitter-stack-graphs-javascript/rust/npm_package.rs b/languages/tree-sitter-stack-graphs-javascript/rust/npm_package.rs index 5a46d6ee1..e0ab0c951 100644 --- a/languages/tree-sitter-stack-graphs-javascript/rust/npm_package.rs +++ b/languages/tree-sitter-stack-graphs-javascript/rust/npm_package.rs @@ -115,7 +115,7 @@ impl FileAnalyzer for NpmPackageAnalyzer { EXPORTS_GUARD, "exports_guard_pop", ); - replace_edge(graph, pkg_name_pop, exports_guard_pop, 1); + set_edge_precedence(graph, pkg_name_pop, exports_guard_pop, 1); let main = Some(npm_pkg.main) .filter(|main| !main.is_empty()) .and_then(|main| NormalizedRelativePath::from_str(&main)) diff --git a/languages/tree-sitter-stack-graphs-javascript/rust/util.rs b/languages/tree-sitter-stack-graphs-javascript/rust/util.rs index 1bed520cd..a53f2e7dd 100644 --- a/languages/tree-sitter-stack-graphs-javascript/rust/util.rs +++ b/languages/tree-sitter-stack-graphs-javascript/rust/util.rs @@ -61,12 +61,16 @@ pub fn add_edge(graph: &mut StackGraph, from: Handle, to: Handle, pr graph.add_edge(from, to, precedence); } -pub fn replace_edge(graph: &mut StackGraph, from: Handle, to: Handle, precedence: i32) { +pub fn set_edge_precedence( + graph: &mut StackGraph, + from: Handle, + to: Handle, + precedence: i32, +) { if from == to { return; } - graph.remove_edge(from, to); - graph.add_edge(from, to, precedence); + graph.set_edge_precedence(from, to, precedence); } pub fn add_module_pops( diff --git a/stack-graphs/src/graph.rs b/stack-graphs/src/graph.rs index 50eb1ab03..53dd392da 100644 --- a/stack-graphs/src/graph.rs +++ b/stack-graphs/src/graph.rs @@ -1303,16 +1303,20 @@ impl StackGraph { let edges = &mut self.outgoing_edges[source]; if let Err(index) = edges.binary_search_by_key(&sink, |o| o.sink) { edges.insert(index, OutgoingEdge { sink, precedence }); - self.incoming_edges[sink] += 1; + self.incoming_edges[sink] += Degree::One; } } /// Removes an edge from the stack graph. - pub fn remove_edge(&mut self, source: Handle, sink: Handle) { + pub fn set_edge_precedence( + &mut self, + source: Handle, + sink: Handle, + precedence: i32, + ) { let edges = &mut self.outgoing_edges[source]; if let Ok(index) = edges.binary_search_by_key(&sink, |o| o.sink) { - edges.remove(index); - self.incoming_edges[sink] -= 1; + edges[index].precedence = precedence; } } @@ -1329,7 +1333,7 @@ impl StackGraph { } /// Returns the number of edges that end at a particular sink node. - pub fn incoming_edge_count(&self, sink: Handle) -> u32 { + pub fn incoming_edge_degree(&self, sink: Handle) -> Degree { self.incoming_edges[sink] } } @@ -1437,6 +1441,36 @@ impl StackGraph { //------------------------------------------------------------------------------------------------- // Stack graphs +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +#[repr(u8)] +pub enum Degree { + Zero, + One, + Multiple, +} + +impl Default for Degree { + fn default() -> Self { + Self::Zero + } +} + +impl std::ops::Add for Degree { + type Output = Self; + fn add(self, rhs: Self) -> Self::Output { + match (self, rhs) { + (Self::Zero, result) | (result, Self::Zero) => result, + _ => Self::Multiple, + } + } +} + +impl std::ops::AddAssign for Degree { + fn add_assign(&mut self, rhs: Self) { + *self = *self + rhs; + } +} + /// Contains all of the nodes and edges that make up a stack graph. pub struct StackGraph { interned_strings: InternedStringArena, @@ -1450,7 +1484,7 @@ pub struct StackGraph { pub(crate) source_info: SupplementalArena, node_id_handles: NodeIDHandles, outgoing_edges: SupplementalArena>, - incoming_edges: SupplementalArena, + incoming_edges: SupplementalArena, pub(crate) node_debug_info: SupplementalArena, pub(crate) edge_debug_info: SupplementalArena, DebugInfo); 4]>>, } diff --git a/stack-graphs/src/stitching.rs b/stack-graphs/src/stitching.rs index 169f1e00a..3d02a14be 100644 --- a/stack-graphs/src/stitching.rs +++ b/stack-graphs/src/stitching.rs @@ -54,6 +54,7 @@ use crate::arena::SupplementalArena; use crate::cycles::Appendables; use crate::cycles::AppendingCycleDetector; use crate::cycles::SimilarPathDetector; +use crate::graph::Degree; use crate::graph::Edge; use crate::graph::File; use crate::graph::Node; @@ -203,7 +204,7 @@ where R: std::iter::Extend; /// Get the number of available candidates that share the given path's end node. - fn get_joining_candidate_count(&self, path: &PartialPath) -> u32; + fn get_joining_candidate_degree(&self, path: &PartialPath) -> Degree; /// Get the graph, partial path arena, and database backing this candidates instance. fn get_graph_partials_and_db(&mut self) -> (&StackGraph, &mut PartialPaths, &Db); @@ -246,8 +247,8 @@ impl ForwardCandidates for GraphEdgeC })); } - fn get_joining_candidate_count(&self, path: &PartialPath) -> u32 { - self.graph.incoming_edge_count(path.end_node) + fn get_joining_candidate_degree(&self, path: &PartialPath) -> Degree { + self.graph.incoming_edge_degree(path.end_node) } fn get_graph_partials_and_db(&mut self) -> (&StackGraph, &mut PartialPaths, &GraphEdges) { @@ -284,7 +285,7 @@ pub struct Database { symbol_stack_key_cache: HashMap, paths_by_start_node: SupplementalArena>>, root_paths_by_precondition: SupplementalArena>>, - incoming_paths: SupplementalArena, + incoming_paths: SupplementalArena, } impl Database { @@ -354,7 +355,7 @@ impl Database { self.paths_by_start_node[start_node].push(handle); } - self.incoming_paths[end_node] += 1; + self.incoming_paths[end_node] += Degree::One; handle } @@ -470,7 +471,7 @@ impl Database { } /// Returns the number of paths in this database that share the given end node. - pub fn get_incoming_path_count(&self, end_node: Handle) -> u32 { + pub fn get_incoming_path_degree(&self, end_node: Handle) -> Degree { self.incoming_paths[end_node] } @@ -617,8 +618,8 @@ impl ForwardCandidates, PartialPath, Database, CancellationE .find_candidate_partial_paths(self.graph, self.partials, path, result); } - fn get_joining_candidate_count(&self, path: &PartialPath) -> u32 { - self.database.get_incoming_path_count(path.end_node) + fn get_joining_candidate_degree(&self, path: &PartialPath) -> Degree { + self.database.get_incoming_path_degree(path.end_node) } fn get_graph_partials_and_db(&mut self) -> (&StackGraph, &mut PartialPaths, &Database) { @@ -886,7 +887,7 @@ impl ForwardPartialPathStitcher { { let check_cycle = !self.check_only_join_nodes || partial_path.start_node == partial_path.end_node - || candidates.get_joining_candidate_count(partial_path) > 1; + || candidates.get_joining_candidate_degree(partial_path) == Degree::Multiple; let (graph, partials, db) = candidates.get_graph_partials_and_db(); copious_debugging!(" Extend {}", partial_path.display(graph, partials)); @@ -955,7 +956,8 @@ impl ForwardPartialPathStitcher { for (new_partial_path, new_cycle_detector) in self.extensions.drain(..) { let check_similar_path = new_has_split && (!self.check_only_join_nodes - || candidates.get_joining_candidate_count(&new_partial_path) > 1); + || candidates.get_joining_candidate_degree(&new_partial_path) + == Degree::Multiple); let (graph, partials, _) = candidates.get_graph_partials_and_db(); if check_similar_path { if let Some(similar_path_detector) = &mut self.similar_path_detector { diff --git a/stack-graphs/src/storage.rs b/stack-graphs/src/storage.rs index 78481dc71..7664ea88f 100644 --- a/stack-graphs/src/storage.rs +++ b/stack-graphs/src/storage.rs @@ -19,6 +19,7 @@ use std::path::PathBuf; use thiserror::Error; use crate::arena::Handle; +use crate::graph::Degree; use crate::graph::File; use crate::graph::Node; use crate::graph::StackGraph; @@ -736,8 +737,8 @@ impl ForwardCandidates, PartialPath, Database, StorageError> .find_candidate_partial_paths(&self.graph, &mut self.partials, path, result); } - fn get_joining_candidate_count(&self, path: &PartialPath) -> u32 { - self.db.get_incoming_path_count(path.end_node) + fn get_joining_candidate_degree(&self, path: &PartialPath) -> Degree { + self.db.get_incoming_path_degree(path.end_node) } fn get_graph_partials_and_db(&mut self) -> (&StackGraph, &mut PartialPaths, &Database) { diff --git a/stack-graphs/tests/it/graph.rs b/stack-graphs/tests/it/graph.rs index 353850c6f..f19bb2c30 100644 --- a/stack-graphs/tests/it/graph.rs +++ b/stack-graphs/tests/it/graph.rs @@ -157,14 +157,6 @@ fn can_add_and_remove_edges() { .collect::>(), hashset! { (h2, 0), (h3, 0), (h4, 0) } ); - graph.remove_edge(h1, h3); - assert_eq!( - graph - .outgoing_edges(h1) - .map(|edge| edge.sink) - .collect::>(), - hashset! { h2, h4 } - ); } #[test] From a2dc6158488db02a3fe0cf4730412d3f17e8d868 Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Tue, 7 Nov 2023 13:48:52 +0100 Subject: [PATCH 039/291] Only export main module from package --- .../rust/npm_package.rs | 1 - .../rust/util.rs | 12 ------ .../src/stack-graphs.tsg | 4 -- ...can-access-package-non-main-explicilty.js} | 32 +++------------ .../package-does-not-export-non-main.js | 32 +++++++++++++++ .../packages/package-exports-explicit-main.js | 39 +++++++++++++++++++ .../package-main-defaults-to-index.js | 38 ++++++++++++++++++ 7 files changed, 114 insertions(+), 44 deletions(-) rename languages/tree-sitter-stack-graphs-javascript/test/old/packages/{package_dependencies.js => can-access-package-non-main-explicilty.js} (64%) create mode 100644 languages/tree-sitter-stack-graphs-javascript/test/old/packages/package-does-not-export-non-main.js create mode 100644 languages/tree-sitter-stack-graphs-javascript/test/old/packages/package-exports-explicit-main.js create mode 100644 languages/tree-sitter-stack-graphs-javascript/test/old/packages/package-main-defaults-to-index.js diff --git a/languages/tree-sitter-stack-graphs-javascript/rust/npm_package.rs b/languages/tree-sitter-stack-graphs-javascript/rust/npm_package.rs index e0ab0c951..ddaa952cc 100644 --- a/languages/tree-sitter-stack-graphs-javascript/rust/npm_package.rs +++ b/languages/tree-sitter-stack-graphs-javascript/rust/npm_package.rs @@ -115,7 +115,6 @@ impl FileAnalyzer for NpmPackageAnalyzer { EXPORTS_GUARD, "exports_guard_pop", ); - set_edge_precedence(graph, pkg_name_pop, exports_guard_pop, 1); let main = Some(npm_pkg.main) .filter(|main| !main.is_empty()) .and_then(|main| NormalizedRelativePath::from_str(&main)) diff --git a/languages/tree-sitter-stack-graphs-javascript/rust/util.rs b/languages/tree-sitter-stack-graphs-javascript/rust/util.rs index a53f2e7dd..17ab8d779 100644 --- a/languages/tree-sitter-stack-graphs-javascript/rust/util.rs +++ b/languages/tree-sitter-stack-graphs-javascript/rust/util.rs @@ -61,18 +61,6 @@ pub fn add_edge(graph: &mut StackGraph, from: Handle, to: Handle, pr graph.add_edge(from, to, precedence); } -pub fn set_edge_precedence( - graph: &mut StackGraph, - from: Handle, - to: Handle, - precedence: i32, -) { - if from == to { - return; - } - graph.set_edge_precedence(from, to, precedence); -} - pub fn add_module_pops( graph: &mut StackGraph, file: Handle, diff --git a/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg b/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg index 3aa12eabd..640ec0976 100644 --- a/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg +++ b/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg @@ -348,10 +348,6 @@ inherit .return_or_yield edge module_pop_end -> prog_exports_pop edge prog_exports_pop -> @prog.exports - ; allow direct access from the package to the modules exports - ; this is used from package.json - edge @prog.pkg_pop -> prog_exports_pop - ;; builtin types node @prog.builtins_number node @prog.builtins_string diff --git a/languages/tree-sitter-stack-graphs-javascript/test/old/packages/package_dependencies.js b/languages/tree-sitter-stack-graphs-javascript/test/old/packages/can-access-package-non-main-explicilty.js similarity index 64% rename from languages/tree-sitter-stack-graphs-javascript/test/old/packages/package_dependencies.js rename to languages/tree-sitter-stack-graphs-javascript/test/old/packages/can-access-package-non-main-explicilty.js index 7a67a873a..820c0368e 100644 --- a/languages/tree-sitter-stack-graphs-javascript/test/old/packages/package_dependencies.js +++ b/languages/tree-sitter-stack-graphs-javascript/test/old/packages/can-access-package-non-main-explicilty.js @@ -1,18 +1,3 @@ -/* --- path: foo/package.json --- */ -/* --- global: FILE_PATH=package.json --- */ -/* --- global: PROJECT_NAME=foo --- */ - -{ - "name": "foo", - "version": "1.0" -} - -/* --- path: foo/src/index.js --- */ -/* --- global: FILE_PATH=src/index.js --- */ -/* --- global: PROJECT_NAME=foo --- */ - -export let x; - /* --- path: acme_foo/package.json --- */ /* --- global: FILE_PATH=package.json --- */ /* --- global: PROJECT_NAME=acme_foo --- */ @@ -23,14 +8,14 @@ export let x; "main": "./api" } -/* --- path: acme_foo/core.js --- */ -/* --- global: FILE_PATH=core.js --- */ +/* --- path: acme_foo/api.js --- */ +/* --- global: FILE_PATH=api.js --- */ /* --- global: PROJECT_NAME=acme_foo --- */ export let x; -/* --- path: acme_foo/api.js --- */ -/* --- global: FILE_PATH=api.js --- */ +/* --- path: acme_foo/core.js --- */ +/* --- global: FILE_PATH=core.js --- */ /* --- global: PROJECT_NAME=acme_foo --- */ export let x; @@ -42,7 +27,6 @@ export let x; { "name": "bar", "dependencies": { - "foo": "1", "@acme/foo": "1" } } @@ -51,11 +35,5 @@ export let x; /* --- global: FILE_PATH=app.js --- */ /* --- global: PROJECT_NAME=bar --- */ -import { x } from "@acme/foo" -// ^ defined: 36 - -import { x } from "foo" -// ^ defined: 14 - import { x } from "@acme/foo/core" -// ^ defined: 30 +// ^ defined: 21 diff --git a/languages/tree-sitter-stack-graphs-javascript/test/old/packages/package-does-not-export-non-main.js b/languages/tree-sitter-stack-graphs-javascript/test/old/packages/package-does-not-export-non-main.js new file mode 100644 index 000000000..cea744788 --- /dev/null +++ b/languages/tree-sitter-stack-graphs-javascript/test/old/packages/package-does-not-export-non-main.js @@ -0,0 +1,32 @@ +/* --- path: foo/package.json --- */ +/* --- global: FILE_PATH=package.json --- */ +/* --- global: PROJECT_NAME=foo --- */ + +{ + "name": "foo", + "version": "1.0" +} + +/* --- path: foo/impl.js --- */ +/* --- global: FILE_PATH=impl.js --- */ +/* --- global: PROJECT_NAME=foo --- */ + +export let x; + +/* --- path: bar/package.json --- */ +/* --- global: FILE_PATH=package.json --- */ +/* --- global: PROJECT_NAME=bar --- */ + +{ + "name": "bar", + "dependencies": { + "foo": "1" + } +} + +/* --- path: bar/app.js --- */ +/* --- global: FILE_PATH=app.js --- */ +/* --- global: PROJECT_NAME=bar --- */ + +import { x } from "foo" +// ^ defined: diff --git a/languages/tree-sitter-stack-graphs-javascript/test/old/packages/package-exports-explicit-main.js b/languages/tree-sitter-stack-graphs-javascript/test/old/packages/package-exports-explicit-main.js new file mode 100644 index 000000000..14ec28812 --- /dev/null +++ b/languages/tree-sitter-stack-graphs-javascript/test/old/packages/package-exports-explicit-main.js @@ -0,0 +1,39 @@ +/* --- path: acme_foo/package.json --- */ +/* --- global: FILE_PATH=package.json --- */ +/* --- global: PROJECT_NAME=acme_foo --- */ + +{ + "name": "@acme/foo", + "version": "1.0", + "main": "./api" +} + +/* --- path: acme_foo/api.js --- */ +/* --- global: FILE_PATH=api.js --- */ +/* --- global: PROJECT_NAME=acme_foo --- */ + +export let x; + +/* --- path: acme_foo/core.js --- */ +/* --- global: FILE_PATH=core.js --- */ +/* --- global: PROJECT_NAME=acme_foo --- */ + +export let x; + +/* --- path: bar/package.json --- */ +/* --- global: FILE_PATH=package.json --- */ +/* --- global: PROJECT_NAME=bar --- */ + +{ + "name": "bar", + "dependencies": { + "@acme/foo": "1" + } +} + +/* --- path: bar/app.js --- */ +/* --- global: FILE_PATH=app.js --- */ +/* --- global: PROJECT_NAME=bar --- */ + +import { x } from "@acme/foo" +// ^ defined: 15 diff --git a/languages/tree-sitter-stack-graphs-javascript/test/old/packages/package-main-defaults-to-index.js b/languages/tree-sitter-stack-graphs-javascript/test/old/packages/package-main-defaults-to-index.js new file mode 100644 index 000000000..c06d0d625 --- /dev/null +++ b/languages/tree-sitter-stack-graphs-javascript/test/old/packages/package-main-defaults-to-index.js @@ -0,0 +1,38 @@ +/* --- path: foo/package.json --- */ +/* --- global: FILE_PATH=package.json --- */ +/* --- global: PROJECT_NAME=foo --- */ + +{ + "name": "foo", + "version": "1.0" +} + +/* --- path: foo/index.js --- */ +/* --- global: FILE_PATH=index.js --- */ +/* --- global: PROJECT_NAME=foo --- */ + +export let x; + +/* --- path: foo/impl.js --- */ +/* --- global: FILE_PATH=impl.js --- */ +/* --- global: PROJECT_NAME=foo --- */ + +export let x; + +/* --- path: bar/package.json --- */ +/* --- global: FILE_PATH=package.json --- */ +/* --- global: PROJECT_NAME=bar --- */ + +{ + "name": "bar", + "dependencies": { + "foo": "1" + } +} + +/* --- path: bar/app.js --- */ +/* --- global: FILE_PATH=app.js --- */ +/* --- global: PROJECT_NAME=bar --- */ + +import { x } from "foo" +// ^ defined: 14 From d8b454025cd9d219fdd22ba70443fa6359eaffb3 Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Thu, 9 Nov 2023 20:04:36 +0100 Subject: [PATCH 040/291] Add a place to document package structure --- .../docs/package-structure.md | 11 +++++++ .../docs/package-structure/example.js | 31 ++++++++++++++++++ .../docs/package-structure/example.png | Bin 0 -> 227817 bytes 3 files changed, 42 insertions(+) create mode 100644 languages/tree-sitter-stack-graphs-javascript/docs/package-structure.md create mode 100644 languages/tree-sitter-stack-graphs-javascript/docs/package-structure/example.js create mode 100644 languages/tree-sitter-stack-graphs-javascript/docs/package-structure/example.png diff --git a/languages/tree-sitter-stack-graphs-javascript/docs/package-structure.md b/languages/tree-sitter-stack-graphs-javascript/docs/package-structure.md new file mode 100644 index 000000000..30b96945a --- /dev/null +++ b/languages/tree-sitter-stack-graphs-javascript/docs/package-structure.md @@ -0,0 +1,11 @@ +# How package structure is modeled + +_This is WIP and only contains links to the example, no explanation yet._ + +An [example](package-structure/example.js) test shows a basic setup with a package `foo` exporting a function `foo`, and a package `bar` depending on `foo` and importing the function. + +The stack graph for this example looks as follows: + +![stack graph for example](package-structure/example.png) + +_Generate this yourself by running `cargo run --features cli -- test --save-graph='%d/%n.html' --output-mode=always docs/package-structure/example.js`._ diff --git a/languages/tree-sitter-stack-graphs-javascript/docs/package-structure/example.js b/languages/tree-sitter-stack-graphs-javascript/docs/package-structure/example.js new file mode 100644 index 000000000..ba8165fa7 --- /dev/null +++ b/languages/tree-sitter-stack-graphs-javascript/docs/package-structure/example.js @@ -0,0 +1,31 @@ +/* --- path: foo/package.json --- */ +/* --- global: FILE_PATH=package.json --- */ +/* --- global: PROJECT_NAME=54EA007B --- */ + +{ + "name": "foo" +} + +/* --- path: foo/index.js --- */ +/* --- global: FILE_PATH=index.js --- */ +/* --- global: PROJECT_NAME=54EA007B --- */ + +export function foo() {} + +/* --- path: bar/package.json --- */ +/* --- global: FILE_PATH=package.json --- */ +/* --- global: PROJECT_NAME=202D9AA4 --- */ + +{ + "name": "bar", + "dependencies": { + "foo": "" + } +} + +/* --- path: bar/index.js --- */ +/* --- global: FILE_PATH=index.js --- */ +/* --- global: PROJECT_NAME=202D9AA4 --- */ + +import { foo } from "foo" +// ^ defined: 13 diff --git a/languages/tree-sitter-stack-graphs-javascript/docs/package-structure/example.png b/languages/tree-sitter-stack-graphs-javascript/docs/package-structure/example.png new file mode 100644 index 0000000000000000000000000000000000000000..83889ee42815f7a49bb64b02efc3ce44dcb18dd3 GIT binary patch literal 227817 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�fJW8btGfc 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�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
Uzgdwkx?;mtjfU&* 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 e{?Xh4Zmr+1Sv? Date: Tue, 14 Nov 2023 11:27:08 +0100 Subject: [PATCH 041/291] Fix arena clear methods --- stack-graphs/src/arena.rs | 8 ++++---- stack-graphs/tests/it/arena.rs | 25 +++++++++++++++++++++++++ 2 files changed, 29 insertions(+), 4 deletions(-) diff --git a/stack-graphs/src/arena.rs b/stack-graphs/src/arena.rs index 6e4747267..18009db02 100644 --- a/stack-graphs/src/arena.rs +++ b/stack-graphs/src/arena.rs @@ -183,10 +183,10 @@ impl Arena { /// Clear the arena, keeping underlying allocated capacity. After this, all previous handles into /// the arena are invalid. - #[cfg_attr(not(feature = "storage"), allow(dead_code))] #[inline(always)] - pub(crate) fn clear(&mut self) { + pub fn clear(&mut self) { self.items.clear(); + self.items.push(MaybeUninit::uninit()); } /// Adds a new instance to this arena, returning a stable handle to it. @@ -290,10 +290,10 @@ impl SupplementalArena { /// Clear the supplemantal arena, keeping underlying allocated capacity. After this, /// all previous handles into the arena are invalid. - #[cfg_attr(not(feature = "storage"), allow(dead_code))] #[inline(always)] - pub(crate) fn clear(&mut self) { + pub fn clear(&mut self) { self.items.clear(); + self.items.push(MaybeUninit::uninit()); } /// Creates a new, empty supplemental arena, preallocating enough space to store supplemental diff --git a/stack-graphs/tests/it/arena.rs b/stack-graphs/tests/it/arena.rs index 5af25d66e..6963dae29 100644 --- a/stack-graphs/tests/it/arena.rs +++ b/stack-graphs/tests/it/arena.rs @@ -229,3 +229,28 @@ fn can_compare_deques() { deque10.ensure_backwards(&mut arena); assert_eq!(deque1.cmp(&mut arena, deque10), Ordering::Less); } + +#[test] +fn can_use_arena_after_clear() { + let mut a = Arena::new(); + let h = a.add(12 as u8); + assert_eq!(12, *a.get(h)); + + a.clear(); + let h = a.add(7); + assert_eq!(7, *a.get(h)); +} + +#[test] +fn can_use_supplemental_arena_after_clear() { + let mut a = Arena::new(); + let h = a.add(()); + + let mut x = SupplementalArena::new(); + x[h] = 12; + assert_eq!(Some(12), x.get(h).cloned()); + + x.clear(); + x[h] = 7; + assert_eq!(Some(7), x.get(h).cloned()); +} From 3696992ed06c7a40ca4eff849eda7b290dbf8950 Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Tue, 14 Nov 2023 17:29:36 +0100 Subject: [PATCH 042/291] Use truncate instead --- stack-graphs/src/arena.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/stack-graphs/src/arena.rs b/stack-graphs/src/arena.rs index 18009db02..134a90d14 100644 --- a/stack-graphs/src/arena.rs +++ b/stack-graphs/src/arena.rs @@ -185,8 +185,7 @@ impl Arena { /// the arena are invalid. #[inline(always)] pub fn clear(&mut self) { - self.items.clear(); - self.items.push(MaybeUninit::uninit()); + self.items.truncate(1); } /// Adds a new instance to this arena, returning a stable handle to it. @@ -292,8 +291,7 @@ impl SupplementalArena { /// all previous handles into the arena are invalid. #[inline(always)] pub fn clear(&mut self) { - self.items.clear(); - self.items.push(MaybeUninit::uninit()); + self.items.truncate(1); } /// Creates a new, empty supplemental arena, preallocating enough space to store supplemental From 64739d3889f84a464e898f42c26a860753f16586 Mon Sep 17 00:00:00 2001 From: Rebecca Valentine Date: Tue, 14 Nov 2023 13:15:40 -0800 Subject: [PATCH 043/291] Add definiens to guard exports Co-Authored-By: Rob Rix --- .../tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg | 1 + 1 file changed, 1 insertion(+) diff --git a/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg b/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg index 0d2a73c18..87ae4e8db 100644 --- a/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg +++ b/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg @@ -346,6 +346,7 @@ inherit .return_or_yield attr (@prog.after_scope) empty_source_span attr (prog_exports_pop) pop_symbol = "GUARD:EXPORTS" + attr (prog_exports_pop) definiens_node = @prog edge module_pop_end -> prog_exports_pop edge prog_exports_pop -> @prog.exports From 16e008e51bf6596df0ac8a19770c274707c5c7f2 Mon Sep 17 00:00:00 2001 From: Rebecca Valentine Date: Tue, 14 Nov 2023 13:32:00 -0800 Subject: [PATCH 044/291] Try modifying the package processor instead? Co-Authored-By: Rob Rix --- .../rust/npm_package.rs | 33 ++++++++++++++----- .../rust/util.rs | 1 + .../src/stack-graphs.tsg | 1 - 3 files changed, 26 insertions(+), 9 deletions(-) diff --git a/languages/tree-sitter-stack-graphs-javascript/rust/npm_package.rs b/languages/tree-sitter-stack-graphs-javascript/rust/npm_package.rs index ddaa952cc..3ce18aa07 100644 --- a/languages/tree-sitter-stack-graphs-javascript/rust/npm_package.rs +++ b/languages/tree-sitter-stack-graphs-javascript/rust/npm_package.rs @@ -104,6 +104,16 @@ impl FileAnalyzer for NpmPackageAnalyzer { ); add_edge(graph, pkg_name_pop, pkg_internal_name_push, 0); + // Common main + let main = Some(npm_pkg.main) + .filter(|main| !main.is_empty()) + .and_then(|main| NormalizedRelativePath::from_str(&main)) + .map(|p| p.into_path_buf()) + .unwrap_or(PathBuf::from("index")) + .with_extension(""); + let main_push = + add_module_pushes(graph, file, &main, pkg_internal_name_push, "main_push"); + // reach main exports directly via package name (with precedence) // // [pop pkg_name] -1-> [pop "GUARD:EXPORTS"] -> [push "GUARD:EXPORTS"] -> [push main]* -> [push pkg_internal_name] @@ -115,17 +125,24 @@ impl FileAnalyzer for NpmPackageAnalyzer { EXPORTS_GUARD, "exports_guard_pop", ); - let main = Some(npm_pkg.main) - .filter(|main| !main.is_empty()) - .and_then(|main| NormalizedRelativePath::from_str(&main)) - .map(|p| p.into_path_buf()) - .unwrap_or(PathBuf::from("index")) - .with_extension(""); - let main_push = - add_module_pushes(graph, file, &main, pkg_internal_name_push, "main_push"); let exports_guard_push = add_push(graph, file, main_push, EXPORTS_GUARD, "exports_guard_push"); add_edge(graph, exports_guard_pop, exports_guard_push, 0); + + // reach main exports directly via package name (with precedence) + // + // [pop pkg_name] -1-> [pop "GUARD:DEFAULT"] -> [push "GUARD:DEFAULT"] -> [push main]* -> [push pkg_internal_name] + // + let default_guard_pop = add_pop( + graph, + file, + pkg_name_pop, + DEFAULT_GUARD, + "default_guard_pop", + ); + let default_guard_push = + add_push(graph, file, main_push, DEFAULT_GUARD, "default_guard_push"); + add_edge(graph, default_guard_pop, default_guard_push, 0); } // reach dependencies via package internal name diff --git a/languages/tree-sitter-stack-graphs-javascript/rust/util.rs b/languages/tree-sitter-stack-graphs-javascript/rust/util.rs index 17ab8d779..1e0c14019 100644 --- a/languages/tree-sitter-stack-graphs-javascript/rust/util.rs +++ b/languages/tree-sitter-stack-graphs-javascript/rust/util.rs @@ -15,6 +15,7 @@ use stack_graphs::graph::File; use stack_graphs::graph::StackGraph; pub const EXPORTS_GUARD: &str = "GUARD:EXPORTS"; +pub const DEFAULT_GUARD: &str = "GUARD:DEFAULT"; pub const PKG_GUARD: &str = "GUARD:PKG"; pub const PKG_INTERNAL_GUARD: &str = "GUARD:PKG_INTERNAL"; diff --git a/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg b/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg index 87ae4e8db..0d2a73c18 100644 --- a/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg +++ b/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg @@ -346,7 +346,6 @@ inherit .return_or_yield attr (@prog.after_scope) empty_source_span attr (prog_exports_pop) pop_symbol = "GUARD:EXPORTS" - attr (prog_exports_pop) definiens_node = @prog edge module_pop_end -> prog_exports_pop edge prog_exports_pop -> @prog.exports From 188090be872b76b4ced85cce69f3f94bbe4544b7 Mon Sep 17 00:00:00 2001 From: Rebecca Valentine Date: Tue, 14 Nov 2023 13:40:55 -0800 Subject: [PATCH 045/291] Add default pop Co-Authored-By: Rob Rix --- .../tree-sitter-stack-graphs-javascript/rust/npm_package.rs | 2 +- .../tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/languages/tree-sitter-stack-graphs-javascript/rust/npm_package.rs b/languages/tree-sitter-stack-graphs-javascript/rust/npm_package.rs index 3ce18aa07..01095022e 100644 --- a/languages/tree-sitter-stack-graphs-javascript/rust/npm_package.rs +++ b/languages/tree-sitter-stack-graphs-javascript/rust/npm_package.rs @@ -129,7 +129,7 @@ impl FileAnalyzer for NpmPackageAnalyzer { add_push(graph, file, main_push, EXPORTS_GUARD, "exports_guard_push"); add_edge(graph, exports_guard_pop, exports_guard_push, 0); - // reach main exports directly via package name (with precedence) + // reach main default directly via package name (with precedence) // // [pop pkg_name] -1-> [pop "GUARD:DEFAULT"] -> [push "GUARD:DEFAULT"] -> [push main]* -> [push pkg_internal_name] // diff --git a/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg b/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg index 0d2a73c18..61e17ad4e 100644 --- a/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg +++ b/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg @@ -349,6 +349,10 @@ inherit .return_or_yield edge module_pop_end -> prog_exports_pop edge prog_exports_pop -> @prog.exports + node prog_default_pop + attr (prog_default_pop) pop_symbol = "GUARD:DEFAULT", definiens_node = @prog + edge module_pop_end -> prog_default_pop + ;; builtin types node @prog.builtins_number node @prog.builtins_string From e884c34d98156eb35c3ae31864e8ce14fd53997b Mon Sep 17 00:00:00 2001 From: Rebecca Valentine Date: Tue, 14 Nov 2023 14:05:50 -0800 Subject: [PATCH 046/291] Restructure graph? Co-Authored-By: Rob Rix --- .../tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg b/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg index 61e17ad4e..240725978 100644 --- a/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg +++ b/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg @@ -351,7 +351,7 @@ inherit .return_or_yield node prog_default_pop attr (prog_default_pop) pop_symbol = "GUARD:DEFAULT", definiens_node = @prog - edge module_pop_end -> prog_default_pop + edge prog_exports_pop -> prog_default_pop ;; builtin types node @prog.builtins_number From e98eb0da4c2834528d85f71ede09776be50185d9 Mon Sep 17 00:00:00 2001 From: Rebecca Valentine Date: Tue, 14 Nov 2023 14:24:43 -0800 Subject: [PATCH 047/291] Remove unnecessary default?? Co-Authored-By: Rob Rix --- .../rust/npm_package.rs | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/languages/tree-sitter-stack-graphs-javascript/rust/npm_package.rs b/languages/tree-sitter-stack-graphs-javascript/rust/npm_package.rs index 01095022e..236d23f96 100644 --- a/languages/tree-sitter-stack-graphs-javascript/rust/npm_package.rs +++ b/languages/tree-sitter-stack-graphs-javascript/rust/npm_package.rs @@ -128,21 +128,6 @@ impl FileAnalyzer for NpmPackageAnalyzer { let exports_guard_push = add_push(graph, file, main_push, EXPORTS_GUARD, "exports_guard_push"); add_edge(graph, exports_guard_pop, exports_guard_push, 0); - - // reach main default directly via package name (with precedence) - // - // [pop pkg_name] -1-> [pop "GUARD:DEFAULT"] -> [push "GUARD:DEFAULT"] -> [push main]* -> [push pkg_internal_name] - // - let default_guard_pop = add_pop( - graph, - file, - pkg_name_pop, - DEFAULT_GUARD, - "default_guard_pop", - ); - let default_guard_push = - add_push(graph, file, main_push, DEFAULT_GUARD, "default_guard_push"); - add_edge(graph, default_guard_pop, default_guard_push, 0); } // reach dependencies via package internal name From eb6657eb4c53ad085aa555332e22a5c614536083 Mon Sep 17 00:00:00 2001 From: Rebecca Valentine Date: Wed, 15 Nov 2023 10:21:24 -0800 Subject: [PATCH 048/291] Remove no-longer-needed DEFAULT_GUARD var Co-Authored-By: Rob Rix --- languages/tree-sitter-stack-graphs-javascript/rust/util.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/languages/tree-sitter-stack-graphs-javascript/rust/util.rs b/languages/tree-sitter-stack-graphs-javascript/rust/util.rs index 1e0c14019..17ab8d779 100644 --- a/languages/tree-sitter-stack-graphs-javascript/rust/util.rs +++ b/languages/tree-sitter-stack-graphs-javascript/rust/util.rs @@ -15,7 +15,6 @@ use stack_graphs::graph::File; use stack_graphs::graph::StackGraph; pub const EXPORTS_GUARD: &str = "GUARD:EXPORTS"; -pub const DEFAULT_GUARD: &str = "GUARD:DEFAULT"; pub const PKG_GUARD: &str = "GUARD:PKG"; pub const PKG_INTERNAL_GUARD: &str = "GUARD:PKG_INTERNAL"; From fb9dc1c4bdc0cc88f219ccbe73ff658cac12d7ff Mon Sep 17 00:00:00 2001 From: Rebecca Valentine Date: Wed, 15 Nov 2023 11:20:59 -0800 Subject: [PATCH 049/291] More debugging! Co-Authored-By: Rob Rix --- .../tree-sitter-stack-graphs-javascript/rust/npm_package.rs | 6 ++++++ .../src/stack-graphs.tsg | 4 ---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/languages/tree-sitter-stack-graphs-javascript/rust/npm_package.rs b/languages/tree-sitter-stack-graphs-javascript/rust/npm_package.rs index 236d23f96..6adbd753a 100644 --- a/languages/tree-sitter-stack-graphs-javascript/rust/npm_package.rs +++ b/languages/tree-sitter-stack-graphs-javascript/rust/npm_package.rs @@ -128,6 +128,12 @@ impl FileAnalyzer for NpmPackageAnalyzer { let exports_guard_push = add_push(graph, file, main_push, EXPORTS_GUARD, "exports_guard_push"); add_edge(graph, exports_guard_pop, exports_guard_push, 0); + + // reach main directly via package name (with precedence) + // + // [pop pkg_name] -1-> [push main]* -> [push pkg_internal_name] + // + add_edge(graph, pkg_name_pop, main_push, 0); } // reach dependencies via package internal name diff --git a/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg b/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg index 240725978..0d2a73c18 100644 --- a/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg +++ b/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg @@ -349,10 +349,6 @@ inherit .return_or_yield edge module_pop_end -> prog_exports_pop edge prog_exports_pop -> @prog.exports - node prog_default_pop - attr (prog_default_pop) pop_symbol = "GUARD:DEFAULT", definiens_node = @prog - edge prog_exports_pop -> prog_default_pop - ;; builtin types node @prog.builtins_number node @prog.builtins_string From 7d71397c7dd98607349482b5c4b28e1dff25fc8e Mon Sep 17 00:00:00 2001 From: Rebecca Valentine Date: Wed, 15 Nov 2023 11:35:05 -0800 Subject: [PATCH 050/291] Removes unneeded guard exports in package.json Co-Authored-By: Rob Rix --- .../rust/npm_package.rs | 15 --------------- .../rust/util.rs | 1 - 2 files changed, 16 deletions(-) diff --git a/languages/tree-sitter-stack-graphs-javascript/rust/npm_package.rs b/languages/tree-sitter-stack-graphs-javascript/rust/npm_package.rs index 6adbd753a..6c346ccd4 100644 --- a/languages/tree-sitter-stack-graphs-javascript/rust/npm_package.rs +++ b/languages/tree-sitter-stack-graphs-javascript/rust/npm_package.rs @@ -114,21 +114,6 @@ impl FileAnalyzer for NpmPackageAnalyzer { let main_push = add_module_pushes(graph, file, &main, pkg_internal_name_push, "main_push"); - // reach main exports directly via package name (with precedence) - // - // [pop pkg_name] -1-> [pop "GUARD:EXPORTS"] -> [push "GUARD:EXPORTS"] -> [push main]* -> [push pkg_internal_name] - // - let exports_guard_pop = add_pop( - graph, - file, - pkg_name_pop, - EXPORTS_GUARD, - "exports_guard_pop", - ); - let exports_guard_push = - add_push(graph, file, main_push, EXPORTS_GUARD, "exports_guard_push"); - add_edge(graph, exports_guard_pop, exports_guard_push, 0); - // reach main directly via package name (with precedence) // // [pop pkg_name] -1-> [push main]* -> [push pkg_internal_name] diff --git a/languages/tree-sitter-stack-graphs-javascript/rust/util.rs b/languages/tree-sitter-stack-graphs-javascript/rust/util.rs index 17ab8d779..c57c409b3 100644 --- a/languages/tree-sitter-stack-graphs-javascript/rust/util.rs +++ b/languages/tree-sitter-stack-graphs-javascript/rust/util.rs @@ -14,7 +14,6 @@ use stack_graphs::arena::Handle; use stack_graphs::graph::File; use stack_graphs::graph::StackGraph; -pub const EXPORTS_GUARD: &str = "GUARD:EXPORTS"; pub const PKG_GUARD: &str = "GUARD:PKG"; pub const PKG_INTERNAL_GUARD: &str = "GUARD:PKG_INTERNAL"; From 33883c4637b5c75cf49531c61c0569c35886f95e Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Sat, 11 Nov 2023 14:36:57 +0100 Subject: [PATCH 051/291] Remove unused module guard --- .../src/stack-graphs.tsg | 6 ------ 1 file changed, 6 deletions(-) diff --git a/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg b/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg index 640ec0976..e88db9778 100644 --- a/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg +++ b/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg @@ -230,7 +230,6 @@ attribute node_symbol = node => symbol = (source-text node), source_n ;; a connected subgraph that is never the less never traversed. ;; - `GUARD:LABEL` - used for the names of labels ;; - `GUARD:MEMBER` - used for the members/fields/properties of objects -;; - `GUARD:MODULE` - used for the final scope of the module ;; - `GUARD:RETURN` - used for the AST nodes for values returned by a function ;; in the function body ;; - `GUARD:THIS` - used for the implicit `this` argument of a function inside @@ -289,7 +288,6 @@ inherit .return_or_yield (program)@prog { node prog_module_pop - node prog_module_scope node prog_exports_pop node prog_pkg_pop_guard node prog_pkg_push_guard @@ -328,17 +326,13 @@ inherit .return_or_yield edge @prog.before_scope -> @prog.pkg_push edge @prog.before_scope -> @prog.hoist_point - attr (prog_module_scope) pop_symbol = "GUARD:MODULE" edge @prog.pkg_pop -> prog_module_pop_start - edge module_pop_end -> prog_module_scope - edge prog_module_scope -> @prog.after_scope attr (prog_legacy_qname_guard) push_symbol = "GUARD:LEGACY_QNAME" edge @prog.before_scope -> prog_legacy_qname_guard edge prog_legacy_qname_guard -> ROOT_NODE attr (prog_module_pop) empty_source_span - attr (prog_module_scope) empty_source_span attr (@prog.exports) empty_source_span attr (prog_exports_pop) empty_source_span attr (@prog.before_scope) empty_source_span From 80b5d61e270e0176fdcff14089e1afcefd5137ad Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Sat, 11 Nov 2023 17:25:02 +0100 Subject: [PATCH 052/291] Guard module names --- .../rust/npm_package.rs | 18 ++++++++++++------ .../rust/util.rs | 4 ++++ .../src/stack-graphs.tsg | 13 +++++++++++-- .../variable_resolves_to_module.js | 4 ++++ 4 files changed, 31 insertions(+), 8 deletions(-) create mode 100644 languages/tree-sitter-stack-graphs-javascript/test/old/bug_regressions/variable_resolves_to_module.js diff --git a/languages/tree-sitter-stack-graphs-javascript/rust/npm_package.rs b/languages/tree-sitter-stack-graphs-javascript/rust/npm_package.rs index ddaa952cc..e3fb50411 100644 --- a/languages/tree-sitter-stack-graphs-javascript/rust/npm_package.rs +++ b/languages/tree-sitter-stack-graphs-javascript/rust/npm_package.rs @@ -61,7 +61,6 @@ impl FileAnalyzer for NpmPackageAnalyzer { pkg_internal_name, "pkg_internal_name_pop", ); - // reach package internals via root // // [push pkg_internal_name] -> [push "GUARD:PKG_INTERNAL"] -> [root] @@ -83,13 +82,21 @@ impl FileAnalyzer for NpmPackageAnalyzer { // reach exports via package name // - // [root] -> [pop "GUARD:PKG"] -> [pop PKG_NAME]* -> [push PKG_INTERNAL_NAME] -> [push "GUARD:PKG_INTERNAL"] -> [root] + // [root] -> [pop "GUARD:PKG"] -> [pop PKG_NAME]* -> [push "GUARD:MODULE"] -> [push PKG_INTERNAL_NAME] -> [push "GUARD:PKG_INTERNAL"] -> [root] // if !npm_pkg.name.is_empty() { // NOTE Because all modules expose their exports at the top-level, both paths created below are equivalent for // exports of the main module. This means multiple equivalent paths to those exports, which is bad for // performance. At the moment, we have no mechanism to prevent this from happening. + let module_guard = add_push( + graph, + file, + pkg_internal_name_push, + MODULE_GUARD, + "main_guard", + ); + // reach package internals via package name // // [root] -> [pop "GUARD:PKG"] -> [pop pkg_name]* -> [push pkg_internal_name] @@ -102,11 +109,11 @@ impl FileAnalyzer for NpmPackageAnalyzer { pkg_guard_pop, "pkg_name_pop", ); - add_edge(graph, pkg_name_pop, pkg_internal_name_push, 0); + add_edge(graph, pkg_name_pop, module_guard, 0); // reach main exports directly via package name (with precedence) // - // [pop pkg_name] -1-> [pop "GUARD:EXPORTS"] -> [push "GUARD:EXPORTS"] -> [push main]* -> [push pkg_internal_name] + // [pop pkg_name] -1-> [pop "GUARD:EXPORTS"] -> [push "GUARD:EXPORTS"] -> [push main]* -> [push "GUARD:MODULE"] -> [push pkg_internal_name] // let exports_guard_pop = add_pop( graph, @@ -121,8 +128,7 @@ impl FileAnalyzer for NpmPackageAnalyzer { .map(|p| p.into_path_buf()) .unwrap_or(PathBuf::from("index")) .with_extension(""); - let main_push = - add_module_pushes(graph, file, &main, pkg_internal_name_push, "main_push"); + let main_push = add_module_pushes(graph, file, &main, module_guard, "main_push"); let exports_guard_push = add_push(graph, file, main_push, EXPORTS_GUARD, "exports_guard_push"); add_edge(graph, exports_guard_pop, exports_guard_push, 0); diff --git a/languages/tree-sitter-stack-graphs-javascript/rust/util.rs b/languages/tree-sitter-stack-graphs-javascript/rust/util.rs index 17ab8d779..02b094005 100644 --- a/languages/tree-sitter-stack-graphs-javascript/rust/util.rs +++ b/languages/tree-sitter-stack-graphs-javascript/rust/util.rs @@ -15,10 +15,14 @@ use stack_graphs::graph::File; use stack_graphs::graph::StackGraph; pub const EXPORTS_GUARD: &str = "GUARD:EXPORTS"; +pub const MODULE_GUARD: &str = "GUARD:MODULE"; pub const PKG_GUARD: &str = "GUARD:PKG"; pub const PKG_INTERNAL_GUARD: &str = "GUARD:PKG_INTERNAL"; pub fn add_debug_name(graph: &mut StackGraph, node: Handle, name: &str) { + if name.is_empty() { + return; + } let key = graph.add_string("name"); let value = graph.add_string(name); graph.node_debug_info_mut(node).add(key, value); diff --git a/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg b/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg index e88db9778..4ab571db9 100644 --- a/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg +++ b/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg @@ -230,6 +230,7 @@ attribute node_symbol = node => symbol = (source-text node), source_n ;; a connected subgraph that is never the less never traversed. ;; - `GUARD:LABEL` - used for the names of labels ;; - `GUARD:MEMBER` - used for the members/fields/properties of objects +;; - `GUARD:MODULE` - used for module names ;; - `GUARD:RETURN` - used for the AST nodes for values returned by a function ;; in the function body ;; - `GUARD:THIS` - used for the implicit `this` argument of a function inside @@ -307,7 +308,12 @@ inherit .return_or_yield edge @prog.pkg_push -> prog_pkg_push_guard edge prog_pkg_push_guard -> ROOT_NODE + node module_guard_pop + attr (module_guard_pop) pop_symbol = "GUARD:MODULE" + edge @prog.pkg_pop -> module_guard_pop + node prog_module_pop_start + edge module_guard_pop -> prog_module_pop_start var module_pop_end = prog_module_pop_start let module_name = (replace FILE_PATH "\.js$" "") scan module_name { @@ -326,7 +332,6 @@ inherit .return_or_yield edge @prog.before_scope -> @prog.pkg_push edge @prog.before_scope -> @prog.hoist_point - edge @prog.pkg_pop -> prog_module_pop_start attr (prog_legacy_qname_guard) push_symbol = "GUARD:LEGACY_QNAME" edge @prog.before_scope -> prog_legacy_qname_guard @@ -826,7 +831,12 @@ inherit .return_or_yield ; relative import let name = (replace (path-normalize (path-join (path-dir FILE_PATH) $1)) "\.js$" "") + node module_guard_push + attr (module_guard_push) push_symbol = "GUARD:MODULE" + edge module_guard_push -> @source.pkg_push + node source_push_end + edge source_push_end -> module_guard_push var push_start = source_push_end scan name { "([^/]+)/" { @@ -850,7 +860,6 @@ inherit .return_or_yield attr (push_start) is_reference, source_node = @source edge source_push_guard_exports -> push_start - edge source_push_end -> @source.pkg_push } "^[\"']([^\./].*)[\"']$" { ; package import diff --git a/languages/tree-sitter-stack-graphs-javascript/test/old/bug_regressions/variable_resolves_to_module.js b/languages/tree-sitter-stack-graphs-javascript/test/old/bug_regressions/variable_resolves_to_module.js new file mode 100644 index 000000000..e829ab7ff --- /dev/null +++ b/languages/tree-sitter-stack-graphs-javascript/test/old/bug_regressions/variable_resolves_to_module.js @@ -0,0 +1,4 @@ +/* --- path: debug.js --- */ + +/**/ debug(42); +// ^ defined: From 6784290074ea32a71ce964eea10b0b31519fa4a0 Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Mon, 20 Nov 2023 13:29:31 +0100 Subject: [PATCH 053/291] Cleanup TypeScript tests and add two missing cases --- .../modules/import-directly-exported-variable.ts | 4 ++-- .../import-from-ambient-module-declaration.ts | 4 ++-- .../modules/import-from-own-direcotry-in-index.ts | 8 ++++++++ .../import-from-own-project-subdirectory.ts | 4 ++-- .../test/modules/import-from-subdirectory.ts | 4 ++-- .../test/modules/import-from-superdirectory.ts | 4 ++-- .../import-indirectly-exported-variable.ts | 4 ++-- ...ug => import-relative-to-project-root.ts.skip} | 4 ++-- .../test/modules/import-via-index.ts | 4 ++-- .../test/modules/import-via-superdirectory.ts | 4 ++-- .../test/modules/import-via-two-reexports.ts | 15 +++++++++++++++ .../rename-imported-directly-exported-variable.ts | 4 ++-- ...ename-imported-indirectly-exported-variable.ts | 4 ++-- .../package-invisible-without-dependency.ts | 12 ++++++------ .../test/projects/baseurl-to-subdir.ts | 6 +++--- .../test/projects/composite-project.ts | 6 +++--- .../test/projects/declarations-are-ignored.ts | 8 ++++---- .../test/projects/exclude-second-subdir.ts | 8 ++++---- .../test/projects/explicit-root-dir.ts | 6 +++--- .../test/projects/import-from-rootdirs-subdir.ts | 10 +++++----- .../test/projects/import-from-rootdirs.ts | 10 +++++----- .../projects/import-with-own-project-baseurl.ts | 6 +++--- .../test/projects/include-one-subdir.ts | 8 ++++---- .../test/projects/invalid-paths-mappings.ts | 2 +- .../test/projects/js-sources-are-ignored.ts.skip | 8 ++++---- .../projects/js-sources-can-be-allowed.ts.skip | 8 ++++---- .../projects/non-relative-single-module-remap.ts | 6 +++--- .../test/projects/non-relative-star-remap.ts | 6 +++--- .../test/projects/non-relative-star-remaps.ts | 6 +++--- .../test/projects/one-file-in-subdir.ts | 8 ++++---- .../package-dependency-with-nested-source-root.ts | 12 ++++++------ .../test/projects/relative-import.ts | 6 +++--- .../test/projects/relative-single-module-remap.ts | 6 +++--- .../test/projects/relative-star-remap.ts | 6 +++--- .../test/projects/remap-with-baseurl.ts | 6 +++--- .../test/projects/sources-in-multiple-subdirs.ts | 8 ++++---- .../test/projects/sources-in-one-subdir.ts | 6 +++--- 37 files changed, 132 insertions(+), 109 deletions(-) create mode 100644 languages/tree-sitter-stack-graphs-typescript/test/modules/import-from-own-direcotry-in-index.ts rename languages/tree-sitter-stack-graphs-typescript/test/modules/{import-relative-to-project-root.ts.bug => import-relative-to-project-root.ts.skip} (55%) create mode 100644 languages/tree-sitter-stack-graphs-typescript/test/modules/import-via-two-reexports.ts diff --git a/languages/tree-sitter-stack-graphs-typescript/test/modules/import-directly-exported-variable.ts b/languages/tree-sitter-stack-graphs-typescript/test/modules/import-directly-exported-variable.ts index bd6a1126b..91e5da6c7 100644 --- a/languages/tree-sitter-stack-graphs-typescript/test/modules/import-directly-exported-variable.ts +++ b/languages/tree-sitter-stack-graphs-typescript/test/modules/import-directly-exported-variable.ts @@ -1,10 +1,10 @@ -/*--- path: ./ModA.ts ---*/ +/*--- path: ModA.ts ---*/ export let a = { v: 42 }; -/*--- path: ./ModB.ts ---*/ +/*--- path: ModB.ts ---*/ import { a } from "./ModA"; // ^ defined: 3 diff --git a/languages/tree-sitter-stack-graphs-typescript/test/modules/import-from-ambient-module-declaration.ts b/languages/tree-sitter-stack-graphs-typescript/test/modules/import-from-ambient-module-declaration.ts index 75aff4238..abef61e5e 100644 --- a/languages/tree-sitter-stack-graphs-typescript/test/modules/import-from-ambient-module-declaration.ts +++ b/languages/tree-sitter-stack-graphs-typescript/test/modules/import-from-ambient-module-declaration.ts @@ -1,8 +1,8 @@ -/* --- path: ./index.ts --- */ +/* --- path: index.ts --- */ import { foo } from "@my/lib"; // ^ defined: 8 -/* --- path: ./mod.ts --- */ +/* --- path: mod.ts --- */ declare module "@my/lib" { export const foo = 42; diff --git a/languages/tree-sitter-stack-graphs-typescript/test/modules/import-from-own-direcotry-in-index.ts b/languages/tree-sitter-stack-graphs-typescript/test/modules/import-from-own-direcotry-in-index.ts new file mode 100644 index 000000000..b62a2fe45 --- /dev/null +++ b/languages/tree-sitter-stack-graphs-typescript/test/modules/import-from-own-direcotry-in-index.ts @@ -0,0 +1,8 @@ +/*--- path: foo/index.ts ---*/ + +import { FOO } from "./bar"; +// ^ defined: 8 + +/*--- path: foo/bar.ts ---*/ + +export const FOO = 42; diff --git a/languages/tree-sitter-stack-graphs-typescript/test/modules/import-from-own-project-subdirectory.ts b/languages/tree-sitter-stack-graphs-typescript/test/modules/import-from-own-project-subdirectory.ts index 2d399830c..743efa90a 100644 --- a/languages/tree-sitter-stack-graphs-typescript/test/modules/import-from-own-project-subdirectory.ts +++ b/languages/tree-sitter-stack-graphs-typescript/test/modules/import-from-own-project-subdirectory.ts @@ -1,11 +1,11 @@ -/*--- path: ./A/ModA.ts ---*/ +/*--- path: A/ModA.ts ---*/ /*--- global: PROJECT_NAME=foo/bar ---*/ export let a = { v: 42 }; -/*--- path: ./ModB.ts ---*/ +/*--- path: ModB.ts ---*/ /*--- global: PROJECT_NAME=foo/bar ---*/ import { a } from "./A/ModA"; diff --git a/languages/tree-sitter-stack-graphs-typescript/test/modules/import-from-subdirectory.ts b/languages/tree-sitter-stack-graphs-typescript/test/modules/import-from-subdirectory.ts index 19e6c03b2..11292dff4 100644 --- a/languages/tree-sitter-stack-graphs-typescript/test/modules/import-from-subdirectory.ts +++ b/languages/tree-sitter-stack-graphs-typescript/test/modules/import-from-subdirectory.ts @@ -1,10 +1,10 @@ -/*--- path: ./A/ModA.ts ---*/ +/*--- path: A/ModA.ts ---*/ export let a = { v: 42 }; -/*--- path: ./ModB.ts ---*/ +/*--- path: ModB.ts ---*/ import { a } from "./A/ModA"; // ^ defined: 3 diff --git a/languages/tree-sitter-stack-graphs-typescript/test/modules/import-from-superdirectory.ts b/languages/tree-sitter-stack-graphs-typescript/test/modules/import-from-superdirectory.ts index bf7865e08..8f728d91c 100644 --- a/languages/tree-sitter-stack-graphs-typescript/test/modules/import-from-superdirectory.ts +++ b/languages/tree-sitter-stack-graphs-typescript/test/modules/import-from-superdirectory.ts @@ -1,10 +1,10 @@ -/*--- path: ./ModA.ts ---*/ +/*--- path: ModA.ts ---*/ export let a = { v: 42 }; -/*--- path: ./B/ModB.ts ---*/ +/*--- path: B/ModB.ts ---*/ import { a } from "../ModA"; // ^ defined: 3 diff --git a/languages/tree-sitter-stack-graphs-typescript/test/modules/import-indirectly-exported-variable.ts b/languages/tree-sitter-stack-graphs-typescript/test/modules/import-indirectly-exported-variable.ts index 3676bf85c..e2906595e 100644 --- a/languages/tree-sitter-stack-graphs-typescript/test/modules/import-indirectly-exported-variable.ts +++ b/languages/tree-sitter-stack-graphs-typescript/test/modules/import-indirectly-exported-variable.ts @@ -1,4 +1,4 @@ -/*--- path: ./ModA.ts ---*/ +/*--- path: ModA.ts ---*/ let a = { v: 42 @@ -7,7 +7,7 @@ let a = { export { a }; // ^ defined: 3 -/*--- path: ./ModB.ts ---*/ +/*--- path: ModB.ts ---*/ import { a } from "./ModA"; // ^ defined: 7, 3 diff --git a/languages/tree-sitter-stack-graphs-typescript/test/modules/import-relative-to-project-root.ts.bug b/languages/tree-sitter-stack-graphs-typescript/test/modules/import-relative-to-project-root.ts.skip similarity index 55% rename from languages/tree-sitter-stack-graphs-typescript/test/modules/import-relative-to-project-root.ts.bug rename to languages/tree-sitter-stack-graphs-typescript/test/modules/import-relative-to-project-root.ts.skip index c3a2589da..eed68b4ee 100644 --- a/languages/tree-sitter-stack-graphs-typescript/test/modules/import-relative-to-project-root.ts.bug +++ b/languages/tree-sitter-stack-graphs-typescript/test/modules/import-relative-to-project-root.ts.skip @@ -1,8 +1,8 @@ -/*--- path: ./ModA.ts ---*/ +/*--- path: ModA.ts ---*/ export const a = 42; -/*--- path: ./B/ModB.ts ---*/ +/*--- path: B/ModB.ts ---*/ import { a } from "./ModA"; // ^ defined: \ No newline at end of file diff --git a/languages/tree-sitter-stack-graphs-typescript/test/modules/import-via-index.ts b/languages/tree-sitter-stack-graphs-typescript/test/modules/import-via-index.ts index ef0504741..3730b1b73 100644 --- a/languages/tree-sitter-stack-graphs-typescript/test/modules/import-via-index.ts +++ b/languages/tree-sitter-stack-graphs-typescript/test/modules/import-via-index.ts @@ -1,10 +1,10 @@ -/*--- path: ./A/index.ts ---*/ +/*--- path: A/index.ts ---*/ export let a = { v: 42 }; -/*--- path: ./B/ModB.ts ---*/ +/*--- path: B/ModB.ts ---*/ import { a } from "../A"; // ^ defined: 3 diff --git a/languages/tree-sitter-stack-graphs-typescript/test/modules/import-via-superdirectory.ts b/languages/tree-sitter-stack-graphs-typescript/test/modules/import-via-superdirectory.ts index 332d6474d..a67e79311 100644 --- a/languages/tree-sitter-stack-graphs-typescript/test/modules/import-via-superdirectory.ts +++ b/languages/tree-sitter-stack-graphs-typescript/test/modules/import-via-superdirectory.ts @@ -1,10 +1,10 @@ -/*--- path: ./A/ModA.ts ---*/ +/*--- path: A/ModA.ts ---*/ export let a = { v: 42 }; -/*--- path: ./B/ModB.ts ---*/ +/*--- path: B/ModB.ts ---*/ import { a } from "../A/ModA"; // ^ defined: 3 diff --git a/languages/tree-sitter-stack-graphs-typescript/test/modules/import-via-two-reexports.ts b/languages/tree-sitter-stack-graphs-typescript/test/modules/import-via-two-reexports.ts new file mode 100644 index 000000000..981e3d15d --- /dev/null +++ b/languages/tree-sitter-stack-graphs-typescript/test/modules/import-via-two-reexports.ts @@ -0,0 +1,15 @@ +/* --- path: src/foo/index.ts --- */ + +export * from "../bar"; + +/* --- path: src/bar/index.ts --- */ + +export * from "./quz"; + +/* --- path: src/bar/quz.ts --- */ + +export const QUZ = 42; + +/* --- path: src/test.ts --- */ + +import { QUZ } from "./foo"; diff --git a/languages/tree-sitter-stack-graphs-typescript/test/modules/rename-imported-directly-exported-variable.ts b/languages/tree-sitter-stack-graphs-typescript/test/modules/rename-imported-directly-exported-variable.ts index f3b403494..ecfdc2d2c 100644 --- a/languages/tree-sitter-stack-graphs-typescript/test/modules/rename-imported-directly-exported-variable.ts +++ b/languages/tree-sitter-stack-graphs-typescript/test/modules/rename-imported-directly-exported-variable.ts @@ -1,10 +1,10 @@ -/*--- path: ./ModA.ts ---*/ +/*--- path: ModA.ts ---*/ export let a = { v: 42 }; -/*--- path: ./ModB.ts ---*/ +/*--- path: ModB.ts ---*/ import { a as b } from "./ModA"; // ^ defined: 3 diff --git a/languages/tree-sitter-stack-graphs-typescript/test/modules/rename-imported-indirectly-exported-variable.ts b/languages/tree-sitter-stack-graphs-typescript/test/modules/rename-imported-indirectly-exported-variable.ts index 48f62099e..e1e99db34 100644 --- a/languages/tree-sitter-stack-graphs-typescript/test/modules/rename-imported-indirectly-exported-variable.ts +++ b/languages/tree-sitter-stack-graphs-typescript/test/modules/rename-imported-indirectly-exported-variable.ts @@ -1,4 +1,4 @@ -/*--- path: ./ModA.ts ---*/ +/*--- path: ModA.ts ---*/ let a = { v: 42 @@ -7,7 +7,7 @@ let a = { export { a }; // ^ defined: 3 -/*--- path: ./ModB.ts ---*/ +/*--- path: ModB.ts ---*/ import { a as b } from "./ModA"; // ^ defined: 7, 3 diff --git a/languages/tree-sitter-stack-graphs-typescript/test/packages/package-invisible-without-dependency.ts b/languages/tree-sitter-stack-graphs-typescript/test/packages/package-invisible-without-dependency.ts index c7c0458ff..87248abe2 100644 --- a/languages/tree-sitter-stack-graphs-typescript/test/packages/package-invisible-without-dependency.ts +++ b/languages/tree-sitter-stack-graphs-typescript/test/packages/package-invisible-without-dependency.ts @@ -1,4 +1,4 @@ -/* --- path: ./my_lib/package.json --- */ +/* --- path: my_lib/package.json --- */ /* --- global: FILE_PATH=package.json --- */ /* --- global: PROJECT_NAME=my_lib --- */ @@ -6,19 +6,19 @@ "name": "@my/lib" } -/* --- path: ./my_lib/tsconfig.json --- */ +/* --- path: my_lib/tsconfig.json --- */ /* --- global: FILE_PATH=tsconfig.json --- */ /* --- global: PROJECT_NAME=my_lib --- */ {} -/* --- path: ./my_lib/src/foo.ts --- */ +/* --- path: my_lib/src/foo.ts --- */ /* --- global: FILE_PATH=src/foo.ts --- */ /* --- global: PROJECT_NAME=my_lib --- */ export const bar = 42; -/* --- path: ./my_app/package.json --- */ +/* --- path: my_app/package.json --- */ /* --- global: FILE_PATH=package.json --- */ /* --- global: PROJECT_NAME=my_app --- */ @@ -26,13 +26,13 @@ export const bar = 42; "name": "@my/app" } -/* --- path: ./my_app/tsconfig.json --- */ +/* --- path: my_app/tsconfig.json --- */ /* --- global: FILE_PATH=tsconfig.json --- */ /* --- global: PROJECT_NAME=my_app --- */ {} -/* --- path: ./my_app/src/index.ts --- */ +/* --- path: my_app/src/index.ts --- */ /* --- global: FILE_PATH=src/index.ts --- */ /* --- global: PROJECT_NAME=my_app --- */ diff --git a/languages/tree-sitter-stack-graphs-typescript/test/projects/baseurl-to-subdir.ts b/languages/tree-sitter-stack-graphs-typescript/test/projects/baseurl-to-subdir.ts index a867d24b1..80f5a00d4 100644 --- a/languages/tree-sitter-stack-graphs-typescript/test/projects/baseurl-to-subdir.ts +++ b/languages/tree-sitter-stack-graphs-typescript/test/projects/baseurl-to-subdir.ts @@ -1,4 +1,4 @@ -/* --- path: ./tsconfig.json --- */ +/* --- path: tsconfig.json --- */ { "compilerOptions": { "composite": true, @@ -6,9 +6,9 @@ } } -/* --- path: ./src/foo.ts --- */ +/* --- path: src/foo.ts --- */ export const bar = 42; -/* --- path: ./src/index.ts --- */ +/* --- path: src/index.ts --- */ import { bar } from "foo"; // ^ defined: 10 diff --git a/languages/tree-sitter-stack-graphs-typescript/test/projects/composite-project.ts b/languages/tree-sitter-stack-graphs-typescript/test/projects/composite-project.ts index 732536dbf..e7fd1b57d 100644 --- a/languages/tree-sitter-stack-graphs-typescript/test/projects/composite-project.ts +++ b/languages/tree-sitter-stack-graphs-typescript/test/projects/composite-project.ts @@ -1,13 +1,13 @@ -/* --- path: ./tsconfig.json --- */ +/* --- path: tsconfig.json --- */ { "compilerOptions": { "composite": true, } } -/* --- path: ./src/foo.ts --- */ +/* --- path: src/foo.ts --- */ export const bar = 42; -/* --- path: ./src/index.ts --- */ +/* --- path: src/index.ts --- */ import { bar } from "./foo"; // ^ defined: 9 diff --git a/languages/tree-sitter-stack-graphs-typescript/test/projects/declarations-are-ignored.ts b/languages/tree-sitter-stack-graphs-typescript/test/projects/declarations-are-ignored.ts index 27cf54427..8c3d2a6b5 100644 --- a/languages/tree-sitter-stack-graphs-typescript/test/projects/declarations-are-ignored.ts +++ b/languages/tree-sitter-stack-graphs-typescript/test/projects/declarations-are-ignored.ts @@ -1,12 +1,12 @@ -/* --- path: ./tsconfig.json --- */ +/* --- path: tsconfig.json --- */ { } -/* --- path: ./src/foo.ts --- */ +/* --- path: src/foo.ts --- */ export const bar = 42; -/* --- path: ./src/index.ts --- */ +/* --- path: src/index.ts --- */ import { bar } from "./foo"; // ^ defined: 6 -/* --- path: ./types/index.d.ts --- */ +/* --- path: types/index.d.ts --- */ diff --git a/languages/tree-sitter-stack-graphs-typescript/test/projects/exclude-second-subdir.ts b/languages/tree-sitter-stack-graphs-typescript/test/projects/exclude-second-subdir.ts index 083f0293b..326557cc7 100644 --- a/languages/tree-sitter-stack-graphs-typescript/test/projects/exclude-second-subdir.ts +++ b/languages/tree-sitter-stack-graphs-typescript/test/projects/exclude-second-subdir.ts @@ -1,13 +1,13 @@ -/* --- path: ./tsconfig.json --- */ +/* --- path: tsconfig.json --- */ { "exclude": ["test/**/*"] } -/* --- path: ./src/foo.ts --- */ +/* --- path: src/foo.ts --- */ export const bar = 42; -/* --- path: ./src/index.ts --- */ +/* --- path: src/index.ts --- */ import { bar } from "./foo"; // ^ defined: 7 -/* --- path: ./test/index.ts --- */ +/* --- path: test/index.ts --- */ diff --git a/languages/tree-sitter-stack-graphs-typescript/test/projects/explicit-root-dir.ts b/languages/tree-sitter-stack-graphs-typescript/test/projects/explicit-root-dir.ts index c51108621..37dbade29 100644 --- a/languages/tree-sitter-stack-graphs-typescript/test/projects/explicit-root-dir.ts +++ b/languages/tree-sitter-stack-graphs-typescript/test/projects/explicit-root-dir.ts @@ -1,13 +1,13 @@ -/* --- path: ./tsconfig.json --- */ +/* --- path: tsconfig.json --- */ { "compilerOptions": { "rootDir": "." } } -/* --- path: ./core/foo.ts --- */ +/* --- path: core/foo.ts --- */ export const bar = 42; -/* --- path: ./core/index.ts --- */ +/* --- path: core/index.ts --- */ import { bar } from "./foo"; // ^ defined: 9 diff --git a/languages/tree-sitter-stack-graphs-typescript/test/projects/import-from-rootdirs-subdir.ts b/languages/tree-sitter-stack-graphs-typescript/test/projects/import-from-rootdirs-subdir.ts index 6c8715cec..5c9d2c896 100644 --- a/languages/tree-sitter-stack-graphs-typescript/test/projects/import-from-rootdirs-subdir.ts +++ b/languages/tree-sitter-stack-graphs-typescript/test/projects/import-from-rootdirs-subdir.ts @@ -1,4 +1,4 @@ -/* --- path: ./tsconfig.json --- */ +/* --- path: tsconfig.json --- */ { "compilerOptions": { "rootDirs": [ @@ -8,13 +8,13 @@ } } -/* --- path: ./src/core/index.ts --- */ +/* --- path: src/core/index.ts --- */ import { bar } from "./foo/baz"; // ^ defined: 16 -/* --- path: ./src/util/foo/baz.ts --- */ +/* --- path: src/util/foo/baz.ts --- */ export const bar = 42; -/* --- path: ./src/util/index.ts --- */ +/* --- path: src/util/index.ts --- */ -/* --- path: ./src/index.ts --- */ +/* --- path: src/index.ts --- */ diff --git a/languages/tree-sitter-stack-graphs-typescript/test/projects/import-from-rootdirs.ts b/languages/tree-sitter-stack-graphs-typescript/test/projects/import-from-rootdirs.ts index d7720e510..7603a8f91 100644 --- a/languages/tree-sitter-stack-graphs-typescript/test/projects/import-from-rootdirs.ts +++ b/languages/tree-sitter-stack-graphs-typescript/test/projects/import-from-rootdirs.ts @@ -1,4 +1,4 @@ -/* --- path: ./tsconfig.json --- */ +/* --- path: tsconfig.json --- */ { "compilerOptions": { "rootDirs": [ @@ -8,13 +8,13 @@ } } -/* --- path: ./src/core/index.ts --- */ +/* --- path: src/core/index.ts --- */ import { bar } from "./foo"; // ^ defined: 16 -/* --- path: ./src/util/foo.ts --- */ +/* --- path: src/util/foo.ts --- */ export const bar = 42; -/* --- path: ./src/util/index.ts --- */ +/* --- path: src/util/index.ts --- */ -/* --- path: ./src/index.ts --- */ +/* --- path: src/index.ts --- */ diff --git a/languages/tree-sitter-stack-graphs-typescript/test/projects/import-with-own-project-baseurl.ts b/languages/tree-sitter-stack-graphs-typescript/test/projects/import-with-own-project-baseurl.ts index 29c2892d8..1e40e23db 100644 --- a/languages/tree-sitter-stack-graphs-typescript/test/projects/import-with-own-project-baseurl.ts +++ b/languages/tree-sitter-stack-graphs-typescript/test/projects/import-with-own-project-baseurl.ts @@ -1,4 +1,4 @@ -/* --- path: ./tsconfig.json --- */ +/* --- path: tsconfig.json --- */ /* --- global: PROJECT_NAME=a --- */ { "compilerOptions": { @@ -7,11 +7,11 @@ } } -/* --- path: ./src/foo.ts --- */ +/* --- path: src/foo.ts --- */ /* --- global: PROJECT_NAME=a --- */ export const bar = 42; -/* --- path: ./src/index.ts --- */ +/* --- path: src/index.ts --- */ /* --- global: PROJECT_NAME=a --- */ import { bar } from "foo"; // ^ defined: 12 diff --git a/languages/tree-sitter-stack-graphs-typescript/test/projects/include-one-subdir.ts b/languages/tree-sitter-stack-graphs-typescript/test/projects/include-one-subdir.ts index 42f991b95..8aed79105 100644 --- a/languages/tree-sitter-stack-graphs-typescript/test/projects/include-one-subdir.ts +++ b/languages/tree-sitter-stack-graphs-typescript/test/projects/include-one-subdir.ts @@ -1,13 +1,13 @@ -/* --- path: ./tsconfig.json --- */ +/* --- path: tsconfig.json --- */ { "include": ["src/**/*"] } -/* --- path: ./src/foo.ts --- */ +/* --- path: src/foo.ts --- */ export const bar = 42; -/* --- path: ./src/index.ts --- */ +/* --- path: src/index.ts --- */ import { bar } from "./foo"; // ^ defined: 7 -/* --- path: ./test/index.ts --- */ +/* --- path: test/index.ts --- */ diff --git a/languages/tree-sitter-stack-graphs-typescript/test/projects/invalid-paths-mappings.ts b/languages/tree-sitter-stack-graphs-typescript/test/projects/invalid-paths-mappings.ts index 6e422f928..926f28e97 100644 --- a/languages/tree-sitter-stack-graphs-typescript/test/projects/invalid-paths-mappings.ts +++ b/languages/tree-sitter-stack-graphs-typescript/test/projects/invalid-paths-mappings.ts @@ -1,4 +1,4 @@ -/* --- path: ./tsconfig.json --- */ +/* --- path: tsconfig.json --- */ { "compilerOptions": { "paths": { diff --git a/languages/tree-sitter-stack-graphs-typescript/test/projects/js-sources-are-ignored.ts.skip b/languages/tree-sitter-stack-graphs-typescript/test/projects/js-sources-are-ignored.ts.skip index 3ad81a358..21f8fae85 100644 --- a/languages/tree-sitter-stack-graphs-typescript/test/projects/js-sources-are-ignored.ts.skip +++ b/languages/tree-sitter-stack-graphs-typescript/test/projects/js-sources-are-ignored.ts.skip @@ -1,12 +1,12 @@ -/* --- path: ./tsconfig.json --- */ +/* --- path: tsconfig.json --- */ { } -/* --- path: ./lib/index.js --- */ +/* --- path: lib/index.js --- */ -/* --- path: ./src/foo.ts --- */ +/* --- path: src/foo.ts --- */ export const bar = 42; -/* --- path: ./src/index.ts --- */ +/* --- path: src/index.ts --- */ import { bar } from "./foo"; // ^ defined: 8 diff --git a/languages/tree-sitter-stack-graphs-typescript/test/projects/js-sources-can-be-allowed.ts.skip b/languages/tree-sitter-stack-graphs-typescript/test/projects/js-sources-can-be-allowed.ts.skip index 1b4bd4619..ed304a1b9 100644 --- a/languages/tree-sitter-stack-graphs-typescript/test/projects/js-sources-can-be-allowed.ts.skip +++ b/languages/tree-sitter-stack-graphs-typescript/test/projects/js-sources-can-be-allowed.ts.skip @@ -1,15 +1,15 @@ -/* --- path: ./tsconfig.json --- */ +/* --- path: tsconfig.json --- */ { "compilerOptions": { "allowJs": true } } -/* --- path: ./lib/index.js --- */ +/* --- path: lib/index.js --- */ -/* --- path: ./src/foo.ts --- */ +/* --- path: src/foo.ts --- */ export const bar = 42; -/* --- path: ./src/index.ts --- */ +/* --- path: src/index.ts --- */ import { bar } from "./foo"; // ^ defined: 11 diff --git a/languages/tree-sitter-stack-graphs-typescript/test/projects/non-relative-single-module-remap.ts b/languages/tree-sitter-stack-graphs-typescript/test/projects/non-relative-single-module-remap.ts index c948f40ee..64036fc0d 100644 --- a/languages/tree-sitter-stack-graphs-typescript/test/projects/non-relative-single-module-remap.ts +++ b/languages/tree-sitter-stack-graphs-typescript/test/projects/non-relative-single-module-remap.ts @@ -1,4 +1,4 @@ -/* --- path: ./tsconfig.json --- */ +/* --- path: tsconfig.json --- */ { "compilerOptions": { "composite": true, @@ -9,9 +9,9 @@ } } -/* --- path: ./lib/the_foo.ts --- */ +/* --- path: lib/the_foo.ts --- */ export const bar = 42; -/* --- path: ./src/index.ts --- */ +/* --- path: src/index.ts --- */ import { bar } from "foo"; // ^ defined: 13 diff --git a/languages/tree-sitter-stack-graphs-typescript/test/projects/non-relative-star-remap.ts b/languages/tree-sitter-stack-graphs-typescript/test/projects/non-relative-star-remap.ts index 2d0c07608..5961eeffa 100644 --- a/languages/tree-sitter-stack-graphs-typescript/test/projects/non-relative-star-remap.ts +++ b/languages/tree-sitter-stack-graphs-typescript/test/projects/non-relative-star-remap.ts @@ -1,4 +1,4 @@ -/* --- path: ./tsconfig.json --- */ +/* --- path: tsconfig.json --- */ { "compilerOptions": { "composite": true, @@ -9,9 +9,9 @@ } } -/* --- path: ./lib/foo.ts --- */ +/* --- path: lib/foo.ts --- */ export const bar = 42; -/* --- path: ./src/index.ts --- */ +/* --- path: src/index.ts --- */ import { bar } from "foo"; // ^ defined: 13 diff --git a/languages/tree-sitter-stack-graphs-typescript/test/projects/non-relative-star-remaps.ts b/languages/tree-sitter-stack-graphs-typescript/test/projects/non-relative-star-remaps.ts index f8146cf96..6d3e26955 100644 --- a/languages/tree-sitter-stack-graphs-typescript/test/projects/non-relative-star-remaps.ts +++ b/languages/tree-sitter-stack-graphs-typescript/test/projects/non-relative-star-remaps.ts @@ -1,4 +1,4 @@ -/* --- path: ./tsconfig.json --- */ +/* --- path: tsconfig.json --- */ { "compilerOptions": { "composite": true, @@ -9,9 +9,9 @@ } } -/* --- path: ./ext/foo.ts --- */ +/* --- path: ext/foo.ts --- */ export const bar = 42; -/* --- path: ./src/index.ts --- */ +/* --- path: src/index.ts --- */ import { bar } from "foo"; // ^ defined: 13 diff --git a/languages/tree-sitter-stack-graphs-typescript/test/projects/one-file-in-subdir.ts b/languages/tree-sitter-stack-graphs-typescript/test/projects/one-file-in-subdir.ts index 5b399e022..9aed45a39 100644 --- a/languages/tree-sitter-stack-graphs-typescript/test/projects/one-file-in-subdir.ts +++ b/languages/tree-sitter-stack-graphs-typescript/test/projects/one-file-in-subdir.ts @@ -1,13 +1,13 @@ -/* --- path: ./tsconfig.json --- */ +/* --- path: tsconfig.json --- */ { "files": ["src/index.ts"] } -/* --- path: ./src/foo.ts --- */ +/* --- path: src/foo.ts --- */ export const bar = 42; -/* --- path: ./src/index.ts --- */ +/* --- path: src/index.ts --- */ import { bar } from "./foo"; // ^ defined: 7 -/* --- path: ./test/index.ts --- */ +/* --- path: test/index.ts --- */ diff --git a/languages/tree-sitter-stack-graphs-typescript/test/projects/package-dependency-with-nested-source-root.ts b/languages/tree-sitter-stack-graphs-typescript/test/projects/package-dependency-with-nested-source-root.ts index f8b2a69fb..16215e31c 100644 --- a/languages/tree-sitter-stack-graphs-typescript/test/projects/package-dependency-with-nested-source-root.ts +++ b/languages/tree-sitter-stack-graphs-typescript/test/projects/package-dependency-with-nested-source-root.ts @@ -1,22 +1,22 @@ -/* --- path: ./my_lib/package.json --- */ +/* --- path: my_lib/package.json --- */ /* --- global: FILE_PATH=package.json --- */ /* --- global: PROJECT_NAME=my_lib --- */ { "name": "@my/lib" } -/* --- path: ./my_lib/tsconfig.json --- */ +/* --- path: my_lib/tsconfig.json --- */ /* --- global: FILE_PATH=tsconfig.json --- */ /* --- global: PROJECT_NAME=my_lib --- */ { } -/* --- path: ./my_lib/src/foo.ts --- */ +/* --- path: my_lib/src/foo.ts --- */ /* --- global: FILE_PATH=src/foo.ts --- */ /* --- global: PROJECT_NAME=my_lib --- */ export const bar = 42; -/* --- path: ./my_app/package.json --- */ +/* --- path: my_app/package.json --- */ /* --- global: FILE_PATH=package.json --- */ /* --- global: PROJECT_NAME=my_app --- */ { @@ -26,13 +26,13 @@ export const bar = 42; } } -/* --- path: ./my_app/tsconfig.json --- */ +/* --- path: my_app/tsconfig.json --- */ /* --- global: FILE_PATH=tsconfig.json --- */ /* --- global: PROJECT_NAME=my_app --- */ { } -/* --- path: ./my_app/src/index.ts --- */ +/* --- path: my_app/src/index.ts --- */ /* --- global: FILE_PATH=src/index.ts --- */ /* --- global: PROJECT_NAME=my_app --- */ import { bar } from "@my/lib/foo"; diff --git a/languages/tree-sitter-stack-graphs-typescript/test/projects/relative-import.ts b/languages/tree-sitter-stack-graphs-typescript/test/projects/relative-import.ts index 1c2670335..e1951f3af 100644 --- a/languages/tree-sitter-stack-graphs-typescript/test/projects/relative-import.ts +++ b/languages/tree-sitter-stack-graphs-typescript/test/projects/relative-import.ts @@ -1,13 +1,13 @@ -/* --- path: ./tsconfig.json --- */ +/* --- path: tsconfig.json --- */ { "compilerOptions": { "composite": true } } -/* --- path: ./src/foo.ts --- */ +/* --- path: src/foo.ts --- */ export const bar = 42; -/* --- path: ./src/index.ts --- */ +/* --- path: src/index.ts --- */ import { bar } from "./foo"; // ^ defined: 9 diff --git a/languages/tree-sitter-stack-graphs-typescript/test/projects/relative-single-module-remap.ts b/languages/tree-sitter-stack-graphs-typescript/test/projects/relative-single-module-remap.ts index 03ee84488..71b6ce834 100644 --- a/languages/tree-sitter-stack-graphs-typescript/test/projects/relative-single-module-remap.ts +++ b/languages/tree-sitter-stack-graphs-typescript/test/projects/relative-single-module-remap.ts @@ -1,4 +1,4 @@ -/* --- path: ./tsconfig.json --- */ +/* --- path: tsconfig.json --- */ { "compilerOptions": { "composite": true, @@ -8,9 +8,9 @@ } } -/* --- path: ./lib/the_foo.ts --- */ +/* --- path: lib/the_foo.ts --- */ export const bar = 42; -/* --- path: ./src/index.ts --- */ +/* --- path: src/index.ts --- */ import { bar } from "foo"; // ^ defined: 12 diff --git a/languages/tree-sitter-stack-graphs-typescript/test/projects/relative-star-remap.ts b/languages/tree-sitter-stack-graphs-typescript/test/projects/relative-star-remap.ts index a116cfbbd..4a169c524 100644 --- a/languages/tree-sitter-stack-graphs-typescript/test/projects/relative-star-remap.ts +++ b/languages/tree-sitter-stack-graphs-typescript/test/projects/relative-star-remap.ts @@ -1,4 +1,4 @@ -/* --- path: ./tsconfig.json --- */ +/* --- path: tsconfig.json --- */ { "compilerOptions": { "composite": true, @@ -8,9 +8,9 @@ } } -/* --- path: ./lib/foo.ts --- */ +/* --- path: lib/foo.ts --- */ export const bar = 42; -/* --- path: ./src/index.ts --- */ +/* --- path: src/index.ts --- */ import { bar } from "foo"; // ^ defined: 12 diff --git a/languages/tree-sitter-stack-graphs-typescript/test/projects/remap-with-baseurl.ts b/languages/tree-sitter-stack-graphs-typescript/test/projects/remap-with-baseurl.ts index 53d47534d..cf2788ba0 100644 --- a/languages/tree-sitter-stack-graphs-typescript/test/projects/remap-with-baseurl.ts +++ b/languages/tree-sitter-stack-graphs-typescript/test/projects/remap-with-baseurl.ts @@ -1,4 +1,4 @@ -/* --- path: ./tsconfig.json --- */ +/* --- path: tsconfig.json --- */ { "compilerOptions": { "composite": true, @@ -9,9 +9,9 @@ } } -/* --- path: ./src/util_impl/foo.ts --- */ +/* --- path: src/util_impl/foo.ts --- */ export const bar = 42; -/* --- path: ./src/index.ts --- */ +/* --- path: src/index.ts --- */ import { bar } from "util/foo"; // ^ defined: 13 diff --git a/languages/tree-sitter-stack-graphs-typescript/test/projects/sources-in-multiple-subdirs.ts b/languages/tree-sitter-stack-graphs-typescript/test/projects/sources-in-multiple-subdirs.ts index e4248c32f..76d763b33 100644 --- a/languages/tree-sitter-stack-graphs-typescript/test/projects/sources-in-multiple-subdirs.ts +++ b/languages/tree-sitter-stack-graphs-typescript/test/projects/sources-in-multiple-subdirs.ts @@ -1,12 +1,12 @@ -/* --- path: ./tsconfig.json --- */ +/* --- path: tsconfig.json --- */ { } -/* --- path: ./src/foo.ts --- */ +/* --- path: src/foo.ts --- */ export const bar = 42; -/* --- path: ./src/index.ts --- */ +/* --- path: src/index.ts --- */ import { bar } from "./foo"; // ^ defined: 6 -/* --- path: ./test/index.ts --- */ +/* --- path: test/index.ts --- */ diff --git a/languages/tree-sitter-stack-graphs-typescript/test/projects/sources-in-one-subdir.ts b/languages/tree-sitter-stack-graphs-typescript/test/projects/sources-in-one-subdir.ts index e97ea71ed..24a9a62ca 100644 --- a/languages/tree-sitter-stack-graphs-typescript/test/projects/sources-in-one-subdir.ts +++ b/languages/tree-sitter-stack-graphs-typescript/test/projects/sources-in-one-subdir.ts @@ -1,10 +1,10 @@ -/* --- path: ./tsconfig.json --- */ +/* --- path: tsconfig.json --- */ { } -/* --- path: ./src/foo.ts --- */ +/* --- path: src/foo.ts --- */ export const bar = 42; -/* --- path: ./src/index.ts --- */ +/* --- path: src/index.ts --- */ import { bar } from "./foo"; // ^ defined: 6 From 94ab2f9936ebb10df2504b92805dddd85a1c0970 Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Fri, 27 Oct 2023 11:58:04 +0100 Subject: [PATCH 054/291] Ensure all viable root paths are loaded from database --- stack-graphs/src/stitching.rs | 101 +++++++++++++---- ...can_find_root_partial_paths_in_database.rs | 9 +- stack-graphs/tests/it/main.rs | 1 + stack-graphs/tests/it/stitching.rs | 104 ++++++++++++++++++ 4 files changed, 192 insertions(+), 23 deletions(-) create mode 100644 stack-graphs/tests/it/stitching.rs diff --git a/stack-graphs/src/stitching.rs b/stack-graphs/src/stitching.rs index 3d02a14be..6aade970d 100644 --- a/stack-graphs/src/stitching.rs +++ b/stack-graphs/src/stitching.rs @@ -284,7 +284,12 @@ pub struct Database { symbol_stack_keys: ListArena>, symbol_stack_key_cache: HashMap, paths_by_start_node: SupplementalArena>>, - root_paths_by_precondition: SupplementalArena>>, + root_paths_by_precondition_prefix: + SupplementalArena>>, + root_paths_by_precondition_with_variable: + SupplementalArena>>, + root_paths_by_precondition_without_variable: + SupplementalArena>>, incoming_paths: SupplementalArena, } @@ -297,7 +302,9 @@ impl Database { symbol_stack_keys: List::new_arena(), symbol_stack_key_cache: HashMap::new(), paths_by_start_node: SupplementalArena::new(), - root_paths_by_precondition: SupplementalArena::new(), + root_paths_by_precondition_prefix: SupplementalArena::new(), + root_paths_by_precondition_with_variable: SupplementalArena::new(), + root_paths_by_precondition_without_variable: SupplementalArena::new(), incoming_paths: SupplementalArena::new(), } } @@ -311,7 +318,9 @@ impl Database { self.symbol_stack_keys.clear(); self.symbol_stack_key_cache.clear(); self.paths_by_start_node.clear(); - self.root_paths_by_precondition.clear(); + self.root_paths_by_precondition_prefix.clear(); + self.root_paths_by_precondition_with_variable.clear(); + self.root_paths_by_precondition_without_variable.clear(); self.incoming_paths.clear(); } @@ -341,14 +350,21 @@ impl Database { if graph[start_node].is_root() { // The join node is root, so there's no need to use half-open symbol stacks here, as we // do for [`PartialPath::concatenate`][]. - let key = SymbolStackKey::from_partial_symbol_stack( + let mut key = SymbolStackKey::from_partial_symbol_stack( partials, self, symbol_stack_precondition, ); if !key.is_empty() { - let key_handle = key.back_handle(); - self.root_paths_by_precondition[key_handle].push(handle); + match symbol_stack_precondition.has_variable() { + true => self.root_paths_by_precondition_with_variable[key.back_handle()] + .push(handle), + false => self.root_paths_by_precondition_without_variable[key.back_handle()] + .push(handle), + } + } + while key.pop_back(self).is_some() && !key.is_empty() { + self.root_paths_by_precondition_prefix[key.back_handle()].push(handle); } } else { // Otherwise index it by its source node. @@ -374,12 +390,12 @@ impl Database { if graph[path.end_node].is_root() { // The join node is root, so there's no need to use half-open symbol stacks here, as we // do for [`PartialPath::concatenate`][]. - let key = SymbolStackKey::from_partial_symbol_stack( + self.find_candidate_partial_paths_from_root( + graph, partials, - self, - path.symbol_stack_postcondition, + Some(path.symbol_stack_postcondition), + result, ); - self.find_candidate_partial_paths_from_root(graph, partials, Some(key), result); } else { self.find_candidate_partial_paths_from_node(graph, partials, path.end_node, result); } @@ -392,7 +408,7 @@ impl Database { &mut self, graph: &StackGraph, partials: &mut PartialPaths, - symbol_stack: Option, + symbol_stack: Option, result: &mut R, ) where R: std::iter::Extend>, @@ -400,31 +416,76 @@ impl Database { // If the path currently ends at the root node, then we need to look up partial paths whose // symbol stack precondition is compatible with the path. match symbol_stack { - Some(mut symbol_stack) => loop { + Some(symbol_stack) => { + let mut key = + SymbolStackKey::from_partial_symbol_stack(partials, self, symbol_stack); copious_debugging!( " Search for symbol stack <{}>", - symbol_stack.display(graph, self) + key.display(graph, self) ); - let key_handle = symbol_stack.back_handle(); - if let Some(paths) = self.root_paths_by_precondition.get(key_handle) { + // paths that have exactly this symbol stack + if let Some(paths) = self + .root_paths_by_precondition_without_variable + .get(key.back_handle()) + { #[cfg(feature = "copious-debugging")] { for path in paths { copious_debugging!( - " Found path {}", + " Found path with exact stack {}", self[*path].display(graph, partials) ); } } result.extend(paths.iter().copied()); } - if symbol_stack.pop_back(self).is_none() { - break; + // paths that have an extension of this symbol stack + if symbol_stack.has_variable() { + if let Some(paths) = self + .root_paths_by_precondition_prefix + .get(key.back_handle()) + { + #[cfg(feature = "copious-debugging")] + { + for path in paths { + copious_debugging!( + " Found path with smaller stack {}", + self[*path].display(graph, partials) + ); + } + } + result.extend(paths.iter().copied()); + } + } + loop { + // paths that have a prefix of this symbol stack + if let Some(paths) = self + .root_paths_by_precondition_with_variable + .get(key.back_handle()) + { + #[cfg(feature = "copious-debugging")] + { + for path in paths { + copious_debugging!( + " Found path with smaller stack {}", + self[*path].display(graph, partials) + ); + } + } + result.extend(paths.iter().copied()); + } + if key.pop_back(self).is_none() { + break; + } } - }, + } None => { copious_debugging!(" Search for all root paths"); - for (_, paths) in self.root_paths_by_precondition.iter() { + for (_, paths) in self + .root_paths_by_precondition_with_variable + .iter() + .chain(self.root_paths_by_precondition_without_variable.iter()) + { #[cfg(feature = "copious-debugging")] { for path in paths { diff --git a/stack-graphs/tests/it/can_find_root_partial_paths_in_database.rs b/stack-graphs/tests/it/can_find_root_partial_paths_in_database.rs index 97dca4788..a70336333 100644 --- a/stack-graphs/tests/it/can_find_root_partial_paths_in_database.rs +++ b/stack-graphs/tests/it/can_find_root_partial_paths_in_database.rs @@ -17,7 +17,6 @@ use stack_graphs::partial::PartialScopedSymbol; use stack_graphs::partial::PartialSymbolStack; use stack_graphs::stitching::Database; use stack_graphs::stitching::ForwardPartialPathStitcher; -use stack_graphs::stitching::SymbolStackKey; use stack_graphs::NoCancellation; use crate::test_graphs; @@ -53,8 +52,12 @@ fn check_root_partial_paths( } let mut results = Vec::>::new(); - let key = SymbolStackKey::from_partial_symbol_stack(&mut partials, &mut db, symbol_stack); - db.find_candidate_partial_paths_from_root(graph, &mut partials, Some(key), &mut results); + db.find_candidate_partial_paths_from_root( + graph, + &mut partials, + Some(symbol_stack), + &mut results, + ); let actual_partial_paths = results .into_iter() diff --git a/stack-graphs/tests/it/main.rs b/stack-graphs/tests/it/main.rs index bea8a6030..75818b076 100644 --- a/stack-graphs/tests/it/main.rs +++ b/stack-graphs/tests/it/main.rs @@ -24,4 +24,5 @@ mod graph; mod partial; #[cfg(feature = "serde")] mod serde; +mod stitching; mod util; diff --git a/stack-graphs/tests/it/stitching.rs b/stack-graphs/tests/it/stitching.rs new file mode 100644 index 000000000..ddb878606 --- /dev/null +++ b/stack-graphs/tests/it/stitching.rs @@ -0,0 +1,104 @@ +// -*- coding: utf-8 -*- +// ------------------------------------------------------------------------------------------------ +// Copyright © 2023, stack-graphs authors. +// Licensed under either of Apache License, Version 2.0, or MIT license, at your option. +// Please see the LICENSE-APACHE or LICENSE-MIT files in this distribution for license details. +// ------------------------------------------------------------------------------------------------ + +use itertools::Itertools; +use stack_graphs::graph::StackGraph; +use stack_graphs::partial::PartialPaths; +use stack_graphs::stitching::Database; + +use crate::util::create_partial_path_and_edges; +use crate::util::create_pop_symbol_node; +use crate::util::create_push_symbol_node; + +fn test_foo_bar_root_candidate_paths(symbols: &[&str], variable: bool) -> usize { + let mut graph = StackGraph::new(); + let file = graph.add_file("test").unwrap(); + let mut partials = PartialPaths::new(); + + let r = StackGraph::root_node(); + let foo = create_pop_symbol_node(&mut graph, file, "foo", true); + let bar = create_pop_symbol_node(&mut graph, file, "bar", true); + + let path_with_variable = + create_partial_path_and_edges(&mut graph, &mut partials, &[r, foo, bar]).unwrap(); + + let mut path_without_variable = path_with_variable.clone(); + path_without_variable.eliminate_precondition_stack_variables(&mut partials); + + let mut db = Database::new(); + db.add_partial_path(&graph, &mut partials, path_with_variable); + db.add_partial_path(&graph, &mut partials, path_without_variable); + + let r = StackGraph::root_node(); + let refs = symbols + .into_iter() + .map(|r| create_push_symbol_node(&mut graph, file, *r, true)) + .chain(std::iter::once(r)) + .collect_vec(); + let mut path = create_partial_path_and_edges(&mut graph, &mut partials, &refs).unwrap(); + if !variable { + path.eliminate_precondition_stack_variables(&mut partials); + } + + let mut results = Vec::new(); + db.find_candidate_partial_paths_from_root( + &mut graph, + &mut partials, + Some(path.symbol_stack_postcondition), + &mut results, + ); + + results.len() +} + +#[test] +fn find_candidates_for_exact_symbol_stack_with_variable() { + // <"foo","bar",%2> ~ <"foo","bar",%1> | yes, %2 = %1 + // <"foo","bar",%2> ~ <"foo","bar"> | yes, %2 = <> + let results = test_foo_bar_root_candidate_paths(&["bar", "foo"], true); + assert_eq!(2, results); +} + +#[test] +fn find_candidates_for_exact_symbol_stack_without_variable() { + // <"foo","bar"> ~ <"foo","bar",%1> | yes, %1 = <> + // <"foo","bar"> ~ <"foo","bar"> | yes + let results = test_foo_bar_root_candidate_paths(&["bar", "foo"], false); + assert_eq!(2, results); +} + +#[test] +fn find_candidates_for_longer_symbol_stack_with_variable() { + // <"foo","bar","quz",%2> ~ <"foo","bar",%1> | yes, %1 = <"quz",%2> + // <"foo","bar","quz",%2> ~ <"foo","bar"> | no + let results = test_foo_bar_root_candidate_paths(&["quz", "bar", "foo"], true); + assert_eq!(1, results); +} + +#[test] +fn find_candidates_for_longer_symbol_stack_without_variable() { + // <"foo","bar","quz"> ~ <"foo","bar",%1> | yes, %1 = <"quz"> + // <"foo","bar","quz"> ~ <"foo","bar"> | no + let results = test_foo_bar_root_candidate_paths(&["quz", "bar", "foo"], false); + assert_eq!(1, results); +} + +#[test] +fn find_candidates_for_shorter_symbol_stack_with_variable() { + // <"foo",%2> ~ <"foo","bar",%1> | yes, %2 = <"bar",%1> + // <"foo",%2> ~ <"foo","bar"> | yes, %2 = <"bar"> + let results = test_foo_bar_root_candidate_paths(&["foo"], true); + assert_eq!(2, results); +} + +#[test] +fn find_candidates_for_shorter_symbol_stack_without_variable() { + // <"foo"> ~ <"foo","bar",%1> | no + // <"foo"> ~ <"foo","bar"> | no + let results = test_foo_bar_root_candidate_paths(&["foo"], false); + assert_eq!(0, results); +} From 5a6744b9672c6128bf938d34733ea52f07f36dee Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Sun, 22 Oct 2023 13:20:08 +0200 Subject: [PATCH 055/291] Ensure all viable root paths are loaded from storage --- stack-graphs/src/storage.rs | 69 +++++++++++------ stack-graphs/tests/it/main.rs | 2 + stack-graphs/tests/it/storage.rs | 127 +++++++++++++++++++++++++++++++ 3 files changed, 173 insertions(+), 25 deletions(-) create mode 100644 stack-graphs/tests/it/storage.rs diff --git a/stack-graphs/src/storage.rs b/stack-graphs/src/storage.rs index 7664ea88f..d3f1348dc 100644 --- a/stack-graphs/src/storage.rs +++ b/stack-graphs/src/storage.rs @@ -7,6 +7,7 @@ use bincode::error::DecodeError; use bincode::error::EncodeError; +use itertools::Itertools; use rusqlite::functions::FunctionFlags; use rusqlite::types::ValueRef; use rusqlite::Connection; @@ -33,7 +34,7 @@ use crate::stitching::ForwardCandidates; use crate::CancellationError; use crate::CancellationFlag; -const VERSION: usize = 5; +const VERSION: usize = 6; const SCHEMA: &str = r#" CREATE TABLE metadata ( @@ -616,9 +617,12 @@ impl SQLiteReader { " * Load extensions from root with symbol stack {}", symbol_stack.display(&self.graph, &mut self.partials) ); - let symbol_stack_prefixes = - symbol_stack.storage_key_prefixes(&self.graph, &mut self.partials); - for symbol_stack in symbol_stack_prefixes { + let mut stmt = self.conn.prepare_cached( + "SELECT file,value from root_paths WHERE symbol_stack LIKE ? ESCAPE ?", + )?; + let (symbol_stack_patterns, escape) = + symbol_stack.storage_key_patterns(&self.graph, &mut self.partials); + for symbol_stack in symbol_stack_patterns { copious_debugging!( " * Load extensions from root with prefix symbol stack {}", symbol_stack @@ -627,10 +631,7 @@ impl SQLiteReader { copious_debugging!(" > Already loaded"); continue; } - let mut stmt = self - .conn - .prepare_cached("SELECT file,value from root_paths WHERE symbol_stack = ?")?; - let paths = stmt.query_map([symbol_stack], |row| { + let paths = stmt.query_map([symbol_stack, escape.clone()], |row| { let file = row.get::<_, String>(0)?; let value = row.get::<_, Vec>(1)?; Ok((file, value)) @@ -682,41 +683,59 @@ impl SQLiteReader { } /// Get the stack graph, partial paths arena, and path database for the currently loaded data. - pub fn get(&mut self) -> (&StackGraph, &mut PartialPaths, &mut Database) { - (&self.graph, &mut self.partials, &mut self.db) + pub fn get(&mut self) -> (&mut StackGraph, &mut PartialPaths, &mut Database) { + (&mut self.graph, &mut self.partials, &mut self.db) } } +// Methods for computing keys and patterns for a symbol stack. The format of a storage key is: +// +// has-var GS ( symbol (US symbol)* )? +// +// where has-var is "V" if the symbol stack has a variable, "X" otherwise. impl PartialSymbolStack { /// Returns a string representation of this symbol stack for indexing in the database. - fn storage_key(mut self, graph: &StackGraph, partials: &mut PartialPaths) -> String { + fn storage_key(self, graph: &StackGraph, partials: &mut PartialPaths) -> String { let mut key = String::new(); - while let Some(symbol) = self.pop_front(partials) { - if !key.is_empty() { - key += "\u{241F}"; - } - key += &graph[symbol.symbol]; + match self.has_variable() { + true => key += "V\u{241E}", + false => key += "X\u{241E}", } + key += &self + .iter(partials) + .map(|s| &graph[s.symbol]) + .join("\u{241F}"); key } /// Returns string representations for all prefixes of this symbol stack for querying the /// index in the database. - fn storage_key_prefixes( + fn storage_key_patterns( mut self, graph: &StackGraph, partials: &mut PartialPaths, - ) -> Vec { - let mut key_prefixes = vec![String::new()]; + ) -> (Vec, String) { + let mut key_patterns = Vec::new(); + let mut symbols = String::new(); while let Some(symbol) = self.pop_front(partials) { - let mut key = key_prefixes.last().unwrap().to_string(); - if !key.is_empty() { - key += "\u{241F}"; + if !symbols.is_empty() { + symbols += "\u{241F}"; } - key += &graph[symbol.symbol]; - key_prefixes.push(key); + let symbol = graph[symbol.symbol] + .replace("%", "\\%") + .replace("_", "\\_") + .to_string(); + symbols += &symbol; + // patterns for paths matching a prefix of this stack + key_patterns.push("V\u{241E}".to_string() + &symbols); + } + // pattern for paths matching exactly this stack + key_patterns.push("X\u{241E}".to_string() + &symbols); + if self.has_variable() { + // patterns for paths for which this stack is a prefix + key_patterns.push("_\u{241E}".to_string() + &symbols + "\u{241F}%"); } - key_prefixes + (key_patterns, "\\".to_string()) } } diff --git a/stack-graphs/tests/it/main.rs b/stack-graphs/tests/it/main.rs index 75818b076..3f0162c3b 100644 --- a/stack-graphs/tests/it/main.rs +++ b/stack-graphs/tests/it/main.rs @@ -25,4 +25,6 @@ mod partial; #[cfg(feature = "serde")] mod serde; mod stitching; +#[cfg(feature = "storage")] +mod storage; mod util; diff --git a/stack-graphs/tests/it/storage.rs b/stack-graphs/tests/it/storage.rs new file mode 100644 index 000000000..464700414 --- /dev/null +++ b/stack-graphs/tests/it/storage.rs @@ -0,0 +1,127 @@ +// -*- coding: utf-8 -*- +// ------------------------------------------------------------------------------------------------ +// Copyright © 2023, stack-graphs authors. +// Licensed under either of Apache License, Version 2.0, or MIT license, at your option. +// Please see the LICENSE-APACHE or LICENSE-MIT files in this distribution for license details. +// ------------------------------------------------------------------------------------------------ + +use itertools::Itertools; +use stack_graphs::graph::StackGraph; +use stack_graphs::partial::PartialPaths; +use stack_graphs::storage::SQLiteWriter; +use stack_graphs::NoCancellation; + +use crate::util::create_partial_path_and_edges; +use crate::util::create_pop_symbol_node; +use crate::util::create_push_symbol_node; + +fn test_foo_bar_root_candidate_paths(symbols: &[&str], variable: bool) -> usize { + let mut reader = { + let mut writer = SQLiteWriter::open_in_memory().unwrap(); + + let mut graph = StackGraph::new(); + let file = graph.add_file("test1").unwrap(); + let mut partials = PartialPaths::new(); + + let r = StackGraph::root_node(); + let foo = create_pop_symbol_node(&mut graph, file, "foo", true); + let bar = create_pop_symbol_node(&mut graph, file, "bar", true); + + let path_with_variable = + create_partial_path_and_edges(&mut graph, &mut partials, &[r, foo, bar]).unwrap(); + + let mut path_without_variable = path_with_variable.clone(); + path_without_variable.eliminate_precondition_stack_variables(&mut partials); + + writer + .store_result_for_file( + &graph, + file, + "", + &mut partials, + vec![&path_with_variable, &path_without_variable], + ) + .unwrap(); + + writer.into_reader() + }; + + { + let (graph, partials, _) = reader.get(); + let file = graph.add_file("test2").unwrap(); + + let r = StackGraph::root_node(); + let refs = symbols + .into_iter() + .map(|r| create_push_symbol_node(graph, file, *r, true)) + .chain(std::iter::once(r)) + .collect_vec(); + let mut path = create_partial_path_and_edges(graph, partials, &refs).unwrap(); + if !variable { + path.eliminate_precondition_stack_variables(partials); + } + + reader + .load_partial_path_extensions(&path, &NoCancellation) + .unwrap(); + + let (graph, partials, db) = reader.get(); + let mut results = Vec::new(); + db.find_candidate_partial_paths_from_root( + graph, + partials, + Some(path.symbol_stack_postcondition), + &mut results, + ); + + results.len() + } +} + +#[test] +fn find_candidates_for_exact_symbol_stack_with_variable() { + // <"foo","bar",%2> ~ <"foo","bar",%1> | yes, %2 = %1 + // <"foo","bar",%2> ~ <"foo","bar"> | yes, %2 = <> + let results = test_foo_bar_root_candidate_paths(&["bar", "foo"], true); + assert_eq!(2, results); +} + +#[test] +fn find_candidates_for_exact_symbol_stack_without_variable() { + // <"foo","bar"> ~ <"foo","bar",%1> | yes, %1 = <> + // <"foo","bar"> ~ <"foo","bar"> | yes + let results = test_foo_bar_root_candidate_paths(&["bar", "foo"], false); + assert_eq!(2, results); +} + +#[test] +fn find_candidates_for_longer_symbol_stack_with_variable() { + // <"foo","bar","quz",%2> ~ <"foo","bar",%1> | yes, %1 = <"quz",%2> + // <"foo","bar","quz",%2> ~ <"foo","bar"> | no + let results = test_foo_bar_root_candidate_paths(&["quz", "bar", "foo"], true); + assert_eq!(1, results); +} + +#[test] +fn find_candidates_for_longer_symbol_stack_without_variable() { + // <"foo","bar","quz"> ~ <"foo","bar",%1> | yes, %1 = <"quz"> + // <"foo","bar","quz"> ~ <"foo","bar"> | no + let results = test_foo_bar_root_candidate_paths(&["quz", "bar", "foo"], false); + assert_eq!(1, results); +} + +#[test] +fn find_candidates_for_shorter_symbol_stack_with_variable() { + // <"foo",%2> ~ <"foo","bar",%1> | yes, %2 = <"bar",%1> + // <"foo",%2> ~ <"foo","bar"> | yes, %2 = <"bar"> + let results = test_foo_bar_root_candidate_paths(&["foo"], true); + assert_eq!(2, results); +} + +#[test] +fn find_candidates_for_shorter_symbol_stack_without_variable() { + // <"foo"> ~ <"foo","bar",%1> | no + // <"foo"> ~ <"foo","bar"> | no + let results = test_foo_bar_root_candidate_paths(&["foo"], false); + assert_eq!(0, results); +} From 87d312a1f585a9f77dada1d83c912091135fd443 Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Mon, 16 Oct 2023 17:39:07 +0200 Subject: [PATCH 056/291] Add support for stitcher configuration --- .../tree-sitter-stack-graphs-java/Cargo.toml | 1 + .../tree-sitter-stack-graphs-java/rust/lib.rs | 4 +++ .../rust/lib.rs | 2 ++ stack-graphs/include/stack-graphs.h | 13 +++++++-- stack-graphs/src/assert.rs | 16 +++++++++-- stack-graphs/src/c.rs | 28 ++++++++++++++++++- stack-graphs/src/stitching.rs | 20 ++++++++++--- .../tests/it/c/can_find_local_nodes.rs | 5 ++++ .../it/c/can_find_partial_paths_in_file.rs | 5 ++++ ...ions_with_phased_partial_path_stitching.rs | 9 ++++++ .../tests/it/c/can_jump_to_definition.rs | 5 ++++ ...tion_with_phased_partial_path_stitching.rs | 9 ++++++ stack-graphs/tests/it/can_find_local_nodes.rs | 2 ++ ...can_find_node_partial_paths_in_database.rs | 2 ++ .../it/can_find_partial_paths_in_file.rs | 3 +- ...can_find_root_partial_paths_in_database.rs | 2 ++ .../tests/it/can_jump_to_definition.rs | 2 ++ ...ion_with_forward_partial_path_stitching.rs | 3 ++ stack-graphs/tests/it/cycles.rs | 4 +++ stack-graphs/tests/it/serde.rs | 3 +- tree-sitter-stack-graphs/src/cli/index.rs | 11 ++++++++ tree-sitter-stack-graphs/src/cli/init.rs | 2 ++ tree-sitter-stack-graphs/src/cli/query.rs | 8 ++++++ tree-sitter-stack-graphs/src/cli/test.rs | 22 +++++++++++++-- tree-sitter-stack-graphs/src/cli/visualize.rs | 8 ++++++ tree-sitter-stack-graphs/src/loader.rs | 6 ++++ tree-sitter-stack-graphs/src/test.rs | 10 ++++++- tree-sitter-stack-graphs/tests/it/loader.rs | 2 ++ tree-sitter-stack-graphs/tests/it/test.rs | 9 +++++- 29 files changed, 200 insertions(+), 16 deletions(-) diff --git a/languages/tree-sitter-stack-graphs-java/Cargo.toml b/languages/tree-sitter-stack-graphs-java/Cargo.toml index 8bd3e5f93..72646a905 100644 --- a/languages/tree-sitter-stack-graphs-java/Cargo.toml +++ b/languages/tree-sitter-stack-graphs-java/Cargo.toml @@ -36,5 +36,6 @@ harness = false # need to provide own main function to handle running tests [dependencies] anyhow = "1.0" clap = { version = "4", features = ["derive"] } +stack-graphs = { version = "0.12", path = "../../stack-graphs" } tree-sitter-stack-graphs = { version = "0.7", path = "../../tree-sitter-stack-graphs", features=["cli"] } tree-sitter-java = { version = "=0.20.0" } diff --git a/languages/tree-sitter-stack-graphs-java/rust/lib.rs b/languages/tree-sitter-stack-graphs-java/rust/lib.rs index 09cb043a5..d1af5bb12 100644 --- a/languages/tree-sitter-stack-graphs-java/rust/lib.rs +++ b/languages/tree-sitter-stack-graphs-java/rust/lib.rs @@ -1,6 +1,7 @@ use tree_sitter_stack_graphs::loader::FileAnalyzers; use tree_sitter_stack_graphs::loader::LanguageConfiguration; use tree_sitter_stack_graphs::loader::LoadError; +use tree_sitter_stack_graphs::loader::StitcherConfig; use tree_sitter_stack_graphs::CancellationFlag; /// The stacks graphs tsg path for this language. @@ -40,6 +41,9 @@ pub fn try_language_configuration( )), Some(STACK_GRAPHS_BUILTINS_CONFIG), FileAnalyzers::new(), + StitcherConfig { + detect_similar_paths: true, + }, cancellation_flag, ) } diff --git a/languages/tree-sitter-stack-graphs-typescript/rust/lib.rs b/languages/tree-sitter-stack-graphs-typescript/rust/lib.rs index 45ce6d255..9ef2f1805 100644 --- a/languages/tree-sitter-stack-graphs-typescript/rust/lib.rs +++ b/languages/tree-sitter-stack-graphs-typescript/rust/lib.rs @@ -8,6 +8,7 @@ use tree_sitter_stack_graphs::loader::FileAnalyzers; use tree_sitter_stack_graphs::loader::LanguageConfiguration; use tree_sitter_stack_graphs::loader::LoadError; +use tree_sitter_stack_graphs::loader::StitcherConfig; use tree_sitter_stack_graphs::CancellationFlag; use crate::npm_package::NpmPackageAnalyzer; @@ -56,6 +57,7 @@ pub fn try_language_configuration( FileAnalyzers::new() .add("tsconfig.json".to_string(), TsConfigAnalyzer {}) .add("package.json".to_string(), NpmPackageAnalyzer {}), + StitcherConfig::default(), cancellation_flag, ) } diff --git a/stack-graphs/include/stack-graphs.h b/stack-graphs/include/stack-graphs.h index 50b7062ed..a1afe93dd 100644 --- a/stack-graphs/include/stack-graphs.h +++ b/stack-graphs/include/stack-graphs.h @@ -437,6 +437,11 @@ struct sg_partial_path { struct sg_partial_path_edge_list edges; }; +struct sg_stitcher_config { + // Enables similar path detection during stiching. + bool detect_similar_paths; +}; + // An array of all of the partial paths in a partial path database. Partial path handles are // indices into this array. There will never be a valid partial path at index 0; a handle with // the value 0 represents a missing partial path. @@ -733,6 +738,7 @@ enum sg_result sg_partial_path_arena_find_partial_paths_in_file(const struct sg_ struct sg_partial_path_arena *partials, sg_file_handle file, struct sg_partial_path_list *partial_path_list, + struct sg_stitcher_config config, const size_t *cancellation_flag); // Finds all complete paths reachable from a set of starting nodes, placing the result into the @@ -748,6 +754,7 @@ enum sg_result sg_partial_path_arena_find_all_complete_paths(const struct sg_sta size_t starting_node_count, const sg_node_handle *starting_nodes, struct sg_partial_path_list *path_list, + struct sg_stitcher_config config, const size_t *cancellation_flag); // Returns a reference to the array of partial path data in this partial path database. The @@ -805,14 +812,16 @@ struct sg_node_handle_set sg_partial_path_database_local_nodes(const struct sg_p struct sg_forward_partial_path_stitcher *sg_forward_partial_path_stitcher_from_nodes(const struct sg_stack_graph *graph, struct sg_partial_path_arena *partials, size_t count, - const sg_node_handle *starting_nodes); + const sg_node_handle *starting_nodes, + struct sg_stitcher_config config); // Creates a new forward partial path stitcher that is "seeded" with a set of initial partial // paths. struct sg_forward_partial_path_stitcher *sg_forward_partial_path_stitcher_from_partial_paths(const struct sg_stack_graph *graph, struct sg_partial_path_arena *partials, size_t count, - const struct sg_partial_path *initial_partial_paths); + const struct sg_partial_path *initial_partial_paths, + struct sg_stitcher_config config); // Sets whether similar path detection should be enabled during path stitching. Paths are similar // if start and end node, and pre- and postconditions are the same. The presence of similar paths diff --git a/stack-graphs/src/assert.rs b/stack-graphs/src/assert.rs index 70237614c..278caabbc 100644 --- a/stack-graphs/src/assert.rs +++ b/stack-graphs/src/assert.rs @@ -20,6 +20,7 @@ use crate::partial::PartialPaths; use crate::stitching::Database; use crate::stitching::DatabaseCandidates; use crate::stitching::ForwardPartialPathStitcher; +use crate::stitching::StitcherConfig; use crate::CancellationError; use crate::CancellationFlag; @@ -149,12 +150,19 @@ impl Assertion { graph: &StackGraph, partials: &mut PartialPaths, db: &mut Database, + stitcher_config: &StitcherConfig, cancellation_flag: &dyn CancellationFlag, ) -> Result<(), AssertionError> { match self { - Self::Defined { source, targets } => { - self.run_defined(graph, partials, db, source, targets, cancellation_flag) - } + Self::Defined { source, targets } => self.run_defined( + graph, + partials, + db, + source, + targets, + stitcher_config, + cancellation_flag, + ), Self::Defines { source, symbols } => self.run_defines(graph, source, symbols), Self::Refers { source, symbols } => self.run_refers(graph, source, symbols), } @@ -167,6 +175,7 @@ impl Assertion { db: &mut Database, source: &AssertionSource, expected_targets: &Vec, + stitcher_config: &StitcherConfig, cancellation_flag: &dyn CancellationFlag, ) -> Result<(), AssertionError> { let references = source.iter_references(graph).collect::>(); @@ -182,6 +191,7 @@ impl Assertion { ForwardPartialPathStitcher::find_all_complete_partial_paths( &mut DatabaseCandidates::new(graph, partials, db), vec![*reference], + stitcher_config, cancellation_flag, |_, _, p| { reference_paths.push(p.clone()); diff --git a/stack-graphs/src/c.rs b/stack-graphs/src/c.rs index f4857c791..4f58caff2 100644 --- a/stack-graphs/src/c.rs +++ b/stack-graphs/src/c.rs @@ -32,6 +32,7 @@ use crate::stitching::Database; use crate::stitching::DatabaseCandidates; use crate::stitching::ForwardPartialPathStitcher; use crate::stitching::GraphEdgeCandidates; +use crate::stitching::StitcherConfig; use crate::CancellationError; use crate::CancellationFlag; @@ -1173,6 +1174,7 @@ pub extern "C" fn sg_partial_path_arena_find_partial_paths_in_file( partials: *mut sg_partial_path_arena, file: sg_file_handle, partial_path_list: *mut sg_partial_path_list, + config: sg_stitcher_config, cancellation_flag: *const usize, ) -> sg_result { let graph = unsafe { &(*graph).inner }; @@ -1185,6 +1187,7 @@ pub extern "C" fn sg_partial_path_arena_find_partial_paths_in_file( graph, partials, file, + &config.into(), &AtomicUsizeCancellationFlag(cancellation_flag), |_graph, partials, path| { let mut path = path.clone(); @@ -1210,6 +1213,7 @@ pub extern "C" fn sg_partial_path_arena_find_all_complete_paths( starting_node_count: usize, starting_nodes: *const sg_node_handle, path_list: *mut sg_partial_path_list, + config: sg_stitcher_config, cancellation_flag: *const usize, ) -> sg_result { let graph = unsafe { &(*graph).inner }; @@ -1221,6 +1225,7 @@ pub extern "C" fn sg_partial_path_arena_find_all_complete_paths( ForwardPartialPathStitcher::find_all_complete_partial_paths( &mut GraphEdgeCandidates::new(graph, partials, None), starting_nodes.iter().copied().map(sg_node_handle::into), + &config.into(), &AtomicUsizeCancellationFlag(cancellation_flag), |graph, _partials, path| { if path.is_complete(graph) { @@ -1391,6 +1396,19 @@ pub struct sg_forward_partial_path_stitcher { pub is_complete: bool, } +// Configuration for partial path stitchers. +#[repr(C)] +pub struct sg_stitcher_config { + /// Enables similar path detection during stiching. + pub detect_similar_paths: bool, +} + +impl Into for sg_stitcher_config { + fn into(self) -> StitcherConfig { + unsafe { std::mem::transmute(self) } + } +} + // This is the Rust equivalent of a common C trick, where you have two versions of a struct — a // publicly visible one and a private one containing internal implementation details. In our case, // `sg_forward_partial_path_stitcher` is the public struct, and @@ -1443,6 +1461,7 @@ pub extern "C" fn sg_forward_partial_path_stitcher_from_nodes( partials: *mut sg_partial_path_arena, count: usize, starting_nodes: *const sg_node_handle, + config: sg_stitcher_config, ) -> *mut sg_forward_partial_path_stitcher { let graph = unsafe { &(*graph).inner }; let partials = unsafe { &mut (*partials).inner }; @@ -1457,7 +1476,12 @@ pub extern "C" fn sg_forward_partial_path_stitcher_from_nodes( p }) .collect::>(); - let stitcher = ForwardPartialPathStitcher::from_partial_paths(graph, partials, initial_paths); + let stitcher = ForwardPartialPathStitcher::from_partial_paths( + graph, + partials, + initial_paths, + &config.into(), + ); Box::into_raw(Box::new(InternalForwardPartialPathStitcher::new( stitcher, partials, ))) as *mut _ @@ -1471,6 +1495,7 @@ pub extern "C" fn sg_forward_partial_path_stitcher_from_partial_paths( partials: *mut sg_partial_path_arena, count: usize, initial_partial_paths: *const sg_partial_path, + config: sg_stitcher_config, ) -> *mut sg_forward_partial_path_stitcher { let graph = unsafe { &(*graph).inner }; let partials = unsafe { &mut (*partials).inner }; @@ -1480,6 +1505,7 @@ pub extern "C" fn sg_forward_partial_path_stitcher_from_partial_paths( graph, partials, initial_partial_paths.to_vec(), + &config.into(), ); Box::into_raw(Box::new(InternalForwardPartialPathStitcher::new( stitcher, partials, diff --git a/stack-graphs/src/stitching.rs b/stack-graphs/src/stitching.rs index 6aade970d..dc7cb8c9b 100644 --- a/stack-graphs/src/stitching.rs +++ b/stack-graphs/src/stitching.rs @@ -795,6 +795,13 @@ impl<'a> Display for DisplaySymbolStackKey<'a> { //------------------------------------------------------------------------------------------------- // Stitching partial paths together +/// Configuration for partial path stitchers. +#[derive(Clone, Debug, Default)] +pub struct StitcherConfig { + /// Enables similar path detection during path stitching. + pub detect_similar_paths: bool, +} + /// Implements a phased forward partial path stitching algorithm. /// /// Our overall goal is to start with a set of _seed_ partial paths, and to repeatedly extend each @@ -851,6 +858,7 @@ impl ForwardPartialPathStitcher { _graph: &StackGraph, _partials: &mut PartialPaths, initial_partial_paths: I, + config: &StitcherConfig, ) -> Self where I: IntoIterator, @@ -863,14 +871,16 @@ impl ForwardPartialPathStitcher { (p, c, false) }) .multiunzip(); - Self { + let similar_path_detector = + Some(SimilarPathDetector::new()).filter(|_| config.detect_similar_paths); + ForwardPartialPathStitcher { candidates: Vec::new(), extensions: Vec::new(), queue: VecDeque::new(), initial_paths: next_iteration.0.len(), next_iteration, appended_paths, - similar_path_detector: Some(SimilarPathDetector::new()), + similar_path_detector, // By default, all nodes are checked for cycles and (if enabled) similarity check_only_join_nodes: false, // By default, there's no artificial bound on the amount of work done per phase @@ -1145,6 +1155,7 @@ impl ForwardPartialPathStitcher { graph: &StackGraph, partials: &mut PartialPaths, file: Handle, + config: &StitcherConfig, cancellation_flag: &dyn CancellationFlag, mut visit: F, ) -> Result<(), CancellationError> @@ -1163,7 +1174,7 @@ impl ForwardPartialPathStitcher { .map(|node| PartialPath::from_node(graph, partials, node)) .collect::>(); let mut stitcher = - ForwardPartialPathStitcher::from_partial_paths(graph, partials, initial_paths); + ForwardPartialPathStitcher::from_partial_paths(graph, partials, initial_paths, config); stitcher.set_check_only_join_nodes(true); while !stitcher.is_complete() { cancellation_flag.check("finding complete partial paths")?; @@ -1196,6 +1207,7 @@ impl ForwardPartialPathStitcher { pub fn find_all_complete_partial_paths( candidates: &mut C, starting_nodes: I, + config: &StitcherConfig, cancellation_flag: &dyn CancellationFlag, mut visit: F, ) -> Result<(), Err> @@ -1218,7 +1230,7 @@ impl ForwardPartialPathStitcher { p }) .collect::>(); - ForwardPartialPathStitcher::from_partial_paths(graph, partials, initial_paths) + ForwardPartialPathStitcher::from_partial_paths(graph, partials, initial_paths, config) }; stitcher.set_check_only_join_nodes(true); while !stitcher.is_complete() { diff --git a/stack-graphs/tests/it/c/can_find_local_nodes.rs b/stack-graphs/tests/it/c/can_find_local_nodes.rs index 0a5d3a402..2d2ecdee5 100644 --- a/stack-graphs/tests/it/c/can_find_local_nodes.rs +++ b/stack-graphs/tests/it/c/can_find_local_nodes.rs @@ -22,6 +22,7 @@ use stack_graphs::c::sg_partial_path_list_free; use stack_graphs::c::sg_partial_path_list_new; use stack_graphs::c::sg_partial_path_list_paths; use stack_graphs::c::sg_stack_graph_nodes; +use stack_graphs::c::sg_stitcher_config; use stack_graphs::graph::Node; use crate::c::test_graph::TestGraph; @@ -33,11 +34,15 @@ fn check_local_nodes(graph: &TestGraph, file: &str, expected_local_nodes: &[&str let partials = sg_partial_path_arena_new(); let path_list = sg_partial_path_list_new(); + let config = sg_stitcher_config { + detect_similar_paths: false, + }; sg_partial_path_arena_find_partial_paths_in_file( graph.graph, partials, file.as_u32(), path_list, + config, std::ptr::null(), ); diff --git a/stack-graphs/tests/it/c/can_find_partial_paths_in_file.rs b/stack-graphs/tests/it/c/can_find_partial_paths_in_file.rs index b39bc524a..65ab8e2c2 100644 --- a/stack-graphs/tests/it/c/can_find_partial_paths_in_file.rs +++ b/stack-graphs/tests/it/c/can_find_partial_paths_in_file.rs @@ -22,6 +22,7 @@ use stack_graphs::c::sg_partial_path_list_new; use stack_graphs::c::sg_partial_path_list_paths; use stack_graphs::c::sg_partial_scope_stack; use stack_graphs::c::sg_partial_symbol_stack; +use stack_graphs::c::sg_stitcher_config; use stack_graphs::c::SG_LIST_EMPTY_HANDLE; use stack_graphs::c::SG_NULL_HANDLE; use stack_graphs::partial::PartialPath; @@ -77,11 +78,15 @@ fn check_partial_paths_in_file(graph: &TestGraph, file: &str, expected_paths: &[ let partials = sg_partial_path_arena_new(); let path_list = sg_partial_path_list_new(); + let config = sg_stitcher_config { + detect_similar_paths: false, + }; sg_partial_path_arena_find_partial_paths_in_file( graph.graph, partials, file.as_u32(), path_list, + config, std::ptr::null(), ); diff --git a/stack-graphs/tests/it/c/can_find_qualified_definitions_with_phased_partial_path_stitching.rs b/stack-graphs/tests/it/c/can_find_qualified_definitions_with_phased_partial_path_stitching.rs index dcc276c86..fe8594dee 100644 --- a/stack-graphs/tests/it/c/can_find_qualified_definitions_with_phased_partial_path_stitching.rs +++ b/stack-graphs/tests/it/c/can_find_qualified_definitions_with_phased_partial_path_stitching.rs @@ -28,6 +28,7 @@ use stack_graphs::c::sg_partial_path_list_free; use stack_graphs::c::sg_partial_path_list_new; use stack_graphs::c::sg_partial_path_list_paths; use stack_graphs::c::sg_stack_graph; +use stack_graphs::c::sg_stitcher_config; use stack_graphs::copious_debugging; use stack_graphs::graph::StackGraph; use stack_graphs::partial::PartialPath; @@ -55,11 +56,15 @@ impl StorageLayer { let rust_graph = unsafe { &(*graph).inner }; let path_list = sg_partial_path_list_new(); for file in rust_graph.iter_files() { + let config = sg_stitcher_config { + detect_similar_paths: false, + }; sg_partial_path_arena_find_partial_paths_in_file( graph, partials, file.as_u32(), path_list, + config, std::ptr::null(), ); } @@ -117,6 +122,9 @@ fn check_find_qualified_definitions( let partials = sg_partial_path_arena_new(); let rust_partials = unsafe { &mut (*partials).inner }; let db = sg_partial_path_database_new(); + let config = sg_stitcher_config { + detect_similar_paths: false, + }; // Create a new external storage layer holding _all_ of the partial paths in the stack graph. let mut storage_layer = StorageLayer::new(graph.graph, partials); @@ -149,6 +157,7 @@ fn check_find_qualified_definitions( partials, 1, &initial_partial_path as *const PartialPath as *const _, + config, ); sg_forward_partial_path_stitcher_set_max_work_per_phase(stitcher, 1); let rust_stitcher = unsafe { &mut *stitcher }; diff --git a/stack-graphs/tests/it/c/can_jump_to_definition.rs b/stack-graphs/tests/it/c/can_jump_to_definition.rs index 800ce06e8..c00fe4cb1 100644 --- a/stack-graphs/tests/it/c/can_jump_to_definition.rs +++ b/stack-graphs/tests/it/c/can_jump_to_definition.rs @@ -15,6 +15,7 @@ use stack_graphs::c::sg_partial_path_list_count; use stack_graphs::c::sg_partial_path_list_free; use stack_graphs::c::sg_partial_path_list_new; use stack_graphs::c::sg_partial_path_list_paths; +use stack_graphs::c::sg_stitcher_config; use stack_graphs::partial::PartialPath; use crate::c::test_graph::TestGraph; @@ -28,12 +29,16 @@ fn check_jump_to_definition(graph: &TestGraph, expected_paths: &[&str]) { .iter_nodes() .filter(|handle| rust_graph[*handle].is_reference()) .collect::>(); + let config = sg_stitcher_config { + detect_similar_paths: false, + }; sg_partial_path_arena_find_all_complete_paths( graph.graph, paths, references.len(), references.as_ptr() as *const _, path_list, + config, std::ptr::null(), ); diff --git a/stack-graphs/tests/it/c/can_jump_to_definition_with_phased_partial_path_stitching.rs b/stack-graphs/tests/it/c/can_jump_to_definition_with_phased_partial_path_stitching.rs index 615ffedd5..3a23ab763 100644 --- a/stack-graphs/tests/it/c/can_jump_to_definition_with_phased_partial_path_stitching.rs +++ b/stack-graphs/tests/it/c/can_jump_to_definition_with_phased_partial_path_stitching.rs @@ -27,6 +27,7 @@ use stack_graphs::c::sg_partial_path_list_free; use stack_graphs::c::sg_partial_path_list_new; use stack_graphs::c::sg_partial_path_list_paths; use stack_graphs::c::sg_stack_graph; +use stack_graphs::c::sg_stitcher_config; use stack_graphs::copious_debugging; use stack_graphs::partial::PartialPath; use stack_graphs::partial::PartialScopeStackBindings; @@ -49,11 +50,15 @@ impl StorageLayer { let rust_graph = unsafe { &(*graph).inner }; let path_list = sg_partial_path_list_new(); for file in rust_graph.iter_files() { + let config = sg_stitcher_config { + detect_similar_paths: false, + }; sg_partial_path_arena_find_partial_paths_in_file( graph, partials, file.as_u32(), path_list, + config, std::ptr::null(), ); } @@ -129,11 +134,15 @@ fn check_jump_to_definition(graph: &TestGraph, file: &str, expected_partial_path }); // Create the forward partial path stitcher. + let config = sg_stitcher_config { + detect_similar_paths: false, + }; let stitcher = sg_forward_partial_path_stitcher_from_nodes( graph.graph, partials, references.len(), references.as_ptr() as *const _, + config, ); sg_forward_partial_path_stitcher_set_max_work_per_phase(stitcher, 1); let rust_stitcher = unsafe { &mut *stitcher }; diff --git a/stack-graphs/tests/it/can_find_local_nodes.rs b/stack-graphs/tests/it/can_find_local_nodes.rs index 105e5a795..6b763b0d7 100644 --- a/stack-graphs/tests/it/can_find_local_nodes.rs +++ b/stack-graphs/tests/it/can_find_local_nodes.rs @@ -12,6 +12,7 @@ use stack_graphs::graph::StackGraph; use stack_graphs::partial::PartialPaths; use stack_graphs::stitching::Database; use stack_graphs::stitching::ForwardPartialPathStitcher; +use stack_graphs::stitching::StitcherConfig; use stack_graphs::NoCancellation; use crate::test_graphs; @@ -24,6 +25,7 @@ fn check_local_nodes(graph: &StackGraph, file: &str, expected_local_nodes: &[&st graph, &mut partials, file, + &StitcherConfig::default(), &NoCancellation, |graph, partials, path| { database.add_partial_path(graph, partials, path.clone()); diff --git a/stack-graphs/tests/it/can_find_node_partial_paths_in_database.rs b/stack-graphs/tests/it/can_find_node_partial_paths_in_database.rs index 3ccea9548..22d311d34 100644 --- a/stack-graphs/tests/it/can_find_node_partial_paths_in_database.rs +++ b/stack-graphs/tests/it/can_find_node_partial_paths_in_database.rs @@ -15,6 +15,7 @@ use stack_graphs::partial::PartialPath; use stack_graphs::partial::PartialPaths; use stack_graphs::stitching::Database; use stack_graphs::stitching::ForwardPartialPathStitcher; +use stack_graphs::stitching::StitcherConfig; use stack_graphs::NoCancellation; use crate::test_graphs; @@ -33,6 +34,7 @@ fn check_node_partial_paths( graph, &mut partials, file, + &StitcherConfig::default(), &NoCancellation, |graph, partials, path| { db.add_partial_path(graph, partials, path.clone()); diff --git a/stack-graphs/tests/it/can_find_partial_paths_in_file.rs b/stack-graphs/tests/it/can_find_partial_paths_in_file.rs index abba6951b..d3e48e33f 100644 --- a/stack-graphs/tests/it/can_find_partial_paths_in_file.rs +++ b/stack-graphs/tests/it/can_find_partial_paths_in_file.rs @@ -10,7 +10,7 @@ use std::collections::BTreeSet; use pretty_assertions::assert_eq; use stack_graphs::graph::StackGraph; use stack_graphs::partial::PartialPaths; -use stack_graphs::stitching::ForwardPartialPathStitcher; +use stack_graphs::stitching::{ForwardPartialPathStitcher, StitcherConfig}; use stack_graphs::NoCancellation; use crate::test_graphs; @@ -23,6 +23,7 @@ fn check_partial_paths_in_file(graph: &StackGraph, file: &str, expected_paths: & graph, &mut partials, file, + &StitcherConfig::default(), &NoCancellation, |graph, partials, path| { results.insert(path.display(graph, partials).to_string()); diff --git a/stack-graphs/tests/it/can_find_root_partial_paths_in_database.rs b/stack-graphs/tests/it/can_find_root_partial_paths_in_database.rs index a70336333..616f9655c 100644 --- a/stack-graphs/tests/it/can_find_root_partial_paths_in_database.rs +++ b/stack-graphs/tests/it/can_find_root_partial_paths_in_database.rs @@ -17,6 +17,7 @@ use stack_graphs::partial::PartialScopedSymbol; use stack_graphs::partial::PartialSymbolStack; use stack_graphs::stitching::Database; use stack_graphs::stitching::ForwardPartialPathStitcher; +use stack_graphs::stitching::StitcherConfig; use stack_graphs::NoCancellation; use crate::test_graphs; @@ -34,6 +35,7 @@ fn check_root_partial_paths( graph, &mut partials, file, + &StitcherConfig::default(), &NoCancellation, |graph, partials, path| { db.add_partial_path(graph, partials, path.clone()); diff --git a/stack-graphs/tests/it/can_jump_to_definition.rs b/stack-graphs/tests/it/can_jump_to_definition.rs index 965207f4c..db288855c 100644 --- a/stack-graphs/tests/it/can_jump_to_definition.rs +++ b/stack-graphs/tests/it/can_jump_to_definition.rs @@ -12,6 +12,7 @@ use stack_graphs::graph::StackGraph; use stack_graphs::partial::PartialPaths; use stack_graphs::stitching::ForwardPartialPathStitcher; use stack_graphs::stitching::GraphEdgeCandidates; +use stack_graphs::stitching::StitcherConfig; use stack_graphs::NoCancellation; use crate::test_graphs; @@ -25,6 +26,7 @@ fn check_jump_to_definition(graph: &StackGraph, expected_paths: &[&str]) { ForwardPartialPathStitcher::find_all_complete_partial_paths( &mut GraphEdgeCandidates::new(graph, &mut paths, None), references, + &StitcherConfig::default(), &NoCancellation, |graph, paths, path| { results.insert(path.display(graph, paths).to_string()); diff --git a/stack-graphs/tests/it/can_jump_to_definition_with_forward_partial_path_stitching.rs b/stack-graphs/tests/it/can_jump_to_definition_with_forward_partial_path_stitching.rs index d45e96afc..32e225a9c 100644 --- a/stack-graphs/tests/it/can_jump_to_definition_with_forward_partial_path_stitching.rs +++ b/stack-graphs/tests/it/can_jump_to_definition_with_forward_partial_path_stitching.rs @@ -13,6 +13,7 @@ use stack_graphs::partial::PartialPaths; use stack_graphs::stitching::Database; use stack_graphs::stitching::DatabaseCandidates; use stack_graphs::stitching::ForwardPartialPathStitcher; +use stack_graphs::stitching::StitcherConfig; use stack_graphs::NoCancellation; use crate::test_graphs; @@ -27,6 +28,7 @@ fn check_jump_to_definition(graph: &StackGraph, expected_partial_paths: &[&str]) graph, &mut partials, file, + &StitcherConfig::default(), &NoCancellation, |graph, partials, path| { db.add_partial_path(graph, partials, path.clone()); @@ -42,6 +44,7 @@ fn check_jump_to_definition(graph: &StackGraph, expected_partial_paths: &[&str]) ForwardPartialPathStitcher::find_all_complete_partial_paths( &mut DatabaseCandidates::new(graph, &mut partials, &mut db), references, + &StitcherConfig::default(), &NoCancellation, |_, _, p| { complete_partial_paths.push(p.clone()); diff --git a/stack-graphs/tests/it/cycles.rs b/stack-graphs/tests/it/cycles.rs index 12c8b5672..abcd43048 100644 --- a/stack-graphs/tests/it/cycles.rs +++ b/stack-graphs/tests/it/cycles.rs @@ -16,6 +16,7 @@ use stack_graphs::partial::PartialPaths; use stack_graphs::stitching::Database; use stack_graphs::stitching::ForwardPartialPathStitcher; use stack_graphs::stitching::GraphEdges; +use stack_graphs::stitching::StitcherConfig; use stack_graphs::CancelAfterDuration; use std::time::Duration; @@ -199,6 +200,7 @@ fn finding_simple_identity_cycle_is_detected() { &graph, &mut partials, file, + &StitcherConfig::default(), &cancellation_flag, |_, _, _| path_count += 1, ); @@ -292,6 +294,7 @@ fn finding_composite_identity_cycle_is_detected() { &graph, &mut partials, file, + &StitcherConfig::default(), &cancellation_flag, |_, _, _| path_count += 1, ); @@ -353,6 +356,7 @@ fn appending_eliminating_cycle_terminates() { &graph, &mut partials, file, + &StitcherConfig::default(), &cancellation_flag, |_, _, _| path_count += 1, ); diff --git a/stack-graphs/tests/it/serde.rs b/stack-graphs/tests/it/serde.rs index 04679f353..c9c047d10 100644 --- a/stack-graphs/tests/it/serde.rs +++ b/stack-graphs/tests/it/serde.rs @@ -12,7 +12,7 @@ use stack_graphs::graph; use stack_graphs::graph::StackGraph; use stack_graphs::partial::PartialPaths; use stack_graphs::serde; -use stack_graphs::stitching::{Database, ForwardPartialPathStitcher}; +use stack_graphs::stitching::{Database, ForwardPartialPathStitcher, StitcherConfig}; use stack_graphs::NoCancellation; use crate::test_graphs; @@ -984,6 +984,7 @@ fn can_serialize_partial_paths() { &graph, &mut partials, file, + &StitcherConfig::default(), &NoCancellation, |g, ps, p| { db.add_partial_path(g, ps, p.clone()); diff --git a/tree-sitter-stack-graphs/src/cli/index.rs b/tree-sitter-stack-graphs/src/cli/index.rs index a90056f4b..882185108 100644 --- a/tree-sitter-stack-graphs/src/cli/index.rs +++ b/tree-sitter-stack-graphs/src/cli/index.rs @@ -316,6 +316,16 @@ impl<'a> Indexer<'a> { .add_file(&source_path.to_string_lossy()) .expect("file not present in empty graph"); + // FIXME We use the stitching config of the primary language here, which is not quite right. + // Ideally we'd use the primary language's stitcher config for computing the paths in + // the primary graph, and similarly for the secondary languages. However, that is not + // much use at the moment, since we use paths from all languages when reading from the + // database. Fixing this properly requires more isolation of graph and paths per language. + let stitcher_config = lcs + .primary + .map(|lc| lc.stitcher_config.clone()) + .unwrap_or_default(); + let result = Self::build_stack_graph( &mut graph, file, @@ -355,6 +365,7 @@ impl<'a> Indexer<'a> { &graph, &mut partials, file, + &stitcher_config, &(&cancellation_flag as &dyn CancellationFlag), |_g, _ps, p| { paths.push(p.clone()); diff --git a/tree-sitter-stack-graphs/src/cli/init.rs b/tree-sitter-stack-graphs/src/cli/init.rs index e87785477..c287909e6 100644 --- a/tree-sitter-stack-graphs/src/cli/init.rs +++ b/tree-sitter-stack-graphs/src/cli/init.rs @@ -721,6 +721,7 @@ impl ProjectSettings<'_> { use tree_sitter_stack_graphs::loader::FileAnalyzers; use tree_sitter_stack_graphs::loader::LanguageConfiguration; use tree_sitter_stack_graphs::loader::LoadError; + use tree_sitter_stack_graphs::loader::StitcherConfig; use tree_sitter_stack_graphs::CancellationFlag; /// The stack graphs tsg source for this language. @@ -758,6 +759,7 @@ impl ProjectSettings<'_> { )), Some(STACK_GRAPHS_BUILTINS_CONFIG), FileAnalyzers::new(), + StitcherConfig::default(), cancellation_flag, ) }} diff --git a/tree-sitter-stack-graphs/src/cli/query.rs b/tree-sitter-stack-graphs/src/cli/query.rs index 08ede0b93..3a9c4d47f 100644 --- a/tree-sitter-stack-graphs/src/cli/query.rs +++ b/tree-sitter-stack-graphs/src/cli/query.rs @@ -10,6 +10,7 @@ use clap::Parser; use clap::Subcommand; use clap::ValueHint; use stack_graphs::stitching::ForwardPartialPathStitcher; +use stack_graphs::stitching::StitcherConfig; use stack_graphs::storage::FileStatus; use stack_graphs::storage::SQLiteReader; use std::path::Path; @@ -185,9 +186,16 @@ impl<'a> Querier<'a> { }; let mut reference_paths = Vec::new(); + // FIXME We use the default stitcher config here, because we currently do not retrieve language + // configurations during querying. A first step to fixing this would be to lookup the language + // corresponding to the file that is queried. However, to solve this properly we would need to + // isolate graphs and paths per language so we don't accidently mix different languages that + // require different stitcher settings. + let config = StitcherConfig::default(); if let Err(err) = ForwardPartialPathStitcher::find_all_complete_partial_paths( self.db, std::iter::once(node), + &config, &cancellation_flag, |_g, _ps, p| { reference_paths.push(p.clone()); diff --git a/tree-sitter-stack-graphs/src/cli/test.rs b/tree-sitter-stack-graphs/src/cli/test.rs index 4bcb47171..e4bd690a5 100644 --- a/tree-sitter-stack-graphs/src/cli/test.rs +++ b/tree-sitter-stack-graphs/src/cli/test.rs @@ -18,6 +18,7 @@ use stack_graphs::serde::Filter; use stack_graphs::stitching::Database; use stack_graphs::stitching::DatabaseCandidates; use stack_graphs::stitching::ForwardPartialPathStitcher; +use stack_graphs::stitching::StitcherConfig; use std::path::Path; use std::path::PathBuf; use std::time::Duration; @@ -331,13 +332,19 @@ impl TestArgs { &test.graph, &mut partials, file, + &lc.stitcher_config, &cancellation_flag.as_ref(), |g, ps, p| { db.add_partial_path(g, ps, p.clone()); }, )?; } - let result = test.run(&mut partials, &mut db, cancellation_flag.as_ref())?; + let result = test.run( + &mut partials, + &mut db, + &lc.stitcher_config, + cancellation_flag.as_ref(), + )?; let success = result.failure_count() == 0; let outputs = if self.output_mode.test(!success) { let files = test.fragments.iter().map(|f| f.file).collect::>(); @@ -349,6 +356,7 @@ impl TestArgs { &mut db, &|_: &StackGraph, h: &Handle| files.contains(h), success, + &lc.stitcher_config, cancellation_flag.as_ref(), )? } else { @@ -397,6 +405,7 @@ impl TestArgs { db: &mut Database, filter: &dyn Filter, success: bool, + stitcher_config: &StitcherConfig, cancellation_flag: &dyn CancellationFlag, ) -> anyhow::Result> { let mut outputs = Vec::with_capacity(3); @@ -425,7 +434,14 @@ impl TestArgs { } let mut db = if save_paths.is_some() || save_visualization.is_some() { - self.compute_paths(graph, partials, db, filter, cancellation_flag)? + self.compute_paths( + graph, + partials, + db, + filter, + stitcher_config, + cancellation_flag, + )? } else { Database::new() }; @@ -474,6 +490,7 @@ impl TestArgs { partials: &mut PartialPaths, db: &mut Database, filter: &dyn Filter, + stitcher_config: &StitcherConfig, cancellation_flag: &dyn CancellationFlag, ) -> anyhow::Result { let references = graph @@ -484,6 +501,7 @@ impl TestArgs { ForwardPartialPathStitcher::find_all_complete_partial_paths( &mut DatabaseCandidates::new(graph, partials, db), references.clone(), + stitcher_config, &cancellation_flag, |_, _, p| { paths.push(p.clone()); diff --git a/tree-sitter-stack-graphs/src/cli/visualize.rs b/tree-sitter-stack-graphs/src/cli/visualize.rs index e0665c353..acffbc9ce 100644 --- a/tree-sitter-stack-graphs/src/cli/visualize.rs +++ b/tree-sitter-stack-graphs/src/cli/visualize.rs @@ -10,6 +10,7 @@ use clap::ValueHint; use stack_graphs::serde::NoFilter; use stack_graphs::stitching::Database; use stack_graphs::stitching::ForwardPartialPathStitcher; +use stack_graphs::stitching::StitcherConfig; use stack_graphs::storage::SQLiteReader; use stack_graphs::NoCancellation; use std::path::Path; @@ -54,9 +55,16 @@ impl VisualizeArgs { .filter(|n| graph[*n].is_reference()) .collect::>(); let mut complete_paths_db = Database::new(); + // FIXME We use the default stitcher config here, because we currently do not retrieve language + // configurations during querying. A first step to fixing this would be to lookup the language + // corresponding to the file that is queried. However, to solve this properly we would need to + // isolate graphs and paths per language so we don't accidently mix different languages that + // require different stitcher settings. + let config = StitcherConfig::default(); ForwardPartialPathStitcher::find_all_complete_partial_paths( &mut db, starting_nodes, + &config, cancellation_flag, |g, ps, p| { complete_paths_db.add_partial_path(g, ps, p.clone()); diff --git a/tree-sitter-stack-graphs/src/loader.rs b/tree-sitter-stack-graphs/src/loader.rs index 0feb6a28c..6464687fe 100644 --- a/tree-sitter-stack-graphs/src/loader.rs +++ b/tree-sitter-stack-graphs/src/loader.rs @@ -26,6 +26,8 @@ use tree_sitter_loader::Config as TsConfig; use tree_sitter_loader::LanguageConfiguration as TSLanguageConfiguration; use tree_sitter_loader::Loader as TsLoader; +pub use stack_graphs::stitching::StitcherConfig; + use crate::CancellationFlag; use crate::FileAnalyzer; use crate::StackGraphLanguage; @@ -44,6 +46,7 @@ pub struct LanguageConfiguration { pub sgl: StackGraphLanguage, pub builtins: StackGraph, pub special_files: FileAnalyzers, + pub stitcher_config: StitcherConfig, } impl LanguageConfiguration { @@ -59,6 +62,7 @@ impl LanguageConfiguration { builtins_source: Option<(PathBuf, &'a str)>, builtins_config: Option<&str>, special_files: FileAnalyzers, + stitcher_config: StitcherConfig, cancellation_flag: &dyn CancellationFlag, ) -> Result> { let sgl = StackGraphLanguage::from_source(language, tsg_path.clone(), tsg_source).map_err( @@ -98,6 +102,7 @@ impl LanguageConfiguration { sgl, builtins, special_files, + stitcher_config, }) } @@ -568,6 +573,7 @@ impl PathLoader { sgl, builtins, special_files: FileAnalyzers::new(), + stitcher_config: StitcherConfig::default(), // FIXME we do not have a place for this configuration }; self.cache.push((language.language, lc)); diff --git a/tree-sitter-stack-graphs/src/test.rs b/tree-sitter-stack-graphs/src/test.rs index 19abda61d..8f3c20c29 100644 --- a/tree-sitter-stack-graphs/src/test.rs +++ b/tree-sitter-stack-graphs/src/test.rs @@ -74,6 +74,7 @@ use stack_graphs::graph::SourceInfo; use stack_graphs::graph::StackGraph; use stack_graphs::partial::PartialPaths; use stack_graphs::stitching::Database; +use stack_graphs::stitching::StitcherConfig; use std::collections::HashMap; use std::path::Path; use std::path::PathBuf; @@ -623,13 +624,20 @@ impl Test { &mut self, partials: &mut PartialPaths, db: &mut Database, + stitcher_config: &StitcherConfig, cancellation_flag: &dyn CancellationFlag, ) -> Result { let mut result = TestResult::new(); for fragment in &self.fragments { for assertion in &fragment.assertions { match assertion - .run(&self.graph, partials, db, &cancellation_flag) + .run( + &self.graph, + partials, + db, + stitcher_config, + &cancellation_flag, + ) .map_or_else(|e| self.from_error(e), |v| Ok(v)) { Ok(_) => result.add_success(), diff --git a/tree-sitter-stack-graphs/tests/it/loader.rs b/tree-sitter-stack-graphs/tests/it/loader.rs index 8fcf0ccac..7525b6fcf 100644 --- a/tree-sitter-stack-graphs/tests/it/loader.rs +++ b/tree-sitter-stack-graphs/tests/it/loader.rs @@ -8,6 +8,7 @@ use once_cell::sync::Lazy; use pretty_assertions::assert_eq; use stack_graphs::graph::StackGraph; +use stack_graphs::stitching::StitcherConfig; use std::path::PathBuf; use tree_sitter_stack_graphs::loader::FileAnalyzers; use tree_sitter_stack_graphs::loader::LanguageConfiguration; @@ -35,6 +36,7 @@ fn can_load_from_provided_language_configuration() { sgl, builtins: StackGraph::new(), special_files: FileAnalyzers::new(), + stitcher_config: StitcherConfig::default(), }; let mut loader = Loader::from_language_configurations(vec![lc], None).expect("Expected loader to succeed"); diff --git a/tree-sitter-stack-graphs/tests/it/test.rs b/tree-sitter-stack-graphs/tests/it/test.rs index b281f283b..483ad011b 100644 --- a/tree-sitter-stack-graphs/tests/it/test.rs +++ b/tree-sitter-stack-graphs/tests/it/test.rs @@ -13,6 +13,7 @@ use stack_graphs::graph::StackGraph; use stack_graphs::partial::PartialPaths; use stack_graphs::stitching::Database; use stack_graphs::stitching::ForwardPartialPathStitcher; +use stack_graphs::stitching::StitcherConfig; use std::path::Path; use std::path::PathBuf; use tree_sitter_graph::Variables; @@ -113,6 +114,7 @@ fn check_test( &test.graph, &mut partials, fragment.file, + &StitcherConfig::default(), &stack_graphs::NoCancellation, |graph, partials, path| { db.add_partial_path(graph, partials, path.clone()); @@ -122,7 +124,12 @@ fn check_test( } let results = test - .run(&mut partials, &mut db, &NoCancellation) + .run( + &mut partials, + &mut db, + &StitcherConfig::default(), + &NoCancellation, + ) .expect("should never be cancelled"); assert_eq!( expected_successes, From 2216fda05fd72da4accca88df8edb4db1ffbc013 Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Mon, 13 Nov 2023 11:59:07 +0100 Subject: [PATCH 057/291] Use stitcher config only for functions that wrap a stitcher, not for stitcher itself --- .../tree-sitter-stack-graphs-java/rust/lib.rs | 4 - .../rust/lib.rs | 2 - stack-graphs/include/stack-graphs.h | 10 +- stack-graphs/src/assert.rs | 4 +- stack-graphs/src/c.rs | 21 ++-- stack-graphs/src/stitching.rs | 113 ++++++++++-------- .../tests/it/c/can_find_local_nodes.rs | 4 +- .../it/c/can_find_partial_paths_in_file.rs | 4 +- ...ions_with_phased_partial_path_stitching.rs | 8 +- .../tests/it/c/can_jump_to_definition.rs | 4 +- ...tion_with_phased_partial_path_stitching.rs | 8 +- stack-graphs/tests/it/can_find_local_nodes.rs | 2 +- ...can_find_node_partial_paths_in_database.rs | 2 +- .../it/can_find_partial_paths_in_file.rs | 2 +- ...can_find_root_partial_paths_in_database.rs | 2 +- .../tests/it/can_jump_to_definition.rs | 2 +- ...ion_with_forward_partial_path_stitching.rs | 4 +- stack-graphs/tests/it/cycles.rs | 6 +- stack-graphs/tests/it/serde.rs | 2 +- tree-sitter-stack-graphs/src/cli/index.rs | 13 +- tree-sitter-stack-graphs/src/cli/query.rs | 8 +- tree-sitter-stack-graphs/src/cli/test.rs | 10 +- tree-sitter-stack-graphs/src/cli/visualize.rs | 8 +- tree-sitter-stack-graphs/src/loader.rs | 6 - tree-sitter-stack-graphs/src/test.rs | 2 +- tree-sitter-stack-graphs/tests/it/loader.rs | 2 - tree-sitter-stack-graphs/tests/it/test.rs | 4 +- 27 files changed, 110 insertions(+), 147 deletions(-) diff --git a/languages/tree-sitter-stack-graphs-java/rust/lib.rs b/languages/tree-sitter-stack-graphs-java/rust/lib.rs index d1af5bb12..09cb043a5 100644 --- a/languages/tree-sitter-stack-graphs-java/rust/lib.rs +++ b/languages/tree-sitter-stack-graphs-java/rust/lib.rs @@ -1,7 +1,6 @@ use tree_sitter_stack_graphs::loader::FileAnalyzers; use tree_sitter_stack_graphs::loader::LanguageConfiguration; use tree_sitter_stack_graphs::loader::LoadError; -use tree_sitter_stack_graphs::loader::StitcherConfig; use tree_sitter_stack_graphs::CancellationFlag; /// The stacks graphs tsg path for this language. @@ -41,9 +40,6 @@ pub fn try_language_configuration( )), Some(STACK_GRAPHS_BUILTINS_CONFIG), FileAnalyzers::new(), - StitcherConfig { - detect_similar_paths: true, - }, cancellation_flag, ) } diff --git a/languages/tree-sitter-stack-graphs-typescript/rust/lib.rs b/languages/tree-sitter-stack-graphs-typescript/rust/lib.rs index 9ef2f1805..45ce6d255 100644 --- a/languages/tree-sitter-stack-graphs-typescript/rust/lib.rs +++ b/languages/tree-sitter-stack-graphs-typescript/rust/lib.rs @@ -8,7 +8,6 @@ use tree_sitter_stack_graphs::loader::FileAnalyzers; use tree_sitter_stack_graphs::loader::LanguageConfiguration; use tree_sitter_stack_graphs::loader::LoadError; -use tree_sitter_stack_graphs::loader::StitcherConfig; use tree_sitter_stack_graphs::CancellationFlag; use crate::npm_package::NpmPackageAnalyzer; @@ -57,7 +56,6 @@ pub fn try_language_configuration( FileAnalyzers::new() .add("tsconfig.json".to_string(), TsConfigAnalyzer {}) .add("package.json".to_string(), NpmPackageAnalyzer {}), - StitcherConfig::default(), cancellation_flag, ) } diff --git a/stack-graphs/include/stack-graphs.h b/stack-graphs/include/stack-graphs.h index a1afe93dd..aeac9e636 100644 --- a/stack-graphs/include/stack-graphs.h +++ b/stack-graphs/include/stack-graphs.h @@ -738,7 +738,7 @@ enum sg_result sg_partial_path_arena_find_partial_paths_in_file(const struct sg_ struct sg_partial_path_arena *partials, sg_file_handle file, struct sg_partial_path_list *partial_path_list, - struct sg_stitcher_config config, + const struct sg_stitcher_config *stitcher_config, const size_t *cancellation_flag); // Finds all complete paths reachable from a set of starting nodes, placing the result into the @@ -754,7 +754,7 @@ enum sg_result sg_partial_path_arena_find_all_complete_paths(const struct sg_sta size_t starting_node_count, const sg_node_handle *starting_nodes, struct sg_partial_path_list *path_list, - struct sg_stitcher_config config, + const struct sg_stitcher_config *stitcher_config, const size_t *cancellation_flag); // Returns a reference to the array of partial path data in this partial path database. The @@ -812,16 +812,14 @@ struct sg_node_handle_set sg_partial_path_database_local_nodes(const struct sg_p struct sg_forward_partial_path_stitcher *sg_forward_partial_path_stitcher_from_nodes(const struct sg_stack_graph *graph, struct sg_partial_path_arena *partials, size_t count, - const sg_node_handle *starting_nodes, - struct sg_stitcher_config config); + const sg_node_handle *starting_nodes); // Creates a new forward partial path stitcher that is "seeded" with a set of initial partial // paths. struct sg_forward_partial_path_stitcher *sg_forward_partial_path_stitcher_from_partial_paths(const struct sg_stack_graph *graph, struct sg_partial_path_arena *partials, size_t count, - const struct sg_partial_path *initial_partial_paths, - struct sg_stitcher_config config); + const struct sg_partial_path *initial_partial_paths); // Sets whether similar path detection should be enabled during path stitching. Paths are similar // if start and end node, and pre- and postconditions are the same. The presence of similar paths diff --git a/stack-graphs/src/assert.rs b/stack-graphs/src/assert.rs index 278caabbc..6e1190673 100644 --- a/stack-graphs/src/assert.rs +++ b/stack-graphs/src/assert.rs @@ -150,7 +150,7 @@ impl Assertion { graph: &StackGraph, partials: &mut PartialPaths, db: &mut Database, - stitcher_config: &StitcherConfig, + stitcher_config: StitcherConfig, cancellation_flag: &dyn CancellationFlag, ) -> Result<(), AssertionError> { match self { @@ -175,7 +175,7 @@ impl Assertion { db: &mut Database, source: &AssertionSource, expected_targets: &Vec, - stitcher_config: &StitcherConfig, + stitcher_config: StitcherConfig, cancellation_flag: &dyn CancellationFlag, ) -> Result<(), AssertionError> { let references = source.iter_references(graph).collect::>(); diff --git a/stack-graphs/src/c.rs b/stack-graphs/src/c.rs index 4f58caff2..26a823eba 100644 --- a/stack-graphs/src/c.rs +++ b/stack-graphs/src/c.rs @@ -1174,20 +1174,21 @@ pub extern "C" fn sg_partial_path_arena_find_partial_paths_in_file( partials: *mut sg_partial_path_arena, file: sg_file_handle, partial_path_list: *mut sg_partial_path_list, - config: sg_stitcher_config, + stitcher_config: *const sg_stitcher_config, cancellation_flag: *const usize, ) -> sg_result { let graph = unsafe { &(*graph).inner }; let partials = unsafe { &mut (*partials).inner }; let file = file.into(); let partial_path_list = unsafe { &mut *partial_path_list }; + let stitcher_config = unsafe { *stitcher_config }; let cancellation_flag: Option<&AtomicUsize> = unsafe { std::mem::transmute(cancellation_flag.as_ref()) }; ForwardPartialPathStitcher::find_minimal_partial_path_set_in_file( graph, partials, file, - &config.into(), + stitcher_config.into(), &AtomicUsizeCancellationFlag(cancellation_flag), |_graph, partials, path| { let mut path = path.clone(); @@ -1213,19 +1214,20 @@ pub extern "C" fn sg_partial_path_arena_find_all_complete_paths( starting_node_count: usize, starting_nodes: *const sg_node_handle, path_list: *mut sg_partial_path_list, - config: sg_stitcher_config, + stitcher_config: *const sg_stitcher_config, cancellation_flag: *const usize, ) -> sg_result { let graph = unsafe { &(*graph).inner }; let partials = unsafe { &mut (*partials).inner }; let starting_nodes = unsafe { std::slice::from_raw_parts(starting_nodes, starting_node_count) }; + let stitcher_config = unsafe { *stitcher_config }; let path_list = unsafe { &mut *path_list }; let cancellation_flag: Option<&AtomicUsize> = unsafe { std::mem::transmute(cancellation_flag.as_ref()) }; ForwardPartialPathStitcher::find_all_complete_partial_paths( &mut GraphEdgeCandidates::new(graph, partials, None), starting_nodes.iter().copied().map(sg_node_handle::into), - &config.into(), + stitcher_config.into(), &AtomicUsizeCancellationFlag(cancellation_flag), |graph, _partials, path| { if path.is_complete(graph) { @@ -1398,6 +1400,7 @@ pub struct sg_forward_partial_path_stitcher { // Configuration for partial path stitchers. #[repr(C)] +#[derive(Clone, Copy)] pub struct sg_stitcher_config { /// Enables similar path detection during stiching. pub detect_similar_paths: bool, @@ -1461,7 +1464,6 @@ pub extern "C" fn sg_forward_partial_path_stitcher_from_nodes( partials: *mut sg_partial_path_arena, count: usize, starting_nodes: *const sg_node_handle, - config: sg_stitcher_config, ) -> *mut sg_forward_partial_path_stitcher { let graph = unsafe { &(*graph).inner }; let partials = unsafe { &mut (*partials).inner }; @@ -1476,12 +1478,7 @@ pub extern "C" fn sg_forward_partial_path_stitcher_from_nodes( p }) .collect::>(); - let stitcher = ForwardPartialPathStitcher::from_partial_paths( - graph, - partials, - initial_paths, - &config.into(), - ); + let stitcher = ForwardPartialPathStitcher::from_partial_paths(graph, partials, initial_paths); Box::into_raw(Box::new(InternalForwardPartialPathStitcher::new( stitcher, partials, ))) as *mut _ @@ -1495,7 +1492,6 @@ pub extern "C" fn sg_forward_partial_path_stitcher_from_partial_paths( partials: *mut sg_partial_path_arena, count: usize, initial_partial_paths: *const sg_partial_path, - config: sg_stitcher_config, ) -> *mut sg_forward_partial_path_stitcher { let graph = unsafe { &(*graph).inner }; let partials = unsafe { &mut (*partials).inner }; @@ -1505,7 +1501,6 @@ pub extern "C" fn sg_forward_partial_path_stitcher_from_partial_paths( graph, partials, initial_partial_paths.to_vec(), - &config.into(), ); Box::into_raw(Box::new(InternalForwardPartialPathStitcher::new( stitcher, partials, diff --git a/stack-graphs/src/stitching.rs b/stack-graphs/src/stitching.rs index dc7cb8c9b..1432669dd 100644 --- a/stack-graphs/src/stitching.rs +++ b/stack-graphs/src/stitching.rs @@ -795,13 +795,6 @@ impl<'a> Display for DisplaySymbolStackKey<'a> { //------------------------------------------------------------------------------------------------- // Stitching partial paths together -/// Configuration for partial path stitchers. -#[derive(Clone, Debug, Default)] -pub struct StitcherConfig { - /// Enables similar path detection during path stitching. - pub detect_similar_paths: bool, -} - /// Implements a phased forward partial path stitching algorithm. /// /// Our overall goal is to start with a set of _seed_ partial paths, and to repeatedly extend each @@ -858,7 +851,6 @@ impl ForwardPartialPathStitcher { _graph: &StackGraph, _partials: &mut PartialPaths, initial_partial_paths: I, - config: &StitcherConfig, ) -> Self where I: IntoIterator, @@ -871,16 +863,14 @@ impl ForwardPartialPathStitcher { (p, c, false) }) .multiunzip(); - let similar_path_detector = - Some(SimilarPathDetector::new()).filter(|_| config.detect_similar_paths); - ForwardPartialPathStitcher { + Self { candidates: Vec::new(), extensions: Vec::new(), queue: VecDeque::new(), initial_paths: next_iteration.0.len(), next_iteration, appended_paths, - similar_path_detector, + similar_path_detector: Some(SimilarPathDetector::new()), // By default, all nodes are checked for cycles and (if enabled) similarity check_only_join_nodes: false, // By default, there's no artificial bound on the amount of work done per phase @@ -889,28 +879,6 @@ impl ForwardPartialPathStitcher { phase_number: 1, } } -} - -impl ForwardPartialPathStitcher { - /// Returns an iterator of all of the (possibly incomplete) partial paths that were encountered - /// during the most recent phase of the algorithm. - pub fn previous_phase_partial_paths(&self) -> impl Iterator + '_ { - self.next_iteration.0.iter() - } - - /// Returns a slice of all of the (possibly incomplete) partial paths that were encountered - /// during the most recent phase of the algorithm. - pub fn previous_phase_partial_paths_slice(&mut self) -> &[PartialPath] { - self.next_iteration.0.make_contiguous(); - self.next_iteration.0.as_slices().0 - } - - /// Returns a mutable slice of all of the (possibly incomplete) partial paths that were - /// encountered during the most recent phase of the algorithm. - pub fn previous_phase_partial_paths_slice_mut(&mut self) -> &mut [PartialPath] { - self.next_iteration.0.make_contiguous(); - self.next_iteration.0.as_mut_slices().0 - } /// Sets whether similar path detection should be enabled during path stitching. Paths are similar /// if start and end node, and pre- and postconditions are the same. The presence of similar paths @@ -940,6 +908,28 @@ impl ForwardPartialPathStitcher { pub fn set_max_work_per_phase(&mut self, max_work_per_phase: usize) { self.max_work_per_phase = max_work_per_phase; } +} + +impl ForwardPartialPathStitcher { + /// Returns an iterator of all of the (possibly incomplete) partial paths that were encountered + /// during the most recent phase of the algorithm. + pub fn previous_phase_partial_paths(&self) -> impl Iterator + '_ { + self.next_iteration.0.iter() + } + + /// Returns a slice of all of the (possibly incomplete) partial paths that were encountered + /// during the most recent phase of the algorithm. + pub fn previous_phase_partial_paths_slice(&mut self) -> &[PartialPath] { + self.next_iteration.0.make_contiguous(); + self.next_iteration.0.as_slices().0 + } + + /// Returns a mutable slice of all of the (possibly incomplete) partial paths that were + /// encountered during the most recent phase of the algorithm. + pub fn previous_phase_partial_paths_slice_mut(&mut self) -> &mut [PartialPath] { + self.next_iteration.0.make_contiguous(); + self.next_iteration.0.as_mut_slices().0 + } /// Attempts to extend one partial path as part of the algorithm. When calling this function, /// you are responsible for ensuring that `db` already contains all of the possible appendables @@ -1155,7 +1145,7 @@ impl ForwardPartialPathStitcher { graph: &StackGraph, partials: &mut PartialPaths, file: Handle, - config: &StitcherConfig, + config: StitcherConfig, cancellation_flag: &dyn CancellationFlag, mut visit: F, ) -> Result<(), CancellationError> @@ -1174,7 +1164,8 @@ impl ForwardPartialPathStitcher { .map(|node| PartialPath::from_node(graph, partials, node)) .collect::>(); let mut stitcher = - ForwardPartialPathStitcher::from_partial_paths(graph, partials, initial_paths, config); + ForwardPartialPathStitcher::from_partial_paths(graph, partials, initial_paths); + config.apply(&mut stitcher); stitcher.set_check_only_join_nodes(true); while !stitcher.is_complete() { cancellation_flag.check("finding complete partial paths")?; @@ -1207,7 +1198,7 @@ impl ForwardPartialPathStitcher { pub fn find_all_complete_partial_paths( candidates: &mut C, starting_nodes: I, - config: &StitcherConfig, + config: StitcherConfig, cancellation_flag: &dyn CancellationFlag, mut visit: F, ) -> Result<(), Err> @@ -1219,19 +1210,19 @@ impl ForwardPartialPathStitcher { F: FnMut(&StackGraph, &mut PartialPaths, &PartialPath), Err: std::convert::From, { - let mut stitcher = { - let (graph, partials, _) = candidates.get_graph_partials_and_db(); - let initial_paths = starting_nodes - .into_iter() - .filter(|n| graph[*n].is_reference()) - .map(|n| { - let mut p = PartialPath::from_node(graph, partials, n); - p.eliminate_precondition_stack_variables(partials); - p - }) - .collect::>(); - ForwardPartialPathStitcher::from_partial_paths(graph, partials, initial_paths, config) - }; + let (graph, partials, _) = candidates.get_graph_partials_and_db(); + let initial_paths = starting_nodes + .into_iter() + .filter(|n| graph[*n].is_reference()) + .map(|n| { + let mut p = PartialPath::from_node(graph, partials, n); + p.eliminate_precondition_stack_variables(partials); + p + }) + .collect::>(); + let mut stitcher = + ForwardPartialPathStitcher::from_partial_paths(graph, partials, initial_paths); + config.apply(&mut stitcher); stitcher.set_check_only_join_nodes(true); while !stitcher.is_complete() { cancellation_flag.check("finding complete partial paths")?; @@ -1249,3 +1240,25 @@ impl ForwardPartialPathStitcher { Ok(()) } } + +/// Configuration for partial path stitchers. +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct StitcherConfig { + /// Enables similar path detection during path stitching. + pub detect_similar_paths: bool, +} + +impl StitcherConfig { + fn apply(&self, stitcher: &mut ForwardPartialPathStitcher) { + stitcher.set_similar_path_detection(self.detect_similar_paths); + } +} + +impl Default for StitcherConfig { + fn default() -> Self { + Self { + detect_similar_paths: true, + } + } +} diff --git a/stack-graphs/tests/it/c/can_find_local_nodes.rs b/stack-graphs/tests/it/c/can_find_local_nodes.rs index 2d2ecdee5..9fbf31859 100644 --- a/stack-graphs/tests/it/c/can_find_local_nodes.rs +++ b/stack-graphs/tests/it/c/can_find_local_nodes.rs @@ -34,7 +34,7 @@ fn check_local_nodes(graph: &TestGraph, file: &str, expected_local_nodes: &[&str let partials = sg_partial_path_arena_new(); let path_list = sg_partial_path_list_new(); - let config = sg_stitcher_config { + let stitcher_config = sg_stitcher_config { detect_similar_paths: false, }; sg_partial_path_arena_find_partial_paths_in_file( @@ -42,7 +42,7 @@ fn check_local_nodes(graph: &TestGraph, file: &str, expected_local_nodes: &[&str partials, file.as_u32(), path_list, - config, + &stitcher_config, std::ptr::null(), ); diff --git a/stack-graphs/tests/it/c/can_find_partial_paths_in_file.rs b/stack-graphs/tests/it/c/can_find_partial_paths_in_file.rs index 65ab8e2c2..ae93f84be 100644 --- a/stack-graphs/tests/it/c/can_find_partial_paths_in_file.rs +++ b/stack-graphs/tests/it/c/can_find_partial_paths_in_file.rs @@ -78,7 +78,7 @@ fn check_partial_paths_in_file(graph: &TestGraph, file: &str, expected_paths: &[ let partials = sg_partial_path_arena_new(); let path_list = sg_partial_path_list_new(); - let config = sg_stitcher_config { + let stitcher_config = sg_stitcher_config { detect_similar_paths: false, }; sg_partial_path_arena_find_partial_paths_in_file( @@ -86,7 +86,7 @@ fn check_partial_paths_in_file(graph: &TestGraph, file: &str, expected_paths: &[ partials, file.as_u32(), path_list, - config, + &stitcher_config, std::ptr::null(), ); diff --git a/stack-graphs/tests/it/c/can_find_qualified_definitions_with_phased_partial_path_stitching.rs b/stack-graphs/tests/it/c/can_find_qualified_definitions_with_phased_partial_path_stitching.rs index fe8594dee..613a54556 100644 --- a/stack-graphs/tests/it/c/can_find_qualified_definitions_with_phased_partial_path_stitching.rs +++ b/stack-graphs/tests/it/c/can_find_qualified_definitions_with_phased_partial_path_stitching.rs @@ -56,7 +56,7 @@ impl StorageLayer { let rust_graph = unsafe { &(*graph).inner }; let path_list = sg_partial_path_list_new(); for file in rust_graph.iter_files() { - let config = sg_stitcher_config { + let stitcher_config = sg_stitcher_config { detect_similar_paths: false, }; sg_partial_path_arena_find_partial_paths_in_file( @@ -64,7 +64,7 @@ impl StorageLayer { partials, file.as_u32(), path_list, - config, + &stitcher_config, std::ptr::null(), ); } @@ -122,9 +122,6 @@ fn check_find_qualified_definitions( let partials = sg_partial_path_arena_new(); let rust_partials = unsafe { &mut (*partials).inner }; let db = sg_partial_path_database_new(); - let config = sg_stitcher_config { - detect_similar_paths: false, - }; // Create a new external storage layer holding _all_ of the partial paths in the stack graph. let mut storage_layer = StorageLayer::new(graph.graph, partials); @@ -157,7 +154,6 @@ fn check_find_qualified_definitions( partials, 1, &initial_partial_path as *const PartialPath as *const _, - config, ); sg_forward_partial_path_stitcher_set_max_work_per_phase(stitcher, 1); let rust_stitcher = unsafe { &mut *stitcher }; diff --git a/stack-graphs/tests/it/c/can_jump_to_definition.rs b/stack-graphs/tests/it/c/can_jump_to_definition.rs index c00fe4cb1..aeea163f6 100644 --- a/stack-graphs/tests/it/c/can_jump_to_definition.rs +++ b/stack-graphs/tests/it/c/can_jump_to_definition.rs @@ -29,7 +29,7 @@ fn check_jump_to_definition(graph: &TestGraph, expected_paths: &[&str]) { .iter_nodes() .filter(|handle| rust_graph[*handle].is_reference()) .collect::>(); - let config = sg_stitcher_config { + let stitcher_config = sg_stitcher_config { detect_similar_paths: false, }; sg_partial_path_arena_find_all_complete_paths( @@ -38,7 +38,7 @@ fn check_jump_to_definition(graph: &TestGraph, expected_paths: &[&str]) { references.len(), references.as_ptr() as *const _, path_list, - config, + &stitcher_config, std::ptr::null(), ); diff --git a/stack-graphs/tests/it/c/can_jump_to_definition_with_phased_partial_path_stitching.rs b/stack-graphs/tests/it/c/can_jump_to_definition_with_phased_partial_path_stitching.rs index 3a23ab763..ce20ae378 100644 --- a/stack-graphs/tests/it/c/can_jump_to_definition_with_phased_partial_path_stitching.rs +++ b/stack-graphs/tests/it/c/can_jump_to_definition_with_phased_partial_path_stitching.rs @@ -50,7 +50,7 @@ impl StorageLayer { let rust_graph = unsafe { &(*graph).inner }; let path_list = sg_partial_path_list_new(); for file in rust_graph.iter_files() { - let config = sg_stitcher_config { + let stitcher_config = sg_stitcher_config { detect_similar_paths: false, }; sg_partial_path_arena_find_partial_paths_in_file( @@ -58,7 +58,7 @@ impl StorageLayer { partials, file.as_u32(), path_list, - config, + &stitcher_config, std::ptr::null(), ); } @@ -134,15 +134,11 @@ fn check_jump_to_definition(graph: &TestGraph, file: &str, expected_partial_path }); // Create the forward partial path stitcher. - let config = sg_stitcher_config { - detect_similar_paths: false, - }; let stitcher = sg_forward_partial_path_stitcher_from_nodes( graph.graph, partials, references.len(), references.as_ptr() as *const _, - config, ); sg_forward_partial_path_stitcher_set_max_work_per_phase(stitcher, 1); let rust_stitcher = unsafe { &mut *stitcher }; diff --git a/stack-graphs/tests/it/can_find_local_nodes.rs b/stack-graphs/tests/it/can_find_local_nodes.rs index 6b763b0d7..4f471d88f 100644 --- a/stack-graphs/tests/it/can_find_local_nodes.rs +++ b/stack-graphs/tests/it/can_find_local_nodes.rs @@ -25,7 +25,7 @@ fn check_local_nodes(graph: &StackGraph, file: &str, expected_local_nodes: &[&st graph, &mut partials, file, - &StitcherConfig::default(), + StitcherConfig::default(), &NoCancellation, |graph, partials, path| { database.add_partial_path(graph, partials, path.clone()); diff --git a/stack-graphs/tests/it/can_find_node_partial_paths_in_database.rs b/stack-graphs/tests/it/can_find_node_partial_paths_in_database.rs index 22d311d34..4e825c47e 100644 --- a/stack-graphs/tests/it/can_find_node_partial_paths_in_database.rs +++ b/stack-graphs/tests/it/can_find_node_partial_paths_in_database.rs @@ -34,7 +34,7 @@ fn check_node_partial_paths( graph, &mut partials, file, - &StitcherConfig::default(), + StitcherConfig::default(), &NoCancellation, |graph, partials, path| { db.add_partial_path(graph, partials, path.clone()); diff --git a/stack-graphs/tests/it/can_find_partial_paths_in_file.rs b/stack-graphs/tests/it/can_find_partial_paths_in_file.rs index d3e48e33f..adef36ae3 100644 --- a/stack-graphs/tests/it/can_find_partial_paths_in_file.rs +++ b/stack-graphs/tests/it/can_find_partial_paths_in_file.rs @@ -23,7 +23,7 @@ fn check_partial_paths_in_file(graph: &StackGraph, file: &str, expected_paths: & graph, &mut partials, file, - &StitcherConfig::default(), + StitcherConfig::default(), &NoCancellation, |graph, partials, path| { results.insert(path.display(graph, partials).to_string()); diff --git a/stack-graphs/tests/it/can_find_root_partial_paths_in_database.rs b/stack-graphs/tests/it/can_find_root_partial_paths_in_database.rs index 616f9655c..e8beff576 100644 --- a/stack-graphs/tests/it/can_find_root_partial_paths_in_database.rs +++ b/stack-graphs/tests/it/can_find_root_partial_paths_in_database.rs @@ -35,7 +35,7 @@ fn check_root_partial_paths( graph, &mut partials, file, - &StitcherConfig::default(), + StitcherConfig::default(), &NoCancellation, |graph, partials, path| { db.add_partial_path(graph, partials, path.clone()); diff --git a/stack-graphs/tests/it/can_jump_to_definition.rs b/stack-graphs/tests/it/can_jump_to_definition.rs index db288855c..70158f495 100644 --- a/stack-graphs/tests/it/can_jump_to_definition.rs +++ b/stack-graphs/tests/it/can_jump_to_definition.rs @@ -26,7 +26,7 @@ fn check_jump_to_definition(graph: &StackGraph, expected_paths: &[&str]) { ForwardPartialPathStitcher::find_all_complete_partial_paths( &mut GraphEdgeCandidates::new(graph, &mut paths, None), references, - &StitcherConfig::default(), + StitcherConfig::default(), &NoCancellation, |graph, paths, path| { results.insert(path.display(graph, paths).to_string()); diff --git a/stack-graphs/tests/it/can_jump_to_definition_with_forward_partial_path_stitching.rs b/stack-graphs/tests/it/can_jump_to_definition_with_forward_partial_path_stitching.rs index 32e225a9c..7f5996e3e 100644 --- a/stack-graphs/tests/it/can_jump_to_definition_with_forward_partial_path_stitching.rs +++ b/stack-graphs/tests/it/can_jump_to_definition_with_forward_partial_path_stitching.rs @@ -28,7 +28,7 @@ fn check_jump_to_definition(graph: &StackGraph, expected_partial_paths: &[&str]) graph, &mut partials, file, - &StitcherConfig::default(), + StitcherConfig::default(), &NoCancellation, |graph, partials, path| { db.add_partial_path(graph, partials, path.clone()); @@ -44,7 +44,7 @@ fn check_jump_to_definition(graph: &StackGraph, expected_partial_paths: &[&str]) ForwardPartialPathStitcher::find_all_complete_partial_paths( &mut DatabaseCandidates::new(graph, &mut partials, &mut db), references, - &StitcherConfig::default(), + StitcherConfig::default(), &NoCancellation, |_, _, p| { complete_partial_paths.push(p.clone()); diff --git a/stack-graphs/tests/it/cycles.rs b/stack-graphs/tests/it/cycles.rs index abcd43048..df840eba2 100644 --- a/stack-graphs/tests/it/cycles.rs +++ b/stack-graphs/tests/it/cycles.rs @@ -200,7 +200,7 @@ fn finding_simple_identity_cycle_is_detected() { &graph, &mut partials, file, - &StitcherConfig::default(), + StitcherConfig::default(), &cancellation_flag, |_, _, _| path_count += 1, ); @@ -294,7 +294,7 @@ fn finding_composite_identity_cycle_is_detected() { &graph, &mut partials, file, - &StitcherConfig::default(), + StitcherConfig::default(), &cancellation_flag, |_, _, _| path_count += 1, ); @@ -356,7 +356,7 @@ fn appending_eliminating_cycle_terminates() { &graph, &mut partials, file, - &StitcherConfig::default(), + StitcherConfig::default(), &cancellation_flag, |_, _, _| path_count += 1, ); diff --git a/stack-graphs/tests/it/serde.rs b/stack-graphs/tests/it/serde.rs index c9c047d10..3ecc1c48f 100644 --- a/stack-graphs/tests/it/serde.rs +++ b/stack-graphs/tests/it/serde.rs @@ -984,7 +984,7 @@ fn can_serialize_partial_paths() { &graph, &mut partials, file, - &StitcherConfig::default(), + StitcherConfig::default(), &NoCancellation, |g, ps, p| { db.add_partial_path(g, ps, p.clone()); diff --git a/tree-sitter-stack-graphs/src/cli/index.rs b/tree-sitter-stack-graphs/src/cli/index.rs index 882185108..436caca1a 100644 --- a/tree-sitter-stack-graphs/src/cli/index.rs +++ b/tree-sitter-stack-graphs/src/cli/index.rs @@ -12,6 +12,7 @@ use stack_graphs::graph::File; use stack_graphs::graph::StackGraph; use stack_graphs::partial::PartialPaths; use stack_graphs::stitching::ForwardPartialPathStitcher; +use stack_graphs::stitching::StitcherConfig; use stack_graphs::storage::FileStatus; use stack_graphs::storage::SQLiteWriter; use std::collections::HashMap; @@ -316,16 +317,6 @@ impl<'a> Indexer<'a> { .add_file(&source_path.to_string_lossy()) .expect("file not present in empty graph"); - // FIXME We use the stitching config of the primary language here, which is not quite right. - // Ideally we'd use the primary language's stitcher config for computing the paths in - // the primary graph, and similarly for the secondary languages. However, that is not - // much use at the moment, since we use paths from all languages when reading from the - // database. Fixing this properly requires more isolation of graph and paths per language. - let stitcher_config = lcs - .primary - .map(|lc| lc.stitcher_config.clone()) - .unwrap_or_default(); - let result = Self::build_stack_graph( &mut graph, file, @@ -365,7 +356,7 @@ impl<'a> Indexer<'a> { &graph, &mut partials, file, - &stitcher_config, + StitcherConfig::default(), &(&cancellation_flag as &dyn CancellationFlag), |_g, _ps, p| { paths.push(p.clone()); diff --git a/tree-sitter-stack-graphs/src/cli/query.rs b/tree-sitter-stack-graphs/src/cli/query.rs index 3a9c4d47f..1063e49e9 100644 --- a/tree-sitter-stack-graphs/src/cli/query.rs +++ b/tree-sitter-stack-graphs/src/cli/query.rs @@ -186,16 +186,10 @@ impl<'a> Querier<'a> { }; let mut reference_paths = Vec::new(); - // FIXME We use the default stitcher config here, because we currently do not retrieve language - // configurations during querying. A first step to fixing this would be to lookup the language - // corresponding to the file that is queried. However, to solve this properly we would need to - // isolate graphs and paths per language so we don't accidently mix different languages that - // require different stitcher settings. - let config = StitcherConfig::default(); if let Err(err) = ForwardPartialPathStitcher::find_all_complete_partial_paths( self.db, std::iter::once(node), - &config, + StitcherConfig::default(), &cancellation_flag, |_g, _ps, p| { reference_paths.push(p.clone()); diff --git a/tree-sitter-stack-graphs/src/cli/test.rs b/tree-sitter-stack-graphs/src/cli/test.rs index e4bd690a5..459573277 100644 --- a/tree-sitter-stack-graphs/src/cli/test.rs +++ b/tree-sitter-stack-graphs/src/cli/test.rs @@ -332,7 +332,7 @@ impl TestArgs { &test.graph, &mut partials, file, - &lc.stitcher_config, + StitcherConfig::default(), &cancellation_flag.as_ref(), |g, ps, p| { db.add_partial_path(g, ps, p.clone()); @@ -342,7 +342,7 @@ impl TestArgs { let result = test.run( &mut partials, &mut db, - &lc.stitcher_config, + StitcherConfig::default(), cancellation_flag.as_ref(), )?; let success = result.failure_count() == 0; @@ -356,7 +356,7 @@ impl TestArgs { &mut db, &|_: &StackGraph, h: &Handle| files.contains(h), success, - &lc.stitcher_config, + StitcherConfig::default(), cancellation_flag.as_ref(), )? } else { @@ -405,7 +405,7 @@ impl TestArgs { db: &mut Database, filter: &dyn Filter, success: bool, - stitcher_config: &StitcherConfig, + stitcher_config: StitcherConfig, cancellation_flag: &dyn CancellationFlag, ) -> anyhow::Result> { let mut outputs = Vec::with_capacity(3); @@ -490,7 +490,7 @@ impl TestArgs { partials: &mut PartialPaths, db: &mut Database, filter: &dyn Filter, - stitcher_config: &StitcherConfig, + stitcher_config: StitcherConfig, cancellation_flag: &dyn CancellationFlag, ) -> anyhow::Result { let references = graph diff --git a/tree-sitter-stack-graphs/src/cli/visualize.rs b/tree-sitter-stack-graphs/src/cli/visualize.rs index acffbc9ce..9f2d53c50 100644 --- a/tree-sitter-stack-graphs/src/cli/visualize.rs +++ b/tree-sitter-stack-graphs/src/cli/visualize.rs @@ -55,16 +55,10 @@ impl VisualizeArgs { .filter(|n| graph[*n].is_reference()) .collect::>(); let mut complete_paths_db = Database::new(); - // FIXME We use the default stitcher config here, because we currently do not retrieve language - // configurations during querying. A first step to fixing this would be to lookup the language - // corresponding to the file that is queried. However, to solve this properly we would need to - // isolate graphs and paths per language so we don't accidently mix different languages that - // require different stitcher settings. - let config = StitcherConfig::default(); ForwardPartialPathStitcher::find_all_complete_partial_paths( &mut db, starting_nodes, - &config, + StitcherConfig::default(), cancellation_flag, |g, ps, p| { complete_paths_db.add_partial_path(g, ps, p.clone()); diff --git a/tree-sitter-stack-graphs/src/loader.rs b/tree-sitter-stack-graphs/src/loader.rs index 6464687fe..0feb6a28c 100644 --- a/tree-sitter-stack-graphs/src/loader.rs +++ b/tree-sitter-stack-graphs/src/loader.rs @@ -26,8 +26,6 @@ use tree_sitter_loader::Config as TsConfig; use tree_sitter_loader::LanguageConfiguration as TSLanguageConfiguration; use tree_sitter_loader::Loader as TsLoader; -pub use stack_graphs::stitching::StitcherConfig; - use crate::CancellationFlag; use crate::FileAnalyzer; use crate::StackGraphLanguage; @@ -46,7 +44,6 @@ pub struct LanguageConfiguration { pub sgl: StackGraphLanguage, pub builtins: StackGraph, pub special_files: FileAnalyzers, - pub stitcher_config: StitcherConfig, } impl LanguageConfiguration { @@ -62,7 +59,6 @@ impl LanguageConfiguration { builtins_source: Option<(PathBuf, &'a str)>, builtins_config: Option<&str>, special_files: FileAnalyzers, - stitcher_config: StitcherConfig, cancellation_flag: &dyn CancellationFlag, ) -> Result> { let sgl = StackGraphLanguage::from_source(language, tsg_path.clone(), tsg_source).map_err( @@ -102,7 +98,6 @@ impl LanguageConfiguration { sgl, builtins, special_files, - stitcher_config, }) } @@ -573,7 +568,6 @@ impl PathLoader { sgl, builtins, special_files: FileAnalyzers::new(), - stitcher_config: StitcherConfig::default(), // FIXME we do not have a place for this configuration }; self.cache.push((language.language, lc)); diff --git a/tree-sitter-stack-graphs/src/test.rs b/tree-sitter-stack-graphs/src/test.rs index 8f3c20c29..236e254c5 100644 --- a/tree-sitter-stack-graphs/src/test.rs +++ b/tree-sitter-stack-graphs/src/test.rs @@ -624,7 +624,7 @@ impl Test { &mut self, partials: &mut PartialPaths, db: &mut Database, - stitcher_config: &StitcherConfig, + stitcher_config: StitcherConfig, cancellation_flag: &dyn CancellationFlag, ) -> Result { let mut result = TestResult::new(); diff --git a/tree-sitter-stack-graphs/tests/it/loader.rs b/tree-sitter-stack-graphs/tests/it/loader.rs index 7525b6fcf..8fcf0ccac 100644 --- a/tree-sitter-stack-graphs/tests/it/loader.rs +++ b/tree-sitter-stack-graphs/tests/it/loader.rs @@ -8,7 +8,6 @@ use once_cell::sync::Lazy; use pretty_assertions::assert_eq; use stack_graphs::graph::StackGraph; -use stack_graphs::stitching::StitcherConfig; use std::path::PathBuf; use tree_sitter_stack_graphs::loader::FileAnalyzers; use tree_sitter_stack_graphs::loader::LanguageConfiguration; @@ -36,7 +35,6 @@ fn can_load_from_provided_language_configuration() { sgl, builtins: StackGraph::new(), special_files: FileAnalyzers::new(), - stitcher_config: StitcherConfig::default(), }; let mut loader = Loader::from_language_configurations(vec![lc], None).expect("Expected loader to succeed"); diff --git a/tree-sitter-stack-graphs/tests/it/test.rs b/tree-sitter-stack-graphs/tests/it/test.rs index 483ad011b..98ec3ab96 100644 --- a/tree-sitter-stack-graphs/tests/it/test.rs +++ b/tree-sitter-stack-graphs/tests/it/test.rs @@ -114,7 +114,7 @@ fn check_test( &test.graph, &mut partials, fragment.file, - &StitcherConfig::default(), + StitcherConfig::default(), &stack_graphs::NoCancellation, |graph, partials, path| { db.add_partial_path(graph, partials, path.clone()); @@ -127,7 +127,7 @@ fn check_test( .run( &mut partials, &mut db, - &StitcherConfig::default(), + StitcherConfig::default(), &NoCancellation, ) .expect("should never be cancelled"); From 098e03a419831125836b26abd06fa08cb94ace2a Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Mon, 13 Nov 2023 14:48:29 +0100 Subject: [PATCH 058/291] Add similar path setting to language configuration --- .../tree-sitter-stack-graphs-java/rust/lib.rs | 2 -- .../rust/lib.rs | 9 ++--- .../rust/lib.rs | 13 +++---- stack-graphs/src/stitching.rs | 13 ++++++- tree-sitter-stack-graphs/src/cli/index.rs | 4 ++- tree-sitter-stack-graphs/src/cli/init.rs | 2 -- tree-sitter-stack-graphs/src/cli/query.rs | 5 ++- tree-sitter-stack-graphs/src/cli/test.rs | 8 +++-- tree-sitter-stack-graphs/src/cli/visualize.rs | 5 ++- tree-sitter-stack-graphs/src/loader.rs | 34 ++++++++++++++++--- tree-sitter-stack-graphs/tests/it/loader.rs | 1 + 11 files changed, 71 insertions(+), 25 deletions(-) diff --git a/languages/tree-sitter-stack-graphs-java/rust/lib.rs b/languages/tree-sitter-stack-graphs-java/rust/lib.rs index 09cb043a5..942785044 100644 --- a/languages/tree-sitter-stack-graphs-java/rust/lib.rs +++ b/languages/tree-sitter-stack-graphs-java/rust/lib.rs @@ -1,4 +1,3 @@ -use tree_sitter_stack_graphs::loader::FileAnalyzers; use tree_sitter_stack_graphs::loader::LanguageConfiguration; use tree_sitter_stack_graphs::loader::LoadError; use tree_sitter_stack_graphs::CancellationFlag; @@ -39,7 +38,6 @@ pub fn try_language_configuration( STACK_GRAPHS_BUILTINS_SOURCE, )), Some(STACK_GRAPHS_BUILTINS_CONFIG), - FileAnalyzers::new(), cancellation_flag, ) } diff --git a/languages/tree-sitter-stack-graphs-javascript/rust/lib.rs b/languages/tree-sitter-stack-graphs-javascript/rust/lib.rs index 508ba58b9..61f113149 100644 --- a/languages/tree-sitter-stack-graphs-javascript/rust/lib.rs +++ b/languages/tree-sitter-stack-graphs-javascript/rust/lib.rs @@ -5,7 +5,6 @@ // Please see the LICENSE-APACHE or LICENSE-MIT files in this distribution for license details. // ------------------------------------------------------------------------------------------------ -use tree_sitter_stack_graphs::loader::FileAnalyzers; use tree_sitter_stack_graphs::loader::LanguageConfiguration; use tree_sitter_stack_graphs::loader::LoadError; use tree_sitter_stack_graphs::CancellationFlag; @@ -38,7 +37,7 @@ pub fn language_configuration(cancellation_flag: &dyn CancellationFlag) -> Langu pub fn try_language_configuration( cancellation_flag: &dyn CancellationFlag, ) -> Result { - LanguageConfiguration::from_sources( + let mut lc = LanguageConfiguration::from_sources( tree_sitter_javascript::language(), Some(String::from("source.js")), None, @@ -50,7 +49,9 @@ pub fn try_language_configuration( STACK_GRAPHS_BUILTINS_SOURCE, )), Some(STACK_GRAPHS_BUILTINS_CONFIG), - FileAnalyzers::new().add("package.json".to_string(), NpmPackageAnalyzer {}), cancellation_flag, - ) + )?; + lc.special_files + .add("package.json".to_string(), NpmPackageAnalyzer {}); + Ok(lc) } diff --git a/languages/tree-sitter-stack-graphs-typescript/rust/lib.rs b/languages/tree-sitter-stack-graphs-typescript/rust/lib.rs index 45ce6d255..0f84d64c1 100644 --- a/languages/tree-sitter-stack-graphs-typescript/rust/lib.rs +++ b/languages/tree-sitter-stack-graphs-typescript/rust/lib.rs @@ -5,7 +5,6 @@ // Please see the LICENSE-APACHE or LICENSE-MIT files in this distribution for license details. // ------------------------------------------------------------------------------------------------ -use tree_sitter_stack_graphs::loader::FileAnalyzers; use tree_sitter_stack_graphs::loader::LanguageConfiguration; use tree_sitter_stack_graphs::loader::LoadError; use tree_sitter_stack_graphs::CancellationFlag; @@ -41,7 +40,7 @@ pub fn language_configuration(cancellation_flag: &dyn CancellationFlag) -> Langu pub fn try_language_configuration( cancellation_flag: &dyn CancellationFlag, ) -> Result { - LanguageConfiguration::from_sources( + let mut lc = LanguageConfiguration::from_sources( tree_sitter_typescript::language_typescript(), Some(String::from("source.ts")), None, @@ -53,9 +52,11 @@ pub fn try_language_configuration( STACK_GRAPHS_BUILTINS_SOURCE, )), Some(STACK_GRAPHS_BUILTINS_CONFIG), - FileAnalyzers::new() - .add("tsconfig.json".to_string(), TsConfigAnalyzer {}) - .add("package.json".to_string(), NpmPackageAnalyzer {}), cancellation_flag, - ) + )?; + lc.special_files + .add("tsconfig.json".to_string(), TsConfigAnalyzer {}) + .add("package.json".to_string(), NpmPackageAnalyzer {}); + lc.has_similar_paths = false; + Ok(lc) } diff --git a/stack-graphs/src/stitching.rs b/stack-graphs/src/stitching.rs index 1432669dd..64a7e5005 100644 --- a/stack-graphs/src/stitching.rs +++ b/stack-graphs/src/stitching.rs @@ -1246,7 +1246,18 @@ impl ForwardPartialPathStitcher { #[derive(Clone, Copy, Debug)] pub struct StitcherConfig { /// Enables similar path detection during path stitching. - pub detect_similar_paths: bool, + detect_similar_paths: bool, +} + +impl StitcherConfig { + pub fn detect_similar_paths(&self) -> bool { + self.detect_similar_paths + } + + pub fn with_detect_similar_paths(mut self, detect_similar_paths: bool) -> Self { + self.detect_similar_paths = detect_similar_paths; + self + } } impl StitcherConfig { diff --git a/tree-sitter-stack-graphs/src/cli/index.rs b/tree-sitter-stack-graphs/src/cli/index.rs index 436caca1a..a37806d45 100644 --- a/tree-sitter-stack-graphs/src/cli/index.rs +++ b/tree-sitter-stack-graphs/src/cli/index.rs @@ -280,6 +280,8 @@ impl<'a> Indexer<'a> { } Err(e) => return Err(IndexError::LoadError(e)), }; + let stitcher_config = + StitcherConfig::default().with_detect_similar_paths(lcs.has_similar_paths()); let source = file_reader.get(source_path)?; let tag = sha1(source); @@ -356,7 +358,7 @@ impl<'a> Indexer<'a> { &graph, &mut partials, file, - StitcherConfig::default(), + stitcher_config, &(&cancellation_flag as &dyn CancellationFlag), |_g, _ps, p| { paths.push(p.clone()); diff --git a/tree-sitter-stack-graphs/src/cli/init.rs b/tree-sitter-stack-graphs/src/cli/init.rs index c287909e6..d9fc0cd59 100644 --- a/tree-sitter-stack-graphs/src/cli/init.rs +++ b/tree-sitter-stack-graphs/src/cli/init.rs @@ -758,8 +758,6 @@ impl ProjectSettings<'_> { STACK_GRAPHS_BUILTINS_SOURCE, )), Some(STACK_GRAPHS_BUILTINS_CONFIG), - FileAnalyzers::new(), - StitcherConfig::default(), cancellation_flag, ) }} diff --git a/tree-sitter-stack-graphs/src/cli/query.rs b/tree-sitter-stack-graphs/src/cli/query.rs index 1063e49e9..6bbf46702 100644 --- a/tree-sitter-stack-graphs/src/cli/query.rs +++ b/tree-sitter-stack-graphs/src/cli/query.rs @@ -186,10 +186,13 @@ impl<'a> Querier<'a> { }; let mut reference_paths = Vec::new(); + let stitcher_config = StitcherConfig::default() + // always detect similar paths, we don't know the language configurations for the data in the database + .with_detect_similar_paths(true); if let Err(err) = ForwardPartialPathStitcher::find_all_complete_partial_paths( self.db, std::iter::once(node), - StitcherConfig::default(), + stitcher_config, &cancellation_flag, |_g, _ps, p| { reference_paths.push(p.clone()); diff --git a/tree-sitter-stack-graphs/src/cli/test.rs b/tree-sitter-stack-graphs/src/cli/test.rs index 459573277..3331a713b 100644 --- a/tree-sitter-stack-graphs/src/cli/test.rs +++ b/tree-sitter-stack-graphs/src/cli/test.rs @@ -325,6 +325,8 @@ impl TestArgs { Ok(_) => {} } } + let stitcher_config = + StitcherConfig::default().with_detect_similar_paths(lc.has_similar_paths); let mut partials = PartialPaths::new(); let mut db = Database::new(); for file in test.graph.iter_files() { @@ -332,7 +334,7 @@ impl TestArgs { &test.graph, &mut partials, file, - StitcherConfig::default(), + stitcher_config, &cancellation_flag.as_ref(), |g, ps, p| { db.add_partial_path(g, ps, p.clone()); @@ -342,7 +344,7 @@ impl TestArgs { let result = test.run( &mut partials, &mut db, - StitcherConfig::default(), + stitcher_config, cancellation_flag.as_ref(), )?; let success = result.failure_count() == 0; @@ -356,7 +358,7 @@ impl TestArgs { &mut db, &|_: &StackGraph, h: &Handle| files.contains(h), success, - StitcherConfig::default(), + stitcher_config, cancellation_flag.as_ref(), )? } else { diff --git a/tree-sitter-stack-graphs/src/cli/visualize.rs b/tree-sitter-stack-graphs/src/cli/visualize.rs index 9f2d53c50..f17f23b88 100644 --- a/tree-sitter-stack-graphs/src/cli/visualize.rs +++ b/tree-sitter-stack-graphs/src/cli/visualize.rs @@ -55,10 +55,13 @@ impl VisualizeArgs { .filter(|n| graph[*n].is_reference()) .collect::>(); let mut complete_paths_db = Database::new(); + let stitcher_config = StitcherConfig::default() + // always detect similar paths, we don't know the language configurations for the data in the database + .with_detect_similar_paths(true); ForwardPartialPathStitcher::find_all_complete_partial_paths( &mut db, starting_nodes, - StitcherConfig::default(), + stitcher_config, cancellation_flag, |g, ps, p| { complete_paths_db.add_partial_path(g, ps, p.clone()); diff --git a/tree-sitter-stack-graphs/src/loader.rs b/tree-sitter-stack-graphs/src/loader.rs index 0feb6a28c..b44996ffe 100644 --- a/tree-sitter-stack-graphs/src/loader.rs +++ b/tree-sitter-stack-graphs/src/loader.rs @@ -44,11 +44,12 @@ pub struct LanguageConfiguration { pub sgl: StackGraphLanguage, pub builtins: StackGraph, pub special_files: FileAnalyzers, + pub has_similar_paths: bool, } impl LanguageConfiguration { /// Build a language configuration from tsg and builtins sources. The tsg path - /// is kept for informational only, see [`StackGraphLanguage::from_source`][]. + /// is kept for informational use only, see [`StackGraphLanguage::from_source`][]. pub fn from_sources<'a>( language: Language, scope: Option, @@ -58,7 +59,6 @@ impl LanguageConfiguration { tsg_source: &'a str, builtins_source: Option<(PathBuf, &'a str)>, builtins_config: Option<&str>, - special_files: FileAnalyzers, cancellation_flag: &dyn CancellationFlag, ) -> Result> { let sgl = StackGraphLanguage::from_source(language, tsg_path.clone(), tsg_source).map_err( @@ -97,7 +97,8 @@ impl LanguageConfiguration { file_types, sgl, builtins, - special_files, + special_files: FileAnalyzers::new(), + has_similar_paths: true, }) } @@ -143,7 +144,7 @@ impl FileAnalyzers { } } - pub fn add( + pub fn with( mut self, file_name: String, analyzer: impl FileAnalyzer + Send + Sync + 'static, @@ -152,6 +153,15 @@ impl FileAnalyzers { self } + pub fn add( + &mut self, + file_name: String, + analyzer: impl FileAnalyzer + Send + Sync + 'static, + ) -> &mut Self { + self.file_analyzers.insert(file_name, Arc::new(analyzer)); + self + } + pub fn get(&self, file_name: &str) -> Option> { self.file_analyzers.get(file_name).cloned() } @@ -358,6 +368,20 @@ impl FileLanguageConfigurations<'_> { pub fn has_some(&self) -> bool { self.primary.is_some() || !self.secondary.is_empty() } + + pub fn has_similar_paths(&self) -> bool { + if let Some(lc) = &self.primary { + if lc.has_similar_paths { + return true; + } + } + for (lc, _) in &self.secondary { + if lc.has_similar_paths { + return true; + } + } + return false; + } } #[derive(Debug, Error)] @@ -568,6 +592,8 @@ impl PathLoader { sgl, builtins, special_files: FileAnalyzers::new(), + // always detect similar paths, we don't know the language configuration when loading from the file system + has_similar_paths: true, }; self.cache.push((language.language, lc)); diff --git a/tree-sitter-stack-graphs/tests/it/loader.rs b/tree-sitter-stack-graphs/tests/it/loader.rs index 8fcf0ccac..176600435 100644 --- a/tree-sitter-stack-graphs/tests/it/loader.rs +++ b/tree-sitter-stack-graphs/tests/it/loader.rs @@ -35,6 +35,7 @@ fn can_load_from_provided_language_configuration() { sgl, builtins: StackGraph::new(), special_files: FileAnalyzers::new(), + has_similar_paths: true, }; let mut loader = Loader::from_language_configurations(vec![lc], None).expect("Expected loader to succeed"); From 966f613175808942015fde20dc45a2295e8788ae Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Mon, 13 Nov 2023 14:50:24 +0100 Subject: [PATCH 059/291] Update project init --- tree-sitter-stack-graphs/src/cli/init.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/tree-sitter-stack-graphs/src/cli/init.rs b/tree-sitter-stack-graphs/src/cli/init.rs index d9fc0cd59..e381786b9 100644 --- a/tree-sitter-stack-graphs/src/cli/init.rs +++ b/tree-sitter-stack-graphs/src/cli/init.rs @@ -718,10 +718,8 @@ impl ProjectSettings<'_> { let mut file = File::create(project_path.join("rust/lib.rs"))?; self.write_license_header(&mut file, "// ")?; writedoc! {file, r#" - use tree_sitter_stack_graphs::loader::FileAnalyzers; use tree_sitter_stack_graphs::loader::LanguageConfiguration; use tree_sitter_stack_graphs::loader::LoadError; - use tree_sitter_stack_graphs::loader::StitcherConfig; use tree_sitter_stack_graphs::CancellationFlag; /// The stack graphs tsg source for this language. From 3d32fbb2cb70fb70995536ff83087f2066bf013f Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Mon, 20 Nov 2023 13:42:04 +0100 Subject: [PATCH 060/291] Do not use transmute for stitcher config --- stack-graphs/src/c.rs | 2 +- stack-graphs/src/stitching.rs | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/stack-graphs/src/c.rs b/stack-graphs/src/c.rs index 26a823eba..9b50349ec 100644 --- a/stack-graphs/src/c.rs +++ b/stack-graphs/src/c.rs @@ -1408,7 +1408,7 @@ pub struct sg_stitcher_config { impl Into for sg_stitcher_config { fn into(self) -> StitcherConfig { - unsafe { std::mem::transmute(self) } + StitcherConfig::default().with_detect_similar_paths(self.detect_similar_paths) } } diff --git a/stack-graphs/src/stitching.rs b/stack-graphs/src/stitching.rs index 64a7e5005..328dc63a9 100644 --- a/stack-graphs/src/stitching.rs +++ b/stack-graphs/src/stitching.rs @@ -1242,7 +1242,6 @@ impl ForwardPartialPathStitcher { } /// Configuration for partial path stitchers. -#[repr(C)] #[derive(Clone, Copy, Debug)] pub struct StitcherConfig { /// Enables similar path detection during path stitching. From 294596aed849e1873e1693da6f2dcdcb243fac83 Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Mon, 20 Nov 2023 14:57:58 +0100 Subject: [PATCH 061/291] Change setting name and add comment explaining the consequences --- .../rust/lib.rs | 2 +- stack-graphs/src/stitching.rs | 1 + tree-sitter-stack-graphs/src/cli/index.rs | 2 +- tree-sitter-stack-graphs/src/cli/test.rs | 2 +- tree-sitter-stack-graphs/src/loader.rs | 23 ++++++++++--------- tree-sitter-stack-graphs/tests/it/loader.rs | 2 +- 6 files changed, 17 insertions(+), 15 deletions(-) diff --git a/languages/tree-sitter-stack-graphs-typescript/rust/lib.rs b/languages/tree-sitter-stack-graphs-typescript/rust/lib.rs index 0f84d64c1..3590649b2 100644 --- a/languages/tree-sitter-stack-graphs-typescript/rust/lib.rs +++ b/languages/tree-sitter-stack-graphs-typescript/rust/lib.rs @@ -57,6 +57,6 @@ pub fn try_language_configuration( lc.special_files .add("tsconfig.json".to_string(), TsConfigAnalyzer {}) .add("package.json".to_string(), NpmPackageAnalyzer {}); - lc.has_similar_paths = false; + lc.no_similar_paths_in_file = true; Ok(lc) } diff --git a/stack-graphs/src/stitching.rs b/stack-graphs/src/stitching.rs index 328dc63a9..dcf1d70c0 100644 --- a/stack-graphs/src/stitching.rs +++ b/stack-graphs/src/stitching.rs @@ -870,6 +870,7 @@ impl ForwardPartialPathStitcher { initial_paths: next_iteration.0.len(), next_iteration, appended_paths, + // By default, all paths are checked for similarity similar_path_detector: Some(SimilarPathDetector::new()), // By default, all nodes are checked for cycles and (if enabled) similarity check_only_join_nodes: false, diff --git a/tree-sitter-stack-graphs/src/cli/index.rs b/tree-sitter-stack-graphs/src/cli/index.rs index a37806d45..dab12e537 100644 --- a/tree-sitter-stack-graphs/src/cli/index.rs +++ b/tree-sitter-stack-graphs/src/cli/index.rs @@ -281,7 +281,7 @@ impl<'a> Indexer<'a> { Err(e) => return Err(IndexError::LoadError(e)), }; let stitcher_config = - StitcherConfig::default().with_detect_similar_paths(lcs.has_similar_paths()); + StitcherConfig::default().with_detect_similar_paths(!lcs.no_similar_paths_in_file()); let source = file_reader.get(source_path)?; let tag = sha1(source); diff --git a/tree-sitter-stack-graphs/src/cli/test.rs b/tree-sitter-stack-graphs/src/cli/test.rs index 3331a713b..780d478f6 100644 --- a/tree-sitter-stack-graphs/src/cli/test.rs +++ b/tree-sitter-stack-graphs/src/cli/test.rs @@ -326,7 +326,7 @@ impl TestArgs { } } let stitcher_config = - StitcherConfig::default().with_detect_similar_paths(lc.has_similar_paths); + StitcherConfig::default().with_detect_similar_paths(!lc.no_similar_paths_in_file); let mut partials = PartialPaths::new(); let mut db = Database::new(); for file in test.graph.iter_files() { diff --git a/tree-sitter-stack-graphs/src/loader.rs b/tree-sitter-stack-graphs/src/loader.rs index b44996ffe..9ffc1d674 100644 --- a/tree-sitter-stack-graphs/src/loader.rs +++ b/tree-sitter-stack-graphs/src/loader.rs @@ -44,7 +44,11 @@ pub struct LanguageConfiguration { pub sgl: StackGraphLanguage, pub builtins: StackGraph, pub special_files: FileAnalyzers, - pub has_similar_paths: bool, + /// Can be set to true if the stack graph rules ensure that there can be no similar + /// paths in a file, in which case it is safe to turn of similar path detection. If + /// incorrectly set to true, performance of path finding suffers from exponential + /// blow up. + pub no_similar_paths_in_file: bool, } impl LanguageConfiguration { @@ -98,7 +102,7 @@ impl LanguageConfiguration { sgl, builtins, special_files: FileAnalyzers::new(), - has_similar_paths: true, + no_similar_paths_in_file: false, }) } @@ -369,18 +373,15 @@ impl FileLanguageConfigurations<'_> { self.primary.is_some() || !self.secondary.is_empty() } - pub fn has_similar_paths(&self) -> bool { + pub fn no_similar_paths_in_file(&self) -> bool { + let mut no_similar_paths_in_file = true; if let Some(lc) = &self.primary { - if lc.has_similar_paths { - return true; - } + no_similar_paths_in_file &= lc.no_similar_paths_in_file; } for (lc, _) in &self.secondary { - if lc.has_similar_paths { - return true; - } + no_similar_paths_in_file &= lc.no_similar_paths_in_file; } - return false; + return no_similar_paths_in_file; } } @@ -593,7 +594,7 @@ impl PathLoader { builtins, special_files: FileAnalyzers::new(), // always detect similar paths, we don't know the language configuration when loading from the file system - has_similar_paths: true, + no_similar_paths_in_file: false, }; self.cache.push((language.language, lc)); diff --git a/tree-sitter-stack-graphs/tests/it/loader.rs b/tree-sitter-stack-graphs/tests/it/loader.rs index 176600435..257326952 100644 --- a/tree-sitter-stack-graphs/tests/it/loader.rs +++ b/tree-sitter-stack-graphs/tests/it/loader.rs @@ -35,7 +35,7 @@ fn can_load_from_provided_language_configuration() { sgl, builtins: StackGraph::new(), special_files: FileAnalyzers::new(), - has_similar_paths: true, + no_similar_paths_in_file: false, }; let mut loader = Loader::from_language_configurations(vec![lc], None).expect("Expected loader to succeed"); From 2c97ba23f225b36c93096eacab34e7cf858b7e25 Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Sun, 22 Oct 2023 13:11:39 +0200 Subject: [PATCH 062/291] Add database reader stats --- stack-graphs/src/storage.rs | 54 ++++++++++++++++++++++- tree-sitter-stack-graphs/src/cli/query.rs | 22 ++++++++- 2 files changed, 74 insertions(+), 2 deletions(-) diff --git a/stack-graphs/src/storage.rs b/stack-graphs/src/storage.rs index d3f1348dc..c3b3e8ebf 100644 --- a/stack-graphs/src/storage.rs +++ b/stack-graphs/src/storage.rs @@ -419,6 +419,7 @@ impl SQLiteWriter { graph: StackGraph::new(), partials: PartialPaths::new(), db: Database::new(), + stats: Stats::default(), } } } @@ -432,6 +433,7 @@ pub struct SQLiteReader { graph: StackGraph, partials: PartialPaths, db: Database, + stats: Stats, } impl SQLiteReader { @@ -454,6 +456,7 @@ impl SQLiteReader { graph: StackGraph::new(), partials: PartialPaths::new(), db: Database::new(), + stats: Stats::default(), }) } @@ -467,6 +470,8 @@ impl SQLiteReader { self.loaded_root_paths.clear(); self.partials.clear(); self.db.clear(); + + self.stats.clear(); } /// Clear path data that has been loaded into this reader instance. @@ -477,6 +482,8 @@ impl SQLiteReader { self.loaded_root_paths.clear(); self.partials.clear(); self.db.clear(); + + self.stats.clear_paths(); } /// Get the file's status in the database. If a tag is provided, it must match or the file @@ -518,7 +525,13 @@ impl SQLiteReader { /// Ensure the graph for the given file is loaded. pub fn load_graph_for_file(&mut self, file: &str) -> Result> { - Self::load_graph_for_file_inner(file, &mut self.graph, &mut self.loaded_graphs, &self.conn) + Self::load_graph_for_file_inner( + file, + &mut self.graph, + &mut self.loaded_graphs, + &self.conn, + &mut self.stats, + ) } fn load_graph_for_file_inner( @@ -526,13 +539,16 @@ impl SQLiteReader { graph: &mut StackGraph, loaded_graphs: &mut HashSet, conn: &Connection, + stats: &mut Stats, ) -> Result> { copious_debugging!("--> Load graph for {}", file); if !loaded_graphs.insert(file.to_string()) { copious_debugging!(" * Already loaded"); + stats.file_cached += 1; return Ok(graph.get_file(file).expect("loaded file to exist")); } copious_debugging!(" * Load from database"); + stats.file_loads += 1; let mut stmt = conn.prepare_cached("SELECT value FROM graphs WHERE file = ?")?; let value = stmt.query_row([file], |row| row.get::<_, Vec>(0))?; let (file_graph, _): (serde::StackGraph, usize) = @@ -554,6 +570,7 @@ impl SQLiteReader { &mut self.graph, &mut self.loaded_graphs, &self.conn, + &mut self.stats, )?; } Ok(()) @@ -568,8 +585,10 @@ impl SQLiteReader { copious_debugging!(" * Load extensions from node {}", node.display(&self.graph)); if !self.loaded_node_paths.insert(node) { copious_debugging!(" > Already loaded"); + self.stats.node_path_cached += 1; return Ok(()); } + self.stats.node_path_loads += 1; let id = self.graph[node].id(); let file = id.file().expect("file node required"); let file = self.graph[file].name(); @@ -591,6 +610,7 @@ impl SQLiteReader { &mut self.graph, &mut self.loaded_graphs, &self.conn, + &mut self.stats, )?; let (path, _): (serde::PartialPath, usize) = bincode::decode_from_slice(&value, BINCODE_CONFIG)?; @@ -629,8 +649,10 @@ impl SQLiteReader { ); if !self.loaded_root_paths.insert(symbol_stack.clone()) { copious_debugging!(" > Already loaded"); + self.stats.root_path_cached += 1; continue; } + self.stats.root_path_loads += 1; let paths = stmt.query_map([symbol_stack, escape.clone()], |row| { let file = row.get::<_, String>(0)?; let value = row.get::<_, Vec>(1)?; @@ -646,6 +668,7 @@ impl SQLiteReader { &mut self.graph, &mut self.loaded_graphs, &self.conn, + &mut self.stats, )?; let (path, _): (serde::PartialPath, usize) = bincode::decode_from_slice(&value, BINCODE_CONFIG)?; @@ -686,6 +709,11 @@ impl SQLiteReader { pub fn get(&mut self) -> (&mut StackGraph, &mut PartialPaths, &mut Database) { (&mut self.graph, &mut self.partials, &mut self.db) } + + /// Return stats about this database reader. + pub fn stats(&self) -> Stats { + self.stats.clone() + } } // Methods for computing keys and patterns for a symbol stack. The format of a storage key is: @@ -765,6 +793,30 @@ impl ForwardCandidates, PartialPath, Database, StorageError> } } +#[derive(Clone, Debug, Default)] +pub struct Stats { + pub file_loads: usize, + pub file_cached: usize, + pub root_path_loads: usize, + pub root_path_cached: usize, + pub node_path_loads: usize, + pub node_path_cached: usize, +} + +impl Stats { + fn clear(&mut self) { + *self = Stats::default(); + } + + fn clear_paths(&mut self) { + *self = Stats { + file_loads: self.file_loads, + file_cached: self.file_cached, + ..Stats::default() + } + } +} + /// Check if the database has the version supported by this library version. fn check_version(conn: &Connection) -> Result<()> { let version = conn.query_row("SELECT version FROM metadata", [], |r| r.get::<_, usize>(0))?; diff --git a/tree-sitter-stack-graphs/src/cli/query.rs b/tree-sitter-stack-graphs/src/cli/query.rs index 6bbf46702..296052748 100644 --- a/tree-sitter-stack-graphs/src/cli/query.rs +++ b/tree-sitter-stack-graphs/src/cli/query.rs @@ -9,6 +9,7 @@ use clap::Args; use clap::Parser; use clap::Subcommand; use clap::ValueHint; +use indoc::printdoc; use stack_graphs::stitching::ForwardPartialPathStitcher; use stack_graphs::stitching::StitcherConfig; use stack_graphs::storage::FileStatus; @@ -34,6 +35,9 @@ pub struct QueryArgs { #[clap(long)] pub wait_at_start: bool, + #[clap(long)] + pub stats: bool, + #[clap(subcommand)] target: Target, } @@ -44,7 +48,23 @@ impl QueryArgs { wait_for_input()?; } let mut db = SQLiteReader::open(&db_path)?; - self.target.run(&mut db) + self.target.run(&mut db)?; + if self.stats { + let stats = db.stats(); + printdoc! {r#" + + db stats | loads | cached + ------------+--------+-------- + files | {:>6} | {:>6} + root paths | {:>6} | {:>6} + node paths | {:>6} | {:>6} + "#, + stats.file_loads, stats.file_cached, + stats.root_path_loads, stats.root_path_cached, + stats.node_path_loads, stats.node_path_cached, + }; + } + Ok(()) } } From a2ce328807228b703841eb086b0190da1361494f Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Tue, 24 Oct 2023 16:20:52 +0100 Subject: [PATCH 063/291] Add frequency distribution type --- stack-graphs/src/lib.rs | 1 + stack-graphs/src/stats.rs | 90 ++++++++++++++++++++++++++++++++++ stack-graphs/tests/it/main.rs | 1 + stack-graphs/tests/it/stats.rs | 45 +++++++++++++++++ 4 files changed, 137 insertions(+) create mode 100644 stack-graphs/src/stats.rs create mode 100644 stack-graphs/tests/it/stats.rs diff --git a/stack-graphs/src/lib.rs b/stack-graphs/src/lib.rs index 009076af9..a8573c5e5 100644 --- a/stack-graphs/src/lib.rs +++ b/stack-graphs/src/lib.rs @@ -69,6 +69,7 @@ pub mod graph; pub mod partial; pub mod paths; pub mod serde; +pub mod stats; pub mod stitching; #[cfg(feature = "storage")] pub mod storage; diff --git a/stack-graphs/src/stats.rs b/stack-graphs/src/stats.rs new file mode 100644 index 000000000..b74098f12 --- /dev/null +++ b/stack-graphs/src/stats.rs @@ -0,0 +1,90 @@ +use std::collections::HashMap; +use std::hash::Hash; + +use itertools::Itertools; + +/// Frequency distribution maintains the frequency of T values. +#[derive(Clone, Debug, Default)] +pub struct FrequencyDistribution +where + T: Eq + Hash, +{ + values: HashMap, + total: usize, +} + +impl FrequencyDistribution { + pub fn total(&self) -> usize { + return self.total; + } + + pub fn unique(&self) -> usize { + return self.values.len(); + } + + pub fn frequencies(&self) -> FrequencyDistribution { + let mut fs = FrequencyDistribution::default(); + for count in self.values.values() { + fs += *count + } + fs + } +} + +impl FrequencyDistribution { + pub fn quantiles(&self, q: usize) -> Vec<&T> { + if q == 0 || self.total == 0 { + return vec![]; + } + + let mut it = self.values.iter().sorted_by_key(|e| e.0); + let mut total_count = 0; + let mut last_value; + let mut result = Vec::new(); + + if let Some((value, count)) = it.next() { + total_count += count; + last_value = value; + } else { + return vec![]; + } + result.push(last_value); + + for k in 1..=q { + let limit = ((self.total as f64 * k as f64) / q as f64).round() as usize; + while total_count < limit { + if let Some((value, count)) = it.next() { + total_count += count; + last_value = value; + } else { + break; + } + } + result.push(last_value); + } + + result + } +} + +impl std::ops::AddAssign for FrequencyDistribution +where + T: Eq + Hash, +{ + fn add_assign(&mut self, rhs: T) { + *self.values.entry(rhs).or_default() += 1; + self.total += 1; + } +} + +impl std::ops::AddAssign<&Self> for FrequencyDistribution +where + T: Eq + Hash + Clone, +{ + fn add_assign(&mut self, rhs: &Self) { + for (value, count) in &rhs.values { + *self.values.entry(value.clone()).or_default() += count; + } + self.total += rhs.total; + } +} diff --git a/stack-graphs/tests/it/main.rs b/stack-graphs/tests/it/main.rs index 3f0162c3b..e5a0e6edd 100644 --- a/stack-graphs/tests/it/main.rs +++ b/stack-graphs/tests/it/main.rs @@ -24,6 +24,7 @@ mod graph; mod partial; #[cfg(feature = "serde")] mod serde; +mod stats; mod stitching; #[cfg(feature = "storage")] mod storage; diff --git a/stack-graphs/tests/it/stats.rs b/stack-graphs/tests/it/stats.rs new file mode 100644 index 000000000..f4a48cd03 --- /dev/null +++ b/stack-graphs/tests/it/stats.rs @@ -0,0 +1,45 @@ +use itertools::Itertools; +use pretty_assertions::assert_eq; + +use stack_graphs::stats::*; + +#[test] +fn empty_distribution() { + let hist: FrequencyDistribution = FrequencyDistribution::default(); + + assert_eq!(0, hist.unique()); + assert_eq!(0, hist.total()); + + let result = hist.quantiles(0).into_iter().cloned().collect_vec(); + let expected: Vec = vec![]; + assert_eq!(expected, result); +} + +#[test] +fn singleton_distribution() { + let mut hist = FrequencyDistribution::default(); + hist += 42; + + assert_eq!(1, hist.unique()); + assert_eq!(1, hist.total()); + + let result = hist.quantiles(4).into_iter().cloned().collect_vec(); + let expected: Vec = vec![42, 42, 42, 42, 42]; + assert_eq!(expected, result); +} + +#[test] +fn four_value_distribution() { + let mut hist = FrequencyDistribution::default(); + hist += 3; + hist += 4; + hist += 1; + hist += 2; + + assert_eq!(4, hist.unique()); + assert_eq!(4, hist.total()); + + let result = hist.quantiles(4).into_iter().cloned().collect_vec(); + let expected: Vec = vec![1, 1, 2, 3, 4]; + assert_eq!(expected, result); +} From c28ca80bd09c4fc443f402aefbee89d85fb12915 Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Wed, 25 Oct 2023 00:52:16 +0100 Subject: [PATCH 064/291] Add stitcher stats --- stack-graphs/src/graph.rs | 2 +- stack-graphs/src/stitching.rs | 85 ++++++++++++- tree-sitter-stack-graphs/src/cli/query.rs | 138 +++++++++++++++++----- 3 files changed, 191 insertions(+), 34 deletions(-) diff --git a/stack-graphs/src/graph.rs b/stack-graphs/src/graph.rs index 53dd392da..fe1321387 100644 --- a/stack-graphs/src/graph.rs +++ b/stack-graphs/src/graph.rs @@ -425,7 +425,7 @@ impl Handle { /// Each node (except for the _root node_ and _jump to scope_ node) lives in a file, and has a /// _local ID_ that must be unique within its file. #[repr(C)] -#[derive(Clone, Copy, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] +#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] pub struct NodeID { file: ControlledOption>, local_id: u32, diff --git a/stack-graphs/src/stitching.rs b/stack-graphs/src/stitching.rs index dcf1d70c0..51aab53ba 100644 --- a/stack-graphs/src/stitching.rs +++ b/stack-graphs/src/stitching.rs @@ -66,6 +66,7 @@ use crate::partial::PartialPaths; use crate::partial::PartialSymbolStack; use crate::paths::Extend; use crate::paths::PathResolutionError; +use crate::stats::FrequencyDistribution; use crate::CancellationError; use crate::CancellationFlag; @@ -839,6 +840,7 @@ pub struct ForwardPartialPathStitcher { similar_path_detector: Option>, check_only_join_nodes: bool, max_work_per_phase: usize, + stats: Stats, #[cfg(feature = "copious-debugging")] phase_number: usize, } @@ -876,6 +878,7 @@ impl ForwardPartialPathStitcher { check_only_join_nodes: false, // By default, there's no artificial bound on the amount of work done per phase max_work_per_phase: usize::MAX, + stats: Stats::default(), #[cfg(feature = "copious-debugging")] phase_number: 1, } @@ -909,6 +912,10 @@ impl ForwardPartialPathStitcher { pub fn set_max_work_per_phase(&mut self, max_work_per_phase: usize) { self.max_work_per_phase = max_work_per_phase; } + + pub fn into_stats(self) -> Stats { + self.stats + } } impl ForwardPartialPathStitcher { @@ -1056,6 +1063,21 @@ impl ForwardPartialPathStitcher { self.next_iteration.2.push(new_has_split); } + let (graph, _, _) = candidates.get_graph_partials_and_db(); + let end_node = &graph[partial_path.end_node]; + if end_node.is_root() { + self.stats.candidates_per_root_path += candidate_count; + self.stats.extensions_per_root_path += extension_count; + self.stats.root_visits += 1; + } else { + self.stats.candidates_per_node_path += candidate_count; + self.stats.extensions_per_node_path += extension_count; + self.stats.node_visits += end_node.id(); + } + + if extension_count == 0 { + self.stats.maximal_path_lengh += partial_path.edges.len(); + } candidate_count } @@ -1089,6 +1111,7 @@ impl ForwardPartialPathStitcher { self.next_iteration.1.drain(..), self.next_iteration.2.drain(..), )); + self.stats.queued_paths_per_phase += self.queue.len(); let mut work_performed = 0; while let Some((partial_path, cycle_detector, has_split)) = self.queue.pop_front() { let (graph, partials, _) = candidates.get_graph_partials_and_db(); @@ -1110,6 +1133,7 @@ impl ForwardPartialPathStitcher { break; } } + self.stats.processed_paths_per_phase += work_performed; #[cfg(feature = "copious-debugging")] { @@ -1149,7 +1173,7 @@ impl ForwardPartialPathStitcher { config: StitcherConfig, cancellation_flag: &dyn CancellationFlag, mut visit: F, - ) -> Result<(), CancellationError> + ) -> Result where F: FnMut(&StackGraph, &mut PartialPaths, &PartialPath), { @@ -1168,6 +1192,8 @@ impl ForwardPartialPathStitcher { ForwardPartialPathStitcher::from_partial_paths(graph, partials, initial_paths); config.apply(&mut stitcher); stitcher.set_check_only_join_nodes(true); + + let mut accepted_path_length = FrequencyDistribution::default(); while !stitcher.is_complete() { cancellation_flag.check("finding complete partial paths")?; stitcher.process_next_phase( @@ -1176,11 +1202,16 @@ impl ForwardPartialPathStitcher { ); for path in stitcher.previous_phase_partial_paths() { if as_complete_as_necessary(graph, path) { + accepted_path_length += path.edges.len(); visit(graph, partials, path); } } } - Ok(()) + + Ok(Stats { + accepted_path_length, + ..stitcher.stats + }) } } @@ -1202,7 +1233,7 @@ impl ForwardPartialPathStitcher { config: StitcherConfig, cancellation_flag: &dyn CancellationFlag, mut visit: F, - ) -> Result<(), Err> + ) -> Result where I: IntoIterator>, A: Appendable, @@ -1225,6 +1256,8 @@ impl ForwardPartialPathStitcher { ForwardPartialPathStitcher::from_partial_paths(graph, partials, initial_paths); config.apply(&mut stitcher); stitcher.set_check_only_join_nodes(true); + + let mut accepted_path_length = FrequencyDistribution::default(); while !stitcher.is_complete() { cancellation_flag.check("finding complete partial paths")?; for path in stitcher.previous_phase_partial_paths() { @@ -1234,11 +1267,55 @@ impl ForwardPartialPathStitcher { let (graph, partials, _) = candidates.get_graph_partials_and_db(); for path in stitcher.previous_phase_partial_paths() { if path.is_complete(graph) { + accepted_path_length += path.edges.len(); visit(graph, partials, path); } } } - Ok(()) + + Ok(Stats { + accepted_path_length, + ..stitcher.stats + }) + } +} + +#[derive(Clone, Debug, Default)] +pub struct Stats { + /// The distribution of the number of queued paths per stitching phase + pub queued_paths_per_phase: FrequencyDistribution, + /// The distribution of the number of processed paths per stitching phase + pub processed_paths_per_phase: FrequencyDistribution, + /// The distribution of the length of accepted paths + pub accepted_path_length: FrequencyDistribution, + /// The distribution of the maximal length of paths (when they cannot be extended more) + pub maximal_path_lengh: FrequencyDistribution, + /// The distribution of the number of candidates for paths ending in a regular node + pub candidates_per_node_path: FrequencyDistribution, + /// The distribution of the number of candidates for paths ending in the root node + pub candidates_per_root_path: FrequencyDistribution, + /// The distribution of the number of extensions (accepted candidates) for paths ending in a regular node + pub extensions_per_node_path: FrequencyDistribution, + /// The distribution of the number of extensions (accepted candidates) for paths ending in the root node + pub extensions_per_root_path: FrequencyDistribution, + /// The number of times the root node is visited + pub root_visits: usize, + /// The distribution of the number of times a regular node is visited + pub node_visits: FrequencyDistribution, +} + +impl std::ops::AddAssign<&Self> for Stats { + fn add_assign(&mut self, rhs: &Self) { + self.queued_paths_per_phase += &rhs.queued_paths_per_phase; + self.processed_paths_per_phase += &rhs.processed_paths_per_phase; + self.accepted_path_length += &rhs.accepted_path_length; + self.maximal_path_lengh += &rhs.maximal_path_lengh; + self.candidates_per_node_path += &rhs.candidates_per_node_path; + self.candidates_per_root_path += &rhs.candidates_per_root_path; + self.extensions_per_node_path += &rhs.extensions_per_node_path; + self.extensions_per_root_path += &rhs.extensions_per_root_path; + self.root_visits += rhs.root_visits; + self.node_visits += &rhs.node_visits; } } diff --git a/tree-sitter-stack-graphs/src/cli/query.rs b/tree-sitter-stack-graphs/src/cli/query.rs index 296052748..74cbb658f 100644 --- a/tree-sitter-stack-graphs/src/cli/query.rs +++ b/tree-sitter-stack-graphs/src/cli/query.rs @@ -5,17 +5,22 @@ // Please see the LICENSE-APACHE or LICENSE-MIT files in this distribution for license details. // ------------------------------------------------------------------------------------------------ +use std::fmt::Display; +use std::hash::Hash; +use std::path::Path; +use std::path::PathBuf; + use clap::Args; use clap::Parser; use clap::Subcommand; use clap::ValueHint; -use indoc::printdoc; +use stack_graphs::stats::FrequencyDistribution; use stack_graphs::stitching::ForwardPartialPathStitcher; +use stack_graphs::stitching::Stats as StitchingStats; use stack_graphs::stitching::StitcherConfig; use stack_graphs::storage::FileStatus; use stack_graphs::storage::SQLiteReader; -use std::path::Path; -use std::path::PathBuf; +use stack_graphs::storage::Stats as StorageStats; use thiserror::Error; use tree_sitter_graph::parse_error::Excerpt; @@ -48,24 +53,91 @@ impl QueryArgs { wait_for_input()?; } let mut db = SQLiteReader::open(&db_path)?; - self.target.run(&mut db)?; + let stitcher_stats = self.target.run(&mut db)?; if self.stats { - let stats = db.stats(); - printdoc! {r#" - - db stats | loads | cached - ------------+--------+-------- - files | {:>6} | {:>6} - root paths | {:>6} | {:>6} - node paths | {:>6} | {:>6} - "#, - stats.file_loads, stats.file_cached, - stats.root_path_loads, stats.root_path_cached, - stats.node_path_loads, stats.node_path_cached, - }; + Self::print_stats(stitcher_stats, db.stats()); } Ok(()) } + + fn print_stats(stitcher_stats: StitchingStats, db_stats: StorageStats) { + fn quartiles(hist: FrequencyDistribution) -> String { + let qs = hist.quantiles(4); + if qs.is_empty() { + format!( + "{:>7} | {:>7} | {:>7} | {:>7} | {:>7} | {:>7}", + "-", "-", "-", "-", "-", 0 + ) + } else { + format!( + "{:>7} | {:>7} | {:>7} | {:>7} | {:>7} | {:>7}", + qs[0], + qs[1], + qs[2], + qs[3], + qs[4], + hist.total(), + ) + } + } + println!(" stitching stats | min | p25 | p50 | p75 | max | total "); + println!("---------------------------+---------+---------+---------+---------+---------+---------"); + println!( + " queued paths per phase | {} ", + quartiles(stitcher_stats.queued_paths_per_phase) + ); + println!( + " processed paths per phase | {} ", + quartiles(stitcher_stats.processed_paths_per_phase) + ); + println!( + " accepted path length | {} ", + quartiles(stitcher_stats.accepted_path_length) + ); + println!( + " maximal path length | {} ", + quartiles(stitcher_stats.maximal_path_lengh) + ); + println!( + " node path candidates | {} ", + quartiles(stitcher_stats.candidates_per_node_path) + ); + println!( + " node path extensions | {} ", + quartiles(stitcher_stats.extensions_per_node_path) + ); + println!( + " root path candidates | {} ", + quartiles(stitcher_stats.candidates_per_root_path) + ); + println!( + " root path extensions | {} ", + quartiles(stitcher_stats.extensions_per_root_path) + ); + println!( + " node visits | {} ", + quartiles(stitcher_stats.node_visits.frequencies()) + ); + println!( + " root visits | {:>7} ", + stitcher_stats.root_visits + ); + println!(); + println!(" database stats | loads | cached "); + println!("---------------------------+---------+---------"); + println!( + " files | {:>7} | {:>7} ", + db_stats.file_loads, db_stats.file_cached + ); + println!( + " node paths | {:>7} | {:>7} ", + db_stats.node_path_loads, db_stats.node_path_cached + ); + println!( + " root paths | {:>7} | {:>7} ", + db_stats.root_path_loads, db_stats.root_path_cached + ); + } } #[derive(Subcommand)] @@ -74,7 +146,7 @@ pub enum Target { } impl Target { - pub fn run(self, db: &mut SQLiteReader) -> anyhow::Result<()> { + pub fn run(self, db: &mut SQLiteReader) -> anyhow::Result { let reporter = ConsoleReporter::details(); let mut querier = Querier::new(db, &reporter); match self { @@ -96,13 +168,16 @@ pub struct Definition { } impl Definition { - pub fn run(self, querier: &mut Querier) -> anyhow::Result<()> { + pub fn run(self, querier: &mut Querier) -> anyhow::Result { let cancellation_flag = NoCancellation; + let mut stats = StitchingStats::default(); let mut file_reader = FileReader::new(); for mut reference in self.references { reference.canonicalize()?; - let results = querier.definitions(reference.clone(), &cancellation_flag)?; + let (results, ref_stats) = + querier.definitions(reference.clone(), &cancellation_flag)?; + stats += &ref_stats; let numbered = results.len() > 1; let indent = if numbered { 6 } else { 0 }; if numbered { @@ -150,7 +225,7 @@ impl Definition { } } } - Ok(()) + Ok(stats) } } @@ -168,7 +243,7 @@ impl<'a> Querier<'a> { &mut self, reference: SourcePosition, cancellation_flag: &dyn CancellationFlag, - ) -> Result> { + ) -> Result<(Vec, StitchingStats)> { let log_path = PathBuf::from(reference.to_string()); let mut file_reader = FileReader::new(); @@ -181,7 +256,7 @@ impl<'a> Querier<'a> { _ => { self.reporter.started(&log_path); self.reporter.failed(&log_path, "file not indexed", None); - return Ok(Vec::default()); + return Ok(Default::default()); } } @@ -195,10 +270,11 @@ impl<'a> Querier<'a> { if starting_nodes.is_empty() { self.reporter .cancelled(&log_path, "no references at location", None); - return Ok(Vec::default()); + return Ok(Default::default()); } let mut result = Vec::new(); + let mut stats = StitchingStats::default(); for (node, span) in starting_nodes { let reference_span = SourceSpan { path: reference.path.clone(), @@ -209,7 +285,7 @@ impl<'a> Querier<'a> { let stitcher_config = StitcherConfig::default() // always detect similar paths, we don't know the language configurations for the data in the database .with_detect_similar_paths(true); - if let Err(err) = ForwardPartialPathStitcher::find_all_complete_partial_paths( + let ref_result = ForwardPartialPathStitcher::find_all_complete_partial_paths( self.db, std::iter::once(node), stitcher_config, @@ -217,9 +293,13 @@ impl<'a> Querier<'a> { |_g, _ps, p| { reference_paths.push(p.clone()); }, - ) { - self.reporter.failed(&log_path, "query timed out", None); - return Err(err.into()); + ); + match ref_result { + Ok(ref_stats) => stats += &ref_stats, + Err(err) => { + self.reporter.failed(&log_path, "query timed out", None); + return Err(err.into()); + } } let (graph, partials, _) = self.db.get(); @@ -269,7 +349,7 @@ impl<'a> Querier<'a> { None, ); - Ok(result) + Ok((result, stats)) } } From 9a49f57ec34b31d8b08b968b74da065e3f068320 Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Wed, 25 Oct 2023 09:18:21 +0100 Subject: [PATCH 065/291] Move stats printing to util module --- tree-sitter-stack-graphs/src/cli/query.rs | 89 ++--------------------- tree-sitter-stack-graphs/src/cli/util.rs | 87 ++++++++++++++++++++++ 2 files changed, 92 insertions(+), 84 deletions(-) diff --git a/tree-sitter-stack-graphs/src/cli/query.rs b/tree-sitter-stack-graphs/src/cli/query.rs index 74cbb658f..99c07e668 100644 --- a/tree-sitter-stack-graphs/src/cli/query.rs +++ b/tree-sitter-stack-graphs/src/cli/query.rs @@ -5,8 +5,6 @@ // Please see the LICENSE-APACHE or LICENSE-MIT files in this distribution for license details. // ------------------------------------------------------------------------------------------------ -use std::fmt::Display; -use std::hash::Hash; use std::path::Path; use std::path::PathBuf; @@ -14,16 +12,16 @@ use clap::Args; use clap::Parser; use clap::Subcommand; use clap::ValueHint; -use stack_graphs::stats::FrequencyDistribution; use stack_graphs::stitching::ForwardPartialPathStitcher; use stack_graphs::stitching::Stats as StitchingStats; use stack_graphs::stitching::StitcherConfig; use stack_graphs::storage::FileStatus; use stack_graphs::storage::SQLiteReader; -use stack_graphs::storage::Stats as StorageStats; use thiserror::Error; use tree_sitter_graph::parse_error::Excerpt; +use crate::cli::util::print_database_stats; +use crate::cli::util::print_stitcher_stats; use crate::cli::util::reporter::ConsoleReporter; use crate::cli::util::reporter::Reporter; use crate::cli::util::sha1; @@ -55,89 +53,12 @@ impl QueryArgs { let mut db = SQLiteReader::open(&db_path)?; let stitcher_stats = self.target.run(&mut db)?; if self.stats { - Self::print_stats(stitcher_stats, db.stats()); + print_stitcher_stats(stitcher_stats); + println!(); + print_database_stats(db.stats()); } Ok(()) } - - fn print_stats(stitcher_stats: StitchingStats, db_stats: StorageStats) { - fn quartiles(hist: FrequencyDistribution) -> String { - let qs = hist.quantiles(4); - if qs.is_empty() { - format!( - "{:>7} | {:>7} | {:>7} | {:>7} | {:>7} | {:>7}", - "-", "-", "-", "-", "-", 0 - ) - } else { - format!( - "{:>7} | {:>7} | {:>7} | {:>7} | {:>7} | {:>7}", - qs[0], - qs[1], - qs[2], - qs[3], - qs[4], - hist.total(), - ) - } - } - println!(" stitching stats | min | p25 | p50 | p75 | max | total "); - println!("---------------------------+---------+---------+---------+---------+---------+---------"); - println!( - " queued paths per phase | {} ", - quartiles(stitcher_stats.queued_paths_per_phase) - ); - println!( - " processed paths per phase | {} ", - quartiles(stitcher_stats.processed_paths_per_phase) - ); - println!( - " accepted path length | {} ", - quartiles(stitcher_stats.accepted_path_length) - ); - println!( - " maximal path length | {} ", - quartiles(stitcher_stats.maximal_path_lengh) - ); - println!( - " node path candidates | {} ", - quartiles(stitcher_stats.candidates_per_node_path) - ); - println!( - " node path extensions | {} ", - quartiles(stitcher_stats.extensions_per_node_path) - ); - println!( - " root path candidates | {} ", - quartiles(stitcher_stats.candidates_per_root_path) - ); - println!( - " root path extensions | {} ", - quartiles(stitcher_stats.extensions_per_root_path) - ); - println!( - " node visits | {} ", - quartiles(stitcher_stats.node_visits.frequencies()) - ); - println!( - " root visits | {:>7} ", - stitcher_stats.root_visits - ); - println!(); - println!(" database stats | loads | cached "); - println!("---------------------------+---------+---------"); - println!( - " files | {:>7} | {:>7} ", - db_stats.file_loads, db_stats.file_cached - ); - println!( - " node paths | {:>7} | {:>7} ", - db_stats.node_path_loads, db_stats.node_path_cached - ); - println!( - " root paths | {:>7} | {:>7} ", - db_stats.root_path_loads, db_stats.root_path_cached - ); - } } #[derive(Subcommand)] diff --git a/tree-sitter-stack-graphs/src/cli/util.rs b/tree-sitter-stack-graphs/src/cli/util.rs index 7d50c8a44..91c861a8c 100644 --- a/tree-sitter-stack-graphs/src/cli/util.rs +++ b/tree-sitter-stack-graphs/src/cli/util.rs @@ -18,8 +18,13 @@ use sha1::Sha1; use stack_graphs::arena::Handle; use stack_graphs::graph::Node; use stack_graphs::graph::StackGraph; +use stack_graphs::stats::FrequencyDistribution; +use stack_graphs::stitching::Stats as StitchingStats; +use stack_graphs::storage::Stats as StorageStats; use std::ffi::OsStr; use std::ffi::OsString; +use std::fmt::Display; +use std::hash::Hash; use std::io::Write; use std::ops::Range; use std::path::Path; @@ -495,3 +500,85 @@ impl std::fmt::Display for DisplayBuildErrorPretty<'_> { ) } } + +pub(super) fn print_stitcher_stats(stats: StitchingStats) { + fn quartiles(hist: FrequencyDistribution) -> String { + let qs = hist.quantiles(4); + if qs.is_empty() { + format!( + "{:>7} | {:>7} | {:>7} | {:>7} | {:>7} | {:>7}", + "-", "-", "-", "-", "-", 0 + ) + } else { + format!( + "{:>7} | {:>7} | {:>7} | {:>7} | {:>7} | {:>7}", + qs[0], + qs[1], + qs[2], + qs[3], + qs[4], + hist.total(), + ) + } + } + println!( + " stitching stats | min | p25 | p50 | p75 | max | total " + ); + println!( + "---------------------------+---------+---------+---------+---------+---------+---------" + ); + println!( + " queued paths per phase | {} ", + quartiles(stats.queued_paths_per_phase) + ); + println!( + " processed paths per phase | {} ", + quartiles(stats.processed_paths_per_phase) + ); + println!( + " accepted path length | {} ", + quartiles(stats.accepted_path_length) + ); + println!( + " maximal path length | {} ", + quartiles(stats.maximal_path_lengh) + ); + println!( + " node path candidates | {} ", + quartiles(stats.candidates_per_node_path) + ); + println!( + " node path extensions | {} ", + quartiles(stats.extensions_per_node_path) + ); + println!( + " root path candidates | {} ", + quartiles(stats.candidates_per_root_path) + ); + println!( + " root path extensions | {} ", + quartiles(stats.extensions_per_root_path) + ); + println!( + " node visits | {} ", + quartiles(stats.node_visits.frequencies()) + ); + println!(" root visits | {:>7} ", stats.root_visits); +} + +pub(super) fn print_database_stats(stats: StorageStats) { + println!(" database stats | loads | cached "); + println!("---------------------------+---------+---------"); + println!( + " files | {:>7} | {:>7} ", + stats.file_loads, stats.file_cached + ); + println!( + " node paths | {:>7} | {:>7} ", + stats.node_path_loads, stats.node_path_cached + ); + println!( + " root paths | {:>7} | {:>7} ", + stats.root_path_loads, stats.root_path_cached + ); +} From 350b46d83467062097cfa5115ce8d42cd0901c85 Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Wed, 25 Oct 2023 09:41:24 +0100 Subject: [PATCH 066/291] Add stats to indexing --- tree-sitter-stack-graphs/src/cli/index.rs | 24 +++++++++++++++++++- tree-sitter-stack-graphs/src/cli/query.rs | 27 ++++++++++++++++------- 2 files changed, 42 insertions(+), 9 deletions(-) diff --git a/tree-sitter-stack-graphs/src/cli/index.rs b/tree-sitter-stack-graphs/src/cli/index.rs index dab12e537..3c48f624a 100644 --- a/tree-sitter-stack-graphs/src/cli/index.rs +++ b/tree-sitter-stack-graphs/src/cli/index.rs @@ -12,6 +12,7 @@ use stack_graphs::graph::File; use stack_graphs::graph::StackGraph; use stack_graphs::partial::PartialPaths; use stack_graphs::stitching::ForwardPartialPathStitcher; +use stack_graphs::stitching::Stats as StitchingStats; use stack_graphs::stitching::StitcherConfig; use stack_graphs::storage::FileStatus; use stack_graphs::storage::SQLiteWriter; @@ -24,6 +25,7 @@ use tree_sitter_graph::Variables; use crate::cli::util::duration_from_seconds_str; use crate::cli::util::iter_files_and_directories; +use crate::cli::util::print_stitcher_stats; use crate::cli::util::reporter::ConsoleReporter; use crate::cli::util::reporter::Level; use crate::cli::util::reporter::Reporter; @@ -79,6 +81,9 @@ pub struct IndexArgs { )] pub max_file_time: Option, + #[clap(long)] + pub stats: bool, + /// Wait for user input before starting analysis. Useful for profiling. #[clap(long)] pub wait_at_start: bool, @@ -94,6 +99,7 @@ impl IndexArgs { hide_error_details: false, max_file_time: None, wait_at_start: false, + stats: false, } } @@ -113,6 +119,10 @@ impl IndexArgs { .map(|p| p.canonicalize()) .collect::, _>>()?; indexer.index_all(source_paths, self.continue_from, &NoCancellation)?; + + if self.stats { + print_stitcher_stats(indexer.into_stats()); + } Ok(()) } @@ -146,6 +156,7 @@ pub struct Indexer<'a> { db: &'a mut SQLiteWriter, loader: &'a mut Loader, reporter: &'a dyn Reporter, + stats: StitchingStats, /// Index files, even if they already exist in the database. pub force: bool, /// Maximum time per file. @@ -164,6 +175,7 @@ impl<'a> Indexer<'a> { reporter, force: false, max_file_time: None, + stats: StitchingStats::default(), } } @@ -364,7 +376,9 @@ impl<'a> Indexer<'a> { paths.push(p.clone()); }, ) { - Ok(_) => {} + Ok(stats) => { + self.stats += &stats; + } Err(_) => { file_status.warning("path computation timed out", None); self.db.store_error_for_file( @@ -442,6 +456,14 @@ impl<'a> Indexer<'a> { *continue_from = None; false } + + pub fn stats(&self) -> &StitchingStats { + &self.stats + } + + pub fn into_stats(self) -> StitchingStats { + self.stats + } } #[derive(Debug, Error)] diff --git a/tree-sitter-stack-graphs/src/cli/query.rs b/tree-sitter-stack-graphs/src/cli/query.rs index 99c07e668..2132b88ea 100644 --- a/tree-sitter-stack-graphs/src/cli/query.rs +++ b/tree-sitter-stack-graphs/src/cli/query.rs @@ -96,9 +96,8 @@ impl Definition { for mut reference in self.references { reference.canonicalize()?; - let (results, ref_stats) = - querier.definitions(reference.clone(), &cancellation_flag)?; - stats += &ref_stats; + let results = querier.definitions(reference.clone(), &cancellation_flag)?; + stats += querier.stats(); let numbered = results.len() > 1; let indent = if numbered { 6 } else { 0 }; if numbered { @@ -153,18 +152,23 @@ impl Definition { pub struct Querier<'a> { db: &'a mut SQLiteReader, reporter: &'a dyn Reporter, + stats: StitchingStats, } impl<'a> Querier<'a> { pub fn new(db: &'a mut SQLiteReader, reporter: &'a dyn Reporter) -> Self { - Self { db, reporter } + Self { + db, + reporter, + stats: StitchingStats::default(), + } } pub fn definitions( &mut self, reference: SourcePosition, cancellation_flag: &dyn CancellationFlag, - ) -> Result<(Vec, StitchingStats)> { + ) -> Result> { let log_path = PathBuf::from(reference.to_string()); let mut file_reader = FileReader::new(); @@ -195,7 +199,6 @@ impl<'a> Querier<'a> { } let mut result = Vec::new(); - let mut stats = StitchingStats::default(); for (node, span) in starting_nodes { let reference_span = SourceSpan { path: reference.path.clone(), @@ -216,7 +219,7 @@ impl<'a> Querier<'a> { }, ); match ref_result { - Ok(ref_stats) => stats += &ref_stats, + Ok(ref_stats) => self.stats += &ref_stats, Err(err) => { self.reporter.failed(&log_path, "query timed out", None); return Err(err.into()); @@ -270,7 +273,15 @@ impl<'a> Querier<'a> { None, ); - Ok((result, stats)) + Ok(result) + } + + pub fn stats(&self) -> &StitchingStats { + &self.stats + } + + pub fn into_stats(self) -> StitchingStats { + self.stats } } From c36df79356438130ac7e38ab01f44c65a330e6dd Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Wed, 25 Oct 2023 09:59:42 +0100 Subject: [PATCH 067/291] Make whitespace before stats consistent --- tree-sitter-stack-graphs/src/cli/index.rs | 1 + tree-sitter-stack-graphs/src/cli/query.rs | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/tree-sitter-stack-graphs/src/cli/index.rs b/tree-sitter-stack-graphs/src/cli/index.rs index 3c48f624a..63b17cbb8 100644 --- a/tree-sitter-stack-graphs/src/cli/index.rs +++ b/tree-sitter-stack-graphs/src/cli/index.rs @@ -121,6 +121,7 @@ impl IndexArgs { indexer.index_all(source_paths, self.continue_from, &NoCancellation)?; if self.stats { + println!(); print_stitcher_stats(indexer.into_stats()); } Ok(()) diff --git a/tree-sitter-stack-graphs/src/cli/query.rs b/tree-sitter-stack-graphs/src/cli/query.rs index 2132b88ea..451a57271 100644 --- a/tree-sitter-stack-graphs/src/cli/query.rs +++ b/tree-sitter-stack-graphs/src/cli/query.rs @@ -53,6 +53,7 @@ impl QueryArgs { let mut db = SQLiteReader::open(&db_path)?; let stitcher_stats = self.target.run(&mut db)?; if self.stats { + println!(); print_stitcher_stats(stitcher_stats); println!(); print_database_stats(db.stats()); @@ -132,7 +133,7 @@ impl Definition { n => println!("{}has {} definitions", " ".repeat(indent), n), } for definition in definitions.into_iter() { - println!( + print!( "{}", Excerpt::from_source( &definition.path, From 5e86ee868903cb1bad6df4be8f018e971ce2f645 Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Wed, 25 Oct 2023 10:03:38 +0100 Subject: [PATCH 068/291] Use consistent names --- tree-sitter-stack-graphs/src/cli/index.rs | 4 ++-- tree-sitter-stack-graphs/src/cli/query.rs | 6 +++--- tree-sitter-stack-graphs/src/cli/util.rs | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/tree-sitter-stack-graphs/src/cli/index.rs b/tree-sitter-stack-graphs/src/cli/index.rs index 63b17cbb8..9a03bf1b6 100644 --- a/tree-sitter-stack-graphs/src/cli/index.rs +++ b/tree-sitter-stack-graphs/src/cli/index.rs @@ -25,7 +25,7 @@ use tree_sitter_graph::Variables; use crate::cli::util::duration_from_seconds_str; use crate::cli::util::iter_files_and_directories; -use crate::cli::util::print_stitcher_stats; +use crate::cli::util::print_stitching_stats; use crate::cli::util::reporter::ConsoleReporter; use crate::cli::util::reporter::Level; use crate::cli::util::reporter::Reporter; @@ -122,7 +122,7 @@ impl IndexArgs { if self.stats { println!(); - print_stitcher_stats(indexer.into_stats()); + print_stitching_stats(indexer.into_stats()); } Ok(()) } diff --git a/tree-sitter-stack-graphs/src/cli/query.rs b/tree-sitter-stack-graphs/src/cli/query.rs index 451a57271..42c210e83 100644 --- a/tree-sitter-stack-graphs/src/cli/query.rs +++ b/tree-sitter-stack-graphs/src/cli/query.rs @@ -21,7 +21,7 @@ use thiserror::Error; use tree_sitter_graph::parse_error::Excerpt; use crate::cli::util::print_database_stats; -use crate::cli::util::print_stitcher_stats; +use crate::cli::util::print_stitching_stats; use crate::cli::util::reporter::ConsoleReporter; use crate::cli::util::reporter::Reporter; use crate::cli::util::sha1; @@ -51,10 +51,10 @@ impl QueryArgs { wait_for_input()?; } let mut db = SQLiteReader::open(&db_path)?; - let stitcher_stats = self.target.run(&mut db)?; + let stitching_stats = self.target.run(&mut db)?; if self.stats { println!(); - print_stitcher_stats(stitcher_stats); + print_stitching_stats(stitching_stats); println!(); print_database_stats(db.stats()); } diff --git a/tree-sitter-stack-graphs/src/cli/util.rs b/tree-sitter-stack-graphs/src/cli/util.rs index 91c861a8c..ff2cddb32 100644 --- a/tree-sitter-stack-graphs/src/cli/util.rs +++ b/tree-sitter-stack-graphs/src/cli/util.rs @@ -501,7 +501,7 @@ impl std::fmt::Display for DisplayBuildErrorPretty<'_> { } } -pub(super) fn print_stitcher_stats(stats: StitchingStats) { +pub(super) fn print_stitching_stats(stats: StitchingStats) { fn quartiles(hist: FrequencyDistribution) -> String { let qs = hist.quantiles(4); if qs.is_empty() { From 61122ba1dd08cfdc199057b5c5096d10a785320a Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Wed, 25 Oct 2023 10:11:43 +0100 Subject: [PATCH 069/291] Give stats more space --- tree-sitter-stack-graphs/src/cli/util.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/tree-sitter-stack-graphs/src/cli/util.rs b/tree-sitter-stack-graphs/src/cli/util.rs index ff2cddb32..882fd1bd3 100644 --- a/tree-sitter-stack-graphs/src/cli/util.rs +++ b/tree-sitter-stack-graphs/src/cli/util.rs @@ -506,12 +506,12 @@ pub(super) fn print_stitching_stats(stats: StitchingStats) { let qs = hist.quantiles(4); if qs.is_empty() { format!( - "{:>7} | {:>7} | {:>7} | {:>7} | {:>7} | {:>7}", + "{:>9} | {:>9} | {:>9} | {:>9} | {:>9} | {:>9}", "-", "-", "-", "-", "-", 0 ) } else { format!( - "{:>7} | {:>7} | {:>7} | {:>7} | {:>7} | {:>7}", + "{:>9} | {:>9} | {:>9} | {:>9} | {:>9} | {:>9}", qs[0], qs[1], qs[2], @@ -522,10 +522,10 @@ pub(super) fn print_stitching_stats(stats: StitchingStats) { } } println!( - " stitching stats | min | p25 | p50 | p75 | max | total " + " stitching stats | min | p25 | p50 | p75 | max | total " ); println!( - "---------------------------+---------+---------+---------+---------+---------+---------" + "---------------------------+-----------+-----------+-----------+-----------+-----------+-----------" ); println!( " queued paths per phase | {} ", @@ -563,22 +563,22 @@ pub(super) fn print_stitching_stats(stats: StitchingStats) { " node visits | {} ", quartiles(stats.node_visits.frequencies()) ); - println!(" root visits | {:>7} ", stats.root_visits); + println!(" root visits | {:>9} ", stats.root_visits); } pub(super) fn print_database_stats(stats: StorageStats) { println!(" database stats | loads | cached "); println!("---------------------------+---------+---------"); println!( - " files | {:>7} | {:>7} ", + " files | {:>9} | {:>9} ", stats.file_loads, stats.file_cached ); println!( - " node paths | {:>7} | {:>7} ", + " node paths | {:>9} | {:>9} ", stats.node_path_loads, stats.node_path_cached ); println!( - " root paths | {:>7} | {:>7} ", + " root paths | {:>9} | {:>9} ", stats.root_path_loads, stats.root_path_cached ); } From 57119900f231598e12005025b78125240aa1ddba Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Fri, 27 Oct 2023 20:32:26 +0100 Subject: [PATCH 070/291] Replace += with record() --- stack-graphs/src/stats.rs | 24 ++++++++++++--- stack-graphs/src/stitching.rs | 37 +++++++++++++++++------ stack-graphs/tests/it/stats.rs | 17 ++++++++--- tree-sitter-stack-graphs/src/cli/index.rs | 2 +- tree-sitter-stack-graphs/src/cli/query.rs | 11 +++---- 5 files changed, 64 insertions(+), 27 deletions(-) diff --git a/stack-graphs/src/stats.rs b/stack-graphs/src/stats.rs index b74098f12..62bde332b 100644 --- a/stack-graphs/src/stats.rs +++ b/stack-graphs/src/stats.rs @@ -1,3 +1,10 @@ +// -*- coding: utf-8 -*- +// ------------------------------------------------------------------------------------------------ +// Copyright © 2023, stack-graphs authors. +// Licensed under either of Apache License, Version 2.0, or MIT license, at your option. +// Please see the LICENSE-APACHE or LICENSE-MIT files in this distribution for license details. +// ------------------------------------------------------------------------------------------------ + use std::collections::HashMap; use std::hash::Hash; @@ -14,6 +21,11 @@ where } impl FrequencyDistribution { + pub fn record(&mut self, value: T) { + *self.values.entry(value).or_default() += 1; + self.total += 1; + } + pub fn total(&self) -> usize { return self.total; } @@ -25,7 +37,7 @@ impl FrequencyDistribution { pub fn frequencies(&self) -> FrequencyDistribution { let mut fs = FrequencyDistribution::default(); for count in self.values.values() { - fs += *count + fs.record(*count); } fs } @@ -67,13 +79,15 @@ impl FrequencyDistribution { } } -impl std::ops::AddAssign for FrequencyDistribution +impl std::ops::AddAssign for FrequencyDistribution where T: Eq + Hash, { - fn add_assign(&mut self, rhs: T) { - *self.values.entry(rhs).or_default() += 1; - self.total += 1; + fn add_assign(&mut self, rhs: Self) { + for (value, count) in rhs.values { + *self.values.entry(value).or_default() += count; + } + self.total += rhs.total; } } diff --git a/stack-graphs/src/stitching.rs b/stack-graphs/src/stitching.rs index 51aab53ba..c5e9b97ed 100644 --- a/stack-graphs/src/stitching.rs +++ b/stack-graphs/src/stitching.rs @@ -1066,17 +1066,19 @@ impl ForwardPartialPathStitcher { let (graph, _, _) = candidates.get_graph_partials_and_db(); let end_node = &graph[partial_path.end_node]; if end_node.is_root() { - self.stats.candidates_per_root_path += candidate_count; - self.stats.extensions_per_root_path += extension_count; + self.stats.candidates_per_root_path.record(candidate_count); + self.stats.extensions_per_root_path.record(extension_count); self.stats.root_visits += 1; } else { - self.stats.candidates_per_node_path += candidate_count; - self.stats.extensions_per_node_path += extension_count; - self.stats.node_visits += end_node.id(); + self.stats.candidates_per_node_path.record(candidate_count); + self.stats.extensions_per_node_path.record(extension_count); + self.stats.node_visits.record(end_node.id()); } if extension_count == 0 { - self.stats.maximal_path_lengh += partial_path.edges.len(); + self.stats + .maximal_path_lengh + .record(partial_path.edges.len()); } candidate_count } @@ -1111,7 +1113,7 @@ impl ForwardPartialPathStitcher { self.next_iteration.1.drain(..), self.next_iteration.2.drain(..), )); - self.stats.queued_paths_per_phase += self.queue.len(); + self.stats.queued_paths_per_phase.record(self.queue.len()); let mut work_performed = 0; while let Some((partial_path, cycle_detector, has_split)) = self.queue.pop_front() { let (graph, partials, _) = candidates.get_graph_partials_and_db(); @@ -1133,7 +1135,7 @@ impl ForwardPartialPathStitcher { break; } } - self.stats.processed_paths_per_phase += work_performed; + self.stats.processed_paths_per_phase.record(work_performed); #[cfg(feature = "copious-debugging")] { @@ -1202,7 +1204,7 @@ impl ForwardPartialPathStitcher { ); for path in stitcher.previous_phase_partial_paths() { if as_complete_as_necessary(graph, path) { - accepted_path_length += path.edges.len(); + accepted_path_length.record(path.edges.len()); visit(graph, partials, path); } } @@ -1267,7 +1269,7 @@ impl ForwardPartialPathStitcher { let (graph, partials, _) = candidates.get_graph_partials_and_db(); for path in stitcher.previous_phase_partial_paths() { if path.is_complete(graph) { - accepted_path_length += path.edges.len(); + accepted_path_length.record(path.edges.len()); visit(graph, partials, path); } } @@ -1304,6 +1306,21 @@ pub struct Stats { pub node_visits: FrequencyDistribution, } +impl std::ops::AddAssign for Stats { + fn add_assign(&mut self, rhs: Self) { + self.queued_paths_per_phase += rhs.queued_paths_per_phase; + self.processed_paths_per_phase += rhs.processed_paths_per_phase; + self.accepted_path_length += rhs.accepted_path_length; + self.maximal_path_lengh += rhs.maximal_path_lengh; + self.candidates_per_node_path += rhs.candidates_per_node_path; + self.candidates_per_root_path += rhs.candidates_per_root_path; + self.extensions_per_node_path += rhs.extensions_per_node_path; + self.extensions_per_root_path += rhs.extensions_per_root_path; + self.root_visits += rhs.root_visits; + self.node_visits += rhs.node_visits; + } +} + impl std::ops::AddAssign<&Self> for Stats { fn add_assign(&mut self, rhs: &Self) { self.queued_paths_per_phase += &rhs.queued_paths_per_phase; diff --git a/stack-graphs/tests/it/stats.rs b/stack-graphs/tests/it/stats.rs index f4a48cd03..d0ebd4757 100644 --- a/stack-graphs/tests/it/stats.rs +++ b/stack-graphs/tests/it/stats.rs @@ -1,3 +1,10 @@ +// -*- coding: utf-8 -*- +// ------------------------------------------------------------------------------------------------ +// Copyright © 2023, stack-graphs authors. +// Licensed under either of Apache License, Version 2.0, or MIT license, at your option. +// Please see the LICENSE-APACHE or LICENSE-MIT files in this distribution for license details. +// ------------------------------------------------------------------------------------------------ + use itertools::Itertools; use pretty_assertions::assert_eq; @@ -18,7 +25,7 @@ fn empty_distribution() { #[test] fn singleton_distribution() { let mut hist = FrequencyDistribution::default(); - hist += 42; + hist.record(42); assert_eq!(1, hist.unique()); assert_eq!(1, hist.total()); @@ -31,10 +38,10 @@ fn singleton_distribution() { #[test] fn four_value_distribution() { let mut hist = FrequencyDistribution::default(); - hist += 3; - hist += 4; - hist += 1; - hist += 2; + hist.record(3); + hist.record(4); + hist.record(1); + hist.record(2); assert_eq!(4, hist.unique()); assert_eq!(4, hist.total()); diff --git a/tree-sitter-stack-graphs/src/cli/index.rs b/tree-sitter-stack-graphs/src/cli/index.rs index 9a03bf1b6..bbfda9d22 100644 --- a/tree-sitter-stack-graphs/src/cli/index.rs +++ b/tree-sitter-stack-graphs/src/cli/index.rs @@ -378,7 +378,7 @@ impl<'a> Indexer<'a> { }, ) { Ok(stats) => { - self.stats += &stats; + self.stats += stats; } Err(_) => { file_status.warning("path computation timed out", None); diff --git a/tree-sitter-stack-graphs/src/cli/query.rs b/tree-sitter-stack-graphs/src/cli/query.rs index 42c210e83..1c57145fe 100644 --- a/tree-sitter-stack-graphs/src/cli/query.rs +++ b/tree-sitter-stack-graphs/src/cli/query.rs @@ -72,8 +72,9 @@ impl Target { let reporter = ConsoleReporter::details(); let mut querier = Querier::new(db, &reporter); match self { - Self::Definition(cmd) => cmd.run(&mut querier), + Self::Definition(cmd) => cmd.run(&mut querier)?, } + Ok(querier.into_stats()) } } @@ -90,15 +91,13 @@ pub struct Definition { } impl Definition { - pub fn run(self, querier: &mut Querier) -> anyhow::Result { + pub fn run(self, querier: &mut Querier) -> anyhow::Result<()> { let cancellation_flag = NoCancellation; - let mut stats = StitchingStats::default(); let mut file_reader = FileReader::new(); for mut reference in self.references { reference.canonicalize()?; let results = querier.definitions(reference.clone(), &cancellation_flag)?; - stats += querier.stats(); let numbered = results.len() > 1; let indent = if numbered { 6 } else { 0 }; if numbered { @@ -146,7 +145,7 @@ impl Definition { } } } - Ok(stats) + Ok(()) } } @@ -220,7 +219,7 @@ impl<'a> Querier<'a> { }, ); match ref_result { - Ok(ref_stats) => self.stats += &ref_stats, + Ok(ref_stats) => self.stats += ref_stats, Err(err) => { self.reporter.failed(&log_path, "query timed out", None); return Err(err.into()); From 08a636f6ea1cbe0420e682db5774abc8f648bd91 Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Thu, 9 Nov 2023 19:16:20 +0100 Subject: [PATCH 071/291] Add some indexing stats --- tree-sitter-stack-graphs/src/cli/index.rs | 44 ++++++-- tree-sitter-stack-graphs/src/cli/util.rs | 123 +++++++++++----------- 2 files changed, 96 insertions(+), 71 deletions(-) diff --git a/tree-sitter-stack-graphs/src/cli/index.rs b/tree-sitter-stack-graphs/src/cli/index.rs index bbfda9d22..ac445b36f 100644 --- a/tree-sitter-stack-graphs/src/cli/index.rs +++ b/tree-sitter-stack-graphs/src/cli/index.rs @@ -11,6 +11,7 @@ use stack_graphs::arena::Handle; use stack_graphs::graph::File; use stack_graphs::graph::StackGraph; use stack_graphs::partial::PartialPaths; +use stack_graphs::stats::FrequencyDistribution; use stack_graphs::stitching::ForwardPartialPathStitcher; use stack_graphs::stitching::Stats as StitchingStats; use stack_graphs::stitching::StitcherConfig; @@ -25,7 +26,7 @@ use tree_sitter_graph::Variables; use crate::cli::util::duration_from_seconds_str; use crate::cli::util::iter_files_and_directories; -use crate::cli::util::print_stitching_stats; +use crate::cli::util::print_indexing_stats; use crate::cli::util::reporter::ConsoleReporter; use crate::cli::util::reporter::Level; use crate::cli::util::reporter::Reporter; @@ -122,7 +123,7 @@ impl IndexArgs { if self.stats { println!(); - print_stitching_stats(indexer.into_stats()); + print_indexing_stats(indexer.into_stats()); } Ok(()) } @@ -157,7 +158,7 @@ pub struct Indexer<'a> { db: &'a mut SQLiteWriter, loader: &'a mut Loader, reporter: &'a dyn Reporter, - stats: StitchingStats, + stats: IndexingStats, /// Index files, even if they already exist in the database. pub force: bool, /// Maximum time per file. @@ -176,7 +177,7 @@ impl<'a> Indexer<'a> { reporter, force: false, max_file_time: None, - stats: StitchingStats::default(), + stats: IndexingStats::default(), } } @@ -364,6 +365,21 @@ impl<'a> Indexer<'a> { } } }; + { + self.stats + .total_graph_nodes + .record(graph.iter_nodes().count()); + let mut total_edges = 0; + for node_edges in graph + .iter_nodes() + .filter(|n| !graph[*n].is_root()) + .map(|n| graph.outgoing_edges(n).count()) + { + self.stats.node_out_degrees.record(node_edges); + total_edges += node_edges; + } + self.stats.total_graph_edges.record(total_edges); + } let mut partials = PartialPaths::new(); let mut paths = Vec::new(); @@ -377,8 +393,8 @@ impl<'a> Indexer<'a> { paths.push(p.clone()); }, ) { - Ok(stats) => { - self.stats += stats; + Ok(stitching_stats) => { + self.stats.stitching_stats += stitching_stats; } Err(_) => { file_status.warning("path computation timed out", None); @@ -458,11 +474,11 @@ impl<'a> Indexer<'a> { false } - pub fn stats(&self) -> &StitchingStats { + pub fn stats(&self) -> &IndexingStats { &self.stats } - pub fn into_stats(self) -> StitchingStats { + pub fn into_stats(self) -> IndexingStats { self.stats } } @@ -488,3 +504,15 @@ impl From for IndexError { } type Result = std::result::Result; + +#[derive(Clone, Debug, Default)] +pub struct IndexingStats { + // The distribution of the total number of nodes per file graph. + pub total_graph_nodes: FrequencyDistribution, + // The distribution of the total number of edges per file graph. + pub total_graph_edges: FrequencyDistribution, + // The distribution of the out-degrees of non-root nodes. + pub node_out_degrees: FrequencyDistribution, + // The stitching statistics. + pub stitching_stats: StitchingStats, +} diff --git a/tree-sitter-stack-graphs/src/cli/util.rs b/tree-sitter-stack-graphs/src/cli/util.rs index 882fd1bd3..54864bea7 100644 --- a/tree-sitter-stack-graphs/src/cli/util.rs +++ b/tree-sitter-stack-graphs/src/cli/util.rs @@ -32,7 +32,8 @@ use std::path::PathBuf; use std::time::Duration; use walkdir::WalkDir; -use self::reporter::Reporter; +use crate::cli::index::IndexingStats; +use crate::cli::util::reporter::Reporter; pub mod reporter; @@ -501,69 +502,27 @@ impl std::fmt::Display for DisplayBuildErrorPretty<'_> { } } +pub(super) fn print_indexing_stats(stats: IndexingStats) { + print_quartiles_header("graph stats"); + print_quartiles_row("total graph nodes", stats.total_graph_nodes); + print_quartiles_row("total graph edges", stats.total_graph_edges); + print_quartiles_row("node out degrees", stats.node_out_degrees); + println!(); + print_stitching_stats(stats.stitching_stats); +} + pub(super) fn print_stitching_stats(stats: StitchingStats) { - fn quartiles(hist: FrequencyDistribution) -> String { - let qs = hist.quantiles(4); - if qs.is_empty() { - format!( - "{:>9} | {:>9} | {:>9} | {:>9} | {:>9} | {:>9}", - "-", "-", "-", "-", "-", 0 - ) - } else { - format!( - "{:>9} | {:>9} | {:>9} | {:>9} | {:>9} | {:>9}", - qs[0], - qs[1], - qs[2], - qs[3], - qs[4], - hist.total(), - ) - } - } - println!( - " stitching stats | min | p25 | p50 | p75 | max | total " - ); - println!( - "---------------------------+-----------+-----------+-----------+-----------+-----------+-----------" - ); - println!( - " queued paths per phase | {} ", - quartiles(stats.queued_paths_per_phase) - ); - println!( - " processed paths per phase | {} ", - quartiles(stats.processed_paths_per_phase) - ); - println!( - " accepted path length | {} ", - quartiles(stats.accepted_path_length) - ); - println!( - " maximal path length | {} ", - quartiles(stats.maximal_path_lengh) - ); - println!( - " node path candidates | {} ", - quartiles(stats.candidates_per_node_path) - ); - println!( - " node path extensions | {} ", - quartiles(stats.extensions_per_node_path) - ); - println!( - " root path candidates | {} ", - quartiles(stats.candidates_per_root_path) - ); - println!( - " root path extensions | {} ", - quartiles(stats.extensions_per_root_path) - ); - println!( - " node visits | {} ", - quartiles(stats.node_visits.frequencies()) - ); - println!(" root visits | {:>9} ", stats.root_visits); + print_quartiles_header("stitching stats"); + print_quartiles_row("queued paths per phase", stats.queued_paths_per_phase); + print_quartiles_row("processed paths per phase", stats.processed_paths_per_phase); + print_quartiles_row("accepted path length", stats.accepted_path_length); + print_quartiles_row("maximal path length", stats.maximal_path_lengh); + print_quartiles_row("node path candidates", stats.candidates_per_node_path); + print_quartiles_row("node path extensions", stats.extensions_per_node_path); + print_quartiles_row("root path candidates", stats.candidates_per_root_path); + print_quartiles_row("root path extensions", stats.extensions_per_root_path); + print_quartiles_row("node visits", stats.node_visits.frequencies()); + print_value_row("root visits", stats.root_visits); } pub(super) fn print_database_stats(stats: StorageStats) { @@ -582,3 +541,41 @@ pub(super) fn print_database_stats(stats: StorageStats) { stats.root_path_loads, stats.root_path_cached ); } + +fn print_quartiles_header(title: &str) { + println!( + "| {:^25} | min | p25 | p50 | p75 | max | total |", + title + ); + println!( + "|---------------------------|-----------|-----------|-----------|-----------|-----------|-----------|" + ); +} + +fn print_quartiles_row(title: &str, hist: FrequencyDistribution) { + let qs = hist.quantiles(4); + if qs.is_empty() { + println!( + "| {:>25} | {:>9} | {:>9} | {:>9} | {:>9} | {:>9} | {:>9} |", + title, "-", "-", "-", "-", "-", 0 + ); + } else { + println!( + "| {:>25} | {:>9} | {:>9} | {:>9} | {:>9} | {:>9} | {:>9} |", + title, + qs[0], + qs[1], + qs[2], + qs[3], + qs[4], + hist.total(), + ); + } +} + +fn print_value_row(title: &str, value: X) { + println!( + "| {:>25} | {:>9} | {:>9} | {:>9} | {:>9} | {:>9} | {:>9} |", + title, "-", "-", "-", "-", "-", value + ); +} From 6ed0f3cf64def2e622c7f8fb57565e3b7a0c571d Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Fri, 10 Nov 2023 11:05:33 +0100 Subject: [PATCH 072/291] Collect similar path stats --- stack-graphs/src/cycles.rs | 54 ++++++++++++++++++++++-- stack-graphs/src/stitching.rs | 14 ++++-- tree-sitter-stack-graphs/src/cli/util.rs | 8 ++++ 3 files changed, 70 insertions(+), 6 deletions(-) diff --git a/stack-graphs/src/cycles.rs b/stack-graphs/src/cycles.rs index d10679385..e989dc471 100644 --- a/stack-graphs/src/cycles.rs +++ b/stack-graphs/src/cycles.rs @@ -44,12 +44,18 @@ use crate::partial::Cyclicity; use crate::partial::PartialPath; use crate::partial::PartialPaths; use crate::paths::PathResolutionError; +use crate::stats::FrequencyDistribution; use crate::stitching::Appendable; use crate::stitching::ToAppendable; /// Helps detect similar paths in the path-finding algorithm. pub struct SimilarPathDetector

{ - paths: HashMap>, + paths: HashMap; 4]>>, +} + +struct SimilarPath

{ + path: P, + count: usize, } #[doc(hidden)] @@ -115,16 +121,20 @@ where // is shadowed by the new path! let possibly_similar_paths = self.paths.entry(key).or_default(); let mut idx = 0; + let mut count = 0; while idx < possibly_similar_paths.len() { - match cmp(arena, path, &possibly_similar_paths[idx]) { + let other_path = &mut possibly_similar_paths[idx]; + match cmp(arena, path, &other_path.path) { Some(Ordering::Less) => { // the new path is better, remove the old one + count += other_path.count; possibly_similar_paths.remove(idx); // keep `idx` which now points to the next element continue; } Some(_) => { // the new path is equal or worse, and ignored + other_path.count += 1; return true; } None => { @@ -133,7 +143,11 @@ where } } - possibly_similar_paths.push(path.clone()); + // this path is either new or better, keep it + possibly_similar_paths.push(SimilarPath { + path: path.clone(), + count, + }); false } @@ -141,6 +155,40 @@ where pub fn max_bucket_size(&self) -> usize { self.paths.iter().map(|b| b.1.len()).max().unwrap_or(0) } + + // Returns the distribution of similar path counts. + pub fn stats(&self) -> SimilarPathStats { + let mut stats = SimilarPathStats::default(); + for bucket in self.paths.values() { + stats.similar_path_bucket_size.record(bucket.len()); + for path in bucket.iter() { + stats.similar_path_count.record(path.count); + } + } + stats + } +} + +#[derive(Clone, Debug, Default)] +pub struct SimilarPathStats { + // The distribution of the number of similar paths detected + pub similar_path_count: FrequencyDistribution, + // The distribution of the internal bucket sizes in the similar path detector + pub similar_path_bucket_size: FrequencyDistribution, +} + +impl std::ops::AddAssign for SimilarPathStats { + fn add_assign(&mut self, rhs: Self) { + self.similar_path_bucket_size += rhs.similar_path_bucket_size; + self.similar_path_count += rhs.similar_path_count; + } +} + +impl std::ops::AddAssign<&Self> for SimilarPathStats { + fn add_assign(&mut self, rhs: &Self) { + self.similar_path_bucket_size += &rhs.similar_path_bucket_size; + self.similar_path_count += &rhs.similar_path_count; + } } // ---------------------------------------------------------------------------- diff --git a/stack-graphs/src/stitching.rs b/stack-graphs/src/stitching.rs index c5e9b97ed..9c1f4ef58 100644 --- a/stack-graphs/src/stitching.rs +++ b/stack-graphs/src/stitching.rs @@ -54,6 +54,7 @@ use crate::arena::SupplementalArena; use crate::cycles::Appendables; use crate::cycles::AppendingCycleDetector; use crate::cycles::SimilarPathDetector; +use crate::cycles::SimilarPathStats; use crate::graph::Degree; use crate::graph::Edge; use crate::graph::File; @@ -913,7 +914,10 @@ impl ForwardPartialPathStitcher { self.max_work_per_phase = max_work_per_phase; } - pub fn into_stats(self) -> Stats { + pub fn into_stats(mut self) -> Stats { + if let Some(similar_path_detector) = self.similar_path_detector { + self.stats.similar_paths_stats = similar_path_detector.stats(); + } self.stats } } @@ -1212,7 +1216,7 @@ impl ForwardPartialPathStitcher { Ok(Stats { accepted_path_length, - ..stitcher.stats + ..stitcher.into_stats() }) } } @@ -1277,7 +1281,7 @@ impl ForwardPartialPathStitcher { Ok(Stats { accepted_path_length, - ..stitcher.stats + ..stitcher.into_stats() }) } } @@ -1304,6 +1308,8 @@ pub struct Stats { pub root_visits: usize, /// The distribution of the number of times a regular node is visited pub node_visits: FrequencyDistribution, + /// The distribution of the number of similar paths between node pairs. + pub similar_paths_stats: SimilarPathStats, } impl std::ops::AddAssign for Stats { @@ -1318,6 +1324,7 @@ impl std::ops::AddAssign for Stats { self.extensions_per_root_path += rhs.extensions_per_root_path; self.root_visits += rhs.root_visits; self.node_visits += rhs.node_visits; + self.similar_paths_stats += rhs.similar_paths_stats; } } @@ -1333,6 +1340,7 @@ impl std::ops::AddAssign<&Self> for Stats { self.extensions_per_root_path += &rhs.extensions_per_root_path; self.root_visits += rhs.root_visits; self.node_visits += &rhs.node_visits; + self.similar_paths_stats += &rhs.similar_paths_stats; } } diff --git a/tree-sitter-stack-graphs/src/cli/util.rs b/tree-sitter-stack-graphs/src/cli/util.rs index 54864bea7..fd93046c4 100644 --- a/tree-sitter-stack-graphs/src/cli/util.rs +++ b/tree-sitter-stack-graphs/src/cli/util.rs @@ -522,6 +522,14 @@ pub(super) fn print_stitching_stats(stats: StitchingStats) { print_quartiles_row("root path candidates", stats.candidates_per_root_path); print_quartiles_row("root path extensions", stats.extensions_per_root_path); print_quartiles_row("node visits", stats.node_visits.frequencies()); + print_quartiles_row( + "similar path counts", + stats.similar_paths_stats.similar_path_count, + ); + print_quartiles_row( + "similar path bucket sizes", + stats.similar_paths_stats.similar_path_bucket_size, + ); print_value_row("root visits", stats.root_visits); } From d677b86893f7c494bc282542ce16e94a105ab1da Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Sat, 11 Nov 2023 17:26:40 +0100 Subject: [PATCH 073/291] Tweak stitching stats --- stack-graphs/src/stats.rs | 4 ++- stack-graphs/src/stitching.rs | 19 +++++++---- stack-graphs/tests/it/stats.rs | 6 ++-- tree-sitter-stack-graphs/src/cli/index.rs | 17 ++++++---- tree-sitter-stack-graphs/src/cli/util.rs | 39 +++++++++++++---------- 5 files changed, 50 insertions(+), 35 deletions(-) diff --git a/stack-graphs/src/stats.rs b/stack-graphs/src/stats.rs index 62bde332b..6b9f6ead8 100644 --- a/stack-graphs/src/stats.rs +++ b/stack-graphs/src/stats.rs @@ -26,10 +26,12 @@ impl FrequencyDistribution { self.total += 1; } - pub fn total(&self) -> usize { + // The number of recorded values. + pub fn count(&self) -> usize { return self.total; } + // The number of unique recorded values. pub fn unique(&self) -> usize { return self.values.len(); } diff --git a/stack-graphs/src/stitching.rs b/stack-graphs/src/stitching.rs index 9c1f4ef58..9a4cca91c 100644 --- a/stack-graphs/src/stitching.rs +++ b/stack-graphs/src/stitching.rs @@ -866,6 +866,8 @@ impl ForwardPartialPathStitcher { (p, c, false) }) .multiunzip(); + let mut stats = Stats::default(); + stats.initial_paths.record(next_iteration.0.len()); Self { candidates: Vec::new(), extensions: Vec::new(), @@ -879,7 +881,7 @@ impl ForwardPartialPathStitcher { check_only_join_nodes: false, // By default, there's no artificial bound on the amount of work done per phase max_work_per_phase: usize::MAX, - stats: Stats::default(), + stats, #[cfg(feature = "copious-debugging")] phase_number: 1, } @@ -1081,7 +1083,7 @@ impl ForwardPartialPathStitcher { if extension_count == 0 { self.stats - .maximal_path_lengh + .nonextensible_path_lengh .record(partial_path.edges.len()); } candidate_count @@ -1185,7 +1187,7 @@ impl ForwardPartialPathStitcher { { fn as_complete_as_necessary(graph: &StackGraph, path: &PartialPath) -> bool { path.starts_at_endpoint(graph) - && (path.ends_at_endpoint(graph) || graph[path.end_node].is_jump_to()) + && (path.ends_at_endpoint(graph) || path.ends_in_jump(graph)) } let initial_paths = graph @@ -1288,6 +1290,8 @@ impl ForwardPartialPathStitcher { #[derive(Clone, Debug, Default)] pub struct Stats { + /// The distribution of the number of initial paths + pub initial_paths: FrequencyDistribution, /// The distribution of the number of queued paths per stitching phase pub queued_paths_per_phase: FrequencyDistribution, /// The distribution of the number of processed paths per stitching phase @@ -1295,7 +1299,7 @@ pub struct Stats { /// The distribution of the length of accepted paths pub accepted_path_length: FrequencyDistribution, /// The distribution of the maximal length of paths (when they cannot be extended more) - pub maximal_path_lengh: FrequencyDistribution, + pub nonextensible_path_lengh: FrequencyDistribution, /// The distribution of the number of candidates for paths ending in a regular node pub candidates_per_node_path: FrequencyDistribution, /// The distribution of the number of candidates for paths ending in the root node @@ -1314,10 +1318,11 @@ pub struct Stats { impl std::ops::AddAssign for Stats { fn add_assign(&mut self, rhs: Self) { + self.initial_paths += rhs.initial_paths; self.queued_paths_per_phase += rhs.queued_paths_per_phase; self.processed_paths_per_phase += rhs.processed_paths_per_phase; self.accepted_path_length += rhs.accepted_path_length; - self.maximal_path_lengh += rhs.maximal_path_lengh; + self.nonextensible_path_lengh += rhs.nonextensible_path_lengh; self.candidates_per_node_path += rhs.candidates_per_node_path; self.candidates_per_root_path += rhs.candidates_per_root_path; self.extensions_per_node_path += rhs.extensions_per_node_path; @@ -1330,10 +1335,10 @@ impl std::ops::AddAssign for Stats { impl std::ops::AddAssign<&Self> for Stats { fn add_assign(&mut self, rhs: &Self) { - self.queued_paths_per_phase += &rhs.queued_paths_per_phase; + self.initial_paths += &rhs.initial_paths; self.processed_paths_per_phase += &rhs.processed_paths_per_phase; self.accepted_path_length += &rhs.accepted_path_length; - self.maximal_path_lengh += &rhs.maximal_path_lengh; + self.nonextensible_path_lengh += &rhs.nonextensible_path_lengh; self.candidates_per_node_path += &rhs.candidates_per_node_path; self.candidates_per_root_path += &rhs.candidates_per_root_path; self.extensions_per_node_path += &rhs.extensions_per_node_path; diff --git a/stack-graphs/tests/it/stats.rs b/stack-graphs/tests/it/stats.rs index d0ebd4757..4d83d0c1d 100644 --- a/stack-graphs/tests/it/stats.rs +++ b/stack-graphs/tests/it/stats.rs @@ -15,7 +15,7 @@ fn empty_distribution() { let hist: FrequencyDistribution = FrequencyDistribution::default(); assert_eq!(0, hist.unique()); - assert_eq!(0, hist.total()); + assert_eq!(0, hist.count()); let result = hist.quantiles(0).into_iter().cloned().collect_vec(); let expected: Vec = vec![]; @@ -28,7 +28,7 @@ fn singleton_distribution() { hist.record(42); assert_eq!(1, hist.unique()); - assert_eq!(1, hist.total()); + assert_eq!(1, hist.count()); let result = hist.quantiles(4).into_iter().cloned().collect_vec(); let expected: Vec = vec![42, 42, 42, 42, 42]; @@ -44,7 +44,7 @@ fn four_value_distribution() { hist.record(2); assert_eq!(4, hist.unique()); - assert_eq!(4, hist.total()); + assert_eq!(4, hist.count()); let result = hist.quantiles(4).into_iter().cloned().collect_vec(); let expected: Vec = vec![1, 1, 2, 3, 4]; diff --git a/tree-sitter-stack-graphs/src/cli/index.rs b/tree-sitter-stack-graphs/src/cli/index.rs index ac445b36f..8faa050d0 100644 --- a/tree-sitter-stack-graphs/src/cli/index.rs +++ b/tree-sitter-stack-graphs/src/cli/index.rs @@ -370,13 +370,14 @@ impl<'a> Indexer<'a> { .total_graph_nodes .record(graph.iter_nodes().count()); let mut total_edges = 0; - for node_edges in graph - .iter_nodes() - .filter(|n| !graph[*n].is_root()) - .map(|n| graph.outgoing_edges(n).count()) - { - self.stats.node_out_degrees.record(node_edges); - total_edges += node_edges; + for n in graph.iter_nodes() { + let edge_count = graph.outgoing_edges(n).count(); + if graph[n].is_root() { + self.stats.root_out_degree = edge_count; + } else { + self.stats.node_out_degrees.record(edge_count); + total_edges += edge_count; + } } self.stats.total_graph_edges.record(total_edges); } @@ -513,6 +514,8 @@ pub struct IndexingStats { pub total_graph_edges: FrequencyDistribution, // The distribution of the out-degrees of non-root nodes. pub node_out_degrees: FrequencyDistribution, + // The root node's out-degree. + pub root_out_degree: usize, // The stitching statistics. pub stitching_stats: StitchingStats, } diff --git a/tree-sitter-stack-graphs/src/cli/util.rs b/tree-sitter-stack-graphs/src/cli/util.rs index fd93046c4..564f13f4d 100644 --- a/tree-sitter-stack-graphs/src/cli/util.rs +++ b/tree-sitter-stack-graphs/src/cli/util.rs @@ -507,21 +507,24 @@ pub(super) fn print_indexing_stats(stats: IndexingStats) { print_quartiles_row("total graph nodes", stats.total_graph_nodes); print_quartiles_row("total graph edges", stats.total_graph_edges); print_quartiles_row("node out degrees", stats.node_out_degrees); + print_value_row("root out degree", stats.root_out_degree); println!(); print_stitching_stats(stats.stitching_stats); } pub(super) fn print_stitching_stats(stats: StitchingStats) { print_quartiles_header("stitching stats"); + print_quartiles_row("initial paths", stats.initial_paths); print_quartiles_row("queued paths per phase", stats.queued_paths_per_phase); print_quartiles_row("processed paths per phase", stats.processed_paths_per_phase); print_quartiles_row("accepted path length", stats.accepted_path_length); - print_quartiles_row("maximal path length", stats.maximal_path_lengh); + print_quartiles_row("non-extensible path length", stats.nonextensible_path_lengh); print_quartiles_row("node path candidates", stats.candidates_per_node_path); print_quartiles_row("node path extensions", stats.extensions_per_node_path); print_quartiles_row("root path candidates", stats.candidates_per_root_path); print_quartiles_row("root path extensions", stats.extensions_per_root_path); print_quartiles_row("node visits", stats.node_visits.frequencies()); + print_value_row("root visits", stats.root_visits); print_quartiles_row( "similar path counts", stats.similar_paths_stats.similar_path_count, @@ -530,33 +533,35 @@ pub(super) fn print_stitching_stats(stats: StitchingStats) { "similar path bucket sizes", stats.similar_paths_stats.similar_path_bucket_size, ); - print_value_row("root visits", stats.root_visits); } pub(super) fn print_database_stats(stats: StorageStats) { - println!(" database stats | loads | cached "); - println!("---------------------------+---------+---------"); println!( - " files | {:>9} | {:>9} ", - stats.file_loads, stats.file_cached + "| {:^29} | {:^9} | {:^9} |", + "database stats", "loads", "cached", + ); + println!("|-------------------------------|-----------|-----------|"); + println!( + "| {:>29} | {:>9} | {:>9} |", + "files", stats.file_loads, stats.file_cached ); println!( - " node paths | {:>9} | {:>9} ", - stats.node_path_loads, stats.node_path_cached + "| {:>29} | {:>9} | {:>9} |", + "node paths", stats.node_path_loads, stats.node_path_cached ); println!( - " root paths | {:>9} | {:>9} ", - stats.root_path_loads, stats.root_path_cached + "| {:>29} | {:>9} | {:>9} |", + "rootpaths", stats.root_path_loads, stats.root_path_cached ); } fn print_quartiles_header(title: &str) { println!( - "| {:^25} | min | p25 | p50 | p75 | max | total |", - title + "| {:^29} | {:^9} | {:^9} | {:^9} | {:^9} | {:^9} | {:^9} |", + title, "min", "p25", "p50", "p75", "max", "count", ); println!( - "|---------------------------|-----------|-----------|-----------|-----------|-----------|-----------|" + "|-------------------------------|-----------|-----------|-----------|-----------|-----------|-----------|" ); } @@ -564,26 +569,26 @@ fn print_quartiles_row(title: &str, hist: Frequenc let qs = hist.quantiles(4); if qs.is_empty() { println!( - "| {:>25} | {:>9} | {:>9} | {:>9} | {:>9} | {:>9} | {:>9} |", + "| {:>29} | {:>9} | {:>9} | {:>9} | {:>9} | {:>9} | {:>9} |", title, "-", "-", "-", "-", "-", 0 ); } else { println!( - "| {:>25} | {:>9} | {:>9} | {:>9} | {:>9} | {:>9} | {:>9} |", + "| {:>29} | {:>9} | {:>9} | {:>9} | {:>9} | {:>9} | {:>9} |", title, qs[0], qs[1], qs[2], qs[3], qs[4], - hist.total(), + hist.count(), ); } } fn print_value_row(title: &str, value: X) { println!( - "| {:>25} | {:>9} | {:>9} | {:>9} | {:>9} | {:>9} | {:>9} |", + "| {:>29} | {:>9} | {:>9} | {:>9} | {:>9} | {:>9} | {:>9} |", title, "-", "-", "-", "-", "-", value ); } From 25139d87306da6229de38e7fefcdd9e69e9847f2 Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Sat, 11 Nov 2023 18:58:40 +0100 Subject: [PATCH 074/291] Tweak name --- stack-graphs/src/stitching.rs | 8 ++++---- tree-sitter-stack-graphs/src/cli/util.rs | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/stack-graphs/src/stitching.rs b/stack-graphs/src/stitching.rs index 9a4cca91c..d31caef93 100644 --- a/stack-graphs/src/stitching.rs +++ b/stack-graphs/src/stitching.rs @@ -1083,7 +1083,7 @@ impl ForwardPartialPathStitcher { if extension_count == 0 { self.stats - .nonextensible_path_lengh + .terminal_path_lengh .record(partial_path.edges.len()); } candidate_count @@ -1299,7 +1299,7 @@ pub struct Stats { /// The distribution of the length of accepted paths pub accepted_path_length: FrequencyDistribution, /// The distribution of the maximal length of paths (when they cannot be extended more) - pub nonextensible_path_lengh: FrequencyDistribution, + pub terminal_path_lengh: FrequencyDistribution, /// The distribution of the number of candidates for paths ending in a regular node pub candidates_per_node_path: FrequencyDistribution, /// The distribution of the number of candidates for paths ending in the root node @@ -1322,7 +1322,7 @@ impl std::ops::AddAssign for Stats { self.queued_paths_per_phase += rhs.queued_paths_per_phase; self.processed_paths_per_phase += rhs.processed_paths_per_phase; self.accepted_path_length += rhs.accepted_path_length; - self.nonextensible_path_lengh += rhs.nonextensible_path_lengh; + self.terminal_path_lengh += rhs.terminal_path_lengh; self.candidates_per_node_path += rhs.candidates_per_node_path; self.candidates_per_root_path += rhs.candidates_per_root_path; self.extensions_per_node_path += rhs.extensions_per_node_path; @@ -1338,7 +1338,7 @@ impl std::ops::AddAssign<&Self> for Stats { self.initial_paths += &rhs.initial_paths; self.processed_paths_per_phase += &rhs.processed_paths_per_phase; self.accepted_path_length += &rhs.accepted_path_length; - self.nonextensible_path_lengh += &rhs.nonextensible_path_lengh; + self.terminal_path_lengh += &rhs.terminal_path_lengh; self.candidates_per_node_path += &rhs.candidates_per_node_path; self.candidates_per_root_path += &rhs.candidates_per_root_path; self.extensions_per_node_path += &rhs.extensions_per_node_path; diff --git a/tree-sitter-stack-graphs/src/cli/util.rs b/tree-sitter-stack-graphs/src/cli/util.rs index 564f13f4d..6b7deab4c 100644 --- a/tree-sitter-stack-graphs/src/cli/util.rs +++ b/tree-sitter-stack-graphs/src/cli/util.rs @@ -518,7 +518,7 @@ pub(super) fn print_stitching_stats(stats: StitchingStats) { print_quartiles_row("queued paths per phase", stats.queued_paths_per_phase); print_quartiles_row("processed paths per phase", stats.processed_paths_per_phase); print_quartiles_row("accepted path length", stats.accepted_path_length); - print_quartiles_row("non-extensible path length", stats.nonextensible_path_lengh); + print_quartiles_row("terminal path length", stats.terminal_path_lengh); print_quartiles_row("node path candidates", stats.candidates_per_node_path); print_quartiles_row("node path extensions", stats.extensions_per_node_path); print_quartiles_row("root path candidates", stats.candidates_per_root_path); From 8fbdfd395ed2b2c2dc2c5fee22a9cd3f1ac1db49 Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Mon, 13 Nov 2023 16:34:21 +0100 Subject: [PATCH 075/291] Only collect stats when explicitly enabled --- stack-graphs/src/stitching.rs | 88 +++++++++++++++-------- tree-sitter-stack-graphs/src/cli/index.rs | 40 ++++++----- tree-sitter-stack-graphs/src/cli/query.rs | 32 ++++++--- 3 files changed, 103 insertions(+), 57 deletions(-) diff --git a/stack-graphs/src/stitching.rs b/stack-graphs/src/stitching.rs index d31caef93..46881037d 100644 --- a/stack-graphs/src/stitching.rs +++ b/stack-graphs/src/stitching.rs @@ -829,7 +829,7 @@ pub struct ForwardPartialPathStitcher { queue: VecDeque<(PartialPath, AppendingCycleDetector, bool)>, // tracks the number of initial paths in the queue because we do not want call // extend_until on those - initial_paths: usize, + initial_paths_in_queue: usize, // next_iteration is a tuple of queues instead of an queue of tuples so that the path queue // can be cheaply exposed through the C API as a continuous memory block next_iteration: ( @@ -841,7 +841,8 @@ pub struct ForwardPartialPathStitcher { similar_path_detector: Option>, check_only_join_nodes: bool, max_work_per_phase: usize, - stats: Stats, + initial_paths: usize, + stats: Option, #[cfg(feature = "copious-debugging")] phase_number: usize, } @@ -866,13 +867,12 @@ impl ForwardPartialPathStitcher { (p, c, false) }) .multiunzip(); - let mut stats = Stats::default(); - stats.initial_paths.record(next_iteration.0.len()); + let initial_paths = next_iteration.0.len(); Self { candidates: Vec::new(), extensions: Vec::new(), queue: VecDeque::new(), - initial_paths: next_iteration.0.len(), + initial_paths_in_queue: initial_paths, next_iteration, appended_paths, // By default, all paths are checked for similarity @@ -881,7 +881,8 @@ impl ForwardPartialPathStitcher { check_only_join_nodes: false, // By default, there's no artificial bound on the amount of work done per phase max_work_per_phase: usize::MAX, - stats, + initial_paths, + stats: None, #[cfg(feature = "copious-debugging")] phase_number: 1, } @@ -916,11 +917,24 @@ impl ForwardPartialPathStitcher { self.max_work_per_phase = max_work_per_phase; } + /// Sets whether to collect statistics during stitching. + pub fn set_collect_stats(&mut self, collect_stats: bool) { + if !collect_stats { + self.stats = None; + } else if self.stats.is_none() { + let mut stats = Stats::default(); + stats.initial_paths.record(self.initial_paths); + self.stats = Some(stats); + } + } + pub fn into_stats(mut self) -> Stats { - if let Some(similar_path_detector) = self.similar_path_detector { - self.stats.similar_paths_stats = similar_path_detector.stats(); + if let (Some(stats), Some(similar_path_detector)) = + (&mut self.stats, self.similar_path_detector) + { + stats.similar_paths_stats = similar_path_detector.stats(); } - self.stats + self.stats.unwrap_or_default() } } @@ -1069,22 +1083,21 @@ impl ForwardPartialPathStitcher { self.next_iteration.2.push(new_has_split); } - let (graph, _, _) = candidates.get_graph_partials_and_db(); - let end_node = &graph[partial_path.end_node]; - if end_node.is_root() { - self.stats.candidates_per_root_path.record(candidate_count); - self.stats.extensions_per_root_path.record(extension_count); - self.stats.root_visits += 1; - } else { - self.stats.candidates_per_node_path.record(candidate_count); - self.stats.extensions_per_node_path.record(extension_count); - self.stats.node_visits.record(end_node.id()); - } - - if extension_count == 0 { - self.stats - .terminal_path_lengh - .record(partial_path.edges.len()); + if let Some(stats) = &mut self.stats { + let (graph, _, _) = candidates.get_graph_partials_and_db(); + let end_node = &graph[partial_path.end_node]; + if end_node.is_root() { + stats.candidates_per_root_path.record(candidate_count); + stats.extensions_per_root_path.record(extension_count); + stats.root_visits += 1; + } else { + stats.candidates_per_node_path.record(candidate_count); + stats.extensions_per_node_path.record(extension_count); + stats.node_visits.record(end_node.id()); + } + if extension_count == 0 { + stats.terminal_path_lengh.record(partial_path.edges.len()); + } } candidate_count } @@ -1119,7 +1132,9 @@ impl ForwardPartialPathStitcher { self.next_iteration.1.drain(..), self.next_iteration.2.drain(..), )); - self.stats.queued_paths_per_phase.record(self.queue.len()); + if let Some(stats) = &mut self.stats { + stats.queued_paths_per_phase.record(self.queue.len()); + } let mut work_performed = 0; while let Some((partial_path, cycle_detector, has_split)) = self.queue.pop_front() { let (graph, partials, _) = candidates.get_graph_partials_and_db(); @@ -1127,8 +1142,8 @@ impl ForwardPartialPathStitcher { "--> Candidate partial path {}", partial_path.display(graph, partials) ); - if self.initial_paths > 0 { - self.initial_paths -= 1; + if self.initial_paths_in_queue > 0 { + self.initial_paths_in_queue -= 1; } else if !extend_while(graph, partials, &partial_path) { copious_debugging!( " Do not extend {}", @@ -1141,7 +1156,9 @@ impl ForwardPartialPathStitcher { break; } } - self.stats.processed_paths_per_phase.record(work_performed); + if let Some(stats) = &mut self.stats { + stats.processed_paths_per_phase.record(work_performed); + } #[cfg(feature = "copious-debugging")] { @@ -1354,6 +1371,8 @@ impl std::ops::AddAssign<&Self> for Stats { pub struct StitcherConfig { /// Enables similar path detection during path stitching. detect_similar_paths: bool, + /// Collect statistics about path stitching. + collect_stats: bool, } impl StitcherConfig { @@ -1365,11 +1384,21 @@ impl StitcherConfig { self.detect_similar_paths = detect_similar_paths; self } + + pub fn collect_stats(&self) -> bool { + self.collect_stats + } + + pub fn with_collect_stats(mut self, collect_stats: bool) -> Self { + self.collect_stats = collect_stats; + self + } } impl StitcherConfig { fn apply(&self, stitcher: &mut ForwardPartialPathStitcher) { stitcher.set_similar_path_detection(self.detect_similar_paths); + stitcher.set_collect_stats(self.collect_stats); } } @@ -1377,6 +1406,7 @@ impl Default for StitcherConfig { fn default() -> Self { Self { detect_similar_paths: true, + collect_stats: false, } } } diff --git a/tree-sitter-stack-graphs/src/cli/index.rs b/tree-sitter-stack-graphs/src/cli/index.rs index 8faa050d0..f47e03cf0 100644 --- a/tree-sitter-stack-graphs/src/cli/index.rs +++ b/tree-sitter-stack-graphs/src/cli/index.rs @@ -113,6 +113,7 @@ impl IndexArgs { let mut indexer = Indexer::new(&mut db, &mut loader, &reporter); indexer.force = self.force; indexer.max_file_time = self.max_file_time; + indexer.set_collect_stats(self.stats); let source_paths = self .source_paths @@ -158,7 +159,7 @@ pub struct Indexer<'a> { db: &'a mut SQLiteWriter, loader: &'a mut Loader, reporter: &'a dyn Reporter, - stats: IndexingStats, + stats: Option, /// Index files, even if they already exist in the database. pub force: bool, /// Maximum time per file. @@ -177,7 +178,15 @@ impl<'a> Indexer<'a> { reporter, force: false, max_file_time: None, - stats: IndexingStats::default(), + stats: None, + } + } + + pub fn set_collect_stats(&mut self, collect_stats: bool) { + if !collect_stats { + self.stats = None; + } else if self.stats.is_none() { + self.stats = Some(IndexingStats::default()); } } @@ -294,8 +303,9 @@ impl<'a> Indexer<'a> { } Err(e) => return Err(IndexError::LoadError(e)), }; - let stitcher_config = - StitcherConfig::default().with_detect_similar_paths(!lcs.no_similar_paths_in_file()); + let stitcher_config = StitcherConfig::default() + .with_detect_similar_paths(!lcs.no_similar_paths_in_file()) + .with_collect_stats(self.stats.is_some()); let source = file_reader.get(source_path)?; let tag = sha1(source); @@ -365,21 +375,19 @@ impl<'a> Indexer<'a> { } } }; - { - self.stats - .total_graph_nodes - .record(graph.iter_nodes().count()); + if let Some(stats) = &mut self.stats { + stats.total_graph_nodes.record(graph.iter_nodes().count()); let mut total_edges = 0; for n in graph.iter_nodes() { let edge_count = graph.outgoing_edges(n).count(); if graph[n].is_root() { - self.stats.root_out_degree = edge_count; + stats.root_out_degree = edge_count; } else { - self.stats.node_out_degrees.record(edge_count); + stats.node_out_degrees.record(edge_count); total_edges += edge_count; } } - self.stats.total_graph_edges.record(total_edges); + stats.total_graph_edges.record(total_edges); } let mut partials = PartialPaths::new(); @@ -395,7 +403,9 @@ impl<'a> Indexer<'a> { }, ) { Ok(stitching_stats) => { - self.stats.stitching_stats += stitching_stats; + if let Some(stats) = &mut self.stats { + stats.stitching_stats += stitching_stats; + } } Err(_) => { file_status.warning("path computation timed out", None); @@ -475,12 +485,8 @@ impl<'a> Indexer<'a> { false } - pub fn stats(&self) -> &IndexingStats { - &self.stats - } - pub fn into_stats(self) -> IndexingStats { - self.stats + self.stats.unwrap_or_default() } } diff --git a/tree-sitter-stack-graphs/src/cli/query.rs b/tree-sitter-stack-graphs/src/cli/query.rs index 1c57145fe..dd5526570 100644 --- a/tree-sitter-stack-graphs/src/cli/query.rs +++ b/tree-sitter-stack-graphs/src/cli/query.rs @@ -51,7 +51,7 @@ impl QueryArgs { wait_for_input()?; } let mut db = SQLiteReader::open(&db_path)?; - let stitching_stats = self.target.run(&mut db)?; + let stitching_stats = self.target.run(&mut db, self.stats)?; if self.stats { println!(); print_stitching_stats(stitching_stats); @@ -68,9 +68,10 @@ pub enum Target { } impl Target { - pub fn run(self, db: &mut SQLiteReader) -> anyhow::Result { + fn run(self, db: &mut SQLiteReader, collect_stats: bool) -> anyhow::Result { let reporter = ConsoleReporter::details(); let mut querier = Querier::new(db, &reporter); + querier.set_collect_stats(collect_stats); match self { Self::Definition(cmd) => cmd.run(&mut querier)?, } @@ -152,7 +153,7 @@ impl Definition { pub struct Querier<'a> { db: &'a mut SQLiteReader, reporter: &'a dyn Reporter, - stats: StitchingStats, + stats: Option, } impl<'a> Querier<'a> { @@ -160,7 +161,15 @@ impl<'a> Querier<'a> { Self { db, reporter, - stats: StitchingStats::default(), + stats: None, + } + } + + pub fn set_collect_stats(&mut self, collect_stats: bool) { + if !collect_stats { + self.stats = None; + } else if self.stats.is_none() { + self.stats = Some(StitchingStats::default()); } } @@ -208,7 +217,8 @@ impl<'a> Querier<'a> { let mut reference_paths = Vec::new(); let stitcher_config = StitcherConfig::default() // always detect similar paths, we don't know the language configurations for the data in the database - .with_detect_similar_paths(true); + .with_detect_similar_paths(true) + .with_collect_stats(self.stats.is_some()); let ref_result = ForwardPartialPathStitcher::find_all_complete_partial_paths( self.db, std::iter::once(node), @@ -219,7 +229,11 @@ impl<'a> Querier<'a> { }, ); match ref_result { - Ok(ref_stats) => self.stats += ref_stats, + Ok(ref_stats) => { + if let Some(stats) = &mut self.stats { + *stats += ref_stats + } + } Err(err) => { self.reporter.failed(&log_path, "query timed out", None); return Err(err.into()); @@ -276,12 +290,8 @@ impl<'a> Querier<'a> { Ok(result) } - pub fn stats(&self) -> &StitchingStats { - &self.stats - } - pub fn into_stats(self) -> StitchingStats { - self.stats + self.stats.unwrap_or_default() } } From 04626dc5687bf3639f3c13143e97e708453e8f4b Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Mon, 20 Nov 2023 14:16:13 +0100 Subject: [PATCH 076/291] Split similar paths and counts to reduce memory usage when stats are not collected --- stack-graphs/src/cycles.rs | 61 ++++++++++++++++++++++------------- stack-graphs/src/stitching.rs | 9 ++++-- 2 files changed, 46 insertions(+), 24 deletions(-) diff --git a/stack-graphs/src/cycles.rs b/stack-graphs/src/cycles.rs index e989dc471..0f00ad16f 100644 --- a/stack-graphs/src/cycles.rs +++ b/stack-graphs/src/cycles.rs @@ -50,12 +50,8 @@ use crate::stitching::ToAppendable; /// Helps detect similar paths in the path-finding algorithm. pub struct SimilarPathDetector

{ - paths: HashMap; 4]>>, -} - -struct SimilarPath

{ - path: P, - count: usize, + paths: HashMap>, + counts: Option>>, } #[doc(hidden)] @@ -98,13 +94,23 @@ where pub fn new() -> SimilarPathDetector

{ SimilarPathDetector { paths: HashMap::new(), + counts: None, + } + } + + /// Set whether to collect statistics for this similar path detector. + pub fn set_collect_stats(&mut self, collect_stats: bool) { + if !collect_stats { + self.counts = None; + } else if self.counts.is_none() { + self.counts = Some(HashMap::new()); } } - /// Determines whether we should process this path during the path-finding algorithm. If we have seen - /// a path with the same start and end node, and the same pre- and postcondition, then we return false. - /// Otherwise, we return true. - pub fn has_similar_path( + /// Add a apth, and determine whether we should process this path during the path-finding algorithm. + /// If we have seen a path with the same start and end node, and the same pre- and postcondition, then + /// we return false. Otherwise, we return true. + pub fn add_path( &mut self, _graph: &StackGraph, arena: &mut P::Arena, @@ -119,22 +125,31 @@ where // Iterate through the bucket to determine if this paths is better than any already known // path. Note that the bucket might be modified during the loop if a path is removed which // is shadowed by the new path! - let possibly_similar_paths = self.paths.entry(key).or_default(); + let possibly_similar_paths = self.paths.entry(key.clone()).or_default(); + let mut possible_similar_counts = self + .counts + .as_mut() + .map(move |cs| cs.entry(key).or_default()); let mut idx = 0; let mut count = 0; while idx < possibly_similar_paths.len() { let other_path = &mut possibly_similar_paths[idx]; - match cmp(arena, path, &other_path.path) { + match cmp(arena, path, other_path) { Some(Ordering::Less) => { // the new path is better, remove the old one - count += other_path.count; possibly_similar_paths.remove(idx); + if let Some(possible_similar_counts) = possible_similar_counts.as_mut() { + count += possible_similar_counts[idx]; + possible_similar_counts.remove(idx); + } // keep `idx` which now points to the next element continue; } Some(_) => { // the new path is equal or worse, and ignored - other_path.count += 1; + if let Some(possible_similar_counts) = possible_similar_counts { + possible_similar_counts[idx] += 1; + } return true; } None => { @@ -144,10 +159,10 @@ where } // this path is either new or better, keep it - possibly_similar_paths.push(SimilarPath { - path: path.clone(), - count, - }); + possibly_similar_paths.push(path.clone()); + if let Some(possible_similar_counts) = possible_similar_counts { + possible_similar_counts.push(count); + } false } @@ -159,10 +174,12 @@ where // Returns the distribution of similar path counts. pub fn stats(&self) -> SimilarPathStats { let mut stats = SimilarPathStats::default(); - for bucket in self.paths.values() { - stats.similar_path_bucket_size.record(bucket.len()); - for path in bucket.iter() { - stats.similar_path_count.record(path.count); + if let Some(counts) = &self.counts { + for bucket in counts.values() { + stats.similar_path_bucket_size.record(bucket.len()); + for count in bucket.iter() { + stats.similar_path_count.record(*count); + } } } stats diff --git a/stack-graphs/src/stitching.rs b/stack-graphs/src/stitching.rs index 46881037d..99aaa8df1 100644 --- a/stack-graphs/src/stitching.rs +++ b/stack-graphs/src/stitching.rs @@ -896,7 +896,9 @@ impl ForwardPartialPathStitcher { if !detect_similar_paths { self.similar_path_detector = None; } else if self.similar_path_detector.is_none() { - self.similar_path_detector = Some(SimilarPathDetector::new()); + let mut similar_path_detector = SimilarPathDetector::new(); + similar_path_detector.set_collect_stats(self.stats.is_some()); + self.similar_path_detector = Some(similar_path_detector); } } @@ -926,6 +928,9 @@ impl ForwardPartialPathStitcher { stats.initial_paths.record(self.initial_paths); self.stats = Some(stats); } + if let Some(similar_path_detector) = &mut self.similar_path_detector { + similar_path_detector.set_collect_stats(collect_stats); + } } pub fn into_stats(mut self) -> Stats { @@ -1050,7 +1055,7 @@ impl ForwardPartialPathStitcher { let (graph, partials, _) = candidates.get_graph_partials_and_db(); if check_similar_path { if let Some(similar_path_detector) = &mut self.similar_path_detector { - if similar_path_detector.has_similar_path( + if similar_path_detector.add_path( graph, partials, &new_partial_path, From 63bf3a5b78645c5454f5f0ecd7a4c355f82b823a Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Tue, 21 Nov 2023 15:37:28 +0100 Subject: [PATCH 077/291] Remove unnecessary dependency --- languages/tree-sitter-stack-graphs-java/Cargo.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/languages/tree-sitter-stack-graphs-java/Cargo.toml b/languages/tree-sitter-stack-graphs-java/Cargo.toml index 72646a905..8bd3e5f93 100644 --- a/languages/tree-sitter-stack-graphs-java/Cargo.toml +++ b/languages/tree-sitter-stack-graphs-java/Cargo.toml @@ -36,6 +36,5 @@ harness = false # need to provide own main function to handle running tests [dependencies] anyhow = "1.0" clap = { version = "4", features = ["derive"] } -stack-graphs = { version = "0.12", path = "../../stack-graphs" } tree-sitter-stack-graphs = { version = "0.7", path = "../../tree-sitter-stack-graphs", features=["cli"] } tree-sitter-java = { version = "=0.20.0" } From 1b01a8c4c2c8a19010370207f3277ed8c469d50d Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Tue, 21 Nov 2023 15:46:07 +0100 Subject: [PATCH 078/291] spelling --- stack-graphs/src/cycles.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stack-graphs/src/cycles.rs b/stack-graphs/src/cycles.rs index 0f00ad16f..212d14085 100644 --- a/stack-graphs/src/cycles.rs +++ b/stack-graphs/src/cycles.rs @@ -107,7 +107,7 @@ where } } - /// Add a apth, and determine whether we should process this path during the path-finding algorithm. + /// Add a path, and determine whether we should process this path during the path-finding algorithm. /// If we have seen a path with the same start and end node, and the same pre- and postcondition, then /// we return false. Otherwise, we return true. pub fn add_path( From 163ff76c6de412c025c9cc784667cc30419a0c35 Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Mon, 20 Nov 2023 18:02:12 +0100 Subject: [PATCH 079/291] Support `satisfies` expression --- .../src/stack-graphs.tsg | 16 ++++++++++++++++ .../test/expressions/satisfies-type.ts | 8 ++++++++ 2 files changed, 24 insertions(+) create mode 100644 languages/tree-sitter-stack-graphs-typescript/test/expressions/satisfies-type.ts diff --git a/languages/tree-sitter-stack-graphs-typescript/src/stack-graphs.tsg b/languages/tree-sitter-stack-graphs-typescript/src/stack-graphs.tsg index 2d05f5891..f96c28122 100644 --- a/languages/tree-sitter-stack-graphs-typescript/src/stack-graphs.tsg +++ b/languages/tree-sitter-stack-graphs-typescript/src/stack-graphs.tsg @@ -2649,6 +2649,7 @@ if none @is_async { (object) (parenthesized_expression) (regex) + (satisfies_expression) (sequence_expression) (spread_element) (string) @@ -2961,6 +2962,21 @@ if none @is_def { +;; Satisfies Expressions + +; { foo: 42 } satisfies { foo: number } + +(satisfies_expression (_)@expr . (_)@type)@satisfies { + ; propagate lexical scope + edge @expr.lexical_scope -> @satisfies.lexical_scope + edge @type.lexical_scope -> @satisfies.lexical_scope + + ; type is union of both + edge @satisfies.type -> @expr.type + edge @satisfies.type -> @type.type +} + + ;; Spread Element (spread_element (_)@expr)@spread_elem { diff --git a/languages/tree-sitter-stack-graphs-typescript/test/expressions/satisfies-type.ts b/languages/tree-sitter-stack-graphs-typescript/test/expressions/satisfies-type.ts new file mode 100644 index 000000000..48b3a86f5 --- /dev/null +++ b/languages/tree-sitter-stack-graphs-typescript/test/expressions/satisfies-type.ts @@ -0,0 +1,8 @@ +type Foo = { foo: number }; + +let x = { foo: 42 } satisfies Foo; + + x.foo +// ^ defined: 3, 1 + +export {} From 282d241780873dedbadb346d772edc2ee418e2cb Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Mon, 20 Nov 2023 19:01:45 +0100 Subject: [PATCH 080/291] Fix bug in reexport as module --- .../src/stack-graphs.tsg | 8 ++++---- .../test/modules/reexport-as-object.ts | 15 +++++++++++++++ 2 files changed, 19 insertions(+), 4 deletions(-) create mode 100644 languages/tree-sitter-stack-graphs-typescript/test/modules/reexport-as-object.ts diff --git a/languages/tree-sitter-stack-graphs-typescript/src/stack-graphs.tsg b/languages/tree-sitter-stack-graphs-typescript/src/stack-graphs.tsg index f96c28122..af6b80071 100644 --- a/languages/tree-sitter-stack-graphs-typescript/src/stack-graphs.tsg +++ b/languages/tree-sitter-stack-graphs-typescript/src/stack-graphs.tsg @@ -923,8 +923,8 @@ if none @is_default { ; ) (export_statement - (namespace_export (identifier)) - source:(_)@name + (namespace_export (identifier)@name) + source:(_)@from )@export_stmt { ; namespace definitions are specified together with (module) and (internal_module) @@ -933,8 +933,8 @@ if none @is_default { edge @export_stmt.exports -> @name.type_def__ns ; connect definitions to exports - edge @export_stmt.expr_member__ns -> @name.mod_ref - edge @export_stmt.type_member__ns -> @name.mod_ref + edge @export_stmt.expr_member__ns -> @from.mod_ref + edge @export_stmt.type_member__ns -> @from.mod_ref } ; export as namespace NAME diff --git a/languages/tree-sitter-stack-graphs-typescript/test/modules/reexport-as-object.ts b/languages/tree-sitter-stack-graphs-typescript/test/modules/reexport-as-object.ts new file mode 100644 index 000000000..54e5494b9 --- /dev/null +++ b/languages/tree-sitter-stack-graphs-typescript/test/modules/reexport-as-object.ts @@ -0,0 +1,15 @@ +/* --- path: foo.ts --- */ + +export const FOO = 42; + +/* --- path: bar.ts --- */ + +export * as quz from "./foo"; + +/* --- path: test.ts --- */ + +import { quz } from "./bar"; + + quz.FOO +// ^ defined: 11, 7 +// ^ defined: 3 From 9323b4c9325f57999a36314e87bad47a00b48c0c Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Wed, 22 Nov 2023 11:35:29 +0100 Subject: [PATCH 081/291] Eliminate nested identifier unrolling --- .../src/stack-graphs.tsg | 358 +++++++++--------- .../test/modules/very-deep-namespace.ts | 6 + 2 files changed, 175 insertions(+), 189 deletions(-) create mode 100644 languages/tree-sitter-stack-graphs-typescript/test/modules/very-deep-namespace.ts diff --git a/languages/tree-sitter-stack-graphs-typescript/src/stack-graphs.tsg b/languages/tree-sitter-stack-graphs-typescript/src/stack-graphs.tsg index af6b80071..965047812 100644 --- a/languages/tree-sitter-stack-graphs-typescript/src/stack-graphs.tsg +++ b/languages/tree-sitter-stack-graphs-typescript/src/stack-graphs.tsg @@ -928,13 +928,33 @@ if none @is_default { )@export_stmt { ; namespace definitions are specified together with (module) and (internal_module) - ; export definitions - edge @export_stmt.exports -> @name.expr_def__ns - edge @export_stmt.exports -> @name.type_def__ns + ; export expression definition + node expr_ns_pop + attr (expr_ns_pop) pop_symbol = "%E" + ; + edge @export_stmt.exports -> expr_ns_pop + edge expr_ns_pop -> @name.expr_def + + ; export type definition + node type_ns_pop + attr (type_ns_pop) pop_symbol = "%T" + ; + edge @export_stmt.exports -> type_ns_pop + edge type_ns_pop -> @name.type_def - ; connect definitions to exports - edge @export_stmt.expr_member__ns -> @from.mod_ref - edge @export_stmt.type_member__ns -> @from.mod_ref + ; connect expression definition to exports + node expr_ns_push + attr (expr_ns_push) push_symbol = "%E" + ; + edge @name.expr_def_member -> expr_ns_push + edge expr_ns_push -> @from.mod_ref + + ; connect type definition to exports + node type_ns_push + attr (type_ns_push) push_symbol = "%T" + ; + edge @name.type_def_member -> type_ns_push + edge type_ns_push -> @from.mod_ref } ; export as namespace NAME @@ -947,16 +967,36 @@ if none @is_default { (program (export_statement "as" "namespace" . (_)@name -)@export_stmt)@prog { +))@prog { ; namespace definitions are specified together with (module) and (internal_module) - ; make definitions global - edge @prog.globals -> @name.expr_def__ns - edge @prog.globals -> @name.type_def__ns + ; make expression definition global + node expr_ns_pop + attr (expr_ns_pop) pop_symbol = "%E" + ; + edge @prog.globals -> expr_ns_pop + edge expr_ns_pop -> @name.expr_def - ; connect definitions to exports - edge @export_stmt.expr_member__ns -> @prog.exports - edge @export_stmt.type_member__ns -> @prog.exports + ; make type definition global + node type_ns_pop + attr (type_ns_pop) pop_symbol = "%T" + ; + edge @prog.globals -> type_ns_pop + edge type_ns_pop -> @name.type_def + + ; connect expression definition to exports + node expr_ns_push + attr (expr_ns_push) push_symbol = "%E" + ; + edge @name.expr_def_member -> expr_ns_push + edge expr_ns_push -> @prog.exports + + ; connect type definition to exports + node type_ns_push + attr (type_ns_push) push_symbol = "%T" + ; + edge @name.type_def_member -> type_ns_push + edge type_ns_push -> @prog.exports } ;; Imports @@ -1085,14 +1125,38 @@ if none @is_type { (import_statement "type"? - (import_clause (namespace_import (identifier))) + (import_clause (namespace_import (identifier)@name)) source:(_)@from )@import_stmt { ; namespace definitions are specified together with (module) and (internal_module) - ; connect definitions to import - edge @import_stmt.expr_member__ns -> @from.mod_ref - edge @import_stmt.type_member__ns -> @from.mod_ref + ; expose expression definition + node expr_ns_pop + attr (expr_ns_pop) pop_symbol = "%E" + ; + edge @import_stmt.lexical_defs -> expr_ns_pop + edge expr_ns_pop -> @name.expr_def + + ; expose type definition + node type_ns_pop + attr (type_ns_pop) pop_symbol = "%T" + ; + edge @import_stmt.lexical_defs -> type_ns_pop + edge type_ns_pop -> @name.type_def + + ; connect expression definition to import + node expr_ns_push + attr (expr_ns_push) push_symbol = "%E" + ; + edge @name.expr_def_member -> expr_ns_push + edge expr_ns_push -> @from.mod_ref + + ; connect type definition to import + node type_ns_push + attr (type_ns_push) push_symbol = "%T" + ; + edge @name.type_def_member -> type_ns_push + edge type_ns_push -> @from.mod_ref } ;; Import Alias @@ -2262,147 +2326,89 @@ if none @is_async { [ ; X - (module name:(identifier)@name @mod) - (internal_module name:(identifier)@name @mod) - (export_statement "as" "namespace" . (identifier)@name) @mod - (export_statement (namespace_export (identifier)@name))@mod - (import_statement (import_clause (namespace_import (identifier)@name)))@mod - (ambient_declaration (module name:(string)@name @mod)) + (module name:(identifier)@name) + (internal_module name:(identifier)@name) + (export_statement "as" "namespace" . (identifier)@name) + (export_statement (namespace_export (identifier)@name)) + (import_statement (import_clause (namespace_import (identifier)@name))) + (ambient_declaration (module name:(string) @name)) ; X._ - (module name:(nested_identifier . (identifier)@name @mod)) - (internal_module name:(nested_identifier . (identifier)@name @mod)) - ; X._._ - (module name:(nested_identifier . (nested_identifier . (identifier)@name @mod))) - (internal_module name:(nested_identifier . (nested_identifier . (identifier)@name @mod))) - ; X._._._ - (module name:(nested_identifier . (nested_identifier . (nested_identifier . (identifier)@name @mod)))) - (internal_module name:(nested_identifier . (nested_identifier . (nested_identifier . (identifier)@name @mod)))) - ; X._._._._ - (module name:(nested_identifier . (nested_identifier . (nested_identifier . (nested_identifier . (identifier)@name @mod))))) - (internal_module name:(nested_identifier . (nested_identifier . (nested_identifier . (nested_identifier . (identifier)@name @mod))))) -]@mod_decl { + (nested_identifier (identifier)@name) +] { node @name.expr_def - node @name.expr_def__ns - node @name.expr_def__typeof - node @mod.expr_member - node @mod.expr_member__ns + node expr_def_typeof + node @name.expr_def_member node @name.type_def - node @name.type_def__ns - node @mod.type_member - node @mod.type_member__ns - - ; expose expression definition - edge @mod_decl.lexical_defs -> @name.expr_def__ns + node @name.type_def_member ; expression definition - attr (@name.expr_def__ns) pop_symbol = "%E" - edge @name.expr_def__ns -> @name.expr_def - ; attr (@name.expr_def) node_definition = @name, syntax_type = "module" - - ; namespaces mimic objects with members to access exported expressions - edge @name.expr_def -> @name.expr_def__typeof + attr (expr_def_typeof) pop_symbol = ":" + attr (@name.expr_def_member) pop_symbol = "." ; - attr (@name.expr_def__typeof) pop_symbol = ":" - edge @name.expr_def__typeof -> @mod.expr_member - ; - attr (@mod.expr_member) pop_symbol = "." - edge @mod.expr_member -> @mod.expr_member__ns - ; - attr (@mod.expr_member__ns) push_symbol = "%E" - - ; expose type definition - edge @mod_decl.lexical_defs -> @name.type_def__ns + edge @name.expr_def -> expr_def_typeof + edge expr_def_typeof -> @name.expr_def_member ; type definition - attr (@name.type_def__ns) pop_symbol = "%T" - edge @name.type_def__ns -> @name.type_def - ; attr (@name.type_def) node_definition = @name, syntax_type = "module" - - ; namespaces mimic types with member types to access exported expressions - edge @name.type_def -> @mod.type_member - ; - attr (@mod.type_member) pop_symbol = "." - edge @mod.type_member -> @mod.type_member__ns + attr (@name.type_def_member) pop_symbol = "." ; - attr (@mod.type_member__ns) push_symbol = "%T" + edge @name.type_def -> @name.type_def_member } -[ - ; _.X - (module name:(nested_identifier . (_)@basemod . (identifier)@name)@mod) - (internal_module name:(nested_identifier . (_)@basemod . (identifier)@name)@mod) - ; _._.X - (module name:(nested_identifier . (nested_identifier . (_)@basemod . (identifier)@name)@mod)) - (internal_module name:(nested_identifier . (nested_identifier . (_)@basemod . (identifier)@name)@mod)) - ; _._._.X - (module name:(nested_identifier . (nested_identifier . (nested_identifier . (_)@basemod . (identifier)@name)@mod))) - (internal_module name:(nested_identifier . (nested_identifier . (nested_identifier . (_)@basemod . (identifier)@name)@mod))) - ; _._._._.X - (module name:(nested_identifier . (nested_identifier . (nested_identifier . (nested_identifier . (_)@basemod . (identifier)@name)@mod)))) - (internal_module name:(nested_identifier . (nested_identifier . (nested_identifier . (nested_identifier . (_)@basemod . (identifier)@name)@mod)))) -] { - node @name.expr_def - node @name.expr_def__ns - node @name.expr_def__typeof - node @mod.expr_member - node @mod.expr_member__ns - node @name.type_def - node @name.type_def__ns - node @mod.type_member - node @mod.type_member__ns - - ; expose expression definition - edge @basemod.expr_member__ns -> @name.expr_def__ns - - ; expression definition - attr (@name.expr_def__ns) pop_symbol = "%E" - edge @name.expr_def__ns -> @name.expr_def - ; - attr (@name.expr_def) node_definition = @name, syntax_type = "module" +(nested_identifier . (_) @mod) @nested { + node @nested.expr_def + node @nested.type_def - ; namespaces mimic objects with members to access exported expressions - edge @name.expr_def -> @name.expr_def__typeof - ; - attr (@name.expr_def__typeof) pop_symbol = ":" - edge @name.expr_def__typeof -> @mod.expr_member - ; - attr (@mod.expr_member) pop_symbol = "." - edge @mod.expr_member -> @mod.expr_member__ns - ; - attr (@mod.expr_member__ns) push_symbol = "%E" + edge @nested.expr_def -> @mod.expr_def + edge @nested.type_def -> @mod.type_def +} - ; expose type definition - edge @basemod.type_member__ns -> @name.type_def__ns +(nested_identifier . (_) @mod . (_) @name .) { + edge @mod.expr_def_member -> @name.expr_def + edge @mod.type_def_member -> @name.type_def +} - ; type definition - attr (@name.type_def__ns) pop_symbol = "%T" - edge @name.type_def__ns -> @name.type_def - ; - attr (@name.type_def) node_definition = @name, syntax_type = "module" +(nested_identifier (_) @name .) @nested { + node @nested.expr_def_member + node @nested.type_def_member - ; namespaces mimic types with member types to access exported expressions - edge @name.type_def -> @mod.type_member - ; - attr (@mod.type_member) pop_symbol = "." - edge @mod.type_member -> @mod.type_member__ns - ; - attr (@mod.type_member__ns) push_symbol = "%T" + edge @name.expr_def_member -> @nested.expr_def_member + edge @name.type_def_member -> @nested.type_def_member } [ - (module name:(_)@mod body:(_)@body) - (internal_module name:(_)@mod body:(_)@body) + (module name:(_)@mod body:(_)@body) + (internal_module name:(_)@mod body:(_)@body) ]@mod_decl { ; propagate lexical scope edge @body.lexical_scope -> @mod_decl.lexical_scope - ; connect object to exports - edge @mod.expr_member__ns -> @body.exports + ; expose expression definition + node expr_ns_pop + attr (expr_ns_pop) pop_symbol = "%E" + ; + edge @mod_decl.lexical_defs -> expr_ns_pop + edge expr_ns_pop -> @mod.expr_def + + ; expose type definition + node type_ns_pop + attr (type_ns_pop) pop_symbol = "%T" + ; + edge @mod_decl.lexical_defs -> type_ns_pop + edge type_ns_pop -> @mod.type_def + + ; connect expression definition to exports + node expr_ns_push + attr (expr_ns_push) push_symbol = "%E" + edge @mod.expr_def_member -> expr_ns_push + edge expr_ns_push -> @body.exports - ; connect type to exports - edge @mod.type_member__ns -> @body.exports + ; connect type definition to exports + node type_ns_push + attr (type_ns_push) push_symbol = "%T" + edge @mod.type_def_member -> type_ns_push + edge type_ns_push -> @body.exports } ; NOTE internal_module is also an expression in the grammar, not only a statement. @@ -5541,70 +5547,44 @@ if none @is_acc { [ ; X - (nested_type_identifier module:(identifier)@name @mod) - ; X._ - (nested_type_identifier module:(nested_identifier . (identifier)@name @mod)) - ; X._._ - (nested_type_identifier module:(nested_identifier . (nested_identifier . (identifier)@name @mod))) - ; X._._._ - (nested_type_identifier module:(nested_identifier . (nested_identifier . (nested_identifier . (identifier)@name @mod)))) - ; X._._._._ - (nested_type_identifier module:(nested_identifier . (nested_identifier . (nested_identifier . (nested_identifier . (identifier)@name @mod))))) -]@nested_type_id { + (nested_type_identifier module:(identifier)@name) + ; X._, _.X._ + (nested_identifier (identifier)@name) +] { node @name.type_ref - node @name.type_ref__ns - node @mod.type - - ; type reference to namespace name - edge @mod.type -> @name.type_ref - ; attr (@name.type_ref) node_reference = @name - edge @name.type_ref -> @name.type_ref__ns - ; - attr (@name.type_ref__ns) push_symbol = "%T" - edge @name.type_ref__ns -> @nested_type_id.lexical_scope + + node @name.type_ref_member + attr (@name.type_ref_member) push_symbol = "." + + edge @name.type_ref_member -> @name.type_ref } -[ - ; _.X - (nested_type_identifier module:(nested_identifier . (_)@basemod . (identifier)@name .)@mod) - ; _._.X - (nested_type_identifier module:(nested_identifier . (nested_identifier . (_)@basemod . (identifier)@name .)@mod)) - ; _._._.X - (nested_type_identifier module:(nested_identifier . (nested_identifier . (nested_identifier . (_)@basemod . (identifier)@name .)@mod))) - ; _._._._.X - (nested_type_identifier module:(nested_identifier . (nested_identifier . (nested_identifier . (nested_identifier . (_)@basemod . (identifier)@name .)@mod)))) -] { - node @name.type_ref - node @name.type_ref__ns - node @mod.member - node @mod.type +(nested_identifier . (_)@mod) @nested { + node @nested.type_ref + edge @mod.type_ref -> @nested.type_ref +} - ; type reference to namespace name - edge @mod.type -> @name.type_ref - ; - attr (@name.type_ref) node_reference = @name - edge @name.type_ref -> @mod.member - ; - attr (@mod.member) push_symbol = "." - edge @mod.member -> @basemod.type +(nested_identifier . (_)@mod . (_)@name .) { + edge @name.type_ref -> @mod.type_ref_member } -(nested_type_identifier - module:(_)@mod - name:(_)@name -)@nested_type_id { - node @name.nested_type_ref ; FIXME non-standard name because the general (type_identifier) rule is also applied to @name - node @nested_type_id.member +(nested_identifier (_)@name .) @nested { + node @nested.type_ref_member + edge @nested.type_ref_member -> @name.type_ref_member +} - ; type reference into namespace type - edge @nested_type_id.type -> @name.nested_type_ref - ; - attr (@name.nested_type_ref) node_reference = @name - edge @name.nested_type_ref -> @nested_type_id.member - ; - attr (@nested_type_id.member) push_symbol = "." - edge @nested_type_id.member -> @mod.type +(nested_type_identifier module:(_)@mod)@nested_type_id { + node type_ns + attr (type_ns) push_symbol = "%T" + + edge @mod.type_ref -> type_ns + edge type_ns -> @nested_type_id.lexical_scope +} + +(nested_type_identifier module:(_)@mod name:(_)@name)@nested_type_id { + edge @nested_type_id.type -> @name.type_ref + edge @name.type_ref -> @mod.type_ref_member } diff --git a/languages/tree-sitter-stack-graphs-typescript/test/modules/very-deep-namespace.ts b/languages/tree-sitter-stack-graphs-typescript/test/modules/very-deep-namespace.ts new file mode 100644 index 000000000..71af87eee --- /dev/null +++ b/languages/tree-sitter-stack-graphs-typescript/test/modules/very-deep-namespace.ts @@ -0,0 +1,6 @@ +namespace a.b.c.d.e.f.g.h.i.j.k.l.m.n.o.p.q.r.s.t.u.v.w.x.y { + export const Z = 42; +} + +a.b.c.d.e.f.g.h.i.j.k.l.m.n.o.p.q.r.s.t.u.v.w.x.y.Z +// ^ defined: 2 From 1af38d2cf7a3c13f75154557948afd01138fbd5a Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Wed, 22 Nov 2023 17:33:40 +0100 Subject: [PATCH 082/291] Fix missing scoped variable on undefined as pattern --- .../tree-sitter-stack-graphs-typescript/src/stack-graphs.tsg | 1 + .../test/expressions/undefined-pattern.ts | 5 +++++ 2 files changed, 6 insertions(+) create mode 100644 languages/tree-sitter-stack-graphs-typescript/test/expressions/undefined-pattern.ts diff --git a/languages/tree-sitter-stack-graphs-typescript/src/stack-graphs.tsg b/languages/tree-sitter-stack-graphs-typescript/src/stack-graphs.tsg index 965047812..01808435e 100644 --- a/languages/tree-sitter-stack-graphs-typescript/src/stack-graphs.tsg +++ b/languages/tree-sitter-stack-graphs-typescript/src/stack-graphs.tsg @@ -4118,6 +4118,7 @@ if none @is_async { (this) (member_expression) (subscript_expression) + (undefined) ]@pat { node @pat.defs } diff --git a/languages/tree-sitter-stack-graphs-typescript/test/expressions/undefined-pattern.ts b/languages/tree-sitter-stack-graphs-typescript/test/expressions/undefined-pattern.ts new file mode 100644 index 000000000..652adf3c6 --- /dev/null +++ b/languages/tree-sitter-stack-graphs-typescript/test/expressions/undefined-pattern.ts @@ -0,0 +1,5 @@ +interface Foo { + bar(undefined?: any): any; +} + +export {} \ No newline at end of file From c17bcd26bfd14127d231bc776fe488e9ddb60aa9 Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Wed, 22 Nov 2023 17:47:24 +0100 Subject: [PATCH 083/291] Fix rest array pattern crash --- .../src/stack-graphs.tsg | 4 ++-- .../test/expressions/rest-array-attern.ts | 6 ++++++ 2 files changed, 8 insertions(+), 2 deletions(-) create mode 100644 languages/tree-sitter-stack-graphs-typescript/test/expressions/rest-array-attern.ts diff --git a/languages/tree-sitter-stack-graphs-typescript/src/stack-graphs.tsg b/languages/tree-sitter-stack-graphs-typescript/src/stack-graphs.tsg index 01808435e..f08f962b6 100644 --- a/languages/tree-sitter-stack-graphs-typescript/src/stack-graphs.tsg +++ b/languages/tree-sitter-stack-graphs-typescript/src/stack-graphs.tsg @@ -4127,7 +4127,7 @@ if none @is_async { (for_in_statement ["var" "let" "const"] left:(identifier)@name) (variable_declarator name:(identifier)@name) (pattern/identifier)@name - (rest_pattern (_)@name) + (rest_pattern (identifier)@name) ] { node @name.cotype node @name.lexical_scope @@ -4137,7 +4137,7 @@ if none @is_async { (for_in_statement ["var" "let" "const"] left:(identifier)@name) (variable_declarator name:(identifier)@name) (pattern/identifier)@name - (rest_pattern (_)@name) + (rest_pattern (identifier)@name) ] { node @name.defs node @name.expr_def diff --git a/languages/tree-sitter-stack-graphs-typescript/test/expressions/rest-array-attern.ts b/languages/tree-sitter-stack-graphs-typescript/test/expressions/rest-array-attern.ts new file mode 100644 index 000000000..7bc38c4a0 --- /dev/null +++ b/languages/tree-sitter-stack-graphs-typescript/test/expressions/rest-array-attern.ts @@ -0,0 +1,6 @@ +function foo(...[bar]) { + bar; +// ^ defined: 1 +} + +export {} \ No newline at end of file From 2cabfdfa6b5d4425e4e9ec75622a385e8dbeb78d Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Wed, 22 Nov 2023 18:05:07 +0100 Subject: [PATCH 084/291] Add support for class static blocks --- .../src/stack-graphs.tsg | 9 +++++++++ .../test/statements/class-static-block.ts | 12 ++++++++++++ 2 files changed, 21 insertions(+) create mode 100644 languages/tree-sitter-stack-graphs-typescript/test/statements/class-static-block.ts diff --git a/languages/tree-sitter-stack-graphs-typescript/src/stack-graphs.tsg b/languages/tree-sitter-stack-graphs-typescript/src/stack-graphs.tsg index f08f962b6..66c432b46 100644 --- a/languages/tree-sitter-stack-graphs-typescript/src/stack-graphs.tsg +++ b/languages/tree-sitter-stack-graphs-typescript/src/stack-graphs.tsg @@ -4348,6 +4348,7 @@ if none @is_async { [ (abstract_method_signature) (call_signature) + (class_static_block) (construct_signature) (decorator) ; not strictly a member, but appears in same positions (index_signature) @@ -4371,6 +4372,14 @@ if none @is_async { +;; Static Block + +(class_static_block body:(_)@body)@static { + edge @body.lexical_scope -> @static.lexical_scope +} + + + ;; Construct Signature ; (construct_signature diff --git a/languages/tree-sitter-stack-graphs-typescript/test/statements/class-static-block.ts b/languages/tree-sitter-stack-graphs-typescript/test/statements/class-static-block.ts new file mode 100644 index 000000000..6ccdadea4 --- /dev/null +++ b/languages/tree-sitter-stack-graphs-typescript/test/statements/class-static-block.ts @@ -0,0 +1,12 @@ +let bar = 42; + +class Foo { + static foo: number; + static { + this.foo = bar; + // ^ defined: 4 + // ^ defined: 1 + } +} + +export {} From 90f9a2117704a4110661e02e4697d9c052c04d5a Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Mon, 4 Dec 2023 17:39:18 +0100 Subject: [PATCH 085/291] Stop using LFS for our small archives --- .gitattributes | 3 --- data/typescript_benchmark.zip | Bin 131 -> 238818 bytes data/typescript_minimal_project.zip | Bin 129 -> 1117 bytes 3 files changed, 3 deletions(-) delete mode 100644 .gitattributes diff --git a/.gitattributes b/.gitattributes deleted file mode 100644 index 041b0e4e4..000000000 --- a/.gitattributes +++ /dev/null @@ -1,3 +0,0 @@ -data/*.tar.gz filter=lfs diff=lfs merge=lfs -text -data/*.tgz filter=lfs diff=lfs merge=lfs -text -data/*.zip filter=lfs diff=lfs merge=lfs -text diff --git a/data/typescript_benchmark.zip b/data/typescript_benchmark.zip index 97f24b96d1dfd9129e30226959a982299ebdc79c..9cf6105adccd4d0458c3872674ebddd7c802e836 100644 GIT binary patch literal 238818 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

RHYU?aJ{J1FA;>A2sm8&E! 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 literal 131 zcmWN?%MrpL5CG6SRnUL|mfw~vm6}|l9~#2R zz%2IVa0&>QR&X;gvV3J^U|xu$LA){FH#AAgqqx(1OI|?8Nj`y{zK= zJg^1rFAt}HFq#F`Tna#-l$V&Bs$`|01a+G()NM*!wOj}%2u%7DW|aEqx+u_k5LUwP zgwm4C9KDiau8!`8p*;ZKtHo>Sa_EiXc-7= z;J2`(I5|HrEi)bA>29F4AdK#5h_65a%)!73^i}DD{q6cdo*57;2p2AEh$o6;w{e~yh(2@<%HY`a7;wxM!2HCcC_-#X^ dAD~5`^n>9oRyJU`Ft7q4FB1cU6%aEp003RaIAZ_+ literal 129 zcmWN?K@!3s3;@7;U(pX(2v9QiHwj5BGb$aSJ@|Uv%U=5FwY}$kjH7SPJ?gkUtuO!g zlb6z7t{+<51yids_npF;x2$muQoy4oA`?r1CZRAhh!1F9A`2x}PN;W6D-^P2!W5GS Mn|l4~3;;O&00FBgkN^Mx From 364395d1e57db42d95b43bef64b6258f43278704 Mon Sep 17 00:00:00 2001 From: Rebecca Valentine Date: Mon, 18 Dec 2023 12:08:48 -0800 Subject: [PATCH 086/291] Fix guard typo Co-Authored-By: Rob Rix --- .../tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg b/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg index 4e82e857d..3923aafe7 100644 --- a/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg +++ b/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg @@ -4476,7 +4476,7 @@ inherit .return_or_yield edge pop_default_guard -> @right.value ;; For ES6 interoperability, expose members as named exports - attr (pop_dot) pop_symbol = "GAURD:MEMBER" + attr (pop_dot) pop_symbol = "GUARD:MEMBER" edge @assignment_expr.exports -> pop_dot edge pop_dot -> @right.value From e76fbe40f4c6e4f8c413df2fe919d4f19a7122dc Mon Sep 17 00:00:00 2001 From: Rebecca Valentine Date: Mon, 18 Dec 2023 12:08:58 -0800 Subject: [PATCH 087/291] Remove extraneous edge Co-Authored-By: Rob Rix --- .../tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg | 3 --- 1 file changed, 3 deletions(-) diff --git a/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg b/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg index 3923aafe7..d7faf39f2 100644 --- a/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg +++ b/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg @@ -3184,9 +3184,6 @@ inherit .return_or_yield edge @new_expr.value -> new_expr_guard_this edge new_expr_guard_this -> new_expr_call - ; value coming from the field decls in the class - edge @new_expr.value -> @constructor.value - ; value also coming from the prototype node guard_prototype attr (guard_prototype) push_symbol = "GUARD:PROTOTYPE" From f011526f86d687e1a8725cdddc23360fdb93dab3 Mon Sep 17 00:00:00 2001 From: Jason Lefever Date: Mon, 8 Jan 2024 17:13:45 -0500 Subject: [PATCH 088/291] Replace "stank graph" with "stack graph" --- tree-sitter-stack-graphs/src/cli/index.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tree-sitter-stack-graphs/src/cli/index.rs b/tree-sitter-stack-graphs/src/cli/index.rs index f47e03cf0..6d192e382 100644 --- a/tree-sitter-stack-graphs/src/cli/index.rs +++ b/tree-sitter-stack-graphs/src/cli/index.rs @@ -498,7 +498,7 @@ pub enum IndexError { LoadError(#[source] crate::loader::LoadError<'static>), #[error("failed to read file")] ReadError(#[from] std::io::Error), - #[error("failed to build stank graph")] + #[error("failed to build stack graph")] StackGraph, #[error(transparent)] StorageError(#[from] stack_graphs::storage::StorageError), From be380496728a91744ab449682af6d7bac6efdc6a Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Thu, 11 Jan 2024 13:57:32 -0800 Subject: [PATCH 089/291] Update README to include some usage guidance --- .github/workflows/ci.yml | 39 +++-- CONTRIBUTING.md | 5 +- .../tree-sitter-stack-graphs-java/README.md | 134 ++++++++++++++++-- .../README.md | 102 +++++++++---- .../README.md | 116 +++++++++------ script/ci-test-init | 18 +++ tree-sitter-stack-graphs/README.md | 105 +++++++++----- tree-sitter-stack-graphs/examples/README.md | 8 +- tree-sitter-stack-graphs/src/cli/init.rs | 126 ++++++++++------ 9 files changed, 465 insertions(+), 188 deletions(-) create mode 100755 script/ci-test-init diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c561bd429..cad93d701 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -58,21 +58,32 @@ jobs: run: cargo test --all-features - name: Run test suite with all optimizations run: cargo test --release - # Do the new project test last, because it adds the crate in the current source - # folder, and that shouldn't influence other tests. + + test-init: + needs: [test-rust] + runs-on: ubuntu-latest + strategy: + matrix: + rust: [stable] + + steps: + - name: Install Rust environment + uses: hecrj/setup-rust-action@v1 + with: + rust-version: ${{ matrix.rust }} + - name: Checkout code + uses: actions/checkout@v3 + - name: Cache dependencies + uses: actions/cache@v3 + with: + path: | + ~/.cargo + target + key: ${{ runner.OS }}-cargo-${{ hashFiles('**/Cargo.lock') }} + restore-keys: | + ${{ runner.OS }}-cargo- - name: Generate, build, and run new language project - run: | - cargo run --bin tree-sitter-stack-graphs --features cli -- init \ - --language-name InitTest \ - --language-id init_test \ - --language-file-extension it \ - --grammar-crate-name tree-sitter-python \ - --grammar-crate-version 0.20.0 \ - --internal \ - --non-interactive - cargo check -p tree-sitter-stack-graphs-init_test --all-features - cargo test -p tree-sitter-stack-graphs-init_test - cargo run -p tree-sitter-stack-graphs-init_test --features cli -- help + run: script/ci-test-init list-languages: runs-on: ubuntu-latest diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 1992fceca..3827e3473 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -32,8 +32,8 @@ Here are a few things you can do that will increase the likelihood of your pull If you are one of the maintainers of this package, bump the version numbers in [`Cargo.toml`](Cargo.toml) and [`README.md`](README.md), then follow the typical instructions to publish a new version to [crates.io][]: ``` -$ cargo package -$ cargo publish +cargo package +cargo publish ``` [crates.io]: https://crates.io/stack-graphs/ @@ -43,4 +43,3 @@ $ cargo publish - [How to Contribute to Open Source](https://opensource.guide/how-to-contribute/) - [Using Pull Requests](https://help.github.com/articles/about-pull-requests/) - [GitHub Help](https://help.github.com) - diff --git a/languages/tree-sitter-stack-graphs-java/README.md b/languages/tree-sitter-stack-graphs-java/README.md index 821796b67..40c0f847f 100644 --- a/languages/tree-sitter-stack-graphs-java/README.md +++ b/languages/tree-sitter-stack-graphs-java/README.md @@ -1,30 +1,138 @@ # tree-sitter-stack-graphs definition for Java -This project defines tree-sitter-stack-graphs rules for Java using the [tree-sitter-java](https://www.npmjs.com/package/tree-sitter-java) grammar. +This project defines tree-sitter-stack-graphs rules for Java using the [tree-sitter-java][] grammar. -## Local Development +[tree-sitter-java]: https://crates.io/crates/tree-sitter-java + +- [API documentation](https://docs.rs/tree-sitter-stack-graphs-java/) +- [Release notes](https://github.com/github/stack-graphs/blob/main/languages/tree-sitter-stack-graphs-java/CHANGELOG.md) + +## Using the API + +To use this library, add the following to your `Cargo.toml`: + +```toml +[dependencies] +tree-sitter-stack-graphs-java = "0.2.0" +``` + +Check out our [documentation](https://docs.rs/tree-sitter-stack-graphs-java/*/) for more details on how to use this library. + +## Using the Command-line Program + +The command-line program for `tree-sitter-stack-graphs-java` lets you do stack graph based analysis and lookup from the command line. + +The CLI can be run as follows: + +1. _(Installed)_ Install the CLI using Cargo as follows: + + ```sh + cargo install --features cli tree-sitter-stack-graphs-java + ``` + + After this, the CLI should be available as `tree-sitter-stack-graphs-java`. + +2. _(From source)_ Instead of installing the CLI, it can also be run directly from the crate directory, as a replacement for a `tree-sitter-stack-graphs-java` invocation, as follows: + + ```sh + cargo run --features cli -- + ``` + +The basic CLI workflow for the command-line program is to index source code and issue queries against the resulting database: + +1. Index a source folder as follows: + + ```sh + tree-sitter-stack-graphs-java index SOURCE_DIR + ``` + + _Indexing will skip any files that have already be indexed. To force a re-index, add the `-f` flag._ + + To check the status if a source folder, run: + + ```sh + tree-sitter-stack-graphs-java status SOURCE_DIR + ``` + + To clean the database and start with a clean slate, run: + + ```sh + tree-sitter-stack-graphs-java clean + ``` + + _Pass the `--delete` flag to not just empty the database, but also delete it. This is useful to resolve `unsupported database version` errors that may occur after a version update._ + +2. Run a query to find the definition(s) for a reference on a given line and column, run: + + ```sh + tree-sitter-stack-graphs-java query definition SOURCE_PATH:LINE:COLUMN + ``` + + Resulting definitions are printed, including a source line if the source file is available. + +Discover all available commands and flags by passing the `-h` flag to the CLI directly, or to any of the subcommands. + +## Development + +The project is written in Rust, and requires a recent version installed. Rust can be installed and updated using [rustup][]. + +[rustup]: https://rustup.rs/ The project is organized as follows: - The stack graph rules are defined in `src/stack-graphs.tsg`. +- Builtins sources and configuration are defined in `src/builtins.it` and `builtins.cfg` respectively. - Tests are put into the `test` directory. -The following commands are intended to be run from the repo root. +### Running Tests + +Run the tests as follows: + +```sh +cargo test +``` + +The project consists of a library and a CLI. By default, running `cargo` only applies to the library. To run `cargo` commands on the CLI as well, add `--features cli` or `--all-features`. + +Run the CLI from source as follows: + +```sh +cargo run --features cli -- ARGS +``` + +Sources are formatted using the standard Rust formatted, which is applied by running: + +```sh +cargo fmt +``` + +### Writing TSG + +The stack graph rules are written in [tree-sitter-graph][]. Checkout the [examples][], +which contain self-contained TSG rules for specific language features. A VSCode +[extension][] is available that provides syntax highlighting for TSG files. -Run all tests in the project by executing the following: +[tree-sitter-graph]: https://github.com/tree-sitter/tree-sitter-graph +[examples]: https://github.com/github/stack-graphs/blob/main/tree-sitter-stack-graphs/examples/ +[extension]: https://marketplace.visualstudio.com/items?itemName=tree-sitter.tree-sitter-graph - cargo test -p tree-sitter-stack-graphs-java +Parse and test a single file by executing the following commands: -Parse a single test file: - `cargo run -p tree-sitter-stack-graphs-java -- parse OPTIONS FILENAME` +```sh +cargo run --features cli -- parse FILES... +cargo run --features cli -- test TESTFILES... +``` -Test a single file: - `cargo run -p tree-sitter-stack-graphs-java -- test OPTIONS FILENAME` +Generate a visualization to debug failing tests by passing the `-V` flag: -For debugging purposes, it is useful to run the visualization tool to generate graphs for the tests being run. +```sh +cargo run --features cli -- test -V TESTFILES... +``` -To run a test and generate the visualization: +To generate the visualization regardless of test outcome, execute: -`cargo run -p tree-sitter-stack-graphs-java -- test --output-mode=always -V=%r/%d/%n.html FILENAME` +```sh +cargo run --features cli -- test -V --output-mode=always TESTFILES... +``` -Go to https://crates.io/crates/tree-sitter-stack-graphs for links to examples and documentation. +Go to for links to examples and documentation. diff --git a/languages/tree-sitter-stack-graphs-javascript/README.md b/languages/tree-sitter-stack-graphs-javascript/README.md index 3b53f03de..6d449be9c 100644 --- a/languages/tree-sitter-stack-graphs-javascript/README.md +++ b/languages/tree-sitter-stack-graphs-javascript/README.md @@ -4,27 +4,73 @@ This project defines tree-sitter-stack-graphs rules for JavaScript using the [tr [tree-sitter-javascript]: https://crates.io/crates/tree-sitter-javascript -## Usage +- [API documentation](https://docs.rs/tree-sitter-stack-graphs-javascript/) +- [Release notes](https://github.com/github/stack-graphs/blob/main/languages/tree-sitter-stack-graphs-javascript/CHANGELOG.md) + +## Using the API To use this library, add the following to your `Cargo.toml`: -``` toml +```toml [dependencies] -tree-sitter-stack-graphs-javascript = "0.0.1" +tree-sitter-stack-graphs-javascript = "0.1.0" ``` Check out our [documentation](https://docs.rs/tree-sitter-stack-graphs-javascript/*/) for more details on how to use this library. -## Command-line Program +## Using the Command-line Program The command-line program for `tree-sitter-stack-graphs-javascript` lets you do stack graph based analysis and lookup from the command line. -Install the program using `cargo install` as follows: +The CLI can be run as follows: -``` sh -$ cargo install --features cli tree-sitter-stack-graphs-javascript -$ tree-sitter-stack-graphs-javascript --help -``` +1. _(Installed)_ Install the CLI using Cargo as follows: + + ```sh + cargo install --features cli tree-sitter-stack-graphs-javascript + ``` + + After this, the CLI should be available as `tree-sitter-stack-graphs-javascript`. + +2. _(From source)_ Instead of installing the CLI, it can also be run directly from the crate directory, as a replacement for a `tree-sitter-stack-graphs-javascript` invocation, as follows: + + ```sh + cargo run --features cli -- + ``` + +The basic CLI workflow for the command-line program is to index source code and issue queries against the resulting database: + +1. Index a source folder as follows: + + ```sh + tree-sitter-stack-graphs-javascript index SOURCE_DIR + ``` + + _Indexing will skip any files that have already be indexed. To force a re-index, add the `-f` flag._ + + To check the status if a source folder, run: + + ```sh + tree-sitter-stack-graphs-javascript status SOURCE_DIR + ``` + + To clean the database and start with a clean slate, run: + + ```sh + tree-sitter-stack-graphs-javascript clean + ``` + + _Pass the `--delete` flag to not just empty the database, but also delete it. This is useful to resolve `unsupported database version` errors that may occur after a version update._ + +2. Run a query to find the definition(s) for a reference on a given line and column, run: + + ```sh + tree-sitter-stack-graphs-javascript query definition SOURCE_PATH:LINE:COLUMN + ``` + + Resulting definitions are printed, including a source line if the source file is available. + +Discover all available commands and flags by passing the `-h` flag to the CLI directly, or to any of the subcommands. ## Development @@ -35,35 +81,29 @@ The project is written in Rust, and requires a recent version installed. Rust c The project is organized as follows: - The stack graph rules are defined in `src/stack-graphs.tsg`. -- Builtins sources and configuration are defined in `src/builtins.js` and `builtins.cfg` respectively. +- Builtins sources and configuration are defined in `src/builtins.it` and `builtins.cfg` respectively. - Tests are put into the `test` directory. -### Building and Running Tests - -Build the project by running: - -``` sh -$ cargo build -``` +### Running Tests Run the tests as follows: -``` sh -$ cargo test +```sh +cargo test ``` The project consists of a library and a CLI. By default, running `cargo` only applies to the library. To run `cargo` commands on the CLI as well, add `--features cli` or `--all-features`. Run the CLI from source as follows: -``` sh -$ cargo run --features cli -- ARGS +```sh +cargo run --features cli -- ARGS ``` Sources are formatted using the standard Rust formatted, which is applied by running: -``` sh -$ cargo fmt +```sh +cargo fmt ``` ### Writing TSG @@ -78,21 +118,21 @@ which contain self-contained TSG rules for specific language features. A VSCode Parse and test a single file by executing the following commands: -``` sh -$ cargo run --features cli -- parse FILES... -$ cargo run --features cli -- test TESTFILES... +```sh +cargo run --features cli -- parse FILES... +cargo run --features cli -- test TESTFILES... ``` Generate a visualization to debug failing tests by passing the `-V` flag: -``` sh -$ cargo run --features cli -- test -V TESTFILES... +```sh +cargo run --features cli -- test -V TESTFILES... ``` To generate the visualization regardless of test outcome, execute: -``` sh -$ cargo run --features cli -- test -V --output-mode=always TESTFILES... +```sh +cargo run --features cli -- test -V --output-mode=always TESTFILES... ``` -Go to https://crates.io/crates/tree-sitter-stack-graphs for links to examples and documentation. +Go to for links to examples and documentation. diff --git a/languages/tree-sitter-stack-graphs-typescript/README.md b/languages/tree-sitter-stack-graphs-typescript/README.md index 0c1f9d5ee..fada7e2df 100644 --- a/languages/tree-sitter-stack-graphs-typescript/README.md +++ b/languages/tree-sitter-stack-graphs-typescript/README.md @@ -4,72 +4,106 @@ This project defines tree-sitter-stack-graphs rules for TypeScript using the [tr [tree-sitter-typescript]: https://crates.io/crates/tree-sitter-typescript -## Usage +- [API documentation](https://docs.rs/tree-sitter-stack-graphs-typescript/) +- [Release notes](https://github.com/github/stack-graphs/blob/main/languages/tree-sitter-stack-graphs-typescript/CHANGELOG.md) + +## Using the API To use this library, add the following to your `Cargo.toml`: -``` toml +```toml [dependencies] -tree-sitter-stack-graphs-typescript = "0.1" +tree-sitter-stack-graphs-typescript = "0.1.0" ``` -Check out our [documentation](https://docs.rs/tree-sitter-stack-graphs-typescript/*/) for -more details on how to use this library. +Check out our [documentation](https://docs.rs/tree-sitter-stack-graphs-typescript/*/) for more details on how to use this library. -## Command-line Program +## Using the Command-line Program -The command-line program for `tree-sitter-stack-graphs-typescript` lets you do -stack graph based analysis and lookup from the command line. +The command-line program for `tree-sitter-stack-graphs-typescript` lets you do stack graph based analysis and lookup from the command line. -Install the program using `cargo install` as follows: +The CLI can be run as follows: -``` sh -$ cargo install --features cli tree-sitter-stack-graphs-typescript -$ tree-sitter-stack-graphs-typescript --help -``` +1. _(Installed)_ Install the CLI using Cargo as follows: + + ```sh + cargo install --features cli tree-sitter-stack-graphs-typescript + ``` + + After this, the CLI should be available as `tree-sitter-stack-graphs-typescript`. + +2. _(From source)_ Instead of installing the CLI, it can also be run directly from the crate directory, as a replacement for a `tree-sitter-stack-graphs-typescript` invocation, as follows: + + ```sh + cargo run --features cli -- + ``` + +The basic CLI workflow for the command-line program is to index source code and issue queries against the resulting database: + +1. Index a source folder as follows: + + ```sh + tree-sitter-stack-graphs-typescript index SOURCE_DIR + ``` + + _Indexing will skip any files that have already be indexed. To force a re-index, add the `-f` flag._ + + To check the status if a source folder, run: + + ```sh + tree-sitter-stack-graphs-typescript status SOURCE_DIR + ``` + + To clean the database and start with a clean slate, run: + + ```sh + tree-sitter-stack-graphs-typescript clean + ``` + + _Pass the `--delete` flag to not just empty the database, but also delete it. This is useful to resolve `unsupported database version` errors that may occur after a version update._ + +2. Run a query to find the definition(s) for a reference on a given line and column, run: + + ```sh + tree-sitter-stack-graphs-typescript query definition SOURCE_PATH:LINE:COLUMN + ``` + + Resulting definitions are printed, including a source line if the source file is available. + +Discover all available commands and flags by passing the `-h` flag to the CLI directly, or to any of the subcommands. ## Development -The project is written in Rust, and requires a recent version installed. -Rust can be installed and updated using [rustup][]. +The project is written in Rust, and requires a recent version installed. Rust can be installed and updated using [rustup][]. [rustup]: https://rustup.rs/ - The project is organized as follows: - The stack graph rules are defined in `src/stack-graphs.tsg`. -- Builtins sources and configuration are defined in `src/builtins.ts` and `builtins.cfg` respectively. +- Builtins sources and configuration are defined in `src/builtins.it` and `builtins.cfg` respectively. - Tests are put into the `test` directory. -### Building and Running Tests - -Build the project by running: - -``` sh -$ cargo build -``` +### Running Tests Run the tests as follows: -``` sh -$ cargo test +```sh +cargo test ``` -The project consists of a library and a CLI. -By default, running `cargo` only applies to the library. -To run `cargo` commands on the CLI as well, add `--features cli` or `--all-features`. +The project consists of a library and a CLI. By default, running `cargo` only applies to the library. To run `cargo` commands on the CLI as well, add `--features cli` or `--all-features`. Run the CLI from source as follows: -``` sh -$ cargo run --features cli -- ARGS +```sh +cargo run --features cli -- ARGS ``` Sources are formatted using the standard Rust formatted, which is applied by running: -``` sh -$ cargo fmt +```sh +cargo fmt ``` ### Writing TSG @@ -84,21 +118,21 @@ which contain self-contained TSG rules for specific language features. A VSCode Parse and test a single file by executing the following commands: -``` sh -$ cargo run --features cli -- parse FILES... -$ cargo run --features cli -- test TESTFILES... +```sh +cargo run --features cli -- parse FILES... +cargo run --features cli -- test TESTFILES... ``` Generate a visualization to debug failing tests by passing the `-V` flag: -``` sh -$ cargo run --features cli -- test -V TESTFILES... +```sh +cargo run --features cli -- test -V TESTFILES... ``` To generate the visualization regardless of test outcome, execute: -``` sh -$ cargo run --features cli -- test -V --output-mode=always TESTFILES... +```sh +cargo run --features cli -- test -V --output-mode=always TESTFILES... ``` -Go to https://crates.io/crates/tree-sitter-stack-graphs for links to examples and documentation. +Go to for links to examples and documentation. diff --git a/script/ci-test-init b/script/ci-test-init new file mode 100755 index 000000000..c58532fd5 --- /dev/null +++ b/script/ci-test-init @@ -0,0 +1,18 @@ +#!/usr/bin/env bash + +set -eu + +cargo run --bin tree-sitter-stack-graphs --features cli -- init \ + --language-name InitTest \ + --language-id init_test \ + --language-file-extension it \ + --grammar-crate-name tree-sitter-python \ + --grammar-crate-version 0.20.0 \ + --internal \ + --non-interactive + +cargo check -p tree-sitter-stack-graphs-init_test --all-features + +cargo test -p tree-sitter-stack-graphs-init_test + +cargo run -p tree-sitter-stack-graphs-init_test --features cli -- -V diff --git a/tree-sitter-stack-graphs/README.md b/tree-sitter-stack-graphs/README.md index 01ce41971..dc7b18a80 100644 --- a/tree-sitter-stack-graphs/README.md +++ b/tree-sitter-stack-graphs/README.md @@ -1,7 +1,6 @@ # tree-sitter-stack-graphs -The `tree-sitter-stack-graphs` crate lets you create stack graphs using the -[tree-sitter][] grammar for a language. +The `tree-sitter-stack-graphs` crate lets you create stack graphs using the [tree-sitter][] grammar for a language. [tree-sitter]: https://tree-sitter.github.io/ @@ -9,50 +8,82 @@ The `tree-sitter-stack-graphs` crate lets you create stack graphs using the - [Examples](https://github.com/github/stack-graphs/blob/main/tree-sitter-stack-graphs/examples/) - [Release notes](https://github.com/github/stack-graphs/blob/main/tree-sitter-stack-graphs/CHANGELOG.md) -## Usage +## Using the API To use this library, add the following to your `Cargo.toml`: -``` toml +```toml [dependencies] tree-sitter-stack-graphs = "0.7" ``` -Check out our [documentation](https://docs.rs/tree-sitter-stack-graphs/*/) for -more details on how to use this library. +Check out our [documentation](https://docs.rs/tree-sitter-stack-graphs/*/) for more details on how to use this library. -## Command-line Program +## Using the Command-line Program -The command-line program for `tree-sitter-stack-graphs` lets you do stack -graph based analysis and lookup from the command line. +The command-line program for `tree-sitter-stack-graphs` lets you do stack graph based analysis and lookup from the command line. -Install the program using `cargo install` as follows: +The CLI can be run as follows: -``` sh -$ cargo install --features cli tree-sitter-stack-graphs -$ tree-sitter-stack-graphs --help -``` +1. _(Installed)_ Install the CLI using Cargo as follows: -Alternatively, the program can be invoked via NPM as follows: + ```sh + cargo install --features cli tree-sitter-stack-graphs + ``` -``` sh -$ npx tree-sitter-stack-graphs -``` + After this, the CLI should be available as `tree-sitter-stack-graphs`. + +2. _(From source)_ Instead of installing the CLI, it can also be run directly from the crate directory, as a replacement for a `tree-sitter-stack-graphs` invocation, as follows: + + ```sh + cargo run --features cli -- + ``` + +The basic CLI workflow for the command-line program is to index source code and issue queries against the resulting database: + +1. Index a source folder as follows: + + ```sh + tree-sitter-stack-graphs index SOURCE_DIR + ``` + + _Indexing will skip any files that have already be indexed. To force a re-index, add the `-f` flag._ + + To check the status if a source folder, run: + + ```sh + tree-sitter-stack-graphs status SOURCE_DIR + ``` -## Getting Started + To clean the database and start with a clean slate, run: -Starting a new project to develop stack graph definitions for your favourite language -is as easy as running the `init` command: + ```sh + tree-sitter-stack-graphs clean + ``` -``` sh -$ tree-sitter-stack-graphs init PROJECT_DIR + _Pass the `--delete` flag to not just empty the database, but also delete it. This is useful to resolve `unsupported database version` errors that may occur after a version update._ + +2. Run a query to find the definition(s) for a reference on a given line and column, run: + + ```sh + tree-sitter-stack-graphs query definition SOURCE_PATH:LINE:COLUMN + ``` + + Resulting definitions are printed, including a source line if the source file is available. + +Discover all available commands and flags by passing the `-h` flag to the CLI directly, or to any of the subcommands. + +## Getting Started on a new Language + +Starting a new project to develop stack graph definitions for your favourite language is as easy as running the `init` command: + +```sh +tree-sitter-stack-graphs init PROJECT_DIR ``` -Answer the questions to provide information about the language, the grammar dependency, and -the project and hit `Generate` to generate the new project. Check out `PROJECT_DIR/README.md` -to find out how to start developing. +Answer the questions to provide information about the language, the grammar dependency, and the project and hit `Generate` to generate the new project. Check out `PROJECT_DIR/README.md` to find out how to start developing. -Also check out our [examples][] for more details on how to work with the CLI. +Check out [examples][] of stack graph rules for typical language features. [examples]: https://github.com/github/stack-graphs/blob/main/tree-sitter-stack-graphs/examples/ @@ -65,14 +96,14 @@ Rust can be installed and updated using [rustup][]. Build the project by running: -``` -$ cargo build +```sh +cargo build ``` Run the tests by running: -``` -$ cargo test +```sh +cargo test ``` The project consists of a library and a CLI. @@ -81,22 +112,22 @@ To run `cargo` commands on the CLI as well, add `--features cli` or `--all-featu Run the CLI from source as follows: -``` sh -$ cargo run --features cli -- ARGS +```sh +cargo run --features cli -- ARGS ``` Sources are formatted using the standard Rust formatted, which is applied by running: -``` -$ cargo fmt +```sh +cargo fmt ``` ## License Licensed under either of - - [Apache License, Version 2.0][apache] ([LICENSE-APACHE](LICENSE-APACHE)) - - [MIT license][mit] ([LICENSE-MIT](LICENSE-MIT)) +- [Apache License, Version 2.0][apache] ([LICENSE-APACHE](LICENSE-APACHE)) +- [MIT license][mit] ([LICENSE-MIT](LICENSE-MIT)) at your option. diff --git a/tree-sitter-stack-graphs/examples/README.md b/tree-sitter-stack-graphs/examples/README.md index 901616422..5d5186ad5 100644 --- a/tree-sitter-stack-graphs/examples/README.md +++ b/tree-sitter-stack-graphs/examples/README.md @@ -7,19 +7,19 @@ Each directory contains a `stack-graphs.tsg` file that describes at the top what Running the examples requires the Python grammar to be available. This can be installed (in this directory) by executing: ```bash -$ ./bootstrap +./bootstrap ``` Run the tests for an example by executing: ```bash -$ ./run EXAMPLE_DIR +./run EXAMPLE_DIR ``` or, from within the example's directory: ```bash -$ ../run +../run ``` To render HTML visualizations of the stack graphs for the tests in an example, add the `-V` flag to run. @@ -27,7 +27,7 @@ To render HTML visualizations of the stack graphs for the tests in an example, a Print the parse tree of an example file by executing: ```bash -$ ./parse EXAMPLE_FILE +./parse EXAMPLE_FILE ``` The following examples are available: diff --git a/tree-sitter-stack-graphs/src/cli/init.rs b/tree-sitter-stack-graphs/src/cli/init.rs index e381786b9..df18339aa 100644 --- a/tree-sitter-stack-graphs/src/cli/init.rs +++ b/tree-sitter-stack-graphs/src/cli/init.rs @@ -455,33 +455,79 @@ impl ProjectSettings<'_> { fn generate_readme(&self, project_path: &Path) -> anyhow::Result<()> { let mut file = File::create(project_path.join("README.md"))?; writedoc! {file, r####" - # tree-sitter-stack-graphs definition for {} + # tree-sitter-stack-graphs definition for {language_name} - This project defines tree-sitter-stack-graphs rules for {} using the [{}][] grammar. + This project defines tree-sitter-stack-graphs rules for {language_name} using the [{grammar_crate_name}][] grammar. - [{}]: https://crates.io/crates/{} + [{grammar_crate_name}]: https://crates.io/crates/{grammar_crate_name} - ## Usage + - [API documentation](https://docs.rs/{crate_name}/) + - [Release notes](https://github.com/github/stack-graphs/blob/main/languages/{crate_name}/CHANGELOG.md) + + ## Using the API To use this library, add the following to your `Cargo.toml`: - ``` toml + ```toml [dependencies] - {} = "{}" + {crate_name} = "{crate_version}" ``` - Check out our [documentation](https://docs.rs/{}/*/) for more details on how to use this library. + Check out our [documentation](https://docs.rs/{crate_name}/*/) for more details on how to use this library. - ## Command-line Program + ## Using the Command-line Program - The command-line program for `{}` lets you do stack graph based analysis and lookup from the command line. + The command-line program for `{crate_name}` lets you do stack graph based analysis and lookup from the command line. - Install the program using `cargo install` as follows: + The CLI can be run as follows: - ``` sh - $ cargo install --features cli {} - $ {} --help - ``` + 1. _(Installed)_ Install the CLI using Cargo as follows: + + ```sh + cargo install --features cli {crate_name} + ``` + + After this, the CLI should be available as `{crate_name}`. + + 2. _(From source)_ Instead of installing the CLI, it can also be run directly from the crate directory, as a replacement for a `{crate_name}` invocation, as follows: + + ```sh + cargo run --features cli -- + ``` + + The basic CLI workflow for the command-line program is to index source code and issue queries against the resulting database: + + 1. Index a source folder as follows: + + ```sh + {crate_name} index SOURCE_DIR + ``` + + _Indexing will skip any files that have already be indexed. To force a re-index, add the `-f` flag._ + + To check the status if a source folder, run: + + ```sh + {crate_name} status SOURCE_DIR + ``` + + To clean the database and start with a clean slate, run: + + ```sh + {crate_name} clean + ``` + + _Pass the `--delete` flag to not just empty the database, but also delete it. This is useful to resolve `unsupported database version` errors that may occur after a version update._ + + 2. Run a query to find the definition(s) for a reference on a given line and column, run: + + ```sh + {crate_name} query definition SOURCE_PATH:LINE:COLUMN + ``` + + Resulting definitions are printed, including a source line if the source file is available. + + Discover all available commands and flags by passing the `-h` flag to the CLI directly, or to any of the subcommands. ## Development @@ -492,35 +538,29 @@ impl ProjectSettings<'_> { The project is organized as follows: - The stack graph rules are defined in `src/stack-graphs.tsg`. - - Builtins sources and configuration are defined in `src/builtins.{}` and `builtins.cfg` respectively. + - Builtins sources and configuration are defined in `src/builtins.{language_file_extension}` and `builtins.cfg` respectively. - Tests are put into the `test` directory. - ### Building and Running Tests - - Build the project by running: - - ``` sh - $ cargo build - ``` + ### Running Tests Run the tests as follows: - ``` sh - $ cargo test + ```sh + cargo test ``` The project consists of a library and a CLI. By default, running `cargo` only applies to the library. To run `cargo` commands on the CLI as well, add `--features cli` or `--all-features`. Run the CLI from source as follows: - ``` sh - $ cargo run --features cli -- ARGS + ```sh + cargo run --features cli -- ARGS ``` Sources are formatted using the standard Rust formatted, which is applied by running: - ``` sh - $ cargo fmt + ```sh + cargo fmt ``` ### Writing TSG @@ -535,34 +575,30 @@ impl ProjectSettings<'_> { Parse and test a single file by executing the following commands: - ``` sh - $ cargo run --features cli -- parse FILES... - $ cargo run --features cli -- test TESTFILES... + ```sh + cargo run --features cli -- parse FILES... + cargo run --features cli -- test TESTFILES... ``` Generate a visualization to debug failing tests by passing the `-V` flag: - ``` sh - $ cargo run --features cli -- test -V TESTFILES... + ```sh + cargo run --features cli -- test -V TESTFILES... ``` To generate the visualization regardless of test outcome, execute: - ``` sh - $ cargo run --features cli -- test -V --output-mode=always TESTFILES... + ```sh + cargo run --features cli -- test -V --output-mode=always TESTFILES... ``` - Go to https://crates.io/crates/tree-sitter-stack-graphs for links to examples and documentation. + Go to for links to examples and documentation. "####, - self.language_name, - self.language_name, self.grammar_crate_name(), - self.grammar_crate_name(), self.grammar_crate_name(), - self.crate_name(), self.crate_version(), - self.crate_name(), - self.crate_name(), - self.crate_name(), - self.crate_name(), - self.language_file_extension, + language_name=self.language_name, + grammar_crate_name=self.grammar_crate_name(), + crate_name=self.crate_name(), + crate_version=self.crate_version(), + language_file_extension=self.language_file_extension, }?; Ok(()) } From 13b5e25b3603126883074caffc6409e8a73b8d4c Mon Sep 17 00:00:00 2001 From: Rob Rix Date: Tue, 16 Jan 2024 14:38:57 -0500 Subject: [PATCH 090/291] Nested arrow functions. Co-Authored-By: Rebecca Valentine <171941+BekaValentine@users.noreply.github.com> --- .../tree-sitter-stack-graphs-javascript/test/base_syntax.js | 1 + 1 file changed, 1 insertion(+) diff --git a/languages/tree-sitter-stack-graphs-javascript/test/base_syntax.js b/languages/tree-sitter-stack-graphs-javascript/test/base_syntax.js index e732ced2a..17a9ddcca 100644 --- a/languages/tree-sitter-stack-graphs-javascript/test/base_syntax.js +++ b/languages/tree-sitter-stack-graphs-javascript/test/base_syntax.js @@ -46,6 +46,7 @@ undefined; [1, 2, 3]; function () { return; }; () => { }; +() => () => { }; function* () { yield 1; }; foo(); foo(bar); From 1f75acb21053475198da0962f730dd02e5419165 Mon Sep 17 00:00:00 2001 From: Rob Rix Date: Tue, 16 Jan 2024 14:52:44 -0500 Subject: [PATCH 091/291] Trace return_or_yield through an edge. Co-Authored-By: Rebecca Valentine <171941+BekaValentine@users.noreply.github.com> --- .../tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg b/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg index d7faf39f2..e15e3e301 100644 --- a/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg +++ b/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg @@ -2818,7 +2818,7 @@ inherit .return_or_yield ; function values have return nodes which need to be visible for returns attr (fun_value_return) pop_symbol = "GUARD:RETURN" edge fun_value_call -> fun_value_return - let @body.return_or_yield = fun_value_return + edge fun_value_return -> @fun.return_or_yield ; function values have this nodes which need to be visible for method calls attr (fun_value_this) push_symbol = "this" From d31bd76627cab9f041810070b189cfb959450331 Mon Sep 17 00:00:00 2001 From: Rob Rix Date: Tue, 16 Jan 2024 15:18:12 -0500 Subject: [PATCH 092/291] Nested functions. Co-Authored-By: Rebecca Valentine <171941+BekaValentine@users.noreply.github.com> --- .../tree-sitter-stack-graphs-javascript/test/base_syntax.js | 1 + 1 file changed, 1 insertion(+) diff --git a/languages/tree-sitter-stack-graphs-javascript/test/base_syntax.js b/languages/tree-sitter-stack-graphs-javascript/test/base_syntax.js index 17a9ddcca..3f21383b5 100644 --- a/languages/tree-sitter-stack-graphs-javascript/test/base_syntax.js +++ b/languages/tree-sitter-stack-graphs-javascript/test/base_syntax.js @@ -45,6 +45,7 @@ undefined; []; [1, 2, 3]; function () { return; }; +function () { return function () { }; }; () => { }; () => () => { }; function* () { yield 1; }; From 4a70bcf496f5a26bfdb5cddc1eb593b3ab06373e Mon Sep 17 00:00:00 2001 From: Rob Rix Date: Tue, 16 Jan 2024 15:25:05 -0500 Subject: [PATCH 093/291] Test comments in param lists. Co-Authored-By: Rebecca Valentine <171941+BekaValentine@users.noreply.github.com> --- .../tree-sitter-stack-graphs-javascript/test/base_syntax.js | 1 + 1 file changed, 1 insertion(+) diff --git a/languages/tree-sitter-stack-graphs-javascript/test/base_syntax.js b/languages/tree-sitter-stack-graphs-javascript/test/base_syntax.js index e732ced2a..6bd200294 100644 --- a/languages/tree-sitter-stack-graphs-javascript/test/base_syntax.js +++ b/languages/tree-sitter-stack-graphs-javascript/test/base_syntax.js @@ -47,6 +47,7 @@ undefined; function () { return; }; () => { }; function* () { yield 1; }; +function (/**/) { }; foo(); foo(bar); foo.bar; From 8c46a27829bfe4b9adfcabd5964aed7cfa5813b2 Mon Sep 17 00:00:00 2001 From: Rob Rix Date: Tue, 16 Jan 2024 15:32:02 -0500 Subject: [PATCH 094/291] Give comments covalues. Co-Authored-By: Rebecca Valentine <171941+BekaValentine@users.noreply.github.com> --- .../tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg | 1 + 1 file changed, 1 insertion(+) diff --git a/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg b/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg index d7faf39f2..2afc77d71 100644 --- a/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg +++ b/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg @@ -398,6 +398,7 @@ inherit .return_or_yield node @comment.after_scope node @comment.before_scope node @comment.value + node @comment.covalue edge @comment.after_scope -> @comment.before_scope } From 8f39af50b135abc630ae5705018ff318fef76f18 Mon Sep 17 00:00:00 2001 From: Rob Rix Date: Tue, 16 Jan 2024 15:40:38 -0500 Subject: [PATCH 095/291] Test with parameters. Co-Authored-By: Rebecca Valentine <171941+BekaValentine@users.noreply.github.com> --- .../tree-sitter-stack-graphs-javascript/test/base_syntax.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/languages/tree-sitter-stack-graphs-javascript/test/base_syntax.js b/languages/tree-sitter-stack-graphs-javascript/test/base_syntax.js index 6bd200294..697edaac6 100644 --- a/languages/tree-sitter-stack-graphs-javascript/test/base_syntax.js +++ b/languages/tree-sitter-stack-graphs-javascript/test/base_syntax.js @@ -48,6 +48,8 @@ function () { return; }; () => { }; function* () { yield 1; }; function (/**/) { }; +function (x /**/) { }; +function (/**/ x) { }; foo(); foo(bar); foo.bar; From c1b16560b0353fdb3ad039ed632b60f6878f1b3c Mon Sep 17 00:00:00 2001 From: Rob Rix Date: Tue, 16 Jan 2024 16:03:30 -0500 Subject: [PATCH 096/291] Extract the else_clause nodes into their own rule. They were being run twice due to :sparkles: comments :sparkles: Co-Authored-By: Rebecca Valentine <171941+BekaValentine@users.noreply.github.com> --- .../src/stack-graphs.tsg | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg b/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg index 9e887ea32..602d19244 100644 --- a/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg +++ b/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg @@ -1708,6 +1708,11 @@ inherit .return_or_yield edge @if_stmt.after_scope -> @alternative.after_scope } +(else_clause)@else_clause { + node @else_clause.after_scope + node @else_clause.before_scope +} + (else_clause (_)@inner)@else_clause { @@ -1716,8 +1721,6 @@ inherit .return_or_yield } (else_clause (_)@inner)@else_clause { - node @else_clause.after_scope - node @else_clause.before_scope ; scopes flow in and right back out edge @inner.before_scope -> @else_clause.before_scope edge @else_clause.after_scope -> @inner.after_scope From 500f5fc4f9789dba24ff9984cba0fcc41d619d87 Mon Sep 17 00:00:00 2001 From: Rob Rix Date: Tue, 16 Jan 2024 16:06:50 -0500 Subject: [PATCH 097/291] The first child is the hoist point. Co-Authored-By: Rebecca Valentine <171941+BekaValentine@users.noreply.github.com> --- .../tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg b/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg index 602d19244..b1c6481d2 100644 --- a/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg +++ b/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg @@ -1714,7 +1714,7 @@ inherit .return_or_yield } (else_clause - (_)@inner)@else_clause { + . (_)@inner)@else_clause { let @else_clause.hoist_point = @inner.before_scope From 726aa2931129b1925488afd48b1f5d68df7cefad Mon Sep 17 00:00:00 2001 From: Rob Rix Date: Tue, 16 Jan 2024 16:13:54 -0500 Subject: [PATCH 098/291] Test comments around if statements. Co-Authored-By: Rebecca Valentine <171941+BekaValentine@users.noreply.github.com> --- .../tree-sitter-stack-graphs-javascript/test/base_syntax.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/languages/tree-sitter-stack-graphs-javascript/test/base_syntax.js b/languages/tree-sitter-stack-graphs-javascript/test/base_syntax.js index b557e8bcf..99a0f98b2 100644 --- a/languages/tree-sitter-stack-graphs-javascript/test/base_syntax.js +++ b/languages/tree-sitter-stack-graphs-javascript/test/base_syntax.js @@ -12,6 +12,11 @@ function* foo() { } class Foo { } { } if (true) { } +if (true) { } else { } +if (/**/ true) /**/ { } else /**/ { } +if (true) return; +if (true) return; else return; +if (/**/ true) /**/ return; else /**/ return; switch (x) { } for (x; y; z) { } for (x in xs) { } From 62a1ff3bb22b4f0934bf8f3deba5b297340b418a Mon Sep 17 00:00:00 2001 From: Rebecca Valentine Date: Wed, 17 Jan 2024 14:06:12 -0800 Subject: [PATCH 099/291] Makes class definiens be the constructor instead of the class body --- .../src/stack-graphs.tsg | 32 ++++++++++++++----- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg b/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg index b1c6481d2..e4eb58924 100644 --- a/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg +++ b/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg @@ -4629,11 +4629,19 @@ inherit .return_or_yield ;; These rules are all about declarations and terms that have the names ;; directly in them. -(class_declaration - name:(_)@name - body:(_)@_body)@class_decl { +( + (class_declaration + name:(_)@name + body:(class_body + (method_definition + name:(_)@name)@constructor + ) + ) + + (#eq? @name "constructor") +) { - attr (@name.pop) definiens_node = @class_decl + attr (@name.pop) definiens_node = @constructor } @@ -4680,11 +4688,19 @@ inherit .return_or_yield } -(class - name:(_)@name - body:(_)@_body)@class { +( + (class + name:(_)@name + body:(class_body + (method_definition + name:(_)@name)@constructor + ) + ) - attr (@name.pop) definiens_node = @class + (#eq? @name "constructor") +) { + + attr (@name.pop) definiens_node = @constructor } From dbcf25ce0dbd1700e1ef492da73bcd0ffbac912d Mon Sep 17 00:00:00 2001 From: Rebecca Valentine Date: Thu, 18 Jan 2024 11:25:57 -0800 Subject: [PATCH 100/291] Adds member field name --- .../tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg b/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg index e4eb58924..4d9266296 100644 --- a/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg +++ b/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg @@ -4633,7 +4633,7 @@ inherit .return_or_yield (class_declaration name:(_)@name body:(class_body - (method_definition + member:(method_definition name:(_)@name)@constructor ) ) @@ -4692,7 +4692,7 @@ inherit .return_or_yield (class name:(_)@name body:(class_body - (method_definition + member:(method_definition name:(_)@name)@constructor ) ) From 7f1034f4358f25a284a29a8a61cbc0e8617896a3 Mon Sep 17 00:00:00 2001 From: Rebecca Valentine Date: Thu, 18 Jan 2024 11:37:10 -0800 Subject: [PATCH 101/291] Fixes bug --- .../src/stack-graphs.tsg | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg b/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg index 4d9266296..5d4100acb 100644 --- a/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg +++ b/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg @@ -4634,11 +4634,11 @@ inherit .return_or_yield name:(_)@name body:(class_body member:(method_definition - name:(_)@name)@constructor + name:(_)@_method_name)@constructor ) ) - (#eq? @name "constructor") + (#eq? @_method_name "constructor") ) { attr (@name.pop) definiens_node = @constructor @@ -4693,11 +4693,11 @@ inherit .return_or_yield name:(_)@name body:(class_body member:(method_definition - name:(_)@name)@constructor + name:(_)@_method_name)@constructor ) ) - (#eq? @name "constructor") + (#eq? @_method_name "constructor") ) { attr (@name.pop) definiens_node = @constructor From e7718570ec9f0f4966bcecead9fa16c8f7a82b2a Mon Sep 17 00:00:00 2001 From: Rebecca Valentine Date: Fri, 26 Jan 2024 15:41:24 -0800 Subject: [PATCH 102/291] Adds reference test --- ...ion_fields_and_methods_visible_in_other_methods.js | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 languages/tree-sitter-stack-graphs-javascript/test/computation_flow/class_declaration_fields_and_methods_visible_in_other_methods.js diff --git a/languages/tree-sitter-stack-graphs-javascript/test/computation_flow/class_declaration_fields_and_methods_visible_in_other_methods.js b/languages/tree-sitter-stack-graphs-javascript/test/computation_flow/class_declaration_fields_and_methods_visible_in_other_methods.js new file mode 100644 index 000000000..d00499691 --- /dev/null +++ b/languages/tree-sitter-stack-graphs-javascript/test/computation_flow/class_declaration_fields_and_methods_visible_in_other_methods.js @@ -0,0 +1,11 @@ +class Foo { + bar = 1; + baz() { } + quux() { + this.bar; + // ^ defined: 2 + + this.baz(); + // ^ defined: 2 + } +} \ No newline at end of file From d4f7b88bfc26f358878321450dd369e025b34832 Mon Sep 17 00:00:00 2001 From: Rebecca Valentine Date: Fri, 26 Jan 2024 17:05:34 -0800 Subject: [PATCH 103/291] fixes bug in test --- ...s_declaration_fields_and_methods_visible_in_other_methods.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/languages/tree-sitter-stack-graphs-javascript/test/computation_flow/class_declaration_fields_and_methods_visible_in_other_methods.js b/languages/tree-sitter-stack-graphs-javascript/test/computation_flow/class_declaration_fields_and_methods_visible_in_other_methods.js index d00499691..5ea5db72e 100644 --- a/languages/tree-sitter-stack-graphs-javascript/test/computation_flow/class_declaration_fields_and_methods_visible_in_other_methods.js +++ b/languages/tree-sitter-stack-graphs-javascript/test/computation_flow/class_declaration_fields_and_methods_visible_in_other_methods.js @@ -6,6 +6,6 @@ class Foo { // ^ defined: 2 this.baz(); - // ^ defined: 2 + // ^ defined: 3 } } \ No newline at end of file From e0198278edb028a1db035abb89df59f18a130305 Mon Sep 17 00:00:00 2001 From: Rebecca Valentine Date: Fri, 26 Jan 2024 17:05:43 -0800 Subject: [PATCH 104/291] Makes test pass --- .../src/stack-graphs.tsg | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg b/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg index 5d4100acb..a9274f6f3 100644 --- a/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg +++ b/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg @@ -257,6 +257,7 @@ inherit .import_statement inherit .pkg_pop inherit .pkg_push inherit .return_or_yield +inherit .containing_class_value @@ -357,6 +358,10 @@ inherit .return_or_yield node @prog.builtins_Regex_prototype node @prog.builtins_arguments_prototype node @prog.builtins_empty_object + ; !!!! HACK + ; stack graphs currently make it impossible to test if an inherited variable + ; like this is defined or not + let @prog.containing_class_value = @prog.builtins_null } @@ -1429,6 +1434,7 @@ inherit .return_or_yield node @name.pop node @class_decl.class_value + let @class_decl.containing_class_value = @class_decl.class_value node guard_prototype node @class_decl.prototype node @class_decl.constructor @@ -2382,6 +2388,14 @@ inherit .return_or_yield ; this is a lookup, ie a push attr (@this.value) symbol_reference = "this", source_node = @this edge @this.value -> @this.before_scope + + node pop_this + attr (pop_this) symbol_definition = "this" + node guard_prototype + attr (guard_prototype) push_symbol = "GUARD:PROTOTYPE" + edge @this.value -> pop_this + edge pop_this -> guard_prototype + edge guard_prototype -> @this.containing_class_value } From cc0e93705be92d3c3c56780f3a290393b3e959d3 Mon Sep 17 00:00:00 2001 From: Rebecca Valentine Date: Fri, 26 Jan 2024 17:27:44 -0800 Subject: [PATCH 105/291] Adds class expressions --- .../src/stack-graphs.tsg | 1 + ..._fields_and_methods_visible_in_other_methods.js | 14 +++++++++++++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg b/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg index a9274f6f3..93cd66066 100644 --- a/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg +++ b/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg @@ -3464,6 +3464,7 @@ inherit .containing_class_value body:(_)@body)@class { node @class.class_value + let @class.containing_class_value = @class.class_value node guard_prototype node @class.prototype node @class.constructor diff --git a/languages/tree-sitter-stack-graphs-javascript/test/computation_flow/class_declaration_fields_and_methods_visible_in_other_methods.js b/languages/tree-sitter-stack-graphs-javascript/test/computation_flow/class_declaration_fields_and_methods_visible_in_other_methods.js index 5ea5db72e..3bed1c415 100644 --- a/languages/tree-sitter-stack-graphs-javascript/test/computation_flow/class_declaration_fields_and_methods_visible_in_other_methods.js +++ b/languages/tree-sitter-stack-graphs-javascript/test/computation_flow/class_declaration_fields_and_methods_visible_in_other_methods.js @@ -8,4 +8,16 @@ class Foo { this.baz(); // ^ defined: 3 } -} \ No newline at end of file +} + +(class { + bar = 1; + baz() { } + quux() { + this.bar; + // ^ defined: 14 + + this.baz(); + // ^ defined: 15 + } +}); \ No newline at end of file From 30d216ffe01223e225bd3c9946f016b8f648500b Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Thu, 1 Jun 2023 08:52:47 +0200 Subject: [PATCH 106/291] Add initial Python language project --- .../.gitignore | 3 + .../CHANGELOG.md | 6 ++ .../Cargo.toml | 38 +++++++ .../tree-sitter-stack-graphs-python/LICENSE | 0 .../tree-sitter-stack-graphs-python/README.md | 98 +++++++++++++++++++ .../rust/bin.rs | 32 ++++++ .../rust/lib.rs | 48 +++++++++ .../rust/test.rs | 23 +++++ .../src/builtins.cfg | 1 + .../src/builtins.py | 0 .../src/stack-graphs.tsg | 42 ++++++++ .../test/test.py | 0 12 files changed, 291 insertions(+) create mode 100644 languages/tree-sitter-stack-graphs-python/.gitignore create mode 100644 languages/tree-sitter-stack-graphs-python/CHANGELOG.md create mode 100644 languages/tree-sitter-stack-graphs-python/Cargo.toml create mode 100644 languages/tree-sitter-stack-graphs-python/LICENSE create mode 100644 languages/tree-sitter-stack-graphs-python/README.md create mode 100644 languages/tree-sitter-stack-graphs-python/rust/bin.rs create mode 100644 languages/tree-sitter-stack-graphs-python/rust/lib.rs create mode 100644 languages/tree-sitter-stack-graphs-python/rust/test.rs create mode 100644 languages/tree-sitter-stack-graphs-python/src/builtins.cfg create mode 100644 languages/tree-sitter-stack-graphs-python/src/builtins.py create mode 100644 languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg create mode 100644 languages/tree-sitter-stack-graphs-python/test/test.py diff --git a/languages/tree-sitter-stack-graphs-python/.gitignore b/languages/tree-sitter-stack-graphs-python/.gitignore new file mode 100644 index 000000000..faf6459fb --- /dev/null +++ b/languages/tree-sitter-stack-graphs-python/.gitignore @@ -0,0 +1,3 @@ +*.html +/Cargo.lock +/target diff --git a/languages/tree-sitter-stack-graphs-python/CHANGELOG.md b/languages/tree-sitter-stack-graphs-python/CHANGELOG.md new file mode 100644 index 000000000..9e6604deb --- /dev/null +++ b/languages/tree-sitter-stack-graphs-python/CHANGELOG.md @@ -0,0 +1,6 @@ +# Changelog for tree-sitter-stack-graphs-python + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). diff --git a/languages/tree-sitter-stack-graphs-python/Cargo.toml b/languages/tree-sitter-stack-graphs-python/Cargo.toml new file mode 100644 index 000000000..9270879c0 --- /dev/null +++ b/languages/tree-sitter-stack-graphs-python/Cargo.toml @@ -0,0 +1,38 @@ +[package] +name = "tree-sitter-stack-graphs-python" +version = "0.1.0" +description = "Stack graphs definition for Python using tree-sitter-python" +readme = "README.md" +keywords = ["tree-sitter", "stack-graphs", "python"] +authors = [ + "GitHub ", +] +license = "MIT OR Apache-2.0" +edition = "2018" + +[[bin]] +name = "tree-sitter-stack-graphs-python" +path = "rust/bin.rs" +required-features = ["cli"] + +[lib] +path = "rust/lib.rs" +test = false + +[[test]] +name = "test" +path = "rust/test.rs" +harness = false + +[features] +cli = ["anyhow", "clap", "tree-sitter-stack-graphs/cli"] + +[dependencies] +anyhow = { version = "1.0", optional = true } +clap = { version = "4", optional = true, features = ["derive"] } +tree-sitter-stack-graphs = { version = "0.7", path = "../../tree-sitter-stack-graphs" } +tree-sitter-python = "0.20.2" + +[dev-dependencies] +anyhow = "1.0" +tree-sitter-stack-graphs = { version = "0.7", path = "../../tree-sitter-stack-graphs", features = ["cli"] } diff --git a/languages/tree-sitter-stack-graphs-python/LICENSE b/languages/tree-sitter-stack-graphs-python/LICENSE new file mode 100644 index 000000000..e69de29bb diff --git a/languages/tree-sitter-stack-graphs-python/README.md b/languages/tree-sitter-stack-graphs-python/README.md new file mode 100644 index 000000000..3bf115da0 --- /dev/null +++ b/languages/tree-sitter-stack-graphs-python/README.md @@ -0,0 +1,98 @@ +# tree-sitter-stack-graphs definition for Python + +This project defines tree-sitter-stack-graphs rules for Python using the [tree-sitter-python][] grammar. + +[tree-sitter-python]: https://crates.io/crates/tree-sitter-python + +## Usage + +To use this library, add the following to your `Cargo.toml`: + +``` toml +[dependencies] +tree-sitter-stack-graphs-python = "0.1.0" +``` + +Check out our [documentation](https://docs.rs/tree-sitter-stack-graphs-python/*/) for more details on how to use this library. + +## Command-line Program + +The command-line program for `tree-sitter-stack-graphs-python` lets you do stack graph based analysis and lookup from the command line. + +Install the program using `cargo install` as follows: + +``` sh +$ cargo install --features cli tree-sitter-stack-graphs-python +$ tree-sitter-stack-graphs-python --help +``` + +## Development + +The project is written in Rust, and requires a recent version installed. Rust can be installed and updated using [rustup][]. + +[rustup]: https://rustup.rs/ + +The project is organized as follows: + +- The stack graph rules are defined in `src/stack-graphs.tsg`. +- Builtins sources and configuration are defined in `src/builtins.py` and `builtins.cfg` respectively. +- Tests are put into the `test` directory. + +### Building and Running Tests + +Build the project by running: + +``` sh +$ cargo build +``` + +Run the tests as follows: + +``` sh +$ cargo test +``` + +The project consists of a library and a CLI. By default, running `cargo` only applies to the library. To run `cargo` commands on the CLI as well, add `--features cli` or `--all-features`. + +Run the CLI from source as follows: + +``` sh +$ cargo run --features cli -- ARGS +``` + +Sources are formatted using the standard Rust formatted, which is applied by running: + +``` sh +$ cargo fmt +``` + +### Writing TSG + +The stack graph rules are written in [tree-sitter-graph][]. Checkout the [examples][], +which contain self-contained TSG rules for specific language features. A VSCode +[extension][] is available that provides syntax highlighting for TSG files. + +[tree-sitter-graph]: https://github.com/tree-sitter/tree-sitter-graph +[examples]: https://github.com/github/stack-graphs/blob/main/tree-sitter-stack-graphs/examples/ +[extension]: https://marketplace.visualstudio.com/items?itemName=tree-sitter.tree-sitter-graph + +Parse and test a single file by executing the following commands: + +``` sh +$ cargo run --features cli -- parse FILES... +$ cargo run --features cli -- test TESTFILES... +``` + +Generate a visualization to debug failing tests by passing the `-V` flag: + +``` sh +$ cargo run --features cli -- test -V TESTFILES... +``` + +To generate the visualization regardless of test outcome, execute: + +``` sh +$ cargo run --features cli -- test -V --output-mode=always TESTFILES... +``` + +Go to https://crates.io/crates/tree-sitter-stack-graphs for links to examples and documentation. diff --git a/languages/tree-sitter-stack-graphs-python/rust/bin.rs b/languages/tree-sitter-stack-graphs-python/rust/bin.rs new file mode 100644 index 000000000..dc9a6772c --- /dev/null +++ b/languages/tree-sitter-stack-graphs-python/rust/bin.rs @@ -0,0 +1,32 @@ +// -*- coding: utf-8 -*- +// ------------------------------------------------------------------------------------------------ +// Copyright © 2023, stack-graphs authors. +// Licensed under either of Apache License, Version 2.0, or MIT license, at your option. +// Please see the LICENSE-APACHE or LICENSE-MIT files in this distribution for license details. +// ------------------------------------------------------------------------------------------------ + +use anyhow::anyhow; +use clap::Parser; +use tree_sitter_stack_graphs::cli::database::default_user_database_path_for_crate; +use tree_sitter_stack_graphs::cli::provided_languages::Subcommands; +use tree_sitter_stack_graphs::NoCancellation; + +fn main() -> anyhow::Result<()> { + let lc = match tree_sitter_stack_graphs_python::try_language_configuration(&NoCancellation) { + Ok(lc) => lc, + Err(err) => { + eprintln!("{}", err.display_pretty()); + return Err(anyhow!("Language configuration error")); + } + }; + let cli = Cli::parse(); + let default_db_path = default_user_database_path_for_crate(env!("CARGO_PKG_NAME"))?; + cli.subcommand.run(default_db_path, vec![lc]) +} + +#[derive(Parser)] +#[clap(about, version)] +pub struct Cli { + #[clap(subcommand)] + subcommand: Subcommands, +} diff --git a/languages/tree-sitter-stack-graphs-python/rust/lib.rs b/languages/tree-sitter-stack-graphs-python/rust/lib.rs new file mode 100644 index 000000000..74176a475 --- /dev/null +++ b/languages/tree-sitter-stack-graphs-python/rust/lib.rs @@ -0,0 +1,48 @@ +// -*- coding: utf-8 -*- +// ------------------------------------------------------------------------------------------------ +// Copyright © 2023, stack-graphs authors. +// Licensed under either of Apache License, Version 2.0, or MIT license, at your option. +// Please see the LICENSE-APACHE or LICENSE-MIT files in this distribution for license details. +// ------------------------------------------------------------------------------------------------ + +use tree_sitter_stack_graphs::loader::LanguageConfiguration; +use tree_sitter_stack_graphs::loader::LoadError; +use tree_sitter_stack_graphs::CancellationFlag; + +/// The stack graphs tsg source for this language. +pub const STACK_GRAPHS_TSG_PATH: &str = "src/stack-graphs.tsg"; +/// The stack graphs tsg source for this language. +pub const STACK_GRAPHS_TSG_SOURCE: &str = include_str!("../src/stack-graphs.tsg"); + +/// The stack graphs builtins configuration for this language. +pub const STACK_GRAPHS_BUILTINS_CONFIG: &str = include_str!("../src/builtins.cfg"); +/// The stack graphs builtins path for this language +pub const STACK_GRAPHS_BUILTINS_PATH: &str = "src/builtins.py"; +/// The stack graphs builtins source for this language. +pub const STACK_GRAPHS_BUILTINS_SOURCE: &str = include_str!("../src/builtins.py"); + +/// The name of the file path global variable. +pub const FILE_PATH_VAR: &str = "FILE_PATH"; + +pub fn language_configuration(cancellation_flag: &dyn CancellationFlag) -> LanguageConfiguration { + try_language_configuration(cancellation_flag).unwrap_or_else(|err| panic!("{}", err)) +} + +pub fn try_language_configuration( + cancellation_flag: &dyn CancellationFlag, +) -> Result { + LanguageConfiguration::from_sources( + tree_sitter_python::language(), + Some(String::from("source.py")), + None, + vec![String::from("py")], + STACK_GRAPHS_TSG_PATH.into(), + STACK_GRAPHS_TSG_SOURCE, + Some(( + STACK_GRAPHS_BUILTINS_PATH.into(), + STACK_GRAPHS_BUILTINS_SOURCE, + )), + Some(STACK_GRAPHS_BUILTINS_CONFIG), + cancellation_flag, + ) +} diff --git a/languages/tree-sitter-stack-graphs-python/rust/test.rs b/languages/tree-sitter-stack-graphs-python/rust/test.rs new file mode 100644 index 000000000..c81edf0d4 --- /dev/null +++ b/languages/tree-sitter-stack-graphs-python/rust/test.rs @@ -0,0 +1,23 @@ +// -*- coding: utf-8 -*- +// ------------------------------------------------------------------------------------------------ +// Copyright © 2023, stack-graphs authors. +// Licensed under either of Apache License, Version 2.0, or MIT license, at your option. +// Please see the LICENSE-APACHE or LICENSE-MIT files in this distribution for license details. +// ------------------------------------------------------------------------------------------------ + +use anyhow::anyhow; +use std::path::PathBuf; +use tree_sitter_stack_graphs::ci::Tester; +use tree_sitter_stack_graphs::NoCancellation; + +fn main() -> anyhow::Result<()> { + let lc = match tree_sitter_stack_graphs_python::try_language_configuration(&NoCancellation) { + Ok(lc) => lc, + Err(err) => { + eprintln!("{}", err.display_pretty()); + return Err(anyhow!("Language configuration error")); + } + }; + let test_path = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("test"); + Tester::new(vec![lc], vec![test_path]).run() +} diff --git a/languages/tree-sitter-stack-graphs-python/src/builtins.cfg b/languages/tree-sitter-stack-graphs-python/src/builtins.cfg new file mode 100644 index 000000000..d685061be --- /dev/null +++ b/languages/tree-sitter-stack-graphs-python/src/builtins.cfg @@ -0,0 +1 @@ +[globals] diff --git a/languages/tree-sitter-stack-graphs-python/src/builtins.py b/languages/tree-sitter-stack-graphs-python/src/builtins.py new file mode 100644 index 000000000..e69de29bb diff --git a/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg b/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg new file mode 100644 index 000000000..99647e75b --- /dev/null +++ b/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg @@ -0,0 +1,42 @@ +;; -*- coding: utf-8 -*- +;; ------------------------------------------------------------------------------------------------ +;; Copyright © 2023, stack-graphs authors. +;; Licensed under either of Apache License, Version 2.0, or MIT license, at your option. +;; Please see the LICENSE-APACHE or LICENSE-MIT files in this distribution for license details. +;; ------------------------------------------------------------------------------------------------ + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Stack graphs definition for Python +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;; Global Variables +;; ^^^^^^^^^^^^^^^^ + +global FILE_PATH +global ROOT_NODE +global JUMP_TO_SCOPE_NODE + +;; Attribute Shorthands +;; ^^^^^^^^^^^^^^^^^^^^ + +attribute node_definition = node => type = "pop_symbol", node_symbol = node, is_definition +attribute node_reference = node => type = "push_symbol", node_symbol = node, is_reference +attribute pop_node = node => type = "pop_symbol", node_symbol = node +attribute pop_scoped_node = node => type = "pop_scoped_symbol", node_symbol = node +attribute pop_scoped_symbol = symbol => type = "pop_scoped_symbol", symbol = symbol +attribute pop_symbol = symbol => type = "pop_symbol", symbol = symbol +attribute push_node = node => type = "push_symbol", node_symbol = node +attribute push_scoped_node = node => type = "push_scoped_symbol", node_symbol = node +attribute push_scoped_symbol = symbol => type = "push_scoped_symbol", symbol = symbol +attribute push_symbol = symbol => type = "push_symbol", symbol = symbol +attribute scoped_node_definition = node => type = "pop_scoped_symbol", node_symbol = node, is_definition +attribute scoped_node_reference = node => type = "push_scoped_symbol", node_symbol = node, is_reference +attribute symbol_definition = symbol => type = "pop_symbol", symbol = symbol, is_definition +attribute symbol_reference = symbol => type = "push_symbol", symbol = symbol, is_reference + +attribute node_symbol = node => symbol = (source-text node), source_node = node + +;; Stack Graph Rules +;; ^^^^^^^^^^^^^^^^^ + +; Have fun! diff --git a/languages/tree-sitter-stack-graphs-python/test/test.py b/languages/tree-sitter-stack-graphs-python/test/test.py new file mode 100644 index 000000000..e69de29bb From 75a3ec5939ead46a7d50c18ad8a936067402c4f6 Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Thu, 1 Jun 2023 09:37:26 +0200 Subject: [PATCH 107/291] Add legacy queries and tests for reference --- .../src/stack-graphs.scm | 779 ++++++++++++++++++ .../test/legacy.skip/aliased_imports.py | 31 + .../test/legacy.skip/attributes.py | 14 + .../test/legacy.skip/blocks.py | 21 + .../test/legacy.skip/chained_functions.py | 23 + .../test/legacy.skip/chained_methods.py | 27 + .../test/legacy.skip/class_members.py | 23 + .../test/legacy.skip/decorators.py | 10 + .../test/legacy.skip/exceptions.py | 5 + .../test/legacy.skip/functions.py | 31 + .../test/legacy.skip/imported_functions.py | 17 + .../test/legacy.skip/imports.py | 31 + .../test/legacy.skip/instance_members.py | 38 + .../test/legacy.skip/loops.py | 19 + .../test/legacy.skip/many_definitions.py | 76 ++ .../test/legacy.skip/pattern_matching.py | 39 + .../test/legacy.skip/redundant_reexport.py | 15 + .../test/legacy.skip/relative_imports.py | 60 ++ .../test/legacy.skip/statement_bindings.py | 9 + .../test/legacy.skip/superclasses.py | 19 + .../test/{ => legacy.skip}/test.py | 0 .../test/legacy.skip/tuples.py | 38 + .../test/legacy.skip/wildcard_import.py | 21 + 23 files changed, 1346 insertions(+) create mode 100644 languages/tree-sitter-stack-graphs-python/src/stack-graphs.scm create mode 100644 languages/tree-sitter-stack-graphs-python/test/legacy.skip/aliased_imports.py create mode 100644 languages/tree-sitter-stack-graphs-python/test/legacy.skip/attributes.py create mode 100644 languages/tree-sitter-stack-graphs-python/test/legacy.skip/blocks.py create mode 100644 languages/tree-sitter-stack-graphs-python/test/legacy.skip/chained_functions.py create mode 100644 languages/tree-sitter-stack-graphs-python/test/legacy.skip/chained_methods.py create mode 100644 languages/tree-sitter-stack-graphs-python/test/legacy.skip/class_members.py create mode 100644 languages/tree-sitter-stack-graphs-python/test/legacy.skip/decorators.py create mode 100644 languages/tree-sitter-stack-graphs-python/test/legacy.skip/exceptions.py create mode 100644 languages/tree-sitter-stack-graphs-python/test/legacy.skip/functions.py create mode 100644 languages/tree-sitter-stack-graphs-python/test/legacy.skip/imported_functions.py create mode 100644 languages/tree-sitter-stack-graphs-python/test/legacy.skip/imports.py create mode 100644 languages/tree-sitter-stack-graphs-python/test/legacy.skip/instance_members.py create mode 100644 languages/tree-sitter-stack-graphs-python/test/legacy.skip/loops.py create mode 100644 languages/tree-sitter-stack-graphs-python/test/legacy.skip/many_definitions.py create mode 100644 languages/tree-sitter-stack-graphs-python/test/legacy.skip/pattern_matching.py create mode 100644 languages/tree-sitter-stack-graphs-python/test/legacy.skip/redundant_reexport.py create mode 100644 languages/tree-sitter-stack-graphs-python/test/legacy.skip/relative_imports.py create mode 100644 languages/tree-sitter-stack-graphs-python/test/legacy.skip/statement_bindings.py create mode 100644 languages/tree-sitter-stack-graphs-python/test/legacy.skip/superclasses.py rename languages/tree-sitter-stack-graphs-python/test/{ => legacy.skip}/test.py (100%) create mode 100644 languages/tree-sitter-stack-graphs-python/test/legacy.skip/tuples.py create mode 100644 languages/tree-sitter-stack-graphs-python/test/legacy.skip/wildcard_import.py diff --git a/languages/tree-sitter-stack-graphs-python/src/stack-graphs.scm b/languages/tree-sitter-stack-graphs-python/src/stack-graphs.scm new file mode 100644 index 000000000..380af7d95 --- /dev/null +++ b/languages/tree-sitter-stack-graphs-python/src/stack-graphs.scm @@ -0,0 +1,779 @@ +;; -*- coding: utf-8 -*- +;; ------------------------------------------------------------------------------------------------ +;; Copyright © 2023, stack-graphs authors. +;; Licensed under either of Apache License, Version 2.0, or MIT license, at your option. +;; Please see the LICENSE-APACHE or LICENSE-MIT files in this distribution for license details. +;; ------------------------------------------------------------------------------------------------ + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; LEGACY DEFINITION! INCLUDED FOR REFERENCE ONLY! ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +; Modules and Imports +;--------------------- + +(module) @mod +{ + var module_def = @mod.file_def + attr module_def "no_span" + var module_ref = @mod.file_ref + attr module_ref "no_span" + var parent_module_def = module_def + var parent_module_ref = module_ref + var grandparent_module_ref = module_ref + + scan filepath { + "([^/]+)/" + { + edge module_def -> module_def.dot + edge module_def.dot -> module_def.next_def + edge module_ref.next_ref -> module_ref.dot + edge module_ref.dot -> module_ref + + attr module_def "pop" = $1 + attr module_def.dot "pop" = "." + attr module_ref "push" = $1 + attr module_ref.dot "push" = "." + + set grandparent_module_ref = parent_module_ref + set parent_module_def = module_def + set parent_module_ref = module_ref + set module_ref = module_ref.next_ref + set module_def = module_def.next_def + attr module_def "no_span" + attr module_ref "no_span" + } + + "__init__\\.py$" + { + attr parent_module_def "definition" + } + + "([^/]+)$" + { + edge module_def -> module_def.dot + edge module_def.dot -> module_def.next_def + + attr module_def "definition", "pop" = (replace $1 "\\.py" "") + attr module_def.dot "pop" = "." + + set module_def = module_def.next_def + attr module_def "no_span" + attr module_ref "no_span" + } + } + + edge root -> @mod.file_def + edge @mod.file_ref -> root + edge module_def -> @mod.after_scope + + edge @mod.before_scope -> @mod.global_dot + edge @mod.global -> root + attr @mod.global "push" = "" + + edge @mod.global_dot -> @mod.global + attr @mod.global_dot "push" = "." + + var @mod::parent_module = parent_module_ref + var @mod::grandparent_module = grandparent_module_ref + var @mod::bottom = @mod.after_scope + var @mod::global = @mod.global + var @mod::global_dot = @mod.global_dot +} + +(import_statement + name: (dotted_name + . (identifier) @root_name)) @stmt +{ + edge @stmt.after_scope -> @root_name.def, "precedence" = 1 + edge @root_name.ref -> root + attr @root_name.ref "push", "reference" + attr @root_name.def "pop", "definition" +} + +(import_statement + name: (aliased_import + (dotted_name . (identifier) @root_name))) @stmt +{ + edge @stmt.after_scope -> @root_name.def + edge @root_name.ref -> root + attr @root_name.ref "push", "reference" +} + +(import_from_statement + module_name: (dotted_name + . (identifier) @prefix_root_name)) @stmt +{ + edge @prefix_root_name.ref -> root + attr @prefix_root_name.ref "push", "reference" +} + +(import_from_statement + name: (dotted_name + . (identifier) @import_root_name)) @stmt +{ + edge @stmt.after_scope -> @import_root_name.def, "precedence" = 1 + edge @import_root_name.ref -> @import_root_name.ref_dot + attr @import_root_name.def "pop", "definition" + attr @import_root_name.ref "push", "reference" + attr @import_root_name.ref_dot "push" = "." +} + +(import_from_statement + name: (aliased_import + (dotted_name + . (identifier) @import_root_name))) @stmt +{ + edge @import_root_name.ref -> @import_root_name.ref_dot + attr @import_root_name.ref "push", "reference" + attr @import_root_name.ref_dot "push" = "." +} + +(import_from_statement + module_name: [ + (dotted_name (identifier) @prefix_leaf_name .) + (relative_import (dotted_name (identifier) @prefix_leaf_name .)) + (relative_import (import_prefix) @prefix_leaf_name .) + ] + name: [ + (dotted_name + . (identifier) @import_root_name) + (aliased_import + (dotted_name + . (identifier) @import_root_name)) + ]) +{ + edge @import_root_name.ref_dot -> @prefix_leaf_name.ref +} + +[ + (import_from_statement + (aliased_import + name: (dotted_name (identifier) @name .) + alias: (identifier) @alias)) + (import_statement + (aliased_import + name: (dotted_name (identifier) @name .) + alias: (identifier) @alias)) +] @stmt +{ + edge @stmt.after_scope -> @alias + edge @alias -> @name.ref + attr @alias "pop", "definition" +} + +[ + (import_statement + name: (dotted_name + (identifier) @leaf_name .)) + (import_from_statement + name: (dotted_name + (identifier) @leaf_name .)) +] +{ + attr @leaf_name.def "pop", "definition" + attr @leaf_name.ref "push", "reference" + edge @leaf_name.def -> @leaf_name.ref +} + +(relative_import + (import_prefix) @prefix + (#eq? @prefix ".")) @import +{ + edge @prefix.ref -> @import::parent_module +} + +(relative_import + (import_prefix) @prefix + (#eq? @prefix "..")) @import +{ + edge @prefix.ref -> @import::grandparent_module +} + +(relative_import + (import_prefix) @prefix + (dotted_name + . (identifier) @name)) +{ + attr @name.ref "push", "reference" + attr @name.ref_dot "push" = "." + edge @name.ref -> @name.ref_dot + edge @name.ref_dot -> @prefix.ref +} + +[ + (import_from_statement + module_name: (relative_import + (dotted_name + (identifier) @parent_name + . + (identifier) @child_name))) + (import_from_statement + module_name: (dotted_name + (identifier) @parent_name + . + (identifier) @child_name)) +] +{ + attr @child_name.ref "push", "reference" + attr @child_name.ref_dot "push" = "." + edge @child_name.ref -> @child_name.ref_dot + edge @child_name.ref_dot -> @parent_name.ref +} + +(import_from_statement + module_name: (dotted_name + . (identifier) @root_name)) +{ + attr @root_name.ref "push", "reference" + edge @root_name.ref -> root +} + +(import_from_statement + module_name: (dotted_name + (identifier) @leaf_name .) + (wildcard_import) @star) @stmt +{ + edge @stmt.after_scope -> @star.ref_dot, "precedence" = 1 + edge @star.ref_dot -> @leaf_name.ref + attr @star.ref_dot "push" = "." +} + +[ + (import_statement + name: (dotted_name + (identifier) @parent_name + . + (identifier) @child_name)) + (import_from_statement + name: (dotted_name + (identifier) @parent_name + . + (identifier) @child_name)) + (import_from_statement + name: (aliased_import + name: (dotted_name + (identifier) @parent_name + . + (identifier) @child_name))) +] +{ + edge @child_name.ref -> @child_name.ref_dot + edge @child_name.ref_dot -> @parent_name.ref + edge @parent_name.def -> @parent_name.def_dot + edge @parent_name.def_dot -> @child_name.def + attr @child_name.def "pop", "definition" + attr @child_name.ref "push","reference" + attr @parent_name.def_dot "pop" = "." + attr @child_name.ref_dot "push" = "." +} + +;-------- +; Scopes +;-------- + +[ + (module (_) @last_stmt .) + (block (_) @last_stmt .) +] @block +{ + edge @block.after_scope -> @last_stmt.after_scope +} + +[ + (module (_) @stmt1 . (_) @stmt2) + (block (_) @stmt1 . (_) @stmt2) +] +{ + edge @stmt2.before_scope -> @stmt1.after_scope +} + +[ + (module (_) @stmt) + (block (_) @stmt) +] +{ + edge @stmt.after_scope -> @stmt.before_scope + let @stmt::local_scope = @stmt.before_scope +} + +[ + (block . (_) @stmt) + (module . (_) @stmt) +] @block +{ + edge @stmt.before_scope -> @block.before_scope +} + +(block (_) @stmt . ) @block +{ + edge @block.after_scope -> @stmt.after_scope +} + +(function_definition (block) @block) +{ + edge @block.before_scope -> @block::local_scope +} + +[ + (while_statement (block) @block) + (if_statement (block) @block) + (with_statement (block) @block) + (try_statement (block) @block) + (for_statement (block) @block) + (_ [ + (else_clause (block) @block) + (elif_clause (block) @block) + (except_clause (block) @block) + (finally_clause (block) @block) + ]) +] @stmt +{ + edge @block.before_scope -> @block::local_scope + edge @stmt.after_scope -> @block.after_scope +} + +(match_statement (case_clause) @block) @stmt +{ + let @block::local_scope = @block.before_scope + edge @block.before_scope -> @stmt.before_scope + edge @stmt.after_scope -> @block.after_scope +} + +[ + (for_statement) + (while_statement) +] @stmt +{ + edge @stmt.before_scope -> @stmt.after_scope +} + +;------------- +; Definitions +;------------- + +[ + (assignment + left: (_) @pattern + right: (_) @value) + (with_item + value: + (as_pattern + (_) @value + alias: (as_pattern_target (_) @pattern))) +] +{ + edge @pattern.input -> @value.output +} + +(function_definition + name: (identifier) @name + parameters: (parameters) @params + body: (block) @body) @func +{ + attr @name "definiens" = @func + edge @func.after_scope -> @name + edge @name -> @func.call + edge @func.call -> @func.return_value + edge @body.before_scope -> @params.after_scope + edge @body.before_scope -> @func.drop_scope + edge @func.drop_scope -> @func::bottom + attr @func.drop_scope "drop" + attr @name "pop", "definition" + attr @func.call "pop" = "()", "pop-scope" + attr @params.before_scope "jump-to" + attr @func.return_value "endpoint" + let @func::function_returns = @func.return_value + + ; Prevent functions defined inside of method bodies from being treated like methods + let @body::class_self_scope = nil + let @body::class_member_attr_scope = nil +} + +;; +;; BEGIN BIG GNARLY DISJUNCTION +;; +;; The following pair of rules is intended to capture the following behavior: +;; +;; If a function definition is used to define a method, by being inside a class +;; definition, then we make its syntax type `method`. Otherwise, we make it's +;; syntax type `function`. Unfortunately, because of the limitations on negation +;; and binding in tree sitter queries, we cannot negate `class_definition` or +;; similar things directly. Instead, we have to manually push the negation down +;; to form the finite disjunction it corresponds to. +;; + +[ + (class_definition (block (decorated_definition (function_definition name: (_)@name)))) + (class_definition (block (function_definition name: (_)@name))) +] +{ + attr @name "syntax_type" = "method" +} + +[ + (module (decorated_definition (function_definition name: (_)@name))) + (module (function_definition name: (_)@name)) + + (if_statement (block (decorated_definition (function_definition name: (_)@name)))) + (if_statement (block (function_definition name: (_)@name))) + + (elif_clause (block (decorated_definition (function_definition name: (_)@name)))) + (elif_clause (block (function_definition name: (_)@name))) + + (else_clause (block (decorated_definition (function_definition name: (_)@name)))) + (else_clause (block (function_definition name: (_)@name))) + + (case_clause (block (decorated_definition (function_definition name: (_)@name)))) + (case_clause (block (function_definition name: (_)@name))) + + (for_statement (block (decorated_definition (function_definition name: (_)@name)))) + (for_statement (block (function_definition name: (_)@name))) + + (while_statement (block (decorated_definition (function_definition name: (_)@name)))) + (while_statement (block (function_definition name: (_)@name))) + + (try_statement (block (decorated_definition (function_definition name: (_)@name)))) + (try_statement (block (function_definition name: (_)@name))) + + (except_clause (block (decorated_definition (function_definition name: (_)@name)))) + (except_clause (block (function_definition name: (_)@name))) + + (finally_clause (block (decorated_definition (function_definition name: (_)@name)))) + (finally_clause (block (function_definition name: (_)@name))) + + (with_statement (block (decorated_definition (function_definition name: (_)@name)))) + (with_statement (block (function_definition name: (_)@name))) + + (function_definition (block (decorated_definition (function_definition name: (_)@name)))) + (function_definition (block (function_definition name: (_)@name))) +] +{ + attr @name "syntax_type" = "function" +} + +;; +;; END BIG GNARLY DISJUNCTION +;; + +(function_definition + parameters: (parameters + . (identifier) @param) + body: (block) @body) +{ + edge @param.input -> @param::class_self_scope + edge @param::class_member_attr_scope -> @param.output + edge @param.output -> @body.after_scope + attr @param.output "push" +} + +(parameter/identifier) @param +{ + attr @param.input "definition", "pop" + attr @param.param_name "push" + edge @param.input -> @param.param_index + edge @param.input -> @param.param_name +} + +[ + (parameter/default_parameter + name: (identifier) @name + value: (_) @value) @param + (parameter/typed_default_parameter + name: (_) @name + value: (_) @value) @param +] +{ + attr @name "definition", "pop" + attr @param.param_name "push" = @name + edge @name -> @param.param_name + edge @name -> @param.param_index + edge @param.input -> @name + edge @name -> @value.output +} + +[ + (parameter/typed_parameter + . (_) @name) @param + (parameter/list_splat_pattern + (_) @name) @param + (parameter/dictionary_splat_pattern + (_) @name) @param +] +{ + attr @name "definition", "pop" + attr @param.param_name "push" = @name + edge @name -> @param.param_name + edge @name -> @param.param_index + edge @param.input -> @name +} + +[ + (pattern_list (_) @pattern) + (tuple_pattern (_) @pattern) +] @list +{ + let statement_scope = @list::local_scope + let @pattern::local_scope = @pattern.pattern_before_scope + edge statement_scope -> @pattern::local_scope, "precedence" = (+ 1 (child-index @pattern)) + + edge @pattern.pattern_index -> @list.input + edge @pattern.input -> @pattern.pattern_index + attr @pattern.pattern_index "push" = (child-index @pattern) +} + +(parameters + (_) @param) @params +{ + attr @param.param_index "push" = (child-index @param) + edge @param.param_index -> @params.before_scope + edge @params.after_scope -> @param.input + edge @param.param_name -> @params.before_scope +} + +(return_statement (_) @expr) @stmt +{ + edge @stmt::function_returns -> @expr.output +} + +(class_definition + name: (identifier) @name) @class +{ + attr @name "definiens" = @class + attr @name "syntax_type" = "class" + edge @class.parent_scope -> @class::class_parent_scope + edge @class.parent_scope -> @class::local_scope + edge @class.after_scope -> @name + edge @name -> @class.call + edge @name -> @class.dot + edge @class.dot -> @class.members + edge @class.call -> @class.call_drop + edge @class.call_drop -> @class.self_scope + edge @class.self_scope -> @class.super_scope + edge @class.self_scope -> @class.self_dot + edge @class.self_dot -> @class.members + edge @class.members -> @class.member_attrs + attr @class.call "pop" = "()", "pop-scope" + attr @class.call_drop "drop" + attr @class.dot "pop" = "." + attr @class.self_dot "pop" = "." + attr @name "pop", "definition" + attr @class.member_attrs "push" = "." + attr @class.self_scope "endpoint" + let @class::super_scope = @class.super_scope + let @class::class_parent_scope = @class.parent_scope + let @class::class_self_scope = @class.call_drop + let @class::class_member_attr_scope = @class.member_attrs +} + +(class_definition + body: (block + (_) @last_stmt .) @body) @class +{ + edge @class.members -> @last_stmt.after_scope +} + +(class_definition + superclasses: (argument_list + (_) @superclass)) @class +{ + edge @class.super_scope -> @superclass.output +} + +(decorated_definition + definition: (_) @def) @stmt +{ + edge @def.before_scope -> @stmt.before_scope + edge @stmt.after_scope -> @def.after_scope +} + +(case_clause + pattern: (_) @pattern + consequence: (_) @consequence) @clause +{ + edge @consequence.before_scope -> @pattern.new_bindings + edge @consequence.before_scope -> @clause.before_scope + edge @clause.after_scope -> @consequence.after_scope +} + +;------------- +; Expressions +;------------- + +(call + function: (_) @fn + arguments: (argument_list) @args) @call +{ + edge @call.output -> @call.output_args + edge @call.output_args -> @fn.output + attr @call.output_args "push" = "()", "push-scope" = @args +} + +(call + function: (attribute + object: (_) @receiver) + arguments: (argument_list + (expression) @arg) @args) +{ + edge @args -> @arg.arg_index + edge @receiver -> @receiver.arg_index + + attr @receiver.arg_index "pop" = "0" + edge @receiver.arg_index -> @receiver.output + + attr @arg.arg_index "pop" = (+ 1 (child-index @arg)) + edge @arg.arg_index -> @arg.output +} + +(call + arguments: (argument_list + (keyword_argument + name: (identifier) @name + value: (_) @val) @arg) @args) @call +{ + edge @args -> @arg.arg_name + attr @arg.arg_name "pop" = @name + edge @arg.arg_name -> @val.output +} + +(argument_list + (expression) @arg) @args +{ + edge @args -> @arg.arg_index + attr @arg.arg_index "pop" = (child-index @arg) + edge @arg.arg_index -> @arg.output +} + +( + (call + function: (identifier) @fn-name) @call + (#eq? @fn-name "super") +) +{ + edge @call.output -> @call::super_scope +} + +[ + (tuple (_) @element) + (expression_list (_) @element) +] @tuple +{ + edge @tuple.output -> @element.el_index + attr @element.el_index "pop" = (child-index @element) + edge @element.el_index -> @element.output + + edge @tuple.new_bindings -> @element.new_bindings +} + +(attribute + object: (_) @object + attribute: (identifier) @name) @expr +{ + edge @expr.output -> @name.output + edge @name.output -> @expr.output_dot + edge @expr.output_dot -> @object.output + edge @object.input -> @expr.input_dot + edge @expr.input_dot -> @name.input + edge @name.input -> @expr.input + attr @expr.output_dot "push" = "." + attr @expr.input_dot "pop" = "." + attr @name.input "pop" + attr @name.output "push" +} + +(pattern/attribute + attribute: (identifier) @name) +{ + attr @name.input "definition" +} + +(primary_expression/attribute + attribute: (identifier) @name) +{ + attr @name.output "reference" +} + +(primary_expression/identifier) @id +{ + edge @id.output -> @id::local_scope + edge @id.output -> @id::class_parent_scope + edge @id::local_scope -> @id.input + attr @id.input "pop" + attr @id.output "push", "reference" + + attr @id.new_binding_pop "pop", "definition" + edge @id.new_bindings -> @id.new_binding_pop +} + +(pattern/identifier) @id +{ + edge @id.output -> @id::local_scope + edge @id.output -> @id::class_parent_scope + edge @id::local_scope -> @id.input, "precedence" = 1 + attr @id.input "pop", "definition" + attr @id.output "push" + + attr @id.new_binding_pop "pop", "definition" + edge @id.new_bindings -> @id.new_binding_pop +} + +(as_pattern + (expression) @value + alias: (as_pattern_target (primary_expression/identifier) @id)) @as_pattern +{ + edge @id.output -> @id::local_scope + edge @id.output -> @id::class_parent_scope + edge @id::local_scope -> @id.input, "precedence" = 1 + attr @id.input "pop", "definition" + attr @id.output "push" + + edge @as_pattern.new_bindings -> @value.new_bindings + edge @as_pattern.new_bindings -> @id.new_bindings +} + +(list) @list +{ + edge @list.output -> @list.called + edge @list.called -> @list::global_dot + attr @list.called "push" = "list" +} + +(list (_) @el) @list +{ + edge @list.new_bindings -> @el.new_bindings +} + +(dictionary (pair) @pair) @dict +{ + edge @dict.new_bindings -> @pair.new_bindings +} + +(pair + value: (_) @value) @pair +{ + edge @pair.new_bindings -> @value.new_bindings +} + +(set (_) @el) @set +{ + edge @set.new_bindings -> @el.new_bindings +} + +(list_splat (_) @splatted) @splat +{ +attr @splat.new_bindings_pop "pop" = @splatted, "definition" +edge @splat.new_bindings -> @splat.new_bindings_pop +} + +(binary_operator + (_) @left + (_) @right) @binop +{ + edge @binop.new_bindings -> @left.new_bindings + edge @binop.new_bindings -> @right.new_bindings +} + +(case_pattern (_) @expr) @pat +{ + edge @pat.new_bindings -> @expr.new_bindings +} diff --git a/languages/tree-sitter-stack-graphs-python/test/legacy.skip/aliased_imports.py b/languages/tree-sitter-stack-graphs-python/test/legacy.skip/aliased_imports.py new file mode 100644 index 000000000..d988757a5 --- /dev/null +++ b/languages/tree-sitter-stack-graphs-python/test/legacy.skip/aliased_imports.py @@ -0,0 +1,31 @@ +#------ path: foo.py ------# + +# module +class A: + a = 1 + +class B: + class C: + class D: + d = 2 + +#------ path: main.py ---# + +from foo import A as X, B.C.D as Y +import foo as f + +print X.a, Y.d +# ^ defined: 5 +# ^ defined: 10 + +print A, B.C +# ^ defined: +# ^ defined: +# ^ defined: + +print f.B +# ^ defined: 2, 15 +# ^ defined: 7 + +print foo +# ^ defined: diff --git a/languages/tree-sitter-stack-graphs-python/test/legacy.skip/attributes.py b/languages/tree-sitter-stack-graphs-python/test/legacy.skip/attributes.py new file mode 100644 index 000000000..6dd3d0e48 --- /dev/null +++ b/languages/tree-sitter-stack-graphs-python/test/legacy.skip/attributes.py @@ -0,0 +1,14 @@ +a = 1 + +a.b = 2 + +a.c.d = 5 + +print a.b +# ^ defined: 1 +# ^ defined: 3 + +print a.c, a.c.d +# ^ defined: 1 +# ^ defined: +# ^ defined: 5 diff --git a/languages/tree-sitter-stack-graphs-python/test/legacy.skip/blocks.py b/languages/tree-sitter-stack-graphs-python/test/legacy.skip/blocks.py new file mode 100644 index 000000000..462890baf --- /dev/null +++ b/languages/tree-sitter-stack-graphs-python/test/legacy.skip/blocks.py @@ -0,0 +1,21 @@ +def f(): + if a: + b = 1 + else: + c = 2 + + print b, c + # ^ defined: 3 + # ^ defined: 5 + +class G: + if d: + e = 1 + + print e + # ^ defined: 13 + +print b, c, e +# ^ defined: +# ^ defined: +# ^ defined: diff --git a/languages/tree-sitter-stack-graphs-python/test/legacy.skip/chained_functions.py b/languages/tree-sitter-stack-graphs-python/test/legacy.skip/chained_functions.py new file mode 100644 index 000000000..ef1dac21d --- /dev/null +++ b/languages/tree-sitter-stack-graphs-python/test/legacy.skip/chained_functions.py @@ -0,0 +1,23 @@ +class A: + w = 1 + x = 2 + y = 3 + z = 4 + +def get_a(): + return A +def get_b(): + return get_a() +def get_c(): + return get_b() +def get_d(): + return get_c() +def get_e(): + return get_d() +def get_f(): + return get_e() + +g = get_f(A) +print g.x, g.y +# ^ defined: 3 +# ^ defined: 4 diff --git a/languages/tree-sitter-stack-graphs-python/test/legacy.skip/chained_methods.py b/languages/tree-sitter-stack-graphs-python/test/legacy.skip/chained_methods.py new file mode 100644 index 000000000..9c4f53115 --- /dev/null +++ b/languages/tree-sitter-stack-graphs-python/test/legacy.skip/chained_methods.py @@ -0,0 +1,27 @@ +class Builder: + def set_a(self, a): + self.a = a + return self + + def set_b(self, b): + self.b = b + return self + + def set_c(self, c): + self.c = c + return self + + def set_d(self, d): + self.d = d + return self + + def set_e(self, e): + self.d = d + return self + +Builder().set_a('a1').set_b('b2').set_c('c3').set_d('d4').set_e('e4') +# ^ defined: 2 +# ^ defined: 6 +# ^ defined: 10 +# ^ defined: 14 +# ^ defined: 18 diff --git a/languages/tree-sitter-stack-graphs-python/test/legacy.skip/class_members.py b/languages/tree-sitter-stack-graphs-python/test/legacy.skip/class_members.py new file mode 100644 index 000000000..c67ace992 --- /dev/null +++ b/languages/tree-sitter-stack-graphs-python/test/legacy.skip/class_members.py @@ -0,0 +1,23 @@ +a = 1 + +class B: + c = a + # ^ defined: 1 + + def d(self): + return self.c + + class E: + f = a + # ^ defined: 1 + +print B.c +# ^ defined: 3 +# ^ defined: 1, 4 + +print B.d(1) +# ^ defined: 7 + +print B.a, E.a +# ^ defined: +# ^ defined: diff --git a/languages/tree-sitter-stack-graphs-python/test/legacy.skip/decorators.py b/languages/tree-sitter-stack-graphs-python/test/legacy.skip/decorators.py new file mode 100644 index 000000000..b55f44b2f --- /dev/null +++ b/languages/tree-sitter-stack-graphs-python/test/legacy.skip/decorators.py @@ -0,0 +1,10 @@ +from a import deprecated +from b import ignore_warnings + +class A: + @deprecated + # ^ defined: 1 + @ignore_warnings.all + # ^ defined: 2 + def b(self): + pass diff --git a/languages/tree-sitter-stack-graphs-python/test/legacy.skip/exceptions.py b/languages/tree-sitter-stack-graphs-python/test/legacy.skip/exceptions.py new file mode 100644 index 000000000..b6b2e57e1 --- /dev/null +++ b/languages/tree-sitter-stack-graphs-python/test/legacy.skip/exceptions.py @@ -0,0 +1,5 @@ +try: + print() +except Exception as e: + x = e + # ^ defined: 3 diff --git a/languages/tree-sitter-stack-graphs-python/test/legacy.skip/functions.py b/languages/tree-sitter-stack-graphs-python/test/legacy.skip/functions.py new file mode 100644 index 000000000..31a1d6782 --- /dev/null +++ b/languages/tree-sitter-stack-graphs-python/test/legacy.skip/functions.py @@ -0,0 +1,31 @@ +import a.x.y +import b.x.y + +def get_x(value): + return value.x + # ^ defined: 4 + +print get_x(a).y +# ^ defined: 1 + +print get_x(b).y +# ^ defined: 2 + +def get_a(): + return a + +print get_a(b).x +# ^ defined: 1 + +print get_x(foo=1, value=a).y +# ^ defined: 1 + +def foo(w: int, x, y=1, z: int=4, *args, **dict): + local = x +# ^ defined: 23 + print(args, w, z) +# ^ defined: 23 +# ^ defined: 23 +# ^ defined: 23 + return y +# ^ defined: 23 diff --git a/languages/tree-sitter-stack-graphs-python/test/legacy.skip/imported_functions.py b/languages/tree-sitter-stack-graphs-python/test/legacy.skip/imported_functions.py new file mode 100644 index 000000000..495697740 --- /dev/null +++ b/languages/tree-sitter-stack-graphs-python/test/legacy.skip/imported_functions.py @@ -0,0 +1,17 @@ +#------ path: a.py ------# + +def foo(x): + return x + +#------ path: b.py ------# + +class A: + bar = 1 + +#------ path: main.py ---------# + +from a import * +from b import * + +foo(A).bar +# ^ defined: 9 diff --git a/languages/tree-sitter-stack-graphs-python/test/legacy.skip/imports.py b/languages/tree-sitter-stack-graphs-python/test/legacy.skip/imports.py new file mode 100644 index 000000000..2f21795da --- /dev/null +++ b/languages/tree-sitter-stack-graphs-python/test/legacy.skip/imports.py @@ -0,0 +1,31 @@ +#------ path: one/two.py -----------# + +# module +import a.b.c + +d = 1 + +e = a.b + +#------ path: three/__init__.py ---# + +# module +f = 3 + +#------ path: main.py -------------# + +from one.two import d, e.c +# ^ defined: 2 +# ^ defined: 6, 17 +# ^ defined: 4, 8, 17 + +import three +# ^ defined: 11, 22 + +print(d, e.c) +# ^ defined: 6, 17 +# ^ defined: 4, 17 + +print three.f +# ^ defined: 11, 22 +# ^ defined: 13 diff --git a/languages/tree-sitter-stack-graphs-python/test/legacy.skip/instance_members.py b/languages/tree-sitter-stack-graphs-python/test/legacy.skip/instance_members.py new file mode 100644 index 000000000..38a6b6d75 --- /dev/null +++ b/languages/tree-sitter-stack-graphs-python/test/legacy.skip/instance_members.py @@ -0,0 +1,38 @@ +#---- path: a.py ------- + +class A: + def __init__(self, b, c): + self.b = b + self.c = A(c, 1) + + def get_b(self): + return self.b + # ^ defined: 4, 5 + + def get_c(self): + return self.c + # ^ defined: 6 + + def get_all(self): + return [self.get_b(), self.get_c()] + # ^ defined: 8 + # ^ defined: 12 + +a = A(1, 2) +a.get_all() +# ^ defined: 16 + +a.b +# ^ defined: 4, 5 + +a.c.b +# ^ defined: 4, 5 +# ^ defined: 6 + +#----- path: main.py --------- + +import a + +print a.A, a.a +# ^ defined: 3 +# ^ defined: 21 diff --git a/languages/tree-sitter-stack-graphs-python/test/legacy.skip/loops.py b/languages/tree-sitter-stack-graphs-python/test/legacy.skip/loops.py new file mode 100644 index 000000000..2f0663295 --- /dev/null +++ b/languages/tree-sitter-stack-graphs-python/test/legacy.skip/loops.py @@ -0,0 +1,19 @@ +class Node3: + value = 3 +class Node2: + value = 2 + next = Node3 +class Node1: + value = 1 + next = Node2 + +def linked_list_search(l, item): + node = l + while node: + if node.value == item: + return node + # ^ defined: 10, 11, 16 + node = node.next + +linked_list_search(Node1, 5).value +# ^ defined: 6 diff --git a/languages/tree-sitter-stack-graphs-python/test/legacy.skip/many_definitions.py b/languages/tree-sitter-stack-graphs-python/test/legacy.skip/many_definitions.py new file mode 100644 index 000000000..f85f3a8ec --- /dev/null +++ b/languages/tree-sitter-stack-graphs-python/test/legacy.skip/many_definitions.py @@ -0,0 +1,76 @@ +#--- path: a.py ---# + +def f0(a): return X.a + X.b +def f1(a): return f0(1) +def f2(a): return f1(2) +def f3(a): return f2(3) +def f4(a): return f3(4) +def f5(a): return f4(5) +def f6(a): return f5(6) +def f7(a): return f6(7) +def f8(a): return f7(8) +def f9(a): return f8(9) + +class C1: + def m0(self, b): return f9(0) + def m1(self, b): return self.m0(1) + def m2(self, b): return self.m1(2) + def m3(self, b): return self.m2(3) + def m4(self, b): return self.m3(4) + def m5(self, b): return self.m4(5) + def m6(self, b): return self.m5(6) + def m7(self, b): return self.m6(7) + def m8(self, b): return self.m7(8) + +def f10(): return C1.m8(0) +def f11(): return f10(1) +def f12(): return X.c(2) +def f13(): return X.c(3) +def f14(): return x(4) +def f15(): return x(5) +def f16(): return x(6) +def f17(): return x(7) +def f18(): return x(8) + +class C2: + def m0(self): return X.d(0) + def m1(self): return X.d(1) + def m2(self): return X.d(2) + def m3(self): return X.d(3) + def m4(self): return X.d(4) + def m5(self): return X.d(5) + def m6(self): return X.d(6) + def m7(self): return X.d(7) + def m8(self): return X.d(8) + +#--- path: main.py ---# + +from a import * + +print f0(), f4(), f8() +# ^ defined: 3 +# ^ defined: 7 +# ^ defined: 11 + +print C1.m0, C1().m0(), C1.m4, C1().m4, C1.m8, C1.m8 +# ^ defined: 14 +# ^ defined: 15 +# ^ defined: 15 +# ^ defined: 19 +# ^ defined: 19 +# ^ defined: 23 +# ^ defined: 23 + +print f10(), f14(), f18() +# ^ defined: 25 +# ^ defined: 29 +# ^ defined: 33 + +print C2.m0, C2().m0(), C2.m4, C2().m4, C2.m8, C2.m8 +# ^ defined: 35 +# ^ defined: 36 +# ^ defined: 36 +# ^ defined: 40 +# ^ defined: 40 +# ^ defined: 44 +# ^ defined: 44 diff --git a/languages/tree-sitter-stack-graphs-python/test/legacy.skip/pattern_matching.py b/languages/tree-sitter-stack-graphs-python/test/legacy.skip/pattern_matching.py new file mode 100644 index 000000000..58c0d16fc --- /dev/null +++ b/languages/tree-sitter-stack-graphs-python/test/legacy.skip/pattern_matching.py @@ -0,0 +1,39 @@ +command = 1 +current_room = 2 +character = 3 + +match command.split(): + # ^ defined: 1 + case ["quit"]: + print("Goodbye!") + quit_game() + case ["look"]: + current_room.describe() + # ^ defined: 2 + case ["get", obj]: + character.get(obj, current_room) + # ^ defined: 3 + # ^ defined: 13 + # ^ defined: 2 + case ["go", direction]: + current_room = current_room.neighbor(direction) + # ^ defined: 18 + case { "foo": foo }: + print(foo) + # ^ defined: 21 + case {bar,quux}: + print(bar,quux) + # ^ defined: 24 + # ^ defined: 24 + case ["grab", { "key": {garply}}]: + print(garply) + # ^ defined: 28 + case ["drop", *objs]: + print(objs) + # ^ defined: 31 + case ["get", obj] | ["pick", "up", obj] | ["pick", obj, "up"]: + print(obj) + # ^ defined: 34, 34, 34 + case ["go", ("north" | "south" | "east" | "west") as direction2]: + current_room = current_room.neighbor(direction2) + # ^ defined: 37 diff --git a/languages/tree-sitter-stack-graphs-python/test/legacy.skip/redundant_reexport.py b/languages/tree-sitter-stack-graphs-python/test/legacy.skip/redundant_reexport.py new file mode 100644 index 000000000..53afca29e --- /dev/null +++ b/languages/tree-sitter-stack-graphs-python/test/legacy.skip/redundant_reexport.py @@ -0,0 +1,15 @@ +#--- path: a/__init__.py ---# + +from . import child + +#--- path: a/child.py ---- + +def f(): + pass + +#--- path: main.py ---# + +import a + +print a.child.f() +# ^ defined: 7 diff --git a/languages/tree-sitter-stack-graphs-python/test/legacy.skip/relative_imports.py b/languages/tree-sitter-stack-graphs-python/test/legacy.skip/relative_imports.py new file mode 100644 index 000000000..2d6e14eb4 --- /dev/null +++ b/languages/tree-sitter-stack-graphs-python/test/legacy.skip/relative_imports.py @@ -0,0 +1,60 @@ +#------ path: foo/bar/main.py ------# + +from . import a +from .b import B +from ..c import see +from ..c.d import D + +print a.A +# ^ defined: 3, 24 +# ^ defined: 26 + +print B.bee +# ^ defined: 4, 31 +# ^ defined: 32 + +print see() +# ^ defined: 5, 37 + + +print D.d +# ^ defined: 44 + +#------ path: foo/bar/a.py --------# + +# module +A = "a" + +#------ path: foo/bar/b.py --------# + +# module +class B: + bee = 1 + +#------ path: foo/c.py ------------# + +# module +def see(): + pass + +#------ path: foo/c/d.py ---------# + +# module +class D: + d = "d" + +#------ path: foo/e/g.py ---# + +# module +G = 1 + +#------ path: foo/e/__init__.py ---# + +# module +from .g import G +# ^ defined: 49, 54 + +from ..c import see +# ^ defined: 37, 57 + +E = 1 diff --git a/languages/tree-sitter-stack-graphs-python/test/legacy.skip/statement_bindings.py b/languages/tree-sitter-stack-graphs-python/test/legacy.skip/statement_bindings.py new file mode 100644 index 000000000..6e3a13abb --- /dev/null +++ b/languages/tree-sitter-stack-graphs-python/test/legacy.skip/statement_bindings.py @@ -0,0 +1,9 @@ +import a.b +import c.d + +with a as x, c as y: + print x.b, y.d + # ^ defined: 1, 4 + # ^ defined: 1 + # ^ defined: 2, 4 + # ^ defined: 2 diff --git a/languages/tree-sitter-stack-graphs-python/test/legacy.skip/superclasses.py b/languages/tree-sitter-stack-graphs-python/test/legacy.skip/superclasses.py new file mode 100644 index 000000000..849a192a6 --- /dev/null +++ b/languages/tree-sitter-stack-graphs-python/test/legacy.skip/superclasses.py @@ -0,0 +1,19 @@ +class A: + def __init__(self): + self.some_attr = 2 + + def some_method(self): + print self + +class B(A): + def method2(self): + print self.some_attr, self.some_method() + # ^ defined: 3 + # ^ defined: 5, 14 + + def some_method(self): + pass + + def other(self): + super().some_method() + # ^ defined: 5 diff --git a/languages/tree-sitter-stack-graphs-python/test/test.py b/languages/tree-sitter-stack-graphs-python/test/legacy.skip/test.py similarity index 100% rename from languages/tree-sitter-stack-graphs-python/test/test.py rename to languages/tree-sitter-stack-graphs-python/test/legacy.skip/test.py diff --git a/languages/tree-sitter-stack-graphs-python/test/legacy.skip/tuples.py b/languages/tree-sitter-stack-graphs-python/test/legacy.skip/tuples.py new file mode 100644 index 000000000..1ea6ee3ef --- /dev/null +++ b/languages/tree-sitter-stack-graphs-python/test/legacy.skip/tuples.py @@ -0,0 +1,38 @@ +class a: + x = 1 +class b: + x = 1 +class c: + x = 1 + +(a1, b1) = (a, b) +a2, b2 = (a, b) +(a3, b3) = (a, b) +a4, b4 = a, b + +print a1.x, b1.x +# ^ defined: 2 +# ^ defined: 4 +print a2.x, b2.x +# ^ defined: 2 +# ^ defined: 4 +print a3.x, b3.x +# ^ defined: 2 +# ^ defined: 4 +print a4.x, b4.x +# ^ defined: 2 +# ^ defined: 4 + +t = (a, b), c +(a5, b5), c5 = t + +print a5.x, b5.x, c5.x +# ^ defined: 2 +# ^ defined: 4 +# ^ defined: 6 + +(a6, (b6, c6)) = (a, (b, c)) + +print a6.x, b6.x +# ^ defined: 2 +# ^ defined: 4 diff --git a/languages/tree-sitter-stack-graphs-python/test/legacy.skip/wildcard_import.py b/languages/tree-sitter-stack-graphs-python/test/legacy.skip/wildcard_import.py new file mode 100644 index 000000000..f2ac040f2 --- /dev/null +++ b/languages/tree-sitter-stack-graphs-python/test/legacy.skip/wildcard_import.py @@ -0,0 +1,21 @@ +#------ path: one/two.py ------# + +a = 1 +b = 2 + +#------ path: one/three.py ------# + +b = 3 +c = 4 + +#------ path: main.py ---------# + +from one.two import * +from one.three import * + +print a +# ^ defined: 3 +print b +# ^ defined: 8 +print c +# ^ defined: 9 From decf584c173adcfc038b3f8c398c0025715832bf Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Thu, 1 Jun 2023 09:42:41 +0200 Subject: [PATCH 108/291] convert: copy old --- .../src/stack-graphs.tsg | 770 +++++++++++++++++- 1 file changed, 767 insertions(+), 3 deletions(-) diff --git a/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg b/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg index 99647e75b..99fd0acaf 100644 --- a/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg +++ b/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg @@ -36,7 +36,771 @@ attribute symbol_reference = symbol => type = "push_symbol", symbol = symbol attribute node_symbol = node => symbol = (source-text node), source_node = node -;; Stack Graph Rules -;; ^^^^^^^^^^^^^^^^^ +; Modules and Imports +;--------------------- -; Have fun! +(module) @mod +{ + var module_def = @mod.file_def + attr module_def "no_span" + var module_ref = @mod.file_ref + attr module_ref "no_span" + var parent_module_def = module_def + var parent_module_ref = module_ref + var grandparent_module_ref = module_ref + + scan filepath { + "([^/]+)/" + { + edge module_def -> module_def.dot + edge module_def.dot -> module_def.next_def + edge module_ref.next_ref -> module_ref.dot + edge module_ref.dot -> module_ref + + attr module_def "pop" = $1 + attr module_def.dot "pop" = "." + attr module_ref "push" = $1 + attr module_ref.dot "push" = "." + + set grandparent_module_ref = parent_module_ref + set parent_module_def = module_def + set parent_module_ref = module_ref + set module_ref = module_ref.next_ref + set module_def = module_def.next_def + attr module_def "no_span" + attr module_ref "no_span" + } + + "__init__\\.py$" + { + attr parent_module_def "definition" + } + + "([^/]+)$" + { + edge module_def -> module_def.dot + edge module_def.dot -> module_def.next_def + + attr module_def "definition", "pop" = (replace $1 "\\.py" "") + attr module_def.dot "pop" = "." + + set module_def = module_def.next_def + attr module_def "no_span" + attr module_ref "no_span" + } + } + + edge root -> @mod.file_def + edge @mod.file_ref -> root + edge module_def -> @mod.after_scope + + edge @mod.before_scope -> @mod.global_dot + edge @mod.global -> root + attr @mod.global "push" = "" + + edge @mod.global_dot -> @mod.global + attr @mod.global_dot "push" = "." + + var @mod::parent_module = parent_module_ref + var @mod::grandparent_module = grandparent_module_ref + var @mod::bottom = @mod.after_scope + var @mod::global = @mod.global + var @mod::global_dot = @mod.global_dot +} + +(import_statement + name: (dotted_name + . (identifier) @root_name)) @stmt +{ + edge @stmt.after_scope -> @root_name.def, "precedence" = 1 + edge @root_name.ref -> root + attr @root_name.ref "push", "reference" + attr @root_name.def "pop", "definition" +} + +(import_statement + name: (aliased_import + (dotted_name . (identifier) @root_name))) @stmt +{ + edge @stmt.after_scope -> @root_name.def + edge @root_name.ref -> root + attr @root_name.ref "push", "reference" +} + +(import_from_statement + module_name: (dotted_name + . (identifier) @prefix_root_name)) @stmt +{ + edge @prefix_root_name.ref -> root + attr @prefix_root_name.ref "push", "reference" +} + +(import_from_statement + name: (dotted_name + . (identifier) @import_root_name)) @stmt +{ + edge @stmt.after_scope -> @import_root_name.def, "precedence" = 1 + edge @import_root_name.ref -> @import_root_name.ref_dot + attr @import_root_name.def "pop", "definition" + attr @import_root_name.ref "push", "reference" + attr @import_root_name.ref_dot "push" = "." +} + +(import_from_statement + name: (aliased_import + (dotted_name + . (identifier) @import_root_name))) @stmt +{ + edge @import_root_name.ref -> @import_root_name.ref_dot + attr @import_root_name.ref "push", "reference" + attr @import_root_name.ref_dot "push" = "." +} + +(import_from_statement + module_name: [ + (dotted_name (identifier) @prefix_leaf_name .) + (relative_import (dotted_name (identifier) @prefix_leaf_name .)) + (relative_import (import_prefix) @prefix_leaf_name .) + ] + name: [ + (dotted_name + . (identifier) @import_root_name) + (aliased_import + (dotted_name + . (identifier) @import_root_name)) + ]) +{ + edge @import_root_name.ref_dot -> @prefix_leaf_name.ref +} + +[ + (import_from_statement + (aliased_import + name: (dotted_name (identifier) @name .) + alias: (identifier) @alias)) + (import_statement + (aliased_import + name: (dotted_name (identifier) @name .) + alias: (identifier) @alias)) +] @stmt +{ + edge @stmt.after_scope -> @alias + edge @alias -> @name.ref + attr @alias "pop", "definition" +} + +[ + (import_statement + name: (dotted_name + (identifier) @leaf_name .)) + (import_from_statement + name: (dotted_name + (identifier) @leaf_name .)) +] +{ + attr @leaf_name.def "pop", "definition" + attr @leaf_name.ref "push", "reference" + edge @leaf_name.def -> @leaf_name.ref +} + +(relative_import + (import_prefix) @prefix + (#eq? @prefix ".")) @import +{ + edge @prefix.ref -> @import::parent_module +} + +(relative_import + (import_prefix) @prefix + (#eq? @prefix "..")) @import +{ + edge @prefix.ref -> @import::grandparent_module +} + +(relative_import + (import_prefix) @prefix + (dotted_name + . (identifier) @name)) +{ + attr @name.ref "push", "reference" + attr @name.ref_dot "push" = "." + edge @name.ref -> @name.ref_dot + edge @name.ref_dot -> @prefix.ref +} + +[ + (import_from_statement + module_name: (relative_import + (dotted_name + (identifier) @parent_name + . + (identifier) @child_name))) + (import_from_statement + module_name: (dotted_name + (identifier) @parent_name + . + (identifier) @child_name)) +] +{ + attr @child_name.ref "push", "reference" + attr @child_name.ref_dot "push" = "." + edge @child_name.ref -> @child_name.ref_dot + edge @child_name.ref_dot -> @parent_name.ref +} + +(import_from_statement + module_name: (dotted_name + . (identifier) @root_name)) +{ + attr @root_name.ref "push", "reference" + edge @root_name.ref -> root +} + +(import_from_statement + module_name: (dotted_name + (identifier) @leaf_name .) + (wildcard_import) @star) @stmt +{ + edge @stmt.after_scope -> @star.ref_dot, "precedence" = 1 + edge @star.ref_dot -> @leaf_name.ref + attr @star.ref_dot "push" = "." +} + +[ + (import_statement + name: (dotted_name + (identifier) @parent_name + . + (identifier) @child_name)) + (import_from_statement + name: (dotted_name + (identifier) @parent_name + . + (identifier) @child_name)) + (import_from_statement + name: (aliased_import + name: (dotted_name + (identifier) @parent_name + . + (identifier) @child_name))) +] +{ + edge @child_name.ref -> @child_name.ref_dot + edge @child_name.ref_dot -> @parent_name.ref + edge @parent_name.def -> @parent_name.def_dot + edge @parent_name.def_dot -> @child_name.def + attr @child_name.def "pop", "definition" + attr @child_name.ref "push","reference" + attr @parent_name.def_dot "pop" = "." + attr @child_name.ref_dot "push" = "." +} + +;-------- +; Scopes +;-------- + +[ + (module (_) @last_stmt .) + (block (_) @last_stmt .) +] @block +{ + edge @block.after_scope -> @last_stmt.after_scope +} + +[ + (module (_) @stmt1 . (_) @stmt2) + (block (_) @stmt1 . (_) @stmt2) +] +{ + edge @stmt2.before_scope -> @stmt1.after_scope +} + +[ + (module (_) @stmt) + (block (_) @stmt) +] +{ + edge @stmt.after_scope -> @stmt.before_scope + let @stmt::local_scope = @stmt.before_scope +} + +[ + (block . (_) @stmt) + (module . (_) @stmt) +] @block +{ + edge @stmt.before_scope -> @block.before_scope +} + +(block (_) @stmt . ) @block +{ + edge @block.after_scope -> @stmt.after_scope +} + +(function_definition (block) @block) +{ + edge @block.before_scope -> @block::local_scope +} + +[ + (while_statement (block) @block) + (if_statement (block) @block) + (with_statement (block) @block) + (try_statement (block) @block) + (for_statement (block) @block) + (_ [ + (else_clause (block) @block) + (elif_clause (block) @block) + (except_clause (block) @block) + (finally_clause (block) @block) + ]) +] @stmt +{ + edge @block.before_scope -> @block::local_scope + edge @stmt.after_scope -> @block.after_scope +} + +(match_statement (case_clause) @block) @stmt +{ + let @block::local_scope = @block.before_scope + edge @block.before_scope -> @stmt.before_scope + edge @stmt.after_scope -> @block.after_scope +} + +[ + (for_statement) + (while_statement) +] @stmt +{ + edge @stmt.before_scope -> @stmt.after_scope +} + +;------------- +; Definitions +;------------- + +[ + (assignment + left: (_) @pattern + right: (_) @value) + (with_item + value: + (as_pattern + (_) @value + alias: (as_pattern_target (_) @pattern))) +] +{ + edge @pattern.input -> @value.output +} + +(function_definition + name: (identifier) @name + parameters: (parameters) @params + body: (block) @body) @func +{ + attr @name "definiens" = @func + edge @func.after_scope -> @name + edge @name -> @func.call + edge @func.call -> @func.return_value + edge @body.before_scope -> @params.after_scope + edge @body.before_scope -> @func.drop_scope + edge @func.drop_scope -> @func::bottom + attr @func.drop_scope "drop" + attr @name "pop", "definition" + attr @func.call "pop" = "()", "pop-scope" + attr @params.before_scope "jump-to" + attr @func.return_value "endpoint" + let @func::function_returns = @func.return_value + + ; Prevent functions defined inside of method bodies from being treated like methods + let @body::class_self_scope = nil + let @body::class_member_attr_scope = nil +} + +;; +;; BEGIN BIG GNARLY DISJUNCTION +;; +;; The following pair of rules is intended to capture the following behavior: +;; +;; If a function definition is used to define a method, by being inside a class +;; definition, then we make its syntax type `method`. Otherwise, we make it's +;; syntax type `function`. Unfortunately, because of the limitations on negation +;; and binding in tree sitter queries, we cannot negate `class_definition` or +;; similar things directly. Instead, we have to manually push the negation down +;; to form the finite disjunction it corresponds to. +;; + +[ + (class_definition (block (decorated_definition (function_definition name: (_)@name)))) + (class_definition (block (function_definition name: (_)@name))) +] +{ + attr @name "syntax_type" = "method" +} + +[ + (module (decorated_definition (function_definition name: (_)@name))) + (module (function_definition name: (_)@name)) + + (if_statement (block (decorated_definition (function_definition name: (_)@name)))) + (if_statement (block (function_definition name: (_)@name))) + + (elif_clause (block (decorated_definition (function_definition name: (_)@name)))) + (elif_clause (block (function_definition name: (_)@name))) + + (else_clause (block (decorated_definition (function_definition name: (_)@name)))) + (else_clause (block (function_definition name: (_)@name))) + + (case_clause (block (decorated_definition (function_definition name: (_)@name)))) + (case_clause (block (function_definition name: (_)@name))) + + (for_statement (block (decorated_definition (function_definition name: (_)@name)))) + (for_statement (block (function_definition name: (_)@name))) + + (while_statement (block (decorated_definition (function_definition name: (_)@name)))) + (while_statement (block (function_definition name: (_)@name))) + + (try_statement (block (decorated_definition (function_definition name: (_)@name)))) + (try_statement (block (function_definition name: (_)@name))) + + (except_clause (block (decorated_definition (function_definition name: (_)@name)))) + (except_clause (block (function_definition name: (_)@name))) + + (finally_clause (block (decorated_definition (function_definition name: (_)@name)))) + (finally_clause (block (function_definition name: (_)@name))) + + (with_statement (block (decorated_definition (function_definition name: (_)@name)))) + (with_statement (block (function_definition name: (_)@name))) + + (function_definition (block (decorated_definition (function_definition name: (_)@name)))) + (function_definition (block (function_definition name: (_)@name))) +] +{ + attr @name "syntax_type" = "function" +} + +;; +;; END BIG GNARLY DISJUNCTION +;; + +(function_definition + parameters: (parameters + . (identifier) @param) + body: (block) @body) +{ + edge @param.input -> @param::class_self_scope + edge @param::class_member_attr_scope -> @param.output + edge @param.output -> @body.after_scope + attr @param.output "push" +} + +(parameter/identifier) @param +{ + attr @param.input "definition", "pop" + attr @param.param_name "push" + edge @param.input -> @param.param_index + edge @param.input -> @param.param_name +} + +[ + (parameter/default_parameter + name: (identifier) @name + value: (_) @value) @param + (parameter/typed_default_parameter + name: (_) @name + value: (_) @value) @param +] +{ + attr @name "definition", "pop" + attr @param.param_name "push" = @name + edge @name -> @param.param_name + edge @name -> @param.param_index + edge @param.input -> @name + edge @name -> @value.output +} + +[ + (parameter/typed_parameter + . (_) @name) @param + (parameter/list_splat_pattern + (_) @name) @param + (parameter/dictionary_splat_pattern + (_) @name) @param +] +{ + attr @name "definition", "pop" + attr @param.param_name "push" = @name + edge @name -> @param.param_name + edge @name -> @param.param_index + edge @param.input -> @name +} + +[ + (pattern_list (_) @pattern) + (tuple_pattern (_) @pattern) +] @list +{ + let statement_scope = @list::local_scope + let @pattern::local_scope = @pattern.pattern_before_scope + edge statement_scope -> @pattern::local_scope, "precedence" = (+ 1 (child-index @pattern)) + + edge @pattern.pattern_index -> @list.input + edge @pattern.input -> @pattern.pattern_index + attr @pattern.pattern_index "push" = (child-index @pattern) +} + +(parameters + (_) @param) @params +{ + attr @param.param_index "push" = (child-index @param) + edge @param.param_index -> @params.before_scope + edge @params.after_scope -> @param.input + edge @param.param_name -> @params.before_scope +} + +(return_statement (_) @expr) @stmt +{ + edge @stmt::function_returns -> @expr.output +} + +(class_definition + name: (identifier) @name) @class +{ + attr @name "definiens" = @class + attr @name "syntax_type" = "class" + edge @class.parent_scope -> @class::class_parent_scope + edge @class.parent_scope -> @class::local_scope + edge @class.after_scope -> @name + edge @name -> @class.call + edge @name -> @class.dot + edge @class.dot -> @class.members + edge @class.call -> @class.call_drop + edge @class.call_drop -> @class.self_scope + edge @class.self_scope -> @class.super_scope + edge @class.self_scope -> @class.self_dot + edge @class.self_dot -> @class.members + edge @class.members -> @class.member_attrs + attr @class.call "pop" = "()", "pop-scope" + attr @class.call_drop "drop" + attr @class.dot "pop" = "." + attr @class.self_dot "pop" = "." + attr @name "pop", "definition" + attr @class.member_attrs "push" = "." + attr @class.self_scope "endpoint" + let @class::super_scope = @class.super_scope + let @class::class_parent_scope = @class.parent_scope + let @class::class_self_scope = @class.call_drop + let @class::class_member_attr_scope = @class.member_attrs +} + +(class_definition + body: (block + (_) @last_stmt .) @body) @class +{ + edge @class.members -> @last_stmt.after_scope +} + +(class_definition + superclasses: (argument_list + (_) @superclass)) @class +{ + edge @class.super_scope -> @superclass.output +} + +(decorated_definition + definition: (_) @def) @stmt +{ + edge @def.before_scope -> @stmt.before_scope + edge @stmt.after_scope -> @def.after_scope +} + +(case_clause + pattern: (_) @pattern + consequence: (_) @consequence) @clause +{ + edge @consequence.before_scope -> @pattern.new_bindings + edge @consequence.before_scope -> @clause.before_scope + edge @clause.after_scope -> @consequence.after_scope +} + +;------------- +; Expressions +;------------- + +(call + function: (_) @fn + arguments: (argument_list) @args) @call +{ + edge @call.output -> @call.output_args + edge @call.output_args -> @fn.output + attr @call.output_args "push" = "()", "push-scope" = @args +} + +(call + function: (attribute + object: (_) @receiver) + arguments: (argument_list + (expression) @arg) @args) +{ + edge @args -> @arg.arg_index + edge @receiver -> @receiver.arg_index + + attr @receiver.arg_index "pop" = "0" + edge @receiver.arg_index -> @receiver.output + + attr @arg.arg_index "pop" = (+ 1 (child-index @arg)) + edge @arg.arg_index -> @arg.output +} + +(call + arguments: (argument_list + (keyword_argument + name: (identifier) @name + value: (_) @val) @arg) @args) @call +{ + edge @args -> @arg.arg_name + attr @arg.arg_name "pop" = @name + edge @arg.arg_name -> @val.output +} + +(argument_list + (expression) @arg) @args +{ + edge @args -> @arg.arg_index + attr @arg.arg_index "pop" = (child-index @arg) + edge @arg.arg_index -> @arg.output +} + +( + (call + function: (identifier) @fn-name) @call + (#eq? @fn-name "super") +) +{ + edge @call.output -> @call::super_scope +} + +[ + (tuple (_) @element) + (expression_list (_) @element) +] @tuple +{ + edge @tuple.output -> @element.el_index + attr @element.el_index "pop" = (child-index @element) + edge @element.el_index -> @element.output + + edge @tuple.new_bindings -> @element.new_bindings +} + +(attribute + object: (_) @object + attribute: (identifier) @name) @expr +{ + edge @expr.output -> @name.output + edge @name.output -> @expr.output_dot + edge @expr.output_dot -> @object.output + edge @object.input -> @expr.input_dot + edge @expr.input_dot -> @name.input + edge @name.input -> @expr.input + attr @expr.output_dot "push" = "." + attr @expr.input_dot "pop" = "." + attr @name.input "pop" + attr @name.output "push" +} + +(pattern/attribute + attribute: (identifier) @name) +{ + attr @name.input "definition" +} + +(primary_expression/attribute + attribute: (identifier) @name) +{ + attr @name.output "reference" +} + +(primary_expression/identifier) @id +{ + edge @id.output -> @id::local_scope + edge @id.output -> @id::class_parent_scope + edge @id::local_scope -> @id.input + attr @id.input "pop" + attr @id.output "push", "reference" + + attr @id.new_binding_pop "pop", "definition" + edge @id.new_bindings -> @id.new_binding_pop +} + +(pattern/identifier) @id +{ + edge @id.output -> @id::local_scope + edge @id.output -> @id::class_parent_scope + edge @id::local_scope -> @id.input, "precedence" = 1 + attr @id.input "pop", "definition" + attr @id.output "push" + + attr @id.new_binding_pop "pop", "definition" + edge @id.new_bindings -> @id.new_binding_pop +} + +(as_pattern + (expression) @value + alias: (as_pattern_target (primary_expression/identifier) @id)) @as_pattern +{ + edge @id.output -> @id::local_scope + edge @id.output -> @id::class_parent_scope + edge @id::local_scope -> @id.input, "precedence" = 1 + attr @id.input "pop", "definition" + attr @id.output "push" + + edge @as_pattern.new_bindings -> @value.new_bindings + edge @as_pattern.new_bindings -> @id.new_bindings +} + +(list) @list +{ + edge @list.output -> @list.called + edge @list.called -> @list::global_dot + attr @list.called "push" = "list" +} + +(list (_) @el) @list +{ + edge @list.new_bindings -> @el.new_bindings +} + +(dictionary (pair) @pair) @dict +{ + edge @dict.new_bindings -> @pair.new_bindings +} + +(pair + value: (_) @value) @pair +{ + edge @pair.new_bindings -> @value.new_bindings +} + +(set (_) @el) @set +{ + edge @set.new_bindings -> @el.new_bindings +} + +(list_splat (_) @splatted) @splat +{ +attr @splat.new_bindings_pop "pop" = @splatted, "definition" +edge @splat.new_bindings -> @splat.new_bindings_pop +} + +(binary_operator + (_) @left + (_) @right) @binop +{ + edge @binop.new_bindings -> @left.new_bindings + edge @binop.new_bindings -> @right.new_bindings +} + +(case_pattern (_) @expr) @pat +{ + edge @pat.new_bindings -> @expr.new_bindings +} From edfe903f0f5b976fd08d490e9ffe1925ea724ed0 Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Thu, 1 Jun 2023 09:42:59 +0200 Subject: [PATCH 109/291] convert: attr syntax --- .../src/stack-graphs.tsg | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg b/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg index 99fd0acaf..5aed50404 100644 --- a/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg +++ b/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg @@ -42,9 +42,9 @@ attribute node_symbol = node => symbol = (source-text node), source_n (module) @mod { var module_def = @mod.file_def - attr module_def "no_span" + attr (module_def) "no_span" var module_ref = @mod.file_ref - attr module_ref "no_span" + attr (module_ref) "no_span" var parent_module_def = module_def var parent_module_ref = module_ref var grandparent_module_ref = module_ref @@ -57,9 +57,9 @@ attribute node_symbol = node => symbol = (source-text node), source_n edge module_ref.next_ref -> module_ref.dot edge module_ref.dot -> module_ref - attr module_def "pop" = $1 + attr (module_def) "pop" = $1 attr module_def.dot "pop" = "." - attr module_ref "push" = $1 + attr (module_ref) "push" = $1 attr module_ref.dot "push" = "." set grandparent_module_ref = parent_module_ref @@ -67,13 +67,13 @@ attribute node_symbol = node => symbol = (source-text node), source_n set parent_module_ref = module_ref set module_ref = module_ref.next_ref set module_def = module_def.next_def - attr module_def "no_span" - attr module_ref "no_span" + attr (module_def) "no_span" + attr (module_ref) "no_span" } "__init__\\.py$" { - attr parent_module_def "definition" + attr (parent_module_def) "definition" } "([^/]+)$" @@ -81,12 +81,12 @@ attribute node_symbol = node => symbol = (source-text node), source_n edge module_def -> module_def.dot edge module_def.dot -> module_def.next_def - attr module_def "definition", "pop" = (replace $1 "\\.py" "") + attr (module_def) "definition", "pop" = (replace $1 "\\.py" "") attr module_def.dot "pop" = "." set module_def = module_def.next_def - attr module_def "no_span" - attr module_ref "no_span" + attr (module_def) "no_span" + attr (module_ref) "no_span" } } From 58c67fd2dbc806ebb331c6a26deb1f45ec1bf871 Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Thu, 1 Jun 2023 09:43:51 +0200 Subject: [PATCH 110/291] convert: no_span --- .../src/stack-graphs.tsg | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg b/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg index 5aed50404..afe702317 100644 --- a/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg +++ b/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg @@ -42,9 +42,9 @@ attribute node_symbol = node => symbol = (source-text node), source_n (module) @mod { var module_def = @mod.file_def - attr (module_def) "no_span" + attr (module_def) empty_source_span var module_ref = @mod.file_ref - attr (module_ref) "no_span" + attr (module_ref) empty_source_span var parent_module_def = module_def var parent_module_ref = module_ref var grandparent_module_ref = module_ref @@ -67,8 +67,8 @@ attribute node_symbol = node => symbol = (source-text node), source_n set parent_module_ref = module_ref set module_ref = module_ref.next_ref set module_def = module_def.next_def - attr (module_def) "no_span" - attr (module_ref) "no_span" + attr (module_def) empty_source_span + attr (module_ref) empty_source_span } "__init__\\.py$" @@ -85,8 +85,8 @@ attribute node_symbol = node => symbol = (source-text node), source_n attr module_def.dot "pop" = "." set module_def = module_def.next_def - attr (module_def) "no_span" - attr (module_ref) "no_span" + attr (module_def) empty_source_span + attr (module_ref) empty_source_span } } From a830ef55ab2b304f3d59c438d1ba1dfcf942537e Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Thu, 1 Jun 2023 09:48:03 +0200 Subject: [PATCH 111/291] convert: more attr syntax --- .../src/stack-graphs.tsg | 150 +++++++++--------- 1 file changed, 75 insertions(+), 75 deletions(-) diff --git a/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg b/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg index afe702317..f6d557b50 100644 --- a/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg +++ b/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg @@ -58,9 +58,9 @@ attribute node_symbol = node => symbol = (source-text node), source_n edge module_ref.dot -> module_ref attr (module_def) "pop" = $1 - attr module_def.dot "pop" = "." + attr (module_def.dot) "pop" = "." attr (module_ref) "push" = $1 - attr module_ref.dot "push" = "." + attr (module_ref.dot) "push" = "." set grandparent_module_ref = parent_module_ref set parent_module_def = module_def @@ -82,7 +82,7 @@ attribute node_symbol = node => symbol = (source-text node), source_n edge module_def.dot -> module_def.next_def attr (module_def) "definition", "pop" = (replace $1 "\\.py" "") - attr module_def.dot "pop" = "." + attr (module_def.dot) "pop" = "." set module_def = module_def.next_def attr (module_def) empty_source_span @@ -96,10 +96,10 @@ attribute node_symbol = node => symbol = (source-text node), source_n edge @mod.before_scope -> @mod.global_dot edge @mod.global -> root - attr @mod.global "push" = "" + attr (@mod.global) "push" = "" edge @mod.global_dot -> @mod.global - attr @mod.global_dot "push" = "." + attr (@mod.global_dot) "push" = "." var @mod::parent_module = parent_module_ref var @mod::grandparent_module = grandparent_module_ref @@ -114,8 +114,8 @@ attribute node_symbol = node => symbol = (source-text node), source_n { edge @stmt.after_scope -> @root_name.def, "precedence" = 1 edge @root_name.ref -> root - attr @root_name.ref "push", "reference" - attr @root_name.def "pop", "definition" + attr (@root_name.ref) "push", "reference" + attr (@root_name.def) "pop", "definition" } (import_statement @@ -124,7 +124,7 @@ attribute node_symbol = node => symbol = (source-text node), source_n { edge @stmt.after_scope -> @root_name.def edge @root_name.ref -> root - attr @root_name.ref "push", "reference" + attr (@root_name.ref) "push", "reference" } (import_from_statement @@ -132,7 +132,7 @@ attribute node_symbol = node => symbol = (source-text node), source_n . (identifier) @prefix_root_name)) @stmt { edge @prefix_root_name.ref -> root - attr @prefix_root_name.ref "push", "reference" + attr (@prefix_root_name.ref) "push", "reference" } (import_from_statement @@ -141,9 +141,9 @@ attribute node_symbol = node => symbol = (source-text node), source_n { edge @stmt.after_scope -> @import_root_name.def, "precedence" = 1 edge @import_root_name.ref -> @import_root_name.ref_dot - attr @import_root_name.def "pop", "definition" - attr @import_root_name.ref "push", "reference" - attr @import_root_name.ref_dot "push" = "." + attr (@import_root_name.def) "pop", "definition" + attr (@import_root_name.ref) "push", "reference" + attr (@import_root_name.ref_dot) "push" = "." } (import_from_statement @@ -152,8 +152,8 @@ attribute node_symbol = node => symbol = (source-text node), source_n . (identifier) @import_root_name))) @stmt { edge @import_root_name.ref -> @import_root_name.ref_dot - attr @import_root_name.ref "push", "reference" - attr @import_root_name.ref_dot "push" = "." + attr (@import_root_name.ref) "push", "reference" + attr (@import_root_name.ref_dot) "push" = "." } (import_from_statement @@ -186,7 +186,7 @@ attribute node_symbol = node => symbol = (source-text node), source_n { edge @stmt.after_scope -> @alias edge @alias -> @name.ref - attr @alias "pop", "definition" + attr (@alias) "pop", "definition" } [ @@ -198,8 +198,8 @@ attribute node_symbol = node => symbol = (source-text node), source_n (identifier) @leaf_name .)) ] { - attr @leaf_name.def "pop", "definition" - attr @leaf_name.ref "push", "reference" + attr (@leaf_name.def) "pop", "definition" + attr (@leaf_name.ref) "push", "reference" edge @leaf_name.def -> @leaf_name.ref } @@ -222,8 +222,8 @@ attribute node_symbol = node => symbol = (source-text node), source_n (dotted_name . (identifier) @name)) { - attr @name.ref "push", "reference" - attr @name.ref_dot "push" = "." + attr (@name.ref) "push", "reference" + attr (@name.ref_dot) "push" = "." edge @name.ref -> @name.ref_dot edge @name.ref_dot -> @prefix.ref } @@ -242,8 +242,8 @@ attribute node_symbol = node => symbol = (source-text node), source_n (identifier) @child_name)) ] { - attr @child_name.ref "push", "reference" - attr @child_name.ref_dot "push" = "." + attr (@child_name.ref) "push", "reference" + attr (@child_name.ref_dot) "push" = "." edge @child_name.ref -> @child_name.ref_dot edge @child_name.ref_dot -> @parent_name.ref } @@ -252,7 +252,7 @@ attribute node_symbol = node => symbol = (source-text node), source_n module_name: (dotted_name . (identifier) @root_name)) { - attr @root_name.ref "push", "reference" + attr (@root_name.ref) "push", "reference" edge @root_name.ref -> root } @@ -263,7 +263,7 @@ attribute node_symbol = node => symbol = (source-text node), source_n { edge @stmt.after_scope -> @star.ref_dot, "precedence" = 1 edge @star.ref_dot -> @leaf_name.ref - attr @star.ref_dot "push" = "." + attr (@star.ref_dot) "push" = "." } [ @@ -289,10 +289,10 @@ attribute node_symbol = node => symbol = (source-text node), source_n edge @child_name.ref_dot -> @parent_name.ref edge @parent_name.def -> @parent_name.def_dot edge @parent_name.def_dot -> @child_name.def - attr @child_name.def "pop", "definition" - attr @child_name.ref "push","reference" - attr @parent_name.def_dot "pop" = "." - attr @child_name.ref_dot "push" = "." + attr (@child_name.def) "pop", "definition" + attr (@child_name.ref) "push","reference" + attr (@parent_name.def_dot) "pop" = "." + attr (@child_name.ref_dot) "push" = "." } ;-------- @@ -398,18 +398,18 @@ attribute node_symbol = node => symbol = (source-text node), source_n parameters: (parameters) @params body: (block) @body) @func { - attr @name "definiens" = @func + attr (@name) "definiens" = @func edge @func.after_scope -> @name edge @name -> @func.call edge @func.call -> @func.return_value edge @body.before_scope -> @params.after_scope edge @body.before_scope -> @func.drop_scope edge @func.drop_scope -> @func::bottom - attr @func.drop_scope "drop" - attr @name "pop", "definition" - attr @func.call "pop" = "()", "pop-scope" - attr @params.before_scope "jump-to" - attr @func.return_value "endpoint" + attr (@func.drop_scope) "drop" + attr (@name) "pop", "definition" + attr (@func.call) "pop" = "()", "pop-scope" + attr (@params.before_scope) "jump-to" + attr (@func.return_value) "endpoint" let @func::function_returns = @func.return_value ; Prevent functions defined inside of method bodies from being treated like methods @@ -435,7 +435,7 @@ attribute node_symbol = node => symbol = (source-text node), source_n (class_definition (block (function_definition name: (_)@name))) ] { - attr @name "syntax_type" = "method" + attr (@name) "syntax_type" = "method" } [ @@ -476,7 +476,7 @@ attribute node_symbol = node => symbol = (source-text node), source_n (function_definition (block (function_definition name: (_)@name))) ] { - attr @name "syntax_type" = "function" + attr (@name) "syntax_type" = "function" } ;; @@ -491,13 +491,13 @@ attribute node_symbol = node => symbol = (source-text node), source_n edge @param.input -> @param::class_self_scope edge @param::class_member_attr_scope -> @param.output edge @param.output -> @body.after_scope - attr @param.output "push" + attr (@param.output) "push" } (parameter/identifier) @param { - attr @param.input "definition", "pop" - attr @param.param_name "push" + attr (@param.input) "definition", "pop" + attr (@param.param_name) "push" edge @param.input -> @param.param_index edge @param.input -> @param.param_name } @@ -511,8 +511,8 @@ attribute node_symbol = node => symbol = (source-text node), source_n value: (_) @value) @param ] { - attr @name "definition", "pop" - attr @param.param_name "push" = @name + attr (@name) "definition", "pop" + attr (@param.param_name) "push" = @name edge @name -> @param.param_name edge @name -> @param.param_index edge @param.input -> @name @@ -528,8 +528,8 @@ attribute node_symbol = node => symbol = (source-text node), source_n (_) @name) @param ] { - attr @name "definition", "pop" - attr @param.param_name "push" = @name + attr (@name) "definition", "pop" + attr (@param.param_name) "push" = @name edge @name -> @param.param_name edge @name -> @param.param_index edge @param.input -> @name @@ -546,13 +546,13 @@ attribute node_symbol = node => symbol = (source-text node), source_n edge @pattern.pattern_index -> @list.input edge @pattern.input -> @pattern.pattern_index - attr @pattern.pattern_index "push" = (child-index @pattern) + attr (@pattern.pattern_index) "push" = (child-index @pattern) } (parameters (_) @param) @params { - attr @param.param_index "push" = (child-index @param) + attr (@param.param_index) "push" = (child-index @param) edge @param.param_index -> @params.before_scope edge @params.after_scope -> @param.input edge @param.param_name -> @params.before_scope @@ -566,8 +566,8 @@ attribute node_symbol = node => symbol = (source-text node), source_n (class_definition name: (identifier) @name) @class { - attr @name "definiens" = @class - attr @name "syntax_type" = "class" + attr (@name) "definiens" = @class + attr (@name) "syntax_type" = "class" edge @class.parent_scope -> @class::class_parent_scope edge @class.parent_scope -> @class::local_scope edge @class.after_scope -> @name @@ -580,13 +580,13 @@ attribute node_symbol = node => symbol = (source-text node), source_n edge @class.self_scope -> @class.self_dot edge @class.self_dot -> @class.members edge @class.members -> @class.member_attrs - attr @class.call "pop" = "()", "pop-scope" - attr @class.call_drop "drop" - attr @class.dot "pop" = "." - attr @class.self_dot "pop" = "." - attr @name "pop", "definition" - attr @class.member_attrs "push" = "." - attr @class.self_scope "endpoint" + attr (@class.call) "pop" = "()", "pop-scope" + attr (@class.call_drop) "drop" + attr (@class.dot) "pop" = "." + attr (@class.self_dot) "pop" = "." + attr (@name) "pop", "definition" + attr (@class.member_attrs) "push" = "." + attr (@class.self_scope) "endpoint" let @class::super_scope = @class.super_scope let @class::class_parent_scope = @class.parent_scope let @class::class_self_scope = @class.call_drop @@ -633,7 +633,7 @@ attribute node_symbol = node => symbol = (source-text node), source_n { edge @call.output -> @call.output_args edge @call.output_args -> @fn.output - attr @call.output_args "push" = "()", "push-scope" = @args + attr (@call.output_args) "push" = "()", "push-scope" = @args } (call @@ -645,10 +645,10 @@ attribute node_symbol = node => symbol = (source-text node), source_n edge @args -> @arg.arg_index edge @receiver -> @receiver.arg_index - attr @receiver.arg_index "pop" = "0" + attr (@receiver.arg_index) "pop" = "0" edge @receiver.arg_index -> @receiver.output - attr @arg.arg_index "pop" = (+ 1 (child-index @arg)) + attr (@arg.arg_index) "pop" = (+ 1 (child-index @arg)) edge @arg.arg_index -> @arg.output } @@ -659,7 +659,7 @@ attribute node_symbol = node => symbol = (source-text node), source_n value: (_) @val) @arg) @args) @call { edge @args -> @arg.arg_name - attr @arg.arg_name "pop" = @name + attr (@arg.arg_name) "pop" = @name edge @arg.arg_name -> @val.output } @@ -667,7 +667,7 @@ attribute node_symbol = node => symbol = (source-text node), source_n (expression) @arg) @args { edge @args -> @arg.arg_index - attr @arg.arg_index "pop" = (child-index @arg) + attr (@arg.arg_index) "pop" = (child-index @arg) edge @arg.arg_index -> @arg.output } @@ -686,7 +686,7 @@ attribute node_symbol = node => symbol = (source-text node), source_n ] @tuple { edge @tuple.output -> @element.el_index - attr @element.el_index "pop" = (child-index @element) + attr (@element.el_index) "pop" = (child-index @element) edge @element.el_index -> @element.output edge @tuple.new_bindings -> @element.new_bindings @@ -702,22 +702,22 @@ attribute node_symbol = node => symbol = (source-text node), source_n edge @object.input -> @expr.input_dot edge @expr.input_dot -> @name.input edge @name.input -> @expr.input - attr @expr.output_dot "push" = "." - attr @expr.input_dot "pop" = "." - attr @name.input "pop" - attr @name.output "push" + attr (@expr.output_dot) "push" = "." + attr (@expr.input_dot) "pop" = "." + attr (@name.input) "pop" + attr (@name.output) "push" } (pattern/attribute attribute: (identifier) @name) { - attr @name.input "definition" + attr (@name.input) "definition" } (primary_expression/attribute attribute: (identifier) @name) { - attr @name.output "reference" + attr (@name.output) "reference" } (primary_expression/identifier) @id @@ -725,10 +725,10 @@ attribute node_symbol = node => symbol = (source-text node), source_n edge @id.output -> @id::local_scope edge @id.output -> @id::class_parent_scope edge @id::local_scope -> @id.input - attr @id.input "pop" - attr @id.output "push", "reference" + attr (@id.input) "pop" + attr (@id.output) "push", "reference" - attr @id.new_binding_pop "pop", "definition" + attr (@id.new_binding_pop) "pop", "definition" edge @id.new_bindings -> @id.new_binding_pop } @@ -737,10 +737,10 @@ attribute node_symbol = node => symbol = (source-text node), source_n edge @id.output -> @id::local_scope edge @id.output -> @id::class_parent_scope edge @id::local_scope -> @id.input, "precedence" = 1 - attr @id.input "pop", "definition" - attr @id.output "push" + attr (@id.input) "pop", "definition" + attr (@id.output) "push" - attr @id.new_binding_pop "pop", "definition" + attr (@id.new_binding_pop) "pop", "definition" edge @id.new_bindings -> @id.new_binding_pop } @@ -751,8 +751,8 @@ attribute node_symbol = node => symbol = (source-text node), source_n edge @id.output -> @id::local_scope edge @id.output -> @id::class_parent_scope edge @id::local_scope -> @id.input, "precedence" = 1 - attr @id.input "pop", "definition" - attr @id.output "push" + attr (@id.input) "pop", "definition" + attr (@id.output) "push" edge @as_pattern.new_bindings -> @value.new_bindings edge @as_pattern.new_bindings -> @id.new_bindings @@ -762,7 +762,7 @@ attribute node_symbol = node => symbol = (source-text node), source_n { edge @list.output -> @list.called edge @list.called -> @list::global_dot - attr @list.called "push" = "list" + attr (@list.called) "push" = "list" } (list (_) @el) @list @@ -788,7 +788,7 @@ attribute node_symbol = node => symbol = (source-text node), source_n (list_splat (_) @splatted) @splat { -attr @splat.new_bindings_pop "pop" = @splatted, "definition" +attr (@splat.new_bindings_pop) "pop" = @splatted, "definition" edge @splat.new_bindings -> @splat.new_bindings_pop } From d5a97bd101f600745fa71c1562e9d1b1291def13 Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Thu, 1 Jun 2023 09:49:10 +0200 Subject: [PATCH 112/291] convert: pop_scoped_symbol --- .../src/stack-graphs.tsg | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg b/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg index f6d557b50..d7b6c33ed 100644 --- a/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg +++ b/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg @@ -58,7 +58,7 @@ attribute node_symbol = node => symbol = (source-text node), source_n edge module_ref.dot -> module_ref attr (module_def) "pop" = $1 - attr (module_def.dot) "pop" = "." + attr (module_def.dot) pop_symbol = "." attr (module_ref) "push" = $1 attr (module_ref.dot) "push" = "." @@ -82,7 +82,7 @@ attribute node_symbol = node => symbol = (source-text node), source_n edge module_def.dot -> module_def.next_def attr (module_def) "definition", "pop" = (replace $1 "\\.py" "") - attr (module_def.dot) "pop" = "." + attr (module_def.dot) pop_symbol = "." set module_def = module_def.next_def attr (module_def) empty_source_span @@ -291,7 +291,7 @@ attribute node_symbol = node => symbol = (source-text node), source_n edge @parent_name.def_dot -> @child_name.def attr (@child_name.def) "pop", "definition" attr (@child_name.ref) "push","reference" - attr (@parent_name.def_dot) "pop" = "." + attr (@parent_name.def_dot) pop_symbol = "." attr (@child_name.ref_dot) "push" = "." } @@ -407,7 +407,7 @@ attribute node_symbol = node => symbol = (source-text node), source_n edge @func.drop_scope -> @func::bottom attr (@func.drop_scope) "drop" attr (@name) "pop", "definition" - attr (@func.call) "pop" = "()", "pop-scope" + attr (@func.call) pop_scoped_symbol = "()" attr (@params.before_scope) "jump-to" attr (@func.return_value) "endpoint" let @func::function_returns = @func.return_value @@ -580,10 +580,10 @@ attribute node_symbol = node => symbol = (source-text node), source_n edge @class.self_scope -> @class.self_dot edge @class.self_dot -> @class.members edge @class.members -> @class.member_attrs - attr (@class.call) "pop" = "()", "pop-scope" + attr (@class.call) pop_scoped_symbol = "()" attr (@class.call_drop) "drop" - attr (@class.dot) "pop" = "." - attr (@class.self_dot) "pop" = "." + attr (@class.dot) pop_symbol = "." + attr (@class.self_dot) pop_symbol = "." attr (@name) "pop", "definition" attr (@class.member_attrs) "push" = "." attr (@class.self_scope) "endpoint" @@ -645,7 +645,7 @@ attribute node_symbol = node => symbol = (source-text node), source_n edge @args -> @arg.arg_index edge @receiver -> @receiver.arg_index - attr (@receiver.arg_index) "pop" = "0" + attr (@receiver.arg_index) pop_symbol = "0" edge @receiver.arg_index -> @receiver.output attr (@arg.arg_index) "pop" = (+ 1 (child-index @arg)) @@ -703,7 +703,7 @@ attribute node_symbol = node => symbol = (source-text node), source_n edge @expr.input_dot -> @name.input edge @name.input -> @expr.input attr (@expr.output_dot) "push" = "." - attr (@expr.input_dot) "pop" = "." + attr (@expr.input_dot) pop_symbol = "." attr (@name.input) "pop" attr (@name.output) "push" } From 01cf05d84a85afa8d4c944c3ff4ccf01bdca22e6 Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Thu, 1 Jun 2023 09:50:13 +0200 Subject: [PATCH 113/291] convert: push symbol --- .../src/stack-graphs.tsg | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg b/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg index d7b6c33ed..e4ef4ada4 100644 --- a/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg +++ b/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg @@ -60,7 +60,7 @@ attribute node_symbol = node => symbol = (source-text node), source_n attr (module_def) "pop" = $1 attr (module_def.dot) pop_symbol = "." attr (module_ref) "push" = $1 - attr (module_ref.dot) "push" = "." + attr (module_ref.dot) push_symbol = "." set grandparent_module_ref = parent_module_ref set parent_module_def = module_def @@ -96,10 +96,10 @@ attribute node_symbol = node => symbol = (source-text node), source_n edge @mod.before_scope -> @mod.global_dot edge @mod.global -> root - attr (@mod.global) "push" = "" + attr (@mod.global) push_symbol = "" edge @mod.global_dot -> @mod.global - attr (@mod.global_dot) "push" = "." + attr (@mod.global_dot) push_symbol = "." var @mod::parent_module = parent_module_ref var @mod::grandparent_module = grandparent_module_ref @@ -143,7 +143,7 @@ attribute node_symbol = node => symbol = (source-text node), source_n edge @import_root_name.ref -> @import_root_name.ref_dot attr (@import_root_name.def) "pop", "definition" attr (@import_root_name.ref) "push", "reference" - attr (@import_root_name.ref_dot) "push" = "." + attr (@import_root_name.ref_dot) push_symbol = "." } (import_from_statement @@ -153,7 +153,7 @@ attribute node_symbol = node => symbol = (source-text node), source_n { edge @import_root_name.ref -> @import_root_name.ref_dot attr (@import_root_name.ref) "push", "reference" - attr (@import_root_name.ref_dot) "push" = "." + attr (@import_root_name.ref_dot) push_symbol = "." } (import_from_statement @@ -223,7 +223,7 @@ attribute node_symbol = node => symbol = (source-text node), source_n . (identifier) @name)) { attr (@name.ref) "push", "reference" - attr (@name.ref_dot) "push" = "." + attr (@name.ref_dot) push_symbol = "." edge @name.ref -> @name.ref_dot edge @name.ref_dot -> @prefix.ref } @@ -243,7 +243,7 @@ attribute node_symbol = node => symbol = (source-text node), source_n ] { attr (@child_name.ref) "push", "reference" - attr (@child_name.ref_dot) "push" = "." + attr (@child_name.ref_dot) push_symbol = "." edge @child_name.ref -> @child_name.ref_dot edge @child_name.ref_dot -> @parent_name.ref } @@ -263,7 +263,7 @@ attribute node_symbol = node => symbol = (source-text node), source_n { edge @stmt.after_scope -> @star.ref_dot, "precedence" = 1 edge @star.ref_dot -> @leaf_name.ref - attr (@star.ref_dot) "push" = "." + attr (@star.ref_dot) push_symbol = "." } [ @@ -292,7 +292,7 @@ attribute node_symbol = node => symbol = (source-text node), source_n attr (@child_name.def) "pop", "definition" attr (@child_name.ref) "push","reference" attr (@parent_name.def_dot) pop_symbol = "." - attr (@child_name.ref_dot) "push" = "." + attr (@child_name.ref_dot) push_symbol = "." } ;-------- @@ -585,7 +585,7 @@ attribute node_symbol = node => symbol = (source-text node), source_n attr (@class.dot) pop_symbol = "." attr (@class.self_dot) pop_symbol = "." attr (@name) "pop", "definition" - attr (@class.member_attrs) "push" = "." + attr (@class.member_attrs) push_symbol = "." attr (@class.self_scope) "endpoint" let @class::super_scope = @class.super_scope let @class::class_parent_scope = @class.parent_scope @@ -633,7 +633,7 @@ attribute node_symbol = node => symbol = (source-text node), source_n { edge @call.output -> @call.output_args edge @call.output_args -> @fn.output - attr (@call.output_args) "push" = "()", "push-scope" = @args + attr (@call.output_args) push_scoped_symbol = "()", scope = @args } (call @@ -702,7 +702,7 @@ attribute node_symbol = node => symbol = (source-text node), source_n edge @object.input -> @expr.input_dot edge @expr.input_dot -> @name.input edge @name.input -> @expr.input - attr (@expr.output_dot) "push" = "." + attr (@expr.output_dot) push_symbol = "." attr (@expr.input_dot) pop_symbol = "." attr (@name.input) "pop" attr (@name.output) "push" @@ -762,7 +762,7 @@ attribute node_symbol = node => symbol = (source-text node), source_n { edge @list.output -> @list.called edge @list.called -> @list::global_dot - attr (@list.called) "push" = "list" + attr (@list.called) push_symbol = "list" } (list (_) @el) @list From 3ab39ce7281c4a6a44c6176bde76f1b223c20bea Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Thu, 1 Jun 2023 10:04:39 +0200 Subject: [PATCH 114/291] convert: push and pop nodes --- .../src/stack-graphs.tsg | 70 +++++++++---------- 1 file changed, 35 insertions(+), 35 deletions(-) diff --git a/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg b/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg index e4ef4ada4..ee9d010eb 100644 --- a/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg +++ b/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg @@ -57,9 +57,9 @@ attribute node_symbol = node => symbol = (source-text node), source_n edge module_ref.next_ref -> module_ref.dot edge module_ref.dot -> module_ref - attr (module_def) "pop" = $1 + attr (module_def) pop_symbol = $1 attr (module_def.dot) pop_symbol = "." - attr (module_ref) "push" = $1 + attr (module_ref) push_symbol = $1 attr (module_ref.dot) push_symbol = "." set grandparent_module_ref = parent_module_ref @@ -81,7 +81,7 @@ attribute node_symbol = node => symbol = (source-text node), source_n edge module_def -> module_def.dot edge module_def.dot -> module_def.next_def - attr (module_def) "definition", "pop" = (replace $1 "\\.py" "") + attr (module_def) symbol_definition = (replace $1 "\\.py" ""), source_node = @mod attr (module_def.dot) pop_symbol = "." set module_def = module_def.next_def @@ -114,8 +114,8 @@ attribute node_symbol = node => symbol = (source-text node), source_n { edge @stmt.after_scope -> @root_name.def, "precedence" = 1 edge @root_name.ref -> root - attr (@root_name.ref) "push", "reference" - attr (@root_name.def) "pop", "definition" + attr (@root_name.ref) node_reference = @root_name + attr (@root_name.def) node_definition = @root_name } (import_statement @@ -124,7 +124,7 @@ attribute node_symbol = node => symbol = (source-text node), source_n { edge @stmt.after_scope -> @root_name.def edge @root_name.ref -> root - attr (@root_name.ref) "push", "reference" + attr (@root_name.ref) node_reference = @root_name } (import_from_statement @@ -132,7 +132,7 @@ attribute node_symbol = node => symbol = (source-text node), source_n . (identifier) @prefix_root_name)) @stmt { edge @prefix_root_name.ref -> root - attr (@prefix_root_name.ref) "push", "reference" + attr (@prefix_root_name.ref) node_reference = @prefix_root_name } (import_from_statement @@ -141,8 +141,8 @@ attribute node_symbol = node => symbol = (source-text node), source_n { edge @stmt.after_scope -> @import_root_name.def, "precedence" = 1 edge @import_root_name.ref -> @import_root_name.ref_dot - attr (@import_root_name.def) "pop", "definition" - attr (@import_root_name.ref) "push", "reference" + attr (@import_root_name.def) node_definition = @import_root_name + attr (@import_root_name.ref) node_reference = @import_root_name attr (@import_root_name.ref_dot) push_symbol = "." } @@ -152,7 +152,7 @@ attribute node_symbol = node => symbol = (source-text node), source_n . (identifier) @import_root_name))) @stmt { edge @import_root_name.ref -> @import_root_name.ref_dot - attr (@import_root_name.ref) "push", "reference" + attr (@import_root_name.ref) node_reference = @import_root_name attr (@import_root_name.ref_dot) push_symbol = "." } @@ -186,7 +186,7 @@ attribute node_symbol = node => symbol = (source-text node), source_n { edge @stmt.after_scope -> @alias edge @alias -> @name.ref - attr (@alias) "pop", "definition" + attr (@alias) node_definition = @alias } [ @@ -198,8 +198,8 @@ attribute node_symbol = node => symbol = (source-text node), source_n (identifier) @leaf_name .)) ] { - attr (@leaf_name.def) "pop", "definition" - attr (@leaf_name.ref) "push", "reference" + attr (@leaf_name.def) node_definition = @leaf_name + attr (@leaf_name.ref) node_reference = @leaf_name edge @leaf_name.def -> @leaf_name.ref } @@ -222,7 +222,7 @@ attribute node_symbol = node => symbol = (source-text node), source_n (dotted_name . (identifier) @name)) { - attr (@name.ref) "push", "reference" + attr (@name.ref) node_reference = @name attr (@name.ref_dot) push_symbol = "." edge @name.ref -> @name.ref_dot edge @name.ref_dot -> @prefix.ref @@ -242,7 +242,7 @@ attribute node_symbol = node => symbol = (source-text node), source_n (identifier) @child_name)) ] { - attr (@child_name.ref) "push", "reference" + attr (@child_name.ref) node_reference = @child_name attr (@child_name.ref_dot) push_symbol = "." edge @child_name.ref -> @child_name.ref_dot edge @child_name.ref_dot -> @parent_name.ref @@ -252,7 +252,7 @@ attribute node_symbol = node => symbol = (source-text node), source_n module_name: (dotted_name . (identifier) @root_name)) { - attr (@root_name.ref) "push", "reference" + attr (@root_name.ref) node_reference = @root_name edge @root_name.ref -> root } @@ -289,8 +289,8 @@ attribute node_symbol = node => symbol = (source-text node), source_n edge @child_name.ref_dot -> @parent_name.ref edge @parent_name.def -> @parent_name.def_dot edge @parent_name.def_dot -> @child_name.def - attr (@child_name.def) "pop", "definition" - attr (@child_name.ref) "push","reference" + attr (@child_name.def) node_definition = @child_name + attr (@child_name.ref) node_reference = @child_name attr (@parent_name.def_dot) pop_symbol = "." attr (@child_name.ref_dot) push_symbol = "." } @@ -406,7 +406,7 @@ attribute node_symbol = node => symbol = (source-text node), source_n edge @body.before_scope -> @func.drop_scope edge @func.drop_scope -> @func::bottom attr (@func.drop_scope) "drop" - attr (@name) "pop", "definition" + attr (@name) node_definition = @name attr (@func.call) pop_scoped_symbol = "()" attr (@params.before_scope) "jump-to" attr (@func.return_value) "endpoint" @@ -491,13 +491,13 @@ attribute node_symbol = node => symbol = (source-text node), source_n edge @param.input -> @param::class_self_scope edge @param::class_member_attr_scope -> @param.output edge @param.output -> @body.after_scope - attr (@param.output) "push" + attr (@param.output) push_node = @param } (parameter/identifier) @param { - attr (@param.input) "definition", "pop" - attr (@param.param_name) "push" + attr (@param.input) node_definition = @param + attr (@param.param_name) push_node = @param edge @param.input -> @param.param_index edge @param.input -> @param.param_name } @@ -511,7 +511,7 @@ attribute node_symbol = node => symbol = (source-text node), source_n value: (_) @value) @param ] { - attr (@name) "definition", "pop" + attr (@name) node_definition = @name attr (@param.param_name) "push" = @name edge @name -> @param.param_name edge @name -> @param.param_index @@ -528,7 +528,7 @@ attribute node_symbol = node => symbol = (source-text node), source_n (_) @name) @param ] { - attr (@name) "definition", "pop" + attr (@name) node_definition = @name attr (@param.param_name) "push" = @name edge @name -> @param.param_name edge @name -> @param.param_index @@ -584,7 +584,7 @@ attribute node_symbol = node => symbol = (source-text node), source_n attr (@class.call_drop) "drop" attr (@class.dot) pop_symbol = "." attr (@class.self_dot) pop_symbol = "." - attr (@name) "pop", "definition" + attr (@name) node_definition = @name attr (@class.member_attrs) push_symbol = "." attr (@class.self_scope) "endpoint" let @class::super_scope = @class.super_scope @@ -704,8 +704,8 @@ attribute node_symbol = node => symbol = (source-text node), source_n edge @name.input -> @expr.input attr (@expr.output_dot) push_symbol = "." attr (@expr.input_dot) pop_symbol = "." - attr (@name.input) "pop" - attr (@name.output) "push" + attr (@name.input) pop_node = @name + attr (@name.output) push_node = @name } (pattern/attribute @@ -725,10 +725,10 @@ attribute node_symbol = node => symbol = (source-text node), source_n edge @id.output -> @id::local_scope edge @id.output -> @id::class_parent_scope edge @id::local_scope -> @id.input - attr (@id.input) "pop" - attr (@id.output) "push", "reference" + attr (@id.input) pop_node = @id + attr (@id.output) node_reference = @id - attr (@id.new_binding_pop) "pop", "definition" + attr (@id.new_binding_pop) node_definition = @id edge @id.new_bindings -> @id.new_binding_pop } @@ -737,10 +737,10 @@ attribute node_symbol = node => symbol = (source-text node), source_n edge @id.output -> @id::local_scope edge @id.output -> @id::class_parent_scope edge @id::local_scope -> @id.input, "precedence" = 1 - attr (@id.input) "pop", "definition" - attr (@id.output) "push" + attr (@id.input) node_definition = @id + attr (@id.output) push_node = @id - attr (@id.new_binding_pop) "pop", "definition" + attr (@id.new_binding_pop) node_definition = @id edge @id.new_bindings -> @id.new_binding_pop } @@ -751,8 +751,8 @@ attribute node_symbol = node => symbol = (source-text node), source_n edge @id.output -> @id::local_scope edge @id.output -> @id::class_parent_scope edge @id::local_scope -> @id.input, "precedence" = 1 - attr (@id.input) "pop", "definition" - attr (@id.output) "push" + attr (@id.input) node_definition = @id + attr (@id.output) push_node = @id edge @as_pattern.new_bindings -> @value.new_bindings edge @as_pattern.new_bindings -> @id.new_bindings From a838d6e8fab1031d5025b41d7fc6b30aec110bc1 Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Thu, 1 Jun 2023 10:06:08 +0200 Subject: [PATCH 115/291] convert: push and pop with explicit values --- .../src/stack-graphs.tsg | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg b/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg index ee9d010eb..6f65fdfbd 100644 --- a/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg +++ b/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg @@ -512,7 +512,7 @@ attribute node_symbol = node => symbol = (source-text node), source_n ] { attr (@name) node_definition = @name - attr (@param.param_name) "push" = @name + attr (@param.param_name) push_node = @name edge @name -> @param.param_name edge @name -> @param.param_index edge @param.input -> @name @@ -529,7 +529,7 @@ attribute node_symbol = node => symbol = (source-text node), source_n ] { attr (@name) node_definition = @name - attr (@param.param_name) "push" = @name + attr (@param.param_name) push_node = @name edge @name -> @param.param_name edge @name -> @param.param_index edge @param.input -> @name @@ -546,13 +546,13 @@ attribute node_symbol = node => symbol = (source-text node), source_n edge @pattern.pattern_index -> @list.input edge @pattern.input -> @pattern.pattern_index - attr (@pattern.pattern_index) "push" = (child-index @pattern) + attr (@pattern.pattern_index) push_symbol = (child-index @pattern) } (parameters (_) @param) @params { - attr (@param.param_index) "push" = (child-index @param) + attr (@param.param_index) push_symbol = (child-index @param) edge @param.param_index -> @params.before_scope edge @params.after_scope -> @param.input edge @param.param_name -> @params.before_scope @@ -648,7 +648,7 @@ attribute node_symbol = node => symbol = (source-text node), source_n attr (@receiver.arg_index) pop_symbol = "0" edge @receiver.arg_index -> @receiver.output - attr (@arg.arg_index) "pop" = (+ 1 (child-index @arg)) + attr (@arg.arg_index) pop_symbol = (+ 1 (child-index @arg)) edge @arg.arg_index -> @arg.output } @@ -659,7 +659,7 @@ attribute node_symbol = node => symbol = (source-text node), source_n value: (_) @val) @arg) @args) @call { edge @args -> @arg.arg_name - attr (@arg.arg_name) "pop" = @name + attr (@arg.arg_name) pop_node = @name edge @arg.arg_name -> @val.output } @@ -667,7 +667,7 @@ attribute node_symbol = node => symbol = (source-text node), source_n (expression) @arg) @args { edge @args -> @arg.arg_index - attr (@arg.arg_index) "pop" = (child-index @arg) + attr (@arg.arg_index) pop_symbol = (child-index @arg) edge @arg.arg_index -> @arg.output } @@ -686,7 +686,7 @@ attribute node_symbol = node => symbol = (source-text node), source_n ] @tuple { edge @tuple.output -> @element.el_index - attr (@element.el_index) "pop" = (child-index @element) + attr (@element.el_index) pop_symbol = (child-index @element) edge @element.el_index -> @element.output edge @tuple.new_bindings -> @element.new_bindings @@ -788,7 +788,7 @@ attribute node_symbol = node => symbol = (source-text node), source_n (list_splat (_) @splatted) @splat { -attr (@splat.new_bindings_pop) "pop" = @splatted, "definition" +attr (@splat.new_bindings_pop) node_definition = @splatted edge @splat.new_bindings -> @splat.new_bindings_pop } From b3179de0b457b539a6066ede2386fa6a4575b5eb Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Thu, 1 Jun 2023 10:08:29 +0200 Subject: [PATCH 116/291] convert: more references and definitions --- .../tree-sitter-stack-graphs-python/src/stack-graphs.tsg | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg b/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg index 6f65fdfbd..7088f8b6f 100644 --- a/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg +++ b/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg @@ -73,7 +73,7 @@ attribute node_symbol = node => symbol = (source-text node), source_n "__init__\\.py$" { - attr (parent_module_def) "definition" + attr (parent_module_def) node_definition = @mod } "([^/]+)$" @@ -711,13 +711,13 @@ attribute node_symbol = node => symbol = (source-text node), source_n (pattern/attribute attribute: (identifier) @name) { - attr (@name.input) "definition" + attr (@name.input) node_definition = @name } (primary_expression/attribute attribute: (identifier) @name) { - attr (@name.output) "reference" + attr (@name.output) node_reference = @name } (primary_expression/identifier) @id From 2f2a4ccb601ce10b1b8adc84018717bda7994e08 Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Thu, 1 Jun 2023 10:11:18 +0200 Subject: [PATCH 117/291] convert: edge precedence --- .../src/stack-graphs.tsg | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg b/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg index 7088f8b6f..b5433f967 100644 --- a/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg +++ b/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg @@ -112,7 +112,8 @@ attribute node_symbol = node => symbol = (source-text node), source_n name: (dotted_name . (identifier) @root_name)) @stmt { - edge @stmt.after_scope -> @root_name.def, "precedence" = 1 + edge @stmt.after_scope -> @root_name.def + attr (@stmt.after_scope -> @root_name.def) precedence = 1 edge @root_name.ref -> root attr (@root_name.ref) node_reference = @root_name attr (@root_name.def) node_definition = @root_name @@ -139,7 +140,8 @@ attribute node_symbol = node => symbol = (source-text node), source_n name: (dotted_name . (identifier) @import_root_name)) @stmt { - edge @stmt.after_scope -> @import_root_name.def, "precedence" = 1 + edge @stmt.after_scope -> @import_root_name.def + attr (@stmt.after_scope -> @import_root_name.def) precedence = 1 edge @import_root_name.ref -> @import_root_name.ref_dot attr (@import_root_name.def) node_definition = @import_root_name attr (@import_root_name.ref) node_reference = @import_root_name @@ -261,7 +263,8 @@ attribute node_symbol = node => symbol = (source-text node), source_n (identifier) @leaf_name .) (wildcard_import) @star) @stmt { - edge @stmt.after_scope -> @star.ref_dot, "precedence" = 1 + edge @stmt.after_scope -> @star.ref_dot + attr (@stmt.after_scope -> @star.ref_dot) precedence = 1 edge @star.ref_dot -> @leaf_name.ref attr (@star.ref_dot) push_symbol = "." } @@ -542,7 +545,8 @@ attribute node_symbol = node => symbol = (source-text node), source_n { let statement_scope = @list::local_scope let @pattern::local_scope = @pattern.pattern_before_scope - edge statement_scope -> @pattern::local_scope, "precedence" = (+ 1 (child-index @pattern)) + edge statement_scope -> @pattern::local_scope + attr (statement_scope -> @pattern::local_scope) precedence = (+ 1 (child-index @pattern)) edge @pattern.pattern_index -> @list.input edge @pattern.input -> @pattern.pattern_index @@ -736,7 +740,8 @@ attribute node_symbol = node => symbol = (source-text node), source_n { edge @id.output -> @id::local_scope edge @id.output -> @id::class_parent_scope - edge @id::local_scope -> @id.input, "precedence" = 1 + edge @id::local_scope -> @id.input + attr (@id::local_scope -> @id.input) precedence = 1 attr (@id.input) node_definition = @id attr (@id.output) push_node = @id @@ -750,7 +755,8 @@ attribute node_symbol = node => symbol = (source-text node), source_n { edge @id.output -> @id::local_scope edge @id.output -> @id::class_parent_scope - edge @id::local_scope -> @id.input, "precedence" = 1 + edge @id::local_scope -> @id.input + attr (@id::local_scope -> @id.input) precedence = 1 attr (@id.input) node_definition = @id attr (@id.output) push_node = @id From 90399c7c91aa5abb6bd16c9b27d830c4dd290b17 Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Thu, 1 Jun 2023 10:14:54 +0200 Subject: [PATCH 118/291] convert: drop --- .../tree-sitter-stack-graphs-python/src/stack-graphs.tsg | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg b/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg index b5433f967..84e8fddb0 100644 --- a/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg +++ b/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg @@ -408,7 +408,7 @@ attribute node_symbol = node => symbol = (source-text node), source_n edge @body.before_scope -> @params.after_scope edge @body.before_scope -> @func.drop_scope edge @func.drop_scope -> @func::bottom - attr (@func.drop_scope) "drop" + attr (@func.drop_scope) type = "drop_scopes" attr (@name) node_definition = @name attr (@func.call) pop_scoped_symbol = "()" attr (@params.before_scope) "jump-to" @@ -585,7 +585,7 @@ attribute node_symbol = node => symbol = (source-text node), source_n edge @class.self_dot -> @class.members edge @class.members -> @class.member_attrs attr (@class.call) pop_scoped_symbol = "()" - attr (@class.call_drop) "drop" + attr (@class.call_drop) type = "drop_scopes" attr (@class.dot) pop_symbol = "." attr (@class.self_dot) pop_symbol = "." attr (@name) node_definition = @name From cfc004ad453f143d6f2e08bbe17228e5745e6d25 Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Thu, 1 Jun 2023 10:15:36 +0200 Subject: [PATCH 119/291] convert: endpoint --- .../tree-sitter-stack-graphs-python/src/stack-graphs.tsg | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg b/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg index 84e8fddb0..9b959b271 100644 --- a/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg +++ b/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg @@ -412,7 +412,7 @@ attribute node_symbol = node => symbol = (source-text node), source_n attr (@name) node_definition = @name attr (@func.call) pop_scoped_symbol = "()" attr (@params.before_scope) "jump-to" - attr (@func.return_value) "endpoint" + attr (@func.return_value) is_exported let @func::function_returns = @func.return_value ; Prevent functions defined inside of method bodies from being treated like methods @@ -590,7 +590,7 @@ attribute node_symbol = node => symbol = (source-text node), source_n attr (@class.self_dot) pop_symbol = "." attr (@name) node_definition = @name attr (@class.member_attrs) push_symbol = "." - attr (@class.self_scope) "endpoint" + attr (@class.self_scope) is_exported let @class::super_scope = @class.super_scope let @class::class_parent_scope = @class.parent_scope let @class::class_self_scope = @class.call_drop From b14aba9220314697c0c470ff97cc72b4b1a8d7a1 Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Thu, 1 Jun 2023 10:17:25 +0200 Subject: [PATCH 120/291] convert: definiens --- .../tree-sitter-stack-graphs-python/src/stack-graphs.tsg | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg b/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg index 9b959b271..fac43e2b3 100644 --- a/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg +++ b/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg @@ -401,7 +401,7 @@ attribute node_symbol = node => symbol = (source-text node), source_n parameters: (parameters) @params body: (block) @body) @func { - attr (@name) "definiens" = @func + attr (@name) definiens_node = @func edge @func.after_scope -> @name edge @name -> @func.call edge @func.call -> @func.return_value @@ -570,7 +570,7 @@ attribute node_symbol = node => symbol = (source-text node), source_n (class_definition name: (identifier) @name) @class { - attr (@name) "definiens" = @class + attr (@name) definiens_node = @class attr (@name) "syntax_type" = "class" edge @class.parent_scope -> @class::class_parent_scope edge @class.parent_scope -> @class::local_scope From 129aa5d3a651fb025139931aa290f15b92f1c8e6 Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Thu, 1 Jun 2023 10:18:08 +0200 Subject: [PATCH 121/291] convert: syntax type --- .../tree-sitter-stack-graphs-python/src/stack-graphs.tsg | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg b/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg index fac43e2b3..d86aba3e0 100644 --- a/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg +++ b/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg @@ -438,7 +438,7 @@ attribute node_symbol = node => symbol = (source-text node), source_n (class_definition (block (function_definition name: (_)@name))) ] { - attr (@name) "syntax_type" = "method" + attr (@name) syntax_type = "method" } [ @@ -479,7 +479,7 @@ attribute node_symbol = node => symbol = (source-text node), source_n (function_definition (block (function_definition name: (_)@name))) ] { - attr (@name) "syntax_type" = "function" + attr (@name) syntax_type = "function" } ;; @@ -571,7 +571,7 @@ attribute node_symbol = node => symbol = (source-text node), source_n name: (identifier) @name) @class { attr (@name) definiens_node = @class - attr (@name) "syntax_type" = "class" + attr (@name) syntax_type = "class" edge @class.parent_scope -> @class::class_parent_scope edge @class.parent_scope -> @class::local_scope edge @class.after_scope -> @name From dba45d9ddcc8e9bf60948f62f0ec362bf15d8014 Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Thu, 1 Jun 2023 10:18:54 +0200 Subject: [PATCH 122/291] convert: jump --- languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg b/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg index d86aba3e0..0ddad1079 100644 --- a/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg +++ b/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg @@ -411,7 +411,7 @@ attribute node_symbol = node => symbol = (source-text node), source_n attr (@func.drop_scope) type = "drop_scopes" attr (@name) node_definition = @name attr (@func.call) pop_scoped_symbol = "()" - attr (@params.before_scope) "jump-to" + edge @params.before_scope -> JUMP_TO_SCOPE_NODE attr (@func.return_value) is_exported let @func::function_returns = @func.return_value From 6bb4d577d1e811fb76204ac751fbd63db649feb0 Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Thu, 1 Jun 2023 15:38:22 +0200 Subject: [PATCH 123/291] convert: nodes --- .../src/stack-graphs.tsg | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg b/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg index 0ddad1079..1323bccba 100644 --- a/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg +++ b/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg @@ -36,6 +36,49 @@ attribute symbol_reference = symbol => type = "push_symbol", symbol = symbol attribute node_symbol = node => symbol = (source-text node), source_node = node +;; Nodes +;; ^^^^^ + +(_)@node { + node @node.after_scope + node @node.arg_index + node @node.arg_name + node @node.before_scope + node @node.call + node @node.call_drop + node @node.called + node @node.def + node @node.def_dot + node @node.dot + node @node.drop_scope + node @node.el_index + node @node.file_def + node @node.file_ref + node @node.global + node @node.global_dot + node @node.input + node @node.input_dot + node @node.member_attrs + node @node.members + node @node.new_binding_pop + node @node.new_bindings + node @node.new_bindings_pop + node @node.output + node @node.output_args + node @node.output_dot + node @node.param_index + node @node.param_name + node @node.parent_scope + node @node.pattern_before_scope + node @node.pattern_index + node @node.ref + node @node.ref_dot + node @node.return_value + node @node.self_dot + node @node.self_scope + node @node.super_scope +} + ; Modules and Imports ;--------------------- From da3728c74f269dd8240e45a49ad2090aa183527a Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Thu, 1 Jun 2023 19:49:58 +0200 Subject: [PATCH 124/291] convert: scoped variables --- .../src/stack-graphs.tsg | 101 ++++++++++-------- 1 file changed, 58 insertions(+), 43 deletions(-) diff --git a/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg b/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg index 1323bccba..896ab715a 100644 --- a/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg +++ b/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg @@ -79,6 +79,21 @@ attribute node_symbol = node => symbol = (source-text node), source_n node @node.super_scope } +;; Inherited Variables +;; ^^^^^^^^^^^^^^^^^^^ + +inherit .bottom +inherit .class_member_attr_scope +inherit .class_parent_scope +inherit .class_self_scope +inherit .function_returns +inherit .global +inherit .global_dot +inherit .grandparent_module +inherit .local_scope +inherit .parent_module +inherit .super_scope + ; Modules and Imports ;--------------------- @@ -92,7 +107,7 @@ attribute node_symbol = node => symbol = (source-text node), source_n var parent_module_ref = module_ref var grandparent_module_ref = module_ref - scan filepath { + scan FILE_PATH { "([^/]+)/" { edge module_def -> module_def.dot @@ -144,11 +159,11 @@ attribute node_symbol = node => symbol = (source-text node), source_n edge @mod.global_dot -> @mod.global attr (@mod.global_dot) push_symbol = "." - var @mod::parent_module = parent_module_ref - var @mod::grandparent_module = grandparent_module_ref - var @mod::bottom = @mod.after_scope - var @mod::global = @mod.global - var @mod::global_dot = @mod.global_dot + let @mod.parent_module = parent_module_ref + let @mod.grandparent_module = grandparent_module_ref + let @mod.bottom = @mod.after_scope + let @mod.global = @mod.global + let @mod.global_dot = @mod.global_dot } (import_statement @@ -252,14 +267,14 @@ attribute node_symbol = node => symbol = (source-text node), source_n (import_prefix) @prefix (#eq? @prefix ".")) @import { - edge @prefix.ref -> @import::parent_module + edge @prefix.ref -> @import.parent_module } (relative_import (import_prefix) @prefix (#eq? @prefix "..")) @import { - edge @prefix.ref -> @import::grandparent_module + edge @prefix.ref -> @import.grandparent_module } (relative_import @@ -367,7 +382,7 @@ attribute node_symbol = node => symbol = (source-text node), source_n ] { edge @stmt.after_scope -> @stmt.before_scope - let @stmt::local_scope = @stmt.before_scope + let @stmt.local_scope = @stmt.before_scope } [ @@ -385,7 +400,7 @@ attribute node_symbol = node => symbol = (source-text node), source_n (function_definition (block) @block) { - edge @block.before_scope -> @block::local_scope + edge @block.before_scope -> @block.local_scope } [ @@ -402,13 +417,13 @@ attribute node_symbol = node => symbol = (source-text node), source_n ]) ] @stmt { - edge @block.before_scope -> @block::local_scope + edge @block.before_scope -> @block.local_scope edge @stmt.after_scope -> @block.after_scope } (match_statement (case_clause) @block) @stmt { - let @block::local_scope = @block.before_scope + let @block.local_scope = @block.before_scope edge @block.before_scope -> @stmt.before_scope edge @stmt.after_scope -> @block.after_scope } @@ -450,17 +465,17 @@ attribute node_symbol = node => symbol = (source-text node), source_n edge @func.call -> @func.return_value edge @body.before_scope -> @params.after_scope edge @body.before_scope -> @func.drop_scope - edge @func.drop_scope -> @func::bottom + edge @func.drop_scope -> @func.bottom attr (@func.drop_scope) type = "drop_scopes" attr (@name) node_definition = @name attr (@func.call) pop_scoped_symbol = "()" edge @params.before_scope -> JUMP_TO_SCOPE_NODE attr (@func.return_value) is_exported - let @func::function_returns = @func.return_value + let @func.function_returns = @func.return_value ; Prevent functions defined inside of method bodies from being treated like methods - let @body::class_self_scope = nil - let @body::class_member_attr_scope = nil + let @body.class_self_scope = nil + let @body.class_member_attr_scope = nil } ;; @@ -534,8 +549,8 @@ attribute node_symbol = node => symbol = (source-text node), source_n . (identifier) @param) body: (block) @body) { - edge @param.input -> @param::class_self_scope - edge @param::class_member_attr_scope -> @param.output + edge @param.input -> @param.class_self_scope + edge @param.class_member_attr_scope -> @param.output edge @param.output -> @body.after_scope attr (@param.output) push_node = @param } @@ -586,10 +601,10 @@ attribute node_symbol = node => symbol = (source-text node), source_n (tuple_pattern (_) @pattern) ] @list { - let statement_scope = @list::local_scope - let @pattern::local_scope = @pattern.pattern_before_scope - edge statement_scope -> @pattern::local_scope - attr (statement_scope -> @pattern::local_scope) precedence = (+ 1 (child-index @pattern)) + let statement_scope = @list.local_scope + let @pattern.local_scope = @pattern.pattern_before_scope + edge statement_scope -> @pattern.local_scope + attr (statement_scope -> @pattern.local_scope) precedence = (plus 1 (child-index @pattern)) edge @pattern.pattern_index -> @list.input edge @pattern.input -> @pattern.pattern_index @@ -607,7 +622,7 @@ attribute node_symbol = node => symbol = (source-text node), source_n (return_statement (_) @expr) @stmt { - edge @stmt::function_returns -> @expr.output + edge @stmt.function_returns -> @expr.output } (class_definition @@ -615,8 +630,8 @@ attribute node_symbol = node => symbol = (source-text node), source_n { attr (@name) definiens_node = @class attr (@name) syntax_type = "class" - edge @class.parent_scope -> @class::class_parent_scope - edge @class.parent_scope -> @class::local_scope + edge @class.parent_scope -> @class.class_parent_scope + edge @class.parent_scope -> @class.local_scope edge @class.after_scope -> @name edge @name -> @class.call edge @name -> @class.dot @@ -634,10 +649,10 @@ attribute node_symbol = node => symbol = (source-text node), source_n attr (@name) node_definition = @name attr (@class.member_attrs) push_symbol = "." attr (@class.self_scope) is_exported - let @class::super_scope = @class.super_scope - let @class::class_parent_scope = @class.parent_scope - let @class::class_self_scope = @class.call_drop - let @class::class_member_attr_scope = @class.member_attrs + let @class.super_scope = @class.super_scope + let @class.class_parent_scope = @class.parent_scope + let @class.class_self_scope = @class.call_drop + let @class.class_member_attr_scope = @class.member_attrs } (class_definition @@ -695,7 +710,7 @@ attribute node_symbol = node => symbol = (source-text node), source_n attr (@receiver.arg_index) pop_symbol = "0" edge @receiver.arg_index -> @receiver.output - attr (@arg.arg_index) pop_symbol = (+ 1 (child-index @arg)) + attr (@arg.arg_index) pop_symbol = (plus 1 (child-index @arg)) edge @arg.arg_index -> @arg.output } @@ -724,7 +739,7 @@ attribute node_symbol = node => symbol = (source-text node), source_n (#eq? @fn-name "super") ) { - edge @call.output -> @call::super_scope + edge @call.output -> @call.super_scope } [ @@ -769,9 +784,9 @@ attribute node_symbol = node => symbol = (source-text node), source_n (primary_expression/identifier) @id { - edge @id.output -> @id::local_scope - edge @id.output -> @id::class_parent_scope - edge @id::local_scope -> @id.input + edge @id.output -> @id.local_scope + edge @id.output -> @id.class_parent_scope + edge @id.local_scope -> @id.input attr (@id.input) pop_node = @id attr (@id.output) node_reference = @id @@ -781,10 +796,10 @@ attribute node_symbol = node => symbol = (source-text node), source_n (pattern/identifier) @id { - edge @id.output -> @id::local_scope - edge @id.output -> @id::class_parent_scope - edge @id::local_scope -> @id.input - attr (@id::local_scope -> @id.input) precedence = 1 + edge @id.output -> @id.local_scope + edge @id.output -> @id.class_parent_scope + edge @id.local_scope -> @id.input + attr (@id.local_scope -> @id.input) precedence = 1 attr (@id.input) node_definition = @id attr (@id.output) push_node = @id @@ -796,10 +811,10 @@ attribute node_symbol = node => symbol = (source-text node), source_n (expression) @value alias: (as_pattern_target (primary_expression/identifier) @id)) @as_pattern { - edge @id.output -> @id::local_scope - edge @id.output -> @id::class_parent_scope - edge @id::local_scope -> @id.input - attr (@id::local_scope -> @id.input) precedence = 1 + edge @id.output -> @id.local_scope + edge @id.output -> @id.class_parent_scope + edge @id.local_scope -> @id.input + attr (@id.local_scope -> @id.input) precedence = 1 attr (@id.input) node_definition = @id attr (@id.output) push_node = @id @@ -810,7 +825,7 @@ attribute node_symbol = node => symbol = (source-text node), source_n (list) @list { edge @list.output -> @list.called - edge @list.called -> @list::global_dot + edge @list.called -> @list.global_dot attr (@list.called) push_symbol = "list" } From 39c262494bdf2252cda5c603918f0cec4fb72bf3 Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Thu, 1 Jun 2023 19:51:04 +0200 Subject: [PATCH 125/291] convert: root --- .../src/stack-graphs.tsg | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg b/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg index 896ab715a..8460d2527 100644 --- a/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg +++ b/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg @@ -148,12 +148,12 @@ inherit .super_scope } } - edge root -> @mod.file_def - edge @mod.file_ref -> root + edge ROOT_NODE -> @mod.file_def + edge @mod.file_ref -> ROOT_NODE edge module_def -> @mod.after_scope edge @mod.before_scope -> @mod.global_dot - edge @mod.global -> root + edge @mod.global -> ROOT_NODE attr (@mod.global) push_symbol = "" edge @mod.global_dot -> @mod.global @@ -172,7 +172,7 @@ inherit .super_scope { edge @stmt.after_scope -> @root_name.def attr (@stmt.after_scope -> @root_name.def) precedence = 1 - edge @root_name.ref -> root + edge @root_name.ref -> ROOT_NODE attr (@root_name.ref) node_reference = @root_name attr (@root_name.def) node_definition = @root_name } @@ -182,7 +182,7 @@ inherit .super_scope (dotted_name . (identifier) @root_name))) @stmt { edge @stmt.after_scope -> @root_name.def - edge @root_name.ref -> root + edge @root_name.ref -> ROOT_NODE attr (@root_name.ref) node_reference = @root_name } @@ -190,7 +190,7 @@ inherit .super_scope module_name: (dotted_name . (identifier) @prefix_root_name)) @stmt { - edge @prefix_root_name.ref -> root + edge @prefix_root_name.ref -> ROOT_NODE attr (@prefix_root_name.ref) node_reference = @prefix_root_name } @@ -313,7 +313,7 @@ inherit .super_scope . (identifier) @root_name)) { attr (@root_name.ref) node_reference = @root_name - edge @root_name.ref -> root + edge @root_name.ref -> ROOT_NODE } (import_from_statement From 8e05b65573ea1c148aaaaa6860105aa0440dbdd4 Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Thu, 1 Jun 2023 19:53:19 +0200 Subject: [PATCH 126/291] convert: captures --- .../src/stack-graphs.tsg | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg b/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg index 8460d2527..3746841b7 100644 --- a/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg +++ b/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg @@ -188,7 +188,7 @@ inherit .super_scope (import_from_statement module_name: (dotted_name - . (identifier) @prefix_root_name)) @stmt + . (identifier) @prefix_root_name)) { edge @prefix_root_name.ref -> ROOT_NODE attr (@prefix_root_name.ref) node_reference = @prefix_root_name @@ -209,7 +209,7 @@ inherit .super_scope (import_from_statement name: (aliased_import (dotted_name - . (identifier) @import_root_name))) @stmt + . (identifier) @import_root_name))) { edge @import_root_name.ref -> @import_root_name.ref_dot attr (@import_root_name.ref) node_reference = @import_root_name @@ -474,8 +474,8 @@ inherit .super_scope let @func.function_returns = @func.return_value ; Prevent functions defined inside of method bodies from being treated like methods - let @body.class_self_scope = nil - let @body.class_member_attr_scope = nil + let @body.class_self_scope = #null + let @body.class_member_attr_scope = #null } ;; @@ -657,7 +657,7 @@ inherit .super_scope (class_definition body: (block - (_) @last_stmt .) @body) @class + (_) @last_stmt .)) @class { edge @class.members -> @last_stmt.after_scope } @@ -718,7 +718,7 @@ inherit .super_scope arguments: (argument_list (keyword_argument name: (identifier) @name - value: (_) @val) @arg) @args) @call + value: (_) @val) @arg) @args) { edge @args -> @arg.arg_name attr (@arg.arg_name) pop_node = @name @@ -735,8 +735,8 @@ inherit .super_scope ( (call - function: (identifier) @fn-name) @call - (#eq? @fn-name "super") + function: (identifier) @_fn_name) @call + (#eq? @_fn_name "super") ) { edge @call.output -> @call.super_scope From 10e33f794042323e114132de66bff03d4f4701f6 Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Fri, 2 Jun 2023 10:45:13 +0200 Subject: [PATCH 127/291] convert: module ref and def --- .../src/stack-graphs.tsg | 73 +++++++++++-------- 1 file changed, 43 insertions(+), 30 deletions(-) diff --git a/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg b/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg index 3746841b7..019a21db6 100644 --- a/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg +++ b/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg @@ -100,51 +100,64 @@ inherit .super_scope (module) @mod { var module_def = @mod.file_def - attr (module_def) empty_source_span + + node parent_module_def_node + var parent_module_def = parent_module_def_node + var module_ref = @mod.file_ref - attr (module_ref) empty_source_span - var parent_module_def = module_def - var parent_module_ref = module_ref - var grandparent_module_ref = module_ref + + node parent_module_ref_node + var parent_module_ref = parent_module_ref_node + + node grandparent_module_ref_node + var grandparent_module_ref = grandparent_module_ref_node scan FILE_PATH { "([^/]+)/" { - edge module_def -> module_def.dot - edge module_def.dot -> module_def.next_def - edge module_ref.next_ref -> module_ref.dot - edge module_ref.dot -> module_ref - + node def_dot + attr (def_dot) pop_symbol = "." + node next_def + ; + edge module_def -> def_dot + edge def_dot -> next_def + ; attr (module_def) pop_symbol = $1 - attr (module_def.dot) pop_symbol = "." + ; + set parent_module_def = module_def + set module_def = next_def + + node ref_dot + attr (ref_dot) push_symbol = "." + node next_ref + ; + edge next_ref -> ref_dot + edge ref_dot -> module_ref + ; attr (module_ref) push_symbol = $1 - attr (module_ref.dot) push_symbol = "." - + ; set grandparent_module_ref = parent_module_ref - set parent_module_def = module_def set parent_module_ref = module_ref - set module_ref = module_ref.next_ref - set module_def = module_def.next_def - attr (module_def) empty_source_span - attr (module_ref) empty_source_span + set module_ref = next_ref } - "__init__\\.py$" + "__init__\.py$" { - attr (parent_module_def) node_definition = @mod + attr (parent_module_def) is_definition, source_node = @mod, empty_source_span } - "([^/]+)$" + "([^/]+)\.py$" { - edge module_def -> module_def.dot - edge module_def.dot -> module_def.next_def - - attr (module_def) symbol_definition = (replace $1 "\\.py" ""), source_node = @mod - attr (module_def.dot) pop_symbol = "." - - set module_def = module_def.next_def - attr (module_def) empty_source_span - attr (module_ref) empty_source_span + node def_dot + attr (def_dot) pop_symbol = "." + node next_def + ; + edge module_def -> def_dot + edge def_dot -> next_def + ; + attr (module_def) pop_symbol = $1, is_definition, source_node = @mod, empty_source_span + ; + set module_def = next_def } } From ff21d32e812d4343393909b087a77f355cb0cdb1 Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Fri, 2 Jun 2023 10:51:30 +0200 Subject: [PATCH 128/291] convert: global/global_dot --- .../src/stack-graphs.tsg | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg b/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg index 019a21db6..93515f30e 100644 --- a/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg +++ b/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg @@ -54,8 +54,6 @@ attribute node_symbol = node => symbol = (source-text node), source_n node @node.el_index node @node.file_def node @node.file_ref - node @node.global - node @node.global_dot node @node.input node @node.input_dot node @node.member_attrs @@ -165,18 +163,21 @@ inherit .super_scope edge @mod.file_ref -> ROOT_NODE edge module_def -> @mod.after_scope - edge @mod.before_scope -> @mod.global_dot - edge @mod.global -> ROOT_NODE - attr (@mod.global) push_symbol = "" + node global + node global_dot - edge @mod.global_dot -> @mod.global - attr (@mod.global_dot) push_symbol = "." + edge @mod.before_scope -> global_dot + edge global -> ROOT_NODE + attr (global) push_symbol = "" + + edge global_dot -> global + attr (global_dot) push_symbol = "." let @mod.parent_module = parent_module_ref let @mod.grandparent_module = grandparent_module_ref let @mod.bottom = @mod.after_scope - let @mod.global = @mod.global - let @mod.global_dot = @mod.global_dot + let @mod.global = global + let @mod.global_dot = global_dot } (import_statement From fb9e45cbadb164be9562d8ee1e389bb9bdc413e7 Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Fri, 2 Jun 2023 10:55:16 +0200 Subject: [PATCH 129/291] convert: syntax nodes as graph nodes --- .../src/stack-graphs.tsg | 46 +++++++++---------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg b/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg index 93515f30e..e3b54923e 100644 --- a/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg +++ b/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg @@ -259,8 +259,8 @@ inherit .super_scope ] @stmt { edge @stmt.after_scope -> @alias - edge @alias -> @name.ref - attr (@alias) node_definition = @alias + edge @alias.def -> @name.ref + attr (@alias.def) node_definition = @alias } [ @@ -473,15 +473,15 @@ inherit .super_scope parameters: (parameters) @params body: (block) @body) @func { - attr (@name) definiens_node = @func - edge @func.after_scope -> @name - edge @name -> @func.call + attr (@name.def) definiens_node = @func + edge @func.after_scope -> @name.def + edge @name.def -> @func.call edge @func.call -> @func.return_value edge @body.before_scope -> @params.after_scope edge @body.before_scope -> @func.drop_scope edge @func.drop_scope -> @func.bottom attr (@func.drop_scope) type = "drop_scopes" - attr (@name) node_definition = @name + attr (@name.def) node_definition = @name attr (@func.call) pop_scoped_symbol = "()" edge @params.before_scope -> JUMP_TO_SCOPE_NODE attr (@func.return_value) is_exported @@ -510,7 +510,7 @@ inherit .super_scope (class_definition (block (function_definition name: (_)@name))) ] { - attr (@name) syntax_type = "method" + attr (@name.def) syntax_type = "method" } [ @@ -551,7 +551,7 @@ inherit .super_scope (function_definition (block (function_definition name: (_)@name))) ] { - attr (@name) syntax_type = "function" + attr (@name.def) syntax_type = "function" } ;; @@ -586,12 +586,12 @@ inherit .super_scope value: (_) @value) @param ] { - attr (@name) node_definition = @name + attr (@name.def) node_definition = @name attr (@param.param_name) push_node = @name - edge @name -> @param.param_name - edge @name -> @param.param_index - edge @param.input -> @name - edge @name -> @value.output + edge @name.def -> @param.param_name + edge @name.def -> @param.param_index + edge @param.input -> @name.def + edge @name.def -> @value.output } [ @@ -603,11 +603,11 @@ inherit .super_scope (_) @name) @param ] { - attr (@name) node_definition = @name + attr (@name.def) node_definition = @name attr (@param.param_name) push_node = @name - edge @name -> @param.param_name - edge @name -> @param.param_index - edge @param.input -> @name + edge @name.def -> @param.param_name + edge @name.def -> @param.param_index + edge @param.input -> @name.def } [ @@ -642,13 +642,13 @@ inherit .super_scope (class_definition name: (identifier) @name) @class { - attr (@name) definiens_node = @class - attr (@name) syntax_type = "class" + attr (@name.def) definiens_node = @class + attr (@name.def) syntax_type = "class" edge @class.parent_scope -> @class.class_parent_scope edge @class.parent_scope -> @class.local_scope - edge @class.after_scope -> @name - edge @name -> @class.call - edge @name -> @class.dot + edge @class.after_scope -> @name.def + edge @name.def -> @class.call + edge @name.def -> @class.dot edge @class.dot -> @class.members edge @class.call -> @class.call_drop edge @class.call_drop -> @class.self_scope @@ -660,7 +660,7 @@ inherit .super_scope attr (@class.call_drop) type = "drop_scopes" attr (@class.dot) pop_symbol = "." attr (@class.self_dot) pop_symbol = "." - attr (@name) node_definition = @name + attr (@name.def) node_definition = @name attr (@class.member_attrs) push_symbol = "." attr (@class.self_scope) is_exported let @class.super_scope = @class.super_scope From e614b60205d9e2c784a33f6aa5cf0038c7d57a42 Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Fri, 2 Jun 2023 10:58:43 +0200 Subject: [PATCH 130/291] convert: super scope --- .../src/stack-graphs.tsg | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg b/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg index e3b54923e..f9d0fc0e2 100644 --- a/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg +++ b/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg @@ -84,13 +84,13 @@ inherit .bottom inherit .class_member_attr_scope inherit .class_parent_scope inherit .class_self_scope +inherit .class_super_scope inherit .function_returns inherit .global inherit .global_dot inherit .grandparent_module inherit .local_scope inherit .parent_module -inherit .super_scope ; Modules and Imports ;--------------------- @@ -663,10 +663,10 @@ inherit .super_scope attr (@name.def) node_definition = @name attr (@class.member_attrs) push_symbol = "." attr (@class.self_scope) is_exported - let @class.super_scope = @class.super_scope + let @class.class_member_attr_scope = @class.member_attrs let @class.class_parent_scope = @class.parent_scope let @class.class_self_scope = @class.call_drop - let @class.class_member_attr_scope = @class.member_attrs + let @class.class_super_scope = @class.super_scope } (class_definition @@ -680,7 +680,7 @@ inherit .super_scope superclasses: (argument_list (_) @superclass)) @class { - edge @class.super_scope -> @superclass.output + edge @class.class_super_scope -> @superclass.output } (decorated_definition @@ -753,7 +753,7 @@ inherit .super_scope (#eq? @_fn_name "super") ) { - edge @call.output -> @call.super_scope + edge @call.output -> @call.class_super_scope } [ From cd0255c73212b671471152336261c96d65acb20c Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Fri, 2 Jun 2023 11:15:53 +0200 Subject: [PATCH 131/291] convert: duplicate edge --- .../tree-sitter-stack-graphs-python/src/stack-graphs.tsg | 5 ----- 1 file changed, 5 deletions(-) diff --git a/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg b/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg index f9d0fc0e2..e19ffdac0 100644 --- a/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg +++ b/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg @@ -407,11 +407,6 @@ inherit .parent_module edge @stmt.before_scope -> @block.before_scope } -(block (_) @stmt . ) @block -{ - edge @block.after_scope -> @stmt.after_scope -} - (function_definition (block) @block) { edge @block.before_scope -> @block.local_scope From 307e03cb757b47041fd32ac93b463b58261a3474 Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Fri, 2 Jun 2023 11:17:10 +0200 Subject: [PATCH 132/291] convert: duplicate stanza --- .../tree-sitter-stack-graphs-python/src/stack-graphs.tsg | 8 -------- 1 file changed, 8 deletions(-) diff --git a/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg b/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg index e19ffdac0..91376a956 100644 --- a/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg +++ b/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg @@ -200,14 +200,6 @@ inherit .parent_module attr (@root_name.ref) node_reference = @root_name } -(import_from_statement - module_name: (dotted_name - . (identifier) @prefix_root_name)) -{ - edge @prefix_root_name.ref -> ROOT_NODE - attr (@prefix_root_name.ref) node_reference = @prefix_root_name -} - (import_from_statement name: (dotted_name . (identifier) @import_root_name)) @stmt From 861fbca8f7473c396251be296182d6ca45e1f1c1 Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Fri, 2 Jun 2023 11:25:15 +0200 Subject: [PATCH 133/291] convert: one more syntax node used directly as grap hnode --- languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg b/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg index 91376a956..3083dcff6 100644 --- a/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg +++ b/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg @@ -250,7 +250,7 @@ inherit .parent_module alias: (identifier) @alias)) ] @stmt { - edge @stmt.after_scope -> @alias + edge @stmt.after_scope -> @alias.def edge @alias.def -> @name.ref attr (@alias.def) node_definition = @alias } From 7aa54c8bd6bac0d694527c8dc09c50ed1be76f04 Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Fri, 2 Jun 2023 11:29:06 +0200 Subject: [PATCH 134/291] convert: toplevel dummy values for inherited variables --- .../tree-sitter-stack-graphs-python/src/stack-graphs.tsg | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg b/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg index 3083dcff6..851d358ff 100644 --- a/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg +++ b/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg @@ -178,6 +178,14 @@ inherit .parent_module let @mod.bottom = @mod.after_scope let @mod.global = global let @mod.global_dot = global_dot + + ;; add a dummy nodes for inherited variables + node @mod.class_member_attr_scope + node @mod.class_parent_scope + node @mod.class_self_scope + node @mod.class_super_scope + node @mod.function_returns + node @mod.local_scope } (import_statement From 1624d70afd320e5c061100a2167966ad0c76ccb9 Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Fri, 2 Jun 2023 11:31:11 +0200 Subject: [PATCH 135/291] convert: duplicate attribute --- .../tree-sitter-stack-graphs-python/src/stack-graphs.tsg | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg b/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg index 851d358ff..63831ff7d 100644 --- a/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg +++ b/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg @@ -782,13 +782,13 @@ inherit .parent_module (pattern/attribute attribute: (identifier) @name) { - attr (@name.input) node_definition = @name + attr (@name.input) is_definition } (primary_expression/attribute attribute: (identifier) @name) { - attr (@name.output) node_reference = @name + attr (@name.output) is_reference } (primary_expression/identifier) @id From 5318655f366e714e085dbf5fc8818310eaea26f5 Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Fri, 2 Jun 2023 11:33:55 +0200 Subject: [PATCH 136/291] convert: graph node for argument_list --- .../tree-sitter-stack-graphs-python/src/stack-graphs.tsg | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg b/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg index 63831ff7d..de8198d25 100644 --- a/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg +++ b/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg @@ -704,7 +704,7 @@ inherit .parent_module { edge @call.output -> @call.output_args edge @call.output_args -> @fn.output - attr (@call.output_args) push_scoped_symbol = "()", scope = @args + attr (@call.output_args) push_scoped_symbol = "()", scope = @args.def } (call @@ -713,7 +713,7 @@ inherit .parent_module arguments: (argument_list (expression) @arg) @args) { - edge @args -> @arg.arg_index + edge @args.def -> @arg.arg_index edge @receiver -> @receiver.arg_index attr (@receiver.arg_index) pop_symbol = "0" @@ -729,7 +729,7 @@ inherit .parent_module name: (identifier) @name value: (_) @val) @arg) @args) { - edge @args -> @arg.arg_name + edge @args.def -> @arg.arg_name attr (@arg.arg_name) pop_node = @name edge @arg.arg_name -> @val.output } @@ -737,7 +737,7 @@ inherit .parent_module (argument_list (expression) @arg) @args { - edge @args -> @arg.arg_index + edge @args.def -> @arg.arg_index attr (@arg.arg_index) pop_symbol = (child-index @arg) edge @arg.arg_index -> @arg.output } From ec0f61b6cd93232e57ccb8db0606fcf778b70b25 Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Fri, 2 Jun 2023 11:34:28 +0200 Subject: [PATCH 137/291] convert: function name --- languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg b/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg index de8198d25..105c576af 100644 --- a/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg +++ b/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg @@ -738,7 +738,7 @@ inherit .parent_module (expression) @arg) @args { edge @args.def -> @arg.arg_index - attr (@arg.arg_index) pop_symbol = (child-index @arg) + attr (@arg.arg_index) pop_symbol = (named-child-index @arg) edge @arg.arg_index -> @arg.output } From 38595361d9b6fbde5567098c98faffad26244c19 Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Fri, 2 Jun 2023 11:36:01 +0200 Subject: [PATCH 138/291] convert: export scope --- languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg | 1 + 1 file changed, 1 insertion(+) diff --git a/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg b/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg index 105c576af..d5d980e10 100644 --- a/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg +++ b/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg @@ -705,6 +705,7 @@ inherit .parent_module edge @call.output -> @call.output_args edge @call.output_args -> @fn.output attr (@call.output_args) push_scoped_symbol = "()", scope = @args.def + attr (@args.def) is_exported } (call From 02f4b5e56aa0a22a6aac52c77676c78057fefa5e Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Fri, 2 Jun 2023 11:37:19 +0200 Subject: [PATCH 139/291] convert: function name --- .../src/stack-graphs.tsg | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg b/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg index d5d980e10..8b176f66c 100644 --- a/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg +++ b/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg @@ -613,17 +613,17 @@ inherit .parent_module let statement_scope = @list.local_scope let @pattern.local_scope = @pattern.pattern_before_scope edge statement_scope -> @pattern.local_scope - attr (statement_scope -> @pattern.local_scope) precedence = (plus 1 (child-index @pattern)) + attr (statement_scope -> @pattern.local_scope) precedence = (plus 1 (named-child-index @pattern)) edge @pattern.pattern_index -> @list.input edge @pattern.input -> @pattern.pattern_index - attr (@pattern.pattern_index) push_symbol = (child-index @pattern) + attr (@pattern.pattern_index) push_symbol = (named-child-index @pattern) } (parameters (_) @param) @params { - attr (@param.param_index) push_symbol = (child-index @param) + attr (@param.param_index) push_symbol = (named-child-index @param) edge @param.param_index -> @params.before_scope edge @params.after_scope -> @param.input edge @param.param_name -> @params.before_scope @@ -720,7 +720,7 @@ inherit .parent_module attr (@receiver.arg_index) pop_symbol = "0" edge @receiver.arg_index -> @receiver.output - attr (@arg.arg_index) pop_symbol = (plus 1 (child-index @arg)) + attr (@arg.arg_index) pop_symbol = (plus 1 (named-child-index @arg)) edge @arg.arg_index -> @arg.output } @@ -758,7 +758,7 @@ inherit .parent_module ] @tuple { edge @tuple.output -> @element.el_index - attr (@element.el_index) pop_symbol = (child-index @element) + attr (@element.el_index) pop_symbol = (named-child-index @element) edge @element.el_index -> @element.output edge @tuple.new_bindings -> @element.new_bindings From 6e39e4a4787df62abf6d264ad2ef232209f36df2 Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Fri, 2 Jun 2023 11:53:22 +0200 Subject: [PATCH 140/291] convert: argument lists --- .../src/stack-graphs.tsg | 40 ++++++++++--------- 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg b/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg index 8b176f66c..8d1adf538 100644 --- a/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg +++ b/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg @@ -41,8 +41,6 @@ attribute node_symbol = node => symbol = (source-text node), source_n (_)@node { node @node.after_scope - node @node.arg_index - node @node.arg_name node @node.before_scope node @node.call node @node.call_drop @@ -702,10 +700,12 @@ inherit .parent_module function: (_) @fn arguments: (argument_list) @args) @call { + node @args.args + attr (@args.args) is_exported + edge @call.output -> @call.output_args edge @call.output_args -> @fn.output - attr (@call.output_args) push_scoped_symbol = "()", scope = @args.def - attr (@args.def) is_exported + attr (@call.output_args) push_scoped_symbol = "()", scope = @args.args } (call @@ -714,33 +714,37 @@ inherit .parent_module arguments: (argument_list (expression) @arg) @args) { - edge @args.def -> @arg.arg_index - edge @receiver -> @receiver.arg_index - - attr (@receiver.arg_index) pop_symbol = "0" - edge @receiver.arg_index -> @receiver.output + node receiver_arg_index + attr (receiver_arg_index) pop_symbol = "0" + edge @args.args -> receiver_arg_index + edge receiver_arg_index -> @receiver.output - attr (@arg.arg_index) pop_symbol = (plus 1 (named-child-index @arg)) - edge @arg.arg_index -> @arg.output + ;; FIXME the arguments will also exist with their unshifted indices because of the general + ;; rule below! + node arg_index + attr (arg_index) pop_symbol = (plus 1 (named-child-index @arg)) + edge arg_index -> @arg.output } (call arguments: (argument_list (keyword_argument name: (identifier) @name - value: (_) @val) @arg) @args) + value: (_) @val)) @args) { - edge @args.def -> @arg.arg_name - attr (@arg.arg_name) pop_node = @name - edge @arg.arg_name -> @val.output + node arg_name + edge @args.args -> arg_name + attr (arg_name) pop_node = @name + edge arg_name -> @val.output } (argument_list (expression) @arg) @args { - edge @args.def -> @arg.arg_index - attr (@arg.arg_index) pop_symbol = (named-child-index @arg) - edge @arg.arg_index -> @arg.output + node arg_index + edge @args.args -> arg_index + attr (arg_index) pop_symbol = (named-child-index @arg) + edge arg_index -> @arg.output } ( From ac61586343112446eaf3e3f1a60d6306a6402118 Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Fri, 2 Jun 2023 12:19:10 +0200 Subject: [PATCH 141/291] convert: duplicate edges --- .../tree-sitter-stack-graphs-python/src/stack-graphs.tsg | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg b/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg index 8d1adf538..090790af3 100644 --- a/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg +++ b/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg @@ -213,8 +213,6 @@ inherit .parent_module edge @stmt.after_scope -> @import_root_name.def attr (@stmt.after_scope -> @import_root_name.def) precedence = 1 edge @import_root_name.ref -> @import_root_name.ref_dot - attr (@import_root_name.def) node_definition = @import_root_name - attr (@import_root_name.ref) node_reference = @import_root_name attr (@import_root_name.ref_dot) push_symbol = "." } @@ -825,12 +823,8 @@ inherit .parent_module (expression) @value alias: (as_pattern_target (primary_expression/identifier) @id)) @as_pattern { - edge @id.output -> @id.local_scope - edge @id.output -> @id.class_parent_scope - edge @id.local_scope -> @id.input attr (@id.local_scope -> @id.input) precedence = 1 - attr (@id.input) node_definition = @id - attr (@id.output) push_node = @id + attr (@id.input) is_definition edge @as_pattern.new_bindings -> @value.new_bindings edge @as_pattern.new_bindings -> @id.new_bindings From a127916f7511bac46cac77c7ab6aa108925699dd Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Fri, 2 Jun 2023 12:33:05 +0200 Subject: [PATCH 142/291] convert: duplicate edges --- languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg | 2 -- 1 file changed, 2 deletions(-) diff --git a/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg b/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg index 090790af3..1d79c8e1f 100644 --- a/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg +++ b/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg @@ -268,8 +268,6 @@ inherit .parent_module (identifier) @leaf_name .)) ] { - attr (@leaf_name.def) node_definition = @leaf_name - attr (@leaf_name.ref) node_reference = @leaf_name edge @leaf_name.def -> @leaf_name.ref } From 6407b1dba56dfd876827ad2b8a2fb809e39a3011 Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Fri, 2 Jun 2023 12:34:46 +0200 Subject: [PATCH 143/291] convert: always create .args --- .../tree-sitter-stack-graphs-python/src/stack-graphs.tsg | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg b/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg index 1d79c8e1f..a88151398 100644 --- a/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg +++ b/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg @@ -696,9 +696,6 @@ inherit .parent_module function: (_) @fn arguments: (argument_list) @args) @call { - node @args.args - attr (@args.args) is_exported - edge @call.output -> @call.output_args edge @call.output_args -> @fn.output attr (@call.output_args) push_scoped_symbol = "()", scope = @args.args @@ -734,6 +731,11 @@ inherit .parent_module edge arg_name -> @val.output } +(argument_list) @args { + node @args.args + attr (@args.args) is_exported +} + (argument_list (expression) @arg) @args { From d5d312ecebab9f351b2727d2af93e6fe009fd0d7 Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Fri, 2 Jun 2023 12:36:13 +0200 Subject: [PATCH 144/291] Enable tests --- .../test/{legacy.skip => }/aliased_imports.py | 0 .../test/{legacy.skip => }/attributes.py | 0 .../test/{legacy.skip => }/blocks.py | 0 .../test/{legacy.skip => }/chained_functions.py | 0 .../{legacy.skip/chained_methods.py => chained_methods.py.skip} | 0 .../test/{legacy.skip => }/class_members.py | 0 .../test/{legacy.skip => }/decorators.py | 0 .../test/{legacy.skip => }/exceptions.py | 0 .../test/{legacy.skip => }/functions.py | 0 .../test/{legacy.skip => }/imported_functions.py | 0 .../test/{legacy.skip => }/imports.py | 0 .../test/{legacy.skip => }/instance_members.py | 0 .../test/{legacy.skip => }/loops.py | 0 .../test/{legacy.skip => }/many_definitions.py | 0 .../test/{legacy.skip => }/pattern_matching.py | 0 .../test/{legacy.skip => }/redundant_reexport.py | 0 .../test/{legacy.skip => }/relative_imports.py | 0 .../test/{legacy.skip => }/statement_bindings.py | 0 .../test/{legacy.skip => }/superclasses.py | 0 .../test/{legacy.skip => }/test.py | 0 .../test/{legacy.skip => }/tuples.py | 0 .../test/{legacy.skip => }/wildcard_import.py | 0 22 files changed, 0 insertions(+), 0 deletions(-) rename languages/tree-sitter-stack-graphs-python/test/{legacy.skip => }/aliased_imports.py (100%) rename languages/tree-sitter-stack-graphs-python/test/{legacy.skip => }/attributes.py (100%) rename languages/tree-sitter-stack-graphs-python/test/{legacy.skip => }/blocks.py (100%) rename languages/tree-sitter-stack-graphs-python/test/{legacy.skip => }/chained_functions.py (100%) rename languages/tree-sitter-stack-graphs-python/test/{legacy.skip/chained_methods.py => chained_methods.py.skip} (100%) rename languages/tree-sitter-stack-graphs-python/test/{legacy.skip => }/class_members.py (100%) rename languages/tree-sitter-stack-graphs-python/test/{legacy.skip => }/decorators.py (100%) rename languages/tree-sitter-stack-graphs-python/test/{legacy.skip => }/exceptions.py (100%) rename languages/tree-sitter-stack-graphs-python/test/{legacy.skip => }/functions.py (100%) rename languages/tree-sitter-stack-graphs-python/test/{legacy.skip => }/imported_functions.py (100%) rename languages/tree-sitter-stack-graphs-python/test/{legacy.skip => }/imports.py (100%) rename languages/tree-sitter-stack-graphs-python/test/{legacy.skip => }/instance_members.py (100%) rename languages/tree-sitter-stack-graphs-python/test/{legacy.skip => }/loops.py (100%) rename languages/tree-sitter-stack-graphs-python/test/{legacy.skip => }/many_definitions.py (100%) rename languages/tree-sitter-stack-graphs-python/test/{legacy.skip => }/pattern_matching.py (100%) rename languages/tree-sitter-stack-graphs-python/test/{legacy.skip => }/redundant_reexport.py (100%) rename languages/tree-sitter-stack-graphs-python/test/{legacy.skip => }/relative_imports.py (100%) rename languages/tree-sitter-stack-graphs-python/test/{legacy.skip => }/statement_bindings.py (100%) rename languages/tree-sitter-stack-graphs-python/test/{legacy.skip => }/superclasses.py (100%) rename languages/tree-sitter-stack-graphs-python/test/{legacy.skip => }/test.py (100%) rename languages/tree-sitter-stack-graphs-python/test/{legacy.skip => }/tuples.py (100%) rename languages/tree-sitter-stack-graphs-python/test/{legacy.skip => }/wildcard_import.py (100%) diff --git a/languages/tree-sitter-stack-graphs-python/test/legacy.skip/aliased_imports.py b/languages/tree-sitter-stack-graphs-python/test/aliased_imports.py similarity index 100% rename from languages/tree-sitter-stack-graphs-python/test/legacy.skip/aliased_imports.py rename to languages/tree-sitter-stack-graphs-python/test/aliased_imports.py diff --git a/languages/tree-sitter-stack-graphs-python/test/legacy.skip/attributes.py b/languages/tree-sitter-stack-graphs-python/test/attributes.py similarity index 100% rename from languages/tree-sitter-stack-graphs-python/test/legacy.skip/attributes.py rename to languages/tree-sitter-stack-graphs-python/test/attributes.py diff --git a/languages/tree-sitter-stack-graphs-python/test/legacy.skip/blocks.py b/languages/tree-sitter-stack-graphs-python/test/blocks.py similarity index 100% rename from languages/tree-sitter-stack-graphs-python/test/legacy.skip/blocks.py rename to languages/tree-sitter-stack-graphs-python/test/blocks.py diff --git a/languages/tree-sitter-stack-graphs-python/test/legacy.skip/chained_functions.py b/languages/tree-sitter-stack-graphs-python/test/chained_functions.py similarity index 100% rename from languages/tree-sitter-stack-graphs-python/test/legacy.skip/chained_functions.py rename to languages/tree-sitter-stack-graphs-python/test/chained_functions.py diff --git a/languages/tree-sitter-stack-graphs-python/test/legacy.skip/chained_methods.py b/languages/tree-sitter-stack-graphs-python/test/chained_methods.py.skip similarity index 100% rename from languages/tree-sitter-stack-graphs-python/test/legacy.skip/chained_methods.py rename to languages/tree-sitter-stack-graphs-python/test/chained_methods.py.skip diff --git a/languages/tree-sitter-stack-graphs-python/test/legacy.skip/class_members.py b/languages/tree-sitter-stack-graphs-python/test/class_members.py similarity index 100% rename from languages/tree-sitter-stack-graphs-python/test/legacy.skip/class_members.py rename to languages/tree-sitter-stack-graphs-python/test/class_members.py diff --git a/languages/tree-sitter-stack-graphs-python/test/legacy.skip/decorators.py b/languages/tree-sitter-stack-graphs-python/test/decorators.py similarity index 100% rename from languages/tree-sitter-stack-graphs-python/test/legacy.skip/decorators.py rename to languages/tree-sitter-stack-graphs-python/test/decorators.py diff --git a/languages/tree-sitter-stack-graphs-python/test/legacy.skip/exceptions.py b/languages/tree-sitter-stack-graphs-python/test/exceptions.py similarity index 100% rename from languages/tree-sitter-stack-graphs-python/test/legacy.skip/exceptions.py rename to languages/tree-sitter-stack-graphs-python/test/exceptions.py diff --git a/languages/tree-sitter-stack-graphs-python/test/legacy.skip/functions.py b/languages/tree-sitter-stack-graphs-python/test/functions.py similarity index 100% rename from languages/tree-sitter-stack-graphs-python/test/legacy.skip/functions.py rename to languages/tree-sitter-stack-graphs-python/test/functions.py diff --git a/languages/tree-sitter-stack-graphs-python/test/legacy.skip/imported_functions.py b/languages/tree-sitter-stack-graphs-python/test/imported_functions.py similarity index 100% rename from languages/tree-sitter-stack-graphs-python/test/legacy.skip/imported_functions.py rename to languages/tree-sitter-stack-graphs-python/test/imported_functions.py diff --git a/languages/tree-sitter-stack-graphs-python/test/legacy.skip/imports.py b/languages/tree-sitter-stack-graphs-python/test/imports.py similarity index 100% rename from languages/tree-sitter-stack-graphs-python/test/legacy.skip/imports.py rename to languages/tree-sitter-stack-graphs-python/test/imports.py diff --git a/languages/tree-sitter-stack-graphs-python/test/legacy.skip/instance_members.py b/languages/tree-sitter-stack-graphs-python/test/instance_members.py similarity index 100% rename from languages/tree-sitter-stack-graphs-python/test/legacy.skip/instance_members.py rename to languages/tree-sitter-stack-graphs-python/test/instance_members.py diff --git a/languages/tree-sitter-stack-graphs-python/test/legacy.skip/loops.py b/languages/tree-sitter-stack-graphs-python/test/loops.py similarity index 100% rename from languages/tree-sitter-stack-graphs-python/test/legacy.skip/loops.py rename to languages/tree-sitter-stack-graphs-python/test/loops.py diff --git a/languages/tree-sitter-stack-graphs-python/test/legacy.skip/many_definitions.py b/languages/tree-sitter-stack-graphs-python/test/many_definitions.py similarity index 100% rename from languages/tree-sitter-stack-graphs-python/test/legacy.skip/many_definitions.py rename to languages/tree-sitter-stack-graphs-python/test/many_definitions.py diff --git a/languages/tree-sitter-stack-graphs-python/test/legacy.skip/pattern_matching.py b/languages/tree-sitter-stack-graphs-python/test/pattern_matching.py similarity index 100% rename from languages/tree-sitter-stack-graphs-python/test/legacy.skip/pattern_matching.py rename to languages/tree-sitter-stack-graphs-python/test/pattern_matching.py diff --git a/languages/tree-sitter-stack-graphs-python/test/legacy.skip/redundant_reexport.py b/languages/tree-sitter-stack-graphs-python/test/redundant_reexport.py similarity index 100% rename from languages/tree-sitter-stack-graphs-python/test/legacy.skip/redundant_reexport.py rename to languages/tree-sitter-stack-graphs-python/test/redundant_reexport.py diff --git a/languages/tree-sitter-stack-graphs-python/test/legacy.skip/relative_imports.py b/languages/tree-sitter-stack-graphs-python/test/relative_imports.py similarity index 100% rename from languages/tree-sitter-stack-graphs-python/test/legacy.skip/relative_imports.py rename to languages/tree-sitter-stack-graphs-python/test/relative_imports.py diff --git a/languages/tree-sitter-stack-graphs-python/test/legacy.skip/statement_bindings.py b/languages/tree-sitter-stack-graphs-python/test/statement_bindings.py similarity index 100% rename from languages/tree-sitter-stack-graphs-python/test/legacy.skip/statement_bindings.py rename to languages/tree-sitter-stack-graphs-python/test/statement_bindings.py diff --git a/languages/tree-sitter-stack-graphs-python/test/legacy.skip/superclasses.py b/languages/tree-sitter-stack-graphs-python/test/superclasses.py similarity index 100% rename from languages/tree-sitter-stack-graphs-python/test/legacy.skip/superclasses.py rename to languages/tree-sitter-stack-graphs-python/test/superclasses.py diff --git a/languages/tree-sitter-stack-graphs-python/test/legacy.skip/test.py b/languages/tree-sitter-stack-graphs-python/test/test.py similarity index 100% rename from languages/tree-sitter-stack-graphs-python/test/legacy.skip/test.py rename to languages/tree-sitter-stack-graphs-python/test/test.py diff --git a/languages/tree-sitter-stack-graphs-python/test/legacy.skip/tuples.py b/languages/tree-sitter-stack-graphs-python/test/tuples.py similarity index 100% rename from languages/tree-sitter-stack-graphs-python/test/legacy.skip/tuples.py rename to languages/tree-sitter-stack-graphs-python/test/tuples.py diff --git a/languages/tree-sitter-stack-graphs-python/test/legacy.skip/wildcard_import.py b/languages/tree-sitter-stack-graphs-python/test/wildcard_import.py similarity index 100% rename from languages/tree-sitter-stack-graphs-python/test/legacy.skip/wildcard_import.py rename to languages/tree-sitter-stack-graphs-python/test/wildcard_import.py From 8d8c7e07b4ca3e9515af1bf58c2f1e22e036e332 Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Fri, 2 Jun 2023 19:06:30 +0200 Subject: [PATCH 145/291] convert: comment out syntax_type and definiens_node which are not yet supported --- .../src/stack-graphs.tsg | 62 +++++++++---------- 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg b/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg index a88151398..23b69b499 100644 --- a/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg +++ b/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg @@ -462,7 +462,7 @@ inherit .parent_module parameters: (parameters) @params body: (block) @body) @func { - attr (@name.def) definiens_node = @func +; attr (@name.def) definiens_node = @func edge @func.after_scope -> @name.def edge @name.def -> @func.call edge @func.call -> @func.return_value @@ -495,52 +495,52 @@ inherit .parent_module ;; [ - (class_definition (block (decorated_definition (function_definition name: (_)@name)))) - (class_definition (block (function_definition name: (_)@name))) + (class_definition (block (decorated_definition (function_definition name: (_)@_name)))) + (class_definition (block (function_definition name: (_)@_name))) ] { - attr (@name.def) syntax_type = "method" +; attr (@name.def) syntax_type = "method" } [ - (module (decorated_definition (function_definition name: (_)@name))) - (module (function_definition name: (_)@name)) + (module (decorated_definition (function_definition name: (_)@_name))) + (module (function_definition name: (_)@_name)) - (if_statement (block (decorated_definition (function_definition name: (_)@name)))) - (if_statement (block (function_definition name: (_)@name))) + (if_statement (block (decorated_definition (function_definition name: (_)@_name)))) + (if_statement (block (function_definition name: (_)@_name))) - (elif_clause (block (decorated_definition (function_definition name: (_)@name)))) - (elif_clause (block (function_definition name: (_)@name))) + (elif_clause (block (decorated_definition (function_definition name: (_)@_name)))) + (elif_clause (block (function_definition name: (_)@_name))) - (else_clause (block (decorated_definition (function_definition name: (_)@name)))) - (else_clause (block (function_definition name: (_)@name))) + (else_clause (block (decorated_definition (function_definition name: (_)@_name)))) + (else_clause (block (function_definition name: (_)@_name))) - (case_clause (block (decorated_definition (function_definition name: (_)@name)))) - (case_clause (block (function_definition name: (_)@name))) + (case_clause (block (decorated_definition (function_definition name: (_)@_name)))) + (case_clause (block (function_definition name: (_)@_name))) - (for_statement (block (decorated_definition (function_definition name: (_)@name)))) - (for_statement (block (function_definition name: (_)@name))) + (for_statement (block (decorated_definition (function_definition name: (_)@_name)))) + (for_statement (block (function_definition name: (_)@_name))) - (while_statement (block (decorated_definition (function_definition name: (_)@name)))) - (while_statement (block (function_definition name: (_)@name))) + (while_statement (block (decorated_definition (function_definition name: (_)@_name)))) + (while_statement (block (function_definition name: (_)@_name))) - (try_statement (block (decorated_definition (function_definition name: (_)@name)))) - (try_statement (block (function_definition name: (_)@name))) + (try_statement (block (decorated_definition (function_definition name: (_)@_name)))) + (try_statement (block (function_definition name: (_)@_name))) - (except_clause (block (decorated_definition (function_definition name: (_)@name)))) - (except_clause (block (function_definition name: (_)@name))) + (except_clause (block (decorated_definition (function_definition name: (_)@_name)))) + (except_clause (block (function_definition name: (_)@_name))) - (finally_clause (block (decorated_definition (function_definition name: (_)@name)))) - (finally_clause (block (function_definition name: (_)@name))) + (finally_clause (block (decorated_definition (function_definition name: (_)@_name)))) + (finally_clause (block (function_definition name: (_)@_name))) - (with_statement (block (decorated_definition (function_definition name: (_)@name)))) - (with_statement (block (function_definition name: (_)@name))) + (with_statement (block (decorated_definition (function_definition name: (_)@_name)))) + (with_statement (block (function_definition name: (_)@_name))) - (function_definition (block (decorated_definition (function_definition name: (_)@name)))) - (function_definition (block (function_definition name: (_)@name))) + (function_definition (block (decorated_definition (function_definition name: (_)@_name)))) + (function_definition (block (function_definition name: (_)@_name))) ] { - attr (@name.def) syntax_type = "function" +; attr (@name.def) syntax_type = "function" } ;; @@ -631,8 +631,8 @@ inherit .parent_module (class_definition name: (identifier) @name) @class { - attr (@name.def) definiens_node = @class - attr (@name.def) syntax_type = "class" +; attr (@name.def) definiens_node = @class +; attr (@name.def) syntax_type = "class" edge @class.parent_scope -> @class.class_parent_scope edge @class.parent_scope -> @class.local_scope edge @class.after_scope -> @name.def From e44bad923c8f91e788e6a3ed18e093e6ca7f4ba4 Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Wed, 15 Nov 2023 12:06:56 +0100 Subject: [PATCH 146/291] Re-enable syntax_type and definiens_node --- .../src/stack-graphs.tsg | 62 +++++++++---------- 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg b/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg index 23b69b499..a88151398 100644 --- a/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg +++ b/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg @@ -462,7 +462,7 @@ inherit .parent_module parameters: (parameters) @params body: (block) @body) @func { -; attr (@name.def) definiens_node = @func + attr (@name.def) definiens_node = @func edge @func.after_scope -> @name.def edge @name.def -> @func.call edge @func.call -> @func.return_value @@ -495,52 +495,52 @@ inherit .parent_module ;; [ - (class_definition (block (decorated_definition (function_definition name: (_)@_name)))) - (class_definition (block (function_definition name: (_)@_name))) + (class_definition (block (decorated_definition (function_definition name: (_)@name)))) + (class_definition (block (function_definition name: (_)@name))) ] { -; attr (@name.def) syntax_type = "method" + attr (@name.def) syntax_type = "method" } [ - (module (decorated_definition (function_definition name: (_)@_name))) - (module (function_definition name: (_)@_name)) + (module (decorated_definition (function_definition name: (_)@name))) + (module (function_definition name: (_)@name)) - (if_statement (block (decorated_definition (function_definition name: (_)@_name)))) - (if_statement (block (function_definition name: (_)@_name))) + (if_statement (block (decorated_definition (function_definition name: (_)@name)))) + (if_statement (block (function_definition name: (_)@name))) - (elif_clause (block (decorated_definition (function_definition name: (_)@_name)))) - (elif_clause (block (function_definition name: (_)@_name))) + (elif_clause (block (decorated_definition (function_definition name: (_)@name)))) + (elif_clause (block (function_definition name: (_)@name))) - (else_clause (block (decorated_definition (function_definition name: (_)@_name)))) - (else_clause (block (function_definition name: (_)@_name))) + (else_clause (block (decorated_definition (function_definition name: (_)@name)))) + (else_clause (block (function_definition name: (_)@name))) - (case_clause (block (decorated_definition (function_definition name: (_)@_name)))) - (case_clause (block (function_definition name: (_)@_name))) + (case_clause (block (decorated_definition (function_definition name: (_)@name)))) + (case_clause (block (function_definition name: (_)@name))) - (for_statement (block (decorated_definition (function_definition name: (_)@_name)))) - (for_statement (block (function_definition name: (_)@_name))) + (for_statement (block (decorated_definition (function_definition name: (_)@name)))) + (for_statement (block (function_definition name: (_)@name))) - (while_statement (block (decorated_definition (function_definition name: (_)@_name)))) - (while_statement (block (function_definition name: (_)@_name))) + (while_statement (block (decorated_definition (function_definition name: (_)@name)))) + (while_statement (block (function_definition name: (_)@name))) - (try_statement (block (decorated_definition (function_definition name: (_)@_name)))) - (try_statement (block (function_definition name: (_)@_name))) + (try_statement (block (decorated_definition (function_definition name: (_)@name)))) + (try_statement (block (function_definition name: (_)@name))) - (except_clause (block (decorated_definition (function_definition name: (_)@_name)))) - (except_clause (block (function_definition name: (_)@_name))) + (except_clause (block (decorated_definition (function_definition name: (_)@name)))) + (except_clause (block (function_definition name: (_)@name))) - (finally_clause (block (decorated_definition (function_definition name: (_)@_name)))) - (finally_clause (block (function_definition name: (_)@_name))) + (finally_clause (block (decorated_definition (function_definition name: (_)@name)))) + (finally_clause (block (function_definition name: (_)@name))) - (with_statement (block (decorated_definition (function_definition name: (_)@_name)))) - (with_statement (block (function_definition name: (_)@_name))) + (with_statement (block (decorated_definition (function_definition name: (_)@name)))) + (with_statement (block (function_definition name: (_)@name))) - (function_definition (block (decorated_definition (function_definition name: (_)@_name)))) - (function_definition (block (function_definition name: (_)@_name))) + (function_definition (block (decorated_definition (function_definition name: (_)@name)))) + (function_definition (block (function_definition name: (_)@name))) ] { -; attr (@name.def) syntax_type = "function" + attr (@name.def) syntax_type = "function" } ;; @@ -631,8 +631,8 @@ inherit .parent_module (class_definition name: (identifier) @name) @class { -; attr (@name.def) definiens_node = @class -; attr (@name.def) syntax_type = "class" + attr (@name.def) definiens_node = @class + attr (@name.def) syntax_type = "class" edge @class.parent_scope -> @class.class_parent_scope edge @class.parent_scope -> @class.local_scope edge @class.after_scope -> @name.def From 5a63def5d66fd5ef048d219f3abf3525dbc7e380 Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Wed, 15 Nov 2023 12:07:26 +0100 Subject: [PATCH 147/291] Don't crash visualization on self edges --- stack-graphs/src/visualization/visualization.js | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/stack-graphs/src/visualization/visualization.js b/stack-graphs/src/visualization/visualization.js index 116850791..6fdab1ef6 100644 --- a/stack-graphs/src/visualization/visualization.js +++ b/stack-graphs/src/visualization/visualization.js @@ -24,6 +24,7 @@ class StackGraph { this.graph = graph; this.paths = paths; + this.cleanup_data(); this.compute_data(); this.current_node = null; @@ -33,6 +34,19 @@ class StackGraph { this.render(); } + cleanup_data() { + let idx = 0; + while (idx < this.graph.edges.length) { + let edge = this.graph.edges[idx]; + if (edge.source.file === edge.sink.file && edge.source.local_id === edge.sink.local_id) { + console.log("ignoring self loop", edge); + this.graph.edges.splice(idx, 1); + } else { + idx += 1; + } + } + } + compute_data() { this.F = {}; this.ID = {}; @@ -47,7 +61,6 @@ class StackGraph { const file = graph.files[i]; this.F[file] = i; } - console.log(this.F); } compute_node_data() { From fe70ba08f34bfec2aa9cb485117ba4ceb827fc65 Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Wed, 15 Nov 2023 12:21:09 +0100 Subject: [PATCH 148/291] Cleanup accidental self loop --- .../src/stack-graphs.tsg | 20 +++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg b/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg index a88151398..d538431b0 100644 --- a/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg +++ b/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg @@ -64,7 +64,6 @@ attribute node_symbol = node => symbol = (source-text node), source_n node @node.output_dot node @node.param_index node @node.param_name - node @node.parent_scope node @node.pattern_before_scope node @node.pattern_index node @node.ref @@ -633,8 +632,6 @@ inherit .parent_module { attr (@name.def) definiens_node = @class attr (@name.def) syntax_type = "class" - edge @class.parent_scope -> @class.class_parent_scope - edge @class.parent_scope -> @class.local_scope edge @class.after_scope -> @name.def edge @name.def -> @class.call edge @name.def -> @class.dot @@ -652,10 +649,17 @@ inherit .parent_module attr (@name.def) node_definition = @name attr (@class.member_attrs) push_symbol = "." attr (@class.self_scope) is_exported - let @class.class_member_attr_scope = @class.member_attrs - let @class.class_parent_scope = @class.parent_scope - let @class.class_self_scope = @class.call_drop - let @class.class_super_scope = @class.super_scope +} + +(class_definition + body: (_) @body) @class +{ + let @body.class_member_attr_scope = @class.member_attrs + node @body.class_parent_scope + edge @body.class_parent_scope -> @class.class_parent_scope + edge @body.class_parent_scope -> @class.local_scope + let @body.class_self_scope = @class.call_drop + let @body.class_super_scope = @class.super_scope } (class_definition @@ -669,7 +673,7 @@ inherit .parent_module superclasses: (argument_list (_) @superclass)) @class { - edge @class.class_super_scope -> @superclass.output + edge @class.super_scope -> @superclass.output } (decorated_definition From dbfb1720f2347ba6afb38cab6c27402c2d10bca1 Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Wed, 15 Nov 2023 12:40:08 +0100 Subject: [PATCH 149/291] Fix module start lines and do not expect definitions at assertion position --- .../test/aliased_imports.py | 2 +- .../tree-sitter-stack-graphs-python/test/imports.py | 10 +++++----- .../test/relative_imports.py | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/languages/tree-sitter-stack-graphs-python/test/aliased_imports.py b/languages/tree-sitter-stack-graphs-python/test/aliased_imports.py index d988757a5..0373a33be 100644 --- a/languages/tree-sitter-stack-graphs-python/test/aliased_imports.py +++ b/languages/tree-sitter-stack-graphs-python/test/aliased_imports.py @@ -24,7 +24,7 @@ class D: # ^ defined: print f.B -# ^ defined: 2, 15 +# ^ defined: 3, 15 # ^ defined: 7 print foo diff --git a/languages/tree-sitter-stack-graphs-python/test/imports.py b/languages/tree-sitter-stack-graphs-python/test/imports.py index 2f21795da..96d561813 100644 --- a/languages/tree-sitter-stack-graphs-python/test/imports.py +++ b/languages/tree-sitter-stack-graphs-python/test/imports.py @@ -15,17 +15,17 @@ #------ path: main.py -------------# from one.two import d, e.c -# ^ defined: 2 -# ^ defined: 6, 17 -# ^ defined: 4, 8, 17 +# ^ defined: 3 +# ^ defined: 6 +# ^ defined: 4, 8 import three -# ^ defined: 11, 22 +# ^ defined: 12 print(d, e.c) # ^ defined: 6, 17 # ^ defined: 4, 17 print three.f -# ^ defined: 11, 22 +# ^ defined: 12, 22 # ^ defined: 13 diff --git a/languages/tree-sitter-stack-graphs-python/test/relative_imports.py b/languages/tree-sitter-stack-graphs-python/test/relative_imports.py index 2d6e14eb4..f14571575 100644 --- a/languages/tree-sitter-stack-graphs-python/test/relative_imports.py +++ b/languages/tree-sitter-stack-graphs-python/test/relative_imports.py @@ -6,7 +6,7 @@ from ..c.d import D print a.A -# ^ defined: 3, 24 +# ^ defined: 3, 25 # ^ defined: 26 print B.bee @@ -52,9 +52,9 @@ class D: # module from .g import G -# ^ defined: 49, 54 +# ^ defined: 49 from ..c import see -# ^ defined: 37, 57 +# ^ defined: 37 E = 1 From 2d8fd6bf0a9fd064782a8a4900d4b75276de9772 Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Wed, 15 Nov 2023 12:47:47 +0100 Subject: [PATCH 150/291] Factor out identifier rules --- .../src/stack-graphs.tsg | 25 +++++++------------ 1 file changed, 9 insertions(+), 16 deletions(-) diff --git a/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg b/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg index d538431b0..80a0a2625 100644 --- a/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg +++ b/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg @@ -192,17 +192,13 @@ inherit .parent_module edge @stmt.after_scope -> @root_name.def attr (@stmt.after_scope -> @root_name.def) precedence = 1 edge @root_name.ref -> ROOT_NODE - attr (@root_name.ref) node_reference = @root_name - attr (@root_name.def) node_definition = @root_name } (import_statement name: (aliased_import - (dotted_name . (identifier) @root_name))) @stmt + (dotted_name . (identifier) @root_name))) { - edge @stmt.after_scope -> @root_name.def edge @root_name.ref -> ROOT_NODE - attr (@root_name.ref) node_reference = @root_name } (import_from_statement @@ -221,7 +217,6 @@ inherit .parent_module . (identifier) @import_root_name))) { edge @import_root_name.ref -> @import_root_name.ref_dot - attr (@import_root_name.ref) node_reference = @import_root_name attr (@import_root_name.ref_dot) push_symbol = "." } @@ -255,7 +250,6 @@ inherit .parent_module { edge @stmt.after_scope -> @alias.def edge @alias.def -> @name.ref - attr (@alias.def) node_definition = @alias } [ @@ -289,7 +283,6 @@ inherit .parent_module (dotted_name . (identifier) @name)) { - attr (@name.ref) node_reference = @name attr (@name.ref_dot) push_symbol = "." edge @name.ref -> @name.ref_dot edge @name.ref_dot -> @prefix.ref @@ -309,7 +302,6 @@ inherit .parent_module (identifier) @child_name)) ] { - attr (@child_name.ref) node_reference = @child_name attr (@child_name.ref_dot) push_symbol = "." edge @child_name.ref -> @child_name.ref_dot edge @child_name.ref_dot -> @parent_name.ref @@ -319,7 +311,6 @@ inherit .parent_module module_name: (dotted_name . (identifier) @root_name)) { - attr (@root_name.ref) node_reference = @root_name edge @root_name.ref -> ROOT_NODE } @@ -357,8 +348,6 @@ inherit .parent_module edge @child_name.ref_dot -> @parent_name.ref edge @parent_name.def -> @parent_name.def_dot edge @parent_name.def_dot -> @child_name.def - attr (@child_name.def) node_definition = @child_name - attr (@child_name.ref) node_reference = @child_name attr (@parent_name.def_dot) pop_symbol = "." attr (@child_name.ref_dot) push_symbol = "." } @@ -442,6 +431,10 @@ inherit .parent_module ; Definitions ;------------- +(identifier) @name { + attr (@name.def) node_definition = @name +} + [ (assignment left: (_) @pattern @@ -469,7 +462,6 @@ inherit .parent_module edge @body.before_scope -> @func.drop_scope edge @func.drop_scope -> @func.bottom attr (@func.drop_scope) type = "drop_scopes" - attr (@name.def) node_definition = @name attr (@func.call) pop_scoped_symbol = "()" edge @params.before_scope -> JUMP_TO_SCOPE_NODE attr (@func.return_value) is_exported @@ -574,7 +566,6 @@ inherit .parent_module value: (_) @value) @param ] { - attr (@name.def) node_definition = @name attr (@param.param_name) push_node = @name edge @name.def -> @param.param_name edge @name.def -> @param.param_index @@ -591,7 +582,6 @@ inherit .parent_module (_) @name) @param ] { - attr (@name.def) node_definition = @name attr (@param.param_name) push_node = @name edge @name.def -> @param.param_name edge @name.def -> @param.param_index @@ -646,7 +636,6 @@ inherit .parent_module attr (@class.call_drop) type = "drop_scopes" attr (@class.dot) pop_symbol = "." attr (@class.self_dot) pop_symbol = "." - attr (@name.def) node_definition = @name attr (@class.member_attrs) push_symbol = "." attr (@class.self_scope) is_exported } @@ -696,6 +685,10 @@ inherit .parent_module ; Expressions ;------------- +(identifier) @name { + attr (@name.ref) node_reference = @name +} + (call function: (_) @fn arguments: (argument_list) @args) @call From fd57fbd4b462d82889464984b1e9fdecd974f668 Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Wed, 15 Nov 2023 13:07:03 +0100 Subject: [PATCH 151/291] Add bug test --- .../relative_import_resolves_to_itself.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 languages/tree-sitter-stack-graphs-python/test/bugs.skip/relative_import_resolves_to_itself.py diff --git a/languages/tree-sitter-stack-graphs-python/test/bugs.skip/relative_import_resolves_to_itself.py b/languages/tree-sitter-stack-graphs-python/test/bugs.skip/relative_import_resolves_to_itself.py new file mode 100644 index 000000000..838595ab7 --- /dev/null +++ b/languages/tree-sitter-stack-graphs-python/test/bugs.skip/relative_import_resolves_to_itself.py @@ -0,0 +1,14 @@ +#--- path: a/__init__.py ---# +from . import b +# ^ defined: 6 + +#--- path: a/b/__init__.py ---# +B = 'b' + +#--- path: main.py ---# +from a import b +# ^ defined: 6 + +print b.B +# ^ defined: 9, 6 +# ^ defined: 6 \ No newline at end of file From 2f619fa3d72524bca0d7eb64db37596d01eed036 Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Wed, 15 Nov 2023 13:10:20 +0100 Subject: [PATCH 152/291] Fix Python grammar versions --- languages/tree-sitter-stack-graphs-python/Cargo.toml | 2 +- tree-sitter-stack-graphs/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/languages/tree-sitter-stack-graphs-python/Cargo.toml b/languages/tree-sitter-stack-graphs-python/Cargo.toml index 9270879c0..ca123cc0f 100644 --- a/languages/tree-sitter-stack-graphs-python/Cargo.toml +++ b/languages/tree-sitter-stack-graphs-python/Cargo.toml @@ -31,7 +31,7 @@ cli = ["anyhow", "clap", "tree-sitter-stack-graphs/cli"] anyhow = { version = "1.0", optional = true } clap = { version = "4", optional = true, features = ["derive"] } tree-sitter-stack-graphs = { version = "0.7", path = "../../tree-sitter-stack-graphs" } -tree-sitter-python = "0.20.2" +tree-sitter-python = "=0.20.2" [dev-dependencies] anyhow = "1.0" diff --git a/tree-sitter-stack-graphs/Cargo.toml b/tree-sitter-stack-graphs/Cargo.toml index 2de26a86f..d5429304a 100644 --- a/tree-sitter-stack-graphs/Cargo.toml +++ b/tree-sitter-stack-graphs/Cargo.toml @@ -82,4 +82,4 @@ walkdir = { version = "2.3", optional = true } [dev-dependencies] pretty_assertions = "0.7" -tree-sitter-python = "0.19.1" +tree-sitter-python = "=0.19.1" From aae723f0fdf97678224fe91c63542edbe9391054 Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Wed, 15 Nov 2023 14:18:43 +0100 Subject: [PATCH 153/291] Remove obsolete nodes because of general identifier rules --- .../tree-sitter-stack-graphs-python/src/stack-graphs.tsg | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg b/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg index 80a0a2625..945428672 100644 --- a/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg +++ b/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg @@ -56,7 +56,6 @@ attribute node_symbol = node => symbol = (source-text node), source_n node @node.input_dot node @node.member_attrs node @node.members - node @node.new_binding_pop node @node.new_bindings node @node.new_bindings_pop node @node.output @@ -799,8 +798,7 @@ inherit .parent_module attr (@id.input) pop_node = @id attr (@id.output) node_reference = @id - attr (@id.new_binding_pop) node_definition = @id - edge @id.new_bindings -> @id.new_binding_pop + edge @id.new_bindings -> @id.def } (pattern/identifier) @id @@ -812,8 +810,7 @@ inherit .parent_module attr (@id.input) node_definition = @id attr (@id.output) push_node = @id - attr (@id.new_binding_pop) node_definition = @id - edge @id.new_bindings -> @id.new_binding_pop + edge @id.new_bindings -> @id.def } (as_pattern From 0b54f6c38a1c031a70c8bdc7aebea59608bd91fc Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Wed, 15 Nov 2023 14:30:28 +0100 Subject: [PATCH 154/291] Fix rule to make it similar to others --- languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg b/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg index 945428672..b1845109c 100644 --- a/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg +++ b/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg @@ -854,8 +854,7 @@ inherit .parent_module (list_splat (_) @splatted) @splat { -attr (@splat.new_bindings_pop) node_definition = @splatted -edge @splat.new_bindings -> @splat.new_bindings_pop + edge @splat.new_bindings -> @splatted.new_bindings } (binary_operator From a899983928d3a0be7bae4fcbfce86ae3b9204e6f Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Wed, 15 Nov 2023 16:45:48 +0100 Subject: [PATCH 155/291] Reuse general definition for parameters --- .../src/stack-graphs.tsg | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg b/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg index b1845109c..f86683c8c 100644 --- a/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg +++ b/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg @@ -542,18 +542,18 @@ inherit .parent_module . (identifier) @param) body: (block) @body) { - edge @param.input -> @param.class_self_scope + edge @param.def -> @param.class_self_scope edge @param.class_member_attr_scope -> @param.output edge @param.output -> @body.after_scope attr (@param.output) push_node = @param } -(parameter/identifier) @param +(parameter/identifier) @param @name { - attr (@param.input) node_definition = @param attr (@param.param_name) push_node = @param - edge @param.input -> @param.param_index - edge @param.input -> @param.param_name + edge @name.def -> @param.param_name + edge @name.def -> @param.param_index + edge @param.input -> @name.def } [ From 1668eece08d779b5083e319687f65976d1eb27b2 Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Wed, 15 Nov 2023 17:01:07 +0100 Subject: [PATCH 156/291] Lower .before_scope and .after_scope --- .../src/stack-graphs.tsg | 56 ++++++++++++++++++- 1 file changed, 55 insertions(+), 1 deletion(-) diff --git a/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg b/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg index f86683c8c..588f7bcf4 100644 --- a/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg +++ b/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg @@ -39,9 +39,63 @@ attribute node_symbol = node => symbol = (source-text node), source_n ;; Nodes ;; ^^^^^ -(_)@node { +(module) @node { + node @node.after_scope + node @node.before_scope +} + +[ + ; _simple_statement + (future_import_statement) + (import_statement) + (import_from_statement) + (print_statement) + (assert_statement) + (expression_statement) + (return_statement) + (delete_statement) + (raise_statement) + (pass_statement) + (break_statement) + (continue_statement) + (global_statement) + (nonlocal_statement) + (exec_statement) + ; _compund_statement + (if_statement) + (for_statement) + (while_statement) + (try_statement) + (with_statement) + (function_definition) + (class_definition) + (decorated_definition) + (match_statement) + ; block + (block) + ; statement clauses + (elif_clause) + (else_clause) + (case_clause) + (except_clause) + (finally_clause) + (with_clause) +] @node { node @node.after_scope node @node.before_scope +} + +(parameters) @node { + node @node.after_scope + node @node.before_scope +} + +(comment) @node { + node @node.after_scope + node @node.before_scope +} + +(_)@node { node @node.call node @node.call_drop node @node.called From e1a0dcfe805205439b02d7df6258d8c0a1993eea Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Wed, 15 Nov 2023 17:06:15 +0100 Subject: [PATCH 157/291] Lower .input and .output --- .../src/stack-graphs.tsg | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg b/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg index 588f7bcf4..740f08233 100644 --- a/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg +++ b/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg @@ -90,9 +90,24 @@ attribute node_symbol = node => symbol = (source-text node), source_n node @node.before_scope } +[ + (identifier) + (expression) + (expression_list) + (primary_expression) + (pattern) + (pattern_list) + (parameter) +] @node { + node @node.input + node @node.output +} + (comment) @node { node @node.after_scope node @node.before_scope + node @node.input + node @node.output } (_)@node { @@ -106,13 +121,11 @@ attribute node_symbol = node => symbol = (source-text node), source_n node @node.el_index node @node.file_def node @node.file_ref - node @node.input node @node.input_dot node @node.member_attrs node @node.members node @node.new_bindings node @node.new_bindings_pop - node @node.output node @node.output_args node @node.output_dot node @node.param_index From 94365dcac5aed77022178c1038f393997a7b10e9 Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Wed, 15 Nov 2023 17:13:25 +0100 Subject: [PATCH 158/291] Lower more nodes --- .../src/stack-graphs.tsg | 50 ++++++++++++------- 1 file changed, 32 insertions(+), 18 deletions(-) diff --git a/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg b/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg index 740f08233..424cb2349 100644 --- a/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg +++ b/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg @@ -92,6 +92,18 @@ attribute node_symbol = node => symbol = (source-text node), source_n [ (identifier) + (import_prefix) + (wildcard_import) +] @node { + node @node.def + node @node.def_dot + node @node.ref + node @node.ref_dot +} + +[ + (identifier) + (wildcard_import) (expression) (expression_list) (primary_expression) @@ -106,34 +118,30 @@ attribute node_symbol = node => symbol = (source-text node), source_n (comment) @node { node @node.after_scope node @node.before_scope + node @node.def + node @node.def_dot node @node.input node @node.output + node @node.ref + node @node.ref_dot } (_)@node { node @node.call node @node.call_drop node @node.called - node @node.def - node @node.def_dot node @node.dot node @node.drop_scope node @node.el_index - node @node.file_def - node @node.file_ref - node @node.input_dot node @node.member_attrs node @node.members node @node.new_bindings node @node.new_bindings_pop node @node.output_args - node @node.output_dot node @node.param_index node @node.param_name node @node.pattern_before_scope node @node.pattern_index - node @node.ref - node @node.ref_dot node @node.return_value node @node.self_dot node @node.self_scope @@ -160,12 +168,15 @@ inherit .parent_module (module) @mod { - var module_def = @mod.file_def + node mod_file_def + node mod_file_ref + + var module_def = mod_file_def node parent_module_def_node var parent_module_def = parent_module_def_node - var module_ref = @mod.file_ref + var module_ref = mod_file_ref node parent_module_ref_node var parent_module_ref = parent_module_ref_node @@ -222,8 +233,8 @@ inherit .parent_module } } - edge ROOT_NODE -> @mod.file_def - edge @mod.file_ref -> ROOT_NODE + edge ROOT_NODE -> mod_file_def + edge mod_file_ref -> ROOT_NODE edge module_def -> @mod.after_scope node global @@ -833,14 +844,17 @@ inherit .parent_module object: (_) @object attribute: (identifier) @name) @expr { + node input_dot + node output_dot + edge @expr.output -> @name.output - edge @name.output -> @expr.output_dot - edge @expr.output_dot -> @object.output - edge @object.input -> @expr.input_dot - edge @expr.input_dot -> @name.input + edge @name.output -> output_dot + edge output_dot -> @object.output + edge @object.input -> input_dot + edge input_dot -> @name.input edge @name.input -> @expr.input - attr (@expr.output_dot) push_symbol = "." - attr (@expr.input_dot) pop_symbol = "." + attr (output_dot) push_symbol = "." + attr (input_dot) pop_symbol = "." attr (@name.input) pop_node = @name attr (@name.output) push_node = @name } From bb0f034ed92ec2d082ad211a7b7333e41ebe0a3b Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Wed, 15 Nov 2023 17:34:48 +0100 Subject: [PATCH 159/291] Lower final set of nodes --- .../src/stack-graphs.tsg | 123 ++++++++++-------- 1 file changed, 68 insertions(+), 55 deletions(-) diff --git a/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg b/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg index 424cb2349..0028994c2 100644 --- a/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg +++ b/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg @@ -109,9 +109,16 @@ attribute node_symbol = node => symbol = (source-text node), source_n (primary_expression) (pattern) (pattern_list) + (case_pattern) (parameter) + (pair) + (list_splat) + (parenthesized_list_splat) + (dictionary_splat) + (keyword_argument) ] @node { node @node.input + node @node.new_bindings node @node.output } @@ -121,33 +128,12 @@ attribute node_symbol = node => symbol = (source-text node), source_n node @node.def node @node.def_dot node @node.input + node @node.new_bindings node @node.output node @node.ref node @node.ref_dot } -(_)@node { - node @node.call - node @node.call_drop - node @node.called - node @node.dot - node @node.drop_scope - node @node.el_index - node @node.member_attrs - node @node.members - node @node.new_bindings - node @node.new_bindings_pop - node @node.output_args - node @node.param_index - node @node.param_name - node @node.pattern_before_scope - node @node.pattern_index - node @node.return_value - node @node.self_dot - node @node.self_scope - node @node.super_scope -} - ;; Inherited Variables ;; ^^^^^^^^^^^^^^^^^^^ @@ -531,18 +517,22 @@ inherit .parent_module parameters: (parameters) @params body: (block) @body) @func { + node call + node drop_scope + node return_value + attr (@name.def) definiens_node = @func edge @func.after_scope -> @name.def - edge @name.def -> @func.call - edge @func.call -> @func.return_value + edge @name.def -> call + edge call -> return_value edge @body.before_scope -> @params.after_scope - edge @body.before_scope -> @func.drop_scope - edge @func.drop_scope -> @func.bottom - attr (@func.drop_scope) type = "drop_scopes" - attr (@func.call) pop_scoped_symbol = "()" + edge @body.before_scope -> drop_scope + edge drop_scope -> @func.bottom + attr (drop_scope) type = "drop_scopes" + attr (call) pop_scoped_symbol = "()" edge @params.before_scope -> JUMP_TO_SCOPE_NODE - attr (@func.return_value) is_exported - let @func.function_returns = @func.return_value + attr (return_value) is_exported + let @func.function_returns = return_value ; Prevent functions defined inside of method bodies from being treated like methods let @body.class_self_scope = #null @@ -670,19 +660,24 @@ inherit .parent_module (tuple_pattern (_) @pattern) ] @list { + node pattern_index + let statement_scope = @list.local_scope - let @pattern.local_scope = @pattern.pattern_before_scope + node @pattern.local_scope edge statement_scope -> @pattern.local_scope attr (statement_scope -> @pattern.local_scope) precedence = (plus 1 (named-child-index @pattern)) - edge @pattern.pattern_index -> @list.input - edge @pattern.input -> @pattern.pattern_index - attr (@pattern.pattern_index) push_symbol = (named-child-index @pattern) + edge pattern_index -> @list.input + edge @pattern.input -> pattern_index + attr (pattern_index) push_symbol = (named-child-index @pattern) } (parameters (_) @param) @params { + node @param.param_index + node @param.param_name + attr (@param.param_index) push_symbol = (named-child-index @param) edge @param.param_index -> @params.before_scope edge @params.after_scope -> @param.input @@ -694,27 +689,39 @@ inherit .parent_module edge @stmt.function_returns -> @expr.output } +(class_definition) @class { + node @class.call_drop + node @class.member_attrs + node @class.members + node @class.super_scope +} + (class_definition name: (identifier) @name) @class { + node call + node self_dot + node self_scope + node members_dot + attr (@name.def) definiens_node = @class attr (@name.def) syntax_type = "class" edge @class.after_scope -> @name.def - edge @name.def -> @class.call - edge @name.def -> @class.dot - edge @class.dot -> @class.members - edge @class.call -> @class.call_drop - edge @class.call_drop -> @class.self_scope - edge @class.self_scope -> @class.super_scope - edge @class.self_scope -> @class.self_dot - edge @class.self_dot -> @class.members + edge @name.def -> call + edge @name.def -> members_dot + edge members_dot -> @class.members + edge call -> @class.call_drop + edge @class.call_drop -> self_scope + edge self_scope -> @class.super_scope + edge self_scope -> self_dot + edge self_dot -> @class.members edge @class.members -> @class.member_attrs - attr (@class.call) pop_scoped_symbol = "()" + attr (call) pop_scoped_symbol = "()" attr (@class.call_drop) type = "drop_scopes" - attr (@class.dot) pop_symbol = "." - attr (@class.self_dot) pop_symbol = "." + attr (members_dot) pop_symbol = "." + attr (self_dot) pop_symbol = "." attr (@class.member_attrs) push_symbol = "." - attr (@class.self_scope) is_exported + attr (self_scope) is_exported } (class_definition @@ -770,9 +777,11 @@ inherit .parent_module function: (_) @fn arguments: (argument_list) @args) @call { - edge @call.output -> @call.output_args - edge @call.output_args -> @fn.output - attr (@call.output_args) push_scoped_symbol = "()", scope = @args.args + node output_args + + edge @call.output -> output_args + edge output_args -> @fn.output + attr (output_args) push_scoped_symbol = "()", scope = @args.args } (call @@ -833,9 +842,11 @@ inherit .parent_module (expression_list (_) @element) ] @tuple { - edge @tuple.output -> @element.el_index - attr (@element.el_index) pop_symbol = (named-child-index @element) - edge @element.el_index -> @element.output + node el_index + + edge @tuple.output -> el_index + attr (el_index) pop_symbol = (named-child-index @element) + edge el_index -> @element.output edge @tuple.new_bindings -> @element.new_bindings } @@ -907,9 +918,11 @@ inherit .parent_module (list) @list { - edge @list.output -> @list.called - edge @list.called -> @list.global_dot - attr (@list.called) push_symbol = "list" + node called + + edge @list.output -> called + edge called -> @list.global_dot + attr (called) push_symbol = "list" } (list (_) @el) @list From d15cb9b23403907611a093e82e84f4061a7e0c19 Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Fri, 17 Nov 2023 12:15:55 +0100 Subject: [PATCH 160/291] Add tests --- .../relative_import_resolves_to_itself.py | 14 -------------- .../imports/import_from_module_a_submodule.py | 14 ++++++++++++++ .../test/imports/import_from_module_a_value.py | 13 +++++++++++++ .../import_from_module_a_value_aliased.py | 13 +++++++++++++ .../imports/import_from_module_wildcard.py | 13 +++++++++++++ .../imports/import_from_submodule_a_value.py | 16 ++++++++++++++++ .../import_from_super_package_a_value.py | 10 ++++++++++ ...ort_from_super_package_submodule_a_value.py | 10 ++++++++++ .../import_from_this_package_a_value.py | 10 ++++++++++ ...port_from_this_package_submodule_a_value.py | 10 ++++++++++ .../test/imports/import_module.py | 11 +++++++++++ .../test/imports/import_module_aliased.py | 14 ++++++++++++++ .../test/imports/import_submodule.py | 15 +++++++++++++++ .../imports/import_submodule_aliased.py.skip | 17 +++++++++++++++++ .../relative_import_resolves_to_itself.py.skip | 14 ++++++++++++++ .../require_explicit_submodule_import.py.skip | 18 ++++++++++++++++++ 16 files changed, 198 insertions(+), 14 deletions(-) delete mode 100644 languages/tree-sitter-stack-graphs-python/test/bugs.skip/relative_import_resolves_to_itself.py create mode 100644 languages/tree-sitter-stack-graphs-python/test/imports/import_from_module_a_submodule.py create mode 100644 languages/tree-sitter-stack-graphs-python/test/imports/import_from_module_a_value.py create mode 100644 languages/tree-sitter-stack-graphs-python/test/imports/import_from_module_a_value_aliased.py create mode 100644 languages/tree-sitter-stack-graphs-python/test/imports/import_from_module_wildcard.py create mode 100644 languages/tree-sitter-stack-graphs-python/test/imports/import_from_submodule_a_value.py create mode 100644 languages/tree-sitter-stack-graphs-python/test/imports/import_from_super_package_a_value.py create mode 100644 languages/tree-sitter-stack-graphs-python/test/imports/import_from_super_package_submodule_a_value.py create mode 100644 languages/tree-sitter-stack-graphs-python/test/imports/import_from_this_package_a_value.py create mode 100644 languages/tree-sitter-stack-graphs-python/test/imports/import_from_this_package_submodule_a_value.py create mode 100644 languages/tree-sitter-stack-graphs-python/test/imports/import_module.py create mode 100644 languages/tree-sitter-stack-graphs-python/test/imports/import_module_aliased.py create mode 100644 languages/tree-sitter-stack-graphs-python/test/imports/import_submodule.py create mode 100644 languages/tree-sitter-stack-graphs-python/test/imports/import_submodule_aliased.py.skip create mode 100644 languages/tree-sitter-stack-graphs-python/test/imports/relative_import_resolves_to_itself.py.skip create mode 100644 languages/tree-sitter-stack-graphs-python/test/imports/require_explicit_submodule_import.py.skip diff --git a/languages/tree-sitter-stack-graphs-python/test/bugs.skip/relative_import_resolves_to_itself.py b/languages/tree-sitter-stack-graphs-python/test/bugs.skip/relative_import_resolves_to_itself.py deleted file mode 100644 index 838595ab7..000000000 --- a/languages/tree-sitter-stack-graphs-python/test/bugs.skip/relative_import_resolves_to_itself.py +++ /dev/null @@ -1,14 +0,0 @@ -#--- path: a/__init__.py ---# -from . import b -# ^ defined: 6 - -#--- path: a/b/__init__.py ---# -B = 'b' - -#--- path: main.py ---# -from a import b -# ^ defined: 6 - -print b.B -# ^ defined: 9, 6 -# ^ defined: 6 \ No newline at end of file diff --git a/languages/tree-sitter-stack-graphs-python/test/imports/import_from_module_a_submodule.py b/languages/tree-sitter-stack-graphs-python/test/imports/import_from_module_a_submodule.py new file mode 100644 index 000000000..ea51f9450 --- /dev/null +++ b/languages/tree-sitter-stack-graphs-python/test/imports/import_from_module_a_submodule.py @@ -0,0 +1,14 @@ +# --- path: foo/bar.py --- + +BAR = 42 + +# --- path: test.py --- + +from foo import bar + +bar.BAR +# ^ defined: 7, 3 +# ^ defined: 3 + +foo +# ^ defined: diff --git a/languages/tree-sitter-stack-graphs-python/test/imports/import_from_module_a_value.py b/languages/tree-sitter-stack-graphs-python/test/imports/import_from_module_a_value.py new file mode 100644 index 000000000..1eb97ffe2 --- /dev/null +++ b/languages/tree-sitter-stack-graphs-python/test/imports/import_from_module_a_value.py @@ -0,0 +1,13 @@ +# --- path: foo.py --- + +FOO = 42 + +# --- path: test.py --- + +from foo import FOO + +FOO +# ^ defined: 7, 3 + +foo +# ^ defined: diff --git a/languages/tree-sitter-stack-graphs-python/test/imports/import_from_module_a_value_aliased.py b/languages/tree-sitter-stack-graphs-python/test/imports/import_from_module_a_value_aliased.py new file mode 100644 index 000000000..088ca9544 --- /dev/null +++ b/languages/tree-sitter-stack-graphs-python/test/imports/import_from_module_a_value_aliased.py @@ -0,0 +1,13 @@ +# --- path: foo.py --- + +FOO = 42 + +# --- path: test.py --- + +from foo import FOO as QUX + +QUX +# ^ defined: 7, 3 + +FOO +# ^ defined: diff --git a/languages/tree-sitter-stack-graphs-python/test/imports/import_from_module_wildcard.py b/languages/tree-sitter-stack-graphs-python/test/imports/import_from_module_wildcard.py new file mode 100644 index 000000000..3a1de4ffe --- /dev/null +++ b/languages/tree-sitter-stack-graphs-python/test/imports/import_from_module_wildcard.py @@ -0,0 +1,13 @@ +# --- path: foo.py --- + +FOO = 42 + +# --- path: test.py --- + +from foo import * + +FOO +# ^ defined: 3 + +foo +# ^ defined: diff --git a/languages/tree-sitter-stack-graphs-python/test/imports/import_from_submodule_a_value.py b/languages/tree-sitter-stack-graphs-python/test/imports/import_from_submodule_a_value.py new file mode 100644 index 000000000..8d89d8538 --- /dev/null +++ b/languages/tree-sitter-stack-graphs-python/test/imports/import_from_submodule_a_value.py @@ -0,0 +1,16 @@ +# --- path: foo/bar.py --- + +BAR = 42 + +# --- path: test.py --- + +from foo.bar import BAR + +BAR +# ^ defined: 7, 3 + +foo +# ^ defined: + +bar +# ^ defined: diff --git a/languages/tree-sitter-stack-graphs-python/test/imports/import_from_super_package_a_value.py b/languages/tree-sitter-stack-graphs-python/test/imports/import_from_super_package_a_value.py new file mode 100644 index 000000000..961cd1622 --- /dev/null +++ b/languages/tree-sitter-stack-graphs-python/test/imports/import_from_super_package_a_value.py @@ -0,0 +1,10 @@ +# --- path: foo/__init__.py --- + +FOO = 42 + +# --- path: foo/bar/test.py --- + +from .. import FOO + +FOO +# ^ defined: 7, 3 diff --git a/languages/tree-sitter-stack-graphs-python/test/imports/import_from_super_package_submodule_a_value.py b/languages/tree-sitter-stack-graphs-python/test/imports/import_from_super_package_submodule_a_value.py new file mode 100644 index 000000000..75652f7cd --- /dev/null +++ b/languages/tree-sitter-stack-graphs-python/test/imports/import_from_super_package_submodule_a_value.py @@ -0,0 +1,10 @@ +# --- path: foo/bar.py --- + +BAR = 42 + +# --- path: foo/baz/test.py --- + +from ..bar import BAR + +BAR +# ^ defined: 7, 3 diff --git a/languages/tree-sitter-stack-graphs-python/test/imports/import_from_this_package_a_value.py b/languages/tree-sitter-stack-graphs-python/test/imports/import_from_this_package_a_value.py new file mode 100644 index 000000000..f8de8042c --- /dev/null +++ b/languages/tree-sitter-stack-graphs-python/test/imports/import_from_this_package_a_value.py @@ -0,0 +1,10 @@ +# --- path: foo/__init__.py --- + +FOO = 42 + +# --- path: foo/test.py --- + +from . import FOO + +FOO +# ^ defined: 7, 3 diff --git a/languages/tree-sitter-stack-graphs-python/test/imports/import_from_this_package_submodule_a_value.py b/languages/tree-sitter-stack-graphs-python/test/imports/import_from_this_package_submodule_a_value.py new file mode 100644 index 000000000..dc64ce387 --- /dev/null +++ b/languages/tree-sitter-stack-graphs-python/test/imports/import_from_this_package_submodule_a_value.py @@ -0,0 +1,10 @@ +# --- path: foo/bar.py --- + +BAR = 42 + +# --- path: foo/test.py --- + +from .bar import BAR + +BAR +# ^ defined: 7, 3 diff --git a/languages/tree-sitter-stack-graphs-python/test/imports/import_module.py b/languages/tree-sitter-stack-graphs-python/test/imports/import_module.py new file mode 100644 index 000000000..897acdf63 --- /dev/null +++ b/languages/tree-sitter-stack-graphs-python/test/imports/import_module.py @@ -0,0 +1,11 @@ +# --- path: foo.py --- + +FOO = 42 + +# --- path: test.py --- + +import foo + +foo.FOO +# ^ defined: 7, 3 +# ^ defined: 3 diff --git a/languages/tree-sitter-stack-graphs-python/test/imports/import_module_aliased.py b/languages/tree-sitter-stack-graphs-python/test/imports/import_module_aliased.py new file mode 100644 index 000000000..220a6b72d --- /dev/null +++ b/languages/tree-sitter-stack-graphs-python/test/imports/import_module_aliased.py @@ -0,0 +1,14 @@ +# --- path: foo.py --- + +FOO = 42 + +# --- path: test.py --- + +import foo as qux + +qux.FOO +# ^ defined: 7, 3 +# ^ defined: 3 + +foo +# ^ defined: diff --git a/languages/tree-sitter-stack-graphs-python/test/imports/import_submodule.py b/languages/tree-sitter-stack-graphs-python/test/imports/import_submodule.py new file mode 100644 index 000000000..312cd66a1 --- /dev/null +++ b/languages/tree-sitter-stack-graphs-python/test/imports/import_submodule.py @@ -0,0 +1,15 @@ +# --- path: foo/bar.py --- + +BAR = 42 + +# --- path: test.py --- + +import foo.bar + +foo.bar.BAR +# ^ defined: 7 +# ^ defined: 7, 3 +# ^ defined: 3 + +bar +# ^ defined: diff --git a/languages/tree-sitter-stack-graphs-python/test/imports/import_submodule_aliased.py.skip b/languages/tree-sitter-stack-graphs-python/test/imports/import_submodule_aliased.py.skip new file mode 100644 index 000000000..dd84f39b5 --- /dev/null +++ b/languages/tree-sitter-stack-graphs-python/test/imports/import_submodule_aliased.py.skip @@ -0,0 +1,17 @@ +# --- path: foo/bar.py --- + +BAR = 42 + +# --- path: test.py --- + +import foo.bar as qux + +qux.BAR +# ^ defined: 7 +# ^ defined: 3 + +foo +# ^ defined: + +bar +# ^ defined: diff --git a/languages/tree-sitter-stack-graphs-python/test/imports/relative_import_resolves_to_itself.py.skip b/languages/tree-sitter-stack-graphs-python/test/imports/relative_import_resolves_to_itself.py.skip new file mode 100644 index 000000000..2cdb2cf66 --- /dev/null +++ b/languages/tree-sitter-stack-graphs-python/test/imports/relative_import_resolves_to_itself.py.skip @@ -0,0 +1,14 @@ +# --- path: foo/__init__.py --- +from . import bar +# ^ defined: 6 + +# --- path: foo/bar/__init__.py --- +BAR = 'b' + +# --- path: main.py --- +from foo import bar +# ^ defined: 6 + +bar.BAR +# ^ defined: 9, 6 +# ^ defined: 6 diff --git a/languages/tree-sitter-stack-graphs-python/test/imports/require_explicit_submodule_import.py.skip b/languages/tree-sitter-stack-graphs-python/test/imports/require_explicit_submodule_import.py.skip new file mode 100644 index 000000000..a9d12848c --- /dev/null +++ b/languages/tree-sitter-stack-graphs-python/test/imports/require_explicit_submodule_import.py.skip @@ -0,0 +1,18 @@ +# --- path: foo/__init__.py --- + +FOO = 42 + +# --- path: foo/bar.py --- + +BAR = 42 + +# --- path: test.py --- + +import foo + +foo.FOO +# ^ defined: 11, 3 +# ^ defined: 3 + +foo.bar +# ^ defined: From 34eba6ea3fc32f424c05d990634e318109d121b5 Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Thu, 18 Jan 2024 15:13:42 +0100 Subject: [PATCH 161/291] Update README --- .../tree-sitter-stack-graphs-python/README.md | 100 ++++++++++++------ 1 file changed, 70 insertions(+), 30 deletions(-) diff --git a/languages/tree-sitter-stack-graphs-python/README.md b/languages/tree-sitter-stack-graphs-python/README.md index 3bf115da0..b5fbc2f32 100644 --- a/languages/tree-sitter-stack-graphs-python/README.md +++ b/languages/tree-sitter-stack-graphs-python/README.md @@ -4,27 +4,73 @@ This project defines tree-sitter-stack-graphs rules for Python using the [tree-s [tree-sitter-python]: https://crates.io/crates/tree-sitter-python -## Usage +- [API documentation](https://docs.rs/tree-sitter-stack-graphs-python/) +- [Release notes](https://github.com/github/stack-graphs/blob/main/languages/tree-sitter-stack-graphs-python/CHANGELOG.md) + +## Using the API To use this library, add the following to your `Cargo.toml`: -``` toml +```toml [dependencies] tree-sitter-stack-graphs-python = "0.1.0" ``` Check out our [documentation](https://docs.rs/tree-sitter-stack-graphs-python/*/) for more details on how to use this library. -## Command-line Program +## Using the Command-line Program The command-line program for `tree-sitter-stack-graphs-python` lets you do stack graph based analysis and lookup from the command line. -Install the program using `cargo install` as follows: +The CLI can be run as follows: -``` sh -$ cargo install --features cli tree-sitter-stack-graphs-python -$ tree-sitter-stack-graphs-python --help -``` +1. _(Installed)_ Install the CLI using Cargo as follows: + + ```sh + cargo install --features cli tree-sitter-stack-graphs-python + ``` + + After this, the CLI should be available as `tree-sitter-stack-graphs-python`. + +2. _(From source)_ Instead of installing the CLI, it can also be run directly from the crate directory, as a replacement for a `tree-sitter-stack-graphs-python` invocation, as follows: + + ```sh + cargo run --features cli -- + ``` + +The basic CLI workflow for the command-line program is to index source code and issue queries against the resulting database: + +1. Index a source folder as follows: + + ```sh + tree-sitter-stack-graphs-python index SOURCE_DIR + ``` + + _Indexing will skip any files that have already be indexed. To force a re-index, add the `-f` flag._ + + To check the status if a source folder, run: + + ```sh + tree-sitter-stack-graphs-python status SOURCE_DIR + ``` + + To clean the database and start with a clean slate, run: + + ```sh + tree-sitter-stack-graphs-python clean + ``` + + _Pass the `--delete` flag to not just empty the database, but also delete it. This is useful to resolve `unsupported database version` errors that may occur after a version update._ + +2. Run a query to find the definition(s) for a reference on a given line and column, run: + + ```sh + tree-sitter-stack-graphs-python query definition SOURCE_PATH:LINE:COLUMN + ``` + + Resulting definitions are printed, including a source line if the source file is available. + +Discover all available commands and flags by passing the `-h` flag to the CLI directly, or to any of the subcommands. ## Development @@ -35,35 +81,29 @@ The project is written in Rust, and requires a recent version installed. Rust c The project is organized as follows: - The stack graph rules are defined in `src/stack-graphs.tsg`. -- Builtins sources and configuration are defined in `src/builtins.py` and `builtins.cfg` respectively. +- Builtins sources and configuration are defined in `src/builtins.it` and `builtins.cfg` respectively. - Tests are put into the `test` directory. -### Building and Running Tests - -Build the project by running: - -``` sh -$ cargo build -``` +### Running Tests Run the tests as follows: -``` sh -$ cargo test +```sh +cargo test ``` The project consists of a library and a CLI. By default, running `cargo` only applies to the library. To run `cargo` commands on the CLI as well, add `--features cli` or `--all-features`. Run the CLI from source as follows: -``` sh -$ cargo run --features cli -- ARGS +```sh +cargo run --features cli -- ARGS ``` Sources are formatted using the standard Rust formatted, which is applied by running: -``` sh -$ cargo fmt +```sh +cargo fmt ``` ### Writing TSG @@ -78,21 +118,21 @@ which contain self-contained TSG rules for specific language features. A VSCode Parse and test a single file by executing the following commands: -``` sh -$ cargo run --features cli -- parse FILES... -$ cargo run --features cli -- test TESTFILES... +```sh +cargo run --features cli -- parse FILES... +cargo run --features cli -- test TESTFILES... ``` Generate a visualization to debug failing tests by passing the `-V` flag: -``` sh -$ cargo run --features cli -- test -V TESTFILES... +```sh +cargo run --features cli -- test -V TESTFILES... ``` To generate the visualization regardless of test outcome, execute: -``` sh -$ cargo run --features cli -- test -V --output-mode=always TESTFILES... +```sh +cargo run --features cli -- test -V --output-mode=always TESTFILES... ``` -Go to https://crates.io/crates/tree-sitter-stack-graphs for links to examples and documentation. +Go to for links to examples and documentation. From c23f7ca23d354b08ba9b80adee5433f0f815513b Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Thu, 16 Nov 2023 12:33:46 +0100 Subject: [PATCH 162/291] Upgrade Python and reorganize rules according to syntax categories --- .../Cargo.toml | 2 +- .../src/stack-graphs.tsg | 726 ++++++++++++------ .../test/pattern_matching.py | 7 +- 3 files changed, 517 insertions(+), 218 deletions(-) diff --git a/languages/tree-sitter-stack-graphs-python/Cargo.toml b/languages/tree-sitter-stack-graphs-python/Cargo.toml index ca123cc0f..4ac4c0633 100644 --- a/languages/tree-sitter-stack-graphs-python/Cargo.toml +++ b/languages/tree-sitter-stack-graphs-python/Cargo.toml @@ -31,7 +31,7 @@ cli = ["anyhow", "clap", "tree-sitter-stack-graphs/cli"] anyhow = { version = "1.0", optional = true } clap = { version = "4", optional = true, features = ["derive"] } tree-sitter-stack-graphs = { version = "0.7", path = "../../tree-sitter-stack-graphs" } -tree-sitter-python = "=0.20.2" +tree-sitter-python = "=0.20.4" [dev-dependencies] anyhow = "1.0" diff --git a/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg b/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg index 0028994c2..00314b687 100644 --- a/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg +++ b/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg @@ -45,6 +45,7 @@ attribute node_symbol = node => symbol = (source-text node), source_n } [ + ; _statement ; _simple_statement (future_import_statement) (import_statement) @@ -61,7 +62,8 @@ attribute node_symbol = node => symbol = (source-text node), source_n (global_statement) (nonlocal_statement) (exec_statement) - ; _compund_statement + (type_alias_statement) + ; _compound_statement (if_statement) (for_statement) (while_statement) @@ -74,18 +76,23 @@ attribute node_symbol = node => symbol = (source-text node), source_n ; block (block) ; statement clauses + (if_clause) (elif_clause) (else_clause) - (case_clause) + (except_group_clause) (except_clause) (finally_clause) (with_clause) + (case_clause) ] @node { node @node.after_scope node @node.before_scope } -(parameters) @node { +[ + (parameters) + (lambda_parameters) +] @node { node @node.after_scope node @node.before_scope } @@ -102,20 +109,95 @@ attribute node_symbol = node => symbol = (source-text node), source_n } [ + ; expressions + (comparison_operator) + (not_operator) + (boolean_operator) + (lambda) + ;(primary_expression) ; unfolded below + (conditional_expression) + (named_expression) + (as_pattern) + ; primary_expression + (await) + (binary_operator) (identifier) - (wildcard_import) - (expression) + ;(keyword_identifier) ; invalid query pattern? + (string) + (concatenated_string) + (integer) + (float) + (true) + (false) + (none) + (unary_operator) + (attribute) + (subscript) + (call) + (list) + (list_comprehension) + (dictionary) + (dictionary_comprehension) + (set) + (set_comprehension) + (tuple) + (parenthesized_expression) + (generator_expression) + (ellipsis) + (list_splat) + + ; expression list (expression_list) - (primary_expression) - (pattern) - (pattern_list) + + ; pattern + (pattern/identifier) + ;(keyword_identifier) ; invalid query pattern? + ;(subscript) + ;(attribute) + (list_splat_pattern) + (tuple_pattern) + (list_pattern) + ; _simple_patterns + (class_pattern) + (splat_pattern) + (union_pattern) + ;(list_pattern) ; already in pattern + ;(tuple_pattern) ; already in pattern + (dict_pattern) + ;(string) ; already in primary_expression + ;(concatenated_string) ; already in primary_expression + ;(true) ; already in primary_expression + ;(false) ; already in primary_expression + ;(none) ; already in primary_expression + ;(integer) ; already in primary_expression + ;(float) ; already in primary_expression + (complex_pattern) + (dotted_name) + ; _as_attern + (as_pattern) + ; keyword pattern + (keyword_pattern) + ; case pattern (case_pattern) - (parameter) - (pair) - (list_splat) - (parenthesized_list_splat) - (dictionary_splat) - (keyword_argument) + ; with item + (with_item) + + ; pattern list + (pattern_list) + + ; parameter + ;(identifier) ; already in expressions + (typed_parameter) + (default_parameter) + (typed_default_parameter) + ;(list_splat_pattern) ; already in patterns + ;(tuple_pattern) ; already in patterns + (keyword_separator) + (positional_separator) + (dictionary_splat_pattern) + + ; parameters + (parameters) ] @node { node @node.input node @node.new_bindings @@ -149,8 +231,16 @@ inherit .grandparent_module inherit .local_scope inherit .parent_module -; Modules and Imports -;--------------------- +;; +;; # # +;; ## ## #### ##### # # # ###### #### +;; # # # # # # # # # # # # # +;; # # # # # # # # # # ##### #### +;; # # # # # # # # # # # +;; # # # # # # # # # # # # +;; # # #### ##### #### ###### ###### #### +;; +;; Modules (module) @mod { @@ -248,6 +338,18 @@ inherit .parent_module node @mod.local_scope } +;; +;; ### +;; # # # ##### #### ##### ##### #### +;; # ## ## # # # # # # # # +;; # # ## # # # # # # # # #### +;; # # # ##### # # ##### # # +;; # # # # # # # # # # # +;; ### # # # #### # # # #### +;; +;; Imports + +;; import _.X (import_statement name: (dotted_name . (identifier) @root_name)) @stmt @@ -257,6 +359,7 @@ inherit .parent_module edge @root_name.ref -> ROOT_NODE } +;; import _.X as _ (import_statement name: (aliased_import (dotted_name . (identifier) @root_name))) @@ -264,6 +367,7 @@ inherit .parent_module edge @root_name.ref -> ROOT_NODE } +;; from _ import _.X (import_from_statement name: (dotted_name . (identifier) @import_root_name)) @stmt @@ -274,6 +378,7 @@ inherit .parent_module attr (@import_root_name.ref_dot) push_symbol = "." } +;; from _ import _.X as _ (import_from_statement name: (aliased_import (dotted_name @@ -283,6 +388,11 @@ inherit .parent_module attr (@import_root_name.ref_dot) push_symbol = "." } +;; from X._ import _ +;; from ._.X import _ +;; from .._.X import _ +;; from . import _ +;; from .. import _ (import_from_statement module_name: [ (dotted_name (identifier) @prefix_leaf_name .) @@ -300,6 +410,8 @@ inherit .parent_module edge @import_root_name.ref_dot -> @prefix_leaf_name.ref } +;; from _ import _.X as Y +;; import _.X as Y [ (import_from_statement (aliased_import @@ -315,6 +427,8 @@ inherit .parent_module edge @alias.def -> @name.ref } +;; import _.X +;; from _ import _.X [ (import_statement name: (dotted_name @@ -327,6 +441,7 @@ inherit .parent_module edge @leaf_name.def -> @leaf_name.ref } +;; . (relative_import (import_prefix) @prefix (#eq? @prefix ".")) @import @@ -334,6 +449,7 @@ inherit .parent_module edge @prefix.ref -> @import.parent_module } +;; .. (relative_import (import_prefix) @prefix (#eq? @prefix "..")) @import @@ -341,6 +457,8 @@ inherit .parent_module edge @prefix.ref -> @import.grandparent_module } +;; .X +;; ..X (relative_import (import_prefix) @prefix (dotted_name @@ -351,6 +469,7 @@ inherit .parent_module edge @name.ref_dot -> @prefix.ref } +;; from . [ (import_from_statement module_name: (relative_import @@ -370,6 +489,7 @@ inherit .parent_module edge @child_name.ref_dot -> @parent_name.ref } +;; from X._ import _ (import_from_statement module_name: (dotted_name . (identifier) @root_name)) @@ -377,6 +497,7 @@ inherit .parent_module edge @root_name.ref -> ROOT_NODE } +;; from _.X import * (import_from_statement module_name: (dotted_name (identifier) @leaf_name .) @@ -388,6 +509,9 @@ inherit .parent_module attr (@star.ref_dot) push_symbol = "." } +;; import _.X.Y._ +;; from _ import _.X.Y._ +;; from _ import _.X.Y._ as _ [ (import_statement name: (dotted_name @@ -415,9 +539,16 @@ inherit .parent_module attr (@child_name.ref_dot) push_symbol = "." } -;-------- -; Scopes -;-------- +;; +;; ###### +;; # # # #### #### # # #### +;; # # # # # # # # # # +;; ###### # # # # #### #### +;; # # # # # # # # # +;; # # # # # # # # # # # +;; ###### ###### #### #### # # #### +;; +;; Blocks [ (module (_) @last_stmt .) @@ -475,7 +606,7 @@ inherit .parent_module edge @stmt.after_scope -> @block.after_scope } -(match_statement (case_clause) @block) @stmt +(match_statement body: (_) @block) @stmt { let @block.local_scope = @block.before_scope edge @block.before_scope -> @stmt.before_scope @@ -490,26 +621,77 @@ inherit .parent_module edge @stmt.before_scope -> @stmt.after_scope } -;------------- -; Definitions -;------------- +;; +;; ##### +;; # # ##### ## ##### ###### # # ###### # # ##### #### +;; # # # # # # ## ## # ## # # # +;; ##### # # # # ##### # ## # ##### # # # # #### +;; # # ###### # # # # # # # # # # +;; # # # # # # # # # # # ## # # # +;; ##### # # # # ###### # # ###### # # # #### +;; +;; Statements -(identifier) @name { - attr (@name.def) node_definition = @name -} +;;;; Simple Statements -[ - (assignment - left: (_) @pattern - right: (_) @value) - (with_item - value: - (as_pattern - (_) @value - alias: (as_pattern_target (_) @pattern))) -] +(print_statement) {} + +(assert_statement) {} + +(expression_statement) {} + +(return_statement (_) @expr) @stmt { - edge @pattern.input -> @value.output + edge @stmt.function_returns -> @expr.output +} + +(delete_statement) {} + +(raise_statement) {} + +(pass_statement) {} + +(break_statement) {} + +(continue_statement) {} + +(global_statement) {} + +(nonlocal_statement) {} + +(exec_statement) {} + +(type_alias_statement) {} + +;;;; Compound Statements + +(if_statement) {} + +(if_clause) {} + +(elif_clause) {} + +(else_clause) {} + +(for_statement) {} + +(while_statement) {} + +(try_statement) {} + +(except_group_clause) {} + +(except_clause) {} + +(finally_clause) {} + +(with_statement) {} + +(with_clause) {} + +(function_definition + name: (identifier) @name) { + attr (@name.def) node_definition = @name } (function_definition @@ -539,72 +721,6 @@ inherit .parent_module let @body.class_member_attr_scope = #null } -;; -;; BEGIN BIG GNARLY DISJUNCTION -;; -;; The following pair of rules is intended to capture the following behavior: -;; -;; If a function definition is used to define a method, by being inside a class -;; definition, then we make its syntax type `method`. Otherwise, we make it's -;; syntax type `function`. Unfortunately, because of the limitations on negation -;; and binding in tree sitter queries, we cannot negate `class_definition` or -;; similar things directly. Instead, we have to manually push the negation down -;; to form the finite disjunction it corresponds to. -;; - -[ - (class_definition (block (decorated_definition (function_definition name: (_)@name)))) - (class_definition (block (function_definition name: (_)@name))) -] -{ - attr (@name.def) syntax_type = "method" -} - -[ - (module (decorated_definition (function_definition name: (_)@name))) - (module (function_definition name: (_)@name)) - - (if_statement (block (decorated_definition (function_definition name: (_)@name)))) - (if_statement (block (function_definition name: (_)@name))) - - (elif_clause (block (decorated_definition (function_definition name: (_)@name)))) - (elif_clause (block (function_definition name: (_)@name))) - - (else_clause (block (decorated_definition (function_definition name: (_)@name)))) - (else_clause (block (function_definition name: (_)@name))) - - (case_clause (block (decorated_definition (function_definition name: (_)@name)))) - (case_clause (block (function_definition name: (_)@name))) - - (for_statement (block (decorated_definition (function_definition name: (_)@name)))) - (for_statement (block (function_definition name: (_)@name))) - - (while_statement (block (decorated_definition (function_definition name: (_)@name)))) - (while_statement (block (function_definition name: (_)@name))) - - (try_statement (block (decorated_definition (function_definition name: (_)@name)))) - (try_statement (block (function_definition name: (_)@name))) - - (except_clause (block (decorated_definition (function_definition name: (_)@name)))) - (except_clause (block (function_definition name: (_)@name))) - - (finally_clause (block (decorated_definition (function_definition name: (_)@name)))) - (finally_clause (block (function_definition name: (_)@name))) - - (with_statement (block (decorated_definition (function_definition name: (_)@name)))) - (with_statement (block (function_definition name: (_)@name))) - - (function_definition (block (decorated_definition (function_definition name: (_)@name)))) - (function_definition (block (function_definition name: (_)@name))) -] -{ - attr (@name.def) syntax_type = "function" -} - -;; -;; END BIG GNARLY DISJUNCTION -;; - (function_definition parameters: (parameters . (identifier) @param) @@ -616,6 +732,22 @@ inherit .parent_module attr (@param.output) push_node = @param } +(parameters + (_) @param) @params +{ + node @param.param_index + node @param.param_name + + attr (@param.param_index) push_symbol = (named-child-index @param) + edge @param.param_index -> @params.before_scope + edge @params.after_scope -> @param.input + edge @param.param_name -> @params.before_scope +} + +(parameter/identifier) @name { + attr (@name.def) node_definition = @name +} + (parameter/identifier) @param @name { attr (@param.param_name) push_node = @param @@ -655,40 +787,6 @@ inherit .parent_module edge @param.input -> @name.def } -[ - (pattern_list (_) @pattern) - (tuple_pattern (_) @pattern) -] @list -{ - node pattern_index - - let statement_scope = @list.local_scope - node @pattern.local_scope - edge statement_scope -> @pattern.local_scope - attr (statement_scope -> @pattern.local_scope) precedence = (plus 1 (named-child-index @pattern)) - - edge pattern_index -> @list.input - edge @pattern.input -> pattern_index - attr (pattern_index) push_symbol = (named-child-index @pattern) -} - -(parameters - (_) @param) @params -{ - node @param.param_index - node @param.param_name - - attr (@param.param_index) push_symbol = (named-child-index @param) - edge @param.param_index -> @params.before_scope - edge @params.after_scope -> @param.input - edge @param.param_name -> @params.before_scope -} - -(return_statement (_) @expr) @stmt -{ - edge @stmt.function_returns -> @expr.output -} - (class_definition) @class { node @class.call_drop node @class.member_attrs @@ -696,6 +794,11 @@ inherit .parent_module node @class.super_scope } +(class_definition + name: (identifier) @name) { + attr (@name.def) node_definition = @name +} + (class_definition name: (identifier) @name) @class { @@ -756,23 +859,107 @@ inherit .parent_module edge @stmt.after_scope -> @def.after_scope } +(match_statement) {} + (case_clause - pattern: (_) @pattern - consequence: (_) @consequence) @clause + (case_pattern) @pattern + consequence: (_) @consequence) { edge @consequence.before_scope -> @pattern.new_bindings +} + +(case_clause + consequence: (_) @consequence) @clause +{ edge @consequence.before_scope -> @clause.before_scope edge @clause.after_scope -> @consequence.after_scope } -;------------- -; Expressions -;------------- +;; +;; ####### +;; # # # ##### ##### ###### #### #### # #### # # #### +;; # # # # # # # # # # # # # ## # # +;; ##### ## # # # # ##### #### #### # # # # # # #### +;; # ## ##### ##### # # # # # # # # # # +;; # # # # # # # # # # # # # # # ## # # +;; ####### # # # # # ###### #### #### # #### # # #### +;; +;; Expressions + +(conditional_expression) {} + +(named_expression) {} + +(as_pattern) {} + +(await) {} + +(binary_operator + (_) @left + (_) @right) @binop +{ + edge @binop.new_bindings -> @left.new_bindings + edge @binop.new_bindings -> @right.new_bindings +} -(identifier) @name { +(primary_expression/identifier) @name { attr (@name.ref) node_reference = @name } +(primary_expression/identifier) @name +{ + edge @name.output -> @name.local_scope + edge @name.output -> @name.class_parent_scope + edge @name.local_scope -> @name.input + attr (@name.input) pop_node = @name + attr (@name.output) node_reference = @name + + edge @name.new_bindings -> @name.def +} + +(string) {} + +(concatenated_string) {} + +(integer) {} + +(float) {} + +(true) {} + +(false) {} + +(none) {} + +(unary_operator) {} + +(attribute + object: (_) @object + attribute: (identifier) @name) @expr +{ + node input_dot + node output_dot + + edge @expr.output -> @name.output + edge @name.output -> output_dot + edge output_dot -> @object.output + edge @object.input -> input_dot + edge input_dot -> @name.input + edge @name.input -> @expr.input + attr (output_dot) push_symbol = "." + attr (input_dot) pop_symbol = "." + attr (@name.input) pop_node = @name + attr (@name.output) push_node = @name +} + +(primary_expression/attribute + attribute: (identifier) @name) +{ + attr (@name.output) is_reference +} + +(subscript) {} + (call function: (_) @fn arguments: (argument_list) @args) @call @@ -837,6 +1024,42 @@ inherit .parent_module edge @call.output -> @call.class_super_scope } +(list) @list +{ + node called + + edge @list.output -> called + edge called -> @list.global_dot + attr (called) push_symbol = "list" +} + +(list (_) @el) @list +{ + edge @list.new_bindings -> @el.new_bindings +} + +(list_comprehension) {} + +(dictionary (pair) @pair) @dict +{ + edge @dict.new_bindings -> @pair.new_bindings +} + +(pair + value: (_) @value) @pair +{ + edge @pair.new_bindings -> @value.new_bindings +} + +(dictionary_comprehension) {} + +(set (_) @el) @set +{ + edge @set.new_bindings -> @el.new_bindings +} + +(set_comprehension) {} + [ (tuple (_) @element) (expression_list (_) @element) @@ -851,115 +1074,188 @@ inherit .parent_module edge @tuple.new_bindings -> @element.new_bindings } -(attribute - object: (_) @object - attribute: (identifier) @name) @expr +(parenthesized_expression) {} + +(generator_expression) {} + +(ellipsis) {} + +(list_splat (_) @splatted) @splat { - node input_dot - node output_dot + edge @splat.new_bindings -> @splatted.new_bindings +} - edge @expr.output -> @name.output - edge @name.output -> output_dot - edge output_dot -> @object.output - edge @object.input -> input_dot - edge input_dot -> @name.input - edge @name.input -> @expr.input - attr (output_dot) push_symbol = "." - attr (input_dot) pop_symbol = "." - attr (@name.input) pop_node = @name +;; +;; ###### +;; # # ## ##### ##### ###### ##### # # #### +;; # # # # # # # # # ## # # +;; ###### # # # # ##### # # # # # #### +;; # ###### # # # ##### # # # # +;; # # # # # # # # # ## # # +;; # # # # # ###### # # # # #### +;; +;; Patterns + +(pattern/identifier) @name { + attr (@name.def) node_definition = @name +} + +(pattern/identifier) @name +{ + edge @name.output -> @name.local_scope + edge @name.output -> @name.class_parent_scope + edge @name.local_scope -> @name.input + attr (@name.local_scope -> @name.input) precedence = 1 + attr (@name.input) node_definition = @name attr (@name.output) push_node = @name + + edge @name.new_bindings -> @name.def } +(pattern/subscript) {} + (pattern/attribute attribute: (identifier) @name) { attr (@name.input) is_definition } -(primary_expression/attribute - attribute: (identifier) @name) -{ - attr (@name.output) is_reference -} -(primary_expression/identifier) @id +(list_splat_pattern) {} + +[ + (pattern_list (_) @pattern) + (tuple_pattern (_) @pattern) + (list_pattern (_) @pattern) +] @list { - edge @id.output -> @id.local_scope - edge @id.output -> @id.class_parent_scope - edge @id.local_scope -> @id.input - attr (@id.input) pop_node = @id - attr (@id.output) node_reference = @id + node pattern_index - edge @id.new_bindings -> @id.def + let statement_scope = @list.local_scope + node @pattern.local_scope + edge statement_scope -> @pattern.local_scope + attr (statement_scope -> @pattern.local_scope) precedence = (plus 1 (named-child-index @pattern)) + + edge pattern_index -> @list.input + edge @pattern.input -> pattern_index + attr (pattern_index) push_symbol = (named-child-index @pattern) } -(pattern/identifier) @id -{ - edge @id.output -> @id.local_scope - edge @id.output -> @id.class_parent_scope - edge @id.local_scope -> @id.input - attr (@id.local_scope -> @id.input) precedence = 1 - attr (@id.input) node_definition = @id - attr (@id.output) push_node = @id +(class_pattern) {} - edge @id.new_bindings -> @id.def -} +(splat_pattern) {} + +(union_pattern) {} + +(dict_pattern) {} + +(complex_pattern) {} (as_pattern (expression) @value - alias: (as_pattern_target (primary_expression/identifier) @id)) @as_pattern + alias: (as_pattern_target (primary_expression/identifier) @name)) @as_pattern { - attr (@id.local_scope -> @id.input) precedence = 1 - attr (@id.input) is_definition + attr (@name.local_scope -> @name.input) precedence = 1 + attr (@name.input) is_definition edge @as_pattern.new_bindings -> @value.new_bindings - edge @as_pattern.new_bindings -> @id.new_bindings + edge @as_pattern.new_bindings -> @name.new_bindings } -(list) @list -{ - node called - - edge @list.output -> called - edge called -> @list.global_dot - attr (called) push_symbol = "list" -} +(keyword_pattern) {} -(list (_) @el) @list +(case_pattern (_) @expr) @pat { - edge @list.new_bindings -> @el.new_bindings + edge @pat.new_bindings -> @expr.new_bindings } -(dictionary (pair) @pair) @dict +[ + (assignment + left: (_) @pattern + right: (_) @value) + (with_item + value: + (as_pattern + (_) @value + alias: (as_pattern_target (_) @pattern))) +] { - edge @dict.new_bindings -> @pair.new_bindings + edge @pattern.input -> @value.output } -(pair - value: (_) @value) @pair -{ - edge @pair.new_bindings -> @value.new_bindings -} +;; +;; ##### ####### +;; # # # # # # ##### ## # # # # # ##### ###### #### +;; # # # ## # # # # # # # # # # # # # +;; ##### # # # # # # # ## # # # # ##### #### +;; # # # # # # ###### ## # # ##### # # +;; # # # # ## # # # # # # # # # # # +;; ##### # # # # # # # # # # # ###### #### +;; +;; Syntax Types -(set (_) @el) @set -{ - edge @set.new_bindings -> @el.new_bindings -} +;; +;; BEGIN BIG GNARLY DISJUNCTION +;; +;; The following pair of rules is intended to capture the following behavior: +;; +;; If a function definition is used to define a method, by being inside a class +;; definition, then we make its syntax type `method`. Otherwise, we make it's +;; syntax type `function`. Unfortunately, because of the limitations on negation +;; and binding in tree sitter queries, we cannot negate `class_definition` or +;; similar things directly. Instead, we have to manually push the negation down +;; to form the finite disjunction it corresponds to. +;; -(list_splat (_) @splatted) @splat +[ + (class_definition (block (decorated_definition (function_definition name: (_)@name)))) + (class_definition (block (function_definition name: (_)@name))) +] { - edge @splat.new_bindings -> @splatted.new_bindings + attr (@name.def) syntax_type = "method" } -(binary_operator - (_) @left - (_) @right) @binop -{ - edge @binop.new_bindings -> @left.new_bindings - edge @binop.new_bindings -> @right.new_bindings -} +[ + (module (decorated_definition (function_definition name: (_)@name))) + (module (function_definition name: (_)@name)) -(case_pattern (_) @expr) @pat + (if_statement (block (decorated_definition (function_definition name: (_)@name)))) + (if_statement (block (function_definition name: (_)@name))) + + (elif_clause (block (decorated_definition (function_definition name: (_)@name)))) + (elif_clause (block (function_definition name: (_)@name))) + + (else_clause (block (decorated_definition (function_definition name: (_)@name)))) + (else_clause (block (function_definition name: (_)@name))) + + (case_clause (block (decorated_definition (function_definition name: (_)@name)))) + (case_clause (block (function_definition name: (_)@name))) + + (for_statement (block (decorated_definition (function_definition name: (_)@name)))) + (for_statement (block (function_definition name: (_)@name))) + + (while_statement (block (decorated_definition (function_definition name: (_)@name)))) + (while_statement (block (function_definition name: (_)@name))) + + (try_statement (block (decorated_definition (function_definition name: (_)@name)))) + (try_statement (block (function_definition name: (_)@name))) + + (except_clause (block (decorated_definition (function_definition name: (_)@name)))) + (except_clause (block (function_definition name: (_)@name))) + + (finally_clause (block (decorated_definition (function_definition name: (_)@name)))) + (finally_clause (block (function_definition name: (_)@name))) + + (with_statement (block (decorated_definition (function_definition name: (_)@name)))) + (with_statement (block (function_definition name: (_)@name))) + + (function_definition (block (decorated_definition (function_definition name: (_)@name)))) + (function_definition (block (function_definition name: (_)@name))) +] { - edge @pat.new_bindings -> @expr.new_bindings + attr (@name.def) syntax_type = "function" } + +;; +;; END BIG GNARLY DISJUNCTION +;; diff --git a/languages/tree-sitter-stack-graphs-python/test/pattern_matching.py b/languages/tree-sitter-stack-graphs-python/test/pattern_matching.py index 58c0d16fc..d6b8980a3 100644 --- a/languages/tree-sitter-stack-graphs-python/test/pattern_matching.py +++ b/languages/tree-sitter-stack-graphs-python/test/pattern_matching.py @@ -21,11 +21,11 @@ case { "foo": foo }: print(foo) # ^ defined: 21 - case {bar,quux}: + case {"bar": bar, "quux": quux}: print(bar,quux) # ^ defined: 24 # ^ defined: 24 - case ["grab", { "key": {garply}}]: + case ["grab", { "key": {"garply": garply}}]: print(garply) # ^ defined: 28 case ["drop", *objs]: @@ -37,3 +37,6 @@ case ["go", ("north" | "south" | "east" | "west") as direction2]: current_room = current_room.neighbor(direction2) # ^ defined: 37 + case (foo, "bar"): + print(foo) + # ^ defined: 40 From 41c8f32fa628c86dfdd76e75a3f9debac65c4d0a Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Fri, 17 Nov 2023 19:09:37 +0100 Subject: [PATCH 163/291] Reorganize imports --- .../src/stack-graphs.tsg | 393 ++++++++++-------- ...ed.py.skip => import_submodule_aliased.py} | 2 +- 2 files changed, 232 insertions(+), 163 deletions(-) rename languages/tree-sitter-stack-graphs-python/test/imports/{import_submodule_aliased.py.skip => import_submodule_aliased.py} (89%) diff --git a/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg b/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg index 00314b687..2c8c1e847 100644 --- a/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg +++ b/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg @@ -99,8 +99,6 @@ attribute node_symbol = node => symbol = (source-text node), source_n [ (identifier) - (import_prefix) - (wildcard_import) ] @node { node @node.def node @node.def_dot @@ -108,6 +106,19 @@ attribute node_symbol = node => symbol = (source-text node), source_n node @node.ref_dot } +[ + (dotted_name) + (aliased_import) + (relative_import) + (wildcard_import) + (import_prefix) +] @node { + node @node.after_scope + node @node.before_scope + node @node.def + node @node.ref +} + [ ; expressions (comparison_operator) @@ -349,194 +360,252 @@ inherit .parent_module ;; ;; Imports -;; import _.X -(import_statement - name: (dotted_name - . (identifier) @root_name)) @stmt -{ - edge @stmt.after_scope -> @root_name.def - attr (@stmt.after_scope -> @root_name.def) precedence = 1 - edge @root_name.ref -> ROOT_NODE +;; Import References +;; ^^^^^^^^^^^^^^^^^ + +;;;; Dotted Names +;; +;; (dotted_name).ref node to connect to to use the reference +;; (dotted_name).before_scope node to connect from to ensure the reference resolves + +;; all names are references +[ + (import_statement name: (dotted_name (identifier) @name)) + (future_import_statement name: (dotted_name (identifier) @name)) + (import_from_statement name: (dotted_name (identifier) @name)) + (import_statement name: (aliased_import name: (dotted_name (identifier) @name))) + (future_import_statement name: (aliased_import name: (dotted_name (identifier) @name))) + (import_from_statement name: (aliased_import name: (dotted_name (identifier) @name))) + (import_from_statement module_name: (dotted_name (identifier) @name)) + (import_from_statement module_name: (relative_import (dotted_name (identifier) @name))) +] { + attr (@name.ref) node_reference = @name } -;; import _.X as _ -(import_statement - name: (aliased_import - (dotted_name . (identifier) @root_name))) -{ - edge @root_name.ref -> ROOT_NODE +;; references are chained +[ + (import_statement name: (dotted_name (identifier) @left . (identifier) @right)) + (future_import_statement name: (dotted_name (identifier) @left . (identifier) @right)) + (import_from_statement name: (dotted_name (identifier) @left . (identifier) @right)) + (import_statement name: (aliased_import name: (dotted_name (identifier) @left . (identifier) @right))) + (future_import_statement name: (aliased_import name: (dotted_name (identifier) @left . (identifier) @right))) + (import_from_statement name: (aliased_import name: (dotted_name (identifier) @left . (identifier) @right))) + (import_from_statement module_name: (dotted_name (identifier) @left . (identifier) @right)) + (import_from_statement module_name: (relative_import (dotted_name (identifier) @left . (identifier) @right))) +] { + node push_dot + attr (push_dot) push_symbol = "." + + edge @right.ref -> push_dot + edge push_dot -> @left.ref +} + +;; lookup first reference +[ + (import_statement name: (dotted_name . (identifier) @first) @dotted) + (future_import_statement name: (dotted_name . (identifier) @first) @dotted) + (import_from_statement name: (dotted_name . (identifier) @first) @dotted) + (import_statement name: (aliased_import name: (dotted_name . (identifier) @first) @dotted)) + (future_import_statement name: (aliased_import name: (dotted_name . (identifier) @first) @dotted)) + (import_from_statement name: (aliased_import name: (dotted_name . (identifier) @first) @dotted)) + (import_from_statement module_name: (dotted_name . (identifier) @first) @dotted) + (import_from_statement module_name: (relative_import (dotted_name . (identifier) @first) @dotted)) +] { + edge @first.ref -> @dotted.before_scope +} + +;; expose last reference +[ + (import_statement name: (dotted_name (identifier) @last .) @dotted) + (future_import_statement name: (dotted_name (identifier) @last .) @dotted) + (import_from_statement name: (dotted_name (identifier) @last .) @dotted) + (import_statement name: (aliased_import name: (dotted_name (identifier) @last .) @dotted)) + (future_import_statement name: (aliased_import name: (dotted_name (identifier) @last .) @dotted)) + (import_from_statement name: (aliased_import name: (dotted_name (identifier) @last .) @dotted)) + (import_from_statement module_name: (dotted_name (identifier) @last .) @dotted) + (import_from_statement module_name: (relative_import (dotted_name (identifier) @last .) @dotted)) +] { + edge @dotted.ref -> @last.ref +} + +;;;; Aliased Import +;; +;; An aliased import behaves like its wrapped dotted_name as a reference +;; +;; (aliased_import).ref node to connect to, to use the reference +;; (aliased_import).before_scope node to connect from to ensure the reference resolves + +(aliased_import name: (dotted_name) @dotted) @aliased { + edge @aliased.ref -> @dotted.ref + edge @dotted.before_scope -> @aliased.before_scope } -;; from _ import _.X -(import_from_statement - name: (dotted_name - . (identifier) @import_root_name)) @stmt -{ - edge @stmt.after_scope -> @import_root_name.def - attr (@stmt.after_scope -> @import_root_name.def) precedence = 1 - edge @import_root_name.ref -> @import_root_name.ref_dot - attr (@import_root_name.ref_dot) push_symbol = "." +;;;; Relative Import +;; +;; A relative import behaves like its wrapped dotted_name as a reference +;; +;; (relative_import).ref node to connect to, to use the reference +;; (relative_import).before_scope node to connect from to ensure the reference resolves + +(relative_import (import_prefix) . (dotted_name) @dotted) @relative { + edge @relative.ref -> @dotted.ref + + node push_dot + attr (push_dot) push_symbol = "." + + edge @dotted.before_scope -> push_dot + edge push_dot -> @relative.before_scope } -;; from _ import _.X as _ -(import_from_statement - name: (aliased_import - (dotted_name - . (identifier) @import_root_name))) -{ - edge @import_root_name.ref -> @import_root_name.ref_dot - attr (@import_root_name.ref_dot) push_symbol = "." -} - -;; from X._ import _ -;; from ._.X import _ -;; from .._.X import _ -;; from . import _ -;; from .. import _ -(import_from_statement - module_name: [ - (dotted_name (identifier) @prefix_leaf_name .) - (relative_import (dotted_name (identifier) @prefix_leaf_name .)) - (relative_import (import_prefix) @prefix_leaf_name .) - ] - name: [ - (dotted_name - . (identifier) @import_root_name) - (aliased_import - (dotted_name - . (identifier) @import_root_name)) - ]) -{ - edge @import_root_name.ref_dot -> @prefix_leaf_name.ref +(relative_import (import_prefix) .) @relative { + edge @relative.ref -> @relative.before_scope +} + +;;;; Wildcard Import +;; +;; A wildcard import simply passes through +;; +;; (wildcard_import).ref node to connect to, to use the reference +;; (wildcard_import).before_scope node to connect from to ensure the reference resolves + +(wildcard_import) @wildcard { + edge @wildcard.ref -> @wildcard.before_scope } -;; from _ import _.X as Y -;; import _.X as Y +;;;; Import from +;; +;; The imported references are resolved in the from module + [ - (import_from_statement - (aliased_import - name: (dotted_name (identifier) @name .) - alias: (identifier) @alias)) - (import_statement - (aliased_import - name: (dotted_name (identifier) @name .) - alias: (identifier) @alias)) -] @stmt -{ - edge @stmt.after_scope -> @alias.def - edge @alias.def -> @name.ref + (import_from_statement module_name: (_) @left name: (_) @right) + (import_from_statement module_name: (_) @left (wildcard_import) @right) +] { + node push_dot + attr (push_dot) push_symbol = "." + + edge @right.before_scope -> push_dot + edge push_dot -> @left.ref } -;; import _.X -;; from _ import _.X +;;;; Non-relative Imports +;; +;; Non-relative imports are resolved in the root scope + [ - (import_statement - name: (dotted_name - (identifier) @leaf_name .)) - (import_from_statement - name: (dotted_name - (identifier) @leaf_name .)) -] -{ - edge @leaf_name.def -> @leaf_name.ref + (import_statement name: (_) @name) + (import_from_statement module_name: (dotted_name) @name) +] { + edge @name.before_scope -> ROOT_NODE } -;; . -(relative_import - (import_prefix) @prefix - (#eq? @prefix ".")) @import -{ - edge @prefix.ref -> @import.parent_module +;;;; Relative Imports +;; +;; Relative imports are resolved in scopes related to the current module + +;; . imports resolve in parent module scope +(import_from_statement module_name: (relative_import (import_prefix) @prefix (#eq? @prefix ".")) @relative) { + edge @relative.before_scope -> @prefix.parent_module } -;; .. -(relative_import - (import_prefix) @prefix - (#eq? @prefix "..")) @import -{ - edge @prefix.ref -> @import.grandparent_module +;; .. imports resolve in grandparent module scope +(import_from_statement module_name: (relative_import (import_prefix) @prefix (#eq? @prefix "..")) @relative) { + edge @relative.before_scope -> @prefix.grandparent_module } -;; .X -;; ..X -(relative_import - (import_prefix) @prefix - (dotted_name - . (identifier) @name)) -{ - attr (@name.ref_dot) push_symbol = "." - edge @name.ref -> @name.ref_dot - edge @name.ref_dot -> @prefix.ref +;;;; Future Imports +;; +;; We don't know the future, so we cannot connect references of future imports anywhere. Maybe one day? + +;; Import Definitions +;; ^^^^^^^^^^^^^^^^^^ + +;;;; Dotted Names & Aliased Imports +;; +;; (dotted_name).after_scope node to connect to, to expose the definition +;; (dotted_name).def node to connect from, to give definition content + +;; unaliased names and aliases are definitions +[ + (import_statement name: (dotted_name (identifier) @name)) + (future_import_statement name: (dotted_name (identifier) @name)) + (import_from_statement name: (dotted_name (identifier) @name)) + (import_statement name: (aliased_import alias: (identifier) @name)) + (future_import_statement name: (aliased_import alias: (identifier) @name)) + (import_from_statement name: (aliased_import alias: (identifier) @name)) +] { + attr (@name.def) node_definition = @name } -;; from . +;; definitions are chained [ - (import_from_statement - module_name: (relative_import - (dotted_name - (identifier) @parent_name - . - (identifier) @child_name))) - (import_from_statement - module_name: (dotted_name - (identifier) @parent_name - . - (identifier) @child_name)) -] -{ - attr (@child_name.ref_dot) push_symbol = "." - edge @child_name.ref -> @child_name.ref_dot - edge @child_name.ref_dot -> @parent_name.ref + (import_statement name: (dotted_name (identifier) @left . (identifier) @right)) + (future_import_statement name: (dotted_name (identifier) @left . (identifier) @right)) + (import_from_statement name: (dotted_name (identifier) @left . (identifier) @right)) +] { + node pop_dot + attr (pop_dot) pop_symbol = "." + + edge @left.def -> pop_dot + edge pop_dot -> @right.def } -;; from X._ import _ -(import_from_statement - module_name: (dotted_name - . (identifier) @root_name)) -{ - edge @root_name.ref -> ROOT_NODE +;; connect last definition +[ + (import_statement name: (dotted_name (identifier) @last .) @outer) + (future_import_statement name: (dotted_name (identifier) @last .) @outer) + (import_from_statement name: (dotted_name (identifier) @last .) @outer) + (import_statement name: (aliased_import alias: (identifier) @last ) @outer) + (future_import_statement name: (aliased_import alias: (identifier) @last ) @outer) + (import_from_statement name: (aliased_import alias: (identifier) @last ) @outer) +] { + edge @last.def -> @outer.def } -;; from _.X import * -(import_from_statement - module_name: (dotted_name - (identifier) @leaf_name .) - (wildcard_import) @star) @stmt -{ - edge @stmt.after_scope -> @star.ref_dot - attr (@stmt.after_scope -> @star.ref_dot) precedence = 1 - edge @star.ref_dot -> @leaf_name.ref - attr (@star.ref_dot) push_symbol = "." +;; expose first definition +[ + (import_statement name: (dotted_name . (identifier) @first) @outer) + (future_import_statement name: (dotted_name . (identifier) @first) @outer) + (import_from_statement name: (dotted_name . (identifier) @first) @outer) + (import_statement name: (aliased_import alias: (identifier) @first ) @outer) + (future_import_statement name: (aliased_import alias: (identifier) @first ) @outer) + (import_from_statement name: (aliased_import alias: (identifier) @first ) @outer) +] { + edge @outer.after_scope -> @first.def +} + +;;;; Wildcard Import +;; +;; Wildcard imports simply pass through + +(wildcard_import) @wildcard { + edge @wildcard.after_scope -> @wildcard.def } -;; import _.X.Y._ -;; from _ import _.X.Y._ -;; from _ import _.X.Y._ as _ +;;;; Import Definitions -> References +;; +;; The definitions introduced by imports are connected to the corresponding references + [ - (import_statement - name: (dotted_name - (identifier) @parent_name - . - (identifier) @child_name)) - (import_from_statement - name: (dotted_name - (identifier) @parent_name - . - (identifier) @child_name)) - (import_from_statement - name: (aliased_import - name: (dotted_name - (identifier) @parent_name - . - (identifier) @child_name))) -] -{ - edge @child_name.ref -> @child_name.ref_dot - edge @child_name.ref_dot -> @parent_name.ref - edge @parent_name.def -> @parent_name.def_dot - edge @parent_name.def_dot -> @child_name.def - attr (@parent_name.def_dot) pop_symbol = "." - attr (@child_name.ref_dot) push_symbol = "." + (import_statement name: (_) @name) + (future_import_statement name: (_) @name) + (import_from_statement name: (_) @name) + (import_from_statement (wildcard_import) @name) +] { + edge @name.def -> @name.ref +} + +;;;; Imports +;; +;; The definitions introduced by imports are visible after the import statement + +[ + (import_statement name: (_) @name) + (future_import_statement name: (_) @name) + (import_from_statement name: (_) @name) + (import_from_statement (wildcard_import) @name) +] @stmt { + edge @stmt.after_scope -> @name.after_scope + attr (@stmt.after_scope -> @name.after_scope) precedence = 1 } ;; diff --git a/languages/tree-sitter-stack-graphs-python/test/imports/import_submodule_aliased.py.skip b/languages/tree-sitter-stack-graphs-python/test/imports/import_submodule_aliased.py similarity index 89% rename from languages/tree-sitter-stack-graphs-python/test/imports/import_submodule_aliased.py.skip rename to languages/tree-sitter-stack-graphs-python/test/imports/import_submodule_aliased.py index dd84f39b5..b6bfdf7b7 100644 --- a/languages/tree-sitter-stack-graphs-python/test/imports/import_submodule_aliased.py.skip +++ b/languages/tree-sitter-stack-graphs-python/test/imports/import_submodule_aliased.py @@ -7,7 +7,7 @@ import foo.bar as qux qux.BAR -# ^ defined: 7 +# ^ defined: 7, 3 # ^ defined: 3 foo From 9135ff5cb66a71e9cda1f11af85e1903066ee4ed Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Thu, 30 Nov 2023 17:59:53 +0100 Subject: [PATCH 164/291] Fix parameter definitions --- .../src/stack-graphs.tsg | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg b/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg index 2c8c1e847..e784c37fc 100644 --- a/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg +++ b/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg @@ -813,12 +813,10 @@ inherit .parent_module edge @param.param_name -> @params.before_scope } -(parameter/identifier) @name { - attr (@name.def) node_definition = @name -} (parameter/identifier) @param @name { + attr (@name.def) node_definition = @name attr (@param.param_name) push_node = @param edge @name.def -> @param.param_name edge @name.def -> @param.param_index @@ -827,13 +825,13 @@ inherit .parent_module [ (parameter/default_parameter - name: (identifier) @name + name: (_) @name value: (_) @value) @param (parameter/typed_default_parameter name: (_) @name value: (_) @value) @param -] -{ +] { + attr (@name.def) node_definition = @name attr (@param.param_name) push_node = @name edge @name.def -> @param.param_name edge @name.def -> @param.param_index @@ -848,8 +846,8 @@ inherit .parent_module (_) @name) @param (parameter/dictionary_splat_pattern (_) @name) @param -] -{ +] { + attr (@name.def) node_definition = @name attr (@param.param_name) push_node = @name edge @name.def -> @param.param_name edge @name.def -> @param.param_index From 0f4bf9b5e14a71f451663c0383a6f26fe9c566c1 Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Thu, 30 Nov 2023 23:47:41 +0100 Subject: [PATCH 165/291] Fixes for pattern identifiers --- .../src/stack-graphs.tsg | 58 ++++++++++++------- 1 file changed, 38 insertions(+), 20 deletions(-) diff --git a/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg b/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg index e784c37fc..7cddd6783 100644 --- a/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg +++ b/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg @@ -1163,20 +1163,28 @@ inherit .parent_module ;; ;; Patterns -(pattern/identifier) @name { - attr (@name.def) node_definition = @name -} -(pattern/identifier) @name -{ - edge @name.output -> @name.local_scope - edge @name.output -> @name.class_parent_scope - edge @name.local_scope -> @name.input - attr (@name.local_scope -> @name.input) precedence = 1 - attr (@name.input) node_definition = @name - attr (@name.output) push_node = @name +[ + (pattern/identifier) @name @pattern + ; identifiers in patterns + (as_pattern (identifier) @name .) @pattern + (keyword_pattern (identifier) @name) @pattern + (splat_pattern (identifier) @name) @pattern + ; every context where _simple_pattern is used, because matching + ; pattern/dotted_name does not work + (case_pattern (dotted_name . (_) @name .) @pattern) + (keyword_pattern (dotted_name . (_) @name .) @pattern) + (union_pattern (dotted_name . (_) @name .) @pattern) +] { + attr (@name.def) node_definition = @name + edge @pattern.output -> @pattern.local_scope + edge @pattern.output -> @pattern.class_parent_scope + edge @pattern.local_scope -> @pattern.input + attr (@pattern.local_scope -> @pattern.input) precedence = 1 + attr (@pattern.input) node_definition = @name + attr (@pattern.output) push_node = @name - edge @name.new_bindings -> @name.def + edge @pattern.new_bindings -> @name.def } (pattern/subscript) {} @@ -1188,7 +1196,9 @@ inherit .parent_module } -(list_splat_pattern) {} +(list_splat_pattern (_) @pattern) @list { + edge @list.new_bindings -> @pattern.new_bindings +} [ (pattern_list (_) @pattern) @@ -1208,19 +1218,27 @@ inherit .parent_module attr (pattern_index) push_symbol = (named-child-index @pattern) } -(class_pattern) {} +(class_pattern (case_pattern) @pattern) @class { + edge @class.new_bindings -> @pattern.new_bindings +} -(splat_pattern) {} +(splat_pattern (_) @pattern) @splat { + edge @splat.new_bindings -> @pattern.new_bindings +} -(union_pattern) {} +(union_pattern (_) @pattern) @union { + edge @union.new_bindings -> @pattern.new_bindings +} -(dict_pattern) {} +(dict_pattern (_) @pattern) @dict { + edge @dict.new_bindings -> @pattern.new_bindings +} (complex_pattern) {} (as_pattern (expression) @value - alias: (as_pattern_target (primary_expression/identifier) @name)) @as_pattern + alias: (as_pattern_target (identifier) @name)) @as_pattern { attr (@name.local_scope -> @name.input) precedence = 1 attr (@name.input) is_definition @@ -1231,9 +1249,9 @@ inherit .parent_module (keyword_pattern) {} -(case_pattern (_) @expr) @pat +(case_pattern (_) @pattern) @case { - edge @pat.new_bindings -> @expr.new_bindings + edge @case.new_bindings -> @pattern.new_bindings } [ From 25ecd41bbaa97bbfe7e7a1f3f2a7665b08b4ccdb Mon Sep 17 00:00:00 2001 From: Rob Rix Date: Mon, 29 Jan 2024 10:56:29 -0500 Subject: [PATCH 166/291] Object patterns. --- .../tree-sitter-stack-graphs-javascript/test/base_syntax.js | 1 + 1 file changed, 1 insertion(+) diff --git a/languages/tree-sitter-stack-graphs-javascript/test/base_syntax.js b/languages/tree-sitter-stack-graphs-javascript/test/base_syntax.js index 99a0f98b2..4036a2cd6 100644 --- a/languages/tree-sitter-stack-graphs-javascript/test/base_syntax.js +++ b/languages/tree-sitter-stack-graphs-javascript/test/base_syntax.js @@ -69,6 +69,7 @@ x++; 1 + 1; -2; (x = 1); +({ x: y } = 1); x += 1; (1, 2); 1 ? 2 : 3; From c6fe01a91f43dbaaf878809733a4262da76f9f91 Mon Sep 17 00:00:00 2001 From: Rob Rix Date: Mon, 29 Jan 2024 10:57:00 -0500 Subject: [PATCH 167/291] Object patterns with comments. --- .../tree-sitter-stack-graphs-javascript/test/base_syntax.js | 1 + 1 file changed, 1 insertion(+) diff --git a/languages/tree-sitter-stack-graphs-javascript/test/base_syntax.js b/languages/tree-sitter-stack-graphs-javascript/test/base_syntax.js index 4036a2cd6..817d41eaa 100644 --- a/languages/tree-sitter-stack-graphs-javascript/test/base_syntax.js +++ b/languages/tree-sitter-stack-graphs-javascript/test/base_syntax.js @@ -70,6 +70,7 @@ x++; -2; (x = 1); ({ x: y } = 1); +({/**/x: y } = 1); x += 1; (1, 2); 1 ? 2 : 3; From 95dda7decd09d6419c9cdf01dd27cfa7a62fe74f Mon Sep 17 00:00:00 2001 From: Rob Rix Date: Mon, 29 Jan 2024 11:00:16 -0500 Subject: [PATCH 168/291] Allow comments occurring in object patterns. --- .../tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg | 1 + 1 file changed, 1 insertion(+) diff --git a/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg b/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg index 5d4100acb..25df7238d 100644 --- a/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg +++ b/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg @@ -399,6 +399,7 @@ inherit .return_or_yield node @comment.before_scope node @comment.value node @comment.covalue + node @comment.new_bindings ; for object patterns edge @comment.after_scope -> @comment.before_scope } From 1c2b0b04f7c50c9e13c933414e4e3e21c309fd0c Mon Sep 17 00:00:00 2001 From: Rob Rix Date: Mon, 29 Jan 2024 11:04:47 -0500 Subject: [PATCH 169/291] Add a parameter. --- .../tree-sitter-stack-graphs-javascript/test/base_syntax.js | 1 + 1 file changed, 1 insertion(+) diff --git a/languages/tree-sitter-stack-graphs-javascript/test/base_syntax.js b/languages/tree-sitter-stack-graphs-javascript/test/base_syntax.js index 99a0f98b2..f5986e241 100644 --- a/languages/tree-sitter-stack-graphs-javascript/test/base_syntax.js +++ b/languages/tree-sitter-stack-graphs-javascript/test/base_syntax.js @@ -8,6 +8,7 @@ debugger; var x; let x; function foo() { } +function foo(a) { } function* foo() { } class Foo { } { } From 5a22ebb33e597b3c23e40fc322edc16deaba4866 Mon Sep 17 00:00:00 2001 From: Rob Rix Date: Mon, 29 Jan 2024 11:05:45 -0500 Subject: [PATCH 170/291] What if the parameter was named all collisiony? --- .../tree-sitter-stack-graphs-javascript/test/base_syntax.js | 1 + 1 file changed, 1 insertion(+) diff --git a/languages/tree-sitter-stack-graphs-javascript/test/base_syntax.js b/languages/tree-sitter-stack-graphs-javascript/test/base_syntax.js index f5986e241..b659631e5 100644 --- a/languages/tree-sitter-stack-graphs-javascript/test/base_syntax.js +++ b/languages/tree-sitter-stack-graphs-javascript/test/base_syntax.js @@ -9,6 +9,7 @@ var x; let x; function foo() { } function foo(a) { } +function foo(undefined) { } function* foo() { } class Foo { } { } From 3638c3010c91f5cb9cb95e7755208580bbd673e2 Mon Sep 17 00:00:00 2001 From: Rob Rix Date: Mon, 29 Jan 2024 11:08:24 -0500 Subject: [PATCH 171/291] Hax! --- .../tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg | 3 +++ 1 file changed, 3 insertions(+) diff --git a/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg b/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg index 5d4100acb..51a60c684 100644 --- a/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg +++ b/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg @@ -2412,6 +2412,9 @@ inherit .return_or_yield ; the value of undefined is the undefined primitive edge @undefined.value -> @undefined.builtins_undefined + + ; HACK: `undefined` is a perfectly cromulent name for a parameter, but the parser thinks it means this node instead. For the moment, work around the problem this causes by giving all `undefined` nodes covalues like parameters enjoy. + node @undefined.covalue } From fbc9e1745212baf9679e537752a5d9e6930b099b Mon Sep 17 00:00:00 2001 From: Rob Rix Date: Mon, 29 Jan 2024 11:15:01 -0500 Subject: [PATCH 172/291] Array patterns. --- .../tree-sitter-stack-graphs-javascript/test/base_syntax.js | 1 + 1 file changed, 1 insertion(+) diff --git a/languages/tree-sitter-stack-graphs-javascript/test/base_syntax.js b/languages/tree-sitter-stack-graphs-javascript/test/base_syntax.js index 99a0f98b2..e9828c76c 100644 --- a/languages/tree-sitter-stack-graphs-javascript/test/base_syntax.js +++ b/languages/tree-sitter-stack-graphs-javascript/test/base_syntax.js @@ -69,6 +69,7 @@ x++; 1 + 1; -2; (x = 1); +([x] = 1); x += 1; (1, 2); 1 ? 2 : 3; From 0ff55a0cc4020623459daaee05bbb1da940427e9 Mon Sep 17 00:00:00 2001 From: Rob Rix Date: Mon, 29 Jan 2024 11:15:34 -0500 Subject: [PATCH 173/291] Subscripts in patterns. --- .../tree-sitter-stack-graphs-javascript/test/base_syntax.js | 1 + 1 file changed, 1 insertion(+) diff --git a/languages/tree-sitter-stack-graphs-javascript/test/base_syntax.js b/languages/tree-sitter-stack-graphs-javascript/test/base_syntax.js index e9828c76c..c5a1b459c 100644 --- a/languages/tree-sitter-stack-graphs-javascript/test/base_syntax.js +++ b/languages/tree-sitter-stack-graphs-javascript/test/base_syntax.js @@ -70,6 +70,7 @@ x++; -2; (x = 1); ([x] = 1); +([x[i]] = 1); x += 1; (1, 2); 1 ? 2 : 3; From 8b3862fc025698dc6d9aad1a64b7d9aea67e0ccf Mon Sep 17 00:00:00 2001 From: Rob Rix Date: Mon, 29 Jan 2024 11:21:29 -0500 Subject: [PATCH 174/291] Subscript expressions get covalues. --- .../tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg | 2 ++ 1 file changed, 2 insertions(+) diff --git a/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg b/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg index 5d4100acb..5b30aa556 100644 --- a/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg +++ b/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg @@ -3132,6 +3132,8 @@ inherit .return_or_yield edge @subscript_expr.value -> @subscript_expr.index_push edge @subscript_expr.index_push -> subscript_expr_push_dot + ; subscript expressions can appear in array patterns, on the left, and thus require a covalue + node @subscript_expr.covalue } (subscript_expression From 9e1b1fd529836d64a71e341bfa8cb4b0dcd94db0 Mon Sep 17 00:00:00 2001 From: Rob Rix Date: Mon, 29 Jan 2024 11:22:07 -0500 Subject: [PATCH 175/291] Subscript expressions get bindings. --- .../tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg b/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg index 5b30aa556..69d1737ae 100644 --- a/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg +++ b/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg @@ -3132,8 +3132,9 @@ inherit .return_or_yield edge @subscript_expr.value -> @subscript_expr.index_push edge @subscript_expr.index_push -> subscript_expr_push_dot - ; subscript expressions can appear in array patterns, on the left, and thus require a covalue + ; subscript expressions can appear in array patterns, on the left, and thus require a covalue & bindings node @subscript_expr.covalue + node @subscript_expr.new_bindings } (subscript_expression From 39490e385290414367c2bf1b4f217cade4981728 Mon Sep 17 00:00:00 2001 From: Rob Rix Date: Mon, 29 Jan 2024 11:23:46 -0500 Subject: [PATCH 176/291] Simplify the test. --- .../tree-sitter-stack-graphs-javascript/test/base_syntax.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/languages/tree-sitter-stack-graphs-javascript/test/base_syntax.js b/languages/tree-sitter-stack-graphs-javascript/test/base_syntax.js index c5a1b459c..ba2101250 100644 --- a/languages/tree-sitter-stack-graphs-javascript/test/base_syntax.js +++ b/languages/tree-sitter-stack-graphs-javascript/test/base_syntax.js @@ -69,8 +69,8 @@ x++; 1 + 1; -2; (x = 1); +(x[i] = 1); ([x] = 1); -([x[i]] = 1); x += 1; (1, 2); 1 ? 2 : 3; From 19bba869d9fe983050de2530c4fa899f81c04a87 Mon Sep 17 00:00:00 2001 From: Rob Rix Date: Mon, 29 Jan 2024 11:32:30 -0500 Subject: [PATCH 177/291] Blank. --- .../tree-sitter-stack-graphs-javascript/test/base_syntax.js | 1 + 1 file changed, 1 insertion(+) diff --git a/languages/tree-sitter-stack-graphs-javascript/test/base_syntax.js b/languages/tree-sitter-stack-graphs-javascript/test/base_syntax.js index 99a0f98b2..cac4d04df 100644 --- a/languages/tree-sitter-stack-graphs-javascript/test/base_syntax.js +++ b/languages/tree-sitter-stack-graphs-javascript/test/base_syntax.js @@ -77,4 +77,5 @@ class { }; <>doo {garply} + {} \ No newline at end of file From 0f2ab27fc4454845aee0c6c437c008456aa1de8e Mon Sep 17 00:00:00 2001 From: Rob Rix Date: Mon, 29 Jan 2024 11:37:51 -0500 Subject: [PATCH 178/291] Space. --- .../tree-sitter-stack-graphs-javascript/test/base_syntax.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/languages/tree-sitter-stack-graphs-javascript/test/base_syntax.js b/languages/tree-sitter-stack-graphs-javascript/test/base_syntax.js index cac4d04df..ee7e35755 100644 --- a/languages/tree-sitter-stack-graphs-javascript/test/base_syntax.js +++ b/languages/tree-sitter-stack-graphs-javascript/test/base_syntax.js @@ -77,5 +77,5 @@ class { }; <>doo {garply} - {} + { } \ No newline at end of file From 8bf750acd511923d9900ce8b1eff31780cbbaf1e Mon Sep 17 00:00:00 2001 From: Rob Rix Date: Mon, 29 Jan 2024 11:38:05 -0500 Subject: [PATCH 179/291] Comment. --- .../tree-sitter-stack-graphs-javascript/test/base_syntax.js | 1 + 1 file changed, 1 insertion(+) diff --git a/languages/tree-sitter-stack-graphs-javascript/test/base_syntax.js b/languages/tree-sitter-stack-graphs-javascript/test/base_syntax.js index ee7e35755..fe122fd94 100644 --- a/languages/tree-sitter-stack-graphs-javascript/test/base_syntax.js +++ b/languages/tree-sitter-stack-graphs-javascript/test/base_syntax.js @@ -78,4 +78,5 @@ class { }; <>doo {garply} { } + {/**/x} \ No newline at end of file From b218bad5b8879fa7896dba046846d805d86da7b3 Mon Sep 17 00:00:00 2001 From: Rob Rix Date: Mon, 29 Jan 2024 11:39:51 -0500 Subject: [PATCH 180/291] Allow for comments in JSX expressions. --- .../src/stack-graphs.tsg | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg b/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg index 5d4100acb..1b6b3e778 100644 --- a/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg +++ b/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg @@ -3675,11 +3675,15 @@ inherit .return_or_yield } -(jsx_expression (_)?@child)@jsx_expression { +(jsx_expression)@jsx_expression { node @jsx_expression.before_scope node @jsx_expression.after_scope +} + +(jsx_expression (_)?@child)@jsx_expression { + if none @child { edge @jsx_expression.after_scope -> @jsx_expression.before_scope } else { From 3efe33ef883653334e2c52ee88216963c81711c9 Mon Sep 17 00:00:00 2001 From: Rob Rix Date: Mon, 29 Jan 2024 11:46:43 -0500 Subject: [PATCH 181/291] Member expressions in patterns. --- .../tree-sitter-stack-graphs-javascript/test/base_syntax.js | 1 + 1 file changed, 1 insertion(+) diff --git a/languages/tree-sitter-stack-graphs-javascript/test/base_syntax.js b/languages/tree-sitter-stack-graphs-javascript/test/base_syntax.js index 99a0f98b2..dde20b4a1 100644 --- a/languages/tree-sitter-stack-graphs-javascript/test/base_syntax.js +++ b/languages/tree-sitter-stack-graphs-javascript/test/base_syntax.js @@ -69,6 +69,7 @@ x++; 1 + 1; -2; (x = 1); +({x:y.z} = 1); x += 1; (1, 2); 1 ? 2 : 3; From a6ebb27be5ab47fa9b093487ae9104991efa8197 Mon Sep 17 00:00:00 2001 From: Rob Rix Date: Mon, 29 Jan 2024 11:47:43 -0500 Subject: [PATCH 182/291] Member expressions get covalues. --- .../tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg | 2 ++ 1 file changed, 2 insertions(+) diff --git a/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg b/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg index 5d4100acb..8c126984c 100644 --- a/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg +++ b/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg @@ -3108,6 +3108,8 @@ inherit .return_or_yield edge @member_expr.value -> property_push edge member_push -> @object.value + ; (member_expression) nodes can occur in patterns + node @member_expr.covalue } ;; ##### Subscript Expressions From a0d8fb64711d2b794e1b9a416fe8155749318318 Mon Sep 17 00:00:00 2001 From: Rob Rix Date: Mon, 29 Jan 2024 11:48:02 -0500 Subject: [PATCH 183/291] Member expressions get bindings. --- .../tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg | 1 + 1 file changed, 1 insertion(+) diff --git a/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg b/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg index 8c126984c..f4ee64588 100644 --- a/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg +++ b/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg @@ -3110,6 +3110,7 @@ inherit .return_or_yield ; (member_expression) nodes can occur in patterns node @member_expr.covalue + node @member_expr.new_bindings } ;; ##### Subscript Expressions From 5bfd95af033c9c66e84e2d939cf460cf33480a5d Mon Sep 17 00:00:00 2001 From: Rob Rix Date: Mon, 29 Jan 2024 11:54:40 -0500 Subject: [PATCH 184/291] Tests returns of values from the top-level scope. --- .../tree-sitter-stack-graphs-javascript/test/base_syntax.js | 1 + 1 file changed, 1 insertion(+) diff --git a/languages/tree-sitter-stack-graphs-javascript/test/base_syntax.js b/languages/tree-sitter-stack-graphs-javascript/test/base_syntax.js index 99a0f98b2..4213068ea 100644 --- a/languages/tree-sitter-stack-graphs-javascript/test/base_syntax.js +++ b/languages/tree-sitter-stack-graphs-javascript/test/base_syntax.js @@ -27,6 +27,7 @@ with (x) { } break; continue; return; +return x; ; foo: x; From 198e06ce1b4b562e4691a13df3e29b7af254c26d Mon Sep 17 00:00:00 2001 From: Rob Rix Date: Mon, 29 Jan 2024 11:55:09 -0500 Subject: [PATCH 185/291] Plumb the program node for returns. --- .../tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg | 3 +++ 1 file changed, 3 insertions(+) diff --git a/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg b/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg index 5d4100acb..e182aa5ef 100644 --- a/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg +++ b/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg @@ -282,6 +282,9 @@ inherit .return_or_yield node @prog.after_scope node @prog.before_scope let @prog.closure_point = @prog.after_scope + + ; apparently it's perfectly cromulent to `return` from the top level scope (because control flow happens there too), so we make a top-level return node. + node @prog.return_or_yield } ;; ### Program Queries From cf5a9f6fe774295531fe9a11820e65d2fe38cc3a Mon Sep 17 00:00:00 2001 From: Rob Rix Date: Mon, 29 Jan 2024 12:46:17 -0500 Subject: [PATCH 186/291] Emphatically hacky. --- .../tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg b/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg index 51a60c684..fccd318d9 100644 --- a/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg +++ b/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg @@ -2413,7 +2413,7 @@ inherit .return_or_yield ; the value of undefined is the undefined primitive edge @undefined.value -> @undefined.builtins_undefined - ; HACK: `undefined` is a perfectly cromulent name for a parameter, but the parser thinks it means this node instead. For the moment, work around the problem this causes by giving all `undefined` nodes covalues like parameters enjoy. + ; !!!! HACK: `undefined` is a perfectly cromulent name for a parameter, but the parser thinks it means this node instead. For the moment, work around the problem this causes by giving all `undefined` nodes covalues like parameters enjoy. node @undefined.covalue } From 16efa5cc91ee5aa7af498096d7b08a934164d6c3 Mon Sep 17 00:00:00 2001 From: Rebecca Valentine Date: Thu, 1 Feb 2024 12:33:58 -0800 Subject: [PATCH 187/291] Adds missing function syntax type --- .../tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg | 1 + 1 file changed, 1 insertion(+) diff --git a/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg b/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg index d8b383c35..9bc0831b8 100644 --- a/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg +++ b/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg @@ -4792,6 +4792,7 @@ inherit .containing_class_value attr (left_ignore_guard) pop_symbol = "GUARD:GANDALF" attr (left_definiens_hook) node_definition = @left attr (left_definiens_hook) definiens_node = @assignment_expr + attr (left_definiens_hook) syntax_type = "function" edge @assignment_expr.pkg_pop -> left_ignore_guard edge left_ignore_guard -> left_definiens_hook From fea338920dc3d8b1a4c1cea74eab404903a6eab0 Mon Sep 17 00:00:00 2001 From: Rebecca Valentine Date: Thu, 1 Feb 2024 13:35:29 -0800 Subject: [PATCH 188/291] Fixes predicate bug --- .../src/stack-graphs.tsg | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg b/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg index 9bc0831b8..17de7f014 100644 --- a/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg +++ b/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg @@ -4761,8 +4761,8 @@ inherit .containing_class_value property:(_)@left ) right: (_))@assignment_expr - (#not-eq @_object "module") - (#not-eq @left "exports") + (#not-eq? @_object "module") + (#not-eq? @left "exports") ) { node left_definiens_hook @@ -4782,8 +4782,8 @@ inherit .containing_class_value property:(_)@left ) right: (_))@assignment_expr - (#eq @_object "module") - (#eq @left "exports") + (#eq? @_object "module") + (#eq? @left "exports") ) { node left_definiens_hook From 94de9153970f0a2f8b509cca504a875f45378a5c Mon Sep 17 00:00:00 2001 From: Rebecca Valentine Date: Thu, 1 Feb 2024 14:36:18 -0800 Subject: [PATCH 189/291] Revert "Adds missing function syntax type" This reverts commit 16efa5cc91ee5aa7af498096d7b08a934164d6c3. --- .../tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg | 1 - 1 file changed, 1 deletion(-) diff --git a/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg b/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg index 17de7f014..273e2388e 100644 --- a/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg +++ b/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg @@ -4792,7 +4792,6 @@ inherit .containing_class_value attr (left_ignore_guard) pop_symbol = "GUARD:GANDALF" attr (left_definiens_hook) node_definition = @left attr (left_definiens_hook) definiens_node = @assignment_expr - attr (left_definiens_hook) syntax_type = "function" edge @assignment_expr.pkg_pop -> left_ignore_guard edge left_ignore_guard -> left_definiens_hook From 26bc1b12b4ad4bd448b3e5adfd53cb7a040a8ca9 Mon Sep 17 00:00:00 2001 From: Rebecca Valentine Date: Thu, 1 Feb 2024 15:12:45 -0800 Subject: [PATCH 190/291] Fixes syntax types for exports --- .../src/stack-graphs.tsg | 240 +++++++++++++++--- 1 file changed, 204 insertions(+), 36 deletions(-) diff --git a/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg b/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg index 273e2388e..e67bfe332 100644 --- a/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg +++ b/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg @@ -557,6 +557,54 @@ inherit .containing_class_value } +(export_statement "default" + value:(_)@value)@export_stmt { + + node @export_stmt.pop_guard_default + attr (@export_stmt.pop_guard_default) symbol_definition = "GUARD:DEFAULT", source_node = @value, definiens_node = @export_stmt.export_statement + edge @export_stmt.exports -> @export_stmt.pop_guard_default + edge @export_stmt.pop_guard_default -> @value.value + + ; !!!! HACK These detour nodes are a massive hack to allow find all refs land on defs + ; for the default values of modules that have useful names like the module name or + ; package name + + node detour_push + node @export_stmt.detour_pop + + scan FILE_PATH { + + "^(.+/)?([^/]+)/index\.js$" { + let module_name = $2 + attr (detour_push) push_symbol = module_name + attr (@export_stmt.detour_pop) symbol_definition = module_name, source_node = @value, definiens_node = @export_stmt + edge @export_stmt.pop_guard_default -> detour_push + edge detour_push -> @export_stmt.detour_pop + edge @export_stmt.detour_pop -> @value.value + } + + "^(.+/)?([^/]+)\.js$" { + let module_name = $2 + attr (detour_push) push_symbol = module_name + attr (@export_stmt.detour_pop) symbol_definition = module_name, source_node = @value, definiens_node = @export_stmt + edge @export_stmt.pop_guard_default -> detour_push + edge detour_push -> @export_stmt.detour_pop + edge @export_stmt.detour_pop -> @value.value + } + + } + + node default_detour_push + node @export_stmt.default_detour_pop + + attr (default_detour_push) push_symbol = "default" + attr (@export_stmt.default_detour_pop) symbol_definition = "default", source_node = @value, definiens_node = @value + edge @export_stmt.pop_guard_default -> default_detour_push + edge default_detour_push -> @export_stmt.default_detour_pop + edge @export_stmt.default_detour_pop -> @value.value + +} + (export_statement declaration: [ (function_declaration name:(identifier)@name) @@ -4438,7 +4486,7 @@ inherit .containing_class_value node pop_default_guard node pop_dot - node pop_name + node @assignment_expr.pop_name attr (pop_default_guard) symbol_definition = "GUARD:DEFAULT", source_node = @exports edge @assignment_expr.exports -> pop_default_guard @@ -4446,47 +4494,47 @@ inherit .containing_class_value attr (pop_dot) pop_symbol = "GUARD:MEMBER" edge pop_default_guard -> pop_dot - attr (pop_name) node_definition = @property - attr (pop_name) definiens_node = @assignment_expr - edge pop_dot -> pop_name - edge pop_name -> @right.value + attr (@assignment_expr.pop_name) node_definition = @property + attr (@assignment_expr.pop_name) definiens_node = @assignment_expr + edge pop_dot -> @assignment_expr.pop_name + edge @assignment_expr.pop_name -> @right.value ;; For ES6 interoperability, expose members as named exports - edge @assignment_expr.exports -> pop_name + edge @assignment_expr.exports -> @assignment_expr.pop_name node detour_push - node detour_pop + node @assignment_expr.detour_pop scan FILE_PATH { "^(.+/)?([^/]+)/index\.js$" { let module_name = $2 attr (detour_push) push_symbol = module_name - attr (detour_pop) symbol_definition = module_name, source_node = @assignment_expr, definiens_node = @assignment_expr + attr (@assignment_expr.detour_pop) symbol_definition = module_name, source_node = @assignment_expr, definiens_node = @assignment_expr edge pop_default_guard -> detour_push - edge detour_push -> detour_pop - edge detour_pop -> @right.value + edge detour_push -> @assignment_expr.detour_pop + edge @assignment_expr.detour_pop -> @right.value } "^(.+/)?([^/]+)\.js$" { let module_name = $2 attr (detour_push) push_symbol = module_name - attr (detour_pop) symbol_definition = module_name, source_node = @assignment_expr, definiens_node = @assignment_expr + attr (@assignment_expr.detour_pop) symbol_definition = module_name, source_node = @assignment_expr, definiens_node = @assignment_expr edge pop_default_guard -> detour_push - edge detour_push -> detour_pop - edge detour_pop -> @right.value + edge detour_push -> @assignment_expr.detour_pop + edge @assignment_expr.detour_pop -> @right.value } } node default_detour_push - node default_detour_pop + node @assignment_expr.default_detour_pop attr (default_detour_push) push_symbol = "default" - attr (default_detour_pop) symbol_definition = "default", source_node = @assignment_expr, definiens_node = @assignment_expr + attr (@assignment_expr.default_detour_pop) symbol_definition = "default", source_node = @assignment_expr, definiens_node = @assignment_expr edge pop_default_guard -> default_detour_push - edge default_detour_push -> default_detour_pop - edge default_detour_pop -> @right.value + edge default_detour_push -> @assignment_expr.default_detour_pop + edge @assignment_expr.default_detour_pop -> @right.value } @@ -4500,13 +4548,13 @@ inherit .containing_class_value (#eq? @exports "exports") ) { - node pop_default_guard + node @assignment_expr.pop_default_guard node pop_dot - attr (pop_default_guard) symbol_definition = "GUARD:DEFAULT", source_node = @exports - attr (pop_default_guard) definiens_node = @assignment_expr - edge @assignment_expr.exports -> pop_default_guard - edge pop_default_guard -> @right.value + attr (@assignment_expr.pop_default_guard) symbol_definition = "GUARD:DEFAULT", source_node = @exports + attr (@assignment_expr.pop_default_guard) definiens_node = @assignment_expr + edge @assignment_expr.exports -> @assignment_expr.pop_default_guard + edge @assignment_expr.pop_default_guard -> @right.value ;; For ES6 interoperability, expose members as named exports attr (pop_dot) pop_symbol = "GUARD:MEMBER" @@ -4514,38 +4562,38 @@ inherit .containing_class_value edge pop_dot -> @right.value node detour_push - node detour_pop + node @assignment_expr.detour_pop scan FILE_PATH { "^(.+/)?([^/]+)/index\.js$" { let module_name = $2 attr (detour_push) push_symbol = module_name - attr (detour_pop) symbol_definition = module_name, source_node = @assignment_expr, definiens_node = @assignment_expr - edge pop_default_guard -> detour_push - edge detour_push -> detour_pop - edge detour_pop -> @right.value + attr (@assignment_expr.detour_pop) symbol_definition = module_name, source_node = @assignment_expr, definiens_node = @assignment_expr + edge @assignment_expr.pop_default_guard -> detour_push + edge detour_push -> @assignment_expr.detour_pop + edge @assignment_expr.detour_pop -> @right.value } "^(.+/)?([^/]+)\.js$" { let module_name = $2 attr (detour_push) push_symbol = module_name - attr (detour_pop) symbol_definition = module_name, source_node = @assignment_expr, definiens_node = @assignment_expr - edge pop_default_guard -> detour_push - edge detour_push -> detour_pop - edge detour_pop -> @right.value + attr (@assignment_expr.detour_pop) symbol_definition = module_name, source_node = @assignment_expr, definiens_node = @assignment_expr + edge @assignment_expr.pop_default_guard -> detour_push + edge detour_push -> @assignment_expr.detour_pop + edge @assignment_expr.detour_pop -> @right.value } } node default_detour_push - node default_detour_pop + node @assignment_expr.default_detour_pop attr (default_detour_push) push_symbol = "default" - attr (default_detour_pop) symbol_definition = "default", source_node = @assignment_expr, definiens_node = @assignment_expr - edge pop_default_guard -> default_detour_push - edge default_detour_push -> default_detour_pop - edge default_detour_pop -> @right.value + attr (@assignment_expr.default_detour_pop) symbol_definition = "default", source_node = @assignment_expr, definiens_node = @assignment_expr + edge @assignment_expr.pop_default_guard -> default_detour_push + edge default_detour_push -> @assignment_expr.default_detour_pop + edge @assignment_expr.default_detour_pop -> @right.value } @@ -4846,6 +4894,72 @@ inherit .containing_class_value } +( + (assignment_expression + left: [ + ( ; exports.foo = ... + (member_expression + object:(_)@_exports + property:(_)@_property) + (#eq? @_exports "exports") + ) + ( ; module.exports.foo = ... + (member_expression + object:(member_expression + object:(_)@_module + property:(_)@_exports) + property:(_)@_property) + (#eq? @_module "module") + (#eq? @_exports "exports") + ) + ] + right: [ + (function) + (generator_function) + (arrow_function) + ])@assignment_expr + +) { + + attr (@assignment_expr.pop_name) syntax_type = "function" + attr (@assignment_expr.detour_pop) syntax_type = "function" + attr (@assignment_expr.default_detour_pop) syntax_type = "function" + +} + +( + (assignment_expression + left: (member_expression + object:(_)@_module + property:(_)@_exports) + right: [ + (function) + (generator_function) + (arrow_function) + ])@assignment_expr + (#eq? @_module "module") + (#eq? @_exports "exports") +) { + + attr (@assignment_expr.pop_default_guard) syntax_type = "function" + attr (@assignment_expr.detour_pop) syntax_type = "function" + attr (@assignment_expr.default_detour_pop) syntax_type = "function" + +} + +(export_statement "default" + value:[ + (function) + (generator_function) + (arrow_function) + ])@export_stmt { + + attr (@export_stmt.pop_guard_default) syntax_type = "function" + attr (@export_stmt.detour_pop) syntax_type = "function" + attr (@export_stmt.default_detour_pop) syntax_type = "function" + +} + (pair key: (_)@name value: [ @@ -4880,6 +4994,60 @@ inherit .containing_class_value } +( + (assignment_expression + left: [ + ( ; exports.foo = ... + (member_expression + object:(_)@_exports + property:(_)@_property) + (#eq? @_exports "exports") + ) + ( ; module.exports.foo = ... + (member_expression + object:(member_expression + object:(_)@_module + property:(_)@_exports) + property:(_)@_property) + (#eq? @_module "module") + (#eq? @_exports "exports") + ) + ] + right: (class))@assignment_expr + +) { + + attr (@assignment_expr.pop_name) syntax_type = "class" + attr (@assignment_expr.detour_pop) syntax_type = "class" + attr (@assignment_expr.default_detour_pop) syntax_type = "class" + +} + +( + (assignment_expression + left: (member_expression + object:(_)@_module + property:(_)@_exports) + right: (class))@assignment_expr + (#eq? @_module "module") + (#eq? @_exports "exports") +) { + + attr (@assignment_expr.pop_default_guard) syntax_type = "class" + attr (@assignment_expr.detour_pop) syntax_type = "class" + attr (@assignment_expr.default_detour_pop) syntax_type = "class" + +} + +(export_statement "default" + value:(class))@export_stmt { + + attr (@export_stmt.pop_guard_default) syntax_type = "class" + attr (@export_stmt.detour_pop) syntax_type = "class" + attr (@export_stmt.default_detour_pop) syntax_type = "class" + +} + (pair key: (_)@name value: (class)) { From 5b7b4d657ace1f20592a115b78ae415db32f41dd Mon Sep 17 00:00:00 2001 From: Rebecca Valentine Date: Thu, 1 Feb 2024 15:55:29 -0800 Subject: [PATCH 191/291] Fixes minor export statement definiens bugs --- .../tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg b/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg index e67bfe332..907a1cb47 100644 --- a/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg +++ b/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg @@ -550,7 +550,7 @@ inherit .containing_class_value node default_detour_pop attr (default_detour_push) push_symbol = "default" - attr (default_detour_pop) symbol_definition = "default", source_node = @decl, definiens_node = @decl + attr (default_detour_pop) symbol_definition = "default", source_node = @decl, definiens_node = @export_stmt edge pop_guard_default -> default_detour_push edge default_detour_push -> default_detour_pop ; edge default_detour_pop -> @decl.value ;; FIXME declarations have no .value @@ -598,7 +598,7 @@ inherit .containing_class_value node @export_stmt.default_detour_pop attr (default_detour_push) push_symbol = "default" - attr (@export_stmt.default_detour_pop) symbol_definition = "default", source_node = @value, definiens_node = @value + attr (@export_stmt.default_detour_pop) symbol_definition = "default", source_node = @value, definiens_node = @export_stmt edge @export_stmt.pop_guard_default -> default_detour_push edge default_detour_push -> @export_stmt.default_detour_pop edge @export_stmt.default_detour_pop -> @value.value From 6bdf56879ed999e4e9a847dfb671c9c91ba0a944 Mon Sep 17 00:00:00 2001 From: Rebecca Valentine Date: Thu, 1 Feb 2024 15:55:48 -0800 Subject: [PATCH 192/291] Fixes unneeded scoped variable --- .../tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg b/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg index 907a1cb47..28497a917 100644 --- a/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg +++ b/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg @@ -513,7 +513,7 @@ inherit .containing_class_value (declaration)@decl)@export_stmt { node pop_guard_default - attr (pop_guard_default) symbol_definition = "GUARD:DEFAULT", source_node = @decl, definiens_node = @export_stmt.export_statement + attr (pop_guard_default) symbol_definition = "GUARD:DEFAULT", source_node = @decl, definiens_node = @export_stmt edge @export_stmt.exports -> pop_guard_default ;; edge export_stmt_pop_guard_default -> @decl.value ;; FIXME declarations have no .value @@ -561,7 +561,7 @@ inherit .containing_class_value value:(_)@value)@export_stmt { node @export_stmt.pop_guard_default - attr (@export_stmt.pop_guard_default) symbol_definition = "GUARD:DEFAULT", source_node = @value, definiens_node = @export_stmt.export_statement + attr (@export_stmt.pop_guard_default) symbol_definition = "GUARD:DEFAULT", source_node = @value, definiens_node = @export_stmt edge @export_stmt.exports -> @export_stmt.pop_guard_default edge @export_stmt.pop_guard_default -> @value.value From 840d9f8fa58a70d915f469097d3687ac6c3fedb5 Mon Sep 17 00:00:00 2001 From: Rebecca Valentine Date: Thu, 1 Feb 2024 16:22:58 -0800 Subject: [PATCH 193/291] Removes redundant default nodes --- .../tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg | 5 ----- 1 file changed, 5 deletions(-) diff --git a/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg b/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg index 28497a917..be7041697 100644 --- a/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg +++ b/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg @@ -790,13 +790,8 @@ inherit .containing_class_value (export_statement value:(_)@default_expr)@export_stmt { - node pop_default_guard - - attr (pop_default_guard) symbol_definition = "GUARD:DEFAULT", source_node = @export_stmt, definiens_node = @export_stmt.export_statement edge @default_expr.before_scope -> @export_stmt.before_scope edge @export_stmt.after_scope -> @default_expr.after_scope - edge @export_stmt.exports -> pop_default_guard - edge pop_default_guard -> @default_expr.value } From 187f972a06aa56d0104fed6a7079afa02c532fb2 Mon Sep 17 00:00:00 2001 From: Rob Rix Date: Fri, 2 Feb 2024 10:23:32 -0500 Subject: [PATCH 194/291] Test an identifier decorator. --- .../tree-sitter-stack-graphs-javascript/test/base_syntax.js | 1 + 1 file changed, 1 insertion(+) diff --git a/languages/tree-sitter-stack-graphs-javascript/test/base_syntax.js b/languages/tree-sitter-stack-graphs-javascript/test/base_syntax.js index fde1656aa..4e61d5389 100644 --- a/languages/tree-sitter-stack-graphs-javascript/test/base_syntax.js +++ b/languages/tree-sitter-stack-graphs-javascript/test/base_syntax.js @@ -12,6 +12,7 @@ function foo(a) { } function foo(undefined) { } function* foo() { } class Foo { } +@Foo class Bar { } { } if (true) { } if (true) { } else { } From 8e9114e78e0ae61b110dfe04bdd0fda6ca461ae6 Mon Sep 17 00:00:00 2001 From: Rob Rix Date: Fri, 2 Feb 2024 10:24:19 -0500 Subject: [PATCH 195/291] Test a call expression decorator. --- .../tree-sitter-stack-graphs-javascript/test/base_syntax.js | 1 + 1 file changed, 1 insertion(+) diff --git a/languages/tree-sitter-stack-graphs-javascript/test/base_syntax.js b/languages/tree-sitter-stack-graphs-javascript/test/base_syntax.js index 4e61d5389..8eb29e333 100644 --- a/languages/tree-sitter-stack-graphs-javascript/test/base_syntax.js +++ b/languages/tree-sitter-stack-graphs-javascript/test/base_syntax.js @@ -13,6 +13,7 @@ function foo(undefined) { } function* foo() { } class Foo { } @Foo class Bar { } +@Foo() class Bar { } { } if (true) { } if (true) { } else { } From 55ba0852f12fd979917387ab8bce918c5a6f9fd3 Mon Sep 17 00:00:00 2001 From: Rob Rix Date: Fri, 2 Feb 2024 10:43:31 -0500 Subject: [PATCH 196/291] Test member expression decorators. --- .../tree-sitter-stack-graphs-javascript/test/base_syntax.js | 1 + 1 file changed, 1 insertion(+) diff --git a/languages/tree-sitter-stack-graphs-javascript/test/base_syntax.js b/languages/tree-sitter-stack-graphs-javascript/test/base_syntax.js index 8eb29e333..e3c659745 100644 --- a/languages/tree-sitter-stack-graphs-javascript/test/base_syntax.js +++ b/languages/tree-sitter-stack-graphs-javascript/test/base_syntax.js @@ -13,6 +13,7 @@ function foo(undefined) { } function* foo() { } class Foo { } @Foo class Bar { } +@Foo.Quux class Bar { } @Foo() class Bar { } { } if (true) { } From 1350957293760199b94f72e21e175d80cfcbfabc Mon Sep 17 00:00:00 2001 From: Rob Rix Date: Fri, 2 Feb 2024 10:44:49 -0500 Subject: [PATCH 197/291] Test member expression in call expression decorators. --- .../tree-sitter-stack-graphs-javascript/test/base_syntax.js | 1 + 1 file changed, 1 insertion(+) diff --git a/languages/tree-sitter-stack-graphs-javascript/test/base_syntax.js b/languages/tree-sitter-stack-graphs-javascript/test/base_syntax.js index e3c659745..cc4076cf5 100644 --- a/languages/tree-sitter-stack-graphs-javascript/test/base_syntax.js +++ b/languages/tree-sitter-stack-graphs-javascript/test/base_syntax.js @@ -15,6 +15,7 @@ class Foo { } @Foo class Bar { } @Foo.Quux class Bar { } @Foo() class Bar { } +@Foo.Quux() class Bar { } { } if (true) { } if (true) { } else { } From 0b386a1fa84373dca1cb29643c5e66a08651d09b Mon Sep 17 00:00:00 2001 From: Rob Rix Date: Fri, 2 Feb 2024 13:13:07 -0500 Subject: [PATCH 198/291] Handle identifiers more or less uniformly. --- .../src/stack-graphs.tsg | 32 ++++++------------- 1 file changed, 9 insertions(+), 23 deletions(-) diff --git a/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg b/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg index d8b383c35..4b2aef557 100644 --- a/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg +++ b/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg @@ -411,6 +411,15 @@ inherit .containing_class_value edge @comment.after_scope -> @comment.before_scope } +(identifier) @identifier { + node @identifier.before_scope + node @identifier.after_scope + node @identifier.value + node @identifier.covalue + node @identifier.new_bindings + edge @identifier.after_scope -> @identifier.before_scope +} + @@ -2226,7 +2235,6 @@ inherit .containing_class_value (member_expression) (parenthesized_expression) (undefined) - (primary_expression/identifier) (this) (super) (number) @@ -2355,9 +2363,6 @@ inherit .containing_class_value ;; #### Variables (primary_expression/identifier)@variable { - ; scopes don't change - edge @variable.after_scope -> @variable.before_scope - ; value is a lookup, ie a push attr (@variable.value) node_reference = @variable edge @variable.value -> @variable.before_scope @@ -2864,16 +2869,9 @@ inherit .containing_class_value (arrow_function parameter:(_)@param)@fun { - - node @param.after_scope node param_arg_index - node @param.before_scope - node @param.covalue node param_pop - ; scope flows from the param right back out - edge @param.after_scope -> @param.before_scope - ; but augmented with a pop, b/c it's not a pattern attr (param_pop) node_definition = @param edge param_pop -> @param.covalue @@ -3742,12 +3740,6 @@ inherit .containing_class_value name:(identifier)@element_name) ] { - - node @element_name.before_scope - node @element_name.after_scope - - edge @element_name.after_scope -> @element_name.before_scope - scan (source-text @element_name) { ; standard HTML elements "^(a|abbr|acronym|address|applet|area|article|aside|audio|b|base|basefont|bdi|bdo|big|blockquote|body|br|button|canvas|caption|center|cite|code|col|colgroup|data|datalist|dd|del|details|dfn|dialog|dir|div|dl|dt|em|embed|fieldset|figcaption|figure|font|footer|form|frame|frameset|h1|h2|h3|h4|h5|h6|head|header|hgroup|hr|html|i|iframe|input|ins|kbd|label|legend|li|link|main|map|mark|menu|meta|meter|nav|noframes|noscript|object|ol|optgroup|option|output|p|param|picture|pre|progress|q|rp|rt|ruby|s|samp|script|search|section|select|small|source|span|strike|strong|style|sub|summary|sup|svg|table|tbody|td|template|textarea|tfoot|th|thead|time|title|tr|track|tt|u|ul|var|video|wbr)$" { @@ -3776,8 +3768,6 @@ inherit .containing_class_value (identifier)@first_part (identifier)@second_part)@nested_identifier { - node @first_part.value - node @second_part.value node guard_member attr (@first_part.value) node_reference = @first_part @@ -3794,7 +3784,6 @@ inherit .containing_class_value (nested_identifier)@first_part (identifier)@second_part)@nested_identifier { - node @second_part.value node guard_member attr (@second_part.value) node_reference = @second_part @@ -4086,13 +4075,11 @@ inherit .containing_class_value ;; ### Attributes Defined on Patterns ;; TODO [ - (assignment_expression left:(identifier)@pattern) (assignment_pattern)@pattern (object_pattern)@pattern (array_pattern)@pattern (rest_pattern)@pattern (pair_pattern)@pattern - (pattern/identifier)@pattern (pattern/property_identifier)@pattern (object_assignment_pattern)@pattern (shorthand_property_identifier_pattern)@pattern @@ -4117,7 +4104,6 @@ inherit .containing_class_value ; scope flows through, binding via a pop edge that goes to an unknown value attr (ident_pat_pop) node_definition = @ident_pat edge ident_pat_pop -> @ident_pat.covalue - edge @ident_pat.after_scope -> @ident_pat.before_scope edge @ident_pat.after_scope -> ident_pat_pop edge @ident_pat.new_bindings -> ident_pat_pop From 76e4b05d9a11e6ccd4a09f240a7e37700a2671fe Mon Sep 17 00:00:00 2001 From: Rob Rix Date: Tue, 6 Feb 2024 12:08:23 -0500 Subject: [PATCH 199/291] Test fields. --- .../tree-sitter-stack-graphs-javascript/test/base_syntax.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/languages/tree-sitter-stack-graphs-javascript/test/base_syntax.js b/languages/tree-sitter-stack-graphs-javascript/test/base_syntax.js index cc4076cf5..686dfb340 100644 --- a/languages/tree-sitter-stack-graphs-javascript/test/base_syntax.js +++ b/languages/tree-sitter-stack-graphs-javascript/test/base_syntax.js @@ -11,7 +11,9 @@ function foo() { } function foo(a) { } function foo(undefined) { } function* foo() { } -class Foo { } +class Foo { + #x = null; +} @Foo class Bar { } @Foo.Quux class Bar { } @Foo() class Bar { } From 5563c01a9c8310ce1af729661800614fbd2effe2 Mon Sep 17 00:00:00 2001 From: Rob Rix Date: Tue, 6 Feb 2024 12:13:57 -0500 Subject: [PATCH 200/291] Always give field defs scopes. --- .../src/stack-graphs.tsg | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg b/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg index 6680f4a90..84b11546f 100644 --- a/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg +++ b/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg @@ -1661,11 +1661,14 @@ inherit .containing_class_value +(field_definition)@field_def { + node @field_def.after_scope + node @field_def.before_scope +} + (field_definition property:(property_identifier)@property)@field_def { - node @field_def.after_scope - node @field_def.before_scope node @property.pop node property_pop_dot From 82679f8acfd9b63b5dd60a134c89d39a6711a5de Mon Sep 17 00:00:00 2001 From: Rob Rix Date: Tue, 6 Feb 2024 12:15:48 -0500 Subject: [PATCH 201/291] Generalize to accept private properties. --- .../tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg b/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg index 84b11546f..956cd0406 100644 --- a/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg +++ b/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg @@ -1667,7 +1667,7 @@ inherit .containing_class_value } (field_definition - property:(property_identifier)@property)@field_def { + property:(_)@property)@field_def { node @property.pop node property_pop_dot From b01ce15ea301937a8a501e8404ff34c9d1c33405 Mon Sep 17 00:00:00 2001 From: Rob Rix Date: Thu, 8 Feb 2024 12:53:13 -0500 Subject: [PATCH 202/291] Test comments in export lists. --- .../tree-sitter-stack-graphs-javascript/test/base_syntax.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/languages/tree-sitter-stack-graphs-javascript/test/base_syntax.js b/languages/tree-sitter-stack-graphs-javascript/test/base_syntax.js index 686dfb340..44e7727c4 100644 --- a/languages/tree-sitter-stack-graphs-javascript/test/base_syntax.js +++ b/languages/tree-sitter-stack-graphs-javascript/test/base_syntax.js @@ -3,6 +3,10 @@ // foo export let x = 1; +export { + // x + A +}; import "foo"; debugger; var x; From 0e6ef5be587b649ed64d1d8e7172f821db527b61 Mon Sep 17 00:00:00 2001 From: Rob Rix Date: Thu, 8 Feb 2024 12:56:35 -0500 Subject: [PATCH 203/291] Support comments in export clauses. --- .../tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg | 1 + 1 file changed, 1 insertion(+) diff --git a/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg b/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg index 956cd0406..677436013 100644 --- a/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg +++ b/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg @@ -409,6 +409,7 @@ inherit .containing_class_value node @comment.covalue node @comment.new_bindings ; for object patterns edge @comment.after_scope -> @comment.before_scope + node @comment.source ; for export clauses with multiple exports } (identifier) @identifier { From b4f16e10a6fef97cc7125b5924cc67e3511f53af Mon Sep 17 00:00:00 2001 From: Rob Rix Date: Fri, 9 Feb 2024 09:47:32 -0500 Subject: [PATCH 204/291] Test class static blocks. --- .../tree-sitter-stack-graphs-javascript/test/base_syntax.js | 1 + 1 file changed, 1 insertion(+) diff --git a/languages/tree-sitter-stack-graphs-javascript/test/base_syntax.js b/languages/tree-sitter-stack-graphs-javascript/test/base_syntax.js index 686dfb340..ab0cf1c05 100644 --- a/languages/tree-sitter-stack-graphs-javascript/test/base_syntax.js +++ b/languages/tree-sitter-stack-graphs-javascript/test/base_syntax.js @@ -13,6 +13,7 @@ function foo(undefined) { } function* foo() { } class Foo { #x = null; + static {} } @Foo class Bar { } @Foo.Quux class Bar { } From e69c8cbe27156edefbc26e2b9417e53c54fa403c Mon Sep 17 00:00:00 2001 From: Rob Rix Date: Fri, 9 Feb 2024 09:51:46 -0500 Subject: [PATCH 205/291] Class static blocks have before/after scopes. --- .../tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg b/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg index 956cd0406..c5e72fd27 100644 --- a/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg +++ b/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg @@ -1693,6 +1693,11 @@ inherit .containing_class_value edge @property.pop -> @value.value } +(class_static_block) @static_block { + node @static_block.before_scope + node @static_block.after_scope +} + ;; #### Statement Block From 4ef094357d0ef1a2058785bc1865230063bb7563 Mon Sep 17 00:00:00 2001 From: Rob Rix Date: Fri, 9 Feb 2024 09:54:15 -0500 Subject: [PATCH 206/291] Propagate scopes internally. --- .../tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg b/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg index c5e72fd27..6d32ef9d8 100644 --- a/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg +++ b/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg @@ -1693,9 +1693,11 @@ inherit .containing_class_value edge @property.pop -> @value.value } -(class_static_block) @static_block { +(class_static_block body: (_) @body) @static_block { node @static_block.before_scope node @static_block.after_scope + + edge @body.before_scope -> @static_block.before_scope } From 7c36737c08c9e66d407da2ec03b3904aa6a2665d Mon Sep 17 00:00:00 2001 From: Rob Rix Date: Fri, 9 Feb 2024 09:54:39 -0500 Subject: [PATCH 207/291] Propagate scopes laterally. --- .../tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg | 2 ++ 1 file changed, 2 insertions(+) diff --git a/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg b/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg index 6d32ef9d8..1f0ef3d9a 100644 --- a/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg +++ b/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg @@ -1698,6 +1698,8 @@ inherit .containing_class_value node @static_block.after_scope edge @body.before_scope -> @static_block.before_scope + + edge @static_block.after_scope -> @static_block.before_scope } From e05c9db6a7a4813cb035ca61f93ed90545855583 Mon Sep 17 00:00:00 2001 From: Rob Rix Date: Fri, 9 Feb 2024 10:10:26 -0500 Subject: [PATCH 208/291] Test computed property names. --- .../tree-sitter-stack-graphs-javascript/test/base_syntax.js | 1 + 1 file changed, 1 insertion(+) diff --git a/languages/tree-sitter-stack-graphs-javascript/test/base_syntax.js b/languages/tree-sitter-stack-graphs-javascript/test/base_syntax.js index 686dfb340..75c74691f 100644 --- a/languages/tree-sitter-stack-graphs-javascript/test/base_syntax.js +++ b/languages/tree-sitter-stack-graphs-javascript/test/base_syntax.js @@ -13,6 +13,7 @@ function foo(undefined) { } function* foo() { } class Foo { #x = null; + get [/**/ foo]() {} } @Foo class Bar { } @Foo.Quux class Bar { } From c4255f04fad9fc0414b70aea88f53922d0d7fef5 Mon Sep 17 00:00:00 2001 From: Rob Rix Date: Fri, 9 Feb 2024 10:11:59 -0500 Subject: [PATCH 209/291] Construct the scopes exactly once. --- .../src/stack-graphs.tsg | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg b/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg index 956cd0406..f5729a37a 100644 --- a/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg +++ b/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg @@ -2582,10 +2582,13 @@ inherit .containing_class_value ; pairs -(computed_property_name (_)@expr)@computed_property_name { - +(computed_property_name)@computed_property_name { node @computed_property_name.after_scope node @computed_property_name.before_scope +} + +(computed_property_name (_)@expr)@computed_property_name { + edge @expr.before_scope -> @computed_property_name.before_scope edge @computed_property_name.after_scope -> @expr.after_scope From bd403b61969f57f0d49fe75c6e0c4b0c892a24e5 Mon Sep 17 00:00:00 2001 From: Rob Rix Date: Fri, 9 Feb 2024 13:05:30 -0500 Subject: [PATCH 210/291] Test namespaced JSX names. --- .../tree-sitter-stack-graphs-javascript/test/base_syntax.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/languages/tree-sitter-stack-graphs-javascript/test/base_syntax.js b/languages/tree-sitter-stack-graphs-javascript/test/base_syntax.js index 686dfb340..74556c075 100644 --- a/languages/tree-sitter-stack-graphs-javascript/test/base_syntax.js +++ b/languages/tree-sitter-stack-graphs-javascript/test/base_syntax.js @@ -93,4 +93,6 @@ class { }; {garply} { } {/**/x} - \ No newline at end of file +; +; +; \ No newline at end of file From 4fc8fe354f84ebe7165d505a6fcd70f7662ffc7d Mon Sep 17 00:00:00 2001 From: Rob Rix Date: Fri, 9 Feb 2024 13:09:43 -0500 Subject: [PATCH 211/291] Namespace names have scopes. --- .../tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg b/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg index 956cd0406..9043ee469 100644 --- a/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg +++ b/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg @@ -3698,6 +3698,11 @@ inherit .containing_class_value } +(jsx_namespace_name (_) @lhs (_) @rhs)@name { + node @name.before_scope + node @name.after_scope +} + (jsx_self_closing_element name:(_)@element_name)@jsx_self_closing_element { From f631081cd53d2cf3b2d498a5c8064bbd505f5dbc Mon Sep 17 00:00:00 2001 From: Rob Rix Date: Fri, 9 Feb 2024 13:10:23 -0500 Subject: [PATCH 212/291] The namespace follows the scopes. --- .../tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg | 2 ++ 1 file changed, 2 insertions(+) diff --git a/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg b/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg index 9043ee469..8db276908 100644 --- a/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg +++ b/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg @@ -3701,6 +3701,8 @@ inherit .containing_class_value (jsx_namespace_name (_) @lhs (_) @rhs)@name { node @name.before_scope node @name.after_scope + + edge @lhs.before_scope -> @name.before_scope } (jsx_self_closing_element From 297d6078f9ad5175f7576e49dbcd7e7d619c1d3a Mon Sep 17 00:00:00 2001 From: Rob Rix Date: Fri, 9 Feb 2024 13:11:43 -0500 Subject: [PATCH 213/291] Namespaced names' namespaced names are namespaced. I'm unsure whether this is the right thing to do, and I'm unsure whether it matters. --- .../tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg | 1 + 1 file changed, 1 insertion(+) diff --git a/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg b/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg index 8db276908..8a01f4a3d 100644 --- a/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg +++ b/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg @@ -3703,6 +3703,7 @@ inherit .containing_class_value node @name.after_scope edge @lhs.before_scope -> @name.before_scope + edge @rhs.before_scope -> @lhs.after_scope } (jsx_self_closing_element From 397a9c9d1cf7c2d375af5f46921c71366784f5b1 Mon Sep 17 00:00:00 2001 From: Rob Rix Date: Fri, 9 Feb 2024 13:12:03 -0500 Subject: [PATCH 214/291] Flow. --- .../tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg | 1 + 1 file changed, 1 insertion(+) diff --git a/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg b/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg index 8a01f4a3d..75b93e9d4 100644 --- a/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg +++ b/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg @@ -3704,6 +3704,7 @@ inherit .containing_class_value edge @lhs.before_scope -> @name.before_scope edge @rhs.before_scope -> @lhs.after_scope + edge @name.after_scope -> @name.before_scope } (jsx_self_closing_element From 1afad1c1fcc47c6af1c659f00b28931ef9f9cb8b Mon Sep 17 00:00:00 2001 From: Rob Rix Date: Fri, 9 Feb 2024 15:24:05 -0500 Subject: [PATCH 215/291] Test with a method (property?) named constructor. --- .../tree-sitter-stack-graphs-javascript/test/base_syntax.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/languages/tree-sitter-stack-graphs-javascript/test/base_syntax.js b/languages/tree-sitter-stack-graphs-javascript/test/base_syntax.js index cd29e4266..4cf3170d0 100644 --- a/languages/tree-sitter-stack-graphs-javascript/test/base_syntax.js +++ b/languages/tree-sitter-stack-graphs-javascript/test/base_syntax.js @@ -11,6 +11,9 @@ import "foo"; debugger; var x; let x; +let x = { + get constructor() {} +}; function foo() { } function foo(a) { } function foo(undefined) { } From 61648e1cf51a12625657a404020bb28620aed46d Mon Sep 17 00:00:00 2001 From: Rob Rix Date: Fri, 9 Feb 2024 15:28:14 -0500 Subject: [PATCH 216/291] Add the requisite things for objects/classes. --- .../tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg | 3 +++ 1 file changed, 3 insertions(+) diff --git a/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg b/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg index cc33b7e23..e69a4636a 100644 --- a/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg +++ b/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg @@ -2525,6 +2525,9 @@ inherit .containing_class_value attr (@object.member_pop) pop_symbol = "GUARD:MEMBER" edge @object.value -> @object.member_pop + node @object.class_value + node @object.constructor + } ; empty objects From 3f3b9bca955567497b031aebfd9d981408ad887e Mon Sep 17 00:00:00 2001 From: Rebecca Valentine Date: Tue, 13 Feb 2024 13:46:12 -0800 Subject: [PATCH 217/291] Adds pairs --- languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg | 1 + 1 file changed, 1 insertion(+) diff --git a/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg b/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg index 7cddd6783..48353f14a 100644 --- a/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg +++ b/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg @@ -152,6 +152,7 @@ attribute node_symbol = node => symbol = (source-text node), source_n (set) (set_comprehension) (tuple) + (pair) (parenthesized_expression) (generator_expression) (ellipsis) From 4b613bf38963f8a0cce38e070d2e48d35a6c2101 Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Tue, 5 Mar 2024 14:17:16 +0100 Subject: [PATCH 218/291] Update dependency versions to fix install problems. --- .github/workflows/ci.yml | 37 ++++++++++--------- .../tree-sitter-stack-graphs-java/Cargo.toml | 14 +++++-- lsp-positions/Cargo.toml | 10 +++-- stack-graphs/Cargo.toml | 7 ++-- tree-sitter-stack-graphs/Cargo.toml | 9 +++-- 5 files changed, 45 insertions(+), 32 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index cad93d701..0d6ec91bb 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -17,12 +17,17 @@ jobs: strategy: matrix: rust: [stable] + env: + # cargo hack does not use the default-members in Cargo.toml, so we restrict to those explicitly + CARGO_HACK: cargo hack -p lsp-positions -p stack-graphs -p tree-sitter-stack-graphs --feature-powerset --exclude-features copious-debugging steps: - name: Install Rust environment uses: hecrj/setup-rust-action@v1 with: rust-version: ${{ matrix.rust }} + - name: Install cargo-hack + run: cargo install cargo-hack - name: Checkout code uses: actions/checkout@v3 - name: Check formatting @@ -36,28 +41,24 @@ jobs: key: ${{ runner.OS }}-cargo-${{ hashFiles('**/Cargo.lock') }} restore-keys: | ${{ runner.OS }}-cargo- + - name: Build library (all feature combinations) + run: ${{ env.CARGO_HACK }} --no-dev-deps build + - name: Run test suite (all feature combinations) + run: ${{ env.CARGO_HACK }} test + - name: Run test suite with all optimizations (default features) + run: cargo test --release - name: Install cargo-valgrind run: | sudo apt-get update sudo apt-get install -y valgrind cargo install cargo-valgrind - - name: Build library - run: cargo build + - name: Run test suite under valgrind (default features) + # We only need to use valgrind to test the crates that have C bindings. + run: cargo valgrind test -p stack-graphs - name: Ensure C headers are up to date run: | script/cbindgen test -z "$(git status --porcelain)" - - name: Run test suite - run: cargo test - - name: Run test suite under valgrind - # We only need to use valgrind to test the crates that have C bindings. - run: cargo valgrind test -p stack-graphs - - name: Run lsp-positions tests without tree-sitter - run: cargo test -p lsp-positions --no-default-features - - name: Run test suite with all features enabled - run: cargo test --all-features - - name: Run test suite with all optimizations - run: cargo test --release test-init: needs: [test-rust] @@ -112,6 +113,8 @@ jobs: uses: hecrj/setup-rust-action@v1 with: rust-version: ${{ matrix.rust }} + - name: Install cargo-hack + run: cargo install cargo-hack - name: Cache dependencies uses: actions/cache@v3 with: @@ -123,10 +126,10 @@ jobs: ${{ runner.OS }}-cargo- - name: Checkout code uses: actions/checkout@v3 - - name: Build - run: cargo build -p ${{ matrix.language }} - - name: Test - run: cargo test -p ${{ matrix.language }} + - name: Build (all feature combinations) + run: cargo hack -p ${{ matrix.language }} --feature-powerset build + - name: Test (all features) + run: cargo test -p ${{ matrix.language }} --all-features test-cli: runs-on: ubuntu-latest diff --git a/languages/tree-sitter-stack-graphs-java/Cargo.toml b/languages/tree-sitter-stack-graphs-java/Cargo.toml index 8bd3e5f93..21b5b25fa 100644 --- a/languages/tree-sitter-stack-graphs-java/Cargo.toml +++ b/languages/tree-sitter-stack-graphs-java/Cargo.toml @@ -23,6 +23,7 @@ keywords = ["tree-sitter", "stack-graphs", "java"] [[bin]] name = "tree-sitter-stack-graphs-java" path = "rust/bin.rs" +required-features = ["cli"] [lib] path = "rust/lib.rs" @@ -33,8 +34,15 @@ name = "test" path = "rust/test.rs" harness = false # need to provide own main function to handle running tests +[features] +cli = ["anyhow", "clap", "tree-sitter-stack-graphs/cli"] + [dependencies] -anyhow = "1.0" -clap = { version = "4", features = ["derive"] } -tree-sitter-stack-graphs = { version = "0.7", path = "../../tree-sitter-stack-graphs", features=["cli"] } +anyhow = { version = "1.0", optional = true } +clap = { version = "4", features = ["derive"], optional = true } tree-sitter-java = { version = "=0.20.0" } +tree-sitter-stack-graphs = { version = "0.7", path = "../../tree-sitter-stack-graphs" } + +[dev-dependencies] +anyhow = { version = "1.0" } +tree-sitter-stack-graphs = { version = "0.7", path = "../../tree-sitter-stack-graphs", features = ["cli"] } diff --git a/lsp-positions/Cargo.toml b/lsp-positions/Cargo.toml index 242a894ff..b7422a9d7 100644 --- a/lsp-positions/Cargo.toml +++ b/lsp-positions/Cargo.toml @@ -22,7 +22,9 @@ tree-sitter = ["dep:tree-sitter"] [dependencies] memchr = "2.4" -tree-sitter = { version=">= 0.19", optional=true } -unicode-segmentation = { version="1.8" } -serde = { version="1", optional=true, features=["derive"] } -bincode = { version="2.0.0-rc.3", optional=true } +tree-sitter = { version = "0.20", optional = true } # keep the same minor version as the tree-sitter + # dependency of tree-sitter-stack-graphs to prevent + # install problems +unicode-segmentation = { version = "1.8" } +serde = { version = "1", features = ["derive"], optional = true } +bincode = { version = "2.0.0-rc.3", optional = true } diff --git a/stack-graphs/Cargo.toml b/stack-graphs/Cargo.toml index 2e55e1768..0633d40c4 100644 --- a/stack-graphs/Cargo.toml +++ b/stack-graphs/Cargo.toml @@ -25,12 +25,12 @@ test = false [dependencies] bincode = { version = "2.0.0-rc.3", optional = true } -bitvec = "1.0" -controlled-option = "0.4" +bitvec = "1.0.1" +controlled-option = "0.4.1" either = "1.6" enumset = "1.1" fxhash = "0.2" -itertools = "0.10" +itertools = "0.10.2" libc = "0.2" lsp-positions = { version = "0.3", path = "../lsp-positions" } rusqlite = { version = "0.28", optional = true, features = ["bundled", "functions"] } @@ -42,7 +42,6 @@ thiserror = { version = "1.0" } [dev-dependencies] assert-json-diff = "2" -itertools = "0.10" maplit = "1.0" pretty_assertions = "0.7" serde_json = { version = "1.0" } diff --git a/tree-sitter-stack-graphs/Cargo.toml b/tree-sitter-stack-graphs/Cargo.toml index d5429304a..ddce19309 100644 --- a/tree-sitter-stack-graphs/Cargo.toml +++ b/tree-sitter-stack-graphs/Cargo.toml @@ -49,12 +49,12 @@ lsp = [ ] [dependencies] -anyhow = "1.0" +anyhow = "1.0.4" base64 = { version = "0.21", optional = true } capture-it = { version = "0.3", optional = true } clap = { version = "4", optional = true, features = ["derive"] } colored = { version = "2.0", optional = true } -controlled-option = ">=0.4" +controlled-option = "0.4.1" crossbeam-channel = { version = "0.5", optional = true } dialoguer = { version = "0.10", optional = true } dirs = { version = "5", optional = true } @@ -69,12 +69,13 @@ regex = "1" rust-ini = "0.18" serde_json = { version="1.0", optional=true } sha1 = { version="0.10", optional=true } -stack-graphs = { version=">=0.11, <=0.12", path="../stack-graphs" } +stack-graphs = { version="0.12", path="../stack-graphs" } thiserror = "1.0" time = { version = "0.3", optional = true } tokio = { version = "1.26", optional = true, features = ["io-std", "rt", "rt-multi-thread"] } tower-lsp = { version = "0.19", optional = true } -tree-sitter = ">= 0.19" +tree-sitter = "0.20" # keep the same minor version as the tree-sitter dependency + # of tree-sitter-graph to prevent install problems tree-sitter-config = { version = "0.19", optional = true } tree-sitter-graph = "0.11" tree-sitter-loader = "0.20" From 9a7a566f8f4ce13d3bfb3b3be60d37761a895f7d Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Tue, 5 Mar 2024 19:06:59 +0100 Subject: [PATCH 219/291] Bump lsp-positions to v0.3.3 --- lsp-positions/CHANGELOG.md | 4 ++++ lsp-positions/Cargo.toml | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/lsp-positions/CHANGELOG.md b/lsp-positions/CHANGELOG.md index 36a0d4ebd..a9e17c13f 100644 --- a/lsp-positions/CHANGELOG.md +++ b/lsp-positions/CHANGELOG.md @@ -5,6 +5,10 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## v0.3.3 -- 2024-03-05 + +The `tree-sitter` dependency version was updated to fix install problems. + ## v0.3.2 -- 2023-06-08 ### Added diff --git a/lsp-positions/Cargo.toml b/lsp-positions/Cargo.toml index b7422a9d7..7054def14 100644 --- a/lsp-positions/Cargo.toml +++ b/lsp-positions/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "lsp-positions" -version = "0.3.2" +version = "0.3.3" description = "LSP-compatible character positions" homepage = "https://github.com/github/stack-graphs/tree/main/lsp-positions" repository = "https://github.com/github/stack-graphs/" From a7f9d9cb0e682aa672d77a5ce2e57f148776ac78 Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Tue, 5 Mar 2024 19:21:23 +0100 Subject: [PATCH 220/291] Bump tree-sitter-stack-graphs to v0.8.0 --- languages/tree-sitter-stack-graphs-java/Cargo.toml | 4 ++-- languages/tree-sitter-stack-graphs-javascript/Cargo.toml | 4 ++-- languages/tree-sitter-stack-graphs-python/Cargo.toml | 4 ++-- languages/tree-sitter-stack-graphs-typescript/Cargo.toml | 4 ++-- tree-sitter-stack-graphs/CHANGELOG.md | 4 +++- tree-sitter-stack-graphs/Cargo.toml | 2 +- tree-sitter-stack-graphs/README.md | 2 +- 7 files changed, 13 insertions(+), 11 deletions(-) diff --git a/languages/tree-sitter-stack-graphs-java/Cargo.toml b/languages/tree-sitter-stack-graphs-java/Cargo.toml index 21b5b25fa..8af4b3277 100644 --- a/languages/tree-sitter-stack-graphs-java/Cargo.toml +++ b/languages/tree-sitter-stack-graphs-java/Cargo.toml @@ -41,8 +41,8 @@ cli = ["anyhow", "clap", "tree-sitter-stack-graphs/cli"] anyhow = { version = "1.0", optional = true } clap = { version = "4", features = ["derive"], optional = true } tree-sitter-java = { version = "=0.20.0" } -tree-sitter-stack-graphs = { version = "0.7", path = "../../tree-sitter-stack-graphs" } +tree-sitter-stack-graphs = { version = "0.8", path = "../../tree-sitter-stack-graphs" } [dev-dependencies] anyhow = { version = "1.0" } -tree-sitter-stack-graphs = { version = "0.7", path = "../../tree-sitter-stack-graphs", features = ["cli"] } +tree-sitter-stack-graphs = { version = "0.8", path = "../../tree-sitter-stack-graphs", features = ["cli"] } diff --git a/languages/tree-sitter-stack-graphs-javascript/Cargo.toml b/languages/tree-sitter-stack-graphs-javascript/Cargo.toml index 624edbab2..5c9522f77 100644 --- a/languages/tree-sitter-stack-graphs-javascript/Cargo.toml +++ b/languages/tree-sitter-stack-graphs-javascript/Cargo.toml @@ -31,9 +31,9 @@ clap = { version = "4", optional = true } serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" stack-graphs = { version = "0.12", path = "../../stack-graphs" } -tree-sitter-stack-graphs = { version = "0.7", path = "../../tree-sitter-stack-graphs" } +tree-sitter-stack-graphs = { version = "0.8", path = "../../tree-sitter-stack-graphs" } tree-sitter-javascript = { git = "https://github.com/tree-sitter/tree-sitter-javascript", rev = "5720b249490b3c17245ba772f6be4a43edb4e3b7" } [dev-dependencies] anyhow = "1.0" -tree-sitter-stack-graphs = { version = "0.7", path = "../../tree-sitter-stack-graphs", features = ["cli"] } +tree-sitter-stack-graphs = { version = "0.8", path = "../../tree-sitter-stack-graphs", features = ["cli"] } diff --git a/languages/tree-sitter-stack-graphs-python/Cargo.toml b/languages/tree-sitter-stack-graphs-python/Cargo.toml index 4ac4c0633..17cfa3d72 100644 --- a/languages/tree-sitter-stack-graphs-python/Cargo.toml +++ b/languages/tree-sitter-stack-graphs-python/Cargo.toml @@ -30,9 +30,9 @@ cli = ["anyhow", "clap", "tree-sitter-stack-graphs/cli"] [dependencies] anyhow = { version = "1.0", optional = true } clap = { version = "4", optional = true, features = ["derive"] } -tree-sitter-stack-graphs = { version = "0.7", path = "../../tree-sitter-stack-graphs" } +tree-sitter-stack-graphs = { version = "0.8", path = "../../tree-sitter-stack-graphs" } tree-sitter-python = "=0.20.4" [dev-dependencies] anyhow = "1.0" -tree-sitter-stack-graphs = { version = "0.7", path = "../../tree-sitter-stack-graphs", features = ["cli"] } +tree-sitter-stack-graphs = { version = "0.8", path = "../../tree-sitter-stack-graphs", features = ["cli"] } diff --git a/languages/tree-sitter-stack-graphs-typescript/Cargo.toml b/languages/tree-sitter-stack-graphs-typescript/Cargo.toml index da5df7ef9..fa273c2cd 100644 --- a/languages/tree-sitter-stack-graphs-typescript/Cargo.toml +++ b/languages/tree-sitter-stack-graphs-typescript/Cargo.toml @@ -33,10 +33,10 @@ glob = "0.3" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" stack-graphs = { version = ">=0.11, <=0.12", path = "../../stack-graphs" } -tree-sitter-stack-graphs = { version = "0.7", path = "../../tree-sitter-stack-graphs" } +tree-sitter-stack-graphs = { version = "0.8", path = "../../tree-sitter-stack-graphs" } tree-sitter-typescript = "=0.20.2" tsconfig = "0.1.0" [dev-dependencies] anyhow = { version = "1.0" } -tree-sitter-stack-graphs = { version = "0.7", path = "../../tree-sitter-stack-graphs", features = ["cli"] } +tree-sitter-stack-graphs = { version = "0.8", path = "../../tree-sitter-stack-graphs", features = ["cli"] } diff --git a/tree-sitter-stack-graphs/CHANGELOG.md b/tree-sitter-stack-graphs/CHANGELOG.md index 8d8ef7ca1..50f8b1f9b 100644 --- a/tree-sitter-stack-graphs/CHANGELOG.md +++ b/tree-sitter-stack-graphs/CHANGELOG.md @@ -5,7 +5,9 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## Unreleased +## v0.8.0 -- 2024-03-05 + +The `tree-sitter` dependency version was updated to fix install problems. ### Library diff --git a/tree-sitter-stack-graphs/Cargo.toml b/tree-sitter-stack-graphs/Cargo.toml index ddce19309..40b750641 100644 --- a/tree-sitter-stack-graphs/Cargo.toml +++ b/tree-sitter-stack-graphs/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "tree-sitter-stack-graphs" -version = "0.7.1" +version = "0.8.0" description = "Create stack graphs using tree-sitter parsers" homepage = "https://github.com/github/stack-graphs/tree/main/tree-sitter-stack-graphs" repository = "https://github.com/github/stack-graphs/" diff --git a/tree-sitter-stack-graphs/README.md b/tree-sitter-stack-graphs/README.md index dc7b18a80..ad1287ad5 100644 --- a/tree-sitter-stack-graphs/README.md +++ b/tree-sitter-stack-graphs/README.md @@ -14,7 +14,7 @@ To use this library, add the following to your `Cargo.toml`: ```toml [dependencies] -tree-sitter-stack-graphs = "0.7" +tree-sitter-stack-graphs = "0.8" ``` Check out our [documentation](https://docs.rs/tree-sitter-stack-graphs/*/) for more details on how to use this library. From 640ba5efeeb67c39f2bea9e3f71c663be3d90376 Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Wed, 6 Mar 2024 11:30:31 +0100 Subject: [PATCH 221/291] Release stack-graphs v0.13.0 and tree-sitter-stack-graphs v0.8.1 --- .../Cargo.toml | 2 +- .../Cargo.toml | 2 +- stack-graphs/CHANGELOG.md | 26 +++++++++++++++++++ stack-graphs/Cargo.toml | 2 +- stack-graphs/README.md | 2 +- stack-graphs/src/graph.rs | 2 +- tree-sitter-stack-graphs/CHANGELOG.md | 4 +++ tree-sitter-stack-graphs/Cargo.toml | 4 +-- 8 files changed, 37 insertions(+), 7 deletions(-) diff --git a/languages/tree-sitter-stack-graphs-javascript/Cargo.toml b/languages/tree-sitter-stack-graphs-javascript/Cargo.toml index 5c9522f77..6c7006d9a 100644 --- a/languages/tree-sitter-stack-graphs-javascript/Cargo.toml +++ b/languages/tree-sitter-stack-graphs-javascript/Cargo.toml @@ -30,7 +30,7 @@ anyhow = { version = "1.0", optional = true } clap = { version = "4", optional = true } serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" -stack-graphs = { version = "0.12", path = "../../stack-graphs" } +stack-graphs = { version = "0.13", path = "../../stack-graphs" } tree-sitter-stack-graphs = { version = "0.8", path = "../../tree-sitter-stack-graphs" } tree-sitter-javascript = { git = "https://github.com/tree-sitter/tree-sitter-javascript", rev = "5720b249490b3c17245ba772f6be4a43edb4e3b7" } diff --git a/languages/tree-sitter-stack-graphs-typescript/Cargo.toml b/languages/tree-sitter-stack-graphs-typescript/Cargo.toml index fa273c2cd..be86d78cd 100644 --- a/languages/tree-sitter-stack-graphs-typescript/Cargo.toml +++ b/languages/tree-sitter-stack-graphs-typescript/Cargo.toml @@ -32,7 +32,7 @@ clap = { version = "4", optional = true } glob = "0.3" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" -stack-graphs = { version = ">=0.11, <=0.12", path = "../../stack-graphs" } +stack-graphs = { version = "0.13", path = "../../stack-graphs" } tree-sitter-stack-graphs = { version = "0.8", path = "../../tree-sitter-stack-graphs" } tree-sitter-typescript = "=0.20.2" tsconfig = "0.1.0" diff --git a/stack-graphs/CHANGELOG.md b/stack-graphs/CHANGELOG.md index c61221e8e..fa051176d 100644 --- a/stack-graphs/CHANGELOG.md +++ b/stack-graphs/CHANGELOG.md @@ -5,6 +5,32 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## v0.13.0 -- 2024-03-06 + +### Added + +- New type `StitcherConfig` to specify configuration flags that control stitching. +- Path stiching statistics can now be collected during stitching for debugging purposes. Disabled by default and can be enabled with `StitcherConfig`. +- A method `StackGraph::set_edge_precedence` that allows changing the precendence of an existing edge in a stack graph. +- A method `StackGraph::incoming_edge_degree` that returns the number of edges ending in a given node. +- A method `Database::get_incoming_path_degree` that returns the number of partial paths in the database ending in a given node. +- Cycle detection improved by using incoming path or edge degrees to reduce the amount of data to keep in memory. +- Visualization uses different colors for nodes of different files, which makes understanding multi-file graphs easier. + +### Changed + +- Methods and types that do stitching in their implementation now require a `StitcherConfig` value. These include `Assertion::run`, `ForwardPartialPathStitcher::find_*`, and `Test::run`. +- The SQLite storage data encoding and queries changed to improve performance. + +### Removed + +- Method `StackGraph::remove_edge` has been removed. + +### Fixed + +- A panic when using an arena after `Arena::clear` or `SupplementalArena::clear` was called. +- Missing candidates when looking up root paths in a `Database`. + ## v0.12.0 -- 2023-07-27 ### Added diff --git a/stack-graphs/Cargo.toml b/stack-graphs/Cargo.toml index 0633d40c4..2e40b76eb 100644 --- a/stack-graphs/Cargo.toml +++ b/stack-graphs/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "stack-graphs" -version = "0.12.0" +version = "0.13.0" description = "Name binding for arbitrary programming languages" homepage = "https://github.com/github/stack-graphs/tree/main/stack-graphs" repository = "https://github.com/github/stack-graphs/" diff --git a/stack-graphs/README.md b/stack-graphs/README.md index 7805d19ef..0c6a9e878 100644 --- a/stack-graphs/README.md +++ b/stack-graphs/README.md @@ -9,7 +9,7 @@ To use this library, add the following to your `Cargo.toml`: ``` toml [dependencies] -stack-graphs = "0.12" +stack-graphs = "0.13" ``` Check out our [documentation](https://docs.rs/stack-graphs/) for more details on diff --git a/stack-graphs/src/graph.rs b/stack-graphs/src/graph.rs index fe1321387..84748c6bf 100644 --- a/stack-graphs/src/graph.rs +++ b/stack-graphs/src/graph.rs @@ -1307,7 +1307,7 @@ impl StackGraph { } } - /// Removes an edge from the stack graph. + /// Sets edge precedence of the given edge. pub fn set_edge_precedence( &mut self, source: Handle, diff --git a/tree-sitter-stack-graphs/CHANGELOG.md b/tree-sitter-stack-graphs/CHANGELOG.md index 50f8b1f9b..2b907e682 100644 --- a/tree-sitter-stack-graphs/CHANGELOG.md +++ b/tree-sitter-stack-graphs/CHANGELOG.md @@ -5,6 +5,10 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## v0.8.1 -- 2024-03-06 + +The `stack-graphs` dependency was updated to `v0.13` to fix the build problems of the `v0.8.0` release. + ## v0.8.0 -- 2024-03-05 The `tree-sitter` dependency version was updated to fix install problems. diff --git a/tree-sitter-stack-graphs/Cargo.toml b/tree-sitter-stack-graphs/Cargo.toml index 40b750641..875faf4ef 100644 --- a/tree-sitter-stack-graphs/Cargo.toml +++ b/tree-sitter-stack-graphs/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "tree-sitter-stack-graphs" -version = "0.8.0" +version = "0.8.1" description = "Create stack graphs using tree-sitter parsers" homepage = "https://github.com/github/stack-graphs/tree/main/tree-sitter-stack-graphs" repository = "https://github.com/github/stack-graphs/" @@ -69,7 +69,7 @@ regex = "1" rust-ini = "0.18" serde_json = { version="1.0", optional=true } sha1 = { version="0.10", optional=true } -stack-graphs = { version="0.12", path="../stack-graphs" } +stack-graphs = { version="0.13", path="../stack-graphs" } thiserror = "1.0" time = { version = "0.3", optional = true } tokio = { version = "1.26", optional = true, features = ["io-std", "rt", "rt-multi-thread"] } From 4ced0962bca76f800d653de5b2c7d8696ac4ddf9 Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Wed, 6 Mar 2024 16:43:42 +0100 Subject: [PATCH 222/291] Release tree-sitter-stack-graphs-java v0.3.0 --- languages/tree-sitter-stack-graphs-java/CHANGELOG.md | 10 ++++++++-- languages/tree-sitter-stack-graphs-java/Cargo.toml | 2 +- languages/tree-sitter-stack-graphs-java/README.md | 2 +- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/languages/tree-sitter-stack-graphs-java/CHANGELOG.md b/languages/tree-sitter-stack-graphs-java/CHANGELOG.md index f7483cabc..0f3a4b861 100644 --- a/languages/tree-sitter-stack-graphs-java/CHANGELOG.md +++ b/languages/tree-sitter-stack-graphs-java/CHANGELOG.md @@ -5,9 +5,15 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## Unreleased +## v0.3.0 -- 2024-03-06 -## [0.2.0] - 2023-03-21 +The `tree-sitter-stack-graphs` is updated to `v0.8`. + +### Changed + +- The `cli` feature is now required to install the CLI. + +## v0.2.0 -- 2023-03-21 ### Added diff --git a/languages/tree-sitter-stack-graphs-java/Cargo.toml b/languages/tree-sitter-stack-graphs-java/Cargo.toml index 8af4b3277..13e9e7340 100644 --- a/languages/tree-sitter-stack-graphs-java/Cargo.toml +++ b/languages/tree-sitter-stack-graphs-java/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "tree-sitter-stack-graphs-java" -version = "0.2.0" +version = "0.3.0" description = "Stack graphs for the Java programming language" homepage = "https://github.com/github/stack-graphs/tree/main/languages/tree-sitter-stack-graphs-java" diff --git a/languages/tree-sitter-stack-graphs-java/README.md b/languages/tree-sitter-stack-graphs-java/README.md index 40c0f847f..cf16b7dab 100644 --- a/languages/tree-sitter-stack-graphs-java/README.md +++ b/languages/tree-sitter-stack-graphs-java/README.md @@ -13,7 +13,7 @@ To use this library, add the following to your `Cargo.toml`: ```toml [dependencies] -tree-sitter-stack-graphs-java = "0.2.0" +tree-sitter-stack-graphs-java = "0.3" ``` Check out our [documentation](https://docs.rs/tree-sitter-stack-graphs-java/*/) for more details on how to use this library. From 426e14dceb2252785ceed2de666dcdab20ef393c Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Wed, 6 Mar 2024 17:00:39 +0100 Subject: [PATCH 223/291] Release tree-sitter-stack-graphs-javascript v0.1.0 --- languages/tree-sitter-stack-graphs-javascript/CHANGELOG.md | 4 ++++ languages/tree-sitter-stack-graphs-javascript/README.md | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/languages/tree-sitter-stack-graphs-javascript/CHANGELOG.md b/languages/tree-sitter-stack-graphs-javascript/CHANGELOG.md index 3b21ea383..2ba28c310 100644 --- a/languages/tree-sitter-stack-graphs-javascript/CHANGELOG.md +++ b/languages/tree-sitter-stack-graphs-javascript/CHANGELOG.md @@ -4,3 +4,7 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## v0.1.0 -- 2024-03-06 + +Initial release. diff --git a/languages/tree-sitter-stack-graphs-javascript/README.md b/languages/tree-sitter-stack-graphs-javascript/README.md index 6d449be9c..04317fbd0 100644 --- a/languages/tree-sitter-stack-graphs-javascript/README.md +++ b/languages/tree-sitter-stack-graphs-javascript/README.md @@ -13,7 +13,7 @@ To use this library, add the following to your `Cargo.toml`: ```toml [dependencies] -tree-sitter-stack-graphs-javascript = "0.1.0" +tree-sitter-stack-graphs-javascript = "0.1" ``` Check out our [documentation](https://docs.rs/tree-sitter-stack-graphs-javascript/*/) for more details on how to use this library. From 91e8a5edefb946ae7aa04ae564a39d7cd72230d5 Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Wed, 6 Mar 2024 17:01:39 +0100 Subject: [PATCH 224/291] Release tree-sitter-stack-graphs-python v0.1.0 --- ...ublish-tree-sitter-stack-graphs-python.yml | 45 +++++++++++++++++++ .../CHANGELOG.md | 4 ++ .../tree-sitter-stack-graphs-python/README.md | 2 +- 3 files changed, 50 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/publish-tree-sitter-stack-graphs-python.yml diff --git a/.github/workflows/publish-tree-sitter-stack-graphs-python.yml b/.github/workflows/publish-tree-sitter-stack-graphs-python.yml new file mode 100644 index 000000000..5d6c83ba2 --- /dev/null +++ b/.github/workflows/publish-tree-sitter-stack-graphs-python.yml @@ -0,0 +1,45 @@ +name: Publish tree-sitter-stack-graphs-python release + +on: + push: + tags: + - tree-sitter-stack-graphs-python-v* + +jobs: + publish-crate: + runs-on: ubuntu-latest + env: + CARGO_TERM_COLOR: always + CARGO_INCREMENTAL: 0 + CRATE_DIR: './languages/tree-sitter-stack-graphs-python' + steps: + - name: Install Rust environment + uses: hecrj/setup-rust-action@v1 + - name: Checkout repository + uses: actions/checkout@v3 + # TODO Verify the crate version matches the tag + - name: Test crate + run: cargo test --all-features + working-directory: ${{ env.CRATE_DIR }} + - name: Verify publish crate + run: cargo publish --dry-run + working-directory: ${{ env.CRATE_DIR }} + - name: Publish crate + run: cargo publish + working-directory: ${{ env.CRATE_DIR }} + env: + CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} + create-release: + needs: publish-crate + runs-on: ubuntu-latest + permissions: + contents: write + steps: + - name: Checkout repository + uses: actions/checkout@v3 + - name: Create GitHub release + uses: ncipollo/release-action@v1 + with: + body: | + Find more info on all releases at https://crates.io/crates/tree-sitter-stack-graphs-python. + token: ${{ secrets.GITHUB_TOKEN }} diff --git a/languages/tree-sitter-stack-graphs-python/CHANGELOG.md b/languages/tree-sitter-stack-graphs-python/CHANGELOG.md index 9e6604deb..876b16cb9 100644 --- a/languages/tree-sitter-stack-graphs-python/CHANGELOG.md +++ b/languages/tree-sitter-stack-graphs-python/CHANGELOG.md @@ -4,3 +4,7 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## v0.1.0 -- 2024-03-06 + +Initial release. diff --git a/languages/tree-sitter-stack-graphs-python/README.md b/languages/tree-sitter-stack-graphs-python/README.md index b5fbc2f32..0393e0004 100644 --- a/languages/tree-sitter-stack-graphs-python/README.md +++ b/languages/tree-sitter-stack-graphs-python/README.md @@ -13,7 +13,7 @@ To use this library, add the following to your `Cargo.toml`: ```toml [dependencies] -tree-sitter-stack-graphs-python = "0.1.0" +tree-sitter-stack-graphs-python = "0.1" ``` Check out our [documentation](https://docs.rs/tree-sitter-stack-graphs-python/*/) for more details on how to use this library. From f1d2ef591786f32c496c901c8ad344901ab3055c Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Wed, 6 Mar 2024 17:03:04 +0100 Subject: [PATCH 225/291] Release tree-sitter-stack-graphs-typescript v0.2.0 --- .../CHANGELOG.md | 14 +++++++++++++- .../tree-sitter-stack-graphs-typescript/Cargo.toml | 2 +- .../tree-sitter-stack-graphs-typescript/README.md | 2 +- 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/languages/tree-sitter-stack-graphs-typescript/CHANGELOG.md b/languages/tree-sitter-stack-graphs-typescript/CHANGELOG.md index e5b478f09..a23db2f0e 100644 --- a/languages/tree-sitter-stack-graphs-typescript/CHANGELOG.md +++ b/languages/tree-sitter-stack-graphs-typescript/CHANGELOG.md @@ -5,7 +5,19 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## Unreleased +## v0.2.0 -- 2024-03-06 + +The `tree-sitter-stack-graphs` is updated to `v0.8`. + +### Added + +- An experimental VSCode LSP plugin that supports code navigation based on the stack graph rules. _Purely an experiment, not ready for serious use!_ Requires the `lsp` feature to be enabled. + +### Changed + +- Various improvements to the rules for imports and packages. + +## v0.1.0 -- 2023-01-27 ### Added diff --git a/languages/tree-sitter-stack-graphs-typescript/Cargo.toml b/languages/tree-sitter-stack-graphs-typescript/Cargo.toml index be86d78cd..822eeb498 100644 --- a/languages/tree-sitter-stack-graphs-typescript/Cargo.toml +++ b/languages/tree-sitter-stack-graphs-typescript/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "tree-sitter-stack-graphs-typescript" -version = "0.1.0" +version = "0.2.0" description = "Stack graphs definition for TypeScript using tree-sitter-typescript" readme = "README.md" keywords = ["tree-sitter", "stack-graphs", "typescript"] diff --git a/languages/tree-sitter-stack-graphs-typescript/README.md b/languages/tree-sitter-stack-graphs-typescript/README.md index fada7e2df..9b6c82069 100644 --- a/languages/tree-sitter-stack-graphs-typescript/README.md +++ b/languages/tree-sitter-stack-graphs-typescript/README.md @@ -13,7 +13,7 @@ To use this library, add the following to your `Cargo.toml`: ```toml [dependencies] -tree-sitter-stack-graphs-typescript = "0.1.0" +tree-sitter-stack-graphs-typescript = "0.2" ``` Check out our [documentation](https://docs.rs/tree-sitter-stack-graphs-typescript/*/) for more details on how to use this library. From 1547ea202ba54ab3703c8334182b3c1f06fc9965 Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Fri, 8 Mar 2024 16:57:46 +0100 Subject: [PATCH 226/291] Update tree-sitter-javascript to 0.20.1 --- .../Cargo.toml | 3 +- .../src/stack-graphs.tsg | 155 +++++++----------- 2 files changed, 57 insertions(+), 101 deletions(-) diff --git a/languages/tree-sitter-stack-graphs-javascript/Cargo.toml b/languages/tree-sitter-stack-graphs-javascript/Cargo.toml index 6c7006d9a..fe8863bba 100644 --- a/languages/tree-sitter-stack-graphs-javascript/Cargo.toml +++ b/languages/tree-sitter-stack-graphs-javascript/Cargo.toml @@ -31,8 +31,9 @@ clap = { version = "4", optional = true } serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" stack-graphs = { version = "0.13", path = "../../stack-graphs" } +tree-sitter-graph = "0.11.2" +tree-sitter-javascript = "=0.20.1" tree-sitter-stack-graphs = { version = "0.8", path = "../../tree-sitter-stack-graphs" } -tree-sitter-javascript = { git = "https://github.com/tree-sitter/tree-sitter-javascript", rev = "5720b249490b3c17245ba772f6be4a43edb4e3b7" } [dev-dependencies] anyhow = "1.0" diff --git a/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg b/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg index e69a4636a..f9e724011 100644 --- a/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg +++ b/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg @@ -1564,7 +1564,7 @@ inherit .containing_class_value node @method_def.after_scope node @method_def.before_scope node @method_def.method_value - + } ( @@ -1572,7 +1572,7 @@ inherit .containing_class_value name:(_)@name)@method_def (#eq? @name "constructor") ) { - + ; augmentation for the constructor attr (@name.pop) symbol_definition = "GUARD:CONSTRUCTOR", source_node = @name edge @method_def.class_value -> @name.pop @@ -1672,7 +1672,7 @@ inherit .containing_class_value node @property.pop node property_pop_dot - + attr (@property.pop) node_definition = @property attr (property_pop_dot) pop_symbol = "GUARD:MEMBER" edge @field_def.after_scope -> property_pop_dot @@ -2321,7 +2321,7 @@ inherit .containing_class_value (yield_expression) (spread_element) ]@expr { - + node @expr.after_scope node @expr.before_scope node @expr.value @@ -2418,7 +2418,10 @@ inherit .containing_class_value ;; #### Variables -(primary_expression/identifier)@variable { +[ + (primary_expression/identifier)@variable + (member_expression . (identifier)@variable) +] { ; value is a lookup, ie a push attr (@variable.value) node_reference = @variable edge @variable.value -> @variable.before_scope @@ -2565,7 +2568,7 @@ inherit .containing_class_value ; shorthand property identifier (shorthand_property_identifier)@shorthand_property_identifier { - + node @shorthand_property_identifier.after_scope node @shorthand_property_identifier.before_scope @@ -2616,7 +2619,7 @@ inherit .containing_class_value ; This is done differently depending on what the key is. See next rules. ; attr @key.pop "pop" = @key, "definition" - + node @pair.key_pop edge @pair.key_pop -> @value.value edge @object.member_pop -> @pair.key_pop @@ -3171,8 +3174,7 @@ inherit .containing_class_value ;; ##### Member Expressions (member_expression - object: (_)@object - property: (_)@property)@member_expr + (_)@object . (_)@property)@member_expr { node member_push @@ -3555,7 +3557,7 @@ inherit .containing_class_value (class name:(_)@name body:(_)@body)@class { - + node @name.pop attr (@name.pop) syntax_type = "class" @@ -3604,7 +3606,6 @@ inherit .containing_class_value (jsx_text) (jsx_element) (jsx_self_closing_element) - (jsx_fragment) (jsx_expression) ]@first_child ) { @@ -3616,7 +3617,6 @@ inherit .containing_class_value (jsx_text) (jsx_element) (jsx_self_closing_element) - (jsx_fragment) (jsx_expression) ]@left_child . @@ -3624,7 +3624,6 @@ inherit .containing_class_value (jsx_text) (jsx_element) (jsx_self_closing_element) - (jsx_fragment) (jsx_expression) ]@right_child ) { @@ -3636,7 +3635,6 @@ inherit .containing_class_value (jsx_text) (jsx_element) (jsx_self_closing_element) - (jsx_fragment) (jsx_expression) ]@last_child . @@ -3648,25 +3646,37 @@ inherit .containing_class_value (jsx_text)@jsx_text { node @jsx_text.before_scope node @jsx_text.after_scope - + edge @jsx_text.after_scope -> @jsx_text.before_scope } -(jsx_opening_element - name:(_)@element_name)@jsx_opening_element { +(jsx_opening_element)@jsx_opening_element { node @jsx_opening_element.before_scope node @jsx_opening_element.after_scope +} + +(jsx_opening_element + name:(_)@element_name)@jsx_opening_element { + edge @element_name.before_scope -> @jsx_opening_element.before_scope } +(jsx_opening_element + !name)@jsx_opening_element +{ + + edge @jsx_opening_element.after_scope -> @jsx_opening_element.before_scope + +} + (jsx_opening_element name:(_)@element_name !attribute)@jsx_opening_element { - + edge @jsx_opening_element.after_scope -> @element_name.after_scope } @@ -3734,11 +3744,20 @@ inherit .containing_class_value } +(jsx_self_closing_element + !name + !attribute)@jsx_self_closing_element +{ + + edge @jsx_self_closing_element.after_scope -> @jsx_self_closing_element.before_scope + +} + (jsx_self_closing_element name:(_)@element_name !attribute)@jsx_self_closing_element { - + edge @jsx_self_closing_element.after_scope -> @element_name.after_scope } @@ -3790,13 +3809,26 @@ inherit .containing_class_value } -(jsx_closing_element - name:(_)@element_name)@jsx_closing_element +(jsx_closing_element)@jsx_closing_element { node @jsx_closing_element.before_scope node @jsx_closing_element.after_scope - + +} + +(jsx_closing_element + !name)@jsx_closing_element +{ + + edge @jsx_closing_element.after_scope -> @jsx_closing_element.before_scope + +} + +(jsx_closing_element + name:(_)@element_name)@jsx_closing_element +{ + edge @element_name.before_scope -> @jsx_closing_element.before_scope edge @jsx_closing_element.after_scope -> @element_name.after_scope @@ -3827,83 +3859,6 @@ inherit .containing_class_value } -(nested_identifier)@nested_identifier { - node @nested_identifier.before_scope - node @nested_identifier.after_scope - node @nested_identifier.value - - edge @nested_identifier.after_scope -> @nested_identifier.before_scope -} - -(nested_identifier - (identifier)@first_part - (identifier)@second_part)@nested_identifier -{ - node guard_member - - attr (@first_part.value) node_reference = @first_part - attr (@second_part.value) node_reference = @second_part - attr (guard_member) symbol_reference = "GUARD:MEMBER" - - edge @first_part.value -> @nested_identifier.before_scope - edge guard_member -> @first_part.value - edge @second_part.value -> guard_member - edge @nested_identifier.value -> @second_part.value -} - -(nested_identifier - (nested_identifier)@first_part - (identifier)@second_part)@nested_identifier -{ - node guard_member - - attr (@second_part.value) node_reference = @second_part - attr (guard_member) symbol_reference = "GUARD:MEMBER" - - edge @first_part.before_scope -> @nested_identifier.before_scope - edge guard_member -> @first_part.value - edge @second_part.value -> guard_member - edge @nested_identifier.value -> @second_part.value -} - -(jsx_fragment (_)*@children)@jsx_fragment { - node @jsx_fragment.before_scope - node @jsx_fragment.after_scope - node @jsx_fragment.value - - if (is-empty @children) { - edge @jsx_fragment.after_scope -> @jsx_fragment.before_scope - } -} - -(jsx_fragment - . - (_)@first_child)@jsx_fragment -{ - edge @first_child.before_scope -> @jsx_fragment.before_scope -} - -(jsx_fragment - (_)@left_child - . - (_)@right_child) -{ - edge @right_child.before_scope -> @left_child.after_scope -} - -(jsx_fragment - (_)@last_child - .)@jsx_fragment -{ - edge @jsx_fragment.after_scope -> @last_child.after_scope -} - - - - - - - @@ -4785,7 +4740,7 @@ inherit .containing_class_value name:(_)@_method_name)@constructor ) ) - + (#eq? @_method_name "constructor") ) { From b785a8e70e72312387796e8b45b1df92516ebff1 Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Fri, 8 Mar 2024 17:26:46 +0100 Subject: [PATCH 227/291] Update tree-sitter-javascript to 0.20.2 --- .../Cargo.toml | 2 +- .../src/stack-graphs.tsg | 86 ++++++++++++------- 2 files changed, 55 insertions(+), 33 deletions(-) diff --git a/languages/tree-sitter-stack-graphs-javascript/Cargo.toml b/languages/tree-sitter-stack-graphs-javascript/Cargo.toml index fe8863bba..72f9069f8 100644 --- a/languages/tree-sitter-stack-graphs-javascript/Cargo.toml +++ b/languages/tree-sitter-stack-graphs-javascript/Cargo.toml @@ -32,7 +32,7 @@ serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" stack-graphs = { version = "0.13", path = "../../stack-graphs" } tree-sitter-graph = "0.11.2" -tree-sitter-javascript = "=0.20.1" +tree-sitter-javascript = "=0.20.2" tree-sitter-stack-graphs = { version = "0.8", path = "../../tree-sitter-stack-graphs" } [dev-dependencies] diff --git a/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg b/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg index f9e724011..a9167c3a1 100644 --- a/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg +++ b/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg @@ -2296,6 +2296,9 @@ inherit .containing_class_value (number) (string) (template_string) + (template_substitution) + (string_fragment) + (escape_sequence) (regex) (true) (false) @@ -2304,7 +2307,7 @@ inherit .containing_class_value (import) (object) (array) - (function) + (function_expression) (arrow_function) (generator_function) (class) @@ -2329,7 +2332,7 @@ inherit .containing_class_value } [ - (function body:(_)@body) + (function_expression body:(_)@body) (arrow_function body:(_)@body) (generator_function body:(_)@body) ] { @@ -2367,8 +2370,8 @@ inherit .containing_class_value ;; #### Template Strings ; template_strings w/ no substitutions -(template_string (_)* @substs)@template_string { - if (is-empty @substs) { +(template_string (_)* @parts)@template_string { + if (is-empty @parts) { edge @template_string.after_scope -> @template_string.before_scope } } @@ -2376,33 +2379,45 @@ inherit .containing_class_value ; nonempty template string, value ; LATER-TODO this isn't really right, but it gets flows through the template string ; which may be useful -(template_string (template_substitution (_)@inner_expr))@template_string { +(template_string (_)@part)@template_string { ; the value of a template string is a template string value built from the values of its substitutions ; attr (@template_string.value) "template_string_value" - edge @template_string.value -> @inner_expr.value + edge @template_string.value -> @part.value } ; nonempty template string, first substitution -(template_string . (template_substitution (_)@first_inner_expr))@template_string { +(template_string . (_)@first)@template_string { ; scopes propagate into the first subtitution of the template string - edge @first_inner_expr.before_scope -> @template_string.before_scope + edge @first.before_scope -> @template_string.before_scope } ; nonempty template string, between substitutions (template_string - (template_substitution (_)@left_inner_expr) + (_) @left . - (template_substitution (_)@right_inner_expr))@_template_string { + (_) @right) { ; scopes propagate from left substitutions to right substitutions - edge @right_inner_expr.before_scope -> @left_inner_expr.after_scope + edge @right.before_scope -> @left.after_scope } ; nonempty template string, last substitution -(template_string . (template_substitution (_)@last_inner_expr))@template_string { +(template_string (_) @last .)@template_string { ; scopes propagate out of the last substitution to the template string - edge @template_string.after_scope -> @last_inner_expr.after_scope + edge @template_string.after_scope -> @last.after_scope +} + +[ + (string_fragment) + (escape_sequence) +]@part { + edge @part.after_scope -> @part.before_scope } +(template_substitution (_)@expr)@subst { + edge @expr.before_scope -> @subst.before_scope + edge @subst.after_scope -> @expr.after_scope + edge @subst.value -> @expr.value +} ;; #### Numbers @@ -2785,7 +2800,7 @@ inherit .containing_class_value ;; #### Function Literals ; functions with names -(function +(function_expression name:(_)@name parameters:(_)@call_sig)@fun { @@ -2800,7 +2815,7 @@ inherit .containing_class_value ; function -(function +(function_expression parameters:(_)@call_sig body:(_)@body)@fun { @@ -2859,7 +2874,7 @@ inherit .containing_class_value edge @fun.value_arg_scope -> JUMP_TO_SCOPE_NODE } -(function +(function_expression parameters: (formal_parameters (_)@param))@fun { @@ -3485,20 +3500,27 @@ inherit .containing_class_value ;; #### Comma Operator / Sequence Expressions -(sequence_expression - left: (_)@left - right: (_)@right)@sequence_expr { +(sequence_expression (_)* @elems)@sequence_expr { + if (is-empty @elems) { + edge @sequence_expr.after_scope -> @sequence_expr.before_scope + } +} + +(sequence_expression . (_)@first)@sequence_expr { + edge @first.before_scope -> @sequence_expr.before_scope +} - ; scopes propagate left to right - edge @left.before_scope -> @sequence_expr.before_scope +(sequence_expression (_)@left . (_)@right) { edge @right.before_scope -> @left.after_scope - edge @sequence_expr.after_scope -> @right.after_scope +} - ; the value is just the value of the right - edge @sequence_expr.value -> @right.value +(sequence_expression (_)@last .)@sequence_expr { + edge @sequence_expr.after_scope -> @last.after_scope + edge @sequence_expr.value -> @last.value } + ;; #### Ternary Expression (ternary_expression @@ -4716,7 +4738,7 @@ inherit .containing_class_value } -(function +(function_expression name:(_)@name parameters:(_)@_call_sig)@fun { @@ -4830,7 +4852,7 @@ inherit .containing_class_value (variable_declarator name:(identifier)@name value: [ - (function) + (function_expression) (generator_function) (arrow_function) ])) @@ -4838,7 +4860,7 @@ inherit .containing_class_value (variable_declarator name:(identifier)@name value: [ - (function) + (function_expression) (generator_function) (arrow_function) ])) @@ -4848,7 +4870,7 @@ inherit .containing_class_value ; (member_expression property:(_)@name) ; FIXME member expressions are references and have no .pop ] right: [ - (function) + (function_expression) (generator_function) (arrow_function) ]) @@ -4878,7 +4900,7 @@ inherit .containing_class_value ) ] right: [ - (function) + (function_expression) (generator_function) (arrow_function) ])@assignment_expr @@ -4897,7 +4919,7 @@ inherit .containing_class_value object:(_)@_module property:(_)@_exports) right: [ - (function) + (function_expression) (generator_function) (arrow_function) ])@assignment_expr @@ -4913,7 +4935,7 @@ inherit .containing_class_value (export_statement "default" value:[ - (function) + (function_expression) (generator_function) (arrow_function) ])@export_stmt { @@ -4927,7 +4949,7 @@ inherit .containing_class_value (pair key: (_)@name value: [ - (function) + (function_expression) (generator_function) (arrow_function) ]) { From b5cb3eb2603c06b54a1f2ba1449bb176f1e82f5a Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Fri, 8 Mar 2024 17:27:22 +0100 Subject: [PATCH 228/291] Update tree-sitter-javascript to 0.20.3 --- languages/tree-sitter-stack-graphs-javascript/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/languages/tree-sitter-stack-graphs-javascript/Cargo.toml b/languages/tree-sitter-stack-graphs-javascript/Cargo.toml index 72f9069f8..b56925c75 100644 --- a/languages/tree-sitter-stack-graphs-javascript/Cargo.toml +++ b/languages/tree-sitter-stack-graphs-javascript/Cargo.toml @@ -32,7 +32,7 @@ serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" stack-graphs = { version = "0.13", path = "../../stack-graphs" } tree-sitter-graph = "0.11.2" -tree-sitter-javascript = "=0.20.2" +tree-sitter-javascript = "=0.20.3" tree-sitter-stack-graphs = { version = "0.8", path = "../../tree-sitter-stack-graphs" } [dev-dependencies] From 88bc1700ad54d09bf30f4c3aa1e7eb2b399c7f75 Mon Sep 17 00:00:00 2001 From: Eugene Yakubovich Date: Wed, 28 Feb 2024 17:18:26 -0800 Subject: [PATCH 229/291] Support for TSX Support TSX dialect of the TypeScript. Because the TSX is almost a superset of TypeScript, it makes sense to reuse the .tsg file. However it requires a bit of preprocessing to handle the differences. --- .../Cargo.toml | 2 + .../askama.toml | 9 +++ .../rust/lib.rs | 56 ++++++++++++++++++- .../src/stack-graphs.tsg | 6 +- tree-sitter-stack-graphs/src/loader.rs | 6 ++ 5 files changed, 76 insertions(+), 3 deletions(-) create mode 100644 languages/tree-sitter-stack-graphs-typescript/askama.toml diff --git a/languages/tree-sitter-stack-graphs-typescript/Cargo.toml b/languages/tree-sitter-stack-graphs-typescript/Cargo.toml index 822eeb498..35bd6e3ae 100644 --- a/languages/tree-sitter-stack-graphs-typescript/Cargo.toml +++ b/languages/tree-sitter-stack-graphs-typescript/Cargo.toml @@ -28,8 +28,10 @@ lsp = ["tree-sitter-stack-graphs/lsp"] [dependencies] anyhow = { version = "1.0", optional = true } +askama = "0.12.1" clap = { version = "4", optional = true } glob = "0.3" +lazy_static = "1.4.0" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" stack-graphs = { version = "0.13", path = "../../stack-graphs" } diff --git a/languages/tree-sitter-stack-graphs-typescript/askama.toml b/languages/tree-sitter-stack-graphs-typescript/askama.toml new file mode 100644 index 000000000..124fdbde6 --- /dev/null +++ b/languages/tree-sitter-stack-graphs-typescript/askama.toml @@ -0,0 +1,9 @@ +[general] +# Directories to search for templates, relative to the crate root. +dirs = ["src"] +# Unless you add a `-` in a block, whitespace characters won't be trimmed. +whitespace = "preserve" + +[[escaper]] +path = "::askama::Text" +extensions = ["tsg"] diff --git a/languages/tree-sitter-stack-graphs-typescript/rust/lib.rs b/languages/tree-sitter-stack-graphs-typescript/rust/lib.rs index 3590649b2..43bc1efb6 100644 --- a/languages/tree-sitter-stack-graphs-typescript/rust/lib.rs +++ b/languages/tree-sitter-stack-graphs-typescript/rust/lib.rs @@ -5,6 +5,11 @@ // Please see the LICENSE-APACHE or LICENSE-MIT files in this distribution for license details. // ------------------------------------------------------------------------------------------------ +use std::path::PathBuf; +use std::borrow::Cow; + +use askama::Template; +use lazy_static::lazy_static; use tree_sitter_stack_graphs::loader::LanguageConfiguration; use tree_sitter_stack_graphs::loader::LoadError; use tree_sitter_stack_graphs::CancellationFlag; @@ -33,10 +38,25 @@ pub const FILE_PATH_VAR: &str = "FILE_PATH"; /// The name of the project name global variable pub const PROJECT_NAME_VAR: &str = "PROJECT_NAME"; +lazy_static! { + static ref STACK_GRAPHS_TS_TSG_SOURCE: String = preprocess_tsg("typescript").unwrap(); + static ref STACK_GRAPHS_TSX_TSG_SOURCE: String = preprocess_tsg("tsx").unwrap(); +} + +#[derive(Template)] +#[template(path = "stack-graphs.tsg")] +struct TsgTemplate<'a> { + dialect: &'a str, +} + pub fn language_configuration(cancellation_flag: &dyn CancellationFlag) -> LanguageConfiguration { try_language_configuration(cancellation_flag).unwrap_or_else(|err| panic!("{}", err)) } +pub fn tsx_language_configuration(cancellation_flag: &dyn CancellationFlag) -> LanguageConfiguration { + try_tsx_language_configuration(cancellation_flag).unwrap_or_else(|err| panic!("{}", err)) +} + pub fn try_language_configuration( cancellation_flag: &dyn CancellationFlag, ) -> Result { @@ -46,7 +66,31 @@ pub fn try_language_configuration( None, vec![String::from("ts")], STACK_GRAPHS_TSG_PATH.into(), - STACK_GRAPHS_TSG_SOURCE, + &STACK_GRAPHS_TS_TSG_SOURCE, + Some(( + STACK_GRAPHS_BUILTINS_PATH.into(), + STACK_GRAPHS_BUILTINS_SOURCE, + )), + Some(STACK_GRAPHS_BUILTINS_CONFIG), + cancellation_flag, + )?; + lc.special_files + .add("tsconfig.json".to_string(), TsConfigAnalyzer {}) + .add("package.json".to_string(), NpmPackageAnalyzer {}); + lc.no_similar_paths_in_file = true; + Ok(lc) +} + +pub fn try_tsx_language_configuration( + cancellation_flag: &dyn CancellationFlag, +) -> Result { + let mut lc = LanguageConfiguration::from_sources( + tree_sitter_typescript::language_tsx(), + Some(String::from("source.tsx")), + None, + vec![String::from("tsx")], + STACK_GRAPHS_TSG_PATH.into(), + &STACK_GRAPHS_TSX_TSG_SOURCE, Some(( STACK_GRAPHS_BUILTINS_PATH.into(), STACK_GRAPHS_BUILTINS_SOURCE, @@ -60,3 +104,13 @@ pub fn try_language_configuration( lc.no_similar_paths_in_file = true; Ok(lc) } + +fn preprocess_tsg(dialect: &'static str) -> Result> { + let tmpl = TsgTemplate{ dialect }; + tmpl.render() + .map_err(|err| LoadError::PreprocessorError { + inner: err.to_string(), + tsg_path: PathBuf::from(STACK_GRAPHS_TSG_PATH), + tsg: Cow::from(STACK_GRAPHS_TSG_SOURCE), + }) +} diff --git a/languages/tree-sitter-stack-graphs-typescript/src/stack-graphs.tsg b/languages/tree-sitter-stack-graphs-typescript/src/stack-graphs.tsg index 66c432b46..1fc595b9a 100644 --- a/languages/tree-sitter-stack-graphs-typescript/src/stack-graphs.tsg +++ b/languages/tree-sitter-stack-graphs-typescript/src/stack-graphs.tsg @@ -2665,7 +2665,9 @@ if none @is_async { (ternary_expression) (this) (true) +{% if dialect == "typescript" -%} (type_assertion) +{% endif -%} (unary_expression) (undefined) (update_expression) @@ -4040,7 +4042,7 @@ if none @is_async { } - +{% if dialect == "typescript" -%} ;; Type Assertion ; (type_assertion @@ -4055,7 +4057,7 @@ if none @is_async { ; propagate lexical scope edge @expr.lexical_scope -> @type_assert.lexical_scope } - +{% endif -%} ;; As Expression diff --git a/tree-sitter-stack-graphs/src/loader.rs b/tree-sitter-stack-graphs/src/loader.rs index 9ffc1d674..88be0acf1 100644 --- a/tree-sitter-stack-graphs/src/loader.rs +++ b/tree-sitter-stack-graphs/src/loader.rs @@ -423,6 +423,12 @@ pub enum LoadError<'a> { }, #[error(transparent)] TreeSitter(anyhow::Error), + #[error("Preprocessor error: {inner}")] + PreprocessorError { + inner: String, + tsg_path: PathBuf, + tsg: Cow<'a, str>, + } } impl LoadError<'_> { From cd323fdfb8dcf30578a61aced42c2721c322174e Mon Sep 17 00:00:00 2001 From: Eugene Yakubovich Date: Sat, 9 Mar 2024 20:31:05 -0800 Subject: [PATCH 230/291] Support for TSX Preprocesses the .tsg file into typescript and tsx specific .tsg files. The preprocessing is done in the build script. Add a --dialect|-d option to the CLI to select the dialect. --- .../Cargo.toml | 6 +- .../askama.toml | 9 -- .../build.rs | 98 +++++++++++++++++++ .../rust/bin.rs | 21 +++- .../rust/lib.rs | 50 +++------- .../src/stack-graphs.tsg | 8 +- tree-sitter-stack-graphs/src/loader.rs | 6 -- 7 files changed, 137 insertions(+), 61 deletions(-) delete mode 100644 languages/tree-sitter-stack-graphs-typescript/askama.toml create mode 100644 languages/tree-sitter-stack-graphs-typescript/build.rs diff --git a/languages/tree-sitter-stack-graphs-typescript/Cargo.toml b/languages/tree-sitter-stack-graphs-typescript/Cargo.toml index 35bd6e3ae..46360f19c 100644 --- a/languages/tree-sitter-stack-graphs-typescript/Cargo.toml +++ b/languages/tree-sitter-stack-graphs-typescript/Cargo.toml @@ -28,10 +28,8 @@ lsp = ["tree-sitter-stack-graphs/lsp"] [dependencies] anyhow = { version = "1.0", optional = true } -askama = "0.12.1" clap = { version = "4", optional = true } glob = "0.3" -lazy_static = "1.4.0" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" stack-graphs = { version = "0.13", path = "../../stack-graphs" } @@ -42,3 +40,7 @@ tsconfig = "0.1.0" [dev-dependencies] anyhow = { version = "1.0" } tree-sitter-stack-graphs = { version = "0.8", path = "../../tree-sitter-stack-graphs", features = ["cli"] } + +[build-dependencies] +anyhow = { version = "1.0" } +regex = "1.10.3" diff --git a/languages/tree-sitter-stack-graphs-typescript/askama.toml b/languages/tree-sitter-stack-graphs-typescript/askama.toml deleted file mode 100644 index 124fdbde6..000000000 --- a/languages/tree-sitter-stack-graphs-typescript/askama.toml +++ /dev/null @@ -1,9 +0,0 @@ -[general] -# Directories to search for templates, relative to the crate root. -dirs = ["src"] -# Unless you add a `-` in a block, whitespace characters won't be trimmed. -whitespace = "preserve" - -[[escaper]] -path = "::askama::Text" -extensions = ["tsg"] diff --git a/languages/tree-sitter-stack-graphs-typescript/build.rs b/languages/tree-sitter-stack-graphs-typescript/build.rs new file mode 100644 index 000000000..3d5867cfa --- /dev/null +++ b/languages/tree-sitter-stack-graphs-typescript/build.rs @@ -0,0 +1,98 @@ +// -*- coding: utf-8 -*- +// ------------------------------------------------------------------------------------------------ +// Copyright © 2024, stack-graphs authors. +// Licensed under either of Apache License, Version 2.0, or MIT license, at your option. +// Please see the LICENSE-APACHE or LICENSE-MIT files in this distribution for license details. +// ------------------------------------------------------------------------------------------------ + +use std::collections::HashSet; +use std::path::Path; + +use anyhow::{Result, anyhow, bail}; +use regex::Regex; + +/// A stack of dialects as selected by the directives +#[derive(Debug, Default)] +struct DialectStack(Vec>); + +impl DialectStack { + fn push(&mut self, values: Vec) -> Result<()> { + // ensure that the new values are a subset of the current scope + if let Some(current) = self.0.last() { + if !values.iter().all(|v| current.contains(v)) { + bail!("Directive values are not a subset of the current scope"); + } + } + + self.0.push(values.into_iter().collect()); + + Ok(()) + } + + fn pop(&mut self) -> Result<()> { + if let Some(_) = self.0.pop() { + Ok(()) + } else { + Err(anyhow!("Directive stack is empty")) + } + } + + fn contains(&self, query: &str) -> bool { + if let Some(current) = self.0.last() { + current.contains(query) + } else { + true + } + } +} + +/// preprocess the input file, removing lines that are not for the selected dialect +fn preprocess(input: impl std::io::Read, mut output: impl std::io::Write, dialect: &str) -> anyhow::Result<()> { + // Matches: ; # dialect typescript tsx + let directive_start = Regex::new(r";[ \t]*#[ \t]*dialect[ \t]+([a-zA-Z\t ]+)").unwrap(); + + // Matches: ; # end + let directirve_end = Regex::new(r";[ \t]*#[ \t]*end").unwrap(); + + let input = std::io::read_to_string(input)?; + + let mut stack = DialectStack::default(); + + for line in input.lines() { + if let Some(captures) = directive_start.captures(line) { + let directive = captures.get(1).unwrap().as_str(); + let dialects = directive.split_whitespace().map(|s| s.to_string()).collect(); + stack.push(dialects)?; + output.write_all(line.as_bytes())?; + } else if directirve_end.is_match(line) { + stack.pop()?; + output.write_all(line.as_bytes())?; + } else { + if stack.contains(dialect) { + output.write_all(line.as_bytes())?; + } + } + // a new line is always written so that removed lines are padded to preserve line numbers + output.write(b"\n")?; + } + + Ok(()) +} + +const TSG_SOURCE: &str = "src/stack-graphs.tsg"; +const DIALECTS: [&str; 2] = ["typescript", "tsx"]; + +fn main() { + let out_dir = std::env::var_os("OUT_DIR").unwrap(); + for dialect in DIALECTS { + let input = std::fs::File::open(TSG_SOURCE).unwrap(); + + let out_filename = Path::new(&out_dir).join(format!("stack-graphs-{dialect}.tsg")); + let output = std::fs::File::create(out_filename).unwrap(); + + preprocess(input, output, dialect).unwrap(); + } + + println!("cargo:rerun-if-changed={TSG_SOURCE}"); + println!("cargo:rerun-if-changed=build.rs"); +} diff --git a/languages/tree-sitter-stack-graphs-typescript/rust/bin.rs b/languages/tree-sitter-stack-graphs-typescript/rust/bin.rs index b1ee4f568..7352111dd 100644 --- a/languages/tree-sitter-stack-graphs-typescript/rust/bin.rs +++ b/languages/tree-sitter-stack-graphs-typescript/rust/bin.rs @@ -12,22 +12,37 @@ use tree_sitter_stack_graphs::cli::provided_languages::Subcommands; use tree_sitter_stack_graphs::NoCancellation; fn main() -> anyhow::Result<()> { - let lc = match tree_sitter_stack_graphs_typescript::try_language_configuration(&NoCancellation) + let cli = Cli::parse(); + let lc = match language_configuration(&cli.dialect) { Ok(lc) => lc, Err(err) => { - eprintln!("{}", err.display_pretty()); + eprintln!("{err}"); return Err(anyhow!("Language configuration error")); } }; - let cli = Cli::parse(); let default_db_path = default_user_database_path_for_crate(env!("CARGO_PKG_NAME"))?; cli.subcommand.run(default_db_path, vec![lc]) } +fn language_configuration(dialect: &str) -> anyhow::Result { + match dialect { + "typescript" => tree_sitter_stack_graphs_typescript::try_language_configuration(&NoCancellation) + .map_err(|e| anyhow::anyhow!("{}", e.display_pretty())), + + "tsx" => tree_sitter_stack_graphs_typescript::try_language_configuration_tsx(&NoCancellation) + .map_err(|e| anyhow::anyhow!("{}", e.display_pretty())), + + _ => anyhow::bail!("Unknown dialect: {}", dialect) + } +} + #[derive(Parser)] #[clap(about, version)] pub struct Cli { + #[clap(short, long, default_value = "typescript")] + dialect: String, + #[clap(subcommand)] subcommand: Subcommands, } diff --git a/languages/tree-sitter-stack-graphs-typescript/rust/lib.rs b/languages/tree-sitter-stack-graphs-typescript/rust/lib.rs index 43bc1efb6..b87a038d4 100644 --- a/languages/tree-sitter-stack-graphs-typescript/rust/lib.rs +++ b/languages/tree-sitter-stack-graphs-typescript/rust/lib.rs @@ -5,11 +5,6 @@ // Please see the LICENSE-APACHE or LICENSE-MIT files in this distribution for license details. // ------------------------------------------------------------------------------------------------ -use std::path::PathBuf; -use std::borrow::Cow; - -use askama::Template; -use lazy_static::lazy_static; use tree_sitter_stack_graphs::loader::LanguageConfiguration; use tree_sitter_stack_graphs::loader::LoadError; use tree_sitter_stack_graphs::CancellationFlag; @@ -22,9 +17,11 @@ pub mod tsconfig; pub mod util; /// The stacks graphs tsg path for this language. -pub const STACK_GRAPHS_TSG_PATH: &str = "src/stack-graphs.tsg"; +pub const STACK_GRAPHS_TSG_TS_PATH: &str = concat!(env!("OUT_DIR"), "/stack-graphs-typescript.tsg"); +pub const STACK_GRAPHS_TSG_TSX_PATH: &str = concat!(env!("OUT_DIR"), "/stack-graphs-tsx.tsg"); /// The stack graphs tsg source for this language -pub const STACK_GRAPHS_TSG_SOURCE: &str = include_str!("../src/stack-graphs.tsg"); +pub const STACK_GRAPHS_TSG_TS_SOURCE: &str = include_str!(concat!(env!("OUT_DIR"), "/stack-graphs-typescript.tsg")); +pub const STACK_GRAPHS_TSG_TSX_SOURCE: &str = include_str!(concat!(env!("OUT_DIR"), "/stack-graphs-tsx.tsg")); /// The stack graphs builtins configuration for this language pub const STACK_GRAPHS_BUILTINS_CONFIG: &str = include_str!("../src/builtins.cfg"); @@ -38,25 +35,10 @@ pub const FILE_PATH_VAR: &str = "FILE_PATH"; /// The name of the project name global variable pub const PROJECT_NAME_VAR: &str = "PROJECT_NAME"; -lazy_static! { - static ref STACK_GRAPHS_TS_TSG_SOURCE: String = preprocess_tsg("typescript").unwrap(); - static ref STACK_GRAPHS_TSX_TSG_SOURCE: String = preprocess_tsg("tsx").unwrap(); -} - -#[derive(Template)] -#[template(path = "stack-graphs.tsg")] -struct TsgTemplate<'a> { - dialect: &'a str, -} - pub fn language_configuration(cancellation_flag: &dyn CancellationFlag) -> LanguageConfiguration { try_language_configuration(cancellation_flag).unwrap_or_else(|err| panic!("{}", err)) } -pub fn tsx_language_configuration(cancellation_flag: &dyn CancellationFlag) -> LanguageConfiguration { - try_tsx_language_configuration(cancellation_flag).unwrap_or_else(|err| panic!("{}", err)) -} - pub fn try_language_configuration( cancellation_flag: &dyn CancellationFlag, ) -> Result { @@ -65,8 +47,8 @@ pub fn try_language_configuration( Some(String::from("source.ts")), None, vec![String::from("ts")], - STACK_GRAPHS_TSG_PATH.into(), - &STACK_GRAPHS_TS_TSG_SOURCE, + STACK_GRAPHS_TSG_TS_PATH.into(), + STACK_GRAPHS_TSG_TS_SOURCE, Some(( STACK_GRAPHS_BUILTINS_PATH.into(), STACK_GRAPHS_BUILTINS_SOURCE, @@ -81,7 +63,11 @@ pub fn try_language_configuration( Ok(lc) } -pub fn try_tsx_language_configuration( +pub fn language_configuration_tsx(cancellation_flag: &dyn CancellationFlag) -> LanguageConfiguration { + try_language_configuration_tsx(cancellation_flag).unwrap_or_else(|err| panic!("{}", err)) +} + +pub fn try_language_configuration_tsx( cancellation_flag: &dyn CancellationFlag, ) -> Result { let mut lc = LanguageConfiguration::from_sources( @@ -89,8 +75,8 @@ pub fn try_tsx_language_configuration( Some(String::from("source.tsx")), None, vec![String::from("tsx")], - STACK_GRAPHS_TSG_PATH.into(), - &STACK_GRAPHS_TSX_TSG_SOURCE, + STACK_GRAPHS_TSG_TSX_PATH.into(), + STACK_GRAPHS_TSG_TSX_SOURCE, Some(( STACK_GRAPHS_BUILTINS_PATH.into(), STACK_GRAPHS_BUILTINS_SOURCE, @@ -104,13 +90,3 @@ pub fn try_tsx_language_configuration( lc.no_similar_paths_in_file = true; Ok(lc) } - -fn preprocess_tsg(dialect: &'static str) -> Result> { - let tmpl = TsgTemplate{ dialect }; - tmpl.render() - .map_err(|err| LoadError::PreprocessorError { - inner: err.to_string(), - tsg_path: PathBuf::from(STACK_GRAPHS_TSG_PATH), - tsg: Cow::from(STACK_GRAPHS_TSG_SOURCE), - }) -} diff --git a/languages/tree-sitter-stack-graphs-typescript/src/stack-graphs.tsg b/languages/tree-sitter-stack-graphs-typescript/src/stack-graphs.tsg index 1fc595b9a..63c56760f 100644 --- a/languages/tree-sitter-stack-graphs-typescript/src/stack-graphs.tsg +++ b/languages/tree-sitter-stack-graphs-typescript/src/stack-graphs.tsg @@ -2665,9 +2665,9 @@ if none @is_async { (ternary_expression) (this) (true) -{% if dialect == "typescript" -%} +; #dialect typescript (type_assertion) -{% endif -%} +; #end (unary_expression) (undefined) (update_expression) @@ -4042,7 +4042,7 @@ if none @is_async { } -{% if dialect == "typescript" -%} +; #dialect typescript ;; Type Assertion ; (type_assertion @@ -4057,7 +4057,7 @@ if none @is_async { ; propagate lexical scope edge @expr.lexical_scope -> @type_assert.lexical_scope } -{% endif -%} +; #end ;; As Expression diff --git a/tree-sitter-stack-graphs/src/loader.rs b/tree-sitter-stack-graphs/src/loader.rs index 88be0acf1..9ffc1d674 100644 --- a/tree-sitter-stack-graphs/src/loader.rs +++ b/tree-sitter-stack-graphs/src/loader.rs @@ -423,12 +423,6 @@ pub enum LoadError<'a> { }, #[error(transparent)] TreeSitter(anyhow::Error), - #[error("Preprocessor error: {inner}")] - PreprocessorError { - inner: String, - tsg_path: PathBuf, - tsg: Cow<'a, str>, - } } impl LoadError<'_> { From 921975535e55f4d5f5451a05df14367a06e78898 Mon Sep 17 00:00:00 2001 From: Eugene Yakubovich Date: Mon, 11 Mar 2024 11:20:02 -0700 Subject: [PATCH 231/291] Address feedback - Remove dialect stack - Better error handling - Use clap::ValueEnum - Append _typescript to {try}_language_configuration --- .../build.rs | 89 +++++++------------ .../rust/bin.rs | 34 ++++--- .../rust/lib.rs | 13 ++- .../rust/test.rs | 2 +- 4 files changed, 60 insertions(+), 78 deletions(-) diff --git a/languages/tree-sitter-stack-graphs-typescript/build.rs b/languages/tree-sitter-stack-graphs-typescript/build.rs index 3d5867cfa..4527a7e8e 100644 --- a/languages/tree-sitter-stack-graphs-typescript/build.rs +++ b/languages/tree-sitter-stack-graphs-typescript/build.rs @@ -5,72 +5,45 @@ // Please see the LICENSE-APACHE or LICENSE-MIT files in this distribution for license details. // ------------------------------------------------------------------------------------------------ -use std::collections::HashSet; use std::path::Path; -use anyhow::{Result, anyhow, bail}; +use anyhow::{Result, bail}; use regex::Regex; -/// A stack of dialects as selected by the directives -#[derive(Debug, Default)] -struct DialectStack(Vec>); - -impl DialectStack { - fn push(&mut self, values: Vec) -> Result<()> { - // ensure that the new values are a subset of the current scope - if let Some(current) = self.0.last() { - if !values.iter().all(|v| current.contains(v)) { - bail!("Directive values are not a subset of the current scope"); - } - } - - self.0.push(values.into_iter().collect()); - - Ok(()) - } - - fn pop(&mut self) -> Result<()> { - if let Some(_) = self.0.pop() { - Ok(()) - } else { - Err(anyhow!("Directive stack is empty")) - } - } - - fn contains(&self, query: &str) -> bool { - if let Some(current) = self.0.last() { - current.contains(query) - } else { - true - } - } -} +const TSG_SOURCE: &str = "src/stack-graphs.tsg"; +const DIALECTS: [&str; 2] = ["typescript", "tsx"]; /// preprocess the input file, removing lines that are not for the selected dialect -fn preprocess(input: impl std::io::Read, mut output: impl std::io::Write, dialect: &str) -> anyhow::Result<()> { - // Matches: ; # dialect typescript tsx - let directive_start = Regex::new(r";[ \t]*#[ \t]*dialect[ \t]+([a-zA-Z\t ]+)").unwrap(); +fn preprocess(input: impl std::io::Read, mut output: impl std::io::Write, dialect: &str) -> Result<()> { + // Matches: ; #dialect typescript + let directive_start = Regex::new(r";\s*#dialect\s+(\w+)").unwrap(); - // Matches: ; # end - let directirve_end = Regex::new(r";[ \t]*#[ \t]*end").unwrap(); + // Matches: ; #end + let directirve_end = Regex::new(r";\s*#end").unwrap(); let input = std::io::read_to_string(input)?; - let mut stack = DialectStack::default(); + // If the filter is None or Some(true), the lines are written to the output + let mut filter: Option = None; - for line in input.lines() { + for (line_no, line) in input.lines().enumerate() { if let Some(captures) = directive_start.captures(line) { let directive = captures.get(1).unwrap().as_str(); - let dialects = directive.split_whitespace().map(|s| s.to_string()).collect(); - stack.push(dialects)?; + if !DIALECTS.contains(&directive) { + bail!("Line {line_no}: unknown dialect: {directive}"); + } + + filter = Some(dialect == directive); output.write_all(line.as_bytes())?; } else if directirve_end.is_match(line) { - stack.pop()?; - output.write_all(line.as_bytes())?; - } else { - if stack.contains(dialect) { - output.write_all(line.as_bytes())?; + if filter.is_none() { + bail!("Line {line_no}: unmatched directive end"); } + + filter = None; + output.write_all(line.as_bytes())?; + } else if filter.unwrap_or(true) { + output.write_all(line.as_bytes())?; } // a new line is always written so that removed lines are padded to preserve line numbers output.write(b"\n")?; @@ -79,18 +52,20 @@ fn preprocess(input: impl std::io::Read, mut output: impl std::io::Write, dialec Ok(()) } -const TSG_SOURCE: &str = "src/stack-graphs.tsg"; -const DIALECTS: [&str; 2] = ["typescript", "tsx"]; - fn main() { - let out_dir = std::env::var_os("OUT_DIR").unwrap(); + let out_dir = std::env::var_os("OUT_DIR") + .expect("OUT_DIR is not set"); + for dialect in DIALECTS { - let input = std::fs::File::open(TSG_SOURCE).unwrap(); + let input = std::fs::File::open(TSG_SOURCE) + .expect("Failed to open stack-graphs.tsg"); let out_filename = Path::new(&out_dir).join(format!("stack-graphs-{dialect}.tsg")); - let output = std::fs::File::create(out_filename).unwrap(); + let output = std::fs::File::create(out_filename) + .expect("Failed to create output file"); - preprocess(input, output, dialect).unwrap(); + preprocess(input, output, dialect) + .expect("Failed to preprocess stack-graphs.tsg"); } println!("cargo:rerun-if-changed={TSG_SOURCE}"); diff --git a/languages/tree-sitter-stack-graphs-typescript/rust/bin.rs b/languages/tree-sitter-stack-graphs-typescript/rust/bin.rs index 7352111dd..d303f3a62 100644 --- a/languages/tree-sitter-stack-graphs-typescript/rust/bin.rs +++ b/languages/tree-sitter-stack-graphs-typescript/rust/bin.rs @@ -6,18 +6,26 @@ // ------------------------------------------------------------------------------------------------ use anyhow::anyhow; -use clap::Parser; +use clap::{Parser, ValueEnum}; use tree_sitter_stack_graphs::cli::database::default_user_database_path_for_crate; use tree_sitter_stack_graphs::cli::provided_languages::Subcommands; +use tree_sitter_stack_graphs::loader::{LanguageConfiguration, LoadError}; use tree_sitter_stack_graphs::NoCancellation; + /// Flag to select the dialect of the language + #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum)] + pub enum Dialect { + Typescript, + TSX, + } + fn main() -> anyhow::Result<()> { let cli = Cli::parse(); - let lc = match language_configuration(&cli.dialect) + let lc = match language_configuration(cli.dialect) { Ok(lc) => lc, Err(err) => { - eprintln!("{err}"); + eprintln!("{}", err.display_pretty()); return Err(anyhow!("Language configuration error")); } }; @@ -25,23 +33,23 @@ fn main() -> anyhow::Result<()> { cli.subcommand.run(default_db_path, vec![lc]) } -fn language_configuration(dialect: &str) -> anyhow::Result { +fn language_configuration<'a>(dialect: Dialect) -> Result> { match dialect { - "typescript" => tree_sitter_stack_graphs_typescript::try_language_configuration(&NoCancellation) - .map_err(|e| anyhow::anyhow!("{}", e.display_pretty())), - - "tsx" => tree_sitter_stack_graphs_typescript::try_language_configuration_tsx(&NoCancellation) - .map_err(|e| anyhow::anyhow!("{}", e.display_pretty())), - - _ => anyhow::bail!("Unknown dialect: {}", dialect) + Dialect::Typescript => tree_sitter_stack_graphs_typescript::try_language_configuration_typescript(&NoCancellation), + Dialect::TSX => tree_sitter_stack_graphs_typescript::try_language_configuration_tsx(&NoCancellation), } } #[derive(Parser)] #[clap(about, version)] pub struct Cli { - #[clap(short, long, default_value = "typescript")] - dialect: String, + #[clap( + short, + long, + value_enum, + default_value_t = Dialect::Typescript, + )] + dialect: Dialect, #[clap(subcommand)] subcommand: Subcommands, diff --git a/languages/tree-sitter-stack-graphs-typescript/rust/lib.rs b/languages/tree-sitter-stack-graphs-typescript/rust/lib.rs index b87a038d4..500bd29f0 100644 --- a/languages/tree-sitter-stack-graphs-typescript/rust/lib.rs +++ b/languages/tree-sitter-stack-graphs-typescript/rust/lib.rs @@ -17,8 +17,7 @@ pub mod tsconfig; pub mod util; /// The stacks graphs tsg path for this language. -pub const STACK_GRAPHS_TSG_TS_PATH: &str = concat!(env!("OUT_DIR"), "/stack-graphs-typescript.tsg"); -pub const STACK_GRAPHS_TSG_TSX_PATH: &str = concat!(env!("OUT_DIR"), "/stack-graphs-tsx.tsg"); +pub const STACK_GRAPHS_TSG_PATH: &str = "./stack-graphs.tsg"; /// The stack graphs tsg source for this language pub const STACK_GRAPHS_TSG_TS_SOURCE: &str = include_str!(concat!(env!("OUT_DIR"), "/stack-graphs-typescript.tsg")); pub const STACK_GRAPHS_TSG_TSX_SOURCE: &str = include_str!(concat!(env!("OUT_DIR"), "/stack-graphs-tsx.tsg")); @@ -35,11 +34,11 @@ pub const FILE_PATH_VAR: &str = "FILE_PATH"; /// The name of the project name global variable pub const PROJECT_NAME_VAR: &str = "PROJECT_NAME"; -pub fn language_configuration(cancellation_flag: &dyn CancellationFlag) -> LanguageConfiguration { - try_language_configuration(cancellation_flag).unwrap_or_else(|err| panic!("{}", err)) +pub fn language_configuration_typescript(cancellation_flag: &dyn CancellationFlag) -> LanguageConfiguration { + try_language_configuration_typescript(cancellation_flag).unwrap_or_else(|err| panic!("{}", err)) } -pub fn try_language_configuration( +pub fn try_language_configuration_typescript( cancellation_flag: &dyn CancellationFlag, ) -> Result { let mut lc = LanguageConfiguration::from_sources( @@ -47,7 +46,7 @@ pub fn try_language_configuration( Some(String::from("source.ts")), None, vec![String::from("ts")], - STACK_GRAPHS_TSG_TS_PATH.into(), + STACK_GRAPHS_TSG_PATH.into(), STACK_GRAPHS_TSG_TS_SOURCE, Some(( STACK_GRAPHS_BUILTINS_PATH.into(), @@ -75,7 +74,7 @@ pub fn try_language_configuration_tsx( Some(String::from("source.tsx")), None, vec![String::from("tsx")], - STACK_GRAPHS_TSG_TSX_PATH.into(), + STACK_GRAPHS_TSG_PATH.into(), STACK_GRAPHS_TSG_TSX_SOURCE, Some(( STACK_GRAPHS_BUILTINS_PATH.into(), diff --git a/languages/tree-sitter-stack-graphs-typescript/rust/test.rs b/languages/tree-sitter-stack-graphs-typescript/rust/test.rs index ca5b8494c..72d41d900 100644 --- a/languages/tree-sitter-stack-graphs-typescript/rust/test.rs +++ b/languages/tree-sitter-stack-graphs-typescript/rust/test.rs @@ -11,7 +11,7 @@ use tree_sitter_stack_graphs::ci::Tester; use tree_sitter_stack_graphs::NoCancellation; fn main() -> anyhow::Result<()> { - let lc = match tree_sitter_stack_graphs_typescript::try_language_configuration(&NoCancellation) + let lc = match tree_sitter_stack_graphs_typescript::try_language_configuration_typescript(&NoCancellation) { Ok(lc) => lc, Err(err) => { From bf0a4476d3e99df9ee8a944a42e21e3ce4387a5c Mon Sep 17 00:00:00 2001 From: Eugene Yakubovich Date: Tue, 12 Mar 2024 11:11:14 -0700 Subject: [PATCH 232/291] Fix formatting and typos - Also fixes error msgs to use 1-based line nums --- .../build.rs | 29 ++++++++++--------- .../rust/bin.rs | 25 +++++++++------- .../rust/lib.rs | 16 ++++++---- .../rust/test.rs | 5 ++-- 4 files changed, 45 insertions(+), 30 deletions(-) diff --git a/languages/tree-sitter-stack-graphs-typescript/build.rs b/languages/tree-sitter-stack-graphs-typescript/build.rs index 4527a7e8e..0c2d01c25 100644 --- a/languages/tree-sitter-stack-graphs-typescript/build.rs +++ b/languages/tree-sitter-stack-graphs-typescript/build.rs @@ -7,26 +7,33 @@ use std::path::Path; -use anyhow::{Result, bail}; +use anyhow::{bail, Result}; use regex::Regex; const TSG_SOURCE: &str = "src/stack-graphs.tsg"; const DIALECTS: [&str; 2] = ["typescript", "tsx"]; /// preprocess the input file, removing lines that are not for the selected dialect -fn preprocess(input: impl std::io::Read, mut output: impl std::io::Write, dialect: &str) -> Result<()> { +fn preprocess( + input: impl std::io::Read, + mut output: impl std::io::Write, + dialect: &str, +) -> Result<()> { // Matches: ; #dialect typescript let directive_start = Regex::new(r";\s*#dialect\s+(\w+)").unwrap(); // Matches: ; #end - let directirve_end = Regex::new(r";\s*#end").unwrap(); + let directive_end = Regex::new(r";\s*#end").unwrap(); let input = std::io::read_to_string(input)?; // If the filter is None or Some(true), the lines are written to the output let mut filter: Option = None; - for (line_no, line) in input.lines().enumerate() { + for (mut line_no, line) in input.lines().enumerate() { + // Line numbers are one based + line_no += 1; + if let Some(captures) = directive_start.captures(line) { let directive = captures.get(1).unwrap().as_str(); if !DIALECTS.contains(&directive) { @@ -35,7 +42,7 @@ fn preprocess(input: impl std::io::Read, mut output: impl std::io::Write, dialec filter = Some(dialect == directive); output.write_all(line.as_bytes())?; - } else if directirve_end.is_match(line) { + } else if directive_end.is_match(line) { if filter.is_none() { bail!("Line {line_no}: unmatched directive end"); } @@ -53,19 +60,15 @@ fn preprocess(input: impl std::io::Read, mut output: impl std::io::Write, dialec } fn main() { - let out_dir = std::env::var_os("OUT_DIR") - .expect("OUT_DIR is not set"); + let out_dir = std::env::var_os("OUT_DIR").expect("OUT_DIR is not set"); for dialect in DIALECTS { - let input = std::fs::File::open(TSG_SOURCE) - .expect("Failed to open stack-graphs.tsg"); + let input = std::fs::File::open(TSG_SOURCE).expect("Failed to open stack-graphs.tsg"); let out_filename = Path::new(&out_dir).join(format!("stack-graphs-{dialect}.tsg")); - let output = std::fs::File::create(out_filename) - .expect("Failed to create output file"); + let output = std::fs::File::create(out_filename).expect("Failed to create output file"); - preprocess(input, output, dialect) - .expect("Failed to preprocess stack-graphs.tsg"); + preprocess(input, output, dialect).expect("Failed to preprocess stack-graphs.tsg"); } println!("cargo:rerun-if-changed={TSG_SOURCE}"); diff --git a/languages/tree-sitter-stack-graphs-typescript/rust/bin.rs b/languages/tree-sitter-stack-graphs-typescript/rust/bin.rs index d303f3a62..797e8cea7 100644 --- a/languages/tree-sitter-stack-graphs-typescript/rust/bin.rs +++ b/languages/tree-sitter-stack-graphs-typescript/rust/bin.rs @@ -12,17 +12,16 @@ use tree_sitter_stack_graphs::cli::provided_languages::Subcommands; use tree_sitter_stack_graphs::loader::{LanguageConfiguration, LoadError}; use tree_sitter_stack_graphs::NoCancellation; - /// Flag to select the dialect of the language - #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum)] - pub enum Dialect { - Typescript, - TSX, - } +/// Flag to select the dialect of the language +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum)] +pub enum Dialect { + Typescript, + TSX, +} fn main() -> anyhow::Result<()> { let cli = Cli::parse(); - let lc = match language_configuration(cli.dialect) - { + let lc = match language_configuration(cli.dialect) { Ok(lc) => lc, Err(err) => { eprintln!("{}", err.display_pretty()); @@ -35,8 +34,14 @@ fn main() -> anyhow::Result<()> { fn language_configuration<'a>(dialect: Dialect) -> Result> { match dialect { - Dialect::Typescript => tree_sitter_stack_graphs_typescript::try_language_configuration_typescript(&NoCancellation), - Dialect::TSX => tree_sitter_stack_graphs_typescript::try_language_configuration_tsx(&NoCancellation), + Dialect::Typescript => { + tree_sitter_stack_graphs_typescript::try_language_configuration_typescript( + &NoCancellation, + ) + } + Dialect::TSX => { + tree_sitter_stack_graphs_typescript::try_language_configuration_tsx(&NoCancellation) + } } } diff --git a/languages/tree-sitter-stack-graphs-typescript/rust/lib.rs b/languages/tree-sitter-stack-graphs-typescript/rust/lib.rs index 500bd29f0..1216de4a2 100644 --- a/languages/tree-sitter-stack-graphs-typescript/rust/lib.rs +++ b/languages/tree-sitter-stack-graphs-typescript/rust/lib.rs @@ -17,10 +17,12 @@ pub mod tsconfig; pub mod util; /// The stacks graphs tsg path for this language. -pub const STACK_GRAPHS_TSG_PATH: &str = "./stack-graphs.tsg"; +pub const STACK_GRAPHS_TSG_PATH: &str = "src/stack-graphs.tsg"; /// The stack graphs tsg source for this language -pub const STACK_GRAPHS_TSG_TS_SOURCE: &str = include_str!(concat!(env!("OUT_DIR"), "/stack-graphs-typescript.tsg")); -pub const STACK_GRAPHS_TSG_TSX_SOURCE: &str = include_str!(concat!(env!("OUT_DIR"), "/stack-graphs-tsx.tsg")); +const STACK_GRAPHS_TSG_TS_SOURCE: &str = + include_str!(concat!(env!("OUT_DIR"), "/stack-graphs-typescript.tsg")); +const STACK_GRAPHS_TSG_TSX_SOURCE: &str = + include_str!(concat!(env!("OUT_DIR"), "/stack-graphs-tsx.tsg")); /// The stack graphs builtins configuration for this language pub const STACK_GRAPHS_BUILTINS_CONFIG: &str = include_str!("../src/builtins.cfg"); @@ -34,7 +36,9 @@ pub const FILE_PATH_VAR: &str = "FILE_PATH"; /// The name of the project name global variable pub const PROJECT_NAME_VAR: &str = "PROJECT_NAME"; -pub fn language_configuration_typescript(cancellation_flag: &dyn CancellationFlag) -> LanguageConfiguration { +pub fn language_configuration_typescript( + cancellation_flag: &dyn CancellationFlag, +) -> LanguageConfiguration { try_language_configuration_typescript(cancellation_flag).unwrap_or_else(|err| panic!("{}", err)) } @@ -62,7 +66,9 @@ pub fn try_language_configuration_typescript( Ok(lc) } -pub fn language_configuration_tsx(cancellation_flag: &dyn CancellationFlag) -> LanguageConfiguration { +pub fn language_configuration_tsx( + cancellation_flag: &dyn CancellationFlag, +) -> LanguageConfiguration { try_language_configuration_tsx(cancellation_flag).unwrap_or_else(|err| panic!("{}", err)) } diff --git a/languages/tree-sitter-stack-graphs-typescript/rust/test.rs b/languages/tree-sitter-stack-graphs-typescript/rust/test.rs index 72d41d900..3ff155b6f 100644 --- a/languages/tree-sitter-stack-graphs-typescript/rust/test.rs +++ b/languages/tree-sitter-stack-graphs-typescript/rust/test.rs @@ -11,8 +11,9 @@ use tree_sitter_stack_graphs::ci::Tester; use tree_sitter_stack_graphs::NoCancellation; fn main() -> anyhow::Result<()> { - let lc = match tree_sitter_stack_graphs_typescript::try_language_configuration_typescript(&NoCancellation) - { + let lc = match tree_sitter_stack_graphs_typescript::try_language_configuration_typescript( + &NoCancellation, + ) { Ok(lc) => lc, Err(err) => { eprintln!("{}", err.display_pretty()); From 805bffdbf08c64ad29e1adda8b0fdf0388327f31 Mon Sep 17 00:00:00 2001 From: Eugene Yakubovich Date: Tue, 12 Mar 2024 11:39:45 -0700 Subject: [PATCH 233/291] Don't allow code mixed with directives on a line --- .../tree-sitter-stack-graphs-typescript/build.rs | 12 ++++++++++-- .../tree-sitter-stack-graphs-typescript/rust/lib.rs | 4 ++-- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/languages/tree-sitter-stack-graphs-typescript/build.rs b/languages/tree-sitter-stack-graphs-typescript/build.rs index 0c2d01c25..d7e71e72c 100644 --- a/languages/tree-sitter-stack-graphs-typescript/build.rs +++ b/languages/tree-sitter-stack-graphs-typescript/build.rs @@ -25,6 +25,8 @@ fn preprocess( // Matches: ; #end let directive_end = Regex::new(r";\s*#end").unwrap(); + let no_code = Regex::new(r"^[\s;]+").unwrap(); + let input = std::io::read_to_string(input)?; // If the filter is None or Some(true), the lines are written to the output @@ -35,20 +37,26 @@ fn preprocess( line_no += 1; if let Some(captures) = directive_start.captures(line) { + if !no_code.is_match(line) { + bail!("Line {line_no}: unexpected code before directive"); + } + let directive = captures.get(1).unwrap().as_str(); if !DIALECTS.contains(&directive) { bail!("Line {line_no}: unknown dialect: {directive}"); } filter = Some(dialect == directive); - output.write_all(line.as_bytes())?; } else if directive_end.is_match(line) { + if !no_code.is_match(line) { + bail!("Line {line_no}: unexpected code before directive end"); + } + if filter.is_none() { bail!("Line {line_no}: unmatched directive end"); } filter = None; - output.write_all(line.as_bytes())?; } else if filter.unwrap_or(true) { output.write_all(line.as_bytes())?; } diff --git a/languages/tree-sitter-stack-graphs-typescript/rust/lib.rs b/languages/tree-sitter-stack-graphs-typescript/rust/lib.rs index 1216de4a2..396fbc68b 100644 --- a/languages/tree-sitter-stack-graphs-typescript/rust/lib.rs +++ b/languages/tree-sitter-stack-graphs-typescript/rust/lib.rs @@ -19,9 +19,9 @@ pub mod util; /// The stacks graphs tsg path for this language. pub const STACK_GRAPHS_TSG_PATH: &str = "src/stack-graphs.tsg"; /// The stack graphs tsg source for this language -const STACK_GRAPHS_TSG_TS_SOURCE: &str = +pub const STACK_GRAPHS_TSG_TS_SOURCE: &str = include_str!(concat!(env!("OUT_DIR"), "/stack-graphs-typescript.tsg")); -const STACK_GRAPHS_TSG_TSX_SOURCE: &str = +pub const STACK_GRAPHS_TSG_TSX_SOURCE: &str = include_str!(concat!(env!("OUT_DIR"), "/stack-graphs-tsx.tsg")); /// The stack graphs builtins configuration for this language From b96c015ad67b63520093c83c25728183675ad0ef Mon Sep 17 00:00:00 2001 From: Eugene Yakubovich Date: Fri, 15 Mar 2024 10:23:46 -0700 Subject: [PATCH 234/291] Check for nested and unmatched directives - Error if one #dialect directive is nested in another - Error if #dialect is missing #end at the end of file --- languages/tree-sitter-stack-graphs-typescript/build.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/languages/tree-sitter-stack-graphs-typescript/build.rs b/languages/tree-sitter-stack-graphs-typescript/build.rs index d7e71e72c..31e3dd9de 100644 --- a/languages/tree-sitter-stack-graphs-typescript/build.rs +++ b/languages/tree-sitter-stack-graphs-typescript/build.rs @@ -41,6 +41,10 @@ fn preprocess( bail!("Line {line_no}: unexpected code before directive"); } + if filter.is_some() { + bail!("Line {line_no}: dialect directive cannot be nested"); + } + let directive = captures.get(1).unwrap().as_str(); if !DIALECTS.contains(&directive) { bail!("Line {line_no}: unknown dialect: {directive}"); @@ -64,6 +68,10 @@ fn preprocess( output.write(b"\n")?; } + if filter.is_some() { + bail!("Unmatched directive end at the end of the file"); + } + Ok(()) } From 1e1207237cab1ac72dc4fbad0a8c5085a6f66612 Mon Sep 17 00:00:00 2001 From: Eugene Yakubovich Date: Thu, 14 Mar 2024 11:39:57 -0700 Subject: [PATCH 235/291] Support JSX elements in TSX - Adds support for JSX elements: {expr} - The grammar uses nested_identifier for tags in the Foo.Bar.Baz form. It's the same symbol as what's used in namespaces: namespace Foo.Bar.Baz {} As such it ends up producing a bit of junk (unconnected cliques) but I don't know if it can be fixed w/o tree sitter queries supporting matching only if under a specified ancestor. - Unlike JavaScript, TypeScript requires variables to be declared. In JSX, {y = 2} is allowed if x, y are not in scope. They'll be created on first use. In TSX, this is disallowed which simplifies lexical_scope propogation compared to the JS TSG implementation. --- .../rust/test.rs | 23 ++- .../src/stack-graphs.tsg | 176 +++++++++++++++++- .../test/jsx/jsx_core.tsx | 39 ++++ .../test/jsx/jsx_fragment.tsx | 12 ++ .../test/jsx/jsx_namespace_name.tsx | 30 +++ .../test/jsx/jsx_self_closing_element.tsx | 37 ++++ .../test/jsx/jsx_text.tsx | 8 + 7 files changed, 311 insertions(+), 14 deletions(-) create mode 100644 languages/tree-sitter-stack-graphs-typescript/test/jsx/jsx_core.tsx create mode 100644 languages/tree-sitter-stack-graphs-typescript/test/jsx/jsx_fragment.tsx create mode 100644 languages/tree-sitter-stack-graphs-typescript/test/jsx/jsx_namespace_name.tsx create mode 100644 languages/tree-sitter-stack-graphs-typescript/test/jsx/jsx_self_closing_element.tsx create mode 100644 languages/tree-sitter-stack-graphs-typescript/test/jsx/jsx_text.tsx diff --git a/languages/tree-sitter-stack-graphs-typescript/rust/test.rs b/languages/tree-sitter-stack-graphs-typescript/rust/test.rs index 3ff155b6f..51ef9cdca 100644 --- a/languages/tree-sitter-stack-graphs-typescript/rust/test.rs +++ b/languages/tree-sitter-stack-graphs-typescript/rust/test.rs @@ -11,15 +11,18 @@ use tree_sitter_stack_graphs::ci::Tester; use tree_sitter_stack_graphs::NoCancellation; fn main() -> anyhow::Result<()> { - let lc = match tree_sitter_stack_graphs_typescript::try_language_configuration_typescript( - &NoCancellation, - ) { - Ok(lc) => lc, - Err(err) => { - eprintln!("{}", err.display_pretty()); - return Err(anyhow!("Language configuration error")); - } - }; + let lc_factories = [ + tree_sitter_stack_graphs_typescript::try_language_configuration_typescript, + tree_sitter_stack_graphs_typescript::try_language_configuration_tsx, + ]; + + let lcs = lc_factories + .iter() + .map(|lc_factory| lc_factory(&NoCancellation)) + .collect::, _>>() + .inspect_err(|err| eprintln!("{}", err.display_pretty())) + .map_err(|_| anyhow!("Language configuration error"))?; + let test_path = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("test"); - Tester::new(vec![lc], vec![test_path]).run() + Tester::new(lcs, vec![test_path]).run() } diff --git a/languages/tree-sitter-stack-graphs-typescript/src/stack-graphs.tsg b/languages/tree-sitter-stack-graphs-typescript/src/stack-graphs.tsg index 63c56760f..fa1cbf8f8 100644 --- a/languages/tree-sitter-stack-graphs-typescript/src/stack-graphs.tsg +++ b/languages/tree-sitter-stack-graphs-typescript/src/stack-graphs.tsg @@ -2667,6 +2667,13 @@ if none @is_async { (true) ; #dialect typescript (type_assertion) +; #end +; #dialect tsx + (jsx_element) + (jsx_self_closing_element) + (jsx_opening_element) + (jsx_closing_element) + (jsx_expression) ; #end (unary_expression) (undefined) @@ -2805,6 +2812,15 @@ if none @is_async { (decorator (call_expression function:(member_expression object:(identifier)@name))) (decorator (call_expression function:(member_expression object:(member_expression object:(identifier)@name)))) (decorator (call_expression function:(member_expression object:(member_expression object:(member_expression object:(identifier)@name))))) +; #dialect tsx + (nested_identifier (identifier)@name) + (nested_identifier (nested_identifier)@name) + (nested_type_identifier module:(nested_identifier)@name) + (internal_module name:(_)@name) + (jsx_opening_element name: (_)@name) + (jsx_closing_element name: (_)@name) + (jsx_self_closing_element name: (_)@name) +; #end ] { if none @is_def { node @name.cotype @@ -2852,6 +2868,12 @@ if none @is_def { (decorator (call_expression function:(member_expression object:(identifier)@name))) (decorator (call_expression function:(member_expression object:(member_expression object:(identifier)@name)))) (decorator (call_expression function:(member_expression object:(member_expression object:(member_expression object:(identifier)@name))))) +; #dialect tsx + (nested_identifier (identifier)@name (identifier)) ; to pick up foo in JSX: + (jsx_opening_element name: (identifier)@name) + (jsx_closing_element name: (identifier)@name) + (jsx_self_closing_element name: (identifier)@name) +; #end ] { if none @is_def { node @name.expr_ref @@ -3524,10 +3546,22 @@ if none @is_async { ; (member_expression (identifier) (property_identifier)) ; (subscript_expression (identifier) (string)) -(member_expression - object: (_)@object - property: (_)@prop -)@member_expr { +[ + (member_expression + object: (_)@object + property: (_)@prop + )@member_expr +; #dialect tsx + (nested_identifier + (nested_identifier)@object + (identifier)@prop + )@member_expr + (nested_identifier + (identifier)@object + (identifier)@prop + )@member_expr +; #end + ] { node @member_expr.member node @prop.expr_ref node @prop.expr_ref__typeof @@ -6160,3 +6194,137 @@ if none @is_acc { attr (@async.async_type__promise_ref__ns) push_symbol = "%T" edge @async.async_type__promise_ref__ns -> @async.lexical_scope } + +; +; # ##### # # +; # # # # # +; # # # # +; # ##### # +; # # # # # +; # # # # # # +; ##### ##### # # +; +; ;;;;;;;;;;;;;;;;;;;; +; #dialect tsx +; ;;;;;;;;;;;;;;;;;;;; + +(jsx_element + open_tag:(_)@open_tag + close_tag:(_)@close_tag)@jsx_element { + + edge @open_tag.lexical_scope -> @jsx_element.lexical_scope + edge @close_tag.lexical_scope -> @jsx_element.lexical_scope +} + +(jsx_element + [ + (jsx_text) + (jsx_element) + (jsx_fragment) + (jsx_self_closing_element) + (jsx_expression) + ]@child +)@parent { + edge @child.lexical_scope -> @parent.lexical_scope +} + +(jsx_fragment)@fragment { + node @fragment.lexical_scope + node @fragment.value + node @fragment.type +} + +(jsx_fragment + [ + (jsx_text) + (jsx_element) + (jsx_fragment) + (jsx_self_closing_element) + (jsx_expression) + ]@child +) @parent { + edge @child.lexical_scope -> @parent.lexical_scope +} + +(jsx_text)@jsx_text { + node @jsx_text.lexical_scope +} + +[ + (jsx_opening_element name: (_)@name)@element + (jsx_closing_element name: (_)@name)@element + (jsx_self_closing_element name: (_)@name)@element +] { + edge @name.lexical_scope -> @element.lexical_scope +} + +(jsx_opening_element + name:(_)@element_name + attribute:(_)@attr +) { + edge @attr.lexical_scope -> @element_name.lexical_scope +} + +(jsx_attribute (_) . (_)?@attr_value)@jsx_attribute { + node @jsx_attribute.lexical_scope + + if some @attr_value { + edge @attr_value.lexical_scope -> @jsx_attribute.lexical_scope + } +} + +(jsx_namespace_name (_) @lhs (_) @rhs)@name { + node @name.lexical_scope + + edge @lhs.lexical_scope -> @name.lexical_scope + edge @rhs.lexical_scope -> @name.lexical_scope +} + +(jsx_self_closing_element + name:(_)@element_name)@element { + edge @element_name.lexical_scope -> @element.lexical_scope +} + +(jsx_self_closing_element + name:(_)@element_name + attribute:(_)@attr +) { + edge @attr.lexical_scope -> @element_name.lexical_scope +} + +(jsx_expression (_)@child)@expr { + edge @child.lexical_scope -> @expr.lexical_scope +} + +(jsx_closing_element + name:(_)@element_name)@element +{ + edge @element_name.lexical_scope -> @element.lexical_scope +} + +[ + (jsx_opening_element + name:(identifier)@element_name) + (jsx_self_closing_element + name:(identifier)@element_name) + (jsx_closing_element + name:(identifier)@element_name) +] { + scan (source-text @element_name) { + ; standard HTML elements + "^(a|abbr|acronym|address|applet|area|article|aside|audio|b|base|basefont|bdi|bdo|big|blockquote|body|br|button|canvas|caption|center|cite|code|col|colgroup|data|datalist|dd|del|details|dfn|dialog|dir|div|dl|dt|em|embed|fieldset|figcaption|figure|font|footer|form|frame|frameset|h1|h2|h3|h4|h5|h6|head|header|hgroup|hr|html|i|iframe|input|ins|kbd|label|legend|li|link|main|map|mark|menu|meta|meter|nav|noframes|noscript|object|ol|optgroup|option|output|p|param|picture|pre|progress|q|rp|rt|ruby|s|samp|script|search|section|select|small|source|span|strike|strong|style|sub|summary|sup|svg|table|tbody|td|template|textarea|tfoot|th|thead|time|title|tr|track|tt|u|ul|var|video|wbr)$" { + ; do nothing! + } + + ; everything else + "^.+$" { + node element_name_pop + attr (element_name_pop) node_reference = @element_name + edge element_name_pop -> @element_name.lexical_scope + } + } +} + +; ;;;;;;;;;;;;;;;;;;;; +; #end +; ;;;;;;;;;;;;;;;;;;;; diff --git a/languages/tree-sitter-stack-graphs-typescript/test/jsx/jsx_core.tsx b/languages/tree-sitter-stack-graphs-typescript/test/jsx/jsx_core.tsx new file mode 100644 index 000000000..93285bc93 --- /dev/null +++ b/languages/tree-sitter-stack-graphs-typescript/test/jsx/jsx_core.tsx @@ -0,0 +1,39 @@ +// The core of JSX tests here verify the behavior of the following node types: +// jsx_element +// jsx_identifier +// jsx_attribute +// jsx_expression +// jsx_opening_element +// jsx_closing_element +// There is no real way to avoid testing all of these at once, +// and so we don't even try to. + +let x = 1; + +// Flow In + +const el = {x}; +// ^ defined: 11 +// ^ defined: 11 + +const el2 = +// ^ defined: 11 +// ^ defined: 11 + +let y = 0; +let z = 2; + +const el = +// ^ defined: 23 + {z = 3} +// ^ defined: 24 +; + +/**/ y; +// ^ defined: 23 + +/**/ z; +// ^ defined: 24 + +/**/ x; +// ^ defined: 11 diff --git a/languages/tree-sitter-stack-graphs-typescript/test/jsx/jsx_fragment.tsx b/languages/tree-sitter-stack-graphs-typescript/test/jsx/jsx_fragment.tsx new file mode 100644 index 000000000..1713694f7 --- /dev/null +++ b/languages/tree-sitter-stack-graphs-typescript/test/jsx/jsx_fragment.tsx @@ -0,0 +1,12 @@ +let x = 1; + +// Flow Around + +const el = <>; + +/**/ x; +// ^ defined: 1 + +// Children +({x}); +// ^ defined: 1 diff --git a/languages/tree-sitter-stack-graphs-typescript/test/jsx/jsx_namespace_name.tsx b/languages/tree-sitter-stack-graphs-typescript/test/jsx/jsx_namespace_name.tsx new file mode 100644 index 000000000..74fe0ff9b --- /dev/null +++ b/languages/tree-sitter-stack-graphs-typescript/test/jsx/jsx_namespace_name.tsx @@ -0,0 +1,30 @@ +let x = 1; + +// Flow Around +namespace noo.bar { + export let baz = 1; +} + +const el = ; +// ^ defined: 4 +// ^ defined: 4 +// ^ defined: 5 +// ^ defined: 4 +// ^ defined: 4 +// ^ defined: 5 + +/**/ x; +// ^ defined: 1 + +// Flow In + +let foo = { + bar: { + baz: 1 + } +}; + +const el2 = ; +// ^ defined: 21 +// ^ defined: 22 +// ^ defined: 23 diff --git a/languages/tree-sitter-stack-graphs-typescript/test/jsx/jsx_self_closing_element.tsx b/languages/tree-sitter-stack-graphs-typescript/test/jsx/jsx_self_closing_element.tsx new file mode 100644 index 000000000..d26746137 --- /dev/null +++ b/languages/tree-sitter-stack-graphs-typescript/test/jsx/jsx_self_closing_element.tsx @@ -0,0 +1,37 @@ +// The core of JSX tests here verify the behavior of the following node types: +// jsx_element +// jsx_identifier +// jsx_attribute +// jsx_expression +// jsx_opening_element +// jsx_closing_element +// There is no real way to avoid testing all of these at once, +// and so we don't even try to. + +let x = 1; + +// Flow In + +const el = ; +// ^ defined: 11 + +const el2 = +// ^ defined: 11 + +// Flow Out + +let y = 2; + +const el = ; +// ^ defined: 23 + +// Flow Across + +const el = ; +// ^ defined: 23 + +// Flow Around + +/**/ x; +// ^ defined: 11 diff --git a/languages/tree-sitter-stack-graphs-typescript/test/jsx/jsx_text.tsx b/languages/tree-sitter-stack-graphs-typescript/test/jsx/jsx_text.tsx new file mode 100644 index 000000000..0cc359765 --- /dev/null +++ b/languages/tree-sitter-stack-graphs-typescript/test/jsx/jsx_text.tsx @@ -0,0 +1,8 @@ +let x = 1; + +// Flow Around + +const el = bar; + +/**/ x; +// ^ defined: 1 \ No newline at end of file From 7db914c01b35ce024f6767e02dd1ad97022a6bc1 Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Wed, 10 Apr 2024 17:26:28 +0200 Subject: [PATCH 236/291] Skip failed files during indexing instead of aborting the whole index operation --- tree-sitter-stack-graphs/CHANGELOG.md | 8 ++++++++ tree-sitter-stack-graphs/Cargo.toml | 2 +- tree-sitter-stack-graphs/src/cli/index.rs | 18 +++++------------- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/tree-sitter-stack-graphs/CHANGELOG.md b/tree-sitter-stack-graphs/CHANGELOG.md index 2b907e682..f72fb402c 100644 --- a/tree-sitter-stack-graphs/CHANGELOG.md +++ b/tree-sitter-stack-graphs/CHANGELOG.md @@ -5,6 +5,14 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## v0.8.2 -- unreleased + +### CLI + +#### Changed + +- Failure to index a file will not abort indexing anymore, but simply mark the file as failed, as we already do for files with parse errors. + ## v0.8.1 -- 2024-03-06 The `stack-graphs` dependency was updated to `v0.13` to fix the build problems of the `v0.8.0` release. diff --git a/tree-sitter-stack-graphs/Cargo.toml b/tree-sitter-stack-graphs/Cargo.toml index 875faf4ef..d1b0fc1c6 100644 --- a/tree-sitter-stack-graphs/Cargo.toml +++ b/tree-sitter-stack-graphs/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "tree-sitter-stack-graphs" -version = "0.8.1" +version = "0.8.2" description = "Create stack graphs using tree-sitter parsers" homepage = "https://github.com/github/stack-graphs/tree/main/tree-sitter-stack-graphs" repository = "https://github.com/github/stack-graphs/" diff --git a/tree-sitter-stack-graphs/src/cli/index.rs b/tree-sitter-stack-graphs/src/cli/index.rs index 6d192e382..e8704f99d 100644 --- a/tree-sitter-stack-graphs/src/cli/index.rs +++ b/tree-sitter-stack-graphs/src/cli/index.rs @@ -355,23 +355,15 @@ impl<'a> Indexer<'a> { if let Err(err) = result { match err.inner { BuildError::Cancelled(_) => { - file_status.warning("parsing timed out", None); + file_status.warning("timed out", None); self.db - .store_error_for_file(source_path, &tag, "parsing timed out")?; - return Ok(()); - } - BuildError::ParseErrors { .. } => { - file_status.failure("parsing failed", Some(&err.display_pretty())); - self.db.store_error_for_file( - source_path, - &tag, - &format!("parsing failed: {}", err.inner), - )?; + .store_error_for_file(source_path, &tag, "timed out")?; return Ok(()); } _ => { - file_status.failure("failed to build stack graph", Some(&err.display_pretty())); - return Err(IndexError::StackGraph); + file_status.failure("failed", Some(&err.display_pretty())); + self.db.store_error_for_file(source_path, &tag, "failed")?; + return Ok(()); } } }; From 4ef3678a28f3521572653b38974999bf6b2778cf Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Mon, 22 Apr 2024 11:33:16 +0200 Subject: [PATCH 237/291] Add support for lambda parameters --- .../src/stack-graphs.tsg | 59 +++++++++++++------ .../test/lambdas.py | 2 + 2 files changed, 42 insertions(+), 19 deletions(-) create mode 100644 languages/tree-sitter-stack-graphs-python/test/lambdas.py diff --git a/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg b/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg index 48353f14a..f9798fe54 100644 --- a/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg +++ b/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg @@ -759,32 +759,39 @@ inherit .parent_module (with_clause) {} -(function_definition - name: (identifier) @name) { - attr (@name.def) node_definition = @name -} - -(function_definition - name: (identifier) @name - parameters: (parameters) @params - body: (block) @body) @func -{ - node call - node drop_scope +[ + (function_definition + parameters: (_) @params + body: (_) @body + ) @func + (lambda + parameters: (_) @params + body: (_) @body + )@func +] { + node @func.call node return_value + node drop_scope - attr (@name.def) definiens_node = @func - edge @func.after_scope -> @name.def - edge @name.def -> call - edge call -> return_value + edge @func.call -> return_value edge @body.before_scope -> @params.after_scope edge @body.before_scope -> drop_scope edge drop_scope -> @func.bottom attr (drop_scope) type = "drop_scopes" - attr (call) pop_scoped_symbol = "()" + attr (@func.call) pop_scoped_symbol = "()" edge @params.before_scope -> JUMP_TO_SCOPE_NODE attr (return_value) is_exported let @func.function_returns = return_value +} + +(function_definition + name: (identifier) @name + body: (_) @body +) @func { + attr (@name.def) node_definition = @name + attr (@name.def) definiens_node = @func + edge @func.after_scope -> @name.def + edge @name.def -> @func.call ; Prevent functions defined inside of method bodies from being treated like methods let @body.class_self_scope = #null @@ -802,8 +809,10 @@ inherit .parent_module attr (@param.output) push_node = @param } -(parameters - (_) @param) @params +[ + (parameters (_) @param) @params + (lambda_parameters (_) @param) @params +] { node @param.param_index node @param.param_name @@ -954,6 +963,18 @@ inherit .parent_module ;; ;; Expressions +(lambda + body: (_) @body +)@lam { + ;; TODO Unify .before_scope, .local_scope, and .input to simplify + ;; uniform treatment of lambdas and function definitions. + node @body.before_scope + let @body.local_scope = @body.before_scope + edge @body.input -> @body.before_scope + + edge @lam.output -> @lam.call +} + (conditional_expression) {} (named_expression) {} diff --git a/languages/tree-sitter-stack-graphs-python/test/lambdas.py b/languages/tree-sitter-stack-graphs-python/test/lambdas.py new file mode 100644 index 000000000..9812df4de --- /dev/null +++ b/languages/tree-sitter-stack-graphs-python/test/lambdas.py @@ -0,0 +1,2 @@ +sorted([1, 2, 3], key=lambda x: x) +# ^ defined: 1 From f7e8984e70350160b1019ac3c12f68bcfe823580 Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Tue, 23 Apr 2024 13:47:25 +0200 Subject: [PATCH 238/291] Cache partial paths for builtins when running tests --- stack-graphs/src/graph.rs | 8 ++- tree-sitter-stack-graphs/src/cli/test.rs | 90 +++++++++++++++++++----- 2 files changed, 77 insertions(+), 21 deletions(-) diff --git a/stack-graphs/src/graph.rs b/stack-graphs/src/graph.rs index 84748c6bf..686344ee5 100644 --- a/stack-graphs/src/graph.rs +++ b/stack-graphs/src/graph.rs @@ -1497,12 +1497,16 @@ impl StackGraph { /// Copies the given stack graph into this stack graph. Panics if any of the files /// in the other stack graph are already defined in the current one. - pub fn add_from_graph(&mut self, other: &StackGraph) -> Result<(), Handle> { + pub fn add_from_graph( + &mut self, + other: &StackGraph, + ) -> Result>, Handle> { let mut files = HashMap::new(); for other_file in other.iter_files() { let file = self.add_file(other[other_file].name())?; files.insert(other_file, file); } + let files = files; let node_id = |other_node_id: NodeID| { if other_node_id.is_root() { NodeID::root() @@ -1645,7 +1649,7 @@ impl StackGraph { } } } - Ok(()) + Ok(files.into_values().collect()) } } diff --git a/tree-sitter-stack-graphs/src/cli/test.rs b/tree-sitter-stack-graphs/src/cli/test.rs index 780d478f6..c7f73e7ce 100644 --- a/tree-sitter-stack-graphs/src/cli/test.rs +++ b/tree-sitter-stack-graphs/src/cli/test.rs @@ -19,9 +19,13 @@ use stack_graphs::stitching::Database; use stack_graphs::stitching::DatabaseCandidates; use stack_graphs::stitching::ForwardPartialPathStitcher; use stack_graphs::stitching::StitcherConfig; +use std::collections::hash_map::Entry; +use std::collections::HashMap; +use std::collections::HashSet; use std::path::Path; use std::path::PathBuf; use std::time::Duration; +use tree_sitter::Language; use tree_sitter_graph::Variables; use crate::cli::util::duration_from_seconds_str; @@ -176,10 +180,16 @@ impl TestArgs { pub fn run(self, mut loader: Loader) -> anyhow::Result<()> { let reporter = self.get_reporter(); let mut total_result = TestResult::new(); + let mut cache = HashMap::new(); for (test_root, test_path, _) in iter_files_and_directories(self.test_paths.clone()) { let mut file_status = CLIFileReporter::new(&reporter, &test_path); - let test_result = - self.run_test(&test_root, &test_path, &mut loader, &mut file_status)?; + let test_result = self.run_test( + &test_root, + &test_path, + &mut loader, + &mut file_status, + &mut cache, + )?; file_status.assert_reported(); total_result.absorb(test_result); } @@ -211,14 +221,15 @@ impl TestArgs { } /// Run test file. Takes care of the output when an error is returned. - fn run_test( + fn run_test<'a>( &self, test_root: &Path, test_path: &Path, - loader: &mut Loader, + loader: &'a mut Loader, file_status: &mut CLIFileReporter, + cache: &mut HashMap, ) -> anyhow::Result { - match self.run_test_inner(test_root, test_path, loader, file_status) { + match self.run_test_inner(test_root, test_path, loader, file_status, cache) { ok @ Ok(_) => ok, err @ Err(_) => { file_status.failure_if_processing("error", None); @@ -227,12 +238,13 @@ impl TestArgs { } } - fn run_test_inner( + fn run_test_inner<'a>( &self, test_root: &Path, test_path: &Path, - loader: &mut Loader, + loader: &'a mut Loader, file_status: &mut CLIFileReporter, + cache: &mut HashMap, ) -> anyhow::Result { let cancellation_flag = CancelAfterDuration::from_option(self.max_test_time); @@ -263,11 +275,24 @@ impl TestArgs { file_status.processing(); + let stitcher_config = + StitcherConfig::default().with_detect_similar_paths(!lc.no_similar_paths_in_file); + let mut partials = PartialPaths::new(); + let mut db = Database::new(); + let source = file_reader.get(test_path)?; let default_fragment_path = test_path.strip_prefix(test_root).unwrap(); let mut test = Test::from_source(test_path, source, default_fragment_path)?; if !self.no_builtins { - self.load_builtins_into(&lc, &mut test.graph)?; + self.load_builtins_into( + &lc, + &mut test.graph, + &mut partials, + &mut db, + stitcher_config, + cancellation_flag.as_ref(), + cache, + )?; } let mut globals = Variables::new(); for test_fragment in &test.fragments { @@ -325,15 +350,11 @@ impl TestArgs { Ok(_) => {} } } - let stitcher_config = - StitcherConfig::default().with_detect_similar_paths(!lc.no_similar_paths_in_file); - let mut partials = PartialPaths::new(); - let mut db = Database::new(); - for file in test.graph.iter_files() { + for fragment in &test.fragments { ForwardPartialPathStitcher::find_minimal_partial_path_set_in_file( &test.graph, &mut partials, - file, + fragment.file, stitcher_config, &cancellation_flag.as_ref(), |g, ps, p| { @@ -387,14 +408,45 @@ impl TestArgs { Ok(result) } - fn load_builtins_into( + fn load_builtins_into<'a>( &self, - lc: &LanguageConfiguration, + lc: &'a LanguageConfiguration, graph: &mut StackGraph, + partials: &mut PartialPaths, + db: &mut Database, + stitcher_config: StitcherConfig, + cancellation_flag: &dyn CancellationFlag, + cache: &mut HashMap, ) -> anyhow::Result<()> { - if let Err(h) = graph.add_from_graph(&lc.builtins) { - return Err(anyhow!("Duplicate builtin file {}", &graph[h])); - } + let files = graph + .add_from_graph(&lc.builtins) + .map_err(|h| anyhow!("Duplicate builtin file {}", &graph[h]))?; + let files = files.into_iter().collect::>(); + match cache.entry(lc.language) { + Entry::Occupied(o) => { + o.get().load_into(graph, partials, db)?; + } + Entry::Vacant(v) => { + for file in &files { + ForwardPartialPathStitcher::find_minimal_partial_path_set_in_file( + graph, + partials, + *file, + stitcher_config, + &cancellation_flag, + |g, ps, p| { + db.add_partial_path(g, ps, p.clone()); + }, + )?; + } + v.insert(db.to_serializable_filter( + graph, + partials, + &|_: &StackGraph, f: &Handle| files.contains(f), + )); + } + }; + Ok(()) } From 6499341a531111afe10489e8cf0fe8495b353e9b Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Tue, 23 Apr 2024 13:55:37 +0200 Subject: [PATCH 239/291] Update changelog --- tree-sitter-stack-graphs/CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tree-sitter-stack-graphs/CHANGELOG.md b/tree-sitter-stack-graphs/CHANGELOG.md index f72fb402c..e7538592a 100644 --- a/tree-sitter-stack-graphs/CHANGELOG.md +++ b/tree-sitter-stack-graphs/CHANGELOG.md @@ -9,6 +9,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### CLI +#### Added + +- Tests run faster for languages with builtins sources by caching the partial paths for the builtins. + #### Changed - Failure to index a file will not abort indexing anymore, but simply mark the file as failed, as we already do for files with parse errors. From b533b31add3953f851baba12983a4bccf1c1d47d Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Wed, 15 May 2024 12:04:21 +0200 Subject: [PATCH 240/291] Always enable both TS & TSX in CLI --- .../rust/bin.rs | 54 ++++++------------- 1 file changed, 16 insertions(+), 38 deletions(-) diff --git a/languages/tree-sitter-stack-graphs-typescript/rust/bin.rs b/languages/tree-sitter-stack-graphs-typescript/rust/bin.rs index 797e8cea7..ea7713595 100644 --- a/languages/tree-sitter-stack-graphs-typescript/rust/bin.rs +++ b/languages/tree-sitter-stack-graphs-typescript/rust/bin.rs @@ -6,56 +6,34 @@ // ------------------------------------------------------------------------------------------------ use anyhow::anyhow; -use clap::{Parser, ValueEnum}; +use clap::Parser; use tree_sitter_stack_graphs::cli::database::default_user_database_path_for_crate; use tree_sitter_stack_graphs::cli::provided_languages::Subcommands; -use tree_sitter_stack_graphs::loader::{LanguageConfiguration, LoadError}; use tree_sitter_stack_graphs::NoCancellation; -/// Flag to select the dialect of the language -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum)] -pub enum Dialect { - Typescript, - TSX, -} - fn main() -> anyhow::Result<()> { let cli = Cli::parse(); - let lc = match language_configuration(cli.dialect) { - Ok(lc) => lc, - Err(err) => { - eprintln!("{}", err.display_pretty()); - return Err(anyhow!("Language configuration error")); - } - }; - let default_db_path = default_user_database_path_for_crate(env!("CARGO_PKG_NAME"))?; - cli.subcommand.run(default_db_path, vec![lc]) -} - -fn language_configuration<'a>(dialect: Dialect) -> Result> { - match dialect { - Dialect::Typescript => { - tree_sitter_stack_graphs_typescript::try_language_configuration_typescript( - &NoCancellation, - ) - } - Dialect::TSX => { - tree_sitter_stack_graphs_typescript::try_language_configuration_tsx(&NoCancellation) - } + let mut lcs = Vec::new(); + for r in [ + tree_sitter_stack_graphs_typescript::try_language_configuration_typescript(&NoCancellation), + tree_sitter_stack_graphs_typescript::try_language_configuration_tsx(&NoCancellation), + ] { + let lc = match r { + Ok(lc) => lc, + Err(err) => { + eprintln!("{}", err.display_pretty()); + return Err(anyhow!("Language configuration error")); + } + }; + lcs.push(lc); } + let default_db_path = default_user_database_path_for_crate(env!("CARGO_PKG_NAME"))?; + cli.subcommand.run(default_db_path, lcs) } #[derive(Parser)] #[clap(about, version)] pub struct Cli { - #[clap( - short, - long, - value_enum, - default_value_t = Dialect::Typescript, - )] - dialect: Dialect, - #[clap(subcommand)] subcommand: Subcommands, } From 36f83ea6b262ee871bf4f7b64a9dcbabfd98e3f7 Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Wed, 15 May 2024 12:14:44 +0200 Subject: [PATCH 241/291] Update version and changelog --- languages/tree-sitter-stack-graphs-typescript/CHANGELOG.md | 6 ++++++ languages/tree-sitter-stack-graphs-typescript/Cargo.toml | 6 +++--- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/languages/tree-sitter-stack-graphs-typescript/CHANGELOG.md b/languages/tree-sitter-stack-graphs-typescript/CHANGELOG.md index a23db2f0e..b4260b2a6 100644 --- a/languages/tree-sitter-stack-graphs-typescript/CHANGELOG.md +++ b/languages/tree-sitter-stack-graphs-typescript/CHANGELOG.md @@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## v0.3.0 -- unreleased + +### Added + +- Support for TSX. A new language configuration for TSX is available, and TSX is enabled in the CLI next to TypeScript. + ## v0.2.0 -- 2024-03-06 The `tree-sitter-stack-graphs` is updated to `v0.8`. diff --git a/languages/tree-sitter-stack-graphs-typescript/Cargo.toml b/languages/tree-sitter-stack-graphs-typescript/Cargo.toml index 46360f19c..e40c12625 100644 --- a/languages/tree-sitter-stack-graphs-typescript/Cargo.toml +++ b/languages/tree-sitter-stack-graphs-typescript/Cargo.toml @@ -1,9 +1,9 @@ [package] name = "tree-sitter-stack-graphs-typescript" -version = "0.2.0" -description = "Stack graphs definition for TypeScript using tree-sitter-typescript" +version = "0.3.0" +description = "Stack graphs definition for TypeScript & TSX using tree-sitter-typescript" readme = "README.md" -keywords = ["tree-sitter", "stack-graphs", "typescript"] +keywords = ["tree-sitter", "stack-graphs", "typescript", "tsx"] authors = ["Hendrik van Antwerpen "] license = "MIT OR Apache-2.0" edition = "2018" From 3f1ebc2f2e14d40e5d358848570e4863f704870d Mon Sep 17 00:00:00 2001 From: nohehf Date: Thu, 23 May 2024 10:20:25 +0200 Subject: [PATCH 242/291] test: add nested functions --- .../test/nested_functions.py | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 languages/tree-sitter-stack-graphs-python/test/nested_functions.py diff --git a/languages/tree-sitter-stack-graphs-python/test/nested_functions.py b/languages/tree-sitter-stack-graphs-python/test/nested_functions.py new file mode 100644 index 000000000..0c6e6666e --- /dev/null +++ b/languages/tree-sitter-stack-graphs-python/test/nested_functions.py @@ -0,0 +1,11 @@ +def outer(a): + def inner(b, c): + pass + + inner(1) + # ^ defined: 2 + + +class A: + def method(a): + pass From 3dfe218ef25cd2eb79bf7d8faece8d0f7d36325c Mon Sep 17 00:00:00 2001 From: nohehf Date: Thu, 23 May 2024 10:23:11 +0200 Subject: [PATCH 243/291] test: nested function test --- .../test/nested_functions.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/languages/tree-sitter-stack-graphs-python/test/nested_functions.py b/languages/tree-sitter-stack-graphs-python/test/nested_functions.py index 0c6e6666e..db655e3a3 100644 --- a/languages/tree-sitter-stack-graphs-python/test/nested_functions.py +++ b/languages/tree-sitter-stack-graphs-python/test/nested_functions.py @@ -1,11 +1,6 @@ def outer(a): - def inner(b, c): + def inner(b): pass inner(1) # ^ defined: 2 - - -class A: - def method(a): - pass From 5164f4b19f7d35097c9ed43dc57de01518647c88 Mon Sep 17 00:00:00 2001 From: nohehf Date: Thu, 23 May 2024 11:11:51 +0200 Subject: [PATCH 244/291] test: not a method --- .../test/not_a_method.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 languages/tree-sitter-stack-graphs-python/test/not_a_method.py diff --git a/languages/tree-sitter-stack-graphs-python/test/not_a_method.py b/languages/tree-sitter-stack-graphs-python/test/not_a_method.py new file mode 100644 index 000000000..c58e9af1e --- /dev/null +++ b/languages/tree-sitter-stack-graphs-python/test/not_a_method.py @@ -0,0 +1,13 @@ +class Foo: + a = 1 + + # Self can be named anything + def first_method(actually_self): + return actually_self.a + # ^ defined: 2 + + def second_method(self): + # First argument here is not self + def not_a_method(not_self): + return not_self.a + # ^ defined: From b020128c0e0c487ccac8e9c12f68a989212ef910 Mon Sep 17 00:00:00 2001 From: nohehf Date: Thu, 23 May 2024 21:52:50 +0200 Subject: [PATCH 245/291] fix: method self --- .../tree-sitter-stack-graphs-python/query | 12 +++++++++ .../src/stack-graphs.tsg | 26 +++++++++++++------ .../test/not_a_method.py | 15 ++++++++--- .../test/self.py | 11 ++++++++ 4 files changed, 52 insertions(+), 12 deletions(-) create mode 100644 languages/tree-sitter-stack-graphs-python/query create mode 100644 languages/tree-sitter-stack-graphs-python/test/self.py diff --git a/languages/tree-sitter-stack-graphs-python/query b/languages/tree-sitter-stack-graphs-python/query new file mode 100644 index 000000000..546c1d8d2 --- /dev/null +++ b/languages/tree-sitter-stack-graphs-python/query @@ -0,0 +1,12 @@ +(class_definition + body: ( + block + (function_definition + parameters: + (parameters + . (identifier) @first_param + ) + body: (block) @method_body + ) + ) +) \ No newline at end of file diff --git a/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg b/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg index f9798fe54..b3973b6ae 100644 --- a/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg +++ b/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg @@ -794,19 +794,29 @@ inherit .parent_module edge @name.def -> @func.call ; Prevent functions defined inside of method bodies from being treated like methods + ; There might be a bug here as calling .self_scope after this, will not fallback to the larger scope let @body.class_self_scope = #null let @body.class_member_attr_scope = #null } -(function_definition - parameters: (parameters - . (identifier) @param) - body: (block) @body) +; method definition +(class_definition + body: ( + block + (function_definition + parameters: + (parameters + . (identifier) @first_param + ) + body: (block) @method_body + ) + ) +) { - edge @param.def -> @param.class_self_scope - edge @param.class_member_attr_scope -> @param.output - edge @param.output -> @body.after_scope - attr (@param.output) push_node = @param + edge @first_param.def -> @first_param.class_self_scope + edge @first_param.class_member_attr_scope -> @first_param.output + edge @first_param.output -> @method_body.after_scope + attr (@first_param.output) push_node = @first_param } [ diff --git a/languages/tree-sitter-stack-graphs-python/test/not_a_method.py b/languages/tree-sitter-stack-graphs-python/test/not_a_method.py index c58e9af1e..f0a207eb0 100644 --- a/languages/tree-sitter-stack-graphs-python/test/not_a_method.py +++ b/languages/tree-sitter-stack-graphs-python/test/not_a_method.py @@ -1,13 +1,20 @@ class Foo: a = 1 - # Self can be named anything - def first_method(actually_self): - return actually_self.a - # ^ defined: 2 + def first_method(self): + self.a + # ^ defined: 2 def second_method(self): + self.a + # ^ defined: 2 + # First argument here is not self def not_a_method(not_self): return not_self.a # ^ defined: + + +def function(self): + self.a + # ^ defined: diff --git a/languages/tree-sitter-stack-graphs-python/test/self.py b/languages/tree-sitter-stack-graphs-python/test/self.py new file mode 100644 index 000000000..763a52eb6 --- /dev/null +++ b/languages/tree-sitter-stack-graphs-python/test/self.py @@ -0,0 +1,11 @@ +class Foo: + a = 1 + + def mathod_1(self): + return self.a + # ^ defined: 2 + + # Self can be named anything + def method_2(actually_self): + return actually_self.a + # ^ defined: 2 From 9551ad995c79277d950ac2e0d5d9a2d5e42631e2 Mon Sep 17 00:00:00 2001 From: nohehf Date: Thu, 23 May 2024 21:59:18 +0200 Subject: [PATCH 246/291] chore: remove unused test tree sitter query --- languages/tree-sitter-stack-graphs-python/query | 12 ------------ 1 file changed, 12 deletions(-) delete mode 100644 languages/tree-sitter-stack-graphs-python/query diff --git a/languages/tree-sitter-stack-graphs-python/query b/languages/tree-sitter-stack-graphs-python/query deleted file mode 100644 index 546c1d8d2..000000000 --- a/languages/tree-sitter-stack-graphs-python/query +++ /dev/null @@ -1,12 +0,0 @@ -(class_definition - body: ( - block - (function_definition - parameters: - (parameters - . (identifier) @first_param - ) - body: (block) @method_body - ) - ) -) \ No newline at end of file From 3d283157bc40442d5b9f12e21ef13342ccf23f35 Mon Sep 17 00:00:00 2001 From: nohehf Date: Mon, 27 May 2024 09:47:41 +0200 Subject: [PATCH 247/291] fix: ignore .ts and .js extensions form imports --- .../src/stack-graphs.tsg | 6 ++++-- .../test/modules/import-extension-js.ts | 6 ++++++ .../test/modules/import-extension-none.ts | 6 ++++++ .../test/modules/import-extension-ts.ts | 6 ++++++ 4 files changed, 22 insertions(+), 2 deletions(-) create mode 100644 languages/tree-sitter-stack-graphs-typescript/test/modules/import-extension-js.ts create mode 100644 languages/tree-sitter-stack-graphs-typescript/test/modules/import-extension-none.ts create mode 100644 languages/tree-sitter-stack-graphs-typescript/test/modules/import-extension-ts.ts diff --git a/languages/tree-sitter-stack-graphs-typescript/src/stack-graphs.tsg b/languages/tree-sitter-stack-graphs-typescript/src/stack-graphs.tsg index fa1cbf8f8..a51a80c07 100644 --- a/languages/tree-sitter-stack-graphs-typescript/src/stack-graphs.tsg +++ b/languages/tree-sitter-stack-graphs-typescript/src/stack-graphs.tsg @@ -260,7 +260,7 @@ attribute node_symbol = node => symbol = (source-text node), source_n ; expose globals edge proj_scope -> @prog.globals - + var mod_scope = proj_scope if (not (is-empty @imexs)) { ; module definition @@ -541,7 +541,9 @@ attribute node_symbol = node => symbol = (source-text node), source_n ; module reference var mod_scope = mod_ref__ns scan (path-normalize mod_path) { - "([^/]+)/?" { + ; ignore .js and .ts extensions on imports, as ts allows this + ; this might lead to false positives + "([^/|(\.j|ts)]+)/?" { node mod_ref attr (mod_ref) push_symbol = $1 edge mod_ref -> mod_scope diff --git a/languages/tree-sitter-stack-graphs-typescript/test/modules/import-extension-js.ts b/languages/tree-sitter-stack-graphs-typescript/test/modules/import-extension-js.ts new file mode 100644 index 000000000..92817fbaf --- /dev/null +++ b/languages/tree-sitter-stack-graphs-typescript/test/modules/import-extension-js.ts @@ -0,0 +1,6 @@ +/* --- path: src/foo.ts --- */ +export const bar = 42; + +/* --- path: src/index.ts --- */ +import { bar } from "./foo.js"; +// ^ defined: 2 diff --git a/languages/tree-sitter-stack-graphs-typescript/test/modules/import-extension-none.ts b/languages/tree-sitter-stack-graphs-typescript/test/modules/import-extension-none.ts new file mode 100644 index 000000000..f221684b4 --- /dev/null +++ b/languages/tree-sitter-stack-graphs-typescript/test/modules/import-extension-none.ts @@ -0,0 +1,6 @@ +/* --- path: src/foo.ts --- */ +export const bar = 42; + +/* --- path: src/index.ts --- */ +import { bar } from "./foo"; +// ^ defined: 2 diff --git a/languages/tree-sitter-stack-graphs-typescript/test/modules/import-extension-ts.ts b/languages/tree-sitter-stack-graphs-typescript/test/modules/import-extension-ts.ts new file mode 100644 index 000000000..1e46349c9 --- /dev/null +++ b/languages/tree-sitter-stack-graphs-typescript/test/modules/import-extension-ts.ts @@ -0,0 +1,6 @@ +/* --- path: src/foo.ts --- */ +export const bar = 42; + +/* --- path: src/index.ts --- */ +import { bar } from "./foo.ts"; +// ^ defined: 2 From 7509f09fdbd55267c9b933c25ce04f6a8fe55581 Mon Sep 17 00:00:00 2001 From: nohehf <64989428+nohehf@users.noreply.github.com> Date: Mon, 27 May 2024 14:17:55 +0200 Subject: [PATCH 248/291] chore: fmt Co-authored-by: Hendrik van Antwerpen --- .../tree-sitter-stack-graphs-python/src/stack-graphs.tsg | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg b/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg index b3973b6ae..751e2a3db 100644 --- a/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg +++ b/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg @@ -801,13 +801,10 @@ inherit .parent_module ; method definition (class_definition - body: ( - block + body: (block (function_definition parameters: - (parameters - . (identifier) @first_param - ) + (parameters . (identifier) @first_param) body: (block) @method_body ) ) From d0b940e66c0e0c2d83533cc743d30142e4728585 Mon Sep 17 00:00:00 2001 From: nohehf Date: Mon, 27 May 2024 15:46:23 +0200 Subject: [PATCH 249/291] fix: add auxiliary function to remove file ext --- .../src/stack-graphs.tsg | 9 ++--- tree-sitter-stack-graphs/src/functions.rs | 37 +++++++++++++++++++ 2 files changed, 41 insertions(+), 5 deletions(-) diff --git a/languages/tree-sitter-stack-graphs-typescript/src/stack-graphs.tsg b/languages/tree-sitter-stack-graphs-typescript/src/stack-graphs.tsg index a51a80c07..7e6359280 100644 --- a/languages/tree-sitter-stack-graphs-typescript/src/stack-graphs.tsg +++ b/languages/tree-sitter-stack-graphs-typescript/src/stack-graphs.tsg @@ -260,7 +260,7 @@ attribute node_symbol = node => symbol = (source-text node), source_n ; expose globals edge proj_scope -> @prog.globals - + var mod_scope = proj_scope if (not (is-empty @imexs)) { ; module definition @@ -540,10 +540,9 @@ attribute node_symbol = node => symbol = (source-text node), source_n ; module reference var mod_scope = mod_ref__ns - scan (path-normalize mod_path) { - ; ignore .js and .ts extensions on imports, as ts allows this - ; this might lead to false positives - "([^/|(\.j|ts)]+)/?" { + ; normalize path and remove the extension as we want to match 'foo', 'foo.js', 'foo.ts', etc. + scan (path-remove-ext (path-normalize mod_path)) { + "([^/]+)/?" { node mod_ref attr (mod_ref) push_symbol = $1 edge mod_ref -> mod_scope diff --git a/tree-sitter-stack-graphs/src/functions.rs b/tree-sitter-stack-graphs/src/functions.rs index 9082ba427..e1f9e94e5 100644 --- a/tree-sitter-stack-graphs/src/functions.rs +++ b/tree-sitter-stack-graphs/src/functions.rs @@ -43,6 +43,10 @@ pub mod path { path_fn(|p| normalize(p).map(|p| p.as_os_str().to_os_string())), ); functions.add("path-split".into(), PathSplit); + functions.add( + "path-remove-ext".into(), + path_fn(|p| remove_extension(p).map(|p| p.as_os_str().to_os_string())), + ); } pub fn path_fn(f: F) -> impl Function @@ -159,4 +163,37 @@ pub mod path { } Some(ret) } + + /// Removes the extension from a path. + /// eg. `foo/bar.rs` -> `foo/bar` + /// eg. `foo/bar` -> `foo/bar` + /// eg. `foo/bar.rs.bak` -> `foo/bar.rs` + pub fn remove_extension(path: &Path) -> Option { + path.extension() + .map_or(Some(path.into()), |_| path.with_extension("").into()) + } + + #[test] + fn test_remove_extension() { + assert_eq!( + remove_extension(Path::new("foo/bar.rs")), + Some(PathBuf::from("foo/bar")) + ); + assert_eq!( + remove_extension(Path::new("foo/bar")), + Some(PathBuf::from("foo/bar")) + ); + assert_eq!( + remove_extension(Path::new("foo/bar.rs.bak")), + Some(PathBuf::from("foo/bar.rs")) + ); + assert_eq!( + remove_extension(Path::new("foo")), + Some(PathBuf::from("foo")) + ); + assert_eq!( + remove_extension(Path::new("foo.rs")), + Some(PathBuf::from("foo")) + ); + } } From b049439a056a5bda754c6fac931b7ea686143fe2 Mon Sep 17 00:00:00 2001 From: nohehf Date: Mon, 27 May 2024 15:51:15 +0200 Subject: [PATCH 250/291] chore: remove unrelated comment --- languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg | 1 - 1 file changed, 1 deletion(-) diff --git a/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg b/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg index 751e2a3db..6b5efd35f 100644 --- a/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg +++ b/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg @@ -794,7 +794,6 @@ inherit .parent_module edge @name.def -> @func.call ; Prevent functions defined inside of method bodies from being treated like methods - ; There might be a bug here as calling .self_scope after this, will not fallback to the larger scope let @body.class_self_scope = #null let @body.class_member_attr_scope = #null } From 7621ac28eb34a0c32a7d6cbb5e485f8f306eeea9 Mon Sep 17 00:00:00 2001 From: nohehf Date: Tue, 28 May 2024 10:47:08 +0200 Subject: [PATCH 251/291] feat: add root path and pass file path explicitely --- .../src/stack-graphs.tsg | 5 ++- .../test/root_path.py | 21 ++++++++++++ tree-sitter-stack-graphs/src/cli/index.rs | 11 ++++++- tree-sitter-stack-graphs/src/cli/test.rs | 2 ++ tree-sitter-stack-graphs/src/lib.rs | 27 +++++++++++++-- tree-sitter-stack-graphs/src/loader.rs | 33 ++++++++++++++----- tree-sitter-stack-graphs/src/test.rs | 4 +++ tree-sitter-stack-graphs/tests/it/builder.rs | 27 ++++++++++++--- tree-sitter-stack-graphs/tests/it/main.rs | 17 ++++++++-- tree-sitter-stack-graphs/tests/it/test.rs | 14 +++++++- 10 files changed, 140 insertions(+), 21 deletions(-) create mode 100644 languages/tree-sitter-stack-graphs-python/test/root_path.py diff --git a/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg b/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg index 6b5efd35f..887e171b3 100644 --- a/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg +++ b/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg @@ -13,6 +13,7 @@ ;; ^^^^^^^^^^^^^^^^ global FILE_PATH +global ROOT_PATH global ROOT_NODE global JUMP_TO_SCOPE_NODE @@ -272,7 +273,9 @@ inherit .parent_module node grandparent_module_ref_node var grandparent_module_ref = grandparent_module_ref_node - scan FILE_PATH { + ; get the file path relative to the root path + let rel_path = (replace FILE_PATH ROOT_PATH "") + scan rel_path { "([^/]+)/" { node def_dot diff --git a/languages/tree-sitter-stack-graphs-python/test/root_path.py b/languages/tree-sitter-stack-graphs-python/test/root_path.py new file mode 100644 index 000000000..23937db7c --- /dev/null +++ b/languages/tree-sitter-stack-graphs-python/test/root_path.py @@ -0,0 +1,21 @@ +# ------ path: foo/bar/module.py -----------# +# ------ global: ROOT_PATH=foo/bar -----------# + +foo = 42 + +# ------ path: foo/bar/baz/module.py -----------# +# ------ global: ROOT_PATH=foo/bar -----------# + +bar = "hello" + +# ------ path: foo/bar/main.py -------------# +# ------ global: ROOT_PATH=foo/bar -----------# + +from module import foo +from baz.module import bar + +print(foo) +# ^ defined: 4, 14 + +print(bar) +# ^ defined: 9, 15 diff --git a/tree-sitter-stack-graphs/src/cli/index.rs b/tree-sitter-stack-graphs/src/cli/index.rs index e8704f99d..5bf48f54f 100644 --- a/tree-sitter-stack-graphs/src/cli/index.rs +++ b/tree-sitter-stack-graphs/src/cli/index.rs @@ -428,10 +428,19 @@ impl<'a> Indexer<'a> { cancellation_flag: &dyn CancellationFlag, ) -> std::result::Result<(), BuildErrorWithSource<'b>> { let relative_source_path = source_path.strip_prefix(source_root).unwrap(); + // here the file should also have stripped the source_root from its path if let Some(lc) = lcs.primary { let globals = Variables::new(); lc.sgl - .build_stack_graph_into(graph, file, source, &globals, cancellation_flag) + .build_stack_graph_into( + graph, + file, + source, + source_path, + source_root, + &globals, + cancellation_flag, + ) .map_err(|inner| BuildErrorWithSource { inner, source_path: source_path.to_path_buf(), diff --git a/tree-sitter-stack-graphs/src/cli/test.rs b/tree-sitter-stack-graphs/src/cli/test.rs index c7f73e7ce..210d79947 100644 --- a/tree-sitter-stack-graphs/src/cli/test.rs +++ b/tree-sitter-stack-graphs/src/cli/test.rs @@ -321,6 +321,8 @@ impl TestArgs { &mut test.graph, test_fragment.file, &test_fragment.source, + &test_fragment.path, + &test_fragment.root_path, &globals, cancellation_flag.as_ref(), ) diff --git a/tree-sitter-stack-graphs/src/lib.rs b/tree-sitter-stack-graphs/src/lib.rs index 86651b6be..aee860420 100644 --- a/tree-sitter-stack-graphs/src/lib.rs +++ b/tree-sitter-stack-graphs/src/lib.rs @@ -438,6 +438,7 @@ static PRECEDENCE_ATTR: &'static str = "precedence"; static ROOT_NODE_VAR: &'static str = "ROOT_NODE"; static JUMP_TO_SCOPE_NODE_VAR: &'static str = "JUMP_TO_SCOPE_NODE"; static FILE_PATH_VAR: &'static str = "FILE_PATH"; +static ROOT_PATH_VAR: &'static str = "ROOT_PATH"; /// Holds information about how to construct stack graphs for a particular language. pub struct StackGraphLanguage { @@ -557,10 +558,12 @@ impl StackGraphLanguage { stack_graph: &'a mut StackGraph, file: Handle, source: &'a str, + source_path: &Path, + source_root: &Path, globals: &'a Variables<'a>, cancellation_flag: &'a dyn CancellationFlag, ) -> Result<(), BuildError> { - self.builder_into_stack_graph(stack_graph, file, source) + self.builder_into_stack_graph(stack_graph, file, source, source_path, source_root) .build(globals, cancellation_flag) } @@ -573,8 +576,10 @@ impl StackGraphLanguage { stack_graph: &'a mut StackGraph, file: Handle, source: &'a str, + source_path: &'a Path, + source_root: &'a Path, ) -> Builder<'a> { - Builder::new(self, stack_graph, file, source) + Builder::new(self, stack_graph, file, source, source_path, source_root) } } @@ -583,6 +588,8 @@ pub struct Builder<'a> { stack_graph: &'a mut StackGraph, file: Handle, source: &'a str, + source_path: &'a Path, + source_root: &'a Path, graph: Graph<'a>, remapped_nodes: HashMap, injected_node_count: usize, @@ -595,6 +602,8 @@ impl<'a> Builder<'a> { stack_graph: &'a mut StackGraph, file: Handle, source: &'a str, + source_path: &'a Path, + source_root: &'a Path, ) -> Self { let span_calculator = SpanCalculator::new(source); Builder { @@ -602,6 +611,8 @@ impl<'a> Builder<'a> { stack_graph, file, source, + source_path, + source_root, graph: Graph::new(), remapped_nodes: HashMap::new(), injected_node_count: 0, @@ -635,23 +646,33 @@ impl<'a> Builder<'a> { let tree = parse_errors.into_tree(); let mut globals = Variables::nested(globals); + if globals.get(&ROOT_NODE_VAR.into()).is_none() { let root_node = self.inject_node(NodeID::root()); globals .add(ROOT_NODE_VAR.into(), root_node.into()) .expect("Failed to set ROOT_NODE"); } + let jump_to_scope_node = self.inject_node(NodeID::jump_to()); globals .add(JUMP_TO_SCOPE_NODE_VAR.into(), jump_to_scope_node.into()) .expect("Failed to set JUMP_TO_SCOPE_NODE"); + if globals.get(&FILE_PATH_VAR.into()).is_none() { - let file_name = self.stack_graph[self.file].to_string(); + let file_name = self.source_path.to_str().unwrap().to_string(); globals .add(FILE_PATH_VAR.into(), file_name.into()) .expect("Failed to set FILE_PATH"); } + if globals.get(&ROOT_PATH_VAR.into()).is_none() { + let root_path = self.source_root.to_str().unwrap().to_string(); + globals + .add(ROOT_PATH_VAR.into(), root_path.into()) + .expect("Failed to set ROOT_PATH"); + } + let mut config = ExecutionConfig::new(&self.sgl.functions, &globals) .lazy(true) .debug_attributes( diff --git a/tree-sitter-stack-graphs/src/loader.rs b/tree-sitter-stack-graphs/src/loader.rs index 9ffc1d674..ced44c2ed 100644 --- a/tree-sitter-stack-graphs/src/loader.rs +++ b/tree-sitter-stack-graphs/src/loader.rs @@ -79,10 +79,14 @@ impl LanguageConfiguration { Loader::load_globals_from_config_str(builtins_config, &mut builtins_globals)?; } let file = builtins.add_file("").unwrap(); + let builtins_path_var = Path::new(""); + let builtins_root = Path::new(""); sgl.build_stack_graph_into( &mut builtins, file, builtins_source, + builtins_path_var, + builtins_root, &builtins_globals, cancellation_flag, ) @@ -325,17 +329,28 @@ impl Loader { graph: &mut StackGraph, cancellation_flag: &dyn CancellationFlag, ) -> Result<(), LoadError<'a>> { - let file = graph.add_file(&path.to_string_lossy()).unwrap(); + let file_name = path.to_string_lossy(); + let file: stack_graphs::arena::Handle = + graph.add_file(&file_name).unwrap(); + let builtins_root = Path::new(""); let mut globals = Variables::new(); Self::load_globals_from_config_str(&config, &mut globals)?; - sgl.build_stack_graph_into(graph, file, &source, &globals, cancellation_flag) - .map_err(|err| LoadError::Builtins { - inner: err, - source_path: path.to_path_buf(), - source, - tsg_path: sgl.tsg_path.to_path_buf(), - tsg: sgl.tsg_source.clone(), - })?; + sgl.build_stack_graph_into( + graph, + file, + &source, + path, + builtins_root, + &globals, + cancellation_flag, + ) + .map_err(|err| LoadError::Builtins { + inner: err, + source_path: path.to_path_buf(), + source, + tsg_path: sgl.tsg_path.to_path_buf(), + tsg: sgl.tsg_source.clone(), + })?; return Ok(()); } diff --git a/tree-sitter-stack-graphs/src/test.rs b/tree-sitter-stack-graphs/src/test.rs index 236e254c5..554783b5f 100644 --- a/tree-sitter-stack-graphs/src/test.rs +++ b/tree-sitter-stack-graphs/src/test.rs @@ -157,6 +157,7 @@ pub struct Test { pub struct TestFragment { pub file: Handle, pub path: PathBuf, + pub root_path: PathBuf, pub source: String, pub assertions: Vec, pub globals: HashMap, @@ -180,6 +181,7 @@ impl Test { let mut prev_source = String::new(); let mut line_files = Vec::new(); let mut line_count = 0; + let default_root_path = PathBuf::from(""); for (current_line_number, current_line) in PositionedSubstring::lines_iter(source).enumerate() { @@ -202,6 +204,7 @@ impl Test { fragments.push(TestFragment { file, path: current_path, + root_path: default_root_path.clone(), source: current_source, assertions: Vec::new(), globals: current_globals, @@ -254,6 +257,7 @@ impl Test { fragments.push(TestFragment { file, path: current_path, + root_path: default_root_path.clone(), source: current_source, assertions: Vec::new(), globals: current_globals, diff --git a/tree-sitter-stack-graphs/tests/it/builder.rs b/tree-sitter-stack-graphs/tests/it/builder.rs index 661e8b973..31dc46994 100644 --- a/tree-sitter-stack-graphs/tests/it/builder.rs +++ b/tree-sitter-stack-graphs/tests/it/builder.rs @@ -5,6 +5,8 @@ // Please see the LICENSE-APACHE or LICENSE-MIT files in this distribution for license details. // ------------------------------------------------------------------------------------------------ +use std::path::Path; + use stack_graphs::graph::StackGraph; use tree_sitter_graph::Variables; use tree_sitter_stack_graphs::NoCancellation; @@ -22,15 +24,27 @@ fn can_support_preexisting_nodes() { "#; let python = "pass"; + let file_name = "test.py"; + let source_path = Path::new(file_name); + let source_root = Path::new(""); + let mut graph = StackGraph::new(); - let file = graph.get_or_create_file("test.py"); + let file = graph.get_or_create_file(file_name); let node_id = graph.new_node_id(file); let _preexisting_node = graph.add_scope_node(node_id, true).unwrap(); let globals = Variables::new(); let language = StackGraphLanguage::from_str(tree_sitter_python::language(), tsg).unwrap(); language - .build_stack_graph_into(&mut graph, file, python, &globals, &NoCancellation) + .build_stack_graph_into( + &mut graph, + file, + python, + source_path, + source_root, + &globals, + &NoCancellation, + ) .expect("Failed to build graph"); } @@ -45,13 +59,18 @@ fn can_support_injected_nodes() { "#; let python = "pass"; + let file_name = "test.py"; + let source_path = Path::new(file_name); + let source_root = Path::new(""); + let mut graph = StackGraph::new(); - let file = graph.get_or_create_file("test.py"); + let file = graph.get_or_create_file(file_name); let node_id = graph.new_node_id(file); let _preexisting_node = graph.add_scope_node(node_id, true).unwrap(); let language = StackGraphLanguage::from_str(tree_sitter_python::language(), tsg).unwrap(); - let mut builder = language.builder_into_stack_graph(&mut graph, file, python); + let mut builder = + language.builder_into_stack_graph(&mut graph, file, python, source_path, source_root); let mut globals = Variables::new(); globals diff --git a/tree-sitter-stack-graphs/tests/it/main.rs b/tree-sitter-stack-graphs/tests/it/main.rs index c1e37a40e..9d430bb73 100644 --- a/tree-sitter-stack-graphs/tests/it/main.rs +++ b/tree-sitter-stack-graphs/tests/it/main.rs @@ -5,6 +5,8 @@ // Please see the LICENSE-APACHE or LICENSE-MIT files in this distribution for license details. // ------------------------------------------------------------------------------------------------ +use std::path::Path; + use stack_graphs::arena::Handle; use stack_graphs::graph::File; use stack_graphs::graph::StackGraph; @@ -23,11 +25,22 @@ pub(self) fn build_stack_graph( python_source: &str, tsg_source: &str, ) -> Result<(StackGraph, Handle), BuildError> { + let file_name = "test.py"; + let source_path = Path::new(file_name); + let source_root = Path::new(""); let language = StackGraphLanguage::from_str(tree_sitter_python::language(), tsg_source).unwrap(); let mut graph = StackGraph::new(); - let file = graph.get_or_create_file("test.py"); + let file = graph.get_or_create_file(file_name); let globals = Variables::new(); - language.build_stack_graph_into(&mut graph, file, python_source, &globals, &NoCancellation)?; + language.build_stack_graph_into( + &mut graph, + file, + python_source, + source_path, + source_root, + &globals, + &NoCancellation, + )?; Ok((graph, file)) } diff --git a/tree-sitter-stack-graphs/tests/it/test.rs b/tree-sitter-stack-graphs/tests/it/test.rs index 98ec3ab96..89ae7b279 100644 --- a/tree-sitter-stack-graphs/tests/it/test.rs +++ b/tree-sitter-stack-graphs/tests/it/test.rs @@ -68,12 +68,22 @@ fn build_stack_graph_into( graph: &mut StackGraph, file: Handle, python_source: &str, + source_path: &Path, + source_root: &Path, tsg_source: &str, globals: &Variables, ) -> Result<(), BuildError> { let language = StackGraphLanguage::from_str(tree_sitter_python::language(), tsg_source).unwrap(); - language.build_stack_graph_into(graph, file, python_source, globals, &NoCancellation)?; + language.build_stack_graph_into( + graph, + file, + python_source, + source_path, + source_root, + globals, + &NoCancellation, + )?; Ok(()) } @@ -102,6 +112,8 @@ fn check_test( &mut test.graph, fragments.file, &fragments.source, + &fragments.path, + &fragments.root_path, tsg_source, &globals, ) From c43fa8b1af2a5ced2174059d41d4b0f2b8da3983 Mon Sep 17 00:00:00 2001 From: nohehf Date: Thu, 30 May 2024 11:36:09 +0200 Subject: [PATCH 252/291] feat: use replace instead of custom function --- .../src/stack-graphs.tsg | 2 +- tree-sitter-stack-graphs/src/functions.rs | 37 ------------------- 2 files changed, 1 insertion(+), 38 deletions(-) diff --git a/languages/tree-sitter-stack-graphs-typescript/src/stack-graphs.tsg b/languages/tree-sitter-stack-graphs-typescript/src/stack-graphs.tsg index 7e6359280..b7478b4e4 100644 --- a/languages/tree-sitter-stack-graphs-typescript/src/stack-graphs.tsg +++ b/languages/tree-sitter-stack-graphs-typescript/src/stack-graphs.tsg @@ -541,7 +541,7 @@ attribute node_symbol = node => symbol = (source-text node), source_n ; module reference var mod_scope = mod_ref__ns ; normalize path and remove the extension as we want to match 'foo', 'foo.js', 'foo.ts', etc. - scan (path-remove-ext (path-normalize mod_path)) { + scan (path-normalize (replace mod_path "\.(js|ts|jsx|tsx)$" "")) { "([^/]+)/?" { node mod_ref attr (mod_ref) push_symbol = $1 diff --git a/tree-sitter-stack-graphs/src/functions.rs b/tree-sitter-stack-graphs/src/functions.rs index e1f9e94e5..9082ba427 100644 --- a/tree-sitter-stack-graphs/src/functions.rs +++ b/tree-sitter-stack-graphs/src/functions.rs @@ -43,10 +43,6 @@ pub mod path { path_fn(|p| normalize(p).map(|p| p.as_os_str().to_os_string())), ); functions.add("path-split".into(), PathSplit); - functions.add( - "path-remove-ext".into(), - path_fn(|p| remove_extension(p).map(|p| p.as_os_str().to_os_string())), - ); } pub fn path_fn(f: F) -> impl Function @@ -163,37 +159,4 @@ pub mod path { } Some(ret) } - - /// Removes the extension from a path. - /// eg. `foo/bar.rs` -> `foo/bar` - /// eg. `foo/bar` -> `foo/bar` - /// eg. `foo/bar.rs.bak` -> `foo/bar.rs` - pub fn remove_extension(path: &Path) -> Option { - path.extension() - .map_or(Some(path.into()), |_| path.with_extension("").into()) - } - - #[test] - fn test_remove_extension() { - assert_eq!( - remove_extension(Path::new("foo/bar.rs")), - Some(PathBuf::from("foo/bar")) - ); - assert_eq!( - remove_extension(Path::new("foo/bar")), - Some(PathBuf::from("foo/bar")) - ); - assert_eq!( - remove_extension(Path::new("foo/bar.rs.bak")), - Some(PathBuf::from("foo/bar.rs")) - ); - assert_eq!( - remove_extension(Path::new("foo")), - Some(PathBuf::from("foo")) - ); - assert_eq!( - remove_extension(Path::new("foo.rs")), - Some(PathBuf::from("foo")) - ); - } } From b3260536a1002743a9a393a64ce8fd33ca859b70 Mon Sep 17 00:00:00 2001 From: nohehf Date: Thu, 30 May 2024 12:17:15 +0200 Subject: [PATCH 253/291] feat: pass paths in globals --- .../src/stack-graphs.tsg | 2 +- tree-sitter-stack-graphs/src/cli/index.rs | 23 +++++----- tree-sitter-stack-graphs/src/cli/test.rs | 12 ++++- tree-sitter-stack-graphs/src/lib.rs | 31 +++---------- tree-sitter-stack-graphs/src/loader.rs | 44 ++++++++++--------- tree-sitter-stack-graphs/src/test.rs | 4 -- tree-sitter-stack-graphs/tests/it/builder.rs | 13 +----- tree-sitter-stack-graphs/tests/it/main.rs | 23 +++++----- tree-sitter-stack-graphs/tests/it/test.rs | 14 +----- 9 files changed, 66 insertions(+), 100 deletions(-) diff --git a/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg b/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg index 887e171b3..5fb407ae8 100644 --- a/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg +++ b/languages/tree-sitter-stack-graphs-python/src/stack-graphs.tsg @@ -13,7 +13,7 @@ ;; ^^^^^^^^^^^^^^^^ global FILE_PATH -global ROOT_PATH +global ROOT_PATH = "" global ROOT_NODE global JUMP_TO_SCOPE_NODE diff --git a/tree-sitter-stack-graphs/src/cli/index.rs b/tree-sitter-stack-graphs/src/cli/index.rs index 5bf48f54f..6425d8ca7 100644 --- a/tree-sitter-stack-graphs/src/cli/index.rs +++ b/tree-sitter-stack-graphs/src/cli/index.rs @@ -42,6 +42,7 @@ use crate::BuildError; use crate::CancelAfterDuration; use crate::CancellationFlag; use crate::NoCancellation; +use crate::{FILE_PATH_VAR, ROOT_PATH_VAR}; #[derive(Args)] pub struct IndexArgs { @@ -428,19 +429,19 @@ impl<'a> Indexer<'a> { cancellation_flag: &dyn CancellationFlag, ) -> std::result::Result<(), BuildErrorWithSource<'b>> { let relative_source_path = source_path.strip_prefix(source_root).unwrap(); - // here the file should also have stripped the source_root from its path if let Some(lc) = lcs.primary { - let globals = Variables::new(); + let mut globals = Variables::new(); + + globals + .add(FILE_PATH_VAR.into(), source_path.to_str().unwrap().into()) + .expect("failed to add file path variable"); + + globals + .add(ROOT_PATH_VAR.into(), source_root.to_str().unwrap().into()) + .expect("failed to add root path variable"); + lc.sgl - .build_stack_graph_into( - graph, - file, - source, - source_path, - source_root, - &globals, - cancellation_flag, - ) + .build_stack_graph_into(graph, file, source, &globals, cancellation_flag) .map_err(|inner| BuildErrorWithSource { inner, source_path: source_path.to_path_buf(), diff --git a/tree-sitter-stack-graphs/src/cli/test.rs b/tree-sitter-stack-graphs/src/cli/test.rs index 210d79947..6ae04114c 100644 --- a/tree-sitter-stack-graphs/src/cli/test.rs +++ b/tree-sitter-stack-graphs/src/cli/test.rs @@ -43,6 +43,7 @@ use crate::test::Test; use crate::test::TestResult; use crate::CancelAfterDuration; use crate::CancellationFlag; +use crate::FILE_PATH_VAR; #[derive(Args)] #[clap(after_help = r#"PATH SPECIFICATIONS: @@ -316,13 +317,20 @@ impl TestArgs { &mut Some(test_fragment.source.as_ref()), )? { globals.clear(); + + // Add FILE_PATH to variables + globals + .add( + FILE_PATH_VAR.into(), + test_fragment.path.to_str().unwrap().into(), + ) + .expect("failed to add file path variable"); + test_fragment.add_globals_to(&mut globals); lc.sgl.build_stack_graph_into( &mut test.graph, test_fragment.file, &test_fragment.source, - &test_fragment.path, - &test_fragment.root_path, &globals, cancellation_flag.as_ref(), ) diff --git a/tree-sitter-stack-graphs/src/lib.rs b/tree-sitter-stack-graphs/src/lib.rs index aee860420..8a02b0d58 100644 --- a/tree-sitter-stack-graphs/src/lib.rs +++ b/tree-sitter-stack-graphs/src/lib.rs @@ -558,12 +558,10 @@ impl StackGraphLanguage { stack_graph: &'a mut StackGraph, file: Handle, source: &'a str, - source_path: &Path, - source_root: &Path, globals: &'a Variables<'a>, cancellation_flag: &'a dyn CancellationFlag, ) -> Result<(), BuildError> { - self.builder_into_stack_graph(stack_graph, file, source, source_path, source_root) + self.builder_into_stack_graph(stack_graph, file, source) .build(globals, cancellation_flag) } @@ -576,10 +574,8 @@ impl StackGraphLanguage { stack_graph: &'a mut StackGraph, file: Handle, source: &'a str, - source_path: &'a Path, - source_root: &'a Path, ) -> Builder<'a> { - Builder::new(self, stack_graph, file, source, source_path, source_root) + Builder::new(self, stack_graph, file, source) } } @@ -588,8 +584,6 @@ pub struct Builder<'a> { stack_graph: &'a mut StackGraph, file: Handle, source: &'a str, - source_path: &'a Path, - source_root: &'a Path, graph: Graph<'a>, remapped_nodes: HashMap, injected_node_count: usize, @@ -602,8 +596,6 @@ impl<'a> Builder<'a> { stack_graph: &'a mut StackGraph, file: Handle, source: &'a str, - source_path: &'a Path, - source_root: &'a Path, ) -> Self { let span_calculator = SpanCalculator::new(source); Builder { @@ -611,8 +603,6 @@ impl<'a> Builder<'a> { stack_graph, file, source, - source_path, - source_root, graph: Graph::new(), remapped_nodes: HashMap::new(), injected_node_count: 0, @@ -659,19 +649,10 @@ impl<'a> Builder<'a> { .add(JUMP_TO_SCOPE_NODE_VAR.into(), jump_to_scope_node.into()) .expect("Failed to set JUMP_TO_SCOPE_NODE"); - if globals.get(&FILE_PATH_VAR.into()).is_none() { - let file_name = self.source_path.to_str().unwrap().to_string(); - globals - .add(FILE_PATH_VAR.into(), file_name.into()) - .expect("Failed to set FILE_PATH"); - } - - if globals.get(&ROOT_PATH_VAR.into()).is_none() { - let root_path = self.source_root.to_str().unwrap().to_string(); - globals - .add(ROOT_PATH_VAR.into(), root_path.into()) - .expect("Failed to set ROOT_PATH"); - } + // FILE_PATH is mandatory + globals + .get(&FILE_PATH_VAR.into()) + .expect("FILE_PATH not set"); let mut config = ExecutionConfig::new(&self.sgl.functions, &globals) .lazy(true) diff --git a/tree-sitter-stack-graphs/src/loader.rs b/tree-sitter-stack-graphs/src/loader.rs index ced44c2ed..b0d6d1875 100644 --- a/tree-sitter-stack-graphs/src/loader.rs +++ b/tree-sitter-stack-graphs/src/loader.rs @@ -29,6 +29,7 @@ use tree_sitter_loader::Loader as TsLoader; use crate::CancellationFlag; use crate::FileAnalyzer; use crate::StackGraphLanguage; +use crate::FILE_PATH_VAR; pub static DEFAULT_TSG_PATHS: Lazy> = Lazy::new(|| vec![LoadPath::Grammar("queries/stack-graphs".into())]); @@ -75,18 +76,23 @@ impl LanguageConfiguration { let mut builtins = StackGraph::new(); if let Some((builtins_path, builtins_source)) = builtins_source { let mut builtins_globals = Variables::new(); + + let builtins_path_var = Path::new(""); + builtins_globals + .add( + FILE_PATH_VAR.into(), + builtins_path_var.to_str().unwrap().into(), + ) + .expect("failed to add file path variable"); + if let Some(builtins_config) = builtins_config { Loader::load_globals_from_config_str(builtins_config, &mut builtins_globals)?; } let file = builtins.add_file("").unwrap(); - let builtins_path_var = Path::new(""); - let builtins_root = Path::new(""); sgl.build_stack_graph_into( &mut builtins, file, builtins_source, - builtins_path_var, - builtins_root, &builtins_globals, cancellation_flag, ) @@ -332,25 +338,21 @@ impl Loader { let file_name = path.to_string_lossy(); let file: stack_graphs::arena::Handle = graph.add_file(&file_name).unwrap(); - let builtins_root = Path::new(""); let mut globals = Variables::new(); + + globals + .add(FILE_PATH_VAR.into(), path.to_str().unwrap().into()) + .expect("failed to add file path variable"); + Self::load_globals_from_config_str(&config, &mut globals)?; - sgl.build_stack_graph_into( - graph, - file, - &source, - path, - builtins_root, - &globals, - cancellation_flag, - ) - .map_err(|err| LoadError::Builtins { - inner: err, - source_path: path.to_path_buf(), - source, - tsg_path: sgl.tsg_path.to_path_buf(), - tsg: sgl.tsg_source.clone(), - })?; + sgl.build_stack_graph_into(graph, file, &source, &globals, cancellation_flag) + .map_err(|err| LoadError::Builtins { + inner: err, + source_path: path.to_path_buf(), + source, + tsg_path: sgl.tsg_path.to_path_buf(), + tsg: sgl.tsg_source.clone(), + })?; return Ok(()); } diff --git a/tree-sitter-stack-graphs/src/test.rs b/tree-sitter-stack-graphs/src/test.rs index 554783b5f..236e254c5 100644 --- a/tree-sitter-stack-graphs/src/test.rs +++ b/tree-sitter-stack-graphs/src/test.rs @@ -157,7 +157,6 @@ pub struct Test { pub struct TestFragment { pub file: Handle, pub path: PathBuf, - pub root_path: PathBuf, pub source: String, pub assertions: Vec, pub globals: HashMap, @@ -181,7 +180,6 @@ impl Test { let mut prev_source = String::new(); let mut line_files = Vec::new(); let mut line_count = 0; - let default_root_path = PathBuf::from(""); for (current_line_number, current_line) in PositionedSubstring::lines_iter(source).enumerate() { @@ -204,7 +202,6 @@ impl Test { fragments.push(TestFragment { file, path: current_path, - root_path: default_root_path.clone(), source: current_source, assertions: Vec::new(), globals: current_globals, @@ -257,7 +254,6 @@ impl Test { fragments.push(TestFragment { file, path: current_path, - root_path: default_root_path.clone(), source: current_source, assertions: Vec::new(), globals: current_globals, diff --git a/tree-sitter-stack-graphs/tests/it/builder.rs b/tree-sitter-stack-graphs/tests/it/builder.rs index 31dc46994..a87b80559 100644 --- a/tree-sitter-stack-graphs/tests/it/builder.rs +++ b/tree-sitter-stack-graphs/tests/it/builder.rs @@ -36,15 +36,7 @@ fn can_support_preexisting_nodes() { let globals = Variables::new(); let language = StackGraphLanguage::from_str(tree_sitter_python::language(), tsg).unwrap(); language - .build_stack_graph_into( - &mut graph, - file, - python, - source_path, - source_root, - &globals, - &NoCancellation, - ) + .build_stack_graph_into(&mut graph, file, python, &globals, &NoCancellation) .expect("Failed to build graph"); } @@ -69,8 +61,7 @@ fn can_support_injected_nodes() { let _preexisting_node = graph.add_scope_node(node_id, true).unwrap(); let language = StackGraphLanguage::from_str(tree_sitter_python::language(), tsg).unwrap(); - let mut builder = - language.builder_into_stack_graph(&mut graph, file, python, source_path, source_root); + let mut builder = language.builder_into_stack_graph(&mut graph, file, python); let mut globals = Variables::new(); globals diff --git a/tree-sitter-stack-graphs/tests/it/main.rs b/tree-sitter-stack-graphs/tests/it/main.rs index 9d430bb73..687564e1e 100644 --- a/tree-sitter-stack-graphs/tests/it/main.rs +++ b/tree-sitter-stack-graphs/tests/it/main.rs @@ -15,6 +15,8 @@ use tree_sitter_stack_graphs::BuildError; use tree_sitter_stack_graphs::NoCancellation; use tree_sitter_stack_graphs::StackGraphLanguage; +static FILE_PATH_VAR: &'static str = "FILE_PATH"; + mod builder; mod edges; mod loader; @@ -26,21 +28,18 @@ pub(self) fn build_stack_graph( tsg_source: &str, ) -> Result<(StackGraph, Handle), BuildError> { let file_name = "test.py"; - let source_path = Path::new(file_name); - let source_root = Path::new(""); let language = StackGraphLanguage::from_str(tree_sitter_python::language(), tsg_source).unwrap(); let mut graph = StackGraph::new(); let file = graph.get_or_create_file(file_name); - let globals = Variables::new(); - language.build_stack_graph_into( - &mut graph, - file, - python_source, - source_path, - source_root, - &globals, - &NoCancellation, - )?; + let mut globals = Variables::new(); + let source_path = Path::new(file_name); + + // The FILE_PATH_VAR is not accessible here + globals + .add(FILE_PATH_VAR.into(), source_path.to_str().unwrap().into()) + .expect("failed to add file path variable"); + + language.build_stack_graph_into(&mut graph, file, python_source, &globals, &NoCancellation)?; Ok((graph, file)) } diff --git a/tree-sitter-stack-graphs/tests/it/test.rs b/tree-sitter-stack-graphs/tests/it/test.rs index 89ae7b279..98ec3ab96 100644 --- a/tree-sitter-stack-graphs/tests/it/test.rs +++ b/tree-sitter-stack-graphs/tests/it/test.rs @@ -68,22 +68,12 @@ fn build_stack_graph_into( graph: &mut StackGraph, file: Handle, python_source: &str, - source_path: &Path, - source_root: &Path, tsg_source: &str, globals: &Variables, ) -> Result<(), BuildError> { let language = StackGraphLanguage::from_str(tree_sitter_python::language(), tsg_source).unwrap(); - language.build_stack_graph_into( - graph, - file, - python_source, - source_path, - source_root, - globals, - &NoCancellation, - )?; + language.build_stack_graph_into(graph, file, python_source, globals, &NoCancellation)?; Ok(()) } @@ -112,8 +102,6 @@ fn check_test( &mut test.graph, fragments.file, &fragments.source, - &fragments.path, - &fragments.root_path, tsg_source, &globals, ) From 8a98dce32fee057bc4d754ebc85055fecbbda0a1 Mon Sep 17 00:00:00 2001 From: nohehf Date: Thu, 30 May 2024 12:33:37 +0200 Subject: [PATCH 254/291] fix: add missing paths --- tree-sitter-stack-graphs/tests/it/builder.rs | 15 ++++++++++----- tree-sitter-stack-graphs/tests/it/test.rs | 10 ++++++++++ 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/tree-sitter-stack-graphs/tests/it/builder.rs b/tree-sitter-stack-graphs/tests/it/builder.rs index a87b80559..b0dad5449 100644 --- a/tree-sitter-stack-graphs/tests/it/builder.rs +++ b/tree-sitter-stack-graphs/tests/it/builder.rs @@ -14,6 +14,7 @@ use tree_sitter_stack_graphs::StackGraphLanguage; use crate::edges::check_stack_graph_edges; use crate::nodes::check_stack_graph_nodes; +use crate::FILE_PATH_VAR; #[test] fn can_support_preexisting_nodes() { @@ -25,15 +26,17 @@ fn can_support_preexisting_nodes() { let python = "pass"; let file_name = "test.py"; - let source_path = Path::new(file_name); - let source_root = Path::new(""); let mut graph = StackGraph::new(); let file = graph.get_or_create_file(file_name); let node_id = graph.new_node_id(file); let _preexisting_node = graph.add_scope_node(node_id, true).unwrap(); - let globals = Variables::new(); + let mut globals = Variables::new(); + globals + .add(FILE_PATH_VAR.into(), file_name.into()) + .expect("failed to add file path variable"); + let language = StackGraphLanguage::from_str(tree_sitter_python::language(), tsg).unwrap(); language .build_stack_graph_into(&mut graph, file, python, &globals, &NoCancellation) @@ -52,8 +55,6 @@ fn can_support_injected_nodes() { let python = "pass"; let file_name = "test.py"; - let source_path = Path::new(file_name); - let source_root = Path::new(""); let mut graph = StackGraph::new(); let file = graph.get_or_create_file(file_name); @@ -64,6 +65,10 @@ fn can_support_injected_nodes() { let mut builder = language.builder_into_stack_graph(&mut graph, file, python); let mut globals = Variables::new(); + globals + .add(FILE_PATH_VAR.into(), file_name.into()) + .expect("failed to add file path variable"); + globals .add("EXT_NODE".into(), builder.inject_node(node_id).into()) .expect("Failed to add EXT_NODE variable"); diff --git a/tree-sitter-stack-graphs/tests/it/test.rs b/tree-sitter-stack-graphs/tests/it/test.rs index 98ec3ab96..9624439c5 100644 --- a/tree-sitter-stack-graphs/tests/it/test.rs +++ b/tree-sitter-stack-graphs/tests/it/test.rs @@ -5,6 +5,7 @@ // Please see the LICENSE-APACHE or LICENSE-MIT files in this distribution for license details. // ------------------------------------------------------------------------------------------------ +use crate::FILE_PATH_VAR; use once_cell::sync::Lazy; use pretty_assertions::assert_eq; use stack_graphs::arena::Handle; @@ -94,9 +95,18 @@ fn check_test( expected_successes + expected_failures, assertion_count, ); + let mut globals = Variables::new(); for fragments in &test.fragments { globals.clear(); + + globals + .add( + FILE_PATH_VAR.into(), + fragments.path.to_str().unwrap().into(), + ) + .expect("failed to add file path variable"); + fragments.add_globals_to(&mut globals); build_stack_graph_into( &mut test.graph, From ad4d517bf52b0615d7798f106e77bbd592ac694f Mon Sep 17 00:00:00 2001 From: nohehf Date: Sat, 1 Jun 2024 14:47:32 +0200 Subject: [PATCH 255/291] chore: clenup after review --- tree-sitter-stack-graphs/src/lib.rs | 19 ++++++++++--------- tree-sitter-stack-graphs/src/loader.rs | 12 +++++------- tree-sitter-stack-graphs/tests/it/builder.rs | 5 +---- tree-sitter-stack-graphs/tests/it/main.rs | 4 +--- 4 files changed, 17 insertions(+), 23 deletions(-) diff --git a/tree-sitter-stack-graphs/src/lib.rs b/tree-sitter-stack-graphs/src/lib.rs index 8a02b0d58..1a3e344b1 100644 --- a/tree-sitter-stack-graphs/src/lib.rs +++ b/tree-sitter-stack-graphs/src/lib.rs @@ -435,10 +435,16 @@ static SCOPE_ATTRS: Lazy> = static PRECEDENCE_ATTR: &'static str = "precedence"; // Global variables -static ROOT_NODE_VAR: &'static str = "ROOT_NODE"; -static JUMP_TO_SCOPE_NODE_VAR: &'static str = "JUMP_TO_SCOPE_NODE"; -static FILE_PATH_VAR: &'static str = "FILE_PATH"; -static ROOT_PATH_VAR: &'static str = "ROOT_PATH"; +/// Name of the variable used to pass the root node. +pub const ROOT_NODE_VAR: &'static str = "ROOT_NODE"; +/// Name of the variable used to pass the jump-to-scope node. +pub const JUMP_TO_SCOPE_NODE_VAR: &'static str = "JUMP_TO_SCOPE_NODE"; +/// Name of the variable used to pass the file path. +/// If a root path is given, it should be a descendant the root path. +pub const FILE_PATH_VAR: &'static str = "FILE_PATH"; +/// Name of the variable used to pass the root path. +/// If given, should be an ancestor of the file path. +pub const ROOT_PATH_VAR: &'static str = "ROOT_PATH"; /// Holds information about how to construct stack graphs for a particular language. pub struct StackGraphLanguage { @@ -649,11 +655,6 @@ impl<'a> Builder<'a> { .add(JUMP_TO_SCOPE_NODE_VAR.into(), jump_to_scope_node.into()) .expect("Failed to set JUMP_TO_SCOPE_NODE"); - // FILE_PATH is mandatory - globals - .get(&FILE_PATH_VAR.into()) - .expect("FILE_PATH not set"); - let mut config = ExecutionConfig::new(&self.sgl.functions, &globals) .lazy(true) .debug_attributes( diff --git a/tree-sitter-stack-graphs/src/loader.rs b/tree-sitter-stack-graphs/src/loader.rs index b0d6d1875..f0ab9891c 100644 --- a/tree-sitter-stack-graphs/src/loader.rs +++ b/tree-sitter-stack-graphs/src/loader.rs @@ -31,6 +31,8 @@ use crate::FileAnalyzer; use crate::StackGraphLanguage; use crate::FILE_PATH_VAR; +const BUILTINS_FILENAME: &str = ""; + pub static DEFAULT_TSG_PATHS: Lazy> = Lazy::new(|| vec![LoadPath::Grammar("queries/stack-graphs".into())]); pub static DEFAULT_BUILTINS_PATHS: Lazy> = @@ -77,18 +79,14 @@ impl LanguageConfiguration { if let Some((builtins_path, builtins_source)) = builtins_source { let mut builtins_globals = Variables::new(); - let builtins_path_var = Path::new(""); builtins_globals - .add( - FILE_PATH_VAR.into(), - builtins_path_var.to_str().unwrap().into(), - ) + .add(FILE_PATH_VAR.into(), BUILTINS_FILENAME.into()) .expect("failed to add file path variable"); if let Some(builtins_config) = builtins_config { Loader::load_globals_from_config_str(builtins_config, &mut builtins_globals)?; } - let file = builtins.add_file("").unwrap(); + let file = builtins.add_file(BUILTINS_FILENAME).unwrap(); sgl.build_stack_graph_into( &mut builtins, file, @@ -341,7 +339,7 @@ impl Loader { let mut globals = Variables::new(); globals - .add(FILE_PATH_VAR.into(), path.to_str().unwrap().into()) + .add(FILE_PATH_VAR.into(), BUILTINS_FILENAME.into()) .expect("failed to add file path variable"); Self::load_globals_from_config_str(&config, &mut globals)?; diff --git a/tree-sitter-stack-graphs/tests/it/builder.rs b/tree-sitter-stack-graphs/tests/it/builder.rs index b0dad5449..acc012eaa 100644 --- a/tree-sitter-stack-graphs/tests/it/builder.rs +++ b/tree-sitter-stack-graphs/tests/it/builder.rs @@ -4,17 +4,14 @@ // Licensed under either of Apache License, Version 2.0, or MIT license, at your option. // Please see the LICENSE-APACHE or LICENSE-MIT files in this distribution for license details. // ------------------------------------------------------------------------------------------------ - -use std::path::Path; - use stack_graphs::graph::StackGraph; use tree_sitter_graph::Variables; use tree_sitter_stack_graphs::NoCancellation; use tree_sitter_stack_graphs::StackGraphLanguage; +use tree_sitter_stack_graphs::FILE_PATH_VAR; use crate::edges::check_stack_graph_edges; use crate::nodes::check_stack_graph_nodes; -use crate::FILE_PATH_VAR; #[test] fn can_support_preexisting_nodes() { diff --git a/tree-sitter-stack-graphs/tests/it/main.rs b/tree-sitter-stack-graphs/tests/it/main.rs index 687564e1e..33ee19241 100644 --- a/tree-sitter-stack-graphs/tests/it/main.rs +++ b/tree-sitter-stack-graphs/tests/it/main.rs @@ -14,8 +14,7 @@ use tree_sitter_graph::Variables; use tree_sitter_stack_graphs::BuildError; use tree_sitter_stack_graphs::NoCancellation; use tree_sitter_stack_graphs::StackGraphLanguage; - -static FILE_PATH_VAR: &'static str = "FILE_PATH"; +use tree_sitter_stack_graphs::FILE_PATH_VAR; mod builder; mod edges; @@ -35,7 +34,6 @@ pub(self) fn build_stack_graph( let mut globals = Variables::new(); let source_path = Path::new(file_name); - // The FILE_PATH_VAR is not accessible here globals .add(FILE_PATH_VAR.into(), source_path.to_str().unwrap().into()) .expect("failed to add file path variable"); From 5ba472ab81783908360aaf3d1b8b15d2e2b85534 Mon Sep 17 00:00:00 2001 From: nohehf Date: Sat, 1 Jun 2024 23:31:00 +0200 Subject: [PATCH 256/291] style: style --- tree-sitter-stack-graphs/src/loader.rs | 3 +-- tree-sitter-stack-graphs/tests/it/builder.rs | 1 + 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tree-sitter-stack-graphs/src/loader.rs b/tree-sitter-stack-graphs/src/loader.rs index f0ab9891c..e0e1d236d 100644 --- a/tree-sitter-stack-graphs/src/loader.rs +++ b/tree-sitter-stack-graphs/src/loader.rs @@ -334,8 +334,7 @@ impl Loader { cancellation_flag: &dyn CancellationFlag, ) -> Result<(), LoadError<'a>> { let file_name = path.to_string_lossy(); - let file: stack_graphs::arena::Handle = - graph.add_file(&file_name).unwrap(); + let file = graph.add_file(&file_name).unwrap(); let mut globals = Variables::new(); globals diff --git a/tree-sitter-stack-graphs/tests/it/builder.rs b/tree-sitter-stack-graphs/tests/it/builder.rs index acc012eaa..14476111e 100644 --- a/tree-sitter-stack-graphs/tests/it/builder.rs +++ b/tree-sitter-stack-graphs/tests/it/builder.rs @@ -4,6 +4,7 @@ // Licensed under either of Apache License, Version 2.0, or MIT license, at your option. // Please see the LICENSE-APACHE or LICENSE-MIT files in this distribution for license details. // ------------------------------------------------------------------------------------------------ + use stack_graphs::graph::StackGraph; use tree_sitter_graph::Variables; use tree_sitter_stack_graphs::NoCancellation; From ba1d3cd125520583a4e56f5e11c92e34f1f855cf Mon Sep 17 00:00:00 2001 From: nohehf Date: Sun, 2 Jun 2024 00:09:42 +0200 Subject: [PATCH 257/291] fix: conditionnaly load FILE_PATH --- tree-sitter-stack-graphs/src/loader.rs | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/tree-sitter-stack-graphs/src/loader.rs b/tree-sitter-stack-graphs/src/loader.rs index e0e1d236d..d382f72c9 100644 --- a/tree-sitter-stack-graphs/src/loader.rs +++ b/tree-sitter-stack-graphs/src/loader.rs @@ -79,13 +79,16 @@ impl LanguageConfiguration { if let Some((builtins_path, builtins_source)) = builtins_source { let mut builtins_globals = Variables::new(); - builtins_globals - .add(FILE_PATH_VAR.into(), BUILTINS_FILENAME.into()) - .expect("failed to add file path variable"); - if let Some(builtins_config) = builtins_config { Loader::load_globals_from_config_str(builtins_config, &mut builtins_globals)?; } + + if builtins_globals.get(&FILE_PATH_VAR.into()).is_some() { + builtins_globals + .add(FILE_PATH_VAR.into(), BUILTINS_FILENAME.into()) + .expect("failed to add file path variable"); + } + let file = builtins.add_file(BUILTINS_FILENAME).unwrap(); sgl.build_stack_graph_into( &mut builtins, @@ -337,11 +340,14 @@ impl Loader { let file = graph.add_file(&file_name).unwrap(); let mut globals = Variables::new(); - globals - .add(FILE_PATH_VAR.into(), BUILTINS_FILENAME.into()) - .expect("failed to add file path variable"); - Self::load_globals_from_config_str(&config, &mut globals)?; + + if globals.get(&FILE_PATH_VAR.into()).is_some() { + globals + .add(FILE_PATH_VAR.into(), BUILTINS_FILENAME.into()) + .expect("failed to add file path variable"); + } + sgl.build_stack_graph_into(graph, file, &source, &globals, cancellation_flag) .map_err(|err| LoadError::Builtins { inner: err, From a297647992b4a2f37e4aa11a3411039849d80a65 Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Sun, 2 Jun 2024 11:31:51 +0200 Subject: [PATCH 258/291] Invert condition --- tree-sitter-stack-graphs/src/loader.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tree-sitter-stack-graphs/src/loader.rs b/tree-sitter-stack-graphs/src/loader.rs index d382f72c9..d03be0f93 100644 --- a/tree-sitter-stack-graphs/src/loader.rs +++ b/tree-sitter-stack-graphs/src/loader.rs @@ -83,7 +83,7 @@ impl LanguageConfiguration { Loader::load_globals_from_config_str(builtins_config, &mut builtins_globals)?; } - if builtins_globals.get(&FILE_PATH_VAR.into()).is_some() { + if builtins_globals.get(&FILE_PATH_VAR.into()).is_none() { builtins_globals .add(FILE_PATH_VAR.into(), BUILTINS_FILENAME.into()) .expect("failed to add file path variable"); From 6bcb5a3c3e39ed16db9ceaf2ae3eac03f4e337ce Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Sun, 2 Jun 2024 11:32:08 +0200 Subject: [PATCH 259/291] Invery condition --- tree-sitter-stack-graphs/src/loader.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tree-sitter-stack-graphs/src/loader.rs b/tree-sitter-stack-graphs/src/loader.rs index d03be0f93..977522528 100644 --- a/tree-sitter-stack-graphs/src/loader.rs +++ b/tree-sitter-stack-graphs/src/loader.rs @@ -342,7 +342,7 @@ impl Loader { Self::load_globals_from_config_str(&config, &mut globals)?; - if globals.get(&FILE_PATH_VAR.into()).is_some() { + if globals.get(&FILE_PATH_VAR.into()).is_none() { globals .add(FILE_PATH_VAR.into(), BUILTINS_FILENAME.into()) .expect("failed to add file path variable"); From c7f9fd9118b0642ae800d4c42237be577afa7d41 Mon Sep 17 00:00:00 2001 From: nohehf Date: Sun, 2 Jun 2024 16:34:44 +0200 Subject: [PATCH 260/291] fix: check that FILE_PATH variable is none everywhere --- tree-sitter-stack-graphs/src/cli/test.rs | 18 ++++++++++-------- tree-sitter-stack-graphs/tests/it/test.rs | 17 ++++++++++------- 2 files changed, 20 insertions(+), 15 deletions(-) diff --git a/tree-sitter-stack-graphs/src/cli/test.rs b/tree-sitter-stack-graphs/src/cli/test.rs index 6ae04114c..790a6c245 100644 --- a/tree-sitter-stack-graphs/src/cli/test.rs +++ b/tree-sitter-stack-graphs/src/cli/test.rs @@ -318,15 +318,17 @@ impl TestArgs { )? { globals.clear(); - // Add FILE_PATH to variables - globals - .add( - FILE_PATH_VAR.into(), - test_fragment.path.to_str().unwrap().into(), - ) - .expect("failed to add file path variable"); - test_fragment.add_globals_to(&mut globals); + + if globals.get(&FILE_PATH_VAR.into()).is_none() { + globals + .add( + FILE_PATH_VAR.into(), + test_fragment.path.to_str().unwrap().into(), + ) + .expect("failed to add file path variable"); + } + lc.sgl.build_stack_graph_into( &mut test.graph, test_fragment.file, diff --git a/tree-sitter-stack-graphs/tests/it/test.rs b/tree-sitter-stack-graphs/tests/it/test.rs index 9624439c5..4c630d57a 100644 --- a/tree-sitter-stack-graphs/tests/it/test.rs +++ b/tree-sitter-stack-graphs/tests/it/test.rs @@ -100,14 +100,17 @@ fn check_test( for fragments in &test.fragments { globals.clear(); - globals - .add( - FILE_PATH_VAR.into(), - fragments.path.to_str().unwrap().into(), - ) - .expect("failed to add file path variable"); - fragments.add_globals_to(&mut globals); + + if globals.get(&FILE_PATH_VAR.into()).is_none() { + globals + .add( + FILE_PATH_VAR.into(), + fragments.path.to_str().unwrap().into(), + ) + .expect("failed to add file path variable"); + } + build_stack_graph_into( &mut test.graph, fragments.file, From 20ad4befe1f8083ddcccf74e2e4b2fe261287fce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cristia=CC=81n=20Arenas=20Ulloa?= Date: Fri, 28 Jun 2024 20:47:14 -0400 Subject: [PATCH 261/291] Update `tree-sitter-java` from `0.20.0` to `0.20.2` --- languages/tree-sitter-stack-graphs-java/Cargo.toml | 2 +- languages/tree-sitter-stack-graphs-java/src/stack-graphs.tsg | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/languages/tree-sitter-stack-graphs-java/Cargo.toml b/languages/tree-sitter-stack-graphs-java/Cargo.toml index 13e9e7340..d50465aae 100644 --- a/languages/tree-sitter-stack-graphs-java/Cargo.toml +++ b/languages/tree-sitter-stack-graphs-java/Cargo.toml @@ -40,7 +40,7 @@ cli = ["anyhow", "clap", "tree-sitter-stack-graphs/cli"] [dependencies] anyhow = { version = "1.0", optional = true } clap = { version = "4", features = ["derive"], optional = true } -tree-sitter-java = { version = "=0.20.0" } +tree-sitter-java = { version = "=0.20.2" } tree-sitter-stack-graphs = { version = "0.8", path = "../../tree-sitter-stack-graphs" } [dev-dependencies] diff --git a/languages/tree-sitter-stack-graphs-java/src/stack-graphs.tsg b/languages/tree-sitter-stack-graphs-java/src/stack-graphs.tsg index 5f3c72466..cfeab55de 100644 --- a/languages/tree-sitter-stack-graphs-java/src/stack-graphs.tsg +++ b/languages/tree-sitter-stack-graphs-java/src/stack-graphs.tsg @@ -1078,6 +1078,10 @@ attribute node_symbol = node => symbol = (source-text node), source_n edge @child.lexical_scope -> @expr.lexical_scope } +(condition (_) @child) @expr { + edge @child.lexical_scope -> @expr.lexical_scope +} + ;; ============= ;; Expressions ;; ============= @@ -1107,6 +1111,7 @@ attribute node_symbol = node => symbol = (source-text node), source_n (this) ; (identifier) (parenthesized_expression) + (condition) (object_creation_expression) (field_access) (array_access) From 14b7556df1353773cc04b8efa0c745ab8424e201 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cristia=CC=81n=20Arenas=20Ulloa?= Date: Mon, 1 Jul 2024 18:14:54 -0400 Subject: [PATCH 262/291] Update `tree-sitter-javascript` from `0.20.3` to `0.20.4` --- languages/tree-sitter-stack-graphs-javascript/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/languages/tree-sitter-stack-graphs-javascript/Cargo.toml b/languages/tree-sitter-stack-graphs-javascript/Cargo.toml index b56925c75..49dc9890d 100644 --- a/languages/tree-sitter-stack-graphs-javascript/Cargo.toml +++ b/languages/tree-sitter-stack-graphs-javascript/Cargo.toml @@ -32,7 +32,7 @@ serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" stack-graphs = { version = "0.13", path = "../../stack-graphs" } tree-sitter-graph = "0.11.2" -tree-sitter-javascript = "=0.20.3" +tree-sitter-javascript = "=0.20.4" tree-sitter-stack-graphs = { version = "0.8", path = "../../tree-sitter-stack-graphs" } [dev-dependencies] From f715ed1ba214cc91bbfb3da8a0fc8650635db264 Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Tue, 9 Jul 2024 15:09:05 +0200 Subject: [PATCH 263/291] Release stack-graphs v0.14.0 --- languages/tree-sitter-stack-graphs-javascript/Cargo.toml | 2 +- languages/tree-sitter-stack-graphs-typescript/Cargo.toml | 2 +- stack-graphs/CHANGELOG.md | 6 ++++++ stack-graphs/Cargo.toml | 2 +- stack-graphs/README.md | 2 +- tree-sitter-stack-graphs/Cargo.toml | 2 +- 6 files changed, 11 insertions(+), 5 deletions(-) diff --git a/languages/tree-sitter-stack-graphs-javascript/Cargo.toml b/languages/tree-sitter-stack-graphs-javascript/Cargo.toml index 49dc9890d..07c21edb8 100644 --- a/languages/tree-sitter-stack-graphs-javascript/Cargo.toml +++ b/languages/tree-sitter-stack-graphs-javascript/Cargo.toml @@ -30,7 +30,7 @@ anyhow = { version = "1.0", optional = true } clap = { version = "4", optional = true } serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" -stack-graphs = { version = "0.13", path = "../../stack-graphs" } +stack-graphs = { path = "../../stack-graphs" } tree-sitter-graph = "0.11.2" tree-sitter-javascript = "=0.20.4" tree-sitter-stack-graphs = { version = "0.8", path = "../../tree-sitter-stack-graphs" } diff --git a/languages/tree-sitter-stack-graphs-typescript/Cargo.toml b/languages/tree-sitter-stack-graphs-typescript/Cargo.toml index e40c12625..988007874 100644 --- a/languages/tree-sitter-stack-graphs-typescript/Cargo.toml +++ b/languages/tree-sitter-stack-graphs-typescript/Cargo.toml @@ -32,7 +32,7 @@ clap = { version = "4", optional = true } glob = "0.3" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" -stack-graphs = { version = "0.13", path = "../../stack-graphs" } +stack-graphs = { path = "../../stack-graphs" } tree-sitter-stack-graphs = { version = "0.8", path = "../../tree-sitter-stack-graphs" } tree-sitter-typescript = "=0.20.2" tsconfig = "0.1.0" diff --git a/stack-graphs/CHANGELOG.md b/stack-graphs/CHANGELOG.md index fa051176d..eeaae0730 100644 --- a/stack-graphs/CHANGELOG.md +++ b/stack-graphs/CHANGELOG.md @@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## v0.14.0 -- 2024-07-09 + +### Changed + +- The method `StackGraph::add_from_graph` returns the handles of the newly added files. + ## v0.13.0 -- 2024-03-06 ### Added diff --git a/stack-graphs/Cargo.toml b/stack-graphs/Cargo.toml index 2e40b76eb..354adaa2b 100644 --- a/stack-graphs/Cargo.toml +++ b/stack-graphs/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "stack-graphs" -version = "0.13.0" +version = "0.14.0" description = "Name binding for arbitrary programming languages" homepage = "https://github.com/github/stack-graphs/tree/main/stack-graphs" repository = "https://github.com/github/stack-graphs/" diff --git a/stack-graphs/README.md b/stack-graphs/README.md index 0c6a9e878..69efa5bd8 100644 --- a/stack-graphs/README.md +++ b/stack-graphs/README.md @@ -9,7 +9,7 @@ To use this library, add the following to your `Cargo.toml`: ``` toml [dependencies] -stack-graphs = "0.13" +stack-graphs = "0.14" ``` Check out our [documentation](https://docs.rs/stack-graphs/) for more details on diff --git a/tree-sitter-stack-graphs/Cargo.toml b/tree-sitter-stack-graphs/Cargo.toml index d1b0fc1c6..ed6fb44e0 100644 --- a/tree-sitter-stack-graphs/Cargo.toml +++ b/tree-sitter-stack-graphs/Cargo.toml @@ -69,7 +69,7 @@ regex = "1" rust-ini = "0.18" serde_json = { version="1.0", optional=true } sha1 = { version="0.10", optional=true } -stack-graphs = { version="0.13", path="../stack-graphs" } +stack-graphs = { path="../stack-graphs" } thiserror = "1.0" time = { version = "0.3", optional = true } tokio = { version = "1.26", optional = true, features = ["io-std", "rt", "rt-multi-thread"] } From 106c728e1da798e410e999473c4e02cf3e5f9d1d Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Tue, 9 Jul 2024 15:47:20 +0200 Subject: [PATCH 264/291] Release tree-sitter-stack-graphs v0.9.0 Includes a bit of code cleanup. --- .../tree-sitter-stack-graphs-java/Cargo.toml | 4 +-- .../tree-sitter-stack-graphs-java/rust/lib.rs | 2 -- .../Cargo.toml | 4 +-- .../rust/lib.rs | 3 +- .../Cargo.toml | 4 +-- .../rust/lib.rs | 3 -- .../Cargo.toml | 4 +-- .../rust/lib.rs | 2 -- tree-sitter-stack-graphs/CHANGELOG.md | 13 +++++++- tree-sitter-stack-graphs/Cargo.toml | 2 +- tree-sitter-stack-graphs/README.md | 2 +- tree-sitter-stack-graphs/npm/.gitignore | 4 --- tree-sitter-stack-graphs/npm/README.md | 13 -------- tree-sitter-stack-graphs/npm/cli.js | 15 ---------- tree-sitter-stack-graphs/npm/install.js | 30 ------------------- tree-sitter-stack-graphs/npm/package.json | 26 ---------------- tree-sitter-stack-graphs/src/cli/init.rs | 3 -- tree-sitter-stack-graphs/src/cli/test.rs | 14 ++++----- tree-sitter-stack-graphs/src/lib.rs | 12 ++++---- tree-sitter-stack-graphs/src/loader.rs | 16 ++++------ tree-sitter-stack-graphs/tests/it/test.rs | 14 ++++----- 21 files changed, 46 insertions(+), 144 deletions(-) delete mode 100644 tree-sitter-stack-graphs/npm/.gitignore delete mode 100644 tree-sitter-stack-graphs/npm/README.md delete mode 100755 tree-sitter-stack-graphs/npm/cli.js delete mode 100644 tree-sitter-stack-graphs/npm/install.js delete mode 100644 tree-sitter-stack-graphs/npm/package.json diff --git a/languages/tree-sitter-stack-graphs-java/Cargo.toml b/languages/tree-sitter-stack-graphs-java/Cargo.toml index d50465aae..21a856760 100644 --- a/languages/tree-sitter-stack-graphs-java/Cargo.toml +++ b/languages/tree-sitter-stack-graphs-java/Cargo.toml @@ -41,8 +41,8 @@ cli = ["anyhow", "clap", "tree-sitter-stack-graphs/cli"] anyhow = { version = "1.0", optional = true } clap = { version = "4", features = ["derive"], optional = true } tree-sitter-java = { version = "=0.20.2" } -tree-sitter-stack-graphs = { version = "0.8", path = "../../tree-sitter-stack-graphs" } +tree-sitter-stack-graphs = { path = "../../tree-sitter-stack-graphs" } [dev-dependencies] anyhow = { version = "1.0" } -tree-sitter-stack-graphs = { version = "0.8", path = "../../tree-sitter-stack-graphs", features = ["cli"] } +tree-sitter-stack-graphs = { path = "../../tree-sitter-stack-graphs", features = ["cli"] } diff --git a/languages/tree-sitter-stack-graphs-java/rust/lib.rs b/languages/tree-sitter-stack-graphs-java/rust/lib.rs index 942785044..14016b614 100644 --- a/languages/tree-sitter-stack-graphs-java/rust/lib.rs +++ b/languages/tree-sitter-stack-graphs-java/rust/lib.rs @@ -14,8 +14,6 @@ pub const STACK_GRAPHS_BUILTINS_PATH: &str = "src/builtins.java"; /// The stack graphs builtins source for this language. pub const STACK_GRAPHS_BUILTINS_SOURCE: &str = include_str!("../src/builtins.java"); -/// The name of the file path global variable -pub const FILE_PATH_VAR: &str = "FILE_PATH"; /// The name of the project name global variable pub const PROJECT_NAME_VAR: &str = "PROJECT_NAME"; diff --git a/languages/tree-sitter-stack-graphs-javascript/Cargo.toml b/languages/tree-sitter-stack-graphs-javascript/Cargo.toml index 07c21edb8..6593327f6 100644 --- a/languages/tree-sitter-stack-graphs-javascript/Cargo.toml +++ b/languages/tree-sitter-stack-graphs-javascript/Cargo.toml @@ -33,8 +33,8 @@ serde_json = "1.0" stack-graphs = { path = "../../stack-graphs" } tree-sitter-graph = "0.11.2" tree-sitter-javascript = "=0.20.4" -tree-sitter-stack-graphs = { version = "0.8", path = "../../tree-sitter-stack-graphs" } +tree-sitter-stack-graphs = { path = "../../tree-sitter-stack-graphs" } [dev-dependencies] anyhow = "1.0" -tree-sitter-stack-graphs = { version = "0.8", path = "../../tree-sitter-stack-graphs", features = ["cli"] } +tree-sitter-stack-graphs = { path = "../../tree-sitter-stack-graphs", features = ["cli"] } diff --git a/languages/tree-sitter-stack-graphs-javascript/rust/lib.rs b/languages/tree-sitter-stack-graphs-javascript/rust/lib.rs index 61f113149..718db8a2e 100644 --- a/languages/tree-sitter-stack-graphs-javascript/rust/lib.rs +++ b/languages/tree-sitter-stack-graphs-javascript/rust/lib.rs @@ -26,8 +26,7 @@ pub const STACK_GRAPHS_BUILTINS_PATH: &str = "src/builtins.js"; /// The stack graphs builtins source for this language. pub const STACK_GRAPHS_BUILTINS_SOURCE: &str = include_str!("../src/builtins.js"); -/// The name of the file path global variable. -pub const FILE_PATH_VAR: &str = "FILE_PATH"; +/// The name of the project name global variable pub const PROJECT_NAME_VAR: &str = "PROJECT_NAME"; pub fn language_configuration(cancellation_flag: &dyn CancellationFlag) -> LanguageConfiguration { diff --git a/languages/tree-sitter-stack-graphs-python/Cargo.toml b/languages/tree-sitter-stack-graphs-python/Cargo.toml index 17cfa3d72..b57c039a8 100644 --- a/languages/tree-sitter-stack-graphs-python/Cargo.toml +++ b/languages/tree-sitter-stack-graphs-python/Cargo.toml @@ -30,9 +30,9 @@ cli = ["anyhow", "clap", "tree-sitter-stack-graphs/cli"] [dependencies] anyhow = { version = "1.0", optional = true } clap = { version = "4", optional = true, features = ["derive"] } -tree-sitter-stack-graphs = { version = "0.8", path = "../../tree-sitter-stack-graphs" } +tree-sitter-stack-graphs = { path = "../../tree-sitter-stack-graphs" } tree-sitter-python = "=0.20.4" [dev-dependencies] anyhow = "1.0" -tree-sitter-stack-graphs = { version = "0.8", path = "../../tree-sitter-stack-graphs", features = ["cli"] } +tree-sitter-stack-graphs = { path = "../../tree-sitter-stack-graphs", features = ["cli"] } diff --git a/languages/tree-sitter-stack-graphs-python/rust/lib.rs b/languages/tree-sitter-stack-graphs-python/rust/lib.rs index 74176a475..e0ffb40ec 100644 --- a/languages/tree-sitter-stack-graphs-python/rust/lib.rs +++ b/languages/tree-sitter-stack-graphs-python/rust/lib.rs @@ -21,9 +21,6 @@ pub const STACK_GRAPHS_BUILTINS_PATH: &str = "src/builtins.py"; /// The stack graphs builtins source for this language. pub const STACK_GRAPHS_BUILTINS_SOURCE: &str = include_str!("../src/builtins.py"); -/// The name of the file path global variable. -pub const FILE_PATH_VAR: &str = "FILE_PATH"; - pub fn language_configuration(cancellation_flag: &dyn CancellationFlag) -> LanguageConfiguration { try_language_configuration(cancellation_flag).unwrap_or_else(|err| panic!("{}", err)) } diff --git a/languages/tree-sitter-stack-graphs-typescript/Cargo.toml b/languages/tree-sitter-stack-graphs-typescript/Cargo.toml index 988007874..1d7df3dbc 100644 --- a/languages/tree-sitter-stack-graphs-typescript/Cargo.toml +++ b/languages/tree-sitter-stack-graphs-typescript/Cargo.toml @@ -33,13 +33,13 @@ glob = "0.3" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" stack-graphs = { path = "../../stack-graphs" } -tree-sitter-stack-graphs = { version = "0.8", path = "../../tree-sitter-stack-graphs" } +tree-sitter-stack-graphs = { path = "../../tree-sitter-stack-graphs" } tree-sitter-typescript = "=0.20.2" tsconfig = "0.1.0" [dev-dependencies] anyhow = { version = "1.0" } -tree-sitter-stack-graphs = { version = "0.8", path = "../../tree-sitter-stack-graphs", features = ["cli"] } +tree-sitter-stack-graphs = { path = "../../tree-sitter-stack-graphs", features = ["cli"] } [build-dependencies] anyhow = { version = "1.0" } diff --git a/languages/tree-sitter-stack-graphs-typescript/rust/lib.rs b/languages/tree-sitter-stack-graphs-typescript/rust/lib.rs index 396fbc68b..2fa484a0a 100644 --- a/languages/tree-sitter-stack-graphs-typescript/rust/lib.rs +++ b/languages/tree-sitter-stack-graphs-typescript/rust/lib.rs @@ -31,8 +31,6 @@ pub const STACK_GRAPHS_BUILTINS_PATH: &str = "src/builtins.ts"; /// The stack graphs builtins source for this language pub const STACK_GRAPHS_BUILTINS_SOURCE: &str = include_str!("../src/builtins.ts"); -/// The name of the file path global variable -pub const FILE_PATH_VAR: &str = "FILE_PATH"; /// The name of the project name global variable pub const PROJECT_NAME_VAR: &str = "PROJECT_NAME"; diff --git a/tree-sitter-stack-graphs/CHANGELOG.md b/tree-sitter-stack-graphs/CHANGELOG.md index e7538592a..faa61295a 100644 --- a/tree-sitter-stack-graphs/CHANGELOG.md +++ b/tree-sitter-stack-graphs/CHANGELOG.md @@ -5,18 +5,29 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## v0.8.2 -- unreleased +## v0.9.0 -- 2024-07-09 + + +### Library + +- New crate-level constants `FILE_PATH_VAR` and `ROOT_PATH_VAR` standardize the TSG global variable names to use for the file and root path. +- The file path variable will only use the filename set in the stack graph if no value was explicitly set. ### CLI #### Added - Tests run faster for languages with builtins sources by caching the partial paths for the builtins. +- Indexing will set a value for the root path variable that is passed to TSG. The value is based on the directory that was provided on the command line. #### Changed - Failure to index a file will not abort indexing anymore, but simply mark the file as failed, as we already do for files with parse errors. +#### Removed + +- The NPM distribution has been deprecated. + ## v0.8.1 -- 2024-03-06 The `stack-graphs` dependency was updated to `v0.13` to fix the build problems of the `v0.8.0` release. diff --git a/tree-sitter-stack-graphs/Cargo.toml b/tree-sitter-stack-graphs/Cargo.toml index ed6fb44e0..327ff4d47 100644 --- a/tree-sitter-stack-graphs/Cargo.toml +++ b/tree-sitter-stack-graphs/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "tree-sitter-stack-graphs" -version = "0.8.2" +version = "0.9.0" description = "Create stack graphs using tree-sitter parsers" homepage = "https://github.com/github/stack-graphs/tree/main/tree-sitter-stack-graphs" repository = "https://github.com/github/stack-graphs/" diff --git a/tree-sitter-stack-graphs/README.md b/tree-sitter-stack-graphs/README.md index ad1287ad5..a4e52add3 100644 --- a/tree-sitter-stack-graphs/README.md +++ b/tree-sitter-stack-graphs/README.md @@ -14,7 +14,7 @@ To use this library, add the following to your `Cargo.toml`: ```toml [dependencies] -tree-sitter-stack-graphs = "0.8" +tree-sitter-stack-graphs = "0.9" ``` Check out our [documentation](https://docs.rs/tree-sitter-stack-graphs/*/) for more details on how to use this library. diff --git a/tree-sitter-stack-graphs/npm/.gitignore b/tree-sitter-stack-graphs/npm/.gitignore deleted file mode 100644 index 02b3615e1..000000000 --- a/tree-sitter-stack-graphs/npm/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -/.crates.toml -/.crates2.json -/bin/ -/package-lock.json diff --git a/tree-sitter-stack-graphs/npm/README.md b/tree-sitter-stack-graphs/npm/README.md deleted file mode 100644 index 27dd321be..000000000 --- a/tree-sitter-stack-graphs/npm/README.md +++ /dev/null @@ -1,13 +0,0 @@ -# tree-sitter-stack-graphs - -This package provides a convenient way to install the [tree-sitter-stack-graphs](https://crates.io/crates/tree-sitter-stack-graphs) CLI in an NPM project. - -Add it as a dev dependency to an existing project using: - - npm i -D tree-sitter-stack-graphs - -It is also possible to invoke it directly using: - - npx tree-sitter-stack-graphs - -See the tree-sitter-stack-graphs [documentation](https://crates.io/crates/tree-sitter-stack-graphs) for details on usage. diff --git a/tree-sitter-stack-graphs/npm/cli.js b/tree-sitter-stack-graphs/npm/cli.js deleted file mode 100755 index f4d654e15..000000000 --- a/tree-sitter-stack-graphs/npm/cli.js +++ /dev/null @@ -1,15 +0,0 @@ -#!/usr/bin/env node - -const spawn = require("child_process").spawn; -const path = require("path"); - -const tssg = process.platform === "win32" - ? "tree-sitter-stack-graphs.exe" - : "tree-sitter-stack-graphs"; - -spawn( - path.join(__dirname, "bin", tssg), process.argv.slice(2), - { - "stdio": "inherit" - }, -).on('close', process.exit); diff --git a/tree-sitter-stack-graphs/npm/install.js b/tree-sitter-stack-graphs/npm/install.js deleted file mode 100644 index 6ac20756b..000000000 --- a/tree-sitter-stack-graphs/npm/install.js +++ /dev/null @@ -1,30 +0,0 @@ -#!/usr/bin/env node - -const child_process = require("child_process"); -const packageJSON = require("./package.json"); - -const cargo = process.platform === "win32" - ? "cargo.exe" - : "cargo"; - -try { - child_process.execSync(cargo); -} catch (error) { - console.error(error.message); - console.error("Failed to execute Cargo. Cargo needs to be available to install this package!"); - process.exit(1); -} - -child_process.spawn( - cargo, [ - "install", - "--quiet", - "--root", ".", - "--version", "^"+packageJSON.version, - "--features", "cli", - packageJSON.name, - ], - { - "stdio": "inherit" - }, -).on('close', process.exit); diff --git a/tree-sitter-stack-graphs/npm/package.json b/tree-sitter-stack-graphs/npm/package.json deleted file mode 100644 index a49ec5fad..000000000 --- a/tree-sitter-stack-graphs/npm/package.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "name": "tree-sitter-stack-graphs", - "version": "0.7.0", - "description": "Create stack graphs using tree-sitter parsers", - "homepage": "https://github.com/github/stack-graphs/tree/main/tree-sitter-stack-graphs", - "repository": { - "type": "git", - "url": "https://github.com/github/stack-graphs.git" - }, - "keywords": [ - "tree-sitter", - "stack-graphs" - ], - "license": "MIT OR Apache-2.0", - "author": "GitHub ", - "contributors": [ - "Douglas Creager ", - "Hendrik van Antwerpen " - ], - "bin": { - "tree-sitter-stack-graphs": "./cli.js" - }, - "scripts": { - "install": "node install.js" - } -} diff --git a/tree-sitter-stack-graphs/src/cli/init.rs b/tree-sitter-stack-graphs/src/cli/init.rs index df18339aa..f71890390 100644 --- a/tree-sitter-stack-graphs/src/cli/init.rs +++ b/tree-sitter-stack-graphs/src/cli/init.rs @@ -770,9 +770,6 @@ impl ProjectSettings<'_> { /// The stack graphs builtins source for this language. pub const STACK_GRAPHS_BUILTINS_SOURCE: &str = include_str!("../src/builtins.{}"); - /// The name of the file path global variable. - pub const FILE_PATH_VAR: &str = "FILE_PATH"; - pub fn language_configuration(cancellation_flag: &dyn CancellationFlag) -> LanguageConfiguration {{ try_language_configuration(cancellation_flag).unwrap_or_else(|err| panic!("{{}}", err)) }} diff --git a/tree-sitter-stack-graphs/src/cli/test.rs b/tree-sitter-stack-graphs/src/cli/test.rs index 790a6c245..1bff78a48 100644 --- a/tree-sitter-stack-graphs/src/cli/test.rs +++ b/tree-sitter-stack-graphs/src/cli/test.rs @@ -320,14 +320,12 @@ impl TestArgs { test_fragment.add_globals_to(&mut globals); - if globals.get(&FILE_PATH_VAR.into()).is_none() { - globals - .add( - FILE_PATH_VAR.into(), - test_fragment.path.to_str().unwrap().into(), - ) - .expect("failed to add file path variable"); - } + globals + .add( + FILE_PATH_VAR.into(), + test_fragment.path.to_str().unwrap().into(), + ) + .unwrap_or_default(); lc.sgl.build_stack_graph_into( &mut test.graph, diff --git a/tree-sitter-stack-graphs/src/lib.rs b/tree-sitter-stack-graphs/src/lib.rs index 1a3e344b1..717214832 100644 --- a/tree-sitter-stack-graphs/src/lib.rs +++ b/tree-sitter-stack-graphs/src/lib.rs @@ -440,7 +440,7 @@ pub const ROOT_NODE_VAR: &'static str = "ROOT_NODE"; /// Name of the variable used to pass the jump-to-scope node. pub const JUMP_TO_SCOPE_NODE_VAR: &'static str = "JUMP_TO_SCOPE_NODE"; /// Name of the variable used to pass the file path. -/// If a root path is given, it should be a descendant the root path. +/// If a root path is given, it should be a descendant of the root path. pub const FILE_PATH_VAR: &'static str = "FILE_PATH"; /// Name of the variable used to pass the root path. /// If given, should be an ancestor of the file path. @@ -643,12 +643,10 @@ impl<'a> Builder<'a> { let mut globals = Variables::nested(globals); - if globals.get(&ROOT_NODE_VAR.into()).is_none() { - let root_node = self.inject_node(NodeID::root()); - globals - .add(ROOT_NODE_VAR.into(), root_node.into()) - .expect("Failed to set ROOT_NODE"); - } + let root_node = self.inject_node(NodeID::root()); + globals + .add(ROOT_NODE_VAR.into(), root_node.into()) + .unwrap_or_default(); let jump_to_scope_node = self.inject_node(NodeID::jump_to()); globals diff --git a/tree-sitter-stack-graphs/src/loader.rs b/tree-sitter-stack-graphs/src/loader.rs index 977522528..14aa97916 100644 --- a/tree-sitter-stack-graphs/src/loader.rs +++ b/tree-sitter-stack-graphs/src/loader.rs @@ -83,11 +83,9 @@ impl LanguageConfiguration { Loader::load_globals_from_config_str(builtins_config, &mut builtins_globals)?; } - if builtins_globals.get(&FILE_PATH_VAR.into()).is_none() { - builtins_globals - .add(FILE_PATH_VAR.into(), BUILTINS_FILENAME.into()) - .expect("failed to add file path variable"); - } + builtins_globals + .add(FILE_PATH_VAR.into(), BUILTINS_FILENAME.into()) + .unwrap_or_default(); let file = builtins.add_file(BUILTINS_FILENAME).unwrap(); sgl.build_stack_graph_into( @@ -342,11 +340,9 @@ impl Loader { Self::load_globals_from_config_str(&config, &mut globals)?; - if globals.get(&FILE_PATH_VAR.into()).is_none() { - globals - .add(FILE_PATH_VAR.into(), BUILTINS_FILENAME.into()) - .expect("failed to add file path variable"); - } + globals + .add(FILE_PATH_VAR.into(), BUILTINS_FILENAME.into()) + .unwrap_or_default(); sgl.build_stack_graph_into(graph, file, &source, &globals, cancellation_flag) .map_err(|err| LoadError::Builtins { diff --git a/tree-sitter-stack-graphs/tests/it/test.rs b/tree-sitter-stack-graphs/tests/it/test.rs index 4c630d57a..cb1efff4f 100644 --- a/tree-sitter-stack-graphs/tests/it/test.rs +++ b/tree-sitter-stack-graphs/tests/it/test.rs @@ -102,14 +102,12 @@ fn check_test( fragments.add_globals_to(&mut globals); - if globals.get(&FILE_PATH_VAR.into()).is_none() { - globals - .add( - FILE_PATH_VAR.into(), - fragments.path.to_str().unwrap().into(), - ) - .expect("failed to add file path variable"); - } + globals + .add( + FILE_PATH_VAR.into(), + fragments.path.to_str().unwrap().into(), + ) + .unwrap_or_default(); build_stack_graph_into( &mut test.graph, From aff01c50c3d30162004f513573b646b889889fce Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Tue, 9 Jul 2024 16:09:19 +0200 Subject: [PATCH 265/291] Release languages - tree-sitter-stack-graphs-java v0.4.0 - tree-sitter-stack-graphs-javascript v0.2.0 - tree-sitter-stack-graphs-python v0.2.0 - tree-sitter-stack-graphs-typescript v0.3.0 --- .../tree-sitter-stack-graphs-java/CHANGELOG.md | 10 ++++++++++ .../tree-sitter-stack-graphs-java/Cargo.toml | 2 +- .../tree-sitter-stack-graphs-java/README.md | 2 +- .../CHANGELOG.md | 6 ++++++ .../Cargo.toml | 2 +- .../README.md | 2 +- .../tree-sitter-stack-graphs-python/CHANGELOG.md | 15 +++++++++++++++ .../tree-sitter-stack-graphs-python/Cargo.toml | 2 +- .../tree-sitter-stack-graphs-python/LICENSE | 0 .../tree-sitter-stack-graphs-python/README.md | 2 +- .../CHANGELOG.md | 16 ++++++++++++++-- .../README.md | 2 +- 12 files changed, 52 insertions(+), 9 deletions(-) delete mode 100644 languages/tree-sitter-stack-graphs-python/LICENSE diff --git a/languages/tree-sitter-stack-graphs-java/CHANGELOG.md b/languages/tree-sitter-stack-graphs-java/CHANGELOG.md index 0f3a4b861..5691a4985 100644 --- a/languages/tree-sitter-stack-graphs-java/CHANGELOG.md +++ b/languages/tree-sitter-stack-graphs-java/CHANGELOG.md @@ -5,6 +5,16 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## v0.4.0 -- 2024-07-09 + +### Added + +- Add rules for the `condition` node that was missing. + +### Removed + +- The `FILE_PATH_VAR` constant has been replaced in favor of `tree_sitter_stack_graphs::FILE_PATH_VAR`. + ## v0.3.0 -- 2024-03-06 The `tree-sitter-stack-graphs` is updated to `v0.8`. diff --git a/languages/tree-sitter-stack-graphs-java/Cargo.toml b/languages/tree-sitter-stack-graphs-java/Cargo.toml index 21a856760..27534ef88 100644 --- a/languages/tree-sitter-stack-graphs-java/Cargo.toml +++ b/languages/tree-sitter-stack-graphs-java/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "tree-sitter-stack-graphs-java" -version = "0.3.0" +version = "0.4.0" description = "Stack graphs for the Java programming language" homepage = "https://github.com/github/stack-graphs/tree/main/languages/tree-sitter-stack-graphs-java" diff --git a/languages/tree-sitter-stack-graphs-java/README.md b/languages/tree-sitter-stack-graphs-java/README.md index cf16b7dab..69d276118 100644 --- a/languages/tree-sitter-stack-graphs-java/README.md +++ b/languages/tree-sitter-stack-graphs-java/README.md @@ -13,7 +13,7 @@ To use this library, add the following to your `Cargo.toml`: ```toml [dependencies] -tree-sitter-stack-graphs-java = "0.3" +tree-sitter-stack-graphs-java = "0.4" ``` Check out our [documentation](https://docs.rs/tree-sitter-stack-graphs-java/*/) for more details on how to use this library. diff --git a/languages/tree-sitter-stack-graphs-javascript/CHANGELOG.md b/languages/tree-sitter-stack-graphs-javascript/CHANGELOG.md index 2ba28c310..19730aeab 100644 --- a/languages/tree-sitter-stack-graphs-javascript/CHANGELOG.md +++ b/languages/tree-sitter-stack-graphs-javascript/CHANGELOG.md @@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## v0.2.0 -- 2024-07-09 + +### Removed + +- The `FILE_PATH_VAR` constant has been replaced in favor of `tree_sitter_stack_graphs::FILE_PATH_VAR`. + ## v0.1.0 -- 2024-03-06 Initial release. diff --git a/languages/tree-sitter-stack-graphs-javascript/Cargo.toml b/languages/tree-sitter-stack-graphs-javascript/Cargo.toml index 6593327f6..a4344c0f3 100644 --- a/languages/tree-sitter-stack-graphs-javascript/Cargo.toml +++ b/languages/tree-sitter-stack-graphs-javascript/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "tree-sitter-stack-graphs-javascript" -version = "0.1.0" +version = "0.2.0" description = "Stack graphs definition for JavaScript using tree-sitter-javascript" readme = "README.md" keywords = ["tree-sitter", "stack-graphs", "javascript"] diff --git a/languages/tree-sitter-stack-graphs-javascript/README.md b/languages/tree-sitter-stack-graphs-javascript/README.md index 04317fbd0..099e633e9 100644 --- a/languages/tree-sitter-stack-graphs-javascript/README.md +++ b/languages/tree-sitter-stack-graphs-javascript/README.md @@ -13,7 +13,7 @@ To use this library, add the following to your `Cargo.toml`: ```toml [dependencies] -tree-sitter-stack-graphs-javascript = "0.1" +tree-sitter-stack-graphs-javascript = "0.2" ``` Check out our [documentation](https://docs.rs/tree-sitter-stack-graphs-javascript/*/) for more details on how to use this library. diff --git a/languages/tree-sitter-stack-graphs-python/CHANGELOG.md b/languages/tree-sitter-stack-graphs-python/CHANGELOG.md index 876b16cb9..61e6ca4e9 100644 --- a/languages/tree-sitter-stack-graphs-python/CHANGELOG.md +++ b/languages/tree-sitter-stack-graphs-python/CHANGELOG.md @@ -5,6 +5,21 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## v0.2.0 -- 2024-07-09 + +### Added + +- Added support for root paths. This fixes import problems when indexing using absolute directory paths. + +### Fixed + +- Fixed crash for lambdas with parameters. +- Fixed crash for nested functions definitions. + +### Removed + +- The `FILE_PATH_VAR` constant has been replaced in favor of `tree_sitter_stack_graphs::FILE_PATH_VAR`. + ## v0.1.0 -- 2024-03-06 Initial release. diff --git a/languages/tree-sitter-stack-graphs-python/Cargo.toml b/languages/tree-sitter-stack-graphs-python/Cargo.toml index b57c039a8..38a5a191c 100644 --- a/languages/tree-sitter-stack-graphs-python/Cargo.toml +++ b/languages/tree-sitter-stack-graphs-python/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "tree-sitter-stack-graphs-python" -version = "0.1.0" +version = "0.2.0" description = "Stack graphs definition for Python using tree-sitter-python" readme = "README.md" keywords = ["tree-sitter", "stack-graphs", "python"] diff --git a/languages/tree-sitter-stack-graphs-python/LICENSE b/languages/tree-sitter-stack-graphs-python/LICENSE deleted file mode 100644 index e69de29bb..000000000 diff --git a/languages/tree-sitter-stack-graphs-python/README.md b/languages/tree-sitter-stack-graphs-python/README.md index 0393e0004..a313a3799 100644 --- a/languages/tree-sitter-stack-graphs-python/README.md +++ b/languages/tree-sitter-stack-graphs-python/README.md @@ -13,7 +13,7 @@ To use this library, add the following to your `Cargo.toml`: ```toml [dependencies] -tree-sitter-stack-graphs-python = "0.1" +tree-sitter-stack-graphs-python = "0.2" ``` Check out our [documentation](https://docs.rs/tree-sitter-stack-graphs-python/*/) for more details on how to use this library. diff --git a/languages/tree-sitter-stack-graphs-typescript/CHANGELOG.md b/languages/tree-sitter-stack-graphs-typescript/CHANGELOG.md index b4260b2a6..47539831f 100644 --- a/languages/tree-sitter-stack-graphs-typescript/CHANGELOG.md +++ b/languages/tree-sitter-stack-graphs-typescript/CHANGELOG.md @@ -5,11 +5,23 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## v0.3.0 -- unreleased +## v0.3.0 -- 2024-07-09 ### Added -- Support for TSX. A new language configuration for TSX is available, and TSX is enabled in the CLI next to TypeScript. +- Support for TSX. A new language configuration for TSX is available with `{try_,}language_configuration_tsx`. TSX is enabled in the CLI next to TypeScript. + +### Fixed + +- Imports are more robust to the presence of file extensions in the import name. + +### Changed + +- The functions `{try_,}language_configuration` have been renamed to `{try_,}language_configuration_typescript`. + +### Removed + +- The `FILE_PATH_VAR` constant has been replaced in favor of `tree_sitter_stack_graphs::FILE_PATH_VAR`. ## v0.2.0 -- 2024-03-06 diff --git a/languages/tree-sitter-stack-graphs-typescript/README.md b/languages/tree-sitter-stack-graphs-typescript/README.md index 9b6c82069..46079a19a 100644 --- a/languages/tree-sitter-stack-graphs-typescript/README.md +++ b/languages/tree-sitter-stack-graphs-typescript/README.md @@ -13,7 +13,7 @@ To use this library, add the following to your `Cargo.toml`: ```toml [dependencies] -tree-sitter-stack-graphs-typescript = "0.2" +tree-sitter-stack-graphs-typescript = "0.3" ``` Check out our [documentation](https://docs.rs/tree-sitter-stack-graphs-typescript/*/) for more details on how to use this library. From a68b92dde2a39876d4dfdff9fc65a9668afeae63 Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Wed, 10 Jul 2024 16:44:43 +0200 Subject: [PATCH 266/291] Improve changelog --- tree-sitter-stack-graphs/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tree-sitter-stack-graphs/CHANGELOG.md b/tree-sitter-stack-graphs/CHANGELOG.md index faa61295a..6f6e86f25 100644 --- a/tree-sitter-stack-graphs/CHANGELOG.md +++ b/tree-sitter-stack-graphs/CHANGELOG.md @@ -26,7 +26,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 #### Removed -- The NPM distribution has been deprecated. +- The NPM distribution has been removed. ## v0.8.1 -- 2024-03-06 From 5fcc8517eb3ee045046fd5cff3b7ef79dedc9f4a Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Wed, 10 Jul 2024 17:07:22 +0200 Subject: [PATCH 267/291] Remove NPM publish workflow job --- .../publish-tree-sitter-stack-graphs.yml | 25 ------------------- 1 file changed, 25 deletions(-) diff --git a/.github/workflows/publish-tree-sitter-stack-graphs.yml b/.github/workflows/publish-tree-sitter-stack-graphs.yml index e9395ab1f..031289464 100644 --- a/.github/workflows/publish-tree-sitter-stack-graphs.yml +++ b/.github/workflows/publish-tree-sitter-stack-graphs.yml @@ -29,31 +29,6 @@ jobs: working-directory: ${{ env.CRATE_DIR }} env: CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} - publish-npm: - needs: publish-crate - runs-on: ubuntu-latest - env: - PACKAGE_DIR: './tree-sitter-stack-graphs/npm' - steps: - - name: Install Node environment - uses: actions/setup-node@v3 - with: - node-version: 16.x - registry-url: 'https://registry.npmjs.org' - - name: Checkout repository - uses: actions/checkout@v3 - # TODO Verify the package version matches the tag - - name: Install dependencies - run: npm install - working-directory: ${{ env.PACKAGE_DIR }} - - name: Verify package - run: npm publish --dry-run - working-directory: ${{ env.PACKAGE_DIR }} - - name: Publish package - run: npm publish - working-directory: ${{ env.PACKAGE_DIR }} - env: - NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} create-release: needs: publish-crate runs-on: ubuntu-latest From 9649181818590f321fd4538e2e0d70edbd903a84 Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Wed, 10 Jul 2024 17:10:33 +0200 Subject: [PATCH 268/291] Restore versions for path dependencies, which are required to publish the crate --- languages/tree-sitter-stack-graphs-java/Cargo.toml | 2 +- languages/tree-sitter-stack-graphs-javascript/Cargo.toml | 4 ++-- languages/tree-sitter-stack-graphs-python/Cargo.toml | 2 +- languages/tree-sitter-stack-graphs-typescript/Cargo.toml | 4 ++-- stack-graphs/Cargo.toml | 2 +- tree-sitter-stack-graphs/Cargo.toml | 4 ++-- 6 files changed, 9 insertions(+), 9 deletions(-) diff --git a/languages/tree-sitter-stack-graphs-java/Cargo.toml b/languages/tree-sitter-stack-graphs-java/Cargo.toml index 27534ef88..96a44ab0f 100644 --- a/languages/tree-sitter-stack-graphs-java/Cargo.toml +++ b/languages/tree-sitter-stack-graphs-java/Cargo.toml @@ -41,7 +41,7 @@ cli = ["anyhow", "clap", "tree-sitter-stack-graphs/cli"] anyhow = { version = "1.0", optional = true } clap = { version = "4", features = ["derive"], optional = true } tree-sitter-java = { version = "=0.20.2" } -tree-sitter-stack-graphs = { path = "../../tree-sitter-stack-graphs" } +tree-sitter-stack-graphs = { version = "0.9", path = "../../tree-sitter-stack-graphs" } # explicit version is required to be able to publish crate [dev-dependencies] anyhow = { version = "1.0" } diff --git a/languages/tree-sitter-stack-graphs-javascript/Cargo.toml b/languages/tree-sitter-stack-graphs-javascript/Cargo.toml index a4344c0f3..ca03e2f9b 100644 --- a/languages/tree-sitter-stack-graphs-javascript/Cargo.toml +++ b/languages/tree-sitter-stack-graphs-javascript/Cargo.toml @@ -30,10 +30,10 @@ anyhow = { version = "1.0", optional = true } clap = { version = "4", optional = true } serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" -stack-graphs = { path = "../../stack-graphs" } +stack-graphs = { version = "0.14", path = "../../stack-graphs" } # explicit version is required to be able to publish crate tree-sitter-graph = "0.11.2" tree-sitter-javascript = "=0.20.4" -tree-sitter-stack-graphs = { path = "../../tree-sitter-stack-graphs" } +tree-sitter-stack-graphs = { version = "0.9", path = "../../tree-sitter-stack-graphs" } # explicit version is required to be able to publish crate [dev-dependencies] anyhow = "1.0" diff --git a/languages/tree-sitter-stack-graphs-python/Cargo.toml b/languages/tree-sitter-stack-graphs-python/Cargo.toml index 38a5a191c..53d714719 100644 --- a/languages/tree-sitter-stack-graphs-python/Cargo.toml +++ b/languages/tree-sitter-stack-graphs-python/Cargo.toml @@ -30,7 +30,7 @@ cli = ["anyhow", "clap", "tree-sitter-stack-graphs/cli"] [dependencies] anyhow = { version = "1.0", optional = true } clap = { version = "4", optional = true, features = ["derive"] } -tree-sitter-stack-graphs = { path = "../../tree-sitter-stack-graphs" } +tree-sitter-stack-graphs = { version = "0.9", path = "../../tree-sitter-stack-graphs" } # explicit version is required to be able to publish crate tree-sitter-python = "=0.20.4" [dev-dependencies] diff --git a/languages/tree-sitter-stack-graphs-typescript/Cargo.toml b/languages/tree-sitter-stack-graphs-typescript/Cargo.toml index 1d7df3dbc..86feee2a1 100644 --- a/languages/tree-sitter-stack-graphs-typescript/Cargo.toml +++ b/languages/tree-sitter-stack-graphs-typescript/Cargo.toml @@ -32,8 +32,8 @@ clap = { version = "4", optional = true } glob = "0.3" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" -stack-graphs = { path = "../../stack-graphs" } -tree-sitter-stack-graphs = { path = "../../tree-sitter-stack-graphs" } +stack-graphs = { version = "0.14", path = "../../stack-graphs" } # explicit version is required to be able to publish crate +tree-sitter-stack-graphs = { version = "0.9", path = "../../tree-sitter-stack-graphs" } # explicit version is required to be able to publish crate tree-sitter-typescript = "=0.20.2" tsconfig = "0.1.0" diff --git a/stack-graphs/Cargo.toml b/stack-graphs/Cargo.toml index 354adaa2b..f114fffdb 100644 --- a/stack-graphs/Cargo.toml +++ b/stack-graphs/Cargo.toml @@ -32,7 +32,7 @@ enumset = "1.1" fxhash = "0.2" itertools = "0.10.2" libc = "0.2" -lsp-positions = { version = "0.3", path = "../lsp-positions" } +lsp-positions = { version = "0.3", path = "../lsp-positions" } # explicit version is required to be able to publish crate rusqlite = { version = "0.28", optional = true, features = ["bundled", "functions"] } serde = { version = "1.0", optional = true, features = ["derive"] } serde_json = { version = "1.0", optional = true } diff --git a/tree-sitter-stack-graphs/Cargo.toml b/tree-sitter-stack-graphs/Cargo.toml index 327ff4d47..0ac84d012 100644 --- a/tree-sitter-stack-graphs/Cargo.toml +++ b/tree-sitter-stack-graphs/Cargo.toml @@ -62,14 +62,14 @@ env_logger = { version = "0.9", optional = true } indoc = { version = "1.0", optional = true } itertools = "0.10" log = "0.4" -lsp-positions = { version="0.3", path="../lsp-positions", features=["tree-sitter"] } +lsp-positions = { version="0.3", path="../lsp-positions", features=["tree-sitter"] } # explicit version is required to be able to publish crate once_cell = "1" pathdiff = { version = "0.2.1", optional = true } regex = "1" rust-ini = "0.18" serde_json = { version="1.0", optional=true } sha1 = { version="0.10", optional=true } -stack-graphs = { path="../stack-graphs" } +stack-graphs = { version = "0.14", path="../stack-graphs" } # explicit version is required to be able to publish crate thiserror = "1.0" time = { version = "0.3", optional = true } tokio = { version = "1.26", optional = true, features = ["io-std", "rt", "rt-multi-thread"] } From a8fe5283413ac79d4188ee2b299f911871d5e86d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 3 Sep 2024 22:20:27 +0000 Subject: [PATCH 269/291] Bump actions/download-artifact Bumps the github_actions group with 1 update in the /.github/workflows directory: [actions/download-artifact](https://github.com/actions/download-artifact). Updates `actions/download-artifact` from 3 to 4.1.7 - [Release notes](https://github.com/actions/download-artifact/releases) - [Commits](https://github.com/actions/download-artifact/compare/v3...v4.1.7) --- updated-dependencies: - dependency-name: actions/download-artifact dependency-type: direct:production dependency-group: github_actions ... Signed-off-by: dependabot[bot] --- .github/workflows/perf.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/perf.yml b/.github/workflows/perf.yml index 5c4288c84..301dffd92 100644 --- a/.github/workflows/perf.yml +++ b/.github/workflows/perf.yml @@ -233,12 +233,12 @@ jobs: # Download results # - name: Download base results - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4.1.7 with: name: ${{ env.BASE_ARTIFACT }} path: ${{ env.BASE_ARTIFACT }} - name: Download head results - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4.1.7 with: name: ${{ env.HEAD_ARTIFACT }} path: ${{ env.HEAD_ARTIFACT }} From 3090222200a9859619f7b5310b0773bc0d4161d7 Mon Sep 17 00:00:00 2001 From: Bastiaan Marinus van de Weerd Date: Tue, 19 Nov 2024 16:47:44 -0300 Subject: [PATCH 270/291] Fix not defaulting to `Builder::file` for `FILE_PATH`. --- tree-sitter-stack-graphs/src/lib.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tree-sitter-stack-graphs/src/lib.rs b/tree-sitter-stack-graphs/src/lib.rs index 717214832..509e34a58 100644 --- a/tree-sitter-stack-graphs/src/lib.rs +++ b/tree-sitter-stack-graphs/src/lib.rs @@ -653,6 +653,13 @@ impl<'a> Builder<'a> { .add(JUMP_TO_SCOPE_NODE_VAR.into(), jump_to_scope_node.into()) .expect("Failed to set JUMP_TO_SCOPE_NODE"); + if globals.get(&FILE_PATH_VAR.into()).is_none() { + let file_name = self.stack_graph[self.file].to_string(); + globals + .add(FILE_PATH_VAR.into(), file_name.into()) + .expect("Failed to set FILE_PATH"); + } + let mut config = ExecutionConfig::new(&self.sgl.functions, &globals) .lazy(true) .debug_attributes( From c2df61c71ff098bd99a34d297d0cfb6def48f4ef Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Tue, 10 Dec 2024 14:51:39 +0100 Subject: [PATCH 271/291] Upgrade tree-sitter dependency to 0.24 --- .../CHANGELOG.md | 6 + .../tree-sitter-stack-graphs-java/Cargo.toml | 6 +- .../tree-sitter-stack-graphs-java/README.md | 2 +- .../tree-sitter-stack-graphs-java/rust/lib.rs | 2 +- .../src/stack-graphs.tsg | 5 - .../CHANGELOG.md | 6 + .../Cargo.toml | 8 +- .../README.md | 2 +- .../rust/lib.rs | 2 +- .../CHANGELOG.md | 6 + .../Cargo.toml | 6 +- .../tree-sitter-stack-graphs-python/README.md | 2 +- .../rust/lib.rs | 2 +- .../Cargo.toml | 4 +- .../rust/lib.rs | 4 +- .../src/stack-graphs.tsg | 163 ++++++++---------- ...e_name.tsx => jsx_namespace_name.tsx.skip} | 0 ...mespace.ts => very-deep-namespace.ts.skip} | 0 ...namespace-by-fully-qualified-name.ts.skip} | 0 ...namespace-by-fully-qualified-name.ts.skip} | 0 lsp-positions/CHANGELOG.md | 4 + lsp-positions/Cargo.toml | 4 +- tree-sitter-stack-graphs/CHANGELOG.md | 5 +- tree-sitter-stack-graphs/Cargo.toml | 14 +- tree-sitter-stack-graphs/README.md | 2 +- tree-sitter-stack-graphs/src/cli/init.rs | 2 +- tree-sitter-stack-graphs/src/cli/load.rs | 2 +- tree-sitter-stack-graphs/src/cli/lsp.rs | 19 -- tree-sitter-stack-graphs/src/cli/match.rs | 2 +- tree-sitter-stack-graphs/src/cli/parse.rs | 4 +- tree-sitter-stack-graphs/src/cli/test.rs | 2 +- tree-sitter-stack-graphs/src/lib.rs | 10 +- tree-sitter-stack-graphs/src/loader.rs | 32 ++-- tree-sitter-stack-graphs/tests/it/builder.rs | 4 +- tree-sitter-stack-graphs/tests/it/loader.rs | 11 +- tree-sitter-stack-graphs/tests/it/main.rs | 2 +- tree-sitter-stack-graphs/tests/it/test.rs | 2 +- 37 files changed, 169 insertions(+), 178 deletions(-) rename languages/tree-sitter-stack-graphs-typescript/test/jsx/{jsx_namespace_name.tsx => jsx_namespace_name.tsx.skip} (100%) rename languages/tree-sitter-stack-graphs-typescript/test/modules/{very-deep-namespace.ts => very-deep-namespace.ts.skip} (100%) rename languages/tree-sitter-stack-graphs-typescript/test/statements/{refer-to-interface-in-nested-namespace-by-fully-qualified-name.ts => refer-to-interface-in-nested-namespace-by-fully-qualified-name.ts.skip} (100%) rename languages/tree-sitter-stack-graphs-typescript/test/statements/{refer-to-interface-in-qualified-namespace-by-fully-qualified-name.ts => refer-to-interface-in-qualified-namespace-by-fully-qualified-name.ts.skip} (100%) diff --git a/languages/tree-sitter-stack-graphs-java/CHANGELOG.md b/languages/tree-sitter-stack-graphs-java/CHANGELOG.md index 5691a4985..23400faf0 100644 --- a/languages/tree-sitter-stack-graphs-java/CHANGELOG.md +++ b/languages/tree-sitter-stack-graphs-java/CHANGELOG.md @@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## v0.5.0 -- Unreleased + +- The `tree-sitter-stack-graphs` dependency is updated to version 0.10. + +- The `tree-sitter-java` dependency is updated to version 0.23.4. + ## v0.4.0 -- 2024-07-09 ### Added diff --git a/languages/tree-sitter-stack-graphs-java/Cargo.toml b/languages/tree-sitter-stack-graphs-java/Cargo.toml index 96a44ab0f..0ad8cb687 100644 --- a/languages/tree-sitter-stack-graphs-java/Cargo.toml +++ b/languages/tree-sitter-stack-graphs-java/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "tree-sitter-stack-graphs-java" -version = "0.4.0" +version = "0.5.0" description = "Stack graphs for the Java programming language" homepage = "https://github.com/github/stack-graphs/tree/main/languages/tree-sitter-stack-graphs-java" @@ -40,8 +40,8 @@ cli = ["anyhow", "clap", "tree-sitter-stack-graphs/cli"] [dependencies] anyhow = { version = "1.0", optional = true } clap = { version = "4", features = ["derive"], optional = true } -tree-sitter-java = { version = "=0.20.2" } -tree-sitter-stack-graphs = { version = "0.9", path = "../../tree-sitter-stack-graphs" } # explicit version is required to be able to publish crate +tree-sitter-java = { version = "=0.23.4" } +tree-sitter-stack-graphs = { version = "0.10", path = "../../tree-sitter-stack-graphs" } # explicit version is required to be able to publish crate [dev-dependencies] anyhow = { version = "1.0" } diff --git a/languages/tree-sitter-stack-graphs-java/README.md b/languages/tree-sitter-stack-graphs-java/README.md index 69d276118..97820ed3f 100644 --- a/languages/tree-sitter-stack-graphs-java/README.md +++ b/languages/tree-sitter-stack-graphs-java/README.md @@ -13,7 +13,7 @@ To use this library, add the following to your `Cargo.toml`: ```toml [dependencies] -tree-sitter-stack-graphs-java = "0.4" +tree-sitter-stack-graphs-java = "0.5" ``` Check out our [documentation](https://docs.rs/tree-sitter-stack-graphs-java/*/) for more details on how to use this library. diff --git a/languages/tree-sitter-stack-graphs-java/rust/lib.rs b/languages/tree-sitter-stack-graphs-java/rust/lib.rs index 14016b614..9b2cb39c8 100644 --- a/languages/tree-sitter-stack-graphs-java/rust/lib.rs +++ b/languages/tree-sitter-stack-graphs-java/rust/lib.rs @@ -25,7 +25,7 @@ pub fn try_language_configuration( cancellation_flag: &dyn CancellationFlag, ) -> Result { LanguageConfiguration::from_sources( - tree_sitter_java::language(), + tree_sitter_java::LANGUAGE.into(), Some(String::from("source.java")), None, vec![String::from("java")], diff --git a/languages/tree-sitter-stack-graphs-java/src/stack-graphs.tsg b/languages/tree-sitter-stack-graphs-java/src/stack-graphs.tsg index cfeab55de..5f3c72466 100644 --- a/languages/tree-sitter-stack-graphs-java/src/stack-graphs.tsg +++ b/languages/tree-sitter-stack-graphs-java/src/stack-graphs.tsg @@ -1078,10 +1078,6 @@ attribute node_symbol = node => symbol = (source-text node), source_n edge @child.lexical_scope -> @expr.lexical_scope } -(condition (_) @child) @expr { - edge @child.lexical_scope -> @expr.lexical_scope -} - ;; ============= ;; Expressions ;; ============= @@ -1111,7 +1107,6 @@ attribute node_symbol = node => symbol = (source-text node), source_n (this) ; (identifier) (parenthesized_expression) - (condition) (object_creation_expression) (field_access) (array_access) diff --git a/languages/tree-sitter-stack-graphs-javascript/CHANGELOG.md b/languages/tree-sitter-stack-graphs-javascript/CHANGELOG.md index 19730aeab..10f85abdd 100644 --- a/languages/tree-sitter-stack-graphs-javascript/CHANGELOG.md +++ b/languages/tree-sitter-stack-graphs-javascript/CHANGELOG.md @@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## v0.3.0 -- Unreleased + +- The `tree-sitter-stack-graphs` dependency is updated to version 0.10. + +- The `tree-sitter-javascript` dependency is updated to version 0.23.1. + ## v0.2.0 -- 2024-07-09 ### Removed diff --git a/languages/tree-sitter-stack-graphs-javascript/Cargo.toml b/languages/tree-sitter-stack-graphs-javascript/Cargo.toml index ca03e2f9b..d1206c6e5 100644 --- a/languages/tree-sitter-stack-graphs-javascript/Cargo.toml +++ b/languages/tree-sitter-stack-graphs-javascript/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "tree-sitter-stack-graphs-javascript" -version = "0.2.0" +version = "0.3.0" description = "Stack graphs definition for JavaScript using tree-sitter-javascript" readme = "README.md" keywords = ["tree-sitter", "stack-graphs", "javascript"] @@ -31,9 +31,9 @@ clap = { version = "4", optional = true } serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" stack-graphs = { version = "0.14", path = "../../stack-graphs" } # explicit version is required to be able to publish crate -tree-sitter-graph = "0.11.2" -tree-sitter-javascript = "=0.20.4" -tree-sitter-stack-graphs = { version = "0.9", path = "../../tree-sitter-stack-graphs" } # explicit version is required to be able to publish crate +tree-sitter-graph = "0.12" +tree-sitter-javascript = "=0.23.1" +tree-sitter-stack-graphs = { version = "0.10", path = "../../tree-sitter-stack-graphs" } # explicit version is required to be able to publish crate [dev-dependencies] anyhow = "1.0" diff --git a/languages/tree-sitter-stack-graphs-javascript/README.md b/languages/tree-sitter-stack-graphs-javascript/README.md index 099e633e9..c7eefa955 100644 --- a/languages/tree-sitter-stack-graphs-javascript/README.md +++ b/languages/tree-sitter-stack-graphs-javascript/README.md @@ -13,7 +13,7 @@ To use this library, add the following to your `Cargo.toml`: ```toml [dependencies] -tree-sitter-stack-graphs-javascript = "0.2" +tree-sitter-stack-graphs-javascript = "0.3" ``` Check out our [documentation](https://docs.rs/tree-sitter-stack-graphs-javascript/*/) for more details on how to use this library. diff --git a/languages/tree-sitter-stack-graphs-javascript/rust/lib.rs b/languages/tree-sitter-stack-graphs-javascript/rust/lib.rs index 718db8a2e..cbf80e5cd 100644 --- a/languages/tree-sitter-stack-graphs-javascript/rust/lib.rs +++ b/languages/tree-sitter-stack-graphs-javascript/rust/lib.rs @@ -37,7 +37,7 @@ pub fn try_language_configuration( cancellation_flag: &dyn CancellationFlag, ) -> Result { let mut lc = LanguageConfiguration::from_sources( - tree_sitter_javascript::language(), + tree_sitter_javascript::LANGUAGE.into(), Some(String::from("source.js")), None, vec![String::from("js")], diff --git a/languages/tree-sitter-stack-graphs-python/CHANGELOG.md b/languages/tree-sitter-stack-graphs-python/CHANGELOG.md index 61e6ca4e9..8ec44913d 100644 --- a/languages/tree-sitter-stack-graphs-python/CHANGELOG.md +++ b/languages/tree-sitter-stack-graphs-python/CHANGELOG.md @@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## v0.3.0 -- Unreleased + +- The `tree-sitter-stack-graphs` dependency is updated to version 0.10. + +- The `tree-sitter-python` dependency is updated to version 0.23.5. + ## v0.2.0 -- 2024-07-09 ### Added diff --git a/languages/tree-sitter-stack-graphs-python/Cargo.toml b/languages/tree-sitter-stack-graphs-python/Cargo.toml index 53d714719..afdfe6091 100644 --- a/languages/tree-sitter-stack-graphs-python/Cargo.toml +++ b/languages/tree-sitter-stack-graphs-python/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "tree-sitter-stack-graphs-python" -version = "0.2.0" +version = "0.3.0" description = "Stack graphs definition for Python using tree-sitter-python" readme = "README.md" keywords = ["tree-sitter", "stack-graphs", "python"] @@ -30,8 +30,8 @@ cli = ["anyhow", "clap", "tree-sitter-stack-graphs/cli"] [dependencies] anyhow = { version = "1.0", optional = true } clap = { version = "4", optional = true, features = ["derive"] } -tree-sitter-stack-graphs = { version = "0.9", path = "../../tree-sitter-stack-graphs" } # explicit version is required to be able to publish crate -tree-sitter-python = "=0.20.4" +tree-sitter-stack-graphs = { version = "0.10", path = "../../tree-sitter-stack-graphs" } # explicit version is required to be able to publish crate +tree-sitter-python = "=0.23.5" [dev-dependencies] anyhow = "1.0" diff --git a/languages/tree-sitter-stack-graphs-python/README.md b/languages/tree-sitter-stack-graphs-python/README.md index a313a3799..7ec9ded72 100644 --- a/languages/tree-sitter-stack-graphs-python/README.md +++ b/languages/tree-sitter-stack-graphs-python/README.md @@ -13,7 +13,7 @@ To use this library, add the following to your `Cargo.toml`: ```toml [dependencies] -tree-sitter-stack-graphs-python = "0.2" +tree-sitter-stack-graphs-python = "0.3" ``` Check out our [documentation](https://docs.rs/tree-sitter-stack-graphs-python/*/) for more details on how to use this library. diff --git a/languages/tree-sitter-stack-graphs-python/rust/lib.rs b/languages/tree-sitter-stack-graphs-python/rust/lib.rs index e0ffb40ec..332250511 100644 --- a/languages/tree-sitter-stack-graphs-python/rust/lib.rs +++ b/languages/tree-sitter-stack-graphs-python/rust/lib.rs @@ -29,7 +29,7 @@ pub fn try_language_configuration( cancellation_flag: &dyn CancellationFlag, ) -> Result { LanguageConfiguration::from_sources( - tree_sitter_python::language(), + tree_sitter_python::LANGUAGE.into(), Some(String::from("source.py")), None, vec![String::from("py")], diff --git a/languages/tree-sitter-stack-graphs-typescript/Cargo.toml b/languages/tree-sitter-stack-graphs-typescript/Cargo.toml index 86feee2a1..379d4d3fe 100644 --- a/languages/tree-sitter-stack-graphs-typescript/Cargo.toml +++ b/languages/tree-sitter-stack-graphs-typescript/Cargo.toml @@ -33,8 +33,8 @@ glob = "0.3" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" stack-graphs = { version = "0.14", path = "../../stack-graphs" } # explicit version is required to be able to publish crate -tree-sitter-stack-graphs = { version = "0.9", path = "../../tree-sitter-stack-graphs" } # explicit version is required to be able to publish crate -tree-sitter-typescript = "=0.20.2" +tree-sitter-stack-graphs = { version = "0.10", path = "../../tree-sitter-stack-graphs" } # explicit version is required to be able to publish crate +tree-sitter-typescript = "=0.23.2" tsconfig = "0.1.0" [dev-dependencies] diff --git a/languages/tree-sitter-stack-graphs-typescript/rust/lib.rs b/languages/tree-sitter-stack-graphs-typescript/rust/lib.rs index 2fa484a0a..8fd63881d 100644 --- a/languages/tree-sitter-stack-graphs-typescript/rust/lib.rs +++ b/languages/tree-sitter-stack-graphs-typescript/rust/lib.rs @@ -44,7 +44,7 @@ pub fn try_language_configuration_typescript( cancellation_flag: &dyn CancellationFlag, ) -> Result { let mut lc = LanguageConfiguration::from_sources( - tree_sitter_typescript::language_typescript(), + tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into(), Some(String::from("source.ts")), None, vec![String::from("ts")], @@ -74,7 +74,7 @@ pub fn try_language_configuration_tsx( cancellation_flag: &dyn CancellationFlag, ) -> Result { let mut lc = LanguageConfiguration::from_sources( - tree_sitter_typescript::language_tsx(), + tree_sitter_typescript::LANGUAGE_TSX.into(), Some(String::from("source.tsx")), None, vec![String::from("tsx")], diff --git a/languages/tree-sitter-stack-graphs-typescript/src/stack-graphs.tsg b/languages/tree-sitter-stack-graphs-typescript/src/stack-graphs.tsg index b7478b4e4..a32c717f5 100644 --- a/languages/tree-sitter-stack-graphs-typescript/src/stack-graphs.tsg +++ b/languages/tree-sitter-stack-graphs-typescript/src/stack-graphs.tsg @@ -1469,6 +1469,8 @@ if none @is_async { edge @dec.lexical_scope -> @class_decl.lexical_scope } + + ; definitions [ (abstract_class_declaration name:(_)@name) @@ -1626,6 +1628,7 @@ if none @is_async { [ (abstract_class_declaration body:(_)@body) (class_declaration body:(_)@body) + (interface_declaration body:(_)@body) ]@class_decl { ; propagate lexical scope ; FIXME the static members have access to type variables like this @@ -1639,16 +1642,6 @@ if none @is_async { edge @class_decl.static_type -> @body.static_members attr (@class_decl.static_type -> @body.static_members) precedence = 2 } -[ - (interface_declaration body:(_)@body) -]@class_decl { - ; propagate lexical scope - edge @body.lexical_scope -> @class_decl.generic_inner_lexical_scope - - ; interface type equals body type - edge @class_decl.generic_inner_type -> @body.type -} - ;; Class Body @@ -1662,23 +1655,35 @@ if none @is_async { ; (public_field_definition)] ; ) -(class_body)@class_body { - node @class_body.lexical_scope - node @class_body.type_members - node @class_body.static_members +[ + (class_body) + (interface_body) +]@body { + node @body.lexical_scope + node @body.type_members + node @body.static_members } -(class_body - (_)@mem -)@class_body { +; decorators +[ + (class_body decorator:(_)@dec) +]@class_body { + ; connect lexical scope + edge @dec.lexical_scope -> @class_body.lexical_scope +} + +[ + (class_body (_)@mem) + (interface_body (_)@mem) +]@body { ; propagate lexical scope - edge @mem.lexical_scope -> @class_body.lexical_scope + edge @mem.lexical_scope -> @body.lexical_scope ; body members are member definitions - edge @class_body.type_members -> @mem.type_members + edge @body.type_members -> @mem.type_members ; body static members are static member definitions - edge @class_body.static_members -> @mem.static_members + edge @body.static_members -> @mem.static_members } @@ -2334,7 +2339,7 @@ if none @is_async { (import_statement (import_clause (namespace_import (identifier)@name))) (ambient_declaration (module name:(string) @name)) ; X._ - (nested_identifier (identifier)@name) + (nested_identifier object:(identifier)@name) ] { node @name.expr_def node expr_def_typeof @@ -2357,7 +2362,7 @@ if none @is_async { edge @name.type_def -> @name.type_def_member } -(nested_identifier . (_) @mod) @nested { +(nested_identifier object:(_)@mod) @nested { node @nested.expr_def node @nested.type_def @@ -2365,12 +2370,12 @@ if none @is_async { edge @nested.type_def -> @mod.type_def } -(nested_identifier . (_) @mod . (_) @name .) { +(nested_identifier object:(_)@mod property:(_)@name) { edge @mod.expr_def_member -> @name.expr_def edge @mod.type_def_member -> @name.type_def } -(nested_identifier (_) @name .) @nested { +(nested_identifier property:(_)@name)@nested { node @nested.expr_def_member node @nested.type_def_member @@ -2644,7 +2649,7 @@ if none @is_async { (call_expression) (class) (false) - (function) + (function_expression) (generator_function) (import) (member_expression) @@ -2814,8 +2819,7 @@ if none @is_async { (decorator (call_expression function:(member_expression object:(member_expression object:(identifier)@name)))) (decorator (call_expression function:(member_expression object:(member_expression object:(member_expression object:(identifier)@name))))) ; #dialect tsx - (nested_identifier (identifier)@name) - (nested_identifier (nested_identifier)@name) + (nested_identifier object:(_)@name) (nested_type_identifier module:(nested_identifier)@name) (internal_module name:(_)@name) (jsx_opening_element name: (_)@name) @@ -2870,7 +2874,7 @@ if none @is_def { (decorator (call_expression function:(member_expression object:(member_expression object:(identifier)@name)))) (decorator (call_expression function:(member_expression object:(member_expression object:(member_expression object:(identifier)@name))))) ; #dialect tsx - (nested_identifier (identifier)@name (identifier)) ; to pick up foo in JSX: +; (nested_identifier object:(identifier)@name property:(identifier)) ; to pick up foo in JSX: ; FIXME never matched (jsx_opening_element name: (identifier)@name) (jsx_closing_element name: (identifier)@name) (jsx_self_closing_element name: (identifier)@name) @@ -3259,20 +3263,20 @@ if none @is_def { ; function (x) {}; -; (function +; (function_expression ; (formal_parameters (identifier)) ; (statement_block)) ; this captures the parameters -; (function +; (function_expression ; parameters: (_)@params) ; this captures the body -; (function +; (function_expression ; body:(_)@body)@function ; functions with names -; (function +; (function_expression ; name:(_)@name ; parameters:(_)@call_sig)@fun { ; } @@ -3307,7 +3311,7 @@ if none @is_def { ; } [ - (function) + (function_expression) (arrow_function) (generator_function) ]@fun { @@ -3334,9 +3338,9 @@ if none @is_def { } [ - (function parameters:(_)@call_sig) - (arrow_function parameters:(_)@call_sig) - (generator_function parameters:(_)@call_sig) + (function_expression parameters:(_)@call_sig) + (arrow_function parameters:(_)@call_sig) + (generator_function parameters:(_)@call_sig) ]@fun { ; propagate lexical scope edge @call_sig.lexical_scope -> @fun.lexical_scope @@ -3385,9 +3389,9 @@ if none @is_def { } [ - (function body:(_)@body) - (arrow_function body:(_)@body) - (generator_function body:(_)@body) + (function_expression body:(_)@body) + (arrow_function body:(_)@body) + (generator_function body:(_)@body) ]@fun { ; propagate lexical scope edge @body.lexical_scope -> @fun.lexical_scope @@ -3396,8 +3400,8 @@ if none @is_def { ;;;; specified return type [ - (function return_type:(_)@return_type) - (arrow_function return_type:(_)@return_type) + (function_expression return_type:(_)@return_type) + (arrow_function return_type:(_)@return_type) ]@fun { ; propagate lexical scope edge @return_type.lexical_scope -> @fun.lexical_scope @@ -3409,8 +3413,8 @@ if none @is_def { ;;;; inferred return type [ - (function "async"?@is_async !return_type body:(_)@body) - (arrow_function "async"?@is_async !return_type body:(statement_block)@body) + (function_expression "async"?@is_async !return_type body:(_)@body) + (arrow_function "async"?@is_async !return_type body:(statement_block)@body) ]@fun { if none @is_async { ; callable is type of return statement @@ -3428,8 +3432,8 @@ if none @is_async { ;;;; inferred async return type [ - (function "async" !return_type body:(_)@body) - (arrow_function "async" body:(statement_block)@body) + (function_expression "async" !return_type body:(_)@body) + (arrow_function "async" body:(statement_block)@body) ]@fun_decl { ; function returns body return type edge @fun_decl.callable__return -> @fun_decl.async_type @@ -3548,19 +3552,9 @@ if none @is_async { ; (subscript_expression (identifier) (string)) [ - (member_expression - object: (_)@object - property: (_)@prop - )@member_expr + (member_expression object:(_)@object property:(_)@prop)@member_expr ; #dialect tsx - (nested_identifier - (nested_identifier)@object - (identifier)@prop - )@member_expr - (nested_identifier - (identifier)@object - (identifier)@prop - )@member_expr +; (nested_identifier object:(_)@object property:(identifier)@prop)@member_expr ; FIXME this never matched anything! ; #end ] { node @member_expr.member @@ -3890,15 +3884,13 @@ if none @is_async { ; (sequence_expression (number) (number)) -(sequence_expression - left: (_)@left - right: (_)@right -)@sequence_expr { +(sequence_expression (_)@expr)@sequence_expr { ; propagate lexical scope - edge @left.lexical_scope -> @sequence_expr.lexical_scope - edge @right.lexical_scope -> @sequence_expr.lexical_scope + edge @expr.lexical_scope -> @sequence_expr.lexical_scope +} - ; FIXME @sequence_expr.type is type of last, but cannot express because of nesting +(sequence_expression (_)@last .)@sequence_expr { + edge @sequence_expr.type -> @last.type } @@ -4040,6 +4032,12 @@ if none @is_async { edge @class_expr.this__type_def -> @class_expr.generic_inner_type } +; decorators +(class decorator:(_)@dec)@class_expr { + ; connect lexical scope + edge @dec.lexical_scope -> @class_expr.lexical_scope +} + ; default constructor ; FIXME only if no other constructor is defined (class)@class_expr { @@ -4571,6 +4569,12 @@ if none @is_async { ; value:(_)@value ; opt ; )@def +; decorators +(public_field_definition decorator:(_)@dec)@def { + ; connect lexical scope + edge @dec.lexical_scope -> @def.lexical_scope +} + (public_field_definition name:(_)@name )@def { @@ -4956,6 +4960,7 @@ if none @is_acc { [ (array_type) (asserts) + (asserts_annotation) (conditional_type) (constraint) (constructor_type) @@ -5071,6 +5076,9 @@ if none @is_acc { edge @type.lexical_scope -> @asserts.lexical_scope } +(asserts_annotation (_)@asserts)@asserts_annotation { + edge @asserts.lexical_scope -> @asserts_annotation.lexical_scope +} ;; Optional Type @@ -5596,7 +5604,7 @@ if none @is_acc { ; X (nested_type_identifier module:(identifier)@name) ; X._, _.X._ - (nested_identifier (identifier)@name) + (nested_identifier object:(identifier)@name) ] { node @name.type_ref attr (@name.type_ref) node_reference = @name @@ -5607,7 +5615,7 @@ if none @is_acc { edge @name.type_ref_member -> @name.type_ref } -(nested_identifier . (_)@mod) @nested { +(nested_identifier object:(_)@mod) @nested { node @nested.type_ref edge @mod.type_ref -> @nested.type_ref } @@ -5616,7 +5624,7 @@ if none @is_acc { edge @name.type_ref -> @mod.type_ref_member } -(nested_identifier (_)@name .) @nested { +(nested_identifier property:(_)@name) @nested { node @nested.type_ref_member edge @nested.type_ref_member -> @name.type_ref_member } @@ -6159,7 +6167,7 @@ if none @is_acc { [ (arrow_function "async") - (function "async") + (function_expression "async") (function_declaration "async") (generator_function "async") (generator_function_declaration "async") @@ -6221,7 +6229,6 @@ if none @is_acc { [ (jsx_text) (jsx_element) - (jsx_fragment) (jsx_self_closing_element) (jsx_expression) ]@child @@ -6229,24 +6236,6 @@ if none @is_acc { edge @child.lexical_scope -> @parent.lexical_scope } -(jsx_fragment)@fragment { - node @fragment.lexical_scope - node @fragment.value - node @fragment.type -} - -(jsx_fragment - [ - (jsx_text) - (jsx_element) - (jsx_fragment) - (jsx_self_closing_element) - (jsx_expression) - ]@child -) @parent { - edge @child.lexical_scope -> @parent.lexical_scope -} - (jsx_text)@jsx_text { node @jsx_text.lexical_scope } diff --git a/languages/tree-sitter-stack-graphs-typescript/test/jsx/jsx_namespace_name.tsx b/languages/tree-sitter-stack-graphs-typescript/test/jsx/jsx_namespace_name.tsx.skip similarity index 100% rename from languages/tree-sitter-stack-graphs-typescript/test/jsx/jsx_namespace_name.tsx rename to languages/tree-sitter-stack-graphs-typescript/test/jsx/jsx_namespace_name.tsx.skip diff --git a/languages/tree-sitter-stack-graphs-typescript/test/modules/very-deep-namespace.ts b/languages/tree-sitter-stack-graphs-typescript/test/modules/very-deep-namespace.ts.skip similarity index 100% rename from languages/tree-sitter-stack-graphs-typescript/test/modules/very-deep-namespace.ts rename to languages/tree-sitter-stack-graphs-typescript/test/modules/very-deep-namespace.ts.skip diff --git a/languages/tree-sitter-stack-graphs-typescript/test/statements/refer-to-interface-in-nested-namespace-by-fully-qualified-name.ts b/languages/tree-sitter-stack-graphs-typescript/test/statements/refer-to-interface-in-nested-namespace-by-fully-qualified-name.ts.skip similarity index 100% rename from languages/tree-sitter-stack-graphs-typescript/test/statements/refer-to-interface-in-nested-namespace-by-fully-qualified-name.ts rename to languages/tree-sitter-stack-graphs-typescript/test/statements/refer-to-interface-in-nested-namespace-by-fully-qualified-name.ts.skip diff --git a/languages/tree-sitter-stack-graphs-typescript/test/statements/refer-to-interface-in-qualified-namespace-by-fully-qualified-name.ts b/languages/tree-sitter-stack-graphs-typescript/test/statements/refer-to-interface-in-qualified-namespace-by-fully-qualified-name.ts.skip similarity index 100% rename from languages/tree-sitter-stack-graphs-typescript/test/statements/refer-to-interface-in-qualified-namespace-by-fully-qualified-name.ts rename to languages/tree-sitter-stack-graphs-typescript/test/statements/refer-to-interface-in-qualified-namespace-by-fully-qualified-name.ts.skip diff --git a/lsp-positions/CHANGELOG.md b/lsp-positions/CHANGELOG.md index a9e17c13f..84aa962b4 100644 --- a/lsp-positions/CHANGELOG.md +++ b/lsp-positions/CHANGELOG.md @@ -5,6 +5,10 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## v0.3.4 -- Unreleased + +Upgraded the `tree-sitter` dependency to version 0.24. + ## v0.3.3 -- 2024-03-05 The `tree-sitter` dependency version was updated to fix install problems. diff --git a/lsp-positions/Cargo.toml b/lsp-positions/Cargo.toml index 7054def14..a9f7acb20 100644 --- a/lsp-positions/Cargo.toml +++ b/lsp-positions/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "lsp-positions" -version = "0.3.3" +version = "0.3.4" description = "LSP-compatible character positions" homepage = "https://github.com/github/stack-graphs/tree/main/lsp-positions" repository = "https://github.com/github/stack-graphs/" @@ -22,7 +22,7 @@ tree-sitter = ["dep:tree-sitter"] [dependencies] memchr = "2.4" -tree-sitter = { version = "0.20", optional = true } # keep the same minor version as the tree-sitter +tree-sitter = { version = "0.24", optional = true } # keep the same minor version as the tree-sitter # dependency of tree-sitter-stack-graphs to prevent # install problems unicode-segmentation = { version = "1.8" } diff --git a/tree-sitter-stack-graphs/CHANGELOG.md b/tree-sitter-stack-graphs/CHANGELOG.md index 6f6e86f25..b45b69b64 100644 --- a/tree-sitter-stack-graphs/CHANGELOG.md +++ b/tree-sitter-stack-graphs/CHANGELOG.md @@ -5,8 +5,11 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## v0.9.0 -- 2024-07-09 +## v0.10.0 -- Unreleased + +Upgraded `tree-sitter` dependency to version 0.24. +## v0.9.0 -- 2024-07-09 ### Library diff --git a/tree-sitter-stack-graphs/Cargo.toml b/tree-sitter-stack-graphs/Cargo.toml index 0ac84d012..3c1c01a80 100644 --- a/tree-sitter-stack-graphs/Cargo.toml +++ b/tree-sitter-stack-graphs/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "tree-sitter-stack-graphs" -version = "0.9.0" +version = "0.10.0" description = "Create stack graphs using tree-sitter parsers" homepage = "https://github.com/github/stack-graphs/tree/main/tree-sitter-stack-graphs" repository = "https://github.com/github/stack-graphs/" @@ -62,7 +62,7 @@ env_logger = { version = "0.9", optional = true } indoc = { version = "1.0", optional = true } itertools = "0.10" log = "0.4" -lsp-positions = { version="0.3", path="../lsp-positions", features=["tree-sitter"] } # explicit version is required to be able to publish crate +lsp-positions = { version="0.3.4", path="../lsp-positions", features=["tree-sitter"] } # explicit version is required to be able to publish crate once_cell = "1" pathdiff = { version = "0.2.1", optional = true } regex = "1" @@ -74,13 +74,13 @@ thiserror = "1.0" time = { version = "0.3", optional = true } tokio = { version = "1.26", optional = true, features = ["io-std", "rt", "rt-multi-thread"] } tower-lsp = { version = "0.19", optional = true } -tree-sitter = "0.20" # keep the same minor version as the tree-sitter dependency +tree-sitter = "0.24" # keep the same minor version as the tree-sitter dependency # of tree-sitter-graph to prevent install problems -tree-sitter-config = { version = "0.19", optional = true } -tree-sitter-graph = "0.11" -tree-sitter-loader = "0.20" +tree-sitter-config = { version = "0.24", optional = true } +tree-sitter-graph = "0.12" +tree-sitter-loader = "0.24" walkdir = { version = "2.3", optional = true } [dev-dependencies] pretty_assertions = "0.7" -tree-sitter-python = "=0.19.1" +tree-sitter-python = "=0.23.5" diff --git a/tree-sitter-stack-graphs/README.md b/tree-sitter-stack-graphs/README.md index a4e52add3..56f61e131 100644 --- a/tree-sitter-stack-graphs/README.md +++ b/tree-sitter-stack-graphs/README.md @@ -14,7 +14,7 @@ To use this library, add the following to your `Cargo.toml`: ```toml [dependencies] -tree-sitter-stack-graphs = "0.9" +tree-sitter-stack-graphs = "0.10" ``` Check out our [documentation](https://docs.rs/tree-sitter-stack-graphs/*/) for more details on how to use this library. diff --git a/tree-sitter-stack-graphs/src/cli/init.rs b/tree-sitter-stack-graphs/src/cli/init.rs index f71890390..e513e5acd 100644 --- a/tree-sitter-stack-graphs/src/cli/init.rs +++ b/tree-sitter-stack-graphs/src/cli/init.rs @@ -778,7 +778,7 @@ impl ProjectSettings<'_> { cancellation_flag: &dyn CancellationFlag, ) -> Result {{ LanguageConfiguration::from_sources( - {}::language(), + {}::LANGUAGE.into(), Some(String::from("source.{}")), None, vec![String::from("{}")], diff --git a/tree-sitter-stack-graphs/src/cli/load.rs b/tree-sitter-stack-graphs/src/cli/load.rs index b32332f9d..1fd22fa2b 100644 --- a/tree-sitter-stack-graphs/src/cli/load.rs +++ b/tree-sitter-stack-graphs/src/cli/load.rs @@ -67,7 +67,7 @@ impl PathLoaderArgs { builtins_paths, )? } else { - let loader_config = TsConfig::load() + let loader_config = TsConfig::load(None) .and_then(|v| v.get()) .map_err(LoadError::TreeSitter)?; Loader::from_tree_sitter_configuration( diff --git a/tree-sitter-stack-graphs/src/cli/lsp.rs b/tree-sitter-stack-graphs/src/cli/lsp.rs index 03f1c8060..57ed770bb 100644 --- a/tree-sitter-stack-graphs/src/cli/lsp.rs +++ b/tree-sitter-stack-graphs/src/cli/lsp.rs @@ -560,25 +560,6 @@ impl FromStdError for std::result::Result { } } -trait FromAnyhowError { - #[must_use] - fn from_error(self) -> Result; -} - -impl FromAnyhowError for std::result::Result { - #[must_use] - fn from_error(self) -> Result { - match self { - Ok(value) => Ok(value), - Err(err) => Err(Error { - code: ErrorCode::ServerError(-1), - message: err.to_string(), - data: None, - }), - } - } -} - #[derive(Debug)] pub enum Job { IndexPath(PathBuf), diff --git a/tree-sitter-stack-graphs/src/cli/match.rs b/tree-sitter-stack-graphs/src/cli/match.rs index 108c8450e..889d2374a 100644 --- a/tree-sitter-stack-graphs/src/cli/match.rs +++ b/tree-sitter-stack-graphs/src/cli/match.rs @@ -49,7 +49,7 @@ impl MatchArgs { None => return Err(anyhow!("No stack graph language found")), }; let source = file_reader.get(&self.source_path)?; - let tree = parse(lc.language, &self.source_path, source)?; + let tree = parse(&lc.language, &self.source_path, source)?; if self.stanza.is_empty() { lc.sgl.tsg.try_visit_matches(&tree, source, true, |mat| { print_matches(lc.sgl.tsg_path(), &self.source_path, source, mat) diff --git a/tree-sitter-stack-graphs/src/cli/parse.rs b/tree-sitter-stack-graphs/src/cli/parse.rs index aff7681b1..64275b915 100644 --- a/tree-sitter-stack-graphs/src/cli/parse.rs +++ b/tree-sitter-stack-graphs/src/cli/parse.rs @@ -40,14 +40,14 @@ impl ParseArgs { None => return Err(anyhow!("No stack graph language found")), }; let source = file_reader.get(&self.source_path)?; - let tree = parse(lang, &self.source_path, source)?; + let tree = parse(&lang, &self.source_path, source)?; print_tree(tree); Ok(()) } } pub(super) fn parse( - language: tree_sitter::Language, + language: &tree_sitter::Language, path: &Path, source: &str, ) -> anyhow::Result { diff --git a/tree-sitter-stack-graphs/src/cli/test.rs b/tree-sitter-stack-graphs/src/cli/test.rs index 1bff78a48..e3034cbbf 100644 --- a/tree-sitter-stack-graphs/src/cli/test.rs +++ b/tree-sitter-stack-graphs/src/cli/test.rs @@ -432,7 +432,7 @@ impl TestArgs { .add_from_graph(&lc.builtins) .map_err(|h| anyhow!("Duplicate builtin file {}", &graph[h]))?; let files = files.into_iter().collect::>(); - match cache.entry(lc.language) { + match cache.entry(lc.language.clone()) { Entry::Occupied(o) => { o.get().load_into(graph, partials, db)?; } diff --git a/tree-sitter-stack-graphs/src/lib.rs b/tree-sitter-stack-graphs/src/lib.rs index 717214832..3c560fb56 100644 --- a/tree-sitter-stack-graphs/src/lib.rs +++ b/tree-sitter-stack-graphs/src/lib.rs @@ -324,7 +324,7 @@ //! import sys //! print(sys.path) //! "#; -//! let grammar = tree_sitter_python::language(); +//! let grammar = tree_sitter_python::LANGUAGE.into(); //! let tsg_source = STACK_GRAPH_RULES; //! let mut language = StackGraphLanguage::from_str(grammar, tsg_source)?; //! let mut stack_graph = StackGraph::new(); @@ -479,7 +479,7 @@ impl StackGraphLanguage { language: tree_sitter::Language, tsg_source: &str, ) -> Result { - let tsg = tree_sitter_graph::ast::File::from_str(language, tsg_source)?; + let tsg = tree_sitter_graph::ast::File::from_str(language.clone(), tsg_source)?; Ok(StackGraphLanguage { language, tsg, @@ -518,8 +518,8 @@ impl StackGraphLanguage { &mut self.functions } - pub fn language(&self) -> tree_sitter::Language { - self.language + pub fn language(&self) -> &tree_sitter::Language { + &self.language } /// Returns the original TSG path, if it was provided at construction or set with @@ -624,7 +624,7 @@ impl<'a> Builder<'a> { ) -> Result<(), BuildError> { let tree = { let mut parser = Parser::new(); - parser.set_language(self.sgl.language)?; + parser.set_language(&self.sgl.language)?; let ts_cancellation_flag = TreeSitterCancellationFlag::from(cancellation_flag); // The parser.set_cancellation_flag` is unsafe, because it does not tie the // lifetime of the parser to the lifetime of the cancellation flag in any way. diff --git a/tree-sitter-stack-graphs/src/loader.rs b/tree-sitter-stack-graphs/src/loader.rs index 14aa97916..861e4ff95 100644 --- a/tree-sitter-stack-graphs/src/loader.rs +++ b/tree-sitter-stack-graphs/src/loader.rs @@ -68,13 +68,12 @@ impl LanguageConfiguration { builtins_config: Option<&str>, cancellation_flag: &dyn CancellationFlag, ) -> Result> { - let sgl = StackGraphLanguage::from_source(language, tsg_path.clone(), tsg_source).map_err( - |err| LoadError::SglParse { - inner: err, - tsg_path, - tsg: Cow::from(tsg_source), - }, - )?; + let sgl = StackGraphLanguage::from_source(language.clone(), tsg_path.clone(), tsg_source) + .map_err(|err| LoadError::SglParse { + inner: err, + tsg_path, + tsg: Cow::from(tsg_source), + })?; let mut builtins = StackGraph::new(); if let Some((builtins_path, builtins_source)) = builtins_source { let mut builtins_globals = Variables::new(); @@ -273,7 +272,7 @@ impl Loader { &mut self, path: &Path, content: &mut dyn ContentProvider, - ) -> Result, LoadError<'static>> { + ) -> Result, LoadError<'static>> { match &mut self.0 { LoaderImpl::Paths(loader) => loader.load_tree_sitter_language_for_file(path, content), LoaderImpl::Provided(loader) => { @@ -496,10 +495,10 @@ impl LanguageConfigurationsLoader { &mut self, path: &Path, content: &mut dyn ContentProvider, - ) -> Result, LoadError<'static>> { + ) -> Result, LoadError<'static>> { for configuration in self.configurations.iter() { if configuration.matches_file(path, content)? { - return Ok(Some(configuration.language)); + return Ok(Some(&configuration.language)); } } Ok(None) @@ -567,9 +566,9 @@ impl PathLoader { &mut self, path: &Path, content: &mut dyn ContentProvider, - ) -> Result, LoadError<'static>> { + ) -> Result, LoadError<'static>> { if let Some(selected_language) = self.select_language_for_file(path, content)? { - return Ok(Some(selected_language.language)); + return Ok(Some(&selected_language.language)); } Ok(None) } @@ -591,7 +590,7 @@ impl PathLoader { Some(index) => index, None => { let tsg = self.load_tsg_from_paths(&language)?; - let sgl = StackGraphLanguage::new(language.language, tsg); + let sgl = StackGraphLanguage::new(language.language.clone(), tsg); let mut builtins = StackGraph::new(); self.load_builtins_from_paths_into( @@ -602,7 +601,7 @@ impl PathLoader { )?; let lc = LanguageConfiguration { - language: language.language, + language: language.language.clone(), scope: language.scope, content_regex: language.content_regex, file_types: language.file_types, @@ -696,7 +695,7 @@ impl PathLoader { } if tsg_path.exists() { let tsg_source = std::fs::read_to_string(tsg_path)?; - return Loader::load_tsg(language.language, Cow::from(tsg_source)); + return Loader::load_tsg(language.language.clone(), Cow::from(tsg_source)); } } return Err(LoadError::NoTsgFound); @@ -786,10 +785,11 @@ impl SupplementedTsLoader { .map_err(LoadError::TreeSitter)?; let configurations = self .0 - .find_language_configurations_at_path(&path) + .find_language_configurations_at_path(&path, true) .map_err(LoadError::TreeSitter)?; let languages = languages .into_iter() + .map(|(l, _)| l) .zip(configurations.into_iter()) .map(SupplementedLanguage::from) .filter(|language| scope.map_or(true, |scope| language.matches_scope(scope))) diff --git a/tree-sitter-stack-graphs/tests/it/builder.rs b/tree-sitter-stack-graphs/tests/it/builder.rs index 14476111e..719e25d71 100644 --- a/tree-sitter-stack-graphs/tests/it/builder.rs +++ b/tree-sitter-stack-graphs/tests/it/builder.rs @@ -35,7 +35,7 @@ fn can_support_preexisting_nodes() { .add(FILE_PATH_VAR.into(), file_name.into()) .expect("failed to add file path variable"); - let language = StackGraphLanguage::from_str(tree_sitter_python::language(), tsg).unwrap(); + let language = StackGraphLanguage::from_str(tree_sitter_python::LANGUAGE.into(), tsg).unwrap(); language .build_stack_graph_into(&mut graph, file, python, &globals, &NoCancellation) .expect("Failed to build graph"); @@ -59,7 +59,7 @@ fn can_support_injected_nodes() { let node_id = graph.new_node_id(file); let _preexisting_node = graph.add_scope_node(node_id, true).unwrap(); - let language = StackGraphLanguage::from_str(tree_sitter_python::language(), tsg).unwrap(); + let language = StackGraphLanguage::from_str(tree_sitter_python::LANGUAGE.into(), tsg).unwrap(); let mut builder = language.builder_into_stack_graph(&mut graph, file, python); let mut globals = Variables::new(); diff --git a/tree-sitter-stack-graphs/tests/it/loader.rs b/tree-sitter-stack-graphs/tests/it/loader.rs index 257326952..401af46e5 100644 --- a/tree-sitter-stack-graphs/tests/it/loader.rs +++ b/tree-sitter-stack-graphs/tests/it/loader.rs @@ -9,6 +9,7 @@ use once_cell::sync::Lazy; use pretty_assertions::assert_eq; use stack_graphs::graph::StackGraph; use std::path::PathBuf; +use tree_sitter::Language; use tree_sitter_stack_graphs::loader::FileAnalyzers; use tree_sitter_stack_graphs::loader::LanguageConfiguration; use tree_sitter_stack_graphs::loader::Loader; @@ -25,10 +26,10 @@ static TSG: Lazy = Lazy::new(|| { #[test] fn can_load_from_provided_language_configuration() { - let language = tree_sitter_python::language(); - let sgl = StackGraphLanguage::from_str(language, &TSG).unwrap(); + let language: Language = tree_sitter_python::LANGUAGE.into(); + let sgl = StackGraphLanguage::from_str(language.clone(), &TSG).unwrap(); let lc = LanguageConfiguration { - language: language, + language: language.clone(), scope: Some("source.py".into()), content_regex: None, file_types: vec!["py".into()], @@ -43,10 +44,10 @@ fn can_load_from_provided_language_configuration() { let tsl = loader .load_tree_sitter_language_for_file(&PATH, &mut None) .expect("Expected loading tree-sitter language to succeed"); - assert_eq!(tsl, Some(language)); + assert_eq!(tsl, Some(&language)); let lc = loader .load_for_file(&PATH, &mut None, &NoCancellation) .expect("Expected loading stack graph language to succeed"); - assert_eq!(lc.primary.map(|lc| lc.language), Some(language)); + assert_eq!(lc.primary.map(|lc| &lc.language), Some(&language)); } diff --git a/tree-sitter-stack-graphs/tests/it/main.rs b/tree-sitter-stack-graphs/tests/it/main.rs index 33ee19241..01cfd9754 100644 --- a/tree-sitter-stack-graphs/tests/it/main.rs +++ b/tree-sitter-stack-graphs/tests/it/main.rs @@ -28,7 +28,7 @@ pub(self) fn build_stack_graph( ) -> Result<(StackGraph, Handle), BuildError> { let file_name = "test.py"; let language = - StackGraphLanguage::from_str(tree_sitter_python::language(), tsg_source).unwrap(); + StackGraphLanguage::from_str(tree_sitter_python::LANGUAGE.into(), tsg_source).unwrap(); let mut graph = StackGraph::new(); let file = graph.get_or_create_file(file_name); let mut globals = Variables::new(); diff --git a/tree-sitter-stack-graphs/tests/it/test.rs b/tree-sitter-stack-graphs/tests/it/test.rs index cb1efff4f..fae14fe76 100644 --- a/tree-sitter-stack-graphs/tests/it/test.rs +++ b/tree-sitter-stack-graphs/tests/it/test.rs @@ -73,7 +73,7 @@ fn build_stack_graph_into( globals: &Variables, ) -> Result<(), BuildError> { let language = - StackGraphLanguage::from_str(tree_sitter_python::language(), tsg_source).unwrap(); + StackGraphLanguage::from_str(tree_sitter_python::LANGUAGE.into(), tsg_source).unwrap(); language.build_stack_graph_into(graph, file, python_source, globals, &NoCancellation)?; Ok(()) } From c0ecac035f0905831ee8e328a1658b96c8ed704e Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Tue, 10 Dec 2024 19:18:15 +0100 Subject: [PATCH 272/291] Handle additional case --- .../tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg | 4 ++-- .../test/expressions/member_expression.js | 4 ++++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg b/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg index a9167c3a1..7ca4eee88 100644 --- a/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg +++ b/languages/tree-sitter-stack-graphs-javascript/src/stack-graphs.tsg @@ -2435,7 +2435,7 @@ inherit .containing_class_value [ (primary_expression/identifier)@variable - (member_expression . (identifier)@variable) + (member_expression object:(identifier)@variable) ] { ; value is a lookup, ie a push attr (@variable.value) node_reference = @variable @@ -3189,7 +3189,7 @@ inherit .containing_class_value ;; ##### Member Expressions (member_expression - (_)@object . (_)@property)@member_expr + object:(_)@object property:(_)@property)@member_expr { node member_push diff --git a/languages/tree-sitter-stack-graphs-javascript/test/expressions/member_expression.js b/languages/tree-sitter-stack-graphs-javascript/test/expressions/member_expression.js index b397d747a..438cf9dd6 100644 --- a/languages/tree-sitter-stack-graphs-javascript/test/expressions/member_expression.js +++ b/languages/tree-sitter-stack-graphs-javascript/test/expressions/member_expression.js @@ -15,4 +15,8 @@ let x = 1; // Flow around /**/ x; +// ^ defined: 1 + +// Optional chain +/**/ x?.foo // ^ defined: 1 \ No newline at end of file From ba2117d75ff56dd4adf7032f18d07ed9c1dac26a Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Tue, 10 Dec 2024 19:32:18 +0100 Subject: [PATCH 273/291] Fix panic --- stack-graphs/CHANGELOG.md | 6 ++++++ stack-graphs/Cargo.toml | 2 +- stack-graphs/src/graph.rs | 5 ++++- stack-graphs/tests/it/graph.rs | 21 ++++++++++++++++++++- 4 files changed, 31 insertions(+), 3 deletions(-) diff --git a/stack-graphs/CHANGELOG.md b/stack-graphs/CHANGELOG.md index eeaae0730..aae6a2998 100644 --- a/stack-graphs/CHANGELOG.md +++ b/stack-graphs/CHANGELOG.md @@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## v0.14.1 -- Unpublished + +### Fixed + +- A panic when using `StackGraph::incoming_edge_degree` on some nodes without incoming edges. + ## v0.14.0 -- 2024-07-09 ### Changed diff --git a/stack-graphs/Cargo.toml b/stack-graphs/Cargo.toml index f114fffdb..e542f4163 100644 --- a/stack-graphs/Cargo.toml +++ b/stack-graphs/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "stack-graphs" -version = "0.14.0" +version = "0.14.1" description = "Name binding for arbitrary programming languages" homepage = "https://github.com/github/stack-graphs/tree/main/stack-graphs" repository = "https://github.com/github/stack-graphs/" diff --git a/stack-graphs/src/graph.rs b/stack-graphs/src/graph.rs index 686344ee5..d55dfc48a 100644 --- a/stack-graphs/src/graph.rs +++ b/stack-graphs/src/graph.rs @@ -1334,7 +1334,10 @@ impl StackGraph { /// Returns the number of edges that end at a particular sink node. pub fn incoming_edge_degree(&self, sink: Handle) -> Degree { - self.incoming_edges[sink] + self.incoming_edges + .get(sink) + .cloned() + .unwrap_or(Degree::Zero) } } diff --git a/stack-graphs/tests/it/graph.rs b/stack-graphs/tests/it/graph.rs index f19bb2c30..620d066c9 100644 --- a/stack-graphs/tests/it/graph.rs +++ b/stack-graphs/tests/it/graph.rs @@ -8,7 +8,7 @@ use std::collections::HashSet; use maplit::hashset; -use stack_graphs::graph::StackGraph; +use stack_graphs::graph::{Degree, StackGraph}; use crate::test_graphs; use crate::test_graphs::CreateStackGraph; @@ -196,3 +196,22 @@ fn can_add_graph_to_empty_graph() { ); } } + +#[test] +fn can_get_incoming_edges() { + let mut graph = StackGraph::new(); + let file = graph.get_or_create_file("test.py"); + let h1 = graph.internal_scope(file, 0); + let h2 = graph.internal_scope(file, 1); + let h3 = graph.internal_scope(file, 2); + assert_eq!(Degree::Zero, graph.incoming_edge_degree(h1)); + assert_eq!(Degree::Zero, graph.incoming_edge_degree(h2)); + assert_eq!(Degree::Zero, graph.incoming_edge_degree(h3)); + graph.add_edge(h1, h2, 0); + graph.add_edge(h3, h2, 0); + assert_eq!(Degree::Zero, graph.incoming_edge_degree(h1)); + assert_eq!(Degree::Multiple, graph.incoming_edge_degree(h2)); + assert_eq!(Degree::Zero, graph.incoming_edge_degree(h3)); + graph.add_edge(h3, h1, 0); + assert_eq!(Degree::One, graph.incoming_edge_degree(h1)); +} From ba182ad26c0a70b2580070e7c61ca254e1cf9aa4 Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Wed, 11 Dec 2024 15:24:54 +0100 Subject: [PATCH 274/291] Fix previously skipped tests --- .../src/stack-graphs.tsg | 235 ++++++++---------- ...e_name.tsx.skip => jsx_namespace_name.tsx} | 0 ...mespace.ts.skip => very-deep-namespace.ts} | 0 ...sted-namespace-by-fully-qualified-name.ts} | 0 ...fied-namespace-by-fully-qualified-name.ts} | 0 5 files changed, 106 insertions(+), 129 deletions(-) rename languages/tree-sitter-stack-graphs-typescript/test/jsx/{jsx_namespace_name.tsx.skip => jsx_namespace_name.tsx} (100%) rename languages/tree-sitter-stack-graphs-typescript/test/modules/{very-deep-namespace.ts.skip => very-deep-namespace.ts} (100%) rename languages/tree-sitter-stack-graphs-typescript/test/statements/{refer-to-interface-in-nested-namespace-by-fully-qualified-name.ts.skip => refer-to-interface-in-nested-namespace-by-fully-qualified-name.ts} (100%) rename languages/tree-sitter-stack-graphs-typescript/test/statements/{refer-to-interface-in-qualified-namespace-by-fully-qualified-name.ts.skip => refer-to-interface-in-qualified-namespace-by-fully-qualified-name.ts} (100%) diff --git a/languages/tree-sitter-stack-graphs-typescript/src/stack-graphs.tsg b/languages/tree-sitter-stack-graphs-typescript/src/stack-graphs.tsg index a32c717f5..773abde7c 100644 --- a/languages/tree-sitter-stack-graphs-typescript/src/stack-graphs.tsg +++ b/languages/tree-sitter-stack-graphs-typescript/src/stack-graphs.tsg @@ -627,16 +627,6 @@ attribute node_symbol = node => symbol = (source-text node), source_n (import_statement "type"?@is_type (import_clause (named_imports (import_specifier name:(_)@name))@clause)) ] { if none @is_type { - node @name.expr_def - node @name.expr_def__ns - node @name.expr_ref - node @name.expr_ref__ns - - ; expr reference - attr (@name.expr_ref) node_reference = @name - edge @name.expr_ref -> @name.expr_ref__ns - ; - attr (@name.expr_ref__ns) push_symbol = "%E" edge @name.expr_ref__ns -> @clause.lexical_scope } } @@ -646,6 +636,9 @@ if none @is_type { (import_statement "type"?@is_type (import_clause (named_imports (import_specifier name:(_)@name !alias))@clause)) ] { if none @is_type { + node @name.expr_def + node @name.expr_def__ns + ; expr definition edge @clause.defs -> @name.expr_def__ns ; FIXME defs, lexical_defs? ; @@ -664,7 +657,6 @@ if none @is_type { if none @is_type { node @alias.expr_def node @alias.expr_def__ns - node @alias.expr_ref ; expr definition edge @clause.defs -> @alias.expr_def__ns @@ -2340,6 +2332,12 @@ if none @is_async { (ambient_declaration (module name:(string) @name)) ; X._ (nested_identifier object:(identifier)@name) + ; _.X._ + (member_expression object:(identifier)@name) + ; _.X + (nested_identifier property:(_)@name) + ; _._.X + (member_expression property:(_)@name) ] { node @name.expr_def node expr_def_typeof @@ -2362,7 +2360,10 @@ if none @is_async { edge @name.type_def -> @name.type_def_member } -(nested_identifier object:(_)@mod) @nested { +[ + (nested_identifier object:(_)@mod) + (member_expression object:[(member_expression) (identifier)]@mod) +]@nested { node @nested.expr_def node @nested.type_def @@ -2370,12 +2371,18 @@ if none @is_async { edge @nested.type_def -> @mod.type_def } -(nested_identifier object:(_)@mod property:(_)@name) { +[ + (nested_identifier object:(_)@mod property:(_)@name) + (member_expression object:[(member_expression) (identifier)]@mod property:(_)@name) +] { edge @mod.expr_def_member -> @name.expr_def edge @mod.type_def_member -> @name.type_def } -(nested_identifier property:(_)@name)@nested { +[ + (nested_identifier property:(_)@name) + (member_expression property:(_)@name) +]@nested { node @nested.expr_def_member node @nested.type_def_member @@ -2782,105 +2789,13 @@ if none @is_async { ; x; -[ - (primary_expression/identifier)@name - ; FIXME expansion of _lhs_expression/identifier and _augmented_assignment_lhs - (for_in_statement ["var" "let" "const"]?@is_def left:(identifier)@name) - (assignment_expression left:(identifier)@name) - (augmented_assignment_expression left:(identifier)@name) - (asserts (identifier)@name) - (type_predicate name:(identifier)@name) - ; FIXME type_query has its own restricted expression production - ; we need to do this for every (identifier) inside a type query - ; this cannot be expressed, so we manually unroll three levels here - (type_query (identifier)@name) - (type_query (member_expression object:(identifier)@name)) - (type_query (member_expression object:(member_expression object:(identifier)@name))) - (type_query (member_expression object:(member_expression object:(member_expression object:(identifier)@name)))) - (type_query (subscript_expression object:(identifier)@name)) - (type_query (subscript_expression object:(member_expression object:(identifier)@name))) - (type_query (subscript_expression object:(member_expression object:(member_expression object:(identifier)@name)))) - (type_query (call_expression function:(identifier)@name)) - (type_query (call_expression function:(member_expression object:(identifier)@name))) - (type_query (call_expression function:(member_expression object:(member_expression object:(identifier)@name)))) - (type_query (call_expression function:(member_expression object:(member_expression object:(member_expression object:(identifier)@name))))) - (type_query (call_expression function:(subscript_expression object:(identifier)@name))) - (type_query (call_expression function:(subscript_expression object:(member_expression object:(identifier)@name)))) - (type_query (call_expression function:(subscript_expression object:(member_expression object:(member_expression object:(identifier)@name))))) - ; FIXME decorator has its own restricted expression production - ; we need to do this for every (identifier) inside a decorator - ; this cannot be expressed, so we manually unroll three levels here - (decorator (identifier)@name) - (decorator (member_expression object:(identifier)@name)) - (decorator (member_expression object:(member_expression object:(identifier)@name))) - (decorator (member_expression object:(member_expression object:(member_expression object:(identifier)@name)))) - (decorator (call_expression function:(identifier)@name)) - (decorator (call_expression function:(member_expression object:(identifier)@name))) - (decorator (call_expression function:(member_expression object:(member_expression object:(identifier)@name)))) - (decorator (call_expression function:(member_expression object:(member_expression object:(member_expression object:(identifier)@name))))) -; #dialect tsx - (nested_identifier object:(_)@name) - (nested_type_identifier module:(nested_identifier)@name) - (internal_module name:(_)@name) - (jsx_opening_element name: (_)@name) - (jsx_closing_element name: (_)@name) - (jsx_self_closing_element name: (_)@name) -; #end -] { -if none @is_def { +(identifier)@name { node @name.cotype node @name.lexical_defs node @name.lexical_scope node @name.type node @name.var_defs -} -} -[ - (primary_expression/identifier)@name - (decorator (identifier)@name) - ; FIXME expansion of _lhs_expression/identifier and _augmented_assignment_lhs - ; we need to do this for every (identifier) inside a type query - ; this cannot be expressed, so we manually unroll three levels here - (for_in_statement ["var" "let" "const"]?@is_def left:(identifier)@name) - (assignment_expression left:(identifier)@name) - (augmented_assignment_expression left:(identifier)@name) - (asserts (identifier)@name) - (type_predicate name:(identifier)@name) - ; FIXME type_query has its own restricted expression production - (type_query (identifier)@name) - (type_query (member_expression object:(identifier)@name)) - (type_query (member_expression object:(member_expression object:(identifier)@name))) - (type_query (member_expression object:(member_expression object:(member_expression object:(identifier)@name)))) - (type_query (subscript_expression object:(identifier)@name)) - (type_query (subscript_expression object:(member_expression object:(identifier)@name))) - (type_query (subscript_expression object:(member_expression object:(member_expression object:(identifier)@name)))) - (type_query (call_expression function:(identifier)@name)) - (type_query (call_expression function:(member_expression object:(identifier)@name))) - (type_query (call_expression function:(member_expression object:(member_expression object:(identifier)@name)))) - (type_query (call_expression function:(member_expression object:(member_expression object:(member_expression object:(identifier)@name))))) - (type_query (call_expression function:(subscript_expression object:(identifier)@name))) - (type_query (call_expression function:(subscript_expression object:(member_expression object:(identifier)@name)))) - (type_query (call_expression function:(subscript_expression object:(member_expression object:(member_expression object:(identifier)@name))))) - ; FIXME decorator has its own restricted expression production - ; we need to do this for every (identifier) inside a decorator - ; this cannot be expressed, so we manually unroll three levels here - (decorator (identifier)@name) - (decorator (member_expression object:(identifier)@name)) - (decorator (member_expression object:(member_expression object:(identifier)@name))) - (decorator (member_expression object:(member_expression object:(member_expression object:(identifier)@name)))) - (decorator (call_expression function:(identifier)@name)) - (decorator (call_expression function:(member_expression object:(identifier)@name))) - (decorator (call_expression function:(member_expression object:(member_expression object:(identifier)@name)))) - (decorator (call_expression function:(member_expression object:(member_expression object:(member_expression object:(identifier)@name))))) -; #dialect tsx -; (nested_identifier object:(identifier)@name property:(identifier)) ; to pick up foo in JSX: ; FIXME never matched - (jsx_opening_element name: (identifier)@name) - (jsx_closing_element name: (identifier)@name) - (jsx_self_closing_element name: (identifier)@name) -; #end -] { -if none @is_def { node @name.expr_ref node @name.expr_ref__ns node @name.expr_ref__typeof @@ -2898,7 +2813,67 @@ if none @is_def { attr (@name.expr_ref__typeof) push_symbol = ":" edge @name.expr_ref__typeof -> @name.expr_ref } -} + +; [ +; (primary_expression/identifier)@name +; ; FIXME expansion of _lhs_expression/identifier and _augmented_assignment_lhs +; ; we need to do this for every (identifier) inside a type query +; ; this cannot be expressed, so we manually unroll three levels here +; (for_in_statement ["var" "let" "const"]?@is_def left:(identifier)@name) +; (assignment_expression left:(identifier)@name) +; (augmented_assignment_expression left:(identifier)@name) +; (asserts (identifier)@name) +; (type_predicate name:(identifier)@name) +; ; FIXME type_query has its own restricted expression production +; ; we need to do this for every (identifier) inside a type query +; ; this cannot be expressed, so we manually unroll three levels here +; (type_query (identifier)@name) +; (type_query (member_expression object:(identifier)@name)) +; (type_query (member_expression object:(member_expression object:(identifier)@name))) +; (type_query (member_expression object:(member_expression object:(member_expression object:(identifier)@name)))) +; (type_query (subscript_expression object:(identifier)@name)) +; (type_query (subscript_expression object:(member_expression object:(identifier)@name))) +; (type_query (subscript_expression object:(member_expression object:(member_expression object:(identifier)@name)))) +; (type_query (call_expression function:(identifier)@name)) +; (type_query (call_expression function:(member_expression object:(identifier)@name))) +; (type_query (call_expression function:(member_expression object:(member_expression object:(identifier)@name)))) +; (type_query (call_expression function:(member_expression object:(member_expression object:(member_expression object:(identifier)@name))))) +; (type_query (call_expression function:(subscript_expression object:(identifier)@name))) +; (type_query (call_expression function:(subscript_expression object:(member_expression object:(identifier)@name)))) +; (type_query (call_expression function:(subscript_expression object:(member_expression object:(member_expression object:(identifier)@name))))) +; ; FIXME decorator has its own restricted expression production +; ; we need to do this for every (identifier) inside a decorator +; ; this cannot be expressed, so we manually unroll three levels here +; (decorator (identifier)@name) +; (decorator (member_expression object:(identifier)@name)) +; (decorator (member_expression object:(member_expression object:(identifier)@name))) +; (decorator (member_expression object:(member_expression object:(member_expression object:(identifier)@name)))) +; (decorator (call_expression function:(identifier)@name)) +; (decorator (call_expression function:(member_expression object:(identifier)@name))) +; (decorator (call_expression function:(member_expression object:(member_expression object:(identifier)@name)))) +; (decorator (call_expression function:(member_expression object:(member_expression object:(member_expression object:(identifier)@name))))) +; ; FIXME nested_identifier has its own restricted expression production +; ; we need to do this for every (identifier) inside a decorator +; ; this cannot be expressed, so we manually unroll three levels here +; (nested_identifier object:(identifier)@name) +; (nested_identifier object:(member_expression object:(identifier)@name)) +; (nested_identifier object:(member_expression object:(member_expression object:(identifier)@name))) +; (nested_identifier object:(member_expression object:(member_expression object:(member_expression object:(identifier)@name)))) +; ; #dialect tsx +; (jsx_opening_element name: (identifier)@name) +; (jsx_closing_element name: (identifier)@name) +; (jsx_self_closing_element name: (identifier)@name) +; ; #end +; ] { +; if none @is_def { +; ; node @name.cotype +; ; node @name.lexical_defs +; ; node @name.lexical_scope +; ; node @name.type +; ; node @name.var_defs + +; } +; } @@ -3551,12 +3526,10 @@ if none @is_async { ; (member_expression (identifier) (property_identifier)) ; (subscript_expression (identifier) (string)) -[ - (member_expression object:(_)@object property:(_)@prop)@member_expr -; #dialect tsx -; (nested_identifier object:(_)@object property:(identifier)@prop)@member_expr ; FIXME this never matched anything! -; #end - ] { +(member_expression + object:(_)@object + property:(_)@prop +)@member_expr { node @member_expr.member node @prop.expr_ref node @prop.expr_ref__typeof @@ -3578,7 +3551,6 @@ if none @is_async { edge @prop.expr_ref__typeof -> @prop.expr_ref } - (subscript_expression object: (_)@object index: (_)@index @@ -4158,16 +4130,6 @@ if none @is_async { node @pat.defs } -[ ; NOTE these are the ones not also variables - (for_in_statement ["var" "let" "const"] left:(identifier)@name) - (variable_declarator name:(identifier)@name) - (pattern/identifier)@name - (rest_pattern (identifier)@name) -] { - node @name.cotype - node @name.lexical_scope -} - [ (for_in_statement ["var" "let" "const"] left:(identifier)@name) (variable_declarator name:(identifier)@name) @@ -5603,8 +5565,14 @@ if none @is_acc { [ ; X (nested_type_identifier module:(identifier)@name) - ; X._, _.X._ + ; X._ (nested_identifier object:(identifier)@name) + ; _.X._ + (member_expression object:(identifier)@name) + ; _.X + (nested_identifier property:(_)@name) + ; _._.X + (member_expression property:(_)@name) ] { node @name.type_ref attr (@name.type_ref) node_reference = @name @@ -5615,16 +5583,25 @@ if none @is_acc { edge @name.type_ref_member -> @name.type_ref } -(nested_identifier object:(_)@mod) @nested { +[ + (nested_identifier object:(_)@mod) + (member_expression object:[(member_expression) (identifier)]@mod) +]@nested { node @nested.type_ref edge @mod.type_ref -> @nested.type_ref } -(nested_identifier . (_)@mod . (_)@name .) { +[ + (nested_identifier object:(_)@mod property:(_)@name) + (member_expression object:[(member_expression) (identifier)]@mod property:(_)@name) +] { edge @name.type_ref -> @mod.type_ref_member } -(nested_identifier property:(_)@name) @nested { +[ + (nested_identifier property:(_)@name) + (member_expression property:(_)@name) +]@nested { node @nested.type_ref_member edge @nested.type_ref_member -> @name.type_ref_member } diff --git a/languages/tree-sitter-stack-graphs-typescript/test/jsx/jsx_namespace_name.tsx.skip b/languages/tree-sitter-stack-graphs-typescript/test/jsx/jsx_namespace_name.tsx similarity index 100% rename from languages/tree-sitter-stack-graphs-typescript/test/jsx/jsx_namespace_name.tsx.skip rename to languages/tree-sitter-stack-graphs-typescript/test/jsx/jsx_namespace_name.tsx diff --git a/languages/tree-sitter-stack-graphs-typescript/test/modules/very-deep-namespace.ts.skip b/languages/tree-sitter-stack-graphs-typescript/test/modules/very-deep-namespace.ts similarity index 100% rename from languages/tree-sitter-stack-graphs-typescript/test/modules/very-deep-namespace.ts.skip rename to languages/tree-sitter-stack-graphs-typescript/test/modules/very-deep-namespace.ts diff --git a/languages/tree-sitter-stack-graphs-typescript/test/statements/refer-to-interface-in-nested-namespace-by-fully-qualified-name.ts.skip b/languages/tree-sitter-stack-graphs-typescript/test/statements/refer-to-interface-in-nested-namespace-by-fully-qualified-name.ts similarity index 100% rename from languages/tree-sitter-stack-graphs-typescript/test/statements/refer-to-interface-in-nested-namespace-by-fully-qualified-name.ts.skip rename to languages/tree-sitter-stack-graphs-typescript/test/statements/refer-to-interface-in-nested-namespace-by-fully-qualified-name.ts diff --git a/languages/tree-sitter-stack-graphs-typescript/test/statements/refer-to-interface-in-qualified-namespace-by-fully-qualified-name.ts.skip b/languages/tree-sitter-stack-graphs-typescript/test/statements/refer-to-interface-in-qualified-namespace-by-fully-qualified-name.ts similarity index 100% rename from languages/tree-sitter-stack-graphs-typescript/test/statements/refer-to-interface-in-qualified-namespace-by-fully-qualified-name.ts.skip rename to languages/tree-sitter-stack-graphs-typescript/test/statements/refer-to-interface-in-qualified-namespace-by-fully-qualified-name.ts From bba2dc482d1206209837f699f49527895c48619e Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Thu, 12 Dec 2024 12:32:35 +0100 Subject: [PATCH 275/291] Try updated runner image to fix valgrind problem --- .github/workflows/ci.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0d6ec91bb..f217e8140 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,7 +13,9 @@ concurrency: jobs: test-rust: - runs-on: ubuntu-latest + # We force a newer runner version to fix a problem with Valgrind. + # Currently, ubuntu-latest is set to 22.04. Revert this back to ubuntu-latest once it is set to 24.04. + runs-on: ubuntu-24.04 strategy: matrix: rust: [stable] From aea8b85a19746326c8e707f6b10ea724b6f78c74 Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Thu, 12 Dec 2024 12:06:30 +0000 Subject: [PATCH 276/291] Ignore Valgrind warnings introduced by Rust 1.83 --- .github/workflows/ci.yml | 4 +++ stack-graphs/valgrind.supp | 74 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 78 insertions(+) create mode 100644 stack-graphs/valgrind.supp diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f217e8140..36cab862c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -57,6 +57,10 @@ jobs: - name: Run test suite under valgrind (default features) # We only need to use valgrind to test the crates that have C bindings. run: cargo valgrind test -p stack-graphs + env: + # Since Rust 1.83, Valgrind reports some possible leaks that cargo-valgrind + # treats as fatal. The given suppressions file makes sure these are ignored. + VALGRINDFLAGS: --suppressions=valgrind.supp - name: Ensure C headers are up to date run: | script/cbindgen diff --git a/stack-graphs/valgrind.supp b/stack-graphs/valgrind.supp new file mode 100644 index 000000000..3986abc5b --- /dev/null +++ b/stack-graphs/valgrind.supp @@ -0,0 +1,74 @@ +{ + rust-1.83-false-positive-1 + Memcheck:Param + statx(file_name) + fun:statx + fun:statx + fun:_ZN3std3sys3pal4unix2fs9try_statx17h2609435043bb8525E + fun:{closure#0} + fun:run_with_cstr_stack + fun:run_with_cstr + fun:run_path_with_cstr + fun:_ZN3std3sys3pal4unix2fs4stat17ha588398797a44835E + fun:metadata<&std::path::PathBuf> + fun:get_dbpath_for_term + fun:from_name + fun:_ZN4test4term8terminfo8TermInfo8from_env17h9b3ad5763ddd396cE + fun:new + fun:_ZN4test4term6stdout17hf274a19176f765bcE + fun:_ZN4test7console17run_tests_console17h3d81643311f50681E + fun:_ZN4test9test_main17h6c0fc3221ed0faa4E + fun:_ZN4test16test_main_static17h897b969e33229363E + fun:_ZN2it4main17hfbc3179ed1c35941E + fun:_ZN4core3ops8function6FnOnce9call_once17h945f64a17647f84cE + fun:_ZN3std3sys9backtrace28__rust_begin_short_backtrace17h41138d0332b09746E + fun:_ZN3std2rt10lang_start28_$u7b$$u7b$closure$u7d$$u7d$17h98fa5fc32f9b0a38E +} +{ + rust-1.83-false-positive-2 + Memcheck:Param + statx(buf) + fun:statx + fun:statx + fun:_ZN3std3sys3pal4unix2fs9try_statx17h2609435043bb8525E + fun:{closure#0} + fun:run_with_cstr_stack + fun:run_with_cstr + fun:run_path_with_cstr + fun:_ZN3std3sys3pal4unix2fs4stat17ha588398797a44835E + fun:metadata<&std::path::PathBuf> + fun:get_dbpath_for_term + fun:from_name + fun:_ZN4test4term8terminfo8TermInfo8from_env17h9b3ad5763ddd396cE + fun:new + fun:_ZN4test4term6stdout17hf274a19176f765bcE + fun:_ZN4test7console17run_tests_console17h3d81643311f50681E + fun:_ZN4test9test_main17h6c0fc3221ed0faa4E + fun:_ZN4test16test_main_static17h897b969e33229363E + fun:_ZN2it4main17hfbc3179ed1c35941E + fun:_ZN4core3ops8function6FnOnce9call_once17h945f64a17647f84cE + fun:_ZN3std3sys9backtrace28__rust_begin_short_backtrace17h41138d0332b09746E + fun:_ZN3std2rt10lang_start28_$u7b$$u7b$closure$u7d$$u7d$17h98fa5fc32f9b0a38E +} +{ + rust-1.83-false-positive-3 + Memcheck:Leak + match-leak-kinds: possible + fun:malloc + fun:alloc + fun:alloc_impl + fun:allocate + fun:{closure#0} + fun:allocate_for_layout, alloc::sync::{impl#14}::new_uninit::{closure_env#0}, fn(*mut u8) -> *mut alloc::sync::ArcInner>> + fun:new_uninit + fun:new_inner + fun:new_main + fun:init + fun:{closure#0} + fun:do_call + fun:try<(), std::rt::lang_start_internal::{closure_env#0}> + fun:catch_unwind + fun:_ZN3std2rt19lang_start_internal17h1c66660c99c8424cE + fun:_ZN3std2rt10lang_start17hb778ad044944e8a4E + fun:main +} From af722227cfdffc9823bb40c39be89cfcc763b5ef Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Thu, 12 Dec 2024 13:14:42 +0100 Subject: [PATCH 277/291] Set release dates --- languages/tree-sitter-stack-graphs-java/CHANGELOG.md | 2 +- languages/tree-sitter-stack-graphs-javascript/CHANGELOG.md | 2 +- languages/tree-sitter-stack-graphs-python/CHANGELOG.md | 2 +- lsp-positions/CHANGELOG.md | 2 +- stack-graphs/CHANGELOG.md | 2 +- tree-sitter-stack-graphs/CHANGELOG.md | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/languages/tree-sitter-stack-graphs-java/CHANGELOG.md b/languages/tree-sitter-stack-graphs-java/CHANGELOG.md index 23400faf0..d295d8ce0 100644 --- a/languages/tree-sitter-stack-graphs-java/CHANGELOG.md +++ b/languages/tree-sitter-stack-graphs-java/CHANGELOG.md @@ -5,7 +5,7 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## v0.5.0 -- Unreleased +## v0.5.0 -- 2024-12-12 - The `tree-sitter-stack-graphs` dependency is updated to version 0.10. diff --git a/languages/tree-sitter-stack-graphs-javascript/CHANGELOG.md b/languages/tree-sitter-stack-graphs-javascript/CHANGELOG.md index 10f85abdd..ae90a2f01 100644 --- a/languages/tree-sitter-stack-graphs-javascript/CHANGELOG.md +++ b/languages/tree-sitter-stack-graphs-javascript/CHANGELOG.md @@ -5,7 +5,7 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## v0.3.0 -- Unreleased +## v0.3.0 -- 2024-12-12 - The `tree-sitter-stack-graphs` dependency is updated to version 0.10. diff --git a/languages/tree-sitter-stack-graphs-python/CHANGELOG.md b/languages/tree-sitter-stack-graphs-python/CHANGELOG.md index 8ec44913d..f21aecc9f 100644 --- a/languages/tree-sitter-stack-graphs-python/CHANGELOG.md +++ b/languages/tree-sitter-stack-graphs-python/CHANGELOG.md @@ -5,7 +5,7 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## v0.3.0 -- Unreleased +## v0.3.0 -- 2024-12-12 - The `tree-sitter-stack-graphs` dependency is updated to version 0.10. diff --git a/lsp-positions/CHANGELOG.md b/lsp-positions/CHANGELOG.md index 84aa962b4..f80bc5c5d 100644 --- a/lsp-positions/CHANGELOG.md +++ b/lsp-positions/CHANGELOG.md @@ -5,7 +5,7 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## v0.3.4 -- Unreleased +## v0.3.4 -- 2024-12-12 Upgraded the `tree-sitter` dependency to version 0.24. diff --git a/stack-graphs/CHANGELOG.md b/stack-graphs/CHANGELOG.md index aae6a2998..802275fc2 100644 --- a/stack-graphs/CHANGELOG.md +++ b/stack-graphs/CHANGELOG.md @@ -5,7 +5,7 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## v0.14.1 -- Unpublished +## v0.14.1 -- 2024-12-12 ### Fixed diff --git a/tree-sitter-stack-graphs/CHANGELOG.md b/tree-sitter-stack-graphs/CHANGELOG.md index b45b69b64..e1df552e4 100644 --- a/tree-sitter-stack-graphs/CHANGELOG.md +++ b/tree-sitter-stack-graphs/CHANGELOG.md @@ -5,7 +5,7 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## v0.10.0 -- Unreleased +## v0.10.0 -- 2024-12-12 Upgraded `tree-sitter` dependency to version 0.24. From 0bd07ba011ac7ae519e246af395e0c80d85eec8f Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Thu, 12 Dec 2024 13:19:48 +0100 Subject: [PATCH 278/291] Update test dependency version --- script/ci-test-init | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/script/ci-test-init b/script/ci-test-init index c58532fd5..0ecc60788 100755 --- a/script/ci-test-init +++ b/script/ci-test-init @@ -7,7 +7,7 @@ cargo run --bin tree-sitter-stack-graphs --features cli -- init \ --language-id init_test \ --language-file-extension it \ --grammar-crate-name tree-sitter-python \ - --grammar-crate-version 0.20.0 \ + --grammar-crate-version 0.23.2 \ --internal \ --non-interactive From 68ce497b225fdccf34347d87b6458aca41b8bb5e Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Thu, 12 Dec 2024 13:51:20 +0100 Subject: [PATCH 279/291] Bump actions versions --- .github/workflows/ci.yml | 18 +++++------ .github/workflows/perf.yml | 30 +++++++++---------- .github/workflows/publish-lsp-positions.yml | 4 +-- .github/workflows/publish-stack-graphs.yml | 4 +-- .../publish-tree-sitter-stack-graphs-java.yml | 4 +-- ...sh-tree-sitter-stack-graphs-javascript.yml | 4 +-- ...ublish-tree-sitter-stack-graphs-python.yml | 4 +-- ...sh-tree-sitter-stack-graphs-typescript.yml | 4 +-- .../publish-tree-sitter-stack-graphs.yml | 4 +-- 9 files changed, 38 insertions(+), 38 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 36cab862c..41105a33c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -31,11 +31,11 @@ jobs: - name: Install cargo-hack run: cargo install cargo-hack - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Check formatting run: cargo fmt --all -- --check - name: Cache dependencies - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: | ~/.cargo @@ -79,9 +79,9 @@ jobs: with: rust-version: ${{ matrix.rust }} - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Cache dependencies - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: | ~/.cargo @@ -99,7 +99,7 @@ jobs: working-directory: languages steps: - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: List languages id: language-list run: echo "languages=$(find -mindepth 1 -maxdepth 1 -type d -printf '%P\n' | jq -R -s -c 'split("\n")[:-1]')" >> $GITHUB_OUTPUT @@ -122,7 +122,7 @@ jobs: - name: Install cargo-hack run: cargo install cargo-hack - name: Cache dependencies - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: | ~/.cargo @@ -131,7 +131,7 @@ jobs: restore-keys: | ${{ runner.OS }}-cargo- - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Build (all feature combinations) run: cargo hack -p ${{ matrix.language }} --feature-powerset build - name: Test (all features) @@ -152,7 +152,7 @@ jobs: with: rust-version: ${{ matrix.rust }} - name: Cache dependencies - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: | ~/.cargo @@ -161,7 +161,7 @@ jobs: restore-keys: | ${{ runner.OS }}-cargo- - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: lfs: true - name: Build diff --git a/.github/workflows/perf.yml b/.github/workflows/perf.yml index 5c4288c84..7da0f9ef8 100644 --- a/.github/workflows/perf.yml +++ b/.github/workflows/perf.yml @@ -37,7 +37,7 @@ jobs: done: ${{ steps.done.outputs.cache-hit }} steps: - name: "Checkout base code" - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: repository: ${{ env.BASE_REPO }} ref: ${{ env.BASE_SHA }} @@ -49,7 +49,7 @@ jobs: printf 'BASE_SHA=%s\n' "$(git rev-list -1 ${{ env.BASE_SHA }} -- stack-graphs)" >> $GITHUB_ENV working-directory: ${{ env.BASE_DIR }} - name: "Checkout head code" - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: repository: ${{ env.HEAD_REPO }} ref: ${{ env.HEAD_SHA }} @@ -62,7 +62,7 @@ jobs: working-directory: ${{ env.HEAD_DIR }} - name: "Check cached status" id: done - uses: actions/cache/restore@v3 + uses: actions/cache/restore@v4 with: path: done key: ${{ runner.os }}-perf-tested-${{ env.BASE_REPO }}@${{ env.BASE_SHA }}-${{ env.HEAD_REPO }}@${{ env.HEAD_SHA }}-${{ env.TEST_NAME }} @@ -83,7 +83,7 @@ jobs: with: rust-version: stable - name: Cache Rust dependencies - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: | ~/.cargo @@ -94,7 +94,7 @@ jobs: sudo apt-get install -y valgrind - name: "Cache base result" id: cache-base-result - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: | ${{ env.MASSIF_OUT }} @@ -102,7 +102,7 @@ jobs: key: ${{ runner.os }}-perf-result-${{ env.BASE_REPO }}@${{ env.BASE_SHA }}-${{ env.TEST_NAME }} - name: "Checkout base code" if: steps.cache-base-result.outputs.cache-hit != 'true' - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: repository: ${{ env.BASE_REPO }} ref: ${{ env.BASE_SHA }} @@ -130,7 +130,7 @@ jobs: ${{ env.BASE_DIR }}/data/${{ env.TEST_NAME }} ms_print ${{ env.MASSIF_OUT }} > ${{ env.MASSIF_REPORT }} - name: Upload results - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: ${{ env.BASE_ARTIFACT }} path: | @@ -152,7 +152,7 @@ jobs: with: rust-version: stable - name: Cache Rust dependencies - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: | ~/.cargo @@ -163,7 +163,7 @@ jobs: sudo apt-get install -y valgrind - name: "Cache head result" id: cache-head-result - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: | ${{ env.MASSIF_OUT }} @@ -171,7 +171,7 @@ jobs: key: ${{ runner.os }}-perf-result-${{ env.HEAD_REPO }}@${{ env.HEAD_SHA }}-${{ env.TEST_NAME }} - name: "Checkout head code" if: steps.cache-head-result.outputs.cache-hit != 'true' - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: repository: ${{ env.HEAD_REPO }} ref: ${{ env.HEAD_SHA }} @@ -199,7 +199,7 @@ jobs: ${{ env.HEAD_DIR }}/data/${{ env.TEST_NAME }} ms_print ${{ env.MASSIF_OUT }} > ${{ env.MASSIF_REPORT }} - name: Upload results - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: ${{ env.HEAD_ARTIFACT }} path: | @@ -233,12 +233,12 @@ jobs: # Download results # - name: Download base results - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: name: ${{ env.BASE_ARTIFACT }} path: ${{ env.BASE_ARTIFACT }} - name: Download head results - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: name: ${{ env.HEAD_ARTIFACT }} path: ${{ env.HEAD_ARTIFACT }} @@ -246,7 +246,7 @@ jobs: # Create report # - name: "Checkout code" - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: path: ${{ env.SRC_DIR }} - name: Generate summary @@ -268,7 +268,7 @@ jobs: - name: Create status marker run: touch done - name: "Cache status" - uses: actions/cache/save@v3 + uses: actions/cache/save@v4 with: path: done key: ${{ runner.os }}-perf-tested-${{ env.BASE_REPO }}@${{ env.BASE_SHA }}-${{ env.HEAD_REPO }}@${{ env.HEAD_SHA }}-${{ env.TEST_NAME }} diff --git a/.github/workflows/publish-lsp-positions.yml b/.github/workflows/publish-lsp-positions.yml index 210662a00..dd021646b 100644 --- a/.github/workflows/publish-lsp-positions.yml +++ b/.github/workflows/publish-lsp-positions.yml @@ -16,7 +16,7 @@ jobs: - name: Install Rust environment uses: hecrj/setup-rust-action@v1 - name: Checkout repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 # TODO Verify the crate version matches the tag - name: Test crate run: cargo test --all-features @@ -36,7 +36,7 @@ jobs: contents: write steps: - name: Checkout repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Create GitHub release uses: ncipollo/release-action@v1 with: diff --git a/.github/workflows/publish-stack-graphs.yml b/.github/workflows/publish-stack-graphs.yml index 641a93488..b94e0337f 100644 --- a/.github/workflows/publish-stack-graphs.yml +++ b/.github/workflows/publish-stack-graphs.yml @@ -16,7 +16,7 @@ jobs: - name: Install Rust environment uses: hecrj/setup-rust-action@v1 - name: Checkout repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 # TODO Verify the crate version matches the tag - name: Test crate run: cargo test --all-features @@ -36,7 +36,7 @@ jobs: contents: write steps: - name: Checkout repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Create GitHub release uses: ncipollo/release-action@v1 with: diff --git a/.github/workflows/publish-tree-sitter-stack-graphs-java.yml b/.github/workflows/publish-tree-sitter-stack-graphs-java.yml index 6d7afb769..64e9f3bf2 100644 --- a/.github/workflows/publish-tree-sitter-stack-graphs-java.yml +++ b/.github/workflows/publish-tree-sitter-stack-graphs-java.yml @@ -16,7 +16,7 @@ jobs: - name: Install Rust environment uses: hecrj/setup-rust-action@v1 - name: Checkout repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 # TODO Verify the crate version matches the tag - name: Test crate run: cargo test --all-features @@ -36,7 +36,7 @@ jobs: contents: write steps: - name: Checkout repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Create GitHub release uses: ncipollo/release-action@v1 with: diff --git a/.github/workflows/publish-tree-sitter-stack-graphs-javascript.yml b/.github/workflows/publish-tree-sitter-stack-graphs-javascript.yml index ff6826770..48375d9d4 100644 --- a/.github/workflows/publish-tree-sitter-stack-graphs-javascript.yml +++ b/.github/workflows/publish-tree-sitter-stack-graphs-javascript.yml @@ -16,7 +16,7 @@ jobs: - name: Install Rust environment uses: hecrj/setup-rust-action@v1 - name: Checkout repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 # TODO Verify the crate version matches the tag - name: Test crate run: cargo test --all-features @@ -36,7 +36,7 @@ jobs: contents: write steps: - name: Checkout repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Create GitHub release uses: ncipollo/release-action@v1 with: diff --git a/.github/workflows/publish-tree-sitter-stack-graphs-python.yml b/.github/workflows/publish-tree-sitter-stack-graphs-python.yml index 5d6c83ba2..77804c28e 100644 --- a/.github/workflows/publish-tree-sitter-stack-graphs-python.yml +++ b/.github/workflows/publish-tree-sitter-stack-graphs-python.yml @@ -16,7 +16,7 @@ jobs: - name: Install Rust environment uses: hecrj/setup-rust-action@v1 - name: Checkout repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 # TODO Verify the crate version matches the tag - name: Test crate run: cargo test --all-features @@ -36,7 +36,7 @@ jobs: contents: write steps: - name: Checkout repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Create GitHub release uses: ncipollo/release-action@v1 with: diff --git a/.github/workflows/publish-tree-sitter-stack-graphs-typescript.yml b/.github/workflows/publish-tree-sitter-stack-graphs-typescript.yml index a3409f397..3fdbe17e2 100644 --- a/.github/workflows/publish-tree-sitter-stack-graphs-typescript.yml +++ b/.github/workflows/publish-tree-sitter-stack-graphs-typescript.yml @@ -16,7 +16,7 @@ jobs: - name: Install Rust environment uses: hecrj/setup-rust-action@v1 - name: Checkout repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 # TODO Verify the crate version matches the tag - name: Test crate run: cargo test --all-features @@ -36,7 +36,7 @@ jobs: contents: write steps: - name: Checkout repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Create GitHub release uses: ncipollo/release-action@v1 with: diff --git a/.github/workflows/publish-tree-sitter-stack-graphs.yml b/.github/workflows/publish-tree-sitter-stack-graphs.yml index 031289464..2dfb743a1 100644 --- a/.github/workflows/publish-tree-sitter-stack-graphs.yml +++ b/.github/workflows/publish-tree-sitter-stack-graphs.yml @@ -16,7 +16,7 @@ jobs: - name: Install Rust environment uses: hecrj/setup-rust-action@v1 - name: Checkout repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 # TODO Verify the crate version matches the tag - name: Test crate run: cargo test --all-features @@ -36,7 +36,7 @@ jobs: contents: write steps: - name: Checkout repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Create GitHub release uses: ncipollo/release-action@v1 with: From b3409fca73900d01656d02ddea86084a37b25e8d Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Thu, 12 Dec 2024 13:59:37 +0100 Subject: [PATCH 280/291] Revert "Try updated runner image to fix valgrind problem" This reverts commit bba2dc482d1206209837f699f49527895c48619e. --- .github/workflows/ci.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 41105a33c..0b025b26d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,9 +13,7 @@ concurrency: jobs: test-rust: - # We force a newer runner version to fix a problem with Valgrind. - # Currently, ubuntu-latest is set to 22.04. Revert this back to ubuntu-latest once it is set to 24.04. - runs-on: ubuntu-24.04 + runs-on: ubuntu-latest strategy: matrix: rust: [stable] From 11c05cb0b49c9330ad077fdfce13768d016e449d Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Thu, 12 Dec 2024 13:19:15 +0000 Subject: [PATCH 281/291] Simplify suppresion traces --- stack-graphs/valgrind.supp | 56 ++++---------------------------------- 1 file changed, 6 insertions(+), 50 deletions(-) diff --git a/stack-graphs/valgrind.supp b/stack-graphs/valgrind.supp index 3986abc5b..dbccaa257 100644 --- a/stack-graphs/valgrind.supp +++ b/stack-graphs/valgrind.supp @@ -3,72 +3,28 @@ Memcheck:Param statx(file_name) fun:statx - fun:statx - fun:_ZN3std3sys3pal4unix2fs9try_statx17h2609435043bb8525E - fun:{closure#0} - fun:run_with_cstr_stack - fun:run_with_cstr - fun:run_path_with_cstr - fun:_ZN3std3sys3pal4unix2fs4stat17ha588398797a44835E - fun:metadata<&std::path::PathBuf> + ... fun:get_dbpath_for_term - fun:from_name - fun:_ZN4test4term8terminfo8TermInfo8from_env17h9b3ad5763ddd396cE - fun:new - fun:_ZN4test4term6stdout17hf274a19176f765bcE - fun:_ZN4test7console17run_tests_console17h3d81643311f50681E - fun:_ZN4test9test_main17h6c0fc3221ed0faa4E - fun:_ZN4test16test_main_static17h897b969e33229363E - fun:_ZN2it4main17hfbc3179ed1c35941E - fun:_ZN4core3ops8function6FnOnce9call_once17h945f64a17647f84cE - fun:_ZN3std3sys9backtrace28__rust_begin_short_backtrace17h41138d0332b09746E - fun:_ZN3std2rt10lang_start28_$u7b$$u7b$closure$u7d$$u7d$17h98fa5fc32f9b0a38E + ... } { rust-1.83-false-positive-2 Memcheck:Param statx(buf) fun:statx - fun:statx - fun:_ZN3std3sys3pal4unix2fs9try_statx17h2609435043bb8525E - fun:{closure#0} - fun:run_with_cstr_stack - fun:run_with_cstr - fun:run_path_with_cstr - fun:_ZN3std3sys3pal4unix2fs4stat17ha588398797a44835E - fun:metadata<&std::path::PathBuf> + ... fun:get_dbpath_for_term - fun:from_name - fun:_ZN4test4term8terminfo8TermInfo8from_env17h9b3ad5763ddd396cE - fun:new - fun:_ZN4test4term6stdout17hf274a19176f765bcE - fun:_ZN4test7console17run_tests_console17h3d81643311f50681E - fun:_ZN4test9test_main17h6c0fc3221ed0faa4E - fun:_ZN4test16test_main_static17h897b969e33229363E - fun:_ZN2it4main17hfbc3179ed1c35941E - fun:_ZN4core3ops8function6FnOnce9call_once17h945f64a17647f84cE - fun:_ZN3std3sys9backtrace28__rust_begin_short_backtrace17h41138d0332b09746E - fun:_ZN3std2rt10lang_start28_$u7b$$u7b$closure$u7d$$u7d$17h98fa5fc32f9b0a38E + ... } { rust-1.83-false-positive-3 Memcheck:Leak match-leak-kinds: possible fun:malloc - fun:alloc - fun:alloc_impl - fun:allocate - fun:{closure#0} - fun:allocate_for_layout, alloc::sync::{impl#14}::new_uninit::{closure_env#0}, fn(*mut u8) -> *mut alloc::sync::ArcInner>> + ... fun:new_uninit fun:new_inner fun:new_main fun:init - fun:{closure#0} - fun:do_call - fun:try<(), std::rt::lang_start_internal::{closure_env#0}> - fun:catch_unwind - fun:_ZN3std2rt19lang_start_internal17h1c66660c99c8424cE - fun:_ZN3std2rt10lang_start17hb778ad044944e8a4E - fun:main + ... } From e3b1afea05c5c32244c5fee06a5bfe649fc4d505 Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Thu, 12 Dec 2024 14:33:36 +0100 Subject: [PATCH 282/291] Save valgrind logs on failure --- .github/workflows/ci.yml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0b025b26d..1c2c613d9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -58,7 +58,13 @@ jobs: env: # Since Rust 1.83, Valgrind reports some possible leaks that cargo-valgrind # treats as fatal. The given suppressions file makes sure these are ignored. - VALGRINDFLAGS: --suppressions=valgrind.supp + VALGRINDFLAGS: --suppressions=valgrind.supp --gen-suppressions=all --log-file=${{ runner.temp }}/valgrind.log + - name: Upload valgrind log + if: ${{ failure() }} + uses: actions/upload-artifact@v4 + with: + name: valgrind logs + path: ${{ runner.temp }}/valgrind.log - name: Ensure C headers are up to date run: | script/cbindgen From 9998fbd2f42b1665475aa2cbf71515d4fa8abf2c Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Thu, 12 Dec 2024 14:21:27 +0000 Subject: [PATCH 283/291] Custom valgrind runner to allow capturing logs --- .github/workflows/ci.yml | 12 ++++------- script/ci-test-valgrind | 23 +++++++++++++++++++++ stack-graphs/valgrind.supp => valgrind.supp | 0 3 files changed, 27 insertions(+), 8 deletions(-) create mode 100755 script/ci-test-valgrind rename stack-graphs/valgrind.supp => valgrind.supp (100%) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1c2c613d9..adc70b3c8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -47,20 +47,16 @@ jobs: run: ${{ env.CARGO_HACK }} test - name: Run test suite with all optimizations (default features) run: cargo test --release - - name: Install cargo-valgrind + - name: Install valgrind run: | sudo apt-get update sudo apt-get install -y valgrind - cargo install cargo-valgrind - name: Run test suite under valgrind (default features) + id: valgrind # We only need to use valgrind to test the crates that have C bindings. - run: cargo valgrind test -p stack-graphs - env: - # Since Rust 1.83, Valgrind reports some possible leaks that cargo-valgrind - # treats as fatal. The given suppressions file makes sure these are ignored. - VALGRINDFLAGS: --suppressions=valgrind.supp --gen-suppressions=all --log-file=${{ runner.temp }}/valgrind.log + run: script/ci-test-valgrind -p stack-graphs - name: Upload valgrind log - if: ${{ failure() }} + if: ${{ failure() && steps.valgrind.outcome == 'failure' }} uses: actions/upload-artifact@v4 with: name: valgrind logs diff --git a/script/ci-test-valgrind b/script/ci-test-valgrind new file mode 100755 index 000000000..3f5e3dae6 --- /dev/null +++ b/script/ci-test-valgrind @@ -0,0 +1,23 @@ +#!/usr/bin/env bash + +set -eu + +# Start by building the test binary, before we try to get its filename +cargo test "$@" --no-run + +# Cargo does not have a clean way to get the test binary, so we do some text processing here to get it +test="$(cargo test "$@" --no-run 2>&1 | grep 'Executable' | head -1 | sed -e 's/[^(]*(\(.*\)).*$/\1/')" +log="${RUNNER_TEMP-.}/valgrind.log" + +# Run the test binary under valgrind +if ! valgrind \ + --leak-check=full \ + --show-leak-kinds=all \ + --error-exitcode=1 \ + --suppressions=valgrind.supp \ + --gen-suppressions=all \ + --log-file="$log" \ + "$test"; then + echo "Valgrind detected errors! See logs: $log" + exit 1 +fi diff --git a/stack-graphs/valgrind.supp b/valgrind.supp similarity index 100% rename from stack-graphs/valgrind.supp rename to valgrind.supp From d2fd2281ef18af9c822e6cdcbc20f971c09be276 Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Thu, 12 Dec 2024 14:28:15 +0000 Subject: [PATCH 284/291] Update suppressions from CI log --- valgrind.supp | 27 +++------------------------ 1 file changed, 3 insertions(+), 24 deletions(-) diff --git a/valgrind.supp b/valgrind.supp index dbccaa257..19b8bb400 100644 --- a/valgrind.supp +++ b/valgrind.supp @@ -1,30 +1,9 @@ { rust-1.83-false-positive-1 - Memcheck:Param - statx(file_name) - fun:statx - ... - fun:get_dbpath_for_term - ... -} -{ - rust-1.83-false-positive-2 - Memcheck:Param - statx(buf) - fun:statx - ... - fun:get_dbpath_for_term - ... -} -{ - rust-1.83-false-positive-3 Memcheck:Leak match-leak-kinds: possible fun:malloc - ... - fun:new_uninit - fun:new_inner - fun:new_main - fun:init - ... + fun:_ZN3std2rt19lang_start_internal17h1c66660c99c8424cE + fun:_ZN3std2rt10lang_start17hb778ad044944e8a4E + fun:main } From 6223c06da3344bd5336cc61c70633985c4c73860 Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Thu, 12 Dec 2024 14:40:17 +0000 Subject: [PATCH 285/291] Remove mangled entries --- valgrind.supp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/valgrind.supp b/valgrind.supp index 19b8bb400..25c3cfbda 100644 --- a/valgrind.supp +++ b/valgrind.supp @@ -3,7 +3,6 @@ Memcheck:Leak match-leak-kinds: possible fun:malloc - fun:_ZN3std2rt19lang_start_internal17h1c66660c99c8424cE - fun:_ZN3std2rt10lang_start17hb778ad044944e8a4E + ... fun:main } From 5d82a0c6f028d1b028319097fbf33261a66fa2dd Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Fri, 13 Dec 2024 13:56:56 +0100 Subject: [PATCH 286/291] Bump tree-sitter-stack-graphs-typescript version --- languages/tree-sitter-stack-graphs-typescript/CHANGELOG.md | 6 ++++++ languages/tree-sitter-stack-graphs-typescript/Cargo.toml | 2 +- languages/tree-sitter-stack-graphs-typescript/README.md | 2 +- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/languages/tree-sitter-stack-graphs-typescript/CHANGELOG.md b/languages/tree-sitter-stack-graphs-typescript/CHANGELOG.md index 47539831f..5a3bfa3d4 100644 --- a/languages/tree-sitter-stack-graphs-typescript/CHANGELOG.md +++ b/languages/tree-sitter-stack-graphs-typescript/CHANGELOG.md @@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## v0.4.0 -- 2024-12-13 + +- The `tree-sitter-stack-graphs` dependency is updated to version 0.10. + +- The `tree-sitter-typescript` dependency is updated to version 0.23.2. + ## v0.3.0 -- 2024-07-09 ### Added diff --git a/languages/tree-sitter-stack-graphs-typescript/Cargo.toml b/languages/tree-sitter-stack-graphs-typescript/Cargo.toml index 379d4d3fe..ed14d9112 100644 --- a/languages/tree-sitter-stack-graphs-typescript/Cargo.toml +++ b/languages/tree-sitter-stack-graphs-typescript/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "tree-sitter-stack-graphs-typescript" -version = "0.3.0" +version = "0.4.0" description = "Stack graphs definition for TypeScript & TSX using tree-sitter-typescript" readme = "README.md" keywords = ["tree-sitter", "stack-graphs", "typescript", "tsx"] diff --git a/languages/tree-sitter-stack-graphs-typescript/README.md b/languages/tree-sitter-stack-graphs-typescript/README.md index 46079a19a..c83d5849d 100644 --- a/languages/tree-sitter-stack-graphs-typescript/README.md +++ b/languages/tree-sitter-stack-graphs-typescript/README.md @@ -13,7 +13,7 @@ To use this library, add the following to your `Cargo.toml`: ```toml [dependencies] -tree-sitter-stack-graphs-typescript = "0.3" +tree-sitter-stack-graphs-typescript = "0.4" ``` Check out our [documentation](https://docs.rs/tree-sitter-stack-graphs-typescript/*/) for more details on how to use this library. From 7fe3e0dc69ec5ab4054b511814e27e77be4c0b5b Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Tue, 4 Mar 2025 13:47:17 +0100 Subject: [PATCH 287/291] Pin thrid party actions to SHA --- .github/workflows/ci.yml | 8 ++++---- .github/workflows/perf.yml | 4 ++-- .github/workflows/publish-lsp-positions.yml | 4 ++-- .github/workflows/publish-stack-graphs.yml | 4 ++-- .../workflows/publish-tree-sitter-stack-graphs-java.yml | 4 ++-- .../publish-tree-sitter-stack-graphs-javascript.yml | 4 ++-- .../workflows/publish-tree-sitter-stack-graphs-python.yml | 4 ++-- .../publish-tree-sitter-stack-graphs-typescript.yml | 4 ++-- .github/workflows/publish-tree-sitter-stack-graphs.yml | 4 ++-- 9 files changed, 20 insertions(+), 20 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index adc70b3c8..89be35c39 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -23,7 +23,7 @@ jobs: steps: - name: Install Rust environment - uses: hecrj/setup-rust-action@v1 + uses: hecrj/setup-rust-action@110f36749599534ca96628b82f52ae67e5d95a3c # v2 with: rust-version: ${{ matrix.rust }} - name: Install cargo-hack @@ -75,7 +75,7 @@ jobs: steps: - name: Install Rust environment - uses: hecrj/setup-rust-action@v1 + uses: hecrj/setup-rust-action@110f36749599534ca96628b82f52ae67e5d95a3c # v2 with: rust-version: ${{ matrix.rust }} - name: Checkout code @@ -116,7 +116,7 @@ jobs: steps: - name: Install Rust environment - uses: hecrj/setup-rust-action@v1 + uses: hecrj/setup-rust-action@110f36749599534ca96628b82f52ae67e5d95a3c # v2 with: rust-version: ${{ matrix.rust }} - name: Install cargo-hack @@ -148,7 +148,7 @@ jobs: steps: - name: Install Rust environment - uses: hecrj/setup-rust-action@v1 + uses: hecrj/setup-rust-action@110f36749599534ca96628b82f52ae67e5d95a3c # v2 with: rust-version: ${{ matrix.rust }} - name: Cache dependencies diff --git a/.github/workflows/perf.yml b/.github/workflows/perf.yml index 7da0f9ef8..1930ff2fb 100644 --- a/.github/workflows/perf.yml +++ b/.github/workflows/perf.yml @@ -79,7 +79,7 @@ jobs: BASE_SHA: ${{ needs.changes.outputs.base-sha }} steps: - name: Install Rust environment - uses: hecrj/setup-rust-action@v1 + uses: hecrj/setup-rust-action@110f36749599534ca96628b82f52ae67e5d95a3c # v2 with: rust-version: stable - name: Cache Rust dependencies @@ -148,7 +148,7 @@ jobs: HEAD_SHA: ${{ needs.changes.outputs.head-sha }} steps: - name: Install Rust environment - uses: hecrj/setup-rust-action@v1 + uses: hecrj/setup-rust-action@110f36749599534ca96628b82f52ae67e5d95a3c # v2 with: rust-version: stable - name: Cache Rust dependencies diff --git a/.github/workflows/publish-lsp-positions.yml b/.github/workflows/publish-lsp-positions.yml index dd021646b..5c48ed71f 100644 --- a/.github/workflows/publish-lsp-positions.yml +++ b/.github/workflows/publish-lsp-positions.yml @@ -14,7 +14,7 @@ jobs: CRATE_DIR: './lsp-positions' steps: - name: Install Rust environment - uses: hecrj/setup-rust-action@v1 + uses: hecrj/setup-rust-action@110f36749599534ca96628b82f52ae67e5d95a3c # v2 - name: Checkout repository uses: actions/checkout@v4 # TODO Verify the crate version matches the tag @@ -38,7 +38,7 @@ jobs: - name: Checkout repository uses: actions/checkout@v4 - name: Create GitHub release - uses: ncipollo/release-action@v1 + uses: ncipollo/release-action@440c8c1cb0ed28b9f43e4d1d670870f059653174 # v1 with: body: | Find more info on all releases at https://crates.io/crates/lsp-positions. diff --git a/.github/workflows/publish-stack-graphs.yml b/.github/workflows/publish-stack-graphs.yml index b94e0337f..cc7b6667b 100644 --- a/.github/workflows/publish-stack-graphs.yml +++ b/.github/workflows/publish-stack-graphs.yml @@ -14,7 +14,7 @@ jobs: CRATE_DIR: './stack-graphs' steps: - name: Install Rust environment - uses: hecrj/setup-rust-action@v1 + uses: hecrj/setup-rust-action@110f36749599534ca96628b82f52ae67e5d95a3c # v2 - name: Checkout repository uses: actions/checkout@v4 # TODO Verify the crate version matches the tag @@ -38,7 +38,7 @@ jobs: - name: Checkout repository uses: actions/checkout@v4 - name: Create GitHub release - uses: ncipollo/release-action@v1 + uses: ncipollo/release-action@440c8c1cb0ed28b9f43e4d1d670870f059653174 # v1 with: body: | Find more info on all releases at https://crates.io/crates/stack-graphs. diff --git a/.github/workflows/publish-tree-sitter-stack-graphs-java.yml b/.github/workflows/publish-tree-sitter-stack-graphs-java.yml index 64e9f3bf2..31196048c 100644 --- a/.github/workflows/publish-tree-sitter-stack-graphs-java.yml +++ b/.github/workflows/publish-tree-sitter-stack-graphs-java.yml @@ -14,7 +14,7 @@ jobs: CRATE_DIR: './languages/tree-sitter-stack-graphs-java' steps: - name: Install Rust environment - uses: hecrj/setup-rust-action@v1 + uses: hecrj/setup-rust-action@110f36749599534ca96628b82f52ae67e5d95a3c # v2 - name: Checkout repository uses: actions/checkout@v4 # TODO Verify the crate version matches the tag @@ -38,7 +38,7 @@ jobs: - name: Checkout repository uses: actions/checkout@v4 - name: Create GitHub release - uses: ncipollo/release-action@v1 + uses: ncipollo/release-action@440c8c1cb0ed28b9f43e4d1d670870f059653174 # v1 with: body: | Find more info on all releases at https://crates.io/crates/tree-sitter-stack-graphs-java. diff --git a/.github/workflows/publish-tree-sitter-stack-graphs-javascript.yml b/.github/workflows/publish-tree-sitter-stack-graphs-javascript.yml index 48375d9d4..f571900b2 100644 --- a/.github/workflows/publish-tree-sitter-stack-graphs-javascript.yml +++ b/.github/workflows/publish-tree-sitter-stack-graphs-javascript.yml @@ -14,7 +14,7 @@ jobs: CRATE_DIR: './languages/tree-sitter-stack-graphs-javascript' steps: - name: Install Rust environment - uses: hecrj/setup-rust-action@v1 + uses: hecrj/setup-rust-action@110f36749599534ca96628b82f52ae67e5d95a3c # v2 - name: Checkout repository uses: actions/checkout@v4 # TODO Verify the crate version matches the tag @@ -38,7 +38,7 @@ jobs: - name: Checkout repository uses: actions/checkout@v4 - name: Create GitHub release - uses: ncipollo/release-action@v1 + uses: ncipollo/release-action@440c8c1cb0ed28b9f43e4d1d670870f059653174 # v1 with: body: | Find more info on all releases at https://crates.io/crates/tree-sitter-stack-graphs-javascript. diff --git a/.github/workflows/publish-tree-sitter-stack-graphs-python.yml b/.github/workflows/publish-tree-sitter-stack-graphs-python.yml index 77804c28e..28d61b11e 100644 --- a/.github/workflows/publish-tree-sitter-stack-graphs-python.yml +++ b/.github/workflows/publish-tree-sitter-stack-graphs-python.yml @@ -14,7 +14,7 @@ jobs: CRATE_DIR: './languages/tree-sitter-stack-graphs-python' steps: - name: Install Rust environment - uses: hecrj/setup-rust-action@v1 + uses: hecrj/setup-rust-action@110f36749599534ca96628b82f52ae67e5d95a3c # v2 - name: Checkout repository uses: actions/checkout@v4 # TODO Verify the crate version matches the tag @@ -38,7 +38,7 @@ jobs: - name: Checkout repository uses: actions/checkout@v4 - name: Create GitHub release - uses: ncipollo/release-action@v1 + uses: ncipollo/release-action@440c8c1cb0ed28b9f43e4d1d670870f059653174 # v1 with: body: | Find more info on all releases at https://crates.io/crates/tree-sitter-stack-graphs-python. diff --git a/.github/workflows/publish-tree-sitter-stack-graphs-typescript.yml b/.github/workflows/publish-tree-sitter-stack-graphs-typescript.yml index 3fdbe17e2..a1f387afd 100644 --- a/.github/workflows/publish-tree-sitter-stack-graphs-typescript.yml +++ b/.github/workflows/publish-tree-sitter-stack-graphs-typescript.yml @@ -14,7 +14,7 @@ jobs: CRATE_DIR: './languages/tree-sitter-stack-graphs-typescript' steps: - name: Install Rust environment - uses: hecrj/setup-rust-action@v1 + uses: hecrj/setup-rust-action@110f36749599534ca96628b82f52ae67e5d95a3c # v2 - name: Checkout repository uses: actions/checkout@v4 # TODO Verify the crate version matches the tag @@ -38,7 +38,7 @@ jobs: - name: Checkout repository uses: actions/checkout@v4 - name: Create GitHub release - uses: ncipollo/release-action@v1 + uses: ncipollo/release-action@440c8c1cb0ed28b9f43e4d1d670870f059653174 # v1 with: body: | Find more info on all releases at https://crates.io/crates/tree-sitter-stack-graphs-typescript. diff --git a/.github/workflows/publish-tree-sitter-stack-graphs.yml b/.github/workflows/publish-tree-sitter-stack-graphs.yml index 2dfb743a1..6e14ab5b1 100644 --- a/.github/workflows/publish-tree-sitter-stack-graphs.yml +++ b/.github/workflows/publish-tree-sitter-stack-graphs.yml @@ -14,7 +14,7 @@ jobs: CRATE_DIR: './tree-sitter-stack-graphs' steps: - name: Install Rust environment - uses: hecrj/setup-rust-action@v1 + uses: hecrj/setup-rust-action@110f36749599534ca96628b82f52ae67e5d95a3c # v2 - name: Checkout repository uses: actions/checkout@v4 # TODO Verify the crate version matches the tag @@ -38,7 +38,7 @@ jobs: - name: Checkout repository uses: actions/checkout@v4 - name: Create GitHub release - uses: ncipollo/release-action@v1 + uses: ncipollo/release-action@440c8c1cb0ed28b9f43e4d1d670870f059653174 # v1 with: body: | Find more info on all releases at https://crates.io/crates/tree-sitter-stack-graphs. From a6b0f7f6af89a3cb8e834866a87c568fcccf02ac Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Tue, 4 Mar 2025 13:52:47 +0100 Subject: [PATCH 288/291] Explicit workflow permissions --- .github/workflows/ci.yml | 4 ++++ .github/workflows/perf.yml | 5 +++++ .github/workflows/publish-lsp-positions.yml | 3 +++ .github/workflows/publish-stack-graphs.yml | 3 +++ .github/workflows/publish-tree-sitter-stack-graphs-java.yml | 3 +++ .../publish-tree-sitter-stack-graphs-javascript.yml | 3 +++ .../workflows/publish-tree-sitter-stack-graphs-python.yml | 3 +++ .../publish-tree-sitter-stack-graphs-typescript.yml | 3 +++ .github/workflows/publish-tree-sitter-stack-graphs.yml | 3 +++ 9 files changed, 30 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 89be35c39..e8bb697e7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,4 +1,5 @@ name: Continuous integration + on: push: branches: [main] @@ -6,6 +7,9 @@ on: schedule: - cron: "0 0 1,15 * *" +permissions: + contents: read + # In the event that there is a new push to the ref, cancel any running jobs because there are now obsolete, and wasting resources. concurrency: group: ${{ github.workflow }}-${{ github.ref }} diff --git a/.github/workflows/perf.yml b/.github/workflows/perf.yml index 1930ff2fb..d68ffa75d 100644 --- a/.github/workflows/perf.yml +++ b/.github/workflows/perf.yml @@ -1,9 +1,14 @@ name: Performance testing + on: pull_request: paths: - 'stack-graphs/**' +permissions: + contents: read + pull-requests: write + # In the event that there is a new push to the ref, cancel any running jobs because there are now obsolete, and wasting resources. concurrency: group: ${{ github.workflow }}-${{ github.ref }} diff --git a/.github/workflows/publish-lsp-positions.yml b/.github/workflows/publish-lsp-positions.yml index 5c48ed71f..2ca05a133 100644 --- a/.github/workflows/publish-lsp-positions.yml +++ b/.github/workflows/publish-lsp-positions.yml @@ -5,6 +5,9 @@ on: tags: - lsp-positions-v* +permissions: + contents: write + jobs: publish-crate: runs-on: ubuntu-latest diff --git a/.github/workflows/publish-stack-graphs.yml b/.github/workflows/publish-stack-graphs.yml index cc7b6667b..29b21020b 100644 --- a/.github/workflows/publish-stack-graphs.yml +++ b/.github/workflows/publish-stack-graphs.yml @@ -5,6 +5,9 @@ on: tags: - stack-graphs-v* +permissions: + contents: write + jobs: publish-crate: runs-on: ubuntu-latest diff --git a/.github/workflows/publish-tree-sitter-stack-graphs-java.yml b/.github/workflows/publish-tree-sitter-stack-graphs-java.yml index 31196048c..40878de95 100644 --- a/.github/workflows/publish-tree-sitter-stack-graphs-java.yml +++ b/.github/workflows/publish-tree-sitter-stack-graphs-java.yml @@ -5,6 +5,9 @@ on: tags: - tree-sitter-stack-graphs-java-v* +permissions: + contents: write + jobs: publish-crate: runs-on: ubuntu-latest diff --git a/.github/workflows/publish-tree-sitter-stack-graphs-javascript.yml b/.github/workflows/publish-tree-sitter-stack-graphs-javascript.yml index f571900b2..b9fbceec3 100644 --- a/.github/workflows/publish-tree-sitter-stack-graphs-javascript.yml +++ b/.github/workflows/publish-tree-sitter-stack-graphs-javascript.yml @@ -5,6 +5,9 @@ on: tags: - tree-sitter-stack-graphs-javascript-v* +permissions: + contents: write + jobs: publish-crate: runs-on: ubuntu-latest diff --git a/.github/workflows/publish-tree-sitter-stack-graphs-python.yml b/.github/workflows/publish-tree-sitter-stack-graphs-python.yml index 28d61b11e..dc90a6974 100644 --- a/.github/workflows/publish-tree-sitter-stack-graphs-python.yml +++ b/.github/workflows/publish-tree-sitter-stack-graphs-python.yml @@ -5,6 +5,9 @@ on: tags: - tree-sitter-stack-graphs-python-v* +permissions: + contents: write + jobs: publish-crate: runs-on: ubuntu-latest diff --git a/.github/workflows/publish-tree-sitter-stack-graphs-typescript.yml b/.github/workflows/publish-tree-sitter-stack-graphs-typescript.yml index a1f387afd..9eb696e2a 100644 --- a/.github/workflows/publish-tree-sitter-stack-graphs-typescript.yml +++ b/.github/workflows/publish-tree-sitter-stack-graphs-typescript.yml @@ -5,6 +5,9 @@ on: tags: - tree-sitter-stack-graphs-typescript-v* +permissions: + contents: write + jobs: publish-crate: runs-on: ubuntu-latest diff --git a/.github/workflows/publish-tree-sitter-stack-graphs.yml b/.github/workflows/publish-tree-sitter-stack-graphs.yml index 6e14ab5b1..0bf56c143 100644 --- a/.github/workflows/publish-tree-sitter-stack-graphs.yml +++ b/.github/workflows/publish-tree-sitter-stack-graphs.yml @@ -5,6 +5,9 @@ on: tags: - tree-sitter-stack-graphs-v* +permissions: + contents: write + jobs: publish-crate: runs-on: ubuntu-latest From 3a596d8354f1c70f7e18415c10d37aa7b95cff82 Mon Sep 17 00:00:00 2001 From: Hendrik van Antwerpen Date: Tue, 4 Mar 2025 18:02:48 +0100 Subject: [PATCH 289/291] Add Blackbird team to codeowners --- .github/CODEOWNERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index c010ba8c1..a4042b8bc 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1 +1 @@ -* @github/semantic-code +* @github/semantic-code @github/blackbird From 4c99b605eea91c28e47e15ce4a0660dc5aa42430 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 11 Jun 2025 21:33:25 +0000 Subject: [PATCH 290/291] Bump brace-expansion Bumps the npm_and_yarn group with 1 update in the /languages/tree-sitter-stack-graphs-typescript/vscode directory: [brace-expansion](https://github.com/juliangruber/brace-expansion). Updates `brace-expansion` from 2.0.1 to 2.0.2 - [Release notes](https://github.com/juliangruber/brace-expansion/releases) - [Commits](https://github.com/juliangruber/brace-expansion/compare/v2.0.1...v2.0.2) --- updated-dependencies: - dependency-name: brace-expansion dependency-version: 2.0.2 dependency-type: indirect dependency-group: npm_and_yarn ... Signed-off-by: dependabot[bot] --- .../vscode/package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/languages/tree-sitter-stack-graphs-typescript/vscode/package-lock.json b/languages/tree-sitter-stack-graphs-typescript/vscode/package-lock.json index 9cc00e7ae..4d95b10b3 100644 --- a/languages/tree-sitter-stack-graphs-typescript/vscode/package-lock.json +++ b/languages/tree-sitter-stack-graphs-typescript/vscode/package-lock.json @@ -38,9 +38,9 @@ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, "node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", "dependencies": { "balanced-match": "^1.0.0" } From a008b75469f70769eeded8771217bd135fabb54c Mon Sep 17 00:00:00 2001 From: Nathan Stocks Date: Tue, 9 Sep 2025 15:49:58 -0600 Subject: [PATCH 291/291] This repository is no longer being maintained --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index df51b81c6..6ae144656 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,5 @@ +**NOTE:** _This repository is no longer supported or updated by GitHub. If you wish to continue to develop this code yourself, we recommend you fork it._ + # Stack graphs The crates in this repository provide a Rust implementation of _stack graphs_,