-
Notifications
You must be signed in to change notification settings - Fork 29.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fs: fix incorrect error message in fs.open family when flags are not string or int #2873
Conversation
} | ||
|
||
cb(err); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note: for async functions this now passes the error to a callback.
5a1bd16
to
47f4ce2
Compare
@@ -537,8 +561,13 @@ fs.open = function(path, flags, mode, callback_) { | |||
var req = new FSReqWrap(); | |||
req.oncomplete = callback; | |||
|
|||
var intFlags = stringToFlags(flags); | |||
if (intFlags === undefined) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
unless flag
could ever be zero, i'd just go
if (!intFlags) {
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To be honest, I'm not sure if it could ever be zero... I could also do a Number.isInteger
test, but I figured this would execute faster.
To stop application bugs from propagating, the rule of thumb has always been "throw on logic error, callback on run-time error." Passing in bad flags or options is a logic error and should fail synchronously. |
in that case just change |
@bnoordhuis I was not familiar with that philosophy. |
Ok don't change the text there then.
.. sounds to me like return an err before even fs.open = function(path, flags, mode, callback_) {
var callback = makeCallback(arguments[arguments.length - 1]);
mode = modeNum(mode, 0o666);
if (!nullCheck(path, callback)) return;
var req = new FSReqWrap();
req.oncomplete = callback;
var intFlags = stringToFlags(flags);
if (intFlags === undefined) {
return throwFlagsError(flags, callback);
}
binding.open(pathModule._makeLong(path),
intFlags,
mode,
req);
}; |
I'll update this PR soon. |
47f4ce2
to
341619a
Compare
@bnoordhuis now it sticks to what you explained. |
if (!(err instanceof TypeError)) { | ||
throw new Error('unexpected error: ' + err); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can replace the try/catch block with an assert.throws(...)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wanted to make sure it's a TypeError and not the Error of "bad flag value" (which can also happen when it's a string, just not the right one).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can still do that like this :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Understood. If you insist, I'll change it, but I would rather stick to a class check here, to allow other contributions to Node to polish error messages when needed without breaking tests left and right. imho tests shouldn't check for the human readable output when avoidable, and in this case it is avoidable.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I second brendan's suggestion. What if TypeError is thrown because of some other problem? I prefer checking the actual message.
I would say, when the mesaages are updated, let the tests also be updated.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Understood. I'll change it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed (I hope :))
Basic premise LGTM. |
d405e03
to
163b012
Compare
@@ -32,8 +32,21 @@ fs.open(__filename, 'rs', function(err, fd) { | |||
openFd2 = fd; | |||
}); | |||
|
|||
assert.throws(function() { | |||
fs.open('/path/to/file/that/does/not/exist', { bad: 'flags' }, function() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can simply this a little bit,
fs.open('/path/to/file/that/does/not/exist', { bad: 'flags' }, assert.fail);
LGTM, with take-it or leave-it suggetions |
Before, it would show "TypeError: flags must be an int", which in native Node is technically correct. But in JS they may be (and usually are) strings. Fixes nodejs#2871
163b012
to
3f691b6
Compare
I liked your suggestions :) |
@ronkorving Glad you liked them :-) Let's give this some time so that we can get more reviewers see this (hopefully ;-)). |
if (err instanceof TypeError) { | ||
return err.message === 'File open flags may only be string or integer'; | ||
} | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can condense this to assert.throws(function() { ... }, /File open flags may only be string or integer/)
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Then I wouldn't be checking that it's a TypeError. That's not a problem?
LGTM |
assert.throws(function() { | ||
fs.open('/path/to/file/that/does/not/exist', { bad: 'flags' }, assert.fail); | ||
}, function(err) { | ||
if (err instanceof TypeError) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think you want to check if the error is a TypeError
. That should be unconditional, right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well I wrote the error as a TypeError since its thrown on bad types only, so might as well test for it, no?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let me rephrase. You should assert that the error is a TypeError
, not put it in an if
statement.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please see my comment below to @bnoordhuis. I can't seem to satisfy both your requests.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do what @bnoordhuis is asking for :-)
LGTM with one comment. |
return flag; | ||
} | ||
|
||
// We only allow strings from here on | ||
if (typeof flag !== 'string') { | ||
throw new TypeError('File open flags may only be string or integer'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Anyone who sees this message and looks at the docs would immediately report think this a bug, since it conflicts with the docs. If you want to say that, I suggest documenting the behaviour.
I strongly suspect that the fact that flags can be an int is historical, kept for backwards compat from the v0.small days, and should be deprecated, but its hard to deprecate a piece of an API.
// Only mess with strings | ||
if (typeof flag !== 'string') { | ||
// Integers are already good to go | ||
if (Number.isInteger(flag)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This check doesn't work as you think it does: the C++ layer requires flags to fit within 32 bits, this check here only requires it to be integral. Try with 0xfffffffff
, and you will see.
That's why in my PR I pass anything with typeof number to the C++ layer, it seems easier to let it do this check, because it does it correctly. Though you can put more code in here, checking the bounds futher.
Closing in favor of #2902 |
This fixes #2871