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

Add SonarTS converters. #1085

Merged
merged 8 commits into from
May 28, 2021
Merged

Add SonarTS converters. #1085

merged 8 commits into from
May 28, 2021

Conversation

Res42
Copy link
Contributor

@Res42 Res42 commented May 22, 2021

PR Checklist

Overview

I added the rules from the table.

I have some questions:

  1. There are existing converters that does the same as a new one for SonarTS. Should I use the existing converter or are these distinct business logic?
Existing converter New converter ESLint rule
convertAdjacentOverloadSignatures convertConsecutiveOverloads @typescript-eslint/adjacent-overload-signatures
convertNoUnnecessarySemicolons convertNoExtraSemicolon no-extra-semi
convertNoForInArray convertNoInMisuse @typescript-eslint/no-for-in-array
convertAwaitPromise convertNoInvalidAwait @typescript-eslint/await-thenable
convertNoMultilineString convertNoMultilineStringLiterals no-multi-str
convertNoUnnecessaryTypeAssertion convertNoUselessCast @typescript-eslint/no-unnecessary-type-assertion
convertNoConstruct convertUsePrimitiveType no-new-wrappers
  1. When do you need mergers? These are the new converters that already have an existing one, but with different rule arguments:
Existing converter New converter ESLint rule
convertNoEmpty convertNoEmptyNestedBlocks no-empty
convertNoUseBeforeDeclare convertNoVariableUsageBeforeDeclaration @typescript-eslint/no-use-before-define
convertBanTypes convertUsePrimitiveType @typescript-eslint/ban-types

Also in the new converter convertUsePrimitiveType I use the ESLint rule @typescript-eslint/ban-types with the types String, Boolean, and Number. Should I do this? I don't know if it is better if I just use the default options (by not giving any arguments) or if the merger can handle if this converter uses these explicit types and another converter use the default (no args) options.

  1. For example there is the @typescript-eslint/no-use-before-define existing converter (convertNoUseBeforeDeclare). This converter does not turn off the default ESLint rule no-use-before-define. But in the @typescript-eslint/no-use-before-define documentation it is written that // note you must disable the base rule as it can report incorrect errors. Is this a bug in the existing converter or should I not turn off the no-use-before-define rule in my converter?

@JoshuaKGoldberg
Copy link
Member

Wowee, all of the rules in a single go: this is very impressive @Res42.

Normally I would request they all be split up into separate PRs but since you've already gone through the effort, and most of these seem pretty straightforward, it seems reasonable to review them all here. 👍

  1. There are existing converters that does the same as a new one for SonarTS. Should I use the existing converter or are these distinct business logic?

Distinct business logic. 👍

