def test_redundant_empty_only_list(): ''' Check that we drop 'use's with an empty only list if they become redundant. #TODO #11 Check for appropriate logging messages here once logging is implemented. ''' fake_parent = KernelSchedule("dummy_schedule") processor = Fparser2Reader() # Empty only-list followed by wildcard import reader = FortranStringReader("use mod1, only:\n" "use mod1\n") fparser2spec = Fortran2003.Specification_Part(reader) processor.process_declarations(fake_parent, fparser2spec.content, []) csym = fake_parent.symbol_table.lookup("mod1") assert csym.wildcard_import # Wildcard import followed by empty only-list reader = FortranStringReader("use mod2\n" "use mod2, only:\n") fparser2spec = Fortran2003.Specification_Part(reader) processor.process_declarations(fake_parent, fparser2spec.content, []) csym = fake_parent.symbol_table.lookup("mod2") assert csym.wildcard_import # Empty only-list followed by named import reader = FortranStringReader("use mod3, only:\n" "use mod3, only: fred\n") fparser2spec = Fortran2003.Specification_Part(reader) processor.process_declarations(fake_parent, fparser2spec.content, []) sym_table = fake_parent.symbol_table csym = sym_table.lookup("mod3") assert not csym.wildcard_import assert sym_table.imported_symbols(csym)[0].name == "fred" # Named import followed by empty only-list reader = FortranStringReader("use mod4, only: bob\n" "use mod4, only:\n") fparser2spec = Fortran2003.Specification_Part(reader) processor.process_declarations(fake_parent, fparser2spec.content, []) csym = sym_table.lookup("mod4") assert not csym.wildcard_import assert sym_table.imported_symbols(csym)[0].name == "bob"
def test_parse_derived_type(use_stmt, type_name): ''' Check that the fronted correctly creates a DataTypeSymbol of type StructureType from the declaration of a derived type. ''' fake_parent = KernelSchedule("dummy_schedule") symtab = fake_parent.symbol_table processor = Fparser2Reader() reader = FortranStringReader("{0}\n" "type :: my_type\n" " integer :: flag\n" " type({1}), private :: grid\n" " real, dimension(3) :: posn\n" "end type my_type\n" "type(my_type) :: var\n".format( use_stmt, type_name)) fparser2spec = Fortran2003.Specification_Part(reader) processor.process_declarations(fake_parent, fparser2spec.content, []) sym = symtab.lookup("my_type") assert isinstance(sym, DataTypeSymbol) assert isinstance(sym.datatype, StructureType) flag = sym.datatype.lookup("flag") assert isinstance(flag.datatype, ScalarType) assert flag.visibility == Symbol.Visibility.PUBLIC grid = sym.datatype.lookup("grid") assert isinstance(grid.datatype, DataTypeSymbol) assert isinstance(grid.datatype.datatype, DeferredType) assert grid.visibility == Symbol.Visibility.PRIVATE posn = sym.datatype.lookup("posn") assert isinstance(posn.datatype, ArrayType) var = symtab.lookup("var") assert var.datatype is sym
def test_multi_use_stmt(): ''' Check that we handle the case where different symbols are imported from a module in separate USE statements. ''' fake_parent = KernelSchedule("dummy_schedule") processor = Fparser2Reader() reader = FortranStringReader("use my_mod, only: some_var\n" "use this_mod\n" "use my_mod, only: var1, var2\n" "use this_mod, only: var3\n") fparser2spec = Fortran2003.Specification_Part(reader) processor.process_declarations(fake_parent, fparser2spec.content, []) symtab = fake_parent.symbol_table csymbols = symtab.containersymbols # Although there are 4 use statements, there are only 2 modules assert len(csymbols) == 2 my_mod = symtab.lookup("my_mod") assert not my_mod.wildcard_import # Check that we have accumulated all imports import_list = symtab.imported_symbols(my_mod) assert len(import_list) == 3 names = [sym.name for sym in import_list] assert sorted(names) == ["some_var", "var1", "var2"] this_mod = symtab.lookup("this_mod") assert this_mod.wildcard_import names = [sym.name for sym in symtab.imported_symbols(this_mod)] assert names == ["var3"]
def test_use_stmt(): ''' Check that SymbolTable entries are correctly created from module use statements. ''' fake_parent = KernelSchedule("dummy_schedule") processor = Fparser2Reader() reader = FortranStringReader("use my_mod, only: some_var\n" "use this_mod\n" "use other_mod, only: var1, var2\n") fparser2spec = Fortran2003.Specification_Part(reader) processor.process_declarations(fake_parent, fparser2spec.content, []) symtab = fake_parent.symbol_table for module_name in ["my_mod", "this_mod", "other_mod"]: container = symtab.lookup(module_name) assert isinstance(container, ContainerSymbol) assert container.name == module_name # Container reference is not updated until explicitly requested assert not container._reference for var in ["some_var", "var1", "var2"]: assert symtab.lookup(var).name == var assert symtab.lookup("some_var").interface.container_symbol \ == symtab.lookup("my_mod") assert symtab.lookup("var2").interface.container_symbol \ == symtab.lookup("other_mod")
def test_missing_derived_type(): ''' Check that the fronted raises an error if it encounters a variable of a derived type that cannot be resolved. ''' fake_parent = KernelSchedule("dummy_schedule") processor = Fparser2Reader() reader = FortranStringReader("type(my_type) :: var") fparser2spec = Fortran2003.Specification_Part(reader) # This should raise an error because there's no Container from which # the definition of 'my_type' can be brought into scope. with pytest.raises(SymbolError) as err: processor.process_declarations(fake_parent, fparser2spec.content, []) assert "No Symbol found for name 'my_type'" in str(err.value)
def test_use_no_only_list(): ''' Check that we create the correct Symbol Table entry for a use statement that has an 'only' clause but no list of imported symbols. ''' fake_parent = KernelSchedule("dummy_schedule") processor = Fparser2Reader() reader = FortranStringReader("use my_mod, only: some_var\n" "use some_mod, only:\n") fparser2spec = Fortran2003.Specification_Part(reader) processor.process_declarations(fake_parent, fparser2spec.content, []) some_mod = fake_parent.symbol_table.lookup("some_mod") assert not some_mod.wildcard_import assert fake_parent.symbol_table.imported_symbols(some_mod) == []
def test_name_clash_use_stmt(): ''' Check that we raise the expected error if we encounter a module with a name that's already taken in the Symbol Table. This is invalid Fortran but we need to test the error-handling in PSyclone. ''' fake_parent = KernelSchedule("dummy_schedule") processor = Fparser2Reader() reader = FortranStringReader("use my_mod, only: some_var\n" "use some_var, only: var1, var2\n") fparser2spec = Fortran2003.Specification_Part(reader) with pytest.raises(SymbolError) as err: processor.process_declarations(fake_parent, fparser2spec.content, []) assert "Found a USE of module 'some_var' but the symbol" in str(err.value)
def test_broken_use(monkeypatch): ''' Check that we raise the expected error if we encounter an unrecognised parse tree for a USE statement. ''' fake_parent = KernelSchedule("dummy_schedule") processor = Fparser2Reader() reader = FortranStringReader("use some_mod, only:\n") fparser2spec = Fortran2003.Specification_Part(reader) # Break the parse tree so that instead of ", ONLY:" it has "hello" monkeypatch.setattr( fparser2spec.content[0], "items", [None, None, Fortran2003.Name('my_mod'), 'hello', None]) with pytest.raises(NotImplementedError) as err: processor.process_declarations(fake_parent, fparser2spec.content, []) assert "unsupported USE statement: 'USE my_modhello'" in str(err.value)
def test_use_stmt_error(monkeypatch): ''' Check that we raise the expected error if the parse tree representing a USE statement doesn't have the expected structure. ''' fake_parent = KernelSchedule("dummy_schedule") processor = Fparser2Reader() reader = FortranStringReader("use my_mod, only: some_var\n" "use this_mod\n" "use other_mod, only: var1, var2\n") fparser2spec = Fortran2003.Specification_Part(reader) monkeypatch.setattr(fparser2spec.content[0], "items", [None, "hello", None]) with pytest.raises(GenerationError) as err: processor.process_declarations(fake_parent, fparser2spec.content, []) assert ("Expected the parse tree for a USE statement to contain 5 items " "but found 3 for 'hello'" in str(err.value))
def test_deferred_derived_type(type_name): ''' Check that we get a symbol with a type given by a DataTypeSymbol (which itself is of DeferredType) for a declaration using an unresolved derived type. ''' fake_parent = KernelSchedule("dummy_schedule") symtab = fake_parent.symbol_table processor = Fparser2Reader() reader = FortranStringReader("use my_mod\n" "type({0}) :: var".format(type_name)) fparser2spec = Fortran2003.Specification_Part(reader) processor.process_declarations(fake_parent, fparser2spec.content, []) vsym = symtab.lookup("var") assert isinstance(vsym.datatype, DataTypeSymbol) assert isinstance(vsym.datatype.datatype, DeferredType) tsym = symtab.lookup("my_type") assert isinstance(tsym, DataTypeSymbol)
def test_use_local_symbol_error(): ''' Check that we raise the expected error if we encounter an import of a symbol that is already declared to be local. ''' from psyclone.psyir.symbols import DataSymbol, LocalInterface, \ INTEGER_SINGLE_TYPE fake_parent = KernelSchedule("dummy_schedule") # In practise this situation is hard to trigger as USE statements must # come before local declarations. Therefore we manually add a symbol # to the table first. fake_parent.symbol_table.add( DataSymbol("fred", INTEGER_SINGLE_TYPE, interface=LocalInterface())) processor = Fparser2Reader() reader = FortranStringReader("use mod2, only: fred\n") fparser2spec = Fortran2003.Specification_Part(reader) with pytest.raises(SymbolError) as err: processor.process_declarations(fake_parent, fparser2spec.content, []) assert "'fred' is imported from module 'mod2' but is" in str(err.value)
def test_derived_type_self_ref(type_name): ''' Test that we can parse a derived type that contains a pointer reference of that same type. The 'pointer' attribute is not supported so we get a DataTypeSymbol of UnknownFortranType. ''' fake_parent = KernelSchedule("dummy_schedule") symtab = fake_parent.symbol_table processor = Fparser2Reader() reader = FortranStringReader("type :: my_type\n" " type({0}), pointer :: next => null()\n" " integer :: flag\n" "end type my_type\n" "type({0}) :: var\n".format(type_name)) fparser2spec = Fortran2003.Specification_Part(reader) processor.process_declarations(fake_parent, fparser2spec.content, []) sym = symtab.lookup("my_type") assert isinstance(sym, DataTypeSymbol) assert isinstance(sym.datatype, UnknownFortranType) assert symtab.lookup("var").datatype is sym
def test_use_same_symbol(): ''' Check that we handle the case where the same symbol is imported from different modules. #TODO #11 Once logging is added, check that we log an appropriate warning for this case. ''' fake_parent = KernelSchedule("dummy_schedule") processor = Fparser2Reader() reader = FortranStringReader("use mod2, only: fred\n" "use mod3, only: fred\n") fparser2spec = Fortran2003.Specification_Part(reader) processor.process_declarations(fake_parent, fparser2spec.content, []) csym = fake_parent.symbol_table.lookup("mod2") assert fake_parent.symbol_table.imported_symbols(csym)[0].name == "fred" csym = fake_parent.symbol_table.lookup("mod3") # mod3 will have an empty list of symbols as 'fred' is already imported # from mod2. assert not fake_parent.symbol_table.imported_symbols(csym)
def test_derived_type_accessibility(): ''' Check that accessibility statements/attributes within a derived type are handled correctly. ''' fake_parent = KernelSchedule("dummy_schedule") symtab = fake_parent.symbol_table processor = Fparser2Reader() reader = FortranStringReader("type :: my_type\n" " private\n" " integer :: flag\n" " real, public :: scale\n" "end type my_type\n") fparser2spec = Fortran2003.Specification_Part(reader) processor.process_declarations(fake_parent, fparser2spec.content, []) sym = symtab.lookup("my_type") assert isinstance(sym, DataTypeSymbol) flag = sym.datatype.lookup("flag") assert flag.visibility == Symbol.Visibility.PRIVATE scale = sym.datatype.lookup("scale") assert scale.visibility == Symbol.Visibility.PUBLIC