diff --git a/common/util/file_util.cc b/common/util/file_util.cc index ddc4d2e07..69ff0e8f9 100644 --- a/common/util/file_util.cc +++ b/common/util/file_util.cc @@ -79,14 +79,14 @@ static absl::Status CreateErrorStatusFromSysError(absl::string_view filename, const char *fallback_msg) { const char *const system_msg = sys_error == 0 ? fallback_msg : strerror(sys_error); - const std::string msg = filename.empty() - ? std::string{system_msg} - : absl::StrCat(filename, ": ", system_msg); + if (filename.empty()) filename = ""; + std::string msg = absl::StrCat(filename, ": ", system_msg); switch (sys_error) { case EPERM: case EACCES: return {absl::StatusCode::kPermissionDenied, msg}; case ENOENT: + case ESRCH: // Win32 returns this for fs::status() on non-existing file. return {absl::StatusCode::kNotFound, msg}; case EEXIST: return {absl::StatusCode::kAlreadyExists, msg}; @@ -94,6 +94,7 @@ static absl::Status CreateErrorStatusFromSysError(absl::string_view filename, case EISDIR: return {absl::StatusCode::kInvalidArgument, msg}; default: + absl::StrAppend(&msg, " (sys_error=", sys_error, ")"); return {absl::StatusCode::kUnknown, msg}; } } @@ -127,15 +128,17 @@ absl::Status UpwardFileSearch(absl::string_view start, if (one_up == probe_dir) break; probe_dir = one_up; } - return absl::NotFoundError("No matching file found."); + return absl::NotFoundError(absl::StrCat("UpwardFileSearch: starting from '", + start, "', no file '", filename, + "' found'")); } absl::Status FileExists(const std::string &filename) { std::error_code err; - fs::file_status stat = fs::status(filename, err); + const fs::file_status stat = fs::status(filename, err); if (err.value() != 0) { - return absl::NotFoundError(absl::StrCat(filename, ": ", err.message())); + return CreateErrorStatusFromErr(filename, err, "file exists check"); } if (fs::is_regular_file(stat) || fs::is_fifo(stat)) { diff --git a/common/util/file_util_test.cc b/common/util/file_util_test.cc index 52fd4c21a..4eaa55490 100644 --- a/common/util/file_util_test.cc +++ b/common/util/file_util_test.cc @@ -248,6 +248,12 @@ TEST(FileUtil, ScopedTestFileEmplace) { } } +TEST(FileUtil, FileNotExistsTests) { + absl::Status s = file::FileExists("not/an/existing/file"); + EXPECT_FALSE(s.ok()); + EXPECT_EQ(s.code(), absl::StatusCode::kNotFound); +} + TEST(FileUtil, FileExistsDirectoryErrorMessage) { absl::Status s; s = file::FileExists(testing::TempDir()); diff --git a/verilog/analysis/symbol_table_test.cc b/verilog/analysis/symbol_table_test.cc index 327c6e607..2f2716b44 100644 --- a/verilog/analysis/symbol_table_test.cc +++ b/verilog/analysis/symbol_table_test.cc @@ -8252,7 +8252,8 @@ TEST(BuildSymbolTableTest, ModuleInstancesFromProjectMissingFile) { symbol_table.BuildSingleTranslationUnit("file/not/found.txt", &build_diagnostics); ASSERT_FALSE(build_diagnostics.empty()); - EXPECT_THAT(build_diagnostics.front().code(), absl::StatusCode::kNotFound); + EXPECT_THAT(build_diagnostics.front().code(), absl::StatusCode::kNotFound) + << build_diagnostics.front(); } TEST(BuildSymbolTableTest, ModuleInstancesFromProjectFilesGood) { diff --git a/verilog/analysis/verilog_project.cc b/verilog/analysis/verilog_project.cc index 1ae3aa4cf..9c0616929 100644 --- a/verilog/analysis/verilog_project.cc +++ b/verilog/analysis/verilog_project.cc @@ -22,6 +22,7 @@ #include #include "absl/status/status.h" +#include "absl/status/statusor.h" #include "absl/strings/str_cat.h" #include "absl/strings/str_join.h" #include "absl/strings/string_view.h" @@ -301,14 +302,14 @@ absl::StatusOr VerilogProject::OpenTranslationUnit( absl::Status VerilogProject::IncludeFileNotFoundError( absl::string_view referenced_filename) const { - return absl::NotFoundError(absl::StrCat( - "Unable to find '", referenced_filename, - "' among the included paths: ", absl::StrJoin(include_paths_, ", "))); + return absl::NotFoundError( + absl::StrCat("'", referenced_filename, "' not in any of the ", + include_paths_.size(), " include paths")); } absl::StatusOr VerilogProject::OpenIncludedFile( absl::string_view referenced_filename) { - VLOG(1) << __FUNCTION__ << ", referenced: " << referenced_filename; + VLOG(2) << __FUNCTION__ << ", referenced: " << referenced_filename; // Check for a pre-existing entry to avoid duplicate files. { const auto opened_file = FindOpenedFile(referenced_filename); @@ -329,18 +330,16 @@ absl::StatusOr VerilogProject::OpenIncludedFile( // Locate the file among the base paths. for (const auto &include_path : include_paths_) { - const std::string resolved_filename = + const std::string resolved = verible::file::JoinPath(include_path, referenced_filename); - if (verible::file::FileExists(resolved_filename).ok()) { - VLOG(2) << "File '" << resolved_filename << "' exists. Resolved from '" - << referenced_filename << "'"; - return OpenFile(referenced_filename, resolved_filename, Corpus()); + if (verible::file::FileExists(resolved).ok()) { + VLOG(2) << referenced_filename << " in incdir '" << resolved << "'"; + return OpenFile(referenced_filename, resolved, Corpus()); } - VLOG(2) << "Checked for file'" << resolved_filename - << "', but not found. Resolved from '" << referenced_filename - << "'"; + VLOG(2) << referenced_filename << " not in incdir '" << resolved << "'"; } + VLOG(1) << __FUNCTION__ << "': '" << referenced_filename << "' not found"; // Not found in any path. Cache this status. const auto inserted = files_.emplace( referenced_filename, diff --git a/verilog/preprocessor/verilog_preprocess_test.cc b/verilog/preprocessor/verilog_preprocess_test.cc index e97d1059e..1893992b8 100644 --- a/verilog/preprocessor/verilog_preprocess_test.cc +++ b/verilog/preprocessor/verilog_preprocess_test.cc @@ -20,6 +20,7 @@ #include #include "absl/status/status.h" +#include "absl/strings/match.h" #include "absl/strings/string_view.h" #include "common/text/macro_definition.h" #include "common/text/token_info.h" @@ -65,22 +66,22 @@ class LexerTester { class PreprocessorTester { public: PreprocessorTester(absl::string_view text, - const VerilogPreprocess::Config& config) + const VerilogPreprocess::Config &config) : analyzer_(text, "<>", config), status_(analyzer_.Analyze()) {} explicit PreprocessorTester(absl::string_view text) : PreprocessorTester(text, VerilogPreprocess::Config()) {} - const VerilogPreprocessData& PreprocessorData() const { + const VerilogPreprocessData &PreprocessorData() const { return analyzer_.PreprocessorData(); } - const verible::TextStructureView& Data() const { return analyzer_.Data(); } + const verible::TextStructureView &Data() const { return analyzer_.Data(); } - const absl::Status& Status() const { return status_; } + const absl::Status &Status() const { return status_; } - const VerilogAnalyzer& Analyzer() const { return analyzer_; } + const VerilogAnalyzer &Analyzer() const { return analyzer_; } private: VerilogAnalyzer analyzer_; @@ -118,12 +119,12 @@ TEST(VerilogPreprocessTest, InvalidPreprocessorInputs) { {"`define FOO(aaa = 9, bbb =\n", 27}, // unterminated parameter list {"`define FOO(aa = 9, bb = 2\n", 27}, // expecting ',' or ')' }; - for (const auto& test_case : test_cases) { + for (const auto &test_case : test_cases) { PreprocessorTester tester(test_case.input); EXPECT_FALSE(tester.Status().ok()) << "Expected preprocess to fail on invalid input: \"" << test_case.input << "\""; - const auto& rejected_tokens = tester.Analyzer().GetRejectedTokens(); + const auto &rejected_tokens = tester.Analyzer().GetRejectedTokens(); ASSERT_FALSE(rejected_tokens.empty()) << "on invalid input: \"" << test_case.input << "\""; const int rejected_token_offset = @@ -148,11 +149,11 @@ TEST(VerilogPreprocessTest, WorksWithoutDefinitions) { "module foo;\nendmodule\n", "module foo(input x, output y);\nendmodule\n", }; - for (const auto& test_case : test_cases) { + for (const auto &test_case : test_cases) { PreprocessorTester tester(test_case); EXPECT_PARSE_OK(); - const auto& definitions = tester.PreprocessorData().macro_definitions; + const auto &definitions = tester.PreprocessorData().macro_definitions; EXPECT_TRUE(definitions.empty()); } } @@ -166,11 +167,11 @@ TEST(VerilogPreprocessTest, OneMacroDefinitionNoParamsNoValue) { "`define FOOOO\n" "module foo;\nendmodule\n", }; - for (const auto& test_case : test_cases) { + for (const auto &test_case : test_cases) { PreprocessorTester tester(test_case); EXPECT_PARSE_OK(); - const auto& definitions = tester.PreprocessorData().macro_definitions; + const auto &definitions = tester.PreprocessorData().macro_definitions; EXPECT_THAT(definitions, ElementsAre(Pair("FOOOO", testing::_))); auto macro = FindOrNull(definitions, "FOOOO"); ASSERT_NE(macro, nullptr); @@ -186,7 +187,7 @@ TEST(VerilogPreprocessTest, OneMacroDefinitionNoParamsSimpleValue) { "`define FOOOO \"bar\"\n"); EXPECT_PARSE_OK(); - const auto& definitions = tester.PreprocessorData().macro_definitions; + const auto &definitions = tester.PreprocessorData().macro_definitions; EXPECT_THAT(definitions, ElementsAre(Pair("FOOOO", testing::_))); auto macro = FindOrNull(definitions, "FOOOO"); ASSERT_NE(macro, nullptr); @@ -201,15 +202,15 @@ TEST(VerilogPreprocessTest, OneMacroDefinitionOneParamWithValue) { "`define FOOOO(x) (x+1)\n"); EXPECT_PARSE_OK(); - const auto& definitions = tester.PreprocessorData().macro_definitions; + const auto &definitions = tester.PreprocessorData().macro_definitions; EXPECT_THAT(definitions, ElementsAre(Pair("FOOOO", testing::_))); auto macro = FindOrNull(definitions, "FOOOO"); ASSERT_NE(macro, nullptr); EXPECT_EQ(macro->DefinitionText().text(), "(x+1)"); EXPECT_TRUE(macro->IsCallable()); - const auto& params = macro->Parameters(); + const auto ¶ms = macro->Parameters(); EXPECT_EQ(params.size(), 1); - const auto& param(params[0]); + const auto ¶m(params[0]); EXPECT_EQ(param.name.text(), "x"); EXPECT_FALSE(param.HasDefaultText()); } @@ -220,15 +221,15 @@ TEST(VerilogPreprocessTest, OneMacroDefinitionOneParamDefaultWithValue) { "`define FOOOO(x=22) (x+3)\n"); EXPECT_PARSE_OK(); - const auto& definitions = tester.PreprocessorData().macro_definitions; + const auto &definitions = tester.PreprocessorData().macro_definitions; EXPECT_THAT(definitions, ElementsAre(Pair("FOOOO", testing::_))); auto macro = FindOrNull(definitions, "FOOOO"); ASSERT_NE(macro, nullptr); EXPECT_EQ(macro->DefinitionText().text(), "(x+3)"); EXPECT_TRUE(macro->IsCallable()); - const auto& params = macro->Parameters(); + const auto ¶ms = macro->Parameters(); EXPECT_EQ(params.size(), 1); - const auto& param(params[0]); + const auto ¶m(params[0]); EXPECT_EQ(param.name.text(), "x"); EXPECT_TRUE(param.HasDefaultText()); EXPECT_EQ(param.default_value.text(), "22"); @@ -240,21 +241,21 @@ TEST(VerilogPreprocessTest, TwoMacroDefinitions) { "`define FOOOO(x=22) (x+3)\n"); EXPECT_PARSE_OK(); - const auto& definitions = tester.PreprocessorData().macro_definitions; + const auto &definitions = tester.PreprocessorData().macro_definitions; EXPECT_THAT(definitions, ElementsAre(Pair("BAAAAR", testing::_), Pair("FOOOO", testing::_))); { auto macro = FindOrNull(definitions, "BAAAAR"); ASSERT_NE(macro, nullptr); EXPECT_TRUE(macro->IsCallable()); - const auto& params = macro->Parameters(); + const auto ¶ms = macro->Parameters(); EXPECT_EQ(params.size(), 2); } { auto macro = FindOrNull(definitions, "FOOOO"); ASSERT_NE(macro, nullptr); EXPECT_TRUE(macro->IsCallable()); - const auto& params = macro->Parameters(); + const auto ¶ms = macro->Parameters(); EXPECT_EQ(params.size(), 1); } } @@ -265,7 +266,7 @@ TEST(VerilogPreprocessTest, UndefMacro) { "`undef FOO"); EXPECT_PARSE_OK(); - const auto& definitions = tester.PreprocessorData().macro_definitions; + const auto &definitions = tester.PreprocessorData().macro_definitions; EXPECT_EQ(definitions.size(), 0); } @@ -273,7 +274,7 @@ TEST(VerilogPreprocessTest, UndefNonexistentMacro) { PreprocessorTester tester("`undef FOO"); EXPECT_PARSE_OK(); - const auto& definitions = tester.PreprocessorData().macro_definitions; + const auto &definitions = tester.PreprocessorData().macro_definitions; EXPECT_EQ(definitions.size(), 0); EXPECT_TRUE(tester.PreprocessorData().warnings.empty()); // not a problem. } @@ -284,10 +285,10 @@ TEST(VerilogPreprocessTest, RedefineMacroWarning) { "`define FOO 2\n"); EXPECT_PARSE_OK(); - const auto& definitions = tester.PreprocessorData().macro_definitions; + const auto &definitions = tester.PreprocessorData().macro_definitions; EXPECT_EQ(definitions.size(), 1); - const auto& warnings = tester.PreprocessorData().warnings; + const auto &warnings = tester.PreprocessorData().warnings; EXPECT_EQ(warnings.size(), 1); EXPECT_EQ(warnings.front().error_message, "Re-defining macro"); } @@ -302,11 +303,11 @@ TEST(VerilogPreprocessTest, DefaultPreprocessorKeepsDefineInStream) { "module x(); endmodule\n"); EXPECT_PARSE_OK(); - const auto& definitions = tester.PreprocessorData().macro_definitions; + const auto &definitions = tester.PreprocessorData().macro_definitions; EXPECT_EQ(definitions.size(), 2); // The original `define tokens are still in the stream - const auto& token_stream = tester.Data().GetTokenStreamView(); + const auto &token_stream = tester.Data().GetTokenStreamView(); const int count_defines = std::count_if(token_stream.begin(), token_stream.end(), [](verible::TokenSequence::const_iterator t) { @@ -336,13 +337,13 @@ TEST(VerilogPreprocessTest, IncompleteOrUnbalancedIfdef) { {"`ifdef FOO\n`elsif BAR\n", 11, "Unterminated preprocessing"}, {"`ifdef FOO\n`elsif BAR\n`else\n", 22, "Unterminated preprocessing"}, }; - for (const BranchFailTest& test : test_cases) { + for (const BranchFailTest &test : test_cases) { PreprocessorTester tester( test.input, VerilogPreprocess::Config({.filter_branches = true})); EXPECT_FALSE(tester.Status().ok()); ASSERT_GE(tester.PreprocessorData().errors.size(), 1); - const auto& error = tester.PreprocessorData().errors.front(); + const auto &error = tester.PreprocessorData().errors.front(); EXPECT_THAT(error.error_message, StartsWith(test.expected_error)); const int error_token_offset = error.token_info.left(tester.Analyzer().Data().Contents()); @@ -562,7 +563,7 @@ module baz(); endmodule module baz(); endmodule )"}}; - for (const RawAndFiltered& test : test_cases) { + for (const RawAndFiltered &test : test_cases) { PreprocessorTester with_filter( test.pp_input, VerilogPreprocess::Config({.filter_branches = true})); EXPECT_TRUE(with_filter.Status().ok()) @@ -572,8 +573,8 @@ module baz(); endmodule EXPECT_TRUE(equivalent.Status().ok()) << equivalent.Status() << " " << test.description; - const auto& filtered_stream = with_filter.Data().GetTokenStreamView(); - const auto& equivalent_stream = equivalent.Data().GetTokenStreamView(); + const auto &filtered_stream = with_filter.Data().GetTokenStreamView(); + const auto &equivalent_stream = equivalent.Data().GetTokenStreamView(); EXPECT_GT(filtered_stream.size(), 0) << test.description; EXPECT_EQ(filtered_stream.size(), equivalent_stream.size()) << test.description; @@ -787,7 +788,7 @@ endmodule }; - for (const auto& test_case : test_cases) { + for (const auto &test_case : test_cases) { PreprocessorTester expanded( test_case.pp_input, VerilogPreprocess::Config({.expand_macros = true})); EXPECT_TRUE(expanded.Status().ok()) @@ -797,8 +798,8 @@ endmodule VerilogPreprocess::Config({.expand_macros = false})); EXPECT_TRUE(equivalent.Status().ok()) << equivalent.Status() << " " << test_case.description; - const auto& expanded_stream = expanded.Data().GetTokenStreamView(); - const auto& equivalent_stream = equivalent.Data().GetTokenStreamView(); + const auto &expanded_stream = expanded.Data().GetTokenStreamView(); + const auto &equivalent_stream = equivalent.Data().GetTokenStreamView(); EXPECT_GT(expanded_stream.size(), 0) << test_case.description; EXPECT_EQ(expanded_stream.size(), equivalent_stream.size()) << test_case.description; @@ -838,7 +839,7 @@ TEST(VerilogPreprocessTest, SetExternalDefines) { preprocessor.setPreprocessingInfo(preprocessing_info); - const auto& pp_data = preprocessor.ScanStream(test_case_stream_view); + const auto &pp_data = preprocessor.ScanStream(test_case_stream_view); EXPECT_THAT(pp_data.preprocessed_token_stream[0]->text(), "VALUE1"); EXPECT_THAT(pp_data.preprocessed_token_stream[1]->text(), "VALUE2"); @@ -868,17 +869,17 @@ TEST(VerilogPreprocessTest, ExternalDefinesWithUndef) { preprocessor.setPreprocessingInfo(preprocessing_info); - const auto& pp_data = preprocessor.ScanStream(test_case_stream_view); + const auto &pp_data = preprocessor.ScanStream(test_case_stream_view); - const auto& errors = pp_data.errors; + const auto &errors = pp_data.errors; EXPECT_THAT(errors.size(), 1); EXPECT_THAT(errors.front().error_message, StartsWith("Error expanding macro identifier")); } -static void IncludeFileTestWithIncludeBracket(const char* start_inc, - const char* end_inc) { +static void IncludeFileTestWithIncludeBracket(const char *start_inc, + const char *end_inc) { const auto tempdir = testing::TempDir(); const std::string includes_dir = JoinPath(tempdir, "includes"); constexpr absl::string_view included_content( @@ -906,16 +907,16 @@ static void IncludeFileTestWithIncludeBracket(const char* start_inc, LexerTester src_lexer(src_content); LexerTester equivalent_lexer(equivalent_content); - const auto& tester_pp_data = + const auto &tester_pp_data = tester.ScanStream(src_lexer.GetTokenStreamView()); - const auto& equivalent_pp_data = + const auto &equivalent_pp_data = equivalent.ScanStream(equivalent_lexer.GetTokenStreamView()); EXPECT_TRUE(tester_pp_data.errors.empty()); EXPECT_TRUE(equivalent_pp_data.errors.empty()); - const auto& tester_stream = tester_pp_data.preprocessed_token_stream; - const auto& equivalent_stream = equivalent_pp_data.preprocessed_token_stream; + const auto &tester_stream = tester_pp_data.preprocessed_token_stream; + const auto &equivalent_stream = equivalent_pp_data.preprocessed_token_stream; EXPECT_FALSE(tester_stream.empty()); EXPECT_EQ(tester_stream.size(), equivalent_stream.size()); @@ -970,16 +971,16 @@ TEST(VerilogPreprocessTest, IncludingFileWithRelativePath) { LexerTester src_lexer(src_content); LexerTester equivalent_lexer(equivalent_content); - const auto& tester_pp_data = + const auto &tester_pp_data = tester.ScanStream(src_lexer.GetTokenStreamView()); - const auto& equivalent_pp_data = + const auto &equivalent_pp_data = equivalent.ScanStream(equivalent_lexer.GetTokenStreamView()); EXPECT_TRUE(tester_pp_data.errors.empty()); EXPECT_TRUE(equivalent_pp_data.errors.empty()); - const auto& tester_stream = tester_pp_data.preprocessed_token_stream; - const auto& equivalent_stream = equivalent_pp_data.preprocessed_token_stream; + const auto &tester_stream = tester_pp_data.preprocessed_token_stream; + const auto &equivalent_stream = equivalent_pp_data.preprocessed_token_stream; EXPECT_FALSE(tester_stream.empty()); EXPECT_EQ(tester_stream.size(), equivalent_stream.size()); @@ -1022,12 +1023,13 @@ TEST(VerilogPreprocessTest, file_opener); LexerTester src_lexer(src_content); - const auto& tester_pp_data = + const auto &tester_pp_data = tester.ScanStream(src_lexer.GetTokenStreamView()); EXPECT_EQ(tester_pp_data.errors.size(), 1); - const auto& error = tester_pp_data.errors.front(); - EXPECT_THAT(error.error_message, StartsWith("Unable to find")); + const auto &error = tester_pp_data.errors.front(); + EXPECT_TRUE(absl::StrContains(error.error_message, "not in any of")) + << error.error_message; } } // namespace diff --git a/verilog/tools/ls/BUILD b/verilog/tools/ls/BUILD index 9498fd529..55df570ca 100644 --- a/verilog/tools/ls/BUILD +++ b/verilog/tools/ls/BUILD @@ -58,6 +58,7 @@ cc_test( deps = [ ":lsp-parse-buffer", "//common/lsp:lsp-text-buffer", + "//common/text:text-structure", "@com_google_absl//absl/strings", "@com_google_googletest//:gtest_main", ], @@ -125,12 +126,15 @@ cc_library( "//common/lsp:lsp-protocol", "//common/strings:line-column-map", "//common/util:file-util", + "//common/util:iterator-adaptors", "//common/util:range", "//verilog/analysis:symbol-table", "//verilog/analysis:verilog-filelist", "//verilog/analysis:verilog-project", "@com_google_absl//absl/container:flat_hash_set", + "@com_google_absl//absl/container:flat_hash_map", "@com_google_absl//absl/flags:flag", + "@com_google_absl//absl/status", "@com_google_absl//absl/strings", "@com_google_absl//absl/time", "@com_google_absl//absl/types:optional", diff --git a/verilog/tools/ls/lsp-parse-buffer_test.cc b/verilog/tools/ls/lsp-parse-buffer_test.cc index 2bb3dc15a..743fe6835 100644 --- a/verilog/tools/ls/lsp-parse-buffer_test.cc +++ b/verilog/tools/ls/lsp-parse-buffer_test.cc @@ -14,7 +14,9 @@ #include "verilog/tools/ls/lsp-parse-buffer.h" +#include "absl/strings/match.h" #include "common/lsp/lsp-text-buffer.h" +#include "common/text/text_structure.h" #include "gtest/gtest.h" namespace verilog { diff --git a/verilog/tools/ls/symbol-table-handler.cc b/verilog/tools/ls/symbol-table-handler.cc index ca6eb83a3..2706926b2 100644 --- a/verilog/tools/ls/symbol-table-handler.cc +++ b/verilog/tools/ls/symbol-table-handler.cc @@ -21,12 +21,14 @@ #include #include +#include "absl/container/flat_hash_map.h" #include "absl/flags/flag.h" #include "absl/time/clock.h" #include "absl/time/time.h" #include "common/lsp/lsp-file-utils.h" #include "common/strings/line_column_map.h" #include "common/util/file_util.h" +#include "common/util/iterator_adaptors.h" #include "common/util/range.h" #include "verilog/analysis/verilog_filelist.h" #include "verilog/tools/ls/lsp-conversion.h" @@ -42,16 +44,31 @@ namespace verilog { // If vlog(2), output all non-ok messages, with vlog(1) just the first few, // else: none static void LogFullIfVLog(const std::vector &statuses) { - if (VLOG_IS_ON(1)) { - int report_count = 0; - for (const auto &s : statuses) { - if (s.ok()) continue; + if (!VLOG_IS_ON(1)) return; + + constexpr int kMaxEmitNoisyMessagesDirectly = 5; + int report_count = 0; + absl::flat_hash_map status_counts; + for (const auto &s : statuses) { + if (s.ok()) continue; + if (++report_count <= kMaxEmitNoisyMessagesDirectly || VLOG_IS_ON(2)) { LOG(INFO) << s; - if (++report_count > 5 && !VLOG_IS_ON(2)) { - LOG(WARNING) << "skipped remaining messages; switch VLOG(2) on for " - << statuses.size() << " statuses"; - break; // only more noisy on request. - } + } else { + const std::string partial_msg(s.ToString().substr(0, 25)); + ++status_counts[partial_msg]; + } + } + + if (!status_counts.empty()) { + LOG(WARNING) << "skipped remaining; switch VLOG(2) on for all " + << statuses.size() << " statuses."; + LOG(INFO) << "Here a summary"; + std::map sort_by_count; + for (const auto &stat : status_counts) { + sort_by_count.emplace(stat.second, stat.first); + } + for (const auto &stat : verible::reversed_view(sort_by_count)) { + LOG(INFO) << absl::StrFormat("%6d x %s...", stat.first, stat.second); } } } diff --git a/verilog/tools/ls/symbol-table-handler.h b/verilog/tools/ls/symbol-table-handler.h index c7f205e43..89318e76e 100644 --- a/verilog/tools/ls/symbol-table-handler.h +++ b/verilog/tools/ls/symbol-table-handler.h @@ -23,6 +23,7 @@ #include #include "absl/container/flat_hash_set.h" +#include "absl/status/status.h" #include "absl/strings/string_view.h" #include "absl/types/optional.h" #include "common/lsp/lsp-protocol.h"