From 2355a6ebbae5864cd3cc2a2a6d90107fb278832c Mon Sep 17 00:00:00 2001 From: Tristan Carel Date: Thu, 28 Sep 2023 18:05:31 +0200 Subject: [PATCH] Improve generated C++ header files in the `nmodl::ast` namespace (#1080) * Improve generated C++ headers: * Use nested namespace definitions * Improve indentation for readability * Prefer passing by reference rather than copy when possible for `std::shared_ptr` and `std::vector` * CI: * CodeCov: ignore jinja templates * SonarSource: ignore a couple of directories / files (submodules, ...) * in bison parser, catch exception by reference, not copy --- codecov.yaml | 2 + docs/DoxygenLayout.xml | 1 - sonar-project.properties | 1 + src/codegen/codegen_cpp_visitor.cpp | 1 - src/codegen/codegen_transform_visitor.cpp | 3 +- src/language/nodes.py | 160 ++++++------ src/language/templates/ast/ast.cpp | 9 +- src/language/templates/ast/ast.hpp | 20 +- .../templates/ast/node_class.template | 245 ++++++++---------- src/language/templates/pybind/pyast.hpp | 4 +- src/parser/unit.yy | 2 +- 11 files changed, 207 insertions(+), 241 deletions(-) create mode 100644 codecov.yaml diff --git a/codecov.yaml b/codecov.yaml new file mode 100644 index 0000000000..43ee99ef88 --- /dev/null +++ b/codecov.yaml @@ -0,0 +1,2 @@ +ignore: +- src/language/templates/ diff --git a/docs/DoxygenLayout.xml b/docs/DoxygenLayout.xml index 258ae9d3b0..97ed605034 100644 --- a/docs/DoxygenLayout.xml +++ b/docs/DoxygenLayout.xml @@ -4,7 +4,6 @@ - diff --git a/sonar-project.properties b/sonar-project.properties index 45c27e0d98..bf1c1814b7 100644 --- a/sonar-project.properties +++ b/sonar-project.properties @@ -6,6 +6,7 @@ sonar.organization=bluebrain #sonar.projectVersion=1.0 sonar.python.version=3.10.12 +sonar.exclusions=ext/**, nmodl/ext/**, docs/notebooks/tree.js # Path is relative to the sonar-project.properties file. Replace "\" by "/" on Windows. #sonar.sources=. diff --git a/src/codegen/codegen_cpp_visitor.cpp b/src/codegen/codegen_cpp_visitor.cpp index a83aad5ce7..e661f5685b 100644 --- a/src/codegen/codegen_cpp_visitor.cpp +++ b/src/codegen/codegen_cpp_visitor.cpp @@ -473,7 +473,6 @@ bool CodegenCppVisitor::need_semicolon(const Statement& node) { || node.is_else_statement() || node.is_from_statement() || node.is_verbatim() - || node.is_from_statement() || node.is_conductance_hint() || node.is_while_statement() || node.is_protect_statement() diff --git a/src/codegen/codegen_transform_visitor.cpp b/src/codegen/codegen_transform_visitor.cpp index f06f5fbd35..6d95bd7f52 100644 --- a/src/codegen/codegen_transform_visitor.cpp +++ b/src/codegen/codegen_transform_visitor.cpp @@ -21,7 +21,8 @@ void CodegenTransformVisitor::visit_function_block(FunctionBlock& node) { auto table_statements = collect_nodes(node, {AstNodeType::TABLE_STATEMENT}); for (auto t: table_statements) { auto t_ = std::dynamic_pointer_cast(t); - t_->set_table_vars({std::make_shared(new String(node.get_node_name()))}); + t_->set_table_vars( + {std::make_shared(std::make_shared(node.get_node_name()))}); } } } // namespace nmodl diff --git a/src/language/nodes.py b/src/language/nodes.py index 7c6d77f144..fc45edf181 100644 --- a/src/language/nodes.py +++ b/src/language/nodes.py @@ -199,6 +199,8 @@ def get_typename(self): def get_rvalue_typename(self): """returns rvalue reference type of the node""" typename = self.get_typename() + if self.is_vector: + return "const " + typename + "&" if self.is_base_type_node and not self.is_integral_type_node or self.is_ptr_excluded_node: return "const " + typename + "&" return typename @@ -242,7 +244,7 @@ def _is_member_type_wrapped_as_shared_pointer(self): def member_rvalue_typename(self): """returns rvalue reference type when used as returned or parameter type""" typename = self.member_typename - if not self.is_integral_type_node and not self._is_member_type_wrapped_as_shared_pointer: + if not self.is_integral_type_node: return "const " + typename + "&" return typename @@ -250,55 +252,55 @@ def get_add_methods_declaration(self): s = '' if self.add_method: method = f""" - /** - * \\brief Add member to {self.varname} by raw pointer - */ - void emplace_back_{to_snake_case(self.class_name)}({self.class_name} *n); - - /** - * \\brief Add member to {self.varname} by shared_ptr - */ - void emplace_back_{to_snake_case(self.class_name)}(std::shared_ptr<{self.class_name}> n); - - /** - * \\brief Erase member to {self.varname} - */ - {self.class_name}Vector::const_iterator erase_{to_snake_case(self.class_name)}({self.class_name}Vector::const_iterator first); - - /** - * \\brief Erase members to {self.varname} - */ - {self.class_name}Vector::const_iterator erase_{to_snake_case(self.class_name)}({self.class_name}Vector::const_iterator first, {self.class_name}Vector::const_iterator last); - - /** - * \\brief Erase non-consecutive members to {self.varname} - * - * loosely following the cpp reference of remove_if - */ - size_t erase_{to_snake_case(self.class_name)}(std::unordered_set<{self.class_name}*>& to_be_erased); - - /** - * \\brief Insert member to {self.varname} - */ - {self.class_name}Vector::const_iterator insert_{to_snake_case(self.class_name)}({self.class_name}Vector::const_iterator position, const std::shared_ptr<{self.class_name}>& n); - - /** - * \\brief Insert members to {self.varname} - */ - template - void insert_{to_snake_case(self.class_name)}({self.class_name}Vector::const_iterator position, NodeType& to, InputIterator first, InputIterator last); - - /** - * \\brief Reset member to {self.varname} - */ - void reset_{to_snake_case(self.class_name)}({self.class_name}Vector::const_iterator position, {self.class_name}* n); - - /** - * \\brief Reset member to {self.varname} - */ - void reset_{to_snake_case(self.class_name)}({self.class_name}Vector::const_iterator position, std::shared_ptr<{self.class_name}> n); - """ - s = textwrap.dedent(method) + /** + * \\brief Add member to {self.varname} by raw pointer + */ + void emplace_back_{to_snake_case(self.class_name)}({self.class_name} *n); + + /** + * \\brief Add member to {self.varname} by shared_ptr + */ + void emplace_back_{to_snake_case(self.class_name)}(std::shared_ptr<{self.class_name}> n); + + /** + * \\brief Erase member to {self.varname} + */ + {self.class_name}Vector::const_iterator erase_{to_snake_case(self.class_name)}({self.class_name}Vector::const_iterator first); + + /** + * \\brief Erase members to {self.varname} + */ + {self.class_name}Vector::const_iterator erase_{to_snake_case(self.class_name)}({self.class_name}Vector::const_iterator first, {self.class_name}Vector::const_iterator last); + + /** + * \\brief Erase non-consecutive members to {self.varname} + * + * loosely following the cpp reference of remove_if + */ + size_t erase_{to_snake_case(self.class_name)}(std::unordered_set<{self.class_name}*>& to_be_erased); + + /** + * \\brief Insert member to {self.varname} + */ + {self.class_name}Vector::const_iterator insert_{to_snake_case(self.class_name)}({self.class_name}Vector::const_iterator position, const std::shared_ptr<{self.class_name}>& n); + + /** + * \\brief Insert members to {self.varname} + */ + template + void insert_{to_snake_case(self.class_name)}({self.class_name}Vector::const_iterator position, NodeType& to, InputIterator first, InputIterator last); + + /** + * \\brief Reset member to {self.varname} + */ + void reset_{to_snake_case(self.class_name)}({self.class_name}Vector::const_iterator position, {self.class_name}* n); + + /** + * \\brief Reset member to {self.varname} + */ + void reset_{to_snake_case(self.class_name)}({self.class_name}Vector::const_iterator position, std::shared_ptr<{self.class_name}> n); + """ + s = textwrap.indent(textwrap.dedent(method), ' ') return s def get_add_methods_definition(self, parent): @@ -440,12 +442,12 @@ def get_node_name_method_declaration(self): * in case of this ast::{self.class_name} has {self.varname} designated as a * node name. * - * @return name of the node as std::string + * \\return name of the node as std::string * * \\sa Ast::get_node_type_name */ std::string get_node_name() const override;""" - s = textwrap.dedent(method) + s = textwrap.indent(textwrap.dedent(method), ' ') return s def get_node_name_method_definition(self, parent): @@ -461,42 +463,40 @@ def get_node_name_method_definition(self, parent): def get_getter_method(self, class_name): getter_method = self.getter_method if self.getter_method else "get_" + to_snake_case(self.varname) - getter_override = " override" if self.getter_override else "" + getter_override = "override" if self.getter_override else "" return_type = self.member_rvalue_typename - return f""" - /** - * \\brief Getter for member variable \\ref {class_name}.{self.varname} - */ - {return_type} {getter_method}() const noexcept {getter_override} {{ - return {self.varname}; - }} - """ + return textwrap.indent(textwrap.dedent(f""" + /** + * \\brief Getter for member variable \\ref {class_name}.{self.varname} + */ + {return_type} {getter_method}() const noexcept {getter_override} {{ + return {self.varname}; + }} + """), ' ') def get_setter_method_declaration(self, class_name): setter_method = "set_" + to_snake_case(self.varname) setter_type = self.member_typename reference = "" if self.is_base_type_node else "&&" if self.is_base_type_node: - return f""" - /** - * \\brief Setter for member variable \\ref {class_name}.{self.varname} - */ - void {setter_method}({setter_type} {self.varname}); - """ + return textwrap.indent(textwrap.dedent(f""" + /** + * \\brief Setter for member variable \\ref {class_name}.{self.varname} + */ + void {setter_method}({setter_type} {self.varname}); + """), ' ') else: - return f""" - /** - * \\brief Setter for member variable \\ref {class_name}.{self.varname} (rvalue reference) - */ - void {setter_method}({setter_type}&& {self.varname}); - - /** - * \\brief Setter for member variable \\ref {class_name}.{self.varname} - */ - void {setter_method}(const {setter_type}& {self.varname}); - """ - - + return textwrap.indent(textwrap.dedent(f""" + /** + * \\brief Setter for member variable \\ref {class_name}.{self.varname} (rvalue reference) + */ + void {setter_method}({setter_type}&& {self.varname}); + + /** + * \\brief Setter for member variable \\ref {class_name}.{self.varname} + */ + void {setter_method}(const {setter_type}& {self.varname}); + """), ' ') def get_setter_method_definition(self, class_name): setter_method = "set_" + to_snake_case(self.varname) diff --git a/src/language/templates/ast/ast.cpp b/src/language/templates/ast/ast.cpp index 04ff475381..360d37dc97 100644 --- a/src/language/templates/ast/ast.cpp +++ b/src/language/templates/ast/ast.cpp @@ -17,8 +17,7 @@ * \brief Auto generated AST classes implementations */ -namespace nmodl { -namespace ast { +namespace nmodl::ast { /// /// Ast member function definition @@ -30,7 +29,7 @@ std::string Ast::get_node_name() const { throw std::logic_error("get_node_name() not implemented"); } -std::shared_ptr Ast::get_statement_block() const { +const std::shared_ptr& Ast::get_statement_block() const { throw std::runtime_error("get_statement_block not implemented"); } @@ -219,7 +218,5 @@ void Ast::set_parent(Ast* p) { {% endfor %} {% endfor %} - -} // namespace ast -} // namespace nmodl +} // namespace nmodl::ast diff --git a/src/language/templates/ast/ast.hpp b/src/language/templates/ast/ast.hpp index 6e9e4b18a4..5af0e551aa 100644 --- a/src/language/templates/ast/ast.hpp +++ b/src/language/templates/ast/ast.hpp @@ -29,8 +29,7 @@ #include "utils/common_utils.hpp" #include "visitors/visitor.hpp" -namespace nmodl { -namespace ast { +namespace nmodl::ast { /** * \page ast_design Design of Abstract Syntax Tree (AST) @@ -50,10 +49,10 @@ namespace ast { */ /** - * @defgroup ast_class AST Classes - * @ingroup ast - * @brief Classes for implementing Abstract Syntax Tree (AST) - * @{ + * \defgroup ast_class AST Classes + * \ingroup ast + * \brief Classes for implementing Abstract Syntax Tree (AST) + * \{ */ /** @@ -122,7 +121,7 @@ struct Ast: public std::enable_shared_from_this { * This type name can be returned as a std::string for printing * ast to text/json form. * - * @return name of the node type as a string + * \return name of the node type as a string * * \sa Ast::get_node_name */ @@ -273,7 +272,7 @@ struct Ast: public std::enable_shared_from_this { * * \sa ast::StatementBlock */ - virtual std::shared_ptr get_statement_block() const; + virtual const std::shared_ptr& get_statement_block() const; /** * \brief Set symbol table for the AST node @@ -342,7 +341,7 @@ struct Ast: public std::enable_shared_from_this { /** *\brief Parent setter * - * Usually, the parent parent pointer cannot be set in the constructor + * Usually, the parent pointer cannot be set in the constructor * because children are generally build BEFORE the parent. Conversely, * we set children parents directly in the parent constructor using * set_parent_in_children() @@ -352,5 +351,4 @@ struct Ast: public std::enable_shared_from_this { virtual void set_parent(Ast* p); }; -} // namespace ast -} // namespace nmodl +} // namespace nmodl::ast diff --git a/src/language/templates/ast/node_class.template b/src/language/templates/ast/node_class.template index 736601591d..87c6c9f590 100644 --- a/src/language/templates/ast/node_class.template +++ b/src/language/templates/ast/node_class.template @@ -2,19 +2,16 @@ this Jinja template is not used to directly generate a file but included by other templates. #} - {# add virtual qualifier if node is an abstract class #} {% macro virtual(node) -%} {% if node.is_abstract %} virtual {% endif %} {% endmacro %} - -namespace nmodl { -namespace ast { +namespace nmodl::ast { /** - * @addtogroup ast_class - * @ingroup ast - * @{ + * \addtogroup ast_class + * \ingroup ast + * \{ */ /** @@ -28,61 +25,52 @@ class {{ node.class_name }} : public {{ node.base_class }} { {% for member in node.private_members() %} {{ '/// ' + member[3] }} {% if member[2] is none %} - {{ member[0] }} {{ member[1] }}; + {{ member[0] }} {{ member[1] }}; {% else %} - {{ member[0] }} {{ member[1] }} = {{ member[2] }}; + {{ member[0] }} {{ member[1] }} = {{ member[2] }}; {% endif %} {% endfor %} {% endif %} public: - {% for member in node.public_members() %} + {%- for member in node.public_members() %} {{ '/// ' + member[3] }} {% if member[2] is none %} - {{ member[0] }} {{ member[1] }}; + {{ member[0] }} {{ member[1] }}; {% else %} - {{ member[0] }} {{ member[1] }} = {{ member[2] }}; + {{ member[0] }} {{ member[1] }} = {{ member[2] }}; {% endif %} - {% endfor %} + {%- endfor %} /// \name Ctor & dtor /// \{ - {% if node.children %} {{ node.ctor_declaration() }} {% if node.has_ptr_children() %} - {{ node.ctor_shrptr_declaration() }} + {{ node.ctor_shrptr_declaration() }} {% endif %} {{ node.class_name }}(const {{ node.class_name }}& obj); {% endif %} - {% if node.requires_default_constructor %} {{ node.class_name}}() = default; {% endif %} - - virtual ~{{ node.class_name }}() = default; - + virtual ~{{ node.class_name }}() = default; /// \} - {% if node.is_base_block_node or node.is_number_node %} /// \name Not implemented /// \{ {% endif %} - {% if node.is_base_block_node %} virtual const ArgumentVector& get_parameters() const { throw std::runtime_error("get_parameters not implemented"); } {% endif %} - {% if node.is_number_node %} {{ virtual(node) }}double to_double() { throw std::runtime_error("to_double not implemented"); } {% endif %} - {{ "/// \}" if node.is_base_block_node or node.is_number_node else "" }} - /** * \brief Check if the ast node is an instance of ast::{{ node.class_name }} * \return true as object is of type ast::{{ node.class_name }} @@ -99,7 +87,7 @@ class {{ node.class_name }} : public {{ node.base_class }} { * passes like nmodl::visitor::InlineVisitor where nodes are cloned in the * ast. * - * @return pointer to the clone/copy of the current node + * \return pointer to the clone/copy of the current node */ // NOLINTBEGIN(clang-analyzer-cplusplus.NewDeleteLeaks) {{ virtual(node) }} {{ node.class_name }}* clone() const override { @@ -171,120 +159,105 @@ class {{ node.class_name }} : public {{ node.base_class }} { } {% if node.has_token %} - /** - * \brief Return associated token for the current ast node - * - * Not all ast nodes have token information. For example, nmodl::visitor::NeuronSolveVisitor - * can insert new nodes in the ast as a solution of ODEs. In this case, we return - * nullptr to store in the nmodl::symtab::SymbolTable. - * - * \return pointer to token if exist otherwise nullptr - */ - const {{ virtual(node) }}ModToken* get_token() const noexcept override { - return token.get(); - } - {% endif %} - - {% if node.is_symtab_needed %} - /** - * \brief Return associated symbol table for the current ast node - * - * Only certain ast nodes (e.g. inherited from ast::Block) have associated - * symbol table. These nodes have nmodl::symtab::SymbolTable as member - * and it can be accessed using this method. - * - * \return pointer to the symbol table - * - * \sa nmodl::symtab::SymbolTable nmodl::visitor::SymtabVisitor - */ - symtab::SymbolTable* get_symbol_table() const override { - return symtab; - } - - {% endif %} - - {% if node.is_program_node %} - /** - * \brief Return global symbol table for the mod file - */ - symtab::ModelSymbolTable* get_model_symbol_table() { - return &model_symtab; - } - {% endif %} - - {# doxygen for these methods is handled by nodes.py #} - {% for child in node.children %} - {{ child.get_add_methods_declaration() }} - - {{ child.get_node_name_method_declaration() }} - - {{ child.get_getter_method(node.class_name) }} - - {% endfor %} - + /** + * \brief Return associated token for the current ast node + * + * Not all ast nodes have token information. For example, nmodl::visitor::NeuronSolveVisitor + * can insert new nodes in the ast as a solution of ODEs. In this case, we return + * nullptr to store in the nmodl::symtab::SymbolTable. + * + * \return pointer to token if exist otherwise nullptr + */ + const {{ virtual(node) }}ModToken* get_token() const noexcept override { + return token.get(); + } +{% endif %} +{%- if node.is_symtab_needed %} + /** + * \brief Return associated symbol table for the current ast node + * + * Only certain ast nodes (e.g. inherited from ast::Block) have associated + * symbol table. These nodes have nmodl::symtab::SymbolTable as member + * and it can be accessed using this method. + * + * \return pointer to the symbol table + * + * \sa nmodl::symtab::SymbolTable nmodl::visitor::SymtabVisitor + */ + symtab::SymbolTable* get_symbol_table() const override { + return symtab; + } +{% endif %} +{% if node.is_program_node %} + /** + * \brief Return global symbol table for the mod file + */ + symtab::ModelSymbolTable* get_model_symbol_table() { + return &model_symtab; + } +{% endif %} +{# doxygen for these methods is handled by nodes.py #} +{% for child in node.children %} + {{ child.get_add_methods_declaration() }} + {{ child.get_node_name_method_declaration() }} + {{ child.get_getter_method(node.class_name) }} +{%- endfor %} /// \} - {% if node.has_setters %} +{% if node.has_setters %} /// \name Setters /// \{ - {% endif %} - - {% if node.is_name_node %} - /** - * \brief Set name for the current ast node - * - * Some ast nodes have a member marked designated as node name (e.g. nodes - * derived from ast::Identifier). This method is used to set new name for those - * nodes. This useful for passes like nmodl::visitor::RenameVisitor. - * - * \sa Ast::get_node_type_name Ast::get_node_name - */ - {{ virtual(node) }}void set_name(const std::string& name) override; - {% endif %} - - {% if node.has_token %} - /** - * \brief Set token for the current ast node - */ - void set_token(const ModToken& tok) { token = std::make_shared(tok); } - {% endif %} - - {% if node.is_symtab_needed %} - /** - * \brief Set symbol table for the current ast node - * - * Top level, block scoped nodes store symbol table in the ast node. - * nmodl::visitor::SymtabVisitor then used this method to setup symbol table - * for every node in the ast. - * - * \sa nmodl::visitor::SymtabVisitor - */ - void set_symbol_table(symtab::SymbolTable* newsymtab) override { - symtab = newsymtab; - } - {% endif %} - - {# if node is base data type but not enum then add set method #} - {% if node.is_data_type_node and not node.is_enum_node %} - /** - * \brief Set new value to the current ast node - * \sa {{ node.class_name }}::eval - */ - void set({{ node.get_data_type_name() }} _value) { - value = _value; - } - {% endif %} - - {# doxygen for these methods is handled by nodes.py #} - {% for child in node.children %} - {{ child.get_setter_method_declaration(node.class_name) }} - {% endfor %} - - {{ "/// \}" if node.has_setters else "" }} +{% endif %} +{% if node.is_name_node %} + /** + * \brief Set name for the current ast node + * + * Some ast nodes have a member marked designated as node name (e.g. nodes + * derived from ast::Identifier). This method is used to set new name for those + * nodes. This useful for passes like nmodl::visitor::RenameVisitor. + * + * \sa Ast::get_node_type_name Ast::get_node_name + */ + {{ virtual(node) }}void set_name(const std::string& name) override; +{% endif %} +{% if node.has_token %} + /** + * \brief Set token for the current ast node + */ + void set_token(const ModToken& tok) { token = std::make_shared(tok); } +{% endif %} +{% if node.is_symtab_needed %} + /** + * \brief Set symbol table for the current ast node + * + * Top level, block scoped nodes store symbol table in the ast node. + * nmodl::visitor::SymtabVisitor then used this method to setup symbol table + * for every node in the ast. + * + * \sa nmodl::visitor::SymtabVisitor + */ + void set_symbol_table(symtab::SymbolTable* newsymtab) override { + symtab = newsymtab; + } +{% endif %} +{# if node is base data type but not enum then add set method #} +{% if node.is_data_type_node and not node.is_enum_node %} + /** + * \brief Set new value to the current ast node + * \sa {{ node.class_name }}::eval + */ + void set({{ node.get_data_type_name() }} _value) { + value = _value; + } +{% endif %} +{# doxygen for these methods is handled by nodes.py #} +{% for child in node.children %} + {{ child.get_setter_method_declaration(node.class_name) }} +{% endfor %} +{{ " /// \}" if node.has_setters else "" }} /// \name Visitor /// \{ - /** * \brief visit children i.e. member variables of current node using provided visitor * @@ -326,7 +299,6 @@ class {{ node.class_name }} : public {{ node.base_class }} { * \copydoc accept(visitor::Visitor&) */ {{ virtual(node) }} void accept(visitor::ConstVisitor& v) const override; - /// \} {% if node.is_base_class_number_node %} @@ -356,7 +328,6 @@ class {{ node.class_name }} : public {{ node.base_class }} { {% endif %} } {% endif %} - {% if node.is_data_type_node %} {# if node is of enum type then return enum value #} {% if node.is_enum_node %} @@ -387,7 +358,6 @@ class {{ node.class_name }} : public {{ node.base_class }} { } {% endif %} {% endif %} - {% if node.children %} private: /** @@ -401,11 +371,10 @@ class {{ node.class_name }} : public {{ node.base_class }} { {% endif %} }; -/** @} */ // end of ast_class +/** \} */ // end of ast_class {% for child in node.children %} {{ child.get_add_methods_inline_definition(node) }} {% endfor %} -} // namespace ast -} // namespace nmodl +} // namespace nmodl::ast diff --git a/src/language/templates/pybind/pyast.hpp b/src/language/templates/pybind/pyast.hpp index e70ffe3aca..f2fa13eb14 100644 --- a/src/language/templates/pybind/pyast.hpp +++ b/src/language/templates/pybind/pyast.hpp @@ -114,8 +114,8 @@ struct PyAst: public Ast { PYBIND11_OVERRIDE(symtab::SymbolTable*, Ast, get_symbol_table, ); } - std::shared_ptr get_statement_block() const override { - PYBIND11_OVERRIDE(std::shared_ptr, Ast, get_statement_block, ); + const std::shared_ptr& get_statement_block() const override { + PYBIND11_OVERRIDE(const std::shared_ptr&, Ast, get_statement_block, ); } void set_symbol_table(symtab::SymbolTable* newsymtab) override { diff --git a/src/parser/unit.yy b/src/parser/unit.yy index 34febc1bdf..03ebf31ed2 100644 --- a/src/parser/unit.yy +++ b/src/parser/unit.yy @@ -107,7 +107,7 @@ table_insertion try { $1->insert($2); } - catch (std::runtime_error e) { + catch (const std::runtime_error& e) { error(scanner.loc, e.what()); } $$ = $1;