head	1.1;
branch	1.1.1;
access;
symbols
	netbsd-11-0-RC4:1.1.1.3
	netbsd-11-0-RC3:1.1.1.3
	netbsd-11-0-RC2:1.1.1.3
	netbsd-11-0-RC1:1.1.1.3
	perseant-exfatfs-base-20250801:1.1.1.3
	netbsd-11:1.1.1.3.0.10
	netbsd-11-base:1.1.1.3
	netbsd-10-1-RELEASE:1.1.1.3
	perseant-exfatfs-base-20240630:1.1.1.3
	perseant-exfatfs:1.1.1.3.0.8
	perseant-exfatfs-base:1.1.1.3
	netbsd-8-3-RELEASE:1.1.1.2
	netbsd-9-4-RELEASE:1.1.1.2
	netbsd-10-0-RELEASE:1.1.1.3
	netbsd-10-0-RC6:1.1.1.3
	netbsd-10-0-RC5:1.1.1.3
	netbsd-10-0-RC4:1.1.1.3
	netbsd-10-0-RC3:1.1.1.3
	netbsd-10-0-RC2:1.1.1.3
	netbsd-10-0-RC1:1.1.1.3
	netbsd-10:1.1.1.3.0.6
	netbsd-10-base:1.1.1.3
	netbsd-9-3-RELEASE:1.1.1.2
	cjep_sun2x:1.1.1.3.0.4
	cjep_sun2x-base:1.1.1.3
	cjep_staticlib_x-base1:1.1.1.3
	netbsd-9-2-RELEASE:1.1.1.2
	cjep_staticlib_x:1.1.1.3.0.2
	cjep_staticlib_x-base:1.1.1.3
	netbsd-9-1-RELEASE:1.1.1.2
	phil-wifi-20200421:1.1.1.3
	phil-wifi-20200411:1.1.1.3
	phil-wifi-20200406:1.1.1.3
	netbsd-8-2-RELEASE:1.1.1.2
	netbsd-9-0-RELEASE:1.1.1.2
	netbsd-9-0-RC2:1.1.1.2
	netbsd-9-0-RC1:1.1.1.2
	netbsd-9:1.1.1.2.0.28
	netbsd-9-base:1.1.1.2
	phil-wifi-20190609:1.1.1.2
	netbsd-8-1-RELEASE:1.1.1.2
	netbsd-8-1-RC1:1.1.1.2
	pgoyette-compat-merge-20190127:1.1.1.2
	pgoyette-compat-20190127:1.1.1.2
	pgoyette-compat-20190118:1.1.1.2
	pgoyette-compat-1226:1.1.1.2
	pgoyette-compat-1126:1.1.1.2
	pgoyette-compat-1020:1.1.1.2
	pgoyette-compat-0930:1.1.1.2
	pgoyette-compat-0906:1.1.1.2
	netbsd-7-2-RELEASE:1.1.1.2
	pgoyette-compat-0728:1.1.1.2
	clang-337282:1.1.1.2
	netbsd-8-0-RELEASE:1.1.1.2
	phil-wifi:1.1.1.2.0.26
	phil-wifi-base:1.1.1.2
	pgoyette-compat-0625:1.1.1.2
	netbsd-8-0-RC2:1.1.1.2
	pgoyette-compat-0521:1.1.1.2
	pgoyette-compat-0502:1.1.1.2
	pgoyette-compat-0422:1.1.1.2
	netbsd-8-0-RC1:1.1.1.2
	pgoyette-compat-0415:1.1.1.2
	pgoyette-compat-0407:1.1.1.2
	pgoyette-compat-0330:1.1.1.2
	pgoyette-compat-0322:1.1.1.2
	pgoyette-compat-0315:1.1.1.2
	netbsd-7-1-2-RELEASE:1.1.1.2
	pgoyette-compat:1.1.1.2.0.24
	pgoyette-compat-base:1.1.1.2
	netbsd-7-1-1-RELEASE:1.1.1.2
	clang-319952:1.1.1.2
	matt-nb8-mediatek:1.1.1.2.0.22
	matt-nb8-mediatek-base:1.1.1.2
	clang-309604:1.1.1.2
	perseant-stdc-iso10646:1.1.1.2.0.20
	perseant-stdc-iso10646-base:1.1.1.2
	netbsd-8:1.1.1.2.0.18
	netbsd-8-base:1.1.1.2
	prg-localcount2-base3:1.1.1.2
	prg-localcount2-base2:1.1.1.2
	prg-localcount2-base1:1.1.1.2
	prg-localcount2:1.1.1.2.0.16
	prg-localcount2-base:1.1.1.2
	pgoyette-localcount-20170426:1.1.1.2
	bouyer-socketcan-base1:1.1.1.2
	pgoyette-localcount-20170320:1.1.1.2
	netbsd-7-1:1.1.1.2.0.14
	netbsd-7-1-RELEASE:1.1.1.2
	netbsd-7-1-RC2:1.1.1.2
	clang-294123:1.1.1.2
	netbsd-7-nhusb-base-20170116:1.1.1.2
	bouyer-socketcan:1.1.1.2.0.12
	bouyer-socketcan-base:1.1.1.2
	clang-291444:1.1.1.2
	pgoyette-localcount-20170107:1.1.1.2
	netbsd-7-1-RC1:1.1.1.2
	pgoyette-localcount-20161104:1.1.1.2
	netbsd-7-0-2-RELEASE:1.1.1.2
	localcount-20160914:1.1.1.2
	netbsd-7-nhusb:1.1.1.2.0.10
	netbsd-7-nhusb-base:1.1.1.2
	clang-280599:1.1.1.2
	pgoyette-localcount-20160806:1.1.1.2
	pgoyette-localcount-20160726:1.1.1.2
	pgoyette-localcount:1.1.1.2.0.8
	pgoyette-localcount-base:1.1.1.2
	netbsd-7-0-1-RELEASE:1.1.1.2
	clang-261930:1.1.1.2
	netbsd-7-0:1.1.1.2.0.6
	netbsd-7-0-RELEASE:1.1.1.2
	netbsd-7-0-RC3:1.1.1.2
	netbsd-7-0-RC2:1.1.1.2
	netbsd-7-0-RC1:1.1.1.2
	clang-237755:1.1.1.2
	clang-232565:1.1.1.2
	clang-227398:1.1.1.2
	tls-maxphys-base:1.1.1.2
	tls-maxphys:1.1.1.2.0.4
	netbsd-7:1.1.1.2.0.2
	netbsd-7-base:1.1.1.2
	clang-215315:1.1.1.2
	clang-209886:1.1.1.2
	yamt-pagecache:1.1.1.1.0.4
	yamt-pagecache-base9:1.1.1.1
	tls-earlyentropy:1.1.1.1.0.2
	tls-earlyentropy-base:1.1.1.2
	riastradh-xf86-video-intel-2-7-1-pre-2-21-15:1.1.1.1
	riastradh-drm2-base3:1.1.1.1
	clang-202566:1.1.1.1
	clang-201163:1.1.1.1
	clang-199312:1.1.1.1
	clang-198450:1.1.1.1
	clang-196603:1.1.1.1
	clang-195771:1.1.1.1
	LLVM:1.1.1;
