예제 #1
0
def make_result(input_filename: str, source_code: str,
                capsys: Any) -> CaseResult:
    file_info = FileInfo(file_path=input_filename, source_code=source_code)
    (syntax_tree, syntax_errors) = get_syntax_tree(file_info=file_info)
    bindation = bind(file_info=file_info,
                     syntax_tree=syntax_tree,
                     global_scope=BINDER_GLOBAL_SCOPE)

    typeation = typecheck(
        file_info=file_info,
        syntax_tree=syntax_tree,
        bindation=bindation,
        global_scope=TYPE_SYSTEM_GLOBAL_SCOPE,
    )

    function_calls = Query(
        syntax_tree=syntax_tree).find_instances(FunctionCallExpr)
    show_type_exprs: List[Expr] = []
    for function_call in function_calls:
        n_callee = function_call.n_callee
        if n_callee is None:
            continue

        text = n_callee.text
        if text != "show_type":
            continue

        n_argument_list = function_call.n_argument_list
        assert n_argument_list is not None
        arguments = n_argument_list.arguments
        assert arguments is not None
        assert len(arguments) == 1
        argument = arguments[0].n_expr
        assert isinstance(argument, Expr)
        show_type_exprs.append(argument)

    output = ""
    for show_type_expr in show_type_exprs:
        line_num = (file_info.get_range_from_offset_range(
            show_type_expr.offset_range).start.line + 1)
        type_info = typeation.ctx.get_infers(expr=show_type_expr)
        output += f"line {line_num}: {type_info!r}\n"

    error_lines = []
    errors: List[Error] = syntax_errors + bindation.errors + typeation.errors
    for i in errors:
        error_lines.extend(get_error_lines(i, ascii=True))

    error: Optional[str]
    if error_lines:
        error = "".join(line + "\n" for line in error_lines)
    else:
        error = None

    return CaseResult(output=output, error=error)
예제 #2
0
def test_fileinfo_get_position_for_offset_exclusive_end():
    source_code = """foo
barbaz
qux"""
    file_info = FileInfo(file_path="dummy", source_code=source_code)

    expected_position = slower_get_position_for_offset(
        source_code=source_code, offset=len(source_code)
    )
    actual_position = file_info.get_position_for_offset(len(source_code))
    assert actual_position.line == expected_position.line
    assert actual_position.character == expected_position.character
예제 #3
0
def test_fileinfo_get_range_from_offset_range():
    source_code = """foo
barbaz
qux
"""
    file_info = FileInfo(file_path="dummy", source_code=source_code)

    offset_range = OffsetRange(start=0, end=len(source_code))
    range = file_info.get_range_from_offset_range(offset_range)
    assert range == Range(
        start=Position(line=0, character=0), end=Position(line=3, character=0)
    )
예제 #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                                        |
   +-----------------------------------------------------+
"""
    )
예제 #5
0
def test_fileinfo_get_position_for_offset():
    source_code = """foo
barbaz
qux"""
    file_info = FileInfo(file_path="dummy", source_code=source_code)

    for i, _c in enumerate(source_code):
        expected_position = slower_get_position_for_offset(
            source_code=source_code, offset=i
        )
        actual_position = file_info.get_position_for_offset(i)
        assert actual_position.line == expected_position.line
        assert actual_position.character == expected_position.character
예제 #6
0
def make_result(input_filename: str, source_code: str,
                capsys: Any) -> CaseResult:
    run_file(FileInfo(file_path=input_filename, source_code=source_code))
    (output, error) = capsys.readouterr()
    if not error:
        error = None
    return CaseResult(output=output, error=error)
예제 #7
0
def test_cst_query_errors() -> None:
    file_info = FileInfo(
        file_path="dummy.pytch",
        source_code="""\
