//===--- SILLocation.cpp - Location information for SIL nodes -------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2015 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See http://swift.org/LICENSE.txt for license information
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//

#include "swift/SIL/SILLocation.h"
#include "swift/AST/Decl.h"
#include "swift/AST/Expr.h"
#include "swift/AST/Pattern.h"
#include "swift/AST/Stmt.h"
#include "llvm/Support/raw_ostream.h"

using namespace swift;


SourceLoc SILLocation::getSourceLoc() const {
  if (hasSILFileSourceLoc())
    return getSILFileSourceLoc();

  return getSourceLoc(ASTNode);
}

SourceLoc SILLocation::getSourceLoc(ASTNodeTy N) const {
  if (N.isNull())
    return SourceLoc();

  if (alwaysPointsToStart() ||
      alwaysPointsToEnd() ||
      getKind() == CleanupKind ||
      getKind() == ImplicitReturnKind)
    return getEndSourceLoc(N);

  // Use the start location for the ReturnKind.
  if (getKind() == ReturnKind)
    return getStartSourceLoc(N);

  if (auto *decl = N.dyn_cast<Decl*>())
    return decl->getLoc();
  if (auto *expr = N.dyn_cast<Expr*>())
    return expr->getLoc();
  if (auto *stmt = N.dyn_cast<Stmt*>())
    return stmt->getStartLoc();
  if (auto *patt = N.dyn_cast<Pattern*>())
    return patt->getStartLoc();
  llvm_unreachable("impossible SILLocation");
}

SourceLoc SILLocation::getDebugSourceLoc() const {
  if (hasSILFileSourceLoc())
    return getSILFileSourceLoc();

  if (isAutoGenerated())
    return SourceLoc();

  if (auto *expr = ASTNode.dyn_cast<Expr*>()) {
    if (isa<CallExpr>(expr))
      return expr->getEndLoc();
    // Code that has an autoclosure as location should not show up in
    // the line table (rdar://problem/14627460). Note also that the
    // closure function still has a valid DW_AT_decl_line.  Depending
    // on how we decide to resolve rdar://problem/14627460, we may
    // want to use the regular getLoc instead and rather use the
    // column info.
    if (isa<AutoClosureExpr>(expr))
      return SourceLoc();
  }

  if (!hasDebugLoc())
    return getSourceLoc(ASTNode);

  assert(!SpecificLoc.DebugLoc.isNull() && "empty debug location");
  return getSourceLoc(SpecificLoc.DebugLoc);
}

SourceLoc SILLocation::getStartSourceLoc() const {
  if (isAutoGenerated())
    return SourceLoc();
  if (ASTNode.isNull())
    return getSILFileSourceLoc();
  return getStartSourceLoc(ASTNode);
}

SourceLoc SILLocation::getStartSourceLoc(ASTNodeTy N) const {
  if (auto *decl = N.dyn_cast<Decl*>())
    return decl->getStartLoc();
  if (auto *expr = N.dyn_cast<Expr*>())
    return expr->getStartLoc();
  if (auto *stmt = N.dyn_cast<Stmt*>())
    return stmt->getStartLoc();
  if (auto *patt = N.dyn_cast<Pattern*>())
    return patt->getStartLoc();
  llvm_unreachable("impossible SILLocation");
}

SourceLoc SILLocation::getEndSourceLoc() const {
  if (isAutoGenerated())
    return SourceLoc();
  if (ASTNode.isNull())
    return getSILFileSourceLoc();
  return getEndSourceLoc(ASTNode);
}

SourceLoc SILLocation::getEndSourceLoc(ASTNodeTy N) const {
  if (auto decl = N.dyn_cast<Decl*>())
    return decl->getEndLoc();
  if (auto expr = N.dyn_cast<Expr*>())
    return expr->getEndLoc();
  if (auto stmt = N.dyn_cast<Stmt*>())
    return stmt->getEndLoc();
  if (auto patt = N.dyn_cast<Pattern*>())
    return patt->getEndLoc();
  llvm_unreachable("impossible SILLocation");
}