locks; strict;
comment	@// @;


1.1
date	2013.11.28.14.14.49;	author joerg;	state Exp;
branches
	1.1.1.1;
next	;
commitid	ow8OybrawrB1f3fx;

1.1.1.1
date	2013.11.28.14.14.49;	author joerg;	state Exp;
branches
	1.1.1.1.2.1
	1.1.1.1.4.1;
next	1.1.1.2;
commitid	ow8OybrawrB1f3fx;

1.1.1.2
date	2014.05.30.18.14.39;	author joerg;	state Exp;
branches
	1.1.1.2.4.1
	1.1.1.2.26.1;
next	1.1.1.3;
commitid	8q0kdlBlCn09GACx;

1.1.1.3
date	2019.11.13.22.19.19;	author joerg;	state dead;
branches;
next	;
commitid	QD8YATxuNG34YJKB;

1.1.1.1.2.1
date	2014.08.10.07.08.06;	author tls;	state Exp;
branches;
next	;
commitid	t01A1TLTYxkpGMLx;

1.1.1.1.4.1
date	2013.11.28.14.14.49;	author yamt;	state dead;
branches;
next	1.1.1.1.4.2;
commitid	WSrDtL5nYAUyiyBx;

1.1.1.1.4.2
date	2014.05.22.16.18.25;	author yamt;	state Exp;
branches;
next	;
commitid	WSrDtL5nYAUyiyBx;

