镜像站点 · 本页由第三方 GitHub 只读镜像提供,非 GitHub 官方站点,不接受任何登录或凭据输入。前往 github.com
Skip to content

console, doc, util: console.log() and util.format() are wrongly documented and may be inconsistent #13908

Description

@vsemozhetbyt

This is an issue that superseded #6341, Previously I thought this was a pure doc issue, now it seems to me that it is a more complicated problem that needs more discussion.

The both docs fragments (here and here) are wrong: util.inspect() is not called on each argument if the first argument is a non-format string. This is true if the first argument is not a string at all (see this path in code). If the first argument is a non-format string, util.inspect() is called only for arguments whose typeof is 'object' or 'symbol' (except null) — see this path in code.

Currently, I've found out that this impacts the output with String and Function arguments (watch out for quotes in the output for strings and absolutely different output for functions):

> console.log(1, 'str'); // .inspect()
1 'str'
> console.log('str', 'str'); // no .inspect()
str str

> console.log(1, () => true); // .inspect()
1 [Function]
> console.log('str', () => true); // no .inspect()
str () => true

Maybe there are other diferences.

Possible solutions:

  1. Document this difference.
  2. As it seems to be a confusing behavior, maybe we need to unify this with a semver-major fix.

UPD: This fragment is more correct:

> console.log(1, 'str2'); // .inspect()
1 'str2'
> console.log('str %s', 'str1', 'str2'); // no .inspect()
str str1 str2

> console.log(1, () => true); // .inspect()
1 [Function]
> console.log('str %s', 'str1', () => true); // no .inspect()
str str1 () => true

As you can see, util.inspect() is not called for excessive String and Function arguments here.

However, this doc fragment can also be improved, as functions are objects (maybe typeof should be mentioned).

