//@flow

type P = {c?: () => void, b?: {c?: {d: number}}};
declare var cc: P;
if (cc.b?.c) {
  var xxx: number = cc.b.c.d;
}

declare var a: ?{
  b?: {c?: {d: number}, e: number, f: ?() => number, g: ?() => number},
};

if (a && a.b) {
  a.b as {}; //ok
  a.b.c as ?{}; //ok
  a.b.c?.d as ?number; //ok
  a.b.c.d; // bad
  a?.b.c?.d as ?number; // ok, one unneeded optional chain
  a?.b.e as number; // ok, one unneeded optional chain
  a.b?.e as number; // ok, one unneeded optional chain
}

if (a && a.b) {
  a.b?.f() as ?number; // unneeded chain and bad
}

if (a && a.b) {
  a.b.f?.() as ?number; // ok
}

if (a && a.b) {
  a.b?.f?.() as ?number; // ok, one unneeded optional chain
}

if (a && a.b && a.b.g) {
  a.b.g() as number; // ok
}

if (a && a.b && a.b.g) {
  a.b.g?.() as number; // ok, unneeded chain
}

if (a && a.b && a.b.g) {
  a.b?.g() as number; // ok, unneeded chain
}

if (a && a.b && a.b.g) {
  a.b?.g?.() as number; // ok, two unneeded chains
} else {
  a as {}; // should fail, sanity check
  a as null | void; // should fail, sanity check
}

function f<T: any>(x: ?T) {
  if (x?.a === null) {
    return;
  }
  if (x) {
    x.a as empty; // ok
  }
  x.a as empty; // should fail
}

if (a?.b) {
  a.b as {}; //ok
  a.b.c as ?{}; //ok
  a.b.c?.d as ?number; //ok
  a.b.c.d; // bad
  a?.b.c?.d as ?number; // ok, one unneeded optional chain
  a?.b.e as number; // ok, one unneeded optional chain
  a.b?.e as number; // ok, one unneeded optional chain
}

if (a?.b) {
  a.b?.f() as ?number; // unneeded chain and bad
}

if (a?.b) {
  a.b.f?.() as ?number; // ok
}

if (a?.b) {
  a.b?.f?.() as ?number; // ok, one unneeded optional chain
}

if (a?.b?.g) {
  a.b.g() as number; // ok
}

if (a?.b?.g) {
  a.b.g?.() as number; // ok, unneeded chain
}

if (a?.b?.g) {
  a.b?.g() as number; // ok, unneeded chain
}

if (a?.b?.g) {
  a.b?.g?.() as number; // ok, two unneeded chains
} else {
  a as {}; // should fail, sanity check
  a as null | void; // should fail, sanity check
}
type Z = {|a: 'hello ', value: number|} | {|b: 'goodbye', value: string|};
declare var b: ?{x: boolean, y?: boolean, z: Z, w?: {u: () => number}};

if (b?.x) {
  b.x as true; // ok
} else {
  b.x as false; // nope
}

if (b?.w.u) {
  // error here
  b as {}; // ok
  b.w.u(); // ok, because of error in predicate
}

if (b && b.w.u) {
  // error consistent with above
  b.w.u(); // no error consistent with above
}

if (b?.z.a) {
  b.z.value as number; // yes
} else {
  b.z.value as string; // error
}

if (b?.y) {
  // sketchy null
  b.y as true; // ok
} else {
  b.y as false; // nope
}

declare var c: Array<?Array<{a: number, b: {c: {d: string}}}>>;
if (c[0]?.[0].b.c) {
  c[0][0].a as number; //yes
  c[0][0].b as {}; //yes
  c[1][0] as {}; // no
  c[0][1].a as number; // yes
}

declare var d: ?{a?: () => {b?: {c: number}}, d: number};
if (d?.a?.().b) {
  d.a().b.c as number; // nope, never was a refinement in the first place
}

declare var a11: ?({a: string} | {});
declare var b11: ?{};
// No error on looking up a11?.a, but that's consistent with non-optional behavior
var x11: empty = a11?.a || b11?.a;

declare var e: ?string;
if (e?.length) {
}

function havoc_d() {
  d = null;
}