let foo =
""",
    )
    (syntax_tree, errors) = get_syntax_tree(file_info)
    assert errors
예제 #8
0
파일: fuzz.py 프로젝트: pytch-lang/pytch
def main() -> None:
    afl.init()
    with open(sys.argv[1]) as f:
        # afl-fuzz will often generate invalid Unicode and count that as a
        # crash. See
        # https://barro.github.io/2018/01/taking-a-look-at-python-afl/
        try:
            file_info = FileInfo(file_path="<stdin>", source_code=f.read())
        except UnicodeDecodeError:
            pass
        else:
            check_for_buggy_parse(file_info)
    os._exit(0)
예제 #9
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)?             |
   +---------------------------------------------------+
"""
    )
예제 #10
0
def test_cst_query() -> None:
    file_info = FileInfo(
        file_path="dummy.pytch",
        source_code="""\
let foo =
  let bar = 3
  bar
""",
    )
    (syntax_tree, errors) = get_syntax_tree(file_info)
    assert not errors

    let_exprs = list(Query(syntax_tree).find_instances(LetExpr))
    assert len(let_exprs) == 2
예제 #11
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                                             |
   +-----------------------------------------------------+
"""
    )
예제 #12
0
def make_result(input_filename: str, source_code: str,
                capsys: Any) -> CaseResult:
    file_info = FileInfo(file_path=input_filename, source_code=source_code)
    lexation = lex(file_info=file_info)
    output = render_token_stream(lexation.tokens)

    error_lines = []
    errors: List[Error] = lexation.errors
    for i in errors:
        error_lines.extend(get_error_lines(i, ascii=True))

    error: Optional[str]
    if error_lines:
        error = "".join(line + "\n" for line in error_lines)
    else:
        error = None

    return CaseResult(output=output, error=error)
예제 #13
0
def make_result(input_filename: str, source_code: str,
                capsys: Any) -> CaseResult:
    (compiled_output, errors) = compile_file(
        FileInfo(file_path=input_filename, source_code=source_code))

    if compiled_output is None:
        compiled_output = ""

    error_lines = []
    for i in errors:
        error_lines.extend(get_error_lines(i, ascii=True))

    error: Optional[str]
    if error_lines:
        error = "".join(line + "\n" for line in error_lines)
    else:
        error = None

    return CaseResult(output=compiled_output, error=error)
예제 #14
0
def test_binder() -> None:
    file_info = FileInfo(
        file_path="<stdin>",
        source_code="""\
let foo =
  let bar = 3
  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)
    [outer_let, inner_let] = Query(syntax_tree).find_instances(LetExpr)
    [bar_ident] = Query(syntax_tree).find_instances(IdentifierExpr)
    assert bar_ident.t_identifier is not None
    assert bar_ident.t_identifier.text == "bar"
    assert bindation.get(bar_ident) == [inner_let.n_pattern]
예제 #15
0
def test_regression2() -> None:
    file_info = FileInfo(
        file_path="test/typesystem/statement.warning.pytch",
        source_code="""\
if True
then
  1
  None
""",
    )
    error = Error(
        file_info=file_info,
        code=ErrorCode.NOT_A_REAL_ERROR,
        severity=Severity.WARNING,
        message="The value of this expression is being thrown away, which might indicate a bug.",
        range=Range(
            start=Position(line=0, character=0), end=Position(line=3, character=6)
        ),
        notes=[],
    )
    lines = lines_to_string(get_error_lines(error, ascii=True))
    print(lines)
    assert (
        lines
        == """\
NOT_A_REAL_ERROR[9001] in test/typesystem/statement.warning.pytch, line 1, character 1:
Warning: The value of this expression is being thrown away, which might
indicate a bug.
   +--------------------------------------------------------------------------+
   | test/typesystem/statement.warning.pytch                                  |
 1 | if True                                                                  |
   | ^~~~~~~                                                                  |
 2 | then                                                                     |
   | ~~~~                                                                     |
 3 |   1                                                                      |
   |   ~                                                                      |
 4 |   None                                                                   |
   |   ~~~~ Warning: The value of this expression is being thrown away, which |
   | might indicate a bug.                                                    |
   +--------------------------------------------------------------------------+
"""
    )
