GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — pass `TokenList` as reference into `ValueFlow::setValues()` by firewave · Pull Request #4868 · cppcheck-opensource/cppcheck

Conversation

@firewave

@firewave firewave commented Mar 7, 2023

Copy link
Copy Markdown
Collaborator

This avoid lots of unchecked pointer dereferences.

There was a single case which checked it and that looked like a leftover. The only way this might have been a nullptr pointer was through several default constructors which were not used at all so I removed them.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
pfultz2
pfultz2 reviewed Mar 7, 2023
View reviewed changes
Comment thread lib/valueflow.cpp

struct ValueFlowAnalyzer : Analyzer {
const TokenList* tokenlist;
const TokenList& tokenlist;

@pfultz2 pfultz2 Mar 7, 2023

Copy link
Copy Markdown
Collaborator

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

I would prefer to use pointers as members instead of references. Otherwise it makes the class non regular(not assignable or default constructible).

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@pfultz2 pfultz2 Mar 7, 2023

Copy link
Copy Markdown
Collaborator

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

Also, when using the object after move, a pointer can be a nullptr which is easier to diagnose the issue.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@firewave firewave Mar 7, 2023

Copy link
Copy Markdown
Collaborator Author

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

Do we need the functionality? They are local objects that are just being passed around.

The point is to avoid that it could ever be a nullptr. Since they are just used internally and the object will always exist it is impossible to happen but I think it would be better not to allow it anyways.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@firewave firewave Mar 7, 2023

Copy link
Copy Markdown
Collaborator Author

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

Also, when using the object after move, a pointer can be a nullptr which is easier to diagnose the issue.

That is a good point. But until #4845 is merged they are not being moved at all. As it seems to complicate things and probably won't give any meaningful improvement I am open to dropping it anyways.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@pfultz2 pfultz2 Mar 8, 2023

Copy link
Copy Markdown
Collaborator

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

There is cppcoreguidelines-avoid-const-or-ref-data-members check in clang tidy which will find these issues. We should enable this check.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@firewave firewave Mar 8, 2023

Copy link
Copy Markdown
Collaborator Author

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

We are also using way too many struct without any access modifiers allowing modification of any members of them if objects are not longer const. We have countless hidden issues because of that which luckily rarely cause issues (as they are mostly in the tests) but just yesterday I ran into one in production code which might cause files to be analyzed with wrong settings.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@firewave firewave Mar 8, 2023

Copy link
Copy Markdown
Collaborator Author

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

Okay - pointer to const objects are still allowed. That makes things just slightly better. But still being able to re-assign them in the class is dangerous. So to properly get a working const * const-like construct you would need some data class with accessors.

class C1
{
public:
    bool f() const;
};

class C2Data
{
protected:
    C2Data(const C1 &c1) : mC1(&c1) {}

