Skip to content

Commit

Permalink
feat(lint): add rule useStrictMode (#3370)
Browse files Browse the repository at this point in the history
Co-authored-by: Victorien Elvinger <[email protected]>
  • Loading branch information
ematipico and Conaclos authored Jul 10, 2024
1 parent a77c00b commit b791bb3
Show file tree
Hide file tree
Showing 18 changed files with 260 additions and 21 deletions.
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.

101 changes: 101 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,101 @@
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.
///
/// The JavaScript [strict mode](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Strict_mode) prohibits some obsolete JavaScript syntaxes and makes some slight semantic chnmages to allow more optimizations by JavaScript engines.
/// EcmaScript modules are always in strict mode, while JavaScript scripts are by default in non-strict mode, also known as _sloppy mode_.
/// A developer can add the `"use strict"` directive at the start of a script file to enable the strict mode in that file.
///
/// Biome considers a CommonJS (`.cjs`) file as a script file.
/// By default, Biome recognizes a JavaScript file (`.js`) as a module file, except if `"type": "commonjs"` is specified in `package.json`.
///
/// ## Examples
///
/// ### Invalid
///
/// ```cjs,expect_diagnostic
/// var a = 1;
/// ```
///
/// ### Valid
///
/// ```cjs
/// "use strict";
///
/// 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
Loading

0 comments on commit b791bb3

Please sign in to comment.