From 7b0c3c9c420076aaba7ff8d29d037d890d46ae1f Mon Sep 17 00:00:00 2001 From: Napalys Date: Thu, 27 Feb 2025 08:24:25 +0100 Subject: [PATCH 1/9] Added intersection test for parsing. --- .../tests/es2024/input/intersection.js | 1 + .../extractor/tests/es2024/options.json | 3 + .../es2024/output/trap/intersection.js.trap | 125 ++++++++++++++++++ 3 files changed, 129 insertions(+) create mode 100644 javascript/extractor/tests/es2024/input/intersection.js create mode 100644 javascript/extractor/tests/es2024/options.json create mode 100644 javascript/extractor/tests/es2024/output/trap/intersection.js.trap diff --git a/javascript/extractor/tests/es2024/input/intersection.js b/javascript/extractor/tests/es2024/input/intersection.js new file mode 100644 index 000000000000..bb990b8fe8c4 --- /dev/null +++ b/javascript/extractor/tests/es2024/input/intersection.js @@ -0,0 +1 @@ +/[[abc]&&[bcd]]/v diff --git a/javascript/extractor/tests/es2024/options.json b/javascript/extractor/tests/es2024/options.json new file mode 100644 index 000000000000..075583ca1f63 --- /dev/null +++ b/javascript/extractor/tests/es2024/options.json @@ -0,0 +1,3 @@ +{ + "experimental": true +} diff --git a/javascript/extractor/tests/es2024/output/trap/intersection.js.trap b/javascript/extractor/tests/es2024/output/trap/intersection.js.trap new file mode 100644 index 000000000000..fdf72bd25a7a --- /dev/null +++ b/javascript/extractor/tests/es2024/output/trap/intersection.js.trap @@ -0,0 +1,125 @@ +#10000=@"/intersection.js;sourcefile" +files(#10000,"/intersection.js") +#10001=@"/;folder" +folders(#10001,"/") +containerparent(#10001,#10000) +#10002=@"loc,{#10000},0,0,0,0" +locations_default(#10002,#10000,0,0,0,0) +hasLocation(#10000,#10002) +#20000=@"global_scope" +scopes(#20000,0) +#20001=@"script;{#10000},1,1" +#20002=* +lines(#20002,#20001,"/[[abc]&&[bcd]]/v"," +") +#20003=@"loc,{#10000},1,1,1,17" +locations_default(#20003,#10000,1,1,1,17) +hasLocation(#20002,#20003) +numlines(#20001,1,1,0) +#20004=* +tokeninfo(#20004,5,#20001,0,"/[[abc]&&[bcd]]/v") +hasLocation(#20004,#20003) +#20005=* +tokeninfo(#20005,0,#20001,1,"") +#20006=@"loc,{#10000},2,1,2,0" +locations_default(#20006,#10000,2,1,2,0) +hasLocation(#20005,#20006) +toplevels(#20001,0) +#20007=@"loc,{#10000},1,1,2,0" +locations_default(#20007,#10000,1,1,2,0) +hasLocation(#20001,#20007) +#20008=* +stmts(#20008,2,#20001,0,"/[[abc]&&[bcd]]/v") +hasLocation(#20008,#20003) +stmt_containers(#20008,#20001) +#20009=* +exprs(#20009,5,#20008,0,"/[[abc]&&[bcd]]/v") +hasLocation(#20009,#20003) +enclosing_stmt(#20009,#20008) +expr_containers(#20009,#20001) +literals("/[[abc]&&[bcd]]/v","/[[abc]&&[bcd]]/v",#20009) +#20010=* +regexpterm(#20010,1,#20009,0,"[[abc]&&[bcd]]") +#20011=@"loc,{#10000},1,2,1,15" +locations_default(#20011,#10000,1,2,1,15) +hasLocation(#20010,#20011) +#20012=* +regexpterm(#20012,23,#20010,0,"[[abc]") +#20013=@"loc,{#10000},1,2,1,7" +locations_default(#20013,#10000,1,2,1,7) +hasLocation(#20012,#20013) +#20014=* +regexpterm(#20014,14,#20012,0,"[") +#20015=@"loc,{#10000},1,3,1,3" +locations_default(#20015,#10000,1,3,1,3) +hasLocation(#20014,#20015) +regexp_const_value(#20014,"[") +#20016=* +regexpterm(#20016,14,#20012,1,"a") +#20017=@"loc,{#10000},1,4,1,4" +locations_default(#20017,#10000,1,4,1,4) +hasLocation(#20016,#20017) +regexp_const_value(#20016,"a") +#20018=* +regexpterm(#20018,14,#20012,2,"b") +#20019=@"loc,{#10000},1,5,1,5" +locations_default(#20019,#10000,1,5,1,5) +hasLocation(#20018,#20019) +regexp_const_value(#20018,"b") +#20020=* +regexpterm(#20020,14,#20012,3,"c") +#20021=@"loc,{#10000},1,6,1,6" +locations_default(#20021,#10000,1,6,1,6) +hasLocation(#20020,#20021) +regexp_const_value(#20020,"c") +#20022=* +regexpterm(#20022,14,#20010,1,"&&") +#20023=@"loc,{#10000},1,8,1,9" +locations_default(#20023,#10000,1,8,1,9) +hasLocation(#20022,#20023) +regexp_const_value(#20022,"&&") +#20024=* +regexpterm(#20024,23,#20010,2,"[bcd]") +#20025=@"loc,{#10000},1,10,1,14" +locations_default(#20025,#10000,1,10,1,14) +hasLocation(#20024,#20025) +#20026=* +regexpterm(#20026,14,#20024,0,"b") +#20027=@"loc,{#10000},1,11,1,11" +locations_default(#20027,#10000,1,11,1,11) +hasLocation(#20026,#20027) +regexp_const_value(#20026,"b") +#20028=* +regexpterm(#20028,14,#20024,1,"c") +#20029=@"loc,{#10000},1,12,1,12" +locations_default(#20029,#10000,1,12,1,12) +hasLocation(#20028,#20029) +regexp_const_value(#20028,"c") +#20030=* +regexpterm(#20030,14,#20024,2,"d") +#20031=@"loc,{#10000},1,13,1,13" +locations_default(#20031,#10000,1,13,1,13) +hasLocation(#20030,#20031) +regexp_const_value(#20030,"d") +#20032=* +regexpterm(#20032,14,#20010,3,"]") +#20033=@"loc,{#10000},1,15,1,15" +locations_default(#20033,#10000,1,15,1,15) +hasLocation(#20032,#20033) +regexp_const_value(#20032,"]") +#20034=* +regexp_parse_errors(#20034,#20010,"unexpected character") +hasLocation(#20034,#20033) +#20035=* +entry_cfg_node(#20035,#20001) +#20036=@"loc,{#10000},1,1,1,0" +locations_default(#20036,#10000,1,1,1,0) +hasLocation(#20035,#20036) +#20037=* +exit_cfg_node(#20037,#20001) +hasLocation(#20037,#20006) +successor(#20008,#20009) +successor(#20009,#20037) +successor(#20035,#20008) +numlines(#10000,1,1,0) +filetype(#10000,"javascript") From c381d3c312d2219429e9c9363b9076dc31f8293e Mon Sep 17 00:00:00 2001 From: Napalys Date: Wed, 4 Dec 2024 08:48:56 +0100 Subject: [PATCH 2/9] JS: Added parser intersection support --- .../semmle/js/ast/regexp/Intersection.java | 22 +++++++++++++++++++ .../src/com/semmle/js/ast/regexp/Visitor.java | 2 ++ .../semmle/js/extractor/RegExpExtractor.java | 10 +++++++++ .../com/semmle/js/parser/RegExpParser.java | 22 +++++++++++++++---- .../es2024/output/trap/intersection.js.trap | 13 +++++------ .../ql/lib/semmlecode.javascript.dbscheme | 3 ++- 6 files changed, 60 insertions(+), 12 deletions(-) create mode 100644 javascript/extractor/src/com/semmle/js/ast/regexp/Intersection.java diff --git a/javascript/extractor/src/com/semmle/js/ast/regexp/Intersection.java b/javascript/extractor/src/com/semmle/js/ast/regexp/Intersection.java new file mode 100644 index 000000000000..d084693bf9a1 --- /dev/null +++ b/javascript/extractor/src/com/semmle/js/ast/regexp/Intersection.java @@ -0,0 +1,22 @@ +package com.semmle.js.ast.regexp; + +import com.semmle.js.ast.SourceLocation; +import java.util.List; + +public class Intersection extends RegExpTerm { + private final List intersections; + + public Intersection(SourceLocation loc, List intersections) { + super(loc, "Intersection"); + this.intersections = intersections; + } + + @Override + public void accept(Visitor v) { + v.visit(this); + } + + public List getIntersections() { + return intersections; + } +} diff --git a/javascript/extractor/src/com/semmle/js/ast/regexp/Visitor.java b/javascript/extractor/src/com/semmle/js/ast/regexp/Visitor.java index 3671a55694be..95a0000fe7f9 100644 --- a/javascript/extractor/src/com/semmle/js/ast/regexp/Visitor.java +++ b/javascript/extractor/src/com/semmle/js/ast/regexp/Visitor.java @@ -61,4 +61,6 @@ public interface Visitor { public void visit(ZeroWidthNegativeLookbehind nd); public void visit(UnicodePropertyEscape nd); + + public void visit(Intersection nd); } diff --git a/javascript/extractor/src/com/semmle/js/extractor/RegExpExtractor.java b/javascript/extractor/src/com/semmle/js/extractor/RegExpExtractor.java index 41d7d446cfe3..f192b75e2497 100644 --- a/javascript/extractor/src/com/semmle/js/extractor/RegExpExtractor.java +++ b/javascript/extractor/src/com/semmle/js/extractor/RegExpExtractor.java @@ -22,6 +22,7 @@ import com.semmle.js.ast.regexp.Group; import com.semmle.js.ast.regexp.HexEscapeSequence; import com.semmle.js.ast.regexp.IdentityEscape; +import com.semmle.js.ast.regexp.Intersection; import com.semmle.js.ast.regexp.Literal; import com.semmle.js.ast.regexp.NamedBackReference; import com.semmle.js.ast.regexp.NonWordBoundary; @@ -92,6 +93,7 @@ public RegExpExtractor(TrapWriter trapwriter, LocationManager locationManager) { termkinds.put("ZeroWidthPositiveLookbehind", 25); termkinds.put("ZeroWidthNegativeLookbehind", 26); termkinds.put("UnicodePropertyEscape", 27); + termkinds.put("Intersection", 28); } private static final String[] errmsgs = @@ -344,6 +346,14 @@ public void visit(CharacterClassRange nd) { visit(nd.getLeft(), lbl, 0); visit(nd.getRight(), lbl, 1); } + + @Override + public void visit(Intersection nd) { + Label lbl = extractTerm(nd, parent, idx); + int i = 0; + for (RegExpTerm element : nd.getIntersections()) + visit(element, lbl, i++); + } } public void extract(String src, SourceMap sourceMap, Node parent, boolean isSpeculativeParsing) { diff --git a/javascript/extractor/src/com/semmle/js/parser/RegExpParser.java b/javascript/extractor/src/com/semmle/js/parser/RegExpParser.java index 5b2177f3fffa..164e64321a0b 100644 --- a/javascript/extractor/src/com/semmle/js/parser/RegExpParser.java +++ b/javascript/extractor/src/com/semmle/js/parser/RegExpParser.java @@ -18,6 +18,7 @@ import com.semmle.js.ast.regexp.Group; import com.semmle.js.ast.regexp.HexEscapeSequence; import com.semmle.js.ast.regexp.IdentityEscape; +import com.semmle.js.ast.regexp.Intersection; import com.semmle.js.ast.regexp.NamedBackReference; import com.semmle.js.ast.regexp.NonWordBoundary; import com.semmle.js.ast.regexp.OctalEscape; @@ -225,20 +226,33 @@ private T finishTerm(T term) { private RegExpTerm parseDisjunction() { SourceLocation loc = new SourceLocation(pos()); List disjuncts = new ArrayList<>(); - disjuncts.add(this.parseAlternative()); - while (this.match("|")) disjuncts.add(this.parseAlternative()); + disjuncts.add(this.parseIntersection()); + while (this.match("|")) { + disjuncts.add(this.parseIntersection()); + } if (disjuncts.size() == 1) return disjuncts.get(0); return this.finishTerm(new Disjunction(loc, disjuncts)); - } +} private RegExpTerm parseAlternative() { SourceLocation loc = new SourceLocation(pos()); List elements = new ArrayList<>(); - while (!this.lookahead(null, "|", ")")) elements.add(this.parseTerm()); + while (!this.lookahead(null, "|", "&&", ")")) elements.add(this.parseTerm()); if (elements.size() == 1) return elements.get(0); return this.finishTerm(new Sequence(loc, elements)); } + private RegExpTerm parseIntersection() { + SourceLocation loc = new SourceLocation(pos()); + List intersections = new ArrayList<>(); + intersections.add(this.parseAlternative()); + while (this.match("&&")) { + intersections.add(this.parseAlternative()); + } + if (intersections.size() == 1) return intersections.get(0); + return this.finishTerm(new Intersection(loc, intersections)); +} + private RegExpTerm parseTerm() { SourceLocation loc = new SourceLocation(pos()); diff --git a/javascript/extractor/tests/es2024/output/trap/intersection.js.trap b/javascript/extractor/tests/es2024/output/trap/intersection.js.trap index fdf72bd25a7a..2d55ec1287c5 100644 --- a/javascript/extractor/tests/es2024/output/trap/intersection.js.trap +++ b/javascript/extractor/tests/es2024/output/trap/intersection.js.trap @@ -39,7 +39,7 @@ enclosing_stmt(#20009,#20008) expr_containers(#20009,#20001) literals("/[[abc]&&[bcd]]/v","/[[abc]&&[bcd]]/v",#20009) #20010=* -regexpterm(#20010,1,#20009,0,"[[abc]&&[bcd]]") +regexpterm(#20010,28,#20009,0,"[[abc]&&[bcd]]") #20011=@"loc,{#10000},1,2,1,15" locations_default(#20011,#10000,1,2,1,15) hasLocation(#20010,#20011) @@ -73,13 +73,12 @@ locations_default(#20021,#10000,1,6,1,6) hasLocation(#20020,#20021) regexp_const_value(#20020,"c") #20022=* -regexpterm(#20022,14,#20010,1,"&&") -#20023=@"loc,{#10000},1,8,1,9" -locations_default(#20023,#10000,1,8,1,9) +regexpterm(#20022,1,#20010,1,"[bcd]]") +#20023=@"loc,{#10000},1,10,1,15" +locations_default(#20023,#10000,1,10,1,15) hasLocation(#20022,#20023) -regexp_const_value(#20022,"&&") #20024=* -regexpterm(#20024,23,#20010,2,"[bcd]") +regexpterm(#20024,23,#20022,0,"[bcd]") #20025=@"loc,{#10000},1,10,1,14" locations_default(#20025,#10000,1,10,1,14) hasLocation(#20024,#20025) @@ -102,7 +101,7 @@ locations_default(#20031,#10000,1,13,1,13) hasLocation(#20030,#20031) regexp_const_value(#20030,"d") #20032=* -regexpterm(#20032,14,#20010,3,"]") +regexpterm(#20032,14,#20022,1,"]") #20033=@"loc,{#10000},1,15,1,15" locations_default(#20033,#10000,1,15,1,15) hasLocation(#20032,#20033) diff --git a/javascript/ql/lib/semmlecode.javascript.dbscheme b/javascript/ql/lib/semmlecode.javascript.dbscheme index c88c69174bd0..eda7d3dc42ba 100644 --- a/javascript/ql/lib/semmlecode.javascript.dbscheme +++ b/javascript/ql/lib/semmlecode.javascript.dbscheme @@ -859,7 +859,8 @@ case @regexpterm.kind of | 24 = @regexp_char_range | 25 = @regexp_positive_lookbehind | 26 = @regexp_negative_lookbehind -| 27 = @regexp_unicode_property_escape; +| 27 = @regexp_unicode_property_escape +| 28 = @regexp_intersection; regexp_parse_errors (unique int id: @regexp_parse_error, int regexp: @regexpterm ref, From 181e8456c2eef64688e043da1a3d1112c45dda1e Mon Sep 17 00:00:00 2001 From: Napalys Date: Thu, 27 Feb 2025 09:44:04 +0100 Subject: [PATCH 3/9] Upgrade DB scheme. --- .../old.dbscheme | 1191 +++++++++++++++++ .../semmlecode.javascript.dbscheme | 1190 ++++++++++++++++ .../upgrade.properties | 2 + .../old.dbscheme | 1190 ++++++++++++++++ .../semmlecode.javascript.dbscheme | 1191 +++++++++++++++++ .../upgrade.properties | 2 + 6 files changed, 4766 insertions(+) create mode 100644 javascript/downgrades/eda7d3dc42baf69d0acb592072d7136acf61d903/old.dbscheme create mode 100644 javascript/downgrades/eda7d3dc42baf69d0acb592072d7136acf61d903/semmlecode.javascript.dbscheme create mode 100644 javascript/downgrades/eda7d3dc42baf69d0acb592072d7136acf61d903/upgrade.properties create mode 100644 javascript/ql/lib/upgrades/c88c69174bd0dd4e95f1bcfbada68a2505e812c3/old.dbscheme create mode 100644 javascript/ql/lib/upgrades/c88c69174bd0dd4e95f1bcfbada68a2505e812c3/semmlecode.javascript.dbscheme create mode 100644 javascript/ql/lib/upgrades/c88c69174bd0dd4e95f1bcfbada68a2505e812c3/upgrade.properties diff --git a/javascript/downgrades/eda7d3dc42baf69d0acb592072d7136acf61d903/old.dbscheme b/javascript/downgrades/eda7d3dc42baf69d0acb592072d7136acf61d903/old.dbscheme new file mode 100644 index 000000000000..eda7d3dc42ba --- /dev/null +++ b/javascript/downgrades/eda7d3dc42baf69d0acb592072d7136acf61d903/old.dbscheme @@ -0,0 +1,1191 @@ +/*** Standard fragments ***/ + +/*- Files and folders -*/ + +/** + * The location of an element. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `file`. + * For more information, see + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). + */ +locations_default( + unique int id: @location_default, + int file: @file ref, + int beginLine: int ref, + int beginColumn: int ref, + int endLine: int ref, + int endColumn: int ref +); + +files( + unique int id: @file, + string name: string ref +); + +folders( + unique int id: @folder, + string name: string ref +); + +@container = @file | @folder + +containerparent( + int parent: @container ref, + unique int child: @container ref +); + +/*- Lines of code -*/ + +numlines( + int element_id: @sourceline ref, + int num_lines: int ref, + int num_code: int ref, + int num_comment: int ref +); + +/*- External data -*/ + +/** + * External data, loaded from CSV files during snapshot creation. See + * [Tutorial: Incorporating external data](https://help.semmle.com/wiki/display/SD/Tutorial%3A+Incorporating+external+data) + * for more information. + */ +externalData( + int id : @externalDataElement, + string path : string ref, + int column: int ref, + string value : string ref +); + +/*- Source location prefix -*/ + +/** + * The source location of the snapshot. + */ +sourceLocationPrefix(string prefix : string ref); + +/*- JavaScript-specific part -*/ + +@location = @location_default + +@sourceline = @locatable; + +filetype( + int file: @file ref, + string filetype: string ref +) + +// top-level code fragments +toplevels (unique int id: @toplevel, + int kind: int ref); + +is_externs (int toplevel: @toplevel ref); + +case @toplevel.kind of + 0 = @script +| 1 = @inline_script +| 2 = @event_handler +| 3 = @javascript_url +| 4 = @template_toplevel; + +is_module (int tl: @toplevel ref); +is_nodejs (int tl: @toplevel ref); +is_es2015_module (int tl: @toplevel ref); +is_closure_module (int tl: @toplevel ref); + +@xml_node_with_code = @xmlelement | @xmlattribute | @template_placeholder_tag; +toplevel_parent_xml_node( + unique int toplevel: @toplevel ref, + int xmlnode: @xml_node_with_code ref); + +xml_element_parent_expression( + unique int xmlnode: @xmlelement ref, + int expression: @expr ref, + int index: int ref); + +// statements +#keyset[parent, idx] +stmts (unique int id: @stmt, + int kind: int ref, + int parent: @stmt_parent ref, + int idx: int ref, + varchar(900) tostring: string ref); + +stmt_containers (unique int stmt: @stmt ref, + int container: @stmt_container ref); + +jump_targets (unique int jump: @stmt ref, + int target: @stmt ref); + +@stmt_parent = @stmt | @toplevel | @function_expr | @arrow_function_expr | @static_initializer; +@stmt_container = @toplevel | @function | @namespace_declaration | @external_module_declaration | @global_augmentation_declaration; + +case @stmt.kind of + 0 = @empty_stmt +| 1 = @block_stmt +| 2 = @expr_stmt +| 3 = @if_stmt +| 4 = @labeled_stmt +| 5 = @break_stmt +| 6 = @continue_stmt +| 7 = @with_stmt +| 8 = @switch_stmt +| 9 = @return_stmt +| 10 = @throw_stmt +| 11 = @try_stmt +| 12 = @while_stmt +| 13 = @do_while_stmt +| 14 = @for_stmt +| 15 = @for_in_stmt +| 16 = @debugger_stmt +| 17 = @function_decl_stmt +| 18 = @var_decl_stmt +| 19 = @case +| 20 = @catch_clause +| 21 = @for_of_stmt +| 22 = @const_decl_stmt +| 23 = @let_stmt +| 24 = @legacy_let_stmt +| 25 = @for_each_stmt +| 26 = @class_decl_stmt +| 27 = @import_declaration +| 28 = @export_all_declaration +| 29 = @export_default_declaration +| 30 = @export_named_declaration +| 31 = @namespace_declaration +| 32 = @import_equals_declaration +| 33 = @export_assign_declaration +| 34 = @interface_declaration +| 35 = @type_alias_declaration +| 36 = @enum_declaration +| 37 = @external_module_declaration +| 38 = @export_as_namespace_declaration +| 39 = @global_augmentation_declaration +| 40 = @using_decl_stmt +; + +@decl_stmt = @var_decl_stmt | @const_decl_stmt | @let_stmt | @legacy_let_stmt | @using_decl_stmt; + +@export_declaration = @export_all_declaration | @export_default_declaration | @export_named_declaration; + +@namespace_definition = @namespace_declaration | @enum_declaration; +@type_definition = @class_definition | @interface_declaration | @enum_declaration | @type_alias_declaration | @enum_member; + +is_instantiated(unique int decl: @namespace_declaration ref); + +@declarable_node = @decl_stmt | @namespace_declaration | @class_decl_stmt | @function_decl_stmt | @enum_declaration | @external_module_declaration | @global_augmentation_declaration | @field; +has_declare_keyword(unique int stmt: @declarable_node ref); + +is_for_await_of(unique int forof: @for_of_stmt ref); + +// expressions +#keyset[parent, idx] +exprs (unique int id: @expr, + int kind: int ref, + int parent: @expr_parent ref, + int idx: int ref, + varchar(900) tostring: string ref); + +literals (varchar(900) value: string ref, + varchar(900) raw: string ref, + unique int expr: @expr_or_type ref); + +enclosing_stmt (unique int expr: @expr_or_type ref, + int stmt: @stmt ref); + +expr_containers (unique int expr: @expr_or_type ref, + int container: @stmt_container ref); + +array_size (unique int ae: @arraylike ref, + int sz: int ref); + +is_delegating (int yield: @yield_expr ref); + +@expr_or_stmt = @expr | @stmt; +@expr_or_type = @expr | @typeexpr; +@expr_parent = @expr_or_stmt | @property | @function_typeexpr; +@arraylike = @array_expr | @array_pattern; +@type_annotation = @typeexpr | @jsdoc_type_expr; +@node_in_stmt_container = @cfg_node | @type_annotation | @toplevel; + +case @expr.kind of + 0 = @label +| 1 = @null_literal +| 2 = @boolean_literal +| 3 = @number_literal +| 4 = @string_literal +| 5 = @regexp_literal +| 6 = @this_expr +| 7 = @array_expr +| 8 = @obj_expr +| 9 = @function_expr +| 10 = @seq_expr +| 11 = @conditional_expr +| 12 = @new_expr +| 13 = @call_expr +| 14 = @dot_expr +| 15 = @index_expr +| 16 = @neg_expr +| 17 = @plus_expr +| 18 = @log_not_expr +| 19 = @bit_not_expr +| 20 = @typeof_expr +| 21 = @void_expr +| 22 = @delete_expr +| 23 = @eq_expr +| 24 = @neq_expr +| 25 = @eqq_expr +| 26 = @neqq_expr +| 27 = @lt_expr +| 28 = @le_expr +| 29 = @gt_expr +| 30 = @ge_expr +| 31 = @lshift_expr +| 32 = @rshift_expr +| 33 = @urshift_expr +| 34 = @add_expr +| 35 = @sub_expr +| 36 = @mul_expr +| 37 = @div_expr +| 38 = @mod_expr +| 39 = @bitor_expr +| 40 = @xor_expr +| 41 = @bitand_expr +| 42 = @in_expr +| 43 = @instanceof_expr +| 44 = @logand_expr +| 45 = @logor_expr +| 47 = @assign_expr +| 48 = @assign_add_expr +| 49 = @assign_sub_expr +| 50 = @assign_mul_expr +| 51 = @assign_div_expr +| 52 = @assign_mod_expr +| 53 = @assign_lshift_expr +| 54 = @assign_rshift_expr +| 55 = @assign_urshift_expr +| 56 = @assign_or_expr +| 57 = @assign_xor_expr +| 58 = @assign_and_expr +| 59 = @preinc_expr +| 60 = @postinc_expr +| 61 = @predec_expr +| 62 = @postdec_expr +| 63 = @par_expr +| 64 = @var_declarator +| 65 = @arrow_function_expr +| 66 = @spread_element +| 67 = @array_pattern +| 68 = @object_pattern +| 69 = @yield_expr +| 70 = @tagged_template_expr +| 71 = @template_literal +| 72 = @template_element +| 73 = @array_comprehension_expr +| 74 = @generator_expr +| 75 = @for_in_comprehension_block +| 76 = @for_of_comprehension_block +| 77 = @legacy_letexpr +| 78 = @var_decl +| 79 = @proper_varaccess +| 80 = @class_expr +| 81 = @super_expr +| 82 = @newtarget_expr +| 83 = @named_import_specifier +| 84 = @import_default_specifier +| 85 = @import_namespace_specifier +| 86 = @named_export_specifier +| 87 = @exp_expr +| 88 = @assign_exp_expr +| 89 = @jsx_element +| 90 = @jsx_qualified_name +| 91 = @jsx_empty_expr +| 92 = @await_expr +| 93 = @function_sent_expr +| 94 = @decorator +| 95 = @export_default_specifier +| 96 = @export_namespace_specifier +| 97 = @bind_expr +| 98 = @external_module_reference +| 99 = @dynamic_import +| 100 = @expression_with_type_arguments +| 101 = @prefix_type_assertion +| 102 = @as_type_assertion +| 103 = @export_varaccess +| 104 = @decorator_list +| 105 = @non_null_assertion +| 106 = @bigint_literal +| 107 = @nullishcoalescing_expr +| 108 = @e4x_xml_anyname +| 109 = @e4x_xml_static_attribute_selector +| 110 = @e4x_xml_dynamic_attribute_selector +| 111 = @e4x_xml_filter_expression +| 112 = @e4x_xml_static_qualident +| 113 = @e4x_xml_dynamic_qualident +| 114 = @e4x_xml_dotdotexpr +| 115 = @import_meta_expr +| 116 = @assignlogandexpr +| 117 = @assignlogorexpr +| 118 = @assignnullishcoalescingexpr +| 119 = @template_pipe_ref +| 120 = @generated_code_expr +| 121 = @satisfies_expr +; + +@varaccess = @proper_varaccess | @export_varaccess; +@varref = @var_decl | @varaccess; + +@identifier = @label | @varref | @type_identifier; + +@literal = @null_literal | @boolean_literal | @number_literal | @string_literal | @regexp_literal | @bigint_literal; + +@propaccess = @dot_expr | @index_expr; + +@invokeexpr = @new_expr | @call_expr; + +@unaryexpr = @neg_expr | @plus_expr | @log_not_expr | @bit_not_expr | @typeof_expr | @void_expr | @delete_expr | @spread_element; + +@equality_test = @eq_expr | @neq_expr | @eqq_expr | @neqq_expr; + +@comparison = @equality_test | @lt_expr | @le_expr | @gt_expr | @ge_expr; + +@binaryexpr = @comparison | @lshift_expr | @rshift_expr | @urshift_expr | @add_expr | @sub_expr | @mul_expr | @div_expr | @mod_expr | @exp_expr | @bitor_expr | @xor_expr | @bitand_expr | @in_expr | @instanceof_expr | @logand_expr | @logor_expr | @nullishcoalescing_expr; + +@assignment = @assign_expr | @assign_add_expr | @assign_sub_expr | @assign_mul_expr | @assign_div_expr | @assign_mod_expr | @assign_exp_expr | @assign_lshift_expr | @assign_rshift_expr | @assign_urshift_expr | @assign_or_expr | @assign_xor_expr | @assign_and_expr | @assignlogandexpr | @assignlogorexpr | @assignnullishcoalescingexpr; + +@updateexpr = @preinc_expr | @postinc_expr | @predec_expr | @postdec_expr; + +@pattern = @varref | @array_pattern | @object_pattern; + +@comprehension_expr = @array_comprehension_expr | @generator_expr; + +@comprehension_block = @for_in_comprehension_block | @for_of_comprehension_block; + +@import_specifier = @named_import_specifier | @import_default_specifier | @import_namespace_specifier; + +@exportspecifier = @named_export_specifier | @export_default_specifier | @export_namespace_specifier; + +@type_keyword_operand = @import_declaration | @export_declaration | @import_specifier; + +@type_assertion = @as_type_assertion | @prefix_type_assertion; + +@class_definition = @class_decl_stmt | @class_expr; +@interface_definition = @interface_declaration | @interface_typeexpr; +@class_or_interface = @class_definition | @interface_definition; + +@lexical_decl = @var_decl | @type_decl; +@lexical_access = @varaccess | @local_type_access | @local_var_type_access | @local_namespace_access; +@lexical_ref = @lexical_decl | @lexical_access; + +@e4x_xml_attribute_selector = @e4x_xml_static_attribute_selector | @e4x_xml_dynamic_attribute_selector; +@e4x_xml_qualident = @e4x_xml_static_qualident | @e4x_xml_dynamic_qualident; + +expr_contains_template_tag_location( + int expr: @expr ref, + int location: @location ref +); + +@template_placeholder_tag_parent = @xmlelement | @xmlattribute | @file; + +template_placeholder_tag_info( + unique int node: @template_placeholder_tag, + int parentNode: @template_placeholder_tag_parent ref, + varchar(900) raw: string ref +); + +// scopes +scopes (unique int id: @scope, + int kind: int ref); + +case @scope.kind of + 0 = @global_scope +| 1 = @function_scope +| 2 = @catch_scope +| 3 = @module_scope +| 4 = @block_scope +| 5 = @for_scope +| 6 = @for_in_scope // for-of scopes work the same as for-in scopes +| 7 = @comprehension_block_scope +| 8 = @class_expr_scope +| 9 = @namespace_scope +| 10 = @class_decl_scope +| 11 = @interface_scope +| 12 = @type_alias_scope +| 13 = @mapped_type_scope +| 14 = @enum_scope +| 15 = @external_module_scope +| 16 = @conditional_type_scope; + +scopenodes (unique int node: @ast_node ref, + int scope: @scope ref); + +scopenesting (unique int inner: @scope ref, + int outer: @scope ref); + +// functions +@function = @function_decl_stmt | @function_expr | @arrow_function_expr; + +@parameterized = @function | @catch_clause; +@type_parameterized = @function | @class_or_interface | @type_alias_declaration | @mapped_typeexpr | @infer_typeexpr; + +is_generator (int fun: @function ref); +has_rest_parameter (int fun: @function ref); +is_async (int fun: @function ref); + +// variables and lexically scoped type names +#keyset[scope, name] +variables (unique int id: @variable, + varchar(900) name: string ref, + int scope: @scope ref); + +#keyset[scope, name] +local_type_names (unique int id: @local_type_name, + varchar(900) name: string ref, + int scope: @scope ref); + +#keyset[scope, name] +local_namespace_names (unique int id: @local_namespace_name, + varchar(900) name: string ref, + int scope: @scope ref); + +is_arguments_object (int id: @variable ref); + +@lexical_name = @variable | @local_type_name | @local_namespace_name; + +@bind_id = @varaccess | @local_var_type_access; +bind (unique int id: @bind_id ref, + int decl: @variable ref); + +decl (unique int id: @var_decl ref, + int decl: @variable ref); + +@typebind_id = @local_type_access | @export_varaccess; +typebind (unique int id: @typebind_id ref, + int decl: @local_type_name ref); + +@typedecl_id = @type_decl | @var_decl; +typedecl (unique int id: @typedecl_id ref, + int decl: @local_type_name ref); + +namespacedecl (unique int id: @var_decl ref, + int decl: @local_namespace_name ref); + +@namespacebind_id = @local_namespace_access | @export_varaccess; +namespacebind (unique int id: @namespacebind_id ref, + int decl: @local_namespace_name ref); + + +// properties in object literals, property patterns in object patterns, and method declarations in classes +#keyset[parent, index] +properties (unique int id: @property, + int parent: @property_parent ref, + int index: int ref, + int kind: int ref, + varchar(900) tostring: string ref); + +case @property.kind of + 0 = @value_property +| 1 = @property_getter +| 2 = @property_setter +| 3 = @jsx_attribute +| 4 = @function_call_signature +| 5 = @constructor_call_signature +| 6 = @index_signature +| 7 = @enum_member +| 8 = @proper_field +| 9 = @parameter_field +| 10 = @static_initializer +; + +@property_parent = @obj_expr | @object_pattern | @class_definition | @jsx_element | @interface_definition | @enum_declaration; +@property_accessor = @property_getter | @property_setter; +@call_signature = @function_call_signature | @constructor_call_signature; +@field = @proper_field | @parameter_field; +@field_or_vardeclarator = @field | @var_declarator; + +is_computed (int id: @property ref); +is_method (int id: @property ref); +is_static (int id: @property ref); +is_abstract_member (int id: @property ref); +is_const_enum (int id: @enum_declaration ref); +is_abstract_class (int id: @class_decl_stmt ref); + +has_public_keyword (int id: @property ref); +has_private_keyword (int id: @property ref); +has_protected_keyword (int id: @property ref); +has_readonly_keyword (int id: @property ref); +has_type_keyword (int id: @type_keyword_operand ref); +is_optional_member (int id: @property ref); +has_definite_assignment_assertion (int id: @field_or_vardeclarator ref); +is_optional_parameter_declaration (unique int parameter: @pattern ref); + +#keyset[constructor, param_index] +parameter_fields( + unique int field: @parameter_field ref, + int constructor: @function_expr ref, + int param_index: int ref +); + +// types +#keyset[parent, idx] +typeexprs ( + unique int id: @typeexpr, + int kind: int ref, + int parent: @typeexpr_parent ref, + int idx: int ref, + varchar(900) tostring: string ref +); + +case @typeexpr.kind of + 0 = @local_type_access +| 1 = @type_decl +| 2 = @keyword_typeexpr +| 3 = @string_literal_typeexpr +| 4 = @number_literal_typeexpr +| 5 = @boolean_literal_typeexpr +| 6 = @array_typeexpr +| 7 = @union_typeexpr +| 8 = @indexed_access_typeexpr +| 9 = @intersection_typeexpr +| 10 = @parenthesized_typeexpr +| 11 = @tuple_typeexpr +| 12 = @keyof_typeexpr +| 13 = @qualified_type_access +| 14 = @generic_typeexpr +| 15 = @type_label +| 16 = @typeof_typeexpr +| 17 = @local_var_type_access +| 18 = @qualified_var_type_access +| 19 = @this_var_type_access +| 20 = @predicate_typeexpr +| 21 = @interface_typeexpr +| 22 = @type_parameter +| 23 = @plain_function_typeexpr +| 24 = @constructor_typeexpr +| 25 = @local_namespace_access +| 26 = @qualified_namespace_access +| 27 = @mapped_typeexpr +| 28 = @conditional_typeexpr +| 29 = @infer_typeexpr +| 30 = @import_type_access +| 31 = @import_namespace_access +| 32 = @import_var_type_access +| 33 = @optional_typeexpr +| 34 = @rest_typeexpr +| 35 = @bigint_literal_typeexpr +| 36 = @readonly_typeexpr +| 37 = @template_literal_typeexpr +; + +@typeref = @typeaccess | @type_decl; +@type_identifier = @type_decl | @local_type_access | @type_label | @local_var_type_access | @local_namespace_access; +@typeexpr_parent = @expr | @stmt | @property | @typeexpr; +@literal_typeexpr = @string_literal_typeexpr | @number_literal_typeexpr | @boolean_literal_typeexpr | @bigint_literal_typeexpr; +@typeaccess = @local_type_access | @qualified_type_access | @import_type_access; +@vartypeaccess = @local_var_type_access | @qualified_var_type_access | @this_var_type_access | @import_var_type_access; +@namespace_access = @local_namespace_access | @qualified_namespace_access | @import_namespace_access; +@import_typeexpr = @import_type_access | @import_namespace_access | @import_var_type_access; + +@function_typeexpr = @plain_function_typeexpr | @constructor_typeexpr; + +// types +types ( + unique int id: @type, + int kind: int ref, + varchar(900) tostring: string ref +); + +#keyset[parent, idx] +type_child ( + int child: @type ref, + int parent: @type ref, + int idx: int ref +); + +case @type.kind of + 0 = @any_type +| 1 = @string_type +| 2 = @number_type +| 3 = @union_type +| 4 = @true_type +| 5 = @false_type +| 6 = @type_reference +| 7 = @object_type +| 8 = @canonical_type_variable_type +| 9 = @typeof_type +| 10 = @void_type +| 11 = @undefined_type +| 12 = @null_type +| 13 = @never_type +| 14 = @plain_symbol_type +| 15 = @unique_symbol_type +| 16 = @objectkeyword_type +| 17 = @intersection_type +| 18 = @tuple_type +| 19 = @lexical_type_variable_type +| 20 = @this_type +| 21 = @number_literal_type +| 22 = @string_literal_type +| 23 = @unknown_type +| 24 = @bigint_type +| 25 = @bigint_literal_type +; + +@boolean_literal_type = @true_type | @false_type; +@symbol_type = @plain_symbol_type | @unique_symbol_type; +@union_or_intersection_type = @union_type | @intersection_type; +@typevariable_type = @canonical_type_variable_type | @lexical_type_variable_type; + +has_asserts_keyword(int node: @predicate_typeexpr ref); + +@typed_ast_node = @expr | @typeexpr | @function; +ast_node_type( + unique int node: @typed_ast_node ref, + int typ: @type ref); + +declared_function_signature( + unique int node: @function ref, + int sig: @signature_type ref +); + +invoke_expr_signature( + unique int node: @invokeexpr ref, + int sig: @signature_type ref +); + +invoke_expr_overload_index( + unique int node: @invokeexpr ref, + int index: int ref +); + +symbols ( + unique int id: @symbol, + int kind: int ref, + varchar(900) name: string ref +); + +symbol_parent ( + unique int symbol: @symbol ref, + int parent: @symbol ref +); + +symbol_module ( + int symbol: @symbol ref, + varchar(900) moduleName: string ref +); + +symbol_global ( + int symbol: @symbol ref, + varchar(900) globalName: string ref +); + +case @symbol.kind of + 0 = @root_symbol +| 1 = @member_symbol +| 2 = @other_symbol +; + +@type_with_symbol = @type_reference | @typevariable_type | @typeof_type | @unique_symbol_type; +@ast_node_with_symbol = @type_definition | @namespace_definition | @toplevel | @typeaccess | @namespace_access | @var_decl | @function | @invokeexpr | @import_declaration | @external_module_reference | @external_module_declaration; + +ast_node_symbol( + unique int node: @ast_node_with_symbol ref, + int symbol: @symbol ref); + +type_symbol( + unique int typ: @type_with_symbol ref, + int symbol: @symbol ref); + +#keyset[typ, name] +type_property( + int typ: @type ref, + varchar(900) name: string ref, + int propertyType: @type ref); + +type_alias( + unique int aliasType: @type ref, + int underlyingType: @type ref); + +@literal_type = @string_literal_type | @number_literal_type | @boolean_literal_type | @bigint_literal_type; +@type_with_literal_value = @string_literal_type | @number_literal_type | @bigint_literal_type; +type_literal_value( + unique int typ: @type_with_literal_value ref, + varchar(900) value: string ref); + +signature_types ( + unique int id: @signature_type, + int kind: int ref, + varchar(900) tostring: string ref, + int type_parameters: int ref, + int required_params: int ref +); + +is_abstract_signature( + unique int sig: @signature_type ref +); + +signature_rest_parameter( + unique int sig: @signature_type ref, + int rest_param_arra_type: @type ref +); + +case @signature_type.kind of + 0 = @function_signature_type +| 1 = @constructor_signature_type +; + +#keyset[typ, kind, index] +type_contains_signature ( + int typ: @type ref, + int kind: int ref, // constructor/call/index + int index: int ref, // ordering of overloaded signatures + int sig: @signature_type ref +); + +#keyset[parent, index] +signature_contains_type ( + int child: @type ref, + int parent: @signature_type ref, + int index: int ref +); + +#keyset[sig, index] +signature_parameter_name ( + int sig: @signature_type ref, + int index: int ref, + varchar(900) name: string ref +); + +number_index_type ( + unique int baseType: @type ref, + int propertyType: @type ref +); + +string_index_type ( + unique int baseType: @type ref, + int propertyType: @type ref +); + +base_type_names( + int typeName: @symbol ref, + int baseTypeName: @symbol ref +); + +self_types( + int typeName: @symbol ref, + int selfType: @type_reference ref +); + +tuple_type_min_length( + unique int typ: @type ref, + int minLength: int ref +); + +tuple_type_rest_index( + unique int typ: @type ref, + int index: int ref +); + +// comments +comments (unique int id: @comment, + int kind: int ref, + int toplevel: @toplevel ref, + varchar(900) text: string ref, + varchar(900) tostring: string ref); + +case @comment.kind of + 0 = @slashslash_comment +| 1 = @slashstar_comment +| 2 = @doc_comment +| 3 = @html_comment_start +| 4 = @htmlcommentend; + +@html_comment = @html_comment_start | @htmlcommentend; +@line_comment = @slashslash_comment | @html_comment; +@block_comment = @slashstar_comment | @doc_comment; + +// source lines +lines (unique int id: @line, + int toplevel: @toplevel ref, + varchar(900) text: string ref, + varchar(2) terminator: string ref); +indentation (int file: @file ref, + int lineno: int ref, + varchar(1) indentChar: string ref, + int indentDepth: int ref); + +// JavaScript parse errors +js_parse_errors (unique int id: @js_parse_error, + int toplevel: @toplevel ref, + varchar(900) message: string ref, + varchar(900) line: string ref); + +// regular expressions +#keyset[parent, idx] +regexpterm (unique int id: @regexpterm, + int kind: int ref, + int parent: @regexpparent ref, + int idx: int ref, + varchar(900) tostring: string ref); + +@regexpparent = @regexpterm | @regexp_literal | @string_literal | @add_expr; + +case @regexpterm.kind of + 0 = @regexp_alt +| 1 = @regexp_seq +| 2 = @regexp_caret +| 3 = @regexp_dollar +| 4 = @regexp_wordboundary +| 5 = @regexp_nonwordboundary +| 6 = @regexp_positive_lookahead +| 7 = @regexp_negative_lookahead +| 8 = @regexp_star +| 9 = @regexp_plus +| 10 = @regexp_opt +| 11 = @regexp_range +| 12 = @regexp_dot +| 13 = @regexp_group +| 14 = @regexp_normal_constant +| 15 = @regexp_hex_escape +| 16 = @regexp_unicode_escape +| 17 = @regexp_dec_escape +| 18 = @regexp_oct_escape +| 19 = @regexp_ctrl_escape +| 20 = @regexp_char_class_escape +| 21 = @regexp_id_escape +| 22 = @regexp_backref +| 23 = @regexp_char_class +| 24 = @regexp_char_range +| 25 = @regexp_positive_lookbehind +| 26 = @regexp_negative_lookbehind +| 27 = @regexp_unicode_property_escape +| 28 = @regexp_intersection; + +regexp_parse_errors (unique int id: @regexp_parse_error, + int regexp: @regexpterm ref, + varchar(900) message: string ref); + +@regexp_quantifier = @regexp_star | @regexp_plus | @regexp_opt | @regexp_range; +@regexp_escape = @regexp_char_escape | @regexp_char_class_escape | @regexp_unicode_property_escape; +@regexp_char_escape = @regexp_hex_escape | @regexp_unicode_escape | @regexp_dec_escape | @regexp_oct_escape | @regexp_ctrl_escape | @regexp_id_escape; +@regexp_constant = @regexp_normal_constant | @regexp_char_escape; +@regexp_lookahead = @regexp_positive_lookahead | @regexp_negative_lookahead; +@regexp_lookbehind = @regexp_positive_lookbehind | @regexp_negative_lookbehind; +@regexp_subpattern = @regexp_lookahead | @regexp_lookbehind; +@regexp_anchor = @regexp_dollar | @regexp_caret; + +is_greedy (int id: @regexp_quantifier ref); +range_quantifier_lower_bound (unique int id: @regexp_range ref, int lo: int ref); +range_quantifier_upper_bound (unique int id: @regexp_range ref, int hi: int ref); +is_capture (unique int id: @regexp_group ref, int number: int ref); +is_named_capture (unique int id: @regexp_group ref, string name: string ref); +is_inverted (int id: @regexp_char_class ref); +regexp_const_value (unique int id: @regexp_constant ref, varchar(1) value: string ref); +char_class_escape (unique int id: @regexp_char_class_escape ref, varchar(1) value: string ref); +backref (unique int id: @regexp_backref ref, int value: int ref); +named_backref (unique int id: @regexp_backref ref, string name: string ref); +unicode_property_escapename (unique int id: @regexp_unicode_property_escape ref, string name: string ref); +unicode_property_escapevalue (unique int id: @regexp_unicode_property_escape ref, string value: string ref); + +// tokens +#keyset[toplevel, idx] +tokeninfo (unique int id: @token, + int kind: int ref, + int toplevel: @toplevel ref, + int idx: int ref, + varchar(900) value: string ref); + +case @token.kind of + 0 = @token_eof +| 1 = @token_null_literal +| 2 = @token_boolean_literal +| 3 = @token_numeric_literal +| 4 = @token_string_literal +| 5 = @token_regular_expression +| 6 = @token_identifier +| 7 = @token_keyword +| 8 = @token_punctuator; + +// associate comments with the token immediately following them (which may be EOF) +next_token (int comment: @comment ref, int token: @token ref); + +// JSON +#keyset[parent, idx] +json (unique int id: @json_value, + int kind: int ref, + int parent: @json_parent ref, + int idx: int ref, + varchar(900) tostring: string ref); + +json_literals (varchar(900) value: string ref, + varchar(900) raw: string ref, + unique int expr: @json_value ref); + +json_properties (int obj: @json_object ref, + varchar(900) property: string ref, + int value: @json_value ref); + +json_errors (unique int id: @json_parse_error, + varchar(900) message: string ref); + +json_locations(unique int locatable: @json_locatable ref, + int location: @location_default ref); + +case @json_value.kind of + 0 = @json_null +| 1 = @json_boolean +| 2 = @json_number +| 3 = @json_string +| 4 = @json_array +| 5 = @json_object; + +@json_parent = @json_object | @json_array | @file; + +@json_locatable = @json_value | @json_parse_error; + +// locations +@ast_node = @toplevel | @stmt | @expr | @property | @typeexpr; + +@locatable = @file + | @ast_node + | @comment + | @line + | @js_parse_error | @regexp_parse_error + | @regexpterm + | @json_locatable + | @token + | @cfg_node + | @jsdoc | @jsdoc_type_expr | @jsdoc_tag + | @yaml_locatable + | @xmllocatable + | @configLocatable + | @template_placeholder_tag; + +hasLocation (unique int locatable: @locatable ref, + int location: @location ref); + +// CFG +entry_cfg_node (unique int id: @entry_node, int container: @stmt_container ref); +exit_cfg_node (unique int id: @exit_node, int container: @stmt_container ref); +guard_node (unique int id: @guard_node, int kind: int ref, int test: @expr ref); +case @guard_node.kind of + 0 = @falsy_guard +| 1 = @truthy_guard; +@condition_guard = @falsy_guard | @truthy_guard; + +@synthetic_cfg_node = @entry_node | @exit_node | @guard_node; +@cfg_node = @synthetic_cfg_node | @expr_parent; + +successor (int pred: @cfg_node ref, int succ: @cfg_node ref); + +// JSDoc comments +jsdoc (unique int id: @jsdoc, varchar(900) description: string ref, int comment: @comment ref); +#keyset[parent, idx] +jsdoc_tags (unique int id: @jsdoc_tag, varchar(900) title: string ref, + int parent: @jsdoc ref, int idx: int ref, varchar(900) tostring: string ref); +jsdoc_tag_descriptions (unique int tag: @jsdoc_tag ref, varchar(900) text: string ref); +jsdoc_tag_names (unique int tag: @jsdoc_tag ref, varchar(900) text: string ref); + +#keyset[parent, idx] +jsdoc_type_exprs (unique int id: @jsdoc_type_expr, + int kind: int ref, + int parent: @jsdoc_type_expr_parent ref, + int idx: int ref, + varchar(900) tostring: string ref); +case @jsdoc_type_expr.kind of + 0 = @jsdoc_any_type_expr +| 1 = @jsdoc_null_type_expr +| 2 = @jsdoc_undefined_type_expr +| 3 = @jsdoc_unknown_type_expr +| 4 = @jsdoc_void_type_expr +| 5 = @jsdoc_named_type_expr +| 6 = @jsdoc_applied_type_expr +| 7 = @jsdoc_nullable_type_expr +| 8 = @jsdoc_non_nullable_type_expr +| 9 = @jsdoc_record_type_expr +| 10 = @jsdoc_array_type_expr +| 11 = @jsdoc_union_type_expr +| 12 = @jsdoc_function_type_expr +| 13 = @jsdoc_optional_type_expr +| 14 = @jsdoc_rest_type_expr +; + +#keyset[id, idx] +jsdoc_record_field_name (int id: @jsdoc_record_type_expr ref, int idx: int ref, varchar(900) name: string ref); +jsdoc_prefix_qualifier (int id: @jsdoc_type_expr ref); +jsdoc_has_new_parameter (int fn: @jsdoc_function_type_expr ref); + +@jsdoc_type_expr_parent = @jsdoc_type_expr | @jsdoc_tag; + +jsdoc_errors (unique int id: @jsdoc_error, int tag: @jsdoc_tag ref, varchar(900) message: string ref, varchar(900) tostring: string ref); + +@dataflownode = @expr | @function_decl_stmt | @class_decl_stmt | @namespace_declaration | @enum_declaration | @property; + +@optionalchainable = @call_expr | @propaccess; + +isOptionalChaining(int id: @optionalchainable ref); + +/** + * The time taken for the extraction of a file. + * This table contains non-deterministic content. + * + * The sum of the `time` column for each (`file`, `timerKind`) pair + * is the total time taken for extraction of `file`. The `extractionPhase` + * column provides a granular view of the extraction time of the file. + */ +extraction_time( + int file : @file ref, + // see `com.semmle.js.extractor.ExtractionMetrics.ExtractionPhase`. + int extractionPhase: int ref, + // 0 for the elapsed CPU time in nanoseconds, 1 for the elapsed wallclock time in nanoseconds + int timerKind: int ref, + float time: float ref +) + +/** +* Non-timing related data for the extraction of a single file. +* This table contains non-deterministic content. +*/ +extraction_data( + int file : @file ref, + // the absolute path to the cache file + varchar(900) cacheFile: string ref, + boolean fromCache: boolean ref, + int length: int ref +) + +/*- YAML -*/ + +#keyset[parent, idx] +yaml (unique int id: @yaml_node, + int kind: int ref, + int parent: @yaml_node_parent ref, + int idx: int ref, + string tag: string ref, + string tostring: string ref); + +case @yaml_node.kind of + 0 = @yaml_scalar_node +| 1 = @yaml_mapping_node +| 2 = @yaml_sequence_node +| 3 = @yaml_alias_node +; + +@yaml_collection_node = @yaml_mapping_node | @yaml_sequence_node; + +@yaml_node_parent = @yaml_collection_node | @file; + +yaml_anchors (unique int node: @yaml_node ref, + string anchor: string ref); + +yaml_aliases (unique int alias: @yaml_alias_node ref, + string target: string ref); + +yaml_scalars (unique int scalar: @yaml_scalar_node ref, + int style: int ref, + string value: string ref); + +yaml_errors (unique int id: @yaml_error, + string message: string ref); + +yaml_locations(unique int locatable: @yaml_locatable ref, + int location: @location_default ref); + +@yaml_locatable = @yaml_node | @yaml_error; + +/*- XML Files -*/ + +xmlEncoding( + unique int id: @file ref, + string encoding: string ref +); + +xmlDTDs( + unique int id: @xmldtd, + string root: string ref, + string publicId: string ref, + string systemId: string ref, + int fileid: @file ref +); + +xmlElements( + unique int id: @xmlelement, + string name: string ref, + int parentid: @xmlparent ref, + int idx: int ref, + int fileid: @file ref +); + +xmlAttrs( + unique int id: @xmlattribute, + int elementid: @xmlelement ref, + string name: string ref, + string value: string ref, + int idx: int ref, + int fileid: @file ref +); + +xmlNs( + int id: @xmlnamespace, + string prefixName: string ref, + string URI: string ref, + int fileid: @file ref +); + +xmlHasNs( + int elementId: @xmlnamespaceable ref, + int nsId: @xmlnamespace ref, + int fileid: @file ref +); + +xmlComments( + unique int id: @xmlcomment, + string text: string ref, + int parentid: @xmlparent ref, + int fileid: @file ref +); + +xmlChars( + unique int id: @xmlcharacters, + string text: string ref, + int parentid: @xmlparent ref, + int idx: int ref, + int isCDATA: int ref, + int fileid: @file ref +); + +@xmlparent = @file | @xmlelement; +@xmlnamespaceable = @xmlelement | @xmlattribute; + +xmllocations( + int xmlElement: @xmllocatable ref, + int location: @location_default ref +); + +@xmllocatable = @xmlcharacters | @xmlelement | @xmlcomment | @xmlattribute | @xmldtd | @file | @xmlnamespace; + +/*- Configuration files with key value pairs -*/ + +configs( + unique int id: @config +); + +configNames( + unique int id: @configName, + int config: @config ref, + string name: string ref +); + +configValues( + unique int id: @configValue, + int config: @config ref, + string value: string ref +); + +configLocations( + int locatable: @configLocatable ref, + int location: @location_default ref +); + +@configLocatable = @config | @configName | @configValue; diff --git a/javascript/downgrades/eda7d3dc42baf69d0acb592072d7136acf61d903/semmlecode.javascript.dbscheme b/javascript/downgrades/eda7d3dc42baf69d0acb592072d7136acf61d903/semmlecode.javascript.dbscheme new file mode 100644 index 000000000000..c88c69174bd0 --- /dev/null +++ b/javascript/downgrades/eda7d3dc42baf69d0acb592072d7136acf61d903/semmlecode.javascript.dbscheme @@ -0,0 +1,1190 @@ +/*** Standard fragments ***/ + +/*- Files and folders -*/ + +/** + * The location of an element. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `file`. + * For more information, see + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). + */ +locations_default( + unique int id: @location_default, + int file: @file ref, + int beginLine: int ref, + int beginColumn: int ref, + int endLine: int ref, + int endColumn: int ref +); + +files( + unique int id: @file, + string name: string ref +); + +folders( + unique int id: @folder, + string name: string ref +); + +@container = @file | @folder + +containerparent( + int parent: @container ref, + unique int child: @container ref +); + +/*- Lines of code -*/ + +numlines( + int element_id: @sourceline ref, + int num_lines: int ref, + int num_code: int ref, + int num_comment: int ref +); + +/*- External data -*/ + +/** + * External data, loaded from CSV files during snapshot creation. See + * [Tutorial: Incorporating external data](https://help.semmle.com/wiki/display/SD/Tutorial%3A+Incorporating+external+data) + * for more information. + */ +externalData( + int id : @externalDataElement, + string path : string ref, + int column: int ref, + string value : string ref +); + +/*- Source location prefix -*/ + +/** + * The source location of the snapshot. + */ +sourceLocationPrefix(string prefix : string ref); + +/*- JavaScript-specific part -*/ + +@location = @location_default + +@sourceline = @locatable; + +filetype( + int file: @file ref, + string filetype: string ref +) + +// top-level code fragments +toplevels (unique int id: @toplevel, + int kind: int ref); + +is_externs (int toplevel: @toplevel ref); + +case @toplevel.kind of + 0 = @script +| 1 = @inline_script +| 2 = @event_handler +| 3 = @javascript_url +| 4 = @template_toplevel; + +is_module (int tl: @toplevel ref); +is_nodejs (int tl: @toplevel ref); +is_es2015_module (int tl: @toplevel ref); +is_closure_module (int tl: @toplevel ref); + +@xml_node_with_code = @xmlelement | @xmlattribute | @template_placeholder_tag; +toplevel_parent_xml_node( + unique int toplevel: @toplevel ref, + int xmlnode: @xml_node_with_code ref); + +xml_element_parent_expression( + unique int xmlnode: @xmlelement ref, + int expression: @expr ref, + int index: int ref); + +// statements +#keyset[parent, idx] +stmts (unique int id: @stmt, + int kind: int ref, + int parent: @stmt_parent ref, + int idx: int ref, + varchar(900) tostring: string ref); + +stmt_containers (unique int stmt: @stmt ref, + int container: @stmt_container ref); + +jump_targets (unique int jump: @stmt ref, + int target: @stmt ref); + +@stmt_parent = @stmt | @toplevel | @function_expr | @arrow_function_expr | @static_initializer; +@stmt_container = @toplevel | @function | @namespace_declaration | @external_module_declaration | @global_augmentation_declaration; + +case @stmt.kind of + 0 = @empty_stmt +| 1 = @block_stmt +| 2 = @expr_stmt +| 3 = @if_stmt +| 4 = @labeled_stmt +| 5 = @break_stmt +| 6 = @continue_stmt +| 7 = @with_stmt +| 8 = @switch_stmt +| 9 = @return_stmt +| 10 = @throw_stmt +| 11 = @try_stmt +| 12 = @while_stmt +| 13 = @do_while_stmt +| 14 = @for_stmt +| 15 = @for_in_stmt +| 16 = @debugger_stmt +| 17 = @function_decl_stmt +| 18 = @var_decl_stmt +| 19 = @case +| 20 = @catch_clause +| 21 = @for_of_stmt +| 22 = @const_decl_stmt +| 23 = @let_stmt +| 24 = @legacy_let_stmt +| 25 = @for_each_stmt +| 26 = @class_decl_stmt +| 27 = @import_declaration +| 28 = @export_all_declaration +| 29 = @export_default_declaration +| 30 = @export_named_declaration +| 31 = @namespace_declaration +| 32 = @import_equals_declaration +| 33 = @export_assign_declaration +| 34 = @interface_declaration +| 35 = @type_alias_declaration +| 36 = @enum_declaration +| 37 = @external_module_declaration +| 38 = @export_as_namespace_declaration +| 39 = @global_augmentation_declaration +| 40 = @using_decl_stmt +; + +@decl_stmt = @var_decl_stmt | @const_decl_stmt | @let_stmt | @legacy_let_stmt | @using_decl_stmt; + +@export_declaration = @export_all_declaration | @export_default_declaration | @export_named_declaration; + +@namespace_definition = @namespace_declaration | @enum_declaration; +@type_definition = @class_definition | @interface_declaration | @enum_declaration | @type_alias_declaration | @enum_member; + +is_instantiated(unique int decl: @namespace_declaration ref); + +@declarable_node = @decl_stmt | @namespace_declaration | @class_decl_stmt | @function_decl_stmt | @enum_declaration | @external_module_declaration | @global_augmentation_declaration | @field; +has_declare_keyword(unique int stmt: @declarable_node ref); + +is_for_await_of(unique int forof: @for_of_stmt ref); + +// expressions +#keyset[parent, idx] +exprs (unique int id: @expr, + int kind: int ref, + int parent: @expr_parent ref, + int idx: int ref, + varchar(900) tostring: string ref); + +literals (varchar(900) value: string ref, + varchar(900) raw: string ref, + unique int expr: @expr_or_type ref); + +enclosing_stmt (unique int expr: @expr_or_type ref, + int stmt: @stmt ref); + +expr_containers (unique int expr: @expr_or_type ref, + int container: @stmt_container ref); + +array_size (unique int ae: @arraylike ref, + int sz: int ref); + +is_delegating (int yield: @yield_expr ref); + +@expr_or_stmt = @expr | @stmt; +@expr_or_type = @expr | @typeexpr; +@expr_parent = @expr_or_stmt | @property | @function_typeexpr; +@arraylike = @array_expr | @array_pattern; +@type_annotation = @typeexpr | @jsdoc_type_expr; +@node_in_stmt_container = @cfg_node | @type_annotation | @toplevel; + +case @expr.kind of + 0 = @label +| 1 = @null_literal +| 2 = @boolean_literal +| 3 = @number_literal +| 4 = @string_literal +| 5 = @regexp_literal +| 6 = @this_expr +| 7 = @array_expr +| 8 = @obj_expr +| 9 = @function_expr +| 10 = @seq_expr +| 11 = @conditional_expr +| 12 = @new_expr +| 13 = @call_expr +| 14 = @dot_expr +| 15 = @index_expr +| 16 = @neg_expr +| 17 = @plus_expr +| 18 = @log_not_expr +| 19 = @bit_not_expr +| 20 = @typeof_expr +| 21 = @void_expr +| 22 = @delete_expr +| 23 = @eq_expr +| 24 = @neq_expr +| 25 = @eqq_expr +| 26 = @neqq_expr +| 27 = @lt_expr +| 28 = @le_expr +| 29 = @gt_expr +| 30 = @ge_expr +| 31 = @lshift_expr +| 32 = @rshift_expr +| 33 = @urshift_expr +| 34 = @add_expr +| 35 = @sub_expr +| 36 = @mul_expr +| 37 = @div_expr +| 38 = @mod_expr +| 39 = @bitor_expr +| 40 = @xor_expr +| 41 = @bitand_expr +| 42 = @in_expr +| 43 = @instanceof_expr +| 44 = @logand_expr +| 45 = @logor_expr +| 47 = @assign_expr +| 48 = @assign_add_expr +| 49 = @assign_sub_expr +| 50 = @assign_mul_expr +| 51 = @assign_div_expr +| 52 = @assign_mod_expr +| 53 = @assign_lshift_expr +| 54 = @assign_rshift_expr +| 55 = @assign_urshift_expr +| 56 = @assign_or_expr +| 57 = @assign_xor_expr +| 58 = @assign_and_expr +| 59 = @preinc_expr +| 60 = @postinc_expr +| 61 = @predec_expr +| 62 = @postdec_expr +| 63 = @par_expr +| 64 = @var_declarator +| 65 = @arrow_function_expr +| 66 = @spread_element +| 67 = @array_pattern +| 68 = @object_pattern +| 69 = @yield_expr +| 70 = @tagged_template_expr +| 71 = @template_literal +| 72 = @template_element +| 73 = @array_comprehension_expr +| 74 = @generator_expr +| 75 = @for_in_comprehension_block +| 76 = @for_of_comprehension_block +| 77 = @legacy_letexpr +| 78 = @var_decl +| 79 = @proper_varaccess +| 80 = @class_expr +| 81 = @super_expr +| 82 = @newtarget_expr +| 83 = @named_import_specifier +| 84 = @import_default_specifier +| 85 = @import_namespace_specifier +| 86 = @named_export_specifier +| 87 = @exp_expr +| 88 = @assign_exp_expr +| 89 = @jsx_element +| 90 = @jsx_qualified_name +| 91 = @jsx_empty_expr +| 92 = @await_expr +| 93 = @function_sent_expr +| 94 = @decorator +| 95 = @export_default_specifier +| 96 = @export_namespace_specifier +| 97 = @bind_expr +| 98 = @external_module_reference +| 99 = @dynamic_import +| 100 = @expression_with_type_arguments +| 101 = @prefix_type_assertion +| 102 = @as_type_assertion +| 103 = @export_varaccess +| 104 = @decorator_list +| 105 = @non_null_assertion +| 106 = @bigint_literal +| 107 = @nullishcoalescing_expr +| 108 = @e4x_xml_anyname +| 109 = @e4x_xml_static_attribute_selector +| 110 = @e4x_xml_dynamic_attribute_selector +| 111 = @e4x_xml_filter_expression +| 112 = @e4x_xml_static_qualident +| 113 = @e4x_xml_dynamic_qualident +| 114 = @e4x_xml_dotdotexpr +| 115 = @import_meta_expr +| 116 = @assignlogandexpr +| 117 = @assignlogorexpr +| 118 = @assignnullishcoalescingexpr +| 119 = @template_pipe_ref +| 120 = @generated_code_expr +| 121 = @satisfies_expr +; + +@varaccess = @proper_varaccess | @export_varaccess; +@varref = @var_decl | @varaccess; + +@identifier = @label | @varref | @type_identifier; + +@literal = @null_literal | @boolean_literal | @number_literal | @string_literal | @regexp_literal | @bigint_literal; + +@propaccess = @dot_expr | @index_expr; + +@invokeexpr = @new_expr | @call_expr; + +@unaryexpr = @neg_expr | @plus_expr | @log_not_expr | @bit_not_expr | @typeof_expr | @void_expr | @delete_expr | @spread_element; + +@equality_test = @eq_expr | @neq_expr | @eqq_expr | @neqq_expr; + +@comparison = @equality_test | @lt_expr | @le_expr | @gt_expr | @ge_expr; + +@binaryexpr = @comparison | @lshift_expr | @rshift_expr | @urshift_expr | @add_expr | @sub_expr | @mul_expr | @div_expr | @mod_expr | @exp_expr | @bitor_expr | @xor_expr | @bitand_expr | @in_expr | @instanceof_expr | @logand_expr | @logor_expr | @nullishcoalescing_expr; + +@assignment = @assign_expr | @assign_add_expr | @assign_sub_expr | @assign_mul_expr | @assign_div_expr | @assign_mod_expr | @assign_exp_expr | @assign_lshift_expr | @assign_rshift_expr | @assign_urshift_expr | @assign_or_expr | @assign_xor_expr | @assign_and_expr | @assignlogandexpr | @assignlogorexpr | @assignnullishcoalescingexpr; + +@updateexpr = @preinc_expr | @postinc_expr | @predec_expr | @postdec_expr; + +@pattern = @varref | @array_pattern | @object_pattern; + +@comprehension_expr = @array_comprehension_expr | @generator_expr; + +@comprehension_block = @for_in_comprehension_block | @for_of_comprehension_block; + +@import_specifier = @named_import_specifier | @import_default_specifier | @import_namespace_specifier; + +@exportspecifier = @named_export_specifier | @export_default_specifier | @export_namespace_specifier; + +@type_keyword_operand = @import_declaration | @export_declaration | @import_specifier; + +@type_assertion = @as_type_assertion | @prefix_type_assertion; + +@class_definition = @class_decl_stmt | @class_expr; +@interface_definition = @interface_declaration | @interface_typeexpr; +@class_or_interface = @class_definition | @interface_definition; + +@lexical_decl = @var_decl | @type_decl; +@lexical_access = @varaccess | @local_type_access | @local_var_type_access | @local_namespace_access; +@lexical_ref = @lexical_decl | @lexical_access; + +@e4x_xml_attribute_selector = @e4x_xml_static_attribute_selector | @e4x_xml_dynamic_attribute_selector; +@e4x_xml_qualident = @e4x_xml_static_qualident | @e4x_xml_dynamic_qualident; + +expr_contains_template_tag_location( + int expr: @expr ref, + int location: @location ref +); + +@template_placeholder_tag_parent = @xmlelement | @xmlattribute | @file; + +template_placeholder_tag_info( + unique int node: @template_placeholder_tag, + int parentNode: @template_placeholder_tag_parent ref, + varchar(900) raw: string ref +); + +// scopes +scopes (unique int id: @scope, + int kind: int ref); + +case @scope.kind of + 0 = @global_scope +| 1 = @function_scope +| 2 = @catch_scope +| 3 = @module_scope +| 4 = @block_scope +| 5 = @for_scope +| 6 = @for_in_scope // for-of scopes work the same as for-in scopes +| 7 = @comprehension_block_scope +| 8 = @class_expr_scope +| 9 = @namespace_scope +| 10 = @class_decl_scope +| 11 = @interface_scope +| 12 = @type_alias_scope +| 13 = @mapped_type_scope +| 14 = @enum_scope +| 15 = @external_module_scope +| 16 = @conditional_type_scope; + +scopenodes (unique int node: @ast_node ref, + int scope: @scope ref); + +scopenesting (unique int inner: @scope ref, + int outer: @scope ref); + +// functions +@function = @function_decl_stmt | @function_expr | @arrow_function_expr; + +@parameterized = @function | @catch_clause; +@type_parameterized = @function | @class_or_interface | @type_alias_declaration | @mapped_typeexpr | @infer_typeexpr; + +is_generator (int fun: @function ref); +has_rest_parameter (int fun: @function ref); +is_async (int fun: @function ref); + +// variables and lexically scoped type names +#keyset[scope, name] +variables (unique int id: @variable, + varchar(900) name: string ref, + int scope: @scope ref); + +#keyset[scope, name] +local_type_names (unique int id: @local_type_name, + varchar(900) name: string ref, + int scope: @scope ref); + +#keyset[scope, name] +local_namespace_names (unique int id: @local_namespace_name, + varchar(900) name: string ref, + int scope: @scope ref); + +is_arguments_object (int id: @variable ref); + +@lexical_name = @variable | @local_type_name | @local_namespace_name; + +@bind_id = @varaccess | @local_var_type_access; +bind (unique int id: @bind_id ref, + int decl: @variable ref); + +decl (unique int id: @var_decl ref, + int decl: @variable ref); + +@typebind_id = @local_type_access | @export_varaccess; +typebind (unique int id: @typebind_id ref, + int decl: @local_type_name ref); + +@typedecl_id = @type_decl | @var_decl; +typedecl (unique int id: @typedecl_id ref, + int decl: @local_type_name ref); + +namespacedecl (unique int id: @var_decl ref, + int decl: @local_namespace_name ref); + +@namespacebind_id = @local_namespace_access | @export_varaccess; +namespacebind (unique int id: @namespacebind_id ref, + int decl: @local_namespace_name ref); + + +// properties in object literals, property patterns in object patterns, and method declarations in classes +#keyset[parent, index] +properties (unique int id: @property, + int parent: @property_parent ref, + int index: int ref, + int kind: int ref, + varchar(900) tostring: string ref); + +case @property.kind of + 0 = @value_property +| 1 = @property_getter +| 2 = @property_setter +| 3 = @jsx_attribute +| 4 = @function_call_signature +| 5 = @constructor_call_signature +| 6 = @index_signature +| 7 = @enum_member +| 8 = @proper_field +| 9 = @parameter_field +| 10 = @static_initializer +; + +@property_parent = @obj_expr | @object_pattern | @class_definition | @jsx_element | @interface_definition | @enum_declaration; +@property_accessor = @property_getter | @property_setter; +@call_signature = @function_call_signature | @constructor_call_signature; +@field = @proper_field | @parameter_field; +@field_or_vardeclarator = @field | @var_declarator; + +is_computed (int id: @property ref); +is_method (int id: @property ref); +is_static (int id: @property ref); +is_abstract_member (int id: @property ref); +is_const_enum (int id: @enum_declaration ref); +is_abstract_class (int id: @class_decl_stmt ref); + +has_public_keyword (int id: @property ref); +has_private_keyword (int id: @property ref); +has_protected_keyword (int id: @property ref); +has_readonly_keyword (int id: @property ref); +has_type_keyword (int id: @type_keyword_operand ref); +is_optional_member (int id: @property ref); +has_definite_assignment_assertion (int id: @field_or_vardeclarator ref); +is_optional_parameter_declaration (unique int parameter: @pattern ref); + +#keyset[constructor, param_index] +parameter_fields( + unique int field: @parameter_field ref, + int constructor: @function_expr ref, + int param_index: int ref +); + +// types +#keyset[parent, idx] +typeexprs ( + unique int id: @typeexpr, + int kind: int ref, + int parent: @typeexpr_parent ref, + int idx: int ref, + varchar(900) tostring: string ref +); + +case @typeexpr.kind of + 0 = @local_type_access +| 1 = @type_decl +| 2 = @keyword_typeexpr +| 3 = @string_literal_typeexpr +| 4 = @number_literal_typeexpr +| 5 = @boolean_literal_typeexpr +| 6 = @array_typeexpr +| 7 = @union_typeexpr +| 8 = @indexed_access_typeexpr +| 9 = @intersection_typeexpr +| 10 = @parenthesized_typeexpr +| 11 = @tuple_typeexpr +| 12 = @keyof_typeexpr +| 13 = @qualified_type_access +| 14 = @generic_typeexpr +| 15 = @type_label +| 16 = @typeof_typeexpr +| 17 = @local_var_type_access +| 18 = @qualified_var_type_access +| 19 = @this_var_type_access +| 20 = @predicate_typeexpr +| 21 = @interface_typeexpr +| 22 = @type_parameter +| 23 = @plain_function_typeexpr +| 24 = @constructor_typeexpr +| 25 = @local_namespace_access +| 26 = @qualified_namespace_access +| 27 = @mapped_typeexpr +| 28 = @conditional_typeexpr +| 29 = @infer_typeexpr +| 30 = @import_type_access +| 31 = @import_namespace_access +| 32 = @import_var_type_access +| 33 = @optional_typeexpr +| 34 = @rest_typeexpr +| 35 = @bigint_literal_typeexpr +| 36 = @readonly_typeexpr +| 37 = @template_literal_typeexpr +; + +@typeref = @typeaccess | @type_decl; +@type_identifier = @type_decl | @local_type_access | @type_label | @local_var_type_access | @local_namespace_access; +@typeexpr_parent = @expr | @stmt | @property | @typeexpr; +@literal_typeexpr = @string_literal_typeexpr | @number_literal_typeexpr | @boolean_literal_typeexpr | @bigint_literal_typeexpr; +@typeaccess = @local_type_access | @qualified_type_access | @import_type_access; +@vartypeaccess = @local_var_type_access | @qualified_var_type_access | @this_var_type_access | @import_var_type_access; +@namespace_access = @local_namespace_access | @qualified_namespace_access | @import_namespace_access; +@import_typeexpr = @import_type_access | @import_namespace_access | @import_var_type_access; + +@function_typeexpr = @plain_function_typeexpr | @constructor_typeexpr; + +// types +types ( + unique int id: @type, + int kind: int ref, + varchar(900) tostring: string ref +); + +#keyset[parent, idx] +type_child ( + int child: @type ref, + int parent: @type ref, + int idx: int ref +); + +case @type.kind of + 0 = @any_type +| 1 = @string_type +| 2 = @number_type +| 3 = @union_type +| 4 = @true_type +| 5 = @false_type +| 6 = @type_reference +| 7 = @object_type +| 8 = @canonical_type_variable_type +| 9 = @typeof_type +| 10 = @void_type +| 11 = @undefined_type +| 12 = @null_type +| 13 = @never_type +| 14 = @plain_symbol_type +| 15 = @unique_symbol_type +| 16 = @objectkeyword_type +| 17 = @intersection_type +| 18 = @tuple_type +| 19 = @lexical_type_variable_type +| 20 = @this_type +| 21 = @number_literal_type +| 22 = @string_literal_type +| 23 = @unknown_type +| 24 = @bigint_type +| 25 = @bigint_literal_type +; + +@boolean_literal_type = @true_type | @false_type; +@symbol_type = @plain_symbol_type | @unique_symbol_type; +@union_or_intersection_type = @union_type | @intersection_type; +@typevariable_type = @canonical_type_variable_type | @lexical_type_variable_type; + +has_asserts_keyword(int node: @predicate_typeexpr ref); + +@typed_ast_node = @expr | @typeexpr | @function; +ast_node_type( + unique int node: @typed_ast_node ref, + int typ: @type ref); + +declared_function_signature( + unique int node: @function ref, + int sig: @signature_type ref +); + +invoke_expr_signature( + unique int node: @invokeexpr ref, + int sig: @signature_type ref +); + +invoke_expr_overload_index( + unique int node: @invokeexpr ref, + int index: int ref +); + +symbols ( + unique int id: @symbol, + int kind: int ref, + varchar(900) name: string ref +); + +symbol_parent ( + unique int symbol: @symbol ref, + int parent: @symbol ref +); + +symbol_module ( + int symbol: @symbol ref, + varchar(900) moduleName: string ref +); + +symbol_global ( + int symbol: @symbol ref, + varchar(900) globalName: string ref +); + +case @symbol.kind of + 0 = @root_symbol +| 1 = @member_symbol +| 2 = @other_symbol +; + +@type_with_symbol = @type_reference | @typevariable_type | @typeof_type | @unique_symbol_type; +@ast_node_with_symbol = @type_definition | @namespace_definition | @toplevel | @typeaccess | @namespace_access | @var_decl | @function | @invokeexpr | @import_declaration | @external_module_reference | @external_module_declaration; + +ast_node_symbol( + unique int node: @ast_node_with_symbol ref, + int symbol: @symbol ref); + +type_symbol( + unique int typ: @type_with_symbol ref, + int symbol: @symbol ref); + +#keyset[typ, name] +type_property( + int typ: @type ref, + varchar(900) name: string ref, + int propertyType: @type ref); + +type_alias( + unique int aliasType: @type ref, + int underlyingType: @type ref); + +@literal_type = @string_literal_type | @number_literal_type | @boolean_literal_type | @bigint_literal_type; +@type_with_literal_value = @string_literal_type | @number_literal_type | @bigint_literal_type; +type_literal_value( + unique int typ: @type_with_literal_value ref, + varchar(900) value: string ref); + +signature_types ( + unique int id: @signature_type, + int kind: int ref, + varchar(900) tostring: string ref, + int type_parameters: int ref, + int required_params: int ref +); + +is_abstract_signature( + unique int sig: @signature_type ref +); + +signature_rest_parameter( + unique int sig: @signature_type ref, + int rest_param_arra_type: @type ref +); + +case @signature_type.kind of + 0 = @function_signature_type +| 1 = @constructor_signature_type +; + +#keyset[typ, kind, index] +type_contains_signature ( + int typ: @type ref, + int kind: int ref, // constructor/call/index + int index: int ref, // ordering of overloaded signatures + int sig: @signature_type ref +); + +#keyset[parent, index] +signature_contains_type ( + int child: @type ref, + int parent: @signature_type ref, + int index: int ref +); + +#keyset[sig, index] +signature_parameter_name ( + int sig: @signature_type ref, + int index: int ref, + varchar(900) name: string ref +); + +number_index_type ( + unique int baseType: @type ref, + int propertyType: @type ref +); + +string_index_type ( + unique int baseType: @type ref, + int propertyType: @type ref +); + +base_type_names( + int typeName: @symbol ref, + int baseTypeName: @symbol ref +); + +self_types( + int typeName: @symbol ref, + int selfType: @type_reference ref +); + +tuple_type_min_length( + unique int typ: @type ref, + int minLength: int ref +); + +tuple_type_rest_index( + unique int typ: @type ref, + int index: int ref +); + +// comments +comments (unique int id: @comment, + int kind: int ref, + int toplevel: @toplevel ref, + varchar(900) text: string ref, + varchar(900) tostring: string ref); + +case @comment.kind of + 0 = @slashslash_comment +| 1 = @slashstar_comment +| 2 = @doc_comment +| 3 = @html_comment_start +| 4 = @htmlcommentend; + +@html_comment = @html_comment_start | @htmlcommentend; +@line_comment = @slashslash_comment | @html_comment; +@block_comment = @slashstar_comment | @doc_comment; + +// source lines +lines (unique int id: @line, + int toplevel: @toplevel ref, + varchar(900) text: string ref, + varchar(2) terminator: string ref); +indentation (int file: @file ref, + int lineno: int ref, + varchar(1) indentChar: string ref, + int indentDepth: int ref); + +// JavaScript parse errors +js_parse_errors (unique int id: @js_parse_error, + int toplevel: @toplevel ref, + varchar(900) message: string ref, + varchar(900) line: string ref); + +// regular expressions +#keyset[parent, idx] +regexpterm (unique int id: @regexpterm, + int kind: int ref, + int parent: @regexpparent ref, + int idx: int ref, + varchar(900) tostring: string ref); + +@regexpparent = @regexpterm | @regexp_literal | @string_literal | @add_expr; + +case @regexpterm.kind of + 0 = @regexp_alt +| 1 = @regexp_seq +| 2 = @regexp_caret +| 3 = @regexp_dollar +| 4 = @regexp_wordboundary +| 5 = @regexp_nonwordboundary +| 6 = @regexp_positive_lookahead +| 7 = @regexp_negative_lookahead +| 8 = @regexp_star +| 9 = @regexp_plus +| 10 = @regexp_opt +| 11 = @regexp_range +| 12 = @regexp_dot +| 13 = @regexp_group +| 14 = @regexp_normal_constant +| 15 = @regexp_hex_escape +| 16 = @regexp_unicode_escape +| 17 = @regexp_dec_escape +| 18 = @regexp_oct_escape +| 19 = @regexp_ctrl_escape +| 20 = @regexp_char_class_escape +| 21 = @regexp_id_escape +| 22 = @regexp_backref +| 23 = @regexp_char_class +| 24 = @regexp_char_range +| 25 = @regexp_positive_lookbehind +| 26 = @regexp_negative_lookbehind +| 27 = @regexp_unicode_property_escape; + +regexp_parse_errors (unique int id: @regexp_parse_error, + int regexp: @regexpterm ref, + varchar(900) message: string ref); + +@regexp_quantifier = @regexp_star | @regexp_plus | @regexp_opt | @regexp_range; +@regexp_escape = @regexp_char_escape | @regexp_char_class_escape | @regexp_unicode_property_escape; +@regexp_char_escape = @regexp_hex_escape | @regexp_unicode_escape | @regexp_dec_escape | @regexp_oct_escape | @regexp_ctrl_escape | @regexp_id_escape; +@regexp_constant = @regexp_normal_constant | @regexp_char_escape; +@regexp_lookahead = @regexp_positive_lookahead | @regexp_negative_lookahead; +@regexp_lookbehind = @regexp_positive_lookbehind | @regexp_negative_lookbehind; +@regexp_subpattern = @regexp_lookahead | @regexp_lookbehind; +@regexp_anchor = @regexp_dollar | @regexp_caret; + +is_greedy (int id: @regexp_quantifier ref); +range_quantifier_lower_bound (unique int id: @regexp_range ref, int lo: int ref); +range_quantifier_upper_bound (unique int id: @regexp_range ref, int hi: int ref); +is_capture (unique int id: @regexp_group ref, int number: int ref); +is_named_capture (unique int id: @regexp_group ref, string name: string ref); +is_inverted (int id: @regexp_char_class ref); +regexp_const_value (unique int id: @regexp_constant ref, varchar(1) value: string ref); +char_class_escape (unique int id: @regexp_char_class_escape ref, varchar(1) value: string ref); +backref (unique int id: @regexp_backref ref, int value: int ref); +named_backref (unique int id: @regexp_backref ref, string name: string ref); +unicode_property_escapename (unique int id: @regexp_unicode_property_escape ref, string name: string ref); +unicode_property_escapevalue (unique int id: @regexp_unicode_property_escape ref, string value: string ref); + +// tokens +#keyset[toplevel, idx] +tokeninfo (unique int id: @token, + int kind: int ref, + int toplevel: @toplevel ref, + int idx: int ref, + varchar(900) value: string ref); + +case @token.kind of + 0 = @token_eof +| 1 = @token_null_literal +| 2 = @token_boolean_literal +| 3 = @token_numeric_literal +| 4 = @token_string_literal +| 5 = @token_regular_expression +| 6 = @token_identifier +| 7 = @token_keyword +| 8 = @token_punctuator; + +// associate comments with the token immediately following them (which may be EOF) +next_token (int comment: @comment ref, int token: @token ref); + +// JSON +#keyset[parent, idx] +json (unique int id: @json_value, + int kind: int ref, + int parent: @json_parent ref, + int idx: int ref, + varchar(900) tostring: string ref); + +json_literals (varchar(900) value: string ref, + varchar(900) raw: string ref, + unique int expr: @json_value ref); + +json_properties (int obj: @json_object ref, + varchar(900) property: string ref, + int value: @json_value ref); + +json_errors (unique int id: @json_parse_error, + varchar(900) message: string ref); + +json_locations(unique int locatable: @json_locatable ref, + int location: @location_default ref); + +case @json_value.kind of + 0 = @json_null +| 1 = @json_boolean +| 2 = @json_number +| 3 = @json_string +| 4 = @json_array +| 5 = @json_object; + +@json_parent = @json_object | @json_array | @file; + +@json_locatable = @json_value | @json_parse_error; + +// locations +@ast_node = @toplevel | @stmt | @expr | @property | @typeexpr; + +@locatable = @file + | @ast_node + | @comment + | @line + | @js_parse_error | @regexp_parse_error + | @regexpterm + | @json_locatable + | @token + | @cfg_node + | @jsdoc | @jsdoc_type_expr | @jsdoc_tag + | @yaml_locatable + | @xmllocatable + | @configLocatable + | @template_placeholder_tag; + +hasLocation (unique int locatable: @locatable ref, + int location: @location ref); + +// CFG +entry_cfg_node (unique int id: @entry_node, int container: @stmt_container ref); +exit_cfg_node (unique int id: @exit_node, int container: @stmt_container ref); +guard_node (unique int id: @guard_node, int kind: int ref, int test: @expr ref); +case @guard_node.kind of + 0 = @falsy_guard +| 1 = @truthy_guard; +@condition_guard = @falsy_guard | @truthy_guard; + +@synthetic_cfg_node = @entry_node | @exit_node | @guard_node; +@cfg_node = @synthetic_cfg_node | @expr_parent; + +successor (int pred: @cfg_node ref, int succ: @cfg_node ref); + +// JSDoc comments +jsdoc (unique int id: @jsdoc, varchar(900) description: string ref, int comment: @comment ref); +#keyset[parent, idx] +jsdoc_tags (unique int id: @jsdoc_tag, varchar(900) title: string ref, + int parent: @jsdoc ref, int idx: int ref, varchar(900) tostring: string ref); +jsdoc_tag_descriptions (unique int tag: @jsdoc_tag ref, varchar(900) text: string ref); +jsdoc_tag_names (unique int tag: @jsdoc_tag ref, varchar(900) text: string ref); + +#keyset[parent, idx] +jsdoc_type_exprs (unique int id: @jsdoc_type_expr, + int kind: int ref, + int parent: @jsdoc_type_expr_parent ref, + int idx: int ref, + varchar(900) tostring: string ref); +case @jsdoc_type_expr.kind of + 0 = @jsdoc_any_type_expr +| 1 = @jsdoc_null_type_expr +| 2 = @jsdoc_undefined_type_expr +| 3 = @jsdoc_unknown_type_expr +| 4 = @jsdoc_void_type_expr +| 5 = @jsdoc_named_type_expr +| 6 = @jsdoc_applied_type_expr +| 7 = @jsdoc_nullable_type_expr +| 8 = @jsdoc_non_nullable_type_expr +| 9 = @jsdoc_record_type_expr +| 10 = @jsdoc_array_type_expr +| 11 = @jsdoc_union_type_expr +| 12 = @jsdoc_function_type_expr +| 13 = @jsdoc_optional_type_expr +| 14 = @jsdoc_rest_type_expr +; + +#keyset[id, idx] +jsdoc_record_field_name (int id: @jsdoc_record_type_expr ref, int idx: int ref, varchar(900) name: string ref); +jsdoc_prefix_qualifier (int id: @jsdoc_type_expr ref); +jsdoc_has_new_parameter (int fn: @jsdoc_function_type_expr ref); + +@jsdoc_type_expr_parent = @jsdoc_type_expr | @jsdoc_tag; + +jsdoc_errors (unique int id: @jsdoc_error, int tag: @jsdoc_tag ref, varchar(900) message: string ref, varchar(900) tostring: string ref); + +@dataflownode = @expr | @function_decl_stmt | @class_decl_stmt | @namespace_declaration | @enum_declaration | @property; + +@optionalchainable = @call_expr | @propaccess; + +isOptionalChaining(int id: @optionalchainable ref); + +/** + * The time taken for the extraction of a file. + * This table contains non-deterministic content. + * + * The sum of the `time` column for each (`file`, `timerKind`) pair + * is the total time taken for extraction of `file`. The `extractionPhase` + * column provides a granular view of the extraction time of the file. + */ +extraction_time( + int file : @file ref, + // see `com.semmle.js.extractor.ExtractionMetrics.ExtractionPhase`. + int extractionPhase: int ref, + // 0 for the elapsed CPU time in nanoseconds, 1 for the elapsed wallclock time in nanoseconds + int timerKind: int ref, + float time: float ref +) + +/** +* Non-timing related data for the extraction of a single file. +* This table contains non-deterministic content. +*/ +extraction_data( + int file : @file ref, + // the absolute path to the cache file + varchar(900) cacheFile: string ref, + boolean fromCache: boolean ref, + int length: int ref +) + +/*- YAML -*/ + +#keyset[parent, idx] +yaml (unique int id: @yaml_node, + int kind: int ref, + int parent: @yaml_node_parent ref, + int idx: int ref, + string tag: string ref, + string tostring: string ref); + +case @yaml_node.kind of + 0 = @yaml_scalar_node +| 1 = @yaml_mapping_node +| 2 = @yaml_sequence_node +| 3 = @yaml_alias_node +; + +@yaml_collection_node = @yaml_mapping_node | @yaml_sequence_node; + +@yaml_node_parent = @yaml_collection_node | @file; + +yaml_anchors (unique int node: @yaml_node ref, + string anchor: string ref); + +yaml_aliases (unique int alias: @yaml_alias_node ref, + string target: string ref); + +yaml_scalars (unique int scalar: @yaml_scalar_node ref, + int style: int ref, + string value: string ref); + +yaml_errors (unique int id: @yaml_error, + string message: string ref); + +yaml_locations(unique int locatable: @yaml_locatable ref, + int location: @location_default ref); + +@yaml_locatable = @yaml_node | @yaml_error; + +/*- XML Files -*/ + +xmlEncoding( + unique int id: @file ref, + string encoding: string ref +); + +xmlDTDs( + unique int id: @xmldtd, + string root: string ref, + string publicId: string ref, + string systemId: string ref, + int fileid: @file ref +); + +xmlElements( + unique int id: @xmlelement, + string name: string ref, + int parentid: @xmlparent ref, + int idx: int ref, + int fileid: @file ref +); + +xmlAttrs( + unique int id: @xmlattribute, + int elementid: @xmlelement ref, + string name: string ref, + string value: string ref, + int idx: int ref, + int fileid: @file ref +); + +xmlNs( + int id: @xmlnamespace, + string prefixName: string ref, + string URI: string ref, + int fileid: @file ref +); + +xmlHasNs( + int elementId: @xmlnamespaceable ref, + int nsId: @xmlnamespace ref, + int fileid: @file ref +); + +xmlComments( + unique int id: @xmlcomment, + string text: string ref, + int parentid: @xmlparent ref, + int fileid: @file ref +); + +xmlChars( + unique int id: @xmlcharacters, + string text: string ref, + int parentid: @xmlparent ref, + int idx: int ref, + int isCDATA: int ref, + int fileid: @file ref +); + +@xmlparent = @file | @xmlelement; +@xmlnamespaceable = @xmlelement | @xmlattribute; + +xmllocations( + int xmlElement: @xmllocatable ref, + int location: @location_default ref +); + +@xmllocatable = @xmlcharacters | @xmlelement | @xmlcomment | @xmlattribute | @xmldtd | @file | @xmlnamespace; + +/*- Configuration files with key value pairs -*/ + +configs( + unique int id: @config +); + +configNames( + unique int id: @configName, + int config: @config ref, + string name: string ref +); + +configValues( + unique int id: @configValue, + int config: @config ref, + string value: string ref +); + +configLocations( + int locatable: @configLocatable ref, + int location: @location_default ref +); + +@configLocatable = @config | @configName | @configValue; diff --git a/javascript/downgrades/eda7d3dc42baf69d0acb592072d7136acf61d903/upgrade.properties b/javascript/downgrades/eda7d3dc42baf69d0acb592072d7136acf61d903/upgrade.properties new file mode 100644 index 000000000000..07964edef857 --- /dev/null +++ b/javascript/downgrades/eda7d3dc42baf69d0acb592072d7136acf61d903/upgrade.properties @@ -0,0 +1,2 @@ +description: Adds support for ECMAScript 2024 v Flag Intersection +compatibility: backwards diff --git a/javascript/ql/lib/upgrades/c88c69174bd0dd4e95f1bcfbada68a2505e812c3/old.dbscheme b/javascript/ql/lib/upgrades/c88c69174bd0dd4e95f1bcfbada68a2505e812c3/old.dbscheme new file mode 100644 index 000000000000..c88c69174bd0 --- /dev/null +++ b/javascript/ql/lib/upgrades/c88c69174bd0dd4e95f1bcfbada68a2505e812c3/old.dbscheme @@ -0,0 +1,1190 @@ +/*** Standard fragments ***/ + +/*- Files and folders -*/ + +/** + * The location of an element. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `file`. + * For more information, see + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). + */ +locations_default( + unique int id: @location_default, + int file: @file ref, + int beginLine: int ref, + int beginColumn: int ref, + int endLine: int ref, + int endColumn: int ref +); + +files( + unique int id: @file, + string name: string ref +); + +folders( + unique int id: @folder, + string name: string ref +); + +@container = @file | @folder + +containerparent( + int parent: @container ref, + unique int child: @container ref +); + +/*- Lines of code -*/ + +numlines( + int element_id: @sourceline ref, + int num_lines: int ref, + int num_code: int ref, + int num_comment: int ref +); + +/*- External data -*/ + +/** + * External data, loaded from CSV files during snapshot creation. See + * [Tutorial: Incorporating external data](https://help.semmle.com/wiki/display/SD/Tutorial%3A+Incorporating+external+data) + * for more information. + */ +externalData( + int id : @externalDataElement, + string path : string ref, + int column: int ref, + string value : string ref +); + +/*- Source location prefix -*/ + +/** + * The source location of the snapshot. + */ +sourceLocationPrefix(string prefix : string ref); + +/*- JavaScript-specific part -*/ + +@location = @location_default + +@sourceline = @locatable; + +filetype( + int file: @file ref, + string filetype: string ref +) + +// top-level code fragments +toplevels (unique int id: @toplevel, + int kind: int ref); + +is_externs (int toplevel: @toplevel ref); + +case @toplevel.kind of + 0 = @script +| 1 = @inline_script +| 2 = @event_handler +| 3 = @javascript_url +| 4 = @template_toplevel; + +is_module (int tl: @toplevel ref); +is_nodejs (int tl: @toplevel ref); +is_es2015_module (int tl: @toplevel ref); +is_closure_module (int tl: @toplevel ref); + +@xml_node_with_code = @xmlelement | @xmlattribute | @template_placeholder_tag; +toplevel_parent_xml_node( + unique int toplevel: @toplevel ref, + int xmlnode: @xml_node_with_code ref); + +xml_element_parent_expression( + unique int xmlnode: @xmlelement ref, + int expression: @expr ref, + int index: int ref); + +// statements +#keyset[parent, idx] +stmts (unique int id: @stmt, + int kind: int ref, + int parent: @stmt_parent ref, + int idx: int ref, + varchar(900) tostring: string ref); + +stmt_containers (unique int stmt: @stmt ref, + int container: @stmt_container ref); + +jump_targets (unique int jump: @stmt ref, + int target: @stmt ref); + +@stmt_parent = @stmt | @toplevel | @function_expr | @arrow_function_expr | @static_initializer; +@stmt_container = @toplevel | @function | @namespace_declaration | @external_module_declaration | @global_augmentation_declaration; + +case @stmt.kind of + 0 = @empty_stmt +| 1 = @block_stmt +| 2 = @expr_stmt +| 3 = @if_stmt +| 4 = @labeled_stmt +| 5 = @break_stmt +| 6 = @continue_stmt +| 7 = @with_stmt +| 8 = @switch_stmt +| 9 = @return_stmt +| 10 = @throw_stmt +| 11 = @try_stmt +| 12 = @while_stmt +| 13 = @do_while_stmt +| 14 = @for_stmt +| 15 = @for_in_stmt +| 16 = @debugger_stmt +| 17 = @function_decl_stmt +| 18 = @var_decl_stmt +| 19 = @case +| 20 = @catch_clause +| 21 = @for_of_stmt +| 22 = @const_decl_stmt +| 23 = @let_stmt +| 24 = @legacy_let_stmt +| 25 = @for_each_stmt +| 26 = @class_decl_stmt +| 27 = @import_declaration +| 28 = @export_all_declaration +| 29 = @export_default_declaration +| 30 = @export_named_declaration +| 31 = @namespace_declaration +| 32 = @import_equals_declaration +| 33 = @export_assign_declaration +| 34 = @interface_declaration +| 35 = @type_alias_declaration +| 36 = @enum_declaration +| 37 = @external_module_declaration +| 38 = @export_as_namespace_declaration +| 39 = @global_augmentation_declaration +| 40 = @using_decl_stmt +; + +@decl_stmt = @var_decl_stmt | @const_decl_stmt | @let_stmt | @legacy_let_stmt | @using_decl_stmt; + +@export_declaration = @export_all_declaration | @export_default_declaration | @export_named_declaration; + +@namespace_definition = @namespace_declaration | @enum_declaration; +@type_definition = @class_definition | @interface_declaration | @enum_declaration | @type_alias_declaration | @enum_member; + +is_instantiated(unique int decl: @namespace_declaration ref); + +@declarable_node = @decl_stmt | @namespace_declaration | @class_decl_stmt | @function_decl_stmt | @enum_declaration | @external_module_declaration | @global_augmentation_declaration | @field; +has_declare_keyword(unique int stmt: @declarable_node ref); + +is_for_await_of(unique int forof: @for_of_stmt ref); + +// expressions +#keyset[parent, idx] +exprs (unique int id: @expr, + int kind: int ref, + int parent: @expr_parent ref, + int idx: int ref, + varchar(900) tostring: string ref); + +literals (varchar(900) value: string ref, + varchar(900) raw: string ref, + unique int expr: @expr_or_type ref); + +enclosing_stmt (unique int expr: @expr_or_type ref, + int stmt: @stmt ref); + +expr_containers (unique int expr: @expr_or_type ref, + int container: @stmt_container ref); + +array_size (unique int ae: @arraylike ref, + int sz: int ref); + +is_delegating (int yield: @yield_expr ref); + +@expr_or_stmt = @expr | @stmt; +@expr_or_type = @expr | @typeexpr; +@expr_parent = @expr_or_stmt | @property | @function_typeexpr; +@arraylike = @array_expr | @array_pattern; +@type_annotation = @typeexpr | @jsdoc_type_expr; +@node_in_stmt_container = @cfg_node | @type_annotation | @toplevel; + +case @expr.kind of + 0 = @label +| 1 = @null_literal +| 2 = @boolean_literal +| 3 = @number_literal +| 4 = @string_literal +| 5 = @regexp_literal +| 6 = @this_expr +| 7 = @array_expr +| 8 = @obj_expr +| 9 = @function_expr +| 10 = @seq_expr +| 11 = @conditional_expr +| 12 = @new_expr +| 13 = @call_expr +| 14 = @dot_expr +| 15 = @index_expr +| 16 = @neg_expr +| 17 = @plus_expr +| 18 = @log_not_expr +| 19 = @bit_not_expr +| 20 = @typeof_expr +| 21 = @void_expr +| 22 = @delete_expr +| 23 = @eq_expr +| 24 = @neq_expr +| 25 = @eqq_expr +| 26 = @neqq_expr +| 27 = @lt_expr +| 28 = @le_expr +| 29 = @gt_expr +| 30 = @ge_expr +| 31 = @lshift_expr +| 32 = @rshift_expr +| 33 = @urshift_expr +| 34 = @add_expr +| 35 = @sub_expr +| 36 = @mul_expr +| 37 = @div_expr +| 38 = @mod_expr +| 39 = @bitor_expr +| 40 = @xor_expr +| 41 = @bitand_expr +| 42 = @in_expr +| 43 = @instanceof_expr +| 44 = @logand_expr +| 45 = @logor_expr +| 47 = @assign_expr +| 48 = @assign_add_expr +| 49 = @assign_sub_expr +| 50 = @assign_mul_expr +| 51 = @assign_div_expr +| 52 = @assign_mod_expr +| 53 = @assign_lshift_expr +| 54 = @assign_rshift_expr +| 55 = @assign_urshift_expr +| 56 = @assign_or_expr +| 57 = @assign_xor_expr +| 58 = @assign_and_expr +| 59 = @preinc_expr +| 60 = @postinc_expr +| 61 = @predec_expr +| 62 = @postdec_expr +| 63 = @par_expr +| 64 = @var_declarator +| 65 = @arrow_function_expr +| 66 = @spread_element +| 67 = @array_pattern +| 68 = @object_pattern +| 69 = @yield_expr +| 70 = @tagged_template_expr +| 71 = @template_literal +| 72 = @template_element +| 73 = @array_comprehension_expr +| 74 = @generator_expr +| 75 = @for_in_comprehension_block +| 76 = @for_of_comprehension_block +| 77 = @legacy_letexpr +| 78 = @var_decl +| 79 = @proper_varaccess +| 80 = @class_expr +| 81 = @super_expr +| 82 = @newtarget_expr +| 83 = @named_import_specifier +| 84 = @import_default_specifier +| 85 = @import_namespace_specifier +| 86 = @named_export_specifier +| 87 = @exp_expr +| 88 = @assign_exp_expr +| 89 = @jsx_element +| 90 = @jsx_qualified_name +| 91 = @jsx_empty_expr +| 92 = @await_expr +| 93 = @function_sent_expr +| 94 = @decorator +| 95 = @export_default_specifier +| 96 = @export_namespace_specifier +| 97 = @bind_expr +| 98 = @external_module_reference +| 99 = @dynamic_import +| 100 = @expression_with_type_arguments +| 101 = @prefix_type_assertion +| 102 = @as_type_assertion +| 103 = @export_varaccess +| 104 = @decorator_list +| 105 = @non_null_assertion +| 106 = @bigint_literal +| 107 = @nullishcoalescing_expr +| 108 = @e4x_xml_anyname +| 109 = @e4x_xml_static_attribute_selector +| 110 = @e4x_xml_dynamic_attribute_selector +| 111 = @e4x_xml_filter_expression +| 112 = @e4x_xml_static_qualident +| 113 = @e4x_xml_dynamic_qualident +| 114 = @e4x_xml_dotdotexpr +| 115 = @import_meta_expr +| 116 = @assignlogandexpr +| 117 = @assignlogorexpr +| 118 = @assignnullishcoalescingexpr +| 119 = @template_pipe_ref +| 120 = @generated_code_expr +| 121 = @satisfies_expr +; + +@varaccess = @proper_varaccess | @export_varaccess; +@varref = @var_decl | @varaccess; + +@identifier = @label | @varref | @type_identifier; + +@literal = @null_literal | @boolean_literal | @number_literal | @string_literal | @regexp_literal | @bigint_literal; + +@propaccess = @dot_expr | @index_expr; + +@invokeexpr = @new_expr | @call_expr; + +@unaryexpr = @neg_expr | @plus_expr | @log_not_expr | @bit_not_expr | @typeof_expr | @void_expr | @delete_expr | @spread_element; + +@equality_test = @eq_expr | @neq_expr | @eqq_expr | @neqq_expr; + +@comparison = @equality_test | @lt_expr | @le_expr | @gt_expr | @ge_expr; + +@binaryexpr = @comparison | @lshift_expr | @rshift_expr | @urshift_expr | @add_expr | @sub_expr | @mul_expr | @div_expr | @mod_expr | @exp_expr | @bitor_expr | @xor_expr | @bitand_expr | @in_expr | @instanceof_expr | @logand_expr | @logor_expr | @nullishcoalescing_expr; + +@assignment = @assign_expr | @assign_add_expr | @assign_sub_expr | @assign_mul_expr | @assign_div_expr | @assign_mod_expr | @assign_exp_expr | @assign_lshift_expr | @assign_rshift_expr | @assign_urshift_expr | @assign_or_expr | @assign_xor_expr | @assign_and_expr | @assignlogandexpr | @assignlogorexpr | @assignnullishcoalescingexpr; + +@updateexpr = @preinc_expr | @postinc_expr | @predec_expr | @postdec_expr; + +@pattern = @varref | @array_pattern | @object_pattern; + +@comprehension_expr = @array_comprehension_expr | @generator_expr; + +@comprehension_block = @for_in_comprehension_block | @for_of_comprehension_block; + +@import_specifier = @named_import_specifier | @import_default_specifier | @import_namespace_specifier; + +@exportspecifier = @named_export_specifier | @export_default_specifier | @export_namespace_specifier; + +@type_keyword_operand = @import_declaration | @export_declaration | @import_specifier; + +@type_assertion = @as_type_assertion | @prefix_type_assertion; + +@class_definition = @class_decl_stmt | @class_expr; +@interface_definition = @interface_declaration | @interface_typeexpr; +@class_or_interface = @class_definition | @interface_definition; + +@lexical_decl = @var_decl | @type_decl; +@lexical_access = @varaccess | @local_type_access | @local_var_type_access | @local_namespace_access; +@lexical_ref = @lexical_decl | @lexical_access; + +@e4x_xml_attribute_selector = @e4x_xml_static_attribute_selector | @e4x_xml_dynamic_attribute_selector; +@e4x_xml_qualident = @e4x_xml_static_qualident | @e4x_xml_dynamic_qualident; + +expr_contains_template_tag_location( + int expr: @expr ref, + int location: @location ref +); + +@template_placeholder_tag_parent = @xmlelement | @xmlattribute | @file; + +template_placeholder_tag_info( + unique int node: @template_placeholder_tag, + int parentNode: @template_placeholder_tag_parent ref, + varchar(900) raw: string ref +); + +// scopes +scopes (unique int id: @scope, + int kind: int ref); + +case @scope.kind of + 0 = @global_scope +| 1 = @function_scope +| 2 = @catch_scope +| 3 = @module_scope +| 4 = @block_scope +| 5 = @for_scope +| 6 = @for_in_scope // for-of scopes work the same as for-in scopes +| 7 = @comprehension_block_scope +| 8 = @class_expr_scope +| 9 = @namespace_scope +| 10 = @class_decl_scope +| 11 = @interface_scope +| 12 = @type_alias_scope +| 13 = @mapped_type_scope +| 14 = @enum_scope +| 15 = @external_module_scope +| 16 = @conditional_type_scope; + +scopenodes (unique int node: @ast_node ref, + int scope: @scope ref); + +scopenesting (unique int inner: @scope ref, + int outer: @scope ref); + +// functions +@function = @function_decl_stmt | @function_expr | @arrow_function_expr; + +@parameterized = @function | @catch_clause; +@type_parameterized = @function | @class_or_interface | @type_alias_declaration | @mapped_typeexpr | @infer_typeexpr; + +is_generator (int fun: @function ref); +has_rest_parameter (int fun: @function ref); +is_async (int fun: @function ref); + +// variables and lexically scoped type names +#keyset[scope, name] +variables (unique int id: @variable, + varchar(900) name: string ref, + int scope: @scope ref); + +#keyset[scope, name] +local_type_names (unique int id: @local_type_name, + varchar(900) name: string ref, + int scope: @scope ref); + +#keyset[scope, name] +local_namespace_names (unique int id: @local_namespace_name, + varchar(900) name: string ref, + int scope: @scope ref); + +is_arguments_object (int id: @variable ref); + +@lexical_name = @variable | @local_type_name | @local_namespace_name; + +@bind_id = @varaccess | @local_var_type_access; +bind (unique int id: @bind_id ref, + int decl: @variable ref); + +decl (unique int id: @var_decl ref, + int decl: @variable ref); + +@typebind_id = @local_type_access | @export_varaccess; +typebind (unique int id: @typebind_id ref, + int decl: @local_type_name ref); + +@typedecl_id = @type_decl | @var_decl; +typedecl (unique int id: @typedecl_id ref, + int decl: @local_type_name ref); + +namespacedecl (unique int id: @var_decl ref, + int decl: @local_namespace_name ref); + +@namespacebind_id = @local_namespace_access | @export_varaccess; +namespacebind (unique int id: @namespacebind_id ref, + int decl: @local_namespace_name ref); + + +// properties in object literals, property patterns in object patterns, and method declarations in classes +#keyset[parent, index] +properties (unique int id: @property, + int parent: @property_parent ref, + int index: int ref, + int kind: int ref, + varchar(900) tostring: string ref); + +case @property.kind of + 0 = @value_property +| 1 = @property_getter +| 2 = @property_setter +| 3 = @jsx_attribute +| 4 = @function_call_signature +| 5 = @constructor_call_signature +| 6 = @index_signature +| 7 = @enum_member +| 8 = @proper_field +| 9 = @parameter_field +| 10 = @static_initializer +; + +@property_parent = @obj_expr | @object_pattern | @class_definition | @jsx_element | @interface_definition | @enum_declaration; +@property_accessor = @property_getter | @property_setter; +@call_signature = @function_call_signature | @constructor_call_signature; +@field = @proper_field | @parameter_field; +@field_or_vardeclarator = @field | @var_declarator; + +is_computed (int id: @property ref); +is_method (int id: @property ref); +is_static (int id: @property ref); +is_abstract_member (int id: @property ref); +is_const_enum (int id: @enum_declaration ref); +is_abstract_class (int id: @class_decl_stmt ref); + +has_public_keyword (int id: @property ref); +has_private_keyword (int id: @property ref); +has_protected_keyword (int id: @property ref); +has_readonly_keyword (int id: @property ref); +has_type_keyword (int id: @type_keyword_operand ref); +is_optional_member (int id: @property ref); +has_definite_assignment_assertion (int id: @field_or_vardeclarator ref); +is_optional_parameter_declaration (unique int parameter: @pattern ref); + +#keyset[constructor, param_index] +parameter_fields( + unique int field: @parameter_field ref, + int constructor: @function_expr ref, + int param_index: int ref +); + +// types +#keyset[parent, idx] +typeexprs ( + unique int id: @typeexpr, + int kind: int ref, + int parent: @typeexpr_parent ref, + int idx: int ref, + varchar(900) tostring: string ref +); + +case @typeexpr.kind of + 0 = @local_type_access +| 1 = @type_decl +| 2 = @keyword_typeexpr +| 3 = @string_literal_typeexpr +| 4 = @number_literal_typeexpr +| 5 = @boolean_literal_typeexpr +| 6 = @array_typeexpr +| 7 = @union_typeexpr +| 8 = @indexed_access_typeexpr +| 9 = @intersection_typeexpr +| 10 = @parenthesized_typeexpr +| 11 = @tuple_typeexpr +| 12 = @keyof_typeexpr +| 13 = @qualified_type_access +| 14 = @generic_typeexpr +| 15 = @type_label +| 16 = @typeof_typeexpr +| 17 = @local_var_type_access +| 18 = @qualified_var_type_access +| 19 = @this_var_type_access +| 20 = @predicate_typeexpr +| 21 = @interface_typeexpr +| 22 = @type_parameter +| 23 = @plain_function_typeexpr +| 24 = @constructor_typeexpr +| 25 = @local_namespace_access +| 26 = @qualified_namespace_access +| 27 = @mapped_typeexpr +| 28 = @conditional_typeexpr +| 29 = @infer_typeexpr +| 30 = @import_type_access +| 31 = @import_namespace_access +| 32 = @import_var_type_access +| 33 = @optional_typeexpr +| 34 = @rest_typeexpr +| 35 = @bigint_literal_typeexpr +| 36 = @readonly_typeexpr +| 37 = @template_literal_typeexpr +; + +@typeref = @typeaccess | @type_decl; +@type_identifier = @type_decl | @local_type_access | @type_label | @local_var_type_access | @local_namespace_access; +@typeexpr_parent = @expr | @stmt | @property | @typeexpr; +@literal_typeexpr = @string_literal_typeexpr | @number_literal_typeexpr | @boolean_literal_typeexpr | @bigint_literal_typeexpr; +@typeaccess = @local_type_access | @qualified_type_access | @import_type_access; +@vartypeaccess = @local_var_type_access | @qualified_var_type_access | @this_var_type_access | @import_var_type_access; +@namespace_access = @local_namespace_access | @qualified_namespace_access | @import_namespace_access; +@import_typeexpr = @import_type_access | @import_namespace_access | @import_var_type_access; + +@function_typeexpr = @plain_function_typeexpr | @constructor_typeexpr; + +// types +types ( + unique int id: @type, + int kind: int ref, + varchar(900) tostring: string ref +); + +#keyset[parent, idx] +type_child ( + int child: @type ref, + int parent: @type ref, + int idx: int ref +); + +case @type.kind of + 0 = @any_type +| 1 = @string_type +| 2 = @number_type +| 3 = @union_type +| 4 = @true_type +| 5 = @false_type +| 6 = @type_reference +| 7 = @object_type +| 8 = @canonical_type_variable_type +| 9 = @typeof_type +| 10 = @void_type +| 11 = @undefined_type +| 12 = @null_type +| 13 = @never_type +| 14 = @plain_symbol_type +| 15 = @unique_symbol_type +| 16 = @objectkeyword_type +| 17 = @intersection_type +| 18 = @tuple_type +| 19 = @lexical_type_variable_type +| 20 = @this_type +| 21 = @number_literal_type +| 22 = @string_literal_type +| 23 = @unknown_type +| 24 = @bigint_type +| 25 = @bigint_literal_type +; + +@boolean_literal_type = @true_type | @false_type; +@symbol_type = @plain_symbol_type | @unique_symbol_type; +@union_or_intersection_type = @union_type | @intersection_type; +@typevariable_type = @canonical_type_variable_type | @lexical_type_variable_type; + +has_asserts_keyword(int node: @predicate_typeexpr ref); + +@typed_ast_node = @expr | @typeexpr | @function; +ast_node_type( + unique int node: @typed_ast_node ref, + int typ: @type ref); + +declared_function_signature( + unique int node: @function ref, + int sig: @signature_type ref +); + +invoke_expr_signature( + unique int node: @invokeexpr ref, + int sig: @signature_type ref +); + +invoke_expr_overload_index( + unique int node: @invokeexpr ref, + int index: int ref +); + +symbols ( + unique int id: @symbol, + int kind: int ref, + varchar(900) name: string ref +); + +symbol_parent ( + unique int symbol: @symbol ref, + int parent: @symbol ref +); + +symbol_module ( + int symbol: @symbol ref, + varchar(900) moduleName: string ref +); + +symbol_global ( + int symbol: @symbol ref, + varchar(900) globalName: string ref +); + +case @symbol.kind of + 0 = @root_symbol +| 1 = @member_symbol +| 2 = @other_symbol +; + +@type_with_symbol = @type_reference | @typevariable_type | @typeof_type | @unique_symbol_type; +@ast_node_with_symbol = @type_definition | @namespace_definition | @toplevel | @typeaccess | @namespace_access | @var_decl | @function | @invokeexpr | @import_declaration | @external_module_reference | @external_module_declaration; + +ast_node_symbol( + unique int node: @ast_node_with_symbol ref, + int symbol: @symbol ref); + +type_symbol( + unique int typ: @type_with_symbol ref, + int symbol: @symbol ref); + +#keyset[typ, name] +type_property( + int typ: @type ref, + varchar(900) name: string ref, + int propertyType: @type ref); + +type_alias( + unique int aliasType: @type ref, + int underlyingType: @type ref); + +@literal_type = @string_literal_type | @number_literal_type | @boolean_literal_type | @bigint_literal_type; +@type_with_literal_value = @string_literal_type | @number_literal_type | @bigint_literal_type; +type_literal_value( + unique int typ: @type_with_literal_value ref, + varchar(900) value: string ref); + +signature_types ( + unique int id: @signature_type, + int kind: int ref, + varchar(900) tostring: string ref, + int type_parameters: int ref, + int required_params: int ref +); + +is_abstract_signature( + unique int sig: @signature_type ref +); + +signature_rest_parameter( + unique int sig: @signature_type ref, + int rest_param_arra_type: @type ref +); + +case @signature_type.kind of + 0 = @function_signature_type +| 1 = @constructor_signature_type +; + +#keyset[typ, kind, index] +type_contains_signature ( + int typ: @type ref, + int kind: int ref, // constructor/call/index + int index: int ref, // ordering of overloaded signatures + int sig: @signature_type ref +); + +#keyset[parent, index] +signature_contains_type ( + int child: @type ref, + int parent: @signature_type ref, + int index: int ref +); + +#keyset[sig, index] +signature_parameter_name ( + int sig: @signature_type ref, + int index: int ref, + varchar(900) name: string ref +); + +number_index_type ( + unique int baseType: @type ref, + int propertyType: @type ref +); + +string_index_type ( + unique int baseType: @type ref, + int propertyType: @type ref +); + +base_type_names( + int typeName: @symbol ref, + int baseTypeName: @symbol ref +); + +self_types( + int typeName: @symbol ref, + int selfType: @type_reference ref +); + +tuple_type_min_length( + unique int typ: @type ref, + int minLength: int ref +); + +tuple_type_rest_index( + unique int typ: @type ref, + int index: int ref +); + +// comments +comments (unique int id: @comment, + int kind: int ref, + int toplevel: @toplevel ref, + varchar(900) text: string ref, + varchar(900) tostring: string ref); + +case @comment.kind of + 0 = @slashslash_comment +| 1 = @slashstar_comment +| 2 = @doc_comment +| 3 = @html_comment_start +| 4 = @htmlcommentend; + +@html_comment = @html_comment_start | @htmlcommentend; +@line_comment = @slashslash_comment | @html_comment; +@block_comment = @slashstar_comment | @doc_comment; + +// source lines +lines (unique int id: @line, + int toplevel: @toplevel ref, + varchar(900) text: string ref, + varchar(2) terminator: string ref); +indentation (int file: @file ref, + int lineno: int ref, + varchar(1) indentChar: string ref, + int indentDepth: int ref); + +// JavaScript parse errors +js_parse_errors (unique int id: @js_parse_error, + int toplevel: @toplevel ref, + varchar(900) message: string ref, + varchar(900) line: string ref); + +// regular expressions +#keyset[parent, idx] +regexpterm (unique int id: @regexpterm, + int kind: int ref, + int parent: @regexpparent ref, + int idx: int ref, + varchar(900) tostring: string ref); + +@regexpparent = @regexpterm | @regexp_literal | @string_literal | @add_expr; + +case @regexpterm.kind of + 0 = @regexp_alt +| 1 = @regexp_seq +| 2 = @regexp_caret +| 3 = @regexp_dollar +| 4 = @regexp_wordboundary +| 5 = @regexp_nonwordboundary +| 6 = @regexp_positive_lookahead +| 7 = @regexp_negative_lookahead +| 8 = @regexp_star +| 9 = @regexp_plus +| 10 = @regexp_opt +| 11 = @regexp_range +| 12 = @regexp_dot +| 13 = @regexp_group +| 14 = @regexp_normal_constant +| 15 = @regexp_hex_escape +| 16 = @regexp_unicode_escape +| 17 = @regexp_dec_escape +| 18 = @regexp_oct_escape +| 19 = @regexp_ctrl_escape +| 20 = @regexp_char_class_escape +| 21 = @regexp_id_escape +| 22 = @regexp_backref +| 23 = @regexp_char_class +| 24 = @regexp_char_range +| 25 = @regexp_positive_lookbehind +| 26 = @regexp_negative_lookbehind +| 27 = @regexp_unicode_property_escape; + +regexp_parse_errors (unique int id: @regexp_parse_error, + int regexp: @regexpterm ref, + varchar(900) message: string ref); + +@regexp_quantifier = @regexp_star | @regexp_plus | @regexp_opt | @regexp_range; +@regexp_escape = @regexp_char_escape | @regexp_char_class_escape | @regexp_unicode_property_escape; +@regexp_char_escape = @regexp_hex_escape | @regexp_unicode_escape | @regexp_dec_escape | @regexp_oct_escape | @regexp_ctrl_escape | @regexp_id_escape; +@regexp_constant = @regexp_normal_constant | @regexp_char_escape; +@regexp_lookahead = @regexp_positive_lookahead | @regexp_negative_lookahead; +@regexp_lookbehind = @regexp_positive_lookbehind | @regexp_negative_lookbehind; +@regexp_subpattern = @regexp_lookahead | @regexp_lookbehind; +@regexp_anchor = @regexp_dollar | @regexp_caret; + +is_greedy (int id: @regexp_quantifier ref); +range_quantifier_lower_bound (unique int id: @regexp_range ref, int lo: int ref); +range_quantifier_upper_bound (unique int id: @regexp_range ref, int hi: int ref); +is_capture (unique int id: @regexp_group ref, int number: int ref); +is_named_capture (unique int id: @regexp_group ref, string name: string ref); +is_inverted (int id: @regexp_char_class ref); +regexp_const_value (unique int id: @regexp_constant ref, varchar(1) value: string ref); +char_class_escape (unique int id: @regexp_char_class_escape ref, varchar(1) value: string ref); +backref (unique int id: @regexp_backref ref, int value: int ref); +named_backref (unique int id: @regexp_backref ref, string name: string ref); +unicode_property_escapename (unique int id: @regexp_unicode_property_escape ref, string name: string ref); +unicode_property_escapevalue (unique int id: @regexp_unicode_property_escape ref, string value: string ref); + +// tokens +#keyset[toplevel, idx] +tokeninfo (unique int id: @token, + int kind: int ref, + int toplevel: @toplevel ref, + int idx: int ref, + varchar(900) value: string ref); + +case @token.kind of + 0 = @token_eof +| 1 = @token_null_literal +| 2 = @token_boolean_literal +| 3 = @token_numeric_literal +| 4 = @token_string_literal +| 5 = @token_regular_expression +| 6 = @token_identifier +| 7 = @token_keyword +| 8 = @token_punctuator; + +// associate comments with the token immediately following them (which may be EOF) +next_token (int comment: @comment ref, int token: @token ref); + +// JSON +#keyset[parent, idx] +json (unique int id: @json_value, + int kind: int ref, + int parent: @json_parent ref, + int idx: int ref, + varchar(900) tostring: string ref); + +json_literals (varchar(900) value: string ref, + varchar(900) raw: string ref, + unique int expr: @json_value ref); + +json_properties (int obj: @json_object ref, + varchar(900) property: string ref, + int value: @json_value ref); + +json_errors (unique int id: @json_parse_error, + varchar(900) message: string ref); + +json_locations(unique int locatable: @json_locatable ref, + int location: @location_default ref); + +case @json_value.kind of + 0 = @json_null +| 1 = @json_boolean +| 2 = @json_number +| 3 = @json_string +| 4 = @json_array +| 5 = @json_object; + +@json_parent = @json_object | @json_array | @file; + +@json_locatable = @json_value | @json_parse_error; + +// locations +@ast_node = @toplevel | @stmt | @expr | @property | @typeexpr; + +@locatable = @file + | @ast_node + | @comment + | @line + | @js_parse_error | @regexp_parse_error + | @regexpterm + | @json_locatable + | @token + | @cfg_node + | @jsdoc | @jsdoc_type_expr | @jsdoc_tag + | @yaml_locatable + | @xmllocatable + | @configLocatable + | @template_placeholder_tag; + +hasLocation (unique int locatable: @locatable ref, + int location: @location ref); + +// CFG +entry_cfg_node (unique int id: @entry_node, int container: @stmt_container ref); +exit_cfg_node (unique int id: @exit_node, int container: @stmt_container ref); +guard_node (unique int id: @guard_node, int kind: int ref, int test: @expr ref); +case @guard_node.kind of + 0 = @falsy_guard +| 1 = @truthy_guard; +@condition_guard = @falsy_guard | @truthy_guard; + +@synthetic_cfg_node = @entry_node | @exit_node | @guard_node; +@cfg_node = @synthetic_cfg_node | @expr_parent; + +successor (int pred: @cfg_node ref, int succ: @cfg_node ref); + +// JSDoc comments +jsdoc (unique int id: @jsdoc, varchar(900) description: string ref, int comment: @comment ref); +#keyset[parent, idx] +jsdoc_tags (unique int id: @jsdoc_tag, varchar(900) title: string ref, + int parent: @jsdoc ref, int idx: int ref, varchar(900) tostring: string ref); +jsdoc_tag_descriptions (unique int tag: @jsdoc_tag ref, varchar(900) text: string ref); +jsdoc_tag_names (unique int tag: @jsdoc_tag ref, varchar(900) text: string ref); + +#keyset[parent, idx] +jsdoc_type_exprs (unique int id: @jsdoc_type_expr, + int kind: int ref, + int parent: @jsdoc_type_expr_parent ref, + int idx: int ref, + varchar(900) tostring: string ref); +case @jsdoc_type_expr.kind of + 0 = @jsdoc_any_type_expr +| 1 = @jsdoc_null_type_expr +| 2 = @jsdoc_undefined_type_expr +| 3 = @jsdoc_unknown_type_expr +| 4 = @jsdoc_void_type_expr +| 5 = @jsdoc_named_type_expr +| 6 = @jsdoc_applied_type_expr +| 7 = @jsdoc_nullable_type_expr +| 8 = @jsdoc_non_nullable_type_expr +| 9 = @jsdoc_record_type_expr +| 10 = @jsdoc_array_type_expr +| 11 = @jsdoc_union_type_expr +| 12 = @jsdoc_function_type_expr +| 13 = @jsdoc_optional_type_expr +| 14 = @jsdoc_rest_type_expr +; + +#keyset[id, idx] +jsdoc_record_field_name (int id: @jsdoc_record_type_expr ref, int idx: int ref, varchar(900) name: string ref); +jsdoc_prefix_qualifier (int id: @jsdoc_type_expr ref); +jsdoc_has_new_parameter (int fn: @jsdoc_function_type_expr ref); + +@jsdoc_type_expr_parent = @jsdoc_type_expr | @jsdoc_tag; + +jsdoc_errors (unique int id: @jsdoc_error, int tag: @jsdoc_tag ref, varchar(900) message: string ref, varchar(900) tostring: string ref); + +@dataflownode = @expr | @function_decl_stmt | @class_decl_stmt | @namespace_declaration | @enum_declaration | @property; + +@optionalchainable = @call_expr | @propaccess; + +isOptionalChaining(int id: @optionalchainable ref); + +/** + * The time taken for the extraction of a file. + * This table contains non-deterministic content. + * + * The sum of the `time` column for each (`file`, `timerKind`) pair + * is the total time taken for extraction of `file`. The `extractionPhase` + * column provides a granular view of the extraction time of the file. + */ +extraction_time( + int file : @file ref, + // see `com.semmle.js.extractor.ExtractionMetrics.ExtractionPhase`. + int extractionPhase: int ref, + // 0 for the elapsed CPU time in nanoseconds, 1 for the elapsed wallclock time in nanoseconds + int timerKind: int ref, + float time: float ref +) + +/** +* Non-timing related data for the extraction of a single file. +* This table contains non-deterministic content. +*/ +extraction_data( + int file : @file ref, + // the absolute path to the cache file + varchar(900) cacheFile: string ref, + boolean fromCache: boolean ref, + int length: int ref +) + +/*- YAML -*/ + +#keyset[parent, idx] +yaml (unique int id: @yaml_node, + int kind: int ref, + int parent: @yaml_node_parent ref, + int idx: int ref, + string tag: string ref, + string tostring: string ref); + +case @yaml_node.kind of + 0 = @yaml_scalar_node +| 1 = @yaml_mapping_node +| 2 = @yaml_sequence_node +| 3 = @yaml_alias_node +; + +@yaml_collection_node = @yaml_mapping_node | @yaml_sequence_node; + +@yaml_node_parent = @yaml_collection_node | @file; + +yaml_anchors (unique int node: @yaml_node ref, + string anchor: string ref); + +yaml_aliases (unique int alias: @yaml_alias_node ref, + string target: string ref); + +yaml_scalars (unique int scalar: @yaml_scalar_node ref, + int style: int ref, + string value: string ref); + +yaml_errors (unique int id: @yaml_error, + string message: string ref); + +yaml_locations(unique int locatable: @yaml_locatable ref, + int location: @location_default ref); + +@yaml_locatable = @yaml_node | @yaml_error; + +/*- XML Files -*/ + +xmlEncoding( + unique int id: @file ref, + string encoding: string ref +); + +xmlDTDs( + unique int id: @xmldtd, + string root: string ref, + string publicId: string ref, + string systemId: string ref, + int fileid: @file ref +); + +xmlElements( + unique int id: @xmlelement, + string name: string ref, + int parentid: @xmlparent ref, + int idx: int ref, + int fileid: @file ref +); + +xmlAttrs( + unique int id: @xmlattribute, + int elementid: @xmlelement ref, + string name: string ref, + string value: string ref, + int idx: int ref, + int fileid: @file ref +); + +xmlNs( + int id: @xmlnamespace, + string prefixName: string ref, + string URI: string ref, + int fileid: @file ref +); + +xmlHasNs( + int elementId: @xmlnamespaceable ref, + int nsId: @xmlnamespace ref, + int fileid: @file ref +); + +xmlComments( + unique int id: @xmlcomment, + string text: string ref, + int parentid: @xmlparent ref, + int fileid: @file ref +); + +xmlChars( + unique int id: @xmlcharacters, + string text: string ref, + int parentid: @xmlparent ref, + int idx: int ref, + int isCDATA: int ref, + int fileid: @file ref +); + +@xmlparent = @file | @xmlelement; +@xmlnamespaceable = @xmlelement | @xmlattribute; + +xmllocations( + int xmlElement: @xmllocatable ref, + int location: @location_default ref +); + +@xmllocatable = @xmlcharacters | @xmlelement | @xmlcomment | @xmlattribute | @xmldtd | @file | @xmlnamespace; + +/*- Configuration files with key value pairs -*/ + +configs( + unique int id: @config +); + +configNames( + unique int id: @configName, + int config: @config ref, + string name: string ref +); + +configValues( + unique int id: @configValue, + int config: @config ref, + string value: string ref +); + +configLocations( + int locatable: @configLocatable ref, + int location: @location_default ref +); + +@configLocatable = @config | @configName | @configValue; diff --git a/javascript/ql/lib/upgrades/c88c69174bd0dd4e95f1bcfbada68a2505e812c3/semmlecode.javascript.dbscheme b/javascript/ql/lib/upgrades/c88c69174bd0dd4e95f1bcfbada68a2505e812c3/semmlecode.javascript.dbscheme new file mode 100644 index 000000000000..eda7d3dc42ba --- /dev/null +++ b/javascript/ql/lib/upgrades/c88c69174bd0dd4e95f1bcfbada68a2505e812c3/semmlecode.javascript.dbscheme @@ -0,0 +1,1191 @@ +/*** Standard fragments ***/ + +/*- Files and folders -*/ + +/** + * The location of an element. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `file`. + * For more information, see + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). + */ +locations_default( + unique int id: @location_default, + int file: @file ref, + int beginLine: int ref, + int beginColumn: int ref, + int endLine: int ref, + int endColumn: int ref +); + +files( + unique int id: @file, + string name: string ref +); + +folders( + unique int id: @folder, + string name: string ref +); + +@container = @file | @folder + +containerparent( + int parent: @container ref, + unique int child: @container ref +); + +/*- Lines of code -*/ + +numlines( + int element_id: @sourceline ref, + int num_lines: int ref, + int num_code: int ref, + int num_comment: int ref +); + +/*- External data -*/ + +/** + * External data, loaded from CSV files during snapshot creation. See + * [Tutorial: Incorporating external data](https://help.semmle.com/wiki/display/SD/Tutorial%3A+Incorporating+external+data) + * for more information. + */ +externalData( + int id : @externalDataElement, + string path : string ref, + int column: int ref, + string value : string ref +); + +/*- Source location prefix -*/ + +/** + * The source location of the snapshot. + */ +sourceLocationPrefix(string prefix : string ref); + +/*- JavaScript-specific part -*/ + +@location = @location_default + +@sourceline = @locatable; + +filetype( + int file: @file ref, + string filetype: string ref +) + +// top-level code fragments +toplevels (unique int id: @toplevel, + int kind: int ref); + +is_externs (int toplevel: @toplevel ref); + +case @toplevel.kind of + 0 = @script +| 1 = @inline_script +| 2 = @event_handler +| 3 = @javascript_url +| 4 = @template_toplevel; + +is_module (int tl: @toplevel ref); +is_nodejs (int tl: @toplevel ref); +is_es2015_module (int tl: @toplevel ref); +is_closure_module (int tl: @toplevel ref); + +@xml_node_with_code = @xmlelement | @xmlattribute | @template_placeholder_tag; +toplevel_parent_xml_node( + unique int toplevel: @toplevel ref, + int xmlnode: @xml_node_with_code ref); + +xml_element_parent_expression( + unique int xmlnode: @xmlelement ref, + int expression: @expr ref, + int index: int ref); + +// statements +#keyset[parent, idx] +stmts (unique int id: @stmt, + int kind: int ref, + int parent: @stmt_parent ref, + int idx: int ref, + varchar(900) tostring: string ref); + +stmt_containers (unique int stmt: @stmt ref, + int container: @stmt_container ref); + +jump_targets (unique int jump: @stmt ref, + int target: @stmt ref); + +@stmt_parent = @stmt | @toplevel | @function_expr | @arrow_function_expr | @static_initializer; +@stmt_container = @toplevel | @function | @namespace_declaration | @external_module_declaration | @global_augmentation_declaration; + +case @stmt.kind of + 0 = @empty_stmt +| 1 = @block_stmt +| 2 = @expr_stmt +| 3 = @if_stmt +| 4 = @labeled_stmt +| 5 = @break_stmt +| 6 = @continue_stmt +| 7 = @with_stmt +| 8 = @switch_stmt +| 9 = @return_stmt +| 10 = @throw_stmt +| 11 = @try_stmt +| 12 = @while_stmt +| 13 = @do_while_stmt +| 14 = @for_stmt +| 15 = @for_in_stmt +| 16 = @debugger_stmt +| 17 = @function_decl_stmt +| 18 = @var_decl_stmt +| 19 = @case +| 20 = @catch_clause +| 21 = @for_of_stmt +| 22 = @const_decl_stmt +| 23 = @let_stmt +| 24 = @legacy_let_stmt +| 25 = @for_each_stmt +| 26 = @class_decl_stmt +| 27 = @import_declaration +| 28 = @export_all_declaration +| 29 = @export_default_declaration +| 30 = @export_named_declaration +| 31 = @namespace_declaration +| 32 = @import_equals_declaration +| 33 = @export_assign_declaration +| 34 = @interface_declaration +| 35 = @type_alias_declaration +| 36 = @enum_declaration +| 37 = @external_module_declaration +| 38 = @export_as_namespace_declaration +| 39 = @global_augmentation_declaration +| 40 = @using_decl_stmt +; + +@decl_stmt = @var_decl_stmt | @const_decl_stmt | @let_stmt | @legacy_let_stmt | @using_decl_stmt; + +@export_declaration = @export_all_declaration | @export_default_declaration | @export_named_declaration; + +@namespace_definition = @namespace_declaration | @enum_declaration; +@type_definition = @class_definition | @interface_declaration | @enum_declaration | @type_alias_declaration | @enum_member; + +is_instantiated(unique int decl: @namespace_declaration ref); + +@declarable_node = @decl_stmt | @namespace_declaration | @class_decl_stmt | @function_decl_stmt | @enum_declaration | @external_module_declaration | @global_augmentation_declaration | @field; +has_declare_keyword(unique int stmt: @declarable_node ref); + +is_for_await_of(unique int forof: @for_of_stmt ref); + +// expressions +#keyset[parent, idx] +exprs (unique int id: @expr, + int kind: int ref, + int parent: @expr_parent ref, + int idx: int ref, + varchar(900) tostring: string ref); + +literals (varchar(900) value: string ref, + varchar(900) raw: string ref, + unique int expr: @expr_or_type ref); + +enclosing_stmt (unique int expr: @expr_or_type ref, + int stmt: @stmt ref); + +expr_containers (unique int expr: @expr_or_type ref, + int container: @stmt_container ref); + +array_size (unique int ae: @arraylike ref, + int sz: int ref); + +is_delegating (int yield: @yield_expr ref); + +@expr_or_stmt = @expr | @stmt; +@expr_or_type = @expr | @typeexpr; +@expr_parent = @expr_or_stmt | @property | @function_typeexpr; +@arraylike = @array_expr | @array_pattern; +@type_annotation = @typeexpr | @jsdoc_type_expr; +@node_in_stmt_container = @cfg_node | @type_annotation | @toplevel; + +case @expr.kind of + 0 = @label +| 1 = @null_literal +| 2 = @boolean_literal +| 3 = @number_literal +| 4 = @string_literal +| 5 = @regexp_literal +| 6 = @this_expr +| 7 = @array_expr +| 8 = @obj_expr +| 9 = @function_expr +| 10 = @seq_expr +| 11 = @conditional_expr +| 12 = @new_expr +| 13 = @call_expr +| 14 = @dot_expr +| 15 = @index_expr +| 16 = @neg_expr +| 17 = @plus_expr +| 18 = @log_not_expr +| 19 = @bit_not_expr +| 20 = @typeof_expr +| 21 = @void_expr +| 22 = @delete_expr +| 23 = @eq_expr +| 24 = @neq_expr +| 25 = @eqq_expr +| 26 = @neqq_expr +| 27 = @lt_expr +| 28 = @le_expr +| 29 = @gt_expr +| 30 = @ge_expr +| 31 = @lshift_expr +| 32 = @rshift_expr +| 33 = @urshift_expr +| 34 = @add_expr +| 35 = @sub_expr +| 36 = @mul_expr +| 37 = @div_expr +| 38 = @mod_expr +| 39 = @bitor_expr +| 40 = @xor_expr +| 41 = @bitand_expr +| 42 = @in_expr +| 43 = @instanceof_expr +| 44 = @logand_expr +| 45 = @logor_expr +| 47 = @assign_expr +| 48 = @assign_add_expr +| 49 = @assign_sub_expr +| 50 = @assign_mul_expr +| 51 = @assign_div_expr +| 52 = @assign_mod_expr +| 53 = @assign_lshift_expr +| 54 = @assign_rshift_expr +| 55 = @assign_urshift_expr +| 56 = @assign_or_expr +| 57 = @assign_xor_expr +| 58 = @assign_and_expr +| 59 = @preinc_expr +| 60 = @postinc_expr +| 61 = @predec_expr +| 62 = @postdec_expr +| 63 = @par_expr +| 64 = @var_declarator +| 65 = @arrow_function_expr +| 66 = @spread_element +| 67 = @array_pattern +| 68 = @object_pattern +| 69 = @yield_expr +| 70 = @tagged_template_expr +| 71 = @template_literal +| 72 = @template_element +| 73 = @array_comprehension_expr +| 74 = @generator_expr +| 75 = @for_in_comprehension_block +| 76 = @for_of_comprehension_block +| 77 = @legacy_letexpr +| 78 = @var_decl +| 79 = @proper_varaccess +| 80 = @class_expr +| 81 = @super_expr +| 82 = @newtarget_expr +| 83 = @named_import_specifier +| 84 = @import_default_specifier +| 85 = @import_namespace_specifier +| 86 = @named_export_specifier +| 87 = @exp_expr +| 88 = @assign_exp_expr +| 89 = @jsx_element +| 90 = @jsx_qualified_name +| 91 = @jsx_empty_expr +| 92 = @await_expr +| 93 = @function_sent_expr +| 94 = @decorator +| 95 = @export_default_specifier +| 96 = @export_namespace_specifier +| 97 = @bind_expr +| 98 = @external_module_reference +| 99 = @dynamic_import +| 100 = @expression_with_type_arguments +| 101 = @prefix_type_assertion +| 102 = @as_type_assertion +| 103 = @export_varaccess +| 104 = @decorator_list +| 105 = @non_null_assertion +| 106 = @bigint_literal +| 107 = @nullishcoalescing_expr +| 108 = @e4x_xml_anyname +| 109 = @e4x_xml_static_attribute_selector +| 110 = @e4x_xml_dynamic_attribute_selector +| 111 = @e4x_xml_filter_expression +| 112 = @e4x_xml_static_qualident +| 113 = @e4x_xml_dynamic_qualident +| 114 = @e4x_xml_dotdotexpr +| 115 = @import_meta_expr +| 116 = @assignlogandexpr +| 117 = @assignlogorexpr +| 118 = @assignnullishcoalescingexpr +| 119 = @template_pipe_ref +| 120 = @generated_code_expr +| 121 = @satisfies_expr +; + +@varaccess = @proper_varaccess | @export_varaccess; +@varref = @var_decl | @varaccess; + +@identifier = @label | @varref | @type_identifier; + +@literal = @null_literal | @boolean_literal | @number_literal | @string_literal | @regexp_literal | @bigint_literal; + +@propaccess = @dot_expr | @index_expr; + +@invokeexpr = @new_expr | @call_expr; + +@unaryexpr = @neg_expr | @plus_expr | @log_not_expr | @bit_not_expr | @typeof_expr | @void_expr | @delete_expr | @spread_element; + +@equality_test = @eq_expr | @neq_expr | @eqq_expr | @neqq_expr; + +@comparison = @equality_test | @lt_expr | @le_expr | @gt_expr | @ge_expr; + +@binaryexpr = @comparison | @lshift_expr | @rshift_expr | @urshift_expr | @add_expr | @sub_expr | @mul_expr | @div_expr | @mod_expr | @exp_expr | @bitor_expr | @xor_expr | @bitand_expr | @in_expr | @instanceof_expr | @logand_expr | @logor_expr | @nullishcoalescing_expr; + +@assignment = @assign_expr | @assign_add_expr | @assign_sub_expr | @assign_mul_expr | @assign_div_expr | @assign_mod_expr | @assign_exp_expr | @assign_lshift_expr | @assign_rshift_expr | @assign_urshift_expr | @assign_or_expr | @assign_xor_expr | @assign_and_expr | @assignlogandexpr | @assignlogorexpr | @assignnullishcoalescingexpr; + +@updateexpr = @preinc_expr | @postinc_expr | @predec_expr | @postdec_expr; + +@pattern = @varref | @array_pattern | @object_pattern; + +@comprehension_expr = @array_comprehension_expr | @generator_expr; + +@comprehension_block = @for_in_comprehension_block | @for_of_comprehension_block; + +@import_specifier = @named_import_specifier | @import_default_specifier | @import_namespace_specifier; + +@exportspecifier = @named_export_specifier | @export_default_specifier | @export_namespace_specifier; + +@type_keyword_operand = @import_declaration | @export_declaration | @import_specifier; + +@type_assertion = @as_type_assertion | @prefix_type_assertion; + +@class_definition = @class_decl_stmt | @class_expr; +@interface_definition = @interface_declaration | @interface_typeexpr; +@class_or_interface = @class_definition | @interface_definition; + +@lexical_decl = @var_decl | @type_decl; +@lexical_access = @varaccess | @local_type_access | @local_var_type_access | @local_namespace_access; +@lexical_ref = @lexical_decl | @lexical_access; + +@e4x_xml_attribute_selector = @e4x_xml_static_attribute_selector | @e4x_xml_dynamic_attribute_selector; +@e4x_xml_qualident = @e4x_xml_static_qualident | @e4x_xml_dynamic_qualident; + +expr_contains_template_tag_location( + int expr: @expr ref, + int location: @location ref +); + +@template_placeholder_tag_parent = @xmlelement | @xmlattribute | @file; + +template_placeholder_tag_info( + unique int node: @template_placeholder_tag, + int parentNode: @template_placeholder_tag_parent ref, + varchar(900) raw: string ref +); + +// scopes +scopes (unique int id: @scope, + int kind: int ref); + +case @scope.kind of + 0 = @global_scope +| 1 = @function_scope +| 2 = @catch_scope +| 3 = @module_scope +| 4 = @block_scope +| 5 = @for_scope +| 6 = @for_in_scope // for-of scopes work the same as for-in scopes +| 7 = @comprehension_block_scope +| 8 = @class_expr_scope +| 9 = @namespace_scope +| 10 = @class_decl_scope +| 11 = @interface_scope +| 12 = @type_alias_scope +| 13 = @mapped_type_scope +| 14 = @enum_scope +| 15 = @external_module_scope +| 16 = @conditional_type_scope; + +scopenodes (unique int node: @ast_node ref, + int scope: @scope ref); + +scopenesting (unique int inner: @scope ref, + int outer: @scope ref); + +// functions +@function = @function_decl_stmt | @function_expr | @arrow_function_expr; + +@parameterized = @function | @catch_clause; +@type_parameterized = @function | @class_or_interface | @type_alias_declaration | @mapped_typeexpr | @infer_typeexpr; + +is_generator (int fun: @function ref); +has_rest_parameter (int fun: @function ref); +is_async (int fun: @function ref); + +// variables and lexically scoped type names +#keyset[scope, name] +variables (unique int id: @variable, + varchar(900) name: string ref, + int scope: @scope ref); + +#keyset[scope, name] +local_type_names (unique int id: @local_type_name, + varchar(900) name: string ref, + int scope: @scope ref); + +#keyset[scope, name] +local_namespace_names (unique int id: @local_namespace_name, + varchar(900) name: string ref, + int scope: @scope ref); + +is_arguments_object (int id: @variable ref); + +@lexical_name = @variable | @local_type_name | @local_namespace_name; + +@bind_id = @varaccess | @local_var_type_access; +bind (unique int id: @bind_id ref, + int decl: @variable ref); + +decl (unique int id: @var_decl ref, + int decl: @variable ref); + +@typebind_id = @local_type_access | @export_varaccess; +typebind (unique int id: @typebind_id ref, + int decl: @local_type_name ref); + +@typedecl_id = @type_decl | @var_decl; +typedecl (unique int id: @typedecl_id ref, + int decl: @local_type_name ref); + +namespacedecl (unique int id: @var_decl ref, + int decl: @local_namespace_name ref); + +@namespacebind_id = @local_namespace_access | @export_varaccess; +namespacebind (unique int id: @namespacebind_id ref, + int decl: @local_namespace_name ref); + + +// properties in object literals, property patterns in object patterns, and method declarations in classes +#keyset[parent, index] +properties (unique int id: @property, + int parent: @property_parent ref, + int index: int ref, + int kind: int ref, + varchar(900) tostring: string ref); + +case @property.kind of + 0 = @value_property +| 1 = @property_getter +| 2 = @property_setter +| 3 = @jsx_attribute +| 4 = @function_call_signature +| 5 = @constructor_call_signature +| 6 = @index_signature +| 7 = @enum_member +| 8 = @proper_field +| 9 = @parameter_field +| 10 = @static_initializer +; + +@property_parent = @obj_expr | @object_pattern | @class_definition | @jsx_element | @interface_definition | @enum_declaration; +@property_accessor = @property_getter | @property_setter; +@call_signature = @function_call_signature | @constructor_call_signature; +@field = @proper_field | @parameter_field; +@field_or_vardeclarator = @field | @var_declarator; + +is_computed (int id: @property ref); +is_method (int id: @property ref); +is_static (int id: @property ref); +is_abstract_member (int id: @property ref); +is_const_enum (int id: @enum_declaration ref); +is_abstract_class (int id: @class_decl_stmt ref); + +has_public_keyword (int id: @property ref); +has_private_keyword (int id: @property ref); +has_protected_keyword (int id: @property ref); +has_readonly_keyword (int id: @property ref); +has_type_keyword (int id: @type_keyword_operand ref); +is_optional_member (int id: @property ref); +has_definite_assignment_assertion (int id: @field_or_vardeclarator ref); +is_optional_parameter_declaration (unique int parameter: @pattern ref); + +#keyset[constructor, param_index] +parameter_fields( + unique int field: @parameter_field ref, + int constructor: @function_expr ref, + int param_index: int ref +); + +// types +#keyset[parent, idx] +typeexprs ( + unique int id: @typeexpr, + int kind: int ref, + int parent: @typeexpr_parent ref, + int idx: int ref, + varchar(900) tostring: string ref +); + +case @typeexpr.kind of + 0 = @local_type_access +| 1 = @type_decl +| 2 = @keyword_typeexpr +| 3 = @string_literal_typeexpr +| 4 = @number_literal_typeexpr +| 5 = @boolean_literal_typeexpr +| 6 = @array_typeexpr +| 7 = @union_typeexpr +| 8 = @indexed_access_typeexpr +| 9 = @intersection_typeexpr +| 10 = @parenthesized_typeexpr +| 11 = @tuple_typeexpr +| 12 = @keyof_typeexpr +| 13 = @qualified_type_access +| 14 = @generic_typeexpr +| 15 = @type_label +| 16 = @typeof_typeexpr +| 17 = @local_var_type_access +| 18 = @qualified_var_type_access +| 19 = @this_var_type_access +| 20 = @predicate_typeexpr +| 21 = @interface_typeexpr +| 22 = @type_parameter +| 23 = @plain_function_typeexpr +| 24 = @constructor_typeexpr +| 25 = @local_namespace_access +| 26 = @qualified_namespace_access +| 27 = @mapped_typeexpr +| 28 = @conditional_typeexpr +| 29 = @infer_typeexpr +| 30 = @import_type_access +| 31 = @import_namespace_access +| 32 = @import_var_type_access +| 33 = @optional_typeexpr +| 34 = @rest_typeexpr +| 35 = @bigint_literal_typeexpr +| 36 = @readonly_typeexpr +| 37 = @template_literal_typeexpr +; + +@typeref = @typeaccess | @type_decl; +@type_identifier = @type_decl | @local_type_access | @type_label | @local_var_type_access | @local_namespace_access; +@typeexpr_parent = @expr | @stmt | @property | @typeexpr; +@literal_typeexpr = @string_literal_typeexpr | @number_literal_typeexpr | @boolean_literal_typeexpr | @bigint_literal_typeexpr; +@typeaccess = @local_type_access | @qualified_type_access | @import_type_access; +@vartypeaccess = @local_var_type_access | @qualified_var_type_access | @this_var_type_access | @import_var_type_access; +@namespace_access = @local_namespace_access | @qualified_namespace_access | @import_namespace_access; +@import_typeexpr = @import_type_access | @import_namespace_access | @import_var_type_access; + +@function_typeexpr = @plain_function_typeexpr | @constructor_typeexpr; + +// types +types ( + unique int id: @type, + int kind: int ref, + varchar(900) tostring: string ref +); + +#keyset[parent, idx] +type_child ( + int child: @type ref, + int parent: @type ref, + int idx: int ref +); + +case @type.kind of + 0 = @any_type +| 1 = @string_type +| 2 = @number_type +| 3 = @union_type +| 4 = @true_type +| 5 = @false_type +| 6 = @type_reference +| 7 = @object_type +| 8 = @canonical_type_variable_type +| 9 = @typeof_type +| 10 = @void_type +| 11 = @undefined_type +| 12 = @null_type +| 13 = @never_type +| 14 = @plain_symbol_type +| 15 = @unique_symbol_type +| 16 = @objectkeyword_type +| 17 = @intersection_type +| 18 = @tuple_type +| 19 = @lexical_type_variable_type +| 20 = @this_type +| 21 = @number_literal_type +| 22 = @string_literal_type +| 23 = @unknown_type +| 24 = @bigint_type +| 25 = @bigint_literal_type +; + +@boolean_literal_type = @true_type | @false_type; +@symbol_type = @plain_symbol_type | @unique_symbol_type; +@union_or_intersection_type = @union_type | @intersection_type; +@typevariable_type = @canonical_type_variable_type | @lexical_type_variable_type; + +has_asserts_keyword(int node: @predicate_typeexpr ref); + +@typed_ast_node = @expr | @typeexpr | @function; +ast_node_type( + unique int node: @typed_ast_node ref, + int typ: @type ref); + +declared_function_signature( + unique int node: @function ref, + int sig: @signature_type ref +); + +invoke_expr_signature( + unique int node: @invokeexpr ref, + int sig: @signature_type ref +); + +invoke_expr_overload_index( + unique int node: @invokeexpr ref, + int index: int ref +); + +symbols ( + unique int id: @symbol, + int kind: int ref, + varchar(900) name: string ref +); + +symbol_parent ( + unique int symbol: @symbol ref, + int parent: @symbol ref +); + +symbol_module ( + int symbol: @symbol ref, + varchar(900) moduleName: string ref +); + +symbol_global ( + int symbol: @symbol ref, + varchar(900) globalName: string ref +); + +case @symbol.kind of + 0 = @root_symbol +| 1 = @member_symbol +| 2 = @other_symbol +; + +@type_with_symbol = @type_reference | @typevariable_type | @typeof_type | @unique_symbol_type; +@ast_node_with_symbol = @type_definition | @namespace_definition | @toplevel | @typeaccess | @namespace_access | @var_decl | @function | @invokeexpr | @import_declaration | @external_module_reference | @external_module_declaration; + +ast_node_symbol( + unique int node: @ast_node_with_symbol ref, + int symbol: @symbol ref); + +type_symbol( + unique int typ: @type_with_symbol ref, + int symbol: @symbol ref); + +#keyset[typ, name] +type_property( + int typ: @type ref, + varchar(900) name: string ref, + int propertyType: @type ref); + +type_alias( + unique int aliasType: @type ref, + int underlyingType: @type ref); + +@literal_type = @string_literal_type | @number_literal_type | @boolean_literal_type | @bigint_literal_type; +@type_with_literal_value = @string_literal_type | @number_literal_type | @bigint_literal_type; +type_literal_value( + unique int typ: @type_with_literal_value ref, + varchar(900) value: string ref); + +signature_types ( + unique int id: @signature_type, + int kind: int ref, + varchar(900) tostring: string ref, + int type_parameters: int ref, + int required_params: int ref +); + +is_abstract_signature( + unique int sig: @signature_type ref +); + +signature_rest_parameter( + unique int sig: @signature_type ref, + int rest_param_arra_type: @type ref +); + +case @signature_type.kind of + 0 = @function_signature_type +| 1 = @constructor_signature_type +; + +#keyset[typ, kind, index] +type_contains_signature ( + int typ: @type ref, + int kind: int ref, // constructor/call/index + int index: int ref, // ordering of overloaded signatures + int sig: @signature_type ref +); + +#keyset[parent, index] +signature_contains_type ( + int child: @type ref, + int parent: @signature_type ref, + int index: int ref +); + +#keyset[sig, index] +signature_parameter_name ( + int sig: @signature_type ref, + int index: int ref, + varchar(900) name: string ref +); + +number_index_type ( + unique int baseType: @type ref, + int propertyType: @type ref +); + +string_index_type ( + unique int baseType: @type ref, + int propertyType: @type ref +); + +base_type_names( + int typeName: @symbol ref, + int baseTypeName: @symbol ref +); + +self_types( + int typeName: @symbol ref, + int selfType: @type_reference ref +); + +tuple_type_min_length( + unique int typ: @type ref, + int minLength: int ref +); + +tuple_type_rest_index( + unique int typ: @type ref, + int index: int ref +); + +// comments +comments (unique int id: @comment, + int kind: int ref, + int toplevel: @toplevel ref, + varchar(900) text: string ref, + varchar(900) tostring: string ref); + +case @comment.kind of + 0 = @slashslash_comment +| 1 = @slashstar_comment +| 2 = @doc_comment +| 3 = @html_comment_start +| 4 = @htmlcommentend; + +@html_comment = @html_comment_start | @htmlcommentend; +@line_comment = @slashslash_comment | @html_comment; +@block_comment = @slashstar_comment | @doc_comment; + +// source lines +lines (unique int id: @line, + int toplevel: @toplevel ref, + varchar(900) text: string ref, + varchar(2) terminator: string ref); +indentation (int file: @file ref, + int lineno: int ref, + varchar(1) indentChar: string ref, + int indentDepth: int ref); + +// JavaScript parse errors +js_parse_errors (unique int id: @js_parse_error, + int toplevel: @toplevel ref, + varchar(900) message: string ref, + varchar(900) line: string ref); + +// regular expressions +#keyset[parent, idx] +regexpterm (unique int id: @regexpterm, + int kind: int ref, + int parent: @regexpparent ref, + int idx: int ref, + varchar(900) tostring: string ref); + +@regexpparent = @regexpterm | @regexp_literal | @string_literal | @add_expr; + +case @regexpterm.kind of + 0 = @regexp_alt +| 1 = @regexp_seq +| 2 = @regexp_caret +| 3 = @regexp_dollar +| 4 = @regexp_wordboundary +| 5 = @regexp_nonwordboundary +| 6 = @regexp_positive_lookahead +| 7 = @regexp_negative_lookahead +| 8 = @regexp_star +| 9 = @regexp_plus +| 10 = @regexp_opt +| 11 = @regexp_range +| 12 = @regexp_dot +| 13 = @regexp_group +| 14 = @regexp_normal_constant +| 15 = @regexp_hex_escape +| 16 = @regexp_unicode_escape +| 17 = @regexp_dec_escape +| 18 = @regexp_oct_escape +| 19 = @regexp_ctrl_escape +| 20 = @regexp_char_class_escape +| 21 = @regexp_id_escape +| 22 = @regexp_backref +| 23 = @regexp_char_class +| 24 = @regexp_char_range +| 25 = @regexp_positive_lookbehind +| 26 = @regexp_negative_lookbehind +| 27 = @regexp_unicode_property_escape +| 28 = @regexp_intersection; + +regexp_parse_errors (unique int id: @regexp_parse_error, + int regexp: @regexpterm ref, + varchar(900) message: string ref); + +@regexp_quantifier = @regexp_star | @regexp_plus | @regexp_opt | @regexp_range; +@regexp_escape = @regexp_char_escape | @regexp_char_class_escape | @regexp_unicode_property_escape; +@regexp_char_escape = @regexp_hex_escape | @regexp_unicode_escape | @regexp_dec_escape | @regexp_oct_escape | @regexp_ctrl_escape | @regexp_id_escape; +@regexp_constant = @regexp_normal_constant | @regexp_char_escape; +@regexp_lookahead = @regexp_positive_lookahead | @regexp_negative_lookahead; +@regexp_lookbehind = @regexp_positive_lookbehind | @regexp_negative_lookbehind; +@regexp_subpattern = @regexp_lookahead | @regexp_lookbehind; +@regexp_anchor = @regexp_dollar | @regexp_caret; + +is_greedy (int id: @regexp_quantifier ref); +range_quantifier_lower_bound (unique int id: @regexp_range ref, int lo: int ref); +range_quantifier_upper_bound (unique int id: @regexp_range ref, int hi: int ref); +is_capture (unique int id: @regexp_group ref, int number: int ref); +is_named_capture (unique int id: @regexp_group ref, string name: string ref); +is_inverted (int id: @regexp_char_class ref); +regexp_const_value (unique int id: @regexp_constant ref, varchar(1) value: string ref); +char_class_escape (unique int id: @regexp_char_class_escape ref, varchar(1) value: string ref); +backref (unique int id: @regexp_backref ref, int value: int ref); +named_backref (unique int id: @regexp_backref ref, string name: string ref); +unicode_property_escapename (unique int id: @regexp_unicode_property_escape ref, string name: string ref); +unicode_property_escapevalue (unique int id: @regexp_unicode_property_escape ref, string value: string ref); + +// tokens +#keyset[toplevel, idx] +tokeninfo (unique int id: @token, + int kind: int ref, + int toplevel: @toplevel ref, + int idx: int ref, + varchar(900) value: string ref); + +case @token.kind of + 0 = @token_eof +| 1 = @token_null_literal +| 2 = @token_boolean_literal +| 3 = @token_numeric_literal +| 4 = @token_string_literal +| 5 = @token_regular_expression +| 6 = @token_identifier +| 7 = @token_keyword +| 8 = @token_punctuator; + +// associate comments with the token immediately following them (which may be EOF) +next_token (int comment: @comment ref, int token: @token ref); + +// JSON +#keyset[parent, idx] +json (unique int id: @json_value, + int kind: int ref, + int parent: @json_parent ref, + int idx: int ref, + varchar(900) tostring: string ref); + +json_literals (varchar(900) value: string ref, + varchar(900) raw: string ref, + unique int expr: @json_value ref); + +json_properties (int obj: @json_object ref, + varchar(900) property: string ref, + int value: @json_value ref); + +json_errors (unique int id: @json_parse_error, + varchar(900) message: string ref); + +json_locations(unique int locatable: @json_locatable ref, + int location: @location_default ref); + +case @json_value.kind of + 0 = @json_null +| 1 = @json_boolean +| 2 = @json_number +| 3 = @json_string +| 4 = @json_array +| 5 = @json_object; + +@json_parent = @json_object | @json_array | @file; + +@json_locatable = @json_value | @json_parse_error; + +// locations +@ast_node = @toplevel | @stmt | @expr | @property | @typeexpr; + +@locatable = @file + | @ast_node + | @comment + | @line + | @js_parse_error | @regexp_parse_error + | @regexpterm + | @json_locatable + | @token + | @cfg_node + | @jsdoc | @jsdoc_type_expr | @jsdoc_tag + | @yaml_locatable + | @xmllocatable + | @configLocatable + | @template_placeholder_tag; + +hasLocation (unique int locatable: @locatable ref, + int location: @location ref); + +// CFG +entry_cfg_node (unique int id: @entry_node, int container: @stmt_container ref); +exit_cfg_node (unique int id: @exit_node, int container: @stmt_container ref); +guard_node (unique int id: @guard_node, int kind: int ref, int test: @expr ref); +case @guard_node.kind of + 0 = @falsy_guard +| 1 = @truthy_guard; +@condition_guard = @falsy_guard | @truthy_guard; + +@synthetic_cfg_node = @entry_node | @exit_node | @guard_node; +@cfg_node = @synthetic_cfg_node | @expr_parent; + +successor (int pred: @cfg_node ref, int succ: @cfg_node ref); + +// JSDoc comments +jsdoc (unique int id: @jsdoc, varchar(900) description: string ref, int comment: @comment ref); +#keyset[parent, idx] +jsdoc_tags (unique int id: @jsdoc_tag, varchar(900) title: string ref, + int parent: @jsdoc ref, int idx: int ref, varchar(900) tostring: string ref); +jsdoc_tag_descriptions (unique int tag: @jsdoc_tag ref, varchar(900) text: string ref); +jsdoc_tag_names (unique int tag: @jsdoc_tag ref, varchar(900) text: string ref); + +#keyset[parent, idx] +jsdoc_type_exprs (unique int id: @jsdoc_type_expr, + int kind: int ref, + int parent: @jsdoc_type_expr_parent ref, + int idx: int ref, + varchar(900) tostring: string ref); +case @jsdoc_type_expr.kind of + 0 = @jsdoc_any_type_expr +| 1 = @jsdoc_null_type_expr +| 2 = @jsdoc_undefined_type_expr +| 3 = @jsdoc_unknown_type_expr +| 4 = @jsdoc_void_type_expr +| 5 = @jsdoc_named_type_expr +| 6 = @jsdoc_applied_type_expr +| 7 = @jsdoc_nullable_type_expr +| 8 = @jsdoc_non_nullable_type_expr +| 9 = @jsdoc_record_type_expr +| 10 = @jsdoc_array_type_expr +| 11 = @jsdoc_union_type_expr +| 12 = @jsdoc_function_type_expr +| 13 = @jsdoc_optional_type_expr +| 14 = @jsdoc_rest_type_expr +; + +#keyset[id, idx] +jsdoc_record_field_name (int id: @jsdoc_record_type_expr ref, int idx: int ref, varchar(900) name: string ref); +jsdoc_prefix_qualifier (int id: @jsdoc_type_expr ref); +jsdoc_has_new_parameter (int fn: @jsdoc_function_type_expr ref); + +@jsdoc_type_expr_parent = @jsdoc_type_expr | @jsdoc_tag; + +jsdoc_errors (unique int id: @jsdoc_error, int tag: @jsdoc_tag ref, varchar(900) message: string ref, varchar(900) tostring: string ref); + +@dataflownode = @expr | @function_decl_stmt | @class_decl_stmt | @namespace_declaration | @enum_declaration | @property; + +@optionalchainable = @call_expr | @propaccess; + +isOptionalChaining(int id: @optionalchainable ref); + +/** + * The time taken for the extraction of a file. + * This table contains non-deterministic content. + * + * The sum of the `time` column for each (`file`, `timerKind`) pair + * is the total time taken for extraction of `file`. The `extractionPhase` + * column provides a granular view of the extraction time of the file. + */ +extraction_time( + int file : @file ref, + // see `com.semmle.js.extractor.ExtractionMetrics.ExtractionPhase`. + int extractionPhase: int ref, + // 0 for the elapsed CPU time in nanoseconds, 1 for the elapsed wallclock time in nanoseconds + int timerKind: int ref, + float time: float ref +) + +/** +* Non-timing related data for the extraction of a single file. +* This table contains non-deterministic content. +*/ +extraction_data( + int file : @file ref, + // the absolute path to the cache file + varchar(900) cacheFile: string ref, + boolean fromCache: boolean ref, + int length: int ref +) + +/*- YAML -*/ + +#keyset[parent, idx] +yaml (unique int id: @yaml_node, + int kind: int ref, + int parent: @yaml_node_parent ref, + int idx: int ref, + string tag: string ref, + string tostring: string ref); + +case @yaml_node.kind of + 0 = @yaml_scalar_node +| 1 = @yaml_mapping_node +| 2 = @yaml_sequence_node +| 3 = @yaml_alias_node +; + +@yaml_collection_node = @yaml_mapping_node | @yaml_sequence_node; + +@yaml_node_parent = @yaml_collection_node | @file; + +yaml_anchors (unique int node: @yaml_node ref, + string anchor: string ref); + +yaml_aliases (unique int alias: @yaml_alias_node ref, + string target: string ref); + +yaml_scalars (unique int scalar: @yaml_scalar_node ref, + int style: int ref, + string value: string ref); + +yaml_errors (unique int id: @yaml_error, + string message: string ref); + +yaml_locations(unique int locatable: @yaml_locatable ref, + int location: @location_default ref); + +@yaml_locatable = @yaml_node | @yaml_error; + +/*- XML Files -*/ + +xmlEncoding( + unique int id: @file ref, + string encoding: string ref +); + +xmlDTDs( + unique int id: @xmldtd, + string root: string ref, + string publicId: string ref, + string systemId: string ref, + int fileid: @file ref +); + +xmlElements( + unique int id: @xmlelement, + string name: string ref, + int parentid: @xmlparent ref, + int idx: int ref, + int fileid: @file ref +); + +xmlAttrs( + unique int id: @xmlattribute, + int elementid: @xmlelement ref, + string name: string ref, + string value: string ref, + int idx: int ref, + int fileid: @file ref +); + +xmlNs( + int id: @xmlnamespace, + string prefixName: string ref, + string URI: string ref, + int fileid: @file ref +); + +xmlHasNs( + int elementId: @xmlnamespaceable ref, + int nsId: @xmlnamespace ref, + int fileid: @file ref +); + +xmlComments( + unique int id: @xmlcomment, + string text: string ref, + int parentid: @xmlparent ref, + int fileid: @file ref +); + +xmlChars( + unique int id: @xmlcharacters, + string text: string ref, + int parentid: @xmlparent ref, + int idx: int ref, + int isCDATA: int ref, + int fileid: @file ref +); + +@xmlparent = @file | @xmlelement; +@xmlnamespaceable = @xmlelement | @xmlattribute; + +xmllocations( + int xmlElement: @xmllocatable ref, + int location: @location_default ref +); + +@xmllocatable = @xmlcharacters | @xmlelement | @xmlcomment | @xmlattribute | @xmldtd | @file | @xmlnamespace; + +/*- Configuration files with key value pairs -*/ + +configs( + unique int id: @config +); + +configNames( + unique int id: @configName, + int config: @config ref, + string name: string ref +); + +configValues( + unique int id: @configValue, + int config: @config ref, + string value: string ref +); + +configLocations( + int locatable: @configLocatable ref, + int location: @location_default ref +); + +@configLocatable = @config | @configName | @configValue; diff --git a/javascript/ql/lib/upgrades/c88c69174bd0dd4e95f1bcfbada68a2505e812c3/upgrade.properties b/javascript/ql/lib/upgrades/c88c69174bd0dd4e95f1bcfbada68a2505e812c3/upgrade.properties new file mode 100644 index 000000000000..07964edef857 --- /dev/null +++ b/javascript/ql/lib/upgrades/c88c69174bd0dd4e95f1bcfbada68a2505e812c3/upgrade.properties @@ -0,0 +1,2 @@ +description: Adds support for ECMAScript 2024 v Flag Intersection +compatibility: backwards From 13b49d931a0a174e25708962adac6fd2756c1eff Mon Sep 17 00:00:00 2001 From: Napalys Date: Thu, 27 Feb 2025 08:46:38 +0100 Subject: [PATCH 4/9] Add tests for RegExp intersection functionality in ql. --- .../RegExp/RegExpIntersection/intersection.js | 7 ++ .../RegExpIntersection/printAst.expected | 117 ++++++++++++++++++ .../RegExp/RegExpIntersection/printAst.ql | 1 + 3 files changed, 125 insertions(+) create mode 100644 javascript/ql/test/library-tests/RegExp/RegExpIntersection/intersection.js create mode 100644 javascript/ql/test/library-tests/RegExp/RegExpIntersection/printAst.expected create mode 100644 javascript/ql/test/library-tests/RegExp/RegExpIntersection/printAst.ql diff --git a/javascript/ql/test/library-tests/RegExp/RegExpIntersection/intersection.js b/javascript/ql/test/library-tests/RegExp/RegExpIntersection/intersection.js new file mode 100644 index 000000000000..5f289de0fd16 --- /dev/null +++ b/javascript/ql/test/library-tests/RegExp/RegExpIntersection/intersection.js @@ -0,0 +1,7 @@ +/[[abc]&&[bcd]]/v; // Valid use of intersection operator, matches b or c +/abc&&bcd/v; //Valid regex, but no intersection operation: Matches the literal string "abc&&bcd" +/[abc]&&[bcd]/v; // Valid regex, but incorrect intersection operation: + // - Matches a single character from [abc] + // - Then the literal "&&" + // - Then a single character from [bcd] +/[abc&&bcd]/v; // Compilation error due to invalid use of intersection operation diff --git a/javascript/ql/test/library-tests/RegExp/RegExpIntersection/printAst.expected b/javascript/ql/test/library-tests/RegExp/RegExpIntersection/printAst.expected new file mode 100644 index 000000000000..1bc5ab38e265 --- /dev/null +++ b/javascript/ql/test/library-tests/RegExp/RegExpIntersection/printAst.expected @@ -0,0 +1,117 @@ +nodes +| intersection.js:1:1:1:17 | [RegExpLiteral] /[[abc]&&[bcd]]/v | semmle.label | [RegExpLiteral] /[[abc]&&[bcd]]/v | +| intersection.js:1:1:1:18 | [ExprStmt] /[[abc]&&[bcd]]/v; | semmle.label | [ExprStmt] /[[abc]&&[bcd]]/v; | +| intersection.js:1:1:1:18 | [ExprStmt] /[[abc]&&[bcd]]/v; | semmle.order | 1 | +| intersection.js:1:2:1:7 | [RegExpCharacterClass] [[abc] | semmle.label | [RegExpCharacterClass] [[abc] | +| intersection.js:1:2:1:15 | [???] [[abc]&&[bcd]] | semmle.label | [???] [[abc]&&[bcd]] | +| intersection.js:1:3:1:3 | [RegExpNormalConstant] [ | semmle.label | [RegExpNormalConstant] [ | +| intersection.js:1:4:1:4 | [RegExpNormalConstant] a | semmle.label | [RegExpNormalConstant] a | +| intersection.js:1:5:1:5 | [RegExpNormalConstant] b | semmle.label | [RegExpNormalConstant] b | +| intersection.js:1:6:1:6 | [RegExpNormalConstant] c | semmle.label | [RegExpNormalConstant] c | +| intersection.js:1:10:1:14 | [RegExpCharacterClass] [bcd] | semmle.label | [RegExpCharacterClass] [bcd] | +| intersection.js:1:10:1:15 | [RegExpSequence] [bcd]] | semmle.label | [RegExpSequence] [bcd]] | +| intersection.js:1:11:1:11 | [RegExpNormalConstant] b | semmle.label | [RegExpNormalConstant] b | +| intersection.js:1:12:1:12 | [RegExpNormalConstant] c | semmle.label | [RegExpNormalConstant] c | +| intersection.js:1:13:1:13 | [RegExpNormalConstant] d | semmle.label | [RegExpNormalConstant] d | +| intersection.js:1:15:1:15 | [RegExpNormalConstant] ] | semmle.label | [RegExpNormalConstant] ] | +| intersection.js:2:1:2:11 | [RegExpLiteral] /abc&&bcd/v | semmle.label | [RegExpLiteral] /abc&&bcd/v | +| intersection.js:2:1:2:12 | [ExprStmt] /abc&&bcd/v; | semmle.label | [ExprStmt] /abc&&bcd/v; | +| intersection.js:2:1:2:12 | [ExprStmt] /abc&&bcd/v; | semmle.order | 2 | +| intersection.js:2:2:2:9 | [RegExpNormalConstant] abc&&bcd | semmle.label | [RegExpNormalConstant] abc&&bcd | +| intersection.js:3:1:3:15 | [RegExpLiteral] /[abc]&&[bcd]/v | semmle.label | [RegExpLiteral] /[abc]&&[bcd]/v | +| intersection.js:3:1:3:16 | [ExprStmt] /[abc]&&[bcd]/v; | semmle.label | [ExprStmt] /[abc]&&[bcd]/v; | +| intersection.js:3:1:3:16 | [ExprStmt] /[abc]&&[bcd]/v; | semmle.order | 3 | +| intersection.js:3:2:3:6 | [RegExpCharacterClass] [abc] | semmle.label | [RegExpCharacterClass] [abc] | +| intersection.js:3:2:3:13 | [???] [abc]&&[bcd] | semmle.label | [???] [abc]&&[bcd] | +| intersection.js:3:3:3:3 | [RegExpNormalConstant] a | semmle.label | [RegExpNormalConstant] a | +| intersection.js:3:4:3:4 | [RegExpNormalConstant] b | semmle.label | [RegExpNormalConstant] b | +| intersection.js:3:5:3:5 | [RegExpNormalConstant] c | semmle.label | [RegExpNormalConstant] c | +| intersection.js:3:9:3:13 | [RegExpCharacterClass] [bcd] | semmle.label | [RegExpCharacterClass] [bcd] | +| intersection.js:3:10:3:10 | [RegExpNormalConstant] b | semmle.label | [RegExpNormalConstant] b | +| intersection.js:3:11:3:11 | [RegExpNormalConstant] c | semmle.label | [RegExpNormalConstant] c | +| intersection.js:3:12:3:12 | [RegExpNormalConstant] d | semmle.label | [RegExpNormalConstant] d | +| intersection.js:7:1:7:13 | [RegExpLiteral] /[abc&&bcd]/v | semmle.label | [RegExpLiteral] /[abc&&bcd]/v | +| intersection.js:7:1:7:14 | [ExprStmt] /[abc&&bcd]/v; | semmle.label | [ExprStmt] /[abc&&bcd]/v; | +| intersection.js:7:1:7:14 | [ExprStmt] /[abc&&bcd]/v; | semmle.order | 4 | +| intersection.js:7:2:7:11 | [RegExpCharacterClass] [abc&&bcd] | semmle.label | [RegExpCharacterClass] [abc&&bcd] | +| intersection.js:7:3:7:3 | [RegExpNormalConstant] a | semmle.label | [RegExpNormalConstant] a | +| intersection.js:7:4:7:4 | [RegExpNormalConstant] b | semmle.label | [RegExpNormalConstant] b | +| intersection.js:7:5:7:5 | [RegExpNormalConstant] c | semmle.label | [RegExpNormalConstant] c | +| intersection.js:7:6:7:6 | [RegExpNormalConstant] & | semmle.label | [RegExpNormalConstant] & | +| intersection.js:7:7:7:7 | [RegExpNormalConstant] & | semmle.label | [RegExpNormalConstant] & | +| intersection.js:7:8:7:8 | [RegExpNormalConstant] b | semmle.label | [RegExpNormalConstant] b | +| intersection.js:7:9:7:9 | [RegExpNormalConstant] c | semmle.label | [RegExpNormalConstant] c | +| intersection.js:7:10:7:10 | [RegExpNormalConstant] d | semmle.label | [RegExpNormalConstant] d | +edges +| intersection.js:1:1:1:17 | [RegExpLiteral] /[[abc]&&[bcd]]/v | intersection.js:1:2:1:15 | [???] [[abc]&&[bcd]] | semmle.label | 0 | +| intersection.js:1:1:1:17 | [RegExpLiteral] /[[abc]&&[bcd]]/v | intersection.js:1:2:1:15 | [???] [[abc]&&[bcd]] | semmle.order | 0 | +| intersection.js:1:1:1:18 | [ExprStmt] /[[abc]&&[bcd]]/v; | intersection.js:1:1:1:17 | [RegExpLiteral] /[[abc]&&[bcd]]/v | semmle.label | 1 | +| intersection.js:1:1:1:18 | [ExprStmt] /[[abc]&&[bcd]]/v; | intersection.js:1:1:1:17 | [RegExpLiteral] /[[abc]&&[bcd]]/v | semmle.order | 1 | +| intersection.js:1:2:1:7 | [RegExpCharacterClass] [[abc] | intersection.js:1:3:1:3 | [RegExpNormalConstant] [ | semmle.label | 0 | +| intersection.js:1:2:1:7 | [RegExpCharacterClass] [[abc] | intersection.js:1:3:1:3 | [RegExpNormalConstant] [ | semmle.order | 0 | +| intersection.js:1:2:1:7 | [RegExpCharacterClass] [[abc] | intersection.js:1:4:1:4 | [RegExpNormalConstant] a | semmle.label | 1 | +| intersection.js:1:2:1:7 | [RegExpCharacterClass] [[abc] | intersection.js:1:4:1:4 | [RegExpNormalConstant] a | semmle.order | 1 | +| intersection.js:1:2:1:7 | [RegExpCharacterClass] [[abc] | intersection.js:1:5:1:5 | [RegExpNormalConstant] b | semmle.label | 2 | +| intersection.js:1:2:1:7 | [RegExpCharacterClass] [[abc] | intersection.js:1:5:1:5 | [RegExpNormalConstant] b | semmle.order | 2 | +| intersection.js:1:2:1:7 | [RegExpCharacterClass] [[abc] | intersection.js:1:6:1:6 | [RegExpNormalConstant] c | semmle.label | 3 | +| intersection.js:1:2:1:7 | [RegExpCharacterClass] [[abc] | intersection.js:1:6:1:6 | [RegExpNormalConstant] c | semmle.order | 3 | +| intersection.js:1:2:1:15 | [???] [[abc]&&[bcd]] | intersection.js:1:2:1:7 | [RegExpCharacterClass] [[abc] | semmle.label | 0 | +| intersection.js:1:2:1:15 | [???] [[abc]&&[bcd]] | intersection.js:1:2:1:7 | [RegExpCharacterClass] [[abc] | semmle.order | 0 | +| intersection.js:1:2:1:15 | [???] [[abc]&&[bcd]] | intersection.js:1:10:1:15 | [RegExpSequence] [bcd]] | semmle.label | 1 | +| intersection.js:1:2:1:15 | [???] [[abc]&&[bcd]] | intersection.js:1:10:1:15 | [RegExpSequence] [bcd]] | semmle.order | 1 | +| intersection.js:1:10:1:14 | [RegExpCharacterClass] [bcd] | intersection.js:1:11:1:11 | [RegExpNormalConstant] b | semmle.label | 0 | +| intersection.js:1:10:1:14 | [RegExpCharacterClass] [bcd] | intersection.js:1:11:1:11 | [RegExpNormalConstant] b | semmle.order | 0 | +| intersection.js:1:10:1:14 | [RegExpCharacterClass] [bcd] | intersection.js:1:12:1:12 | [RegExpNormalConstant] c | semmle.label | 1 | +| intersection.js:1:10:1:14 | [RegExpCharacterClass] [bcd] | intersection.js:1:12:1:12 | [RegExpNormalConstant] c | semmle.order | 1 | +| intersection.js:1:10:1:14 | [RegExpCharacterClass] [bcd] | intersection.js:1:13:1:13 | [RegExpNormalConstant] d | semmle.label | 2 | +| intersection.js:1:10:1:14 | [RegExpCharacterClass] [bcd] | intersection.js:1:13:1:13 | [RegExpNormalConstant] d | semmle.order | 2 | +| intersection.js:1:10:1:15 | [RegExpSequence] [bcd]] | intersection.js:1:10:1:14 | [RegExpCharacterClass] [bcd] | semmle.label | 0 | +| intersection.js:1:10:1:15 | [RegExpSequence] [bcd]] | intersection.js:1:10:1:14 | [RegExpCharacterClass] [bcd] | semmle.order | 0 | +| intersection.js:1:10:1:15 | [RegExpSequence] [bcd]] | intersection.js:1:15:1:15 | [RegExpNormalConstant] ] | semmle.label | 1 | +| intersection.js:1:10:1:15 | [RegExpSequence] [bcd]] | intersection.js:1:15:1:15 | [RegExpNormalConstant] ] | semmle.order | 1 | +| intersection.js:2:1:2:11 | [RegExpLiteral] /abc&&bcd/v | intersection.js:2:2:2:9 | [RegExpNormalConstant] abc&&bcd | semmle.label | 0 | +| intersection.js:2:1:2:11 | [RegExpLiteral] /abc&&bcd/v | intersection.js:2:2:2:9 | [RegExpNormalConstant] abc&&bcd | semmle.order | 0 | +| intersection.js:2:1:2:12 | [ExprStmt] /abc&&bcd/v; | intersection.js:2:1:2:11 | [RegExpLiteral] /abc&&bcd/v | semmle.label | 1 | +| intersection.js:2:1:2:12 | [ExprStmt] /abc&&bcd/v; | intersection.js:2:1:2:11 | [RegExpLiteral] /abc&&bcd/v | semmle.order | 1 | +| intersection.js:3:1:3:15 | [RegExpLiteral] /[abc]&&[bcd]/v | intersection.js:3:2:3:13 | [???] [abc]&&[bcd] | semmle.label | 0 | +| intersection.js:3:1:3:15 | [RegExpLiteral] /[abc]&&[bcd]/v | intersection.js:3:2:3:13 | [???] [abc]&&[bcd] | semmle.order | 0 | +| intersection.js:3:1:3:16 | [ExprStmt] /[abc]&&[bcd]/v; | intersection.js:3:1:3:15 | [RegExpLiteral] /[abc]&&[bcd]/v | semmle.label | 1 | +| intersection.js:3:1:3:16 | [ExprStmt] /[abc]&&[bcd]/v; | intersection.js:3:1:3:15 | [RegExpLiteral] /[abc]&&[bcd]/v | semmle.order | 1 | +| intersection.js:3:2:3:6 | [RegExpCharacterClass] [abc] | intersection.js:3:3:3:3 | [RegExpNormalConstant] a | semmle.label | 0 | +| intersection.js:3:2:3:6 | [RegExpCharacterClass] [abc] | intersection.js:3:3:3:3 | [RegExpNormalConstant] a | semmle.order | 0 | +| intersection.js:3:2:3:6 | [RegExpCharacterClass] [abc] | intersection.js:3:4:3:4 | [RegExpNormalConstant] b | semmle.label | 1 | +| intersection.js:3:2:3:6 | [RegExpCharacterClass] [abc] | intersection.js:3:4:3:4 | [RegExpNormalConstant] b | semmle.order | 1 | +| intersection.js:3:2:3:6 | [RegExpCharacterClass] [abc] | intersection.js:3:5:3:5 | [RegExpNormalConstant] c | semmle.label | 2 | +| intersection.js:3:2:3:6 | [RegExpCharacterClass] [abc] | intersection.js:3:5:3:5 | [RegExpNormalConstant] c | semmle.order | 2 | +| intersection.js:3:2:3:13 | [???] [abc]&&[bcd] | intersection.js:3:2:3:6 | [RegExpCharacterClass] [abc] | semmle.label | 0 | +| intersection.js:3:2:3:13 | [???] [abc]&&[bcd] | intersection.js:3:2:3:6 | [RegExpCharacterClass] [abc] | semmle.order | 0 | +| intersection.js:3:2:3:13 | [???] [abc]&&[bcd] | intersection.js:3:9:3:13 | [RegExpCharacterClass] [bcd] | semmle.label | 1 | +| intersection.js:3:2:3:13 | [???] [abc]&&[bcd] | intersection.js:3:9:3:13 | [RegExpCharacterClass] [bcd] | semmle.order | 1 | +| intersection.js:3:9:3:13 | [RegExpCharacterClass] [bcd] | intersection.js:3:10:3:10 | [RegExpNormalConstant] b | semmle.label | 0 | +| intersection.js:3:9:3:13 | [RegExpCharacterClass] [bcd] | intersection.js:3:10:3:10 | [RegExpNormalConstant] b | semmle.order | 0 | +| intersection.js:3:9:3:13 | [RegExpCharacterClass] [bcd] | intersection.js:3:11:3:11 | [RegExpNormalConstant] c | semmle.label | 1 | +| intersection.js:3:9:3:13 | [RegExpCharacterClass] [bcd] | intersection.js:3:11:3:11 | [RegExpNormalConstant] c | semmle.order | 1 | +| intersection.js:3:9:3:13 | [RegExpCharacterClass] [bcd] | intersection.js:3:12:3:12 | [RegExpNormalConstant] d | semmle.label | 2 | +| intersection.js:3:9:3:13 | [RegExpCharacterClass] [bcd] | intersection.js:3:12:3:12 | [RegExpNormalConstant] d | semmle.order | 2 | +| intersection.js:7:1:7:13 | [RegExpLiteral] /[abc&&bcd]/v | intersection.js:7:2:7:11 | [RegExpCharacterClass] [abc&&bcd] | semmle.label | 0 | +| intersection.js:7:1:7:13 | [RegExpLiteral] /[abc&&bcd]/v | intersection.js:7:2:7:11 | [RegExpCharacterClass] [abc&&bcd] | semmle.order | 0 | +| intersection.js:7:1:7:14 | [ExprStmt] /[abc&&bcd]/v; | intersection.js:7:1:7:13 | [RegExpLiteral] /[abc&&bcd]/v | semmle.label | 1 | +| intersection.js:7:1:7:14 | [ExprStmt] /[abc&&bcd]/v; | intersection.js:7:1:7:13 | [RegExpLiteral] /[abc&&bcd]/v | semmle.order | 1 | +| intersection.js:7:2:7:11 | [RegExpCharacterClass] [abc&&bcd] | intersection.js:7:3:7:3 | [RegExpNormalConstant] a | semmle.label | 0 | +| intersection.js:7:2:7:11 | [RegExpCharacterClass] [abc&&bcd] | intersection.js:7:3:7:3 | [RegExpNormalConstant] a | semmle.order | 0 | +| intersection.js:7:2:7:11 | [RegExpCharacterClass] [abc&&bcd] | intersection.js:7:4:7:4 | [RegExpNormalConstant] b | semmle.label | 1 | +| intersection.js:7:2:7:11 | [RegExpCharacterClass] [abc&&bcd] | intersection.js:7:4:7:4 | [RegExpNormalConstant] b | semmle.order | 1 | +| intersection.js:7:2:7:11 | [RegExpCharacterClass] [abc&&bcd] | intersection.js:7:5:7:5 | [RegExpNormalConstant] c | semmle.label | 2 | +| intersection.js:7:2:7:11 | [RegExpCharacterClass] [abc&&bcd] | intersection.js:7:5:7:5 | [RegExpNormalConstant] c | semmle.order | 2 | +| intersection.js:7:2:7:11 | [RegExpCharacterClass] [abc&&bcd] | intersection.js:7:6:7:6 | [RegExpNormalConstant] & | semmle.label | 3 | +| intersection.js:7:2:7:11 | [RegExpCharacterClass] [abc&&bcd] | intersection.js:7:6:7:6 | [RegExpNormalConstant] & | semmle.order | 3 | +| intersection.js:7:2:7:11 | [RegExpCharacterClass] [abc&&bcd] | intersection.js:7:7:7:7 | [RegExpNormalConstant] & | semmle.label | 4 | +| intersection.js:7:2:7:11 | [RegExpCharacterClass] [abc&&bcd] | intersection.js:7:7:7:7 | [RegExpNormalConstant] & | semmle.order | 4 | +| intersection.js:7:2:7:11 | [RegExpCharacterClass] [abc&&bcd] | intersection.js:7:8:7:8 | [RegExpNormalConstant] b | semmle.label | 5 | +| intersection.js:7:2:7:11 | [RegExpCharacterClass] [abc&&bcd] | intersection.js:7:8:7:8 | [RegExpNormalConstant] b | semmle.order | 5 | +| intersection.js:7:2:7:11 | [RegExpCharacterClass] [abc&&bcd] | intersection.js:7:9:7:9 | [RegExpNormalConstant] c | semmle.label | 6 | +| intersection.js:7:2:7:11 | [RegExpCharacterClass] [abc&&bcd] | intersection.js:7:9:7:9 | [RegExpNormalConstant] c | semmle.order | 6 | +| intersection.js:7:2:7:11 | [RegExpCharacterClass] [abc&&bcd] | intersection.js:7:10:7:10 | [RegExpNormalConstant] d | semmle.label | 7 | +| intersection.js:7:2:7:11 | [RegExpCharacterClass] [abc&&bcd] | intersection.js:7:10:7:10 | [RegExpNormalConstant] d | semmle.order | 7 | +graphProperties +| semmle.graphKind | tree | diff --git a/javascript/ql/test/library-tests/RegExp/RegExpIntersection/printAst.ql b/javascript/ql/test/library-tests/RegExp/RegExpIntersection/printAst.ql new file mode 100644 index 000000000000..8ceaf83964a3 --- /dev/null +++ b/javascript/ql/test/library-tests/RegExp/RegExpIntersection/printAst.ql @@ -0,0 +1 @@ +import semmle.javascript.PrintAst From 4122b60f3e1c534c2347d015043aec48507e6c7a Mon Sep 17 00:00:00 2001 From: Napalys Date: Thu, 27 Feb 2025 08:49:45 +0100 Subject: [PATCH 5/9] Add RegExpIntersection class to support intersection terms in regex --- .../javascript/MembershipCandidates.qll | 1 + .../ql/lib/semmle/javascript/Regexp.qll | 23 +++++++++++++++ .../RegExpIntersection/printAst.expected | 28 +++++++++---------- 3 files changed, 38 insertions(+), 14 deletions(-) diff --git a/javascript/ql/lib/semmle/javascript/MembershipCandidates.qll b/javascript/ql/lib/semmle/javascript/MembershipCandidates.qll index da9e90744ef0..45680c0d49f1 100644 --- a/javascript/ql/lib/semmle/javascript/MembershipCandidates.qll +++ b/javascript/ql/lib/semmle/javascript/MembershipCandidates.qll @@ -146,6 +146,7 @@ module MembershipCandidate { child instanceof RegExpDollar or child instanceof RegExpConstant or child instanceof RegExpAlt or + child instanceof RegExpIntersection or child instanceof RegExpGroup ) and // exclude "length matches" that match every string diff --git a/javascript/ql/lib/semmle/javascript/Regexp.qll b/javascript/ql/lib/semmle/javascript/Regexp.qll index acfc888756e8..e84fcacf7e15 100644 --- a/javascript/ql/lib/semmle/javascript/Regexp.qll +++ b/javascript/ql/lib/semmle/javascript/Regexp.qll @@ -301,6 +301,29 @@ class RegExpAlt extends RegExpTerm, @regexp_alt { override string getAPrimaryQlClass() { result = "RegExpAlt" } } +/** + * An intersection term, that is, a term of the form `a&&b`. + * + * Example: + * + * ``` + * /\p{Script_Extensions=Greek}&&\p{Letter}/v + * ``` + */ +class RegExpIntersection extends RegExpTerm, @regexp_intersection { + /** Gets an intersected term of this term. */ + RegExpTerm getIntersectedTerm() { result = this.getAChild() } + + /** Gets the number of intersected terms of this term. */ + int getNumIntersectedTerm() { result = this.getNumChild() } + + override predicate isNullable() { this.getIntersectedTerm().isNullable() } + + override string getAMatchedString() { result = this.getIntersectedTerm().getAMatchedString() } + + override string getAPrimaryQlClass() { result = "RegExpIntersection" } +} + /** * A sequence term. * diff --git a/javascript/ql/test/library-tests/RegExp/RegExpIntersection/printAst.expected b/javascript/ql/test/library-tests/RegExp/RegExpIntersection/printAst.expected index 1bc5ab38e265..be3cf50afa67 100644 --- a/javascript/ql/test/library-tests/RegExp/RegExpIntersection/printAst.expected +++ b/javascript/ql/test/library-tests/RegExp/RegExpIntersection/printAst.expected @@ -3,7 +3,7 @@ nodes | intersection.js:1:1:1:18 | [ExprStmt] /[[abc]&&[bcd]]/v; | semmle.label | [ExprStmt] /[[abc]&&[bcd]]/v; | | intersection.js:1:1:1:18 | [ExprStmt] /[[abc]&&[bcd]]/v; | semmle.order | 1 | | intersection.js:1:2:1:7 | [RegExpCharacterClass] [[abc] | semmle.label | [RegExpCharacterClass] [[abc] | -| intersection.js:1:2:1:15 | [???] [[abc]&&[bcd]] | semmle.label | [???] [[abc]&&[bcd]] | +| intersection.js:1:2:1:15 | [RegExpIntersection] [[abc]&&[bcd]] | semmle.label | [RegExpIntersection] [[abc]&&[bcd]] | | intersection.js:1:3:1:3 | [RegExpNormalConstant] [ | semmle.label | [RegExpNormalConstant] [ | | intersection.js:1:4:1:4 | [RegExpNormalConstant] a | semmle.label | [RegExpNormalConstant] a | | intersection.js:1:5:1:5 | [RegExpNormalConstant] b | semmle.label | [RegExpNormalConstant] b | @@ -22,7 +22,7 @@ nodes | intersection.js:3:1:3:16 | [ExprStmt] /[abc]&&[bcd]/v; | semmle.label | [ExprStmt] /[abc]&&[bcd]/v; | | intersection.js:3:1:3:16 | [ExprStmt] /[abc]&&[bcd]/v; | semmle.order | 3 | | intersection.js:3:2:3:6 | [RegExpCharacterClass] [abc] | semmle.label | [RegExpCharacterClass] [abc] | -| intersection.js:3:2:3:13 | [???] [abc]&&[bcd] | semmle.label | [???] [abc]&&[bcd] | +| intersection.js:3:2:3:13 | [RegExpIntersection] [abc]&&[bcd] | semmle.label | [RegExpIntersection] [abc]&&[bcd] | | intersection.js:3:3:3:3 | [RegExpNormalConstant] a | semmle.label | [RegExpNormalConstant] a | | intersection.js:3:4:3:4 | [RegExpNormalConstant] b | semmle.label | [RegExpNormalConstant] b | | intersection.js:3:5:3:5 | [RegExpNormalConstant] c | semmle.label | [RegExpNormalConstant] c | @@ -43,8 +43,8 @@ nodes | intersection.js:7:9:7:9 | [RegExpNormalConstant] c | semmle.label | [RegExpNormalConstant] c | | intersection.js:7:10:7:10 | [RegExpNormalConstant] d | semmle.label | [RegExpNormalConstant] d | edges -| intersection.js:1:1:1:17 | [RegExpLiteral] /[[abc]&&[bcd]]/v | intersection.js:1:2:1:15 | [???] [[abc]&&[bcd]] | semmle.label | 0 | -| intersection.js:1:1:1:17 | [RegExpLiteral] /[[abc]&&[bcd]]/v | intersection.js:1:2:1:15 | [???] [[abc]&&[bcd]] | semmle.order | 0 | +| intersection.js:1:1:1:17 | [RegExpLiteral] /[[abc]&&[bcd]]/v | intersection.js:1:2:1:15 | [RegExpIntersection] [[abc]&&[bcd]] | semmle.label | 0 | +| intersection.js:1:1:1:17 | [RegExpLiteral] /[[abc]&&[bcd]]/v | intersection.js:1:2:1:15 | [RegExpIntersection] [[abc]&&[bcd]] | semmle.order | 0 | | intersection.js:1:1:1:18 | [ExprStmt] /[[abc]&&[bcd]]/v; | intersection.js:1:1:1:17 | [RegExpLiteral] /[[abc]&&[bcd]]/v | semmle.label | 1 | | intersection.js:1:1:1:18 | [ExprStmt] /[[abc]&&[bcd]]/v; | intersection.js:1:1:1:17 | [RegExpLiteral] /[[abc]&&[bcd]]/v | semmle.order | 1 | | intersection.js:1:2:1:7 | [RegExpCharacterClass] [[abc] | intersection.js:1:3:1:3 | [RegExpNormalConstant] [ | semmle.label | 0 | @@ -55,10 +55,10 @@ edges | intersection.js:1:2:1:7 | [RegExpCharacterClass] [[abc] | intersection.js:1:5:1:5 | [RegExpNormalConstant] b | semmle.order | 2 | | intersection.js:1:2:1:7 | [RegExpCharacterClass] [[abc] | intersection.js:1:6:1:6 | [RegExpNormalConstant] c | semmle.label | 3 | | intersection.js:1:2:1:7 | [RegExpCharacterClass] [[abc] | intersection.js:1:6:1:6 | [RegExpNormalConstant] c | semmle.order | 3 | -| intersection.js:1:2:1:15 | [???] [[abc]&&[bcd]] | intersection.js:1:2:1:7 | [RegExpCharacterClass] [[abc] | semmle.label | 0 | -| intersection.js:1:2:1:15 | [???] [[abc]&&[bcd]] | intersection.js:1:2:1:7 | [RegExpCharacterClass] [[abc] | semmle.order | 0 | -| intersection.js:1:2:1:15 | [???] [[abc]&&[bcd]] | intersection.js:1:10:1:15 | [RegExpSequence] [bcd]] | semmle.label | 1 | -| intersection.js:1:2:1:15 | [???] [[abc]&&[bcd]] | intersection.js:1:10:1:15 | [RegExpSequence] [bcd]] | semmle.order | 1 | +| intersection.js:1:2:1:15 | [RegExpIntersection] [[abc]&&[bcd]] | intersection.js:1:2:1:7 | [RegExpCharacterClass] [[abc] | semmle.label | 0 | +| intersection.js:1:2:1:15 | [RegExpIntersection] [[abc]&&[bcd]] | intersection.js:1:2:1:7 | [RegExpCharacterClass] [[abc] | semmle.order | 0 | +| intersection.js:1:2:1:15 | [RegExpIntersection] [[abc]&&[bcd]] | intersection.js:1:10:1:15 | [RegExpSequence] [bcd]] | semmle.label | 1 | +| intersection.js:1:2:1:15 | [RegExpIntersection] [[abc]&&[bcd]] | intersection.js:1:10:1:15 | [RegExpSequence] [bcd]] | semmle.order | 1 | | intersection.js:1:10:1:14 | [RegExpCharacterClass] [bcd] | intersection.js:1:11:1:11 | [RegExpNormalConstant] b | semmle.label | 0 | | intersection.js:1:10:1:14 | [RegExpCharacterClass] [bcd] | intersection.js:1:11:1:11 | [RegExpNormalConstant] b | semmle.order | 0 | | intersection.js:1:10:1:14 | [RegExpCharacterClass] [bcd] | intersection.js:1:12:1:12 | [RegExpNormalConstant] c | semmle.label | 1 | @@ -73,8 +73,8 @@ edges | intersection.js:2:1:2:11 | [RegExpLiteral] /abc&&bcd/v | intersection.js:2:2:2:9 | [RegExpNormalConstant] abc&&bcd | semmle.order | 0 | | intersection.js:2:1:2:12 | [ExprStmt] /abc&&bcd/v; | intersection.js:2:1:2:11 | [RegExpLiteral] /abc&&bcd/v | semmle.label | 1 | | intersection.js:2:1:2:12 | [ExprStmt] /abc&&bcd/v; | intersection.js:2:1:2:11 | [RegExpLiteral] /abc&&bcd/v | semmle.order | 1 | -| intersection.js:3:1:3:15 | [RegExpLiteral] /[abc]&&[bcd]/v | intersection.js:3:2:3:13 | [???] [abc]&&[bcd] | semmle.label | 0 | -| intersection.js:3:1:3:15 | [RegExpLiteral] /[abc]&&[bcd]/v | intersection.js:3:2:3:13 | [???] [abc]&&[bcd] | semmle.order | 0 | +| intersection.js:3:1:3:15 | [RegExpLiteral] /[abc]&&[bcd]/v | intersection.js:3:2:3:13 | [RegExpIntersection] [abc]&&[bcd] | semmle.label | 0 | +| intersection.js:3:1:3:15 | [RegExpLiteral] /[abc]&&[bcd]/v | intersection.js:3:2:3:13 | [RegExpIntersection] [abc]&&[bcd] | semmle.order | 0 | | intersection.js:3:1:3:16 | [ExprStmt] /[abc]&&[bcd]/v; | intersection.js:3:1:3:15 | [RegExpLiteral] /[abc]&&[bcd]/v | semmle.label | 1 | | intersection.js:3:1:3:16 | [ExprStmt] /[abc]&&[bcd]/v; | intersection.js:3:1:3:15 | [RegExpLiteral] /[abc]&&[bcd]/v | semmle.order | 1 | | intersection.js:3:2:3:6 | [RegExpCharacterClass] [abc] | intersection.js:3:3:3:3 | [RegExpNormalConstant] a | semmle.label | 0 | @@ -83,10 +83,10 @@ edges | intersection.js:3:2:3:6 | [RegExpCharacterClass] [abc] | intersection.js:3:4:3:4 | [RegExpNormalConstant] b | semmle.order | 1 | | intersection.js:3:2:3:6 | [RegExpCharacterClass] [abc] | intersection.js:3:5:3:5 | [RegExpNormalConstant] c | semmle.label | 2 | | intersection.js:3:2:3:6 | [RegExpCharacterClass] [abc] | intersection.js:3:5:3:5 | [RegExpNormalConstant] c | semmle.order | 2 | -| intersection.js:3:2:3:13 | [???] [abc]&&[bcd] | intersection.js:3:2:3:6 | [RegExpCharacterClass] [abc] | semmle.label | 0 | -| intersection.js:3:2:3:13 | [???] [abc]&&[bcd] | intersection.js:3:2:3:6 | [RegExpCharacterClass] [abc] | semmle.order | 0 | -| intersection.js:3:2:3:13 | [???] [abc]&&[bcd] | intersection.js:3:9:3:13 | [RegExpCharacterClass] [bcd] | semmle.label | 1 | -| intersection.js:3:2:3:13 | [???] [abc]&&[bcd] | intersection.js:3:9:3:13 | [RegExpCharacterClass] [bcd] | semmle.order | 1 | +| intersection.js:3:2:3:13 | [RegExpIntersection] [abc]&&[bcd] | intersection.js:3:2:3:6 | [RegExpCharacterClass] [abc] | semmle.label | 0 | +| intersection.js:3:2:3:13 | [RegExpIntersection] [abc]&&[bcd] | intersection.js:3:2:3:6 | [RegExpCharacterClass] [abc] | semmle.order | 0 | +| intersection.js:3:2:3:13 | [RegExpIntersection] [abc]&&[bcd] | intersection.js:3:9:3:13 | [RegExpCharacterClass] [bcd] | semmle.label | 1 | +| intersection.js:3:2:3:13 | [RegExpIntersection] [abc]&&[bcd] | intersection.js:3:9:3:13 | [RegExpCharacterClass] [bcd] | semmle.order | 1 | | intersection.js:3:9:3:13 | [RegExpCharacterClass] [bcd] | intersection.js:3:10:3:10 | [RegExpNormalConstant] b | semmle.label | 0 | | intersection.js:3:9:3:13 | [RegExpCharacterClass] [bcd] | intersection.js:3:10:3:10 | [RegExpNormalConstant] b | semmle.order | 0 | | intersection.js:3:9:3:13 | [RegExpCharacterClass] [bcd] | intersection.js:3:11:3:11 | [RegExpNormalConstant] c | semmle.label | 1 | From 14e80e461067787fb38c2089f8d75860e29cdd82 Mon Sep 17 00:00:00 2001 From: Napalys Date: Thu, 27 Feb 2025 10:08:38 +0100 Subject: [PATCH 6/9] Added change note. --- javascript/ql/lib/change-notes/2025-02-27-regex-v-flag.md | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 javascript/ql/lib/change-notes/2025-02-27-regex-v-flag.md diff --git a/javascript/ql/lib/change-notes/2025-02-27-regex-v-flag.md b/javascript/ql/lib/change-notes/2025-02-27-regex-v-flag.md new file mode 100644 index 000000000000..fc5281566e76 --- /dev/null +++ b/javascript/ql/lib/change-notes/2025-02-27-regex-v-flag.md @@ -0,0 +1,4 @@ +--- +category: minorAnalysis +--- +* Added support for ECMAScript 2024 `v` Flag Intersection `&&` operator. From 4a034107630728a2a9aa539c92c1e5206bc98021 Mon Sep 17 00:00:00 2001 From: Napalys Date: Thu, 27 Feb 2025 13:16:42 +0100 Subject: [PATCH 7/9] test --- .../com/semmle/js/parser/RegExpParser.java | 8 +- .../tests/es2024/input/intersection.js | 8 +- .../es2024/output/trap/intersection.js.trap | 445 ++++++++++++++---- 3 files changed, 373 insertions(+), 88 deletions(-) diff --git a/javascript/extractor/src/com/semmle/js/parser/RegExpParser.java b/javascript/extractor/src/com/semmle/js/parser/RegExpParser.java index 164e64321a0b..487ad7c74416 100644 --- a/javascript/extractor/src/com/semmle/js/parser/RegExpParser.java +++ b/javascript/extractor/src/com/semmle/js/parser/RegExpParser.java @@ -226,18 +226,18 @@ private T finishTerm(T term) { private RegExpTerm parseDisjunction() { SourceLocation loc = new SourceLocation(pos()); List disjuncts = new ArrayList<>(); - disjuncts.add(this.parseIntersection()); + disjuncts.add(this.parseAlternative()); while (this.match("|")) { - disjuncts.add(this.parseIntersection()); + disjuncts.add(this.parseAlternative()); } if (disjuncts.size() == 1) return disjuncts.get(0); return this.finishTerm(new Disjunction(loc, disjuncts)); -} + } private RegExpTerm parseAlternative() { SourceLocation loc = new SourceLocation(pos()); List elements = new ArrayList<>(); - while (!this.lookahead(null, "|", "&&", ")")) elements.add(this.parseTerm()); + while (!this.lookahead(null, "|", ")")) elements.add(this.parseTerm()); if (elements.size() == 1) return elements.get(0); return this.finishTerm(new Sequence(loc, elements)); } diff --git a/javascript/extractor/tests/es2024/input/intersection.js b/javascript/extractor/tests/es2024/input/intersection.js index bb990b8fe8c4..5f289de0fd16 100644 --- a/javascript/extractor/tests/es2024/input/intersection.js +++ b/javascript/extractor/tests/es2024/input/intersection.js @@ -1 +1,7 @@ -/[[abc]&&[bcd]]/v +/[[abc]&&[bcd]]/v; // Valid use of intersection operator, matches b or c +/abc&&bcd/v; //Valid regex, but no intersection operation: Matches the literal string "abc&&bcd" +/[abc]&&[bcd]/v; // Valid regex, but incorrect intersection operation: + // - Matches a single character from [abc] + // - Then the literal "&&" + // - Then a single character from [bcd] +/[abc&&bcd]/v; // Compilation error due to invalid use of intersection operation diff --git a/javascript/extractor/tests/es2024/output/trap/intersection.js.trap b/javascript/extractor/tests/es2024/output/trap/intersection.js.trap index 2d55ec1287c5..a19310ff4bb1 100644 --- a/javascript/extractor/tests/es2024/output/trap/intersection.js.trap +++ b/javascript/extractor/tests/es2024/output/trap/intersection.js.trap @@ -10,115 +10,394 @@ hasLocation(#10000,#10002) scopes(#20000,0) #20001=@"script;{#10000},1,1" #20002=* -lines(#20002,#20001,"/[[abc]&&[bcd]]/v"," -") -#20003=@"loc,{#10000},1,1,1,17" -locations_default(#20003,#10000,1,1,1,17) +comments(#20002,0,#20001," Valid use of intersection operator, matches b or c","// Vali ... b or c") +#20003=@"loc,{#10000},1,20,1,72" +locations_default(#20003,#10000,1,20,1,72) hasLocation(#20002,#20003) -numlines(#20001,1,1,0) #20004=* -tokeninfo(#20004,5,#20001,0,"/[[abc]&&[bcd]]/v") -hasLocation(#20004,#20003) -#20005=* -tokeninfo(#20005,0,#20001,1,"") -#20006=@"loc,{#10000},2,1,2,0" -locations_default(#20006,#10000,2,1,2,0) -hasLocation(#20005,#20006) -toplevels(#20001,0) -#20007=@"loc,{#10000},1,1,2,0" -locations_default(#20007,#10000,1,1,2,0) -hasLocation(#20001,#20007) +comments(#20004,0,#20001,"Valid regex, but no intersection operation: Matches the literal string ""abc&&bcd""","//Valid ... c&&bcd""") +#20005=@"loc,{#10000},2,14,2,96" +locations_default(#20005,#10000,2,14,2,96) +hasLocation(#20004,#20005) +#20006=* +comments(#20006,0,#20001," Valid regex, but incorrect intersection operation: ","// Vali ... ation: ") +#20007=@"loc,{#10000},3,18,3,71" +locations_default(#20007,#10000,3,18,3,71) +hasLocation(#20006,#20007) #20008=* -stmts(#20008,2,#20001,0,"/[[abc]&&[bcd]]/v") -hasLocation(#20008,#20003) -stmt_containers(#20008,#20001) -#20009=* -exprs(#20009,5,#20008,0,"/[[abc]&&[bcd]]/v") -hasLocation(#20009,#20003) -enclosing_stmt(#20009,#20008) -expr_containers(#20009,#20001) -literals("/[[abc]&&[bcd]]/v","/[[abc]&&[bcd]]/v",#20009) +comments(#20008,0,#20001," - Matches a single character from [abc]","// - Ma ... m [abc]") +#20009=@"loc,{#10000},4,18,4,59" +locations_default(#20009,#10000,4,18,4,59) +hasLocation(#20008,#20009) #20010=* -regexpterm(#20010,28,#20009,0,"[[abc]&&[bcd]]") -#20011=@"loc,{#10000},1,2,1,15" -locations_default(#20011,#10000,1,2,1,15) +comments(#20010,0,#20001," - Then the literal ""&&""","// - Th ... al ""&&""") +#20011=@"loc,{#10000},5,18,5,43" +locations_default(#20011,#10000,5,18,5,43) hasLocation(#20010,#20011) #20012=* -regexpterm(#20012,23,#20010,0,"[[abc]") -#20013=@"loc,{#10000},1,2,1,7" -locations_default(#20013,#10000,1,2,1,7) +comments(#20012,0,#20001," - Then a single character from [bcd]","// - Th ... m [bcd]") +#20013=@"loc,{#10000},6,18,6,56" +locations_default(#20013,#10000,6,18,6,56) hasLocation(#20012,#20013) #20014=* -regexpterm(#20014,14,#20012,0,"[") -#20015=@"loc,{#10000},1,3,1,3" -locations_default(#20015,#10000,1,3,1,3) +comments(#20014,0,#20001," Compilation error due to invalid use of intersection operation","// Comp ... eration") +#20015=@"loc,{#10000},7,16,7,80" +locations_default(#20015,#10000,7,16,7,80) hasLocation(#20014,#20015) -regexp_const_value(#20014,"[") #20016=* -regexpterm(#20016,14,#20012,1,"a") -#20017=@"loc,{#10000},1,4,1,4" -locations_default(#20017,#10000,1,4,1,4) +lines(#20016,#20001,"/[[abc]&&[bcd]]/v; // Valid use of intersection operator, matches b or c"," +") +#20017=@"loc,{#10000},1,1,1,72" +locations_default(#20017,#10000,1,1,1,72) hasLocation(#20016,#20017) -regexp_const_value(#20016,"a") #20018=* -regexpterm(#20018,14,#20012,2,"b") -#20019=@"loc,{#10000},1,5,1,5" -locations_default(#20019,#10000,1,5,1,5) +lines(#20018,#20001,"/abc&&bcd/v; //Valid regex, but no intersection operation: Matches the literal string ""abc&&bcd"""," +") +#20019=@"loc,{#10000},2,1,2,96" +locations_default(#20019,#10000,2,1,2,96) hasLocation(#20018,#20019) -regexp_const_value(#20018,"b") #20020=* -regexpterm(#20020,14,#20012,3,"c") -#20021=@"loc,{#10000},1,6,1,6" -locations_default(#20021,#10000,1,6,1,6) +lines(#20020,#20001,"/[abc]&&[bcd]/v; // Valid regex, but incorrect intersection operation: "," +") +#20021=@"loc,{#10000},3,1,3,71" +locations_default(#20021,#10000,3,1,3,71) hasLocation(#20020,#20021) -regexp_const_value(#20020,"c") #20022=* -regexpterm(#20022,1,#20010,1,"[bcd]]") -#20023=@"loc,{#10000},1,10,1,15" -locations_default(#20023,#10000,1,10,1,15) +lines(#20022,#20001," // - Matches a single character from [abc]"," +") +#20023=@"loc,{#10000},4,1,4,59" +locations_default(#20023,#10000,4,1,4,59) hasLocation(#20022,#20023) +indentation(#10000,4," ",17) #20024=* -regexpterm(#20024,23,#20022,0,"[bcd]") -#20025=@"loc,{#10000},1,10,1,14" -locations_default(#20025,#10000,1,10,1,14) +lines(#20024,#20001," // - Then the literal ""&&"""," +") +#20025=@"loc,{#10000},5,1,5,43" +locations_default(#20025,#10000,5,1,5,43) hasLocation(#20024,#20025) +indentation(#10000,5," ",17) #20026=* -regexpterm(#20026,14,#20024,0,"b") -#20027=@"loc,{#10000},1,11,1,11" -locations_default(#20027,#10000,1,11,1,11) +lines(#20026,#20001," // - Then a single character from [bcd]"," +") +#20027=@"loc,{#10000},6,1,6,56" +locations_default(#20027,#10000,6,1,6,56) hasLocation(#20026,#20027) -regexp_const_value(#20026,"b") +indentation(#10000,6," ",17) #20028=* -regexpterm(#20028,14,#20024,1,"c") -#20029=@"loc,{#10000},1,12,1,12" -locations_default(#20029,#10000,1,12,1,12) +lines(#20028,#20001,"/[abc&&bcd]/v; // Compilation error due to invalid use of intersection operation"," +") +#20029=@"loc,{#10000},7,1,7,80" +locations_default(#20029,#10000,7,1,7,80) hasLocation(#20028,#20029) -regexp_const_value(#20028,"c") +numlines(#20001,7,4,7) #20030=* -regexpterm(#20030,14,#20024,2,"d") -#20031=@"loc,{#10000},1,13,1,13" -locations_default(#20031,#10000,1,13,1,13) +tokeninfo(#20030,5,#20001,0,"/[[abc]&&[bcd]]/v") +#20031=@"loc,{#10000},1,1,1,17" +locations_default(#20031,#10000,1,1,1,17) hasLocation(#20030,#20031) -regexp_const_value(#20030,"d") #20032=* -regexpterm(#20032,14,#20022,1,"]") -#20033=@"loc,{#10000},1,15,1,15" -locations_default(#20033,#10000,1,15,1,15) +tokeninfo(#20032,8,#20001,1,";") +#20033=@"loc,{#10000},1,18,1,18" +locations_default(#20033,#10000,1,18,1,18) hasLocation(#20032,#20033) -regexp_const_value(#20032,"]") #20034=* -regexp_parse_errors(#20034,#20010,"unexpected character") -hasLocation(#20034,#20033) -#20035=* -entry_cfg_node(#20035,#20001) -#20036=@"loc,{#10000},1,1,1,0" -locations_default(#20036,#10000,1,1,1,0) -hasLocation(#20035,#20036) -#20037=* -exit_cfg_node(#20037,#20001) -hasLocation(#20037,#20006) -successor(#20008,#20009) -successor(#20009,#20037) -successor(#20035,#20008) -numlines(#10000,1,1,0) +tokeninfo(#20034,5,#20001,2,"/abc&&bcd/v") +#20035=@"loc,{#10000},2,1,2,11" +locations_default(#20035,#10000,2,1,2,11) +hasLocation(#20034,#20035) +next_token(#20002,#20034) +#20036=* +tokeninfo(#20036,8,#20001,3,";") +#20037=@"loc,{#10000},2,12,2,12" +locations_default(#20037,#10000,2,12,2,12) +hasLocation(#20036,#20037) +#20038=* +tokeninfo(#20038,5,#20001,4,"/[abc]&&[bcd]/v") +#20039=@"loc,{#10000},3,1,3,15" +locations_default(#20039,#10000,3,1,3,15) +hasLocation(#20038,#20039) +next_token(#20004,#20038) +#20040=* +tokeninfo(#20040,8,#20001,5,";") +#20041=@"loc,{#10000},3,16,3,16" +locations_default(#20041,#10000,3,16,3,16) +hasLocation(#20040,#20041) +#20042=* +tokeninfo(#20042,5,#20001,6,"/[abc&&bcd]/v") +#20043=@"loc,{#10000},7,1,7,13" +locations_default(#20043,#10000,7,1,7,13) +hasLocation(#20042,#20043) +next_token(#20006,#20042) +next_token(#20008,#20042) +next_token(#20010,#20042) +next_token(#20012,#20042) +#20044=* +tokeninfo(#20044,8,#20001,7,";") +#20045=@"loc,{#10000},7,14,7,14" +locations_default(#20045,#10000,7,14,7,14) +hasLocation(#20044,#20045) +#20046=* +tokeninfo(#20046,0,#20001,8,"") +#20047=@"loc,{#10000},8,1,8,0" +locations_default(#20047,#10000,8,1,8,0) +hasLocation(#20046,#20047) +next_token(#20014,#20046) +toplevels(#20001,0) +#20048=@"loc,{#10000},1,1,8,0" +locations_default(#20048,#10000,1,1,8,0) +hasLocation(#20001,#20048) +#20049=* +stmts(#20049,2,#20001,0,"/[[abc]&&[bcd]]/v;") +#20050=@"loc,{#10000},1,1,1,18" +locations_default(#20050,#10000,1,1,1,18) +hasLocation(#20049,#20050) +stmt_containers(#20049,#20001) +#20051=* +exprs(#20051,5,#20049,0,"/[[abc]&&[bcd]]/v") +hasLocation(#20051,#20031) +enclosing_stmt(#20051,#20049) +expr_containers(#20051,#20001) +literals("/[[abc]&&[bcd]]/v","/[[abc]&&[bcd]]/v",#20051) +#20052=* +regexpterm(#20052,1,#20051,0,"[[abc]&&[bcd]]") +#20053=@"loc,{#10000},1,2,1,15" +locations_default(#20053,#10000,1,2,1,15) +hasLocation(#20052,#20053) +#20054=* +regexpterm(#20054,23,#20052,0,"[[abc]") +#20055=@"loc,{#10000},1,2,1,7" +locations_default(#20055,#10000,1,2,1,7) +hasLocation(#20054,#20055) +#20056=* +regexpterm(#20056,14,#20054,0,"[") +#20057=@"loc,{#10000},1,3,1,3" +locations_default(#20057,#10000,1,3,1,3) +hasLocation(#20056,#20057) +regexp_const_value(#20056,"[") +#20058=* +regexpterm(#20058,14,#20054,1,"a") +#20059=@"loc,{#10000},1,4,1,4" +locations_default(#20059,#10000,1,4,1,4) +hasLocation(#20058,#20059) +regexp_const_value(#20058,"a") +#20060=* +regexpterm(#20060,14,#20054,2,"b") +#20061=@"loc,{#10000},1,5,1,5" +locations_default(#20061,#10000,1,5,1,5) +hasLocation(#20060,#20061) +regexp_const_value(#20060,"b") +#20062=* +regexpterm(#20062,14,#20054,3,"c") +#20063=@"loc,{#10000},1,6,1,6" +locations_default(#20063,#10000,1,6,1,6) +hasLocation(#20062,#20063) +regexp_const_value(#20062,"c") +#20064=* +regexpterm(#20064,14,#20052,1,"&&") +#20065=@"loc,{#10000},1,8,1,9" +locations_default(#20065,#10000,1,8,1,9) +hasLocation(#20064,#20065) +regexp_const_value(#20064,"&&") +#20066=* +regexpterm(#20066,23,#20052,2,"[bcd]") +#20067=@"loc,{#10000},1,10,1,14" +locations_default(#20067,#10000,1,10,1,14) +hasLocation(#20066,#20067) +#20068=* +regexpterm(#20068,14,#20066,0,"b") +#20069=@"loc,{#10000},1,11,1,11" +locations_default(#20069,#10000,1,11,1,11) +hasLocation(#20068,#20069) +regexp_const_value(#20068,"b") +#20070=* +regexpterm(#20070,14,#20066,1,"c") +#20071=@"loc,{#10000},1,12,1,12" +locations_default(#20071,#10000,1,12,1,12) +hasLocation(#20070,#20071) +regexp_const_value(#20070,"c") +#20072=* +regexpterm(#20072,14,#20066,2,"d") +#20073=@"loc,{#10000},1,13,1,13" +locations_default(#20073,#10000,1,13,1,13) +hasLocation(#20072,#20073) +regexp_const_value(#20072,"d") +#20074=* +regexpterm(#20074,14,#20052,3,"]") +#20075=@"loc,{#10000},1,15,1,15" +locations_default(#20075,#10000,1,15,1,15) +hasLocation(#20074,#20075) +regexp_const_value(#20074,"]") +#20076=* +regexp_parse_errors(#20076,#20052,"unexpected character") +hasLocation(#20076,#20075) +#20077=* +stmts(#20077,2,#20001,1,"/abc&&bcd/v;") +#20078=@"loc,{#10000},2,1,2,12" +locations_default(#20078,#10000,2,1,2,12) +hasLocation(#20077,#20078) +stmt_containers(#20077,#20001) +#20079=* +exprs(#20079,5,#20077,0,"/abc&&bcd/v") +hasLocation(#20079,#20035) +enclosing_stmt(#20079,#20077) +expr_containers(#20079,#20001) +literals("/abc&&bcd/v","/abc&&bcd/v",#20079) +#20080=* +regexpterm(#20080,14,#20079,0,"abc&&bcd") +#20081=@"loc,{#10000},2,2,2,9" +locations_default(#20081,#10000,2,2,2,9) +hasLocation(#20080,#20081) +regexp_const_value(#20080,"abc&&bcd") +#20082=* +stmts(#20082,2,#20001,2,"/[abc]&&[bcd]/v;") +#20083=@"loc,{#10000},3,1,3,16" +locations_default(#20083,#10000,3,1,3,16) +hasLocation(#20082,#20083) +stmt_containers(#20082,#20001) +#20084=* +exprs(#20084,5,#20082,0,"/[abc]&&[bcd]/v") +hasLocation(#20084,#20039) +enclosing_stmt(#20084,#20082) +expr_containers(#20084,#20001) +literals("/[abc]&&[bcd]/v","/[abc]&&[bcd]/v",#20084) +#20085=* +regexpterm(#20085,1,#20084,0,"[abc]&&[bcd]") +#20086=@"loc,{#10000},3,2,3,13" +locations_default(#20086,#10000,3,2,3,13) +hasLocation(#20085,#20086) +#20087=* +regexpterm(#20087,23,#20085,0,"[abc]") +#20088=@"loc,{#10000},3,2,3,6" +locations_default(#20088,#10000,3,2,3,6) +hasLocation(#20087,#20088) +#20089=* +regexpterm(#20089,14,#20087,0,"a") +#20090=@"loc,{#10000},3,3,3,3" +locations_default(#20090,#10000,3,3,3,3) +hasLocation(#20089,#20090) +regexp_const_value(#20089,"a") +#20091=* +regexpterm(#20091,14,#20087,1,"b") +#20092=@"loc,{#10000},3,4,3,4" +locations_default(#20092,#10000,3,4,3,4) +hasLocation(#20091,#20092) +regexp_const_value(#20091,"b") +#20093=* +regexpterm(#20093,14,#20087,2,"c") +#20094=@"loc,{#10000},3,5,3,5" +locations_default(#20094,#10000,3,5,3,5) +hasLocation(#20093,#20094) +regexp_const_value(#20093,"c") +#20095=* +regexpterm(#20095,14,#20085,1,"&&") +#20096=@"loc,{#10000},3,7,3,8" +locations_default(#20096,#10000,3,7,3,8) +hasLocation(#20095,#20096) +regexp_const_value(#20095,"&&") +#20097=* +regexpterm(#20097,23,#20085,2,"[bcd]") +#20098=@"loc,{#10000},3,9,3,13" +locations_default(#20098,#10000,3,9,3,13) +hasLocation(#20097,#20098) +#20099=* +regexpterm(#20099,14,#20097,0,"b") +#20100=@"loc,{#10000},3,10,3,10" +locations_default(#20100,#10000,3,10,3,10) +hasLocation(#20099,#20100) +regexp_const_value(#20099,"b") +#20101=* +regexpterm(#20101,14,#20097,1,"c") +#20102=@"loc,{#10000},3,11,3,11" +locations_default(#20102,#10000,3,11,3,11) +hasLocation(#20101,#20102) +regexp_const_value(#20101,"c") +#20103=* +regexpterm(#20103,14,#20097,2,"d") +#20104=@"loc,{#10000},3,12,3,12" +locations_default(#20104,#10000,3,12,3,12) +hasLocation(#20103,#20104) +regexp_const_value(#20103,"d") +#20105=* +stmts(#20105,2,#20001,3,"/[abc&&bcd]/v;") +#20106=@"loc,{#10000},7,1,7,14" +locations_default(#20106,#10000,7,1,7,14) +hasLocation(#20105,#20106) +stmt_containers(#20105,#20001) +#20107=* +exprs(#20107,5,#20105,0,"/[abc&&bcd]/v") +hasLocation(#20107,#20043) +enclosing_stmt(#20107,#20105) +expr_containers(#20107,#20001) +literals("/[abc&&bcd]/v","/[abc&&bcd]/v",#20107) +#20108=* +regexpterm(#20108,23,#20107,0,"[abc&&bcd]") +#20109=@"loc,{#10000},7,2,7,11" +locations_default(#20109,#10000,7,2,7,11) +hasLocation(#20108,#20109) +#20110=* +regexpterm(#20110,14,#20108,0,"a") +#20111=@"loc,{#10000},7,3,7,3" +locations_default(#20111,#10000,7,3,7,3) +hasLocation(#20110,#20111) +regexp_const_value(#20110,"a") +#20112=* +regexpterm(#20112,14,#20108,1,"b") +#20113=@"loc,{#10000},7,4,7,4" +locations_default(#20113,#10000,7,4,7,4) +hasLocation(#20112,#20113) +regexp_const_value(#20112,"b") +#20114=* +regexpterm(#20114,14,#20108,2,"c") +#20115=@"loc,{#10000},7,5,7,5" +locations_default(#20115,#10000,7,5,7,5) +hasLocation(#20114,#20115) +regexp_const_value(#20114,"c") +#20116=* +regexpterm(#20116,14,#20108,3,"&") +#20117=@"loc,{#10000},7,6,7,6" +locations_default(#20117,#10000,7,6,7,6) +hasLocation(#20116,#20117) +regexp_const_value(#20116,"&") +#20118=* +regexpterm(#20118,14,#20108,4,"&") +#20119=@"loc,{#10000},7,7,7,7" +locations_default(#20119,#10000,7,7,7,7) +hasLocation(#20118,#20119) +regexp_const_value(#20118,"&") +#20120=* +regexpterm(#20120,14,#20108,5,"b") +#20121=@"loc,{#10000},7,8,7,8" +locations_default(#20121,#10000,7,8,7,8) +hasLocation(#20120,#20121) +regexp_const_value(#20120,"b") +#20122=* +regexpterm(#20122,14,#20108,6,"c") +#20123=@"loc,{#10000},7,9,7,9" +locations_default(#20123,#10000,7,9,7,9) +hasLocation(#20122,#20123) +regexp_const_value(#20122,"c") +#20124=* +regexpterm(#20124,14,#20108,7,"d") +#20125=@"loc,{#10000},7,10,7,10" +locations_default(#20125,#10000,7,10,7,10) +hasLocation(#20124,#20125) +regexp_const_value(#20124,"d") +#20126=* +entry_cfg_node(#20126,#20001) +#20127=@"loc,{#10000},1,1,1,0" +locations_default(#20127,#10000,1,1,1,0) +hasLocation(#20126,#20127) +#20128=* +exit_cfg_node(#20128,#20001) +hasLocation(#20128,#20047) +successor(#20105,#20107) +successor(#20107,#20128) +successor(#20082,#20084) +successor(#20084,#20105) +successor(#20077,#20079) +successor(#20079,#20082) +successor(#20049,#20051) +successor(#20051,#20077) +successor(#20126,#20049) +numlines(#10000,7,4,7) filetype(#10000,"javascript") From 17b7d8faeb1d9665a05f6c2e7a0c87895748525a Mon Sep 17 00:00:00 2001 From: Napalys Date: Thu, 27 Feb 2025 13:36:23 +0100 Subject: [PATCH 8/9] Add support for nested character classes in RegExpParser --- .../com/semmle/js/parser/RegExpParser.java | 27 ++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/javascript/extractor/src/com/semmle/js/parser/RegExpParser.java b/javascript/extractor/src/com/semmle/js/parser/RegExpParser.java index 487ad7c74416..1570d36c670e 100644 --- a/javascript/extractor/src/com/semmle/js/parser/RegExpParser.java +++ b/javascript/extractor/src/com/semmle/js/parser/RegExpParser.java @@ -68,6 +68,7 @@ public List getErrors() { private List errors; private List backrefs; private int maxbackref; + private boolean vFlag = true; /** Parse the given string as a regular expression. */ public Result parse(String src) { @@ -507,9 +508,9 @@ private RegExpTerm parseAtomEscape(SourceLocation loc, boolean inCharClass) { } private RegExpTerm parseCharacterClass() { + if (vFlag) return parseNestedCharacterClass(); SourceLocation loc = new SourceLocation(pos()); List elements = new ArrayList<>(); - this.match("["); boolean inverted = this.match("^"); while (!this.match("]")) { @@ -522,6 +523,27 @@ private RegExpTerm parseCharacterClass() { return this.finishTerm(new CharacterClass(loc, elements, inverted)); } + // New method to support nested character classes. + private RegExpTerm parseNestedCharacterClass() { + SourceLocation loc = new SourceLocation(pos()); + this.match("["); // consume '[' + boolean inverted = this.match("^"); + List elements = new ArrayList<>(); + while (!this.match("]")) { + if (this.atEOS()) { + this.error(Error.EXPECTED_RBRACKET); + break; + } + // If nested '[' is found, recursively parse it. + if (vFlag && lookahead("[")) { + elements.add(parseNestedCharacterClass()); + } else { + elements.add(this.parseCharacterClassElement()); + } + } + return this.finishTerm(new CharacterClass(loc, elements, inverted)); + } + private static final List escapeClasses = Arrays.asList("d", "D", "s", "S", "w", "W"); private RegExpTerm parseCharacterClassElement() { @@ -540,6 +562,9 @@ private RegExpTerm parseCharacterClassElement() { private RegExpTerm parseCharacterClassAtom() { SourceLocation loc = new SourceLocation(pos()); + if (vFlag && peekChar(true) == '[') { + return parseNestedCharacterClass(); + } char c = this.nextChar(); if (c == '\\') { if (this.match("b")) return this.finishTerm(new ControlEscape(loc, "\b", 8, "\\b")); From 788b95b5891130f08378eddc3d8c67ce13f27b18 Mon Sep 17 00:00:00 2001 From: Napalys Date: Fri, 28 Feb 2025 17:57:03 +0100 Subject: [PATCH 9/9] test2 --- .../js/ast/regexp/StringDisjunction.java | 25 + .../com/semmle/js/ast/regexp/Subtraction.java | 22 + .../src/com/semmle/js/ast/regexp/Union.java | 22 + .../src/com/semmle/js/ast/regexp/Visitor.java | 6 + .../semmle/js/extractor/RegExpExtractor.java | 28 + .../com/semmle/js/parser/RegExpParser.java | 154 +++++- .../tests/es2024/input/combination.js | 1 + .../tests/es2024/input/intersection.js | 1 - .../tests/es2024/input/multi-intersection.js | 1 + .../tests/es2024/input/subtraction.js | 2 + .../extractor/tests/es2024/input/union.js | 6 + .../es2024/output/trap/combination.js.trap | 0 .../es2024/output/trap/intersection.js.trap | 507 +++++++----------- .../output/trap/multi-intersection.js.trap | 125 +++++ .../es2024/output/trap/subtraction.js.trap | 0 .../tests/es2024/output/trap/union.js.trap | 0 .../ql/lib/semmlecode.javascript.dbscheme | 5 +- 17 files changed, 579 insertions(+), 326 deletions(-) create mode 100644 javascript/extractor/src/com/semmle/js/ast/regexp/StringDisjunction.java create mode 100644 javascript/extractor/src/com/semmle/js/ast/regexp/Subtraction.java create mode 100644 javascript/extractor/src/com/semmle/js/ast/regexp/Union.java create mode 100644 javascript/extractor/tests/es2024/input/combination.js create mode 100644 javascript/extractor/tests/es2024/input/multi-intersection.js create mode 100644 javascript/extractor/tests/es2024/input/subtraction.js create mode 100644 javascript/extractor/tests/es2024/input/union.js create mode 100644 javascript/extractor/tests/es2024/output/trap/combination.js.trap create mode 100644 javascript/extractor/tests/es2024/output/trap/multi-intersection.js.trap create mode 100644 javascript/extractor/tests/es2024/output/trap/subtraction.js.trap create mode 100644 javascript/extractor/tests/es2024/output/trap/union.js.trap diff --git a/javascript/extractor/src/com/semmle/js/ast/regexp/StringDisjunction.java b/javascript/extractor/src/com/semmle/js/ast/regexp/StringDisjunction.java new file mode 100644 index 000000000000..3167d4aa7755 --- /dev/null +++ b/javascript/extractor/src/com/semmle/js/ast/regexp/StringDisjunction.java @@ -0,0 +1,25 @@ +package com.semmle.js.ast.regexp; + +import com.semmle.js.ast.SourceLocation; + +/** + * A '\q{}' escape sequence in a regular expression, which is a special extension + * to standard regular expressions. + */ +public class StringDisjunction extends RegExpTerm { + private final RegExpTerm term; + + public StringDisjunction(SourceLocation loc, RegExpTerm term) { + super(loc, "StringDisjunction"); + this.term = term; + } + + public RegExpTerm getTerm() { + return term; + } + + @Override + public void accept(Visitor v) { + v.visit(this); + } +} diff --git a/javascript/extractor/src/com/semmle/js/ast/regexp/Subtraction.java b/javascript/extractor/src/com/semmle/js/ast/regexp/Subtraction.java new file mode 100644 index 000000000000..4508b4be296f --- /dev/null +++ b/javascript/extractor/src/com/semmle/js/ast/regexp/Subtraction.java @@ -0,0 +1,22 @@ +package com.semmle.js.ast.regexp; + +import com.semmle.js.ast.SourceLocation; +import java.util.List; + +public class Subtraction extends RegExpTerm { + private final List subtraction; + + public Subtraction(SourceLocation loc, List subtraction) { + super(loc, "Intersection"); + this.subtraction = subtraction; + } + + @Override + public void accept(Visitor v) { + v.visit(this); + } + + public List getSubtraction() { + return subtraction; + } +} diff --git a/javascript/extractor/src/com/semmle/js/ast/regexp/Union.java b/javascript/extractor/src/com/semmle/js/ast/regexp/Union.java new file mode 100644 index 000000000000..2e2d2c11e3c7 --- /dev/null +++ b/javascript/extractor/src/com/semmle/js/ast/regexp/Union.java @@ -0,0 +1,22 @@ +package com.semmle.js.ast.regexp; + +import com.semmle.js.ast.SourceLocation; +import java.util.List; + +public class Union extends RegExpTerm { + private final List union; + + public Union(SourceLocation loc, List union) { + super(loc, "Union"); + this.union = union; + } + + @Override + public void accept(Visitor v) { + v.visit(this); + } + + public List getUnion() { + return union; + } +} diff --git a/javascript/extractor/src/com/semmle/js/ast/regexp/Visitor.java b/javascript/extractor/src/com/semmle/js/ast/regexp/Visitor.java index 95a0000fe7f9..97284516ed1c 100644 --- a/javascript/extractor/src/com/semmle/js/ast/regexp/Visitor.java +++ b/javascript/extractor/src/com/semmle/js/ast/regexp/Visitor.java @@ -63,4 +63,10 @@ public interface Visitor { public void visit(UnicodePropertyEscape nd); public void visit(Intersection nd); + + public void visit(Subtraction nd); + + public void visit(Union nd); + + public void visit(StringDisjunction nd); } diff --git a/javascript/extractor/src/com/semmle/js/extractor/RegExpExtractor.java b/javascript/extractor/src/com/semmle/js/extractor/RegExpExtractor.java index f192b75e2497..878c1744492c 100644 --- a/javascript/extractor/src/com/semmle/js/extractor/RegExpExtractor.java +++ b/javascript/extractor/src/com/semmle/js/extractor/RegExpExtractor.java @@ -23,6 +23,8 @@ import com.semmle.js.ast.regexp.HexEscapeSequence; import com.semmle.js.ast.regexp.IdentityEscape; import com.semmle.js.ast.regexp.Intersection; +import com.semmle.js.ast.regexp.Subtraction; +import com.semmle.js.ast.regexp.Union; import com.semmle.js.ast.regexp.Literal; import com.semmle.js.ast.regexp.NamedBackReference; import com.semmle.js.ast.regexp.NonWordBoundary; @@ -34,6 +36,7 @@ import com.semmle.js.ast.regexp.RegExpTerm; import com.semmle.js.ast.regexp.Sequence; import com.semmle.js.ast.regexp.Star; +import com.semmle.js.ast.regexp.StringDisjunction; import com.semmle.js.ast.regexp.UnicodeEscapeSequence; import com.semmle.js.ast.regexp.UnicodePropertyEscape; import com.semmle.js.ast.regexp.Visitor; @@ -94,6 +97,9 @@ public RegExpExtractor(TrapWriter trapwriter, LocationManager locationManager) { termkinds.put("ZeroWidthNegativeLookbehind", 26); termkinds.put("UnicodePropertyEscape", 27); termkinds.put("Intersection", 28); + termkinds.put("Subtraction", 29); + termkinds.put("Union", 30); + termkinds.put("StringDisjunction", 31); // Add StringDisjunction to the kind map } private static final String[] errmsgs = @@ -292,6 +298,12 @@ public void visit(UnicodePropertyEscape nd) { if (nd.hasValue()) trapwriter.addTuple("unicode_property_escapevalue", lbl, nd.getValue()); } + @Override + public void visit(StringDisjunction nd) { + Label lbl = extractTerm(nd, parent, idx); + visit(nd.getTerm(), lbl, 0); + } + @Override public void visit(DecimalEscape nd) { visit((Literal) nd); @@ -354,6 +366,22 @@ public void visit(Intersection nd) { for (RegExpTerm element : nd.getIntersections()) visit(element, lbl, i++); } + + @Override + public void visit(Subtraction nd) { + Label lbl = extractTerm(nd, parent, idx); + int i = 0; + for (RegExpTerm element : nd.getSubtraction()) + visit(element, lbl, i++); + } + + @Override + public void visit(Union nd) { + Label lbl = extractTerm(nd, parent, idx); + int i = 0; + for (RegExpTerm element : nd.getUnion()) + visit(element, lbl, i++); + } } public void extract(String src, SourceMap sourceMap, Node parent, boolean isSpeculativeParsing) { diff --git a/javascript/extractor/src/com/semmle/js/parser/RegExpParser.java b/javascript/extractor/src/com/semmle/js/parser/RegExpParser.java index 1570d36c670e..84a3622e3455 100644 --- a/javascript/extractor/src/com/semmle/js/parser/RegExpParser.java +++ b/javascript/extractor/src/com/semmle/js/parser/RegExpParser.java @@ -19,6 +19,8 @@ import com.semmle.js.ast.regexp.HexEscapeSequence; import com.semmle.js.ast.regexp.IdentityEscape; import com.semmle.js.ast.regexp.Intersection; +import com.semmle.js.ast.regexp.Subtraction; +import com.semmle.js.ast.regexp.Union; import com.semmle.js.ast.regexp.NamedBackReference; import com.semmle.js.ast.regexp.NonWordBoundary; import com.semmle.js.ast.regexp.OctalEscape; @@ -28,6 +30,7 @@ import com.semmle.js.ast.regexp.RegExpTerm; import com.semmle.js.ast.regexp.Sequence; import com.semmle.js.ast.regexp.Star; +import com.semmle.js.ast.regexp.StringDisjunction; import com.semmle.js.ast.regexp.UnicodeEscapeSequence; import com.semmle.js.ast.regexp.UnicodePropertyEscape; import com.semmle.js.ast.regexp.WordBoundary; @@ -37,6 +40,7 @@ import com.semmle.js.ast.regexp.ZeroWidthPositiveLookbehind; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.List; /** A parser for ECMAScript 2018 regular expressions. */ @@ -72,6 +76,7 @@ public List getErrors() { /** Parse the given string as a regular expression. */ public Result parse(String src) { + System.out.println("Parsing regex pattern: '" + src + "'"); this.src = src; this.pos = 0; this.errors = new ArrayList<>(); @@ -81,6 +86,7 @@ public Result parse(String src) { for (BackReference backref : backrefs) if (backref.getValue() > maxbackref) errors.add(new Error(backref.getLoc(), Error.INVALID_BACKREF)); + System.out.println("Parse completed with " + errors.size() + " errors"); return new Result(root, errors); } @@ -232,7 +238,46 @@ private RegExpTerm parseDisjunction() { disjuncts.add(this.parseAlternative()); } if (disjuncts.size() == 1) return disjuncts.get(0); - return this.finishTerm(new Disjunction(loc, disjuncts)); + return this.finishTerm(new Disjunction(loc, disjuncts)); + } + + private RegExpTerm parseDisjunctionInsideQuoatedString() { + SourceLocation loc = new SourceLocation(pos()); + List disjuncts = new ArrayList<>(); + disjuncts.add(this.parseAlternativeInsideQuoatedString()); + while (this.match("|")) { + disjuncts.add(this.parseAlternativeInsideQuoatedString()); + } + if (disjuncts.size() == 1) return disjuncts.get(0); + return this.finishTerm(new Disjunction(loc, disjuncts)); + } + + private RegExpTerm parseAlternativeInsideQuoatedString() { + SourceLocation loc = new SourceLocation(pos()); + StringBuilder sb = new StringBuilder(); + boolean escaped = false; + while (true) { + // If we're at the end of the string, something went wrong. + if (this.atEOS()) { + this.error(Error.UNEXPECTED_EOS); + break; + } + // We can end parsing if we're not escaped and we see a `|` which would mean Alternation + // or `}` which would mean the end of the Quoted String. + if(!escaped && this.lookahead(null, "|", "}")){ + break; + } + char c = this.nextChar(); + // Track whether the character is an escape character. + escaped = !escaped && (c == '\\'); + sb.append(c); + } + + String literal = sb.toString(); + loc.setEnd(pos()); + loc.setSource(literal); + + return new Constant(loc, literal); } private RegExpTerm parseAlternative() { @@ -240,19 +285,9 @@ private RegExpTerm parseAlternative() { List elements = new ArrayList<>(); while (!this.lookahead(null, "|", ")")) elements.add(this.parseTerm()); if (elements.size() == 1) return elements.get(0); - return this.finishTerm(new Sequence(loc, elements)); + return this.finishTerm(new Sequence(loc, elements)); } - private RegExpTerm parseIntersection() { - SourceLocation loc = new SourceLocation(pos()); - List intersections = new ArrayList<>(); - intersections.add(this.parseAlternative()); - while (this.match("&&")) { - intersections.add(this.parseAlternative()); - } - if (intersections.size() == 1) return intersections.get(0); - return this.finishTerm(new Intersection(loc, intersections)); -} private RegExpTerm parseTerm() { SourceLocation loc = new SourceLocation(pos()); @@ -436,6 +471,14 @@ private RegExpTerm parseAtomEscape(SourceLocation loc, boolean inCharClass) { return this.finishTerm(new NamedBackReference(loc, name, "\\k<" + name + ">")); } + if (this.match("q{")) { + System.out.println("Parsing string disjunction at position " + pos); + RegExpTerm term = parseDisjunctionInsideQuoatedString(); + this.expectRBrace(); + System.out.println("Finished Parsing string disjunction at position " + pos); + return this.finishTerm(new StringDisjunction(loc, term)); + } + if (this.match("p{", "P{")) { String name = this.readIdentifier(); if (this.match("=")) { @@ -513,6 +556,7 @@ private RegExpTerm parseCharacterClass() { List elements = new ArrayList<>(); this.match("["); boolean inverted = this.match("^"); + System.out.println("Parsing simple character class (inverted: " + inverted + ") at position " + pos); while (!this.match("]")) { if (this.atEOS()) { this.error(Error.EXPECTED_RBRACKET); @@ -520,27 +564,88 @@ private RegExpTerm parseCharacterClass() { } elements.add(this.parseCharacterClassElement()); } + System.out.println("Created character class with " + elements.size() + " elements"); return this.finishTerm(new CharacterClass(loc, elements, inverted)); } + /** + * Enum representing the types of character class structures. + */ + private enum CharacterClassType { + STANDARD, + INTERSECTION, + SUBTRACTION, + UNION + } + // New method to support nested character classes. private RegExpTerm parseNestedCharacterClass() { SourceLocation loc = new SourceLocation(pos()); this.match("["); // consume '[' boolean inverted = this.match("^"); List elements = new ArrayList<>(); + CharacterClassType classType = CharacterClassType.STANDARD; + while (!this.match("]")) { if (this.atEOS()) { this.error(Error.EXPECTED_RBRACKET); break; } + // If nested '[' is found, recursively parse it. if (vFlag && lookahead("[")) { + System.out.println("Found nested character class at position " + pos); elements.add(parseNestedCharacterClass()); - } else { - elements.add(this.parseCharacterClassElement()); + } + else if (vFlag && lookahead("&&")) { + this.match("&&"); + classType = CharacterClassType.INTERSECTION; + System.out.println("Found intersection operator at position " + pos); + } + else if (vFlag && lookahead("--")) { + this.match("--"); + classType = CharacterClassType.SUBTRACTION; + System.out.println("Found subtraction operator at position " + pos); } + else { + RegExpTerm element = this.parseCharacterClassElement(); + elements.add(element); + System.out.println("Added character class element: " + element.getClass().getSimpleName() + " at position " + pos); + } + } + boolean containsComplex = elements.stream() + .anyMatch(term -> term instanceof UnicodePropertyEscape || + term instanceof StringDisjunction || + term instanceof CharacterClass); + // Set type to UNION only if: + // 1. We haven't already determined a specific type (intersection/subtraction) + // 2. We have more than one element + // 3. We have at least one complex element (i.e. a nested character class or a UnicodePropertyEscape) + if (containsComplex && classType == CharacterClassType.STANDARD && elements.size() > 1) { + classType = CharacterClassType.UNION; + + System.out.println("Converting to UNION due to complex element composition"); + } + + // Create appropriate RegExpTerm based on the detected class type + switch (classType) { + case INTERSECTION: + elements = Collections.singletonList(new Intersection(loc, elements)); + System.out.println("Completed INTERSECTION class with " + elements.size() + " elements"); + break; + case SUBTRACTION: + elements = Collections.singletonList(new Subtraction(loc, elements)); + System.out.println("Completed SUBTRACTION class with " + elements.size() + " elements"); + break; + case UNION: + elements = Collections.singletonList(new Union(loc, elements)); + System.out.println("Completed UNION class with " + elements.size() + " elements"); + break; + case STANDARD: + default: + System.out.println("Completed STANDARD class with " + elements.size() + " elements"); } + return this.finishTerm(new CharacterClass(loc, elements, inverted)); } @@ -555,25 +660,40 @@ private RegExpTerm parseCharacterClassElement() { return atom; } } - if (!this.lookahead("-]") && this.match("-") && !(atom instanceof CharacterClassEscape)) - return this.finishTerm(new CharacterClassRange(loc, atom, this.parseCharacterClassAtom())); + if (!this.lookahead("-]") && !this.lookahead("--") && this.match("-") && !(atom instanceof CharacterClassEscape)) { + RegExpTerm rangeEnd = this.parseCharacterClassAtom(); + System.out.println("Created character class range from " + + (atom instanceof Constant ? ((Constant)atom).getValue() : atom.getClass().getSimpleName()) + + " to " + + (rangeEnd instanceof Constant ? ((Constant)rangeEnd).getValue() : rangeEnd.getClass().getSimpleName())); + return this.finishTerm(new CharacterClassRange(loc, atom, rangeEnd)); + } return atom; } private RegExpTerm parseCharacterClassAtom() { SourceLocation loc = new SourceLocation(pos()); if (vFlag && peekChar(true) == '[') { + System.out.println("Found nested character class atom at position " + pos); return parseNestedCharacterClass(); } char c = this.nextChar(); + System.err.println("Parsing character class atom: '" + c + "' at position " + pos); if (c == '\\') { - if (this.match("b")) return this.finishTerm(new ControlEscape(loc, "\b", 8, "\\b")); + if (this.match("b")) { + System.out.println("Parsed backspace escape at position " + pos); + return this.finishTerm(new ControlEscape(loc, "\b", 8, "\\b")); + } + System.out.println("Parsing atom escape at position " + pos); return this.finishTerm(this.parseAtomEscape(loc, true)); } String value = String.valueOf(c); // Extract a surrogate pair as a single constant. if (Character.isHighSurrogate(c) && Character.isLowSurrogate(peekChar(true))) { value += this.nextChar(); + System.out.println("Parsed surrogate pair: " + value + " at position " + pos); + } else { + System.out.println("Parsed character: '" + value + "' at position " + pos); } return this.finishTerm(new Constant(loc, value)); } diff --git a/javascript/extractor/tests/es2024/input/combination.js b/javascript/extractor/tests/es2024/input/combination.js new file mode 100644 index 000000000000..1f0a099001a2 --- /dev/null +++ b/javascript/extractor/tests/es2024/input/combination.js @@ -0,0 +1 @@ +/[\p{Letter}&&[abc]&&[\q{aa|\}}--[abnc]--\p{Script_Extensions=Greek}]]/v; diff --git a/javascript/extractor/tests/es2024/input/intersection.js b/javascript/extractor/tests/es2024/input/intersection.js index 5f289de0fd16..cd69a21bdc80 100644 --- a/javascript/extractor/tests/es2024/input/intersection.js +++ b/javascript/extractor/tests/es2024/input/intersection.js @@ -4,4 +4,3 @@ // - Matches a single character from [abc] // - Then the literal "&&" // - Then a single character from [bcd] -/[abc&&bcd]/v; // Compilation error due to invalid use of intersection operation diff --git a/javascript/extractor/tests/es2024/input/multi-intersection.js b/javascript/extractor/tests/es2024/input/multi-intersection.js new file mode 100644 index 000000000000..f5a892dbe3d9 --- /dev/null +++ b/javascript/extractor/tests/es2024/input/multi-intersection.js @@ -0,0 +1 @@ +/[[abc]&&[bcd]&&[a]]/v; diff --git a/javascript/extractor/tests/es2024/input/subtraction.js b/javascript/extractor/tests/es2024/input/subtraction.js new file mode 100644 index 000000000000..fecb678a977f --- /dev/null +++ b/javascript/extractor/tests/es2024/input/subtraction.js @@ -0,0 +1,2 @@ +/[\p{Script_Extensions=Greek}--\p{Letter}]/v; +/[[abc]--[cbd]]/v; diff --git a/javascript/extractor/tests/es2024/input/union.js b/javascript/extractor/tests/es2024/input/union.js new file mode 100644 index 000000000000..9eeab43f43aa --- /dev/null +++ b/javascript/extractor/tests/es2024/input/union.js @@ -0,0 +1,6 @@ +/[\p{Script_Extensions=Greek}\p{RGI_Emoji}]/v; +/[[abc][cbd]]/v; +/[\p{Emoji}\q{a&}byz]/v; +/[\q{\\\}a&}byz]/v; +/[\q{\\}]/v; +/[\q{abc|cbd|\}}]/v; diff --git a/javascript/extractor/tests/es2024/output/trap/combination.js.trap b/javascript/extractor/tests/es2024/output/trap/combination.js.trap new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/javascript/extractor/tests/es2024/output/trap/intersection.js.trap b/javascript/extractor/tests/es2024/output/trap/intersection.js.trap index a19310ff4bb1..01fa1cf4bb77 100644 --- a/javascript/extractor/tests/es2024/output/trap/intersection.js.trap +++ b/javascript/extractor/tests/es2024/output/trap/intersection.js.trap @@ -40,364 +40,257 @@ comments(#20012,0,#20001," - Then a single character from [bcd]","// - Th ... m locations_default(#20013,#10000,6,18,6,56) hasLocation(#20012,#20013) #20014=* -comments(#20014,0,#20001," Compilation error due to invalid use of intersection operation","// Comp ... eration") -#20015=@"loc,{#10000},7,16,7,80" -locations_default(#20015,#10000,7,16,7,80) +lines(#20014,#20001,"/[[abc]&&[bcd]]/v; // Valid use of intersection operator, matches b or c"," +") +#20015=@"loc,{#10000},1,1,1,72" +locations_default(#20015,#10000,1,1,1,72) hasLocation(#20014,#20015) #20016=* -lines(#20016,#20001,"/[[abc]&&[bcd]]/v; // Valid use of intersection operator, matches b or c"," +lines(#20016,#20001,"/abc&&bcd/v; //Valid regex, but no intersection operation: Matches the literal string ""abc&&bcd"""," ") -#20017=@"loc,{#10000},1,1,1,72" -locations_default(#20017,#10000,1,1,1,72) +#20017=@"loc,{#10000},2,1,2,96" +locations_default(#20017,#10000,2,1,2,96) hasLocation(#20016,#20017) #20018=* -lines(#20018,#20001,"/abc&&bcd/v; //Valid regex, but no intersection operation: Matches the literal string ""abc&&bcd"""," +lines(#20018,#20001,"/[abc]&&[bcd]/v; // Valid regex, but incorrect intersection operation: "," ") -#20019=@"loc,{#10000},2,1,2,96" -locations_default(#20019,#10000,2,1,2,96) +#20019=@"loc,{#10000},3,1,3,71" +locations_default(#20019,#10000,3,1,3,71) hasLocation(#20018,#20019) #20020=* -lines(#20020,#20001,"/[abc]&&[bcd]/v; // Valid regex, but incorrect intersection operation: "," +lines(#20020,#20001," // - Matches a single character from [abc]"," ") -#20021=@"loc,{#10000},3,1,3,71" -locations_default(#20021,#10000,3,1,3,71) +#20021=@"loc,{#10000},4,1,4,59" +locations_default(#20021,#10000,4,1,4,59) hasLocation(#20020,#20021) +indentation(#10000,4," ",17) #20022=* -lines(#20022,#20001," // - Matches a single character from [abc]"," +lines(#20022,#20001," // - Then the literal ""&&"""," ") -#20023=@"loc,{#10000},4,1,4,59" -locations_default(#20023,#10000,4,1,4,59) +#20023=@"loc,{#10000},5,1,5,43" +locations_default(#20023,#10000,5,1,5,43) hasLocation(#20022,#20023) -indentation(#10000,4," ",17) +indentation(#10000,5," ",17) #20024=* -lines(#20024,#20001," // - Then the literal ""&&"""," +lines(#20024,#20001," // - Then a single character from [bcd]"," ") -#20025=@"loc,{#10000},5,1,5,43" -locations_default(#20025,#10000,5,1,5,43) +#20025=@"loc,{#10000},6,1,6,56" +locations_default(#20025,#10000,6,1,6,56) hasLocation(#20024,#20025) -indentation(#10000,5," ",17) +indentation(#10000,6," ",17) +numlines(#20001,6,3,6) #20026=* -lines(#20026,#20001," // - Then a single character from [bcd]"," -") -#20027=@"loc,{#10000},6,1,6,56" -locations_default(#20027,#10000,6,1,6,56) +tokeninfo(#20026,5,#20001,0,"/[[abc]&&[bcd]]/v") +#20027=@"loc,{#10000},1,1,1,17" +locations_default(#20027,#10000,1,1,1,17) hasLocation(#20026,#20027) -indentation(#10000,6," ",17) #20028=* -lines(#20028,#20001,"/[abc&&bcd]/v; // Compilation error due to invalid use of intersection operation"," -") -#20029=@"loc,{#10000},7,1,7,80" -locations_default(#20029,#10000,7,1,7,80) +tokeninfo(#20028,8,#20001,1,";") +#20029=@"loc,{#10000},1,18,1,18" +locations_default(#20029,#10000,1,18,1,18) hasLocation(#20028,#20029) -numlines(#20001,7,4,7) #20030=* -tokeninfo(#20030,5,#20001,0,"/[[abc]&&[bcd]]/v") -#20031=@"loc,{#10000},1,1,1,17" -locations_default(#20031,#10000,1,1,1,17) +tokeninfo(#20030,5,#20001,2,"/abc&&bcd/v") +#20031=@"loc,{#10000},2,1,2,11" +locations_default(#20031,#10000,2,1,2,11) hasLocation(#20030,#20031) +next_token(#20002,#20030) #20032=* -tokeninfo(#20032,8,#20001,1,";") -#20033=@"loc,{#10000},1,18,1,18" -locations_default(#20033,#10000,1,18,1,18) +tokeninfo(#20032,8,#20001,3,";") +#20033=@"loc,{#10000},2,12,2,12" +locations_default(#20033,#10000,2,12,2,12) hasLocation(#20032,#20033) #20034=* -tokeninfo(#20034,5,#20001,2,"/abc&&bcd/v") -#20035=@"loc,{#10000},2,1,2,11" -locations_default(#20035,#10000,2,1,2,11) +tokeninfo(#20034,5,#20001,4,"/[abc]&&[bcd]/v") +#20035=@"loc,{#10000},3,1,3,15" +locations_default(#20035,#10000,3,1,3,15) hasLocation(#20034,#20035) -next_token(#20002,#20034) +next_token(#20004,#20034) #20036=* -tokeninfo(#20036,8,#20001,3,";") -#20037=@"loc,{#10000},2,12,2,12" -locations_default(#20037,#10000,2,12,2,12) +tokeninfo(#20036,8,#20001,5,";") +#20037=@"loc,{#10000},3,16,3,16" +locations_default(#20037,#10000,3,16,3,16) hasLocation(#20036,#20037) #20038=* -tokeninfo(#20038,5,#20001,4,"/[abc]&&[bcd]/v") -#20039=@"loc,{#10000},3,1,3,15" -locations_default(#20039,#10000,3,1,3,15) +tokeninfo(#20038,0,#20001,6,"") +#20039=@"loc,{#10000},7,1,7,0" +locations_default(#20039,#10000,7,1,7,0) hasLocation(#20038,#20039) -next_token(#20004,#20038) -#20040=* -tokeninfo(#20040,8,#20001,5,";") -#20041=@"loc,{#10000},3,16,3,16" -locations_default(#20041,#10000,3,16,3,16) -hasLocation(#20040,#20041) -#20042=* -tokeninfo(#20042,5,#20001,6,"/[abc&&bcd]/v") -#20043=@"loc,{#10000},7,1,7,13" -locations_default(#20043,#10000,7,1,7,13) -hasLocation(#20042,#20043) -next_token(#20006,#20042) -next_token(#20008,#20042) -next_token(#20010,#20042) -next_token(#20012,#20042) +next_token(#20006,#20038) +next_token(#20008,#20038) +next_token(#20010,#20038) +next_token(#20012,#20038) +toplevels(#20001,0) +#20040=@"loc,{#10000},1,1,7,0" +locations_default(#20040,#10000,1,1,7,0) +hasLocation(#20001,#20040) +#20041=* +stmts(#20041,2,#20001,0,"/[[abc]&&[bcd]]/v;") +#20042=@"loc,{#10000},1,1,1,18" +locations_default(#20042,#10000,1,1,1,18) +hasLocation(#20041,#20042) +stmt_containers(#20041,#20001) +#20043=* +exprs(#20043,5,#20041,0,"/[[abc]&&[bcd]]/v") +hasLocation(#20043,#20027) +enclosing_stmt(#20043,#20041) +expr_containers(#20043,#20001) +literals("/[[abc]&&[bcd]]/v","/[[abc]&&[bcd]]/v",#20043) #20044=* -tokeninfo(#20044,8,#20001,7,";") -#20045=@"loc,{#10000},7,14,7,14" -locations_default(#20045,#10000,7,14,7,14) +regexpterm(#20044,23,#20043,0,"[[abc]&&[bcd]]") +#20045=@"loc,{#10000},1,2,1,15" +locations_default(#20045,#10000,1,2,1,15) hasLocation(#20044,#20045) #20046=* -tokeninfo(#20046,0,#20001,8,"") -#20047=@"loc,{#10000},8,1,8,0" -locations_default(#20047,#10000,8,1,8,0) -hasLocation(#20046,#20047) -next_token(#20014,#20046) -toplevels(#20001,0) -#20048=@"loc,{#10000},1,1,8,0" -locations_default(#20048,#10000,1,1,8,0) -hasLocation(#20001,#20048) +regexpterm(#20046,28,#20044,0,"[[abc]&&[bcd]]") +hasLocation(#20046,#20045) +#20047=* +regexpterm(#20047,23,#20046,0,"[abc]") +#20048=@"loc,{#10000},1,3,1,7" +locations_default(#20048,#10000,1,3,1,7) +hasLocation(#20047,#20048) #20049=* -stmts(#20049,2,#20001,0,"/[[abc]&&[bcd]]/v;") -#20050=@"loc,{#10000},1,1,1,18" -locations_default(#20050,#10000,1,1,1,18) +regexpterm(#20049,14,#20047,0,"a") +#20050=@"loc,{#10000},1,4,1,4" +locations_default(#20050,#10000,1,4,1,4) hasLocation(#20049,#20050) -stmt_containers(#20049,#20001) +regexp_const_value(#20049,"a") #20051=* -exprs(#20051,5,#20049,0,"/[[abc]&&[bcd]]/v") -hasLocation(#20051,#20031) -enclosing_stmt(#20051,#20049) -expr_containers(#20051,#20001) -literals("/[[abc]&&[bcd]]/v","/[[abc]&&[bcd]]/v",#20051) -#20052=* -regexpterm(#20052,1,#20051,0,"[[abc]&&[bcd]]") -#20053=@"loc,{#10000},1,2,1,15" -locations_default(#20053,#10000,1,2,1,15) -hasLocation(#20052,#20053) -#20054=* -regexpterm(#20054,23,#20052,0,"[[abc]") -#20055=@"loc,{#10000},1,2,1,7" -locations_default(#20055,#10000,1,2,1,7) -hasLocation(#20054,#20055) -#20056=* -regexpterm(#20056,14,#20054,0,"[") -#20057=@"loc,{#10000},1,3,1,3" -locations_default(#20057,#10000,1,3,1,3) -hasLocation(#20056,#20057) -regexp_const_value(#20056,"[") -#20058=* -regexpterm(#20058,14,#20054,1,"a") -#20059=@"loc,{#10000},1,4,1,4" -locations_default(#20059,#10000,1,4,1,4) -hasLocation(#20058,#20059) -regexp_const_value(#20058,"a") -#20060=* -regexpterm(#20060,14,#20054,2,"b") -#20061=@"loc,{#10000},1,5,1,5" -locations_default(#20061,#10000,1,5,1,5) -hasLocation(#20060,#20061) -regexp_const_value(#20060,"b") -#20062=* -regexpterm(#20062,14,#20054,3,"c") -#20063=@"loc,{#10000},1,6,1,6" -locations_default(#20063,#10000,1,6,1,6) -hasLocation(#20062,#20063) -regexp_const_value(#20062,"c") -#20064=* -regexpterm(#20064,14,#20052,1,"&&") -#20065=@"loc,{#10000},1,8,1,9" -locations_default(#20065,#10000,1,8,1,9) -hasLocation(#20064,#20065) -regexp_const_value(#20064,"&&") +regexpterm(#20051,14,#20047,1,"b") +#20052=@"loc,{#10000},1,5,1,5" +locations_default(#20052,#10000,1,5,1,5) +hasLocation(#20051,#20052) +regexp_const_value(#20051,"b") +#20053=* +regexpterm(#20053,14,#20047,2,"c") +#20054=@"loc,{#10000},1,6,1,6" +locations_default(#20054,#10000,1,6,1,6) +hasLocation(#20053,#20054) +regexp_const_value(#20053,"c") +#20055=* +regexpterm(#20055,23,#20046,1,"[bcd]") +#20056=@"loc,{#10000},1,10,1,14" +locations_default(#20056,#10000,1,10,1,14) +hasLocation(#20055,#20056) +#20057=* +regexpterm(#20057,14,#20055,0,"b") +#20058=@"loc,{#10000},1,11,1,11" +locations_default(#20058,#10000,1,11,1,11) +hasLocation(#20057,#20058) +regexp_const_value(#20057,"b") +#20059=* +regexpterm(#20059,14,#20055,1,"c") +#20060=@"loc,{#10000},1,12,1,12" +locations_default(#20060,#10000,1,12,1,12) +hasLocation(#20059,#20060) +regexp_const_value(#20059,"c") +#20061=* +regexpterm(#20061,14,#20055,2,"d") +#20062=@"loc,{#10000},1,13,1,13" +locations_default(#20062,#10000,1,13,1,13) +hasLocation(#20061,#20062) +regexp_const_value(#20061,"d") +#20063=* +stmts(#20063,2,#20001,1,"/abc&&bcd/v;") +#20064=@"loc,{#10000},2,1,2,12" +locations_default(#20064,#10000,2,1,2,12) +hasLocation(#20063,#20064) +stmt_containers(#20063,#20001) +#20065=* +exprs(#20065,5,#20063,0,"/abc&&bcd/v") +hasLocation(#20065,#20031) +enclosing_stmt(#20065,#20063) +expr_containers(#20065,#20001) +literals("/abc&&bcd/v","/abc&&bcd/v",#20065) #20066=* -regexpterm(#20066,23,#20052,2,"[bcd]") -#20067=@"loc,{#10000},1,10,1,14" -locations_default(#20067,#10000,1,10,1,14) +regexpterm(#20066,14,#20065,0,"abc&&bcd") +#20067=@"loc,{#10000},2,2,2,9" +locations_default(#20067,#10000,2,2,2,9) hasLocation(#20066,#20067) +regexp_const_value(#20066,"abc&&bcd") #20068=* -regexpterm(#20068,14,#20066,0,"b") -#20069=@"loc,{#10000},1,11,1,11" -locations_default(#20069,#10000,1,11,1,11) +stmts(#20068,2,#20001,2,"/[abc]&&[bcd]/v;") +#20069=@"loc,{#10000},3,1,3,16" +locations_default(#20069,#10000,3,1,3,16) hasLocation(#20068,#20069) -regexp_const_value(#20068,"b") +stmt_containers(#20068,#20001) #20070=* -regexpterm(#20070,14,#20066,1,"c") -#20071=@"loc,{#10000},1,12,1,12" -locations_default(#20071,#10000,1,12,1,12) -hasLocation(#20070,#20071) -regexp_const_value(#20070,"c") -#20072=* -regexpterm(#20072,14,#20066,2,"d") -#20073=@"loc,{#10000},1,13,1,13" -locations_default(#20073,#10000,1,13,1,13) -hasLocation(#20072,#20073) -regexp_const_value(#20072,"d") -#20074=* -regexpterm(#20074,14,#20052,3,"]") -#20075=@"loc,{#10000},1,15,1,15" -locations_default(#20075,#10000,1,15,1,15) -hasLocation(#20074,#20075) -regexp_const_value(#20074,"]") -#20076=* -regexp_parse_errors(#20076,#20052,"unexpected character") -hasLocation(#20076,#20075) +exprs(#20070,5,#20068,0,"/[abc]&&[bcd]/v") +hasLocation(#20070,#20035) +enclosing_stmt(#20070,#20068) +expr_containers(#20070,#20001) +literals("/[abc]&&[bcd]/v","/[abc]&&[bcd]/v",#20070) +#20071=* +regexpterm(#20071,1,#20070,0,"[abc]&&[bcd]") +#20072=@"loc,{#10000},3,2,3,13" +locations_default(#20072,#10000,3,2,3,13) +hasLocation(#20071,#20072) +#20073=* +regexpterm(#20073,23,#20071,0,"[abc]") +#20074=@"loc,{#10000},3,2,3,6" +locations_default(#20074,#10000,3,2,3,6) +hasLocation(#20073,#20074) +#20075=* +regexpterm(#20075,14,#20073,0,"a") +#20076=@"loc,{#10000},3,3,3,3" +locations_default(#20076,#10000,3,3,3,3) +hasLocation(#20075,#20076) +regexp_const_value(#20075,"a") #20077=* -stmts(#20077,2,#20001,1,"/abc&&bcd/v;") -#20078=@"loc,{#10000},2,1,2,12" -locations_default(#20078,#10000,2,1,2,12) +regexpterm(#20077,14,#20073,1,"b") +#20078=@"loc,{#10000},3,4,3,4" +locations_default(#20078,#10000,3,4,3,4) hasLocation(#20077,#20078) -stmt_containers(#20077,#20001) +regexp_const_value(#20077,"b") #20079=* -exprs(#20079,5,#20077,0,"/abc&&bcd/v") -hasLocation(#20079,#20035) -enclosing_stmt(#20079,#20077) -expr_containers(#20079,#20001) -literals("/abc&&bcd/v","/abc&&bcd/v",#20079) -#20080=* -regexpterm(#20080,14,#20079,0,"abc&&bcd") -#20081=@"loc,{#10000},2,2,2,9" -locations_default(#20081,#10000,2,2,2,9) -hasLocation(#20080,#20081) -regexp_const_value(#20080,"abc&&bcd") -#20082=* -stmts(#20082,2,#20001,2,"/[abc]&&[bcd]/v;") -#20083=@"loc,{#10000},3,1,3,16" -locations_default(#20083,#10000,3,1,3,16) -hasLocation(#20082,#20083) -stmt_containers(#20082,#20001) -#20084=* -exprs(#20084,5,#20082,0,"/[abc]&&[bcd]/v") -hasLocation(#20084,#20039) -enclosing_stmt(#20084,#20082) -expr_containers(#20084,#20001) -literals("/[abc]&&[bcd]/v","/[abc]&&[bcd]/v",#20084) +regexpterm(#20079,14,#20073,2,"c") +#20080=@"loc,{#10000},3,5,3,5" +locations_default(#20080,#10000,3,5,3,5) +hasLocation(#20079,#20080) +regexp_const_value(#20079,"c") +#20081=* +regexpterm(#20081,14,#20071,1,"&&") +#20082=@"loc,{#10000},3,7,3,8" +locations_default(#20082,#10000,3,7,3,8) +hasLocation(#20081,#20082) +regexp_const_value(#20081,"&&") +#20083=* +regexpterm(#20083,23,#20071,2,"[bcd]") +#20084=@"loc,{#10000},3,9,3,13" +locations_default(#20084,#10000,3,9,3,13) +hasLocation(#20083,#20084) #20085=* -regexpterm(#20085,1,#20084,0,"[abc]&&[bcd]") -#20086=@"loc,{#10000},3,2,3,13" -locations_default(#20086,#10000,3,2,3,13) +regexpterm(#20085,14,#20083,0,"b") +#20086=@"loc,{#10000},3,10,3,10" +locations_default(#20086,#10000,3,10,3,10) hasLocation(#20085,#20086) +regexp_const_value(#20085,"b") #20087=* -regexpterm(#20087,23,#20085,0,"[abc]") -#20088=@"loc,{#10000},3,2,3,6" -locations_default(#20088,#10000,3,2,3,6) +regexpterm(#20087,14,#20083,1,"c") +#20088=@"loc,{#10000},3,11,3,11" +locations_default(#20088,#10000,3,11,3,11) hasLocation(#20087,#20088) +regexp_const_value(#20087,"c") #20089=* -regexpterm(#20089,14,#20087,0,"a") -#20090=@"loc,{#10000},3,3,3,3" -locations_default(#20090,#10000,3,3,3,3) +regexpterm(#20089,14,#20083,2,"d") +#20090=@"loc,{#10000},3,12,3,12" +locations_default(#20090,#10000,3,12,3,12) hasLocation(#20089,#20090) -regexp_const_value(#20089,"a") +regexp_const_value(#20089,"d") #20091=* -regexpterm(#20091,14,#20087,1,"b") -#20092=@"loc,{#10000},3,4,3,4" -locations_default(#20092,#10000,3,4,3,4) +entry_cfg_node(#20091,#20001) +#20092=@"loc,{#10000},1,1,1,0" +locations_default(#20092,#10000,1,1,1,0) hasLocation(#20091,#20092) -regexp_const_value(#20091,"b") #20093=* -regexpterm(#20093,14,#20087,2,"c") -#20094=@"loc,{#10000},3,5,3,5" -locations_default(#20094,#10000,3,5,3,5) -hasLocation(#20093,#20094) -regexp_const_value(#20093,"c") -#20095=* -regexpterm(#20095,14,#20085,1,"&&") -#20096=@"loc,{#10000},3,7,3,8" -locations_default(#20096,#10000,3,7,3,8) -hasLocation(#20095,#20096) -regexp_const_value(#20095,"&&") -#20097=* -regexpterm(#20097,23,#20085,2,"[bcd]") -#20098=@"loc,{#10000},3,9,3,13" -locations_default(#20098,#10000,3,9,3,13) -hasLocation(#20097,#20098) -#20099=* -regexpterm(#20099,14,#20097,0,"b") -#20100=@"loc,{#10000},3,10,3,10" -locations_default(#20100,#10000,3,10,3,10) -hasLocation(#20099,#20100) -regexp_const_value(#20099,"b") -#20101=* -regexpterm(#20101,14,#20097,1,"c") -#20102=@"loc,{#10000},3,11,3,11" -locations_default(#20102,#10000,3,11,3,11) -hasLocation(#20101,#20102) -regexp_const_value(#20101,"c") -#20103=* -regexpterm(#20103,14,#20097,2,"d") -#20104=@"loc,{#10000},3,12,3,12" -locations_default(#20104,#10000,3,12,3,12) -hasLocation(#20103,#20104) -regexp_const_value(#20103,"d") -#20105=* -stmts(#20105,2,#20001,3,"/[abc&&bcd]/v;") -#20106=@"loc,{#10000},7,1,7,14" -locations_default(#20106,#10000,7,1,7,14) -hasLocation(#20105,#20106) -stmt_containers(#20105,#20001) -#20107=* -exprs(#20107,5,#20105,0,"/[abc&&bcd]/v") -hasLocation(#20107,#20043) -enclosing_stmt(#20107,#20105) -expr_containers(#20107,#20001) -literals("/[abc&&bcd]/v","/[abc&&bcd]/v",#20107) -#20108=* -regexpterm(#20108,23,#20107,0,"[abc&&bcd]") -#20109=@"loc,{#10000},7,2,7,11" -locations_default(#20109,#10000,7,2,7,11) -hasLocation(#20108,#20109) -#20110=* -regexpterm(#20110,14,#20108,0,"a") -#20111=@"loc,{#10000},7,3,7,3" -locations_default(#20111,#10000,7,3,7,3) -hasLocation(#20110,#20111) -regexp_const_value(#20110,"a") -#20112=* -regexpterm(#20112,14,#20108,1,"b") -#20113=@"loc,{#10000},7,4,7,4" -locations_default(#20113,#10000,7,4,7,4) -hasLocation(#20112,#20113) -regexp_const_value(#20112,"b") -#20114=* -regexpterm(#20114,14,#20108,2,"c") -#20115=@"loc,{#10000},7,5,7,5" -locations_default(#20115,#10000,7,5,7,5) -hasLocation(#20114,#20115) -regexp_const_value(#20114,"c") -#20116=* -regexpterm(#20116,14,#20108,3,"&") -#20117=@"loc,{#10000},7,6,7,6" -locations_default(#20117,#10000,7,6,7,6) -hasLocation(#20116,#20117) -regexp_const_value(#20116,"&") -#20118=* -regexpterm(#20118,14,#20108,4,"&") -#20119=@"loc,{#10000},7,7,7,7" -locations_default(#20119,#10000,7,7,7,7) -hasLocation(#20118,#20119) -regexp_const_value(#20118,"&") -#20120=* -regexpterm(#20120,14,#20108,5,"b") -#20121=@"loc,{#10000},7,8,7,8" -locations_default(#20121,#10000,7,8,7,8) -hasLocation(#20120,#20121) -regexp_const_value(#20120,"b") -#20122=* -regexpterm(#20122,14,#20108,6,"c") -#20123=@"loc,{#10000},7,9,7,9" -locations_default(#20123,#10000,7,9,7,9) -hasLocation(#20122,#20123) -regexp_const_value(#20122,"c") -#20124=* -regexpterm(#20124,14,#20108,7,"d") -#20125=@"loc,{#10000},7,10,7,10" -locations_default(#20125,#10000,7,10,7,10) -hasLocation(#20124,#20125) -regexp_const_value(#20124,"d") -#20126=* -entry_cfg_node(#20126,#20001) -#20127=@"loc,{#10000},1,1,1,0" -locations_default(#20127,#10000,1,1,1,0) -hasLocation(#20126,#20127) -#20128=* -exit_cfg_node(#20128,#20001) -hasLocation(#20128,#20047) -successor(#20105,#20107) -successor(#20107,#20128) -successor(#20082,#20084) -successor(#20084,#20105) -successor(#20077,#20079) -successor(#20079,#20082) -successor(#20049,#20051) -successor(#20051,#20077) -successor(#20126,#20049) -numlines(#10000,7,4,7) +exit_cfg_node(#20093,#20001) +hasLocation(#20093,#20039) +successor(#20068,#20070) +successor(#20070,#20093) +successor(#20063,#20065) +successor(#20065,#20068) +successor(#20041,#20043) +successor(#20043,#20063) +successor(#20091,#20041) +numlines(#10000,6,3,6) filetype(#10000,"javascript") diff --git a/javascript/extractor/tests/es2024/output/trap/multi-intersection.js.trap b/javascript/extractor/tests/es2024/output/trap/multi-intersection.js.trap new file mode 100644 index 000000000000..7f997ccfca17 --- /dev/null +++ b/javascript/extractor/tests/es2024/output/trap/multi-intersection.js.trap @@ -0,0 +1,125 @@ +#10000=@"/multi-intersection.js;sourcefile" +files(#10000,"/multi-intersection.js") +#10001=@"/;folder" +folders(#10001,"/") +containerparent(#10001,#10000) +#10002=@"loc,{#10000},0,0,0,0" +locations_default(#10002,#10000,0,0,0,0) +hasLocation(#10000,#10002) +#20000=@"global_scope" +scopes(#20000,0) +#20001=@"script;{#10000},1,1" +#20002=* +lines(#20002,#20001,"/[[abc]&&[bcd]&&[a]]/v;"," +") +#20003=@"loc,{#10000},1,1,1,23" +locations_default(#20003,#10000,1,1,1,23) +hasLocation(#20002,#20003) +numlines(#20001,1,1,0) +#20004=* +tokeninfo(#20004,5,#20001,0,"/[[abc]&&[bcd]&&[a]]/v") +#20005=@"loc,{#10000},1,1,1,22" +locations_default(#20005,#10000,1,1,1,22) +hasLocation(#20004,#20005) +#20006=* +tokeninfo(#20006,8,#20001,1,";") +#20007=@"loc,{#10000},1,23,1,23" +locations_default(#20007,#10000,1,23,1,23) +hasLocation(#20006,#20007) +#20008=* +tokeninfo(#20008,0,#20001,2,"") +#20009=@"loc,{#10000},2,1,2,0" +locations_default(#20009,#10000,2,1,2,0) +hasLocation(#20008,#20009) +toplevels(#20001,0) +#20010=@"loc,{#10000},1,1,2,0" +locations_default(#20010,#10000,1,1,2,0) +hasLocation(#20001,#20010) +#20011=* +stmts(#20011,2,#20001,0,"/[[abc] ... [a]]/v;") +hasLocation(#20011,#20003) +stmt_containers(#20011,#20001) +#20012=* +exprs(#20012,5,#20011,0,"/[[abc] ... &[a]]/v") +hasLocation(#20012,#20005) +enclosing_stmt(#20012,#20011) +expr_containers(#20012,#20001) +literals("/[[abc]&&[bcd]&&[a]]/v","/[[abc]&&[bcd]&&[a]]/v",#20012) +#20013=* +regexpterm(#20013,23,#20012,0,"[[abc]&&[bcd]&&[a]]") +#20014=@"loc,{#10000},1,2,1,20" +locations_default(#20014,#10000,1,2,1,20) +hasLocation(#20013,#20014) +#20015=* +regexpterm(#20015,28,#20013,0,"[[abc]&&[bcd]&&[a]]") +hasLocation(#20015,#20014) +#20016=* +regexpterm(#20016,23,#20015,0,"[abc]") +#20017=@"loc,{#10000},1,3,1,7" +locations_default(#20017,#10000,1,3,1,7) +hasLocation(#20016,#20017) +#20018=* +regexpterm(#20018,14,#20016,0,"a") +#20019=@"loc,{#10000},1,4,1,4" +locations_default(#20019,#10000,1,4,1,4) +hasLocation(#20018,#20019) +regexp_const_value(#20018,"a") +#20020=* +regexpterm(#20020,14,#20016,1,"b") +#20021=@"loc,{#10000},1,5,1,5" +locations_default(#20021,#10000,1,5,1,5) +hasLocation(#20020,#20021) +regexp_const_value(#20020,"b") +#20022=* +regexpterm(#20022,14,#20016,2,"c") +#20023=@"loc,{#10000},1,6,1,6" +locations_default(#20023,#10000,1,6,1,6) +hasLocation(#20022,#20023) +regexp_const_value(#20022,"c") +#20024=* +regexpterm(#20024,23,#20015,1,"[bcd]") +#20025=@"loc,{#10000},1,10,1,14" +locations_default(#20025,#10000,1,10,1,14) +hasLocation(#20024,#20025) +#20026=* +regexpterm(#20026,14,#20024,0,"b") +#20027=@"loc,{#10000},1,11,1,11" +locations_default(#20027,#10000,1,11,1,11) +hasLocation(#20026,#20027) +regexp_const_value(#20026,"b") +#20028=* +regexpterm(#20028,14,#20024,1,"c") +#20029=@"loc,{#10000},1,12,1,12" +locations_default(#20029,#10000,1,12,1,12) +hasLocation(#20028,#20029) +regexp_const_value(#20028,"c") +#20030=* +regexpterm(#20030,14,#20024,2,"d") +#20031=@"loc,{#10000},1,13,1,13" +locations_default(#20031,#10000,1,13,1,13) +hasLocation(#20030,#20031) +regexp_const_value(#20030,"d") +#20032=* +regexpterm(#20032,23,#20015,2,"[a]") +#20033=@"loc,{#10000},1,17,1,19" +locations_default(#20033,#10000,1,17,1,19) +hasLocation(#20032,#20033) +#20034=* +regexpterm(#20034,14,#20032,0,"a") +#20035=@"loc,{#10000},1,18,1,18" +locations_default(#20035,#10000,1,18,1,18) +hasLocation(#20034,#20035) +regexp_const_value(#20034,"a") +#20036=* +entry_cfg_node(#20036,#20001) +#20037=@"loc,{#10000},1,1,1,0" +locations_default(#20037,#10000,1,1,1,0) +hasLocation(#20036,#20037) +#20038=* +exit_cfg_node(#20038,#20001) +hasLocation(#20038,#20009) +successor(#20011,#20012) +successor(#20012,#20038) +successor(#20036,#20011) +numlines(#10000,1,1,0) +filetype(#10000,"javascript") diff --git a/javascript/extractor/tests/es2024/output/trap/subtraction.js.trap b/javascript/extractor/tests/es2024/output/trap/subtraction.js.trap new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/javascript/extractor/tests/es2024/output/trap/union.js.trap b/javascript/extractor/tests/es2024/output/trap/union.js.trap new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/javascript/ql/lib/semmlecode.javascript.dbscheme b/javascript/ql/lib/semmlecode.javascript.dbscheme index eda7d3dc42ba..31951a28a4a5 100644 --- a/javascript/ql/lib/semmlecode.javascript.dbscheme +++ b/javascript/ql/lib/semmlecode.javascript.dbscheme @@ -860,7 +860,10 @@ case @regexpterm.kind of | 25 = @regexp_positive_lookbehind | 26 = @regexp_negative_lookbehind | 27 = @regexp_unicode_property_escape -| 28 = @regexp_intersection; +| 28 = @regexp_intersection +| 29 = @regexp_substraction +| 30 = @regexp_union +| 31 = @regexp_string_disjunction; regexp_parse_errors (unique int id: @regexp_parse_error, int regexp: @regexpterm ref,