// RUN: %target-typecheck-verify-swift

extension extension_for_invalid_type_1 { // expected-error {{use of undeclared type 'extension_for_invalid_type_1'}}
  func f() { }
}
extension extension_for_invalid_type_2 { // expected-error {{use of undeclared type 'extension_for_invalid_type_2'}}
  static func f() { }
}
extension extension_for_invalid_type_3 { // expected-error {{use of undeclared type 'extension_for_invalid_type_3'}}
  init() {}
}
extension extension_for_invalid_type_4 { // expected-error {{use of undeclared type 'extension_for_invalid_type_4'}}
  deinit {} // expected-error {{deinitializers may only be declared within a class}}
}
extension extension_for_invalid_type_5 { // expected-error {{use of undeclared type 'extension_for_invalid_type_5'}}
  typealias X = Int
}

//===--- Test that we only allow extensions at file scope.
struct Foo { }

extension NestingTest1 { // expected-error {{use of undeclared type 'NestingTest1'}}
  extension Foo {} // expected-error {{declaration is only valid at file scope}}
}
struct NestingTest2 {
  extension Foo {} // expected-error {{declaration is only valid at file scope}}
}
class NestingTest3 {
  extension Foo {} // expected-error {{declaration is only valid at file scope}}
}
enum NestingTest4 {
  extension Foo {} // expected-error {{declaration is only valid at file scope}}
}
protocol NestingTest5 {
  extension Foo {} // expected-error {{declaration is only valid at file scope}}
}
func nestingTest6() {
  extension Foo {} // expected-error {{declaration is only valid at file scope}}
}

//===--- Test that we only allow extensions only for nominal types.

struct S1 {
  struct NestedStruct {}
}
extension S1 {} // no-error
extension S1.Type {} // expected-error {{cannot extend a metatype 'S1.Type'}}
extension S1.NestedStruct {} // no-error

struct S1_2 {
  // expected-error @+2 {{type member must not be named 'Type', since it would conflict with the 'foo.Type' expression}}
  // expected-note @+1 {{if this name is unavoidable, use backticks to escape it}} {{8-12=`Type`}}
  enum Type {}
}
struct S1_3 {
  enum `Type` {} // no-error
}

extension S1_2.Type {} // expected-error {{cannot extend a metatype 'S1_2.Type'}}
extension S1_3.`Type` {} // no-error

typealias TA_S1 = S1
extension TA_S1 {} // no-error

typealias TA_S1_NestedStruct = S1.NestedStruct
extension TA_S1_NestedStruct {} // no-error

enum U1 {
  struct NestedStruct {}
}
extension U1 {} // no-error
extension U1.NestedStruct {} // no-error

class C1 {
  struct NestedStruct {}
}
extension C1 {} // no-error
extension C1.NestedStruct {} // no-error

protocol P1 {}

protocol P2 {}

extension () {} // expected-error {{non-nominal type '()' cannot be extended}}

typealias TupleAlias = (x: Int, y: Int)
extension TupleAlias {} // expected-error{{non-nominal type 'TupleAlias' (aka '(x: Int, y: Int)') cannot be extended}}

// Test property accessors in extended types
class C {}
extension C {
  var p1: Int {
    get {return 1}
    set(v) {}
  }
}
var c = C()
var x = c.p1
c.p1 = 1

protocol P3 {
  associatedtype Assoc
  func foo() -> Assoc
}

struct X3 : P3 {
}

extension X3.Assoc {
}

extension X3 {
  func foo() -> Int { return 0 }
}

// Make sure the test case from https://bugs.swift.org/browse/SR-3847 doesn't
// cause problems when the later extension is incorrectly nested inside another
// declaration.
extension C1.NestedStruct {
  static let originalValue = 0
}
struct WrapperContext {
  extension C1.NestedStruct { // expected-error {{declaration is only valid at file scope}}
    static let propUsingMember = originalValue
  }
}

// Class-constrained extension where protocol does not impose class requirement
// SR-11298

