1pub struct BuiltinDoc {
7 pub signature: &'static str,
9 pub description: &'static str,
11}
12
13pub(super) fn is_control_keyword(name: &str) -> bool {
17 matches!(name, "next" | "last" | "redo" | "goto" | "return" | "exit" | "die")
18}
19
20pub(super) fn is_builtin_function(name: &str) -> bool {
24 matches!(
25 name,
26 "print"
27 | "say"
28 | "printf"
29 | "sprintf"
30 | "open"
31 | "close"
32 | "read"
33 | "write"
34 | "chomp"
35 | "chop"
36 | "split"
37 | "join"
38 | "push"
39 | "pop"
40 | "shift"
41 | "unshift"
42 | "sort"
43 | "reverse"
44 | "map"
45 | "grep"
46 | "length"
47 | "substr"
48 | "index"
49 | "rindex"
50 | "lc"
51 | "uc"
52 | "lcfirst"
53 | "ucfirst"
54 | "defined"
55 | "undef"
56 | "ref"
57 | "blessed"
58 | "die"
59 | "warn"
60 | "eval"
61 | "require"
62 | "use"
63 | "return"
64 | "next"
65 | "last"
66 | "redo"
67 | "goto" )
69}
70
71pub(super) fn is_file_test_operator(op: &str) -> bool {
76 matches!(
77 op,
78 "-e" | "-d"
79 | "-f"
80 | "-r"
81 | "-w"
82 | "-x"
83 | "-s"
84 | "-z"
85 | "-T"
86 | "-B"
87 | "-M"
88 | "-A"
89 | "-C"
90 | "-l"
91 | "-p"
92 | "-S"
93 | "-u"
94 | "-g"
95 | "-k"
96 | "-t"
97 | "-O"
98 | "-G"
99 | "-R"
100 | "-b"
101 | "-c"
102 )
103}
104
105pub fn get_builtin_documentation(name: &str) -> Option<BuiltinDoc> {
114 match name {
115 "print" => Some(BuiltinDoc {
117 signature: "print FILEHANDLE LIST\nprint LIST\nprint",
118 description: "Prints a string or list of strings. If FILEHANDLE is omitted, prints to the last selected output handle (STDOUT by default).",
119 }),
120 "say" => Some(BuiltinDoc {
121 signature: "say FILEHANDLE LIST\nsay LIST\nsay",
122 description: "Like print, but appends a newline to the output.",
123 }),
124 "printf" => Some(BuiltinDoc {
125 signature: "printf FILEHANDLE FORMAT, LIST\nprintf FORMAT, LIST",
126 description: "Prints a formatted string to FILEHANDLE (default STDOUT).",
127 }),
128 "sprintf" => Some(BuiltinDoc {
129 signature: "sprintf FORMAT, LIST",
130 description: "Returns a formatted string (like C sprintf). Does not print.",
131 }),
132 "open" => Some(BuiltinDoc {
133 signature: "open FILEHANDLE, MODE, EXPR\nopen FILEHANDLE, EXPR\nopen FILEHANDLE",
134 description: "Opens the file whose filename is given by EXPR, and associates it with FILEHANDLE.",
135 }),
136 "close" => Some(BuiltinDoc {
137 signature: "close FILEHANDLE\nclose",
138 description: "Closes the file, socket, or pipe associated with FILEHANDLE.",
139 }),
140 "read" => Some(BuiltinDoc {
141 signature: "read FILEHANDLE, SCALAR, LENGTH, OFFSET\nread FILEHANDLE, SCALAR, LENGTH",
142 description: "Reads LENGTH bytes of data into SCALAR from FILEHANDLE. Returns the number of bytes read, or undef on error.",
143 }),
144 "write" => Some(BuiltinDoc {
145 signature: "write FILEHANDLE\nwrite",
146 description: "Writes a formatted record to FILEHANDLE using the format associated with it.",
147 }),
148 "seek" => Some(BuiltinDoc {
149 signature: "seek FILEHANDLE, POSITION, WHENCE",
150 description: "Sets the position for a filehandle. WHENCE: 0=start, 1=current, 2=end.",
151 }),
152 "tell" => Some(BuiltinDoc {
153 signature: "tell FILEHANDLE\ntell",
154 description: "Returns the current position in bytes for FILEHANDLE.",
155 }),
156 "eof" => Some(BuiltinDoc {
157 signature: "eof FILEHANDLE\neof()\neof",
158 description: "Returns true if the next read on FILEHANDLE would return end of file.",
159 }),
160 "binmode" => Some(BuiltinDoc {
161 signature: "binmode FILEHANDLE, LAYER\nbinmode FILEHANDLE",
162 description: "Sets binary mode on FILEHANDLE, or specifies an I/O layer.",
163 }),
164 "truncate" => Some(BuiltinDoc {
165 signature: "truncate FILEHANDLE, LENGTH",
166 description: "Truncates the file at the given LENGTH.",
167 }),
168
169 "chomp" => Some(BuiltinDoc {
171 signature: "chomp VARIABLE\nchomp LIST\nchomp",
172 description: "Removes the trailing newline from VARIABLE. Returns the number of characters removed.",
173 }),
174 "chop" => Some(BuiltinDoc {
175 signature: "chop VARIABLE\nchop LIST\nchop",
176 description: "Removes and returns the last character from VARIABLE.",
177 }),
178 "length" => Some(BuiltinDoc {
179 signature: "length EXPR\nlength",
180 description: "Returns the length in characters of the value of EXPR.",
181 }),
182 "substr" => Some(BuiltinDoc {
183 signature: "substr EXPR, OFFSET, LENGTH, REPLACEMENT\nsubstr EXPR, OFFSET, LENGTH\nsubstr EXPR, OFFSET",
184 description: "Extracts a substring out of EXPR and returns it. With REPLACEMENT, replaces the substring in-place.",
185 }),
186 "index" => Some(BuiltinDoc {
187 signature: "index STR, SUBSTR, POSITION\nindex STR, SUBSTR",
188 description: "Returns the position of the first occurrence of SUBSTR in STR at or after POSITION. Returns -1 if not found.",
189 }),
190 "rindex" => Some(BuiltinDoc {
191 signature: "rindex STR, SUBSTR, POSITION\nrindex STR, SUBSTR",
192 description: "Returns the position of the last occurrence of SUBSTR in STR at or before POSITION.",
193 }),
194 "lc" => Some(BuiltinDoc {
195 signature: "lc EXPR\nlc",
196 description: "Returns a lowercased version of EXPR (or $_ if omitted).",
197 }),
198 "uc" => Some(BuiltinDoc {
199 signature: "uc EXPR\nuc",
200 description: "Returns an uppercased version of EXPR (or $_ if omitted).",
201 }),
202 "lcfirst" => Some(BuiltinDoc {
203 signature: "lcfirst EXPR\nlcfirst",
204 description: "Returns EXPR with the first character lowercased.",
205 }),
206 "ucfirst" => Some(BuiltinDoc {
207 signature: "ucfirst EXPR\nucfirst",
208 description: "Returns EXPR with the first character uppercased.",
209 }),
210 "chr" => Some(BuiltinDoc {
211 signature: "chr NUMBER\nchr",
212 description: "Returns the character represented by NUMBER in the character set.",
213 }),
214 "ord" => Some(BuiltinDoc {
215 signature: "ord EXPR\nord",
216 description: "Returns the numeric value of the first character of EXPR.",
217 }),
218 "hex" => Some(BuiltinDoc {
219 signature: "hex EXPR\nhex",
220 description: "Interprets EXPR as a hex string and returns the corresponding numeric value.",
221 }),
222 "oct" => Some(BuiltinDoc {
223 signature: "oct EXPR\noct",
224 description: "Interprets EXPR as an octal string and returns the corresponding value. Handles 0x, 0b, and 0 prefixes.",
225 }),
226 "quotemeta" => Some(BuiltinDoc {
227 signature: "quotemeta EXPR\nquotemeta",
228 description: "Returns EXPR with all non-alphanumeric characters backslashed (escaped for regex).",
229 }),
230 "join" => Some(BuiltinDoc {
231 signature: "join EXPR, LIST",
232 description: "Joins the separate strings of LIST into a single string with fields separated by EXPR, and returns that string.\n\n```perl\nmy $str = join(', ', 'a', 'b', 'c'); # \"a, b, c\"\nmy $csv = join(',', @fields);\n```",
233 }),
234 "split" => Some(BuiltinDoc {
235 signature: "split /PATTERN/, EXPR, LIMIT\nsplit /PATTERN/, EXPR\nsplit /PATTERN/\nsplit",
236 description: "Splits the string EXPR into a list of strings and returns the list. If LIMIT is specified, splits into at most that many fields.\n\n```perl\nmy @words = split /\\s+/, $line; # split on whitespace\nmy @fields = split /,/, $csv, 10; # at most 10 fields\n```",
237 }),
238
239 "push" => Some(BuiltinDoc {
241 signature: "push ARRAY, LIST",
242 description: "Appends one or more values to the end of ARRAY. Returns the number of elements in the resulting array.\n\n```perl\nmy @list = (1, 2);\npush @list, 3, 4; # @list is now (1, 2, 3, 4)\n```",
243 }),
244 "pop" => Some(BuiltinDoc {
245 signature: "pop ARRAY\npop",
246 description: "Removes and returns the last element of ARRAY.\n\n```perl\nmy @stack = (1, 2, 3);\nmy $top = pop @stack; # $top = 3, @stack = (1, 2)\n```",
247 }),
248 "shift" => Some(BuiltinDoc {
249 signature: "shift ARRAY\nshift",
250 description: "Removes and returns the first element of ARRAY, shortening the array by 1.\n\n```perl\nmy @queue = ('first', 'second');\nmy $item = shift @queue; # $item = 'first'\n```",
251 }),
252 "unshift" => Some(BuiltinDoc {
253 signature: "unshift ARRAY, LIST",
254 description: "Prepends LIST to the front of ARRAY. Returns the number of elements in the new array.\n\n```perl\nmy @list = (3, 4);\nunshift @list, 1, 2; # @list is now (1, 2, 3, 4)\n```",
255 }),
256 "splice" => Some(BuiltinDoc {
257 signature: "splice ARRAY, OFFSET, LENGTH, LIST\nsplice ARRAY, OFFSET, LENGTH\nsplice ARRAY, OFFSET\nsplice ARRAY",
258 description: "Removes LENGTH elements from ARRAY starting at OFFSET, replacing them with LIST. Returns the removed elements. In scalar context, returns the last removed element.",
259 }),
260 "sort" => Some(BuiltinDoc {
261 signature: "sort SUBNAME LIST\nsort BLOCK LIST\nsort LIST",
262 description: "Sorts LIST and returns the sorted list. BLOCK or SUBNAME provides a custom comparison function using $a and $b. Only valid in list context; using sort in scalar context returns undef (avoid).",
263 }),
264 "reverse" => Some(BuiltinDoc {
265 signature: "reverse LIST",
266 description: "In list context, returns LIST in reverse order. In scalar context, returns a string with characters reversed.",
267 }),
268 "map" => Some(BuiltinDoc {
269 signature: "map BLOCK LIST\nmap EXPR, LIST",
270 description: "Evaluates the BLOCK or EXPR for each element of LIST (locally setting $_ to each element) and composes a list of the results. In scalar context, returns the number of elements the expression would produce.\n\n```perl\nmy @doubled = map { $_ * 2 } @numbers;\nmy @names = map { $_->{name} } @records;\n```",
271 }),
272 "grep" => Some(BuiltinDoc {
273 signature: "grep BLOCK LIST\ngrep EXPR, LIST",
274 description: "Evaluates BLOCK or EXPR for each element of LIST and returns the list of elements for which the expression is true. In scalar context, returns the number of matching elements rather than the list.\n\n```perl\nmy @evens = grep { $_ % 2 == 0 } @numbers;\nmy $count = grep { /pattern/ } @lines;\n```",
275 }),
276 "scalar" => Some(BuiltinDoc {
277 signature: "scalar EXPR",
278 description: "Forces EXPR to be interpreted in scalar context and returns the value of EXPR.",
279 }),
280 "wantarray" => Some(BuiltinDoc {
281 signature: "wantarray",
282 description: "Returns true if the subroutine is called in list context, false (defined but false) in scalar context, and undef in void context. Use to write context-sensitive subs: `return wantarray ? @list : $count;`",
283 }),
284
285 "keys" => Some(BuiltinDoc {
287 signature: "keys HASH\nkeys ARRAY",
288 description: "In list context, returns all keys of the named hash or indices of an array. In scalar context, returns the number of keys (an integer count). Note: `scalar keys %h` is the idiomatic way to count hash entries.",
289 }),
290 "values" => Some(BuiltinDoc {
291 signature: "values HASH\nvalues ARRAY",
292 description: "In list context, returns all values of the named hash or values of an array. In scalar context, returns the number of values (same as scalar keys).",
293 }),
294 "each" => Some(BuiltinDoc {
295 signature: "each HASH\neach ARRAY",
296 description: "Returns the next key-value pair from the hash as a two-element list, or an empty list when exhausted. The iterator resets when the list is exhausted, when keys() or values() is called on the hash, or when the hash is modified. Call in a while loop: `while (my ($k, $v) = each %h) { ... }`",
297 }),
298 "exists" => Some(BuiltinDoc {
299 signature: "exists EXPR",
300 description: "Returns true if the specified hash key or array element exists, even if its value is undef.",
301 }),
302 "delete" => Some(BuiltinDoc {
303 signature: "delete EXPR",
304 description: "Deletes the specified keys and their associated values from a hash, or elements from an array.",
305 }),
306 "defined" => Some(BuiltinDoc {
307 signature: "defined EXPR\ndefined",
308 description: "Returns true if EXPR has a value other than undef.",
309 }),
310 "undef" => Some(BuiltinDoc {
311 signature: "undef EXPR\nundef",
312 description: "Undefines the value of EXPR. Can be used on scalars, arrays, hashes, subroutines, and typeglobs.",
313 }),
314
315 "ref" => Some(BuiltinDoc {
317 signature: "ref EXPR\nref",
318 description: "Returns a string indicating the type of reference EXPR is, or empty string if not a reference. E.g. HASH, ARRAY, SCALAR, CODE.",
319 }),
320 "bless" => Some(BuiltinDoc {
321 signature: "bless REF, CLASSNAME\nbless REF",
322 description: "Associates the referent of REF with package CLASSNAME (or current package). Returns the reference.",
323 }),
324 "blessed" => Some(BuiltinDoc {
325 signature: "blessed EXPR",
326 description: "Returns the name of the package EXPR is blessed into, or undef if EXPR is not a blessed reference. From Scalar::Util.",
327 }),
328 "tie" => Some(BuiltinDoc {
329 signature: "tie VARIABLE, CLASSNAME, LIST",
330 description: "Binds a variable to a package class that provides the implementation for the variable.",
331 }),
332 "untie" => Some(BuiltinDoc {
333 signature: "untie VARIABLE",
334 description: "Breaks the binding between a variable and its package.",
335 }),
336 "tied" => Some(BuiltinDoc {
337 signature: "tied VARIABLE",
338 description: "Returns a reference to the object underlying VARIABLE if it is tied, or undef if not.",
339 }),
340
341 "TIESCALAR" => Some(BuiltinDoc {
343 signature: "TIESCALAR CLASSNAME, LIST",
344 description: "Constructor called when `tie $scalar, CLASSNAME, LIST` is used. Must return a blessed reference.",
345 }),
346 "TIEARRAY" => Some(BuiltinDoc {
347 signature: "TIEARRAY CLASSNAME, LIST",
348 description: "Constructor called when `tie @array, CLASSNAME, LIST` is used. Must return a blessed reference.",
349 }),
350 "TIEHASH" => Some(BuiltinDoc {
351 signature: "TIEHASH CLASSNAME, LIST",
352 description: "Constructor called when `tie %hash, CLASSNAME, LIST` is used. Must return a blessed reference.",
353 }),
354 "TIEHANDLE" => Some(BuiltinDoc {
355 signature: "TIEHANDLE CLASSNAME, LIST",
356 description: "Constructor called when `tie *FH, CLASSNAME, LIST` is used. Must return a blessed reference.",
357 }),
358 "FETCH" => Some(BuiltinDoc {
359 signature: "FETCH this",
360 description: "Called on every access of a tied scalar or array/hash element. Returns the value.",
361 }),
362 "STORE" => Some(BuiltinDoc {
363 signature: "STORE this, value",
364 description: "Called on every assignment to a tied scalar or array/hash element.",
365 }),
366 "FIRSTKEY" => Some(BuiltinDoc {
367 signature: "FIRSTKEY this",
368 description: "Called when `keys` or `each` is first invoked on a tied hash.",
369 }),
370 "NEXTKEY" => Some(BuiltinDoc {
371 signature: "NEXTKEY this, lastkey",
372 description: "Called during iteration of a tied hash with `each` or `keys`.",
373 }),
374 "DESTROY" => Some(BuiltinDoc {
375 signature: "DESTROY this",
376 description: "Called when the tied object goes out of scope or is explicitly untied.",
377 }),
378
379 "die" => Some(BuiltinDoc {
381 signature: "die LIST",
382 description: "Raises an exception. If LIST does not end in '\\n', Perl appends the script name and line number. In modules, prefer Carp::croak() to preserve the caller's stack frame. The exception is available in $@ after an eval block.",
383 }),
384 "warn" => Some(BuiltinDoc {
385 signature: "warn LIST",
386 description: "Prints a warning to STDERR. Does not exit. If the message does not end in '\\n', Perl appends the script name and line number. In modules, prefer Carp::carp() to report from the caller's perspective.",
387 }),
388 "eval" => Some(BuiltinDoc {
389 signature: "eval BLOCK\neval EXPR",
390 description: "Evaluates BLOCK or EXPR and traps exceptions. After the eval, check $@ for errors: if ($@) { ... }. BLOCK form is preferred — EXPR form (string eval) is a security risk and triggers the PL600 diagnostic.",
391 }),
392 "croak" => Some(BuiltinDoc {
394 signature: "croak LIST",
395 description: "Like die but reports the error from the caller's perspective. Part of the Carp module. Use instead of die in library code so the stack trace points to the caller, not the module internals.",
396 }),
397 "carp" => Some(BuiltinDoc {
398 signature: "carp LIST",
399 description: "Like warn but reports the warning from the caller's perspective. Part of the Carp module. Prefer over warn in library code.",
400 }),
401 "confess" => Some(BuiltinDoc {
402 signature: "confess LIST",
403 description: "Like croak but includes a full stack trace. Part of the Carp module. Use when the full call chain is needed for debugging.",
404 }),
405 "cluck" => Some(BuiltinDoc {
406 signature: "cluck LIST",
407 description: "Like carp but includes a full stack trace. Part of the Carp module. Use for warnings that benefit from call chain context.",
408 }),
409 "return" => Some(BuiltinDoc {
410 signature: "return EXPR\nreturn",
411 description: "Returns from a subroutine with the value of EXPR.",
412 }),
413 "next" => Some(BuiltinDoc {
414 signature: "next LABEL\nnext",
415 description: "Starts the next iteration of the loop (like C 'continue').",
416 }),
417 "last" => Some(BuiltinDoc {
418 signature: "last LABEL\nlast",
419 description: "Exits the loop immediately (like C 'break').",
420 }),
421 "redo" => Some(BuiltinDoc {
422 signature: "redo LABEL\nredo",
423 description: "Restarts the loop block without re-evaluating the condition.",
424 }),
425 "goto" => Some(BuiltinDoc {
426 signature: "goto LABEL\ngoto EXPR\ngoto &NAME",
427 description: "Transfers control to the named label, computed label, or substitutes a call to the named subroutine.",
428 }),
429 "caller" => Some(BuiltinDoc {
430 signature: "caller EXPR\ncaller",
431 description: "Without argument, returns (package, filename, line) in list context or the package name in scalar context. With EXPR returns additional call-frame info: (package, filename, line, subroutine, hasargs, wantarray, evaltext, is_require, hints, bitmask, hinthash).",
432 }),
433 "exit" => Some(BuiltinDoc {
434 signature: "exit EXPR\nexit",
435 description: "Exits the program with status EXPR (default 0). Calls END blocks and DESTROY methods before exit.",
436 }),
437
438 "require" => Some(BuiltinDoc {
440 signature: "require EXPR\nrequire",
441 description: "Loads a library module at runtime. Raises an exception on failure.",
442 }),
443 "use" => Some(BuiltinDoc {
444 signature: "use Module VERSION LIST\nuse Module VERSION\nuse Module LIST\nuse Module",
445 description: "Loads and imports a module at compile time. Equivalent to BEGIN { require Module; Module->import( LIST ); }",
446 }),
447 "do" => Some(BuiltinDoc {
448 signature: "do BLOCK\ndo EXPR",
449 description: "As do BLOCK: executes BLOCK and returns its value. As do EXPR: reads and executes a Perl file.",
450 }),
451
452 "abs" => Some(BuiltinDoc {
454 signature: "abs VALUE\nabs",
455 description: "Returns the absolute value of its argument.",
456 }),
457 "int" => Some(BuiltinDoc {
458 signature: "int EXPR\nint",
459 description: "Returns the integer portion of EXPR (truncates toward zero).",
460 }),
461 "sqrt" => Some(BuiltinDoc {
462 signature: "sqrt EXPR\nsqrt",
463 description: "Returns the positive square root of EXPR.",
464 }),
465 "log" => Some(BuiltinDoc {
466 signature: "log EXPR\nlog",
467 description: "Returns the natural logarithm (base e) of EXPR.",
468 }),
469 "exp" => Some(BuiltinDoc {
470 signature: "exp EXPR\nexp",
471 description: "Returns e (the natural logarithm base) to the power of EXPR.",
472 }),
473 "sin" => Some(BuiltinDoc {
474 signature: "sin EXPR\nsin",
475 description: "Returns the sine of EXPR (expressed in radians).",
476 }),
477 "cos" => Some(BuiltinDoc {
478 signature: "cos EXPR\ncos",
479 description: "Returns the cosine of EXPR (expressed in radians).",
480 }),
481 "atan2" => Some(BuiltinDoc {
482 signature: "atan2 Y, X",
483 description: "Returns the arctangent of Y/X in the range -PI to PI.",
484 }),
485 "rand" => Some(BuiltinDoc {
486 signature: "rand EXPR\nrand",
487 description: "Returns a random fractional number greater than or equal to 0 and less than EXPR (default 1).",
488 }),
489 "srand" => Some(BuiltinDoc {
490 signature: "srand EXPR\nsrand",
491 description: "Sets the random number seed for the rand operator.",
492 }),
493
494 "stat" => Some(BuiltinDoc {
496 signature: "stat FILEHANDLE\nstat EXPR",
497 description: "Returns a 13-element list (dev, ino, mode, nlink, uid, gid, rdev, size, atime, mtime, ctime, blksize, blocks) or an empty list on failure.",
498 }),
499 "lstat" => Some(BuiltinDoc {
500 signature: "lstat FILEHANDLE\nlstat EXPR",
501 description: "Like stat, but if the last component of the filename is a symbolic link, stats the link itself.",
502 }),
503 "chmod" => Some(BuiltinDoc {
504 signature: "chmod MODE, LIST",
505 description: "Changes the permissions of a list of files. Returns the number of files successfully changed.",
506 }),
507 "chown" => Some(BuiltinDoc {
508 signature: "chown UID, GID, LIST",
509 description: "Changes the owner and group of a list of files.",
510 }),
511 "unlink" => Some(BuiltinDoc {
512 signature: "unlink LIST\nunlink",
513 description: "Deletes a list of files. Returns the number of files successfully deleted.",
514 }),
515 "rename" => Some(BuiltinDoc {
516 signature: "rename OLDNAME, NEWNAME",
517 description: "Renames a file. Returns true on success, false otherwise.",
518 }),
519 "mkdir" => Some(BuiltinDoc {
520 signature: "mkdir FILENAME, MODE\nmkdir FILENAME",
521 description: "Creates the directory specified by FILENAME. Returns true on success.",
522 }),
523 "rmdir" => Some(BuiltinDoc {
524 signature: "rmdir FILENAME\nrmdir",
525 description: "Deletes the directory if it is empty. Returns true on success.",
526 }),
527 "opendir" => Some(BuiltinDoc {
528 signature: "opendir DIRHANDLE, EXPR",
529 description: "Opens a directory for reading by readdir.",
530 }),
531 "readdir" => Some(BuiltinDoc {
532 signature: "readdir DIRHANDLE",
533 description: "Returns the next entry (or entries in list context) from the directory.",
534 }),
535 "closedir" => Some(BuiltinDoc {
536 signature: "closedir DIRHANDLE",
537 description: "Closes a directory opened by opendir.",
538 }),
539 "link" => Some(BuiltinDoc {
540 signature: "link OLDFILE, NEWFILE",
541 description: "Creates a new hard link for an existing file.",
542 }),
543 "symlink" => Some(BuiltinDoc {
544 signature: "symlink OLDFILE, NEWFILE",
545 description: "Creates a new symbolic link for an existing file.",
546 }),
547 "readlink" => Some(BuiltinDoc {
548 signature: "readlink EXPR\nreadlink",
549 description: "Returns the value of a symbolic link.",
550 }),
551 "chdir" => Some(BuiltinDoc {
552 signature: "chdir EXPR\nchdir",
553 description: "Changes the working directory to EXPR (or home directory if omitted).",
554 }),
555 "glob" => Some(BuiltinDoc {
556 signature: "glob EXPR\nglob",
557 description: "Returns the filenames matching the shell-style glob pattern EXPR.",
558 }),
559
560 "system" => Some(BuiltinDoc {
562 signature: "system LIST\nsystem PROGRAM LIST",
563 description: "Executes a system command and returns the exit status. The return value is the exit status of the program as returned by the wait call.",
564 }),
565 "exec" => Some(BuiltinDoc {
566 signature: "exec LIST\nexec PROGRAM LIST",
567 description: "Replaces the current process with an external command. Never returns on success.",
568 }),
569 "fork" => Some(BuiltinDoc {
570 signature: "fork",
571 description: "Creates a child process. Returns the child pid to the parent, 0 to the child, or undef on failure.",
572 }),
573 "wait" => Some(BuiltinDoc {
574 signature: "wait",
575 description: "Waits for a child process to terminate and returns the pid of the deceased process.",
576 }),
577 "waitpid" => Some(BuiltinDoc {
578 signature: "waitpid PID, FLAGS",
579 description: "Waits for a particular child process to terminate and returns the pid.",
580 }),
581 "kill" => Some(BuiltinDoc {
582 signature: "kill SIGNAL, LIST",
583 description: "Sends a signal to a list of processes. Returns the number of processes signalled.",
584 }),
585 "sleep" => Some(BuiltinDoc {
586 signature: "sleep EXPR\nsleep",
587 description: "Causes the script to sleep for EXPR seconds (or forever if no argument).",
588 }),
589 "alarm" => Some(BuiltinDoc {
590 signature: "alarm SECONDS\nalarm",
591 description: "Arranges to have a SIGALRM delivered after SECONDS seconds.",
592 }),
593
594 "pack" => Some(BuiltinDoc {
596 signature: "pack TEMPLATE, LIST",
597 description: "Takes a list of values and packs it into a binary string according to TEMPLATE.",
598 }),
599 "unpack" => Some(BuiltinDoc {
600 signature: "unpack TEMPLATE, EXPR",
601 description: "Takes a binary string and expands it into a list of values according to TEMPLATE.",
602 }),
603 "crypt" => Some(BuiltinDoc {
604 signature: "crypt PLAINTEXT, SALT",
605 description: "Encrypts a string using the system crypt() function.",
606 }),
607
608 "time" => Some(BuiltinDoc {
610 signature: "time",
611 description: "Returns the number of seconds since the epoch (January 1, 1970 UTC).",
612 }),
613 "localtime" => Some(BuiltinDoc {
614 signature: "localtime EXPR\nlocaltime",
615 description: "Converts a time value to a 9-element list with the time analyzed for the local time zone. In scalar context returns a ctime(3) string.",
616 }),
617 "gmtime" => Some(BuiltinDoc {
618 signature: "gmtime EXPR\ngmtime",
619 description: "Like localtime but uses Greenwich Mean Time (UTC). In list context returns a 9-element time list (sec, min, hour, mday, mon, year, wday, yday, isdst). In scalar context returns a ctime(3)-style string.",
620 }),
621
622 "prototype" => Some(BuiltinDoc {
624 signature: "prototype FUNCTION",
625 description: "Returns the prototype of a function as a string, or undef if the function has no prototype.",
626 }),
627 "local" => Some(BuiltinDoc {
628 signature: "local EXPR",
629 description: "Temporarily localizes the listed global variables to the enclosing block. The original values are restored at the end of the block.",
630 }),
631 "my" => Some(BuiltinDoc {
632 signature: "my VARLIST\nmy TYPE VARLIST",
633 description: "Declares lexically scoped variables. Variables are visible only within the enclosing block.",
634 }),
635 "our" => Some(BuiltinDoc {
636 signature: "our VARLIST",
637 description: "Declares package variables visible in the current lexical scope without qualifying the name.",
638 }),
639 "state" => Some(BuiltinDoc {
640 signature: "state VARLIST",
641 description: "Declares lexically scoped variables that persist across calls to the enclosing subroutine (like C static variables).",
642 }),
643 "BEGIN" => Some(BuiltinDoc {
644 signature: "BEGIN { BLOCK }",
645 description: "Executed at **compile time**, before the rest of the program runs. \
646 Used to initialize modules, set up the symbol table, or run code \
647 that must complete before compilation continues. Multiple BEGIN \
648 blocks run in the order they appear in source.",
649 }),
650 "END" => Some(BuiltinDoc {
651 signature: "END { BLOCK }",
652 description: "Executed at **program exit**, after the main program finishes (including \
653 `die` and `exit`). Used for cleanup. Multiple END blocks run in \
654 reverse order of definition. `$?` holds the exit status.",
655 }),
656 "INIT" => Some(BuiltinDoc {
657 signature: "INIT { BLOCK }",
658 description: "Executed after compilation completes but **before** the main program \
659 runs. Runs in first-seen order. Unlike BEGIN, INIT sees the fully \
660 compiled symbol table.",
661 }),
662 "CHECK" => Some(BuiltinDoc {
663 signature: "CHECK { BLOCK }",
664 description: "Executed at the **end of compilation**, after all BEGIN blocks. Runs \
665 in reverse order of definition. Used by modules that need to inspect \
666 or modify the compiled program before it runs (e.g. B::* modules).",
667 }),
668 "UNITCHECK" => Some(BuiltinDoc {
669 signature: "UNITCHECK { BLOCK }",
670 description: "Executed at the **end of the compilation unit** that defined it \
671 (file, string eval, or require). Runs in reverse order of definition \
672 within that unit. More granular than CHECK — each required file's \
673 UNITCHECK runs before the requiring file's UNITCHECK.",
674 }),
675
676 _ => None,
677 }
678}
679
680pub fn get_moose_type_documentation(type_str: &str) -> Option<BuiltinDoc> {
689 let base = type_str.split('[').next().unwrap_or(type_str).trim();
691
692 match base {
693 "Any" => Some(BuiltinDoc {
695 signature: "Any",
696 description: "The root type. Every value passes this constraint.",
697 }),
698 "Item" => Some(BuiltinDoc {
699 signature: "Item",
700 description: "Synonym for Any. Used as a base for the type hierarchy.",
701 }),
702 "Undef" => Some(BuiltinDoc { signature: "Undef", description: "Accepts only undef." }),
704 "Defined" => Some(BuiltinDoc {
705 signature: "Defined",
706 description: "Accepts any defined value (anything that is not undef).",
707 }),
708 "Value" => Some(BuiltinDoc {
710 signature: "Value",
711 description: "Accepts any defined, non-reference value (scalars and strings).",
712 }),
713 "Bool" => Some(BuiltinDoc {
714 signature: "Bool",
715 description: "Accepts 1, 0, the empty string '', or undef — Perl's boolean-ish values.",
716 }),
717 "Str" => Some(BuiltinDoc {
719 signature: "Str",
720 description: "Accepts any defined, non-reference scalar value (a string or number).",
721 }),
722 "Num" => Some(BuiltinDoc {
723 signature: "Num",
724 description: "Accepts any value that looks like a number (integer or float).",
725 }),
726 "Int" => Some(BuiltinDoc {
727 signature: "Int",
728 description: "Accepts only integer values (no decimal point).",
729 }),
730 "ClassName" => Some(BuiltinDoc {
731 signature: "ClassName",
732 description: "Accepts a string that is the name of a loaded Perl package/class.",
733 }),
734 "RoleName" => Some(BuiltinDoc {
735 signature: "RoleName",
736 description: "Accepts a string that is the name of a loaded Moose role.",
737 }),
738 "Ref" => Some(BuiltinDoc { signature: "Ref", description: "Accepts any reference." }),
740 "ScalarRef" => Some(BuiltinDoc {
741 signature: "ScalarRef[TYPE]",
742 description: "Accepts a scalar reference. Optionally parametrized: ScalarRef[Int] requires the referent to satisfy Int.",
743 }),
744 "ArrayRef" => Some(BuiltinDoc {
745 signature: "ArrayRef[TYPE]",
746 description: "Accepts an array reference. Optionally parametrized: ArrayRef[Int] requires all elements to satisfy Int.",
747 }),
748 "HashRef" => Some(BuiltinDoc {
749 signature: "HashRef[TYPE]",
750 description: "Accepts a hash reference. Optionally parametrized: HashRef[Str] requires all values to satisfy Str.",
751 }),
752 "CodeRef" => Some(BuiltinDoc {
753 signature: "CodeRef",
754 description: "Accepts a code reference (subroutine reference).",
755 }),
756 "RegexpRef" => Some(BuiltinDoc {
757 signature: "RegexpRef",
758 description: "Accepts a compiled regular expression reference (qr//).",
759 }),
760 "GlobRef" => {
761 Some(BuiltinDoc { signature: "GlobRef", description: "Accepts a glob reference." })
762 }
763 "FileHandle" => Some(BuiltinDoc {
764 signature: "FileHandle",
765 description: "Accepts an IO object or a glob reference that can be used as a filehandle.",
766 }),
767 "Object" => Some(BuiltinDoc {
769 signature: "Object",
770 description: "Accepts any blessed reference (an object).",
771 }),
772 "Maybe" => Some(BuiltinDoc {
774 signature: "Maybe[TYPE]",
775 description: "Accepts undef or any value satisfying TYPE. Useful for optional attributes: Maybe[Str] accepts either a string or undef.",
776 }),
777 "InstanceOf" => Some(BuiltinDoc {
779 signature: "InstanceOf[CLASSNAME]",
780 description: "Accepts a blessed object that is an instance of CLASSNAME.",
781 }),
782 "ConsumerOf" => Some(BuiltinDoc {
783 signature: "ConsumerOf[ROLENAME]",
784 description: "Accepts a blessed object that consumes ROLENAME.",
785 }),
786 "HasMethods" => Some(BuiltinDoc {
787 signature: "HasMethods[METHOD, ...]",
788 description: "Accepts a blessed object that has all the listed methods.",
789 }),
790 "Dict" => Some(BuiltinDoc {
791 signature: "Dict[KEY => TYPE, ...]",
792 description: "Accepts a hash reference matching a specific key/type schema (Type::Tiny).",
793 }),
794 "Tuple" => Some(BuiltinDoc {
795 signature: "Tuple[TYPE, ...]",
796 description: "Accepts an array reference matching a specific positional type schema (Type::Tiny).",
797 }),
798 "Map" => Some(BuiltinDoc {
799 signature: "Map[KEYTYPE, VALUETYPE]",
800 description: "Accepts a hash reference where keys satisfy KEYTYPE and values satisfy VALUETYPE (Type::Tiny).",
801 }),
802 "Enum" => Some(BuiltinDoc {
803 signature: "Enum[VALUE, ...]",
804 description: "Accepts a string that is one of the listed values (Type::Tiny).",
805 }),
806
807 _ => None,
808 }
809}
810
811pub fn get_attribute_documentation(attr: &str) -> Option<BuiltinDoc> {
819 let name = attr.trim_start_matches(':');
821
822 match name {
823 "lvalue" => Some(BuiltinDoc {
824 signature: ":lvalue",
825 description: "Marks a subroutine as an lvalue subroutine. The return value can be assigned to, enabling constructs like `foo() = 42;`.",
826 }),
827 "method" => Some(BuiltinDoc {
828 signature: ":method",
829 description: "Marks a subroutine as a method. Used by some attribute handlers to modify dispatch or prototype checking.",
830 }),
831 "prototype" => Some(BuiltinDoc {
832 signature: ":prototype(PROTO)",
833 description: "Sets the prototype of a subroutine. Controls how Perl parses calls to the sub (e.g. `prototype($$)` for two scalar args).",
834 }),
835 "const" => Some(BuiltinDoc {
836 signature: ":const",
837 description: "Marks a subroutine as a constant. The value is computed once and cached; subsequent calls return the cached value immutably.",
838 }),
839 "shared" => Some(BuiltinDoc {
840 signature: ":shared",
841 description: "Marks a variable or subroutine as shared across threads (requires `threads::shared`). The variable is accessible from all threads.",
842 }),
843 "weak_ref" => Some(BuiltinDoc {
844 signature: ":weak_ref",
845 description: "Marks a Moose/Moo attribute as a weak reference. The stored reference will not prevent the referent from being garbage-collected.",
846 }),
847 "overload" => Some(BuiltinDoc {
848 signature: ":overload(OP)",
849 description: "Declares that a subroutine implements an operator overload for OP.",
850 }),
851 _ => None,
852 }
853}
854
855#[derive(Debug, Clone)]
861pub struct ExceptionContext {
862 pub error_variable: Option<String>,
864 pub preferred_alternative: Option<String>,
867}
868
869pub fn is_exception_function(name: &str) -> bool {
886 matches!(name, "die" | "warn" | "croak" | "carp" | "confess" | "cluck")
887}
888
889pub fn get_exception_context(name: &str) -> Option<ExceptionContext> {
909 match name {
910 "die" => Some(ExceptionContext {
911 error_variable: Some("$@".to_string()),
912 preferred_alternative: Some("Carp::croak".to_string()),
913 }),
914 "warn" => Some(ExceptionContext {
915 error_variable: None,
916 preferred_alternative: Some("Carp::carp".to_string()),
917 }),
918 "croak" | "confess" => Some(ExceptionContext {
919 error_variable: Some("$@".to_string()),
920 preferred_alternative: None,
921 }),
922 "carp" | "cluck" => {
923 Some(ExceptionContext { error_variable: None, preferred_alternative: None })
924 }
925 _ => None,
926 }
927}
928
929#[cfg(test)]
930mod tests {
931 use super::get_builtin_documentation;
932
933 #[test]
934 fn test_get_builtin_documentation_begin() -> Result<(), Box<dyn std::error::Error>> {
935 let doc = get_builtin_documentation("BEGIN").ok_or("BEGIN should have docs")?;
936 assert!(
937 doc.description.contains("compile time") || doc.description.contains("compile-time"),
938 "BEGIN doc should mention compile time, got: {}",
939 doc.description
940 );
941 Ok(())
942 }
943
944 #[test]
945 fn test_get_builtin_documentation_end() -> Result<(), Box<dyn std::error::Error>> {
946 let doc = get_builtin_documentation("END").ok_or("END should have docs")?;
947 assert!(
948 doc.description.contains("exit") || doc.description.contains("cleanup"),
949 "END doc should mention exit or cleanup, got: {}",
950 doc.description
951 );
952 Ok(())
953 }
954
955 #[test]
956 fn test_get_builtin_documentation_check() -> Result<(), Box<dyn std::error::Error>> {
957 let doc = get_builtin_documentation("CHECK").ok_or("CHECK should have docs")?;
958 assert!(
959 doc.description.contains("compilation") || doc.description.contains("compile"),
960 "CHECK doc should mention compilation, got: {}",
961 doc.description
962 );
963 Ok(())
964 }
965
966 #[test]
967 fn test_get_builtin_documentation_init() -> Result<(), Box<dyn std::error::Error>> {
968 let doc = get_builtin_documentation("INIT").ok_or("INIT should have docs")?;
969 assert!(
970 doc.description.contains("compilation") || doc.description.contains("before"),
971 "INIT doc should mention post-compile execution, got: {}",
972 doc.description
973 );
974 Ok(())
975 }
976
977 #[test]
978 fn test_get_builtin_documentation_unitcheck() -> Result<(), Box<dyn std::error::Error>> {
979 let doc = get_builtin_documentation("UNITCHECK").ok_or("UNITCHECK should have docs")?;
980 assert!(
981 doc.description.contains("compilation unit") || doc.description.contains("unit"),
982 "UNITCHECK doc should mention compilation unit scope, got: {}",
983 doc.description
984 );
985 Ok(())
986 }
987}