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-05-09 03:38:08 +01:00
2020-06-03 09:23:14 +01:00
/// Get a declaration's doc comment node
fn getDocCommentNode ( tree : * ast . Tree , node : * ast . Node ) ? * ast . Node . DocComment {
2020-07-16 15:56:22 +01:00
if ( node . castTag ( . FnProto ) ) | func | {
2020-07-16 17:04:23 +01:00
return func . getTrailer ( " doc_comments " ) ;
2020-07-16 15:56:22 +01:00
} else if ( node . castTag ( . VarDecl ) ) | var_decl | {
2020-07-16 17:04:23 +01:00
return var_decl . getTrailer ( " doc_comments " ) ;
2020-07-16 15:56:22 +01:00
} else if ( node . castTag ( . ContainerField ) ) | field | {
2020-06-03 09:23:14 +01:00
return field . doc_comments ;
2020-07-16 15:56:22 +01:00
} else if ( node . castTag ( . ErrorTag ) ) | tag | {
2020-06-03 09:23:14 +01:00
return tag . doc_comments ;
}
return null ;
}
/// 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 ,
tree : * ast . Tree ,
node : * ast . Node ,
format : types . MarkupKind ,
) ! ? [ ] const u8 {
2020-06-03 09:23:14 +01:00
if ( getDocCommentNode ( tree , node ) ) | doc_comment_node | {
2020-06-12 15:42:41 +01:00
return try collectDocComments ( allocator , tree , doc_comment_node , format ) ;
2020-05-03 22:27:08 +01:00
}
2020-05-14 17:07:46 +01:00
return null ;
}
2020-06-12 15:42:41 +01:00
pub fn collectDocComments (
allocator : * std . mem . Allocator ,
tree : * ast . Tree ,
doc_comments : * ast . Node . DocComment ,
format : types . MarkupKind ,
) ! [ ] const u8 {
2020-05-14 17:07:46 +01:00
var lines = std . ArrayList ( [ ] const u8 ) . init ( allocator ) ;
defer lines . deinit ( ) ;
2020-05-23 23:21:02 +01:00
var curr_line_tok = doc_comments . first_line ;
while ( true ) : ( curr_line_tok + = 1 ) {
switch ( tree . token_ids [ curr_line_tok ] ) {
. LineComment = > continue ,
. DocComment , . ContainerDocComment = > {
try lines . append ( std . fmt . trim ( tree . tokenSlice ( curr_line_tok ) [ 3 . . ] ) ) ;
} ,
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)
2020-05-09 03:38:08 +01:00
pub fn getFunctionSignature ( tree : * ast . Tree , func : * ast . Node . FnProto ) [ ] const u8 {
2020-05-23 23:21:02 +01:00
const start = tree . token_locs [ func . firstToken ( ) ] . start ;
const end = tree . token_locs [ switch ( func . return_type ) {
2020-05-16 16:30:16 +01:00
. Explicit , . InferErrorSet = > | node | node . lastToken ( ) ,
2020-05-14 15:22:15 +01:00
. Invalid = > | r_paren | r_paren ,
2020-05-23 23:21:02 +01:00
} ] . end ;
2020-05-03 22:27:08 +01:00
return tree . source [ start . . end ] ;
}
2020-05-04 03:17:19 +01:00
2020-05-09 03:38:08 +01:00
/// Gets a function snippet insert text
2020-06-06 00:44:43 +01:00
pub fn getFunctionSnippet ( allocator : * std . mem . Allocator , tree : * ast . Tree , func : * ast . Node . FnProto , skip_self_param : bool ) ! [ ] const u8 {
2020-07-16 20:02:30 +01:00
const name_tok = func . getTrailer ( " name_token " ) orelse unreachable ;
2020-05-09 03:38:08 +01:00
var buffer = std . ArrayList ( u8 ) . init ( allocator ) ;
try buffer . ensureCapacity ( 128 ) ;
try buffer . appendSlice ( tree . tokenSlice ( name_tok ) ) ;
try buffer . append ( '(' ) ;
var buf_stream = buffer . outStream ( ) ;
2020-05-23 23:21:02 +01:00
for ( func . paramsConst ( ) ) | param , param_num | {
2020-06-06 00:44:43 +01:00
if ( skip_self_param and param_num = = 0 ) continue ;
2020-06-06 08:35:59 +01:00
if ( param_num ! = @boolToInt ( skip_self_param ) ) try buffer . appendSlice ( " , ${ " ) else try buffer . appendSlice ( " ${ " ) ;
2020-05-09 03:38:08 +01:00
2020-05-23 23:21:02 +01:00
try buf_stream . print ( " {}: " , . { param_num + 1 } ) ;
2020-05-09 03:38:08 +01:00
2020-05-23 23:21:02 +01:00
if ( param . comptime_token ) | _ | {
2020-05-12 17:59:16 +01:00
try buffer . appendSlice ( " comptime " ) ;
}
2020-05-23 23:21:02 +01:00
if ( param . noalias_token ) | _ | {
2020-05-12 17:59:16 +01:00
try buffer . appendSlice ( " noalias " ) ;
}
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 ( " : " ) ;
}
2020-05-23 23:21:02 +01:00
switch ( param . param_type ) {
2020-07-12 20:12:09 +01:00
. any_type = > try buffer . appendSlice ( " anytype " ) ,
2020-05-16 15:21:42 +01:00
. type_expr = > | type_expr | {
var curr_tok = type_expr . firstToken ( ) ;
2020-05-16 15:24:41 +01:00
var end_tok = type_expr . lastToken ( ) ;
2020-05-16 15:21:42 +01:00
while ( curr_tok < = end_tok ) : ( curr_tok + = 1 ) {
2020-05-23 23:21:02 +01:00
const id = tree . token_ids [ curr_tok ] ;
const is_comma = id = = . Comma ;
2020-05-13 18:30:57 +01:00
2020-05-16 15:21:42 +01:00
if ( curr_tok = = end_tok and is_comma ) continue ;
2020-05-13 18:30:57 +01:00
2020-05-16 15:21:42 +01:00
try buffer . appendSlice ( tree . tokenSlice ( curr_tok ) ) ;
if ( is_comma or id = = . Keyword_const ) try buffer . append ( ' ' ) ;
}
2020-05-17 15:23:04 +01:00
} ,
2020-05-09 03:38:08 +01:00
}
try buffer . append ( '}' ) ;
}
try buffer . append ( ')' ) ;
return buffer . toOwnedSlice ( ) ;
}
2020-05-04 03:17:19 +01:00
/// Gets a function signature (keywords, name, return value)
2020-05-09 03:38:08 +01:00
pub fn getVariableSignature ( tree : * ast . Tree , var_decl : * ast . Node . VarDecl ) [ ] const u8 {
2020-05-23 23:21:02 +01:00
const start = tree . token_locs [ var_decl . firstToken ( ) ] . start ;
const end = tree . token_locs [ var_decl . semicolon_token ] . start ;
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)
pub fn getContainerFieldSignature ( tree : * ast . Tree , field : * ast . Node . ContainerField ) [ ] const u8 {
const start = tree . token_locs [ field . firstToken ( ) ] . start ;
const end = tree . token_locs [ field . lastToken ( ) ] . end ;
return tree . source [ start . . end ] ;
}
2020-06-12 12:56:46 +01:00
/// The type node is "type"
fn typeIsType ( tree : * ast . Tree , node : * ast . Node ) bool {
2020-07-16 15:56:22 +01:00
if ( node . castTag ( . Identifier ) ) | ident | {
2020-06-12 12:56:46 +01:00
return std . mem . eql ( u8 , tree . tokenSlice ( ident . token ) , " type " ) ;
}
return false ;
}
2020-05-17 15:23:04 +01:00
pub fn isTypeFunction ( tree : * ast . Tree , func : * ast . Node . FnProto ) bool {
switch ( func . return_type ) {
2020-06-12 12:56:46 +01:00
. Explicit = > | node | return typeIsType ( tree , node ) ,
2020-05-17 15:23:04 +01:00
. InferErrorSet , . Invalid = > return false ,
}
}
2020-06-27 18:45:58 +01:00
pub fn isGenericFunction ( tree : * ast . Tree , func : * ast . Node . FnProto ) bool {
for ( func . paramsConst ( ) ) | param | {
2020-07-12 20:12:09 +01:00
if ( param . param_type = = . any_type or param . comptime_token ! = 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
2020-05-18 21:19:23 +01:00
pub fn getDeclNameToken ( tree : * ast . Tree , node : * ast . Node ) ? ast . TokenIndex {
2020-07-16 15:56:22 +01:00
switch ( node . tag ) {
2020-05-18 21:19:23 +01:00
. VarDecl = > {
2020-07-16 15:56:22 +01:00
const vari = node . castTag ( . VarDecl ) . ? ;
2020-05-18 21:19:23 +01:00
return vari . name_token ;
} ,
. FnProto = > {
2020-07-16 15:56:22 +01:00
const func = node . castTag ( . FnProto ) . ? ;
2020-07-16 17:04:23 +01:00
if ( func . getTrailer ( " name_token " ) = = null ) return null ;
2020-07-16 17:07:21 +01:00
return func . getTrailer ( " name_token " ) . ? ;
2020-05-18 21:19:23 +01:00
} ,
. ContainerField = > {
2020-07-16 15:56:22 +01:00
const field = node . castTag ( . ContainerField ) . ? ;
2020-05-18 21:19:23 +01:00
return field . name_token ;
} ,
2020-06-11 09:21:08 +01:00
. ErrorTag = > {
2020-07-16 15:56:22 +01:00
const tag = node . castTag ( . ErrorTag ) . ? ;
2020-06-11 09:21:08 +01:00
return tag . name_token ;
} ,
2020-06-10 23:00:13 +01:00
// We need identifier for captures and error set tags
2020-05-23 00:58:39 +01:00
. Identifier = > {
2020-07-16 15:56:22 +01:00
const ident = node . castTag ( . Identifier ) . ? ;
2020-05-23 00:58:39 +01:00
return ident . token ;
} ,
2020-05-28 13:41:40 +01:00
. TestDecl = > {
2020-07-16 15:56:22 +01:00
const decl = node . castTag ( . TestDecl ) . ? ;
return ( decl . name . castTag ( . StringLiteral ) orelse return null ) . token ;
2020-05-28 13:41:40 +01:00
} ,
2020-05-18 21:19:23 +01:00
else = > { } ,
}
return null ;
}
fn getDeclName ( tree : * ast . Tree , node : * ast . Node ) ? [ ] const u8 {
2020-05-28 13:41:40 +01:00
const name = tree . tokenSlice ( getDeclNameToken ( tree , node ) orelse return null ) ;
2020-07-16 15:56:22 +01:00
return switch ( node . tag ) {
2020-06-01 18:48:14 +01:00
. TestDecl = > name [ 1 . . name . len - 1 ] ,
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 . * ) {
2020-07-16 15:56:22 +01:00
. ast_node = > | inner_node | inner_node . tag = = . ContainerDecl or inner_node . tag = = . Root ,
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 ;
2020-07-16 15:56:22 +01:00
if ( node_handle . node . castTag ( . Identifier ) ) | ident | {
2020-06-15 01:59:49 +01:00
return try lookupSymbolGlobal ( store , arena , handle , handle . tree . tokenSlice ( ident . token ) , handle . tree . token_locs [ ident . token ] . start ) ;
}
2020-07-16 20:02:30 +01:00
if ( node_handle . node . cast ( ast . Node . SimpleInfixOp ) ) | infix_op | {
if ( node_handle . node . tag ! = . Period ) return null ;
2020-06-15 01:59:49 +01:00
2020-07-16 15:56:22 +01:00
const container_node = if ( infix_op . lhs . castTag ( . BuiltinCall ) ) | builtin_call | block : {
2020-06-15 01:59:49 +01:00
if ( ! std . mem . eql ( u8 , handle . tree . tokenSlice ( builtin_call . builtin_token ) , " @import " ) )
return null ;
const inner_node = ( try resolveTypeOfNode ( store , arena , . { . node = infix_op . lhs , . handle = handle } ) ) orelse return null ;
2020-07-16 15:56:22 +01:00
std . debug . assert ( inner_node . type . data . other . tag = = . Root ) ;
2020-06-17 03:12:12 +01:00
break : block NodeWithHandle { . node = inner_node . type . data . other , . handle = inner_node . handle } ;
2020-06-15 01:59:49 +01:00
} else if ( try resolveVarDeclAliasInternal ( store , arena , . { . node = infix_op . 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 ,
} ;
2020-07-16 15:56:22 +01:00
if ( resolved_node . tag ! = . ContainerDecl and resolved_node . tag ! = . Root ) 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 ;
if ( try lookupSymbolContainer ( store , arena , container_node , handle . tree . tokenSlice ( infix_op . rhs . firstToken ( ) ) , false ) ) | inner_decl | {
2020-06-15 02:39:50 +01:00
if ( root ) return inner_decl ;
2020-06-15 01:59:49 +01:00
return inner_decl ;
}
}
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 ;
2020-07-16 15:56:22 +01:00
if ( decl . castTag ( . VarDecl ) ) | var_decl | {
2020-07-16 17:04:23 +01:00
if ( ! var_decl . trailer_flags . has ( " init_node " ) ) return null ;
2020-06-17 03:12:12 +01:00
if ( handle . tree . token_ids [ var_decl . mut_token ] ! = . Keyword_const ) return null ;
2020-06-14 23:19:21 +01:00
2020-07-16 17:04:23 +01:00
const base_expr = var_decl . getTrailer ( " init_node " ) . ? ;
2020-07-16 20:02:30 +01:00
if ( base_expr . cast ( ast . Node . SimpleInfixOp ) ) | infix_op | {
if ( base_expr . tag ! = . Period ) return null ;
2020-06-15 01:59:49 +01:00
const name = handle . tree . tokenSlice ( infix_op . rhs . firstToken ( ) ) ;
if ( ! std . mem . eql ( u8 , handle . tree . tokenSlice ( var_decl . name_token ) , name ) )
return null ;
2020-06-14 23:19:21 +01:00
2020-06-15 01:59:49 +01:00
return try resolveVarDeclAliasInternal ( store , arena , . { . node = base_expr , . handle = handle } , true ) ;
2020-06-14 23:19:21 +01:00
}
}
return null ;
}
2020-05-24 13:24:18 +01:00
fn findReturnStatementInternal (
tree : * ast . Tree ,
fn_decl : * ast . Node . FnProto ,
base_node : * ast . Node ,
already_found : * bool ,
) ? * ast . Node . ControlFlowExpression {
2020-05-19 14:42:36 +01:00
var result : ? * ast . Node . ControlFlowExpression = null ;
2020-05-24 15:07:48 +01:00
var child_idx : usize = 0 ;
2020-05-24 15:31:28 +01:00
while ( base_node . iterate ( child_idx ) ) | child_node | : ( child_idx + = 1 ) {
2020-07-24 08:33:13 +01:00
if ( child_node . castTag ( . Return ) ) | cfe | {
// If we are calling ourselves recursively, ignore this return.
if ( cfe . getRHS ( ) ) | rhs | {
if ( rhs . castTag ( . Call ) ) | call_node | {
if ( call_node . lhs . tag = = . Identifier ) {
if ( std . mem . eql ( u8 , getDeclName ( tree , call_node . lhs ) . ? , getDeclName ( tree , & fn_decl . base ) . ? ) ) {
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 ;
result = cfe ;
continue ;
2020-05-19 14:42:36 +01:00
}
2020-05-24 13:24:18 +01:00
result = findReturnStatementInternal ( tree , fn_decl , child_node , already_found ) ;
2020-05-19 14:42:36 +01:00
}
return result ;
}
2020-05-24 13:24:18 +01:00
fn findReturnStatement ( tree : * ast . Tree , fn_decl : * ast . Node . FnProto ) ? * ast . Node . ControlFlowExpression {
2020-05-19 14:42:36 +01:00
var already_found = false ;
2020-07-16 20:02:30 +01:00
return findReturnStatementInternal ( tree , fn_decl , fn_decl . getTrailer ( " body_node " ) . ? , & already_found ) ;
2020-05-19 14:42:36 +01:00
}
/// Resolves the return type of a function
2020-06-12 12:56:46 +01:00
fn resolveReturnType (
store : * DocumentStore ,
arena : * std . heap . ArenaAllocator ,
fn_decl : * ast . Node . FnProto ,
handle : * DocumentStore . Handle ,
bound_type_params : * BoundTypeParams ,
2020-06-17 03:12:12 +01:00
) ! ? TypeWithHandle {
2020-07-16 20:02:30 +01:00
if ( isTypeFunction ( handle . tree , fn_decl ) and fn_decl . trailer_flags . has ( " body_node " ) ) {
2020-05-19 14:42:36 +01: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.
2020-06-10 17:54:01 +01:00
const ret = findReturnStatement ( handle . tree , fn_decl ) orelse return null ;
2020-07-24 08:33:13 +01:00
if ( ret . getRHS ( ) ) | rhs | {
2020-06-14 21:43:29 +01:00
return try resolveTypeOfNodeInternal ( store , arena , . {
2020-06-12 12:56:46 +01:00
. node = rhs ,
. handle = handle ,
2020-06-14 21:43:29 +01:00
} , bound_type_params ) ;
}
2020-05-19 14:42:36 +01:00
return null ;
}
return switch ( fn_decl . return_type ) {
2020-06-13 19:20:04 +01:00
. InferErrorSet = > | return_type | block : {
const child_type = ( try resolveTypeOfNodeInternal ( store , arena , . {
. node = return_type ,
. handle = handle ,
} , bound_type_params ) ) orelse return null ;
2020-06-17 03:12:12 +01:00
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 } ;
2020-06-13 19:20:04 +01:00
} ,
2020-06-17 03:12:12 +01:00
. Explicit = > | return_type | ( ( try resolveTypeOfNodeInternal ( store , arena , . {
2020-06-12 12:56:46 +01:00
. node = return_type ,
. handle = handle ,
2020-06-17 03:12:12 +01:00
} , bound_type_params ) ) orelse return null ) . instanceTypeVal ( ) ,
2020-05-19 14:42:36 +01:00
. Invalid = > null ,
} ;
}
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 ,
} ;
2020-07-16 20:02:30 +01:00
if ( opt_node . cast ( ast . Node . SimplePrefixOp ) ) | prefix_op | {
if ( opt_node . tag = = . OptionalType ) {
2020-06-17 03:12:12 +01:00
return ( ( try resolveTypeOfNodeInternal ( store , arena , . {
2020-06-12 12:56:46 +01:00
. node = prefix_op . rhs ,
. handle = opt . handle ,
2020-06-17 03:12:12 +01:00
} , 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
} ;
2020-07-16 20:02:30 +01:00
if ( rhs_node . cast ( ast . Node . SimpleInfixOp ) ) | infix_op | {
if ( rhs_node . tag = = . ErrorUnion ) {
2020-06-17 03:12:12 +01:00
return ( ( try resolveTypeOfNodeInternal ( store , arena , . {
2020-06-12 12:56:46 +01:00
. node = infix_op . rhs ,
. handle = rhs . handle ,
2020-06-17 03:12:12 +01:00
} , bound_type_params ) ) orelse return null ) . instanceTypeVal ( ) ;
2020-06-11 00:40:11 +01:00
}
}
return null ;
}
2020-07-16 20:02:30 +01:00
/// Resolves the child type of a deref type
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 ,
else = > return null ,
} ;
2020-07-23 18:30:03 +01:00
if ( deref_node . castTag ( . PtrType ) ) | ptr_type | {
switch ( deref . handle . tree . token_ids [ ptr_type . op_token ] ) {
. Asterisk = > {
return ( ( try resolveTypeOfNodeInternal ( store , arena , . {
. node = ptr_type . rhs ,
. handle = deref . handle ,
} , bound_type_params ) ) orelse return null ) . instanceTypeVal ( ) ;
} ,
. LBracket , . AsteriskAsterisk = > return null ,
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 ,
} ;
2020-07-23 19:33:43 +01:00
if ( lhs_node . castTag ( . SliceType ) ) | slice_type | {
if ( rhs = = . Single )
return ( ( try resolveTypeOfNodeInternal ( store , arena , . {
. node = slice_type . rhs ,
. handle = lhs . handle ,
} , bound_type_params ) ) orelse return null ) . instanceTypeVal ( ) ;
return lhs ;
} else if ( lhs_node . castTag ( . ArrayType ) ) | array_type | {
if ( rhs = = . Single )
return ( ( try resolveTypeOfNodeInternal ( store , arena , . {
. node = array_type . rhs ,
. handle = lhs . handle ,
} , bound_type_params ) ) orelse return null ) . instanceTypeVal ( ) ;
return TypeWithHandle {
. type = . { . data = . { . slice = array_type . rhs } , . is_type_val = false } ,
. handle = lhs . handle ,
} ;
} else if ( lhs_node . castTag ( . PtrType ) ) | ptr_type | {
if ( ptr_type . rhs . castTag ( . ArrayType ) ) | child_arr | {
if ( rhs = = . Single ) {
return ( ( try resolveTypeOfNodeInternal ( store , arena , . {
. node = child_arr . rhs ,
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 ( ) ;
}
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
}
2020-06-17 21:36:40 +01:00
pub const BoundTypeParams = std . AutoHashMap ( * const ast . Node . FnProto . ParamDecl , TypeWithHandle ) ;
fn allDigits ( str : [ ] const u8 ) bool {
for ( str ) | c | {
if ( ! std . ascii . isDigit ( c ) ) return false ;
}
return true ;
}
pub fn isTypeIdent ( tree : * ast . Tree , token_idx : ast . TokenIndex ) bool {
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 ;
2020-07-16 15:56:22 +01:00
switch ( node . tag ) {
2020-05-13 14:03:33 +01:00
. VarDecl = > {
2020-07-16 15:56:22 +01:00
const vari = node . castTag ( . VarDecl ) . ? ;
2020-07-16 20:02:30 +01:00
if ( vari . getTrailer ( " type_node " ) ) | type_node | block : {
2020-06-17 03:12:12 +01:00
return ( ( try resolveTypeOfNodeInternal (
store ,
arena ,
2020-07-16 20:02:30 +01:00
. { . node = vari . getTrailer ( " type_node " ) orelse break : block , . handle = handle } ,
2020-06-17 03:12:12 +01:00
bound_type_params ,
) ) orelse break : block ) . instanceTypeVal ( ) ;
}
2020-07-16 20:02:30 +01:00
if ( vari . getTrailer ( " init_node " ) = = null ) return null ;
2020-06-17 03:12:12 +01:00
2020-07-16 20:02:30 +01:00
return try resolveTypeOfNodeInternal ( store , arena , . { . node = vari . getTrailer ( " init_node " ) . ? , . handle = handle } , bound_type_params ) ;
2020-05-11 13:28:08 +01:00
} ,
2020-05-13 14:03:33 +01:00
. Identifier = > {
2020-06-17 21:36:40 +01:00
if ( isTypeIdent ( handle . tree , node . firstToken ( ) ) ) {
return TypeWithHandle {
. type = . { . data = . primitive , . is_type_val = true } ,
. handle = handle ,
} ;
}
2020-06-11 00:40:11 +01:00
if ( try lookupSymbolGlobal ( store , arena , handle , handle . tree . getNodeSource ( node ) , handle . tree . token_locs [ node . firstToken ( ) ] . start ) ) | child | {
2020-06-18 13:12:09 +01:00
switch ( child . decl . * ) {
2020-06-18 01:23:56 +01:00
. ast_node = > | n | if ( n = = node ) return null ,
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
} ,
2020-05-13 14:03:33 +01:00
. ContainerField = > {
2020-07-16 15:56:22 +01:00
const field = node . castTag ( . ContainerField ) . ? ;
2020-06-17 03:12:12 +01:00
return ( ( try resolveTypeOfNodeInternal (
store ,
arena ,
. { . node = field . type_expr orelse return null , . handle = handle } ,
bound_type_params ,
) ) orelse return null ) . instanceTypeVal ( ) ;
2020-05-11 13:28:08 +01:00
} ,
2020-05-23 23:21:02 +01:00
. Call = > {
2020-07-16 15:56:22 +01:00
const call = node . castTag ( . Call ) . ? ;
2020-06-17 13:07:21 +01:00
const decl = ( try resolveTypeOfNodeInternal (
store ,
arena ,
. { . node = call . lhs , . handle = handle } ,
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 ,
} ;
2020-07-16 15:56:22 +01:00
if ( decl_node . castTag ( . FnProto ) ) | fn_decl | {
2020-06-13 19:20:04 +01:00
var has_self_param : u8 = 0 ;
2020-07-16 20:02:30 +01:00
if ( call . lhs . cast ( ast . Node . SimpleInfixOp ) ) | lhs_infix_op | {
if ( call . lhs . tag = = . Period ) {
2020-06-13 19:20:04 +01:00
has_self_param = 1 ;
}
}
2020-06-12 12:56:46 +01:00
// Bidn type params to the expressions passed in the calls.
2020-06-13 19:20:04 +01:00
const param_len = std . math . min ( call . params_len + has_self_param , fn_decl . params_len ) ;
2020-06-12 12:56:46 +01:00
for ( fn_decl . paramsConst ( ) ) | * decl_param , param_idx | {
2020-06-13 19:20:04 +01:00
if ( param_idx < has_self_param ) continue ;
2020-06-12 12:56:46 +01:00
if ( param_idx > = param_len ) break ;
const type_param = switch ( decl_param . param_type ) {
. type_expr = > | type_node | typeIsType ( decl . handle . tree , type_node ) ,
else = > false ,
} ;
if ( ! type_param ) continue ;
const call_param_type = ( try resolveTypeOfNodeInternal ( store , arena , . {
2020-06-13 19:20:04 +01:00
. node = call . paramsConst ( ) [ param_idx - has_self_param ] ,
2020-06-12 12:56:46 +01:00
. handle = handle ,
} , 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
2020-06-13 19:20:04 +01:00
_ = try bound_type_params . put ( decl_param , call_param_type ) ;
2020-06-12 12:56:46 +01:00
}
return try resolveReturnType ( store , arena , fn_decl , decl . handle , bound_type_params ) ;
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
} ,
2020-06-23 17:57:28 +01:00
. Comptime = > {
2020-07-16 15:56:22 +01:00
const ct = node . castTag ( . Comptime ) . ? ;
2020-06-23 17:57:28 +01:00
return try resolveTypeOfNodeInternal ( store , arena , . { . node = ct . expr , . handle = handle } , bound_type_params ) ;
} ,
2020-06-11 09:12:31 +01:00
. GroupedExpression = > {
2020-07-16 15:56:22 +01:00
const grouped = node . castTag ( . GroupedExpression ) . ? ;
2020-06-12 12:56:46 +01:00
return try resolveTypeOfNodeInternal ( store , arena , . { . node = grouped . expr , . handle = handle } , bound_type_params ) ;
2020-06-11 09:12:31 +01:00
} ,
2020-05-23 23:21:02 +01:00
. StructInitializer = > {
2020-07-16 15:56:22 +01:00
const struct_init = node . castTag ( . StructInitializer ) . ? ;
2020-06-17 03:12:12 +01:00
return ( ( try resolveTypeOfNodeInternal (
store ,
arena ,
. { . node = struct_init . lhs , . handle = handle } ,
bound_type_params ,
) ) orelse return null ) . instanceTypeVal ( ) ;
2020-05-11 13:28:08 +01:00
} ,
2020-05-16 17:04:07 +01:00
. ErrorSetDecl = > {
2020-06-17 03:12:12 +01:00
return TypeWithHandle . typeVal ( node_handle ) ;
2020-05-16 17:04:07 +01:00
} ,
2020-07-23 17:06:39 +01:00
. Slice = > {
const slice = node . castTag ( . Slice ) . ? ;
2020-06-12 12:56:46 +01:00
const left_type = ( try resolveTypeOfNodeInternal ( store , arena , . {
2020-07-23 17:06:39 +01:00
. node = slice . 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
} ,
. Deref , . UnwrapOptional = > {
const suffix = node . cast ( ast . Node . SimpleSuffixOp ) . ? ;
const left_type = ( try resolveTypeOfNodeInternal ( store , arena , . {
. node = suffix . lhs ,
. handle = handle ,
} , bound_type_params ) ) orelse return null ;
return switch ( node . tag ) {
2020-06-12 12:56:46 +01:00
. UnwrapOptional = > 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
} ,
. ArrayAccess = > {
const arr_acc = node . castTag ( . ArrayAccess ) . ? ;
const left_type = ( try resolveTypeOfNodeInternal ( store , arena , . {
. node = arr_acc . lhs ,
. 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
} ,
2020-07-16 20:19:08 +01:00
. Period = > {
const infix_op = node . cast ( ast . Node . SimpleInfixOp ) . ? ;
const rhs_str = nodeToString ( handle . tree , infix_op . rhs ) orelse return null ;
// If we are accessing a pointer type, remove one pointerness level :)
const left_type = try resolveFieldAccessLhsType (
store ,
arena ,
( try resolveTypeOfNodeInternal ( store , arena , . {
. node = infix_op . lhs ,
. 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 ,
! left_type . type . is_type_val ,
) ) | child | {
return try child . resolveType ( store , arena , bound_type_params ) ;
} else return null ;
} ,
2020-07-23 17:06:39 +01:00
. OrElse = > {
2020-07-16 20:19:08 +01:00
const infix_op = node . cast ( ast . Node . SimpleInfixOp ) . ? ;
const left_type = ( try resolveTypeOfNodeInternal ( store , arena , . {
. node = infix_op . lhs ,
. handle = handle ,
} , bound_type_params ) ) orelse return null ;
return try resolveUnwrapOptionalType ( store , arena , left_type , bound_type_params ) ;
} ,
. Catch = > {
2020-07-17 15:01:21 +01:00
const infix_op = node . cast ( ast . Node . Catch ) . ? ;
2020-07-16 20:19:08 +01:00
const left_type = ( try resolveTypeOfNodeInternal ( store , arena , . {
. node = infix_op . lhs ,
. handle = handle ,
} , bound_type_params ) ) orelse return null ;
return try resolveUnwrapErrorType ( store , arena , left_type , bound_type_params ) ;
} ,
. ErrorUnion = > return TypeWithHandle . typeVal ( node_handle ) ,
. SliceType ,
. ArrayType ,
. OptionalType ,
. PtrType ,
= > return TypeWithHandle . typeVal ( node_handle ) ,
. Try = > {
const prefix_op = node . cast ( ast . Node . SimplePrefixOp ) . ? ;
const rhs_type = ( try resolveTypeOfNodeInternal ( store , arena , . {
. node = prefix_op . rhs ,
. handle = handle ,
} , bound_type_params ) ) orelse return null ;
return try resolveUnwrapErrorType ( store , arena , rhs_type , bound_type_params ) ;
} ,
2020-07-24 11:20:13 +01:00
. AddressOf = > {
const prefix_op = node . cast ( ast . Node . SimplePrefixOp ) . ? ;
const rhs_type = ( try resolveTypeOfNodeInternal ( store , arena , . {
. node = prefix_op . rhs ,
. handle = handle ,
} , bound_type_params ) ) orelse return null ;
const rhs_node = switch ( rhs_type . type . data ) {
. other = > | n | n ,
else = > return null ,
} ;
return TypeWithHandle {
. type = . { . data = . { . pointer = rhs_node } , . is_type_val = false } ,
. handle = rhs_type . handle ,
} ;
} ,
2020-05-14 00:10:41 +01:00
. BuiltinCall = > {
2020-07-16 15:56:22 +01:00
const builtin_call = node . castTag ( . BuiltinCall ) . ? ;
2020-06-10 19:24:17 +01:00
const call_name = handle . tree . tokenSlice ( builtin_call . builtin_token ) ;
2020-05-19 16:53:01 +01:00
if ( std . mem . eql ( u8 , call_name , " @This " ) ) {
2020-05-23 23:21:02 +01:00
if ( builtin_call . params_len ! = 0 ) return null ;
2020-06-10 19:24:17 +01:00
return innermostContainer ( handle , handle . tree . token_locs [ builtin_call . firstToken ( ) ] . start ) ;
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 ) ) {
if ( builtin_call . params_len < 1 ) return null ;
2020-06-17 03:12:12 +01:00
return ( ( try resolveTypeOfNodeInternal ( store , arena , . {
2020-06-12 12:56:46 +01:00
. node = builtin_call . paramsConst ( ) [ 0 ] ,
. 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 " ) ) {
if ( builtin_call . params_len < 1 ) return null ;
var resolved_type = ( try resolveTypeOfNodeInternal ( store , arena , . {
. node = builtin_call . paramsConst ( ) [ 0 ] ,
. 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 ;
2020-06-08 17:07:16 +01:00
if ( builtin_call . params_len < 1 ) return null ;
2020-05-14 00:10:41 +01:00
2020-05-23 23:21:02 +01:00
const import_param = builtin_call . paramsConst ( ) [ 0 ] ;
2020-07-16 15:56:22 +01:00
if ( import_param . tag ! = . StringLiteral ) return null ;
2020-05-14 00:10:41 +01:00
2020-07-16 15:56:22 +01:00
const import_str = handle . tree . tokenSlice ( import_param . castTag ( . StringLiteral ) . ? . token ) ;
2020-06-10 19:24:17 +01:00
const new_handle = ( store . resolveImport ( handle , import_str [ 1 . . import_str . len - 1 ] ) catch | err | block : {
2020-06-26 12:29:59 +01:00
std . log . debug ( . analysis , " Error {} while processing import {} \n " , . { err , import_str } ) ;
2020-06-10 19:24:17 +01:00
return null ;
} ) orelse return null ;
2020-06-17 03:12:12 +01:00
return TypeWithHandle . typeVal ( . { . node = & new_handle . tree . root_node . base , . handle = new_handle } ) ;
2020-05-14 00:10:41 +01:00
} ,
2020-05-24 13:24:18 +01:00
. ContainerDecl = > {
2020-07-16 15:56:22 +01:00
const container = node . castTag ( . ContainerDecl ) . ? ;
2020-06-10 19:24:17 +01:00
const kind = handle . tree . token_ids [ container . kind_token ] ;
2020-06-17 03:12:12 +01:00
return TypeWithHandle . typeVal ( node_handle ) ;
} ,
2020-06-17 13:07:21 +01:00
. FnProto = > {
// This is a function type
2020-07-23 18:30:03 +01:00
if ( ! node . castTag ( . FnProto ) . ? . trailer_flags . has ( " name_token " ) ) {
2020-06-17 13:07:21 +01:00
return TypeWithHandle . typeVal ( node_handle ) ;
}
return TypeWithHandle {
. type = . { . data = . { . other = node } , . is_type_val = false } ,
. handle = handle ,
} ;
} ,
. MultilineStringLiteral , . StringLiteral = > 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
} ,
2020-06-26 12:29:59 +01:00
else = > { } , //std.log.debug(.analysis, "Type resolution case not implemented; {}\n", .{node.id}),
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 ) {
2020-07-24 11:20:13 +01:00
pointer : * ast . Node ,
2020-06-17 03:12:12 +01:00
slice : * ast . Node ,
error_union : * ast . Node ,
other : * ast . Node ,
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 ,
fn typeVal ( node_handle : NodeWithHandle ) TypeWithHandle {
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 ) {
2020-07-16 15:56:22 +01:00
. other = > | n | return n . tag = = . Root ,
2020-06-17 21:36:40 +01:00
else = > return false ,
}
}
fn isContainer ( self : TypeWithHandle , container_kind_tok : std . zig . Token . Id ) bool {
switch ( self . type . data ) {
. other = > | n | {
2020-07-16 15:56:22 +01:00
if ( n . castTag ( . ContainerDecl ) ) | cont | {
2020-06-17 21:36:40 +01:00
return self . handle . tree . token_ids [ cont . kind_token ] = = container_kind_tok ;
}
return false ;
} ,
else = > return false ,
}
}
pub fn isStructType ( self : TypeWithHandle ) bool {
return self . isContainer ( . Keyword_struct ) or self . isRoot ( ) ;
}
2020-06-27 18:45:58 +01:00
pub fn isNamespace ( self : TypeWithHandle ) bool {
if ( ! self . isStructType ( ) ) return false ;
var idx : usize = 0 ;
while ( self . type . data . other . iterate ( idx ) ) | child | : ( idx + = 1 ) {
2020-07-16 15:56:22 +01:00
if ( child . tag = = . ContainerField )
2020-06-27 18:45:58 +01:00
return false ;
}
return true ;
}
2020-06-17 21:36:40 +01:00
pub fn isEnumType ( self : TypeWithHandle ) bool {
return self . isContainer ( . Keyword_enum ) ;
}
pub fn isUnionType ( self : TypeWithHandle ) bool {
return self . isContainer ( . Keyword_union ) ;
}
2020-06-18 13:12:09 +01:00
pub fn isTypeFunc ( self : TypeWithHandle ) bool {
switch ( self . type . data ) {
. other = > | n | {
2020-07-16 15:56:22 +01:00
if ( n . castTag ( . FnProto ) ) | fn_proto | {
2020-06-18 13:12:09 +01:00
return isTypeFunction ( self . handle . tree , fn_proto ) ;
}
return false ;
2020-06-27 18:45:58 +01:00
} ,
else = > return false ,
}
}
pub fn isGenericFunc ( self : TypeWithHandle ) bool {
switch ( self . type . data ) {
. other = > | n | {
2020-07-16 15:56:22 +01:00
if ( n . castTag ( . FnProto ) ) | fn_proto | {
2020-06-27 18:45:58 +01:00
return isGenericFunction ( self . handle . tree , fn_proto ) ;
}
return false ;
2020-06-18 13:12:09 +01:00
} ,
else = > return false ,
}
}
pub fn isFunc ( self : TypeWithHandle ) bool {
switch ( self . type . data ) {
. other = > | n | {
2020-07-16 15:56:22 +01:00
return n . tag = = . FnProto ;
2020-06-18 13:12:09 +01:00
} ,
else = > return false ,
}
}
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 ) ;
}
2020-05-14 12:51:07 +01:00
fn maybeCollectImport ( tree : * ast . Tree , builtin_call : * ast . Node . BuiltinCall , arr : * std . ArrayList ( [ ] const u8 ) ) ! void {
if ( ! std . mem . eql ( u8 , tree . tokenSlice ( builtin_call . builtin_token ) , " @import " ) ) return ;
2020-05-23 23:21:02 +01:00
if ( builtin_call . params_len > 1 ) return ;
2020-05-14 12:51:07 +01:00
2020-05-23 23:21:02 +01:00
const import_param = builtin_call . paramsConst ( ) [ 0 ] ;
2020-07-16 15:56:22 +01:00
if ( import_param . tag ! = . StringLiteral ) return ;
2020-05-14 12:51:07 +01:00
2020-07-16 15:56:22 +01:00
const import_str = tree . tokenSlice ( import_param . castTag ( . StringLiteral ) . ? . token ) ;
2020-05-14 12:51:07 +01:00
try arr . append ( import_str [ 1 . . import_str . len - 1 ] ) ;
}
/// Collects all imports we can find into a slice of import paths (without quotes).
/// The import paths are valid as long as the tree is.
2020-05-18 13:14:16 +01:00
pub fn collectImports ( import_arr : * std . ArrayList ( [ ] const u8 ) , tree : * ast . Tree ) ! void {
2020-05-18 13:46:17 +01:00
// TODO: Currently only detects `const smth = @import("string literal")<.SomeThing>;`
2020-05-23 23:21:02 +01:00
for ( tree . root_node . decls ( ) ) | decl | {
2020-07-16 15:56:22 +01:00
if ( decl . tag ! = . VarDecl ) continue ;
const var_decl = decl . castTag ( . VarDecl ) . ? ;
2020-07-16 17:04:23 +01:00
if ( ! var_decl . trailer_flags . has ( " init_node " ) ) continue ;
2020-05-16 16:30:16 +01:00
2020-07-16 17:07:21 +01:00
switch ( var_decl . getTrailer ( " init_node " ) . ? . tag ) {
2020-05-14 12:51:07 +01:00
. BuiltinCall = > {
2020-07-16 20:02:30 +01:00
const builtin_call = var_decl . getTrailer ( " init_node " ) . ? . castTag ( . BuiltinCall ) . ? ;
2020-05-18 13:14:16 +01:00
try maybeCollectImport ( tree , builtin_call , import_arr ) ;
2020-05-14 12:51:07 +01:00
} ,
2020-07-16 20:02:30 +01:00
. Period = > {
const infix_op = var_decl . getTrailer ( " init_node " ) . ? . cast ( ast . Node . SimpleInfixOp ) . ? ;
2020-05-16 16:30:16 +01:00
2020-07-16 15:56:22 +01:00
if ( infix_op . lhs . tag ! = . BuiltinCall ) continue ;
try maybeCollectImport ( tree , infix_op . lhs . castTag ( . BuiltinCall ) . ? , import_arr ) ;
2020-05-14 12:51:07 +01:00
} ,
else = > { } ,
}
}
}
2020-06-10 17:54:01 +01:00
pub const NodeWithHandle = struct {
node : * ast . Node ,
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 ) ;
2020-05-13 14:03:33 +01:00
while ( true ) {
2020-05-26 23:45:18 +01:00
const tok = tokenizer . next ( ) ;
2020-07-16 16:05:56 +01:00
switch ( tok . id ) {
2020-07-07 21:26:12 +01:00
. Eof = > return FieldAccessReturn {
. original = current_type ,
. unwrapped = try resolveDerefType ( store , arena , current_type , & bound_type_params ) ,
} ,
2020-05-13 14:03:33 +01:00
. Identifier = > {
2020-06-17 03:12:12 +01:00
if ( try lookupSymbolGlobal ( store , arena , current_type . handle , tokenizer . buffer [ tok . loc . start . . tok . loc . end ] , source_index ) ) | child | {
current_type = ( try child . resolveType ( store , arena , & bound_type_params ) ) orelse return null ;
2020-05-13 14:03:33 +01:00
} else return null ;
} ,
. Period = > {
2020-05-26 23:45:18 +01:00
const after_period = tokenizer . next ( ) ;
2020-07-16 20:02:30 +01:00
switch ( after_period . id ) {
2020-07-07 21:26:12 +01:00
. Eof = > return FieldAccessReturn {
. original = current_type ,
. unwrapped = try resolveDerefType ( store , arena , current_type , & bound_type_params ) ,
} ,
2020-05-26 23:45:18 +01: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 | {
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
} ,
. QuestionMark = > {
2020-06-17 03:12:12 +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 = > {
2020-06-26 12:29:59 +01:00
std . log . debug ( . analysis , " Unrecognized token {} after period. \n " , . { after_period . id } ) ;
2020-05-26 23:45:18 +01:00
return null ;
} ,
2020-05-13 14:03:33 +01:00
}
} ,
2020-05-26 23:45:18 +01:00
. PeriodAsterisk = > {
2020-06-17 03:12:12 +01:00
current_type = ( try resolveDerefType ( store , arena , current_type , & bound_type_params ) ) orelse return null ;
2020-05-26 23:45:18 +01:00
} ,
2020-05-24 03:07:09 +01:00
. LParen = > {
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 ;
2020-07-16 15:56:22 +01:00
if ( current_type_node . castTag ( . FnProto ) ) | func | {
2020-06-17 03:12:12 +01:00
if ( try resolveReturnType ( store , arena , func , current_type . handle , & bound_type_params ) ) | ret | {
current_type = ret ;
// Skip to the right paren
var paren_count : usize = 1 ;
var next = tokenizer . next ( ) ;
while ( next . id ! = . Eof ) : ( next = tokenizer . next ( ) ) {
if ( next . id = = . RParen ) {
paren_count - = 1 ;
if ( paren_count = = 0 ) break ;
} else if ( next . id = = . LParen ) {
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
} ,
2020-05-26 23:45:18 +01:00
. LBracket = > {
2020-05-27 16:49:11 +01:00
var brack_count : usize = 1 ;
var next = tokenizer . next ( ) ;
var is_range = false ;
while ( next . id ! = . Eof ) : ( next = tokenizer . next ( ) ) {
if ( next . id = = . RBracket ) {
brack_count - = 1 ;
if ( brack_count = = 0 ) break ;
} else if ( next . id = = . LBracket ) {
brack_count + = 1 ;
} else if ( next . id = = . Ellipsis2 and brack_count = = 1 ) {
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 = > {
2020-06-26 12:29:59 +01:00
std . log . debug ( . analysis , " Unimplemented token: {} \n " , . { tok . id } ) ;
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
}
2020-05-14 17:23:26 +01:00
pub fn isNodePublic ( tree : * ast . Tree , node : * ast . Node ) bool {
2020-07-16 15:56:22 +01:00
switch ( node . tag ) {
2020-05-14 17:14:35 +01:00
. VarDecl = > {
2020-07-16 15:56:22 +01:00
const var_decl = node . castTag ( . VarDecl ) . ? ;
2020-07-16 17:04:23 +01:00
return var_decl . trailer_flags . has ( " visib_token " ) ;
2020-05-14 17:14:35 +01:00
} ,
. FnProto = > {
2020-07-16 15:56:22 +01:00
const func = node . castTag ( . FnProto ) . ? ;
2020-07-16 17:04:23 +01:00
return func . trailer_flags . has ( " visib_token " ) ;
2020-05-16 16:30:16 +01:00
} ,
2020-05-17 15:23:04 +01:00
else = > return true ,
2020-05-14 17:14:35 +01:00
}
}
2020-05-14 00:10:41 +01:00
pub fn nodeToString ( tree : * ast . Tree , node : * ast . Node ) ? [ ] const u8 {
2020-07-16 15:56:22 +01:00
switch ( node . tag ) {
2020-05-13 14:03:33 +01:00
. ContainerField = > {
2020-07-16 15:56:22 +01:00
const field = node . castTag ( . ContainerField ) . ? ;
2020-05-13 14:03:33 +01:00
return tree . tokenSlice ( field . name_token ) ;
} ,
2020-05-14 17:11:03 +01:00
. ErrorTag = > {
2020-07-16 15:56:22 +01:00
const tag = node . castTag ( . ErrorTag ) . ? ;
2020-05-14 17:11:03 +01:00
return tree . tokenSlice ( tag . name_token ) ;
} ,
2020-05-13 14:03:33 +01:00
. Identifier = > {
2020-07-16 15:56:22 +01:00
const field = node . castTag ( . Identifier ) . ? ;
2020-05-13 14:03:33 +01:00
return tree . tokenSlice ( field . token ) ;
2020-05-11 13:28:08 +01:00
} ,
2020-05-13 16:59:44 +01:00
. FnProto = > {
2020-07-16 15:56:22 +01:00
const func = node . castTag ( . FnProto ) . ? ;
2020-07-16 17:04:23 +01:00
if ( func . getTrailer ( " name_token " ) ) | name_token | {
2020-05-13 16:59:44 +01:00
return tree . tokenSlice ( name_token ) ;
}
} ,
2020-05-11 13:28:08 +01:00
else = > {
2020-07-16 15:56:22 +01:00
std . log . debug ( . analysis , " INVALID: {} \n " , . { node . tag } ) ;
2020-05-16 16:30:16 +01:00
} ,
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
2020-05-22 16:51:57 +01:00
fn nodeContainsSourceIndex ( tree : * ast . Tree , node : * ast . Node , source_index : usize ) bool {
2020-05-23 23:21:02 +01:00
const first_token = tree . token_locs [ node . firstToken ( ) ] ;
const last_token = tree . token_locs [ node . lastToken ( ) ] ;
2020-05-22 16:51:57 +01:00
return source_index > = first_token . start and source_index < = last_token . end ;
}
pub fn getImportStr ( tree : * ast . Tree , source_index : usize ) ? [ ] const u8 {
var node = & tree . root_node . base ;
2020-05-23 23:21:02 +01:00
2020-05-24 15:07:48 +01:00
var child_idx : usize = 0 ;
2020-05-25 22:37:18 +01:00
while ( node . iterate ( child_idx ) ) | child | {
2020-05-22 16:51:57 +01:00
if ( ! nodeContainsSourceIndex ( tree , child , source_index ) ) {
2020-05-25 22:37:18 +01:00
child_idx + = 1 ;
2020-05-22 16:51:57 +01:00
continue ;
}
2020-07-16 15:56:22 +01:00
if ( child . castTag ( . BuiltinCall ) ) | builtin_call | blk : {
2020-05-22 16:51:57 +01:00
const call_name = tree . tokenSlice ( builtin_call . builtin_token ) ;
if ( ! std . mem . eql ( u8 , call_name , " @import " ) ) break : blk ;
2020-05-23 23:21:02 +01:00
if ( builtin_call . params_len ! = 1 ) break : blk ;
2020-05-22 16:51:57 +01:00
2020-05-23 23:21:02 +01:00
const import_param = builtin_call . paramsConst ( ) [ 0 ] ;
2020-07-16 15:56:22 +01:00
const import_str_node = import_param . castTag ( . StringLiteral ) orelse break : blk ;
2020-05-22 16:51:57 +01:00
const import_str = tree . tokenSlice ( import_str_node . token ) ;
return import_str [ 1 . . import_str . len - 1 ] ;
}
node = child ;
2020-05-24 15:07:48 +01:00
child_idx = 0 ;
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 ;
pub fn documentPositionContext ( arena : * std . heap . ArenaAllocator , document : types . TextDocument , doc_position : DocumentPosition ) ! PositionContext {
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.
switch ( tok . id ) {
. Invalid , . Invalid_ampersands = > {
// 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 ;
} ,
. LineComment , . DocComment , . ContainerDocComment = > return . comment ,
. Eof = > break ,
else = > { } ,
}
// State changes
var curr_ctx = try peek ( & stack ) ;
switch ( tok . id ) {
2020-05-27 11:09:50 +01:00
. StringLiteral , . MultilineStringLiteralLine = > curr_ctx . ctx = . { . string_literal = tok . loc } ,
2020-05-26 23:45:18 +01:00
. 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 = > { } ,
} ,
. 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 = > { } ,
} ,
. Period , . PeriodAsterisk = > 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 ) ,
} ,
} ,
2020-06-15 02:39:50 +01:00
. Keyword_break , . Keyword_continue = > curr_ctx . ctx = . pre_label ,
2020-06-16 13:49:57 +01:00
. 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
} ,
2020-05-26 23:45:18 +01:00
. QuestionMark = > switch ( curr_ctx . ctx ) {
. field_access = > { } ,
else = > curr_ctx . ctx = . empty ,
} ,
. LParen = > try stack . append ( . { . ctx = . empty , . stack_id = . Paren } ) ,
. LBracket = > try stack . append ( . { . ctx = . empty , . stack_id = . Bracket } ) ,
. RParen = > {
_ = stack . pop ( ) ;
if ( curr_ctx . stack_id ! = . Paren ) {
( try peek ( & stack ) ) . ctx = . empty ;
}
} ,
. RBracket = > {
_ = stack . pop ( ) ;
if ( curr_ctx . stack_id ! = . Bracket ) {
( try peek ( & stack ) ) . ctx = . empty ;
}
} ,
2020-05-16 17:04:07 +01: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 : {
if ( stack . popOrNull ( ) ) | state | break : block state . ctx ;
break : block . empty ;
} ;
}
2020-05-28 01:39:36 +01:00
2020-07-07 09:57:02 +01:00
fn addOutlineNodes ( allocator : * std . mem . Allocator , tree : * ast . Tree , child : * ast . Node , context : * GetDocumentSymbolsContext ) anyerror ! void {
2020-07-16 15:56:22 +01:00
switch ( child . tag ) {
2020-07-24 08:33:13 +01:00
. StringLiteral ,
. IntegerLiteral ,
. BuiltinCall ,
. Call ,
. Identifier ,
. Add ,
. AddWrap ,
. ArrayCat ,
. ArrayMult ,
. Assign ,
. AssignBitAnd ,
. AssignBitOr ,
. AssignBitShiftLeft ,
. AssignBitShiftRight ,
. AssignBitXor ,
. AssignDiv ,
. AssignSub ,
. AssignSubWrap ,
. AssignMod ,
. AssignAdd ,
. AssignAddWrap ,
. AssignMul ,
. AssignMulWrap ,
. BangEqual ,
. BitAnd ,
. BitOr ,
. BitShiftLeft ,
. BitShiftRight ,
. BitXor ,
. BoolAnd ,
. BoolOr ,
. Div ,
. EqualEqual ,
. ErrorUnion ,
. GreaterOrEqual ,
. GreaterThan ,
. LessOrEqual ,
. LessThan ,
. MergeErrorSets ,
. Mod ,
. Mul ,
. MulWrap ,
. Period ,
. Range ,
. Sub ,
. SubWrap ,
. OrElse ,
. AddressOf ,
. Await ,
. BitNot ,
. BoolNot ,
. OptionalType ,
. Negation ,
. NegationWrap ,
. Resume ,
. Try ,
. ArrayType ,
. ArrayTypeSentinel ,
. PtrType ,
. SliceType ,
. Slice ,
. Deref ,
. UnwrapOptional ,
. ArrayAccess ,
. Return ,
. Break ,
. Continue ,
. ArrayInitializerDot ,
. SwitchElse ,
. SwitchCase ,
. For ,
. EnumLiteral ,
. PointerIndexPayload ,
. StructInitializerDot ,
. PointerPayload ,
. While ,
. Switch ,
. Else ,
. BoolLiteral ,
. NullLiteral ,
. Defer ,
. StructInitializer ,
. FieldInitializer ,
. If ,
. MultilineStringLiteral ,
. UndefinedLiteral ,
. AnyType ,
. Block ,
. ErrorSetDecl ,
= > return ,
2020-06-01 18:48:14 +01:00
2020-05-28 01:39:36 +01:00
. ContainerDecl = > {
2020-07-16 15:56:22 +01:00
const decl = child . castTag ( . ContainerDecl ) . ? ;
2020-05-28 01:39:36 +01:00
2020-05-28 13:41:40 +01:00
for ( decl . fieldsAndDecls ( ) ) | cchild |
2020-07-07 09:57:02 +01:00
try addOutlineNodes ( allocator , tree , cchild , context ) ;
2020-05-28 01:39:36 +01:00
return ;
} ,
2020-05-28 16:18:48 +01:00
else = > { } ,
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 ,
} ;
fn getDocumentSymbolsInternal ( allocator : * std . mem . Allocator , tree : * ast . Tree , node : * ast . Node , 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 ;
const start_loc = context . prev_loc . add ( try offsets . tokenRelativeLocation ( tree , context . prev_loc . offset , node . firstToken ( ) , context . encoding ) ) ;
const end_loc = start_loc . add ( try offsets . tokenRelativeLocation ( tree , start_loc . offset , node . lastToken ( ) , context . encoding ) ) ;
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
} ;
2020-07-07 09:57:02 +01:00
( try context . symbols . addOne ( ) ) . * = . {
. name = name ,
2020-07-16 15:56:22 +01:00
. kind = switch ( node . tag ) {
2020-05-28 01:39:36 +01:00
. FnProto = > . Function ,
. VarDecl = > . Variable ,
. ContainerField = > . 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 ,
} ;
2020-05-28 01:39:36 +01:00
var index : usize = 0 ;
while ( node . iterate ( index ) ) | child | : ( index + = 1 ) {
2020-07-07 09:57:02 +01:00
try addOutlineNodes ( allocator , tree , child , & child_context ) ;
2020-05-28 01:39:36 +01:00
}
break : ch children . items ;
} ,
} ;
}
2020-07-07 09:57:02 +01: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 . root_node . decls_len ) ;
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
for ( tree . root_node . decls ( ) ) | node | {
2020-07-07 09:57:02 +01:00
try getDocumentSymbolsInternal ( allocator , tree , node , & 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 ) {
ast_node : * ast . Node ,
param_decl : * ast . Node . FnProto . ParamDecl ,
pointer_payload : struct {
node : * ast . Node . PointerPayload ,
condition : * ast . Node ,
} ,
array_payload : struct {
identifier : * ast . Node ,
array_expr : * ast . Node ,
} ,
switch_payload : struct {
node : * ast . Node . PointerPayload ,
2020-06-23 11:32:37 +01:00
switch_expr : * ast . Node ,
2020-06-10 14:12:00 +01:00
items : [ ] const * ast . Node ,
} ,
2020-06-14 20:24:18 +01:00
label_decl : * ast . Node , // .id is While, For or Block (firstToken will be the label)
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 ;
return switch ( self . decl . * ) {
2020-06-27 13:29:45 +01:00
. ast_node = > | n | getDeclNameToken ( tree , n ) . ? ,
. param_decl = > | p | p . name_token . ? ,
. pointer_payload = > | pp | pp . node . value_symbol . firstToken ( ) ,
. array_payload = > | ap | ap . identifier . firstToken ( ) ,
. switch_payload = > | sp | sp . node . value_symbol . firstToken ( ) ,
. label_decl = > | ld | ld . firstToken ( ) ,
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 ;
2020-07-07 09:57:02 +01:00
return try offsets . tokenRelativeLocation ( tree , 0 , 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 {
2020-06-10 18:48:40 +01:00
return switch ( self . decl . * ) {
2020-06-12 12:56:46 +01:00
. ast_node = > | node | try resolveTypeOfNodeInternal ( store , arena , . { . node = node , . handle = self . handle } , bound_type_params ) ,
2020-06-10 17:54:01 +01:00
. param_decl = > | param_decl | switch ( param_decl . param_type ) {
2020-06-12 12:56:46 +01:00
. type_expr = > | type_node | {
if ( typeIsType ( self . handle . tree , type_node ) ) {
var bound_param_it = bound_type_params . iterator ( ) ;
while ( bound_param_it . next ( ) ) | entry | {
if ( entry . key = = param_decl ) return entry . value ;
}
return null ;
2020-07-16 15:56:22 +01:00
} else if ( type_node . castTag ( . Identifier ) ) | type_ident | {
2020-06-23 10:15:18 +01:00
if ( param_decl . name_token ) | name_tok | {
if ( std . mem . eql ( u8 , self . handle . tree . tokenSlice ( type_ident . firstToken ( ) ) , self . handle . tree . tokenSlice ( name_tok ) ) )
return null ;
}
2020-06-12 12:56:46 +01:00
}
2020-06-17 13:07:21 +01:00
return ( ( try resolveTypeOfNodeInternal (
store ,
arena ,
. { . node = type_node , . handle = self . handle } ,
bound_type_params ,
) ) orelse return null ) . instanceTypeVal ( ) ;
2020-06-12 12:56:46 +01:00
} ,
2020-06-10 17:54:01 +01:00
else = > null ,
} ,
. 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
) ,
. array_payload = > | pay | try resolveBracketAccessType (
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 . array_expr ,
. handle = self . handle ,
2020-06-12 12:56:46 +01:00
} , bound_type_params ) ) orelse return null ,
2020-06-10 17:54:01 +01:00
. Single ,
2020-06-12 12:56:46 +01:00
bound_type_params ,
2020-06-10 17:54:01 +01:00
) ,
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 ;
if ( ! switch_expr_type . isUnionType ( ) )
return null ;
2020-07-16 15:56:22 +01:00
if ( pay . items [ 0 ] . castTag ( . EnumLiteral ) ) | enum_lit | {
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 ;
2020-07-05 22:56:41 +01:00
if ( scope . decls . getEntry ( self . handle . tree . tokenSlice ( enum_lit . name ) ) ) | candidate | {
2020-06-23 11:32:37 +01:00
switch ( candidate . value ) {
. ast_node = > | node | {
2020-07-16 15:56:22 +01:00
if ( node . castTag ( . ContainerField ) ) | container_field | {
2020-06-23 11:32:37 +01:00
if ( container_field . type_expr ) | type_expr | {
return ( ( try resolveTypeOfNodeInternal (
store ,
arena ,
. { . node = type_expr , . handle = switch_expr_type . handle } ,
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 ;
2020-07-16 15:56:22 +01:00
if ( container . tag ! = . ContainerDecl and container . tag ! = . Root and container . tag ! = . ErrorSetDecl ) {
2020-06-11 00:40:11 +01:00
return null ;
}
// Find the container scope.
var container_scope : ? * Scope = null ;
for ( handle . document_scope . scopes ) | * scope | {
switch ( scope . * . data ) {
. container = > | node | if ( node = = container ) {
container_scope = scope ;
break ;
} ,
else = > { } ,
}
}
return container_scope ;
}
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 ,
2020-06-23 10:43:56 +01:00
use_trail : * std . ArrayList ( * ast . Node . Use ) ,
2020-06-11 00:40:11 +01:00
) error { OutOfMemory } ! void {
const container = container_handle . node ;
const handle = container_handle . handle ;
2020-07-16 15:56:22 +01:00
const is_enum = if ( container . castTag ( . ContainerDecl ) ) | cont_decl |
2020-06-18 13:43:03 +01:00
handle . tree . token_ids [ cont_decl . kind_token ] = = . Keyword_enum
else
false ;
2020-06-11 00:40:11 +01:00
if ( findContainerScope ( container_handle ) ) | container_scope | {
var decl_it = container_scope . decls . iterator ( ) ;
while ( decl_it . next ( ) ) | entry | {
2020-06-18 13:43:03 +01:00
switch ( entry . value ) {
. ast_node = > | node | {
2020-07-16 15:56:22 +01:00
if ( node . tag = = . ContainerField ) {
2020-06-18 13:43:03 +01:00
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
const decl = DeclWithHandle { . decl = & entry . value , . handle = handle } ;
if ( handle ! = orig_handle and ! decl . isPublic ( ) ) continue ;
try callback ( context , decl ) ;
}
for ( container_scope . uses ) | use | {
if ( handle ! = orig_handle and use . visib_token = = null ) continue ;
2020-06-23 10:43:56 +01:00
if ( std . mem . indexOfScalar ( * ast . Node . Use , use_trail . items , use ) ! = null ) continue ;
2020-06-23 11:32:37 +01:00
try use_trail . append ( use ) ;
2020-06-23 10:43:56 +01:00
2020-06-11 00:40:11 +01:00
const use_expr = ( try resolveTypeOfNode ( store , arena , . { . node = use . expr , . handle = handle } ) ) orelse continue ;
2020-06-17 03:12:12 +01:00
const use_expr_node = switch ( use_expr . type . data ) {
. other = > | n | n ,
else = > continue ,
} ;
2020-06-23 10:43:56 +01:00
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 {
var use_trail = std . ArrayList ( * ast . Node . Use ) . init ( & arena . allocator ) ;
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 ,
2020-06-23 10:43:56 +01:00
use_trail : * std . ArrayList ( * ast . Node . Use ) ,
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 | {
if ( source_index > = scope . range . start and source_index < scope . range . end ) {
var decl_it = scope . decls . iterator ( ) ;
while ( decl_it . next ( ) ) | entry | {
2020-07-16 15:56:22 +01:00
if ( entry . value = = . ast_node and entry . value . ast_node . tag = = . ContainerField ) 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
2020-06-10 17:01:44 +01:00
for ( scope . uses ) | use | {
2020-06-23 10:43:56 +01:00
if ( std . mem . indexOfScalar ( * ast . Node . Use , use_trail . items , use ) ! = null ) continue ;
try use_trail . append ( use ) ;
2020-06-11 00:40:11 +01:00
const use_expr = ( try resolveTypeOfNode ( store , arena , . { . node = use . expr , . handle = handle } ) ) orelse continue ;
2020-06-17 03:12:12 +01:00
const use_expr_node = switch ( use_expr . type . data ) {
. other = > | n | n ,
else = > continue ,
} ;
2020-06-23 10:43:56 +01:00
try iterateSymbolsContainerInternal ( store , arena , . { . node = use_expr_node , . handle = use_expr . handle } , handle , callback , context , false , use_trail ) ;
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 {
var use_trail = std . ArrayList ( * ast . Node . Use ) . init ( & arena . allocator ) ;
return try iterateSymbolsGlobalInternal ( store , arena , handle , source_index , callback , context , & use_trail ) ;
}
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 | {
if ( source_index > = scope . range . start and source_index < scope . range . end ) {
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 ,
uses : [ ] const * ast . Node . Use ,
symbol : [ ] const u8 ,
handle : * DocumentStore . Handle ,
2020-06-23 10:43:56 +01:00
use_trail : * std . ArrayList ( * ast . Node . Use ) ,
2020-06-11 00:40:11 +01:00
) error { OutOfMemory } ! ? DeclWithHandle {
for ( uses ) | use | {
2020-06-23 10:43:56 +01:00
if ( std . mem . indexOfScalar ( * ast . Node . Use , use_trail . items , use ) ! = null ) continue ;
try use_trail . append ( use ) ;
2020-06-11 00:40:11 +01:00
const use_expr = ( try resolveTypeOfNode ( store , arena , . { . node = use . expr , . handle = handle } ) ) orelse continue ;
2020-06-17 03:12:12 +01:00
const use_expr_node = switch ( use_expr . type . data ) {
. other = > | n | n ,
else = > continue ,
} ;
2020-06-23 10:43:56 +01:00
if ( try lookupSymbolContainerInternal ( store , arena , . { . node = use_expr_node , . handle = use_expr . handle } , symbol , false , use_trail ) ) | candidate | {
2020-06-11 00:40:11 +01:00
if ( candidate . handle ! = handle and ! candidate . isPublic ( ) ) {
continue ;
}
return candidate ;
}
}
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 ;
}
2020-06-23 10:43:56 +01:00
fn lookupSymbolGlobalInternal (
2020-06-11 00:40:11 +01:00
store : * DocumentStore ,
arena : * std . heap . ArenaAllocator ,
handle : * DocumentStore . Handle ,
symbol : [ ] const u8 ,
source_index : usize ,
2020-06-23 10:43:56 +01:00
use_trail : * std . ArrayList ( * ast . Node . Use ) ,
2020-06-11 00:40:11 +01:00
) error { OutOfMemory } ! ? DeclWithHandle {
2020-06-10 17:01:44 +01:00
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-10 17:01:44 +01:00
switch ( candidate . value ) {
. ast_node = > | node | {
2020-07-16 16:05:56 +01:00
if ( node . tag = = . ContainerField ) continue ;
2020-06-10 17:01:44 +01:00
} ,
2020-06-14 20:24:18 +01:00
. label_decl = > continue ,
2020-06-10 17:01:44 +01:00
else = > { } ,
}
return DeclWithHandle {
. decl = & candidate . value ,
. handle = handle ,
} ;
}
2020-06-23 10:43:56 +01:00
if ( try resolveUse ( store , arena , scope . uses , symbol , handle , use_trail ) ) | result | return result ;
2020-06-10 17:01:44 +01:00
}
2020-06-10 19:24:17 +01:00
if ( scope . range . start > source_index ) return null ;
2020-06-10 17:01:44 +01:00
}
return null ;
}
2020-06-23 10:43:56 +01:00
pub fn lookupSymbolGlobal (
store : * DocumentStore ,
arena : * std . heap . ArenaAllocator ,
handle : * DocumentStore . Handle ,
symbol : [ ] const u8 ,
source_index : usize ,
) error { OutOfMemory } ! ? DeclWithHandle {
var use_trail = std . ArrayList ( * ast . Node . Use ) . init ( & arena . allocator ) ;
return try lookupSymbolGlobalInternal ( store , arena , handle , symbol , source_index , & use_trail ) ;
}
fn lookupSymbolContainerInternal (
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-23 10:43:56 +01:00
use_trail : * std . ArrayList ( * ast . Node . Use ) ,
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 ;
2020-06-10 23:00:13 +01:00
2020-07-16 15:56:22 +01:00
const is_enum = if ( container . castTag ( . ContainerDecl ) ) | cont_decl |
2020-06-18 13:43:03 +01:00
handle . tree . token_ids [ cont_decl . kind_token ] = = . Keyword_enum
else
false ;
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 | {
2020-07-16 15:56:22 +01:00
if ( node . tag = = . ContainerField ) {
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
}
2020-06-23 10:43:56 +01:00
if ( try resolveUse ( store , arena , container_scope . uses , symbol , handle , use_trail ) ) | 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
}
2020-06-23 10:43:56 +01:00
pub fn lookupSymbolContainer (
store : * DocumentStore ,
arena : * std . heap . ArenaAllocator ,
container_handle : NodeWithHandle ,
symbol : [ ] const u8 ,
/// 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 ,
) error { OutOfMemory } ! ? DeclWithHandle {
var use_trail = std . ArrayList ( * ast . Node . Use ) . init ( & arena . allocator ) ;
return try lookupSymbolContainerInternal ( store , arena , container_handle , symbol , instance_access , & use_trail ) ;
}
2020-06-10 17:01:44 +01:00
pub const DocumentScope = struct {
2020-06-10 18:48:40 +01:00
scopes : [ ] Scope ,
2020-07-08 02:05:44 +01:00
error_completions : [ ] types . CompletionItem ,
enum_completions : [ ] types . CompletionItem ,
2020-06-10 14:12:00 +01:00
pub fn debugPrint ( self : DocumentScope ) void {
for ( self . scopes ) | scope | {
2020-06-26 12:29:59 +01:00
std . log . debug ( . analysis ,
2020-06-10 14:12:00 +01:00
\\--------------------------
\\Scope {}, range: [{}, {})
\\ {} usingnamespaces
\\Decls:
, . {
scope . data ,
scope . range . start ,
scope . range . end ,
scope . uses . len ,
} ) ;
var decl_it = scope . decls . iterator ( ) ;
var idx : usize = 0 ;
while ( decl_it . next ( ) ) | name_decl | : ( idx + = 1 ) {
2020-06-26 12:29:59 +01:00
if ( idx ! = 0 ) std . log . debug ( . analysis , " , " , . { } ) ;
std . log . debug ( . analysis , " {} " , . { name_decl . key } ) ;
2020-06-10 14:12:00 +01:00
}
2020-06-26 12:29:59 +01:00
std . log . debug ( . analysis , " \n -------------------------- \n " , . { } ) ;
2020-06-10 14:12:00 +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 ( ) ;
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 ) ;
2020-07-08 02:05:44 +01:00
for ( self . error_completions ) | item | if ( item . documentation ) | doc | allocator . free ( doc . value ) ;
allocator . free ( self . error_completions ) ;
for ( self . enum_completions ) | item | if ( item . documentation ) | doc | allocator . free ( doc . value ) ;
allocator . free ( self . enum_completions ) ;
2020-06-10 14:12:00 +01:00
}
} ;
pub const Scope = struct {
pub const Data = union ( enum ) {
2020-06-10 23:00:13 +01:00
container : * ast . Node , // .id is ContainerDecl or Root or ErrorSetDecl
2020-06-10 14:12:00 +01:00
function : * ast . Node , // .id is FnProto
block : * ast . Node , // .id is Block
other ,
} ;
range : SourceRange ,
decls : std . StringHashMap ( Declaration ) ,
2020-06-10 17:01:44 +01:00
tests : [ ] const * ast . Node ,
2020-06-10 14:12:00 +01:00
uses : [ ] const * ast . Node . Use ,
data : Data ,
} ;
pub fn makeDocumentScope ( allocator : * std . mem . Allocator , tree : * ast . Tree ) ! DocumentScope {
2020-07-08 02:05:44 +01:00
var scopes = std . ArrayListUnmanaged ( Scope ) { } ;
var error_completions = std . ArrayListUnmanaged ( types . CompletionItem ) { } ;
var enum_completions = std . ArrayListUnmanaged ( types . CompletionItem ) { } ;
errdefer {
scopes . deinit ( allocator ) ;
for ( error_completions . items ) | item | if ( item . documentation ) | doc | allocator . free ( doc . value ) ;
error_completions . deinit ( allocator ) ;
for ( enum_completions . items ) | item | if ( item . documentation ) | doc | allocator . free ( doc . value ) ;
enum_completions . deinit ( allocator ) ;
}
2020-06-10 14:12:00 +01:00
2020-07-08 02:05:44 +01:00
try makeScopeInternal ( allocator , & scopes , & error_completions , & enum_completions , tree , & tree . root_node . base ) ;
2020-06-10 14:12:00 +01:00
return DocumentScope {
2020-07-08 02:05:44 +01:00
. scopes = scopes . toOwnedSlice ( allocator ) ,
. error_completions = error_completions . toOwnedSlice ( allocator ) ,
. enum_completions = enum_completions . toOwnedSlice ( allocator ) ,
2020-06-10 14:12:00 +01:00
} ;
}
fn nodeSourceRange ( tree : * ast . Tree , node : * ast . Node ) SourceRange {
return SourceRange {
. start = tree . token_locs [ node . firstToken ( ) ] . start ,
. end = tree . token_locs [ node . lastToken ( ) ] . end ,
} ;
}
2020-06-10 20:05:11 +01:00
// TODO Possibly collect all imports to diff them on changes
// as well
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 ) ,
error_completions : * std . ArrayListUnmanaged ( types . CompletionItem ) ,
enum_completions : * std . ArrayListUnmanaged ( types . CompletionItem ) ,
2020-06-10 14:12:00 +01:00
tree : * ast . Tree ,
node : * ast . Node ,
) error { OutOfMemory } ! void {
2020-07-16 15:56:22 +01:00
if ( node . tag = = . Root or node . tag = = . ContainerDecl or node . tag = = . ErrorSetDecl ) {
const ast_decls = switch ( node . tag ) {
. ContainerDecl = > node . castTag ( . ContainerDecl ) . ? . fieldsAndDeclsConst ( ) ,
. Root = > node . castTag ( . Root ) . ? . declsConst ( ) ,
. ErrorSetDecl = > node . castTag ( . ErrorSetDecl ) . ? . declsConst ( ) ,
2020-06-10 14:12:00 +01:00
else = > unreachable ,
} ;
2020-07-08 02:05:44 +01:00
( try scopes . addOne ( allocator ) ) . * = . {
2020-06-10 14:12:00 +01:00
. range = nodeSourceRange ( tree , node ) ,
. decls = std . StringHashMap ( Declaration ) . init ( allocator ) ,
. uses = & [ 0 ] * ast . Node . Use { } ,
2020-06-10 17:01:44 +01:00
. tests = & [ 0 ] * ast . Node { } ,
2020-06-10 14:12:00 +01:00
. data = . { . container = node } ,
} ;
2020-06-11 09:21:08 +01:00
const scope_idx = scopes . items . len - 1 ;
2020-06-10 14:12:00 +01:00
var uses = std . ArrayList ( * ast . Node . Use ) . init ( allocator ) ;
2020-06-10 17:01:44 +01:00
var tests = std . ArrayList ( * ast . Node ) . init ( allocator ) ;
2020-06-10 14:12:00 +01:00
errdefer {
scopes . items [ scope_idx ] . decls . deinit ( ) ;
uses . deinit ( ) ;
2020-06-10 17:01:44 +01:00
tests . deinit ( ) ;
2020-06-10 14:12:00 +01:00
}
for ( ast_decls ) | decl | {
2020-07-16 15:56:22 +01:00
if ( decl . castTag ( . Use ) ) | use | {
2020-06-10 14:12:00 +01:00
try uses . append ( use ) ;
continue ;
}
2020-07-08 02:05:44 +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 ;
2020-07-16 15:56:22 +01:00
if ( decl . tag = = . TestDecl ) {
2020-06-10 17:01:44 +01:00
try tests . append ( decl ) ;
continue ;
}
2020-07-16 15:56:22 +01:00
if ( node . tag = = . ErrorSetDecl ) {
2020-07-08 02:05:44 +01:00
( try error_completions . addOne ( allocator ) ) . * = . {
. label = name ,
. kind = . Constant ,
. documentation = if ( try getDocComments ( allocator , tree , decl , . Markdown ) ) | docs |
. { . kind = . Markdown , . value = docs }
else
null ,
} ;
}
2020-07-16 15:56:22 +01:00
if ( decl . castTag ( . ContainerField ) ) | field | {
2020-07-08 02:05:44 +01:00
const empty_field = field . type_expr = = null and field . value_expr = = null ;
2020-07-16 15:56:22 +01:00
if ( empty_field and node . tag = = . Root ) {
2020-07-08 02:05:44 +01:00
continue ;
}
2020-07-16 15:56:22 +01:00
if ( node . castTag ( . ContainerDecl ) ) | container | {
2020-07-08 02:05:44 +01:00
const kind = tree . token_ids [ container . kind_token ] ;
2020-07-23 19:10:40 +01:00
if ( empty_field and ( kind = = . Keyword_struct or ( kind = = . Keyword_union and container . init_arg_expr = = . None ) ) ) {
2020-07-08 02:05:44 +01:00
continue ;
2020-06-11 09:12:31 +01:00
}
2020-07-08 02:05:44 +01:00
2020-07-08 02:11:52 +01:00
if ( ! std . mem . eql ( u8 , name , " _ " ) ) {
( try enum_completions . addOne ( 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-07-05 22:56:41 +01:00
if ( try scopes . items [ scope_idx ] . decls . fetchPut ( name , . { . ast_node = decl } ) ) | existing | {
2020-06-10 14:12:00 +01:00
// TODO Record a redefinition error.
}
}
2020-07-02 17:13:10 +01:00
scopes . items [ scope_idx ] . tests = tests . toOwnedSlice ( ) ;
2020-06-10 14:12:00 +01:00
scopes . items [ scope_idx ] . uses = uses . toOwnedSlice ( ) ;
return ;
}
2020-07-16 15:56:22 +01:00
switch ( node . tag ) {
2020-06-10 14:12:00 +01:00
. FnProto = > {
2020-07-16 15:56:22 +01:00
const func = node . castTag ( . FnProto ) . ? ;
2020-06-10 14:12:00 +01:00
2020-07-08 02:05:44 +01:00
( try scopes . addOne ( allocator ) ) . * = . {
2020-06-10 14:12:00 +01:00
. range = nodeSourceRange ( tree , node ) ,
. decls = std . StringHashMap ( Declaration ) . init ( allocator ) ,
. uses = & [ 0 ] * ast . Node . Use { } ,
2020-06-10 18:48:40 +01:00
. tests = & [ 0 ] * ast . Node { } ,
2020-06-10 14:12:00 +01:00
. data = . { . function = node } ,
} ;
var scope_idx = scopes . items . len - 1 ;
errdefer scopes . items [ scope_idx ] . decls . deinit ( ) ;
for ( func . params ( ) ) | * param | {
if ( param . name_token ) | name_tok | {
2020-07-05 22:56:41 +01:00
if ( try scopes . items [ scope_idx ] . decls . fetchPut ( tree . tokenSlice ( name_tok ) , . { . param_decl = param } ) ) | existing | {
2020-06-10 14:12:00 +01:00
// TODO Record a redefinition error
}
}
}
2020-07-16 17:07:21 +01:00
if ( func . getTrailer ( " body_node " ) ) | body | {
2020-07-08 02:05:44 +01:00
try makeScopeInternal ( allocator , scopes , error_completions , enum_completions , tree , body ) ;
2020-06-10 14:12:00 +01:00
}
return ;
} ,
. TestDecl = > {
2020-07-16 15:56:22 +01:00
return try makeScopeInternal ( allocator , scopes , error_completions , enum_completions , tree , node . castTag ( . TestDecl ) . ? . body_node ) ;
2020-06-10 14:12:00 +01:00
} ,
. Block = > {
2020-07-16 15:56:22 +01:00
const block = node . castTag ( . Block ) . ? ;
2020-06-14 20:24:18 +01:00
if ( block . label ) | label | {
std . debug . assert ( tree . token_ids [ 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 = . {
. start = tree . token_locs [ block . lbrace ] . start ,
. end = tree . token_locs [ block . rbrace ] . end ,
} ,
. decls = std . StringHashMap ( Declaration ) . init ( allocator ) ,
. uses = & [ 0 ] * ast . Node . Use { } ,
. tests = & [ 0 ] * ast . Node { } ,
. data = . other ,
} ;
errdefer scope . decls . deinit ( ) ;
try scope . decls . putNoClobber ( tree . tokenSlice ( label ) , . {
. label_decl = node ,
} ) ;
}
2020-07-08 02:05:44 +01:00
( try scopes . addOne ( allocator ) ) . * = . {
2020-06-10 14:12:00 +01:00
. range = nodeSourceRange ( tree , node ) ,
. decls = std . StringHashMap ( Declaration ) . init ( allocator ) ,
. uses = & [ 0 ] * ast . Node . Use { } ,
2020-06-10 18:48:40 +01:00
. tests = & [ 0 ] * ast . Node { } ,
2020-06-10 14:12:00 +01:00
. data = . { . block = node } ,
} ;
var scope_idx = scopes . items . len - 1 ;
var uses = std . ArrayList ( * ast . Node . Use ) . init ( allocator ) ;
errdefer {
scopes . items [ scope_idx ] . decls . deinit ( ) ;
uses . deinit ( ) ;
}
var child_idx : usize = 0 ;
while ( node . iterate ( child_idx ) ) | child_node | : ( child_idx + = 1 ) {
2020-07-16 15:56:22 +01:00
if ( child_node . castTag ( . Use ) ) | use | {
2020-06-10 14:12:00 +01:00
try uses . append ( use ) ;
continue ;
}
2020-07-08 02:05:44 +01:00
try makeScopeInternal ( allocator , scopes , error_completions , enum_completions , tree , child_node ) ;
2020-07-16 15:56:22 +01:00
if ( child_node . castTag ( . VarDecl ) ) | var_decl | {
2020-06-10 14:12:00 +01:00
const name = tree . tokenSlice ( var_decl . name_token ) ;
2020-07-05 22:56:41 +01:00
if ( try scopes . items [ scope_idx ] . decls . fetchPut ( name , . { . ast_node = child_node } ) ) | existing | {
2020-06-10 14:12:00 +01:00
// TODO Record a redefinition error.
}
}
}
scopes . items [ scope_idx ] . uses = uses . toOwnedSlice ( ) ;
return ;
} ,
. Comptime = > {
2020-07-16 15:56:22 +01:00
return try makeScopeInternal ( allocator , scopes , error_completions , enum_completions , tree , node . castTag ( . Comptime ) . ? . expr ) ;
2020-06-10 14:12:00 +01:00
} ,
. If = > {
2020-07-16 15:56:22 +01:00
const if_node = node . castTag ( . If ) . ? ;
2020-06-10 14:12:00 +01:00
if ( if_node . payload ) | payload | {
2020-07-16 15:56:22 +01:00
std . debug . assert ( payload . tag = = . PointerPayload ) ;
2020-07-08 02:05:44 +01:00
var scope = try scopes . addOne ( allocator ) ;
2020-06-10 14:12:00 +01:00
scope . * = . {
. range = . {
. start = tree . token_locs [ payload . firstToken ( ) ] . start ,
. end = tree . token_locs [ if_node . body . lastToken ( ) ] . end ,
} ,
. decls = std . StringHashMap ( Declaration ) . init ( allocator ) ,
. uses = & [ 0 ] * ast . Node . Use { } ,
2020-06-10 18:48:40 +01:00
. tests = & [ 0 ] * ast . Node { } ,
2020-06-10 14:12:00 +01:00
. data = . other ,
} ;
errdefer scope . decls . deinit ( ) ;
2020-07-16 15:56:22 +01:00
const ptr_payload = payload . castTag ( . PointerPayload ) . ? ;
std . debug . assert ( ptr_payload . value_symbol . tag = = . Identifier ) ;
2020-06-10 14:12:00 +01:00
const name = tree . tokenSlice ( ptr_payload . value_symbol . firstToken ( ) ) ;
try scope . decls . putNoClobber ( name , . {
. pointer_payload = . {
. node = ptr_payload ,
. condition = if_node . condition ,
} ,
} ) ;
}
2020-07-08 02:05:44 +01:00
try makeScopeInternal ( allocator , scopes , error_completions , enum_completions , tree , if_node . body ) ;
2020-06-10 14:12:00 +01:00
if ( if_node . @ " else " ) | else_node | {
if ( else_node . payload ) | payload | {
2020-07-16 15:56:22 +01:00
std . debug . assert ( payload . tag = = . 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 = . {
. start = tree . token_locs [ payload . firstToken ( ) ] . start ,
. end = tree . token_locs [ else_node . body . lastToken ( ) ] . end ,
} ,
. decls = std . StringHashMap ( Declaration ) . init ( allocator ) ,
. uses = & [ 0 ] * ast . Node . Use { } ,
2020-06-10 18:48:40 +01:00
. tests = & [ 0 ] * ast . Node { } ,
2020-06-10 14:12:00 +01:00
. data = . other ,
} ;
errdefer scope . decls . deinit ( ) ;
2020-07-16 15:56:22 +01:00
const err_payload = payload . castTag ( . Payload ) . ? ;
std . debug . assert ( err_payload . error_symbol . tag = = . Identifier ) ;
2020-06-10 14:12:00 +01:00
const name = tree . tokenSlice ( err_payload . error_symbol . firstToken ( ) ) ;
try scope . decls . putNoClobber ( name , . { . ast_node = payload } ) ;
}
2020-07-08 02:05:44 +01:00
try makeScopeInternal ( allocator , scopes , error_completions , enum_completions , tree , else_node . body ) ;
2020-06-10 14:12:00 +01:00
}
} ,
. While = > {
2020-07-16 15:56:22 +01:00
const while_node = node . castTag ( . While ) . ? ;
2020-06-14 20:24:18 +01:00
if ( while_node . label ) | label | {
std . debug . assert ( tree . token_ids [ 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 = . {
. start = tree . token_locs [ while_node . while_token ] . start ,
. end = tree . token_locs [ while_node . lastToken ( ) ] . end ,
} ,
. decls = std . StringHashMap ( Declaration ) . init ( allocator ) ,
. uses = & [ 0 ] * ast . Node . Use { } ,
. tests = & [ 0 ] * ast . Node { } ,
. data = . other ,
} ;
errdefer scope . decls . deinit ( ) ;
try scope . decls . putNoClobber ( tree . tokenSlice ( label ) , . {
. label_decl = node ,
} ) ;
}
2020-06-10 14:12:00 +01:00
if ( while_node . payload ) | payload | {
2020-07-16 15:56:22 +01:00
std . debug . assert ( payload . tag = = . PointerPayload ) ;
2020-07-08 02:05:44 +01:00
var scope = try scopes . addOne ( allocator ) ;
2020-06-10 14:12:00 +01:00
scope . * = . {
. range = . {
. start = tree . token_locs [ payload . firstToken ( ) ] . start ,
. end = tree . token_locs [ while_node . body . lastToken ( ) ] . end ,
} ,
. decls = std . StringHashMap ( Declaration ) . init ( allocator ) ,
. uses = & [ 0 ] * ast . Node . Use { } ,
2020-06-10 18:48:40 +01:00
. tests = & [ 0 ] * ast . Node { } ,
2020-06-10 14:12:00 +01:00
. data = . other ,
} ;
errdefer scope . decls . deinit ( ) ;
2020-07-16 15:56:22 +01:00
const ptr_payload = payload . castTag ( . PointerPayload ) . ? ;
std . debug . assert ( ptr_payload . value_symbol . tag = = . Identifier ) ;
2020-06-10 14:12:00 +01:00
const name = tree . tokenSlice ( ptr_payload . value_symbol . firstToken ( ) ) ;
try scope . decls . putNoClobber ( name , . {
. pointer_payload = . {
. node = ptr_payload ,
. condition = while_node . condition ,
} ,
} ) ;
}
2020-07-08 02:05:44 +01:00
try makeScopeInternal ( allocator , scopes , error_completions , enum_completions , tree , while_node . body ) ;
2020-06-10 14:12:00 +01:00
if ( while_node . @ " else " ) | else_node | {
if ( else_node . payload ) | payload | {
2020-07-16 15:56:22 +01:00
std . debug . assert ( payload . tag = = . 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 = . {
. start = tree . token_locs [ payload . firstToken ( ) ] . start ,
. end = tree . token_locs [ else_node . body . lastToken ( ) ] . end ,
} ,
. decls = std . StringHashMap ( Declaration ) . init ( allocator ) ,
. uses = & [ 0 ] * ast . Node . Use { } ,
2020-06-10 18:48:40 +01:00
. tests = & [ 0 ] * ast . Node { } ,
2020-06-10 14:12:00 +01:00
. data = . other ,
} ;
errdefer scope . decls . deinit ( ) ;
2020-07-16 15:56:22 +01:00
const err_payload = payload . castTag ( . Payload ) . ? ;
std . debug . assert ( err_payload . error_symbol . tag = = . Identifier ) ;
2020-06-10 14:12:00 +01:00
const name = tree . tokenSlice ( err_payload . error_symbol . firstToken ( ) ) ;
try scope . decls . putNoClobber ( name , . { . ast_node = payload } ) ;
}
2020-07-08 02:05:44 +01:00
try makeScopeInternal ( allocator , scopes , error_completions , enum_completions , tree , else_node . body ) ;
2020-06-10 14:12:00 +01:00
}
} ,
. For = > {
2020-07-16 15:56:22 +01:00
const for_node = node . castTag ( . For ) . ? ;
2020-06-14 20:24:18 +01:00
if ( for_node . label ) | label | {
std . debug . assert ( tree . token_ids [ 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 = . {
. start = tree . token_locs [ for_node . for_token ] . start ,
. end = tree . token_locs [ for_node . lastToken ( ) ] . end ,
} ,
. decls = std . StringHashMap ( Declaration ) . init ( allocator ) ,
. uses = & [ 0 ] * ast . Node . Use { } ,
. tests = & [ 0 ] * ast . Node { } ,
. data = . other ,
} ;
errdefer scope . decls . deinit ( ) ;
try scope . decls . putNoClobber ( tree . tokenSlice ( label ) , . {
. label_decl = node ,
} ) ;
}
2020-07-16 15:56:22 +01:00
std . debug . assert ( for_node . payload . tag = = . PointerIndexPayload ) ;
const ptr_idx_payload = for_node . payload . castTag ( . PointerIndexPayload ) . ? ;
std . debug . assert ( ptr_idx_payload . value_symbol . tag = = . Identifier ) ;
2020-06-10 14:12:00 +01:00
2020-07-08 02:05:44 +01:00
var scope = try scopes . addOne ( allocator ) ;
2020-06-10 14:12:00 +01:00
scope . * = . {
. range = . {
. start = tree . token_locs [ ptr_idx_payload . firstToken ( ) ] . start ,
. end = tree . token_locs [ for_node . body . lastToken ( ) ] . end ,
} ,
. decls = std . StringHashMap ( Declaration ) . init ( allocator ) ,
. uses = & [ 0 ] * ast . Node . Use { } ,
2020-06-10 18:48:40 +01:00
. tests = & [ 0 ] * ast . Node { } ,
2020-06-10 14:12:00 +01:00
. data = . other ,
} ;
errdefer scope . decls . deinit ( ) ;
const value_name = tree . tokenSlice ( ptr_idx_payload . value_symbol . firstToken ( ) ) ;
try scope . decls . putNoClobber ( value_name , . {
. array_payload = . {
. identifier = ptr_idx_payload . value_symbol ,
. array_expr = for_node . array_expr ,
} ,
} ) ;
if ( ptr_idx_payload . index_symbol ) | index_symbol | {
2020-07-16 15:56:22 +01:00
std . debug . assert ( index_symbol . tag = = . Identifier ) ;
2020-06-10 14:12:00 +01:00
const index_name = tree . tokenSlice ( index_symbol . firstToken ( ) ) ;
2020-07-05 22:56:41 +01:00
if ( try scope . decls . fetchPut ( index_name , . { . ast_node = index_symbol } ) ) | existing | {
2020-06-10 14:12:00 +01:00
// TODO Record a redefinition error
}
}
2020-07-08 02:05:44 +01:00
try makeScopeInternal ( allocator , scopes , error_completions , enum_completions , tree , for_node . body ) ;
2020-06-10 14:12:00 +01:00
if ( for_node . @ " else " ) | else_node | {
std . debug . assert ( else_node . payload = = null ) ;
2020-07-08 02:05:44 +01:00
try makeScopeInternal ( allocator , scopes , error_completions , enum_completions , tree , else_node . body ) ;
2020-06-10 14:12:00 +01:00
}
} ,
. Switch = > {
2020-07-16 15:56:22 +01:00
const switch_node = node . castTag ( . Switch ) . ? ;
2020-06-10 14:12:00 +01:00
for ( switch_node . casesConst ( ) ) | case | {
2020-07-16 15:56:22 +01:00
if ( case . * . castTag ( . SwitchCase ) ) | case_node | {
2020-06-10 14:12:00 +01:00
if ( case_node . payload ) | payload | {
2020-07-16 15:56:22 +01:00
std . debug . assert ( payload . tag = = . PointerPayload ) ;
2020-07-08 02:05:44 +01:00
var scope = try scopes . addOne ( allocator ) ;
2020-06-10 14:12:00 +01:00
scope . * = . {
. range = . {
. start = tree . token_locs [ payload . firstToken ( ) ] . start ,
. end = tree . token_locs [ case_node . expr . lastToken ( ) ] . end ,
} ,
. decls = std . StringHashMap ( Declaration ) . init ( allocator ) ,
. uses = & [ 0 ] * ast . Node . Use { } ,
2020-06-10 18:48:40 +01:00
. tests = & [ 0 ] * ast . Node { } ,
2020-06-10 14:12:00 +01:00
. data = . other ,
} ;
errdefer scope . decls . deinit ( ) ;
2020-07-16 15:56:22 +01:00
const ptr_payload = payload . castTag ( . PointerPayload ) . ? ;
std . debug . assert ( ptr_payload . value_symbol . tag = = . Identifier ) ;
2020-06-10 14:12:00 +01:00
const name = tree . tokenSlice ( ptr_payload . value_symbol . firstToken ( ) ) ;
try scope . decls . putNoClobber ( name , . {
. switch_payload = . {
. node = ptr_payload ,
2020-06-23 11:32:37 +01:00
. switch_expr = switch_node . expr ,
2020-06-10 14:12:00 +01:00
. items = case_node . itemsConst ( ) ,
} ,
} ) ;
}
2020-07-08 02:05:44 +01:00
try makeScopeInternal ( allocator , scopes , error_completions , enum_completions , tree , case_node . expr ) ;
2020-06-10 14:12:00 +01:00
}
}
} ,
. VarDecl = > {
2020-07-16 15:56:22 +01:00
const var_decl = node . castTag ( . VarDecl ) . ? ;
2020-07-16 17:07:21 +01:00
if ( var_decl . getTrailer ( " type_node " ) ) | type_node | {
2020-07-08 02:05:44 +01:00
try makeScopeInternal ( allocator , scopes , error_completions , enum_completions , tree , type_node ) ;
2020-06-10 14:12:00 +01:00
}
2020-07-16 17:07:21 +01:00
if ( var_decl . getTrailer ( " init_node " ) ) | init_node | {
2020-07-08 02:05:44 +01:00
try makeScopeInternal ( allocator , scopes , error_completions , enum_completions , tree , init_node ) ;
2020-06-10 14:12:00 +01:00
}
} ,
else = > {
var child_idx : usize = 0 ;
while ( node . iterate ( child_idx ) ) | child_node | : ( child_idx + = 1 ) {
2020-07-08 02:05:44 +01:00
try makeScopeInternal ( allocator , scopes , error_completions , enum_completions , tree , child_node ) ;
2020-06-10 14:12:00 +01:00
}
} ,
}
}