//===--- ModuleAPIDiff.cpp - Swift Module API I/O -------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//

#include "ModuleAPIDiff.h"
#include "swift/AST/DiagnosticEngine.h"
#include "swift/AST/ASTVisitor.h"
#include "swift/Basic/SourceManager.h"
#include "swift/Driver/FrontendUtil.h"
#include "swift/Frontend/Frontend.h"
#include "swift/Frontend/PrintingDiagnosticConsumer.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/Optional.h"
#include "llvm/Support/YAMLTraits.h"
#include <memory>

using namespace swift;

/*

Machine-Readable Representation of API and ABI of a Swift Module (SMA)
======================================================================

SMA stands for Swift Module API/ABI.

This format is designed to accurately represent API (and, in future, ABI) of a
Swift module.  Design constraints are as follows:

- the format should allow comparing API and ABI of two different modules
  produced by two different compilers;

- the comparison procedure should be as simple as possible (ideally, applying
  diff(1) to the files should produce reasonable results; next preference is a
  simple, generic, tree diffing algorithm that is not actually aware of the
  data model);

- a comparison that is aware of the data model should be able to automatically
  judge if two modules are ABI- or ABI-compatible;

- the format should be machine-readable;

- the format should be human-readable as long as it does not hurt other goals;

- the format should be able to sustain significant changes (within reason) to
  any aspects of the Swift language, including syntax changes, type system
  changes, AST representation changes within the compiler etc.

- the immediate goal is to capture the module API, but we would like to be able
  to extend this format to carry ABI information in future.

If you find this format useful for any other purpose (for example, generating
documentation), it is completely incidental; we recommend not to rely on this
facility for other purposes.

module ::=
    Name: <identifier>
    ExportedModules: [ <submodule-name>* ]  (default: none)
    <nested-decls>

nested-decls ::=
    NestedDecls:                                (all unordered, default: none)
        Structs: [ <struct-decl>* ]
        Enums: [ <enum-decl>* ]
        Classes: [ <class-decl>* ]
        Protocols: [ <protocol-decl>* ]
        Typealiases: [ <typealias-decl>* ]
        AssociatedTypes: [ <associated-type-decl>* ]
        Vars: [ <var-decl>* ]
        Lets: [ <let-decl>* ]
        Functions: [ <func-decl>* ]
        Initializers: [ <init-decl>* ]
        Deinitializers: [ <deinit-decl>* ]

generic-signature ::=
    GenericSignature:
        GenericParams:                  (ordered)
            - Name: <identifier>
        ConformanceRequirements:
            - Type: <type-name>
              ProtocolOrClass: <type-name>
        SameTypeRequirements:
            - FirstType: <type-name>
              SecondType: <type-name>

struct-decl ::=
    Name: <identifier>
    <generic-signature>?
    ConformsToProtocols: [ <type-name>* ]       (default: none)
    <decl-attributes>?
    <nested-decls>

enum-case-decl ::=
    Name: <identifier>
    ArgumentType: <type-name>                   (default: none)
    RawValue: <string>                          (default: none)
    <decl-attributes>?

enum-decl ::=
    Name: <identifier>
    <generic-signature>?
    RawType: <type-name>?                       (default: none)
    ConformsToProtocols: [ <type-name>* ]       (default: none)
    <decl-attributes>?
    Cases: [ <enum-case-decl>* ]                (default: none)
    <nested-decls>

class-decl ::=
    Name: <identifier>
    <generic-signature>?
    Superclass: <type-name>?                    (default: none)
    ConformsToProtocols: [ <type-name>* ]       (default: none)
    <decl-attributes>?
    <nested-decls>

protocol-decl ::=
    Name: <identifier>
    ConformsToProtocols: [ <type-name>* ]       (default: none)
    <decl-attributes>?
    <nested-decls>

typealias-decl ::=
    Name: <identifier>
    Type: <type-name>
    <decl-attributes>?

associated-type-decl ::=
    Name: <identifier>
    Superclass: <type-name>?                    (default: none)
    DefaultDefinition: <type-name>?             (default: none)
    <decl-attributes>?

var-decl ::=
    Name: <identifier>
    Type: <type-name>
    IsSettable: <bool>
    IsStored: <bool>
    <decl-attributes>?

let-decl ::=
    Name: <identifier>
    Type: <type-name>
    <decl-attributes>?

func-param ::=
    Name: <identifier>
    Type: <type-name>
    IsInout: <bool>             (default: false)
    <decl-attributes>?

func-decl ::=
    IsStatic: <bool>            (default: false)
    Name: <function-name>
    <generic-signature>?
    Params:
        [ [ <func-param>* ]* ]
    ResultType: <type-name>
    <decl-attributes>?

init-decl ::=
    InitializerKind: (Designated|Convenience)
    InitializerFailability: (None|Optional|ImplicitlyUnwrappedOptional) (default: None)
    <generic-signature>?
    Params:
        [ <func-param>* ]
    IsTrappingStub: <bool>      (default: false)
    <decl-attributes>?

deinit-decl ::=
    Name: deinit (always 'deinit'; introduced so that the decl is non-empty)
    <decl-attributes>?

identifier ::= <string>

// A sequence of dot-separated identifiers.
submodule-name ::= <string>

type-name ::= <string>

decl-attributes ::=
    Attributes:                         (default: false)
        IsClassProtocol: <bool>
        IsDynamic: <bool>
        IsFinal: <bool>
        IsLazy: <bool>
        IsMutating: <bool>
        IsObjC: <bool>
        IsOptional: <bool>
        IsRequired: <bool>
        IsRequiresStoredPropertyInits: <bool>
        IsTransparent: <bool>
        IsWeak: <bool>

*/

