Skip to content
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

Override method with generic array argument generated with incorrect type #126

Closed
PaulWagener opened this issue May 23, 2020 · 4 comments · Fixed by #132
Closed

Override method with generic array argument generated with incorrect type #126

PaulWagener opened this issue May 23, 2020 · 4 comments · Fixed by #132
Assignees
Labels
generator bug Causes malformed generated code reproducible It’s not just you; we see it too!
Milestone

Comments

@PaulWagener
Copy link

The generator fails to handle the case where a derived class overrides a method with a generic type in its signature.

Minimal example:

class Concrete {
}

class Base<T> {
    func method(withArg: T) {
    }
}

class Derived: Base<Concrete> {
    override func method(withArg: Concrete) {
    }
}

The generated code this produces:

  // MARK: Mocked `method`(`withArg`: Mocky.Concrete)

  public override func `method`(`withArg`: Mocky.Concrete) -> Void {
    let invocation: Mockingbird.Invocation = Mockingbird.Invocation(selectorName: "`method`(`withArg`: Mocky.Concrete) -> Void", arguments: [Mockingbird.ArgumentMatcher(`withArg`)])
    mockingContext.didInvoke(invocation) { () -> Void in
      let implementation = stubbingContext.implementation(for: invocation)
      if let concreteImplementation = implementation as? (Mocky.Concrete) -> Void {
        concreteImplementation(`withArg`)
      } else if let concreteImplementation = implementation as? () -> Void {
        concreteImplementation()
      }
    }
  }

  public func `method`(`withArg`: @escaping @autoclosure () -> Mocky.Concrete) -> Mockingbird.Mockable<Mockingbird.FunctionDeclaration, (Mocky.Concrete) -> Void, Void> {
    let arguments: [Mockingbird.ArgumentMatcher] = [Mockingbird.resolve(`withArg`)]
    let invocation: Mockingbird.Invocation = Mockingbird.Invocation(selectorName: "`method`(`withArg`: Mocky.Concrete) -> Void", arguments: arguments)
    return Mockingbird.Mockable<Mockingbird.FunctionDeclaration, (Mocky.Concrete) -> Void, Void>(mock: self, invocation: invocation)
  }

  // MARK: Mocked `method`(`withArg`: Mocky.Concrete)

  public override func `method`(`withArg`: Mocky.Concrete) -> Void {
    let invocation: Mockingbird.Invocation = Mockingbird.Invocation(selectorName: "`method`(`withArg`: Mocky.Concrete) -> Void", arguments: [Mockingbird.ArgumentMatcher(`withArg`)])
    mockingContext.didInvoke(invocation) { () -> Void in
      let implementation = stubbingContext.implementation(for: invocation)
      if let concreteImplementation = implementation as? (Mocky.Concrete) -> Void {
        concreteImplementation(`withArg`)
      } else if let concreteImplementation = implementation as? () -> Void {
        concreteImplementation()
      }
    }
  }

  public func `method`(`withArg`: @escaping @autoclosure () -> Mocky.Concrete) -> Mockingbird.Mockable<Mockingbird.FunctionDeclaration, (Mocky.Concrete) -> Void, Void> {
    let arguments: [Mockingbird.ArgumentMatcher] = [Mockingbird.resolve(`withArg`)]
    let invocation: Mockingbird.Invocation = Mockingbird.Invocation(selectorName: "`method`(`withArg`: Mocky.Concrete) -> Void", arguments: arguments)
    return Mockingbird.Mockable<Mockingbird.FunctionDeclaration, (Mocky.Concrete) -> Void, Void>(mock: self, invocation: invocation)
  }

Xcode can't compile this because

public override func `method`(`withArg`: Mocky.Concrete) -> Void {

is generated twice and it fails with a method(withArg:) has already been overridden

Environment

  • CLI version: 0.12.0
  • Swift version: 5.2.2
  • Xcode version: 11.4.1
  • Installed via Cocoapods
  • XCTest framework
@PaulWagener
Copy link
Author

Just found out that the minimal example doesn't even involve generics. The following example also produces a duplicate method in the generated code:

class Base {
    func method(withArg: UInt) {
    }
}


class Derived: Base {
    override func method(withArg: UInt) {
    }
}

@andrewchang-bird
Copy link
Contributor

andrewchang-bird commented May 23, 2020

Thanks for reporting, Paul! This regression in 0.12 should be fixed by #119. The original plan in the #118 issue was to wait until the upcoming release, but since this extends to declarations in class mocks we’ll push out a patch on Monday.

@andrewchang-bird andrewchang-bird added generator bug Causes malformed generated code reproducible It’s not just you; we see it too! labels May 23, 2020
@PaulWagener
Copy link
Author

Thanks for the quick fix. I can confirm that the duplicate override method has been fixed when using the latest code in master.

However another issue has still remained with regards to overriding methods with generics.
Take for example this example code:

class Concrete {
}

class Base<T> {
    func method(vertices: [T]) {
    }
}

class Derived: Base<Concrete> {
}

It results in the following generated code:

public final class DerivedMock: Mocky.Derived, Mockingbird.Mock {

  ...

  // MARK: Mocked `method`(`vertices`: [T])

  public override func `method`(`vertices`: [T]) -> Void {
    let invocation: Mockingbird.Invocation = Mockingbird.Invocation(selectorName: "`method`(`vertices`: [T]) -> Void", arguments: [Mockingbird.ArgumentMatcher(`vertices`)], returnType: Swift.ObjectIdentifier((Void).self))
    mockingContext.didInvoke(invocation) { () -> Void in
      let implementation = stubbingContext.implementation(for: invocation)
      if let concreteImplementation = implementation as? ([T]) -> Void {
        concreteImplementation(`vertices`)
      } else if let concreteImplementation = implementation as? () -> Void {
        concreteImplementation()
      }
    }
  }
  ...
}

This results in the error Use of undeclared type 'T'. The expected generated code would use the Concrete type here.

Interestingly it does generate the correct code when the type parameter isn't an array

I've tested this on a246a83 (master)

@PaulWagener PaulWagener changed the title Override method with generic argument generated twice Override method with generic array argument generated with incorrect type May 24, 2020
@andrewchang-bird
Copy link
Contributor

Thanks for the update!

The latest issue is caused by the type specializer not working recursively on type declarations. There isn’t a great workaround for this right now, but I’ll look into it for 0.13.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
generator bug Causes malformed generated code reproducible It’s not just you; we see it too!
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants