2020-05-03 22:27:08 +01:00
const std = @import ( " std " ) ;
2022-07-15 17:06:18 +01:00
const DocumentStore = @import ( " DocumentStore.zig " ) ;
2021-10-01 02:44:06 +01:00
const Ast = std . zig . Ast ;
2022-12-27 06:47:57 +00:00
const types = @import ( " lsp.zig " ) ;
2022-07-15 17:06:18 +01:00
const offsets = @import ( " offsets.zig " ) ;
2023-01-16 18:46:33 +00:00
const URI = @import ( " uri.zig " ) ;
2023-03-05 19:08:31 +00:00
const log = std . log . scoped ( . zls_analysis ) ;
2022-07-15 17:06:18 +01:00
const ast = @import ( " ast.zig " ) ;
2023-03-11 18:43:14 +00:00
const tracy = @import ( " tracy.zig " ) ;
2022-10-28 19:24:38 +01:00
const ComptimeInterpreter = @import ( " ComptimeInterpreter.zig " ) ;
2023-02-08 20:01:15 +00:00
const InternPool = ComptimeInterpreter . InternPool ;
2023-03-31 16:54:46 +01:00
const references = @import ( " features/references.zig " ) ;
2020-05-09 03:38:08 +01:00
2023-03-12 22:48:31 +00:00
const Analyser = @This ( ) ;
gpa : std . mem . Allocator ,
2023-03-29 19:36:32 +01:00
arena : std . heap . ArenaAllocator ,
2023-03-12 22:48:31 +00:00
store : * DocumentStore ,
2023-05-09 05:25:26 +01:00
ip : ? InternPool ,
2023-03-12 22:48:31 +00:00
bound_type_params : std . AutoHashMapUnmanaged ( Ast . full . FnProto . Param , TypeWithHandle ) = . { } ,
using_trail : std . AutoHashMapUnmanaged ( Ast . Node . Index , void ) = . { } ,
2023-03-13 00:08:22 +00:00
resolved_nodes : std . HashMapUnmanaged ( NodeWithUri , ? TypeWithHandle , NodeWithUri . Context , std . hash_map . default_max_load_percentage ) = . { } ,
2023-03-12 22:48:31 +00:00
2023-03-29 19:36:32 +01:00
pub fn init ( gpa : std . mem . Allocator , store : * DocumentStore ) Analyser {
2023-03-12 22:48:31 +00:00
return . {
. gpa = gpa ,
2023-03-29 19:36:32 +01:00
. arena = std . heap . ArenaAllocator . init ( gpa ) ,
2023-03-12 22:48:31 +00:00
. store = store ,
2023-05-09 05:25:26 +01:00
. ip = null ,
2023-03-12 22:48:31 +00:00
} ;
}
pub fn deinit ( self : * Analyser ) void {
self . bound_type_params . deinit ( self . gpa ) ;
self . using_trail . deinit ( self . gpa ) ;
self . resolved_nodes . deinit ( self . gpa ) ;
2023-05-09 05:25:26 +01:00
if ( self . ip ) | * intern_pool | intern_pool . deinit ( self . gpa ) ;
2023-03-29 19:36:32 +01:00
self . arena . deinit ( ) ;
2021-04-07 14:10:18 +01:00
}
2023-03-12 22:48:31 +00:00
pub fn invalidate ( self : * Analyser ) void {
self . bound_type_params . clearRetainingCapacity ( ) ;
self . using_trail . clearRetainingCapacity ( ) ;
self . resolved_nodes . clearRetainingCapacity ( ) ;
2023-03-29 19:36:32 +01:00
_ = self . arena . reset ( . free_all ) ;
2021-04-07 14:10:18 +01:00
}
2021-04-15 10:07:43 +01:00
/// Gets a declaration's doc comments. Caller owns returned memory.
2022-12-27 06:47:57 +00:00
pub fn getDocComments ( allocator : std . mem . Allocator , tree : Ast , node : Ast . Node . Index , format : types . MarkupKind ) ! ? [ ] const u8 {
2021-04-04 00:03:25 +01:00
const base = tree . nodes . items ( . main_token ) [ node ] ;
2021-04-15 10:07:43 +01:00
const base_kind = tree . nodes . items ( . tag ) [ node ] ;
2021-04-04 00:03:25 +01:00
const tokens = tree . tokens . items ( . tag ) ;
2021-04-15 10:07:43 +01:00
switch ( base_kind ) {
2021-04-15 10:21:49 +01:00
// As far as I know, this does not actually happen yet, but it
// may come in useful.
. root = > return try collectDocComments ( allocator , tree , 0 , format , true ) ,
2021-04-15 10:07:43 +01:00
. fn_proto ,
. fn_proto_one ,
. fn_proto_simple ,
. fn_proto_multi ,
. fn_decl ,
. local_var_decl ,
. global_var_decl ,
. aligned_var_decl ,
2021-04-15 10:21:49 +01:00
. simple_var_decl ,
2021-05-07 10:10:13 +01:00
. container_field_init ,
2023-02-03 23:58:53 +00:00
. container_field ,
2021-04-15 10:21:49 +01:00
= > {
2021-04-15 10:07:43 +01:00
if ( getDocCommentTokenIndex ( tokens , base ) ) | doc_comment_index |
2021-04-15 10:21:49 +01:00
return try collectDocComments ( allocator , tree , doc_comment_index , format , false ) ;
} ,
else = > { } ,
2020-05-03 22:27:08 +01:00
}
2020-05-14 17:07:46 +01:00
return null ;
}
2021-04-15 10:07:43 +01:00
/// Get the first doc comment of a declaration.
2022-10-05 12:40:11 +01:00
pub fn getDocCommentTokenIndex ( tokens : [ ] const std . zig . Token . Tag , base_token : Ast . TokenIndex ) ? Ast . TokenIndex {
2021-04-04 00:03:25 +01:00
var idx = base_token ;
if ( idx = = 0 ) return null ;
2023-03-12 06:53:05 +00:00
idx - | = 1 ;
if ( tokens [ idx ] = = . keyword_threadlocal and idx > 0 ) idx - | = 1 ;
if ( tokens [ idx ] = = . string_literal and idx > 1 and tokens [ idx - | 1 ] = = . keyword_extern ) idx - | = 1 ;
if ( tokens [ idx ] = = . keyword_extern and idx > 0 ) idx - | = 1 ;
if ( tokens [ idx ] = = . keyword_export and idx > 0 ) idx - | = 1 ;
if ( tokens [ idx ] = = . keyword_inline and idx > 0 ) idx - | = 1 ;
if ( tokens [ idx ] = = . identifier and idx > 0 ) idx - | = 1 ;
if ( tokens [ idx ] = = . keyword_pub and idx > 0 ) idx - | = 1 ;
2021-04-04 00:03:25 +01:00
// Find first doc comment token
2021-04-15 10:07:43 +01:00
if ( ! ( tokens [ idx ] = = . doc_comment ) )
2021-04-04 00:03:25 +01:00
return null ;
2021-04-15 10:07:43 +01:00
return while ( tokens [ idx ] = = . doc_comment ) {
2021-04-04 00:12:57 +01:00
if ( idx = = 0 ) break 0 ;
2023-03-12 06:53:05 +00:00
idx - | = 1 ;
2021-04-04 00:12:57 +01:00
} else idx + 1 ;
2021-04-04 00:03:25 +01:00
}
2022-12-27 06:47:57 +00:00
pub fn collectDocComments ( allocator : std . mem . Allocator , tree : Ast , doc_comments : Ast . TokenIndex , format : types . MarkupKind , container_doc : bool ) ! [ ] const u8 {
2020-05-14 17:07:46 +01:00
var lines = std . ArrayList ( [ ] const u8 ) . init ( allocator ) ;
defer lines . deinit ( ) ;
2021-04-04 00:03:25 +01:00
const tokens = tree . tokens . items ( . tag ) ;
2021-02-27 15:38:06 +00:00
var curr_line_tok = doc_comments ;
2020-05-23 23:21:02 +01:00
while ( true ) : ( curr_line_tok + = 1 ) {
2021-04-15 10:07:43 +01:00
const comm = tokens [ curr_line_tok ] ;
2021-04-15 10:21:49 +01:00
if ( ( container_doc and comm = = . container_doc_comment ) or ( ! container_doc and comm = = . doc_comment ) ) {
2022-12-10 21:21:55 +00:00
try lines . append ( std . mem . trim ( u8 , tree . tokenSlice ( curr_line_tok ) [ 3 . . ] , & std . ascii . whitespace ) ) ;
2021-04-15 10:07:43 +01:00
} else break ;
2020-05-14 17:07:46 +01:00
}
2022-12-27 06:47:57 +00:00
return try std . mem . join ( allocator , if ( format = = . markdown ) " \n " else " \n " , lines . items ) ;
2020-05-03 22:27:08 +01:00
}
2021-04-15 10:07:43 +01:00
/// Gets a function's keyword, name, arguments and return value.
2021-10-01 02:57:45 +01:00
pub fn getFunctionSignature ( tree : Ast , func : Ast . full . FnProto ) [ ] const u8 {
2022-09-16 01:33:49 +01:00
const start = offsets . tokenToLoc ( tree , func . ast . fn_token ) ;
2021-04-15 10:07:43 +01:00
const end = if ( func . ast . return_type ! = 0 )
2022-09-16 01:33:49 +01:00
offsets . tokenToLoc ( tree , ast . lastToken ( tree , func . ast . return_type ) )
2021-04-15 10:21:49 +01:00
else
start ;
2021-04-15 10:07:43 +01:00
return tree . source [ start . start . . end . end ] ;
2020-05-03 22:27:08 +01:00
}
2020-05-04 03:17:19 +01:00
2022-07-14 13:44:22 +01:00
fn formatSnippetPlaceholder (
data : [ ] const u8 ,
comptime fmt : [ ] const u8 ,
options : std . fmt . FormatOptions ,
writer : anytype ,
) ! void {
_ = fmt ;
_ = options ;
2023-03-15 16:20:40 +00:00
var split_it = std . mem . split ( u8 , data , " } " ) ;
while ( split_it . next ( ) ) | segment | {
2022-07-14 13:44:22 +01:00
try writer . writeAll ( segment ) ;
2023-03-15 16:20:40 +00:00
if ( split_it . index ) | index |
2022-07-14 13:44:22 +01:00
if ( data [ index - 1 ] = = '}' ) {
try writer . writeAll ( " \\ } " ) ;
} ;
}
}
const SnippetPlaceholderFormatter = std . fmt . Formatter ( formatSnippetPlaceholder ) ;
fn fmtSnippetPlaceholder ( bytes : [ ] const u8 ) SnippetPlaceholderFormatter {
return . { . data = bytes } ;
}
2021-04-15 10:07:43 +01:00
/// Creates snippet insert text for a function. Caller owns returned memory.
2021-12-02 05:16:15 +00:00
pub fn getFunctionSnippet ( allocator : std . mem . Allocator , tree : Ast , func : Ast . full . FnProto , skip_self_param : bool ) ! [ ] const u8 {
2021-03-28 15:02:48 +01:00
const name_index = func . name_token . ? ;
2020-05-09 03:38:08 +01:00
2022-08-23 11:44:26 +01:00
var buffer = std . ArrayListUnmanaged ( u8 ) { } ;
try buffer . ensureTotalCapacity ( allocator , 128 ) ;
2020-05-09 03:38:08 +01:00
2022-08-23 11:44:26 +01:00
var buf_stream = buffer . writer ( allocator ) ;
2020-05-09 03:38:08 +01:00
2022-08-23 11:44:26 +01:00
try buf_stream . writeAll ( tree . tokenSlice ( name_index ) ) ;
try buf_stream . writeByte ( '(' ) ;
2020-05-09 03:38:08 +01:00
2021-02-27 15:38:06 +00:00
const token_tags = tree . tokens . items ( . tag ) ;
2020-05-09 03:38:08 +01:00
2022-04-01 00:01:43 +01:00
var it = func . iterate ( & tree ) ;
2021-03-09 18:53:59 +00:00
var i : usize = 0 ;
2022-08-25 21:17:38 +01:00
while ( ast . nextFnParam ( & it ) ) | param | : ( i + = 1 ) {
2021-03-09 18:53:59 +00:00
if ( skip_self_param and i = = 0 ) continue ;
if ( i ! = @boolToInt ( skip_self_param ) )
2022-08-23 11:44:26 +01:00
try buf_stream . writeAll ( " , ${ " )
2021-03-09 18:53:59 +00:00
else
2022-08-23 11:44:26 +01:00
try buf_stream . writeAll ( " ${ " ) ;
2020-05-09 03:38:08 +01:00
2021-03-09 18:53:59 +00:00
try buf_stream . print ( " {d}: " , . { i + 1 } ) ;
2020-05-12 17:59:16 +01:00
2021-02-27 15:38:06 +00:00
if ( param . comptime_noalias ) | token_index | {
if ( token_tags [ token_index ] = = . keyword_comptime )
2022-08-23 11:44:26 +01:00
try buf_stream . writeAll ( " comptime " )
2021-02-27 15:38:06 +00:00
else
2022-08-23 11:44:26 +01:00
try buf_stream . writeAll ( " noalias " ) ;
2020-05-12 17:59:16 +01:00
}
2020-05-23 23:21:02 +01:00
if ( param . name_token ) | name_token | {
2022-07-14 13:44:22 +01:00
try buf_stream . print ( " {} " , . { fmtSnippetPlaceholder ( tree . tokenSlice ( name_token ) ) } ) ;
2022-08-23 11:44:26 +01:00
try buf_stream . writeAll ( " : " ) ;
2020-05-12 17:59:16 +01:00
}
2021-02-27 15:38:06 +00:00
if ( param . anytype_ellipsis3 ) | token_index | {
if ( token_tags [ token_index ] = = . keyword_anytype )
2022-08-23 11:44:26 +01:00
try buf_stream . writeAll ( " anytype " )
2021-02-27 15:38:06 +00:00
else
2022-08-23 11:44:26 +01:00
try buf_stream . writeAll ( " ... " ) ;
2021-03-09 18:53:59 +00:00
} else if ( param . type_expr ! = 0 ) {
2021-03-04 12:26:11 +00:00
var curr_token = tree . firstToken ( param . type_expr ) ;
2021-10-03 00:39:24 +01:00
var end_token = ast . lastToken ( tree , param . type_expr ) ;
2021-02-27 15:38:06 +00:00
while ( curr_token < = end_token ) : ( curr_token + = 1 ) {
const tag = token_tags [ curr_token ] ;
const is_comma = tag = = . comma ;
if ( curr_token = = end_token and is_comma ) continue ;
2022-07-14 13:44:22 +01:00
try buf_stream . print ( " {} " , . { fmtSnippetPlaceholder ( tree . tokenSlice ( curr_token ) ) } ) ;
2022-08-23 11:44:26 +01:00
if ( is_comma or tag = = . keyword_const ) try buf_stream . writeByte ( ' ' ) ;
2021-02-27 15:38:06 +00:00
}
2022-08-25 21:17:38 +01:00
} // else Incomplete and that's ok :)
2021-03-09 18:53:59 +00:00
2022-08-23 11:44:26 +01:00
try buf_stream . writeByte ( '}' ) ;
2020-05-09 03:38:08 +01:00
}
2022-08-23 11:44:26 +01:00
try buf_stream . writeByte ( ')' ) ;
2020-05-09 03:38:08 +01:00
2022-08-23 11:44:26 +01:00
return buffer . toOwnedSlice ( allocator ) ;
2020-05-09 03:38:08 +01:00
}
2023-03-12 22:48:31 +00:00
pub fn hasSelfParam ( analyser : * Analyser , handle : * const DocumentStore . Handle , func : Ast . full . FnProto ) ! bool {
2021-04-03 16:54:26 +01:00
// Non-decl prototypes cannot have a self parameter.
if ( func . name_token = = null ) return false ;
if ( func . ast . params . len = = 0 ) return false ;
const tree = handle . tree ;
2022-04-01 00:01:43 +01:00
var it = func . iterate ( & tree ) ;
2022-08-25 21:17:38 +01:00
const param = ast . nextFnParam ( & it ) . ? ;
2021-04-03 16:54:26 +01:00
if ( param . type_expr = = 0 ) return false ;
const token_starts = tree . tokens . items ( . start ) ;
const in_container = innermostContainer ( handle , token_starts [ func . ast . fn_token ] ) ;
2023-03-12 22:48:31 +00:00
if ( try analyser . resolveTypeOfNode ( . {
2021-04-03 16:54:26 +01:00
. node = param . type_expr ,
. handle = handle ,
} ) ) | resolved_type | {
if ( std . meta . eql ( in_container , resolved_type ) )
return true ;
}
2023-01-24 23:55:38 +00:00
if ( ast . fullPtrType ( tree , param . type_expr ) ) | ptr_type | {
2023-03-12 22:48:31 +00:00
if ( try analyser . resolveTypeOfNode ( . {
2023-01-24 23:55:38 +00:00
. node = ptr_type . ast . child_type ,
2021-04-03 16:54:26 +01:00
. handle = handle ,
} ) ) | resolved_prefix_op | {
if ( std . meta . eql ( in_container , resolved_prefix_op ) )
return true ;
}
}
return false ;
}
2021-10-01 02:57:45 +01:00
pub fn getVariableSignature ( tree : Ast , var_decl : Ast . full . VarDecl ) [ ] const u8 {
2022-09-16 01:33:49 +01:00
const start = offsets . tokenToIndex ( tree , var_decl . ast . mut_token ) ;
const end = offsets . tokenToLoc ( tree , ast . lastToken ( tree , var_decl . ast . init_node ) ) . end ;
2020-05-04 03:17:19 +01:00
return tree . source [ start . . end ] ;
}
2021-10-01 02:57:45 +01:00
pub fn getContainerFieldSignature ( tree : Ast , field : Ast . full . ContainerField ) [ ] const u8 {
2022-08-23 11:44:26 +01:00
if ( field . ast . value_expr = = 0 and field . ast . type_expr = = 0 and field . ast . align_expr = = 0 ) {
2022-08-20 17:28:41 +01:00
return " " ; // TODO display the container's type
}
2022-11-26 17:22:16 +00:00
const start = offsets . tokenToIndex ( tree , field . ast . main_token ) ;
2021-03-02 21:01:13 +00:00
const end_node = if ( field . ast . value_expr ! = 0 ) field . ast . value_expr else field . ast . type_expr ;
2022-09-16 01:33:49 +01:00
const end = offsets . tokenToLoc ( tree , ast . lastToken ( tree , end_node ) ) . end ;
2021-03-05 21:38:42 +00:00
return tree . source [ start . . end ] ;
2020-06-10 23:00:13 +01:00
}
2021-04-05 12:46:05 +01:00
/// The node is the meta-type `type`
2021-10-01 02:57:45 +01:00
fn isMetaType ( tree : Ast , node : Ast . Node . Index ) bool {
2021-02-26 20:26:52 +00:00
if ( tree . nodes . items ( . tag ) [ node ] = = . identifier ) {
2021-03-02 21:01:13 +00:00
return std . mem . eql ( u8 , tree . tokenSlice ( tree . nodes . items ( . main_token ) [ node ] ) , " type " ) ;
2020-06-12 12:56:46 +01:00
}
return false ;
}
2021-10-01 02:57:45 +01:00
pub fn isTypeFunction ( tree : Ast , func : Ast . full . FnProto ) bool {
2021-04-05 12:46:05 +01:00
return isMetaType ( tree , func . ast . return_type ) ;
2020-05-17 15:23:04 +01:00
}
2021-10-01 02:57:45 +01:00
pub fn isGenericFunction ( tree : Ast , func : Ast . full . FnProto ) bool {
2022-04-01 00:01:43 +01:00
var it = func . iterate ( & tree ) ;
2022-08-25 21:17:38 +01:00
while ( ast . nextFnParam ( & it ) ) | param | {
2021-02-26 20:26:52 +00:00
if ( param . anytype_ellipsis3 ! = null or param . comptime_noalias ! = null ) {
2020-06-27 18:45:58 +01:00
return true ;
}
}
return false ;
}
2021-04-15 10:07:43 +01:00
2020-05-04 03:17:19 +01:00
// STYLE
pub fn isCamelCase ( name : [ ] const u8 ) bool {
2021-04-15 10:07:43 +01:00
return ! std . ascii . isUpper ( name [ 0 ] ) and ! isSnakeCase ( name ) ;
2020-05-04 03:17:19 +01:00
}
2020-05-08 16:01:34 +01:00
pub fn isPascalCase ( name : [ ] const u8 ) bool {
2021-04-15 10:07:43 +01:00
return std . ascii . isUpper ( name [ 0 ] ) and ! isSnakeCase ( name ) ;
}
pub fn isSnakeCase ( name : [ ] const u8 ) bool {
return std . mem . indexOf ( u8 , name , " _ " ) ! = null ;
2020-05-08 16:01:34 +01:00
}
2020-05-11 13:28:08 +01:00
// ANALYSIS ENGINE
2021-10-01 02:57:45 +01:00
pub fn getDeclNameToken ( tree : Ast , node : Ast . Node . Index ) ? Ast . TokenIndex {
2021-02-26 20:26:52 +00:00
const tags = tree . nodes . items ( . tag ) ;
2023-03-12 06:24:54 +00:00
const datas = tree . nodes . items ( . data ) ;
2021-03-02 21:01:13 +00:00
const main_token = tree . nodes . items ( . main_token ) [ node ] ;
2023-03-12 06:24:54 +00:00
2021-03-02 21:01:13 +00:00
return switch ( tags [ node ] ) {
2021-02-26 20:26:52 +00:00
// regular declaration names. + 1 to mut token because name comes after 'const'/'var'
2022-12-15 18:03:09 +00:00
. local_var_decl ,
. global_var_decl ,
. simple_var_decl ,
. aligned_var_decl ,
2023-03-12 06:53:05 +00:00
= > {
const tok = tree . fullVarDecl ( node ) . ? . ast . mut_token + 1 ;
return if ( tok > = tree . tokens . len )
null
else
tok ;
} ,
2021-02-26 20:26:52 +00:00
// function declaration names
2021-03-02 14:32:38 +00:00
. fn_proto ,
. fn_proto_multi ,
. fn_proto_one ,
. fn_proto_simple ,
. fn_decl ,
2021-03-02 21:01:13 +00:00
= > blk : {
2021-10-01 02:44:06 +01:00
var params : [ 1 ] Ast . Node . Index = undefined ;
2023-01-24 23:55:38 +00:00
break : blk tree . fullFnProto ( & params , node ) . ? . name_token ;
2020-05-28 13:41:40 +01:00
} ,
2021-02-26 20:26:52 +00:00
// containers
2023-01-06 18:59:20 +00:00
. container_field ,
. container_field_init ,
. container_field_align ,
= > {
2023-01-24 23:55:38 +00:00
const field = tree . fullContainerField ( node ) . ? . ast ;
2023-01-06 18:59:20 +00:00
return field . main_token ;
2022-11-26 17:22:16 +00:00
} ,
2021-03-02 21:01:13 +00:00
. identifier = > main_token ,
2023-03-12 06:53:05 +00:00
. error_value = > {
const tok = main_token + 2 ;
return if ( tok > = tree . tokens . len )
null
else
tok ;
} , // 'error'.<main_token +2>
2021-02-26 20:26:52 +00:00
2023-03-12 06:24:54 +00:00
. test_decl = > datas [ node ] . lhs ,
2021-03-02 21:01:13 +00:00
else = > null ,
} ;
2020-05-18 21:19:23 +01:00
}
2022-10-28 04:59:24 +01:00
pub fn getDeclName ( tree : Ast , node : Ast . Node . Index ) ? [ ] const u8 {
2023-03-12 06:24:54 +00:00
const name_token = getDeclNameToken ( tree , node ) orelse return null ;
const name = offsets . tokenToSlice ( tree , name_token ) ;
if ( tree . nodes . items ( . tag ) [ node ] = = . test_decl and
tree . tokens . items ( . tag ) [ name_token ] = = . string_literal )
{
return name [ 1 . . name . len - 1 ] ;
}
return name ;
2020-05-18 21:19:23 +01:00
}
2023-03-12 22:48:31 +00:00
fn resolveVarDeclAliasInternal ( analyser : * Analyser , node_handle : NodeWithHandle ) error { OutOfMemory } ! ? DeclWithHandle {
2020-06-15 01:59:49 +01:00
const handle = node_handle . handle ;
2021-03-01 13:32:19 +00:00
const tree = handle . tree ;
const node_tags = tree . nodes . items ( . tag ) ;
const main_tokens = tree . nodes . items ( . main_token ) ;
const datas = tree . nodes . items ( . data ) ;
if ( node_tags [ node_handle . node ] = = . identifier ) {
const token = main_tokens [ node_handle . node ] ;
2023-03-12 22:48:31 +00:00
return try analyser . lookupSymbolGlobal (
2021-03-01 13:32:19 +00:00
handle ,
tree . tokenSlice ( token ) ,
2021-03-03 15:34:24 +00:00
tree . tokens . items ( . start ) [ token ] ,
2021-03-01 13:32:19 +00:00
) ;
2020-06-15 01:59:49 +01:00
}
2021-03-01 13:32:19 +00:00
if ( node_tags [ node_handle . node ] = = . field_access ) {
const lhs = datas [ node_handle . node ] . lhs ;
2020-06-15 01:59:49 +01:00
2021-10-03 00:39:24 +01:00
const container_node = if ( ast . isBuiltinCall ( tree , lhs ) ) block : {
2022-08-18 23:00:46 +01:00
const name = tree . tokenSlice ( main_tokens [ lhs ] ) ;
if ( ! std . mem . eql ( u8 , name , " @import " ) and ! std . mem . eql ( u8 , name , " @cImport " ) )
2020-06-15 01:59:49 +01:00
return null ;
2021-03-01 13:32:19 +00:00
2023-03-12 22:48:31 +00:00
const inner_node = ( try analyser . resolveTypeOfNode ( . { . node = lhs , . handle = handle } ) ) orelse return null ;
2021-03-01 13:32:19 +00:00
// assert root node
std . debug . assert ( inner_node . type . data . other = = 0 ) ;
2020-06-17 03:12:12 +01:00
break : block NodeWithHandle { . node = inner_node . type . data . other , . handle = inner_node . handle } ;
2023-03-12 22:48:31 +00:00
} else if ( try analyser . resolveVarDeclAliasInternal ( . { . node = lhs , . handle = handle } ) ) | decl_handle | block : {
2020-06-15 02:39:50 +01:00
if ( decl_handle . decl . * ! = . ast_node ) return null ;
2023-03-12 22:48:31 +00:00
const resolved = ( try analyser . resolveTypeOfNode ( . { . node = decl_handle . decl . ast_node , . handle = decl_handle . handle } ) ) orelse return null ;
2020-06-17 03:12:12 +01:00
const resolved_node = switch ( resolved . type . data ) {
. other = > | n | n ,
else = > return null ,
} ;
2021-10-03 00:39:24 +01:00
if ( ! ast . isContainer ( resolved . handle . tree , resolved_node ) ) return null ;
2020-06-17 03:12:12 +01:00
break : block NodeWithHandle { . node = resolved_node , . handle = resolved . handle } ;
2020-06-15 01:59:49 +01:00
} else return null ;
2023-03-12 22:48:31 +00:00
return try analyser . lookupSymbolContainer ( container_node , tree . tokenSlice ( datas [ node_handle . node ] . rhs ) , false ) ;
2020-06-15 01:59:49 +01:00
}
return null ;
}
/// Resolves variable declarations consisting of chains of imports and field accesses of containers, ending with the same name as the variable decl's name
/// Examples:
///```zig
/// const decl = @import("decl-file.zig").decl;
/// const other = decl.middle.other;
///```
2023-03-12 22:48:31 +00:00
pub fn resolveVarDeclAlias ( analyser : * Analyser , decl_handle : NodeWithHandle ) ! ? DeclWithHandle {
2020-06-14 23:19:21 +01:00
const decl = decl_handle . node ;
const handle = decl_handle . handle ;
2021-03-01 13:32:19 +00:00
const tree = handle . tree ;
const token_tags = tree . tokens . items ( . tag ) ;
const node_tags = tree . nodes . items ( . tag ) ;
2020-06-14 23:19:21 +01:00
2023-01-24 23:55:38 +00:00
if ( handle . tree . fullVarDecl ( decl ) ) | var_decl | {
2021-03-01 13:32:19 +00:00
if ( var_decl . ast . init_node = = 0 ) return null ;
const base_exp = var_decl . ast . init_node ;
2021-03-03 15:34:24 +00:00
if ( token_tags [ var_decl . ast . mut_token ] ! = . keyword_const ) return null ;
2020-06-14 23:19:21 +01:00
2021-03-01 13:32:19 +00:00
if ( node_tags [ base_exp ] = = . field_access ) {
2021-03-03 15:34:24 +00:00
const name = tree . tokenSlice ( tree . nodes . items ( . data ) [ base_exp ] . rhs ) ;
2021-03-01 13:32:19 +00:00
if ( ! std . mem . eql ( u8 , tree . tokenSlice ( var_decl . ast . mut_token + 1 ) , name ) )
2020-06-15 01:59:49 +01:00
return null ;
2020-06-14 23:19:21 +01:00
2023-03-12 22:48:31 +00:00
return try analyser . resolveVarDeclAliasInternal ( . { . node = base_exp , . handle = handle } ) ;
2020-06-14 23:19:21 +01:00
}
}
return null ;
}
2021-10-01 02:57:45 +01:00
fn findReturnStatementInternal ( tree : Ast , fn_decl : Ast . full . FnProto , body : Ast . Node . Index , already_found : * bool ) ? Ast . Node . Index {
2021-10-01 02:44:06 +01:00
var result : ? Ast . Node . Index = null ;
2021-03-06 19:55:59 +00:00
const node_tags = tree . nodes . items ( . tag ) ;
const datas = tree . nodes . items ( . data ) ;
2022-08-17 23:52:21 +01:00
var buffer : [ 2 ] Ast . Node . Index = undefined ;
const statements = ast . blockStatements ( tree , body , & buffer ) orelse return null ;
2021-03-06 19:55:59 +00:00
for ( statements ) | child_idx | {
if ( node_tags [ child_idx ] = = . @ " return " ) {
if ( datas [ child_idx ] . lhs ! = 0 ) {
const lhs = datas [ child_idx ] . lhs ;
2023-01-24 23:55:38 +00:00
var buf : [ 1 ] Ast . Node . Index = undefined ;
if ( tree . fullCall ( & buf , lhs ) ) | call | {
const call_name = getDeclName ( tree , call . ast . fn_expr ) ;
2021-03-06 19:55:59 +00:00
if ( call_name ) | name | {
if ( std . mem . eql ( u8 , name , tree . tokenSlice ( fn_decl . name_token . ? ) ) ) {
2020-07-24 08:33:13 +01:00
continue ;
2020-05-24 13:24:18 +01:00
}
}
2020-05-19 14:42:36 +01:00
}
2020-07-24 08:33:13 +01:00
}
2020-06-14 21:43:29 +01:00
2020-07-24 08:33:13 +01:00
if ( already_found . * ) return null ;
already_found . * = true ;
2021-03-06 19:55:59 +00:00
result = child_idx ;
2020-07-24 08:33:13 +01:00
continue ;
2020-05-19 14:42:36 +01:00
}
2021-03-06 19:55:59 +00:00
result = findReturnStatementInternal ( tree , fn_decl , child_idx , already_found ) ;
2020-05-19 14:42:36 +01:00
}
2021-03-06 19:55:59 +00:00
2020-05-19 14:42:36 +01:00
return result ;
}
2021-10-01 02:57:45 +01:00
fn findReturnStatement ( tree : Ast , fn_decl : Ast . full . FnProto , body : Ast . Node . Index ) ? Ast . Node . Index {
2020-05-19 14:42:36 +01:00
var already_found = false ;
2021-03-06 19:55:59 +00:00
return findReturnStatementInternal ( tree , fn_decl , body , & already_found ) ;
2020-05-19 14:42:36 +01:00
}
2023-03-12 22:48:31 +00:00
fn resolveReturnType ( analyser : * Analyser , fn_decl : Ast . full . FnProto , handle : * const DocumentStore . Handle , fn_body : ? Ast . Node . Index ) ! ? TypeWithHandle {
2021-03-03 15:34:24 +00:00
const tree = handle . tree ;
if ( isTypeFunction ( tree , fn_decl ) and fn_body ! = null ) {
2021-03-06 19:55:59 +00:00
// If this is a type function and it only contains a single return statement that returns
// a container declaration, we will return that declaration.
const ret = findReturnStatement ( tree , fn_decl , fn_body . ? ) orelse return null ;
const data = tree . nodes . items ( . data ) [ ret ] ;
if ( data . lhs ! = 0 ) {
2023-03-12 22:48:31 +00:00
return try analyser . resolveTypeOfNodeInternal ( . { . node = data . lhs , . handle = handle } ) ;
2021-03-06 19:55:59 +00:00
}
return null ;
2021-03-03 15:34:24 +00:00
}
if ( fn_decl . ast . return_type = = 0 ) return null ;
2021-03-07 17:45:37 +00:00
const return_type = fn_decl . ast . return_type ;
2021-04-05 14:26:06 +01:00
const ret = . { . node = return_type , . handle = handle } ;
2023-03-12 22:48:31 +00:00
const child_type = ( try analyser . resolveTypeOfNodeInternal ( ret ) ) orelse
2021-04-05 14:26:06 +01:00
return null ;
2021-03-07 17:45:37 +00:00
const is_inferred_error = tree . tokens . items ( . tag ) [ tree . firstToken ( return_type ) - 1 ] = = . bang ;
2021-04-05 14:26:06 +01:00
if ( is_inferred_error ) {
2021-03-07 17:45:37 +00:00
const child_type_node = switch ( child_type . type . data ) {
. other = > | n | n ,
else = > return null ,
} ;
2021-04-05 14:26:06 +01:00
return TypeWithHandle {
2021-03-07 17:45:37 +00:00
. type = . { . data = . { . error_union = child_type_node } , . is_type_val = false } ,
. handle = child_type . handle ,
} ;
2021-04-15 10:21:49 +01:00
} else return child_type . instanceTypeVal ( ) ;
2020-05-19 14:42:36 +01:00
}
2020-05-27 16:49:11 +01:00
/// Resolves the child type of an optional type
2023-03-12 22:48:31 +00:00
fn resolveUnwrapOptionalType ( analyser : * Analyser , opt : TypeWithHandle ) ! ? TypeWithHandle {
2020-06-17 03:12:12 +01:00
const opt_node = switch ( opt . type . data ) {
. other = > | n | n ,
else = > return null ,
} ;
2021-02-28 20:57:15 +00:00
if ( opt . handle . tree . nodes . items ( . tag ) [ opt_node ] = = . optional_type ) {
2023-03-12 22:48:31 +00:00
return ( ( try analyser . resolveTypeOfNodeInternal ( . {
2021-02-28 20:57:15 +00:00
. node = opt . handle . tree . nodes . items ( . data ) [ opt_node ] . lhs ,
. handle = opt . handle ,
2023-03-12 22:48:31 +00:00
} ) ) orelse return null ) . instanceTypeVal ( ) ;
2020-05-27 16:49:11 +01:00
}
return null ;
}
2023-03-12 22:48:31 +00:00
fn resolveUnwrapErrorType ( analyser : * Analyser , rhs : TypeWithHandle ) ! ? TypeWithHandle {
2020-06-17 03:12:12 +01:00
const rhs_node = switch ( rhs . type . data ) {
. other = > | n | n ,
. error_union = > | n | return TypeWithHandle {
2020-06-17 13:07:21 +01:00
. type = . { . data = . { . other = n } , . is_type_val = rhs . type . is_type_val } ,
2020-06-17 03:12:12 +01:00
. handle = rhs . handle ,
} ,
2023-03-07 17:17:45 +00:00
. primitive , . slice , . pointer , . array_index , . @ " comptime " , . either = > return null ,
2020-06-17 03:12:12 +01:00
} ;
2021-03-07 17:45:37 +00:00
2021-02-28 20:57:15 +00:00
if ( rhs . handle . tree . nodes . items ( . tag ) [ rhs_node ] = = . error_union ) {
2023-03-12 22:48:31 +00:00
return ( ( try analyser . resolveTypeOfNodeInternal ( . {
2021-02-28 20:57:15 +00:00
. node = rhs . handle . tree . nodes . items ( . data ) [ rhs_node ] . rhs ,
. handle = rhs . handle ,
2023-03-12 22:48:31 +00:00
} ) ) orelse return null ) . instanceTypeVal ( ) ;
2020-06-11 00:40:11 +01:00
}
return null ;
}
2020-07-16 20:02:30 +01:00
/// Resolves the child type of a deref type
2023-03-12 22:48:31 +00:00
fn resolveDerefType ( analyser : * Analyser , deref : TypeWithHandle ) ! ? TypeWithHandle {
2020-06-17 03:12:12 +01:00
const deref_node = switch ( deref . type . data ) {
. other = > | n | n ,
2021-03-30 14:45:49 +01:00
. pointer = > | n | return TypeWithHandle {
. type = . {
. is_type_val = false ,
. data = . { . other = n } ,
} ,
. handle = deref . handle ,
} ,
2020-06-17 03:12:12 +01:00
else = > return null ,
} ;
2021-02-28 16:42:34 +00:00
const tree = deref . handle . tree ;
const main_token = tree . nodes . items ( . main_token ) [ deref_node ] ;
const token_tag = tree . tokens . items ( . tag ) [ main_token ] ;
2020-06-17 03:12:12 +01:00
2023-01-24 23:55:38 +00:00
if ( ast . fullPtrType ( tree , deref_node ) ) | ptr_type | {
2021-02-28 16:42:34 +00:00
switch ( token_tag ) {
. asterisk = > {
2023-03-12 22:48:31 +00:00
return ( ( try analyser . resolveTypeOfNodeInternal ( . {
2021-03-03 15:34:24 +00:00
. node = ptr_type . ast . child_type ,
2020-07-23 18:30:03 +01:00
. handle = deref . handle ,
2023-03-12 22:48:31 +00:00
} ) ) orelse return null ) . instanceTypeVal ( ) ;
2020-07-23 18:30:03 +01:00
} ,
2021-02-28 16:42:34 +00:00
. l_bracket , . asterisk_asterisk = > return null ,
2020-07-23 18:30:03 +01:00
else = > unreachable ,
2020-05-27 16:49:11 +01:00
}
}
return null ;
}
2021-04-05 14:26:06 +01:00
/// Resolves slicing and array access
2023-03-12 22:48:31 +00:00
fn resolveBracketAccessType ( analyser : * Analyser , lhs : TypeWithHandle , rhs : enum { Single , Range } ) ! ? TypeWithHandle {
2020-06-17 03:12:12 +01:00
const lhs_node = switch ( lhs . type . data ) {
. other = > | n | n ,
else = > return null ,
} ;
2021-03-01 13:32:19 +00:00
const tree = lhs . handle . tree ;
const tags = tree . nodes . items ( . tag ) ;
2021-02-28 20:57:15 +00:00
const tag = tags [ lhs_node ] ;
2021-03-01 13:32:19 +00:00
const data = tree . nodes . items ( . data ) [ lhs_node ] ;
2021-03-08 18:46:23 +00:00
2021-02-28 20:57:15 +00:00
if ( tag = = . array_type or tag = = . array_type_sentinel ) {
2020-07-23 19:33:43 +01:00
if ( rhs = = . Single )
2023-03-12 22:48:31 +00:00
return ( ( try analyser . resolveTypeOfNodeInternal ( . {
2021-02-28 20:57:15 +00:00
. node = data . rhs ,
2020-07-23 19:33:43 +01:00
. handle = lhs . handle ,
2023-03-12 22:48:31 +00:00
} ) ) orelse return null ) . instanceTypeVal ( ) ;
2020-07-23 19:33:43 +01:00
return TypeWithHandle {
2021-02-28 20:57:15 +00:00
. type = . { . data = . { . slice = data . rhs } , . is_type_val = false } ,
2020-07-23 19:33:43 +01:00
. handle = lhs . handle ,
} ;
2023-01-24 23:55:38 +00:00
} else if ( ast . fullPtrType ( tree , lhs_node ) ) | ptr_type | {
2021-03-08 18:46:23 +00:00
if ( ptr_type . size = = . Slice ) {
2020-07-23 19:33:43 +01:00
if ( rhs = = . Single ) {
2023-03-12 22:48:31 +00:00
return ( ( try analyser . resolveTypeOfNodeInternal ( . {
2021-03-08 18:46:23 +00:00
. node = ptr_type . ast . child_type ,
2020-06-17 03:12:12 +01:00
. handle = lhs . handle ,
2023-03-12 22:48:31 +00:00
} ) ) orelse return null ) . instanceTypeVal ( ) ;
2020-07-23 19:33:43 +01:00
}
2021-03-03 15:34:24 +00:00
return lhs ;
2020-05-27 16:49:11 +01:00
}
}
2020-07-23 19:33:43 +01:00
2020-05-27 16:49:11 +01:00
return null ;
}
/// Called to remove one level of pointerness before a field access
2023-03-12 22:48:31 +00:00
pub fn resolveFieldAccessLhsType ( analyser : * Analyser , lhs : TypeWithHandle ) ! TypeWithHandle {
// analyser.bound_type_params.clearRetainingCapacity();
return ( try analyser . resolveDerefType ( lhs ) ) orelse lhs ;
2020-05-27 16:49:11 +01:00
}
2020-06-17 21:36:40 +01:00
fn allDigits ( str : [ ] const u8 ) bool {
for ( str ) | c | {
if ( ! std . ascii . isDigit ( c ) ) return false ;
}
return true ;
}
2023-03-12 16:31:17 +00:00
pub fn isValueIdent ( text : [ ] const u8 ) bool {
const PrimitiveTypes = std . ComptimeStringMap ( void , . {
. { " true " } ,
. { " false " } ,
. { " null " } ,
. { " undefined " } ,
} ) ;
return PrimitiveTypes . has ( text ) ;
}
2022-08-05 13:20:16 +01:00
pub fn isTypeIdent ( text : [ ] const u8 ) bool {
2020-06-17 21:36:40 +01:00
const PrimitiveTypes = std . ComptimeStringMap ( void , . {
2022-02-14 13:18:56 +00:00
. { " isize " } , . { " usize " } ,
. { " c_short " } , . { " c_ushort " } ,
. { " c_int " } , . { " c_uint " } ,
. { " c_long " } , . { " c_ulong " } ,
. { " c_longlong " } , . { " c_ulonglong " } ,
. { " c_longdouble " } , . { " anyopaque " } ,
. { " f16 " } , . { " f32 " } ,
. { " f64 " } , . { " f80 " } ,
. { " f128 " } , . { " bool " } ,
. { " void " } , . { " noreturn " } ,
. { " type " } , . { " anyerror " } ,
. { " comptime_int " } , . { " comptime_float " } ,
. { " anyframe " } , . { " anytype " } ,
2023-04-30 18:21:43 +01:00
. { " c_char " } ,
2020-06-17 21:36:40 +01:00
} ) ;
if ( PrimitiveTypes . has ( text ) ) return true ;
2021-10-01 02:46:14 +01:00
if ( text . len = = 1 ) return false ;
if ( ! ( text [ 0 ] = = 'u' or text [ 0 ] = = 'i' ) ) return false ;
if ( ! allDigits ( text [ 1 . . ] ) ) return false ;
2021-10-01 02:46:35 +01:00
_ = std . fmt . parseUnsigned ( u16 , text [ 1 . . ] , 10 ) catch return false ;
2021-10-01 02:46:14 +01:00
return true ;
2020-06-17 21:36:40 +01:00
}
2020-06-12 12:56:46 +01:00
2020-05-13 14:03:33 +01:00
/// Resolves the type of a node
2023-03-12 22:48:31 +00:00
fn resolveTypeOfNodeInternal ( analyser : * Analyser , node_handle : NodeWithHandle ) error { OutOfMemory } ! ? TypeWithHandle {
2023-03-13 00:08:22 +00:00
const node_with_uri = NodeWithUri {
. node = node_handle . node ,
. uri = node_handle . handle . uri ,
} ;
const gop = try analyser . resolved_nodes . getOrPut ( analyser . gpa , node_with_uri ) ;
2023-03-12 22:48:31 +00:00
if ( gop . found_existing ) return gop . value_ptr . * ;
// we insert null before resolving the type so that a recursive definition doesn't result in an infinite loop
gop . value_ptr . * = null ;
2021-04-05 12:46:05 +01:00
2023-03-12 22:48:31 +00:00
const type_handle = try analyser . resolveTypeOfNodeUncached ( node_handle ) ;
2023-03-13 00:08:22 +00:00
analyser . resolved_nodes . getPtr ( node_with_uri ) . ? . * = type_handle ;
2023-03-12 22:48:31 +00:00
return type_handle ;
// if (analyser.resolved_nodes.get(node_handle)) |type_handle| return type_handle;
//// If we were asked to resolve this node before,
//// it is self-referential and we cannot resolve it.
//for (analyser.resolve_trail.items) |i| {
// if (std.meta.eql(i, node_handle))
// return null;
//}
//try analyser.resolve_trail.append(analyser.gpa, node_handle);
//defer _ = analyser.resolve_trail.pop();
}
fn resolveTypeOfNodeUncached ( analyser : * Analyser , node_handle : NodeWithHandle ) error { OutOfMemory } ! ? TypeWithHandle {
2021-04-05 14:26:06 +01:00
const node = node_handle . node ;
const handle = node_handle . handle ;
const tree = handle . tree ;
2021-02-28 16:42:34 +00:00
const main_tokens = tree . nodes . items ( . main_token ) ;
const node_tags = tree . nodes . items ( . tag ) ;
const datas = tree . nodes . items ( . data ) ;
const token_tags = tree . tokens . items ( . tag ) ;
const starts = tree . tokens . items ( . start ) ;
2020-06-10 18:48:40 +01:00
2021-02-28 16:42:34 +00:00
switch ( node_tags [ node ] ) {
2021-03-05 21:38:42 +00:00
. global_var_decl ,
. local_var_decl ,
. simple_var_decl ,
. aligned_var_decl ,
= > {
2023-01-24 23:55:38 +00:00
const var_decl = tree . fullVarDecl ( node ) . ? ;
2021-04-05 12:46:05 +01:00
if ( var_decl . ast . type_node ! = 0 ) {
const decl_type = . { . node = var_decl . ast . type_node , . handle = handle } ;
2023-03-12 22:48:31 +00:00
if ( try analyser . resolveTypeOfNodeInternal ( decl_type ) ) | typ |
2021-04-05 12:46:05 +01:00
return typ . instanceTypeVal ( ) ;
2020-06-17 03:12:12 +01:00
}
2021-04-05 12:46:05 +01:00
if ( var_decl . ast . init_node = = 0 )
return null ;
const value = . { . node = var_decl . ast . init_node , . handle = handle } ;
2023-03-12 22:48:31 +00:00
return try analyser . resolveTypeOfNodeInternal ( value ) ;
2020-05-11 13:28:08 +01:00
} ,
2021-02-28 16:42:34 +00:00
. identifier = > {
2022-09-16 01:33:49 +01:00
const name = offsets . nodeToSlice ( tree , node ) ;
2022-08-05 13:20:16 +01:00
if ( isTypeIdent ( name ) ) {
2020-06-17 21:36:40 +01:00
return TypeWithHandle {
2022-07-08 19:29:53 +01:00
. type = . { . data = . { . primitive = node } , . is_type_val = true } ,
2020-06-17 21:36:40 +01:00
. handle = handle ,
} ;
}
2023-03-12 22:48:31 +00:00
if ( try analyser . lookupSymbolGlobal (
2021-03-30 18:59:58 +01:00
handle ,
2022-08-05 13:20:16 +01:00
name ,
2021-03-30 18:59:58 +01:00
starts [ main_tokens [ node ] ] ,
) ) | child | {
2020-06-18 13:12:09 +01:00
switch ( child . decl . * ) {
2020-11-15 18:37:00 +00:00
. ast_node = > | n | {
if ( n = = node ) return null ;
2023-01-24 23:55:38 +00:00
if ( child . handle . tree . fullVarDecl ( n ) ) | var_decl | {
2021-04-05 12:46:05 +01:00
if ( var_decl . ast . init_node = = node )
return null ;
2020-11-15 18:37:00 +00:00
}
} ,
2020-06-18 01:23:56 +01:00
else = > { } ,
}
2023-03-12 22:48:31 +00:00
return try child . resolveType ( analyser ) ;
2020-06-10 18:48:40 +01:00
}
return null ;
2020-05-11 13:28:08 +01:00
} ,
2021-02-28 16:42:34 +00:00
. call ,
. call_comma ,
. async_call ,
. async_call_comma ,
. call_one ,
. call_one_comma ,
. async_call_one ,
. async_call_one_comma ,
2021-06-24 11:38:01 +01:00
= > {
2021-10-01 02:44:06 +01:00
var params : [ 1 ] Ast . Node . Index = undefined ;
2023-01-24 23:55:38 +00:00
const call = tree . fullCall ( & params , node ) orelse unreachable ;
2021-02-28 16:42:34 +00:00
2021-04-05 12:46:05 +01:00
const callee = . { . node = call . ast . fn_expr , . handle = handle } ;
2023-03-12 22:48:31 +00:00
const decl = ( try analyser . resolveTypeOfNodeInternal ( callee ) ) orelse
2021-04-05 12:52:41 +01:00
return null ;
2020-06-02 14:48:23 +01:00
2020-06-17 13:07:21 +01:00
if ( decl . type . is_type_val ) return null ;
2020-06-17 03:12:12 +01:00
const decl_node = switch ( decl . type . data ) {
. other = > | n | n ,
else = > return null ,
} ;
2021-10-01 02:44:06 +01:00
var buf : [ 1 ] Ast . Node . Index = undefined ;
2023-01-24 23:55:38 +00:00
const func_maybe = decl . handle . tree . fullFnProto ( & buf , decl_node ) ;
2021-02-28 16:42:34 +00:00
if ( func_maybe ) | fn_decl | {
2021-04-05 12:46:05 +01:00
var expected_params = fn_decl . ast . params . len ;
// If we call as method, the first parameter should be skipped
// TODO: Back-parse to extract the self argument?
2022-04-01 00:01:43 +01:00
var it = fn_decl . iterate ( & decl . handle . tree ) ;
2021-04-05 12:46:05 +01:00
if ( token_tags [ call . ast . lparen - 2 ] = = . period ) {
2023-03-12 22:48:31 +00:00
if ( try analyser . hasSelfParam ( decl . handle , fn_decl ) ) {
2022-08-25 21:17:38 +01:00
_ = ast . nextFnParam ( & it ) ;
2021-04-05 12:46:05 +01:00
expected_params - = 1 ;
}
}
2020-06-12 12:56:46 +01:00
2021-04-05 12:46:05 +01:00
// Bind type params to the arguments passed in the call.
const param_len = std . math . min ( call . ast . params . len , expected_params ) ;
2021-03-09 18:53:59 +00:00
var i : usize = 0 ;
2022-08-25 21:17:38 +01:00
while ( ast . nextFnParam ( & it ) ) | decl_param | : ( i + = 1 ) {
2021-03-09 18:53:59 +00:00
if ( i > = param_len ) break ;
2021-04-05 12:46:05 +01:00
if ( ! isMetaType ( decl . handle . tree , decl_param . type_expr ) )
continue ;
2020-06-12 12:56:46 +01:00
2021-04-05 12:52:41 +01:00
const argument = . { . node = call . ast . params [ i ] , . handle = handle } ;
2023-03-12 22:48:31 +00:00
const argument_type = ( try analyser . resolveTypeOfNodeInternal (
2021-04-05 12:52:41 +01:00
argument ,
) ) orelse
continue ;
2021-04-05 12:46:05 +01:00
if ( ! argument_type . type . is_type_val ) continue ;
2020-06-12 12:56:46 +01:00
2023-03-12 22:48:31 +00:00
try analyser . bound_type_params . put ( analyser . gpa , decl_param , argument_type ) ;
2020-06-12 12:56:46 +01:00
}
2021-03-03 16:45:42 +00:00
const has_body = decl . handle . tree . nodes . items ( . tag ) [ decl_node ] = = . fn_decl ;
const body = decl . handle . tree . nodes . items ( . data ) [ decl_node ] . rhs ;
2023-03-12 22:48:31 +00:00
if ( try analyser . resolveReturnType ( fn_decl , decl . handle , if ( has_body ) body else null ) ) | ret | {
2022-10-28 19:24:38 +01:00
return ret ;
2023-04-30 18:21:43 +01:00
} else if ( analyser . store . config . dangerous_comptime_experiments_do_not_enable ) {
2022-10-28 19:24:38 +01:00
// TODO: Better case-by-case; we just use the ComptimeInterpreter when all else fails,
// probably better to use it more liberally
// TODO: Handle non-isolate args; e.g. `const T = u8; TypeFunc(T);`
2022-10-29 06:46:22 +01:00
// var interpreter = ComptimeInterpreter{ .tree = tree, .allocator = arena.allocator() };
2022-10-28 19:24:38 +01:00
2022-10-29 06:46:22 +01:00
// var top_decl = try (try interpreter.interpret(0, null, .{})).getValue();
// var top_scope = interpreter.typeToTypeInfo(top_decl.@"type".info_idx).@"struct".scope;
// var fn_decl_scope = top_scope.getParentScopeFromNode(node);
2022-10-29 22:28:44 +01:00
log . info ( " Invoking interpreter! " , . { } ) ;
2023-05-09 05:25:26 +01:00
const interpreter = analyser . store . ensureInterpreterExists ( handle . uri , & analyser . ip . ? ) catch | err | {
2023-01-26 16:14:42 +00:00
log . err ( " Failed to interpret file: {s} " , . { @errorName ( err ) } ) ;
2022-10-29 22:28:44 +01:00
if ( @errorReturnTrace ( ) ) | trace | {
std . debug . dumpStackTrace ( trace . * ) ;
}
2022-10-29 06:46:22 +01:00
return null ;
} ;
2023-01-28 17:02:29 +00:00
const root_namespace = @intToEnum ( ComptimeInterpreter . Namespace . Index , 0 ) ;
2022-10-29 06:46:22 +01:00
// TODO: Start from current/nearest-current scope
2023-01-26 16:14:42 +00:00
const result = interpreter . interpret ( node , root_namespace , . { } ) catch | err | {
log . err ( " Failed to interpret node: {s} " , . { @errorName ( err ) } ) ;
2022-10-29 06:46:22 +01:00
if ( @errorReturnTrace ( ) ) | trace | {
std . debug . dumpStackTrace ( trace . * ) ;
}
2022-10-28 19:24:38 +01:00
return null ;
} ;
2023-01-23 21:23:01 +00:00
const value = result . getValue ( ) catch | err | {
2023-01-26 16:14:42 +00:00
log . err ( " interpreter return no result: {s} " , . { @errorName ( err ) } ) ;
2022-10-29 06:46:22 +01:00
if ( @errorReturnTrace ( ) ) | trace | {
std . debug . dumpStackTrace ( trace . * ) ;
}
2022-10-28 19:24:38 +01:00
return null ;
} ;
2023-02-27 22:53:46 +00:00
const is_type_val = interpreter . ip . indexToKey ( value . index ) . typeOf ( ) = = . type_type ;
2022-10-28 19:24:38 +01:00
return TypeWithHandle {
. type = . {
2022-12-27 01:46:57 +00:00
. data = . { . @ " comptime " = . {
. interpreter = interpreter ,
2023-01-23 21:23:01 +00:00
. value = value ,
2022-12-27 01:46:57 +00:00
} } ,
2023-02-27 22:53:46 +00:00
. is_type_val = is_type_val ,
2022-10-28 19:24:38 +01:00
} ,
. handle = node_handle . handle ,
} ;
}
2020-06-01 18:48:14 +01:00
}
2020-06-17 13:07:21 +01:00
return null ;
2020-05-23 23:21:02 +01:00
} ,
2021-03-05 21:38:42 +00:00
. @ " comptime " ,
. @ " nosuspend " ,
. grouped_expression ,
2021-04-05 14:26:06 +01:00
. container_field ,
. container_field_init ,
. container_field_align ,
2021-03-05 21:38:42 +00:00
. struct_init ,
. struct_init_comma ,
. struct_init_one ,
. struct_init_one_comma ,
. slice ,
. slice_sentinel ,
. slice_open ,
. deref ,
. unwrap_optional ,
2021-04-05 14:26:06 +01:00
. array_access ,
. @ " orelse " ,
. @ " catch " ,
. @ " try " ,
. address_of ,
2021-03-05 21:38:42 +00:00
= > {
2021-04-05 14:26:06 +01:00
const base = . { . node = datas [ node ] . lhs , . handle = handle } ;
2023-03-12 22:48:31 +00:00
const base_type = ( try analyser . resolveTypeOfNodeInternal ( base ) ) orelse
2021-04-05 14:26:06 +01:00
return null ;
2021-02-28 19:37:21 +00:00
return switch ( node_tags [ node ] ) {
2021-04-05 14:26:06 +01:00
. @ " comptime " ,
. @ " nosuspend " ,
. grouped_expression ,
= > base_type ,
. container_field ,
. container_field_init ,
. container_field_align ,
. struct_init ,
. struct_init_comma ,
. struct_init_one ,
. struct_init_one_comma ,
= > base_type . instanceTypeVal ( ) ,
. slice ,
. slice_sentinel ,
. slice_open ,
2023-03-12 22:48:31 +00:00
= > try analyser . resolveBracketAccessType ( base_type , . Range ) ,
. deref = > try analyser . resolveDerefType ( base_type ) ,
. unwrap_optional = > try analyser . resolveUnwrapOptionalType ( base_type ) ,
. array_access = > try analyser . resolveBracketAccessType ( base_type , . Single ) ,
. @ " orelse " = > try analyser . resolveUnwrapOptionalType ( base_type ) ,
. @ " catch " = > try analyser . resolveUnwrapErrorType ( base_type ) ,
. @ " try " = > try analyser . resolveUnwrapErrorType ( base_type ) ,
2021-04-05 14:26:06 +01:00
. address_of = > {
const lhs_node = switch ( base_type . type . data ) {
. other = > | n | n ,
else = > return null ,
} ;
return TypeWithHandle {
. type = . { . data = . { . pointer = lhs_node } , . is_type_val = base_type . type . is_type_val } ,
. handle = base_type . handle ,
} ;
} ,
2020-07-23 17:06:39 +01:00
else = > unreachable ,
2020-07-24 08:33:13 +01:00
} ;
2020-07-23 17:06:39 +01:00
} ,
2021-02-28 19:37:21 +00:00
. field_access = > {
2021-03-03 16:45:42 +00:00
if ( datas [ node ] . rhs = = 0 ) return null ;
2022-09-07 18:14:11 +01:00
2023-03-12 22:48:31 +00:00
const lhs = ( try analyser . resolveTypeOfNodeInternal ( . {
. node = datas [ node ] . lhs ,
. handle = handle ,
} ) ) orelse return null ;
2020-07-16 20:19:08 +01:00
// If we are accessing a pointer type, remove one pointerness level :)
2023-03-12 22:48:31 +00:00
const left_type = ( try analyser . resolveDerefType ( lhs ) ) orelse lhs ;
2020-06-17 03:12:12 +01:00
2020-07-16 20:19:08 +01:00
const left_type_node = switch ( left_type . type . data ) {
. other = > | n | n ,
else = > return null ,
} ;
2022-12-15 22:01:42 +00:00
2023-03-12 22:48:31 +00:00
if ( try analyser . lookupSymbolContainer (
2020-07-16 20:19:08 +01:00
. { . node = left_type_node , . handle = left_type . handle } ,
2022-12-15 22:01:42 +00:00
tree . tokenSlice ( datas [ node ] . rhs ) ,
2021-03-06 19:55:59 +00:00
! left_type . type . is_type_val ,
2020-07-16 20:19:08 +01:00
) ) | child | {
2023-03-12 22:48:31 +00:00
return try child . resolveType ( analyser ) ;
2020-07-16 20:19:08 +01:00
} else return null ;
} ,
2021-02-28 19:37:21 +00:00
. array_type ,
. array_type_sentinel ,
. optional_type ,
. ptr_type_aligned ,
2022-08-05 12:22:55 +01:00
. ptr_type_sentinel ,
2021-02-28 19:37:21 +00:00
. ptr_type ,
. ptr_type_bit_range ,
2021-03-06 19:55:59 +00:00
. error_union ,
2021-04-05 14:26:06 +01:00
. error_set_decl ,
. container_decl ,
. container_decl_arg ,
. container_decl_arg_trailing ,
. container_decl_trailing ,
. container_decl_two ,
. container_decl_two_trailing ,
. tagged_union ,
. tagged_union_trailing ,
. tagged_union_two ,
. tagged_union_two_trailing ,
. tagged_union_enum_tag ,
. tagged_union_enum_tag_trailing ,
2020-07-16 20:19:08 +01:00
= > return TypeWithHandle . typeVal ( node_handle ) ,
2021-03-06 19:55:59 +00:00
. builtin_call ,
. builtin_call_comma ,
. builtin_call_two ,
. builtin_call_two_comma ,
= > {
2022-08-17 23:52:21 +01:00
var buffer : [ 2 ] Ast . Node . Index = undefined ;
const params = ast . builtinCallParams ( tree , node , & buffer ) . ? ;
2021-02-28 19:37:21 +00:00
const call_name = tree . tokenSlice ( main_tokens [ node ] ) ;
2020-05-19 16:53:01 +01:00
if ( std . mem . eql ( u8 , call_name , " @This " ) ) {
2021-02-28 19:37:21 +00:00
if ( params . len ! = 0 ) return null ;
return innermostContainer ( handle , starts [ tree . firstToken ( node ) ] ) ;
2020-05-19 16:53:01 +01:00
}
2020-05-22 16:51:57 +01:00
2020-06-08 17:07:16 +01:00
const cast_map = std . ComptimeStringMap ( void , . {
2020-06-16 13:49:57 +01:00
. { " @as " } ,
. { " @bitCast " } ,
. { " @fieldParentPtr " } ,
. { " @floatCast " } ,
. { " @floatToInt " } ,
. { " @intCast " } ,
. { " @intToEnum " } ,
. { " @intToFloat " } ,
. { " @intToPtr " } ,
. { " @truncate " } ,
. { " @ptrCast " } ,
2020-06-08 17:07:16 +01:00
} ) ;
if ( cast_map . has ( call_name ) ) {
2021-02-28 19:37:21 +00:00
if ( params . len < 1 ) return null ;
2023-03-12 22:48:31 +00:00
return ( ( try analyser . resolveTypeOfNodeInternal ( . {
2021-02-28 19:37:21 +00:00
. node = params [ 0 ] ,
2020-06-12 12:56:46 +01:00
. handle = handle ,
2023-03-12 22:48:31 +00:00
} ) ) orelse return null ) . instanceTypeVal ( ) ;
2020-06-17 03:12:12 +01:00
}
// Almost the same as the above, return a type value though.
2020-06-17 13:07:21 +01:00
// TODO Do peer type resolution, we just keep the first for now.
2020-06-17 03:12:12 +01:00
if ( std . mem . eql ( u8 , call_name , " @TypeOf " ) ) {
2021-02-28 19:37:21 +00:00
if ( params . len < 1 ) return null ;
2023-03-12 22:48:31 +00:00
var resolved_type = ( try analyser . resolveTypeOfNodeInternal ( . {
2021-02-28 19:37:21 +00:00
. node = params [ 0 ] ,
2020-06-17 03:12:12 +01:00
. handle = handle ,
2023-03-12 22:48:31 +00:00
} ) ) orelse return null ;
2020-06-17 03:12:12 +01:00
if ( resolved_type . type . is_type_val ) return null ;
resolved_type . type . is_type_val = true ;
return resolved_type ;
2020-06-08 17:07:16 +01:00
}
2023-01-16 18:46:33 +00:00
if ( std . mem . eql ( u8 , call_name , " @typeInfo " ) ) {
2023-03-29 19:36:32 +01:00
const zig_lib_path = try URI . fromPath ( analyser . arena . allocator ( ) , analyser . store . config . zig_lib_path orelse return null ) ;
2023-01-16 18:46:33 +00:00
2023-03-29 19:36:32 +01:00
const builtin_uri = URI . pathRelative ( analyser . arena . allocator ( ) , zig_lib_path , " /std/builtin.zig " ) catch | err | switch ( err ) {
2023-01-16 18:46:33 +00:00
error . OutOfMemory = > | e | return e ,
else = > return null ,
} ;
2023-03-12 22:48:31 +00:00
const new_handle = analyser . store . getOrLoadHandle ( builtin_uri ) orelse return null ;
2023-01-24 20:43:48 +00:00
const root_scope_decls = new_handle . document_scope . scopes . items ( . decls ) [ 0 ] ;
const decl = root_scope_decls . get ( " Type " ) orelse return null ;
2023-01-16 18:46:33 +00:00
if ( decl ! = . ast_node ) return null ;
2023-01-24 23:55:38 +00:00
const var_decl = new_handle . tree . fullVarDecl ( decl . ast_node ) orelse return null ;
2023-01-16 18:46:33 +00:00
return TypeWithHandle {
. type = . {
. data = . { . other = var_decl . ast . init_node } ,
. is_type_val = false ,
} ,
. handle = new_handle ,
} ;
}
2022-08-18 23:00:46 +01:00
if ( std . mem . eql ( u8 , call_name , " @import " ) ) {
if ( params . len = = 0 ) return null ;
const import_param = params [ 0 ] ;
if ( node_tags [ import_param ] ! = . string_literal ) return null ;
const import_str = tree . tokenSlice ( main_tokens [ import_param ] ) ;
2023-03-29 19:36:32 +01:00
const import_uri = ( try analyser . store . uriFromImportStr ( analyser . arena . allocator ( ) , handle . * , import_str [ 1 . . import_str . len - 1 ] ) ) orelse return null ;
2022-10-05 12:23:38 +01:00
2023-03-12 22:48:31 +00:00
const new_handle = analyser . store . getOrLoadHandle ( import_uri ) orelse return null ;
2022-08-18 23:00:46 +01:00
// reference to node '0' which is root
return TypeWithHandle . typeVal ( . { . node = 0 , . handle = new_handle } ) ;
} else if ( std . mem . eql ( u8 , call_name , " @cImport " ) ) {
2023-03-12 22:48:31 +00:00
const cimport_uri = ( try analyser . store . resolveCImport ( handle . * , node ) ) orelse return null ;
2022-10-05 12:23:38 +01:00
2023-03-12 22:48:31 +00:00
const new_handle = analyser . store . getOrLoadHandle ( cimport_uri ) orelse return null ;
2022-08-18 23:00:46 +01:00
// reference to node '0' which is root
return TypeWithHandle . typeVal ( . { . node = 0 , . handle = new_handle } ) ;
}
2020-05-14 00:10:41 +01:00
} ,
2021-03-06 19:55:59 +00:00
. fn_proto ,
. fn_proto_multi ,
. fn_proto_one ,
. fn_proto_simple ,
. fn_decl ,
= > {
2021-10-01 02:44:06 +01:00
var buf : [ 1 ] Ast . Node . Index = undefined ;
2020-06-17 13:07:21 +01:00
// This is a function type
2023-01-24 23:55:38 +00:00
if ( tree . fullFnProto ( & buf , node ) . ? . name_token = = null ) {
2020-06-17 13:07:21 +01:00
return TypeWithHandle . typeVal ( node_handle ) ;
}
2021-02-28 19:37:21 +00:00
2020-06-17 13:07:21 +01:00
return TypeWithHandle {
. type = . { . data = . { . other = node } , . is_type_val = false } ,
. handle = handle ,
} ;
} ,
2021-03-06 19:55:59 +00:00
. multiline_string_literal ,
. string_literal ,
= > return TypeWithHandle {
2020-06-17 03:12:12 +01:00
. type = . { . data = . { . other = node } , . is_type_val = false } ,
. handle = handle ,
2020-05-24 13:24:18 +01:00
} ,
2023-03-07 17:17:45 +00:00
. @ " if " , . if_simple = > {
const if_node = ast . fullIf ( tree , node ) . ? ;
2023-03-12 22:48:31 +00:00
var either = std . ArrayListUnmanaged ( Type . EitherEntry ) { } ;
if ( try analyser . resolveTypeOfNodeInternal ( . { . handle = handle , . node = if_node . ast . then_expr } ) ) | t |
2023-03-29 19:36:32 +01:00
try either . append ( analyser . arena . allocator ( ) , . { . type_with_handle = t , . descriptor = tree . getNodeSource ( if_node . ast . cond_expr ) } ) ;
2023-03-12 22:48:31 +00:00
if ( try analyser . resolveTypeOfNodeInternal ( . { . handle = handle , . node = if_node . ast . else_expr } ) ) | t |
2023-03-29 19:36:32 +01:00
try either . append ( analyser . arena . allocator ( ) , . { . type_with_handle = t , . descriptor = try std . fmt . allocPrint ( analyser . arena . allocator ( ) , " !({s}) " , . { tree . getNodeSource ( if_node . ast . cond_expr ) } ) } ) ;
2023-03-07 17:17:45 +00:00
return TypeWithHandle {
2023-03-29 19:36:32 +01:00
. type = . { . data = . { . either = try either . toOwnedSlice ( analyser . arena . allocator ( ) ) } , . is_type_val = false } ,
2023-03-07 17:17:45 +00:00
. handle = handle ,
} ;
} ,
. @ " switch " ,
. switch_comma ,
= > {
const extra = tree . extraData ( datas [ node ] . rhs , Ast . Node . SubRange ) ;
const cases = tree . extra_data [ extra . start . . extra . end ] ;
2023-03-12 22:48:31 +00:00
var either = std . ArrayListUnmanaged ( Type . EitherEntry ) { } ;
2023-03-07 17:17:45 +00:00
for ( cases ) | case | {
const switch_case = tree . fullSwitchCase ( case ) . ? ;
2023-03-12 22:48:31 +00:00
var descriptor = std . ArrayListUnmanaged ( u8 ) { } ;
2023-03-07 17:17:45 +00:00
2023-03-15 16:20:40 +00:00
for ( switch_case . ast . values , 0 . . ) | values , index | {
2023-03-29 19:36:32 +01:00
try descriptor . appendSlice ( analyser . arena . allocator ( ) , tree . getNodeSource ( values ) ) ;
if ( index ! = switch_case . ast . values . len - 1 ) try descriptor . appendSlice ( analyser . arena . allocator ( ) , " , " ) ;
2023-03-07 17:17:45 +00:00
}
2023-03-12 22:48:31 +00:00
if ( try analyser . resolveTypeOfNodeInternal ( . { . handle = handle , . node = switch_case . ast . target_expr } ) ) | t |
2023-03-29 19:36:32 +01:00
try either . append ( analyser . arena . allocator ( ) , . {
2023-03-07 17:17:45 +00:00
. type_with_handle = t ,
2023-03-29 19:36:32 +01:00
. descriptor = try descriptor . toOwnedSlice ( analyser . arena . allocator ( ) ) ,
2023-03-07 17:17:45 +00:00
} ) ;
}
return TypeWithHandle {
2023-03-29 19:36:32 +01:00
. type = . { . data = . { . either = try either . toOwnedSlice ( analyser . arena . allocator ( ) ) } , . is_type_val = false } ,
2023-03-07 17:17:45 +00:00
. handle = handle ,
} ;
} ,
2021-03-05 21:38:42 +00:00
else = > { } ,
2020-05-13 14:03:33 +01:00
}
return null ;
}
2020-05-11 13:28:08 +01:00
2020-06-17 03:12:12 +01:00
// TODO Reorganize this file, perhaps split into a couple as well
// TODO Make this better, nested levels of type vals
pub const Type = struct {
2023-03-07 17:17:45 +00:00
pub const EitherEntry = struct {
type_with_handle : TypeWithHandle ,
descriptor : [ ] const u8 ,
} ;
2020-06-17 03:12:12 +01:00
data : union ( enum ) {
2021-10-01 02:44:06 +01:00
pointer : Ast . Node . Index ,
slice : Ast . Node . Index ,
error_union : Ast . Node . Index ,
other : Ast . Node . Index ,
2022-07-08 19:29:53 +01:00
primitive : Ast . Node . Index ,
2023-03-07 17:17:45 +00:00
either : [ ] const EitherEntry ,
2022-07-08 19:29:53 +01:00
array_index ,
2022-10-28 19:24:38 +01:00
@ " comptime " : struct {
2022-10-29 06:46:22 +01:00
interpreter : * ComptimeInterpreter ,
2023-01-23 21:23:01 +00:00
value : ComptimeInterpreter . Value ,
2022-10-28 19:24:38 +01:00
} ,
2020-06-17 03:12:12 +01:00
} ,
/// If true, the type `type`, the attached data is the value of the type value.
is_type_val : bool ,
} ;
pub const TypeWithHandle = struct {
type : Type ,
2022-10-05 12:40:11 +01:00
handle : * const DocumentStore . Handle ,
2020-06-17 03:12:12 +01:00
2023-03-31 16:54:46 +01:00
const Context = struct {
// Note that we don't hash/equate descriptors to remove
// duplicates
fn hashType ( hasher : * std . hash . Wyhash , ty : Type ) void {
hasher . update ( & . { @boolToInt ( ty . is_type_val ) , @enumToInt ( ty . data ) } ) ;
switch ( ty . data ) {
. pointer ,
. slice ,
. error_union ,
. other ,
. primitive ,
= > | idx | hasher . update ( & std . mem . toBytes ( idx ) ) ,
. either = > | entries | {
for ( entries ) | e | {
hasher . update ( e . descriptor ) ;
hasher . update ( e . type_with_handle . handle . uri ) ;
hashType ( hasher , e . type_with_handle . type ) ;
}
} ,
. array_index = > { } ,
. @ " comptime " = > {
// TODO
} ,
}
}
pub fn hash ( self : @This ( ) , item : TypeWithHandle ) u64 {
_ = self ;
var hasher = std . hash . Wyhash . init ( 0 ) ;
hashType ( & hasher , item . type ) ;
hasher . update ( item . handle . uri ) ;
return hasher . final ( ) ;
}
pub fn eql ( self : @This ( ) , a : TypeWithHandle , b : TypeWithHandle ) bool {
_ = self ;
if ( ! std . mem . eql ( u8 , a . handle . uri , b . handle . uri ) ) return false ;
if ( a . type . is_type_val ! = b . type . is_type_val ) return false ;
if ( @enumToInt ( a . type . data ) ! = @enumToInt ( b . type . data ) ) return false ;
switch ( a . type . data ) {
inline . pointer ,
. slice ,
. error_union ,
. other ,
. primitive ,
= > | a_idx , name | {
if ( a_idx ! = @field ( b . type . data , @tagName ( name ) ) ) return false ;
} ,
. either = > | a_entries | {
const b_entries = b . type . data . either ;
if ( a_entries . len ! = b_entries . len ) return false ;
for ( a_entries , b_entries ) | ae , be | {
if ( ! std . mem . eql ( u8 , ae . descriptor , be . descriptor ) ) return false ;
if ( ! eql ( . { } , ae . type_with_handle , be . type_with_handle ) ) return false ;
}
} ,
. array_index = > { } ,
. @ " comptime " = > {
// TODO
} ,
}
return true ;
}
} ;
2020-09-12 16:48:09 +01:00
pub fn typeVal ( node_handle : NodeWithHandle ) TypeWithHandle {
2020-06-17 03:12:12 +01:00
return . {
. type = . {
. data = . { . other = node_handle . node } ,
. is_type_val = true ,
} ,
. handle = node_handle . handle ,
} ;
}
2023-03-31 16:54:46 +01:00
pub const Deduplicator = std . HashMapUnmanaged ( TypeWithHandle , void , TypeWithHandle . Context , std . hash_map . default_max_load_percentage ) ;
2023-03-07 17:17:45 +00:00
/// Resolves possible types of a type (single for all except array_index and either)
2023-03-31 16:54:46 +01:00
/// Drops duplicates
2023-03-07 17:17:45 +00:00
pub fn getAllTypesWithHandles ( ty : TypeWithHandle , arena : std . mem . Allocator ) ! [ ] const TypeWithHandle {
var all_types = std . ArrayListUnmanaged ( TypeWithHandle ) { } ;
try ty . getAllTypesWithHandlesArrayList ( arena , & all_types ) ;
return try all_types . toOwnedSlice ( arena ) ;
}
pub fn getAllTypesWithHandlesArrayList ( ty : TypeWithHandle , arena : std . mem . Allocator , all_types : * std . ArrayListUnmanaged ( TypeWithHandle ) ) ! void {
switch ( ty . type . data ) {
. either = > | e | for ( e ) | i | try i . type_with_handle . getAllTypesWithHandlesArrayList ( arena , all_types ) ,
else = > try all_types . append ( arena , ty ) ,
}
}
2020-06-17 03:12:12 +01:00
fn instanceTypeVal ( self : TypeWithHandle ) ? TypeWithHandle {
if ( ! self . type . is_type_val ) return null ;
return TypeWithHandle {
. type = . { . data = self . type . data , . is_type_val = false } ,
. handle = self . handle ,
} ;
}
2020-06-17 21:36:40 +01:00
fn isRoot ( self : TypeWithHandle ) bool {
switch ( self . type . data ) {
2021-02-28 20:57:15 +00:00
// root is always index 0
. other = > | n | return n = = 0 ,
2020-06-17 21:36:40 +01:00
else = > return false ,
}
}
2021-03-04 21:53:54 +00:00
fn isContainerKind ( self : TypeWithHandle , container_kind_tok : std . zig . Token . Tag ) bool {
const tree = self . handle . tree ;
2021-02-28 20:57:15 +00:00
const main_tokens = tree . nodes . items ( . main_token ) ;
const tags = tree . tokens . items ( . tag ) ;
2020-06-17 21:36:40 +01:00
switch ( self . type . data ) {
2021-02-28 20:57:15 +00:00
. other = > | n | return tags [ main_tokens [ n ] ] = = container_kind_tok ,
2020-06-17 21:36:40 +01:00
else = > return false ,
}
}
2021-03-04 21:53:54 +00:00
pub fn isStructType ( self : TypeWithHandle ) bool {
return self . isContainerKind ( . keyword_struct ) or self . isRoot ( ) ;
2020-06-17 21:36:40 +01:00
}
2021-03-04 21:53:54 +00:00
pub fn isNamespace ( self : TypeWithHandle ) bool {
if ( ! self . isStructType ( ) ) return false ;
const tree = self . handle . tree ;
2021-03-04 21:30:25 +00:00
const node = self . type . data . other ;
const tags = tree . nodes . items ( . tag ) ;
2023-01-24 23:55:38 +00:00
var buf : [ 2 ] Ast . Node . Index = undefined ;
const full = tree . fullContainerDecl ( & buf , node ) orelse return true ;
for ( full . ast . members ) | member | {
if ( tags [ member ] . isContainerField ( ) ) return false ;
2020-06-27 18:45:58 +01:00
}
return true ;
}
2021-03-04 21:53:54 +00:00
pub fn isEnumType ( self : TypeWithHandle ) bool {
return self . isContainerKind ( . keyword_enum ) ;
2020-06-17 21:36:40 +01:00
}
2021-03-04 21:53:54 +00:00
pub fn isUnionType ( self : TypeWithHandle ) bool {
return self . isContainerKind ( . keyword_union ) ;
2020-06-17 21:36:40 +01:00
}
2020-06-18 13:12:09 +01:00
2021-03-04 21:53:54 +00:00
pub fn isOpaqueType ( self : TypeWithHandle ) bool {
return self . isContainerKind ( . keyword_opaque ) ;
2020-10-10 10:25:50 +01:00
}
2021-03-04 21:53:54 +00:00
pub fn isTypeFunc ( self : TypeWithHandle ) bool {
2021-10-01 02:44:06 +01:00
var buf : [ 1 ] Ast . Node . Index = undefined ;
2021-03-04 21:53:54 +00:00
const tree = self . handle . tree ;
2021-03-03 15:34:24 +00:00
return switch ( self . type . data ) {
2023-01-24 23:55:38 +00:00
. other = > | n | if ( tree . fullFnProto ( & buf , n ) ) | fn_proto | blk : {
2021-03-03 15:34:24 +00:00
break : blk isTypeFunction ( tree , fn_proto ) ;
} else false ,
else = > false ,
} ;
2020-06-27 18:45:58 +01:00
}
2021-03-04 21:53:54 +00:00
pub fn isGenericFunc ( self : TypeWithHandle ) bool {
2021-10-01 02:44:06 +01:00
var buf : [ 1 ] Ast . Node . Index = undefined ;
2021-03-04 21:53:54 +00:00
const tree = self . handle . tree ;
2021-03-03 15:34:24 +00:00
return switch ( self . type . data ) {
2023-01-24 23:55:38 +00:00
. other = > | n | if ( tree . fullFnProto ( & buf , n ) ) | fn_proto | blk : {
2021-03-03 15:34:24 +00:00
break : blk isGenericFunction ( tree , fn_proto ) ;
} else false ,
else = > false ,
} ;
2020-06-18 13:12:09 +01:00
}
2021-03-04 21:53:54 +00:00
pub fn isFunc ( self : TypeWithHandle ) bool {
const tree = self . handle . tree ;
2021-02-28 20:57:15 +00:00
const tags = tree . nodes . items ( . tag ) ;
2021-03-03 15:34:24 +00:00
return switch ( self . type . data ) {
. other = > | n | switch ( tags [ n ] ) {
2021-02-28 20:57:15 +00:00
. fn_proto ,
. fn_proto_multi ,
. fn_proto_one ,
. fn_proto_simple ,
2021-03-02 14:32:38 +00:00
. fn_decl ,
2021-02-28 20:57:15 +00:00
= > true ,
else = > false ,
2020-06-18 13:12:09 +01:00
} ,
2021-03-03 15:34:24 +00:00
else = > false ,
} ;
2020-06-18 13:12:09 +01:00
}
2020-06-17 03:12:12 +01:00
} ;
2023-03-12 22:48:31 +00:00
pub fn resolveTypeOfNode ( analyser : * Analyser , node_handle : NodeWithHandle ) error { OutOfMemory } ! ? TypeWithHandle {
analyser . bound_type_params . clearRetainingCapacity ( ) ;
return analyser . resolveTypeOfNodeInternal ( node_handle ) ;
2020-06-12 12:56:46 +01:00
}
2022-08-18 23:00:46 +01:00
/// Collects all `@import`'s we can find into a slice of import paths (without quotes).
2022-08-23 11:44:26 +01:00
pub fn collectImports ( allocator : std . mem . Allocator , tree : Ast ) error { OutOfMemory } ! std . ArrayListUnmanaged ( [ ] const u8 ) {
2022-08-18 23:00:46 +01:00
var imports = std . ArrayListUnmanaged ( [ ] const u8 ) { } ;
2022-12-27 05:52:15 +00:00
errdefer imports . deinit ( allocator ) ;
2022-08-18 23:00:46 +01:00
2021-03-30 09:33:21 +01:00
const tags = tree . tokens . items ( . tag ) ;
2021-02-26 20:26:52 +00:00
2021-03-30 10:07:29 +01:00
var i : usize = 0 ;
2021-03-30 09:33:21 +01:00
while ( i < tags . len ) : ( i + = 1 ) {
if ( tags [ i ] ! = . builtin )
continue ;
2021-03-30 10:07:29 +01:00
const text = tree . tokenSlice ( @intCast ( u32 , i ) ) ;
2021-03-30 13:41:59 +01:00
2021-03-30 09:33:21 +01:00
if ( std . mem . eql ( u8 , text , " @import " ) ) {
if ( i + 3 > = tags . len )
break ;
if ( tags [ i + 1 ] ! = . l_paren )
continue ;
if ( tags [ i + 2 ] ! = . string_literal )
continue ;
if ( tags [ i + 3 ] ! = . r_paren )
continue ;
2021-02-26 20:26:52 +00:00
2021-03-30 10:07:29 +01:00
const str = tree . tokenSlice ( @intCast ( u32 , i + 2 ) ) ;
2022-08-18 23:00:46 +01:00
try imports . append ( allocator , str [ 1 . . str . len - 1 ] ) ;
2020-05-14 12:51:07 +01:00
}
}
2022-08-18 23:00:46 +01:00
2022-08-23 11:44:26 +01:00
return imports ;
2022-08-18 23:00:46 +01:00
}
/// Collects all `@cImport` nodes
/// Caller owns returned memory.
pub fn collectCImportNodes ( allocator : std . mem . Allocator , tree : Ast ) error { OutOfMemory } ! [ ] Ast . Node . Index {
2023-03-11 18:43:14 +00:00
const tracy_zone = tracy . trace ( @src ( ) ) ;
defer tracy_zone . end ( ) ;
2022-08-18 23:00:46 +01:00
var import_nodes = std . ArrayListUnmanaged ( Ast . Node . Index ) { } ;
errdefer import_nodes . deinit ( allocator ) ;
const node_tags = tree . nodes . items ( . tag ) ;
const main_tokens = tree . nodes . items ( . main_token ) ;
var i : usize = 0 ;
while ( i < node_tags . len ) : ( i + = 1 ) {
const node = @intCast ( Ast . Node . Index , i ) ;
if ( ! ast . isBuiltinCall ( tree , node ) ) continue ;
if ( ! std . mem . eql ( u8 , Ast . tokenSlice ( tree , main_tokens [ node ] ) , " @cImport " ) ) continue ;
try import_nodes . append ( allocator , node ) ;
}
return import_nodes . toOwnedSlice ( allocator ) ;
2020-05-14 12:51:07 +01:00
}
2023-03-13 00:08:22 +00:00
pub const NodeWithUri = struct {
2021-10-01 02:44:06 +01:00
node : Ast . Node . Index ,
2023-03-13 00:08:22 +00:00
uri : [ ] const u8 ,
2023-03-12 22:48:31 +00:00
const Context = struct {
2023-03-13 00:08:22 +00:00
pub fn hash ( self : @This ( ) , item : NodeWithUri ) u64 {
2023-03-12 22:48:31 +00:00
_ = self ;
var hasher = std . hash . Wyhash . init ( 0 ) ;
std . hash . autoHash ( & hasher , item . node ) ;
2023-03-13 00:08:22 +00:00
hasher . update ( item . uri ) ;
2023-03-12 22:48:31 +00:00
return hasher . final ( ) ;
}
2023-03-13 00:08:22 +00:00
pub fn eql ( self : @This ( ) , a : NodeWithUri , b : NodeWithUri ) bool {
2023-03-12 22:48:31 +00:00
_ = self ;
if ( a . node ! = b . node ) return false ;
2023-03-13 00:08:22 +00:00
return std . mem . eql ( u8 , a . uri , b . uri ) ;
2023-03-12 22:48:31 +00:00
}
} ;
2020-06-10 17:54:01 +01:00
} ;
2020-05-29 10:46:50 +01:00
2023-03-13 00:08:22 +00:00
pub const NodeWithHandle = struct {
node : Ast . Node . Index ,
handle : * const DocumentStore . Handle ,
} ;
2020-07-07 21:26:12 +01:00
pub const FieldAccessReturn = struct {
original : TypeWithHandle ,
unwrapped : ? TypeWithHandle = null ,
} ;
2023-03-12 22:48:31 +00:00
pub fn getFieldAccessType ( analyser : * Analyser , handle : * const DocumentStore . Handle , source_index : usize , tokenizer : * std . zig . Tokenizer ) ! ? FieldAccessReturn {
analyser . bound_type_params . clearRetainingCapacity ( ) ;
2020-05-11 13:28:08 +01:00
2023-03-12 22:48:31 +00:00
var current_type : ? TypeWithHandle = null ;
2020-06-12 12:56:46 +01:00
2020-05-13 14:03:33 +01:00
while ( true ) {
2020-05-26 23:45:18 +01:00
const tok = tokenizer . next ( ) ;
2021-02-28 16:42:34 +00:00
switch ( tok . tag ) {
. eof = > return FieldAccessReturn {
2022-09-29 21:39:29 +01:00
. original = current_type orelse return null ,
2023-03-12 22:48:31 +00:00
. unwrapped = try analyser . resolveDerefType ( current_type orelse return null ) ,
2020-07-07 21:26:12 +01:00
} ,
2021-02-28 16:42:34 +00:00
. identifier = > {
2022-09-29 21:39:29 +01:00
const ct_handle = if ( current_type ) | c | c . handle else handle ;
2023-03-12 22:48:31 +00:00
if ( try analyser . lookupSymbolGlobal (
2022-09-29 21:39:29 +01:00
ct_handle ,
2021-03-30 14:45:49 +01:00
tokenizer . buffer [ tok . loc . start . . tok . loc . end ] ,
source_index ,
) ) | child | {
2023-03-12 22:48:31 +00:00
current_type = ( try child . resolveType ( analyser ) ) orelse return null ;
2020-05-13 14:03:33 +01:00
} else return null ;
} ,
2021-02-28 16:42:34 +00:00
. period = > {
2020-05-26 23:45:18 +01:00
const after_period = tokenizer . next ( ) ;
2021-02-28 20:57:15 +00:00
switch ( after_period . tag ) {
2021-03-12 10:56:51 +00:00
. eof = > {
// function labels cannot be dot accessed
2022-09-29 21:39:29 +01:00
if ( current_type ) | ct | {
if ( ct . isFunc ( ) ) return null ;
return FieldAccessReturn {
. original = ct ,
2023-03-12 22:48:31 +00:00
. unwrapped = try analyser . resolveDerefType ( ct ) ,
2022-09-29 21:39:29 +01:00
} ;
} else {
return null ;
}
2020-07-07 21:26:12 +01:00
} ,
2021-03-01 13:32:19 +00:00
. identifier = > {
2020-07-07 21:26:12 +01:00
if ( after_period . loc . end = = tokenizer . buffer . len ) {
2022-09-29 21:39:29 +01:00
if ( current_type ) | ct | {
return FieldAccessReturn {
. original = ct ,
2023-03-12 22:48:31 +00:00
. unwrapped = try analyser . resolveDerefType ( ct ) ,
2022-09-29 21:39:29 +01:00
} ;
} else {
return null ;
}
2020-07-07 21:26:12 +01:00
}
2020-06-17 03:12:12 +01:00
2023-03-12 22:48:31 +00:00
const deref_type = if ( current_type ) | ty |
if ( try analyser . resolveDerefType ( ty ) ) | deref_ty | deref_ty else ty
else
return null ;
2022-09-29 21:39:29 +01:00
2023-03-29 19:36:32 +01:00
const current_type_nodes = try deref_type . getAllTypesWithHandles ( analyser . arena . allocator ( ) ) ;
2020-06-17 03:12:12 +01:00
2023-03-07 17:17:45 +00:00
// TODO: Return all options instead of first valid one
// (this would require a huge rewrite and im lazy)
for ( current_type_nodes ) | ty | {
const current_type_node = switch ( ty . type . data ) {
. other = > | n | n ,
else = > continue ,
} ;
2023-03-12 22:48:31 +00:00
if ( try analyser . lookupSymbolContainer (
2023-03-07 17:17:45 +00:00
. { . node = current_type_node , . handle = ty . handle } ,
tokenizer . buffer [ after_period . loc . start . . after_period . loc . end ] ,
! current_type . ? . type . is_type_val ,
) ) | child | {
2023-03-12 22:48:31 +00:00
current_type . ? = ( try child . resolveType ( analyser ) ) orelse continue ;
2023-03-07 17:17:45 +00:00
break ;
} else continue ;
} else {
return null ;
}
2020-05-26 23:45:18 +01:00
} ,
2021-03-01 13:32:19 +00:00
. question_mark = > {
2023-03-12 22:48:31 +00:00
current_type = ( try analyser . resolveUnwrapOptionalType ( current_type orelse return null ) ) orelse return null ;
2020-05-26 23:45:18 +01:00
} ,
else = > {
2021-02-28 20:57:15 +00:00
log . debug ( " Unrecognized token {} after period. " , . { after_period . tag } ) ;
2020-05-26 23:45:18 +01:00
return null ;
} ,
2020-05-13 14:03:33 +01:00
}
} ,
2021-02-28 16:42:34 +00:00
. period_asterisk = > {
2023-03-12 22:48:31 +00:00
current_type = ( try analyser . resolveDerefType ( current_type orelse return null ) ) orelse return null ;
2020-05-26 23:45:18 +01:00
} ,
2021-02-28 16:42:34 +00:00
. l_paren = > {
2022-09-29 21:39:29 +01:00
if ( current_type = = null ) {
return null ;
}
const current_type_node = switch ( current_type . ? . type . data ) {
2020-06-17 03:12:12 +01:00
. other = > | n | n ,
else = > return null ,
} ;
2020-06-17 13:07:21 +01:00
// Can't call a function type, we need a function type instance.
2022-09-29 21:39:29 +01:00
if ( current_type . ? . type . is_type_val ) return null ;
const cur_tree = current_type . ? . handle . tree ;
2021-10-01 02:44:06 +01:00
var buf : [ 1 ] Ast . Node . Index = undefined ;
2023-01-24 23:55:38 +00:00
if ( cur_tree . fullFnProto ( & buf , current_type_node ) ) | func | {
2021-03-08 18:46:23 +00:00
// Check if the function has a body and if so, pass it
// so the type can be resolved if it's a generic function returning
// an anonymous struct
const has_body = cur_tree . nodes . items ( . tag ) [ current_type_node ] = = . fn_decl ;
const body = cur_tree . nodes . items ( . data ) [ current_type_node ] . rhs ;
2021-04-05 12:46:05 +01:00
// TODO Actually bind params here when calling functions instead of just skipping args.
2023-03-12 22:48:31 +00:00
if ( try analyser . resolveReturnType ( func , current_type . ? . handle , if ( has_body ) body else null ) ) | ret | {
2020-06-17 03:12:12 +01:00
current_type = ret ;
// Skip to the right paren
var paren_count : usize = 1 ;
var next = tokenizer . next ( ) ;
2021-03-01 13:32:19 +00:00
while ( next . tag ! = . eof ) : ( next = tokenizer . next ( ) ) {
if ( next . tag = = . r_paren ) {
2020-06-17 03:12:12 +01:00
paren_count - = 1 ;
if ( paren_count = = 0 ) break ;
2021-03-01 13:32:19 +00:00
} else if ( next . tag = = . l_paren ) {
2020-06-17 03:12:12 +01:00
paren_count + = 1 ;
}
2020-05-26 23:45:18 +01:00
} else return null ;
2020-06-17 03:12:12 +01:00
} else return null ;
} else return null ;
2020-05-24 13:24:18 +01:00
} ,
2021-02-28 16:42:34 +00:00
. l_bracket = > {
2020-05-27 16:49:11 +01:00
var brack_count : usize = 1 ;
var next = tokenizer . next ( ) ;
var is_range = false ;
2021-03-01 13:32:19 +00:00
while ( next . tag ! = . eof ) : ( next = tokenizer . next ( ) ) {
if ( next . tag = = . r_bracket ) {
2020-05-27 16:49:11 +01:00
brack_count - = 1 ;
if ( brack_count = = 0 ) break ;
2021-03-01 13:32:19 +00:00
} else if ( next . tag = = . l_bracket ) {
2020-05-27 16:49:11 +01:00
brack_count + = 1 ;
2021-03-01 13:32:19 +00:00
} else if ( next . tag = = . ellipsis2 and brack_count = = 1 ) {
2020-05-27 16:49:11 +01:00
is_range = true ;
}
} else return null ;
2023-03-12 22:48:31 +00:00
current_type = ( try analyser . resolveBracketAccessType ( current_type orelse return null , if ( is_range ) . Range else . Single ) ) orelse return null ;
2020-05-24 03:07:09 +01:00
} ,
2023-03-23 07:42:20 +00:00
. builtin = > {
const curr_handle = if ( current_type = = null ) handle else current_type . ? . handle ;
if ( std . mem . eql ( u8 , tokenizer . buffer [ tok . loc . start . . tok . loc . end ] , " @import " ) ) {
if ( tokenizer . next ( ) . tag ! = . l_paren ) return null ;
var import_str_tok = tokenizer . next ( ) ; // should be the .string_literal
if ( import_str_tok . tag ! = . string_literal ) return null ;
if ( import_str_tok . loc . end - import_str_tok . loc . start < 2 ) return null ;
var import_str = offsets . locToSlice ( tokenizer . buffer , . {
. start = import_str_tok . loc . start + 1 ,
. end = import_str_tok . loc . end - 1 ,
} ) ;
2023-03-29 19:36:32 +01:00
const uri = try analyser . store . uriFromImportStr ( analyser . arena . allocator ( ) , curr_handle . * , import_str ) orelse return null ;
2023-03-23 07:42:20 +00:00
const node_handle = analyser . store . getOrLoadHandle ( uri ) orelse return null ;
current_type = TypeWithHandle . typeVal ( NodeWithHandle { . handle = node_handle , . node = 0 } ) ;
_ = tokenizer . next ( ) ; // eat the .r_paren
} else {
log . debug ( " Unhandled builtin: {s} " , . { offsets . locToSlice ( tokenizer . buffer , tok . loc ) } ) ;
return null ;
}
} ,
2020-05-26 23:45:18 +01:00
else = > {
2021-02-28 16:42:34 +00:00
log . debug ( " Unimplemented token: {} " , . { tok . tag } ) ;
2020-05-26 23:45:18 +01:00
return null ;
2020-05-27 16:49:11 +01:00
} ,
2020-05-13 14:03:33 +01:00
}
}
2023-03-12 22:48:31 +00:00
std . debug . print ( " current_type: {?} \n " , . { current_type } ) ;
2022-09-29 21:39:29 +01:00
if ( current_type ) | ct | {
return FieldAccessReturn {
. original = ct ,
2023-03-12 22:48:31 +00:00
. unwrapped = try analyser . resolveDerefType ( ct ) ,
2022-09-29 21:39:29 +01:00
} ;
} else {
return null ;
}
2020-05-13 14:03:33 +01:00
}
2021-10-01 02:57:45 +01:00
pub fn isNodePublic ( tree : Ast , node : Ast . Node . Index ) bool {
2021-10-01 02:44:06 +01:00
var buf : [ 1 ] Ast . Node . Index = undefined ;
2021-03-01 15:30:43 +00:00
return switch ( tree . nodes . items ( . tag ) [ node ] ) {
2021-03-03 15:34:24 +00:00
. global_var_decl ,
. local_var_decl ,
. simple_var_decl ,
. aligned_var_decl ,
2023-01-24 23:55:38 +00:00
= > tree . fullVarDecl ( node ) . ? . visib_token ! = null ,
2021-03-03 15:34:24 +00:00
. fn_proto ,
. fn_proto_multi ,
. fn_proto_one ,
. fn_proto_simple ,
. fn_decl ,
2023-01-24 23:55:38 +00:00
= > tree . fullFnProto ( & buf , node ) . ? . visib_token ! = null ,
2021-03-01 15:30:43 +00:00
else = > true ,
} ;
2020-05-14 17:14:35 +01:00
}
2021-10-01 02:57:45 +01:00
pub fn nodeToString ( tree : Ast , node : Ast . Node . Index ) ? [ ] const u8 {
2021-03-02 14:32:38 +00:00
const data = tree . nodes . items ( . data ) ;
2021-03-02 21:01:13 +00:00
const main_token = tree . nodes . items ( . main_token ) [ node ] ;
2021-10-01 02:44:06 +01:00
var buf : [ 1 ] Ast . Node . Index = undefined ;
2022-12-15 18:03:09 +00:00
return switch ( tree . nodes . items ( . tag ) [ node ] ) {
. container_field ,
. container_field_init ,
. container_field_align ,
= > {
2023-01-24 23:55:38 +00:00
const field = tree . fullContainerField ( node ) . ? . ast ;
2022-11-26 17:22:16 +00:00
return if ( field . tuple_like ) null else tree . tokenSlice ( field . main_token ) ;
} ,
2022-12-15 18:03:09 +00:00
. error_value = > tree . tokenSlice ( data [ node ] . rhs ) ,
. identifier = > tree . tokenSlice ( main_token ) ,
2021-03-03 15:34:24 +00:00
. fn_proto ,
. fn_proto_multi ,
. fn_proto_one ,
. fn_proto_simple ,
. fn_decl ,
2023-01-24 23:55:38 +00:00
= > if ( tree . fullFnProto ( & buf , node ) . ? . name_token ) | name | tree . tokenSlice ( name ) else null ,
2022-12-15 18:03:09 +00:00
. field_access = > tree . tokenSlice ( data [ node ] . rhs ) ,
2021-03-03 16:45:42 +00:00
. call ,
. call_comma ,
. async_call ,
. async_call_comma ,
2022-12-15 18:03:09 +00:00
= > tree . tokenSlice ( tree . callFull ( node ) . ast . lparen - 1 ) ,
2021-03-03 16:45:42 +00:00
. call_one ,
. call_one_comma ,
. async_call_one ,
. async_call_one_comma ,
2022-12-15 18:03:09 +00:00
= > tree . tokenSlice ( tree . callOne ( & buf , node ) . ast . lparen - 1 ) ,
. test_decl = > if ( data [ node ] . lhs ! = 0 ) tree . tokenSlice ( data [ node ] . lhs ) else null ,
else = > | tag | {
log . debug ( " INVALID: {} " , . { tag } ) ;
return null ;
} ,
} ;
2020-05-13 14:03:33 +01:00
}
2020-05-16 19:06:48 +01:00
2020-05-27 19:58:35 +01:00
pub const PositionContext = union ( enum ) {
2022-09-16 01:33:49 +01:00
builtin : offsets . Loc ,
2020-05-26 23:45:18 +01:00
comment ,
2022-09-16 01:33:49 +01:00
import_string_literal : offsets . Loc ,
2023-02-11 20:25:55 +00:00
cinclude_string_literal : offsets . Loc ,
2022-09-16 01:33:49 +01:00
embedfile_string_literal : offsets . Loc ,
string_literal : offsets . Loc ,
field_access : offsets . Loc ,
var_access : offsets . Loc ,
2020-05-16 17:04:07 +01:00
global_error_set ,
2020-05-26 23:45:18 +01:00
enum_literal ,
2020-06-15 02:39:50 +01:00
pre_label ,
2020-06-18 13:12:09 +01:00
label : bool ,
2020-05-26 23:45:18 +01:00
other ,
empty ,
2022-09-16 01:33:49 +01:00
pub fn loc ( self : PositionContext ) ? offsets . Loc {
2020-05-26 23:45:18 +01:00
return switch ( self ) {
. builtin = > | r | r ,
. comment = > null ,
2022-07-09 09:43:46 +01:00
. import_string_literal = > | r | r ,
2023-02-11 20:25:55 +00:00
. cinclude_string_literal = > | r | r ,
2022-07-09 09:43:46 +01:00
. embedfile_string_literal = > | r | r ,
2020-05-26 23:45:18 +01:00
. string_literal = > | r | r ,
. field_access = > | r | r ,
. var_access = > | r | r ,
. enum_literal = > null ,
2020-06-15 02:39:50 +01:00
. pre_label = > null ,
2020-06-14 20:24:18 +01:00
. label = > null ,
2020-05-26 23:45:18 +01:00
. other = > null ,
. empty = > null ,
2020-05-16 17:04:07 +01:00
. global_error_set = > null ,
2020-05-26 23:45:18 +01:00
} ;
}
} ;
const StackState = struct {
ctx : PositionContext ,
stack_id : enum { Paren , Bracket , Global } ,
} ;
2022-08-23 11:44:26 +01:00
fn peek ( allocator : std . mem . Allocator , arr : * std . ArrayListUnmanaged ( StackState ) ) ! * StackState {
2020-05-26 23:45:18 +01:00
if ( arr . items . len = = 0 ) {
2022-08-23 11:44:26 +01:00
try arr . append ( allocator , . { . ctx = . empty , . stack_id = . Global } ) ;
2020-05-26 23:45:18 +01:00
}
return & arr . items [ arr . items . len - 1 ] ;
}
2022-09-16 01:33:49 +01:00
fn tokenLocAppend ( prev : offsets . Loc , token : std . zig . Token ) offsets . Loc {
2020-05-26 23:45:18 +01:00
return . {
. start = prev . start ,
. end = token . loc . end ,
} ;
}
2023-01-22 20:47:53 +00:00
pub fn isSymbolChar ( char : u8 ) bool {
return std . ascii . isAlphanumeric ( char ) or char = = '_' ;
}
2023-01-11 17:30:47 +00:00
/// Given a byte index in a document (typically cursor offset), classify what kind of entity is at that index.
///
2023-03-15 16:20:40 +00:00
/// Classification is based on the lexical structure -- we fetch the line containing index, tokenize it,
2023-01-11 17:30:47 +00:00
/// and look at the sequence of tokens just before the cursor. Due to the nice way zig is designed (only line
/// comments, etc) lexing just a single line is always correct.
2023-01-22 20:47:53 +00:00
pub fn getPositionContext (
allocator : std . mem . Allocator ,
text : [ ] const u8 ,
doc_index : usize ,
/// Should we look to the end of the current context? Yes for goto def, no for completions
lookahead : bool ,
) ! PositionContext {
2023-03-11 18:43:14 +00:00
const tracy_zone = tracy . trace ( @src ( ) ) ;
defer tracy_zone . end ( ) ;
2023-01-22 20:47:53 +00:00
var new_index = doc_index ;
if ( lookahead and new_index < text . len and isSymbolChar ( text [ new_index ] ) ) {
new_index + = 1 ;
} else if ( lookahead and new_index + 1 < text . len and text [ new_index ] = = '@' ) {
new_index + = 2 ;
}
const line_loc = if ( ! lookahead ) offsets . lineLocAtIndex ( text , new_index ) else offsets . lineLocUntilIndex ( text , new_index ) ;
2022-10-01 01:45:45 +01:00
const line = offsets . locToSlice ( text , line_loc ) ;
2023-01-22 20:47:53 +00:00
const prev_char = if ( new_index > 0 ) text [ new_index - 1 ] else 0 ;
2021-07-10 17:58:37 +01:00
2022-11-16 22:34:36 +00:00
const is_comment = std . mem . startsWith ( u8 , std . mem . trimLeft ( u8 , line , " \t " ) , " // " ) ;
if ( is_comment ) return . comment ;
2022-09-16 01:33:49 +01:00
var stack = try std . ArrayListUnmanaged ( StackState ) . initCapacity ( allocator , 8 ) ;
defer stack . deinit ( allocator ) ;
2022-08-23 11:44:26 +01:00
2021-07-10 17:58:37 +01:00
{
2022-10-01 01:45:45 +01:00
var held_line = try allocator . dupeZ ( u8 , text [ 0 . . line_loc . end ] ) ;
defer allocator . free ( held_line ) ;
2022-09-16 01:33:49 +01:00
var tokenizer : std . zig . Tokenizer = . {
2022-10-01 01:45:45 +01:00
. buffer = held_line ,
2022-09-16 01:33:49 +01:00
. index = line_loc . start ,
. pending_invalid_token = null ,
} ;
2020-05-26 23:45:18 +01:00
2021-07-10 17:58:37 +01:00
while ( true ) {
2023-02-13 22:47:57 +00:00
var tok = tokenizer . next ( ) ;
2021-07-10 17:58:37 +01:00
// Early exits.
2023-01-22 20:47:53 +00:00
if ( tok . loc . start > new_index ) break ;
if ( tok . loc . start = = new_index ) {
2023-03-20 18:31:23 +00:00
// Tie-breaking, the cursor is exactly between two tokens, and
2023-01-11 17:30:47 +00:00
// `tok` is the latter of the two.
if ( tok . tag ! = . identifier ) break ;
}
2021-07-10 17:58:37 +01:00
switch ( tok . tag ) {
. invalid = > {
// Single '@' do not return a builtin token so we check this on our own.
2023-01-11 17:30:47 +00:00
if ( prev_char = = '@' ) {
2021-07-10 17:58:37 +01:00
return PositionContext {
. builtin = . {
2022-09-16 01:33:49 +01:00
. start = line_loc . end - 1 ,
. end = line_loc . end ,
2021-07-10 17:58:37 +01:00
} ,
} ;
}
2023-02-15 18:33:58 +00:00
const s = held_line [ tok . loc . start . . tok . loc . end ] ;
const q = std . mem . indexOf ( u8 , s , " \" " ) orelse return . other ;
if ( s [ q - | 1 ] = = '@' ) {
2023-02-13 22:47:57 +00:00
tok . tag = . identifier ;
} else {
tok . tag = . string_literal ;
}
2021-07-10 17:58:37 +01:00
} ,
. doc_comment , . container_doc_comment = > return . comment ,
. eof = > break ,
else = > { } ,
}
2020-05-26 23:45:18 +01:00
2021-07-10 17:58:37 +01:00
// State changes
2022-09-16 01:33:49 +01:00
var curr_ctx = try peek ( allocator , & stack ) ;
2021-07-10 17:58:37 +01:00
switch ( tok . tag ) {
2022-07-09 09:43:46 +01:00
. string_literal , . multiline_string_literal_line = > string_lit_block : {
if ( curr_ctx . stack_id = = . Paren and stack . items . len > = 2 ) {
const perhaps_builtin = stack . items [ stack . items . len - 2 ] ;
switch ( perhaps_builtin . ctx ) {
. builtin = > | loc | {
const builtin_name = tokenizer . buffer [ loc . start . . loc . end ] ;
if ( std . mem . eql ( u8 , builtin_name , " @import " ) ) {
curr_ctx . ctx = . { . import_string_literal = tok . loc } ;
break : string_lit_block ;
2023-02-11 20:25:55 +00:00
} else if ( std . mem . eql ( u8 , builtin_name , " @cInclude " ) ) {
curr_ctx . ctx = . { . cinclude_string_literal = tok . loc } ;
break : string_lit_block ;
} else if ( std . mem . eql ( u8 , builtin_name , " @embedFile " ) ) {
2022-07-09 09:43:46 +01:00
curr_ctx . ctx = . { . embedfile_string_literal = tok . loc } ;
break : string_lit_block ;
}
} ,
else = > { } ,
}
}
curr_ctx . ctx = . { . string_literal = tok . loc } ;
} ,
2021-07-10 17:58:37 +01:00
. identifier = > switch ( curr_ctx . ctx ) {
. empty , . pre_label = > curr_ctx . ctx = . { . var_access = tok . loc } ,
. label = > | filled | if ( ! filled ) {
curr_ctx . ctx = . { . label = true } ;
} else {
curr_ctx . ctx = . { . var_access = tok . loc } ;
} ,
else = > { } ,
} ,
. builtin = > switch ( curr_ctx . ctx ) {
. empty , . pre_label = > curr_ctx . ctx = . { . builtin = tok . loc } ,
else = > { } ,
} ,
. period , . period_asterisk = > switch ( curr_ctx . ctx ) {
. empty , . pre_label = > curr_ctx . ctx = . enum_literal ,
. enum_literal = > curr_ctx . ctx = . empty ,
. field_access = > { } ,
. other = > { } ,
. global_error_set = > { } ,
2022-12-17 22:27:51 +00:00
. label = > { } ,
2021-07-10 17:58:37 +01:00
else = > curr_ctx . ctx = . {
2022-09-16 01:33:49 +01:00
. field_access = tokenLocAppend ( curr_ctx . ctx . loc ( ) . ? , tok ) ,
2021-07-10 17:58:37 +01:00
} ,
} ,
. keyword_break , . keyword_continue = > curr_ctx . ctx = . pre_label ,
. colon = > if ( curr_ctx . ctx = = . pre_label ) {
curr_ctx . ctx = . { . label = false } ;
2020-06-18 13:12:09 +01:00
} else {
2021-07-10 17:58:37 +01:00
curr_ctx . ctx = . empty ;
2020-06-18 13:12:09 +01:00
} ,
2021-07-10 17:58:37 +01:00
. question_mark = > switch ( curr_ctx . ctx ) {
. field_access = > { } ,
else = > curr_ctx . ctx = . empty ,
2020-05-26 23:45:18 +01:00
} ,
2022-09-16 01:33:49 +01:00
. l_paren = > try stack . append ( allocator , . { . ctx = . empty , . stack_id = . Paren } ) ,
. l_bracket = > try stack . append ( allocator , . { . ctx = . empty , . stack_id = . Bracket } ) ,
2021-07-10 17:58:37 +01:00
. r_paren = > {
_ = stack . pop ( ) ;
if ( curr_ctx . stack_id ! = . Paren ) {
2022-09-16 01:33:49 +01:00
( try peek ( allocator , & stack ) ) . ctx = . empty ;
2021-07-10 17:58:37 +01:00
}
} ,
. r_bracket = > {
_ = stack . pop ( ) ;
if ( curr_ctx . stack_id ! = . Bracket ) {
2022-09-16 01:33:49 +01:00
( try peek ( allocator , & stack ) ) . ctx = . empty ;
2021-07-10 17:58:37 +01:00
}
} ,
. keyword_error = > curr_ctx . ctx = . global_error_set ,
2020-05-26 23:45:18 +01:00
else = > curr_ctx . ctx = . empty ,
2021-07-10 17:58:37 +01:00
}
2020-05-26 23:45:18 +01:00
2021-07-10 17:58:37 +01:00
switch ( curr_ctx . ctx ) {
. field_access = > | r | curr_ctx . ctx = . {
2022-09-16 01:33:49 +01:00
. field_access = tokenLocAppend ( r , tok ) ,
2021-07-10 17:58:37 +01:00
} ,
else = > { } ,
}
2020-05-26 23:45:18 +01:00
}
}
2022-12-15 18:03:09 +00:00
if ( stack . popOrNull ( ) ) | state | {
switch ( state . ctx ) {
. empty = > { } ,
. label = > | filled | {
// We need to check this because the state could be a filled
// label if only a space follows it
2023-01-11 17:30:47 +00:00
if ( ! filled or prev_char ! = ' ' ) {
2022-12-15 18:03:09 +00:00
return state . ctx ;
}
} ,
else = > return state . ctx ,
2021-03-31 00:25:49 +01:00
}
2022-12-15 18:03:09 +00:00
}
2021-07-10 17:58:37 +01:00
2022-12-15 18:03:09 +00:00
if ( line . len = = 0 ) return . empty ;
2022-09-16 01:33:49 +01:00
2022-12-15 18:03:09 +00:00
var held_line = try allocator . dupeZ ( u8 , offsets . locToSlice ( text , line_loc ) ) ;
defer allocator . free ( held_line ) ;
2022-09-16 01:33:49 +01:00
2022-12-15 18:03:09 +00:00
switch ( line [ 0 ] ) {
'a' . . . 'z' , 'A' . . . 'Z' , '_' , '@' = > { } ,
else = > return . empty ,
}
var tokenizer = std . zig . Tokenizer . init ( held_line ) ;
const tok = tokenizer . next ( ) ;
return if ( tok . tag = = . identifier ) PositionContext { . var_access = tok . loc } else . empty ;
2020-05-26 23:45:18 +01:00
}
2020-05-28 01:39:36 +01:00
2020-06-10 14:12:00 +01:00
pub const Declaration = union ( enum ) {
2021-02-27 15:38:06 +00:00
/// Index of the ast node
2021-10-01 02:44:06 +01:00
ast_node : Ast . Node . Index ,
2021-02-27 15:38:06 +00:00
/// Function parameter
2022-09-24 20:25:32 +01:00
param_payload : struct {
param : Ast . full . FnProto . Param ,
2023-03-31 16:54:46 +01:00
param_idx : u16 ,
2022-09-24 20:25:32 +01:00
func : Ast . Node . Index ,
} ,
2020-06-10 14:12:00 +01:00
pointer_payload : struct {
2021-10-01 02:44:06 +01:00
name : Ast . TokenIndex ,
condition : Ast . Node . Index ,
2020-06-10 14:12:00 +01:00
} ,
2021-03-08 18:46:23 +00:00
array_payload : struct {
2021-10-01 02:44:06 +01:00
identifier : Ast . TokenIndex ,
array_expr : Ast . Node . Index ,
2021-03-08 18:46:23 +00:00
} ,
2021-10-01 02:44:06 +01:00
array_index : Ast . TokenIndex ,
2020-06-10 14:12:00 +01:00
switch_payload : struct {
2021-10-01 02:44:06 +01:00
node : Ast . TokenIndex ,
switch_expr : Ast . Node . Index ,
items : [ ] const Ast . Node . Index ,
2020-06-10 14:12:00 +01:00
} ,
2022-12-14 03:07:36 +00:00
label_decl : struct {
label : Ast . TokenIndex ,
block : Ast . Node . Index ,
} ,
2023-01-06 18:59:20 +00:00
/// always an identifier
error_token : Ast . Node . Index ,
2023-03-31 16:54:46 +01:00
pub fn eql ( a : Declaration , b : Declaration ) bool {
return std . meta . eql ( a , b ) ;
}
2020-06-10 14:12:00 +01:00
} ;
2020-06-10 17:01:44 +01:00
pub const DeclWithHandle = struct {
decl : * Declaration ,
2022-10-05 12:40:11 +01:00
handle : * const DocumentStore . Handle ,
2020-06-10 17:01:44 +01:00
2023-03-31 16:54:46 +01:00
pub fn eql ( a : DeclWithHandle , b : DeclWithHandle ) bool {
return a . decl . eql ( b . decl . * ) and std . mem . eql ( u8 , a . handle . uri , b . handle . uri ) ;
}
2021-10-01 02:44:06 +01:00
pub fn nameToken ( self : DeclWithHandle ) Ast . TokenIndex {
2020-06-10 17:01:44 +01:00
const tree = self . handle . tree ;
return switch ( self . decl . * ) {
2020-06-27 13:29:45 +01:00
. ast_node = > | n | getDeclNameToken ( tree , n ) . ? ,
2022-09-24 20:25:32 +01:00
. param_payload = > | pp | pp . param . name_token . ? ,
2021-03-01 13:32:19 +00:00
. pointer_payload = > | pp | pp . name ,
2021-03-08 18:46:23 +00:00
. array_payload = > | ap | ap . identifier ,
2021-03-09 18:53:59 +00:00
. array_index = > | ai | ai ,
2021-03-09 11:35:56 +00:00
. switch_payload = > | sp | sp . node ,
2022-12-14 03:07:36 +00:00
. label_decl = > | ld | ld . label ,
2023-01-06 18:59:20 +00:00
. error_token = > | et | et ,
2020-06-10 17:01:44 +01:00
} ;
}
2020-06-10 17:54:01 +01:00
2020-06-11 00:40:11 +01:00
fn isPublic ( self : DeclWithHandle ) bool {
return switch ( self . decl . * ) {
. ast_node = > | node | isNodePublic ( self . handle . tree , node ) ,
else = > true ,
} ;
}
2023-03-12 22:48:31 +00:00
pub fn resolveType ( self : DeclWithHandle , analyser : * Analyser ) ! ? TypeWithHandle {
2021-02-28 20:57:15 +00:00
const tree = self . handle . tree ;
const node_tags = tree . nodes . items ( . tag ) ;
const main_tokens = tree . nodes . items ( . main_token ) ;
2020-06-10 18:48:40 +01:00
return switch ( self . decl . * ) {
2023-03-12 22:48:31 +00:00
. ast_node = > | node | try analyser . resolveTypeOfNodeInternal (
2021-03-08 18:46:23 +00:00
. { . node = node , . handle = self . handle } ,
) ,
2022-09-24 20:25:32 +01:00
. param_payload = > | pay | {
2023-03-31 16:54:46 +01:00
// handle anytype
if ( pay . param . type_expr = = 0 ) {
var func_decl = Declaration { . ast_node = pay . func } ;
var func_buf : [ 1 ] Ast . Node . Index = undefined ;
const func = tree . fullFnProto ( & func_buf , pay . func ) . ? ;
var func_params_len : usize = 0 ;
var it = func . iterate ( & tree ) ;
while ( ast . nextFnParam ( & it ) ) | _ | {
func_params_len + = 1 ;
}
var refs = try references . callsiteReferences ( analyser . arena . allocator ( ) , analyser , . {
. decl = & func_decl ,
. handle = self . handle ,
} , false , false , false ) ;
// TODO: Set `workspace` to true; current problems
// - we gather dependencies, not dependents
// - stack overflow due to cyclically anytype resolution(?)
var possible = std . ArrayListUnmanaged ( Type . EitherEntry ) { } ;
var deduplicator = TypeWithHandle . Deduplicator { } ;
defer deduplicator . deinit ( analyser . gpa ) ;
for ( refs . items ) | ref | {
var handle = analyser . store . getOrLoadHandle ( ref . uri ) . ? ;
var call_buf : [ 1 ] Ast . Node . Index = undefined ;
var call = handle . tree . fullCall ( & call_buf , ref . call_node ) . ? ;
const real_param_idx = if ( func_params_len ! = 0 and pay . param_idx ! = 0 and call . ast . params . len = = func_params_len - 1 )
pay . param_idx - 1
else
pay . param_idx ;
if ( real_param_idx > = call . ast . params . len ) continue ;
if ( try analyser . resolveTypeOfNode ( . {
// TODO?: this is a """heuristic based approach"""
// perhaps it would be better to use proper self detection
// maybe it'd be a perf issue and this is fine?
// you figure it out future contributor <3
. node = call . ast . params [ real_param_idx ] ,
. handle = handle ,
} ) ) | ty | {
var gop = try deduplicator . getOrPut ( analyser . gpa , ty ) ;
if ( gop . found_existing ) continue ;
var loc = offsets . tokenToPosition ( handle . tree , main_tokens [ call . ast . params [ real_param_idx ] ] , . @ " utf-8 " ) ;
try possible . append ( analyser . arena . allocator ( ) , . { // TODO: Dedup
. type_with_handle = ty ,
. descriptor = try std . fmt . allocPrint ( analyser . arena . allocator ( ) , " {s}:{d}:{d} " , . { handle . uri , loc . line + 1 , loc . character + 1 } ) ,
} ) ;
}
}
return TypeWithHandle {
. type = . { . data = . { . either = try possible . toOwnedSlice ( analyser . arena . allocator ( ) ) } , . is_type_val = false } ,
. handle = self . handle ,
} ;
}
2022-09-24 20:25:32 +01:00
const param_decl = pay . param ;
2021-04-05 12:46:05 +01:00
if ( isMetaType ( self . handle . tree , param_decl . type_expr ) ) {
2023-03-12 22:48:31 +00:00
var bound_param_it = analyser . bound_type_params . iterator ( ) ;
2021-02-28 20:57:15 +00:00
while ( bound_param_it . next ( ) ) | entry | {
2021-06-04 12:06:11 +01:00
if ( std . meta . eql ( entry . key_ptr . * , param_decl ) ) return entry . value_ptr . * ;
2020-06-12 12:56:46 +01:00
}
2021-02-28 20:57:15 +00:00
return null ;
} else if ( node_tags [ param_decl . type_expr ] = = . identifier ) {
if ( param_decl . name_token ) | name_tok | {
2021-03-03 15:34:24 +00:00
if ( std . mem . eql ( u8 , tree . tokenSlice ( main_tokens [ param_decl . type_expr ] ) , tree . tokenSlice ( name_tok ) ) )
2021-02-28 20:57:15 +00:00
return null ;
}
}
2023-03-12 22:48:31 +00:00
return ( ( try analyser . resolveTypeOfNodeInternal (
2021-02-28 20:57:15 +00:00
. { . node = param_decl . type_expr , . handle = self . handle } ,
) ) orelse return null ) . instanceTypeVal ( ) ;
2020-06-10 17:54:01 +01:00
} ,
2023-03-12 22:48:31 +00:00
. pointer_payload = > | pay | try analyser . resolveUnwrapOptionalType (
( try analyser . resolveTypeOfNodeInternal ( . {
2020-06-10 17:54:01 +01:00
. node = pay . condition ,
. handle = self . handle ,
2023-03-12 22:48:31 +00:00
} ) ) orelse return null ,
2020-06-10 17:54:01 +01:00
) ,
2023-03-12 22:48:31 +00:00
. array_payload = > | pay | try analyser . resolveBracketAccessType (
( try analyser . resolveTypeOfNodeInternal ( . {
2021-03-08 18:46:23 +00:00
. node = pay . array_expr ,
. handle = self . handle ,
2023-03-12 22:48:31 +00:00
} ) ) orelse return null ,
2021-03-08 18:46:23 +00:00
. Single ,
) ,
2021-03-09 18:53:59 +00:00
. array_index = > TypeWithHandle {
2022-07-08 19:29:53 +01:00
. type = . { . data = . array_index , . is_type_val = false } ,
2021-03-09 18:53:59 +00:00
. handle = self . handle ,
} ,
2020-06-14 20:24:18 +01:00
. label_decl = > return null ,
2020-06-23 11:32:37 +01:00
. switch_payload = > | pay | {
if ( pay . items . len = = 0 ) return null ;
// TODO Peer type resolution, we just use the first item for now.
2023-03-12 22:48:31 +00:00
const switch_expr_type = ( try analyser . resolveTypeOfNodeInternal ( . {
2020-06-23 11:32:37 +01:00
. node = pay . switch_expr ,
. handle = self . handle ,
2023-03-12 22:48:31 +00:00
} ) ) orelse return null ;
2021-03-04 21:53:54 +00:00
if ( ! switch_expr_type . isUnionType ( ) )
2020-06-23 11:32:37 +01:00
return null ;
2023-01-24 20:43:48 +00:00
if ( node_tags [ pay . items [ 0 ] ] ! = . enum_literal ) return null ;
const scope_index = findContainerScopeIndex ( . { . node = switch_expr_type . type . data . other , . handle = switch_expr_type . handle } ) orelse return null ;
const scope_decls = switch_expr_type . handle . document_scope . scopes . items ( . decls ) ;
const candidate = scope_decls [ scope_index ] . getEntry ( tree . tokenSlice ( main_tokens [ pay . items [ 0 ] ] ) ) orelse return null ;
switch ( candidate . value_ptr . * ) {
. ast_node = > | node | {
2023-01-24 23:55:38 +00:00
if ( switch_expr_type . handle . tree . fullContainerField ( node ) ) | container_field | {
2023-01-24 20:43:48 +00:00
if ( container_field . ast . type_expr ! = 0 ) {
2023-03-12 22:48:31 +00:00
return ( ( try analyser . resolveTypeOfNodeInternal (
2023-01-24 20:43:48 +00:00
. { . node = container_field . ast . type_expr , . handle = switch_expr_type . handle } ,
) ) orelse return null ) . instanceTypeVal ( ) ;
}
2020-06-23 11:32:37 +01:00
}
2023-01-24 20:43:48 +00:00
} ,
else = > { } ,
2020-06-23 11:32:37 +01:00
}
return null ;
} ,
2023-01-06 18:59:20 +00:00
. error_token = > return null ,
2020-06-10 17:54:01 +01:00
} ;
}
2020-06-10 17:01:44 +01:00
} ;
2023-01-24 20:43:48 +00:00
fn findContainerScopeIndex ( container_handle : NodeWithHandle ) ? usize {
2020-06-11 00:40:11 +01:00
const container = container_handle . node ;
const handle = container_handle . handle ;
2021-10-03 00:39:24 +01:00
if ( ! ast . isContainer ( handle . tree , container ) ) return null ;
2020-06-11 00:40:11 +01:00
2023-02-21 22:11:35 +00:00
return for ( handle . document_scope . scopes . items ( . data ) , 0 . . ) | data , scope_index | {
2023-01-24 20:43:48 +00:00
switch ( data ) {
2020-06-11 00:40:11 +01:00
. container = > | node | if ( node = = container ) {
2023-01-24 20:43:48 +00:00
break scope_index ;
2020-06-11 00:40:11 +01:00
} ,
else = > { } ,
}
2021-03-01 13:32:19 +00:00
} else null ;
2020-06-11 00:40:11 +01:00
}
2023-03-12 22:48:31 +00:00
fn iterateSymbolsContainerInternal (
analyser : * Analyser ,
container_handle : NodeWithHandle ,
orig_handle : * const DocumentStore . Handle ,
comptime callback : anytype ,
context : anytype ,
instance_access : bool ,
) error { OutOfMemory } ! void {
2020-06-11 00:40:11 +01:00
const container = container_handle . node ;
const handle = container_handle . handle ;
2021-03-01 15:30:43 +00:00
const tree = handle . tree ;
const node_tags = tree . nodes . items ( . tag ) ;
const token_tags = tree . tokens . items ( . tag ) ;
const main_token = tree . nodes . items ( . main_token ) [ container ] ;
2021-03-10 16:04:14 +00:00
const is_enum = token_tags [ main_token ] = = . keyword_enum ;
2021-03-06 19:55:59 +00:00
2023-01-24 20:43:48 +00:00
const scope_decls = handle . document_scope . scopes . items ( . decls ) ;
const scope_uses = handle . document_scope . scopes . items ( . uses ) ;
const container_scope_index = findContainerScopeIndex ( container_handle ) orelse return ;
2021-03-10 16:04:14 +00:00
2023-01-24 20:43:48 +00:00
var decl_it = scope_decls [ container_scope_index ] . iterator ( ) ;
2021-03-10 16:04:14 +00:00
while ( decl_it . next ( ) ) | entry | {
2021-06-04 12:06:11 +01:00
switch ( entry . value_ptr . * ) {
2021-03-10 16:04:14 +00:00
. ast_node = > | node | {
if ( node_tags [ node ] . isContainerField ( ) ) {
if ( ! instance_access and ! is_enum ) continue ;
if ( instance_access and is_enum ) continue ;
2021-05-08 17:30:22 +01:00
} else if ( node_tags [ node ] = = . global_var_decl or
node_tags [ node ] = = . local_var_decl or
node_tags [ node ] = = . simple_var_decl or
2021-06-04 12:06:11 +01:00
node_tags [ node ] = = . aligned_var_decl )
{
if ( instance_access ) continue ;
2021-03-10 16:04:14 +00:00
}
} ,
. label_decl = > continue ,
else = > { } ,
2020-06-11 00:40:11 +01:00
}
2021-06-04 12:06:11 +01:00
const decl = DeclWithHandle { . decl = entry . value_ptr , . handle = handle } ;
2021-03-10 16:04:14 +00:00
if ( handle ! = orig_handle and ! decl . isPublic ( ) ) continue ;
try callback ( context , decl ) ;
}
2021-03-07 20:54:54 +00:00
2023-03-12 22:57:47 +00:00
for ( scope_uses [ container_scope_index ] ) | use | {
2022-07-12 00:57:41 +01:00
const use_token = tree . nodes . items ( . main_token ) [ use ] ;
2021-03-10 16:04:14 +00:00
const is_pub = use_token > 0 and token_tags [ use_token - 1 ] = = . keyword_pub ;
if ( handle ! = orig_handle and ! is_pub ) continue ;
2023-03-12 22:48:31 +00:00
const gop = try analyser . using_trail . getOrPut ( analyser . gpa , use ) ;
if ( gop . found_existing ) continue ;
2021-03-10 16:04:14 +00:00
2022-07-12 00:57:41 +01:00
const lhs = tree . nodes . items ( . data ) [ use ] . lhs ;
2023-03-12 22:48:31 +00:00
const use_expr = ( try analyser . resolveTypeOfNode ( . {
2021-03-10 16:04:14 +00:00
. node = lhs ,
. handle = handle ,
} ) ) orelse continue ;
const use_expr_node = switch ( use_expr . type . data ) {
. other = > | n | n ,
else = > continue ,
} ;
2023-03-12 22:48:31 +00:00
try analyser . iterateSymbolsContainerInternal (
2021-03-10 16:04:14 +00:00
. { . node = use_expr_node , . handle = use_expr . handle } ,
orig_handle ,
callback ,
context ,
false ,
) ;
2020-06-11 00:40:11 +01:00
}
2020-06-23 10:43:56 +01:00
}
2020-06-11 00:40:11 +01:00
2023-01-24 20:43:48 +00:00
pub const EnclosingScopeIterator = struct {
scope_locs : [ ] offsets . Loc ,
2023-03-13 00:43:13 +00:00
scope_children : [ ] const std . ArrayListUnmanaged ( Scope . Index ) ,
current_scope : Scope . Index ,
2023-01-24 20:43:48 +00:00
source_index : usize ,
2023-03-13 00:43:13 +00:00
pub fn next ( self : * EnclosingScopeIterator ) ? Scope . Index {
if ( self . current_scope = = . none ) return null ;
2023-01-24 20:43:48 +00:00
2023-03-13 00:43:13 +00:00
const child_scopes = self . scope_children [ @enumToInt ( self . current_scope ) ] ;
defer self . current_scope = for ( child_scopes . items ) | child_scope | {
const child_loc = self . scope_locs [ @enumToInt ( child_scope ) ] ;
if ( child_loc . start < = self . source_index and self . source_index < = child_loc . end ) {
break child_scope ;
2023-01-24 20:43:48 +00:00
}
2023-03-13 00:43:13 +00:00
} else . none ;
return self . current_scope ;
2023-01-24 20:43:48 +00:00
}
} ;
2023-03-12 22:48:31 +00:00
fn iterateEnclosingScopes ( document_scope : DocumentScope , source_index : usize ) EnclosingScopeIterator {
2023-01-24 20:43:48 +00:00
return . {
. scope_locs = document_scope . scopes . items ( . loc ) ,
2023-03-13 00:43:13 +00:00
. scope_children = document_scope . scopes . items ( . child_scopes ) ,
. current_scope = @intToEnum ( Scope . Index , 0 ) ,
2023-01-24 20:43:48 +00:00
. source_index = source_index ,
} ;
}
2023-03-12 22:48:31 +00:00
pub fn iterateSymbolsContainer (
analyser : * Analyser ,
container_handle : NodeWithHandle ,
orig_handle : * const DocumentStore . Handle ,
comptime callback : anytype ,
context : anytype ,
instance_access : bool ,
) error { OutOfMemory } ! void {
analyser . using_trail . clearRetainingCapacity ( ) ;
return try analyser . iterateSymbolsContainerInternal ( container_handle , orig_handle , callback , context , instance_access ) ;
2020-06-11 00:40:11 +01:00
}
2022-10-05 12:40:11 +01:00
pub fn iterateLabels ( handle : * const DocumentStore . Handle , source_index : usize , comptime callback : anytype , context : anytype ) error { OutOfMemory } ! void {
2023-01-24 20:43:48 +00:00
const scope_decls = handle . document_scope . scopes . items ( . decls ) ;
var scope_iterator = iterateEnclosingScopes ( handle . document_scope , source_index ) ;
while ( scope_iterator . next ( ) ) | scope_index | {
2023-03-13 00:43:13 +00:00
var decl_it = scope_decls [ @enumToInt ( scope_index ) ] . iterator ( ) ;
2023-01-24 20:43:48 +00:00
while ( decl_it . next ( ) ) | entry | {
switch ( entry . value_ptr . * ) {
. label_decl = > { } ,
else = > continue ,
2020-06-14 20:24:18 +01:00
}
2023-01-24 20:43:48 +00:00
try callback ( context , DeclWithHandle { . decl = entry . value_ptr , . handle = handle } ) ;
2020-06-14 20:24:18 +01:00
}
}
}
2023-03-12 22:48:31 +00:00
fn iterateSymbolsGlobalInternal (
analyser : * Analyser ,
handle : * const DocumentStore . Handle ,
source_index : usize ,
comptime callback : anytype ,
context : anytype ,
) error { OutOfMemory } ! void {
2023-01-24 20:43:48 +00:00
const scope_decls = handle . document_scope . scopes . items ( . decls ) ;
const scope_uses = handle . document_scope . scopes . items ( . uses ) ;
var scope_iterator = iterateEnclosingScopes ( handle . document_scope , source_index ) ;
while ( scope_iterator . next ( ) ) | scope_index | {
2023-03-13 00:43:13 +00:00
var decl_it = scope_decls [ @enumToInt ( scope_index ) ] . iterator ( ) ;
2023-01-24 20:43:48 +00:00
while ( decl_it . next ( ) ) | entry | {
if ( entry . value_ptr . * = = . ast_node and
handle . tree . nodes . items ( . tag ) [ entry . value_ptr . * . ast_node ] . isContainerField ( ) ) continue ;
if ( entry . value_ptr . * = = . label_decl ) continue ;
try callback ( context , DeclWithHandle { . decl = entry . value_ptr , . handle = handle } ) ;
}
2020-06-10 14:12:00 +01:00
2023-03-13 00:43:13 +00:00
for ( scope_uses [ @enumToInt ( scope_index ) ] ) | use | {
2023-03-12 22:48:31 +00:00
const gop = try analyser . using_trail . getOrPut ( analyser . gpa , use ) ;
if ( gop . found_existing ) continue ;
2021-02-26 20:26:52 +00:00
2023-03-12 22:48:31 +00:00
const use_expr = ( try analyser . resolveTypeOfNodeInternal (
2023-01-24 20:43:48 +00:00
. { . node = handle . tree . nodes . items ( . data ) [ use ] . lhs , . handle = handle } ,
) ) orelse continue ;
const use_expr_node = switch ( use_expr . type . data ) {
. other = > | n | n ,
else = > continue ,
} ;
2023-03-12 22:48:31 +00:00
try analyser . iterateSymbolsContainerInternal (
2023-01-24 20:43:48 +00:00
. { . node = use_expr_node , . handle = use_expr . handle } ,
handle ,
callback ,
context ,
false ,
) ;
2020-06-10 17:01:44 +01:00
}
}
}
2023-03-12 22:48:31 +00:00
pub fn iterateSymbolsGlobal (
analyser : * Analyser ,
handle : * const DocumentStore . Handle ,
source_index : usize ,
comptime callback : anytype ,
context : anytype ,
) error { OutOfMemory } ! void {
analyser . using_trail . clearRetainingCapacity ( ) ;
return try analyser . iterateSymbolsGlobalInternal ( handle , source_index , callback , context ) ;
2020-06-23 10:43:56 +01:00
}
2023-03-13 00:43:13 +00:00
pub fn innermostBlockScopeIndex ( handle : DocumentStore . Handle , source_index : usize ) Scope . Index {
var scope_iterator = iterateEnclosingScopes ( handle . document_scope , source_index ) ;
2023-03-13 23:54:31 +00:00
var scope_index : Scope . Index = . none ;
2023-03-13 00:43:13 +00:00
while ( scope_iterator . next ( ) ) | inner_scope | {
2023-03-13 23:54:31 +00:00
scope_index = inner_scope ;
2021-04-02 18:49:01 +01:00
}
2023-03-13 23:54:31 +00:00
return scope_index ;
2021-04-02 18:49:01 +01:00
}
2021-10-01 02:44:06 +01:00
pub fn innermostBlockScope ( handle : DocumentStore . Handle , source_index : usize ) Ast . Node . Index {
2023-03-13 23:54:31 +00:00
const scope_datas = handle . document_scope . scopes . items ( . data ) ;
const scope_parents = handle . document_scope . scopes . items ( . parent ) ;
var scope_index = innermostBlockScopeIndex ( handle , source_index ) ;
while ( true ) {
defer scope_index = scope_parents [ @enumToInt ( scope_index ) ] ;
switch ( scope_datas [ @enumToInt ( scope_index ) ] ) {
. container , . function , . block = > return scope_datas [ @enumToInt ( scope_index ) ] . toNodeIndex ( ) . ? ,
else = > { } ,
}
}
2021-04-04 13:28:57 +01:00
}
2022-10-05 12:40:11 +01:00
pub fn innermostContainer ( handle : * const DocumentStore . Handle , source_index : usize ) TypeWithHandle {
2023-01-24 20:43:48 +00:00
const scope_datas = handle . document_scope . scopes . items ( . data ) ;
2020-06-10 19:24:17 +01:00
2023-01-24 20:43:48 +00:00
var current = scope_datas [ 0 ] . container ;
if ( handle . document_scope . scopes . len = = 1 ) return TypeWithHandle . typeVal ( . { . node = current , . handle = handle } ) ;
var scope_iterator = iterateEnclosingScopes ( handle . document_scope , source_index ) ;
while ( scope_iterator . next ( ) ) | scope_index | {
2023-03-13 00:43:13 +00:00
switch ( scope_datas [ @enumToInt ( scope_index ) ] ) {
2023-01-24 20:43:48 +00:00
. container = > | node | current = node ,
else = > { } ,
2020-06-10 19:24:17 +01:00
}
}
2020-06-17 03:12:12 +01:00
return TypeWithHandle . typeVal ( . { . node = current , . handle = handle } ) ;
2020-06-10 19:24:17 +01:00
}
2023-03-12 22:48:31 +00:00
fn resolveUse ( analyser : * Analyser , uses : [ ] const Ast . Node . Index , symbol : [ ] const u8 , handle : * const DocumentStore . Handle ) error { OutOfMemory } ! ? DeclWithHandle {
2022-07-12 00:57:41 +01:00
for ( uses ) | index | {
2023-03-12 22:48:31 +00:00
const gop = try analyser . using_trail . getOrPut ( analyser . gpa , index ) ;
if ( gop . found_existing ) continue ;
2021-06-18 12:58:15 +01:00
if ( handle . tree . nodes . items ( . data ) . len < = index ) continue ;
const expr = . { . node = handle . tree . nodes . items ( . data ) [ index ] . lhs , . handle = handle } ;
2023-03-12 22:48:31 +00:00
const expr_type_node = ( try analyser . resolveTypeOfNode ( expr ) ) orelse
2021-04-05 12:52:41 +01:00
continue ;
2021-03-06 19:55:59 +00:00
2021-04-05 12:46:05 +01:00
const expr_type = . {
. node = switch ( expr_type_node . type . data ) {
2021-04-05 12:52:41 +01:00
. other = > | n | n ,
else = > continue ,
} ,
. handle = expr_type_node . handle ,
2021-03-04 12:26:11 +00:00
} ;
2021-04-05 12:46:05 +01:00
2023-03-12 22:48:31 +00:00
if ( try analyser . lookupSymbolContainer ( expr_type , symbol , false ) ) | candidate | {
2021-04-05 12:46:05 +01:00
if ( candidate . handle = = handle or candidate . isPublic ( ) ) {
return candidate ;
2021-03-04 12:26:11 +00:00
}
}
}
2020-06-11 00:40:11 +01:00
return null ;
}
2023-03-07 17:17:45 +00:00
pub fn lookupLabel (
handle : * const DocumentStore . Handle ,
symbol : [ ] const u8 ,
source_index : usize ,
) error { OutOfMemory } ! ? DeclWithHandle {
2023-01-24 20:43:48 +00:00
const scope_decls = handle . document_scope . scopes . items ( . decls ) ;
var scope_iterator = iterateEnclosingScopes ( handle . document_scope , source_index ) ;
while ( scope_iterator . next ( ) ) | scope_index | {
2023-03-13 00:43:13 +00:00
const candidate = scope_decls [ @enumToInt ( scope_index ) ] . getEntry ( symbol ) orelse continue ;
2023-01-24 20:43:48 +00:00
switch ( candidate . value_ptr . * ) {
. label_decl = > { } ,
else = > continue ,
2020-06-14 20:24:18 +01:00
}
2023-01-24 20:43:48 +00:00
return DeclWithHandle {
. decl = candidate . value_ptr ,
. handle = handle ,
} ;
2020-06-14 20:24:18 +01:00
}
return null ;
}
2023-03-12 22:48:31 +00:00
pub fn lookupSymbolGlobal ( analyser : * Analyser , handle : * const DocumentStore . Handle , symbol : [ ] const u8 , source_index : usize ) error { OutOfMemory } ! ? DeclWithHandle {
2023-03-13 00:43:13 +00:00
const scope_parents = handle . document_scope . scopes . items ( . parent ) ;
2023-01-24 20:43:48 +00:00
const scope_decls = handle . document_scope . scopes . items ( . decls ) ;
const scope_uses = handle . document_scope . scopes . items ( . uses ) ;
2023-03-13 00:43:13 +00:00
var current_scope = innermostBlockScopeIndex ( handle . * , source_index ) ;
while ( current_scope ! = . none ) {
const scope_index = @enumToInt ( current_scope ) ;
defer current_scope = scope_parents [ scope_index ] ;
if ( scope_decls [ scope_index ] . getEntry ( symbol ) ) | candidate | {
switch ( candidate . value_ptr . * ) {
. ast_node = > | node | {
if ( handle . tree . nodes . items ( . tag ) [ node ] . isContainerField ( ) ) continue ;
} ,
. label_decl = > continue ,
else = > { } ,
2020-06-10 17:01:44 +01:00
}
2023-03-13 00:43:13 +00:00
return DeclWithHandle {
. decl = candidate . value_ptr ,
. handle = handle ,
} ;
2021-03-07 20:54:54 +00:00
}
2023-03-13 00:43:13 +00:00
if ( try analyser . resolveUse ( scope_uses [ scope_index ] , symbol , handle ) ) | result | return result ;
2020-06-10 17:01:44 +01:00
}
2023-03-13 00:43:13 +00:00
2020-06-10 17:01:44 +01:00
return null ;
}
2021-04-04 23:35:33 +01:00
pub fn lookupSymbolContainer (
2023-03-12 22:48:31 +00:00
analyser : * Analyser ,
2020-06-11 00:40:11 +01:00
container_handle : NodeWithHandle ,
symbol : [ ] const u8 ,
2020-06-18 13:43:03 +01:00
/// If true, we are looking up the symbol like we are accessing through a field access
/// of an instance of the type, otherwise as a field access of the type value itself.
instance_access : bool ,
2020-06-11 00:40:11 +01:00
) error { OutOfMemory } ! ? DeclWithHandle {
2020-06-10 17:54:01 +01:00
const container = container_handle . node ;
const handle = container_handle . handle ;
2021-03-01 15:30:43 +00:00
const tree = handle . tree ;
const node_tags = tree . nodes . items ( . tag ) ;
const token_tags = tree . tokens . items ( . tag ) ;
const main_token = tree . nodes . items ( . main_token ) [ container ] ;
2020-06-10 23:00:13 +01:00
2021-03-11 12:59:09 +00:00
const is_enum = token_tags [ main_token ] = = . keyword_enum ;
2023-01-24 20:43:48 +00:00
const scope_decls = handle . document_scope . scopes . items ( . decls ) ;
const scope_uses = handle . document_scope . scopes . items ( . uses ) ;
2020-06-18 13:43:03 +01:00
2023-01-24 20:43:48 +00:00
if ( findContainerScopeIndex ( container_handle ) ) | container_scope_index | {
if ( scope_decls [ container_scope_index ] . getEntry ( symbol ) ) | candidate | {
2021-06-04 12:06:11 +01:00
switch ( candidate . value_ptr . * ) {
2020-06-10 17:01:44 +01:00
. ast_node = > | node | {
2021-03-01 15:30:43 +00:00
if ( node_tags [ node ] . isContainerField ( ) ) {
2020-06-18 13:43:03 +01:00
if ( ! instance_access and ! is_enum ) return null ;
if ( instance_access and is_enum ) return null ;
}
2020-06-10 17:01:44 +01:00
} ,
2020-06-14 20:24:18 +01:00
. label_decl = > unreachable ,
2020-06-10 17:01:44 +01:00
else = > { } ,
}
2021-06-04 12:06:11 +01:00
return DeclWithHandle { . decl = candidate . value_ptr , . handle = handle } ;
2020-06-10 17:01:44 +01:00
}
2023-03-12 22:57:47 +00:00
if ( try analyser . resolveUse ( scope_uses [ container_scope_index ] , symbol , handle ) ) | result | return result ;
2020-06-10 17:01:44 +01:00
}
2020-06-10 23:00:13 +01:00
return null ;
2020-06-10 17:01:44 +01:00
}
2021-06-04 12:06:11 +01:00
const CompletionContext = struct {
pub fn hash ( self : @This ( ) , item : types . CompletionItem ) u32 {
2021-06-24 11:38:01 +01:00
_ = self ;
2021-06-04 12:06:11 +01:00
return @truncate ( u32 , std . hash . Wyhash . hash ( 0 , item . label ) ) ;
}
2021-03-29 12:02:58 +01:00
2022-02-03 05:00:31 +00:00
pub fn eql ( self : @This ( ) , a : types . CompletionItem , b : types . CompletionItem , b_index : usize ) bool {
2021-06-24 11:38:01 +01:00
_ = self ;
2022-02-03 05:00:31 +00:00
_ = b_index ;
2021-06-04 12:06:11 +01:00
return std . mem . eql ( u8 , a . label , b . label ) ;
}
} ;
2021-03-29 12:02:58 +01:00
pub const CompletionSet = std . ArrayHashMapUnmanaged (
types . CompletionItem ,
void ,
2021-06-04 12:06:11 +01:00
CompletionContext ,
2021-03-29 12:02:58 +01:00
false ,
) ;
comptime {
2021-06-04 12:06:11 +01:00
std . debug . assert ( @sizeOf ( types . CompletionItem ) = = @sizeOf ( CompletionSet . Data ) ) ;
2021-03-29 12:02:58 +01:00
}
2020-06-10 17:01:44 +01:00
pub const DocumentScope = struct {
2023-01-24 20:43:48 +00:00
scopes : std . MultiArrayList ( Scope ) ,
2021-03-29 12:02:58 +01:00
error_completions : CompletionSet ,
enum_completions : CompletionSet ,
2020-06-10 14:12:00 +01:00
2021-12-02 05:16:15 +00:00
pub fn deinit ( self : * DocumentScope , allocator : std . mem . Allocator ) void {
2023-03-13 00:43:13 +00:00
for (
self . scopes . items ( . decls ) ,
self . scopes . items ( . child_scopes ) ,
self . scopes . items ( . tests ) ,
self . scopes . items ( . uses ) ,
) | * decls , * child_scopes , tests , uses | {
2023-03-12 22:57:47 +00:00
decls . deinit ( allocator ) ;
2023-03-13 00:43:13 +00:00
child_scopes . deinit ( allocator ) ;
2023-03-12 22:57:47 +00:00
allocator . free ( tests ) ;
allocator . free ( uses ) ;
2020-06-10 14:12:00 +01:00
}
2022-08-23 11:44:26 +01:00
self . scopes . deinit ( allocator ) ;
2023-01-24 20:43:48 +00:00
2021-06-04 12:06:11 +01:00
for ( self . error_completions . entries . items ( . key ) ) | item | {
2023-01-06 18:59:20 +00:00
if ( item . detail ) | detail | allocator . free ( detail ) ;
2022-12-27 06:47:57 +00:00
switch ( item . documentation orelse continue ) {
. string = > | str | allocator . free ( str ) ,
. MarkupContent = > | content | allocator . free ( content . value ) ,
}
2021-03-29 12:02:58 +01:00
}
self . error_completions . deinit ( allocator ) ;
2021-06-04 12:06:11 +01:00
for ( self . enum_completions . entries . items ( . key ) ) | item | {
2023-01-06 18:59:20 +00:00
if ( item . detail ) | detail | allocator . free ( detail ) ;
2022-12-27 06:47:57 +00:00
switch ( item . documentation orelse continue ) {
. string = > | str | allocator . free ( str ) ,
. MarkupContent = > | content | allocator . free ( content . value ) ,
}
2021-03-29 12:02:58 +01:00
}
self . enum_completions . deinit ( allocator ) ;
2020-06-10 14:12:00 +01:00
}
} ;
pub const Scope = struct {
pub const Data = union ( enum ) {
2021-10-01 02:44:06 +01:00
container : Ast . Node . Index , // .tag is ContainerDecl or Root or ErrorSetDecl
function : Ast . Node . Index , // .tag is FnProto
block : Ast . Node . Index , // .tag is Block
2020-06-10 14:12:00 +01:00
other ,
2023-01-24 20:43:48 +00:00
pub fn toNodeIndex ( self : Data ) ? Ast . Node . Index {
return switch ( self ) {
. container , . function , . block = > | idx | idx ,
else = > null ,
} ;
}
2020-06-10 14:12:00 +01:00
} ;
2023-03-13 00:43:13 +00:00
pub const Index = enum ( u32 ) {
none = std . math . maxInt ( u32 ) ,
_ ,
} ;
2022-09-16 01:33:49 +01:00
loc : offsets . Loc ,
2023-03-13 00:43:13 +00:00
parent : Index ,
data : Data ,
2022-08-23 11:44:26 +01:00
decls : std . StringHashMapUnmanaged ( Declaration ) = . { } ,
2023-03-13 00:43:13 +00:00
child_scopes : std . ArrayListUnmanaged ( Scope . Index ) = . { } ,
2023-03-12 22:57:47 +00:00
tests : [ ] const Ast . Node . Index = & . { } ,
uses : [ ] const Ast . Node . Index = & . { } ,
2020-06-10 14:12:00 +01:00
} ;
2021-12-02 05:16:15 +00:00
pub fn makeDocumentScope ( allocator : std . mem . Allocator , tree : Ast ) ! DocumentScope {
2023-03-11 18:43:14 +00:00
const tracy_zone = tracy . trace ( @src ( ) ) ;
defer tracy_zone . end ( ) ;
2022-08-23 11:44:26 +01:00
var document_scope = DocumentScope {
. scopes = . { } ,
. error_completions = . { } ,
. enum_completions = . { } ,
} ;
errdefer document_scope . deinit ( allocator ) ;
2020-07-08 02:05:44 +01:00
2023-03-13 00:43:13 +00:00
var current_scope : Scope . Index = . none ;
try makeInnerScope ( . {
. allocator = allocator ,
2022-08-23 11:44:26 +01:00
. scopes = & document_scope . scopes ,
2023-03-13 00:43:13 +00:00
. current_scope = & current_scope ,
. tree = tree ,
2022-08-23 11:44:26 +01:00
. errors = & document_scope . error_completions ,
. enums = & document_scope . enum_completions ,
2021-04-05 23:04:27 +01:00
} , 0 ) ;
2022-08-23 11:44:26 +01:00
return document_scope ;
2020-06-10 14:12:00 +01:00
}
2021-04-05 23:04:27 +01:00
const ScopeContext = struct {
2023-03-13 00:43:13 +00:00
allocator : std . mem . Allocator ,
2023-01-24 20:43:48 +00:00
scopes : * std . MultiArrayList ( Scope ) ,
2023-03-13 00:43:13 +00:00
current_scope : * Scope . Index ,
tree : Ast ,
2021-04-05 23:04:27 +01:00
enums : * CompletionSet ,
errors : * CompletionSet ,
2023-03-13 00:43:13 +00:00
fn pushScope ( context : ScopeContext , loc : offsets . Loc , data : Scope . Data ) error { OutOfMemory } ! usize {
try context . scopes . append ( context . allocator , . {
. parent = context . current_scope . * ,
. loc = loc ,
. data = data ,
} ) ;
const new_scope = @intToEnum ( Scope . Index , context . scopes . len - 1 ) ;
if ( context . current_scope . * ! = . none ) {
try context . scopes . items ( . child_scopes ) [ @enumToInt ( context . current_scope . * ) ] . append ( context . allocator , new_scope ) ;
}
context . current_scope . * = new_scope ;
return @enumToInt ( new_scope ) ;
}
fn popScope ( context : ScopeContext ) void {
const parent_scope = context . scopes . items ( . parent ) [ @enumToInt ( context . current_scope . * ) ] ;
context . current_scope . * = parent_scope ;
}
2021-04-05 23:04:27 +01:00
} ;
2023-03-13 00:43:13 +00:00
fn makeInnerScope ( context : ScopeContext , node_idx : Ast . Node . Index ) error { OutOfMemory } ! void {
2023-03-11 18:43:14 +00:00
const tracy_zone = tracy . trace ( @src ( ) ) ;
defer tracy_zone . end ( ) ;
2023-03-13 00:43:13 +00:00
const allocator = context . allocator ;
2021-04-05 23:04:27 +01:00
const scopes = context . scopes ;
const tree = context . tree ;
2021-02-27 15:38:06 +00:00
const tags = tree . nodes . items ( . tag ) ;
2023-03-13 00:43:13 +00:00
const scope_index = try context . pushScope (
offsets . nodeToLoc ( tree , node_idx ) ,
. { . container = node_idx } ,
) ;
defer context . popScope ( ) ;
2021-03-29 10:28:52 +01:00
2023-01-06 18:59:20 +00:00
var buf : [ 2 ] Ast . Node . Index = undefined ;
2023-01-24 23:55:38 +00:00
const container_decl = tree . fullContainerDecl ( & buf , node_idx ) . ? ;
2023-03-12 22:57:47 +00:00
var tests = std . ArrayListUnmanaged ( Ast . Node . Index ) { } ;
errdefer tests . deinit ( allocator ) ;
var uses = std . ArrayListUnmanaged ( Ast . Node . Index ) { } ;
errdefer uses . deinit ( allocator ) ;
2023-01-24 23:55:38 +00:00
for ( container_decl . ast . members ) | decl | {
2021-04-05 23:04:27 +01:00
if ( tags [ decl ] = = . @ " usingnamespace " ) {
2023-03-12 22:57:47 +00:00
try uses . append ( allocator , decl ) ;
2021-04-05 23:04:27 +01:00
continue ;
}
2020-06-10 14:12:00 +01:00
2023-03-13 00:43:13 +00:00
try makeScopeInternal ( context , decl ) ;
2021-04-05 23:04:27 +01:00
const name = getDeclName ( tree , decl ) orelse continue ;
2021-03-29 13:35:14 +01:00
2021-04-05 23:04:27 +01:00
if ( tags [ decl ] = = . test_decl ) {
2023-03-12 22:57:47 +00:00
try tests . append ( allocator , decl ) ;
2021-04-05 23:04:27 +01:00
continue ;
}
2023-03-20 18:39:15 +00:00
try scopes . items ( . decls ) [ scope_index ] . put ( allocator , name , . { . ast_node = decl } ) ;
2021-03-29 13:41:58 +01:00
2023-01-06 18:59:20 +00:00
if ( container_decl . ast . enum_token ! = null ) {
if ( std . mem . eql ( u8 , name , " _ " ) ) return ;
const Documentation = @TypeOf ( @as ( types . CompletionItem , undefined ) . documentation ) ;
2022-12-27 06:47:57 +00:00
2023-01-06 18:59:20 +00:00
var doc : Documentation = if ( try getDocComments ( allocator , tree , decl , . markdown ) ) | docs | . { . MarkupContent = types . MarkupContent { . kind = . markdown , . value = docs } } else null ;
var gop_res = try context . enums . getOrPut ( allocator , . {
. label = name ,
. kind = . Constant ,
. insertText = name ,
. insertTextFormat = . PlainText ,
. documentation = doc ,
} ) ;
if ( gop_res . found_existing ) {
if ( doc ) | d | allocator . free ( d . MarkupContent . value ) ;
2020-06-11 01:20:44 +01:00
}
2020-06-10 14:12:00 +01:00
}
2021-04-05 23:04:27 +01:00
}
2023-03-12 22:57:47 +00:00
scopes . items ( . tests ) [ scope_index ] = try tests . toOwnedSlice ( allocator ) ;
scopes . items ( . uses ) [ scope_index ] = try uses . toOwnedSlice ( allocator ) ;
2021-04-05 23:04:27 +01:00
}
2023-03-20 18:51:42 +00:00
/// If `node_idx` is a block it's scope index will be returned
/// Otherwise, a new scope will be created that will enclose `node_idx`
fn makeBlockScopeInternal ( context : ScopeContext , node_idx : Ast . Node . Index ) error { OutOfMemory } ! ? usize {
if ( node_idx = = 0 ) return null ;
const tags = context . tree . nodes . items ( . tag ) ;
// if node_idx is a block, the next scope will be a block so we store its index here
const block_scope_index = context . scopes . len ;
try makeScopeInternal ( context , node_idx ) ;
switch ( tags [ node_idx ] ) {
. block ,
. block_semicolon ,
. block_two ,
. block_two_semicolon ,
= > {
std . debug . assert ( context . scopes . items ( . data ) [ block_scope_index ] = = . block ) ;
return block_scope_index ;
} ,
else = > {
const new_scope = try context . pushScope (
offsets . nodeToLoc ( context . tree , node_idx ) ,
. other ,
) ;
context . popScope ( ) ;
return new_scope ;
} ,
}
}
2023-03-13 00:43:13 +00:00
fn makeScopeInternal ( context : ScopeContext , node_idx : Ast . Node . Index ) error { OutOfMemory } ! void {
2023-03-12 22:49:21 +00:00
if ( node_idx = = 0 ) return ;
2023-03-13 00:43:13 +00:00
const allocator = context . allocator ;
2021-04-05 23:04:27 +01:00
const scopes = context . scopes ;
const tree = context . tree ;
const tags = tree . nodes . items ( . tag ) ;
const token_tags = tree . tokens . items ( . tag ) ;
const data = tree . nodes . items ( . data ) ;
const main_tokens = tree . nodes . items ( . main_token ) ;
const node_tag = tags [ node_idx ] ;
2021-03-26 19:04:51 +00:00
switch ( node_tag ) {
2023-03-12 22:49:21 +00:00
. root = > unreachable ,
2021-04-05 23:04:27 +01:00
. container_decl ,
. container_decl_trailing ,
. container_decl_arg ,
. container_decl_arg_trailing ,
. container_decl_two ,
. container_decl_two_trailing ,
. tagged_union ,
. tagged_union_trailing ,
. tagged_union_two ,
. tagged_union_two_trailing ,
. tagged_union_enum_tag ,
. tagged_union_enum_tag_trailing ,
= > {
2023-03-13 00:43:13 +00:00
try makeInnerScope ( context , node_idx ) ;
2021-04-05 23:04:27 +01:00
} ,
2023-01-24 23:55:38 +00:00
. error_set_decl = > {
2023-03-13 00:43:13 +00:00
const scope_index = try context . pushScope (
offsets . nodeToLoc ( tree , node_idx ) ,
. { . container = node_idx } ,
) ;
defer context . popScope ( ) ;
2023-01-24 23:55:38 +00:00
2023-03-11 17:36:48 +00:00
// All identifiers in main_token..data.rhs are error fields.
var tok_i = main_tokens [ node_idx ] + 2 ;
while ( tok_i < data [ node_idx ] . rhs ) : ( tok_i + = 1 ) {
switch ( token_tags [ tok_i ] ) {
. doc_comment , . comma = > { } ,
. identifier = > {
const name = offsets . tokenToSlice ( tree , tok_i ) ;
2023-03-20 18:39:15 +00:00
try scopes . items ( . decls ) [ scope_index ] . put ( allocator , name , . { . error_token = tok_i } ) ;
2023-03-11 17:36:48 +00:00
const gop = try context . errors . getOrPut ( allocator , . {
. label = name ,
. kind = . Constant ,
//.detail =
. insertText = name ,
. insertTextFormat = . PlainText ,
} ) ;
if ( ! gop . found_existing ) {
gop . key_ptr . detail = try std . fmt . allocPrint ( allocator , " error.{s} " , . { name } ) ;
}
} ,
else = > { } ,
2023-01-24 23:55:38 +00:00
}
}
} ,
2021-03-06 19:55:59 +00:00
. fn_proto ,
. fn_proto_one ,
. fn_proto_simple ,
. fn_proto_multi ,
. fn_decl ,
= > | fn_tag | {
2021-10-01 02:44:06 +01:00
var buf : [ 1 ] Ast . Node . Index = undefined ;
2023-01-24 23:55:38 +00:00
const func = tree . fullFnProto ( & buf , node_idx ) . ? ;
2020-06-10 14:12:00 +01:00
2023-03-13 00:43:13 +00:00
const scope_index = try context . pushScope (
offsets . nodeToLoc ( tree , node_idx ) ,
. { . function = node_idx } ,
) ;
defer context . popScope ( ) ;
2020-06-10 14:12:00 +01:00
2023-03-31 16:54:46 +01:00
// NOTE: We count the param index ourselves
// as param_i stops counting; TODO: change this
var param_index : usize = 0 ;
2022-04-01 00:01:43 +01:00
var it = func . iterate ( & tree ) ;
2022-08-25 21:17:38 +01:00
while ( ast . nextFnParam ( & it ) ) | param | {
2021-03-29 10:28:52 +01:00
// Add parameter decls
2021-02-27 20:55:39 +00:00
if ( param . name_token ) | name_token | {
2023-03-20 18:39:15 +00:00
try scopes . items ( . decls ) [ scope_index ] . put (
2022-08-23 11:44:26 +01:00
allocator ,
2021-03-29 10:28:52 +01:00
tree . tokenSlice ( name_token ) ,
2023-03-31 16:54:46 +01:00
. { . param_payload = . { . param = param , . param_idx = @intCast ( u16 , param_index ) , . func = node_idx } } ,
2023-03-20 18:39:15 +00:00
) ;
2020-06-10 14:12:00 +01:00
}
2021-03-29 10:28:52 +01:00
// Visit parameter types to pick up any error sets and enum
// completions
2023-03-13 00:43:13 +00:00
try makeScopeInternal ( context , param . type_expr ) ;
2023-03-31 16:54:46 +01:00
param_index + = 1 ;
2020-06-10 14:12:00 +01:00
}
2021-03-30 10:23:09 +01:00
2021-04-04 00:03:25 +01:00
if ( fn_tag = = . fn_decl ) blk : {
if ( data [ node_idx ] . lhs = = 0 ) break : blk ;
const return_type_node = data [ data [ node_idx ] . lhs ] . rhs ;
// Visit the return type
2023-03-13 00:43:13 +00:00
try makeScopeInternal ( context , return_type_node ) ;
2021-03-02 14:32:38 +00:00
}
2021-04-04 00:03:25 +01:00
// Visit the function body
2023-03-13 00:43:13 +00:00
try makeScopeInternal ( context , data [ node_idx ] . rhs ) ;
2020-06-10 14:12:00 +01:00
} ,
2021-03-06 19:55:59 +00:00
. block ,
. block_semicolon ,
. block_two ,
. block_two_semicolon ,
= > {
2021-02-27 20:55:39 +00:00
const first_token = tree . firstToken ( node_idx ) ;
2023-03-20 19:24:31 +00:00
const last_token = ast . lastToken ( tree , node_idx ) ;
// the last token may not always be the closing brace because of broken ast
// so we look at most 16 characters ahead to find the closing brace
// TODO this should automatically be done by `ast.lastToken`
var end_index = offsets . tokenToLoc ( tree , last_token ) . start ;
const lookahead_buffer = tree . source [ end_index . . @min ( tree . source . len , end_index + 16 ) ] ;
end_index + = std . mem . indexOfScalar ( u8 , lookahead_buffer , '}' ) orelse 0 ;
2023-03-17 19:02:51 +00:00
2023-03-13 00:43:13 +00:00
const scope_index = try context . pushScope (
2023-03-20 19:24:31 +00:00
. {
. start = offsets . tokenToIndex ( tree , main_tokens [ node_idx ] ) ,
. end = end_index ,
} ,
2023-03-13 00:43:13 +00:00
. { . block = node_idx } ,
) ;
defer context . popScope ( ) ;
2020-06-14 20:24:18 +01:00
2023-03-20 18:51:42 +00:00
// if labeled block
if ( token_tags [ first_token ] = = . identifier ) {
try scopes . items ( . decls ) [ scope_index ] . putNoClobber (
allocator ,
tree . tokenSlice ( first_token ) ,
. { . label_decl = . { . label = first_token , . block = node_idx } } ,
) ;
}
2022-08-17 23:52:21 +01:00
var buffer : [ 2 ] Ast . Node . Index = undefined ;
const statements = ast . blockStatements ( tree , node_idx , & buffer ) . ? ;
2020-06-10 14:12:00 +01:00
2022-07-12 00:57:41 +01:00
for ( statements ) | idx | {
2023-03-13 00:43:13 +00:00
try makeScopeInternal ( context , idx ) ;
2023-01-24 23:55:38 +00:00
if ( tree . fullVarDecl ( idx ) ) | var_decl | {
2021-02-27 20:55:39 +00:00
const name = tree . tokenSlice ( var_decl . ast . mut_token + 1 ) ;
2023-03-20 18:39:15 +00:00
try scopes . items ( . decls ) [ scope_index ] . put ( allocator , name , . { . ast_node = idx } ) ;
2020-06-10 14:12:00 +01:00
}
}
} ,
2021-03-06 19:55:59 +00:00
. @ " if " ,
. if_simple ,
= > {
2023-01-24 23:55:38 +00:00
const if_node = ast . fullIf ( tree , node_idx ) . ? ;
2020-06-10 14:12:00 +01:00
2023-03-20 18:51:42 +00:00
const then_scope = ( try makeBlockScopeInternal ( context , if_node . ast . then_expr ) ) . ? ;
2020-06-10 14:12:00 +01:00
2023-03-20 18:51:42 +00:00
if ( if_node . payload_token ) | payload | {
2021-02-27 20:55:39 +00:00
const name_token = payload + @boolToInt ( token_tags [ payload ] = = . asterisk ) ;
std . debug . assert ( token_tags [ name_token ] = = . identifier ) ;
const name = tree . tokenSlice ( name_token ) ;
2023-03-20 18:51:42 +00:00
try scopes . items ( . decls ) [ then_scope ] . put (
allocator ,
name ,
. { . pointer_payload = . { . name = name_token , . condition = if_node . ast . cond_expr } } ,
) ;
2020-06-10 14:12:00 +01:00
}
2021-02-27 20:55:39 +00:00
if ( if_node . ast . else_expr ! = 0 ) {
2023-03-20 18:51:42 +00:00
const else_scope = ( try makeBlockScopeInternal ( context , if_node . ast . else_expr ) ) . ? ;
2021-02-27 20:55:39 +00:00
if ( if_node . error_token ) | err_token | {
const name = tree . tokenSlice ( err_token ) ;
2023-03-20 18:51:42 +00:00
try scopes . items ( . decls ) [ else_scope ] . put ( allocator , name , . { . ast_node = if_node . ast . else_expr } ) ;
2020-06-10 14:12:00 +01:00
}
}
} ,
2021-04-05 23:04:27 +01:00
. @ " catch " = > {
2023-03-13 00:43:13 +00:00
try makeScopeInternal ( context , data [ node_idx ] . lhs ) ;
2021-04-07 14:42:33 +01:00
2023-03-20 18:51:42 +00:00
const expr_scope = ( try makeBlockScopeInternal ( context , data [ node_idx ] . rhs ) ) . ? ;
2021-04-07 14:42:33 +01:00
2023-03-20 18:51:42 +00:00
const catch_token = main_tokens [ node_idx ] + 2 ;
if ( token_tags . len > catch_token and
token_tags [ catch_token - 1 ] = = . pipe and
token_tags [ catch_token ] = = . identifier )
2021-04-07 16:26:50 +01:00
{
2023-03-20 18:51:42 +00:00
const name = tree . tokenSlice ( catch_token ) ;
try scopes . items ( . decls ) [ expr_scope ] . put ( allocator , name , . { . ast_node = data [ node_idx ] . rhs } ) ;
2021-04-07 14:42:33 +01:00
}
2021-04-05 23:04:27 +01:00
} ,
2021-03-06 19:55:59 +00:00
. @ " while " ,
. while_simple ,
. while_cont ,
2021-03-26 19:04:51 +00:00
= > {
2023-03-20 18:51:42 +00:00
// label_token: inline_token while (cond_expr) |payload_token| : (cont_expr) then_expr else else_expr
2023-01-24 23:55:38 +00:00
const while_node = ast . fullWhile ( tree , node_idx ) . ? ;
2021-03-08 18:46:23 +00:00
2023-03-20 18:51:42 +00:00
try makeScopeInternal ( context , while_node . ast . cond_expr ) ;
const cont_scope = try makeBlockScopeInternal ( context , while_node . ast . cont_expr ) ;
const then_scope = ( try makeBlockScopeInternal ( context , while_node . ast . then_expr ) ) . ? ;
const else_scope = try makeBlockScopeInternal ( context , while_node . ast . else_expr ) ;
2021-02-27 20:55:39 +00:00
if ( while_node . label_token ) | label | {
2021-03-02 14:32:38 +00:00
std . debug . assert ( token_tags [ label ] = = . identifier ) ;
2023-03-13 00:43:13 +00:00
2023-03-20 18:51:42 +00:00
const name = tree . tokenSlice ( label ) ;
try scopes . items ( . decls ) [ then_scope ] . put (
2023-03-20 18:39:15 +00:00
allocator ,
2023-03-20 18:51:42 +00:00
name ,
2023-03-20 18:39:15 +00:00
. { . label_decl = . { . label = label , . block = while_node . ast . then_expr } } ,
) ;
2023-03-20 18:51:42 +00:00
if ( else_scope ) | index | {
try scopes . items ( . decls ) [ index ] . put (
allocator ,
name ,
. { . label_decl = . { . label = label , . block = while_node . ast . else_expr } } ,
) ;
}
2020-06-14 20:24:18 +01:00
}
2021-02-27 20:55:39 +00:00
if ( while_node . payload_token ) | payload | {
const name_token = payload + @boolToInt ( token_tags [ payload ] = = . asterisk ) ;
std . debug . assert ( token_tags [ name_token ] = = . identifier ) ;
const name = tree . tokenSlice ( name_token ) ;
2023-03-20 18:51:42 +00:00
const decl : Declaration = . {
2021-04-15 10:21:49 +01:00
. pointer_payload = . {
. name = name_token ,
. condition = while_node . ast . cond_expr ,
} ,
2023-03-20 18:51:42 +00:00
} ;
if ( cont_scope ) | index | {
try scopes . items ( . decls ) [ index ] . put ( allocator , name , decl ) ;
2021-03-08 18:46:23 +00:00
}
2023-03-20 18:51:42 +00:00
try scopes . items ( . decls ) [ then_scope ] . put ( allocator , name , decl ) ;
2020-06-10 14:12:00 +01:00
}
2023-03-20 18:51:42 +00:00
if ( while_node . error_token ) | err_token | {
std . debug . assert ( token_tags [ err_token ] = = . identifier ) ;
const name = tree . tokenSlice ( err_token ) ;
try scopes . items ( . decls ) [ else_scope . ? ] . put ( allocator , name , . { . ast_node = while_node . ast . else_expr } ) ;
2020-06-10 14:12:00 +01:00
}
} ,
2023-02-21 22:11:35 +00:00
. @ " for " ,
. for_simple ,
= > {
2023-03-20 18:51:42 +00:00
// label_token: inline_token for (inputs) |capture_tokens| then_expr else else_expr
2023-02-21 22:11:35 +00:00
const for_node = ast . fullFor ( tree , node_idx ) . ? ;
2023-03-20 18:51:42 +00:00
for ( for_node . ast . inputs ) | input_node | {
try makeScopeInternal ( context , input_node ) ;
2023-02-21 22:11:35 +00:00
}
2023-03-20 18:51:42 +00:00
const then_scope = ( try makeBlockScopeInternal ( context , for_node . ast . then_expr ) ) . ? ;
const else_scope = try makeBlockScopeInternal ( context , for_node . ast . else_expr ) ;
2023-02-21 22:11:35 +00:00
var capture_token = for_node . payload_token ;
for ( for_node . ast . inputs ) | input | {
if ( capture_token + 1 > = tree . tokens . len ) break ;
const capture_is_ref = token_tags [ capture_token ] = = . asterisk ;
const name_token = capture_token + @boolToInt ( capture_is_ref ) ;
capture_token = name_token + 2 ;
2023-03-20 18:51:42 +00:00
try scopes . items ( . decls ) [ then_scope ] . put (
allocator ,
offsets . tokenToSlice ( tree , name_token ) ,
. { . array_payload = . { . identifier = name_token , . array_expr = input } } ,
) ;
2023-02-21 22:11:35 +00:00
}
2023-03-20 18:51:42 +00:00
if ( for_node . label_token ) | label | {
std . debug . assert ( token_tags [ label ] = = . identifier ) ;
2023-02-21 22:11:35 +00:00
2023-03-20 18:51:42 +00:00
const name = tree . tokenSlice ( label ) ;
try scopes . items ( . decls ) [ then_scope ] . put (
allocator ,
name ,
. { . label_decl = . { . label = label , . block = for_node . ast . then_expr } } ,
) ;
if ( else_scope ) | index | {
try scopes . items ( . decls ) [ index ] . put (
allocator ,
name ,
. { . label_decl = . { . label = label , . block = for_node . ast . else_expr } } ,
) ;
}
2023-02-21 22:11:35 +00:00
}
} ,
2021-03-09 11:35:56 +00:00
. @ " switch " ,
. switch_comma ,
2021-03-06 19:55:59 +00:00
= > {
2021-03-09 11:35:56 +00:00
const cond = data [ node_idx ] . lhs ;
2021-10-01 02:44:06 +01:00
const extra = tree . extraData ( data [ node_idx ] . rhs , Ast . Node . SubRange ) ;
2021-03-09 11:35:56 +00:00
const cases = tree . extra_data [ extra . start . . extra . end ] ;
2021-03-09 18:53:59 +00:00
for ( cases ) | case | {
2023-01-24 23:55:38 +00:00
const switch_case : Ast . full . SwitchCase = tree . fullSwitchCase ( case ) . ? ;
2020-06-14 20:24:18 +01:00
2021-03-09 11:35:56 +00:00
if ( switch_case . payload_token ) | payload | {
2023-03-20 18:51:42 +00:00
const expr_index = ( try makeBlockScopeInternal ( context , switch_case . ast . target_expr ) ) . ? ;
2021-03-09 11:35:56 +00:00
// if payload is *name than get next token
const name_token = payload + @boolToInt ( token_tags [ payload ] = = . asterisk ) ;
const name = tree . tokenSlice ( name_token ) ;
2020-06-10 14:12:00 +01:00
2023-03-20 18:51:42 +00:00
try scopes . items ( . decls ) [ expr_index ] . put ( allocator , name , . {
2021-03-09 11:35:56 +00:00
. switch_payload = . {
. node = name_token ,
. switch_expr = cond ,
. items = switch_case . ast . values ,
} ,
} ) ;
2023-03-31 16:54:46 +01:00
try makeScopeInternal ( context , switch_case . ast . target_expr ) ;
2023-03-20 18:51:42 +00:00
} else {
try makeScopeInternal ( context , switch_case . ast . target_expr ) ;
2021-03-09 11:35:56 +00:00
}
}
2020-06-10 14:12:00 +01:00
} ,
2021-03-06 19:55:59 +00:00
. global_var_decl ,
. local_var_decl ,
. aligned_var_decl ,
. simple_var_decl ,
= > {
2023-01-24 23:55:38 +00:00
const var_decl = tree . fullVarDecl ( node_idx ) . ? ;
2021-02-28 12:09:10 +00:00
if ( var_decl . ast . type_node ! = 0 ) {
2023-03-13 00:43:13 +00:00
try makeScopeInternal ( context , var_decl . ast . type_node ) ;
2020-06-10 14:12:00 +01:00
}
2021-02-28 12:09:10 +00:00
if ( var_decl . ast . init_node ! = 0 ) {
2023-03-13 00:43:13 +00:00
try makeScopeInternal ( context , var_decl . ast . init_node ) ;
2020-06-10 14:12:00 +01:00
}
} ,
2021-03-06 19:55:59 +00:00
. call ,
. call_comma ,
. call_one ,
. call_one_comma ,
. async_call ,
. async_call_comma ,
. async_call_one ,
. async_call_one_comma ,
= > {
2021-10-01 02:44:06 +01:00
var buf : [ 1 ] Ast . Node . Index = undefined ;
2023-01-24 23:55:38 +00:00
const call = tree . fullCall ( & buf , node_idx ) . ? ;
2021-03-06 19:55:59 +00:00
2023-03-13 00:43:13 +00:00
try makeScopeInternal ( context , call . ast . fn_expr ) ;
2021-03-06 19:55:59 +00:00
for ( call . ast . params ) | param |
2023-03-13 00:43:13 +00:00
try makeScopeInternal ( context , param ) ;
2021-03-06 19:55:59 +00:00
} ,
. struct_init ,
. struct_init_comma ,
. struct_init_dot ,
. struct_init_dot_comma ,
. struct_init_dot_two ,
. struct_init_dot_two_comma ,
. struct_init_one ,
. struct_init_one_comma ,
= > {
2021-10-01 02:44:06 +01:00
var buf : [ 2 ] Ast . Node . Index = undefined ;
2023-01-24 23:55:38 +00:00
const struct_init : Ast . full . StructInit = tree . fullStructInit ( & buf , node_idx ) . ? ;
2021-03-06 19:55:59 +00:00
if ( struct_init . ast . type_expr ! = 0 )
2023-03-13 00:43:13 +00:00
try makeScopeInternal ( context , struct_init . ast . type_expr ) ;
2021-03-06 19:55:59 +00:00
for ( struct_init . ast . fields ) | field | {
2023-03-13 00:43:13 +00:00
try makeScopeInternal ( context , field ) ;
2021-03-06 19:55:59 +00:00
}
} ,
. array_init ,
. array_init_comma ,
. array_init_dot ,
. array_init_dot_comma ,
. array_init_dot_two ,
. array_init_dot_two_comma ,
. array_init_one ,
. array_init_one_comma ,
= > {
2021-10-01 02:44:06 +01:00
var buf : [ 2 ] Ast . Node . Index = undefined ;
2023-01-24 23:55:38 +00:00
const array_init : Ast . full . ArrayInit = tree . fullArrayInit ( & buf , node_idx ) . ? ;
2021-03-06 19:55:59 +00:00
if ( array_init . ast . type_expr ! = 0 )
2023-03-13 00:43:13 +00:00
try makeScopeInternal ( context , array_init . ast . type_expr ) ;
2021-03-06 19:55:59 +00:00
for ( array_init . ast . elements ) | elem | {
2023-03-13 00:43:13 +00:00
try makeScopeInternal ( context , elem ) ;
2021-03-06 19:55:59 +00:00
}
} ,
. container_field ,
. container_field_align ,
. container_field_init ,
= > {
2023-01-24 23:55:38 +00:00
const field = tree . fullContainerField ( node_idx ) . ? ;
2021-03-06 19:55:59 +00:00
2023-03-13 00:43:13 +00:00
try makeScopeInternal ( context , field . ast . type_expr ) ;
try makeScopeInternal ( context , field . ast . align_expr ) ;
try makeScopeInternal ( context , field . ast . value_expr ) ;
2021-03-06 19:55:59 +00:00
} ,
. builtin_call ,
. builtin_call_comma ,
. builtin_call_two ,
. builtin_call_two_comma ,
= > {
2022-08-17 23:52:21 +01:00
var buffer : [ 2 ] Ast . Node . Index = undefined ;
const params = ast . builtinCallParams ( tree , node_idx , & buffer ) . ? ;
2021-03-06 19:55:59 +00:00
for ( params ) | param | {
2023-03-13 00:43:13 +00:00
try makeScopeInternal ( context , param ) ;
2021-03-06 19:55:59 +00:00
}
} ,
. ptr_type ,
. ptr_type_aligned ,
. ptr_type_bit_range ,
. ptr_type_sentinel ,
= > {
2023-01-24 23:55:38 +00:00
const ptr_type : Ast . full . PtrType = ast . fullPtrType ( tree , node_idx ) . ? ;
2021-04-05 23:04:27 +01:00
2023-03-13 00:43:13 +00:00
try makeScopeInternal ( context , ptr_type . ast . sentinel ) ;
try makeScopeInternal ( context , ptr_type . ast . align_node ) ;
try makeScopeInternal ( context , ptr_type . ast . child_type ) ;
2021-03-06 19:55:59 +00:00
} ,
2023-03-20 18:30:13 +00:00
. array_type_sentinel = > {
const array_type : Ast . full . ArrayType = tree . fullArrayType ( node_idx ) . ? ;
try makeScopeInternal ( context , array_type . ast . elem_count ) ;
try makeScopeInternal ( context , array_type . ast . elem_type ) ;
try makeScopeInternal ( context , array_type . ast . sentinel ) ;
} ,
2021-03-06 19:55:59 +00:00
. slice ,
. slice_open ,
. slice_sentinel ,
= > {
2023-01-24 23:55:38 +00:00
const slice : Ast . full . Slice = tree . fullSlice ( node_idx ) . ? ;
2023-03-13 00:43:13 +00:00
try makeScopeInternal ( context , slice . ast . sliced ) ;
try makeScopeInternal ( context , slice . ast . start ) ;
try makeScopeInternal ( context , slice . ast . end ) ;
try makeScopeInternal ( context , slice . ast . sentinel ) ;
2021-03-06 19:55:59 +00:00
} ,
2021-03-11 19:45:05 +00:00
. @ " errdefer " = > {
2023-03-20 18:51:42 +00:00
const expr_scope = ( try makeBlockScopeInternal ( context , data [ node_idx ] . rhs ) ) . ? ;
2021-03-11 19:45:05 +00:00
2023-03-20 18:51:42 +00:00
const payload_token = data [ node_idx ] . lhs ;
if ( payload_token ! = 0 ) {
2021-03-11 19:45:05 +00:00
const name = tree . tokenSlice ( payload_token ) ;
2023-03-20 18:51:42 +00:00
try scopes . items ( . decls ) [ expr_scope ] . putNoClobber ( allocator , name , . { . ast_node = data [ node_idx ] . rhs } ) ;
2021-03-11 19:45:05 +00:00
}
} ,
2021-03-06 19:55:59 +00:00
2023-03-20 18:31:23 +00:00
. switch_case ,
. switch_case_inline ,
. switch_case_one ,
. switch_case_inline_one ,
2021-03-06 19:55:59 +00:00
. @ " asm " ,
. asm_simple ,
. asm_output ,
. asm_input ,
. error_value ,
. multiline_string_literal ,
. string_literal ,
. enum_literal ,
. identifier ,
. anyframe_literal ,
. char_literal ,
2022-09-14 19:56:17 +01:00
. number_literal ,
2021-03-06 19:55:59 +00:00
. unreachable_literal ,
. @ " continue " ,
= > { } ,
2023-03-20 18:31:23 +00:00
. test_decl ,
. @ " break " ,
. @ " defer " ,
. anyframe_type ,
= > {
2023-03-13 00:43:13 +00:00
try makeScopeInternal ( context , data [ node_idx ] . rhs ) ;
2021-03-06 19:55:59 +00:00
} ,
2021-04-05 23:04:27 +01:00
2021-03-06 19:55:59 +00:00
. @ " return " ,
. @ " resume " ,
. field_access ,
. @ " suspend " ,
. deref ,
. @ " try " ,
. @ " await " ,
. optional_type ,
. @ " comptime " ,
. @ " nosuspend " ,
. bool_not ,
. negation ,
. bit_not ,
. negation_wrap ,
. address_of ,
. grouped_expression ,
. unwrap_optional ,
2021-03-11 12:59:09 +00:00
. @ " usingnamespace " ,
2021-03-06 19:55:59 +00:00
= > {
2023-03-13 00:43:13 +00:00
try makeScopeInternal ( context , data [ node_idx ] . lhs ) ;
2021-03-06 19:55:59 +00:00
} ,
2021-04-05 23:04:27 +01:00
. equal_equal ,
. bang_equal ,
. less_than ,
. greater_than ,
. less_or_equal ,
. greater_or_equal ,
. assign_mul ,
. assign_div ,
. assign_mod ,
. assign_add ,
. assign_sub ,
2021-09-29 05:15:37 +01:00
. assign_shl ,
. assign_shr ,
2021-04-05 23:04:27 +01:00
. assign_bit_and ,
. assign_bit_xor ,
. assign_bit_or ,
. assign_mul_wrap ,
. assign_add_wrap ,
. assign_sub_wrap ,
2021-09-29 05:15:37 +01:00
. assign_mul_sat ,
. assign_add_sat ,
. assign_sub_sat ,
. assign_shl_sat ,
2021-04-05 23:04:27 +01:00
. assign ,
. merge_error_sets ,
. mul ,
. div ,
. mod ,
. array_mult ,
. mul_wrap ,
2021-09-29 05:15:37 +01:00
. mul_sat ,
2021-04-05 23:04:27 +01:00
. add ,
. sub ,
. array_cat ,
. add_wrap ,
. sub_wrap ,
2021-09-29 05:15:37 +01:00
. add_sat ,
. sub_sat ,
. shl ,
. shl_sat ,
. shr ,
2021-04-05 23:04:27 +01:00
. bit_and ,
. bit_xor ,
. bit_or ,
. @ " orelse " ,
. bool_and ,
. bool_or ,
. array_type ,
. array_access ,
. error_union ,
2023-02-21 22:11:35 +00:00
. for_range ,
2023-03-20 18:31:23 +00:00
. switch_range ,
2021-04-05 23:04:27 +01:00
= > {
2023-03-13 00:43:13 +00:00
try makeScopeInternal ( context , data [ node_idx ] . lhs ) ;
try makeScopeInternal ( context , data [ node_idx ] . rhs ) ;
2020-06-10 14:12:00 +01:00
} ,
}
}