-
-
Notifications
You must be signed in to change notification settings - Fork 130
Fix mocks for inaccessible method signature types #6641
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -364,7 +364,7 @@ private static void CollectMembers( | |
| } | ||
|
|
||
| var explicitName = RequiresExplicitImpl(primaryClassSymbol, evt) ? interfaceFqn : null; | ||
| state.Events.Add(Tag(CreateEventModel(evt, explicitName, interfaceFqn), ownerTypeIndex)); | ||
| state.Events.Add(Tag(CreateEventModel(evt, explicitName, interfaceFqn, compilation: compilation), ownerTypeIndex)); | ||
| break; | ||
| } | ||
| } | ||
|
|
@@ -511,7 +511,7 @@ private static void ProcessClassMembers( | |
| if (evt.IsAbstract || evt.IsVirtual || evt.IsOverride) | ||
| { | ||
| if (!seenEvents.Add(key)) continue; | ||
| events.Add(CreateEventModel(evt, null, compilationAssembly: compilationAssembly)); | ||
| events.Add(CreateEventModel(evt, null, compilationAssembly: compilationAssembly, compilation: compilation)); | ||
| } | ||
| else | ||
| { | ||
|
|
@@ -748,12 +748,21 @@ private static MockMemberModel CreateMethodModel(IMethodSymbol method, ref int m | |
| OverrideAccessModifier = GetOverrideAccessModifier(method, compilationAssembly), | ||
| IsRefStructReturn = returnType.IsRefLikeType, | ||
| AutoMockFactoryMethod = autoMockFactoryMethod, | ||
| IsSignatureAccessibleFromAssembly = IsMethodSignatureAccessibleFromAssembly(method, compilation), | ||
| IsReturnTypeStaticAbstractInterface = returnTypeHasStaticAbstract, | ||
| SpanReturnElementType = returnType.IsRefLikeType ? GetSpanElementType(returnType) : null, | ||
| ObsoleteAttribute = GetObsoleteAttributeSyntax(method) | ||
| }; | ||
| } | ||
|
|
||
| private static bool IsMethodSignatureAccessibleFromAssembly(IMethodSymbol method, Compilation compilation) | ||
| => TypeAccessibility.IsAccessibleFromAssembly(method.ReturnType, compilation) | ||
| && method.Parameters.All(parameter => | ||
| TypeAccessibility.IsAccessibleFromAssembly(parameter.Type, compilation)) | ||
| && method.TypeParameters.All(typeParameter => | ||
| typeParameter.ConstraintTypes.All(constraint => | ||
| TypeAccessibility.IsAccessibleFromAssembly(constraint, compilation))); | ||
|
Comment on lines
+762
to
+764
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
When a mocked generic interface method is constrained to a type inaccessible from the consumer assembly, Knowledge Base Used: TUnit.Mocks: source-generated mocking |
||
|
|
||
| /// <summary> | ||
| /// When a property with the same name appears from multiple interfaces, merge getter/setter | ||
| /// accessors so the generated class satisfies all interfaces. | ||
|
|
@@ -819,6 +828,7 @@ private static MockMemberModel CreatePropertyModel(IPropertySymbol property, ref | |
| SetterAccessModifier = GetAccessorAccessModifier(property.SetMethod, overrideAccessModifier, compilationAssembly), | ||
| IsRefStructReturn = property.Type.IsRefLikeType, | ||
| AutoMockFactoryMethod = GetAutoMockFactoryMethod(property.Type, compilation), | ||
| IsSignatureAccessibleFromAssembly = IsPropertySignatureAccessibleFromAssembly(property, compilation), | ||
| IsReturnTypeStaticAbstractInterface = IsInterfaceWithStaticAbstractMembers(property.Type), | ||
| SpanReturnElementType = property.Type.IsRefLikeType ? GetSpanElementType(property.Type) : null, | ||
| ObsoleteAttribute = propertyObsolete, | ||
|
|
@@ -827,6 +837,11 @@ private static MockMemberModel CreatePropertyModel(IPropertySymbol property, ref | |
| }; | ||
| } | ||
|
|
||
| private static bool IsPropertySignatureAccessibleFromAssembly(IPropertySymbol property, Compilation compilation) | ||
| => TypeAccessibility.IsAccessibleFromAssembly(property.Type, compilation) | ||
| && property.Parameters.All(parameter => | ||
| TypeAccessibility.IsAccessibleFromAssembly(parameter.Type, compilation)); | ||
|
|
||
| /// <summary>Returns the [Obsolete] attribute for a single accessor, but only when the | ||
| /// containing property is NOT itself marked obsolete. When the property is marked, the | ||
| /// property-level emission already covers the accessor and emitting both would duplicate. | ||
|
|
@@ -944,6 +959,7 @@ private static MockMemberModel CreateIndexerModel(IPropertySymbol indexer, ref i | |
| SetterAccessModifier = GetAccessorAccessModifier(indexer.SetMethod, overrideAccessModifier, compilationAssembly), | ||
| IsRefStructReturn = indexer.Type.IsRefLikeType, | ||
| AutoMockFactoryMethod = GetAutoMockFactoryMethod(indexer.Type, compilation), | ||
| IsSignatureAccessibleFromAssembly = IsPropertySignatureAccessibleFromAssembly(indexer, compilation), | ||
| IsReturnTypeStaticAbstractInterface = IsInterfaceWithStaticAbstractMembers(indexer.Type), | ||
| SpanReturnElementType = indexer.Type.IsRefLikeType ? GetSpanElementType(indexer.Type) : null, | ||
| ObsoleteAttribute = indexerObsolete, | ||
|
|
@@ -991,7 +1007,7 @@ private static MockMemberModel CreateIndexerModel(IPropertySymbol indexer, ref i | |
| return $"{globalPrefix}{baseName}MockFactory.CreateAutoMock<{typeArguments}>"; | ||
| } | ||
|
|
||
| private static MockEventModel CreateEventModel(IEventSymbol evt, string? explicitInterfaceName, string? declaringInterfaceName = null, IAssemblySymbol? compilationAssembly = null) | ||
| private static MockEventModel CreateEventModel(IEventSymbol evt, string? explicitInterfaceName, string? declaringInterfaceName = null, IAssemblySymbol? compilationAssembly = null, Compilation compilation = null!) | ||
| { | ||
| var eventHandlerType = evt.Type.GetFullyQualifiedNameWithNullability(); | ||
|
|
||
|
|
@@ -1052,6 +1068,7 @@ private static MockEventModel CreateEventModel(IEventSymbol evt, string? explici | |
| ExplicitInterfaceName = explicitInterfaceName, | ||
| DeclaringInterfaceName = declaringInterfaceName, | ||
| OverrideAccessModifier = GetOverrideAccessModifier(evt, compilationAssembly), | ||
| IsSignatureAccessibleFromAssembly = TypeAccessibility.IsAccessibleFromAssembly(evt.Type, compilation), | ||
| RaiseParameterList = raiseParameterList, | ||
| ObsoleteAttribute = GetObsoleteAttributeSyntax(evt) | ||
| }; | ||
|
|
@@ -1335,7 +1352,7 @@ private static void CollectStaticAbstractMember( | |
| var key = $"E:{evt.Name}"; | ||
| if (!seenEvents.Add(key)) break; | ||
|
|
||
| var model = CreateEventModel(evt, interfaceFqn) with | ||
| var model = CreateEventModel(evt, interfaceFqn, compilation: compilation) with | ||
| { | ||
| IsStaticAbstract = true | ||
| }; | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When a multi-type mock's primary class has an inaccessible method such as
protected State Get()and an additional interface exposesint Get(), this predicate removes the primary method from the generated member surface, butSecondarySurfaceFactory.CreateContextstill adds it toPrimaryMethodNameParams. Consequently,BuildPairModelunnecessarily renames the accessible interface setup toIExtra_Get, even though no primaryGetextension exists to conflict with it. Apply the same accessibility predicate when constructing the primary collision context so the secondary API retains its expected name.Useful? React with 👍 / 👎.