Skip to content

Commit

Permalink
Merge pull request #3841 from rrousselGit:sync-dev
Browse files Browse the repository at this point in the history
Sync dev with master
  • Loading branch information
rrousselGit authored Nov 19, 2024
2 parents d499f35 + ecb52b3 commit e79523a
Show file tree
Hide file tree
Showing 12 changed files with 425 additions and 67 deletions.
3 changes: 3 additions & 0 deletions analysis_options.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ analyzer:
included_file_warning: ignore
# false positive when using Freezed
invalid_annotation_target: ignore
# I prefer specifying a parameter on a widget even if they are unused (such as Key)
# for the sake of consistency.
unused_element_parameter: false

linter:
rules:
Expand Down
3 changes: 3 additions & 0 deletions examples/marvel/analysis_options.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,6 @@ analyzer:
errors:
# Some assets are voluntarily gitignored
asset_does_not_exist: ignore
# I prefer specifying a parameter on a widget even if they are unused (such as Key)
# for the sake of consistency.
unused_element_parameter: false
2 changes: 1 addition & 1 deletion packages/hooks_riverpod/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ dependencies:
collection: ^1.15.0
flutter:
sdk: flutter
flutter_hooks: '>=0.18.0 <0.21.0'
flutter_hooks: '>=0.18.0 <0.22.0'
flutter_riverpod: 3.0.0-dev.3
riverpod: 3.0.0-dev.3
state_notifier: ">=0.7.2 <2.0.0"
Expand Down
2 changes: 1 addition & 1 deletion packages/riverpod/lib/src/core/ref.dart
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,7 @@ final <yourProvider> = Provider(dependencies: [<dependency>]);
/// Using [asReload] will disable this behavior and count as a
/// "hard refresh".
///
/// If used on a provider which is not initialized, this method will have no effect.
/// If used on a provider which is not initialized or disposed, this method will have no effect.
/// {@endtemplate}
void invalidate(ProviderOrFamily providerOrFamily, {bool asReload = false}) {
_throwIfInvalidUsage();
Expand Down
142 changes: 137 additions & 5 deletions packages/riverpod_analyzer_utils_tests/test/ref_invocation_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,15 @@ import 'analyzer_test_utils.dart';
// ignore: invalid_use_of_internal_member
extension on RiverpodAnalysisResult {
List<RefWatchInvocation> get refWatchInvocations {
return refInvocations.cast();
return refInvocations.whereType<RefWatchInvocation>().toList();
}

List<RefListenInvocation> get refListenInvocations {
return refInvocations.cast();
return refInvocations.whereType<RefListenInvocation>().toList();
}

List<RefReadInvocation> get refReadInvocations {
return refInvocations.cast();
return refInvocations.whereType<RefReadInvocation>().toList();
}
}

Expand Down Expand Up @@ -436,8 +436,93 @@ final provider = Provider<int>((ref) {
);
});

testSource('Decodes unknown ref usages',
timeout: const Timeout.factor(4), source: '''
testSource('Decodes nested ref.read invocations with family providers',
runGenerator: true, source: '''
import 'package:riverpod/riverpod.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'foo.g.dart';
final dep = FutureProvider((ref) => 0);
final dep2 = FutureProvider.family((ref, int arg) => 0);
final dep3 = FutureProvider.family((ref, int arg) => 0);
final provider = Provider<int>((ref) {
ref.read(dep2(ref.read(dep)));
return 0;
});
final provider2 = Provider<int>((ref) {
ref.read(dep3(ref.read(dep2(ref.read(dep)))));
return 0;
});
int transformArg(int arg) {
return arg;
}
final provider3 = Provider<int>((ref) {
ref.read(dep3(transformArg(ref.read(dep))));
return 0;
});
''', (resolver, unit, units) async {
final result = await resolver.resolveRiverpodAnalysisResult();

expect(result.refReadInvocations, hasLength(7));
expect(result.refInvocations, result.refReadInvocations);

// provider
expect(result.refReadInvocations[0].node.toSource(), 'ref.read(dep)');
expect(result.refReadInvocations[0].function.toSource(), 'read');
expect(
result.refReadInvocations[0].listenable.provider?.providerElement,
same(
result.legacyProviderDeclarations.findByName('dep').providerElement,
),
);

expect(
result.refReadInvocations[1].node.toSource(),
'ref.read(dep2(ref.read(dep)))',
);
expect(result.refReadInvocations[1].function.toSource(), 'read');
expect(
result.refReadInvocations[1].listenable.provider?.providerElement,
same(
result.legacyProviderDeclarations.findByName('dep2').providerElement,
),
);

// provider2
expect(result.refReadInvocations[2].node.toSource(), 'ref.read(dep)');
expect(
result.refReadInvocations[3].node.toSource(),
'ref.read(dep2(ref.read(dep)))',
);
expect(
result.refReadInvocations[4].node.toSource(),
'ref.read(dep3(ref.read(dep2(ref.read(dep)))))',
);
expect(result.refReadInvocations[4].function.toSource(), 'read');
expect(
result.refReadInvocations[4].listenable.provider?.providerElement,
same(
result.legacyProviderDeclarations.findByName('dep3').providerElement,
),
);

// provider3
expect(result.refReadInvocations[5].node.toSource(), 'ref.read(dep)');
expect(
result.refReadInvocations[6].node.toSource(),
'ref.read(dep3(transformArg(ref.read(dep))))',
);
});

testSource('Decodes unknown ref usages', source: '''
import 'package:riverpod/riverpod.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
Expand Down Expand Up @@ -592,6 +677,53 @@ void fn(_Ref ref) {
);
});

