// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -disable-objc-attr-requires-foundation-module -typecheck %s -verify
import ObjectiveC

// REQUIRES: objc_interop

@objc class A { }
@objc class B { }

class C1 {
  @objc init(a: A, b: B) { }

  @objc func method1(_ a: A, b: B) { }
  @objc(someMethodWithA:B:) func method2(_ a: A, b: B) { }

  @objc class func method3(_ a: A, b: B) { } // expected-note{{found this candidate}}
  @objc class func method3(a: A, b: B) { } // expected-note{{found this candidate}}

  @objc(ambiguous1:b:) class func ambiguous(a: A, b: A) { } // expected-note{{found this candidate}}
  @objc(ambiguous2:b:) class func ambiguous(a: A, b: B) { } // expected-note{{found this candidate}}

  @objc func getC1() -> AnyObject { return self }

  @objc func testUnqualifiedSelector(_ a: A, b: B) {
    _ = #selector(testUnqualifiedSelector(_:b:))
    let testUnqualifiedSelector = 1
    _ = #selector(testUnqualifiedSelector(_:b:))
    _ = testUnqualifiedSelector // suppress unused warning
  }

  @objc func testParam(_ testParam: A) { // expected-note{{'testParam' declared here}}
    _ = #selector(testParam) // expected-error{{argument of '#selector' cannot refer to parameter 'testParam'}}
  }

  @objc func testVariable() {
    let testVariable = 1 // expected-note{{'testVariable' declared here}}
    _ = #selector(testVariable) // expected-error{{argument of '#selector' cannot refer to variable 'testVariable'}}
  }
}

@objc protocol P1 {
  func method4(_ a: A, b: B)
  static func method5(_ a: B, b: B)
}

extension C1 {
  final func method6() { } // expected-note{{add '@objc' to expose this instance method to Objective-C}}{{3-3=@objc }}
}

func testSelector(_ c1: C1, p1: P1, obj: AnyObject) {
  // Instance methods on an instance
  let sel1 = #selector(c1.method1)
  _ = #selector(c1.method1(_:b:))
  _ = #selector(c1.method2)

  // Instance methods on a class.
  _ = #selector(C1.method1)
  _ = #selector(C1.method1(_:b:))
  _ = #selector(C1.method2)

  // Class methods on a class.
  _ = #selector(C1.method3(_:b:))
  _ = #selector(C1.method3(a:b:))

  // Methods on a protocol.
  _ = #selector(P1.method4)
  _ = #selector(P1.method4(_:b:))
  _ = #selector(P1.method5) // expected-error{{static member 'method5' cannot be used on protocol metatype '(any P1).Type'}}
  _ = #selector(P1.method5(_:b:)) // expected-error{{static member 'method5(_:b:)' cannot be used on protocol metatype '(any P1).Type'}}
  _ = #selector(p1.method4)
  _ = #selector(p1.method4(_:b:))
  _ = #selector(type(of: p1).method5)
  _ = #selector(type(of: p1).method5(_:b:))

  // Interesting expressions that refer to methods.
  _ = #selector(Swift.AnyObject.method1)
  _ = #selector(AnyObject.method1!)
  // expected-error@-1 {{cannot force unwrap value of non-optional type '(AnyObject) -> ((A, B) -> ())?'}}
  _ = #selector(obj.getC1?().method1)

  // Initializers
  _ = #selector(C1.init(a:b:))

  // Make sure the result has type "ObjectiveC.Selector"
  let sel2: Selector
  sel2 = sel1
  _ = sel2

  let dict: [Selector: Int] = [:]
  let _: Int? = dict[#selector(c1.method1)]
  let _ = [#selector(c1.method1)]
}

func testAmbiguity() {
  _ = #selector(C1.method3) // expected-error{{ambiguous use of 'method3'}}
  _ = #selector(C1.ambiguous) // expected-error{{ambiguous use of 'ambiguous(a:b:)'}}
}

func testUnusedSelector() {
    #selector(C1.getC1) // expected-warning{{result of '#selector' is unused}}
}

func testNonObjC(_ c1: C1) {
  _ = #selector(c1.method6) // expected-error{{argument of '#selector' refers to instance method 'method6()' that is not exposed to Objective-C}}
}

func testParseErrors1() {
  _ = #selector foo // expected-error{{expected '(' following '#selector'}}
}

func testParseErrors2() {
  _ = #selector( // expected-error{{expected expression naming a method within '#selector(...)'}}
}

func testParseErrors3(_ c1: C1) {
  _ = #selector( // expected-note{{to match this opening '('}}
      c1.method1(_:b:) // expected-error{{expected ')' to complete '#selector' expression}}
}

func testParseErrors4() {
  _ = #selector(C1.subscript) // expected-error{{type 'C1' has no member 'subscript'}}
}

// https://github.com/apple/swift/issues/44436
do {
  let optionalSel: Selector?

  switch optionalSel {
  case #selector(C1.method1)?:
    break
  default:
    break
  }

  @objc class C {
    @objc func bar() {}
  }

  switch optionalSel {
  case #selector(C.bar):
    break
  case #selector(C.bar)!: // expected-error{{cannot force unwrap value of non-optional type 'Selector'}}
    break
  case #selector(C.bar)?:
    break
  default:
    break
  }
}

// https://github.com/apple/swift/issues/51857

protocol SomeProtocol {
  func someFunction()
  func anotherFunction()
}

extension SomeProtocol {
  func someFunction() {
    let _ = #selector(anotherFunction) // expected-error {{cannot use 'anotherFunction' as a selector because protocol 'SomeProtocol' is not exposed to Objective-C}} {{none}}
  }

  func anotherFunction() {
    print("Hello world!")
 }
}

@objc class OverloadedFuncAndProperty {
  @objc static func f() {}
  @objc var f: Int { 0 }
}

func test() -> Selector {
  #selector(OverloadedFuncAndProperty.f)
}
