Beispiel #1
0
def test_regression1_note_with_no_range() -> None:
    file_info = FileInfo(
        file_path="dummy.pytch",
        source_code="""\
let foo =
  let bar = 3
  baz
  bar
""",
    )
    error = Error(
        file_info=file_info,
        code=ErrorCode.UNBOUND_NAME,
        severity=Severity.ERROR,
        message="I couldn't find a binding...",
        range=Range(
            start=Position(line=2, character=2), end=Position(line=2, character=5)
        ),
        notes=[
            Note(file_info=file_info, message="Did you mean 'map' (a builtin)?"),
            Note(
                file_info=file_info,
                message="Did you mean 'bar', defined here?",
                range=Range(
                    start=Position(line=1, character=6),
                    end=Position(line=1, character=9),
                ),
            ),
        ],
    )
    lines = lines_to_string(get_error_lines(error, ascii=True))
    print(lines)
    assert (
        lines
        == """\
UNBOUND_NAME[2000] in dummy.pytch, line 3, character 3:
Error: I couldn't find a binding...
   +---------------------------------------------------+
   | dummy.pytch                                       |
 1 | let foo =                                         |
 2 |   let bar = 3                                     |
   |       ^~~ Note: Did you mean 'bar', defined here? |
 3 |   baz                                             |
   |   ^~~ Error: I couldn't find a binding...         |
 4 |   bar                                             |
   +---------------------------------------------------+
   | Note: Did you mean 'map' (a builtin)?             |
   +---------------------------------------------------+
"""
    )
Beispiel #2
0
def function_application_infer(
        env: Env, ctx: TypingContext, ty: Ty,
        function_call_expr: FunctionCallExpr) -> Tuple[Env, TypingContext, Ty]:
    """The function-application relation ⇒⇒, discussed in Dunfield 2013."""
    n_callee = function_call_expr.n_callee
    assert n_callee is not None, "should have been checked by parser"

    n_argument_list = function_call_expr.n_argument_list
    assert n_argument_list is not None, "should have been checked by parser"

    arguments = n_argument_list.arguments
    assert arguments is not None, "should have been checked by parser"

    if isinstance(ty, FunctionTy):
        if len(arguments) != len(ty.domain):
            if len(arguments) < len(ty.domain):
                error_code = ErrorCode.TOO_FEW_ARGUMENTS
            elif len(arguments) > len(ty.domain):
                error_code = ErrorCode.TOO_MANY_ARGUMENTS
            else:
                raise AssertionError("neither too many nor too few arguments")

            actual_num_arguments = count(len(arguments), "argument",
                                         "arguments")
            expected_num_arguments = count(len(ty.domain), "argument",
                                           "arguments")
            env = env.add_error(
                Error(
                    file_info=env.file_info,
                    code=error_code,
                    severity=Severity.ERROR,
                    message=(
                        f"I was expecting you to pass {expected_num_arguments} "
                        +
                        f"here, but you passed {actual_num_arguments} instead."
                    ),
                    range=env.get_range_for_node(n_argument_list),
                    notes=[
                        Note(
                            file_info=env.file_info,
                            message=(f"This is the function being called. " +
                                     f"It takes {expected_num_arguments}."),
                            range=env.get_range_for_node(n_callee),
                        )
                    ],
                ))

        for argument, argument_ty in zip(arguments, ty.domain):
            n_expr = argument.n_expr
            assert n_expr is not None, "should have been checked by parser"
            (env, ctx, _reason) = check(env, ctx, expr=n_expr, ty=argument_ty)
        return (env, ctx, ty.codomain)

    elif isinstance(ty, ExistentialTyVar):
        raise NotImplementedError()

    else:
        assert (
            False
        ), f"Unexpected function_application_infer type: {ty.__class__.__name__}"
Beispiel #3
0
def test_binder_error() -> None:
    file_info = FileInfo(
        file_path="<stdin>",
        source_code="""\
let foo =
  let bar = 3
  baz
""",
    )
    (syntax_tree, errors) = get_syntax_tree(file_info)
    assert not errors
    bindation = bind(file_info=file_info,
                     syntax_tree=syntax_tree,
                     global_scope=GLOBAL_SCOPE)
    assert bindation.errors == [
        Error(
            file_info=file_info,
            code=ErrorCode.UNBOUND_NAME,
            severity=Severity.ERROR,
            message=("I couldn't find a binding in the current scope " +
                     "with the name 'baz'."),
            range=Range(start=Position(line=2, character=2),
                        end=Position(line=2, character=5)),
            notes=[
                Note(
                    file_info=file_info,
                    message="Did you mean 'map' (a builtin)?",
                    range=None,
                ),
                Note(
                    file_info=file_info,
                    message="Did you mean 'bar', defined here?",
                    range=Range(
                        start=Position(line=1, character=6),
                        end=Position(line=1, character=9),
                    ),
                ),
            ],
        )
    ]
