Merge pull request #2062 from kbrunham-intel/feat/add_dryrun_option

Added --verify option to verilog-formatter.
diff --git a/verilog/tools/formatter/BUILD b/verilog/tools/formatter/BUILD
index d1467b3..693f280 100644
--- a/verilog/tools/formatter/BUILD
+++ b/verilog/tools/formatter/BUILD
@@ -68,6 +68,22 @@
 )
 
 sh_test_with_runfiles_lib(
+    name = "format-file-check_test",
+    size = "small",
+    srcs = ["format_file_check_test.sh"],
+    args = ["$(location :verible-verilog-format)"],
+    data = [":verible-verilog-format"],
+)
+
+sh_test_with_runfiles_lib(
+    name = "format-file-nochange-check_test",
+    size = "small",
+    srcs = ["format_file_check_nochange_test.sh"],
+    args = ["$(location :verible-verilog-format)"],
+    data = [":verible-verilog-format"],
+)
+
+sh_test_with_runfiles_lib(
     name = "format-file-lex-error_test",
     size = "small",
     srcs = ["format_file_lex_error_test.sh"],
diff --git a/verilog/tools/formatter/format_file_check_nochange_test.sh b/verilog/tools/formatter/format_file_check_nochange_test.sh
new file mode 100755
index 0000000..7a73fdb
--- /dev/null
+++ b/verilog/tools/formatter/format_file_check_nochange_test.sh
@@ -0,0 +1,45 @@
+#!/usr/bin/env bash
+# Copyright 2017-2020 The Verible Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Tests verible-verilog-format reading from a file, checking for formatting
+# changes where no changes are needed. This should return 0.
+
+declare -r MY_OUTPUT_FILE="${TEST_TMPDIR}/myoutput.txt"
+
+# Get tool from argument
+[[ "$#" == 1 ]] || {
+  echo "Expecting 1 positional argument, verible-verilog-format path."
+  exit 1
+}
+formatter="$(rlocation ${TEST_WORKSPACE}/$1)"
+
+# Will overwrite this file in-place.
+cat >${MY_OUTPUT_FILE} <<EOF
+  module    m   ;endmodule
+EOF
+
+# Run formatter.
+${formatter} --inplace ${MY_OUTPUT_FILE} || exit 1
+
+# Now run the verify mode, which should return 0 since changes
+# are already made
+${formatter} --verify ${MY_OUTPUT_FILE}
+if [ "$?" -neq 0 ]; then
+    echo "No changes should result in 0 error code"
+    echo "FAIL"
+    exit 1
+fi
+
+echo "PASS"
diff --git a/verilog/tools/formatter/format_file_check_test.sh b/verilog/tools/formatter/format_file_check_test.sh
new file mode 100755
index 0000000..bcb101a
--- /dev/null
+++ b/verilog/tools/formatter/format_file_check_test.sh
@@ -0,0 +1,41 @@
+#!/usr/bin/env bash
+# Copyright 2017-2020 The Verible Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Tests verible-verilog-format reading from a file, checking for formatting
+# changes where changes are needed. This should return 1.
+
+
+declare -r MY_INPUT_FILE="${TEST_TMPDIR}/myinput.txt"
+
+# Get tool from argument
+[[ "$#" == 1 ]] || {
+  echo "Expecting 1 positional argument, verible-verilog-format path."
+  exit 1
+}
+formatter="$(rlocation ${TEST_WORKSPACE}/${1})"
+
+cat >${MY_INPUT_FILE} <<EOF
+  module    m   ;endmodule
+EOF
+
+# Run formatter.
+${formatter} --verbose --verify ${MY_INPUT_FILE}
+if [ "$?" -eq 0 ]; then
+    echo "Changes should produce non-zero error code"
+    echo "FAIL"
+    exit 1
+fi
+
+echo "PASS"
diff --git a/verilog/tools/formatter/verilog_format.cc b/verilog/tools/formatter/verilog_format.cc
index fd6c514..170cdf7 100644
--- a/verilog/tools/formatter/verilog_format.cc
+++ b/verilog/tools/formatter/verilog_format.cc
@@ -85,6 +85,11 @@
 // TODO(fangism): Provide -i alias, as it is canonical to many formatters
 ABSL_FLAG(bool, inplace, false,
           "If true, overwrite the input file on successful conditions.");
+ABSL_FLAG(
+    bool, verify, false,
+    "If true, only checks if formatting would be done. Return code 0 means "
+    "no files would change. Return code 1 means some files would "
+    "be reformatted.");
 ABSL_FLAG(std::string, stdin_name, "<stdin>",
           "When using '-' to read from stdin, this gives an alternate name for "
           "diagnostic purposes.  Otherwise this is ignored.");
@@ -125,11 +130,15 @@
   return std::cerr;
 }
 
