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

feat(lint/noEvolvingAny): add rule #2112

Merged
merged 11 commits into from
Mar 23, 2024

Conversation

fujiyamaorange
Copy link
Contributor

@fujiyamaorange fujiyamaorange commented Mar 16, 2024

Summary

#1883

Test Plan

Invalid

let a;
const b = [];
let c = null;

Valid

let a: number;
let b = 1
var c : string;
var d = "abn"
const e: never[] = [];
const f = [null];
const g = ['1'];
const h = [1];
let workspace: Workspace | null = null;

@github-actions github-actions bot added A-Project Area: project A-Linter Area: linter A-Website Area: website L-JavaScript Language: JavaScript and super languages A-Diagnostic Area: diagnostocis labels Mar 16, 2024
Copy link

netlify bot commented Mar 16, 2024

Deploy Preview for biomejs ready!

Name Link
🔨 Latest commit fe46edb
🔍 Latest deploy log https://app.netlify.com/sites/biomejs/deploys/65fd30cc8caac50008c7fc1d
😎 Deploy Preview https://deploy-preview-2112--biomejs.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
Lighthouse
Lighthouse
1 paths audited
Performance: 91 (🔴 down 5 from production)
Accessibility: 97 (no change from production)
Best Practices: 100 (no change from production)
SEO: 93 (no change from production)
PWA: -
View the detailed breakdown and full score reports

To edit notification comments on pull requests, go to your Netlify site configuration.

Copy link

codspeed-hq bot commented Mar 16, 2024

CodSpeed Performance Report

Merging #2112 will not alter performance

⚠️ No base runs were found

Falling back to comparing fujiyamaorange:lint-no-evolving-any (fe46edb) with main (644d2f4)

Summary

✅ 93 untouched benchmarks

@github-actions github-actions bot added the A-Changelog Area: changelog label Mar 17, 2024
@fujiyamaorange fujiyamaorange marked this pull request as ready for review March 17, 2024 12:59
@fujiyamaorange
Copy link
Contributor Author

@ematipico @Conaclos
Would you be so kind as to review this PR at your earliest convenience? Your insights would be greatly appreciated.

Copy link
Contributor

@togami2864 togami2864 left a comment

Choose a reason for hiding this comment

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

Thank you! Could you rebase the branch?

}

if is_initialized {
let initializer = variable.initializer().unwrap();
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
let initializer = variable.initializer().unwrap();
let initializer = variable.initializer()?;

Comment on lines 74 to 93
let optional_array_expression = expression.as_js_array_expression();
let optional_js_literal_expression = expression.as_any_js_literal_expression();

if let Some(array_expression) = optional_array_expression {
if array_expression.elements().into_iter().next().is_none()
&& !is_type_annotated
{
return Some(variable);
}
}

if let Some(js_literal_expression) = optional_js_literal_expression {
if js_literal_expression
.as_js_null_literal_expression()
.is_some()
&& !is_type_annotated
{
return Some(variable);
}
}
Copy link
Member

@unvalley unvalley Mar 20, 2024

Choose a reason for hiding this comment

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

We can simplify here like:

match expression {
    AnyJsExpression::AnyJsLiteralExpression(literal_expr) => {
        if literal_expr.as_js_null_literal_expression().is_some()
            && !is_type_annotated
        {
            return Some(variable);
        }
    }
    AnyJsExpression::JsArrayExpression(array_expr) => {
        if array_expr.elements().into_iter().next().is_none() && !is_type_annotated
        {
            return Some(variable);
        }
    }
    _ => continue
};

},
)
.note(markup! {
"Variable's type may evolve, leading to "<Emphasis>"any"</Emphasis>". Use explicit type or initialization, e.g., 'let x: number;' or 'let x = 0;'."
Copy link
Member

@unvalley unvalley Mar 20, 2024

Choose a reason for hiding this comment

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

IMO, currently we can reduce the examples. Because it may confuse users who should use string type annotation (or others). And we can describe what users should do here (or maybe good to add .note chain)

Suggested change
"Variable's type may evolve, leading to "<Emphasis>"any"</Emphasis>". Use explicit type or initialization, e.g., 'let x: number;' or 'let x = 0;'."
"Variable's type may evolve, leading to "<Emphasis>"any"</Emphasis>". Use explicit type or initialization. Specify an explicit type or initial value to avoid implicit type evolution."

Comment on lines 23 to 27
/// ```ts,expect_diagnostic
/// let a = 'hello';
/// const b = ['hello'];
/// const c = null;
/// ```
Copy link
Member

@unvalley unvalley Mar 20, 2024

Choose a reason for hiding this comment

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

I think this block is valid, so we can remove expect_diagnostic

/// Disallow variables from evolving into `any` type through reassignments.
///
/// In TypeScript, variables without explicit type annotations can evolve their types based on subsequent assignments.
/// This behavior can inadvertently lead to variables with an `any` type, weakening type safety.
Copy link
Member

Choose a reason for hiding this comment

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

IMHO, the word accidentally is used more frequently than inadvertently.

Suggested change
/// This behavior can inadvertently lead to variables with an `any` type, weakening type safety.
/// This behavior can accidentally lead to variables with an `any` type, weakening type safety.

rule_category!(),
variable.text_range(),
markup! {
"This variable's type is allowed to evolve implicitly, leading to potential "<Emphasis>"any"</Emphasis>" types. Specify an explicit type or initialization to avoid implicit type evolution."
Copy link
Member

Choose a reason for hiding this comment

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

I think this one sentence describes the error.

Suggested change
"This variable's type is allowed to evolve implicitly, leading to potential "<Emphasis>"any"</Emphasis>" types. Specify an explicit type or initialization to avoid implicit type evolution."
"This variable's type is allowed to evolve implicitly, leading to potential "<Emphasis>"any"</Emphasis>" types."

Copy link
Member

@unvalley unvalley left a comment

Choose a reason for hiding this comment

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

Thank you, I left some suggestions!

@fujiyamaorange
Copy link
Contributor Author

@togami2864 @unvalley
Thank you for your review.
I've made some adjustments. Could you kindly take another look at your convenience?

Copy link
Member

@ematipico ematipico left a comment

Choose a reason for hiding this comment

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

Code wise, it looks good. I left some things that you should address before merging :)

Thank you!

Some(
RuleDiagnostic::new(
rule_category!(),
variable.text_range(),
Copy link
Member

Choose a reason for hiding this comment

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

You should use text_trimmed_range. If you check the snapshots, you will see that diagnostics show the variable and the space.

  ! This variable's type is allowed to evolve implicitly, leading to potential any types.
  
    1 │ let a;
    2 │ const b = [];
  > 3 │ let c = null;
      │     ^^
    4 │ 
    5 │ let someVar1; 

text_trimmed_range will return the range without the trivia, while text_range will return the range with trivia

Comment on lines 17 to 20
/// ```ts,expect_diagnostic
/// let a;
/// const b = [];
/// let c = null;
Copy link
Member

Choose a reason for hiding this comment

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

I am not sure if you read the contribution guide, but when creating a snippet that should emit diagnostics, you should make sure that each snippet emits ONLY one diagnostic.

https://github.com/biomejs/biome/blob/main/crates/biome_analyze/CONTRIBUTING.md#document-the-rule

When adding invalid snippets in the ### Invalid section, you must use the expect_diagnostic code block property. We use this property to generate a diagnostic and attach it to the snippet. A snippet must emit only ONE diagnostic.

This means that you have to break down this snippet in THREE code blocks:

```ts,expect_diagnostic
let a;
```

```ts,expect_diagnostic
const b = [];
```

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I sincerely apologize for overlooking that rule. 🙏 I will make the necessary corrections promptly.

Copy link
Member

@ematipico ematipico Mar 21, 2024

Choose a reason for hiding this comment

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

No problem! That's what a review is for :) to catch possible bugs!

@ematipico
Copy link
Member

ematipico commented Mar 21, 2024

I think there's something wrong with the snippets you used for the valid, check the rendered documentation page: https://deploy-preview-2112--biomejs.netlify.app/linter/rules/no-evolving-any/#_top

It seems that the rule is triggered, which it should not. Could you check why? Try to run the command just gen-lint locally, and see if the command shows some error.

EDIT

I was right:

https://github.com/biomejs/biome/actions/runs/8375011378/job/22931461819?pr=2112#step:10:52

rule_category!(),
variable.text_trimmed_range(),
markup! {
"This variable's type is allowed to evolve implicitly, leading to potential "<Emphasis>"any"</Emphasis>" types."
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
"This variable's type is allowed to evolve implicitly, leading to potential "<Emphasis>"any"</Emphasis>" types."
"This variable's type is not allowed to evolve implicitly, leading to potential "<Emphasis>"any"</Emphasis>" types."

},
)
.note(markup! {
"Variable's type may evolve, leading to "<Emphasis>"any"</Emphasis>". Use explicit type or initialization. Specify an explicit type or initial value to avoid implicit type evolution."
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
"Variable's type may evolve, leading to "<Emphasis>"any"</Emphasis>". Use explicit type or initialization. Specify an explicit type or initial value to avoid implicit type evolution."
"The variable's type may evolve, leading to "<Emphasis>"any"</Emphasis>". Use an explicit type or initialization. Specifying an explicit type or initial value to avoid implicit type evolution."

Comment on lines 6 to 11
/// Disallow variables from evolving into `any` type through reassignments.
///
/// In TypeScript, variables without explicit type annotations can evolve their types based on subsequent assignments.
/// This behavior can accidentally lead to variables with an `any` type, weakening type safety.
/// Just like the `any` type, evolved `any` types disable many type checking rules and should be avoided to maintain strong type safety.
/// This rule prevents such cases by ensuring variables do not evolve into `any` type, encouraging explicit type annotations and controlled type evolutions.
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
/// Disallow variables from evolving into `any` type through reassignments.
///
/// In TypeScript, variables without explicit type annotations can evolve their types based on subsequent assignments.
/// This behavior can accidentally lead to variables with an `any` type, weakening type safety.
/// Just like the `any` type, evolved `any` types disable many type checking rules and should be avoided to maintain strong type safety.
/// This rule prevents such cases by ensuring variables do not evolve into `any` type, encouraging explicit type annotations and controlled type evolutions.
/// Disallow variables from evolving into `any` type through reassignments.
///
/// In TypeScript, variables without explicit type annotations can evolve their types based on subsequent assignments.
/// This behaviour can accidentally lead to variables with an `any` type, weakening type safety.
/// Just like the `any` type, evolved `any` types disable many type-checking rules and should be avoided to maintain strong type safety.
/// This rule prevents such cases by ensuring variables do not evolve into `any` type, encouraging explicit type annotations and controlled type evolutions.

@fujiyamaorange
Copy link
Contributor Author

@ematipico
Thank you for the advice! I fixed in cd14d09

Preview

CHANGELOG.md Outdated
Copy link
Member

Choose a reason for hiding this comment

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

@fujiyamaorange Something formatter removes line breaks. Could you fix this?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@unvalley
Thank you!
I fixed in fe46edb

@unvalley unvalley merged commit a2027da into biomejs:main Mar 23, 2024
18 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-Changelog Area: changelog A-Diagnostic Area: diagnostocis A-Linter Area: linter A-Project Area: project A-Website Area: website L-JavaScript Language: JavaScript and super languages
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants