From 4ce1cb5bf1dc507224792543d8e56e6ab431a2b5 Mon Sep 17 00:00:00 2001 From: Jon Parise Date: Mon, 23 Dec 2024 09:57:42 +0100 Subject: [PATCH 1/3] runtime(graphql): contribute vim-graphql to Vim core Contribute the core of my vim-graphql project (ftplugin, indent, syntax) to the Vim project. This replaces the basic ftplugin support that was already in the runtime with a more complete set of filetype settings. I can assume maintainership for all of these files. I'll continue to maintain the higher-level embedded filetype support separately (in vim-graphql) for now, because it's fairly complex, but we can consider integrating that code directly into vim later. runtime files use the MIT license. closes: #16273 Signed-off-by: Jon Parise Signed-off-by: Christian Brabandt --- .github/MAINTAINERS | 4 +- runtime/ftplugin/graphql.vim | 17 +++++-- runtime/indent/graphql.vim | 92 ++++++++++++++++++++++++++++++++++++ runtime/syntax/graphql.vim | 90 +++++++++++++++++++++++++++++++++++ 4 files changed, 198 insertions(+), 5 deletions(-) create mode 100644 runtime/indent/graphql.vim create mode 100644 runtime/syntax/graphql.vim diff --git a/.github/MAINTAINERS b/.github/MAINTAINERS index 28b754bbc903d3..236763dbbcf51a 100644 --- a/.github/MAINTAINERS +++ b/.github/MAINTAINERS @@ -181,7 +181,7 @@ runtime/ftplugin/go.vim @dbarnett runtime/ftplugin/goaccess.vim @meonkeys runtime/ftplugin/gomod.vim @yu-yk runtime/ftplugin/gprof.vim @dpelle -runtime/ftplugin/graphql.vim @ribru17 +runtime/ftplugin/graphql.vim @jparise runtime/ftplugin/gyp.vim @ObserverOfTime runtime/ftplugin/haml.vim @tpope runtime/ftplugin/hare.vim @selenebun @@ -339,6 +339,7 @@ runtime/indent/gitconfig.vim @tpope runtime/indent/gitolite.vim @sitaramc runtime/indent/glsl.vim @gpanders runtime/indent/go.vim @dbarnett +runtime/indent/graphql.vim @jparise runtime/indent/gyp.vim @ObserverOfTime runtime/indent/haml.vim @tpope runtime/indent/hare.vim @selenebun @@ -481,6 +482,7 @@ runtime/syntax/goaccess.vim @meonkeys runtime/syntax/godoc.vim @dbarnett runtime/syntax/gp.vim @KBelabas runtime/syntax/gprof.vim @dpelle +runtime/syntax/graphql.vim @jparise runtime/syntax/groff.vim @jmarshall runtime/syntax/gyp.vim @ObserverOfTime runtime/syntax/haml.vim @tpope diff --git a/runtime/ftplugin/graphql.vim b/runtime/ftplugin/graphql.vim index 56f6e36e20b630..1717ebf2ccc7ed 100644 --- a/runtime/ftplugin/graphql.vim +++ b/runtime/ftplugin/graphql.vim @@ -1,13 +1,22 @@ " Vim filetype plugin " Language: graphql -" Maintainer: Riley Bruins -" Last Change: 2024 May 18 +" Maintainer: Jon Parise +" Filenames: *.graphql *.graphqls *.gql +" URL: https://github.com/jparise/vim-graphql +" License: MIT +" Last Change: 2024 Dec 21 if exists('b:did_ftplugin') finish endif let b:did_ftplugin = 1 -setl comments=:# commentstring=#\ %s +setlocal comments=:# +setlocal commentstring=#\ %s +setlocal formatoptions-=t +setlocal iskeyword+=$,@-@ +setlocal softtabstop=2 +setlocal shiftwidth=2 +setlocal expandtab -let b:undo_ftplugin = 'setl com< cms<' +let b:undo_ftplugin = 'setlocal com< cms< fo< isk< sts< sw< et<' diff --git a/runtime/indent/graphql.vim b/runtime/indent/graphql.vim new file mode 100644 index 00000000000000..dc0769b9a8124d --- /dev/null +++ b/runtime/indent/graphql.vim @@ -0,0 +1,92 @@ +" Vim indent file +" Language: graphql +" Maintainer: Jon Parise +" Filenames: *.graphql *.graphqls *.gql +" URL: https://github.com/jparise/vim-graphql +" License: MIT +" Last Change: 2024 Dec 21 + +" Set our local options if indentation hasn't already been set up. +" This generally means we've been detected as the primary filetype. +if !exists('b:did_indent') + setlocal autoindent + setlocal nocindent + setlocal nolisp + setlocal nosmartindent + + setlocal indentexpr=GetGraphQLIndent() + setlocal indentkeys=0{,0},0),0[,0],0#,!^F,o,O + + let b:did_indent = 1 +endif + +" If our indentation function already exists, we have nothing more to do. +if exists('*GetGraphQLIndent') + finish +endif + +let s:cpo_save = &cpoptions +set cpoptions&vim + +" searchpair() skip expression that matches in comments and strings. +let s:pair_skip_expr = + \ 'synIDattr(synID(line("."), col("."), 0), "name") =~? "comment\\|string"' + +" Check if the character at lnum:col is inside a string. +function s:InString(lnum, col) + return synIDattr(synID(a:lnum, a:col, 1), 'name') ==# 'graphqlString' +endfunction + +function GetGraphQLIndent() + " If this is the first non-blank line, we have nothing more to do because + " all of our indentation rules are based on matching against earlier lines. + let l:prevlnum = prevnonblank(v:lnum - 1) + if l:prevlnum == 0 + return 0 + endif + + " If the previous line isn't GraphQL, assume we're part of a template + " string and indent this new line within it. + let l:stack = map(synstack(l:prevlnum, 1), "synIDattr(v:val, 'name')") + if get(l:stack, -1) !~# '^graphql' + return indent(l:prevlnum) + shiftwidth() + endif + + let l:line = getline(v:lnum) + + " If this line contains just a closing bracket, find its matching opening + " bracket and indent the closing bracket to match. + let l:col = matchend(l:line, '^\s*[]})]') + if l:col > 0 && !s:InString(v:lnum, l:col) + call cursor(v:lnum, l:col) + + let l:bracket = l:line[l:col - 1] + if l:bracket ==# '}' + let l:matched = searchpair('{', '', '}', 'bW', s:pair_skip_expr) + elseif l:bracket ==# ']' + let l:matched = searchpair('\[', '', '\]', 'bW', s:pair_skip_expr) + elseif l:bracket ==# ')' + let l:matched = searchpair('(', '', ')', 'bW', s:pair_skip_expr) + else + let l:matched = -1 + endif + + return l:matched > 0 ? indent(l:matched) : virtcol('.') - 1 + endif + + " If we're inside of a multiline string, continue with the same indentation. + if s:InString(v:lnum, matchend(l:line, '^\s*') + 1) + return indent(v:lnum) + endif + + " If the previous line ended with an opening bracket, indent this line. + if getline(l:prevlnum) =~# '\%(#.*\)\@ +" Filenames: *.graphql *.graphqls *.gql +" URL: https://github.com/jparise/vim-graphql +" License: MIT +" Last Change: 2024 Dec 21 + +if !exists('main_syntax') + if exists('b:current_syntax') + finish + endif + let main_syntax = 'graphql' +endif + +syn case match + +syn match graphqlComment "#.*$" contains=@Spell + +syn match graphqlOperator "=" display +syn match graphqlOperator "!" display +syn match graphqlOperator "|" display +syn match graphqlOperator "&" display +syn match graphqlOperator "\M..." display + +syn keyword graphqlBoolean true false +syn keyword graphqlNull null +syn match graphqlNumber "-\=\<\%(0\|[1-9]\d*\)\%(\.\d\+\)\=\%([eE][-+]\=\d\+\)\=\>" display +syn region graphqlString start=+"+ skip=+\\\\\|\\"+ end=+"\|$+ +syn region graphqlString start=+"""+ skip=+\\"""+ end=+"""+ + +syn keyword graphqlKeyword repeatable nextgroup=graphqlKeyword skipwhite +syn keyword graphqlKeyword on nextgroup=graphqlType,graphqlDirectiveLocation skipwhite + +syn keyword graphqlStructure enum scalar type union nextgroup=graphqlType skipwhite +syn keyword graphqlStructure input interface subscription nextgroup=graphqlType skipwhite +syn keyword graphqlStructure implements nextgroup=graphqlType skipwhite +syn keyword graphqlStructure query mutation fragment nextgroup=graphqlName skipwhite +syn keyword graphqlStructure directive nextgroup=graphqlDirective skipwhite +syn keyword graphqlStructure extend nextgroup=graphqlStructure skipwhite +syn keyword graphqlStructure schema nextgroup=graphqlFold skipwhite + +syn match graphqlDirective "\<@\h\w*\>" display +syn match graphqlVariable "\<\$\h\w*\>" display +syn match graphqlName "\<\h\w*\>" display +syn match graphqlType "\<_*\u\w*\>" display + +" https://spec.graphql.org/October2021/#ExecutableDirectiveLocation +syn keyword graphqlDirectiveLocation QUERY MUTATION SUBSCRIPTION FIELD +syn keyword graphqlDirectiveLocation FRAGMENT_DEFINITION FRAGMENT_SPREAD +syn keyword graphqlDirectiveLocation INLINE_FRAGMENT VARIABLE_DEFINITION +" https://spec.graphql.org/October2021/#TypeSystemDirectiveLocation +syn keyword graphqlDirectiveLocation SCHEMA SCALAR OBJECT FIELD_DEFINITION +syn keyword graphqlDirectiveLocation ARGUMENT_DEFINITION INTERFACE UNION +syn keyword graphqlDirectiveLocation ENUM ENUM_VALUE INPUT_OBJECT +syn keyword graphqlDirectiveLocation INPUT_FIELD_DEFINITION + +syn keyword graphqlMetaFields __schema __type __typename + +syn region graphqlFold matchgroup=graphqlBraces start="{" end="}" transparent fold contains=ALLBUT,graphqlStructure +syn region graphqlList matchgroup=graphqlBraces start="\[" end="]" transparent contains=ALLBUT,graphqlDirective,graphqlStructure + +if main_syntax ==# 'graphql' + syn sync minlines=500 +endif + +hi def link graphqlComment Comment +hi def link graphqlOperator Operator + +hi def link graphqlBraces Delimiter + +hi def link graphqlBoolean Boolean +hi def link graphqlNull Keyword +hi def link graphqlNumber Number +hi def link graphqlString String + +hi def link graphqlDirective PreProc +hi def link graphqlDirectiveLocation Special +hi def link graphqlName Identifier +hi def link graphqlMetaFields Special +hi def link graphqlKeyword Keyword +hi def link graphqlStructure Structure +hi def link graphqlType Type +hi def link graphqlVariable Identifier + +let b:current_syntax = 'graphql' + +if main_syntax ==# 'graphql' + unlet main_syntax +endif From 08be9ddc8568918714e0e70347bd109c26b73db0 Mon Sep 17 00:00:00 2001 From: h-east Date: Mon, 23 Dec 2024 10:11:25 +0100 Subject: [PATCH 2/3] runtime(doc): move help tag E1182 closes: #16279 Signed-off-by: h-east Signed-off-by: Christian Brabandt --- runtime/doc/eval.txt | 4 ++-- runtime/doc/tags | 2 +- runtime/doc/vim9.txt | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index 6b264750428afc..af5b3a954432e1 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -1,4 +1,4 @@ -*eval.txt* For Vim version 9.1. Last change: 2024 Nov 02 +*eval.txt* For Vim version 9.1. Last change: 2024 Dec 23 VIM REFERENCE MANUAL by Bram Moolenaar @@ -797,7 +797,7 @@ length minus one is used: > Blob modification ~ - *blob-modification* *E1182* *E1184* + *blob-modification* *E1184* To change a specific byte of a blob use |:let| this way: > :let blob[4] = 0x44 diff --git a/runtime/doc/tags b/runtime/doc/tags index 56004e9c63483e..0f5bb0ee914b77 100644 --- a/runtime/doc/tags +++ b/runtime/doc/tags @@ -4292,7 +4292,7 @@ E1179 options.txt /*E1179* E118 eval.txt /*E118* E1180 vim9.txt /*E1180* E1181 vim9.txt /*E1181* -E1182 eval.txt /*E1182* +E1182 vim9.txt /*E1182* E1183 eval.txt /*E1183* E1184 eval.txt /*E1184* E1185 various.txt /*E1185* diff --git a/runtime/doc/vim9.txt b/runtime/doc/vim9.txt index 68e5d997ff91c9..a978ea36930faf 100644 --- a/runtime/doc/vim9.txt +++ b/runtime/doc/vim9.txt @@ -1,4 +1,4 @@ -*vim9.txt* For Vim version 9.1. Last change: 2024 May 31 +*vim9.txt* For Vim version 9.1. Last change: 2024 Dec 23 VIM REFERENCE MANUAL by Bram Moolenaar @@ -193,7 +193,7 @@ created yet. In this case you can call `execute()` to invoke it at runtime. > "closure". A `:def` function always aborts on an error (unless `:silent!` was used for the command or the error was caught a `:try` block), does not get a range passed, cannot be a "dict" function, and can always be a closure. - *vim9-no-dict-function* + *vim9-no-dict-function* *E1182* You can use a Vim9 Class (|Vim9-class|) instead of a "dict function". You can also pass the dictionary explicitly: > def DictFunc(self: dict, arg: string) From f07c10d7bb770547ab88cf479621b06a16c09b55 Mon Sep 17 00:00:00 2001 From: Yegappan Lakshmanan Date: Mon, 23 Dec 2024 10:15:08 +0100 Subject: [PATCH 3/3] patch 9.1.0955: Vim9: vim9compile.c can be further improved Problem: Vim9: vim9compile.c can be further improved Solution: refactor the compile_def_function (Yegappan Lakshmanan) closes: #16280 Signed-off-by: Yegappan Lakshmanan Signed-off-by: Christian Brabandt --- src/version.c | 2 + src/vim9compile.c | 395 +++++++++++++++++++++++++++++----------------- 2 files changed, 249 insertions(+), 148 deletions(-) diff --git a/src/version.c b/src/version.c index 41e6777d7523b6..d668ad77a1d313 100644 --- a/src/version.c +++ b/src/version.c @@ -704,6 +704,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 955, /**/ 954, /**/ diff --git a/src/vim9compile.c b/src/vim9compile.c index eb9ddafbbaabc9..c8409636041ee9 100644 --- a/src/vim9compile.c +++ b/src/vim9compile.c @@ -3827,6 +3827,36 @@ get_compile_type(ufunc_T *ufunc) return CT_NONE; } +/* + * Free the compiled instructions saved for a def function. This is used when + * compiling a def function and the function was compiled before. + * The index is reused. + */ + static void +clear_def_function(ufunc_T *ufunc, compiletype_T compile_type) +{ + isn_T *instr_dest = NULL; + dfunc_T *dfunc; + + dfunc = ((dfunc_T *)def_functions.ga_data) + ufunc->uf_dfunc_idx; + + switch (compile_type) + { + case CT_PROFILE: +#ifdef FEAT_PROFILE + instr_dest = dfunc->df_instr_prof; break; +#endif + case CT_NONE: instr_dest = dfunc->df_instr; break; + case CT_DEBUG: instr_dest = dfunc->df_instr_debug; break; + } + + if (instr_dest != NULL) + // Was compiled in this mode before: Free old instructions. + delete_def_function_contents(dfunc, FALSE); + + ga_clear_strings(&dfunc->df_var_names); + dfunc->df_defer_var_idx = 0; +} /* * Add a function to the list of :def functions. @@ -3861,6 +3891,60 @@ add_def_function(ufunc_T *ufunc) return OK; } + static int +compile_dfunc_ufunc_init( + ufunc_T *ufunc, + cctx_T *outer_cctx, + compiletype_T compile_type, + int *new_def_function) +{ + // When using a function that was compiled before: Free old instructions. + // The index is reused. Otherwise add a new entry in "def_functions". + if (ufunc->uf_dfunc_idx > 0) + clear_def_function(ufunc, compile_type); + else + { + if (add_def_function(ufunc) == FAIL) + return FAIL; + + *new_def_function = TRUE; + } + + if ((ufunc->uf_flags & FC_CLOSURE) && outer_cctx == NULL) + { + semsg(_(e_compiling_closure_without_context_str), + printable_func_name(ufunc)); + return FAIL; + } + + ufunc->uf_def_status = UF_COMPILING; + + return OK; +} + +/* + * Initialize the compilation context for compiling a def function. + */ + static void +compile_dfunc_cctx_init( + cctx_T *cctx, + cctx_T *outer_cctx, + ufunc_T *ufunc, + compiletype_T compile_type) +{ + CLEAR_FIELD(*cctx); + + cctx->ctx_compile_type = compile_type; + cctx->ctx_ufunc = ufunc; + cctx->ctx_lnum = -1; + cctx->ctx_outer = outer_cctx; + ga_init2(&cctx->ctx_locals, sizeof(lvar_T), 10); + // Each entry on the type stack consists of two type pointers. + ga_init2(&cctx->ctx_type_stack, sizeof(type2_T), 50); + cctx->ctx_type_list = &ufunc->uf_type_list; + ga_init2(&cctx->ctx_instr, sizeof(isn_T), 50); +} + /* * For an object constructor, generate instruction to setup "this" (the first * local variable) and to initialize the object variables. @@ -3964,8 +4048,8 @@ obj_method_prologue(ufunc_T *ufunc, cctx_T *cctx) static int compile_def_function_default_args( ufunc_T *ufunc, - cctx_T *cctx, - garray_T *instr) + garray_T *instr, + cctx_T *cctx) { int count = ufunc->uf_def_args.ga_len; int first_def_arg = ufunc->uf_args.ga_len - count; @@ -4036,11 +4120,11 @@ compile_def_function_default_args( */ static int compile_def_function_body( - cctx_T *cctx, int last_func_lnum, int check_return_type, garray_T *lines_to_free, - char **errormsg) + char **errormsg, + cctx_T *cctx) { char_u *line = NULL; char_u *p; @@ -4588,6 +4672,150 @@ compile_def_function_body( return OK; } +/* + * Returns TRUE if the end of a scope (if, while, for, block) is missing. + * Called after compiling a def function body. + */ + static int +compile_dfunc_scope_end_missing(cctx_T *cctx) +{ + if (cctx->ctx_scope == NULL) + return FALSE; + + if (cctx->ctx_scope->se_type == IF_SCOPE) + emsg(_(e_missing_endif)); + else if (cctx->ctx_scope->se_type == WHILE_SCOPE) + emsg(_(e_missing_endwhile)); + else if (cctx->ctx_scope->se_type == FOR_SCOPE) + emsg(_(e_missing_endfor)); + else + emsg(_(e_missing_rcurly)); + + return TRUE; +} + +/* + * When compiling a def function, if it doesn not have an explicit return + * statement, then generate a default return instruction. For an object + * constructor, return the object. + */ + static int +compile_dfunc_generate_default_return(ufunc_T *ufunc, cctx_T *cctx) +{ + // TODO: if a function ends in "throw" but there was a return elsewhere we + // should not assume the return type is "void". + if (cctx->ctx_had_return || cctx->ctx_had_throw) + return OK; + + if (ufunc->uf_ret_type->tt_type == VAR_UNKNOWN) + ufunc->uf_ret_type = &t_void; + else if (ufunc->uf_ret_type->tt_type != VAR_VOID + && !IS_CONSTRUCTOR_METHOD(ufunc)) + { + emsg(_(e_missing_return_statement)); + return FAIL; + } + + // Return void if there is no return at the end. + // For a constructor return the object. + if (IS_CONSTRUCTOR_METHOD(ufunc)) + { + generate_instr(cctx, ISN_RETURN_OBJECT); + ufunc->uf_ret_type = &ufunc->uf_class->class_object_type; + } + else + generate_instr(cctx, ISN_RETURN_VOID); + + return OK; +} + +/* + * Perform the chores after successfully compiling a def function. + */ + static void +compile_dfunc_epilogue( + cctx_T *outer_cctx, + ufunc_T *ufunc, + garray_T *instr, + cctx_T *cctx) +{ + dfunc_T *dfunc; + + dfunc = ((dfunc_T *)def_functions.ga_data) + ufunc->uf_dfunc_idx; + dfunc->df_deleted = FALSE; + dfunc->df_script_seq = current_sctx.sc_seq; + +#ifdef FEAT_PROFILE + if (cctx->ctx_compile_type == CT_PROFILE) + { + dfunc->df_instr_prof = instr->ga_data; + dfunc->df_instr_prof_count = instr->ga_len; + } + else +#endif + if (cctx->ctx_compile_type == CT_DEBUG) + { + dfunc->df_instr_debug = instr->ga_data; + dfunc->df_instr_debug_count = instr->ga_len; + } + else + { + dfunc->df_instr = instr->ga_data; + dfunc->df_instr_count = instr->ga_len; + } + dfunc->df_varcount = dfunc->df_var_names.ga_len; + dfunc->df_has_closure = cctx->ctx_has_closure; + + if (cctx->ctx_outer_used) + { + ufunc->uf_flags |= FC_CLOSURE; + if (outer_cctx != NULL) + ++outer_cctx->ctx_closure_count; + } + + ufunc->uf_def_status = UF_COMPILED; +} + +/* + * Perform the cleanup when a def function compilation fails. + */ + static void +compile_dfunc_ufunc_cleanup( + ufunc_T *ufunc, + garray_T *instr, + int new_def_function, + char *errormsg, + int did_emsg_before, + cctx_T *cctx) +{ + dfunc_T *dfunc; + + dfunc = ((dfunc_T *)def_functions.ga_data) + ufunc->uf_dfunc_idx; + + // Compiling aborted, free the generated instructions. + clear_instr_ga(instr); + VIM_CLEAR(dfunc->df_name); + ga_clear_strings(&dfunc->df_var_names); + + // If using the last entry in the table and it was added above, we + // might as well remove it. + if (!dfunc->df_deleted && new_def_function + && ufunc->uf_dfunc_idx == def_functions.ga_len - 1) + { + --def_functions.ga_len; + ufunc->uf_dfunc_idx = 0; + } + ufunc->uf_def_status = UF_COMPILE_ERROR; + + while (cctx->ctx_scope != NULL) + drop_scope(cctx); + + if (errormsg != NULL) + emsg(errormsg); + else if (did_emsg == did_emsg_before) + emsg(_(e_compiling_def_function_failed)); +} + /* * After ex_function() has collected all the function lines: parse and compile * the lines into instructions. @@ -4624,56 +4852,13 @@ compile_def_function( // allocated lines are freed at the end ga_init2(&lines_to_free, sizeof(char_u *), 50); - // When using a function that was compiled before: Free old instructions. - // The index is reused. Otherwise add a new entry in "def_functions". - if (ufunc->uf_dfunc_idx > 0) - { - dfunc_T *dfunc = ((dfunc_T *)def_functions.ga_data) - + ufunc->uf_dfunc_idx; - isn_T *instr_dest = NULL; - - switch (compile_type) - { - case CT_PROFILE: -#ifdef FEAT_PROFILE - instr_dest = dfunc->df_instr_prof; break; -#endif - case CT_NONE: instr_dest = dfunc->df_instr; break; - case CT_DEBUG: instr_dest = dfunc->df_instr_debug; break; - } - if (instr_dest != NULL) - // Was compiled in this mode before: Free old instructions. - delete_def_function_contents(dfunc, FALSE); - ga_clear_strings(&dfunc->df_var_names); - dfunc->df_defer_var_idx = 0; - } - else - { - if (add_def_function(ufunc) == FAIL) - return FAIL; - new_def_function = TRUE; - } - - if ((ufunc->uf_flags & FC_CLOSURE) && outer_cctx == NULL) - { - semsg(_(e_compiling_closure_without_context_str), - printable_func_name(ufunc)); + // Initialize the ufunc and the compilation context + if (compile_dfunc_ufunc_init(ufunc, outer_cctx, compile_type, + &new_def_function) == FAIL) return FAIL; - } - - ufunc->uf_def_status = UF_COMPILING; - CLEAR_FIELD(cctx); + compile_dfunc_cctx_init(&cctx, outer_cctx, ufunc, compile_type); - cctx.ctx_compile_type = compile_type; - cctx.ctx_ufunc = ufunc; - cctx.ctx_lnum = -1; - cctx.ctx_outer = outer_cctx; - ga_init2(&cctx.ctx_locals, sizeof(lvar_T), 10); - // Each entry on the type stack consists of two type pointers. - ga_init2(&cctx.ctx_type_stack, sizeof(type2_T), 50); - cctx.ctx_type_list = &ufunc->uf_type_list; - ga_init2(&cctx.ctx_instr, sizeof(isn_T), 50); instr = &cctx.ctx_instr; // Set the context to the function, it may be compiled when called from @@ -4691,6 +4876,7 @@ compile_def_function( estack_push_ufunc(ufunc, 1); estack_compiling = TRUE; + // Make sure arguments don't shadow variables in the context if (check_args_shadowing(ufunc, &cctx) == FAIL) goto erret; @@ -4701,7 +4887,7 @@ compile_def_function( goto erret; if (ufunc->uf_def_args.ga_len > 0) - if (compile_def_function_default_args(ufunc, &cctx, instr) == FAIL) + if (compile_def_function_default_args(ufunc, instr, &cctx) == FAIL) goto erret; ufunc->uf_args_visible = ufunc->uf_args.ga_len; @@ -4715,116 +4901,29 @@ compile_def_function( } // compile the function body - if (compile_def_function_body(&cctx, ufunc->uf_lines.ga_len, - check_return_type, &lines_to_free, &errormsg) == FAIL) + if (compile_def_function_body(ufunc->uf_lines.ga_len, check_return_type, + &lines_to_free, &errormsg, &cctx) == FAIL) goto erret; - if (cctx.ctx_scope != NULL) - { - if (cctx.ctx_scope->se_type == IF_SCOPE) - emsg(_(e_missing_endif)); - else if (cctx.ctx_scope->se_type == WHILE_SCOPE) - emsg(_(e_missing_endwhile)); - else if (cctx.ctx_scope->se_type == FOR_SCOPE) - emsg(_(e_missing_endfor)); - else - emsg(_(e_missing_rcurly)); + if (compile_dfunc_scope_end_missing(&cctx)) goto erret; - } - - // TODO: if a function ends in "throw" but there was a return elsewhere we - // should not assume the return type is "void". - if (!cctx.ctx_had_return && !cctx.ctx_had_throw) - { - if (ufunc->uf_ret_type->tt_type == VAR_UNKNOWN) - ufunc->uf_ret_type = &t_void; - else if (ufunc->uf_ret_type->tt_type != VAR_VOID - && !IS_CONSTRUCTOR_METHOD(ufunc)) - { - emsg(_(e_missing_return_statement)); - goto erret; - } - // Return void if there is no return at the end. - // For a constructor return the object. - if (IS_CONSTRUCTOR_METHOD(ufunc)) - { - generate_instr(&cctx, ISN_RETURN_OBJECT); - ufunc->uf_ret_type = &ufunc->uf_class->class_object_type; - } - else - generate_instr(&cctx, ISN_RETURN_VOID); - } + if (compile_dfunc_generate_default_return(ufunc, &cctx) == FAIL) + goto erret; // When compiled with ":silent!" and there was an error don't consider the // function compiled. if (emsg_silent == 0 || did_emsg_silent == did_emsg_silent_before) - { - dfunc_T *dfunc = ((dfunc_T *)def_functions.ga_data) - + ufunc->uf_dfunc_idx; - dfunc->df_deleted = FALSE; - dfunc->df_script_seq = current_sctx.sc_seq; -#ifdef FEAT_PROFILE - if (cctx.ctx_compile_type == CT_PROFILE) - { - dfunc->df_instr_prof = instr->ga_data; - dfunc->df_instr_prof_count = instr->ga_len; - } - else -#endif - if (cctx.ctx_compile_type == CT_DEBUG) - { - dfunc->df_instr_debug = instr->ga_data; - dfunc->df_instr_debug_count = instr->ga_len; - } - else - { - dfunc->df_instr = instr->ga_data; - dfunc->df_instr_count = instr->ga_len; - } - dfunc->df_varcount = dfunc->df_var_names.ga_len; - dfunc->df_has_closure = cctx.ctx_has_closure; - - if (cctx.ctx_outer_used) - { - ufunc->uf_flags |= FC_CLOSURE; - if (outer_cctx != NULL) - ++outer_cctx->ctx_closure_count; - } - - ufunc->uf_def_status = UF_COMPILED; - } + compile_dfunc_epilogue(outer_cctx, ufunc, instr, &cctx); ret = OK; erret: if (ufunc->uf_def_status == UF_COMPILING) { - dfunc_T *dfunc = ((dfunc_T *)def_functions.ga_data) - + ufunc->uf_dfunc_idx; - - // Compiling aborted, free the generated instructions. - clear_instr_ga(instr); - VIM_CLEAR(dfunc->df_name); - ga_clear_strings(&dfunc->df_var_names); - - // If using the last entry in the table and it was added above, we - // might as well remove it. - if (!dfunc->df_deleted && new_def_function - && ufunc->uf_dfunc_idx == def_functions.ga_len - 1) - { - --def_functions.ga_len; - ufunc->uf_dfunc_idx = 0; - } - ufunc->uf_def_status = UF_COMPILE_ERROR; - - while (cctx.ctx_scope != NULL) - drop_scope(&cctx); - - if (errormsg != NULL) - emsg(errormsg); - else if (did_emsg == did_emsg_before) - emsg(_(e_compiling_def_function_failed)); + // compilation failed. do cleanup. + compile_dfunc_ufunc_cleanup(ufunc, instr, new_def_function, + errormsg, did_emsg_before, &cctx); } if (cctx.ctx_redir_lhs.lhs_name != NULL)