//===-- Transforms.h - Transformations to ARC mode --------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_CLANG_LIB_ARCMIGRATE_TRANSFORMS_H
#define LLVM_CLANG_LIB_ARCMIGRATE_TRANSFORMS_H

#include "clang/AST/ParentMap.h"
#include "clang/AST/RecursiveASTVisitor.h"
#include "llvm/ADT/DenseSet.h"
#include "llvm/Support/SaveAndRestore.h"

namespace clang {
  class Decl;
  class Stmt;
  class BlockDecl;
  class ObjCMethodDecl;
  class FunctionDecl;

namespace arcmt {
  class MigrationPass;

namespace trans {

  class MigrationContext;

//===----------------------------------------------------------------------===//
// Transformations.
//===----------------------------------------------------------------------===//

void rewriteAutoreleasePool(MigrationPass &pass);
void rewriteUnbridgedCasts(MigrationPass &pass);
void makeAssignARCSafe(MigrationPass &pass);
void removeRetainReleaseDeallocFinalize(MigrationPass &pass);
void removeZeroOutPropsInDeallocFinalize(MigrationPass &pass);
void rewriteUnusedInitDelegate(MigrationPass &pass);
void checkAPIUses(MigrationPass &pass);

void removeEmptyStatementsAndDeallocFinalize(MigrationPass &pass);

class BodyContext {
  MigrationContext &MigrateCtx;
  ParentMap PMap;
  Stmt *TopStmt;

public:
  BodyContext(MigrationContext &MigrateCtx, Stmt *S)
    : MigrateCtx(MigrateCtx), PMap(S), TopStmt(S) {}

  MigrationContext &getMigrationContext() { return MigrateCtx; }
  ParentMap &getParentMap() { return PMap; }
  Stmt *getTopStmt() { return TopStmt; }
};

class ObjCImplementationContext {
  MigrationContext &MigrateCtx;
  ObjCImplementationDecl *ImpD;

public:
  ObjCImplementationContext(MigrationContext &MigrateCtx,
                            ObjCImplementationDecl *D)
    : MigrateCtx(MigrateCtx), ImpD(D) {}

  MigrationContext &getMigrationContext() { return MigrateCtx; }
  ObjCImplementationDecl *getImplementationDecl() { return ImpD; }
};

class ASTTraverser {
public:
  virtual ~ASTTraverser();
  virtual void traverseTU(MigrationContext &MigrateCtx) { }
  virtual void traverseBody(BodyContext &BodyCtx) { }
  virtual void traverseObjCImplementation(ObjCImplementationContext &ImplCtx) {}
};

class MigrationContext {
  std::vector<ASTTraverser *> Traversers;

public:
  MigrationPass &Pass;

  struct GCAttrOccurrence {
    enum AttrKind { Weak, Strong } Kind;
    SourceLocation Loc;
    QualType ModifiedType;
    Decl *Dcl;
    /// true if the attribute is owned, e.g. it is in a body and not just
    /// in an interface.
    bool FullyMigratable;
  };
  std::vector<GCAttrOccurrence> GCAttrs;
  llvm::DenseSet<unsigned> AttrSet;
  llvm::DenseSet<unsigned> RemovedAttrSet;

  /// Set of raw '@' locations for 'assign' properties group that contain
  /// GC __weak.
  llvm::DenseSet<unsigned> AtPropsWeak;

  explicit MigrationContext(MigrationPass &pass) : Pass(pass) {}
  ~MigrationContext();

  typedef std::vector<ASTTraverser *>::iterator traverser_iterator;
  traverser_iterator traversers_begin() { return Traversers.begin(); }
  traverser_iterator traversers_end() { return Traversers.end(); }

  void addTraverser(ASTTraverser *traverser) {
    Traversers.push_back(traverser);
  }

  bool isGCOwnedNonObjC(QualType T);
  bool removePropertyAttribute(StringRef fromAttr, SourceLocation atLoc) {
    return rewritePropertyAttribute(fromAttr, StringRef(), atLoc);
  }
  bool rewritePropertyAttribute(StringRef fromAttr, StringRef toAttr,
                                SourceLocation atLoc);
  bool addPropertyAttribute(StringRef attr, SourceLocation atLoc);

  void traverse(TranslationUnitDecl *TU);

  void dumpGCAttrs();
};

class PropertyRewriteTraverser : public ASTTraverser {
public:
  void traverseObjCImplementation(ObjCImplementationContext &ImplCtx) override;
};

class BlockObjCVariableTraverser : public ASTTraverser {
public:
  void traverseBody(BodyContext &BodyCtx) override;
};

class ProtectedScopeTraverser : public ASTTraverser {
public:
  void traverseBody(BodyContext &BodyCtx) override;
};

// GC transformations

class GCAttrsTraverser : public ASTTraverser {
public:
  void traverseTU(MigrationContext &MigrateCtx) override;
};

class GCCollectableCallsTraverser : public ASTTraverser {
public:
  void traverseBody(BodyContext &BodyCtx) override;
};

//===----------------------------------------------------------------------===//
// Helpers.
//===----------------------------------------------------------------------===//

/// Determine whether we can add weak to the given type.
bool canApplyWeak(ASTContext &Ctx, QualType type,
                  bool AllowOnUnknownClass = false);

bool isPlusOneAssign(const BinaryOperator *E);
bool isPlusOne(const Expr *E);

/// 'Loc' is the end of a statement range. This returns the location
/// immediately after the semicolon following the statement.
/// If no semicolon is found or the location is inside a macro, the returned
/// source location will be invalid.
SourceLocation findLocationAfterSemi(SourceLocation loc, ASTContext &Ctx,
                                     bool IsDecl = false);

/// 'Loc' is the end of a statement range. This returns the location
/// of the semicolon following the statement.
/// If no semicolon is found or the location is inside a macro, the returned
/// source location will be invalid.
SourceLocation findSemiAfterLocation(SourceLocation loc, ASTContext &Ctx,
                                     bool IsDecl = false);

bool hasSideEffects(Expr *E, ASTContext &Ctx);
bool isGlobalVar(Expr *E);
/// Returns "nil" or "0" if 'nil' macro is not actually defined.
StringRef getNilString(MigrationPass &Pass);

template <typename BODY_TRANS>
class BodyTransform : public RecursiveASTVisitor<BodyTransform<BODY_TRANS> > {
  MigrationPass &Pass;
  Decl *ParentD;

  typedef RecursiveASTVisitor<BodyTransform<BODY_TRANS> > base;
public:
  BodyTransform(MigrationPass &pass) : Pass(pass), ParentD(nullptr) { }

  bool TraverseStmt(Stmt *rootS) {
    if (rootS)
      BODY_TRANS(Pass).transformBody(rootS, ParentD);
    return true;
  }

  bool TraverseObjCMethodDecl(ObjCMethodDecl *D) {
    SaveAndRestore<Decl *> SetParent(ParentD, D);
    return base::TraverseObjCMethodDecl(D);
  }
};

typedef llvm::DenseSet<Expr *> ExprSet;

void clearRefsIn(Stmt *S, ExprSet &refs);
template <typename iterator>
void clearRefsIn(iterator begin, iterator end, ExprSet &refs) {
  for (; begin != end; ++begin)
    clearRefsIn(*begin, refs);
}

void collectRefs(ValueDecl *D, Stmt *S, ExprSet &refs);

void collectRemovables(Stmt *S, ExprSet &exprs);

} // end namespace trans

} // end namespace arcmt

} // end namespace clang

#endif
