//===--- ArtificialIdentifierPolicy.cpp -----------------------------------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// Hacks and fun related to the code rewriter.
//
//===----------------------------------------------------------------------===//

#include "clang/ASTProcessing/AttachedPragmas.h"
#include "clang/ASTProcessing/StmtCollector.h"
#include "clang/Lex/Preprocessor.h"
#include "clang/Lex/LiteralSupport.h"
#include "clang/Lex/Pragma.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Basic/Diagnostic.h"
#include <map>

//--------------------------------------------------------- 
namespace clang {

//--------------------------------------------------------- 
namespace ASTProcessing {


//--------------------------------------------------------- 
bool PragmaArgumentInfo::hasArgument(const char* pName) const
{
  const std::string* pStr;
  for (tArguments::const_iterator i = m_Arguments.begin(), e = m_Arguments.end();
       i != e; ++i)
  {
    if ((pStr = boost::get<std::string>(&*i)) != 0 && *pStr == pName)
    {
      return true;
    }
  }
  return false;
}

//--------------------------------------------------------- 
class AttachedPragmaCollector::Handler : public PragmaHandler
{
  struct tPragma
  {
    IdentifierInfo*     m_ActionName;
    PragmaArgumentInfo  m_Arguments;
  };

  typedef std::map<unsigned, tPragma> tCollectedPragmas;
  tCollectedPragmas m_CollectedPragmas;   // key is line number

  typedef std::multimap<unsigned, const tPragma*> tDeclPragmas;
  mutable tDeclPragmas m_DeclPragmas;
  SourceManager& m_SM;

public:
  Handler(SourceManager& SM, const IdentifierInfo* domainName);
  virtual void HandlePragma(Preprocessor &PP, Token &FirstToken);

  void sortCollectionToStmts(StmtCollector& collector) const;

