//===--- Diagnostics.h - Helper class for error diagnostics -----*- 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
//
//===----------------------------------------------------------------------===//
///
/// \file
/// Diagnostics class to manage error messages.
///
//===----------------------------------------------------------------------===//

#ifndef LLVM_CLANG_ASTMATCHERS_DYNAMIC_DIAGNOSTICS_H
#define LLVM_CLANG_ASTMATCHERS_DYNAMIC_DIAGNOSTICS_H

#include "clang/ASTMatchers/Dynamic/VariantValue.h"
#include "clang/Basic/LLVM.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/Twine.h"
#include "llvm/Support/raw_ostream.h"
#include <string>
#include <vector>

namespace clang {
namespace ast_matchers {
namespace dynamic {

struct SourceLocation {
  SourceLocation() = default;
  unsigned Line = 0;
  unsigned Column = 0;
};

struct SourceRange {
  SourceLocation Start;
  SourceLocation End;
};

/// A VariantValue instance annotated with its parser context.
struct ParserValue {
  ParserValue() {}
  StringRef Text;
  SourceRange Range;
  VariantValue Value;
};

/// Helper class to manage error messages.
class Diagnostics {
public:
  /// Parser context types.
  enum ContextType {
    CT_MatcherArg = 0,
    CT_MatcherConstruct = 1
  };

  /// All errors from the system.
  enum ErrorType {
    ET_None = 0,

    ET_RegistryMatcherNotFound = 1,
    ET_RegistryWrongArgCount = 2,
    ET_RegistryWrongArgType = 3,
    ET_RegistryNotBindable = 4,
    ET_RegistryAmbiguousOverload = 5,
    ET_RegistryValueNotFound = 6,
    ET_RegistryUnknownEnumWithReplace = 7,
    ET_RegistryNonNodeMatcher = 8,
    ET_RegistryMatcherNoWithSupport = 9,

    ET_ParserStringError = 100,
    ET_ParserNoOpenParen = 101,
    ET_ParserNoCloseParen = 102,
    ET_ParserNoComma = 103,
    ET_ParserNoCode = 104,
    ET_ParserNotAMatcher = 105,
    ET_ParserInvalidToken = 106,
    ET_ParserMalformedBindExpr = 107,
    ET_ParserTrailingCode = 108,
    ET_ParserNumberError = 109,
    ET_ParserOverloadedType = 110,
    ET_ParserMalformedChainedExpr = 111,
    ET_ParserFailedToBuildMatcher = 112
  };

  /// Helper stream class.
  class ArgStream {
  public:
    ArgStream(std::vector<std::string> *Out) : Out(Out) {}
    template <class T> ArgStream &operator<<(const T &Arg) {
      return operator<<(Twine(Arg));
    }
    ArgStream &operator<<(const Twine &Arg);

  private:
    std::vector<std::string> *Out;
  };

  /// Class defining a parser context.
  ///
  /// Used by the parser to specify (possibly recursive) contexts where the
  /// parsing/construction can fail. Any error triggered within a context will
  /// keep information about the context chain.
  /// This class should be used as a RAII instance in the stack.
  struct Context {
  public:
    /// About to call the constructor for a matcher.
    enum ConstructMatcherEnum { ConstructMatcher };
    Context(ConstructMatcherEnum, Diagnostics *Error, StringRef MatcherName,
            SourceRange MatcherRange);
    /// About to recurse into parsing one argument for a matcher.
    enum MatcherArgEnum { MatcherArg };
    Context(MatcherArgEnum, Diagnostics *Error, StringRef MatcherName,
            SourceRange MatcherRange, unsigned ArgNumber);
    ~Context();

  private:
    Diagnostics *const Error;
  };

  /// Context for overloaded matcher construction.
  ///
  /// This context will take care of merging all errors that happen within it
  /// as "candidate" overloads for the same matcher.
  struct OverloadContext {
  public:
   OverloadContext(Diagnostics* Error);
   ~OverloadContext();

   /// Revert all errors that happened within this context.
   void revertErrors();

  private:
    Diagnostics *const Error;
    unsigned BeginIndex;
  };

  /// Add an error to the diagnostics.
  ///
  /// All the context information will be kept on the error message.
  /// \return a helper class to allow the caller to pass the arguments for the
  /// error message, using the << operator.
  ArgStream addError(SourceRange Range, ErrorType Error);

  /// Information stored for one frame of the context.
  struct ContextFrame {
    ContextType Type;
    SourceRange Range;
    std::vector<std::string> Args;
  };

  /// Information stored for each error found.
  struct ErrorContent {
    std::vector<ContextFrame> ContextStack;
    struct Message {
      SourceRange Range;
      ErrorType Type;
      std::vector<std::string> Args;
    };
    std::vector<Message> Messages;
  };
  ArrayRef<ErrorContent> errors() const { return Errors; }

  /// Returns a simple string representation of each error.
  ///
  /// Each error only shows the error message without any context.
  void printToStream(llvm::raw_ostream &OS) const;
  std::string toString() const;

  /// Returns the full string representation of each error.
  ///
  /// Each error message contains the full context.
  void printToStreamFull(llvm::raw_ostream &OS) const;
  std::string toStringFull() const;

private:
  /// Helper function used by the constructors of ContextFrame.
  ArgStream pushContextFrame(ContextType Type, SourceRange Range);

  std::vector<ContextFrame> ContextStack;
  std::vector<ErrorContent> Errors;
};

}  // namespace dynamic
}  // namespace ast_matchers
}  // namespace clang

#endif // LLVM_CLANG_ASTMATCHERS_DYNAMIC_DIAGNOSTICS_H
