Compare commits

..

169 Commits

Author SHA1 Message Date
Hans Wennborg
ce25e1a77e Merging r279647:
------------------------------------------------------------------------
r279647 | sanjoy | 2016-08-24 11:10:21 -0700 (Wed, 24 Aug 2016) | 5 lines

[SCCP] Don't delete side-effecting instructions

I'm not sure if the `!isa<CallInst>(Inst) &&
!isa<TerminatorInst>(Inst))` bit is correct either, but this fixes the
case we know is broken.
------------------------------------------------------------------------

llvm-svn: 279689
2016-08-24 23:46:52 +00:00
Hans Wennborg
08064c20db Merging r279268:
------------------------------------------------------------------------
r279268 | majnemer | 2016-08-19 09:37:40 -0700 (Fri, 19 Aug 2016) | 5 lines

[CloneFunction] Don't remove unrelated nodes from the CGSSC

CGSCC use a WeakVH to track call sites.  RAUW a call within a function
can result in that WeakVH getting confused about whether or not the call
site is still around.
------------------------------------------------------------------------

llvm-svn: 279477
2016-08-22 21:23:55 +00:00
Hans Wennborg
f7ff8644fb Merging r279368:
------------------------------------------------------------------------
r279368 | ed | 2016-08-20 03:54:51 -0700 (Sat, 20 Aug 2016) | 12 lines

Add R_386_TLS_LE as a relocation having an implicit addend.

TLS on i386 in non-PIE/PIC code seems broken right now, because we don't
properly add the addend encoded in the instruction to the resulting
offset when processing R_386_TLS_LE relocations.

Extend one of the existing tests for TLS on i686 to use an addend.

PR:		https://llvm.org/bugs/show_bug.cgi?id=29068
Reviewed by:	ruiu
Differential Revision:	https://reviews.llvm.org/D23741

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

llvm-svn: 279476
2016-08-22 21:15:19 +00:00
Hans Wennborg
0f664e3843 Merging r279369 and update the test:
------------------------------------------------------------------------
r279369 | mssimpso | 2016-08-20 07:10:06 -0700 (Sat, 20 Aug 2016) | 1 line

[SLP] Add command line option for minimum tree size (NFC)
------------------------------------------------------------------------

llvm-svn: 279474
2016-08-22 21:04:17 +00:00
Hans Wennborg
e7d842f670 Merging r279352:
------------------------------------------------------------------------
r279352 | eugenis | 2016-08-19 17:38:55 -0700 (Fri, 19 Aug 2016) | 1 line

[msan] Disable prlimit test on glibc < 2.13.
------------------------------------------------------------------------

llvm-svn: 279471
2016-08-22 20:44:16 +00:00
Hans Wennborg
afe209420a Fix gather-root.ll SLP vectorizer test to not expose UB.
The undefined behaviour (signed integer overflow) is not a regression
in itself as it was already there, but the test exposing it is a
regression compared to rc1, i.e. the lit tests no longer run ubsan-clean.

This commit fixes the test based on Matt's change in r279125 to not
expose the undefined behaviour.

llvm-svn: 279468
2016-08-22 20:27:42 +00:00
Rui Ueyama
945a843fca Add a note that 3.9 is a major milestone for us.
Differential Revision: https://reviews.llvm.org/D23695

llvm-svn: 279260
2016-08-19 15:30:54 +00:00
Anastasia Stulova
6c02be1058 Minor change to OpenCL release notes to add one missing item.
llvm-svn: 279224
2016-08-19 09:19:36 +00:00
Hans Wennborg
cf3f88d23a ReleaseNotes: fix gold plugin link
llvm-svn: 279177
2016-08-18 23:13:30 +00:00
Hans Wennborg
c867c52e48 ReleaseNotes: missing char
llvm-svn: 279176
2016-08-18 23:01:18 +00:00
Hans Wennborg
c0de4d5dfe Merging r279125 and r278343:
------------------------------------------------------------------------
r279125 | mssimpso | 2016-08-18 12:50:32 -0700 (Thu, 18 Aug 2016) | 14 lines

[SLP] Initialize VectorizedValue when gathering

We abort building vectorizable trees in some cases (e.g., if the maximum
recursion depth is reached, if the region size is too large, etc.). If this
happens for a reduction, we can be left with a root entry that needs to be
gathered. For these cases, we need make sure we actually set VectorizedValue to
the resulting vector.

This patch ensures we properly set VectorizedValue, and it also ensures the
insertelement sequence generated for the gathers is inserted at the correct
location.

Reference: https://llvm.org/bugs/show_bug.cgi?id=28330
Differential Revison: https://reviews.llvm.org/D23410
------------------------------------------------------------------------

------------------------------------------------------------------------
r278343 | mssimpso | 2016-08-11 08:28:45 -0700 (Thu, 11 Aug 2016) | 1 line

[SLP] Make RecursionMaxDepth a command line option (NFC)
------------------------------------------------------------------------

llvm-svn: 279174
2016-08-18 22:38:06 +00:00
Hans Wennborg
8d1eea8bab ReleaseNotes: sphinx build fixes
llvm-svn: 279147
2016-08-18 21:09:28 +00:00
Hans Wennborg
95487b9324 ReleaseNotes: sphinx build fixes
llvm-svn: 279146
2016-08-18 21:09:19 +00:00
Hans Wennborg
1917148ae1 Drop doxygen link; the release ships it in tarball instead
llvm-svn: 279144
2016-08-18 20:54:38 +00:00
Hans Wennborg
b97bb7bd7e ReleaseNotes: tidy up
llvm-svn: 279142
2016-08-18 20:52:16 +00:00
Hans Wennborg
ff55bde145 ReleaseNotes: drop in-progress warning
llvm-svn: 279139
2016-08-18 20:39:47 +00:00
Hans Wennborg
67b2d3e548 ReleaseNotes: clean up
llvm-svn: 279138
2016-08-18 20:38:52 +00:00
Hans Wennborg
09a8bb52ec ReleaseNotes: remove in-progress warning
llvm-svn: 279137
2016-08-18 20:37:36 +00:00
Hans Wennborg
febb13003a ReleaseNotes: tidy up
llvm-svn: 279136
2016-08-18 20:36:59 +00:00
Hans Wennborg
a87302cea2 ReleaseNotes: remove in-progress warning
llvm-svn: 279135
2016-08-18 20:34:11 +00:00
Hans Wennborg
39970299fd ReleaseNotes: clean up
llvm-svn: 279134
2016-08-18 20:32:21 +00:00
Hans Wennborg
cce20faf0e ReleaseNotes: remove in-progress warning
llvm-svn: 279131
2016-08-18 20:18:58 +00:00
Hans Wennborg
893ec2fa37 ReleaseNotes: drop in-progress warning
llvm-svn: 279130
2016-08-18 20:15:53 +00:00
Hans Wennborg
e4cfe21a40 ReleaseNotes: reduced jump table density
llvm-svn: 279128
2016-08-18 20:07:07 +00:00
Hans Wennborg
52078d5b4c Merging r278559:
------------------------------------------------------------------------
r278559 | efriedma | 2016-08-12 13:28:02 -0700 (Fri, 12 Aug 2016) | 7 lines

[AArch64LoadStoreOpt] Handle offsets correctly for post-indexed paired loads.

Trunk would try to create something like "stp x9, x8, [x0], #512", which isn't actually a valid instruction.

Differential revision: https://reviews.llvm.org/D23368


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

llvm-svn: 279123
2016-08-18 19:43:50 +00:00
Hans Wennborg
5050823a40 Merging r278562:
------------------------------------------------------------------------
r278562 | efriedma | 2016-08-12 13:39:51 -0700 (Fri, 12 Aug 2016) | 7 lines

[AArch64LoadStoreOptimizer] Check aliasing correctly when creating paired loads/stores.

The existing code accidentally skipped the aliasing check in edge cases.

Differential revision: https://reviews.llvm.org/D23372


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

llvm-svn: 279107
2016-08-18 18:14:41 +00:00
Hans Wennborg
96ebd6939d Merging r278988:
------------------------------------------------------------------------
r278988 | cbieneman | 2016-08-17 14:54:30 -0700 (Wed, 17 Aug 2016) | 14 lines

[Darwin] Stop linking libclang_rt.eprintf.a

Summary:
The eprintf library was added before the general OS X builtins library existed as a place to store one builtin function. Since we have for several years had an actual mandated builtin library for OS X > 10.5, we should just merge eprintf into the main library.

This change will resolve PR28855.

As a follow up I'll also patch compiler-rt to not generate the eprintf library anymore.

Reviewers: ddunbar, bob.wilson

Subscribers: cfe-commits

Differential Revision: https://reviews.llvm.org/D23531
------------------------------------------------------------------------

llvm-svn: 279103
2016-08-18 17:56:48 +00:00
Hans Wennborg
4d137ce709 Merging r278999:
------------------------------------------------------------------------
r278999 | hans | 2016-08-17 15:50:18 -0700 (Wed, 17 Aug 2016) | 3 lines

SCEV: Don't assert about non-SCEV-able value in isSCEVExprNeverPoison() (PR28932)

Differential Revision: https://reviews.llvm.org/D23594
------------------------------------------------------------------------

llvm-svn: 279093
2016-08-18 17:34:01 +00:00
Anastasia Stulova
0462821695 Removed extra space in OpenCL release notes
llvm-svn: 279083
2016-08-18 16:17:20 +00:00
Rui Ueyama
648212d30e Update release notes.
llvm-svn: 279018
2016-08-18 00:43:02 +00:00
Hans Wennborg
1256b9e01d Merging r279008:
------------------------------------------------------------------------
r279008 | marshall | 2016-08-17 16:24:02 -0700 (Wed, 17 Aug 2016) | 1 line

make the associative containers do the right thing for propogate_on_container_assignment. Fixes bug #29001. Tests are only for <map> right now - more complete tests will come when we revamp our allocator testing structure.
------------------------------------------------------------------------

llvm-svn: 279017
2016-08-18 00:23:33 +00:00
Hans Wennborg
bd5a61f962 Merging r278904:
------------------------------------------------------------------------
r278904 | marshall | 2016-08-16 22:58:40 -0700 (Tue, 16 Aug 2016) | 1 line

Support allocators with explicit conversion constructors. Fixes bug #29000
------------------------------------------------------------------------

llvm-svn: 279015
2016-08-18 00:20:59 +00:00
Hans Wennborg
c400b144b3 Merging r278949: (excluding the clang-include-fixer.el part)
------------------------------------------------------------------------
r278949 | eugenezelenko | 2016-08-17 10:27:56 -0700 (Wed, 17 Aug 2016) | 4 lines

[Include-fixer] Install executables and support scripts

Differential revision: https://reviews.llvm.org/D23045

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

llvm-svn: 278997
2016-08-17 22:21:18 +00:00
Hans Wennborg
a1f6fd32a3 Merging r278938:
------------------------------------------------------------------------
r278938 | mcrosier | 2016-08-17 08:54:39 -0700 (Wed, 17 Aug 2016) | 5 lines

Revert "Reassociate: Reprocess RedoInsts after each inst".

This reverts commit r258830, which introduced a bug described in PR28367.

PR28367
------------------------------------------------------------------------

llvm-svn: 278993
2016-08-17 22:13:00 +00:00
Hans Wennborg
4c27d26902 Merging r278900:
------------------------------------------------------------------------
r278900 | cycheng | 2016-08-16 20:17:44 -0700 (Tue, 16 Aug 2016) | 12 lines

[ppc64] Don't apply sibling call optimization if callee has any byval arg

This is a quick work around, because in some cases, e.g. caller's stack
size > callee's stack size, we are still able to apply sibling call
optimization even callee has any byval arg.

This patch fix: https://llvm.org/bugs/show_bug.cgi?id=28328

Reviewers: hfinkel kbarton nemanjai amehsan
Subscribers: hans, tjablin

https://reviews.llvm.org/D23441
------------------------------------------------------------------------

llvm-svn: 278990
2016-08-17 22:03:07 +00:00
Hans Wennborg
178a30f3d0 Merging r278786:
------------------------------------------------------------------------
r278786 | jamesm | 2016-08-16 02:45:36 -0700 (Tue, 16 Aug 2016) | 4 lines

Left shifts of negative values are defined if -fwrapv is set

This means we shouldn't emit ubsan detection code or warn.
Fixes PR25552.
------------------------------------------------------------------------

llvm-svn: 278989
2016-08-17 21:58:11 +00:00
Hans Wennborg
d8f682e8b8 Merging r277852:
------------------------------------------------------------------------
r277852 | dblaikie | 2016-08-05 12:03:01 -0700 (Fri, 05 Aug 2016) | 7 lines

PR26423: Assert on valid use of using declaration of a function with an undeduced auto return type

For now just disregard the using declaration in this case. Suboptimal,
but wiring up the ability to have declarations of functions that are
separate from their definition (we currently only do that for member
functions) and have differing return types (we don't have any support
for that) is more work than seems reasonable to at least fix this crash.
------------------------------------------------------------------------

llvm-svn: 278877
2016-08-17 00:25:07 +00:00
Hans Wennborg
6993bdaeac ReleaseNotes: mention new /imsvc flag
llvm-svn: 278876
2016-08-17 00:19:41 +00:00
Hans Wennborg
743618894f Merging r278841:
------------------------------------------------------------------------
r278841 | haicheng | 2016-08-16 13:06:25 -0700 (Tue, 16 Aug 2016) | 3 lines

[BranchFolding] Change a test case of r278575.

Rename the operands to make the test less brittle.
------------------------------------------------------------------------

llvm-svn: 278874
2016-08-17 00:15:15 +00:00
Hans Wennborg
de5499dc23 Merging r278571:
------------------------------------------------------------------------
r278571 | rnk | 2016-08-12 15:23:04 -0700 (Fri, 12 Aug 2016) | 6 lines

[Inliner] Don't treat inalloca allocas as static

They aren't static, and moving them to the entry block across something
else will only result in tears.

Root cause of http://crbug.com/636558.
------------------------------------------------------------------------

llvm-svn: 278831
2016-08-16 18:06:44 +00:00
Hans Wennborg
fcfc07ab52 Merging r278575 (with changes to the test):
------------------------------------------------------------------------
r278575 | haicheng | 2016-08-12 16:13:38 -0700 (Fri, 12 Aug 2016) | 6 lines

Reapply [BranchFolding] Restrict tail merging loop blocks after MBP

Fixed a bug in the test case.

To fix PR28104, this patch restricts tail merging to blocks that belong to the
same loop after MBP.
------------------------------------------------------------------------

I had to adjust the test as it wasn't passing on the branch, presumably
due to different machine block placement.

llvm-svn: 278827
2016-08-16 17:51:12 +00:00
Hans Wennborg
ef5d170f9a Merging r278763:
------------------------------------------------------------------------
r278763 | rsmith | 2016-08-15 17:13:47 -0700 (Mon, 15 Aug 2016) | 5 lines

PR28978: If we need overload resolution for the move constructor of an
anonymous union member of a class, we need overload resolution for the move
constructor of the class itself too; we can't rely on Sema to do the right
thing for us for anonymous union types.

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

llvm-svn: 278817
2016-08-16 16:19:18 +00:00
Faisal Vali
6bfcc10dca [Branch 3.9] Remove any traces of partial constexpr lambda implementation
This patch essentially reverses all the changes from the following commit: https://reviews.llvm.org/rL264513 for branch 3.9.

Requested by Richard and Approved by Hans here: https://reviews.llvm.org/D23485

llvm-svn: 278771
2016-08-16 02:11:53 +00:00
Hans Wennborg
723dfb5dc9 Merging r278558:
------------------------------------------------------------------------
r278558 | cbieneman | 2016-08-12 13:11:03 -0700 (Fri, 12 Aug 2016) | 3 lines

Remove autoconf references from LICENSE.TXT

Since we don't actually have the autoconf subdirectories anymore, we don't need this reference here.
------------------------------------------------------------------------

llvm-svn: 278688
2016-08-15 17:48:44 +00:00
Hans Wennborg
cb0913a242 Merging r278584:
------------------------------------------------------------------------
r278584 | sanjoy | 2016-08-12 17:58:31 -0700 (Fri, 12 Aug 2016) | 15 lines

[IndVars] Ignore (s|z)exts that don't extend the induction variable

`IVVisitor::visitCast` used to have the invariant that if the
instruction it was passed was a sext or zext instruction, the result of
the instruction would be wider than the induction variable.  This is no
longer true after rL275037, so this change teaches `IndVarSimplify` s
implementation of `IVVisitor::visitCast` to work with the relaxed
invariant.

A corresponding change to SimplifyIndVar to preserve the said invariant
after rL275037 would also work, but given how `IVVisitor::visitCast` is
spelled (no indication of said invariant), I figured the current fix is
cleaner.

Fixes PR28935.
------------------------------------------------------------------------

llvm-svn: 278685
2016-08-15 17:29:29 +00:00
Hans Wennborg
c0c7d6c26d Merging r278454:
------------------------------------------------------------------------
r278454 | cbieneman | 2016-08-11 18:29:26 -0700 (Thu, 11 Aug 2016) | 3 lines

[CMake] If the compiler supports _Atomic include atomic.c in builtins libraries

This fixes a long-standing TODO by implementing a compiler check for supporting the _Atomic keyword. If the _Atomic keyword is supported by the compiler we should include it in the builtin library sources.
------------------------------------------------------------------------

llvm-svn: 278679
2016-08-15 16:40:19 +00:00
Hans Wennborg
d8147e7bde Merging r277783 and r278156 to unbreak Clang on the branch after r278674
------------------------------------------------------------------------
r277783 | timshen | 2016-08-04 16:03:44 -0700 (Thu, 04 Aug 2016) | 9 lines

[ADT] Migrate DepthFirstIterator to use NodeRef

Summary: The corresponding LLVM change is D23146.

Reviewers: dblaikie, chandlerc

Subscribers: cfe-commits

Differential Revision: https://reviews.llvm.org/D23147
------------------------------------------------------------------------

------------------------------------------------------------------------
r278156 | timshen | 2016-08-09 13:22:55 -0700 (Tue, 09 Aug 2016) | 16 lines

[ADT] Change iterator_adaptor_base's default template arguments to forward more underlying typedefs

Summary:
The corresponding LLVM change: D23217.

LazyVector::iterator breaks, because int isn't an iterator type.
Since iterator_adaptor_base shouldn't be blamed to break at the call to
iterator_traits<int>::xxx, I'd rather "fix" LazyVector::iterator.

The perfect solution is to model "relative pointer", but it's beyond the goal of this patch.

Reviewers: chandlerc, bkramer

Subscribers: cfe-commits

Differential Revision: https://reviews.llvm.org/D23218
------------------------------------------------------------------------

llvm-svn: 278678
2016-08-15 16:32:18 +00:00
Anastasia Stulova
2e8edcaa80 OpenCL release notes
llvm-svn: 278677
2016-08-15 16:28:10 +00:00
Hans Wennborg
0777ee9f6f Merging r278579:
------------------------------------------------------------------------
r278579 | mehdi_amini | 2016-08-12 17:02:33 -0700 (Fri, 12 Aug 2016) | 3 lines

Fix ASAN failures in the demangler

These were found fuzzing with ASAN.
------------------------------------------------------------------------

llvm-svn: 278675
2016-08-15 16:10:06 +00:00
Hans Wennborg
e7fedbcdbb Merging r278573 (and r277399, r278157, r278569):
------------------------------------------------------------------------
r278573 | timshen | 2016-08-12 15:47:13 -0700 (Fri, 12 Aug 2016) | 8 lines

[LoopVectorize] Detect loops in the innermost loop before creating InnerLoopVectorizer

InnerLoopVectorizer shouldn't handle a loop with cycles inside the loop
body, even if that cycle isn't a natural loop.

Fixes PR28541.

Differential Revision: https://reviews.llvm.org/D22952
------------------------------------------------------------------------

------------------------------------------------------------------------
r277399 | timshen | 2016-08-01 15:32:20 -0700 (Mon, 01 Aug 2016) | 9 lines

[ADT] NFC: Generalize GraphTraits requirement of "NodeType *" in interfaces to "NodeRef", and migrate SCCIterator.h to use NodeRef

Summary: By generalize the interface, users are able to inject more flexible Node token into the algorithm, for example, a pair of vector<Node>* and index integer. Currently I only migrated SCCIterator to use NodeRef, but more is coming. It's a NFC.

Reviewers: dblaikie, chandlerc

Subscribers: llvm-commits

Differential Revision: https://reviews.llvm.org/D22937
------------------------------------------------------------------------

------------------------------------------------------------------------
r278157 | timshen | 2016-08-09 13:23:13 -0700 (Tue, 09 Aug 2016) | 7 lines

[ADT] Change iterator_adaptor_base's default template arguments to forward more underlying typedefs

Reviewers: chandlerc

Subscribers: llvm-commits

Differential Revision: https://reviews.llvm.org/D23217
------------------------------------------------------------------------

------------------------------------------------------------------------
r278569 | timshen | 2016-08-12 15:03:28 -0700 (Fri, 12 Aug 2016) | 3 lines

[ADT] Add filter_iterator for filtering elements

Differential Revision: https://reviews.llvm.org/D22951
------------------------------------------------------------------------

llvm-svn: 278674
2016-08-15 16:06:31 +00:00
Ed Schouten
fd4ed8eaab Merge r278393 and r278395.
LLVM/Clang 3.8 is directly usable as a cross compiler for CloudABI/i686.
In 3.9rc1 there are a couple of regressions in the driver that cause it
to be less usable:

- PIE was enabled unconditionally, even though it's only available for
  x86-64 and aarch64.
- Some inline assembly fails to build, due to a shortage of registers,
  as frame pointers are not omitted.

Both these changes are fairly low risk (read: they don't affect other
targets), so go ahead and merge them into 3.9, so we can use an
unmodified compiler on all architectures.

llvm-svn: 278605
2016-08-13 20:43:56 +00:00
Hans Wennborg
d26597b492 Merging r277997, r277999 and r278001:
------------------------------------------------------------------------
r277997 | labath | 2016-08-08 05:26:57 -0700 (Mon, 08 Aug 2016) | 3 lines

Remove _isatty from Android.h

it is just #defined to isatty anyway, which lldb already knows how to use.
------------------------------------------------------------------------

------------------------------------------------------------------------
r277999 | labath | 2016-08-08 05:40:11 -0700 (Mon, 08 Aug 2016) | 3 lines

Remove SYS_tgkill from Android.h

instead, use __NR_tgkill directly, which seems to be the preferred form in the codebase anyway.
------------------------------------------------------------------------

------------------------------------------------------------------------
r278001 | labath | 2016-08-08 06:13:03 -0700 (Mon, 08 Aug 2016) | 5 lines

Clean up linux/Ptrace.h

This removes references to PT_XXX macros from the file, as they were not used anyway. It also
changes the macro used to check for the definition of __ptrace_request, as there are other C
libraries which do not define this type.
------------------------------------------------------------------------

llvm-svn: 278540
2016-08-12 18:10:54 +00:00
Hans Wennborg
b6c05cc725 Merging r278357:
------------------------------------------------------------------------
r278357 | compnerd | 2016-08-11 09:58:12 -0700 (Thu, 11 Aug 2016) | 6 lines

test: relax the FS test a slight bit to be more reliable

Some filesystems track atime always.  This relaxes the test to accept either a
filesystem which does not accurately track atime or does track the atime
accurately.  This allows the test to pass on filesystems mounted with
`strictatime` on Linux or on macOS.
------------------------------------------------------------------------

llvm-svn: 278538
2016-08-12 17:59:24 +00:00
George Burgess IV
a972a1c52e Merging r278471:
------------------------------------------------------------------------
r278471 | gbiv | 2016-08-11 21:12:31 -0700 (Thu, 11 Aug 2016) | 11 lines

[Sema] Fix a crash on variadic enable_if functions.

Currently, when trying to evaluate an enable_if condition, we try to
evaluate all arguments a user passes to a function. Given that we can't
use variadic arguments from said condition anyway, not converting them
is a reasonable thing to do. So, this patch makes us ignore any varargs
when attempting to check an enable_if condition.

We'd crash because, in order to convert an argument, we need its
ParmVarDecl. Variadic arguments don't have ParmVarDecls.

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

llvm-svn: 278479
2016-08-12 05:11:46 +00:00
Richard Trieu
b5d131ee9f Update release notes for new warnings.
Document -Wcomma, -Wfloat-zero-conversion, and -Wfloat-overflow-conversion

llvm-svn: 278441
2016-08-12 00:00:21 +00:00
Hans Wennborg
d5f90e1a8f Merging r277093:
------------------------------------------------------------------------
r277093 | eugenezelenko | 2016-07-28 17:46:13 -0700 (Thu, 28 Jul 2016) | 4 lines

Add LLVM_ENABLE_LLD option to use LLD as C/C++ linker.

Differential revision: https://reviews.llvm.org/D22896

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

llvm-svn: 278428
2016-08-11 21:55:38 +00:00
Hans Wennborg
6d88d11e5c Remove test/asan/TestCases/Darwin/dead-strip.c
The test is gated on OS version, but actually depends on what SDK
is used, see PR28743. Deleting to unblock 3.9.

llvm-svn: 278427
2016-08-11 21:50:18 +00:00
Hans Wennborg
43d7c78a86 Merging r278282:
------------------------------------------------------------------------
r278282 | marshall | 2016-08-10 13:04:46 -0700 (Wed, 10 Aug 2016) | 1 line

std:: quailfy the calls for cend/crend/cbegin/cend. Fixes bug 28927.
------------------------------------------------------------------------

llvm-svn: 278426
2016-08-11 21:48:37 +00:00
Hans Wennborg
eb6343e251 Merging r278387:
------------------------------------------------------------------------
r278387 | marshall | 2016-08-11 11:46:24 -0700 (Thu, 11 Aug 2016) | 1 line

Remove test for the sign of a NaN - doesn't work on MIPS, not strictly legal. Fixes bug 28936
------------------------------------------------------------------------

llvm-svn: 278425
2016-08-11 21:47:28 +00:00
Hans Wennborg
6ea7621321 Merging r278413:
------------------------------------------------------------------------
r278413 | gberry | 2016-08-11 14:05:17 -0700 (Thu, 11 Aug 2016) | 17 lines

[SCEV] Update interface to handle SCEVExpander insert point motion.

Summary:
This is an extension of the fix in r271424.  That fix dealt with builder
insert points being moved by SCEV expansion, but only for the lifetime
of the expand call.  This change modifies the interface so that LSR can
safely call expand multiple times at the same insert point and do the
right thing if one of the expansions decides to move the original insert
point.

This is a fix for PR28719.

Reviewers: sanjoy

Subscribers: llvm-commits, mcrosier, mzolotukhin

Differential Revision: https://reviews.llvm.org/D23342
------------------------------------------------------------------------

llvm-svn: 278424
2016-08-11 21:43:23 +00:00
Hans Wennborg
3d88a187c6 Merging r278370:
------------------------------------------------------------------------
r278370 | mkuper | 2016-08-11 10:38:33 -0700 (Thu, 11 Aug 2016) | 7 lines

Make TwoAddressInstructionPass::rescheduleMIBelowKill subreg-aware

This fixes PR28824.

Differential Revision: https://reviews.llvm.org/D23220


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

llvm-svn: 278422
2016-08-11 21:39:47 +00:00
Hans Wennborg
2a531d02a4 Merging r277522:
------------------------------------------------------------------------
r277522 | dcoughlin | 2016-08-02 14:07:23 -0700 (Tue, 02 Aug 2016) | 8 lines

[CFG] Fix crash finding destructor of lifetime-extended temporary.

Fix a crash under -Wthread-safety when finding the destructor for a
lifetime-extending reference.

A patch by Nandor Licker!

Differential Revision: https://reviews.llvm.org/D22419
------------------------------------------------------------------------

llvm-svn: 278376
2016-08-11 18:16:47 +00:00
Hans Wennborg
3e892bf1f5 Merging r276900:
------------------------------------------------------------------------
r276900 | epilk | 2016-07-27 11:25:10 -0700 (Wed, 27 Jul 2016) | 5 lines

[Sema] Teach getCurrentThisType to reconize lambda in in-class initializer

Fixes PR27994, a crash on valid.

Differential revision: https://reviews.llvm.org/D21145
------------------------------------------------------------------------

llvm-svn: 278374
2016-08-11 18:13:48 +00:00
Hans Wennborg
6cee7f203c Merging r276712:
------------------------------------------------------------------------
r276712 | eugenis | 2016-07-25 17:05:14 -0700 (Mon, 25 Jul 2016) | 3 lines

[safestack] Fix stack guard live range.

Stack guard slot is live throughout the function.
------------------------------------------------------------------------

llvm-svn: 278283
2016-08-10 20:06:22 +00:00
Hans Wennborg
3d9fd91926 Merging r276676:
------------------------------------------------------------------------
r276676 | eugenis | 2016-07-25 12:25:40 -0700 (Mon, 25 Jul 2016) | 1 line

Fix invalid iterator use in safestack coloring.
------------------------------------------------------------------------

llvm-svn: 278281
2016-08-10 20:02:49 +00:00
Hans Wennborg
52360ab08e Merging r278234 and r278235:
------------------------------------------------------------------------
r278234 | joey | 2016-08-10 08:57:02 -0700 (Wed, 10 Aug 2016) | 5 lines

[OpenCL] Change block descriptor address space to constant.

The block descriptor is a GlobalVariable in the LLVM IR, so it shouldn't be
in the private address space.

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

------------------------------------------------------------------------
r278235 | joey | 2016-08-10 09:04:14 -0700 (Wed, 10 Aug 2016) | 2 lines

[OpenCL] Fix typo in test that I accidentally introduced in my previous commit.

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

llvm-svn: 278248
2016-08-10 17:34:17 +00:00
Hans Wennborg
3e34550899 Merging r276051 and r276823:
------------------------------------------------------------------------
r276051 | arsenm | 2016-07-19 16:16:53 -0700 (Tue, 19 Jul 2016) | 8 lines

AMDGPU: Change fdiv lowering based on !fpmath metadata

If 2.5 ulp is acceptable, denormals are not required, and
isn't a reciprocal which will already be handled, replace
with a faster fdiv.

Simplify the lowering tests by using per function
subtarget features.
------------------------------------------------------------------------

------------------------------------------------------------------------
r276823 | arsenm | 2016-07-26 16:25:44 -0700 (Tue, 26 Jul 2016) | 4 lines

AMDGPU: Use rcp for fdiv 1, x with fpmath metadata

Using rcp should be OK for safe math usually, so this
should not be replacing the original fdiv.
------------------------------------------------------------------------

llvm-svn: 278243
2016-08-10 16:45:40 +00:00
Hans Wennborg
1969971eef Merging r278139:
------------------------------------------------------------------------
r278139 | rnk | 2016-08-09 10:23:56 -0700 (Tue, 09 Aug 2016) | 6 lines

[clang-cl] Make -gline-tables-only imply -gcodeview

It's surprising that you have to pass /Z7 in addition to -gcodeview to
get debug info. The sanitizer runtime, for example, expects that if the
compiler supports the -gline-tables-only flag, then it will emit debug
info.
------------------------------------------------------------------------

llvm-svn: 278240
2016-08-10 16:36:05 +00:00
Hans Wennborg
8e1170431d Merging r278002:
------------------------------------------------------------------------
r278002 | sbaranga | 2016-08-08 06:13:57 -0700 (Mon, 08 Aug 2016) | 18 lines

[AArch64] PR28877: Don't assume we're running after legalization when creating vcvtfp2fxs

Summary:
The DAG combine transformation that was generating the
aarch64_neon_vcvtfp2fxs node was assuming that all
inputs where legal and wasn't accounting that the input
could be a v4f64 if we're trying to do the transformation
before legalization. We now bail out in this case.

All illegal types besides v4f64 were already rejected.

Fixes https://llvm.org/bugs/show_bug.cgi?id=28877.

Reviewers: jmolloy

Subscribers: aemerson, rengolin, llvm-commits

Differential Revision: https://reviews.llvm.org/D23261
------------------------------------------------------------------------

llvm-svn: 278239
2016-08-10 16:27:26 +00:00
Devin Coughlin
d981f97664 [docs] Update 3.9 release notes for the static analyzer.
llvm-svn: 278175
2016-08-09 23:01:43 +00:00
Hans Wennborg
eef377133d ReleaseNotes: mention more mov-to-push
llvm-svn: 278167
2016-08-09 21:26:16 +00:00
Hans Wennborg
11b7a9b72f Merging r278133:
------------------------------------------------------------------------
r278133 | hans | 2016-08-09 09:46:02 -0700 (Tue, 09 Aug 2016) | 3 lines

test-release.sh: Drop autoconf support

The autoconf build was deleted some time ago.
------------------------------------------------------------------------

llvm-svn: 278134
2016-08-09 16:46:35 +00:00
Hans Wennborg
fd00608136 Merging r278086:
------------------------------------------------------------------------
r278086 | matze | 2016-08-08 18:47:26 -0700 (Mon, 08 Aug 2016) | 6 lines

X86InstrInfo: Update liveness in classifyLea()

We need to update liveness information when we create COPYs in
classifyLea().

This fixes http://llvm.org/28301
------------------------------------------------------------------------

llvm-svn: 278128
2016-08-09 15:48:01 +00:00
Hans Wennborg
4ef81a7fdb Merging r278036:
------------------------------------------------------------------------
r278036 | dim | 2016-08-08 11:34:05 -0700 (Mon, 08 Aug 2016) | 18 lines

Fix linking of omp_foreign_thread_team_reuse test on FreeBSD

Summary:
On FreeBSD, linking the misc_bugs/omp_foreign_thread_team_reuse.c test
case fails with:

   /usr/local/bin/ld: /tmp/omp_foreign_thread_team_reuse-c5e71b.o: undefined reference to symbol 'pthread_create@@FBSD_1.0'

This is because the program is linked without `-lpthread`.  Since the
%libomp-compile-and-run macro does not allow that option to be added to
the compile command line, split it up and add the required `-lpthread`
between %libomp-compile and %libomp-run.

Reviewers: jlpeyton, hfinkel, Hahnfeld

Subscribers: Hahnfeld, emaste, openmp-commits

Differential Revision: https://reviews.llvm.org/D23084
------------------------------------------------------------------------

llvm-svn: 278059
2016-08-08 22:11:41 +00:00
Hans Wennborg
4e15deb8e0 Merging r277426:
------------------------------------------------------------------------
r277426 | nitesh.jain | 2016-08-02 00:18:07 -0700 (Tue, 02 Aug 2016) | 7 lines

[LLVM][MIPS] Add (D)SUBU, (D)ADDU, LUI instructions emulation . Fix emulation for (D)ADDIU, SD/SW and LW/LD instructions

Reviewers: clayborg, jaydeep, bhushan

Subscribers: mohit.bhakkad, slthakur, sdardis, lldb-commits

Differential Revision: https://reviews.llvm.org/D20357
------------------------------------------------------------------------

llvm-svn: 278047
2016-08-08 20:20:15 +00:00
Hans Wennborg
7ae7c29bfb Merging r277343:
------------------------------------------------------------------------
r277343 | nitesh.jain | 2016-08-01 06:45:51 -0700 (Mon, 01 Aug 2016) | 7 lines

[LLVM][MIPS] Fix FPU Size Based on Dynamic FR.

Reviewers: jingham, clayborg

Subscribers: jaydeep, bhushan, mohit.bhakkad, slthakur, lldb-commits, emaste, nemanjai, labath, sdardis

Differential Revision: https://reviews.llvm.org/D20357
------------------------------------------------------------------------

llvm-svn: 278046
2016-08-08 20:19:34 +00:00
Kai Nacke
0316f30e18 Add LDC compiler to list of external OS projects using LLVM 3.9
llvm-svn: 278045
2016-08-08 20:17:36 +00:00
Hans Wennborg
ae8bfb5779 Merging r278030 with version number adjusted:
------------------------------------------------------------------------
r278030 | eugenezelenko | 2016-08-08 10:59:02 -0700 (Mon, 08 Aug 2016) | 4 lines

CMakeLists.txt cleanups: synchronize version with rest of LLVM, consistent spacing.

Differential revision: https://reviews.llvm.org/D23092

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

llvm-svn: 278044
2016-08-08 20:06:27 +00:00
Hans Wennborg
dd08defbe8 Merging r278029 with version number adjusted:
------------------------------------------------------------------------
r278029 | eugenezelenko | 2016-08-08 10:56:28 -0700 (Mon, 08 Aug 2016) | 4 lines

CMakeLists.txt cleanups: synchronize version and CMake minimum required version with rest of LLVM, consistent spacing.

Differential revision: https://reviews.llvm.org/D23094

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

llvm-svn: 278043
2016-08-08 20:04:18 +00:00
Hans Wennborg
f37ca4f446 Merging r277868:
------------------------------------------------------------------------
r277868 | compnerd | 2016-08-05 14:35:28 -0700 (Fri, 05 Aug 2016) | 4 lines

unwind: disable executable stacks

Similar to compiler-rt, ensure that we disable executable stacks for the custom
assembly.
------------------------------------------------------------------------

llvm-svn: 278023
2016-08-08 17:18:56 +00:00
Hans Wennborg
404f2eb91d Merging r277796, r277797, r277866, r277889 and r277900:
------------------------------------------------------------------------
r277796 | rtrieu | 2016-08-04 19:39:30 -0700 (Thu, 04 Aug 2016) | 6 lines

Allow -1 to assign max value to unsigned bitfields.

Silence the -Wbitfield-constant-conversion warning for when -1 or other
negative values are assigned to unsigned bitfields, provided that the bitfield
is wider than the minimum number of bits needed to encode the negative value.

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

------------------------------------------------------------------------
r277797 | rtrieu | 2016-08-04 20:16:36 -0700 (Thu, 04 Aug 2016) | 7 lines

Fix crash in template type diffing.

When the type being diffed is a type alias, and the orginal type is not a
templated type, then there will be no unsugared TemplateSpecializationType.
When this happens, exit early from the constructor.  Also add assertions to
the other iterator accessor to prevent the iterator from being used.

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

------------------------------------------------------------------------
r277866 | rtrieu | 2016-08-05 14:02:34 -0700 (Fri, 05 Aug 2016) | 12 lines

Fix false positive in -Wunsequenced and templates.

For builtin logical operators, there is a well-defined ordering of argument
evaluation.  For overloaded operator of the same type, there is no argument
evaluation order, similar to other function calls.  When both are present,
uninstantiated templates with an operator&& is treated as an unresolved
function call.  Unresolved function calls are treated as normal function calls,
and may result in false positives when the builtin logical operator is used.
Have the unsequenced checker ignore dependent expressions to avoid this
false positive.  The check also happens in template instantiations to catch
when the overloaded operator is used.

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

------------------------------------------------------------------------
r277889 | rtrieu | 2016-08-05 16:24:47 -0700 (Fri, 05 Aug 2016) | 9 lines

Fix two false positives in -Wreturn-stack-address

If the return type is a pointer and the function returns the reference to a
pointer, don't warn since only the value is returned, not the reference.

If a reference function parameter appears in the reference chain, don't warn
since binding happens at the caller scope, so addresses returned are not
to local stack.  This includes default arguments as well.

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

------------------------------------------------------------------------
r277900 | rtrieu | 2016-08-05 18:44:06 -0700 (Fri, 05 Aug 2016) | 2 lines

Fix typos from r277797 and unused variable from r277889.

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

llvm-svn: 278020
2016-08-08 16:37:00 +00:00
Hans Wennborg
4192c1db5d Merging r277743:
------------------------------------------------------------------------
r277743 | bader | 2016-08-04 11:06:27 -0700 (Thu, 04 Aug 2016) | 12 lines

[OpenCL] Added underscores to the names of 'to_addr' OpenCL built-ins.

Summary:
In order to re-define OpenCL built-in functions
'to_{private,local,global}' in OpenCL run-time library LLVM names must
be different from the clang built-in function names.

Reviewers: yaxunl, Anastasia

Subscribers: cfe-commits

Differential Revision: https://reviews.llvm.org/D23120
------------------------------------------------------------------------

llvm-svn: 278019
2016-08-08 16:32:10 +00:00
Vasileios Kalintiris
cb9c50e25c Add release note for the MIPS target.
llvm-svn: 277998
2016-08-08 12:28:57 +00:00
Simon Atanasyan
59f49e1610 Update release notes for MIPS target
llvm-svn: 277807
2016-08-05 11:05:23 +00:00
Nicolai Haehnle
a2296f018d Add release note for AMDGPU target.
llvm-svn: 277803
2016-08-05 08:31:59 +00:00
Hans Wennborg
48466efc6f Merging r277691, r277693, and r277773:
------------------------------------------------------------------------
r277691 | majnemer | 2016-08-03 21:24:02 -0700 (Wed, 03 Aug 2016) | 4 lines

Reinstate "[CloneFunction] Don't remove side effecting calls"

This reinstates r277611 + r277614 and reverts r277642.  A cast_or_null
should have been a dyn_cast_or_null.
------------------------------------------------------------------------

------------------------------------------------------------------------
r277693 | majnemer | 2016-08-03 21:47:18 -0700 (Wed, 03 Aug 2016) | 1 line

Forgot the dyn_cast_or_null intended for r277691.
------------------------------------------------------------------------

------------------------------------------------------------------------
r277773 | majnemer | 2016-08-04 14:28:59 -0700 (Thu, 04 Aug 2016) | 6 lines

[CloneFunction] Add a testcase for r277691/r277693

PR28848 had a very nice reduction of the underlying cause of the bug.
Our ValueMap had, in an entry for an Instruction, a ConstantInt.

This is not at all unexpected but should be handled properly.
------------------------------------------------------------------------

llvm-svn: 277781
2016-08-04 22:44:36 +00:00
Devin Coughlin
8a5dbfe87e [docs] Add 3.9 release notes for the static analyzer.
llvm-svn: 277772
2016-08-04 21:27:47 +00:00
Nico Weber
ff00bfa19b Add a note about clang-cl pch support to the 3.9 release notes.
llvm-svn: 277751
2016-08-04 18:55:57 +00:00
Nico Weber
94f84eb3c0 grammar fix
llvm-svn: 277750
2016-08-04 18:52:17 +00:00
George Burgess IV
cf7397c3e9 Add release note for MemorySSA.
llvm-svn: 277739
2016-08-04 17:51:18 +00:00
Hans Wennborg
07101cec4f Merging r277625:
------------------------------------------------------------------------
r277625 | dexonsmith | 2016-08-03 11:19:43 -0700 (Wed, 03 Aug 2016) | 42 lines

IR: Drop uniquing when an MDNode Value operand is deleted

This is a fix for PR28697.

An MDNode can indirectly refer to a GlobalValue, through a
ConstantAsMetadata.  When the GlobalValue is deleted, the MDNode operand
is reset to `nullptr`.  If the node is uniqued, this can lead to a
hard-to-detect cache invalidation in a Metadata map that's shared across
an LLVMContext.

Consider:

 1. A map from Metadata* to `T` called RemappedMDs.
 2. A node that references a global variable, `!{i1* @GV}`.
 3. Insert `!{i1* @GV} -> SomeT` in the map.
 4. Delete `@GV`, leaving behind `!{null} -> SomeT`.

Looking up the generic and uninteresting `!{null}` gives you `SomeT`,
which is likely related to `@GV`.  Worse, `SomeT`'s lifetime may be tied
to the deleted `@GV`.

This occurs in practice in the shared ValueMap used since r266579 in the
IRMover.  Other code that handles more than one Module (with different
lifetimes) in the same LLVMContext could hit it too.

The fix here is a partial revert of r225223: in the rare case that an
MDNode operand is a ConstantAsMetadata (i.e., wrapping a node from the
Value hierarchy), drop uniquing if it gets replaced with `nullptr`.
This changes step #4 above to leave behind `distinct !{null} -> SomeT`,
which can't be confused with the generic `!{null}`.

In theory, this can cause some churn in the LLVMContext's MDNode
uniquing map when Values are being deleted.  However:

  - The number of GlobalValues referenced from uniqued MDNodes is
    expected to be quite small.  E.g., the debug info metadata schema
    only references GlobalValues from distinct nodes.

  - Other Constants have the lifetime of the LLVMContext, whose teardown
    is careful to drop references before deleting the constants.

As a result, I don't expect a compile time regression from this change.
------------------------------------------------------------------------

llvm-svn: 277639
2016-08-03 19:52:03 +00:00
Teresa Johnson
2546545e27 Add ThinLTO release note.
llvm-svn: 277628
2016-08-03 18:52:04 +00:00
Hans Wennborg
2f58a7236e Merging r277504:
------------------------------------------------------------------------
r277504 | nha | 2016-08-02 12:31:14 -0700 (Tue, 02 Aug 2016) | 20 lines

AMDGPU: Stay in WQM for non-intrinsic stores

Summary:
Two types of stores are possible in pixel shaders: stores to memory that are
explicitly requested at the API level, and stores that are an implementation
detail of register spilling or lowering of arrays.

For the first kind of store, we must ensure that helper pixels have no effect
and hence WQM must be disabled. The second kind of store must always be
executed, because the written value may be loaded again in a way that is
relevant for helper pixels as well -- and there are no externally visible
effects anyway.

This is a candidate for the 3.9 release branch.

Reviewers: arsenm, tstellarAMD, mareko

Subscribers: arsenm, kzhuravl, llvm-commits

Differential Revision: https://reviews.llvm.org/D22675
------------------------------------------------------------------------

llvm-svn: 277620
2016-08-03 18:13:01 +00:00
Hans Wennborg
3b3179a953 Merging r277500:
------------------------------------------------------------------------
r277500 | nha | 2016-08-02 12:17:37 -0700 (Tue, 02 Aug 2016) | 17 lines

AMDGPU: Track physical registers in SIWholeQuadMode

Summary:
There are cases where uniform branch conditions are computed in VGPRs, and
we didn't correctly mark those as WQM.

The stray change in basic-branch.ll is because invoking the LiveIntervals
analysis leads to the detection of a dead register that would otherwise not
be seen at -O0.

This is a candidate for the 3.9 branch, as it fixes a possible hang.

Reviewers: arsenm, tstellarAMD, mareko

Subscribers: arsenm, llvm-commits, kzhuravl

Differential Revision: https://reviews.llvm.org/D22673
------------------------------------------------------------------------

llvm-svn: 277619
2016-08-03 18:09:48 +00:00
Chris Bieneman
abc0f33858 [docs] Release Notes: autoconf has been removed
Fleshing out the note about the autoconf build system being removed and pointing to CMake documentation.

llvm-svn: 277617
2016-08-03 18:08:14 +00:00
Renato Golin
382e38731b [docs] release notes: self-host clang+lld on aarch64, stops
llvm-svn: 277606
2016-08-03 16:09:12 +00:00
Renato Golin
eba143a88d [code] Fix release notes typo
llvm-svn: 277593
2016-08-03 13:20:58 +00:00
Renato Golin
299cf98d0d [docs] Release notes 3.9.0: Adding ARM target changes
llvm-svn: 277591
2016-08-03 12:52:40 +00:00
Diana Picus
db31d91f01 Add a few things to the AArch64 release notes
llvm-svn: 277590
2016-08-03 12:00:27 +00:00
Benjamin Kramer
7d2d8faedf Expand the clang-include-fixer relnotes a bit.
llvm-svn: 277588
2016-08-03 11:59:03 +00:00
Renato Golin
55dcf07620 [docs] Release Notes 3.9.0: GCC ABI Tag
Adding a short explanation of the GCC ABI Tag discussion and issues, with
links and considerations.

llvm-svn: 277587
2016-08-03 11:07:48 +00:00
Hans Wennborg
3f72426b35 Merging r277095:
------------------------------------------------------------------------
r277095 | epilk | 2016-07-28 17:55:40 -0700 (Thu, 28 Jul 2016) | 5 lines

[Parser] Fix bug where delayed typo in conditional expression was corrected twice

Patch by David Tarditi!

Differential revision: https://reviews.llvm.org/D22930
------------------------------------------------------------------------

llvm-svn: 277525
2016-08-02 21:29:54 +00:00
Hans Wennborg
abaeaf8db4 Merging r277371:
------------------------------------------------------------------------
r277371 | mkuper | 2016-08-01 12:39:49 -0700 (Mon, 01 Aug 2016) | 9 lines

[DAGCombine] Make sext(setcc) combine respect getBooleanContents

We used to combine "sext(setcc x, y, cc) -> (select (setcc x, y, cc), -1, 0)"
Instead, we should combine to (select (setcc x, y, cc), T, 0) where the value
of T is 1 or -1, depending on the type of the setcc, and getBooleanContents()
for the type if it is not i1.

This fixes PR28504.

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

llvm-svn: 277509
2016-08-02 19:54:53 +00:00
Hans Wennborg
446b0b091d Merging r276648:
------------------------------------------------------------------------
r276648 | delena | 2016-07-25 09:51:00 -0700 (Mon, 25 Jul 2016) | 6 lines

AVX-512: Fixed [US]INT_TO_FP selection for i1 vectors.
It failed with assertion before this patch.

Differential Revision: https://reviews.llvm.org/D22735


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

llvm-svn: 277508
2016-08-02 19:41:53 +00:00
Diana Picus
7db37f42d9 Merging r277457
[clang-cl] Fix PCH tests to use x86_64 as target

These tests require x86-registered-target, but they don't force the target as
x86 on the command line, which means they will be run and they might fail when
building the x86 backend on another platform (such as AArch64).

Fixes https://llvm.org/bugs/show_bug.cgi?id=28797

llvm-svn: 277462
2016-08-02 14:34:15 +00:00
Renato Golin
ba9e903704 Merging r276701 and r277439
The saturation instructions appeared in v6T2 / DSP extensions, but they
were being accepted / generated on any, with the new introduction of the
saturation detection in the back-end. This commit restricts the usage to
v6T2 / DSP-enable only cores.

Fixes PR28607.

llvm-svn: 277440
2016-08-02 10:26:08 +00:00
Dimitry Andric
d10126296d Merging r277307:
------------------------------------------------------------------------
r277307 | dim | 2016-07-31 22:23:23 +0200 (Sun, 31 Jul 2016) | 23 lines

Add more gcc compatibility names to clang's cpuid.h

Summary:
Some cpuid bit defines are named slightly different from how gcc's
cpuid.h calls them.

Define a few more compatibility names to appease software built for gcc:

* `bit_PCLMUL`      alias of `bit_PCLMULQDQ`
* `bit_SSE4_1`      alias of `bit_SSE41`
* `bit_SSE4_2`      alias of `bit_SSE42`
* `bit_AES`         alias of `bit_AESNI`
* `bit_CMPXCHG8B`   alias of `bit_CX8`

While here, add the misssing 29th bit, `bit_F16C` (which is how gcc
calls this bit).

Reviewers: joerg, rsmith

Subscribers: bruno, cfe-commits

Differential Revision: https://reviews.llvm.org/D22010

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

llvm-svn: 277425
2016-08-02 06:46:09 +00:00
Dimitry Andric
b53a8594cb Merging r277300:
------------------------------------------------------------------------
r277300 | dim | 2016-07-31 22:16:59 +0200 (Sun, 31 Jul 2016) | 5 lines

Fix ASan alloca_constant_size.cc test on FreeBSD.

On FreeBSD <alloca.h> does not exist: alloca(3) is defined in <stdlib.h>
instead.

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

llvm-svn: 277424
2016-08-02 06:44:37 +00:00
Dimitry Andric
458d2d9d1b Merging r277297:
------------------------------------------------------------------------
r277297 | dim | 2016-07-31 21:27:46 +0200 (Sun, 31 Jul 2016) | 21 lines

XFAIL one sanitizer symbolizer test for FreeBSD

Summary:
Due to a QoI issuse in FreeBSD's libcxxrt-based demangler, one sanitizer
symbolizer test consistently appears to fail:

    Value of: DemangleSwiftAndCXX("foo")
      Actual: "float"
    Expected: "foo"

This is because libcxxrt's __cxa_demangle() incorrectly demangles the "foo"
identifier to "float".  It should return an error instead.

For now, XFAIL this particular test for FreeBSD, until we can fix libcxxrt
properly (which might take some time to coordinate with upstream).

Reviewers: rnk, zaks.anna, emaste

Subscribers: emaste, llvm-commits, kubabrecka

Differential Revision: https://reviews.llvm.org/D23001
------------------------------------------------------------------------

llvm-svn: 277423
2016-08-02 06:42:41 +00:00
Hans Wennborg
ec84a0a7d0 Merging r277097:
------------------------------------------------------------------------
r277097 | prazek | 2016-07-28 19:10:23 -0700 (Thu, 28 Jul 2016) | 5 lines

[clang-tidy] Fixes to modernize-use-emplace

Not everything is valid, but it should works for 99.8% cases

https://reviews.llvm.org/D22208
------------------------------------------------------------------------

llvm-svn: 277385
2016-08-01 20:35:50 +00:00
Hans Wennborg
3a27de34df Merging r277221:
------------------------------------------------------------------------
r277221 | echristo | 2016-07-29 15:11:11 -0700 (Fri, 29 Jul 2016) | 3 lines

Remove unused variable.

Fixes PR28761.
------------------------------------------------------------------------

llvm-svn: 277384
2016-08-01 20:33:18 +00:00
Hans Wennborg
5ca33d661a Merging r277114:
------------------------------------------------------------------------
r277114 | majnemer | 2016-07-28 22:39:21 -0700 (Thu, 28 Jul 2016) | 6 lines

[EarlyCSE] Correctly handle simplified, but live, instructions

Some instructions may have their uses replaced with a symbolic constant.
However, the instruction may still have side effects which percludes it
from being removed from the function.  EarlyCSE treated such an
instruction as if it were removed, resulting in PR28763.
------------------------------------------------------------------------

llvm-svn: 277382
2016-08-01 20:30:12 +00:00
Hans Wennborg
ad3f102610 Delete offloading-interoperability.c
The test is now failing on Windows. This is causing too much pain,
let's just drop it from the branch.

llvm-svn: 277207
2016-07-29 20:02:38 +00:00
Hans Wennborg
4d46ceafb1 Merging r276983, r277138 and r277141:
------------------------------------------------------------------------
r276983 | sfantao | 2016-07-28 07:56:19 -0700 (Thu, 28 Jul 2016) | 5 lines

[OpenMP] Fix link command pattern in offloading interoperability test.

It was causing a few bots to fail.
------------------------------------------------------------------------

------------------------------------------------------------------------
r277138 | d0k | 2016-07-29 06:07:09 -0700 (Fri, 29 Jul 2016) | 4 lines

Make test not fail on hosts where the default omp library is gomp.

This is the case on some linuxes, just force libomp so we get the
desired results.
------------------------------------------------------------------------

------------------------------------------------------------------------
r277141 | djasper | 2016-07-29 06:45:03 -0700 (Fri, 29 Jul 2016) | 1 line

Add missing '-no-canonical-prefixes' in test.
------------------------------------------------------------------------

llvm-svn: 277193
2016-07-29 18:36:36 +00:00
Hans Wennborg
739e31c3c0 Merging r277135:
------------------------------------------------------------------------
r277135 | niravd | 2016-07-29 04:49:32 -0700 (Fri, 29 Jul 2016) | 12 lines

Cleanup TransferDbgValues

[DAG] Check debug values for invalidation before transferring and mark
old debug values invalid when transferring to another SDValue.

This fixes PR28613.

Reviewers: jyknight, hans, dblaikie, echristo

Subscribers: yaron.keren, ismail, llvm-commits

Differential Revision: https://reviews.llvm.org/D22858
------------------------------------------------------------------------

llvm-svn: 277164
2016-07-29 16:18:18 +00:00
Hans Wennborg
b6794b6182 Merging r276980:
------------------------------------------------------------------------
r276980 | tstellar | 2016-07-28 07:30:43 -0700 (Thu, 28 Jul 2016) | 12 lines

AMDGPU/SI: Don't use reserved VGPRs for SGPR spilling

Summary:
We were using reserved VGPRs for SGPR spilling and this was causing
some programs with a workgroup size of 1024 to use more than 64
registers, which is illegal.

Reviewers: arsenm, mareko, nhaehnle

Subscribers: nhaehnle, arsenm, llvm-commits, kzhuravl

Differential Revision: https://reviews.llvm.org/D22032
------------------------------------------------------------------------

llvm-svn: 277084
2016-07-28 23:31:17 +00:00
Hans Wennborg
2ec706fe1f Merging r276435:
------------------------------------------------------------------------
r276435 | arsenm | 2016-07-22 10:01:21 -0700 (Fri, 22 Jul 2016) | 4 lines

AMDGPU: Fix i1 fp_to_int

R600's i1 fp_to_uint selected but was incorrect according to
what instcombine constant folds to.
------------------------------------------------------------------------

llvm-svn: 277082
2016-07-28 23:29:33 +00:00
Hans Wennborg
b5792d90e2 Merging r276119:
------------------------------------------------------------------------
r276119 | yaxunl | 2016-07-20 07:38:06 -0700 (Wed, 20 Jul 2016) | 3 lines

AMDGPU: Fix bug causing crash due to invalid opencl version metadata.

Differential Revision: https://reviews.llvm.org/D22526
------------------------------------------------------------------------

llvm-svn: 277079
2016-07-28 23:18:47 +00:00
Hans Wennborg
454777f777 Merging r276956:
------------------------------------------------------------------------
r276956 | majnemer | 2016-07-27 22:03:22 -0700 (Wed, 27 Jul 2016) | 6 lines

[CodeView] Don't crash on functions without subprograms

A function may have instructions annotated with debug info without
having a subprogram.

This fixes PR28747.
------------------------------------------------------------------------

llvm-svn: 277078
2016-07-28 23:15:39 +00:00
Hans Wennborg
6da6f0883c Merging r275869:
------------------------------------------------------------------------
r275869 | arsenm | 2016-07-18 11:34:53 -0700 (Mon, 18 Jul 2016) | 7 lines

AMDGPU: Remove dead check in AMDGPUPromoteAlloca

This is currently only called with GEP users. A direct
alloca would only happen with current typed pointers
for arrays which are a perverse case.

Also fix crashes on 0 x and 1 x arrays.
------------------------------------------------------------------------

llvm-svn: 277077
2016-07-28 23:12:00 +00:00
Hans Wennborg
09851a032b Merging r275868:
------------------------------------------------------------------------
r275868 | arsenm | 2016-07-18 11:34:48 -0700 (Mon, 18 Jul 2016) | 4 lines

AMDGPU: Remove dead code and redundant check

Non intrinsic calls aren't really handled, and this
IntrinsicInst dyn_cast checks for the function for us.
------------------------------------------------------------------------

llvm-svn: 277076
2016-07-28 23:10:33 +00:00
Hans Wennborg
8a688571fa Merging r276653:
------------------------------------------------------------------------
r276653 | ssrivastava | 2016-07-25 10:17:06 -0700 (Mon, 25 Jul 2016) | 11 lines

Support '#pragma once' in headers when using PCH
    
The '#pragma once' directive was erroneously ignored when encountered
in the header-file specified in generate-PCH-mode. This resulted in
compile-time errors in some cases with legal code, and also a misleading
warning being produced.

Patch by Warren Ristow!

Differential Revision: http://reviews.llvm.org/D19815

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

llvm-svn: 277075
2016-07-28 23:06:58 +00:00
Hans Wennborg
a2cc88dc6b Merging r276979:
------------------------------------------------------------------------
r276979 | sfantao | 2016-07-28 07:29:18 -0700 (Thu, 28 Jul 2016) | 12 lines

[OpenMP][CUDA] Do not forward OpenMP flags for CUDA device actions.

Summary:
This patch prevents OpenMP flags from being forwarded to CUDA device commands. That was causing the CUDA frontend to attempt to emit OpenMP code which is not supported.

This fixes the bug reported in https://llvm.org/bugs/show_bug.cgi?id=28723.

Reviewers: hfinkel, carlo.bertolli, arpith-jacob, kkwli0, tra, ABataev

Subscribers: caomhin, cfe-commits

Differential Revision: https://reviews.llvm.org/D22895
------------------------------------------------------------------------

llvm-svn: 277004
2016-07-28 17:11:02 +00:00
Hans Wennborg
60aa90df97 Mention of proper support for "__unaligned" type qualifier in 3.9 clang release notes
Patch by: Andrey Bokhanko <andreybokhanko@gmail.com>

Differential Revision: https://reviews.llvm.org/D22913

llvm-svn: 276994
2016-07-28 15:59:18 +00:00
Hans Wennborg
19e55fceac Update scripts/Xcode/build-llvm.py to use llvm/clang 3.9 branch
llvm-svn: 276992
2016-07-28 15:46:49 +00:00
Hans Wennborg
59c12c5799 Merging r276102:
------------------------------------------------------------------------
r276102 | rksimon | 2016-07-20 03:18:01 -0700 (Wed, 20 Jul 2016) | 11 lines

[X86][SSE] Reimplement SSE fp2si conversion intrinsics instead of using generic IR

D20859 and D20860 attempted to replace the SSE (V)CVTTPS2DQ and VCVTTPD2DQ truncating conversions with generic IR instead.

It turns out that the behaviour of these intrinsics is different enough from generic IR that this will cause problems, INF/NAN/out of range values are guaranteed to result in a 0x80000000 value - which plays havoc with constant folding which converts them to either zero or UNDEF. This is also an issue with the scalar implementations (which were already generic IR and what I was trying to match).

This patch changes both scalar and packed versions back to using x86-specific builtins.

It also deals with the other scalar conversion cases that are runtime rounding mode dependent and can have similar issues with constant folding.

Differential Revision: https://reviews.llvm.org/D22105
------------------------------------------------------------------------

llvm-svn: 276991
2016-07-28 15:41:25 +00:00
Hans Wennborg
30082a166c Merging r275981 and r276740:
------------------------------------------------------------------------
r275981 | rksimon | 2016-07-19 08:07:43 -0700 (Tue, 19 Jul 2016) | 13 lines

[X86][SSE] Reimplement SSE fp2si conversion intrinsics instead of using generic IR

D20859 and D20860 attempted to replace the SSE (V)CVTTPS2DQ and VCVTTPD2DQ truncating conversions with generic IR instead.

It turns out that the behaviour of these intrinsics is different enough from generic IR that this will cause problems, INF/NAN/out of range values are guaranteed to result in a 0x80000000 value - which plays havoc with constant folding which converts them to either zero or UNDEF. This is also an issue with the scalar implementations (which were already generic IR and what I was trying to match).

This patch changes both scalar and packed versions back to using x86-specific builtins.

It also deals with the other scalar conversion cases that are runtime rounding mode dependent and can have similar issues with constant folding.

A companion clang patch is at D22105

Differential Revision: https://reviews.llvm.org/D22106
------------------------------------------------------------------------

------------------------------------------------------------------------
r276740 | rksimon | 2016-07-26 03:41:28 -0700 (Tue, 26 Jul 2016) | 5 lines

[X86][SSE] Fixed issue with memory folding of (v)cvtsd2ss intrinsics

Fixed typo in the intrinsic definitions of (v)cvtsd2ss with memory folding.

This was only unearthed when rL276102 started using the intrinsic again.....
------------------------------------------------------------------------

llvm-svn: 276990
2016-07-28 15:38:57 +00:00
Hans Wennborg
b3c29bd13b Back-port r276209:
------------------------------------------------------------------------
r276209 | spatel | 2016-07-20 16:40:01 -0700 (Wed, 20 Jul 2016) | 4 lines

[InstSimplify][InstCombine] don't crash when folding vector selects of icmp

Differential Revision: https://reviews.llvm.org/D22602

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

llvm-svn: 276986
2016-07-28 15:25:03 +00:00
Hans Wennborg
c6d28602de Merging r276350:
------------------------------------------------------------------------
r276350 | epilk | 2016-07-21 15:31:40 -0700 (Thu, 21 Jul 2016) | 3 lines

[CodeGen] Fix a crash when constant folding switch statement

Differential revision: https://reviews.llvm.org/D22542
------------------------------------------------------------------------

llvm-svn: 276985
2016-07-28 15:18:14 +00:00
Hans Wennborg
00378ef53e docs: Remove 'if you are using a released version' warning
llvm-svn: 276893
2016-07-27 17:06:33 +00:00
Hans Wennborg
b699c21d07 Merging r276887:
------------------------------------------------------------------------
r276887 | hans | 2016-07-27 09:39:45 -0700 (Wed, 27 Jul 2016) | 1 line

Update cxx_dr_Status after 3.9 branch
------------------------------------------------------------------------

llvm-svn: 276892
2016-07-27 16:56:35 +00:00
Hans Wennborg
2245ca9d1b Generate docs/AttributeReference.rst
$ bin/clang-tblgen -gen-attr-docs -I../cfe.src/include \
    ../cfe.src/include/clang/Basic/Attr.td \
    -o ../cfe.src/docs/AttributeReference.rst

llvm-svn: 276890
2016-07-27 16:48:23 +00:00
Hans Wennborg
c27cf5f781 Merging r276887:
------------------------------------------------------------------------
r276887 | hans | 2016-07-27 09:39:45 -0700 (Wed, 27 Jul 2016) | 1 line

Update cxx_dr_Status after 3.9 branch
------------------------------------------------------------------------

llvm-svn: 276888
2016-07-27 16:41:01 +00:00
Hans Wennborg
5eaa7156fe Merging r275978:
------------------------------------------------------------------------
r275978 | pgode | 2016-07-19 07:30:21 -0700 (Tue, 19 Jul 2016) | 7 lines

[AArch64] PredictableSelectIsExpensive for Vulcan.

Adding PredictableSelectIsExpensive for Vulcan

Differential Revision: https://reviews.llvm.org/D22448


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

llvm-svn: 276880
2016-07-27 15:24:55 +00:00
Hans Wennborg
87ac29751a Merging r276015:
------------------------------------------------------------------------
r276015 | vedantk | 2016-07-19 13:16:08 -0700 (Tue, 19 Jul 2016) | 7 lines

[tsan] Don't instrument __llvm_gcov_global_state_pred or __llvm_gcda*

r274801 did not go far enough to allow gcov+tsan to cooperate. With this
commit it's possible to run the following code without false positives:

  std::thread T1(fib), T2(fib);
  T1.join(); T2.join();
------------------------------------------------------------------------

llvm-svn: 276810
2016-07-26 21:22:57 +00:00
Hans Wennborg
7bf9e46fbe Merging r276716:
------------------------------------------------------------------------
r276716 | vedantk | 2016-07-25 17:24:59 -0700 (Mon, 25 Jul 2016) | 10 lines

[Coverage] Do not write out coverage mappings with zero entries

After r275121, we stopped mapping regions from system headers. Lambdas
declared in regions belonging to system headers started producing empty
coverage mappings, since the files corresponding to their spelling locs
were being ignored.

The coverage reader doesn't know what to do with these empty mappings.
This commit makes sure that we don't produce them and adds a test. I'll
make the reader stricter in a follow-up commit.
------------------------------------------------------------------------

llvm-svn: 276801
2016-07-26 20:47:37 +00:00
Hans Wennborg
79d0e34a0a Merging r276109:
------------------------------------------------------------------------
r276109 | rengolin | 2016-07-20 05:16:38 -0700 (Wed, 20 Jul 2016) | 20 lines

[docs] Fixing Sphinx warnings to unclog the buildbot

Lots of blocks had "llvm" or "nasm" syntax types but either weren't following
the syntax, or the syntax has changed (and sphinx hasn't keep up) or the type
doesn't even exist (nasm?).

Other documents had :options: what were invalid. I only removed those that had
warnings, and left the ones that didn't, in order to follow the principle of
least surprise.

This is like this for ages, but the buildbot is now failing on errors. It may
take a while to upgrade the buildbot's sphinx, if that's even possible, but
that shouldn't stop us from getting docs updates (which seem down for quite
a while).

Also, we're not losing any syntax highlight, since when it doesn't parse, it
doesn't colour. Ie. those blocks are not being highlighted anyway.

I'm trying to get all docs in one go, so that it's easy to revert later if we
do fix, or at least easy to know what's to fix.
------------------------------------------------------------------------

llvm-svn: 276758
2016-07-26 15:58:53 +00:00
Hans Wennborg
d82de6f06e Merging r276473:
------------------------------------------------------------------------
r276473 | vvassilev | 2016-07-22 14:08:24 -0700 (Fri, 22 Jul 2016) | 13 lines

[modules] Teach the ASTWriter to ignore mutations coming from the ASTReader.

Processing update records (and loading a module, in general) might trigger
unexpected calls to the ASTWriter (being a mutation listener). Now we have a
mechanism to suppress those calls to the ASTWriter but notify other possible
mutation listeners.

Fixes https://llvm.org/bugs/show_bug.cgi?id=28332

Patch by Cristina Cristescu and me.

Reviewed by Richard Smith (D21800).

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

llvm-svn: 276757
2016-07-26 15:57:30 +00:00
Hans Wennborg
d6dff71d7d Merging r276510:
------------------------------------------------------------------------
r276510 | majnemer | 2016-07-22 19:56:49 -0700 (Fri, 22 Jul 2016) | 9 lines

[LoopUnrollAnalyzer] Handle out of bounds accesses in visitLoad

While we handed loads past the end of an array, we didn't handle loads
_before_ the array.

This fixes PR28062.

N.B. While the bug in the code is obvious, I am struggling to craft a
test case which is reasonable in size.
------------------------------------------------------------------------

llvm-svn: 276688
2016-07-25 20:53:27 +00:00
Hans Wennborg
5244f2f904 Merging r276077:
------------------------------------------------------------------------
r276077 | mzolotukhin | 2016-07-19 18:55:27 -0700 (Tue, 19 Jul 2016) | 4 lines

Revert "Revert r275883 and r275891. They seem to cause PR28608."

This reverts commit r276064, and thus reapplies r275891 and r275883 with
a fix for PR28608.
------------------------------------------------------------------------

llvm-svn: 276665
2016-07-25 17:45:34 +00:00
Hans Wennborg
f26092687c Merging r275928 and r276438:
------------------------------------------------------------------------
r275928 | arsenm | 2016-07-18 16:09:51 -0700 (Mon, 18 Jul 2016) | 1 line

AMDGPU: Fix test name and broken CHECK-LABEL
------------------------------------------------------------------------

------------------------------------------------------------------------
r276438 | arsenm | 2016-07-22 10:01:33 -0700 (Fri, 22 Jul 2016) | 6 lines

AMDGPU: Fix groupstaticsize for large LDS

The size can exceed s_movk_i32's limit, and we don't
want to use it this early since it inhibits optimizations.

This should probably be merged to the release branch.
------------------------------------------------------------------------

llvm-svn: 276664
2016-07-25 17:42:22 +00:00
Hans Wennborg
a487e1a649 Merging r276236 and r276237:
------------------------------------------------------------------------
r276236 | deadalnix | 2016-07-20 21:25:06 -0700 (Wed, 20 Jul 2016) | 9 lines

Expose AttributeSetNode, use it to provide aggregate getter for attribute in the C API.

Summary: See D19181 for context.

Reviewers: whitequark, Wallbraker, jyknight, echristo, bkramer, void

Subscribers: mehdi_amini

Differential Revision: http://reviews.llvm.org/D21265
------------------------------------------------------------------------

------------------------------------------------------------------------
r276237 | deadalnix | 2016-07-20 21:31:38 -0700 (Wed, 20 Jul 2016) | 1 line

Add missing import to fix the build
------------------------------------------------------------------------

llvm-svn: 276663
2016-07-25 17:37:43 +00:00
Hans Wennborg
aaa653d4d4 Merging r276389:
------------------------------------------------------------------------
r276389 | majnemer | 2016-07-21 21:54:44 -0700 (Thu, 21 Jul 2016) | 6 lines

Don't remove side effecting instructions due to ConstantFoldInstruction

Just because we can constant fold the result of an instruction does not
imply that we can delete the instruction.  It may have side effects.

This fixes PR28655.
------------------------------------------------------------------------

llvm-svn: 276660
2016-07-25 17:27:28 +00:00
Hans Wennborg
8e554281db Merging r276361:
------------------------------------------------------------------------
r276361 | wolfgangp | 2016-07-21 16:28:18 -0700 (Thu, 21 Jul 2016) | 5 lines

Reverting r275115 which caused PR28634.
When empty (forwarding) basic blocks that are referenced by user labels
are removed, incorrect code may be generated.


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

llvm-svn: 276656
2016-07-25 17:19:19 +00:00
Hans Wennborg
76fe58b155 Merging r276479:
------------------------------------------------------------------------
r276479 | asbirlea | 2016-07-22 15:02:19 -0700 (Fri, 22 Jul 2016) | 11 lines

Add flag to PassManagerBuilder to disable GVN Hoist Pass.

Summary:
Adding a flag to diable GVN Hoisting by default.
Note: The GVN Hoist Pass causes some Halide tests to hang. Halide will disable the pass while investigating.

Reviewers: llvm-commits, chandlerc, spop, dberlin

Subscribers: mehdi_amini

Differential Revision: https://reviews.llvm.org/D22639
------------------------------------------------------------------------

llvm-svn: 276647
2016-07-25 16:37:31 +00:00
Hans Wennborg
3ce6eededa Merging Clang-side of r275967: (the LLVM side already merged in r276561)
------------------------------------------------------------------------
r275967 | dsanders | 2016-07-19 03:49:03 -0700 (Tue, 19 Jul 2016) | 16 lines

[mips] Correct label prefixes for N32 and N64.

Summary:
N32 and N64 follow the standard ELF conventions (.L) whereas O32 uses its own
($).

This fixes the majority of object differences between -fintegrated-as and
-fno-integrated-as.

Reviewers: sdardis

Subscribers: dsanders, sdardis, llvm-commits

Differential Revision: https://reviews.llvm.org/D22412


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

llvm-svn: 276643
2016-07-25 16:21:28 +00:00
Daniel Sanders
5bdc21f29c Merging r275968:
------------------------------------------------------------------------
r275968 | dsanders | 2016-07-19 11:58:06 +0100 (Tue, 19 Jul 2016) | 7 lines

[mips][ias] R_MIPS_GOT_(PAGE|OFST) do not need symbols

Reviewers: sdardis

Subscribers: dsanders, llvm-commits, sdardis

Differential Revision: https://reviews.llvm.org/D22458
------------------------------------------------------------------------

llvm-svn: 276562
2016-07-24 11:40:23 +00:00
Daniel Sanders
120ca53c91 Merging r275967:
------------------------------------------------------------------------
r275967 | dsanders | 2016-07-19 11:49:03 +0100 (Tue, 19 Jul 2016) | 16 lines

[mips] Correct label prefixes for N32 and N64.

Summary:
N32 and N64 follow the standard ELF conventions (.L) whereas O32 uses its own
($).

This fixes the majority of object differences between -fintegrated-as and
-fno-integrated-as.

Reviewers: sdardis

Subscribers: dsanders, sdardis, llvm-commits

Differential Revision: https://reviews.llvm.org/D22412


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

llvm-svn: 276561
2016-07-24 11:39:45 +00:00
Daniel Sanders
53da0ecd27 Merging r275966:
------------------------------------------------------------------------
r275966 | dsanders | 2016-07-19 11:22:19 +0100 (Tue, 19 Jul 2016) | 11 lines

[mips] Recognise the triple used by Debian stretch for mips64el.

Summary:
The triple used for this distribution is mips64el-linux-gnuabi64.

Reviewers: sdardis

Subscribers: sdardis, llvm-commits

Differential Revision: https://reviews.llvm.org/D22406

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

llvm-svn: 276560
2016-07-24 10:34:15 +00:00
George Burgess IV
f5408dc358 Merging r276232:
------------------------------------------------------------------------
r276232 | gbiv | 2016-07-20 20:28:13 -0700 (Wed, 20 Jul 2016) | 13 lines

[Sema] Fix PR28623.

In atomic builtins, we assumed that the LValue conversion on the first
argument would succeed. So, we would crash given code like:

```
void ovl(char);
void ovl(int);
__atomic_store_n(ovl, 0, 0);
```

This patch makes us not assume that said conversion is successful. :)

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

llvm-svn: 276489
2016-07-22 22:57:21 +00:00
Eric Fiselier
765e9c1d2a Merging r276215:
------------------------------------------------------------------------
r276215 | ericwf | 2016-07-20 17:56:42 -0600 (Wed, 20 Jul 2016) | 9 lines

[libunwind] Properly align _Unwind_Exception.

Summary: _Unwind_Exception is required to be double word aligned. Currently the struct is under aligned.

Reviewers: mclow.lists, compnerd, kledzik, emaste

Subscribers: emaste, cfe-commits

Differential Revision: https://reviews.llvm.org/D22543
------------------------------------------------------------------------

llvm-svn: 276462
2016-07-22 20:04:56 +00:00
Ed Maste
e24755b5fa Merge r276128: libunwind: limit stack usage in unwind cursor
llvm-svn: 276424
2016-07-22 15:00:42 +00:00
Hans Wennborg
9c50fd5f17 ReleaseNotes: s/3.7/3.8/ in 'since the 3.7 release'
llvm-svn: 276421
2016-07-22 14:17:04 +00:00
Hans Wennborg
0a3b90dada Merging r276358, r276364, and r276368
------------------------------------------------------------------------
r276358 | spop | 2016-07-21 16:22:10 -0700 (Thu, 21 Jul 2016) | 6 lines

GVH-hoist: only clone GEPs (PR28606)

Do not clone stored values unless they are GEPs that are special cased to avoid
hoisting them without hoisting their associated ld/st.

Differential revision: https://reviews.llvm.org/D22652
------------------------------------------------------------------------

------------------------------------------------------------------------
r276364 | spop | 2016-07-21 16:32:39 -0700 (Thu, 21 Jul 2016) | 1 line

GVN-hoist: add missing check for all GEP operands available
------------------------------------------------------------------------

------------------------------------------------------------------------
r276368 | spop | 2016-07-21 17:07:01 -0700 (Thu, 21 Jul 2016) | 1 line

GVN-hoist: move check before mutating the IR
------------------------------------------------------------------------

llvm-svn: 276420
2016-07-22 14:12:11 +00:00
Hans Wennborg
6c35e88a87 Merging r276181:
------------------------------------------------------------------------
r276181 | majnemer | 2016-07-20 14:05:01 -0700 (Wed, 20 Jul 2016) | 6 lines

[GVNHoist] Don't hoist PHI nodes

We hoisted PHIs without respecting their special insertion point in the
block, leading to verfier errors.

This fixes PR28626.
------------------------------------------------------------------------

llvm-svn: 276418
2016-07-22 14:08:45 +00:00
George Burgess IV
e30f938b24 [MSSA] Fix PR28632 in the 3.9 branch.
The now-removed assertion was really more for initial debugging; it's
perfectly valid (albeit relatively rare) for `Q.Visited.size()` to be
greater than 1,000. A similar patch hasn't been applied to trunk
because the piece of code this assertion was a part of no longer exists
in trunk.

Fix okayed by Danny, merge to 3.9 okayed by Hans.

llvm-svn: 276337
2016-07-21 21:09:24 +00:00
Eric Fiselier
669800fd7c Merging r276003:
------------------------------------------------------------------------
r276003 | ericwf | 2016-07-19 11:56:20 -0600 (Tue, 19 Jul 2016) | 35 lines

Fix undefined behavior in __tree

Summary:
This patch attempts to fix the undefined behavior in __tree by changing the node pointer types used throughout. The pointer types are changed for raw pointers in the current ABI and for fancy pointers in ABI V2 (since the fancy pointer types may not be ABI compatible).

The UB in `__tree` arises because tree downcasts the embedded end node and then deferences that pointer. Currently there are 3 node types in __tree.

* `__tree_end_node` which contains the `__left_` pointer. This node is embedded within the container.
* `__tree_node_base` which contains `__right_`, `__parent_` and `__is_black`. This node is used throughout the tree rebalancing algorithms.
* `__tree_node` which contains `__value_`.

Currently `__tree` stores the start of the tree, `__begin_node_`, as a pointer to a `__tree_node`. Additionally the iterators store their position as a pointer to a `__tree_node`. In both of these cases the pointee can be the end node. This is fixed by changing them to store `__tree_end_node` pointers instead.

To make this change I introduced an `__iter_pointer` typedef which is defined to be a pointer to either `__tree_end_node` in the new ABI or `__tree_node` in the current one.
Both `__tree::__begin_node_` and iterator pointers are now stored as `__iter_pointers`.

The other situation where `__tree_end_node` is stored as the wrong type is in `__tree_node_base::__parent_`.  Currently `__left_`, `__right_`, and `__parent_` are all `__tree_node_base` pointers. Since the end node will only be stored in `__parent_` the fix is to change `__parent_` to be a pointer to `__tree_end_node`.

To make this change I introduced a `__parent_pointer` typedef which is defined to be a pointer to either `__tree_end_node` in the new ABI or `__tree_node_base` in the current one.

Note that in the new ABI `__iter_pointer` and `__parent_pointer` are the same type (but not in the old one). The confusion between these two types is unfortunate but it was the best solution I could come up with that maintains the ABI.

The typedef changes force a ton of explicit type casts to correct pointer types and to make current code compatible with both the old and new pointer typedefs. This is the bulk of the change and it's really messy. Unfortunately I don't know how to avoid it.

Please let me know what you think.





Reviewers: howard.hinnant, mclow.lists

Subscribers: howard.hinnant, bbannier, cfe-commits

Differential Revision: https://reviews.llvm.org/D20786
------------------------------------------------------------------------

llvm-svn: 276212
2016-07-20 23:53:44 +00:00
Hans Wennborg
64903ae434 Merging r275935:
------------------------------------------------------------------------
r275935 | arsenm | 2016-07-18 17:35:22 -0700 (Mon, 18 Jul 2016) | 6 lines

AMDGPU/SI: Fix SI scheduler refcount issue

Without this fix, releaseSuccessors when InOrOutBlock is
false could release SUs outside the schedule BasicBlock.

Patch by Axel Davy
------------------------------------------------------------------------

llvm-svn: 276116
2016-07-20 14:09:21 +00:00
Hans Wennborg
ebc53c200f Merging r275943:
------------------------------------------------------------------------
r275943 | compnerd | 2016-07-18 19:13:08 -0700 (Mon, 18 Jul 2016) | 1 line

clang-rename: fix referenced variable in vim-script
------------------------------------------------------------------------

llvm-svn: 276115
2016-07-20 13:45:40 +00:00
Hans Wennborg
f1c81f72da Merging r275946 and r275948:
------------------------------------------------------------------------
r275946 | bruening | 2016-07-18 22:03:38 -0700 (Mon, 18 Jul 2016) | 5 lines

[esan] Fix sideline thread flaky assert

Fixes an esan sideline thread CHECK that failed to account for the sideline
thread reaching its code before the internal_clone() return value was
assigned in the parent.
------------------------------------------------------------------------

------------------------------------------------------------------------
r275948 | bruening | 2016-07-18 22:06:48 -0700 (Mon, 18 Jul 2016) | 4 lines

[esan|wset] Fix flaky sampling tests

Adds a new esan public interface routine __esan_get_sample_count() and uses
it to ensure that tests of sampling receive the minimum number of samples.
------------------------------------------------------------------------

llvm-svn: 276114
2016-07-20 13:43:47 +00:00
Hans Wennborg
891d9aafe9 Add attribute abi_tag to the release notes
Patch by Dmitry Polukhin <dmitry.polukhin@gmail.com>!

Differential Revision: https://reviews.llvm.org/D21970

llvm-svn: 276113
2016-07-20 13:28:36 +00:00
Tim Northover
3078a53316 Merging r275866:
------------------------------------------------------------------------
r275866 | tnorthover | 2016-07-18 11:28:52 -0700 (Mon, 18 Jul 2016) | 6 lines

CodeGenPrep: use correct function to determine Global's alignment.

Elsewhere (particularly computeKnownBits) we assume that a global will be
aligned to the value returned by Value::getPointerAlignment. This is used to
boost the alignment on memcpy/memset, so any target-specific request can only
increase that value.
------------------------------------------------------------------------

llvm-svn: 275918
2016-07-18 21:36:33 +00:00
Hans Wennborg
e7249290c2 Merging r275879:
------------------------------------------------------------------------
r275879 | rnk | 2016-07-18 11:53:50 -0700 (Mon, 18 Jul 2016) | 1 line

Fix -Wmicrosoft-enum-value in GVNHoist.cpp
------------------------------------------------------------------------

llvm-svn: 275910
2016-07-18 20:48:17 +00:00
Hans Wennborg
6718a4577b Merging r275880:
------------------------------------------------------------------------
r275880 | dcoughlin | 2016-07-18 11:57:50 -0700 (Mon, 18 Jul 2016) | 3 lines

Revert "[analyzer] Add checker modeling potential C++ self-assignment"

This reverts commit r275820. It is failing on the bots.
------------------------------------------------------------------------

llvm-svn: 275902
2016-07-18 20:12:49 +00:00
Hans Wennborg
b862a9d664 Merging r275898:
------------------------------------------------------------------------
r275898 | hans | 2016-07-18 13:06:27 -0700 (Mon, 18 Jul 2016) | 8 lines

Revert r273099 "If the revision number starts with r, drop it. It will get added back"

This doesn't seem to work with Bash:

$ /work/llvm/utils/release/merge.sh --proj llvm --rev r275870
/work/llvm/utils/release/merge.sh: line 34: ${$1#r}: bad substitution

I get the same error with and without a leading 'r'.
------------------------------------------------------------------------

llvm-svn: 275900
2016-07-18 20:07:11 +00:00
Hans Wennborg
d60f41c121 Merging r275870:
------------------------------------------------------------------------
r275870 | arsenm | 2016-07-18 11:34:59 -0700 (Mon, 18 Jul 2016) | 1 line

AMDGPU/R600: Replace barrier intrinsics
------------------------------------------------------------------------

llvm-svn: 275896
2016-07-18 20:03:22 +00:00
Hans Wennborg
768082d9f1 Analyzer docs: update version to 3.9
llvm-svn: 275851
2016-07-18 18:20:49 +00:00
Hans Wennborg
f31b1e4799 Change version from 3.9.0svn to 3.9.0
llvm-svn: 275850
2016-07-18 18:19:54 +00:00
Hans Wennborg
aa71f5361b Creating release_39 branch off revision 275826
llvm-svn: 275841
llvm-svn: 275839
llvm-svn: 275838
llvm-svn: 275836
llvm-svn: 275835
llvm-svn: 275834
llvm-svn: 275833
llvm-svn: 275832
llvm-svn: 275831
llvm-svn: 275829
llvm-svn: 275828
2016-07-18 17:53:22 +00:00
21060 changed files with 1008392 additions and 1840629 deletions

View File

@@ -1,15 +1,11 @@
add_subdirectory(clang-apply-replacements)
add_subdirectory(clang-rename)
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(include-fixer)
add_subdirectory(pp-trace)
add_subdirectory(tool-template)

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)

View File

@@ -1,906 +0,0 @@
//===-- ChangeNamespace.cpp - Change namespace implementation -------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "ChangeNamespace.h"
#include "clang/Format/Format.h"
#include "clang/Lex/Lexer.h"
#include "llvm/Support/ErrorHandling.h"
using namespace clang::ast_matchers;
namespace clang {
namespace change_namespace {
namespace {
inline std::string
joinNamespaces(const llvm::SmallVectorImpl<StringRef> &Namespaces) {
if (Namespaces.empty())
return "";
std::string Result = Namespaces.front();
for (auto I = Namespaces.begin() + 1, E = Namespaces.end(); I != E; ++I)
Result += ("::" + *I).str();
return Result;
}
SourceLocation startLocationForType(TypeLoc TLoc) {
// For elaborated types (e.g. `struct a::A`) we want the portion after the
// `struct` but including the namespace qualifier, `a::`.
if (TLoc.getTypeLocClass() == TypeLoc::Elaborated) {
NestedNameSpecifierLoc NestedNameSpecifier =
TLoc.castAs<ElaboratedTypeLoc>().getQualifierLoc();
if (NestedNameSpecifier.getNestedNameSpecifier())
return NestedNameSpecifier.getBeginLoc();
TLoc = TLoc.getNextTypeLoc();
}
return TLoc.getLocStart();
}
SourceLocation endLocationForType(TypeLoc TLoc) {
// Dig past any namespace or keyword qualifications.
while (TLoc.getTypeLocClass() == TypeLoc::Elaborated ||
TLoc.getTypeLocClass() == TypeLoc::Qualified)
TLoc = TLoc.getNextTypeLoc();
// The location for template specializations (e.g. Foo<int>) includes the
// templated types in its location range. We want to restrict this to just
// before the `<` character.
if (TLoc.getTypeLocClass() == TypeLoc::TemplateSpecialization)
return TLoc.castAs<TemplateSpecializationTypeLoc>()
.getLAngleLoc()
.getLocWithOffset(-1);
return TLoc.getEndLoc();
}
// Returns the containing namespace of `InnerNs` by skipping `PartialNsName`.
// If the `InnerNs` does not have `PartialNsName` as suffix, or `PartialNsName`
// is empty, nullptr is returned.
// For example, if `InnerNs` is "a::b::c" and `PartialNsName` is "b::c", then
// the NamespaceDecl of namespace "a" will be returned.
const NamespaceDecl *getOuterNamespace(const NamespaceDecl *InnerNs,
llvm::StringRef PartialNsName) {
if (!InnerNs || PartialNsName.empty())
return nullptr;
const auto *CurrentContext = llvm::cast<DeclContext>(InnerNs);
const auto *CurrentNs = InnerNs;
llvm::SmallVector<llvm::StringRef, 4> PartialNsNameSplitted;
PartialNsName.split(PartialNsNameSplitted, "::", /*MaxSplit=*/-1,
/*KeepEmpty=*/false);
while (!PartialNsNameSplitted.empty()) {
// Get the inner-most namespace in CurrentContext.
while (CurrentContext && !llvm::isa<NamespaceDecl>(CurrentContext))
CurrentContext = CurrentContext->getParent();
if (!CurrentContext)
return nullptr;
CurrentNs = llvm::cast<NamespaceDecl>(CurrentContext);
if (PartialNsNameSplitted.back() != CurrentNs->getNameAsString())
return nullptr;
PartialNsNameSplitted.pop_back();
CurrentContext = CurrentContext->getParent();
}
return CurrentNs;
}
static std::unique_ptr<Lexer>
getLexerStartingFromLoc(SourceLocation Loc, const SourceManager &SM,
const LangOptions &LangOpts) {
if (Loc.isMacroID() &&
!Lexer::isAtEndOfMacroExpansion(Loc, SM, LangOpts, &Loc))
return nullptr;
// Break down the source location.
std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc);
// Try to load the file buffer.
bool InvalidTemp = false;
llvm::StringRef File = SM.getBufferData(LocInfo.first, &InvalidTemp);
if (InvalidTemp)
return nullptr;
const char *TokBegin = File.data() + LocInfo.second;
// Lex from the start of the given location.
return llvm::make_unique<Lexer>(SM.getLocForStartOfFile(LocInfo.first),
LangOpts, File.begin(), TokBegin, File.end());
}
// FIXME: get rid of this helper function if this is supported in clang-refactor
// library.
static SourceLocation getStartOfNextLine(SourceLocation Loc,
const SourceManager &SM,
const LangOptions &LangOpts) {
std::unique_ptr<Lexer> Lex = getLexerStartingFromLoc(Loc, SM, LangOpts);
if (!Lex.get())
return SourceLocation();
llvm::SmallVector<char, 16> Line;
// FIXME: this is a bit hacky to get ReadToEndOfLine work.
Lex->setParsingPreprocessorDirective(true);
Lex->ReadToEndOfLine(&Line);
auto End = Loc.getLocWithOffset(Line.size());
return SM.getLocForEndOfFile(SM.getDecomposedLoc(Loc).first) == End
? End
: End.getLocWithOffset(1);
}
// Returns `R` with new range that refers to code after `Replaces` being
// applied.
tooling::Replacement
getReplacementInChangedCode(const tooling::Replacements &Replaces,
const tooling::Replacement &R) {
unsigned NewStart = Replaces.getShiftedCodePosition(R.getOffset());
unsigned NewEnd =
Replaces.getShiftedCodePosition(R.getOffset() + R.getLength());
return tooling::Replacement(R.getFilePath(), NewStart, NewEnd - NewStart,
R.getReplacementText());
}
// Adds a replacement `R` into `Replaces` or merges it into `Replaces` by
// applying all existing Replaces first if there is conflict.
void addOrMergeReplacement(const tooling::Replacement &R,
tooling::Replacements *Replaces) {
auto Err = Replaces->add(R);
if (Err) {
llvm::consumeError(std::move(Err));
auto Replace = getReplacementInChangedCode(*Replaces, R);
*Replaces = Replaces->merge(tooling::Replacements(Replace));
}
}
tooling::Replacement createReplacement(SourceLocation Start, SourceLocation End,
llvm::StringRef ReplacementText,
const SourceManager &SM) {
if (!Start.isValid() || !End.isValid()) {
llvm::errs() << "start or end location were invalid\n";
return tooling::Replacement();
}
if (SM.getDecomposedLoc(Start).first != SM.getDecomposedLoc(End).first) {
llvm::errs()
<< "start or end location were in different macro expansions\n";
return tooling::Replacement();
}
Start = SM.getSpellingLoc(Start);
End = SM.getSpellingLoc(End);
if (SM.getFileID(Start) != SM.getFileID(End)) {
llvm::errs() << "start or end location were in different files\n";
return tooling::Replacement();
}
return tooling::Replacement(
SM, CharSourceRange::getTokenRange(SM.getSpellingLoc(Start),
SM.getSpellingLoc(End)),
ReplacementText);
}
void addReplacementOrDie(
SourceLocation Start, SourceLocation End, llvm::StringRef ReplacementText,
const SourceManager &SM,
std::map<std::string, tooling::Replacements> *FileToReplacements) {
const auto R = createReplacement(Start, End, ReplacementText, SM);
auto Err = (*FileToReplacements)[R.getFilePath()].add(R);
if (Err)
llvm_unreachable(llvm::toString(std::move(Err)).c_str());
}
tooling::Replacement createInsertion(SourceLocation Loc,
llvm::StringRef InsertText,
const SourceManager &SM) {
if (Loc.isInvalid()) {
llvm::errs() << "insert Location is invalid.\n";
return tooling::Replacement();
}
Loc = SM.getSpellingLoc(Loc);
return tooling::Replacement(SM, Loc, 0, InsertText);
}
// Returns the shortest qualified name for declaration `DeclName` in the
// namespace `NsName`. For example, if `DeclName` is "a::b::X" and `NsName`
// is "a::c::d", then "b::X" will be returned.
// \param DeclName A fully qualified name, "::a::b::X" or "a::b::X".
// \param NsName A fully qualified name, "::a::b" or "a::b". Global namespace
// will have empty name.
std::string getShortestQualifiedNameInNamespace(llvm::StringRef DeclName,
llvm::StringRef NsName) {
DeclName = DeclName.ltrim(':');
NsName = NsName.ltrim(':');
if (DeclName.find(':') == llvm::StringRef::npos)
return DeclName;
while (!DeclName.consume_front((NsName + "::").str())) {
const auto Pos = NsName.find_last_of(':');
if (Pos == llvm::StringRef::npos)
return DeclName;
assert(Pos > 0);
NsName = NsName.substr(0, Pos - 1);
}
return DeclName;
}
std::string wrapCodeInNamespace(StringRef NestedNs, std::string Code) {
if (Code.back() != '\n')
Code += "\n";
llvm::SmallVector<StringRef, 4> NsSplitted;
NestedNs.split(NsSplitted, "::", /*MaxSplit=*/-1,
/*KeepEmpty=*/false);
while (!NsSplitted.empty()) {
// FIXME: consider code style for comments.
Code = ("namespace " + NsSplitted.back() + " {\n" + Code +
"} // namespace " + NsSplitted.back() + "\n")
.str();
NsSplitted.pop_back();
}
return Code;
}
// Returns true if \p D is a nested DeclContext in \p Context
bool isNestedDeclContext(const DeclContext *D, const DeclContext *Context) {
while (D) {
if (D == Context)
return true;
D = D->getParent();
}
return false;
}
// Returns true if \p D is visible at \p Loc with DeclContext \p DeclCtx.
bool isDeclVisibleAtLocation(const SourceManager &SM, const Decl *D,
const DeclContext *DeclCtx, SourceLocation Loc) {
SourceLocation DeclLoc = SM.getSpellingLoc(D->getLocation());
Loc = SM.getSpellingLoc(Loc);
return SM.isBeforeInTranslationUnit(DeclLoc, Loc) &&
(SM.getFileID(DeclLoc) == SM.getFileID(Loc) &&
isNestedDeclContext(DeclCtx, D->getDeclContext()));
}
} // anonymous namespace
ChangeNamespaceTool::ChangeNamespaceTool(
llvm::StringRef OldNs, llvm::StringRef NewNs, llvm::StringRef FilePattern,
std::map<std::string, tooling::Replacements> *FileToReplacements,
llvm::StringRef FallbackStyle)
: FallbackStyle(FallbackStyle), FileToReplacements(*FileToReplacements),
OldNamespace(OldNs.ltrim(':')), NewNamespace(NewNs.ltrim(':')),
FilePattern(FilePattern), FilePatternRE(FilePattern) {
FileToReplacements->clear();
llvm::SmallVector<llvm::StringRef, 4> OldNsSplitted;
llvm::SmallVector<llvm::StringRef, 4> NewNsSplitted;
llvm::StringRef(OldNamespace).split(OldNsSplitted, "::");
llvm::StringRef(NewNamespace).split(NewNsSplitted, "::");
// Calculates `DiffOldNamespace` and `DiffNewNamespace`.
while (!OldNsSplitted.empty() && !NewNsSplitted.empty() &&
OldNsSplitted.front() == NewNsSplitted.front()) {
OldNsSplitted.erase(OldNsSplitted.begin());
NewNsSplitted.erase(NewNsSplitted.begin());
}
DiffOldNamespace = joinNamespaces(OldNsSplitted);
DiffNewNamespace = joinNamespaces(NewNsSplitted);
}
void ChangeNamespaceTool::registerMatchers(ast_matchers::MatchFinder *Finder) {
std::string FullOldNs = "::" + OldNamespace;
// Prefix is the outer-most namespace in DiffOldNamespace. For example, if the
// OldNamespace is "a::b::c" and DiffOldNamespace is "b::c", then Prefix will
// be "a::b". Declarations in this namespace will not be visible in the new
// namespace. If DiffOldNamespace is empty, Prefix will be a invalid name "-".
llvm::SmallVector<llvm::StringRef, 4> DiffOldNsSplitted;
llvm::StringRef(DiffOldNamespace)
.split(DiffOldNsSplitted, "::", /*MaxSplit=*/-1,
/*KeepEmpty=*/false);
std::string Prefix = "-";
if (!DiffOldNsSplitted.empty())
Prefix = (StringRef(FullOldNs).drop_back(DiffOldNamespace.size()) +
DiffOldNsSplitted.front())
.str();
auto IsInMovedNs =
allOf(hasAncestor(namespaceDecl(hasName(FullOldNs)).bind("ns_decl")),
isExpansionInFileMatching(FilePattern));
auto IsVisibleInNewNs = anyOf(
IsInMovedNs, unless(hasAncestor(namespaceDecl(hasName(Prefix)))));
// Match using declarations.
Finder->addMatcher(
usingDecl(isExpansionInFileMatching(FilePattern), IsVisibleInNewNs)
.bind("using"),
this);
// Match using namespace declarations.
Finder->addMatcher(usingDirectiveDecl(isExpansionInFileMatching(FilePattern),
IsVisibleInNewNs)
.bind("using_namespace"),
this);
// Match namespace alias declarations.
Finder->addMatcher(namespaceAliasDecl(isExpansionInFileMatching(FilePattern),
IsVisibleInNewNs)
.bind("namespace_alias"),
this);
// Match old namespace blocks.
Finder->addMatcher(
namespaceDecl(hasName(FullOldNs), isExpansionInFileMatching(FilePattern))
.bind("old_ns"),
this);
// Match class forward-declarations in the old namespace.
// Note that forward-declarations in classes are not matched.
Finder->addMatcher(cxxRecordDecl(unless(anyOf(isImplicit(), isDefinition())),
IsInMovedNs, hasParent(namespaceDecl()))
.bind("class_fwd_decl"),
this);
// Match template class forward-declarations in the old namespace.
Finder->addMatcher(
classTemplateDecl(unless(hasDescendant(cxxRecordDecl(isDefinition()))),
IsInMovedNs, hasParent(namespaceDecl()))
.bind("template_class_fwd_decl"),
this);
// Match references to types that are not defined in the old namespace.
// Forward-declarations in the old namespace are also matched since they will
// be moved back to the old namespace.
auto DeclMatcher = namedDecl(
hasAncestor(namespaceDecl()),
unless(anyOf(
isImplicit(), hasAncestor(namespaceDecl(isAnonymous())),
hasAncestor(cxxRecordDecl()),
allOf(IsInMovedNs, unless(cxxRecordDecl(unless(isDefinition())))))));
// Using shadow declarations in classes always refers to base class, which
// does not need to be qualified since it can be inferred from inheritance.
// Note that this does not match using alias declarations.
auto UsingShadowDeclInClass =
usingDecl(hasAnyUsingShadowDecl(decl()), hasParent(cxxRecordDecl()));
// Match TypeLocs on the declaration. Carefully match only the outermost
// TypeLoc and template specialization arguments (which are not outermost)
// that are directly linked to types matching `DeclMatcher`. Nested name
// specifier locs are handled separately below.
Finder->addMatcher(
typeLoc(IsInMovedNs,
loc(qualType(hasDeclaration(DeclMatcher.bind("from_decl")))),
unless(anyOf(hasParent(typeLoc(loc(qualType(
allOf(hasDeclaration(DeclMatcher),
unless(templateSpecializationType())))))),
hasParent(nestedNameSpecifierLoc()),
hasAncestor(isImplicit()),
hasAncestor(UsingShadowDeclInClass))),
hasAncestor(decl().bind("dc")))
.bind("type"),
this);
// Types in `UsingShadowDecl` is not matched by `typeLoc` above, so we need to
// special case it.
// Since using declarations inside classes must have the base class in the
// nested name specifier, we leave it to the nested name specifier matcher.
Finder->addMatcher(usingDecl(IsInMovedNs, hasAnyUsingShadowDecl(decl()),
unless(UsingShadowDeclInClass))
.bind("using_with_shadow"),
this);
// Handle types in nested name specifier. Specifiers that are in a TypeLoc
// matched above are not matched, e.g. "A::" in "A::A" is not matched since
// "A::A" would have already been fixed.
Finder->addMatcher(
nestedNameSpecifierLoc(
hasAncestor(decl(IsInMovedNs).bind("dc")),
loc(nestedNameSpecifier(
specifiesType(hasDeclaration(DeclMatcher.bind("from_decl"))))),
unless(anyOf(hasAncestor(isImplicit()),
hasAncestor(UsingShadowDeclInClass),
hasAncestor(typeLoc(loc(qualType(hasDeclaration(
decl(equalsBoundNode("from_decl"))))))))))
.bind("nested_specifier_loc"),
this);
// Matches base class initializers in constructors. TypeLocs of base class
// initializers do not need to be fixed. For example,
// class X : public a::b::Y {
// public:
// X() : Y::Y() {} // Y::Y do not need namespace specifier.
// };
Finder->addMatcher(
cxxCtorInitializer(isBaseInitializer()).bind("base_initializer"), this);
// Handle function.
// Only handle functions that are defined in a namespace excluding member
// function, static methods (qualified by nested specifier), and functions
// defined in the global namespace.
// Note that the matcher does not exclude calls to out-of-line static method
// definitions, so we need to exclude them in the callback handler.
auto FuncMatcher =
functionDecl(unless(anyOf(cxxMethodDecl(), IsInMovedNs,
hasAncestor(namespaceDecl(isAnonymous())),
hasAncestor(cxxRecordDecl()))),
hasParent(namespaceDecl()));
Finder->addMatcher(decl(forEachDescendant(expr(anyOf(
callExpr(callee(FuncMatcher)).bind("call"),
declRefExpr(to(FuncMatcher.bind("func_decl")))
.bind("func_ref")))),
IsInMovedNs, unless(isImplicit()))
.bind("dc"),
this);
auto GlobalVarMatcher = varDecl(
hasGlobalStorage(), hasParent(namespaceDecl()),
unless(anyOf(IsInMovedNs, hasAncestor(namespaceDecl(isAnonymous())))));
Finder->addMatcher(declRefExpr(IsInMovedNs, hasAncestor(decl().bind("dc")),
to(GlobalVarMatcher.bind("var_decl")))
.bind("var_ref"),
this);
}
void ChangeNamespaceTool::run(
const ast_matchers::MatchFinder::MatchResult &Result) {
if (const auto *Using = Result.Nodes.getNodeAs<UsingDecl>("using")) {
UsingDecls.insert(Using);
} else if (const auto *UsingNamespace =
Result.Nodes.getNodeAs<UsingDirectiveDecl>(
"using_namespace")) {
UsingNamespaceDecls.insert(UsingNamespace);
} else if (const auto *NamespaceAlias =
Result.Nodes.getNodeAs<NamespaceAliasDecl>(
"namespace_alias")) {
NamespaceAliasDecls.insert(NamespaceAlias);
} else if (const auto *NsDecl =
Result.Nodes.getNodeAs<NamespaceDecl>("old_ns")) {
moveOldNamespace(Result, NsDecl);
} else if (const auto *FwdDecl =
Result.Nodes.getNodeAs<CXXRecordDecl>("class_fwd_decl")) {
moveClassForwardDeclaration(Result, cast<NamedDecl>(FwdDecl));
} else if (const auto *TemplateFwdDecl =
Result.Nodes.getNodeAs<ClassTemplateDecl>(
"template_class_fwd_decl")) {
moveClassForwardDeclaration(Result, cast<NamedDecl>(TemplateFwdDecl));
} else if (const auto *UsingWithShadow =
Result.Nodes.getNodeAs<UsingDecl>("using_with_shadow")) {
fixUsingShadowDecl(Result, UsingWithShadow);
} else if (const auto *Specifier =
Result.Nodes.getNodeAs<NestedNameSpecifierLoc>(
"nested_specifier_loc")) {
SourceLocation Start = Specifier->getBeginLoc();
SourceLocation End = endLocationForType(Specifier->getTypeLoc());
fixTypeLoc(Result, Start, End, Specifier->getTypeLoc());
} else if (const auto *BaseInitializer =
Result.Nodes.getNodeAs<CXXCtorInitializer>(
"base_initializer")) {
BaseCtorInitializerTypeLocs.push_back(
BaseInitializer->getTypeSourceInfo()->getTypeLoc());
} else if (const auto *TLoc = Result.Nodes.getNodeAs<TypeLoc>("type")) {
// This avoids fixing types with record types as qualifier, which is not
// filtered by matchers in some cases, e.g. the type is templated. We should
// handle the record type qualifier instead.
TypeLoc Loc = *TLoc;
while (Loc.getTypeLocClass() == TypeLoc::Qualified)
Loc = Loc.getNextTypeLoc();
if (Loc.getTypeLocClass() == TypeLoc::Elaborated) {
NestedNameSpecifierLoc NestedNameSpecifier =
Loc.castAs<ElaboratedTypeLoc>().getQualifierLoc();
const Type *SpecifierType =
NestedNameSpecifier.getNestedNameSpecifier()->getAsType();
if (SpecifierType && SpecifierType->isRecordType())
return;
}
fixTypeLoc(Result, startLocationForType(Loc), endLocationForType(Loc), Loc);
} else if (const auto *VarRef =
Result.Nodes.getNodeAs<DeclRefExpr>("var_ref")) {
const auto *Var = Result.Nodes.getNodeAs<VarDecl>("var_decl");
assert(Var);
if (Var->getCanonicalDecl()->isStaticDataMember())
return;
const auto *Context = Result.Nodes.getNodeAs<Decl>("dc");
assert(Context && "Empty decl context.");
fixDeclRefExpr(Result, Context->getDeclContext(),
llvm::cast<NamedDecl>(Var), VarRef);
} else if (const auto *FuncRef =
Result.Nodes.getNodeAs<DeclRefExpr>("func_ref")) {
// If this reference has been processed as a function call, we do not
// process it again.
if (ProcessedFuncRefs.count(FuncRef))
return;
ProcessedFuncRefs.insert(FuncRef);
const auto *Func = Result.Nodes.getNodeAs<FunctionDecl>("func_decl");
assert(Func);
const auto *Context = Result.Nodes.getNodeAs<Decl>("dc");
assert(Context && "Empty decl context.");
fixDeclRefExpr(Result, Context->getDeclContext(),
llvm::cast<NamedDecl>(Func), FuncRef);
} else {
const auto *Call = Result.Nodes.getNodeAs<CallExpr>("call");
assert(Call != nullptr && "Expecting callback for CallExpr.");
const auto *CalleeFuncRef =
llvm::cast<DeclRefExpr>(Call->getCallee()->IgnoreImplicit());
ProcessedFuncRefs.insert(CalleeFuncRef);
const FunctionDecl *Func = Call->getDirectCallee();
assert(Func != nullptr);
// FIXME: ignore overloaded operators. This would miss cases where operators
// are called by qualified names (i.e. "ns::operator <"). Ignore such
// cases for now.
if (Func->isOverloadedOperator())
return;
// Ignore out-of-line static methods since they will be handled by nested
// name specifiers.
if (Func->getCanonicalDecl()->getStorageClass() ==
StorageClass::SC_Static &&
Func->isOutOfLine())
return;
const auto *Context = Result.Nodes.getNodeAs<Decl>("dc");
assert(Context && "Empty decl context.");
SourceRange CalleeRange = Call->getCallee()->getSourceRange();
replaceQualifiedSymbolInDeclContext(
Result, Context->getDeclContext(), CalleeRange.getBegin(),
CalleeRange.getEnd(), llvm::cast<NamedDecl>(Func));
}
}
static SourceLocation getLocAfterNamespaceLBrace(const NamespaceDecl *NsDecl,
const SourceManager &SM,
const LangOptions &LangOpts) {
std::unique_ptr<Lexer> Lex =
getLexerStartingFromLoc(NsDecl->getLocStart(), SM, LangOpts);
assert(Lex.get() &&
"Failed to create lexer from the beginning of namespace.");
if (!Lex.get())
return SourceLocation();
Token Tok;
while (!Lex->LexFromRawLexer(Tok) && Tok.isNot(tok::TokenKind::l_brace)) {
}
return Tok.isNot(tok::TokenKind::l_brace)
? SourceLocation()
: Tok.getEndLoc().getLocWithOffset(1);
}
// Stores information about a moved namespace in `MoveNamespaces` and leaves
// the actual movement to `onEndOfTranslationUnit()`.
void ChangeNamespaceTool::moveOldNamespace(
const ast_matchers::MatchFinder::MatchResult &Result,
const NamespaceDecl *NsDecl) {
// If the namespace is empty, do nothing.
if (Decl::castToDeclContext(NsDecl)->decls_empty())
return;
const SourceManager &SM = *Result.SourceManager;
// Get the range of the code in the old namespace.
SourceLocation Start =
getLocAfterNamespaceLBrace(NsDecl, SM, Result.Context->getLangOpts());
assert(Start.isValid() && "Can't find l_brace for namespace.");
MoveNamespace MoveNs;
MoveNs.Offset = SM.getFileOffset(Start);
// The range of the moved namespace is from the location just past the left
// brace to the location right before the right brace.
MoveNs.Length = SM.getFileOffset(NsDecl->getRBraceLoc()) - MoveNs.Offset;
// Insert the new namespace after `DiffOldNamespace`. For example, if
// `OldNamespace` is "a::b::c" and `NewNamespace` is `a::x::y`, then
// "x::y" will be inserted inside the existing namespace "a" and after "a::b".
// `OuterNs` is the first namespace in `DiffOldNamespace`, e.g. "namespace b"
// in the above example.
// If there is no outer namespace (i.e. DiffOldNamespace is empty), the new
// namespace will be a nested namespace in the old namespace.
const NamespaceDecl *OuterNs = getOuterNamespace(NsDecl, DiffOldNamespace);
SourceLocation InsertionLoc = Start;
if (OuterNs) {
SourceLocation LocAfterNs = getStartOfNextLine(
OuterNs->getRBraceLoc(), SM, Result.Context->getLangOpts());
assert(LocAfterNs.isValid() &&
"Failed to get location after DiffOldNamespace");
InsertionLoc = LocAfterNs;
}
MoveNs.InsertionOffset = SM.getFileOffset(SM.getSpellingLoc(InsertionLoc));
MoveNs.FID = SM.getFileID(Start);
MoveNs.SourceMgr = Result.SourceManager;
MoveNamespaces[SM.getFilename(Start)].push_back(MoveNs);
}
// Removes a class forward declaration from the code in the moved namespace and
// creates an `InsertForwardDeclaration` to insert the forward declaration back
// into the old namespace after moving code from the old namespace to the new
// 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
void ChangeNamespaceTool::moveClassForwardDeclaration(
const ast_matchers::MatchFinder::MatchResult &Result,
const NamedDecl *FwdDecl) {
SourceLocation Start = FwdDecl->getLocStart();
SourceLocation End = FwdDecl->getLocEnd();
SourceLocation AfterSemi = Lexer::findLocationAfterToken(
End, tok::semi, *Result.SourceManager, Result.Context->getLangOpts(),
/*SkipTrailingWhitespaceAndNewLine=*/true);
if (AfterSemi.isValid())
End = AfterSemi.getLocWithOffset(-1);
// Delete the forward declaration from the code to be moved.
addReplacementOrDie(Start, End, "", *Result.SourceManager,
&FileToReplacements);
llvm::StringRef Code = Lexer::getSourceText(
CharSourceRange::getTokenRange(
Result.SourceManager->getSpellingLoc(Start),
Result.SourceManager->getSpellingLoc(End)),
*Result.SourceManager, Result.Context->getLangOpts());
// Insert the forward declaration back into the old namespace after moving the
// code from old namespace to new namespace.
// Insertion information is stored in `InsertFwdDecls` and actual
// insertion will be performed in `onEndOfTranslationUnit`.
// Get the (old) namespace that contains the forward declaration.
const auto *NsDecl = Result.Nodes.getNodeAs<NamespaceDecl>("ns_decl");
// The namespace contains the forward declaration, so it must not be empty.
assert(!NsDecl->decls_empty());
const auto Insertion = createInsertion(NsDecl->decls_begin()->getLocStart(),
Code, *Result.SourceManager);
InsertForwardDeclaration InsertFwd;
InsertFwd.InsertionOffset = Insertion.getOffset();
InsertFwd.ForwardDeclText = Insertion.getReplacementText().str();
InsertFwdDecls[Insertion.getFilePath()].push_back(InsertFwd);
}
// Replaces a qualified symbol (in \p DeclCtx) that refers to a declaration \p
// FromDecl with the shortest qualified name possible when the reference is in
// `NewNamespace`.
void ChangeNamespaceTool::replaceQualifiedSymbolInDeclContext(
const ast_matchers::MatchFinder::MatchResult &Result,
const DeclContext *DeclCtx, SourceLocation Start, SourceLocation End,
const NamedDecl *FromDecl) {
const auto *NsDeclContext = DeclCtx->getEnclosingNamespaceContext();
if (llvm::isa<TranslationUnitDecl>(NsDeclContext)) {
// This should not happen in usual unless the TypeLoc is in function type
// parameters, e.g `std::function<void(T)>`. In this case, DeclContext of
// `T` will be the translation unit. We simply use fully-qualified name
// here.
// Note that `FromDecl` must not be defined in the old namespace (according
// to `DeclMatcher`), so its fully-qualified name will not change after
// changing the namespace.
addReplacementOrDie(Start, End, FromDecl->getQualifiedNameAsString(),
*Result.SourceManager, &FileToReplacements);
return;
}
const auto *NsDecl = llvm::cast<NamespaceDecl>(NsDeclContext);
// Calculate the name of the `NsDecl` after it is moved to new namespace.
std::string OldNs = NsDecl->getQualifiedNameAsString();
llvm::StringRef Postfix = OldNs;
bool Consumed = Postfix.consume_front(OldNamespace);
assert(Consumed && "Expect OldNS to start with OldNamespace.");
(void)Consumed;
const std::string NewNs = (NewNamespace + Postfix).str();
llvm::StringRef NestedName = Lexer::getSourceText(
CharSourceRange::getTokenRange(
Result.SourceManager->getSpellingLoc(Start),
Result.SourceManager->getSpellingLoc(End)),
*Result.SourceManager, Result.Context->getLangOpts());
// If the symbol is already fully qualified, no change needs to be make.
if (NestedName.startswith("::"))
return;
std::string FromDeclName = FromDecl->getQualifiedNameAsString();
std::string ReplaceName =
getShortestQualifiedNameInNamespace(FromDeclName, NewNs);
// Checks if there is any using namespace declarations that can shorten the
// qualified name.
for (const auto *UsingNamespace : UsingNamespaceDecls) {
if (!isDeclVisibleAtLocation(*Result.SourceManager, UsingNamespace, DeclCtx,
Start))
continue;
StringRef FromDeclNameRef = FromDeclName;
if (FromDeclNameRef.consume_front(UsingNamespace->getNominatedNamespace()
->getQualifiedNameAsString())) {
FromDeclNameRef = FromDeclNameRef.drop_front(2);
if (FromDeclNameRef.size() < ReplaceName.size())
ReplaceName = FromDeclNameRef;
}
}
// Checks if there is any namespace alias declarations that can shorten the
// qualified name.
for (const auto *NamespaceAlias : NamespaceAliasDecls) {
if (!isDeclVisibleAtLocation(*Result.SourceManager, NamespaceAlias, DeclCtx,
Start))
continue;
StringRef FromDeclNameRef = FromDeclName;
if (FromDeclNameRef.consume_front(
NamespaceAlias->getNamespace()->getQualifiedNameAsString() +
"::")) {
std::string AliasName = NamespaceAlias->getNameAsString();
std::string AliasQualifiedName =
NamespaceAlias->getQualifiedNameAsString();
// We only consider namespace aliases define in the global namepspace or
// in namespaces that are directly visible from the reference, i.e.
// ancestor of the `OldNs`. Note that declarations in ancestor namespaces
// but not visible in the new namespace is filtered out by
// "IsVisibleInNewNs" matcher.
if (AliasQualifiedName != AliasName) {
// The alias is defined in some namespace.
assert(StringRef(AliasQualifiedName).endswith("::" + AliasName));
llvm::StringRef AliasNs =
StringRef(AliasQualifiedName).drop_back(AliasName.size() + 2);
if (!llvm::StringRef(OldNs).startswith(AliasNs))
continue;
}
std::string NameWithAliasNamespace =
(AliasName + "::" + FromDeclNameRef).str();
if (NameWithAliasNamespace.size() < ReplaceName.size())
ReplaceName = NameWithAliasNamespace;
}
}
// Checks if there is any using shadow declarations that can shorten the
// qualified name.
bool Matched = false;
for (const UsingDecl *Using : UsingDecls) {
if (Matched)
break;
if (isDeclVisibleAtLocation(*Result.SourceManager, Using, DeclCtx, Start)) {
for (const auto *UsingShadow : Using->shadows()) {
const auto *TargetDecl = UsingShadow->getTargetDecl();
if (TargetDecl == FromDecl) {
ReplaceName = FromDecl->getNameAsString();
Matched = true;
break;
}
}
}
}
// If the new nested name in the new namespace is the same as it was in the
// old namespace, we don't create replacement.
if (NestedName == ReplaceName)
return;
// If the reference need to be fully-qualified, add a leading "::" unless
// NewNamespace is the global namespace.
if (ReplaceName == FromDeclName && !NewNamespace.empty())
ReplaceName = "::" + ReplaceName;
addReplacementOrDie(Start, End, ReplaceName, *Result.SourceManager,
&FileToReplacements);
}
// Replace the [Start, End] of `Type` with the shortest qualified name when the
// `Type` is in `NewNamespace`.
void ChangeNamespaceTool::fixTypeLoc(
const ast_matchers::MatchFinder::MatchResult &Result, SourceLocation Start,
SourceLocation End, TypeLoc Type) {
// FIXME: do not rename template parameter.
if (Start.isInvalid() || End.isInvalid())
return;
// Types of CXXCtorInitializers do not need to be fixed.
if (llvm::is_contained(BaseCtorInitializerTypeLocs, Type))
return;
// The declaration which this TypeLoc refers to.
const auto *FromDecl = Result.Nodes.getNodeAs<NamedDecl>("from_decl");
// `hasDeclaration` gives underlying declaration, but if the type is
// a typedef type, we need to use the typedef type instead.
auto IsInMovedNs = [&](const NamedDecl *D) {
if (!llvm::StringRef(D->getQualifiedNameAsString())
.startswith(OldNamespace + "::"))
return false;
auto ExpansionLoc = Result.SourceManager->getExpansionLoc(D->getLocStart());
if (ExpansionLoc.isInvalid())
return false;
llvm::StringRef Filename = Result.SourceManager->getFilename(ExpansionLoc);
return FilePatternRE.match(Filename);
};
// Make `FromDecl` the immediate declaration that `Type` refers to, i.e. if
// `Type` is an alias type, we make `FromDecl` the type alias declaration.
// Also, don't fix the \p Type if it refers to a type alias decl in the moved
// namespace since the alias decl will be moved along with the type reference.
if (auto *Typedef = Type.getType()->getAs<TypedefType>()) {
FromDecl = Typedef->getDecl();
if (IsInMovedNs(FromDecl))
return;
} else if (auto *TemplateType =
Type.getType()->getAs<TemplateSpecializationType>()) {
if (TemplateType->isTypeAlias()) {
FromDecl = TemplateType->getTemplateName().getAsTemplateDecl();
if (IsInMovedNs(FromDecl))
return;
}
}
const auto *DeclCtx = Result.Nodes.getNodeAs<Decl>("dc");
assert(DeclCtx && "Empty decl context.");
replaceQualifiedSymbolInDeclContext(Result, DeclCtx->getDeclContext(), Start,
End, FromDecl);
}
void ChangeNamespaceTool::fixUsingShadowDecl(
const ast_matchers::MatchFinder::MatchResult &Result,
const UsingDecl *UsingDeclaration) {
SourceLocation Start = UsingDeclaration->getLocStart();
SourceLocation End = UsingDeclaration->getLocEnd();
if (Start.isInvalid() || End.isInvalid())
return;
assert(UsingDeclaration->shadow_size() > 0);
// FIXME: it might not be always accurate to use the first using-decl.
const NamedDecl *TargetDecl =
UsingDeclaration->shadow_begin()->getTargetDecl();
std::string TargetDeclName = TargetDecl->getQualifiedNameAsString();
// FIXME: check if target_decl_name is in moved ns, which doesn't make much
// sense. If this happens, we need to use name with the new namespace.
// Use fully qualified name in UsingDecl for now.
addReplacementOrDie(Start, End, "using ::" + TargetDeclName,
*Result.SourceManager, &FileToReplacements);
}
void ChangeNamespaceTool::fixDeclRefExpr(
const ast_matchers::MatchFinder::MatchResult &Result,
const DeclContext *UseContext, const NamedDecl *From,
const DeclRefExpr *Ref) {
SourceRange RefRange = Ref->getSourceRange();
replaceQualifiedSymbolInDeclContext(Result, UseContext, RefRange.getBegin(),
RefRange.getEnd(), From);
}
void ChangeNamespaceTool::onEndOfTranslationUnit() {
// Move namespace blocks and insert forward declaration to old namespace.
for (const auto &FileAndNsMoves : MoveNamespaces) {
auto &NsMoves = FileAndNsMoves.second;
if (NsMoves.empty())
continue;
const std::string &FilePath = FileAndNsMoves.first;
auto &Replaces = FileToReplacements[FilePath];
auto &SM = *NsMoves.begin()->SourceMgr;
llvm::StringRef Code = SM.getBufferData(NsMoves.begin()->FID);
auto ChangedCode = tooling::applyAllReplacements(Code, Replaces);
if (!ChangedCode) {
llvm::errs() << llvm::toString(ChangedCode.takeError()) << "\n";
continue;
}
// Replacements on the changed code for moving namespaces and inserting
// forward declarations to old namespaces.
tooling::Replacements NewReplacements;
// Cut the changed code from the old namespace and paste the code in the new
// namespace.
for (const auto &NsMove : NsMoves) {
// Calculate the range of the old namespace block in the changed
// code.
const unsigned NewOffset = Replaces.getShiftedCodePosition(NsMove.Offset);
const unsigned NewLength =
Replaces.getShiftedCodePosition(NsMove.Offset + NsMove.Length) -
NewOffset;
tooling::Replacement Deletion(FilePath, NewOffset, NewLength, "");
std::string MovedCode = ChangedCode->substr(NewOffset, NewLength);
std::string MovedCodeWrappedInNewNs =
wrapCodeInNamespace(DiffNewNamespace, MovedCode);
// Calculate the new offset at which the code will be inserted in the
// changed code.
unsigned NewInsertionOffset =
Replaces.getShiftedCodePosition(NsMove.InsertionOffset);
tooling::Replacement Insertion(FilePath, NewInsertionOffset, 0,
MovedCodeWrappedInNewNs);
addOrMergeReplacement(Deletion, &NewReplacements);
addOrMergeReplacement(Insertion, &NewReplacements);
}
// After moving namespaces, insert forward declarations back to old
// namespaces.
const auto &FwdDeclInsertions = InsertFwdDecls[FilePath];
for (const auto &FwdDeclInsertion : FwdDeclInsertions) {
unsigned NewInsertionOffset =
Replaces.getShiftedCodePosition(FwdDeclInsertion.InsertionOffset);
tooling::Replacement Insertion(FilePath, NewInsertionOffset, 0,
FwdDeclInsertion.ForwardDeclText);
addOrMergeReplacement(Insertion, &NewReplacements);
}
// Add replacements referring to the changed code to existing replacements,
// which refers to the original code.
Replaces = Replaces.merge(NewReplacements);
format::FormatStyle Style =
format::getStyle("file", FilePath, FallbackStyle);
// Clean up old namespaces if there is nothing in it after moving.
auto CleanReplacements =
format::cleanupAroundReplacements(Code, Replaces, Style);
if (!CleanReplacements) {
llvm::errs() << llvm::toString(CleanReplacements.takeError()) << "\n";
continue;
}
FileToReplacements[FilePath] = *CleanReplacements;
}
// Make sure we don't generate replacements for files that do not match
// FilePattern.
for (auto &Entry : FileToReplacements)
if (!FilePatternRE.match(Entry.first))
Entry.second.clear();
}
} // namespace change_namespace
} // namespace clang

View File

@@ -1,172 +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,
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;
};
} // 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,114 +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"
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<std::string> Style("style",
cl::desc("The style name used for reformatting."),
cl::init("LLVM"), cl::cat(ChangeNamespaceCategory));
} // 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);
change_namespace::ChangeNamespaceTool NamespaceTool(
OldNamespace, NewNamespace, FilePattern, &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();
for (const auto &File : Files) {
const auto *Entry = FileMgr.getFile(File);
auto ID = Sources.getOrCreateFileID(Entry, SrcMgr::C_User);
// FIXME: print results in parsable format, e.g. JSON.
outs() << "============== " << File << " ==============\n";
Rewrite.getEditBuffer(ID).write(llvm::outs());
outs() << "\n============================================\n";
}
return 0;
}

View File

@@ -16,7 +16,6 @@
#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"
@@ -39,14 +38,12 @@ namespace replace {
typedef std::vector<clang::tooling::Range> RangeVector;
/// \brief Collection of TranslationUnitReplacements.
typedef std::vector<clang::tooling::TranslationUnitReplacements> TUReplacements;
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>>
@@ -62,20 +59,18 @@ typedef llvm::DenseMap<const clang::FileEntry *,
/// \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
/// TranslationUnitReplacements.
/// \param[out] TURFiles 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);
std::error_code
collectReplacementsFromDirectory(const llvm::StringRef Directory,
TUReplacements &TUs,
TUReplacementFiles &TURFiles,
clang::DiagnosticsEngine &Diagnostics);
/// \brief Deduplicate, check for conflicts, and apply all Replacements stored
/// in \c TUs. If conflicts occur, no Replacements are applied.
@@ -83,8 +78,7 @@ std::error_code collectReplacementsFromDirectory(
/// \post For all (key,value) in GroupedReplacements, value[i].getOffset() <=
/// value[i+1].getOffset().
///
/// \param[in] TUs Collection of TranslationUnitReplacements or
/// TranslationUnitDiagnostics to merge,
/// \param[in] TUs Collection of TranslationUnitReplacements to merge,
/// deduplicate, and test for conflicts.
/// \param[out] GroupedReplacements Container grouping all Replacements by the
/// file they target.
@@ -97,15 +91,6 @@ 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
@@ -125,7 +110,7 @@ bool applyReplacements(const FileToReplacementsMap &GroupedReplacements,
/// \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(

View File

@@ -20,7 +20,6 @@
#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"
@@ -31,58 +30,16 @@
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,
TUReplacements &TUs,
TUReplacementFiles & TURFiles,
clang::DiagnosticsEngine &Diagnostics) {
using namespace llvm::sys::fs;
using namespace llvm::sys::path;
@@ -100,7 +57,7 @@ collectReplacementsFromDirectory(const llvm::StringRef Directory,
if (extension(I->path()) != ".yaml")
continue;
TUFiles.push_back(I->path());
TURFiles.push_back(I->path());
ErrorOr<std::unique_ptr<MemoryBuffer>> Out =
MemoryBuffer::getFile(I->path());
@@ -111,7 +68,7 @@ collectReplacementsFromDirectory(const llvm::StringRef Directory,
}
yaml::Input YIn(Out.get()->getBuffer(), nullptr, &eatDiagnostics);
tooling::TranslationUnitDiagnostics TU;
tooling::TranslationUnitReplacements TU;
YIn >> TU;
if (YIn.error()) {
// File doesn't appear to be a header change description. Ignore it.
@@ -165,78 +122,6 @@ static void reportConflict(
}
}
// 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.
///
@@ -259,7 +144,7 @@ static bool deduplicateAndDetectConflicts(FileToReplacementsMap &Replacements,
assert(Entry != nullptr && "No file entry!");
std::vector<tooling::Range> Conflicts;
deduplicate(FileAndReplacements.second, Conflicts);
tooling::deduplicate(FileAndReplacements.second, Conflicts);
if (Conflicts.empty())
continue;
@@ -302,34 +187,6 @@ bool mergeAndDeduplicate(const TUReplacements &TUs,
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) {
@@ -340,7 +197,7 @@ bool applyReplacements(const FileToReplacementsMap &GroupedReplacements,
// 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))
if (!tooling::applyAllReplacements(FileAndReplacements.second, Rewrites))
return false;
}
@@ -365,9 +222,10 @@ RangeVector calculateChangedRanges(
bool writeFiles(const clang::Rewriter &Rewrites) {
for (auto BufferI = Rewrites.buffer_begin(), BufferE = Rewrites.buffer_end();
for (Rewriter::const_buffer_iterator BufferI = Rewrites.buffer_begin(),
BufferE = Rewrites.buffer_end();
BufferI != BufferE; ++BufferI) {
StringRef FileName =
const char *FileName =
Rewrites.getSourceMgr().getFileEntryForID(BufferI->first)->getName();
std::error_code EC;

View File

@@ -43,6 +43,7 @@ static cl::opt<bool> RemoveTUReplacementFiles(
"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"
@@ -62,11 +63,11 @@ static cl::opt<std::string> FormatStyleConfig(
cl::init(""), cl::cat(FormattingCategory));
static cl::opt<std::string>
FormatStyleOpt("style", cl::desc(format::StyleOptionHelpDescription),
cl::init("LLVM"), cl::cat(FormattingCategory));
FormatStyleOpt("style", cl::desc(format::StyleOptionHelpDescription),
cl::init("LLVM"), cl::cat(FormattingCategory));
namespace {
// Helper object to remove the TUReplacement and TUDiagnostic (triggered by
// Helper object to remove the TUReplacement files (triggered by
// "remove-change-desc-files" command line option) when exiting current scope.
class ScopedFileRemover {
public:
@@ -74,7 +75,9 @@ public:
clang::DiagnosticsEngine &Diagnostics)
: TURFiles(Files), Diag(Diagnostics) {}
~ScopedFileRemover() { deleteReplacementFiles(TURFiles, Diag); }
~ScopedFileRemover() {
deleteReplacementFiles(TURFiles, Diag);
}
private:
const TUReplacementFiles &TURFiles;
@@ -103,10 +106,9 @@ static void printVersion() {
static bool
getRewrittenData(const std::vector<tooling::Replacement> &Replacements,
Rewriter &Rewrites, std::string &Result) {
if (Replacements.empty())
return true;
if (Replacements.empty()) return true;
if (!applyAllReplacements(Replacements, Rewrites))
if (!tooling::applyAllReplacements(Replacements, Rewrites))
return false;
SourceManager &SM = Rewrites.getSourceMgr();
@@ -204,23 +206,19 @@ int main(int argc, char **argv) {
IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts(new DiagnosticOptions());
DiagnosticsEngine Diagnostics(
IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs()), DiagOpts.get());
IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs()),
DiagOpts.get());
// Determine a formatting style from options.
format::FormatStyle FormatStyle;
if (DoFormat)
FormatStyle = format::getStyle(FormatStyleOpt, FormatStyleConfig, "LLVM");
TUReplacements TURs;
TUReplacementFiles TUFiles;
TUReplacements TUs;
TUReplacementFiles TURFiles;
std::error_code ErrorCode =
collectReplacementsFromDirectory(Directory, TURs, TUFiles, Diagnostics);
TUDiagnostics TUDs;
TUFiles.clear();
ErrorCode =
collectReplacementsFromDirectory(Directory, TUDs, TUFiles, Diagnostics);
collectReplacementsFromDirectory(Directory, TUs, TURFiles, Diagnostics);
if (ErrorCode) {
errs() << "Trouble iterating over directory '" << Directory
@@ -232,15 +230,13 @@ int main(int argc, char **argv) {
// command line option) when exiting main().
std::unique_ptr<ScopedFileRemover> Remover;
if (RemoveTUReplacementFiles)
Remover.reset(new ScopedFileRemover(TUFiles, Diagnostics));
Remover.reset(new ScopedFileRemover(TURFiles, Diagnostics));
FileManager Files((FileSystemOptions()));
SourceManager SM(Diagnostics, Files);
FileToReplacementsMap GroupedReplacements;
if (!mergeAndDeduplicate(TURs, GroupedReplacements, SM))
return 1;
if (!mergeAndDeduplicate(TUDs, GroupedReplacements, SM))
if (!mergeAndDeduplicate(TUs, GroupedReplacements, SM))
return 1;
Rewriter ReplacementsRewriter(SM, LangOptions());
@@ -252,7 +248,7 @@ int main(int argc, char **argv) {
continue;
std::string NewFileData;
StringRef FileName = FileAndReplacements.first->getName();
const char *FileName = FileAndReplacements.first->getName();
if (!applyReplacements(FileAndReplacements.second, NewFileData,
Diagnostics)) {
errs() << "Failed to apply replacements to " << FileName << "\n";

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,883 +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_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 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 ForwardDecls =
cxxRecordDecl(unless(anyOf(isImplicit(), isDefinition())));
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(ForwardDecls), unless(namespaceDecl()),
unless(usingDirectiveDecl()), // using namespace decl.
unless(classTemplateDecl(has(ForwardDecls))), // template forward 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(ForwardDecls, 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)))
.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 DefinitionInOldCC = allOf(isDefinition(), unless(InMovedClass), InOldCC);
auto IsOldCCHelperDefinition =
allOf(DefinitionInOldCC, anyOf(isStaticStorageClass(), InAnonymousNS));
// Match helper classes separately with helper functions/variables since we
// want to reuse these matchers in finding helpers usage below.
auto HelperFuncOrVar = namedDecl(anyOf(functionDecl(IsOldCCHelperDefinition),
varDecl(IsOldCCHelperDefinition)));
auto HelperClasses = cxxRecordDecl(DefinitionInOldCC, 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());
// 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>("static_decls")) {
MovedDecls.push_back(ND);
} else if (const auto *ND =
Result.Nodes.getNodeAs<clang::NamedDecl>("helper_decls")) {
MovedDecls.push_back(ND);
HelperDeclarations.push_back(ND);
} 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) {
if (!UsedDecls.count(HelperDeclRGBuilder::getOutmostClassOrFunDecl(D))) {
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);
format::FormatStyle Style =
format::getStyle("file", FilePath, Context->FallbackStyle);
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)))
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::Function || Kind == Decl::Kind::FunctionTemplate)
Reporter->reportDeclaration(QualifiedName, "Function");
else if (Kind == Decl::Kind::ClassTemplate ||
Kind == Decl::Kind::CXXRecord)
Reporter->reportDeclaration(QualifiedName, "Class");
}
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:
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,128 +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 <vector>
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);
RG->addEdge(getOutmostClassOrFunDecl(DC->getCanonicalDecl()),
getOutmostClassOrFunDecl(FuncRef->getDecl()));
} else if (const auto *UsedClass =
Result.Nodes.getNodeAs<CXXRecordDecl>("used_class")) {
const auto *DC = Result.Nodes.getNodeAs<Decl>("dc");
assert(DC);
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,211 +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;
auto Factory = llvm::make_unique<clang::move::ClangMoveActionFactory>(
&Context, &Reporter);
int CodeStatus = Tool.run(Factory.get());
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

@@ -65,7 +65,7 @@ struct CollectBoundNodes : MatchFinder::MatchCallback {
}
};
} // namespace
} // namespace
bool MatchQuery::run(llvm::raw_ostream &OS, QuerySession &QS) const {
unsigned MatchCount = 0;
@@ -86,21 +86,25 @@ bool MatchQuery::run(llvm::raw_ostream &OS, QuerySession &QS) const {
}
Finder.matchAST(AST->getASTContext());
for (auto MI = Matches.begin(), ME = Matches.end(); MI != ME; ++MI) {
for (std::vector<BoundNodes>::iterator 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) {
for (BoundNodes::IDToNodeMap::const_iterator 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(R.getBegin(), DiagnosticsEngine::Note,
"\"" + BI->first + "\" binds here",
CharSourceRange::getTokenRange(R), None,
&AST->getSourceManager());
TD.emitDiagnostic(
R.getBegin(), DiagnosticsEngine::Note,
"\"" + BI->first + "\" binds here",
CharSourceRange::getTokenRange(R),
None, &AST->getSourceManager());
}
break;
}

View File

@@ -18,7 +18,11 @@
namespace clang {
namespace query {
enum OutputKind { OK_Diag, OK_Print, OK_Dump };
enum OutputKind {
OK_Diag,
OK_Print,
OK_Dump
};
enum QueryKind {
QK_Invalid,

View File

@@ -76,7 +76,9 @@ template <typename T> struct QueryParser::LexOrCompleteWord {
return *this;
}
T Default(const T &Value) const { return Switch.Default(Value); }
T Default(const T& Value) const {
return Switch.Default(Value);
}
};
// Lexes a word and stores it in Word. Returns a LexOrCompleteWord<T> object
@@ -99,9 +101,9 @@ QueryParser::lexOrCompleteWord(StringRef &Word) {
QueryRef QueryParser::parseSetBool(bool QuerySession::*Var) {
StringRef ValStr;
unsigned Value = lexOrCompleteWord<unsigned>(ValStr)
.Case("false", 0)
.Case("true", 1)
.Default(~0u);
.Case("false", 0)
.Case("true", 1)
.Default(~0u);
if (Value == ~0u) {
return new InvalidQuery("expected 'true' or 'false', got '" + ValStr + "'");
}
@@ -143,7 +145,11 @@ enum ParsedQueryKind {
PQK_Quit
};
enum ParsedQueryVariable { PQV_Invalid, PQV_Output, PQV_BindRoot };
enum ParsedQueryVariable {
PQV_Invalid,
PQV_Output,
PQV_BindRoot
};
QueryRef makeInvalidQueryFromDiagnostics(const Diagnostics &Diag) {
std::string ErrStr;
@@ -152,13 +158,15 @@ QueryRef makeInvalidQueryFromDiagnostics(const Diagnostics &Diag) {
return new InvalidQuery(OS.str());
}
} // namespace
} // 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) {
for (std::vector<MatcherCompletion>::iterator I = Comps.begin(),
E = Comps.end();
I != E; ++I) {
Completions.push_back(LineEditor::Completion(I->TypedText, I->MatcherDecl));
}
return QueryRef();

View File

@@ -37,7 +37,8 @@ public:
private:
QueryParser(StringRef Line, const QuerySession &QS)
: Begin(Line.begin()), End(Line.end()), CompletionPos(nullptr), QS(QS) {}
: Begin(Line.begin()), End(Line.end()),
CompletionPos(nullptr), QS(QS) {}
StringRef lexWord();

View File

@@ -77,13 +77,17 @@ int main(int argc, const char **argv) {
QuerySession QS(ASTs);
if (!Commands.empty()) {
for (auto I = Commands.begin(), E = Commands.end(); I != E; ++I) {
QueryRef Q = QueryParser::parse(*I, QS);
for (cl::list<std::string>::iterator I = Commands.begin(),
E = Commands.end();
I != E; ++I) {
QueryRef Q = QueryParser::parse(I->c_str(), QS);
if (!Q->run(llvm::outs(), QS))
return 1;
}
} else if (!CommandFiles.empty()) {
for (auto I = CommandFiles.begin(), E = CommandFiles.end(); I != E; ++I) {
for (cl::list<std::string>::iterator 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";
@@ -93,7 +97,7 @@ int main(int argc, const char **argv) {
std::string Line;
std::getline(Input, Line);
QueryRef Q = QueryParser::parse(Line, QS);
QueryRef Q = QueryParser::parse(Line.c_str(), QS);
if (!Q->run(llvm::outs(), QS))
return 1;
}

View File

@@ -8,7 +8,6 @@ add_clang_library(clangRename
LINK_LIBS
clangAST
clangASTMatchers
clangBasic
clangIndex
clangLex

View File

@@ -34,59 +34,54 @@ namespace rename {
class RenamingASTConsumer : public ASTConsumer {
public:
RenamingASTConsumer(
const std::vector<std::string> &NewNames,
const std::vector<std::string> &PrevNames,
const std::vector<std::vector<std::string>> &USRList,
std::map<std::string, tooling::Replacements> &FileToReplaces,
bool PrintLocations)
: NewNames(NewNames), PrevNames(PrevNames), USRList(USRList),
FileToReplaces(FileToReplaces), PrintLocations(PrintLocations) {}
void HandleTranslationUnit(ASTContext &Context) override {
for (unsigned I = 0; I < NewNames.size(); ++I)
HandleOneRename(Context, NewNames[I], PrevNames[I], USRList[I]);
RenamingASTConsumer(const std::string &NewName,
const std::string &PrevName,
const std::vector<std::string> &USRs,
tooling::Replacements &Replaces,
bool PrintLocations)
: NewName(NewName), PrevName(PrevName), USRs(USRs), Replaces(Replaces),
PrintLocations(PrintLocations) {
}
void HandleOneRename(ASTContext &Context, const std::string &NewName,
const std::string &PrevName,
const std::vector<std::string> &USRs) {
const SourceManager &SourceMgr = Context.getSourceManager();
void HandleTranslationUnit(ASTContext &Context) override {
const auto &SourceMgr = Context.getSourceManager();
std::vector<SourceLocation> RenamingCandidates;
std::vector<SourceLocation> NewCandidates;
NewCandidates =
getLocationsOfUSRs(USRs, PrevName, Context.getTranslationUnitDecl());
RenamingCandidates.insert(RenamingCandidates.end(), NewCandidates.begin(),
NewCandidates.end());
for (const auto &USR : USRs) {
NewCandidates = getLocationsOfUSR(USR, PrevName,
Context.getTranslationUnitDecl());
RenamingCandidates.insert(RenamingCandidates.end(), NewCandidates.begin(),
NewCandidates.end());
NewCandidates.clear();
}
unsigned PrevNameLen = PrevName.length();
for (const auto &Loc : RenamingCandidates) {
if (PrintLocations) {
auto PrevNameLen = PrevName.length();
if (PrintLocations)
for (const auto &Loc : RenamingCandidates) {
FullSourceLoc FullLoc(Loc, SourceMgr);
errs() << "clang-rename: renamed at: " << SourceMgr.getFilename(Loc)
<< ":" << FullLoc.getSpellingLineNumber() << ":"
<< FullLoc.getSpellingColumnNumber() << "\n";
Replaces.insert(tooling::Replacement(SourceMgr, Loc, PrevNameLen,
NewName));
}
// FIXME: better error handling.
tooling::Replacement Replace(SourceMgr, Loc, PrevNameLen, NewName);
llvm::Error Err = FileToReplaces[Replace.getFilePath()].add(Replace);
if (Err)
llvm::errs() << "Renaming failed in " << Replace.getFilePath() << "! "
<< llvm::toString(std::move(Err)) << "\n";
}
else
for (const auto &Loc : RenamingCandidates)
Replaces.insert(tooling::Replacement(SourceMgr, Loc, PrevNameLen,
NewName));
}
private:
const std::vector<std::string> &NewNames, &PrevNames;
const std::vector<std::vector<std::string>> &USRList;
std::map<std::string, tooling::Replacements> &FileToReplaces;
const std::string &NewName, &PrevName;
const std::vector<std::string> &USRs;
tooling::Replacements &Replaces;
bool PrintLocations;
};
std::unique_ptr<ASTConsumer> RenamingAction::newASTConsumer() {
return llvm::make_unique<RenamingASTConsumer>(NewNames, PrevNames, USRList,
FileToReplaces, PrintLocations);
return llvm::make_unique<RenamingASTConsumer>(NewName, PrevName, USRs,
Replaces, PrintLocations);
}
} // namespace rename

View File

@@ -12,8 +12,8 @@
///
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_RENAME_RENAMING_ACTION_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_RENAME_RENAMING_ACTION_H
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_RENAME_RENAMING_ACTION_H_
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_RENAME_RENAMING_ACTION_H_
#include "clang/Tooling/Refactoring.h"
@@ -25,24 +25,23 @@ namespace rename {
class RenamingAction {
public:
RenamingAction(const std::vector<std::string> &NewNames,
const std::vector<std::string> &PrevNames,
const std::vector<std::vector<std::string>> &USRList,
std::map<std::string, tooling::Replacements> &FileToReplaces,
bool PrintLocations = false)
: NewNames(NewNames), PrevNames(PrevNames), USRList(USRList),
FileToReplaces(FileToReplaces), PrintLocations(PrintLocations) {}
RenamingAction(const std::string &NewName, const std::string &PrevName,
const std::vector<std::string> &USRs,
tooling::Replacements &Replaces, bool PrintLocations = false)
: NewName(NewName), PrevName(PrevName), USRs(USRs), Replaces(Replaces),
PrintLocations(PrintLocations) {
}
std::unique_ptr<ASTConsumer> newASTConsumer();
private:
const std::vector<std::string> &NewNames, &PrevNames;
const std::vector<std::vector<std::string>> &USRList;
std::map<std::string, tooling::Replacements> &FileToReplaces;
const std::string &NewName, &PrevName;
const std::vector<std::string> &USRs;
tooling::Replacements &Replaces;
bool PrintLocations;
};
} // namespace rename
} // namespace clang
}
}
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_RENAME_RENAMING_ACTION_H
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_RENAME_RENAMING_ACTION_H_

View File

@@ -34,15 +34,19 @@ class NamedDeclFindingASTVisitor
public:
// \brief Finds the NamedDecl at a point in the source.
// \param Point the location in the source to search for the NamedDecl.
explicit NamedDeclFindingASTVisitor(const SourceLocation Point,
const ASTContext &Context)
: Result(nullptr), Point(Point), Context(Context) {}
explicit NamedDeclFindingASTVisitor(const SourceManager &SourceMgr,
const SourceLocation Point)
: Result(nullptr), SourceMgr(SourceMgr),
Point(Point) {
}
// \brief Finds the NamedDecl for a name in the source.
// \param Name the fully qualified name.
explicit NamedDeclFindingASTVisitor(const std::string &Name,
const ASTContext &Context)
: Result(nullptr), Name(Name), Context(Context) {}
explicit NamedDeclFindingASTVisitor(const SourceManager &SourceMgr,
const std::string &Name)
: Result(nullptr), SourceMgr(SourceMgr),
Name(Name) {
}
// Declaration visitors:
@@ -51,92 +55,63 @@ public:
// checking if the point lies within the length of the name of the declaration
// and the start location is sufficient.
bool VisitNamedDecl(const NamedDecl *Decl) {
return dyn_cast<CXXConversionDecl>(Decl)
? true
: setResult(Decl, Decl->getLocation(),
Decl->getNameAsString().length());
return setResult(Decl, Decl->getLocation(),
Decl->getNameAsString().length());
}
// Expression visitors:
bool VisitDeclRefExpr(const DeclRefExpr *Expr) {
const NamedDecl *Decl = Expr->getFoundDecl();
// Check the namespace specifier first.
if (!checkNestedNameSpecifierLoc(Expr->getQualifierLoc()))
return false;
const auto *Decl = Expr->getFoundDecl();
return setResult(Decl, Expr->getLocation(),
Decl->getNameAsString().length());
}
bool VisitMemberExpr(const MemberExpr *Expr) {
const NamedDecl *Decl = Expr->getFoundDecl().getDecl();
const auto *Decl = Expr->getFoundDecl().getDecl();
return setResult(Decl, Expr->getMemberLoc(),
Decl->getNameAsString().length());
}
// Other visitors:
// Other:
bool VisitTypeLoc(const TypeLoc Loc) {
const SourceLocation TypeBeginLoc = Loc.getBeginLoc();
const SourceLocation TypeEndLoc = Lexer::getLocForEndOfToken(
TypeBeginLoc, 0, Context.getSourceManager(), Context.getLangOpts());
if (const auto *TemplateTypeParm =
dyn_cast<TemplateTypeParmType>(Loc.getType()))
return setResult(TemplateTypeParm->getDecl(), TypeBeginLoc, TypeEndLoc);
if (const auto *TemplateSpecType =
dyn_cast<TemplateSpecializationType>(Loc.getType())) {
return setResult(TemplateSpecType->getTemplateName().getAsTemplateDecl(),
TypeBeginLoc, TypeEndLoc);
}
return setResult(Loc.getType()->getAsCXXRecordDecl(), TypeBeginLoc,
TypeEndLoc);
const NamedDecl *getNamedDecl() {
return Result;
}
bool VisitCXXConstructorDecl(clang::CXXConstructorDecl *ConstructorDecl) {
for (const auto *Initializer : ConstructorDecl->inits()) {
// Ignore implicit initializers.
if (!Initializer->isWritten())
continue;
if (const clang::FieldDecl *FieldDecl = Initializer->getMember()) {
const SourceLocation InitBeginLoc = Initializer->getSourceLocation(),
InitEndLoc = Lexer::getLocForEndOfToken(
InitBeginLoc, 0, Context.getSourceManager(),
Context.getLangOpts());
if (!setResult(FieldDecl, InitBeginLoc, InitEndLoc))
return false;
}
private:
// \brief Determines if a namespace qualifier contains the point.
// \returns false on success and sets Result.
bool checkNestedNameSpecifierLoc(NestedNameSpecifierLoc NameLoc) {
while (NameLoc) {
const auto *Decl = NameLoc.getNestedNameSpecifier()->getAsNamespace();
if (Decl && !setResult(Decl, NameLoc.getLocalBeginLoc(),
Decl->getNameAsString().length()))
return false;
NameLoc = NameLoc.getPrefix();
}
return true;
}
// Other:
const NamedDecl *getNamedDecl() { return Result; }
// \brief Determines if a namespace qualifier contains the point.
// \returns false on success and sets Result.
void handleNestedNameSpecifierLoc(NestedNameSpecifierLoc NameLoc) {
while (NameLoc) {
const NamespaceDecl *Decl =
NameLoc.getNestedNameSpecifier()->getAsNamespace();
setResult(Decl, NameLoc.getLocalBeginLoc(), NameLoc.getLocalEndLoc());
NameLoc = NameLoc.getPrefix();
}
}
private:
// \brief Sets Result to Decl if the Point is within Start and End.
// \returns false on success.
bool setResult(const NamedDecl *Decl, SourceLocation Start,
SourceLocation End) {
if (!Decl)
return true;
if (Name.empty()) {
// Offset is used to find the declaration.
if (!Start.isValid() || !Start.isFileID() || !End.isValid() ||
!End.isFileID() || !isPointWithin(Start, End))
!End.isFileID() || !isPointWithin(Start, End)) {
return true;
}
} else {
// Fully qualified name is used to find the declaration.
if (Name != Decl->getQualifiedNameAsString())
if (Name != Decl->getQualifiedNameAsString()) {
return true;
}
}
Result = Decl;
return false;
@@ -144,7 +119,8 @@ private:
// \brief Sets Result to Decl if Point is within Loc and Loc + Offset.
// \returns false on success.
bool setResult(const NamedDecl *Decl, SourceLocation Loc, unsigned Offset) {
bool setResult(const NamedDecl *Decl, SourceLocation Loc,
unsigned Offset) {
// FIXME: Add test for Offset == 0. Add test for Offset - 1 (vs -2 etc).
return Offset == 0 ||
setResult(Decl, Loc, Loc.getLocWithOffset(Offset - 1));
@@ -154,48 +130,55 @@ private:
bool isPointWithin(const SourceLocation Start, const SourceLocation End) {
// FIXME: Add tests for Point == End.
return Point == Start || Point == End ||
(Context.getSourceManager().isBeforeInTranslationUnit(Start,
Point) &&
Context.getSourceManager().isBeforeInTranslationUnit(Point, End));
(SourceMgr.isBeforeInTranslationUnit(Start, Point) &&
SourceMgr.isBeforeInTranslationUnit(Point, End));
}
const NamedDecl *Result;
const SourceManager &SourceMgr;
const SourceLocation Point; // The location to find the NamedDecl.
const std::string Name;
const ASTContext &Context;
};
} // namespace
const NamedDecl *getNamedDeclAt(const ASTContext &Context,
const SourceLocation Point) {
const SourceManager &SM = Context.getSourceManager();
NamedDeclFindingASTVisitor Visitor(Point, Context);
const auto &SourceMgr = Context.getSourceManager();
const auto SearchFile = SourceMgr.getFilename(Point);
// Try to be clever about pruning down the number of top-level declarations we
// see. If both start and end is either before or after the point we're
// looking for the point cannot be inside of this decl. Don't even look at it.
for (auto *CurrDecl : Context.getTranslationUnitDecl()->decls()) {
SourceLocation StartLoc = CurrDecl->getLocStart();
SourceLocation EndLoc = CurrDecl->getLocEnd();
if (StartLoc.isValid() && EndLoc.isValid() &&
SM.isBeforeInTranslationUnit(StartLoc, Point) !=
SM.isBeforeInTranslationUnit(EndLoc, Point))
NamedDeclFindingASTVisitor Visitor(SourceMgr, Point);
// We only want to search the decls that exist in the same file as the point.
auto Decls = Context.getTranslationUnitDecl()->decls();
for (auto &CurrDecl : Decls) {
const auto FileLoc = CurrDecl->getLocStart();
const auto FileName = SourceMgr.getFilename(FileLoc);
// FIXME: Add test.
if (FileName == SearchFile) {
Visitor.TraverseDecl(CurrDecl);
if (const NamedDecl *Result = Visitor.getNamedDecl()) {
return Result;
}
}
}
NestedNameSpecifierLocFinder Finder(const_cast<ASTContext &>(Context));
for (const auto &Location : Finder.getNestedNameSpecifierLocations())
Visitor.handleNestedNameSpecifierLoc(Location);
return Visitor.getNamedDecl();
return nullptr;
}
const NamedDecl *getNamedDeclFor(const ASTContext &Context,
const std::string &Name) {
NamedDeclFindingASTVisitor Visitor(Name, Context);
Visitor.TraverseDecl(Context.getTranslationUnitDecl());
const auto &SourceMgr = Context.getSourceManager();
NamedDeclFindingASTVisitor Visitor(SourceMgr, Name);
auto Decls = Context.getTranslationUnitDecl()->decls();
return Visitor.getNamedDecl();
for (auto &CurrDecl : Decls) {
Visitor.TraverseDecl(CurrDecl);
if (const NamedDecl *Result = Visitor.getNamedDecl()) {
return Result;
}
}
return nullptr;
}
std::string getUSRForDecl(const Decl *Decl) {

View File

@@ -12,21 +12,12 @@
/// code.
///
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_RENAME_USR_FINDER_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_RENAME_USR_FINDER_H
#include "clang/AST/AST.h"
#include "clang/AST/ASTContext.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include <string>
#include <vector>
using namespace llvm;
using namespace clang::ast_matchers;
namespace clang {
class ASTContext;
class Decl;
class SourceLocation;
@@ -48,37 +39,7 @@ const NamedDecl *getNamedDeclFor(const ASTContext &Context,
// Converts a Decl into a USR.
std::string getUSRForDecl(const Decl *Decl);
// FIXME: Implement RecursiveASTVisitor<T>::VisitNestedNameSpecifier instead.
class NestedNameSpecifierLocFinder : public MatchFinder::MatchCallback {
public:
explicit NestedNameSpecifierLocFinder(ASTContext &Context)
: Context(Context) {}
std::vector<NestedNameSpecifierLoc> getNestedNameSpecifierLocations() {
addMatchers();
Finder.matchAST(Context);
return Locations;
}
private:
void addMatchers() {
const auto NestedNameSpecifierLocMatcher =
nestedNameSpecifierLoc().bind("nestedNameSpecifierLoc");
Finder.addMatcher(NestedNameSpecifierLocMatcher, this);
}
void run(const MatchFinder::MatchResult &Result) override {
const auto *NNS = Result.Nodes.getNodeAs<NestedNameSpecifierLoc>(
"nestedNameSpecifierLoc");
Locations.push_back(*NNS);
}
ASTContext &Context;
std::vector<NestedNameSpecifierLoc> Locations;
MatchFinder Finder;
};
} // namespace rename
} // namespace clang
}
}
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_RENAME_USR_FINDER_H

View File

@@ -8,8 +8,7 @@
//===----------------------------------------------------------------------===//
///
/// \file
/// \brief Provides an action to find USR for the symbol at <offset>, as well as
/// all additional USRs.
/// \brief Provides an action to rename every symbol at a point.
///
//===----------------------------------------------------------------------===//
@@ -18,8 +17,6 @@
#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/Basic/FileManager.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Frontend/FrontendAction.h"
@@ -28,9 +25,6 @@
#include "clang/Tooling/CommonOptionsParser.h"
#include "clang/Tooling/Refactoring.h"
#include "clang/Tooling/Tooling.h"
#include <algorithm>
#include <set>
#include <string>
#include <vector>
@@ -39,187 +33,89 @@ using namespace llvm;
namespace clang {
namespace rename {
namespace {
// \brief NamedDeclFindingConsumer should delegate finding USRs of given Decl to
// AdditionalUSRFinder. AdditionalUSRFinder adds USRs of ctor and dtor if given
// Decl refers to class and adds USRs of all overridden methods if Decl refers
// to virtual method.
class AdditionalUSRFinder : public RecursiveASTVisitor<AdditionalUSRFinder> {
public:
AdditionalUSRFinder(const Decl *FoundDecl, ASTContext &Context)
: FoundDecl(FoundDecl), Context(Context) {}
// Get the USRs for the constructors of the class.
static std::vector<std::string> getAllConstructorUSRs(
const CXXRecordDecl *Decl) {
std::vector<std::string> USRs;
std::vector<std::string> Find() {
// Fill OverriddenMethods and PartialSpecs storages.
TraverseDecl(Context.getTranslationUnitDecl());
if (const auto *MethodDecl = dyn_cast<CXXMethodDecl>(FoundDecl)) {
addUSRsOfOverridenFunctions(MethodDecl);
for (const auto &OverriddenMethod : OverriddenMethods) {
if (checkIfOverriddenFunctionAscends(OverriddenMethod))
USRSet.insert(getUSRForDecl(OverriddenMethod));
}
} else if (const auto *RecordDecl = dyn_cast<CXXRecordDecl>(FoundDecl)) {
handleCXXRecordDecl(RecordDecl);
} else if (const auto *TemplateDecl =
dyn_cast<ClassTemplateDecl>(FoundDecl)) {
handleClassTemplateDecl(TemplateDecl);
// We need to get the definition of the record (as opposed to any forward
// declarations) in order to find the constructor and destructor.
const auto *RecordDecl = Decl->getDefinition();
// Iterate over all the constructors and add their USRs.
for (const auto *CtorDecl : RecordDecl->ctors())
USRs.push_back(getUSRForDecl(CtorDecl));
// Ignore destructors. GetLocationsOfUSR will find the declaration of and
// explicit calls to a destructor through TagTypeLoc (and it is better for the
// purpose of renaming).
//
// For example, in the following code segment,
// 1 class C {
// 2 ~C();
// 3 };
// At line 3, there is a NamedDecl starting from '~' and a TagTypeLoc starting
// from 'C'.
return USRs;
}
struct NamedDeclFindingConsumer : public ASTConsumer {
void HandleTranslationUnit(ASTContext &Context) override {
const auto &SourceMgr = Context.getSourceManager();
// The file we look for the USR in will always be the main source file.
const auto Point = SourceMgr.getLocForStartOfFile(
SourceMgr.getMainFileID()).getLocWithOffset(SymbolOffset);
if (!Point.isValid())
return;
const NamedDecl *FoundDecl = nullptr;
if (OldName.empty()) {
FoundDecl = getNamedDeclAt(Context, Point);
} else {
USRSet.insert(getUSRForDecl(FoundDecl));
FoundDecl = getNamedDeclFor(Context, OldName);
}
return std::vector<std::string>(USRSet.begin(), USRSet.end());
}
bool VisitCXXMethodDecl(const CXXMethodDecl *MethodDecl) {
if (MethodDecl->isVirtual())
OverriddenMethods.push_back(MethodDecl);
return true;
}
bool VisitClassTemplatePartialSpecializationDecl(
const ClassTemplatePartialSpecializationDecl *PartialSpec) {
PartialSpecs.push_back(PartialSpec);
return true;
}
private:
void handleCXXRecordDecl(const CXXRecordDecl *RecordDecl) {
RecordDecl = RecordDecl->getDefinition();
if (const auto *ClassTemplateSpecDecl =
dyn_cast<ClassTemplateSpecializationDecl>(RecordDecl))
handleClassTemplateDecl(ClassTemplateSpecDecl->getSpecializedTemplate());
addUSRsOfCtorDtors(RecordDecl);
}
void handleClassTemplateDecl(const ClassTemplateDecl *TemplateDecl) {
for (const auto *Specialization : TemplateDecl->specializations())
addUSRsOfCtorDtors(Specialization);
for (const auto *PartialSpec : PartialSpecs) {
if (PartialSpec->getSpecializedTemplate() == TemplateDecl)
addUSRsOfCtorDtors(PartialSpec);
}
addUSRsOfCtorDtors(TemplateDecl->getTemplatedDecl());
}
void addUSRsOfCtorDtors(const CXXRecordDecl *RecordDecl) {
RecordDecl = RecordDecl->getDefinition();
for (const auto *CtorDecl : RecordDecl->ctors())
USRSet.insert(getUSRForDecl(CtorDecl));
USRSet.insert(getUSRForDecl(RecordDecl->getDestructor()));
USRSet.insert(getUSRForDecl(RecordDecl));
}
void addUSRsOfOverridenFunctions(const CXXMethodDecl *MethodDecl) {
USRSet.insert(getUSRForDecl(MethodDecl));
// Recursively visit each OverridenMethod.
for (const auto &OverriddenMethod : MethodDecl->overridden_methods())
addUSRsOfOverridenFunctions(OverriddenMethod);
}
bool checkIfOverriddenFunctionAscends(const CXXMethodDecl *MethodDecl) {
for (const auto &OverriddenMethod : MethodDecl->overridden_methods()) {
if (USRSet.find(getUSRForDecl(OverriddenMethod)) != USRSet.end())
return true;
return checkIfOverriddenFunctionAscends(OverriddenMethod);
}
return false;
}
const Decl *FoundDecl;
ASTContext &Context;
std::set<std::string> USRSet;
std::vector<const CXXMethodDecl *> OverriddenMethods;
std::vector<const ClassTemplatePartialSpecializationDecl *> PartialSpecs;
};
} // namespace
class NamedDeclFindingConsumer : public ASTConsumer {
public:
NamedDeclFindingConsumer(ArrayRef<unsigned> SymbolOffsets,
ArrayRef<std::string> QualifiedNames,
std::vector<std::string> &SpellingNames,
std::vector<std::vector<std::string>> &USRList,
bool &ErrorOccurred)
: SymbolOffsets(SymbolOffsets), QualifiedNames(QualifiedNames),
SpellingNames(SpellingNames), USRList(USRList),
ErrorOccurred(ErrorOccurred) {}
private:
bool FindSymbol(ASTContext &Context, const SourceManager &SourceMgr,
unsigned SymbolOffset, const std::string &QualifiedName) {
DiagnosticsEngine &Engine = Context.getDiagnostics();
const FileID MainFileID = SourceMgr.getMainFileID();
if (SymbolOffset >= SourceMgr.getFileIDSize(MainFileID)) {
ErrorOccurred = true;
unsigned InvalidOffset = Engine.getCustomDiagID(
DiagnosticsEngine::Error,
"SourceLocation in file %0 at offset %1 is invalid");
Engine.Report(SourceLocation(), InvalidOffset)
<< SourceMgr.getFileEntryForID(MainFileID)->getName() << SymbolOffset;
return false;
}
const SourceLocation Point = SourceMgr.getLocForStartOfFile(MainFileID)
.getLocWithOffset(SymbolOffset);
const NamedDecl *FoundDecl = QualifiedName.empty()
? getNamedDeclAt(Context, Point)
: getNamedDeclFor(Context, QualifiedName);
if (FoundDecl == nullptr) {
if (QualifiedName.empty()) {
FullSourceLoc FullLoc(Point, SourceMgr);
unsigned CouldNotFindSymbolAt = Engine.getCustomDiagID(
DiagnosticsEngine::Error,
"clang-rename could not find symbol (offset %0)");
Engine.Report(Point, CouldNotFindSymbolAt) << SymbolOffset;
ErrorOccurred = true;
return false;
}
unsigned CouldNotFindSymbolNamed = Engine.getCustomDiagID(
DiagnosticsEngine::Error, "clang-rename could not find symbol %0");
Engine.Report(CouldNotFindSymbolNamed) << QualifiedName;
ErrorOccurred = true;
return false;
FullSourceLoc FullLoc(Point, SourceMgr);
errs() << "clang-rename: could not find symbol at "
<< SourceMgr.getFilename(Point) << ":"
<< FullLoc.getSpellingLineNumber() << ":"
<< FullLoc.getSpellingColumnNumber() << " (offset " << SymbolOffset
<< ").\n";
return;
}
// If FoundDecl is a constructor or destructor, we want to instead take
// the Decl of the corresponding class.
// If the decl is a constructor or destructor, we want to instead take the
// decl of the parent record.
if (const auto *CtorDecl = dyn_cast<CXXConstructorDecl>(FoundDecl))
FoundDecl = CtorDecl->getParent();
else if (const auto *DtorDecl = dyn_cast<CXXDestructorDecl>(FoundDecl))
FoundDecl = DtorDecl->getParent();
SpellingNames.push_back(FoundDecl->getNameAsString());
AdditionalUSRFinder Finder(FoundDecl, Context);
USRList.push_back(Finder.Find());
return true;
// If the decl is in any way relatedpp to a class, we want to make sure we
// search for the constructor and destructor as well as everything else.
if (const auto *Record = dyn_cast<CXXRecordDecl>(FoundDecl))
*USRs = getAllConstructorUSRs(Record);
USRs->push_back(getUSRForDecl(FoundDecl));
*SpellingName = FoundDecl->getNameAsString();
}
void HandleTranslationUnit(ASTContext &Context) override {
const SourceManager &SourceMgr = Context.getSourceManager();
for (unsigned Offset : SymbolOffsets) {
if (!FindSymbol(Context, SourceMgr, Offset, ""))
return;
}
for (const std::string &QualifiedName : QualifiedNames) {
if (!FindSymbol(Context, SourceMgr, 0, QualifiedName))
return;
}
}
ArrayRef<unsigned> SymbolOffsets;
ArrayRef<std::string> QualifiedNames;
std::vector<std::string> &SpellingNames;
std::vector<std::vector<std::string>> &USRList;
bool &ErrorOccurred;
unsigned SymbolOffset;
std::string OldName;
std::string *SpellingName;
std::vector<std::string> *USRs;
};
std::unique_ptr<ASTConsumer> USRFindingAction::newASTConsumer() {
return llvm::make_unique<NamedDeclFindingConsumer>(
SymbolOffsets, QualifiedNames, SpellingNames, USRList, ErrorOccurred);
std::unique_ptr<ASTConsumer>
USRFindingAction::newASTConsumer() {
std::unique_ptr<NamedDeclFindingConsumer> Consumer(
new NamedDeclFindingConsumer);
SpellingName = "";
Consumer->SymbolOffset = SymbolOffset;
Consumer->OldName = OldName;
Consumer->USRs = &USRs;
Consumer->SpellingName = &SpellingName;
return std::move(Consumer);
}
} // namespace rename

View File

@@ -12,14 +12,10 @@
///
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_RENAME_USR_FINDING_ACTION_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_RENAME_USR_FINDING_ACTION_H
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_RENAME_USR_FINDING_ACTION_H_
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_RENAME_USR_FINDING_ACTION_H_
#include "clang/Basic/LLVM.h"
#include "llvm/ADT/ArrayRef.h"
#include <string>
#include <vector>
#include "clang/Frontend/FrontendAction.h"
namespace clang {
class ASTConsumer;
@@ -29,25 +25,23 @@ class NamedDecl;
namespace rename {
struct USRFindingAction {
USRFindingAction(ArrayRef<unsigned> SymbolOffsets,
ArrayRef<std::string> QualifiedNames)
: SymbolOffsets(SymbolOffsets), QualifiedNames(QualifiedNames),
ErrorOccurred(false) {}
USRFindingAction(unsigned Offset, const std::string &Name)
: SymbolOffset(Offset), OldName(Name) {}
std::unique_ptr<ASTConsumer> newASTConsumer();
ArrayRef<std::string> getUSRSpellings() { return SpellingNames; }
ArrayRef<std::vector<std::string>> getUSRList() { return USRList; }
bool errorOccurred() { return ErrorOccurred; }
// \brief get the spelling of the USR(s) as it would appear in source files.
const std::string &getUSRSpelling() { return SpellingName; }
const std::vector<std::string> &getUSRs() { return USRs; }
private:
std::vector<unsigned> SymbolOffsets;
std::vector<std::string> QualifiedNames;
std::vector<std::string> SpellingNames;
std::vector<std::vector<std::string>> USRList;
bool ErrorOccurred;
unsigned SymbolOffset;
std::string OldName;
std::string SpellingName;
std::vector<std::string> USRs;
};
} // namespace rename
} // namespace clang
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_RENAME_USR_FINDING_ACTION_H
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_RENAME_USR_FINDING_ACTION_H_

View File

@@ -18,16 +18,10 @@
#include "USRFinder.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/RecursiveASTVisitor.h"
#include "clang/Basic/LLVM.h"
#include "clang/Basic/SourceLocation.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Index/USRGeneration.h"
#include "clang/Lex/Lexer.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/Casting.h"
#include <cstddef>
#include <set>
#include <string>
#include <vector>
#include "llvm/ADT/SmallVector.h"
using namespace llvm;
@@ -35,78 +29,140 @@ namespace clang {
namespace rename {
namespace {
// \brief This visitor recursively searches for all instances of a USR in a
// translation unit and stores them for later usage.
class USRLocFindingASTVisitor
: public clang::RecursiveASTVisitor<USRLocFindingASTVisitor> {
public:
explicit USRLocFindingASTVisitor(const std::vector<std::string> &USRs,
StringRef PrevName,
const ASTContext &Context)
: USRSet(USRs.begin(), USRs.end()), PrevName(PrevName), Context(Context) {
}
explicit USRLocFindingASTVisitor(StringRef USR, StringRef PrevName)
: USR(USR), PrevName(PrevName) {}
// Declaration visitors:
bool VisitCXXConstructorDecl(clang::CXXConstructorDecl *ConstructorDecl) {
for (const auto *Initializer : ConstructorDecl->inits()) {
// Ignore implicit initializers.
if (!Initializer->isWritten())
continue;
if (const clang::FieldDecl *FieldDecl = Initializer->getMember()) {
if (USRSet.find(getUSRForDecl(FieldDecl)) != USRSet.end())
LocationsFound.push_back(Initializer->getSourceLocation());
bool VisitNamedDecl(const NamedDecl *Decl) {
if (getUSRForDecl(Decl) == USR) {
LocationsFound.push_back(Decl->getLocation());
}
return true;
}
bool VisitVarDecl(clang::VarDecl *Decl) {
clang::QualType Type = Decl->getType();
const clang::RecordDecl *RecordDecl = Type->getPointeeCXXRecordDecl();
if (RecordDecl) {
if (getUSRForDecl(RecordDecl) == USR) {
// The declaration refers to a type that is to be renamed.
LocationsFound.push_back(Decl->getTypeSpecStartLoc());
}
}
return true;
}
bool VisitNamedDecl(const NamedDecl *Decl) {
if (USRSet.find(getUSRForDecl(Decl)) != USRSet.end())
checkAndAddLocation(Decl->getLocation());
bool VisitCXXConstructorDecl(clang::CXXConstructorDecl *ConstructorDecl) {
const ASTContext &Context = ConstructorDecl->getASTContext();
for (auto &Initializer : ConstructorDecl->inits()) {
if (Initializer->getSourceOrder() == -1) {
// Ignore implicit initializers.
continue;
}
if (const clang::FieldDecl *FieldDecl = Initializer->getAnyMember()) {
if (getUSRForDecl(FieldDecl) == USR) {
// The initializer refers to a field that is to be renamed.
SourceLocation Location = Initializer->getSourceLocation();
StringRef TokenName = Lexer::getSourceText(
CharSourceRange::getTokenRange(Location),
Context.getSourceManager(), Context.getLangOpts());
if (TokenName == PrevName) {
// The token of the source location we find actually has the old
// name.
LocationsFound.push_back(Initializer->getSourceLocation());
}
}
}
}
if (getUSRForDecl(ConstructorDecl) == USR) {
// This takes care of the class name part of a non-inline ctor definition.
LocationsFound.push_back(ConstructorDecl->getLocStart());
}
return true;
}
bool VisitCXXDestructorDecl(clang::CXXDestructorDecl *DestructorDecl) {
if (getUSRForDecl(DestructorDecl->getParent()) == USR) {
// Handles "~Foo" from "Foo::~Foo".
SourceLocation Location = DestructorDecl->getLocation();
const ASTContext &Context = DestructorDecl->getASTContext();
StringRef LLVM_ATTRIBUTE_UNUSED TokenName = Lexer::getSourceText(
CharSourceRange::getTokenRange(Location), Context.getSourceManager(),
Context.getLangOpts());
// 1 is the length of the "~" string that is not to be touched by the
// rename.
assert(TokenName.startswith("~"));
LocationsFound.push_back(Location.getLocWithOffset(1));
if (DestructorDecl->isThisDeclarationADefinition()) {
// Handles "Foo" from "Foo::~Foo".
LocationsFound.push_back(DestructorDecl->getLocStart());
}
}
return true;
}
// Expression visitors:
bool VisitDeclRefExpr(const DeclRefExpr *Expr) {
const NamedDecl *Decl = Expr->getFoundDecl();
const auto *Decl = Expr->getFoundDecl();
if (USRSet.find(getUSRForDecl(Decl)) != USRSet.end()) {
checkNestedNameSpecifierLoc(Expr->getQualifierLoc());
if (getUSRForDecl(Decl) == USR) {
const SourceManager &Manager = Decl->getASTContext().getSourceManager();
SourceLocation Location = Manager.getSpellingLoc(Expr->getLocation());
checkAndAddLocation(Location);
LocationsFound.push_back(Location);
}
return true;
}
bool VisitMemberExpr(const MemberExpr *Expr) {
const NamedDecl *Decl = Expr->getFoundDecl().getDecl();
if (USRSet.find(getUSRForDecl(Decl)) != USRSet.end()) {
const auto *Decl = Expr->getFoundDecl().getDecl();
if (getUSRForDecl(Decl) == USR) {
const SourceManager &Manager = Decl->getASTContext().getSourceManager();
SourceLocation Location = Manager.getSpellingLoc(Expr->getMemberLoc());
checkAndAddLocation(Location);
LocationsFound.push_back(Location);
}
return true;
}
// Other visitors:
bool VisitCXXConstructExpr(const CXXConstructExpr *Expr) {
CXXConstructorDecl *Decl = Expr->getConstructor();
bool VisitTypeLoc(const TypeLoc Loc) {
if (USRSet.find(getUSRForDecl(Loc.getType()->getAsCXXRecordDecl())) !=
USRSet.end())
checkAndAddLocation(Loc.getBeginLoc());
if (const auto *TemplateTypeParm =
dyn_cast<TemplateTypeParmType>(Loc.getType())) {
if (USRSet.find(getUSRForDecl(TemplateTypeParm->getDecl())) !=
USRSet.end())
checkAndAddLocation(Loc.getBeginLoc());
if (getUSRForDecl(Decl) == USR) {
// This takes care of 'new <name>' expressions.
LocationsFound.push_back(Expr->getLocation());
}
return true;
}
bool VisitCXXStaticCastExpr(clang::CXXStaticCastExpr *Expr) {
return handleCXXNamedCastExpr(Expr);
}
bool VisitCXXDynamicCastExpr(clang::CXXDynamicCastExpr *Expr) {
return handleCXXNamedCastExpr(Expr);
}
bool VisitCXXReinterpretCastExpr(clang::CXXReinterpretCastExpr *Expr) {
return handleCXXNamedCastExpr(Expr);
}
bool VisitCXXConstCastExpr(clang::CXXConstCastExpr *Expr) {
return handleCXXNamedCastExpr(Expr);
}
// Non-visitors:
// \brief Returns a list of unique locations. Duplicate or overlapping
@@ -115,51 +171,48 @@ public:
return LocationsFound;
}
private:
// Namespace traversal:
void handleNestedNameSpecifierLoc(NestedNameSpecifierLoc NameLoc) {
void checkNestedNameSpecifierLoc(NestedNameSpecifierLoc NameLoc) {
while (NameLoc) {
const NamespaceDecl *Decl =
NameLoc.getNestedNameSpecifier()->getAsNamespace();
if (Decl && USRSet.find(getUSRForDecl(Decl)) != USRSet.end())
checkAndAddLocation(NameLoc.getLocalBeginLoc());
const auto *Decl = NameLoc.getNestedNameSpecifier()->getAsNamespace();
if (Decl && getUSRForDecl(Decl) == USR)
LocationsFound.push_back(NameLoc.getLocalBeginLoc());
NameLoc = NameLoc.getPrefix();
}
}
private:
void checkAndAddLocation(SourceLocation Loc) {
const SourceLocation BeginLoc = Loc;
const SourceLocation EndLoc = Lexer::getLocForEndOfToken(
BeginLoc, 0, Context.getSourceManager(), Context.getLangOpts());
StringRef TokenName =
Lexer::getSourceText(CharSourceRange::getTokenRange(BeginLoc, EndLoc),
Context.getSourceManager(), Context.getLangOpts());
size_t Offset = TokenName.find(PrevName);
bool handleCXXNamedCastExpr(clang::CXXNamedCastExpr *Expr) {
clang::QualType Type = Expr->getType();
// See if this a cast of a pointer.
const RecordDecl *Decl = Type->getPointeeCXXRecordDecl();
if (!Decl) {
// See if this is a cast of a reference.
Decl = Type->getAsCXXRecordDecl();
}
// The token of the source location we find actually has the old
// name.
if (Offset != StringRef::npos)
LocationsFound.push_back(BeginLoc.getLocWithOffset(Offset));
if (Decl && getUSRForDecl(Decl) == USR) {
SourceLocation Location =
Expr->getTypeInfoAsWritten()->getTypeLoc().getBeginLoc();
LocationsFound.push_back(Location);
}
return true;
}
const std::set<std::string> USRSet;
// All the locations of the USR were found.
const std::string USR;
// Old name that is renamed.
const std::string PrevName;
std::vector<clang::SourceLocation> LocationsFound;
const ASTContext &Context;
};
} // namespace
std::vector<SourceLocation>
getLocationsOfUSRs(const std::vector<std::string> &USRs, StringRef PrevName,
Decl *Decl) {
USRLocFindingASTVisitor Visitor(USRs, PrevName, Decl->getASTContext());
std::vector<SourceLocation> getLocationsOfUSR(StringRef USR, StringRef PrevName,
Decl *Decl) {
USRLocFindingASTVisitor Visitor(USR, PrevName);
Visitor.TraverseDecl(Decl);
NestedNameSpecifierLocFinder Finder(Decl->getASTContext());
for (const auto &Location : Finder.getNestedNameSpecifierLocations())
Visitor.handleNestedNameSpecifierLoc(Location);
return Visitor.getLocationsFound();
}

View File

@@ -26,8 +26,7 @@ namespace rename {
// FIXME: make this an AST matcher. Wouldn't that be awesome??? I agree!
std::vector<SourceLocation>
getLocationsOfUSRs(const std::vector<std::string> &USRs,
llvm::StringRef PrevName, Decl *Decl);
getLocationsOfUSR(llvm::StringRef USR, llvm::StringRef PrevName, Decl *Decl);
} // namespace rename
} // namespace clang

View File

@@ -10,10 +10,3 @@ target_link_libraries(clang-rename
)
install(TARGETS clang-rename RUNTIME DESTINATION bin)
install(PROGRAMS clang-rename.py
DESTINATION share/clang
COMPONENT clang-rename)
install(PROGRAMS clang-rename.el
DESTINATION share/clang
COMPONENT clang-rename)

View File

@@ -13,171 +13,119 @@
///
//===----------------------------------------------------------------------===//
#include "../RenamingAction.h"
#include "../USRFindingAction.h"
#include "clang/Basic/Diagnostic.h"
#include "clang/Basic/DiagnosticOptions.h"
#include "../RenamingAction.h"
#include "clang/AST/ASTConsumer.h"
#include "clang/AST/ASTContext.h"
#include "clang/Basic/FileManager.h"
#include "clang/Basic/IdentifierTable.h"
#include "clang/Basic/LangOptions.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Basic/TokenKinds.h"
#include "clang/Basic/TargetInfo.h"
#include "clang/Basic/TargetOptions.h"
#include "clang/Frontend/CommandLineSourceLoc.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Frontend/FrontendAction.h"
#include "clang/Frontend/TextDiagnosticPrinter.h"
#include "clang/Lex/Lexer.h"
#include "clang/Lex/Preprocessor.h"
#include "clang/Parse/ParseAST.h"
#include "clang/Parse/Parser.h"
#include "clang/Rewrite/Core/Rewriter.h"
#include "clang/Tooling/CommonOptionsParser.h"
#include "clang/Tooling/Refactoring.h"
#include "clang/Tooling/ReplacementsYaml.h"
#include "clang/Tooling/Tooling.h"
#include "llvm/ADT/IntrusiveRefCntPtr.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/YAMLTraits.h"
#include "llvm/Support/raw_ostream.h"
#include <cstdlib>
#include "llvm/Support/Host.h"
#include <string>
#include <system_error>
using namespace llvm;
using namespace clang;
/// \brief An oldname -> newname rename.
struct RenameAllInfo {
unsigned Offset = 0;
std::string QualifiedName;
std::string NewName;
};
cl::OptionCategory ClangRenameCategory("Clang-rename options");
LLVM_YAML_IS_SEQUENCE_VECTOR(RenameAllInfo)
namespace llvm {
namespace yaml {
/// \brief Specialized MappingTraits to describe how a RenameAllInfo is
/// (de)serialized.
template <> struct MappingTraits<RenameAllInfo> {
static void mapping(IO &IO, RenameAllInfo &Info) {
IO.mapOptional("Offset", Info.Offset);
IO.mapOptional("QualifiedName", Info.QualifiedName);
IO.mapRequired("NewName", Info.NewName);
}
};
} // end namespace yaml
} // end namespace llvm
static cl::OptionCategory ClangRenameOptions("clang-rename common options");
static cl::list<unsigned> SymbolOffsets(
static cl::opt<std::string>
NewName(
"new-name",
cl::desc("The new name to change the symbol to."),
cl::cat(ClangRenameCategory));
static cl::opt<unsigned>
SymbolOffset(
"offset",
cl::desc("Locates the symbol by offset as opposed to <line>:<column>."),
cl::ZeroOrMore, cl::cat(ClangRenameOptions));
static cl::opt<bool> Inplace("i", cl::desc("Overwrite edited <file>s."),
cl::cat(ClangRenameOptions));
static cl::list<std::string>
QualifiedNames("qualified-name",
cl::desc("The fully qualified name of the symbol."),
cl::ZeroOrMore, cl::cat(ClangRenameOptions));
static cl::list<std::string>
NewNames("new-name", cl::desc("The new name to change the symbol to."),
cl::ZeroOrMore, cl::cat(ClangRenameOptions));
static cl::opt<bool> PrintName(
cl::cat(ClangRenameCategory));
static cl::opt<std::string>
OldName(
"old-name",
cl::desc("The fully qualified name of the symbol, if -offset is not used."),
cl::cat(ClangRenameCategory));
static cl::opt<bool>
Inplace(
"i",
cl::desc("Overwrite edited <file>s."),
cl::cat(ClangRenameCategory));
static cl::opt<bool>
PrintName(
"pn",
cl::desc("Print the found symbol's name prior to renaming to stderr."),
cl::cat(ClangRenameOptions));
static cl::opt<bool> PrintLocations(
"pl", cl::desc("Print the locations affected by renaming to stderr."),
cl::cat(ClangRenameOptions));
cl::cat(ClangRenameCategory));
static cl::opt<bool>
PrintLocations(
"pl",
cl::desc("Print the locations affected by renaming to stderr."),
cl::cat(ClangRenameCategory));
static cl::opt<std::string>
ExportFixes("export-fixes",
cl::desc("YAML file to store suggested fixes in."),
cl::value_desc("filename"), cl::cat(ClangRenameOptions));
static cl::opt<std::string>
Input("input", cl::desc("YAML file to load oldname-newname pairs from."),
cl::Optional, cl::cat(ClangRenameOptions));
ExportFixes(
"export-fixes",
cl::desc("YAML file to store suggested fixes in."),
cl::value_desc("filename"),
cl::cat(ClangRenameCategory));
#define CLANG_RENAME_VERSION "0.0.1"
static void PrintVersion() {
outs() << "clang-rename version " << CLANG_RENAME_VERSION << '\n';
}
using namespace clang;
const char RenameUsage[] = "A tool to rename symbols in C/C++ code.\n\
clang-rename renames every occurrence of a symbol found at <offset> in\n\
<source0>. If -i is specified, the edited files are overwritten to disk.\n\
Otherwise, the results are written to stdout.\n";
int main(int argc, const char **argv) {
tooling::CommonOptionsParser OP(argc, argv, ClangRenameOptions);
if (!Input.empty()) {
// Populate QualifiedNames and NewNames from a YAML file.
ErrorOr<std::unique_ptr<MemoryBuffer>> Buffer =
llvm::MemoryBuffer::getFile(Input);
if (!Buffer) {
errs() << "clang-rename: failed to read " << Input << ": "
<< Buffer.getError().message() << "\n";
return 1;
}
std::vector<RenameAllInfo> Infos;
llvm::yaml::Input YAML(Buffer.get()->getBuffer());
YAML >> Infos;
for (const auto &Info : Infos) {
if (!Info.QualifiedName.empty())
QualifiedNames.push_back(Info.QualifiedName);
else
SymbolOffsets.push_back(Info.Offset);
NewNames.push_back(Info.NewName);
}
}
cl::SetVersionPrinter(PrintVersion);
tooling::CommonOptionsParser OP(argc, argv, ClangRenameCategory, RenameUsage);
// Check the arguments for correctness.
if (NewNames.empty()) {
errs() << "clang-rename: -new-name must be specified.\n\n";
exit(1);
}
if (SymbolOffsets.empty() == QualifiedNames.empty()) {
errs() << "clang-rename: -offset and -qualified-name can't be present at "
"the same time.\n";
exit(1);
}
// Check if NewNames is a valid identifier in C++17.
LangOptions Options;
Options.CPlusPlus = true;
Options.CPlusPlus1z = true;
IdentifierTable Table(Options);
for (const auto &NewName : NewNames) {
auto NewNameTokKind = Table.get(NewName).getTokenID();
if (!tok::isAnyIdentifier(NewNameTokKind)) {
errs() << "ERROR: new name is not a valid identifier in C++17.\n\n";
exit(1);
}
}
if (SymbolOffsets.size() + QualifiedNames.size() != NewNames.size()) {
errs() << "clang-rename: number of symbol offsets(" << SymbolOffsets.size()
<< ") + number of qualified names (" << QualifiedNames.size()
<< ") must be equal to number of new names(" << NewNames.size()
<< ").\n\n";
cl::PrintHelpMessage();
if (NewName.empty()) {
errs() << "clang-rename: no new name provided.\n\n";
exit(1);
}
// Get the USRs.
auto Files = OP.getSourcePathList();
tooling::RefactoringTool Tool(OP.getCompilations(), Files);
rename::USRFindingAction FindingAction(SymbolOffsets, QualifiedNames);
Tool.run(tooling::newFrontendActionFactory(&FindingAction).get());
const std::vector<std::vector<std::string>> &USRList =
FindingAction.getUSRList();
const std::vector<std::string> &PrevNames = FindingAction.getUSRSpellings();
if (PrintName) {
for (const auto &PrevName : PrevNames) {
outs() << "clang-rename found name: " << PrevName << '\n';
}
rename::USRFindingAction USRAction(SymbolOffset, OldName);
// Find the USRs.
Tool.run(tooling::newFrontendActionFactory(&USRAction).get());
const auto &USRs = USRAction.getUSRs();
const auto &PrevName = USRAction.getUSRSpelling();
if (PrevName.empty()) {
// An error should have already been printed.
exit(1);
}
if (FindingAction.errorOccurred()) {
// Diagnostics are already issued at this point.
exit(1);
if (PrintName) {
errs() << "clang-rename: found name: " << PrevName << '\n';
}
// Perform the renaming.
rename::RenamingAction RenameAction(NewNames, PrevNames, USRList,
rename::RenamingAction RenameAction(NewName, PrevName, USRs,
Tool.getReplacements(), PrintLocations);
std::unique_ptr<tooling::FrontendActionFactory> Factory =
tooling::newFrontendActionFactory(&RenameAction);
auto Factory = tooling::newFrontendActionFactory(&RenameAction);
int ExitCode;
if (Inplace) {
@@ -195,10 +143,9 @@ int main(int argc, const char **argv) {
// Export replacements.
tooling::TranslationUnitReplacements TUR;
const auto &FileToReplacements = Tool.getReplacements();
for (const auto &Entry : FileToReplacements)
TUR.Replacements.insert(TUR.Replacements.end(), Entry.second.begin(),
Entry.second.end());
const tooling::Replacements &Replacements = Tool.getReplacements();
TUR.Replacements.insert(TUR.Replacements.end(), Replacements.begin(),
Replacements.end());
yaml::Output YAML(OS);
YAML << TUR;
@@ -210,11 +157,12 @@ int main(int argc, const char **argv) {
// indication of which files start where, other than that we print the files
// in the same order we see them.
LangOptions DefaultLangOptions;
IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions();
IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts =
new DiagnosticOptions();
TextDiagnosticPrinter DiagnosticPrinter(errs(), &*DiagOpts);
DiagnosticsEngine Diagnostics(
IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs()), &*DiagOpts,
&DiagnosticPrinter, false);
IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs()),
&*DiagOpts, &DiagnosticPrinter, false);
auto &FileMgr = Tool.getFiles();
SourceManager Sources(Diagnostics, FileMgr);
Rewriter Rewrite(Sources, DefaultLangOptions);
@@ -222,7 +170,7 @@ int main(int argc, const char **argv) {
Tool.applyAllReplacements(Rewrite);
for (const auto &File : Files) {
const auto *Entry = FileMgr.getFile(File);
const auto ID = Sources.getOrCreateFileID(Entry, SrcMgr::C_User);
auto ID = Sources.translateFile(Entry);
Rewrite.getEditBuffer(ID).write(outs());
}
}

View File

@@ -1,79 +0,0 @@
;;; clang-rename.el --- Renames every occurrence of a symbol found at <offset>. -*- lexical-binding: t; -*-
;; Keywords: tools, c
;;; Commentary:
;; To install clang-rename.el make sure the directory of this file is in your
;; `load-path' and add
;;
;; (require 'clang-rename)
;;
;; to your .emacs configuration.
;;; Code:
(defgroup clang-rename nil
"Integration with clang-rename"
:group 'c)
(defcustom clang-rename-binary "clang-rename"
"Path to clang-rename executable."
:type '(file :must-match t)
:group 'clang-rename)
;;;###autoload
(defun clang-rename (new-name)
"Rename all instances of the symbol at point to NEW-NAME using clang-rename."
(interactive "sEnter a new name: ")
(save-some-buffers :all)
;; clang-rename should not be combined with other operations when undoing.
(undo-boundary)
(let ((output-buffer (get-buffer-create "*clang-rename*")))
(with-current-buffer output-buffer (erase-buffer))
(let ((exit-code (call-process
clang-rename-binary nil output-buffer nil
(format "-offset=%d"
;; clang-rename wants file (byte) offsets, not
;; buffer (character) positions.
(clang-rename--bufferpos-to-filepos
;; Emacs treats one character after a symbol as
;; part of the symbol, but clang-rename doesnt.
;; Use the beginning of the current symbol, if
;; available, to resolve the inconsistency.
(or (car (bounds-of-thing-at-point 'symbol))
(point))
'exact))
(format "-new-name=%s" new-name)
"-i" (buffer-file-name))))
(if (and (integerp exit-code) (zerop exit-code))
;; Success; revert current buffer so it gets the modifications.
(progn
(kill-buffer output-buffer)
(revert-buffer :ignore-auto :noconfirm :preserve-modes))
;; Failure; append exit code to output buffer and display it.
(let ((message (clang-rename--format-message
"clang-rename failed with %s %s"
(if (integerp exit-code) "exit status" "signal")
exit-code)))
(with-current-buffer output-buffer
(insert ?\n message ?\n))
(message "%s" message)
(display-buffer output-buffer))))))
(defalias 'clang-rename--bufferpos-to-filepos
(if (fboundp 'bufferpos-to-filepos)
'bufferpos-to-filepos
;; Emacs 24 doesnt have bufferpos-to-filepos, simulate it using
;; position-bytes.
(lambda (position &optional _quality _coding-system)
(1- (position-bytes position)))))
;; format-message is new in Emacs 25.1. Provide a fallback for older
;; versions.
(defalias 'clang-rename--format-message
(if (fboundp 'format-message) 'format-message 'format))
(provide 'clang-rename)
;;; clang-rename.el ends here

View File

@@ -9,13 +9,13 @@ Before installing make sure one of the following is satisfied:
To install, simply put this into your ~/.vimrc
noremap <leader>cr :pyf <path-to>/clang-rename.py<cr>
map ,cr :pyf <path-to>/clang-rename.py<cr>
IMPORTANT NOTE: Before running the tool, make sure you saved the file.
All you have to do now is to place a cursor on a variable/function/class which
you would like to rename and press '<leader>cr'. You will be prompted for a new
name if the cursor points to a valid symbol.
you would like to rename and press ',cr'. You will be prompted for a new name if
the cursor points to a valid symbol.
'''
import vim
@@ -54,7 +54,7 @@ def main():
print stderr
# Reload all buffers in Vim.
vim.command("checktime")
vim.command("bufdo edit")
if __name__ == '__main__':

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

@@ -15,7 +15,6 @@ add_clang_library(clangTidy
clangAST
clangASTMatchers
clangBasic
clangFormat
clangFrontend
clangLex
clangRewrite
@@ -35,7 +34,6 @@ add_subdirectory(cppcoreguidelines)
add_subdirectory(google)
add_subdirectory(misc)
add_subdirectory(modernize)
add_subdirectory(mpi)
add_subdirectory(performance)
add_subdirectory(readability)
add_subdirectory(utils)

View File

@@ -22,7 +22,6 @@
#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"
@@ -33,9 +32,7 @@
#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"
@@ -49,7 +46,7 @@ using namespace clang::driver;
using namespace clang::tooling;
using namespace llvm;
LLVM_INSTANTIATE_REGISTRY(clang::tidy::ClangTidyModuleRegistry)
template class llvm::Registry<clang::tidy::ClangTidyModule>;
namespace clang {
namespace tidy {
@@ -57,6 +54,15 @@ namespace tidy {
namespace {
static const char *AnalyzerCheckNamePrefix = "clang-analyzer-";
static const StringRef StaticAnalyzerChecks[] = {
#define GET_CHECKERS
#define CHECKER(FULLNAME, CLASS, DESCFILE, HELPTEXT, GROUPINDEX, HIDDEN) \
FULLNAME,
#include "clang/StaticAnalyzer/Checkers/Checkers.inc"
#undef CHECKER
#undef GET_CHECKERS
};
class AnalyzerDiagnosticConsumer : public ento::PathDiagnosticConsumer {
public:
AnalyzerDiagnosticConsumer(ClangTidyContext &Context) : Context(Context) {}
@@ -89,13 +95,14 @@ private:
class ErrorReporter {
public:
ErrorReporter(bool ApplyFixes, StringRef FormatStyle)
ErrorReporter(bool ApplyFixes)
: Files(FileSystemOptions()), DiagOpts(new DiagnosticOptions()),
DiagPrinter(new TextDiagnosticPrinter(llvm::outs(), &*DiagOpts)),
Diags(IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs), &*DiagOpts,
DiagPrinter),
SourceMgr(Diags, Files), ApplyFixes(ApplyFixes), TotalFixes(0),
AppliedFixes(0), WarningsAsErrors(0), FormatStyle(FormatStyle) {
SourceMgr(Diags, Files), Rewrite(SourceMgr, LangOpts),
ApplyFixes(ApplyFixes), TotalFixes(0), AppliedFixes(0),
WarningsAsErrors(0) {
DiagOpts->ShowColors = llvm::sys::Process::StandardOutHasColors();
DiagPrinter->BeginSourceFile(LangOpts);
}
@@ -103,14 +110,14 @@ public:
SourceManager &getSourceManager() { return SourceMgr; }
void reportDiagnostic(const ClangTidyError &Error) {
const tooling::DiagnosticMessage &Message = Error.Message;
const ClangTidyMessage &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;
std::string Name = Error.CheckName;
if (Error.IsWarningAsError) {
Name += ",-warnings-as-errors";
Level = DiagnosticsEngine::Error;
@@ -118,59 +125,27 @@ public:
}
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";
}
for (const tooling::Replacement &Fix : Error.Fix) {
// 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;
if (Fix.isApplicable()) {
SmallString<128> FixAbsoluteFilePath = Fix.getFilePath();
Files.makeAbsolutePath(FixAbsoluteFilePath);
FixLoc = getLocation(FixAbsoluteFilePath, Fix.getOffset());
SourceLocation FixEndLoc = FixLoc.getLocWithOffset(Fix.getLength());
Range = SourceRange(FixLoc, FixEndLoc);
Diag << FixItHint::CreateReplacement(Range, Fix.getReplacementText());
}
} 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));
++TotalFixes;
if (ApplyFixes) {
bool Success = Fix.isApplicable() && Fix.apply(Rewrite);
if (Success)
++AppliedFixes;
FixLocations.push_back(std::make_pair(FixLoc, Success));
}
}
}
@@ -178,43 +153,16 @@ public:
Diags.Report(Fix.first, Fix.second ? diag::note_fixit_applied
: diag::note_fixit_failed);
}
for (const auto &Note : Error.Notes)
for (const ClangTidyMessage &Note : Error.Notes)
reportNote(Note);
}
void Finish() {
// FIXME: Run clang-format on changes.
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();
format::FormatStyle Style = format::getStyle("file", File, FormatStyle);
llvm::Expected<Replacements> CleanReplacements =
format::cleanupAroundReplacements(Code, FileAndReplacements.second,
Style);
if (!CleanReplacements) {
llvm::errs() << llvm::toString(CleanReplacements.takeError()) << "\n";
continue;
}
if (!tooling::applyAllReplacements(CleanReplacements.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";
}
llvm::errs() << "clang-tidy applied " << AppliedFixes << " of "
<< TotalFixes << " suggested fixes.\n";
Rewrite.overwriteChangedFiles();
}
}
@@ -230,9 +178,10 @@ private:
return SourceMgr.getLocForStartOfFile(ID).getLocWithOffset(Offset);
}
void reportNote(const tooling::DiagnosticMessage &Message) {
void reportNote(const ClangTidyMessage &Message) {
SourceLocation Loc = getLocation(Message.FilePath, Message.FileOffset);
Diags.Report(Loc, Diags.getCustomDiagID(DiagnosticsEngine::Note, "%0"))
DiagnosticBuilder Diag =
Diags.Report(Loc, Diags.getCustomDiagID(DiagnosticsEngine::Note, "%0"))
<< Message.Message;
}
@@ -242,12 +191,11 @@ private:
DiagnosticConsumer *DiagPrinter;
DiagnosticsEngine Diags;
SourceManager SourceMgr;
llvm::StringMap<Replacements> FileReplacements;
Rewriter Rewrite;
bool ApplyFixes;
unsigned TotalFixes;
unsigned AppliedFixes;
unsigned WarningsAsErrors;
StringRef FormatStyle;
};
class ClangTidyASTConsumer : public MultiplexConsumer {
@@ -287,36 +235,6 @@ static void setStaticAnalyzerCheckerOpts(const ClangTidyOptions &Opts,
}
}
typedef std::vector<std::pair<std::string, bool>> CheckersList;
static CheckersList getCheckersControlList(GlobList &Filter) {
CheckersList List;
const auto &RegisteredCheckers =
AnalyzerOptions::getRegisteredCheckers(/*IncludeExperimental=*/false);
bool AnalyzerChecksEnabled = false;
for (StringRef CheckName : RegisteredCheckers) {
std::string ClangTidyCheckName((AnalyzerCheckNamePrefix + CheckName).str());
AnalyzerChecksEnabled |= Filter.contains(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") || Filter.contains(ClangTidyCheckName))
List.emplace_back(CheckName, true);
}
return List;
}
std::unique_ptr<clang::ASTConsumer>
ClangTidyASTConsumerFactory::CreateASTConsumer(
clang::CompilerInstance &Compiler, StringRef File) {
@@ -400,6 +318,37 @@ ClangTidyOptions::OptionMap ClangTidyASTConsumerFactory::getCheckOptions() {
return Options;
}
ClangTidyASTConsumerFactory::CheckersList
ClangTidyASTConsumerFactory::getCheckersControlList(GlobList &Filter) {
CheckersList List;
bool AnalyzerChecksEnabled = false;
for (StringRef CheckName : StaticAnalyzerChecks) {
std::string Checker((AnalyzerCheckNamePrefix + CheckName).str());
AnalyzerChecksEnabled =
AnalyzerChecksEnabled ||
(!CheckName.startswith("debug") && Filter.contains(Checker));
}
if (AnalyzerChecksEnabled) {
// Run our regex against all possible static analyzer checkers. Note that
// debug checkers print values / run programs to visualize the CFG and are
// thus not applicable to clang-tidy in general.
//
// Always add all core checkers if any other static analyzer checks are
// enabled. This is currently necessary, as other path sensitive checks
// rely on the core checkers.
for (StringRef CheckName : StaticAnalyzerChecks) {
std::string Checker((AnalyzerCheckNamePrefix + CheckName).str());
if (CheckName.startswith("core") ||
(!CheckName.startswith("debug") && Filter.contains(Checker)))
List.push_back(std::make_pair(CheckName, true));
}
}
return List;
}
DiagnosticBuilder ClangTidyCheck::diag(SourceLocation Loc, StringRef Message,
DiagnosticIDs::Level Level) {
return Context->diag(CheckName, Loc, Message, Level);
@@ -461,7 +410,7 @@ ClangTidyOptions::OptionMap getCheckOptions(const ClangTidyOptions &Options) {
ClangTidyStats
runClangTidy(std::unique_ptr<ClangTidyOptionsProvider> OptionsProvider,
const CompilationDatabase &Compilations,
const tooling::CompilationDatabase &Compilations,
ArrayRef<std::string> InputFiles,
std::vector<ClangTidyError> *Errors, ProfileData *Profile) {
ClangTool Tool(Compilations, InputFiles);
@@ -471,14 +420,10 @@ runClangTidy(std::unique_ptr<ClangTidyOptionsProvider> OptionsProvider,
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());
}
CommandLineArguments AdjustedArgs;
if (Opts.ExtraArgsBefore)
AdjustedArgs = *Opts.ExtraArgsBefore;
AdjustedArgs.insert(AdjustedArgs.begin(), Args.begin(), Args.end());
if (Opts.ExtraArgs)
AdjustedArgs.insert(AdjustedArgs.end(), Opts.ExtraArgs->begin(),
Opts.ExtraArgs->end());
@@ -538,8 +483,8 @@ runClangTidy(std::unique_ptr<ClangTidyOptionsProvider> OptionsProvider,
}
void handleErrors(const std::vector<ClangTidyError> &Errors, bool Fix,
StringRef FormatStyle, unsigned &WarningsAsErrorsCount) {
ErrorReporter Reporter(Fix, FormatStyle);
unsigned &WarningsAsErrorsCount) {
ErrorReporter Reporter(Fix);
vfs::FileSystem &FileSystem =
*Reporter.getSourceManager().getFileManager().getVirtualFileSystem();
auto InitialWorkingDir = FileSystem.getCurrentWorkingDirectory();
@@ -562,18 +507,15 @@ void handleErrors(const std::vector<ClangTidyError> &Errors, bool Fix,
WarningsAsErrorsCount += Reporter.getWarningsAsErrorsCount();
}
void exportReplacements(const llvm::StringRef MainFilePath,
const std::vector<ClangTidyError> &Errors,
void exportReplacements(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);
}
tooling::TranslationUnitReplacements TUR;
for (const ClangTidyError &Error : Errors)
TUR.Replacements.insert(TUR.Replacements.end(), Error.Fix.begin(),
Error.Fix.end());
yaml::Output YAML(OS);
YAML << TUD;
YAML << TUR;
}
} // namespace tidy

View File

@@ -73,23 +73,6 @@ public:
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,
@@ -204,6 +187,9 @@ public:
ClangTidyOptions::OptionMap getCheckOptions();
private:
typedef std::vector<std::pair<std::string, bool>> CheckersList;
CheckersList getCheckersControlList(GlobList &Filter);
ClangTidyContext &Context;
std::unique_ptr<ClangTidyCheckFactories> CheckFactories;
};
@@ -235,15 +221,13 @@ runClangTidy(std::unique_ptr<ClangTidyOptionsProvider> OptionsProvider,
// 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.
/// Errors containing fixes are automatically applied.
void handleErrors(const std::vector<ClangTidyError> &Errors, bool Fix,
StringRef FormatStyle, unsigned &WarningsAsErrorsCount);
unsigned &WarningsAsErrorsCount);
/// \brief Serializes replacements into YAML and writes them to the specified
/// output stream.
void exportReplacements(StringRef MainFilePath,
const std::vector<ClangTidyError> &Errors,
void exportReplacements(const std::vector<ClangTidyError> &Errors,
raw_ostream &OS);
} // end namespace tidy

View File

@@ -7,8 +7,8 @@
//
//===----------------------------------------------------------------------===//
///
/// \file This file implements ClangTidyDiagnosticConsumer, ClangTidyContext
/// and ClangTidyError classes.
/// \file This file implements ClangTidyDiagnosticConsumer, ClangTidyMessage,
/// ClangTidyContext and ClangTidyError classes.
///
/// This tool uses the Clang Tooling infrastructure, see
/// http://clang.llvm.org/docs/HowToSetupToolingForLLVM.html
@@ -45,13 +45,13 @@ protected:
// 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 + "]";
std::string CheckNameInMessage = " [" + Error.CheckName + "]";
if (Message.endswith(CheckNameInMessage))
Message = Message.substr(0, Message.size() - CheckNameInMessage.size());
auto TidyMessage = Loc.isValid()
? tooling::DiagnosticMessage(Message, *SM, Loc)
: tooling::DiagnosticMessage(Message);
ClangTidyMessage TidyMessage = Loc.isValid()
? ClangTidyMessage(Message, *SM, Loc)
: ClangTidyMessage(Message);
if (Level == DiagnosticsEngine::Note) {
Error.Notes.push_back(TidyMessage);
return;
@@ -77,15 +77,7 @@ protected:
assert(Range.getBegin().isFileID() && Range.getEnd().isFileID() &&
"Only file locations supported in fix-it hints.");
tooling::Replacement Replacement(SM, 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!");
}
Error.Fix.insert(tooling::Replacement(SM, Range, FixIt.CodeToInsert));
}
}
@@ -110,10 +102,23 @@ private:
};
} // end anonymous namespace
ClangTidyMessage::ClangTidyMessage(StringRef Message)
: Message(Message), FileOffset(0) {}
ClangTidyMessage::ClangTidyMessage(StringRef Message,
const SourceManager &Sources,
SourceLocation Loc)
: Message(Message) {
assert(Loc.isValid() && Loc.isFileID());
FilePath = Sources.getFilename(Loc);
FileOffset = Sources.getFileOffset(Loc);
}
ClangTidyError::ClangTidyError(StringRef CheckName,
ClangTidyError::Level DiagLevel,
StringRef BuildDirectory, bool IsWarningAsError)
: tooling::Diagnostic(CheckName, DiagLevel, BuildDirectory),
bool IsWarningAsError,
StringRef BuildDirectory)
: CheckName(CheckName), BuildDirectory(BuildDirectory), DiagLevel(DiagLevel),
IsWarningAsError(IsWarningAsError) {}
// Returns true if GlobList starts with the negative indicator ('-'), removes it
@@ -171,7 +176,8 @@ DiagnosticBuilder ClangTidyContext::diag(
assert(Loc.isValid());
unsigned ID = DiagEngine->getDiagnosticIDs()->getCustomDiagID(
Level, (Description + " [" + CheckName + "]").str());
CheckNamesByDiagnosticID.try_emplace(ID, CheckName);
if (CheckNamesByDiagnosticID.count(ID) == 0)
CheckNamesByDiagnosticID.insert(std::make_pair(ID, CheckName.str()));
return DiagEngine->Report(Loc, ID);
}
@@ -237,7 +243,7 @@ StringRef ClangTidyContext::getCheckName(unsigned DiagnosticID) const {
ClangTidyDiagnosticConsumer::ClangTidyDiagnosticConsumer(ClangTidyContext &Ctx)
: Context(Ctx), LastErrorRelatesToUserCode(false),
LastErrorPassesLineFilter(false), LastErrorWasIgnored(false) {
LastErrorPassesLineFilter(false) {
IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions();
Diags.reset(new DiagnosticsEngine(
IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs), &*DiagOpts, this,
@@ -248,7 +254,7 @@ ClangTidyDiagnosticConsumer::ClangTidyDiagnosticConsumer(ClangTidyContext &Ctx)
void ClangTidyDiagnosticConsumer::finalizeLastError() {
if (!Errors.empty()) {
ClangTidyError &Error = Errors.back();
if (!Context.getChecksFilter().contains(Error.DiagnosticName) &&
if (!Context.getChecksFilter().contains(Error.CheckName) &&
Error.DiagLevel != ClangTidyError::Error) {
++Context.Stats.ErrorsIgnoredCheckFilter;
Errors.pop_back();
@@ -266,7 +272,7 @@ void ClangTidyDiagnosticConsumer::finalizeLastError() {
LastErrorPassesLineFilter = false;
}
static bool LineIsMarkedWithNOLINT(SourceManager &SM, SourceLocation Loc) {
static bool LineIsMarkedWithNOLINT(SourceManager& SM, SourceLocation Loc) {
bool Invalid;
const char *CharacterData = SM.getCharacterData(Loc, &Invalid);
if (!Invalid) {
@@ -282,34 +288,15 @@ static bool LineIsMarkedWithNOLINT(SourceManager &SM, SourceLocation Loc) {
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 &&
if (Info.getLocation().isValid() &&
DiagLevel != DiagnosticsEngine::Error &&
DiagLevel != DiagnosticsEngine::Fatal &&
LineIsMarkedWithNOLINTinMacro(Diags->getSourceManager(),
Info.getLocation())) {
LineIsMarkedWithNOLINT(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);
@@ -354,8 +341,8 @@ void ClangTidyDiagnosticConsumer::HandleDiagnostic(
bool IsWarningAsError =
DiagLevel == DiagnosticsEngine::Warning &&
Context.getWarningAsErrorFilter().contains(CheckName);
Errors.emplace_back(CheckName, Level, Context.getCurrentBuildDirectory(),
IsWarningAsError);
Errors.push_back(ClangTidyError(CheckName, Level, IsWarningAsError,
Context.getCurrentBuildDirectory()));
}
ClangTidyDiagnosticRenderer Converter(
@@ -440,7 +427,7 @@ void ClangTidyDiagnosticConsumer::removeIncompatibleErrors(
// 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
// proccess, 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.
@@ -462,7 +449,7 @@ void ClangTidyDiagnosticConsumer::removeIncompatibleErrors(
// 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
// inapplicable the ones that we proccess 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
@@ -470,14 +457,14 @@ void ClangTidyDiagnosticConsumer::removeIncompatibleErrors(
// 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
// potentially contain the other one, so we want to proccess 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,
// one will be proccessed before, disallowing the second one, and the
// end point of the first one will also be proccessed before,
// disallowing the first one.
if (Type == ET_Begin)
Priority = std::make_tuple(Begin, Type, -End, -ErrorSize, ErrorId);
@@ -502,28 +489,25 @@ void ClangTidyDiagnosticConsumer::removeIncompatibleErrors(
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();
}
for (const auto &Replace : Error.Fix)
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]);
}
for (const auto &Replace : Errors[I].Fix) {
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;
FileEvents[FilePath].push_back(
Event(Begin, End, Event::ET_Begin, I, Sizes[I]));
FileEvents[FilePath].push_back(
Event(Begin, End, Event::ET_End, I, Sizes[I]));
}
}
@@ -549,8 +533,9 @@ void ClangTidyDiagnosticConsumer::removeIncompatibleErrors(
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");
Errors[I].Notes.push_back(
ClangTidyMessage("this fix will not be applied because"
" it overlaps with another fix"));
}
}
}
@@ -558,8 +543,8 @@ void ClangTidyDiagnosticConsumer::removeIncompatibleErrors(
namespace {
struct LessClangTidyError {
bool operator()(const ClangTidyError &LHS, const ClangTidyError &RHS) const {
const tooling::DiagnosticMessage &M1 = LHS.Message;
const tooling::DiagnosticMessage &M2 = RHS.Message;
const ClangTidyMessage &M1 = LHS.Message;
const ClangTidyMessage &M2 = RHS.Message;
return std::tie(M1.FilePath, M1.FileOffset, M1.Message) <
std::tie(M2.FilePath, M2.FileOffset, M2.Message);

View File

@@ -13,7 +13,6 @@
#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"
@@ -33,6 +32,18 @@ class CompilationDatabase;
namespace tidy {
/// \brief A message from a clang-tidy check.
///
/// Note that this is independent of a \c SourceManager.
struct ClangTidyMessage {
ClangTidyMessage(StringRef Message = "");
ClangTidyMessage(StringRef Message, const SourceManager &Sources,
SourceLocation Loc);
std::string Message;
std::string FilePath;
unsigned FileOffset;
};
/// \brief A detected error complete with information to display diagnostic and
/// automatic fix.
///
@@ -40,10 +51,30 @@ namespace tidy {
/// 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);
struct ClangTidyError {
enum Level {
Warning = DiagnosticsEngine::Warning,
Error = DiagnosticsEngine::Error
};
ClangTidyError(StringRef CheckName, Level DiagLevel, bool IsWarningAsError,
StringRef BuildDirectory);
std::string CheckName;
ClangTidyMessage Message;
tooling::Replacements Fix;
SmallVector<ClangTidyMessage, 1> Notes;
// A build directory of the diagnostic source file.
//
// It's an absolute path which is `directory` field of the source file in
// compilation database. If users don't specify the compilation database
// directory, it is the current directory where clang-tidy runs.
//
// Note: it is empty in unittest.
std::string BuildDirectory;
Level DiagLevel;
bool IsWarningAsError;
};
@@ -254,7 +285,6 @@ private:
std::unique_ptr<llvm::Regex> HeaderFilter;
bool LastErrorRelatesToUserCode;
bool LastErrorPassesLineFilter;
bool LastErrorWasIgnored;
};
} // end namespace tidy

View File

@@ -26,9 +26,8 @@ namespace tidy {
/// this object.
class ClangTidyCheckFactories {
public:
typedef std::function<ClangTidyCheck *(StringRef Name,
ClangTidyContext *Context)>
CheckFactory;
typedef std::function<ClangTidyCheck *(
StringRef Name, ClangTidyContext *Context)> CheckFactory;
/// \brief Registers check \p Factory with name \p Name.
///
@@ -59,8 +58,8 @@ public:
template <typename CheckType> void registerCheck(StringRef CheckName) {
registerCheckFactory(CheckName,
[](StringRef Name, ClangTidyContext *Context) {
return new CheckType(Name, Context);
});
return new CheckType(Name, Context);
});
}
/// \brief Create instances of all checks matching \p CheckRegexString and

View File

@@ -13,6 +13,8 @@
#include "ClangTidyModule.h"
#include "llvm/Support/Registry.h"
extern template class llvm::Registry<clang::tidy::ClangTidyModule>;
namespace clang {
namespace tidy {

View File

@@ -117,40 +117,34 @@ ClangTidyOptions ClangTidyOptions::getDefaults() {
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.User, Other.User);
mergeVectors(Result.ExtraArgs, Other.ExtraArgs);
mergeVectors(Result.ExtraArgsBefore, Other.ExtraArgsBefore);
// Merge comma-separated glob lists by appending the new value after a comma.
if (Other.Checks)
Result.Checks =
(Result.Checks && !Result.Checks->empty() ? *Result.Checks + "," : "") +
*Other.Checks;
if (Other.WarningsAsErrors)
Result.WarningsAsErrors =
(Result.WarningsAsErrors && !Result.WarningsAsErrors->empty()
? *Result.WarningsAsErrors + ","
: "") +
*Other.WarningsAsErrors;
if (Other.HeaderFilterRegex)
Result.HeaderFilterRegex = Other.HeaderFilterRegex;
if (Other.SystemHeaders)
Result.SystemHeaders = Other.SystemHeaders;
if (Other.AnalyzeTemporaryDtors)
Result.AnalyzeTemporaryDtors = Other.AnalyzeTemporaryDtors;
if (Other.User)
Result.User = Other.User;
if (Other.ExtraArgs)
Result.ExtraArgs = Other.ExtraArgs;
if (Other.ExtraArgsBefore)
Result.ExtraArgsBefore = Other.ExtraArgsBefore;
for (const auto &KeyValue : Other.CheckOptions)
Result.CheckOptions[KeyValue.first] = KeyValue.second;
@@ -215,7 +209,8 @@ FileOptionsProvider::FileOptionsProvider(
const ClangTidyOptions &OverrideOptions,
const FileOptionsProvider::ConfigFileHandlers &ConfigHandlers)
: DefaultOptionsProvider(GlobalOptions, DefaultOptions),
OverrideOptions(OverrideOptions), ConfigHandlers(ConfigHandlers) {}
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

View File

@@ -173,8 +173,7 @@ 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;
llvm::StringRef)>> ConfigFileHandler;
/// \brief Configuration file handlers listed in the order of priority.
///

View File

@@ -226,13 +226,8 @@ def update_checks_list(clang_tidy_path):
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)
doc.read())
if match:
return ' %(check)s (redirects to %(target)s) <%(check)s>\n' % {
'check': check_name,

View File

@@ -18,7 +18,6 @@
#include "../misc/ThrowByValueCatchByReferenceCheck.h"
#include "CommandProcessorCheck.h"
#include "FloatLoopCounter.h"
#include "LimitedRandomnessCheck.h"
#include "SetLongJmpCheck.h"
#include "StaticObjectExceptionCheck.h"
#include "StrToNumCheck.h"
@@ -34,7 +33,8 @@ public:
void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override {
// C++ checkers
// DCL
CheckFactories.registerCheck<VariadicFunctionDefCheck>("cert-dcl50-cpp");
CheckFactories.registerCheck<VariadicFunctionDefCheck>(
"cert-dcl50-cpp");
CheckFactories.registerCheck<misc::NewDeleteOverloadsCheck>(
"cert-dcl54-cpp");
CheckFactories.registerCheck<google::build::UnnamedNamespaceInHeaderCheck>(
@@ -43,29 +43,36 @@ public:
CheckFactories.registerCheck<misc::MoveConstructorInitCheck>(
"cert-oop11-cpp");
// ERR
CheckFactories.registerCheck<misc::ThrowByValueCatchByReferenceCheck>(
"cert-err09-cpp");
CheckFactories.registerCheck<SetLongJmpCheck>("cert-err52-cpp");
CheckFactories.registerCheck<StaticObjectExceptionCheck>("cert-err58-cpp");
CheckFactories.registerCheck<ThrownExceptionTypeCheck>("cert-err60-cpp");
CheckFactories.registerCheck<SetLongJmpCheck>(
"cert-err52-cpp");
CheckFactories.registerCheck<StaticObjectExceptionCheck>(
"cert-err58-cpp");
CheckFactories.registerCheck<ThrownExceptionTypeCheck>(
"cert-err60-cpp");
CheckFactories.registerCheck<misc::ThrowByValueCatchByReferenceCheck>(
"cert-err61-cpp");
// MSC
CheckFactories.registerCheck<LimitedRandomnessCheck>("cert-msc50-cpp");
// C checkers
// DCL
CheckFactories.registerCheck<misc::StaticAssertCheck>("cert-dcl03-c");
CheckFactories.registerCheck<misc::StaticAssertCheck>(
"cert-dcl03-c");
// ENV
CheckFactories.registerCheck<CommandProcessorCheck>("cert-env33-c");
CheckFactories.registerCheck<CommandProcessorCheck>(
"cert-env33-c");
// FLP
CheckFactories.registerCheck<FloatLoopCounter>("cert-flp30-c");
CheckFactories.registerCheck<FloatLoopCounter>(
"cert-flp30-c");
// FIO
CheckFactories.registerCheck<misc::NonCopyableObjectsCheck>("cert-fio38-c");
CheckFactories.registerCheck<misc::NonCopyableObjectsCheck>(
"cert-fio38-c");
// ERR
CheckFactories.registerCheck<StrToNumCheck>("cert-err34-c");
// MSC
CheckFactories.registerCheck<LimitedRandomnessCheck>("cert-msc30-c");
CheckFactories.registerCheck<StrToNumCheck>(
"cert-err34-c");
}
ClangTidyOptions getModuleOptions() override {
ClangTidyOptions Options;
Options.CheckOptions["cert-oop11-cpp.UseCERTSemantics"] = "1";
return Options;
}
};
@@ -73,8 +80,8 @@ public:
// Register the MiscTidyModule using this statically initialized variable.
static ClangTidyModuleRegistry::Add<cert::CERTModule>
X("cert-module",
"Adds lint checks corresponding to CERT secure coding guidelines.");
X("cert-module",
"Adds lint checks corresponding to CERT secure coding guidelines.");
// This anchor is used to force the linker to link in the generated object file
// and thus register the CERTModule.

View File

@@ -4,7 +4,6 @@ add_clang_library(clangTidyCERTModule
CERTTidyModule.cpp
CommandProcessorCheck.cpp
FloatLoopCounter.cpp
LimitedRandomnessCheck.cpp
SetLongJmpCheck.cpp
StaticObjectExceptionCheck.cpp
StrToNumCheck.cpp

View File

@@ -7,7 +7,7 @@ additions:
Any file referencing a CERT Secure Coding guideline:
Please allow this letter to serve as confirmation that open source projects on
http://llvm.org are permitted to link via hypertext to the CERT(R) secure coding
guidelines available at https://www.securecoding.cert.org.
guidelines available at https://www.securecoding.cert.org.
The foregoing is permitted by the Terms of Use as follows:
"Linking to the Service

View File

@@ -1,38 +0,0 @@
//===--- LimitedRandomnessCheck.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 "LimitedRandomnessCheck.h"
#include "clang/AST/ASTContext.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
using namespace clang::ast_matchers;
namespace clang {
namespace tidy {
namespace cert {
void LimitedRandomnessCheck::registerMatchers(MatchFinder *Finder) {
Finder->addMatcher(callExpr(callee(functionDecl(namedDecl(hasName("::rand")),
parameterCountIs(0))))
.bind("randomGenerator"),
this);
}
void LimitedRandomnessCheck::check(const MatchFinder::MatchResult &Result) {
std::string msg = "";
if (getLangOpts().CPlusPlus)
msg = "; use C++11 random library instead";
const auto *MatchedDecl = Result.Nodes.getNodeAs<CallExpr>("randomGenerator");
diag(MatchedDecl->getLocStart(), "rand() has limited randomness" + msg);
}
} // namespace cert
} // namespace tidy
} // namespace clang

View File

@@ -1,38 +0,0 @@
//===--- LimitedRandomnessCheck.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_CERT_LIMITED_RANDOMNESS_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CERT_LIMITED_RANDOMNESS_H
#include "../ClangTidy.h"
namespace clang {
namespace tidy {
namespace cert {
/// Pseudorandom number generators are not genuinely random. The result of the
/// std::rand() function makes no guarantees as to the quality of the random
/// sequence produced.
/// This check warns for the usage of std::rand() function.
///
/// For the user-facing documentation see:
/// http://clang.llvm.org/extra/clang-tidy/checks/cert-msc50-cpp.html
class LimitedRandomnessCheck : public ClangTidyCheck {
public:
LimitedRandomnessCheck(StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context) {}
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
};
} // namespace cert
} // namespace tidy
} // namespace clang
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CERT_LIMITED_RANDOMNESS_H

View File

@@ -22,37 +22,27 @@ void StaticObjectExceptionCheck::registerMatchers(MatchFinder *Finder) {
if (!getLangOpts().CPlusPlus)
return;
// Match any static or thread_local variable declaration that has an
// initializer that can throw.
// Match any static or thread_local variable declaration that is initialized
// with a constructor that can throw.
Finder->addMatcher(
varDecl(anyOf(hasThreadStorageDuration(), hasStaticStorageDuration()),
unless(hasAncestor(functionDecl())),
anyOf(hasDescendant(cxxConstructExpr(hasDeclaration(
cxxConstructorDecl(unless(isNoThrow())).bind("func")))),
hasDescendant(cxxNewExpr(hasDeclaration(
functionDecl(unless(isNoThrow())).bind("func")))),
hasDescendant(callExpr(hasDeclaration(
functionDecl(unless(isNoThrow())).bind("func"))))))
hasInitializer(cxxConstructExpr(hasDeclaration(
cxxConstructorDecl(unless(isNoThrow()))
.bind("ctor")))))
.bind("var"),
this);
}
void StaticObjectExceptionCheck::check(const MatchFinder::MatchResult &Result) {
const auto *VD = Result.Nodes.getNodeAs<VarDecl>("var");
const auto *Func = Result.Nodes.getNodeAs<FunctionDecl>("func");
const auto *Ctor = Result.Nodes.getNodeAs<CXXConstructorDecl>("ctor");
diag(VD->getLocation(),
"initialization of %0 with %select{static|thread_local}1 storage "
"construction of %0 with %select{static|thread_local}1 storage "
"duration may throw an exception that cannot be caught")
<< VD << (VD->getStorageDuration() == SD_Static ? 0 : 1);
SourceLocation FuncLocation = Func->getLocation();
if (FuncLocation.isValid()) {
diag(FuncLocation,
"possibly throwing %select{constructor|function}0 declared here",
DiagnosticIDs::Note)
<< (isa<CXXConstructorDecl>(Func) ? 0 : 1);
}
diag(Ctor->getLocation(), "possibly throwing constructor declared here",
DiagnosticIDs::Note);
}
} // namespace cert

View File

@@ -8,9 +8,9 @@
//===----------------------------------------------------------------------===//
#include "StrToNumCheck.h"
#include "clang/Analysis/Analyses/FormatString.h"
#include "clang/AST/ASTContext.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/Analysis/Analyses/FormatString.h"
#include "llvm/ADT/StringSwitch.h"
#include <cassert>
@@ -127,7 +127,7 @@ ConversionKind ClassifyFormatString(StringRef Fmt, const LangOptions &LO,
Handler H;
analyze_format_string::ParseScanfString(H, Fmt.begin(), Fmt.end(), LO, TI);
return H.get();
}
@@ -214,7 +214,7 @@ void StrToNumCheck::check(const MatchFinder::MatchResult &Result) {
// Formatted input functions need further checking of the format string to
// determine whether a problematic conversion may be happening.
Conversion = ClassifyFormatString(FmtStr, getLangOpts(),
Conversion = ClassifyFormatString(FmtStr, Result.Context->getLangOpts(),
Result.Context->getTargetInfo());
if (Conversion != ConversionKind::None)
FuncDecl = FFD;

View File

@@ -23,7 +23,7 @@ namespace cert {
class ThrownExceptionTypeCheck : public ClangTidyCheck {
public:
ThrownExceptionTypeCheck(StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context) {}
: ClangTidyCheck(Name, Context) {}
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
};

View File

@@ -3,7 +3,6 @@ set(LLVM_LINK_COMPONENTS support)
add_clang_library(clangTidyCppCoreGuidelinesModule
CppCoreGuidelinesTidyModule.cpp
InterfacesGlobalInitCheck.cpp
NoMallocCheck.cpp
ProBoundsArrayToPointerDecayCheck.cpp
ProBoundsConstantArrayIndexCheck.cpp
ProBoundsPointerArithmeticCheck.cpp
@@ -14,8 +13,6 @@ add_clang_library(clangTidyCppCoreGuidelinesModule
ProTypeStaticCastDowncastCheck.cpp
ProTypeUnionAccessCheck.cpp
ProTypeVarargCheck.cpp
SpecialMemberFunctionsCheck.cpp
SlicingCheck.cpp
LINK_LIBS
clangAST

View File

@@ -12,7 +12,6 @@
#include "../ClangTidyModuleRegistry.h"
#include "../misc/UnconventionalAssignOperatorCheck.h"
#include "InterfacesGlobalInitCheck.h"
#include "NoMallocCheck.h"
#include "ProBoundsArrayToPointerDecayCheck.h"
#include "ProBoundsConstantArrayIndexCheck.h"
#include "ProBoundsPointerArithmeticCheck.h"
@@ -23,8 +22,6 @@
#include "ProTypeStaticCastDowncastCheck.h"
#include "ProTypeUnionAccessCheck.h"
#include "ProTypeVarargCheck.h"
#include "SlicingCheck.h"
#include "SpecialMemberFunctionsCheck.h"
namespace clang {
namespace tidy {
@@ -36,7 +33,6 @@ public:
void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override {
CheckFactories.registerCheck<InterfacesGlobalInitCheck>(
"cppcoreguidelines-interfaces-global-init");
CheckFactories.registerCheck<NoMallocCheck>("cppcoreguidelines-no-malloc");
CheckFactories.registerCheck<ProBoundsArrayToPointerDecayCheck>(
"cppcoreguidelines-pro-bounds-array-to-pointer-decay");
CheckFactories.registerCheck<ProBoundsConstantArrayIndexCheck>(
@@ -57,9 +53,6 @@ public:
"cppcoreguidelines-pro-type-union-access");
CheckFactories.registerCheck<ProTypeVarargCheck>(
"cppcoreguidelines-pro-type-vararg");
CheckFactories.registerCheck<SpecialMemberFunctionsCheck>(
"cppcoreguidelines-special-member-functions");
CheckFactories.registerCheck<SlicingCheck>("cppcoreguidelines-slicing");
CheckFactories.registerCheck<misc::UnconventionalAssignOperatorCheck>(
"cppcoreguidelines-c-copy-assignment-signature");
}

View File

@@ -1,62 +0,0 @@
//===--- NoMallocCheck.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 "NoMallocCheck.h"
#include "clang/AST/ASTContext.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include <iostream>
#include <string>
using namespace clang::ast_matchers;
namespace clang {
namespace tidy {
namespace cppcoreguidelines {
void NoMallocCheck::registerMatchers(MatchFinder *Finder) {
// C-style memory management is only problematic in C++.
if (!getLangOpts().CPlusPlus)
return;
// Registering malloc, will suggest RAII.
Finder->addMatcher(
callExpr(callee(functionDecl(hasAnyName("::malloc", "::calloc"))))
.bind("aquisition"),
this);
// Registering realloc calls, suggest std::vector or std::string.
Finder->addMatcher(
callExpr(callee(functionDecl(hasName("::realloc")))).bind("realloc"),
this);
// Registering free calls, will suggest RAII instead.
Finder->addMatcher(
callExpr(callee(functionDecl(hasName("::free")))).bind("free"), this);
}
void NoMallocCheck::check(const MatchFinder::MatchResult &Result) {
const CallExpr *Call = nullptr;
StringRef Recommendation;
if ((Call = Result.Nodes.getNodeAs<CallExpr>("aquisition")))
Recommendation = "consider a container or a smart pointer";
else if ((Call = Result.Nodes.getNodeAs<CallExpr>("realloc")))
Recommendation = "consider std::vector or std::string";
else if ((Call = Result.Nodes.getNodeAs<CallExpr>("free")))
Recommendation = "use RAII";
assert(Call && "Unhandled binding in the Matcher");
diag(Call->getLocStart(), "do not manage memory manually; %0")
<< Recommendation << SourceRange(Call->getLocStart(), Call->getLocEnd());
}
} // namespace cppcoreguidelines
} // namespace tidy
} // namespace clang

View File

@@ -1,44 +0,0 @@
//===--- NoMallocCheck.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_CPPCOREGUIDELINES_NO_MALLOC_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_NO_MALLOC_H
#include "../ClangTidy.h"
#include "clang/AST/ASTContext.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
namespace clang {
namespace tidy {
namespace cppcoreguidelines {
/// This checker is concerned with C-style memory management and suggest modern
/// alternatives to it.
/// The check is only enabled in C++. For analyzing malloc calls see Clang
/// Static Analyzer - unix.Malloc.
///
/// For the user-facing documentation see:
/// http://clang.llvm.org/extra/clang-tidy/checks/cppcoreguidelines-no-malloc.html
class NoMallocCheck : public ClangTidyCheck {
public:
NoMallocCheck(StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context) {}
/// Registering for malloc, calloc, realloc and free calls.
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
/// Checks matched function calls and gives suggestion to modernize the code.
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
};
} // namespace cppcoreguidelines
} // namespace tidy
} // namespace clang
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_NO_MALLOC_H

View File

@@ -15,7 +15,7 @@
namespace clang {
namespace tidy {
namespace cppcoreguidelines {
/// This check flags all array to pointer decays
///
/// For the user-facing documentation see:

View File

@@ -45,14 +45,11 @@ void ProBoundsConstantArrayIndexCheck::registerMatchers(MatchFinder *Finder) {
if (!getLangOpts().CPlusPlus)
return;
// Note: if a struct contains an array member, the compiler-generated
// constructor has an arraySubscriptExpr.
Finder->addMatcher(
arraySubscriptExpr(
hasBase(ignoringImpCasts(hasType(constantArrayType().bind("type")))),
hasIndex(expr().bind("index")), unless(hasAncestor(isImplicit())))
.bind("expr"),
this);
Finder->addMatcher(arraySubscriptExpr(hasBase(ignoringImpCasts(hasType(
constantArrayType().bind("type")))),
hasIndex(expr().bind("index")))
.bind("expr"),
this);
Finder->addMatcher(
cxxOperatorCallExpr(
@@ -112,7 +109,8 @@ void ProBoundsConstantArrayIndexCheck::check(
return;
if (Index.isSigned() && Index.isNegative()) {
diag(Matched->getExprLoc(), "std::array<> index %0 is negative")
diag(Matched->getExprLoc(),
"std::array<> index %0 is negative")
<< Index.toString(10);
return;
}
@@ -129,9 +127,8 @@ void ProBoundsConstantArrayIndexCheck::check(
// Get uint64_t values, because different bitwidths would lead to an assertion
// in APInt::uge.
if (Index.getZExtValue() >= ArraySize.getZExtValue()) {
diag(Matched->getExprLoc(),
"std::array<> index %0 is past the end of the array "
"(which contains %1 elements)")
diag(Matched->getExprLoc(), "std::array<> index %0 is past the end of the array "
"(which contains %1 elements)")
<< Index.toString(10) << ArraySize.toString(10, false);
}
}

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