Some Eclipse Foundation services are deprecated, or will be soon. Please ensure you've read this important communication.
Bug 339795 - [checker] Checker that finds class members that are not initialized in constructor
Summary: [checker] Checker that finds class members that are not initialized in constr...
Status: RESOLVED FIXED
Alias: None
Product: CDT
Classification: Tools
Component: cdt-codan (show other bugs)
Version: 8.0   Edit
Hardware: All All
: P3 enhancement (vote)
Target Milestone: 8.1.0   Edit
Assignee: Marc-André Laperle CLA
QA Contact: Elena Laskavaia CLA
URL:
Whiteboard:
Keywords:
Depends on:
Blocks:
 
Reported: 2011-03-12 11:53 EST by Anton G. CLA
Modified: 2012-02-23 11:33 EST (History)
5 users (show)

See Also:


Attachments
Basic implementation of the checker described above (17.69 KB, patch)
2011-03-18 04:40 EDT, Anton G. CLA
no flags Details | Diff
Basic implementation of the checker described above (with comment fixed) (17.67 KB, patch)
2011-03-18 06:17 EDT, Anton G. CLA
no flags Details | Diff
Checker implementation with members order check (20.74 KB, patch)
2011-03-21 07:26 EDT, Anton G. CLA
no flags Details | Diff
Checker implementation with static members fix (but without member order check) (18.07 KB, patch)
2011-03-29 03:50 EDT, Anton G. CLA
no flags Details | Diff
Fixed checker implementation (see comment for details) (17.17 KB, patch)
2011-04-14 08:07 EDT, Anton G. CLA
malaperle: iplog+
Details | Diff
Fixed checker implementation & tests (20.07 KB, patch)
2011-04-28 08:47 EDT, Anton G. CLA
no flags Details | Diff
Checker implementation that skips constructors with function and non-const method calls (with preference) (26.29 KB, patch)
2011-07-05 05:55 EDT, Anton G. CLA
malaperle: iplog+
Details | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Anton G. CLA 2011-03-12 11:53:34 EST
Code examples. The simplest case:
struct C
{
    C() {}

    int v;  // <-- Variable "v" is not initialized in constructor. It may cause to random behavior in functions that will read it.
}


More complex cases:
struct C
{
    C()
       : v1 (0)
    {
        v2 = 0;
    }

    int v1;         // OK: Variable may be initialized in initialization list
    int v2;         // OK: Variable may be initialized in constructor body
    int v3;         // Warning: v3 is not initialized!
    std::string s;  // OK: Variables of complex types (classes & structs) may be no initialized
}

Of course more complicated cases are possible (e.g. initialization of variable by pointer or reference in another member function that is called from constructor) but it seems complex example above covers the most common used cases.


