//===- LifetimeSafety.cpp - C++ Lifetime Safety Analysis -*--------- 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
//
//===----------------------------------------------------------------------===//
//
// This file implements the main LifetimeSafetyAnalysis class, which coordinates
// the various components (fact generation, loan propagation, live origins
// analysis, and checking) to detect lifetime safety violations in C++ code.
//
//===----------------------------------------------------------------------===//
#include "clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h"
#include "clang/AST/Decl.h"
#include "clang/AST/Expr.h"
#include "clang/AST/Type.h"
#include "clang/Analysis/Analyses/LifetimeSafety/Checker.h"
#include "clang/Analysis/Analyses/LifetimeSafety/Facts.h"
#include "clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h"
#include "clang/Analysis/Analyses/LifetimeSafety/LifetimeStats.h"
#include "clang/Analysis/Analyses/LifetimeSafety/LiveOrigins.h"
#include "clang/Analysis/Analyses/LifetimeSafety/LoanPropagation.h"
#include "clang/Analysis/Analyses/LifetimeSafety/Origins.h"
#include "clang/Analysis/AnalysisDeclContext.h"
#include "clang/Analysis/CFG.h"
#include "llvm/ADT/FoldingSet.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/TimeProfiler.h"
#include <memory>

namespace clang::lifetimes {
namespace internal {

#ifndef NDEBUG
static void DebugOnlyFunction(AnalysisDeclContext &AC, const CFG &Cfg,
                              FactManager &FactMgr) {
  std::string Name;
  if (const Decl *D = AC.getDecl()) {
    if (const auto *ND = dyn_cast<NamedDecl>(D))
      Name = ND->getQualifiedNameAsString();
  };
  DEBUG_WITH_TYPE(Name.c_str(), AC.getDecl()->dumpColor());
  DEBUG_WITH_TYPE(Name.c_str(), Cfg.dump(AC.getASTContext().getLangOpts(),
                                         /*ShowColors=*/true));
  DEBUG_WITH_TYPE(Name.c_str(), FactMgr.dump(Cfg, AC));
}
#endif

LifetimeSafetyAnalysis::LifetimeSafetyAnalysis(AnalysisDeclContext &AC,
                                               LifetimeSafetyReporter *Reporter)
    : AC(AC), Reporter(Reporter) {}

void LifetimeSafetyAnalysis::run() {
  llvm::TimeTraceScope TimeProfile("LifetimeSafetyAnalysis");

  const CFG &Cfg = *AC.getCFG();
  DEBUG_WITH_TYPE("PrintCFG", Cfg.dump(AC.getASTContext().getLangOpts(),
                                       /*ShowColors=*/true));

  FactMgr = std::make_unique<FactManager>(AC, Cfg);

  FactsGenerator FactGen(*FactMgr, AC);
  FactGen.run();

  DEBUG_WITH_TYPE("LifetimeFacts", FactMgr->dump(Cfg, AC));

  // Debug print facts for a specific function using
  // -debug-only=EnableFilterByFunctionName,YourFunctionNameFoo
  DEBUG_WITH_TYPE("EnableFilterByFunctionName",
                  DebugOnlyFunction(AC, Cfg, *FactMgr));

  /// TODO(opt): Consider optimizing individual blocks before running the
  /// dataflow analysis.
  /// 1. Expression Origins: These are assigned once and read at most once,
  ///    forming simple chains. These chains can be compressed into a single
  ///    assignment.
  /// 2. Block-Local Loans: Origins of expressions are never read by other
  ///    blocks; only Decls are visible.  Therefore, loans in a block that
  ///    never reach an Origin associated with a Decl can be safely dropped by
  ///    the analysis.
  /// 3. Collapse ExpireFacts belonging to same source location into a single
  ///    Fact.
  LoanPropagation = std::make_unique<LoanPropagationAnalysis>(
      Cfg, AC, *FactMgr, Factory.OriginMapFactory, Factory.LoanSetFactory);

  LiveOrigins = std::make_unique<LiveOriginsAnalysis>(
      Cfg, AC, *FactMgr, Factory.LivenessMapFactory);
  DEBUG_WITH_TYPE("LiveOrigins",
                  LiveOrigins->dump(llvm::dbgs(), FactMgr->getTestPoints()));

  runLifetimeChecker(*LoanPropagation, *LiveOrigins, *FactMgr, AC, Reporter);
}

void collectLifetimeStats(AnalysisDeclContext &AC, OriginManager &OM,
                          LifetimeSafetyStats &Stats) {
  Stmt *FunctionBody = AC.getBody();
  if (FunctionBody == nullptr)
    return;
  OM.collectMissingOrigins(*FunctionBody, Stats);
}
} // namespace internal

void runLifetimeSafetyAnalysis(AnalysisDeclContext &AC,
                               LifetimeSafetyReporter *Reporter,
                               LifetimeSafetyStats &Stats, bool CollectStats) {
  internal::LifetimeSafetyAnalysis Analysis(AC, Reporter);
  Analysis.run();
  if (CollectStats)
    collectLifetimeStats(AC, Analysis.getFactManager().getOriginMgr(), Stats);
}
} // namespace clang::lifetimes