// SMA data model is defined in 'swift::sma' namespace.
//
// Never 'use namespace swift::sma'.
//
// It is fine to shadow names from the 'swift' namespace in 'swift::sma'.
//
// Don't use any AST types in 'swift::sma'.  Only use simple types like
// 'std::string', 'std::vector', 'std::map' etc.

/// Define a type 'swift::sma::TYPE_NAME' that is a "strong typedef" for
/// 'std::string'.
#define DEFINE_SMA_STRING_STRONG_TYPEDEF(TYPE_NAME, STRING_MEMBER_NAME)        \
  namespace swift {                                                            \
  namespace sma {                                                              \
  struct TYPE_NAME {                                                           \
    std::string STRING_MEMBER_NAME;                                            \
  };                                                                           \
  } /* namespace sma */                                                        \
  } /* namespace swift */                                                      \
                                                                               \
  namespace llvm {                                                             \
  namespace yaml {                                                             \
  template <> struct ScalarTraits<::swift::sma::TYPE_NAME> {                   \
    static void output(const ::swift::sma::TYPE_NAME &Val, void *Context,      \
                       llvm::raw_ostream &Out) {                               \
      ScalarTraits<std::string>::output(Val.STRING_MEMBER_NAME, Context, Out); \
    }                                                                          \
    static StringRef input(StringRef Scalar, void *Context,                    \
                           ::swift::sma::TYPE_NAME &Val) {                     \
      return ScalarTraits<std::string>::input(Scalar, Context,                 \
                                              Val.STRING_MEMBER_NAME);         \
    }                                                                          \
    static bool mustQuote(StringRef S) {                                       \
      return ScalarTraits<std::string>::mustQuote(S);                          \
    }                                                                          \
  };                                                                           \
  } /* namespace yaml */                                                       \
  } /* namespace llvm */

DEFINE_SMA_STRING_STRONG_TYPEDEF(Identifier, Name)
DEFINE_SMA_STRING_STRONG_TYPEDEF(FunctionName, Name)
DEFINE_SMA_STRING_STRONG_TYPEDEF(TypeName, Name)
DEFINE_SMA_STRING_STRONG_TYPEDEF(SubmoduleName, Name)

#undef DEFINE_SMA_STRING_STRONG_TYPEDEF

