Better user error detection in assert()
Reorders the assert() function usage checks for clarity, and detects
incorrect usage of assert (when the message is not a string) even when
the assertion condition is true.
Change-Id: Ia6e6f897d265bc76ca3d5a6d2b6acf6d3462831a
Reviewed-on: https://gn-review.googlesource.com/c/gn/+/6340
Reviewed-by: Brett Wilson <brettw@chromium.org>
Commit-Queue: Brett Wilson <brettw@chromium.org>
diff --git a/tools/gn/functions.cc b/tools/gn/functions.cc
index b08dbd0..ce76280 100644
--- a/tools/gn/functions.cc
+++ b/tools/gn/functions.cc
@@ -231,43 +231,55 @@
const FunctionCallNode* function,
const std::vector<Value>& args,
Err* err) {
+ // Check usage: Assert takes 1 or 2 arguments.
if (args.size() != 1 && args.size() != 2) {
*err = Err(function->function(), "Wrong number of arguments.",
- "assert() takes one or two argument, "
- "were you expecting somethig else?");
- } else if (args[0].type() != Value::BOOLEAN) {
- *err = Err(function->function(), "Assertion value not a bool.");
- } else if (!args[0].boolean_value()) {
- if (args.size() == 2) {
- // Optional string message.
- if (args[1].type() != Value::STRING) {
- *err = Err(function->function(), "Assertion failed.",
- "<<<ERROR MESSAGE IS NOT A STRING>>>");
- } else {
- *err = Err(function->function(), "Assertion failed.",
- args[1].string_value());
- }
- } else {
- *err = Err(function->function(), "Assertion failed.");
- }
+ "assert() takes one or two arguments, "
+ "were you expecting something else?");
+ return Value();
+ }
- if (args[0].origin()) {
- // If you do "assert(foo)" we'd ideally like to show you where foo was
- // set, and in this case the origin of the args will tell us that.
- // However, if you do "assert(foo && bar)" the source of the value will
- // be the assert like, which isn't so helpful.
- //
- // So we try to see if the args are from the same line or not. This will
- // break if you do "assert(\nfoo && bar)" and we may show the second line
- // as the source, oh well. The way around this is to check to see if the
- // origin node is inside our function call block.
- Location origin_location = args[0].origin()->GetRange().begin();
- if (origin_location.file() != function->function().location().file() ||
- origin_location.line_number() !=
- function->function().location().line_number()) {
- err->AppendSubErr(
- Err(args[0].origin()->GetRange(), "", "This is where it was set."));
- }
+ // Check usage: The first argument must be a boolean.
+ if (args[0].type() != Value::BOOLEAN) {
+ *err = Err(function->function(), "Assertion value not a bool.");
+ return Value();
+ }
+ bool assertion_passed = args[0].boolean_value();
+
+ // Check usage: The second argument, if present, must be a string.
+ if (args.size() == 2 && args[1].type() != Value::STRING) {
+ *err = Err(function->function(), "Assertion message is not a string.");
+ return Value();
+ }
+
+ // Assertion passed: there is nothing to do, so return an empty value.
+ if (assertion_passed) {
+ return Value();
+ }
+
+ // Assertion failed; try to make a useful message and report it.
+ if (args.size() == 2) {
+ *err = Err(function->function(), "Assertion failed.",
+ args[1].string_value());
+ } else {
+ *err = Err(function->function(), "Assertion failed.");
+ }
+ if (args[0].origin()) {
+ // If you do "assert(foo)" we'd ideally like to show you where foo was
+ // set, and in this case the origin of the args will tell us that.
+ // However, if you do "assert(foo && bar)" the source of the value will
+ // be the assert like, which isn't so helpful.
+ //
+ // So we try to see if the args are from the same line or not. This will
+ // break if you do "assert(\nfoo && bar)" and we may show the second line
+ // as the source, oh well. The way around this is to check to see if the
+ // origin node is inside our function call block.
+ Location origin_location = args[0].origin()->GetRange().begin();
+ if (origin_location.file() != function->function().location().file() ||
+ origin_location.line_number() !=
+ function->function().location().line_number()) {
+ err->AppendSubErr(
+ Err(args[0].origin()->GetRange(), "", "This is where it was set."));
}
}
return Value();