Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add new lint rule for negative array dimensions #1835

Merged
merged 6 commits into from
Apr 15, 2023

Conversation

IEncinas10
Copy link
Collaborator

@IEncinas10 IEncinas10 commented Mar 26, 2023

Attempt to fix #382. I'm sure the code could use some improvements, so happy to accomodate any feedback!

Edit:

Sorry for the several updates, after making the pull request I realized that there are some corner cases (parenthesis, ...) so I tried to fix some of those. It still won't pick up things like +(-5) but I can't see an easy way to fix that.

@codecov-commenter
Copy link

codecov-commenter commented Mar 26, 2023

Codecov Report

Patch coverage: 97.22% and project coverage change: +0.01 🎉

Comparison is base (bb28b0b) 93.08% compared to head (ab991cf) 93.09%.

📣 This organization is not using Codecov’s GitHub App Integration. We recommend you install it so Codecov can continue to function properly for your repositories. Learn more

Additional details and impacted files
@@            Coverage Diff             @@
##           master    #1835      +/-   ##
==========================================
+ Coverage   93.08%   93.09%   +0.01%     
==========================================
  Files         353      354       +1     
  Lines       25656    25723      +67     
==========================================
+ Hits        23881    23946      +65     
- Misses       1775     1777       +2     
Impacted Files Coverage Δ
verilog/CST/expression.h 100.00% <ø> (ø)
verilog/CST/expression.cc 95.89% <93.33%> (-0.67%) ⬇️
...log/analysis/checkers/forbid_negative_array_dim.cc 100.00% <100.00%> (ø)

... and 8 files with indirect coverage changes

Help us with your feedback. Take ten seconds to tell us how you rate us. Have a feature suggestion? Share it here.

☔ View full report in Codecov by Sentry.
📢 Do you have feedback about the report comment? Let us know in this issue.

Copy link
Collaborator

@fangism fangism left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for contributing this!

