This is an archive of the discontinued LLVM Phabricator instance.

[clangd] **Prototype**: clang-tidy-based tweaks
Needs ReviewPublic

Authored by hokein on Feb 8 2019, 3:53 AM.

Details

Summary

clang-tidy has already provided some simple refactoring-like checks (e.g. modernize-use-auto,
modernize-use-using), so we can leverage the power of clang-tidy to implement tweaks.

clang-tidy-based tweaks compared to clang-tidy check:

  • For refactoring checks, tweaks maybe a better UI compared to showing as diagnostics (we only show on the UI when users do it intendedly)
  • faster, we run the check on a single AST node rather than the whole AST

Event Timeline

hokein created this revision.Feb 8 2019, 3:53 AM
Herald added a project: Restricted Project. · View Herald TranscriptFeb 8 2019, 3:53 AM

This is my experiment of playing around tweaks, I think we'll need this.

This is an intriguing idea, and is at least useful to prototype new tweaks.

I'm not sure whether clang-tidy is the ultimately right API to write tweaks:

  • it doesn't have the needed constraints to ensure prepare() is fast (e.g. it emits diagnostics and fixes eagerly)
  • the exact set of nodes that it will trigger on may or may not match what we want
  • it doesn't produce context-sensitive titles

Maybe we should have this behind a flag, and use it to investigate which tweaks should be implemented natively?

clangd/refactor/tweaks/ClangTidy.cpp
45 ↗(On Diff #185941)

I can't quite parse this.
"only checks whose matchers match exactly the selected node will work"?

One concern here is that this is basically an implementation detail of a check: checks can choose to e.g. match on a specific node and do a cheap analysis, or match on a higher-level node and do an expensive one. Maybe as long as the specific checks we want work well, it's not an issue. But every check added here needs to be carefully reviewed for performance.

66 ↗(On Diff #185941)

obviously this is central to the approach, but... prepare needs to be *fast*. It's hard to see how we guarantee that with all this machinery, especially because the match callback contents may change over time.

83 ↗(On Diff #185941)

looping over all the ancestors might give better results (but leaves less control over performance).

141 ↗(On Diff #185941)

this should really specify which type is getting converted - seems like a possible weakness of this approach.

unittests/clangd/TweakTests.cpp
231 ↗(On Diff #185941)

this needs to also trigger if you select "unsigned"

hokein updated this revision to Diff 186261.Feb 11 2019, 7:49 AM
hokein marked 5 inline comments as done.

Update the comments

This is an intriguing idea, and is at least useful to prototype new tweaks.

I'm not sure whether clang-tidy is the ultimately right API to write tweaks:

it doesn't have the needed constraints to ensure prepare() is fast (e.g. it emits diagnostics and fixes eagerly)
the exact set of nodes that it will trigger on may or may not match what we want
it doesn't produce context-sensitive titles

Yeah, relying on the checks' implementations is the Achilles' heel of this approach. To reuse the existing code (create fast prototypes), we sacrifice our flexibility unfortunately :(

Another option would be creating a more general refactoring library, and refactoring-like clang-tidy and tweaks can be built on it.

Maybe we should have this behind a flag, and use it to investigate which tweaks should be implemented natively?

It seems to me there are no straight-forward ways to do it since the tweaks' implementations are isolated (maybe via a macro ENABLE_CLANG_TIDY_TWEAKS to control whether we should register these tweaks?).

clangd/refactor/tweaks/ClangTidy.cpp
66 ↗(On Diff #185941)

Most of the checks' callback are just to detect whether the match result is the interesting case, and generate message & FixIt, I'd say it is cheap, and we only run clang-tidy check on the selected AST node, which should be fast.

83 ↗(On Diff #185941)

Besides the performance concern, looping over ancestors would extend the clang-tidy check to run on other AST nodes, for the case below, we would also apply the tweak to the ast nodes that are not selected.

typedef int Foo
ty^pedef int Foo2;
141 ↗(On Diff #185941)

Agree, we may use the check diagnostic messages (e.g. use auto when declaring iterators) as the title, but this depends on the check implementation, probably not an ideal way.

unittests/clangd/TweakTests.cpp
231 ↗(On Diff #185941)

this would be nice to have, but it is a weak point of this approach -- relying on the check implementation.

Another alternative we could consider would be collecting these fixes when producing the clang-tidy diagnostics.
We won't show the diagnostics to the users if they have the check disabled, but we would stash the ranges and the textual fixes somewhere.
I.e. I'm suggesting to reuse something closer to the mechanism of diagnostics fix-its, not the code tweak interfaces.

The advantages are:

  1. keeping clang-tidy integration in one place,
  2. no extra runs of ast matchers, all tidy checks will run during diagnostics.

WDYT?

hokein updated this revision to Diff 207058.Jun 28 2019, 6:58 AM

Rebase the code, and hide the tweaks.

Another alternative we could consider would be collecting these fixes when producing the clang-tidy diagnostics.
We won't show the diagnostics to the users if they have the check disabled, but we would stash the ranges and the textual fixes somewhere.
I.e. I'm suggesting to reuse something closer to the mechanism of diagnostics fix-its, not the code tweak interfaces.

The advantages are:

  1. keeping clang-tidy integration in one place,
  2. no extra runs of ast matchers, all tidy checks will run during diagnostics.

WDYT?

Hidden diagnostic is definitely another approach, but it'd require more discussion/work/design. As I mentioned before, it is used for experimental, and I'm not sure this is something we want.

I have rebased the branch, and hidden this under the hidden-features flag. Let me know if we want to move forward or not.