Skip to content

Bug: filter callback in getFirstToken/getLastToken is called with undefined #16742

@fasttime

Description

@fasttime

Environment

Node version: v19.3.0
npm version: v9.2.0
Local ESLint version: v8.31.0 (Currently used)
Global ESLint version: Not found
Operating System: darwin 22.1.0

What parser are you using?

Default (Espree)

What did you do?

While working on a custom rule I noticed an edge case where the filter callback passed to sourceCode.getFirstToken() and sourceCode.getLastToken() was being called with an undefined argument instead of a Token object.

The following is a minimal example I created to illustrate the problem.

const { RuleTester } = require('eslint');

function isCommaToken(token) {
    return token.type === 'Punctuator' && token.value === ',';
}

const rule = {
    meta: {
        docs: { description: 'Forbid commas' },
        messages: { fail: 'commas not allowed' },
    },
    create(context) {
        const sourceCode = context.getSourceCode();
        return {
            Program: node => {
                if (sourceCode.getFirstToken(node, { filter: isCommaToken })) {
                    context.report({ node, messageId: 'fail' });
                }
            }
        };
    }
};

new RuleTester().run(
    'no-commas',
    rule,
    {
        valid:
        [
            '// comment',
        ],
        invalid: [],
    },
);

What did you expect to happen?

The sample test code should run without errors.

What actually happened?

An error occurs, because the filter callback is called with undefined:

TypeError: Cannot read properties of undefined (reading 'type')
Occurred while linting :1
Rule: "no-commas"
    at isCommaToken (.../no-commas.js:4:18)
    at FilterCursor.moveNext (.../node_modules/eslint/lib/source-code/token-store/filter-cursor.js:37:17)
    at FilterCursor.getOneToken (.../node_modules/eslint/lib/source-code/token-store/cursor.js:49:21)
    at SourceCode.getFirstToken (.../node_modules/eslint/lib/source-code/token-store/index.js:264:11)
    at Program (.../no-commas.js:16:32)
    at ruleErrorHandler (.../node_modules/eslint/lib/linter/linter.js:1115:28)
    at .../node_modules/eslint/lib/linter/safe-emitter.js:45:58
    at Array.forEach ()
    at Object.emit (.../node_modules/eslint/lib/linter/safe-emitter.js:45:38)
    at NodeEventGenerator.applySelector (.../node_modules/eslint/lib/linter/node-event-generator.js:297:26) {
  ruleId: 'no-commas',
  currentNode: Node {
    type: 'Program',
    loc: SourceLocation {
      start: Position { line: 1, column: 0 },
      end: Position { line: 1, column: 10 }
    },
    range: [ 0, 10 ],
    body: [],
    sourceType: 'script',
    comments: [
      {
        type: 'Line',
        value: ' comment',
        range: [ 0, 10 ],
        loc: {
          start: Position { line: 1, column: 0 },
          end: Position { line: 1, column: 10 }
        }
      }
    ],
    tokens: [],
    parent: null
  }
}

Participation

  • I am willing to submit a pull request for this issue.

Additional comments

The problem occurs when a file being linted is empty or contains only comments, which is not extremely uncommon in practice.

I checked the usages of getFirstToken and getLastToken in the code base and it seems that none of the core rule is affected by this problem, but since these methods are part of the public API, I am wondering if this issue should be fixed. If not, it would be great to have it documented that the callback argument could be undefined sometimes, although this does not seem to be the intended behavior.

Metadata

Metadata

Assignees

No one assigned

    Labels

    acceptedThere is consensus among the team that this change meets the criteria for inclusionarchived due to ageThis issue has been archived; please open a new issue for any further discussionbugESLint is working incorrectlycoreRelates to ESLint's core APIs and featuresrepro:yesIssues with a reproducible example

    Type

    No type

    Projects

    Status

    Complete

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions