This is an archive of the discontinued LLVM Phabricator instance.

Add a clang-tidy Visual Studio extension
ClosedPublic

Authored by zturner on Aug 24 2016, 12:02 PM.

Details

Summary

This gets a pretty solid UI in place for editing clang-tidy files. It displays all the checks, descriptions of most of them with more to come later (it's a bit tedious), and allows disabling / enabling individual checks or entire groups of checks using a familiar UI such as the one in the C++ project system that supports inheritance or explicit overrides.

It supports serialization to and from Yaml using the NuGet package YamlDotNet, and depends on an additional NuGetPackage called BrutalStrongNameSigner which is required in order to strong name sign YamlDotNet (otherwise our extension can't load it).

Unfortunately implementing an inheritance-based property system such as Visual Studio's is *highly* non-trivial, and so this patch turned out much larger than I originally anticipated.

If need be, I can try to submit a skeleton patch that does nothing but add a blank options page first, so that this patch is not muddled up with the actual content.

I'm not sure who the best C# reviewer is around here, so adding a few people.

Diff Detail

Repository
rL LLVM

Event Timeline

zturner updated this revision to Diff 69148.Aug 24 2016, 12:02 PM
zturner retitled this revision from to Add a clang-tidy Visual Studio extension.
zturner updated this object.
zturner added a subscriber: cfe-commits.
alexfh requested changes to this revision.Aug 24 2016, 1:10 PM
alexfh edited edge metadata.

Awesome!

A few comments.

CMakeLists.txt
6 ↗(On Diff #69148)

Should the plugin be placed inside clang-tidy directory?

clang-tidy-vs/ClangTidy/CategoryVerb.cs
29 ↗(On Diff #69148)

Remove the empty line.

34 ↗(On Diff #69148)

Should this be value is string?

clang-tidy-vs/ClangTidy/CheckTree.cs
130 ↗(On Diff #69148)

Ideally, this should be configurable without re-compilation. Not necessary for now, but please add a FIXME.

clang-tidy-vs/ClangTidy/ClangTidyConfigurationPage.cs
10 ↗(On Diff #69148)

Are all of these actually used?

26 ↗(On Diff #69148)

^K^F or whatever poor C# folks do to format code without clang-format ;)

clang-tidy-vs/ClangTidy/ClangTidyPackage.cs
58 ↗(On Diff #69148)

Remove commented-out code?

clang-tidy-vs/ClangTidy/ClangTidyProperties.cs
81 ↗(On Diff #69148)

I hope, this file is generated?

A way to update this file should be documented.

clang-tidy-vs/ClangTidy/Properties/AssemblyInfo.cs
32 ↗(On Diff #69148)

Ideally, the file should be generated by CMake to use proper version number. Please add a FIXME.

clang-tidy-vs/ClangTidy/PropertyFileParser.cs
80 ↗(On Diff #69148)

s/ClangTidy/ConfigFile/

208 ↗(On Diff #69148)

Is the copy needed to avoid aliasing? (i.e. will this break if you iterate over Props.FindChecksMatching(Check) instead?)

clang-tidy-vs/ClangTidy/Utility.cs
31 ↗(On Diff #69148)

Question marks are not supported in check patterns, so .Replace(@"\?", ".") can be dropped.

This revision now requires changes to proceed.Aug 24 2016, 1:10 PM
Eugene.Zelenko added inline comments.
clang-tidy-vs/ClangTidy/CheckTree.cs
79 ↗(On Diff #69148)

Unnecessary empty line.

93 ↗(On Diff #69148)

Unnecessary empty line.

clang-tidy-vs/ClangTidy/ClangTidy.vsct
22 ↗(On Diff #69148)

Unnecessary empty line, same below in many places here.

clang-tidy-vs/ClangTidy/ClangTidyPropertyGrid.cs
178 ↗(On Diff #69148)

Unnecessary empty line.

clang-tidy-vs/ClangTidy/PropertyFileParser.cs
14 ↗(On Diff #69148)

Unnecessary empty line.

23 ↗(On Diff #69148)

Please add empty line.

I can fix the empty lines, but keep in mind that Visual Studio's C# editor is MUCH more aggressive about auto-formatting your code. So it seems like a fruitless endeavor to me, as we will constantly be fighting against their auto formatter, which does this automatically every time you add a new function.

zturner added inline comments.Aug 24 2016, 1:41 PM
CMakeLists.txt
6 ↗(On Diff #69148)

I followed the same way that the clang format VS extension uses. I don't mind to move it, I just did it this way for consistency.

clang-tidy-vs/ClangTidy/ClangTidyProperties.cs
81 ↗(On Diff #69148)

No, I actually typed this entire file :-/ I don't know of a good way to auto generate it. We would need a single file, perhaps Yaml or something, consisting of:

  1. Category Name
  2. Display Name
  3. Description
  4. clang-tidy built in default value of check.

At that point, we wouldn't even need to generate the file, we could read it at runtime and add the properties dynamically (through the ICustomTypeDescriptor interface).

For now though, the way to update the file is to copy / paste one property and change the values accordingly to add a new check.

clang-tidy-vs/ClangTidy/PropertyFileParser.cs
208 ↗(On Diff #69148)

The copy is not needed to avoid aliasing. It's fine to iterate over Props.FindChecksMatching(Check). I wrote it this way because it helps for debugging, as I could just add a watch for Matches and view the array in the debugger.

zturner updated this revision to Diff 69168.Aug 24 2016, 2:03 PM
zturner edited edge metadata.

Updated with suggestions. I haven't fixed the empty lines yet because there are a lot of them, and I will wait to see how strongly people feel about it first. Personally I don't think it's worth worry about since we will be fighting with Visual Studio over this endlessly and we don't have a clang-format for C# to alleviate this nuisance.

I can fix the empty lines, but keep in mind that Visual Studio's C# editor is MUCH more aggressive about auto-formatting your code. So it seems like a fruitless endeavor to me, as we will constantly be fighting against their auto formatter, which does this automatically every time you add a new function.

I would definitely not fight with visual editors that reset formatting every time you touch the file. However, if VS only adds empty lines when initially adding a function, that doesn't promise much resistance ;)

CMakeLists.txt
6 ↗(On Diff #69148)

I don't feel strongly either way. Let's figure this out later.

clang-tidy-vs/ClangTidy/ClangTidy.vsct
22 ↗(On Diff #69148)

If there's a way to visually edit this file, I wouldn't care about formatting.

clang-tidy-vs/ClangTidy/ClangTidyProperties.cs
81 ↗(On Diff #69148)

Manually updating this file won't fly =[

I guess, we could try to use .rst files as the source of truth. They follow a strict naming scheme (<check-name>.rst) and we could require the first paragraph of the text to be a single-sentence description of the check suitable for this purpose as well.

I'd also make the display name the same as the full clang-tidy check name (DCL50-CPP is neither more useful nor convenient than the full check name).

The only thing we'd need to maintain manually in this case is category descriptions.

WDYT?

zturner added inline comments.Aug 24 2016, 2:15 PM
clang-tidy-vs/ClangTidy/ClangTidyProperties.cs
82 ↗(On Diff #69168)

Are the .rst files in the repo somewhere already? I don't see them.

As for the display name, I agree this one is bad (I forgot to change it). But you can look at some of the ones below for better examples. For example, I find I.22 - Complex Global Initializers to be a better short descriptor than cppcoreguidelines-interfaces-global-init.

What about a hand-maintained Yaml file that adheres to a format similar to the following?

---
Checks:
  - Name:        cert-dcl54-cpp
    Label:       Overloaded allocation function pairs
    Description: Checks for violations of CERT DCL54-CPP - Overload allocation and deallocation functions as a pair in the same scope
    Category:    CERT Secure Coding Standards
  - Name:        cppcoreguidelines-interfaces-global-init
    Label:       I.22 - Complex Global Initializers
    Description: Checks for violations of Core Guideline I.22 - Avoid complex initializers of global objects
    Category:    C++ Core Guidelines
...

Some file somewhere is going to have to be maintained by hand, and since a file such as this doesn't appear to exist in clang-tidy already, we might as well use a format that we already have good tools to parse at runtime, such as Yaml.

BTW, since I forgot to do so in the original review, here is a screenshot of what the UI for the property editor looks like in this extension:

clang-tidy-vs/ClangTidy/ClangTidy.vsct
23 ↗(On Diff #69168)

This file is auto-generated by Visual Studio. You can manually add your own things to it (as we've done at the end of the file), but it doesn't seem worthwhile to go back and fix auto-generated code.

BTW, this is just an XML file, so it gets collapse functionality in the Visual Studio Editor. That is, you can collapse tags so that they are not displayed. Not sure if that makes a difference.

alexfh added inline comments.Aug 24 2016, 2:38 PM
clang-tidy-vs/ClangTidy/ClangTidyProperties.cs
82 ↗(On Diff #69168)

The .rst files are there: https://reviews.llvm.org/diffusion/L/browse/clang-tools-extra/trunk/docs/clang-tidy/checks/

Docs + source code are already enough hassle to maintain, so adding yet another file with a similar information seems too much. We can start with a hand-written YAML file and later add a script to generate it from the docs. I'm not sure though, what Label could be mapped on. Do you need it for the property editor or can you get away with just the name?

zturner added inline comments.Aug 24 2016, 2:44 PM
clang-tidy-vs/ClangTidy/ClangTidyProperties.cs
82 ↗(On Diff #69168)

Well you can look at the screenshot to see how I'm using it. Basically the left hand side of the grid are all the "label" values. It's what people are going to see when deciding whether they want to enable a check or not. So it should be mostly self-explanatory without having to click on it to get more detail. The name kinda is, but the point of the extension is to be friendlier than editing files by hand, so I think we should try to provide a friendlier name too. When you're using the command line tool you have no choice but to specify the check name, but if you're using a UI I think we can give the user a little bit more info about each check.

Generating YAML from docs seems like a decent idea. Perhaps to map label we could extend the rst format a little to add the label to it. Sort of like "summary" vs "description".

I'll do a hand-written Yaml file for now, and think about the best way to generate the Yaml from RST in a later patch.

alexfh added inline comments.Aug 24 2016, 2:59 PM
clang-tidy-vs/ClangTidy/ClangTidyProperties.cs
82 ↗(On Diff #69168)

SG.

Re: label vs check name. I think, even if we show a friendlier label (which we'll need to require for all new checks and back-fill for the existing ones), the extension should also show the check name somewhere for transparency. It's supposed to be human-readable to some degree (not a proper explanation though, rather a hint to what the check is about). It's also shown in the diagnostic messages, so there's no point in hiding it elsewhere.

amccarth edited edge metadata.Aug 24 2016, 3:19 PM

I don't know enough C# to review for language usage. I was mostly reading for understandability. Overall, I think this looks really nice.

clang-tidy-vs/ClangTidy/CheckTree.cs
67 ↗(On Diff #69168)

This seems overly complicated so I assume I'm missing some nuance. Why not:

if (Parent_ == null)
    return Name_;
string ParentPath = Parent_.Path;

Is there a case where Parent_.Path could return null besides the one that you created?

clang-tidy-vs/ClangTidy/InheritablePropertyComponent.cs
14 ↗(On Diff #69168)

s/good makes/makes/

clang-tidy-vs/README.txt
8 ↗(On Diff #69168)

2010? 2015? Will Community editions work?

zturner updated this revision to Diff 69276.Aug 25 2016, 11:36 AM
zturner edited edge metadata.

Remove the hand-written mega class and generate all the properties dynamically.

It was a little tricky getting dynamically generated properties working, so the diff is bigger than I expected. But it should all work now, and the code is much cleaner as a result I think. I renamed all the Inheritable stuff to Dynamic to better clarify its nature.

I think will be good idea to mention this plugin in docs/ReleaseNotes.rst.

I think will be good idea to mention this plugin in docs/ReleaseNotes.rst.

I was planning to wait until it's "finished". This patch will give you a user interface for editing .clang-tidy files, but it still won't provide any means of *running* clang-tidy on the currently active source file, project, or solution.

I had planned to add those features incrementally to make the reviews more manageable. What do you guys think?

zturner added inline comments.Aug 25 2016, 12:46 PM
clang-tidy-vs/ClangTidy/CheckTree.cs
67 ↗(On Diff #69168)

If Parent_ == null, that is the root note, or default node. It doesn't represent any particualr check, it's just the node at the top of the tree containing all the branches. So we have to return null in that case. We could probably return Name_ as it would probably be equal to null, but it seems more clear to just return a constant.

Even if Parent_ is not null though, Parent_.Path could equal null. This would happen for any node immediately under the root, because the parent would be the root, and by the previous explanation, Parent_.Path would then return null. So, for top-level check nodes we just want to return the name, because there is nothing before it to prepend a - to.

For deeper nodes though, we want to get the path from the parent, then join it with a -.

zturner updated this revision to Diff 69296.Aug 25 2016, 2:51 PM

Properly save and reload the settings when the user clicks various buttons in the dialog (Apply, Cancel, etc).

amccarth added inline comments.Aug 25 2016, 2:59 PM
clang-tidy-vs/ClangTidy/CheckTree.cs
68 ↗(On Diff #69296)

OK, cool. I assumed that the root had a name. Thus a node with no parent (a root) could return its name and the rest of the children would each append '-' + Name_. But if roots are nameless, then your solution is fine.

Anyone else have any thoughts on this or is this good to go?

I am planning to submit this today if there are no further suggestions.

alexfh accepted this revision.Sep 7 2016, 4:25 AM
alexfh edited edge metadata.

A few late comments. Looks good overall.

clang-tidy-vs/ClangTidy/CategoryVerb.cs
52 ↗(On Diff #69296)

Should this be value is CategoryVerb?

clang-tidy-vs/ClangTidy/ClangTidyConfigurationPage.cs
11 ↗(On Diff #69296)

Is this addressed?

clang-tidy-vs/ClangTidy/ClangTidyPropertyGrid.cs
176 ↗(On Diff #69296)

I guess, this handler is not needed, since it's empty.

clang-tidy-vs/ClangTidy/Guids.cs
13 ↗(On Diff #69296)

Could you fix the No newline at end of file?

clang-tidy-vs/ClangTidy/PkgCmdID.cs
8 ↗(On Diff #69296)

Could you fix the No newline at end of file?

clang-tidy-vs/ClangTidy/Resources/ClangTidyChecks.yaml
1 ↗(On Diff #69296)

Does YAML support comments? If yes, I'd add a not that this file should be updated when new checks are added and a TODO to add a generator from .rst.

This revision is now accepted and ready to land.Sep 7 2016, 4:25 AM
alexfh added inline comments.Sep 7 2016, 4:27 AM
clang-tidy-vs/ClangTidy/ClangTidyPackage.cs
53 ↗(On Diff #69296)

Add a FIXME to actually implement this.

This revision was automatically updated to reflect the committed changes.