Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Warnings

A warning reports code the checker accepts but that has a simpler correct form. Warnings never reject a program and never change an exit status.

Checking reports them: plenty --check, plenty --check-module, plenty check, and the language server. Compiling and running do not, because a program cache hit skips the frontend that finds them. Run a check to see a program’s warnings.

The command line prints each warning on stderr after the check passes, sorted by file and position:

main.plenty:3:14: warning: `copy(original)?` is redundant: `str` copies on assignment and when passed or returned; write `original` [redundant-copy]

The bracketed name is the lint’s stable identifier. The language server publishes the same text with warning severity and the name as the diagnostic code. A check that fails reports only its errors.

There is no option to disable a warning or silence one site. Every warning has a replacement that keeps the program’s behavior; apply it.

std code and the bodies of generic functions and generic class or enum methods report no warnings: a generic body is checked per instance, and code that is redundant for one type argument can be needed for another.

Lints

NameReports
redundant-copycopy(x)? when the type of x already copies

redundant-copy

A value whose type copies is copied by plain use: assigning, passing, or returning it. copy(x)? then produces the same value as x, with a Result and an AllocError path that cannot add anything. The lint uses the checker’s own copy rule for the instantiated type, so str, numbers, bool, bytes, copyable tuples and sums, and @copy classes such as Point[i64] are reported, and a list or ordinary class is not.

It reports copy(x)? only when writing x instead keeps the type, evaluates the same expression once, and moves nothing. x must be:

  • a local that is not a reference or view;
  • a field path from such a local, such as record.name; a field read through a reference, such as self.name in a self method, is left alone;
  • a list, dictionary, or tuple element, such as words[i];
  • a string, number, boolean, or bytes literal.

For a name or field path the message spells the replacement. These are left alone:

  • copy(x) used as a Result, for example matched or stored, rather than propagated with ?;
  • a reference argument such as copy(r)? for r: &str, or copy(&x)?; *r reads a copyable value through a reference;
  • a call or other computed argument, such as copy(f())?;
  • copy(x)? of a type that moves, which makes an independent value.
def main() -> Result[(), AllocError | IoError]:
    original = "hello"
    copied = original
    print(original, copied)?
    Ok(())
hello hello

Removing a copy(...)? can leave a function’s AllocError unused. The function still checks; narrow its declared error type if nothing else needs it.