namespace swift {
namespace sma {

using llvm::Optional;

#define SMA_FOR_EVERY_DECL_ATTRIBUTE(MACRO) \
  MACRO(IsDynamic) \
  MACRO(IsFinal) \
  MACRO(IsLazy) \
  MACRO(IsMutating) \
  MACRO(IsObjC) \
  MACRO(IsOptional) \
  MACRO(IsRequired) \
  MACRO(IsRequiresStoredPropertyInits) \
  MACRO(IsTransparent) \
  MACRO(IsWeak)

struct DeclAttributes {
#define DEFINE_MEMBER(NAME) bool NAME = false;
  SMA_FOR_EVERY_DECL_ATTRIBUTE(DEFINE_MEMBER)
#undef DEFINE_MEMBER
};

bool operator==(const DeclAttributes &LHS, const DeclAttributes &RHS) {
  return
#define PROCESS_MEMBER(NAME) LHS.NAME == RHS.NAME &&
      SMA_FOR_EVERY_DECL_ATTRIBUTE(PROCESS_MEMBER)
#undef PROCESS_MEMBER
      // Add an identity element for && over bools.
      true;
}

struct StructDecl;
struct EnumDecl;
struct ClassDecl;
struct ProtocolDecl;
struct TypealiasDecl;
struct AssociatedTypeDecl;
struct VarDecl;
struct LetDecl;
struct FuncDecl;
struct InitDecl;
struct DeinitDecl;

/// A container for declarations contained in some declaration context.
struct NestedDecls {
  std::vector<std::shared_ptr<StructDecl>> Structs;
  std::vector<std::shared_ptr<EnumDecl>> Enums;
  std::vector<std::shared_ptr<ClassDecl>> Classes;
  std::vector<std::shared_ptr<ProtocolDecl>> Protocols;
  std::vector<std::shared_ptr<TypealiasDecl>> Typealiases;
  std::vector<std::shared_ptr<AssociatedTypeDecl>> AssociatedTypes;
  std::vector<std::shared_ptr<VarDecl>> Vars;
  std::vector<std::shared_ptr<LetDecl>> Lets;
  std::vector<std::shared_ptr<FuncDecl>> Functions;
  std::vector<std::shared_ptr<InitDecl>> Initializers;
  std::vector<std::shared_ptr<DeinitDecl>> Deinitializers;

  bool isEmpty() const {
    return Structs.empty() && Enums.empty() && Classes.empty() &&
           Protocols.empty() && Typealiases.empty() &&
           AssociatedTypes.empty() && Vars.empty() && Lets.empty() &&
           Functions.empty() && Initializers.empty() && Deinitializers.empty();
  }
};

bool operator==(const NestedDecls &LHS, const NestedDecls &RHS) {
  // Only empty instances compare equal.
  return LHS.isEmpty() && RHS.isEmpty();
}

struct GenericParam {
  Identifier Name;
};

struct ConformanceRequirement {
  TypeName Type;
  TypeName Protocol;
};

struct SameTypeRequirement {
  TypeName FirstType;
  TypeName SecondType;
};

struct GenericSignature {
  std::vector<GenericParam> GenericParams;
  std::vector<ConformanceRequirement> ConformanceRequirements;
  std::vector<SameTypeRequirement> SameTypeRequirements;
};

struct Module {
  Identifier Name;
  std::vector<SubmoduleName> ExportedModules;
  NestedDecls Decls;
};

struct StructDecl {
  Identifier Name;
  Optional<GenericSignature> TheGenericSignature;
  std::vector<TypeName> ConformsToProtocols;
  DeclAttributes Attributes;
  NestedDecls Decls;
};

struct EnumCaseDecl {
  Identifier Name;
  TypeName ArgumentType;
  std::string RawValue;
  DeclAttributes Attributes;
};

struct EnumDecl {
  Identifier Name;
  Optional<GenericSignature> TheGenericSignature;
  Optional<TypeName> RawType;
  std::vector<TypeName> ConformsToProtocols;
  DeclAttributes Attributes;
  std::vector<std::shared_ptr<EnumCaseDecl>> Cases;
  NestedDecls Decls;
};

struct ClassDecl {
  Identifier Name;
  Optional<GenericSignature> TheGenericSignature;
  Optional<TypeName> Superclass;
  std::vector<TypeName> ConformsToProtocols;
  DeclAttributes Attributes;
  NestedDecls Decls;
};

struct ProtocolDecl {
  Identifier Name;
  std::vector<TypeName> ConformsToProtocols;
  DeclAttributes Attributes;
  NestedDecls Decls;
};

struct TypealiasDecl {
  Identifier Name;
  TypeName Type;
  DeclAttributes Attributes;
};

struct AssociatedTypeDecl {
  Identifier Name;
  Optional<TypeName> Superclass;
  Optional<TypeName> DefaultDefinition;
  DeclAttributes Attributes;
};

struct VarDecl {
  Identifier Name;
  TypeName Type;
  bool IsSettable = false;
  bool IsStored = false;
  DeclAttributes Attributes;
};

struct LetDecl {
  Identifier Name;
  TypeName Type;
  DeclAttributes Attributes;
};

struct FuncParam {
  Identifier Name;
  TypeName Type;
  bool IsInout = false;
  DeclAttributes Attributes;
};

struct FuncDecl {
  FunctionName Name;
  bool IsStatic = false;
  Optional<GenericSignature> TheGenericSignature;
  std::vector<std::vector<FuncParam>> Params;
  TypeName ResultType;
  DeclAttributes Attributes;
};

enum class InitializerKind {
  Designated,
  Convenience,
};

enum class InitializerFailability {
  None, // default
  Optional,
  ImplicitlyUnwrappedOptional,
};

struct InitDecl {
  InitializerKind TheInitializerKind;
  InitializerFailability TheInitializerFailability;
  Optional<GenericSignature> TheGenericSignature;
  std::vector<FuncParam> Params;
  bool IsTrappingStub = false;
  DeclAttributes Attributes;
};

struct DeinitDecl {
  Identifier Name;
  DeclAttributes Attributes;
};

} // namespace sma
} // namespace swift