1.1.1.2.4.1
date	2014.05.30.18.14.39;	author tls;	state dead;
branches;
next	1.1.1.2.4.2;
commitid	jTnpym9Qu0o4R1Nx;

1.1.1.2.4.2
date	2014.08.19.23.47.25;	author tls;	state Exp;
branches;
next	;
commitid	jTnpym9Qu0o4R1Nx;

1.1.1.2.26.1
date	2020.04.13.07.46.29;	author martin;	state dead;
branches;
next	;
commitid	X01YhRUPVUDaec4C;


desc
@@


1.1
log
@Initial revision
@
text
@//===--- TransProtectedScope.cpp - Transformations to ARC mode ------------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// Adds brackets in case statements that "contain" initialization of retaining
// variable, thus emitting the "switch case is in protected scope" error.
//
//===----------------------------------------------------------------------===//

#include "Transforms.h"
#include "Internals.h"
#include "clang/AST/ASTContext.h"
#include "clang/Sema/SemaDiagnostic.h"

using namespace clang;
using namespace arcmt;
using namespace trans;

namespace {

class LocalRefsCollector : public RecursiveASTVisitor<LocalRefsCollector> {
  SmallVectorImpl<DeclRefExpr *> &Refs;

public:
  LocalRefsCollector(SmallVectorImpl<DeclRefExpr *> &refs)
    : Refs(refs) { }

  bool VisitDeclRefExpr(DeclRefExpr *E) {
    if (ValueDecl *D = E->getDecl())
      if (D->getDeclContext()->getRedeclContext()->isFunctionOrMethod())
        Refs.push_back(E);
    return true;
  }
};

struct CaseInfo {
  SwitchCase *SC;
  SourceRange Range;
  enum {
    St_Unchecked,
    St_CannotFix,
    St_Fixed
  } State;
  
  CaseInfo() : SC(0), State(St_Unchecked) {}
  CaseInfo(SwitchCase *S, SourceRange Range)
    : SC(S), Range(Range), State(St_Unchecked) {}
};

class CaseCollector : public RecursiveASTVisitor<CaseCollector> {
  ParentMap &PMap;
  SmallVectorImpl<CaseInfo> &Cases;

public:
  CaseCollector(ParentMap &PMap, SmallVectorImpl<CaseInfo> &Cases)
    : PMap(PMap), Cases(Cases) { }

  bool VisitSwitchStmt(SwitchStmt *S) {
    SwitchCase *Curr = S->getSwitchCaseList();
    if (!Curr)
      return true;
    Stmt *Parent = getCaseParent(Curr);
    Curr = Curr->getNextSwitchCase();
    // Make sure all case statements are in the same scope.
    while (Curr) {
      if (getCaseParent(Curr) != Parent)
        return true;
      Curr = Curr->getNextSwitchCase();
    }

    SourceLocation NextLoc = S->getLocEnd();
    Curr = S->getSwitchCaseList();
    // We iterate over case statements in reverse source-order.
    while (Curr) {
      Cases.push_back(CaseInfo(Curr,SourceRange(Curr->getLocStart(), NextLoc)));
      NextLoc = Curr->getLocStart();
      Curr = Curr->getNextSwitchCase();
    }
    return true;
  }

