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

Added --verify option to verilog-formatter.
diff --git a/common/analysis/linter_test_utils.h b/common/analysis/linter_test_utils.h
index 7c7a0b0..88b401f 100644
--- a/common/analysis/linter_test_utils.h
+++ b/common/analysis/linter_test_utils.h
@@ -1,4 +1,4 @@
-// Copyright 2017-2020 The Verible Authors.
+// Copyright 2017-2023 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.
@@ -143,7 +143,7 @@
 
 template <class AnalyzerType, class RuleClass>
 void RunApplyFixCases(std::initializer_list<AutoFixInOut> tests,
-                      absl::string_view configuration) {
+                      absl::string_view configuration = "") {
   using rule_type = typename RuleClass::rule_type;
   auto rule_generator = [&configuration]() -> std::unique_ptr<rule_type> {
     std::unique_ptr<rule_type> instance(new RuleClass());
diff --git a/verilog/analysis/checkers/BUILD b/verilog/analysis/checkers/BUILD
index 1b06db6..f61323d 100644
--- a/verilog/analysis/checkers/BUILD
+++ b/verilog/analysis/checkers/BUILD
@@ -62,6 +62,7 @@
         ":signal-name-style-rule",
         ":struct-union-name-style-rule",
         ":suggest-parentheses-rule",
+        ":suspicious-semicolon-rule",
         ":token-stream-lint-rule",
         ":truncated-numeric-literal-rule",
         ":undersized-binary-literal-rule",
@@ -2123,3 +2124,28 @@
         "@com_google_googletest//:gtest_main",
     ],
 )
+
+cc_library(
+    name = "suspicious-semicolon-rule",
+    srcs = ["suspicious_semicolon_rule.cc"],
+    hdrs = ["suspicious_semicolon_rule.h"],
+    deps = [
+         "//common/analysis:lint-rule-status",
+         "//common/analysis:syntax-tree-lint-rule",
+         "//verilog/CST:verilog-matchers",
+         "//verilog/analysis:descriptions",
+         "//verilog/analysis:lint-rule-registry",
+    ],
+    alwayslink = 1,
+)
+
+cc_test(
+  name = "suspicious-semicolon-rule_test",
+  srcs = ["suspicious_semicolon_rule_test.cc"],
+  deps = [
+      ":suspicious-semicolon-rule",
+      "//common/analysis:syntax-tree-linter-test-utils",
+      "//verilog/analysis:verilog-analyzer",
+      "@com_google_googletest//:gtest_main",
+  ],
+)
diff --git a/verilog/analysis/checkers/suspicious_semicolon_rule.cc b/verilog/analysis/checkers/suspicious_semicolon_rule.cc
new file mode 100644
index 0000000..043c21a
--- /dev/null
+++ b/verilog/analysis/checkers/suspicious_semicolon_rule.cc
@@ -0,0 +1,81 @@
+// Copyright 2017-2023 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.
+
+#include "verilog/analysis/checkers/suspicious_semicolon_rule.h"
+
+#include "common/analysis/matcher/matcher.h"
+#include "verilog/CST/verilog_matchers.h"
+#include "verilog/CST/verilog_nonterminals.h"
+#include "verilog/analysis/lint_rule_registry.h"
+
+namespace verilog {
+namespace analysis {
+
+using verible::matcher::Matcher;
+
+VERILOG_REGISTER_LINT_RULE(SuspiciousSemicolon);
+
+static constexpr absl::string_view kMessage =
+    "Potentially unintended semicolon";
+
+const LintRuleDescriptor &SuspiciousSemicolon::GetDescriptor() {
+  static const LintRuleDescriptor d{
+      .name = "suspicious-semicolon",
+      .topic = "bugprone",
+      .desc =
+          "Checks that there are no suspicious semicolons that might affect "
+          "code behaviour but escape quick visual inspection"};
+  return d;
+}
+
+static const Matcher &NullStatementMatcher() {
+  static const Matcher matcher(NodekNullStatement());
+  return matcher;
+}
+
+void SuspiciousSemicolon::HandleNode(
+    const verible::SyntaxTreeNode &node,
+    const verible::SyntaxTreeContext &context) {
+  verible::matcher::BoundSymbolManager manager;
+  if (!NullStatementMatcher().Matches(node, &manager)) return;
+
+  // Waive @(posedge clk);
+  // But catch always_ff @(posedge clk);
+  const bool parent_is_proc_timing_ctrl_statement =
+      context.DirectParentIs(NodeEnum::kProceduralTimingControlStatement);
+  if (!context.IsInside(NodeEnum::kAlwaysStatement) &&
+      parent_is_proc_timing_ctrl_statement) {
+    return;
+  }
+
+  if (!parent_is_proc_timing_ctrl_statement &&
+      !context.DirectParentIsOneOf(
+          {NodeEnum::kForeachLoopStatement, NodeEnum::kWhileLoopStatement,
+           NodeEnum::kForLoopStatement, NodeEnum::kForeverLoopStatement,
+           NodeEnum::kIfBody, NodeEnum::kElseBody})) {
+    return;
+  }
+
+  violations_.insert(verible::LintViolation(
+      node, kMessage, context,
+      {verible::AutoFix("Remove ';'",
+                        {verible::StringSpanOfSymbol(node), ""})}));
+}
+
+verible::LintRuleStatus SuspiciousSemicolon::Report() const {
+  return verible::LintRuleStatus(violations_, GetDescriptor());
+}
+
+}  // namespace analysis
+}  // namespace verilog
diff --git a/verilog/analysis/checkers/suspicious_semicolon_rule.h b/verilog/analysis/checkers/suspicious_semicolon_rule.h
new file mode 100644
index 0000000..a03a407
--- /dev/null
+++ b/verilog/analysis/checkers/suspicious_semicolon_rule.h
@@ -0,0 +1,62 @@
+// Copyright 2017-2023 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.
+
+#ifndef VERIBLE_VERILOG_ANALYSIS_CHECKERS_SUSPICIOUS_SEMICOLON_H_
+#define VERIBLE_VERILOG_ANALYSIS_CHECKERS_SUSPICIOUS_SEMICOLON_H_
+
+#include "common/analysis/lint_rule_status.h"
+#include "common/analysis/syntax_tree_lint_rule.h"
+#include "verilog/analysis/descriptions.h"
+
+namespace verilog {
+namespace analysis {
+
+/*
+ * Detect suspicious semicolons. Inspired by clang-tidy's
+ * bugprone-suspicious-semicolon check.
+ *
+ * This rule detects extra semicolons that modify code behaviour
+ * while having a good chance to escape quick visual inspection.
+ *
+ * A couple of examples:
+ *
+ * if (condition);
+ *   `uvm_fatal(...);
+ *
+ * while (condition); begin
+ *   doSomething();
+ * end
+ *
+ * Reference:
+ * https://clang.llvm.org/extra/clang-tidy/checks/bugprone/suspicious-semicolon.html#bugprone-suspicious-semicolon
+ */
+class SuspiciousSemicolon : public verible::SyntaxTreeLintRule {
+ public:
+  using rule_type = verible::SyntaxTreeLintRule;
+
+  static const LintRuleDescriptor &GetDescriptor();
+
+  void HandleNode(const verible::SyntaxTreeNode &node,
+                  const verible::SyntaxTreeContext &context) final;
+
+  verible::LintRuleStatus Report() const final;
+
+ private:
+  std::set<verible::LintViolation> violations_;
+};
+
+}  // namespace analysis
+}  // namespace verilog
+
+#endif  // VERIBLE_VERILOG_ANALYSIS_CHECKERS_SUSPICIOUS_SEMICOLON_H_
diff --git a/verilog/analysis/checkers/suspicious_semicolon_rule_test.cc b/verilog/analysis/checkers/suspicious_semicolon_rule_test.cc
new file mode 100644
index 0000000..3e422f5
--- /dev/null
+++ b/verilog/analysis/checkers/suspicious_semicolon_rule_test.cc
@@ -0,0 +1,83 @@
+// Copyright 2017-2023 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.
+
+#include "verilog/analysis/checkers/suspicious_semicolon_rule.h"
+
+#include "common/analysis/syntax_tree_linter_test_utils.h"
+#include "verilog/analysis/verilog_analyzer.h"
+
+namespace verilog {
+namespace analysis {
+namespace {
+
+static constexpr int kToken = ';';
+TEST(SuspiciousSemicolon, DetectSuspiciousSemicolons) {
+  const std::initializer_list<verible::LintTestCase>
+      kSuspiciousSemicolonTestCases = {
+          {"module m; initial begin if(x)", {kToken, ";"}, " end endmodule"},
+          {"module m; initial begin if(x) x; else",
+           {kToken, ";"},
+           " y; end endmodule"},
+          {"module m; initial begin while(x)", {kToken, ";"}, " end endmodule"},
+          {"module m; initial begin forever", {kToken, ";"}, " end endmodule"},
+          {"module m; always_ff @(posedge clk)", {kToken, ";"}, " endmodule"},
+          {"module m; initial begin for(;;)", {kToken, ";"}, " end endmodule"},
+          {"module m; initial begin foreach (array[i])",
+           {kToken, ";"},
+           " end endmodule"},
+      };
+
+  verible::RunLintTestCases<VerilogAnalyzer, SuspiciousSemicolon>(
+      kSuspiciousSemicolonTestCases);
+}
+
+TEST(SuspiciousSemicolon, ShouldNotComplain) {
+  const std::initializer_list<verible::LintTestCase>
+      kSuspiciousSemicolonTestCases = {
+          {""},
+          {"module m; initial begin if(x) begin end end endmodule"},
+          {"module m; @(posedge clk); endmodule"},
+          {"module m; always_ff @(posedge clk) begin ; end endmodule"},
+          {"module m; endmodule;"},
+          {"class c; int x;; endclass"},
+      };
+
+  verible::RunLintTestCases<VerilogAnalyzer, SuspiciousSemicolon>(
+      kSuspiciousSemicolonTestCases);
+}
+
+TEST(SuspiciousSemicolon, ApplyAutoFix) {
+  const std::initializer_list<verible::AutoFixInOut>
+      kSuspiciousSemicolonTestCases = {
+          {"module m; initial begin if(x); end endmodule",
+           "module m; initial begin if(x) end endmodule"},
+          {"module m; initial begin if(x) x; else; y; end endmodule",
+           "module m; initial begin if(x) x; else y; end endmodule"},
+          {"module m; initial begin while(x); end endmodule",
+           "module m; initial begin while(x) end endmodule"},
+          {"module m; initial begin forever; end endmodule",
+           "module m; initial begin forever end endmodule"},
+          {"module m; always_ff @(posedge clk); endmodule",
+           "module m; always_ff @(posedge clk) endmodule"},
+          {"module m; initial begin foreach (array[i]); end endmodule",
+           "module m; initial begin foreach (array[i]) end endmodule"},
+      };
+
+  verible::RunApplyFixCases<VerilogAnalyzer, SuspiciousSemicolon>(
+      kSuspiciousSemicolonTestCases);
+}
+
+}  // namespace
+}  // namespace analysis
+}  // namespace verilog
diff --git a/verilog/tools/kythe/scope_resolver.h b/verilog/tools/kythe/scope_resolver.h
index 4571ca3..5deba61 100644
--- a/verilog/tools/kythe/scope_resolver.h
+++ b/verilog/tools/kythe/scope_resolver.h
@@ -140,8 +140,8 @@
   absl::flat_hash_map<std::string, absl::flat_hash_set<ScopedVname>>
       variable_to_scoped_vname_;
 
