@@ -11,9 +11,10 @@ use biome_rowan::{AstNode, SyntaxNodeOptionExt, TextRange};
1111use biome_rule_options:: no_invalid_use_before_declaration:: NoInvalidUseBeforeDeclarationOptions ;
1212
1313declare_lint_rule ! {
14- /// Disallow the use of variables and function parameters before their declaration
14+ /// Disallow the use of variables, function parameters, classes, and enums before their declaration
1515 ///
16- /// JavaScript doesn't allow the use of block-scoped variables (`let`, `const`) and function parameters before their declaration.
16+ /// JavaScript doesn't allow the use of block-scoped variables (`let`, `const`), function parameters, and classes before their declaration.
17+ /// Similarly TypeScript doesn't allow the use of enums before their declaration.
1718 /// A `ReferenceError` will be thrown with any attempt to access the variable or the parameter before its declaration.
1819 ///
1920 /// The rule also reports the use of variables declared with `var` before their declarations.
@@ -40,14 +41,16 @@ declare_lint_rule! {
4041 /// function f(a = b, b = 0) {}
4142 /// ```
4243 ///
44+ /// ```js,expect_diagnostic
45+ /// new C();
46+ /// class C {}
47+ /// ```
48+ ///
4349 /// ### Valid
4450 ///
4551 /// ```js
4652 /// f();
4753 /// function f() {}
48- ///
49- /// new C();
50- /// class C {}
5154 /// ```
5255 ///
5356 /// ```js
@@ -60,6 +63,14 @@ declare_lint_rule! {
6063 /// function f() { return CONSTANT; }
6164 /// const CONSTANT = 0;
6265 /// ```
66+ ///
67+ /// ```ts
68+ /// function f() {
69+ /// new C();
70+ /// }
71+ /// let c: C;
72+ /// class C {}
73+ /// ```
6374 pub NoInvalidUseBeforeDeclaration {
6475 version: "1.5.0" ,
6576 name: "noInvalidUseBeforeDeclaration" ,
@@ -105,20 +116,21 @@ impl Rule for NoInvalidUseBeforeDeclaration {
105116 let Ok ( declaration_kind) = DeclarationKind :: try_from ( & declaration) else {
106117 continue ;
107118 } ;
108- let declaration_end = declaration. range ( ) . end ( ) ;
109- let declaration_control_flow_root =
110- if let AnyJsBindingDeclaration :: JsVariableDeclarator ( declarator) = declaration
111- . parent_binding_pattern_declaration ( )
112- . unwrap_or ( declaration)
113- {
114- declarator
115- . syntax ( )
116- . ancestors ( )
117- . skip ( 1 )
118- . find ( |ancestor| AnyJsControlFlowRoot :: can_cast ( ancestor. kind ( ) ) )
119- } else {
120- None
121- } ;
119+ let declaration_end = if matches ! (
120+ declaration_kind,
121+ DeclarationKind :: Class | DeclarationKind :: Enum
122+ ) {
123+ // A class can be instantiated by its properties.
124+ // Enum members can be qualified by the enum name.
125+ id. range ( ) . end ( )
126+ } else {
127+ declaration. range ( ) . end ( )
128+ } ;
129+ let declaration_control_flow_root = declaration
130+ . syntax ( )
131+ . ancestors ( )
132+ . skip ( 1 )
133+ . find ( |ancestor| AnyJsControlFlowRoot :: can_cast ( ancestor. kind ( ) ) ) ;
122134 for reference in binding. all_references ( ) {
123135 if reference. range_start ( ) < declaration_end {
124136 let reference_syntax = reference. syntax ( ) ;
@@ -177,6 +189,8 @@ impl Rule for NoInvalidUseBeforeDeclaration {
177189 binding_range : declaration_range,
178190 } = state;
179191 let declaration_kind_text = match declaration_kind {
192+ DeclarationKind :: Class => "class" ,
193+ DeclarationKind :: Enum => "enum" ,
180194 DeclarationKind :: EnumMember => "enum member" ,
181195 DeclarationKind :: Parameter => "parameter" ,
182196 DeclarationKind :: Variable => "variable" ,
@@ -202,8 +216,10 @@ pub struct InvalidUseBeforeDeclaration {
202216 binding_range : TextRange ,
203217}
204218
205- #[ derive( Debug , Copy , Clone ) ]
219+ #[ derive( Debug , Copy , Clone , Eq , PartialEq ) ]
206220pub enum DeclarationKind {
221+ Class ,
222+ Enum ,
207223 EnumMember ,
208224 Parameter ,
209225 Variable ,
@@ -220,9 +236,8 @@ impl TryFrom<&AnyJsBindingDeclaration> for DeclarationKind {
220236 | AnyJsBindingDeclaration :: JsArrayBindingPatternRestElement ( _)
221237 | AnyJsBindingDeclaration :: JsObjectBindingPatternProperty ( _)
222238 | AnyJsBindingDeclaration :: JsObjectBindingPatternRest ( _)
223- | AnyJsBindingDeclaration :: JsObjectBindingPatternShorthandProperty ( _) => {
224- Ok ( Self :: Variable )
225- }
239+ | AnyJsBindingDeclaration :: JsObjectBindingPatternShorthandProperty ( _)
240+ | AnyJsBindingDeclaration :: TsImportEqualsDeclaration ( _) => Ok ( Self :: Variable ) ,
226241 AnyJsBindingDeclaration :: JsVariableDeclarator ( declarator) => {
227242 if let Some ( var_decl) = declarator. declaration ( )
228243 && let Some ( var_decl_clause) = var_decl. parent :: < JsVariableDeclarationClause > ( )
@@ -239,6 +254,21 @@ impl TryFrom<&AnyJsBindingDeclaration> for DeclarationKind {
239254 AnyJsBindingDeclaration :: JsFormalParameter ( _)
240255 | AnyJsBindingDeclaration :: JsRestParameter ( _)
241256 | AnyJsBindingDeclaration :: TsPropertyParameter ( _) => Ok ( Self :: Parameter ) ,
257+ AnyJsBindingDeclaration :: JsClassDeclaration ( _)
258+ | AnyJsBindingDeclaration :: JsClassExportDefaultDeclaration ( _) => {
259+ if value. parent :: < TsDeclareStatement > ( ) . is_some ( ) {
260+ Err ( ( ) )
261+ } else {
262+ Ok ( Self :: Class )
263+ }
264+ }
265+ AnyJsBindingDeclaration :: TsEnumDeclaration ( _) => {
266+ if value. parent :: < TsDeclareStatement > ( ) . is_some ( ) {
267+ Err ( ( ) )
268+ } else {
269+ Ok ( Self :: Enum )
270+ }
271+ }
242272 // Other declarations allow use before definition
243273 AnyJsBindingDeclaration :: JsArrowFunctionExpression ( _)
244274 | AnyJsBindingDeclaration :: JsBogusParameter ( _)
@@ -249,20 +279,16 @@ impl TryFrom<&AnyJsBindingDeclaration> for DeclarationKind {
249279 | AnyJsBindingDeclaration :: JsFunctionDeclaration ( _)
250280 | AnyJsBindingDeclaration :: JsFunctionExpression ( _)
251281 | AnyJsBindingDeclaration :: TsDeclareFunctionDeclaration ( _)
252- | AnyJsBindingDeclaration :: JsClassDeclaration ( _)
253282 | AnyJsBindingDeclaration :: JsClassExpression ( _)
254283 | AnyJsBindingDeclaration :: TsInterfaceDeclaration ( _)
255284 | AnyJsBindingDeclaration :: TsTypeAliasDeclaration ( _)
256- | AnyJsBindingDeclaration :: TsEnumDeclaration ( _)
257285 | AnyJsBindingDeclaration :: TsExternalModuleDeclaration ( _)
258286 | AnyJsBindingDeclaration :: TsModuleDeclaration ( _)
259287 | AnyJsBindingDeclaration :: JsShorthandNamedImportSpecifier ( _)
260288 | AnyJsBindingDeclaration :: JsNamedImportSpecifier ( _)
261289 | AnyJsBindingDeclaration :: JsBogusNamedImportSpecifier ( _)
262290 | AnyJsBindingDeclaration :: JsDefaultImportSpecifier ( _)
263291 | AnyJsBindingDeclaration :: JsNamespaceImportSpecifier ( _)
264- | AnyJsBindingDeclaration :: TsImportEqualsDeclaration ( _)
265- | AnyJsBindingDeclaration :: JsClassExportDefaultDeclaration ( _)
266292 | AnyJsBindingDeclaration :: JsFunctionExportDefaultDeclaration ( _)
267293 | AnyJsBindingDeclaration :: TsDeclareFunctionExportDefaultDeclaration ( _)
268294 | AnyJsBindingDeclaration :: JsCatchDeclaration ( _) => Err ( ( ) ) ,
0 commit comments