namespace llvm {
namespace yaml {
template <typename T> struct MappingTraits<std::shared_ptr<T>> {
  static void mapping(IO &io, std::shared_ptr<T> &Ptr) {
    MappingTraits<T>::mapping(io, *Ptr.get());
  }
};
} // namespace yaml
} // namespace llvm

LLVM_YAML_IS_SEQUENCE_VECTOR(::swift::sma::TypeName)
LLVM_YAML_IS_SEQUENCE_VECTOR(::swift::sma::SubmoduleName)

LLVM_YAML_IS_SEQUENCE_VECTOR(std::shared_ptr<::swift::sma::StructDecl>)
LLVM_YAML_IS_SEQUENCE_VECTOR(std::shared_ptr<::swift::sma::EnumCaseDecl>)
LLVM_YAML_IS_SEQUENCE_VECTOR(std::shared_ptr<::swift::sma::EnumDecl>)
LLVM_YAML_IS_SEQUENCE_VECTOR(std::shared_ptr<::swift::sma::ClassDecl>)
LLVM_YAML_IS_SEQUENCE_VECTOR(std::shared_ptr<::swift::sma::ProtocolDecl>)
LLVM_YAML_IS_SEQUENCE_VECTOR(std::shared_ptr<::swift::sma::TypealiasDecl>)
LLVM_YAML_IS_SEQUENCE_VECTOR(std::shared_ptr<::swift::sma::AssociatedTypeDecl>)
LLVM_YAML_IS_SEQUENCE_VECTOR(std::shared_ptr<::swift::sma::VarDecl>)
LLVM_YAML_IS_SEQUENCE_VECTOR(std::shared_ptr<::swift::sma::LetDecl>)
LLVM_YAML_IS_SEQUENCE_VECTOR(::swift::sma::FuncParam)
LLVM_YAML_IS_SEQUENCE_VECTOR(std::vector<::swift::sma::FuncParam>)
LLVM_YAML_IS_SEQUENCE_VECTOR(std::shared_ptr<::swift::sma::FuncDecl>)
LLVM_YAML_IS_SEQUENCE_VECTOR(std::shared_ptr<::swift::sma::InitDecl>)
LLVM_YAML_IS_SEQUENCE_VECTOR(std::shared_ptr<::swift::sma::DeinitDecl>)

LLVM_YAML_IS_SEQUENCE_VECTOR(::swift::sma::GenericParam)
LLVM_YAML_IS_SEQUENCE_VECTOR(::swift::sma::ConformanceRequirement)
LLVM_YAML_IS_SEQUENCE_VECTOR(::swift::sma::SameTypeRequirement)

namespace llvm {
namespace yaml {
template <> struct MappingTraits<::swift::sma::DeclAttributes> {
  static void mapping(IO &io, ::swift::sma::DeclAttributes &DA) {
#define SERIALIZE_MEMBER(NAME) io.mapOptional(#NAME, DA.NAME, false);
    SMA_FOR_EVERY_DECL_ATTRIBUTE(SERIALIZE_MEMBER)
#undef SERIALIZE_MEMBER
  }
};

template <> struct MappingTraits<::swift::sma::NestedDecls> {
  // Defined out of line to break circular dependency.
  static void mapping(IO &io, ::swift::sma::NestedDecls &ND);
};

template <> struct MappingTraits<::swift::sma::GenericParam> {
  static void mapping(IO &io, ::swift::sma::GenericParam &ND) {
    io.mapRequired("Name", ND.Name);
  }
};

template <> struct MappingTraits<::swift::sma::ConformanceRequirement> {
  static void mapping(IO &io, ::swift::sma::ConformanceRequirement &CR) {
    io.mapRequired("Type", CR.Type);
    io.mapRequired("Protocol", CR.Protocol);
  }
};

template <> struct MappingTraits<::swift::sma::SameTypeRequirement> {
  static void mapping(IO &io, ::swift::sma::SameTypeRequirement &STR) {
    io.mapRequired("FirstType", STR.FirstType);
    io.mapRequired("SecondType", STR.SecondType);
  }
};

template <> struct MappingTraits<::swift::sma::GenericSignature> {
  static void mapping(IO &io, ::swift::sma::GenericSignature &GS) {
    io.mapOptional("GenericParams", GS.GenericParams);
    io.mapOptional("ConformanceRequirements", GS.ConformanceRequirements);
    io.mapOptional("SameTypeRequirements", GS.SameTypeRequirements);
  }
};

template <> struct MappingTraits<::swift::sma::Module> {
  static void mapping(IO &io, ::swift::sma::Module &M) {
    io.mapRequired("Name", M.Name);
    io.mapOptional("ExportedModules", M.ExportedModules);
    io.mapOptional("NestedDecls", M.Decls, ::swift::sma::NestedDecls());
  }
};

template <> struct MappingTraits<::swift::sma::StructDecl> {
  static void mapping(IO &io, ::swift::sma::StructDecl &SD) {
    io.mapRequired("Name", SD.Name);
    io.mapOptional("GenericSignature", SD.TheGenericSignature);
    io.mapOptional("ConformsToProtocols", SD.ConformsToProtocols);
    io.mapOptional("Attributes", SD.Attributes, ::swift::sma::DeclAttributes());
    io.mapOptional("NestedDecls", SD.Decls, ::swift::sma::NestedDecls());
  }
};

template <> struct MappingTraits<::swift::sma::EnumCaseDecl> {
  static void mapping(IO &io, ::swift::sma::EnumCaseDecl &ECD) {
    io.mapRequired("Name", ECD.Name);
    io.mapOptional("ArgumentType", ECD.ArgumentType);
    io.mapOptional("RawValue", ECD.RawValue);
    io.mapOptional("Attributes", ECD.Attributes,
                   ::swift::sma::DeclAttributes());
  }
};

template <> struct MappingTraits<::swift::sma::EnumDecl> {
  static void mapping(IO &io, ::swift::sma::EnumDecl &ED) {
    io.mapRequired("Name", ED.Name);
    io.mapOptional("GenericSignature", ED.TheGenericSignature);
    io.mapOptional("RawType", ED.RawType);
    io.mapOptional("ConformsToProtocols", ED.ConformsToProtocols);
    io.mapOptional("Attributes", ED.Attributes, ::swift::sma::DeclAttributes());
    io.mapOptional("Cases", ED.Cases);
    io.mapOptional("NestedDecls", ED.Decls, ::swift::sma::NestedDecls());
  }
};

template <> struct MappingTraits<::swift::sma::ClassDecl> {
  static void mapping(IO &io, ::swift::sma::ClassDecl &CD) {
    io.mapRequired("Name", CD.Name);
    io.mapOptional("GenericSignature", CD.TheGenericSignature);
    io.mapOptional("Superclass", CD.Superclass);
    io.mapOptional("ConformsToProtocols", CD.ConformsToProtocols);
    io.mapOptional("Attributes", CD.Attributes, ::swift::sma::DeclAttributes());
    io.mapOptional("NestedDecls", CD.Decls, ::swift::sma::NestedDecls());
  }
};

template <> struct MappingTraits<::swift::sma::ProtocolDecl> {
  static void mapping(IO &io, ::swift::sma::ProtocolDecl &PD) {
    io.mapRequired("Name", PD.Name);
    io.mapOptional("ConformsToProtocols", PD.ConformsToProtocols);
    io.mapOptional("Attributes", PD.Attributes, ::swift::sma::DeclAttributes());
    io.mapOptional("NestedDecls", PD.Decls, ::swift::sma::NestedDecls());
  }
};

template <> struct MappingTraits<::swift::sma::TypealiasDecl> {
  static void mapping(IO &io, ::swift::sma::TypealiasDecl &TD) {
    io.mapRequired("Name", TD.Name);
    io.mapRequired("Type", TD.Type);
    io.mapOptional("Attributes", TD.Attributes, ::swift::sma::DeclAttributes());
  }
};

template <> struct MappingTraits<::swift::sma::AssociatedTypeDecl> {
  static void mapping(IO &io, ::swift::sma::AssociatedTypeDecl &ATD) {
    io.mapRequired("Name", ATD.Name);
    io.mapOptional("Superclass", ATD.Superclass);
    io.mapOptional("DefaultDefinition", ATD.DefaultDefinition);
    io.mapOptional("Attributes", ATD.Attributes,
                   ::swift::sma::DeclAttributes());
  }
};

template <> struct MappingTraits<::swift::sma::VarDecl> {
  static void mapping(IO &io, ::swift::sma::VarDecl &VD) {
    io.mapRequired("Name", VD.Name);
    io.mapRequired("Type", VD.Type);
    io.mapOptional("IsSettable", VD.IsSettable, false);
    io.mapOptional("IsStored", VD.IsStored, false);
    io.mapOptional("Attributes", VD.Attributes, ::swift::sma::DeclAttributes());
  }
};

template <> struct MappingTraits<::swift::sma::LetDecl> {
  static void mapping(IO &io, ::swift::sma::LetDecl &LD) {
    io.mapRequired("Name", LD.Name);
    io.mapRequired("Type", LD.Type);
    io.mapOptional("Attributes", LD.Attributes, ::swift::sma::DeclAttributes());
  }
};

template <> struct MappingTraits<::swift::sma::FuncParam> {
  static void mapping(IO &io, ::swift::sma::FuncParam &FP) {
    io.mapRequired("Name", FP.Name);
    io.mapRequired("Type", FP.Type);
    io.mapOptional("IsInout", FP.IsInout, false);
    io.mapOptional("Attributes", FP.Attributes, ::swift::sma::DeclAttributes());
  }
};

template <> struct MappingTraits<::swift::sma::FuncDecl> {
  static void mapping(IO &io, ::swift::sma::FuncDecl &FD) {
    io.mapRequired("Name", FD.Name);
    io.mapOptional("IsStatic", FD.IsStatic, false);
    io.mapOptional("GenericSignature", FD.TheGenericSignature);
    io.mapRequired("Params", FD.Params);
    io.mapRequired("ResultType", FD.ResultType);
    io.mapOptional("Attributes", FD.Attributes, ::swift::sma::DeclAttributes());
  }
};

template <> struct ScalarEnumerationTraits<::swift::sma::InitializerKind> {
  static void enumeration(IO &io, ::swift::sma::InitializerKind &Value) {
    io.enumCase(Value, "Designated", ::swift::sma::InitializerKind::Designated);
    io.enumCase(Value, "Convenience",
                ::swift::sma::InitializerKind::Convenience);
  }
};

template <>
struct ScalarEnumerationTraits<::swift::sma::InitializerFailability> {
  static void enumeration(IO &io, ::swift::sma::InitializerFailability &Value) {
    io.enumCase(Value, "None", ::swift::sma::InitializerFailability::None);
    io.enumCase(Value, "Optional",
                ::swift::sma::InitializerFailability::Optional);
    io.enumCase(
        Value, "ImplicitlyUnwrappedOptional",
        ::swift::sma::InitializerFailability::ImplicitlyUnwrappedOptional);
  }
};

template <> struct MappingTraits<::swift::sma::InitDecl> {
  static void mapping(IO &io, ::swift::sma::InitDecl &ID) {
    io.mapRequired("InitializerKind", ID.TheInitializerKind);
    io.mapRequired("InitializerFailability", ID.TheInitializerFailability);
    io.mapOptional("GenericSignature", ID.TheGenericSignature);
    io.mapRequired("Params", ID.Params);
    io.mapOptional("IsTrappingStub", ID.IsTrappingStub);
    io.mapOptional("Attributes", ID.Attributes, ::swift::sma::DeclAttributes());
  }
};

template <> struct MappingTraits<::swift::sma::DeinitDecl> {
  static void mapping(IO &io, ::swift::sma::DeinitDecl &DD) {
    io.mapRequired("Name", DD.Name);
    io.mapOptional("Attributes", DD.Attributes, ::swift::sma::DeclAttributes());
  }
};

void MappingTraits<::swift::sma::NestedDecls>::mapping(
    IO &io, ::swift::sma::NestedDecls &ND) {
  io.mapOptional("Structs", ND.Structs);
  io.mapOptional("Enums", ND.Enums);
  io.mapOptional("Classes", ND.Classes);
  io.mapOptional("Protocols", ND.Protocols);
  io.mapOptional("Typealiases", ND.Typealiases);
  io.mapOptional("AssociatedTypes", ND.AssociatedTypes);
  io.mapOptional("Vars", ND.Vars);
  io.mapOptional("Lets", ND.Lets);
  io.mapOptional("Functions", ND.Functions);
  io.mapOptional("Initializers", ND.Initializers);
  io.mapOptional("Deinitializers", ND.Deinitializers);
}

} // namespace yaml
} // namespace llvm

namespace {

class SMAModelGenerator : public DeclVisitor<SMAModelGenerator> {
  sma::NestedDecls Result;

public:
  void visit(const Decl *D) {
    DeclVisitor<SMAModelGenerator>::visit(const_cast<Decl *>(D));
  }

  sma::NestedDecls &&takeNestedDecls() { return std::move(Result); }

  sma::Identifier convertToIdentifier(Identifier I) const {
    return sma::Identifier{I.str().str()};
  }

  sma::TypeName convertToTypeName(Type T) const {
    PrintOptions Options;
    Options.PreferTypeRepr = true;

    sma::TypeName ResultTN;
    llvm::raw_string_ostream OS(ResultTN.Name);
    T.print(OS, Options);
    return ResultTN;
  }

  llvm::Optional<sma::TypeName> convertToOptionalTypeName(Type T) const {
    if (!T)
      return None;
    return convertToTypeName(T);
  }

  llvm::Optional<sma::GenericSignature>
  convertToGenericSignature(GenericSignature *GS) {
    if (!GS)
      return None;
    sma::GenericSignature ResultGS;
    for (auto *GTPT : GS->getGenericParams()) {
      sma::GenericParam ResultGP;
      ResultGP.Name = convertToIdentifier(GTPT->getName());
      ResultGS.GenericParams.emplace_back(std::move(ResultGP));
    }
    for (auto &Req : GS->getRequirements()) {
      switch (Req.getKind()) {
      case RequirementKind::Superclass:
      case RequirementKind::Conformance:
        ResultGS.ConformanceRequirements.emplace_back(
            sma::ConformanceRequirement{
                convertToTypeName(Req.getFirstType()),
                convertToTypeName(Req.getSecondType())});
        break;
      case RequirementKind::Layout:
        // FIXME
        assert(false && "Not implemented");
        break;
      case RequirementKind::SameType:
        ResultGS.SameTypeRequirements.emplace_back(
            sma::SameTypeRequirement{convertToTypeName(Req.getFirstType()),
                                     convertToTypeName(Req.getSecondType())});
        break;
      }
    }
    return ResultGS;
  }