  Stmt *getCaseParent(SwitchCase *S) {
    Stmt *Parent = PMap.getParent(S);
    while (Parent && (isa<SwitchCase>(Parent) || isa<LabelStmt>(Parent)))
      Parent = PMap.getParent(Parent);
    return Parent;
  }
};

class ProtectedScopeFixer {
  MigrationPass &Pass;
  SourceManager &SM;
  SmallVector<CaseInfo, 16> Cases;
  SmallVector<DeclRefExpr *, 16> LocalRefs;

public:
  ProtectedScopeFixer(BodyContext &BodyCtx)
    : Pass(BodyCtx.getMigrationContext().Pass),
      SM(Pass.Ctx.getSourceManager()) {

    CaseCollector(BodyCtx.getParentMap(), Cases)
        .TraverseStmt(BodyCtx.getTopStmt());
    LocalRefsCollector(LocalRefs).TraverseStmt(BodyCtx.getTopStmt());

    SourceRange BodyRange = BodyCtx.getTopStmt()->getSourceRange();
    const CapturedDiagList &DiagList = Pass.getDiags();
    // Copy the diagnostics so we don't have to worry about invaliding iterators
    // from the diagnostic list.
    SmallVector<StoredDiagnostic, 16> StoredDiags;
    StoredDiags.append(DiagList.begin(), DiagList.end());
    SmallVectorImpl<StoredDiagnostic>::iterator
        I = StoredDiags.begin(), E = StoredDiags.end();
    while (I != E) {
      if (I->getID() == diag::err_switch_into_protected_scope &&
          isInRange(I->getLocation(), BodyRange)) {
        handleProtectedScopeError(I, E);
        continue;
      }
      ++I;
    }
  }

  void handleProtectedScopeError(
                             SmallVectorImpl<StoredDiagnostic>::iterator &DiagI,
                             SmallVectorImpl<StoredDiagnostic>::iterator DiagE){
    Transaction Trans(Pass.TA);
    assert(DiagI->getID() == diag::err_switch_into_protected_scope);
    SourceLocation ErrLoc = DiagI->getLocation();
    bool handledAllNotes = true;
    ++DiagI;
    for (; DiagI != DiagE && DiagI->getLevel() == DiagnosticsEngine::Note;
         ++DiagI) {
      if (!handleProtectedNote(*DiagI))
        handledAllNotes = false;
    }

    if (handledAllNotes)
      Pass.TA.clearDiagnostic(diag::err_switch_into_protected_scope, ErrLoc);
  }

  bool handleProtectedNote(const StoredDiagnostic &Diag) {
    assert(Diag.getLevel() == DiagnosticsEngine::Note);

    for (unsigned i = 0; i != Cases.size(); i++) {
      CaseInfo &info = Cases[i];
      if (isInRange(Diag.getLocation(), info.Range)) {

        if (info.State == CaseInfo::St_Unchecked)
          tryFixing(info);
        assert(info.State != CaseInfo::St_Unchecked);

        if (info.State == CaseInfo::St_Fixed) {
          Pass.TA.clearDiagnostic(Diag.getID(), Diag.getLocation());
          return true;
        }
        return false;
      }
    }

    return false;
  }

  void tryFixing(CaseInfo &info) {
    assert(info.State == CaseInfo::St_Unchecked);
    if (hasVarReferencedOutside(info)) {
      info.State = CaseInfo::St_CannotFix;
      return;
    }

    Pass.TA.insertAfterToken(info.SC->getColonLoc(), " {");
    Pass.TA.insert(info.Range.getEnd(), "}\n");
    info.State = CaseInfo::St_Fixed;
  }

  bool hasVarReferencedOutside(CaseInfo &info) {
    for (unsigned i = 0, e = LocalRefs.size(); i != e; ++i) {
      DeclRefExpr *DRE = LocalRefs[i];
      if (isInRange(DRE->getDecl()->getLocation(), info.Range) &&
          !isInRange(DRE->getLocation(), info.Range))
        return true;
    }
    return false;
  }

