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

Code Quality: PVS Studio Finds #1901

Merged

Conversation

thorstenhater
Copy link
Contributor

It's the season again when I put our code through the grinder and fix at least a few issues found.

@@ -281,7 +281,7 @@ class lexer {
}

// test if the symbol matches a keyword
auto it = keyword_to_tok.find(symbol.c_str());
auto it = keyword_to_tok.find(symbol);
Copy link
Contributor

Choose a reason for hiding this comment

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

There are two kinds of people: those that append .c_str() gratuitously and those that really need it. How can PVSS tell which it is?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Find takes a const std::string& as std::string is the key type?!

Copy link
Contributor

Choose a reason for hiding this comment

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

.c_str() isn't a std::string?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It's a const char*, like in C. If std::string where a std::vector<char>, c_str would be like data, returning the underlying buffer by pointer.

Copy link
Contributor

Choose a reason for hiding this comment

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

Yes, but that's not what I meant. I guess the answer to my question is: yes, in this case PVSS knows how symbol is used and c_str() is not required for that use.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It knows since it can see this

std::map<K, V>::find(const K&)

and here K=std::string. We are calling

map.find(string.c_str())

so the temporary C string is converted back to a std::string.
That in turn is not possible without copying the bytes, so

  1. not needed
  2. wasteful

@brenthuisman
Copy link
Contributor

I have the feeling a lot of the errors are related to the assumption by PVSS that {} and = default are equal.

@thorstenhater
Copy link
Contributor Author

thorstenhater commented Jun 5, 2022

I have the feeling a lot of the errors are related to the assumption by PVSS that {} and = default are equal.

But for the default ctor and dtor this is entirely correct, see nice answer here

https://stackoverflow.com/questions/20828907/the-new-syntax-default-in-c11?lq=1

The copy constructor is much more sketchy, but for empty classes it should be fine.

The errors are caused by something different, and they do not occur (neither a warning) without the sanitizers.
Fascinating.

@brenthuisman
Copy link
Contributor

OK, so {} and = default are the same, apart from: "Giving a user-defined constructor, even though it does nothing, makes the type not an aggregate and also not trivial." The definitions of trivial and aggregate/POD seem awfully close btw., but if that's important, why not not declare the de-/constructor? Reading the above, = default gives you that behavior.

@thorstenhater
Copy link
Contributor Author

If you declare any of the 5 methods (copy ctor, move ctor, copy=, move=, non-default ctor), the rest will not be auto-generated. But you can get them back by =default

@brenthuisman
Copy link
Contributor

Ach so....

@thorstenhater
Copy link
Contributor Author

I have the feeling a lot of the errors are related to the assumption by PVSS that {} and = default are equal.

But for the default ctor and dtor this is entirely correct, see nice answer here

https://stackoverflow.com/questions/20828907/the-new-syntax-default-in-c11?lq=1

The copy constructor is much more sketchy, but for empty classes it should be fine.

The errors are caused by something different, and they do not occur (neither a warning) without the sanitizers. Fascinating.

So, the compile errors were cause by the SIMD classes declaring friend fma(SIMD, SIMD, SIMD), but not friend fma(const SIMD&, ...). That's all.

@@ -248,32 +248,34 @@ ARB_ARBOR_API fvm_cv_discretization fvm_cv_discretize(const cable_cell& cell, co
double dflt_potential = *(dflt.init_membrane_potential | global_dflt.init_membrane_potential);
double dflt_temperature = *(dflt.temperature_K | global_dflt.temperature_K);

const auto& assignments = cell.region_assignments();
Copy link
Contributor

Choose a reason for hiding this comment

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

I can't find where these are used (so I don't understand why they were added).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

They are use in multiple places, yielding longish expressions, so we name it.

@@ -214,6 +214,7 @@ struct ARB_ARBOR_API s_expr {
s_expr(s_expr l, s_expr r):
state(pair_type(std::move(l), std::move(r)))
{}
s_expr& operator=(const s_expr& s) { state = s.state; return *this; }
Copy link
Contributor

Choose a reason for hiding this comment

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

What's the algo for adding these? operator= must always be implemented or sth?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

No, not really. The rule is to implement op= if you implement copy and vice veras. The argument is one of correctness:
If you need write one, it'll do something interesting. Therefore, you cannot use the default one.
You'll get a warning, but the result will be wrong, since the assignment operator will still be made for you see
https://gcc.godbolt.org/z/KfreGvxjY
Note especially the difference here

S a = S{};     // copy ctor, despite looking like assignment.
S a; a = S{}; // assignment

const p3d vw = w-v;
const p3d vp = p-v;
Copy link
Contributor

Choose a reason for hiding this comment

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

Strange to remove one but not both. Any idea why?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

vp is constructed inside a conditional and closer to its sole site of use. So, it's clearer and it might save the odd cycle.

Copy link
Contributor

@brenthuisman brenthuisman left a comment

Choose a reason for hiding this comment

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

I went through the whole thing, looks reasonable to me. Have a few more questions, see above, but they are about PVSS's choices, not the code itself.

@thorstenhater
Copy link
Contributor Author

thorstenhater commented Jun 11, 2022

Most of them are reasonable things to admonish, although I ignored about 60% of the finds.
I also excluded the directories python, test, modcc, and example outright.
Next time, we can do some more.

Here's a suggestion: Ask PVS for a license (free for FOSS), run it and go over the results.
I can give you my filter scripts. Each has a key, which you can index into their exaplanation DB.
Or you can just ask (me).

@thorstenhater thorstenhater merged commit 14f2d6a into arbor-sim:master Jun 13, 2022
@thorstenhater thorstenhater deleted the quality/static-analysis-22 branch August 1, 2022 07:39
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.

2 participants