| // Copyright 2017-2020 The Verible Authors. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| #include "verilog/CST/statement.h" |
| |
| #include <vector> |
| |
| #include "common/analysis/matcher/matcher.h" |
| #include "common/analysis/matcher/matcher_builders.h" |
| #include "common/analysis/syntax_tree_search.h" |
| #include "common/text/concrete_syntax_leaf.h" |
| #include "common/text/concrete_syntax_tree.h" |
| #include "common/text/symbol.h" |
| #include "common/text/token_info.h" |
| #include "common/text/tree_utils.h" |
| #include "verilog/CST/verilog_matchers.h" // IWYU pragma: keep |
| |
| namespace verilog { |
| |
| using verible::Symbol; |
| using verible::SymbolCastToNode; |
| using verible::SyntaxTreeNode; |
| |
| static const SyntaxTreeNode& GetGenericStatementBody( |
| const SyntaxTreeNode& node) { |
| // In most controlled constructs, the controlled statement body is |
| // in tail position. Exceptions include: DoWhile. |
| return SymbolCastToNode(*node.children().back()); |
| } |
| |
| const SyntaxTreeNode& GetIfClauseGenerateBody(const Symbol& if_clause) { |
| const auto& body_node = GetGenericStatementBody( |
| CheckNodeEnum(SymbolCastToNode(if_clause), NodeEnum::kGenerateIfClause)); |
| return GetSubtreeAsNode(body_node, NodeEnum::kGenerateIfBody, 0); |
| } |
| |
| const SyntaxTreeNode& GetElseClauseGenerateBody(const Symbol& else_clause) { |
| const auto& body_node = GetGenericStatementBody(CheckNodeEnum( |
| SymbolCastToNode(else_clause), NodeEnum::kGenerateElseClause)); |
| return GetSubtreeAsNode(body_node, NodeEnum::kGenerateElseBody, 0); |
| } |
| |
| const SyntaxTreeNode& GetLoopGenerateBody(const Symbol& loop) { |
| return GetGenericStatementBody( |
| CheckNodeEnum(SymbolCastToNode(loop), NodeEnum::kLoopGenerateConstruct)); |
| } |
| |
| const SyntaxTreeNode& GetConditionalGenerateIfClause( |
| const Symbol& conditional) { |
| return GetSubtreeAsNode(conditional, NodeEnum::kConditionalGenerateConstruct, |
| 0, NodeEnum::kGenerateIfClause); |
| } |
| |
| const SyntaxTreeNode* GetConditionalGenerateElseClause( |
| const Symbol& conditional) { |
| const auto& node = CheckNodeEnum(SymbolCastToNode(conditional), |
| NodeEnum::kConditionalGenerateConstruct); |
| if (node.children().size() < 2) return nullptr; |
| const Symbol* else_ptr = node.children().back().get(); |
| if (else_ptr == nullptr) return nullptr; |
| return &CheckNodeEnum(SymbolCastToNode(*else_ptr), |
| NodeEnum::kGenerateElseClause); |
| } |
| |
| const SyntaxTreeNode& GetIfClauseStatementBody(const Symbol& if_clause) { |
| const auto& body_node = GetGenericStatementBody( |
| CheckNodeEnum(SymbolCastToNode(if_clause), NodeEnum::kIfClause)); |
| return GetSubtreeAsNode(body_node, NodeEnum::kIfBody, 0); |
| } |
| |
| const SyntaxTreeNode& GetElseClauseStatementBody(const Symbol& else_clause) { |
| const auto& body_node = GetGenericStatementBody( |
| CheckNodeEnum(SymbolCastToNode(else_clause), NodeEnum::kElseClause)); |
| return GetSubtreeAsNode(body_node, NodeEnum::kElseBody, 0); |
| } |
| |
| const SyntaxTreeNode& GetConditionalStatementIfClause( |
| const Symbol& conditional) { |
| return GetSubtreeAsNode(conditional, NodeEnum::kConditionalStatement, 0, |
| NodeEnum::kIfClause); |
| } |
| |
| const SyntaxTreeNode* GetConditionalStatementElseClause( |
| const Symbol& conditional) { |
| const auto& node = CheckNodeEnum(SymbolCastToNode(conditional), |
| NodeEnum::kConditionalStatement); |
| if (node.children().size() < 2) return nullptr; |
| const Symbol* else_ptr = node.children().back().get(); |
| if (else_ptr == nullptr) return nullptr; |
| return &CheckNodeEnum(SymbolCastToNode(*else_ptr), NodeEnum::kElseClause); |
| } |
| |
| const SyntaxTreeNode& GetAssertionStatementAssertClause( |
| const Symbol& assertion_statement) { |
| return GetSubtreeAsNode(assertion_statement, NodeEnum::kAssertionStatement, 0, |
| NodeEnum::kAssertionClause); |
| } |
| |
| const SyntaxTreeNode* GetAssertionClauseStatementBody( |
| const Symbol& assertion_clause) { |
| const auto& body_node = GetGenericStatementBody(CheckNodeEnum( |
| SymbolCastToNode(assertion_clause), NodeEnum::kAssertionClause)); |
| return verible::CheckOptionalSymbolAsNode( |
| GetSubtreeAsSymbol(body_node, NodeEnum::kAssertionBody, 0)); |
| } |
| |
| const SyntaxTreeNode* GetAssertionStatementElseClause( |
| const Symbol& assertion_statement) { |
| const auto& node = CheckNodeEnum(SymbolCastToNode(assertion_statement), |
| NodeEnum::kAssertionStatement); |
| const Symbol* else_ptr = node.children().back().get(); |
| if (else_ptr == nullptr) return nullptr; |
| return &CheckNodeEnum(SymbolCastToNode(*else_ptr), NodeEnum::kElseClause); |
| } |
| |
| const SyntaxTreeNode& GetAssumeStatementAssumeClause( |
| const Symbol& assume_statement) { |
| return GetSubtreeAsNode(assume_statement, NodeEnum::kAssumeStatement, 0, |
| NodeEnum::kAssumeClause); |
| } |
| |
| const SyntaxTreeNode* GetAssumeClauseStatementBody( |
| const Symbol& assume_clause) { |
| const auto& body_node = GetGenericStatementBody( |
| CheckNodeEnum(SymbolCastToNode(assume_clause), NodeEnum::kAssumeClause)); |
| return verible::CheckOptionalSymbolAsNode( |
| GetSubtreeAsSymbol(body_node, NodeEnum::kAssumeBody, 0)); |
| } |
| |
| const SyntaxTreeNode* GetAssumeStatementElseClause( |
| const Symbol& assume_statement) { |
| const auto& node = CheckNodeEnum(SymbolCastToNode(assume_statement), |
| NodeEnum::kAssumeStatement); |
| const Symbol* else_ptr = node.children().back().get(); |
| if (else_ptr == nullptr) return nullptr; |
| return &CheckNodeEnum(SymbolCastToNode(*else_ptr), NodeEnum::kElseClause); |
| } |
| |
| const SyntaxTreeNode* GetCoverStatementBody(const Symbol& cover_statement) { |
| const auto& body_node = GetGenericStatementBody(CheckNodeEnum( |
| SymbolCastToNode(cover_statement), NodeEnum::kCoverStatement)); |
| return verible::CheckOptionalSymbolAsNode( |
| GetSubtreeAsSymbol(body_node, NodeEnum::kCoverBody, 0)); |
| } |
| |
| const SyntaxTreeNode* GetWaitStatementBody(const Symbol& wait_statement) { |
| const auto& body_node = GetGenericStatementBody(CheckNodeEnum( |
| SymbolCastToNode(wait_statement), NodeEnum::kWaitStatement)); |
| return verible::CheckOptionalSymbolAsNode( |
| GetSubtreeAsSymbol(body_node, NodeEnum::kWaitBody, 0)); |
| } |
| |
| const SyntaxTreeNode& GetAssertPropertyStatementAssertClause( |
| const Symbol& assert_property_statement) { |
| return GetSubtreeAsNode(assert_property_statement, |
| NodeEnum::kAssertPropertyStatement, 0, |
| NodeEnum::kAssertPropertyClause); |
| } |
| |
| const SyntaxTreeNode* GetAssertPropertyStatementBody( |
| const Symbol& assert_clause) { |
| const auto& body_node = GetGenericStatementBody(CheckNodeEnum( |
| SymbolCastToNode(assert_clause), NodeEnum::kAssertPropertyClause)); |
| return verible::CheckOptionalSymbolAsNode( |
| GetSubtreeAsSymbol(body_node, NodeEnum::kAssertPropertyBody, 0)); |
| } |
| |
| const SyntaxTreeNode* GetAssertPropertyStatementElseClause( |
| const Symbol& assert_property_statement) { |
| const auto& node = CheckNodeEnum(SymbolCastToNode(assert_property_statement), |
| NodeEnum::kAssertPropertyStatement); |
| const Symbol* else_ptr = node.children().back().get(); |
| if (else_ptr == nullptr) return nullptr; |
| return &CheckNodeEnum(SymbolCastToNode(*else_ptr), NodeEnum::kElseClause); |
| } |
| |
| const SyntaxTreeNode& GetAssumePropertyStatementAssumeClause( |
| const Symbol& assume_property_statement) { |
| return GetSubtreeAsNode(assume_property_statement, |
| NodeEnum::kAssumePropertyStatement, 0, |
| NodeEnum::kAssumePropertyClause); |
| } |
| |
| const SyntaxTreeNode* GetAssumePropertyStatementBody( |
| const Symbol& assume_clause) { |
| const auto& body_node = GetGenericStatementBody(CheckNodeEnum( |
| SymbolCastToNode(assume_clause), NodeEnum::kAssumePropertyClause)); |
| return verible::CheckOptionalSymbolAsNode( |
| GetSubtreeAsSymbol(body_node, NodeEnum::kAssumePropertyBody, 0)); |
| } |
| |
| const SyntaxTreeNode* GetAssumePropertyStatementElseClause( |
| const Symbol& assume_property_statement) { |
| const auto& node = CheckNodeEnum(SymbolCastToNode(assume_property_statement), |
| NodeEnum::kAssumePropertyStatement); |
| const Symbol* else_ptr = node.children().back().get(); |
| if (else_ptr == nullptr) return nullptr; |
| return &CheckNodeEnum(SymbolCastToNode(*else_ptr), NodeEnum::kElseClause); |
| } |
| |
| const SyntaxTreeNode& GetExpectPropertyStatementExpectClause( |
| const Symbol& expect_property_statement) { |
| return GetSubtreeAsNode(expect_property_statement, |
| NodeEnum::kExpectPropertyStatement, 0, |
| NodeEnum::kExpectPropertyClause); |
| } |
| |
| const SyntaxTreeNode* GetExpectPropertyStatementBody( |
| const Symbol& expect_clause) { |
| const auto& body_node = GetGenericStatementBody(CheckNodeEnum( |
| SymbolCastToNode(expect_clause), NodeEnum::kExpectPropertyClause)); |
| return verible::CheckOptionalSymbolAsNode( |
| GetSubtreeAsSymbol(body_node, NodeEnum::kExpectPropertyBody, 0)); |
| } |
| |
| const SyntaxTreeNode* GetExpectPropertyStatementElseClause( |
| const Symbol& expect_property_statement) { |
| const auto& node = CheckNodeEnum(SymbolCastToNode(expect_property_statement), |
| NodeEnum::kExpectPropertyStatement); |
| const Symbol* else_ptr = node.children().back().get(); |
| if (else_ptr == nullptr) return nullptr; |
| return &CheckNodeEnum(SymbolCastToNode(*else_ptr), NodeEnum::kElseClause); |
| } |
| |
| const SyntaxTreeNode* GetCoverPropertyStatementBody( |
| const Symbol& cover_property) { |
| const auto& body_node = GetGenericStatementBody(CheckNodeEnum( |
| SymbolCastToNode(cover_property), NodeEnum::kCoverPropertyStatement)); |
| return verible::CheckOptionalSymbolAsNode( |
| GetSubtreeAsSymbol(body_node, NodeEnum::kCoverPropertyBody, 0)); |
| } |
| |
| const SyntaxTreeNode* GetCoverSequenceStatementBody( |
| const Symbol& cover_sequence) { |
| const auto& body_node = GetGenericStatementBody(CheckNodeEnum( |
| SymbolCastToNode(cover_sequence), NodeEnum::kCoverSequenceStatement)); |
| return verible::CheckOptionalSymbolAsNode( |
| GetSubtreeAsSymbol(body_node, NodeEnum::kCoverSequenceBody, 0)); |
| } |
| |
| const SyntaxTreeNode& GetLoopStatementBody(const Symbol& loop) { |
| return GetGenericStatementBody( |
| CheckNodeEnum(SymbolCastToNode(loop), NodeEnum::kForLoopStatement)); |
| } |
| |
| const SyntaxTreeNode& GetDoWhileStatementBody(const Symbol& do_while) { |
| return GetSubtreeAsNode(SymbolCastToNode(do_while), |
| NodeEnum::kDoWhileLoopStatement, 1); |
| } |
| |
| const SyntaxTreeNode& GetForeverStatementBody(const Symbol& forever) { |
| return GetGenericStatementBody(CheckNodeEnum( |
| SymbolCastToNode(forever), NodeEnum::kForeverLoopStatement)); |
| } |
| |
| const SyntaxTreeNode& GetForeachStatementBody(const Symbol& foreach) { |
| return GetGenericStatementBody(CheckNodeEnum( |
| SymbolCastToNode(foreach), NodeEnum::kForeachLoopStatement)); |
| } |
| |
| const SyntaxTreeNode& GetRepeatStatementBody(const Symbol& repeat) { |
| return GetGenericStatementBody( |
| CheckNodeEnum(SymbolCastToNode(repeat), NodeEnum::kRepeatLoopStatement)); |
| } |
| |
| const SyntaxTreeNode& GetWhileStatementBody(const Symbol& while_stmt) { |
| return GetGenericStatementBody(CheckNodeEnum(SymbolCastToNode(while_stmt), |
| NodeEnum::kWhileLoopStatement)); |
| } |
| |
| const SyntaxTreeNode& GetProceduralTimingControlStatementBody( |
| const Symbol& proc_timing_control) { |
| return GetGenericStatementBody( |
| CheckNodeEnum(SymbolCastToNode(proc_timing_control), |
| NodeEnum::kProceduralTimingControlStatement)); |
| } |
| |
| const SyntaxTreeNode* GetAnyControlStatementBody(const Symbol& statement) { |
| switch (NodeEnum(SymbolCastToNode(statement).Tag().tag)) { |
| // generate |
| case NodeEnum::kGenerateIfClause: |
| return &GetIfClauseGenerateBody(statement); |
| case NodeEnum::kGenerateElseClause: |
| return &GetElseClauseGenerateBody(statement); |
| case NodeEnum::kLoopGenerateConstruct: |
| return &GetLoopGenerateBody(statement); |
| |
| // statements |
| case NodeEnum::kIfClause: |
| return &GetIfClauseStatementBody(statement); |
| case NodeEnum::kElseClause: |
| return &GetElseClauseStatementBody(statement); |
| case NodeEnum::kForLoopStatement: |
| return &GetLoopStatementBody(statement); |
| case NodeEnum::kDoWhileLoopStatement: |
| return &GetDoWhileStatementBody(statement); |
| case NodeEnum::kForeverLoopStatement: |
| return &GetForeverStatementBody(statement); |
| case NodeEnum::kForeachLoopStatement: |
| return &GetForeachStatementBody(statement); |
| case NodeEnum::kRepeatLoopStatement: |
| return &GetRepeatStatementBody(statement); |
| case NodeEnum::kWhileLoopStatement: |
| return &GetWhileStatementBody(statement); |
| case NodeEnum::kProceduralTimingControlStatement: |
| return &GetProceduralTimingControlStatementBody(statement); |
| |
| // immediate assertions |
| case NodeEnum::kAssertionClause: |
| return GetAssertionClauseStatementBody(statement); |
| case NodeEnum::kAssumeClause: |
| return GetAssumeClauseStatementBody(statement); |
| case NodeEnum::kCoverStatement: |
| return GetCoverStatementBody(statement); |
| |
| case NodeEnum::kWaitStatement: |
| return GetWaitStatementBody(statement); |
| |
| // concurrent assertions |
| case NodeEnum::kAssertPropertyClause: |
| return GetAssertPropertyStatementBody(statement); |
| case NodeEnum::kAssumePropertyClause: |
| return GetAssumePropertyStatementBody(statement); |
| case NodeEnum::kExpectPropertyClause: |
| return GetExpectPropertyStatementBody(statement); |
| case NodeEnum::kCoverPropertyStatement: |
| return GetCoverPropertyStatementBody(statement); |
| case NodeEnum::kCoverSequenceStatement: |
| return GetCoverSequenceStatementBody(statement); |
| |
| default: |
| return nullptr; |
| } |
| } |
| |
| const SyntaxTreeNode* GetAnyConditionalIfClause(const Symbol& conditional) { |
| // by IfClause, we main the first clause |
| switch (NodeEnum(SymbolCastToNode(conditional).Tag().tag)) { |
| // generate |
| case NodeEnum::kConditionalGenerateConstruct: |
| return &GetConditionalGenerateIfClause(conditional); |
| |
| // statement |
| case NodeEnum::kConditionalStatement: |
| return &GetConditionalStatementIfClause(conditional); |
| |
| // immediate assertions |
| case NodeEnum::kAssertionStatement: |
| return &GetAssertionStatementAssertClause(conditional); |
| case NodeEnum::kAssumeStatement: |
| return &GetAssumeStatementAssumeClause(conditional); |
| |
| // concurrent assertions |
| case NodeEnum::kAssertPropertyStatement: |
| return &GetAssertPropertyStatementAssertClause(conditional); |
| case NodeEnum::kAssumePropertyStatement: |
| return &GetAssumePropertyStatementAssumeClause(conditional); |
| case NodeEnum::kExpectPropertyStatement: |
| return &GetExpectPropertyStatementExpectClause(conditional); |
| |
| default: |
| return nullptr; |
| } |
| } |
| |
| const SyntaxTreeNode* GetAnyConditionalElseClause(const Symbol& conditional) { |
| switch (NodeEnum(SymbolCastToNode(conditional).Tag().tag)) { |
| // generate |
| case NodeEnum::kConditionalGenerateConstruct: |
| return GetConditionalGenerateElseClause(conditional); |
| |
| // statement |
| case NodeEnum::kConditionalStatement: |
| return GetConditionalStatementElseClause(conditional); |
| |
| // immediate assertions |
| case NodeEnum::kAssertionStatement: |
| return GetAssertionStatementElseClause(conditional); |
| case NodeEnum::kAssumeStatement: |
| return GetAssumeStatementElseClause(conditional); |
| |
| // concurrent assertions |
| case NodeEnum::kAssertPropertyStatement: |
| return GetAssertPropertyStatementElseClause(conditional); |
| case NodeEnum::kAssumePropertyStatement: |
| return GetAssumePropertyStatementElseClause(conditional); |
| case NodeEnum::kExpectPropertyStatement: |
| return GetExpectPropertyStatementElseClause(conditional); |
| |
| default: |
| return nullptr; |
| } |
| } |
| |
| } // namespace verilog |