-  // Mapping from scope to all its members.
-  absl::flat_hash_map<SignatureDigest, absl::flat_hash_set<VName>>
+  // Mapping from scope to all its members. NOTE: requires pointer stability!
+  absl::node_hash_map<SignatureDigest, absl::flat_hash_set<VName>>
       scope_to_vnames_;
 
   // Maps the scope to the human readable description. Available only when debug
diff --git a/verilog/tools/lint/BUILD b/verilog/tools/lint/BUILD
index 13a4a24..04fd143 100644
--- a/verilog/tools/lint/BUILD
+++ b/verilog/tools/lint/BUILD
@@ -69,6 +69,7 @@
     ("signal-name-style", "signal_name_style", False),
     ("struct-union-name-style", "struct_name_style", True),
     ("struct-union-name-style", "union_name_style", True),
+    ("suspicious-semicolon", "suspicious_semicolon", False),
     ("v2001-generate-begin", "generate_begin_module", True),
     ("void-cast", "void-cast", True),
     ("undersized-binary-literal", "undersized_binary_literal", True),
diff --git a/verilog/tools/lint/testdata/forbid_consecutive_null_statements.sv b/verilog/tools/lint/testdata/forbid_consecutive_null_statements.sv
index 273d124..492a415 100644
--- a/verilog/tools/lint/testdata/forbid_consecutive_null_statements.sv
+++ b/verilog/tools/lint/testdata/forbid_consecutive_null_statements.sv
@@ -1,4 +1,3 @@
 module forbid_consecutive_null_statements;
-  always_ff @(posedge foo)
     ;; // [Style: consecutive-null-statements] [forbid-consecutive-null-statements]
 endmodule
diff --git a/verilog/tools/lint/testdata/suspicious_semicolon.sv b/verilog/tools/lint/testdata/suspicious_semicolon.sv
new file mode 100644
index 0000000..ab49fb6
--- /dev/null
+++ b/verilog/tools/lint/testdata/suspicious_semicolon.sv
@@ -0,0 +1,6 @@
+module suspicious_semicolon ();
+  initial begin
+    if (x);
+      $display("Hi");
+  end
+endmodule
diff --git a/verilog/tools/ls/README.md b/verilog/tools/ls/README.md
index bd626d4..373b6da 100644
--- a/verilog/tools/ls/README.md
+++ b/verilog/tools/ls/README.md
@@ -116,7 +116,7 @@
 Here is a simple setup: put this in your `~/.emacs` file
 and make sure the binary is in your `$PATH` (or use full path).
 
-```lisp
+```elisp
 (require 'lsp-mode)
 (add-to-list 'lsp-language-id-configuration '(verilog-mode . "verilog"))
 (lsp-register-client
@@ -127,6 +127,15 @@
 (add-hook 'verilog-mode-hook 'lsp)
 ```
 
+It is also possible to automatically configure `eglot` and `lsp-mode` using
+the [verilog-ext](https://github.com/gmlarumbe/verilog-ext.git) package:
+```elisp
+(require 'verilog-ext)
+(verilog-ext-mode-setup)
+(verilog-ext-eglot-set-server 've-verible-ls) ;`eglot' config
+(verilog-ext-lsp-set-server 've-verible-ls)   ; `lsp' config
+```
+
 ### Kakoune
 
 First, go to [kak-lsp](https://github.com/kak-lsp/kak-lsp) project and follow the installation and configuration steps.