  DiagnosticBuilder Warn(Preprocessor &PP, const SourceLocation& loc, const char* msg);
};

//--------------------------------------------------------- 
AttachedPragmaCollector::Handler::Handler(SourceManager& SM, 
                                          const IdentifierInfo* domainName) :
  PragmaHandler(domainName),
  m_SM(SM)
{}

//--------------------------------------------------------- 
void AttachedPragmaCollector::Handler::HandlePragma(Preprocessor &PP, 
                                                    Token &FirstToken)
{
  assert(FirstToken.is(tok::identifier) && 
         getName() == FirstToken.getIdentifierInfo());
  Token nextTokenAtLine;
  IdentifierInfo* actionName = 0;
  PragmaArgumentInfo* pArguments = 0;
  while (PP.Lex(nextTokenAtLine), !nextTokenAtLine.is(tok::eom))
  {
    if (actionName == 0 && nextTokenAtLine.is(tok::identifier))
    {
      unsigned pragmaLine = m_SM.getPresumedLoc(nextTokenAtLine.getLocation()).getLine();
      actionName = nextTokenAtLine.getIdentifierInfo();
      tPragma& scoutPragma = m_CollectedPragmas[pragmaLine];
      pArguments = &scoutPragma.m_Arguments;
      scoutPragma.m_ActionName = actionName;
    }
    else if (pArguments != 0)
    {
      if (nextTokenAtLine.is(tok::numeric_constant))
      {
        llvm::SmallString<512> IntegerBuffer;
        // Add padding so that NumericLiteralParser can overread by one character.
        IntegerBuffer.resize(nextTokenAtLine.getLength()+1);
        const char *ThisTokBegin = &IntegerBuffer[0];
        // Get the spelling of the token, which eliminates trigraphs, etc.
        unsigned ActualLength = PP.getSpelling(nextTokenAtLine, ThisTokBegin);
        NumericLiteralParser Literal(ThisTokBegin, ThisTokBegin+ActualLength,
                                     nextTokenAtLine.getLocation(), PP);
        llvm::APInt Val;
        if (Literal.hadError || (!Literal.GetIntegerValue(Val)))
        {
          Warn(PP, nextTokenAtLine.getLocation(), "erroneous integral number in pragma action %0") << actionName;
          break;
        }
        pArguments->m_Arguments.push_back(Val.getSExtValue());
      }
      else if (nextTokenAtLine.is(tok::identifier))
      {
        pArguments->m_Arguments.push_back(nextTokenAtLine.getIdentifierInfo()->getName());
      }
      else
      {
        Warn(PP, nextTokenAtLine.getLocation(), "unrecognized argument type in pragma action %0") << actionName;
      }
    }
  }
  if (actionName == 0)
  {
    Warn(PP, nextTokenAtLine.getLocation(), "missing action in pragma domain %0") << FirstToken.getIdentifierInfo();
  }
}

//--------------------------------------------------------- 
DiagnosticBuilder AttachedPragmaCollector::Handler::Warn(Preprocessor &PP, const SourceLocation& loc, const char* msg)
{
  unsigned warn_pragma = PP.getDiagnostics().getCustomDiagID(Diagnostic::Warning, msg);
  return PP.getDiagnostics().Report(FullSourceLoc(loc, m_SM), warn_pragma);
}

//--------------------------------------------------------- 
void AttachedPragmaCollector::Handler::sortCollectionToStmts(StmtCollector& collector) const
{
  for (tDeclPragmas::const_iterator i = m_DeclPragmas.begin(), 
       e = m_DeclPragmas.end(); i != e; ++i)
  {
    collector.attachPragma(i->first, getName(), 
      i->second->m_ActionName, &i->second->m_Arguments);
  }

  unsigned fnDeclLine = m_SM.getPresumedLoc(collector.getFnDecl().getLocation()).getLine();
  for (unsigned pragmaLine = fnDeclLine; pragmaLine > 0;)
  {
    --pragmaLine;
    tCollectedPragmas::const_iterator iPragma = m_CollectedPragmas.find(pragmaLine);
    // support only pragmas as immediate successors of a function declaration:
    if (iPragma == m_CollectedPragmas.end())
    {
      break;
    }
    collector.attachPragma(fnDeclLine, getName(), 
      iPragma->second.m_ActionName, &iPragma->second.m_Arguments);
    m_DeclPragmas.insert(tDeclPragmas::value_type(fnDeclLine, &iPragma->second));
  }

  llvm::df_iterator<Stmt*> i = llvm::df_begin(collector.getRoot()), 
                           e = llvm::df_end(collector.getRoot());
  assert(i != e && "there is no root!");
  unsigned currentLine = m_SM.getPresumedLoc(i->getSourceRange().getBegin()).getLine();
  tCollectedPragmas::const_iterator 
    // we don't support pragmas between a function declaration and the body:
    iPragma = m_CollectedPragmas.upper_bound(currentLine), 
    ePragma = m_CollectedPragmas.end();
  for (++i; i != e && iPragma != ePragma; ++i)
  {
    unsigned stmtLine = m_SM.getPresumedLoc(i->getSourceRange().getBegin()).getLine();
    if (stmtLine != currentLine)
    {
      for (; iPragma != ePragma && iPragma->first < stmtLine; ++iPragma)
      {
        collector.attachPragma(stmtLine, getName(), 
          iPragma->second.m_ActionName, &iPragma->second.m_Arguments);
      }
      currentLine = stmtLine;
    }
  }
}

//--------------------------------------------------------- 
AttachedPragmaCollector::~AttachedPragmaCollector()
{
  for (tHandlers::const_iterator i = PragmaHandlers.begin(), 
       e = PragmaHandlers.end(); i != e; ++i)
  {
    PP.RemovePragmaHandler(m_Namespace.c_str(), i->get());
  }
}

//--------------------------------------------------------- 
void AttachedPragmaCollector::addDomain(const char* pDomainName)
{
  const IdentifierInfo* domainName = PP.getIdentifierInfo(pDomainName);
  Handler* pHandler = new Handler(PP.getSourceManager(), domainName);
  PragmaHandlers.push_back(std::tr1::shared_ptr<Handler>(pHandler));
  PP.AddPragmaHandler(m_Namespace.c_str(), pHandler);
}

//--------------------------------------------------------- 
void AttachedPragmaCollector::collectParsedPragmas(
  StmtCollector& collector) const
{
  for (tHandlers::const_iterator i = PragmaHandlers.begin(),
       e = PragmaHandlers.end(); i != e; ++i)
  {
    (*i)->sortCollectionToStmts(collector);
  }
}

//--------------------------------------------------------- 
} // namespace ASTProcessing

} // ns clang
