Compare commits

..

102 Commits

Author SHA1 Message Date
Tanya Lattner
bc12345cbe Merge r143770 from mainline to fix MSVC build.
llvm-svn: 144894
2011-11-17 07:40:32 +00:00
Bill Wendling
1b871840be Merging r142531:
------------------------------------------------------------------------
r142531 | rafael | 2011-10-19 07:50:34 -0700 (Wed, 19 Oct 2011) | 3 lines

Fix the signatures of vfork, __sigsetjmp and sigsetjmp.

Patch by Dimitry Andric.
------------------------------------------------------------------------

llvm-svn: 144262
2011-11-10 04:22:48 +00:00
Bill Wendling
a7d131fb47 Merging r143712:
------------------------------------------------------------------------
r143712 | efriedma | 2011-11-04 10:29:35 -0700 (Fri, 04 Nov 2011) | 3 lines

Add missing argument for atomic instructions in c++ backend.  PR11268, part 2.


------------------------------------------------------------------------

llvm-svn: 143999
2011-11-07 19:38:25 +00:00
Bill Wendling
0fbdb0ad4a Merging r143406:
------------------------------------------------------------------------
r143406 | efriedma | 2011-10-31 16:59:22 -0700 (Mon, 31 Oct 2011) | 3 lines

Add support for new atomics to cpp backend.  Misc other fixes while I'm here.  PR11268.


------------------------------------------------------------------------

llvm-svn: 143998
2011-11-07 19:38:01 +00:00
Chandler Carruth
5e5ca787a3 Merging r143917:
------------------------------------------------------------------------
r143917 | chandlerc | 2011-11-07 01:17:31 -0800 (Mon, 07 Nov 2011) | 12 lines

Rip out one of the features I added for the driver-include-management.
We don't actually need a separate flag for non-sysrooted paths as the
driver has to manage the sysroot anyways. The driver is not infrequently
adding paths to the header search based on their existence on the
filesystem. For that, it has to add the sysroot anyways, we should pass
it on down to CC1 already joined. More importantly, the driver cannot in
all cases distinguish between sysrooted paths and paths that are
relative to the Clang binary's installation directory. Essentially, we
always need to ignore the system root for these internal header search
options. It turns out in most of the places we were already providing
the system root in the driver, and then another one in CC1 so this fixes
several bugs.
------------------------------------------------------------------------

llvm-svn: 143956
2011-11-07 10:45:21 +00:00
Chandler Carruth
de9c697e1a Merging r143916:
------------------------------------------------------------------------
r143916 | chandlerc | 2011-11-07 01:01:17 -0800 (Mon, 07 Nov 2011) | 4 lines

Fix Linux libc++ usage. Somehow this slipped through during the port.

Test cases for this and all the rest of the port are still in the works,
but will wait for a fixed computer and post 3.0 merging...
------------------------------------------------------------------------

llvm-svn: 143955
2011-11-07 10:44:38 +00:00
Chandler Carruth
02591c29cf Merging r143897:
------------------------------------------------------------------------
r143897 | chandlerc | 2011-11-06 15:10:49 -0800 (Sun, 06 Nov 2011) | 2 lines

Remove an old OpenSUSE hack that is no longer needed -- it is exactly
the same as a directory added further down in the new logic.
------------------------------------------------------------------------

llvm-svn: 143954
2011-11-07 10:43:59 +00:00
Chandler Carruth
ce9034fb11 Merging r143896:
------------------------------------------------------------------------
r143896 | chandlerc | 2011-11-06 15:09:05 -0800 (Sun, 06 Nov 2011) | 3 lines

Remove the HasMultilib check. It was essentially useless. The driver now
looks for evidence of a multilib installation, and adds the appropriate
bits to the search paths.
------------------------------------------------------------------------

llvm-svn: 143953
2011-11-07 10:43:26 +00:00
Chandler Carruth
5aaaf48c94 Merging r143875:
------------------------------------------------------------------------
r143875 | chandlerc | 2011-11-06 02:51:30 -0800 (Sun, 06 Nov 2011) | 2 lines

The version objects need to actually store the version strings; they
aren't guaranteed to live long enough otherwise.
------------------------------------------------------------------------

llvm-svn: 143952
2011-11-07 10:41:56 +00:00
Chandler Carruth
f1c66f598f Merging r143874:
------------------------------------------------------------------------
r143874 | chandlerc | 2011-11-06 02:31:01 -0800 (Sun, 06 Nov 2011) | 16 lines

Throw the switch and move all Linux header search over to the GCC
detection system that is providing the library paths and crt object
files.

This, modulo any bugs that need to be shaken out, resolves numerous bugs
with how we handle header paths. Here are a few that I know of:
- We no longer need to enumerate all GCC versions searched.
- OpenSUSE searched GCC versions in the wrong order.
- There were typos when selecting various patterns, etc.
- We aren't stating quite some many directories now.
- SysRoot didn't always work in a reasonable way.

I'm working on tests for this, but the tests are making me and Lit sad.
The real testing for this type of driver change is to try it out on
various distributions. I'll hit the common ones right away, and start
more thorough testing tomorrow after some sleep.
------------------------------------------------------------------------

llvm-svn: 143951
2011-11-07 10:40:53 +00:00
Chandler Carruth
afa0e6fb42 Merging r143873:
------------------------------------------------------------------------
r143873 | chandlerc | 2011-11-06 02:30:58 -0800 (Sun, 06 Nov 2011) | 2 lines

Add a missing triple spotted by inspecting and testing of the include
path triples. Also order the 32-bit triples a bit more reasonably.
------------------------------------------------------------------------

llvm-svn: 143949
2011-11-07 10:40:08 +00:00
Chandler Carruth
c954a30696 Merging r143871:
------------------------------------------------------------------------
r143871 | chandlerc | 2011-11-06 01:39:46 -0800 (Sun, 06 Nov 2011) | 3 lines

Lift the GCCVersion type into the header file and start persisting it in
the detected GCC installation. This allows us to expose another aspect
of what we detected: the GCC version. This will be used shortly.
------------------------------------------------------------------------

llvm-svn: 143948
2011-11-07 10:39:29 +00:00
Chandler Carruth
421c8baa5f Merging r143869:
------------------------------------------------------------------------
r143869 | chandlerc | 2011-11-06 01:21:54 -0800 (Sun, 06 Nov 2011) | 2 lines

Switch some of these interfaces from std::string to StringRef and Twine.
This will facilitate further use and recombinations of them.
------------------------------------------------------------------------

llvm-svn: 143947
2011-11-07 10:38:50 +00:00
Chandler Carruth
9cab0c3e89 Merging r143866:
------------------------------------------------------------------------
r143866 | chandlerc | 2011-11-06 01:21:07 -0700 (Sun, 06 Nov 2011) | 4 lines

Take a better approach to detecting and selecting multiarch include
directories. This way we stop at the first multiarch directory found on
the system. This achieves the real intended result of pruning
non-existent directories.
------------------------------------------------------------------------

llvm-svn: 143946
2011-11-07 10:34:06 +00:00
Chandler Carruth
69c35faa45 Merging r143863:
------------------------------------------------------------------------
r143863 | chandlerc | 2011-11-05 23:59:15 -0700 (Sat, 05 Nov 2011) | 4 lines

This test was assuming that /usr/include was in the system header search
path. That assumption should never have been true, but it was until
I fixed it. Now that its fixed, add a triple here to get correct
behavior even on Windows.
------------------------------------------------------------------------

llvm-svn: 143945
2011-11-07 10:33:32 +00:00
Chandler Carruth
ae95127c7e Merging r143842:
------------------------------------------------------------------------
r143842 | chandlerc | 2011-11-05 16:29:28 -0700 (Sat, 05 Nov 2011) | 3 lines

Switch Lit to directly query the driver for the builtin inclue path.
Thanks to Peter for pointing out how easy this is to do. I'm now much
happier with this solution.
------------------------------------------------------------------------

llvm-svn: 143944
2011-11-07 10:32:55 +00:00
Chandler Carruth
c1c82eee94 Merging r143841:
------------------------------------------------------------------------
r143841 | chandlerc | 2011-11-05 16:24:30 -0700 (Sat, 05 Nov 2011) | 22 lines

Enhance the GCC version parsing and comparison logic to handle some more
edge cases and have better behavior. Specifically, we should actually
prefer the general '4.6' version string over the '4.6.1' string, as
'4.6.2' should be able to replace it without breaking rpaths or any
other place that these paths have been embedded. Debian-based
distributions are already using a path structure with symlinks to
achieve in-place upgrades for patch versions. Now our parsing reflects
this and we select the shorter paths instead of the longer paths.

A separate issue was that we would not parse a leading patch version
number even in the presence of a suffix. The above change makes this
more problematic as it would cause a suffix being added to make us treat
the entire thing as patch-version-agnostic, which it isn't. This changes
the logic to distinguish between '4.4.x' and 4.4.1-x', and retain that
the latter has *some* patch number information. Currently, we always
bias toward the shorter and more canonical version strings. If it
becomes important we can add more Debian like rules to produce sequences
such as '4.4.1b' > '4.4.1' > '4.4.1-rc3' > '4.4.1-rc2' > '4.4.1-pre5',
but I'm very doubtful this will ever matter or be desirable.

I've made the tests for this logic a bit more interesting, and added
some specific tests for logic that is now different.
------------------------------------------------------------------------

llvm-svn: 143943
2011-11-07 10:32:20 +00:00
Chandler Carruth
9eeddcf338 Merging r143840:
------------------------------------------------------------------------
r143840 | chandlerc | 2011-11-05 15:23:14 -0700 (Sat, 05 Nov 2011) | 4 lines

Remove a pointless member. I have no idea why I made this not a local
variable to begin with... As I'm planning to add include root
information to this object, this would have caused confusion. It didn't
even *actually* hold the include root by the time we were done with it.
------------------------------------------------------------------------

llvm-svn: 143942
2011-11-07 10:31:25 +00:00
Chandler Carruth
8d9c0888e2 Merging r143839:
------------------------------------------------------------------------
r143839 | chandlerc | 2011-11-05 15:23:11 -0700 (Sat, 05 Nov 2011) | 2 lines

Remove a no-longer needed helper function. Thanks for implementing this
Michael!
------------------------------------------------------------------------

llvm-svn: 143941
2011-11-07 10:30:25 +00:00
Chandler Carruth
bec38f0dc6 Merging r143838:
------------------------------------------------------------------------
r143838 | chandlerc | 2011-11-05 15:07:51 -0700 (Sat, 05 Nov 2011) | 10 lines

Move the GCC installation detection helper a member of the Linux
toolchain instead of merely using it in the constructor. This will allow
us to query it when building include paths as well as the file search
paths built in the constructor. I've lifted as little of it as I could
into the header file.

Eventually this will likely sink down into some of the Generic
toolchains and be used on more platforms, but I'm starting on Linux so
I can work out all the APIs needed there, where it is easiest to test
and we have the most pressing need.
------------------------------------------------------------------------

llvm-svn: 143940
2011-11-07 10:29:29 +00:00
Chandler Carruth
246dabacf2 Merging r143836:
------------------------------------------------------------------------
r143836 | chandlerc | 2011-11-05 14:26:18 -0700 (Sat, 05 Nov 2011) | 5 lines

Remove support for Gentoo subversion-ebuild installed llvm-gcc libstdc++
headers. As llvm-gcc is dead, and I have no idea if this ever really
worked, I think it's time for it to go. More importantly, it makes it
harder to generalize the include search logic. If someone really wants
these to work, they can set the CPLUS_INCLUDE_PATH environment variable.
------------------------------------------------------------------------

llvm-svn: 143939
2011-11-07 10:28:49 +00:00
Chandler Carruth
5fe100a8b5 Merging r143823:
------------------------------------------------------------------------
r143823 | chandlerc | 2011-11-05 13:55:50 -0700 (Sat, 05 Nov 2011) | 4 lines

Teach lit to ask the Clang it is running what version string to use
rather than presuming that it is 3.0. This is extra important as the
version should be 3.1, but CMake hasn't caught up with the times.
That'll be fixed in a separate commit.
------------------------------------------------------------------------

llvm-svn: 143938
2011-11-07 10:28:08 +00:00
Chandler Carruth
7507b8d3dc Merging r143822:
------------------------------------------------------------------------
r143822 | chandlerc | 2011-11-05 13:17:13 -0700 (Sat, 05 Nov 2011) | 12 lines

Move the Linux header searching from the Frontend to the Driver. This is
the first (and diff-noisiest) step to making Linux header searching
tremendously more principled and less brittle. Note that this step
should have essentially no functional impact. We still search the exact
same set of paths in the exact same order. The only change here is where
the code implementing such a search lives.

This has one obvious negative impact -- we now pass a ludicrous number
of flags to the CC1 layer. That should go away as I re-base this logic
on the logic to detect a GCC installation. I want to do this in two
phases so the bots can tell me if this step alone breaks something, and
so that the diffs of the refactoring make more sense.
------------------------------------------------------------------------

llvm-svn: 143937
2011-11-07 10:27:39 +00:00
Chandler Carruth
36cbea60ff Merging r143807:
------------------------------------------------------------------------
r143807 | chandlerc | 2011-11-05 03:41:42 -0700 (Sat, 05 Nov 2011) | 4 lines

Compute the path properly on different platforms. Specifically, compute
it the exact same way that the Clang code computes this path.

Hopefully with this, the MSVC bots will actually come back to life.
------------------------------------------------------------------------

llvm-svn: 143936
2011-11-07 10:21:56 +00:00
Chandler Carruth
4e61d20111 Merging r143806:
------------------------------------------------------------------------
r143806 | chandlerc | 2011-11-05 03:15:33 -0700 (Sat, 05 Nov 2011) | 15 lines

Change this test to reflect the state we are moving in. The Clang
builtin headers are no longer going to receive the old 'implicit extern
"C" block' semantics. This hint is actually ignored by both Clang and
GCC at this point, and Clang's own builtin headers can simply be changed
if there is any issue with this. Clang should be free to include these
however it wants, and so shorter and simpler is better.

Note: *nothing* is changing about the *system* stddef.h include. That
should always have the exact same include semantics, whether with Clang
or GCC or any other compiler. Only the compiler-builtin header search
path is changing.

If anyone knows of some risk that this introduces that I've not thought
of, please chime in. So far, only Windows has switched to the Brave New
World, but others should be switching soon.
------------------------------------------------------------------------

llvm-svn: 143935
2011-11-07 10:21:27 +00:00
Chandler Carruth
c71ec93cfd Merging r143805:
------------------------------------------------------------------------
r143805 | chandlerc | 2011-11-05 03:15:30 -0700 (Sat, 05 Nov 2011) | 4 lines

Switch these two tests to use the Clang driver instead of CC1. They want
to do "realistic" includes, and so need the header search logic now in
the driver. This in turn requires switching the CC1 options to the
actual driver options, and passing -Xclang where there is no analogy.
------------------------------------------------------------------------

llvm-svn: 143934
2011-11-07 10:20:49 +00:00
Chandler Carruth
1e38f1d014 Merging r143804:
------------------------------------------------------------------------
r143804 | chandlerc | 2011-11-05 03:15:27 -0700 (Sat, 05 Nov 2011) | 38 lines

Teach Lit to pass the CC1 invocation the builtin include directory. This
is a pretty gross hack, but I don't have any significantly cleaner ideas
for this. There are several things obviously gross about it:

1) Lit shouldn't know that Clang needs this. This really that bad, as
   Lit already knows about CC1 and other internal details.
2) This hard codes the '3.0' version number, which is pretty lame.
3) This hard codes every other aspect of the resource dir structure
   which is less lame than the version number, but still not great.

However, it should bring the MSVC tests back to life, and it should
unblock the rest of the move from Frontend to Driver, so I think it's
worth a bit of grossness that is isolated in our testing infrastructure
while we figure out the best long term approach. I have the following
ideas, some of which only solve part of the problem (and thus might need
to be combined with other ideas):

a) Create a symlink or other convenience path instead of a version
   number.
b) Run 'clang' directly in the lit.cfg, look at its resource dir, and use
   that.
c) Switch all the tests to use the driver instead of CC1.
d) Hack the frontend to synthesize builtin include directories when none
   are provided by the driver.

I don't like (d) because it feels very hackish and likely to break. We
can only solve a small part of the problem with (a). I wanted to vote
for (c), but lots of the tests in this bucket are really heavily using
internal-only flags like -verify and -triple. I'm loath to complicate
them with the full driver layer. Also, switching them to the driver adds
more than just builtin headers, but all of the rest of the system
headers!

This leaves me with (b). If others like (b), I'll switch to it, but it
felt a bit icky. Nothing concrete, and the other options look
significantly worse, but I felt icky enough that I wanted to start with
a more brain-dead patch to stop the bleeding, and gauge others' feelings
here.
------------------------------------------------------------------------

llvm-svn: 143933
2011-11-07 10:20:16 +00:00
Chandler Carruth
4fb21f4bd6 Merging r143801:
------------------------------------------------------------------------
r143801 | chandlerc | 2011-11-05 02:24:44 -0700 (Sat, 05 Nov 2011) | 20 lines

Fix a significant oversight in my move of MSVC includes to the driver:
actually manage the builtin header file includes as well as the system
ones.

This one is actually debatable whether it belongs in the driver or not,
as the builtin includes are really an internal bit of implementation
goop for Clang. However, they must be included at *exactly* the right
point in the sequence of header files, which makes it essentially
impossible to have this be managed by the Frontend and the rest by the
Driver. I have terrible ideas that would "work", but I think they're
worse than putting this in the driver and making the Frontend library
even more ignorant of the environment and system on which it is being
run.

Also fix the fact that we weren't properly respecting the flags which
suppress standard system include directories.

Note that this still leaves all of the Clang tests which run CC1
directly and include builtin header files broken on Windows. I'm working
on a followup patch to address that.
------------------------------------------------------------------------

llvm-svn: 143932
2011-11-07 10:19:48 +00:00
Chandler Carruth
c0b5d07324 Merging r143798:
------------------------------------------------------------------------
r143798 | chandlerc | 2011-11-05 01:30:29 -0700 (Sat, 05 Nov 2011) | 16 lines

Add two flags to the CC1 layer that I was hoping to avoid. We need to
encode the *exact* semantics which the header search paths internally
built by the Frontend layer have had, which is both non-user-provided,
and at times adding the implicit extern "C" bit to the directory entry.

There are lots of CC1 options that are very close, but none do quite
this, and they are all already overloaded for other purposes. In some
senses this makes the command lines more clean as it clearly indicates
which flags are exclusively used to implement internal detection of
"standard" header search paths.

Lots of the implementation of this is really crufty, due to the
surrounding cruft. It doesn't seem worth investing lots of time cleaning
this up as it isn't new, and hopefully *lots* of this code will melt
away as header search inside of the frontend becomes increasingly
trivial.
------------------------------------------------------------------------

llvm-svn: 143931
2011-11-07 10:18:59 +00:00
Chandler Carruth
720a948f4c Merging r143752:
------------------------------------------------------------------------
r143752 | chandlerc | 2011-11-04 16:49:05 -0700 (Fri, 04 Nov 2011) | 5 lines

Begin the migration of header search logic to the driver, starting with
Windows. There are still FIXMEs and lots of problems with this code.
Some of them will be addressed shortly by my follow-up patches, but most
are going to wait until we isolate this code and can fix it properly.
This version should be no worse than what we had before.
------------------------------------------------------------------------

llvm-svn: 143930
2011-11-07 10:15:39 +00:00
Chandler Carruth
2d8f608f6c Merging r143751:
------------------------------------------------------------------------
r143751 | chandlerc | 2011-11-04 16:49:01 -0700 (Fri, 04 Nov 2011) | 3 lines

Switch the C++ include interface in the ToolChain to use the same naming
as the system include interface before I start adding implementations of
it to individual ToolChain implementations.
------------------------------------------------------------------------

llvm-svn: 143929
2011-11-07 10:15:01 +00:00
Chandler Carruth
3854a32832 Merging r143687:
------------------------------------------------------------------------
r143687 | chandlerc | 2011-11-04 00:43:33 -0700 (Fri, 04 Nov 2011) | 16 lines

Sink the strange '-stdlib=...' flag handling into the C++ include
handling logic of the generic ToolChain. This flag, despite its name,
has *nothing* to do with the GCC flag '-nostdlib' that relates
(exclusively) to the linking behavior. It is a most unfortunate name in
that regard...

It is used to tell InitHeaderSearch.cpp *which* set of C++ standard
library header search paths to use -- those for libstdc++ from GCC's
installation, or those from a libc++ installation. As this logic is
hoisted out of the Frontend, and into the Driver as part of this
ToolChain, the generic method will be overridden for the platform, where
it can implement this logic directly. As such, hiding the CC1 option
passing in the generic space is a natural fit despite the odd naming.

Also, expand on the comments to clarify whats going on, and tidy up the
Tools.cpp code now that its simpler.
------------------------------------------------------------------------

llvm-svn: 143928
2011-11-07 10:13:50 +00:00
Chandler Carruth
35d7d54446 Merging r143686:
------------------------------------------------------------------------
r143686 | chandlerc | 2011-11-04 00:34:47 -0700 (Fri, 04 Nov 2011) | 4 lines

Sink the handling of -fobjc-arc-cxxlib to live with the other -fobjc-arc
implementation in the driver. This cleans up the signature and semantics
of the include flag adding component of the toolchain. Another step to
ready it for holding all the InitHeaderSearch logic.
------------------------------------------------------------------------

llvm-svn: 143927
2011-11-07 10:12:59 +00:00
Chandler Carruth
58d9326bad ------------------------------------------------------------------------
r143684 | chandlerc | 2011-11-04 00:12:53 -0700 (Fri, 04 Nov 2011) | 7 lines

Add a system include management interface to the toolchain, and call it
and the C++ include management routine from the proper place when
forming preprocessor options in the driver. This is the first step to
teaching the driver to manage all of the header search paths. Currently,
these methods remain just stubs in the abstract toolchain. Subsequent
patches will flesh them out with implementations for various toolchains
based on the current code in InitHeaderSearch.cpp.
------------------------------------------------------------------------

llvm-svn: 143926
2011-11-07 10:07:22 +00:00
Bill Wendling
90ba37cff6 Merging r143159:
------------------------------------------------------------------------
r143159 | efriedma | 2011-10-27 15:32:13 -0700 (Thu, 27 Oct 2011) | 3 lines

The default alias analysis is -noaa; update the docs to reflect that.  Patch by Michael Ilseman.


------------------------------------------------------------------------

llvm-svn: 143681
2011-11-04 06:30:50 +00:00
Bill Wendling
5eafd41d83 Merging r143349:
------------------------------------------------------------------------
r143349 | chapuni | 2011-10-31 06:04:26 -0700 (Mon, 31 Oct 2011) | 1 line

docs/*.html: Fix markups.
------------------------------------------------------------------------

llvm-svn: 143610
2011-11-03 06:43:54 +00:00
Bill Wendling
75981ed191 Merging r143348:
------------------------------------------------------------------------
r143348 | chapuni | 2011-10-31 04:21:59 -0700 (Mon, 31 Oct 2011) | 1 line

docs/*.html: Appease W3C Checker to add "charset=utf-8".
------------------------------------------------------------------------

llvm-svn: 143609
2011-11-03 06:43:23 +00:00
Bill Wendling
e8bdd73f80 Merging r143328:
------------------------------------------------------------------------
r143328 | nicholas | 2011-10-30 18:32:21 -0700 (Sun, 30 Oct 2011) | 2 lines

Close <div> that was indenting the rest of the page.

------------------------------------------------------------------------

llvm-svn: 143608
2011-11-03 06:42:43 +00:00
Bill Wendling
cc9407d0e8 Merging r143290:
------------------------------------------------------------------------
r143290 | d0k | 2011-10-29 12:43:38 -0700 (Sat, 29 Oct 2011) | 3 lines

PPC: Disable moves for all CR subregisters.

Should fix assertion failures on ppc buildbots.
------------------------------------------------------------------------

llvm-svn: 143451
2011-11-01 05:11:00 +00:00
Bill Wendling
ecf0769549 Merging r143303:
------------------------------------------------------------------------

llvm-svn: 143450
2011-11-01 05:09:30 +00:00
Bill Wendling
97ba845838 Merging r143194:
------------------------------------------------------------------------
r143194 | chapuni | 2011-10-28 07:12:22 -0700 (Fri, 28 Oct 2011) | 7 lines

Dwarf: [PR11022] Fix emitting DW_AT_const_value(>i64), to be host-endian-neutral.

Don't assume APInt::getRawData() would hold target-aware endianness nor host-compliant endianness. rawdata[0] holds most lower i64, even on big endian host.

FIXME: Add a testcase for big endian target.

FIXME: Ditto on CompileUnit::addConstantFPValue() ?
------------------------------------------------------------------------

llvm-svn: 143449
2011-11-01 05:08:55 +00:00
Bill Wendling
491019467c Fixed in r143195. Still it is not "TARGETS" neutral.
llvm-svn: 143448
2011-11-01 05:06:53 +00:00
Bill Wendling
6131d993df Fixes r142984, r143120 and r143191 for PPC.
llvm-svn: 143447
2011-11-01 05:05:06 +00:00
Bill Wendling
12bfc7914b Update release notes to ToT. WIP.
llvm-svn: 143439
2011-11-01 04:51:35 +00:00
Bill Wendling
aee68a10e7 Merging r143437:
------------------------------------------------------------------------
r143437 | efriedma | 2011-10-31 21:49:29 -0700 (Mon, 31 Oct 2011) | 3 lines

Make sure we use the right insertion point when instcombine replaces a PHI with another instruction.  (Specifically, don't insert an arbitrary instruction before a PHI.)  Fixes PR11275.


------------------------------------------------------------------------

llvm-svn: 143438
2011-11-01 04:51:01 +00:00
Bill Wendling
3a2254cbcb Merging r143345:
------------------------------------------------------------------------
r143345 | chandlerc | 2011-10-31 01:42:27 -0700 (Mon, 31 Oct 2011) | 7 lines

Add "just one more" include path to the monstrosity that is our header
search logic. The Debian multiarch seems to have completely changed from
when these were originally added, and I'd like to remove a bunch of
them, but I'll be lazy and delay that until this logic is hoisted into
the driver where it belongs.

This should resolve PR11223.
------------------------------------------------------------------------

llvm-svn: 143433
2011-11-01 04:12:17 +00:00
Bill Wendling
b4a5136638 Merging r143344:
------------------------------------------------------------------------
r143344 | chandlerc | 2011-10-31 01:42:24 -0700 (Mon, 31 Oct 2011) | 14 lines

Fix part of PR11223 and probably a few dups as well. This teaches the
library search logic to "properly" handle multiarch installations. I've
tested this on both Debian unstable and the latest Ubuntu which both use
this setup, and this appears to work largely the same way as GCC does.
It isn't exactly the same, but it is close enough and more principled in
its behavior where it differs. This should resolve any failures to find
'crt1.o' etc on Debian-based Linux distributions. If folks find more
cases where we fail, please file bugs and CC me.

Test cases for all of the debian silliness are waiting both to simplify
the process of merging these down into the 3.0 release, and because
they're so crazy I haven't yet been able to really produce a fake tree
that represents what we need to test for. I'll eventually add them
though.
------------------------------------------------------------------------

llvm-svn: 143432
2011-11-01 04:11:09 +00:00
Bill Wendling
5817c40379 Merging r143302:
------------------------------------------------------------------------
r143302 | void | 2011-10-29 16:49:52 -0700 (Sat, 29 Oct 2011) | 1 line

Do a relative path ln command instead of an absolute path one. Some people strangely enough have different directory layouts...
------------------------------------------------------------------------

llvm-svn: 143303
2011-10-29 23:50:44 +00:00
Bill Wendling
925024ef0a Patch for PR9614. By Rafael Espindola.
llvm-svn: 143301
2011-10-29 23:46:36 +00:00
Bill Wendling
4122a1132c Merging r143247:
------------------------------------------------------------------------
r143247 | chapuni | 2011-10-28 16:11:03 -0700 (Fri, 28 Oct 2011) | 1 line

test/CodeGen/PowerPC/2008-10-17-AsmMatchingOperands.ll: [PR11218] Mark "REQUIRES: asserts" for now.
------------------------------------------------------------------------

llvm-svn: 143272
2011-10-29 01:20:35 +00:00
Bill Wendling
6a44832760 Fixes an intermittent bug that I finally got a good test case for in GNU ObjC.
Patch by David Chisnall.

llvm-svn: 143270
2011-10-29 01:19:11 +00:00
Bill Wendling
8e7812f680 Merging r143222:
------------------------------------------------------------------------

llvm-svn: 143226
2011-10-28 21:08:15 +00:00
Bill Wendling
474d7226a0 Merging r142998:
------------------------------------------------------------------------
r142998 | efriedma | 2011-10-25 17:55:23 -0700 (Tue, 25 Oct 2011) | 3 lines

Add a note about the removal of the atomic intrinsics to the "Major Changes" section of the release notes.


------------------------------------------------------------------------

llvm-svn: 143070
2011-10-26 22:36:46 +00:00
Bill Wendling
e45cf9ef9f Merging r142994:
------------------------------------------------------------------------
r142994 | efriedma | 2011-10-25 17:36:41 -0700 (Tue, 25 Oct 2011) | 3 lines

Remove dead atomic intrinsics from LangRef.


------------------------------------------------------------------------

llvm-svn: 143069
2011-10-26 22:35:59 +00:00
Bill Wendling
377aca8d42 Complete the missing parts of MIPS-JIT functionality. Patch by Petar Jovanovic.
llvm-svn: 143014
2011-10-26 04:27:18 +00:00
Bill Wendling
2b2cfe2ead Merging r143006:
------------------------------------------------------------------------
r143006 | stoklund | 2011-10-25 18:47:48 -0700 (Tue, 25 Oct 2011) | 8 lines

Don't use floating point to do an integer's job.

This code makes different decisions when compiled into x87 instructions
because of different rounding behavior.  That caused phase 2/3
miscompares on 32-bit Linux when the phase 1 compiler was built with gcc
(using x87), and the phase 2 compiler was built with clang (using SSE).

This fixes PR11200.
------------------------------------------------------------------------

llvm-svn: 143013
2011-10-26 04:25:53 +00:00
Bill Wendling
bb15689d96 Merging r142956:
------------------------------------------------------------------------
r142956 | grosbach | 2011-10-25 13:30:48 -0700 (Tue, 25 Oct 2011) | 4 lines

Make assert() message more informative.

PR11217.

------------------------------------------------------------------------

llvm-svn: 142967
2011-10-25 20:42:34 +00:00
Bill Wendling
3493a5de9d Merging r142918:
------------------------------------------------------------------------
r142918 | theraven | 2011-10-25 03:12:21 -0700 (Tue, 25 Oct 2011) | 3 lines

Change an int64_t to an intptr_t so that we don't end up with crashes in the back end on large classes on 32-bit.


------------------------------------------------------------------------

llvm-svn: 142953
2011-10-25 20:19:33 +00:00
Bill Wendling
b9f7abc7cc Merging r142869:
------------------------------------------------------------------------
r142869 | void | 2011-10-24 16:05:43 -0700 (Mon, 24 Oct 2011) | 4 lines

Check the visibility of the global variable before placing it into the stubs
table. A hidden variable could potentially end up in both lists.
<rdar://problem/10336715>

------------------------------------------------------------------------

llvm-svn: 142870
2011-10-24 23:06:48 +00:00
Bill Wendling
5d6720886c Merging r142841:
------------------------------------------------------------------------
r142841 | efriedma | 2011-10-24 13:24:21 -0700 (Mon, 24 Oct 2011) | 3 lines

Add support to the old JIT for acquire/release loads and stores on x86.  PR11207.


------------------------------------------------------------------------

llvm-svn: 142842
2011-10-24 20:27:56 +00:00
Bill Wendling
85debca4cb In LLVM 2.9, the GHC calling convention is only supported on x86-32,
x86-64. We (GHC team) would like this patch included as we've recently
added support to GHC for the ARM platform.
Patch by David Terei!

llvm-svn: 142820
2011-10-24 18:08:38 +00:00
Bill Wendling
68fa59d902 Merging r142806:
------------------------------------------------------------------------
r142806 | grosbach | 2011-10-24 10:26:26 -0700 (Mon, 24 Oct 2011) | 1 line

Update test for r142801.
------------------------------------------------------------------------

llvm-svn: 142809
2011-10-24 17:33:30 +00:00
Bill Wendling
97fa7e0ee2 Merging r142801:
------------------------------------------------------------------------
r142801 | grosbach | 2011-10-24 10:16:24 -0700 (Mon, 24 Oct 2011) | 4 lines

Thumb2 LDM instructions can target PC. Make sure to encode it.

PR11220

------------------------------------------------------------------------

llvm-svn: 142808
2011-10-24 17:33:07 +00:00
Bill Wendling
9eb29f154c Merging r142476:
------------------------------------------------------------------------
r142476 | dgregor | 2011-10-18 22:50:34 -0700 (Tue, 18 Oct 2011) | 2 lines

Add TypeKind.CONSTANTARRAY, from  Anders Waldenborg!

------------------------------------------------------------------------

llvm-svn: 142723
2011-10-22 11:36:45 +00:00
Bill Wendling
20529b792b Merging r142474:
------------------------------------------------------------------------
r142474 | dgregor | 2011-10-18 22:47:46 -0700 (Tue, 18 Oct 2011) | 5 lines

This new field was introduced in clang-c in r141277

Python needs this in its structure definition so it allocates enough
memory. From Anders Waldenborg!

------------------------------------------------------------------------

llvm-svn: 142722
2011-10-22 11:35:15 +00:00
Bill Wendling
24e86d5820 Patch to build win64 LLVM on Mingw. Patch by Ruben Van Boxem.
llvm-svn: 142614
2011-10-20 20:33:11 +00:00
Bill Wendling
fff6b94f92 Merging r142574:
------------------------------------------------------------------------
r142574 | baldrick | 2011-10-20 01:57:54 -0700 (Thu, 20 Oct 2011) | 3 lines

Comment out or remove unused parameter names so as to avoid a slew of
compiler warnings.

------------------------------------------------------------------------

llvm-svn: 142611
2011-10-20 20:28:51 +00:00
Bill Wendling
ef34692ce0 Merging r142573:
------------------------------------------------------------------------
r142573 | baldrick | 2011-10-20 01:56:41 -0700 (Thu, 20 Oct 2011) | 2 lines

Avoid warnings about the parameter G being unused.

------------------------------------------------------------------------

llvm-svn: 142610
2011-10-20 20:28:30 +00:00
Bill Wendling
ff7a27c82b Merging r142350:
------------------------------------------------------------------------
r142350 | baldrick | 2011-10-18 05:44:00 -0700 (Tue, 18 Oct 2011) | 3 lines

Fix a bunch of unused variable warnings when doing a release
build with gcc-4.6.

------------------------------------------------------------------------

llvm-svn: 142609
2011-10-20 20:28:08 +00:00
Bill Wendling
3e8b49ff47 Merging r142559:
------------------------------------------------------------------------
r142559 | void | 2011-10-19 17:45:46 -0700 (Wed, 19 Oct 2011) | 1 line

Revamp the script to handle the 'TEST=simple' output.
------------------------------------------------------------------------

llvm-svn: 142560
2011-10-20 00:46:28 +00:00
Bill Wendling
440ca6f9f1 Merging r142550:
------------------------------------------------------------------------
r142550 | evancheng | 2011-10-19 15:22:54 -0700 (Wed, 19 Oct 2011) | 1 line

Fix TLS lowering bug. The CopyFromReg must be glued to the TLSCALL. rdar://10291355
------------------------------------------------------------------------

llvm-svn: 142555
2011-10-19 23:49:38 +00:00
Bill Wendling
2406e73c0e Merging r142493:
------------------------------------------------------------------------
r142493 | chandlerc | 2011-10-19 03:32:19 -0700 (Wed, 19 Oct 2011) | 5 lines

Generalize the reading of probability metadata to work for both branches
and switches, with arbitrary numbers of successors. Still optimized for
the common case of 2 successors for a conditional branch.

Add a test case for switch metadata showing up in the BlockFrequencyInfo pass.
------------------------------------------------------------------------

llvm-svn: 142548
2011-10-19 22:13:20 +00:00
Bill Wendling
6b0c30492b Merging r142492:
------------------------------------------------------------------------
r142492 | chandlerc | 2011-10-19 03:30:30 -0700 (Wed, 19 Oct 2011) | 7 lines

Teach the BranchProbabilityInfo analysis pass to read any metadata
encoding of probabilities. In the absense of metadata, it continues to
fall back on static heuristics.

This allows __builtin_expect, after lowering through llvm.expect
a branch instruction's metadata, to actually enter the branch
probability model. This is one component of resolving PR2577.
------------------------------------------------------------------------

llvm-svn: 142547
2011-10-19 22:12:52 +00:00
Bill Wendling
b5cd42ab52 Merging r142491:
------------------------------------------------------------------------
r142491 | chandlerc | 2011-10-19 03:12:41 -0700 (Wed, 19 Oct 2011) | 9 lines

Add pass printing support to BlockFrequencyInfo pass. The implementation
layer already had support for printing the results of this analysis, but
the wiring was missing.

Now that printing the analysis works, actually bring some of this
analysis, and the BranchProbabilityInfo analysis that it wraps, under
test! I'm planning on fixing some bugs and doing other work here, so
having a nice place to add regression tests and a way to observe the
results is really useful.
------------------------------------------------------------------------

llvm-svn: 142546
2011-10-19 22:12:25 +00:00
Bill Wendling
7355790567 Merging r142168:
------------------------------------------------------------------------
r142168 | chandlerc | 2011-10-16 18:11:57 -0700 (Sun, 16 Oct 2011) | 8 lines

Add a routine to swap branch instruction operands, and update any
profile metadata at the same time. Use it to preserve metadata attached
to a branch when re-writing it in InstCombine.

Add metadata to the canonicalize_branch InstCombine test, and check that
it is tranformed correctly.

Reviewed by Nick Lewycky!
------------------------------------------------------------------------

llvm-svn: 142545
2011-10-19 22:11:55 +00:00
Bill Wendling
9feb000b5d Merging r142537:
------------------------------------------------------------------------
r142537 | rafael | 2011-10-19 11:48:52 -0700 (Wed, 19 Oct 2011) | 1 line

Fix parsing of a line with only a # in it.
------------------------------------------------------------------------

llvm-svn: 142539
2011-10-19 19:48:46 +00:00
Bill Wendling
e0f468e366 Merging r142489:
------------------------------------------------------------------------
r142489 | void | 2011-10-19 02:47:00 -0700 (Wed, 19 Oct 2011) | 1 line

Duncan pointed out that sometimes CC and CXX are used to specify the compiler. Also that the configure script takes care of finding an appropriate compiler if one's not specified.
------------------------------------------------------------------------

llvm-svn: 142490
2011-10-19 09:47:36 +00:00
Bill Wendling
d71be28020 Merging r142486:
------------------------------------------------------------------------
r142486 | void | 2011-10-19 02:25:49 -0700 (Wed, 19 Oct 2011) | 1 line

Use bash instead.
------------------------------------------------------------------------

llvm-svn: 142487
2011-10-19 09:26:28 +00:00
Bill Wendling
a6c52d2c2c Merging r142482:
------------------------------------------------------------------------
r142482 | void | 2011-10-19 01:42:07 -0700 (Wed, 19 Oct 2011) | 1 line

Make changes so that this runs on FreeBSD.
------------------------------------------------------------------------

llvm-svn: 142483
2011-10-19 08:45:26 +00:00
Bill Wendling
514c81fdf1 Merging r142187:
------------------------------------------------------------------------
r142187 | dgregor | 2011-10-17 07:55:37 -0700 (Mon, 17 Oct 2011) | 6 lines

When building a module, use the macro definitions on the command line
as part of the hash rather than ignoring them. This means we'll end up
building more module variants (overall), but it allows configuration
macros such as NDEBUG to work so long as they're specified via command
line. More to come in this space.

------------------------------------------------------------------------

llvm-svn: 142428
2011-10-18 21:54:53 +00:00
Bill Wendling
274c302dde Merging r142349:
------------------------------------------------------------------------
r142349 | d0k | 2011-10-18 03:10:08 -0700 (Tue, 18 Oct 2011) | 4 lines

Use llvm::Triple's methods to parse FreeBSD version numbers.

Who could've thought that FreeBSD would ever reach version 10!
Patch from Dimitry Andric.
------------------------------------------------------------------------

llvm-svn: 142366
2011-10-18 17:22:17 +00:00
Bill Wendling
b35703c039 Merging r142243:
------------------------------------------------------------------------
r142243 | void | 2011-10-17 13:28:54 -0700 (Mon, 17 Oct 2011) | 1 line

Remove mention of llvm-gcc and add mention of dragonegg.
------------------------------------------------------------------------

llvm-svn: 142244
2011-10-17 20:32:14 +00:00
Bill Wendling
2d66439333 Rough out the EH changes. Include here a sketch on how to use the new APIs.
llvm-svn: 142181
2011-10-17 06:31:58 +00:00
Bill Wendling
9517c0fdb2 Add the 3.0 release.
llvm-svn: 142180
2011-10-17 06:31:32 +00:00
Bill Wendling
f67c377203 Formatting changes. No content change.
llvm-svn: 142178
2011-10-17 05:39:25 +00:00
Bill Wendling
906cc78d7c Merging r142134:
------------------------------------------------------------------------
r142134 | chandlerc | 2011-10-16 04:05:04 -0700 (Sun, 16 Oct 2011) | 1 line

Fix a silly bug introduced in r142133.
------------------------------------------------------------------------

llvm-svn: 142175
2011-10-17 05:14:50 +00:00
Bill Wendling
e6d5fab835 Merging r142133:
------------------------------------------------------------------------
r142133 | chandlerc | 2011-10-16 03:54:30 -0700 (Sun, 16 Oct 2011) | 30 lines

Clean up some cruft in the library path searching logic by making
'libdir' mean the actual library directory, not the GCC subdirectory of
the library directory. That was just a confusing pattern. Instead,
supply proper GCC subdirectories when scanning for various triple-based
subdirectories with a GCC installation in them. This also makes it much
more obvious how multiarch installations, which have a triple-based
prefix as well as suffix work.

Also clean up our handling of these triple-prefixed trees by using them
in both a multiarch pattern and a non-multiarch pattern whenever they
exist.

Note that this *does not* match what GCC does on Debian, the only truly
multiarch installation I've been able to get installed and test on. GCC
appears to have a bug, and ends up searching paths like
'/lib/../../lib32' which makes no sense what-so-ever. Instead, I've
tried to encode the rational logic that seems clearly intended by GCC's
pattern. GCC ends up with patterns like:

  /lib/../../lib32
  /usr/lib/../../lib32
  /usr/lib/x86_64-linux-gnu/../..lib32

Only the last one makes any sense having a '/../..' in it, so in Clang,
that's the only one which gets a '/../..' in it.

I *think* this will fix Debian multiarch links. I'm committing without
baking this logic into our test suite so I can test on a few different
systems. If all goes well (and no one screams) I'll check in some more
comprehensive tests for multiarch behavior tomorrow.
------------------------------------------------------------------------

llvm-svn: 142174
2011-10-17 05:14:05 +00:00
Bill Wendling
a3e5429d9b Apply r142132:
PR11143: Save the old diagnostic handler and call it when munging diagnostics for #line directives.

This reenables proper inline asm diagnostics in clang

llvm-svn: 142169
2011-10-17 03:47:20 +00:00
Bill Wendling
2723da100f Merging r142165:
------------------------------------------------------------------------
r142165 | void | 2011-10-16 15:44:08 -0700 (Sun, 16 Oct 2011) | 5 lines

Overhaul the 'test-release' script.

This removes support for building llvm-gcc. It will eventually add support for
building other projects.

------------------------------------------------------------------------

llvm-svn: 142166
2011-10-16 22:47:07 +00:00
Bill Wendling
9e7107c9d9 This patch relies upon non-incorporated patches.
--- Reverse-merging r142156 into '.':
U    include/llvm/Support/SourceMgr.h
U    lib/MC/MCParser/AsmParser.cpp

llvm-svn: 142161
2011-10-16 21:58:10 +00:00
Bill Wendling
c7248e8f2b Merging r142132:
------------------------------------------------------------------------
r142132 | d0k | 2011-10-16 03:48:29 -0700 (Sun, 16 Oct 2011) | 3 lines

PR11143: Save the old diagnostic handler and call it when munging diagnostics for #line directives.

This reenables proper inline asm diagnostics in clang
------------------------------------------------------------------------

llvm-svn: 142156
2011-10-16 21:01:22 +00:00
Bill Wendling
2dfa76cecd Merging r142125:
------------------------------------------------------------------------
r142125 | void | 2011-10-16 01:24:30 -0700 (Sun, 16 Oct 2011) | 1 line

Install the PNG file as well. PR4780.
------------------------------------------------------------------------

llvm-svn: 142126
2011-10-16 08:25:24 +00:00
Bill Wendling
29ef90e6c4 Merging r142123:
------------------------------------------------------------------------
r142123 | void | 2011-10-16 01:06:54 -0700 (Sun, 16 Oct 2011) | 4 lines

Update tutorial to reflect the current APIs. Also correct a small omission in
LangImpl6.html (it needed to defined the 'binary :' operator).
PR9052

------------------------------------------------------------------------

llvm-svn: 142124
2011-10-16 08:07:38 +00:00
Bill Wendling
58de1fdf54 Merging r142113:
------------------------------------------------------------------------
r142113 | void | 2011-10-15 23:43:18 -0700 (Sat, 15 Oct 2011) | 1 line

Enable CREATE_SUBDIRS for a performance win, because there are a lot of files being generated.
------------------------------------------------------------------------

llvm-svn: 142115
2011-10-16 06:46:57 +00:00
Bill Wendling
ac42e4daef Merging r142112:
------------------------------------------------------------------------
r142112 | void | 2011-10-15 23:43:10 -0700 (Sat, 15 Oct 2011) | 1 line

Enable CREATE_SUBDIRS for a performance win, because there are a lot of files being generated.
------------------------------------------------------------------------

llvm-svn: 142114
2011-10-16 06:46:15 +00:00
Bill Wendling
5ebd8c9a1c Update to compile with assertions turned off. Say that optimizations are turned on by default.
llvm-svn: 142099
2011-10-16 02:52:56 +00:00
Bill Wendling
c1174de9f4 Merging r142055:
------------------------------------------------------------------------
r142055 | chapuni | 2011-10-14 21:29:36 -0700 (Fri, 14 Oct 2011) | 5 lines

Revert r142046, "Fix for llvm::sys::getHostTriple on Windows.  Instead of relying on the triple"

It extremely breaks builds when optimization is enabled. Twine should not hold temporary objects.

By the way, I might feel sad if I saw "i786-" "i1586-" or "iF86-".
------------------------------------------------------------------------

llvm-svn: 142096
2011-10-16 01:51:52 +00:00
Bill Wendling
0389c51dcb Merging r142039:
------------------------------------------------------------------------
r142039 | chapuni | 2011-10-14 18:58:16 -0700 (Fri, 14 Oct 2011) | 3 lines

Windows/Memory.inc: Support the ability to allocate memory "near" another block of memory on Win32. It has fixed FIXME.

Thanks to Aaron Ballman!
------------------------------------------------------------------------

llvm-svn: 142095
2011-10-16 01:49:31 +00:00
Bill Wendling
229865e2a4 ------------------------------------------------------------------------
r142058 | atrick | 2011-10-14 23:19:55 -0700 (Fri, 14 Oct 2011) | 7 lines

Fix SCEVExpander assert during LSR: "argument of incompatible type".

Just because we're dealing with a GEP doesn't mean we can assert the
SCEV has a pointer type. The fix is simply to ignore the SCEV pointer
type, which we really didn't need.
Fixes PR11138 webkit crash.

------------------------------------------------------------------------

llvm-svn: 142094
2011-10-16 01:47:12 +00:00
Bill Wendling
6cf1189fe7 Drop the 'svn' from the version number.
llvm-svn: 142093
2011-10-16 00:53:24 +00:00
Bill Wendling
e43a4cbd5f Creating release_30 branch
llvm-svn: 142043
llvm-svn: 142042
llvm-svn: 142041
2011-10-15 01:58:38 +00:00
Bill Wendling
ebcd208ddd Creating release_30 branch
llvm-svn: 142038
llvm-svn: 142037
2011-10-15 01:58:16 +00:00
64661 changed files with 1287752 additions and 9469673 deletions

View File

@@ -1,4 +0,0 @@
{
"project_id" : "clang-tools-extra",
"conduit_uri" : "https://reviews.llvm.org/"
}

View File

@@ -1,32 +0,0 @@
#==============================================================================#
# This file specifies intentionally untracked files that git should ignore.
# See: http://www.kernel.org/pub/software/scm/git/docs/gitignore.html
#
# This file is intentionally different from the output of `git svn show-ignore`,
# as most of those are useless.
#==============================================================================#
#==============================================================================#
# File extensions to be ignored anywhere in the tree.
#==============================================================================#
# Temp files created by most text editors.
*~
# Merge files created by git.
*.orig
# Byte compiled python modules.
*.pyc
# vim swap files
.*.swp
.sw?
#==============================================================================#
# Explicit files to ignore (only matches one).
#==============================================================================#
cscope.files
cscope.out
.clang_complete
#==============================================================================#
# Directories to ignore (do not add trailing '/'s, they skip symlinks).
#==============================================================================#
docs/_build

View File

@@ -1,29 +0,0 @@
add_subdirectory(clang-apply-replacements)
add_subdirectory(clang-reorder-fields)
add_subdirectory(modularize)
if(CLANG_ENABLE_STATIC_ANALYZER)
add_subdirectory(clang-tidy)
add_subdirectory(clang-tidy-vs)
endif()
add_subdirectory(change-namespace)
add_subdirectory(clang-query)
add_subdirectory(clang-move)
add_subdirectory(clangd)
add_subdirectory(include-fixer)
add_subdirectory(pp-trace)
add_subdirectory(tool-template)
# Add the common testsuite after all the tools.
# TODO: Support tests with more granularity when features are off?
if(CLANG_ENABLE_STATIC_ANALYZER AND CLANG_INCLUDE_TESTS)
add_subdirectory(test)
add_subdirectory(unittests)
endif()
option(CLANG_TOOLS_EXTRA_INCLUDE_DOCS "Generate build targets for the Clang Extra Tools docs."
${LLVM_INCLUDE_DOCS})
if( CLANG_TOOLS_EXTRA_INCLUDE_DOCS )
add_subdirectory(docs)
endif()

View File

@@ -1,21 +0,0 @@
This file is a list of the people responsible for ensuring that patches for a
particular tool are reviewed, either by themself or by someone else. They are
also the gatekeepers for their part of Clang, with the final word on what goes
in or not.
The list is sorted by surname and formatted to allow easy grepping and
beautification by scripts. The fields are: name (N), email (E), web-address
(W), PGP key ID and fingerprint (P), description (D), and snail-mail address
(S).
N: Peter Collingbourne
E: peter@pcc.me.uk
D: clang-query
N: Manuel Klimek
E: klimek@google.com
D: clang-rename, all parts of clang-tools-extra not covered by someone else
N: Alexander Kornienko
E: alexfh@google.com
D: clang-tidy

View File

@@ -1,63 +0,0 @@
==============================================================================
LLVM Release License
==============================================================================
University of Illinois/NCSA
Open Source License
Copyright (c) 2007-2016 University of Illinois at Urbana-Champaign.
All rights reserved.
Developed by:
LLVM Team
University of Illinois at Urbana-Champaign
http://llvm.org
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal with
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimers.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimers in the
documentation and/or other materials provided with the distribution.
* Neither the names of the LLVM Team, University of Illinois at
Urbana-Champaign, nor the names of its contributors may be used to
endorse or promote products derived from this Software without specific
prior written permission.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE
SOFTWARE.
==============================================================================
The LLVM software contains code written by third parties. Such software will
have its own individual LICENSE.TXT file in the directory in which it appears.
This file will describe the copyrights, license, and restrictions which apply
to that code.
The disclaimer of warranty in the University of Illinois Open Source License
applies to all code in the LLVM Distribution, and nothing in any of the
other licenses gives permission to use the names of the LLVM Team or the
University of Illinois to endorse or promote products derived from this
Software.
The following pieces of software have additional or alternate copyrights,
licenses, and/or restrictions:
Program Directory
------- ---------
clang-tidy clang-tidy/cert
clang-tidy clang-tidy/hicpp

View File

@@ -1,22 +0,0 @@
//===----------------------------------------------------------------------===//
// Clang Tools repository
//===----------------------------------------------------------------------===//
Welcome to the repository of extra Clang Tools. This repository holds tools
that are developed as part of the LLVM compiler infrastructure project and the
Clang frontend. These tools are kept in a separate "extra" repository to
allow lighter weight checkouts of the core Clang codebase.
This repository is only intended to be checked out inside of a full LLVM+Clang
tree, and in the 'tools/extra' subdirectory of the Clang checkout.
All discussion regarding Clang, Clang-based tools, and code in this repository
should be held using the standard Clang mailing lists:
http://lists.llvm.org/mailman/listinfo/cfe-dev
Code review for this tree should take place on the standard Clang patch and
commit lists:
http://lists.llvm.org/mailman/listinfo/cfe-commits
If you find a bug in these tools, please file it in the LLVM bug tracker:
http://llvm.org/bugs/

View File

@@ -1,19 +0,0 @@
set(LLVM_LINK_COMPONENTS
support
)
add_clang_library(clangChangeNamespace
ChangeNamespace.cpp
LINK_LIBS
clangAST
clangASTMatchers
clangBasic
clangFormat
clangFrontend
clangLex
clangTooling
clangToolingCore
)
add_subdirectory(tool)

File diff suppressed because it is too large Load Diff

View File

@@ -1,176 +0,0 @@
//===-- ChangeNamespace.h -- Change namespace ------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_TOOLS_EXTRA_CHANGE_NAMESPACE_CHANGENAMESPACE_H
#define LLVM_CLANG_TOOLS_EXTRA_CHANGE_NAMESPACE_CHANGENAMESPACE_H
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/Format/Format.h"
#include "clang/Tooling/Core/Replacement.h"
#include "llvm/Support/Regex.h"
#include <string>
namespace clang {
namespace change_namespace {
// This tool can be used to change the surrounding namespaces of class/function
// definitions. Classes/functions in the moved namespace will have new
// namespaces while references to symbols (e.g. types, functions) which are not
// defined in the changed namespace will be correctly qualified by prepending
// namespace specifiers before them.
// This will try to add shortest namespace specifiers possible. When a symbol
// reference needs to be fully-qualified, this adds a "::" prefix to the
// namespace specifiers unless the new namespace is the global namespace.
// For classes, only classes that are declared/defined in the given namespace in
// speficifed files will be moved: forward declarations will remain in the old
// namespace.
// For example, changing "a" to "x":
// Old code:
// namespace a {
// class FWD;
// class A { FWD *fwd; }
// } // a
// New code:
// namespace a {
// class FWD;
// } // a
// namespace x {
// class A { ::a::FWD *fwd; }
// } // x
// FIXME: support moving typedef, enums across namespaces.
class ChangeNamespaceTool : public ast_matchers::MatchFinder::MatchCallback {
public:
// Moves code in the old namespace `OldNs` to the new namespace `NewNs` in
// files matching `FilePattern`.
ChangeNamespaceTool(
llvm::StringRef OldNs, llvm::StringRef NewNs, llvm::StringRef FilePattern,
llvm::ArrayRef<std::string> WhiteListedSymbolPatterns,
std::map<std::string, tooling::Replacements> *FileToReplacements,
llvm::StringRef FallbackStyle = "LLVM");
void registerMatchers(ast_matchers::MatchFinder *Finder);
void run(const ast_matchers::MatchFinder::MatchResult &Result) override;
// Moves the changed code in old namespaces but leaves class forward
// declarations behind.
void onEndOfTranslationUnit() override;
private:
void moveOldNamespace(const ast_matchers::MatchFinder::MatchResult &Result,
const NamespaceDecl *NsDecl);
void moveClassForwardDeclaration(
const ast_matchers::MatchFinder::MatchResult &Result,
const NamedDecl *FwdDecl);
void replaceQualifiedSymbolInDeclContext(
const ast_matchers::MatchFinder::MatchResult &Result,
const DeclContext *DeclContext, SourceLocation Start, SourceLocation End,
const NamedDecl *FromDecl);
void fixTypeLoc(const ast_matchers::MatchFinder::MatchResult &Result,
SourceLocation Start, SourceLocation End, TypeLoc Type);
void fixUsingShadowDecl(const ast_matchers::MatchFinder::MatchResult &Result,
const UsingDecl *UsingDeclaration);
void fixDeclRefExpr(const ast_matchers::MatchFinder::MatchResult &Result,
const DeclContext *UseContext, const NamedDecl *From,
const DeclRefExpr *Ref);
// Information about moving an old namespace.
struct MoveNamespace {
// The start offset of the namespace block being moved in the original
// code.
unsigned Offset;
// The length of the namespace block in the original code.
unsigned Length;
// The offset at which the new namespace block will be inserted in the
// original code.
unsigned InsertionOffset;
// The file in which the namespace is declared.
FileID FID;
SourceManager *SourceMgr;
};
// Information about inserting a class forward declaration.
struct InsertForwardDeclaration {
// The offset at while the forward declaration will be inserted in the
// original code.
unsigned InsertionOffset;
// The code to be inserted.
std::string ForwardDeclText;
};
std::string FallbackStyle;
// In match callbacks, this contains replacements for replacing `typeLoc`s in
// and deleting forward declarations in the moved namespace blocks.
// In `onEndOfTranslationUnit` callback, the previous added replacements are
// applied (on the moved namespace blocks), and then changed code in old
// namespaces re moved to new namespaces, and previously deleted forward
// declarations are inserted back to old namespaces, from which they are
// deleted.
std::map<std::string, tooling::Replacements> &FileToReplacements;
// A fully qualified name of the old namespace without "::" prefix, e.g.
// "a::b::c".
std::string OldNamespace;
// A fully qualified name of the new namespace without "::" prefix, e.g.
// "x::y::z".
std::string NewNamespace;
// The longest suffix in the old namespace that does not overlap the new
// namespace.
// For example, if `OldNamespace` is "a::b::c" and `NewNamespace` is
// "a::x::y", then `DiffOldNamespace` will be "b::c".
std::string DiffOldNamespace;
// The longest suffix in the new namespace that does not overlap the old
// namespace.
// For example, if `OldNamespace` is "a::b::c" and `NewNamespace` is
// "a::x::y", then `DiffNewNamespace` will be "x::y".
std::string DiffNewNamespace;
// A regex pattern that matches files to be processed.
std::string FilePattern;
llvm::Regex FilePatternRE;
// Information about moved namespaces grouped by file.
// Since we are modifying code in old namespaces (e.g. add namespace
// spedifiers) as well as moving them, we store information about namespaces
// to be moved and only move them after all modifications are finished (i.e.
// in `onEndOfTranslationUnit`).
std::map<std::string, std::vector<MoveNamespace>> MoveNamespaces;
// Information about forward declaration insertions grouped by files.
// A class forward declaration is not moved, so it will be deleted from the
// moved code block and inserted back into the old namespace. The insertion
// will be done after removing the code from the old namespace and before
// inserting it to the new namespace.
std::map<std::string, std::vector<InsertForwardDeclaration>> InsertFwdDecls;
// Records all using declarations, which can be used to shorten namespace
// specifiers.
llvm::SmallPtrSet<const UsingDecl *, 8> UsingDecls;
// Records all using namespace declarations, which can be used to shorten
// namespace specifiers.
llvm::SmallPtrSet<const UsingDirectiveDecl *, 8> UsingNamespaceDecls;
// Records all namespace alias declarations, which can be used to shorten
// namespace specifiers.
llvm::SmallPtrSet<const NamespaceAliasDecl *, 8> NamespaceAliasDecls;
// TypeLocs of CXXCtorInitializer. Types of CXXCtorInitializers do not need to
// be fixed.
llvm::SmallVector<TypeLoc, 8> BaseCtorInitializerTypeLocs;
// Since a DeclRefExpr for a function call can be matched twice (one as
// CallExpr and one as DeclRefExpr), we record all DeclRefExpr's that have
// been processed so that we don't handle them twice.
llvm::SmallPtrSet<const clang::DeclRefExpr*, 16> ProcessedFuncRefs;
// Patterns of symbol names whose references are not expected to be updated
// when changing namespaces around them.
std::vector<llvm::Regex> WhiteListedSymbolRegexes;
};
} // namespace change_namespace
} // namespace clang
#endif // LLVM_CLANG_TOOLS_EXTRA_CHANGE_NAMESPACE_CHANGENAMESPACE_H

View File

@@ -1,23 +0,0 @@
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/..)
set(LLVM_LINK_COMPONENTS
Support
)
add_clang_executable(clang-change-namespace
ClangChangeNamespace.cpp
)
target_link_libraries(clang-change-namespace
clangAST
clangASTMatchers
clangBasic
clangChangeNamespace
clangFormat
clangFrontend
clangRewrite
clangTooling
clangToolingCore
)
install(TARGETS clang-change-namespace
RUNTIME DESTINATION bin)

View File

@@ -1,178 +0,0 @@
//===-- ClangIncludeFixer.cpp - Standalone change namespace ---------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
// This tool can be used to change the surrounding namespaces of class/function
// definitions.
//
// Example: test.cc
// namespace na {
// class X {};
// namespace nb {
// class Y { X x; };
// } // namespace nb
// } // namespace na
// To move the definition of class Y from namespace "na::nb" to "x::y", run:
// clang-change-namespace --old_namespace "na::nb" \
// --new_namespace "x::y" --file_pattern "test.cc" test.cc --
// Output:
// namespace na {
// class X {};
// } // namespace na
// namespace x {
// namespace y {
// class Y { na::X x; };
// } // namespace y
// } // namespace x
#include "ChangeNamespace.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/Frontend/FrontendActions.h"
#include "clang/Frontend/TextDiagnosticPrinter.h"
#include "clang/Rewrite/Core/Rewriter.h"
#include "clang/Tooling/CommonOptionsParser.h"
#include "clang/Tooling/Refactoring.h"
#include "clang/Tooling/Tooling.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Signals.h"
#include "llvm/Support/YAMLTraits.h"
using namespace clang;
using namespace llvm;
namespace {
cl::OptionCategory ChangeNamespaceCategory("Change namespace.");
cl::opt<std::string> OldNamespace("old_namespace", cl::Required,
cl::desc("Old namespace."),
cl::cat(ChangeNamespaceCategory));
cl::opt<std::string> NewNamespace("new_namespace", cl::Required,
cl::desc("New namespace."),
cl::cat(ChangeNamespaceCategory));
cl::opt<std::string> FilePattern(
"file_pattern", cl::Required,
cl::desc("Only rename namespaces in files that match the given pattern."),
cl::cat(ChangeNamespaceCategory));
cl::opt<bool> Inplace("i", cl::desc("Inplace edit <file>s, if specified."),
cl::cat(ChangeNamespaceCategory));
cl::opt<bool>
DumpYAML("dump_result",
cl::desc("Dump new file contents in YAML, if specified."),
cl::cat(ChangeNamespaceCategory));
cl::opt<std::string> Style("style",
cl::desc("The style name used for reformatting."),
cl::init("LLVM"), cl::cat(ChangeNamespaceCategory));
cl::opt<std::string> WhiteListFile(
"whitelist_file",
cl::desc("A file containing regexes of symbol names that are not expected "
"to be updated when changing namespaces around them."),
cl::init(""), cl::cat(ChangeNamespaceCategory));
llvm::ErrorOr<std::vector<std::string>> GetWhiteListedSymbolPatterns() {
std::vector<std::string> Patterns;
if (WhiteListFile.empty())
return Patterns;
llvm::SmallVector<StringRef, 8> Lines;
llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> File =
llvm::MemoryBuffer::getFile(WhiteListFile);
if (!File)
return File.getError();
llvm::StringRef Content = File.get()->getBuffer();
Content.split(Lines, '\n', /*MaxSplit=*/-1, /*KeepEmpty=*/false);
for (auto Line : Lines)
Patterns.push_back(Line.trim());
return Patterns;
}
} // anonymous namespace
int main(int argc, const char **argv) {
llvm::sys::PrintStackTraceOnErrorSignal(argv[0]);
tooling::CommonOptionsParser OptionsParser(argc, argv,
ChangeNamespaceCategory);
const auto &Files = OptionsParser.getSourcePathList();
tooling::RefactoringTool Tool(OptionsParser.getCompilations(), Files);
llvm::ErrorOr<std::vector<std::string>> WhiteListPatterns =
GetWhiteListedSymbolPatterns();
if (!WhiteListPatterns) {
llvm::errs() << "Failed to open whitelist file " << WhiteListFile << ". "
<< WhiteListPatterns.getError().message() << "\n";
return 1;
}
change_namespace::ChangeNamespaceTool NamespaceTool(
OldNamespace, NewNamespace, FilePattern, *WhiteListPatterns,
&Tool.getReplacements(), Style);
ast_matchers::MatchFinder Finder;
NamespaceTool.registerMatchers(&Finder);
std::unique_ptr<tooling::FrontendActionFactory> Factory =
tooling::newFrontendActionFactory(&Finder);
if (int Result = Tool.run(Factory.get()))
return Result;
LangOptions DefaultLangOptions;
IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions();
clang::TextDiagnosticPrinter DiagnosticPrinter(errs(), &*DiagOpts);
DiagnosticsEngine Diagnostics(
IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs()), &*DiagOpts,
&DiagnosticPrinter, false);
auto &FileMgr = Tool.getFiles();
SourceManager Sources(Diagnostics, FileMgr);
Rewriter Rewrite(Sources, DefaultLangOptions);
if (!formatAndApplyAllReplacements(Tool.getReplacements(), Rewrite, Style)) {
llvm::errs() << "Failed applying all replacements.\n";
return 1;
}
if (Inplace)
return Rewrite.overwriteChangedFiles();
std::set<llvm::StringRef> ChangedFiles;
for (const auto &it : Tool.getReplacements())
ChangedFiles.insert(it.first);
if (DumpYAML) {
auto WriteToYAML = [&](llvm::raw_ostream &OS) {
OS << "[\n";
for (auto I = ChangedFiles.begin(), E = ChangedFiles.end(); I != E; ++I) {
OS << " {\n";
OS << " \"FilePath\": \"" << *I << "\",\n";
const auto *Entry = FileMgr.getFile(*I);
auto ID = Sources.getOrCreateFileID(Entry, SrcMgr::C_User);
std::string Content;
llvm::raw_string_ostream ContentStream(Content);
Rewrite.getEditBuffer(ID).write(ContentStream);
OS << " \"SourceText\": \""
<< llvm::yaml::escape(ContentStream.str()) << "\"\n";
OS << " }";
if (I != std::prev(E))
OS << ",\n";
}
OS << "\n]\n";
};
WriteToYAML(llvm::outs());
return 0;
}
for (const auto &File : ChangedFiles) {
const auto *Entry = FileMgr.getFile(File);
auto ID = Sources.getOrCreateFileID(Entry, SrcMgr::C_User);
outs() << "============== " << File << " ==============\n";
Rewrite.getEditBuffer(ID).write(llvm::outs());
outs() << "\n============================================\n";
}
return 0;
}

View File

@@ -1,19 +0,0 @@
set(LLVM_LINK_COMPONENTS
Support
)
add_clang_library(clangApplyReplacements
lib/Tooling/ApplyReplacements.cpp
LINK_LIBS
clangAST
clangBasic
clangRewrite
clangToolingCore
)
include_directories(
${CMAKE_CURRENT_SOURCE_DIR}
include
)
add_subdirectory(tool)

View File

@@ -1,155 +0,0 @@
//===-- ApplyReplacements.h - Deduplicate and apply replacements -- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
///
/// \file
/// \brief This file provides the interface for deduplicating, detecting
/// conflicts in, and applying collections of Replacements.
///
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_APPLYREPLACEMENTS_H
#define LLVM_CLANG_APPLYREPLACEMENTS_H
#include "clang/Tooling/Core/Diagnostic.h"
#include "clang/Tooling/Refactoring.h"
#include "llvm/ADT/StringMap.h"
#include "llvm/ADT/StringRef.h"
#include <string>
#include <system_error>
#include <vector>
namespace clang {
class DiagnosticsEngine;
class Rewriter;
namespace format {
struct FormatStyle;
} // end namespace format
namespace replace {
/// \brief Collection of source ranges.
typedef std::vector<clang::tooling::Range> RangeVector;
/// \brief Collection of TranslationUnitReplacements.
typedef std::vector<clang::tooling::TranslationUnitReplacements> TUReplacements;
/// \brief Collection of TranslationUnitReplacement files.
typedef std::vector<std::string> TUReplacementFiles;
/// \brief Collection of TranslationUniDiagnostics.
typedef std::vector<clang::tooling::TranslationUnitDiagnostics> TUDiagnostics;
/// \brief Map mapping file name to Replacements targeting that file.
typedef llvm::DenseMap<const clang::FileEntry *,
std::vector<clang::tooling::Replacement>>
FileToReplacementsMap;
/// \brief Recursively descends through a directory structure rooted at \p
/// Directory and attempts to deserialize *.yaml files as
/// TranslationUnitReplacements. All docs that successfully deserialize are
/// added to \p TUs.
///
/// Directories starting with '.' are ignored during traversal.
///
/// \param[in] Directory Directory to begin search for serialized
/// TranslationUnitReplacements.
/// \param[out] TUs Collection of all found and deserialized
/// TranslationUnitReplacements or TranslationUnitDiagnostics.
/// \param[out] TUFiles Collection of all TranslationUnitReplacement files
/// found in \c Directory.
/// \param[in] Diagnostics DiagnosticsEngine used for error output.
///
/// \returns An error_code indicating success or failure in navigating the
/// directory structure.
std::error_code collectReplacementsFromDirectory(
const llvm::StringRef Directory, TUReplacements &TUs,
TUReplacementFiles &TUFiles, clang::DiagnosticsEngine &Diagnostics);
std::error_code collectReplacementsFromDirectory(
const llvm::StringRef Directory, TUDiagnostics &TUs,
TUReplacementFiles &TUFiles, clang::DiagnosticsEngine &Diagnostics);
/// \brief Deduplicate, check for conflicts, and apply all Replacements stored
/// in \c TUs. If conflicts occur, no Replacements are applied.
///
/// \post For all (key,value) in GroupedReplacements, value[i].getOffset() <=
/// value[i+1].getOffset().
///
/// \param[in] TUs Collection of TranslationUnitReplacements or
/// TranslationUnitDiagnostics to merge,
/// deduplicate, and test for conflicts.
/// \param[out] GroupedReplacements Container grouping all Replacements by the
/// file they target.
/// \param[in] SM SourceManager required for conflict reporting.
///
/// \returns \parblock
/// \li true If all changes were applied successfully.
/// \li false If there were conflicts.
bool mergeAndDeduplicate(const TUReplacements &TUs,
FileToReplacementsMap &GroupedReplacements,
clang::SourceManager &SM);
bool mergeAndDeduplicate(const TUDiagnostics &TUs,
FileToReplacementsMap &GroupedReplacements,
clang::SourceManager &SM);
// FIXME: Remove this function after changing clang-apply-replacements to use
// Replacements class.
bool applyAllReplacements(const std::vector<tooling::Replacement> &Replaces,
Rewriter &Rewrite);
/// \brief Apply all replacements in \c GroupedReplacements.
///
/// \param[in] GroupedReplacements Deduplicated and conflict free Replacements
/// to apply.
/// \param[out] Rewrites The results of applying replacements will be applied
/// to this Rewriter.
///
/// \returns \parblock
/// \li true If all changes were applied successfully.
/// \li false If a replacement failed to apply.
bool applyReplacements(const FileToReplacementsMap &GroupedReplacements,
clang::Rewriter &Rewrites);
/// \brief Given a collection of Replacements for a single file, produces a list
/// of source ranges that enclose those Replacements.
///
/// \pre Replacements[i].getOffset() <= Replacements[i+1].getOffset().
///
/// \param[in] Replacements Replacements from a single file.
///
/// \returns Collection of source ranges that enclose all given Replacements.
/// One range is created for each replacement.
RangeVector calculateChangedRanges(
const std::vector<clang::tooling::Replacement> &Replacements);
/// \brief Write the contents of \c FileContents to disk. Keys of the map are
/// filenames and values are the new contents for those files.
///
/// \param[in] Rewrites Rewriter containing written files to write to disk.
bool writeFiles(const clang::Rewriter &Rewrites);
/// \brief Delete the replacement files.
///
/// \param[in] Files Replacement files to delete.
/// \param[in] Diagnostics DiagnosticsEngine used for error output.
///
/// \returns \parblock
/// \li true If all files have been deleted successfully.
/// \li false If at least one or more failures occur when deleting
/// files.
bool deleteReplacementFiles(const TUReplacementFiles &Files,
clang::DiagnosticsEngine &Diagnostics);
} // end namespace replace
} // end namespace clang
#endif // LLVM_CLANG_APPLYREPLACEMENTS_H

View File

@@ -1,402 +0,0 @@
//===-- ApplyReplacements.cpp - Apply and deduplicate replacements --------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
///
/// \file
/// \brief This file provides the implementation for deduplicating, detecting
/// conflicts in, and applying collections of Replacements.
///
/// FIXME: Use Diagnostics for output instead of llvm::errs().
///
//===----------------------------------------------------------------------===//
#include "clang-apply-replacements/Tooling/ApplyReplacements.h"
#include "clang/Basic/LangOptions.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Format/Format.h"
#include "clang/Lex/Lexer.h"
#include "clang/Rewrite/Core/Rewriter.h"
#include "clang/Tooling/DiagnosticsYaml.h"
#include "clang/Tooling/ReplacementsYaml.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/raw_ostream.h"
using namespace llvm;
using namespace clang;
static void eatDiagnostics(const SMDiagnostic &, void *) {}
namespace clang {
namespace replace {
std::error_code collectReplacementsFromDirectory(
const llvm::StringRef Directory, TUReplacements &TUs,
TUReplacementFiles &TUFiles, clang::DiagnosticsEngine &Diagnostics) {
using namespace llvm::sys::fs;
using namespace llvm::sys::path;
std::error_code ErrorCode;
for (recursive_directory_iterator I(Directory, ErrorCode), E;
I != E && !ErrorCode; I.increment(ErrorCode)) {
if (filename(I->path())[0] == '.') {
// Indicate not to descend into directories beginning with '.'
I.no_push();
continue;
}
if (extension(I->path()) != ".yaml")
continue;
TUFiles.push_back(I->path());
ErrorOr<std::unique_ptr<MemoryBuffer>> Out =
MemoryBuffer::getFile(I->path());
if (std::error_code BufferError = Out.getError()) {
errs() << "Error reading " << I->path() << ": " << BufferError.message()
<< "\n";
continue;
}
yaml::Input YIn(Out.get()->getBuffer(), nullptr, &eatDiagnostics);
tooling::TranslationUnitReplacements TU;
YIn >> TU;
if (YIn.error()) {
// File doesn't appear to be a header change description. Ignore it.
continue;
}
// Only keep files that properly parse.
TUs.push_back(TU);
}
return ErrorCode;
}
std::error_code
collectReplacementsFromDirectory(const llvm::StringRef Directory,
TUDiagnostics &TUs, TUReplacementFiles &TUFiles,
clang::DiagnosticsEngine &Diagnostics) {
using namespace llvm::sys::fs;
using namespace llvm::sys::path;
std::error_code ErrorCode;
for (recursive_directory_iterator I(Directory, ErrorCode), E;
I != E && !ErrorCode; I.increment(ErrorCode)) {
if (filename(I->path())[0] == '.') {
// Indicate not to descend into directories beginning with '.'
I.no_push();
continue;
}
if (extension(I->path()) != ".yaml")
continue;
TUFiles.push_back(I->path());
ErrorOr<std::unique_ptr<MemoryBuffer>> Out =
MemoryBuffer::getFile(I->path());
if (std::error_code BufferError = Out.getError()) {
errs() << "Error reading " << I->path() << ": " << BufferError.message()
<< "\n";
continue;
}
yaml::Input YIn(Out.get()->getBuffer(), nullptr, &eatDiagnostics);
tooling::TranslationUnitDiagnostics TU;
YIn >> TU;
if (YIn.error()) {
// File doesn't appear to be a header change description. Ignore it.
continue;
}
// Only keep files that properly parse.
TUs.push_back(TU);
}
return ErrorCode;
}
/// \brief Dumps information for a sequence of conflicting Replacements.
///
/// \param[in] File FileEntry for the file the conflicting Replacements are
/// for.
/// \param[in] ConflictingReplacements List of conflicting Replacements.
/// \param[in] SM SourceManager used for reporting.
static void reportConflict(
const FileEntry *File,
const llvm::ArrayRef<clang::tooling::Replacement> ConflictingReplacements,
SourceManager &SM) {
FileID FID = SM.translateFile(File);
if (FID.isInvalid())
FID = SM.createFileID(File, SourceLocation(), SrcMgr::C_User);
// FIXME: Output something a little more user-friendly (e.g. unified diff?)
errs() << "The following changes conflict:\n";
for (const tooling::Replacement &R : ConflictingReplacements) {
if (R.getLength() == 0) {
errs() << " Insert at " << SM.getLineNumber(FID, R.getOffset()) << ":"
<< SM.getColumnNumber(FID, R.getOffset()) << " "
<< R.getReplacementText() << "\n";
} else {
if (R.getReplacementText().empty())
errs() << " Remove ";
else
errs() << " Replace ";
errs() << SM.getLineNumber(FID, R.getOffset()) << ":"
<< SM.getColumnNumber(FID, R.getOffset()) << "-"
<< SM.getLineNumber(FID, R.getOffset() + R.getLength() - 1) << ":"
<< SM.getColumnNumber(FID, R.getOffset() + R.getLength() - 1);
if (R.getReplacementText().empty())
errs() << "\n";
else
errs() << " with \"" << R.getReplacementText() << "\"\n";
}
}
}
// FIXME: Remove this function after changing clang-apply-replacements to use
// Replacements class.
bool applyAllReplacements(const std::vector<tooling::Replacement> &Replaces,
Rewriter &Rewrite) {
bool Result = true;
for (auto I = Replaces.begin(), E = Replaces.end(); I != E; ++I) {
if (I->isApplicable()) {
Result = I->apply(Rewrite) && Result;
} else {
Result = false;
}
}
return Result;
}
// FIXME: moved from libToolingCore. remove this when std::vector<Replacement>
// is replaced with tooling::Replacements class.
static void deduplicate(std::vector<tooling::Replacement> &Replaces,
std::vector<tooling::Range> &Conflicts) {
if (Replaces.empty())
return;
auto LessNoPath = [](const tooling::Replacement &LHS,
const tooling::Replacement &RHS) {
if (LHS.getOffset() != RHS.getOffset())
return LHS.getOffset() < RHS.getOffset();
if (LHS.getLength() != RHS.getLength())
return LHS.getLength() < RHS.getLength();
return LHS.getReplacementText() < RHS.getReplacementText();
};
auto EqualNoPath = [](const tooling::Replacement &LHS,
const tooling::Replacement &RHS) {
return LHS.getOffset() == RHS.getOffset() &&
LHS.getLength() == RHS.getLength() &&
LHS.getReplacementText() == RHS.getReplacementText();
};
// Deduplicate. We don't want to deduplicate based on the path as we assume
// that all replacements refer to the same file (or are symlinks).
std::sort(Replaces.begin(), Replaces.end(), LessNoPath);
Replaces.erase(std::unique(Replaces.begin(), Replaces.end(), EqualNoPath),
Replaces.end());
// Detect conflicts
tooling::Range ConflictRange(Replaces.front().getOffset(),
Replaces.front().getLength());
unsigned ConflictStart = 0;
unsigned ConflictLength = 1;
for (unsigned i = 1; i < Replaces.size(); ++i) {
tooling::Range Current(Replaces[i].getOffset(), Replaces[i].getLength());
if (ConflictRange.overlapsWith(Current)) {
// Extend conflicted range
ConflictRange =
tooling::Range(ConflictRange.getOffset(),
std::max(ConflictRange.getLength(),
Current.getOffset() + Current.getLength() -
ConflictRange.getOffset()));
++ConflictLength;
} else {
if (ConflictLength > 1)
Conflicts.push_back(tooling::Range(ConflictStart, ConflictLength));
ConflictRange = Current;
ConflictStart = i;
ConflictLength = 1;
}
}
if (ConflictLength > 1)
Conflicts.push_back(tooling::Range(ConflictStart, ConflictLength));
}
/// \brief Deduplicates and tests for conflicts among the replacements for each
/// file in \c Replacements. Any conflicts found are reported.
///
/// \post Replacements[i].getOffset() <= Replacements[i+1].getOffset().
///
/// \param[in,out] Replacements Container of all replacements grouped by file
/// to be deduplicated and checked for conflicts.
/// \param[in] SM SourceManager required for conflict reporting.
///
/// \returns \parblock
/// \li true if conflicts were detected
/// \li false if no conflicts were detected
static bool deduplicateAndDetectConflicts(FileToReplacementsMap &Replacements,
SourceManager &SM) {
bool conflictsFound = false;
for (auto &FileAndReplacements : Replacements) {
const FileEntry *Entry = FileAndReplacements.first;
auto &Replacements = FileAndReplacements.second;
assert(Entry != nullptr && "No file entry!");
std::vector<tooling::Range> Conflicts;
deduplicate(FileAndReplacements.second, Conflicts);
if (Conflicts.empty())
continue;
conflictsFound = true;
errs() << "There are conflicting changes to " << Entry->getName() << ":\n";
for (const tooling::Range &Conflict : Conflicts) {
auto ConflictingReplacements = llvm::makeArrayRef(
&Replacements[Conflict.getOffset()], Conflict.getLength());
reportConflict(Entry, ConflictingReplacements, SM);
}
}
return conflictsFound;
}
bool mergeAndDeduplicate(const TUReplacements &TUs,
FileToReplacementsMap &GroupedReplacements,
clang::SourceManager &SM) {
// Group all replacements by target file.
std::set<StringRef> Warned;
for (const auto &TU : TUs) {
for (const tooling::Replacement &R : TU.Replacements) {
// Use the file manager to deduplicate paths. FileEntries are
// automatically canonicalized.
const FileEntry *Entry = SM.getFileManager().getFile(R.getFilePath());
if (!Entry && Warned.insert(R.getFilePath()).second) {
errs() << "Described file '" << R.getFilePath()
<< "' doesn't exist. Ignoring...\n";
continue;
}
GroupedReplacements[Entry].push_back(R);
}
}
// Ask clang to deduplicate and report conflicts.
return !deduplicateAndDetectConflicts(GroupedReplacements, SM);
}
bool mergeAndDeduplicate(const TUDiagnostics &TUs,
FileToReplacementsMap &GroupedReplacements,
clang::SourceManager &SM) {
// Group all replacements by target file.
std::set<StringRef> Warned;
for (const auto &TU : TUs) {
for (const auto &D : TU.Diagnostics) {
for (const auto &Fix : D.Fix) {
for (const tooling::Replacement &R : Fix.second) {
// Use the file manager to deduplicate paths. FileEntries are
// automatically canonicalized.
const FileEntry *Entry = SM.getFileManager().getFile(R.getFilePath());
if (!Entry && Warned.insert(R.getFilePath()).second) {
errs() << "Described file '" << R.getFilePath()
<< "' doesn't exist. Ignoring...\n";
continue;
}
GroupedReplacements[Entry].push_back(R);
}
}
}
}
// Ask clang to deduplicate and report conflicts.
return !deduplicateAndDetectConflicts(GroupedReplacements, SM);
}
bool applyReplacements(const FileToReplacementsMap &GroupedReplacements,
clang::Rewriter &Rewrites) {
// Apply all changes
//
// FIXME: No longer certain GroupedReplacements is really the best kind of
// data structure for applying replacements. Rewriter certainly doesn't care.
// However, until we nail down the design of ReplacementGroups, might as well
// leave this as is.
for (const auto &FileAndReplacements : GroupedReplacements) {
if (!applyAllReplacements(FileAndReplacements.second, Rewrites))
return false;
}
return true;
}
RangeVector calculateChangedRanges(
const std::vector<clang::tooling::Replacement> &Replaces) {
RangeVector ChangedRanges;
// Generate the new ranges from the replacements.
int Shift = 0;
for (const tooling::Replacement &R : Replaces) {
unsigned Offset = R.getOffset() + Shift;
unsigned Length = R.getReplacementText().size();
Shift += Length - R.getLength();
ChangedRanges.push_back(tooling::Range(Offset, Length));
}
return ChangedRanges;
}
bool writeFiles(const clang::Rewriter &Rewrites) {
for (auto BufferI = Rewrites.buffer_begin(), BufferE = Rewrites.buffer_end();
BufferI != BufferE; ++BufferI) {
StringRef FileName =
Rewrites.getSourceMgr().getFileEntryForID(BufferI->first)->getName();
std::error_code EC;
llvm::raw_fd_ostream FileStream(FileName, EC, llvm::sys::fs::F_Text);
if (EC) {
errs() << "Warning: Could not write to " << EC.message() << "\n";
continue;
}
BufferI->second.write(FileStream);
}
return true;
}
bool deleteReplacementFiles(const TUReplacementFiles &Files,
clang::DiagnosticsEngine &Diagnostics) {
bool Success = true;
for (const auto &Filename : Files) {
std::error_code Error = llvm::sys::fs::remove(Filename);
if (Error) {
Success = false;
// FIXME: Use Diagnostics for outputting errors.
errs() << "Error deleting file: " << Filename << "\n";
errs() << Error.message() << "\n";
errs() << "Please delete the file manually\n";
}
}
return Success;
}
} // end namespace replace
} // end namespace clang

View File

@@ -1,17 +0,0 @@
set(LLVM_LINK_COMPONENTS
Support
)
add_clang_executable(clang-apply-replacements
ClangApplyReplacementsMain.cpp
)
target_link_libraries(clang-apply-replacements
clangApplyReplacements
clangBasic
clangFormat
clangRewrite
clangToolingCore
)
install(TARGETS clang-apply-replacements
RUNTIME DESTINATION bin)

View File

@@ -1,290 +0,0 @@
//===-- ClangApplyReplacementsMain.cpp - Main file for the tool -----------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
///
/// \file
/// \brief This file provides the main function for the
/// clang-apply-replacements tool.
///
//===----------------------------------------------------------------------===//
#include "clang-apply-replacements/Tooling/ApplyReplacements.h"
#include "clang/Basic/Diagnostic.h"
#include "clang/Basic/DiagnosticOptions.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Basic/Version.h"
#include "clang/Format/Format.h"
#include "clang/Rewrite/Core/Rewriter.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/StringSet.h"
#include "llvm/Support/CommandLine.h"
using namespace llvm;
using namespace clang;
using namespace clang::replace;
static cl::opt<std::string> Directory(cl::Positional, cl::Required,
cl::desc("<Search Root Directory>"));
static cl::OptionCategory ReplacementCategory("Replacement Options");
static cl::OptionCategory FormattingCategory("Formatting Options");
const cl::OptionCategory *VisibleCategories[] = {&ReplacementCategory,
&FormattingCategory};
static cl::opt<bool> RemoveTUReplacementFiles(
"remove-change-desc-files",
cl::desc("Remove the change description files regardless of successful\n"
"merging/replacing."),
cl::init(false), cl::cat(ReplacementCategory));
static cl::opt<bool> DoFormat(
"format",
cl::desc("Enable formatting of code changed by applying replacements.\n"
"Use -style to choose formatting style.\n"),
cl::cat(FormattingCategory));
// FIXME: Consider making the default behaviour for finding a style
// configuration file to start the search anew for every file being changed to
// handle situations where the style is different for different parts of a
// project.
static cl::opt<std::string> FormatStyleConfig(
"style-config",
cl::desc("Path to a directory containing a .clang-format file\n"
"describing a formatting style to use for formatting\n"
"code when -style=file.\n"),
cl::init(""), cl::cat(FormattingCategory));
static cl::opt<std::string>
FormatStyleOpt("style", cl::desc(format::StyleOptionHelpDescription),
cl::init("LLVM"), cl::cat(FormattingCategory));
namespace {
// Helper object to remove the TUReplacement and TUDiagnostic (triggered by
// "remove-change-desc-files" command line option) when exiting current scope.
class ScopedFileRemover {
public:
ScopedFileRemover(const TUReplacementFiles &Files,
clang::DiagnosticsEngine &Diagnostics)
: TURFiles(Files), Diag(Diagnostics) {}
~ScopedFileRemover() { deleteReplacementFiles(TURFiles, Diag); }
private:
const TUReplacementFiles &TURFiles;
clang::DiagnosticsEngine &Diag;
};
} // namespace
static void printVersion() {
outs() << "clang-apply-replacements version " CLANG_VERSION_STRING << "\n";
}
/// \brief Convenience function to get rewritten content for \c Filename from
/// \c Rewrites.
///
/// \pre Replacements[i].getFilePath() == Replacements[i+1].getFilePath().
/// \post Replacements.empty() -> Result.empty()
///
/// \param[in] Replacements Replacements to apply
/// \param[in] Rewrites Rewriter to use to apply replacements.
/// \param[out] Result Contents of the file after applying replacements if
/// replacements were provided.
///
/// \returns \parblock
/// \li true if all replacements were applied successfully.
/// \li false if at least one replacement failed to apply.
static bool
getRewrittenData(const std::vector<tooling::Replacement> &Replacements,
Rewriter &Rewrites, std::string &Result) {
if (Replacements.empty())
return true;
if (!applyAllReplacements(Replacements, Rewrites))
return false;
SourceManager &SM = Rewrites.getSourceMgr();
FileManager &Files = SM.getFileManager();
StringRef FileName = Replacements.begin()->getFilePath();
const clang::FileEntry *Entry = Files.getFile(FileName);
assert(Entry && "Expected an existing file");
FileID ID = SM.translateFile(Entry);
assert(ID.isValid() && "Expected a valid FileID");
const RewriteBuffer *Buffer = Rewrites.getRewriteBufferFor(ID);
Result = std::string(Buffer->begin(), Buffer->end());
return true;
}
/// \brief Apply \c Replacements and return the new file contents.
///
/// \pre Replacements[i].getFilePath() == Replacements[i+1].getFilePath().
/// \post Replacements.empty() -> Result.empty()
///
/// \param[in] Replacements Replacements to apply.
/// \param[out] Result Contents of the file after applying replacements if
/// replacements were provided.
/// \param[in] Diagnostics For diagnostic output.
///
/// \returns \parblock
/// \li true if all replacements applied successfully.
/// \li false if at least one replacement failed to apply.
static bool
applyReplacements(const std::vector<tooling::Replacement> &Replacements,
std::string &Result, DiagnosticsEngine &Diagnostics) {
FileManager Files((FileSystemOptions()));
SourceManager SM(Diagnostics, Files);
Rewriter Rewrites(SM, LangOptions());
return getRewrittenData(Replacements, Rewrites, Result);
}
/// \brief Apply code formatting to all places where replacements were made.
///
/// \pre !Replacements.empty().
/// \pre Replacements[i].getFilePath() == Replacements[i+1].getFilePath().
/// \pre Replacements[i].getOffset() <= Replacements[i+1].getOffset().
///
/// \param[in] Replacements Replacements that were made to the file. Provided
/// to indicate where changes were made.
/// \param[in] FileData The contents of the file \b after \c Replacements have
/// been applied.
/// \param[out] FormattedFileData The contents of the file after reformatting.
/// \param[in] FormatStyle Style to apply.
/// \param[in] Diagnostics For diagnostic output.
///
/// \returns \parblock
/// \li true if reformatting replacements were all successfully
/// applied.
/// \li false if at least one reformatting replacement failed to apply.
static bool
applyFormatting(const std::vector<tooling::Replacement> &Replacements,
const StringRef FileData, std::string &FormattedFileData,
const format::FormatStyle &FormatStyle,
DiagnosticsEngine &Diagnostics) {
assert(!Replacements.empty() && "Need at least one replacement");
RangeVector Ranges = calculateChangedRanges(Replacements);
StringRef FileName = Replacements.begin()->getFilePath();
tooling::Replacements R =
format::reformat(FormatStyle, FileData, Ranges, FileName);
// FIXME: Remove this copy when tooling::Replacements is implemented as a
// vector instead of a set.
std::vector<tooling::Replacement> FormattingReplacements;
std::copy(R.begin(), R.end(), back_inserter(FormattingReplacements));
if (FormattingReplacements.empty()) {
FormattedFileData = FileData;
return true;
}
FileManager Files((FileSystemOptions()));
SourceManager SM(Diagnostics, Files);
SM.overrideFileContents(Files.getFile(FileName),
llvm::MemoryBuffer::getMemBufferCopy(FileData));
Rewriter Rewrites(SM, LangOptions());
return getRewrittenData(FormattingReplacements, Rewrites, FormattedFileData);
}
int main(int argc, char **argv) {
cl::HideUnrelatedOptions(makeArrayRef(VisibleCategories));
cl::SetVersionPrinter(&printVersion);
cl::ParseCommandLineOptions(argc, argv);
IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts(new DiagnosticOptions());
DiagnosticsEngine Diagnostics(
IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs()), DiagOpts.get());
// Determine a formatting style from options.
format::FormatStyle FormatStyle;
if (DoFormat) {
auto FormatStyleOrError =
format::getStyle(FormatStyleOpt, FormatStyleConfig, "LLVM");
if (!FormatStyleOrError) {
llvm::errs() << llvm::toString(FormatStyleOrError.takeError()) << "\n";
return 1;
}
FormatStyle = *FormatStyleOrError;
}
TUReplacements TURs;
TUReplacementFiles TUFiles;
std::error_code ErrorCode =
collectReplacementsFromDirectory(Directory, TURs, TUFiles, Diagnostics);
TUDiagnostics TUDs;
TUFiles.clear();
ErrorCode =
collectReplacementsFromDirectory(Directory, TUDs, TUFiles, Diagnostics);
if (ErrorCode) {
errs() << "Trouble iterating over directory '" << Directory
<< "': " << ErrorCode.message() << "\n";
return 1;
}
// Remove the TUReplacementFiles (triggered by "remove-change-desc-files"
// command line option) when exiting main().
std::unique_ptr<ScopedFileRemover> Remover;
if (RemoveTUReplacementFiles)
Remover.reset(new ScopedFileRemover(TUFiles, Diagnostics));
FileManager Files((FileSystemOptions()));
SourceManager SM(Diagnostics, Files);
FileToReplacementsMap GroupedReplacements;
if (!mergeAndDeduplicate(TURs, GroupedReplacements, SM))
return 1;
if (!mergeAndDeduplicate(TUDs, GroupedReplacements, SM))
return 1;
Rewriter ReplacementsRewriter(SM, LangOptions());
for (const auto &FileAndReplacements : GroupedReplacements) {
// This shouldn't happen but if a file somehow has no replacements skip to
// next file.
if (FileAndReplacements.second.empty())
continue;
std::string NewFileData;
StringRef FileName = FileAndReplacements.first->getName();
if (!applyReplacements(FileAndReplacements.second, NewFileData,
Diagnostics)) {
errs() << "Failed to apply replacements to " << FileName << "\n";
continue;
}
// Apply formatting if requested.
if (DoFormat &&
!applyFormatting(FileAndReplacements.second, NewFileData, NewFileData,
FormatStyle, Diagnostics)) {
errs() << "Failed to apply reformatting replacements for " << FileName
<< "\n";
continue;
}
// Write new file to disk
std::error_code EC;
llvm::raw_fd_ostream FileStream(FileName, EC, llvm::sys::fs::F_None);
if (EC) {
llvm::errs() << "Could not open " << FileName << " for writing\n";
continue;
}
FileStream << NewFileData;
}
return 0;
}

View File

@@ -1,21 +0,0 @@
set(LLVM_LINK_COMPONENTS
support
)
add_clang_library(clangMove
ClangMove.cpp
HelperDeclRefGraph.cpp
LINK_LIBS
clangAnalysis
clangAST
clangASTMatchers
clangBasic
clangFormat
clangFrontend
clangLex
clangTooling
clangToolingCore
)
add_subdirectory(tool)

View File

@@ -1,931 +0,0 @@
//===-- ClangMove.cpp - Implement ClangMove functationalities ---*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "ClangMove.h"
#include "HelperDeclRefGraph.h"
#include "clang/ASTMatchers/ASTMatchers.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Format/Format.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Lex/Lexer.h"
#include "clang/Lex/Preprocessor.h"
#include "clang/Rewrite/Core/Rewriter.h"
#include "clang/Tooling/Core/Replacement.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/Path.h"
#define DEBUG_TYPE "clang-move"
using namespace clang::ast_matchers;
namespace clang {
namespace move {
namespace {
// FIXME: Move to ASTMatchers.
AST_MATCHER(VarDecl, isStaticDataMember) { return Node.isStaticDataMember(); }
AST_MATCHER(NamedDecl, notInMacro) { return !Node.getLocation().isMacroID(); }
AST_MATCHER_P(Decl, hasOutermostEnclosingClass,
ast_matchers::internal::Matcher<Decl>, InnerMatcher) {
const auto *Context = Node.getDeclContext();
if (!Context)
return false;
while (const auto *NextContext = Context->getParent()) {
if (isa<NamespaceDecl>(NextContext) ||
isa<TranslationUnitDecl>(NextContext))
break;
Context = NextContext;
}
return InnerMatcher.matches(*Decl::castFromDeclContext(Context), Finder,
Builder);
}
AST_MATCHER_P(CXXMethodDecl, ofOutermostEnclosingClass,
ast_matchers::internal::Matcher<CXXRecordDecl>, InnerMatcher) {
const CXXRecordDecl *Parent = Node.getParent();
if (!Parent)
return false;
while (const auto *NextParent =
dyn_cast<CXXRecordDecl>(Parent->getParent())) {
Parent = NextParent;
}
return InnerMatcher.matches(*Parent, Finder, Builder);
}
// Make the Path absolute using the CurrentDir if the Path is not an absolute
// path. An empty Path will result in an empty string.
std::string MakeAbsolutePath(StringRef CurrentDir, StringRef Path) {
if (Path.empty())
return "";
llvm::SmallString<128> InitialDirectory(CurrentDir);
llvm::SmallString<128> AbsolutePath(Path);
if (std::error_code EC =
llvm::sys::fs::make_absolute(InitialDirectory, AbsolutePath))
llvm::errs() << "Warning: could not make absolute file: '" << EC.message()
<< '\n';
llvm::sys::path::remove_dots(AbsolutePath, /*remove_dot_dot=*/true);
llvm::sys::path::native(AbsolutePath);
return AbsolutePath.str();
}
// Make the Path absolute using the current working directory of the given
// SourceManager if the Path is not an absolute path.
//
// The Path can be a path relative to the build directory, or retrieved from
// the SourceManager.
std::string MakeAbsolutePath(const SourceManager &SM, StringRef Path) {
llvm::SmallString<128> AbsolutePath(Path);
if (std::error_code EC =
SM.getFileManager().getVirtualFileSystem()->makeAbsolute(
AbsolutePath))
llvm::errs() << "Warning: could not make absolute file: '" << EC.message()
<< '\n';
// Handle symbolic link path cases.
// We are trying to get the real file path of the symlink.
const DirectoryEntry *Dir = SM.getFileManager().getDirectory(
llvm::sys::path::parent_path(AbsolutePath.str()));
if (Dir) {
StringRef DirName = SM.getFileManager().getCanonicalName(Dir);
SmallVector<char, 128> AbsoluteFilename;
llvm::sys::path::append(AbsoluteFilename, DirName,
llvm::sys::path::filename(AbsolutePath.str()));
return llvm::StringRef(AbsoluteFilename.data(), AbsoluteFilename.size())
.str();
}
return AbsolutePath.str();
}
// Matches AST nodes that are expanded within the given AbsoluteFilePath.
AST_POLYMORPHIC_MATCHER_P(isExpansionInFile,
AST_POLYMORPHIC_SUPPORTED_TYPES(Decl, Stmt, TypeLoc),
std::string, AbsoluteFilePath) {
auto &SourceManager = Finder->getASTContext().getSourceManager();
auto ExpansionLoc = SourceManager.getExpansionLoc(Node.getLocStart());
if (ExpansionLoc.isInvalid())
return false;
auto FileEntry =
SourceManager.getFileEntryForID(SourceManager.getFileID(ExpansionLoc));
if (!FileEntry)
return false;
return MakeAbsolutePath(SourceManager, FileEntry->getName()) ==
AbsoluteFilePath;
}
class FindAllIncludes : public clang::PPCallbacks {
public:
explicit FindAllIncludes(SourceManager *SM, ClangMoveTool *const MoveTool)
: SM(*SM), MoveTool(MoveTool) {}
void InclusionDirective(clang::SourceLocation HashLoc,
const clang::Token & /*IncludeTok*/,
StringRef FileName, bool IsAngled,
clang::CharSourceRange FilenameRange,
const clang::FileEntry * /*File*/,
StringRef SearchPath, StringRef /*RelativePath*/,
const clang::Module * /*Imported*/) override {
if (const auto *FileEntry = SM.getFileEntryForID(SM.getFileID(HashLoc)))
MoveTool->addIncludes(FileName, IsAngled, SearchPath,
FileEntry->getName(), FilenameRange, SM);
}
private:
const SourceManager &SM;
ClangMoveTool *const MoveTool;
};
/// Add a declatration being moved to new.h/cc. Note that the declaration will
/// also be deleted in old.h/cc.
void MoveDeclFromOldFileToNewFile(ClangMoveTool *MoveTool, const NamedDecl *D) {
MoveTool->getMovedDecls().push_back(D);
MoveTool->addRemovedDecl(D);
MoveTool->getUnremovedDeclsInOldHeader().erase(D);
}
class FunctionDeclarationMatch : public MatchFinder::MatchCallback {
public:
explicit FunctionDeclarationMatch(ClangMoveTool *MoveTool)
: MoveTool(MoveTool) {}
void run(const MatchFinder::MatchResult &Result) override {
const auto *FD = Result.Nodes.getNodeAs<clang::FunctionDecl>("function");
assert(FD);
const clang::NamedDecl *D = FD;
if (const auto *FTD = FD->getDescribedFunctionTemplate())
D = FTD;
MoveDeclFromOldFileToNewFile(MoveTool, D);
}
private:
ClangMoveTool *MoveTool;
};
class VarDeclarationMatch : public MatchFinder::MatchCallback {
public:
explicit VarDeclarationMatch(ClangMoveTool *MoveTool)
: MoveTool(MoveTool) {}
void run(const MatchFinder::MatchResult &Result) override {
const auto *VD = Result.Nodes.getNodeAs<clang::VarDecl>("var");
assert(VD);
MoveDeclFromOldFileToNewFile(MoveTool, VD);
}
private:
ClangMoveTool *MoveTool;
};
class TypeAliasMatch : public MatchFinder::MatchCallback {
public:
explicit TypeAliasMatch(ClangMoveTool *MoveTool)
: MoveTool(MoveTool) {}
void run(const MatchFinder::MatchResult &Result) override {
if (const auto *TD = Result.Nodes.getNodeAs<clang::TypedefDecl>("typedef"))
MoveDeclFromOldFileToNewFile(MoveTool, TD);
else if (const auto *TAD =
Result.Nodes.getNodeAs<clang::TypeAliasDecl>("type_alias")) {
const NamedDecl * D = TAD;
if (const auto * TD = TAD->getDescribedAliasTemplate())
D = TD;
MoveDeclFromOldFileToNewFile(MoveTool, D);
}
}
private:
ClangMoveTool *MoveTool;
};
class EnumDeclarationMatch : public MatchFinder::MatchCallback {
public:
explicit EnumDeclarationMatch(ClangMoveTool *MoveTool)
: MoveTool(MoveTool) {}
void run(const MatchFinder::MatchResult &Result) override {
const auto *ED = Result.Nodes.getNodeAs<clang::EnumDecl>("enum");
assert(ED);
MoveDeclFromOldFileToNewFile(MoveTool, ED);
}
private:
ClangMoveTool *MoveTool;
};
class ClassDeclarationMatch : public MatchFinder::MatchCallback {
public:
explicit ClassDeclarationMatch(ClangMoveTool *MoveTool)
: MoveTool(MoveTool) {}
void run(const MatchFinder::MatchResult &Result) override {
clang::SourceManager* SM = &Result.Context->getSourceManager();
if (const auto *CMD =
Result.Nodes.getNodeAs<clang::CXXMethodDecl>("class_method"))
MatchClassMethod(CMD, SM);
else if (const auto *VD = Result.Nodes.getNodeAs<clang::VarDecl>(
"class_static_var_decl"))
MatchClassStaticVariable(VD, SM);
else if (const auto *CD = Result.Nodes.getNodeAs<clang::CXXRecordDecl>(
"moved_class"))
MatchClassDeclaration(CD, SM);
}
private:
void MatchClassMethod(const clang::CXXMethodDecl* CMD,
clang::SourceManager* SM) {
// Skip inline class methods. isInline() ast matcher doesn't ignore this
// case.
if (!CMD->isInlined()) {
MoveTool->getMovedDecls().push_back(CMD);
MoveTool->addRemovedDecl(CMD);
// Get template class method from its method declaration as
// UnremovedDecls stores template class method.
if (const auto *FTD = CMD->getDescribedFunctionTemplate())
MoveTool->getUnremovedDeclsInOldHeader().erase(FTD);
else
MoveTool->getUnremovedDeclsInOldHeader().erase(CMD);
}
}
void MatchClassStaticVariable(const clang::NamedDecl *VD,
clang::SourceManager* SM) {
MoveDeclFromOldFileToNewFile(MoveTool, VD);
}
void MatchClassDeclaration(const clang::CXXRecordDecl *CD,
clang::SourceManager* SM) {
// Get class template from its class declaration as UnremovedDecls stores
// class template.
if (const auto *TC = CD->getDescribedClassTemplate())
MoveTool->getMovedDecls().push_back(TC);
else
MoveTool->getMovedDecls().push_back(CD);
MoveTool->addRemovedDecl(MoveTool->getMovedDecls().back());
MoveTool->getUnremovedDeclsInOldHeader().erase(
MoveTool->getMovedDecls().back());
}
ClangMoveTool *MoveTool;
};
// Expand to get the end location of the line where the EndLoc of the given
// Decl.
SourceLocation
getLocForEndOfDecl(const clang::Decl *D,
const LangOptions &LangOpts = clang::LangOptions()) {
const auto &SM = D->getASTContext().getSourceManager();
auto EndExpansionLoc = SM.getExpansionLoc(D->getLocEnd());
std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(EndExpansionLoc);
// Try to load the file buffer.
bool InvalidTemp = false;
llvm::StringRef File = SM.getBufferData(LocInfo.first, &InvalidTemp);
if (InvalidTemp)
return SourceLocation();
const char *TokBegin = File.data() + LocInfo.second;
// Lex from the start of the given location.
Lexer Lex(SM.getLocForStartOfFile(LocInfo.first), LangOpts, File.begin(),
TokBegin, File.end());
llvm::SmallVector<char, 16> Line;
// FIXME: this is a bit hacky to get ReadToEndOfLine work.
Lex.setParsingPreprocessorDirective(true);
Lex.ReadToEndOfLine(&Line);
SourceLocation EndLoc = EndExpansionLoc.getLocWithOffset(Line.size());
// If we already reach EOF, just return the EOF SourceLocation;
// otherwise, move 1 offset ahead to include the trailing newline character
// '\n'.
return SM.getLocForEndOfFile(LocInfo.first) == EndLoc
? EndLoc
: EndLoc.getLocWithOffset(1);
}
// Get full range of a Decl including the comments associated with it.
clang::CharSourceRange
getFullRange(const clang::Decl *D,
const clang::LangOptions &options = clang::LangOptions()) {
const auto &SM = D->getASTContext().getSourceManager();
clang::SourceRange Full(SM.getExpansionLoc(D->getLocStart()),
getLocForEndOfDecl(D));
// Expand to comments that are associated with the Decl.
if (const auto *Comment = D->getASTContext().getRawCommentForDeclNoCache(D)) {
if (SM.isBeforeInTranslationUnit(Full.getEnd(), Comment->getLocEnd()))
Full.setEnd(Comment->getLocEnd());
// FIXME: Don't delete a preceding comment, if there are no other entities
// it could refer to.
if (SM.isBeforeInTranslationUnit(Comment->getLocStart(), Full.getBegin()))
Full.setBegin(Comment->getLocStart());
}
return clang::CharSourceRange::getCharRange(Full);
}
std::string getDeclarationSourceText(const clang::Decl *D) {
const auto &SM = D->getASTContext().getSourceManager();
llvm::StringRef SourceText =
clang::Lexer::getSourceText(getFullRange(D), SM, clang::LangOptions());
return SourceText.str();
}
bool isInHeaderFile(const clang::Decl *D,
llvm::StringRef OriginalRunningDirectory,
llvm::StringRef OldHeader) {
const auto &SM = D->getASTContext().getSourceManager();
if (OldHeader.empty())
return false;
auto ExpansionLoc = SM.getExpansionLoc(D->getLocStart());
if (ExpansionLoc.isInvalid())
return false;
if (const auto *FE = SM.getFileEntryForID(SM.getFileID(ExpansionLoc))) {
return MakeAbsolutePath(SM, FE->getName()) ==
MakeAbsolutePath(OriginalRunningDirectory, OldHeader);
}
return false;
}
std::vector<std::string> getNamespaces(const clang::Decl *D) {
std::vector<std::string> Namespaces;
for (const auto *Context = D->getDeclContext(); Context;
Context = Context->getParent()) {
if (llvm::isa<clang::TranslationUnitDecl>(Context) ||
llvm::isa<clang::LinkageSpecDecl>(Context))
break;
if (const auto *ND = llvm::dyn_cast<clang::NamespaceDecl>(Context))
Namespaces.push_back(ND->getName().str());
}
std::reverse(Namespaces.begin(), Namespaces.end());
return Namespaces;
}
clang::tooling::Replacements
createInsertedReplacements(const std::vector<std::string> &Includes,
const std::vector<const NamedDecl *> &Decls,
llvm::StringRef FileName, bool IsHeader = false,
StringRef OldHeaderInclude = "") {
std::string NewCode;
std::string GuardName(FileName);
if (IsHeader) {
for (size_t i = 0; i < GuardName.size(); ++i) {
if (!isAlphanumeric(GuardName[i]))
GuardName[i] = '_';
}
GuardName = StringRef(GuardName).upper();
NewCode += "#ifndef " + GuardName + "\n";
NewCode += "#define " + GuardName + "\n\n";
}
NewCode += OldHeaderInclude;
// Add #Includes.
for (const auto &Include : Includes)
NewCode += Include;
if (!Includes.empty())
NewCode += "\n";
// Add moved class definition and its related declarations. All declarations
// in same namespace are grouped together.
//
// Record namespaces where the current position is in.
std::vector<std::string> CurrentNamespaces;
for (const auto *MovedDecl : Decls) {
// The namespaces of the declaration being moved.
std::vector<std::string> DeclNamespaces = getNamespaces(MovedDecl);
auto CurrentIt = CurrentNamespaces.begin();
auto DeclIt = DeclNamespaces.begin();
// Skip the common prefix.
while (CurrentIt != CurrentNamespaces.end() &&
DeclIt != DeclNamespaces.end()) {
if (*CurrentIt != *DeclIt)
break;
++CurrentIt;
++DeclIt;
}
// Calculate the new namespaces after adding MovedDecl in CurrentNamespace,
// which is used for next iteration of this loop.
std::vector<std::string> NextNamespaces(CurrentNamespaces.begin(),
CurrentIt);
NextNamespaces.insert(NextNamespaces.end(), DeclIt, DeclNamespaces.end());
// End with CurrentNamespace.
bool HasEndCurrentNamespace = false;
auto RemainingSize = CurrentNamespaces.end() - CurrentIt;
for (auto It = CurrentNamespaces.rbegin(); RemainingSize > 0;
--RemainingSize, ++It) {
assert(It < CurrentNamespaces.rend());
NewCode += "} // namespace " + *It + "\n";
HasEndCurrentNamespace = true;
}
// Add trailing '\n' after the nested namespace definition.
if (HasEndCurrentNamespace)
NewCode += "\n";
// If the moved declaration is not in CurrentNamespace, add extra namespace
// definitions.
bool IsInNewNamespace = false;
while (DeclIt != DeclNamespaces.end()) {
NewCode += "namespace " + *DeclIt + " {\n";
IsInNewNamespace = true;
++DeclIt;
}
// If the moved declaration is in same namespace CurrentNamespace, add
// a preceeding `\n' before the moved declaration.
// FIXME: Don't add empty lines between using declarations.
if (!IsInNewNamespace)
NewCode += "\n";
NewCode += getDeclarationSourceText(MovedDecl);
CurrentNamespaces = std::move(NextNamespaces);
}
std::reverse(CurrentNamespaces.begin(), CurrentNamespaces.end());
for (const auto &NS : CurrentNamespaces)
NewCode += "} // namespace " + NS + "\n";
if (IsHeader)
NewCode += "\n#endif // " + GuardName + "\n";
return clang::tooling::Replacements(
clang::tooling::Replacement(FileName, 0, 0, NewCode));
}
// Return a set of all decls which are used/referenced by the given Decls.
// Specically, given a class member declaration, this method will return all
// decls which are used by the whole class.
llvm::DenseSet<const Decl *>
getUsedDecls(const HelperDeclRefGraph *RG,
const std::vector<const NamedDecl *> &Decls) {
assert(RG);
llvm::DenseSet<const CallGraphNode *> Nodes;
for (const auto *D : Decls) {
auto Result = RG->getReachableNodes(
HelperDeclRGBuilder::getOutmostClassOrFunDecl(D));
Nodes.insert(Result.begin(), Result.end());
}
llvm::DenseSet<const Decl *> Results;
for (const auto *Node : Nodes)
Results.insert(Node->getDecl());
return Results;
}
} // namespace
std::unique_ptr<clang::ASTConsumer>
ClangMoveAction::CreateASTConsumer(clang::CompilerInstance &Compiler,
StringRef /*InFile*/) {
Compiler.getPreprocessor().addPPCallbacks(llvm::make_unique<FindAllIncludes>(
&Compiler.getSourceManager(), &MoveTool));
return MatchFinder.newASTConsumer();
}
ClangMoveTool::ClangMoveTool(ClangMoveContext *const Context,
DeclarationReporter *const Reporter)
: Context(Context), Reporter(Reporter) {
if (!Context->Spec.NewHeader.empty())
CCIncludes.push_back("#include \"" + Context->Spec.NewHeader + "\"\n");
}
void ClangMoveTool::addRemovedDecl(const NamedDecl *Decl) {
const auto &SM = Decl->getASTContext().getSourceManager();
auto Loc = Decl->getLocation();
StringRef FilePath = SM.getFilename(Loc);
FilePathToFileID[FilePath] = SM.getFileID(Loc);
RemovedDecls.push_back(Decl);
}
void ClangMoveTool::registerMatchers(ast_matchers::MatchFinder *Finder) {
auto InOldHeader =
isExpansionInFile(makeAbsolutePath(Context->Spec.OldHeader));
auto InOldCC = isExpansionInFile(makeAbsolutePath(Context->Spec.OldCC));
auto InOldFiles = anyOf(InOldHeader, InOldCC);
auto classTemplateForwardDecls =
classTemplateDecl(unless(has(cxxRecordDecl(isDefinition()))));
auto ForwardClassDecls = namedDecl(
anyOf(cxxRecordDecl(unless(anyOf(isImplicit(), isDefinition()))),
classTemplateForwardDecls));
auto TopLevelDecl =
hasDeclContext(anyOf(namespaceDecl(), translationUnitDecl()));
//============================================================================
// Matchers for old header
//============================================================================
// Match all top-level named declarations (e.g. function, variable, enum) in
// old header, exclude forward class declarations and namespace declarations.
//
// We consider declarations inside a class belongs to the class. So these
// declarations will be ignored.
auto AllDeclsInHeader = namedDecl(
unless(ForwardClassDecls), unless(namespaceDecl()),
unless(usingDirectiveDecl()), // using namespace decl.
InOldHeader,
hasParent(decl(anyOf(namespaceDecl(), translationUnitDecl()))),
hasDeclContext(decl(anyOf(namespaceDecl(), translationUnitDecl()))));
Finder->addMatcher(AllDeclsInHeader.bind("decls_in_header"), this);
// Don't register other matchers when dumping all declarations in header.
if (Context->DumpDeclarations)
return;
// Match forward declarations in old header.
Finder->addMatcher(namedDecl(ForwardClassDecls, InOldHeader).bind("fwd_decl"),
this);
//============================================================================
// Matchers for old cc
//============================================================================
auto IsOldCCTopLevelDecl = allOf(
hasParent(decl(anyOf(namespaceDecl(), translationUnitDecl()))), InOldCC);
// Matching using decls/type alias decls which are in named/anonymous/global
// namespace, these decls are always copied to new.h/cc. Those in classes,
// functions are covered in other matchers.
Finder->addMatcher(namedDecl(anyOf(usingDecl(IsOldCCTopLevelDecl),
usingDirectiveDecl(IsOldCCTopLevelDecl),
typeAliasDecl(IsOldCCTopLevelDecl)),
notInMacro())
.bind("using_decl"),
this);
// Match static functions/variable definitions which are defined in named
// namespaces.
Optional<ast_matchers::internal::Matcher<NamedDecl>> HasAnySymbolNames;
for (StringRef SymbolName : Context->Spec.Names) {
llvm::StringRef GlobalSymbolName = SymbolName.trim().ltrim(':');
const auto HasName = hasName(("::" + GlobalSymbolName).str());
HasAnySymbolNames =
HasAnySymbolNames ? anyOf(*HasAnySymbolNames, HasName) : HasName;
}
if (!HasAnySymbolNames) {
llvm::errs() << "No symbols being moved.\n";
return;
}
auto InMovedClass =
hasOutermostEnclosingClass(cxxRecordDecl(*HasAnySymbolNames));
// Matchers for helper declarations in old.cc.
auto InAnonymousNS = hasParent(namespaceDecl(isAnonymous()));
auto NotInMovedClass= allOf(unless(InMovedClass), InOldCC);
auto IsOldCCHelper =
allOf(NotInMovedClass, anyOf(isStaticStorageClass(), InAnonymousNS));
// Match helper classes separately with helper functions/variables since we
// want to reuse these matchers in finding helpers usage below.
//
// There could be forward declarations usage for helpers, especially for
// classes and functions. We need include these forward declarations.
//
// Forward declarations for variable helpers will be excluded as these
// declarations (with "extern") are not supposed in cpp file.
auto HelperFuncOrVar =
namedDecl(notInMacro(), anyOf(functionDecl(IsOldCCHelper),
varDecl(isDefinition(), IsOldCCHelper)));
auto HelperClasses =
cxxRecordDecl(notInMacro(), NotInMovedClass, InAnonymousNS);
// Save all helper declarations in old.cc.
Finder->addMatcher(
namedDecl(anyOf(HelperFuncOrVar, HelperClasses)).bind("helper_decls"),
this);
// Construct an AST-based call graph of helper declarations in old.cc.
// In the following matcheres, "dc" is a caller while "helper_decls" and
// "used_class" is a callee, so a new edge starting from caller to callee will
// be add in the graph.
//
// Find helper function/variable usages.
Finder->addMatcher(
declRefExpr(to(HelperFuncOrVar), hasAncestor(decl().bind("dc")))
.bind("func_ref"),
&RGBuilder);
// Find helper class usages.
Finder->addMatcher(
typeLoc(loc(recordType(hasDeclaration(HelperClasses.bind("used_class")))),
hasAncestor(decl().bind("dc"))),
&RGBuilder);
//============================================================================
// Matchers for old files, including old.h/old.cc
//============================================================================
// Create a MatchCallback for class declarations.
MatchCallbacks.push_back(llvm::make_unique<ClassDeclarationMatch>(this));
// Match moved class declarations.
auto MovedClass = cxxRecordDecl(InOldFiles, *HasAnySymbolNames,
isDefinition(), TopLevelDecl)
.bind("moved_class");
Finder->addMatcher(MovedClass, MatchCallbacks.back().get());
// Match moved class methods (static methods included) which are defined
// outside moved class declaration.
Finder->addMatcher(
cxxMethodDecl(InOldFiles, ofOutermostEnclosingClass(*HasAnySymbolNames),
isDefinition())
.bind("class_method"),
MatchCallbacks.back().get());
// Match static member variable definition of the moved class.
Finder->addMatcher(
varDecl(InMovedClass, InOldFiles, isDefinition(), isStaticDataMember())
.bind("class_static_var_decl"),
MatchCallbacks.back().get());
MatchCallbacks.push_back(llvm::make_unique<FunctionDeclarationMatch>(this));
Finder->addMatcher(functionDecl(InOldFiles, *HasAnySymbolNames, TopLevelDecl)
.bind("function"),
MatchCallbacks.back().get());
MatchCallbacks.push_back(llvm::make_unique<VarDeclarationMatch>(this));
Finder->addMatcher(
varDecl(InOldFiles, *HasAnySymbolNames, TopLevelDecl).bind("var"),
MatchCallbacks.back().get());
// Match enum definition in old.h. Enum helpers (which are defined in old.cc)
// will not be moved for now no matter whether they are used or not.
MatchCallbacks.push_back(llvm::make_unique<EnumDeclarationMatch>(this));
Finder->addMatcher(
enumDecl(InOldHeader, *HasAnySymbolNames, isDefinition(), TopLevelDecl)
.bind("enum"),
MatchCallbacks.back().get());
// Match type alias in old.h, this includes "typedef" and "using" type alias
// declarations. Type alias helpers (which are defined in old.cc) will not be
// moved for now no matter whether they are used or not.
MatchCallbacks.push_back(llvm::make_unique<TypeAliasMatch>(this));
Finder->addMatcher(namedDecl(anyOf(typedefDecl().bind("typedef"),
typeAliasDecl().bind("type_alias")),
InOldHeader, *HasAnySymbolNames, TopLevelDecl),
MatchCallbacks.back().get());
}
void ClangMoveTool::run(const ast_matchers::MatchFinder::MatchResult &Result) {
if (const auto *D =
Result.Nodes.getNodeAs<clang::NamedDecl>("decls_in_header")) {
UnremovedDeclsInOldHeader.insert(D);
} else if (const auto *FWD =
Result.Nodes.getNodeAs<clang::CXXRecordDecl>("fwd_decl")) {
// Skip all forward declarations which appear after moved class declaration.
if (RemovedDecls.empty()) {
if (const auto *DCT = FWD->getDescribedClassTemplate())
MovedDecls.push_back(DCT);
else
MovedDecls.push_back(FWD);
}
} else if (const auto *ND =
Result.Nodes.getNodeAs<clang::NamedDecl>("helper_decls")) {
MovedDecls.push_back(ND);
HelperDeclarations.push_back(ND);
DEBUG(llvm::dbgs() << "Add helper : "
<< ND->getNameAsString() << " (" << ND << ")\n");
} else if (const auto *UD =
Result.Nodes.getNodeAs<clang::NamedDecl>("using_decl")) {
MovedDecls.push_back(UD);
}
}
std::string ClangMoveTool::makeAbsolutePath(StringRef Path) {
return MakeAbsolutePath(Context->OriginalRunningDirectory, Path);
}
void ClangMoveTool::addIncludes(llvm::StringRef IncludeHeader, bool IsAngled,
llvm::StringRef SearchPath,
llvm::StringRef FileName,
clang::CharSourceRange IncludeFilenameRange,
const SourceManager &SM) {
SmallVector<char, 128> HeaderWithSearchPath;
llvm::sys::path::append(HeaderWithSearchPath, SearchPath, IncludeHeader);
std::string AbsoluteOldHeader = makeAbsolutePath(Context->Spec.OldHeader);
if (AbsoluteOldHeader ==
MakeAbsolutePath(SM, llvm::StringRef(HeaderWithSearchPath.data(),
HeaderWithSearchPath.size()))) {
OldHeaderIncludeRange = IncludeFilenameRange;
return;
}
std::string IncludeLine =
IsAngled ? ("#include <" + IncludeHeader + ">\n").str()
: ("#include \"" + IncludeHeader + "\"\n").str();
std::string AbsoluteCurrentFile = MakeAbsolutePath(SM, FileName);
if (AbsoluteOldHeader == AbsoluteCurrentFile) {
HeaderIncludes.push_back(IncludeLine);
} else if (makeAbsolutePath(Context->Spec.OldCC) == AbsoluteCurrentFile) {
CCIncludes.push_back(IncludeLine);
}
}
void ClangMoveTool::removeDeclsInOldFiles() {
if (RemovedDecls.empty()) return;
// If old_header is not specified (only move declarations from old.cc), remain
// all the helper function declarations in old.cc as UnremovedDeclsInOldHeader
// is empty in this case, there is no way to verify unused/used helpers.
if (!Context->Spec.OldHeader.empty()) {
std::vector<const NamedDecl *> UnremovedDecls;
for (const auto *D : UnremovedDeclsInOldHeader)
UnremovedDecls.push_back(D);
auto UsedDecls = getUsedDecls(RGBuilder.getGraph(), UnremovedDecls);
// We remove the helper declarations which are not used in the old.cc after
// moving the given declarations.
for (const auto *D : HelperDeclarations) {
DEBUG(llvm::dbgs() << "Check helper is used: "
<< D->getNameAsString() << " (" << D << ")\n");
if (!UsedDecls.count(HelperDeclRGBuilder::getOutmostClassOrFunDecl(
D->getCanonicalDecl()))) {
DEBUG(llvm::dbgs() << "Helper removed in old.cc: "
<< D->getNameAsString() << " (" << D << ")\n");
RemovedDecls.push_back(D);
}
}
}
for (const auto *RemovedDecl : RemovedDecls) {
const auto &SM = RemovedDecl->getASTContext().getSourceManager();
auto Range = getFullRange(RemovedDecl);
clang::tooling::Replacement RemoveReplacement(
SM,
clang::CharSourceRange::getCharRange(Range.getBegin(), Range.getEnd()),
"");
std::string FilePath = RemoveReplacement.getFilePath().str();
auto Err = Context->FileToReplacements[FilePath].add(RemoveReplacement);
if (Err)
llvm::errs() << llvm::toString(std::move(Err)) << "\n";
}
const auto &SM = RemovedDecls[0]->getASTContext().getSourceManager();
// Post process of cleanup around all the replacements.
for (auto &FileAndReplacements : Context->FileToReplacements) {
StringRef FilePath = FileAndReplacements.first;
// Add #include of new header to old header.
if (Context->Spec.OldDependOnNew &&
MakeAbsolutePath(SM, FilePath) ==
makeAbsolutePath(Context->Spec.OldHeader)) {
// FIXME: Minimize the include path like include-fixer.
std::string IncludeNewH =
"#include \"" + Context->Spec.NewHeader + "\"\n";
// This replacment for inserting header will be cleaned up at the end.
auto Err = FileAndReplacements.second.add(
tooling::Replacement(FilePath, UINT_MAX, 0, IncludeNewH));
if (Err)
llvm::errs() << llvm::toString(std::move(Err)) << "\n";
}
auto SI = FilePathToFileID.find(FilePath);
// Ignore replacements for new.h/cc.
if (SI == FilePathToFileID.end()) continue;
llvm::StringRef Code = SM.getBufferData(SI->second);
auto Style = format::getStyle("file", FilePath, Context->FallbackStyle);
if (!Style) {
llvm::errs() << llvm::toString(Style.takeError()) << "\n";
continue;
}
auto CleanReplacements = format::cleanupAroundReplacements(
Code, Context->FileToReplacements[FilePath], *Style);
if (!CleanReplacements) {
llvm::errs() << llvm::toString(CleanReplacements.takeError()) << "\n";
continue;
}
Context->FileToReplacements[FilePath] = *CleanReplacements;
}
}
void ClangMoveTool::moveDeclsToNewFiles() {
std::vector<const NamedDecl *> NewHeaderDecls;
std::vector<const NamedDecl *> NewCCDecls;
for (const auto *MovedDecl : MovedDecls) {
if (isInHeaderFile(MovedDecl, Context->OriginalRunningDirectory,
Context->Spec.OldHeader))
NewHeaderDecls.push_back(MovedDecl);
else
NewCCDecls.push_back(MovedDecl);
}
auto UsedDecls = getUsedDecls(RGBuilder.getGraph(), RemovedDecls);
std::vector<const NamedDecl *> ActualNewCCDecls;
// Filter out all unused helpers in NewCCDecls.
// We only move the used helpers (including transively used helpers) and the
// given symbols being moved.
for (const auto *D : NewCCDecls) {
if (llvm::is_contained(HelperDeclarations, D) &&
!UsedDecls.count(HelperDeclRGBuilder::getOutmostClassOrFunDecl(
D->getCanonicalDecl())))
continue;
DEBUG(llvm::dbgs() << "Helper used in new.cc: " << D->getNameAsString()
<< " " << D << "\n");
ActualNewCCDecls.push_back(D);
}
if (!Context->Spec.NewHeader.empty()) {
std::string OldHeaderInclude =
Context->Spec.NewDependOnOld
? "#include \"" + Context->Spec.OldHeader + "\"\n"
: "";
Context->FileToReplacements[Context->Spec.NewHeader] =
createInsertedReplacements(HeaderIncludes, NewHeaderDecls,
Context->Spec.NewHeader, /*IsHeader=*/true,
OldHeaderInclude);
}
if (!Context->Spec.NewCC.empty())
Context->FileToReplacements[Context->Spec.NewCC] =
createInsertedReplacements(CCIncludes, ActualNewCCDecls,
Context->Spec.NewCC);
}
// Move all contents from OldFile to NewFile.
void ClangMoveTool::moveAll(SourceManager &SM, StringRef OldFile,
StringRef NewFile) {
const FileEntry *FE = SM.getFileManager().getFile(makeAbsolutePath(OldFile));
if (!FE) {
llvm::errs() << "Failed to get file: " << OldFile << "\n";
return;
}
FileID ID = SM.getOrCreateFileID(FE, SrcMgr::C_User);
auto Begin = SM.getLocForStartOfFile(ID);
auto End = SM.getLocForEndOfFile(ID);
clang::tooling::Replacement RemoveAll (
SM, clang::CharSourceRange::getCharRange(Begin, End), "");
std::string FilePath = RemoveAll.getFilePath().str();
Context->FileToReplacements[FilePath] =
clang::tooling::Replacements(RemoveAll);
StringRef Code = SM.getBufferData(ID);
if (!NewFile.empty()) {
auto AllCode = clang::tooling::Replacements(
clang::tooling::Replacement(NewFile, 0, 0, Code));
// If we are moving from old.cc, an extra step is required: excluding
// the #include of "old.h", instead, we replace it with #include of "new.h".
if (Context->Spec.NewCC == NewFile && OldHeaderIncludeRange.isValid()) {
AllCode = AllCode.merge(
clang::tooling::Replacements(clang::tooling::Replacement(
SM, OldHeaderIncludeRange, '"' + Context->Spec.NewHeader + '"')));
}
Context->FileToReplacements[NewFile] = std::move(AllCode);
}
}
void ClangMoveTool::onEndOfTranslationUnit() {
if (Context->DumpDeclarations) {
assert(Reporter);
for (const auto *Decl : UnremovedDeclsInOldHeader) {
auto Kind = Decl->getKind();
const std::string QualifiedName = Decl->getQualifiedNameAsString();
if (Kind == Decl::Kind::Var)
Reporter->reportDeclaration(QualifiedName, "Variable");
else if (Kind == Decl::Kind::Function ||
Kind == Decl::Kind::FunctionTemplate)
Reporter->reportDeclaration(QualifiedName, "Function");
else if (Kind == Decl::Kind::ClassTemplate ||
Kind == Decl::Kind::CXXRecord)
Reporter->reportDeclaration(QualifiedName, "Class");
else if (Kind == Decl::Kind::Enum)
Reporter->reportDeclaration(QualifiedName, "Enum");
else if (Kind == Decl::Kind::Typedef ||
Kind == Decl::Kind::TypeAlias ||
Kind == Decl::Kind::TypeAliasTemplate)
Reporter->reportDeclaration(QualifiedName, "TypeAlias");
}
return;
}
if (RemovedDecls.empty())
return;
// Ignore symbols that are not supported (e.g. typedef and enum) when
// checking if there is unremoved symbol in old header. This makes sure that
// we always move old files to new files when all symbols produced from
// dump_decls are moved.
auto IsSupportedKind = [](const clang::NamedDecl *Decl) {
switch (Decl->getKind()) {
case Decl::Kind::Function:
case Decl::Kind::FunctionTemplate:
case Decl::Kind::ClassTemplate:
case Decl::Kind::CXXRecord:
case Decl::Kind::Enum:
case Decl::Kind::Typedef:
case Decl::Kind::TypeAlias:
case Decl::Kind::TypeAliasTemplate:
case Decl::Kind::Var:
return true;
default:
return false;
}
};
if (std::none_of(UnremovedDeclsInOldHeader.begin(),
UnremovedDeclsInOldHeader.end(), IsSupportedKind) &&
!Context->Spec.OldHeader.empty()) {
auto &SM = RemovedDecls[0]->getASTContext().getSourceManager();
moveAll(SM, Context->Spec.OldHeader, Context->Spec.NewHeader);
moveAll(SM, Context->Spec.OldCC, Context->Spec.NewCC);
return;
}
DEBUG(RGBuilder.getGraph()->dump());
moveDeclsToNewFiles();
removeDeclsInOldFiles();
}
} // namespace move
} // namespace clang

View File

@@ -1,229 +0,0 @@
//===-- ClangMove.h - Clang move -----------------------------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_MOVE_CLANGMOVE_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_MOVE_CLANGMOVE_H
#include "HelperDeclRefGraph.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/Frontend/FrontendAction.h"
#include "clang/Tooling/Core/Replacement.h"
#include "clang/Tooling/Tooling.h"
#include "llvm/ADT/SmallPtrSet.h"
#include "llvm/ADT/StringMap.h"
#include <map>
#include <memory>
#include <string>
#include <vector>
namespace clang {
namespace move {
// A reporter which collects and reports declarations in old header.
class DeclarationReporter {
public:
DeclarationReporter() = default;
~DeclarationReporter() = default;
void reportDeclaration(llvm::StringRef DeclarationName,
llvm::StringRef Type) {
DeclarationList.emplace_back(DeclarationName, Type);
};
// A <DeclarationName, DeclarationKind> pair.
// The DeclarationName is a fully qualified name for the declaration, like
// A::B::Foo. The DeclarationKind is a string represents the kind of the
// declaration, currently only "Function" and "Class" are supported.
typedef std::pair<std::string, std::string> DeclarationPair;
const std::vector<DeclarationPair> getDeclarationList() const {
return DeclarationList;
}
private:
std::vector<DeclarationPair> DeclarationList;
};
// Specify declarations being moved. It contains all information of the moved
// declarations.
struct MoveDefinitionSpec {
// The list of fully qualified names, e.g. Foo, a::Foo, b::Foo.
SmallVector<std::string, 4> Names;
// The file path of old header, can be relative path and absolute path.
std::string OldHeader;
// The file path of old cc, can be relative path and absolute path.
std::string OldCC;
// The file path of new header, can be relative path and absolute path.
std::string NewHeader;
// The file path of new cc, can be relative path and absolute path.
std::string NewCC;
// Whether old.h depends on new.h. If true, #include "new.h" will be added
// in old.h.
bool OldDependOnNew = false;
// Whether new.h depends on old.h. If true, #include "old.h" will be added
// in new.h.
bool NewDependOnOld = false;
};
// A Context which contains extra options which are used in ClangMoveTool.
struct ClangMoveContext {
MoveDefinitionSpec Spec;
// The Key is file path, value is the replacements being applied to the file.
std::map<std::string, tooling::Replacements> &FileToReplacements;
// The original working directory where the local clang-move binary runs.
//
// clang-move will change its current working directory to the build
// directory when analyzing the source file. We save the original working
// directory in order to get the absolute file path for the fields in Spec.
std::string OriginalRunningDirectory;
// The name of a predefined code style.
std::string FallbackStyle;
// Whether dump all declarations in old header.
bool DumpDeclarations;
};
// This tool is used to move class/function definitions from the given source
// files (old.h/cc) to new files (new.h/cc).
// The goal of this tool is to make the new/old files as compilable as possible.
//
// When moving a symbol,all used helper declarations (e.g. static
// functions/variables definitions in global/named namespace,
// functions/variables/classes definitions in anonymous namespace) used by the
// moved symbol in old.cc are moved to the new.cc. In addition, all
// using-declarations in old.cc are also moved to new.cc; forward class
// declarations in old.h are also moved to new.h.
//
// The remaining helper declarations which are unused by non-moved symbols in
// old.cc will be removed.
//
// Note: When all declarations in old header are being moved, all code in
// old.h/cc will be moved, which means old.h/cc are empty. This ignores symbols
// that are not supported (e.g. typedef and enum) so that we always move old
// files to new files when all symbols produced from dump_decls are moved.
class ClangMoveTool : public ast_matchers::MatchFinder::MatchCallback {
public:
ClangMoveTool(ClangMoveContext *const Context,
DeclarationReporter *const Reporter);
void registerMatchers(ast_matchers::MatchFinder *Finder);
void run(const ast_matchers::MatchFinder::MatchResult &Result) override;
void onEndOfTranslationUnit() override;
/// Add #includes from old.h/cc files.
///
/// \param IncludeHeader The name of the file being included, as written in
/// the source code.
/// \param IsAngled Whether the file name was enclosed in angle brackets.
/// \param SearchPath The search path which was used to find the IncludeHeader
/// in the file system. It can be a relative path or an absolute path.
/// \param FileName The name of file where the IncludeHeader comes from.
/// \param IncludeFilenameRange The source range for the written file name in
/// #include (i.e. "old.h" for #include "old.h") in old.cc.
/// \param SM The SourceManager.
void addIncludes(llvm::StringRef IncludeHeader, bool IsAngled,
llvm::StringRef SearchPath, llvm::StringRef FileName,
clang::CharSourceRange IncludeFilenameRange,
const SourceManager &SM);
std::vector<const NamedDecl *> &getMovedDecls() { return MovedDecls; }
/// Add declarations being removed from old.h/cc. For each declarations, the
/// method also records the mapping relationship between the corresponding
/// FilePath and its FileID.
void addRemovedDecl(const NamedDecl *Decl);
llvm::SmallPtrSet<const NamedDecl *, 8> &getUnremovedDeclsInOldHeader() {
return UnremovedDeclsInOldHeader;
}
private:
// Make the Path absolute using the OrignalRunningDirectory if the Path is not
// an absolute path. An empty Path will result in an empty string.
std::string makeAbsolutePath(StringRef Path);
void removeDeclsInOldFiles();
void moveDeclsToNewFiles();
void moveAll(SourceManager& SM, StringRef OldFile, StringRef NewFile);
// Stores all MatchCallbacks created by this tool.
std::vector<std::unique_ptr<ast_matchers::MatchFinder::MatchCallback>>
MatchCallbacks;
// Store all potential declarations (decls being moved, forward decls) that
// might need to move to new.h/cc. It includes all helper declarations
// (include unused ones) by default. The unused ones will be filtered out in
// the last stage. Saving in an AST-visited order.
std::vector<const NamedDecl *> MovedDecls;
// The declarations that needs to be removed in old.cc/h.
std::vector<const NamedDecl *> RemovedDecls;
// The #includes in old_header.h.
std::vector<std::string> HeaderIncludes;
// The #includes in old_cc.cc.
std::vector<std::string> CCIncludes;
// Records all helper declarations (function/variable/class definitions in
// anonymous namespaces, static function/variable definitions in global/named
// namespaces) in old.cc. saving in an AST-visited order.
std::vector<const NamedDecl *> HelperDeclarations;
// The unmoved named declarations in old header.
llvm::SmallPtrSet<const NamedDecl*, 8> UnremovedDeclsInOldHeader;
/// The source range for the written file name in #include (i.e. "old.h" for
/// #include "old.h") in old.cc, including the enclosing quotes or angle
/// brackets.
clang::CharSourceRange OldHeaderIncludeRange;
/// Mapping from FilePath to FileID, which can be used in post processes like
/// cleanup around replacements.
llvm::StringMap<FileID> FilePathToFileID;
/// A context contains all running options. It is not owned.
ClangMoveContext *const Context;
/// A reporter to report all declarations from old header. It is not owned.
DeclarationReporter *const Reporter;
/// Builder for helper declarations reference graph.
HelperDeclRGBuilder RGBuilder;
};
class ClangMoveAction : public clang::ASTFrontendAction {
public:
ClangMoveAction(ClangMoveContext *const Context,
DeclarationReporter *const Reporter)
: MoveTool(Context, Reporter) {
MoveTool.registerMatchers(&MatchFinder);
}
~ClangMoveAction() override = default;
std::unique_ptr<clang::ASTConsumer>
CreateASTConsumer(clang::CompilerInstance &Compiler,
llvm::StringRef InFile) override;
private:
ast_matchers::MatchFinder MatchFinder;
ClangMoveTool MoveTool;
};
class ClangMoveActionFactory : public tooling::FrontendActionFactory {
public:
ClangMoveActionFactory(ClangMoveContext *const Context,
DeclarationReporter *const Reporter = nullptr)
: Context(Context), Reporter(Reporter) {}
clang::FrontendAction *create() override {
return new ClangMoveAction(Context, Reporter);
}
private:
// Not owned.
ClangMoveContext *const Context;
DeclarationReporter *const Reporter;
};
} // namespace move
} // namespace clang
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_MOVE_CLANGMOVE_H

View File

@@ -1,137 +0,0 @@
//===-- UsedHelperDeclFinder.cpp - AST-based call graph for helper decls --===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "HelperDeclRefGraph.h"
#include "ClangMove.h"
#include "clang/AST/Decl.h"
#include "llvm/Support/Debug.h"
#include <vector>
#define DEBUG_TYPE "clang-move"
namespace clang {
namespace move {
void HelperDeclRefGraph::print(raw_ostream &OS) const {
OS << " --- Call graph Dump --- \n";
for (auto I = DeclMap.begin(); I != DeclMap.end(); ++I) {
const CallGraphNode *N = (I->second).get();
OS << " Declarations: ";
N->print(OS);
OS << " (" << N << ") ";
OS << " calls: ";
for (auto CI = N->begin(), CE = N->end(); CI != CE; ++CI) {
(*CI)->print(OS);
OS << " (" << CI << ") ";
}
OS << '\n';
}
OS.flush();
}
void HelperDeclRefGraph::addEdge(const Decl *Caller, const Decl *Callee) {
assert(Caller);
assert(Callee);
// Ignore the case where Caller equals Callee. This happens in the static
// class member definitions in global namespace like "int CLASS::static_var =
// 1;", its DC is a VarDel whose outmost enclosing declaration is the "CLASS"
// CXXRecordDecl.
if (Caller == Callee) return;
// Allocate a new node, mark it as root, and process it's calls.
CallGraphNode *CallerNode = getOrInsertNode(const_cast<Decl *>(Caller));
CallGraphNode *CalleeNode = getOrInsertNode(const_cast<Decl *>(Callee));
CallerNode->addCallee(CalleeNode);
}
void HelperDeclRefGraph::dump() const { print(llvm::errs()); }
CallGraphNode *HelperDeclRefGraph::getOrInsertNode(Decl *F) {
F = F->getCanonicalDecl();
std::unique_ptr<CallGraphNode> &Node = DeclMap[F];
if (Node)
return Node.get();
Node = llvm::make_unique<CallGraphNode>(F);
return Node.get();
}
CallGraphNode *HelperDeclRefGraph::getNode(const Decl *D) const {
auto I = DeclMap.find(D->getCanonicalDecl());
return I == DeclMap.end() ? nullptr : I->second.get();
}
llvm::DenseSet<const CallGraphNode *>
HelperDeclRefGraph::getReachableNodes(const Decl *Root) const {
const auto *RootNode = getNode(Root);
if (!RootNode)
return {};
llvm::DenseSet<const CallGraphNode *> ConnectedNodes;
std::function<void(const CallGraphNode *)> VisitNode =
[&](const CallGraphNode *Node) {
if (ConnectedNodes.count(Node))
return;
ConnectedNodes.insert(Node);
for (auto It = Node->begin(), End = Node->end(); It != End; ++It)
VisitNode(*It);
};
VisitNode(RootNode);
return ConnectedNodes;
}
const Decl *HelperDeclRGBuilder::getOutmostClassOrFunDecl(const Decl *D) {
const auto *DC = D->getDeclContext();
const auto *Result = D;
while (DC) {
if (const auto *RD = dyn_cast<CXXRecordDecl>(DC))
Result = RD;
else if (const auto *FD = dyn_cast<FunctionDecl>(DC))
Result = FD;
DC = DC->getParent();
}
return Result;
}
void HelperDeclRGBuilder::run(
const ast_matchers::MatchFinder::MatchResult &Result) {
// Construct the graph by adding a directed edge from caller to callee.
//
// "dc" is the closest ancestor declaration of "func_ref" or "used_class", it
// might be not the targetted Caller Decl, we always use the outmost enclosing
// FunctionDecl/CXXRecordDecl of "dc". For example,
//
// int MoveClass::F() { int a = helper(); return a; }
//
// The matched "dc" of "helper" DeclRefExpr is a VarDecl, we traverse up AST
// to find the outmost "MoveClass" CXXRecordDecl and use it as Caller.
if (const auto *FuncRef = Result.Nodes.getNodeAs<DeclRefExpr>("func_ref")) {
const auto *DC = Result.Nodes.getNodeAs<Decl>("dc");
assert(DC);
DEBUG(llvm::dbgs() << "Find helper function usage: "
<< FuncRef->getDecl()->getNameAsString() << " ("
<< FuncRef->getDecl() << ")\n");
RG->addEdge(
getOutmostClassOrFunDecl(DC->getCanonicalDecl()),
getOutmostClassOrFunDecl(FuncRef->getDecl()->getCanonicalDecl()));
} else if (const auto *UsedClass =
Result.Nodes.getNodeAs<CXXRecordDecl>("used_class")) {
const auto *DC = Result.Nodes.getNodeAs<Decl>("dc");
assert(DC);
DEBUG(llvm::dbgs() << "Find helper class usage: "
<< UsedClass->getNameAsString() << " (" << UsedClass
<< ")\n");
RG->addEdge(getOutmostClassOrFunDecl(DC->getCanonicalDecl()), UsedClass);
}
}
} // namespace move
} // namespace clang

View File

@@ -1,99 +0,0 @@
//===-- UsedHelperDeclFinder.h - AST-based call graph for helper decls ----===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_MOVE_USED_HELPER_DECL_FINDER_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_MOVE_USED_HELPER_DECL_FINDER_H
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/Analysis/CallGraph.h"
#include "llvm/ADT/DenseSet.h"
#include <memory>
#include <vector>
namespace clang {
namespace move {
// A reference graph for finding used/unused helper declarations in a single
// translation unit (e.g. old.cc). We don't reuse CallGraph in clang/Analysis
// because that CallGraph only supports function declarations.
//
// Helper declarations include following types:
// * function/variable/class definitions in an anonymous namespace.
// * static function/variable definitions in a global/named namespace.
//
// The reference graph is a directed graph. Each node in the graph represents a
// helper declaration in old.cc or a non-moved/moved declaration (e.g. class,
// function) in old.h, which means each node is associated with a Decl.
//
// To construct the graph, we use AST matcher to find interesting Decls (usually
// a pair of Caller and Callee), and add an edge from the Caller node to the
// Callee node.
//
// Specially, for a class, it might have multiple declarations such methods
// and member variables. We only use a single node to present this class, and
// this node is associated with the class declaration (CXXRecordDecl).
//
// The graph has 3 types of edges:
// 1. moved_decl => helper_decl
// 2. non_moved_decl => helper_decl
// 3. helper_decl => helper_decl
class HelperDeclRefGraph {
public:
HelperDeclRefGraph() = default;
~HelperDeclRefGraph() = default;
// Add a directed edge from the caller node to the callee node.
// A new node will be created if the node for Caller/Callee doesn't exist.
//
// Note that, all class member declarations are represented by a single node
// in the graph. The corresponding Decl of this node is the class declaration.
void addEdge(const Decl *Caller, const Decl *Callee);
CallGraphNode *getNode(const Decl *D) const;
// Get all reachable nodes in the graph from the given declaration D's node,
// including D.
llvm::DenseSet<const CallGraphNode *> getReachableNodes(const Decl *D) const;
// Dump the call graph for debug purpose.
void dump() const;
private:
void print(raw_ostream &OS) const;
// Lookup a node for the given declaration D. If not found, insert a new
// node into the graph.
CallGraphNode *getOrInsertNode(Decl *D);
typedef llvm::DenseMap<const Decl *, std::unique_ptr<CallGraphNode>>
DeclMapTy;
// DeclMap owns all CallGraphNodes.
DeclMapTy DeclMap;
};
// A builder helps to construct a call graph of helper declarations.
class HelperDeclRGBuilder : public ast_matchers::MatchFinder::MatchCallback {
public:
HelperDeclRGBuilder() : RG(new HelperDeclRefGraph) {}
void run(const ast_matchers::MatchFinder::MatchResult &Result) override;
const HelperDeclRefGraph *getGraph() const { return RG.get(); }
// Find out the outmost enclosing class/function declaration of a given D.
// For a CXXMethodDecl, get its CXXRecordDecl; For a VarDecl/FunctionDecl, get
// its outmost enclosing FunctionDecl or CXXRecordDecl.
// Return D if not found.
static const Decl *getOutmostClassOrFunDecl(const Decl *D);
private:
std::unique_ptr<HelperDeclRefGraph> RG;
};
} // namespace move
} // namespace clang
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_MOVE_USED_HELPER_DECL_FINDER_H

View File

@@ -1,17 +0,0 @@
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/..)
add_clang_executable(clang-move
ClangMoveMain.cpp
)
target_link_libraries(clang-move
clangAST
clangASTMatchers
clangBasic
clangFormat
clangFrontend
clangMove
clangRewrite
clangTooling
clangToolingCore
)

View File

@@ -1,210 +0,0 @@
//===-- ClangMoveMain.cpp - move defintion to new file ----------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "ClangMove.h"
#include "clang/Frontend/TextDiagnosticPrinter.h"
#include "clang/Rewrite/Core/Rewriter.h"
#include "clang/Tooling/ArgumentsAdjusters.h"
#include "clang/Tooling/CommonOptionsParser.h"
#include "clang/Tooling/Refactoring.h"
#include "clang/Tooling/Tooling.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/Process.h"
#include "llvm/Support/Signals.h"
#include "llvm/Support/YAMLTraits.h"
#include <set>
#include <string>
using namespace clang;
using namespace llvm;
namespace {
std::error_code CreateNewFile(const llvm::Twine &path) {
int fd = 0;
if (std::error_code ec =
llvm::sys::fs::openFileForWrite(path, fd, llvm::sys::fs::F_Text))
return ec;
return llvm::sys::Process::SafelyCloseFileDescriptor(fd);
}
cl::OptionCategory ClangMoveCategory("clang-move options");
cl::list<std::string> Names("names", cl::CommaSeparated,
cl::desc("The list of the names of classes being "
"moved, e.g. \"Foo,a::Foo,b::Foo\"."),
cl::cat(ClangMoveCategory));
cl::opt<std::string>
OldHeader("old_header",
cl::desc("The relative/absolute file path of old header."),
cl::cat(ClangMoveCategory));
cl::opt<std::string>
OldCC("old_cc", cl::desc("The relative/absolute file path of old cc."),
cl::cat(ClangMoveCategory));
cl::opt<std::string>
NewHeader("new_header",
cl::desc("The relative/absolute file path of new header."),
cl::cat(ClangMoveCategory));
cl::opt<std::string>
NewCC("new_cc", cl::desc("The relative/absolute file path of new cc."),
cl::cat(ClangMoveCategory));
cl::opt<bool>
OldDependOnNew("old_depend_on_new",
cl::desc("Whether old header will depend on new header. If "
"true, clang-move will "
"add #include of new header to old header."),
cl::init(false), cl::cat(ClangMoveCategory));
cl::opt<bool>
NewDependOnOld("new_depend_on_old",
cl::desc("Whether new header will depend on old header. If "
"true, clang-move will "
"add #include of old header to new header."),
cl::init(false), cl::cat(ClangMoveCategory));
cl::opt<std::string>
Style("style",
cl::desc("The style name used for reformatting. Default is \"llvm\""),
cl::init("llvm"), cl::cat(ClangMoveCategory));
cl::opt<bool> Dump("dump_result",
cl::desc("Dump results in JSON format to stdout."),
cl::cat(ClangMoveCategory));
cl::opt<bool> DumpDecls(
"dump_decls",
cl::desc("Dump all declarations in old header (JSON format) to stdout. If "
"the option is specified, other command options will be ignored. "
"An empty JSON will be returned if old header isn't specified."),
cl::cat(ClangMoveCategory));
} // namespace
int main(int argc, const char **argv) {
llvm::sys::PrintStackTraceOnErrorSignal(argv[0]);
tooling::CommonOptionsParser OptionsParser(argc, argv, ClangMoveCategory);
if (OldDependOnNew && NewDependOnOld) {
llvm::errs() << "Provide either --old_depend_on_new or "
"--new_depend_on_old. clang-move doesn't support these two "
"options at same time (It will introduce include cycle).\n";
return 1;
}
tooling::RefactoringTool Tool(OptionsParser.getCompilations(),
OptionsParser.getSourcePathList());
// Add "-fparse-all-comments" compile option to make clang parse all comments.
Tool.appendArgumentsAdjuster(tooling::getInsertArgumentAdjuster(
"-fparse-all-comments", tooling::ArgumentInsertPosition::BEGIN));
move::MoveDefinitionSpec Spec;
Spec.Names = {Names.begin(), Names.end()};
Spec.OldHeader = OldHeader;
Spec.NewHeader = NewHeader;
Spec.OldCC = OldCC;
Spec.NewCC = NewCC;
Spec.OldDependOnNew = OldDependOnNew;
Spec.NewDependOnOld = NewDependOnOld;
llvm::SmallString<128> InitialDirectory;
if (std::error_code EC = llvm::sys::fs::current_path(InitialDirectory))
llvm::report_fatal_error("Cannot detect current path: " +
Twine(EC.message()));
move::ClangMoveContext Context{Spec, Tool.getReplacements(),
InitialDirectory.str(), Style, DumpDecls};
move::DeclarationReporter Reporter;
move::ClangMoveActionFactory Factory(&Context, &Reporter);
int CodeStatus = Tool.run(&Factory);
if (CodeStatus)
return CodeStatus;
if (DumpDecls) {
llvm::outs() << "[\n";
const auto &Declarations = Reporter.getDeclarationList();
for (auto I = Declarations.begin(), E = Declarations.end(); I != E; ++I) {
llvm::outs() << " {\n";
llvm::outs() << " \"DeclarationName\": \"" << I->first << "\",\n";
llvm::outs() << " \"DeclarationType\": \"" << I->second << "\"\n";
llvm::outs() << " }";
// Don't print trailing "," at the end of last element.
if (I != std::prev(E))
llvm::outs() << ",\n";
}
llvm::outs() << "\n]\n";
return 0;
}
if (!NewCC.empty()) {
std::error_code EC = CreateNewFile(NewCC);
if (EC) {
llvm::errs() << "Failed to create " << NewCC << ": " << EC.message()
<< "\n";
return EC.value();
}
}
if (!NewHeader.empty()) {
std::error_code EC = CreateNewFile(NewHeader);
if (EC) {
llvm::errs() << "Failed to create " << NewHeader << ": " << EC.message()
<< "\n";
return EC.value();
}
}
IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts(new DiagnosticOptions());
clang::TextDiagnosticPrinter DiagnosticPrinter(errs(), &*DiagOpts);
DiagnosticsEngine Diagnostics(
IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs()), &*DiagOpts,
&DiagnosticPrinter, false);
auto &FileMgr = Tool.getFiles();
SourceManager SM(Diagnostics, FileMgr);
Rewriter Rewrite(SM, LangOptions());
if (!formatAndApplyAllReplacements(Tool.getReplacements(), Rewrite, Style)) {
llvm::errs() << "Failed applying all replacements.\n";
return 1;
}
if (Dump) {
std::set<llvm::StringRef> Files;
for (const auto &it : Tool.getReplacements())
Files.insert(it.first);
auto WriteToJson = [&](llvm::raw_ostream &OS) {
OS << "[\n";
for (auto I = Files.begin(), E = Files.end(); I != E; ++I) {
OS << " {\n";
OS << " \"FilePath\": \"" << *I << "\",\n";
const auto *Entry = FileMgr.getFile(*I);
auto ID = SM.translateFile(Entry);
std::string Content;
llvm::raw_string_ostream ContentStream(Content);
Rewrite.getEditBuffer(ID).write(ContentStream);
OS << " \"SourceText\": \""
<< llvm::yaml::escape(ContentStream.str()) << "\"\n";
OS << " }";
if (I != std::prev(E))
OS << ",\n";
}
OS << "\n]\n";
};
WriteToJson(llvm::outs());
return 0;
}
return Rewrite.overwriteChangedFiles();
}

View File

@@ -1,18 +0,0 @@
set(LLVM_LINK_COMPONENTS
lineeditor
support
)
add_clang_library(clangQuery
Query.cpp
QueryParser.cpp
LINK_LIBS
clangAST
clangASTMatchers
clangBasic
clangDynamicASTMatchers
clangFrontend
)
add_subdirectory(tool)

View File

@@ -1,146 +0,0 @@
//===---- Query.cpp - clang-query query -----------------------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "Query.h"
#include "QuerySession.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/Frontend/ASTUnit.h"
#include "clang/Frontend/TextDiagnostic.h"
#include "llvm/Support/raw_ostream.h"
using namespace clang::ast_matchers;
using namespace clang::ast_matchers::dynamic;
namespace clang {
namespace query {
Query::~Query() {}
bool InvalidQuery::run(llvm::raw_ostream &OS, QuerySession &QS) const {
OS << ErrStr << "\n";
return false;
}
bool NoOpQuery::run(llvm::raw_ostream &OS, QuerySession &QS) const {
return true;
}
bool HelpQuery::run(llvm::raw_ostream &OS, QuerySession &QS) const {
OS << "Available commands:\n\n"
" match MATCHER, m MATCHER "
"Match the loaded ASTs against the given matcher.\n"
" let NAME MATCHER, l NAME MATCHER "
"Give a matcher expression a name, to be used later\n"
" "
"as part of other expressions.\n"
" set bind-root (true|false) "
"Set whether to bind the root matcher to \"root\".\n"
" set output (diag|print|dump) "
"Set whether to print bindings as diagnostics,\n"
" "
"AST pretty prints or AST dumps.\n"
" quit "
"Terminates the query session.\n\n";
return true;
}
bool QuitQuery::run(llvm::raw_ostream &OS, QuerySession &QS) const {
QS.Terminate = true;
return true;
}
namespace {
struct CollectBoundNodes : MatchFinder::MatchCallback {
std::vector<BoundNodes> &Bindings;
CollectBoundNodes(std::vector<BoundNodes> &Bindings) : Bindings(Bindings) {}
void run(const MatchFinder::MatchResult &Result) override {
Bindings.push_back(Result.Nodes);
}
};
} // namespace
bool MatchQuery::run(llvm::raw_ostream &OS, QuerySession &QS) const {
unsigned MatchCount = 0;
for (auto &AST : QS.ASTs) {
MatchFinder Finder;
std::vector<BoundNodes> Matches;
DynTypedMatcher MaybeBoundMatcher = Matcher;
if (QS.BindRoot) {
llvm::Optional<DynTypedMatcher> M = Matcher.tryBind("root");
if (M)
MaybeBoundMatcher = *M;
}
CollectBoundNodes Collect(Matches);
if (!Finder.addDynamicMatcher(MaybeBoundMatcher, &Collect)) {
OS << "Not a valid top-level matcher.\n";
return false;
}
Finder.matchAST(AST->getASTContext());
for (auto MI = Matches.begin(), ME = Matches.end(); MI != ME; ++MI) {
OS << "\nMatch #" << ++MatchCount << ":\n\n";
for (auto BI = MI->getMap().begin(), BE = MI->getMap().end(); BI != BE;
++BI) {
switch (QS.OutKind) {
case OK_Diag: {
clang::SourceRange R = BI->second.getSourceRange();
if (R.isValid()) {
TextDiagnostic TD(OS, AST->getASTContext().getLangOpts(),
&AST->getDiagnostics().getDiagnosticOptions());
TD.emitDiagnostic(
FullSourceLoc(R.getBegin(), AST->getSourceManager()),
DiagnosticsEngine::Note, "\"" + BI->first + "\" binds here",
CharSourceRange::getTokenRange(R), None);
}
break;
}
case OK_Print: {
OS << "Binding for \"" << BI->first << "\":\n";
BI->second.print(OS, AST->getASTContext().getPrintingPolicy());
OS << "\n";
break;
}
case OK_Dump: {
OS << "Binding for \"" << BI->first << "\":\n";
BI->second.dump(OS, AST->getSourceManager());
OS << "\n";
break;
}
}
}
if (MI->getMap().empty())
OS << "No bindings.\n";
}
}
OS << MatchCount << (MatchCount == 1 ? " match.\n" : " matches.\n");
return true;
}
bool LetQuery::run(llvm::raw_ostream &OS, QuerySession &QS) const {
if (Value) {
QS.NamedValues[Name] = Value;
} else {
QS.NamedValues.erase(Name);
}
return true;
}
#ifndef _MSC_VER
const QueryKind SetQueryKind<bool>::value;
const QueryKind SetQueryKind<OutputKind>::value;
#endif
} // namespace query
} // namespace clang

View File

@@ -1,136 +0,0 @@
//===--- Query.h - clang-query ----------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_QUERY_QUERY_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_QUERY_QUERY_H
#include "clang/ASTMatchers/Dynamic/VariantValue.h"
#include "llvm/ADT/IntrusiveRefCntPtr.h"
#include "llvm/ADT/Optional.h"
#include <string>
namespace clang {
namespace query {
enum OutputKind { OK_Diag, OK_Print, OK_Dump };
enum QueryKind {
QK_Invalid,
QK_NoOp,
QK_Help,
QK_Let,
QK_Match,
QK_SetBool,
QK_SetOutputKind,
QK_Quit
};
class QuerySession;
struct Query : llvm::RefCountedBase<Query> {
Query(QueryKind Kind) : Kind(Kind) {}
virtual ~Query();
/// Perform the query on \p QS and print output to \p OS.
///
/// \return false if an error occurs, otherwise return true.
virtual bool run(llvm::raw_ostream &OS, QuerySession &QS) const = 0;
const QueryKind Kind;
};
typedef llvm::IntrusiveRefCntPtr<Query> QueryRef;
/// Any query which resulted in a parse error. The error message is in ErrStr.
struct InvalidQuery : Query {
InvalidQuery(const Twine &ErrStr) : Query(QK_Invalid), ErrStr(ErrStr.str()) {}
bool run(llvm::raw_ostream &OS, QuerySession &QS) const override;
std::string ErrStr;
static bool classof(const Query *Q) { return Q->Kind == QK_Invalid; }
};
/// No-op query (i.e. a blank line).
struct NoOpQuery : Query {
NoOpQuery() : Query(QK_NoOp) {}
bool run(llvm::raw_ostream &OS, QuerySession &QS) const override;
static bool classof(const Query *Q) { return Q->Kind == QK_NoOp; }
};
/// Query for "help".
struct HelpQuery : Query {
HelpQuery() : Query(QK_Help) {}
bool run(llvm::raw_ostream &OS, QuerySession &QS) const override;
static bool classof(const Query *Q) { return Q->Kind == QK_Help; }
};
/// Query for "quit".
struct QuitQuery : Query {
QuitQuery() : Query(QK_Quit) {}
bool run(llvm::raw_ostream &OS, QuerySession &QS) const override;
static bool classof(const Query *Q) { return Q->Kind == QK_Quit; }
};
/// Query for "match MATCHER".
struct MatchQuery : Query {
MatchQuery(const ast_matchers::dynamic::DynTypedMatcher &Matcher)
: Query(QK_Match), Matcher(Matcher) {}
bool run(llvm::raw_ostream &OS, QuerySession &QS) const override;
ast_matchers::dynamic::DynTypedMatcher Matcher;
static bool classof(const Query *Q) { return Q->Kind == QK_Match; }
};
struct LetQuery : Query {
LetQuery(StringRef Name, const ast_matchers::dynamic::VariantValue &Value)
: Query(QK_Let), Name(Name), Value(Value) {}
bool run(llvm::raw_ostream &OS, QuerySession &QS) const override;
std::string Name;
ast_matchers::dynamic::VariantValue Value;
static bool classof(const Query *Q) { return Q->Kind == QK_Let; }
};
template <typename T> struct SetQueryKind {};
template <> struct SetQueryKind<bool> {
static const QueryKind value = QK_SetBool;
};
template <> struct SetQueryKind<OutputKind> {
static const QueryKind value = QK_SetOutputKind;
};
/// Query for "set VAR VALUE".
template <typename T> struct SetQuery : Query {
SetQuery(T QuerySession::*Var, T Value)
: Query(SetQueryKind<T>::value), Var(Var), Value(Value) {}
bool run(llvm::raw_ostream &OS, QuerySession &QS) const override {
QS.*Var = Value;
return true;
}
static bool classof(const Query *Q) {
return Q->Kind == SetQueryKind<T>::value;
}
T QuerySession::*Var;
T Value;
};
} // namespace query
} // namespace clang
#endif

View File

@@ -1,278 +0,0 @@
//===---- QueryParser.cpp - clang-query command parser --------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "QueryParser.h"
#include "Query.h"
#include "QuerySession.h"
#include "clang/ASTMatchers/Dynamic/Parser.h"
#include "clang/Basic/CharInfo.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/StringSwitch.h"
#include <set>
using namespace llvm;
using namespace clang::ast_matchers::dynamic;
namespace clang {
namespace query {
// Lex any amount of whitespace followed by a "word" (any sequence of
// non-whitespace characters) from the start of region [Begin,End). If no word
// is found before End, return StringRef(). Begin is adjusted to exclude the
// lexed region.
StringRef QueryParser::lexWord() {
while (true) {
if (Begin == End)
return StringRef(Begin, 0);
if (!isWhitespace(*Begin))
break;
++Begin;
}
const char *WordBegin = Begin;
while (true) {
++Begin;
if (Begin == End || isWhitespace(*Begin))
return StringRef(WordBegin, Begin - WordBegin);
}
}
// This is the StringSwitch-alike used by lexOrCompleteWord below. See that
// function for details.
template <typename T> struct QueryParser::LexOrCompleteWord {
StringSwitch<T> Switch;
QueryParser *P;
StringRef Word;
// Set to the completion point offset in Word, or StringRef::npos if
// completion point not in Word.
size_t WordCompletionPos;
LexOrCompleteWord(QueryParser *P, StringRef Word, size_t WCP)
: Switch(Word), P(P), Word(Word), WordCompletionPos(WCP) {}
template <unsigned N>
LexOrCompleteWord &Case(const char (&S)[N], const T &Value,
bool IsCompletion = true) {
StringRef CaseStr(S, N - 1);
if (WordCompletionPos == StringRef::npos)
Switch.Case(S, Value);
else if (N != 1 && IsCompletion && WordCompletionPos <= CaseStr.size() &&
CaseStr.substr(0, WordCompletionPos) ==
Word.substr(0, WordCompletionPos))
P->Completions.push_back(LineEditor::Completion(
(CaseStr.substr(WordCompletionPos) + " ").str(), CaseStr));
return *this;
}
T Default(const T &Value) const { return Switch.Default(Value); }
};
// Lexes a word and stores it in Word. Returns a LexOrCompleteWord<T> object
// that can be used like a llvm::StringSwitch<T>, but adds cases as possible
// completions if the lexed word contains the completion point.
template <typename T>
QueryParser::LexOrCompleteWord<T>
QueryParser::lexOrCompleteWord(StringRef &Word) {
Word = lexWord();
size_t WordCompletionPos = StringRef::npos;
if (CompletionPos && CompletionPos <= Word.data() + Word.size()) {
if (CompletionPos < Word.data())
WordCompletionPos = 0;
else
WordCompletionPos = CompletionPos - Word.data();
}
return LexOrCompleteWord<T>(this, Word, WordCompletionPos);
}
QueryRef QueryParser::parseSetBool(bool QuerySession::*Var) {
StringRef ValStr;
unsigned Value = lexOrCompleteWord<unsigned>(ValStr)
.Case("false", 0)
.Case("true", 1)
.Default(~0u);
if (Value == ~0u) {
return new InvalidQuery("expected 'true' or 'false', got '" + ValStr + "'");
}
return new SetQuery<bool>(Var, Value);
}
QueryRef QueryParser::parseSetOutputKind() {
StringRef ValStr;
unsigned OutKind = lexOrCompleteWord<unsigned>(ValStr)
.Case("diag", OK_Diag)
.Case("print", OK_Print)
.Case("dump", OK_Dump)
.Default(~0u);
if (OutKind == ~0u) {
return new InvalidQuery("expected 'diag', 'print' or 'dump', got '" +
ValStr + "'");
}
return new SetQuery<OutputKind>(&QuerySession::OutKind, OutputKind(OutKind));
}
QueryRef QueryParser::endQuery(QueryRef Q) {
const char *Extra = Begin;
if (!lexWord().empty())
return new InvalidQuery("unexpected extra input: '" +
StringRef(Extra, End - Extra) + "'");
return Q;
}
namespace {
enum ParsedQueryKind {
PQK_Invalid,
PQK_NoOp,
PQK_Help,
PQK_Let,
PQK_Match,
PQK_Set,
PQK_Unlet,
PQK_Quit
};
enum ParsedQueryVariable { PQV_Invalid, PQV_Output, PQV_BindRoot };
QueryRef makeInvalidQueryFromDiagnostics(const Diagnostics &Diag) {
std::string ErrStr;
llvm::raw_string_ostream OS(ErrStr);
Diag.printToStreamFull(OS);
return new InvalidQuery(OS.str());
}
} // namespace
QueryRef QueryParser::completeMatcherExpression() {
std::vector<MatcherCompletion> Comps = Parser::completeExpression(
StringRef(Begin, End - Begin), CompletionPos - Begin, nullptr,
&QS.NamedValues);
for (auto I = Comps.begin(), E = Comps.end(); I != E; ++I) {
Completions.push_back(LineEditor::Completion(I->TypedText, I->MatcherDecl));
}
return QueryRef();
}
QueryRef QueryParser::doParse() {
StringRef CommandStr;
ParsedQueryKind QKind = lexOrCompleteWord<ParsedQueryKind>(CommandStr)
.Case("", PQK_NoOp)
.Case("help", PQK_Help)
.Case("m", PQK_Match, /*IsCompletion=*/false)
.Case("let", PQK_Let)
.Case("match", PQK_Match)
.Case("set", PQK_Set)
.Case("unlet", PQK_Unlet)
.Case("quit", PQK_Quit)
.Default(PQK_Invalid);
switch (QKind) {
case PQK_NoOp:
return new NoOpQuery;
case PQK_Help:
return endQuery(new HelpQuery);
case PQK_Quit:
return endQuery(new QuitQuery);
case PQK_Let: {
StringRef Name = lexWord();
if (Name.empty())
return new InvalidQuery("expected variable name");
if (CompletionPos)
return completeMatcherExpression();
Diagnostics Diag;
ast_matchers::dynamic::VariantValue Value;
if (!Parser::parseExpression(StringRef(Begin, End - Begin), nullptr,
&QS.NamedValues, &Value, &Diag)) {
return makeInvalidQueryFromDiagnostics(Diag);
}
return new LetQuery(Name, Value);
}
case PQK_Match: {
if (CompletionPos)
return completeMatcherExpression();
Diagnostics Diag;
Optional<DynTypedMatcher> Matcher = Parser::parseMatcherExpression(
StringRef(Begin, End - Begin), nullptr, &QS.NamedValues, &Diag);
if (!Matcher) {
return makeInvalidQueryFromDiagnostics(Diag);
}
return new MatchQuery(*Matcher);
}
case PQK_Set: {
StringRef VarStr;
ParsedQueryVariable Var = lexOrCompleteWord<ParsedQueryVariable>(VarStr)
.Case("output", PQV_Output)
.Case("bind-root", PQV_BindRoot)
.Default(PQV_Invalid);
if (VarStr.empty())
return new InvalidQuery("expected variable name");
if (Var == PQV_Invalid)
return new InvalidQuery("unknown variable: '" + VarStr + "'");
QueryRef Q;
switch (Var) {
case PQV_Output:
Q = parseSetOutputKind();
break;
case PQV_BindRoot:
Q = parseSetBool(&QuerySession::BindRoot);
break;
case PQV_Invalid:
llvm_unreachable("Invalid query kind");
}
return endQuery(Q);
}
case PQK_Unlet: {
StringRef Name = lexWord();
if (Name.empty())
return new InvalidQuery("expected variable name");
return endQuery(new LetQuery(Name, VariantValue()));
}
case PQK_Invalid:
return new InvalidQuery("unknown command: " + CommandStr);
}
llvm_unreachable("Invalid query kind");
}
QueryRef QueryParser::parse(StringRef Line, const QuerySession &QS) {
return QueryParser(Line, QS).doParse();
}
std::vector<LineEditor::Completion>
QueryParser::complete(StringRef Line, size_t Pos, const QuerySession &QS) {
QueryParser P(Line, QS);
P.CompletionPos = Line.data() + Pos;
P.doParse();
return P.Completions;
}
} // namespace query
} // namespace clang

View File

@@ -1,71 +0,0 @@
//===--- QueryParser.h - clang-query ----------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_QUERY_QUERY_PARSER_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_QUERY_QUERY_PARSER_H
#include "Query.h"
#include "QuerySession.h"
#include "llvm/LineEditor/LineEditor.h"
#include <cstddef>
namespace clang {
namespace query {
class QuerySession;
class QueryParser {
public:
/// Parse \a Line as a query.
///
/// \return A QueryRef representing the query, which may be an InvalidQuery.
static QueryRef parse(StringRef Line, const QuerySession &QS);
/// Compute a list of completions for \a Line assuming a cursor at
/// \param Pos characters past the start of \a Line, ordered from most
/// likely to least likely.
///
/// \return A vector of completions for \a Line.
static std::vector<llvm::LineEditor::Completion>
complete(StringRef Line, size_t Pos, const QuerySession &QS);
private:
QueryParser(StringRef Line, const QuerySession &QS)
: Begin(Line.begin()), End(Line.end()), CompletionPos(nullptr), QS(QS) {}
StringRef lexWord();
template <typename T> struct LexOrCompleteWord;
template <typename T> LexOrCompleteWord<T> lexOrCompleteWord(StringRef &Str);
QueryRef parseSetBool(bool QuerySession::*Var);
QueryRef parseSetOutputKind();
QueryRef completeMatcherExpression();
QueryRef endQuery(QueryRef Q);
/// \brief Parse [\p Begin,\p End).
///
/// \return A reference to the parsed query object, which may be an
/// \c InvalidQuery if a parse error occurs.
QueryRef doParse();
const char *Begin;
const char *End;
const char *CompletionPos;
std::vector<llvm::LineEditor::Completion> Completions;
const QuerySession &QS;
};
} // namespace query
} // namespace clang
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_QUERY_QUERY_PARSER_H

View File

@@ -1,40 +0,0 @@
//===--- QuerySession.h - clang-query ---------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_QUERY_QUERY_SESSION_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_QUERY_QUERY_SESSION_H
#include "Query.h"
#include "clang/ASTMatchers/Dynamic/VariantValue.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/StringMap.h"
namespace clang {
class ASTUnit;
namespace query {
/// Represents the state for a particular clang-query session.
class QuerySession {
public:
QuerySession(llvm::ArrayRef<std::unique_ptr<ASTUnit>> ASTs)
: ASTs(ASTs), OutKind(OK_Diag), BindRoot(true), Terminate(false) {}
llvm::ArrayRef<std::unique_ptr<ASTUnit>> ASTs;
OutputKind OutKind;
bool BindRoot;
bool Terminate;
llvm::StringMap<ast_matchers::dynamic::VariantValue> NamedValues;
};
} // namespace query
} // namespace clang
#endif

View File

@@ -1,14 +0,0 @@
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/..)
add_clang_executable(clang-query ClangQuery.cpp)
target_link_libraries(clang-query
clangAST
clangASTMatchers
clangBasic
clangDynamicASTMatchers
clangFrontend
clangQuery
clangTooling
)
install(TARGETS clang-query RUNTIME DESTINATION bin)

View File

@@ -1,116 +0,0 @@
//===---- ClangQuery.cpp - clang-query tool -------------------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This tool is for interactive exploration of the Clang AST using AST matchers.
// It currently allows the user to enter a matcher at an interactive prompt and
// view the resulting bindings as diagnostics, AST pretty prints or AST dumps.
// Example session:
//
// $ cat foo.c
// void foo(void) {}
// $ clang-query foo.c --
// clang-query> match functionDecl()
//
// Match #1:
//
// foo.c:1:1: note: "root" binds here
// void foo(void) {}
// ^~~~~~~~~~~~~~~~~
// 1 match.
//
//===----------------------------------------------------------------------===//
#include "Query.h"
#include "QueryParser.h"
#include "QuerySession.h"
#include "clang/Frontend/ASTUnit.h"
#include "clang/Tooling/CommonOptionsParser.h"
#include "clang/Tooling/Tooling.h"
#include "llvm/LineEditor/LineEditor.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/Signals.h"
#include <fstream>
#include <string>
using namespace clang;
using namespace clang::ast_matchers;
using namespace clang::ast_matchers::dynamic;
using namespace clang::query;
using namespace clang::tooling;
using namespace llvm;
static cl::extrahelp CommonHelp(CommonOptionsParser::HelpMessage);
static cl::OptionCategory ClangQueryCategory("clang-query options");
static cl::list<std::string> Commands("c", cl::desc("Specify command to run"),
cl::value_desc("command"),
cl::cat(ClangQueryCategory));
static cl::list<std::string> CommandFiles("f",
cl::desc("Read commands from file"),
cl::value_desc("file"),
cl::cat(ClangQueryCategory));
int main(int argc, const char **argv) {
llvm::sys::PrintStackTraceOnErrorSignal(argv[0]);
CommonOptionsParser OptionsParser(argc, argv, ClangQueryCategory);
if (!Commands.empty() && !CommandFiles.empty()) {
llvm::errs() << argv[0] << ": cannot specify both -c and -f\n";
return 1;
}
ClangTool Tool(OptionsParser.getCompilations(),
OptionsParser.getSourcePathList());
std::vector<std::unique_ptr<ASTUnit>> ASTs;
if (Tool.buildASTs(ASTs) != 0)
return 1;
QuerySession QS(ASTs);
if (!Commands.empty()) {
for (auto I = Commands.begin(), E = Commands.end(); I != E; ++I) {
QueryRef Q = QueryParser::parse(*I, QS);
if (!Q->run(llvm::outs(), QS))
return 1;
}
} else if (!CommandFiles.empty()) {
for (auto I = CommandFiles.begin(), E = CommandFiles.end(); I != E; ++I) {
std::ifstream Input(I->c_str());
if (!Input.is_open()) {
llvm::errs() << argv[0] << ": cannot open " << *I << "\n";
return 1;
}
while (Input.good()) {
std::string Line;
std::getline(Input, Line);
QueryRef Q = QueryParser::parse(Line, QS);
if (!Q->run(llvm::outs(), QS))
return 1;
}
}
} else {
LineEditor LE("clang-query");
LE.setListCompleter([&QS](StringRef Line, size_t Pos) {
return QueryParser::complete(Line, Pos, QS);
});
while (llvm::Optional<std::string> Line = LE.readLine()) {
QueryRef Q = QueryParser::parse(*Line, QS);
Q->run(llvm::outs(), QS);
llvm::outs().flush();
if (QS.Terminate)
break;
}
}
return 0;
}

View File

@@ -1,15 +0,0 @@
set(LLVM_LINK_COMPONENTS support)
add_clang_library(clangReorderFields
ReorderFieldsAction.cpp
LINK_LIBS
clangAST
clangASTMatchers
clangBasic
clangIndex
clangLex
clangToolingCore
)
add_subdirectory(tool)

View File

@@ -1,264 +0,0 @@
//===-- tools/extra/clang-reorder-fields/ReorderFieldsAction.cpp -*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
///
/// \file
/// This file contains the definition of the
/// ReorderFieldsAction::newASTConsumer method
///
//===----------------------------------------------------------------------===//
#include "ReorderFieldsAction.h"
#include "clang/AST/AST.h"
#include "clang/AST/ASTConsumer.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/Decl.h"
#include "clang/AST/RecursiveASTVisitor.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/Lex/Lexer.h"
#include "clang/Tooling/Refactoring.h"
#include <algorithm>
#include <string>
namespace clang {
namespace reorder_fields {
using namespace clang::ast_matchers;
/// \brief Finds the definition of a record by name.
///
/// \returns nullptr if the name is ambiguous or not found.
static const CXXRecordDecl *findDefinition(StringRef RecordName,
ASTContext &Context) {
auto Results = match(
recordDecl(hasName(RecordName), isDefinition()).bind("cxxRecordDecl"),
Context);
if (Results.empty()) {
llvm::errs() << "Definition of " << RecordName << " not found\n";
return nullptr;
}
if (Results.size() > 1) {
llvm::errs() << "The name " << RecordName
<< " is ambiguous, several definitions found\n";
return nullptr;
}
return selectFirst<CXXRecordDecl>("cxxRecordDecl", Results);
}
/// \brief Calculates the new order of fields.
///
/// \returns empty vector if the list of fields doesn't match the definition.
static SmallVector<unsigned, 4>
getNewFieldsOrder(const CXXRecordDecl *Definition,
ArrayRef<std::string> DesiredFieldsOrder) {
assert(Definition && "Definition is null");
llvm::StringMap<unsigned> NameToIndex;
for (const auto *Field : Definition->fields())
NameToIndex[Field->getName()] = Field->getFieldIndex();
if (DesiredFieldsOrder.size() != NameToIndex.size()) {
llvm::errs() << "Number of provided fields doesn't match definition.\n";
return {};
}
SmallVector<unsigned, 4> NewFieldsOrder;
for (const auto &Name : DesiredFieldsOrder) {
if (!NameToIndex.count(Name)) {
llvm::errs() << "Field " << Name << " not found in definition.\n";
return {};
}
NewFieldsOrder.push_back(NameToIndex[Name]);
}
assert(NewFieldsOrder.size() == NameToIndex.size());
return NewFieldsOrder;
}
// FIXME: error-handling
/// \brief Replaces one range of source code by another.
static void
addReplacement(SourceRange Old, SourceRange New, const ASTContext &Context,
std::map<std::string, tooling::Replacements> &Replacements) {
StringRef NewText =
Lexer::getSourceText(CharSourceRange::getTokenRange(New),
Context.getSourceManager(), Context.getLangOpts());
tooling::Replacement R(Context.getSourceManager(),
CharSourceRange::getTokenRange(Old), NewText,
Context.getLangOpts());
consumeError(Replacements[R.getFilePath()].add(R));
}
/// \brief Reorders fields in the definition of a struct/class.
///
/// At the moment reodering of fields with
/// different accesses (public/protected/private) is not supported.
/// \returns true on success.
static bool reorderFieldsInDefinition(
const CXXRecordDecl *Definition, ArrayRef<unsigned> NewFieldsOrder,
const ASTContext &Context,
std::map<std::string, tooling::Replacements> &Replacements) {
assert(Definition && "Definition is null");
SmallVector<const FieldDecl *, 10> Fields;
for (const auto *Field : Definition->fields())
Fields.push_back(Field);
// Check that the permutation of the fields doesn't change the accesses
for (const auto *Field : Definition->fields()) {
const auto FieldIndex = Field->getFieldIndex();
if (Field->getAccess() != Fields[NewFieldsOrder[FieldIndex]]->getAccess()) {
llvm::errs() << "Currently reodering of fields with different accesses "
"is not supported\n";
return false;
}
}
for (const auto *Field : Definition->fields()) {
const auto FieldIndex = Field->getFieldIndex();
if (FieldIndex == NewFieldsOrder[FieldIndex])
continue;
addReplacement(Field->getSourceRange(),
Fields[NewFieldsOrder[FieldIndex]]->getSourceRange(),
Context, Replacements);
}
return true;
}
/// \brief Reorders initializers in a C++ struct/class constructor.
///
/// A constructor can have initializers for an arbitrary subset of the class's fields.
/// Thus, we need to ensure that we reorder just the initializers that are present.
static void reorderFieldsInConstructor(
const CXXConstructorDecl *CtorDecl, ArrayRef<unsigned> NewFieldsOrder,
const ASTContext &Context,
std::map<std::string, tooling::Replacements> &Replacements) {
assert(CtorDecl && "Constructor declaration is null");
if (CtorDecl->isImplicit() || CtorDecl->getNumCtorInitializers() <= 1)
return;
// The method FunctionDecl::isThisDeclarationADefinition returns false
// for a defaulted function unless that function has been implicitly defined.
// Thus this assert needs to be after the previous checks.
assert(CtorDecl->isThisDeclarationADefinition() && "Not a definition");
SmallVector<unsigned, 10> NewFieldsPositions(NewFieldsOrder.size());
for (unsigned i = 0, e = NewFieldsOrder.size(); i < e; ++i)
NewFieldsPositions[NewFieldsOrder[i]] = i;
SmallVector<const CXXCtorInitializer *, 10> OldWrittenInitializersOrder;
SmallVector<const CXXCtorInitializer *, 10> NewWrittenInitializersOrder;
for (const auto *Initializer : CtorDecl->inits()) {
if (!Initializer->isWritten())
continue;
OldWrittenInitializersOrder.push_back(Initializer);
NewWrittenInitializersOrder.push_back(Initializer);
}
auto ByFieldNewPosition = [&](const CXXCtorInitializer *LHS,
const CXXCtorInitializer *RHS) {
assert(LHS && RHS);
return NewFieldsPositions[LHS->getMember()->getFieldIndex()] <
NewFieldsPositions[RHS->getMember()->getFieldIndex()];
};
std::sort(std::begin(NewWrittenInitializersOrder),
std::end(NewWrittenInitializersOrder), ByFieldNewPosition);
assert(OldWrittenInitializersOrder.size() ==
NewWrittenInitializersOrder.size());
for (unsigned i = 0, e = NewWrittenInitializersOrder.size(); i < e; ++i)
if (OldWrittenInitializersOrder[i] != NewWrittenInitializersOrder[i])
addReplacement(OldWrittenInitializersOrder[i]->getSourceRange(),
NewWrittenInitializersOrder[i]->getSourceRange(), Context,
Replacements);
}
/// \brief Reorders initializers in the brace initialization of an aggregate.
///
/// At the moment partial initialization is not supported.
/// \returns true on success
static bool reorderFieldsInInitListExpr(
const InitListExpr *InitListEx, ArrayRef<unsigned> NewFieldsOrder,
const ASTContext &Context,
std::map<std::string, tooling::Replacements> &Replacements) {
assert(InitListEx && "Init list expression is null");
// We care only about InitListExprs which originate from source code.
// Implicit InitListExprs are created by the semantic analyzer.
if (!InitListEx->isExplicit())
return true;
// The method InitListExpr::getSyntacticForm may return nullptr indicating that
// the current initializer list also serves as its syntactic form.
if (const auto *SyntacticForm = InitListEx->getSyntacticForm())
InitListEx = SyntacticForm;
// If there are no initializers we do not need to change anything.
if (!InitListEx->getNumInits())
return true;
if (InitListEx->getNumInits() != NewFieldsOrder.size()) {
llvm::errs() << "Currently only full initialization is supported\n";
return false;
}
for (unsigned i = 0, e = InitListEx->getNumInits(); i < e; ++i)
if (i != NewFieldsOrder[i])
addReplacement(
InitListEx->getInit(i)->getSourceRange(),
InitListEx->getInit(NewFieldsOrder[i])->getSourceRange(), Context,
Replacements);
return true;
}
namespace {
class ReorderingConsumer : public ASTConsumer {
StringRef RecordName;
ArrayRef<std::string> DesiredFieldsOrder;
std::map<std::string, tooling::Replacements> &Replacements;
public:
ReorderingConsumer(StringRef RecordName,
ArrayRef<std::string> DesiredFieldsOrder,
std::map<std::string, tooling::Replacements> &Replacements)
: RecordName(RecordName), DesiredFieldsOrder(DesiredFieldsOrder),
Replacements(Replacements) {}
ReorderingConsumer(const ReorderingConsumer &) = delete;
ReorderingConsumer &operator=(const ReorderingConsumer &) = delete;
void HandleTranslationUnit(ASTContext &Context) override {
const CXXRecordDecl *RD = findDefinition(RecordName, Context);
if (!RD)
return;
SmallVector<unsigned, 4> NewFieldsOrder =
getNewFieldsOrder(RD, DesiredFieldsOrder);
if (NewFieldsOrder.empty())
return;
if (!reorderFieldsInDefinition(RD, NewFieldsOrder, Context, Replacements))
return;
for (const auto *C : RD->ctors())
if (const auto *D = dyn_cast<CXXConstructorDecl>(C->getDefinition()))
reorderFieldsInConstructor(cast<const CXXConstructorDecl>(D),
NewFieldsOrder, Context, Replacements);
// We only need to reorder init list expressions for aggregate types.
// For other types the order of constructor parameters is used,
// which we don't change at the moment.
// Now (v0) partial initialization is not supported.
if (RD->isAggregate())
for (auto Result :
match(initListExpr(hasType(equalsNode(RD))).bind("initListExpr"),
Context))
if (!reorderFieldsInInitListExpr(
Result.getNodeAs<InitListExpr>("initListExpr"), NewFieldsOrder,
Context, Replacements)) {
Replacements.clear();
return;
}
}
};
} // end anonymous namespace
std::unique_ptr<ASTConsumer> ReorderFieldsAction::newASTConsumer() {
return llvm::make_unique<ReorderingConsumer>(RecordName, DesiredFieldsOrder,
Replacements);
}
} // namespace reorder_fields
} // namespace clang

View File

@@ -1,47 +0,0 @@
//===-- tools/extra/clang-reorder-fields/ReorderFieldsAction.h -*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
///
/// \file
/// This file contains the declarations of the ReorderFieldsAction class and
/// the FieldPosition struct.
///
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_REORDER_FIELDS_ACTION_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_REORDER_FIELDS_ACTION_H
#include "clang/Tooling/Refactoring.h"
namespace clang {
class ASTConsumer;
namespace reorder_fields {
class ReorderFieldsAction {
llvm::StringRef RecordName;
llvm::ArrayRef<std::string> DesiredFieldsOrder;
std::map<std::string, tooling::Replacements> &Replacements;
public:
ReorderFieldsAction(
llvm::StringRef RecordName,
llvm::ArrayRef<std::string> DesiredFieldsOrder,
std::map<std::string, tooling::Replacements> &Replacements)
: RecordName(RecordName), DesiredFieldsOrder(DesiredFieldsOrder),
Replacements(Replacements) {}
ReorderFieldsAction(const ReorderFieldsAction &) = delete;
ReorderFieldsAction &operator=(const ReorderFieldsAction &) = delete;
std::unique_ptr<ASTConsumer> newASTConsumer();
};
} // namespace reorder_fields
} // namespace clang
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_REORDER_FIELDS_ACTION_H

View File

@@ -1,12 +0,0 @@
add_clang_executable(clang-reorder-fields ClangReorderFields.cpp)
target_link_libraries(clang-reorder-fields
clangBasic
clangFrontend
clangReorderFields
clangRewrite
clangTooling
clangToolingCore
)
install(TARGETS clang-reorder-fields RUNTIME DESTINATION bin)

View File

@@ -1,88 +0,0 @@
//===-- tools/extra/clang-reorder-fields/tool/ClangReorderFields.cpp -*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
///
/// \file
/// This file contains the implementation of clang-reorder-fields tool
///
//===----------------------------------------------------------------------===//
#include "../ReorderFieldsAction.h"
#include "clang/Basic/Diagnostic.h"
#include "clang/Basic/DiagnosticOptions.h"
#include "clang/Basic/FileManager.h"
#include "clang/Basic/LangOptions.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Frontend/TextDiagnosticPrinter.h"
#include "clang/Rewrite/Core/Rewriter.h"
#include "clang/Tooling/CommonOptionsParser.h"
#include "clang/Tooling/Refactoring.h"
#include "clang/Tooling/Tooling.h"
#include "llvm/ADT/IntrusiveRefCntPtr.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/FileSystem.h"
#include <cstdlib>
#include <string>
#include <system_error>
using namespace llvm;
using namespace clang;
cl::OptionCategory ClangReorderFieldsCategory("clang-reorder-fields options");
static cl::opt<std::string>
RecordName("record-name", cl::Required,
cl::desc("The name of the struct/class."),
cl::cat(ClangReorderFieldsCategory));
static cl::list<std::string> FieldsOrder("fields-order", cl::CommaSeparated,
cl::OneOrMore,
cl::desc("The desired fields order."),
cl::cat(ClangReorderFieldsCategory));
static cl::opt<bool> Inplace("i", cl::desc("Overwrite edited files."),
cl::cat(ClangReorderFieldsCategory));
const char Usage[] = "A tool to reorder fields in C/C++ structs/classes.\n";
int main(int argc, const char **argv) {
tooling::CommonOptionsParser OP(argc, argv, ClangReorderFieldsCategory,
Usage);
auto Files = OP.getSourcePathList();
tooling::RefactoringTool Tool(OP.getCompilations(), Files);
reorder_fields::ReorderFieldsAction Action(RecordName, FieldsOrder,
Tool.getReplacements());
auto Factory = tooling::newFrontendActionFactory(&Action);
if (Inplace)
return Tool.runAndSave(Factory.get());
int ExitCode = Tool.run(Factory.get());
LangOptions DefaultLangOptions;
IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts(new DiagnosticOptions());
TextDiagnosticPrinter DiagnosticPrinter(errs(), &*DiagOpts);
DiagnosticsEngine Diagnostics(
IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs()), &*DiagOpts,
&DiagnosticPrinter, false);
auto &FileMgr = Tool.getFiles();
SourceManager Sources(Diagnostics, FileMgr);
Rewriter Rewrite(Sources, DefaultLangOptions);
Tool.applyAllReplacements(Rewrite);
for (const auto &File : Files) {
const auto *Entry = FileMgr.getFile(File);
const auto ID = Sources.getOrCreateFileID(Entry, SrcMgr::C_User);
Rewrite.getEditBuffer(ID).write(outs());
}
return ExitCode;
}

View File

@@ -1,7 +0,0 @@
obj/
bin/
.vs/
Key.snk
clang-tidy.exe
packages/
*.csproj.user

View File

@@ -1,28 +0,0 @@
option(BUILD_CLANG_TIDY_VS_PLUGIN "Build clang-tidy VS plugin" OFF)
if (BUILD_CLANG_TIDY_VS_PLUGIN)
add_custom_target(clang_tidy_exe_for_vsix
${CMAKE_COMMAND} -E copy_if_different
"${LLVM_TOOLS_BINARY_DIR}/clang-tidy.exe"
"${CMAKE_CURRENT_SOURCE_DIR}/ClangTidy/clang-tidy.exe"
DEPENDS clang-tidy)
add_custom_target(clang_tidy_license
${CMAKE_COMMAND} -E copy_if_different
"${CLANG_SOURCE_DIR}/LICENSE.TXT"
"${CMAKE_CURRENT_SOURCE_DIR}/ClangTidy/license.txt")
if (NOT CLANG_TIDY_VS_VERSION)
set(CLANG_TIDY_VS_VERSION "${LLVM_VERSION_MAJOR}.${LLVM_VERSION_MINOR}.${LLVM_VERSION_PATCH}")
endif()
configure_file("source.extension.vsixmanifest.in"
"${CMAKE_CURRENT_SOURCE_DIR}/ClangTidy/source.extension.vsixmanifest")
add_custom_target(clang_tidy_vsix ALL
devenv "${CMAKE_CURRENT_SOURCE_DIR}/ClangTidy.sln" /Build Release
DEPENDS clang_tidy_exe_for_vsix "${CMAKE_CURRENT_SOURCE_DIR}/ClangTidy/source.extension.vsixmanifest"
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"${CMAKE_CURRENT_SOURCE_DIR}/ClangTidy/bin/Release/ClangTidy.vsix"
"${LLVM_TOOLS_BINARY_DIR}/ClangTidy.vsix"
DEPENDS clang_tidy_exe_for_vsix clang_tidy_license)
endif()

View File

@@ -1,22 +0,0 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 14
VisualStudioVersion = 14.0.25123.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ClangTidy", "ClangTidy\ClangTidy.csproj", "{BE261DA1-36C6-449A-95C5-4653A549170A}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{BE261DA1-36C6-449A-95C5-4653A549170A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{BE261DA1-36C6-449A-95C5-4653A549170A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{BE261DA1-36C6-449A-95C5-4653A549170A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{BE261DA1-36C6-449A-95C5-4653A549170A}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal

View File

@@ -1,70 +0,0 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace LLVM.ClangTidy
{
/// <summary>
/// Allows entire categories of properties to be enabled, disabled, or inherited
/// in one fell swoop. We add properties to each category with the value being
/// this enum, and when the value is selected, we use reflection to find all other
/// properties in the same category and perform the corresponding action.
/// </summary>
public enum CategoryVerb
{
None,
Disable,
Enable,
Inherit
}
public class CategoryVerbConverter : EnumConverter
{
public CategoryVerbConverter() : base(typeof(CategoryVerb))
{
}
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{
if (value is string)
{
switch ((string)value)
{
case "Disable Category":
return CategoryVerb.Disable;
case "Enable Category":
return CategoryVerb.Enable;
case "Inherit Category":
return CategoryVerb.Inherit;
case "":
return CategoryVerb.None;
}
}
return base.ConvertFrom(context, culture, value);
}
public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
{
if (value is CategoryVerb && destinationType == typeof(string))
{
switch ((CategoryVerb)value)
{
case CategoryVerb.Disable:
return "Disable Category";
case CategoryVerb.Enable:
return "Enable Category";
case CategoryVerb.Inherit:
return "Inherit Category";
case CategoryVerb.None:
return String.Empty;
}
}
return base.ConvertTo(context, culture, value, destinationType);
}
}
}

View File

@@ -1,67 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using YamlDotNet.Serialization;
using YamlDotNet.Serialization.NamingConventions;
namespace LLVM.ClangTidy
{
public class CheckInfo
{
[YamlAlias("Name")]
public string Name { get; set; }
[YamlAlias("Label")]
public string Label { get; set; }
[YamlAlias("Description")]
public string Desc { get; set; }
[YamlAlias("Category")]
public string Category { get; set; }
}
/// <summary>
/// Reads the list of checks from Yaml and builds a description of each one.
/// This list of checks is then used by the PropertyGrid to determine what
/// items to display.
/// </summary>
public static class CheckDatabase
{
static CheckInfo[] Checks_ = null;
class CheckRoot
{
[YamlAlias("Checks")]
public CheckInfo[] Checks { get; set; }
}
static CheckDatabase()
{
using (StringReader Reader = new StringReader(Resources.ClangTidyChecks))
{
Deserializer D = new Deserializer(namingConvention: new PascalCaseNamingConvention());
var Root = D.Deserialize<CheckRoot>(Reader);
Checks_ = Root.Checks;
HashSet<string> Names = new HashSet<string>();
foreach (var Check in Checks_)
{
if (Names.Contains(Check.Name))
continue;
Names.Add(Check.Name);
}
}
}
public static IEnumerable<CheckInfo> Checks
{
get
{
return Checks_;
}
}
}
}

View File

@@ -1,273 +0,0 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
namespace LLVM.ClangTidy
{
/// <summary>
/// CheckTree is used to group checks into categories and subcategories. For
/// example, given the following list of checks:
///
/// llvm-include-order
/// llvm-namespace-comment
/// llvm-twine-local
/// llvm-header-guard
/// google-runtime-member-string-references
/// google-runtime-int
/// google-readability-namespace-comments
///
/// the corresponding CheckTree would look like this:
///
/// llvm
/// include-order
/// namespace-comment
/// twine-local
/// header-guard
/// google
/// runtime
/// member-string-references
/// int
/// readability
/// namespace-comments
/// redundant-smartptr-get
///
/// This is useful when serializing a set of options out to a .clang-tidy file,
/// because we need to decide the most efficient way to serialize the sequence
/// of check commands, when to use wildcards, etc. For example, if everything
/// under google is inherited, we can simply leave that entry out entirely from
/// the .clang-tidy file. On the other hand, if anything is inherited, we *must
/// not* add or remove google-* by wildcard because that, by definition, means
/// the property is no longer inherited. When we can categorize the checks into
/// groups and subgroups like this, it is possible to efficiently serialize to
/// a minimal representative .clang-tidy file.
/// </summary>
public abstract class CheckTreeNode
{
private string Name_;
private CheckTreeNode Parent_;
protected CheckTreeNode(string Name, CheckTreeNode Parent)
{
Name_ = Name;
Parent_ = Parent;
}
public string Path
{
get
{
if (Parent_ == null)
return null;
string ParentPath = Parent_.Path;
if (ParentPath == null)
return Name_;
return ParentPath + "-" + Name_;
}
}
public string Name
{
get
{
return Name_;
}
}
public abstract int CountChecks { get; }
public abstract int CountExplicitlyDisabledChecks { get; }
public abstract int CountExplicitlyEnabledChecks { get; }
public abstract int CountInheritedChecks { get; }
}
public class CheckTree : CheckTreeNode
{
private Dictionary<string, CheckTreeNode> Children_ = new Dictionary<string, CheckTreeNode>();
public CheckTree()
: base(null, null)
{
}
private CheckTree(string Name, CheckTree Parent)
: base(Name, Parent)
{
}
private void AddLeaf(string Name, DynamicPropertyDescriptor<bool> Property)
{
Children_[Name] = new CheckLeaf(Name, this, Property);
}
private CheckTree AddOrCreateSubgroup(string Name)
{
CheckTreeNode Subgroup = null;
if (Children_.TryGetValue(Name, out Subgroup))
{
System.Diagnostics.Debug.Assert(Subgroup is CheckTree);
return (CheckTree)Subgroup;
}
CheckTree SG = new CheckTree(Name, this);
Children_[Name] = SG;
return SG;
}
public static CheckTree Build(ClangTidyProperties Config)
{
// Since some check names contain dashes in them, it doesn't make sense to
// simply split all check names by dash and construct a huge tree. For
// example, in the check called google-runtime-member-string-references,
// we don't need each of those to be a different subgroup. So instead we
// explicitly specify the common breaking points at which a user might want
// to use a -* and everything else falls as a leaf under one of these
// categories.
// FIXME: This should be configurable without recompilation
CheckTree Root = new CheckTree();
string[][] Groups = new string[][] {
new string[] {"boost"},
new string[] {"cert"},
new string[] {"clang", "diagnostic"},
new string[] {"cppcoreguidelines", "interfaces"},
new string[] {"cppcoreguidelines", "pro", "bounds"},
new string[] {"cppcoreguidelines", "pro", "type"},
new string[] {"google", "build"},
new string[] {"google", "readability"},
new string[] {"google", "runtime"},
new string[] {"llvm"},
new string[] {"misc"},
};
foreach (string[] Group in Groups)
{
CheckTree Subgroup = Root;
foreach (string Component in Group)
Subgroup = Subgroup.AddOrCreateSubgroup(Component);
}
var Props = Config.GetProperties()
.Cast<PropertyDescriptor>()
.OfType<DynamicPropertyDescriptor<bool>>()
.Where(x => x.Attributes.OfType<ClangTidyCheckAttribute>().Count() > 0)
.Select(x => new KeyValuePair<DynamicPropertyDescriptor<bool>, string>(
x, x.Attributes.OfType<ClangTidyCheckAttribute>().First().CheckName));
var PropArray = Props.ToArray();
foreach (var CheckInfo in PropArray)
{
string LeafName = null;
CheckTree Tree = Root.LocateCheckLeafGroup(CheckInfo.Value, out LeafName);
Tree.AddLeaf(LeafName, CheckInfo.Key);
}
return Root;
}
private CheckTree LocateCheckLeafGroup(string Check, out string LeafName)
{
string[] Components = Check.Split('-');
string FirstComponent = Components.FirstOrDefault();
if (FirstComponent == null)
{
LeafName = Check;
return this;
}
CheckTreeNode Subgroup = null;
if (!Children_.TryGetValue(FirstComponent, out Subgroup))
{
LeafName = Check;
return this;
}
System.Diagnostics.Debug.Assert(Subgroup is CheckTree);
CheckTree Child = (CheckTree)Subgroup;
string ChildName = Check.Substring(FirstComponent.Length + 1);
return Child.LocateCheckLeafGroup(ChildName, out LeafName);
}
public override int CountChecks
{
get
{
return Children_.Aggregate(0, (X, V) => { return X + V.Value.CountChecks; });
}
}
public override int CountExplicitlyDisabledChecks
{
get
{
return Children_.Aggregate(0, (X, V) => { return X + V.Value.CountExplicitlyDisabledChecks; });
}
}
public override int CountExplicitlyEnabledChecks
{
get
{
return Children_.Aggregate(0, (X, V) => { return X + V.Value.CountExplicitlyEnabledChecks; });
}
}
public override int CountInheritedChecks
{
get
{
return Children_.Aggregate(0, (X, V) => { return X + V.Value.CountInheritedChecks; });
}
}
public IDictionary<string, CheckTreeNode> Children
{
get { return Children_; }
}
}
public class CheckLeaf : CheckTreeNode
{
private DynamicPropertyDescriptor<bool> Property_;
public CheckLeaf(string Name, CheckTree Parent, DynamicPropertyDescriptor<bool> Property)
: base(Name, Parent)
{
Property_ = Property;
}
public override int CountChecks
{
get
{
return 1;
}
}
public override int CountExplicitlyDisabledChecks
{
get
{
if (Property_.IsInheriting)
return 0;
return (bool)Property_.GetValue(null) ? 0 : 1;
}
}
public override int CountExplicitlyEnabledChecks
{
get
{
if (Property_.IsInheriting)
return 0;
return (bool)Property_.GetValue(null) ? 1 : 0;
}
}
public override int CountInheritedChecks
{
get
{
return (Property_.IsInheriting) ? 1 : 0;
}
}
}
}

View File

@@ -1,267 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="4.0">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{BE261DA1-36C6-449A-95C5-4653A549170A}</ProjectGuid>
<ProjectTypeGuids>{82b43b9b-a64c-4715-b499-d71e9ca2bd60};{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>LLVM.ClangTidy</RootNamespace>
<AssemblyName>ClangTidy</AssemblyName>
<SignAssembly>true</SignAssembly>
<AssemblyOriginatorKeyFile>Key.snk</AssemblyOriginatorKeyFile>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<MinimumVisualStudioVersion>14.0</MinimumVisualStudioVersion>
<FileUpgradeFlags>
</FileUpgradeFlags>
<UpgradeBackupLocation>
</UpgradeBackupLocation>
<OldToolsVersion>4.0</OldToolsVersion>
<PublishUrl>publish\</PublishUrl>
<Install>true</Install>
<InstallFrom>Disk</InstallFrom>
<UpdateEnabled>false</UpdateEnabled>
<UpdateMode>Foreground</UpdateMode>
<UpdateInterval>7</UpdateInterval>
<UpdateIntervalUnits>Days</UpdateIntervalUnits>
<UpdatePeriodically>false</UpdatePeriodically>
<UpdateRequired>false</UpdateRequired>
<MapFileExtensions>true</MapFileExtensions>
<ApplicationRevision>0</ApplicationRevision>
<ApplicationVersion>1.0.0.%2a</ApplicationVersion>
<IsWebBootstrapper>false</IsWebBootstrapper>
<UseApplicationTrust>false</UseApplicationTrust>
<BootstrapperEnabled>true</BootstrapperEnabled>
<TargetFrameworkProfile />
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>0</WarningLevel>
<Prefer32Bit>false</Prefer32Bit>
<PlatformTarget>AnyCPU</PlatformTarget>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<RunCodeAnalysis>true</RunCodeAnalysis>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<ItemGroup>
<Reference Include="Microsoft.CSharp" />
<Reference Include="Microsoft.VisualStudio.CoreUtility, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" />
<Reference Include="Microsoft.VisualStudio.Editor, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" />
<Reference Include="Microsoft.VisualStudio.OLE.Interop" />
<Reference Include="Microsoft.VisualStudio.Settings.14.0, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=x86" />
<Reference Include="Microsoft.VisualStudio.Shell.14.0, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" />
<Reference Include="Microsoft.VisualStudio.Shell.Immutable.10.0, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" />
<Reference Include="Microsoft.VisualStudio.Shell.Immutable.14.0, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" />
<Reference Include="Microsoft.VisualStudio.Shell.Interop" />
<Reference Include="Microsoft.VisualStudio.TextManager.Interop" />
<Reference Include="Microsoft.VisualStudio.TextManager.Interop" />
<Reference Include="Microsoft.VisualStudio.Utilities, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" />
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Data" />
<Reference Include="System.Design" />
<Reference Include="System.Drawing" />
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Xml" />
<Reference Include="System.Xml.Linq" />
<Reference Include="YamlDotNet, Version=3.3.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\YamlDotNet.3.3.0\lib\net35\YamlDotNet.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="YamlDotNet.Dynamic, Version=3.2.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\YamlDotNet.Dynamic.3.2.3\lib\net40\YamlDotNet.Dynamic.dll</HintPath>
<Private>True</Private>
</Reference>
</ItemGroup>
<ItemGroup>
<COMReference Include="EnvDTE">
<Guid>{80CC9F66-E7D8-4DDD-85B6-D9E6CD0E93E2}</Guid>
<VersionMajor>8</VersionMajor>
<VersionMinor>0</VersionMinor>
<Lcid>0</Lcid>
<WrapperTool>primary</WrapperTool>
<Isolated>False</Isolated>
<EmbedInteropTypes>False</EmbedInteropTypes>
</COMReference>
<COMReference Include="EnvDTE100">
<Guid>{26AD1324-4B7C-44BC-84F8-B86AED45729F}</Guid>
<VersionMajor>10</VersionMajor>
<VersionMinor>0</VersionMinor>
<Lcid>0</Lcid>
<WrapperTool>primary</WrapperTool>
<Isolated>False</Isolated>
<EmbedInteropTypes>False</EmbedInteropTypes>
</COMReference>
<COMReference Include="EnvDTE80">
<Guid>{1A31287A-4D7D-413E-8E32-3B374931BD89}</Guid>
<VersionMajor>8</VersionMajor>
<VersionMinor>0</VersionMinor>
<Lcid>0</Lcid>
<WrapperTool>primary</WrapperTool>
<Isolated>False</Isolated>
<EmbedInteropTypes>False</EmbedInteropTypes>
</COMReference>
<COMReference Include="EnvDTE90">
<Guid>{2CE2370E-D744-4936-A090-3FFFE667B0E1}</Guid>
<VersionMajor>9</VersionMajor>
<VersionMinor>0</VersionMinor>
<Lcid>0</Lcid>
<WrapperTool>primary</WrapperTool>
<Isolated>False</Isolated>
<EmbedInteropTypes>False</EmbedInteropTypes>
</COMReference>
<COMReference Include="Microsoft.VisualStudio.CommandBars">
<Guid>{1CBA492E-7263-47BB-87FE-639000619B15}</Guid>
<VersionMajor>8</VersionMajor>
<VersionMinor>0</VersionMinor>
<Lcid>0</Lcid>
<WrapperTool>primary</WrapperTool>
<Isolated>False</Isolated>
<EmbedInteropTypes>False</EmbedInteropTypes>
</COMReference>
<COMReference Include="stdole">
<Guid>{00020430-0000-0000-C000-000000000046}</Guid>
<VersionMajor>2</VersionMajor>
<VersionMinor>0</VersionMinor>
<Lcid>0</Lcid>
<WrapperTool>primary</WrapperTool>
<Isolated>False</Isolated>
<EmbedInteropTypes>False</EmbedInteropTypes>
</COMReference>
</ItemGroup>
<ItemGroup>
<Compile Include="CategoryVerb.cs" />
<Compile Include="CheckDatabase.cs" />
<Compile Include="CheckTree.cs" />
<Compile Include="ClangTidyCheckAttribute.cs" />
<Compile Include="ClangTidyConfigurationPage.cs">
<SubType>Component</SubType>
</Compile>
<Compile Include="ClangTidyProperties.cs">
<SubType>Component</SubType>
</Compile>
<Compile Include="DynamicPropertyConverter.cs" />
<Compile Include="DynamicPropertyDescriptor.cs" />
<Compile Include="ForwardingPropertyDescriptor.cs" />
<Compile Include="Guids.cs" />
<Compile Include="DynamicPropertyComponent.cs">
<SubType>Component</SubType>
</Compile>
<Compile Include="DynamicPropertyComponent.Designer.cs" />
<Compile Include="ClangTidyConfigParser.cs" />
<Compile Include="Resources.Designer.cs">
<AutoGen>True</AutoGen>
<DesignTime>True</DesignTime>
<DependentUpon>Resources.resx</DependentUpon>
</Compile>
<Compile Include="GlobalSuppressions.cs" />
<Compile Include="ClangTidyPackage.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="PkgCmdID.cs" />
<Compile Include="ClangTidyPropertyGrid.cs">
<SubType>UserControl</SubType>
</Compile>
<Compile Include="ClangTidyPropertyGrid.Designer.cs">
<DependentUpon>ClangTidyPropertyGrid.cs</DependentUpon>
</Compile>
<Compile Include="Utility.cs" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Resources.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
<SubType>Designer</SubType>
</EmbeddedResource>
<EmbeddedResource Include="ClangTidyPropertyGrid.resx">
<DependentUpon>ClangTidyPropertyGrid.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="VSPackage.resx">
<MergeWithCTO>true</MergeWithCTO>
<ManifestResourceName>VSPackage</ManifestResourceName>
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<None Include="Key.snk" />
<None Include="packages.config" />
<None Include="Resources\ClangTidyChecks.yaml" />
<None Include="source.extension.vsixmanifest">
<SubType>Designer</SubType>
</None>
</ItemGroup>
<ItemGroup>
<VSCTCompile Include="ClangTidy.vsct">
<ResourceName>Menus.ctmenu</ResourceName>
<SubType>Designer</SubType>
</VSCTCompile>
</ItemGroup>
<ItemGroup>
<None Include="Resources\Images_32bit.bmp" />
</ItemGroup>
<ItemGroup>
<Content Include="clang-tidy.exe">
<IncludeInVSIX>true</IncludeInVSIX>
</Content>
<Content Include="license.txt">
<IncludeInVSIX>true</IncludeInVSIX>
</Content>
<Content Include="Resources\Package.ico" />
</ItemGroup>
<ItemGroup>
<BootstrapperPackage Include=".NETFramework,Version=v4.0">
<Visible>False</Visible>
<ProductName>Microsoft .NET Framework 4 %28x86 and x64%29</ProductName>
<Install>true</Install>
</BootstrapperPackage>
<BootstrapperPackage Include="Microsoft.Net.Client.3.5">
<Visible>False</Visible>
<ProductName>.NET Framework 3.5 SP1 Client Profile</ProductName>
<Install>false</Install>
</BootstrapperPackage>
<BootstrapperPackage Include="Microsoft.Net.Framework.3.5.SP1">
<Visible>False</Visible>
<ProductName>.NET Framework 3.5 SP1</ProductName>
<Install>false</Install>
</BootstrapperPackage>
<BootstrapperPackage Include="Microsoft.Windows.Installer.4.5">
<Visible>False</Visible>
<ProductName>Windows Installer 4.5</ProductName>
<Install>true</Install>
</BootstrapperPackage>
</ItemGroup>
<PropertyGroup>
<UseCodebase>true</UseCodebase>
</PropertyGroup>
<PropertyGroup>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">10.0</VisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
</PropertyGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<Import Project="$(VSToolsPath)\VSSDK\Microsoft.VsSDK.targets" Condition="'$(VSToolsPath)' != ''" />
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v10.0\VSSDK\Microsoft.VsSDK.targets" Condition="false" />
<PropertyGroup>
<PreBuildEvent>if not exist $(ProjectDir)Key.snk ("$(SDKToolsPath)\sn.exe" -k $(ProjectDir)Key.snk)</PreBuildEvent>
</PropertyGroup>
<Target Name="BeforeBuild">
<Exec ContinueOnError="false" Command="&quot;..\packages\Brutal.Dev.StrongNameSigner.1.8.0\tools\StrongNameSigner.Console.exe&quot; -in &quot;..\packages&quot; -l Summary" />
</Target>
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="AfterBuild">
</Target>
-->
</Project>

View File

@@ -1,118 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<CommandTable xmlns="http://schemas.microsoft.com/VisualStudio/2005-10-18/CommandTable" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<!-- This is the file that defines the actual layout and type of the commands.
It is divided in different sections (e.g. command definition, command
placement, ...), with each defining a specific set of properties.
See the comment before each section for more details about how to
use it. -->
<!-- The VSCT compiler (the tool that translates this file into the binary
format that VisualStudio will consume) has the ability to run a preprocessor
on the vsct file; this preprocessor is (usually) the C++ preprocessor, so
it is possible to define includes and macros with the same syntax used
in C++ files. Using this ability of the compiler here, we include some files
defining some of the constants that we will use inside the file. -->
<!--This is the file that defines the IDs for all the commands exposed by VisualStudio. -->
<Extern href="stdidcmd.h"/>
<!--This header contains the command ids for the menus provided by the shell. -->
<Extern href="vsshlids.h"/>
<!--The Commands section is where we the commands, menus and menu groups are defined.
This section uses a Guid to identify the package that provides the command defined inside it. -->
<Commands package="guidClangTidyPkg">
<!-- Inside this section we have different sub-sections: one for the menus, another
for the menu groups, one for the buttons (the actual commands), one for the combos
and the last one for the bitmaps used. Each element is identified by a command id that
is a unique pair of guid and numeric identifier; the guid part of the identifier is usually
called "command set" and is used to group different command inside a logically related
group; your package should define its own command set in order to avoid collisions
with command ids defined by other packages. -->
<!-- In this section you can define new menu groups. A menu group is a container for
other menus or buttons (commands); from a visual point of view you can see the
group as the part of a menu contained between two lines. The parent of a group
must be a menu. -->
<Groups>
<Group guid="guidClangTidyCmdSet" id="MyMenuGroup" priority="0x0600">
<Parent guid="guidSHLMainMenu" id="IDM_VS_MENU_TOOLS"/>
</Group>
</Groups>
<!--Buttons section. -->
<!--This section defines the elements the user can interact with, like a menu command or a button
or combo box in a toolbar. -->
<Buttons>
<!--To define a menu group you have to specify its ID, the parent menu and its display priority.
The command is visible and enabled by default. If you need to change the visibility, status, etc, you can use
the CommandFlag node.
You can add more than one CommandFlag node e.g.:
<CommandFlag>DefaultInvisible</CommandFlag>
<CommandFlag>DynamicVisibility</CommandFlag>
If you do not want an image next to your command, remove the Icon node /> -->
<Button guid="guidClangTidyCmdSet" id="cmdidClangTidy" priority="0x0100" type="Button">
<Parent guid="guidClangTidyCmdSet" id="MyMenuGroup" />
<Icon guid="guidImages" id="bmpPic1" />
<Strings>
<ButtonText>ClangTidy</ButtonText>
</Strings>
</Button>
</Buttons>
<!--The bitmaps section is used to define the bitmaps that are used for the commands.-->
<Bitmaps>
<!-- The bitmap id is defined in a way that is a little bit different from the others:
the declaration starts with a guid for the bitmap strip, then there is the resource id of the
bitmap strip containing the bitmaps and then there are the numeric ids of the elements used
inside a button definition. An important aspect of this declaration is that the element id
must be the actual index (1-based) of the bitmap inside the bitmap strip. -->
<Bitmap guid="guidImages" href="Resources\Images_32bit.bmp" usedList="bmpPic1, bmpPic2, bmpPicSearch, bmpPicX, bmpPicArrows"/>
</Bitmaps>
</Commands>
<KeyBindings>
<KeyBinding guid="guidClangTidyCmdSet" id="cmdidClangTidy" editor="guidTextEditor" key1="R" mod1="Control" key2="T" mod2="Control"/>
</KeyBindings>
<Symbols>
<!-- This is the package guid. -->
<GuidSymbol name="guidClangTidyPkg" value="{AE4956BE-3DB8-430E-BBAB-7E2E9A014E9C}" />
<!-- This is the guid used to group the menu commands together -->
<GuidSymbol name="guidClangTidyCmdSet" value="{9E0F0493-6493-46DE-AEE1-ACD8F60F265E}">
<IDSymbol name="MyMenuGroup" value="0x1020" />
<IDSymbol name="cmdidClangTidy" value="0x0100" />
</GuidSymbol>
<GuidSymbol name="guidTextEditor" value="{E10FAD35-7FB8-4991-A269-EF88F12166C9}" />
<GuidSymbol name="guidImages" value="{942F126F-942D-428A-84B4-4AC7C523D0B2}" >
<IDSymbol name="bmpPic1" value="1" />
<IDSymbol name="bmpPic2" value="2" />
<IDSymbol name="bmpPicSearch" value="3" />
<IDSymbol name="bmpPicX" value="4" />
<IDSymbol name="bmpPicArrows" value="5" />
</GuidSymbol>
</Symbols>
</CommandTable>

View File

@@ -1,22 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace LLVM.ClangTidy
{
public class ClangTidyCheckAttribute : Attribute
{
private string CheckName_;
public ClangTidyCheckAttribute(string CheckName)
{
this.CheckName_ = CheckName;
}
public string CheckName
{
get { return CheckName_; }
}
}
}

View File

@@ -1,214 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using YamlDotNet.Serialization;
using YamlDotNet.Serialization.NamingConventions;
namespace LLVM.ClangTidy
{
static class ClangTidyConfigParser
{
public class CheckOption
{
[YamlAlias("key")]
public string Key { get; set; }
[YamlAlias("value")]
public string Value { get; set; }
}
public class ClangTidyYaml
{
[YamlAlias("Checks")]
public string Checks { get; set; }
[YamlAlias("CheckOptions")]
public List<CheckOption> CheckOptions { get; set; }
}
public static List<KeyValuePair<string, ClangTidyProperties>> ParseConfigurationChain(string ClangTidyFile)
{
List<KeyValuePair<string, ClangTidyProperties>> Result = new List<KeyValuePair<string, ClangTidyProperties>>();
Result.Add(new KeyValuePair<string, ClangTidyProperties>(null, ClangTidyProperties.RootProperties));
foreach (string P in Utility.SplitPath(ClangTidyFile).Reverse())
{
if (!Utility.HasClangTidyFile(P))
continue;
string ConfigFile = Path.Combine(P, ".clang-tidy");
using (StreamReader Reader = new StreamReader(ConfigFile))
{
Deserializer D = new Deserializer(namingConvention: new PascalCaseNamingConvention());
ClangTidyYaml Y = D.Deserialize<ClangTidyYaml>(Reader);
ClangTidyProperties Parent = Result[Result.Count - 1].Value;
ClangTidyProperties NewProps = new ClangTidyProperties(Parent);
SetPropertiesFromYaml(Y, NewProps);
Result.Add(new KeyValuePair<string, ClangTidyProperties>(P, NewProps));
}
}
return Result;
}
enum TreeLevelOp
{
Enable,
Disable,
Inherit
}
public static void SerializeClangTidyFile(ClangTidyProperties Props, string ClangTidyFilePath)
{
List<string> CommandList = new List<string>();
SerializeCheckTree(CommandList, Props.GetCheckTree(), TreeLevelOp.Inherit);
CommandList.Sort((x, y) =>
{
bool LeftSub = x.StartsWith("-");
bool RightSub = y.StartsWith("-");
if (LeftSub && !RightSub)
return -1;
if (RightSub && !LeftSub)
return 1;
return StringComparer.CurrentCulture.Compare(x, y);
});
string ConfigFile = Path.Combine(ClangTidyFilePath, ".clang-tidy");
using (StreamWriter Writer = new StreamWriter(ConfigFile))
{
Serializer S = new Serializer(namingConvention: new PascalCaseNamingConvention());
ClangTidyYaml Yaml = new ClangTidyYaml();
Yaml.Checks = String.Join(",", CommandList.ToArray());
S.Serialize(Writer, Yaml);
}
}
/// <summary>
/// Convert the given check tree into serialized list of commands that can be written to
/// the Yaml. The goal here is to determine the minimal sequence of check commands that
/// will produce the exact configuration displayed in the UI. This is complicated by the
/// fact that an inherited True is not the same as an explicitly specified True. If the
/// user has chosen to inherit a setting in a .clang-tidy file, then changing it in the
/// parent should show the reflected changes in the current file as well. So we cannot
/// simply -* everything and then add in the checks we need, because -* immediately marks
/// every single check as explicitly false, thus disabling inheritance.
/// </summary>
/// <param name="CommandList">State passed through this recursive algorithm representing
/// the sequence of commands we have determined so far.
/// </param>
/// <param name="Tree">The check tree to serialize. This is the parameter that will be
/// recursed on as successive subtrees get serialized to `CommandList`.
/// </param>
/// <param name="CurrentOp">The current state of the subtree. For example, if the
/// algorithm decides to -* an entire subtree and then add back one single check,
/// after adding a -subtree-* command to CommandList, it would pass in a value of
/// CurrentOp=TreeLevelOp.Disable when it recurses down. This allows deeper iterations
/// of the algorithm to know what kind of command (if any) needs to be added to CommandList
/// in order to put a particular check into a particular state.
/// </param>
private static void SerializeCheckTree(List<string> CommandList, CheckTree Tree, TreeLevelOp CurrentOp)
{
int NumChecks = Tree.CountChecks;
int NumDisabled = Tree.CountExplicitlyDisabledChecks;
int NumEnabled = Tree.CountExplicitlyEnabledChecks;
int NumInherited = Tree.CountInheritedChecks;
if (NumChecks == 0)
return;
if (NumInherited > 0)
System.Diagnostics.Debug.Assert(CurrentOp == TreeLevelOp.Inherit);
// If this entire tree is inherited, just exit, nothing about this needs to
// go in the clang-tidy file.
if (NumInherited == NumChecks)
return;
TreeLevelOp NewOp = CurrentOp;
// If there are no inherited properties in this subtree, decide whether to
// explicitly enable or disable this subtree. Decide by looking at whether
// there is a larger proportion of disabled or enabled descendants. If
// there are more disabled items in this subtree for example, disabling the
// subtree will lead to a smaller configuration file.
if (NumInherited == 0)
{
if (NumDisabled >= NumEnabled)
NewOp = TreeLevelOp.Disable;
else
NewOp = TreeLevelOp.Enable;
}
if (NewOp == TreeLevelOp.Disable)
{
// Only add an explicit disable command if the tree was not already disabled
// to begin with.
if (CurrentOp != TreeLevelOp.Disable)
{
string WildcardPath = "*";
if (Tree.Path != null)
WildcardPath = Tree.Path + "-" + WildcardPath;
CommandList.Add("-" + WildcardPath);
}
// If the entire subtree was disabled, there's no point descending.
if (NumDisabled == NumChecks)
return;
}
else if (NewOp == TreeLevelOp.Enable)
{
// Only add an explicit enable command if the tree was not already enabled
// to begin with. Note that if we're at the root, all checks are already
// enabled by default, so there's no need to explicitly include *
if (CurrentOp != TreeLevelOp.Enable && Tree.Path != null)
{
string WildcardPath = Tree.Path + "-*";
CommandList.Add(WildcardPath);
}
// If the entire subtree was enabled, there's no point descending.
if (NumEnabled == NumChecks)
return;
}
foreach (var Child in Tree.Children)
{
if (Child.Value is CheckLeaf)
{
CheckLeaf Leaf = (CheckLeaf)Child.Value;
if (Leaf.CountExplicitlyEnabledChecks == 1 && NewOp != TreeLevelOp.Enable)
CommandList.Add(Leaf.Path);
else if (Leaf.CountExplicitlyDisabledChecks == 1 && NewOp != TreeLevelOp.Disable)
CommandList.Add("-" + Leaf.Path);
continue;
}
System.Diagnostics.Debug.Assert(Child.Value is CheckTree);
CheckTree ChildTree = (CheckTree)Child.Value;
SerializeCheckTree(CommandList, ChildTree, NewOp);
}
}
private static void SetPropertiesFromYaml(ClangTidyYaml Yaml, ClangTidyProperties Props)
{
string[] CheckCommands = Yaml.Checks.Split(',');
foreach (string Command in CheckCommands)
{
if (Command == null || Command.Length == 0)
continue;
bool Add = true;
string Pattern = Command;
if (Pattern[0] == '-')
{
Pattern = Pattern.Substring(1);
Add = false;
}
foreach (var Match in CheckDatabase.Checks.Where(x => Utility.MatchWildcardString(x.Name, Pattern)))
{
Props.SetDynamicValue(Match.Name, Add);
}
}
}
}
}

View File

@@ -1,61 +0,0 @@
using Microsoft.VisualStudio;
using Microsoft.VisualStudio.Shell;
using Microsoft.VisualStudio.Shell.Interop;
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace LLVM.ClangTidy
{
[ClassInterface(ClassInterfaceType.AutoDual)]
[CLSCompliant(false), ComVisible(true)]
public class ClangTidyConfigurationPage : DialogPage
{
ClangTidyPropertyGrid Grid = null;
protected override IWin32Window Window
{
get
{
if (Grid == null)
Grid = new ClangTidyPropertyGrid();
return Grid;
}
}
protected override void SaveSetting(PropertyDescriptor property)
{
base.SaveSetting(property);
}
public override void SaveSettingsToStorage()
{
if (Grid != null)
Grid.SaveSettingsToStorage();
base.SaveSettingsToStorage();
}
public override void ResetSettings()
{
base.ResetSettings();
}
protected override void LoadSettingFromStorage(PropertyDescriptor prop)
{
base.LoadSettingFromStorage(prop);
}
public override void LoadSettingsFromStorage()
{
if (Grid != null)
Grid.InitializeSettings();
base.LoadSettingsFromStorage();
}
}
}

View File

@@ -1,56 +0,0 @@
//===-- ClangTidyPackages.cs - VSPackage for clang-tidy ----------*- C# -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This class contains a VS extension package that runs clang-tidy over a
// file in a VS text editor.
//
//===----------------------------------------------------------------------===//
using Microsoft.VisualStudio.Editor;
using Microsoft.VisualStudio.Shell;
using Microsoft.VisualStudio.Shell.Interop;
using Microsoft.VisualStudio.TextManager.Interop;
using System;
using System.Collections;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.IO;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using System.Xml.Linq;
namespace LLVM.ClangTidy
{
[PackageRegistration(UseManagedResourcesOnly = true)]
[InstalledProductRegistration("#110", "#112", "1.0", IconResourceID = 400)]
[ProvideMenuResource("Menus.ctmenu", 1)]
[Guid(GuidList.guidClangTidyPkgString)]
[ProvideOptionPage(typeof(ClangTidyConfigurationPage), "LLVM/Clang", "ClangTidy", 0, 0, true)]
public sealed class ClangTidyPackage : Package
{
#region Package Members
protected override void Initialize()
{
base.Initialize();
var commandService = GetService(typeof(IMenuCommandService)) as OleMenuCommandService;
if (commandService != null)
{
var menuCommandID = new CommandID(GuidList.guidClangTidyCmdSet, (int)PkgCmdIDList.cmdidClangTidy);
var menuItem = new MenuCommand(MenuItemCallback, menuCommandID);
commandService.AddCommand(menuItem);
}
}
#endregion
private void MenuItemCallback(object sender, EventArgs args)
{
}
}
}

View File

@@ -1,83 +0,0 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
namespace LLVM.ClangTidy
{
public class ClangTidyProperties : DynamicPropertyComponent
{
private static ClangTidyProperties RootProperties_ = null;
private CheckTree CheckTree_;
private bool HasUnsavedChanges_ = false;
public struct CheckMapping
{
public string CheckName;
public string Property;
}
public ClangTidyProperties()
: base(null)
{
AddClangCheckProperties();
CheckTree_ = CheckTree.Build(this);
}
public ClangTidyProperties(DynamicPropertyComponent Parent)
: base(Parent)
{
AddClangCheckProperties();
CheckTree_ = CheckTree.Build(this);
}
static ClangTidyProperties()
{
RootProperties_ = new ClangTidyProperties(null);
}
public static ClangTidyProperties RootProperties
{
get { return RootProperties_; }
}
private void AddClangCheckProperties()
{
// Add each check in the check database
HashSet<string> Categories = new HashSet<string>();
foreach (var Check in CheckDatabase.Checks)
{
string Name = Check.Name.Replace('-', '_');
List<Attribute> Attrs = new List<Attribute>();
Attrs.Add(new CategoryAttribute(Check.Category));
Attrs.Add(new DisplayNameAttribute(Check.Label));
Attrs.Add(new DefaultValueAttribute(true));
Attrs.Add(new DescriptionAttribute(Check.Desc));
Attrs.Add(new ClangTidyCheckAttribute(Check.Name));
Categories.Add(Check.Category);
AddDynamicProperty<bool>(Check.Name, Attrs.ToArray());
}
// Add a category verb for each unique category.
foreach (string Cat in Categories)
{
List<Attribute> Attrs = new List<Attribute>();
Attrs.Add(new CategoryAttribute(Cat));
Attrs.Add(new DisplayNameAttribute("(Category Verbs)"));
Attrs.Add(new TypeConverterAttribute(typeof(CategoryVerbConverter)));
Attrs.Add(new DefaultValueAttribute(CategoryVerb.None));
AddDynamicProperty<CategoryVerb>(Cat + "Verb", Attrs.ToArray());
}
}
public CheckTree GetCheckTree() { return CheckTree_; }
public bool GetHasUnsavedChanges() { return HasUnsavedChanges_; }
public void SetHasUnsavedChanges(bool Value) { HasUnsavedChanges_ = Value; }
}
}

View File

@@ -1,119 +0,0 @@
namespace LLVM.ClangTidy
{
partial class ClangTidyPropertyGrid
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Component Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.label1 = new System.Windows.Forms.Label();
this.textBox1 = new System.Windows.Forms.TextBox();
this.button1 = new System.Windows.Forms.Button();
this.propertyGrid1 = new System.Windows.Forms.PropertyGrid();
this.clangTidyProperties1 = new LLVM.ClangTidy.ClangTidyProperties();
this.clangTidyConfigurationPage1 = new LLVM.ClangTidy.ClangTidyConfigurationPage();
this.linkLabelPath = new System.Windows.Forms.LinkLabel();
this.SuspendLayout();
//
// label1
//
this.label1.AutoSize = true;
this.label1.Location = new System.Drawing.Point(14, 17);
this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(88, 13);
this.label1.TabIndex = 0;
this.label1.Text = "Configuration File";
//
// textBox1
//
this.textBox1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.textBox1.Location = new System.Drawing.Point(108, 14);
this.textBox1.Name = "textBox1";
this.textBox1.Size = new System.Drawing.Size(222, 20);
this.textBox1.TabIndex = 1;
//
// button1
//
this.button1.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
this.button1.Location = new System.Drawing.Point(336, 14);
this.button1.Name = "button1";
this.button1.Size = new System.Drawing.Size(78, 20);
this.button1.TabIndex = 2;
this.button1.Text = "Browse";
this.button1.UseVisualStyleBackColor = true;
this.button1.Click += new System.EventHandler(this.button1_Click);
//
// propertyGrid1
//
this.propertyGrid1.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
| System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.propertyGrid1.Location = new System.Drawing.Point(20, 73);
this.propertyGrid1.Name = "propertyGrid1";
this.propertyGrid1.SelectedObject = this.clangTidyProperties1;
this.propertyGrid1.Size = new System.Drawing.Size(391, 384);
this.propertyGrid1.TabIndex = 6;
this.propertyGrid1.ViewBorderColor = System.Drawing.SystemColors.ControlDarkDark;
this.propertyGrid1.PropertyValueChanged += new System.Windows.Forms.PropertyValueChangedEventHandler(this.propertyGrid1_PropertyValueChanged);
//
// linkLabelPath
//
this.linkLabelPath.AutoSize = true;
this.linkLabelPath.Location = new System.Drawing.Point(29, 50);
this.linkLabelPath.Name = "linkLabelPath";
this.linkLabelPath.Size = new System.Drawing.Size(55, 13);
this.linkLabelPath.TabIndex = 7;
this.linkLabelPath.TabStop = true;
this.linkLabelPath.Text = "linkLabel1";
this.linkLabelPath.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.linkLabelPath_LinkClicked);
//
// ClangTidyPropertyGrid
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.Controls.Add(this.linkLabelPath);
this.Controls.Add(this.propertyGrid1);
this.Controls.Add(this.button1);
this.Controls.Add(this.textBox1);
this.Controls.Add(this.label1);
this.Name = "ClangTidyPropertyGrid";
this.Size = new System.Drawing.Size(444, 469);
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private System.Windows.Forms.Label label1;
private System.Windows.Forms.TextBox textBox1;
private System.Windows.Forms.Button button1;
private System.Windows.Forms.PropertyGrid propertyGrid1;
private ClangTidyProperties clangTidyProperties1;
private ClangTidyConfigurationPage clangTidyConfigurationPage1;
private System.Windows.Forms.LinkLabel linkLabelPath;
}
}

View File

@@ -1,208 +0,0 @@
//===-- ClangTidyPropertyGrid.cs - UI for configuring clang-tidy -*- C# -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This class contains a UserControl consisting of a .NET PropertyGrid control
// allowing configuration of checks and check options for ClangTidy.
//
//===----------------------------------------------------------------------===//
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.IO;
using Microsoft.VisualStudio.Shell;
namespace LLVM.ClangTidy
{
/// <summary>
/// A UserControl displaying a PropertyGrid allowing configuration of clang-tidy
/// checks and check options, as well as serialization and deserialization of
/// clang-tidy configuration files. When a configuration file is loaded, the
/// entire chain of configuration files is analyzed based on the file path,
/// and quick access is provided to edit or view any of the files in the
/// configuration chain, allowing easy visualization of where values come from
/// (similar in spirit to the -explain-config option of clang-tidy).
/// </summary>
public partial class ClangTidyPropertyGrid : UserControl
{
/// <summary>
/// The sequence of .clang-tidy configuration files, starting from the root
/// of the filesystem, down to the selected file.
/// </summary>
List<KeyValuePair<string, ClangTidyProperties>> PropertyChain_ = null;
public ClangTidyPropertyGrid()
{
InitializeComponent();
InitializeSettings();
}
private enum ShouldCancel
{
Yes,
No,
}
public void SaveSettingsToStorage()
{
PersistUnsavedChanges(false);
}
private ShouldCancel PersistUnsavedChanges(bool PromptFirst)
{
var UnsavedResults = PropertyChain_.Where(x => x.Key != null && x.Value.GetHasUnsavedChanges());
if (UnsavedResults.Count() == 0)
return ShouldCancel.No;
bool ShouldSave = false;
if (PromptFirst)
{
var Response = MessageBox.Show(
"You have unsaved changes! Do you want to save before loading a new file?",
"clang-tidy",
MessageBoxButtons.YesNoCancel);
ShouldSave = (Response == DialogResult.Yes);
if (Response == DialogResult.Cancel)
return ShouldCancel.Yes;
}
else
ShouldSave = true;
if (ShouldSave)
{
foreach (var Result in UnsavedResults)
{
ClangTidyConfigParser.SerializeClangTidyFile(Result.Value, Result.Key);
Result.Value.SetHasUnsavedChanges(false);
}
}
return ShouldCancel.No;
}
public void InitializeSettings()
{
PropertyChain_ = new List<KeyValuePair<string, ClangTidyProperties>>();
PropertyChain_.Add(new KeyValuePair<string, ClangTidyProperties>(null, ClangTidyProperties.RootProperties));
reloadPropertyChain();
}
private void button1_Click(object sender, EventArgs e)
{
ShouldCancel Cancel = PersistUnsavedChanges(true);
if (Cancel == ShouldCancel.Yes)
return;
using (OpenFileDialog D = new OpenFileDialog())
{
D.Filter = "Clang Tidy files|.clang-tidy";
D.CheckPathExists = true;
D.CheckFileExists = true;
if (D.ShowDialog() == DialogResult.OK)
{
PropertyChain_.Clear();
PropertyChain_ = ClangTidyConfigParser.ParseConfigurationChain(D.FileName);
textBox1.Text = D.FileName;
reloadPropertyChain();
}
}
}
private static readonly string DefaultText = "(Default)";
private static readonly string BrowseText = "Browse for a file to edit its properties";
/// <summary>
/// After a new configuration file is chosen, analyzes the directory hierarchy
/// and finds all .clang-tidy files in the path, parses them and updates the
/// PropertyGrid and quick-access LinkLabel control to reflect the new property
/// chain.
/// </summary>
private void reloadPropertyChain()
{
StringBuilder LinkBuilder = new StringBuilder();
LinkBuilder.Append(DefaultText);
LinkBuilder.Append(" > ");
int PrefixLength = LinkBuilder.Length;
if (PropertyChain_.Count == 1)
LinkBuilder.Append(BrowseText);
else
LinkBuilder.Append(PropertyChain_[PropertyChain_.Count - 1].Key);
linkLabelPath.Text = LinkBuilder.ToString();
// Given a path like D:\Foo\Bar\Baz, construct a LinkLabel where individual
// components of the path are clickable iff they contain a .clang-tidy file.
// Clicking one of the links then updates the PropertyGrid to display the
// selected .clang-tidy file.
ClangTidyProperties LastProps = ClangTidyProperties.RootProperties;
linkLabelPath.Links.Clear();
linkLabelPath.Links.Add(0, DefaultText.Length, LastProps);
foreach (var Prop in PropertyChain_.Skip(1))
{
LastProps = Prop.Value;
string ClangTidyFolder = Path.GetFileName(Prop.Key);
int ClangTidyFolderOffset = Prop.Key.Length - ClangTidyFolder.Length;
linkLabelPath.Links.Add(PrefixLength + ClangTidyFolderOffset, ClangTidyFolder.Length, LastProps);
}
propertyGrid1.SelectedObject = LastProps;
}
private void propertyGrid1_PropertyValueChanged(object s, PropertyValueChangedEventArgs e)
{
ClangTidyProperties Props = (ClangTidyProperties)propertyGrid1.SelectedObject;
Props.SetHasUnsavedChanges(true);
// When a CategoryVerb is selected, perform the corresponding action.
PropertyDescriptor Property = e.ChangedItem.PropertyDescriptor;
if (!(e.ChangedItem.Value is CategoryVerb))
return;
CategoryVerb Action = (CategoryVerb)e.ChangedItem.Value;
if (Action == CategoryVerb.None)
return;
var Category = Property.Attributes.OfType<CategoryAttribute>().FirstOrDefault();
if (Category == null)
return;
var SameCategoryProps = Props.GetProperties(new Attribute[] { Category });
foreach (PropertyDescriptor P in SameCategoryProps)
{
if (P == Property)
continue;
switch (Action)
{
case CategoryVerb.Disable:
P.SetValue(propertyGrid1.SelectedObject, false);
break;
case CategoryVerb.Enable:
P.SetValue(propertyGrid1.SelectedObject, true);
break;
case CategoryVerb.Inherit:
P.ResetValue(propertyGrid1.SelectedObject);
break;
}
}
Property.ResetValue(propertyGrid1.SelectedObject);
propertyGrid1.Invalidate();
}
private void linkLabelPath_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
{
ClangTidyProperties Props = (ClangTidyProperties)e.Link.LinkData;
propertyGrid1.SelectedObject = Props;
}
}
}

View File

@@ -1,123 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<metadata name="clangTidyConfigurationPage1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>183, 17</value>
</metadata>
</root>

View File

@@ -1,42 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace LLVM.ClangTidy
{
partial class DynamicPropertyComponent
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Component Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
components = new System.ComponentModel.Container();
}
#endregion
}
}

View File

@@ -1,138 +0,0 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace LLVM.ClangTidy
{
/// <summary>
/// The goal of this class is to enable displaying of a PropertyGrid in much the
/// same way that Visual Studio's C++ project system does. A project or file can
/// have properties which might inherit from their parent, or be overridden.
/// It turns out this is somewhat non-trivial. The .NET PropertyGrid is good makes
/// displaying simple properties with a static notion of what constitutes a
/// "default" value very easy. You simply apply an Attribute to the class that says
/// what the default value is and you're done. But when you try to introduce the idea
/// that a property's default value depends on some other factor, things get much more
/// complicated due to the static nature of Attributes.
///
/// The solution to this is to inherit from ICustomTypeDescriptor. This is the mechanism
/// by which you can inject or modify attributes or properties at runtime. The .NET
/// PropertyGrid is designed in such a way that instead of using simple .NET Reflection to
/// look for the properties and attributes on a class, it will invoke the methods of
/// ICustomTypeDescriptor (if your type inherits from it), and ask those methods. Our
/// implementation of ICustomTypeDescriptor works by waiting until the PropertyGrid requests
/// PropertyDescriptors for each of the properties, and then "decorating" them with our
/// own custom PropertyDescriptor implementation which understands the proeprty inheritance
/// model we wish to implement.
/// </summary>
public partial class DynamicPropertyComponent : Component, ICustomTypeDescriptor
{
PropertyDescriptorCollection DynamicProperties_ = new PropertyDescriptorCollection(null);
private DynamicPropertyComponent Parent_;
public DynamicPropertyComponent(DynamicPropertyComponent Parent)
{
Parent_ = Parent;
}
public DynamicPropertyComponent(DynamicPropertyComponent Parent, IContainer container)
{
Parent_ = Parent;
container.Add(this);
InitializeComponent();
}
public AttributeCollection GetAttributes()
{
return TypeDescriptor.GetAttributes(GetType());
}
public string GetClassName()
{
return TypeDescriptor.GetClassName(GetType());
}
public string GetComponentName()
{
return TypeDescriptor.GetComponentName(GetType());
}
public TypeConverter GetConverter()
{
return TypeDescriptor.GetConverter(GetType());
}
public EventDescriptor GetDefaultEvent()
{
return TypeDescriptor.GetDefaultEvent(GetType());
}
public PropertyDescriptor GetDefaultProperty()
{
return TypeDescriptor.GetDefaultProperty(GetType());
}
public object GetEditor(Type editorBaseType)
{
return TypeDescriptor.GetEditor(GetType(), editorBaseType);
}
public EventDescriptorCollection GetEvents()
{
return TypeDescriptor.GetEvents(GetType());
}
public EventDescriptorCollection GetEvents(Attribute[] attributes)
{
return TypeDescriptor.GetEvents(GetType(), attributes);
}
public PropertyDescriptorCollection GetProperties()
{
return DynamicProperties_;
}
public PropertyDescriptorCollection GetProperties(Attribute[] attributes)
{
var Props = DynamicProperties_.OfType<PropertyDescriptor>();
var Filtered = Props.Where(x => x.Attributes.Contains(attributes)).ToArray();
return new PropertyDescriptorCollection(Filtered);
}
public object GetPropertyOwner(PropertyDescriptor pd)
{
return this;
}
public void SetDynamicValue<T>(string Name, T Value)
{
Name = Name.Replace('-', '_');
DynamicPropertyDescriptor<T> Descriptor = (DynamicPropertyDescriptor<T>)DynamicProperties_.Find(Name, false);
Descriptor.SetValue(this, Value);
}
public T GetDynamicValue<T>(string Name)
{
Name = Name.Replace('-', '_');
DynamicPropertyDescriptor<T> Descriptor = (DynamicPropertyDescriptor<T>)DynamicProperties_.Find(Name, false);
return (T)Descriptor.GetValue(this);
}
protected void AddDynamicProperty<T>(string Name, Attribute[] Attributes)
{
Name = Name.Replace('-', '_');
// If we have a parent, find the corresponding PropertyDescriptor with the same
// name from the parent.
DynamicPropertyDescriptor<T> ParentDescriptor = null;
if (Parent_ != null)
ParentDescriptor = (DynamicPropertyDescriptor<T>)Parent_.GetProperties().Find(Name, false);
DynamicProperties_.Add(new DynamicPropertyDescriptor<T>(Name, ParentDescriptor, Name, Attributes));
}
}
}

View File

@@ -1,139 +0,0 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace LLVM.ClangTidy
{
class MagicInheritance
{
public static readonly string Value = "{3A27184D-1774-489B-9BB7-7191B8E8E622}";
public static readonly string Text = "<Inherit from project or parent>";
}
class DynamicPropertyConverter<T> : TypeConverter
{
private DynamicPropertyDescriptor<T> Descriptor_;
private TypeConverter Root_;
public DynamicPropertyConverter(DynamicPropertyDescriptor<T> Descriptor, TypeConverter Root)
{
Descriptor_ = Descriptor;
Root_ = Root;
}
/// <summary>
/// Returns true if there are specific values that can be chosen from a dropdown
/// for this property. Regardless of whether standard values are supported for
/// the underlying type, we always support standard values because we need to
/// display the inheritance option.
/// </summary>
/// <returns>true</returns>
public override bool GetStandardValuesSupported(ITypeDescriptorContext context)
{
return true;
}
/// <summary>
/// Get the set of all standard values that can be chosen from a dropdown for this
/// property. If the underlying type supports standard values, we want to include
/// all those. Additionally, we want to display the option to inherit the value,
/// but only if the value is not already inheriting.
/// </summary>
public override StandardValuesCollection GetStandardValues(ITypeDescriptorContext context)
{
List<object> Values = new List<object>();
if (Root_.GetStandardValuesSupported(context))
{
StandardValuesCollection RootValues = Root_.GetStandardValues(context);
Values.AddRange(RootValues.Cast<object>());
}
if (!Descriptor_.IsInheriting)
Values.Add(MagicInheritance.Value);
StandardValuesCollection Result = new StandardValuesCollection(Values);
return Result;
}
/// <summary>
/// Determines whether this property can accept values other than those specified
/// in the dropdown (for example by manually typing into the field).
/// </summary>
public override bool GetStandardValuesExclusive(ITypeDescriptorContext context)
{
// Although we add items to the dropdown list, we do not change whether or not
// the set of values are exclusive. If the user could type into the field before
// they still can. And if they couldn't before, they still can't.
return Root_.GetStandardValuesExclusive(context);
}
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
{
return Root_.CanConvertFrom(context, sourceType);
}
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
{
return Root_.CanConvertTo(context, destinationType);
}
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{
if (value.Equals(MagicInheritance.Value))
return MagicInheritance.Text;
return Root_.ConvertFrom(context, culture, value);
}
public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
{
if (value.GetType() == destinationType)
return value;
return Root_.ConvertTo(context, culture, value, destinationType);
}
public override object CreateInstance(ITypeDescriptorContext context, IDictionary propertyValues)
{
return Root_.CreateInstance(context, propertyValues);
}
public override bool Equals(object obj)
{
return Root_.Equals(obj);
}
public override bool GetCreateInstanceSupported(ITypeDescriptorContext context)
{
return Root_.GetCreateInstanceSupported(context);
}
public override int GetHashCode()
{
return Root_.GetHashCode();
}
public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext context, object value, Attribute[] attributes)
{
return Root_.GetProperties(context, value, attributes);
}
public override bool GetPropertiesSupported(ITypeDescriptorContext context)
{
return Root_.GetPropertiesSupported(context);
}
public override bool IsValid(ITypeDescriptorContext context, object value)
{
return Root_.IsValid(context, value);
}
public override string ToString()
{
return Root_.ToString();
}
}
}

View File

@@ -1,137 +0,0 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace LLVM.ClangTidy
{
public class DynamicPropertyDescriptor<T> : PropertyDescriptor
{
T Value_;
DynamicPropertyDescriptor<T> Parent_;
bool IsInheriting_;
object Component_;
public DynamicPropertyDescriptor(object Component, DynamicPropertyDescriptor<T> Parent, string Name, Attribute[] Attrs)
: base(Name, Attrs)
{
foreach (DefaultValueAttribute Attr in Attrs.OfType<DefaultValueAttribute>())
{
Value_ = (T)Attr.Value;
}
Parent_ = Parent;
IsInheriting_ = true;
Component_ = Component;
}
public bool IsInheriting { get { return IsInheriting_; } set { IsInheriting_ = value; } }
public DynamicPropertyDescriptor<T> Parent { get { return Parent_; } }
/// <summary>
/// Determines whether this property's value should be considered "default" (e.g.
/// displayed in bold in the property grid). Root properties are unmodifiable and
/// always default. Non-root properties are default iff they are inheriting.
/// That is to say, if a property is explicitly set to False, the property should
/// be serialized even if the parent is also False. It would only not be serialized
/// if the user had explicitly chosen to inherit it.
/// </summary>
/// <param name="component"></param>
/// <returns></returns>
public override bool ShouldSerializeValue(object component)
{
return (Parent_ != null) && !IsInheriting;
}
/// <summary>
/// Set the value back to the default. For root properties, this essentially does
/// nothing as they are read-only anyway. For non-root properties, this only means
/// that the property is now inheriting.
/// </summary>
/// <param name="component"></param>
public override void ResetValue(object component)
{
IsInheriting_ = true;
}
public override void SetValue(object component, object value)
{
// This is a bit of a trick. If the user chose the inheritance option from the
// dropdown, we will try to set the value to that string. So look for that and
// then just reset the value.
if (value.Equals(MagicInheritance.Text))
ResetValue(component);
else
{
// By explicitly setting the value, this property is no longer inheriting,
// even if the value the property is being set to is the same as that of
// the parent.
IsInheriting_ = false;
Value_ = (T)value;
}
}
public override TypeConverter Converter
{
get
{
// We need to return a DynamicPropertyConverter<> that can deal with our requirement
// to inject the inherit property option into the dropdown. But we still need to use
// the "real" converter to do the actual work for the underlying type. Therefore,
// we need to look for a TypeConverter<> attribute on the property, and if it is present
// forward an instance of that converter to the DynamicPropertyConverter<>. Otherwise,
// forward an instance of the default converter for type T to the DynamicPropertyConverter<>.
TypeConverter UnderlyingConverter = null;
var ConverterAttr = this.Attributes.OfType<TypeConverterAttribute>().LastOrDefault();
if (ConverterAttr != null)
{
Type ConverterType = Type.GetType(ConverterAttr.ConverterTypeName);
UnderlyingConverter = (TypeConverter)Activator.CreateInstance(ConverterType);
}
else
UnderlyingConverter = TypeDescriptor.GetConverter(typeof(T));
return new DynamicPropertyConverter<T>(this, UnderlyingConverter);
}
}
public override bool IsReadOnly
{
get
{
return (Parent_ == null);
}
}
public override Type ComponentType
{
get
{
return Component_.GetType();
}
}
public override object GetValue(object component)
{
// Return either this property's value or the parents value, depending on
// whether or not this property is inheriting.
if (IsInheriting_ && Parent != null)
return Parent.GetValue(component);
return Value_;
}
public override bool CanResetValue(object component)
{
return !IsReadOnly;
}
public override Type PropertyType
{
get
{
return typeof(T);
}
}
}
}

View File

@@ -1,191 +0,0 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace LLVM.ClangTidy
{
/// <summary>
/// A decorator of sorts. Accepts a PropertyDescriptor to its constructor
/// and forwards all calls to the underlying PropertyDescriptor. In this way
/// we can inherit from ForwardingPropertyDescriptor and override only the
/// few methods we need to customize the behavior of, while allowing the
/// underlying PropertyDescriptor to do the real work.
/// </summary>
public abstract class ForwardingPropertyDescriptor : PropertyDescriptor
{
private readonly PropertyDescriptor root;
protected PropertyDescriptor Root { get { return root; } }
protected ForwardingPropertyDescriptor(PropertyDescriptor root)
: base(root)
{
this.root = root;
}
public override void AddValueChanged(object component, EventHandler handler)
{
root.AddValueChanged(component, handler);
}
public override AttributeCollection Attributes
{
get
{
return root.Attributes;
}
}
public override bool CanResetValue(object component)
{
return root.CanResetValue(component);
}
public override string Category
{
get
{
return root.Category;
}
}
public override Type ComponentType
{
get
{
return root.ComponentType;
}
}
public override TypeConverter Converter
{
get
{
return root.Converter;
}
}
public override string Description
{
get
{
return root.Description;
}
}
public override bool DesignTimeOnly
{
get
{
return root.DesignTimeOnly;
}
}
public override string DisplayName
{
get
{
return root.DisplayName;
}
}
public override bool Equals(object obj)
{
return root.Equals(obj);
}
public override PropertyDescriptorCollection GetChildProperties(object instance, Attribute[] filter)
{
return root.GetChildProperties(instance, filter);
}
public override object GetEditor(Type editorBaseType)
{
return root.GetEditor(editorBaseType);
}
public override int GetHashCode()
{
return root.GetHashCode();
}
public override object GetValue(object component)
{
return root.GetValue(component);
}
public override bool IsBrowsable
{
get
{
return root.IsBrowsable;
}
}
public override bool IsLocalizable
{
get
{
return root.IsLocalizable;
}
}
public override bool IsReadOnly
{
get
{
return root.IsReadOnly;
}
}
public override string Name
{
get
{
return root.Name;
}
}
public override Type PropertyType
{
get
{
return root.PropertyType;
}
}
public override void RemoveValueChanged(object component, EventHandler handler)
{
root.RemoveValueChanged(component, handler);
}
public override void ResetValue(object component)
{
root.ResetValue(component);
}
public override void SetValue(object component, object value)
{
root.SetValue(component, value);
}
public override bool ShouldSerializeValue(object component)
{
return root.ShouldSerializeValue(component);
}
public override bool SupportsChangeEvents
{
get
{
return root.SupportsChangeEvents;
}
}
public override string ToString()
{
return root.ToString();
}
}
}

View File

@@ -1,11 +0,0 @@
// This file is used by Code Analysis to maintain SuppressMessage
// attributes that are applied to this project. Project-level
// suppressions either have no target or are given a specific target
// and scoped to a namespace, type, member, etc.
//
// To add a suppression to this file, right-click the message in the
// Error List, point to "Suppress Message(s)", and click "In Project
// Suppression File". You do not need to add suppressions to this
// file manually.
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1017:MarkAssembliesWithComVisible")]

View File

@@ -1,12 +0,0 @@
using System;
namespace LLVM.ClangTidy
{
static class GuidList
{
public const string guidClangTidyPkgString = "AE4956BE-3DB8-430E-BBAB-7E2E9A014E9C";
public const string guidClangTidyCmdSetString = "9E0F0493-6493-46DE-AEE1-ACD8F60F265E";
public static readonly Guid guidClangTidyCmdSet = new Guid(guidClangTidyCmdSetString);
};
}

View File

@@ -1,7 +0,0 @@
namespace LLVM.ClangTidy
{
static class PkgCmdIDList
{
public const uint cmdidClangTidy = 0x100;
};
}

View File

@@ -1,33 +0,0 @@
using System;
using System.Reflection;
using System.Resources;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("ClangFormat")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("LLVM")]
[assembly: AssemblyProduct("ClangFormat")]
[assembly: AssemblyCopyright("")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
[assembly: ComVisible(false)]
[assembly: CLSCompliant(false)]
[assembly: NeutralResourcesLanguage("en-US")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Revision and Build Numbers
// by using the '*' as shown below:
// FIXME: Add a way to have this generated automatically by CMake
[assembly: AssemblyVersion("1.1.0.0")]
[assembly: AssemblyFileVersion("1.1.0.0")]

View File

@@ -1,81 +0,0 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.42000
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace LLVM.ClangTidy {
using System;
/// <summary>
/// A strongly-typed resource class, for looking up localized strings, etc.
/// </summary>
// This class was auto-generated by the StronglyTypedResourceBuilder
// class via a tool like ResGen or Visual Studio.
// To add or remove a member, edit your .ResX file then rerun ResGen
// with the /str option, or rebuild your VS project.
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class Resources {
private static global::System.Resources.ResourceManager resourceMan;
private static global::System.Globalization.CultureInfo resourceCulture;
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal Resources() {
}
/// <summary>
/// Returns the cached ResourceManager instance used by this class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Resources.ResourceManager ResourceManager {
get {
if (object.ReferenceEquals(resourceMan, null)) {
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("LLVM.ClangTidy.Resources", typeof(Resources).Assembly);
resourceMan = temp;
}
return resourceMan;
}
}
/// <summary>
/// Overrides the current thread's CurrentUICulture property for all
/// resource lookups using this strongly typed resource class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Globalization.CultureInfo Culture {
get {
return resourceCulture;
}
set {
resourceCulture = value;
}
}
/// <summary>
/// Looks up a localized string similar to ---
///Checks:
///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 object [rest of string was truncated]&quot;;.
/// </summary>
internal static string ClangTidyChecks {
get {
return ResourceManager.GetString("ClangTidyChecks", resourceCulture);
}
}
}
}

View File

@@ -1,124 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
<data name="ClangTidyChecks" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>Resources\ClangTidyChecks.yaml;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;Windows-1252</value>
</data>
</root>

View File

@@ -1,321 +0,0 @@
---
Checks:
# This file should be updated when new checks are added, and eventually we should
# generate this file automatically from the .rst files in clang-tidy.
- Category: CERT Secure Coding Standards
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
Name: cert-dcl54-cpp
- Category: C++ Core Guidelines
Label: I.22 - Complex Global Initializers
Description: Checks for violations of Core Guideline I.22 - Avoid complex initializers of global objects
Name: cppcoreguidelines-interfaces-global-init
- Category: CERT Secure Coding Standards
Label: DCL50-CPP
Description: Checks for violations of CERT DCL50-CPP - Do not define a C-style variadic function
Name: cert-dcl50-cpp
- Category: C++ Core Guidelines
Label: Bounds.1 - No pointer arithmetic
Description: Checks for violations of Core Guideline Bounds.3 - Don't use pointer arithmetic. Use span<> instead.
Name: cppcoreguidelines-pro-bounds-pointer-arithmetic
- Category: C++ Core Guidelines
Label: Bounds.2 - Constant array indices
Description: Checks for violations of Core Bounds.2 - Only index into arrays using constant expressions.
Name: cppcoreguidelines-pro-bounds-constant-array-index
- Category: C++ Core Guidelines
Label: Bounds.3 - Array to Pointer Decay
Description: Checks for violations of Core Guideline Bounds.3 - No array-to-pointer decay
Name: cppcoreguidelines-pro-bounds-array-to-pointer-decay
- Category: C++ Core Guidelines
Label: const_cast (Type.3)
Description: Checks for violations of Core Guideline Type.3 - Don't use const_cast to cast away const
Name: cppcoreguidelines-pro-type-const-cast
- Category: C++ Core Guidelines
Label: C style casts (Type.4)
Description: Checks for violations of Core Guideline Type.3 - Don't use C-style (T)expression casts that would perform a static downcast, const_cast, or reinterpret_cast
Name: cppcoreguidelines-pro-type-cstyle-cast
- Category: C++ Core Guidelines
Label: reinterpret_cast (Type.1)
Description: Checks for violations of Core Guideline Type.1 - Don't use reinterpret_cast.
Name: cppcoreguidelines-pro-type-reinterpret-cast
- Category: C++ Core Guidelines
Label: Prefer dynamic_cast (Type.2)
Description: Checks for violations of Core Guideline Type.2 - Don't use static_cast downcasts. Use dynamic_cast instead.
Name: cppcoreguidelines-pro-type-static-cast-downcast
- Category: C++ Core Guidelines
Label: Member variable initialization (Type.6)
Description: Checks for violations of Core Guideline Type.6 - Always initialize a member variable.
Name: cppcoreguidelines-pro-type-member-init
- Category: C++ Core Guidelines
Label: Avoid unions (Type.7)
Description: Checks for violations of Core Guideline Type.7 - Avoid accessing members of raw unions. Use variant instead.
Name: cppcoreguidelines-pro-type-union-access
- Category: C++ Core Guidelines
Label: Don't use varargs (Type.8)
Description: Checks for violations of Core Guideline Type.8 - Avoid reading varargs or passing vararg arguments. Prefer variadic templates instead.
Name: cppcoreguidelines-pro-type-vararg
- Category: C++ Core Guidelines
Label: Don't slice (ES.63 & C.145)
Description: Checks for violations of Core Guidelines ES.63 (Don't slice) and C.145 (Access polymorphic objects through pointers and references)
Name: cppcoreguidelines-slicing
- Category: C++ Core Guidelines
Label: Detect unsafe special functions (C.21)
Description: Checks for violations of Core Guidelines C.21 - If you define or =delete any default operation, define or =delete them all.
Name: cppcoreguidelines-special-member-functions
- Category: Google Style Guide
Label: Forbid explicitly parameterized make_pair
Description:
Name: google-build-explicit-make-pair
- Category: Google Style Guide
Label: Anonymous namespace in headers
Description:
Name: google-build-namespaces
- Category: Google Style Guide
Label: Find using namespace directives
Description:
Name: google-build-using-namespace
- Category: Google Style Guide
Label: Default arguments in virtual methods
Description:
Name: google-default-arguments
- Category: Google Style Guide
Label: explicit constructors
Description:
Name: google-explicit-constructor
- Category: Google Style Guide
Label: Global namespace pollution in headers
Description:
Name: google-global-names-in-headers
- Category: Google Style Guide
Label: Braces around statements
Description:
Name: google-readability-braces-around-statements
- Category: Google Style Guide
Label: No C-style casts
Description:
Name: google-readability-casting
- Category: Google Style Guide
Label: Find large functions
Description:
Name: google-readability-function-size
- Category: Google Style Guide
Label: Namespace closing comments
Description:
Name: google-readability-namespace-comments
- Category: Google Style Guide
Label: Find unnecessary calls to .get()
Description:
Name: google-readability-redundant-smartptr-get
- Category: Google Style Guide
Label: Find noncomformant TODO comments
Description:
Name: google-readability-todo
- Category: Google Style Guide
Label: Find implementation-specific integral types
Description:
Name: google-runtime-int
- Category: Google Style Guide
Label: Find const string references
Description:
Name: google-runtime-member-string-references
- Category: Google Style Guide
Label: Find zero-length memsets
Description:
Name: google-runtime-memset
- Category: Google Style Guide
Label: Find overloads of operator&
Description:
Name: google-runtime-operator
- Category: Google Style Guide
Label: Check usage of non-const references
Description:
Name: google-runtime-references
- Category: LLVM Style Guide
Label: LLVM header guards
Description:
Name: llvm-header-guard
- Category: LLVM Style Guide
Label: LLVM include order
Description:
Name: llvm-include-order
- Category: LLVM Style Guide
Label: LLVM namespace comments
Description:
Name: llvm-namespace-comment
- Category: LLVM Style Guide
Label: Find local twines
Description:
Name: llvm-twine-local
- Category: Clang Diagnostics
Label: Warnings
Description:
Name: clang-diagnostic-warning
- Category: Clang Diagnostics
Label: Errors
Description:
Name: clang-diagnostic-error
- Category: Clang Diagnostics
Label: Unknown
Description:
Name: clang-diagnostic-unknown
- Category: Miscellaneous
Label: Validate argument comments
Description:
Name: misc-argument-comment
- Category: Miscellaneous
Label: Side effects in assert()
Description:
Name: misc-assert-side-effect
- Category: Miscellaneous
Label: bool / pointer implicit conversions
Description:
Name: misc-bool-pointer-implicit-conversion
- Category: Miscellaneous
Label: Dangling handles
Description:
Name: misc-dangling-handle
- Category: Miscellaneous
Label: Definitions in headers
Description:
Name: misc-definitions-in-headers
- Category: Miscellaneous
Label: Type mismatch in fold operations
Description:
Name: misc-fold-init-type
- Category: Miscellaneous
Label: Forward declaration namespace
Description:
Name: misc-forward-declaration-namespace
- Category: Miscellaneous
Label: Inaccurate erase
Description:
Name: misc-inaccurate-erase
- Category: Miscellaneous
Label: Incorrect rounding
Description:
Name: misc-incorrect-roundings
- Category: Miscellaneous
Label: Inefficient STL algorithms
Description:
Name: misc-inefficient-algorithm
- Category: Miscellaneous
Label: Macro parentheses
Description:
Name: misc-macro-parentheses
- Category: Miscellaneous
Label: Macro repeated side effects
Description:
Name: misc-macro-repeated-side-effects
- Category: Miscellaneous
Label: Misplaced const
Description:
Name: misc-misplaced-const
- Category: Miscellaneous
Label: Misplaced widening casts
Description:
Name: misc-misplaced-widening-cast
- Category: Miscellaneous
Label: Move constructor const arguments
Description:
Name: misc-move-const-arg
- Category: Miscellaneous
Label: Move constructor initialization
Description:
Name: misc-move-constructor-init
- Category: Miscellaneous
Label: Multi-statement macros
Description:
Name: misc-multiple-statement-macro
- Category: Miscellaneous
Label: Verify new / delete overloads
Description:
Name: misc-new-delete-overloads
- Category: Miscellaneous
Label: Ensure move constructors are noexcept
Description:
Name: misc-noexcept-move-constructor
- Category: Miscellaneous
Label: Copying of non-copyable objects
Description:
Name: misc-non-copyable-objects
- Category: Miscellaneous
Label: Find redundant expressions
Description:
Name: misc-redundant-expression
- Category: Miscellaneous
Label: sizeof() on stl containers
Description:
Name: misc-sizeof-container
- Category: Miscellaneous
Label: Suspicious sizeof() usage
Description:
Name: misc-sizeof-expression
- Category: Miscellaneous
Label: Replace assert with static_assert
Description:
Name: misc-static-assert
- Category: Miscellaneous
Label: Suspicious string constructor
Description:
Name: misc-string-constructor
- Category: Miscellaneous
Label: String integer assignment
Description:
Name: misc-string-integer-assignment
- Category: Miscellaneous
Label: String literal with embedded null
Description:
Name: misc-string-literal-with-embedded-nul
- Category: Miscellaneous
Label: Suspicious missing comma
Description:
Name: misc-suspicious-missing-comma
- Category: Miscellaneous
Label: Suspicious semicolon
Description:
Name: misc-suspicious-semicolon
- Category: Miscellaneous
Label: Suspicious string compare
Description:
Name: misc-suspicious-string-compare
- Category: Miscellaneous
Label: Swapped arguments
Description:
Name: misc-swapped-arguments
- Category: Miscellaneous
Label: Throw by value / catch by reference
Description:
Name: misc-throw-by-value-catch-by-reference
- Category: Miscellaneous
Label: Unconventional operator=()
Description:
Name: misc-unconventional-assign-operator
- Category: Miscellaneous
Label: Undelegated constructor
Description:
Name: misc-undelegated-constructor
- Category: Miscellaneous
Label: unique_ptr<> reset / release
Description:
Name: misc-uniqueptr-reset-release
- Category: Miscellaneous
Label: Unused Alias Decls
Description:
Name: misc-unused-alias-decls
- Category: Miscellaneous
Label: Unused Params
Description:
Name: misc-unused-parameters
- Category: Miscellaneous
Label: Unused Raii
Description:
Name: misc-unused-raii
- Category: Miscellaneous
Label: Unused Using Decls
Description:
Name: misc-unused-using-decls
- Category: Miscellaneous
Label: Virtual Near Miss
Description:
Name: misc-virtual-near-miss
...

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -1,35 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
namespace LLVM.ClangTidy
{
static class Utility
{
public static IEnumerable<string> SplitPath(string FileOrDir)
{
string P = Path.GetDirectoryName(FileOrDir);
do
{
yield return P;
P = Path.GetDirectoryName(P);
} while (P != null);
}
public static bool HasClangTidyFile(string Folder)
{
string ClangTidy = Path.Combine(Folder, ".clang-tidy");
return File.Exists(ClangTidy);
}
public static bool MatchWildcardString(string Value, string Pattern)
{
string RE = Regex.Escape(Pattern).Replace(@"\*", ".*");
return Regex.IsMatch(Value, RE);
}
}
}

View File

@@ -1,130 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="110" xml:space="preserve">
<value>ClangTidy</value>
</data>
<data name="112" xml:space="preserve">
<value>Analyzes code by calling the clang-tidy executable.</value>
</data>
<assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
<data name="400" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>Resources\Package.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
</root>

View File

@@ -1,63 +0,0 @@
==============================================================================
LLVM Release License
==============================================================================
University of Illinois/NCSA
Open Source License
Copyright (c) 2007-2016 University of Illinois at Urbana-Champaign.
All rights reserved.
Developed by:
LLVM Team
University of Illinois at Urbana-Champaign
http://llvm.org
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal with
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimers.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimers in the
documentation and/or other materials provided with the distribution.
* Neither the names of the LLVM Team, University of Illinois at
Urbana-Champaign, nor the names of its contributors may be used to
endorse or promote products derived from this Software without specific
prior written permission.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE
SOFTWARE.
==============================================================================
The LLVM software contains code written by third parties. Such software will
have its own individual LICENSE.TXT file in the directory in which it appears.
This file will describe the copyrights, license, and restrictions which apply
to that code.
The disclaimer of warranty in the University of Illinois Open Source License
applies to all code in the LLVM Distribution, and nothing in any of the
other licenses gives permission to use the names of the LLVM Team or the
University of Illinois to endorse or promote products derived from this
Software.
The following pieces of software have additional or alternate copyrights,
licenses, and/or restrictions:
Program Directory
------- ---------
<none yet>

View File

@@ -1,6 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Brutal.Dev.StrongNameSigner" version="1.8.0" targetFramework="net45" />
<package id="YamlDotNet" version="3.3.0" targetFramework="net45" />
<package id="YamlDotNet.Dynamic" version="3.2.3" targetFramework="net45" />
</packages>

View File

@@ -1,36 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Vsix Version="1.0" xmlns="http://schemas.microsoft.com/developer/vsx-schema/2010">
<Identifier Id="405594C3-042A-4155-B9A6-E25DAB8B1924">
<Name>ClangFormat</Name>
<Author>LLVM</Author>
<Version>4.0.0</Version>
<Description xml:space="preserve">A static analysis tool for C/C++ code.</Description>
<Locale>1033</Locale>
<MoreInfoUrl>http://clang.llvm.org/extra/clang-tidy/</MoreInfoUrl>
<License>license.txt</License>
<InstalledByMsi>false</InstalledByMsi>
<SupportedProducts>
<VisualStudio Version="10.0">
<Edition>Pro</Edition>
</VisualStudio>
<VisualStudio Version="11.0">
<Edition>Pro</Edition>
</VisualStudio>
<VisualStudio Version="12.0">
<Edition>Pro</Edition>
</VisualStudio>
<VisualStudio Version="14.0">
<Edition>Pro</Edition>
</VisualStudio>
</SupportedProducts>
<SupportedFrameworkRuntimeEdition MinVersion="4.0" MaxVersion="4.0" />
</Identifier>
<References>
<Reference Id="Microsoft.VisualStudio.MPF" MinVersion="10.0">
<Name>Visual Studio MPF</Name>
</Reference>
</References>
<Content>
<VsPackage>|%CurrentProject%;PkgdefProjectOutputGroup|</VsPackage>
</Content>
</Vsix>

View File

@@ -1,17 +0,0 @@
This directory contains a VSPackage project to generate a Visual Studio extension
for clang-tidy.
Build prerequisites are:
- Visual Studio 2013 Professional
- Visual Studio 2013 SDK
- Visual Studio 2010 Professional (?)
- Visual Studio 2010 SDK (?)
The extension is built using CMake by setting BUILD_CLANG_TIDY_VS_PLUGIN=ON
when configuring a Clang build, and building the clang_tidy_vsix target.
The CMake build will copy clang-tidy.exe and LICENSE.TXT into the ClangTidy/
directory so they can be bundled with the plug-in, as well as creating
ClangTidy/source.extension.vsixmanifest. Once the plug-in has been built with
CMake once, it can be built manually from the ClangTidy.sln solution in Visual
Studio.

View File

@@ -1,36 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Vsix Version="1.0" xmlns="http://schemas.microsoft.com/developer/vsx-schema/2010">
<Identifier Id="405594C3-042A-4155-B9A6-E25DAB8B1924">
<Name>ClangTidy</Name>
<Author>LLVM</Author>
<Version>@CLANG_TIDY_VS_VERSION@</Version>
<Description xml:space="preserve">A static analysis tool for C/C++ code.</Description>
<Locale>1033</Locale>
<MoreInfoUrl>http://clang.llvm.org/extra/clang-tidy/</MoreInfoUrl>
<License>license.txt</License>
<InstalledByMsi>false</InstalledByMsi>
<SupportedProducts>
<VisualStudio Version="10.0">
<Edition>Pro</Edition>
</VisualStudio>
<VisualStudio Version="11.0">
<Edition>Pro</Edition>
</VisualStudio>
<VisualStudio Version="12.0">
<Edition>Pro</Edition>
</VisualStudio>
<VisualStudio Version="14.0">
<Edition>Pro</Edition>
</VisualStudio>
</SupportedProducts>
<SupportedFrameworkRuntimeEdition MinVersion="4.0" MaxVersion="4.0" />
</Identifier>
<References>
<Reference Id="Microsoft.VisualStudio.MPF" MinVersion="10.0">
<Name>Visual Studio MPF</Name>
</Reference>
</References>
<Content>
<VsPackage>|%CurrentProject%;PkgdefProjectOutputGroup|</VsPackage>
</Content>
</Vsix>

View File

@@ -1,44 +0,0 @@
set(LLVM_LINK_COMPONENTS
Support
)
add_clang_library(clangTidy
ClangTidy.cpp
ClangTidyModule.cpp
ClangTidyDiagnosticConsumer.cpp
ClangTidyOptions.cpp
DEPENDS
ClangSACheckers
LINK_LIBS
clangAST
clangASTMatchers
clangBasic
clangFormat
clangFrontend
clangLex
clangRewrite
clangSema
clangStaticAnalyzerCore
clangStaticAnalyzerFrontend
clangTooling
clangToolingCore
)
add_subdirectory(android)
add_subdirectory(boost)
add_subdirectory(bugprone)
add_subdirectory(cert)
add_subdirectory(cppcoreguidelines)
add_subdirectory(google)
add_subdirectory(hicpp)
add_subdirectory(llvm)
add_subdirectory(misc)
add_subdirectory(modernize)
add_subdirectory(mpi)
add_subdirectory(performance)
add_subdirectory(plugin)
add_subdirectory(readability)
add_subdirectory(tool)
add_subdirectory(utils)

View File

@@ -1,588 +0,0 @@
//===--- tools/extra/clang-tidy/ClangTidy.cpp - Clang tidy tool -----------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
///
/// \file This file implements a clang-tidy tool.
///
/// This tool uses the Clang Tooling infrastructure, see
/// http://clang.llvm.org/docs/HowToSetupToolingForLLVM.html
/// for details on setting it up with LLVM source tree.
///
//===----------------------------------------------------------------------===//
#include "ClangTidy.h"
#include "ClangTidyDiagnosticConsumer.h"
#include "ClangTidyModuleRegistry.h"
#include "clang/AST/ASTConsumer.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/Decl.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/Format/Format.h"
#include "clang/Frontend/ASTConsumers.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Frontend/FrontendActions.h"
#include "clang/Frontend/FrontendDiagnostic.h"
#include "clang/Frontend/MultiplexConsumer.h"
#include "clang/Frontend/TextDiagnosticPrinter.h"
#include "clang/Lex/PPCallbacks.h"
#include "clang/Lex/Preprocessor.h"
#include "clang/Rewrite/Frontend/FixItRewriter.h"
#include "clang/Rewrite/Frontend/FrontendActions.h"
#include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h"
#include "clang/StaticAnalyzer/Frontend/AnalysisConsumer.h"
#include "clang/Tooling/DiagnosticsYaml.h"
#include "clang/Tooling/Refactoring.h"
#include "clang/Tooling/ReplacementsYaml.h"
#include "clang/Tooling/Tooling.h"
#include "llvm/Support/Process.h"
#include "llvm/Support/Signals.h"
#include <algorithm>
#include <utility>
using namespace clang::ast_matchers;
using namespace clang::driver;
using namespace clang::tooling;
using namespace llvm;
LLVM_INSTANTIATE_REGISTRY(clang::tidy::ClangTidyModuleRegistry)
namespace clang {
namespace tidy {
namespace {
static const char *AnalyzerCheckNamePrefix = "clang-analyzer-";
class AnalyzerDiagnosticConsumer : public ento::PathDiagnosticConsumer {
public:
AnalyzerDiagnosticConsumer(ClangTidyContext &Context) : Context(Context) {}
void FlushDiagnosticsImpl(std::vector<const ento::PathDiagnostic *> &Diags,
FilesMade *filesMade) override {
for (const ento::PathDiagnostic *PD : Diags) {
SmallString<64> CheckName(AnalyzerCheckNamePrefix);
CheckName += PD->getCheckName();
Context.diag(CheckName, PD->getLocation().asLocation(),
PD->getShortDescription())
<< PD->path.back()->getRanges();
for (const auto &DiagPiece :
PD->path.flatten(/*ShouldFlattenMacros=*/true)) {
Context.diag(CheckName, DiagPiece->getLocation().asLocation(),
DiagPiece->getString(), DiagnosticIDs::Note)
<< DiagPiece->getRanges();
}
}
}
StringRef getName() const override { return "ClangTidyDiags"; }
bool supportsLogicalOpControlFlow() const override { return true; }
bool supportsCrossFileDiagnostics() const override { return true; }
private:
ClangTidyContext &Context;
};
class ErrorReporter {
public:
ErrorReporter(ClangTidyContext &Context, bool ApplyFixes)
: Files(FileSystemOptions()), DiagOpts(new DiagnosticOptions()),
DiagPrinter(new TextDiagnosticPrinter(llvm::outs(), &*DiagOpts)),
Diags(IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs), &*DiagOpts,
DiagPrinter),
SourceMgr(Diags, Files), Context(Context), ApplyFixes(ApplyFixes),
TotalFixes(0), AppliedFixes(0), WarningsAsErrors(0) {
DiagOpts->ShowColors = llvm::sys::Process::StandardOutHasColors();
DiagPrinter->BeginSourceFile(LangOpts);
}
SourceManager &getSourceManager() { return SourceMgr; }
void reportDiagnostic(const ClangTidyError &Error) {
const tooling::DiagnosticMessage &Message = Error.Message;
SourceLocation Loc = getLocation(Message.FilePath, Message.FileOffset);
// Contains a pair for each attempted fix: location and whether the fix was
// applied successfully.
SmallVector<std::pair<SourceLocation, bool>, 4> FixLocations;
{
auto Level = static_cast<DiagnosticsEngine::Level>(Error.DiagLevel);
std::string Name = Error.DiagnosticName;
if (Error.IsWarningAsError) {
Name += ",-warnings-as-errors";
Level = DiagnosticsEngine::Error;
WarningsAsErrors++;
}
auto Diag = Diags.Report(Loc, Diags.getCustomDiagID(Level, "%0 [%1]"))
<< Message.Message << Name;
for (const auto &FileAndReplacements : Error.Fix) {
for (const auto &Repl : FileAndReplacements.second) {
// Retrieve the source range for applicable fixes. Macro definitions
// on the command line have locations in a virtual buffer and don't
// have valid file paths and are therefore not applicable.
SourceRange Range;
SourceLocation FixLoc;
++TotalFixes;
bool CanBeApplied = false;
if (Repl.isApplicable()) {
SmallString<128> FixAbsoluteFilePath = Repl.getFilePath();
Files.makeAbsolutePath(FixAbsoluteFilePath);
if (ApplyFixes) {
tooling::Replacement R(FixAbsoluteFilePath, Repl.getOffset(),
Repl.getLength(),
Repl.getReplacementText());
Replacements &Replacements = FileReplacements[R.getFilePath()];
llvm::Error Err = Replacements.add(R);
if (Err) {
// FIXME: Implement better conflict handling.
llvm::errs() << "Trying to resolve conflict: "
<< llvm::toString(std::move(Err)) << "\n";
unsigned NewOffset =
Replacements.getShiftedCodePosition(R.getOffset());
unsigned NewLength = Replacements.getShiftedCodePosition(
R.getOffset() + R.getLength()) -
NewOffset;
if (NewLength == R.getLength()) {
R = Replacement(R.getFilePath(), NewOffset, NewLength,
R.getReplacementText());
Replacements = Replacements.merge(tooling::Replacements(R));
CanBeApplied = true;
++AppliedFixes;
} else {
llvm::errs()
<< "Can't resolve conflict, skipping the replacement.\n";
}
} else {
CanBeApplied = true;
++AppliedFixes;
}
}
FixLoc = getLocation(FixAbsoluteFilePath, Repl.getOffset());
SourceLocation FixEndLoc =
FixLoc.getLocWithOffset(Repl.getLength());
Range = SourceRange(FixLoc, FixEndLoc);
Diag << FixItHint::CreateReplacement(Range,
Repl.getReplacementText());
}
if (ApplyFixes)
FixLocations.push_back(std::make_pair(FixLoc, CanBeApplied));
}
}
}
for (auto Fix : FixLocations) {
Diags.Report(Fix.first, Fix.second ? diag::note_fixit_applied
: diag::note_fixit_failed);
}
for (const auto &Note : Error.Notes)
reportNote(Note);
}
void Finish() {
if (ApplyFixes && TotalFixes > 0) {
Rewriter Rewrite(SourceMgr, LangOpts);
for (const auto &FileAndReplacements : FileReplacements) {
StringRef File = FileAndReplacements.first();
llvm::ErrorOr<std::unique_ptr<MemoryBuffer>> Buffer =
SourceMgr.getFileManager().getBufferForFile(File);
if (!Buffer) {
llvm::errs() << "Can't get buffer for file " << File << ": "
<< Buffer.getError().message() << "\n";
// FIXME: Maybe don't apply fixes for other files as well.
continue;
}
StringRef Code = Buffer.get()->getBuffer();
auto Style = format::getStyle(
*Context.getOptionsForFile(File).FormatStyle, File, "none");
if (!Style) {
llvm::errs() << llvm::toString(Style.takeError()) << "\n";
continue;
}
llvm::Expected<tooling::Replacements> Replacements =
format::cleanupAroundReplacements(Code, FileAndReplacements.second,
*Style);
if (!Replacements) {
llvm::errs() << llvm::toString(Replacements.takeError()) << "\n";
continue;
}
if (llvm::Expected<tooling::Replacements> FormattedReplacements =
format::formatReplacements(Code, *Replacements, *Style)) {
Replacements = std::move(FormattedReplacements);
if (!Replacements)
llvm_unreachable("!Replacements");
} else {
llvm::errs() << llvm::toString(FormattedReplacements.takeError())
<< ". Skipping formatting.\n";
}
if (!tooling::applyAllReplacements(Replacements.get(), Rewrite)) {
llvm::errs() << "Can't apply replacements for file " << File << "\n";
}
}
if (Rewrite.overwriteChangedFiles()) {
llvm::errs() << "clang-tidy failed to apply suggested fixes.\n";
} else {
llvm::errs() << "clang-tidy applied " << AppliedFixes << " of "
<< TotalFixes << " suggested fixes.\n";
}
}
}
unsigned getWarningsAsErrorsCount() const { return WarningsAsErrors; }
private:
SourceLocation getLocation(StringRef FilePath, unsigned Offset) {
if (FilePath.empty())
return SourceLocation();
const FileEntry *File = SourceMgr.getFileManager().getFile(FilePath);
FileID ID = SourceMgr.getOrCreateFileID(File, SrcMgr::C_User);
return SourceMgr.getLocForStartOfFile(ID).getLocWithOffset(Offset);
}
void reportNote(const tooling::DiagnosticMessage &Message) {
SourceLocation Loc = getLocation(Message.FilePath, Message.FileOffset);
Diags.Report(Loc, Diags.getCustomDiagID(DiagnosticsEngine::Note, "%0"))
<< Message.Message;
}
FileManager Files;
LangOptions LangOpts; // FIXME: use langopts from each original file
IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts;
DiagnosticConsumer *DiagPrinter;
DiagnosticsEngine Diags;
SourceManager SourceMgr;
llvm::StringMap<Replacements> FileReplacements;
ClangTidyContext &Context;
bool ApplyFixes;
unsigned TotalFixes;
unsigned AppliedFixes;
unsigned WarningsAsErrors;
};
class ClangTidyASTConsumer : public MultiplexConsumer {
public:
ClangTidyASTConsumer(std::vector<std::unique_ptr<ASTConsumer>> Consumers,
std::unique_ptr<ast_matchers::MatchFinder> Finder,
std::vector<std::unique_ptr<ClangTidyCheck>> Checks)
: MultiplexConsumer(std::move(Consumers)), Finder(std::move(Finder)),
Checks(std::move(Checks)) {}
private:
std::unique_ptr<ast_matchers::MatchFinder> Finder;
std::vector<std::unique_ptr<ClangTidyCheck>> Checks;
};
} // namespace
ClangTidyASTConsumerFactory::ClangTidyASTConsumerFactory(
ClangTidyContext &Context)
: Context(Context), CheckFactories(new ClangTidyCheckFactories) {
for (ClangTidyModuleRegistry::iterator I = ClangTidyModuleRegistry::begin(),
E = ClangTidyModuleRegistry::end();
I != E; ++I) {
std::unique_ptr<ClangTidyModule> Module(I->instantiate());
Module->addCheckFactories(*CheckFactories);
}
}
static void setStaticAnalyzerCheckerOpts(const ClangTidyOptions &Opts,
AnalyzerOptionsRef AnalyzerOptions) {
StringRef AnalyzerPrefix(AnalyzerCheckNamePrefix);
for (const auto &Opt : Opts.CheckOptions) {
StringRef OptName(Opt.first);
if (!OptName.startswith(AnalyzerPrefix))
continue;
AnalyzerOptions->Config[OptName.substr(AnalyzerPrefix.size())] = Opt.second;
}
}
typedef std::vector<std::pair<std::string, bool>> CheckersList;
static CheckersList getCheckersControlList(ClangTidyContext &Context) {
CheckersList List;
const auto &RegisteredCheckers =
AnalyzerOptions::getRegisteredCheckers(/*IncludeExperimental=*/false);
bool AnalyzerChecksEnabled = false;
for (StringRef CheckName : RegisteredCheckers) {
std::string ClangTidyCheckName((AnalyzerCheckNamePrefix + CheckName).str());
AnalyzerChecksEnabled |= Context.isCheckEnabled(ClangTidyCheckName);
}
if (!AnalyzerChecksEnabled)
return List;
// List all static analyzer checkers that our filter enables.
//
// Always add all core checkers if any other static analyzer check is enabled.
// This is currently necessary, as other path sensitive checks rely on the
// core checkers.
for (StringRef CheckName : RegisteredCheckers) {
std::string ClangTidyCheckName((AnalyzerCheckNamePrefix + CheckName).str());
if (CheckName.startswith("core") ||
Context.isCheckEnabled(ClangTidyCheckName)) {
List.emplace_back(CheckName, true);
}
}
return List;
}
std::unique_ptr<clang::ASTConsumer>
ClangTidyASTConsumerFactory::CreateASTConsumer(
clang::CompilerInstance &Compiler, StringRef File) {
// FIXME: Move this to a separate method, so that CreateASTConsumer doesn't
// modify Compiler.
Context.setSourceManager(&Compiler.getSourceManager());
Context.setCurrentFile(File);
Context.setASTContext(&Compiler.getASTContext());
auto WorkingDir = Compiler.getSourceManager()
.getFileManager()
.getVirtualFileSystem()
->getCurrentWorkingDirectory();
if (WorkingDir)
Context.setCurrentBuildDirectory(WorkingDir.get());
std::vector<std::unique_ptr<ClangTidyCheck>> Checks;
CheckFactories->createChecks(&Context, Checks);
ast_matchers::MatchFinder::MatchFinderOptions FinderOptions;
if (auto *P = Context.getCheckProfileData())
FinderOptions.CheckProfiling.emplace(P->Records);
std::unique_ptr<ast_matchers::MatchFinder> Finder(
new ast_matchers::MatchFinder(std::move(FinderOptions)));
for (auto &Check : Checks) {
Check->registerMatchers(&*Finder);
Check->registerPPCallbacks(Compiler);
}
std::vector<std::unique_ptr<ASTConsumer>> Consumers;
if (!Checks.empty())
Consumers.push_back(Finder->newASTConsumer());
AnalyzerOptionsRef AnalyzerOptions = Compiler.getAnalyzerOpts();
// FIXME: Remove this option once clang's cfg-temporary-dtors option defaults
// to true.
AnalyzerOptions->Config["cfg-temporary-dtors"] =
Context.getOptions().AnalyzeTemporaryDtors ? "true" : "false";
AnalyzerOptions->CheckersControlList = getCheckersControlList(Context);
if (!AnalyzerOptions->CheckersControlList.empty()) {
setStaticAnalyzerCheckerOpts(Context.getOptions(), AnalyzerOptions);
AnalyzerOptions->AnalysisStoreOpt = RegionStoreModel;
AnalyzerOptions->AnalysisDiagOpt = PD_NONE;
AnalyzerOptions->AnalyzeNestedBlocks = true;
AnalyzerOptions->eagerlyAssumeBinOpBifurcation = true;
std::unique_ptr<ento::AnalysisASTConsumer> AnalysisConsumer =
ento::CreateAnalysisConsumer(Compiler);
AnalysisConsumer->AddDiagnosticConsumer(
new AnalyzerDiagnosticConsumer(Context));
Consumers.push_back(std::move(AnalysisConsumer));
}
return llvm::make_unique<ClangTidyASTConsumer>(
std::move(Consumers), std::move(Finder), std::move(Checks));
}
std::vector<std::string> ClangTidyASTConsumerFactory::getCheckNames() {
std::vector<std::string> CheckNames;
for (const auto &CheckFactory : *CheckFactories) {
if (Context.isCheckEnabled(CheckFactory.first))
CheckNames.push_back(CheckFactory.first);
}
for (const auto &AnalyzerCheck : getCheckersControlList(Context))
CheckNames.push_back(AnalyzerCheckNamePrefix + AnalyzerCheck.first);
std::sort(CheckNames.begin(), CheckNames.end());
return CheckNames;
}
ClangTidyOptions::OptionMap ClangTidyASTConsumerFactory::getCheckOptions() {
ClangTidyOptions::OptionMap Options;
std::vector<std::unique_ptr<ClangTidyCheck>> Checks;
CheckFactories->createChecks(&Context, Checks);
for (const auto &Check : Checks)
Check->storeOptions(Options);
return Options;
}
DiagnosticBuilder ClangTidyCheck::diag(SourceLocation Loc, StringRef Message,
DiagnosticIDs::Level Level) {
return Context->diag(CheckName, Loc, Message, Level);
}
void ClangTidyCheck::run(const ast_matchers::MatchFinder::MatchResult &Result) {
Context->setSourceManager(Result.SourceManager);
check(Result);
}
OptionsView::OptionsView(StringRef CheckName,
const ClangTidyOptions::OptionMap &CheckOptions)
: NamePrefix(CheckName.str() + "."), CheckOptions(CheckOptions) {}
std::string OptionsView::get(StringRef LocalName, StringRef Default) const {
const auto &Iter = CheckOptions.find(NamePrefix + LocalName.str());
if (Iter != CheckOptions.end())
return Iter->second;
return Default;
}
std::string OptionsView::getLocalOrGlobal(StringRef LocalName,
StringRef Default) const {
auto Iter = CheckOptions.find(NamePrefix + LocalName.str());
if (Iter != CheckOptions.end())
return Iter->second;
// Fallback to global setting, if present.
Iter = CheckOptions.find(LocalName.str());
if (Iter != CheckOptions.end())
return Iter->second;
return Default;
}
void OptionsView::store(ClangTidyOptions::OptionMap &Options,
StringRef LocalName, StringRef Value) const {
Options[NamePrefix + LocalName.str()] = Value;
}
void OptionsView::store(ClangTidyOptions::OptionMap &Options,
StringRef LocalName, int64_t Value) const {
store(Options, LocalName, llvm::itostr(Value));
}
std::vector<std::string> getCheckNames(const ClangTidyOptions &Options) {
clang::tidy::ClangTidyContext Context(
llvm::make_unique<DefaultOptionsProvider>(ClangTidyGlobalOptions(),
Options));
ClangTidyASTConsumerFactory Factory(Context);
return Factory.getCheckNames();
}
ClangTidyOptions::OptionMap getCheckOptions(const ClangTidyOptions &Options) {
clang::tidy::ClangTidyContext Context(
llvm::make_unique<DefaultOptionsProvider>(ClangTidyGlobalOptions(),
Options));
ClangTidyASTConsumerFactory Factory(Context);
return Factory.getCheckOptions();
}
void runClangTidy(clang::tidy::ClangTidyContext &Context,
const CompilationDatabase &Compilations,
ArrayRef<std::string> InputFiles, ProfileData *Profile) {
ClangTool Tool(Compilations, InputFiles);
// Add extra arguments passed by the clang-tidy command-line.
ArgumentsAdjuster PerFileExtraArgumentsInserter =
[&Context](const CommandLineArguments &Args, StringRef Filename) {
ClangTidyOptions Opts = Context.getOptionsForFile(Filename);
CommandLineArguments AdjustedArgs = Args;
if (Opts.ExtraArgsBefore) {
auto I = AdjustedArgs.begin();
if (I != AdjustedArgs.end() && !StringRef(*I).startswith("-"))
++I; // Skip compiler binary name, if it is there.
AdjustedArgs.insert(I, Opts.ExtraArgsBefore->begin(),
Opts.ExtraArgsBefore->end());
}
if (Opts.ExtraArgs)
AdjustedArgs.insert(AdjustedArgs.end(), Opts.ExtraArgs->begin(),
Opts.ExtraArgs->end());
return AdjustedArgs;
};
// Remove plugins arguments.
ArgumentsAdjuster PluginArgumentsRemover =
[](const CommandLineArguments &Args, StringRef Filename) {
CommandLineArguments AdjustedArgs;
for (size_t I = 0, E = Args.size(); I < E; ++I) {
if (I + 4 < Args.size() && Args[I] == "-Xclang" &&
(Args[I + 1] == "-load" || Args[I + 1] == "-add-plugin" ||
StringRef(Args[I + 1]).startswith("-plugin-arg-")) &&
Args[I + 2] == "-Xclang") {
I += 3;
} else
AdjustedArgs.push_back(Args[I]);
}
return AdjustedArgs;
};
Tool.appendArgumentsAdjuster(PerFileExtraArgumentsInserter);
Tool.appendArgumentsAdjuster(PluginArgumentsRemover);
if (Profile)
Context.setCheckProfileData(Profile);
ClangTidyDiagnosticConsumer DiagConsumer(Context);
Tool.setDiagnosticConsumer(&DiagConsumer);
class ActionFactory : public FrontendActionFactory {
public:
ActionFactory(ClangTidyContext &Context) : ConsumerFactory(Context) {}
FrontendAction *create() override { return new Action(&ConsumerFactory); }
private:
class Action : public ASTFrontendAction {
public:
Action(ClangTidyASTConsumerFactory *Factory) : Factory(Factory) {}
std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &Compiler,
StringRef File) override {
return Factory->CreateASTConsumer(Compiler, File);
}
private:
ClangTidyASTConsumerFactory *Factory;
};
ClangTidyASTConsumerFactory ConsumerFactory;
};
ActionFactory Factory(Context);
Tool.run(&Factory);
}
void handleErrors(ClangTidyContext &Context, bool Fix,
unsigned &WarningsAsErrorsCount) {
ErrorReporter Reporter(Context, Fix);
vfs::FileSystem &FileSystem =
*Reporter.getSourceManager().getFileManager().getVirtualFileSystem();
auto InitialWorkingDir = FileSystem.getCurrentWorkingDirectory();
if (!InitialWorkingDir)
llvm::report_fatal_error("Cannot get current working path.");
for (const ClangTidyError &Error : Context.getErrors()) {
if (!Error.BuildDirectory.empty()) {
// By default, the working directory of file system is the current
// clang-tidy running directory.
//
// Change the directory to the one used during the analysis.
FileSystem.setCurrentWorkingDirectory(Error.BuildDirectory);
}
Reporter.reportDiagnostic(Error);
// Return to the initial directory to correctly resolve next Error.
FileSystem.setCurrentWorkingDirectory(InitialWorkingDir.get());
}
Reporter.Finish();
WarningsAsErrorsCount += Reporter.getWarningsAsErrorsCount();
}
void exportReplacements(const llvm::StringRef MainFilePath,
const std::vector<ClangTidyError> &Errors,
raw_ostream &OS) {
TranslationUnitDiagnostics TUD;
TUD.MainSourceFile = MainFilePath;
for (const auto &Error : Errors) {
tooling::Diagnostic Diag = Error;
TUD.Diagnostics.insert(TUD.Diagnostics.end(), Diag);
}
yaml::Output YAML(OS);
YAML << TUD;
}
} // namespace tidy
} // namespace clang

View File

@@ -1,250 +0,0 @@
//===--- ClangTidy.h - clang-tidy -------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CLANGTIDY_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CLANGTIDY_H
#include "ClangTidyDiagnosticConsumer.h"
#include "ClangTidyOptions.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/Basic/Diagnostic.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Tooling/Refactoring.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/Support/raw_ostream.h"
#include <memory>
#include <type_traits>
#include <vector>
namespace clang {
class CompilerInstance;
namespace tooling {
class CompilationDatabase;
}
namespace tidy {
/// \brief Provides access to the ``ClangTidyCheck`` options via check-local
/// names.
///
/// Methods of this class prepend ``CheckName + "."`` to translate check-local
/// option names to global option names.
class OptionsView {
public:
/// \brief Initializes the instance using \p CheckName + "." as a prefix.
OptionsView(StringRef CheckName,
const ClangTidyOptions::OptionMap &CheckOptions);
/// \brief Read a named option from the ``Context``.
///
/// Reads the option with the check-local name \p LocalName from the
/// ``CheckOptions``. If the corresponding key is not present, returns
/// \p Default.
std::string get(StringRef LocalName, StringRef Default) const;
/// \brief Read a named option from the ``Context``.
///
/// Reads the option with the check-local name \p LocalName from local or
/// global ``CheckOptions``. Gets local option first. If local is not present,
/// falls back to get global option. If global option is not present either,
/// returns Default.
std::string getLocalOrGlobal(StringRef LocalName, StringRef Default) const;
/// \brief Read a named option from the ``Context`` and parse it as an
/// integral type ``T``.
///
/// Reads the option with the check-local name \p LocalName from the
/// ``CheckOptions``. If the corresponding key is not present, returns
/// \p Default.
template <typename T>
typename std::enable_if<std::is_integral<T>::value, T>::type
get(StringRef LocalName, T Default) const {
std::string Value = get(LocalName, "");
T Result = Default;
if (!Value.empty())
StringRef(Value).getAsInteger(10, Result);
return Result;
}
/// \brief Read a named option from the ``Context`` and parse it as an
/// integral type ``T``.
///
/// Reads the option with the check-local name \p LocalName from local or
/// global ``CheckOptions``. Gets local option first. If local is not present,
/// falls back to get global option. If global option is not present either,
/// returns Default.
template <typename T>
typename std::enable_if<std::is_integral<T>::value, T>::type
getLocalOrGlobal(StringRef LocalName, T Default) const {
std::string Value = getLocalOrGlobal(LocalName, "");
T Result = Default;
if (!Value.empty())
StringRef(Value).getAsInteger(10, Result);
return Result;
}
/// \brief Stores an option with the check-local name \p LocalName with string
/// value \p Value to \p Options.
void store(ClangTidyOptions::OptionMap &Options, StringRef LocalName,
StringRef Value) const;
/// \brief Stores an option with the check-local name \p LocalName with
/// ``int64_t`` value \p Value to \p Options.
void store(ClangTidyOptions::OptionMap &Options, StringRef LocalName,
int64_t Value) const;
private:
std::string NamePrefix;
const ClangTidyOptions::OptionMap &CheckOptions;
};
/// \brief Base class for all clang-tidy checks.
///
/// To implement a ``ClangTidyCheck``, write a subclass and override some of the
/// base class's methods. E.g. to implement a check that validates namespace
/// declarations, override ``registerMatchers``:
///
/// ~~~{.cpp}
/// void registerMatchers(ast_matchers::MatchFinder *Finder) override {
/// Finder->addMatcher(namespaceDecl().bind("namespace"), this);
/// }
/// ~~~
///
/// and then override ``check(const MatchResult &Result)`` to do the actual
/// check for each match.
///
/// A new ``ClangTidyCheck`` instance is created per translation unit.
///
/// FIXME: Figure out whether carrying information from one TU to another is
/// useful/necessary.
class ClangTidyCheck : public ast_matchers::MatchFinder::MatchCallback {
public:
/// \brief Initializes the check with \p CheckName and \p Context.
///
/// Derived classes must implement the constructor with this signature or
/// delegate it. If a check needs to read options, it can do this in the
/// constructor using the Options.get() methods below.
ClangTidyCheck(StringRef CheckName, ClangTidyContext *Context)
: CheckName(CheckName), Context(Context),
Options(CheckName, Context->getOptions().CheckOptions) {
assert(Context != nullptr);
assert(!CheckName.empty());
}
/// \brief Override this to register ``PPCallbacks`` with ``Compiler``.
///
/// This should be used for clang-tidy checks that analyze preprocessor-
/// dependent properties, e.g. the order of include directives.
virtual void registerPPCallbacks(CompilerInstance &Compiler) {}
/// \brief Override this to register AST matchers with \p Finder.
///
/// This should be used by clang-tidy checks that analyze code properties that
/// dependent on AST knowledge.
///
/// You can register as many matchers as necessary with \p Finder. Usually,
/// "this" will be used as callback, but you can also specify other callback
/// classes. Thereby, different matchers can trigger different callbacks.
///
/// If you need to merge information between the different matchers, you can
/// store these as members of the derived class. However, note that all
/// matches occur in the order of the AST traversal.
virtual void registerMatchers(ast_matchers::MatchFinder *Finder) {}
/// \brief ``ClangTidyChecks`` that register ASTMatchers should do the actual
/// work in here.
virtual void check(const ast_matchers::MatchFinder::MatchResult &Result) {}
/// \brief Add a diagnostic with the check's name.
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description,
DiagnosticIDs::Level Level = DiagnosticIDs::Warning);
/// \brief Should store all options supported by this check with their
/// current values or default values for options that haven't been overridden.
///
/// The check should use ``Options.store()`` to store each option it supports
/// whether it has the default value or it has been overridden.
virtual void storeOptions(ClangTidyOptions::OptionMap &Options) {}
private:
void run(const ast_matchers::MatchFinder::MatchResult &Result) override;
StringRef getID() const override { return CheckName; }
std::string CheckName;
ClangTidyContext *Context;
protected:
OptionsView Options;
/// \brief Returns the main file name of the current translation unit.
StringRef getCurrentMainFile() const { return Context->getCurrentFile(); }
/// \brief Returns the language options from the context.
LangOptions getLangOpts() const { return Context->getLangOpts(); }
};
class ClangTidyCheckFactories;
class ClangTidyASTConsumerFactory {
public:
ClangTidyASTConsumerFactory(ClangTidyContext &Context);
/// \brief Returns an ASTConsumer that runs the specified clang-tidy checks.
std::unique_ptr<clang::ASTConsumer>
CreateASTConsumer(clang::CompilerInstance &Compiler, StringRef File);
/// \brief Get the list of enabled checks.
std::vector<std::string> getCheckNames();
/// \brief Get the union of options from all checks.
ClangTidyOptions::OptionMap getCheckOptions();
private:
ClangTidyContext &Context;
std::unique_ptr<ClangTidyCheckFactories> CheckFactories;
};
/// \brief Fills the list of check names that are enabled when the provided
/// filters are applied.
std::vector<std::string> getCheckNames(const ClangTidyOptions &Options);
/// \brief Returns the effective check-specific options.
///
/// The method configures ClangTidy with the specified \p Options and collects
/// effective options from all created checks. The returned set of options
/// includes default check-specific options for all keys not overridden by \p
/// Options.
ClangTidyOptions::OptionMap getCheckOptions(const ClangTidyOptions &Options);
/// \brief Run a set of clang-tidy checks on a set of files.
///
/// \param Profile if provided, it enables check profile collection in
/// MatchFinder, and will contain the result of the profile.
void runClangTidy(clang::tidy::ClangTidyContext &Context,
const tooling::CompilationDatabase &Compilations,
ArrayRef<std::string> InputFiles,
ProfileData *Profile = nullptr);
// FIXME: This interface will need to be significantly extended to be useful.
// FIXME: Implement confidence levels for displaying/fixing errors.
//
/// \brief Displays the found \p Errors to the users. If \p Fix is true, \p
/// Errors containing fixes are automatically applied and reformatted. If no
/// clang-format configuration file is found, the given \P FormatStyle is used.
void handleErrors(ClangTidyContext &Context, bool Fix,
unsigned &WarningsAsErrorsCount);
/// \brief Serializes replacements into YAML and writes them to the specified
/// output stream.
void exportReplacements(StringRef MainFilePath,
const std::vector<ClangTidyError> &Errors,
raw_ostream &OS);
} // end namespace tidy
} // end namespace clang
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CLANGTIDY_H

View File

@@ -1,643 +0,0 @@
//===--- tools/extra/clang-tidy/ClangTidyDiagnosticConsumer.cpp ----------=== //
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
///
/// \file This file implements ClangTidyDiagnosticConsumer, ClangTidyContext
/// and ClangTidyError classes.
///
/// This tool uses the Clang Tooling infrastructure, see
/// http://clang.llvm.org/docs/HowToSetupToolingForLLVM.html
/// for details on setting it up with LLVM source tree.
///
//===----------------------------------------------------------------------===//
#include "ClangTidyDiagnosticConsumer.h"
#include "ClangTidyOptions.h"
#include "clang/AST/ASTDiagnostic.h"
#include "clang/Basic/DiagnosticOptions.h"
#include "clang/Frontend/DiagnosticRenderer.h"
#include "llvm/ADT/SmallString.h"
#include <tuple>
#include <vector>
using namespace clang;
using namespace tidy;
namespace {
class ClangTidyDiagnosticRenderer : public DiagnosticRenderer {
public:
ClangTidyDiagnosticRenderer(const LangOptions &LangOpts,
DiagnosticOptions *DiagOpts,
ClangTidyError &Error)
: DiagnosticRenderer(LangOpts, DiagOpts), Error(Error) {}
protected:
void emitDiagnosticMessage(FullSourceLoc Loc, PresumedLoc PLoc,
DiagnosticsEngine::Level Level, StringRef Message,
ArrayRef<CharSourceRange> Ranges,
DiagOrStoredDiag Info) override {
// Remove check name from the message.
// FIXME: Remove this once there's a better way to pass check names than
// appending the check name to the message in ClangTidyContext::diag and
// using getCustomDiagID.
std::string CheckNameInMessage = " [" + Error.DiagnosticName + "]";
if (Message.endswith(CheckNameInMessage))
Message = Message.substr(0, Message.size() - CheckNameInMessage.size());
auto TidyMessage =
Loc.isValid()
? tooling::DiagnosticMessage(Message, Loc.getManager(), Loc)
: tooling::DiagnosticMessage(Message);
if (Level == DiagnosticsEngine::Note) {
Error.Notes.push_back(TidyMessage);
return;
}
assert(Error.Message.Message.empty() && "Overwriting a diagnostic message");
Error.Message = TidyMessage;
}
void emitDiagnosticLoc(FullSourceLoc Loc, PresumedLoc PLoc,
DiagnosticsEngine::Level Level,
ArrayRef<CharSourceRange> Ranges) override {}
void emitCodeContext(FullSourceLoc Loc, DiagnosticsEngine::Level Level,
SmallVectorImpl<CharSourceRange> &Ranges,
ArrayRef<FixItHint> Hints) override {
assert(Loc.isValid());
for (const auto &FixIt : Hints) {
CharSourceRange Range = FixIt.RemoveRange;
assert(Range.getBegin().isValid() && Range.getEnd().isValid() &&
"Invalid range in the fix-it hint.");
assert(Range.getBegin().isFileID() && Range.getEnd().isFileID() &&
"Only file locations supported in fix-it hints.");
tooling::Replacement Replacement(Loc.getManager(), Range,
FixIt.CodeToInsert);
llvm::Error Err = Error.Fix[Replacement.getFilePath()].add(Replacement);
// FIXME: better error handling (at least, don't let other replacements be
// applied).
if (Err) {
llvm::errs() << "Fix conflicts with existing fix! "
<< llvm::toString(std::move(Err)) << "\n";
assert(false && "Fix conflicts with existing fix!");
}
}
}
void emitIncludeLocation(FullSourceLoc Loc, PresumedLoc PLoc) override {}
void emitImportLocation(FullSourceLoc Loc, PresumedLoc PLoc,
StringRef ModuleName) override {}
void emitBuildingModuleLocation(FullSourceLoc Loc, PresumedLoc PLoc,
StringRef ModuleName) override {}
void endDiagnostic(DiagOrStoredDiag D,
DiagnosticsEngine::Level Level) override {
assert(!Error.Message.Message.empty() && "Message has not been set");
}
private:
ClangTidyError &Error;
};
} // end anonymous namespace
ClangTidyError::ClangTidyError(StringRef CheckName,
ClangTidyError::Level DiagLevel,
StringRef BuildDirectory, bool IsWarningAsError)
: tooling::Diagnostic(CheckName, DiagLevel, BuildDirectory),
IsWarningAsError(IsWarningAsError) {}
// Returns true if GlobList starts with the negative indicator ('-'), removes it
// from the GlobList.
static bool ConsumeNegativeIndicator(StringRef &GlobList) {
GlobList = GlobList.trim(' ');
if (GlobList.startswith("-")) {
GlobList = GlobList.substr(1);
return true;
}
return false;
}
// Converts first glob from the comma-separated list of globs to Regex and
// removes it and the trailing comma from the GlobList.
static llvm::Regex ConsumeGlob(StringRef &GlobList) {
StringRef UntrimmedGlob = GlobList.substr(0, GlobList.find(','));
StringRef Glob = UntrimmedGlob.trim(' ');
GlobList = GlobList.substr(UntrimmedGlob.size() + 1);
SmallString<128> RegexText("^");
StringRef MetaChars("()^$|*+?.[]\\{}");
for (char C : Glob) {
if (C == '*')
RegexText.push_back('.');
else if (MetaChars.find(C) != StringRef::npos)
RegexText.push_back('\\');
RegexText.push_back(C);
}
RegexText.push_back('$');
return llvm::Regex(RegexText);
}
GlobList::GlobList(StringRef Globs)
: Positive(!ConsumeNegativeIndicator(Globs)), Regex(ConsumeGlob(Globs)),
NextGlob(Globs.empty() ? nullptr : new GlobList(Globs)) {}
bool GlobList::contains(StringRef S, bool Contains) {
if (Regex.match(S))
Contains = Positive;
if (NextGlob)
Contains = NextGlob->contains(S, Contains);
return Contains;
}
class ClangTidyContext::CachedGlobList {
public:
CachedGlobList(StringRef Globs) : Globs(Globs) {}
bool contains(StringRef S) {
switch (auto &Result = Cache[S]) {
case Yes: return true;
case No: return false;
case None:
Result = Globs.contains(S) ? Yes : No;
return Result == Yes;
}
llvm_unreachable("invalid enum");
}
private:
GlobList Globs;
enum Tristate { None, Yes, No };
llvm::StringMap<Tristate> Cache;
};
ClangTidyContext::ClangTidyContext(
std::unique_ptr<ClangTidyOptionsProvider> OptionsProvider)
: DiagEngine(nullptr), OptionsProvider(std::move(OptionsProvider)),
Profile(nullptr) {
// Before the first translation unit we can get errors related to command-line
// parsing, use empty string for the file name in this case.
setCurrentFile("");
}
ClangTidyContext::~ClangTidyContext() = default;
DiagnosticBuilder ClangTidyContext::diag(
StringRef CheckName, SourceLocation Loc, StringRef Description,
DiagnosticIDs::Level Level /* = DiagnosticIDs::Warning*/) {
assert(Loc.isValid());
unsigned ID = DiagEngine->getDiagnosticIDs()->getCustomDiagID(
Level, (Description + " [" + CheckName + "]").str());
CheckNamesByDiagnosticID.try_emplace(ID, CheckName);
return DiagEngine->Report(Loc, ID);
}
void ClangTidyContext::setDiagnosticsEngine(DiagnosticsEngine *Engine) {
DiagEngine = Engine;
}
void ClangTidyContext::setSourceManager(SourceManager *SourceMgr) {
DiagEngine->setSourceManager(SourceMgr);
}
void ClangTidyContext::setCurrentFile(StringRef File) {
CurrentFile = File;
CurrentOptions = getOptionsForFile(CurrentFile);
CheckFilter = llvm::make_unique<CachedGlobList>(*getOptions().Checks);
WarningAsErrorFilter =
llvm::make_unique<CachedGlobList>(*getOptions().WarningsAsErrors);
}
void ClangTidyContext::setASTContext(ASTContext *Context) {
DiagEngine->SetArgToStringFn(&FormatASTNodeDiagnosticArgument, Context);
LangOpts = Context->getLangOpts();
}
const ClangTidyGlobalOptions &ClangTidyContext::getGlobalOptions() const {
return OptionsProvider->getGlobalOptions();
}
const ClangTidyOptions &ClangTidyContext::getOptions() const {
return CurrentOptions;
}
ClangTidyOptions ClangTidyContext::getOptionsForFile(StringRef File) const {
// Merge options on top of getDefaults() as a safeguard against options with
// unset values.
return ClangTidyOptions::getDefaults().mergeWith(
OptionsProvider->getOptions(File));
}
void ClangTidyContext::setCheckProfileData(ProfileData *P) { Profile = P; }
bool ClangTidyContext::isCheckEnabled(StringRef CheckName) const {
assert(CheckFilter != nullptr);
return CheckFilter->contains(CheckName);
}
bool ClangTidyContext::treatAsError(StringRef CheckName) const {
assert(WarningAsErrorFilter != nullptr);
return WarningAsErrorFilter->contains(CheckName);
}
/// \brief Store a \c ClangTidyError.
void ClangTidyContext::storeError(const ClangTidyError &Error) {
Errors.push_back(Error);
}
StringRef ClangTidyContext::getCheckName(unsigned DiagnosticID) const {
llvm::DenseMap<unsigned, std::string>::const_iterator I =
CheckNamesByDiagnosticID.find(DiagnosticID);
if (I != CheckNamesByDiagnosticID.end())
return I->second;
return "";
}
ClangTidyDiagnosticConsumer::ClangTidyDiagnosticConsumer(
ClangTidyContext &Ctx, bool RemoveIncompatibleErrors)
: Context(Ctx), RemoveIncompatibleErrors(RemoveIncompatibleErrors),
LastErrorRelatesToUserCode(false), LastErrorPassesLineFilter(false),
LastErrorWasIgnored(false) {
IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions();
Diags = llvm::make_unique<DiagnosticsEngine>(
IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs), &*DiagOpts, this,
/*ShouldOwnClient=*/false);
Context.setDiagnosticsEngine(Diags.get());
}
void ClangTidyDiagnosticConsumer::finalizeLastError() {
if (!Errors.empty()) {
ClangTidyError &Error = Errors.back();
if (!Context.isCheckEnabled(Error.DiagnosticName) &&
Error.DiagLevel != ClangTidyError::Error) {
++Context.Stats.ErrorsIgnoredCheckFilter;
Errors.pop_back();
} else if (!LastErrorRelatesToUserCode) {
++Context.Stats.ErrorsIgnoredNonUserCode;
Errors.pop_back();
} else if (!LastErrorPassesLineFilter) {
++Context.Stats.ErrorsIgnoredLineFilter;
Errors.pop_back();
} else {
++Context.Stats.ErrorsDisplayed;
}
}
LastErrorRelatesToUserCode = false;
LastErrorPassesLineFilter = false;
}
static bool LineIsMarkedWithNOLINT(SourceManager &SM, SourceLocation Loc) {
bool Invalid;
const char *CharacterData = SM.getCharacterData(Loc, &Invalid);
if (Invalid)
return false;
// Check if there's a NOLINT on this line.
const char *P = CharacterData;
while (*P != '\0' && *P != '\r' && *P != '\n')
++P;
StringRef RestOfLine(CharacterData, P - CharacterData + 1);
// FIXME: Handle /\bNOLINT\b(\([^)]*\))?/ as cpplint.py does.
if (RestOfLine.find("NOLINT") != StringRef::npos)
return true;
// Check if there's a NOLINTNEXTLINE on the previous line.
const char *BufBegin =
SM.getCharacterData(SM.getLocForStartOfFile(SM.getFileID(Loc)), &Invalid);
if (Invalid || P == BufBegin)
return false;
// Scan backwards over the current line.
P = CharacterData;
while (P != BufBegin && *P != '\n')
--P;
// If we reached the begin of the file there is no line before it.
if (P == BufBegin)
return false;
// Skip over the newline.
--P;
const char *LineEnd = P;
// Now we're on the previous line. Skip to the beginning of it.
while (P != BufBegin && *P != '\n')
--P;
RestOfLine = StringRef(P, LineEnd - P + 1);
if (RestOfLine.find("NOLINTNEXTLINE") != StringRef::npos)
return true;
return false;
}
static bool LineIsMarkedWithNOLINTinMacro(SourceManager &SM,
SourceLocation Loc) {
while (true) {
if (LineIsMarkedWithNOLINT(SM, Loc))
return true;
if (!Loc.isMacroID())
return false;
Loc = SM.getImmediateExpansionRange(Loc).first;
}
return false;
}
void ClangTidyDiagnosticConsumer::HandleDiagnostic(
DiagnosticsEngine::Level DiagLevel, const Diagnostic &Info) {
if (LastErrorWasIgnored && DiagLevel == DiagnosticsEngine::Note)
return;
if (Info.getLocation().isValid() && DiagLevel != DiagnosticsEngine::Error &&
DiagLevel != DiagnosticsEngine::Fatal &&
LineIsMarkedWithNOLINTinMacro(Diags->getSourceManager(),
Info.getLocation())) {
++Context.Stats.ErrorsIgnoredNOLINT;
// Ignored a warning, should ignore related notes as well
LastErrorWasIgnored = true;
return;
}
LastErrorWasIgnored = false;
// Count warnings/errors.
DiagnosticConsumer::HandleDiagnostic(DiagLevel, Info);
if (DiagLevel == DiagnosticsEngine::Note) {
assert(!Errors.empty() &&
"A diagnostic note can only be appended to a message.");
} else {
finalizeLastError();
StringRef WarningOption =
Context.DiagEngine->getDiagnosticIDs()->getWarningOptionForDiag(
Info.getID());
std::string CheckName = !WarningOption.empty()
? ("clang-diagnostic-" + WarningOption).str()
: Context.getCheckName(Info.getID()).str();
if (CheckName.empty()) {
// This is a compiler diagnostic without a warning option. Assign check
// name based on its level.
switch (DiagLevel) {
case DiagnosticsEngine::Error:
case DiagnosticsEngine::Fatal:
CheckName = "clang-diagnostic-error";
break;
case DiagnosticsEngine::Warning:
CheckName = "clang-diagnostic-warning";
break;
default:
CheckName = "clang-diagnostic-unknown";
break;
}
}
ClangTidyError::Level Level = ClangTidyError::Warning;
if (DiagLevel == DiagnosticsEngine::Error ||
DiagLevel == DiagnosticsEngine::Fatal) {
// Force reporting of Clang errors regardless of filters and non-user
// code.
Level = ClangTidyError::Error;
LastErrorRelatesToUserCode = true;
LastErrorPassesLineFilter = true;
}
bool IsWarningAsError = DiagLevel == DiagnosticsEngine::Warning &&
Context.treatAsError(CheckName);
Errors.emplace_back(CheckName, Level, Context.getCurrentBuildDirectory(),
IsWarningAsError);
}
ClangTidyDiagnosticRenderer Converter(
Context.getLangOpts(), &Context.DiagEngine->getDiagnosticOptions(),
Errors.back());
SmallString<100> Message;
Info.FormatDiagnostic(Message);
FullSourceLoc Loc =
(Info.getLocation().isInvalid())
? FullSourceLoc()
: FullSourceLoc(Info.getLocation(), Info.getSourceManager());
Converter.emitDiagnostic(Loc, DiagLevel, Message, Info.getRanges(),
Info.getFixItHints());
checkFilters(Info.getLocation());
}
bool ClangTidyDiagnosticConsumer::passesLineFilter(StringRef FileName,
unsigned LineNumber) const {
if (Context.getGlobalOptions().LineFilter.empty())
return true;
for (const FileFilter &Filter : Context.getGlobalOptions().LineFilter) {
if (FileName.endswith(Filter.Name)) {
if (Filter.LineRanges.empty())
return true;
for (const FileFilter::LineRange &Range : Filter.LineRanges) {
if (Range.first <= LineNumber && LineNumber <= Range.second)
return true;
}
return false;
}
}
return false;
}
void ClangTidyDiagnosticConsumer::checkFilters(SourceLocation Location) {
// Invalid location may mean a diagnostic in a command line, don't skip these.
if (!Location.isValid()) {
LastErrorRelatesToUserCode = true;
LastErrorPassesLineFilter = true;
return;
}
const SourceManager &Sources = Diags->getSourceManager();
if (!*Context.getOptions().SystemHeaders &&
Sources.isInSystemHeader(Location))
return;
// FIXME: We start with a conservative approach here, but the actual type of
// location needed depends on the check (in particular, where this check wants
// to apply fixes).
FileID FID = Sources.getDecomposedExpansionLoc(Location).first;
const FileEntry *File = Sources.getFileEntryForID(FID);
// -DMACRO definitions on the command line have locations in a virtual buffer
// that doesn't have a FileEntry. Don't skip these as well.
if (!File) {
LastErrorRelatesToUserCode = true;
LastErrorPassesLineFilter = true;
return;
}
StringRef FileName(File->getName());
LastErrorRelatesToUserCode = LastErrorRelatesToUserCode ||
Sources.isInMainFile(Location) ||
getHeaderFilter()->match(FileName);
unsigned LineNumber = Sources.getExpansionLineNumber(Location);
LastErrorPassesLineFilter =
LastErrorPassesLineFilter || passesLineFilter(FileName, LineNumber);
}
llvm::Regex *ClangTidyDiagnosticConsumer::getHeaderFilter() {
if (!HeaderFilter)
HeaderFilter =
llvm::make_unique<llvm::Regex>(*Context.getOptions().HeaderFilterRegex);
return HeaderFilter.get();
}
void ClangTidyDiagnosticConsumer::removeIncompatibleErrors(
SmallVectorImpl<ClangTidyError> &Errors) const {
// Each error is modelled as the set of intervals in which it applies
// replacements. To detect overlapping replacements, we use a sweep line
// algorithm over these sets of intervals.
// An event here consists of the opening or closing of an interval. During the
// process, we maintain a counter with the amount of open intervals. If we
// find an endpoint of an interval and this counter is different from 0, it
// means that this interval overlaps with another one, so we set it as
// inapplicable.
struct Event {
// An event can be either the begin or the end of an interval.
enum EventType {
ET_Begin = 1,
ET_End = -1,
};
Event(unsigned Begin, unsigned End, EventType Type, unsigned ErrorId,
unsigned ErrorSize)
: Type(Type), ErrorId(ErrorId) {
// The events are going to be sorted by their position. In case of draw:
//
// * If an interval ends at the same position at which other interval
// begins, this is not an overlapping, so we want to remove the ending
// interval before adding the starting one: end events have higher
// priority than begin events.
//
// * If we have several begin points at the same position, we will mark as
// inapplicable the ones that we process later, so the first one has to
// be the one with the latest end point, because this one will contain
// all the other intervals. For the same reason, if we have several end
// points in the same position, the last one has to be the one with the
// earliest begin point. In both cases, we sort non-increasingly by the
// position of the complementary.
//
// * In case of two equal intervals, the one whose error is bigger can
// potentially contain the other one, so we want to process its begin
// points before and its end points later.
//
// * Finally, if we have two equal intervals whose errors have the same
// size, none of them will be strictly contained inside the other.
// Sorting by ErrorId will guarantee that the begin point of the first
// one will be processed before, disallowing the second one, and the
// end point of the first one will also be processed before,
// disallowing the first one.
if (Type == ET_Begin)
Priority = std::make_tuple(Begin, Type, -End, -ErrorSize, ErrorId);
else
Priority = std::make_tuple(End, Type, -Begin, ErrorSize, ErrorId);
}
bool operator<(const Event &Other) const {
return Priority < Other.Priority;
}
// Determines if this event is the begin or the end of an interval.
EventType Type;
// The index of the error to which the interval that generated this event
// belongs.
unsigned ErrorId;
// The events will be sorted based on this field.
std::tuple<unsigned, EventType, int, int, unsigned> Priority;
};
// Compute error sizes.
std::vector<int> Sizes;
for (const auto &Error : Errors) {
int Size = 0;
for (const auto &FileAndReplaces : Error.Fix) {
for (const auto &Replace : FileAndReplaces.second)
Size += Replace.getLength();
}
Sizes.push_back(Size);
}
// Build events from error intervals.
std::map<std::string, std::vector<Event>> FileEvents;
for (unsigned I = 0; I < Errors.size(); ++I) {
for (const auto &FileAndReplace : Errors[I].Fix) {
for (const auto &Replace : FileAndReplace.second) {
unsigned Begin = Replace.getOffset();
unsigned End = Begin + Replace.getLength();
const std::string &FilePath = Replace.getFilePath();
// FIXME: Handle empty intervals, such as those from insertions.
if (Begin == End)
continue;
auto &Events = FileEvents[FilePath];
Events.emplace_back(Begin, End, Event::ET_Begin, I, Sizes[I]);
Events.emplace_back(Begin, End, Event::ET_End, I, Sizes[I]);
}
}
}
std::vector<bool> Apply(Errors.size(), true);
for (auto &FileAndEvents : FileEvents) {
std::vector<Event> &Events = FileAndEvents.second;
// Sweep.
std::sort(Events.begin(), Events.end());
int OpenIntervals = 0;
for (const auto &Event : Events) {
if (Event.Type == Event::ET_End)
--OpenIntervals;
// This has to be checked after removing the interval from the count if it
// is an end event, or before adding it if it is a begin event.
if (OpenIntervals != 0)
Apply[Event.ErrorId] = false;
if (Event.Type == Event::ET_Begin)
++OpenIntervals;
}
assert(OpenIntervals == 0 && "Amount of begin/end points doesn't match");
}
for (unsigned I = 0; I < Errors.size(); ++I) {
if (!Apply[I]) {
Errors[I].Fix.clear();
Errors[I].Notes.emplace_back(
"this fix will not be applied because it overlaps with another fix");
}
}
}
namespace {
struct LessClangTidyError {
bool operator()(const ClangTidyError &LHS, const ClangTidyError &RHS) const {
const tooling::DiagnosticMessage &M1 = LHS.Message;
const tooling::DiagnosticMessage &M2 = RHS.Message;
return std::tie(M1.FilePath, M1.FileOffset, M1.Message) <
std::tie(M2.FilePath, M2.FileOffset, M2.Message);
}
};
struct EqualClangTidyError {
bool operator()(const ClangTidyError &LHS, const ClangTidyError &RHS) const {
LessClangTidyError Less;
return !Less(LHS, RHS) && !Less(RHS, LHS);
}
};
} // end anonymous namespace
// Flushes the internal diagnostics buffer to the ClangTidyContext.
void ClangTidyDiagnosticConsumer::finish() {
finalizeLastError();
std::sort(Errors.begin(), Errors.end(), LessClangTidyError());
Errors.erase(std::unique(Errors.begin(), Errors.end(), EqualClangTidyError()),
Errors.end());
if (RemoveIncompatibleErrors)
removeIncompatibleErrors(Errors);
for (const ClangTidyError &Error : Errors)
Context.storeError(Error);
Errors.clear();
}

View File

@@ -1,268 +0,0 @@
//===--- ClangTidyDiagnosticConsumer.h - clang-tidy -------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CLANGTIDYDIAGNOSTICCONSUMER_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CLANGTIDYDIAGNOSTICCONSUMER_H
#include "ClangTidyOptions.h"
#include "clang/Basic/Diagnostic.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Tooling/Core/Diagnostic.h"
#include "clang/Tooling/Refactoring.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/StringMap.h"
#include "llvm/Support/Regex.h"
#include "llvm/Support/Timer.h"
namespace clang {
class ASTContext;
class CompilerInstance;
namespace ast_matchers {
class MatchFinder;
}
namespace tooling {
class CompilationDatabase;
}
namespace tidy {
/// \brief A detected error complete with information to display diagnostic and
/// automatic fix.
///
/// This is used as an intermediate format to transport Diagnostics without a
/// dependency on a SourceManager.
///
/// FIXME: Make Diagnostics flexible enough to support this directly.
struct ClangTidyError : tooling::Diagnostic {
ClangTidyError(StringRef CheckName, Level DiagLevel, StringRef BuildDirectory,
bool IsWarningAsError);
bool IsWarningAsError;
};
/// \brief Read-only set of strings represented as a list of positive and
/// negative globs. Positive globs add all matched strings to the set, negative
/// globs remove them in the order of appearance in the list.
class GlobList {
public:
/// \brief \p GlobList is a comma-separated list of globs (only '*'
/// metacharacter is supported) with optional '-' prefix to denote exclusion.
GlobList(StringRef Globs);
/// \brief Returns \c true if the pattern matches \p S. The result is the last
/// matching glob's Positive flag.
bool contains(StringRef S) { return contains(S, false); }
private:
bool contains(StringRef S, bool Contains);
bool Positive;
llvm::Regex Regex;
std::unique_ptr<GlobList> NextGlob;
};
/// \brief Contains displayed and ignored diagnostic counters for a ClangTidy
/// run.
struct ClangTidyStats {
ClangTidyStats()
: ErrorsDisplayed(0), ErrorsIgnoredCheckFilter(0), ErrorsIgnoredNOLINT(0),
ErrorsIgnoredNonUserCode(0), ErrorsIgnoredLineFilter(0) {}
unsigned ErrorsDisplayed;
unsigned ErrorsIgnoredCheckFilter;
unsigned ErrorsIgnoredNOLINT;
unsigned ErrorsIgnoredNonUserCode;
unsigned ErrorsIgnoredLineFilter;
unsigned errorsIgnored() const {
return ErrorsIgnoredNOLINT + ErrorsIgnoredCheckFilter +
ErrorsIgnoredNonUserCode + ErrorsIgnoredLineFilter;
}
};
/// \brief Container for clang-tidy profiling data.
struct ProfileData {
llvm::StringMap<llvm::TimeRecord> Records;
};
/// \brief Every \c ClangTidyCheck reports errors through a \c DiagnosticsEngine
/// provided by this context.
///
/// A \c ClangTidyCheck always has access to the active context to report
/// warnings like:
/// \code
/// Context->Diag(Loc, "Single-argument constructors must be explicit")
/// << FixItHint::CreateInsertion(Loc, "explicit ");
/// \endcode
class ClangTidyContext {
public:
/// \brief Initializes \c ClangTidyContext instance.
ClangTidyContext(std::unique_ptr<ClangTidyOptionsProvider> OptionsProvider);
~ClangTidyContext();
/// \brief Report any errors detected using this method.
///
/// This is still under heavy development and will likely change towards using
/// tablegen'd diagnostic IDs.
/// FIXME: Figure out a way to manage ID spaces.
DiagnosticBuilder diag(StringRef CheckName, SourceLocation Loc,
StringRef Message,
DiagnosticIDs::Level Level = DiagnosticIDs::Warning);
/// \brief Sets the \c SourceManager of the used \c DiagnosticsEngine.
///
/// This is called from the \c ClangTidyCheck base class.
void setSourceManager(SourceManager *SourceMgr);
/// \brief Should be called when starting to process new translation unit.
void setCurrentFile(StringRef File);
/// \brief Returns the main file name of the current translation unit.
StringRef getCurrentFile() const { return CurrentFile; }
/// \brief Sets ASTContext for the current translation unit.
void setASTContext(ASTContext *Context);
/// \brief Gets the language options from the AST context.
const LangOptions &getLangOpts() const { return LangOpts; }
/// \brief Returns the name of the clang-tidy check which produced this
/// diagnostic ID.
StringRef getCheckName(unsigned DiagnosticID) const;
/// \brief Returns \c true if the check is enabled for the \c CurrentFile.
///
/// The \c CurrentFile can be changed using \c setCurrentFile.
bool isCheckEnabled(StringRef CheckName) const;
/// \brief Returns \c true if the check should be upgraded to error for the
/// \c CurrentFile.
bool treatAsError(StringRef CheckName) const;
/// \brief Returns global options.
const ClangTidyGlobalOptions &getGlobalOptions() const;
/// \brief Returns options for \c CurrentFile.
///
/// The \c CurrentFile can be changed using \c setCurrentFile.
const ClangTidyOptions &getOptions() const;
/// \brief Returns options for \c File. Does not change or depend on
/// \c CurrentFile.
ClangTidyOptions getOptionsForFile(StringRef File) const;
/// \brief Returns \c ClangTidyStats containing issued and ignored diagnostic
/// counters.
const ClangTidyStats &getStats() const { return Stats; }
/// \brief Returns all collected errors.
ArrayRef<ClangTidyError> getErrors() const { return Errors; }
/// \brief Clears collected errors.
void clearErrors() { Errors.clear(); }
/// \brief Set the output struct for profile data.
///
/// Setting a non-null pointer here will enable profile collection in
/// clang-tidy.
void setCheckProfileData(ProfileData *Profile);
ProfileData *getCheckProfileData() const { return Profile; }
/// \brief Should be called when starting to process new translation unit.
void setCurrentBuildDirectory(StringRef BuildDirectory) {
CurrentBuildDirectory = BuildDirectory;
}
/// \brief Returns build directory of the current translation unit.
const std::string &getCurrentBuildDirectory() {
return CurrentBuildDirectory;
}
private:
// Calls setDiagnosticsEngine() and storeError().
friend class ClangTidyDiagnosticConsumer;
friend class ClangTidyPluginAction;
/// \brief Sets the \c DiagnosticsEngine so that Diagnostics can be generated
/// correctly.
void setDiagnosticsEngine(DiagnosticsEngine *Engine);
/// \brief Store an \p Error.
void storeError(const ClangTidyError &Error);
std::vector<ClangTidyError> Errors;
DiagnosticsEngine *DiagEngine;
std::unique_ptr<ClangTidyOptionsProvider> OptionsProvider;
std::string CurrentFile;
ClangTidyOptions CurrentOptions;
class CachedGlobList;
std::unique_ptr<CachedGlobList> CheckFilter;
std::unique_ptr<CachedGlobList> WarningAsErrorFilter;
LangOptions LangOpts;
ClangTidyStats Stats;
std::string CurrentBuildDirectory;
llvm::DenseMap<unsigned, std::string> CheckNamesByDiagnosticID;
ProfileData *Profile;
};
/// \brief A diagnostic consumer that turns each \c Diagnostic into a
/// \c SourceManager-independent \c ClangTidyError.
//
// FIXME: If we move away from unit-tests, this can be moved to a private
// implementation file.
class ClangTidyDiagnosticConsumer : public DiagnosticConsumer {
public:
ClangTidyDiagnosticConsumer(ClangTidyContext &Ctx,
bool RemoveIncompatibleErrors = true);
// FIXME: The concept of converting between FixItHints and Replacements is
// more generic and should be pulled out into a more useful Diagnostics
// library.
void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,
const Diagnostic &Info) override;
/// \brief Flushes the internal diagnostics buffer to the ClangTidyContext.
void finish() override;
private:
void finalizeLastError();
void removeIncompatibleErrors(SmallVectorImpl<ClangTidyError> &Errors) const;
/// \brief Returns the \c HeaderFilter constructed for the options set in the
/// context.
llvm::Regex *getHeaderFilter();
/// \brief Updates \c LastErrorRelatesToUserCode and LastErrorPassesLineFilter
/// according to the diagnostic \p Location.
void checkFilters(SourceLocation Location);
bool passesLineFilter(StringRef FileName, unsigned LineNumber) const;
ClangTidyContext &Context;
bool RemoveIncompatibleErrors;
std::unique_ptr<DiagnosticsEngine> Diags;
SmallVector<ClangTidyError, 8> Errors;
std::unique_ptr<llvm::Regex> HeaderFilter;
bool LastErrorRelatesToUserCode;
bool LastErrorPassesLineFilter;
bool LastErrorWasIgnored;
};
} // end namespace tidy
} // end namespace clang
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CLANGTIDYDIAGNOSTICCONSUMER_H

View File

@@ -1,38 +0,0 @@
//===--- tools/extra/clang-tidy/ClangTidyModule.cpp - Clang tidy tool -----===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
///
/// \file Implements classes required to build clang-tidy modules.
///
//===----------------------------------------------------------------------===//
#include "ClangTidyModule.h"
namespace clang {
namespace tidy {
void ClangTidyCheckFactories::registerCheckFactory(StringRef Name,
CheckFactory Factory) {
Factories[Name] = std::move(Factory);
}
void ClangTidyCheckFactories::createChecks(
ClangTidyContext *Context,
std::vector<std::unique_ptr<ClangTidyCheck>> &Checks) {
for (const auto &Factory : Factories) {
if (Context->isCheckEnabled(Factory.first))
Checks.emplace_back(Factory.second(Factory.first, Context));
}
}
ClangTidyOptions ClangTidyModule::getModuleOptions() {
return ClangTidyOptions();
}
} // namespace tidy
} // namespace clang

View File

@@ -1,99 +0,0 @@
//===--- ClangTidyModule.h - clang-tidy -------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CLANGTIDYMODULE_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CLANGTIDYMODULE_H
#include "ClangTidy.h"
#include "llvm/ADT/StringRef.h"
#include <functional>
#include <map>
#include <string>
#include <utility>
namespace clang {
namespace tidy {
/// \brief A collection of \c ClangTidyCheckFactory instances.
///
/// All clang-tidy modules register their check factories with an instance of
/// this object.
class ClangTidyCheckFactories {
public:
typedef std::function<ClangTidyCheck *(StringRef Name,
ClangTidyContext *Context)>
CheckFactory;
/// \brief Registers check \p Factory with name \p Name.
///
/// For all checks that have default constructors, use \c registerCheck.
void registerCheckFactory(StringRef Name, CheckFactory Factory);
/// \brief Registers the \c CheckType with the name \p Name.
///
/// This method should be used for all \c ClangTidyChecks that don't require
/// constructor parameters.
///
/// For example, if have a clang-tidy check like:
/// \code
/// class MyTidyCheck : public ClangTidyCheck {
/// void registerMatchers(ast_matchers::MatchFinder *Finder) override {
/// ..
/// }
/// };
/// \endcode
/// you can register it with:
/// \code
/// class MyModule : public ClangTidyModule {
/// void addCheckFactories(ClangTidyCheckFactories &Factories) override {
/// Factories.registerCheck<MyTidyCheck>("myproject-my-check");
/// }
/// };
/// \endcode
template <typename CheckType> void registerCheck(StringRef CheckName) {
registerCheckFactory(CheckName,
[](StringRef Name, ClangTidyContext *Context) {
return new CheckType(Name, Context);
});
}
/// \brief Create instances of all checks matching \p CheckRegexString and
/// store them in \p Checks.
///
/// The caller takes ownership of the return \c ClangTidyChecks.
void createChecks(ClangTidyContext *Context,
std::vector<std::unique_ptr<ClangTidyCheck>> &Checks);
typedef std::map<std::string, CheckFactory> FactoryMap;
FactoryMap::const_iterator begin() const { return Factories.begin(); }
FactoryMap::const_iterator end() const { return Factories.end(); }
bool empty() const { return Factories.empty(); }
private:
FactoryMap Factories;
};
/// \brief A clang-tidy module groups a number of \c ClangTidyChecks and gives
/// them a prefixed name.
class ClangTidyModule {
public:
virtual ~ClangTidyModule() {}
/// \brief Implement this function in order to register all \c CheckFactories
/// belonging to this module.
virtual void addCheckFactories(ClangTidyCheckFactories &CheckFactories) = 0;
/// \brief Gets default options for checks defined in this module.
virtual ClangTidyOptions getModuleOptions();
};
} // end namespace tidy
} // end namespace clang
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CLANGTIDYMODULE_H

View File

@@ -1,24 +0,0 @@
//===--- ClangTidyModuleRegistry.h - clang-tidy -----------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CLANGTIDYMODULEREGISTRY_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CLANGTIDYMODULEREGISTRY_H
#include "ClangTidyModule.h"
#include "llvm/Support/Registry.h"
namespace clang {
namespace tidy {
typedef llvm::Registry<ClangTidyModule> ClangTidyModuleRegistry;
} // end namespace tidy
} // end namespace clang
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CLANGTIDYMODULEREGISTRY_H

View File

@@ -1,340 +0,0 @@
//===--- ClangTidyOptions.cpp - clang-tidy ----------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "ClangTidyOptions.h"
#include "ClangTidyModuleRegistry.h"
#include "clang/Basic/LLVM.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/Errc.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/YAMLTraits.h"
#include "llvm/Support/raw_ostream.h"
#include <utility>
#define DEBUG_TYPE "clang-tidy-options"
using clang::tidy::ClangTidyOptions;
using clang::tidy::FileFilter;
using OptionsSource = clang::tidy::ClangTidyOptionsProvider::OptionsSource;
LLVM_YAML_IS_FLOW_SEQUENCE_VECTOR(FileFilter)
LLVM_YAML_IS_FLOW_SEQUENCE_VECTOR(FileFilter::LineRange)
namespace llvm {
namespace yaml {
// Map std::pair<int, int> to a JSON array of size 2.
template <> struct SequenceTraits<FileFilter::LineRange> {
static size_t size(IO &IO, FileFilter::LineRange &Range) {
return Range.first == 0 ? 0 : Range.second == 0 ? 1 : 2;
}
static unsigned &element(IO &IO, FileFilter::LineRange &Range, size_t Index) {
if (Index > 1)
IO.setError("Too many elements in line range.");
return Index == 0 ? Range.first : Range.second;
}
};
template <> struct MappingTraits<FileFilter> {
static void mapping(IO &IO, FileFilter &File) {
IO.mapRequired("name", File.Name);
IO.mapOptional("lines", File.LineRanges);
}
static StringRef validate(IO &io, FileFilter &File) {
if (File.Name.empty())
return "No file name specified";
for (const FileFilter::LineRange &Range : File.LineRanges) {
if (Range.first <= 0 || Range.second <= 0)
return "Invalid line range";
}
return StringRef();
}
};
template <> struct MappingTraits<ClangTidyOptions::StringPair> {
static void mapping(IO &IO, ClangTidyOptions::StringPair &KeyValue) {
IO.mapRequired("key", KeyValue.first);
IO.mapRequired("value", KeyValue.second);
}
};
struct NOptionMap {
NOptionMap(IO &) {}
NOptionMap(IO &, const ClangTidyOptions::OptionMap &OptionMap)
: Options(OptionMap.begin(), OptionMap.end()) {}
ClangTidyOptions::OptionMap denormalize(IO &) {
ClangTidyOptions::OptionMap Map;
for (const auto &KeyValue : Options)
Map[KeyValue.first] = KeyValue.second;
return Map;
}
std::vector<ClangTidyOptions::StringPair> Options;
};
template <> struct MappingTraits<ClangTidyOptions> {
static void mapping(IO &IO, ClangTidyOptions &Options) {
MappingNormalization<NOptionMap, ClangTidyOptions::OptionMap> NOpts(
IO, Options.CheckOptions);
IO.mapOptional("Checks", Options.Checks);
IO.mapOptional("WarningsAsErrors", Options.WarningsAsErrors);
IO.mapOptional("HeaderFilterRegex", Options.HeaderFilterRegex);
IO.mapOptional("AnalyzeTemporaryDtors", Options.AnalyzeTemporaryDtors);
IO.mapOptional("FormatStyle", Options.FormatStyle);
IO.mapOptional("User", Options.User);
IO.mapOptional("CheckOptions", NOpts->Options);
IO.mapOptional("ExtraArgs", Options.ExtraArgs);
IO.mapOptional("ExtraArgsBefore", Options.ExtraArgsBefore);
}
};
} // namespace yaml
} // namespace llvm
namespace clang {
namespace tidy {
ClangTidyOptions ClangTidyOptions::getDefaults() {
ClangTidyOptions Options;
Options.Checks = "";
Options.WarningsAsErrors = "";
Options.HeaderFilterRegex = "";
Options.SystemHeaders = false;
Options.AnalyzeTemporaryDtors = false;
Options.FormatStyle = "none";
Options.User = llvm::None;
for (ClangTidyModuleRegistry::iterator I = ClangTidyModuleRegistry::begin(),
E = ClangTidyModuleRegistry::end();
I != E; ++I)
Options = Options.mergeWith(I->instantiate()->getModuleOptions());
return Options;
}
template <typename T>
static void mergeVectors(Optional<T> &Dest, const Optional<T> &Src) {
if (Src) {
if (Dest)
Dest->insert(Dest->end(), Src->begin(), Src->end());
else
Dest = Src;
}
}
static void mergeCommaSeparatedLists(Optional<std::string> &Dest,
const Optional<std::string> &Src) {
if (Src)
Dest = (Dest && !Dest->empty() ? *Dest + "," : "") + *Src;
}
template <typename T>
static void overrideValue(Optional<T> &Dest, const Optional<T> &Src) {
if (Src)
Dest = Src;
}
ClangTidyOptions
ClangTidyOptions::mergeWith(const ClangTidyOptions &Other) const {
ClangTidyOptions Result = *this;
mergeCommaSeparatedLists(Result.Checks, Other.Checks);
mergeCommaSeparatedLists(Result.WarningsAsErrors, Other.WarningsAsErrors);
overrideValue(Result.HeaderFilterRegex, Other.HeaderFilterRegex);
overrideValue(Result.SystemHeaders, Other.SystemHeaders);
overrideValue(Result.AnalyzeTemporaryDtors, Other.AnalyzeTemporaryDtors);
overrideValue(Result.FormatStyle, Other.FormatStyle);
overrideValue(Result.User, Other.User);
mergeVectors(Result.ExtraArgs, Other.ExtraArgs);
mergeVectors(Result.ExtraArgsBefore, Other.ExtraArgsBefore);
for (const auto &KeyValue : Other.CheckOptions)
Result.CheckOptions[KeyValue.first] = KeyValue.second;
return Result;
}
const char ClangTidyOptionsProvider::OptionsSourceTypeDefaultBinary[] =
"clang-tidy binary";
const char ClangTidyOptionsProvider::OptionsSourceTypeCheckCommandLineOption[] =
"command-line option '-checks'";
const char
ClangTidyOptionsProvider::OptionsSourceTypeConfigCommandLineOption[] =
"command-line option '-config'";
ClangTidyOptions
ClangTidyOptionsProvider::getOptions(llvm::StringRef FileName) {
ClangTidyOptions Result;
for (const auto &Source : getRawOptions(FileName))
Result = Result.mergeWith(Source.first);
return Result;
}
std::vector<OptionsSource>
DefaultOptionsProvider::getRawOptions(llvm::StringRef FileName) {
std::vector<OptionsSource> Result;
Result.emplace_back(DefaultOptions, OptionsSourceTypeDefaultBinary);
return Result;
}
ConfigOptionsProvider::ConfigOptionsProvider(
const ClangTidyGlobalOptions &GlobalOptions,
const ClangTidyOptions &DefaultOptions,
const ClangTidyOptions &ConfigOptions,
const ClangTidyOptions &OverrideOptions)
: DefaultOptionsProvider(GlobalOptions, DefaultOptions),
ConfigOptions(ConfigOptions), OverrideOptions(OverrideOptions) {}
std::vector<OptionsSource>
ConfigOptionsProvider::getRawOptions(llvm::StringRef FileName) {
std::vector<OptionsSource> RawOptions =
DefaultOptionsProvider::getRawOptions(FileName);
RawOptions.emplace_back(ConfigOptions,
OptionsSourceTypeConfigCommandLineOption);
RawOptions.emplace_back(OverrideOptions,
OptionsSourceTypeCheckCommandLineOption);
return RawOptions;
}
FileOptionsProvider::FileOptionsProvider(
const ClangTidyGlobalOptions &GlobalOptions,
const ClangTidyOptions &DefaultOptions,
const ClangTidyOptions &OverrideOptions)
: DefaultOptionsProvider(GlobalOptions, DefaultOptions),
OverrideOptions(OverrideOptions) {
ConfigHandlers.emplace_back(".clang-tidy", parseConfiguration);
}
FileOptionsProvider::FileOptionsProvider(
const ClangTidyGlobalOptions &GlobalOptions,
const ClangTidyOptions &DefaultOptions,
const ClangTidyOptions &OverrideOptions,
const FileOptionsProvider::ConfigFileHandlers &ConfigHandlers)
: DefaultOptionsProvider(GlobalOptions, DefaultOptions),
OverrideOptions(OverrideOptions), ConfigHandlers(ConfigHandlers) {}
// FIXME: This method has some common logic with clang::format::getStyle().
// Consider pulling out common bits to a findParentFileWithName function or
// similar.
std::vector<OptionsSource>
FileOptionsProvider::getRawOptions(StringRef FileName) {
DEBUG(llvm::dbgs() << "Getting options for file " << FileName << "...\n");
std::vector<OptionsSource> RawOptions =
DefaultOptionsProvider::getRawOptions(FileName);
OptionsSource CommandLineOptions(OverrideOptions,
OptionsSourceTypeCheckCommandLineOption);
// Look for a suitable configuration file in all parent directories of the
// file. Start with the immediate parent directory and move up.
StringRef Path = llvm::sys::path::parent_path(FileName);
for (StringRef CurrentPath = Path; !CurrentPath.empty();
CurrentPath = llvm::sys::path::parent_path(CurrentPath)) {
llvm::Optional<OptionsSource> Result;
auto Iter = CachedOptions.find(CurrentPath);
if (Iter != CachedOptions.end())
Result = Iter->second;
if (!Result)
Result = tryReadConfigFile(CurrentPath);
if (Result) {
// Store cached value for all intermediate directories.
while (Path != CurrentPath) {
DEBUG(llvm::dbgs() << "Caching configuration for path " << Path
<< ".\n");
CachedOptions[Path] = *Result;
Path = llvm::sys::path::parent_path(Path);
}
CachedOptions[Path] = *Result;
RawOptions.push_back(*Result);
break;
}
}
RawOptions.push_back(CommandLineOptions);
return RawOptions;
}
llvm::Optional<OptionsSource>
FileOptionsProvider::tryReadConfigFile(StringRef Directory) {
assert(!Directory.empty());
if (!llvm::sys::fs::is_directory(Directory)) {
llvm::errs() << "Error reading configuration from " << Directory
<< ": directory doesn't exist.\n";
return llvm::None;
}
for (const ConfigFileHandler &ConfigHandler : ConfigHandlers) {
SmallString<128> ConfigFile(Directory);
llvm::sys::path::append(ConfigFile, ConfigHandler.first);
DEBUG(llvm::dbgs() << "Trying " << ConfigFile << "...\n");
bool IsFile = false;
// Ignore errors from is_regular_file: we only need to know if we can read
// the file or not.
llvm::sys::fs::is_regular_file(Twine(ConfigFile), IsFile);
if (!IsFile)
continue;
llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> Text =
llvm::MemoryBuffer::getFile(ConfigFile.c_str());
if (std::error_code EC = Text.getError()) {
llvm::errs() << "Can't read " << ConfigFile << ": " << EC.message()
<< "\n";
continue;
}
// Skip empty files, e.g. files opened for writing via shell output
// redirection.
if ((*Text)->getBuffer().empty())
continue;
llvm::ErrorOr<ClangTidyOptions> ParsedOptions =
ConfigHandler.second((*Text)->getBuffer());
if (!ParsedOptions) {
if (ParsedOptions.getError())
llvm::errs() << "Error parsing " << ConfigFile << ": "
<< ParsedOptions.getError().message() << "\n";
continue;
}
return OptionsSource(*ParsedOptions, ConfigFile.c_str());
}
return llvm::None;
}
/// \brief Parses -line-filter option and stores it to the \c Options.
std::error_code parseLineFilter(StringRef LineFilter,
clang::tidy::ClangTidyGlobalOptions &Options) {
llvm::yaml::Input Input(LineFilter);
Input >> Options.LineFilter;
return Input.error();
}
llvm::ErrorOr<ClangTidyOptions> parseConfiguration(StringRef Config) {
llvm::yaml::Input Input(Config);
ClangTidyOptions Options;
Input >> Options;
if (Input.error())
return Input.error();
return Options;
}
std::string configurationAsText(const ClangTidyOptions &Options) {
std::string Text;
llvm::raw_string_ostream Stream(Text);
llvm::yaml::Output Output(Stream);
// We use the same mapping method for input and output, so we need a non-const
// reference here.
ClangTidyOptions NonConstValue = Options;
Output << NonConstValue;
return Stream.str();
}
} // namespace tidy
} // namespace clang

View File

@@ -1,274 +0,0 @@
//===--- ClangTidyOptions.h - clang-tidy ------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CLANGTIDYOPTIONS_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CLANGTIDYOPTIONS_H
#include "llvm/ADT/Optional.h"
#include "llvm/ADT/StringMap.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/ErrorOr.h"
#include <functional>
#include <map>
#include <string>
#include <system_error>
#include <utility>
#include <vector>
namespace clang {
namespace tidy {
/// \brief Contains a list of line ranges in a single file.
struct FileFilter {
/// \brief File name.
std::string Name;
/// \brief LineRange is a pair<start, end> (inclusive).
typedef std::pair<unsigned, unsigned> LineRange;
/// \brief A list of line ranges in this file, for which we show warnings.
std::vector<LineRange> LineRanges;
};
/// \brief Global options. These options are neither stored nor read from
/// configuration files.
struct ClangTidyGlobalOptions {
/// \brief Output warnings from certain line ranges of certain files only.
/// If empty, no warnings will be filtered.
std::vector<FileFilter> LineFilter;
};
/// \brief Contains options for clang-tidy. These options may be read from
/// configuration files, and may be different for different translation units.
struct ClangTidyOptions {
/// \brief These options are used for all settings that haven't been
/// overridden by the \c OptionsProvider.
///
/// Allow no checks and no headers by default. This method initializes
/// check-specific options by calling \c ClangTidyModule::getModuleOptions()
/// of each registered \c ClangTidyModule.
static ClangTidyOptions getDefaults();
/// \brief Creates a new \c ClangTidyOptions instance combined from all fields
/// of this instance overridden by the fields of \p Other that have a value.
ClangTidyOptions mergeWith(const ClangTidyOptions &Other) const;
/// \brief Checks filter.
llvm::Optional<std::string> Checks;
/// \brief WarningsAsErrors filter.
llvm::Optional<std::string> WarningsAsErrors;
/// \brief Output warnings from headers matching this filter. Warnings from
/// main files will always be displayed.
llvm::Optional<std::string> HeaderFilterRegex;
/// \brief Output warnings from system headers matching \c HeaderFilterRegex.
llvm::Optional<bool> SystemHeaders;
/// \brief Turns on temporary destructor-based analysis.
llvm::Optional<bool> AnalyzeTemporaryDtors;
/// \brief Format code around applied fixes with clang-format using this
/// style.
///
/// Can be one of:
/// * 'none' - don't format code around applied fixes;
/// * 'llvm', 'google', 'mozilla' or other predefined clang-format style
/// names;
/// * 'file' - use the .clang-format file in the closest parent directory of
/// each source file;
/// * '{inline-formatting-style-in-yaml-format}'.
///
/// See clang-format documentation for more about configuring format style.
llvm::Optional<std::string> FormatStyle;
/// \brief Specifies the name or e-mail of the user running clang-tidy.
///
/// This option is used, for example, to place the correct user name in TODO()
/// comments in the relevant check.
llvm::Optional<std::string> User;
typedef std::pair<std::string, std::string> StringPair;
typedef std::map<std::string, std::string> OptionMap;
/// \brief Key-value mapping used to store check-specific options.
OptionMap CheckOptions;
typedef std::vector<std::string> ArgList;
/// \brief Add extra compilation arguments to the end of the list.
llvm::Optional<ArgList> ExtraArgs;
/// \brief Add extra compilation arguments to the start of the list.
llvm::Optional<ArgList> ExtraArgsBefore;
};
/// \brief Abstract interface for retrieving various ClangTidy options.
class ClangTidyOptionsProvider {
public:
static const char OptionsSourceTypeDefaultBinary[];
static const char OptionsSourceTypeCheckCommandLineOption[];
static const char OptionsSourceTypeConfigCommandLineOption[];
virtual ~ClangTidyOptionsProvider() {}
/// \brief Returns global options, which are independent of the file.
virtual const ClangTidyGlobalOptions &getGlobalOptions() = 0;
/// \brief ClangTidyOptions and its source.
//
/// clang-tidy has 3 types of the sources in order of increasing priority:
/// * clang-tidy binary.
/// * '-config' commandline option or a specific configuration file. If the
/// commandline option is specified, clang-tidy will ignore the
/// configuration file.
/// * '-checks' commandline option.
typedef std::pair<ClangTidyOptions, std::string> OptionsSource;
/// \brief Returns an ordered vector of OptionsSources, in order of increasing
/// priority.
virtual std::vector<OptionsSource>
getRawOptions(llvm::StringRef FileName) = 0;
/// \brief Returns options applying to a specific translation unit with the
/// specified \p FileName.
ClangTidyOptions getOptions(llvm::StringRef FileName);
};
/// \brief Implementation of the \c ClangTidyOptionsProvider interface, which
/// returns the same options for all files.
class DefaultOptionsProvider : public ClangTidyOptionsProvider {
public:
DefaultOptionsProvider(const ClangTidyGlobalOptions &GlobalOptions,
const ClangTidyOptions &Options)
: GlobalOptions(GlobalOptions), DefaultOptions(Options) {}
const ClangTidyGlobalOptions &getGlobalOptions() override {
return GlobalOptions;
}
std::vector<OptionsSource> getRawOptions(llvm::StringRef FileName) override;
private:
ClangTidyGlobalOptions GlobalOptions;
ClangTidyOptions DefaultOptions;
};
/// \brief Implementation of ClangTidyOptions interface, which is used for
/// '-config' command-line option.
class ConfigOptionsProvider : public DefaultOptionsProvider {
public:
ConfigOptionsProvider(const ClangTidyGlobalOptions &GlobalOptions,
const ClangTidyOptions &DefaultOptions,
const ClangTidyOptions &ConfigOptions,
const ClangTidyOptions &OverrideOptions);
std::vector<OptionsSource> getRawOptions(llvm::StringRef FileName) override;
private:
ClangTidyOptions ConfigOptions;
ClangTidyOptions OverrideOptions;
};
/// \brief Implementation of the \c ClangTidyOptionsProvider interface, which
/// tries to find a configuration file in the closest parent directory of each
/// source file.
///
/// By default, files named ".clang-tidy" will be considered, and the
/// \c clang::tidy::parseConfiguration function will be used for parsing, but a
/// custom set of configuration file names and parsing functions can be
/// specified using the appropriate constructor.
class FileOptionsProvider : public DefaultOptionsProvider {
public:
// \brief A pair of configuration file base name and a function parsing
// configuration from text in the corresponding format.
typedef std::pair<std::string, std::function<llvm::ErrorOr<ClangTidyOptions>(
llvm::StringRef)>>
ConfigFileHandler;
/// \brief Configuration file handlers listed in the order of priority.
///
/// Custom configuration file formats can be supported by constructing the
/// list of handlers and passing it to the appropriate \c FileOptionsProvider
/// constructor. E.g. initialization of a \c FileOptionsProvider with support
/// of a custom configuration file format for files named ".my-tidy-config"
/// could look similar to this:
/// \code
/// FileOptionsProvider::ConfigFileHandlers ConfigHandlers;
/// ConfigHandlers.emplace_back(".my-tidy-config", parseMyConfigFormat);
/// ConfigHandlers.emplace_back(".clang-tidy", parseConfiguration);
/// return llvm::make_unique<FileOptionsProvider>(
/// GlobalOptions, DefaultOptions, OverrideOptions, ConfigHandlers);
/// \endcode
///
/// With the order of handlers shown above, the ".my-tidy-config" file would
/// take precedence over ".clang-tidy" if both reside in the same directory.
typedef std::vector<ConfigFileHandler> ConfigFileHandlers;
/// \brief Initializes the \c FileOptionsProvider instance.
///
/// \param GlobalOptions are just stored and returned to the caller of
/// \c getGlobalOptions.
///
/// \param DefaultOptions are used for all settings not specified in a
/// configuration file.
///
/// If any of the \param OverrideOptions fields are set, they will override
/// whatever options are read from the configuration file.
FileOptionsProvider(const ClangTidyGlobalOptions &GlobalOptions,
const ClangTidyOptions &DefaultOptions,
const ClangTidyOptions &OverrideOptions);
/// \brief Initializes the \c FileOptionsProvider instance with a custom set
/// of configuration file handlers.
///
/// \param GlobalOptions are just stored and returned to the caller of
/// \c getGlobalOptions.
///
/// \param DefaultOptions are used for all settings not specified in a
/// configuration file.
///
/// If any of the \param OverrideOptions fields are set, they will override
/// whatever options are read from the configuration file.
///
/// \param ConfigHandlers specifies a custom set of configuration file
/// handlers. Each handler is a pair of configuration file name and a function
/// that can parse configuration from this file type. The configuration files
/// in each directory are searched for in the order of appearance in
/// \p ConfigHandlers.
FileOptionsProvider(const ClangTidyGlobalOptions &GlobalOptions,
const ClangTidyOptions &DefaultOptions,
const ClangTidyOptions &OverrideOptions,
const ConfigFileHandlers &ConfigHandlers);
std::vector<OptionsSource> getRawOptions(llvm::StringRef FileName) override;
protected:
/// \brief Try to read configuration files from \p Directory using registered
/// \c ConfigHandlers.
llvm::Optional<OptionsSource> tryReadConfigFile(llvm::StringRef Directory);
llvm::StringMap<OptionsSource> CachedOptions;
ClangTidyOptions OverrideOptions;
ConfigFileHandlers ConfigHandlers;
};
/// \brief Parses LineFilter from JSON and stores it to the \p Options.
std::error_code parseLineFilter(llvm::StringRef LineFilter,
ClangTidyGlobalOptions &Options);
/// \brief Parses configuration from JSON and returns \c ClangTidyOptions or an
/// error.
llvm::ErrorOr<ClangTidyOptions> parseConfiguration(llvm::StringRef Config);
/// \brief Serializes configuration to a YAML-encoded string.
std::string configurationAsText(const ClangTidyOptions &Options);
} // end namespace tidy
} // end namespace clang
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CLANGTIDYOPTIONS_H

View File

@@ -1,342 +0,0 @@
#!/usr/bin/env python
#
#===- add_new_check.py - clang-tidy check generator ----------*- python -*--===#
#
# The LLVM Compiler Infrastructure
#
# This file is distributed under the University of Illinois Open Source
# License. See LICENSE.TXT for details.
#
#===------------------------------------------------------------------------===#
import os
import re
import sys
# Adapts the module's CMakelist file. Returns 'True' if it could add a new entry
# and 'False' if the entry already existed.
def adapt_cmake(module_path, check_name_camel):
filename = os.path.join(module_path, 'CMakeLists.txt')
with open(filename, 'r') as f:
lines = f.readlines()
cpp_file = check_name_camel + '.cpp'
# Figure out whether this check already exists.
for line in lines:
if line.strip() == cpp_file:
return False
print('Updating %s...' % filename)
with open(filename, 'wb') as f:
cpp_found = False
file_added = False
for line in lines:
cpp_line = line.strip().endswith('.cpp')
if (not file_added) and (cpp_line or cpp_found):
cpp_found = True
if (line.strip() > cpp_file) or (not cpp_line):
f.write(' ' + cpp_file + '\n')
file_added = True
f.write(line)
return True
# Adds a header for the new check.
def write_header(module_path, module, check_name, check_name_camel):
check_name_dashes = module + '-' + check_name
filename = os.path.join(module_path, check_name_camel) + '.h'
print('Creating %s...' % filename)
with open(filename, 'wb') as f:
header_guard = ('LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_' + module.upper() + '_'
+ check_name.upper().replace('-', '_') + '_H')
f.write('//===--- ')
f.write(os.path.basename(filename))
f.write(' - clang-tidy')
f.write('-' * max(0, 43 - len(os.path.basename(filename))))
f.write('*- C++ -*-===//')
f.write("""
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef %(header_guard)s
#define %(header_guard)s
#include "../ClangTidy.h"
namespace clang {
namespace tidy {
namespace %(module)s {
/// FIXME: Write a short description.
///
/// For the user-facing documentation see:
/// http://clang.llvm.org/extra/clang-tidy/checks/%(check_name_dashes)s.html
class %(check_name)s : public ClangTidyCheck {
public:
%(check_name)s(StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context) {}
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
};
} // namespace %(module)s
} // namespace tidy
} // namespace clang
#endif // %(header_guard)s
""" % {'header_guard': header_guard,
'check_name': check_name_camel,
'check_name_dashes': check_name_dashes,
'module': module})
# Adds the implementation of the new check.
def write_implementation(module_path, module, check_name_camel):
filename = os.path.join(module_path, check_name_camel) + '.cpp'
print('Creating %s...' % filename)
with open(filename, 'wb') as f:
f.write('//===--- ')
f.write(os.path.basename(filename))
f.write(' - clang-tidy')
f.write('-' * max(0, 52 - len(os.path.basename(filename))))
f.write('-===//')
f.write("""
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "%(check_name)s.h"
#include "clang/AST/ASTContext.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
using namespace clang::ast_matchers;
namespace clang {
namespace tidy {
namespace %(module)s {
void %(check_name)s::registerMatchers(MatchFinder *Finder) {
// FIXME: Add matchers.
Finder->addMatcher(functionDecl().bind("x"), this);
}
void %(check_name)s::check(const MatchFinder::MatchResult &Result) {
// FIXME: Add callback implementation.
const auto *MatchedDecl = Result.Nodes.getNodeAs<FunctionDecl>("x");
if (MatchedDecl->getName().startswith("awesome_"))
return;
diag(MatchedDecl->getLocation(), "function %%0 is insufficiently awesome")
<< MatchedDecl
<< FixItHint::CreateInsertion(MatchedDecl->getLocation(), "awesome_");
}
} // namespace %(module)s
} // namespace tidy
} // namespace clang
""" % {'check_name': check_name_camel,
'module': module})
# Modifies the module to include the new check.
def adapt_module(module_path, module, check_name, check_name_camel):
modulecpp = filter(lambda p: p.lower() == module.lower() + 'tidymodule.cpp',
os.listdir(module_path))[0]
filename = os.path.join(module_path, modulecpp)
with open(filename, 'r') as f:
lines = f.readlines()
print('Updating %s...' % filename)
with open(filename, 'wb') as f:
header_added = False
header_found = False
check_added = False
check_decl = (' CheckFactories.registerCheck<' + check_name_camel +
'>(\n "' + module + '-' + check_name + '");\n')
for line in lines:
if not header_added:
match = re.search('#include "(.*)"', line)
if match:
header_found = True
if match.group(1) > check_name_camel:
header_added = True
f.write('#include "' + check_name_camel + '.h"\n')
elif header_found:
header_added = True
f.write('#include "' + check_name_camel + '.h"\n')
if not check_added:
if line.strip() == '}':
check_added = True
f.write(check_decl)
else:
match = re.search('registerCheck<(.*)>', line)
if match and match.group(1) > check_name_camel:
check_added = True
f.write(check_decl)
f.write(line)
# Adds a release notes entry.
def add_release_notes(module_path, module, check_name):
check_name_dashes = module + '-' + check_name
filename = os.path.normpath(os.path.join(module_path,
'../../docs/ReleaseNotes.rst'))
with open(filename, 'r') as f:
lines = f.readlines()
print('Updating %s...' % filename)
with open(filename, 'wb') as f:
note_added = False
header_found = False
for line in lines:
if not note_added:
match = re.search('Improvements to clang-tidy', line)
if match:
header_found = True
elif header_found:
if not line.startswith('----'):
f.write("""
- New `%s
<http://clang.llvm.org/extra/clang-tidy/checks/%s.html>`_ check
FIXME: add release notes.
""" % (check_name_dashes, check_name_dashes))
note_added = True
f.write(line)
# Adds a test for the check.
def write_test(module_path, module, check_name):
check_name_dashes = module + '-' + check_name
filename = os.path.normpath(os.path.join(module_path, '../../test/clang-tidy',
check_name_dashes + '.cpp'))
print('Creating %s...' % filename)
with open(filename, 'wb') as f:
f.write("""// RUN: %%check_clang_tidy %%s %(check_name_dashes)s %%t
// FIXME: Add something that triggers the check here.
void f();
// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: function 'f' is insufficiently awesome [%(check_name_dashes)s]
// FIXME: Verify the applied fix.
// * Make the CHECK patterns specific enough and try to make verified lines
// unique to avoid incorrect matches.
// * Use {{}} for regular expressions.
// CHECK-FIXES: {{^}}void awesome_f();{{$}}
// FIXME: Add something that doesn't trigger the check here.
void awesome_f2();
""" % {'check_name_dashes': check_name_dashes})
# Recreates the list of checks in the docs/clang-tidy/checks directory.
def update_checks_list(clang_tidy_path):
docs_dir = os.path.join(clang_tidy_path, '../docs/clang-tidy/checks')
filename = os.path.normpath(os.path.join(docs_dir, 'list.rst'))
with open(filename, 'r') as f:
lines = f.readlines()
doc_files = filter(lambda s: s.endswith('.rst') and s != 'list.rst',
os.listdir(docs_dir))
doc_files.sort()
def format_link(doc_file):
check_name = doc_file.replace('.rst', '')
with open(os.path.join(docs_dir, doc_file), 'r') as doc:
content = doc.read()
match = re.search('.*:orphan:.*', content)
if match:
return ''
match = re.search('.*:http-equiv=refresh: \d+;URL=(.*).html.*',
content)
if match:
return ' %(check)s (redirects to %(target)s) <%(check)s>\n' % {
'check': check_name,
'target': match.group(1)
}
return ' %s\n' % check_name
checks = map(format_link, doc_files)
print('Updating %s...' % filename)
with open(filename, 'wb') as f:
for line in lines:
f.write(line)
if line.startswith('.. toctree::'):
f.writelines(checks)
break
# Adds a documentation for the check.
def write_docs(module_path, module, check_name):
check_name_dashes = module + '-' + check_name
filename = os.path.normpath(os.path.join(
module_path, '../../docs/clang-tidy/checks/', check_name_dashes + '.rst'))
print('Creating %s...' % filename)
with open(filename, 'wb') as f:
f.write(""".. title:: clang-tidy - %(check_name_dashes)s
%(check_name_dashes)s
%(underline)s
FIXME: Describe what patterns does the check detect and why. Give examples.
""" % {'check_name_dashes': check_name_dashes,
'underline': '=' * len(check_name_dashes)})
def main():
if len(sys.argv) == 2 and sys.argv[1] == '--update-docs':
update_checks_list(os.path.dirname(sys.argv[0]))
return
if len(sys.argv) != 3:
print """\
Usage: add_new_check.py <module> <check>, e.g.
add_new_check.py misc awesome-functions
Alternatively, run 'add_new_check.py --update-docs' to just update the list of
documentation files."""
return
module = sys.argv[1]
check_name = sys.argv[2]
if check_name.startswith(module):
print 'Check name "%s" must not start with the module "%s". Exiting.' % (
check_name, module)
return
check_name_camel = ''.join(map(lambda elem: elem.capitalize(),
check_name.split('-'))) + 'Check'
clang_tidy_path = os.path.dirname(sys.argv[0])
module_path = os.path.join(clang_tidy_path, module)
if not adapt_cmake(module_path, check_name_camel):
return
write_header(module_path, module, check_name, check_name_camel)
write_implementation(module_path, module, check_name_camel)
adapt_module(module_path, module, check_name, check_name_camel)
add_release_notes(module_path, module, check_name)
write_test(module_path, module, check_name)
write_docs(module_path, module, check_name)
update_checks_list(clang_tidy_path)
print('Done. Now it\'s your turn!')
if __name__ == '__main__':
main()

View File

@@ -1,46 +0,0 @@
//===--- AndroidTidyModule.cpp - clang-tidy--------------------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "../ClangTidy.h"
#include "../ClangTidyModule.h"
#include "../ClangTidyModuleRegistry.h"
#include "CloexecCreatCheck.h"
#include "CloexecFopenCheck.h"
#include "CloexecOpenCheck.h"
#include "CloexecSocketCheck.h"
using namespace clang::ast_matchers;
namespace clang {
namespace tidy {
namespace android {
/// This module is for Android specific checks.
class AndroidModule : public ClangTidyModule {
public:
void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override {
CheckFactories.registerCheck<CloexecCreatCheck>("android-cloexec-creat");
CheckFactories.registerCheck<CloexecFopenCheck>("android-cloexec-fopen");
CheckFactories.registerCheck<CloexecOpenCheck>("android-cloexec-open");
CheckFactories.registerCheck<CloexecSocketCheck>("android-cloexec-socket");
}
};
// Register the AndroidTidyModule using this statically initialized variable.
static ClangTidyModuleRegistry::Add<AndroidModule>
X("android-module", "Adds Android platform checks.");
} // namespace android
// This anchor is used to force the linker to link in the generated object file
// and thus register the AndroidModule.
volatile int AndroidModuleAnchorSource = 0;
} // namespace tidy
} // namespace clang

View File

@@ -1,17 +0,0 @@
set(LLVM_LINK_COMPONENTS support)
add_clang_library(clangTidyAndroidModule
AndroidTidyModule.cpp
CloexecCreatCheck.cpp
CloexecFopenCheck.cpp
CloexecOpenCheck.cpp
CloexecSocketCheck.cpp
LINK_LIBS
clangAST
clangASTMatchers
clangBasic
clangLex
clangTidy
clangTidyUtils
)

View File

@@ -1,59 +0,0 @@
//===--- CloexecCreatCheck.cpp - clang-tidy--------------------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "CloexecCreatCheck.h"
#include "clang/AST/ASTContext.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/Lex/Lexer.h"
using namespace clang::ast_matchers;
namespace clang {
namespace tidy {
namespace android {
void CloexecCreatCheck::registerMatchers(MatchFinder *Finder) {
auto CharPointerType = hasType(pointerType(pointee(isAnyCharacter())));
auto MODETType = hasType(namedDecl(hasName("mode_t")));
Finder->addMatcher(
callExpr(callee(functionDecl(isExternC(), returns(isInteger()),
hasName("creat"),
hasParameter(0, CharPointerType),
hasParameter(1, MODETType))
.bind("funcDecl")))
.bind("creatFn"),
this);
}
void CloexecCreatCheck::check(const MatchFinder::MatchResult &Result) {
const auto *MatchedCall = Result.Nodes.getNodeAs<CallExpr>("creatFn");
const SourceManager &SM = *Result.SourceManager;
const std::string &ReplacementText =
(Twine("open (") +
Lexer::getSourceText(CharSourceRange::getTokenRange(
MatchedCall->getArg(0)->getSourceRange()),
SM, Result.Context->getLangOpts()) +
", O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, " +
Lexer::getSourceText(CharSourceRange::getTokenRange(
MatchedCall->getArg(1)->getSourceRange()),
SM, Result.Context->getLangOpts()) +
")")
.str();
diag(MatchedCall->getLocStart(),
"prefer open() to creat() because open() allows O_CLOEXEC")
<< FixItHint::CreateReplacement(MatchedCall->getSourceRange(),
ReplacementText);
}
} // namespace android
} // namespace tidy
} // namespace clang

View File

@@ -1,35 +0,0 @@
//===--- CloexecCreatCheck.h - clang-tidy------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_ANDROID_CLOEXEC_CREAT_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_ANDROID_CLOEXEC_CREAT_H
#include "../ClangTidy.h"
namespace clang {
namespace tidy {
namespace android {
/// creat() is better to be replaced by open().
/// Find the usage of creat() and redirect user to use open().
/// http://clang.llvm.org/extra/clang-tidy/checks/android-cloexec-creat.html
class CloexecCreatCheck : public ClangTidyCheck {
public:
CloexecCreatCheck(StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context) {}
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
};
} // namespace android
} // namespace tidy
} // namespace clang
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_ANDROID_CLOEXEC_CREAT_H

View File

@@ -1,74 +0,0 @@
//===--- CloexecFopenCheck.cpp - clang-tidy--------------------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details. //
//===----------------------------------------------------------------------===//
#include "CloexecFopenCheck.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/Type.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/Lex/Lexer.h"
using namespace clang::ast_matchers;
namespace clang {
namespace tidy {
namespace android {
namespace {
static const char MODE = 'e';
// Build the replace text. If it's string constant, add 'e' directly in the end
// of the string. Else, add "e".
std::string BuildReplaceText(const Expr *Arg, const SourceManager &SM,
const LangOptions &LangOpts) {
if (Arg->getLocStart().isMacroID())
return (Lexer::getSourceText(
CharSourceRange::getTokenRange(Arg->getSourceRange()), SM,
LangOpts) +
" \"" + Twine(MODE) + "\"")
.str();
StringRef SR = cast<StringLiteral>(Arg->IgnoreParenCasts())->getString();
return ("\"" + SR + Twine(MODE) + "\"").str();
}
} // namespace
void CloexecFopenCheck::registerMatchers(MatchFinder *Finder) {
auto CharPointerType = hasType(pointerType(pointee(isAnyCharacter())));
Finder->addMatcher(
callExpr(callee(functionDecl(isExternC(), returns(asString("FILE *")),
hasName("fopen"),
hasParameter(0, CharPointerType),
hasParameter(1, CharPointerType))
.bind("funcDecl")))
.bind("fopenFn"),
this);
}
void CloexecFopenCheck::check(const MatchFinder::MatchResult &Result) {
const auto *MatchedCall = Result.Nodes.getNodeAs<CallExpr>("fopenFn");
const auto *FD = Result.Nodes.getNodeAs<FunctionDecl>("funcDecl");
const Expr *ModeArg = MatchedCall->getArg(1);
// Check if the 'e' may be in the mode string.
const auto *ModeStr = dyn_cast<StringLiteral>(ModeArg->IgnoreParenCasts());
if (!ModeStr || (ModeStr->getString().find(MODE) != StringRef::npos))
return;
const std::string &ReplacementText = BuildReplaceText(
ModeArg, *Result.SourceManager, Result.Context->getLangOpts());
diag(ModeArg->getLocStart(), "use %0 mode 'e' to set O_CLOEXEC")
<< FD
<< FixItHint::CreateReplacement(ModeArg->getSourceRange(),
ReplacementText);
}
} // namespace android
} // namespace tidy
} // namespace clang

View File

@@ -1,38 +0,0 @@
//===--- CloexecFopenCheck.h - clang-tidy------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source //
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_ANDROID_CLOEXEC_FOPEN_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_ANDROID_CLOEXEC_FOPEN_H
#include "../ClangTidy.h"
namespace clang {
namespace tidy {
namespace android {
/// fopen() is suggested to include "e" in their mode string; like "re" would be
/// better than "r".
///
/// This check only works when corresponding argument is StringLiteral. No
/// constant propagation.
///
/// http://clang.llvm.org/extra/clang-tidy/checks/android-cloexec-fopen.html
class CloexecFopenCheck : public ClangTidyCheck {
public:
CloexecFopenCheck(StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context) {}
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
};
} // namespace android
} // namespace tidy
} // namespace clang
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_ANDROID_CLOEXEC_FOPEN_H

View File

@@ -1,74 +0,0 @@
//===--- CloexecOpenCheck.cpp - clang-tidy---------------------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "CloexecOpenCheck.h"
#include "../utils/ASTUtils.h"
#include "clang/AST/ASTContext.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/Lex/Lexer.h"
using namespace clang::ast_matchers;
namespace clang {
namespace tidy {
namespace android {
static constexpr const char *O_CLOEXEC = "O_CLOEXEC";
void CloexecOpenCheck::registerMatchers(MatchFinder *Finder) {
auto CharPointerType = hasType(pointerType(pointee(isAnyCharacter())));
Finder->addMatcher(
callExpr(callee(functionDecl(isExternC(), returns(isInteger()),
hasAnyName("open", "open64"),
hasParameter(0, CharPointerType),
hasParameter(1, hasType(isInteger())))
.bind("funcDecl")))
.bind("openFn"),
this);
Finder->addMatcher(
callExpr(callee(functionDecl(isExternC(), returns(isInteger()),
hasName("openat"),
hasParameter(0, hasType(isInteger())),
hasParameter(1, CharPointerType),
hasParameter(2, hasType(isInteger())))
.bind("funcDecl")))
.bind("openatFn"),
this);
}
void CloexecOpenCheck::check(const MatchFinder::MatchResult &Result) {
const Expr *FlagArg = nullptr;
if (const auto *OpenFnCall = Result.Nodes.getNodeAs<CallExpr>("openFn"))
FlagArg = OpenFnCall->getArg(1);
else if (const auto *OpenFnCall =
Result.Nodes.getNodeAs<CallExpr>("openatFn"))
FlagArg = OpenFnCall->getArg(2);
assert(FlagArg);
const auto *FD = Result.Nodes.getNodeAs<FunctionDecl>("funcDecl");
// Check the required flag.
SourceManager &SM = *Result.SourceManager;
if (utils::exprHasBitFlagWithSpelling(FlagArg->IgnoreParenCasts(), SM,
Result.Context->getLangOpts(), O_CLOEXEC))
return;
SourceLocation EndLoc =
Lexer::getLocForEndOfToken(SM.getFileLoc(FlagArg->getLocEnd()), 0, SM,
Result.Context->getLangOpts());
diag(EndLoc, "%0 should use %1 where possible")
<< FD << O_CLOEXEC
<< FixItHint::CreateInsertion(EndLoc, (Twine(" | ") + O_CLOEXEC).str());
}
} // namespace android
} // namespace tidy
} // namespace clang

View File

@@ -1,40 +0,0 @@
//===--- CloexecOpenCheck.h - clang-tidy-----------------------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_ANDROID_CLOEXEC_OPEN_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_ANDROID_CLOEXEC_OPEN_H
#include "../ClangTidy.h"
namespace clang {
namespace tidy {
namespace android {
/// Finds code that opens file without using the O_CLOEXEC flag.
///
/// open(), openat(), and open64() had better to include O_CLOEXEC in their
/// flags argument. Only consider simple cases that the corresponding argument
/// is constant or binary operation OR among constants like 'O_CLOEXEC' or
/// 'O_CLOEXEC | O_RDONLY'. No constant propagation is performed.
///
/// Only the symbolic 'O_CLOEXEC' macro definition is checked, not the concrete
/// value.
class CloexecOpenCheck : public ClangTidyCheck {
public:
CloexecOpenCheck(StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context) {}
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
};
} // namespace android
} // namespace tidy
} // namespace clang
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_ANDROID_CLOEXEC_OPEN_H

View File

@@ -1,57 +0,0 @@
//===--- CloexecSocketCheck.cpp - clang-tidy-------------------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "CloexecSocketCheck.h"
#include "../utils/ASTUtils.h"
#include "clang/AST/ASTContext.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
using namespace clang::ast_matchers;
namespace clang {
namespace tidy {
namespace android {
static constexpr const char *SOCK_CLOEXEC = "SOCK_CLOEXEC";
void CloexecSocketCheck::registerMatchers(MatchFinder *Finder) {
Finder->addMatcher(
callExpr(callee(functionDecl(isExternC(), returns(isInteger()),
hasName("socket"),
hasParameter(0, hasType(isInteger())),
hasParameter(1, hasType(isInteger())),
hasParameter(2, hasType(isInteger())))
.bind("funcDecl")))
.bind("socketFn"),
this);
}
void CloexecSocketCheck::check(const MatchFinder::MatchResult &Result) {
const auto *MatchedCall = Result.Nodes.getNodeAs<CallExpr>("socketFn");
const auto *FD = Result.Nodes.getNodeAs<FunctionDecl>("funcDecl");
const Expr *FlagArg = MatchedCall->getArg(1);
SourceManager &SM = *Result.SourceManager;
if (utils::exprHasBitFlagWithSpelling(FlagArg->IgnoreParenCasts(), SM,
Result.Context->getLangOpts(), SOCK_CLOEXEC))
return;
SourceLocation EndLoc =
Lexer::getLocForEndOfToken(SM.getFileLoc(FlagArg->getLocEnd()), 0, SM,
Result.Context->getLangOpts());
diag(EndLoc, "%0 should use %1 where possible")
<< FD << SOCK_CLOEXEC
<< FixItHint::CreateInsertion(EndLoc,
(Twine(" | ") + SOCK_CLOEXEC).str());
}
} // namespace android
} // namespace tidy
} // namespace clang

View File

@@ -1,35 +0,0 @@
//===--- CloexecSocketCheck.h - clang-tidy-----------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_ANDROID_CLOEXEC_SOCKET_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_ANDROID_CLOEXEC_SOCKET_H
#include "../ClangTidy.h"
namespace clang {
namespace tidy {
namespace android {
/// Finds code that uses socket() without using the SOCK_CLOEXEC flag.
///
/// For the user-facing documentation see:
/// http://clang.llvm.org/extra/clang-tidy/checks/android-cloexec-socket.html
class CloexecSocketCheck : public ClangTidyCheck {
public:
CloexecSocketCheck(StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context) {}
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
};
} // namespace android
} // namespace tidy
} // namespace clang
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_ANDROID_CLOEXEC_SOCKET_H

View File

@@ -1,38 +0,0 @@
//===------- BoostTidyModule.cpp - clang-tidy -----------------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "../ClangTidy.h"
#include "../ClangTidyModule.h"
#include "../ClangTidyModuleRegistry.h"
#include "UseToStringCheck.h"
using namespace clang::ast_matchers;
namespace clang {
namespace tidy {
namespace boost {
class BoostModule : public ClangTidyModule {
public:
void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override {
CheckFactories.registerCheck<UseToStringCheck>("boost-use-to-string");
}
};
// Register the BoostModule using this statically initialized variable.
static ClangTidyModuleRegistry::Add<BoostModule> X("boost-module",
"Add boost checks.");
} // namespace boost
// This anchor is used to force the linker to link in the generated object file
// and thus register the BoostModule.
volatile int BoostModuleAnchorSource = 0;
} // namespace tidy
} // namespace clang

View File

@@ -1,14 +0,0 @@
set(LLVM_LINK_COMPONENTS support)
add_clang_library(clangTidyBoostModule
BoostTidyModule.cpp
UseToStringCheck.cpp
LINK_LIBS
clangAST
clangASTMatchers
clangBasic
clangLex
clangTidy
clangTidyUtils
)

View File

@@ -1,73 +0,0 @@
//===--- UseToStringCheck.cpp - clang-tidy---------------------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "UseToStringCheck.h"
using namespace clang::ast_matchers;
namespace clang {
namespace tidy {
namespace boost {
AST_MATCHER(Type, isStrictlyInteger) {
return Node.isIntegerType() && !Node.isAnyCharacterType() &&
!Node.isBooleanType();
}
void UseToStringCheck::registerMatchers(MatchFinder *Finder) {
if (!getLangOpts().CPlusPlus)
return;
Finder->addMatcher(
callExpr(
hasDeclaration(functionDecl(
returns(hasDeclaration(classTemplateSpecializationDecl(
hasName("std::basic_string"),
hasTemplateArgument(0,
templateArgument().bind("char_type"))))),
hasName("boost::lexical_cast"),
hasParameter(0, hasType(qualType(has(substTemplateTypeParmType(
isStrictlyInteger()))))))),
argumentCountIs(1), unless(isInTemplateInstantiation()))
.bind("to_string"),
this);
}
void UseToStringCheck::check(const MatchFinder::MatchResult &Result) {
const auto *Call = Result.Nodes.getNodeAs<CallExpr>("to_string");
auto CharType =
Result.Nodes.getNodeAs<TemplateArgument>("char_type")->getAsType();
StringRef StringType;
if (CharType->isSpecificBuiltinType(BuiltinType::Char_S) ||
CharType->isSpecificBuiltinType(BuiltinType::Char_U))
StringType = "string";
else if (CharType->isSpecificBuiltinType(BuiltinType::WChar_S) ||
CharType->isSpecificBuiltinType(BuiltinType::WChar_U))
StringType = "wstring";
else
return;
auto Loc = Call->getLocStart();
auto Diag =
diag(Loc, "use std::to_%0 instead of boost::lexical_cast<std::%0>")
<< StringType;
if (Loc.isMacroID())
return;
Diag << FixItHint::CreateReplacement(
CharSourceRange::getCharRange(Call->getLocStart(),
Call->getArg(0)->getLocStart()),
(llvm::Twine("std::to_") + StringType + "(").str());
}
} // namespace boost
} // namespace tidy
} // namespace clang

View File

@@ -1,37 +0,0 @@
//===--- UseToStringCheck.h - clang-tidy-------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BOOST_USE_TO_STRING_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BOOST_USE_TO_STRING_H
#include "../ClangTidy.h"
namespace clang {
namespace tidy {
namespace boost {
/// Finds calls to ``boost::lexical_cast<std::string>`` and
/// ``boost::lexical_cast<std::wstring>`` and replaces them with
/// ``std::to_string`` and ``std::to_wstring`` calls.
///
/// For the user-facing documentation see:
/// http://clang.llvm.org/extra/clang-tidy/checks/boost-use-to-string.html
class UseToStringCheck : public ClangTidyCheck {
public:
UseToStringCheck(StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context) {}
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
};
} // namespace boost
} // namespace tidy
} // namespace clang
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BOOST_USE_TO_STRING_H

View File

@@ -1,41 +0,0 @@
//===--- BugproneTidyModule.cpp - clang-tidy ------------------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "../ClangTidy.h"
#include "../ClangTidyModule.h"
#include "../ClangTidyModuleRegistry.h"
#include "SuspiciousMemsetUsageCheck.h"
#include "UndefinedMemoryManipulationCheck.h"
namespace clang {
namespace tidy {
namespace bugprone {
class BugproneModule : public ClangTidyModule {
public:
void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override {
CheckFactories.registerCheck<SuspiciousMemsetUsageCheck>(
"bugprone-suspicious-memset-usage");
CheckFactories.registerCheck<UndefinedMemoryManipulationCheck>(
"bugprone-undefined-memory-manipulation");
}
};
} // namespace bugprone
// Register the BugproneTidyModule using this statically initialized variable.
static ClangTidyModuleRegistry::Add<bugprone::BugproneModule>
X("bugprone-module", "Adds checks for bugprone code constructs.");
// This anchor is used to force the linker to link in the generated object file
// and thus register the BugproneModule.
volatile int BugproneModuleAnchorSource = 0;
} // namespace tidy
} // namespace clang

View File

@@ -1,17 +0,0 @@
set(LLVM_LINK_COMPONENTS support)
add_clang_library(clangTidyBugproneModule
BugproneTidyModule.cpp
SuspiciousMemsetUsageCheck.cpp
UndefinedMemoryManipulationCheck.cpp
LINK_LIBS
clangAnalysis
clangAST
clangASTMatchers
clangBasic
clangLex
clangTidy
clangTidyUtils
clangTooling
)

Some files were not shown because too many files have changed in this diff Show More