    const C1 &c1() const { return *mC1; }
private:
    const C1 * mC1;
};

class C2 : public C2Data
{
public:
    C2(const C1 &c1) : C2Data(c1) {}
    bool f() {
        return c1().f();
    }
};

static void f()
{
    const C1 c1;
    C2 c2(c1);
}

Maybe you have to adjust the inheritance access but if I ever knew something about that I have forgotten it.

Still somebody would be able to do stupid things within C2Data. That would require some other check that tells you the data class is only there for storage and accessors.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@firewave firewave Mar 9, 2023

Copy link
Copy Markdown
Collaborator Author

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

Or simply std::reference_wrapper?

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@pfultz2 pfultz2 Mar 9, 2023

Copy link
Copy Markdown
Collaborator

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

Yea you could use std::reference_wrapper here(which is similar to gsl::non_null)

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@firewave firewave Mar 9, 2023

Copy link
Copy Markdown
Collaborator Author

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

Alright. I will start testing it after I am done with most of my backlog. That will probably spill into the next dev cycle since I keep unearthing issues addressing other things and I don't want to pile yet another refactoring on top of it.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
@pfultz2

pfultz2 commented Mar 7, 2023

Copy link
Copy Markdown
Collaborator

Do we need the functionality?

If ever we want to use these classes with any generic function/algorithm(ie swap) then we do. These are fundamental operations on types.

Making classes non-regular very much limits what we can do in the future. There is already certain types of analysis we can't do because TokenList and Token are not regular classes.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@firewave

firewave commented Mar 7, 2023

Copy link
Copy Markdown
Collaborator Author

If ever we want to use these classes with any generic function/algorithm(ie swap) then we do. These are fundamental operations on types.

Ok. Can we leave the parameter a reference and just store the pointer?

Making classes non-regular very much limits what we can do in the future. There is already certain types of analysis we can't do because TokenList and Token are not regular classes.

Could you please track those issues in tickets? Thanks.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@firewave

firewave commented Mar 7, 2023

Copy link
Copy Markdown
Collaborator Author

The constParameter false negatives exposed by these changes are being tracked in https://trac.cppcheck.net/ticket/11599.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@pfultz2

pfultz2 commented Mar 8, 2023

Copy link
Copy Markdown
Collaborator

Ok. Can we leave the parameter a reference and just store the pointer?

Yes, parameters should be references, but member variables should not.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@firewave

firewave commented Mar 8, 2023

Copy link
Copy Markdown
Collaborator Author

Ok. Can we leave the parameter a reference and just store the pointer?

Yes, parameters should be references, but member variables should not.

And if I hand it out I could make it a reference again?

That sounds like you should have something like a std::shared_ptrwhich doesn't allow nullptr but that could wreck havoc with the lifetime of objects which isn't good either.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@pfultz2

pfultz2 commented Mar 9, 2023

Copy link
Copy Markdown
Collaborator

We want something like gsl::non_null.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@firewave

firewave commented Mar 9, 2023

Copy link
Copy Markdown
Collaborator Author

Sometimes I am just stupid - what about #4785? I was thinking about disallowing nullptr in that.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@firewave

firewave commented Mar 9, 2023

Copy link
Copy Markdown
Collaborator Author

We already use this pattern all over the place and it is not an isolated case. So could we discuss this in the other ticket and merge this? I have a few more commits depending on this and IIRC those are just parameter and not member changes so things and even if things would just very, very slightly get worse.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@pfultz2

pfultz2 commented Mar 9, 2023

Copy link
Copy Markdown
Collaborator

Sometimes I am just stupid - what about #4785?

I dont think its helpful to have a non-owning pointer propagate const. It can be simply bypassed by copying the pointer:

void f(const safe_ptr<int>& ptr) {
    // *ptr += 1 doesnt compile, but the below does
    auto ptr2 = ptr;
    *ptr += 1;
}

Its better to use const int*, which will prevent mutations to the data(and it can be used as a member variable).

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@firewave

firewave commented Mar 9, 2023

Copy link
Copy Markdown
Collaborator Author

I dont think its helpful to have a non-owning pointer propagate const. It can be simply bypassed by copying the pointer:

Obviously - but those are not meant to be used outside of the class. It's to enforce the "physical constness" as far as possible and reducing the code which relies on "logical constness".

Its better to use const int*, which will prevent mutations to the data(and it can be used as a member variable).

If it is const T * to begin with there is no need for safe_ptr. This is for cases where it has to be mutable within the class but we don't want it to be mutable within const methods.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@firewave
firewave force-pushed the ptr-ref branch 2 times, most recently from f12375c to 35fc752 Compare March 14, 2023 20:50
danmar
danmar reviewed Mar 27, 2023
View reviewed changes
Comment thread lib/valueflow.cpp Resolved

Uh oh!

There was an error while loading. Please reload this page.

@firewave
firewave force-pushed the ptr-ref branch 3 times, most recently from 91345e1 to 2b33083 Compare April 8, 2023 10:17
@firewave

firewave commented Apr 8, 2023

Copy link
Copy Markdown
Collaborator Author

Another constParameter false negative exposed by this is already being tracked as https://trac.cppcheck.net/ticket/11097.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@firewave
firewave force-pushed the ptr-ref branch from 2b33083 to 2c3420e Compare April 8, 2023 11:20
@firewave
firewave force-pushed the ptr-ref branch 2 times, most recently from e011760 to b403f49 Compare April 29, 2023 10:39
danmar
danmar reviewed Apr 30, 2023
View reviewed changes

@danmar danmar left a comment

Copy link
Copy Markdown
Collaborator

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

Looks good to me

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
@firewave
firewave force-pushed the ptr-ref branch from b403f49 to 57afc83 Compare August 2, 2023 09:50
@firewave
pass TokenList as reference into ValueFlow::setValues()
f0d6a1e
@firewave
firewave force-pushed the ptr-ref branch from 57afc83 to f0d6a1e Compare August 2, 2023 09:54
@firewave
firewave merged commit 77c479a into cppcheck-opensource:main Aug 4, 2023
@firewave
firewave deleted the ptr-ref branch August 4, 2023 16:17
@pfultz2 pfultz2 mentioned this pull request Jul 7, 2026
Fix 15102: Make copy-constructible classes copyable #8705
Merged
pfultz2 added a commit that referenced this pull request Oct 27, 2026
@pfultz2
Fix 15102: Make copy-constructible classes copyable (#8705)
a573ac3
Lots of copy constructible classes were using const and ref members which makes the classes non-copyable due to no longer supporting a copy-assignment. I replaced the ref members with a `RefThunk` class which is kind of like `std::reference_wrapper`, but it can access the member with `()` so its easier to access the members. It doesnt use `*` or `->` so it wont be confused for a possible null pointer.

Now this PR doesnt replace all reference members, just for the classes that are copy-constructible. If we want to convert a class back to use reference or const members then we can delete the copy constructor and assignment. 

Furthermore, I enabled the clang-tidy check `cppcoreguidelines-avoid-const-or-ref-data-members` to check for these cases in the future. Here are some references explaining why this is bad practice:

* https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#rc-constref
* https://lesleylai.info/en/const-and-reference-member-variables/
* https://quuxplusone.github.io/blog/2022/01/23/dont-const-all-the-things/#data-members-never-const

Beyond just being bad practice, this also has prevent me from doing certain things with the `ForwardAnalyzer` recently that might have improved it further such as joining or swaping a forked analyzer. I intentionally made these classes copyable for this reason(and were changed to non-copyable against [my feedback](#4868 (comment)) as well). I understand that using references help prevent dereferencing a nullptr which is why I added a `RefThunk` class instead of using raw pointers like previously.
chrchr-github pushed a commit to chrchr-github/cppcheck that referenced this pull request Nov 7, 2026
@pfultz2 @web-flow
Fix 15102: Make copy-constructible classes copyable (cppcheck-opensou…
ea886b2
…rce#8705)

Lots of copy constructible classes were using const and ref members which makes the classes non-copyable due to no longer supporting a copy-assignment. I replaced the ref members with a `RefThunk` class which is kind of like `std::reference_wrapper`, but it can access the member with `()` so its easier to access the members. It doesnt use `*` or `->` so it wont be confused for a possible null pointer.

Now this PR doesnt replace all reference members, just for the classes that are copy-constructible. If we want to convert a class back to use reference or const members then we can delete the copy constructor and assignment. 

Furthermore, I enabled the clang-tidy check `cppcoreguidelines-avoid-const-or-ref-data-members` to check for these cases in the future. Here are some references explaining why this is bad practice:

* https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#rc-constref
* https://lesleylai.info/en/const-and-reference-member-variables/
* https://quuxplusone.github.io/blog/2022/01/23/dont-const-all-the-things/#data-members-never-const

Beyond just being bad practice, this also has prevent me from doing certain things with the `ForwardAnalyzer` recently that might have improved it further such as joining or swaping a forked analyzer. I intentionally made these classes copyable for this reason(and were changed to non-copyable against [my feedback](cppcheck-opensource#4868 (comment)) as well). I understand that using references help prevent dereferencing a nullptr which is why I added a `RefThunk` class instead of using raw pointers like previously.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Reviewers

@danmar danmar danmar left review comments
@pfultz2 pfultz2 pfultz2 left review comments

Uh oh!

There was an error while loading. Please reload this page.

Assignees

No one assigned

Labels

None yet

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

Uh oh!

There was an error while loading. Please reload this page.

3 participants

@firewave @pfultz2 @danmar
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
You are visitor 01402503 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.