Skip to content

Commit

Permalink
fix: improve shadow_scope (#242)
Browse files Browse the repository at this point in the history
  • Loading branch information
morgante authored Oct 22, 2024
1 parent f04a793 commit fe2a4e1
Show file tree
Hide file tree
Showing 4 changed files with 110 additions and 31 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,43 +2,16 @@
tags: [utility, test]
---

## Test shadow scope

This tests the `shadows_identifier` pattern by finding all cases where a variable is shadowed.
## Shadow Identifiers

This tests the `scope_for_identifier` pattern by finding all cases where a variable is shadowed.

```grit
language js
// Implementation
pattern shadows_identifier($name) {
or {
statement_block($statements) where {
$statements <: some variable($declarations) where {
$declarations <: contains variable_declarator(name=$name)
}
},
arrow_function($parameters) where {
$parameters <: contains $name
},
function_declaration($parameters) where {
$parameters <: contains $name
},
for_in_statement() as $statement where {
$statement <: contains $name
},
for_statement() as $statement where {
$statement <: contains $name
},
`try { $_ } catch($catch) { $_ }` where {
$catch <: contains $name
},
}
}
// Test case
file($body) where {
$body <: contains bubble shadows_identifier(`x`) as $scope where {
$body <: contains bubble identifier_scope(`x`) as $scope where {
$scope <: contains `x` => `shadowed`
}
}
Expand Down Expand Up @@ -68,6 +41,28 @@ function notShadowingVar() {
shadowingExample();
```

## Anonymous function variable definition

```js
var shadowingExample = function (x) {
console.log(x);
};
var notShadowingVar = function (y) {
console.log(y);
};
shadowingExample();
```

```js
var shadowingExample = function (shadowed) {
console.log(shadowed);
};
var notShadowingVar = function (y) {
console.log(y);
};
shadowingExample();
```

## If statement

```js
Expand Down
2 changes: 1 addition & 1 deletion .grit/patterns/js/replace_wildcard_imports.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ pattern replace_wildcard_import() {
`import * as $alias from $src` as $import where {
$refs = [],
$kept_refs = [],
$program <: contains used_alias($alias, $refs, $kept_refs) until shadows_identifier(name=$alias),
$program <: contains used_alias($alias, $refs, $kept_refs) until identifier_scope(name=$alias),
$refs = distinct($refs),
$joined_refs = join($refs, `, `),
// Try the different scenarios
Expand Down
28 changes: 28 additions & 0 deletions .grit/patterns/js/shadow_scope.grit
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@

pattern identifier_scope($name) {
or {
statement_block($statements) where {
$statements <: some variable($declarations) where {
$declarations <: contains variable_declarator(name=$name)
}
},
function($parameters) where {
$parameters <: contains $name
},
arrow_function($parameters) where {
$parameters <: contains $name
},
function_declaration($parameters) where {
$parameters <: contains $name
},
for_in_statement() as $statement where {
$statement <: contains $name
},
for_statement() as $statement where {
$statement <: contains $name
},
`try { $_ } catch($catch) { $_ }` where {
$catch <: contains $name
},
}
}
56 changes: 56 additions & 0 deletions .grit/patterns/js/variable_scoping.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
---
tags:
- docs
- full-examples
---

# Variable Scoping with `identifier_scope`

Default Grit patterns are not generally aware of variable scoping, but you can use the `identifier_scope` pattern to find (or exclude) [scopes](https://developer.mozilla.org/en-US/docs/Glossary/Scope) where an identifier has been _locally_ defined.

This is most often used when you want to target an import from a shared module but exclude scopes where the identifier is shadowed locally.

For example, this pattern would rename `t` from the `translation` library to `translate` unless `t` is shadowed locally:

```grit
language js
`t` as $t => `translate` where {
$t <: imported_from(from=`"translation"`),
$t <: not within identifier_scope(name=`t`)
}
```

Here is a simple example file where `t` is shadowed locally:

```js
import { t } from 'translation';

console.log(t('hello world'));

function normal() {
console.log(t('hello world'));
}

// t is an argument to this function, so the global t is not used and we should *not* rename it here.
function shadowed(t) {
console.log(t('hello world'));
}
```

When we rewrite it, the shadowed `t` is not renamed:

```js
import { translate } from 'translation';

console.log(translate('hello world'));

function normal() {
console.log(translate('hello world'));
}

// t is an argument to this function, so the global t is not used and we should *not* rename it here.
function shadowed(t) {
console.log(t('hello world'));
}
```

0 comments on commit fe2a4e1

Please sign in to comment.