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

fix: improve shadow_scope #242

Merged
merged 10 commits into from
Oct 22, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
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'));
}
```
Loading