예제 #16
0
def make_result(input_filename: str, source_code: str,
                capsys: Any) -> CaseResult:
    file_info = FileInfo(file_path=input_filename, source_code=source_code)
    lexation = lex(file_info=file_info)
    parsation = parse(file_info=file_info, tokens=lexation.tokens)
    offset, rendered_st_lines = dump_syntax_tree(source_code,
                                                 parsation.green_cst)
    output = "".join(line + "\n" for line in rendered_st_lines)

    error_lines = []
    errors: List[Error] = lexation.errors + parsation.errors
    for i in errors:
        error_lines.extend(get_error_lines(i, ascii=True))

    error: Optional[str]
    if error_lines:
        error = "".join(line + "\n" for line in error_lines)
    else:
        error = None

    return CaseResult(output=output, error=error)
예제 #17
0
def test_merge_contexts() -> None:
    file_info = FileInfo(file_path="dummy.pytch", source_code="foo")
    contexts = [
        _DiagnosticContext(file_info=file_info, line_ranges=[(2, 4)]),
        _DiagnosticContext(file_info=file_info, line_ranges=[(1, 3)]),
        _DiagnosticContext(file_info=file_info, line_ranges=None),
        _DiagnosticContext(file_info=file_info, line_ranges=[(2, 3)]),
    ]
    assert list(_merge_contexts(contexts)) == [
        _DiagnosticContext(file_info=file_info, line_ranges=[(1, 4)]),
        _DiagnosticContext(file_info=file_info, line_ranges=None),
    ]

    contexts = [
        _DiagnosticContext(file_info=file_info, line_ranges=[(1, 3)]),
        _DiagnosticContext(file_info=file_info, line_ranges=[(3, 4)]),
        _DiagnosticContext(file_info=file_info, line_ranges=[(10, 11)]),
    ]
    assert list(_merge_contexts(contexts)) == [
        _DiagnosticContext(file_info=file_info, line_ranges=[(1, 4), (10, 11)])
    ]
예제 #18
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),
                    ),
                ),
            ],
        )
    ]
예제 #19
0
def test_wrap_message() -> None:
    file_info = FileInfo(
        file_path="dummy.pytch",
        source_code="""line1
  line2
  line3
  line4
""",
    )
    long_message = ("xxxx " * (80 // len("xxxx "))) + "y."
    error = Error(
        file_info=file_info,
        code=ErrorCode.NOT_A_REAL_ERROR,
        severity=Severity.ERROR,
        message=long_message,
        range=Range(
            start=Position(line=1, character=3), end=Position(line=2, character=2)
        ),
        notes=[],
    )
    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: xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx
xxxx xxxx y.
   +------------------------------------------------------------------------+
   | dummy.pytch                                                            |
 1 | line1                                                                  |
 2 |   line2                                                                |
   |    ^~~~                                                                |
 3 |   line3                                                                |
   |   ~ Error: xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx |
   | xxxx xxxx xxxx xxxx y.                                                 |
 4 |   line4                                                                |
   +------------------------------------------------------------------------+
"""
    )
예제 #20
0
def test_get_diagnostic_lines_to_insert() -> None:
    file_info = FileInfo(file_path="dummy.pytch", source_code="foo\nbar\nbaz\n")
    error = Error(
        file_info=file_info,
        code=ErrorCode.NOT_A_REAL_ERROR,
        severity=Severity.ERROR,
        message="An error message",
        notes=[],
        range=Range(
            start=Position(line=1, character=1), end=Position(line=2, character=0)
        ),
    )
    color = error.color
    context = _DiagnosticContext(file_info=file_info, line_ranges=[(0, 3)])
    assert _get_diagnostic_lines_to_insert(
        output_env=get_output_env(ascii=True), context=context, diagnostics=[error]
    ) == {
        1: [_MessageLine(text=" ^~", color=color, is_wrappable=False)],
        2: [
            _MessageLine(
                text="~ Error: An error message", color=color, is_wrappable=True
            )
        ],
    }
예제 #21
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,
                )
            ],
        ),
    ]