Skip to content

Commit

Permalink
Parse import aliases in ref expressions (#2942)
Browse files Browse the repository at this point in the history
fixes #2935
  • Loading branch information
rrousselGit authored Oct 2, 2023
1 parent bd4e1d8 commit 90c7c73
Show file tree
Hide file tree
Showing 9 changed files with 154 additions and 3 deletions.
4 changes: 4 additions & 0 deletions packages/riverpod_analyzer_utils/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## Unreleased patch

- Correctly parse import aliases when used inside `ref.watch(<...>)`

## 0.3.4 - 2023-09-27

- Fixed `refInvocations` not getting parsed for generated providers with arguments.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,16 @@ class ProviderListenableExpression extends RiverpodAst {
ProviderListenableExpression._({
required this.node,
required this.provider,
required this.providerPrefix,
required this.providerElement,
required this.familyArguments,
});

static ProviderListenableExpression? _parse(Expression? expression) {
if (expression == null) return null;

// print('oy $expression // ${expression.runtimeType}');
SimpleIdentifier? provider;
SimpleIdentifier? providerPrefix;
ProviderDeclarationElement? providerElement;
ArgumentList? familyArguments;

Expand All @@ -26,6 +27,7 @@ class ProviderListenableExpression extends RiverpodAst {
provider = expression;
final element = expression.staticElement;
if (element is PropertyAccessorElement) {
// watch(provider)
DartObject? annotation;
try {
annotation =
Expand All @@ -50,7 +52,13 @@ class ProviderListenableExpression extends RiverpodAst {
parseExpression(expression.target);
} else if (expression is PrefixedIdentifier) {
// watch(expression.modifier)
parseExpression(expression.prefix);
final element = expression.prefix.staticElement;
if (element is PrefixElement) {
providerPrefix = expression.prefix;
parseExpression(expression.identifier);
} else {
parseExpression(expression.prefix);
}
} else if (expression is IndexExpression) {
// watch(expression[])
parseExpression(expression.target);
Expand All @@ -65,6 +73,7 @@ class ProviderListenableExpression extends RiverpodAst {
return ProviderListenableExpression._(
node: expression,
provider: provider,
providerPrefix: providerPrefix,
providerElement: providerElement,
// Make sure `(){}()` doesn't report an argument list even though it's not a provider
familyArguments: provider == null ? null : familyArguments,
Expand Down Expand Up @@ -96,6 +105,7 @@ class ProviderListenableExpression extends RiverpodAst {
}

final Expression node;
final SimpleIdentifier? providerPrefix;
final SimpleIdentifier? provider;
final ProviderDeclarationElement? providerElement;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ void testSource(
String description,
Future<void> Function(Resolver resolver) run, {
required String source,
Map<String, String> files = const {},
bool runGenerator = false,
}) {
final testId = _testNumber++;
Expand All @@ -33,10 +34,19 @@ void testSource(

final enclosingZone = Zone.current;

final otherSources = {
for (final entry in files.entries)
'$packageName|lib/${entry.key}':
'library "${entry.key}"; ${entry.value}',
};

String? generated;
if (runGenerator) {
final analysisResult = await resolveSources(
{'$packageName|lib/foo.dart': sourceWithLibrary},
{
'$packageName|lib/foo.dart': sourceWithLibrary,
...otherSources,
},
(resolver) {
return resolver.resolveRiverpodLibraryResult(
ignoreErrors: true,
Expand All @@ -50,6 +60,7 @@ void testSource(
'$packageName|lib/foo.dart': sourceWithLibrary,
if (generated != null)
'$packageName|lib/foo.g.dart': 'part of "foo.dart";$generated',
...otherSources,
}, (resolver) async {
try {
final originalZone = Zone.current;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,47 @@ import 'package:test/test.dart';
import 'analyser_test_utils.dart';

void main() {
testSource(
'Parses import aliases',
runGenerator: true,
files: {
'file.dart': '''
import 'package:riverpod_annotation/riverpod_annotation.dart';
final aProvider = Provider<int>((ref) => 0);
''',
},
source: '''
import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'file.dart' as alias;
part 'foo.g.dart';
@Riverpod(keepAlive: true)
int aliased(AliasedRef ref) {
ref.watch(alias.aProvider);
return 0;
}
''',
(resolver) async {
final result = await resolver.resolveRiverpodAnalyssiResult();

expect(result.refWatchInvocations, hasLength(1));
expect(result.refInvocations.single.function.toSource(), 'watch');
expect(
result.refInvocations.single.node.toSource(),
'ref.watch(alias.aProvider)',
);

expect(
result.refWatchInvocations.single.provider.provider?.toSource(),
'aProvider',
);
expect(
result.refWatchInvocations.single.provider.providerPrefix?.toSource(),
'alias',
);
},
);

testSource('Decode watch expressions with syntax errors', source: '''
import 'package:riverpod_annotation/riverpod_annotation.dart';
Expand Down
1 change: 1 addition & 0 deletions packages/riverpod_lint/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
## Unreleased minor

- Added `avoid_build_context_in_providers` (thanks to @charlescyt)
- Fixed false positive with `avoid_manual_providers_as_generated_provider_dependency` when using import aliases

## 2.1.1 - 2023-09-27

Expand Down
8 changes: 8 additions & 0 deletions packages/riverpod_lint_flutter_test/test/lints/another.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import 'package:riverpod_annotation/riverpod_annotation.dart';

part 'another.g.dart';

final aProvider = Provider<int>((ref) => 0);

@riverpod
int b(BRef ref) => 0;
24 changes: 24 additions & 0 deletions packages/riverpod_lint_flutter_test/test/lints/another.g.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

23 changes: 23 additions & 0 deletions packages/riverpod_lint_flutter_test/test/lints/dependencies.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
// ignore_for_file: unused_field

import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'another.dart' as import_alias;

part 'dependencies.g.dart';

Expand Down Expand Up @@ -198,3 +201,23 @@ int familyDep2(FamilyDep2Ref ref, int p) {
final test = ref.watch(familyDepProvider(0));
return test * p;
}

// Regression test for https://github.com/rrousselGit/riverpod/issues/2935
@riverpod
int alias(AliasRef ref) {
// expect_lint: avoid_manual_providers_as_generated_provider_dependency
ref.watch(import_alias.aProvider);
ref.watch(import_alias.bProvider);
return 0;
}

// Regression test for https://github.com/rrousselGit/riverpod/issues/2935
@riverpod
class AliasClass extends _$AliasClass {
// expect_lint: avoid_manual_providers_as_generated_provider_dependency
late final int _a = ref.read(import_alias.aProvider);
late final int _b = ref.read(import_alias.bProvider);

@override
int build() => 0;
}
29 changes: 29 additions & 0 deletions packages/riverpod_lint_flutter_test/test/lints/dependencies.g.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 90c7c73

Please sign in to comment.