// RUN: %clang_cc1 -fsyntax-only -verify -Wno-objc-root-class %s

// # Flexible array member.
// ## Instance variables only in interface.
@interface LastIvar {
  char flexible[];
}
@end

@interface NotLastIvar {
  char flexible[]; // expected-error {{flexible array member 'flexible' with type 'char []' is not at the end of class}}
  int last; // expected-note {{next instance variable declaration is here}}
}
@end

// ## Instance variables in implementation.
@interface LastIvarInImpl
@end
@implementation LastIvarInImpl {
  char flexible[]; // expected-warning {{field 'flexible' with variable sized type 'char []' is not visible to subclasses and can conflict with their instance variables}}
}
@end

@interface NotLastIvarInImpl
@end
@implementation NotLastIvarInImpl {
  char flexible[]; // expected-error {{flexible array member 'flexible' with type 'char []' is not at the end of class}}
  // expected-warning@-1 {{field 'flexible' with variable sized type 'char []' is not visible to subclasses and can conflict with their instance variables}}
  int last; // expected-note {{next instance variable declaration is here}}
}
@end

@implementation NotLastIvarInImplWithoutInterface { // expected-warning {{cannot find interface declaration for 'NotLastIvarInImplWithoutInterface'}}
  char flexible[]; // expected-error {{flexible array member 'flexible' with type 'char []' is not at the end of class}}
  // expected-warning@-1 {{field 'flexible' with variable sized type 'char []' is not visible to subclasses and can conflict with their instance variables}}
  int last; // expected-note {{next instance variable declaration is here}}
}
@end

@interface LastIvarInClass_OtherIvarInImpl {
  char flexible[]; // expected-error {{flexible array member 'flexible' with type 'char []' is not at the end of class}}
}
@end
@implementation LastIvarInClass_OtherIvarInImpl {
  int last; // expected-note {{next instance variable declaration is here}}
}
@end

// ## Non-instance variables in implementation.
@interface LastIvarInClass_UnrelatedVarInImpl {
  char flexible[];
}
@end
@implementation LastIvarInClass_UnrelatedVarInImpl
int nonIvar;
@end

// ## Instance variables in class extension.
@interface LastIvarInExtension
@end
@interface LastIvarInExtension() {
  char flexible[]; // expected-warning {{field 'flexible' with variable sized type 'char []' is not visible to subclasses and can conflict with their instance variables}}
}
@end

@interface NotLastIvarInExtension
@end
@interface NotLastIvarInExtension() {
  char flexible[]; // expected-error {{flexible array member 'flexible' with type 'char []' is not at the end of class}}
  // expected-warning@-1 {{field 'flexible' with variable sized type 'char []' is not visible to subclasses and can conflict with their instance variables}}
  int last; // expected-note {{next instance variable declaration is here}}
}
@end

@interface LastIvarInClass_OtherIvarInExtension {
  char flexible[]; // expected-error {{flexible array member 'flexible' with type 'char []' is not at the end of class}}
}
@end
@interface LastIvarInClass_OtherIvarInExtension() {
  int last; // expected-note {{next instance variable declaration is here}}
}
@end

@interface LastIvarInExtension_OtherIvarInExtension
@end
@interface LastIvarInExtension_OtherIvarInExtension() {
  int last; // expected-note {{next instance variable declaration is here}}
}
@end
@interface LastIvarInExtension_OtherIvarInExtension()
// Extension without ivars to test we see through such extensions.
@end
@interface LastIvarInExtension_OtherIvarInExtension() {
  char flexible[]; // expected-error {{flexible array member 'flexible' with type 'char []' is not at the end of class}}
  // expected-warning@-1 {{field 'flexible' with variable sized type 'char []' is not visible to subclasses and can conflict with their instance variables}}
}
@end

@interface LastIvarInExtension_OtherIvarInImpl
@end
@interface LastIvarInExtension_OtherIvarInImpl() {
  char flexible[]; // expected-error {{flexible array member 'flexible' with type 'char []' is not at the end of class}}
  // expected-warning@-1 {{field 'flexible' with variable sized type 'char []' is not visible to subclasses and can conflict with their instance variables}}
}
@end
@implementation LastIvarInExtension_OtherIvarInImpl {
  int last; // expected-note {{next instance variable declaration is here}}
}
@end

// ## Instance variables in named categories.
@interface IvarInNamedCategory
@end
@interface IvarInNamedCategory(Category) {
  char flexible[]; // expected-error {{instance variables may not be placed in categories}}
}
@end

// ## Synthesized instance variable.
@interface LastIvarAndProperty {
  char _flexible[];
}
@property char flexible[]; // expected-error {{property cannot have array or function type 'char []'}}
@end

// ## Synthesize other instance variables.
@interface LastIvar_ExplicitlyNamedPropertyBackingIvarPreceding {
  int _elementsCount;
  char flexible[];
}
@property int count;
@end
@implementation LastIvar_ExplicitlyNamedPropertyBackingIvarPreceding
@synthesize count = _elementsCount;
@end

@interface LastIvar_ImplicitlyNamedPropertyBackingIvarPreceding {
  int count;
  char flexible[];
}
@property int count;
@end
@implementation LastIvar_ImplicitlyNamedPropertyBackingIvarPreceding
@synthesize count;
@end