void SILLocation::dump(const SourceManager &SM) const {
  if (auto D = ASTNode.dyn_cast<Decl *>())
    llvm::errs() << Decl::getKindName(D->getKind()) << "Decl @ ";
  if (auto E = ASTNode.dyn_cast<Expr *>())
    llvm::errs() << Expr::getKindName(E->getKind()) << "Expr @ ";
  if (auto S = ASTNode.dyn_cast<Stmt *>())
    llvm::errs() << Stmt::getKindName(S->getKind()) << "Stmt @ ";
  if (auto P = ASTNode.dyn_cast<Pattern *>())
    llvm::errs() << Pattern::getKindName(P->getKind()) << "Pattern @ ";

  print(llvm::errs(), SM);

  if (isAutoGenerated())     llvm::errs() << ":auto";
  if (alwaysPointsToStart()) llvm::errs() << ":start";
  if (alwaysPointsToEnd())   llvm::errs() << ":end";
  if (isInTopLevel())        llvm::errs() << ":toplevel";
  if (isInPrologue())        llvm::errs() << ":prologue";
  if (hasSILFileSourceLoc()) llvm::errs() << ":sil";
  if (hasDebugLoc()) {
    llvm::errs() << ":debug[";
    getDebugSourceLoc().print(llvm::errs(), SM);
    llvm::errs() << "]\n";
  }
}

void SILLocation::print(raw_ostream &OS, const SourceManager &SM) const {
  if (isNull())
    OS << "<no loc>";
  getSourceLoc().print(OS, SM);
}

InlinedLocation InlinedLocation::getInlinedLocation(SILLocation L) {
  if (Expr *E = L.getAsASTNode<Expr>())
    return InlinedLocation(E, L.getSpecialFlags());
  if (Stmt *S = L.getAsASTNode<Stmt>())
    return InlinedLocation(S, L.getSpecialFlags());
  if (Pattern *P = L.getAsASTNode<Pattern>())
    return InlinedLocation(P, L.getSpecialFlags());
  if (Decl *D = L.getAsASTNode<Decl>())
    return InlinedLocation(D, L.getSpecialFlags());

  if (L.hasSILFileSourceLoc())
    return InlinedLocation(L.getSILFileSourceLoc(), L.getSpecialFlags());

  if (L.isInTopLevel())
    return InlinedLocation::getModuleLocation(L.getSpecialFlags());

  if (L.isAutoGenerated()) {
    InlinedLocation IL;
    IL.markAutoGenerated();
    return IL;
  }
  llvm_unreachable("Cannot construct Inlined loc from the given location.");
}

MandatoryInlinedLocation
MandatoryInlinedLocation::getMandatoryInlinedLocation(SILLocation L) {
  if (Expr *E = L.getAsASTNode<Expr>())
    return MandatoryInlinedLocation(E, L.getSpecialFlags());
  if (Stmt *S = L.getAsASTNode<Stmt>())
    return MandatoryInlinedLocation(S, L.getSpecialFlags());
  if (Pattern *P = L.getAsASTNode<Pattern>())
    return MandatoryInlinedLocation(P, L.getSpecialFlags());
  if (Decl *D = L.getAsASTNode<Decl>())
    return MandatoryInlinedLocation(D, L.getSpecialFlags());

  if (L.hasSILFileSourceLoc())
    return MandatoryInlinedLocation(L.getSILFileSourceLoc(),
                                    L.getSpecialFlags());

  if (L.isInTopLevel())
    return MandatoryInlinedLocation::getModuleLocation(L.getSpecialFlags());

  llvm_unreachable("Cannot construct Inlined loc from the given location.");
}

CleanupLocation CleanupLocation::get(SILLocation L) {
  if (Expr *E = L.getAsASTNode<Expr>())
    return CleanupLocation(E, L.getSpecialFlags());
  if (Stmt *S = L.getAsASTNode<Stmt>())
    return CleanupLocation(S, L.getSpecialFlags());
  if (Pattern *P = L.getAsASTNode<Pattern>())
    return CleanupLocation(P, L.getSpecialFlags());
  if (Decl *D = L.getAsASTNode<Decl>())
    return CleanupLocation(D, L.getSpecialFlags());
  if (L.isNull())
    return CleanupLocation();
  if (L.getAs<SILFileLocation>())
    return CleanupLocation();
  llvm_unreachable("Cannot construct Cleanup loc from the "
                   "given location.");
}