Each input TSLint rule is associated with a single converter, regardless of what ESLint rule(s) any converters may output. Other converters might result in the same output rule (which is what you're seeing and asking about, I think), but that doesn't impact each other converter. [Architecture]

  1. When do you need mergers?

Any time two or more rule converters output the same ESLint rule. You've noted quite a few places where, as of this PR, that might happen: e.g. both convertAdjacentOverloadSignatures and convertConsecutiveOverloads outputting @typescript-eslint/adjacent-overload-signatures.

Right now a merger will be called in convertRules.ts whenever an ESLint rule is seen the second time:
// 4c. If this is the first time the output ESLint rule is seen, it's directly marked as converted.
if (existingConversion === undefined) {
    converted.set(changes.ruleName, newConversion);
    continue;
}

// 4d. If not, a rule merger is run to combine it with its existing output settings.
const merger = dependencies.ruleMergers.get(changes.ruleName);
if (merger === undefined) {
    failed.push(ConversionError.forMerger(changes.ruleName));
} else {
    const existingNotices = existingConversion.notices ?? [];
    const newNotices = newConversion.notices ?? [];

    converted.set(changes.ruleName, {
        ...existingConversion,
        ruleArguments: merger(
            existingConversion.ruleArguments,
            newConversion.ruleArguments,
        ),
        notices: Array.from(new Set([...existingNotices, ...newNotices])),
    });
}

This PR raises the good point that we can skip the mergers if the rules' arguments are actually the same: such as from not existing, or having deep value equality. Filed #1086.

I think we should block this PR from being mergeable until that feature is in, because we'll have to otherwise write a bunch of mergers that do roughly nothing and will be deleted soon. Do you have the time+energy to implement it @Res42? I can if not!

@JoshuaKGoldberg JoshuaKGoldberg added status: blocked We can't make progress on this issue until something else is resolved... status: waiting for author The PR author should address requested changes labels May 23, 2021
@JoshuaKGoldberg
Copy link
Member

But in the @typescript-eslint/no-use-before-define documentation it is written that // note you must disable the base rule as it can report incorrect errors. Is this a bug in the existing converter or should I not turn off the no-use-before-define rule in my converter?

That is a bug! Were there any other rules you noticed? Go ahead and file an issue if you'd like, much appreciated. I can go through and check them after this to see as well.

@Res42
Copy link
Contributor Author

Res42 commented May 23, 2021

  1. Okay, then I won't delete these new converters. (Yes I was asking about the output rules.)
  2. So after Don't bother with a rule merger if rule arguments are the same #1086 this PR won't need mergers for the simple cases (first table), but there is a need for 2 new mergers. Should I add these to this PR?
    • no-empty
    • @typescript-eslint/no-use-before-define
  3. Okay, I'll file it. Sorry, I only checked the rules (for existing converters) mentioned in the original issue.

@Res42
Copy link
Contributor Author

Res42 commented May 23, 2021

I saw this issue: #1087 but I have one more problem with the ban-types merger for my use case:

  • When merging a rule with no-args and a rule only with types that are included in the default options (with no-args) it should merge these to no-args since that is "stronger".

The current default options:

Default Options
const defaultTypes = {
  String: {
    message: 'Use string instead',
    fixWith: 'string',
  },
  Boolean: {
    message: 'Use boolean instead',
    fixWith: 'boolean',
  },
  Number: {
    message: 'Use number instead',
    fixWith: 'number',
  },
  Symbol: {
    message: 'Use symbol instead',
    fixWith: 'symbol',
  },

  Function: {
    message: [
      'The `Function` type accepts any function-like value.',
      'It provides no type safety when calling the function, which can be a common source of bugs.',
      'It also accepts things like class declarations, which will throw at runtime as they will not be called with `new`.',
      'If you are expecting the function to accept certain arguments, you should explicitly define the function shape.',
    ].join('\n'),
  },

  // object typing
  Object: {
    message: [
      'The `Object` type actually means "any non-nullish value", so it is marginally better than `unknown`.',
      '- If you want a type meaning "any object", you probably want `Record<string, unknown>` instead.',
      '- If you want a type meaning "any value", you probably want `unknown` instead.',
    ].join('\n'),
  },
  '{}': {
    message: [
      '`{}` actually means "any non-nullish value".',
      '- If you want a type meaning "any object", you probably want `Record<string, unknown>` instead.',
      '- If you want a type meaning "any value", you probably want `unknown` instead.',
    ].join('\n'),
  },
  object: {
    message: [
      'The `object` type is currently hard to use ([see this issue](https://github.com/microsoft/TypeScript/issues/21732)).',
      'Consider using `Record<string, unknown>` instead, as it allows you to more easily inspect and use the keys.',
    ].join('\n'),
  },
};

Source: https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/ban-types.md

@JoshuaKGoldberg
Copy link
Member

Should I add these to this PR?

Yes - more generally, whenever there's a known introduction the need for a merger, the same PR should include that merger. This way we don't introduce error cases.

one more problem with the ban-types merger for my use case:

Cool 😄 . Are you up for posting in #1087 then?

@Res42
Copy link
Contributor Author

Res42 commented May 23, 2021

Merger check according the new handling described in #1086;

ESLint rule Merger needed Merger added
@typescript-eslint/adjacent-overload-signatures No (not configurable) -
no-extra-semi No (not configurable) -
@typescript-eslint/no-for-in-array No (not configurable) -
@typescript-eslint/await-thenable No (not configurable) -
no-multi-str No (not configurable) -
@typescript-eslint/no-unnecessary-type-assertion No (already exists) -
no-new-wrappers No (not configurable) -
no-empty Yes Yes
@typescript-eslint/no-use-before-define ​ Yes Yes
@typescript-eslint/ban-types No (already exists) -

@JoshuaKGoldberg JoshuaKGoldberg added status: waiting for reviewer Waiting for a maintainer to review and removed status: blocked We can't make progress on this issue until something else is resolved... status: waiting for author The PR author should address requested changes labels May 26, 2021
@JoshuaKGoldberg JoshuaKGoldberg self-requested a review May 26, 2021 08:25
Copy link
Member

@JoshuaKGoldberg JoshuaKGoldberg left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is beautiful... just one little thing off!

Copy link
Member

@JoshuaKGoldberg JoshuaKGoldberg left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Grr, wrong button*

@JoshuaKGoldberg
Copy link
Member

FYI @KingDarBoja - I'm not explicitly requesting your review because this PR is huge and I don't want to bug you for things I'm going over anyway 😄 , but if you request a review from yourself I'll hold off merging until you've approved.

@JoshuaKGoldberg JoshuaKGoldberg self-requested a review May 28, 2021 13:21
Copy link
Member

@JoshuaKGoldberg JoshuaKGoldberg left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fantastic, thanks so much @Res42!

@JoshuaKGoldberg JoshuaKGoldberg merged commit f4a6784 into typescript-eslint:main May 28, 2021
@Res42 Res42 deleted the feature/sonar branch May 28, 2021 13:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
status: waiting for reviewer Waiting for a maintainer to review
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Missing converter(s): SonarTS
2 participants