  bool isInRange(SourceLocation Loc, SourceRange R) {
    if (Loc.isInvalid())
      return false;
    return !SM.isBeforeInTranslationUnit(Loc, R.getBegin()) &&
            SM.isBeforeInTranslationUnit(Loc, R.getEnd());
  }
};

} // anonymous namespace

void ProtectedScopeTraverser::traverseBody(BodyContext &BodyCtx) {
  ProtectedScopeFixer Fix(BodyCtx);
}
@


1.1.1.1
log
@Import Clang 3.4rc1 r195771.
@
text
@@


1.1.1.1.2.1
log
@Rebase.
@
text
@d50 1
a50 1
  CaseInfo() : SC(nullptr), State(St_Unchecked) {}
@


1.1.1.2
log
@Import Clang 3.5svn r209886.
@
text
@d50 1
a50 1
  CaseInfo() : SC(nullptr), State(St_Unchecked) {}
@


1.1.1.2.26.1
log
@Mostly merge changes from HEAD upto 20200411
@
text
@@


1.1.1.3
log
@Mark old LLVM instance as dead.
@
text
@@


1.1.1.2.4.1
log
@file TransProtectedScope.cpp was added on branch tls-maxphys on 2014-08-19 23:47:25 +0000
@
text
@d1 202
@


1.1.1.2.4.2
log
@Rebase to HEAD as of a few days ago.
@
text
@a0 202
//===--- TransProtectedScope.cpp - Transformations to ARC mode ------------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// Adds brackets in case statements that "contain" initialization of retaining
// variable, thus emitting the "switch case is in protected scope" error.
//
//===----------------------------------------------------------------------===//

#include "Transforms.h"
#include "Internals.h"
#include "clang/AST/ASTContext.h"
#include "clang/Sema/SemaDiagnostic.h"

using namespace clang;
using namespace arcmt;
using namespace trans;

namespace {

class LocalRefsCollector : public RecursiveASTVisitor<LocalRefsCollector> {
  SmallVectorImpl<DeclRefExpr *> &Refs;

public:
  LocalRefsCollector(SmallVectorImpl<DeclRefExpr *> &refs)
    : Refs(refs) { }

  bool VisitDeclRefExpr(DeclRefExpr *E) {
    if (ValueDecl *D = E->getDecl())
      if (D->getDeclContext()->getRedeclContext()->isFunctionOrMethod())
        Refs.push_back(E);
    return true;
  }
};

struct CaseInfo {
  SwitchCase *SC;
  SourceRange Range;
  enum {
    St_Unchecked,
    St_CannotFix,
    St_Fixed
  } State;
  
  CaseInfo() : SC(nullptr), State(St_Unchecked) {}
  CaseInfo(SwitchCase *S, SourceRange Range)
    : SC(S), Range(Range), State(St_Unchecked) {}
};

class CaseCollector : public RecursiveASTVisitor<CaseCollector> {
  ParentMap &PMap;
  SmallVectorImpl<CaseInfo> &Cases;

public:
  CaseCollector(ParentMap &PMap, SmallVectorImpl<CaseInfo> &Cases)
    : PMap(PMap), Cases(Cases) { }

  bool VisitSwitchStmt(SwitchStmt *S) {
    SwitchCase *Curr = S->getSwitchCaseList();
    if (!Curr)
      return true;
    Stmt *Parent = getCaseParent(Curr);
    Curr = Curr->getNextSwitchCase();
    // Make sure all case statements are in the same scope.
    while (Curr) {
      if (getCaseParent(Curr) != Parent)
        return true;
      Curr = Curr->getNextSwitchCase();
    }

    SourceLocation NextLoc = S->getLocEnd();
    Curr = S->getSwitchCaseList();
    // We iterate over case statements in reverse source-order.
    while (Curr) {
      Cases.push_back(CaseInfo(Curr,SourceRange(Curr->getLocStart(), NextLoc)));
      NextLoc = Curr->getLocStart();
      Curr = Curr->getNextSwitchCase();
    }
    return true;
  }