protocol DoesNotImposeClassReq_1 {}

class JustAClass: DoesNotImposeClassReq_1 {
  var property: String = ""
}

extension DoesNotImposeClassReq_1 where Self: JustAClass {
  var wrappingProperty1: String {
    get { return property }
    set { property = newValue } // Okay
  }
  
  var wrappingProperty2: String {
    get { return property }
    nonmutating set { property = newValue } // Okay
  }
  
  var wrappingProperty3: String {
    get { return property }
    mutating set { property = newValue } // Okay
  }
  
  mutating func foo() {
    property = "" // Okay
    wrappingProperty1 = "" // Okay
    wrappingProperty2 = "" // Okay
    wrappingProperty3 = "" // Okay
  }
  
  func bar() { // expected-note {{mark method 'mutating' to make 'self' mutable}}{{3-3=mutating }}
    property = "" // Okay
    wrappingProperty1 = "" // Okay
    wrappingProperty2 = "" // Okay
    wrappingProperty3 = "" // expected-error {{cannot assign to property: 'self' is immutable}}
  }
  
  nonmutating func baz() { // expected-note {{mark method 'mutating' to make 'self' mutable}}{{3-14=mutating}}
    property = "" // Okay
    wrappingProperty1 = "" // Okay
    wrappingProperty2 = "" // Okay
    wrappingProperty3 = "" // expected-error {{cannot assign to property: 'self' is immutable}}
  }
}

let instanceOfJustAClass1 = JustAClass() // expected-note 2{{change 'let' to 'var' to make it mutable}}
instanceOfJustAClass1.wrappingProperty1 = "" // Okay
instanceOfJustAClass1.wrappingProperty2 = "" // Okay
instanceOfJustAClass1.wrappingProperty3 = "" // expected-error {{cannot assign to property: 'instanceOfJustAClass1' is a 'let' constant}}
instanceOfJustAClass1.foo() // expected-error {{cannot use mutating member on immutable value: 'instanceOfJustAClass1' is a 'let' constant}}
instanceOfJustAClass1.bar() // Okay
instanceOfJustAClass1.baz() // Okay

var instanceOfJustAClass2 = JustAClass()
instanceOfJustAClass2.foo() // Okay

protocol DoesNotImposeClassReq_2 {
  var property: String { get set }
}

extension DoesNotImposeClassReq_2 where Self : AnyObject {
  var wrappingProperty1: String {
    get { property }
    set { property = newValue } // expected-error {{cannot assign to property: 'self' is immutable}}
    // expected-note@-1 {{mark accessor 'mutating' to make 'self' mutable}}{{5-5=mutating }}
  }
  
  var wrappingProperty2: String {
    get { property }
    nonmutating set { property = newValue } // expected-error {{cannot assign to property: 'self' is immutable}}
    // expected-note@-1 {{mark accessor 'mutating' to make 'self' mutable}}{{5-16=mutating}}
  }
  
  var wrappingProperty3: String {
    get { property }
    mutating set { property = newValue } // Okay
  }
  
  mutating func foo() {
    property = "" // Okay
    wrappingProperty1 = "" // Okay (the error is on the setter declaration above)
    wrappingProperty2 = "" // Okay (the error is on the setter declaration above)
    wrappingProperty3 = "" // Okay
  }
  
  func bar() { // expected-note 2{{mark method 'mutating' to make 'self' mutable}}{{3-3=mutating }}
    property = "" // expected-error {{cannot assign to property: 'self' is immutable}}
    wrappingProperty1 = "" // Okay (the error is on the setter declaration above)
    wrappingProperty2 = "" // Okay (the error is on the setter declaration above)
    wrappingProperty3 = "" // expected-error {{cannot assign to property: 'self' is immutable}}
  }
  