testSource('Decodes mix of nested ref.watch and ref.read invocations',
runGenerator: true, source: '''
import 'package:riverpod/riverpod.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'foo.g.dart';
final dep = FutureProvider((ref) => 0);
final dep2 = FutureProvider.family((ref, int arg) => 0);
final provider = Provider<int>((ref) {
ref.watch(dep2(ref.read(dep)));
return 0;
});
''', (resolver, unit, units) async {
final result = await resolver.resolveRiverpodAnalysisResult();

expect(result.refWatchInvocations, hasLength(1));
expect(result.refReadInvocations, hasLength(1));
expect(
result.refInvocations,
[...result.refReadInvocations, ...result.refWatchInvocations],
);

expect(
result.refWatchInvocations[0].node.toSource(),
'ref.watch(dep2(ref.read(dep)))',
);
expect(result.refWatchInvocations[0].function.toSource(), 'watch');
expect(
result.refWatchInvocations[0].listenable.provider?.providerElement,
same(
result.legacyProviderDeclarations.findByName('dep2').providerElement,
),
);

expect(result.refReadInvocations[0].node.toSource(), 'ref.read(dep)');
expect(result.refReadInvocations[0].function.toSource(), 'read');
expect(
result.refReadInvocations[0].listenable.provider?.providerElement,
same(
result.legacyProviderDeclarations.findByName('dep').providerElement,
),
);
});