Beispiel #4
0
def test_diagnostics_across_multiple_files() -> None:
    file_info_1 = FileInfo(
        file_path="dummy1.pytch",
        source_code="""dummy1 line1
dummy1 line2
""",
    )
    file_info_2 = FileInfo(
        file_path="dummy2.pytch",
        source_code="""dummy2 line1
dummy2 line2
""",
    )
    error = Error(
        file_info=file_info_1,
        code=ErrorCode.NOT_A_REAL_ERROR,
        severity=Severity.ERROR,
        message="Look into this",
        range=Range(
            start=Position(line=0, character=7), end=Position(line=0, character=12)
        ),
        notes=[
            Note(
                file_info=file_info_2,
                message="This is an additional point of interest",
                range=Range(
                    start=Position(line=0, character=0),
                    end=Position(line=0, character=5),
                ),
            )
        ],
    )
    lines = lines_to_string(get_error_lines(error, ascii=True))
    print(lines)
    assert (
        lines
        == """\
NOT_A_REAL_ERROR[9001] in dummy1.pytch, line 1, character 8:
Error: Look into this
   +-----------------------------------------------------+
   | dummy1.pytch                                        |
 1 | dummy1 line1                                        |
   |        ^~~~~ Error: Look into this                  |
 2 | dummy1 line2                                        |
   +-----------------------------------------------------+
   | dummy2.pytch                                        |
 1 | dummy2 line1                                        |
   | ^~~~~ Note: This is an additional point of interest |
 2 | dummy2 line2                                        |
   +-----------------------------------------------------+
"""
    )
Beispiel #5
0
def test_print_error() -> None:
    file_info = FileInfo(
        file_path="dummy.pytch",
        source_code="""line1
  line2
  line3
  line4
""",
    )
    error = Error(
        file_info=file_info,
        code=ErrorCode.NOT_A_REAL_ERROR,
        severity=Severity.ERROR,
        message="Look into this",
        range=Range(
            start=Position(line=1, character=3), end=Position(line=2, character=2)
        ),
        notes=[
            Note(
                file_info=file_info,
                message="This is an additional point of interest",
                range=Range(
                    start=Position(line=0, character=0),
                    end=Position(line=0, character=5),
                ),
            )
        ],
    )
    lines = lines_to_string(get_error_lines(error, ascii=True))
    print(lines)
    assert (
        lines
        == """\
NOT_A_REAL_ERROR[9001] in dummy.pytch, line 2, character 4:
Error: Look into this
   +-----------------------------------------------------+
   | dummy.pytch                                         |
 1 | line1                                               |
   | ^~~~~ Note: This is an additional point of interest |
 2 |   line2                                             |
   |    ^~~~                                             |
 3 |   line3                                             |
   |   ~ Error: Look into this                           |
 4 |   line4                                             |
   +-----------------------------------------------------+
"""
    )
