//===- unittest/Format/FormatTestVerilog.cpp ------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//

#include "FormatTestUtils.h"
#include "clang/Format/Format.h"
#include "llvm/Support/Debug.h"
#include "gtest/gtest.h"

#define DEBUG_TYPE "format-test"

namespace clang {
namespace format {

class FormatTestVerilog : public ::testing::Test {
protected:
  static std::string format(llvm::StringRef Code, unsigned Offset,
                            unsigned Length, const FormatStyle &Style) {
    LLVM_DEBUG(llvm::errs() << "---\n");
    LLVM_DEBUG(llvm::errs() << Code << "\n\n");
    std::vector<tooling::Range> Ranges(1, tooling::Range(Offset, Length));
    tooling::Replacements Replaces = reformat(Style, Code, Ranges);
    auto Result = applyAllReplacements(Code, Replaces);
    EXPECT_TRUE(static_cast<bool>(Result));
    LLVM_DEBUG(llvm::errs() << "\n" << *Result << "\n\n");
    return *Result;
  }

  static std::string
  format(llvm::StringRef Code,
         const FormatStyle &Style = getLLVMStyle(FormatStyle::LK_Verilog)) {
    return format(Code, 0, Code.size(), Style);
  }

  static void verifyFormat(
      llvm::StringRef Code,
      const FormatStyle &Style = getLLVMStyle(FormatStyle::LK_Verilog)) {
    EXPECT_EQ(Code.str(), format(Code, Style)) << "Expected code is not stable";
    EXPECT_EQ(Code.str(),
              format(test::messUp(Code, /*HandleHash=*/false), Style));
  }
};

TEST_F(FormatTestVerilog, BasedLiteral) {
  verifyFormat("x = '0;");
  verifyFormat("x = '1;");
  verifyFormat("x = 'X;");
  verifyFormat("x = 'x;");
  verifyFormat("x = 'Z;");
  verifyFormat("x = 'z;");
  verifyFormat("x = 659;");
  verifyFormat("x = 'h837ff;");
  verifyFormat("x = 'o7460;");
  verifyFormat("x = 4'b1001;");
  verifyFormat("x = 5'D3;");
  verifyFormat("x = 3'b01x;");
  verifyFormat("x = 12'hx;");
  verifyFormat("x = 16'hz;");
  verifyFormat("x = -8'd6;");
  verifyFormat("x = 4'shf;");
  verifyFormat("x = -4'sd15;");
  verifyFormat("x = 16'sd?;");
}

TEST_F(FormatTestVerilog, Block) {
  verifyFormat("begin\n"
               "  x = x;\n"
               "end");
  verifyFormat("begin : x\n"
               "  x = x;\n"
               "end : x");
  verifyFormat("begin\n"
               "  x = x;\n"
               "  x = x;\n"
               "end");
  verifyFormat("fork\n"
               "  x = x;\n"
               "join");
  verifyFormat("fork\n"
               "  x = x;\n"
               "join_any");
  verifyFormat("fork\n"
               "  x = x;\n"
               "join_none");
  verifyFormat("generate\n"
               "  x = x;\n"
               "endgenerate");
  verifyFormat("generate : x\n"
               "  x = x;\n"
               "endgenerate : x");
  // Nested blocks.
  verifyFormat("begin\n"
               "  begin\n"
               "  end\n"
               "end");
  verifyFormat("begin : x\n"
               "  begin\n"
               "  end\n"
               "end : x");
  verifyFormat("begin : x\n"
               "  begin : x\n"
               "  end : x\n"
               "end : x");
  verifyFormat("begin\n"
               "  begin : x\n"
               "  end : x\n"
               "end");
  // Test that 'disable fork' and 'rand join' don't get mistaken as blocks.
  verifyFormat("disable fork;\n"
               "x = x;");
  verifyFormat("rand join x x;\n"
               "x = x;");
}

TEST_F(FormatTestVerilog, Case) {
  verifyFormat("case (data)\n"
               "endcase");
  verifyFormat("casex (data)\n"
               "endcase");
  verifyFormat("casez (data)\n"
               "endcase");
  verifyFormat("case (data) inside\n"
               "endcase");
  verifyFormat("case (data)\n"
               "  16'd0:\n"
               "    result = 10'b0111111111;\n"
               "endcase");
  verifyFormat("case (data)\n"
               "  xxxxxxxx:\n"
               "    result = 10'b0111111111;\n"
               "endcase");
  // Test labels with multiple options.
  verifyFormat("case (data)\n"
               "  16'd0, 16'd1:\n"
               "    result = 10'b0111111111;\n"
               "endcase");
  verifyFormat("case (data)\n"
               "  16'd0, //\n"
               "      16'd1:\n"
               "    result = 10'b0111111111;\n"
               "endcase");
  // Test that blocks following labels are indented.
  verifyFormat("case (data)\n"
               "  16'd1: fork\n"
               "    result = 10'b1011111111;\n"
               "  join\n"
               "endcase\n");
  verifyFormat("case (data)\n"
               "  16'd1: fork : x\n"
               "    result = 10'b1011111111;\n"
               "  join : x\n"
               "endcase\n");
  // Test default.
  verifyFormat("case (data)\n"
               "  default\n"
               "    result = 10'b1011111111;\n"
               "endcase");
  verifyFormat("case (data)\n"
               "  default:\n"
               "    result = 10'b1011111111;\n"
               "endcase");
  // Test that question marks and colons don't get mistaken as labels.
  verifyFormat("case (data)\n"
               "  8'b1???????:\n"
               "    instruction1(ir);\n"
               "endcase");
  verifyFormat("case (data)\n"
               "  x ? 8'b1??????? : 1:\n"
               "    instruction3(ir);\n"
               "endcase");
  // Test indention options.
  auto Style = getLLVMStyle(FormatStyle::LK_Verilog);
  Style.IndentCaseLabels = false;
  verifyFormat("case (data)\n"
               "16'd0:\n"
               "  result = 10'b0111111111;\n"
               "endcase",
               Style);
  verifyFormat("case (data)\n"
               "16'd0: begin\n"
               "  result = 10'b0111111111;\n"
               "end\n"
               "endcase",
               Style);
  Style.IndentCaseLabels = true;
  verifyFormat("case (data)\n"
               "  16'd0:\n"
               "    result = 10'b0111111111;\n"
               "endcase",
               Style);
  verifyFormat("case (data)\n"
               "  16'd0: begin\n"
               "    result = 10'b0111111111;\n"
               "  end\n"
               "endcase",
               Style);
}

TEST_F(FormatTestVerilog, Delay) {
  // Delay by the default unit.
  verifyFormat("#0;");
  verifyFormat("#1;");
  verifyFormat("#10;");
  verifyFormat("#1.5;");
  // Explicit unit.
  verifyFormat("#1fs;");
  verifyFormat("#1.5fs;");
  verifyFormat("#1ns;");
  verifyFormat("#1.5ns;");
  verifyFormat("#1us;");
  verifyFormat("#1.5us;");
  verifyFormat("#1ms;");
  verifyFormat("#1.5ms;");
  verifyFormat("#1s;");
  verifyFormat("#1.5s;");
  // The following expression should be on the same line.
  verifyFormat("#1 x = x;");
  EXPECT_EQ("#1 x = x;", format("#1\n"
                                "x = x;"));
}

TEST_F(FormatTestVerilog, Hierarchy) {
  verifyFormat("module x;\n"
               "endmodule");
  // Test that the end label is on the same line as the end keyword.
  verifyFormat("module x;\n"
               "endmodule : x");
  // Test that things inside are indented.
  verifyFormat("module x;\n"
               "  generate\n"
               "  endgenerate\n"
               "endmodule");
  verifyFormat("program x;\n"
               "  generate\n"
               "  endgenerate\n"
               "endprogram");
  verifyFormat("interface x;\n"
               "  generate\n"
               "  endgenerate\n"
               "endinterface");
  verifyFormat("task x;\n"
               "  generate\n"
               "  endgenerate\n"
               "endtask");
  verifyFormat("function x;\n"
               "  generate\n"
               "  endgenerate\n"
               "endfunction");
  verifyFormat("class x;\n"
               "  generate\n"
               "  endgenerate\n"
               "endclass");
  // Test that they nest.
  verifyFormat("module x;\n"
               "  program x;\n"
               "    program x;\n"
               "    endprogram\n"
               "  endprogram\n"
               "endmodule");
  // Test that an extern declaration doesn't change the indentation.
  verifyFormat("extern module x;\n"
               "x = x;");
  // Test complex headers
  verifyFormat("extern module x\n"
               "    import x.x::x::*;\n"
               "    import x;\n"
               "    #(parameter x)\n"
               "    (output x);");
  verifyFormat("module x\n"
               "    import x.x::x::*;\n"
               "    import x;\n"
               "    #(parameter x)\n"
               "    (output x);\n"
               "  generate\n"
               "  endgenerate\n"
               "endmodule : x");
  verifyFormat("virtual class x\n"
               "    (x)\n"
               "    extends x(x)\n"
               "    implements x, x, x;\n"
               "  generate\n"
               "  endgenerate\n"
               "endclass : x\n");
  verifyFormat("function automatic logic [1 : 0] x\n"
               "    (input x);\n"
               "  generate\n"
               "  endgenerate\n"
               "endfunction : x");
}

TEST_F(FormatTestVerilog, If) {
  verifyFormat("if (x)\n"
               "  x = x;");
  verifyFormat("unique if (x)\n"
               "  x = x;");
  verifyFormat("unique0 if (x)\n"
               "  x = x;");
  verifyFormat("priority if (x)\n"
               "  x = x;");
  verifyFormat("if (x)\n"
               "  x = x;\n"
               "x = x;");

  // Test else
  verifyFormat("if (x)\n"
               "  x = x;\n"
               "else if (x)\n"
               "  x = x;\n"
               "else\n"
               "  x = x;");
  verifyFormat("if (x) begin\n"
               "  x = x;\n"
               "end else if (x) begin\n"
               "  x = x;\n"
               "end else begin\n"
               "  x = x;\n"
               "end");
  verifyFormat("if (x) begin : x\n"
               "  x = x;\n"
               "end : x else if (x) begin : x\n"
               "  x = x;\n"
               "end : x else begin : x\n"
               "  x = x;\n"
               "end : x");

  // Test block keywords.
  verifyFormat("if (x) begin\n"
               "  x = x;\n"
               "end");
  verifyFormat("if (x) begin : x\n"
               "  x = x;\n"
               "end : x");
  verifyFormat("if (x) begin\n"
               "  x = x;\n"
               "  x = x;\n"
               "end");
  verifyFormat("if (x) fork\n"
               "  x = x;\n"
               "join");
  verifyFormat("if (x) fork\n"
               "  x = x;\n"
               "join_any");
  verifyFormat("if (x) fork\n"
               "  x = x;\n"
               "join_none");
  verifyFormat("if (x) generate\n"
               "  x = x;\n"
               "endgenerate");
  verifyFormat("if (x) generate : x\n"
               "  x = x;\n"
               "endgenerate : x");

  // Test that concatenation braces don't get regarded as blocks.
  verifyFormat("if (x)\n"
               "  {x} = x;");
  verifyFormat("if (x)\n"
               "  x = {x};");
  verifyFormat("if (x)\n"
               "  x = {x};\n"
               "else\n"
               "  {x} = {x};");

  // With attributes.
  verifyFormat("(* x *) if (x)\n"
               "  x = x;");
  verifyFormat("(* x = \"x\" *) if (x)\n"
               "  x = x;");
  verifyFormat("(* x, x = \"x\" *) if (x)\n"
               "  x = x;");
}

TEST_F(FormatTestVerilog, Operators) {
  // Test that unary operators are not followed by space.
  verifyFormat("x = +x;");
  verifyFormat("x = -x;");
  verifyFormat("x = !x;");
  verifyFormat("x = ~x;");
  verifyFormat("x = &x;");
  verifyFormat("x = ~&x;");
  verifyFormat("x = |x;");
  verifyFormat("x = ~|x;");
  verifyFormat("x = ^x;");
  verifyFormat("x = ~^x;");
  verifyFormat("x = ^~x;");
  verifyFormat("x = ++x;");
  verifyFormat("x = --x;");

  // Test that operators don't get split.
  verifyFormat("x = x++;");
  verifyFormat("x = x--;");
  verifyFormat("x = x ** x;");
  verifyFormat("x = x << x;");
  verifyFormat("x = x >> x;");
  verifyFormat("x = x <<< x;");
  verifyFormat("x = x >>> x;");
  verifyFormat("x = x <= x;");
  verifyFormat("x = x >= x;");
  verifyFormat("x = x == x;");
  verifyFormat("x = x != x;");
  verifyFormat("x = x === x;");
  verifyFormat("x = x !== x;");
  verifyFormat("x = x ==? x;");
  verifyFormat("x = x !=? x;");
  verifyFormat("x = x ~^ x;");
  verifyFormat("x = x ^~ x;");
  verifyFormat("x = x && x;");
  verifyFormat("x = x || x;");
  verifyFormat("x = x->x;");
  verifyFormat("x = x <-> x;");
  verifyFormat("x += x;");
  verifyFormat("x -= x;");
  verifyFormat("x *= x;");
  verifyFormat("x /= x;");
  verifyFormat("x %= x;");
  verifyFormat("x &= x;");
  verifyFormat("x ^= x;");
  verifyFormat("x |= x;");
  verifyFormat("x <<= x;");
  verifyFormat("x >>= x;");
  verifyFormat("x <<<= x;");
  verifyFormat("x >>>= x;");
  verifyFormat("x <= x;");

  // Test that space is added between operators.
  EXPECT_EQ("x = x < -x;", format("x=x<-x;"));
  EXPECT_EQ("x = x << -x;", format("x=x<<-x;"));
  EXPECT_EQ("x = x <<< -x;", format("x=x<<<-x;"));
}

TEST_F(FormatTestVerilog, Preprocessor) {
  auto Style = getLLVMStyle(FormatStyle::LK_Verilog);
  Style.ColumnLimit = 20;

  // Macro definitions.
  EXPECT_EQ("`define X          \\\n"
            "  if (x)           \\\n"
            "    x = x;",
            format("`define X if(x)x=x;", Style));
  EXPECT_EQ("`define X(x)       \\\n"
            "  if (x)           \\\n"
            "    x = x;",
            format("`define X(x) if(x)x=x;", Style));
  EXPECT_EQ("`define X          \\\n"
            "  x = x;           \\\n"
            "  x = x;",
            format("`define X x=x;x=x;", Style));
  // Macro definitions with invocations inside.
  EXPECT_EQ("`define LIST       \\\n"
            "  `ENTRY           \\\n"
            "  `ENTRY",
            format("`define LIST \\\n"
                   "`ENTRY \\\n"
                   "`ENTRY",
                   Style));
  EXPECT_EQ("`define LIST       \\\n"
            "  `x = `x;         \\\n"
            "  `x = `x;",
            format("`define LIST \\\n"
                   "`x = `x; \\\n"
                   "`x = `x;",
                   Style));
  EXPECT_EQ("`define LIST       \\\n"
            "  `x = `x;         \\\n"
            "  `x = `x;",
            format("`define LIST `x=`x;`x=`x;", Style));
  // Macro invocations.
  verifyFormat("`x = (`x1 + `x2 + x);");
  // Lines starting with a preprocessor directive should not be indented.
  std::string Directives[] = {
      "begin_keywords",
      "celldefine",
      "default_nettype",
      "define",
      "else",
      "elsif",
      "end_keywords",
      "endcelldefine",
      "endif",
      "ifdef",
      "ifndef",
      "include",
      "line",
      "nounconnected_drive",
      "pragma",
      "resetall",
      "timescale",
      "unconnected_drive",
      "undef",
      "undefineall",
  };
  for (auto &Name : Directives) {
    EXPECT_EQ("if (x)\n"
              "`" +
                  Name +
                  "\n"
                  "  ;",
              format("if (x)\n"
                     "`" +
                         Name +
                         "\n"
                         ";",
                     Style));
  }
  // Lines starting with a regular macro invocation should be indented as a
  // normal line.
  EXPECT_EQ("if (x)\n"
            "  `x = `x;\n"
            "`timescale 1ns / 1ps",
            format("if (x)\n"
                   "`x = `x;\n"
                   "`timescale 1ns / 1ps",
                   Style));
  EXPECT_EQ("if (x)\n"
            "`timescale 1ns / 1ps\n"
            "  `x = `x;",
            format("if (x)\n"
                   "`timescale 1ns / 1ps\n"
                   "`x = `x;",
                   Style));
  std::string NonDirectives[] = {
      // For `__FILE__` and `__LINE__`, although the standard classifies them as
      // preprocessor directives, they are used like regular macros.
      "__FILE__", "__LINE__", "elif", "foo", "x",
  };
  for (auto &Name : NonDirectives) {
    EXPECT_EQ("if (x)\n"
              "  `" +
                  Name + ";",
              format("if (x)\n"
                     "`" +
                         Name +
                         "\n"
                         ";",
                     Style));
  }
}

TEST_F(FormatTestVerilog, Primitive) {
  verifyFormat("primitive multiplexer\n"
               "    (mux, control, dataA, dataB);\n"
               "  output mux;\n"
               "  input control, dataA, dataB;\n"
               "  table\n"
               "    0 1 ? : 1;\n"
               "    0 0 ? : 0;\n"
               "    1 ? 1 : 1;\n"
               "    1 ? 0 : 0;\n"
               "    x 0 0 : 0;\n"
               "    x 1 1 : 1;\n"
               "  endtable\n"
               "endprimitive");
  verifyFormat("primitive latch\n"
               "    (q, ena_, data);\n"
               "  output q;\n"
               "  reg q;\n"
               "  input ena_, data;\n"
               "  table\n"
               "    0 1 : ? : 1;\n"
               "    0 0 : ? : 0;\n"
               "    1 ? : ? : -;\n"
               "    ? * : ? : -;\n"
               "  endtable\n"
               "endprimitive");
  verifyFormat("primitive d\n"
               "    (q, clock, data);\n"
               "  output q;\n"
               "  reg q;\n"
               "  input clock, data;\n"
               "  table\n"
               "    (01) 0 : ? : 0;\n"
               "    (01) 1 : ? : 1;\n"
               "    (0?) 1 : 1 : 1;\n"
               "    (0?) 0 : 0 : 0;\n"
               "    (?0) ? : ? : -;\n"
               "    (?\?) ? : ? : -;\n"
               "  endtable\n"
               "endprimitive");
}
} // namespace format
} // end namespace clang
