Skip to content
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

Change Request: Correct and unify LintMessage type #16968

Closed
1 task done
btmills opened this issue Mar 7, 2023 · 2 comments · Fixed by #17076
Closed
1 task done

Change Request: Correct and unify LintMessage type #16968

btmills opened this issue Mar 7, 2023 · 2 comments · Fixed by #17076
Assignees
Labels
archived due to age This issue has been archived; please open a new issue for any further discussion core Relates to ESLint's core APIs and features enhancement This change enhances an existing feature of ESLint evaluating The team will evaluate this issue to decide whether it meets the criteria for inclusion

Comments

@btmills
Copy link
Member

btmills commented Mar 7, 2023

ESLint version

v8.35.0

What problem do you want to solve?

For the processor docs update, we need to know why the LintMessage JSDoc typedef differs from real lint messages I captured at runtime. The short version is "because it doesn't reflect the implementation." I'll update the typedef or implementation, whichever is appropriate, to make sure they match.

What do you think is the correct solution?

After examining the implementation, I'm confident in at least three changes to the LintMessage type:

 /**
  * @typedef {Object} LintMessage
  * @property {number|undefined} column The 1-based column number.
  * @property {number} [endColumn] The 1-based column number of the end location.
  * @property {number} [endLine] The 1-based line number of the end location.
- * @property {boolean} fatal If `true` then this is a fatal error.
+ * @property {boolean} [fatal] If `true` then this is a fatal error.
  * @property {{range:[number,number], text:string}} [fix] Information for autofix.
  * @property {number|undefined} line The 1-based line number.
  * @property {string} message The error message.
+ * @property {string} [messageId] The ID of the message in the rule's meta.
+ * @property {(string|null)} [nodeType] Type of node
  * @property {string|null} ruleId The ID of the rule which makes this message.
  * @property {0|1|2} severity The severity of this message.
  * @property {Array<{desc?: string, messageId?: string, fix: {range: [number, number], text: string}}>} [suggestions] Information for suggestions.
  */
  1. Many places that create LintMessages don't set fatal, so I made it optional. This would be a breaking change if we made types part of our exports, but in this case I believe it's not because it's updating JSDoc documentation to reflect the reality of the implementation.
  2. Added an optional messageId property that's set by reports from rules.
  3. Added an optional, nullable nodeType property.

In addition, there are a couple types we can unify:

  1. There are a handful of places that currently reference an undefined Problem type that really means LintMessage, and I'll update those.
  2. The ReportInfo type duplicates LintMessage, and I'll update that to reference LintMessage instead.

Participation

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

Additional comments

I have two open questions:

  1. Should I make nodeType non-nullable and update the places in the implementation that explicitly set it to null to omit it instead? A few places already omit it for e.g. parse errors or ignored file messages, so API consumers already have to check that it might not be set.

  2. line and column are currently required but possibly undefined.

    Should I either:

    1. Keep line and column required but possibly undefined and update ignored file messages to explicitly set them to 0, following precedent elsewhere?
    2. Or make line and column optional and remove fallback 0/0 or 1/0 values from configuration issues?

    The second option is how I'd design the API from scratch because I wouldn't report a location unless we had a real location. But it might be a breaking change. Even though the type is specified as possibly undefined, in practice, some API consumers might only see messages that have real or fallback locations if they don't encounter ignored file messages. The second option would slightly increase the prevalence of location-less messages.

@btmills btmills added enhancement This change enhances an existing feature of ESLint triage An ESLint team member will look at this issue soon core Relates to ESLint's core APIs and features labels Mar 7, 2023
@btmills btmills self-assigned this Mar 7, 2023
@btmills btmills added evaluating The team will evaluate this issue to decide whether it meets the criteria for inclusion and removed triage An ESLint team member will look at this issue soon labels Mar 7, 2023
@fasttime
Copy link
Member

fasttime commented Mar 7, 2023

I recall from a previous discussion that changes in JSDoc should not be treated as breaking, so there should be no problem in redeclaring line and column as optional. As for removing the different fallbacks, that's a change that could affect existing implementations. The same is true for omitting nodeType where it is being set to null. I'd like to know what the rest of the team thinks about this.

@nzakas
Copy link
Member

nzakas commented Mar 7, 2023

Thanks for flagging this! I think updating the type definition so it reflects reality is the right way to go. Because we don't export types, this is non-breaking.

To answer your questions:

Should I make nodeType non-nullable and update the places in the implementation that explicitly set it to null to omit it instead?

I think this is okay because it appears we normalize nodeType before we expose it externally.

Keep line and column required but possibly undefined and update ignored file messages to explicitly set them to 0, following precedent elsewhere?

I also think this option is preferable to limit the amount of changes we are introducing into the codebase. I agree that if we were doing type checking from the start, we'd probably want the other option, but since we aren't, I'll favor the less invasive change.

@mdjermanovic what do you think?

btmills added a commit to btmills/eslint that referenced this issue Apr 9, 2023
`LintMessage.nodeType` is currently defined as required but nullable.
Actual implementation explicitly sets it to `null` in a couple places
and omits it in several.

After discussion in eslint#16968, we initially leaned toward making it
non-nullable but optional. I pursued that, but it resulted in slightly
more runtime code changes, including some branching to set it
conditionally.

Instead, I'm presenting the opposite solution of updating the remaining
implementations to match the existing type definition by explicitly
setting `nodeType` to `null`.
btmills added a commit to btmills/eslint that referenced this issue Apr 9, 2023
`LintMessage.nodeType` is currently defined as required but nullable.
Actual implementations explicitly set it to `null` in a couple places
and omit it in several.

After discussion in eslint#16968, we initially leaned toward making it
non-nullable but optional. I pursued that, but it resulted in slightly
more runtime code changes, including some branching in
`report-translator` to set it conditionally.

Instead, I'm presenting the opposite solution of updating the remaining
implementations to match the existing type definition by explicitly
setting `nodeType` to `null`.
btmills added a commit to btmills/eslint that referenced this issue Apr 9, 2023
`LintMessage.nodeType` is currently defined as required but nullable.
Actual implementations explicitly set it to `null` in a couple places
and omit it in several.

After discussion in eslint#16968, we initially leaned toward making it
non-nullable but optional. I pursued that, but it resulted in slightly
more runtime code changes, including some branching in
`report-translator` to set it conditionally.

Instead, I'm presenting the opposite solution of updating the remaining
implementations to match the existing type definition by explicitly
setting `nodeType` to `null`.
btmills added a commit to btmills/eslint that referenced this issue Apr 9, 2023
`LintMessage.nodeType` is currently defined as required but nullable.
Actual implementations explicitly set it to `null` in a couple places
and omit it in several.

After discussion in eslint#16968, we initially leaned toward making it
non-nullable but optional. I pursued that, but it resulted in slightly
more runtime code changes, including some branching in
`report-translator` to set it conditionally.

Instead, I'm presenting the opposite solution of updating the remaining
implementations to match the existing type definition by explicitly
setting `nodeType` to `null`.
btmills added a commit to btmills/eslint that referenced this issue May 7, 2023
`LintMessage.nodeType` is currently defined as required but nullable.
Actual implementations explicitly set it to `null` in a couple places
and omit it in several.

After discussion in eslint#16968, we initially leaned toward making it
non-nullable but optional. I pursued that, but it resulted in slightly
more runtime code changes, including some branching in
`report-translator` to set it conditionally.

Instead, I'm presenting the opposite solution of updating the remaining
implementations to match the existing type definition by explicitly
setting `nodeType` to `null`.
mdjermanovic pushed a commit that referenced this issue May 16, 2023
* Update LintMessage type to reflect implementation

I made three changes to `LintMessage` and `SuppressedLintMessage`

1. Many places that create `LintMessage`s don't set `fatal`, so I made
   it optional. This would be a breaking change if we made types part of
   our exports, but in this case I believe it's updating JSDoc
   documentation to reflect reality.
2. Added an optional `messageId` property that's set by reports from rules.
3. Added an optional, nullable `nodeType` property.
    - Reports from rules set it to a value.
    - `applyDirectives()` explicitly sets it to `null` for unused
      disable directives.
    - `Linter`'s `createLintingProblem()` explicitly sets it to `null`.

* Add missing LintResult type import

* Use LintMessage type

All of these previously referenced a `Problem` type that does not have a
definition.

* Replace ReportInfo type with LintMessage type

`ReportInfo` was defined within `report-translator.js` to be very
similar to `LintMessage`. It had one extra property, `source`, which
wasn't ever set, so it would've been removed anyway. In
`Linter.runRules()`, the return value from `reportTranslator()` gets
pushed into a `lintingProblems` array and returned, and the return type
annotation is `LintMessage[]`, which gives me confidence that
`ReportInfo` was intended to be the same type as `LintMessage`
elsewhere.

* Make ruleId required but nullable

Originally, we talked about the reverse - making `ruleId` optional but
non-nullable and relying on `report-translator.js`'s `createProblem()`
to normalize it. However, the `LintMessage` type would differ from
`createProblem()`'s return value only by `null` vs `undefined` for
`ruleId`. So instead, I made `ruleId` required but nullable and
explicitly set it to `null` in the few remaining places that didn't
already.

* Add missing null nodeTypes

`LintMessage.nodeType` is currently defined as required but nullable.
Actual implementations explicitly set it to `null` in a couple places
and omit it in several.

After discussion in #16968, we initially leaned toward making it
non-nullable but optional. I pursued that, but it resulted in slightly
more runtime code changes, including some branching in
`report-translator` to set it conditionally.

Instead, I'm presenting the opposite solution of updating the remaining
implementations to match the existing type definition by explicitly
setting `nodeType` to `null`.

* Update LintMessage docs

This updates existing documented properties to match the updated `LintMessage`
type and implementations.

`messageId`, `nodeType`, and `suppressions` remain undocumented.
@eslint-github-bot eslint-github-bot bot locked and limited conversation to collaborators Nov 13, 2023
@eslint-github-bot eslint-github-bot bot added the archived due to age This issue has been archived; please open a new issue for any further discussion label Nov 13, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
archived due to age This issue has been archived; please open a new issue for any further discussion core Relates to ESLint's core APIs and features enhancement This change enhances an existing feature of ESLint evaluating The team will evaluate this issue to decide whether it meets the criteria for inclusion
Projects
Archived in project
Development

Successfully merging a pull request may close this issue.

3 participants