throws

add_throws try_catch

Annotations for documenting and enforcing throwing functions.

This package is the annotation layer. It works with:

  • throws_plugin: analyzer plugin that enforces annotations and call handling.
  • throws_collector: optional heuristic tool that can generate maps for external APIs to improve plugin accuracy.

Install

Add the dependency to your pubspec.yaml.

Usage

Annotate functions that can throw:

@Throws(reason: 'Parsing input failed', errors: {FormatException, RangeError})
int parsePositiveInt(String input) {
  final value = int.parse(input);
  if (value < 0) {
    throw RangeError('Value must be non-negative');
  }
  return value;
}

You can also use the shorthand constant:

@throws
void mightThrow() {
  throw Exception('x');
}

Expected errors

Use errors to declare the error types callers should handle. This is a set of Type literals.

@Throws(reason: 'Reason', errors: {StateError})
int singleValue(Iterable<int> values) => values.single;

Examples

Missing annotation

Bad (missing @Throws):

int parsePositiveInt(String input) {
  final value = int.parse(input);
  if (value < 0) {
    throw RangeError('Value must be non-negative');
  }
  return value;
}

Good:

@Throws(reason: 'Parsing input failed', errors: {RangeError})
int parsePositiveInt(String input) {
  final value = int.parse(input);
  if (value < 0) {
    throw RangeError('Value must be non-negative');
  }
  return value;
}

Calling a throwing function without handling

Bad (no try/catch and no @Throws on caller):

@Throws(reason: 'May throw', errors: {StateError})
int singleValue(Iterable<int> values) => values.single;

int readOne(Iterable<int> values) {
  return singleValue(values);
}

Good (handle it):

@Throws(reason: 'May throw', errors: {StateError})
int singleValue(Iterable<int> values) => values.single;

int readOne(Iterable<int> values) {
  try {
    return singleValue(values);
  } on StateError {
    return -1;
  }
}

Good (or declare it):

@Throws(reason: 'May throw', errors: {StateError})
int singleValue(Iterable<int> values) => values.single;

@Throws(reason: 'Pass-through', errors: {StateError})
int readOne(Iterable<int> values) => singleValue(values);

Annotation mismatch

Bad (declared errors do not match):

@Throws(reason: 'Wrong error set', errors: {ArgumentError})
void risky() {
  throw StateError('boom');
}

Good:

@Throws(reason: 'Right error set', errors: {StateError})
void risky() {
  throw StateError('boom');
}

How it all works together

  1. You annotate functions with @Throws or @throws.
  2. throws_plugin inspects your code and:
    • Requires annotations on functions that throw.
    • Requires try/catch or annotations when calling throwing functions.
    • Validates that declared errors match what is thrown.
  3. throws_plugin can also read a throws.yaml file at your package root to learn about throwing members outside your package (SDK or dependencies).
  4. throws_collector can generate those maps, but it is optional and best-effort.

This package does not perform analysis itself. It only provides the annotations and shared types for the plugin.

Libraries

throws
Annotations for documenting throwing functions.