+// TODO: Refactor and simplify
 static bool formatOneFile(absl::string_view filename,
-                          const LineNumberSet& lines_to_format) {
+                          const LineNumberSet& lines_to_format,
+                          bool* any_changes) {
   const bool inplace = absl::GetFlag(FLAGS_inplace);
+  const bool check_changes_only = absl::GetFlag(FLAGS_verify);
   const bool is_stdin = filename == "-";
   const auto& stdin_name = absl::GetFlag(FLAGS_stdin_name);
+  *any_changes = false;
 
   if (inplace && is_stdin) {
     FileMsg(filename)
@@ -203,21 +212,34 @@
     return absl::GetFlag(FLAGS_failsafe_success);
   }
 
-  // Safe to write out result, having passed above verification.
-  if (inplace && !is_stdin) {
-    // Don't write if the output is exactly as the input, so that we don't mess
-    // with tools that look for timestamp changes (such as make).
-    if (*content_or != formatted_output) {
-      if (auto status = verible::file::SetContents(filename, formatted_output);
-          !status.ok()) {
-        FileMsg(filename) << "error writing result " << status << std::endl;
-        return false;
-      }
+  // Check if the output is the same as the input.
+  *any_changes = (*content_or != formatted_output);
+
+  // Don't output or write if --check is set.
+  if (check_changes_only) {
+    if (*any_changes) {
+      FileMsg(filename) << "Needs formatting." << std::endl;
     } else if (absl::GetFlag(FLAGS_verbose)) {
       FileMsg(filename) << "Already formatted, no change." << std::endl;
     }
   } else {
-    std::cout << formatted_output;
+    // Safe to write out result, having passed above verification.
+    if (inplace && !is_stdin) {
+      // Don't write if the output is exactly as the input, so that we don't
+      // mess with tools that look for timestamp changes (such as make).
+      if (*any_changes) {
+        if (auto status =
+                verible::file::SetContents(filename, formatted_output);
+            !status.ok()) {
+          FileMsg(filename) << "error writing result " << status << std::endl;
+          return false;
+        }
+      } else if (absl::GetFlag(FLAGS_verbose)) {
+        FileMsg(filename) << "Already formatted, no change." << std::endl;
+      }
+    } else {
+      std::cout << formatted_output;
+    }
   }
 
   return true;
@@ -260,11 +282,19 @@
   }
 
   bool all_success = true;
+  bool any_changes = false;
   // All positional arguments are file names.  Exclude program name.
   for (const absl::string_view filename :
        verible::make_range(file_args.begin() + 1, file_args.end())) {
-    all_success &= formatOneFile(filename, lines_to_format);
+    all_success &= formatOneFile(filename, lines_to_format, &any_changes);
   }
 
-  return all_success ? 0 : 1;
+  int ret_val = 0;
+  if (absl::GetFlag(FLAGS_verify)) {
+    ret_val = any_changes;
+  } else {
+    ret_val = all_success ? 0 : 1;
+  }
+
+  return ret_val;
 }