visitInstanceCall()

in lib/semantic.js [2380:2462]


  visitInstanceCall(ast, env) {
    assert.equal(ast.left.type, 'instance_call');
    const type = this.getVariableType(ast.left.id, env);
    let moduleName = type.name;
    if(isBasicType(type.type)) {
      moduleName = this.getBuiltinModule(type.type);
      ast.builtinModule = moduleName;
      ast.left.id.moduleType = {
        type: 'basic',
        name: type.type,
      };
    } else if(type.type === 'basic' && isBasicType(type.name)) {
      moduleName = this.getBuiltinModule(type.name);
      ast.builtinModule = moduleName;
      ast.left.id.moduleType = {
        type: 'basic',
        name: type.name,
      };
    } else if(type.type === 'model') {
      moduleName = '$ModelInstance';
      ast.builtinModule = moduleName;
      ast.left.id.moduleType = {
        type: 'model',
        name: type.name,
      };
    } else if(builtin.has(moduleName)) {
      ast.builtinModule = moduleName;
      ast.left.id.moduleType = {
        type: 'builtin_module',
        name: moduleName,
      };
    } else if (ast.left.id.tag === Tag.ID) {
      moduleName = type.name;
      ast.left.id.moduleType = {
        type: 'module',
        name: moduleName
      };
    }

    const checker = this.getChecker(moduleName);
    const method = ast.left.propertyPath[0];
    const name = method.lexeme;
    const def = checker.getInstanceMethod(name, moduleName);
    if (!def) {
      this.error(`the instance function/api "${name}" is undefined in ${moduleName}`, method);
    }

    const { method: definedApi, module: moduleNameOfDef } = def;

    const actual = [];
    for (let i = 0; i < ast.args.length; i++) {
      const arg = ast.args[i];
      this.visitExpr(arg, env);
      const type = this.getExprType(arg, env);
      actual.push(type);
    }

    const expected = this.getParameterTypes(definedApi, moduleNameOfDef);
    if (!eql(expected, actual)) {
      this.error(`the parameter` +
        ` types are mismatched. expected ` +
        `${moduleName}.${name}(${expected.map((item) => display(item)).join(', ')}), but ` +
        `${moduleName}.${name}(${actual.map((item) => display(item)).join(', ')})`, method);
    }

    for (let i = 0; i < ast.args.length; i++) {
      const arg = ast.args[i];
      arg.needCast = isNeedToMap(expected[i], arg.inferred);
      arg.expectedType = expected[i];
    }


    if(this.checkMagicType(definedApi.returnType)) {
      const magicType = this.getInstanceType(ast.left.id, env);
      ast.inferred = this.getMagicType(definedApi.returnType, magicType);
    } else {
      ast.inferred = this.getType(definedApi.returnType, moduleName);
    }

    ast.isAsync = definedApi.isAsync;
    ast.isStatic = definedApi.isStatic;
    ast.hasThrow = definedApi.isAsync || definedApi.hasThrow;
  }