@interface NotLastIvar_ExplicitlyNamedPropertyBackingIvarLast {
  char flexible[]; // expected-error {{flexible array member 'flexible' with type 'char []' is not at the end of class}}
}
@property int count;
@end
@implementation NotLastIvar_ExplicitlyNamedPropertyBackingIvarLast
@synthesize count = _elementsCount; // expected-note {{next synthesized instance variable is here}}
@end

@interface NotLastIvar_ImplicitlyNamedPropertyBackingIvarLast {
  char flexible[]; // expected-error {{flexible array member 'flexible' with type 'char []' is not at the end of class}}
}
@property int count; // expected-note {{next synthesized instance variable is here}}
@end
@implementation NotLastIvar_ImplicitlyNamedPropertyBackingIvarLast
// Test auto-synthesize.
//@synthesize count;
@end


// # Variable sized types.
struct Packet {
  unsigned int size;
  char data[];
};

// ## Instance variables only in interface.
@interface LastStructIvar {
  struct Packet flexible;
}
@end

@interface NotLastStructIvar {
  struct Packet flexible; // expected-error {{field 'flexible' with variable sized type 'struct Packet' is not at the end of class}}
  int last; // expected-note {{next instance variable declaration is here}}
}
@end

// ## Instance variables in implementation.
@interface LastStructIvarInImpl
@end
@implementation LastStructIvarInImpl {
  struct Packet flexible; // expected-warning {{field 'flexible' with variable sized type 'struct Packet' is not visible to subclasses and can conflict with their instance variables}}
}
@end

@interface NotLastStructIvarInImpl
@end
@implementation NotLastStructIvarInImpl {
  struct Packet flexible; // expected-error {{field 'flexible' with variable sized type 'struct Packet' is not at the end of class}}
  // expected-warning@-1 {{field 'flexible' with variable sized type 'struct Packet' is not visible to subclasses and can conflict with their instance variables}}
  int last; // expected-note {{next instance variable declaration is here}}
}
@end

@interface LastStructIvarInClass_OtherIvarInImpl {
  struct Packet flexible; // expected-error {{field 'flexible' with variable sized type 'struct Packet' is not at the end of class}}
}
@end
@implementation LastStructIvarInClass_OtherIvarInImpl {
  int last; // expected-note {{next instance variable declaration is here}}
}
@end

// ## Synthesized instance variable.
@interface LastSynthesizeStructIvar
@property int first;
@property struct Packet flexible; // expected-error {{synthesized property with variable size type 'struct Packet' requires an existing instance variable}}
@end
@implementation LastSynthesizeStructIvar
@end

@interface NotLastSynthesizeStructIvar
@property struct Packet flexible; // expected-error {{synthesized property with variable size type 'struct Packet' requires an existing instance variable}}
@property int last;
@end
@implementation NotLastSynthesizeStructIvar
@end

@interface LastStructIvarWithExistingIvarAndSynthesizedProperty {
  struct Packet _flexible;
}
@property struct Packet flexible;
@end
@implementation LastStructIvarWithExistingIvarAndSynthesizedProperty
@end


// # Subclasses.
@interface FlexibleArrayMemberBase {
  char flexible[]; // expected-note6 {{'flexible' declared here}}
}
@end

@interface NoIvarAdditions : FlexibleArrayMemberBase
@end
@implementation NoIvarAdditions
@end

@interface AddedIvarInInterface : FlexibleArrayMemberBase {
  int last; // expected-warning {{field 'last' can overwrite instance variable 'flexible' with variable sized type 'char []' in superclass 'FlexibleArrayMemberBase'}}
}
@end

@interface AddedIvarInImplementation : FlexibleArrayMemberBase
@end
@implementation AddedIvarInImplementation {
  int last; // expected-warning {{field 'last' can overwrite instance variable 'flexible' with variable sized type 'char []' in superclass 'FlexibleArrayMemberBase'}}
}
@end

@interface AddedIvarInExtension : FlexibleArrayMemberBase
@end
@interface AddedIvarInExtension() {
  int last; // expected-warning {{field 'last' can overwrite instance variable 'flexible' with variable sized type 'char []' in superclass 'FlexibleArrayMemberBase'}}
}
@end

@interface SynthesizedIvar : FlexibleArrayMemberBase
@property int count;
@end
@implementation SynthesizedIvar
@synthesize count; // expected-warning {{field 'count' can overwrite instance variable 'flexible' with variable sized type 'char []' in superclass 'FlexibleArrayMemberBase'}}
@end

@interface WarnInSubclassOnlyOnce : FlexibleArrayMemberBase {
  int last; // expected-warning {{field 'last' can overwrite instance variable 'flexible' with variable sized type 'char []' in superclass 'FlexibleArrayMemberBase'}}
}
@end
@interface WarnInSubclassOnlyOnce() {
  int laster;
}
@end
@implementation WarnInSubclassOnlyOnce {
  int lastest;
}
@end

@interface AddedIvarInSubSubClass : NoIvarAdditions {
  int last; // expected-warning {{field 'last' can overwrite instance variable 'flexible' with variable sized type 'char []' in superclass 'FlexibleArrayMemberBase'}}
}
@end
