From 4fc1e39fa246a1e7c797399302d56f13f512f195 Mon Sep 17 00:00:00 2001 From: Christoffer Lerno Date: Sun, 9 Jun 2024 14:38:57 +0200 Subject: [PATCH] Improve inlining warning messages. Added `index_of_char_from`. --- lib/std/core/string.c3 | 113 ++++++++++++++++++++++++++++++++++++-- releasenotes.md | 1 + src/compiler/sema_decls.c | 4 +- 3 files changed, 111 insertions(+), 7 deletions(-) diff --git a/lib/std/core/string.c3 b/lib/std/core/string.c3 index b45ac67bd..f52cd879e 100644 --- a/lib/std/core/string.c3 +++ b/lib/std/core/string.c3 @@ -31,6 +31,11 @@ fault NumberConversion FLOAT_OUT_OF_RANGE, } +/** + * Return a temporary String created using the formatting function. + * + * @param [in] fmt `The formatting string` + **/ macro String tformat(String fmt, ...) { DString str = dstring::temp_with_capacity(fmt.len + $vacount * 8); @@ -38,12 +43,24 @@ macro String tformat(String fmt, ...) return str.str_view(); } +/** + * Return a temporary ZString created using the formatting function. + * + * @param [in] fmt `The formatting string` + **/ macro ZString tformat_zstr(String fmt, ...) { DString str = dstring::temp_with_capacity(fmt.len + $vacount * 8); str.appendf(fmt, $vasplat()); return str.zstr_view(); } + +/** + * Return a new String created using the formatting function. + * + * @param [in] fmt `The formatting string` + * @param [inout] allocator `The allocator to use` + **/ macro String new_format(String fmt, ..., Allocator allocator = allocator::heap()) { @pool(allocator) @@ -54,6 +71,12 @@ macro String new_format(String fmt, ..., Allocator allocator = allocator::heap() }; } +/** + * Return a new ZString created using the formatting function. + * + * @param [in] fmt `The formatting string` + * @param [inout] allocator `The allocator to use` + **/ macro ZString new_format_zstr(String fmt, ..., Allocator allocator = allocator::heap()) { @pool(allocator) @@ -64,6 +87,14 @@ macro ZString new_format_zstr(String fmt, ..., Allocator allocator = allocator:: }; } +/** + * Check if a character is in a set. + * + * @param c `the character to check` + * @param [in] set `The formatting string` + * @pure + * @return `True if a character is in the set` + **/ macro bool char_in_set(char c, String set) { foreach (ch : set) if (ch == c) return true; @@ -96,8 +127,12 @@ fn String join_new(String[] s, String joiner, Allocator allocator = allocator::h } /** - * @param [in] string - * @param [in] to_trim + * Remove characters from the front and end of a string. + * + * @param [in] string `The string to trim` + * @param [in] to_trim `The set of characters to trim, defaults to whitespace` + * @pure + * @return `a substring of the string passed in` **/ fn String String.trim(string, String to_trim = "\t\n\r ") { @@ -111,8 +146,12 @@ fn String String.trim(string, String to_trim = "\t\n\r ") } /** + * Check if the String starts with the needle. + * * @param [in] string * @param [in] needle + * @pure + * @return `'true' if the string starts with the needle` **/ fn bool String.starts_with(string, String needle) { @@ -122,8 +161,12 @@ fn bool String.starts_with(string, String needle) } /** + * Check if the String ends with the needle. + * * @param [in] string * @param [in] needle + * @pure + * @return `'true' if the string ends with the needle` **/ fn bool String.ends_with(string, String needle) { @@ -137,6 +180,8 @@ fn bool String.ends_with(string, String needle) * * @param [in] string * @param [in] needle + * @pure + * @return `the substring with the prefix removed` **/ fn String String.strip(string, String needle) { @@ -149,6 +194,8 @@ fn String String.strip(string, String needle) * * @param [in] string * @param [in] needle + * @pure + * @return `the substring with the suffix removed` **/ fn String String.strip_end(string, String needle) { @@ -211,6 +258,14 @@ fn String[] String.tsplit(s, String needle, usz max = 0) return s.split(needle, max, allocator::temp()) @inline; } +/** + * Check if a substring is found in the string. + + * @param [in] s + * @param [in] needle "The string to look for." + * @pure + * @return "true if the string contains the substring, false otherwise" + **/ fn bool String.contains(s, String needle) { return @ok(s.index_of(needle)); @@ -220,6 +275,7 @@ fn bool String.contains(s, String needle) * Find the index of the first incidence of a string. * * @param [in] s + * @param needle "The character to look for" * @pure * @ensure return < s.len * @return "the index of the needle" @@ -235,9 +291,32 @@ fn usz! String.index_of_char(s, char needle) } /** - * Find the index of the first incidence of a string. + * Find the index of the first incidence of a character. * * @param [in] s + * @param needle "The character to look for" + * @param start_index "The index to start with, may exceed max index." + * @pure + * @ensure return < s.len + * @return "the index of the needle" + * @return! SearchResult.MISSING "if the needle cannot be found starting from the start_index" + **/ +fn usz! String.index_of_char_from(s, char needle, usz start_index) +{ + usz len = s.len; + if (len <= start_index) return SearchResult.MISSING?; + for (usz i = start_index; i < len; i++) + { + if (s[i] == needle) return i; + } + return SearchResult.MISSING?; +} + +/** + * Find the index of the first incidence of a character starting from the end. + * + * @param [in] s + * @param needle "the character to find" * @pure * @ensure return < s.len * @return "the index of the needle" @@ -430,9 +509,15 @@ fn Char32[]! String.to_temp_utf32(s) return s.to_new_utf32(allocator::temp()); } +/** + * Convert a string to ASCII lower case. + * + * @param [inout] s + * @pure + **/ fn void String.convert_ascii_to_lower(s) { - foreach (&c : s) if (c.is_upper()) *c += 'a' - 'A'; + foreach (&c : s) if (c.is_upper() @pure) *c += 'a' - 'A'; } fn String String.new_ascii_to_lower(s, Allocator allocator = allocator::heap()) @@ -447,11 +532,25 @@ fn String String.temp_ascii_to_lower(s, Allocator allocator = allocator::heap()) return s.new_ascii_to_lower(allocator::temp()); } +/** + * Convert a string to ASCII upper case. + * + * @param [inout] s + * @pure + **/ fn void String.convert_ascii_to_upper(s) { - foreach (&c : s) if (c.is_lower()) *c -= 'a' - 'A'; + foreach (&c : s) if (c.is_lower() @pure) *c -= 'a' - 'A'; } +/** + * Returns a string converted to ASCII upper case. + * + * @param [in] s + * @param [inout] allocator + * + * @return `a new String converted to ASCII upper case.` + **/ fn String String.new_ascii_to_upper(s, Allocator allocator = allocator::heap()) { String copy = s.copy(allocator); @@ -464,6 +563,10 @@ fn StringIterator String.iterator(s) return { s, 0 }; } +/** + * @param [in] s + * @return `a temporary String converted to ASCII upper case.` + **/ fn String String.temp_ascii_to_upper(s) { return s.new_ascii_to_upper(allocator::temp()); diff --git a/releasenotes.md b/releasenotes.md index a443b3e18..7a22a9d27 100644 --- a/releasenotes.md +++ b/releasenotes.md @@ -32,6 +32,7 @@ - Bug when assigning an optional from an optional. - Lambdas were not type checked thoroughly #1185. - Fix problems using reflection on interface types #1203. +- `@param` with unnamed macro varargs could crash the compiler. ### Stdlib changes - "init_new/init_temp" removed. diff --git a/src/compiler/sema_decls.c b/src/compiler/sema_decls.c index e4f37fafb..f2285d75c 100644 --- a/src/compiler/sema_decls.c +++ b/src/compiler/sema_decls.c @@ -2704,13 +2704,13 @@ static inline bool sema_analyse_doc_header(SemaContext *context, AstId doc, VECEACH(params, j) { param = params[j]; - if (param->name == param_name) goto NEXT; + if (param && param->name == param_name) goto NEXT; } VECEACH(extra_params, j) { assert(extra_params); param = extra_params[j]; - if (param->name == param_name) goto NEXT; + if (param && param->name == param_name) goto NEXT; } RETURN_SEMA_ERROR(&directive->contract_stmt.param, "There is no parameter '%s', did you misspell it?", param_name); NEXT:;