  Stmt *getCaseParent(SwitchCase *S) {
    Stmt *Parent = PMap.getParent(S);
    while (Parent && (isa<SwitchCase>(Parent) || isa<LabelStmt>(Parent)))
      Parent = PMap.getParent(Parent);
    return Parent;
  }
};

class ProtectedScopeFixer {
  MigrationPass &Pass;
  SourceManager &SM;
  SmallVector<CaseInfo, 16> Cases;
  SmallVector<DeclRefExpr *, 16> LocalRefs;

public:
  ProtectedScopeFixer(BodyContext &BodyCtx)
    : Pass(BodyCtx.getMigrationContext().Pass),
      SM(Pass.Ctx.getSourceManager()) {

    CaseCollector(BodyCtx.getParentMap(), Cases)
        .TraverseStmt(BodyCtx.getTopStmt());
    LocalRefsCollector(LocalRefs).TraverseStmt(BodyCtx.getTopStmt());

    SourceRange BodyRange = BodyCtx.getTopStmt()->getSourceRange();
    const CapturedDiagList &DiagList = Pass.getDiags();
    // Copy the diagnostics so we don't have to worry about invaliding iterators
    // from the diagnostic list.
    SmallVector<StoredDiagnostic, 16> StoredDiags;
    StoredDiags.append(DiagList.begin(), DiagList.end());
    SmallVectorImpl<StoredDiagnostic>::iterator
        I = StoredDiags.begin(), E = StoredDiags.end();
    while (I != E) {
      if (I->getID() == diag::err_switch_into_protected_scope &&
          isInRange(I->getLocation(), BodyRange)) {
        handleProtectedScopeError(I, E);
        continue;
      }
      ++I;
    }
  }

  void handleProtectedScopeError(
                             SmallVectorImpl<StoredDiagnostic>::iterator &DiagI,
                             SmallVectorImpl<StoredDiagnostic>::iterator DiagE){
    Transaction Trans(Pass.TA);
    assert(DiagI->getID() == diag::err_switch_into_protected_scope);
    SourceLocation ErrLoc = DiagI->getLocation();
    bool handledAllNotes = true;
    ++DiagI;
    for (; DiagI != DiagE && DiagI->getLevel() == DiagnosticsEngine::Note;
         ++DiagI) {
      if (!handleProtectedNote(*DiagI))
        handledAllNotes = false;
    }

    if (handledAllNotes)
      Pass.TA.clearDiagnostic(diag::err_switch_into_protected_scope, ErrLoc);
  }

  bool handleProtectedNote(const StoredDiagnostic &Diag) {
    assert(Diag.getLevel() == DiagnosticsEngine::Note);

    for (unsigned i = 0; i != Cases.size(); i++) {
      CaseInfo &info = Cases[i];
      if (isInRange(Diag.getLocation(), info.Range)) {

        if (info.State == CaseInfo::St_Unchecked)
          tryFixing(info);
        assert(info.State != CaseInfo::St_Unchecked);

        if (info.State == CaseInfo::St_Fixed) {
          Pass.TA.clearDiagnostic(Diag.getID(), Diag.getLocation());
          return true;
        }
        return false;
      }
    }

    return false;
  }

  void tryFixing(CaseInfo &info) {
    assert(info.State == CaseInfo::St_Unchecked);
    if (hasVarReferencedOutside(info)) {
      info.State = CaseInfo::St_CannotFix;
      return;
    }

    Pass.TA.insertAfterToken(info.SC->getColonLoc(), " {");
    Pass.TA.insert(info.Range.getEnd(), "}\n");
    info.State = CaseInfo::St_Fixed;
  }

  bool hasVarReferencedOutside(CaseInfo &info) {
    for (unsigned i = 0, e = LocalRefs.size(); i != e; ++i) {
      DeclRefExpr *DRE = LocalRefs[i];
      if (isInRange(DRE->getDecl()->getLocation(), info.Range) &&
          !isInRange(DRE->getLocation(), info.Range))
        return true;
    }
    return false;
  }

