diff --git a/src/parser/nmodl.yy b/src/parser/nmodl.yy index 344aa4ad63..1c04119ab8 100644 --- a/src/parser/nmodl.yy +++ b/src/parser/nmodl.yy @@ -565,7 +565,6 @@ units : { unit : "(" { scanner.scan_unit(); } ")" { - // @todo Empty units should be handled in semantic analysis auto unit = scanner.get_unit(); auto text = unit->eval(); $$ = new ast::Unit(unit); diff --git a/src/visitors/semantic_analysis_visitor.cpp b/src/visitors/semantic_analysis_visitor.cpp index 5464398790..1b47e9c14b 100644 --- a/src/visitors/semantic_analysis_visitor.cpp +++ b/src/visitors/semantic_analysis_visitor.cpp @@ -7,6 +7,7 @@ #include "ast/string.hpp" #include "ast/suffix.hpp" #include "ast/table_statement.hpp" +#include "ast/unit.hpp" #include "symtab/symbol_properties.hpp" #include "utils/logger.hpp" #include "visitors/visitor_utils.hpp" @@ -163,5 +164,13 @@ void SemanticAnalysisVisitor::visit_mutex_unlock(const ast::MutexUnlock& /* node /// --> } +void SemanticAnalysisVisitor::visit_unit(const ast::Unit& node) { + /// <-- This code is for check 8 + if (node.get_name()->get_value().empty()) { + logger->error("SemanticAnalysisVisitor:: An unit cannot be created without name."); + check_fail = true; + } +} + } // namespace visitor } // namespace nmodl diff --git a/src/visitors/semantic_analysis_visitor.hpp b/src/visitors/semantic_analysis_visitor.hpp index bb7bdbf2dc..94616a37ba 100644 --- a/src/visitors/semantic_analysis_visitor.hpp +++ b/src/visitors/semantic_analysis_visitor.hpp @@ -29,8 +29,9 @@ * 3. A TABLE statement in functions cannot have name list, and should have one in procedures. * 4. Check if ion variables from a `USEION` statement are not declared in `CONSTANT` block. * 5. Check if an independent variable is not 't'. - * 6. Check that mutex are not badly use + * 6. Check that mutex are not badly use. * 7. Check than function table got at least one argument. + * 8. Check that unit always have a name. */ #include "ast/ast.hpp" #include "visitors/ast_visitor.hpp" @@ -82,6 +83,9 @@ class SemanticAnalysisVisitor: public ConstAstVisitor { /// Look if MUTEXUNLOCK is outside a locked block void visit_mutex_unlock(const ast::MutexUnlock& node) override; + /// Visit an unit and check that name is not empty + void visit_unit(const ast::Unit& node) override; + public: SemanticAnalysisVisitor(bool accel_backend = false) : accel_backend(accel_backend) {} diff --git a/test/unit/visitor/semantic_analysis.cpp b/test/unit/visitor/semantic_analysis.cpp index 5dc5f1a44f..5d208e22dd 100644 --- a/test/unit/visitor/semantic_analysis.cpp +++ b/test/unit/visitor/semantic_analysis.cpp @@ -165,3 +165,34 @@ SCENARIO("FUNCTION_TABLE block", "[visitor][semantic_analysis]") { } } } + +SCENARIO("UNITS block", "[visitor][semantic_analysis]") { + GIVEN("A mod file with UNITS empty") { + std::string nmodl_text = R"( + UNITS {} + )"; + THEN("Semantic analysis should success") { + REQUIRE_FALSE(run_semantic_analysis_visitor(nmodl_text)); + } + } + GIVEN("A mod file with UNITS with space as name") { + std::string nmodl_text = R"( + UNITS { + ( ) = (millivolt) + } + )"; + THEN("Semantic analysis should success") { + REQUIRE_FALSE(run_semantic_analysis_visitor(nmodl_text)); + } + } + GIVEN("A mod file with UNITS with no name") { + std::string nmodl_text = R"( + UNITS { + () = (millivolt) + } + )"; + THEN("Semantic analysis should fail") { + REQUIRE(run_semantic_analysis_visitor(nmodl_text)); + } + } +}