tint: Move "suggest alternatives" logic to utils
This will be used for suggesting alternative diagnostic rule names in
the Resolver.
Bug: tint:1809
Change-Id: Icc9af02937326f6f774fbaf2aeaa9314c88fdea6
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/117565
Kokoro: Kokoro <noreply+kokoro@google.com>
Reviewed-by: Ben Clayton <bclayton@google.com>
diff --git a/src/tint/reader/wgsl/parser_impl.cc b/src/tint/reader/wgsl/parser_impl.cc
index 37c3ac3..f7aa4b8 100644
--- a/src/tint/reader/wgsl/parser_impl.cc
+++ b/src/tint/reader/wgsl/parser_impl.cc
@@ -1240,40 +1240,15 @@
}
/// Create a sensible error message
- std::stringstream err;
+ std::ostringstream err;
err << "expected " << name;
if (!use.empty()) {
err << " for " << use;
}
+ err << "\n";
- // If the string typed was within kSuggestionDistance of one of the possible enum values,
- // suggest that. Don't bother with suggestions if the string was extremely long.
- constexpr size_t kSuggestionDistance = 5;
- constexpr size_t kSuggestionMaxLength = 64;
- if (auto got = t.to_str(); !got.empty() && got.size() < kSuggestionMaxLength) {
- size_t candidate_dist = kSuggestionDistance;
- const char* candidate = nullptr;
- for (auto* str : strings) {
- auto dist = utils::Distance(str, got);
- if (dist < candidate_dist) {
- candidate = str;
- candidate_dist = dist;
- }
- }
- if (candidate) {
- err << ". Did you mean '" << candidate << "'?";
- }
- }
-
- // List all the possible enumerator values
- err << "\nPossible values: ";
- for (auto* str : strings) {
- if (str != strings[0]) {
- err << ", ";
- }
- err << "'" << str << "'";
- }
+ utils::SuggestAlternatives(t.to_str(), strings, err);
synchronized_ = false;
return add_error(t.source(), err.str());
diff --git a/src/tint/reader/wgsl/parser_impl_enable_directive_test.cc b/src/tint/reader/wgsl/parser_impl_enable_directive_test.cc
index 23a8cb2..260175d 100644
--- a/src/tint/reader/wgsl/parser_impl_enable_directive_test.cc
+++ b/src/tint/reader/wgsl/parser_impl_enable_directive_test.cc
@@ -74,7 +74,8 @@
p->enable_directive();
// Error when unknown extension found
EXPECT_TRUE(p->has_error());
- EXPECT_EQ(p->error(), R"(1:8: expected extension. Did you mean 'f16'?
+ EXPECT_EQ(p->error(), R"(1:8: expected extension
+Did you mean 'f16'?
Possible values: 'chromium_disable_uniformity_analysis', 'chromium_experimental_dp4a', 'chromium_experimental_full_ptr_parameters', 'chromium_experimental_push_constant', 'f16')");
auto program = p->program();
auto& ast = program.AST();
diff --git a/src/tint/reader/wgsl/parser_impl_error_msg_test.cc b/src/tint/reader/wgsl/parser_impl_error_msg_test.cc
index f458834..5337c3c 100644
--- a/src/tint/reader/wgsl/parser_impl_error_msg_test.cc
+++ b/src/tint/reader/wgsl/parser_impl_error_msg_test.cc
@@ -1110,7 +1110,8 @@
TEST_F(ParserImplErrorTest, GlobalDeclVarAttrBuiltinInvalidValue) {
EXPECT("@builtin(frag_d3pth) var i : i32;",
- R"(test.wgsl:1:10 error: expected builtin. Did you mean 'frag_depth'?
+ R"(test.wgsl:1:10 error: expected builtin
+Did you mean 'frag_depth'?
Possible values: 'frag_depth', 'front_facing', 'global_invocation_id', 'instance_index', 'local_invocation_id', 'local_invocation_index', 'num_workgroups', 'position', 'sample_index', 'sample_mask', 'vertex_index', 'workgroup_id'
@builtin(frag_d3pth) var i : i32;
^^^^^^^^^^
diff --git a/src/tint/reader/wgsl/parser_impl_texture_sampler_test.cc b/src/tint/reader/wgsl/parser_impl_texture_sampler_test.cc
index 299bd33..7b051d3 100644
--- a/src/tint/reader/wgsl/parser_impl_texture_sampler_test.cc
+++ b/src/tint/reader/wgsl/parser_impl_texture_sampler_test.cc
@@ -230,7 +230,8 @@
EXPECT_FALSE(t.matched);
EXPECT_TRUE(t.errored);
EXPECT_EQ(p->error(),
- R"(1:20: expected texel format for storage texture type. Did you mean 'rg32float'?
+ R"(1:20: expected texel format for storage texture type
+Did you mean 'rg32float'?
Possible values: 'bgra8unorm', 'r32float', 'r32sint', 'r32uint', 'rg32float', 'rg32sint', 'rg32uint', 'rgba16float', 'rgba16sint', 'rgba16uint', 'rgba32float', 'rgba32sint', 'rgba32uint', 'rgba8sint', 'rgba8snorm', 'rgba8uint', 'rgba8unorm')");
}
@@ -241,7 +242,8 @@
EXPECT_FALSE(t.matched);
EXPECT_TRUE(t.errored);
EXPECT_EQ(p->error(),
- R"(1:30: expected access control for storage texture type. Did you mean 'read'?
+ R"(1:30: expected access control for storage texture type
+Did you mean 'read'?
Possible values: 'read', 'read_write', 'write')");
}
diff --git a/src/tint/reader/wgsl/parser_impl_type_decl_test.cc b/src/tint/reader/wgsl/parser_impl_type_decl_test.cc
index 5e18b00..0a4198e 100644
--- a/src/tint/reader/wgsl/parser_impl_type_decl_test.cc
+++ b/src/tint/reader/wgsl/parser_impl_type_decl_test.cc
@@ -316,7 +316,8 @@
ASSERT_EQ(t.value, nullptr);
ASSERT_TRUE(p->has_error());
ASSERT_EQ(p->error(),
- R"(1:5: expected address space for ptr declaration. Did you mean 'uniform'?
+ R"(1:5: expected address space for ptr declaration
+Did you mean 'uniform'?
Possible values: 'function', 'private', 'push_constant', 'storage', 'uniform', 'workgroup')");
}
diff --git a/src/tint/reader/wgsl/parser_impl_type_decl_without_ident_test.cc b/src/tint/reader/wgsl/parser_impl_type_decl_without_ident_test.cc
index ea146c9..409062a 100644
--- a/src/tint/reader/wgsl/parser_impl_type_decl_without_ident_test.cc
+++ b/src/tint/reader/wgsl/parser_impl_type_decl_without_ident_test.cc
@@ -307,7 +307,8 @@
ASSERT_EQ(t.value, nullptr);
ASSERT_TRUE(p->has_error());
ASSERT_EQ(p->error(),
- R"(1:5: expected address space for ptr declaration. Did you mean 'uniform'?
+ R"(1:5: expected address space for ptr declaration
+Did you mean 'uniform'?
Possible values: 'function', 'private', 'push_constant', 'storage', 'uniform', 'workgroup')");
}
diff --git a/src/tint/reader/wgsl/parser_impl_variable_attribute_list_test.cc b/src/tint/reader/wgsl/parser_impl_variable_attribute_list_test.cc
index 79574a8..9de2a3d 100644
--- a/src/tint/reader/wgsl/parser_impl_variable_attribute_list_test.cc
+++ b/src/tint/reader/wgsl/parser_impl_variable_attribute_list_test.cc
@@ -69,7 +69,8 @@
EXPECT_TRUE(attrs.errored);
EXPECT_FALSE(attrs.matched);
EXPECT_TRUE(attrs.value.IsEmpty());
- EXPECT_EQ(p->error(), R"(1:10: expected builtin. Did you mean 'instance_index'?
+ EXPECT_EQ(p->error(), R"(1:10: expected builtin
+Did you mean 'instance_index'?
Possible values: 'frag_depth', 'front_facing', 'global_invocation_id', 'instance_index', 'local_invocation_id', 'local_invocation_index', 'num_workgroups', 'position', 'sample_index', 'sample_mask', 'vertex_index', 'workgroup_id')");
}
} // namespace
diff --git a/src/tint/reader/wgsl/parser_impl_variable_attribute_test.cc b/src/tint/reader/wgsl/parser_impl_variable_attribute_test.cc
index 1b538a2..602cc20 100644
--- a/src/tint/reader/wgsl/parser_impl_variable_attribute_test.cc
+++ b/src/tint/reader/wgsl/parser_impl_variable_attribute_test.cc
@@ -323,7 +323,8 @@
EXPECT_TRUE(attr.errored);
EXPECT_EQ(attr.value, nullptr);
EXPECT_TRUE(p->has_error());
- EXPECT_EQ(p->error(), R"(1:9: expected builtin. Did you mean 'front_facing'?
+ EXPECT_EQ(p->error(), R"(1:9: expected builtin
+Did you mean 'front_facing'?
Possible values: 'frag_depth', 'front_facing', 'global_invocation_id', 'instance_index', 'local_invocation_id', 'local_invocation_index', 'num_workgroups', 'position', 'sample_index', 'sample_mask', 'vertex_index', 'workgroup_id')");
}
@@ -494,7 +495,8 @@
EXPECT_TRUE(attr.errored);
EXPECT_EQ(attr.value, nullptr);
EXPECT_TRUE(p->has_error());
- EXPECT_EQ(p->error(), R"(1:26: expected interpolation sampling. Did you mean 'sample'?
+ EXPECT_EQ(p->error(), R"(1:26: expected interpolation sampling
+Did you mean 'sample'?
Possible values: 'center', 'centroid', 'sample')");
}
diff --git a/src/tint/reader/wgsl/parser_impl_variable_decl_test.cc b/src/tint/reader/wgsl/parser_impl_variable_decl_test.cc
index ba4a872..7e29203 100644
--- a/src/tint/reader/wgsl/parser_impl_variable_decl_test.cc
+++ b/src/tint/reader/wgsl/parser_impl_variable_decl_test.cc
@@ -106,7 +106,8 @@
EXPECT_TRUE(v.errored);
EXPECT_TRUE(p->has_error());
EXPECT_EQ(p->error(),
- R"(1:5: expected address space for variable declaration. Did you mean 'uniform'?
+ R"(1:5: expected address space for variable declaration
+Did you mean 'uniform'?
Possible values: 'function', 'private', 'push_constant', 'storage', 'uniform', 'workgroup')");
}
diff --git a/src/tint/utils/string.h b/src/tint/utils/string.h
index abfe51a..5c05ebf 100644
--- a/src/tint/utils/string.h
+++ b/src/tint/utils/string.h
@@ -66,6 +66,43 @@
/// @returns the Levenshtein distance between @p a and @p b
size_t Distance(std::string_view a, std::string_view b);
+/// Suggest alternatives for an unrecognized string from a list of expected values.
+/// @param got the unrecognized string
+/// @param strings the list of expected values
+/// @param ss the stream to write the suggest and list of possible values to
+template <size_t N>
+void SuggestAlternatives(std::string_view got,
+ const char* const (&strings)[N],
+ std::ostringstream& ss) {
+ // If the string typed was within kSuggestionDistance of one of the possible enum values,
+ // suggest that. Don't bother with suggestions if the string was extremely long.
+ constexpr size_t kSuggestionDistance = 5;
+ constexpr size_t kSuggestionMaxLength = 64;
+ if (!got.empty() && got.size() < kSuggestionMaxLength) {
+ size_t candidate_dist = kSuggestionDistance;
+ const char* candidate = nullptr;
+ for (auto* str : strings) {
+ auto dist = utils::Distance(str, got);
+ if (dist < candidate_dist) {
+ candidate = str;
+ candidate_dist = dist;
+ }
+ }
+ if (candidate) {
+ ss << "Did you mean '" << candidate << "'?\n";
+ }
+ }
+
+ // List all the possible enumerator values
+ ss << "Possible values: ";
+ for (auto* str : strings) {
+ if (str != strings[0]) {
+ ss << ", ";
+ }
+ ss << "'" << str << "'";
+ }
+}
+
} // namespace tint::utils
#endif // SRC_TINT_UTILS_STRING_H_
diff --git a/src/tint/utils/string_test.cc b/src/tint/utils/string_test.cc
index bbf8e0f..6b17dfb 100644
--- a/src/tint/utils/string_test.cc
+++ b/src/tint/utils/string_test.cc
@@ -58,5 +58,19 @@
EXPECT_EQ(Distance("", "Hello world"), 11u);
}
+TEST(StringTest, SuggestAlternatives) {
+ {
+ std::ostringstream ss;
+ SuggestAlternatives("hello wordl", {"hello world", "Hello World"}, ss);
+ EXPECT_EQ(ss.str(), R"(Did you mean 'hello world'?
+Possible values: 'hello world', 'Hello World')");
+ }
+ {
+ std::ostringstream ss;
+ SuggestAlternatives("hello world", {"foobar", "something else"}, ss);
+ EXPECT_EQ(ss.str(), R"(Possible values: 'foobar', 'something else')");
+ }
+}
+
} // namespace
} // namespace tint::utils