Skip to content

Commit

Permalink
Fixes using upper namespaced function indirectly (#1319)
Browse files Browse the repository at this point in the history
* Fixes using upper namespaced function indirectly

* Added test to verify validating correct function

* Clarifies namespace docs
  • Loading branch information
markwpearce authored Oct 9, 2024
1 parent 9cde191 commit 91e55c6
Show file tree
Hide file tree
Showing 4 changed files with 168 additions and 5 deletions.
46 changes: 42 additions & 4 deletions docs/namespaces.md
Original file line number Diff line number Diff line change
Expand Up @@ -110,14 +110,18 @@ end sub
```
</details>

## Sharing name of namespaced item and non-namespaced item is prohibited
The compiler will throw an error whenever it encounters a namespaced function with the same name as a global function. The same rule applies to classes.
## Sharing name of namespaced item and non-namespaced items
The compiler allows a namespaced function with the same name as a global function. As per the [name-shadowing](./variable-shadowing.md) rules, the function inside the namespace will be used in the transpiled code. The same rule applies to classes.

```BrighterScript
sub Quack()
end sub
namespace Vertibrates.Birds
sub Quack() ' this will result in a compile error.
sub Quack()
end sub
sub Speak()
Quack() ' calls the function Vertibrates.Birds.Quack()
end sub
end namespace
```
Expand All @@ -128,7 +132,10 @@ end namespace
```BrightScript
sub Quack()
end sub
sub Vertibrates_Birds_Quack() ' this will result in a compile error.
sub Vertibrates_Birds_Quack()
end sub
sub Vertibrates_Birds_Speak()
Vertibrates_Birds_Quack() ' calls the function Vertibrates.Birds.Quack()
end sub
```
</details>
Expand Down Expand Up @@ -161,3 +168,34 @@ sub Vertibrates_Reptiles_Hiss()
end sub
```
</details>

## Calling parent namespace functions

Brighterscript does not support accessing a function (or other entity) of a parent namespace without fully qualifying the name of the function.

```BrighterScript
namespace Vertibrates
sub Move()
end sub
namespace Birds
sub Fly()
Move() ' this is an error - no global Move() function
Vertibrates.Move() ' this is allowed
end sub
end namespace
end namespace
```

<details>
<summary>View the transpiled BrightScript code</summary>

```BrightScript
sub Vertibrates_Move()
end sub
sub Vertibrates_Birds_Fly()
Move() ' this is an error - no global Move() function
Vertibrates_Move() ' this is allowed
end sub
```
</details>
84 changes: 84 additions & 0 deletions src/bscPlugin/validation/ScopeValidator.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,30 @@ describe('ScopeValidator', () => {
//should have an error
expectZeroDiagnostics(program);
});

it('validates against scope-defined func in inner namespace, when outer namespace has same named func', () => {
program.setFile('source/main.bs', `
namespace alpha
sub foo()
end sub
namespace beta
sub bar()
foo()
end sub
end namespace
end namespace
function foo(x as integer) as integer
return x
end function
`);

program.validate();
expectDiagnostics(program, [
DiagnosticMessages.mismatchArgumentCount(1, 0).message
]);
});
});

describe('argumentTypeMismatch', () => {
Expand Down Expand Up @@ -1839,6 +1863,66 @@ describe('ScopeValidator', () => {
program.validate();
expectZeroDiagnostics(program);
});

it('has error when referencing something in outer namespace directly', () => {
program.setFile('source/main.bs', `
namespace alpha
sub foo()
end sub
namespace beta
sub bar()
foo()
end sub
end namespace
end namespace
`);

program.validate();
expectDiagnostics(program, [
DiagnosticMessages.cannotFindFunction('foo').message
]);
});

it('allows referencing something in outer namespace with namespace in front', () => {
program.setFile('source/main.bs', `
namespace alpha
sub foo()
end sub
namespace beta
sub bar()
alpha.foo()
end sub
end namespace
end namespace
`);

program.validate();
expectZeroDiagnostics(program);
});

it('allows referencing scope-defined func in inner namespace, when outer namespace has same named func', () => {
program.setFile('source/main.bs', `
namespace alpha
sub foo()
end sub
namespace beta
sub bar()
foo(1)
end sub
end namespace
end namespace
function foo(x as integer) as integer
return x
end function
`);

program.validate();
expectZeroDiagnostics(program);
});
});

describe('itemCannotBeUsedAsVariable', () => {
Expand Down
41 changes: 41 additions & 0 deletions src/files/BrsFile.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2344,6 +2344,47 @@ describe('BrsFile', () => {
);
});

it('transpiles namespaced functions when used as variables', async () => {
await testTranspile(`
namespace Vertibrates.Birds
function GetAllBirds()
return [
GetDuck(),
GetGoose()
]
end function
function GetDuck()
end function
function GetGoose()
end function
function Test()
duckGetter = Vertibrates.Birds.GetDuck
gooseGetter = GetGoose
end function
end namespace`, `
function Vertibrates_Birds_GetAllBirds()
return [
Vertibrates_Birds_GetDuck()
Vertibrates_Birds_GetGoose()
]
end function
function Vertibrates_Birds_GetDuck()
end function
function Vertibrates_Birds_GetGoose()
end function
function Vertibrates_Birds_Test()
duckGetter = Vertibrates_Birds_GetDuck
gooseGetter = Vertibrates_Birds_GetGoose
end function
`);
});

});

describe('shadowing', () => {
Expand Down
2 changes: 1 addition & 1 deletion src/parser/Statement.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1879,7 +1879,7 @@ export class NamespaceStatement extends Statement implements TypedefProvider {
};
this.nameExpression = options.nameExpression;
this.body = options.body;
this.symbolTable = new SymbolTable(`NamespaceStatement: '${this.name}'`, () => this.parent?.getSymbolTable());
this.symbolTable = new SymbolTable(`NamespaceStatement: '${this.name}'`, () => this.getRoot()?.getSymbolTable());
}

public readonly tokens: {
Expand Down

0 comments on commit 91e55c6

Please sign in to comment.