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): add rule useStrictMode #3370

Merged
merged 4 commits into from
Jul 10, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 29 additions & 8 deletions crates/biome_configuration/src/linter/rules.rs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions crates/biome_diagnostics_categories/src/categories.rs
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,7 @@ define_categories! {
"lint/nursery/useNumberToFixedDigitsArgument": "https://biomejs.dev/linter/rules/use-number-to-fixed-digits-argument",
"lint/nursery/useSemanticElements": "https://biomejs.dev/linter/rules/use-semantic-elements",
"lint/nursery/useSortedClasses": "https://biomejs.dev/linter/rules/use-sorted-classes",
"lint/nursery/useStrictMode": "https://biomejs.dev/linter/rules/use-strict-mode",
"lint/nursery/useThrowNewError": "https://biomejs.dev/linter/rules/use-throw-new-error",
"lint/nursery/useThrowOnlyError": "https://biomejs.dev/linter/rules/use-throw-only-error",
"lint/nursery/useTopLevelRegex": "https://biomejs.dev/linter/rules/use-top-level-regex",
Expand Down
2 changes: 2 additions & 0 deletions crates/biome_js_analyze/src/lint/nursery.rs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

94 changes: 94 additions & 0 deletions crates/biome_js_analyze/src/lint/nursery/use_strict_mode.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
use crate::JsRuleAction;
use biome_analyze::{
context::RuleContext, declare_lint_rule, ActionCategory, Ast, FixKind, Rule, RuleDiagnostic,
};
use biome_console::markup;
use biome_js_factory::make::{
js_directive, js_directive_list, jsx_string_literal, jsx_string_literal_single_quotes,
};
use biome_js_syntax::{JsFileSource, JsScript};
use biome_rowan::{AstNode, AstNodeList, BatchMutationExt};

declare_lint_rule! {
/// Enforce the use of the directive `"use strict"` in script files.
///
Conaclos marked this conversation as resolved.
Show resolved Hide resolved
ematipico marked this conversation as resolved.
Show resolved Hide resolved
/// ## Examples
///
/// ### Invalid
///
/// ```cjs,expect_diagnostic
/// var a = 1;
/// ```
///
/// ### Valid
///
/// ```cjs
/// "use strict"
ematipico marked this conversation as resolved.
Show resolved Hide resolved
///
/// var a = 1;
/// ```
///
pub UseStrictMode {
version: "1.8.0",
name: "useStrictMode",
language: "js",
recommended: true,
fix_kind: FixKind::Safe,
}
}

impl Rule for UseStrictMode {
type Query = Ast<JsScript>;
type State = ();
type Signals = Option<Self::State>;
type Options = ();

fn run(ctx: &RuleContext<Self>) -> Self::Signals {
let node = ctx.query();
let file_source = ctx.source_type::<JsFileSource>();

if node.directives().is_empty() && file_source.is_script() {
Some(())
} else {
None
}
}

fn diagnostic(ctx: &RuleContext<Self>, _state: &Self::State) -> Option<RuleDiagnostic> {
let node = ctx.query();
Some(
RuleDiagnostic::new(
rule_category!(),
node.range(),
markup! {
"Unexpected absence of the directive "<Emphasis>"\"use strict\"."</Emphasis>
},
)
.note(markup! {
"Strict mode allows to opt-in some optimisations of the runtime engines, and it eliminates some JavaScript silent errors by changing them to throw errors."
})
.note(markup!{
"Check the "<Hyperlink href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Strict_mode">"for more information regarding strict mode."</Hyperlink>
}),
)
}

fn action(ctx: &RuleContext<Self>, _state: &Self::State) -> Option<JsRuleAction> {
let node = ctx.query().clone();
let mut mutation = ctx.root().begin();
let value = if ctx.as_preferred_quote().is_double() {
jsx_string_literal("use strict")
} else {
jsx_string_literal_single_quotes("use strict")
};
let directives = js_directive_list(vec![js_directive(value).build()]);
let new_node = node.clone().with_directives(directives);
mutation.replace_node(node, new_node);
Some(JsRuleAction::new(
ActionCategory::QuickFix,
ctx.metadata().applicability(),
markup!("Insert a top level"<Emphasis>"\"use strict\" "</Emphasis>".").to_owned(),
mutation,
))
}
}
2 changes: 2 additions & 0 deletions crates/biome_js_analyze/src/options.rs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

20 changes: 15 additions & 5 deletions crates/biome_js_analyze/tests/spec_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ use biome_analyze::{AnalysisFilter, AnalyzerAction, ControlFlow, Never, RuleFilt
use biome_diagnostics::advice::CodeSuggestionAdvice;
use biome_diagnostics::{DiagnosticExt, Severity};
use biome_js_parser::{parse, JsParserOptions};
use biome_js_syntax::{JsFileSource, JsLanguage};
use biome_js_syntax::{JsFileSource, JsLanguage, ModuleKind};
use biome_project::PackageType;
use biome_rowan::AstNode;
use biome_test_utils::{
assert_errors_are_absent, code_fix_to_string, create_analyzer_options, diagnostic_to_string,
Expand Down Expand Up @@ -92,20 +93,29 @@ fn run_test(input: &'static str, _: &str, _: &str, _: &str) {
pub(crate) fn analyze_and_snap(
snapshot: &mut String,
input_code: &str,
source_type: JsFileSource,
mut source_type: JsFileSource,
filter: AnalysisFilter,
file_name: &str,
input_file: &Path,
check_action_type: CheckActionType,
parser_options: JsParserOptions,
) -> usize {
let mut diagnostics = Vec::new();
let mut code_fixes = Vec::new();
let manifest = load_manifest(input_file, &mut diagnostics);

if let Some(manifest) = &manifest {
if manifest.r#type == Some(PackageType::Commonjs) &&
// At the moment we treat JS and JSX at the same way
(source_type.file_extension() == "js" || source_type.file_extension() == "jsx" )
{
source_type.set_module_kind(ModuleKind::Script)
}
}
let parsed = parse(input_code, source_type, parser_options.clone());
let root = parsed.tree();

let mut diagnostics = Vec::new();
let mut code_fixes = Vec::new();
let options = create_analyzer_options(input_file, &mut diagnostics);
let manifest = load_manifest(input_file, &mut diagnostics);

let (_, errors) =
biome_js_analyze::analyze(&root, filter, &options, source_type, manifest, |event| {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@


function f() {
return "lorem ipsum"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
---
source: crates/biome_js_analyze/tests/spec_tests.rs
expression: invalid.js
---
# Input
```jsx


function f() {
return "lorem ipsum"
}

```

# Diagnostics
```
invalid.js:3:1 lint/nursery/useStrictMode FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

! Unexpected absence of the directive "use strict".

> 3 │ function f() {
│ ^^^^^^^^^^^^^^
> 4 │ return "lorem ipsum"
> 5 │ }
│ ^
6 │

i Strict mode allows to opt-in some optimisations of the runtime engines, and it eliminates some JavaScript silent errors by changing them to throw errors.

i Check the for more information regarding strict mode.

i Safe fix: Insert a top level"use strict" .

1 1 │
2 2 │
3 │ + "use·strict"
4 │ +
3 5 │ function f() {
4 6 │ return "lorem ipsum"


```
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"type": "commonjs"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
/* should not generate diagnostics */
// var a = 1;
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
source: crates/biome_js_analyze/tests/spec_tests.rs
expression: valid.js
---
# Input
```jsx
/* should not generate diagnostics */
// var a = 1;
```
8 changes: 8 additions & 0 deletions crates/biome_js_syntax/src/file_source.rs
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,10 @@ impl JsFileSource {
self
}

pub fn set_module_kind(&mut self, kind: ModuleKind) {
self.module_kind = kind;
}

pub const fn with_version(mut self, version: LanguageVersion) -> Self {
self.version = version;
self
Expand Down Expand Up @@ -248,6 +252,10 @@ impl JsFileSource {
self.module_kind.is_module()
}

pub const fn is_script(&self) -> bool {
self.module_kind.is_script()
}

pub const fn is_typescript(&self) -> bool {
self.language.is_typescript()
}
Expand Down
2 changes: 1 addition & 1 deletion crates/biome_project/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use biome_diagnostics::serde::Diagnostic;
use biome_parser::diagnostic::ParseDiagnostic;
use biome_rowan::Language;
pub use license::generated::*;
pub use node_js_project::{Dependencies, NodeJsProject, PackageJson};
pub use node_js_project::{Dependencies, NodeJsProject, PackageJson, PackageType};
use std::any::TypeId;
use std::fmt::Debug;
use std::path::Path;
Expand Down
Loading
Loading