  bool isInRange(SourceLocation Loc, SourceRange R) {
    if (Loc.isInvalid())
      return false;
    return !SM.isBeforeInTranslationUnit(Loc, R.getBegin()) &&
            SM.isBeforeInTranslationUnit(Loc, R.getEnd());
  }
};

} // anonymous namespace

void ProtectedScopeTraverser::traverseBody(BodyContext &BodyCtx) {
  ProtectedScopeFixer Fix(BodyCtx);
}
@


1.1.1.1.4.1
log
@file TransProtectedScope.cpp was added on branch yamt-pagecache on 2014-05-22 16:18:25 +0000
@
text
@d1 202
@


1.1.1.1.4.2
log
@sync with head.

for a reference, the tree before this commit was tagged
as yamt-pagecache-tag8.

this commit was splitted into small chunks to avoid
a limitation of cvs.  ("Protocol error: too many arguments")
@
text
@a0 202
//===--- TransProtectedScope.cpp - Transformations to ARC mode ------------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// Adds brackets in case statements that "contain" initialization of retaining
// variable, thus emitting the "switch case is in protected scope" error.
//
//===----------------------------------------------------------------------===//

#include "Transforms.h"
#include "Internals.h"
#include "clang/AST/ASTContext.h"
#include "clang/Sema/SemaDiagnostic.h"

using namespace clang;
using namespace arcmt;
using namespace trans;

namespace {

class LocalRefsCollector : public RecursiveASTVisitor<LocalRefsCollector> {
  SmallVectorImpl<DeclRefExpr *> &Refs;

public:
  LocalRefsCollector(SmallVectorImpl<DeclRefExpr *> &refs)
    : Refs(refs) { }

  bool VisitDeclRefExpr(DeclRefExpr *E) {
    if (ValueDecl *D = E->getDecl())
      if (D->getDeclContext()->getRedeclContext()->isFunctionOrMethod())
        Refs.push_back(E);
    return true;
  }
};

struct CaseInfo {
  SwitchCase *SC;
  SourceRange Range;
  enum {
    St_Unchecked,
    St_CannotFix,
    St_Fixed
  } State;
  
  CaseInfo() : SC(0), State(St_Unchecked) {}
  CaseInfo(SwitchCase *S, SourceRange Range)
    : SC(S), Range(Range), State(St_Unchecked) {}
};

class CaseCollector : public RecursiveASTVisitor<CaseCollector> {
  ParentMap &PMap;
  SmallVectorImpl<CaseInfo> &Cases;

public:
  CaseCollector(ParentMap &PMap, SmallVectorImpl<CaseInfo> &Cases)
    : PMap(PMap), Cases(Cases) { }

  bool VisitSwitchStmt(SwitchStmt *S) {
    SwitchCase *Curr = S->getSwitchCaseList();
    if (!Curr)
      return true;
    Stmt *Parent = getCaseParent(Curr);
    Curr = Curr->getNextSwitchCase();
    // Make sure all case statements are in the same scope.
    while (Curr) {
      if (getCaseParent(Curr) != Parent)
        return true;
      Curr = Curr->getNextSwitchCase();
    }

    SourceLocation NextLoc = S->getLocEnd();
    Curr = S->getSwitchCaseList();
    // We iterate over case statements in reverse source-order.
    while (Curr) {
      Cases.push_back(CaseInfo(Curr,SourceRange(Curr->getLocStart(), NextLoc)));
      NextLoc = Curr->getLocStart();
      Curr = Curr->getNextSwitchCase();
    }
    return true;
  }

  Stmt *getCaseParent(SwitchCase *S) {
    Stmt *Parent = PMap.getParent(S);
    while (Parent && (isa<SwitchCase>(Parent) || isa<LabelStmt>(Parent)))
      Parent = PMap.getParent(Parent);
    return Parent;
  }
};

class ProtectedScopeFixer {
  MigrationPass &Pass;
  SourceManager &SM;
  SmallVector<CaseInfo, 16> Cases;
  SmallVector<DeclRefExpr *, 16> LocalRefs;

public:
  ProtectedScopeFixer(BodyContext &BodyCtx)
    : Pass(BodyCtx.getMigrationContext().Pass),
      SM(Pass.Ctx.getSourceManager()) {

    CaseCollector(BodyCtx.getParentMap(), Cases)
        .TraverseStmt(BodyCtx.getTopStmt());
    LocalRefsCollector(LocalRefs).TraverseStmt(BodyCtx.getTopStmt());

    SourceRange BodyRange = BodyCtx.getTopStmt()->getSourceRange();
    const CapturedDiagList &DiagList = Pass.getDiags();
    // Copy the diagnostics so we don't have to worry about invaliding iterators
    // from the diagnostic list.
    SmallVector<StoredDiagnostic, 16> StoredDiags;
    StoredDiags.append(DiagList.begin(), DiagList.end());
    SmallVectorImpl<StoredDiagnostic>::iterator
        I = StoredDiags.begin(), E = StoredDiags.end();
    while (I != E) {
      if (I->getID() == diag::err_switch_into_protected_scope &&
          isInRange(I->getLocation(), BodyRange)) {
        handleProtectedScopeError(I, E);
        continue;
      }
      ++I;
    }
  }

  void handleProtectedScopeError(
                             SmallVectorImpl<StoredDiagnostic>::iterator &DiagI,
                             SmallVectorImpl<StoredDiagnostic>::iterator DiagE){
    Transaction Trans(Pass.TA);
    assert(DiagI->getID() == diag::err_switch_into_protected_scope);
    SourceLocation ErrLoc = DiagI->getLocation();
    bool handledAllNotes = true;
    ++DiagI;
    for (; DiagI != DiagE && DiagI->getLevel() == DiagnosticsEngine::Note;
         ++DiagI) {
      if (!handleProtectedNote(*DiagI))
        handledAllNotes = false;
    }

    if (handledAllNotes)
      Pass.TA.clearDiagnostic(diag::err_switch_into_protected_scope, ErrLoc);
  }

  bool handleProtectedNote(const StoredDiagnostic &Diag) {
    assert(Diag.getLevel() == DiagnosticsEngine::Note);

    for (unsigned i = 0; i != Cases.size(); i++) {
      CaseInfo &info = Cases[i];
      if (isInRange(Diag.getLocation(), info.Range)) {

        if (info.State == CaseInfo::St_Unchecked)
          tryFixing(info);
        assert(info.State != CaseInfo::St_Unchecked);

        if (info.State == CaseInfo::St_Fixed) {
          Pass.TA.clearDiagnostic(Diag.getID(), Diag.getLocation());
          return true;
        }
        return false;
      }
    }

    return false;
  }

  void tryFixing(CaseInfo &info) {
    assert(info.State == CaseInfo::St_Unchecked);
    if (hasVarReferencedOutside(info)) {
      info.State = CaseInfo::St_CannotFix;
      return;
    }

    Pass.TA.insertAfterToken(info.SC->getColonLoc(), " {");
    Pass.TA.insert(info.Range.getEnd(), "}\n");
    info.State = CaseInfo::St_Fixed;
  }

  bool hasVarReferencedOutside(CaseInfo &info) {
    for (unsigned i = 0, e = LocalRefs.size(); i != e; ++i) {
      DeclRefExpr *DRE = LocalRefs[i];
      if (isInRange(DRE->getDecl()->getLocation(), info.Range) &&
          !isInRange(DRE->getLocation(), info.Range))
        return true;
    }
    return false;
  }

  bool isInRange(SourceLocation Loc, SourceRange R) {
    if (Loc.isInvalid())
      return false;
    return !SM.isBeforeInTranslationUnit(Loc, R.getBegin()) &&
            SM.isBeforeInTranslationUnit(Loc, R.getEnd());
  }
};

} // anonymous namespace

void ProtectedScopeTraverser::traverseBody(BodyContext &BodyCtx) {
  ProtectedScopeFixer Fix(BodyCtx);
}
@