testSource('Decodes provider.query ref.watch usages',
timeout: const Timeout.factor(4), runGenerator: true, source: r'''
import 'package:riverpod/riverpod.dart';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,21 @@ import 'analyzer_test_utils.dart';
// ignore: invalid_use_of_internal_member
extension on RiverpodAnalysisResult {
List<WidgetRefWatchInvocation> get widgetRefWatchInvocations {
return widgetRefInvocations.cast();
return widgetRefInvocations.whereType<WidgetRefWatchInvocation>().toList();
}

List<WidgetRefReadInvocation> get widgetRefReadInvocations {
return widgetRefInvocations.cast();
return widgetRefInvocations.whereType<WidgetRefReadInvocation>().toList();
}

List<WidgetRefListenInvocation> get widgetRefListenInvocations {
return widgetRefInvocations.cast();
return widgetRefInvocations.whereType<WidgetRefListenInvocation>().toList();
}

List<WidgetRefListenManualInvocation> get widgetRefListenManualInvocations {
return widgetRefInvocations.cast();
return widgetRefInvocations
.whereType<WidgetRefListenManualInvocation>()
.toList();
}
}

Expand Down Expand Up @@ -652,6 +654,89 @@ void fn(_Ref ref) {
);
});

testSource('Decodes nested ref.watch invocations with family providers',
runGenerator: true, source: '''
import 'package:riverpod/riverpod.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter/material.dart';
part 'foo.g.dart';
final family = FutureProvider.family<int, int>((ref, id) => 0);
@Riverpod(keepAlive: true)
Future<int> family2(Family2Ref ref, {required int id}) async => 0;
class MyWidget extends ConsumerWidget {
const MyWidget({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
ref.watch(family(ref.read(family2Provider(id: 0))));
ref.watch(family2Provider(ref.watch(family(id: 0))));
return Container();
}
}
''', (resolver, unit, units) async {
final result = await resolver.resolveRiverpodAnalysisResult();

expect(result.widgetRefWatchInvocations, hasLength(3));

expect(
result.widgetRefWatchInvocations[0].node.toSource(),
'ref.watch(family(ref.read(family2Provider(id: 0))))',
);
expect(result.widgetRefWatchInvocations[0].function.toSource(), 'watch');
expect(
result.widgetRefWatchInvocations[0].listenable.node.toSource(),
'family(ref.read(family2Provider(id: 0)))',
);
expect(
result.widgetRefWatchInvocations[0].listenable.provider?.node.toSource(),
'family',
);
expect(
result.widgetRefWatchInvocations[0].listenable.provider?.providerElement,
same(
result.legacyProviderDeclarations.findByName('family').providerElement,
),
);
expect(
result.widgetRefWatchInvocations[0].listenable.familyArguments
?.toSource(),
'(ref.read(family2Provider(id: 0)))',
);

// ref.watch(family2Provider(ref.watch(family(id: 0)));
expect(
result.widgetRefWatchInvocations[2].node.toSource(),
'ref.watch(family2Provider(ref.watch(family(id: 0))))',
);
expect(result.widgetRefWatchInvocations[2].function.toSource(), 'watch');
expect(
result.widgetRefWatchInvocations[2].listenable.node.toSource(),
'family2Provider(ref.watch(family(id: 0)))',
);
expect(
result.widgetRefWatchInvocations[2].listenable.provider?.node.toSource(),
'family2Provider',
);
expect(
result.widgetRefWatchInvocations[2].listenable.provider?.providerElement,
same(
result.functionalProviderDeclarations
.findByName('family2')
.providerElement,
),
);
expect(
result.widgetRefWatchInvocations[2].listenable.familyArguments
?.toSource(),
'(ref.watch(family(id: 0)))',
);
});

testSource('Decodes provider.query ref.watch usages',
timeout: const Timeout.factor(4), runGenerator: true, source: r'''
import 'package:riverpod/riverpod.dart';
Expand Down
4 changes: 4 additions & 0 deletions packages/riverpod_generator/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@

- Added support for `@Riverpod(retry: ...)`

## 2.6.3 - 2024-11-18

- `riverpod_analyzer_utils` upgraded to `0.5.8`

## 2.6.2 - 2024-10-27

- `riverpod_analyzer_utils` upgraded to `0.5.7`
Expand Down
4 changes: 4 additions & 0 deletions packages/riverpod_lint/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@ Various lints had their severity changed:

- `riverpod` upgraded to `3.0.0-dev.0`

## 2.6.3 - 2024-11-18

- provider_dependencies now correctly detects nested ref invocations where a dependency is used as a parameter of another dependency (thanks to @josh-burton)

## 2.6.2 - 2024-10-27

- Support latest custom_lint
Expand Down
37 changes: 0 additions & 37 deletions packages/riverpod_lint/lib/src/lints/functional_ref.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,8 @@ import 'package:analyzer/error/error.dart'
// ignore: undefined_hidden_name, necessary to support lower analyzer version
LintCode;
import 'package:analyzer/error/listener.dart';
import 'package:analyzer_plugin/utilities/change_builder/change_builder_dart.dart';
import 'package:collection/collection.dart';
import 'package:custom_lint_builder/custom_lint_builder.dart';
import 'package:meta/meta.dart';

import '../riverpod_custom_lint.dart';

Expand Down Expand Up @@ -133,41 +131,6 @@ extension LibraryForNode on AstNode {
LibraryElement get library => (root as CompilationUnit).library;
}

extension ImportFix on DartFileEditBuilder {
@useResult
String importRef() {
return _importWithPrefix('Ref');
}

@useResult
String _importWithPrefix(String name) {
final hooksRiverpodUri =
Uri(scheme: 'package', path: 'hooks_riverpod/hooks_riverpod.dart');
final flutterRiverpodUri =
Uri(scheme: 'package', path: 'flutter_riverpod/flutter_riverpod.dart');
final riverpodUri = Uri(scheme: 'package', path: 'riverpod/riverpod.dart');

if (importsLibrary(hooksRiverpodUri)) {
return _buildImport(hooksRiverpodUri, name);
}

if (importsLibrary(flutterRiverpodUri)) {
return _buildImport(flutterRiverpodUri, name);
}

return _buildImport(riverpodUri, name);
}

String _buildImport(Uri uri, String name) {
final import = importLibraryElement(uri);

final prefix = import.prefix;
if (prefix != null) return '$prefix.$name';

return name;
}
}

TypeAnnotation typeAnnotationFor(FormalParameter param) {
if (param is DefaultFormalParameter) {
return typeAnnotationFor(param.parameter);
Expand Down
Loading

0 comments on commit e79523a

Please sign in to comment.