This is an archive of the discontinued LLVM Phabricator instance.

[ScopePrinting] Added support to print full scopes of types and declarations.
Needs ReviewPublic

Authored by schroedersi on Mar 14 2017, 9:40 AM.

Details

Reviewers
bkramer
rsmith
Summary

PrintingPolicy::SuppressScope was replaced by PrintingPolicy::Scope. Possible values for PrintingPolicy::Scope are:

  • FullScope: Print all nested name specifiers (including the global scope specifier).
    • This is necessary if a printed non-absolute scope would not select the desired scope. Example: Consider the following code:
namespace Z {
  namespace Z {
    namespace Y {
      class X { }; // (1)
    }
  }
  namespace Y {
    class X { }; // (2)
  }
  // (3)
}
      • Printing type ::Z::Y::X (marked with (2)) without FullScope results in Z::Y::X. If this is used at the position marked with (3), it will select the wrong type ::Z::Z::Y::X (marked with (1)). With FullScope the result is ::Z::Y::X and the correct type is selected.
    • Please note that in some cases it is not possible to print the full scope. For example in case of a local class or a dependent name.
  • DefaultScope: This corresponds to the previous behavior with SuppressScope==false: In case of an elaborated type, print the outer scope as written in the source. (If there is a tag keyword and no scope in the source then no scope is printed.) Otherwise print the full scope but without the global scope specifier. This distinction is made for inner scopes recursively.
  • SuppressScope: Do not print any scope.

Diff Detail

Event Timeline

schroedersi created this revision.Mar 14 2017, 9:40 AM
bkramer edited edge metadata.

Can you please run clang-format on this change? There are pieces that don't follow the style.

Also the mutable state in PrintingPolicy is really really ugly, is there no better way for this? :(

  • Ran clang-format (current trunk version) on the changes (except on some test files)
  • Adapted patch to current trunk version
schroedersi added a comment.EditedMay 13 2017, 6:04 AM

Also the mutable state in PrintingPolicy is really really ugly, is there no better way for this? :(

Thanks for your comment :-)

I assume with mutable state you mean PrintingPolicy::TemporarySuppressScope? I do not really like it either. But other states of the policy are also mutated during the printing process (e.g. SuppressScope, SuppressTagKeyword and SuppressSpecifiers inside the RAII helpers).

A little background: It is necessary that scope printing can be temporarily suppressed without changing the actual scope printing kind setting. For example, in the case of an ElaboratedType, it is necessary that the underlying type is printed without the outer scope (because it has already been printed based on how it was written in the source and based on the scope printing kind setting). However, inner scopes of the underlying type (e.g. scopes of template arguments) should be printed according to the scope printing kind setting. Another example are types inside a nested name specifier. Again, the outer scope can not be printed. However, inner scopes (e.g. for template arguments) must be printed according to the scope printing kind setting.
For this temporal suppression, the information about whether a scope has already been suppressed and thus the scope printing kind setting must be used, must be shared over all member functions involved in the printing. These member functions are the TypePrinter member functions and
TemplateName::print, NestedNameSpecifier::print, and NamedDecl::printQualifiedName. Currently, the only object shared across all these member functions is the PrintingPolicy. That is why I added the state to the PrintingPolicy.

As an alternative to the current solution, the above-mentioned member functions could each be extended by a "PrintingContext" argument, which then contains the variable states.

And a bit of clarification: Without the patch, even with SuppressScope = true it is not possible to suppress all scopes, and inner types are printed with a scope most of the time (which seems to lead to the desired effect for most users). For example, printing the type of A in namespace N{class B{}; template<typename T> class C{}; C<::N::B> A;} with SuppressScope = true results in C< ::N::B> which still includes scopes. That is, there is already some kind of temporary suppress scope functionality, but it is called SuppressScope. The patch only expands this functionality to full scopes (see summary above), makes the behavior more consistent, and ensures that the settings do what - at least I - would expect from their name and documentation.

I hope this explains my decision. Either way, I look forward to further comments :-).

schroedersi edited the summary of this revision. (Show Details)Jun 1 2017, 2:26 AM

Would it help the acceptance of the patch if I add a fourth option LegacySuppressScope (or similar) that emulates the current/pre-patch SuppressScope==true? This way the patched type printing would be a real superset of the current/non-patched type printing (except for some small changes to SuppressScope==false/DefaultScope as seen in the diff of tests p6.cpp and comment-cplus-decls.cpp).

rsmith edited edge metadata.Jun 27 2017, 2:04 PM

As an alternative to the current solution, the above-mentioned member functions could each be extended by a "PrintingContext" argument, which then contains the variable states.

I'd be interested to see how much complexity that adds, if you're prepared to give it a try.

And a bit of clarification: [...] there is already some kind of temporary suppress scope functionality, but it is called SuppressScope.

Yes, I think that's right -- SuppressScope isn't really an externally useful "print without scopes" mechanism, it's an internal mechanism / implementation detail used for printing an "inner" AST node when we've already printed a scope for it based on outer sugar (eg, the entity is a type component of a nested name specifier, or the type inside an elaborated type specifier, or the class name in a destructor name). It's not really SuppressScope, it's ScopeWasAlreadyPrintedForWhateverTopLevelThingIAskedYouToPrint =)

I'd be interested to see how much complexity that adds, if you're prepared to give it a try.

Thanks for your feedback :). I added a printing context and moved TemporarySuppressScope from PrintingPolicy to the printing context. The printing context is currently passed around by value. This is not ideal because changes to the context made inside a function are not available in the caller but it is sufficient for the temporary suppress scope functionality. And it gives an overview of the amount of code changes.

Passing the context around by reference would be better but it would also necessitate the creation of a overloaded version of almost each affected function. (Something like this:

void print(const PrintingPolicy& Policy, PrintingContext& Context) { /*...*/ }

void print(const PrintingPolicy& Policy) {
  PrintingContext Context;
  print(Policy, Context);
}

).

Yes, I think that's right -- SuppressScope isn't really an externally useful "print without scopes" mechanism, it's an internal mechanism / implementation detail used for printing an "inner" AST node when we've already printed a scope for it based on outer sugar (eg, the entity is a type component of a nested name specifier, or the type inside an elaborated type specifier, or the class name in a destructor name). It's not really SuppressScope, it's ScopeWasAlreadyPrintedForWhateverTopLevelThingIAskedYouToPrint =)

Thanks for clarification :)

I forgot to add the previous changes in Diff 104649 (it only contains the incremental diff of the printing context changes). This diff contains all changes between trunk and the current patch state. I apologize!

Hello Richard, what do you think about the printing context changes? Are they what you expected?

Any feedback is appreciated!