-
Notifications
You must be signed in to change notification settings - Fork 382
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
Re-export types to overridden method signatures #675
base: master
Are you sure you want to change the base?
Conversation
Should close #105. |
Will this also work if the return type (or param) of a base method is only forward-declared in |
Thanks, I'll look into it. There's also the question of the meaning of "works" here 🙂. Forward-declaring the return type is a hint that users are responsible, so I need to make sure the transitivity works all the way through use of derived to derived to base. |
I added some tests for forward-declares, which led to a slight improvement -- the compiler disallows a method definition if the return type is incomplete, even if the type is not mentioned in the body. So I adjusted the check to only cover pure declarations. This seems to work according to expectations, please take another look. |
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.
Hey! Some minor things and a suggestion about how to better handle override methods with definition. Great stuff!
95885b0
to
118cd9e
Compare
With range loops, auto and judicious use of a temporary, this became much easier to read.
When a method overrides a virtual method in a base class, all types mentioned in the signature will already be covered for by the base's header. As we must derive from a base to override methods, and derivation requires a complete type, we already know that we've included the base header to be able to override a method. Exploit this and ignore all uses in overridden method signatures as they must already be satisifed for the code to compile. For example: // base.h #include <string> class Foo; struct Base { virtual std::string method(); virtual Foo foo_method(); }; // derived.h #include "base.h" struct Derived : public Base { // No warnings about <string> or Foo here. std::string method(); Foo foo_method(); }; But this does not extend to the all uses of the type. For example, if we use the type in the definition of an overridden method, the definition file must still include the type: // derived.cc #include "derived.h" // This requires <string>. std::string Derived::method() { return std::string("abc"); } // This requires the complete type of Foo, // even if it's not mentioned in the body. Foo Derived::foo_method() { abort(); } This reduces the number of required includes in headers significantly. Fixes issue include-what-you-use#105.
Updated. I think this is even better:
If someone has time to take a look, I'd be delighted. Will probably merge this soonish as is if I don't hear anything. |
Hey! I will take a look :) |
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.
Looking good! Left a few minor comments.
} | ||
|
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.
It has been a while and not recall exactly, but could it be that there is only a FunctionProtoType
without a corresponding FunctionDecl
? Because in that case the new code won't find the prototype.
if (HasCovariantReturnType(method)) { | ||
// Covariant return types require that the full return type is available, | ||
// so don't special-case the return type for functions with covariance. | ||
} else { |
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 find this a bit confusing. Why not invert the condition and explain what we do for non-covariant return types?
|
||
// The return type could be a type loc, though never seen this in practice. | ||
const TypeSourceInfo* tsi = method->getTypeSourceInfo(); | ||
const TypeLoc retloc = tsi->getTypeLoc(); |
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.
Doesn't this refer to the type of the method itself?
// Unused argument available from Base, in inline method. | ||
void ArgumentUnusedInline(const IndirectClass& x) override { | ||
} | ||
}; |
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.
Not sure if really needed, but it could be interesting to see that a method not overridden from the parent requires its types to be included directly.
I just tried out the branch and it works nicely. Thanks @kimgr ! It doesn't seem to re-export the return type, but it does do the parameter types and that cuts down on quite a lot of noise form IWYU. What's needed to get this PR merged? |
@mactalla Thank you, good to hear! I've been experimenting with alternative solutions (lots of them!) offline, but still don't have anything I'm entirely happy with. Maybe I should pick a favorite and get it merged 🙂. I'm a little pressed for time right now, but I'll make an effort to cobble something together soon. |
Just to make sure there's some update: I'm still working on/investigating solutions for this offline. It's a surprisingly tricky problem. My current take is:
There's quite a bit of reporting machinery to get the So I think there's some design shortcut for templates that needs to be resolved first, and then this might be easier. I'll keep at it. |
@kimgr, you may take a look on my variant. I block method declaration traversal except default arguments which may differ from declaration in base class (not sure about exception specifications). Covariant return types are handled in |
But, maybe, just adding base class headers into associated headers is better. It would also cover type reporting for public and protected field members, which is also redundant for derived classes. On the other hand, private members are base class implementation details, and subclasses should not rely on presence their type info in base class header. Not sure, how acceptable it would be... |
When a method overrides a virtual method in a base class, all types
mentioned in the signature will already be covered for by the base's
header.
As we must derive from a base to override methods, and derivation
requires a complete type, we already know that we've included the base
header to be able to override a method.
Exploit this and ignore all uses in overridden method signatures as they
must already be satisifed for the code to compile.
For example:
But this does not extend to the all uses of the type. For example, if we
use the type in the body of an overridden method, the definition file
must still include the type:
This reduces the number of required includes in headers significantly.