if (UnaryPrefixExprMatcher().Matches(symbol, &manager)) {
const auto& node = SymbolCastToNode(symbol);
const auto& children = node.children();
const auto& leaf_symbol = *children[0].get();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I recommend defining a named function for anything that indexes into the list of childre.
e.g. add a GetUnaryPrefixExprOperator to https://cs.opensource.google/verible/verible/+/master:verilog/CST/expression.h

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Incidentally, what I'm requesting here is aligned with #159

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Went ahead and added the suggested functions. Let me know if they're ok or if you think they could use some more work.

if (children.size() <= 1) return;

int value = -1;
const auto& child_node = children[1];
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar suggestion, maybe call this GetUnaryPrefixExprOperand in https://cs.opensource.google/verible/verible/+/master:verilog/CST/expression.h

const auto& child_node = children[1];
const bool is_constant = ConstantIntegerValue(*child_node.get(), &value);

const verible::TokenInfo token(TK_OTHER,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can be declared inside the following if body, because it is only used there.

namespace verilog {
namespace analysis {

class ForbidNegativeArrayDim : public verible::SyntaxTreeLintRule {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Recommend documenting what this class does here, with some simple examples.

@@ -0,0 +1,48 @@
// Copyright 2017-2020 The Verible Authors.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

might as well make this 2023 (other files too)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done! Updated also expression.cc and expression.h files

{"logic l [10+(-5):0];"},
{"logic l [(", {kScalar, "-5"}, "):0];"},
{"logic l [-(-5):0];"},
};
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested cases:

  • foo_type [a:b] name [c:d]
  • declaration inside module ports
  • declaration inside module body
  • declaration inside class
  • local declaration inside function

Add auxiliary functions for kUnaryExpressions
Add more test cases
Update copyright
Add auxiliary functions for kUnaryExpressions
Add more test cases
Update copyright for modified files
@@ -98,6 +98,19 @@ const verible::Symbol* GetConditionExpressionFalseCase(
return GetSubtreeAsSymbol(condition_expr, NodeEnum::kConditionExpression, 4);
}

const verible::TokenInfo* GetUnaryPrefixOperator(
const verible::Symbol& symbol) {
const auto& children = verible::SymbolCastToNode(symbol).children();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if symbol does not happen to be a node, then this will crash. You probably want to return nullptr in case symbol.Kind() is not a node.
Or you could test that it is a kUnaryPrefixExpression, which is a better, more specific, test.

(Also add a test for it to see that other nodes than kUnaryPrefixExpressions always return nullptr).

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added the checks in both functions, thanks pointing it out.

const verible::TokenInfo* GetUnaryPrefixOperator(
const verible::Symbol& symbol) {
const auto& children = verible::SymbolCastToNode(symbol).children();
const auto leaf_symbol = children.front().get();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In general, we try to avoid using auto unless whatever comes back is a huge templated type which does not add any benefit for the reader. So use the type that is returned.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Got it, thanks! Used auto because I saw it in use and thought that was the general style, looks much better now.

const verible::Symbol& symbol) {
const auto& children = verible::SymbolCastToNode(symbol).children();
const auto leaf_symbol = children.front().get();
return &verible::down_cast<const verible::SyntaxTreeLeaf*>(leaf_symbol)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we know that leaf_symbol we get is a SyntaxTreeLeaf so that we can safely cast ?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I assumed every kUnaryPrefixExpression has the following form

Node @0 (tag: kUnaryPrefixExpression) {
                        Leaf @0 /* ... */
                        Node @1 /* ... /*

So I guess that if we're sure the current node is a kUnaryPrefixExpression we can assume the cast will work always? Let me know if I'm wrong.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At the time of the writing of this comment, we only got verible::Symbol, so didn't know that yet.

@@ -81,6 +81,12 @@ const verible::Symbol* GetConditionExpressionTrueCase(const verible::Symbol&);
// Returns the false-case expression of a kConditionExpression.
const verible::Symbol* GetConditionExpressionFalseCase(const verible::Symbol&);

// Returns the operator of a kUnaryPrefixExpression
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add tests for the new functions in expression_test.cc.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. Let me know if they're thorough enough.

const auto operand = verilog::GetUnaryPrefixOperand(symbol);

const bool is_constant = verilog::ConstantIntegerValue(*operand, &value);
if (is_constant > 0 && value > 0 && u_operator->text() == "-") {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is_constant is a bool, so using > on it is confusing.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, sorry about that. Removed "value > 0" check, noticed it was needed and when I added it back I somehow made a mess.

// Extract operand and operator from kUnaryPrefixExpression
const auto u_operator = verilog::GetUnaryPrefixOperator(symbol);
const auto operand = verilog::GetUnaryPrefixOperand(symbol);

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we're at this point, did the matcher make sure that u_operator and operand are never nullptr ?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As the matcher ensures that symbol is a kUnaryPrefixExpression, I would expect that they would never be nullptr (if my asumption about the general format for every kUnaryPrefixExpression is correct). If I'm wrong, I would just add an early return if either operand or u_operator are nullptr.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it is sufficient if you add that as a comment; something like:

// symbol is a UnaryPrefixExpression, so operator and operand are defined

- Change auto for the actual types
- Add tests for new functions for unary expressions
- Add checks for correct input type for new functions. Return nullptr
  when appropiate
- Remove typo when checking boolean value (redundant > 0)
Copy link
Collaborator

@hzeller hzeller left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice. A few more comments.

const verible::Symbol& symbol) {
const auto& children = verible::SymbolCastToNode(symbol).children();
const auto leaf_symbol = children.front().get();
return &verible::down_cast<const verible::SyntaxTreeLeaf*>(leaf_symbol)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At the time of the writing of this comment, we only got verible::Symbol, so didn't know that yet.

@@ -98,6 +98,25 @@ const verible::Symbol* GetConditionExpressionFalseCase(
return GetSubtreeAsSymbol(condition_expr, NodeEnum::kConditionExpression, 4);
}

const verible::TokenInfo* GetUnaryPrefixOperator(
const verible::Symbol& symbol) {
const SyntaxTreeNode* node = &verible::SymbolCastToNode(symbol);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this will crash if symbol is not a node.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Check added.

}
}
}

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also add tests where Symbol is not a unary prefix expression. What if symbol is a leaf ?
Having test cases with broken input helps to make sure we handle that gracefully.

Copy link
Collaborator Author

@IEncinas10 IEncinas10 Apr 4, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added calls to the GetUnaryPrefix... functions with wrong input and check they return null pointer

// Extract operand and operator from kUnaryPrefixExpression
const auto u_operator = verilog::GetUnaryPrefixOperator(symbol);
const auto operand = verilog::GetUnaryPrefixOperand(symbol);

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it is sufficient if you add that as a comment; something like:

// symbol is a UnaryPrefixExpression, so operator and operand are defined

}

verible::matcher::BoundSymbolManager manager;
if (UnaryPrefixExprMatcher().Matches(symbol, &manager)) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this is not matching, we can early out here. Saves one level of indentation and reduces the stack-size the reader of this code needs :)

if (!UnaryPrefixExprMatcher().Matches(symbol, &manager)) return;

... everything that follows implies that we have a unary prefix expression.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done, thanks for pointing that out

- Perform check before doing SymbolCastToNode
- Call function with broken input (leafs, ...)
- Add comments, style cleanup.
/* Can't detect this at the moment */
{"logic l [+(-5):0];"},
/* Packed */
{"logic [", {kScalar, "-10"}, ":-0] l;"},
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am wondering, should there be a configuration for the packed and unpacked ranges ?
Maybe a particular style allows to set a bitwidth with negative values but not array sizes.

Something to discuss in #382, then a potential implementation that distinguishes the ranges in a follow-up PR though.

Copy link
Collaborator

@hzeller hzeller left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, @fangism any additional comments ?

@hzeller hzeller requested a review from fangism April 14, 2023 23:23
Copy link
Collaborator

@fangism fangism left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks great, thank you for your hard work!

@IEncinas10
Copy link
Collaborator Author

Thank you both! My pleasure, I'll search for other issues feasible for my with my little knowledge of the codebase. Feel free to point me in any direction that might be of use to you!

Thanks again, enjoy the weekend and thanks for the great project you manage!

@hzeller hzeller merged commit 525ffaf into chipsalliance:master Apr 15, 2023
@IEncinas10 IEncinas10 deleted the forbid_negative_array_dim branch April 17, 2023 21:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

forbid negative constant integer literals in declared array dimensions
4 participants