Skip to content

Commit

Permalink
add no return check
Browse files Browse the repository at this point in the history
  • Loading branch information
JacksonTian committed Aug 21, 2020
1 parent 23477f3 commit 3ed6ebf
Show file tree
Hide file tree
Showing 4 changed files with 192 additions and 2 deletions.
57 changes: 57 additions & 0 deletions lib/semantic.js
Expand Up @@ -683,6 +683,15 @@ class TypeChecker {
local: local
};
this.visitFunctionBody(ast.functionBody, env);

if (returnType.tag === Tag.TYPE && returnType.lexeme === 'void') {
// no check for void
return;
}

if (!this.hasReturnStmt(ast.functionBody.stmts)) {
this.error(`no return statement`, ast.functionName);
}
}
}

Expand All @@ -691,6 +700,46 @@ class TypeChecker {
this.visitStmts(ast.stmts, env);
}

hasReturnStmt(ast) {
assert.equal(ast.type, 'stmts');

if (ast.stmts.length === 0) {
return false;
}

const stmt = ast.stmts[ast.stmts.length - 1];
if (stmt.type === 'return') {
return true;
}

if (stmt.type === 'if') {
if (!this.hasReturnStmt(stmt.stmts)) {
return false;
}

for (let index = 0; index < stmt.elseIfs.length; index++) {
const branch = stmt.elseIfs[index];
if (!this.hasReturnStmt(branch.stmts)) {
return false;
}
}

if (!stmt.elseStmts) {
return false;
}

if (!this.hasReturnStmt(stmt.elseStmts)) {
return false;
}

return true;
}

// TODO: try/catch/finally, for

return false;
}

visitAPI(ast) {
assert.equal(ast.type, 'api');
const paramMap = this.visitParams(ast.params, {
Expand All @@ -717,6 +766,14 @@ class TypeChecker {
env.inReturnsBlock = true;
env.local.set('__response', _model('$Response'));
this.visitReturnBody(ast.returns, env);
if (returnType.tag === Tag.TYPE && returnType.lexeme === 'void') {
// no check for void
return;
}

if (!this.hasReturnStmt(ast.returns.stmts)) {
this.error(`no return statement`, ast.apiName);
}
}
env.local = preEnv;
if (ast.runtimeBody) {
Expand Down
1 change: 1 addition & 0 deletions test/fixtures/import_module_call/return_module.dara
@@ -1,4 +1,5 @@
import OSS

static function test(): OSS {
return new OSS();
}
1 change: 1 addition & 0 deletions test/fixtures/import_module_call/return_module_model.dara
@@ -1,4 +1,5 @@
import OSS

static function test(): OSS.MyModel {
return new OSS.MyModel;
}
135 changes: 133 additions & 2 deletions test/semantic.test.js
Expand Up @@ -289,6 +289,7 @@ describe('semantic', function () {
var query = {
...a
};
return query;
}`, '__filename');
}).to.not.throwException();
});
Expand Down Expand Up @@ -853,7 +854,7 @@ describe('semantic', function () {
}
static function test(): GroupDetailResponse.abilities {
return new GroupDetailResponse.abilities{};
}`, '__filename');
}).to.not.throwError();

Expand Down Expand Up @@ -3833,6 +3834,7 @@ describe('semantic', function () {
32, 32,
64, 64
);
return '';
}
init() {}`, '__filename');
Expand Down Expand Up @@ -4145,7 +4147,6 @@ describe('semantic', function () {
`, '__filename');
}).to.not.throwError();


expect(function () {
parse(`
model M {
Expand Down Expand Up @@ -5358,4 +5359,134 @@ describe('semantic', function () {
}
});
});

it('no return should not ok', function () {
expect(function() {
parse(`
static function main(): string {
}`, '__filename');
}).to.throwException(function(ex) {
expect(ex).be.a(SyntaxError);
expect(ex.message).to.be('no return statement');
});
});

it('no return when void should ok', function () {
expect(function() {
parse(`
static function main(): void {
}`, '__filename');
}).to.not.throwException();
});

it('return string should ok', function () {
expect(function() {
parse(`
static function main(): string {
return '';
}`, '__filename');
}).to.not.throwException();
});

it('return string should ok', function () {
expect(function() {
parse(`
static function main(): string {
if (true) {
return '';
} else {
return '';
}
}`, '__filename');
}).to.not.throwException();

expect(function() {
parse(`
static function main(): string {
if (true) {
}
}`, '__filename');
}).to.throwException(function(ex) {
expect(ex).be.a(SyntaxError);
expect(ex.message).to.be('no return statement');
});

expect(function() {
parse(`
static function main(): string {
if (true) {
return '';
}
}`, '__filename');
}).to.throwException(function(ex) {
expect(ex).be.a(SyntaxError);
expect(ex.message).to.be('no return statement');
});

expect(function() {
parse(`
static function main(): string {
if (true) {
return '';
} else {
}
}`, '__filename');
}).to.throwException(function(ex) {
expect(ex).be.a(SyntaxError);
expect(ex.message).to.be('no return statement');
});

expect(function() {
parse(`
static function main(): string {
if (true) {
return '';
} else if (true) {
}
}`, '__filename');
}).to.throwException(function(ex) {
expect(ex).be.a(SyntaxError);
expect(ex.message).to.be('no return statement');
});

expect(function() {
parse(`
static function main(): string {
if (true) {
return '';
} else if (true) {
return '';
}
}`, '__filename');
}).to.throwException(function(ex) {
expect(ex).be.a(SyntaxError);
expect(ex.message).to.be('no return statement');
});

expect(function() {
parse(`
static function main(): string {
'';
}`, '__filename');
}).to.throwException(function(ex) {
expect(ex).be.a(SyntaxError);
expect(ex.message).to.be('no return statement');
});
});

it('no return stmt in api should not ok', function () {
expect(function() {
parse(`
api hello(): string {
return '';
} returns {
}`, '__filename');
}).to.throwException(function(ex) {
expect(ex).be.a(SyntaxError);
expect(ex.message).to.be('no return statement');
});
});
});

0 comments on commit 3ed6ebf

Please sign in to comment.