From 41ea335160d69e2acf9e7a79a5663ae7085a89f3 Mon Sep 17 00:00:00 2001 From: Remi Rousselet Date: Tue, 25 Feb 2025 21:25:57 +0100 Subject: [PATCH 1/3] Ask mutations to manually call state= And separate mutations' return value from the state --- packages/riverpod/lib/src/mutation.dart | 166 +++--- .../lib/src/errors.dart | 1 + .../lib/src/nodes.dart | 1 - .../lib/src/nodes/providers/function.dart | 2 +- .../lib/src/nodes/providers/notifier.dart | 55 +- .../lib/src/nodes/providers/providers.dart | 46 +- .../lib/src/json_generator.dart | 1 - .../lib/src/riverpod_generator.dart | 1 - .../lib/src/templates/mutation.dart | 24 +- .../lib/src/templates/notifier.dart | 1 - .../lib/src/templates/provider.dart | 1 - packages/riverpod_generator/lib/src/type.dart | 23 - .../test/integration/mutation.dart | 76 ++- .../test/integration/mutation.g.dart | 521 +++++++++++++++--- 14 files changed, 674 insertions(+), 245 deletions(-) delete mode 100644 packages/riverpod_generator/lib/src/type.dart diff --git a/packages/riverpod/lib/src/mutation.dart b/packages/riverpod/lib/src/mutation.dart index ba4de62f8..4cbde5dc8 100644 --- a/packages/riverpod/lib/src/mutation.dart +++ b/packages/riverpod/lib/src/mutation.dart @@ -59,24 +59,22 @@ const mutationZoneKey = #_mutation; /// Once we have defined a notifier, we can add a mutation to it. /// To do so, we define a method in the notifier class and annotate it with [Mutation]: /// -/// **Note**: -/// That method **must** return a [Future] that resolves with a value of the same -/// type as the notifier's state. -/// In our above example, the state of our notifier is `List`, so the mutation -/// must return a `Future>`. -/// /// ```dart /// @riverpod /// class TodoListNotifier extends $ExampleNotifier { /// /* ... */ /// /// @mutation -/// Future> addTodo(Todo todo) async { +/// Future addTodo(String task) async { +/// final todo = Todo(task); /// /* to-do: Make an HTTP post request to notify the server about the added todo */ /// /// // Mutations are expected to return the new state for our notifier. /// // Riverpod will then assign this value to `this.state` -/// return [...await future, todo]; +/// state = AsyncData([...await future, todo]); +/// +/// // We return the new todo, so that the UI can display it. +/// return todo; /// } /// } /// ``` @@ -115,7 +113,7 @@ const mutationZoneKey = #_mutation; /// /// return ElevatedButton( /// // Pressing the button will call `TodoListNotifier.addTodo` -/// onPressed: () => addTodo(Todo('Buy milk')), +/// onPressed: () => addTodo('Buy milk'), /// ); /// ``` /// @@ -142,7 +140,7 @@ const mutationZoneKey = #_mutation; /// case ErrorMutationState(:final error): /// print('The mutation has failed with $error'); /// case SuccessMutationState(:final value): -/// print('The mutation has succeeded, and the new state is $value'); +/// print('The mutation has succeeded, and $value was returned'); /// } /// ``` /// @@ -229,7 +227,7 @@ const mutationZoneKey = #_mutation; /// return ElevatedButton( /// onPressed: addTodo.state is PendingMutationState /// ? null // If the mutation is in progress, disable the button -/// : () => addTodo(Todo('Buy milk')), // Otherwise enable the button +/// : () => addTodo('Buy milk'), // Otherwise enable the button /// ); /// } /// } @@ -355,54 +353,88 @@ abstract class MutationBase { @internal abstract class $SyncMutationBase< - StateT, - MutationT extends $SyncMutationBase, - ClassT extends NotifierBase> - extends _MutationBase { + ResultT, + MutationT extends $SyncMutationBase, + ClassT extends NotifierBase> + extends _MutationBase { $SyncMutationBase({super.state, super.key}); - @override - void setData(StateT value) { - element.setStateResult($Result.data(value)); + @protected + ResultT mutate( + Invocation invocation, + ResultT Function(ClassT clazz) cb, + ) { + return _run(invocation, (_, notifier) => cb(notifier)); } } @internal abstract class $AsyncMutationBase< - StateT, - MutationT extends $AsyncMutationBase, - ClassT extends NotifierBase>> - extends _MutationBase, MutationT, ClassT> { + ResultT, + MutationT extends $AsyncMutationBase, + ClassT extends NotifierBase> + extends _MutationBase { $AsyncMutationBase({super.state, super.key}); - @override - void setData(StateT value) { - element.setStateResult($Result.data(AsyncData(value))); + @protected + Future mutate( + Invocation invocation, + FutureOr Function(ClassT clazz) cb, + ) { + return _run( + invocation, + (mutationContext, notifier) async { + // ! is safe because of the flush() above + final key = Object(); + try { + _setState( + mutationContext, + copyWith(PendingMutationState._(), key: key), + ); + + final result = await cb(notifier); + if (key == _currentKey) { + _setState( + mutationContext, + copyWith(SuccessMutationState._(result)), + ); + } + + return result; + } catch (err, stack) { + if (key == _currentKey) { + _setState( + mutationContext, + copyWith(ErrorMutationState._(err, stack)), + ); + } + + rethrow; + } + }, + ); } } abstract class _MutationBase< - ValueT, - StateT, - MutationT extends _MutationBase, - ClassT extends NotifierBase> implements MutationBase { - _MutationBase({MutationState? state, this.key}) - : state = state ?? IdleMutationState._() { + ResultT, + MutationT extends _MutationBase, + ClassT extends NotifierBase> implements MutationBase { + _MutationBase({MutationState? state, this.key}) + : state = state ?? IdleMutationState._() { listenable.onCancel = _scheduleAutoReset; } @override - final MutationState state; + final MutationState state; final Object? key; - $ClassProviderElement get element; + $ClassProviderElement get element; $ElementLense get listenable; Object? get _currentKey => listenable.result?.stateOrNull?.key; - MutationT copyWith(MutationState state, {Object? key}); - - void setData(ValueT value); + MutationT copyWith(MutationState state, {Object? key}); void _scheduleAutoReset() { Future.microtask(() { @@ -414,15 +446,29 @@ abstract class _MutationBase< @override void reset() { - if (state is IdleMutationState) return; + if (state is IdleMutationState) return; - listenable.result = ResultData(copyWith(IdleMutationState._())); + listenable.result = ResultData(copyWith(IdleMutationState._())); final context = ProviderObserverContext(element.origin, element.container); _notifyObserver((obs) => obs.mutationReset(context)); } + T _run( + Invocation invocation, + T Function(MutationContext mutationContext, ClassT notifier) cb, + ) { + element.flush(); + final notifier = element.classListenable.value; + final mutationContext = MutationContext(invocation, notifier); + + return runZoned( + zoneValues: {mutationZoneKey: mutationContext}, + () => cb(mutationContext, notifier), + ); + } + void _notifyObserver(void Function(ProviderObserver obs) cb) { for (final observer in element.container.observers) { runUnaryGuarded(cb, observer); @@ -463,50 +509,6 @@ abstract class _MutationBase< } } - @protected - Future mutateAsync( - Invocation invocation, - FutureOr Function(ClassT clazz) cb, - ) { - element.flush(); - final notifier = element.classListenable.value; - final mutationContext = MutationContext(invocation, notifier); - - return runZoned( - zoneValues: {mutationZoneKey: mutationContext}, - () async { - // ! is safe because of the flush() above - final key = Object(); - try { - _setState( - mutationContext, - copyWith(PendingMutationState._(), key: key), - ); - - final result = await cb(notifier); - if (key == _currentKey) { - _setState( - mutationContext, - copyWith(SuccessMutationState._(result)), - ); - } - setData(result); - - return result; - } catch (err, stack) { - if (key == _currentKey) { - _setState( - mutationContext, - copyWith(ErrorMutationState._(err, stack)), - ); - } - - rethrow; - } - }, - ); - } - @override String toString() => '$runtimeType#${shortHash(this)}($state)'; } diff --git a/packages/riverpod_analyzer_utils/lib/src/errors.dart b/packages/riverpod_analyzer_utils/lib/src/errors.dart index eed83d768..787f138b7 100644 --- a/packages/riverpod_analyzer_utils/lib/src/errors.dart +++ b/packages/riverpod_analyzer_utils/lib/src/errors.dart @@ -22,6 +22,7 @@ enum RiverpodAnalysisErrorCode { mutationReturnTypeMismatch, mutationIsStatic, mutationIsAbstract, + unsupportedMutationReturnType, } class RiverpodAnalysisError { diff --git a/packages/riverpod_analyzer_utils/lib/src/nodes.dart b/packages/riverpod_analyzer_utils/lib/src/nodes.dart index aa7381b60..862a4087f 100644 --- a/packages/riverpod_analyzer_utils/lib/src/nodes.dart +++ b/packages/riverpod_analyzer_utils/lib/src/nodes.dart @@ -10,7 +10,6 @@ import 'package:analyzer/dart/ast/token.dart'; import 'package:analyzer/dart/ast/visitor.dart'; import 'package:analyzer/dart/constant/value.dart'; import 'package:analyzer/dart/element/element.dart'; -import 'package:analyzer/dart/element/nullability_suffix.dart'; import 'package:analyzer/dart/element/type.dart'; import 'package:collection/collection.dart'; import 'package:crypto/crypto.dart'; diff --git a/packages/riverpod_analyzer_utils/lib/src/nodes/providers/function.dart b/packages/riverpod_analyzer_utils/lib/src/nodes/providers/function.dart index 0c903c5f5..3fc1f851a 100644 --- a/packages/riverpod_analyzer_utils/lib/src/nodes/providers/function.dart +++ b/packages/riverpod_analyzer_utils/lib/src/nodes/providers/function.dart @@ -34,7 +34,7 @@ extension FunctionalProviderDeclarationX on FunctionDeclaration { annotation: riverpod, createdTypeNode: createdTypeNode, exposedTypeNode: exposedTypeNode, - valueTypeNode: _getValueType(createdTypeNode, element.library), + valueTypeNode: _getValueType(createdTypeNode), ); }); } diff --git a/packages/riverpod_analyzer_utils/lib/src/nodes/providers/notifier.dart b/packages/riverpod_analyzer_utils/lib/src/nodes/providers/notifier.dart index 63593c840..8c247e501 100644 --- a/packages/riverpod_analyzer_utils/lib/src/nodes/providers/notifier.dart +++ b/packages/riverpod_analyzer_utils/lib/src/nodes/providers/notifier.dart @@ -85,7 +85,7 @@ extension ClassBasedProviderDeclarationX on ClassDeclaration { return e.annotationOfType(riverpodPersistType, exact: false) != null; }); - final valueTypeNode = _getValueType(createdTypeNode, element.library); + final valueTypeNode = _getValueType(createdTypeNode); final classBasedProviderDeclaration = ClassBasedProviderDeclaration._( name: name, node: this, @@ -180,40 +180,35 @@ extension MutationMethodDeclarationX on MethodDeclaration { ?.returnType; if (expectedReturnType == null) return null; - final expectedValueType = _getValueType( - expectedReturnType, - element.library, - ); + final expectedValueType = _getValueType(expectedReturnType); if (expectedValueType == null) return null; - final expectedType = - element.library.typeProvider.futureOrElement.instantiate( - typeArguments: [expectedValueType.type!], - nullabilitySuffix: NullabilitySuffix.none, - ); - - final actualType = element.returnType; - - final isAssignable = element.library.typeSystem.isAssignableTo( - actualType, - expectedType, - strictCasts: true, - ); - if (!isAssignable) { - errorReporter( - RiverpodAnalysisError( - 'The return type of mutations must match the type returned by the "build" method.', - targetNode: this, - targetElement: element, - code: RiverpodAnalysisErrorCode.mutationReturnTypeMismatch, - ), - ); - return null; + final returnType = this.returnType; + final createdType = SupportedCreatedType.from(returnType); + String? valueDisplayString; + switch (createdType) { + case SupportedCreatedType.future: + valueDisplayString = (returnType! as NamedType) + .typeArguments + ?.arguments + .firstOrNull + ?.toSource(); + case SupportedCreatedType.stream: + errorReporter( + RiverpodAnalysisError( + 'Mutations returning Streams are not supported', + code: RiverpodAnalysisErrorCode.unsupportedMutationReturnType, + ), + ); + case SupportedCreatedType.value: + valueDisplayString = returnType?.toSource(); } final mutation = Mutation._( node: this, element: mutationElement, + createdType: createdType, + valueDisplayType: valueDisplayString ?? '', ); return mutation; @@ -225,9 +220,13 @@ final class Mutation { Mutation._({ required this.node, required this.element, + required this.valueDisplayType, + required this.createdType, }); String get name => node.name.lexeme; + final String valueDisplayType; + final SupportedCreatedType createdType; final MethodDeclaration node; final MutationElement element; } diff --git a/packages/riverpod_analyzer_utils/lib/src/nodes/providers/providers.dart b/packages/riverpod_analyzer_utils/lib/src/nodes/providers/providers.dart index 4ca651d43..02af971d8 100644 --- a/packages/riverpod_analyzer_utils/lib/src/nodes/providers/providers.dart +++ b/packages/riverpod_analyzer_utils/lib/src/nodes/providers/providers.dart @@ -26,6 +26,26 @@ extension GeneratorProviderDeclarationX on Declaration { } } +enum SupportedCreatedType { + future, + stream, + value; + + static SupportedCreatedType from(TypeAnnotation? type) { + final dartType = type?.type; + switch (dartType) { + case != null + when !dartType.isRaw && + (dartType.isDartAsyncFutureOr || dartType.isDartAsyncFuture): + return SupportedCreatedType.future; + case != null when !dartType.isRaw && dartType.isDartAsyncStream: + return SupportedCreatedType.stream; + case _: + return SupportedCreatedType.value; + } + } +} + sealed class GeneratorProviderDeclaration extends ProviderDeclaration { @override GeneratorProviderDeclarationElement get providerElement; @@ -49,6 +69,9 @@ sealed class GeneratorProviderDeclaration extends ProviderDeclaration { SourcedType? get exposedTypeNode; TypeAnnotation? get createdTypeNode; + SupportedCreatedType get createdType => + SupportedCreatedType.from(createdTypeNode); + String computeProviderHash() { final bytes = utf8.encode(node.toSource()); final digest = sha1.convert(bytes); @@ -136,21 +159,12 @@ SourcedType? _computeExposedType( ); } -TypeAnnotation? _getValueType( - TypeAnnotation? createdType, - LibraryElement library, -) { - if (createdType == null) return null; - final dartType = createdType.type!; - if (dartType.isRaw) return createdType; - - if (dartType.isDartAsyncFuture || - dartType.isDartAsyncFutureOr || - dartType.isDartAsyncStream) { - createdType as NamedType; - - return createdType.typeArguments?.arguments.firstOrNull; +TypeAnnotation? _getValueType(TypeAnnotation? createdType) { + switch (SupportedCreatedType.from(createdType)) { + case SupportedCreatedType.future: + case SupportedCreatedType.stream: + return (createdType! as NamedType).typeArguments?.arguments.firstOrNull; + case SupportedCreatedType.value: + return createdType; } - - return createdType; } diff --git a/packages/riverpod_generator/lib/src/json_generator.dart b/packages/riverpod_generator/lib/src/json_generator.dart index 55a674ae3..4eb076eba 100644 --- a/packages/riverpod_generator/lib/src/json_generator.dart +++ b/packages/riverpod_generator/lib/src/json_generator.dart @@ -11,7 +11,6 @@ import 'package:source_gen/source_gen.dart'; import 'models.dart'; import 'parse_generator.dart'; import 'riverpod_generator.dart'; -import 'type.dart'; @immutable class JsonGenerator extends ParserGenerator { diff --git a/packages/riverpod_generator/lib/src/riverpod_generator.dart b/packages/riverpod_generator/lib/src/riverpod_generator.dart index abbae9ba9..0d61eec8e 100644 --- a/packages/riverpod_generator/lib/src/riverpod_generator.dart +++ b/packages/riverpod_generator/lib/src/riverpod_generator.dart @@ -18,7 +18,6 @@ import 'templates/notifier.dart'; import 'templates/parameters.dart'; import 'templates/provider.dart'; import 'templates/provider_variable.dart'; -import 'type.dart'; String providerDocFor(Element element) { return element.documentationComment == null diff --git a/packages/riverpod_generator/lib/src/templates/mutation.dart b/packages/riverpod_generator/lib/src/templates/mutation.dart index 902569b52..af16fdd22 100644 --- a/packages/riverpod_generator/lib/src/templates/mutation.dart +++ b/packages/riverpod_generator/lib/src/templates/mutation.dart @@ -1,7 +1,7 @@ import 'package:riverpod_analyzer_utils/riverpod_analyzer_utils.dart'; +import 'package:source_gen/source_gen.dart'; import '../riverpod_generator.dart'; -import '../type.dart'; import 'element.dart'; import 'parameters.dart'; import 'template.dart'; @@ -19,14 +19,17 @@ class MutationTemplate extends Template { parameter: parameter.name.toString(), }); - final mutationBase = switch (provider.createdType) { + final mutationBase = switch (mutation.createdType) { SupportedCreatedType.future => r'$AsyncMutationBase', - SupportedCreatedType.stream => r'$AsyncMutationBase', + SupportedCreatedType.stream => throw InvalidGenerationSource( + 'Stream mutations are not supported', + element: mutation.node.declaredElement, + ), SupportedCreatedType.value => r'$SyncMutationBase', }; buffer.writeln(''' -sealed class ${mutation.generatedMutationInterfaceName} extends MutationBase<${provider.valueTypeDisplayString}> { +sealed class ${mutation.generatedMutationInterfaceName} extends MutationBase<${mutation.valueDisplayType}> { /// Starts the mutation. /// /// This will first set the state to [PendingMutationState], then @@ -36,19 +39,16 @@ sealed class ${mutation.generatedMutationInterfaceName} extends MutationBase<${p /// [SuccessMutationState] or [ErrorMutationState] based on if the method /// threw or not. /// - /// Lastly, if the method completes without throwing, the Notifier's state - /// will be updated with the new value. - /// /// **Note**: /// If the notifier threw in its constructor, the mutation won't start /// and [call] will throw. /// This should generally never happen though, as Notifiers are not supposed /// to have logic in their constructors. - Future<${provider.valueTypeDisplayString}> call${mutation.node.typeParameters.genericDefinitionDisplayString()}${mutation.node.parameters}; + ${mutation.node.returnType ?? ''} call${mutation.node.typeParameters.genericDefinitionDisplayString()}${mutation.node.parameters}; } final class ${mutation.generatedMutationImplName} - extends $mutationBase<${provider.valueTypeDisplayString}, ${mutation.generatedMutationImplName}, ${provider.name}> + extends $mutationBase<${mutation.valueDisplayType}, ${mutation.generatedMutationImplName}, ${provider.name}> implements ${mutation.generatedMutationInterfaceName} { ${mutation.generatedMutationImplName}(this.element, {super.state, super.key}); @@ -59,15 +59,15 @@ final class ${mutation.generatedMutationImplName} \$ElementLense<${mutation.generatedMutationImplName}> get listenable => element.${mutation.elementFieldName}; @override - Future<${provider.valueTypeDisplayString}> call${mutation.node.typeParameters.genericDefinitionDisplayString()}${mutation.node.parameters} { - return mutateAsync( + ${mutation.node.returnType ?? ''} call${mutation.node.typeParameters.genericDefinitionDisplayString()}${mutation.node.parameters} { + return mutate( ${_mutationInvocation()}, (\$notifier) => \$notifier.${mutation.name}${mutation.node.typeParameters.genericUsageDisplayString()}($parametersPassThrough), ); } @override - ${mutation.generatedMutationImplName} copyWith(MutationState<${provider.valueTypeDisplayString}> state, {Object? key}) => ${mutation.generatedMutationImplName}(element, state: state, key: key); + ${mutation.generatedMutationImplName} copyWith(MutationState<${mutation.valueDisplayType}> state, {Object? key}) => ${mutation.generatedMutationImplName}(element, state: state, key: key); } '''); } diff --git a/packages/riverpod_generator/lib/src/templates/notifier.dart b/packages/riverpod_generator/lib/src/templates/notifier.dart index fd061d8bb..7c385d61a 100644 --- a/packages/riverpod_generator/lib/src/templates/notifier.dart +++ b/packages/riverpod_generator/lib/src/templates/notifier.dart @@ -2,7 +2,6 @@ import 'package:riverpod_analyzer_utils/riverpod_analyzer_utils.dart'; import '../models.dart'; import '../riverpod_generator.dart'; -import '../type.dart'; import 'parameters.dart'; import 'template.dart'; diff --git a/packages/riverpod_generator/lib/src/templates/provider.dart b/packages/riverpod_generator/lib/src/templates/provider.dart index 46729b2fb..4286478d4 100644 --- a/packages/riverpod_generator/lib/src/templates/provider.dart +++ b/packages/riverpod_generator/lib/src/templates/provider.dart @@ -2,7 +2,6 @@ import 'package:riverpod_analyzer_utils/riverpod_analyzer_utils.dart'; import '../models.dart'; import '../riverpod_generator.dart'; -import '../type.dart'; import 'element.dart'; import 'parameters.dart'; import 'template.dart'; diff --git a/packages/riverpod_generator/lib/src/type.dart b/packages/riverpod_generator/lib/src/type.dart deleted file mode 100644 index 1883c4d61..000000000 --- a/packages/riverpod_generator/lib/src/type.dart +++ /dev/null @@ -1,23 +0,0 @@ -import 'package:riverpod_analyzer_utils/riverpod_analyzer_utils.dart'; - -extension SwitchCreatedType on GeneratorProviderDeclaration { - SupportedCreatedType get createdType { - final dartType = createdTypeNode?.type; - switch (dartType) { - case != null - when !dartType.isRaw && - (dartType.isDartAsyncFutureOr || dartType.isDartAsyncFuture): - return SupportedCreatedType.future; - case != null when !dartType.isRaw && dartType.isDartAsyncStream: - return SupportedCreatedType.stream; - case _: - return SupportedCreatedType.value; - } - } -} - -enum SupportedCreatedType { - future, - stream, - value, -} diff --git a/packages/riverpod_generator/test/integration/mutation.dart b/packages/riverpod_generator/test/integration/mutation.dart index ab8071f79..de0c74f40 100644 --- a/packages/riverpod_generator/test/integration/mutation.dart +++ b/packages/riverpod_generator/test/integration/mutation.dart @@ -2,19 +2,57 @@ import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'mutation.g.dart'; +class Todo {} + +@riverpod +class SyncTodoList extends _$SyncTodoList { + @override + List build() => []; + + @mutation + Todo addSync(Todo todo) { + state.add(todo); + return todo; + } + + @mutation + Future addAsync(Todo todo) async { + state.add(todo); + return todo; + } +} + +@riverpod +class AsyncTodoList extends _$AsyncTodoList { + @override + Future> build() async => []; + + @mutation + Todo addSync(Todo todo) { + state = AsyncData([...state.requireValue, todo]); + return todo; + } + + @mutation + Future addAsync(Todo todo) async { + state = AsyncData([...state.requireValue, todo]); + return todo; + } +} + @riverpod class Simple extends _$Simple { @override int build() => 0; @mutation - Future increment([int inc = 1]) async => state + inc; + Future increment([int inc = 1]) async => state += inc; @mutation - FutureOr incrementOr() => state + 1; + FutureOr incrementOr() => state++; @mutation - Future delegated(Future Function() fn) => fn(); + Future delegated(Future Function() fn) async => state = await fn(); } @riverpod @@ -23,10 +61,10 @@ class SimpleFamily extends _$SimpleFamily { int build(String arg) => 0; @mutation - Future increment([int inc = 1]) async => state + inc; + Future increment([int inc = 1]) async => state += inc; @mutation - FutureOr incrementOr() => state + 1; + FutureOr incrementOr() => state++; } @riverpod @@ -35,12 +73,18 @@ class SimpleAsync extends _$SimpleAsync { Future build() async => 0; @mutation - Future increment([int inc = 1]) async => (await future) + inc; + Future increment([int inc = 1]) async { + state = AsyncData((await future) + inc); + + return state.requireValue; + } @mutation Future delegated(Future Function() fn) async { await future; - return fn(); + state = AsyncData(await fn()); + + return state.requireValue; } } @@ -50,7 +94,10 @@ class SimpleAsync2 extends _$SimpleAsync2 { Stream build(String arg) => Stream.value(0); @mutation - Future increment() async => (await future) + 1; + Future increment() async { + state = AsyncData((await future) + 1); + return state.requireValue; + } } @riverpod @@ -59,7 +106,10 @@ class Generic extends _$Generic { Future build() async => 0; @mutation - Future increment() async => (await future) + 1; + Future increment() async { + state = AsyncData((await future) + 1); + return state.requireValue; + } } @riverpod @@ -68,8 +118,10 @@ class GenericMut extends _$GenericMut { Future build() async => 0; @mutation - Future increment(T value) async => - (await future) + value.ceil(); + Future increment(T value) async { + state = AsyncData((await future) + value.ceil()); + return state.requireValue; + } } @riverpod @@ -82,7 +134,7 @@ class FailingCtor extends _$FailingCtor { int build() => 0; @mutation - Future increment([int inc = 1]) async => state + inc; + Future increment([int inc = 1]) async => state += inc; } @riverpod diff --git a/packages/riverpod_generator/test/integration/mutation.g.dart b/packages/riverpod_generator/test/integration/mutation.g.dart index a409c974b..d6d6f790e 100644 --- a/packages/riverpod_generator/test/integration/mutation.g.dart +++ b/packages/riverpod_generator/test/integration/mutation.g.dart @@ -6,6 +6,431 @@ part of 'mutation.dart'; // RiverpodGenerator // ************************************************************************** +@ProviderFor(SyncTodoList) +const syncTodoListProvider = SyncTodoListProvider._(); + +final class SyncTodoListProvider + extends $NotifierProvider> { + const SyncTodoListProvider._( + {super.runNotifierBuildOverride, SyncTodoList Function()? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'syncTodoListProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final SyncTodoList Function()? _createCb; + + @override + String debugGetCreateSourceHash() => _$syncTodoListHash(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(List value) { + return $ProviderOverride( + origin: this, + providerOverride: $ValueProvider>(value), + ); + } + + @$internal + @override + SyncTodoList create() => _createCb?.call() ?? SyncTodoList(); + + @$internal + @override + SyncTodoListProvider $copyWithCreate( + SyncTodoList Function() create, + ) { + return SyncTodoListProvider._(create: create); + } + + @$internal + @override + SyncTodoListProvider $copyWithBuild( + List Function( + Ref, + SyncTodoList, + ) build, + ) { + return SyncTodoListProvider._(runNotifierBuildOverride: build); + } + + @$internal + @override + _$SyncTodoListElement $createElement($ProviderPointer pointer) => + _$SyncTodoListElement(this, pointer); + + ProviderListenable get addSync => + $LazyProxyListenable>( + this, + (element) { + element as _$SyncTodoListElement; + + return element._$addSync; + }, + ); + + ProviderListenable get addAsync => + $LazyProxyListenable>( + this, + (element) { + element as _$SyncTodoListElement; + + return element._$addAsync; + }, + ); +} + +String _$syncTodoListHash() => r'18d459affe35603d564ac90e05c1978d4e862f40'; + +abstract class _$SyncTodoList extends $Notifier> { + List build(); + @$internal + @override + void runBuild() { + final created = build(); + final ref = this.ref as $Ref>; + final element = ref.element as $ClassProviderElement< + NotifierBase>, List, Object?, Object?>; + element.handleValue(ref, created); + } +} + +class _$SyncTodoListElement + extends $NotifierProviderElement> { + _$SyncTodoListElement(super.provider, super.pointer) { + _$addSync.result = $Result.data(_$SyncTodoList$AddSync(this)); + _$addAsync.result = $Result.data(_$SyncTodoList$AddAsync(this)); + } + final _$addSync = $ElementLense<_$SyncTodoList$AddSync>(); + final _$addAsync = $ElementLense<_$SyncTodoList$AddAsync>(); + @override + void mount() { + super.mount(); + _$addSync.result!.stateOrNull!.reset(); + _$addAsync.result!.stateOrNull!.reset(); + } + + @override + void visitListenables( + void Function($ElementLense element) listenableVisitor, + ) { + super.visitListenables(listenableVisitor); + + listenableVisitor(_$addSync); + listenableVisitor(_$addAsync); + } +} + +sealed class SyncTodoList$AddSync extends MutationBase { + /// Starts the mutation. + /// + /// This will first set the state to [PendingMutationState], then + /// will call [SyncTodoList.addSync] with the provided parameters. + /// + /// After the method completes, the mutation state will be updated to either + /// [SuccessMutationState] or [ErrorMutationState] based on if the method + /// threw or not. + /// + /// **Note**: + /// If the notifier threw in its constructor, the mutation won't start + /// and [call] will throw. + /// This should generally never happen though, as Notifiers are not supposed + /// to have logic in their constructors. + Todo call(Todo todo); +} + +final class _$SyncTodoList$AddSync + extends $SyncMutationBase + implements SyncTodoList$AddSync { + _$SyncTodoList$AddSync(this.element, {super.state, super.key}); + + @override + final _$SyncTodoListElement element; + + @override + $ElementLense<_$SyncTodoList$AddSync> get listenable => element._$addSync; + + @override + Todo call(Todo todo) { + return mutate( + Invocation.method( + #addSync, + [todo], + ), + ($notifier) => $notifier.addSync( + todo, + ), + ); + } + + @override + _$SyncTodoList$AddSync copyWith(MutationState state, {Object? key}) => + _$SyncTodoList$AddSync(element, state: state, key: key); +} + +sealed class SyncTodoList$AddAsync extends MutationBase { + /// Starts the mutation. + /// + /// This will first set the state to [PendingMutationState], then + /// will call [SyncTodoList.addAsync] with the provided parameters. + /// + /// After the method completes, the mutation state will be updated to either + /// [SuccessMutationState] or [ErrorMutationState] based on if the method + /// threw or not. + /// + /// **Note**: + /// If the notifier threw in its constructor, the mutation won't start + /// and [call] will throw. + /// This should generally never happen though, as Notifiers are not supposed + /// to have logic in their constructors. + Future call(Todo todo); +} + +final class _$SyncTodoList$AddAsync + extends $AsyncMutationBase + implements SyncTodoList$AddAsync { + _$SyncTodoList$AddAsync(this.element, {super.state, super.key}); + + @override + final _$SyncTodoListElement element; + + @override + $ElementLense<_$SyncTodoList$AddAsync> get listenable => element._$addAsync; + + @override + Future call(Todo todo) { + return mutate( + Invocation.method( + #addAsync, + [todo], + ), + ($notifier) => $notifier.addAsync( + todo, + ), + ); + } + + @override + _$SyncTodoList$AddAsync copyWith(MutationState state, {Object? key}) => + _$SyncTodoList$AddAsync(element, state: state, key: key); +} + +@ProviderFor(AsyncTodoList) +const asyncTodoListProvider = AsyncTodoListProvider._(); + +final class AsyncTodoListProvider + extends $AsyncNotifierProvider> { + const AsyncTodoListProvider._( + {super.runNotifierBuildOverride, AsyncTodoList Function()? create}) + : _createCb = create, + super( + from: null, + argument: null, + retry: null, + name: r'asyncTodoListProvider', + isAutoDispose: true, + dependencies: null, + allTransitiveDependencies: null, + ); + + final AsyncTodoList Function()? _createCb; + + @override + String debugGetCreateSourceHash() => _$asyncTodoListHash(); + + @$internal + @override + AsyncTodoList create() => _createCb?.call() ?? AsyncTodoList(); + + @$internal + @override + AsyncTodoListProvider $copyWithCreate( + AsyncTodoList Function() create, + ) { + return AsyncTodoListProvider._(create: create); + } + + @$internal + @override + AsyncTodoListProvider $copyWithBuild( + FutureOr> Function( + Ref, + AsyncTodoList, + ) build, + ) { + return AsyncTodoListProvider._(runNotifierBuildOverride: build); + } + + @$internal + @override + _$AsyncTodoListElement $createElement($ProviderPointer pointer) => + _$AsyncTodoListElement(this, pointer); + + ProviderListenable get addSync => + $LazyProxyListenable>>( + this, + (element) { + element as _$AsyncTodoListElement; + + return element._$addSync; + }, + ); + + ProviderListenable get addAsync => + $LazyProxyListenable>>( + this, + (element) { + element as _$AsyncTodoListElement; + + return element._$addAsync; + }, + ); +} + +String _$asyncTodoListHash() => r'73d9aa3b39ad5d0c157510754bfc273a98075d30'; + +abstract class _$AsyncTodoList extends $AsyncNotifier> { + FutureOr> build(); + @$internal + @override + void runBuild() { + final created = build(); + final ref = this.ref as $Ref>>; + final element = ref.element as $ClassProviderElement< + NotifierBase>>, + AsyncValue>, + Object?, + Object?>; + element.handleValue(ref, created); + } +} + +class _$AsyncTodoListElement + extends $AsyncNotifierProviderElement> { + _$AsyncTodoListElement(super.provider, super.pointer) { + _$addSync.result = $Result.data(_$AsyncTodoList$AddSync(this)); + _$addAsync.result = $Result.data(_$AsyncTodoList$AddAsync(this)); + } + final _$addSync = $ElementLense<_$AsyncTodoList$AddSync>(); + final _$addAsync = $ElementLense<_$AsyncTodoList$AddAsync>(); + @override + void mount() { + super.mount(); + _$addSync.result!.stateOrNull!.reset(); + _$addAsync.result!.stateOrNull!.reset(); + } + + @override + void visitListenables( + void Function($ElementLense element) listenableVisitor, + ) { + super.visitListenables(listenableVisitor); + + listenableVisitor(_$addSync); + listenableVisitor(_$addAsync); + } +} + +sealed class AsyncTodoList$AddSync extends MutationBase { + /// Starts the mutation. + /// + /// This will first set the state to [PendingMutationState], then + /// will call [AsyncTodoList.addSync] with the provided parameters. + /// + /// After the method completes, the mutation state will be updated to either + /// [SuccessMutationState] or [ErrorMutationState] based on if the method + /// threw or not. + /// + /// **Note**: + /// If the notifier threw in its constructor, the mutation won't start + /// and [call] will throw. + /// This should generally never happen though, as Notifiers are not supposed + /// to have logic in their constructors. + Todo call(Todo todo); +} + +final class _$AsyncTodoList$AddSync + extends $SyncMutationBase + implements AsyncTodoList$AddSync { + _$AsyncTodoList$AddSync(this.element, {super.state, super.key}); + + @override + final _$AsyncTodoListElement element; + + @override + $ElementLense<_$AsyncTodoList$AddSync> get listenable => element._$addSync; + + @override + Todo call(Todo todo) { + return mutate( + Invocation.method( + #addSync, + [todo], + ), + ($notifier) => $notifier.addSync( + todo, + ), + ); + } + + @override + _$AsyncTodoList$AddSync copyWith(MutationState state, {Object? key}) => + _$AsyncTodoList$AddSync(element, state: state, key: key); +} + +sealed class AsyncTodoList$AddAsync extends MutationBase { + /// Starts the mutation. + /// + /// This will first set the state to [PendingMutationState], then + /// will call [AsyncTodoList.addAsync] with the provided parameters. + /// + /// After the method completes, the mutation state will be updated to either + /// [SuccessMutationState] or [ErrorMutationState] based on if the method + /// threw or not. + /// + /// **Note**: + /// If the notifier threw in its constructor, the mutation won't start + /// and [call] will throw. + /// This should generally never happen though, as Notifiers are not supposed + /// to have logic in their constructors. + Future call(Todo todo); +} + +final class _$AsyncTodoList$AddAsync + extends $AsyncMutationBase + implements AsyncTodoList$AddAsync { + _$AsyncTodoList$AddAsync(this.element, {super.state, super.key}); + + @override + final _$AsyncTodoListElement element; + + @override + $ElementLense<_$AsyncTodoList$AddAsync> get listenable => element._$addAsync; + + @override + Future call(Todo todo) { + return mutate( + Invocation.method( + #addAsync, + [todo], + ), + ($notifier) => $notifier.addAsync( + todo, + ), + ); + } + + @override + _$AsyncTodoList$AddAsync copyWith(MutationState state, {Object? key}) => + _$AsyncTodoList$AddAsync(element, state: state, key: key); +} + @ProviderFor(Simple) const simpleProvider = SimpleProvider._(); @@ -95,7 +520,7 @@ final class SimpleProvider extends $NotifierProvider { ); } -String _$simpleHash() => r'c84cd9b6e3b09516b19316b0b21ea5ba5bc08a07'; +String _$simpleHash() => r'bbccebb4e8d2a097b945f6d7ab5e54ac11781c49'; abstract class _$Simple extends $Notifier { int build(); @@ -149,9 +574,6 @@ sealed class Simple$Increment extends MutationBase { /// [SuccessMutationState] or [ErrorMutationState] based on if the method /// threw or not. /// - /// Lastly, if the method completes without throwing, the Notifier's state - /// will be updated with the new value. - /// /// **Note**: /// If the notifier threw in its constructor, the mutation won't start /// and [call] will throw. @@ -161,7 +583,7 @@ sealed class Simple$Increment extends MutationBase { } final class _$Simple$Increment - extends $SyncMutationBase + extends $AsyncMutationBase implements Simple$Increment { _$Simple$Increment(this.element, {super.state, super.key}); @@ -173,7 +595,7 @@ final class _$Simple$Increment @override Future call([int inc = 1]) { - return mutateAsync( + return mutate( Invocation.method( #increment, [inc], @@ -199,19 +621,16 @@ sealed class Simple$IncrementOr extends MutationBase { /// [SuccessMutationState] or [ErrorMutationState] based on if the method /// threw or not. /// - /// Lastly, if the method completes without throwing, the Notifier's state - /// will be updated with the new value. - /// /// **Note**: /// If the notifier threw in its constructor, the mutation won't start /// and [call] will throw. /// This should generally never happen though, as Notifiers are not supposed /// to have logic in their constructors. - Future call(); + FutureOr call(); } final class _$Simple$IncrementOr - extends $SyncMutationBase + extends $AsyncMutationBase implements Simple$IncrementOr { _$Simple$IncrementOr(this.element, {super.state, super.key}); @@ -222,8 +641,8 @@ final class _$Simple$IncrementOr $ElementLense<_$Simple$IncrementOr> get listenable => element._$incrementOr; @override - Future call() { - return mutateAsync( + FutureOr call() { + return mutate( Invocation.method( #incrementOr, [], @@ -247,9 +666,6 @@ sealed class Simple$Delegated extends MutationBase { /// [SuccessMutationState] or [ErrorMutationState] based on if the method /// threw or not. /// - /// Lastly, if the method completes without throwing, the Notifier's state - /// will be updated with the new value. - /// /// **Note**: /// If the notifier threw in its constructor, the mutation won't start /// and [call] will throw. @@ -259,7 +675,7 @@ sealed class Simple$Delegated extends MutationBase { } final class _$Simple$Delegated - extends $SyncMutationBase + extends $AsyncMutationBase implements Simple$Delegated { _$Simple$Delegated(this.element, {super.state, super.key}); @@ -271,7 +687,7 @@ final class _$Simple$Delegated @override Future call(Future Function() fn) { - return mutateAsync( + return mutate( Invocation.method( #delegated, [fn], @@ -390,7 +806,7 @@ final class SimpleFamilyProvider extends $NotifierProvider { } } -String _$simpleFamilyHash() => r'7f7a9985568e147b78fbcd6ed7691a6677f75aeb'; +String _$simpleFamilyHash() => r'5a1afef2fb83836b8cbdc48fda6975a9149d9f2d'; final class SimpleFamilyFamily extends Family { const SimpleFamilyFamily._() @@ -508,9 +924,6 @@ sealed class SimpleFamily$Increment extends MutationBase { /// [SuccessMutationState] or [ErrorMutationState] based on if the method /// threw or not. /// - /// Lastly, if the method completes without throwing, the Notifier's state - /// will be updated with the new value. - /// /// **Note**: /// If the notifier threw in its constructor, the mutation won't start /// and [call] will throw. @@ -520,7 +933,7 @@ sealed class SimpleFamily$Increment extends MutationBase { } final class _$SimpleFamily$Increment - extends $SyncMutationBase + extends $AsyncMutationBase implements SimpleFamily$Increment { _$SimpleFamily$Increment(this.element, {super.state, super.key}); @@ -532,7 +945,7 @@ final class _$SimpleFamily$Increment @override Future call([int inc = 1]) { - return mutateAsync( + return mutate( Invocation.method( #increment, [inc], @@ -558,19 +971,16 @@ sealed class SimpleFamily$IncrementOr extends MutationBase { /// [SuccessMutationState] or [ErrorMutationState] based on if the method /// threw or not. /// - /// Lastly, if the method completes without throwing, the Notifier's state - /// will be updated with the new value. - /// /// **Note**: /// If the notifier threw in its constructor, the mutation won't start /// and [call] will throw. /// This should generally never happen though, as Notifiers are not supposed /// to have logic in their constructors. - Future call(); + FutureOr call(); } final class _$SimpleFamily$IncrementOr - extends $SyncMutationBase + extends $AsyncMutationBase implements SimpleFamily$IncrementOr { _$SimpleFamily$IncrementOr(this.element, {super.state, super.key}); @@ -582,8 +992,8 @@ final class _$SimpleFamily$IncrementOr element._$incrementOr; @override - Future call() { - return mutateAsync( + FutureOr call() { + return mutate( Invocation.method( #incrementOr, [], @@ -670,7 +1080,7 @@ final class SimpleAsyncProvider ); } -String _$simpleAsyncHash() => r'ed00b8e5170e48855d0b3cddddabd316fef466cf'; +String _$simpleAsyncHash() => r'62dd0ee93e61fa27d139247b9a899630d5d3572c'; abstract class _$SimpleAsync extends $AsyncNotifier { FutureOr build(); @@ -721,9 +1131,6 @@ sealed class SimpleAsync$Increment extends MutationBase { /// [SuccessMutationState] or [ErrorMutationState] based on if the method /// threw or not. /// - /// Lastly, if the method completes without throwing, the Notifier's state - /// will be updated with the new value. - /// /// **Note**: /// If the notifier threw in its constructor, the mutation won't start /// and [call] will throw. @@ -745,7 +1152,7 @@ final class _$SimpleAsync$Increment @override Future call([int inc = 1]) { - return mutateAsync( + return mutate( Invocation.method( #increment, [inc], @@ -771,9 +1178,6 @@ sealed class SimpleAsync$Delegated extends MutationBase { /// [SuccessMutationState] or [ErrorMutationState] based on if the method /// threw or not. /// - /// Lastly, if the method completes without throwing, the Notifier's state - /// will be updated with the new value. - /// /// **Note**: /// If the notifier threw in its constructor, the mutation won't start /// and [call] will throw. @@ -795,7 +1199,7 @@ final class _$SimpleAsync$Delegated @override Future call(Future Function() fn) { - return mutateAsync( + return mutate( Invocation.method( #delegated, [fn], @@ -897,7 +1301,7 @@ final class SimpleAsync2Provider } } -String _$simpleAsync2Hash() => r'7b372f85f3e4f1c2a954402b82a9a7b68bbc1407'; +String _$simpleAsync2Hash() => r'b2268a85a058e6f40c5bbfce8c20c9d285270967'; final class SimpleAsync2Family extends Family { const SimpleAsync2Family._() @@ -1011,9 +1415,6 @@ sealed class SimpleAsync2$Increment extends MutationBase { /// [SuccessMutationState] or [ErrorMutationState] based on if the method /// threw or not. /// - /// Lastly, if the method completes without throwing, the Notifier's state - /// will be updated with the new value. - /// /// **Note**: /// If the notifier threw in its constructor, the mutation won't start /// and [call] will throw. @@ -1035,7 +1436,7 @@ final class _$SimpleAsync2$Increment @override Future call() { - return mutateAsync( + return mutate( Invocation.method( #increment, [], @@ -1149,7 +1550,7 @@ final class GenericProvider } } -String _$genericHash() => r'4089b4d9b08bfff0256ad67cf35780a6409f7a87'; +String _$genericHash() => r'd5341a17852b21f307e508a4f9d9470c5863aa17'; final class GenericFamily extends Family { const GenericFamily._() @@ -1243,9 +1644,6 @@ sealed class Generic$Increment extends MutationBase { /// [SuccessMutationState] or [ErrorMutationState] based on if the method /// threw or not. /// - /// Lastly, if the method completes without throwing, the Notifier's state - /// will be updated with the new value. - /// /// **Note**: /// If the notifier threw in its constructor, the mutation won't start /// and [call] will throw. @@ -1267,7 +1665,7 @@ final class _$Generic$Increment @override Future call() { - return mutateAsync( + return mutate( Invocation.method( #increment, [], @@ -1342,7 +1740,7 @@ final class GenericMutProvider extends $AsyncNotifierProvider { ); } -String _$genericMutHash() => r'43acfc1b7cf59fb05f31ed4c2d5470422198feb0'; +String _$genericMutHash() => r'1f38b70cf937501fb313ae35c8bf824728bbd8ba'; abstract class _$GenericMut extends $AsyncNotifier { FutureOr build(); @@ -1389,9 +1787,6 @@ sealed class GenericMut$Increment extends MutationBase { /// [SuccessMutationState] or [ErrorMutationState] based on if the method /// threw or not. /// - /// Lastly, if the method completes without throwing, the Notifier's state - /// will be updated with the new value. - /// /// **Note**: /// If the notifier threw in its constructor, the mutation won't start /// and [call] will throw. @@ -1413,7 +1808,7 @@ final class _$GenericMut$Increment @override Future call(T value) { - return mutateAsync( + return mutate( Invocation.genericMethod( #increment, [T], @@ -1499,7 +1894,7 @@ final class FailingCtorProvider extends $NotifierProvider { ); } -String _$failingCtorHash() => r'6cdef257a2d783fa5a606b411be0d23744766cdc'; +String _$failingCtorHash() => r'5d80d3b1dba058415cc8cfec17bc14e1f9c83fae'; abstract class _$FailingCtor extends $Notifier { int build(); @@ -1545,9 +1940,6 @@ sealed class FailingCtor$Increment extends MutationBase { /// [SuccessMutationState] or [ErrorMutationState] based on if the method /// threw or not. /// - /// Lastly, if the method completes without throwing, the Notifier's state - /// will be updated with the new value. - /// /// **Note**: /// If the notifier threw in its constructor, the mutation won't start /// and [call] will throw. @@ -1557,7 +1949,7 @@ sealed class FailingCtor$Increment extends MutationBase { } final class _$FailingCtor$Increment - extends $SyncMutationBase + extends $AsyncMutationBase implements FailingCtor$Increment { _$FailingCtor$Increment(this.element, {super.state, super.key}); @@ -1569,7 +1961,7 @@ final class _$FailingCtor$Increment @override Future call([int inc = 1]) { - return mutateAsync( + return mutate( Invocation.method( #increment, [inc], @@ -1700,9 +2092,6 @@ sealed class Typed$Mutate extends MutationBase { /// [SuccessMutationState] or [ErrorMutationState] based on if the method /// threw or not. /// - /// Lastly, if the method completes without throwing, the Notifier's state - /// will be updated with the new value. - /// /// **Note**: /// If the notifier threw in its constructor, the mutation won't start /// and [call] will throw. @@ -1712,7 +2101,7 @@ sealed class Typed$Mutate extends MutationBase { } final class _$Typed$Mutate - extends $SyncMutationBase + extends $AsyncMutationBase implements Typed$Mutate { _$Typed$Mutate(this.element, {super.state, super.key}); @@ -1725,7 +2114,7 @@ final class _$Typed$Mutate @override Future call(String one, {required String two, required String three}) { - return mutateAsync( + return mutate( Invocation.method(#mutate, [one], {#two: two, #three: three}), ($notifier) => $notifier.mutate( one, From 63980143fbb477ffc953aa36be23e24004d768d9 Mon Sep 17 00:00:00 2001 From: Remi Rousselet Date: Tue, 25 Feb 2025 21:43:26 +0100 Subject: [PATCH 2/3] Lint --- .../lib/riverpod_analyzer_utils.dart | 2 +- packages/riverpod_analyzer_utils/lib/src/errors.dart | 6 ++---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/packages/riverpod_analyzer_utils/lib/riverpod_analyzer_utils.dart b/packages/riverpod_analyzer_utils/lib/riverpod_analyzer_utils.dart index e2bdd7b46..37935fba4 100644 --- a/packages/riverpod_analyzer_utils/lib/riverpod_analyzer_utils.dart +++ b/packages/riverpod_analyzer_utils/lib/riverpod_analyzer_utils.dart @@ -1,4 +1,4 @@ -export 'src/errors.dart' hide ErrorReporter; +export 'src/errors.dart'; export 'src/nodes.dart' hide parseFirstProviderFor, diff --git a/packages/riverpod_analyzer_utils/lib/src/errors.dart b/packages/riverpod_analyzer_utils/lib/src/errors.dart index 787f138b7..e6d9d276c 100644 --- a/packages/riverpod_analyzer_utils/lib/src/errors.dart +++ b/packages/riverpod_analyzer_utils/lib/src/errors.dart @@ -1,11 +1,9 @@ import 'package:analyzer/dart/ast/ast.dart'; import 'package:analyzer/dart/element/element.dart'; -import 'package:meta/meta.dart'; -@internal -typedef ErrorReporter = void Function(RiverpodAnalysisError); +typedef RiverpodErrorReporter = void Function(RiverpodAnalysisError); -ErrorReporter errorReporter = (error) { +RiverpodErrorReporter errorReporter = (error) { throw UnsupportedError( 'RiverpodAnalysisError found but no errorReporter specified: $error', ); From 366dbb5de149f5247097d4015da1a42755fd0780 Mon Sep 17 00:00:00 2001 From: Remi Rousselet Date: Tue, 25 Feb 2025 21:54:59 +0100 Subject: [PATCH 3/3] Fix test --- .../lib/src/errors.dart | 1 - .../lib/src/nodes/providers/notifier.dart | 2 ++ .../test/mutation_test.dart | 25 +++---------------- 3 files changed, 6 insertions(+), 22 deletions(-) diff --git a/packages/riverpod_analyzer_utils/lib/src/errors.dart b/packages/riverpod_analyzer_utils/lib/src/errors.dart index e6d9d276c..b240c1a1c 100644 --- a/packages/riverpod_analyzer_utils/lib/src/errors.dart +++ b/packages/riverpod_analyzer_utils/lib/src/errors.dart @@ -17,7 +17,6 @@ enum RiverpodAnalysisErrorCode { providerDependencyListParseError, providerOrFamilyExpressionParseError, invalidRetryArgument, - mutationReturnTypeMismatch, mutationIsStatic, mutationIsAbstract, unsupportedMutationReturnType, diff --git a/packages/riverpod_analyzer_utils/lib/src/nodes/providers/notifier.dart b/packages/riverpod_analyzer_utils/lib/src/nodes/providers/notifier.dart index 8c247e501..84223ecaf 100644 --- a/packages/riverpod_analyzer_utils/lib/src/nodes/providers/notifier.dart +++ b/packages/riverpod_analyzer_utils/lib/src/nodes/providers/notifier.dart @@ -198,6 +198,8 @@ extension MutationMethodDeclarationX on MethodDeclaration { RiverpodAnalysisError( 'Mutations returning Streams are not supported', code: RiverpodAnalysisErrorCode.unsupportedMutationReturnType, + targetNode: this, + targetElement: element, ), ); case SupportedCreatedType.value: diff --git a/packages/riverpod_analyzer_utils_tests/test/mutation_test.dart b/packages/riverpod_analyzer_utils_tests/test/mutation_test.dart index d9dccd451..1cdd38f7f 100644 --- a/packages/riverpod_analyzer_utils_tests/test/mutation_test.dart +++ b/packages/riverpod_analyzer_utils_tests/test/mutation_test.dart @@ -4,9 +4,7 @@ import 'package:test/test.dart'; import 'analyzer_test_utils.dart'; void main() { - testSource( - 'Rejects mutations with a return value non-matching the build value', - source: r''' + testSource('Rejects mutations with a Stream return type', source: r''' import 'package:riverpod_annotation/riverpod_annotation.dart'; @riverpod @@ -16,27 +14,12 @@ class SyncNotifier extends _$SyncNotifier { @mutation Stream a() => throw 42; - - @mutation - T b() => throw 42; - - @mutation - FutureOr c() => throw 42; - - @mutation - Future d() => throw 42; - - @mutation - Future e() => throw 42; - - @mutation - int e() => throw 42; } ''', (resolver, unit, units) async { final result = await resolver.resolveRiverpodAnalysisResult(ignoreErrors: true); - expect(result.errors, hasLength(3)); + expect(result.errors, hasLength(1)); expect( result.errors, @@ -47,12 +30,12 @@ class SyncNotifier extends _$SyncNotifier { .having( (e) => e.code, 'code', - RiverpodAnalysisErrorCode.mutationReturnTypeMismatch, + RiverpodAnalysisErrorCode.unsupportedMutationReturnType, ) .having( (e) => e.message, 'message', - 'The return type of mutations must match the type returned by the "build" method.', + 'Mutations returning Streams are not supported', ), ), );