  std::vector<sma::TypeName> collectProtocolConformances(NominalTypeDecl *NTD) {
    std::vector<sma::TypeName> Result;
    for (const auto *PD : NTD->getAllProtocols()) {
      Result.emplace_back(convertToTypeName(PD->getDeclaredType()));
    }
    return Result;
  }

  void visitDecl(Decl *D) {
    // FIXME: maybe don't have a default case
  }

  void visitStructDecl(StructDecl *SD) {
    auto ResultSD = std::make_shared<sma::StructDecl>();
    ResultSD->Name = convertToIdentifier(SD->getName());
    ResultSD->TheGenericSignature =
        convertToGenericSignature(SD->getGenericSignature());
    ResultSD->ConformsToProtocols = collectProtocolConformances(SD);
    // FIXME
    // ResultSD->Attributes = ?;
    // ResultSD->Decls = ?;
    Result.Structs.emplace_back(std::move(ResultSD));
  }

  void visitEnumDecl(EnumDecl *ED) {
    auto ResultED = std::make_shared<sma::EnumDecl>();
    ResultED->Name = convertToIdentifier(ED->getName());
    ResultED->TheGenericSignature =
        convertToGenericSignature(ED->getGenericSignature());
    ResultED->RawType = convertToOptionalTypeName(ED->getRawType());
    ResultED->ConformsToProtocols = collectProtocolConformances(ED);
    // FIXME
    // ResultED->Attributes = ?;
    // ResultED->Decls = ?;
    Result.Enums.emplace_back(std::move(ResultED));
  }

  void visitClassDecl(ClassDecl *CD) {
    auto ResultCD = std::make_shared<sma::ClassDecl>();
    ResultCD->Name = convertToIdentifier(CD->getName());
    ResultCD->TheGenericSignature =
        convertToGenericSignature(CD->getGenericSignature());
    ResultCD->Superclass = convertToOptionalTypeName(CD->getSuperclass());
    ResultCD->ConformsToProtocols = collectProtocolConformances(CD);
    // FIXME
    // ResultCD->Attributes = ?;
    // ResultCD->Decls = ?;
    Result.Classes.emplace_back(std::move(ResultCD));
  }

