This is an archive of the discontinued LLVM Phabricator instance.

[clangd] Use operator<< to prevent printers issues in Gtest
ClosedPublic

Authored by malaperle on Mar 21 2018, 5:27 PM.

Details

Summary

It is possible that there will be two different instantiations of
the printer template for a given type and some tests could end up calling the
wrong (default) one. For example, it was seen in CodeCompleteTests.cpp when
printing CompletionItems that it would use the wrong printer because the default
is also instantiated in ClangdTests.cpp.

With this change, objects that were previously printed with a custom Printer now
get printed through the operator<< which is declared alongside the class.
This rule of the thumb should make it less error-prone.

Event Timeline

malaperle created this revision.Mar 21 2018, 5:27 PM
ilya-biryukov accepted this revision.Mar 22 2018, 5:43 AM

LGTM.
I wonder if there's a mechanism to always include the printers when including gtest.h, but having a convention to always include them seems ok for now.

This revision is now accepted and ready to land.Mar 22 2018, 5:43 AM

We could create a file `ClangdTesting.h" which includes everything tested related (gtest, gmock, printers, syncapi, etc). The convention would be that test files would just include that.

We could create a file `ClangdTesting.h" which includes everything tested related (gtest, gmock, printers, syncapi, etc). The convention would be that test files would just include that.

Yeah, sounds good. I would exclude syncapi from the list, though. Not all tests should necessarily depend on ClangdServer.
@sammccall , @ioeric , @hokein, @bkramer are you on board with the idea to have an umbrella header for gtest+gmock+printers?

We could create a file `ClangdTesting.h" which includes everything tested related (gtest, gmock, printers, syncapi, etc). The convention would be that test files would just include that.

Yeah, sounds good. I would exclude syncapi from the list, though. Not all tests should necessarily depend on ClangdServer.
@sammccall , @ioeric , @hokein, @bkramer are you on board with the idea to have an umbrella header for gtest+gmock+printers?

Sounds good to me too. I can update the patch once there is an agreement.

malaperle updated this revision to Diff 139598.Mar 23 2018, 7:41 AM

Use a header for common inclusions for tests

malaperle retitled this revision from [clangd] Move GTest printers to separate file to [clangd] Use a header for common inclusions for tests.Mar 23 2018, 7:42 AM
malaperle edited the summary of this revision. (Show Details)
simark accepted this revision.Mar 23 2018, 7:57 AM
simark added inline comments.
unittests/clangd/Printers.h
13

To be more precise, I think it's linker keeping only one of the two versions, so it's _all_ tests, not some, that may end up calling the wrong (default) one.

We could create a file `ClangdTesting.h" which includes everything tested related (gtest, gmock, printers, syncapi, etc). The convention would be that test files would just include that.

Yeah, sounds good. I would exclude syncapi from the list, though. Not all tests should necessarily depend on ClangdServer.
@sammccall , @ioeric , @hokein, @bkramer are you on board with the idea to have an umbrella header for gtest+gmock+printers?

Sounds good to me too. I can update the patch once there is an agreement.

(Sorry, have been OOO)
I understand the template instantiations here are a mess and we need to solve the problem. However I'm not sure this is the right direction:

  • it violates IWYU, and we expect people consistently to get gmock transitively via this header. This goes against common practice, is inconsistent with the rest of LLVM, and confuses tools (including clangd itself!). It would need to be carefully documented, but I don't think this is enough.
  • it's fragile - as soon as one test includes gmock directly we have this problem again
  • printers.h takes a horizontal slice of the types we have - it seems to suffer from the same problem as "not all tests should depend on clangdserver".

I'd suggest instead:

  • where there's a fairly nice general-purpose debugging representation of a type, we should name that operator<< and declare it in the header alongside the type. That way it's clear that it's available, there's no risk of ODR violation, and it can be used for debugging as well as by gtest.
    • where there isn't one (or it's insufficiently detailed), we should use something more primitive like EXPECT_THAT(foo, ...) << dump(foo) with dump defined locally in the cpp file. We can't share these, but these aren't really sharable anyway. This isn't ideal (nested matcher messages) but should be good enough in practice.

You raise a good point. If we can get away with using operator << everywhere, it is definitely the best approach.
Then our rule of thumb is: never define PrintTo overload, define operator << for a type instead, right?

I understand the template instantiations here are a mess and we need to solve the problem. However I'm not sure this is the right direction:

  • it violates IWYU, and we expect people consistently to get gmock transitively via this header. This goes against common practice, is inconsistent with the rest of LLVM, and confuses tools (including clangd itself!). It would need to be carefully documented, but I don't think this is enough.

I didn't know LLVM code was IWYU, is this mentioned in the guide-line? I would think for tests this can be more lax maybe. I'm curious what you mean though by confusing the tools?

  • it's fragile - as soon as one test includes gmock directly we have this problem again
  • printers.h takes a horizontal slice of the types we have - it seems to suffer from the same problem as "not all tests should depend on clangdserver".

I'd suggest instead:

  • where there's a fairly nice general-purpose debugging representation of a type, we should name that operator<< and declare it in the header alongside the type. That way it's clear that it's available, there's no risk of ODR violation, and it can be used for debugging as well as by gtest.
    • where there isn't one (or it's insufficiently detailed), we should use something more primitive like EXPECT_THAT(foo, ...) << dump(foo) with dump defined locally in the cpp file. We can't share these, but these aren't really sharable anyway. This isn't ideal (nested matcher messages) but should be good enough in practice.

Yes, it is fragile but even using operator<<, we can still have the problem of anyone adding a Printer, which is normally how gtest is used. So I think either way is going to be fragile. But I don't mind either way, so I'll change the patch for operator<<

malaperle updated this revision to Diff 140180.Mar 28 2018, 7:57 PM

Use operator<< where we can

malaperle retitled this revision from [clangd] Use a header for common inclusions for tests to [clangd] Use operator<< to prevent printers issues in Gtest.Mar 28 2018, 7:58 PM
malaperle edited the summary of this revision. (Show Details)
malaperle added inline comments.
unittests/clangd/JSONExprTests.cpp
19

This one I couldn't change to operator<< because there was already one defined with raw_ostream. But this just means losing indentation so perhaps that's not too bad?

sammccall added inline comments.Mar 29 2018, 11:42 AM
clangd/Protocol.h
673 ↗(On Diff #140180)

I think raw_ostream should work fine here, it's what we've done elsewhere.
Is there a reason to use std::ostream?

(llvm's gtest is specially modified so raw_ostream printers can be used)

unittests/clangd/JSONExprTests.cpp
19

yeah, I think this is fine.

(Thanks for sorting this out BTW!)

malaperle updated this revision to Diff 140382.Mar 29 2018, 8:48 PM
malaperle marked an inline comment as done.

Use raw_ostream instead.

malaperle added inline comments.Mar 29 2018, 8:49 PM
clangd/Protocol.h
673 ↗(On Diff #140180)

No reason, I changed it.

@sammccall Are you OK with the latest version? Thanks!

sammccall accepted this revision.Apr 10 2018, 7:24 AM

Sorry I lost track of this. LGTM!

This revision was automatically updated to reflect the committed changes.