Beispiel #6
0
def check(env: Env, ctx: TypingContext, expr: Expr,
          ty: Ty) -> Tuple[Env, TypingContext, Optional[Reason]]:
    if isinstance(expr, LetExpr):
        # The typing rule for let-bindings is
        #
        #     Ψ ⊢ e ⇒ A   Ψ, x:A ⊢ e' ⇐ C
        #     ---------------------------  let
        #       Ψ ⊢ let x = e in e' ⇐ C
        n_pattern = expr.n_pattern
        if n_pattern is None:
            return (env, ctx, InvalidSyntaxReason())

        n_value = expr.n_value
        if n_value is None:
            return (env, ctx, InvalidSyntaxReason())
        (env, ctx, value_ty) = infer(env, ctx, n_value)
        if not isinstance(n_pattern, VariablePattern):
            raise NotImplementedError(
                "TODO: patterns other than VariablePattern not supported")
        ctx = ctx.add_pattern_ty(n_pattern, value_ty)
        if tys_equal(value_ty, VOID_TY):
            env = env.add_error(
                Error(
                    file_info=env.file_info,
                    code=ErrorCode.CANNOT_BIND_TO_VOID,
                    severity=Severity.ERROR,
                    message=
                    (f"This expression has type {ctx.ty_to_string(VOID_TY)}, "
                     + "so it cannot be bound to a variable."),
                    range=env.get_range_for_node(n_value),
                    notes=[
                        Note(
                            file_info=env.file_info,
                            message="This is the variable it's being bound to.",
                            range=env.get_range_for_node(n_pattern),
                        )
                    ],
                ))

        n_next = expr.n_body
        if n_next is None:
            return (env, ctx, InvalidSyntaxReason())

        return check(env, ctx, expr=n_next, ty=ty)

    elif isinstance(expr, DefExpr):
        # The typing rule for let-bindings is
        #
        #     Ψ ⊢ e ⇒ A   Ψ, x:A ⊢ e' ⇐ C
        #     ---------------------------  let
        #       Ψ ⊢ let x = e in e' ⇐ C
        #
        # Note that we have to adapt this for function definitions by also
        # using the rule for typing lambdas.
        n_name = expr.n_name
        if n_name is None:
            return (env, ctx, InvalidSyntaxReason())

        n_parameter_list = expr.n_parameter_list
        if n_parameter_list is None:
            return (env, ctx, InvalidSyntaxReason())

        parameters = n_parameter_list.parameters
        if parameters is None:
            raise NotImplementedError(
                "TODO(missing): raise error for missing parameters")

        (env, ctx, function_ty) = infer_function_definition(env, ctx, expr)
        n_next = expr.n_next
        if n_next is None:
            raise NotImplementedError(
                "TODO(missing): raise error for missing function body")

        # parameters_with_tys: PVector[CtxElemExprHasTy] = PVector()
        # for n_argument, argument_ty in zip(parameters, ty.domain):
        #     n_expr = n_argument.n_expr
        #     if n_expr is None:
        #         return (env, ctx, True)
        #     parameters_with_tys = parameters_with_tys.append(
        #         CtxElemExprHasTy(expr=n_expr, ty=argument_ty)
        #     )
        # ctx = ctx.add_elem(CtxElemExprsHaveTys(expr_tys=parameters_with_tys))

        assert isinstance(n_name, VariablePattern)
        ctx = ctx.add_pattern_ty(n_name, function_ty)
        return check(env, ctx, expr=n_next, ty=ty)
    else:
        (env, ctx, actual_ty) = infer(env, ctx, expr=expr)
        (env, ctx, reason) = check_subtype(env, ctx, lhs=actual_ty, rhs=ty)
        if reason is not None:
            return (env, ctx, reason)
        else:
            env = env.add_error(
                Error(
                    file_info=env.file_info,
                    code=ErrorCode.INCOMPATIBLE_TYPES,
                    severity=Severity.ERROR,
                    message=
                    (f"I was expecting this expression to have type {ctx.ty_to_string(ty)}, "
                     +
                     f"but it actually had type {ctx.ty_to_string(actual_ty)}."
                     ),
                    range=env.get_range_for_node(expr),
                    notes=[],
                ))
            return (env, ctx, reason)
Beispiel #7
0
def test_binding_defs() -> None:
    file_info = FileInfo(
        file_path="<stdin>",
        source_code="""\
def foo(bar) =>
    bar + baz
bar
""",
    )
    (syntax_tree, errors) = get_syntax_tree(file_info)
    assert not errors
    bindation = bind(file_info=file_info,
                     syntax_tree=syntax_tree,
                     global_scope=GLOBAL_SCOPE)
    [containing_def] = Query(syntax_tree).find_instances(DefExpr)
    [_,
     bar_ident_definition] = Query(syntax_tree).find_instances(VariablePattern)
    [bar_ident_use, _, _] = Query(syntax_tree).find_instances(IdentifierExpr)
    assert bar_ident_use.t_identifier is not None
    assert bar_ident_use.t_identifier.text == "bar"
    assert bindation.get(bar_ident_use) == [bar_ident_definition]
    assert bindation.errors == [
        Error(
            file_info=file_info,
            code=ErrorCode.UNBOUND_NAME,
            severity=Severity.ERROR,
            message=("I couldn't find a binding in the current scope " +
                     "with the name 'baz'."),
            range=Range(start=Position(line=1, character=10),
                        end=Position(line=1, character=13)),
            notes=[
                Note(
                    file_info=file_info,
                    message="Did you mean 'map' (a builtin)?",
                    range=None,
                ),
                Note(
                    file_info=file_info,
                    message="Did you mean 'bar', defined here?",
                    range=Range(
                        start=Position(line=0, character=8),
                        end=Position(line=0, character=11),
                    ),
                ),
            ],
        ),
        Error(
            file_info=file_info,
            code=ErrorCode.UNBOUND_NAME,
            severity=Severity.ERROR,
            message=("I couldn't find a binding in the current scope " +
                     "with the name 'bar'."),
            range=Range(start=Position(line=2, character=0),
                        end=Position(line=2, character=3)),
            notes=[
                Note(
                    file_info=file_info,
                    message="Did you mean 'map' (a builtin)?",
                    range=None,
                )
            ],
        ),
    ]