  void visitProtocolDecl(ProtocolDecl *PD) {
    auto ResultPD = std::make_shared<sma::ProtocolDecl>();
    ResultPD->Name = convertToIdentifier(PD->getName());
    ResultPD->ConformsToProtocols = collectProtocolConformances(PD);
    // FIXME
    // ResultPD->Attributes = ?;
    // ResultPD->Decls = ?;
    Result.Protocols.emplace_back(std::move(ResultPD));
  }

  void visitTypeAliasDecl(TypeAliasDecl *TAD) {
    auto ResultTD = std::make_shared<sma::TypealiasDecl>();
    ResultTD->Name = convertToIdentifier(TAD->getName());
    ResultTD->Type = convertToTypeName(TAD->getUnderlyingTypeLoc().getType());
    // FIXME
    // ResultTD->Attributes = ?;
    Result.Typealiases.emplace_back(std::move(ResultTD));
  }

  void visitAssociatedTypeDecl(AssociatedTypeDecl *ATD) {
    auto ResultATD = std::make_shared<sma::AssociatedTypeDecl>();
    ResultATD->Name = convertToIdentifier(ATD->getName());
    ResultATD->Superclass = convertToOptionalTypeName(ATD->getSuperclass());
    ResultATD->DefaultDefinition =
        convertToOptionalTypeName(ATD->getDefaultDefinitionType());
    // FIXME
    // ResultATD->Attributes = ?;
    Result.AssociatedTypes.emplace_back(std::move(ResultATD));
  }

  // FIXME
  // VarDecl
  // LetDecl

  void visitFuncDecl(FuncDecl *FD) {
    auto ResultFD = std::make_shared<sma::FuncDecl>();
    // FIXME
    Result.Functions.emplace_back(std::move(ResultFD));
  }

  void visitConstructorDecl(ConstructorDecl *CD) {
    auto ResultID = std::make_shared<sma::InitDecl>();
    // FIXME
    Result.Initializers.emplace_back(std::move(ResultID));
  }

  void visitDestructorDecl(DestructorDecl *DD) {
    auto ResultDD = std::make_shared<sma::DeinitDecl>();
    ResultDD->Name.Name = "deinit";
    // FIXME
    // ResultDD->Attributes = ?;
    Result.Deinitializers.emplace_back(std::move(ResultDD));
  }
};

std::shared_ptr<sma::Module> createSMAModel(ModuleDecl *M) {
  SmallVector<Decl *, 1> Decls;
  M->getDisplayDecls(Decls);

  SMAModelGenerator Generator;
  for (auto *D : Decls) {
    Generator.visit(D);
  }

  auto ResultM = std::make_shared<sma::Module>();
  ResultM->Name = Generator.convertToIdentifier(M->getName());

  // FIXME:
  // ResultM->ExportedModules = ?;

  ResultM->Decls = Generator.takeNestedDecls();

  return ResultM;
}

} // unnamed namespace

int swift::doGenerateModuleAPIDescription(StringRef MainExecutablePath,
                                          ArrayRef<std::string> Args) {
  std::vector<const char *> CStringArgs;
  for (auto &S : Args) {
    CStringArgs.push_back(S.c_str());
  }

  PrintingDiagnosticConsumer PDC;
  SourceManager SM;
  DiagnosticEngine Diags(SM);
  Diags.addConsumer(PDC);

  std::unique_ptr<CompilerInvocation> Invocation =
      driver::createCompilerInvocation(CStringArgs, Diags);

  if (!Invocation) {
    llvm::errs() << "error: unable to create a CompilerInvocation\n";
    return 1;
  }

  Invocation->setMainExecutablePath(MainExecutablePath);

  CompilerInstance CI;
  CI.addDiagnosticConsumer(&PDC);
  if (CI.setup(*Invocation))
    return 1;
  CI.performSema();

  PrintOptions Options = PrintOptions::printEverything();

  ModuleDecl *M = CI.getMainModule();
  M->getMainSourceFile(Invocation->getSourceFileKind()).print(llvm::outs(),
                                                        Options);

  auto SMAModel = createSMAModel(M);
  llvm::yaml::Output YOut(llvm::outs());
  YOut << *SMAModel.get();

  return 0;
}

