2020-05-03 22:27:08 +01:00
const std = @import ( " std " ) ;
2020-06-10 17:54:01 +01:00
const DocumentStore = @import ( " document_store.zig " ) ;
2020-05-09 03:38:08 +01:00
const ast = std . zig . ast ;
2020-05-28 01:39:36 +01:00
const types = @import ( " types.zig " ) ;
2020-07-07 09:57:02 +01:00
const offsets = @import ( " offsets.zig " ) ;
2020-08-14 11:41:34 +01:00
const log = std . log . scoped ( . analysis ) ;
2021-03-26 19:46:49 +00:00
usingnamespace @import ( " ast.zig " ) ;
2020-05-09 03:38:08 +01:00
2020-06-03 09:23:14 +01:00
/// Gets a declaration's doc comments, caller must free memory when a value is returned
2020-05-03 22:27:08 +01:00
/// Like:
///```zig
///var comments = getFunctionDocComments(allocator, tree, func);
///defer if (comments) |comments_pointer| allocator.free(comments_pointer);
///```
2020-06-12 15:42:41 +01:00
pub fn getDocComments (
allocator : * std . mem . Allocator ,
2021-02-26 20:26:52 +00:00
tree : ast . Tree ,
2021-02-27 15:38:06 +00:00
node : ast . Node . Index ,
2020-11-06 08:08:20 +00:00
format : types . MarkupContent . Kind ,
2020-06-12 15:42:41 +01:00
) ! ? [ ] const u8 {
2021-04-04 00:03:25 +01:00
const base = tree . nodes . items ( . main_token ) [ node ] ;
const tokens = tree . tokens . items ( . tag ) ;
if ( getDocCommentTokenIndex ( tokens , base ) ) | doc_comment_index | {
2021-02-27 15:38:06 +00:00
return try collectDocComments ( allocator , tree , doc_comment_index , format ) ;
2020-05-03 22:27:08 +01:00
}
2020-05-14 17:07:46 +01:00
return null ;
}
2021-04-04 00:03:25 +01:00
/// Get a declaration's doc comment token index
pub fn getDocCommentTokenIndex ( tokens : [ ] std . zig . Token . Tag , base_token : ast . TokenIndex ) ? ast . TokenIndex {
var idx = base_token ;
if ( idx = = 0 ) return null ;
idx - = 1 ;
if ( tokens [ idx ] = = . keyword_threadlocal and idx > 0 ) idx - = 1 ;
2021-04-04 00:12:57 +01:00
if ( tokens [ idx ] = = . string_literal and idx > 1 and tokens [ idx - 1 ] = = . keyword_extern ) idx - = 1 ;
2021-04-04 00:03:25 +01:00
if ( tokens [ idx ] = = . keyword_extern and idx > 0 ) idx - = 1 ;
if ( tokens [ idx ] = = . keyword_export and idx > 0 ) idx - = 1 ;
if ( tokens [ idx ] = = . keyword_pub and idx > 0 ) idx - = 1 ;
// Find first doc comment token
if ( ! ( tokens [ idx ] = = . doc_comment or tokens [ idx ] = = . container_doc_comment ) )
return null ;
2021-04-04 00:12:57 +01:00
return while ( tokens [ idx ] = = . doc_comment or tokens [ idx ] = = . container_doc_comment ) {
if ( idx = = 0 ) break 0 ;
2021-04-04 00:03:25 +01:00
idx - = 1 ;
2021-04-04 00:12:57 +01:00
} else idx + 1 ;
2021-04-04 00:03:25 +01:00
}
2020-06-12 15:42:41 +01:00
pub fn collectDocComments (
allocator : * std . mem . Allocator ,
2021-02-26 20:26:52 +00:00
tree : ast . Tree ,
2021-02-27 15:38:06 +00:00
doc_comments : ast . TokenIndex ,
2020-11-06 08:08:20 +00:00
format : types . MarkupContent . Kind ,
2020-06-12 15:42:41 +01:00
) ! [ ] 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-04 00:03:25 +01:00
switch ( tokens [ curr_line_tok ] ) {
2021-02-27 15:38:06 +00:00
. doc_comment , . container_doc_comment = > {
2020-11-03 22:44:50 +00:00
try lines . append ( std . mem . trim ( u8 , tree . tokenSlice ( curr_line_tok ) [ 3 . . ] , & std . ascii . spaces ) ) ;
2020-05-23 23:21:02 +01:00
} ,
else = > break ,
}
2020-05-14 17:07:46 +01:00
}
2020-06-12 18:31:33 +01:00
return try std . mem . join ( allocator , if ( format = = . Markdown ) " \n " else " \n " , lines . items ) ;
2020-05-03 22:27:08 +01:00
}
2020-05-03 22:27:37 +01:00
/// Gets a function signature (keywords, name, return value)
2021-03-01 15:02:24 +00:00
pub fn getFunctionSignature ( tree : ast . Tree , func : ast . full . FnProto ) [ ] const u8 {
2021-03-06 19:55:59 +00:00
const start = offsets . tokenLocation ( tree , func . ast . fn_token ) ;
// return type can be 0 when user wrote incorrect fn signature
// to ensure we don't break, just end the signature at end of fn token
if ( func . ast . return_type = = 0 ) return tree . source [ start . start . . start . end ] ;
2021-03-13 11:18:32 +00:00
const end = offsets . tokenLocation ( tree , lastToken ( tree , func . ast . return_type ) ) . end ;
2021-03-06 19:55:59 +00:00
return tree . source [ start . start . . end ] ;
2020-05-03 22:27:08 +01:00
}
2020-05-04 03:17:19 +01:00
2020-05-09 03:38:08 +01:00
/// Gets a function snippet insert text
2021-03-09 18:53:59 +00:00
pub fn getFunctionSnippet (
allocator : * std . mem . Allocator ,
tree : ast . Tree ,
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
var buffer = std . ArrayList ( u8 ) . init ( allocator ) ;
try buffer . ensureCapacity ( 128 ) ;
2021-03-01 15:30:43 +00:00
try buffer . appendSlice ( tree . tokenSlice ( name_index ) ) ;
2020-05-09 03:38:08 +01:00
try buffer . append ( '(' ) ;
2021-01-10 07:12:11 +00:00
var buf_stream = buffer . writer ( ) ;
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
2021-02-27 15:38:06 +00:00
var it = func . iterate ( tree ) ;
2021-03-09 18:53:59 +00:00
var i : usize = 0 ;
while ( it . next ( ) ) | param | : ( i + = 1 ) {
if ( skip_self_param and i = = 0 ) continue ;
if ( i ! = @boolToInt ( skip_self_param ) )
try buffer . appendSlice ( " , ${ " )
else
try buffer . appendSlice ( " ${ " ) ;
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 )
try buffer . appendSlice ( " comptime " )
else
try buffer . appendSlice ( " noalias " ) ;
2020-05-12 17:59:16 +01:00
}
2020-05-23 23:21:02 +01:00
if ( param . name_token ) | name_token | {
2020-05-12 17:59:16 +01:00
try buffer . appendSlice ( tree . tokenSlice ( name_token ) ) ;
try buffer . appendSlice ( " : " ) ;
}
2021-02-27 15:38:06 +00:00
if ( param . anytype_ellipsis3 ) | token_index | {
if ( token_tags [ token_index ] = = . keyword_anytype )
try buffer . appendSlice ( " anytype " )
else
try buffer . appendSlice ( " ... " ) ;
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-03-13 11:18:32 +00:00
var end_token = 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 ;
try buffer . appendSlice ( tree . tokenSlice ( curr_token ) ) ;
2021-02-28 20:57:15 +00:00
if ( is_comma or tag = = . keyword_const ) try buffer . append ( ' ' ) ;
2021-02-27 15:38:06 +00:00
}
2021-03-09 18:53:59 +00:00
} else unreachable ;
2020-05-09 03:38:08 +01:00
try buffer . append ( '}' ) ;
}
try buffer . append ( ')' ) ;
return buffer . toOwnedSlice ( ) ;
}
2021-04-03 16:54:26 +01:00
/// Returns true if a function has a `self` parameter
pub fn hasSelfParam (
arena : * std . heap . ArenaAllocator ,
document_store : * DocumentStore ,
handle : * DocumentStore . Handle ,
func : ast . full . FnProto ,
) ! bool {
// 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 ;
var it = func . iterate ( tree ) ;
const param = it . next ( ) . ? ;
if ( param . type_expr = = 0 ) return false ;
const token_starts = tree . tokens . items ( . start ) ;
const token_data = tree . nodes . items ( . data ) ;
const in_container = innermostContainer ( handle , token_starts [ func . ast . fn_token ] ) ;
if ( try resolveTypeOfNode ( document_store , arena , . {
. node = param . type_expr ,
. handle = handle ,
} ) ) | resolved_type | {
if ( std . meta . eql ( in_container , resolved_type ) )
return true ;
}
if ( isPtrType ( tree , param . type_expr ) ) {
if ( try resolveTypeOfNode ( document_store , arena , . {
. node = token_data [ param . type_expr ] . rhs ,
. handle = handle ,
} ) ) | resolved_prefix_op | {
if ( std . meta . eql ( in_container , resolved_prefix_op ) )
return true ;
}
}
return false ;
}
2020-05-04 03:17:19 +01:00
/// Gets a function signature (keywords, name, return value)
2021-03-01 15:02:24 +00:00
pub fn getVariableSignature ( tree : ast . Tree , var_decl : ast . full . VarDecl ) [ ] const u8 {
2021-03-05 21:38:42 +00:00
const start = offsets . tokenLocation ( tree , var_decl . ast . mut_token ) . start ;
2021-03-13 11:18:32 +00:00
const end = offsets . tokenLocation ( tree , lastToken ( tree , var_decl . ast . init_node ) ) . end ;
2020-05-04 03:17:19 +01:00
return tree . source [ start . . end ] ;
}
2020-06-10 23:00:13 +01:00
// analysis.getContainerFieldSignature(handle.tree, field)
2021-03-01 15:02:24 +00:00
pub fn getContainerFieldSignature ( tree : ast . Tree , field : ast . full . ContainerField ) [ ] const u8 {
2021-03-05 21:38:42 +00:00
const start = offsets . tokenLocation ( tree , field . ast . name_token ) . start ;
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 ;
2021-03-13 11:18:32 +00:00
const end = offsets . tokenLocation ( tree , 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
}
2020-06-12 12:56:46 +01:00
/// The type node is "type"
2021-02-26 20:26:52 +00:00
fn typeIsType ( tree : ast . Tree , node : ast . Node . Index ) bool {
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-02-26 20:26:52 +00:00
pub fn isTypeFunction ( tree : ast . Tree , func : ast . full . FnProto ) bool {
2021-02-27 15:38:06 +00:00
return typeIsType ( tree , func . ast . return_type ) ;
2020-05-17 15:23:04 +01:00
}
2021-03-04 15:14:30 +00:00
pub fn isGenericFunction ( tree : ast . Tree , func : ast . full . FnProto ) bool {
2021-03-04 21:30:25 +00:00
var it = func . iterate ( tree ) ;
2021-02-26 20:26:52 +00:00
while ( it . next ( ) ) | param | {
if ( param . anytype_ellipsis3 ! = null or param . comptime_noalias ! = null ) {
2020-06-27 18:45:58 +01:00
return true ;
}
}
return false ;
}
2020-05-04 03:17:19 +01:00
// STYLE
pub fn isCamelCase ( name : [ ] const u8 ) bool {
2020-06-24 10:28:02 +01:00
return ! std . ascii . isUpper ( name [ 0 ] ) and std . mem . indexOf ( u8 , name [ 0 . . ( name . len - 1 ) ] , " _ " ) = = null ;
2020-05-04 03:17:19 +01:00
}
2020-05-08 16:01:34 +01:00
pub fn isPascalCase ( name : [ ] const u8 ) bool {
2020-06-24 10:28:02 +01:00
return std . ascii . isUpper ( name [ 0 ] ) and std . mem . indexOf ( u8 , name [ 0 . . ( name . len - 1 ) ] , " _ " ) = = null ;
2020-05-08 16:01:34 +01:00
}
2020-05-11 13:28:08 +01:00
// ANALYSIS ENGINE
2021-02-26 20:26:52 +00:00
pub fn getDeclNameToken ( tree : ast . Tree , node : ast . Node . Index ) ? ast . TokenIndex {
const tags = tree . nodes . items ( . tag ) ;
2021-03-02 21:01:13 +00:00
const main_token = tree . nodes . items ( . main_token ) [ node ] ;
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'
2021-03-02 21:01:13 +00:00
. local_var_decl = > tree . localVarDecl ( node ) . ast . mut_token + 1 ,
. global_var_decl = > tree . globalVarDecl ( node ) . ast . mut_token + 1 ,
. simple_var_decl = > tree . simpleVarDecl ( node ) . ast . mut_token + 1 ,
. aligned_var_decl = > tree . alignedVarDecl ( node ) . ast . mut_token + 1 ,
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-02-26 20:26:52 +00:00
var params : [ 1 ] ast . Node . Index = undefined ;
2021-03-02 21:01:13 +00:00
break : blk fnProto ( tree , node , & params ) . ? . name_token ;
2020-05-28 13:41:40 +01:00
} ,
2021-02-26 20:26:52 +00:00
// containers
2021-03-02 21:01:13 +00:00
. container_field = > tree . containerField ( node ) . ast . name_token ,
. container_field_init = > tree . containerFieldInit ( node ) . ast . name_token ,
. container_field_align = > tree . containerFieldAlign ( node ) . ast . name_token ,
. identifier = > main_token ,
2021-03-06 19:55:59 +00:00
. error_value = > main_token + 2 , // 'error'.<main_token +2>
2021-02-26 20:26:52 +00:00
// lhs of main token is name token, so use `node` - 1
2021-03-02 21:01:13 +00:00
. test_decl = > if ( tree . tokens . items ( . tag ) [ main_token + 1 ] = = . string_literal )
return main_token + 1
else
null ,
else = > null ,
} ;
2020-05-18 21:19:23 +01:00
}
2021-02-26 20:26:52 +00:00
fn getDeclName ( tree : ast . Tree , node : ast . Node . Index ) ? [ ] const u8 {
2020-05-28 13:41:40 +01:00
const name = tree . tokenSlice ( getDeclNameToken ( tree , node ) orelse return null ) ;
2021-02-26 20:26:52 +00:00
return switch ( tree . nodes . items ( . tag ) [ node ] ) {
. test_decl = > name [ 1 . . name . len - 1 ] ,
2020-06-01 18:48:14 +01:00
else = > name ,
2020-05-28 13:41:40 +01:00
} ;
2020-05-18 21:19:23 +01:00
}
2020-06-15 01:59:49 +01:00
fn isContainerDecl ( decl_handle : DeclWithHandle ) bool {
return switch ( decl_handle . decl . * ) {
2021-03-02 21:01:13 +00:00
. ast_node = > | inner_node | isContainer ( decl_handle . handle . tree . nodes . items ( . tag ) [ inner_node ] ) ,
2020-06-15 01:59:49 +01:00
else = > false ,
} ;
}
fn resolveVarDeclAliasInternal (
store : * DocumentStore ,
arena : * std . heap . ArenaAllocator ,
node_handle : NodeWithHandle ,
root : bool ,
) error { OutOfMemory } ! ? DeclWithHandle {
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 ] ;
return try lookupSymbolGlobal (
store ,
arena ,
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-03-01 13:32:19 +00:00
const container_node = if ( isBuiltinCall ( tree , lhs ) ) block : {
2021-03-02 14:32:38 +00:00
const data = datas [ lhs ] ;
const builtin = switch ( node_tags [ lhs ] ) {
. builtin_call , . builtin_call_comma = > tree . extra_data [ data . lhs . . data . rhs ] ,
. builtin_call_two , . builtin_call_two_comma = > if ( data . lhs = = 0 )
& [ _ ] ast . Node . Index { }
else if ( data . rhs = = 0 )
& [ _ ] ast . Node . Index { data . lhs }
else
& [ _ ] ast . Node . Index { data . lhs , data . rhs } ,
else = > unreachable ,
} ;
2021-03-01 13:32:19 +00:00
if ( ! std . mem . eql ( u8 , tree . tokenSlice ( main_tokens [ lhs ] ) , " @import " ) )
2020-06-15 01:59:49 +01:00
return null ;
2021-03-01 13:32:19 +00:00
const inner_node = ( try resolveTypeOfNode ( store , arena , . { . node = lhs , . handle = handle } ) ) orelse return null ;
// 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 } ;
2021-03-01 13:32:19 +00:00
} else if ( try resolveVarDeclAliasInternal ( store , arena , . { . node = lhs , . handle = handle } , false ) ) | decl_handle | block : {
2020-06-15 02:39:50 +01:00
if ( decl_handle . decl . * ! = . ast_node ) return null ;
const resolved = ( try resolveTypeOfNode ( store , arena , . { . 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-03-26 19:04:51 +00:00
const resolved_tree_tags = resolved . handle . tree . nodes . items ( . tag ) ;
if ( ! 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 ;
2021-03-06 19:55:59 +00:00
return try lookupSymbolContainer ( store , arena , 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;
///```
2020-06-14 23:19:21 +01:00
pub fn resolveVarDeclAlias ( store : * DocumentStore , arena : * std . heap . ArenaAllocator , decl_handle : NodeWithHandle ) ! ? DeclWithHandle {
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 main_tokes = tree . nodes . items ( . main_token ) ;
const node_tags = tree . nodes . items ( . tag ) ;
2020-06-14 23:19:21 +01:00
2021-03-01 13:32:19 +00:00
if ( varDecl ( handle . tree , decl ) ) | var_decl | {
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
2021-03-01 13:32:19 +00:00
return try resolveVarDeclAliasInternal ( store , arena , . { . node = base_exp , . handle = handle } , true ) ;
2020-06-14 23:19:21 +01:00
}
}
return null ;
}
2021-03-06 19:55:59 +00:00
/// Returns `true` when the given `node` is one of the block tags
fn isBlock ( tree : ast . Tree , node : ast . Node . Index ) bool {
return switch ( tree . nodes . items ( . tag ) [ node ] ) {
. block ,
. block_semicolon ,
. block_two ,
. block_two_semicolon ,
= > true ,
else = > false ,
} ;
}
2020-05-24 13:24:18 +01:00
fn findReturnStatementInternal (
2021-02-26 20:26:52 +00:00
tree : ast . Tree ,
2021-03-06 19:55:59 +00:00
fn_decl : ast . full . FnProto ,
body : ast . Node . Index ,
2020-05-24 13:24:18 +01:00
already_found : * bool ,
2021-03-06 19:55:59 +00:00
) ? ast . Node . Index {
var result : ? ast . Node . Index = null ;
const node_tags = tree . nodes . items ( . tag ) ;
const datas = tree . nodes . items ( . data ) ;
if ( ! isBlock ( tree , body ) ) return null ;
const statements : [ ] const ast . Node . Index = switch ( node_tags [ body ] ) {
. block , . block_semicolon = > tree . extra_data [ datas [ body ] . lhs . . datas [ body ] . rhs ] ,
. block_two , . block_two_semicolon = > blk : {
const statements = & [ _ ] ast . Node . Index { datas [ body ] . lhs , datas [ body ] . rhs } ;
const len : usize = if ( datas [ body ] . lhs = = 0 )
@as ( usize , 0 )
else if ( datas [ body ] . rhs = = 0 )
@as ( usize , 1 )
else
@as ( usize , 2 ) ;
break : blk statements [ 0 . . len ] ;
} ,
else = > unreachable ,
} ;
for ( statements ) | child_idx | {
if ( node_tags [ child_idx ] = = . @ " return " ) {
if ( datas [ child_idx ] . lhs ! = 0 ) {
const lhs = datas [ child_idx ] . lhs ;
if ( isCall ( tree , lhs ) ) {
const call_name = getDeclName ( tree , datas [ lhs ] . lhs ) ;
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-03-06 19:55:59 +00:00
fn findReturnStatement ( tree : ast . Tree , 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
}
/// Resolves the return type of a function
2020-09-12 16:48:09 +01:00
pub fn resolveReturnType (
2020-06-12 12:56:46 +01:00
store : * DocumentStore ,
arena : * std . heap . ArenaAllocator ,
2021-02-28 16:42:34 +00:00
fn_decl : ast . full . FnProto ,
2020-06-12 12:56:46 +01:00
handle : * DocumentStore . Handle ,
bound_type_params : * BoundTypeParams ,
2021-03-03 15:34:24 +00:00
fn_body : ? ast . Node . Index ,
2020-06-17 03:12:12 +01:00
) ! ? 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 ) {
return try resolveTypeOfNodeInternal ( store , arena , . {
. node = data . lhs ,
. handle = handle ,
} , bound_type_params ) ;
}
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 ;
const is_inferred_error = tree . tokens . items ( . tag ) [ tree . firstToken ( return_type ) - 1 ] = = . bang ;
return if ( is_inferred_error ) block : {
const child_type = ( try resolveTypeOfNodeInternal ( store , arena , . {
. node = return_type ,
. handle = handle ,
} , bound_type_params ) ) orelse return null ;
const child_type_node = switch ( child_type . type . data ) {
. other = > | n | n ,
else = > return null ,
} ;
break : block TypeWithHandle {
. type = . { . data = . { . error_union = child_type_node } , . is_type_val = false } ,
. handle = child_type . handle ,
} ;
} else ( ( try resolveTypeOfNodeInternal ( store , arena , . {
. node = return_type ,
2021-02-28 16:42:34 +00:00
. handle = handle ,
2021-03-06 19:55:59 +00:00
} , bound_type_params ) ) orelse return null ) . 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
2020-06-12 12:56:46 +01:00
fn resolveUnwrapOptionalType (
store : * DocumentStore ,
arena : * std . heap . ArenaAllocator ,
2020-06-17 03:12:12 +01:00
opt : TypeWithHandle ,
2020-06-12 12:56:46 +01:00
bound_type_params : * BoundTypeParams ,
2020-06-17 03:12:12 +01:00
) ! ? TypeWithHandle {
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 ) {
return ( ( try resolveTypeOfNodeInternal ( store , arena , . {
. node = opt . handle . tree . nodes . items ( . data ) [ opt_node ] . lhs ,
. handle = opt . handle ,
} , bound_type_params ) ) orelse return null ) . instanceTypeVal ( ) ;
2020-05-27 16:49:11 +01:00
}
return null ;
}
2020-06-12 12:56:46 +01:00
fn resolveUnwrapErrorType (
store : * DocumentStore ,
arena : * std . heap . ArenaAllocator ,
2020-06-17 03:12:12 +01:00
rhs : TypeWithHandle ,
2020-06-12 12:56:46 +01:00
bound_type_params : * BoundTypeParams ,
2020-06-17 03:12:12 +01:00
) ! ? TypeWithHandle {
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 ,
} ,
2020-07-24 11:20:13 +01:00
. primitive , . slice , . pointer = > 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 ) {
return ( ( try resolveTypeOfNodeInternal ( store , arena , . {
. node = rhs . handle . tree . nodes . items ( . data ) [ rhs_node ] . rhs ,
. handle = rhs . handle ,
} , bound_type_params ) ) orelse return null ) . instanceTypeVal ( ) ;
2020-06-11 00:40:11 +01:00
}
return null ;
}
2021-03-01 15:02:24 +00:00
pub fn isPtrType ( tree : ast . Tree , node : ast . Node . Index ) bool {
2021-02-28 16:42:34 +00:00
return switch ( tree . nodes . items ( . tag ) [ node ] ) {
. ptr_type ,
. ptr_type_aligned ,
. ptr_type_bit_range ,
. ptr_type_sentinel ,
= > true ,
else = > false ,
} ;
}
2020-07-16 20:02:30 +01:00
/// Resolves the child type of a deref type
2020-06-12 12:56:46 +01:00
fn resolveDerefType (
store : * DocumentStore ,
arena : * std . heap . ArenaAllocator ,
2020-06-17 03:12:12 +01:00
deref : TypeWithHandle ,
2020-06-12 12:56:46 +01:00
bound_type_params : * BoundTypeParams ,
2020-06-17 03:12:12 +01:00
) ! ? TypeWithHandle {
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
2021-02-28 19:37:21 +00:00
if ( isPtrType ( tree , deref_node ) ) {
2021-03-03 15:34:24 +00:00
const ptr_type = ptrType ( tree , deref_node ) . ? ;
2021-02-28 16:42:34 +00:00
switch ( token_tag ) {
. asterisk = > {
2020-07-23 18:30:03 +01:00
return ( ( try resolveTypeOfNodeInternal ( store , arena , . {
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 ,
} , bound_type_params ) ) orelse return null ) . instanceTypeVal ( ) ;
} ,
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 ;
}
/// Resolves bracket access type (both slicing and array access)
fn resolveBracketAccessType (
2020-06-10 17:54:01 +01:00
store : * DocumentStore ,
arena : * std . heap . ArenaAllocator ,
2020-06-17 03:12:12 +01:00
lhs : TypeWithHandle ,
2020-05-27 16:49:11 +01:00
rhs : enum { Single , Range } ,
2020-06-12 12:56:46 +01:00
bound_type_params : * BoundTypeParams ,
2020-06-17 03:12:12 +01:00
) ! ? TypeWithHandle {
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 )
return ( ( try resolveTypeOfNodeInternal ( store , arena , . {
2021-02-28 20:57:15 +00:00
. node = data . rhs ,
2020-07-23 19:33:43 +01:00
. handle = lhs . handle ,
} , bound_type_params ) ) orelse return null ) . instanceTypeVal ( ) ;
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 ,
} ;
2021-03-08 18:46:23 +00:00
} else if ( ptrType ( tree , lhs_node ) ) | ptr_type | {
if ( ptr_type . size = = . Slice ) {
2020-07-23 19:33:43 +01:00
if ( rhs = = . Single ) {
return ( ( try resolveTypeOfNodeInternal ( store , arena , . {
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 ,
2020-07-23 19:33:43 +01:00
} , bound_type_params ) ) orelse return null ) . instanceTypeVal ( ) ;
}
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
2020-06-18 13:12:09 +01:00
pub fn resolveFieldAccessLhsType (
2020-06-12 12:56:46 +01:00
store : * DocumentStore ,
arena : * std . heap . ArenaAllocator ,
2020-06-17 03:12:12 +01:00
lhs : TypeWithHandle ,
2020-06-12 12:56:46 +01:00
bound_type_params : * BoundTypeParams ,
2020-06-17 03:12:12 +01:00
) ! TypeWithHandle {
2020-06-12 12:56:46 +01:00
return ( try resolveDerefType ( store , arena , lhs , bound_type_params ) ) orelse lhs ;
2020-05-27 16:49:11 +01:00
}
2021-03-03 15:34:24 +00:00
pub const BoundTypeParams = std . AutoHashMap ( ast . full . FnProto . Param , TypeWithHandle ) ;
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 ;
}
2021-02-26 20:26:52 +00:00
pub fn isTypeIdent ( tree : ast . Tree , token_idx : ast . TokenIndex ) bool {
2020-06-17 21:36:40 +01:00
const PrimitiveTypes = std . ComptimeStringMap ( void , . {
. { " isize " } , . { " usize " } ,
. { " c_short " } , . { " c_ushort " } ,
. { " c_int " } , . { " c_uint " } ,
. { " c_long " } , . { " c_ulong " } ,
. { " c_longlong " } , . { " c_ulonglong " } ,
. { " c_longdouble " } , . { " c_void " } ,
. { " f16 " } , . { " f32 " } ,
. { " f64 " } , . { " f128 " } ,
. { " bool " } , . { " void " } ,
. { " noreturn " } , . { " type " } ,
. { " anyerror " } , . { " comptime_int " } ,
. { " comptime_float " } , . { " anyframe " } ,
} ) ;
const text = tree . tokenSlice ( token_idx ) ;
if ( PrimitiveTypes . has ( text ) ) return true ;
if ( text . len > 1 and ( text [ 0 ] = = 'u' or text [ 0 ] = = 'i' ) and allDigits ( text [ 1 . . ] ) )
return true ;
return false ;
}
2020-06-12 12:56:46 +01:00
2020-05-13 14:03:33 +01:00
/// Resolves the type of a node
2020-06-18 13:12:09 +01:00
pub fn resolveTypeOfNodeInternal (
2020-06-12 12:56:46 +01:00
store : * DocumentStore ,
arena : * std . heap . ArenaAllocator ,
node_handle : NodeWithHandle ,
bound_type_params : * BoundTypeParams ,
2020-06-17 03:12:12 +01:00
) error { OutOfMemory } ! ? TypeWithHandle {
2020-06-10 18:48:40 +01:00
const node = node_handle . node ;
const handle = node_handle . handle ;
2021-02-28 16:42:34 +00:00
const tree = handle . tree ;
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 ,
= > {
2021-02-28 16:42:34 +00:00
const var_decl = varDecl ( tree , node ) . ? ;
if ( var_decl . ast . type_node ! = 0 ) block : {
2020-06-17 03:12:12 +01:00
return ( ( try resolveTypeOfNodeInternal (
store ,
arena ,
2021-02-28 16:42:34 +00:00
. { . node = var_decl . ast . type_node , . handle = handle } ,
2020-06-17 03:12:12 +01:00
bound_type_params ,
) ) orelse break : block ) . instanceTypeVal ( ) ;
}
2021-02-28 16:42:34 +00:00
return if ( var_decl . ast . init_node ! = 0 )
try resolveTypeOfNodeInternal ( store , arena , . { . node = var_decl . ast . init_node , . handle = handle } , bound_type_params )
else
null ;
2020-05-11 13:28:08 +01:00
} ,
2021-02-28 16:42:34 +00:00
. identifier = > {
2021-03-03 15:34:24 +00:00
if ( isTypeIdent ( handle . tree , main_tokens [ node ] ) ) {
2020-06-17 21:36:40 +01:00
return TypeWithHandle {
. type = . { . data = . primitive , . is_type_val = true } ,
. handle = handle ,
} ;
}
2021-03-30 18:59:58 +01:00
if ( try lookupSymbolGlobal (
store ,
arena ,
handle ,
tree . getNodeSource ( node ) ,
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 ;
2021-03-06 19:55:59 +00:00
if ( varDecl ( child . handle . tree , n ) ) | var_decl | {
2021-03-03 16:45:42 +00:00
if ( var_decl . ast . init_node ! = 0 and 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 = > { } ,
}
2020-06-12 12:56:46 +01:00
return try child . resolveType ( store , arena , bound_type_params ) ;
2020-06-10 18:48:40 +01:00
}
return null ;
2020-05-11 13:28:08 +01:00
} ,
2021-03-05 21:38:42 +00:00
. container_field ,
. container_field_init ,
. container_field_align ,
= > | c | {
2021-02-28 16:42:34 +00:00
const field : ast . full . ContainerField = switch ( c ) {
. container_field = > tree . containerField ( node ) ,
. container_field_align = > tree . containerFieldAlign ( node ) ,
. container_field_init = > tree . containerFieldInit ( node ) ,
else = > unreachable ,
} ;
if ( field . ast . type_expr = = 0 ) return null ;
2020-06-17 03:12:12 +01:00
return ( ( try resolveTypeOfNodeInternal (
store ,
arena ,
2021-02-28 16:42:34 +00:00
. { . node = field . ast . type_expr , . handle = handle } ,
2020-06-17 03:12:12 +01:00
bound_type_params ,
) ) orelse return null ) . instanceTypeVal ( ) ;
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 ,
= > | c | {
var params : [ 1 ] ast . Node . Index = undefined ;
const call : ast . full . Call = switch ( c ) {
. call , . call_comma , . async_call , . async_call_comma = > tree . callFull ( node ) ,
. call_one , . call_one_comma , . async_call_one , . async_call_one_comma = > tree . callOne ( & params , node ) ,
else = > unreachable ,
} ;
2020-06-17 13:07:21 +01:00
const decl = ( try resolveTypeOfNodeInternal (
store ,
arena ,
2021-02-28 16:42:34 +00:00
. { . node = call . ast . fn_expr , . handle = handle } ,
2020-06-17 13:07:21 +01:00
bound_type_params ,
) ) orelse 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-02-28 16:42:34 +00:00
var buf : [ 1 ] ast . Node . Index = undefined ;
2021-03-03 16:45:42 +00:00
const func_maybe = fnProto ( decl . handle . tree , decl_node , & buf ) ;
2021-02-28 16:42:34 +00:00
if ( func_maybe ) | fn_decl | {
// check for x.y(..). if '.' is found, it means first param should be skipped
const has_self_param = token_tags [ call . ast . lparen - 2 ] = = . period ;
2021-03-03 16:45:42 +00:00
var it = fn_decl . iterate ( decl . handle . tree ) ;
2020-06-12 12:56:46 +01:00
2021-03-04 15:14:30 +00:00
// Bind type params to the expressions passed in txhe calls.
2021-02-28 16:42:34 +00:00
const param_len = std . math . min ( call . ast . params . len + @boolToInt ( has_self_param ) , fn_decl . ast . params . len ) ;
2021-03-09 18:53:59 +00:00
var i : usize = 0 ;
while ( it . next ( ) ) | decl_param | : ( i + = 1 ) {
if ( i = = 0 and has_self_param ) continue ;
if ( i > = param_len ) break ;
2021-03-03 16:45:42 +00:00
if ( ! typeIsType ( decl . handle . tree , decl_param . type_expr ) ) continue ;
2020-06-12 12:56:46 +01:00
const call_param_type = ( try resolveTypeOfNodeInternal ( store , arena , . {
2021-03-09 18:53:59 +00:00
. node = call . ast . params [ i - @boolToInt ( has_self_param ) ] ,
2021-03-06 19:55:59 +00:00
. handle = handle ,
2020-06-12 12:56:46 +01:00
} , bound_type_params ) ) orelse continue ;
2020-06-17 03:12:12 +01:00
if ( ! call_param_type . type . is_type_val ) continue ;
2020-06-12 12:56:46 +01:00
2021-03-03 15:34:24 +00:00
_ = try bound_type_params . put ( decl_param , call_param_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 ;
2021-03-03 15:34:24 +00:00
return try resolveReturnType ( store , arena , fn_decl , decl . handle , bound_type_params , if ( has_body ) body else null ) ;
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-02-28 19:37:21 +00:00
return try resolveTypeOfNodeInternal ( store , arena , . { . node = datas [ node ] . lhs , . handle = handle } , bound_type_params ) ;
2020-06-11 09:12:31 +01:00
} ,
2021-03-05 21:38:42 +00:00
. struct_init ,
. struct_init_comma ,
. struct_init_one ,
. struct_init_one_comma ,
= > {
2020-06-17 03:12:12 +01:00
return ( ( try resolveTypeOfNodeInternal (
store ,
arena ,
2021-02-28 19:37:21 +00:00
. { . node = datas [ node ] . lhs , . handle = handle } ,
2020-06-17 03:12:12 +01:00
bound_type_params ,
) ) orelse return null ) . instanceTypeVal ( ) ;
2020-05-11 13:28:08 +01:00
} ,
2021-02-28 19:37:21 +00:00
. error_set_decl = > {
2020-06-17 03:12:12 +01:00
return TypeWithHandle . typeVal ( node_handle ) ;
2020-05-16 17:04:07 +01:00
} ,
2021-03-05 21:38:42 +00:00
. slice ,
. slice_sentinel ,
. slice_open ,
= > {
2020-06-12 12:56:46 +01:00
const left_type = ( try resolveTypeOfNodeInternal ( store , arena , . {
2021-03-01 13:32:19 +00:00
. node = datas [ node ] . lhs ,
2020-06-12 12:56:46 +01:00
. handle = handle ,
} , bound_type_params ) ) orelse return null ;
2020-07-24 08:33:13 +01:00
return try resolveBracketAccessType ( store , arena , left_type , . Range , bound_type_params ) ;
2020-07-23 17:06:39 +01:00
} ,
2021-03-05 21:38:42 +00:00
. deref ,
. unwrap_optional ,
= > {
2020-07-23 17:06:39 +01:00
const left_type = ( try resolveTypeOfNodeInternal ( store , arena , . {
2021-03-01 13:32:19 +00:00
. node = datas [ node ] . lhs ,
2020-07-23 17:06:39 +01:00
. handle = handle ,
} , bound_type_params ) ) orelse return null ;
2021-02-28 19:37:21 +00:00
return switch ( node_tags [ node ] ) {
. unwrap_optional = > try resolveUnwrapOptionalType ( store , arena , left_type , bound_type_params ) ,
. deref = > try resolveDerefType ( store , arena , left_type , bound_type_params ) ,
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
. array_access = > {
2020-07-23 17:06:39 +01:00
const left_type = ( try resolveTypeOfNodeInternal ( store , arena , . {
2021-02-28 19:37:21 +00:00
. node = datas [ node ] . lhs ,
2020-07-23 17:06:39 +01:00
. handle = handle ,
} , bound_type_params ) ) orelse return null ;
return try resolveBracketAccessType ( store , arena , left_type , . Single , bound_type_params ) ;
2020-05-27 16:49:11 +01:00
} ,
2021-02-28 19:37:21 +00:00
. field_access = > {
2021-03-02 14:32:38 +00:00
const field_access = datas [ node ] ;
2021-03-02 21:01:13 +00:00
2021-03-03 16:45:42 +00:00
if ( datas [ node ] . rhs = = 0 ) return null ;
const rhs_str = tree . tokenSlice ( datas [ node ] . rhs ) ;
2020-07-16 20:19:08 +01:00
// If we are accessing a pointer type, remove one pointerness level :)
const left_type = try resolveFieldAccessLhsType (
store ,
arena ,
( try resolveTypeOfNodeInternal ( store , arena , . {
2021-03-02 14:32:38 +00:00
. node = field_access . lhs ,
2020-07-16 20:19:08 +01:00
. handle = handle ,
} , bound_type_params ) ) orelse return null ,
bound_type_params ,
) ;
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 ,
} ;
if ( try lookupSymbolContainer (
store ,
arena ,
. { . node = left_type_node , . handle = left_type . handle } ,
rhs_str ,
2021-03-06 19:55:59 +00:00
! left_type . type . is_type_val ,
2020-07-16 20:19:08 +01:00
) ) | child | {
return try child . resolveType ( store , arena , bound_type_params ) ;
} else return null ;
} ,
2021-02-28 19:37:21 +00:00
. @ " orelse " = > {
2020-07-16 20:19:08 +01:00
const left_type = ( try resolveTypeOfNodeInternal ( store , arena , . {
2021-02-28 19:37:21 +00:00
. node = datas [ node ] . lhs ,
2020-07-16 20:19:08 +01:00
. handle = handle ,
} , bound_type_params ) ) orelse return null ;
return try resolveUnwrapOptionalType ( store , arena , left_type , bound_type_params ) ;
} ,
2021-02-28 19:37:21 +00:00
. @ " catch " = > {
2020-07-16 20:19:08 +01:00
const left_type = ( try resolveTypeOfNodeInternal ( store , arena , . {
2021-02-28 19:37:21 +00:00
. node = datas [ node ] . lhs ,
2020-07-16 20:19:08 +01:00
. handle = handle ,
} , bound_type_params ) ) orelse return null ;
return try resolveUnwrapErrorType ( store , arena , left_type , bound_type_params ) ;
} ,
2021-02-28 19:37:21 +00:00
. array_type ,
. array_type_sentinel ,
. optional_type ,
. ptr_type_aligned ,
. ptr_type ,
. ptr_type_bit_range ,
2021-03-06 19:55:59 +00:00
. error_union ,
2020-07-16 20:19:08 +01:00
= > return TypeWithHandle . typeVal ( node_handle ) ,
2021-02-28 19:37:21 +00:00
. @ " try " = > {
2020-07-16 20:19:08 +01:00
const rhs_type = ( try resolveTypeOfNodeInternal ( store , arena , . {
2021-02-28 19:37:21 +00:00
. node = datas [ node ] . lhs ,
2020-07-16 20:19:08 +01:00
. handle = handle ,
} , bound_type_params ) ) orelse return null ;
return try resolveUnwrapErrorType ( store , arena , rhs_type , bound_type_params ) ;
} ,
2021-02-28 19:37:21 +00:00
. address_of = > {
2020-07-24 11:20:13 +01:00
const rhs_type = ( try resolveTypeOfNodeInternal ( store , arena , . {
2021-02-28 19:37:21 +00:00
. node = datas [ node ] . lhs ,
2020-07-24 11:20:13 +01:00
. handle = handle ,
} , bound_type_params ) ) orelse return null ;
const rhs_node = switch ( rhs_type . type . data ) {
. other = > | n | n ,
else = > return null ,
} ;
return TypeWithHandle {
2021-04-03 16:54:26 +01:00
. type = . { . data = . { . pointer = rhs_node } , . is_type_val = rhs_type . type . is_type_val } ,
2020-07-24 11:20:13 +01:00
. handle = rhs_type . handle ,
} ;
} ,
2021-03-06 19:55:59 +00:00
. builtin_call ,
. builtin_call_comma ,
. builtin_call_two ,
. builtin_call_two_comma ,
= > {
2021-03-02 14:32:38 +00:00
const data = datas [ node ] ;
const params = switch ( node_tags [ node ] ) {
. builtin_call , . builtin_call_comma = > tree . extra_data [ data . lhs . . data . rhs ] ,
. builtin_call_two , . builtin_call_two_comma = > if ( data . lhs = = 0 )
& [ _ ] ast . Node . Index { }
else if ( data . rhs = = 0 )
& [ _ ] ast . Node . Index { data . lhs }
else
& [ _ ] ast . Node . Index { data . lhs , data . rhs } ,
else = > unreachable ,
} ;
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 ;
2020-06-17 03:12:12 +01:00
return ( ( try resolveTypeOfNodeInternal ( store , arena , . {
2021-02-28 19:37:21 +00:00
. node = params [ 0 ] ,
2020-06-12 12:56:46 +01:00
. handle = handle ,
2020-06-17 03:12:12 +01:00
} , bound_type_params ) ) orelse return null ) . instanceTypeVal ( ) ;
}
// 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 ;
2020-06-17 03:12:12 +01:00
var resolved_type = ( try resolveTypeOfNodeInternal ( store , arena , . {
2021-02-28 19:37:21 +00:00
. node = params [ 0 ] ,
2020-06-17 03:12:12 +01:00
. handle = handle ,
} , bound_type_params ) ) orelse return null ;
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
}
2020-05-19 16:53:01 +01:00
if ( ! std . mem . eql ( u8 , call_name , " @import " ) ) return null ;
2021-03-02 14:32:38 +00:00
if ( params . len = = 0 ) return null ;
2020-05-14 00:10:41 +01:00
2021-02-28 19:37:21 +00:00
const import_param = params [ 0 ] ;
if ( node_tags [ import_param ] ! = . string_literal ) return null ;
2020-05-14 00:10:41 +01:00
2021-02-28 19:37:21 +00:00
const import_str = tree . tokenSlice ( main_tokens [ import_param ] ) ;
2020-08-14 11:27:10 +01:00
const new_handle = ( store . resolveImport ( handle , import_str [ 1 . . import_str . len - 1 ] ) catch | err | {
2021-01-04 17:51:26 +00:00
log . debug ( " Error {} while processing import {s} " , . { err , import_str } ) ;
2020-06-10 19:24:17 +01:00
return null ;
} ) orelse return null ;
2021-02-28 19:37:21 +00: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-02-28 19:37:21 +00:00
. container_decl ,
. container_decl_arg ,
. container_decl_arg_trailing ,
. container_decl_trailing ,
. container_decl_two ,
. container_decl_two_trailing ,
2021-03-06 19:55:59 +00:00
. tagged_union ,
. tagged_union_trailing ,
. tagged_union_two ,
. tagged_union_two_trailing ,
. tagged_union_enum_tag ,
. tagged_union_enum_tag_trailing ,
2021-02-28 19:37:21 +00:00
= > {
2020-06-17 03:12:12 +01:00
return TypeWithHandle . typeVal ( node_handle ) ;
} ,
2021-03-06 19:55:59 +00:00
. fn_proto ,
. fn_proto_multi ,
. fn_proto_one ,
. fn_proto_simple ,
. fn_decl ,
= > {
2021-02-28 19:37:21 +00:00
var buf : [ 1 ] ast . Node . Index = undefined ;
2020-06-17 13:07:21 +01:00
// This is a function type
2021-03-06 19:55:59 +00:00
if ( fnProto ( tree , node , & buf ) . ? . 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
} ,
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 {
data : union ( enum ) {
2021-02-28 16:42:34 +00:00
pointer : ast . Node . Index ,
slice : ast . Node . Index ,
error_union : ast . Node . Index ,
other : ast . Node . Index ,
2020-06-17 21:36:40 +01:00
primitive ,
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 ,
handle : * DocumentStore . Handle ,
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 ,
} ;
}
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 ) ;
2021-03-26 19:04:51 +00:00
if ( isContainer ( tree , node ) ) {
2021-03-04 21:30:25 +00:00
var buf : [ 2 ] ast . Node . Index = undefined ;
2021-03-26 19:04:51 +00:00
for ( declMembers ( tree , node , & buf ) ) | child | {
2021-03-04 21:30:25 +00:00
if ( tags [ child ] . 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-02-28 20:57:15 +00: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 ) {
. other = > | n | if ( fnProto ( tree , n , & buf ) ) | fn_proto | blk : {
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-02-28 20:57:15 +00: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 ) {
. other = > | n | if ( fnProto ( tree , n , & buf ) ) | fn_proto | blk : {
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
} ;
pub fn resolveTypeOfNode ( store : * DocumentStore , arena : * std . heap . ArenaAllocator , node_handle : NodeWithHandle ) error { OutOfMemory } ! ? TypeWithHandle {
2020-06-12 12:56:46 +01:00
var bound_type_params = BoundTypeParams . init ( & arena . allocator ) ;
return resolveTypeOfNodeInternal ( store , arena , node_handle , & bound_type_params ) ;
}
2021-03-30 09:33:21 +01:00
/// Collects all imports we can find into a slice of import paths (without quotes).
pub fn collectImports ( import_arr : * std . ArrayList ( [ ] const u8 ) , tree : ast . Tree ) ! void {
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 ) ) ;
2021-03-30 13:41:59 +01:00
try import_arr . append ( str [ 1 . . str . len - 1 ] ) ;
2020-05-14 12:51:07 +01:00
}
}
}
2020-06-10 17:54:01 +01:00
pub const NodeWithHandle = struct {
2021-02-28 16:42:34 +00:00
node : ast . Node . Index ,
2020-06-10 17:54:01 +01:00
handle : * DocumentStore . Handle ,
} ;
2020-05-29 10:46:50 +01:00
2020-07-07 21:26:12 +01:00
pub const FieldAccessReturn = struct {
original : TypeWithHandle ,
unwrapped : ? TypeWithHandle = null ,
} ;
2020-06-17 03:12:12 +01:00
pub fn getFieldAccessType (
2020-06-10 17:54:01 +01:00
store : * DocumentStore ,
arena : * std . heap . ArenaAllocator ,
handle : * DocumentStore . Handle ,
2020-06-10 22:24:57 +01:00
source_index : usize ,
2020-05-18 14:21:16 +01:00
tokenizer : * std . zig . Tokenizer ,
2020-07-07 21:26:12 +01:00
) ! ? FieldAccessReturn {
2020-06-17 03:12:12 +01:00
var current_type = TypeWithHandle . typeVal ( . {
2020-07-23 18:30:03 +01:00
. node = undefined ,
2020-06-10 17:54:01 +01:00
. handle = handle ,
2020-06-17 03:12:12 +01:00
} ) ;
2020-05-11 13:28:08 +01:00
2020-06-12 12:56:46 +01:00
// TODO Actually bind params here when calling functions instead of just skipping args.
var bound_type_params = BoundTypeParams . init ( & arena . allocator ) ;
2021-03-01 13:32:19 +00:00
const tree = handle . tree ;
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 {
2020-07-07 21:26:12 +01:00
. original = current_type ,
. unwrapped = try resolveDerefType ( store , arena , current_type , & bound_type_params ) ,
} ,
2021-02-28 16:42:34 +00:00
. identifier = > {
2021-03-30 14:45:49 +01:00
if ( try lookupSymbolGlobal (
store ,
arena ,
current_type . handle ,
tokenizer . buffer [ tok . loc . start . . tok . loc . end ] ,
source_index ,
) ) | child | {
2020-06-17 03:12:12 +01:00
current_type = ( try child . resolveType ( store , arena , & bound_type_params ) ) 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
if ( current_type . isFunc ( ) ) return null ;
return FieldAccessReturn {
. original = current_type ,
. unwrapped = try resolveDerefType ( store , arena , current_type , & bound_type_params ) ,
} ;
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 ) {
return FieldAccessReturn {
. original = current_type ,
. unwrapped = try resolveDerefType ( store , arena , current_type , & bound_type_params ) ,
} ;
}
2020-06-17 03:12:12 +01:00
current_type = try resolveFieldAccessLhsType ( store , arena , current_type , & bound_type_params ) ;
const current_type_node = switch ( current_type . type . data ) {
. other = > | n | n ,
else = > return null ,
} ;
if ( try lookupSymbolContainer (
store ,
arena ,
. { . node = current_type_node , . handle = current_type . handle } ,
tokenizer . buffer [ after_period . loc . start . . after_period . loc . end ] ,
! current_type . type . is_type_val ,
) ) | child | {
2021-03-30 14:45:49 +01:00
current_type = ( try child . resolveType (
store ,
arena ,
& bound_type_params ,
) ) orelse return null ;
2020-05-13 16:43:28 +01:00
} else return null ;
2020-05-26 23:45:18 +01:00
} ,
2021-03-01 13:32:19 +00:00
. question_mark = > {
2021-03-30 14:45:49 +01:00
current_type = ( try resolveUnwrapOptionalType (
store ,
arena ,
current_type ,
& bound_type_params ,
) ) 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 = > {
2021-03-30 14:45:49 +01:00
current_type = ( try resolveDerefType (
store ,
arena ,
current_type ,
& bound_type_params ,
) ) orelse return null ;
2020-05-26 23:45:18 +01:00
} ,
2021-02-28 16:42:34 +00:00
. l_paren = > {
2020-06-17 03:12:12 +01:00
const current_type_node = switch ( current_type . type . data ) {
. 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.
if ( current_type . type . is_type_val ) return null ;
2021-03-08 18:46:23 +00:00
const cur_tree = current_type . handle . tree ;
2021-03-01 13:32:19 +00:00
var buf : [ 1 ] ast . Node . Index = undefined ;
2021-03-08 18:46:23 +00:00
if ( fnProto ( cur_tree , current_type_node , & buf ) ) | func | {
// 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-03-03 15:34:24 +00:00
if ( try resolveReturnType ( store , arena , func , current_type . handle , & bound_type_params , 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 ;
2020-06-17 03:12:12 +01:00
current_type = ( try resolveBracketAccessType ( store , arena , current_type , if ( is_range ) . Range else . Single , & bound_type_params ) ) orelse return null ;
2020-05-24 03:07:09 +01:00
} ,
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
}
}
2020-07-07 21:26:12 +01:00
return FieldAccessReturn {
. original = current_type ,
. unwrapped = try resolveDerefType ( store , arena , current_type , & bound_type_params ) ,
} ;
2020-05-13 14:03:33 +01:00
}
2021-02-28 16:42:34 +00:00
pub fn isNodePublic ( tree : ast . Tree , node : ast . Node . Index ) bool {
2021-03-01 15:30:43 +00:00
var buf : [ 1 ] ast . Node . Index = undefined ;
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 ,
= > varDecl ( tree , node ) . ? . visib_token ! = null ,
. fn_proto ,
. fn_proto_multi ,
. fn_proto_one ,
. fn_proto_simple ,
. fn_decl ,
= > fnProto ( tree , node , & buf ) . ? . visib_token ! = null ,
2021-03-01 15:30:43 +00:00
else = > true ,
} ;
2020-05-14 17:14:35 +01:00
}
2021-02-28 16:42:34 +00:00
pub fn nodeToString ( tree : ast . Tree , 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-03-01 15:30:43 +00:00
var buf : [ 1 ] ast . Node . Index = undefined ;
2021-02-28 16:42:34 +00:00
switch ( tree . nodes . items ( . tag ) [ node ] ) {
. container_field = > return tree . tokenSlice ( tree . containerField ( node ) . ast . name_token ) ,
. container_field_init = > return tree . tokenSlice ( tree . containerFieldInit ( node ) . ast . name_token ) ,
. container_field_align = > return tree . tokenSlice ( tree . containerFieldAlign ( node ) . ast . name_token ) ,
2021-03-06 19:55:59 +00:00
. error_value = > return tree . tokenSlice ( data [ node ] . rhs ) ,
2021-03-03 15:34:24 +00:00
. identifier = > return tree . tokenSlice ( main_token ) ,
. fn_proto ,
. fn_proto_multi ,
. fn_proto_one ,
. fn_proto_simple ,
. fn_decl ,
= > if ( fnProto ( tree , node , & buf ) . ? . name_token ) | name |
return tree . tokenSlice ( name ) ,
2021-03-02 21:01:13 +00:00
. field_access = > return tree . tokenSlice ( data [ node ] . rhs ) ,
2021-03-03 16:45:42 +00:00
. call ,
. call_comma ,
. async_call ,
. async_call_comma ,
= > return tree . tokenSlice ( tree . callFull ( node ) . ast . lparen - 1 ) ,
. call_one ,
. call_one_comma ,
. async_call_one ,
. async_call_one_comma ,
= > return tree . tokenSlice ( tree . callOne ( & buf , node ) . ast . lparen - 1 ) ,
2021-03-05 21:38:42 +00:00
. test_decl = > if ( data [ node ] . lhs ! = 0 )
return tree . tokenSlice ( data [ node ] . lhs ) ,
2021-03-03 15:34:24 +00:00
else = > | tag | log . debug ( " INVALID: {} " , . { tag } ) ,
2020-05-11 13:28:08 +01:00
}
2020-05-16 16:30:16 +01:00
2020-05-13 16:59:44 +01:00
return null ;
2020-05-13 14:03:33 +01:00
}
2020-05-16 19:06:48 +01:00
2021-02-28 16:42:34 +00:00
fn nodeContainsSourceIndex ( tree : ast . Tree , node : ast . Node . Index , source_index : usize ) bool {
2021-03-05 21:38:42 +00:00
const first_token = offsets . tokenLocation ( tree , tree . firstToken ( node ) ) . start ;
2021-03-13 11:18:32 +00:00
const last_token = offsets . tokenLocation ( tree , lastToken ( tree , node ) ) . end ;
2021-02-28 16:42:34 +00:00
return source_index > = first_token and source_index < = last_token ;
}
pub fn getImportStr ( tree : ast . Tree , node : ast . Node . Index , source_index : usize ) ? [ ] const u8 {
const node_tags = tree . nodes . items ( . tag ) ;
var buf : [ 2 ] ast . Node . Index = undefined ;
2021-03-26 19:04:51 +00:00
if ( isContainer ( tree , node ) ) {
const decls = declMembers ( tree , node , & buf ) ;
2021-03-07 20:54:54 +00:00
for ( decls ) | decl_idx | {
if ( getImportStr ( tree , decl_idx , source_index ) ) | name | {
return name ;
}
2020-05-22 16:51:57 +01:00
}
2021-03-07 20:54:54 +00:00
return null ;
} else if ( varDecl ( tree , node ) ) | var_decl | {
return getImportStr ( tree , var_decl . ast . init_node , source_index ) ;
} else if ( node_tags [ node ] = = . @ " usingnamespace " ) {
return getImportStr ( tree , tree . nodes . items ( . data ) [ node ] . lhs , source_index ) ;
}
2020-05-22 16:51:57 +01:00
2021-03-07 20:54:54 +00:00
if ( ! nodeContainsSourceIndex ( tree , node , source_index ) ) {
return null ;
}
2021-03-02 14:32:38 +00:00
2021-03-07 20:54:54 +00:00
if ( isBuiltinCall ( tree , node ) ) {
const builtin_token = tree . nodes . items ( . main_token ) [ node ] ;
const call_name = tree . tokenSlice ( builtin_token ) ;
if ( ! std . mem . eql ( u8 , call_name , " @import " ) ) return null ;
const data = tree . nodes . items ( . data ) [ node ] ;
const params = switch ( node_tags [ node ] ) {
. builtin_call , . builtin_call_comma = > tree . extra_data [ data . lhs . . data . rhs ] ,
. builtin_call_two , . builtin_call_two_comma = > if ( data . lhs = = 0 )
& [ _ ] ast . Node . Index { }
else if ( data . rhs = = 0 )
& [ _ ] ast . Node . Index { data . lhs }
else
& [ _ ] ast . Node . Index { data . lhs , data . rhs } ,
else = > unreachable ,
} ;
2021-02-28 16:42:34 +00:00
2021-03-07 20:54:54 +00:00
if ( params . len ! = 1 ) return null ;
2021-02-28 16:42:34 +00:00
2021-03-07 20:54:54 +00:00
const import_str = tree . tokenSlice ( tree . nodes . items ( . main_token ) [ params [ 0 ] ] ) ;
return import_str [ 1 . . import_str . len - 1 ] ;
2020-05-22 16:51:57 +01:00
}
2021-02-28 16:42:34 +00:00
2020-05-22 16:51:57 +01:00
return null ;
}
2020-05-26 23:45:18 +01:00
pub const SourceRange = std . zig . Token . Loc ;
2020-05-27 19:58:35 +01:00
pub const PositionContext = union ( enum ) {
2020-05-26 23:45:18 +01:00
builtin : SourceRange ,
comment ,
string_literal : SourceRange ,
field_access : SourceRange ,
var_access : SourceRange ,
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 ,
2020-05-27 19:58:35 +01:00
pub fn range ( self : PositionContext ) ? SourceRange {
2020-05-26 23:45:18 +01:00
return switch ( self ) {
. builtin = > | r | r ,
. comment = > null ,
. 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 } ,
} ;
fn peek ( arr : * std . ArrayList ( StackState ) ) ! * StackState {
if ( arr . items . len = = 0 ) {
try arr . append ( . { . ctx = . empty , . stack_id = . Global } ) ;
}
return & arr . items [ arr . items . len - 1 ] ;
}
fn tokenRangeAppend ( prev : SourceRange , token : std . zig . Token ) SourceRange {
return . {
. start = prev . start ,
. end = token . loc . end ,
} ;
}
2020-07-03 00:31:28 +01:00
const DocumentPosition = @import ( " offsets.zig " ) . DocumentPosition ;
2021-03-31 00:25:49 +01:00
pub fn documentPositionContext (
arena : * std . heap . ArenaAllocator ,
document : types . TextDocument ,
doc_position : DocumentPosition ,
) ! PositionContext {
2020-07-03 00:31:28 +01:00
const line = doc_position . line ;
var tokenizer = std . zig . Tokenizer . init ( line [ 0 . . doc_position . line_index ] ) ;
2020-05-26 23:45:18 +01:00
var stack = try std . ArrayList ( StackState ) . initCapacity ( & arena . allocator , 8 ) ;
while ( true ) {
const tok = tokenizer . next ( ) ;
// Early exits.
2021-02-26 20:26:52 +00:00
switch ( tok . tag ) {
. invalid , . invalid_ampersands = > {
2020-05-26 23:45:18 +01:00
// Single '@' do not return a builtin token so we check this on our own.
2020-07-03 00:31:28 +01:00
if ( line [ doc_position . line_index - 1 ] = = '@' ) {
2020-05-26 23:45:18 +01:00
return PositionContext {
. builtin = . {
2020-07-03 00:31:28 +01:00
. start = doc_position . line_index - 1 ,
. end = doc_position . line_index ,
2020-05-26 23:45:18 +01:00
} ,
} ;
}
return . other ;
} ,
2021-02-28 16:42:34 +00:00
. doc_comment , . container_doc_comment = > return . comment ,
2021-02-26 20:26:52 +00:00
. eof = > break ,
2020-05-26 23:45:18 +01:00
else = > { } ,
}
// State changes
var curr_ctx = try peek ( & stack ) ;
2021-02-26 20:26:52 +00:00
switch ( tok . tag ) {
. string_literal , . multiline_string_literal_line = > curr_ctx . ctx = . { . string_literal = tok . loc } ,
. identifier = > switch ( curr_ctx . ctx ) {
2020-06-15 02:39:50 +01:00
. empty , . pre_label = > curr_ctx . ctx = . { . var_access = tok . loc } ,
2020-06-18 13:12:09 +01:00
. label = > | filled | if ( ! filled ) {
curr_ctx . ctx = . { . label = true } ;
} else {
curr_ctx . ctx = . { . var_access = tok . loc } ;
} ,
2020-05-26 23:45:18 +01:00
else = > { } ,
} ,
2021-02-26 20:26:52 +00:00
. builtin = > switch ( curr_ctx . ctx ) {
2020-06-15 02:39:50 +01:00
. empty , . pre_label = > curr_ctx . ctx = . { . builtin = tok . loc } ,
2020-05-26 23:45:18 +01:00
else = > { } ,
} ,
2021-02-26 20:26:52 +00:00
. period , . period_asterisk = > switch ( curr_ctx . ctx ) {
2020-06-15 02:39:50 +01:00
. empty , . pre_label = > curr_ctx . ctx = . enum_literal ,
2020-05-26 23:45:18 +01:00
. enum_literal = > curr_ctx . ctx = . empty ,
. field_access = > { } ,
. other = > { } ,
2020-05-16 17:04:07 +01:00
. global_error_set = > { } ,
2020-05-26 23:45:18 +01:00
else = > curr_ctx . ctx = . {
. field_access = tokenRangeAppend ( curr_ctx . ctx . range ( ) . ? , tok ) ,
} ,
} ,
2021-02-26 20:26:52 +00:00
. keyword_break , . keyword_continue = > curr_ctx . ctx = . pre_label ,
. colon = > if ( curr_ctx . ctx = = . pre_label ) {
2020-06-18 13:12:09 +01:00
curr_ctx . ctx = . { . label = false } ;
2020-06-17 13:07:21 +01:00
} else {
curr_ctx . ctx = . empty ;
2020-06-16 13:49:57 +01:00
} ,
2021-02-26 20:26:52 +00:00
. question_mark = > switch ( curr_ctx . ctx ) {
2020-05-26 23:45:18 +01:00
. field_access = > { } ,
else = > curr_ctx . ctx = . empty ,
} ,
2021-02-26 20:26:52 +00:00
. l_paren = > try stack . append ( . { . ctx = . empty , . stack_id = . Paren } ) ,
. l_bracket = > try stack . append ( . { . ctx = . empty , . stack_id = . Bracket } ) ,
. r_paren = > {
2020-05-26 23:45:18 +01:00
_ = stack . pop ( ) ;
if ( curr_ctx . stack_id ! = . Paren ) {
( try peek ( & stack ) ) . ctx = . empty ;
}
} ,
2021-02-26 20:26:52 +00:00
. r_bracket = > {
2020-05-26 23:45:18 +01:00
_ = stack . pop ( ) ;
if ( curr_ctx . stack_id ! = . Bracket ) {
( try peek ( & stack ) ) . ctx = . empty ;
}
} ,
2021-02-26 20:26:52 +00:00
. keyword_error = > curr_ctx . ctx = . global_error_set ,
2020-05-26 23:45:18 +01:00
else = > curr_ctx . ctx = . empty ,
}
switch ( curr_ctx . ctx ) {
. field_access = > | r | curr_ctx . ctx = . {
. field_access = tokenRangeAppend ( r , tok ) ,
} ,
else = > { } ,
}
}
return block : {
2021-03-31 00:25:49 +01: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
const last_char = line [ doc_position . line_index - 1 ] ;
if ( ! filled or last_char ! = ' ' ) {
break : block state . ctx ;
}
} ,
else = > break : block state . ctx ,
}
}
if ( doc_position . line_index < line . len ) {
switch ( line [ doc_position . line_index ] ) {
'a' . . . 'z' , 'A' . . . 'Z' , '_' , '@' = > { } ,
else = > break : block . empty ,
}
tokenizer = std . zig . Tokenizer . init ( line [ doc_position . line_index . . ] ) ;
const tok = tokenizer . next ( ) ;
if ( tok . tag = = . identifier )
break : block PositionContext { . var_access = tok . loc } ;
}
2020-05-26 23:45:18 +01:00
break : block . empty ;
} ;
}
2020-05-28 01:39:36 +01:00
2021-03-01 21:18:38 +00:00
fn addOutlineNodes ( allocator : * std . mem . Allocator , tree : ast . Tree , child : ast . Node . Index , context : * GetDocumentSymbolsContext ) anyerror ! void {
switch ( tree . nodes . items ( . tag ) [ child ] ) {
. string_literal ,
. integer_literal ,
. builtin_call ,
. builtin_call_comma ,
. builtin_call_two ,
. builtin_call_two_comma ,
. call ,
. call_comma ,
. call_one ,
. call_one_comma ,
. async_call ,
. async_call_comma ,
. async_call_one ,
. async_call_one_comma ,
. identifier ,
. add ,
. add_wrap ,
. array_cat ,
. array_mult ,
. assign ,
. assign_bit_and ,
. assign_bit_or ,
. assign_bit_shift_left ,
. assign_bit_shift_right ,
. assign_bit_xor ,
. assign_div ,
. assign_sub ,
. assign_sub_wrap ,
. assign_mod ,
. assign_add ,
. assign_add_wrap ,
. assign_mul ,
. assign_mul_wrap ,
. bang_equal ,
. bit_and ,
. bit_or ,
. bit_shift_left ,
. bit_shift_right ,
. bit_xor ,
. bool_and ,
. bool_or ,
. div ,
. equal_equal ,
. error_union ,
. greater_or_equal ,
. greater_than ,
. less_or_equal ,
. less_than ,
. merge_error_sets ,
. mod ,
. mul ,
. mul_wrap ,
. field_access ,
. switch_range ,
. sub ,
. sub_wrap ,
. @ " orelse " ,
. address_of ,
. @ " await " ,
. bit_not ,
. bool_not ,
. optional_type ,
. negation ,
. negation_wrap ,
. @ " resume " ,
. @ " try " ,
. array_type ,
. array_type_sentinel ,
. ptr_type ,
. ptr_type_aligned ,
. ptr_type_bit_range ,
. ptr_type_sentinel ,
. slice_open ,
. slice_sentinel ,
. deref ,
. unwrap_optional ,
. array_access ,
. @ " return " ,
. @ " break " ,
. @ " continue " ,
. 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 ,
. @ " switch " ,
. switch_comma ,
. switch_case ,
. switch_case_one ,
. @ " for " ,
. for_simple ,
. enum_literal ,
. 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 ,
. @ " while " ,
. while_simple ,
. while_cont ,
. true_literal ,
. false_literal ,
. null_literal ,
. @ " defer " ,
. @ " if " ,
. if_simple ,
. multiline_string_literal ,
. undefined_literal ,
. @ " anytype " ,
. block ,
. block_semicolon ,
. block_two ,
. block_two_semicolon ,
. error_set_decl ,
2020-07-24 08:33:13 +01:00
= > return ,
2021-03-01 21:18:38 +00:00
. container_decl ,
. container_decl_arg ,
. container_decl_arg_trailing ,
. container_decl_two ,
. container_decl_two_trailing ,
. tagged_union ,
. tagged_union_trailing ,
. tagged_union_enum_tag ,
. tagged_union_enum_tag_trailing ,
. tagged_union_two ,
. tagged_union_two_trailing ,
= > {
var buf : [ 2 ] ast . Node . Index = undefined ;
2021-03-26 19:04:51 +00:00
for ( declMembers ( tree , child , & buf ) ) | member |
2021-03-01 21:18:38 +00:00
try addOutlineNodes ( allocator , tree , member , context ) ;
2020-05-28 01:39:36 +01:00
return ;
} ,
2021-03-01 21:18:38 +00:00
else = > | t | { } ,
2020-05-28 01:39:36 +01:00
}
2020-07-07 09:57:02 +01:00
try getDocumentSymbolsInternal ( allocator , tree , child , context ) ;
2020-05-28 01:39:36 +01:00
}
2020-07-07 09:57:02 +01:00
const GetDocumentSymbolsContext = struct {
prev_loc : offsets . TokenLocation = . {
. line = 0 ,
. column = 0 ,
. offset = 0 ,
} ,
symbols : * std . ArrayList ( types . DocumentSymbol ) ,
encoding : offsets . Encoding ,
} ;
2021-02-26 20:26:52 +00:00
fn getDocumentSymbolsInternal ( allocator : * std . mem . Allocator , tree : ast . Tree , node : ast . Node . Index , context : * GetDocumentSymbolsContext ) anyerror ! void {
2020-07-24 11:04:01 +01:00
const name = getDeclName ( tree , node ) orelse return ;
2020-07-07 09:57:02 +01:00
if ( name . len = = 0 )
return ;
2021-03-07 13:51:47 +00:00
const starts = tree . tokens . items ( . start ) ;
const start_loc = context . prev_loc . add ( try offsets . tokenRelativeLocation (
tree ,
context . prev_loc . offset ,
starts [ tree . firstToken ( node ) ] ,
context . encoding ,
) ) ;
const end_loc = start_loc . add ( try offsets . tokenRelativeLocation (
tree ,
start_loc . offset ,
2021-03-13 11:18:32 +00:00
starts [ lastToken ( tree , node ) ] ,
2021-03-07 13:51:47 +00:00
context . encoding ,
) ) ;
2020-07-07 09:57:02 +01:00
context . prev_loc = end_loc ;
2020-05-28 01:39:36 +01:00
const range = types . Range {
. start = . {
. line = @intCast ( i64 , start_loc . line ) ,
. character = @intCast ( i64 , start_loc . column ) ,
} ,
. end = . {
. line = @intCast ( i64 , end_loc . line ) ,
. character = @intCast ( i64 , end_loc . column ) ,
2020-05-28 16:18:48 +01:00
} ,
2020-05-28 01:39:36 +01:00
} ;
2021-02-26 20:26:52 +00:00
const tags = tree . nodes . items ( . tag ) ;
2020-07-07 09:57:02 +01:00
( try context . symbols . addOne ( ) ) . * = . {
. name = name ,
2021-02-26 20:26:52 +00:00
. kind = switch ( tags [ node ] ) {
. fn_proto ,
. fn_proto_simple ,
. fn_proto_multi ,
. fn_proto_one ,
2021-03-01 21:18:38 +00:00
. fn_decl ,
2021-02-26 20:26:52 +00:00
= > . Function ,
. local_var_decl ,
. global_var_decl ,
. aligned_var_decl ,
. simple_var_decl ,
= > . Variable ,
. container_field ,
. container_field_align ,
. container_field_init ,
2021-02-27 20:55:39 +00:00
. tagged_union_enum_tag ,
. tagged_union_enum_tag_trailing ,
2021-03-01 21:18:38 +00:00
. tagged_union ,
. tagged_union_trailing ,
. tagged_union_two ,
. tagged_union_two_trailing ,
2021-02-26 20:26:52 +00:00
= > . Field ,
2020-05-28 16:18:48 +01:00
else = > . Variable ,
2020-05-28 01:39:36 +01:00
} ,
. range = range ,
. selectionRange = range ,
2020-07-07 09:57:02 +01:00
. detail = " " ,
2020-05-28 01:39:36 +01:00
. children = ch : {
var children = std . ArrayList ( types . DocumentSymbol ) . init ( allocator ) ;
2020-07-07 09:57:02 +01:00
var child_context = GetDocumentSymbolsContext {
. prev_loc = start_loc ,
. symbols = & children ,
. encoding = context . encoding ,
} ;
2021-03-26 19:04:51 +00:00
if ( isContainer ( tree , node ) ) {
2021-03-01 21:18:38 +00:00
var buf : [ 2 ] ast . Node . Index = undefined ;
2021-03-26 19:04:51 +00:00
for ( declMembers ( tree , node , & buf ) ) | child |
2021-03-01 21:18:38 +00:00
try addOutlineNodes ( allocator , tree , child , & child_context ) ;
}
2020-05-28 01:39:36 +01:00
2021-03-01 21:18:38 +00:00
if ( varDecl ( tree , node ) ) | var_decl | {
if ( var_decl . ast . init_node ! = 0 )
try addOutlineNodes ( allocator , tree , var_decl . ast . init_node , & child_context ) ;
}
2020-05-28 01:39:36 +01:00
break : ch children . items ;
} ,
} ;
}
2021-02-26 20:26:52 +00:00
pub fn getDocumentSymbols ( allocator : * std . mem . Allocator , tree : ast . Tree , encoding : offsets . Encoding ) ! [ ] types . DocumentSymbol {
var symbols = try std . ArrayList ( types . DocumentSymbol ) . initCapacity ( allocator , tree . rootDecls ( ) . len ) ;
2020-07-07 09:57:02 +01:00
2020-07-07 21:26:12 +01:00
var context = GetDocumentSymbolsContext {
2020-07-07 09:57:02 +01:00
. symbols = & symbols ,
. encoding = encoding ,
} ;
2020-05-28 01:39:36 +01:00
2021-02-26 20:26:52 +00:00
for ( tree . rootDecls ( ) ) | idx | {
try getDocumentSymbolsInternal ( allocator , tree , idx , & context ) ;
2020-05-28 01:39:36 +01:00
}
return symbols . items ;
}
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
ast_node : ast . Node . Index ,
/// Function parameter
2021-02-26 20:26:52 +00:00
param_decl : ast . full . FnProto . Param ,
2020-06-10 14:12:00 +01:00
pointer_payload : struct {
2021-02-27 20:55:39 +00: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 {
identifier : ast . TokenIndex ,
array_expr : ast . Node . Index ,
} ,
2021-03-09 18:53:59 +00:00
array_index : ast . TokenIndex ,
2020-06-10 14:12:00 +01:00
switch_payload : struct {
2021-02-28 12:09:10 +00:00
node : ast . TokenIndex ,
switch_expr : ast . Node . Index ,
items : [ ] const ast . Node . Index ,
2020-06-10 14:12:00 +01:00
} ,
2021-03-08 18:46:23 +00:00
label_decl : ast . TokenIndex ,
2020-06-10 14:12:00 +01:00
} ;
2020-06-10 17:01:44 +01:00
pub const DeclWithHandle = struct {
decl : * Declaration ,
handle : * DocumentStore . Handle ,
2020-06-27 13:29:45 +01:00
pub fn nameToken ( self : DeclWithHandle ) ast . TokenIndex {
2020-06-10 17:01:44 +01:00
const tree = self . handle . tree ;
2021-03-01 13:32:19 +00:00
const token_tags = tree . tokens . items ( . tag ) ;
2020-06-10 17:01:44 +01:00
return switch ( self . decl . * ) {
2020-06-27 13:29:45 +01:00
. ast_node = > | n | getDeclNameToken ( tree , n ) . ? ,
. param_decl = > | p | p . 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 ,
2021-03-01 13:32:19 +00:00
. label_decl = > | ld | ld ,
2020-06-10 17:01:44 +01:00
} ;
}
2020-06-10 17:54:01 +01:00
2020-07-07 09:57:02 +01:00
pub fn location ( self : DeclWithHandle , encoding : offsets . Encoding ) ! offsets . TokenLocation {
2020-06-27 13:29:45 +01:00
const tree = self . handle . tree ;
2021-03-07 13:51:47 +00:00
return try offsets . tokenRelativeLocation ( tree , 0 , tree . tokens . items ( . start ) [ self . nameToken ( ) ] , encoding ) ;
2020-06-27 13:29:45 +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 ,
} ;
}
2020-06-17 21:36:40 +01:00
pub fn resolveType ( self : DeclWithHandle , store : * DocumentStore , arena : * std . heap . ArenaAllocator , bound_type_params : * BoundTypeParams ) ! ? 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 . * ) {
2021-03-08 18:46:23 +00:00
. ast_node = > | node | try resolveTypeOfNodeInternal (
store ,
arena ,
. { . node = node , . handle = self . handle } ,
bound_type_params ,
) ,
2021-03-03 15:34:24 +00:00
. param_decl = > | param_decl | {
2021-02-28 20:57:15 +00:00
if ( typeIsType ( self . handle . tree , param_decl . type_expr ) ) {
var bound_param_it = bound_type_params . iterator ( ) ;
while ( bound_param_it . next ( ) ) | entry | {
2021-03-03 15:34:24 +00:00
if ( std . meta . eql ( entry . key , param_decl ) ) return entry . value ;
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 ;
}
}
return ( ( try resolveTypeOfNodeInternal (
store ,
arena ,
. { . node = param_decl . type_expr , . handle = self . handle } ,
bound_type_params ,
) ) orelse return null ) . instanceTypeVal ( ) ;
2020-06-10 17:54:01 +01:00
} ,
. pointer_payload = > | pay | try resolveUnwrapOptionalType (
store ,
2020-06-10 18:48:40 +01:00
arena ,
2020-06-12 12:56:46 +01:00
( try resolveTypeOfNodeInternal ( store , arena , . {
2020-06-10 17:54:01 +01:00
. node = pay . condition ,
. handle = self . handle ,
2020-06-12 12:56:46 +01:00
} , bound_type_params ) ) orelse return null ,
bound_type_params ,
2020-06-10 17:54:01 +01:00
) ,
2021-03-08 18:46:23 +00:00
. array_payload = > | pay | try resolveBracketAccessType (
store ,
arena ,
( try resolveTypeOfNodeInternal ( store , arena , . {
. node = pay . array_expr ,
. handle = self . handle ,
} , bound_type_params ) ) orelse return null ,
. Single ,
bound_type_params ,
) ,
2021-03-09 18:53:59 +00:00
. array_index = > TypeWithHandle {
2021-03-10 08:29:25 +00:00
. type = . { . data = . primitive , . 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.
const switch_expr_type = ( try resolveTypeOfNodeInternal ( store , arena , . {
. node = pay . switch_expr ,
. handle = self . handle ,
} , bound_type_params ) ) 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 ;
2021-02-28 20:57:15 +00:00
if ( node_tags [ pay . items [ 0 ] ] = = . enum_literal ) {
2020-06-23 11:32:37 +01:00
const scope = findContainerScope ( . { . node = switch_expr_type . type . data . other , . handle = switch_expr_type . handle } ) orelse return null ;
2021-03-03 15:34:24 +00:00
if ( scope . decls . getEntry ( tree . tokenSlice ( main_tokens [ pay . items [ 0 ] ] ) ) ) | candidate | {
2020-06-23 11:32:37 +01:00
switch ( candidate . value ) {
. ast_node = > | node | {
2021-03-09 18:53:59 +00:00
if ( containerField ( switch_expr_type . handle . tree , node ) ) | container_field | {
2021-02-28 20:57:15 +00:00
if ( container_field . ast . type_expr ! = 0 ) {
2020-06-23 11:32:37 +01:00
return ( ( try resolveTypeOfNodeInternal (
store ,
arena ,
2021-02-28 20:57:15 +00:00
. { . node = container_field . ast . type_expr , . handle = switch_expr_type . handle } ,
2020-06-23 11:32:37 +01:00
bound_type_params ,
) ) orelse return null ) . instanceTypeVal ( ) ;
}
}
} ,
else = > { } ,
}
return null ;
}
}
return null ;
} ,
2020-06-10 17:54:01 +01:00
} ;
}
2020-06-10 17:01:44 +01:00
} ;
2020-06-11 00:40:11 +01:00
fn findContainerScope ( container_handle : NodeWithHandle ) ? * Scope {
const container = container_handle . node ;
const handle = container_handle . handle ;
2021-03-26 19:04:51 +00:00
if ( ! isContainer ( handle . tree , container ) ) return null ;
2020-06-11 00:40:11 +01:00
// Find the container scope.
2021-03-01 13:32:19 +00:00
return for ( handle . document_scope . scopes ) | * scope | {
2021-03-02 21:01:13 +00:00
switch ( scope . data ) {
2020-06-11 00:40:11 +01:00
. container = > | node | if ( node = = container ) {
2021-03-02 21:01:13 +00:00
break scope ;
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
}
2020-06-23 10:43:56 +01:00
fn iterateSymbolsContainerInternal (
2020-06-11 00:40:11 +01:00
store : * DocumentStore ,
arena : * std . heap . ArenaAllocator ,
container_handle : NodeWithHandle ,
orig_handle : * DocumentStore . Handle ,
2020-07-12 20:12:09 +01:00
comptime callback : anytype ,
context : anytype ,
2020-06-18 13:43:03 +01:00
instance_access : bool ,
2021-03-11 12:59:09 +00:00
use_trail : * std . ArrayList ( * const ast . Node . Index ) ,
2020-06-11 00:40:11 +01:00
) error { OutOfMemory } ! void {
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
2021-03-10 16:04:14 +00:00
const container_scope = findContainerScope ( container_handle ) orelse return ;
var decl_it = container_scope . decls . iterator ( ) ;
while ( decl_it . next ( ) ) | entry | {
switch ( entry . value ) {
. ast_node = > | node | {
if ( node_tags [ node ] . isContainerField ( ) ) {
if ( ! instance_access and ! is_enum ) continue ;
if ( instance_access and is_enum ) continue ;
}
} ,
. label_decl = > continue ,
else = > { } ,
2020-06-11 00:40:11 +01:00
}
2021-03-10 16:04:14 +00:00
const decl = DeclWithHandle { . decl = & entry . value , . handle = handle } ;
if ( handle ! = orig_handle and ! decl . isPublic ( ) ) continue ;
try callback ( context , decl ) ;
}
2021-03-07 20:54:54 +00:00
2021-03-10 16:04:14 +00:00
for ( container_scope . uses ) | use | {
2021-03-11 12:59:09 +00: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 ;
2021-03-11 12:59:09 +00:00
if ( std . mem . indexOfScalar ( * const ast . Node . Index , use_trail . items , use ) ! = null ) continue ;
2021-03-10 16:04:14 +00:00
try use_trail . append ( use ) ;
2021-03-11 12:59:09 +00:00
const lhs = tree . nodes . items ( . data ) [ use . * ] . lhs ;
2021-03-10 16:04:14 +00:00
const use_expr = ( try resolveTypeOfNode ( store , arena , . {
. node = lhs ,
. handle = handle ,
} ) ) orelse continue ;
const use_expr_node = switch ( use_expr . type . data ) {
. other = > | n | n ,
else = > continue ,
} ;
try iterateSymbolsContainerInternal (
store ,
arena ,
. { . node = use_expr_node , . handle = use_expr . handle } ,
orig_handle ,
callback ,
context ,
false ,
use_trail ,
) ;
2020-06-11 00:40:11 +01:00
}
2020-06-23 10:43:56 +01:00
}
2020-06-11 00:40:11 +01:00
2020-06-23 10:43:56 +01:00
pub fn iterateSymbolsContainer (
store : * DocumentStore ,
arena : * std . heap . ArenaAllocator ,
container_handle : NodeWithHandle ,
orig_handle : * DocumentStore . Handle ,
2020-07-12 20:12:09 +01:00
comptime callback : anytype ,
context : anytype ,
2020-06-23 10:43:56 +01:00
instance_access : bool ,
) error { OutOfMemory } ! void {
2021-03-11 12:59:09 +00:00
var use_trail = std . ArrayList ( * const ast . Node . Index ) . init ( & arena . allocator ) ;
2020-06-23 10:43:56 +01:00
return try iterateSymbolsContainerInternal ( store , arena , container_handle , orig_handle , callback , context , instance_access , & use_trail ) ;
2020-06-11 00:40:11 +01:00
}
2020-06-14 20:24:18 +01:00
pub fn iterateLabels (
handle : * DocumentStore . Handle ,
source_index : usize ,
2020-07-12 20:12:09 +01:00
comptime callback : anytype ,
context : anytype ,
2020-06-14 20:24:18 +01:00
) error { OutOfMemory } ! void {
for ( handle . document_scope . scopes ) | scope | {
if ( source_index > = scope . range . start and source_index < scope . range . end ) {
var decl_it = scope . decls . iterator ( ) ;
while ( decl_it . next ( ) ) | entry | {
switch ( entry . value ) {
. label_decl = > { } ,
else = > continue ,
}
try callback ( context , DeclWithHandle { . decl = & entry . value , . handle = handle } ) ;
}
}
if ( scope . range . start > = source_index ) return ;
}
}
2020-06-23 10:43:56 +01:00
fn iterateSymbolsGlobalInternal (
2020-06-10 17:01:44 +01:00
store : * DocumentStore ,
2020-06-11 00:40:11 +01:00
arena : * std . heap . ArenaAllocator ,
2020-06-10 17:01:44 +01:00
handle : * DocumentStore . Handle ,
source_index : usize ,
2020-07-12 20:12:09 +01:00
comptime callback : anytype ,
context : anytype ,
2021-03-11 12:59:09 +00:00
use_trail : * std . ArrayList ( * const ast . Node . Index ) ,
2020-06-11 00:40:11 +01:00
) error { OutOfMemory } ! void {
2020-06-10 17:01:44 +01:00
for ( handle . document_scope . scopes ) | scope | {
2021-03-10 08:29:25 +00:00
if ( source_index > = scope . range . start and source_index < = scope . range . end ) {
2020-06-10 17:01:44 +01:00
var decl_it = scope . decls . iterator ( ) ;
while ( decl_it . next ( ) ) | entry | {
2021-03-01 15:30:43 +00:00
if ( entry . value = = . ast_node and handle . tree . nodes . items ( . tag ) [ entry . value . ast_node ] . isContainerField ( ) ) continue ;
2020-06-14 20:24:18 +01:00
if ( entry . value = = . label_decl ) continue ;
2020-06-10 17:01:44 +01:00
try callback ( context , DeclWithHandle { . decl = & entry . value , . handle = handle } ) ;
}
2020-06-10 14:12:00 +01:00
2021-03-04 12:26:11 +00:00
for ( scope . uses ) | use | {
2021-03-11 12:59:09 +00:00
if ( std . mem . indexOfScalar ( * const ast . Node . Index , use_trail . items , use ) ! = null ) continue ;
2021-03-04 12:26:11 +00:00
try use_trail . append ( use ) ;
2021-02-26 20:26:52 +00:00
2021-03-10 16:04:14 +00:00
const use_expr = ( try resolveTypeOfNode (
store ,
arena ,
2021-03-11 12:59:09 +00:00
. { . node = handle . tree . nodes . items ( . data ) [ use . * ] . lhs , . handle = handle } ,
2021-03-10 16:04:14 +00:00
) ) orelse continue ;
2021-03-04 12:26:11 +00:00
const use_expr_node = switch ( use_expr . type . data ) {
. other = > | n | n ,
else = > continue ,
} ;
2021-03-10 16:04:14 +00:00
try iterateSymbolsContainerInternal (
store ,
arena ,
. { . node = use_expr_node , . handle = use_expr . handle } ,
handle ,
callback ,
context ,
false ,
use_trail ,
) ;
2021-03-04 12:26:11 +00:00
}
2020-06-10 17:01:44 +01:00
}
2020-06-10 14:12:00 +01:00
2020-06-10 17:01:44 +01:00
if ( scope . range . start > = source_index ) return ;
}
}
2020-06-23 10:43:56 +01:00
pub fn iterateSymbolsGlobal (
store : * DocumentStore ,
arena : * std . heap . ArenaAllocator ,
handle : * DocumentStore . Handle ,
source_index : usize ,
2020-07-12 20:12:09 +01:00
comptime callback : anytype ,
context : anytype ,
2020-06-23 10:43:56 +01:00
) error { OutOfMemory } ! void {
2021-03-11 12:59:09 +00:00
var use_trail = std . ArrayList ( * const ast . Node . Index ) . init ( & arena . allocator ) ;
2020-06-23 10:43:56 +01:00
return try iterateSymbolsGlobalInternal ( store , arena , handle , source_index , callback , context , & use_trail ) ;
}
2021-04-04 13:28:57 +01:00
pub fn innermostBlockScopeIndex ( handle : DocumentStore . Handle , source_index : usize ) usize {
if ( handle . document_scope . scopes . len = = 1 ) return 0 ;
2021-04-02 18:49:01 +01:00
2021-04-04 13:28:57 +01:00
var current : usize = 0 ;
for ( handle . document_scope . scopes [ 1 . . ] ) | * scope , idx | {
2021-04-02 18:49:01 +01:00
if ( source_index > = scope . range . start and source_index < = scope . range . end ) {
switch ( scope . data ) {
2021-04-04 13:28:57 +01:00
. container , . function , . block = > | node | current = idx + 1 ,
2021-04-02 18:49:01 +01:00
else = > { } ,
}
}
2021-04-04 13:28:57 +01:00
if ( scope . range . start > source_index ) break ;
2021-04-02 18:49:01 +01:00
}
return current ;
}
2021-04-04 13:28:57 +01:00
pub fn innermostBlockScope ( handle : DocumentStore . Handle , source_index : usize ) ast . Node . Index {
return handle . document_scope . scopes [ innermostBlockScopeIndex ( handle , source_index ) ] . toNodeIndex ( ) . ? ;
}
2020-06-17 03:12:12 +01:00
pub fn innermostContainer ( handle : * DocumentStore . Handle , source_index : usize ) TypeWithHandle {
2020-06-10 19:24:17 +01:00
var current = handle . document_scope . scopes [ 0 ] . data . container ;
2020-06-17 03:12:12 +01:00
if ( handle . document_scope . scopes . len = = 1 ) return TypeWithHandle . typeVal ( . { . node = current , . handle = handle } ) ;
2020-06-10 19:24:17 +01:00
for ( handle . document_scope . scopes [ 1 . . ] ) | scope | {
2021-03-10 08:29:25 +00:00
if ( source_index > = scope . range . start and source_index < = scope . range . end ) {
2020-06-10 19:24:17 +01:00
switch ( scope . data ) {
. container = > | node | current = node ,
else = > { } ,
}
}
if ( scope . range . start > source_index ) break ;
}
2020-06-17 03:12:12 +01:00
return TypeWithHandle . typeVal ( . { . node = current , . handle = handle } ) ;
2020-06-10 19:24:17 +01:00
}
2020-06-11 00:40:11 +01:00
fn resolveUse (
store : * DocumentStore ,
arena : * std . heap . ArenaAllocator ,
2021-03-11 12:59:09 +00:00
uses : [ ] const * const ast . Node . Index ,
2020-06-11 00:40:11 +01:00
symbol : [ ] const u8 ,
handle : * DocumentStore . Handle ,
) error { OutOfMemory } ! ? DeclWithHandle {
2021-04-04 23:35:33 +01:00
const state = struct {
var using_trail = std . ArrayListUnmanaged ( * const ast . Node . Index ) { } ;
} ;
2021-03-04 12:26:11 +00:00
for ( uses ) | use | {
2021-04-04 23:35:33 +01:00
if ( std . mem . indexOfScalar ( * const ast . Node . Index , state . using_trail . items , use ) ! = null ) continue ;
// We use the child_allocator here because the ArrayList expects its
// allocated memory to persist while it is empty.
try state . using_trail . append ( arena . child_allocator , use ) ;
defer _ = state . using_trail . pop ( ) ;
2021-03-04 12:26:11 +00:00
2021-03-06 19:55:59 +00:00
const use_expr = ( try resolveTypeOfNode (
store ,
arena ,
2021-03-11 12:59:09 +00:00
. { . node = handle . tree . nodes . items ( . data ) [ use . * ] . lhs , . handle = handle } ,
2021-03-06 19:55:59 +00:00
) ) orelse continue ;
2021-03-04 12:26:11 +00:00
const use_expr_node = switch ( use_expr . type . data ) {
. other = > | n | n ,
else = > continue ,
} ;
2021-04-04 23:35:33 +01:00
if ( try lookupSymbolContainer (
2021-03-06 19:55:59 +00:00
store ,
arena ,
. { . node = use_expr_node , . handle = use_expr . handle } ,
symbol ,
false ,
) ) | candidate | {
2021-03-04 12:26:11 +00:00
if ( candidate . handle ! = handle and ! candidate . isPublic ( ) ) {
continue ;
}
return candidate ;
}
}
2020-06-11 00:40:11 +01:00
return null ;
}
2020-06-14 20:24:18 +01:00
pub fn lookupLabel (
handle : * DocumentStore . Handle ,
symbol : [ ] const u8 ,
source_index : usize ,
) error { OutOfMemory } ! ? DeclWithHandle {
for ( handle . document_scope . scopes ) | scope | {
if ( source_index > = scope . range . start and source_index < scope . range . end ) {
2020-07-05 22:56:41 +01:00
if ( scope . decls . getEntry ( symbol ) ) | candidate | {
2020-06-14 20:24:18 +01:00
switch ( candidate . value ) {
. label_decl = > { } ,
else = > continue ,
}
return DeclWithHandle {
. decl = & candidate . value ,
. handle = handle ,
} ;
}
}
if ( scope . range . start > source_index ) return null ;
}
return null ;
}
2021-04-04 23:35:33 +01:00
pub fn lookupSymbolGlobal (
2020-06-11 00:40:11 +01:00
store : * DocumentStore ,
arena : * std . heap . ArenaAllocator ,
handle : * DocumentStore . Handle ,
symbol : [ ] const u8 ,
source_index : usize ,
) error { OutOfMemory } ! ? DeclWithHandle {
2021-04-04 13:28:57 +01:00
const innermost_scope_idx = innermostBlockScopeIndex ( handle . * , source_index ) ;
var curr = innermost_scope_idx ;
while ( curr > = 0 ) : ( curr - = 1 ) {
const scope = & handle . document_scope . scopes [ curr ] ;
2021-04-04 23:35:33 +01:00
if ( source_index > = scope . range . start and source_index < = scope . range . end ) blk : {
2021-03-07 20:54:54 +00:00
if ( scope . decls . getEntry ( symbol ) ) | candidate | {
switch ( candidate . value ) {
. ast_node = > | node | {
2021-04-04 23:35:33 +01:00
if ( handle . tree . nodes . items ( . tag ) [ node ] . isContainerField ( ) ) break : blk ;
2021-03-07 20:54:54 +00:00
} ,
2021-04-04 23:35:33 +01:00
. label_decl = > break : blk ,
2021-03-07 20:54:54 +00:00
else = > { } ,
}
return DeclWithHandle {
. decl = & candidate . value ,
. handle = handle ,
} ;
2020-06-10 17:01:44 +01:00
}
2021-04-04 23:35:33 +01:00
if ( try resolveUse ( store , arena , scope . uses , symbol , handle ) ) | result | return result ;
2021-03-07 20:54:54 +00:00
}
2021-04-04 13:28:57 +01:00
if ( curr = = 0 ) break ;
2020-06-10 17:01:44 +01:00
}
return null ;
}
2021-04-04 23:35:33 +01:00
pub fn lookupSymbolContainer (
2020-06-11 00:40:11 +01:00
store : * DocumentStore ,
arena : * std . heap . ArenaAllocator ,
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 ;
2020-06-18 13:43:03 +01:00
2020-06-11 00:40:11 +01:00
if ( findContainerScope ( container_handle ) ) | container_scope | {
2020-07-05 22:56:41 +01:00
if ( container_scope . decls . getEntry ( symbol ) ) | candidate | {
2020-06-10 17:01:44 +01:00
switch ( candidate . value ) {
. 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 = > { } ,
}
2020-06-10 18:48:40 +01:00
return DeclWithHandle { . decl = & candidate . value , . handle = handle } ;
2020-06-10 17:01:44 +01:00
}
2021-04-04 23:35:33 +01:00
if ( try resolveUse ( store , arena , container_scope . uses , symbol , handle ) ) | result | return result ;
2020-06-10 17:01:44 +01:00
return null ;
}
2020-06-10 23:00:13 +01:00
return null ;
2020-06-10 17:01:44 +01:00
}
2021-03-29 12:02:58 +01:00
fn eqlCompletionItem ( a : types . CompletionItem , b : types . CompletionItem ) bool {
return std . mem . eql ( u8 , a . label , b . label ) ;
}
fn hashCompletionItem ( completion_item : types . CompletionItem ) u32 {
return @truncate ( u32 , std . hash . Wyhash . hash ( 0 , completion_item . label ) ) ;
}
pub const CompletionSet = std . ArrayHashMapUnmanaged (
types . CompletionItem ,
void ,
hashCompletionItem ,
eqlCompletionItem ,
false ,
) ;
comptime {
std . debug . assert ( @sizeOf ( types . CompletionItem ) = = @sizeOf ( CompletionSet . Entry ) ) ;
}
2020-06-10 17:01:44 +01:00
pub const DocumentScope = struct {
2020-06-10 18:48:40 +01:00
scopes : [ ] Scope ,
2021-03-29 12:02:58 +01:00
error_completions : CompletionSet ,
enum_completions : CompletionSet ,
2020-06-10 14:12:00 +01:00
pub fn debugPrint ( self : DocumentScope ) void {
for ( self . scopes ) | scope | {
2020-08-14 11:41:34 +01:00
log . debug (
2020-06-10 14:12:00 +01:00
\\--------------------------
2021-03-04 12:26:11 +00:00
\\Scope {}, range: [{d}, {d})
\\ {d} usingnamespaces
2020-06-10 14:12:00 +01:00
\\Decls:
, . {
scope . data ,
scope . range . start ,
scope . range . end ,
2021-03-03 20:02:31 +00:00
scope . uses . len ,
2020-06-10 14:12:00 +01:00
} ) ;
var decl_it = scope . decls . iterator ( ) ;
var idx : usize = 0 ;
while ( decl_it . next ( ) ) | name_decl | : ( idx + = 1 ) {
2020-08-14 11:41:34 +01:00
if ( idx ! = 0 ) log . debug ( " , " , . { } ) ;
2020-06-10 14:12:00 +01:00
}
2021-01-10 07:12:11 +00:00
log . debug ( " {s} " , . { name_decl . key } ) ;
2020-08-14 11:41:34 +01:00
log . debug ( " \n -------------------------- \n " , . { } ) ;
2020-06-10 14:12:00 +01:00
}
}
2021-03-29 12:02:58 +01:00
pub fn deinit ( self : * DocumentScope , allocator : * std . mem . Allocator ) void {
2020-07-05 22:56:41 +01:00
for ( self . scopes ) | * scope | {
2020-06-10 14:12:00 +01:00
scope . decls . deinit ( ) ;
2021-03-03 20:02:31 +00:00
allocator . free ( scope . uses ) ;
2020-06-16 22:26:45 +01:00
allocator . free ( scope . tests ) ;
2020-06-10 14:12:00 +01:00
}
allocator . free ( self . scopes ) ;
2021-03-29 12:02:58 +01:00
for ( self . error_completions . entries . items ) | entry | {
if ( entry . key . documentation ) | doc | allocator . free ( doc . value ) ;
}
self . error_completions . deinit ( allocator ) ;
for ( self . enum_completions . entries . items ) | entry | {
if ( entry . key . documentation ) | doc | allocator . free ( doc . value ) ;
}
self . enum_completions . deinit ( allocator ) ;
2020-06-10 14:12:00 +01:00
}
} ;
pub const Scope = struct {
pub const Data = union ( enum ) {
2021-02-27 15:38:06 +00: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 ,
} ;
range : SourceRange ,
decls : std . StringHashMap ( Declaration ) ,
2021-02-27 15:38:06 +00:00
tests : [ ] const ast . Node . Index ,
2021-03-11 12:59:09 +00:00
uses : [ ] const * const ast . Node . Index ,
2020-06-10 14:12:00 +01:00
data : Data ,
2021-04-04 13:28:57 +01:00
pub fn toNodeIndex ( self : Scope ) ? ast . Node . Index {
return switch ( self . data ) {
. container , . function , . block = > | idx | idx ,
else = > null ,
} ;
}
2020-06-10 14:12:00 +01:00
} ;
2021-02-26 20:26:52 +00:00
pub fn makeDocumentScope ( allocator : * std . mem . Allocator , tree : ast . Tree ) ! DocumentScope {
2020-07-08 02:05:44 +01:00
var scopes = std . ArrayListUnmanaged ( Scope ) { } ;
2021-03-29 12:02:58 +01:00
var error_completions = CompletionSet { } ;
var enum_completions = CompletionSet { } ;
2020-07-08 02:05:44 +01:00
errdefer {
scopes . deinit ( allocator ) ;
2021-03-29 12:02:58 +01:00
for ( error_completions . entries . items ) | entry | {
if ( entry . key . documentation ) | doc | allocator . free ( doc . value ) ;
}
2020-07-08 02:05:44 +01:00
error_completions . deinit ( allocator ) ;
2021-03-29 12:02:58 +01:00
for ( enum_completions . entries . items ) | entry | {
if ( entry . key . documentation ) | doc | allocator . free ( doc . value ) ;
}
2020-07-08 02:05:44 +01:00
enum_completions . deinit ( allocator ) ;
}
2021-02-27 15:38:06 +00:00
// pass root node index ('0')
try makeScopeInternal ( allocator , & scopes , & error_completions , & enum_completions , tree , 0 ) ;
2020-06-10 14:12:00 +01:00
return DocumentScope {
2020-07-08 02:05:44 +01:00
. scopes = scopes . toOwnedSlice ( allocator ) ,
2021-03-29 12:02:58 +01:00
. error_completions = error_completions ,
. enum_completions = enum_completions ,
2020-06-10 14:12:00 +01:00
} ;
}
2021-02-26 20:26:52 +00:00
fn nodeSourceRange ( tree : ast . Tree , node : ast . Node . Index ) SourceRange {
2021-03-05 21:38:42 +00:00
const loc_start = offsets . tokenLocation ( tree , tree . firstToken ( node ) ) ;
2021-03-13 11:18:32 +00:00
const loc_end = offsets . tokenLocation ( tree , lastToken ( tree , node ) ) ;
2020-06-10 14:12:00 +01:00
return SourceRange {
2021-03-05 21:38:42 +00:00
. start = loc_start . start ,
. end = loc_end . end ,
2020-06-10 14:12:00 +01:00
} ;
}
fn makeScopeInternal (
allocator : * std . mem . Allocator ,
2020-07-08 02:05:44 +01:00
scopes : * std . ArrayListUnmanaged ( Scope ) ,
2021-03-29 12:02:58 +01:00
error_completions : * CompletionSet ,
enum_completions : * CompletionSet ,
2021-02-26 20:26:52 +00:00
tree : ast . Tree ,
node_idx : ast . Node . Index ,
2020-06-10 14:12:00 +01:00
) error { OutOfMemory } ! void {
2021-02-27 15:38:06 +00:00
const tags = tree . nodes . items ( . tag ) ;
const token_tags = tree . tokens . items ( . tag ) ;
2021-02-27 20:55:39 +00:00
const data = tree . nodes . items ( . data ) ;
const main_tokens = tree . nodes . items ( . main_token ) ;
2021-03-26 19:04:51 +00:00
const node_tag = tags [ node_idx ] ;
2021-02-27 15:38:06 +00:00
2021-03-26 19:04:51 +00:00
if ( isContainer ( tree , node_idx ) ) {
2021-03-01 18:34:28 +00:00
var buf : [ 2 ] ast . Node . Index = undefined ;
2021-03-28 15:02:48 +01:00
const ast_decls = declMembers ( tree , node_idx , & buf ) ;
2020-06-10 14:12:00 +01:00
2020-07-08 02:05:44 +01:00
( try scopes . addOne ( allocator ) ) . * = . {
2021-02-27 15:38:06 +00:00
. range = nodeSourceRange ( tree , node_idx ) ,
2020-06-10 14:12:00 +01:00
. decls = std . StringHashMap ( Declaration ) . init ( allocator ) ,
2021-03-03 20:02:31 +00:00
. uses = & . { } ,
2021-02-27 20:55:39 +00:00
. tests = & . { } ,
2021-02-27 15:38:06 +00:00
. data = . { . container = node_idx } ,
2020-06-10 14:12:00 +01:00
} ;
2020-06-11 09:21:08 +01:00
const scope_idx = scopes . items . len - 1 ;
2021-03-29 10:28:52 +01:00
var uses = std . ArrayListUnmanaged ( * const ast . Node . Index ) { } ;
var tests = std . ArrayListUnmanaged ( ast . Node . Index ) { } ;
2020-06-10 14:12:00 +01:00
errdefer {
scopes . items [ scope_idx ] . decls . deinit ( ) ;
2021-03-29 10:28:52 +01:00
uses . deinit ( allocator ) ;
tests . deinit ( allocator ) ;
}
if ( node_tag = = . error_set_decl ) {
// All identifiers in main_token..data.lhs are error fields.
var i = main_tokens [ node_idx ] ;
while ( i < data [ node_idx ] . rhs ) : ( i + = 1 ) {
if ( token_tags [ i ] = = . identifier ) {
2021-03-29 12:02:58 +01:00
try error_completions . put ( allocator , . {
2021-03-29 10:28:52 +01:00
. label = tree . tokenSlice ( i ) ,
. kind = . Constant ,
. insertTextFormat = . PlainText ,
. insertText = tree . tokenSlice ( i ) ,
2021-03-29 12:02:58 +01:00
} , { } ) ;
2021-03-29 10:28:52 +01:00
}
}
2020-06-10 14:12:00 +01:00
}
2021-03-29 12:57:16 +01:00
const container_decl = switch ( node_tag ) {
. container_decl , . container_decl_trailing = > tree . containerDecl ( node_idx ) ,
. container_decl_arg , . container_decl_arg_trailing = > tree . containerDeclArg ( node_idx ) ,
. container_decl_two , . container_decl_two_trailing = > blk : {
var buffer : [ 2 ] ast . Node . Index = undefined ;
break : blk tree . containerDeclTwo ( & buffer , node_idx ) ;
} ,
. tagged_union , . tagged_union_trailing = > tree . taggedUnion ( node_idx ) ,
. tagged_union_enum_tag , . tagged_union_enum_tag_trailing = > tree . taggedUnionEnumTag ( node_idx ) ,
. tagged_union_two , . tagged_union_two_trailing = > blk : {
var buffer : [ 2 ] ast . Node . Index = undefined ;
break : blk tree . taggedUnionTwo ( & buffer , node_idx ) ;
} ,
else = > null ,
} ;
// Only tagged unions and enums should pass this
const can_have_enum_completions = if ( container_decl ) | container | blk : {
const kind = token_tags [ container . ast . main_token ] ;
break : blk kind ! = . keyword_struct and
( kind ! = . keyword_union or container . ast . enum_token ! = null or container . ast . arg ! = 0 ) ;
} else false ;
2021-03-11 12:59:09 +00:00
for ( ast_decls ) | * ptr_decl | {
const decl = ptr_decl . * ;
2021-03-03 20:02:31 +00:00
if ( tags [ decl ] = = . @ " usingnamespace " ) {
2021-03-29 10:28:52 +01:00
try uses . append ( allocator , ptr_decl ) ;
2021-03-03 20:02:31 +00:00
continue ;
}
2020-06-10 14:12:00 +01:00
2021-03-29 10:28:52 +01:00
try makeScopeInternal (
allocator ,
scopes ,
error_completions ,
enum_completions ,
tree ,
decl ,
) ;
2020-06-10 14:12:00 +01:00
const name = getDeclName ( tree , decl ) orelse continue ;
2021-03-29 13:35:14 +01:00
2021-03-06 19:55:59 +00:00
if ( tags [ decl ] = = . test_decl ) {
2021-03-29 10:28:52 +01:00
try tests . append ( allocator , decl ) ;
2021-03-06 19:55:59 +00:00
continue ;
}
2021-03-29 13:41:58 +01:00
if ( try scopes . items [ scope_idx ] . decls . fetchPut ( name , . { . ast_node = decl } ) ) | existing | {
// TODO Record a redefinition error.
}
2021-03-29 12:57:16 +01:00
if ( ! can_have_enum_completions )
continue ;
2020-06-10 17:01:44 +01:00
2021-03-29 10:28:52 +01:00
const container_field = switch ( tags [ decl ] ) {
2021-02-27 15:38:06 +00:00
. container_field = > tree . containerField ( decl ) ,
. container_field_align = > tree . containerFieldAlign ( decl ) ,
. container_field_init = > tree . containerFieldInit ( decl ) ,
else = > null ,
} ;
if ( container_field ) | field | {
2021-03-29 12:57:16 +01:00
if ( ! std . mem . eql ( u8 , name , " _ " ) ) {
try enum_completions . put ( allocator , . {
. label = name ,
. kind = . Constant ,
. documentation = if ( try getDocComments ( allocator , tree , decl , . Markdown ) ) | docs | . {
. kind = . Markdown ,
. value = docs ,
} else null ,
} , { } ) ;
2020-06-11 01:20:44 +01:00
}
}
2020-06-10 14:12:00 +01:00
}
2021-03-29 10:28:52 +01:00
scopes . items [ scope_idx ] . tests = tests . toOwnedSlice ( allocator ) ;
scopes . items [ scope_idx ] . uses = uses . toOwnedSlice ( allocator ) ;
2020-06-10 14:12:00 +01:00
return ;
}
2021-03-26 19:04:51 +00:00
switch ( node_tag ) {
2021-03-06 19:55:59 +00:00
. fn_proto ,
. fn_proto_one ,
. fn_proto_simple ,
. fn_proto_multi ,
. fn_decl ,
= > | fn_tag | {
2021-02-27 20:55:39 +00:00
var buf : [ 1 ] ast . Node . Index = undefined ;
2021-03-01 13:32:19 +00:00
const func = fnProto ( tree , node_idx , & buf ) . ? ;
2020-06-10 14:12:00 +01:00
2020-07-08 02:05:44 +01:00
( try scopes . addOne ( allocator ) ) . * = . {
2021-02-27 20:55:39 +00:00
. range = nodeSourceRange ( tree , node_idx ) ,
2020-06-10 14:12:00 +01:00
. decls = std . StringHashMap ( Declaration ) . init ( allocator ) ,
2021-03-03 20:02:31 +00:00
. uses = & . { } ,
2021-02-27 20:55:39 +00:00
. tests = & . { } ,
2021-02-28 12:09:10 +00:00
. data = . { . function = node_idx } ,
2020-06-10 14:12:00 +01:00
} ;
var scope_idx = scopes . items . len - 1 ;
errdefer scopes . items [ scope_idx ] . decls . deinit ( ) ;
2021-02-27 20:55:39 +00:00
var it = func . iterate ( tree ) ;
while ( it . next ( ) ) | 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 | {
2021-03-29 10:28:52 +01:00
if ( try scopes . items [ scope_idx ] . decls . fetchPut (
tree . tokenSlice ( name_token ) ,
. { . param_decl = param } ,
) ) | existing | {
2021-02-27 20:55:39 +00:00
// TODO record a redefinition error
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
2021-03-29 10:40:52 +01:00
if ( param . type_expr ! = 0 ) try makeScopeInternal (
2021-03-29 10:28:52 +01:00
allocator ,
scopes ,
error_completions ,
enum_completions ,
tree ,
param . type_expr ,
) ;
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 ;
if ( return_type_node = = 0 ) break : blk ;
// Visit the return type
2021-03-29 10:28:52 +01:00
try makeScopeInternal (
allocator ,
scopes ,
error_completions ,
enum_completions ,
tree ,
2021-04-04 00:03:25 +01:00
return_type_node ,
2021-03-29 10:28:52 +01:00
) ;
2021-03-02 14:32:38 +00:00
}
2021-04-04 00:03:25 +01:00
if ( data [ node_idx ] . rhs = = 0 ) return ;
// Visit the function body
try makeScopeInternal (
allocator ,
scopes ,
error_completions ,
enum_completions ,
tree ,
data [ node_idx ] . rhs ,
) ;
2020-06-10 14:12:00 +01:00
} ,
2021-02-27 20:55:39 +00:00
. test_decl = > {
2021-03-29 10:28:52 +01:00
return try makeScopeInternal (
allocator ,
scopes ,
error_completions ,
enum_completions ,
tree ,
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 ) ;
2021-03-13 11:18:32 +00:00
const last_token = lastToken ( tree , node_idx ) ;
2020-08-15 20:21:20 +01:00
2021-02-27 20:55:39 +00:00
// if labeled block
if ( token_tags [ first_token ] = = . identifier ) {
const scope = try scopes . addOne ( allocator ) ;
scope . * = . {
. range = . {
2021-03-05 21:38:42 +00:00
. start = offsets . tokenLocation ( tree , main_tokens [ node_idx ] ) . start ,
. end = offsets . tokenLocation ( tree , last_token ) . start ,
2021-02-27 20:55:39 +00:00
} ,
. decls = std . StringHashMap ( Declaration ) . init ( allocator ) ,
2021-03-03 20:02:31 +00:00
. uses = & . { } ,
2021-02-28 12:09:10 +00:00
. tests = & . { } ,
2021-02-27 20:55:39 +00:00
. data = . other ,
} ;
errdefer scope . decls . deinit ( ) ;
2021-02-28 12:09:10 +00:00
try scope . decls . putNoClobber ( tree . tokenSlice ( first_token ) , . { . label_decl = first_token } ) ;
2021-02-27 20:55:39 +00:00
}
2020-06-14 20:24:18 +01:00
2020-08-15 20:21:20 +01:00
( try scopes . addOne ( allocator ) ) . * = . {
2021-02-27 20:55:39 +00:00
. range = nodeSourceRange ( tree , node_idx ) ,
2020-08-15 20:21:20 +01:00
. decls = std . StringHashMap ( Declaration ) . init ( allocator ) ,
2021-03-03 20:02:31 +00:00
. uses = & . { } ,
2021-02-27 20:55:39 +00:00
. tests = & . { } ,
. data = . { . block = node_idx } ,
2020-08-15 20:21:20 +01:00
} ;
var scope_idx = scopes . items . len - 1 ;
2021-03-11 12:59:09 +00:00
var uses = std . ArrayList ( * const ast . Node . Index ) . init ( allocator ) ;
2020-08-15 20:21:20 +01:00
errdefer {
scopes . items [ scope_idx ] . decls . deinit ( ) ;
2021-03-03 20:02:31 +00:00
uses . deinit ( ) ;
2020-06-14 20:24:18 +01:00
}
2021-03-26 19:04:51 +00:00
const statements : [ ] const ast . Node . Index = switch ( node_tag ) {
2021-02-27 20:55:39 +00:00
. block , . block_semicolon = > tree . extra_data [ data [ node_idx ] . lhs . . data [ node_idx ] . rhs ] ,
. block_two , . block_two_semicolon = > blk : {
2021-02-28 12:09:10 +00:00
const statements = & [ _ ] ast . Node . Index { data [ node_idx ] . lhs , data [ node_idx ] . rhs } ;
const len : usize = if ( data [ node_idx ] . lhs = = 0 )
@as ( usize , 0 )
else if ( data [ node_idx ] . rhs = = 0 )
@as ( usize , 1 )
else
@as ( usize , 2 ) ;
2021-02-27 20:55:39 +00:00
break : blk statements [ 0 . . len ] ;
} ,
else = > unreachable ,
2020-06-10 14:12:00 +01:00
} ;
2021-03-11 12:59:09 +00:00
for ( statements ) | * ptr_stmt | {
const idx = ptr_stmt . * ;
2021-03-03 20:02:31 +00:00
if ( tags [ idx ] = = . @ " usingnamespace " ) {
2021-03-11 12:59:09 +00:00
try uses . append ( ptr_stmt ) ;
2021-03-03 20:02:31 +00:00
continue ;
}
2021-02-27 20:55:39 +00:00
try makeScopeInternal ( allocator , scopes , error_completions , enum_completions , tree , idx ) ;
2021-02-28 12:09:10 +00:00
if ( varDecl ( tree , idx ) ) | var_decl | {
2021-02-27 20:55:39 +00:00
const name = tree . tokenSlice ( var_decl . ast . mut_token + 1 ) ;
if ( try scopes . items [ scope_idx ] . decls . fetchPut ( name , . { . ast_node = idx } ) ) | existing | {
// TODO record a redefinition error.
2020-06-10 14:12:00 +01:00
}
}
}
2021-03-03 20:02:31 +00:00
scopes . items [ scope_idx ] . uses = uses . toOwnedSlice ( ) ;
2020-06-10 14:12:00 +01:00
return ;
} ,
2021-03-06 19:55:59 +00:00
. @ " if " ,
. if_simple ,
= > {
2021-03-26 19:04:51 +00:00
const if_node : ast . full . If = if ( node_tag = = . @ " if " )
2021-03-26 19:46:49 +00:00
ifFull ( tree , node_idx )
2021-02-27 20:55:39 +00:00
else
2021-03-30 18:59:58 +01:00
ifSimple ( tree , node_idx ) ;
2020-06-10 14:12:00 +01:00
2021-02-27 20:55:39 +00:00
if ( if_node . payload_token ) | payload | {
2020-07-08 02:05:44 +01:00
var scope = try scopes . addOne ( allocator ) ;
2020-06-10 14:12:00 +01:00
scope . * = . {
. range = . {
2021-03-05 21:38:42 +00:00
. start = offsets . tokenLocation ( tree , payload ) . start ,
2021-03-13 11:18:32 +00:00
. end = offsets . tokenLocation ( tree , lastToken ( tree , if_node . ast . then_expr ) ) . end ,
2020-06-10 14:12:00 +01:00
} ,
. decls = std . StringHashMap ( Declaration ) . init ( allocator ) ,
2021-03-03 20:02:31 +00:00
. uses = & . { } ,
2021-02-27 20:55:39 +00:00
. tests = & . { } ,
2020-06-10 14:12:00 +01:00
. data = . other ,
} ;
errdefer scope . decls . deinit ( ) ;
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 ) ;
2020-06-10 14:12:00 +01:00
try scope . decls . putNoClobber ( name , . {
. pointer_payload = . {
2021-02-27 20:55:39 +00:00
. name = name_token ,
. condition = if_node . ast . cond_expr ,
2020-06-10 14:12:00 +01:00
} ,
} ) ;
}
2021-03-29 11:01:24 +01:00
try makeScopeInternal (
allocator ,
scopes ,
error_completions ,
enum_completions ,
tree ,
if_node . ast . then_expr ,
) ;
2021-02-27 20:55:39 +00:00
if ( if_node . ast . else_expr ! = 0 ) {
if ( if_node . error_token ) | err_token | {
std . debug . assert ( token_tags [ err_token ] = = . identifier ) ;
2020-07-08 02:05:44 +01:00
var scope = try scopes . addOne ( allocator ) ;
2020-06-10 14:12:00 +01:00
scope . * = . {
. range = . {
2021-03-05 21:38:42 +00:00
. start = offsets . tokenLocation ( tree , err_token ) . start ,
2021-03-13 11:18:32 +00:00
. end = offsets . tokenLocation ( tree , lastToken ( tree , if_node . ast . else_expr ) ) . end ,
2020-06-10 14:12:00 +01:00
} ,
. decls = std . StringHashMap ( Declaration ) . init ( allocator ) ,
2021-03-03 20:02:31 +00:00
. uses = & . { } ,
2021-02-27 20:55:39 +00:00
. tests = & . { } ,
2020-06-10 14:12:00 +01:00
. data = . other ,
} ;
errdefer scope . decls . deinit ( ) ;
2021-02-27 20:55:39 +00:00
const name = tree . tokenSlice ( err_token ) ;
try scope . decls . putNoClobber ( name , . { . ast_node = if_node . ast . else_expr } ) ;
2020-06-10 14:12:00 +01:00
}
2021-03-29 11:01:24 +01:00
try makeScopeInternal (
allocator ,
scopes ,
error_completions ,
enum_completions ,
tree ,
if_node . ast . else_expr ,
) ;
2020-06-10 14:12:00 +01:00
}
} ,
2021-03-06 19:55:59 +00:00
. @ " while " ,
. while_simple ,
. while_cont ,
. @ " for " ,
. for_simple ,
2021-03-26 19:04:51 +00:00
= > {
2021-03-30 18:59:58 +01:00
const while_node = whileAst ( tree , node_idx ) . ? ;
2021-03-26 19:04:51 +00:00
const is_for = node_tag = = . @ " for " or node_tag = = . for_simple ;
2021-03-08 18:46:23 +00:00
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 ) ;
2020-07-08 02:05:44 +01:00
var scope = try scopes . addOne ( allocator ) ;
2020-06-14 20:24:18 +01:00
scope . * = . {
. range = . {
2021-03-05 21:38:42 +00:00
. start = offsets . tokenLocation ( tree , while_node . ast . while_token ) . start ,
2021-03-13 11:18:32 +00:00
. end = offsets . tokenLocation ( tree , lastToken ( tree , node_idx ) ) . end ,
2020-06-14 20:24:18 +01:00
} ,
. decls = std . StringHashMap ( Declaration ) . init ( allocator ) ,
2021-03-03 20:02:31 +00:00
. uses = & . { } ,
2021-02-27 20:55:39 +00:00
. tests = & . { } ,
2020-06-14 20:24:18 +01:00
. data = . other ,
} ;
errdefer scope . decls . deinit ( ) ;
2021-02-27 20:55:39 +00:00
try scope . decls . putNoClobber ( tree . tokenSlice ( label ) , . { . label_decl = label } ) ;
2020-06-14 20:24:18 +01:00
}
2021-02-27 20:55:39 +00:00
if ( while_node . payload_token ) | payload | {
2020-07-08 02:05:44 +01:00
var scope = try scopes . addOne ( allocator ) ;
2020-06-10 14:12:00 +01:00
scope . * = . {
. range = . {
2021-03-05 21:38:42 +00:00
. start = offsets . tokenLocation ( tree , payload ) . start ,
2021-03-13 11:18:32 +00:00
. end = offsets . tokenLocation ( tree , lastToken ( tree , while_node . ast . then_expr ) ) . end ,
2020-06-10 14:12:00 +01:00
} ,
. decls = std . StringHashMap ( Declaration ) . init ( allocator ) ,
2021-03-03 20:02:31 +00:00
. uses = & . { } ,
2021-02-27 20:55:39 +00:00
. tests = & . { } ,
2020-06-10 14:12:00 +01:00
. data = . other ,
} ;
errdefer scope . decls . deinit ( ) ;
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 ) ;
2021-03-28 15:02:48 +01:00
try scope . decls . putNoClobber ( name , if ( is_for ) . {
. array_payload = . {
. identifier = name_token ,
. array_expr = while_node . ast . cond_expr ,
} ,
} else . {
. pointer_payload = . {
. name = name_token ,
. condition = while_node . ast . cond_expr ,
} ,
} ) ;
2021-03-08 18:46:23 +00:00
// for loop with index as well
if ( token_tags [ name_token + 1 ] = = . comma ) {
const index_token = name_token + 2 ;
std . debug . assert ( token_tags [ index_token ] = = . identifier ) ;
2021-03-29 11:01:24 +01:00
if ( try scope . decls . fetchPut (
tree . tokenSlice ( index_token ) ,
. { . array_index = index_token } ,
) ) | existing | {
2021-03-08 18:46:23 +00:00
// TODO Record a redefinition error
}
}
2020-06-10 14:12:00 +01:00
}
2021-03-29 11:01:24 +01:00
try makeScopeInternal (
allocator ,
scopes ,
error_completions ,
enum_completions ,
tree ,
while_node . ast . then_expr ,
) ;
2020-06-10 14:12:00 +01:00
2021-02-27 20:55:39 +00:00
if ( while_node . ast . else_expr ! = 0 ) {
if ( while_node . error_token ) | err_token | {
std . debug . assert ( token_tags [ err_token ] = = . identifier ) ;
2020-07-08 02:05:44 +01:00
var scope = try scopes . addOne ( allocator ) ;
2020-06-10 14:12:00 +01:00
scope . * = . {
. range = . {
2021-03-05 21:38:42 +00:00
. start = offsets . tokenLocation ( tree , err_token ) . start ,
2021-03-13 11:18:32 +00:00
. end = offsets . tokenLocation ( tree , lastToken ( tree , while_node . ast . else_expr ) ) . end ,
2020-06-10 14:12:00 +01:00
} ,
. decls = std . StringHashMap ( Declaration ) . init ( allocator ) ,
2021-03-03 20:02:31 +00:00
. uses = & . { } ,
2021-02-27 20:55:39 +00:00
. tests = & . { } ,
2020-06-10 14:12:00 +01:00
. data = . other ,
} ;
errdefer scope . decls . deinit ( ) ;
2021-02-27 20:55:39 +00:00
const name = tree . tokenSlice ( err_token ) ;
2021-02-28 12:09:10 +00:00
try scope . decls . putNoClobber ( name , . { . ast_node = while_node . ast . else_expr } ) ;
2020-06-10 14:12:00 +01:00
}
2021-03-29 11:01:24 +01:00
try makeScopeInternal (
allocator ,
scopes ,
error_completions ,
enum_completions ,
tree ,
while_node . ast . else_expr ,
) ;
2020-06-10 14:12:00 +01: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 ;
const extra = tree . extraData ( data [ node_idx ] . rhs , ast . Node . SubRange ) ;
const cases = tree . extra_data [ extra . start . . extra . end ] ;
2021-03-09 18:53:59 +00:00
for ( cases ) | case | {
2021-03-09 11:35:56 +00:00
const switch_case : ast . full . SwitchCase = switch ( tags [ case ] ) {
. switch_case = > tree . switchCase ( case ) ,
. switch_case_one = > tree . switchCaseOne ( case ) ,
else = > continue ,
2020-06-14 20:24:18 +01:00
} ;
2021-03-09 11:35:56 +00:00
if ( switch_case . payload_token ) | payload | {
var scope = try scopes . addOne ( allocator ) ;
scope . * = . {
. range = . {
. start = offsets . tokenLocation ( tree , payload ) . start ,
2021-03-13 11:18:32 +00:00
. end = offsets . tokenLocation ( tree , lastToken ( tree , switch_case . ast . target_expr ) ) . end ,
2021-03-09 11:35:56 +00:00
} ,
. decls = std . StringHashMap ( Declaration ) . init ( allocator ) ,
. uses = & . { } ,
. tests = & . { } ,
. data = . other ,
} ;
errdefer scope . decls . deinit ( ) ;
2020-06-10 14:12:00 +01:00
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
2021-03-09 11:35:56 +00:00
try scope . decls . putNoClobber ( name , . {
. switch_payload = . {
. node = name_token ,
. switch_expr = cond ,
. items = switch_case . ast . values ,
} ,
} ) ;
}
2021-03-29 11:01:24 +01:00
try makeScopeInternal (
allocator ,
scopes ,
error_completions ,
enum_completions ,
tree ,
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 ,
= > {
2021-02-28 12:09:10 +00:00
const var_decl = varDecl ( tree , node_idx ) . ? ;
if ( var_decl . ast . type_node ! = 0 ) {
2021-03-29 11:01:24 +01:00
try makeScopeInternal (
allocator ,
scopes ,
error_completions ,
enum_completions ,
tree ,
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 ) {
2021-03-29 11:01:24 +01:00
try makeScopeInternal (
allocator ,
scopes ,
error_completions ,
enum_completions ,
tree ,
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 ,
= > {
var buf : [ 1 ] ast . Node . Index = undefined ;
2021-04-01 12:14:49 +01:00
const call = callFull ( tree , node_idx , & buf ) . ? ;
2021-03-06 19:55:59 +00:00
try makeScopeInternal ( allocator , scopes , error_completions , enum_completions , tree , call . ast . fn_expr ) ;
for ( call . ast . params ) | param |
try makeScopeInternal ( allocator , scopes , error_completions , enum_completions , tree , param ) ;
} ,
. 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 ,
= > {
var buf : [ 2 ] ast . Node . Index = undefined ;
2021-03-26 19:04:51 +00:00
const struct_init : ast . full . StructInit = switch ( node_tag ) {
2021-03-06 19:55:59 +00:00
. struct_init , . struct_init_comma = > tree . structInit ( node_idx ) ,
. struct_init_dot , . struct_init_dot_comma = > tree . structInitDot ( node_idx ) ,
. struct_init_dot_two , . struct_init_dot_two_comma = > tree . structInitDotTwo ( & buf , node_idx ) ,
. struct_init_one , . struct_init_one_comma = > tree . structInitOne ( buf [ 0 . . 1 ] , node_idx ) ,
else = > unreachable ,
} ;
if ( struct_init . ast . type_expr ! = 0 )
2021-03-29 11:01:24 +01:00
try makeScopeInternal (
allocator ,
scopes ,
error_completions ,
enum_completions ,
tree ,
struct_init . ast . type_expr ,
) ;
2021-03-06 19:55:59 +00:00
for ( struct_init . ast . fields ) | field | {
try makeScopeInternal ( allocator , scopes , error_completions , enum_completions , tree , field ) ;
}
} ,
. 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 ,
= > {
var buf : [ 2 ] ast . Node . Index = undefined ;
2021-03-26 19:04:51 +00:00
const array_init : ast . full . ArrayInit = switch ( node_tag ) {
2021-03-06 19:55:59 +00:00
. array_init , . array_init_comma = > tree . arrayInit ( node_idx ) ,
. array_init_dot , . array_init_dot_comma = > tree . arrayInitDot ( node_idx ) ,
. array_init_dot_two , . array_init_dot_two_comma = > tree . arrayInitDotTwo ( & buf , node_idx ) ,
. array_init_one , . array_init_one_comma = > tree . arrayInitOne ( buf [ 0 . . 1 ] , node_idx ) ,
else = > unreachable ,
} ;
if ( array_init . ast . type_expr ! = 0 )
2021-03-29 11:01:24 +01:00
try makeScopeInternal (
allocator ,
scopes ,
error_completions ,
enum_completions ,
tree ,
array_init . ast . type_expr ,
) ;
2021-03-06 19:55:59 +00:00
for ( array_init . ast . elements ) | elem | {
try makeScopeInternal ( allocator , scopes , error_completions , enum_completions , tree , elem ) ;
}
} ,
. container_field ,
. container_field_align ,
. container_field_init ,
= > {
const field = containerField ( tree , node_idx ) . ? ;
2021-03-30 10:07:29 +01:00
if ( field . ast . type_expr ! = 0 )
2021-03-29 11:01:24 +01:00
try makeScopeInternal (
allocator ,
scopes ,
error_completions ,
enum_completions ,
tree ,
field . ast . type_expr ,
) ;
2021-03-30 10:07:29 +01:00
if ( field . ast . align_expr ! = 0 )
2021-03-29 11:01:24 +01:00
try makeScopeInternal (
allocator ,
scopes ,
error_completions ,
enum_completions ,
tree ,
field . ast . align_expr ,
) ;
2021-03-30 10:07:29 +01:00
if ( field . ast . value_expr ! = 0 )
2021-03-29 11:01:24 +01:00
try makeScopeInternal (
allocator ,
scopes ,
error_completions ,
enum_completions ,
tree ,
field . ast . value_expr ,
) ;
2021-03-06 19:55:59 +00:00
} ,
. builtin_call ,
. builtin_call_comma ,
. builtin_call_two ,
. builtin_call_two_comma ,
= > {
const b_data = data [ node_idx ] ;
2021-03-26 19:04:51 +00:00
const params = switch ( node_tag ) {
2021-03-06 19:55:59 +00:00
. builtin_call , . builtin_call_comma = > tree . extra_data [ b_data . lhs . . b_data . rhs ] ,
. builtin_call_two , . builtin_call_two_comma = > if ( b_data . lhs = = 0 )
& [ _ ] ast . Node . Index { }
else if ( b_data . rhs = = 0 )
& [ _ ] ast . Node . Index { b_data . lhs }
else
& [ _ ] ast . Node . Index { b_data . lhs , b_data . rhs } ,
else = > unreachable ,
} ;
for ( params ) | param | {
try makeScopeInternal ( allocator , scopes , error_completions , enum_completions , tree , param ) ;
}
} ,
. ptr_type ,
. ptr_type_aligned ,
. ptr_type_bit_range ,
. ptr_type_sentinel ,
= > {
const ptr_type : ast . full . PtrType = ptrType ( tree , node_idx ) . ? ;
if ( ptr_type . ast . sentinel ! = 0 )
2021-03-29 11:01:24 +01:00
try makeScopeInternal (
allocator ,
scopes ,
error_completions ,
enum_completions ,
tree ,
ptr_type . ast . sentinel ,
) ;
2021-03-06 19:55:59 +00:00
if ( ptr_type . ast . align_node ! = 0 )
2021-03-29 11:01:24 +01:00
try makeScopeInternal (
allocator ,
scopes ,
error_completions ,
enum_completions ,
tree ,
ptr_type . ast . align_node ,
) ;
2021-03-06 19:55:59 +00:00
if ( ptr_type . ast . child_type ! = 0 )
2021-03-29 11:01:24 +01:00
try makeScopeInternal (
allocator ,
scopes ,
error_completions ,
enum_completions ,
tree ,
ptr_type . ast . child_type ,
) ;
2021-03-06 19:55:59 +00:00
} ,
. slice ,
. slice_open ,
. slice_sentinel ,
= > {
2021-03-26 19:04:51 +00:00
const slice : ast . full . Slice = switch ( node_tag ) {
2021-03-06 19:55:59 +00:00
. slice = > tree . slice ( node_idx ) ,
. slice_open = > tree . sliceOpen ( node_idx ) ,
. slice_sentinel = > tree . sliceSentinel ( node_idx ) ,
else = > unreachable ,
} ;
if ( slice . ast . sliced ! = 0 )
2021-03-29 11:01:24 +01:00
try makeScopeInternal (
allocator ,
scopes ,
error_completions ,
enum_completions ,
tree ,
slice . ast . sliced ,
) ;
2021-03-06 19:55:59 +00:00
if ( slice . ast . start ! = 0 )
2021-03-29 11:01:24 +01:00
try makeScopeInternal (
allocator ,
scopes ,
error_completions ,
enum_completions ,
tree ,
slice . ast . start ,
) ;
2021-03-06 19:55:59 +00:00
if ( slice . ast . end ! = 0 )
2021-03-29 11:01:24 +01:00
try makeScopeInternal (
allocator ,
scopes ,
error_completions ,
enum_completions ,
tree ,
slice . ast . end ,
) ;
2021-03-06 19:55:59 +00:00
if ( slice . ast . sentinel ! = 0 )
2021-03-29 11:01:24 +01:00
try makeScopeInternal (
allocator ,
scopes ,
error_completions ,
enum_completions ,
tree ,
slice . ast . sentinel ,
) ;
2021-03-06 19:55:59 +00:00
} ,
2021-03-11 19:45:05 +00:00
. @ " errdefer " = > {
const expr = data [ node_idx ] . rhs ;
if ( data [ node_idx ] . lhs ! = 0 ) {
const payload_token = data [ node_idx ] . lhs ;
var scope = try scopes . addOne ( allocator ) ;
scope . * = . {
. range = . {
. start = offsets . tokenLocation ( tree , payload_token ) . start ,
2021-03-13 11:18:32 +00:00
. end = offsets . tokenLocation ( tree , lastToken ( tree , expr ) ) . end ,
2021-03-11 19:45:05 +00:00
} ,
. decls = std . StringHashMap ( Declaration ) . init ( allocator ) ,
. uses = & . { } ,
. tests = & . { } ,
. data = . other ,
} ;
errdefer scope . decls . deinit ( ) ;
const name = tree . tokenSlice ( payload_token ) ;
try scope . decls . putNoClobber ( name , . { . ast_node = expr } ) ;
}
try makeScopeInternal ( allocator , scopes , error_completions , enum_completions , tree , expr ) ;
} ,
2021-03-06 19:55:59 +00:00
// no scope
. @ " asm " ,
. asm_simple ,
. asm_output ,
. asm_input ,
. error_value ,
. @ " anytype " ,
. multiline_string_literal ,
. string_literal ,
. enum_literal ,
. identifier ,
. anyframe_type ,
. anyframe_literal ,
. char_literal ,
. integer_literal ,
. float_literal ,
. false_literal ,
. true_literal ,
. null_literal ,
. undefined_literal ,
. unreachable_literal ,
. @ " continue " ,
= > { } ,
. @ " break " , . @ " defer " = > {
if ( data [ node_idx ] . rhs ! = 0 )
2021-03-29 11:01:24 +01:00
try makeScopeInternal (
allocator ,
scopes ,
error_completions ,
enum_completions ,
tree ,
data [ node_idx ] . rhs ,
) ;
2021-03-06 19:55:59 +00:00
} ,
// all lhs kind of nodes
. @ " 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
= > {
if ( data [ node_idx ] . lhs ! = 0 ) {
2021-03-29 11:01:24 +01:00
try makeScopeInternal (
allocator ,
scopes ,
error_completions ,
enum_completions ,
tree ,
data [ node_idx ] . lhs ,
) ;
2021-03-06 19:55:59 +00:00
}
} ,
2020-06-10 14:12:00 +01:00
else = > {
2021-03-06 19:55:59 +00:00
if ( data [ node_idx ] . lhs ! = 0 )
2021-03-29 11:01:24 +01:00
try makeScopeInternal (
allocator ,
scopes ,
error_completions ,
enum_completions ,
tree ,
data [ node_idx ] . lhs ,
) ;
2021-03-06 19:55:59 +00:00
if ( data [ node_idx ] . rhs ! = 0 )
2021-03-29 11:01:24 +01:00
try makeScopeInternal (
allocator ,
scopes ,
error_completions ,
enum_completions ,
tree ,
data [ node_idx ] . rhs ,
) ;
2020-06-10 14:12:00 +01:00
} ,
}
}