Note: The similar checker exists in cppcheck tool (http://sourceforge.net/projects/cppcheck/).
Comment 1 Anton G. CLA 2011-03-18 04:40:32 EDT
Created attachment 191487 [details]
Basic implementation of the checker described above

I have implemented checker for the problem described above. It seems it works fine. Can somebody review and commit or comment it?

P.S. Should I set review-flag in cases like this?
Comment 2 Markus Schorn CLA 2011-03-18 05:12:15 EDT
(In reply to comment #1)
> Created attachment 191487 [details]
> Basic implementation of the checker described above
> I have implemented checker for the problem described above. It seems it works
> fine. Can somebody review and commit or comment it?
> P.S. Should I set review-flag in cases like this?

Note, in c++ a field will always be initialized. In the example 'v3' will have the value 0.
Comment 3 Anton G. CLA 2011-03-18 05:26:30 EDT
(In reply to comment #2)
> Note, in c++ a field will always be initialized. In the example 'v3' will have
> the value 0.

You are not right. I have just tried to instantiate this class like this:

int main() {
	C c;
	cout << c.v3 << endl;
	return 0;
}

The output was:
-1079585528
And it differs between launches.

Maybe your compiler initializes values for you and maybe it is in debug only? I use gcc 4 on Linux, but it seems C++ standard says that class member initial values are not defined.

NOTE: In C++ fields are always initialized with 0 if class was created as global or statical.
Comment 4 Markus Schorn CLA 2011-03-18 06:08:28 EDT
(In reply to comment #3)
You are right, thanks for the clarification. 
The standard says that members like 'v3' are default initialized. That sounds promising, however for an integral type, default initialization does not do anything.
Comment 5 Anton G. CLA 2011-03-18 06:17:26 EDT
Created attachment 191496 [details]
Basic implementation of the checker described above (with comment fixed)

Sorry, I have just notices that I didn't change the comment at the beginning of ClassMembersInitializationCheckerTest file. This patch differs from previous only in that.
Comment 6 Marc-André Laperle CLA 2011-03-18 13:20:23 EDT
I think we should warn for initializer inversions in the same checker.

class Foo
{
   int a;
   int b;
public:
   Foo(): 
     b(0), 
     a(0)  // warning
     {}
};
Comment 7 Anton G. CLA 2011-03-18 16:48:18 EDT
(In reply to comment #6)
> I think we should warn for initializer inversions in the same checker.
> 
> class Foo
> {
>    int a;
>    int b;
> public:
>    Foo(): 
>      b(0), 
>      a(0)  // warning
>      {}
> };

I think it should be another checker because:
  - slightly differ information should be gathered (members declaration order instead of simple declaration).
  - slightly another construct set should be checked (only initialization list but not assignments).
  - user may want to turn off or limit scope of violations from one of this checker but leave from another.

So I will try to implement it too, but as the separate checker, don't you mind?
Comment 8 Anton G. CLA 2011-03-21 07:26:26 EDT
Created attachment 191603 [details]
Checker implementation with members order check

I have looked at Codan preferences once again and noticed that user can turn on/off problems but not checkers. So I agree with you, I should be the same checker. Here its implementation.
Comment 9 Anton G. CLA 2011-03-21 10:38:44 EDT
(In reply to comment #8)
> Created attachment 191603 [details]
> Checker implementation with members order check
> 
> I have looked at Codan preferences once again and noticed that user can turn
> on/off problems but not checkers. So I agree with you, I should be the same
> checker. Here its implementation.

There is a few problems with this patch:
- it does not take into account order of base classes in initialization list (I have fixed this problem in my working copy).
- members order returned by ICPPClassType.getDeclaredFields() is not in which they were declared by user. I don't know how to resolve this problem. Can somebody advise me something?

P.S. So the most working patch for now is the second.
Comment 10 Marc-André Laperle CLA 2011-03-28 13:39:59 EDT
Hi, I've been using the checker for a while (second patch), I noticed a false positive with static members:

class Foo
{
    Foo(){} // warning here
    static int bar;
};

int Foo::bar = 0;
Comment 11 Anton G. CLA 2011-03-28 15:17:59 EDT
(In reply to comment #10)
> Hi, I've been using the checker for a while (second patch), I noticed a false
> positive with static members:
> 
> class Foo
> {
>     Foo(){} // warning here
>     static int bar;
> };
> 
> int Foo::bar = 0;

Oh, yes, you are right. I don't process static member separately. I'll fix this and create a new patch soon.

P.S. I have to revert members order check (from the third patch) cause it is not properly implemented and I have not ideas how to fix the problem.
Comment 12 Anton G. CLA 2011-03-29 03:50:53 EDT
Created attachment 192065 [details]
Checker implementation with static members fix (but without member order check)

Static members are taken into account now.
NOTE: There is not warning in cases like this:
  class Foo
  {
      Foo(){} // warning here
      static int bar;
  };

  int Foo::bar;  // No initialization here

Cause static variables are always initialized with 0.
Comment 13 Anton G. CLA 2011-03-31 10:59:29 EDT
Will somebody review and commit the latest patch? Or is it not useful enough?
Comment 14 Marc-André Laperle CLA 2011-03-31 23:11:46 EDT
(In reply to comment #13)
> Will somebody review and commit the latest patch? Or is it not useful enough?

It is useful, I've been using it :) I'll have some time in the next days to look at the patch.
Comment 15 Anton G. CLA 2011-04-01 02:03:36 EDT
(In reply to comment #14)
> (In reply to comment #13)
> > Will somebody review and commit the latest patch? Or is it not useful enough?
> 
> It is useful, I've been using it :) I'll have some time in the next days to
> look at the patch.

Thanks, it will be very kind of you! I want this checker to be part of Codan.
Comment 16 Marc-André Laperle CLA 2011-04-05 08:15:06 EDT
Hi, here's what I have so far:

- I'm not sure we need getOrResolveBinding, isn't just calling resolveBinding equivalent?
- I think all for loops can be replaced by foreach style of loops
- Consider 2 classes A and B. If B subclasses A and the analyzed B constructor doesn't call one of the class A constructors and doesn't initialize one of the class A members, then I think it should warn about A members. I think it's reasonable to ignore the the case B constructor calls A constructor but the A constructor doesn't initialize all members. Doing this would be too expensive since we would need to possibly build an additional AST to analyse the A constructor.

- testBaseClassMEmberShouldNotBeTakenIntoAccount, there's an extra capital E there.
- constructorMap, should be constructorSet instead? Or something more descriptive? fieldsInConstructor?
- I think this should be considered a false positive:
void func(int & a)
{
  a = 0;
}

class A {
  int foo;
public:
  A() // warning
  {
    func(foo);
  }
};

- I think this should be considered a false positive:
class A {
  int foo;
public:
  A() // warning
  {
    (foo) = 0;
  }
};

- I think this should be considered a false positive. But we may just consider foo assigned as soon as a variable holds a reference to it, just to keep things sane.
class A {
  int foo;
public:
  A() // warning
  {
    int & bar = foo;
    bar = 0;
  }
};
Comment 17 Anton G. CLA 2011-04-05 10:28:32 EDT
Hi, thank you for review. I have a few questions about cases you

(In reply to comment #16)
> Hi, here's what I have so far:
> 
> - I'm not sure we need getOrResolveBinding, isn't just calling resolveBinding
> equivalent?

It seems you are right. I saw this in other checkers and copied to my. I'll fix it.


> - Consider 2 classes A and B. If B subclasses A and the analyzed B constructor
> doesn't call one of the class A constructors and doesn't initialize one of the
> class A members, then I think it should warn about A members. I think it's
> reasonable to ignore the the case B constructor calls A constructor but the A
> constructor doesn't initialize all members. Doing this would be too expensive
> since we would need to possibly build an additional AST to analyse the A
> constructor.

If B constructor does not call A constructor directly the default A constructor will be called. So warnings will be added to the following correct case:
struct A
{
  A() : i(0) {}
  int a;
};
struct B
{
  B() {}  // A() will be called by default => A::a will be initialized
};


> - I think this should be considered a false positive:
> void func(int & a)
> {
>   a = 0;
> }
> 
> class A {
>   int foo;
> public:
>   A() // warning
>   {
>     func(foo);
>   }
> };
> - I think this should be considered a false positive. But we may just consider
> foo assigned as soon as a variable holds a reference to it, just to keep things
> sane.
> class A {
>   int foo;
> public:
>   A() // warning
>   {
>     int & bar = foo;
>     bar = 0;
>   }
> };

Ok, how far should we go? There are a lot of ways in which class members may be initialized. Here are some of them:
1. Only declaration of function is known:
void func(int & a);  // func() is defined in some other place
class A {
  int foo;
public:
  A() // need warning on foo?
  {
    func(foo);
  }
};

2. How should we process pointers?
class A {
  int foo;
public:
  A() // need warning on foo?
  {
    int* bar = &foo;
    bar++;  // Pointer was changed.
    *bar = 0;
  }
};

3. What about code with semantic errors?
void func(const int & a)
{
  a = 0;  // Yes, this code will not compile, but how checker should process it?
}
class A {
  int foo;
public:
  A() // need warning on foo?
  {
    func(foo);
  }
};

4. Should we track execution flow?
class A {
  int foo;
public:
  A() // need warning on foo?
  {
    if (someFunction())
      foo = 0;
  }
};

So, I can cover cases you mentioned above but it seems a lot of other uncovered cases are left. Don't you think that we should consider only base cases?
Comment 18 Marc-André Laperle CLA 2011-04-05 21:08:52 EDT
(In reply to comment #17)
> If B constructor does not call A constructor directly the default A constructor
> will be called. So warnings will be added to the following correct case:

Duh, yes sorry.
 
> Ok, how far should we go? There are a lot of ways in which class members may be
> initialized.
...
> So, I can cover cases you mentioned above but it seems a lot of other uncovered
> cases are left. Don't you think that we should consider only base cases?

I think we should only consider base cases but if we encounter a complex case for a field it should default the field to initialized, not uninitialized. I wonder if we could do something like CSearchUtil.isWriteOccurrence which is being used for marking write occurrences?

CSearchUtil.isWriteOccurrence is in cdt.ui but we only need the CPP case so we can do something like
bool isWrite = ((CPPVariableReadWriteFlags.getReadWriteFlags(node) & PDOMName.WRITE_ACCESS) != 0);

So it would go something like this:
1. Build constructorMap as we do right now
2. Visit all IASTName in the constructor definition
3. Check the write access, remove from constructorMap if it is

> 1. Only declaration of function is known:
> void func(int & a);  // func() is defined in some other place
> class A {
>   int foo;
> public:
>   A() // need warning on foo?
>   {
>     func(foo);
>   }
> };

No warning. I don't think we should look into func definition (like for occurrences, it will be considered write).
 
> 2. How should we process pointers?
> class A {
>   int foo;
> public:
>   A() // need warning on foo?
>   {
>     int* bar = &foo;
>     bar++;  // Pointer was changed.
>     *bar = 0;
>   }
> };

No warning, as soon as we detect int* bar = &foo; (like for occurrences, it will be considered write).

> 3. What about code with semantic errors?
> void func(const int & a)
> {
>   a = 0;  // Yes, this code will not compile, but how checker should process
> it?
> }
> class A {
>   int foo;
> public:
>   A() // need warning on foo?
>   {
>     func(foo);
>   }
> };

Warning. (like for occurrences, it will be considered read)

> 4. Should we track execution flow?
> class A {
>   int foo;
> public:
>   A() // need warning on foo?
>   {
>     if (someFunction())
>       foo = 0;
>   }
> };

No warning. (like for occurrences, it will be considered write).
Comment 19 Anton G. CLA 2011-04-06 02:19:12 EDT
(In reply to comment #18)

> CSearchUtil.isWriteOccurrence is in cdt.ui but we only need the CPP case so we
> can do something like
> bool isWrite = ((CPPVariableReadWriteFlags.getReadWriteFlags(node) &
> PDOMName.WRITE_ACCESS) != 0);
...

Oh, I didn't know about it, but it seems it should help. I'll fix your notes and provide a new patch soon.
Comment 20 Anton G. CLA 2011-04-14 08:07:38 EDT
Created attachment 193246 [details]
Fixed checker implementation (see comment for details)

Sorry for delay, finally I fixed things described above:
1. CPPVariableReadWriteFlags is used to detect variable modification (it really makes checker much more clever!).
2. resolveBinding() is used instead of getOrResolveBinding().
3. Foreach loops are used everywhere.
4. Typo in testBaseClassMemberShouldNotBeTakenIntoAccount was fixed.
5. constructorMap was renamed to fieldsInConstructor.
6. A few test cases were added (the examples that were considered as false positive).


I have a question:
There is a warning on my checker's code:
  - Discouraged access: The type CPPVariableReadWriteFlags is not accessible due to restriction on required project org.eclipse.cdt.core
  (and a few similar)
How it should be fixed?
Comment 21 Marc-André Laperle CLA 2011-04-17 18:06:38 EDT
(In reply to comment #20)
> I have a question:
> There is a warning on my checker's code:
>   - Discouraged access: The type CPPVariableReadWriteFlags is not accessible
> due to restriction on required project org.eclipse.cdt.core
>   (and a few similar)
> How it should be fixed?

It's unfortunate but I think the checker really benefits a lot from using this. If we start using this in other checkers then we might think about creating API for it. But for now, I think it's OK.

In testNoErrorsOnReadingFunctionCall, in the comments of the test code, 
// 1 warning for: i1.
It should be 'for: i'. But it's minor so I'll fix it when I commit.

I'll fill a CQ for IP review.
Comment 22 Marc-André Laperle CLA 2011-04-17 18:29:55 EDT
CQ 5099. I think you need to do a similar statement to bug 326269 comment 12.
Comment 23 Anton G. CLA 2011-04-18 02:07:09 EDT
(In reply to comment #21)
> It's unfortunate but I think the checker really benefits a lot from using this.
> If we start using this in other checkers then we might think about creating API
> for it. But for now, I think it's OK.

Eclipse's Quick Fix proposes to add "@SuppressWarnings("restriction")". Should it be added until we decide to create API?
Comment 24 Anton G. CLA 2011-04-18 02:08:42 EDT
a.  I authored 100% of the content I am contributing
b.  I have the rights to donate the content to Eclipse
c.  I contribute the content under the EPL
Comment 25 Marc-André Laperle CLA 2011-04-21 02:57:19 EDT
(In reply to comment #23)
> (In reply to comment #21)
> > It's unfortunate but I think the checker really benefits a lot from using this.
> > If we start using this in other checkers then we might think about creating API
> > for it. But for now, I think it's OK.
> 
> Eclipse's Quick Fix proposes to add "@SuppressWarnings("restriction")". Should
> it be added until we decide to create API?

I'd rather keep the warnings as a reminder. It seems most of CDT doesn't suppress the warnings like that, not sure if that's intentional or not.
Comment 26 Anton G. CLA 2011-04-21 03:02:19 EDT
(In reply to comment #25)
> I'd rather keep the warnings as a reminder. It seems most of CDT doesn't
> suppress the warnings like that, not sure if that's intentional or not.
Ok, thank you for answer.
Comment 27 Elena Laskavaia CLA 2011-04-24 20:54:28 EDT
Are you guys trying to write checker for uninitialized variables?
It is huge can of warms, just few notes:
- Pick a simple case and try to implement that
- In more complex case choose NOT to report error (which include things like templates, multiple inheritance, fields of unknown types, conditional initialization, etc)
Comment 28 Anton G. CLA 2011-04-25 08:23:40 EDT
(In reply to comment #27)
> Are you guys trying to write checker for uninitialized variables?
> It is huge can of warms, just few notes:
> - Pick a simple case and try to implement that
> - In more complex case choose NOT to report error (which include things like
> templates, multiple inheritance, fields of unknown types, conditional
> initialization, etc)

Thanks, we just came up with this. If there are some false positives, please let me know - I'll try to fix them.
Comment 29 Elena Laskavaia CLA 2011-04-26 21:53:20 EDT
tests:
- checkMultiErrorsOnLine is same as checkErrorLines method in superclass
- checkErrorOnLine same as checkErrorLines with one argument
- first and second test case - same code ?
- test itself should be added is suite - AutomatedIntegrationSuite

False positive:
// rather bad coding - protected constructor - class A cannot be instantiate with uninitialized var
 class A { protected: A(){}; public: int a;};
 class C: public A {
   C() {
	   a = 1;
   }
 };

// using setters in constructor
class A { int a; 
   A(int a){ setA(a);};
   void setA(int a) { this->a = a;}
}

// unions
 union U {
	 int a;
	 char b;
	 U(){
		a=0; // reports that b is not initialized - not true strictly speaking
	 }
 };

Expected false negative (should we write tests for them too?)
- No default constructor found
- Field initialed only on the one of the if branches
Comment 30 Anton G. CLA 2011-04-27 06:57:13 EDT
(In reply to comment #29)
> tests:
> - checkMultiErrorsOnLine is same as checkErrorLines method in superclass

It is not quite the same. For example: checkMultiErrorsOnLine(3, 5) is the same as checkErrorLines(3, 3, 3, 3, 3), but it seems that checkMultiErrorsOnLine() is more convenient. So, should I remove checkMultiErrorsOnLine() and use checkErrorLines() instead?


> - checkErrorOnLine same as checkErrorLines with one argument
> - first and second test case - same code ?
> - test itself should be added is suite - AutomatedIntegrationSuite

Thanks, fixed.


> False positive:
> // rather bad coding - protected constructor - class A cannot be instantiate
> with uninitialized var
>  class A { protected: A(){}; public: int a;};
>  class C: public A {
>    C() {
>        a = 1;
>    }
>  };

Why it is false positive? There might me another class (may be even in another translation unit) that does not initialize 'a'. Consider another case:
void f()
{
  class A { protected: A(){}; public: int a;};  // Warning on 'a'
  A a;
  a.a = 1;
}
Is this false positive too?
If you don't agree please, describe more detailed which cases should be handled.


> // using setters in constructor
> class A { int a; 
>    A(int a){ setA(a);};
>    void setA(int a) { this->a = a;}
> }

Do you think we should check all the methods called from constructor too?


> // unions
>  union U {
>      int a;
>      char b;
>      U(){
>         a=0; // reports that b is not initialized - not true strictly speaking
>      }
>  };

I think unions should be skipped at all, cause it is quite hard to figure out what field of union were initialized with assignment.


> Expected false negative (should we write tests for them too?)
> - No default constructor found

Maybe it will be better to add warning if there are no constructors in the class and there are a few variables of simple type?


> - Field initialed only on the one of the if branches

I don't want to add tests that rely on incorrect checker behavior, but if you think they should be added - I'll add them.
Comment 31 Elena Laskavaia CLA 2011-04-27 19:06:13 EDT
> 
> 
> > False positive:
> > // rather bad coding - protected constructor - class A cannot be instantiate
> > with uninitialized var
> >  class A { protected: A(){}; public: int a;};
> >  class C: public A {
> >    C() {
> >        a = 1;
> >    }
> >  };
> 
> Why it is false positive? There might me another class (may be even in another
> translation unit) that does not initialize 'a'. Consider another case:
> void f()
> {
>   class A { protected: A(){}; public: int a;};  // Warning on 'a'
>   A a;
>   a.a = 1;
> }
> Is this false positive too?
> If you don't agree please, describe more detailed which cases should be
> handled.

You cannot have a code like that, you cannot instantiate A because it does not have a public constructor. I am not saying your checker should catch that, it is too complex and bad code style anyway.


> 
> 
> > // using setters in constructor
> > class A { int a; 
> >    A(int a){ setA(a);};
> >    void setA(int a) { this->a = a;}
> > }
> 
> Do you think we should check all the methods called from constructor too?
No, it would be too much

> 
> 
> > // unions
> >  union U {
> >      int a;
> >      char b;
> >      U(){
> >         a=0; // reports that b is not initialized - not true strictly speaking
> >      }
> >  };
> 
> I think unions should be skipped at all, cause it is quite hard to figure out
> what field of union were initialized with assignment.

Yes, they should be skipped.

> 
> 
> > Expected false negative (should we write tests for them too?)
> > - No default constructor found
> 
> Maybe it will be better to add warning if there are no constructors in the
> class and there are a few variables of simple type?

If indexer cannot find the implementation it may be false positive

> 
> 
> > - Field initialed only on the one of the if branches
> 
> I don't want to add tests that rely on incorrect checker behavior, but if you
> think they should be added - I'll add them.

Tests should be added for expected behavior, in case it changes - test would fail and it should be inspected.
Comment 32 Elena Laskavaia CLA 2011-04-27 22:25:13 EDT
(In reply to comment #30)
> (In reply to comment #29)
> > tests:
> > - checkMultiErrorsOnLine is same as checkErrorLines method in superclass
> 
> It is not quite the same. For example: checkMultiErrorsOnLine(3, 5) is the same
> as checkErrorLines(3, 3, 3, 3, 3), but it seems that checkMultiErrorsOnLine()
> is more convenient. So, should I remove checkMultiErrorsOnLine() and use
> checkErrorLines() instead?
> 
Sorry it is a different method please disregard my comment
Comment 33 Anton G. CLA 2011-04-28 02:23:29 EDT
(In reply to comment #31)
> > I think unions should be skipped at all, cause it is quite hard to figure out
> > what field of union were initialized with assignment.
> 
> Yes, they should be skipped.

Fixed.


> > > Expected false negative (should we write tests for them too?)
> > > - No default constructor found
> > 
> > Maybe it will be better to add warning if there are no constructors in the
> > class and there are a few variables of simple type?
> 
> If indexer cannot find the implementation it may be false positive

How can it happen? Can you give an example


> Tests should be added for expected behavior, in case it changes - test would
> fail and it should be inspected.

Ok, the tests were added.

P.S. I will provide a new patch with fixes soon.
Comment 34 Anton G. CLA 2011-04-28 08:47:03 EDT
Created attachment 194262 [details]
Fixed checker implementation & tests

Changes:
  - unions are skipped
  - tests were fixed after Alena's review
Comment 35 Anton G. CLA 2011-06-28 13:29:05 EDT
So what about my patch? Indigo Release is done, migration to git seems to be completed, is it time to commit the patch? What is the status of IP review request? Is there anything I should do (e.g. convert my patch to the new git repository)?
Comment 36 Marc-André Laperle CLA 2011-06-28 13:35:10 EDT
(In reply to comment #35)
> So what about my patch? Indigo Release is done, migration to git seems to be
> completed, is it time to commit the patch? What is the status of IP review
> request? Is there anything I should do (e.g. convert my patch to the new git
> repository)?

IP review is done. I'll commit soon.
Comment 37 Anton G. CLA 2011-06-28 13:39:42 EDT
(In reply to comment #36)
> IP review is done. I'll commit soon.

Ok, thanks!
Comment 38 Marc-André Laperle CLA 2011-07-01 09:48:01 EDT
> // using setters in constructor
> class A { int a; 
>    A(int a){ setA(a);};
>    void setA(int a) { this->a = a;}
> }

Sorry, I thought that case was handled in the newest patch. I feel like it's too important to ignore. I think we should do false negatives for cases where constructor calls non-const method or a function that passes 'this' as a reference or pointer, such as:

class C;
void initObject(C* c);

class C {
public:
    int field;
    C() {
        initObject(this);
    }
};
Comment 39 Anton G. CLA 2011-07-01 10:14:22 EDT
(In reply to comment #38)
> I think we should do false negatives for cases where
> constructor calls non-const method or a function that passes 'this' as a
> reference or pointer, such as:
> 
> class C;
> void initObject(C* c);
> 
> class C {
> public:
>     int field;
>     C() {
>         initObject(this);
>     }
> };
I cannot figure out what wrong with this case. It seems currently checker will print warning on field. Do you think it is not necessary? Or doesn't it print?
Comment 40 Anton G. CLA 2011-07-01 10:47:44 EDT
(In reply to comment #38)
> I think we should do false negatives for cases where constructor calls non-const method...

And one more question for non-const method call. Should we check its body or just don't report any warnings on class members if it is found? And what should we do if method body is not available (e.g. it is in cpp-file, but constructor is in header)?
Comment 41 Marc-André Laperle CLA 2011-07-01 21:15:06 EDT
(In reply to comment #40)
> (In reply to comment #38)
> > I think we should do false negatives for cases where constructor calls non-const method...
> 
> And one more question for non-const method call. Should we check its body or
> just don't report any warnings on class members if it is found? And what should
> we do if method body is not available (e.g. it is in cpp-file, but constructor
> is in header)?

I meant we should not report any warnings. I think analyzing the body inside all method/function calls would be excessive, especially if additional ASTs need to be computed.

Sorry, I should've added this:

void initObject(C* c) {
	c->field = 0; // false positive
}

So, I think we should not report any warnings if we find a function call that's passing 'this' as a reference or pointer parameter.
Comment 42 Anton G. CLA 2011-07-02 07:22:30 EDT
(In reply to comment #41)
> So, I think we should not report any warnings if we find a function call that's
> passing 'this' as a reference or pointer parameter.

It is not hard to implement, but it seems that we'll have too many false negatives.

I think it is better to print warning on this case:
> class C {
> public:
>     int field;
>     C() {
>         initObject(this);
>     }
> };

Cause it is easy to fix it like this:

class C {
public:
    int field;
    C() : field(0) {
        initObject(this);
    }
};

And it will be more reliable cause if body of initObject() will change in future the variable field will still be initialized. However if we just skip these cases we will miss the initObject() change and field may become uninitialized. What do you think?
Comment 43 Marc-André Laperle CLA 2011-07-05 02:12:07 EDT
(In reply to comment #42)
> (In reply to comment #41)
> > So, I think we should not report any warnings if we find a function call that's
> > passing 'this' as a reference or pointer parameter.
> 
> It is not hard to implement, but it seems that we'll have too many false
> negatives.

I would rather have the checker work only on simple cases and have false negatives than have false positives and I think that's the approach the other checkers take as well. That said, one thing we could do to minimize the false negatives is to check the function bodies that are in the same AST, to avoid computing additional ASTs. IASTTranslationUnit.getDefinitionsInAST might be of help here to locate the non-const function of interest in the AST then it can be visited to look for the fields. I don't know how fast that would be.

For functions defined outside the current AST, there's probably a way to use IIndex.findDefinitions(IBinding binding), IIndexName.getEnclosedNames() and IIndexName.isWriteAccess().

So, there are some options if we want to look inside the function bodies but I feel like the checker would still be useful if we only considered basic cases and did not warn at all when we have such non-const function calls.

> I think it is better to print warning on this case:
> > class C {
> > public:
> >     int field;
> >     C() {
> >         initObject(this);
> >     }
> > };
> 
> Cause it is easy to fix it like this:
> 
> class C {
> public:
>     int field;
>     C() : field(0) {
>         initObject(this);
>     }
> };
> 
> And it will be more reliable cause if body of initObject() will change in
> future the variable field will still be initialized. However if we just skip
> these cases we will miss the initObject() change and field may become
> uninitialized. What do you think?

I don't know about that, I wouldn't want to have to initialize a member twice to make a false positive disappear, especially if it's not a simple constant and it involves other calls. I also feel like this checker should be more about warning when it knows for sure that a member is not initialized and not about it being initialized in a more 'future-proof' way.
Comment 44 Anton G. CLA 2011-07-05 03:19:02 EDT
(In reply to comment #43)

> I don't know about that, I wouldn't want to have to initialize a member twice
> to make a false positive disappear, especially if it's not a simple constant
> and it involves other calls.

The checker checks simple types only (int, pointers, enums, ...), so it seems there should not involve other calls.

> I also feel like this checker should be more about
> warning when it knows for sure that a member is not initialized and not about
> it being initialized in a more 'future-proof' way.

Ok, let me implement a preference "Skip constructors with non-constant methods calls" which will be enabled by default and will work as you said. But if user wants to check all constructors, it can disable this option and the checker will work as it currently does. Do you agree with this solution?
Comment 45 Anton G. CLA 2011-07-05 05:55:51 EDT
Created attachment 199104 [details]
Checker implementation that skips constructors with function and non-const method calls (with preference)

The patch implementing my proposed solution.
Comment 46 Marc-André Laperle CLA 2011-07-06 01:56:07 EDT
(In reply to comment #44)
> (In reply to comment #43)
> Ok, let me implement a preference "Skip constructors with non-constant methods
> calls" which will be enabled by default and will work as you said. But if user
> wants to check all constructors, it can disable this option and the checker
> will work as it currently does. Do you agree with this solution?

Yes I agree. Patch and tests look good.
Comment 47 Marc-André Laperle CLA 2011-07-06 01:58:18 EDT
Comment on attachment 199104 [details]
Checker implementation that skips constructors with function and non-const method calls (with preference)

Woops, meant iplog+ not review+
Comment 48 Marc-André Laperle CLA 2011-07-06 02:07:19 EDT
Comment on attachment 193246 [details]
Fixed checker implementation (see comment for details)

This was the original approved patch. I don't believe the new one has to go through IP review again since it's not that big of a difference really.
Comment 49 Marc-André Laperle CLA 2011-07-06 02:37:37 EDT
Committed to master. Thanks for working on this!
Comment 50 Anton G. CLA 2011-07-06 08:40:05 EDT
(In reply to comment #49)
> Committed to master. Thanks for working on this!

Thank you too!
Comment 51 Axel Mueller CLA 2012-01-11 09:32:58 EST
(In reply to comment #38)
> class C;
> void initObject(C* c);
> 
> class C {
> public:
>     int field;
>     C() {
>         initObject(this);
>     }
> };
When I put the constructor definition into the .cpp file 

C::C(){
initObject(this);
}

then I still get false positive warnings.
Can somebody have a look at this?
Comment 52 Axel Mueller CLA 2012-01-11 09:39:46 EST
I get another false positive warning when I use a simple copy constructor:

class C
{
public:
  int field;
  C();
  C(const C& toBeCopied)
  {
    *this = toBeCopied;  
  };
};
Comment 53 Marc-André Laperle CLA 2012-01-11 11:30:30 EST
(In reply to comment #51)
> then I still get false positive warnings.
> Can somebody have a look at this?

Can you create a separate bug? I will look at it. Thanks!
Comment 54 Axel Mueller CLA 2012-01-12 03:50:45 EST
(In reply to comment #53)
> (In reply to comment #51)
> > then I still get false positive warnings.
> > Can somebody have a look at this?
> 
> Can you create a separate bug? I will look at it. Thanks!
I created bugs #368419 and #368420 to cover the problems mentioned in comment #51 and #52