3 * This source code is released for free distribution under the terms of the
4 * GNU General Public License.
6 * This module contains functions for generating tags for Rust files.
12 #include "general.h" /* must always come first */
27 #define MAX_STRING_LENGTH 256
49 static kindOption rustKinds
[] = {
50 {TRUE
, 'n', "module", "module"},
51 {TRUE
, 's', "struct", "structural type"},
52 {TRUE
, 'i', "interface", "trait interface"},
53 {TRUE
, 'c', "implementation", "implementation"},
54 {TRUE
, 'f', "function", "Function"},
55 {TRUE
, 'g', "enum", "Enum"},
56 {TRUE
, 't', "typedef", "Type Alias"},
57 {TRUE
, 'v', "variable", "Global variable"},
58 {TRUE
, 'M', "macro", "Macro Definition"},
59 {TRUE
, 'm', "field", "A struct field"},
60 {TRUE
, 'e', "enumerator", "An enum variant"},
61 {TRUE
, 'F', "method", "A method"},
90 static void parseBlock (lexerState
*lexer
, boolean delim
, int kind
, vString
*scope
);
93 * FUNCTION DEFINITIONS
96 /* Resets the scope string to the old length */
97 static void resetScope (vString
*scope
, size_t old_len
)
99 scope
->length
= old_len
;
100 scope
->buffer
[old_len
] = '\0';
103 /* Adds a name to the end of the scope string */
104 static void addToScope (vString
*scope
, vString
*name
)
106 if (vStringLength(scope
) > 0)
107 vStringCatS(scope
, "::");
108 vStringCat(scope
, name
);
111 /* Write the lexer's current token to string, taking care of special tokens */
112 static void writeCurTokenToStr (lexerState
*lexer
, vString
*out_str
)
114 switch (lexer
->cur_token
)
117 vStringCat(out_str
, lexer
->token_str
);
120 vStringCat(out_str
, lexer
->token_str
);
122 case TOKEN_WHITESPACE
:
123 vStringPut(out_str
, ' ');
126 vStringCatS(out_str
, "<<");
129 vStringCatS(out_str
, ">>");
132 vStringCatS(out_str
, "->");
135 vStringPut(out_str
, (char) lexer
->cur_token
);
139 /* Reads a character from the file */
140 static void advanceChar (lexerState
*lexer
)
142 lexer
->cur_c
= lexer
->next_c
;
143 lexer
->next_c
= fileGetc();
146 /* Reads N characters from the file */
147 static void advanceNChar (lexerState
*lexer
, int n
)
153 /* Store the current character in lexerState::token_str if there is space
154 * (set by MAX_STRING_LENGTH), and then read the next character from the file */
155 static void advanceAndStoreChar (lexerState
*lexer
)
157 if (vStringLength(lexer
->token_str
) < MAX_STRING_LENGTH
)
158 vStringPut(lexer
->token_str
, (char) lexer
->cur_c
);
162 static boolean
isWhitespace (int c
)
164 return c
== ' ' || c
== '\t' || c
== '\r' || c
== '\n';
167 static boolean
isAscii (int c
)
169 return (c
>= 0) && (c
< 0x80);
172 /* This isn't quite right for Unicode identifiers */
173 static boolean
isIdentifierStart (int c
)
175 return (isAscii(c
) && (isalpha(c
) || c
== '_')) || !isAscii(c
);
178 /* This isn't quite right for Unicode identifiers */
179 static boolean
isIdentifierContinue (int c
)
181 return (isAscii(c
) && (isalnum(c
) || c
== '_')) || !isAscii(c
);
184 static void scanWhitespace (lexerState
*lexer
)
186 while (isWhitespace(lexer
->cur_c
))
190 /* Normal line comments start with two /'s and continue until the next \n
191 * (potentially after a \r). Additionally, a shebang in the beginning of the
192 * file also counts as a line comment as long as it is not this sequence: #![ .
193 * Block comments start with / followed by a * and end with a * followed by a /.
194 * Unlike in C/C++ they nest. */
195 static void scanComments (lexerState
*lexer
)
198 if (lexer
->next_c
== '/')
200 advanceNChar(lexer
, 2);
201 while (lexer
->cur_c
!= EOF
&& lexer
->cur_c
!= '\n')
205 else if (lexer
->next_c
== '!')
207 advanceNChar(lexer
, 2);
208 /* If it is exactly #![ then it is not a comment, but an attribute */
209 if (lexer
->cur_c
== '[')
211 while (lexer
->cur_c
!= EOF
&& lexer
->cur_c
!= '\n')
215 else if (lexer
->next_c
== '*')
218 advanceNChar(lexer
, 2);
219 while (lexer
->cur_c
!= EOF
&& level
> 0)
221 if (lexer
->cur_c
== '*' && lexer
->next_c
== '/')
224 advanceNChar(lexer
, 2);
226 else if (lexer
->cur_c
== '/' && lexer
->next_c
== '*')
229 advanceNChar(lexer
, 2);
239 static void scanIdentifier (lexerState
*lexer
)
241 vStringClear(lexer
->token_str
);
244 advanceAndStoreChar(lexer
);
245 } while(lexer
->cur_c
!= EOF
&& isIdentifierContinue(lexer
->cur_c
));
248 /* Double-quoted strings, we only care about the \" escape. These
249 * last past the end of the line, so be careful not too store too much
250 * of them (see MAX_STRING_LENGTH). The only place we look at their
251 * contents is in the function definitions, and there the valid strings are
252 * things like "C" and "Rust" */
253 static void scanString (lexerState
*lexer
)
255 vStringClear(lexer
->token_str
);
256 advanceAndStoreChar(lexer
);
257 while (lexer
->cur_c
!= EOF
&& lexer
->cur_c
!= '"')
259 if (lexer
->cur_c
== '\\' && lexer
->next_c
== '"')
260 advanceAndStoreChar(lexer
);
261 advanceAndStoreChar(lexer
);
263 advanceAndStoreChar(lexer
);
266 /* Raw strings look like this: r"" or r##""## where the number of
267 * hashes must match */
268 static void scanRawString (lexerState
*lexer
)
270 size_t num_initial_hashes
= 0;
271 vStringClear(lexer
->token_str
);
272 advanceAndStoreChar(lexer
);
273 /* Count how many leading hashes there are */
274 while (lexer
->cur_c
== '#')
276 num_initial_hashes
++;
277 advanceAndStoreChar(lexer
);
279 if (lexer
->cur_c
!= '"')
281 advanceAndStoreChar(lexer
);
282 while (lexer
->cur_c
!= EOF
)
284 /* Count how many trailing hashes there are. If the number is equal or more
285 * than the number of leading hashes, break. */
286 if (lexer
->cur_c
== '"')
288 size_t num_trailing_hashes
= 0;
289 advanceAndStoreChar(lexer
);
290 while (lexer
->cur_c
== '#' && num_trailing_hashes
< num_initial_hashes
)
292 num_trailing_hashes
++;
294 advanceAndStoreChar(lexer
);
296 if (num_trailing_hashes
== num_initial_hashes
)
301 advanceAndStoreChar(lexer
);
306 /* This deals with character literals: 'n', '\n', '\uFFFF'; and lifetimes:
307 * 'lifetime. We'll use this approximate regexp for the literals:
308 * \' \\ [^']+ \' or \' [^'] \' or \' \\ \' \'. Either way, we'll treat this
309 * token as a string, so it gets preserved as is for function signatures with
311 static void scanCharacterOrLifetime (lexerState
*lexer
)
313 vStringClear(lexer
->token_str
);
314 advanceAndStoreChar(lexer
);
316 if (lexer
->cur_c
== '\\')
318 advanceAndStoreChar(lexer
);
319 /* The \' \\ \' \' (literally '\'') case */
320 if (lexer
->cur_c
== '\'' && lexer
->next_c
== '\'')
322 advanceAndStoreChar(lexer
);
323 advanceAndStoreChar(lexer
);
325 /* The \' \\ [^']+ \' case */
328 while (lexer
->cur_c
!= EOF
&& lexer
->cur_c
!= '\'')
329 advanceAndStoreChar(lexer
);
332 /* The \' [^'] \' case */
333 else if (lexer
->cur_c
!= '\'' && lexer
->next_c
== '\'')
335 advanceAndStoreChar(lexer
);
336 advanceAndStoreChar(lexer
);
338 /* Otherwise it is malformed, or a lifetime */
341 /* Advances the parser one token, optionally skipping whitespace
342 * (otherwise it is concatenated and returned as a single whitespace token).
343 * Whitespace is needed to properly render function signatures. Unrecognized
344 * token starts are stored literally, e.g. token may equal to a character '#'. */
345 static int advanceToken (lexerState
*lexer
, boolean skip_whitspace
)
347 boolean have_whitespace
= FALSE
;
348 lexer
->line
= getSourceLineNumber();
349 lexer
->pos
= getInputFilePosition();
350 while (lexer
->cur_c
!= EOF
)
352 if (isWhitespace(lexer
->cur_c
))
354 scanWhitespace(lexer
);
355 have_whitespace
= TRUE
;
357 else if (lexer
->cur_c
== '/' && (lexer
->next_c
== '/' || lexer
->next_c
== '*'))
360 have_whitespace
= TRUE
;
364 if (have_whitespace
&& !skip_whitspace
)
365 return lexer
->cur_token
= TOKEN_WHITESPACE
;
369 lexer
->line
= getSourceLineNumber();
370 lexer
->pos
= getInputFilePosition();
371 while (lexer
->cur_c
!= EOF
)
373 if (lexer
->cur_c
== '"')
376 return lexer
->cur_token
= TOKEN_STRING
;
378 else if (lexer
->cur_c
== 'r' && (lexer
->next_c
== '#' || lexer
->next_c
== '"'))
380 scanRawString(lexer
);
381 return lexer
->cur_token
= TOKEN_STRING
;
383 else if (lexer
->cur_c
== '\'')
385 scanCharacterOrLifetime(lexer
);
386 return lexer
->cur_token
= TOKEN_STRING
;
388 else if (isIdentifierStart(lexer
->cur_c
))
390 scanIdentifier(lexer
);
391 return lexer
->cur_token
= TOKEN_IDENT
;
393 /* These shift tokens aren't too important for tag-generation per se,
394 * but they confuse the skipUntil code which tracks the <> pairs. */
395 else if (lexer
->cur_c
== '>' && lexer
->next_c
== '>')
397 advanceNChar(lexer
, 2);
398 return lexer
->cur_token
= TOKEN_RSHIFT
;
400 else if (lexer
->cur_c
== '<' && lexer
->next_c
== '<')
402 advanceNChar(lexer
, 2);
403 return lexer
->cur_token
= TOKEN_LSHIFT
;
405 else if (lexer
->cur_c
== '-' && lexer
->next_c
== '>')
407 advanceNChar(lexer
, 2);
408 return lexer
->cur_token
= TOKEN_RARROW
;
412 int c
= lexer
->cur_c
;
414 return lexer
->cur_token
= c
;
417 return lexer
->cur_token
= TOKEN_EOF
;
420 static void initLexer (lexerState
*lexer
)
422 advanceNChar(lexer
, 2);
423 lexer
->token_str
= vStringNew();
425 if (lexer
->cur_c
== '#' && lexer
->next_c
== '!')
427 advanceToken(lexer
, TRUE
);
430 static void deInitLexer (lexerState
*lexer
)
432 vStringDelete(lexer
->token_str
);
433 lexer
->token_str
= NULL
;
436 static void addTag (vString
* ident
, const char* type
, const char* arg_list
, int kind
, unsigned long line
, MIOPos pos
, vString
*scope
, int parent_kind
)
441 initTagEntry(&tag
, ident
->buffer
);
443 tag
.lineNumber
= line
;
444 tag
.filePosition
= pos
;
445 tag
.sourceFileName
= getSourceFileName();
447 tag
.kindName
= rustKinds
[kind
].name
;
448 tag
.kind
= rustKinds
[kind
].letter
;
450 tag
.extensionFields
.signature
= arg_list
;
451 tag
.extensionFields
.varType
= type
;
452 if (parent_kind
!= K_NONE
)
454 tag
.extensionFields
.scope
[0] = rustKinds
[parent_kind
].name
;
455 tag
.extensionFields
.scope
[1] = scope
->buffer
;
460 /* Skip tokens until one of the goal tokens is hit. Escapes when level = 0 if there are no goal tokens.
461 * Keeps track of balanced <>'s, ()'s, []'s, and {}'s and ignores the goal tokens within those pairings */
462 static void skipUntil (lexerState
*lexer
, int goal_tokens
[], int num_goal_tokens
)
467 int bracket_level
= 0;
468 while (lexer
->cur_token
!= TOKEN_EOF
)
470 if (angle_level
== 0 && paren_level
== 0 && brace_level
== 0
471 && bracket_level
== 0)
474 for(ii
= 0; ii
< num_goal_tokens
; ii
++)
476 if (lexer
->cur_token
== goal_tokens
[ii
])
481 if (ii
< num_goal_tokens
)
484 switch (lexer
->cur_token
)
511 if (angle_level
>= 2)
514 /* TOKEN_LSHIFT is never interpreted as two <'s in valid Rust code */
518 /* Has to be after the token switch to catch the case when we start with the initial level token */
519 if (num_goal_tokens
== 0 && angle_level
== 0 && paren_level
== 0 && brace_level
== 0
520 && bracket_level
== 0)
522 advanceToken(lexer
, TRUE
);
527 * "fn" <ident>[<type_bounds>] "(" [<args>] ")" ["->" <ret_type>] "{" [<body>] "}"*/
528 static void parseFn (lexerState
*lexer
, vString
*scope
, int parent_kind
)
530 int kind
= (parent_kind
== K_TRAIT
|| parent_kind
== K_IMPL
) ? K_METHOD
: K_FN
;
536 boolean found_paren
= FALSE
;
537 boolean valid_signature
= TRUE
;
539 advanceToken(lexer
, TRUE
);
540 if (lexer
->cur_token
!= TOKEN_IDENT
)
543 name
= vStringNewCopy(lexer
->token_str
);
544 arg_list
= vStringNew();
549 advanceToken(lexer
, TRUE
);
551 /* HACK: This is a bit coarse as far as what tag entry means by
553 while (lexer
->cur_token
!= '{' && lexer
->cur_token
!= ';')
555 if (lexer
->cur_token
== '}')
557 valid_signature
= FALSE
;
560 else if (lexer
->cur_token
== '(')
565 else if (lexer
->cur_token
== ')')
570 valid_signature
= FALSE
;
574 else if (lexer
->cur_token
== TOKEN_EOF
)
576 valid_signature
= FALSE
;
579 writeCurTokenToStr(lexer
, arg_list
);
580 advanceToken(lexer
, FALSE
);
582 if (!found_paren
|| paren_level
!= 0)
583 valid_signature
= FALSE
;
587 vStringStripTrailing(arg_list
);
588 addTag(name
, NULL
, arg_list
->buffer
, kind
, line
, pos
, scope
, parent_kind
);
589 addToScope(scope
, name
);
590 parseBlock(lexer
, TRUE
, kind
, scope
);
594 vStringDelete(arg_list
);
598 * "mod" <ident> "{" [<body>] "}"
599 * "mod" <ident> ";"*/
600 static void parseMod (lexerState
*lexer
, vString
*scope
, int parent_kind
)
602 advanceToken(lexer
, TRUE
);
603 if (lexer
->cur_token
!= TOKEN_IDENT
)
606 addTag(lexer
->token_str
, NULL
, NULL
, K_MOD
, lexer
->line
, lexer
->pos
, scope
, parent_kind
);
607 addToScope(scope
, lexer
->token_str
);
609 advanceToken(lexer
, TRUE
);
611 parseBlock(lexer
, TRUE
, K_MOD
, scope
);
615 * "trait" <ident> [<type_bounds>] "{" [<body>] "}"
617 static void parseTrait (lexerState
*lexer
, vString
*scope
, int parent_kind
)
619 int goal_tokens
[] = {'{'};
621 advanceToken(lexer
, TRUE
);
622 if (lexer
->cur_token
!= TOKEN_IDENT
)
625 addTag(lexer
->token_str
, NULL
, NULL
, K_TRAIT
, lexer
->line
, lexer
->pos
, scope
, parent_kind
);
626 addToScope(scope
, lexer
->token_str
);
628 advanceToken(lexer
, TRUE
);
630 skipUntil(lexer
, goal_tokens
, 1);
632 parseBlock(lexer
, TRUE
, K_TRAIT
, scope
);
635 /* Skips type blocks of the form <T:T<T>, ...> */
636 static void skipTypeBlock (lexerState
*lexer
)
638 if (lexer
->cur_token
== '<')
640 skipUntil(lexer
, NULL
, 0);
641 advanceToken(lexer
, TRUE
);
645 /* Essentially grabs the last ident before 'for', '<' and '{', which
646 * tends to correspond to what we want as the impl tag entry name */
647 static void parseQualifiedType (lexerState
*lexer
, vString
* name
)
649 while (lexer
->cur_token
!= TOKEN_EOF
)
651 if (lexer
->cur_token
== TOKEN_IDENT
)
653 if (strcmp(lexer
->token_str
->buffer
, "for") == 0
654 || strcmp(lexer
->token_str
->buffer
, "where") == 0)
657 vStringCat(name
, lexer
->token_str
);
659 else if (lexer
->cur_token
== '<' || lexer
->cur_token
== '{')
663 advanceToken(lexer
, TRUE
);
665 skipTypeBlock(lexer
);
669 * "impl" [<type_bounds>] <qualified_ident>[<type_bounds>] ["for" <qualified_ident>[<type_bounds>]] "{" [<body>] "}"
671 static void parseImpl (lexerState
*lexer
, vString
*scope
, int parent_kind
)
677 advanceToken(lexer
, TRUE
);
682 skipTypeBlock(lexer
);
686 parseQualifiedType(lexer
, name
);
688 if (lexer
->cur_token
== TOKEN_IDENT
&& strcmp(lexer
->token_str
->buffer
, "for") == 0)
690 advanceToken(lexer
, TRUE
);
691 parseQualifiedType(lexer
, name
);
694 addTag(name
, NULL
, NULL
, K_IMPL
, line
, pos
, scope
, parent_kind
);
695 addToScope(scope
, name
);
697 parseBlock(lexer
, TRUE
, K_IMPL
, scope
);
703 * "static" ["mut"] <ident>
705 static void parseStatic (lexerState
*lexer
, vString
*scope
, int parent_kind
)
707 advanceToken(lexer
, TRUE
);
708 if (lexer
->cur_token
!= TOKEN_IDENT
)
710 if (strcmp(lexer
->token_str
->buffer
, "mut") == 0)
712 advanceToken(lexer
, TRUE
);
714 if (lexer
->cur_token
!= TOKEN_IDENT
)
717 addTag(lexer
->token_str
, NULL
, NULL
, K_STATIC
, lexer
->line
, lexer
->pos
, scope
, parent_kind
);
723 static void parseType (lexerState
*lexer
, vString
*scope
, int parent_kind
)
725 advanceToken(lexer
, TRUE
);
726 if (lexer
->cur_token
!= TOKEN_IDENT
)
729 addTag(lexer
->token_str
, NULL
, NULL
, K_TYPE
, lexer
->line
, lexer
->pos
, scope
, parent_kind
);
732 /* Structs and enums are very similar syntax-wise.
733 * It is possible to parse variants a bit more cleverly (e.g. make tuple variants functions and
734 * struct variants structs) but it'd be too clever and the signature wouldn't make too much sense without
735 * the enum's definition (e.g. for the type bounds)
737 * Struct/Enum format:
738 * "struct/enum" <ident>[<type_bounds>] "{" [<ident>,]+ "}"
739 * "struct/enum" <ident>[<type_bounds>] ";"
741 static void parseStructOrEnum (lexerState
*lexer
, vString
*scope
, int parent_kind
, boolean is_struct
)
743 int kind
= is_struct
? K_STRUCT
: K_ENUM
;
744 int field_kind
= is_struct
? K_FIELD
: K_VARIANT
;
745 int goal_tokens1
[] = {';', '{'};
747 advanceToken(lexer
, TRUE
);
748 if (lexer
->cur_token
!= TOKEN_IDENT
)
751 addTag(lexer
->token_str
, NULL
, NULL
, kind
, lexer
->line
, lexer
->pos
, scope
, parent_kind
);
752 addToScope(scope
, lexer
->token_str
);
754 skipUntil(lexer
, goal_tokens1
, 2);
756 if (lexer
->cur_token
== '{')
758 vString
*field_name
= vStringNew();
759 while (lexer
->cur_token
!= TOKEN_EOF
)
761 int goal_tokens2
[] = {'}', ','};
762 /* Skip attributes. Format:
765 if (lexer
->cur_token
== '#')
767 advanceToken(lexer
, TRUE
);
768 if (lexer
->cur_token
== '!')
769 advanceToken(lexer
, TRUE
);
770 if (lexer
->cur_token
== '[')
772 /* It's an attribute, skip it. */
773 skipUntil(lexer
, NULL
, 0);
777 /* Something's up with this field, skip to the next one */
778 skipUntil(lexer
, goal_tokens2
, 2);
782 if (lexer
->cur_token
== TOKEN_IDENT
)
784 if (strcmp(lexer
->token_str
->buffer
, "priv") == 0
785 || strcmp(lexer
->token_str
->buffer
, "pub") == 0)
787 advanceToken(lexer
, TRUE
);
788 if (lexer
->cur_token
!= TOKEN_IDENT
)
790 /* Something's up with this field, skip to the next one */
791 skipUntil(lexer
, goal_tokens2
, 2);
796 vStringClear(field_name
);
797 vStringCat(field_name
, lexer
->token_str
);
798 addTag(field_name
, NULL
, NULL
, field_kind
, lexer
->line
, lexer
->pos
, scope
, kind
);
799 skipUntil(lexer
, goal_tokens2
, 2);
801 if (lexer
->cur_token
== '}')
803 advanceToken(lexer
, TRUE
);
806 advanceToken(lexer
, TRUE
);
808 vStringDelete(field_name
);
812 /* Skip the body of the macro. Can't use skipUntil here as
813 * the body of the macro may have arbitrary code which confuses it (e.g.
814 * bitshift operators/function return arrows) */
815 static void skipMacro (lexerState
*lexer
)
821 advanceToken(lexer
, TRUE
);
822 switch (lexer
->cur_token
)
840 while (lexer
->cur_token
!= TOKEN_EOF
)
842 if (lexer
->cur_token
== plus_token
)
844 else if (lexer
->cur_token
== minus_token
)
848 advanceToken(lexer
, TRUE
);
850 advanceToken(lexer
, TRUE
);
854 * Macro rules format:
855 * "macro_rules" "!" <ident> <macro_body>
857 static void parseMacroRules (lexerState
*lexer
, vString
*scope
, int parent_kind
)
859 advanceToken(lexer
, TRUE
);
861 if (lexer
->cur_token
!= '!')
864 advanceToken(lexer
, TRUE
);
866 if (lexer
->cur_token
!= TOKEN_IDENT
)
869 addTag(lexer
->token_str
, NULL
, NULL
, K_MACRO
, lexer
->line
, lexer
->pos
, scope
, parent_kind
);
875 * Rust is very liberal with nesting, so this function is used pretty much for any block
877 static void parseBlock (lexerState
*lexer
, boolean delim
, int kind
, vString
*scope
)
882 if (lexer
->cur_token
!= '{')
884 advanceToken(lexer
, TRUE
);
886 while (lexer
->cur_token
!= TOKEN_EOF
)
888 if (lexer
->cur_token
== TOKEN_IDENT
)
890 size_t old_scope_len
= vStringLength(scope
);
891 if (strcmp(lexer
->token_str
->buffer
, "fn") == 0)
893 parseFn(lexer
, scope
, kind
);
895 else if(strcmp(lexer
->token_str
->buffer
, "mod") == 0)
897 parseMod(lexer
, scope
, kind
);
899 else if(strcmp(lexer
->token_str
->buffer
, "static") == 0)
901 parseStatic(lexer
, scope
, kind
);
903 else if(strcmp(lexer
->token_str
->buffer
, "trait") == 0)
905 parseTrait(lexer
, scope
, kind
);
907 else if(strcmp(lexer
->token_str
->buffer
, "type") == 0)
909 parseType(lexer
, scope
, kind
);
911 else if(strcmp(lexer
->token_str
->buffer
, "impl") == 0)
913 parseImpl(lexer
, scope
, kind
);
915 else if(strcmp(lexer
->token_str
->buffer
, "struct") == 0)
917 parseStructOrEnum(lexer
, scope
, kind
, TRUE
);
919 else if(strcmp(lexer
->token_str
->buffer
, "enum") == 0)
921 parseStructOrEnum(lexer
, scope
, kind
, FALSE
);
923 else if(strcmp(lexer
->token_str
->buffer
, "macro_rules") == 0)
925 parseMacroRules(lexer
, scope
, kind
);
929 advanceToken(lexer
, TRUE
);
930 if (lexer
->cur_token
== '!')
935 resetScope(scope
, old_scope_len
);
937 else if (lexer
->cur_token
== '{')
940 advanceToken(lexer
, TRUE
);
942 else if (lexer
->cur_token
== '}')
945 advanceToken(lexer
, TRUE
);
947 else if (lexer
->cur_token
== '\'')
949 /* Skip over the 'static lifetime, as it confuses the static parser above */
950 advanceToken(lexer
, TRUE
);
951 if (lexer
->cur_token
== TOKEN_IDENT
&& strcmp(lexer
->token_str
->buffer
, "static") == 0)
952 advanceToken(lexer
, TRUE
);
956 advanceToken(lexer
, TRUE
);
958 if (delim
&& level
<= 0)
963 static void findRustTags (void)
966 vString
* scope
= vStringNew();
969 parseBlock(&lexer
, FALSE
, K_NONE
, scope
);
970 vStringDelete(scope
);
975 extern parserDefinition
*RustParser (void)
977 static const char *const extensions
[] = { "rs", NULL
};
978 parserDefinition
*def
= parserNew ("Rust");
979 def
->kinds
= rustKinds
;
980 def
->kindCount
= KIND_COUNT (rustKinds
);
981 def
->extensions
= extensions
;
982 def
->parser
= findRustTags
;