Activity

  1. added
    consoleIssues and PRs related to the console subsystem.
    docIssues and PRs related to Node.js documentation.
    utilIssues and PRs related to the built-in util module.
    on Jun 25, 2017
  2. changed the title [-]console.log and util.format with non-format string[/-] [+]console, doc, util: console.log() and util.format() are wrongly documented[/+] on Jun 25, 2017
  3. changed the title [-]console, doc, util: console.log() and util.format() are wrongly documented[/-] [+]console, doc, util: console.log() and util.format() are wrongly documented and may be inconsistent[/+] on Jun 25, 2017
  4. nattelog commented on Jun 25, 2017

    @nattelog
    Contributor

    IMO if the first argument is not a format string, i.e. it could be a string type but with no %-characters (unless you \-escape them), all arguments should be called with util.inspect() for consistency purpose.

  5. vsemozhetbyt commented on Jun 25, 2017

    @vsemozhetbyt
    ContributorAuthor

    I am really not sure if this should also be applied for all excessive arguments after a format string. Both ways can be considered somehow inconsistent.

  6. nattelog commented on Jun 25, 2017

    @nattelog
    Contributor

    @vsemozhetbyt What would be the option? To throw an error?

  7. vsemozhetbyt commented on Jun 25, 2017

    @vsemozhetbyt
    ContributorAuthor

    I think throwing would be needlessly obstructive. Maybe this case should be just documented more clearly. Because if we apply inspect-all-format-not-respective-arguments here, this could be more confusing:

    > console.log('foo %s', 'str', 'str');
    foo str 'str' // instead of current: foo str str
  8. nattelog commented on Jun 25, 2017

    @nattelog
    Contributor

    I think it seems consistent. If you for any case would want to write that string you simply write console.log('foo %s str', 'str')?

  9. vsemozhetbyt commented on Jun 25, 2017

    @vsemozhetbyt
    ContributorAuthor

    Or console.log('foo %s %s', 'str', 'str');. Well, maybe this makes sense.

    However, this should be widely discussed, as these ones are rather heavily used functions across the userland.

  10. vsemozhetbyt commented on Jun 27, 2017

    @vsemozhetbyt
    ContributorAuthor

    I do not know how this can be worded in good English with not so confusing examples, but this is some dry summation.

    Various sets of arguments with different behavior and results.

    1. String with N placeholders + N arguments = string where all placeholders are replaced with converted arguments.

    2. String with N placeholders + zero or less than N arguments = string where placeholders without corresponding arguments are not replaced.

    3. String with N placeholders (including zero) + more than N arguments = string where placeholders are replaced with converted corresponding arguments + space + extra arguments coerced into strings and concatenated with spaces (util.inspect() is used for arguments whose typeof is 'object' (except null) or 'symbol').

    4. Non-string argument + zero or more arguments = string where all arguments coerced into strings and concatenated with spaces (each argument is converted to a string using util.inspect())

    5. No arguments = empty string (or '\n' from console.log()). The doc for util.format() may also be corrected here, as the first argument is not mandatory (this is a useless case, nevertheless, this may be clarified).

    const f = () => {};
    const s = 'str';
    const o = new Array(3);
    
    console.log('%j %s %j');
    console.log('%j %s %j', f);
    
    console.log('%j %s %j', f, s, o);
    
    console.log('%j %s', f, s, o);
    console.log('', f, s, o);
    
    console.log(f, s, o);
    
    console.log();
    %j %s %j
    undefined %s %j
    
    undefined str [null,null,null]
    
    undefined str [ <3 empty items> ]
     () => {} str [ <3 empty items> ]
    
    [Function: f] 'str' [ <3 empty items> ]
    
    < '\n' >
    const f = () => {};
    const s = 'str';
    const o = new Array(3);
    
    > util.format('%j %s %j');
    '%j %s %j'
    > util.format('%j %s %j', f);
    'undefined %s %j'
    
    > util.format('%j %s %j', f, s, o);
    'undefined str [null,null,null]'
    
    > util.format('%j %s', f, s, o);
    'undefined str [ <3 empty items> ]'
    > util.format('', f, s, o);
    ' () => {} str [ <3 empty items> ]'
    
    > util.format(f, s, o);
    '[Function: f] \'str\' [ <3 empty items> ]'
    
    > util.format();
    ''

    cc @nodejs/documentation, @bnoordhuis, @cjihrig, @evanlucas

  11. nattelog commented on Jun 30, 2017

    @nattelog
    Contributor

    Would it be of interest if I submitted a PR with this proposal realised?

  12. vsemozhetbyt commented on Jun 30, 2017

    @vsemozhetbyt
    ContributorAuthor

    @nattelog I hope a doc PR would draw some more attention at least)

  13. nattelog commented on Jun 30, 2017

    @nattelog
    Contributor

    @vsemozhetbyt And by doc PR you mean one where I document the util.format() function?

  14. nattelog commented on Jun 30, 2017

    @nattelog
    Contributor

    @vsemozhetbyt I added a small check to this if statement:

    if (typeof f !== 'string' || !/%\w/.test(f))

    To guard against non-format strings. However, it makes many tests fail. See this for instance:

    === release test-cli-eval ===                            
    Path: parallel/test-cli-eval
    assert.js:60
      throw new errors.AssertionError({
      ^
    
    AssertionError [ERR_ASSERTION]: '\'start\'\n\'beforeExit\'\n\'exit\'\n' === 'start\nbeforeExit\nexit\n'
        at Object.<anonymous> (/Users/nattelog/Projekt/node/test/parallel/test-cli-eval.js:174:10)
        at Module._compile (module.js:569:30)
        at Object.Module._extensions..js (module.js:580:10)
        at Module.load (module.js:503:32)
        at tryModuleLoad (module.js:466:12)
        at Function.Module._load (module.js:458:3)
        at Function.Module.runMain (module.js:605:10)
        at startup (bootstrap_node.js:158:16)
        at bootstrap_node.js:575:3
    Command: out/Release/node /Users/nattelog/Projekt/node/test/parallel/test-cli-eval.js

    Since util.inspect() will be called on every argument passed now, there will be more cases of util.format() outputs with an extra '-character around strings which will cause assertion errors like the one in the example.

    If this change will be implemented, many test cases must be changed as well.

  15. vsemozhetbyt commented on Jun 30, 2017

    @vsemozhetbyt
    ContributorAuthor

    @nattelog I think the doc for util.format() (and maybe some cleanup and hint in the doc for console.log()) would be a good start and a base for backports to docs for all actual versions.

    If you like to unify the API behavior, it would be better to raise a separate PR for this.

    Yes, this change will be a drastic one and many tests will need fixing, so this will be some time-consuming project. And the guard also should be more careful (currently it would erroneously consider strings like ' %%s ' or ' %_wrong ' as format strings ).

    So maybe we should get some +1 from CTC members before somebody dares to undertake this fix.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    consoleIssues and PRs related to the console subsystem.docIssues and PRs related to Node.js documentation.utilIssues and PRs related to the built-in util module.

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions