mirror of
https://github.com/flutter/packages.git
synced 2026-03-13 10:40:37 +08:00
[pigeon] Produce a helpful error for bad method return type (#11204)
When we encounter an unexpected (or missing) method return type, we now produce a meaningful error instead of a null-pointer exception. Fixes https://github.com/flutter/flutter/issues/92639
This commit is contained in:
@@ -1,3 +1,8 @@
|
||||
## 26.2.3
|
||||
|
||||
* Produces a helpful error message when a method return type is missing or an
|
||||
unsupported type, such as a function type or record type.
|
||||
|
||||
## 26.2.2
|
||||
|
||||
* [dart] Ignores all lint rules in generated code.
|
||||
|
||||
@@ -15,7 +15,7 @@ import 'generator.dart';
|
||||
/// The current version of pigeon.
|
||||
///
|
||||
/// This must match the version in pubspec.yaml.
|
||||
const String pigeonVersion = '26.2.2';
|
||||
const String pigeonVersion = '26.2.3';
|
||||
|
||||
/// Default plugin package name.
|
||||
const String defaultPluginPackageName = 'dev.flutter.pigeon';
|
||||
|
||||
@@ -1913,36 +1913,58 @@ class RootBuilder extends dart_ast_visitor.RecursiveAstVisitor<Object?> {
|
||||
|
||||
if (_currentApi != null) {
|
||||
// Methods without named return types aren't supported.
|
||||
final dart_ast.TypeAnnotation returnType = node.returnType!;
|
||||
returnType as dart_ast.NamedType;
|
||||
_currentApi!.methods.add(
|
||||
Method(
|
||||
name: node.name.lexeme,
|
||||
returnType: TypeDeclaration(
|
||||
baseName: _getNamedTypeQualifiedName(returnType),
|
||||
typeArguments: _typeAnnotationsToTypeArguments(
|
||||
returnType.typeArguments,
|
||||
final dart_ast.TypeAnnotation? returnType = node.returnType;
|
||||
if (returnType is! dart_ast.NamedType) {
|
||||
// In order to support implicit types (either `dynamic` or inherited
|
||||
// return types), type aliases, function types, or record types, we'd
|
||||
// need to use the analyzer's element model, via `getParsedUnit` instead
|
||||
// of `getParsedUnit` in `Pigeon.parseFile`, and then access the
|
||||
// resolved return type, via
|
||||
// `node.declaredFragment!.element.returnType`.
|
||||
String erroneousDeclaration = node.name.lexeme;
|
||||
final dart_ast.AstNode? enclosingDeclaration = node.parent;
|
||||
if (enclosingDeclaration is dart_ast.ClassDeclaration) {
|
||||
erroneousDeclaration =
|
||||
'${enclosingDeclaration.name}.$erroneousDeclaration';
|
||||
}
|
||||
_errors.add(
|
||||
Error(
|
||||
message:
|
||||
'Expected a named type for the return type of '
|
||||
'("$erroneousDeclaration").',
|
||||
lineNumber: calculateLineNumber(source, node.offset),
|
||||
),
|
||||
);
|
||||
} else {
|
||||
_currentApi!.methods.add(
|
||||
Method(
|
||||
name: node.name.lexeme,
|
||||
returnType: TypeDeclaration(
|
||||
baseName: _getNamedTypeQualifiedName(returnType),
|
||||
typeArguments: _typeAnnotationsToTypeArguments(
|
||||
returnType.typeArguments,
|
||||
),
|
||||
isNullable: returnType.question != null,
|
||||
),
|
||||
parameters: arguments,
|
||||
isStatic: isStatic,
|
||||
location: switch (_currentApi!) {
|
||||
AstHostApi() => ApiLocation.host,
|
||||
AstProxyApi() => ApiLocation.host,
|
||||
AstFlutterApi() => ApiLocation.flutter,
|
||||
AstEventChannelApi() => ApiLocation.host,
|
||||
},
|
||||
isAsynchronous: isAsynchronous,
|
||||
objcSelector: objcSelector,
|
||||
swiftFunction: swiftFunction,
|
||||
offset: node.offset,
|
||||
taskQueueType: taskQueueType,
|
||||
documentationComments: _documentationCommentsParser(
|
||||
node.documentationComment?.tokens,
|
||||
),
|
||||
isNullable: returnType.question != null,
|
||||
),
|
||||
parameters: arguments,
|
||||
isStatic: isStatic,
|
||||
location: switch (_currentApi!) {
|
||||
AstHostApi() => ApiLocation.host,
|
||||
AstProxyApi() => ApiLocation.host,
|
||||
AstFlutterApi() => ApiLocation.flutter,
|
||||
AstEventChannelApi() => ApiLocation.host,
|
||||
},
|
||||
isAsynchronous: isAsynchronous,
|
||||
objcSelector: objcSelector,
|
||||
swiftFunction: swiftFunction,
|
||||
offset: node.offset,
|
||||
taskQueueType: taskQueueType,
|
||||
documentationComments: _documentationCommentsParser(
|
||||
node.documentationComment?.tokens,
|
||||
),
|
||||
),
|
||||
);
|
||||
);
|
||||
}
|
||||
} else if (_currentClass != null) {
|
||||
_errors.add(
|
||||
Error(
|
||||
|
||||
@@ -2,7 +2,7 @@ name: pigeon
|
||||
description: Code generator tool to make communication between Flutter and the host platform type-safe and easier.
|
||||
repository: https://github.com/flutter/packages/tree/main/packages/pigeon
|
||||
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+pigeon%22
|
||||
version: 26.2.2 # This must match the version in lib/src/generator_tools.dart
|
||||
version: 26.2.3 # This must match the version in lib/src/generator_tools.dart
|
||||
|
||||
environment:
|
||||
sdk: ^3.9.0
|
||||
|
||||
@@ -1265,6 +1265,38 @@ abstract class Api {
|
||||
expect(results.errors[0].message, contains('Unknown type: Foo'));
|
||||
});
|
||||
|
||||
test('function return type is missing', () {
|
||||
const code = '''
|
||||
@HostApi()
|
||||
abstract class Api {
|
||||
storeAll(List<int?> foos);
|
||||
}
|
||||
''';
|
||||
final ParseResults results = parseSource(code);
|
||||
expect(results.errors, hasLength(1));
|
||||
expect(results.errors[0].lineNumber, 3);
|
||||
expect(
|
||||
results.errors[0].message,
|
||||
contains('Expected a named type for the return type of ("Api.storeAll")'),
|
||||
);
|
||||
});
|
||||
|
||||
test('function return type is record type', () {
|
||||
const code = '''
|
||||
@HostApi()
|
||||
abstract class Api {
|
||||
(int, int) storeAll(List<int?> foos);
|
||||
}
|
||||
''';
|
||||
final ParseResults results = parseSource(code);
|
||||
expect(results.errors, hasLength(1));
|
||||
expect(results.errors[0].lineNumber, 3);
|
||||
expect(
|
||||
results.errors[0].message,
|
||||
contains('Expected a named type for the return type of ("Api.storeAll")'),
|
||||
);
|
||||
});
|
||||
|
||||
test('Object type argument', () {
|
||||
const code = '''
|
||||
@HostApi()
|
||||
|
||||
Reference in New Issue
Block a user