@@ -836,13 +836,19 @@ class _MockTargetGatherer {
836836 }
837837 }
838838
839+ String get _tryUnsupportedMembersMessage => 'Try generating this mock with '
840+ "a MockSpec with 'unsupportedMembers' or a dummy generator (see "
841+ 'https://pub.dev/documentation/mockito/latest/annotations/MockSpec-class.html).' ;
842+
839843 /// Checks [function] for properties that would make it un-stubbable.
840844 ///
841845 /// Types are checked in the following positions:
842846 /// - return type
843847 /// - parameter types
844848 /// - bounds of type parameters
845- /// - type arguments on types in the above three positions
849+ /// - recursively, written types on types in the above three positions
850+ /// (namely, type arguments, return types of function types, and parameter
851+ /// types of function types)
846852 ///
847853 /// If any type in the above positions is private, [function] is un-stubbable.
848854 /// If the return type is potentially non-nullable, [function] is
@@ -861,14 +867,19 @@ class _MockTargetGatherer {
861867 final errorMessages = < String > [];
862868 final returnType = function.returnType;
863869 if (returnType is analyzer.InterfaceType ) {
864- if (returnType.element2.isPrivate) {
865- errorMessages.add (
866- '${enclosingElement .fullName } features a private return type, and '
867- 'cannot be stubbed.' );
870+ if (returnType.containsPrivateName) {
871+ if (! allowUnsupportedMember && ! hasDummyGenerator) {
872+ errorMessages.add (
873+ '${enclosingElement .fullName } features a private return type, '
874+ 'and cannot be stubbed. $_tryUnsupportedMembersMessage ' );
875+ }
868876 }
869877 errorMessages.addAll (_checkTypeArguments (
870- returnType.typeArguments, enclosingElement,
871- isParameter: isParameter));
878+ returnType.typeArguments,
879+ enclosingElement,
880+ isParameter: isParameter,
881+ allowUnsupportedMember: allowUnsupportedMember,
882+ ));
872883 } else if (returnType is analyzer.FunctionType ) {
873884 errorMessages.addAll (_checkFunction (returnType, enclosingElement,
874885 allowUnsupportedMember: allowUnsupportedMember,
@@ -878,11 +889,10 @@ class _MockTargetGatherer {
878889 ! allowUnsupportedMember &&
879890 ! hasDummyGenerator &&
880891 _entryLib.typeSystem.isPotentiallyNonNullable (returnType)) {
881- errorMessages.add (
882- '${enclosingElement .fullName } features a non-nullable unknown '
883- 'return type, and cannot be stubbed. Try generating this mock with '
884- "a MockSpec with 'unsupportedMembers' or a dummy generator (see "
885- 'https://pub.dev/documentation/mockito/latest/annotations/MockSpec-class.html).' );
892+ errorMessages
893+ .add ('${enclosingElement .fullName } features a non-nullable unknown '
894+ 'return type, and cannot be stubbed. '
895+ '$_tryUnsupportedMembersMessage ' );
886896 }
887897 }
888898
@@ -894,13 +904,19 @@ class _MockTargetGatherer {
894904 // Technically, we can expand the type in the mock to something like
895905 // `Object?`. However, until there is a decent use case, we will not
896906 // generate such a mock.
897- errorMessages.add (
898- '${enclosingElement .fullName } features a private parameter type, '
899- "'${parameterTypeElement .name }', and cannot be stubbed." );
907+ if (! allowUnsupportedMember) {
908+ errorMessages.add (
909+ '${enclosingElement .fullName } features a private parameter '
910+ "type, '${parameterTypeElement .name }', and cannot be stubbed. "
911+ '$_tryUnsupportedMembersMessage ' );
912+ }
900913 }
901914 errorMessages.addAll (_checkTypeArguments (
902- parameterType.typeArguments, enclosingElement,
903- isParameter: true ));
915+ parameterType.typeArguments,
916+ enclosingElement,
917+ isParameter: true ,
918+ allowUnsupportedMember: allowUnsupportedMember,
919+ ));
904920 } else if (parameterType is analyzer.FunctionType ) {
905921 errorMessages.addAll (
906922 _checkFunction (parameterType, enclosingElement, isParameter: true ));
@@ -928,6 +944,8 @@ class _MockTargetGatherer {
928944 var typeParameter = element.bound;
929945 if (typeParameter == null ) continue ;
930946 if (typeParameter is analyzer.InterfaceType ) {
947+ // TODO(srawlins): Check for private names in bound; could be
948+ // `List<_Bar>`.
931949 if (typeParameter.element2.isPrivate) {
932950 errorMessages.add (
933951 '${enclosingElement .fullName } features a private type parameter '
@@ -947,18 +965,23 @@ class _MockTargetGatherer {
947965 List <analyzer.DartType > typeArguments,
948966 Element enclosingElement, {
949967 bool isParameter = false ,
968+ bool allowUnsupportedMember = false ,
950969 }) {
951970 var errorMessages = < String > [];
952971 for (var typeArgument in typeArguments) {
953972 if (typeArgument is analyzer.InterfaceType ) {
954- if (typeArgument.element2.isPrivate) {
973+ if (typeArgument.element2.isPrivate && ! allowUnsupportedMember ) {
955974 errorMessages.add (
956975 '${enclosingElement .fullName } features a private type argument, '
957- 'and cannot be stubbed.' );
976+ 'and cannot be stubbed. $ _tryUnsupportedMembersMessage ' );
958977 }
959978 } else if (typeArgument is analyzer.FunctionType ) {
960- errorMessages.addAll (_checkFunction (typeArgument, enclosingElement,
961- isParameter: isParameter));
979+ errorMessages.addAll (_checkFunction (
980+ typeArgument,
981+ enclosingElement,
982+ isParameter: isParameter,
983+ allowUnsupportedMember: allowUnsupportedMember,
984+ ));
962985 }
963986 }
964987 return errorMessages;
@@ -1233,11 +1256,16 @@ class _MockClassInfo {
12331256 void _buildOverridingMethod (MethodBuilder builder, MethodElement method) {
12341257 var name = method.displayName;
12351258 if (method.isOperator) name = 'operator$name ' ;
1259+ final returnType = method.returnType;
12361260 builder
12371261 ..name = name
12381262 ..annotations.add (referImported ('override' , 'dart:core' ))
1239- ..returns = _typeReference (method.returnType)
12401263 ..types.addAll (method.typeParameters.map (_typeParameterReference));
1264+ // We allow overriding a method with a private return type by omitting the
1265+ // return type (which is then inherited).
1266+ if (! returnType.containsPrivateName) {
1267+ builder.returns = _typeReference (returnType);
1268+ }
12411269
12421270 // These two variables store the arguments that will be passed to the
12431271 // [Invocation] built for `noSuchMethod`.
@@ -1246,20 +1274,16 @@ class _MockClassInfo {
12461274
12471275 var position = 0 ;
12481276 for (final parameter in method.parameters) {
1249- if (parameter.isRequiredPositional) {
1250- final superParameterType =
1251- _escapeCovariance (parameter, position: position);
1252- final matchingParameter = _matchingParameter (parameter,
1253- superParameterType: superParameterType, forceNullable: true );
1254- builder.requiredParameters.add (matchingParameter);
1255- invocationPositionalArgs.add (refer (parameter.displayName));
1256- position++ ;
1257- } else if (parameter.isOptionalPositional) {
1277+ if (parameter.isRequiredPositional || parameter.isOptionalPositional) {
12581278 final superParameterType =
12591279 _escapeCovariance (parameter, position: position);
12601280 final matchingParameter = _matchingParameter (parameter,
12611281 superParameterType: superParameterType, forceNullable: true );
1262- builder.optionalParameters.add (matchingParameter);
1282+ if (parameter.isRequiredPositional) {
1283+ builder.requiredParameters.add (matchingParameter);
1284+ } else {
1285+ builder.optionalParameters.add (matchingParameter);
1286+ }
12631287 invocationPositionalArgs.add (refer (parameter.displayName));
12641288 position++ ;
12651289 } else if (parameter.isNamed) {
@@ -1282,11 +1306,18 @@ class _MockClassInfo {
12821306 return ;
12831307 }
12841308
1285- final returnType = method.returnType;
1309+ final returnTypeIsTypeVariable =
1310+ typeSystem.isPotentiallyNonNullable (returnType) &&
1311+ returnType is analyzer.TypeParameterType ;
12861312 final fallbackGenerator = fallbackGenerators[method.name];
1287- if (typeSystem.isPotentiallyNonNullable (returnType) &&
1288- returnType is analyzer.TypeParameterType &&
1289- fallbackGenerator == null ) {
1313+ final parametersContainPrivateName =
1314+ method.parameters.any ((p) => p.type.containsPrivateName);
1315+ final throwsUnsupported = fallbackGenerator == null &&
1316+ (returnTypeIsTypeVariable ||
1317+ returnType.containsPrivateName ||
1318+ parametersContainPrivateName);
1319+
1320+ if (throwsUnsupported) {
12901321 if (! mockTarget.unsupportedMembers.contains (name)) {
12911322 // We shouldn't get here as this is guarded against in
12921323 // [_MockTargetGatherer._checkFunction].
@@ -1557,10 +1588,11 @@ class _MockClassInfo {
15571588 '$defaultName ' );
15581589 final name = parameter.name.isEmpty ? defaultName! : parameter.name;
15591590 return Parameter ((pBuilder) {
1560- pBuilder
1561- ..name = name
1562- . .type =
1591+ pBuilder.name = name;
1592+ if ( ! superParameterType.containsPrivateName) {
1593+ pBuilder .type =
15631594 _typeReference (superParameterType, forceNullable: forceNullable);
1595+ }
15641596 if (parameter.isNamed) pBuilder.named = true ;
15651597 if (parameter.defaultValueCode != null ) {
15661598 try {
@@ -2025,6 +2057,30 @@ extension on Element {
20252057}
20262058
20272059extension on analyzer.DartType {
2060+ /// Whether this type contains a private name, perhaps in a type argument or a
2061+ /// function type's parameters, etc.
2062+ bool get containsPrivateName {
2063+ final self = this ;
2064+ if (self is analyzer.DynamicType ) {
2065+ return false ;
2066+ } else if (self is analyzer.InterfaceType ) {
2067+ return self.element2.isPrivate ||
2068+ self.typeArguments.any ((t) => t.containsPrivateName);
2069+ } else if (self is analyzer.FunctionType ) {
2070+ return self.returnType.containsPrivateName ||
2071+ self.parameters.any ((p) => p.type.containsPrivateName);
2072+ } else if (self is analyzer.NeverType ) {
2073+ return false ;
2074+ } else if (self is analyzer.TypeParameterType ) {
2075+ return false ;
2076+ } else if (self is analyzer.VoidType ) {
2077+ return false ;
2078+ } else {
2079+ assert (false , 'Unexpected subtype of DartType: ${self .runtimeType }' );
2080+ return false ;
2081+ }
2082+ }
2083+
20282084 /// Returns whether this type is `Future<void>` or `Future<void>?` .
20292085 bool get isFutureOfVoid =>
20302086 isDartAsyncFuture &&
0 commit comments