diff --git a/csharp/downgrades/66044cfa5bbf2ecfabd06ead25e91db2bdd79764/old.dbscheme b/csharp/downgrades/66044cfa5bbf2ecfabd06ead25e91db2bdd79764/old.dbscheme
new file mode 100644
index 000000000000..66044cfa5bbf
--- /dev/null
+++ b/csharp/downgrades/66044cfa5bbf2ecfabd06ead25e91db2bdd79764/old.dbscheme
@@ -0,0 +1,1460 @@
+/* This is a dummy line to alter the dbscheme, so we can make a database upgrade
+ * without actually changing any of the dbscheme predicates. It contains a date
+ * to allow for such updates in the future as well.
+ *
+ * 2021-07-14
+ *
+ * DO NOT remove this comment carelessly, since it can revert the dbscheme back to a
+ * previously seen state (matching a previously seen SHA), which would make the upgrade
+ * mechanism not work properly.
+ */
+
+/**
+ * An invocation of the compiler. Note that more than one file may be
+ * compiled per invocation. For example, this command compiles three
+ * source files:
+ *
+ * csc f1.cs f2.cs f3.cs
+ *
+ * The `id` simply identifies the invocation, while `cwd` is the working
+ * directory from which the compiler was invoked.
+ */
+compilations(
+ unique int id : @compilation,
+ string cwd : string ref
+);
+
+compilation_info(
+ int id : @compilation ref,
+ string info_key: string ref,
+ string info_value: string ref
+)
+
+/**
+ * The arguments that were passed to the extractor for a compiler
+ * invocation. If `id` is for the compiler invocation
+ *
+ * csc f1.cs f2.cs f3.cs
+ *
+ * then typically there will be rows for
+ *
+ * num | arg
+ * --- | ---
+ * 0 | --compiler
+ * 1 | *path to compiler*
+ * 2 | f1.cs
+ * 3 | f2.cs
+ * 4 | f3.cs
+ */
+#keyset[id, num]
+compilation_args(
+ int id : @compilation ref,
+ int num : int ref,
+ string arg : string ref
+);
+
+/**
+ * The expanded arguments that were passed to the extractor for a
+ * compiler invocation. This is similar to `compilation_args`, but
+ * for a `@someFile.rsp` argument, it includes the arguments from that
+ * file, rather than just taking the argument literally.
+ */
+#keyset[id, num]
+compilation_expanded_args(
+ int id : @compilation ref,
+ int num : int ref,
+ string arg : string ref
+);
+
+/**
+ * The source files that are compiled by a compiler invocation.
+ * If `id` is for the compiler invocation
+ *
+ * csc f1.cs f2.cs f3.cs
+ *
+ * then there will be rows for
+ *
+ * num | arg
+ * --- | ---
+ * 0 | f1.cs
+ * 1 | f2.cs
+ * 2 | f3.cs
+ */
+#keyset[id, num]
+compilation_compiling_files(
+ int id : @compilation ref,
+ int num : int ref,
+ int file : @file ref
+);
+
+/**
+ * The references used by a compiler invocation.
+ * If `id` is for the compiler invocation
+ *
+ * csc f1.cs f2.cs f3.cs /r:ref1.dll /r:ref2.dll /r:ref3.dll
+ *
+ * then there will be rows for
+ *
+ * num | arg
+ * --- | ---
+ * 0 | ref1.dll
+ * 1 | ref2.dll
+ * 2 | ref3.dll
+ */
+#keyset[id, num]
+compilation_referencing_files(
+ int id : @compilation ref,
+ int num : int ref,
+ int file : @file ref
+);
+
+/**
+ * The time taken by the extractor for a compiler invocation.
+ *
+ * For each file `num`, there will be rows for
+ *
+ * kind | seconds
+ * ---- | ---
+ * 1 | CPU seconds used by the extractor frontend
+ * 2 | Elapsed seconds during the extractor frontend
+ * 3 | CPU seconds used by the extractor backend
+ * 4 | Elapsed seconds during the extractor backend
+ */
+#keyset[id, num, kind]
+compilation_time(
+ int id : @compilation ref,
+ int num : int ref,
+ /* kind:
+ 1 = frontend_cpu_seconds
+ 2 = frontend_elapsed_seconds
+ 3 = extractor_cpu_seconds
+ 4 = extractor_elapsed_seconds
+ */
+ int kind : int ref,
+ float seconds : float ref
+);
+
+/**
+ * An error or warning generated by the extractor.
+ * The diagnostic message `diagnostic` was generated during compiler
+ * invocation `compilation`, and is the `file_number_diagnostic_number`th
+ * message generated while extracting the `file_number`th file of that
+ * invocation.
+ */
+#keyset[compilation, file_number, file_number_diagnostic_number]
+diagnostic_for(
+ unique int diagnostic : @diagnostic ref,
+ int compilation : @compilation ref,
+ int file_number : int ref,
+ int file_number_diagnostic_number : int ref
+);
+
+diagnostics(
+ unique int id: @diagnostic,
+ int severity: int ref,
+ string error_tag: string ref,
+ string error_message: string ref,
+ string full_error_message: string ref,
+ int location: @location ref
+);
+
+extractor_messages(
+ unique int id: @extractor_message,
+ int severity: int ref,
+ string origin : string ref,
+ string text : string ref,
+ string entity : string ref,
+ int location: @location ref,
+ string stack_trace : string ref
+);
+
+/**
+ * If extraction was successful, then `cpu_seconds` and
+ * `elapsed_seconds` are the CPU time and elapsed time (respectively)
+ * that extraction took for compiler invocation `id`.
+ */
+compilation_finished(
+ unique int id : @compilation ref,
+ float cpu_seconds : float ref,
+ float elapsed_seconds : float ref
+);
+
+compilation_assembly(
+ unique int id : @compilation ref,
+ int assembly: @assembly ref
+)
+
+// Populated by the CSV extractor
+externalData(
+ int id: @externalDataElement,
+ string path: string ref,
+ int column: int ref,
+ string value: string ref);
+
+sourceLocationPrefix(
+ string prefix: string ref);
+
+/*
+ * C# dbscheme
+ */
+
+/** ELEMENTS **/
+
+@element = @declaration | @stmt | @expr | @modifier | @attribute | @namespace_declaration
+ | @using_directive | @type_parameter_constraints | @externalDataElement
+ | @xmllocatable | @asp_element | @namespace | @preprocessor_directive;
+
+@declaration = @callable | @generic | @assignable | @namespace;
+
+@named_element = @namespace | @declaration;
+
+@declaration_with_accessors = @property | @indexer | @event;
+
+@assignable = @variable | @assignable_with_accessors | @event;
+
+@assignable_with_accessors = @property | @indexer;
+
+@attributable = @assembly | @field | @parameter | @operator | @method | @constructor
+ | @destructor | @callable_accessor | @value_or_ref_type | @declaration_with_accessors
+ | @local_function | @lambda_expr;
+
+/** LOCATIONS, ASEMMBLIES, MODULES, FILES and FOLDERS **/
+
+@location = @location_default | @assembly;
+
+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);
+
+locations_mapped(
+ unique int id: @location_default ref,
+ int mapped_to: @location_default ref);
+
+@sourceline = @file | @callable | @xmllocatable;
+
+numlines(
+ int element_id: @sourceline ref,
+ int num_lines: int ref,
+ int num_code: int ref,
+ int num_comment: int ref);
+
+assemblies(
+ unique int id: @assembly,
+ int file: @file ref,
+ string fullname: string ref,
+ string name: string ref,
+ string version: string ref);
+
+files(
+ unique int id: @file,
+ string name: string ref);
+
+folders(
+ unique int id: @folder,
+ string name: string ref);
+
+@container = @folder | @file ;
+
+containerparent(
+ int parent: @container ref,
+ unique int child: @container ref);
+
+file_extraction_mode(
+ unique int file: @file ref,
+ int mode: int ref
+ /* 0 = normal, 1 = standalone extractor */
+ );
+
+/** NAMESPACES **/
+
+@type_container = @namespace | @type;
+
+namespaces(
+ unique int id: @namespace,
+ string name: string ref);
+
+namespace_declarations(
+ unique int id: @namespace_declaration,
+ int namespace_id: @namespace ref);
+
+namespace_declaration_location(
+ unique int id: @namespace_declaration ref,
+ int loc: @location ref);
+
+parent_namespace(
+ unique int child_id: @type_container ref,
+ int namespace_id: @namespace ref);
+
+@declaration_or_directive = @namespace_declaration | @type | @using_directive;
+
+parent_namespace_declaration(
+ int child_id: @declaration_or_directive ref, // cannot be unique because of partial classes
+ int namespace_id: @namespace_declaration ref);
+
+@using_directive = @using_namespace_directive | @using_static_directive;
+
+using_global(
+ unique int id: @using_directive ref
+);
+
+using_namespace_directives(
+ unique int id: @using_namespace_directive,
+ int namespace_id: @namespace ref);
+
+using_static_directives(
+ unique int id: @using_static_directive,
+ int type_id: @type_or_ref ref);
+
+using_directive_location(
+ unique int id: @using_directive ref,
+ int loc: @location ref);
+
+@preprocessor_directive = @pragma_warning | @pragma_checksum | @directive_define | @directive_undefine | @directive_warning
+ | @directive_error | @directive_nullable | @directive_line | @directive_region | @directive_endregion | @directive_if
+ | @directive_elif | @directive_else | @directive_endif;
+
+@conditional_directive = @directive_if | @directive_elif;
+@branch_directive = @directive_if | @directive_elif | @directive_else;
+
+directive_ifs(
+ unique int id: @directive_if,
+ int branchTaken: int ref, /* 0: false, 1: true */
+ int conditionValue: int ref); /* 0: false, 1: true */
+
+directive_elifs(
+ unique int id: @directive_elif,
+ int branchTaken: int ref, /* 0: false, 1: true */
+ int conditionValue: int ref, /* 0: false, 1: true */
+ int parent: @directive_if ref,
+ int index: int ref);
+
+directive_elses(
+ unique int id: @directive_else,
+ int branchTaken: int ref, /* 0: false, 1: true */
+ int parent: @directive_if ref,
+ int index: int ref);
+
+#keyset[id, start]
+directive_endifs(
+ unique int id: @directive_endif,
+ unique int start: @directive_if ref);
+
+directive_define_symbols(
+ unique int id: @define_symbol_expr ref,
+ string name: string ref);
+
+directive_regions(
+ unique int id: @directive_region,
+ string name: string ref);
+
+#keyset[id, start]
+directive_endregions(
+ unique int id: @directive_endregion,
+ unique int start: @directive_region ref);
+
+directive_lines(
+ unique int id: @directive_line,
+ int kind: int ref); /* 0: default, 1: hidden, 2: numeric, 3: span */
+
+directive_line_value(
+ unique int id: @directive_line ref,
+ int line: int ref);
+
+directive_line_file(
+ unique int id: @directive_line ref,
+ int file: @file ref);
+
+directive_line_offset(
+ unique int id: @directive_line ref,
+ int offset: int ref);
+
+directive_line_span(
+ unique int id: @directive_line ref,
+ int startLine: int ref,
+ int startColumn: int ref,
+ int endLine: int ref,
+ int endColumn: int ref);
+
+directive_nullables(
+ unique int id: @directive_nullable,
+ int setting: int ref, /* 0: disable, 1: enable, 2: restore */
+ int target: int ref); /* 0: none, 1: annotations, 2: warnings */
+
+directive_warnings(
+ unique int id: @directive_warning,
+ string message: string ref);
+
+directive_errors(
+ unique int id: @directive_error,
+ string message: string ref);
+
+directive_undefines(
+ unique int id: @directive_undefine,
+ string name: string ref);
+
+directive_defines(
+ unique int id: @directive_define,
+ string name: string ref);
+
+pragma_checksums(
+ unique int id: @pragma_checksum,
+ int file: @file ref,
+ string guid: string ref,
+ string bytes: string ref);
+
+pragma_warnings(
+ unique int id: @pragma_warning,
+ int kind: int ref /* 0 = disable, 1 = restore */);
+
+#keyset[id, index]
+pragma_warning_error_codes(
+ int id: @pragma_warning ref,
+ string errorCode: string ref,
+ int index: int ref);
+
+preprocessor_directive_location(
+ unique int id: @preprocessor_directive ref,
+ int loc: @location ref);
+
+preprocessor_directive_compilation(
+ int id: @preprocessor_directive ref,
+ int compilation: @compilation ref);
+
+preprocessor_directive_active(
+ unique int id: @preprocessor_directive ref,
+ int active: int ref); /* 0: false, 1: true */
+
+/** TYPES **/
+
+types(
+ unique int id: @type,
+ int kind: int ref,
+ string name: string ref);
+
+case @type.kind of
+ 1 = @bool_type
+| 2 = @char_type
+| 3 = @decimal_type
+| 4 = @sbyte_type
+| 5 = @short_type
+| 6 = @int_type
+| 7 = @long_type
+| 8 = @byte_type
+| 9 = @ushort_type
+| 10 = @uint_type
+| 11 = @ulong_type
+| 12 = @float_type
+| 13 = @double_type
+| 14 = @enum_type
+| 15 = @struct_type
+| 17 = @class_type
+| 19 = @interface_type
+| 20 = @delegate_type
+| 21 = @null_type
+| 22 = @type_parameter
+| 23 = @pointer_type
+| 24 = @nullable_type
+| 25 = @array_type
+| 26 = @void_type
+| 27 = @int_ptr_type
+| 28 = @uint_ptr_type
+| 29 = @dynamic_type
+| 30 = @arglist_type
+| 31 = @unknown_type
+| 32 = @tuple_type
+| 33 = @function_pointer_type
+| 34 = @inline_array_type
+ ;
+
+@simple_type = @bool_type | @char_type | @integral_type | @floating_point_type | @decimal_type;
+@integral_type = @signed_integral_type | @unsigned_integral_type;
+@signed_integral_type = @sbyte_type | @short_type | @int_type | @long_type;
+@unsigned_integral_type = @byte_type | @ushort_type | @uint_type | @ulong_type;
+@floating_point_type = @float_type | @double_type;
+@value_type = @simple_type | @enum_type | @struct_type | @nullable_type | @int_ptr_type
+ | @uint_ptr_type | @tuple_type | @void_type | @inline_array_type;
+@ref_type = @class_type | @interface_type | @array_type | @delegate_type | @null_type
+ | @dynamic_type;
+@value_or_ref_type = @value_type | @ref_type;
+
+typerefs(
+ unique int id: @typeref,
+ string name: string ref);
+
+typeref_type(
+ int id: @typeref ref,
+ unique int typeId: @type ref);
+
+@type_or_ref = @type | @typeref;
+
+array_element_type(
+ unique int array: @array_type ref,
+ int dimension: int ref,
+ int rank: int ref,
+ int element: @type_or_ref ref);
+
+nullable_underlying_type(
+ unique int nullable: @nullable_type ref,
+ int underlying: @type_or_ref ref);
+
+pointer_referent_type(
+ unique int pointer: @pointer_type ref,
+ int referent: @type_or_ref ref);
+
+enum_underlying_type(
+ unique int enum_id: @enum_type ref,
+ int underlying_type_id: @type_or_ref ref);
+
+delegate_return_type(
+ unique int delegate_id: @delegate_type ref,
+ int return_type_id: @type_or_ref ref);
+
+function_pointer_return_type(
+ unique int function_pointer_id: @function_pointer_type ref,
+ int return_type_id: @type_or_ref ref);
+
+extend(
+ int sub: @type ref,
+ int super: @type_or_ref ref);
+
+anonymous_types(
+ unique int id: @type ref);
+
+@interface_or_ref = @interface_type | @typeref;
+
+implement(
+ int sub: @type ref,
+ int super: @type_or_ref ref);
+
+type_location(
+ int id: @type ref,
+ int loc: @location ref);
+
+tuple_underlying_type(
+ unique int tuple: @tuple_type ref,
+ int struct: @type_or_ref ref);
+
+#keyset[tuple, index]
+tuple_element(
+ int tuple: @tuple_type ref,
+ int index: int ref,
+ unique int field: @field ref);
+
+attributes(
+ unique int id: @attribute,
+ int kind: int ref,
+ int type_id: @type_or_ref ref,
+ int target: @attributable ref);
+
+case @attribute.kind of
+ 0 = @attribute_default
+| 1 = @attribute_return
+| 2 = @attribute_assembly
+| 3 = @attribute_module
+;
+
+attribute_location(
+ int id: @attribute ref,
+ int loc: @location ref);
+
+@type_mention_parent = @element | @type_mention;
+
+type_mention(
+ unique int id: @type_mention,
+ int type_id: @type_or_ref ref,
+ int parent: @type_mention_parent ref);
+
+type_mention_location(
+ unique int id: @type_mention ref,
+ int loc: @location ref);
+
+@has_type_annotation = @assignable | @type_parameter | @callable | @expr | @delegate_type | @generic | @function_pointer_type;
+
+/**
+ * A direct annotation on an entity, for example `string? x;`.
+ *
+ * Annotations:
+ * 2 = reftype is not annotated "!"
+ * 3 = reftype is annotated "?"
+ * 4 = readonly ref type / in parameter
+ * 5 = ref type parameter, return or local variable
+ * 6 = out parameter
+ *
+ * Note that the annotation depends on the element it annotates.
+ * @assignable: The annotation is on the type of the assignable, for example the variable type.
+ * @type_parameter: The annotation is on the reftype constraint
+ * @callable: The annotation is on the return type
+ * @array_type: The annotation is on the element type
+ */
+type_annotation(int id: @has_type_annotation ref, int annotation: int ref);
+
+nullability(unique int nullability: @nullability, int kind: int ref);
+
+case @nullability.kind of
+ 0 = @oblivious
+| 1 = @not_annotated
+| 2 = @annotated
+;
+
+#keyset[parent, index]
+nullability_parent(int nullability: @nullability ref, int index: int ref, int parent: @nullability ref)
+
+type_nullability(int id: @has_type_annotation ref, int nullability: @nullability ref);
+
+/**
+ * The nullable flow state of an expression, as determined by Roslyn.
+ * 0 = none (default, not populated)
+ * 1 = not null
+ * 2 = maybe null
+ */
+expr_flowstate(unique int id: @expr ref, int state: int ref);
+
+/** GENERICS **/
+
+@generic = @type | @method | @local_function;
+
+type_parameters(
+ unique int id: @type_parameter ref,
+ int index: int ref,
+ int generic_id: @generic ref,
+ int variance: int ref /* none = 0, out = 1, in = 2 */);
+
+#keyset[constructed_id, index]
+type_arguments(
+ int id: @type_or_ref ref,
+ int index: int ref,
+ int constructed_id: @generic_or_ref ref);
+
+@generic_or_ref = @generic | @typeref;
+
+constructed_generic(
+ unique int constructed: @generic ref,
+ int generic: @generic_or_ref ref);
+
+type_parameter_constraints(
+ unique int id: @type_parameter_constraints,
+ int param_id: @type_parameter ref);
+
+type_parameter_constraints_location(
+ int id: @type_parameter_constraints ref,
+ int loc: @location ref);
+
+general_type_parameter_constraints(
+ int id: @type_parameter_constraints ref,
+ int kind: int ref /* class = 1, struct = 2, new = 3 */);
+
+specific_type_parameter_constraints(
+ int id: @type_parameter_constraints ref,
+ int base_id: @type_or_ref ref);
+
+specific_type_parameter_nullability(
+ int id: @type_parameter_constraints ref,
+ int base_id: @type_or_ref ref,
+ int nullability: @nullability ref);
+
+/** FUNCTION POINTERS */
+
+function_pointer_calling_conventions(
+ int id: @function_pointer_type ref,
+ int kind: int ref);
+
+#keyset[id, index]
+has_unmanaged_calling_conventions(
+ int id: @function_pointer_type ref,
+ int index: int ref,
+ int conv_id: @type_or_ref ref);
+
+/** MODIFIERS */
+
+@modifiable = @modifiable_direct | @event_accessor;
+
+@modifiable_direct = @member | @accessor | @local_function | @anonymous_function_expr;
+
+modifiers(
+ unique int id: @modifier,
+ string name: string ref);
+
+has_modifiers(
+ int id: @modifiable_direct ref,
+ int mod_id: @modifier ref);
+
+/** MEMBERS **/
+
+@member = @method | @constructor | @destructor | @field | @property | @event | @operator | @indexer | @type;
+
+@named_exprorstmt = @goto_stmt | @labeled_stmt | @expr;
+
+@virtualizable = @method | @property | @indexer | @event | @operator;
+
+exprorstmt_name(
+ unique int parent_id: @named_exprorstmt ref,
+ string name: string ref);
+
+nested_types(
+ unique int id: @type ref,
+ int declaring_type_id: @type ref,
+ int unbound_id: @type ref);
+
+properties(
+ unique int id: @property,
+ string name: string ref,
+ int declaring_type_id: @type ref,
+ int type_id: @type_or_ref ref,
+ int unbound_id: @property ref);
+
+property_location(
+ int id: @property ref,
+ int loc: @location ref);
+
+indexers(
+ unique int id: @indexer,
+ string name: string ref,
+ int declaring_type_id: @type ref,
+ int type_id: @type_or_ref ref,
+ int unbound_id: @indexer ref);
+
+indexer_location(
+ int id: @indexer ref,
+ int loc: @location ref);
+
+accessors(
+ unique int id: @accessor,
+ int kind: int ref,
+ string name: string ref,
+ int declaring_member_id: @member ref,
+ int unbound_id: @accessor ref);
+
+case @accessor.kind of
+ 1 = @getter
+| 2 = @setter
+ ;
+
+init_only_accessors(
+ unique int id: @accessor ref);
+
+accessor_location(
+ int id: @accessor ref,
+ int loc: @location ref);
+
+events(
+ unique int id: @event,
+ string name: string ref,
+ int declaring_type_id: @type ref,
+ int type_id: @type_or_ref ref,
+ int unbound_id: @event ref);
+
+event_location(
+ int id: @event ref,
+ int loc: @location ref);
+
+event_accessors(
+ unique int id: @event_accessor,
+ int kind: int ref,
+ string name: string ref,
+ int declaring_event_id: @event ref,
+ int unbound_id: @event_accessor ref);
+
+case @event_accessor.kind of
+ 1 = @add_event_accessor
+| 2 = @remove_event_accessor
+ ;
+
+event_accessor_location(
+ int id: @event_accessor ref,
+ int loc: @location ref);
+
+operators(
+ unique int id: @operator,
+ string name: string ref,
+ string symbol: string ref,
+ int declaring_type_id: @type ref,
+ int type_id: @type_or_ref ref,
+ int unbound_id: @operator ref);
+
+operator_location(
+ int id: @operator ref,
+ int loc: @location ref);
+
+constant_value(
+ int id: @variable ref,
+ string value: string ref);
+
+/** CALLABLES **/
+
+@callable = @method | @constructor | @destructor | @operator | @callable_accessor | @anonymous_function_expr | @local_function;
+
+@callable_accessor = @accessor | @event_accessor;
+
+methods(
+ unique int id: @method,
+ string name: string ref,
+ int declaring_type_id: @type ref,
+ int type_id: @type_or_ref ref,
+ int unbound_id: @method ref);
+
+method_location(
+ int id: @method ref,
+ int loc: @location ref);
+
+constructors(
+ unique int id: @constructor,
+ string name: string ref,
+ int declaring_type_id: @type ref,
+ int unbound_id: @constructor ref);
+
+constructor_location(
+ int id: @constructor ref,
+ int loc: @location ref);
+
+destructors(
+ unique int id: @destructor,
+ string name: string ref,
+ int declaring_type_id: @type ref,
+ int unbound_id: @destructor ref);
+
+destructor_location(
+ int id: @destructor ref,
+ int loc: @location ref);
+
+overrides(
+ int id: @callable ref,
+ int base_id: @callable ref);
+
+explicitly_implements(
+ int id: @member ref,
+ int interface_id: @interface_or_ref ref);
+
+local_functions(
+ unique int id: @local_function,
+ string name: string ref,
+ int return_type: @type ref,
+ int unbound_id: @local_function ref);
+
+local_function_stmts(
+ unique int fn: @local_function_stmt ref,
+ int stmt: @local_function ref);
+
+/** VARIABLES **/
+
+@variable = @local_scope_variable | @field;
+
+@local_scope_variable = @local_variable | @parameter;
+
+fields(
+ unique int id: @field,
+ int kind: int ref,
+ string name: string ref,
+ int declaring_type_id: @type ref,
+ int type_id: @type_or_ref ref,
+ int unbound_id: @field ref);
+
+case @field.kind of
+ 1 = @addressable_field
+| 2 = @constant
+ ;
+
+field_location(
+ int id: @field ref,
+ int loc: @location ref);
+
+localvars(
+ unique int id: @local_variable,
+ int kind: int ref,
+ string name: string ref,
+ int implicitly_typed: int ref /* 0 = no, 1 = yes */,
+ int type_id: @type_or_ref ref,
+ int parent_id: @local_var_decl_expr ref);
+
+case @local_variable.kind of
+ 1 = @addressable_local_variable
+| 2 = @local_constant
+| 3 = @local_variable_ref
+ ;
+
+localvar_location(
+ unique int id: @local_variable ref,
+ int loc: @location ref);
+
+@parameterizable = @callable | @delegate_type | @indexer | @function_pointer_type;
+
+#keyset[name, parent_id]
+#keyset[index, parent_id]
+params(
+ unique int id: @parameter,
+ string name: string ref,
+ int type_id: @type_or_ref ref,
+ int index: int ref,
+ int mode: int ref, /* value = 0, ref = 1, out = 2, params/array = 3, this = 4, in = 5, ref readonly = 6 */
+ int parent_id: @parameterizable ref,
+ int unbound_id: @parameter ref);
+
+param_location(
+ int id: @parameter ref,
+ int loc: @location ref);
+
+@has_scoped_annotation = @local_scope_variable
+
+scoped_annotation(
+ int id: @has_scoped_annotation ref,
+ int kind: int ref // scoped ref = 1, scoped value = 2
+ );
+
+/** STATEMENTS **/
+
+@exprorstmt_parent = @control_flow_element | @top_level_exprorstmt_parent;
+
+statements(
+ unique int id: @stmt,
+ int kind: int ref);
+
+#keyset[index, parent]
+stmt_parent(
+ unique int stmt: @stmt ref,
+ int index: int ref,
+ int parent: @control_flow_element ref);
+
+@top_level_stmt_parent = @callable;
+
+// [index, parent] is not a keyset because the same parent may be compiled multiple times
+stmt_parent_top_level(
+ unique int stmt: @stmt ref,
+ int index: int ref,
+ int parent: @top_level_stmt_parent ref);
+
+case @stmt.kind of
+ 1 = @block_stmt
+| 2 = @expr_stmt
+| 3 = @if_stmt
+| 4 = @switch_stmt
+| 5 = @while_stmt
+| 6 = @do_stmt
+| 7 = @for_stmt
+| 8 = @foreach_stmt
+| 9 = @break_stmt
+| 10 = @continue_stmt
+| 11 = @goto_stmt
+| 12 = @goto_case_stmt
+| 13 = @goto_default_stmt
+| 14 = @throw_stmt
+| 15 = @return_stmt
+| 16 = @yield_stmt
+| 17 = @try_stmt
+| 18 = @checked_stmt
+| 19 = @unchecked_stmt
+| 20 = @lock_stmt
+| 21 = @using_block_stmt
+| 22 = @var_decl_stmt
+| 23 = @const_decl_stmt
+| 24 = @empty_stmt
+| 25 = @unsafe_stmt
+| 26 = @fixed_stmt
+| 27 = @label_stmt
+| 28 = @catch
+| 29 = @case_stmt
+| 30 = @local_function_stmt
+| 31 = @using_decl_stmt
+ ;
+
+@using_stmt = @using_block_stmt | @using_decl_stmt;
+
+@labeled_stmt = @label_stmt | @case;
+
+@decl_stmt = @var_decl_stmt | @const_decl_stmt | @using_decl_stmt;
+
+@cond_stmt = @if_stmt | @switch_stmt;
+
+@loop_stmt = @while_stmt | @do_stmt | @for_stmt | @foreach_stmt;
+
+@jump_stmt = @break_stmt | @goto_any_stmt | @continue_stmt | @throw_stmt | @return_stmt
+ | @yield_stmt;
+
+@goto_any_stmt = @goto_default_stmt | @goto_case_stmt | @goto_stmt;
+
+
+stmt_location(
+ unique int id: @stmt ref,
+ int loc: @location ref);
+
+catch_type(
+ unique int catch_id: @catch ref,
+ int type_id: @type_or_ref ref,
+ int kind: int ref /* explicit = 1, implicit = 2 */);
+
+foreach_stmt_info(
+ unique int id: @foreach_stmt ref,
+ int kind: int ref /* non-async = 1, async = 2 */);
+
+@foreach_symbol = @method | @property | @type_or_ref;
+
+#keyset[id, kind]
+foreach_stmt_desugar(
+ int id: @foreach_stmt ref,
+ int symbol: @foreach_symbol ref,
+ int kind: int ref /* GetEnumeratorMethod = 1, CurrentProperty = 2, MoveNextMethod = 3, DisposeMethod = 4, ElementType = 5 */);
+
+/** EXPRESSIONS **/
+
+expressions(
+ unique int id: @expr,
+ int kind: int ref,
+ int type_id: @type_or_ref ref);
+
+#keyset[index, parent]
+expr_parent(
+ unique int expr: @expr ref,
+ int index: int ref,
+ int parent: @control_flow_element ref);
+
+@top_level_expr_parent = @attribute | @field | @property | @indexer | @parameter | @directive_if | @directive_elif;
+
+@top_level_exprorstmt_parent = @top_level_expr_parent | @top_level_stmt_parent;
+
+// [index, parent] is not a keyset because the same parent may be compiled multiple times
+expr_parent_top_level(
+ unique int expr: @expr ref,
+ int index: int ref,
+ int parent: @top_level_exprorstmt_parent ref);
+
+case @expr.kind of
+/* literal */
+ 1 = @bool_literal_expr
+| 2 = @char_literal_expr
+| 3 = @decimal_literal_expr
+| 4 = @int_literal_expr
+| 5 = @long_literal_expr
+| 6 = @uint_literal_expr
+| 7 = @ulong_literal_expr
+| 8 = @float_literal_expr
+| 9 = @double_literal_expr
+| 10 = @utf16_string_literal_expr
+| 11 = @null_literal_expr
+/* primary & unary */
+| 12 = @this_access_expr
+| 13 = @base_access_expr
+| 14 = @local_variable_access_expr
+| 15 = @parameter_access_expr
+| 16 = @field_access_expr
+| 17 = @property_access_expr
+| 18 = @method_access_expr
+| 19 = @event_access_expr
+| 20 = @indexer_access_expr
+| 21 = @array_access_expr
+| 22 = @type_access_expr
+| 23 = @typeof_expr
+| 24 = @method_invocation_expr
+| 25 = @delegate_invocation_expr
+| 26 = @operator_invocation_expr
+| 27 = @cast_expr
+| 28 = @object_creation_expr
+| 29 = @explicit_delegate_creation_expr
+| 30 = @implicit_delegate_creation_expr
+| 31 = @array_creation_expr
+| 32 = @default_expr
+| 33 = @plus_expr
+| 34 = @minus_expr
+| 35 = @bit_not_expr
+| 36 = @log_not_expr
+| 37 = @post_incr_expr
+| 38 = @post_decr_expr
+| 39 = @pre_incr_expr
+| 40 = @pre_decr_expr
+/* multiplicative */
+| 41 = @mul_expr
+| 42 = @div_expr
+| 43 = @rem_expr
+/* additive */
+| 44 = @add_expr
+| 45 = @sub_expr
+/* shift */
+| 46 = @lshift_expr
+| 47 = @rshift_expr
+/* relational */
+| 48 = @lt_expr
+| 49 = @gt_expr
+| 50 = @le_expr
+| 51 = @ge_expr
+/* equality */
+| 52 = @eq_expr
+| 53 = @ne_expr
+/* logical */
+| 54 = @bit_and_expr
+| 55 = @bit_xor_expr
+| 56 = @bit_or_expr
+| 57 = @log_and_expr
+| 58 = @log_or_expr
+/* type testing */
+| 59 = @is_expr
+| 60 = @as_expr
+/* null coalescing */
+| 61 = @null_coalescing_expr
+/* conditional */
+| 62 = @conditional_expr
+/* assignment */
+| 63 = @simple_assign_expr
+| 64 = @assign_add_expr
+| 65 = @assign_sub_expr
+| 66 = @assign_mul_expr
+| 67 = @assign_div_expr
+| 68 = @assign_rem_expr
+| 69 = @assign_and_expr
+| 70 = @assign_xor_expr
+| 71 = @assign_or_expr
+| 72 = @assign_lshift_expr
+| 73 = @assign_rshift_expr
+/* more */
+| 74 = @object_init_expr
+| 75 = @collection_init_expr
+| 76 = @array_init_expr
+| 77 = @checked_expr
+| 78 = @unchecked_expr
+| 79 = @constructor_init_expr
+| 80 = @add_event_expr
+| 81 = @remove_event_expr
+| 82 = @par_expr
+| 83 = @local_var_decl_expr
+| 84 = @lambda_expr
+| 85 = @anonymous_method_expr
+| 86 = @namespace_expr
+/* dynamic */
+| 92 = @dynamic_element_access_expr
+| 93 = @dynamic_member_access_expr
+/* unsafe */
+| 100 = @pointer_indirection_expr
+| 101 = @address_of_expr
+| 102 = @sizeof_expr
+/* async */
+| 103 = @await_expr
+/* C# 6.0 */
+| 104 = @nameof_expr
+| 105 = @interpolated_string_expr
+| 106 = @unknown_expr
+/* C# 7.0 */
+| 107 = @throw_expr
+| 108 = @tuple_expr
+| 109 = @local_function_invocation_expr
+| 110 = @ref_expr
+| 111 = @discard_expr
+/* C# 8.0 */
+| 112 = @range_expr
+| 113 = @index_expr
+| 114 = @switch_expr
+| 115 = @recursive_pattern_expr
+| 116 = @property_pattern_expr
+| 117 = @positional_pattern_expr
+| 118 = @switch_case_expr
+| 119 = @assign_coalesce_expr
+| 120 = @suppress_nullable_warning_expr
+| 121 = @namespace_access_expr
+/* C# 9.0 */
+| 122 = @lt_pattern_expr
+| 123 = @gt_pattern_expr
+| 124 = @le_pattern_expr
+| 125 = @ge_pattern_expr
+| 126 = @not_pattern_expr
+| 127 = @and_pattern_expr
+| 128 = @or_pattern_expr
+| 129 = @function_pointer_invocation_expr
+| 130 = @with_expr
+/* C# 11.0 */
+| 131 = @list_pattern_expr
+| 132 = @slice_pattern_expr
+| 133 = @urshift_expr
+| 134 = @assign_urshift_expr
+| 135 = @utf8_string_literal_expr
+/* C# 12.0 */
+| 136 = @collection_expr
+| 137 = @spread_element_expr
+| 138 = @interpolated_string_insert_expr
+/* Preprocessor */
+| 999 = @define_symbol_expr
+;
+
+@switch = @switch_stmt | @switch_expr;
+@case = @case_stmt | @switch_case_expr;
+@pattern_match = @case | @is_expr;
+@unary_pattern_expr = @not_pattern_expr;
+@relational_pattern_expr = @gt_pattern_expr | @lt_pattern_expr | @ge_pattern_expr | @le_pattern_expr;
+@binary_pattern_expr = @and_pattern_expr | @or_pattern_expr;
+
+@integer_literal_expr = @int_literal_expr | @long_literal_expr | @uint_literal_expr | @ulong_literal_expr;
+@real_literal_expr = @float_literal_expr | @double_literal_expr | @decimal_literal_expr;
+@string_literal_expr = @utf16_string_literal_expr | @utf8_string_literal_expr;
+@literal_expr = @bool_literal_expr | @char_literal_expr | @integer_literal_expr | @real_literal_expr
+ | @string_literal_expr | @null_literal_expr;
+
+@assign_expr = @simple_assign_expr | @assign_op_expr | @local_var_decl_expr;
+@assign_op_expr = @assign_arith_expr | @assign_bitwise_expr | @assign_event_expr | @assign_coalesce_expr;
+@assign_event_expr = @add_event_expr | @remove_event_expr;
+
+@assign_arith_expr = @assign_add_expr | @assign_sub_expr | @assign_mul_expr | @assign_div_expr
+ | @assign_rem_expr
+@assign_bitwise_expr = @assign_and_expr | @assign_or_expr | @assign_xor_expr
+ | @assign_lshift_expr | @assign_rshift_expr | @assign_urshift_expr;
+
+@member_access_expr = @field_access_expr | @property_access_expr | @indexer_access_expr | @event_access_expr
+ | @method_access_expr | @type_access_expr | @dynamic_member_access_expr;
+@access_expr = @member_access_expr | @this_access_expr | @base_access_expr | @assignable_access_expr | @namespace_access_expr;
+@element_access_expr = @indexer_access_expr | @array_access_expr | @dynamic_element_access_expr;
+
+@local_variable_access = @local_variable_access_expr | @local_var_decl_expr;
+@local_scope_variable_access_expr = @parameter_access_expr | @local_variable_access;
+@variable_access_expr = @local_scope_variable_access_expr | @field_access_expr;
+
+@assignable_access_expr = @variable_access_expr | @property_access_expr | @element_access_expr
+ | @event_access_expr | @dynamic_member_access_expr;
+
+@objectorcollection_init_expr = @object_init_expr | @collection_init_expr;
+
+@delegate_creation_expr = @explicit_delegate_creation_expr | @implicit_delegate_creation_expr;
+
+@bin_arith_op_expr = @mul_expr | @div_expr | @rem_expr | @add_expr | @sub_expr;
+@incr_op_expr = @pre_incr_expr | @post_incr_expr;
+@decr_op_expr = @pre_decr_expr | @post_decr_expr;
+@mut_op_expr = @incr_op_expr | @decr_op_expr;
+@un_arith_op_expr = @plus_expr | @minus_expr | @mut_op_expr;
+@arith_op_expr = @bin_arith_op_expr | @un_arith_op_expr;
+
+@ternary_log_op_expr = @conditional_expr;
+@bin_log_op_expr = @log_and_expr | @log_or_expr | @null_coalescing_expr;
+@un_log_op_expr = @log_not_expr;
+@log_expr = @un_log_op_expr | @bin_log_op_expr | @ternary_log_op_expr;
+
+@bin_bit_op_expr = @bit_and_expr | @bit_or_expr | @bit_xor_expr | @lshift_expr
+ | @rshift_expr | @urshift_expr;
+@un_bit_op_expr = @bit_not_expr;
+@bit_expr = @un_bit_op_expr | @bin_bit_op_expr;
+
+@equality_op_expr = @eq_expr | @ne_expr;
+@rel_op_expr = @gt_expr | @lt_expr| @ge_expr | @le_expr;
+@comp_expr = @equality_op_expr | @rel_op_expr;
+
+@op_expr = @assign_expr | @un_op | @bin_op | @ternary_op;
+
+@ternary_op = @ternary_log_op_expr;
+@bin_op = @bin_arith_op_expr | @bin_log_op_expr | @bin_bit_op_expr | @comp_expr;
+@un_op = @un_arith_op_expr | @un_log_op_expr | @un_bit_op_expr | @sizeof_expr
+ | @pointer_indirection_expr | @address_of_expr;
+
+@anonymous_function_expr = @lambda_expr | @anonymous_method_expr;
+
+@call = @method_invocation_expr | @constructor_init_expr | @operator_invocation_expr
+ | @delegate_invocation_expr | @object_creation_expr | @call_access_expr
+ | @local_function_invocation_expr | @function_pointer_invocation_expr;
+
+@call_access_expr = @property_access_expr | @event_access_expr | @indexer_access_expr;
+
+@late_bindable_expr = @dynamic_element_access_expr | @dynamic_member_access_expr
+ | @object_creation_expr | @method_invocation_expr | @operator_invocation_expr;
+
+@throw_element = @throw_expr | @throw_stmt;
+
+@implicitly_typeable_object_creation_expr = @object_creation_expr | @explicit_delegate_creation_expr;
+
+implicitly_typed_array_creation(
+ unique int id: @array_creation_expr ref);
+
+explicitly_sized_array_creation(
+ unique int id: @array_creation_expr ref);
+
+stackalloc_array_creation(
+ unique int id: @array_creation_expr ref);
+
+implicitly_typed_object_creation(
+ unique int id: @implicitly_typeable_object_creation_expr ref);
+
+mutator_invocation_mode(
+ unique int id: @operator_invocation_expr ref,
+ int mode: int ref /* prefix = 1, postfix = 2*/);
+
+expr_value(
+ unique int id: @expr ref,
+ string value: string ref);
+
+expr_call(
+ unique int caller_id: @expr ref,
+ int target_id: @callable ref);
+
+expr_access(
+ unique int accesser_id: @access_expr ref,
+ int target_id: @accessible ref);
+
+@accessible = @method | @assignable | @local_function | @namespace;
+
+expr_location(
+ unique int id: @expr ref,
+ int loc: @location ref);
+
+dynamic_member_name(
+ unique int id: @late_bindable_expr ref,
+ string name: string ref);
+
+@qualifiable_expr = @member_access_expr
+ | @method_invocation_expr
+ | @element_access_expr;
+
+conditional_access(
+ unique int id: @qualifiable_expr ref);
+
+expr_argument(
+ unique int id: @expr ref,
+ int mode: int ref);
+ /* mode is the same as params: value = 0, ref = 1, out = 2 */
+
+expr_argument_name(
+ unique int id: @expr ref,
+ string name: string ref);
+
+lambda_expr_return_type(
+ unique int id: @lambda_expr ref,
+ int type_id: @type_or_ref ref);
+
+/* Compiler generated */
+
+compiler_generated(unique int id: @element ref);
+
+/** CONTROL/DATA FLOW **/
+
+@control_flow_element = @stmt | @expr;
+
+/* 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;
+
+/* Comments */
+
+commentline(
+ unique int id: @commentline,
+ int kind: int ref,
+ string text: string ref,
+ string rawtext: string ref);
+
+case @commentline.kind of
+ 0 = @singlelinecomment
+| 1 = @xmldoccomment
+| 2 = @multilinecomment;
+
+commentline_location(
+ unique int id: @commentline ref,
+ int loc: @location ref);
+
+commentblock(
+ unique int id : @commentblock);
+
+commentblock_location(
+ unique int id: @commentblock ref,
+ int loc: @location ref);
+
+commentblock_binding(
+ int id: @commentblock ref,
+ int entity: @element ref,
+ int bindtype: int ref); /* 0: Parent, 1: Best, 2: Before, 3: After */
+
+commentblock_child(
+ int id: @commentblock ref,
+ int commentline: @commentline ref,
+ int index: int ref);
+
+/* ASP.NET */
+
+case @asp_element.kind of
+ 0=@asp_close_tag
+| 1=@asp_code
+| 2=@asp_comment
+| 3=@asp_data_binding
+| 4=@asp_directive
+| 5=@asp_open_tag
+| 6=@asp_quoted_string
+| 7=@asp_text
+| 8=@asp_xml_directive;
+
+@asp_attribute = @asp_code | @asp_data_binding | @asp_quoted_string;
+
+asp_elements(
+ unique int id: @asp_element,
+ int kind: int ref,
+ int loc: @location ref);
+
+asp_comment_server(unique int comment: @asp_comment ref);
+asp_code_inline(unique int code: @asp_code ref);
+asp_directive_attribute(
+ int directive: @asp_directive ref,
+ int index: int ref,
+ string name: string ref,
+ int value: @asp_quoted_string ref);
+asp_directive_name(
+ unique int directive: @asp_directive ref,
+ string name: string ref);
+asp_element_body(
+ unique int element: @asp_element ref,
+ string body: string ref);
+asp_tag_attribute(
+ int tag: @asp_open_tag ref,
+ int index: int ref,
+ string name: string ref,
+ int attribute: @asp_attribute ref);
+asp_tag_name(
+ unique int tag: @asp_open_tag ref,
+ string name: string ref);
+asp_tag_isempty(int tag: @asp_open_tag ref);
diff --git a/csharp/downgrades/66044cfa5bbf2ecfabd06ead25e91db2bdd79764/semmlecode.csharp.dbscheme b/csharp/downgrades/66044cfa5bbf2ecfabd06ead25e91db2bdd79764/semmlecode.csharp.dbscheme
new file mode 100644
index 000000000000..a2bda57dbc6e
--- /dev/null
+++ b/csharp/downgrades/66044cfa5bbf2ecfabd06ead25e91db2bdd79764/semmlecode.csharp.dbscheme
@@ -0,0 +1,1459 @@
+/* This is a dummy line to alter the dbscheme, so we can make a database upgrade
+ * without actually changing any of the dbscheme predicates. It contains a date
+ * to allow for such updates in the future as well.
+ *
+ * 2021-07-14
+ *
+ * DO NOT remove this comment carelessly, since it can revert the dbscheme back to a
+ * previously seen state (matching a previously seen SHA), which would make the upgrade
+ * mechanism not work properly.
+ */
+
+/**
+ * An invocation of the compiler. Note that more than one file may be
+ * compiled per invocation. For example, this command compiles three
+ * source files:
+ *
+ * csc f1.cs f2.cs f3.cs
+ *
+ * The `id` simply identifies the invocation, while `cwd` is the working
+ * directory from which the compiler was invoked.
+ */
+compilations(
+ unique int id : @compilation,
+ string cwd : string ref
+);
+
+compilation_info(
+ int id : @compilation ref,
+ string info_key: string ref,
+ string info_value: string ref
+)
+
+/**
+ * The arguments that were passed to the extractor for a compiler
+ * invocation. If `id` is for the compiler invocation
+ *
+ * csc f1.cs f2.cs f3.cs
+ *
+ * then typically there will be rows for
+ *
+ * num | arg
+ * --- | ---
+ * 0 | --compiler
+ * 1 | *path to compiler*
+ * 2 | f1.cs
+ * 3 | f2.cs
+ * 4 | f3.cs
+ */
+#keyset[id, num]
+compilation_args(
+ int id : @compilation ref,
+ int num : int ref,
+ string arg : string ref
+);
+
+/**
+ * The expanded arguments that were passed to the extractor for a
+ * compiler invocation. This is similar to `compilation_args`, but
+ * for a `@someFile.rsp` argument, it includes the arguments from that
+ * file, rather than just taking the argument literally.
+ */
+#keyset[id, num]
+compilation_expanded_args(
+ int id : @compilation ref,
+ int num : int ref,
+ string arg : string ref
+);
+
+/**
+ * The source files that are compiled by a compiler invocation.
+ * If `id` is for the compiler invocation
+ *
+ * csc f1.cs f2.cs f3.cs
+ *
+ * then there will be rows for
+ *
+ * num | arg
+ * --- | ---
+ * 0 | f1.cs
+ * 1 | f2.cs
+ * 2 | f3.cs
+ */
+#keyset[id, num]
+compilation_compiling_files(
+ int id : @compilation ref,
+ int num : int ref,
+ int file : @file ref
+);
+
+/**
+ * The references used by a compiler invocation.
+ * If `id` is for the compiler invocation
+ *
+ * csc f1.cs f2.cs f3.cs /r:ref1.dll /r:ref2.dll /r:ref3.dll
+ *
+ * then there will be rows for
+ *
+ * num | arg
+ * --- | ---
+ * 0 | ref1.dll
+ * 1 | ref2.dll
+ * 2 | ref3.dll
+ */
+#keyset[id, num]
+compilation_referencing_files(
+ int id : @compilation ref,
+ int num : int ref,
+ int file : @file ref
+);
+
+/**
+ * The time taken by the extractor for a compiler invocation.
+ *
+ * For each file `num`, there will be rows for
+ *
+ * kind | seconds
+ * ---- | ---
+ * 1 | CPU seconds used by the extractor frontend
+ * 2 | Elapsed seconds during the extractor frontend
+ * 3 | CPU seconds used by the extractor backend
+ * 4 | Elapsed seconds during the extractor backend
+ */
+#keyset[id, num, kind]
+compilation_time(
+ int id : @compilation ref,
+ int num : int ref,
+ /* kind:
+ 1 = frontend_cpu_seconds
+ 2 = frontend_elapsed_seconds
+ 3 = extractor_cpu_seconds
+ 4 = extractor_elapsed_seconds
+ */
+ int kind : int ref,
+ float seconds : float ref
+);
+
+/**
+ * An error or warning generated by the extractor.
+ * The diagnostic message `diagnostic` was generated during compiler
+ * invocation `compilation`, and is the `file_number_diagnostic_number`th
+ * message generated while extracting the `file_number`th file of that
+ * invocation.
+ */
+#keyset[compilation, file_number, file_number_diagnostic_number]
+diagnostic_for(
+ unique int diagnostic : @diagnostic ref,
+ int compilation : @compilation ref,
+ int file_number : int ref,
+ int file_number_diagnostic_number : int ref
+);
+
+diagnostics(
+ unique int id: @diagnostic,
+ int severity: int ref,
+ string error_tag: string ref,
+ string error_message: string ref,
+ string full_error_message: string ref,
+ int location: @location ref
+);
+
+extractor_messages(
+ unique int id: @extractor_message,
+ int severity: int ref,
+ string origin : string ref,
+ string text : string ref,
+ string entity : string ref,
+ int location: @location ref,
+ string stack_trace : string ref
+);
+
+/**
+ * If extraction was successful, then `cpu_seconds` and
+ * `elapsed_seconds` are the CPU time and elapsed time (respectively)
+ * that extraction took for compiler invocation `id`.
+ */
+compilation_finished(
+ unique int id : @compilation ref,
+ float cpu_seconds : float ref,
+ float elapsed_seconds : float ref
+);
+
+compilation_assembly(
+ unique int id : @compilation ref,
+ int assembly: @assembly ref
+)
+
+// Populated by the CSV extractor
+externalData(
+ int id: @externalDataElement,
+ string path: string ref,
+ int column: int ref,
+ string value: string ref);
+
+sourceLocationPrefix(
+ string prefix: string ref);
+
+/*
+ * C# dbscheme
+ */
+
+/** ELEMENTS **/
+
+@element = @declaration | @stmt | @expr | @modifier | @attribute | @namespace_declaration
+ | @using_directive | @type_parameter_constraints | @externalDataElement
+ | @xmllocatable | @asp_element | @namespace | @preprocessor_directive;
+
+@declaration = @callable | @generic | @assignable | @namespace;
+
+@named_element = @namespace | @declaration;
+
+@declaration_with_accessors = @property | @indexer | @event;
+
+@assignable = @variable | @assignable_with_accessors | @event;
+
+@assignable_with_accessors = @property | @indexer;
+
+@attributable = @assembly | @field | @parameter | @operator | @method | @constructor
+ | @destructor | @callable_accessor | @value_or_ref_type | @declaration_with_accessors
+ | @local_function | @lambda_expr;
+
+/** LOCATIONS, ASEMMBLIES, MODULES, FILES and FOLDERS **/
+
+@location = @location_default | @assembly;
+
+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);
+
+locations_mapped(
+ unique int id: @location_default ref,
+ int mapped_to: @location_default ref);
+
+@sourceline = @file | @callable | @xmllocatable;
+
+numlines(
+ int element_id: @sourceline ref,
+ int num_lines: int ref,
+ int num_code: int ref,
+ int num_comment: int ref);
+
+assemblies(
+ unique int id: @assembly,
+ int file: @file ref,
+ string fullname: string ref,
+ string name: string ref,
+ string version: string ref);
+
+files(
+ unique int id: @file,
+ string name: string ref);
+
+folders(
+ unique int id: @folder,
+ string name: string ref);
+
+@container = @folder | @file ;
+
+containerparent(
+ int parent: @container ref,
+ unique int child: @container ref);
+
+file_extraction_mode(
+ unique int file: @file ref,
+ int mode: int ref
+ /* 0 = normal, 1 = standalone extractor */
+ );
+
+/** NAMESPACES **/
+
+@type_container = @namespace | @type;
+
+namespaces(
+ unique int id: @namespace,
+ string name: string ref);
+
+namespace_declarations(
+ unique int id: @namespace_declaration,
+ int namespace_id: @namespace ref);
+
+namespace_declaration_location(
+ unique int id: @namespace_declaration ref,
+ int loc: @location ref);
+
+parent_namespace(
+ unique int child_id: @type_container ref,
+ int namespace_id: @namespace ref);
+
+@declaration_or_directive = @namespace_declaration | @type | @using_directive;
+
+parent_namespace_declaration(
+ int child_id: @declaration_or_directive ref, // cannot be unique because of partial classes
+ int namespace_id: @namespace_declaration ref);
+
+@using_directive = @using_namespace_directive | @using_static_directive;
+
+using_global(
+ unique int id: @using_directive ref
+);
+
+using_namespace_directives(
+ unique int id: @using_namespace_directive,
+ int namespace_id: @namespace ref);
+
+using_static_directives(
+ unique int id: @using_static_directive,
+ int type_id: @type_or_ref ref);
+
+using_directive_location(
+ unique int id: @using_directive ref,
+ int loc: @location ref);
+
+@preprocessor_directive = @pragma_warning | @pragma_checksum | @directive_define | @directive_undefine | @directive_warning
+ | @directive_error | @directive_nullable | @directive_line | @directive_region | @directive_endregion | @directive_if
+ | @directive_elif | @directive_else | @directive_endif;
+
+@conditional_directive = @directive_if | @directive_elif;
+@branch_directive = @directive_if | @directive_elif | @directive_else;
+
+directive_ifs(
+ unique int id: @directive_if,
+ int branchTaken: int ref, /* 0: false, 1: true */
+ int conditionValue: int ref); /* 0: false, 1: true */
+
+directive_elifs(
+ unique int id: @directive_elif,
+ int branchTaken: int ref, /* 0: false, 1: true */
+ int conditionValue: int ref, /* 0: false, 1: true */
+ int parent: @directive_if ref,
+ int index: int ref);
+
+directive_elses(
+ unique int id: @directive_else,
+ int branchTaken: int ref, /* 0: false, 1: true */
+ int parent: @directive_if ref,
+ int index: int ref);
+
+#keyset[id, start]
+directive_endifs(
+ unique int id: @directive_endif,
+ unique int start: @directive_if ref);
+
+directive_define_symbols(
+ unique int id: @define_symbol_expr ref,
+ string name: string ref);
+
+directive_regions(
+ unique int id: @directive_region,
+ string name: string ref);
+
+#keyset[id, start]
+directive_endregions(
+ unique int id: @directive_endregion,
+ unique int start: @directive_region ref);
+
+directive_lines(
+ unique int id: @directive_line,
+ int kind: int ref); /* 0: default, 1: hidden, 2: numeric, 3: span */
+
+directive_line_value(
+ unique int id: @directive_line ref,
+ int line: int ref);
+
+directive_line_file(
+ unique int id: @directive_line ref,
+ int file: @file ref);
+
+directive_line_offset(
+ unique int id: @directive_line ref,
+ int offset: int ref);
+
+directive_line_span(
+ unique int id: @directive_line ref,
+ int startLine: int ref,
+ int startColumn: int ref,
+ int endLine: int ref,
+ int endColumn: int ref);
+
+directive_nullables(
+ unique int id: @directive_nullable,
+ int setting: int ref, /* 0: disable, 1: enable, 2: restore */
+ int target: int ref); /* 0: none, 1: annotations, 2: warnings */
+
+directive_warnings(
+ unique int id: @directive_warning,
+ string message: string ref);
+
+directive_errors(
+ unique int id: @directive_error,
+ string message: string ref);
+
+directive_undefines(
+ unique int id: @directive_undefine,
+ string name: string ref);
+
+directive_defines(
+ unique int id: @directive_define,
+ string name: string ref);
+
+pragma_checksums(
+ unique int id: @pragma_checksum,
+ int file: @file ref,
+ string guid: string ref,
+ string bytes: string ref);
+
+pragma_warnings(
+ unique int id: @pragma_warning,
+ int kind: int ref /* 0 = disable, 1 = restore */);
+
+#keyset[id, index]
+pragma_warning_error_codes(
+ int id: @pragma_warning ref,
+ string errorCode: string ref,
+ int index: int ref);
+
+preprocessor_directive_location(
+ unique int id: @preprocessor_directive ref,
+ int loc: @location ref);
+
+preprocessor_directive_compilation(
+ int id: @preprocessor_directive ref,
+ int compilation: @compilation ref);
+
+preprocessor_directive_active(
+ unique int id: @preprocessor_directive ref,
+ int active: int ref); /* 0: false, 1: true */
+
+/** TYPES **/
+
+types(
+ unique int id: @type,
+ int kind: int ref,
+ string name: string ref);
+
+case @type.kind of
+ 1 = @bool_type
+| 2 = @char_type
+| 3 = @decimal_type
+| 4 = @sbyte_type
+| 5 = @short_type
+| 6 = @int_type
+| 7 = @long_type
+| 8 = @byte_type
+| 9 = @ushort_type
+| 10 = @uint_type
+| 11 = @ulong_type
+| 12 = @float_type
+| 13 = @double_type
+| 14 = @enum_type
+| 15 = @struct_type
+| 17 = @class_type
+| 19 = @interface_type
+| 20 = @delegate_type
+| 21 = @null_type
+| 22 = @type_parameter
+| 23 = @pointer_type
+| 24 = @nullable_type
+| 25 = @array_type
+| 26 = @void_type
+| 27 = @int_ptr_type
+| 28 = @uint_ptr_type
+| 29 = @dynamic_type
+| 30 = @arglist_type
+| 31 = @unknown_type
+| 32 = @tuple_type
+| 33 = @function_pointer_type
+| 34 = @inline_array_type
+ ;
+
+@simple_type = @bool_type | @char_type | @integral_type | @floating_point_type | @decimal_type;
+@integral_type = @signed_integral_type | @unsigned_integral_type;
+@signed_integral_type = @sbyte_type | @short_type | @int_type | @long_type;
+@unsigned_integral_type = @byte_type | @ushort_type | @uint_type | @ulong_type;
+@floating_point_type = @float_type | @double_type;
+@value_type = @simple_type | @enum_type | @struct_type | @nullable_type | @int_ptr_type
+ | @uint_ptr_type | @tuple_type | @void_type | @inline_array_type;
+@ref_type = @class_type | @interface_type | @array_type | @delegate_type | @null_type
+ | @dynamic_type;
+@value_or_ref_type = @value_type | @ref_type;
+
+typerefs(
+ unique int id: @typeref,
+ string name: string ref);
+
+typeref_type(
+ int id: @typeref ref,
+ unique int typeId: @type ref);
+
+@type_or_ref = @type | @typeref;
+
+array_element_type(
+ unique int array: @array_type ref,
+ int dimension: int ref,
+ int rank: int ref,
+ int element: @type_or_ref ref);
+
+nullable_underlying_type(
+ unique int nullable: @nullable_type ref,
+ int underlying: @type_or_ref ref);
+
+pointer_referent_type(
+ unique int pointer: @pointer_type ref,
+ int referent: @type_or_ref ref);
+
+enum_underlying_type(
+ unique int enum_id: @enum_type ref,
+ int underlying_type_id: @type_or_ref ref);
+
+delegate_return_type(
+ unique int delegate_id: @delegate_type ref,
+ int return_type_id: @type_or_ref ref);
+
+function_pointer_return_type(
+ unique int function_pointer_id: @function_pointer_type ref,
+ int return_type_id: @type_or_ref ref);
+
+extend(
+ int sub: @type ref,
+ int super: @type_or_ref ref);
+
+anonymous_types(
+ unique int id: @type ref);
+
+@interface_or_ref = @interface_type | @typeref;
+
+implement(
+ int sub: @type ref,
+ int super: @type_or_ref ref);
+
+type_location(
+ int id: @type ref,
+ int loc: @location ref);
+
+tuple_underlying_type(
+ unique int tuple: @tuple_type ref,
+ int struct: @type_or_ref ref);
+
+#keyset[tuple, index]
+tuple_element(
+ int tuple: @tuple_type ref,
+ int index: int ref,
+ unique int field: @field ref);
+
+attributes(
+ unique int id: @attribute,
+ int kind: int ref,
+ int type_id: @type_or_ref ref,
+ int target: @attributable ref);
+
+case @attribute.kind of
+ 0 = @attribute_default
+| 1 = @attribute_return
+| 2 = @attribute_assembly
+| 3 = @attribute_module
+;
+
+attribute_location(
+ int id: @attribute ref,
+ int loc: @location ref);
+
+@type_mention_parent = @element | @type_mention;
+
+type_mention(
+ unique int id: @type_mention,
+ int type_id: @type_or_ref ref,
+ int parent: @type_mention_parent ref);
+
+type_mention_location(
+ unique int id: @type_mention ref,
+ int loc: @location ref);
+
+@has_type_annotation = @assignable | @type_parameter | @callable | @expr | @delegate_type | @generic | @function_pointer_type;
+
+/**
+ * A direct annotation on an entity, for example `string? x;`.
+ *
+ * Annotations:
+ * 2 = reftype is not annotated "!"
+ * 3 = reftype is annotated "?"
+ * 4 = readonly ref type / in parameter
+ * 5 = ref type parameter, return or local variable
+ * 6 = out parameter
+ *
+ * Note that the annotation depends on the element it annotates.
+ * @assignable: The annotation is on the type of the assignable, for example the variable type.
+ * @type_parameter: The annotation is on the reftype constraint
+ * @callable: The annotation is on the return type
+ * @array_type: The annotation is on the element type
+ */
+type_annotation(int id: @has_type_annotation ref, int annotation: int ref);
+
+nullability(unique int nullability: @nullability, int kind: int ref);
+
+case @nullability.kind of
+ 0 = @oblivious
+| 1 = @not_annotated
+| 2 = @annotated
+;
+
+#keyset[parent, index]
+nullability_parent(int nullability: @nullability ref, int index: int ref, int parent: @nullability ref)
+
+type_nullability(int id: @has_type_annotation ref, int nullability: @nullability ref);
+
+/**
+ * The nullable flow state of an expression, as determined by Roslyn.
+ * 0 = none (default, not populated)
+ * 1 = not null
+ * 2 = maybe null
+ */
+expr_flowstate(unique int id: @expr ref, int state: int ref);
+
+/** GENERICS **/
+
+@generic = @type | @method | @local_function;
+
+type_parameters(
+ unique int id: @type_parameter ref,
+ int index: int ref,
+ int generic_id: @generic ref,
+ int variance: int ref /* none = 0, out = 1, in = 2 */);
+
+#keyset[constructed_id, index]
+type_arguments(
+ int id: @type_or_ref ref,
+ int index: int ref,
+ int constructed_id: @generic_or_ref ref);
+
+@generic_or_ref = @generic | @typeref;
+
+constructed_generic(
+ unique int constructed: @generic ref,
+ int generic: @generic_or_ref ref);
+
+type_parameter_constraints(
+ unique int id: @type_parameter_constraints,
+ int param_id: @type_parameter ref);
+
+type_parameter_constraints_location(
+ int id: @type_parameter_constraints ref,
+ int loc: @location ref);
+
+general_type_parameter_constraints(
+ int id: @type_parameter_constraints ref,
+ int kind: int ref /* class = 1, struct = 2, new = 3 */);
+
+specific_type_parameter_constraints(
+ int id: @type_parameter_constraints ref,
+ int base_id: @type_or_ref ref);
+
+specific_type_parameter_nullability(
+ int id: @type_parameter_constraints ref,
+ int base_id: @type_or_ref ref,
+ int nullability: @nullability ref);
+
+/** FUNCTION POINTERS */
+
+function_pointer_calling_conventions(
+ int id: @function_pointer_type ref,
+ int kind: int ref);
+
+#keyset[id, index]
+has_unmanaged_calling_conventions(
+ int id: @function_pointer_type ref,
+ int index: int ref,
+ int conv_id: @type_or_ref ref);
+
+/** MODIFIERS */
+
+@modifiable = @modifiable_direct | @event_accessor;
+
+@modifiable_direct = @member | @accessor | @local_function | @anonymous_function_expr;
+
+modifiers(
+ unique int id: @modifier,
+ string name: string ref);
+
+has_modifiers(
+ int id: @modifiable_direct ref,
+ int mod_id: @modifier ref);
+
+/** MEMBERS **/
+
+@member = @method | @constructor | @destructor | @field | @property | @event | @operator | @indexer | @type;
+
+@named_exprorstmt = @goto_stmt | @labeled_stmt | @expr;
+
+@virtualizable = @method | @property | @indexer | @event | @operator;
+
+exprorstmt_name(
+ unique int parent_id: @named_exprorstmt ref,
+ string name: string ref);
+
+nested_types(
+ unique int id: @type ref,
+ int declaring_type_id: @type ref,
+ int unbound_id: @type ref);
+
+properties(
+ unique int id: @property,
+ string name: string ref,
+ int declaring_type_id: @type ref,
+ int type_id: @type_or_ref ref,
+ int unbound_id: @property ref);
+
+property_location(
+ int id: @property ref,
+ int loc: @location ref);
+
+indexers(
+ unique int id: @indexer,
+ string name: string ref,
+ int declaring_type_id: @type ref,
+ int type_id: @type_or_ref ref,
+ int unbound_id: @indexer ref);
+
+indexer_location(
+ int id: @indexer ref,
+ int loc: @location ref);
+
+accessors(
+ unique int id: @accessor,
+ int kind: int ref,
+ string name: string ref,
+ int declaring_member_id: @member ref,
+ int unbound_id: @accessor ref);
+
+case @accessor.kind of
+ 1 = @getter
+| 2 = @setter
+ ;
+
+init_only_accessors(
+ unique int id: @accessor ref);
+
+accessor_location(
+ int id: @accessor ref,
+ int loc: @location ref);
+
+events(
+ unique int id: @event,
+ string name: string ref,
+ int declaring_type_id: @type ref,
+ int type_id: @type_or_ref ref,
+ int unbound_id: @event ref);
+
+event_location(
+ int id: @event ref,
+ int loc: @location ref);
+
+event_accessors(
+ unique int id: @event_accessor,
+ int kind: int ref,
+ string name: string ref,
+ int declaring_event_id: @event ref,
+ int unbound_id: @event_accessor ref);
+
+case @event_accessor.kind of
+ 1 = @add_event_accessor
+| 2 = @remove_event_accessor
+ ;
+
+event_accessor_location(
+ int id: @event_accessor ref,
+ int loc: @location ref);
+
+operators(
+ unique int id: @operator,
+ string name: string ref,
+ string symbol: string ref,
+ int declaring_type_id: @type ref,
+ int type_id: @type_or_ref ref,
+ int unbound_id: @operator ref);
+
+operator_location(
+ int id: @operator ref,
+ int loc: @location ref);
+
+constant_value(
+ int id: @variable ref,
+ string value: string ref);
+
+/** CALLABLES **/
+
+@callable = @method | @constructor | @destructor | @operator | @callable_accessor | @anonymous_function_expr | @local_function;
+
+@callable_accessor = @accessor | @event_accessor;
+
+methods(
+ unique int id: @method,
+ string name: string ref,
+ int declaring_type_id: @type ref,
+ int type_id: @type_or_ref ref,
+ int unbound_id: @method ref);
+
+method_location(
+ int id: @method ref,
+ int loc: @location ref);
+
+constructors(
+ unique int id: @constructor,
+ string name: string ref,
+ int declaring_type_id: @type ref,
+ int unbound_id: @constructor ref);
+
+constructor_location(
+ int id: @constructor ref,
+ int loc: @location ref);
+
+destructors(
+ unique int id: @destructor,
+ string name: string ref,
+ int declaring_type_id: @type ref,
+ int unbound_id: @destructor ref);
+
+destructor_location(
+ int id: @destructor ref,
+ int loc: @location ref);
+
+overrides(
+ int id: @callable ref,
+ int base_id: @callable ref);
+
+explicitly_implements(
+ int id: @member ref,
+ int interface_id: @interface_or_ref ref);
+
+local_functions(
+ unique int id: @local_function,
+ string name: string ref,
+ int return_type: @type ref,
+ int unbound_id: @local_function ref);
+
+local_function_stmts(
+ unique int fn: @local_function_stmt ref,
+ int stmt: @local_function ref);
+
+/** VARIABLES **/
+
+@variable = @local_scope_variable | @field;
+
+@local_scope_variable = @local_variable | @parameter;
+
+fields(
+ unique int id: @field,
+ int kind: int ref,
+ string name: string ref,
+ int declaring_type_id: @type ref,
+ int type_id: @type_or_ref ref,
+ int unbound_id: @field ref);
+
+case @field.kind of
+ 1 = @addressable_field
+| 2 = @constant
+ ;
+
+field_location(
+ int id: @field ref,
+ int loc: @location ref);
+
+localvars(
+ unique int id: @local_variable,
+ int kind: int ref,
+ string name: string ref,
+ int implicitly_typed: int ref /* 0 = no, 1 = yes */,
+ int type_id: @type_or_ref ref,
+ int parent_id: @local_var_decl_expr ref);
+
+case @local_variable.kind of
+ 1 = @addressable_local_variable
+| 2 = @local_constant
+| 3 = @local_variable_ref
+ ;
+
+localvar_location(
+ unique int id: @local_variable ref,
+ int loc: @location ref);
+
+@parameterizable = @callable | @delegate_type | @indexer | @function_pointer_type;
+
+#keyset[name, parent_id]
+#keyset[index, parent_id]
+params(
+ unique int id: @parameter,
+ string name: string ref,
+ int type_id: @type_or_ref ref,
+ int index: int ref,
+ int mode: int ref, /* value = 0, ref = 1, out = 2, params/array = 3, this = 4, in = 5, ref readonly = 6 */
+ int parent_id: @parameterizable ref,
+ int unbound_id: @parameter ref);
+
+param_location(
+ int id: @parameter ref,
+ int loc: @location ref);
+
+@has_scoped_annotation = @local_scope_variable
+
+scoped_annotation(
+ int id: @has_scoped_annotation ref,
+ int kind: int ref // scoped ref = 1, scoped value = 2
+ );
+
+/** STATEMENTS **/
+
+@exprorstmt_parent = @control_flow_element | @top_level_exprorstmt_parent;
+
+statements(
+ unique int id: @stmt,
+ int kind: int ref);
+
+#keyset[index, parent]
+stmt_parent(
+ unique int stmt: @stmt ref,
+ int index: int ref,
+ int parent: @control_flow_element ref);
+
+@top_level_stmt_parent = @callable;
+
+// [index, parent] is not a keyset because the same parent may be compiled multiple times
+stmt_parent_top_level(
+ unique int stmt: @stmt ref,
+ int index: int ref,
+ int parent: @top_level_stmt_parent ref);
+
+case @stmt.kind of
+ 1 = @block_stmt
+| 2 = @expr_stmt
+| 3 = @if_stmt
+| 4 = @switch_stmt
+| 5 = @while_stmt
+| 6 = @do_stmt
+| 7 = @for_stmt
+| 8 = @foreach_stmt
+| 9 = @break_stmt
+| 10 = @continue_stmt
+| 11 = @goto_stmt
+| 12 = @goto_case_stmt
+| 13 = @goto_default_stmt
+| 14 = @throw_stmt
+| 15 = @return_stmt
+| 16 = @yield_stmt
+| 17 = @try_stmt
+| 18 = @checked_stmt
+| 19 = @unchecked_stmt
+| 20 = @lock_stmt
+| 21 = @using_block_stmt
+| 22 = @var_decl_stmt
+| 23 = @const_decl_stmt
+| 24 = @empty_stmt
+| 25 = @unsafe_stmt
+| 26 = @fixed_stmt
+| 27 = @label_stmt
+| 28 = @catch
+| 29 = @case_stmt
+| 30 = @local_function_stmt
+| 31 = @using_decl_stmt
+ ;
+
+@using_stmt = @using_block_stmt | @using_decl_stmt;
+
+@labeled_stmt = @label_stmt | @case;
+
+@decl_stmt = @var_decl_stmt | @const_decl_stmt | @using_decl_stmt;
+
+@cond_stmt = @if_stmt | @switch_stmt;
+
+@loop_stmt = @while_stmt | @do_stmt | @for_stmt | @foreach_stmt;
+
+@jump_stmt = @break_stmt | @goto_any_stmt | @continue_stmt | @throw_stmt | @return_stmt
+ | @yield_stmt;
+
+@goto_any_stmt = @goto_default_stmt | @goto_case_stmt | @goto_stmt;
+
+
+stmt_location(
+ unique int id: @stmt ref,
+ int loc: @location ref);
+
+catch_type(
+ unique int catch_id: @catch ref,
+ int type_id: @type_or_ref ref,
+ int kind: int ref /* explicit = 1, implicit = 2 */);
+
+foreach_stmt_info(
+ unique int id: @foreach_stmt ref,
+ int kind: int ref /* non-async = 1, async = 2 */);
+
+@foreach_symbol = @method | @property | @type_or_ref;
+
+#keyset[id, kind]
+foreach_stmt_desugar(
+ int id: @foreach_stmt ref,
+ int symbol: @foreach_symbol ref,
+ int kind: int ref /* GetEnumeratorMethod = 1, CurrentProperty = 2, MoveNextMethod = 3, DisposeMethod = 4, ElementType = 5 */);
+
+/** EXPRESSIONS **/
+
+expressions(
+ unique int id: @expr,
+ int kind: int ref,
+ int type_id: @type_or_ref ref);
+
+#keyset[index, parent]
+expr_parent(
+ unique int expr: @expr ref,
+ int index: int ref,
+ int parent: @control_flow_element ref);
+
+@top_level_expr_parent = @attribute | @field | @property | @indexer | @parameter | @directive_if | @directive_elif;
+
+@top_level_exprorstmt_parent = @top_level_expr_parent | @top_level_stmt_parent;
+
+// [index, parent] is not a keyset because the same parent may be compiled multiple times
+expr_parent_top_level(
+ unique int expr: @expr ref,
+ int index: int ref,
+ int parent: @top_level_exprorstmt_parent ref);
+
+case @expr.kind of
+/* literal */
+ 1 = @bool_literal_expr
+| 2 = @char_literal_expr
+| 3 = @decimal_literal_expr
+| 4 = @int_literal_expr
+| 5 = @long_literal_expr
+| 6 = @uint_literal_expr
+| 7 = @ulong_literal_expr
+| 8 = @float_literal_expr
+| 9 = @double_literal_expr
+| 10 = @utf16_string_literal_expr
+| 11 = @null_literal_expr
+/* primary & unary */
+| 12 = @this_access_expr
+| 13 = @base_access_expr
+| 14 = @local_variable_access_expr
+| 15 = @parameter_access_expr
+| 16 = @field_access_expr
+| 17 = @property_access_expr
+| 18 = @method_access_expr
+| 19 = @event_access_expr
+| 20 = @indexer_access_expr
+| 21 = @array_access_expr
+| 22 = @type_access_expr
+| 23 = @typeof_expr
+| 24 = @method_invocation_expr
+| 25 = @delegate_invocation_expr
+| 26 = @operator_invocation_expr
+| 27 = @cast_expr
+| 28 = @object_creation_expr
+| 29 = @explicit_delegate_creation_expr
+| 30 = @implicit_delegate_creation_expr
+| 31 = @array_creation_expr
+| 32 = @default_expr
+| 33 = @plus_expr
+| 34 = @minus_expr
+| 35 = @bit_not_expr
+| 36 = @log_not_expr
+| 37 = @post_incr_expr
+| 38 = @post_decr_expr
+| 39 = @pre_incr_expr
+| 40 = @pre_decr_expr
+/* multiplicative */
+| 41 = @mul_expr
+| 42 = @div_expr
+| 43 = @rem_expr
+/* additive */
+| 44 = @add_expr
+| 45 = @sub_expr
+/* shift */
+| 46 = @lshift_expr
+| 47 = @rshift_expr
+/* relational */
+| 48 = @lt_expr
+| 49 = @gt_expr
+| 50 = @le_expr
+| 51 = @ge_expr
+/* equality */
+| 52 = @eq_expr
+| 53 = @ne_expr
+/* logical */
+| 54 = @bit_and_expr
+| 55 = @bit_xor_expr
+| 56 = @bit_or_expr
+| 57 = @log_and_expr
+| 58 = @log_or_expr
+/* type testing */
+| 59 = @is_expr
+| 60 = @as_expr
+/* null coalescing */
+| 61 = @null_coalescing_expr
+/* conditional */
+| 62 = @conditional_expr
+/* assignment */
+| 63 = @simple_assign_expr
+| 64 = @assign_add_expr
+| 65 = @assign_sub_expr
+| 66 = @assign_mul_expr
+| 67 = @assign_div_expr
+| 68 = @assign_rem_expr
+| 69 = @assign_and_expr
+| 70 = @assign_xor_expr
+| 71 = @assign_or_expr
+| 72 = @assign_lshift_expr
+| 73 = @assign_rshift_expr
+/* more */
+| 74 = @object_init_expr
+| 75 = @collection_init_expr
+| 76 = @array_init_expr
+| 77 = @checked_expr
+| 78 = @unchecked_expr
+| 79 = @constructor_init_expr
+| 80 = @add_event_expr
+| 81 = @remove_event_expr
+| 82 = @par_expr
+| 83 = @local_var_decl_expr
+| 84 = @lambda_expr
+| 85 = @anonymous_method_expr
+| 86 = @namespace_expr
+/* dynamic */
+| 92 = @dynamic_element_access_expr
+| 93 = @dynamic_member_access_expr
+/* unsafe */
+| 100 = @pointer_indirection_expr
+| 101 = @address_of_expr
+| 102 = @sizeof_expr
+/* async */
+| 103 = @await_expr
+/* C# 6.0 */
+| 104 = @nameof_expr
+| 105 = @interpolated_string_expr
+| 106 = @unknown_expr
+/* C# 7.0 */
+| 107 = @throw_expr
+| 108 = @tuple_expr
+| 109 = @local_function_invocation_expr
+| 110 = @ref_expr
+| 111 = @discard_expr
+/* C# 8.0 */
+| 112 = @range_expr
+| 113 = @index_expr
+| 114 = @switch_expr
+| 115 = @recursive_pattern_expr
+| 116 = @property_pattern_expr
+| 117 = @positional_pattern_expr
+| 118 = @switch_case_expr
+| 119 = @assign_coalesce_expr
+| 120 = @suppress_nullable_warning_expr
+| 121 = @namespace_access_expr
+/* C# 9.0 */
+| 122 = @lt_pattern_expr
+| 123 = @gt_pattern_expr
+| 124 = @le_pattern_expr
+| 125 = @ge_pattern_expr
+| 126 = @not_pattern_expr
+| 127 = @and_pattern_expr
+| 128 = @or_pattern_expr
+| 129 = @function_pointer_invocation_expr
+| 130 = @with_expr
+/* C# 11.0 */
+| 131 = @list_pattern_expr
+| 132 = @slice_pattern_expr
+| 133 = @urshift_expr
+| 134 = @assign_urshift_expr
+| 135 = @utf8_string_literal_expr
+/* C# 12.0 */
+| 136 = @collection_expr
+| 137 = @spread_element_expr
+/* Preprocessor */
+| 999 = @define_symbol_expr
+;
+
+@switch = @switch_stmt | @switch_expr;
+@case = @case_stmt | @switch_case_expr;
+@pattern_match = @case | @is_expr;
+@unary_pattern_expr = @not_pattern_expr;
+@relational_pattern_expr = @gt_pattern_expr | @lt_pattern_expr | @ge_pattern_expr | @le_pattern_expr;
+@binary_pattern_expr = @and_pattern_expr | @or_pattern_expr;
+
+@integer_literal_expr = @int_literal_expr | @long_literal_expr | @uint_literal_expr | @ulong_literal_expr;
+@real_literal_expr = @float_literal_expr | @double_literal_expr | @decimal_literal_expr;
+@string_literal_expr = @utf16_string_literal_expr | @utf8_string_literal_expr;
+@literal_expr = @bool_literal_expr | @char_literal_expr | @integer_literal_expr | @real_literal_expr
+ | @string_literal_expr | @null_literal_expr;
+
+@assign_expr = @simple_assign_expr | @assign_op_expr | @local_var_decl_expr;
+@assign_op_expr = @assign_arith_expr | @assign_bitwise_expr | @assign_event_expr | @assign_coalesce_expr;
+@assign_event_expr = @add_event_expr | @remove_event_expr;
+
+@assign_arith_expr = @assign_add_expr | @assign_sub_expr | @assign_mul_expr | @assign_div_expr
+ | @assign_rem_expr
+@assign_bitwise_expr = @assign_and_expr | @assign_or_expr | @assign_xor_expr
+ | @assign_lshift_expr | @assign_rshift_expr | @assign_urshift_expr;
+
+@member_access_expr = @field_access_expr | @property_access_expr | @indexer_access_expr | @event_access_expr
+ | @method_access_expr | @type_access_expr | @dynamic_member_access_expr;
+@access_expr = @member_access_expr | @this_access_expr | @base_access_expr | @assignable_access_expr | @namespace_access_expr;
+@element_access_expr = @indexer_access_expr | @array_access_expr | @dynamic_element_access_expr;
+
+@local_variable_access = @local_variable_access_expr | @local_var_decl_expr;
+@local_scope_variable_access_expr = @parameter_access_expr | @local_variable_access;
+@variable_access_expr = @local_scope_variable_access_expr | @field_access_expr;
+
+@assignable_access_expr = @variable_access_expr | @property_access_expr | @element_access_expr
+ | @event_access_expr | @dynamic_member_access_expr;
+
+@objectorcollection_init_expr = @object_init_expr | @collection_init_expr;
+
+@delegate_creation_expr = @explicit_delegate_creation_expr | @implicit_delegate_creation_expr;
+
+@bin_arith_op_expr = @mul_expr | @div_expr | @rem_expr | @add_expr | @sub_expr;
+@incr_op_expr = @pre_incr_expr | @post_incr_expr;
+@decr_op_expr = @pre_decr_expr | @post_decr_expr;
+@mut_op_expr = @incr_op_expr | @decr_op_expr;
+@un_arith_op_expr = @plus_expr | @minus_expr | @mut_op_expr;
+@arith_op_expr = @bin_arith_op_expr | @un_arith_op_expr;
+
+@ternary_log_op_expr = @conditional_expr;
+@bin_log_op_expr = @log_and_expr | @log_or_expr | @null_coalescing_expr;
+@un_log_op_expr = @log_not_expr;
+@log_expr = @un_log_op_expr | @bin_log_op_expr | @ternary_log_op_expr;
+
+@bin_bit_op_expr = @bit_and_expr | @bit_or_expr | @bit_xor_expr | @lshift_expr
+ | @rshift_expr | @urshift_expr;
+@un_bit_op_expr = @bit_not_expr;
+@bit_expr = @un_bit_op_expr | @bin_bit_op_expr;
+
+@equality_op_expr = @eq_expr | @ne_expr;
+@rel_op_expr = @gt_expr | @lt_expr| @ge_expr | @le_expr;
+@comp_expr = @equality_op_expr | @rel_op_expr;
+
+@op_expr = @assign_expr | @un_op | @bin_op | @ternary_op;
+
+@ternary_op = @ternary_log_op_expr;
+@bin_op = @bin_arith_op_expr | @bin_log_op_expr | @bin_bit_op_expr | @comp_expr;
+@un_op = @un_arith_op_expr | @un_log_op_expr | @un_bit_op_expr | @sizeof_expr
+ | @pointer_indirection_expr | @address_of_expr;
+
+@anonymous_function_expr = @lambda_expr | @anonymous_method_expr;
+
+@call = @method_invocation_expr | @constructor_init_expr | @operator_invocation_expr
+ | @delegate_invocation_expr | @object_creation_expr | @call_access_expr
+ | @local_function_invocation_expr | @function_pointer_invocation_expr;
+
+@call_access_expr = @property_access_expr | @event_access_expr | @indexer_access_expr;
+
+@late_bindable_expr = @dynamic_element_access_expr | @dynamic_member_access_expr
+ | @object_creation_expr | @method_invocation_expr | @operator_invocation_expr;
+
+@throw_element = @throw_expr | @throw_stmt;
+
+@implicitly_typeable_object_creation_expr = @object_creation_expr | @explicit_delegate_creation_expr;
+
+implicitly_typed_array_creation(
+ unique int id: @array_creation_expr ref);
+
+explicitly_sized_array_creation(
+ unique int id: @array_creation_expr ref);
+
+stackalloc_array_creation(
+ unique int id: @array_creation_expr ref);
+
+implicitly_typed_object_creation(
+ unique int id: @implicitly_typeable_object_creation_expr ref);
+
+mutator_invocation_mode(
+ unique int id: @operator_invocation_expr ref,
+ int mode: int ref /* prefix = 1, postfix = 2*/);
+
+expr_value(
+ unique int id: @expr ref,
+ string value: string ref);
+
+expr_call(
+ unique int caller_id: @expr ref,
+ int target_id: @callable ref);
+
+expr_access(
+ unique int accesser_id: @access_expr ref,
+ int target_id: @accessible ref);
+
+@accessible = @method | @assignable | @local_function | @namespace;
+
+expr_location(
+ unique int id: @expr ref,
+ int loc: @location ref);
+
+dynamic_member_name(
+ unique int id: @late_bindable_expr ref,
+ string name: string ref);
+
+@qualifiable_expr = @member_access_expr
+ | @method_invocation_expr
+ | @element_access_expr;
+
+conditional_access(
+ unique int id: @qualifiable_expr ref);
+
+expr_argument(
+ unique int id: @expr ref,
+ int mode: int ref);
+ /* mode is the same as params: value = 0, ref = 1, out = 2 */
+
+expr_argument_name(
+ unique int id: @expr ref,
+ string name: string ref);
+
+lambda_expr_return_type(
+ unique int id: @lambda_expr ref,
+ int type_id: @type_or_ref ref);
+
+/* Compiler generated */
+
+compiler_generated(unique int id: @element ref);
+
+/** CONTROL/DATA FLOW **/
+
+@control_flow_element = @stmt | @expr;
+
+/* 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;
+
+/* Comments */
+
+commentline(
+ unique int id: @commentline,
+ int kind: int ref,
+ string text: string ref,
+ string rawtext: string ref);
+
+case @commentline.kind of
+ 0 = @singlelinecomment
+| 1 = @xmldoccomment
+| 2 = @multilinecomment;
+
+commentline_location(
+ unique int id: @commentline ref,
+ int loc: @location ref);
+
+commentblock(
+ unique int id : @commentblock);
+
+commentblock_location(
+ unique int id: @commentblock ref,
+ int loc: @location ref);
+
+commentblock_binding(
+ int id: @commentblock ref,
+ int entity: @element ref,
+ int bindtype: int ref); /* 0: Parent, 1: Best, 2: Before, 3: After */
+
+commentblock_child(
+ int id: @commentblock ref,
+ int commentline: @commentline ref,
+ int index: int ref);
+
+/* ASP.NET */
+
+case @asp_element.kind of
+ 0=@asp_close_tag
+| 1=@asp_code
+| 2=@asp_comment
+| 3=@asp_data_binding
+| 4=@asp_directive
+| 5=@asp_open_tag
+| 6=@asp_quoted_string
+| 7=@asp_text
+| 8=@asp_xml_directive;
+
+@asp_attribute = @asp_code | @asp_data_binding | @asp_quoted_string;
+
+asp_elements(
+ unique int id: @asp_element,
+ int kind: int ref,
+ int loc: @location ref);
+
+asp_comment_server(unique int comment: @asp_comment ref);
+asp_code_inline(unique int code: @asp_code ref);
+asp_directive_attribute(
+ int directive: @asp_directive ref,
+ int index: int ref,
+ string name: string ref,
+ int value: @asp_quoted_string ref);
+asp_directive_name(
+ unique int directive: @asp_directive ref,
+ string name: string ref);
+asp_element_body(
+ unique int element: @asp_element ref,
+ string body: string ref);
+asp_tag_attribute(
+ int tag: @asp_open_tag ref,
+ int index: int ref,
+ string name: string ref,
+ int attribute: @asp_attribute ref);
+asp_tag_name(
+ unique int tag: @asp_open_tag ref,
+ string name: string ref);
+asp_tag_isempty(int tag: @asp_open_tag ref);
diff --git a/csharp/downgrades/66044cfa5bbf2ecfabd06ead25e91db2bdd79764/string_interpol_insert.ql b/csharp/downgrades/66044cfa5bbf2ecfabd06ead25e91db2bdd79764/string_interpol_insert.ql
new file mode 100644
index 000000000000..3f83b1ea3cce
--- /dev/null
+++ b/csharp/downgrades/66044cfa5bbf2ecfabd06ead25e91db2bdd79764/string_interpol_insert.ql
@@ -0,0 +1,39 @@
+class Expr extends @expr {
+ string toString() { none() }
+}
+
+class TypeOrRef extends @type_or_ref {
+ string toString() { none() }
+}
+
+class InterpolatedStringInsertExpr extends Expr, @interpolated_string_insert_expr { }
+
+private predicate remove_expr(Expr e) {
+ exists(InterpolatedStringInsertExpr ie |
+ e = ie
+ or
+ // Alignment
+ expr_parent(e, 1, ie)
+ or
+ // Format
+ expr_parent(e, 2, ie)
+ )
+}
+
+query predicate new_expressions(Expr e, int kind, TypeOrRef t) {
+ expressions(e, kind, t) and
+ // Remove the syntheetic intert expression and previously un-extracted children
+ not remove_expr(e)
+}
+
+query predicate new_expr_parent(Expr e, int child, Expr parent) {
+ expr_parent(e, child, parent) and
+ not remove_expr(e) and
+ not remove_expr(parent)
+ or
+ // Use the string interpolation as parent instead of the synthetic insert expression
+ exists(InterpolatedStringInsertExpr ie |
+ expr_parent(e, 0, ie) and
+ expr_parent(ie, child, parent)
+ )
+}
diff --git a/csharp/downgrades/66044cfa5bbf2ecfabd06ead25e91db2bdd79764/upgrade.properties b/csharp/downgrades/66044cfa5bbf2ecfabd06ead25e91db2bdd79764/upgrade.properties
new file mode 100644
index 000000000000..9e8118060f2b
--- /dev/null
+++ b/csharp/downgrades/66044cfa5bbf2ecfabd06ead25e91db2bdd79764/upgrade.properties
@@ -0,0 +1,4 @@
+description: Remove `interpolated_string_insert_expr` kind.
+compatibility: backwards
+expressions.rel: run string_interpol_insert.qlo new_expressions
+expr_parent.rel: run string_interpol_insert.qlo new_expr_parent
diff --git a/csharp/extractor/Semmle.Extraction.CSharp/CodeAnalysisExtensions/SymbolExtensions.cs b/csharp/extractor/Semmle.Extraction.CSharp/CodeAnalysisExtensions/SymbolExtensions.cs
index cb1f36f8a2de..72f78f160598 100644
--- a/csharp/extractor/Semmle.Extraction.CSharp/CodeAnalysisExtensions/SymbolExtensions.cs
+++ b/csharp/extractor/Semmle.Extraction.CSharp/CodeAnalysisExtensions/SymbolExtensions.cs
@@ -29,6 +29,15 @@ public AnnotatedTypeSymbol(ITypeSymbol? symbol, NullableAnnotation nullability)
symbol is null ? (AnnotatedTypeSymbol?)null : new AnnotatedTypeSymbol(symbol, NullableAnnotation.None);
}
+ internal static class AnnotatedTypeSymbolExtensions
+ {
+ ///
+ /// Returns true if the type is a string type.
+ ///
+ public static bool IsStringType(this AnnotatedTypeSymbol? type) =>
+ type.HasValue && type.Value.Symbol?.SpecialType == SpecialType.System_String;
+ }
+
internal static class SymbolExtensions
{
///
diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Binary.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Binary.cs
index eeb1b9ba63b2..d4cc5cc81d58 100644
--- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Binary.cs
+++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Binary.cs
@@ -18,7 +18,7 @@ private Expression CreateChild(Context cx, ExpressionSyntax node, int child)
{
// If this is a "+" expression we might need to wrap the child expressions
// in ToString calls
- return Kind == ExprKind.ADD
+ return Kind == ExprKind.ADD && Type.IsStringType()
? ImplicitToString.Create(cx, node, this, child)
: Create(cx, node, this, child);
}
diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/ImplicitToString.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/ImplicitToString.cs
index 32c00f8a729e..bebc0c2c5d81 100644
--- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/ImplicitToString.cs
+++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/ImplicitToString.cs
@@ -39,16 +39,13 @@ private ImplicitToString(ExpressionNodeInfo info, IMethodSymbol toString) : base
Context.TrapWriter.Writer.expr_call(this, target);
}
- private static bool IsStringType(AnnotatedTypeSymbol? type) =>
- type.HasValue && type.Value.Symbol?.SpecialType == SpecialType.System_String;
-
///
/// Creates a new expression, adding a compiler generated `ToString` call if required.
///
- public static Expression Create(Context cx, ExpressionSyntax node, Expression parent, int child)
+ public static Expression Create(Context cx, ExpressionSyntax node, IExpressionParentEntity parent, int child)
{
var info = new ExpressionNodeInfo(cx, node, parent, child);
- return CreateFromNode(info.SetImplicitToString(IsStringType(parent.Type) && !IsStringType(info.Type)));
+ return CreateFromNode(info.SetImplicitToString(!info.Type.IsStringType()));
}
///
diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/InterpolatedString.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/InterpolatedString.cs
index 6d17d1e7f176..5cd13dffe20b 100644
--- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/InterpolatedString.cs
+++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/InterpolatedString.cs
@@ -1,5 +1,4 @@
using System.IO;
-using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Semmle.Extraction.Kinds;
@@ -21,15 +20,7 @@ protected override void PopulateExpression(TextWriter trapFile)
{
case SyntaxKind.Interpolation:
var interpolation = (InterpolationSyntax)c;
- var exp = interpolation.Expression;
- if (Context.GetTypeInfo(exp).Type is ITypeSymbol type && !type.ImplementsIFormattable())
- {
- ImplicitToString.Create(Context, exp, this, child++);
- }
- else
- {
- Create(Context, exp, this, child++);
- }
+ new InterpolatedStringInsert(Context, interpolation, this, child++);
break;
case SyntaxKind.InterpolatedStringText:
// Create a string literal
diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/InterpolatedStringInsert.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/InterpolatedStringInsert.cs
new file mode 100644
index 000000000000..beac4bc64a64
--- /dev/null
+++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/InterpolatedStringInsert.cs
@@ -0,0 +1,41 @@
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Semmle.Extraction.Kinds;
+
+namespace Semmle.Extraction.CSharp.Entities.Expressions
+{
+ internal class InterpolatedStringInsert : Expression
+ {
+ public InterpolatedStringInsert(Context cx, InterpolationSyntax syntax, Expression parent, int child) :
+ base(new ExpressionInfo(cx, null, cx.CreateLocation(syntax.GetLocation()), ExprKind.INTERPOLATED_STRING_INSERT, parent, child, isCompilerGenerated: false, null))
+ {
+ var exp = syntax.Expression;
+ if (parent.Type.IsStringType() &&
+ cx.GetTypeInfo(exp).Type is ITypeSymbol type &&
+ !type.ImplementsIFormattable())
+ {
+ ImplicitToString.Create(cx, exp, this, 0);
+ }
+ else
+ {
+ Create(cx, exp, this, 0);
+ }
+
+ // Hardcode the child number of the optional alignment clause to 1 and format clause to 2.
+ // This simplifies the logic in QL.
+ if (syntax.AlignmentClause?.Value is ExpressionSyntax alignment)
+ {
+ Create(cx, alignment, this, 1);
+ }
+
+ if (syntax.FormatClause is InterpolationFormatClauseSyntax format)
+ {
+ var f = format.FormatStringToken.ValueText;
+ var t = AnnotatedTypeSymbol.CreateNotAnnotated(cx.Compilation.GetSpecialType(SpecialType.System_String));
+ new Expression(new ExpressionInfo(cx, t, cx.CreateLocation(format.GetLocation()), ExprKind.UTF16_STRING_LITERAL, this, 2, isCompilerGenerated: false, f));
+ }
+
+ }
+ }
+
+}
diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Kinds/ExprKind.cs b/csharp/extractor/Semmle.Extraction.CSharp/Kinds/ExprKind.cs
index 297acda9524c..46a694192842 100644
--- a/csharp/extractor/Semmle.Extraction.CSharp/Kinds/ExprKind.cs
+++ b/csharp/extractor/Semmle.Extraction.CSharp/Kinds/ExprKind.cs
@@ -132,6 +132,7 @@ public enum ExprKind
UTF8_STRING_LITERAL = 135,
COLLECTION = 136,
SPREAD_ELEMENT = 137,
+ INTERPOLATED_STRING_INSERT = 138,
DEFINE_SYMBOL = 999,
}
}
diff --git a/csharp/ql/lib/change-notes/2025-03-21-string-interpolation.md b/csharp/ql/lib/change-notes/2025-03-21-string-interpolation.md
new file mode 100644
index 000000000000..3507d35b5135
--- /dev/null
+++ b/csharp/ql/lib/change-notes/2025-03-21-string-interpolation.md
@@ -0,0 +1,4 @@
+---
+category: minorAnalysis
+---
+* The *alignment* and *format* clauses in string interpolation expressions are now extracted. That is, in `$"Hello {name,align:format}"` *name*, *align* and *format* are extracted as children of the string interpolation *insert* `{name,align:format}`.
diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/TaintTrackingPrivate.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/TaintTrackingPrivate.qll
index 1a044a77777d..b7681994e2c3 100644
--- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/TaintTrackingPrivate.qll
+++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/TaintTrackingPrivate.qll
@@ -66,6 +66,9 @@ private class LocalTaintExprStepConfiguration extends ControlFlowReachabilityCon
e1 = e2.(InterpolatedStringExpr).getAChild() and
scope = e2
or
+ e1 = e2.(InterpolatedStringInsertExpr).getInsert() and
+ scope = e2
+ or
e2 =
any(OperatorCall oc |
oc.getTarget().(ConversionOperator).fromLibrary() and
diff --git a/csharp/ql/lib/semmle/code/csharp/exprs/Expr.qll b/csharp/ql/lib/semmle/code/csharp/exprs/Expr.qll
index 85676bbd2701..ada010652588 100644
--- a/csharp/ql/lib/semmle/code/csharp/exprs/Expr.qll
+++ b/csharp/ql/lib/semmle/code/csharp/exprs/Expr.qll
@@ -34,8 +34,9 @@ private import semmle.code.csharp.TypeRef
* `as` expression (`AsExpr`), a cast (`CastExpr`), a `typeof` expression
* (`TypeofExpr`), a `default` expression (`DefaultValueExpr`), an `await`
* expression (`AwaitExpr`), a `nameof` expression (`NameOfExpr`), an
- * interpolated string (`InterpolatedStringExpr`), a qualifiable expression
- * (`QualifiableExpr`), or a literal (`Literal`).
+ * interpolated string (`InterpolatedStringExpr`), an interpolated string
+ * insert (`InterpolatedStringInsertExpr`), a qualifiable expression (`QualifiableExpr`),
+ * or a literal (`Literal`).
*/
class Expr extends ControlFlowElement, @expr {
override Location getALocation() { expr_location(this, result) }
@@ -917,6 +918,40 @@ class NameOfExpr extends Expr, @nameof_expr {
override string getAPrimaryQlClass() { result = "NameOfExpr" }
}
+/**
+ * An interpolated string insert, for example `{pi, align:F3}` on line 3 in
+ *
+ * ```csharp
+ * void Pi() {
+ * float pi = 3.14159f;
+ * Console.WriteLine($"Hello Pi, {pi,6:F3}");
+ * }
+ * ```
+ */
+class InterpolatedStringInsertExpr extends Expr, @interpolated_string_insert_expr {
+ override string toString() { result = "{...}" }
+
+ override string getAPrimaryQlClass() { result = "InterpolatedStringInsertExpr" }
+
+ /**
+ * Gets the insert expression in this interpolated string insert. For
+ * example, the insert expression in `{pi, align:F3}` is `pi`.
+ */
+ Expr getInsert() { result = this.getChild(0) }
+
+ /**
+ * Gets the alignment expression in this interpolated string insert, if any.
+ * For example, the alignment expression in `{pi, align:F3}` is `align`.
+ */
+ Expr getAlignment() { result = this.getChild(1) }
+
+ /**
+ * Gets the format expression in this interpolated string insert, if any.
+ * For example, the format expression in `{pi, align:F3}` is `F3`.
+ */
+ StringLiteral getFormat() { result = this.getChild(2) }
+}
+
/**
* An interpolated string, for example `$"Hello, {name}!"` on line 2 in
*
@@ -931,16 +966,20 @@ class InterpolatedStringExpr extends Expr, @interpolated_string_expr {
override string getAPrimaryQlClass() { result = "InterpolatedStringExpr" }
+ /**
+ * Gets the interpolated string insert at index `i` in this interpolated string, if any.
+ * For example, the interpolated string insert at index `i = 1` is `{pi, align:F3}`
+ * in `$"Hello, {pi, align:F3}!"`.
+ */
+ InterpolatedStringInsertExpr getInterpolatedInsert(int i) { result = this.getChild(i) }
+
/**
* Gets the insert at index `i` in this interpolated string, if any. For
* example, the insert at index `i = 1` is `name` in `$"Hello, {name}!"`.
* Note that there is no insert at index `i = 0`, but instead a text
* element (`getText(0)` gets the text).
*/
- Expr getInsert(int i) {
- result = this.getChild(i) and
- not result instanceof StringLiteral
- }
+ Expr getInsert(int i) { result = this.getInterpolatedInsert(i).getInsert() }
/**
* Gets the text element at index `i` in this interpolated string, if any.
diff --git a/csharp/ql/lib/semmlecode.csharp.dbscheme b/csharp/ql/lib/semmlecode.csharp.dbscheme
index a2bda57dbc6e..66044cfa5bbf 100644
--- a/csharp/ql/lib/semmlecode.csharp.dbscheme
+++ b/csharp/ql/lib/semmlecode.csharp.dbscheme
@@ -1168,6 +1168,7 @@ case @expr.kind of
/* C# 12.0 */
| 136 = @collection_expr
| 137 = @spread_element_expr
+| 138 = @interpolated_string_insert_expr
/* Preprocessor */
| 999 = @define_symbol_expr
;
diff --git a/csharp/ql/lib/upgrades/a2bda57dbc6eea94c50128522aae536e8edd5a3c/old.dbscheme b/csharp/ql/lib/upgrades/a2bda57dbc6eea94c50128522aae536e8edd5a3c/old.dbscheme
new file mode 100644
index 000000000000..a2bda57dbc6e
--- /dev/null
+++ b/csharp/ql/lib/upgrades/a2bda57dbc6eea94c50128522aae536e8edd5a3c/old.dbscheme
@@ -0,0 +1,1459 @@
+/* This is a dummy line to alter the dbscheme, so we can make a database upgrade
+ * without actually changing any of the dbscheme predicates. It contains a date
+ * to allow for such updates in the future as well.
+ *
+ * 2021-07-14
+ *
+ * DO NOT remove this comment carelessly, since it can revert the dbscheme back to a
+ * previously seen state (matching a previously seen SHA), which would make the upgrade
+ * mechanism not work properly.
+ */
+
+/**
+ * An invocation of the compiler. Note that more than one file may be
+ * compiled per invocation. For example, this command compiles three
+ * source files:
+ *
+ * csc f1.cs f2.cs f3.cs
+ *
+ * The `id` simply identifies the invocation, while `cwd` is the working
+ * directory from which the compiler was invoked.
+ */
+compilations(
+ unique int id : @compilation,
+ string cwd : string ref
+);
+
+compilation_info(
+ int id : @compilation ref,
+ string info_key: string ref,
+ string info_value: string ref
+)
+
+/**
+ * The arguments that were passed to the extractor for a compiler
+ * invocation. If `id` is for the compiler invocation
+ *
+ * csc f1.cs f2.cs f3.cs
+ *
+ * then typically there will be rows for
+ *
+ * num | arg
+ * --- | ---
+ * 0 | --compiler
+ * 1 | *path to compiler*
+ * 2 | f1.cs
+ * 3 | f2.cs
+ * 4 | f3.cs
+ */
+#keyset[id, num]
+compilation_args(
+ int id : @compilation ref,
+ int num : int ref,
+ string arg : string ref
+);
+
+/**
+ * The expanded arguments that were passed to the extractor for a
+ * compiler invocation. This is similar to `compilation_args`, but
+ * for a `@someFile.rsp` argument, it includes the arguments from that
+ * file, rather than just taking the argument literally.
+ */
+#keyset[id, num]
+compilation_expanded_args(
+ int id : @compilation ref,
+ int num : int ref,
+ string arg : string ref
+);
+
+/**
+ * The source files that are compiled by a compiler invocation.
+ * If `id` is for the compiler invocation
+ *
+ * csc f1.cs f2.cs f3.cs
+ *
+ * then there will be rows for
+ *
+ * num | arg
+ * --- | ---
+ * 0 | f1.cs
+ * 1 | f2.cs
+ * 2 | f3.cs
+ */
+#keyset[id, num]
+compilation_compiling_files(
+ int id : @compilation ref,
+ int num : int ref,
+ int file : @file ref
+);
+
+/**
+ * The references used by a compiler invocation.
+ * If `id` is for the compiler invocation
+ *
+ * csc f1.cs f2.cs f3.cs /r:ref1.dll /r:ref2.dll /r:ref3.dll
+ *
+ * then there will be rows for
+ *
+ * num | arg
+ * --- | ---
+ * 0 | ref1.dll
+ * 1 | ref2.dll
+ * 2 | ref3.dll
+ */
+#keyset[id, num]
+compilation_referencing_files(
+ int id : @compilation ref,
+ int num : int ref,
+ int file : @file ref
+);
+
+/**
+ * The time taken by the extractor for a compiler invocation.
+ *
+ * For each file `num`, there will be rows for
+ *
+ * kind | seconds
+ * ---- | ---
+ * 1 | CPU seconds used by the extractor frontend
+ * 2 | Elapsed seconds during the extractor frontend
+ * 3 | CPU seconds used by the extractor backend
+ * 4 | Elapsed seconds during the extractor backend
+ */
+#keyset[id, num, kind]
+compilation_time(
+ int id : @compilation ref,
+ int num : int ref,
+ /* kind:
+ 1 = frontend_cpu_seconds
+ 2 = frontend_elapsed_seconds
+ 3 = extractor_cpu_seconds
+ 4 = extractor_elapsed_seconds
+ */
+ int kind : int ref,
+ float seconds : float ref
+);
+
+/**
+ * An error or warning generated by the extractor.
+ * The diagnostic message `diagnostic` was generated during compiler
+ * invocation `compilation`, and is the `file_number_diagnostic_number`th
+ * message generated while extracting the `file_number`th file of that
+ * invocation.
+ */
+#keyset[compilation, file_number, file_number_diagnostic_number]
+diagnostic_for(
+ unique int diagnostic : @diagnostic ref,
+ int compilation : @compilation ref,
+ int file_number : int ref,
+ int file_number_diagnostic_number : int ref
+);
+
+diagnostics(
+ unique int id: @diagnostic,
+ int severity: int ref,
+ string error_tag: string ref,
+ string error_message: string ref,
+ string full_error_message: string ref,
+ int location: @location ref
+);
+
+extractor_messages(
+ unique int id: @extractor_message,
+ int severity: int ref,
+ string origin : string ref,
+ string text : string ref,
+ string entity : string ref,
+ int location: @location ref,
+ string stack_trace : string ref
+);
+
+/**
+ * If extraction was successful, then `cpu_seconds` and
+ * `elapsed_seconds` are the CPU time and elapsed time (respectively)
+ * that extraction took for compiler invocation `id`.
+ */
+compilation_finished(
+ unique int id : @compilation ref,
+ float cpu_seconds : float ref,
+ float elapsed_seconds : float ref
+);
+
+compilation_assembly(
+ unique int id : @compilation ref,
+ int assembly: @assembly ref
+)
+
+// Populated by the CSV extractor
+externalData(
+ int id: @externalDataElement,
+ string path: string ref,
+ int column: int ref,
+ string value: string ref);
+
+sourceLocationPrefix(
+ string prefix: string ref);
+
+/*
+ * C# dbscheme
+ */
+
+/** ELEMENTS **/
+
+@element = @declaration | @stmt | @expr | @modifier | @attribute | @namespace_declaration
+ | @using_directive | @type_parameter_constraints | @externalDataElement
+ | @xmllocatable | @asp_element | @namespace | @preprocessor_directive;
+
+@declaration = @callable | @generic | @assignable | @namespace;
+
+@named_element = @namespace | @declaration;
+
+@declaration_with_accessors = @property | @indexer | @event;
+
+@assignable = @variable | @assignable_with_accessors | @event;
+
+@assignable_with_accessors = @property | @indexer;
+
+@attributable = @assembly | @field | @parameter | @operator | @method | @constructor
+ | @destructor | @callable_accessor | @value_or_ref_type | @declaration_with_accessors
+ | @local_function | @lambda_expr;
+
+/** LOCATIONS, ASEMMBLIES, MODULES, FILES and FOLDERS **/
+
+@location = @location_default | @assembly;
+
+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);
+
+locations_mapped(
+ unique int id: @location_default ref,
+ int mapped_to: @location_default ref);
+
+@sourceline = @file | @callable | @xmllocatable;
+
+numlines(
+ int element_id: @sourceline ref,
+ int num_lines: int ref,
+ int num_code: int ref,
+ int num_comment: int ref);
+
+assemblies(
+ unique int id: @assembly,
+ int file: @file ref,
+ string fullname: string ref,
+ string name: string ref,
+ string version: string ref);
+
+files(
+ unique int id: @file,
+ string name: string ref);
+
+folders(
+ unique int id: @folder,
+ string name: string ref);
+
+@container = @folder | @file ;
+
+containerparent(
+ int parent: @container ref,
+ unique int child: @container ref);
+
+file_extraction_mode(
+ unique int file: @file ref,
+ int mode: int ref
+ /* 0 = normal, 1 = standalone extractor */
+ );
+
+/** NAMESPACES **/
+
+@type_container = @namespace | @type;
+
+namespaces(
+ unique int id: @namespace,
+ string name: string ref);
+
+namespace_declarations(
+ unique int id: @namespace_declaration,
+ int namespace_id: @namespace ref);
+
+namespace_declaration_location(
+ unique int id: @namespace_declaration ref,
+ int loc: @location ref);
+
+parent_namespace(
+ unique int child_id: @type_container ref,
+ int namespace_id: @namespace ref);
+
+@declaration_or_directive = @namespace_declaration | @type | @using_directive;
+
+parent_namespace_declaration(
+ int child_id: @declaration_or_directive ref, // cannot be unique because of partial classes
+ int namespace_id: @namespace_declaration ref);
+
+@using_directive = @using_namespace_directive | @using_static_directive;
+
+using_global(
+ unique int id: @using_directive ref
+);
+
+using_namespace_directives(
+ unique int id: @using_namespace_directive,
+ int namespace_id: @namespace ref);
+
+using_static_directives(
+ unique int id: @using_static_directive,
+ int type_id: @type_or_ref ref);
+
+using_directive_location(
+ unique int id: @using_directive ref,
+ int loc: @location ref);
+
+@preprocessor_directive = @pragma_warning | @pragma_checksum | @directive_define | @directive_undefine | @directive_warning
+ | @directive_error | @directive_nullable | @directive_line | @directive_region | @directive_endregion | @directive_if
+ | @directive_elif | @directive_else | @directive_endif;
+
+@conditional_directive = @directive_if | @directive_elif;
+@branch_directive = @directive_if | @directive_elif | @directive_else;
+
+directive_ifs(
+ unique int id: @directive_if,
+ int branchTaken: int ref, /* 0: false, 1: true */
+ int conditionValue: int ref); /* 0: false, 1: true */
+
+directive_elifs(
+ unique int id: @directive_elif,
+ int branchTaken: int ref, /* 0: false, 1: true */
+ int conditionValue: int ref, /* 0: false, 1: true */
+ int parent: @directive_if ref,
+ int index: int ref);
+
+directive_elses(
+ unique int id: @directive_else,
+ int branchTaken: int ref, /* 0: false, 1: true */
+ int parent: @directive_if ref,
+ int index: int ref);
+
+#keyset[id, start]
+directive_endifs(
+ unique int id: @directive_endif,
+ unique int start: @directive_if ref);
+
+directive_define_symbols(
+ unique int id: @define_symbol_expr ref,
+ string name: string ref);
+
+directive_regions(
+ unique int id: @directive_region,
+ string name: string ref);
+
+#keyset[id, start]
+directive_endregions(
+ unique int id: @directive_endregion,
+ unique int start: @directive_region ref);
+
+directive_lines(
+ unique int id: @directive_line,
+ int kind: int ref); /* 0: default, 1: hidden, 2: numeric, 3: span */
+
+directive_line_value(
+ unique int id: @directive_line ref,
+ int line: int ref);
+
+directive_line_file(
+ unique int id: @directive_line ref,
+ int file: @file ref);
+
+directive_line_offset(
+ unique int id: @directive_line ref,
+ int offset: int ref);
+
+directive_line_span(
+ unique int id: @directive_line ref,
+ int startLine: int ref,
+ int startColumn: int ref,
+ int endLine: int ref,
+ int endColumn: int ref);
+
+directive_nullables(
+ unique int id: @directive_nullable,
+ int setting: int ref, /* 0: disable, 1: enable, 2: restore */
+ int target: int ref); /* 0: none, 1: annotations, 2: warnings */
+
+directive_warnings(
+ unique int id: @directive_warning,
+ string message: string ref);
+
+directive_errors(
+ unique int id: @directive_error,
+ string message: string ref);
+
+directive_undefines(
+ unique int id: @directive_undefine,
+ string name: string ref);
+
+directive_defines(
+ unique int id: @directive_define,
+ string name: string ref);
+
+pragma_checksums(
+ unique int id: @pragma_checksum,
+ int file: @file ref,
+ string guid: string ref,
+ string bytes: string ref);
+
+pragma_warnings(
+ unique int id: @pragma_warning,
+ int kind: int ref /* 0 = disable, 1 = restore */);
+
+#keyset[id, index]
+pragma_warning_error_codes(
+ int id: @pragma_warning ref,
+ string errorCode: string ref,
+ int index: int ref);
+
+preprocessor_directive_location(
+ unique int id: @preprocessor_directive ref,
+ int loc: @location ref);
+
+preprocessor_directive_compilation(
+ int id: @preprocessor_directive ref,
+ int compilation: @compilation ref);
+
+preprocessor_directive_active(
+ unique int id: @preprocessor_directive ref,
+ int active: int ref); /* 0: false, 1: true */
+
+/** TYPES **/
+
+types(
+ unique int id: @type,
+ int kind: int ref,
+ string name: string ref);
+
+case @type.kind of
+ 1 = @bool_type
+| 2 = @char_type
+| 3 = @decimal_type
+| 4 = @sbyte_type
+| 5 = @short_type
+| 6 = @int_type
+| 7 = @long_type
+| 8 = @byte_type
+| 9 = @ushort_type
+| 10 = @uint_type
+| 11 = @ulong_type
+| 12 = @float_type
+| 13 = @double_type
+| 14 = @enum_type
+| 15 = @struct_type
+| 17 = @class_type
+| 19 = @interface_type
+| 20 = @delegate_type
+| 21 = @null_type
+| 22 = @type_parameter
+| 23 = @pointer_type
+| 24 = @nullable_type
+| 25 = @array_type
+| 26 = @void_type
+| 27 = @int_ptr_type
+| 28 = @uint_ptr_type
+| 29 = @dynamic_type
+| 30 = @arglist_type
+| 31 = @unknown_type
+| 32 = @tuple_type
+| 33 = @function_pointer_type
+| 34 = @inline_array_type
+ ;
+
+@simple_type = @bool_type | @char_type | @integral_type | @floating_point_type | @decimal_type;
+@integral_type = @signed_integral_type | @unsigned_integral_type;
+@signed_integral_type = @sbyte_type | @short_type | @int_type | @long_type;
+@unsigned_integral_type = @byte_type | @ushort_type | @uint_type | @ulong_type;
+@floating_point_type = @float_type | @double_type;
+@value_type = @simple_type | @enum_type | @struct_type | @nullable_type | @int_ptr_type
+ | @uint_ptr_type | @tuple_type | @void_type | @inline_array_type;
+@ref_type = @class_type | @interface_type | @array_type | @delegate_type | @null_type
+ | @dynamic_type;
+@value_or_ref_type = @value_type | @ref_type;
+
+typerefs(
+ unique int id: @typeref,
+ string name: string ref);
+
+typeref_type(
+ int id: @typeref ref,
+ unique int typeId: @type ref);
+
+@type_or_ref = @type | @typeref;
+
+array_element_type(
+ unique int array: @array_type ref,
+ int dimension: int ref,
+ int rank: int ref,
+ int element: @type_or_ref ref);
+
+nullable_underlying_type(
+ unique int nullable: @nullable_type ref,
+ int underlying: @type_or_ref ref);
+
+pointer_referent_type(
+ unique int pointer: @pointer_type ref,
+ int referent: @type_or_ref ref);
+
+enum_underlying_type(
+ unique int enum_id: @enum_type ref,
+ int underlying_type_id: @type_or_ref ref);
+
+delegate_return_type(
+ unique int delegate_id: @delegate_type ref,
+ int return_type_id: @type_or_ref ref);
+
+function_pointer_return_type(
+ unique int function_pointer_id: @function_pointer_type ref,
+ int return_type_id: @type_or_ref ref);
+
+extend(
+ int sub: @type ref,
+ int super: @type_or_ref ref);
+
+anonymous_types(
+ unique int id: @type ref);
+
+@interface_or_ref = @interface_type | @typeref;
+
+implement(
+ int sub: @type ref,
+ int super: @type_or_ref ref);
+
+type_location(
+ int id: @type ref,
+ int loc: @location ref);
+
+tuple_underlying_type(
+ unique int tuple: @tuple_type ref,
+ int struct: @type_or_ref ref);
+
+#keyset[tuple, index]
+tuple_element(
+ int tuple: @tuple_type ref,
+ int index: int ref,
+ unique int field: @field ref);
+
+attributes(
+ unique int id: @attribute,
+ int kind: int ref,
+ int type_id: @type_or_ref ref,
+ int target: @attributable ref);
+
+case @attribute.kind of
+ 0 = @attribute_default
+| 1 = @attribute_return
+| 2 = @attribute_assembly
+| 3 = @attribute_module
+;
+
+attribute_location(
+ int id: @attribute ref,
+ int loc: @location ref);
+
+@type_mention_parent = @element | @type_mention;
+
+type_mention(
+ unique int id: @type_mention,
+ int type_id: @type_or_ref ref,
+ int parent: @type_mention_parent ref);
+
+type_mention_location(
+ unique int id: @type_mention ref,
+ int loc: @location ref);
+
+@has_type_annotation = @assignable | @type_parameter | @callable | @expr | @delegate_type | @generic | @function_pointer_type;
+
+/**
+ * A direct annotation on an entity, for example `string? x;`.
+ *
+ * Annotations:
+ * 2 = reftype is not annotated "!"
+ * 3 = reftype is annotated "?"
+ * 4 = readonly ref type / in parameter
+ * 5 = ref type parameter, return or local variable
+ * 6 = out parameter
+ *
+ * Note that the annotation depends on the element it annotates.
+ * @assignable: The annotation is on the type of the assignable, for example the variable type.
+ * @type_parameter: The annotation is on the reftype constraint
+ * @callable: The annotation is on the return type
+ * @array_type: The annotation is on the element type
+ */
+type_annotation(int id: @has_type_annotation ref, int annotation: int ref);
+
+nullability(unique int nullability: @nullability, int kind: int ref);
+
+case @nullability.kind of
+ 0 = @oblivious
+| 1 = @not_annotated
+| 2 = @annotated
+;
+
+#keyset[parent, index]
+nullability_parent(int nullability: @nullability ref, int index: int ref, int parent: @nullability ref)
+
+type_nullability(int id: @has_type_annotation ref, int nullability: @nullability ref);
+
+/**
+ * The nullable flow state of an expression, as determined by Roslyn.
+ * 0 = none (default, not populated)
+ * 1 = not null
+ * 2 = maybe null
+ */
+expr_flowstate(unique int id: @expr ref, int state: int ref);
+
+/** GENERICS **/
+
+@generic = @type | @method | @local_function;
+
+type_parameters(
+ unique int id: @type_parameter ref,
+ int index: int ref,
+ int generic_id: @generic ref,
+ int variance: int ref /* none = 0, out = 1, in = 2 */);
+
+#keyset[constructed_id, index]
+type_arguments(
+ int id: @type_or_ref ref,
+ int index: int ref,
+ int constructed_id: @generic_or_ref ref);
+
+@generic_or_ref = @generic | @typeref;
+
+constructed_generic(
+ unique int constructed: @generic ref,
+ int generic: @generic_or_ref ref);
+
+type_parameter_constraints(
+ unique int id: @type_parameter_constraints,
+ int param_id: @type_parameter ref);
+
+type_parameter_constraints_location(
+ int id: @type_parameter_constraints ref,
+ int loc: @location ref);
+
+general_type_parameter_constraints(
+ int id: @type_parameter_constraints ref,
+ int kind: int ref /* class = 1, struct = 2, new = 3 */);
+
+specific_type_parameter_constraints(
+ int id: @type_parameter_constraints ref,
+ int base_id: @type_or_ref ref);
+
+specific_type_parameter_nullability(
+ int id: @type_parameter_constraints ref,
+ int base_id: @type_or_ref ref,
+ int nullability: @nullability ref);
+
+/** FUNCTION POINTERS */
+
+function_pointer_calling_conventions(
+ int id: @function_pointer_type ref,
+ int kind: int ref);
+
+#keyset[id, index]
+has_unmanaged_calling_conventions(
+ int id: @function_pointer_type ref,
+ int index: int ref,
+ int conv_id: @type_or_ref ref);
+
+/** MODIFIERS */
+
+@modifiable = @modifiable_direct | @event_accessor;
+
+@modifiable_direct = @member | @accessor | @local_function | @anonymous_function_expr;
+
+modifiers(
+ unique int id: @modifier,
+ string name: string ref);
+
+has_modifiers(
+ int id: @modifiable_direct ref,
+ int mod_id: @modifier ref);
+
+/** MEMBERS **/
+
+@member = @method | @constructor | @destructor | @field | @property | @event | @operator | @indexer | @type;
+
+@named_exprorstmt = @goto_stmt | @labeled_stmt | @expr;
+
+@virtualizable = @method | @property | @indexer | @event | @operator;
+
+exprorstmt_name(
+ unique int parent_id: @named_exprorstmt ref,
+ string name: string ref);
+
+nested_types(
+ unique int id: @type ref,
+ int declaring_type_id: @type ref,
+ int unbound_id: @type ref);
+
+properties(
+ unique int id: @property,
+ string name: string ref,
+ int declaring_type_id: @type ref,
+ int type_id: @type_or_ref ref,
+ int unbound_id: @property ref);
+
+property_location(
+ int id: @property ref,
+ int loc: @location ref);
+
+indexers(
+ unique int id: @indexer,
+ string name: string ref,
+ int declaring_type_id: @type ref,
+ int type_id: @type_or_ref ref,
+ int unbound_id: @indexer ref);
+
+indexer_location(
+ int id: @indexer ref,
+ int loc: @location ref);
+
+accessors(
+ unique int id: @accessor,
+ int kind: int ref,
+ string name: string ref,
+ int declaring_member_id: @member ref,
+ int unbound_id: @accessor ref);
+
+case @accessor.kind of
+ 1 = @getter
+| 2 = @setter
+ ;
+
+init_only_accessors(
+ unique int id: @accessor ref);
+
+accessor_location(
+ int id: @accessor ref,
+ int loc: @location ref);
+
+events(
+ unique int id: @event,
+ string name: string ref,
+ int declaring_type_id: @type ref,
+ int type_id: @type_or_ref ref,
+ int unbound_id: @event ref);
+
+event_location(
+ int id: @event ref,
+ int loc: @location ref);
+
+event_accessors(
+ unique int id: @event_accessor,
+ int kind: int ref,
+ string name: string ref,
+ int declaring_event_id: @event ref,
+ int unbound_id: @event_accessor ref);
+
+case @event_accessor.kind of
+ 1 = @add_event_accessor
+| 2 = @remove_event_accessor
+ ;
+
+event_accessor_location(
+ int id: @event_accessor ref,
+ int loc: @location ref);
+
+operators(
+ unique int id: @operator,
+ string name: string ref,
+ string symbol: string ref,
+ int declaring_type_id: @type ref,
+ int type_id: @type_or_ref ref,
+ int unbound_id: @operator ref);
+
+operator_location(
+ int id: @operator ref,
+ int loc: @location ref);
+
+constant_value(
+ int id: @variable ref,
+ string value: string ref);
+
+/** CALLABLES **/
+
+@callable = @method | @constructor | @destructor | @operator | @callable_accessor | @anonymous_function_expr | @local_function;
+
+@callable_accessor = @accessor | @event_accessor;
+
+methods(
+ unique int id: @method,
+ string name: string ref,
+ int declaring_type_id: @type ref,
+ int type_id: @type_or_ref ref,
+ int unbound_id: @method ref);
+
+method_location(
+ int id: @method ref,
+ int loc: @location ref);
+
+constructors(
+ unique int id: @constructor,
+ string name: string ref,
+ int declaring_type_id: @type ref,
+ int unbound_id: @constructor ref);
+
+constructor_location(
+ int id: @constructor ref,
+ int loc: @location ref);
+
+destructors(
+ unique int id: @destructor,
+ string name: string ref,
+ int declaring_type_id: @type ref,
+ int unbound_id: @destructor ref);
+
+destructor_location(
+ int id: @destructor ref,
+ int loc: @location ref);
+
+overrides(
+ int id: @callable ref,
+ int base_id: @callable ref);
+
+explicitly_implements(
+ int id: @member ref,
+ int interface_id: @interface_or_ref ref);
+
+local_functions(
+ unique int id: @local_function,
+ string name: string ref,
+ int return_type: @type ref,
+ int unbound_id: @local_function ref);
+
+local_function_stmts(
+ unique int fn: @local_function_stmt ref,
+ int stmt: @local_function ref);
+
+/** VARIABLES **/
+
+@variable = @local_scope_variable | @field;
+
+@local_scope_variable = @local_variable | @parameter;
+
+fields(
+ unique int id: @field,
+ int kind: int ref,
+ string name: string ref,
+ int declaring_type_id: @type ref,
+ int type_id: @type_or_ref ref,
+ int unbound_id: @field ref);
+
+case @field.kind of
+ 1 = @addressable_field
+| 2 = @constant
+ ;
+
+field_location(
+ int id: @field ref,
+ int loc: @location ref);
+
+localvars(
+ unique int id: @local_variable,
+ int kind: int ref,
+ string name: string ref,
+ int implicitly_typed: int ref /* 0 = no, 1 = yes */,
+ int type_id: @type_or_ref ref,
+ int parent_id: @local_var_decl_expr ref);
+
+case @local_variable.kind of
+ 1 = @addressable_local_variable
+| 2 = @local_constant
+| 3 = @local_variable_ref
+ ;
+
+localvar_location(
+ unique int id: @local_variable ref,
+ int loc: @location ref);
+
+@parameterizable = @callable | @delegate_type | @indexer | @function_pointer_type;
+
+#keyset[name, parent_id]
+#keyset[index, parent_id]
+params(
+ unique int id: @parameter,
+ string name: string ref,
+ int type_id: @type_or_ref ref,
+ int index: int ref,
+ int mode: int ref, /* value = 0, ref = 1, out = 2, params/array = 3, this = 4, in = 5, ref readonly = 6 */
+ int parent_id: @parameterizable ref,
+ int unbound_id: @parameter ref);
+
+param_location(
+ int id: @parameter ref,
+ int loc: @location ref);
+
+@has_scoped_annotation = @local_scope_variable
+
+scoped_annotation(
+ int id: @has_scoped_annotation ref,
+ int kind: int ref // scoped ref = 1, scoped value = 2
+ );
+
+/** STATEMENTS **/
+
+@exprorstmt_parent = @control_flow_element | @top_level_exprorstmt_parent;
+
+statements(
+ unique int id: @stmt,
+ int kind: int ref);
+
+#keyset[index, parent]
+stmt_parent(
+ unique int stmt: @stmt ref,
+ int index: int ref,
+ int parent: @control_flow_element ref);
+
+@top_level_stmt_parent = @callable;
+
+// [index, parent] is not a keyset because the same parent may be compiled multiple times
+stmt_parent_top_level(
+ unique int stmt: @stmt ref,
+ int index: int ref,
+ int parent: @top_level_stmt_parent ref);
+
+case @stmt.kind of
+ 1 = @block_stmt
+| 2 = @expr_stmt
+| 3 = @if_stmt
+| 4 = @switch_stmt
+| 5 = @while_stmt
+| 6 = @do_stmt
+| 7 = @for_stmt
+| 8 = @foreach_stmt
+| 9 = @break_stmt
+| 10 = @continue_stmt
+| 11 = @goto_stmt
+| 12 = @goto_case_stmt
+| 13 = @goto_default_stmt
+| 14 = @throw_stmt
+| 15 = @return_stmt
+| 16 = @yield_stmt
+| 17 = @try_stmt
+| 18 = @checked_stmt
+| 19 = @unchecked_stmt
+| 20 = @lock_stmt
+| 21 = @using_block_stmt
+| 22 = @var_decl_stmt
+| 23 = @const_decl_stmt
+| 24 = @empty_stmt
+| 25 = @unsafe_stmt
+| 26 = @fixed_stmt
+| 27 = @label_stmt
+| 28 = @catch
+| 29 = @case_stmt
+| 30 = @local_function_stmt
+| 31 = @using_decl_stmt
+ ;
+
+@using_stmt = @using_block_stmt | @using_decl_stmt;
+
+@labeled_stmt = @label_stmt | @case;
+
+@decl_stmt = @var_decl_stmt | @const_decl_stmt | @using_decl_stmt;
+
+@cond_stmt = @if_stmt | @switch_stmt;
+
+@loop_stmt = @while_stmt | @do_stmt | @for_stmt | @foreach_stmt;
+
+@jump_stmt = @break_stmt | @goto_any_stmt | @continue_stmt | @throw_stmt | @return_stmt
+ | @yield_stmt;
+
+@goto_any_stmt = @goto_default_stmt | @goto_case_stmt | @goto_stmt;
+
+
+stmt_location(
+ unique int id: @stmt ref,
+ int loc: @location ref);
+
+catch_type(
+ unique int catch_id: @catch ref,
+ int type_id: @type_or_ref ref,
+ int kind: int ref /* explicit = 1, implicit = 2 */);
+
+foreach_stmt_info(
+ unique int id: @foreach_stmt ref,
+ int kind: int ref /* non-async = 1, async = 2 */);
+
+@foreach_symbol = @method | @property | @type_or_ref;
+
+#keyset[id, kind]
+foreach_stmt_desugar(
+ int id: @foreach_stmt ref,
+ int symbol: @foreach_symbol ref,
+ int kind: int ref /* GetEnumeratorMethod = 1, CurrentProperty = 2, MoveNextMethod = 3, DisposeMethod = 4, ElementType = 5 */);
+
+/** EXPRESSIONS **/
+
+expressions(
+ unique int id: @expr,
+ int kind: int ref,
+ int type_id: @type_or_ref ref);
+
+#keyset[index, parent]
+expr_parent(
+ unique int expr: @expr ref,
+ int index: int ref,
+ int parent: @control_flow_element ref);
+
+@top_level_expr_parent = @attribute | @field | @property | @indexer | @parameter | @directive_if | @directive_elif;
+
+@top_level_exprorstmt_parent = @top_level_expr_parent | @top_level_stmt_parent;
+
+// [index, parent] is not a keyset because the same parent may be compiled multiple times
+expr_parent_top_level(
+ unique int expr: @expr ref,
+ int index: int ref,
+ int parent: @top_level_exprorstmt_parent ref);
+
+case @expr.kind of
+/* literal */
+ 1 = @bool_literal_expr
+| 2 = @char_literal_expr
+| 3 = @decimal_literal_expr
+| 4 = @int_literal_expr
+| 5 = @long_literal_expr
+| 6 = @uint_literal_expr
+| 7 = @ulong_literal_expr
+| 8 = @float_literal_expr
+| 9 = @double_literal_expr
+| 10 = @utf16_string_literal_expr
+| 11 = @null_literal_expr
+/* primary & unary */
+| 12 = @this_access_expr
+| 13 = @base_access_expr
+| 14 = @local_variable_access_expr
+| 15 = @parameter_access_expr
+| 16 = @field_access_expr
+| 17 = @property_access_expr
+| 18 = @method_access_expr
+| 19 = @event_access_expr
+| 20 = @indexer_access_expr
+| 21 = @array_access_expr
+| 22 = @type_access_expr
+| 23 = @typeof_expr
+| 24 = @method_invocation_expr
+| 25 = @delegate_invocation_expr
+| 26 = @operator_invocation_expr
+| 27 = @cast_expr
+| 28 = @object_creation_expr
+| 29 = @explicit_delegate_creation_expr
+| 30 = @implicit_delegate_creation_expr
+| 31 = @array_creation_expr
+| 32 = @default_expr
+| 33 = @plus_expr
+| 34 = @minus_expr
+| 35 = @bit_not_expr
+| 36 = @log_not_expr
+| 37 = @post_incr_expr
+| 38 = @post_decr_expr
+| 39 = @pre_incr_expr
+| 40 = @pre_decr_expr
+/* multiplicative */
+| 41 = @mul_expr
+| 42 = @div_expr
+| 43 = @rem_expr
+/* additive */
+| 44 = @add_expr
+| 45 = @sub_expr
+/* shift */
+| 46 = @lshift_expr
+| 47 = @rshift_expr
+/* relational */
+| 48 = @lt_expr
+| 49 = @gt_expr
+| 50 = @le_expr
+| 51 = @ge_expr
+/* equality */
+| 52 = @eq_expr
+| 53 = @ne_expr
+/* logical */
+| 54 = @bit_and_expr
+| 55 = @bit_xor_expr
+| 56 = @bit_or_expr
+| 57 = @log_and_expr
+| 58 = @log_or_expr
+/* type testing */
+| 59 = @is_expr
+| 60 = @as_expr
+/* null coalescing */
+| 61 = @null_coalescing_expr
+/* conditional */
+| 62 = @conditional_expr
+/* assignment */
+| 63 = @simple_assign_expr
+| 64 = @assign_add_expr
+| 65 = @assign_sub_expr
+| 66 = @assign_mul_expr
+| 67 = @assign_div_expr
+| 68 = @assign_rem_expr
+| 69 = @assign_and_expr
+| 70 = @assign_xor_expr
+| 71 = @assign_or_expr
+| 72 = @assign_lshift_expr
+| 73 = @assign_rshift_expr
+/* more */
+| 74 = @object_init_expr
+| 75 = @collection_init_expr
+| 76 = @array_init_expr
+| 77 = @checked_expr
+| 78 = @unchecked_expr
+| 79 = @constructor_init_expr
+| 80 = @add_event_expr
+| 81 = @remove_event_expr
+| 82 = @par_expr
+| 83 = @local_var_decl_expr
+| 84 = @lambda_expr
+| 85 = @anonymous_method_expr
+| 86 = @namespace_expr
+/* dynamic */
+| 92 = @dynamic_element_access_expr
+| 93 = @dynamic_member_access_expr
+/* unsafe */
+| 100 = @pointer_indirection_expr
+| 101 = @address_of_expr
+| 102 = @sizeof_expr
+/* async */
+| 103 = @await_expr
+/* C# 6.0 */
+| 104 = @nameof_expr
+| 105 = @interpolated_string_expr
+| 106 = @unknown_expr
+/* C# 7.0 */
+| 107 = @throw_expr
+| 108 = @tuple_expr
+| 109 = @local_function_invocation_expr
+| 110 = @ref_expr
+| 111 = @discard_expr
+/* C# 8.0 */
+| 112 = @range_expr
+| 113 = @index_expr
+| 114 = @switch_expr
+| 115 = @recursive_pattern_expr
+| 116 = @property_pattern_expr
+| 117 = @positional_pattern_expr
+| 118 = @switch_case_expr
+| 119 = @assign_coalesce_expr
+| 120 = @suppress_nullable_warning_expr
+| 121 = @namespace_access_expr
+/* C# 9.0 */
+| 122 = @lt_pattern_expr
+| 123 = @gt_pattern_expr
+| 124 = @le_pattern_expr
+| 125 = @ge_pattern_expr
+| 126 = @not_pattern_expr
+| 127 = @and_pattern_expr
+| 128 = @or_pattern_expr
+| 129 = @function_pointer_invocation_expr
+| 130 = @with_expr
+/* C# 11.0 */
+| 131 = @list_pattern_expr
+| 132 = @slice_pattern_expr
+| 133 = @urshift_expr
+| 134 = @assign_urshift_expr
+| 135 = @utf8_string_literal_expr
+/* C# 12.0 */
+| 136 = @collection_expr
+| 137 = @spread_element_expr
+/* Preprocessor */
+| 999 = @define_symbol_expr
+;
+
+@switch = @switch_stmt | @switch_expr;
+@case = @case_stmt | @switch_case_expr;
+@pattern_match = @case | @is_expr;
+@unary_pattern_expr = @not_pattern_expr;
+@relational_pattern_expr = @gt_pattern_expr | @lt_pattern_expr | @ge_pattern_expr | @le_pattern_expr;
+@binary_pattern_expr = @and_pattern_expr | @or_pattern_expr;
+
+@integer_literal_expr = @int_literal_expr | @long_literal_expr | @uint_literal_expr | @ulong_literal_expr;
+@real_literal_expr = @float_literal_expr | @double_literal_expr | @decimal_literal_expr;
+@string_literal_expr = @utf16_string_literal_expr | @utf8_string_literal_expr;
+@literal_expr = @bool_literal_expr | @char_literal_expr | @integer_literal_expr | @real_literal_expr
+ | @string_literal_expr | @null_literal_expr;
+
+@assign_expr = @simple_assign_expr | @assign_op_expr | @local_var_decl_expr;
+@assign_op_expr = @assign_arith_expr | @assign_bitwise_expr | @assign_event_expr | @assign_coalesce_expr;
+@assign_event_expr = @add_event_expr | @remove_event_expr;
+
+@assign_arith_expr = @assign_add_expr | @assign_sub_expr | @assign_mul_expr | @assign_div_expr
+ | @assign_rem_expr
+@assign_bitwise_expr = @assign_and_expr | @assign_or_expr | @assign_xor_expr
+ | @assign_lshift_expr | @assign_rshift_expr | @assign_urshift_expr;
+
+@member_access_expr = @field_access_expr | @property_access_expr | @indexer_access_expr | @event_access_expr
+ | @method_access_expr | @type_access_expr | @dynamic_member_access_expr;
+@access_expr = @member_access_expr | @this_access_expr | @base_access_expr | @assignable_access_expr | @namespace_access_expr;
+@element_access_expr = @indexer_access_expr | @array_access_expr | @dynamic_element_access_expr;
+
+@local_variable_access = @local_variable_access_expr | @local_var_decl_expr;
+@local_scope_variable_access_expr = @parameter_access_expr | @local_variable_access;
+@variable_access_expr = @local_scope_variable_access_expr | @field_access_expr;
+
+@assignable_access_expr = @variable_access_expr | @property_access_expr | @element_access_expr
+ | @event_access_expr | @dynamic_member_access_expr;
+
+@objectorcollection_init_expr = @object_init_expr | @collection_init_expr;
+
+@delegate_creation_expr = @explicit_delegate_creation_expr | @implicit_delegate_creation_expr;
+
+@bin_arith_op_expr = @mul_expr | @div_expr | @rem_expr | @add_expr | @sub_expr;
+@incr_op_expr = @pre_incr_expr | @post_incr_expr;
+@decr_op_expr = @pre_decr_expr | @post_decr_expr;
+@mut_op_expr = @incr_op_expr | @decr_op_expr;
+@un_arith_op_expr = @plus_expr | @minus_expr | @mut_op_expr;
+@arith_op_expr = @bin_arith_op_expr | @un_arith_op_expr;
+
+@ternary_log_op_expr = @conditional_expr;
+@bin_log_op_expr = @log_and_expr | @log_or_expr | @null_coalescing_expr;
+@un_log_op_expr = @log_not_expr;
+@log_expr = @un_log_op_expr | @bin_log_op_expr | @ternary_log_op_expr;
+
+@bin_bit_op_expr = @bit_and_expr | @bit_or_expr | @bit_xor_expr | @lshift_expr
+ | @rshift_expr | @urshift_expr;
+@un_bit_op_expr = @bit_not_expr;
+@bit_expr = @un_bit_op_expr | @bin_bit_op_expr;
+
+@equality_op_expr = @eq_expr | @ne_expr;
+@rel_op_expr = @gt_expr | @lt_expr| @ge_expr | @le_expr;
+@comp_expr = @equality_op_expr | @rel_op_expr;
+
+@op_expr = @assign_expr | @un_op | @bin_op | @ternary_op;
+
+@ternary_op = @ternary_log_op_expr;
+@bin_op = @bin_arith_op_expr | @bin_log_op_expr | @bin_bit_op_expr | @comp_expr;
+@un_op = @un_arith_op_expr | @un_log_op_expr | @un_bit_op_expr | @sizeof_expr
+ | @pointer_indirection_expr | @address_of_expr;
+
+@anonymous_function_expr = @lambda_expr | @anonymous_method_expr;
+
+@call = @method_invocation_expr | @constructor_init_expr | @operator_invocation_expr
+ | @delegate_invocation_expr | @object_creation_expr | @call_access_expr
+ | @local_function_invocation_expr | @function_pointer_invocation_expr;
+
+@call_access_expr = @property_access_expr | @event_access_expr | @indexer_access_expr;
+
+@late_bindable_expr = @dynamic_element_access_expr | @dynamic_member_access_expr
+ | @object_creation_expr | @method_invocation_expr | @operator_invocation_expr;
+
+@throw_element = @throw_expr | @throw_stmt;
+
+@implicitly_typeable_object_creation_expr = @object_creation_expr | @explicit_delegate_creation_expr;
+
+implicitly_typed_array_creation(
+ unique int id: @array_creation_expr ref);
+
+explicitly_sized_array_creation(
+ unique int id: @array_creation_expr ref);
+
+stackalloc_array_creation(
+ unique int id: @array_creation_expr ref);
+
+implicitly_typed_object_creation(
+ unique int id: @implicitly_typeable_object_creation_expr ref);
+
+mutator_invocation_mode(
+ unique int id: @operator_invocation_expr ref,
+ int mode: int ref /* prefix = 1, postfix = 2*/);
+
+expr_value(
+ unique int id: @expr ref,
+ string value: string ref);
+
+expr_call(
+ unique int caller_id: @expr ref,
+ int target_id: @callable ref);
+
+expr_access(
+ unique int accesser_id: @access_expr ref,
+ int target_id: @accessible ref);
+
+@accessible = @method | @assignable | @local_function | @namespace;
+
+expr_location(
+ unique int id: @expr ref,
+ int loc: @location ref);
+
+dynamic_member_name(
+ unique int id: @late_bindable_expr ref,
+ string name: string ref);
+
+@qualifiable_expr = @member_access_expr
+ | @method_invocation_expr
+ | @element_access_expr;
+
+conditional_access(
+ unique int id: @qualifiable_expr ref);
+
+expr_argument(
+ unique int id: @expr ref,
+ int mode: int ref);
+ /* mode is the same as params: value = 0, ref = 1, out = 2 */
+
+expr_argument_name(
+ unique int id: @expr ref,
+ string name: string ref);
+
+lambda_expr_return_type(
+ unique int id: @lambda_expr ref,
+ int type_id: @type_or_ref ref);
+
+/* Compiler generated */
+
+compiler_generated(unique int id: @element ref);
+
+/** CONTROL/DATA FLOW **/
+
+@control_flow_element = @stmt | @expr;
+
+/* 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;
+
+/* Comments */
+
+commentline(
+ unique int id: @commentline,
+ int kind: int ref,
+ string text: string ref,
+ string rawtext: string ref);
+
+case @commentline.kind of
+ 0 = @singlelinecomment
+| 1 = @xmldoccomment
+| 2 = @multilinecomment;
+
+commentline_location(
+ unique int id: @commentline ref,
+ int loc: @location ref);
+
+commentblock(
+ unique int id : @commentblock);
+
+commentblock_location(
+ unique int id: @commentblock ref,
+ int loc: @location ref);
+
+commentblock_binding(
+ int id: @commentblock ref,
+ int entity: @element ref,
+ int bindtype: int ref); /* 0: Parent, 1: Best, 2: Before, 3: After */
+
+commentblock_child(
+ int id: @commentblock ref,
+ int commentline: @commentline ref,
+ int index: int ref);
+
+/* ASP.NET */
+
+case @asp_element.kind of
+ 0=@asp_close_tag
+| 1=@asp_code
+| 2=@asp_comment
+| 3=@asp_data_binding
+| 4=@asp_directive
+| 5=@asp_open_tag
+| 6=@asp_quoted_string
+| 7=@asp_text
+| 8=@asp_xml_directive;
+
+@asp_attribute = @asp_code | @asp_data_binding | @asp_quoted_string;
+
+asp_elements(
+ unique int id: @asp_element,
+ int kind: int ref,
+ int loc: @location ref);
+
+asp_comment_server(unique int comment: @asp_comment ref);
+asp_code_inline(unique int code: @asp_code ref);
+asp_directive_attribute(
+ int directive: @asp_directive ref,
+ int index: int ref,
+ string name: string ref,
+ int value: @asp_quoted_string ref);
+asp_directive_name(
+ unique int directive: @asp_directive ref,
+ string name: string ref);
+asp_element_body(
+ unique int element: @asp_element ref,
+ string body: string ref);
+asp_tag_attribute(
+ int tag: @asp_open_tag ref,
+ int index: int ref,
+ string name: string ref,
+ int attribute: @asp_attribute ref);
+asp_tag_name(
+ unique int tag: @asp_open_tag ref,
+ string name: string ref);
+asp_tag_isempty(int tag: @asp_open_tag ref);
diff --git a/csharp/ql/lib/upgrades/a2bda57dbc6eea94c50128522aae536e8edd5a3c/semmlecode.csharp.dbscheme b/csharp/ql/lib/upgrades/a2bda57dbc6eea94c50128522aae536e8edd5a3c/semmlecode.csharp.dbscheme
new file mode 100644
index 000000000000..66044cfa5bbf
--- /dev/null
+++ b/csharp/ql/lib/upgrades/a2bda57dbc6eea94c50128522aae536e8edd5a3c/semmlecode.csharp.dbscheme
@@ -0,0 +1,1460 @@
+/* This is a dummy line to alter the dbscheme, so we can make a database upgrade
+ * without actually changing any of the dbscheme predicates. It contains a date
+ * to allow for such updates in the future as well.
+ *
+ * 2021-07-14
+ *
+ * DO NOT remove this comment carelessly, since it can revert the dbscheme back to a
+ * previously seen state (matching a previously seen SHA), which would make the upgrade
+ * mechanism not work properly.
+ */
+
+/**
+ * An invocation of the compiler. Note that more than one file may be
+ * compiled per invocation. For example, this command compiles three
+ * source files:
+ *
+ * csc f1.cs f2.cs f3.cs
+ *
+ * The `id` simply identifies the invocation, while `cwd` is the working
+ * directory from which the compiler was invoked.
+ */
+compilations(
+ unique int id : @compilation,
+ string cwd : string ref
+);
+
+compilation_info(
+ int id : @compilation ref,
+ string info_key: string ref,
+ string info_value: string ref
+)
+
+/**
+ * The arguments that were passed to the extractor for a compiler
+ * invocation. If `id` is for the compiler invocation
+ *
+ * csc f1.cs f2.cs f3.cs
+ *
+ * then typically there will be rows for
+ *
+ * num | arg
+ * --- | ---
+ * 0 | --compiler
+ * 1 | *path to compiler*
+ * 2 | f1.cs
+ * 3 | f2.cs
+ * 4 | f3.cs
+ */
+#keyset[id, num]
+compilation_args(
+ int id : @compilation ref,
+ int num : int ref,
+ string arg : string ref
+);
+
+/**
+ * The expanded arguments that were passed to the extractor for a
+ * compiler invocation. This is similar to `compilation_args`, but
+ * for a `@someFile.rsp` argument, it includes the arguments from that
+ * file, rather than just taking the argument literally.
+ */
+#keyset[id, num]
+compilation_expanded_args(
+ int id : @compilation ref,
+ int num : int ref,
+ string arg : string ref
+);
+
+/**
+ * The source files that are compiled by a compiler invocation.
+ * If `id` is for the compiler invocation
+ *
+ * csc f1.cs f2.cs f3.cs
+ *
+ * then there will be rows for
+ *
+ * num | arg
+ * --- | ---
+ * 0 | f1.cs
+ * 1 | f2.cs
+ * 2 | f3.cs
+ */
+#keyset[id, num]
+compilation_compiling_files(
+ int id : @compilation ref,
+ int num : int ref,
+ int file : @file ref
+);
+
+/**
+ * The references used by a compiler invocation.
+ * If `id` is for the compiler invocation
+ *
+ * csc f1.cs f2.cs f3.cs /r:ref1.dll /r:ref2.dll /r:ref3.dll
+ *
+ * then there will be rows for
+ *
+ * num | arg
+ * --- | ---
+ * 0 | ref1.dll
+ * 1 | ref2.dll
+ * 2 | ref3.dll
+ */
+#keyset[id, num]
+compilation_referencing_files(
+ int id : @compilation ref,
+ int num : int ref,
+ int file : @file ref
+);
+
+/**
+ * The time taken by the extractor for a compiler invocation.
+ *
+ * For each file `num`, there will be rows for
+ *
+ * kind | seconds
+ * ---- | ---
+ * 1 | CPU seconds used by the extractor frontend
+ * 2 | Elapsed seconds during the extractor frontend
+ * 3 | CPU seconds used by the extractor backend
+ * 4 | Elapsed seconds during the extractor backend
+ */
+#keyset[id, num, kind]
+compilation_time(
+ int id : @compilation ref,
+ int num : int ref,
+ /* kind:
+ 1 = frontend_cpu_seconds
+ 2 = frontend_elapsed_seconds
+ 3 = extractor_cpu_seconds
+ 4 = extractor_elapsed_seconds
+ */
+ int kind : int ref,
+ float seconds : float ref
+);
+
+/**
+ * An error or warning generated by the extractor.
+ * The diagnostic message `diagnostic` was generated during compiler
+ * invocation `compilation`, and is the `file_number_diagnostic_number`th
+ * message generated while extracting the `file_number`th file of that
+ * invocation.
+ */
+#keyset[compilation, file_number, file_number_diagnostic_number]
+diagnostic_for(
+ unique int diagnostic : @diagnostic ref,
+ int compilation : @compilation ref,
+ int file_number : int ref,
+ int file_number_diagnostic_number : int ref
+);
+
+diagnostics(
+ unique int id: @diagnostic,
+ int severity: int ref,
+ string error_tag: string ref,
+ string error_message: string ref,
+ string full_error_message: string ref,
+ int location: @location ref
+);
+
+extractor_messages(
+ unique int id: @extractor_message,
+ int severity: int ref,
+ string origin : string ref,
+ string text : string ref,
+ string entity : string ref,
+ int location: @location ref,
+ string stack_trace : string ref
+);
+
+/**
+ * If extraction was successful, then `cpu_seconds` and
+ * `elapsed_seconds` are the CPU time and elapsed time (respectively)
+ * that extraction took for compiler invocation `id`.
+ */
+compilation_finished(
+ unique int id : @compilation ref,
+ float cpu_seconds : float ref,
+ float elapsed_seconds : float ref
+);
+
+compilation_assembly(
+ unique int id : @compilation ref,
+ int assembly: @assembly ref
+)
+
+// Populated by the CSV extractor
+externalData(
+ int id: @externalDataElement,
+ string path: string ref,
+ int column: int ref,
+ string value: string ref);
+
+sourceLocationPrefix(
+ string prefix: string ref);
+
+/*
+ * C# dbscheme
+ */
+
+/** ELEMENTS **/
+
+@element = @declaration | @stmt | @expr | @modifier | @attribute | @namespace_declaration
+ | @using_directive | @type_parameter_constraints | @externalDataElement
+ | @xmllocatable | @asp_element | @namespace | @preprocessor_directive;
+
+@declaration = @callable | @generic | @assignable | @namespace;
+
+@named_element = @namespace | @declaration;
+
+@declaration_with_accessors = @property | @indexer | @event;
+
+@assignable = @variable | @assignable_with_accessors | @event;
+
+@assignable_with_accessors = @property | @indexer;
+
+@attributable = @assembly | @field | @parameter | @operator | @method | @constructor
+ | @destructor | @callable_accessor | @value_or_ref_type | @declaration_with_accessors
+ | @local_function | @lambda_expr;
+
+/** LOCATIONS, ASEMMBLIES, MODULES, FILES and FOLDERS **/
+
+@location = @location_default | @assembly;
+
+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);
+
+locations_mapped(
+ unique int id: @location_default ref,
+ int mapped_to: @location_default ref);
+
+@sourceline = @file | @callable | @xmllocatable;
+
+numlines(
+ int element_id: @sourceline ref,
+ int num_lines: int ref,
+ int num_code: int ref,
+ int num_comment: int ref);
+
+assemblies(
+ unique int id: @assembly,
+ int file: @file ref,
+ string fullname: string ref,
+ string name: string ref,
+ string version: string ref);
+
+files(
+ unique int id: @file,
+ string name: string ref);
+
+folders(
+ unique int id: @folder,
+ string name: string ref);
+
+@container = @folder | @file ;
+
+containerparent(
+ int parent: @container ref,
+ unique int child: @container ref);
+
+file_extraction_mode(
+ unique int file: @file ref,
+ int mode: int ref
+ /* 0 = normal, 1 = standalone extractor */
+ );
+
+/** NAMESPACES **/
+
+@type_container = @namespace | @type;
+
+namespaces(
+ unique int id: @namespace,
+ string name: string ref);
+
+namespace_declarations(
+ unique int id: @namespace_declaration,
+ int namespace_id: @namespace ref);
+
+namespace_declaration_location(
+ unique int id: @namespace_declaration ref,
+ int loc: @location ref);
+
+parent_namespace(
+ unique int child_id: @type_container ref,
+ int namespace_id: @namespace ref);
+
+@declaration_or_directive = @namespace_declaration | @type | @using_directive;
+
+parent_namespace_declaration(
+ int child_id: @declaration_or_directive ref, // cannot be unique because of partial classes
+ int namespace_id: @namespace_declaration ref);
+
+@using_directive = @using_namespace_directive | @using_static_directive;
+
+using_global(
+ unique int id: @using_directive ref
+);
+
+using_namespace_directives(
+ unique int id: @using_namespace_directive,
+ int namespace_id: @namespace ref);
+
+using_static_directives(
+ unique int id: @using_static_directive,
+ int type_id: @type_or_ref ref);
+
+using_directive_location(
+ unique int id: @using_directive ref,
+ int loc: @location ref);
+
+@preprocessor_directive = @pragma_warning | @pragma_checksum | @directive_define | @directive_undefine | @directive_warning
+ | @directive_error | @directive_nullable | @directive_line | @directive_region | @directive_endregion | @directive_if
+ | @directive_elif | @directive_else | @directive_endif;
+
+@conditional_directive = @directive_if | @directive_elif;
+@branch_directive = @directive_if | @directive_elif | @directive_else;
+
+directive_ifs(
+ unique int id: @directive_if,
+ int branchTaken: int ref, /* 0: false, 1: true */
+ int conditionValue: int ref); /* 0: false, 1: true */
+
+directive_elifs(
+ unique int id: @directive_elif,
+ int branchTaken: int ref, /* 0: false, 1: true */
+ int conditionValue: int ref, /* 0: false, 1: true */
+ int parent: @directive_if ref,
+ int index: int ref);
+
+directive_elses(
+ unique int id: @directive_else,
+ int branchTaken: int ref, /* 0: false, 1: true */
+ int parent: @directive_if ref,
+ int index: int ref);
+
+#keyset[id, start]
+directive_endifs(
+ unique int id: @directive_endif,
+ unique int start: @directive_if ref);
+
+directive_define_symbols(
+ unique int id: @define_symbol_expr ref,
+ string name: string ref);
+
+directive_regions(
+ unique int id: @directive_region,
+ string name: string ref);
+
+#keyset[id, start]
+directive_endregions(
+ unique int id: @directive_endregion,
+ unique int start: @directive_region ref);
+
+directive_lines(
+ unique int id: @directive_line,
+ int kind: int ref); /* 0: default, 1: hidden, 2: numeric, 3: span */
+
+directive_line_value(
+ unique int id: @directive_line ref,
+ int line: int ref);
+
+directive_line_file(
+ unique int id: @directive_line ref,
+ int file: @file ref);
+
+directive_line_offset(
+ unique int id: @directive_line ref,
+ int offset: int ref);
+
+directive_line_span(
+ unique int id: @directive_line ref,
+ int startLine: int ref,
+ int startColumn: int ref,
+ int endLine: int ref,
+ int endColumn: int ref);
+
+directive_nullables(
+ unique int id: @directive_nullable,
+ int setting: int ref, /* 0: disable, 1: enable, 2: restore */
+ int target: int ref); /* 0: none, 1: annotations, 2: warnings */
+
+directive_warnings(
+ unique int id: @directive_warning,
+ string message: string ref);
+
+directive_errors(
+ unique int id: @directive_error,
+ string message: string ref);
+
+directive_undefines(
+ unique int id: @directive_undefine,
+ string name: string ref);
+
+directive_defines(
+ unique int id: @directive_define,
+ string name: string ref);
+
+pragma_checksums(
+ unique int id: @pragma_checksum,
+ int file: @file ref,
+ string guid: string ref,
+ string bytes: string ref);
+
+pragma_warnings(
+ unique int id: @pragma_warning,
+ int kind: int ref /* 0 = disable, 1 = restore */);
+
+#keyset[id, index]
+pragma_warning_error_codes(
+ int id: @pragma_warning ref,
+ string errorCode: string ref,
+ int index: int ref);
+
+preprocessor_directive_location(
+ unique int id: @preprocessor_directive ref,
+ int loc: @location ref);
+
+preprocessor_directive_compilation(
+ int id: @preprocessor_directive ref,
+ int compilation: @compilation ref);
+
+preprocessor_directive_active(
+ unique int id: @preprocessor_directive ref,
+ int active: int ref); /* 0: false, 1: true */
+
+/** TYPES **/
+
+types(
+ unique int id: @type,
+ int kind: int ref,
+ string name: string ref);
+
+case @type.kind of
+ 1 = @bool_type
+| 2 = @char_type
+| 3 = @decimal_type
+| 4 = @sbyte_type
+| 5 = @short_type
+| 6 = @int_type
+| 7 = @long_type
+| 8 = @byte_type
+| 9 = @ushort_type
+| 10 = @uint_type
+| 11 = @ulong_type
+| 12 = @float_type
+| 13 = @double_type
+| 14 = @enum_type
+| 15 = @struct_type
+| 17 = @class_type
+| 19 = @interface_type
+| 20 = @delegate_type
+| 21 = @null_type
+| 22 = @type_parameter
+| 23 = @pointer_type
+| 24 = @nullable_type
+| 25 = @array_type
+| 26 = @void_type
+| 27 = @int_ptr_type
+| 28 = @uint_ptr_type
+| 29 = @dynamic_type
+| 30 = @arglist_type
+| 31 = @unknown_type
+| 32 = @tuple_type
+| 33 = @function_pointer_type
+| 34 = @inline_array_type
+ ;
+
+@simple_type = @bool_type | @char_type | @integral_type | @floating_point_type | @decimal_type;
+@integral_type = @signed_integral_type | @unsigned_integral_type;
+@signed_integral_type = @sbyte_type | @short_type | @int_type | @long_type;
+@unsigned_integral_type = @byte_type | @ushort_type | @uint_type | @ulong_type;
+@floating_point_type = @float_type | @double_type;
+@value_type = @simple_type | @enum_type | @struct_type | @nullable_type | @int_ptr_type
+ | @uint_ptr_type | @tuple_type | @void_type | @inline_array_type;
+@ref_type = @class_type | @interface_type | @array_type | @delegate_type | @null_type
+ | @dynamic_type;
+@value_or_ref_type = @value_type | @ref_type;
+
+typerefs(
+ unique int id: @typeref,
+ string name: string ref);
+
+typeref_type(
+ int id: @typeref ref,
+ unique int typeId: @type ref);
+
+@type_or_ref = @type | @typeref;
+
+array_element_type(
+ unique int array: @array_type ref,
+ int dimension: int ref,
+ int rank: int ref,
+ int element: @type_or_ref ref);
+
+nullable_underlying_type(
+ unique int nullable: @nullable_type ref,
+ int underlying: @type_or_ref ref);
+
+pointer_referent_type(
+ unique int pointer: @pointer_type ref,
+ int referent: @type_or_ref ref);
+
+enum_underlying_type(
+ unique int enum_id: @enum_type ref,
+ int underlying_type_id: @type_or_ref ref);
+
+delegate_return_type(
+ unique int delegate_id: @delegate_type ref,
+ int return_type_id: @type_or_ref ref);
+
+function_pointer_return_type(
+ unique int function_pointer_id: @function_pointer_type ref,
+ int return_type_id: @type_or_ref ref);
+
+extend(
+ int sub: @type ref,
+ int super: @type_or_ref ref);
+
+anonymous_types(
+ unique int id: @type ref);
+
+@interface_or_ref = @interface_type | @typeref;
+
+implement(
+ int sub: @type ref,
+ int super: @type_or_ref ref);
+
+type_location(
+ int id: @type ref,
+ int loc: @location ref);
+
+tuple_underlying_type(
+ unique int tuple: @tuple_type ref,
+ int struct: @type_or_ref ref);
+
+#keyset[tuple, index]
+tuple_element(
+ int tuple: @tuple_type ref,
+ int index: int ref,
+ unique int field: @field ref);
+
+attributes(
+ unique int id: @attribute,
+ int kind: int ref,
+ int type_id: @type_or_ref ref,
+ int target: @attributable ref);
+
+case @attribute.kind of
+ 0 = @attribute_default
+| 1 = @attribute_return
+| 2 = @attribute_assembly
+| 3 = @attribute_module
+;
+
+attribute_location(
+ int id: @attribute ref,
+ int loc: @location ref);
+
+@type_mention_parent = @element | @type_mention;
+
+type_mention(
+ unique int id: @type_mention,
+ int type_id: @type_or_ref ref,
+ int parent: @type_mention_parent ref);
+
+type_mention_location(
+ unique int id: @type_mention ref,
+ int loc: @location ref);
+
+@has_type_annotation = @assignable | @type_parameter | @callable | @expr | @delegate_type | @generic | @function_pointer_type;
+
+/**
+ * A direct annotation on an entity, for example `string? x;`.
+ *
+ * Annotations:
+ * 2 = reftype is not annotated "!"
+ * 3 = reftype is annotated "?"
+ * 4 = readonly ref type / in parameter
+ * 5 = ref type parameter, return or local variable
+ * 6 = out parameter
+ *
+ * Note that the annotation depends on the element it annotates.
+ * @assignable: The annotation is on the type of the assignable, for example the variable type.
+ * @type_parameter: The annotation is on the reftype constraint
+ * @callable: The annotation is on the return type
+ * @array_type: The annotation is on the element type
+ */
+type_annotation(int id: @has_type_annotation ref, int annotation: int ref);
+
+nullability(unique int nullability: @nullability, int kind: int ref);
+
+case @nullability.kind of
+ 0 = @oblivious
+| 1 = @not_annotated
+| 2 = @annotated
+;
+
+#keyset[parent, index]
+nullability_parent(int nullability: @nullability ref, int index: int ref, int parent: @nullability ref)
+
+type_nullability(int id: @has_type_annotation ref, int nullability: @nullability ref);
+
+/**
+ * The nullable flow state of an expression, as determined by Roslyn.
+ * 0 = none (default, not populated)
+ * 1 = not null
+ * 2 = maybe null
+ */
+expr_flowstate(unique int id: @expr ref, int state: int ref);
+
+/** GENERICS **/
+
+@generic = @type | @method | @local_function;
+
+type_parameters(
+ unique int id: @type_parameter ref,
+ int index: int ref,
+ int generic_id: @generic ref,
+ int variance: int ref /* none = 0, out = 1, in = 2 */);
+
+#keyset[constructed_id, index]
+type_arguments(
+ int id: @type_or_ref ref,
+ int index: int ref,
+ int constructed_id: @generic_or_ref ref);
+
+@generic_or_ref = @generic | @typeref;
+
+constructed_generic(
+ unique int constructed: @generic ref,
+ int generic: @generic_or_ref ref);
+
+type_parameter_constraints(
+ unique int id: @type_parameter_constraints,
+ int param_id: @type_parameter ref);
+
+type_parameter_constraints_location(
+ int id: @type_parameter_constraints ref,
+ int loc: @location ref);
+
+general_type_parameter_constraints(
+ int id: @type_parameter_constraints ref,
+ int kind: int ref /* class = 1, struct = 2, new = 3 */);
+
+specific_type_parameter_constraints(
+ int id: @type_parameter_constraints ref,
+ int base_id: @type_or_ref ref);
+
+specific_type_parameter_nullability(
+ int id: @type_parameter_constraints ref,
+ int base_id: @type_or_ref ref,
+ int nullability: @nullability ref);
+
+/** FUNCTION POINTERS */
+
+function_pointer_calling_conventions(
+ int id: @function_pointer_type ref,
+ int kind: int ref);
+
+#keyset[id, index]
+has_unmanaged_calling_conventions(
+ int id: @function_pointer_type ref,
+ int index: int ref,
+ int conv_id: @type_or_ref ref);
+
+/** MODIFIERS */
+
+@modifiable = @modifiable_direct | @event_accessor;
+
+@modifiable_direct = @member | @accessor | @local_function | @anonymous_function_expr;
+
+modifiers(
+ unique int id: @modifier,
+ string name: string ref);
+
+has_modifiers(
+ int id: @modifiable_direct ref,
+ int mod_id: @modifier ref);
+
+/** MEMBERS **/
+
+@member = @method | @constructor | @destructor | @field | @property | @event | @operator | @indexer | @type;
+
+@named_exprorstmt = @goto_stmt | @labeled_stmt | @expr;
+
+@virtualizable = @method | @property | @indexer | @event | @operator;
+
+exprorstmt_name(
+ unique int parent_id: @named_exprorstmt ref,
+ string name: string ref);
+
+nested_types(
+ unique int id: @type ref,
+ int declaring_type_id: @type ref,
+ int unbound_id: @type ref);
+
+properties(
+ unique int id: @property,
+ string name: string ref,
+ int declaring_type_id: @type ref,
+ int type_id: @type_or_ref ref,
+ int unbound_id: @property ref);
+
+property_location(
+ int id: @property ref,
+ int loc: @location ref);
+
+indexers(
+ unique int id: @indexer,
+ string name: string ref,
+ int declaring_type_id: @type ref,
+ int type_id: @type_or_ref ref,
+ int unbound_id: @indexer ref);
+
+indexer_location(
+ int id: @indexer ref,
+ int loc: @location ref);
+
+accessors(
+ unique int id: @accessor,
+ int kind: int ref,
+ string name: string ref,
+ int declaring_member_id: @member ref,
+ int unbound_id: @accessor ref);
+
+case @accessor.kind of
+ 1 = @getter
+| 2 = @setter
+ ;
+
+init_only_accessors(
+ unique int id: @accessor ref);
+
+accessor_location(
+ int id: @accessor ref,
+ int loc: @location ref);
+
+events(
+ unique int id: @event,
+ string name: string ref,
+ int declaring_type_id: @type ref,
+ int type_id: @type_or_ref ref,
+ int unbound_id: @event ref);
+
+event_location(
+ int id: @event ref,
+ int loc: @location ref);
+
+event_accessors(
+ unique int id: @event_accessor,
+ int kind: int ref,
+ string name: string ref,
+ int declaring_event_id: @event ref,
+ int unbound_id: @event_accessor ref);
+
+case @event_accessor.kind of
+ 1 = @add_event_accessor
+| 2 = @remove_event_accessor
+ ;
+
+event_accessor_location(
+ int id: @event_accessor ref,
+ int loc: @location ref);
+
+operators(
+ unique int id: @operator,
+ string name: string ref,
+ string symbol: string ref,
+ int declaring_type_id: @type ref,
+ int type_id: @type_or_ref ref,
+ int unbound_id: @operator ref);
+
+operator_location(
+ int id: @operator ref,
+ int loc: @location ref);
+
+constant_value(
+ int id: @variable ref,
+ string value: string ref);
+
+/** CALLABLES **/
+
+@callable = @method | @constructor | @destructor | @operator | @callable_accessor | @anonymous_function_expr | @local_function;
+
+@callable_accessor = @accessor | @event_accessor;
+
+methods(
+ unique int id: @method,
+ string name: string ref,
+ int declaring_type_id: @type ref,
+ int type_id: @type_or_ref ref,
+ int unbound_id: @method ref);
+
+method_location(
+ int id: @method ref,
+ int loc: @location ref);
+
+constructors(
+ unique int id: @constructor,
+ string name: string ref,
+ int declaring_type_id: @type ref,
+ int unbound_id: @constructor ref);
+
+constructor_location(
+ int id: @constructor ref,
+ int loc: @location ref);
+
+destructors(
+ unique int id: @destructor,
+ string name: string ref,
+ int declaring_type_id: @type ref,
+ int unbound_id: @destructor ref);
+
+destructor_location(
+ int id: @destructor ref,
+ int loc: @location ref);
+
+overrides(
+ int id: @callable ref,
+ int base_id: @callable ref);
+
+explicitly_implements(
+ int id: @member ref,
+ int interface_id: @interface_or_ref ref);
+
+local_functions(
+ unique int id: @local_function,
+ string name: string ref,
+ int return_type: @type ref,
+ int unbound_id: @local_function ref);
+
+local_function_stmts(
+ unique int fn: @local_function_stmt ref,
+ int stmt: @local_function ref);
+
+/** VARIABLES **/
+
+@variable = @local_scope_variable | @field;
+
+@local_scope_variable = @local_variable | @parameter;
+
+fields(
+ unique int id: @field,
+ int kind: int ref,
+ string name: string ref,
+ int declaring_type_id: @type ref,
+ int type_id: @type_or_ref ref,
+ int unbound_id: @field ref);
+
+case @field.kind of
+ 1 = @addressable_field
+| 2 = @constant
+ ;
+
+field_location(
+ int id: @field ref,
+ int loc: @location ref);
+
+localvars(
+ unique int id: @local_variable,
+ int kind: int ref,
+ string name: string ref,
+ int implicitly_typed: int ref /* 0 = no, 1 = yes */,
+ int type_id: @type_or_ref ref,
+ int parent_id: @local_var_decl_expr ref);
+
+case @local_variable.kind of
+ 1 = @addressable_local_variable
+| 2 = @local_constant
+| 3 = @local_variable_ref
+ ;
+
+localvar_location(
+ unique int id: @local_variable ref,
+ int loc: @location ref);
+
+@parameterizable = @callable | @delegate_type | @indexer | @function_pointer_type;
+
+#keyset[name, parent_id]
+#keyset[index, parent_id]
+params(
+ unique int id: @parameter,
+ string name: string ref,
+ int type_id: @type_or_ref ref,
+ int index: int ref,
+ int mode: int ref, /* value = 0, ref = 1, out = 2, params/array = 3, this = 4, in = 5, ref readonly = 6 */
+ int parent_id: @parameterizable ref,
+ int unbound_id: @parameter ref);
+
+param_location(
+ int id: @parameter ref,
+ int loc: @location ref);
+
+@has_scoped_annotation = @local_scope_variable
+
+scoped_annotation(
+ int id: @has_scoped_annotation ref,
+ int kind: int ref // scoped ref = 1, scoped value = 2
+ );
+
+/** STATEMENTS **/
+
+@exprorstmt_parent = @control_flow_element | @top_level_exprorstmt_parent;
+
+statements(
+ unique int id: @stmt,
+ int kind: int ref);
+
+#keyset[index, parent]
+stmt_parent(
+ unique int stmt: @stmt ref,
+ int index: int ref,
+ int parent: @control_flow_element ref);
+
+@top_level_stmt_parent = @callable;
+
+// [index, parent] is not a keyset because the same parent may be compiled multiple times
+stmt_parent_top_level(
+ unique int stmt: @stmt ref,
+ int index: int ref,
+ int parent: @top_level_stmt_parent ref);
+
+case @stmt.kind of
+ 1 = @block_stmt
+| 2 = @expr_stmt
+| 3 = @if_stmt
+| 4 = @switch_stmt
+| 5 = @while_stmt
+| 6 = @do_stmt
+| 7 = @for_stmt
+| 8 = @foreach_stmt
+| 9 = @break_stmt
+| 10 = @continue_stmt
+| 11 = @goto_stmt
+| 12 = @goto_case_stmt
+| 13 = @goto_default_stmt
+| 14 = @throw_stmt
+| 15 = @return_stmt
+| 16 = @yield_stmt
+| 17 = @try_stmt
+| 18 = @checked_stmt
+| 19 = @unchecked_stmt
+| 20 = @lock_stmt
+| 21 = @using_block_stmt
+| 22 = @var_decl_stmt
+| 23 = @const_decl_stmt
+| 24 = @empty_stmt
+| 25 = @unsafe_stmt
+| 26 = @fixed_stmt
+| 27 = @label_stmt
+| 28 = @catch
+| 29 = @case_stmt
+| 30 = @local_function_stmt
+| 31 = @using_decl_stmt
+ ;
+
+@using_stmt = @using_block_stmt | @using_decl_stmt;
+
+@labeled_stmt = @label_stmt | @case;
+
+@decl_stmt = @var_decl_stmt | @const_decl_stmt | @using_decl_stmt;
+
+@cond_stmt = @if_stmt | @switch_stmt;
+
+@loop_stmt = @while_stmt | @do_stmt | @for_stmt | @foreach_stmt;
+
+@jump_stmt = @break_stmt | @goto_any_stmt | @continue_stmt | @throw_stmt | @return_stmt
+ | @yield_stmt;
+
+@goto_any_stmt = @goto_default_stmt | @goto_case_stmt | @goto_stmt;
+
+
+stmt_location(
+ unique int id: @stmt ref,
+ int loc: @location ref);
+
+catch_type(
+ unique int catch_id: @catch ref,
+ int type_id: @type_or_ref ref,
+ int kind: int ref /* explicit = 1, implicit = 2 */);
+
+foreach_stmt_info(
+ unique int id: @foreach_stmt ref,
+ int kind: int ref /* non-async = 1, async = 2 */);
+
+@foreach_symbol = @method | @property | @type_or_ref;
+
+#keyset[id, kind]
+foreach_stmt_desugar(
+ int id: @foreach_stmt ref,
+ int symbol: @foreach_symbol ref,
+ int kind: int ref /* GetEnumeratorMethod = 1, CurrentProperty = 2, MoveNextMethod = 3, DisposeMethod = 4, ElementType = 5 */);
+
+/** EXPRESSIONS **/
+
+expressions(
+ unique int id: @expr,
+ int kind: int ref,
+ int type_id: @type_or_ref ref);
+
+#keyset[index, parent]
+expr_parent(
+ unique int expr: @expr ref,
+ int index: int ref,
+ int parent: @control_flow_element ref);
+
+@top_level_expr_parent = @attribute | @field | @property | @indexer | @parameter | @directive_if | @directive_elif;
+
+@top_level_exprorstmt_parent = @top_level_expr_parent | @top_level_stmt_parent;
+
+// [index, parent] is not a keyset because the same parent may be compiled multiple times
+expr_parent_top_level(
+ unique int expr: @expr ref,
+ int index: int ref,
+ int parent: @top_level_exprorstmt_parent ref);
+
+case @expr.kind of
+/* literal */
+ 1 = @bool_literal_expr
+| 2 = @char_literal_expr
+| 3 = @decimal_literal_expr
+| 4 = @int_literal_expr
+| 5 = @long_literal_expr
+| 6 = @uint_literal_expr
+| 7 = @ulong_literal_expr
+| 8 = @float_literal_expr
+| 9 = @double_literal_expr
+| 10 = @utf16_string_literal_expr
+| 11 = @null_literal_expr
+/* primary & unary */
+| 12 = @this_access_expr
+| 13 = @base_access_expr
+| 14 = @local_variable_access_expr
+| 15 = @parameter_access_expr
+| 16 = @field_access_expr
+| 17 = @property_access_expr
+| 18 = @method_access_expr
+| 19 = @event_access_expr
+| 20 = @indexer_access_expr
+| 21 = @array_access_expr
+| 22 = @type_access_expr
+| 23 = @typeof_expr
+| 24 = @method_invocation_expr
+| 25 = @delegate_invocation_expr
+| 26 = @operator_invocation_expr
+| 27 = @cast_expr
+| 28 = @object_creation_expr
+| 29 = @explicit_delegate_creation_expr
+| 30 = @implicit_delegate_creation_expr
+| 31 = @array_creation_expr
+| 32 = @default_expr
+| 33 = @plus_expr
+| 34 = @minus_expr
+| 35 = @bit_not_expr
+| 36 = @log_not_expr
+| 37 = @post_incr_expr
+| 38 = @post_decr_expr
+| 39 = @pre_incr_expr
+| 40 = @pre_decr_expr
+/* multiplicative */
+| 41 = @mul_expr
+| 42 = @div_expr
+| 43 = @rem_expr
+/* additive */
+| 44 = @add_expr
+| 45 = @sub_expr
+/* shift */
+| 46 = @lshift_expr
+| 47 = @rshift_expr
+/* relational */
+| 48 = @lt_expr
+| 49 = @gt_expr
+| 50 = @le_expr
+| 51 = @ge_expr
+/* equality */
+| 52 = @eq_expr
+| 53 = @ne_expr
+/* logical */
+| 54 = @bit_and_expr
+| 55 = @bit_xor_expr
+| 56 = @bit_or_expr
+| 57 = @log_and_expr
+| 58 = @log_or_expr
+/* type testing */
+| 59 = @is_expr
+| 60 = @as_expr
+/* null coalescing */
+| 61 = @null_coalescing_expr
+/* conditional */
+| 62 = @conditional_expr
+/* assignment */
+| 63 = @simple_assign_expr
+| 64 = @assign_add_expr
+| 65 = @assign_sub_expr
+| 66 = @assign_mul_expr
+| 67 = @assign_div_expr
+| 68 = @assign_rem_expr
+| 69 = @assign_and_expr
+| 70 = @assign_xor_expr
+| 71 = @assign_or_expr
+| 72 = @assign_lshift_expr
+| 73 = @assign_rshift_expr
+/* more */
+| 74 = @object_init_expr
+| 75 = @collection_init_expr
+| 76 = @array_init_expr
+| 77 = @checked_expr
+| 78 = @unchecked_expr
+| 79 = @constructor_init_expr
+| 80 = @add_event_expr
+| 81 = @remove_event_expr
+| 82 = @par_expr
+| 83 = @local_var_decl_expr
+| 84 = @lambda_expr
+| 85 = @anonymous_method_expr
+| 86 = @namespace_expr
+/* dynamic */
+| 92 = @dynamic_element_access_expr
+| 93 = @dynamic_member_access_expr
+/* unsafe */
+| 100 = @pointer_indirection_expr
+| 101 = @address_of_expr
+| 102 = @sizeof_expr
+/* async */
+| 103 = @await_expr
+/* C# 6.0 */
+| 104 = @nameof_expr
+| 105 = @interpolated_string_expr
+| 106 = @unknown_expr
+/* C# 7.0 */
+| 107 = @throw_expr
+| 108 = @tuple_expr
+| 109 = @local_function_invocation_expr
+| 110 = @ref_expr
+| 111 = @discard_expr
+/* C# 8.0 */
+| 112 = @range_expr
+| 113 = @index_expr
+| 114 = @switch_expr
+| 115 = @recursive_pattern_expr
+| 116 = @property_pattern_expr
+| 117 = @positional_pattern_expr
+| 118 = @switch_case_expr
+| 119 = @assign_coalesce_expr
+| 120 = @suppress_nullable_warning_expr
+| 121 = @namespace_access_expr
+/* C# 9.0 */
+| 122 = @lt_pattern_expr
+| 123 = @gt_pattern_expr
+| 124 = @le_pattern_expr
+| 125 = @ge_pattern_expr
+| 126 = @not_pattern_expr
+| 127 = @and_pattern_expr
+| 128 = @or_pattern_expr
+| 129 = @function_pointer_invocation_expr
+| 130 = @with_expr
+/* C# 11.0 */
+| 131 = @list_pattern_expr
+| 132 = @slice_pattern_expr
+| 133 = @urshift_expr
+| 134 = @assign_urshift_expr
+| 135 = @utf8_string_literal_expr
+/* C# 12.0 */
+| 136 = @collection_expr
+| 137 = @spread_element_expr
+| 138 = @interpolated_string_insert_expr
+/* Preprocessor */
+| 999 = @define_symbol_expr
+;
+
+@switch = @switch_stmt | @switch_expr;
+@case = @case_stmt | @switch_case_expr;
+@pattern_match = @case | @is_expr;
+@unary_pattern_expr = @not_pattern_expr;
+@relational_pattern_expr = @gt_pattern_expr | @lt_pattern_expr | @ge_pattern_expr | @le_pattern_expr;
+@binary_pattern_expr = @and_pattern_expr | @or_pattern_expr;
+
+@integer_literal_expr = @int_literal_expr | @long_literal_expr | @uint_literal_expr | @ulong_literal_expr;
+@real_literal_expr = @float_literal_expr | @double_literal_expr | @decimal_literal_expr;
+@string_literal_expr = @utf16_string_literal_expr | @utf8_string_literal_expr;
+@literal_expr = @bool_literal_expr | @char_literal_expr | @integer_literal_expr | @real_literal_expr
+ | @string_literal_expr | @null_literal_expr;
+
+@assign_expr = @simple_assign_expr | @assign_op_expr | @local_var_decl_expr;
+@assign_op_expr = @assign_arith_expr | @assign_bitwise_expr | @assign_event_expr | @assign_coalesce_expr;
+@assign_event_expr = @add_event_expr | @remove_event_expr;
+
+@assign_arith_expr = @assign_add_expr | @assign_sub_expr | @assign_mul_expr | @assign_div_expr
+ | @assign_rem_expr
+@assign_bitwise_expr = @assign_and_expr | @assign_or_expr | @assign_xor_expr
+ | @assign_lshift_expr | @assign_rshift_expr | @assign_urshift_expr;
+
+@member_access_expr = @field_access_expr | @property_access_expr | @indexer_access_expr | @event_access_expr
+ | @method_access_expr | @type_access_expr | @dynamic_member_access_expr;
+@access_expr = @member_access_expr | @this_access_expr | @base_access_expr | @assignable_access_expr | @namespace_access_expr;
+@element_access_expr = @indexer_access_expr | @array_access_expr | @dynamic_element_access_expr;
+
+@local_variable_access = @local_variable_access_expr | @local_var_decl_expr;
+@local_scope_variable_access_expr = @parameter_access_expr | @local_variable_access;
+@variable_access_expr = @local_scope_variable_access_expr | @field_access_expr;
+
+@assignable_access_expr = @variable_access_expr | @property_access_expr | @element_access_expr
+ | @event_access_expr | @dynamic_member_access_expr;
+
+@objectorcollection_init_expr = @object_init_expr | @collection_init_expr;
+
+@delegate_creation_expr = @explicit_delegate_creation_expr | @implicit_delegate_creation_expr;
+
+@bin_arith_op_expr = @mul_expr | @div_expr | @rem_expr | @add_expr | @sub_expr;
+@incr_op_expr = @pre_incr_expr | @post_incr_expr;
+@decr_op_expr = @pre_decr_expr | @post_decr_expr;
+@mut_op_expr = @incr_op_expr | @decr_op_expr;
+@un_arith_op_expr = @plus_expr | @minus_expr | @mut_op_expr;
+@arith_op_expr = @bin_arith_op_expr | @un_arith_op_expr;
+
+@ternary_log_op_expr = @conditional_expr;
+@bin_log_op_expr = @log_and_expr | @log_or_expr | @null_coalescing_expr;
+@un_log_op_expr = @log_not_expr;
+@log_expr = @un_log_op_expr | @bin_log_op_expr | @ternary_log_op_expr;
+
+@bin_bit_op_expr = @bit_and_expr | @bit_or_expr | @bit_xor_expr | @lshift_expr
+ | @rshift_expr | @urshift_expr;
+@un_bit_op_expr = @bit_not_expr;
+@bit_expr = @un_bit_op_expr | @bin_bit_op_expr;
+
+@equality_op_expr = @eq_expr | @ne_expr;
+@rel_op_expr = @gt_expr | @lt_expr| @ge_expr | @le_expr;
+@comp_expr = @equality_op_expr | @rel_op_expr;
+
+@op_expr = @assign_expr | @un_op | @bin_op | @ternary_op;
+
+@ternary_op = @ternary_log_op_expr;
+@bin_op = @bin_arith_op_expr | @bin_log_op_expr | @bin_bit_op_expr | @comp_expr;
+@un_op = @un_arith_op_expr | @un_log_op_expr | @un_bit_op_expr | @sizeof_expr
+ | @pointer_indirection_expr | @address_of_expr;
+
+@anonymous_function_expr = @lambda_expr | @anonymous_method_expr;
+
+@call = @method_invocation_expr | @constructor_init_expr | @operator_invocation_expr
+ | @delegate_invocation_expr | @object_creation_expr | @call_access_expr
+ | @local_function_invocation_expr | @function_pointer_invocation_expr;
+
+@call_access_expr = @property_access_expr | @event_access_expr | @indexer_access_expr;
+
+@late_bindable_expr = @dynamic_element_access_expr | @dynamic_member_access_expr
+ | @object_creation_expr | @method_invocation_expr | @operator_invocation_expr;
+
+@throw_element = @throw_expr | @throw_stmt;
+
+@implicitly_typeable_object_creation_expr = @object_creation_expr | @explicit_delegate_creation_expr;
+
+implicitly_typed_array_creation(
+ unique int id: @array_creation_expr ref);
+
+explicitly_sized_array_creation(
+ unique int id: @array_creation_expr ref);
+
+stackalloc_array_creation(
+ unique int id: @array_creation_expr ref);
+
+implicitly_typed_object_creation(
+ unique int id: @implicitly_typeable_object_creation_expr ref);
+
+mutator_invocation_mode(
+ unique int id: @operator_invocation_expr ref,
+ int mode: int ref /* prefix = 1, postfix = 2*/);
+
+expr_value(
+ unique int id: @expr ref,
+ string value: string ref);
+
+expr_call(
+ unique int caller_id: @expr ref,
+ int target_id: @callable ref);
+
+expr_access(
+ unique int accesser_id: @access_expr ref,
+ int target_id: @accessible ref);
+
+@accessible = @method | @assignable | @local_function | @namespace;
+
+expr_location(
+ unique int id: @expr ref,
+ int loc: @location ref);
+
+dynamic_member_name(
+ unique int id: @late_bindable_expr ref,
+ string name: string ref);
+
+@qualifiable_expr = @member_access_expr
+ | @method_invocation_expr
+ | @element_access_expr;
+
+conditional_access(
+ unique int id: @qualifiable_expr ref);
+
+expr_argument(
+ unique int id: @expr ref,
+ int mode: int ref);
+ /* mode is the same as params: value = 0, ref = 1, out = 2 */
+
+expr_argument_name(
+ unique int id: @expr ref,
+ string name: string ref);
+
+lambda_expr_return_type(
+ unique int id: @lambda_expr ref,
+ int type_id: @type_or_ref ref);
+
+/* Compiler generated */
+
+compiler_generated(unique int id: @element ref);
+
+/** CONTROL/DATA FLOW **/
+
+@control_flow_element = @stmt | @expr;
+
+/* 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;
+
+/* Comments */
+
+commentline(
+ unique int id: @commentline,
+ int kind: int ref,
+ string text: string ref,
+ string rawtext: string ref);
+
+case @commentline.kind of
+ 0 = @singlelinecomment
+| 1 = @xmldoccomment
+| 2 = @multilinecomment;
+
+commentline_location(
+ unique int id: @commentline ref,
+ int loc: @location ref);
+
+commentblock(
+ unique int id : @commentblock);
+
+commentblock_location(
+ unique int id: @commentblock ref,
+ int loc: @location ref);
+
+commentblock_binding(
+ int id: @commentblock ref,
+ int entity: @element ref,
+ int bindtype: int ref); /* 0: Parent, 1: Best, 2: Before, 3: After */
+
+commentblock_child(
+ int id: @commentblock ref,
+ int commentline: @commentline ref,
+ int index: int ref);
+
+/* ASP.NET */
+
+case @asp_element.kind of
+ 0=@asp_close_tag
+| 1=@asp_code
+| 2=@asp_comment
+| 3=@asp_data_binding
+| 4=@asp_directive
+| 5=@asp_open_tag
+| 6=@asp_quoted_string
+| 7=@asp_text
+| 8=@asp_xml_directive;
+
+@asp_attribute = @asp_code | @asp_data_binding | @asp_quoted_string;
+
+asp_elements(
+ unique int id: @asp_element,
+ int kind: int ref,
+ int loc: @location ref);
+
+asp_comment_server(unique int comment: @asp_comment ref);
+asp_code_inline(unique int code: @asp_code ref);
+asp_directive_attribute(
+ int directive: @asp_directive ref,
+ int index: int ref,
+ string name: string ref,
+ int value: @asp_quoted_string ref);
+asp_directive_name(
+ unique int directive: @asp_directive ref,
+ string name: string ref);
+asp_element_body(
+ unique int element: @asp_element ref,
+ string body: string ref);
+asp_tag_attribute(
+ int tag: @asp_open_tag ref,
+ int index: int ref,
+ string name: string ref,
+ int attribute: @asp_attribute ref);
+asp_tag_name(
+ unique int tag: @asp_open_tag ref,
+ string name: string ref);
+asp_tag_isempty(int tag: @asp_open_tag ref);
diff --git a/csharp/ql/lib/upgrades/a2bda57dbc6eea94c50128522aae536e8edd5a3c/string_interpol_insert.ql b/csharp/ql/lib/upgrades/a2bda57dbc6eea94c50128522aae536e8edd5a3c/string_interpol_insert.ql
new file mode 100644
index 000000000000..2df164b2842e
--- /dev/null
+++ b/csharp/ql/lib/upgrades/a2bda57dbc6eea94c50128522aae536e8edd5a3c/string_interpol_insert.ql
@@ -0,0 +1,57 @@
+class Expr extends @expr {
+ string toString() { none() }
+}
+
+class TypeOrRef extends @type_or_ref {
+ string toString() { none() }
+}
+
+class StringLiteral extends Expr, @string_literal_expr { }
+
+class InterpolatedStringExpr extends Expr, @interpolated_string_expr { }
+
+class StringInterpolationInsert extends Expr, @element {
+ StringInterpolationInsert() {
+ expressions(this, _, _) and
+ expr_parent(this, _, any(InterpolatedStringExpr x)) and
+ not this instanceof StringLiteral
+ }
+}
+
+newtype TAddedElement = TInsert(StringInterpolationInsert e)
+
+module Fresh = QlBuiltins::NewEntity;
+
+class TNewExpr = @expr or Fresh::EntityId;
+
+class NewExpr extends TNewExpr {
+ string toString() { none() }
+}
+
+query predicate new_expressions(NewExpr id, int kind, TypeOrRef t) {
+ expressions(id, kind, t)
+ or
+ exists(StringInterpolationInsert e |
+ // The type of `e` is just copied even though a null type would be preferred.
+ expressions(e, _, t) and
+ Fresh::map(TInsert(e)) = id and
+ kind = 138
+ )
+}
+
+query predicate new_expr_parent(NewExpr id, int child, NewExpr parent) {
+ // Keep all parent child relationships except for string interpolation inserts
+ expr_parent(id, child, parent) and not id instanceof StringInterpolationInsert
+ or
+ exists(StringInterpolationInsert e, int child0, NewExpr p0, NewExpr new_id |
+ expr_parent(e, child0, p0) and new_id = Fresh::map(TInsert(e))
+ |
+ id = new_id and
+ child = child0 and
+ parent = p0
+ or
+ id = e and
+ child = 0 and
+ parent = new_id
+ )
+}
diff --git a/csharp/ql/lib/upgrades/a2bda57dbc6eea94c50128522aae536e8edd5a3c/upgrade.properties b/csharp/ql/lib/upgrades/a2bda57dbc6eea94c50128522aae536e8edd5a3c/upgrade.properties
new file mode 100644
index 000000000000..ef7a5cbf3436
--- /dev/null
+++ b/csharp/ql/lib/upgrades/a2bda57dbc6eea94c50128522aae536e8edd5a3c/upgrade.properties
@@ -0,0 +1,4 @@
+description: Add `interpolated_string_insert_expr` kind.
+compatibility: backwards
+expressions.rel: run string_interpol_insert.qlo new_expressions
+expr_parent.rel: run string_interpol_insert.qlo new_expr_parent
diff --git a/csharp/ql/test/library-tests/controlflow/graph/BasicBlock.expected b/csharp/ql/test/library-tests/controlflow/graph/BasicBlock.expected
index b3ea35fe7e4b..623b5404f781 100644
--- a/csharp/ql/test/library-tests/controlflow/graph/BasicBlock.expected
+++ b/csharp/ql/test/library-tests/controlflow/graph/BasicBlock.expected
@@ -377,8 +377,8 @@
| Conditions.cs:143:10:143:12 | exit M11 (normal) | Conditions.cs:143:10:143:12 | exit M11 | 2 |
| Conditions.cs:145:21:145:23 | [b (line 143): true] "a" | Conditions.cs:146:13:146:13 | [b (line 143): true] access to parameter b | 5 |
| Conditions.cs:145:27:145:29 | [b (line 143): false] "b" | Conditions.cs:146:13:146:13 | [b (line 143): false] access to parameter b | 5 |
-| Conditions.cs:147:13:147:49 | ...; | Conditions.cs:147:13:147:48 | call to method WriteLine | 5 |
-| Conditions.cs:149:13:149:49 | ...; | Conditions.cs:149:13:149:48 | call to method WriteLine | 5 |
+| Conditions.cs:147:13:147:49 | ...; | Conditions.cs:147:13:147:48 | call to method WriteLine | 6 |
+| Conditions.cs:149:13:149:49 | ...; | Conditions.cs:149:13:149:48 | call to method WriteLine | 6 |
| ExitMethods.cs:6:7:6:17 | enter ExitMethods | ExitMethods.cs:6:7:6:17 | exit ExitMethods | 5 |
| ExitMethods.cs:8:10:8:11 | enter M1 | ExitMethods.cs:8:10:8:11 | exit M1 | 8 |
| ExitMethods.cs:14:10:14:11 | enter M2 | ExitMethods.cs:14:10:14:11 | exit M2 | 8 |
@@ -859,11 +859,11 @@
| Patterns.cs:5:10:5:11 | enter M1 | Patterns.cs:8:18:8:23 | Int32 i1 | 8 |
| Patterns.cs:8:13:8:23 | [false] ... is ... | Patterns.cs:8:13:8:23 | [false] ... is ... | 1 |
| Patterns.cs:8:13:8:23 | [true] ... is ... | Patterns.cs:8:13:8:23 | [true] ... is ... | 1 |
-| Patterns.cs:9:9:11:9 | {...} | Patterns.cs:10:13:10:42 | call to method WriteLine | 6 |
+| Patterns.cs:9:9:11:9 | {...} | Patterns.cs:10:13:10:42 | call to method WriteLine | 7 |
| Patterns.cs:12:14:18:9 | if (...) ... | Patterns.cs:12:23:12:31 | String s1 | 3 |
| Patterns.cs:12:18:12:31 | [false] ... is ... | Patterns.cs:12:18:12:31 | [false] ... is ... | 1 |
| Patterns.cs:12:18:12:31 | [true] ... is ... | Patterns.cs:12:18:12:31 | [true] ... is ... | 1 |
-| Patterns.cs:13:9:15:9 | {...} | Patterns.cs:14:13:14:45 | call to method WriteLine | 6 |
+| Patterns.cs:13:9:15:9 | {...} | Patterns.cs:14:13:14:45 | call to method WriteLine | 7 |
| Patterns.cs:16:14:18:9 | if (...) ... | Patterns.cs:16:23:16:28 | Object v1 | 3 |
| Patterns.cs:16:18:16:28 | [false] ... is ... | Patterns.cs:16:18:16:28 | [false] ... is ... | 1 |
| Patterns.cs:16:18:16:28 | [true] ... is ... | Patterns.cs:16:18:16:28 | [true] ... is ... | 1 |
@@ -872,11 +872,11 @@
| Patterns.cs:23:17:23:22 | break; | Patterns.cs:23:17:23:22 | break; | 1 |
| Patterns.cs:24:13:24:36 | case ...: | Patterns.cs:24:18:24:23 | Int32 i2 | 2 |
| Patterns.cs:24:30:24:31 | access to local variable i2 | Patterns.cs:24:30:24:35 | ... > ... | 3 |
-| Patterns.cs:25:17:25:52 | ...; | Patterns.cs:26:17:26:22 | break; | 6 |
+| Patterns.cs:25:17:25:52 | ...; | Patterns.cs:26:17:26:22 | break; | 7 |
| Patterns.cs:27:13:27:24 | case ...: | Patterns.cs:27:18:27:23 | Int32 i3 | 2 |
-| Patterns.cs:28:17:28:47 | ...; | Patterns.cs:29:17:29:22 | break; | 6 |
+| Patterns.cs:28:17:28:47 | ...; | Patterns.cs:29:17:29:22 | break; | 7 |
| Patterns.cs:30:13:30:27 | case ...: | Patterns.cs:30:18:30:26 | String s2 | 2 |
-| Patterns.cs:31:17:31:50 | ...; | Patterns.cs:32:17:32:22 | break; | 6 |
+| Patterns.cs:31:17:31:50 | ...; | Patterns.cs:32:17:32:22 | break; | 7 |
| Patterns.cs:33:13:33:24 | case ...: | Patterns.cs:33:18:33:23 | Object v2 | 2 |
| Patterns.cs:34:17:34:22 | break; | Patterns.cs:34:17:34:22 | break; | 1 |
| Patterns.cs:35:13:35:20 | default: | Patterns.cs:37:17:37:22 | break; | 5 |
@@ -1076,8 +1076,8 @@
| Switch.cs:156:36:156:38 | "a" | Switch.cs:156:28:156:38 | ... => ... | 2 |
| Switch.cs:156:41:156:45 | false | Switch.cs:156:41:156:45 | false | 1 |
| Switch.cs:156:50:156:52 | "b" | Switch.cs:156:41:156:52 | ... => ... | 2 |
-| Switch.cs:158:13:158:49 | ...; | Switch.cs:158:13:158:48 | call to method WriteLine | 5 |
-| Switch.cs:160:13:160:49 | ...; | Switch.cs:160:13:160:48 | call to method WriteLine | 5 |
+| Switch.cs:158:13:158:49 | ...; | Switch.cs:158:13:158:48 | call to method WriteLine | 6 |
+| Switch.cs:160:13:160:49 | ...; | Switch.cs:160:13:160:48 | call to method WriteLine | 6 |
| TypeAccesses.cs:1:7:1:18 | enter TypeAccesses | TypeAccesses.cs:1:7:1:18 | exit TypeAccesses | 5 |
| TypeAccesses.cs:3:10:3:10 | enter M | TypeAccesses.cs:7:18:7:22 | Int32 j | 13 |
| TypeAccesses.cs:7:13:7:22 | [false] ... is ... | TypeAccesses.cs:7:13:7:22 | [false] ... is ... | 1 |
diff --git a/csharp/ql/test/library-tests/controlflow/graph/Dominance.expected b/csharp/ql/test/library-tests/controlflow/graph/Dominance.expected
index 7706539ad306..b2d700660689 100644
--- a/csharp/ql/test/library-tests/controlflow/graph/Dominance.expected
+++ b/csharp/ql/test/library-tests/controlflow/graph/Dominance.expected
@@ -1527,11 +1527,13 @@ dominance
| Conditions.cs:147:13:147:49 | ...; | Conditions.cs:147:40:147:43 | "a = " |
| Conditions.cs:147:38:147:47 | $"..." | Conditions.cs:147:13:147:48 | call to method WriteLine |
| Conditions.cs:147:40:147:43 | "a = " | Conditions.cs:147:45:147:45 | access to local variable s |
-| Conditions.cs:147:45:147:45 | access to local variable s | Conditions.cs:147:38:147:47 | $"..." |
+| Conditions.cs:147:44:147:46 | {...} | Conditions.cs:147:38:147:47 | $"..." |
+| Conditions.cs:147:45:147:45 | access to local variable s | Conditions.cs:147:44:147:46 | {...} |
| Conditions.cs:149:13:149:49 | ...; | Conditions.cs:149:40:149:43 | "b = " |
| Conditions.cs:149:38:149:47 | $"..." | Conditions.cs:149:13:149:48 | call to method WriteLine |
| Conditions.cs:149:40:149:43 | "b = " | Conditions.cs:149:45:149:45 | access to local variable s |
-| Conditions.cs:149:45:149:45 | access to local variable s | Conditions.cs:149:38:149:47 | $"..." |
+| Conditions.cs:149:44:149:46 | {...} | Conditions.cs:149:38:149:47 | $"..." |
+| Conditions.cs:149:45:149:45 | access to local variable s | Conditions.cs:149:44:149:46 | {...} |
| ExitMethods.cs:6:7:6:17 | call to constructor Object | ExitMethods.cs:6:7:6:17 | {...} |
| ExitMethods.cs:6:7:6:17 | enter ExitMethods | ExitMethods.cs:6:7:6:17 | call to constructor Object |
| ExitMethods.cs:6:7:6:17 | exit ExitMethods (normal) | ExitMethods.cs:6:7:6:17 | exit ExitMethods |
@@ -3147,7 +3149,8 @@ dominance
| Patterns.cs:10:13:10:43 | ...; | Patterns.cs:10:33:10:36 | "int " |
| Patterns.cs:10:31:10:41 | $"..." | Patterns.cs:10:13:10:42 | call to method WriteLine |
| Patterns.cs:10:33:10:36 | "int " | Patterns.cs:10:38:10:39 | access to local variable i1 |
-| Patterns.cs:10:38:10:39 | access to local variable i1 | Patterns.cs:10:31:10:41 | $"..." |
+| Patterns.cs:10:37:10:40 | {...} | Patterns.cs:10:31:10:41 | $"..." |
+| Patterns.cs:10:38:10:39 | access to local variable i1 | Patterns.cs:10:37:10:40 | {...} |
| Patterns.cs:12:14:18:9 | if (...) ... | Patterns.cs:12:18:12:18 | access to local variable o |
| Patterns.cs:12:18:12:18 | access to local variable o | Patterns.cs:12:23:12:31 | String s1 |
| Patterns.cs:12:18:12:31 | [false] ... is ... | Patterns.cs:16:14:18:9 | if (...) ... |
@@ -3158,7 +3161,8 @@ dominance
| Patterns.cs:14:13:14:46 | ...; | Patterns.cs:14:33:14:39 | "string " |
| Patterns.cs:14:31:14:44 | $"..." | Patterns.cs:14:13:14:45 | call to method WriteLine |
| Patterns.cs:14:33:14:39 | "string " | Patterns.cs:14:41:14:42 | access to local variable s1 |
-| Patterns.cs:14:41:14:42 | access to local variable s1 | Patterns.cs:14:31:14:44 | $"..." |
+| Patterns.cs:14:40:14:43 | {...} | Patterns.cs:14:31:14:44 | $"..." |
+| Patterns.cs:14:41:14:42 | access to local variable s1 | Patterns.cs:14:40:14:43 | {...} |
| Patterns.cs:16:14:18:9 | if (...) ... | Patterns.cs:16:18:16:18 | access to local variable o |
| Patterns.cs:16:18:16:18 | access to local variable o | Patterns.cs:16:23:16:28 | Object v1 |
| Patterns.cs:16:18:16:28 | [true] ... is ... | Patterns.cs:17:9:18:9 | {...} |
@@ -3179,7 +3183,8 @@ dominance
| Patterns.cs:25:17:25:52 | ...; | Patterns.cs:25:37:25:45 | "positive " |
| Patterns.cs:25:35:25:50 | $"..." | Patterns.cs:25:17:25:51 | call to method WriteLine |
| Patterns.cs:25:37:25:45 | "positive " | Patterns.cs:25:47:25:48 | access to local variable i2 |
-| Patterns.cs:25:47:25:48 | access to local variable i2 | Patterns.cs:25:35:25:50 | $"..." |
+| Patterns.cs:25:46:25:49 | {...} | Patterns.cs:25:35:25:50 | $"..." |
+| Patterns.cs:25:47:25:48 | access to local variable i2 | Patterns.cs:25:46:25:49 | {...} |
| Patterns.cs:27:13:27:24 | case ...: | Patterns.cs:27:18:27:23 | Int32 i3 |
| Patterns.cs:27:18:27:23 | Int32 i3 | Patterns.cs:28:17:28:47 | ...; |
| Patterns.cs:27:18:27:23 | Int32 i3 | Patterns.cs:30:13:30:27 | case ...: |
@@ -3187,7 +3192,8 @@ dominance
| Patterns.cs:28:17:28:47 | ...; | Patterns.cs:28:37:28:40 | "int " |
| Patterns.cs:28:35:28:45 | $"..." | Patterns.cs:28:17:28:46 | call to method WriteLine |
| Patterns.cs:28:37:28:40 | "int " | Patterns.cs:28:42:28:43 | access to local variable i3 |
-| Patterns.cs:28:42:28:43 | access to local variable i3 | Patterns.cs:28:35:28:45 | $"..." |
+| Patterns.cs:28:41:28:44 | {...} | Patterns.cs:28:35:28:45 | $"..." |
+| Patterns.cs:28:42:28:43 | access to local variable i3 | Patterns.cs:28:41:28:44 | {...} |
| Patterns.cs:30:13:30:27 | case ...: | Patterns.cs:30:18:30:26 | String s2 |
| Patterns.cs:30:18:30:26 | String s2 | Patterns.cs:31:17:31:50 | ...; |
| Patterns.cs:30:18:30:26 | String s2 | Patterns.cs:33:13:33:24 | case ...: |
@@ -3195,7 +3201,8 @@ dominance
| Patterns.cs:31:17:31:50 | ...; | Patterns.cs:31:37:31:43 | "string " |
| Patterns.cs:31:35:31:48 | $"..." | Patterns.cs:31:17:31:49 | call to method WriteLine |
| Patterns.cs:31:37:31:43 | "string " | Patterns.cs:31:45:31:46 | access to local variable s2 |
-| Patterns.cs:31:45:31:46 | access to local variable s2 | Patterns.cs:31:35:31:48 | $"..." |
+| Patterns.cs:31:44:31:47 | {...} | Patterns.cs:31:35:31:48 | $"..." |
+| Patterns.cs:31:45:31:46 | access to local variable s2 | Patterns.cs:31:44:31:47 | {...} |
| Patterns.cs:33:13:33:24 | case ...: | Patterns.cs:33:18:33:23 | Object v2 |
| Patterns.cs:33:18:33:23 | Object v2 | Patterns.cs:34:17:34:22 | break; |
| Patterns.cs:33:18:33:23 | Object v2 | Patterns.cs:35:13:35:20 | default: |
@@ -3664,11 +3671,13 @@ dominance
| Switch.cs:158:13:158:49 | ...; | Switch.cs:158:40:158:43 | "a = " |
| Switch.cs:158:38:158:47 | $"..." | Switch.cs:158:13:158:48 | call to method WriteLine |
| Switch.cs:158:40:158:43 | "a = " | Switch.cs:158:45:158:45 | access to local variable s |
-| Switch.cs:158:45:158:45 | access to local variable s | Switch.cs:158:38:158:47 | $"..." |
+| Switch.cs:158:44:158:46 | {...} | Switch.cs:158:38:158:47 | $"..." |
+| Switch.cs:158:45:158:45 | access to local variable s | Switch.cs:158:44:158:46 | {...} |
| Switch.cs:160:13:160:49 | ...; | Switch.cs:160:40:160:43 | "b = " |
| Switch.cs:160:38:160:47 | $"..." | Switch.cs:160:13:160:48 | call to method WriteLine |
| Switch.cs:160:40:160:43 | "b = " | Switch.cs:160:45:160:45 | access to local variable s |
-| Switch.cs:160:45:160:45 | access to local variable s | Switch.cs:160:38:160:47 | $"..." |
+| Switch.cs:160:44:160:46 | {...} | Switch.cs:160:38:160:47 | $"..." |
+| Switch.cs:160:45:160:45 | access to local variable s | Switch.cs:160:44:160:46 | {...} |
| TypeAccesses.cs:1:7:1:18 | call to constructor Object | TypeAccesses.cs:1:7:1:18 | {...} |
| TypeAccesses.cs:1:7:1:18 | enter TypeAccesses | TypeAccesses.cs:1:7:1:18 | call to constructor Object |
| TypeAccesses.cs:1:7:1:18 | exit TypeAccesses (normal) | TypeAccesses.cs:1:7:1:18 | exit TypeAccesses |
@@ -5858,13 +5867,15 @@ postDominance
| Conditions.cs:146:13:146:13 | [b (line 143): true] access to parameter b | Conditions.cs:146:9:149:49 | [b (line 143): true] if (...) ... |
| Conditions.cs:147:13:147:48 | call to method WriteLine | Conditions.cs:147:38:147:47 | $"..." |
| Conditions.cs:147:13:147:49 | ...; | Conditions.cs:146:13:146:13 | [b (line 143): true] access to parameter b |
-| Conditions.cs:147:38:147:47 | $"..." | Conditions.cs:147:45:147:45 | access to local variable s |
+| Conditions.cs:147:38:147:47 | $"..." | Conditions.cs:147:44:147:46 | {...} |
| Conditions.cs:147:40:147:43 | "a = " | Conditions.cs:147:13:147:49 | ...; |
+| Conditions.cs:147:44:147:46 | {...} | Conditions.cs:147:45:147:45 | access to local variable s |
| Conditions.cs:147:45:147:45 | access to local variable s | Conditions.cs:147:40:147:43 | "a = " |
| Conditions.cs:149:13:149:48 | call to method WriteLine | Conditions.cs:149:38:149:47 | $"..." |
| Conditions.cs:149:13:149:49 | ...; | Conditions.cs:146:13:146:13 | [b (line 143): false] access to parameter b |
-| Conditions.cs:149:38:149:47 | $"..." | Conditions.cs:149:45:149:45 | access to local variable s |
+| Conditions.cs:149:38:149:47 | $"..." | Conditions.cs:149:44:149:46 | {...} |
| Conditions.cs:149:40:149:43 | "b = " | Conditions.cs:149:13:149:49 | ...; |
+| Conditions.cs:149:44:149:46 | {...} | Conditions.cs:149:45:149:45 | access to local variable s |
| Conditions.cs:149:45:149:45 | access to local variable s | Conditions.cs:149:40:149:43 | "b = " |
| ExitMethods.cs:6:7:6:17 | call to constructor Object | ExitMethods.cs:6:7:6:17 | enter ExitMethods |
| ExitMethods.cs:6:7:6:17 | exit ExitMethods | ExitMethods.cs:6:7:6:17 | exit ExitMethods (normal) |
@@ -7341,8 +7352,9 @@ postDominance
| Patterns.cs:9:9:11:9 | {...} | Patterns.cs:8:13:8:23 | [true] ... is ... |
| Patterns.cs:10:13:10:42 | call to method WriteLine | Patterns.cs:10:31:10:41 | $"..." |
| Patterns.cs:10:13:10:43 | ...; | Patterns.cs:9:9:11:9 | {...} |
-| Patterns.cs:10:31:10:41 | $"..." | Patterns.cs:10:38:10:39 | access to local variable i1 |
+| Patterns.cs:10:31:10:41 | $"..." | Patterns.cs:10:37:10:40 | {...} |
| Patterns.cs:10:33:10:36 | "int " | Patterns.cs:10:13:10:43 | ...; |
+| Patterns.cs:10:37:10:40 | {...} | Patterns.cs:10:38:10:39 | access to local variable i1 |
| Patterns.cs:10:38:10:39 | access to local variable i1 | Patterns.cs:10:33:10:36 | "int " |
| Patterns.cs:12:14:18:9 | if (...) ... | Patterns.cs:8:13:8:23 | [false] ... is ... |
| Patterns.cs:12:18:12:18 | access to local variable o | Patterns.cs:12:14:18:9 | if (...) ... |
@@ -7350,8 +7362,9 @@ postDominance
| Patterns.cs:13:9:15:9 | {...} | Patterns.cs:12:18:12:31 | [true] ... is ... |
| Patterns.cs:14:13:14:45 | call to method WriteLine | Patterns.cs:14:31:14:44 | $"..." |
| Patterns.cs:14:13:14:46 | ...; | Patterns.cs:13:9:15:9 | {...} |
-| Patterns.cs:14:31:14:44 | $"..." | Patterns.cs:14:41:14:42 | access to local variable s1 |
+| Patterns.cs:14:31:14:44 | $"..." | Patterns.cs:14:40:14:43 | {...} |
| Patterns.cs:14:33:14:39 | "string " | Patterns.cs:14:13:14:46 | ...; |
+| Patterns.cs:14:40:14:43 | {...} | Patterns.cs:14:41:14:42 | access to local variable s1 |
| Patterns.cs:14:41:14:42 | access to local variable s1 | Patterns.cs:14:33:14:39 | "string " |
| Patterns.cs:16:14:18:9 | if (...) ... | Patterns.cs:12:18:12:31 | [false] ... is ... |
| Patterns.cs:16:18:16:18 | access to local variable o | Patterns.cs:16:14:18:9 | if (...) ... |
@@ -7368,20 +7381,23 @@ postDominance
| Patterns.cs:24:30:24:35 | ... > ... | Patterns.cs:24:35:24:35 | 0 |
| Patterns.cs:24:35:24:35 | 0 | Patterns.cs:24:30:24:31 | access to local variable i2 |
| Patterns.cs:25:17:25:51 | call to method WriteLine | Patterns.cs:25:35:25:50 | $"..." |
-| Patterns.cs:25:35:25:50 | $"..." | Patterns.cs:25:47:25:48 | access to local variable i2 |
+| Patterns.cs:25:35:25:50 | $"..." | Patterns.cs:25:46:25:49 | {...} |
| Patterns.cs:25:37:25:45 | "positive " | Patterns.cs:25:17:25:52 | ...; |
+| Patterns.cs:25:46:25:49 | {...} | Patterns.cs:25:47:25:48 | access to local variable i2 |
| Patterns.cs:25:47:25:48 | access to local variable i2 | Patterns.cs:25:37:25:45 | "positive " |
| Patterns.cs:26:17:26:22 | break; | Patterns.cs:25:17:25:51 | call to method WriteLine |
| Patterns.cs:27:18:27:23 | Int32 i3 | Patterns.cs:27:13:27:24 | case ...: |
| Patterns.cs:28:17:28:46 | call to method WriteLine | Patterns.cs:28:35:28:45 | $"..." |
-| Patterns.cs:28:35:28:45 | $"..." | Patterns.cs:28:42:28:43 | access to local variable i3 |
+| Patterns.cs:28:35:28:45 | $"..." | Patterns.cs:28:41:28:44 | {...} |
| Patterns.cs:28:37:28:40 | "int " | Patterns.cs:28:17:28:47 | ...; |
+| Patterns.cs:28:41:28:44 | {...} | Patterns.cs:28:42:28:43 | access to local variable i3 |
| Patterns.cs:28:42:28:43 | access to local variable i3 | Patterns.cs:28:37:28:40 | "int " |
| Patterns.cs:29:17:29:22 | break; | Patterns.cs:28:17:28:46 | call to method WriteLine |
| Patterns.cs:30:18:30:26 | String s2 | Patterns.cs:30:13:30:27 | case ...: |
| Patterns.cs:31:17:31:49 | call to method WriteLine | Patterns.cs:31:35:31:48 | $"..." |
-| Patterns.cs:31:35:31:48 | $"..." | Patterns.cs:31:45:31:46 | access to local variable s2 |
+| Patterns.cs:31:35:31:48 | $"..." | Patterns.cs:31:44:31:47 | {...} |
| Patterns.cs:31:37:31:43 | "string " | Patterns.cs:31:17:31:50 | ...; |
+| Patterns.cs:31:44:31:47 | {...} | Patterns.cs:31:45:31:46 | access to local variable s2 |
| Patterns.cs:31:45:31:46 | access to local variable s2 | Patterns.cs:31:37:31:43 | "string " |
| Patterns.cs:32:17:32:22 | break; | Patterns.cs:31:17:31:49 | call to method WriteLine |
| Patterns.cs:33:18:33:23 | Object v2 | Patterns.cs:33:13:33:24 | case ...: |
@@ -7829,12 +7845,14 @@ postDominance
| Switch.cs:157:9:160:49 | if (...) ... | Switch.cs:156:13:156:54 | String s = ... |
| Switch.cs:157:13:157:13 | access to parameter b | Switch.cs:157:9:160:49 | if (...) ... |
| Switch.cs:158:13:158:48 | call to method WriteLine | Switch.cs:158:38:158:47 | $"..." |
-| Switch.cs:158:38:158:47 | $"..." | Switch.cs:158:45:158:45 | access to local variable s |
+| Switch.cs:158:38:158:47 | $"..." | Switch.cs:158:44:158:46 | {...} |
| Switch.cs:158:40:158:43 | "a = " | Switch.cs:158:13:158:49 | ...; |
+| Switch.cs:158:44:158:46 | {...} | Switch.cs:158:45:158:45 | access to local variable s |
| Switch.cs:158:45:158:45 | access to local variable s | Switch.cs:158:40:158:43 | "a = " |
| Switch.cs:160:13:160:48 | call to method WriteLine | Switch.cs:160:38:160:47 | $"..." |
-| Switch.cs:160:38:160:47 | $"..." | Switch.cs:160:45:160:45 | access to local variable s |
+| Switch.cs:160:38:160:47 | $"..." | Switch.cs:160:44:160:46 | {...} |
| Switch.cs:160:40:160:43 | "b = " | Switch.cs:160:13:160:49 | ...; |
+| Switch.cs:160:44:160:46 | {...} | Switch.cs:160:45:160:45 | access to local variable s |
| Switch.cs:160:45:160:45 | access to local variable s | Switch.cs:160:40:160:43 | "b = " |
| TypeAccesses.cs:1:7:1:18 | call to constructor Object | TypeAccesses.cs:1:7:1:18 | enter TypeAccesses |
| TypeAccesses.cs:1:7:1:18 | exit TypeAccesses | TypeAccesses.cs:1:7:1:18 | exit TypeAccesses (normal) |
diff --git a/csharp/ql/test/library-tests/controlflow/graph/EnclosingCallable.expected b/csharp/ql/test/library-tests/controlflow/graph/EnclosingCallable.expected
index 4f208361ea1a..57e393adc39a 100644
--- a/csharp/ql/test/library-tests/controlflow/graph/EnclosingCallable.expected
+++ b/csharp/ql/test/library-tests/controlflow/graph/EnclosingCallable.expected
@@ -1642,11 +1642,13 @@ nodeEnclosing
| Conditions.cs:147:13:147:49 | ...; | Conditions.cs:143:10:143:12 | M11 |
| Conditions.cs:147:38:147:47 | $"..." | Conditions.cs:143:10:143:12 | M11 |
| Conditions.cs:147:40:147:43 | "a = " | Conditions.cs:143:10:143:12 | M11 |
+| Conditions.cs:147:44:147:46 | {...} | Conditions.cs:143:10:143:12 | M11 |
| Conditions.cs:147:45:147:45 | access to local variable s | Conditions.cs:143:10:143:12 | M11 |
| Conditions.cs:149:13:149:48 | call to method WriteLine | Conditions.cs:143:10:143:12 | M11 |
| Conditions.cs:149:13:149:49 | ...; | Conditions.cs:143:10:143:12 | M11 |
| Conditions.cs:149:38:149:47 | $"..." | Conditions.cs:143:10:143:12 | M11 |
| Conditions.cs:149:40:149:43 | "b = " | Conditions.cs:143:10:143:12 | M11 |
+| Conditions.cs:149:44:149:46 | {...} | Conditions.cs:143:10:143:12 | M11 |
| Conditions.cs:149:45:149:45 | access to local variable s | Conditions.cs:143:10:143:12 | M11 |
| ExitMethods.cs:6:7:6:17 | call to constructor Object | ExitMethods.cs:6:7:6:17 | ExitMethods |
| ExitMethods.cs:6:7:6:17 | enter ExitMethods | ExitMethods.cs:6:7:6:17 | ExitMethods |
@@ -3436,6 +3438,7 @@ nodeEnclosing
| Patterns.cs:10:13:10:43 | ...; | Patterns.cs:5:10:5:11 | M1 |
| Patterns.cs:10:31:10:41 | $"..." | Patterns.cs:5:10:5:11 | M1 |
| Patterns.cs:10:33:10:36 | "int " | Patterns.cs:5:10:5:11 | M1 |
+| Patterns.cs:10:37:10:40 | {...} | Patterns.cs:5:10:5:11 | M1 |
| Patterns.cs:10:38:10:39 | access to local variable i1 | Patterns.cs:5:10:5:11 | M1 |
| Patterns.cs:12:14:18:9 | if (...) ... | Patterns.cs:5:10:5:11 | M1 |
| Patterns.cs:12:18:12:18 | access to local variable o | Patterns.cs:5:10:5:11 | M1 |
@@ -3447,6 +3450,7 @@ nodeEnclosing
| Patterns.cs:14:13:14:46 | ...; | Patterns.cs:5:10:5:11 | M1 |
| Patterns.cs:14:31:14:44 | $"..." | Patterns.cs:5:10:5:11 | M1 |
| Patterns.cs:14:33:14:39 | "string " | Patterns.cs:5:10:5:11 | M1 |
+| Patterns.cs:14:40:14:43 | {...} | Patterns.cs:5:10:5:11 | M1 |
| Patterns.cs:14:41:14:42 | access to local variable s1 | Patterns.cs:5:10:5:11 | M1 |
| Patterns.cs:16:14:18:9 | if (...) ... | Patterns.cs:5:10:5:11 | M1 |
| Patterns.cs:16:18:16:18 | access to local variable o | Patterns.cs:5:10:5:11 | M1 |
@@ -3468,6 +3472,7 @@ nodeEnclosing
| Patterns.cs:25:17:25:52 | ...; | Patterns.cs:5:10:5:11 | M1 |
| Patterns.cs:25:35:25:50 | $"..." | Patterns.cs:5:10:5:11 | M1 |
| Patterns.cs:25:37:25:45 | "positive " | Patterns.cs:5:10:5:11 | M1 |
+| Patterns.cs:25:46:25:49 | {...} | Patterns.cs:5:10:5:11 | M1 |
| Patterns.cs:25:47:25:48 | access to local variable i2 | Patterns.cs:5:10:5:11 | M1 |
| Patterns.cs:26:17:26:22 | break; | Patterns.cs:5:10:5:11 | M1 |
| Patterns.cs:27:13:27:24 | case ...: | Patterns.cs:5:10:5:11 | M1 |
@@ -3476,6 +3481,7 @@ nodeEnclosing
| Patterns.cs:28:17:28:47 | ...; | Patterns.cs:5:10:5:11 | M1 |
| Patterns.cs:28:35:28:45 | $"..." | Patterns.cs:5:10:5:11 | M1 |
| Patterns.cs:28:37:28:40 | "int " | Patterns.cs:5:10:5:11 | M1 |
+| Patterns.cs:28:41:28:44 | {...} | Patterns.cs:5:10:5:11 | M1 |
| Patterns.cs:28:42:28:43 | access to local variable i3 | Patterns.cs:5:10:5:11 | M1 |
| Patterns.cs:29:17:29:22 | break; | Patterns.cs:5:10:5:11 | M1 |
| Patterns.cs:30:13:30:27 | case ...: | Patterns.cs:5:10:5:11 | M1 |
@@ -3484,6 +3490,7 @@ nodeEnclosing
| Patterns.cs:31:17:31:50 | ...; | Patterns.cs:5:10:5:11 | M1 |
| Patterns.cs:31:35:31:48 | $"..." | Patterns.cs:5:10:5:11 | M1 |
| Patterns.cs:31:37:31:43 | "string " | Patterns.cs:5:10:5:11 | M1 |
+| Patterns.cs:31:44:31:47 | {...} | Patterns.cs:5:10:5:11 | M1 |
| Patterns.cs:31:45:31:46 | access to local variable s2 | Patterns.cs:5:10:5:11 | M1 |
| Patterns.cs:32:17:32:22 | break; | Patterns.cs:5:10:5:11 | M1 |
| Patterns.cs:33:13:33:24 | case ...: | Patterns.cs:5:10:5:11 | M1 |
@@ -4014,11 +4021,13 @@ nodeEnclosing
| Switch.cs:158:13:158:49 | ...; | Switch.cs:154:10:154:12 | M15 |
| Switch.cs:158:38:158:47 | $"..." | Switch.cs:154:10:154:12 | M15 |
| Switch.cs:158:40:158:43 | "a = " | Switch.cs:154:10:154:12 | M15 |
+| Switch.cs:158:44:158:46 | {...} | Switch.cs:154:10:154:12 | M15 |
| Switch.cs:158:45:158:45 | access to local variable s | Switch.cs:154:10:154:12 | M15 |
| Switch.cs:160:13:160:48 | call to method WriteLine | Switch.cs:154:10:154:12 | M15 |
| Switch.cs:160:13:160:49 | ...; | Switch.cs:154:10:154:12 | M15 |
| Switch.cs:160:38:160:47 | $"..." | Switch.cs:154:10:154:12 | M15 |
| Switch.cs:160:40:160:43 | "b = " | Switch.cs:154:10:154:12 | M15 |
+| Switch.cs:160:44:160:46 | {...} | Switch.cs:154:10:154:12 | M15 |
| Switch.cs:160:45:160:45 | access to local variable s | Switch.cs:154:10:154:12 | M15 |
| TypeAccesses.cs:1:7:1:18 | call to constructor Object | TypeAccesses.cs:1:7:1:18 | TypeAccesses |
| TypeAccesses.cs:1:7:1:18 | enter TypeAccesses | TypeAccesses.cs:1:7:1:18 | TypeAccesses |
diff --git a/csharp/ql/test/library-tests/controlflow/graph/EntryElement.expected b/csharp/ql/test/library-tests/controlflow/graph/EntryElement.expected
index fddab21f2b62..cc26ecf7c8ed 100644
--- a/csharp/ql/test/library-tests/controlflow/graph/EntryElement.expected
+++ b/csharp/ql/test/library-tests/controlflow/graph/EntryElement.expected
@@ -1158,11 +1158,13 @@
| Conditions.cs:147:13:147:49 | ...; | Conditions.cs:147:13:147:49 | ...; |
| Conditions.cs:147:38:147:47 | $"..." | Conditions.cs:147:40:147:43 | "a = " |
| Conditions.cs:147:40:147:43 | "a = " | Conditions.cs:147:40:147:43 | "a = " |
+| Conditions.cs:147:44:147:46 | {...} | Conditions.cs:147:45:147:45 | access to local variable s |
| Conditions.cs:147:45:147:45 | access to local variable s | Conditions.cs:147:45:147:45 | access to local variable s |
| Conditions.cs:149:13:149:48 | call to method WriteLine | Conditions.cs:149:40:149:43 | "b = " |
| Conditions.cs:149:13:149:49 | ...; | Conditions.cs:149:13:149:49 | ...; |
| Conditions.cs:149:38:149:47 | $"..." | Conditions.cs:149:40:149:43 | "b = " |
| Conditions.cs:149:40:149:43 | "b = " | Conditions.cs:149:40:149:43 | "b = " |
+| Conditions.cs:149:44:149:46 | {...} | Conditions.cs:149:45:149:45 | access to local variable s |
| Conditions.cs:149:45:149:45 | access to local variable s | Conditions.cs:149:45:149:45 | access to local variable s |
| ExitMethods.cs:6:7:6:17 | call to constructor Object | ExitMethods.cs:6:7:6:17 | call to constructor Object |
| ExitMethods.cs:6:7:6:17 | {...} | ExitMethods.cs:6:7:6:17 | {...} |
@@ -2238,6 +2240,7 @@
| Patterns.cs:10:13:10:43 | ...; | Patterns.cs:10:13:10:43 | ...; |
| Patterns.cs:10:31:10:41 | $"..." | Patterns.cs:10:33:10:36 | "int " |
| Patterns.cs:10:33:10:36 | "int " | Patterns.cs:10:33:10:36 | "int " |
+| Patterns.cs:10:37:10:40 | {...} | Patterns.cs:10:38:10:39 | access to local variable i1 |
| Patterns.cs:10:38:10:39 | access to local variable i1 | Patterns.cs:10:38:10:39 | access to local variable i1 |
| Patterns.cs:12:14:18:9 | if (...) ... | Patterns.cs:12:14:18:9 | if (...) ... |
| Patterns.cs:12:18:12:18 | access to local variable o | Patterns.cs:12:18:12:18 | access to local variable o |
@@ -2248,6 +2251,7 @@
| Patterns.cs:14:13:14:46 | ...; | Patterns.cs:14:13:14:46 | ...; |
| Patterns.cs:14:31:14:44 | $"..." | Patterns.cs:14:33:14:39 | "string " |
| Patterns.cs:14:33:14:39 | "string " | Patterns.cs:14:33:14:39 | "string " |
+| Patterns.cs:14:40:14:43 | {...} | Patterns.cs:14:41:14:42 | access to local variable s1 |
| Patterns.cs:14:41:14:42 | access to local variable s1 | Patterns.cs:14:41:14:42 | access to local variable s1 |
| Patterns.cs:16:14:18:9 | if (...) ... | Patterns.cs:16:14:18:9 | if (...) ... |
| Patterns.cs:16:18:16:18 | access to local variable o | Patterns.cs:16:18:16:18 | access to local variable o |
@@ -2268,6 +2272,7 @@
| Patterns.cs:25:17:25:52 | ...; | Patterns.cs:25:17:25:52 | ...; |
| Patterns.cs:25:35:25:50 | $"..." | Patterns.cs:25:37:25:45 | "positive " |
| Patterns.cs:25:37:25:45 | "positive " | Patterns.cs:25:37:25:45 | "positive " |
+| Patterns.cs:25:46:25:49 | {...} | Patterns.cs:25:47:25:48 | access to local variable i2 |
| Patterns.cs:25:47:25:48 | access to local variable i2 | Patterns.cs:25:47:25:48 | access to local variable i2 |
| Patterns.cs:26:17:26:22 | break; | Patterns.cs:26:17:26:22 | break; |
| Patterns.cs:27:13:27:24 | case ...: | Patterns.cs:27:13:27:24 | case ...: |
@@ -2276,6 +2281,7 @@
| Patterns.cs:28:17:28:47 | ...; | Patterns.cs:28:17:28:47 | ...; |
| Patterns.cs:28:35:28:45 | $"..." | Patterns.cs:28:37:28:40 | "int " |
| Patterns.cs:28:37:28:40 | "int " | Patterns.cs:28:37:28:40 | "int " |
+| Patterns.cs:28:41:28:44 | {...} | Patterns.cs:28:42:28:43 | access to local variable i3 |
| Patterns.cs:28:42:28:43 | access to local variable i3 | Patterns.cs:28:42:28:43 | access to local variable i3 |
| Patterns.cs:29:17:29:22 | break; | Patterns.cs:29:17:29:22 | break; |
| Patterns.cs:30:13:30:27 | case ...: | Patterns.cs:30:13:30:27 | case ...: |
@@ -2284,6 +2290,7 @@
| Patterns.cs:31:17:31:50 | ...; | Patterns.cs:31:17:31:50 | ...; |
| Patterns.cs:31:35:31:48 | $"..." | Patterns.cs:31:37:31:43 | "string " |
| Patterns.cs:31:37:31:43 | "string " | Patterns.cs:31:37:31:43 | "string " |
+| Patterns.cs:31:44:31:47 | {...} | Patterns.cs:31:45:31:46 | access to local variable s2 |
| Patterns.cs:31:45:31:46 | access to local variable s2 | Patterns.cs:31:45:31:46 | access to local variable s2 |
| Patterns.cs:32:17:32:22 | break; | Patterns.cs:32:17:32:22 | break; |
| Patterns.cs:33:13:33:24 | case ...: | Patterns.cs:33:13:33:24 | case ...: |
@@ -2696,11 +2703,13 @@
| Switch.cs:158:13:158:49 | ...; | Switch.cs:158:13:158:49 | ...; |
| Switch.cs:158:38:158:47 | $"..." | Switch.cs:158:40:158:43 | "a = " |
| Switch.cs:158:40:158:43 | "a = " | Switch.cs:158:40:158:43 | "a = " |
+| Switch.cs:158:44:158:46 | {...} | Switch.cs:158:45:158:45 | access to local variable s |
| Switch.cs:158:45:158:45 | access to local variable s | Switch.cs:158:45:158:45 | access to local variable s |
| Switch.cs:160:13:160:48 | call to method WriteLine | Switch.cs:160:40:160:43 | "b = " |
| Switch.cs:160:13:160:49 | ...; | Switch.cs:160:13:160:49 | ...; |
| Switch.cs:160:38:160:47 | $"..." | Switch.cs:160:40:160:43 | "b = " |
| Switch.cs:160:40:160:43 | "b = " | Switch.cs:160:40:160:43 | "b = " |
+| Switch.cs:160:44:160:46 | {...} | Switch.cs:160:45:160:45 | access to local variable s |
| Switch.cs:160:45:160:45 | access to local variable s | Switch.cs:160:45:160:45 | access to local variable s |
| TypeAccesses.cs:1:7:1:18 | call to constructor Object | TypeAccesses.cs:1:7:1:18 | call to constructor Object |
| TypeAccesses.cs:1:7:1:18 | {...} | TypeAccesses.cs:1:7:1:18 | {...} |
diff --git a/csharp/ql/test/library-tests/controlflow/graph/ExitElement.expected b/csharp/ql/test/library-tests/controlflow/graph/ExitElement.expected
index 81ee139f6a68..bc47b5b3fa26 100644
--- a/csharp/ql/test/library-tests/controlflow/graph/ExitElement.expected
+++ b/csharp/ql/test/library-tests/controlflow/graph/ExitElement.expected
@@ -1452,11 +1452,13 @@
| Conditions.cs:147:13:147:49 | ...; | Conditions.cs:147:13:147:48 | call to method WriteLine | normal |
| Conditions.cs:147:38:147:47 | $"..." | Conditions.cs:147:38:147:47 | $"..." | normal |
| Conditions.cs:147:40:147:43 | "a = " | Conditions.cs:147:40:147:43 | "a = " | normal |
+| Conditions.cs:147:44:147:46 | {...} | Conditions.cs:147:44:147:46 | {...} | normal |
| Conditions.cs:147:45:147:45 | access to local variable s | Conditions.cs:147:45:147:45 | access to local variable s | normal |
| Conditions.cs:149:13:149:48 | call to method WriteLine | Conditions.cs:149:13:149:48 | call to method WriteLine | normal |
| Conditions.cs:149:13:149:49 | ...; | Conditions.cs:149:13:149:48 | call to method WriteLine | normal |
| Conditions.cs:149:38:149:47 | $"..." | Conditions.cs:149:38:149:47 | $"..." | normal |
| Conditions.cs:149:40:149:43 | "b = " | Conditions.cs:149:40:149:43 | "b = " | normal |
+| Conditions.cs:149:44:149:46 | {...} | Conditions.cs:149:44:149:46 | {...} | normal |
| Conditions.cs:149:45:149:45 | access to local variable s | Conditions.cs:149:45:149:45 | access to local variable s | normal |
| ExitMethods.cs:6:7:6:17 | call to constructor Object | ExitMethods.cs:6:7:6:17 | call to constructor Object | normal |
| ExitMethods.cs:6:7:6:17 | {...} | ExitMethods.cs:6:7:6:17 | {...} | normal |
@@ -2911,6 +2913,7 @@
| Patterns.cs:10:13:10:43 | ...; | Patterns.cs:10:13:10:42 | call to method WriteLine | normal |
| Patterns.cs:10:31:10:41 | $"..." | Patterns.cs:10:31:10:41 | $"..." | normal |
| Patterns.cs:10:33:10:36 | "int " | Patterns.cs:10:33:10:36 | "int " | normal |
+| Patterns.cs:10:37:10:40 | {...} | Patterns.cs:10:37:10:40 | {...} | normal |
| Patterns.cs:10:38:10:39 | access to local variable i1 | Patterns.cs:10:38:10:39 | access to local variable i1 | normal |
| Patterns.cs:12:14:18:9 | if (...) ... | Patterns.cs:14:13:14:45 | call to method WriteLine | normal |
| Patterns.cs:12:14:18:9 | if (...) ... | Patterns.cs:16:18:16:28 | ... is ... | false |
@@ -2925,6 +2928,7 @@
| Patterns.cs:14:13:14:46 | ...; | Patterns.cs:14:13:14:45 | call to method WriteLine | normal |
| Patterns.cs:14:31:14:44 | $"..." | Patterns.cs:14:31:14:44 | $"..." | normal |
| Patterns.cs:14:33:14:39 | "string " | Patterns.cs:14:33:14:39 | "string " | normal |
+| Patterns.cs:14:40:14:43 | {...} | Patterns.cs:14:40:14:43 | {...} | normal |
| Patterns.cs:14:41:14:42 | access to local variable s1 | Patterns.cs:14:41:14:42 | access to local variable s1 | normal |
| Patterns.cs:16:14:18:9 | if (...) ... | Patterns.cs:16:18:16:28 | ... is ... | false |
| Patterns.cs:16:14:18:9 | if (...) ... | Patterns.cs:17:9:18:9 | {...} | normal |
@@ -2959,6 +2963,7 @@
| Patterns.cs:25:17:25:52 | ...; | Patterns.cs:25:17:25:51 | call to method WriteLine | normal |
| Patterns.cs:25:35:25:50 | $"..." | Patterns.cs:25:35:25:50 | $"..." | normal |
| Patterns.cs:25:37:25:45 | "positive " | Patterns.cs:25:37:25:45 | "positive " | normal |
+| Patterns.cs:25:46:25:49 | {...} | Patterns.cs:25:46:25:49 | {...} | normal |
| Patterns.cs:25:47:25:48 | access to local variable i2 | Patterns.cs:25:47:25:48 | access to local variable i2 | normal |
| Patterns.cs:26:17:26:22 | break; | Patterns.cs:26:17:26:22 | break; | break |
| Patterns.cs:27:13:27:24 | case ...: | Patterns.cs:27:18:27:23 | Int32 i3 | no-match |
@@ -2969,6 +2974,7 @@
| Patterns.cs:28:17:28:47 | ...; | Patterns.cs:28:17:28:46 | call to method WriteLine | normal |
| Patterns.cs:28:35:28:45 | $"..." | Patterns.cs:28:35:28:45 | $"..." | normal |
| Patterns.cs:28:37:28:40 | "int " | Patterns.cs:28:37:28:40 | "int " | normal |
+| Patterns.cs:28:41:28:44 | {...} | Patterns.cs:28:41:28:44 | {...} | normal |
| Patterns.cs:28:42:28:43 | access to local variable i3 | Patterns.cs:28:42:28:43 | access to local variable i3 | normal |
| Patterns.cs:29:17:29:22 | break; | Patterns.cs:29:17:29:22 | break; | break |
| Patterns.cs:30:13:30:27 | case ...: | Patterns.cs:30:18:30:26 | String s2 | no-match |
@@ -2979,6 +2985,7 @@
| Patterns.cs:31:17:31:50 | ...; | Patterns.cs:31:17:31:49 | call to method WriteLine | normal |
| Patterns.cs:31:35:31:48 | $"..." | Patterns.cs:31:35:31:48 | $"..." | normal |
| Patterns.cs:31:37:31:43 | "string " | Patterns.cs:31:37:31:43 | "string " | normal |
+| Patterns.cs:31:44:31:47 | {...} | Patterns.cs:31:44:31:47 | {...} | normal |
| Patterns.cs:31:45:31:46 | access to local variable s2 | Patterns.cs:31:45:31:46 | access to local variable s2 | normal |
| Patterns.cs:32:17:32:22 | break; | Patterns.cs:32:17:32:22 | break; | break |
| Patterns.cs:33:13:33:24 | case ...: | Patterns.cs:33:18:33:23 | Object v2 | no-match |
@@ -3566,11 +3573,13 @@
| Switch.cs:158:13:158:49 | ...; | Switch.cs:158:13:158:48 | call to method WriteLine | normal |
| Switch.cs:158:38:158:47 | $"..." | Switch.cs:158:38:158:47 | $"..." | normal |
| Switch.cs:158:40:158:43 | "a = " | Switch.cs:158:40:158:43 | "a = " | normal |
+| Switch.cs:158:44:158:46 | {...} | Switch.cs:158:44:158:46 | {...} | normal |
| Switch.cs:158:45:158:45 | access to local variable s | Switch.cs:158:45:158:45 | access to local variable s | normal |
| Switch.cs:160:13:160:48 | call to method WriteLine | Switch.cs:160:13:160:48 | call to method WriteLine | normal |
| Switch.cs:160:13:160:49 | ...; | Switch.cs:160:13:160:48 | call to method WriteLine | normal |
| Switch.cs:160:38:160:47 | $"..." | Switch.cs:160:38:160:47 | $"..." | normal |
| Switch.cs:160:40:160:43 | "b = " | Switch.cs:160:40:160:43 | "b = " | normal |
+| Switch.cs:160:44:160:46 | {...} | Switch.cs:160:44:160:46 | {...} | normal |
| Switch.cs:160:45:160:45 | access to local variable s | Switch.cs:160:45:160:45 | access to local variable s | normal |
| TypeAccesses.cs:1:7:1:18 | call to constructor Object | TypeAccesses.cs:1:7:1:18 | call to constructor Object | normal |
| TypeAccesses.cs:1:7:1:18 | {...} | TypeAccesses.cs:1:7:1:18 | {...} | normal |
diff --git a/csharp/ql/test/library-tests/controlflow/graph/NodeGraph.expected b/csharp/ql/test/library-tests/controlflow/graph/NodeGraph.expected
index 0ae9a8a1e35c..5b3860e04ede 100644
--- a/csharp/ql/test/library-tests/controlflow/graph/NodeGraph.expected
+++ b/csharp/ql/test/library-tests/controlflow/graph/NodeGraph.expected
@@ -1679,12 +1679,14 @@
| Conditions.cs:147:13:147:49 | ...; | Conditions.cs:147:40:147:43 | "a = " | |
| Conditions.cs:147:38:147:47 | $"..." | Conditions.cs:147:13:147:48 | call to method WriteLine | |
| Conditions.cs:147:40:147:43 | "a = " | Conditions.cs:147:45:147:45 | access to local variable s | |
-| Conditions.cs:147:45:147:45 | access to local variable s | Conditions.cs:147:38:147:47 | $"..." | |
+| Conditions.cs:147:44:147:46 | {...} | Conditions.cs:147:38:147:47 | $"..." | |
+| Conditions.cs:147:45:147:45 | access to local variable s | Conditions.cs:147:44:147:46 | {...} | |
| Conditions.cs:149:13:149:48 | call to method WriteLine | Conditions.cs:143:10:143:12 | exit M11 (normal) | |
| Conditions.cs:149:13:149:49 | ...; | Conditions.cs:149:40:149:43 | "b = " | |
| Conditions.cs:149:38:149:47 | $"..." | Conditions.cs:149:13:149:48 | call to method WriteLine | |
| Conditions.cs:149:40:149:43 | "b = " | Conditions.cs:149:45:149:45 | access to local variable s | |
-| Conditions.cs:149:45:149:45 | access to local variable s | Conditions.cs:149:38:149:47 | $"..." | |
+| Conditions.cs:149:44:149:46 | {...} | Conditions.cs:149:38:149:47 | $"..." | |
+| Conditions.cs:149:45:149:45 | access to local variable s | Conditions.cs:149:44:149:46 | {...} | |
| ExitMethods.cs:6:7:6:17 | call to constructor Object | ExitMethods.cs:6:7:6:17 | {...} | |
| ExitMethods.cs:6:7:6:17 | enter ExitMethods | ExitMethods.cs:6:7:6:17 | call to constructor Object | |
| ExitMethods.cs:6:7:6:17 | exit ExitMethods (normal) | ExitMethods.cs:6:7:6:17 | exit ExitMethods | |
@@ -3528,7 +3530,8 @@
| Patterns.cs:10:13:10:43 | ...; | Patterns.cs:10:33:10:36 | "int " | |
| Patterns.cs:10:31:10:41 | $"..." | Patterns.cs:10:13:10:42 | call to method WriteLine | |
| Patterns.cs:10:33:10:36 | "int " | Patterns.cs:10:38:10:39 | access to local variable i1 | |
-| Patterns.cs:10:38:10:39 | access to local variable i1 | Patterns.cs:10:31:10:41 | $"..." | |
+| Patterns.cs:10:37:10:40 | {...} | Patterns.cs:10:31:10:41 | $"..." | |
+| Patterns.cs:10:38:10:39 | access to local variable i1 | Patterns.cs:10:37:10:40 | {...} | |
| Patterns.cs:12:14:18:9 | if (...) ... | Patterns.cs:12:18:12:18 | access to local variable o | |
| Patterns.cs:12:18:12:18 | access to local variable o | Patterns.cs:12:23:12:31 | String s1 | |
| Patterns.cs:12:18:12:31 | [false] ... is ... | Patterns.cs:16:14:18:9 | if (...) ... | false |
@@ -3540,7 +3543,8 @@
| Patterns.cs:14:13:14:46 | ...; | Patterns.cs:14:33:14:39 | "string " | |
| Patterns.cs:14:31:14:44 | $"..." | Patterns.cs:14:13:14:45 | call to method WriteLine | |
| Patterns.cs:14:33:14:39 | "string " | Patterns.cs:14:41:14:42 | access to local variable s1 | |
-| Patterns.cs:14:41:14:42 | access to local variable s1 | Patterns.cs:14:31:14:44 | $"..." | |
+| Patterns.cs:14:40:14:43 | {...} | Patterns.cs:14:31:14:44 | $"..." | |
+| Patterns.cs:14:41:14:42 | access to local variable s1 | Patterns.cs:14:40:14:43 | {...} | |
| Patterns.cs:16:14:18:9 | if (...) ... | Patterns.cs:16:18:16:18 | access to local variable o | |
| Patterns.cs:16:18:16:18 | access to local variable o | Patterns.cs:16:23:16:28 | Object v1 | |
| Patterns.cs:16:18:16:28 | [false] ... is ... | Patterns.cs:20:9:38:9 | switch (...) {...} | false |
@@ -3565,7 +3569,8 @@
| Patterns.cs:25:17:25:52 | ...; | Patterns.cs:25:37:25:45 | "positive " | |
| Patterns.cs:25:35:25:50 | $"..." | Patterns.cs:25:17:25:51 | call to method WriteLine | |
| Patterns.cs:25:37:25:45 | "positive " | Patterns.cs:25:47:25:48 | access to local variable i2 | |
-| Patterns.cs:25:47:25:48 | access to local variable i2 | Patterns.cs:25:35:25:50 | $"..." | |
+| Patterns.cs:25:46:25:49 | {...} | Patterns.cs:25:35:25:50 | $"..." | |
+| Patterns.cs:25:47:25:48 | access to local variable i2 | Patterns.cs:25:46:25:49 | {...} | |
| Patterns.cs:26:17:26:22 | break; | Patterns.cs:40:9:42:9 | switch (...) {...} | break |
| Patterns.cs:27:13:27:24 | case ...: | Patterns.cs:27:18:27:23 | Int32 i3 | |
| Patterns.cs:27:18:27:23 | Int32 i3 | Patterns.cs:28:17:28:47 | ...; | match |
@@ -3574,7 +3579,8 @@
| Patterns.cs:28:17:28:47 | ...; | Patterns.cs:28:37:28:40 | "int " | |
| Patterns.cs:28:35:28:45 | $"..." | Patterns.cs:28:17:28:46 | call to method WriteLine | |
| Patterns.cs:28:37:28:40 | "int " | Patterns.cs:28:42:28:43 | access to local variable i3 | |
-| Patterns.cs:28:42:28:43 | access to local variable i3 | Patterns.cs:28:35:28:45 | $"..." | |
+| Patterns.cs:28:41:28:44 | {...} | Patterns.cs:28:35:28:45 | $"..." | |
+| Patterns.cs:28:42:28:43 | access to local variable i3 | Patterns.cs:28:41:28:44 | {...} | |
| Patterns.cs:29:17:29:22 | break; | Patterns.cs:40:9:42:9 | switch (...) {...} | break |
| Patterns.cs:30:13:30:27 | case ...: | Patterns.cs:30:18:30:26 | String s2 | |
| Patterns.cs:30:18:30:26 | String s2 | Patterns.cs:31:17:31:50 | ...; | match |
@@ -3583,7 +3589,8 @@
| Patterns.cs:31:17:31:50 | ...; | Patterns.cs:31:37:31:43 | "string " | |
| Patterns.cs:31:35:31:48 | $"..." | Patterns.cs:31:17:31:49 | call to method WriteLine | |
| Patterns.cs:31:37:31:43 | "string " | Patterns.cs:31:45:31:46 | access to local variable s2 | |
-| Patterns.cs:31:45:31:46 | access to local variable s2 | Patterns.cs:31:35:31:48 | $"..." | |
+| Patterns.cs:31:44:31:47 | {...} | Patterns.cs:31:35:31:48 | $"..." | |
+| Patterns.cs:31:45:31:46 | access to local variable s2 | Patterns.cs:31:44:31:47 | {...} | |
| Patterns.cs:32:17:32:22 | break; | Patterns.cs:40:9:42:9 | switch (...) {...} | break |
| Patterns.cs:33:13:33:24 | case ...: | Patterns.cs:33:18:33:23 | Object v2 | |
| Patterns.cs:33:18:33:23 | Object v2 | Patterns.cs:34:17:34:22 | break; | match |
@@ -4130,12 +4137,14 @@
| Switch.cs:158:13:158:49 | ...; | Switch.cs:158:40:158:43 | "a = " | |
| Switch.cs:158:38:158:47 | $"..." | Switch.cs:158:13:158:48 | call to method WriteLine | |
| Switch.cs:158:40:158:43 | "a = " | Switch.cs:158:45:158:45 | access to local variable s | |
-| Switch.cs:158:45:158:45 | access to local variable s | Switch.cs:158:38:158:47 | $"..." | |
+| Switch.cs:158:44:158:46 | {...} | Switch.cs:158:38:158:47 | $"..." | |
+| Switch.cs:158:45:158:45 | access to local variable s | Switch.cs:158:44:158:46 | {...} | |
| Switch.cs:160:13:160:48 | call to method WriteLine | Switch.cs:154:10:154:12 | exit M15 (normal) | |
| Switch.cs:160:13:160:49 | ...; | Switch.cs:160:40:160:43 | "b = " | |
| Switch.cs:160:38:160:47 | $"..." | Switch.cs:160:13:160:48 | call to method WriteLine | |
| Switch.cs:160:40:160:43 | "b = " | Switch.cs:160:45:160:45 | access to local variable s | |
-| Switch.cs:160:45:160:45 | access to local variable s | Switch.cs:160:38:160:47 | $"..." | |
+| Switch.cs:160:44:160:46 | {...} | Switch.cs:160:38:160:47 | $"..." | |
+| Switch.cs:160:45:160:45 | access to local variable s | Switch.cs:160:44:160:46 | {...} | |
| TypeAccesses.cs:1:7:1:18 | call to constructor Object | TypeAccesses.cs:1:7:1:18 | {...} | |
| TypeAccesses.cs:1:7:1:18 | enter TypeAccesses | TypeAccesses.cs:1:7:1:18 | call to constructor Object | |
| TypeAccesses.cs:1:7:1:18 | exit TypeAccesses (normal) | TypeAccesses.cs:1:7:1:18 | exit TypeAccesses | |
diff --git a/csharp/ql/test/library-tests/csharp11/PrintAst.expected b/csharp/ql/test/library-tests/csharp11/PrintAst.expected
index b10e2096489b..3a3f44974231 100644
--- a/csharp/ql/test/library-tests/csharp11/PrintAst.expected
+++ b/csharp/ql/test/library-tests/csharp11/PrintAst.expected
@@ -1332,14 +1332,15 @@ Strings.cs:
# 8| 0: [ReturnStmt] return ...;
# 8| 0: [InterpolatedStringExpr] $"..."
# 8| 0: [StringLiteralUtf16] "This is my int "
-# 8| 1: [SwitchExpr] ... switch { ... }
-# 8| -1: [ParameterAccess] access to parameter x
-# 10| 0: [SwitchCaseExpr] ... => ...
-# 10| 0: [ConstantPatternExpr,IntLiteral] 42
-# 10| 2: [StringLiteralUtf16] "forty two"
-# 11| 1: [SwitchCaseExpr] ... => ...
-# 11| 0: [DiscardPatternExpr] _
-# 11| 2: [StringLiteralUtf16] "something else"
+# 8| 1: [InterpolatedStringInsertExpr] {...}
+# 8| 0: [SwitchExpr] ... switch { ... }
+# 8| -1: [ParameterAccess] access to parameter x
+# 10| 0: [SwitchCaseExpr] ... => ...
+# 10| 0: [ConstantPatternExpr,IntLiteral] 42
+# 10| 2: [StringLiteralUtf16] "forty two"
+# 11| 1: [SwitchCaseExpr] ... => ...
+# 11| 0: [DiscardPatternExpr] _
+# 11| 2: [StringLiteralUtf16] "something else"
# 12| 2: [StringLiteralUtf16] "."
# 15| 6: [Method] M2
# 15| -1: [TypeMention] Void
@@ -1359,7 +1360,8 @@ Strings.cs:
# 26| 1: [InterpolatedStringExpr] $"..."
# 27| 0: [StringLiteralUtf16] "The nested message
# 27| is \""
-# 28| 1: [LocalVariableAccess] access to local variable message1
+# 28| 1: [InterpolatedStringInsertExpr] {...}
+# 28| 0: [LocalVariableAccess] access to local variable message1
# 28| 2: [StringLiteralUtf16] "\" and everything
# 28| spans multiple lines."
# 33| 2: [LocalVariableDeclStmt] ... ...;
@@ -1368,10 +1370,12 @@ Strings.cs:
# 33| 0: [LocalVariableAccess] access to local variable message3
# 33| 1: [InterpolatedStringExpr] $"..."
# 34| 0: [StringLiteralUtf16] "Show no curly braces: "
-# 34| 1: [LocalVariableAccess] access to local variable message1
+# 34| 1: [InterpolatedStringInsertExpr] {...}
+# 34| 0: [LocalVariableAccess] access to local variable message1
# 34| 2: [StringLiteralUtf16] "
# 34| Show matching set of curly braces: {"
-# 35| 3: [LocalVariableAccess] access to local variable message2
+# 35| 3: [InterpolatedStringInsertExpr] {...}
+# 35| 0: [LocalVariableAccess] access to local variable message2
# 35| 4: [StringLiteralUtf16] "}"
# 40| 7: [Method] M3
# 40| -1: [TypeMention] Void
diff --git a/csharp/ql/test/library-tests/csharp6/InterpolatedStringExpr.expected b/csharp/ql/test/library-tests/csharp6/InterpolatedStringExpr.expected
index d10365546bd9..a7c904f56867 100644
--- a/csharp/ql/test/library-tests/csharp6/InterpolatedStringExpr.expected
+++ b/csharp/ql/test/library-tests/csharp6/InterpolatedStringExpr.expected
@@ -1,14 +1,14 @@
-| csharp6.cs:25:23:25:96 | $"..." | csharp6.cs:25:26:25:36 | nameof(...) |
+| csharp6.cs:25:23:25:96 | $"..." | csharp6.cs:25:25:25:37 | {...} |
| csharp6.cs:25:23:25:96 | $"..." | csharp6.cs:25:38:25:41 | " is " |
-| csharp6.cs:25:23:25:96 | $"..." | csharp6.cs:25:43:25:45 | access to local variable foo |
+| csharp6.cs:25:23:25:96 | $"..." | csharp6.cs:25:42:25:46 | {...} |
| csharp6.cs:25:23:25:96 | $"..." | csharp6.cs:25:47:25:52 | ", and " |
-| csharp6.cs:25:23:25:96 | $"..." | csharp6.cs:25:54:25:64 | nameof(...) |
+| csharp6.cs:25:23:25:96 | $"..." | csharp6.cs:25:53:25:65 | {...} |
| csharp6.cs:25:23:25:96 | $"..." | csharp6.cs:25:66:25:77 | " has length " |
-| csharp6.cs:25:23:25:96 | $"..." | csharp6.cs:25:79:25:94 | ... ?? ... |
-| csharp6.cs:27:16:27:90 | $"..." | csharp6.cs:27:20:27:30 | nameof(...) |
+| csharp6.cs:25:23:25:96 | $"..." | csharp6.cs:25:78:25:95 | {...} |
+| csharp6.cs:27:16:27:90 | $"..." | csharp6.cs:27:19:27:31 | {...} |
| csharp6.cs:27:16:27:90 | $"..." | csharp6.cs:27:32:27:35 | " is " |
-| csharp6.cs:27:16:27:90 | $"..." | csharp6.cs:27:37:27:39 | access to local variable foo |
+| csharp6.cs:27:16:27:90 | $"..." | csharp6.cs:27:36:27:40 | {...} |
| csharp6.cs:27:16:27:90 | $"..." | csharp6.cs:27:41:27:46 | ", and " |
-| csharp6.cs:27:16:27:90 | $"..." | csharp6.cs:27:48:27:58 | nameof(...) |
+| csharp6.cs:27:16:27:90 | $"..." | csharp6.cs:27:47:27:59 | {...} |
| csharp6.cs:27:16:27:90 | $"..." | csharp6.cs:27:60:27:71 | " has length " |
-| csharp6.cs:27:16:27:90 | $"..." | csharp6.cs:27:73:27:88 | ... ?? ... |
+| csharp6.cs:27:16:27:90 | $"..." | csharp6.cs:27:72:27:89 | {...} |
diff --git a/csharp/ql/test/library-tests/csharp6/PrintAst.expected b/csharp/ql/test/library-tests/csharp6/PrintAst.expected
index 892ad6dd4b15..424a18bcb023 100644
--- a/csharp/ql/test/library-tests/csharp6/PrintAst.expected
+++ b/csharp/ql/test/library-tests/csharp6/PrintAst.expected
@@ -30,33 +30,41 @@ csharp6.cs:
# 25| 1: [ExprStmt] ...;
# 25| 0: [MethodCall] call to method WriteLine
# 25| 0: [InterpolatedStringExpr] $"..."
-# 25| 0: [NameOfExpr] nameof(...)
-# 25| 0: [LocalVariableAccess] access to local variable foo
+# 25| 0: [InterpolatedStringInsertExpr] {...}
+# 25| 0: [NameOfExpr] nameof(...)
+# 25| 0: [LocalVariableAccess] access to local variable foo
# 25| 1: [StringLiteralUtf16] " is "
-# 25| 2: [LocalVariableAccess] access to local variable foo
+# 25| 2: [InterpolatedStringInsertExpr] {...}
+# 25| 0: [LocalVariableAccess] access to local variable foo
# 25| 3: [StringLiteralUtf16] ", and "
-# 25| 4: [NameOfExpr] nameof(...)
-# 25| 0: [LocalVariableAccess] access to local variable bar
+# 25| 4: [InterpolatedStringInsertExpr] {...}
+# 25| 0: [NameOfExpr] nameof(...)
+# 25| 0: [LocalVariableAccess] access to local variable bar
# 25| 5: [StringLiteralUtf16] " has length "
-# 25| 6: [NullCoalescingExpr] ... ?? ...
-# 25| 0: [PropertyCall] access to property Length
-# 25| -1: [LocalVariableAccess] access to local variable bar
-# 25| 1: [IntLiteral] 0
+# 25| 6: [InterpolatedStringInsertExpr] {...}
+# 25| 0: [NullCoalescingExpr] ... ?? ...
+# 25| 0: [PropertyCall] access to property Length
+# 25| -1: [LocalVariableAccess] access to local variable bar
+# 25| 1: [IntLiteral] 0
# 27| 2: [ExprStmt] ...;
# 27| 0: [MethodCall] call to method Fn
# 27| 0: [InterpolatedStringExpr] $"..."
-# 27| 0: [NameOfExpr] nameof(...)
-# 27| 0: [LocalVariableAccess] access to local variable foo
+# 27| 0: [InterpolatedStringInsertExpr] {...}
+# 27| 0: [NameOfExpr] nameof(...)
+# 27| 0: [LocalVariableAccess] access to local variable foo
# 27| 1: [StringLiteralUtf16] " is "
-# 27| 2: [LocalVariableAccess] access to local variable foo
+# 27| 2: [InterpolatedStringInsertExpr] {...}
+# 27| 0: [LocalVariableAccess] access to local variable foo
# 27| 3: [StringLiteralUtf16] ", and "
-# 27| 4: [NameOfExpr] nameof(...)
-# 27| 0: [LocalVariableAccess] access to local variable bar
+# 27| 4: [InterpolatedStringInsertExpr] {...}
+# 27| 0: [NameOfExpr] nameof(...)
+# 27| 0: [LocalVariableAccess] access to local variable bar
# 27| 5: [StringLiteralUtf16] " has length "
-# 27| 6: [NullCoalescingExpr] ... ?? ...
-# 27| 0: [PropertyCall] access to property Length
-# 27| -1: [LocalVariableAccess] access to local variable bar
-# 27| 1: [IntLiteral] 0
+# 27| 6: [InterpolatedStringInsertExpr] {...}
+# 27| 0: [NullCoalescingExpr] ... ?? ...
+# 27| 0: [PropertyCall] access to property Length
+# 27| -1: [LocalVariableAccess] access to local variable bar
+# 27| 1: [IntLiteral] 0
# 29| 3: [LocalVariableDeclStmt] ... ...;
# 29| 0: [LocalVariableDeclAndInitExpr] Nullable anythingInBar = ...
# 29| -1: [TypeMention] bool?
diff --git a/csharp/ql/test/library-tests/csharp7.3/PrintAst.expected b/csharp/ql/test/library-tests/csharp7.3/PrintAst.expected
index d55d5d279ada..70bfee85c04b 100644
--- a/csharp/ql/test/library-tests/csharp7.3/PrintAst.expected
+++ b/csharp/ql/test/library-tests/csharp7.3/PrintAst.expected
@@ -110,4 +110,5 @@ csharp73.cs:
# 51| 0: [TypeMention] Console
# 51| 0: [InterpolatedStringExpr] $"..."
# 51| 0: [StringLiteralUtf16] "x is "
-# 51| 1: [LocalVariableAccess] access to local variable x
+# 51| 1: [InterpolatedStringInsertExpr] {...}
+# 51| 0: [LocalVariableAccess] access to local variable x
diff --git a/csharp/ql/test/library-tests/csharp7/IsFlow.expected b/csharp/ql/test/library-tests/csharp7/IsFlow.expected
index b3e283528457..ce37b655bb85 100644
--- a/csharp/ql/test/library-tests/csharp7/IsFlow.expected
+++ b/csharp/ql/test/library-tests/csharp7/IsFlow.expected
@@ -24,7 +24,8 @@
| CSharp7.cs:255:17:255:45 | ...; | CSharp7.cs:255:37:255:38 | "x " | semmle.label | successor |
| CSharp7.cs:255:35:255:43 | $"..." | CSharp7.cs:255:17:255:44 | call to method WriteLine | semmle.label | successor |
| CSharp7.cs:255:37:255:38 | "x " | CSharp7.cs:255:40:255:41 | access to local variable s4 | semmle.label | successor |
-| CSharp7.cs:255:40:255:41 | access to local variable s4 | CSharp7.cs:255:35:255:43 | $"..." | semmle.label | successor |
+| CSharp7.cs:255:39:255:42 | {...} | CSharp7.cs:255:35:255:43 | $"..." | semmle.label | successor |
+| CSharp7.cs:255:40:255:41 | access to local variable s4 | CSharp7.cs:255:39:255:42 | {...} | semmle.label | successor |
| CSharp7.cs:256:17:256:22 | break; | CSharp7.cs:230:10:230:13 | exit Test (normal) | semmle.label | break |
| CSharp7.cs:257:13:257:36 | case ...: | CSharp7.cs:257:18:257:23 | Int32 i2 | semmle.label | successor |
| CSharp7.cs:257:18:257:23 | Int32 i2 | CSharp7.cs:257:30:257:31 | access to local variable i2 | semmle.label | match |
@@ -37,7 +38,8 @@
| CSharp7.cs:258:17:258:52 | ...; | CSharp7.cs:258:37:258:45 | "positive " | semmle.label | successor |
| CSharp7.cs:258:35:258:50 | $"..." | CSharp7.cs:258:17:258:51 | call to method WriteLine | semmle.label | successor |
| CSharp7.cs:258:37:258:45 | "positive " | CSharp7.cs:258:47:258:48 | access to local variable i2 | semmle.label | successor |
-| CSharp7.cs:258:47:258:48 | access to local variable i2 | CSharp7.cs:258:35:258:50 | $"..." | semmle.label | successor |
+| CSharp7.cs:258:46:258:49 | {...} | CSharp7.cs:258:35:258:50 | $"..." | semmle.label | successor |
+| CSharp7.cs:258:47:258:48 | access to local variable i2 | CSharp7.cs:258:46:258:49 | {...} | semmle.label | successor |
| CSharp7.cs:259:17:259:22 | break; | CSharp7.cs:230:10:230:13 | exit Test (normal) | semmle.label | break |
| CSharp7.cs:260:13:260:24 | case ...: | CSharp7.cs:260:18:260:23 | Int32 i3 | semmle.label | successor |
| CSharp7.cs:260:18:260:23 | Int32 i3 | CSharp7.cs:261:17:261:47 | ...; | semmle.label | match |
@@ -46,7 +48,8 @@
| CSharp7.cs:261:17:261:47 | ...; | CSharp7.cs:261:37:261:40 | "int " | semmle.label | successor |
| CSharp7.cs:261:35:261:45 | $"..." | CSharp7.cs:261:17:261:46 | call to method WriteLine | semmle.label | successor |
| CSharp7.cs:261:37:261:40 | "int " | CSharp7.cs:261:42:261:43 | access to local variable i3 | semmle.label | successor |
-| CSharp7.cs:261:42:261:43 | access to local variable i3 | CSharp7.cs:261:35:261:45 | $"..." | semmle.label | successor |
+| CSharp7.cs:261:41:261:44 | {...} | CSharp7.cs:261:35:261:45 | $"..." | semmle.label | successor |
+| CSharp7.cs:261:42:261:43 | access to local variable i3 | CSharp7.cs:261:41:261:44 | {...} | semmle.label | successor |
| CSharp7.cs:262:17:262:22 | break; | CSharp7.cs:230:10:230:13 | exit Test (normal) | semmle.label | break |
| CSharp7.cs:263:13:263:27 | case ...: | CSharp7.cs:263:18:263:26 | String s2 | semmle.label | successor |
| CSharp7.cs:263:18:263:26 | String s2 | CSharp7.cs:264:17:264:50 | ...; | semmle.label | match |
@@ -55,7 +58,8 @@
| CSharp7.cs:264:17:264:50 | ...; | CSharp7.cs:264:37:264:43 | "string " | semmle.label | successor |
| CSharp7.cs:264:35:264:48 | $"..." | CSharp7.cs:264:17:264:49 | call to method WriteLine | semmle.label | successor |
| CSharp7.cs:264:37:264:43 | "string " | CSharp7.cs:264:45:264:46 | access to local variable s2 | semmle.label | successor |
-| CSharp7.cs:264:45:264:46 | access to local variable s2 | CSharp7.cs:264:35:264:48 | $"..." | semmle.label | successor |
+| CSharp7.cs:264:44:264:47 | {...} | CSharp7.cs:264:35:264:48 | $"..." | semmle.label | successor |
+| CSharp7.cs:264:45:264:46 | access to local variable s2 | CSharp7.cs:264:44:264:47 | {...} | semmle.label | successor |
| CSharp7.cs:265:17:265:22 | break; | CSharp7.cs:230:10:230:13 | exit Test (normal) | semmle.label | break |
| CSharp7.cs:266:13:266:26 | case ...: | CSharp7.cs:266:18:266:23 | access to type Double | semmle.label | successor |
| CSharp7.cs:266:18:266:23 | access to type Double | CSharp7.cs:267:17:267:44 | ...; | semmle.label | match |
diff --git a/csharp/ql/test/library-tests/csharp7/LocalTaintFlow.expected b/csharp/ql/test/library-tests/csharp7/LocalTaintFlow.expected
index 4a16e2491dfe..1bba9f6dd8ba 100644
--- a/csharp/ql/test/library-tests/csharp7/LocalTaintFlow.expected
+++ b/csharp/ql/test/library-tests/csharp7/LocalTaintFlow.expected
@@ -254,7 +254,8 @@
| CSharp7.cs:233:28:233:33 | ... > ... | CSharp7.cs:233:13:233:33 | [true] ... && ... |
| CSharp7.cs:235:13:235:42 | [input] SSA phi read(o) | CSharp7.cs:248:9:274:9 | SSA phi read(o) |
| CSharp7.cs:235:33:235:36 | "int " | CSharp7.cs:235:31:235:41 | $"..." |
-| CSharp7.cs:235:38:235:39 | access to local variable i1 | CSharp7.cs:235:31:235:41 | $"..." |
+| CSharp7.cs:235:37:235:40 | {...} | CSharp7.cs:235:31:235:41 | $"..." |
+| CSharp7.cs:235:38:235:39 | access to local variable i1 | CSharp7.cs:235:37:235:40 | {...} |
| CSharp7.cs:237:18:237:18 | access to local variable o | CSharp7.cs:237:23:237:31 | String s1 |
| CSharp7.cs:237:18:237:18 | access to local variable o | CSharp7.cs:239:13:239:45 | [input] SSA phi read(o) |
| CSharp7.cs:237:18:237:18 | access to local variable o | CSharp7.cs:241:18:241:18 | access to local variable o |
@@ -262,7 +263,8 @@
| CSharp7.cs:237:23:237:31 | String s1 | CSharp7.cs:237:23:237:31 | SSA def(s1) |
| CSharp7.cs:239:13:239:45 | [input] SSA phi read(o) | CSharp7.cs:248:9:274:9 | SSA phi read(o) |
| CSharp7.cs:239:33:239:39 | "string " | CSharp7.cs:239:31:239:44 | $"..." |
-| CSharp7.cs:239:41:239:42 | access to local variable s1 | CSharp7.cs:239:31:239:44 | $"..." |
+| CSharp7.cs:239:40:239:43 | {...} | CSharp7.cs:239:31:239:44 | $"..." |
+| CSharp7.cs:239:41:239:42 | access to local variable s1 | CSharp7.cs:239:40:239:43 | {...} |
| CSharp7.cs:241:18:241:18 | access to local variable o | CSharp7.cs:242:9:243:9 | [input] SSA phi read(o) |
| CSharp7.cs:241:18:241:18 | access to local variable o | CSharp7.cs:244:18:244:18 | access to local variable o |
| CSharp7.cs:242:9:243:9 | [input] SSA phi read(o) | CSharp7.cs:248:9:274:9 | SSA phi read(o) |
@@ -283,21 +285,25 @@
| CSharp7.cs:254:32:254:40 | SSA def(s4) | CSharp7.cs:255:40:255:41 | access to local variable s4 |
| CSharp7.cs:254:32:254:40 | String s4 | CSharp7.cs:254:32:254:40 | SSA def(s4) |
| CSharp7.cs:255:37:255:38 | "x " | CSharp7.cs:255:35:255:43 | $"..." |
-| CSharp7.cs:255:40:255:41 | access to local variable s4 | CSharp7.cs:255:35:255:43 | $"..." |
+| CSharp7.cs:255:39:255:42 | {...} | CSharp7.cs:255:35:255:43 | $"..." |
+| CSharp7.cs:255:40:255:41 | access to local variable s4 | CSharp7.cs:255:39:255:42 | {...} |
| CSharp7.cs:257:18:257:23 | Int32 i2 | CSharp7.cs:257:18:257:23 | SSA def(i2) |
| CSharp7.cs:257:18:257:23 | SSA def(i2) | CSharp7.cs:257:30:257:31 | access to local variable i2 |
| CSharp7.cs:257:30:257:31 | access to local variable i2 | CSharp7.cs:257:30:257:35 | ... > ... |
| CSharp7.cs:257:30:257:31 | access to local variable i2 | CSharp7.cs:258:47:258:48 | access to local variable i2 |
| CSharp7.cs:258:37:258:45 | "positive " | CSharp7.cs:258:35:258:50 | $"..." |
-| CSharp7.cs:258:47:258:48 | access to local variable i2 | CSharp7.cs:258:35:258:50 | $"..." |
+| CSharp7.cs:258:46:258:49 | {...} | CSharp7.cs:258:35:258:50 | $"..." |
+| CSharp7.cs:258:47:258:48 | access to local variable i2 | CSharp7.cs:258:46:258:49 | {...} |
| CSharp7.cs:260:18:260:23 | Int32 i3 | CSharp7.cs:260:18:260:23 | SSA def(i3) |
| CSharp7.cs:260:18:260:23 | SSA def(i3) | CSharp7.cs:261:42:261:43 | access to local variable i3 |
| CSharp7.cs:261:37:261:40 | "int " | CSharp7.cs:261:35:261:45 | $"..." |
-| CSharp7.cs:261:42:261:43 | access to local variable i3 | CSharp7.cs:261:35:261:45 | $"..." |
+| CSharp7.cs:261:41:261:44 | {...} | CSharp7.cs:261:35:261:45 | $"..." |
+| CSharp7.cs:261:42:261:43 | access to local variable i3 | CSharp7.cs:261:41:261:44 | {...} |
| CSharp7.cs:263:18:263:26 | SSA def(s2) | CSharp7.cs:264:45:264:46 | access to local variable s2 |
| CSharp7.cs:263:18:263:26 | String s2 | CSharp7.cs:263:18:263:26 | SSA def(s2) |
| CSharp7.cs:264:37:264:43 | "string " | CSharp7.cs:264:35:264:48 | $"..." |
-| CSharp7.cs:264:45:264:46 | access to local variable s2 | CSharp7.cs:264:35:264:48 | $"..." |
+| CSharp7.cs:264:44:264:47 | {...} | CSharp7.cs:264:35:264:48 | $"..." |
+| CSharp7.cs:264:45:264:46 | access to local variable s2 | CSharp7.cs:264:44:264:47 | {...} |
| CSharp7.cs:282:13:282:16 | access to local variable dict | CSharp7.cs:282:13:282:48 | SSA def(dict) |
| CSharp7.cs:282:13:282:48 | SSA def(dict) | CSharp7.cs:283:20:283:23 | access to local variable dict |
| CSharp7.cs:282:20:282:48 | object creation of type Dictionary | CSharp7.cs:282:13:282:16 | access to local variable dict |
diff --git a/csharp/ql/test/library-tests/csharp7/PrintAst.expected b/csharp/ql/test/library-tests/csharp7/PrintAst.expected
index e5d009e0df63..bf0b07abbeb8 100644
--- a/csharp/ql/test/library-tests/csharp7/PrintAst.expected
+++ b/csharp/ql/test/library-tests/csharp7/PrintAst.expected
@@ -727,7 +727,8 @@ CSharp7.cs:
# 235| 0: [TypeMention] Console
# 235| 0: [InterpolatedStringExpr] $"..."
# 235| 0: [StringLiteralUtf16] "int "
-# 235| 1: [LocalVariableAccess] access to local variable i1
+# 235| 1: [InterpolatedStringInsertExpr] {...}
+# 235| 0: [LocalVariableAccess] access to local variable i1
# 237| 2: [IfStmt] if (...) ...
# 237| 0: [IsExpr] ... is ...
# 237| 0: [LocalVariableAccess] access to local variable o
@@ -740,7 +741,8 @@ CSharp7.cs:
# 239| 0: [TypeMention] Console
# 239| 0: [InterpolatedStringExpr] $"..."
# 239| 0: [StringLiteralUtf16] "string "
-# 239| 1: [LocalVariableAccess] access to local variable s1
+# 239| 1: [InterpolatedStringInsertExpr] {...}
+# 239| 0: [LocalVariableAccess] access to local variable s1
# 241| 2: [IfStmt] if (...) ...
# 241| 0: [IsExpr] ... is ...
# 241| 0: [LocalVariableAccess] access to local variable o
@@ -775,7 +777,8 @@ CSharp7.cs:
# 255| 0: [TypeMention] Console
# 255| 0: [InterpolatedStringExpr] $"..."
# 255| 0: [StringLiteralUtf16] "x "
-# 255| 1: [LocalVariableAccess] access to local variable s4
+# 255| 1: [InterpolatedStringInsertExpr] {...}
+# 255| 0: [LocalVariableAccess] access to local variable s4
# 256| 6: [BreakStmt] break;
# 257| 7: [CaseStmt] case ...:
# 257| 0: [VariablePatternExpr] Int32 i2
@@ -789,7 +792,8 @@ CSharp7.cs:
# 258| 0: [TypeMention] Console
# 258| 0: [InterpolatedStringExpr] $"..."
# 258| 0: [StringLiteralUtf16] "positive "
-# 258| 1: [LocalVariableAccess] access to local variable i2
+# 258| 1: [InterpolatedStringInsertExpr] {...}
+# 258| 0: [LocalVariableAccess] access to local variable i2
# 259| 9: [BreakStmt] break;
# 260| 10: [CaseStmt] case ...:
# 260| 0: [VariablePatternExpr] Int32 i3
@@ -800,7 +804,8 @@ CSharp7.cs:
# 261| 0: [TypeMention] Console
# 261| 0: [InterpolatedStringExpr] $"..."
# 261| 0: [StringLiteralUtf16] "int "
-# 261| 1: [LocalVariableAccess] access to local variable i3
+# 261| 1: [InterpolatedStringInsertExpr] {...}
+# 261| 0: [LocalVariableAccess] access to local variable i3
# 262| 12: [BreakStmt] break;
# 263| 13: [CaseStmt] case ...:
# 263| 0: [VariablePatternExpr] String s2
@@ -811,7 +816,8 @@ CSharp7.cs:
# 264| 0: [TypeMention] Console
# 264| 0: [InterpolatedStringExpr] $"..."
# 264| 0: [StringLiteralUtf16] "string "
-# 264| 1: [LocalVariableAccess] access to local variable s2
+# 264| 1: [InterpolatedStringInsertExpr] {...}
+# 264| 0: [LocalVariableAccess] access to local variable s2
# 265| 15: [BreakStmt] break;
# 266| 16: [CaseStmt] case ...:
# 266| 0: [TypeAccessPatternExpr] access to type Double
diff --git a/csharp/ql/test/library-tests/csharp8/PrintAst.expected b/csharp/ql/test/library-tests/csharp8/PrintAst.expected
index c33374e4761d..4ec30c915b3a 100644
--- a/csharp/ql/test/library-tests/csharp8/PrintAst.expected
+++ b/csharp/ql/test/library-tests/csharp8/PrintAst.expected
@@ -4,12 +4,14 @@ AlternateInterpolatedStrings.cs:
# 5| -1: [TypeMention] string
# 5| 1: [InterpolatedStringExpr] $"..."
# 5| 0: [StringLiteralUtf16] "C:"
-# 5| 1: [IntLiteral] 12
+# 5| 1: [InterpolatedStringInsertExpr] {...}
+# 5| 0: [IntLiteral] 12
# 6| 6: [Field] s2
# 6| -1: [TypeMention] string
# 6| 1: [InterpolatedStringExpr] $"..."
# 6| 0: [StringLiteralUtf16] "C:"
-# 6| 1: [IntLiteral] 12
+# 6| 1: [InterpolatedStringInsertExpr] {...}
+# 6| 0: [IntLiteral] 12
AsyncStreams.cs:
# 6| [Class] AsyncStreams
# 8| 5: [Method] Items
diff --git a/csharp/ql/test/library-tests/csharp9/PrintAst.expected b/csharp/ql/test/library-tests/csharp9/PrintAst.expected
index a1ecbd0a2123..e3b89de20096 100644
--- a/csharp/ql/test/library-tests/csharp9/PrintAst.expected
+++ b/csharp/ql/test/library-tests/csharp9/PrintAst.expected
@@ -1008,8 +1008,9 @@ Record.cs:
# 49| 0: [LocalVariableAccess] access to local variable s
# 50| 2: [ReturnStmt] return ...;
# 50| 0: [InterpolatedStringExpr] $"..."
-# 50| 0: [MethodCall] call to method ToString
-# 50| -1: [LocalVariableAccess] access to local variable s
+# 50| 0: [InterpolatedStringInsertExpr] {...}
+# 50| 0: [MethodCall] call to method ToString
+# 50| -1: [LocalVariableAccess] access to local variable s
# 50| 1: [StringLiteralUtf16] " is a dog"
# 54| [RecordClass] R1
# 54| 12: [NEOperator] !=
diff --git a/csharp/ql/test/library-tests/dataflow/implicittostring/PrintAst.expected b/csharp/ql/test/library-tests/dataflow/implicittostring/PrintAst.expected
index bf2a515a8895..cd7658f6f5e9 100644
--- a/csharp/ql/test/library-tests/dataflow/implicittostring/PrintAst.expected
+++ b/csharp/ql/test/library-tests/dataflow/implicittostring/PrintAst.expected
@@ -68,8 +68,9 @@ implicitToString.cs:
# 32| 0: [LocalVariableAccess] access to local variable x2
# 32| 1: [InterpolatedStringExpr] $"..."
# 32| 0: [StringLiteralUtf16] "Hello "
-# 32| 1: [MethodCall] call to method ToString
-# 32| -1: [LocalVariableAccess] access to local variable x1
+# 32| 1: [InterpolatedStringInsertExpr] {...}
+# 32| 0: [MethodCall] call to method ToString
+# 32| -1: [LocalVariableAccess] access to local variable x1
# 33| 2: [ExprStmt] ...;
# 33| 0: [MethodCall] call to method Sink
# 33| 0: [LocalVariableAccess] access to local variable x2
@@ -88,8 +89,9 @@ implicitToString.cs:
# 39| 0: [LocalVariableAccess] access to local variable x2
# 39| 1: [InterpolatedStringExpr] $"..."
# 39| 0: [StringLiteralUtf16] "Hello "
-# 39| 1: [MethodCall] call to method ToString
-# 39| -1: [LocalVariableAccess] access to local variable x1
+# 39| 1: [InterpolatedStringInsertExpr] {...}
+# 39| 0: [MethodCall] call to method ToString
+# 39| -1: [LocalVariableAccess] access to local variable x1
# 40| 2: [ExprStmt] ...;
# 40| 0: [MethodCall] call to method Sink
# 40| 0: [LocalVariableAccess] access to local variable x2
diff --git a/csharp/ql/test/library-tests/dataflow/local/TaintTrackingStep.expected b/csharp/ql/test/library-tests/dataflow/local/TaintTrackingStep.expected
index ea0ae7f9da73..7187280f7555 100644
--- a/csharp/ql/test/library-tests/dataflow/local/TaintTrackingStep.expected
+++ b/csharp/ql/test/library-tests/dataflow/local/TaintTrackingStep.expected
@@ -547,12 +547,14 @@
| LocalDataFlow.cs:273:13:273:36 | SSA def(sink69) | LocalDataFlow.cs:274:15:274:20 | access to local variable sink69 |
| LocalDataFlow.cs:273:22:273:36 | $"..." | LocalDataFlow.cs:273:13:273:18 | access to local variable sink69 |
| LocalDataFlow.cs:273:24:273:28 | "test " | LocalDataFlow.cs:273:22:273:36 | $"..." |
-| LocalDataFlow.cs:273:30:273:34 | access to local variable sink1 | LocalDataFlow.cs:273:22:273:36 | $"..." |
+| LocalDataFlow.cs:273:29:273:35 | {...} | LocalDataFlow.cs:273:22:273:36 | $"..." |
+| LocalDataFlow.cs:273:30:273:34 | access to local variable sink1 | LocalDataFlow.cs:273:29:273:35 | {...} |
| LocalDataFlow.cs:277:9:277:16 | access to local variable nonSink0 | LocalDataFlow.cs:277:9:277:37 | SSA def(nonSink0) |
| LocalDataFlow.cs:277:9:277:37 | SSA def(nonSink0) | LocalDataFlow.cs:278:15:278:22 | access to local variable nonSink0 |
| LocalDataFlow.cs:277:20:277:37 | $"..." | LocalDataFlow.cs:277:9:277:16 | access to local variable nonSink0 |
| LocalDataFlow.cs:277:22:277:26 | "test " | LocalDataFlow.cs:277:20:277:37 | $"..." |
-| LocalDataFlow.cs:277:28:277:35 | access to local variable nonSink0 | LocalDataFlow.cs:277:20:277:37 | $"..." |
+| LocalDataFlow.cs:277:27:277:36 | {...} | LocalDataFlow.cs:277:20:277:37 | $"..." |
+| LocalDataFlow.cs:277:28:277:35 | access to local variable nonSink0 | LocalDataFlow.cs:277:27:277:36 | {...} |
| LocalDataFlow.cs:278:15:278:22 | [post] access to local variable nonSink0 | LocalDataFlow.cs:285:31:285:38 | access to local variable nonSink0 |
| LocalDataFlow.cs:278:15:278:22 | access to local variable nonSink0 | LocalDataFlow.cs:285:31:285:38 | access to local variable nonSink0 |
| LocalDataFlow.cs:281:13:281:18 | access to local variable sink70 | LocalDataFlow.cs:281:13:281:34 | SSA def(sink70) |
@@ -1237,8 +1239,10 @@
| Splitting.cs:56:13:56:19 | [b (line 46): true] $"..." | Splitting.cs:56:9:56:9 | access to local variable x |
| Splitting.cs:56:15:56:15 | [b (line 46): false] "c" | Splitting.cs:56:13:56:19 | [b (line 46): false] $"..." |
| Splitting.cs:56:15:56:15 | [b (line 46): true] "c" | Splitting.cs:56:13:56:19 | [b (line 46): true] $"..." |
-| Splitting.cs:56:17:56:17 | [b (line 46): false] access to local variable x | Splitting.cs:56:13:56:19 | [b (line 46): false] $"..." |
-| Splitting.cs:56:17:56:17 | [b (line 46): true] access to local variable x | Splitting.cs:56:13:56:19 | [b (line 46): true] $"..." |
+| Splitting.cs:56:16:56:18 | [b (line 46): false] {...} | Splitting.cs:56:13:56:19 | [b (line 46): false] $"..." |
+| Splitting.cs:56:16:56:18 | [b (line 46): true] {...} | Splitting.cs:56:13:56:19 | [b (line 46): true] $"..." |
+| Splitting.cs:56:17:56:17 | [b (line 46): false] access to local variable x | Splitting.cs:56:16:56:18 | [b (line 46): false] {...} |
+| Splitting.cs:56:17:56:17 | [b (line 46): true] access to local variable x | Splitting.cs:56:16:56:18 | [b (line 46): true] {...} |
| Splitting.cs:57:13:57:24 | [b (line 46): false] access to field Item1 | Splitting.cs:57:9:57:9 | access to local variable x |
| Splitting.cs:57:13:57:24 | [b (line 46): true] access to field Item1 | Splitting.cs:57:9:57:9 | access to local variable x |
| Splitting.cs:57:17:57:17 | [b (line 46): false] access to local variable y | Splitting.cs:58:27:58:27 | [b (line 46): false] access to local variable y |
diff --git a/csharp/ql/test/library-tests/stringinterpolation/PrintAst.expected b/csharp/ql/test/library-tests/stringinterpolation/PrintAst.expected
new file mode 100644
index 000000000000..6529f9feed5c
--- /dev/null
+++ b/csharp/ql/test/library-tests/stringinterpolation/PrintAst.expected
@@ -0,0 +1,70 @@
+StringInterpolation.cs:
+# 3| [Class] MyStringInterpolationClass
+# 6| 5: [Method] M
+# 6| -1: [TypeMention] Void
+# 7| 4: [BlockStmt] {...}
+# 8| 0: [LocalVariableDeclStmt] ... ...;
+# 8| 0: [LocalVariableDeclAndInitExpr] Single i = ...
+# 8| -1: [TypeMention] float
+# 8| 0: [LocalVariableAccess] access to local variable i
+# 8| 1: [FloatLiteral] 3.14159
+# 9| 1: [LocalConstantDeclStmt] const ... ...;
+# 9| 0: [LocalVariableDeclAndInitExpr] Int32 align = ...
+# 9| -1: [TypeMention] int
+# 9| 0: [LocalVariableAccess] access to local variable align
+# 9| 1: [IntLiteral] 5
+# 10| 2: [LocalVariableDeclStmt] ... ...;
+# 10| 0: [LocalVariableDeclAndInitExpr] String x1 = ...
+# 10| -1: [TypeMention] string
+# 10| 0: [LocalVariableAccess] access to local variable x1
+# 10| 1: [InterpolatedStringExpr] $"..."
+# 10| 0: [StringLiteralUtf16] "Hello, Pi "
+# 10| 1: [InterpolatedStringInsertExpr] {...}
+# 10| 0: [LocalVariableAccess] access to local variable i
+# 11| 3: [LocalVariableDeclStmt] ... ...;
+# 11| 0: [LocalVariableDeclAndInitExpr] String x2 = ...
+# 11| -1: [TypeMention] string
+# 11| 0: [LocalVariableAccess] access to local variable x2
+# 11| 1: [InterpolatedStringExpr] $"..."
+# 11| 0: [StringLiteralUtf16] "Hello, Pi "
+# 11| 1: [InterpolatedStringInsertExpr] {...}
+# 11| 0: [LocalVariableAccess] access to local variable i
+# 11| 2: [StringLiteralUtf16] "F1"
+# 12| 4: [LocalVariableDeclStmt] ... ...;
+# 12| 0: [LocalVariableDeclAndInitExpr] String x3 = ...
+# 12| -1: [TypeMention] string
+# 12| 0: [LocalVariableAccess] access to local variable x3
+# 12| 1: [InterpolatedStringExpr] $"..."
+# 12| 0: [StringLiteralUtf16] "Hello, Pi "
+# 12| 1: [InterpolatedStringInsertExpr] {...}
+# 12| 0: [LocalVariableAccess] access to local variable i
+# 12| 1: [IntLiteral] 6
+# 13| 5: [LocalVariableDeclStmt] ... ...;
+# 13| 0: [LocalVariableDeclAndInitExpr] String x4 = ...
+# 13| -1: [TypeMention] string
+# 13| 0: [LocalVariableAccess] access to local variable x4
+# 13| 1: [InterpolatedStringExpr] $"..."
+# 13| 0: [StringLiteralUtf16] "Hello, Pi "
+# 13| 1: [InterpolatedStringInsertExpr] {...}
+# 13| 0: [LocalVariableAccess] access to local variable i
+# 13| 1: [IntLiteral] 6
+# 13| 2: [StringLiteralUtf16] "F3"
+# 14| 6: [LocalVariableDeclStmt] ... ...;
+# 14| 0: [LocalVariableDeclAndInitExpr] String x5 = ...
+# 14| -1: [TypeMention] string
+# 14| 0: [LocalVariableAccess] access to local variable x5
+# 14| 1: [InterpolatedStringExpr] $"..."
+# 14| 0: [StringLiteralUtf16] "Hello, Pi "
+# 14| 1: [InterpolatedStringInsertExpr] {...}
+# 14| 0: [LocalVariableAccess] access to local variable i
+# 14| 1: [LocalVariableAccess] access to local variable align
+# 15| 7: [LocalVariableDeclStmt] ... ...;
+# 15| 0: [LocalVariableDeclAndInitExpr] String x6 = ...
+# 15| -1: [TypeMention] string
+# 15| 0: [LocalVariableAccess] access to local variable x6
+# 15| 1: [InterpolatedStringExpr] $"..."
+# 15| 0: [StringLiteralUtf16] "Hello, Pi "
+# 15| 1: [InterpolatedStringInsertExpr] {...}
+# 15| 0: [LocalVariableAccess] access to local variable i
+# 15| 1: [LocalVariableAccess] access to local variable align
+# 15| 2: [StringLiteralUtf16] "F2"
diff --git a/csharp/ql/test/library-tests/stringinterpolation/PrintAst.qlref b/csharp/ql/test/library-tests/stringinterpolation/PrintAst.qlref
new file mode 100644
index 000000000000..f867dd01f9f8
--- /dev/null
+++ b/csharp/ql/test/library-tests/stringinterpolation/PrintAst.qlref
@@ -0,0 +1 @@
+shared/PrintAst.ql
\ No newline at end of file
diff --git a/csharp/ql/test/library-tests/stringinterpolation/StringInterpolation.cs b/csharp/ql/test/library-tests/stringinterpolation/StringInterpolation.cs
new file mode 100644
index 000000000000..d14c3d75e465
--- /dev/null
+++ b/csharp/ql/test/library-tests/stringinterpolation/StringInterpolation.cs
@@ -0,0 +1,17 @@
+using System;
+
+public class MyStringInterpolationClass
+{
+
+ public void M()
+ {
+ float i = 3.14159f;
+ const int align = 5;
+ var x1 = $"Hello, Pi {i}";
+ var x2 = $"Hello, Pi {i:F1}";
+ var x3 = $"Hello, Pi {i,6}";
+ var x4 = $"Hello, Pi {i,6:F3}";
+ var x5 = $"Hello, Pi {i,align}";
+ var x6 = $"Hello, Pi {i,align:F2}";
+ }
+}
diff --git a/csharp/ql/test/library-tests/stringinterpolation/options b/csharp/ql/test/library-tests/stringinterpolation/options
new file mode 100644
index 000000000000..77b22963f5c8
--- /dev/null
+++ b/csharp/ql/test/library-tests/stringinterpolation/options
@@ -0,0 +1,2 @@
+semmle-extractor-options: /nostdlib /noconfig
+semmle-extractor-options: --load-sources-from-project:${testdir}/../../resources/stubs/_frameworks/Microsoft.NETCore.App/Microsoft.NETCore.App.csproj
diff --git a/csharp/ql/test/library-tests/stringinterpolation/stringInterpolation.expected b/csharp/ql/test/library-tests/stringinterpolation/stringInterpolation.expected
new file mode 100644
index 000000000000..9ce88ad83c31
--- /dev/null
+++ b/csharp/ql/test/library-tests/stringinterpolation/stringInterpolation.expected
@@ -0,0 +1,23 @@
+inserts
+| StringInterpolation.cs:10:18:10:33 | $"..." | StringInterpolation.cs:10:31:10:31 | access to local variable i |
+| StringInterpolation.cs:11:18:11:36 | $"..." | StringInterpolation.cs:11:31:11:31 | access to local variable i |
+| StringInterpolation.cs:12:18:12:35 | $"..." | StringInterpolation.cs:12:31:12:31 | access to local variable i |
+| StringInterpolation.cs:13:18:13:38 | $"..." | StringInterpolation.cs:13:31:13:31 | access to local variable i |
+| StringInterpolation.cs:14:18:14:39 | $"..." | StringInterpolation.cs:14:31:14:31 | access to local variable i |
+| StringInterpolation.cs:15:18:15:42 | $"..." | StringInterpolation.cs:15:31:15:31 | access to local variable i |
+texts
+| StringInterpolation.cs:10:18:10:33 | $"..." | StringInterpolation.cs:10:20:10:29 | "Hello, Pi " |
+| StringInterpolation.cs:11:18:11:36 | $"..." | StringInterpolation.cs:11:20:11:29 | "Hello, Pi " |
+| StringInterpolation.cs:12:18:12:35 | $"..." | StringInterpolation.cs:12:20:12:29 | "Hello, Pi " |
+| StringInterpolation.cs:13:18:13:38 | $"..." | StringInterpolation.cs:13:20:13:29 | "Hello, Pi " |
+| StringInterpolation.cs:14:18:14:39 | $"..." | StringInterpolation.cs:14:20:14:29 | "Hello, Pi " |
+| StringInterpolation.cs:15:18:15:42 | $"..." | StringInterpolation.cs:15:20:15:29 | "Hello, Pi " |
+interpolationInsertsWithAlign
+| StringInterpolation.cs:12:18:12:35 | $"..." | StringInterpolation.cs:12:31:12:31 | access to local variable i | StringInterpolation.cs:12:33:12:33 | 6 |
+| StringInterpolation.cs:13:18:13:38 | $"..." | StringInterpolation.cs:13:31:13:31 | access to local variable i | StringInterpolation.cs:13:33:13:33 | 6 |
+| StringInterpolation.cs:14:18:14:39 | $"..." | StringInterpolation.cs:14:31:14:31 | access to local variable i | StringInterpolation.cs:14:33:14:37 | access to local variable align |
+| StringInterpolation.cs:15:18:15:42 | $"..." | StringInterpolation.cs:15:31:15:31 | access to local variable i | StringInterpolation.cs:15:33:15:37 | access to local variable align |
+interpolationInsertsWithFormat
+| StringInterpolation.cs:11:18:11:36 | $"..." | StringInterpolation.cs:11:31:11:31 | access to local variable i | StringInterpolation.cs:11:32:11:34 | "F1" |
+| StringInterpolation.cs:13:18:13:38 | $"..." | StringInterpolation.cs:13:31:13:31 | access to local variable i | StringInterpolation.cs:13:34:13:36 | "F3" |
+| StringInterpolation.cs:15:18:15:42 | $"..." | StringInterpolation.cs:15:31:15:31 | access to local variable i | StringInterpolation.cs:15:38:15:40 | "F2" |
diff --git a/csharp/ql/test/library-tests/stringinterpolation/stringInterpolation.ql b/csharp/ql/test/library-tests/stringinterpolation/stringInterpolation.ql
new file mode 100644
index 000000000000..f4d683858105
--- /dev/null
+++ b/csharp/ql/test/library-tests/stringinterpolation/stringInterpolation.ql
@@ -0,0 +1,23 @@
+import csharp
+
+query predicate inserts(InterpolatedStringExpr expr, Expr e) { expr.getAnInsert() = e }
+
+query predicate texts(InterpolatedStringExpr expr, StringLiteral literal) {
+ expr.getAText() = literal
+}
+
+query predicate interpolationInsertsWithAlign(InterpolatedStringExpr expr, Expr insert, Expr align) {
+ exists(InterpolatedStringInsertExpr e | expr.getInterpolatedInsert(_) = e |
+ insert = e.getInsert() and
+ align = e.getAlignment()
+ )
+}
+
+query predicate interpolationInsertsWithFormat(
+ InterpolatedStringExpr expr, Expr insert, StringLiteral format
+) {
+ exists(InterpolatedStringInsertExpr e | expr.getInterpolatedInsert(_) = e |
+ insert = e.getInsert() and
+ format = e.getFormat()
+ )
+}