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)
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
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) )
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 | +-----------------------------------------------------+ """ )
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
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)
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
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)
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)? | +---------------------------------------------------+ """ )
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
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 | +-----------------------------------------------------+ """ )
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)
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)
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]
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. | +--------------------------------------------------------------------------+ """ )
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)
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)]) ]
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), ), ), ], ) ]
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 | +------------------------------------------------------------------------+ """ )
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 ) ], }
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, ) ], ), ]