  nonmutating func baz() { // expected-note 2{{mark method 'mutating' to make 'self' mutable}}{{3-14=mutating}}
    property = "" // expected-error {{cannot assign to property: 'self' is immutable}}
    wrappingProperty1 = "" // Okay (the error is on the setter declaration above)
    wrappingProperty2 = "" // Okay (the error is on the setter declaration above)
    wrappingProperty3 = "" // expected-error {{cannot assign to property: 'self' is immutable}}
  }
}

protocol DoesNotImposeClassReq_3 {
  var someProperty: Int { get set }
}

class JustAClass1: DoesNotImposeClassReq_3 {
  var someProperty = 0
}

extension DoesNotImposeClassReq_3 where Self: JustAClass1 {
  var anotherProperty1: Int {
    get { return someProperty }
    set { someProperty = newValue } // Okay
  }

  var anotherProperty2: Int {
    get { return someProperty }
    mutating set { someProperty = newValue } // Okay
  }
}

let justAClass1 = JustAClass1() // expected-note {{change 'let' to 'var' to make it mutable}}
justAClass1.anotherProperty1 = 1234 // Okay
justAClass1.anotherProperty2 = 4321 // expected-error {{cannot assign to property: 'justAClass1' is a 'let' constant}}

protocol ImposeClassReq1: AnyObject {
  var someProperty: Int { get set }
}

class JustAClass2: ImposeClassReq1 {
  var someProperty = 0
}

extension ImposeClassReq1 where Self: AnyObject {
  var wrappingProperty1: Int {
    get { return someProperty }
    set { someProperty = newValue }
  }

  var wrappingProperty2: Int {
    get { return someProperty }
    mutating set { someProperty = newValue } // expected-error {{'mutating' isn't valid on methods in classes or class-bound protocols}}
  }

  mutating func foo() { // expected-error {{mutating' isn't valid on methods in classes or class-bound protocols}}
    someProperty = 1
  }

  nonmutating func bar() { // expected-error {{'nonmutating' isn't valid on methods in classes or class-bound protocols}}
    someProperty = 2
  }

  func baz() { // Okay
    someProperty = 3
  }
}

extension ImposeClassReq1 {
  var wrappingProperty3: Int {
    get { return someProperty }
    set { someProperty = newValue }
  }
}

let justAClass2 = JustAClass2() // expected-note {{change 'let' to 'var' to make it mutable}}
justAClass2.wrappingProperty1 = 9876 // Okay
justAClass2.wrappingProperty3 = 0987 // Okay
justAClass2.foo() // expected-error {{cannot use mutating member on immutable value: 'justAClass2' is a 'let' constant}}
justAClass2.bar() // Okay as well (complains about explicit nonmutating on decl)
justAClass2.baz() // Okay

protocol ImposeClassReq2: AnyObject {
  var someProperty: Int { get set }
}

extension ImposeClassReq2 {
  var wrappingProperty1: Int {
    get { return someProperty }
    set { someProperty = newValue }
  }

  var wrappingProperty2: Int {
    get { return someProperty }
    mutating set { someProperty = newValue } // expected-error {{'mutating' isn't valid on methods in classes or class-bound protocols}}
  }

  mutating func foo() { // expected-error {{mutating' isn't valid on methods in classes or class-bound protocols}}
    someProperty = 1
  }

  nonmutating func bar() { // expected-error {{'nonmutating' isn't valid on methods in classes or class-bound protocols}}
    someProperty = 2
  }

  func baz() { // Okay
    someProperty = 3
  }
}

// Reject extension of nominal type via parameterized typealias

struct Nest<Egg> { typealias Contents = Egg }
struct Tree { 
  typealias LimbContent = Nest<Int> 
  typealias BoughPayload = Nest<Nest<Int>>
}

extension Tree.LimbContent.Contents {
  // expected-error@-1 {{extension of type 'Tree.LimbContent.Contents' (aka 'Int') must be declared as an extension of 'Int'}}
  // expected-note@-2 {{did you mean to extend 'Int' instead?}} {{11-36=Int}}
}

extension Tree.BoughPayload.Contents {
 // expected-error@-1 {{constrained extension must be declared on the unspecialized generic type 'Nest'}}
}
