Compare commits

..

223 Commits

Author SHA1 Message Date
Hans Wennborg
e4f84abd37 ReleaseNotes: move the retpoline bullets higher
llvm-svn: 326550
2018-03-02 09:50:00 +00:00
Hans Wennborg
e2f407be52 ReleaseNotes: move the retpoline bullet higher
llvm-svn: 326549
2018-03-02 09:46:38 +00:00
Chandler Carruth
dc752a9de9 Add some minimal release notes for retpolines.
llvm-svn: 326540
2018-03-02 05:49:03 +00:00
Chandler Carruth
9b3f8b662e Add some minimal release notes for retpoline support.
llvm-svn: 326539
2018-03-02 05:48:38 +00:00
Hans Wennborg
7c0247e37b Merging r326393:
------------------------------------------------------------------------
r326393 | ctopper | 2018-03-01 01:08:38 +0100 (Thu, 01 Mar 2018) | 5 lines

[X86] Make sure we don't combine (fneg (fma X, Y, Z)) to a target specific node when there are no FMA instructions.

This would cause a 'cannot select' error at isel when we should have emitted a lib call and an xor.

Fixes PR36553.
------------------------------------------------------------------------

llvm-svn: 326423
2018-03-01 09:05:01 +00:00
Martin Storsjo
2c62a710df Fix a typo in the section about C++2a features; it follows C++17, not C++14
llvm-svn: 326317
2018-02-28 11:55:11 +00:00
Richard Smith
2c77d8645b A few release notes updates for C++ support in Clang 6.0.
llvm-svn: 326297
2018-02-28 02:59:20 +00:00
Hans Wennborg
a90838136c ReleaseNotes: remote the ... stuff
llvm-svn: 326200
2018-02-27 15:16:54 +00:00
Hans Wennborg
eca4aa5ddb ReleaseNotes: tidy up, fix some links etc.
llvm-svn: 326199
2018-02-27 14:51:16 +00:00
Hans Wennborg
bfee381f00 Regenerate docs/AttributeReference.rst
llvm-svn: 326197
2018-02-27 13:54:38 +00:00
Hans Wennborg
ab40952d65 Merging r326195:
------------------------------------------------------------------------
r326195 | hans | 2018-02-27 14:48:50 +0100 (Tue, 27 Feb 2018) | 1 line

AttrDocs.td: fix some bad code-blocks
------------------------------------------------------------------------

llvm-svn: 326196
2018-02-27 13:53:37 +00:00
Hans Wennborg
c3d23aa808 ReleaseNotes: tidy up
llvm-svn: 326190
2018-02-27 13:21:07 +00:00
Hans Wennborg
4a69f7f1d9 ReleaseNotes: tidy up
llvm-svn: 326186
2018-02-27 11:11:51 +00:00
Hans Wennborg
a426de8b83 ReleaseNotes: drop in-progress warning
llvm-svn: 326185
2018-02-27 10:34:17 +00:00
Hans Wennborg
59ed12ccf0 Sphinx: fix 'Inline emphasis start-string without end-string'
llvm-svn: 326184
2018-02-27 10:33:49 +00:00
Rafael Espindola
eeb3bcecc7 Fix type in the Release Notes.
Thanks to Martin Storsjö for noticing it!

llvm-svn: 326103
2018-02-26 17:57:55 +00:00
Hans Wennborg
ea0ca9187d Merging r325946:
------------------------------------------------------------------------
r325946 | echristo | 2018-02-23 21:12:24 +0100 (Fri, 23 Feb 2018) | 15 lines

Because of CVE-2018-6574, some compiler options and linker options are restricted to prevent arbitrary code execution.

https://github.com/golang/go/issues/23672

By this change, building a Go code with LLVM Go bindings causes a compilation error as follows.

  go build llvm.org/llvm/bindings/go/llvm: invalid flag in #cgo LDFLAGS: -Wl,-headerpad_max_install_names

llvm-go tool generates cgo LDFLAGS directive from `llvm-config --ldflags` and it contains -Wl,option options. But -Wl,option is banned by default. To avoid this problem, we need to set $CGO_LDFLAGS_ALLOW environment variable to notify a compiler that the flags should be allowed.

  $ export CGO_LDFLAGS_ALLOW='-Wl,(-search_paths_first|-headerpad_max_install_names)'

By default for go 1.10 and go 1.9.5 these options should appear in the accepted set of options, however, if you're running into the error it's useful to have this documented.

Patch by Ryuichi Hayashida
------------------------------------------------------------------------

llvm-svn: 326076
2018-02-26 10:04:25 +00:00
Hans Wennborg
171ff48d3d Fix an unused variable warning in non-assert builds
llvm-svn: 326075
2018-02-26 09:56:32 +00:00
Craig Topper
f34f917402 [ReleaseNotes] More X86 updates
llvm-svn: 325932
2018-02-23 18:33:04 +00:00
Hans Wennborg
105dd77431 Merging r325894:
------------------------------------------------------------------------
r325894 | hans | 2018-02-23 13:20:26 +0100 (Fri, 23 Feb 2018) | 1 line

llvm-config: Add advapi32 to --system-libs on Windows (PR36372)
------------------------------------------------------------------------

llvm-svn: 325895
2018-02-23 12:22:51 +00:00
Hans Wennborg
744fb0a05c Regenerate DiagnosticsReference.rst
$ bin/clang-tblgen -gen-diag-docs -I../cfe.src/include/clang/Basic \
    ../cfe.src/include/clang/Basic/Diagnostic.td \
    -o ../cfe.src/docs/DiagnosticsReference.rst

llvm-svn: 325889
2018-02-23 11:19:13 +00:00
Simon Dardis
329e1e192f [mips] 6.0 Release notes
Reviewers: atanasyan, arichardson, petarj, smaksimovic, abeserminji

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

llvm-svn: 325876
2018-02-23 10:19:00 +00:00
Hans Wennborg
8a2cebe272 Add more items to lld 6.0 release note
By Rui Ueyama!

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

llvm-svn: 325871
2018-02-23 08:36:10 +00:00
Rafael Espindola
f1706f29e6 Add a few release notes.
llvm-svn: 325811
2018-02-22 18:20:06 +00:00
Hans Wennborg
856de4774a Regenerate ClangCommandLineReference.rst
$ bin/clang-tblgen -gen-opt-docs -I../cfe.src/include \
    -I../cfe.src/include/clang/Driver -I../llvm.src/include \
    ../cfe.src/include/clang/Driver/ClangOptionDocs.td \
    -o ../cfe.src/docs/ClangCommandLineReference.rst

llvm-svn: 325793
2018-02-22 15:16:58 +00:00
Hans Wennborg
16810fa55f docs: remove in-progress warnings
llvm-svn: 325791
2018-02-22 14:47:59 +00:00
Hans Wennborg
61eefef5b1 docs: remove in-progress warnings
llvm-svn: 325790
2018-02-22 14:46:40 +00:00
Hans Wennborg
d56eb4da97 docs: remove in-progress warnings
llvm-svn: 325789
2018-02-22 14:44:52 +00:00
Hans Wennborg
e35287ad97 docs: remove in-progress warnings
llvm-svn: 325787
2018-02-22 14:41:44 +00:00
Hans Wennborg
c408896eae docs: remove in-progress warnings
llvm-svn: 325786
2018-02-22 14:38:34 +00:00
Hans Wennborg
9772d4eb26 ReleaseNotes: a few flags etc. By Nico Weber.
llvm-svn: 325783
2018-02-22 13:50:50 +00:00
Hans Wennborg
6449b9de4e ReleaseNotes: mention new -nostdlib++ flag, and clang-cl exposing --version
By Nico Weber

llvm-svn: 325781
2018-02-22 13:39:43 +00:00
Hans Wennborg
ffc21e79b3 ReleaseNotes: mention improvements to -Wdelete-non-virtual-dtor and -Wunreachable-code
By Nico Weber!

llvm-svn: 325780
2018-02-22 13:35:08 +00:00
Hans Wennborg
e47de14b48 ReleaseNotes: The fast-math-flags changes
By Sanjay Patel!

llvm-svn: 325776
2018-02-22 13:24:27 +00:00
Hans Wennborg
1c2fc12847 Merging r325687:
------------------------------------------------------------------------
r325687 | sbaranga | 2018-02-21 16:20:32 +0100 (Wed, 21 Feb 2018) | 8 lines

[SCEV] Temporarily disable loop versioning for the purpose
of turning SCEVUnknowns of PHIs into AddRecExprs.

This feature is now hidden behind the -scev-version-unknown flag.

Fixes PR36032 and PR35432.


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

llvm-svn: 325773
2018-02-22 12:53:02 +00:00
Hans Wennborg
bbda6e325c Merging r325739:
------------------------------------------------------------------------
r325739 | nemanjai | 2018-02-22 04:02:41 +0100 (Thu, 22 Feb 2018) | 9 lines

[PowerPC] Do not produce invalid CTR loop with an FRem

An FRem instruction inside a loop should prevent the loop from being converted
into a CTR loop since this is not an operation that is legal on any PPC
subtarget. This will always be a call to a library function which means the
loop will be invalid if this instruction is in the body.

Fixes PR36292.

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

llvm-svn: 325767
2018-02-22 11:29:35 +00:00
Hans Wennborg
559169d2f3 Merging r324308:
------------------------------------------------------------------------
r324308 | rtrieu | 2018-02-06 03:58:21 +0100 (Tue, 06 Feb 2018) | 4 lines

Fix crash on invalid.

Don't call a method when the pointer is null.

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

llvm-svn: 325766
2018-02-22 11:25:29 +00:00
Hans Wennborg
cfe9b24521 Revert r324100 "Merging r324043: Fix typo: --nopie -> --no-pie."
This broke OpenBSD (PR36423). Revert back to the behaviour at the branch point
(which matches lld 5.0.1) and let the correct spelling be figured out on trunk.

llvm-svn: 325759
2018-02-22 09:26:44 +00:00
Hans Wennborg
90e00e4763 Merging r325714:
------------------------------------------------------------------------
r325714 | ruiu | 2018-02-21 21:08:14 +0100 (Wed, 21 Feb 2018) | 6 lines

Revert r325679: [ELF] Add -nopie alias for -no-pie (PR36423)

This reverts commit r325679 that was committed without discussion.
Actually, in the discussion thread, most people opposed to have this
option in lld. Reverting that change doesn't mean that this is a
final decision, but that needs to be discussed first.
------------------------------------------------------------------------

llvm-svn: 325758
2018-02-22 09:23:24 +00:00
Simon Atanasyan
93feb90b0f [docs][mips] Add MIPS specific release notes for LLD 6.0
Differential Revision: https://reviews.llvm.org/D43575

llvm-svn: 325721
2018-02-21 21:16:53 +00:00
Craig Topper
dd6e36c513 [ReleaseNotes] Initial release notes for X86 target.
llvm-svn: 325709
2018-02-21 19:27:01 +00:00
Hans Wennborg
f5b0723586 Merging r325679:
------------------------------------------------------------------------
r325679 | hans | 2018-02-21 14:54:26 +0100 (Wed, 21 Feb 2018) | 9 lines

[ELF] Add -nopie alias for -no-pie (PR36423)

In r324043, --nopie was renamed to --no-pie to presumably fix a typo.

As it turns out, "nopie" wasn't a typo but the spelling used by
OpenBSD's binutils ld. Gold on the other hand spells the flag "no-pie".
(Vanilla binutils doesn't have a flag like this at all.)

Since they do the same thing, let's support both spellings.
------------------------------------------------------------------------

llvm-svn: 325680
2018-02-21 13:58:32 +00:00
Hans Wennborg
379a6f8895 Merging r325655:
------------------------------------------------------------------------
r325655 | ctopper | 2018-02-21 01:16:50 +0100 (Wed, 21 Feb 2018) | 8 lines

[X86] Disable CLWB in Cannon Lake

Cannon Lake does not support CLWB, therefore it
does not include all features listed under SKX.

Patch by Gabor Buella

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

llvm-svn: 325672
2018-02-21 11:14:24 +00:00
Hans Wennborg
0bb5f10a0a Merging r325654:
------------------------------------------------------------------------
r325654 | ctopper | 2018-02-21 01:15:48 +0100 (Wed, 21 Feb 2018) | 10 lines

[X86] Disable CLWB for Cannon Lake

Cannon Lake does not support CLWB, therefore it
does not include all features listed under SKX anymore.

Instead, enumerate all SKX features with the exception of CLWB.

Patch by Gabor Buella

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

llvm-svn: 325671
2018-02-21 11:11:33 +00:00
Hans Wennborg
03a7173799 ReleaseNotes: mention NetBSD support for sanitizers
By Kamil Rytarowski!

llvm-svn: 325670
2018-02-21 10:50:27 +00:00
Hans Wennborg
59108acabb [AArch64][GlobalISel] Support G_INSERT/G_EXTRACT of types < s32 bits.
These are needed for operations on fp16 types in a later patch.

This also re-instates the test/CodeGen/AArch64/GlobalISel/fp16-copy-gpr.mir
test that was deleted which depended on this patch.

(See PR36345.)

llvm-svn: 325669
2018-02-21 10:25:22 +00:00
Hans Wennborg
0c14c94253 Merging r325525:
------------------------------------------------------------------------
r325525 | steven_wu | 2018-02-19 20:22:28 +0100 (Mon, 19 Feb 2018) | 13 lines

bitcode support change for fast flags compatibility

Summary: The discussion and as per need, each vendor needs a way to keep the old fast flags and the new fast flags in the auto upgrade path of the IR upgrader.  This revision addresses that issue.

Patched by Michael Berg

Reviewers: qcolombet, hans, steven_wu

Reviewed By: qcolombet, steven_wu

Subscribers: dexonsmith, vsk, mehdi_amini, andrewrk, MatzeB, wristow, spatel

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

llvm-svn: 325592
2018-02-20 16:22:18 +00:00
Hans Wennborg
5bd09dd59c Merging r325550:
I couldn't get fp16-copy-gpr.mir to pass after merging so I removed it until
aemerson; the other test I re-generated and it seems to work.

------------------------------------------------------------------------
r325550 | aemerson | 2018-02-20 06:11:57 +0100 (Tue, 20 Feb 2018) | 7 lines

[AArch64][GlobalISel] When copying from a gpr32 to an fpr16 reg, convert to fpr32 first.

This is a follow on commit to r[x] where we fix the other direction of copy.
For this case, after converting the source from gpr32 -> fpr32, we use a
subregister copy, which is essentially what EXTRACT_SUBREG does in SDAG land.

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

llvm-svn: 325591
2018-02-20 16:18:57 +00:00
Hans Wennborg
855905c2e7 Merging r325463:
(I had to re-generate the test and manually update to handle the r323922 MIR physical register sigil.

------------------------------------------------------------------------
r325463 | aemerson | 2018-02-18 18:10:49 +0100 (Sun, 18 Feb 2018) | 8 lines

[AArch64][GlobalISel] Fix an assert fail/miscompile when fp16 types are copied
to gpr register banks.

PR36345.

rdar://36478867

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

llvm-svn: 325586
2018-02-20 15:49:15 +00:00
Hans Wennborg
69b3f16a5a Merging r324110:
------------------------------------------------------------------------
r324110 | aemerson | 2018-02-02 19:03:30 +0100 (Fri, 02 Feb 2018) | 3 lines

[AArch64][GlobalISel] Use getRegClassForTypeOnBank() in selectCopy.

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

llvm-svn: 325584
2018-02-20 15:34:56 +00:00
Hans Wennborg
5f967a7248 Merging r325576:
------------------------------------------------------------------------
r325576 | hans | 2018-02-20 13:43:02 +0100 (Tue, 20 Feb 2018) | 13 lines

Revert r325375 "[MS] Make constexpr static data members implicitly inline"

This broke Clang bootstrap on Windows, PR36453.

> This handles them exactly the same way that we handle const integral
> static data members with inline definitions, which is what MSVC does.
>
> As a follow-up, now that we have a way to mark variables inline in the
> AST, we should consider marking them implicitly inline there instead of
> only treating them as inline in CodeGen. Unfortunately, this breaks a
> lot of dllimport test cases, so that is future work for now.
>
> Fixes PR36125.
------------------------------------------------------------------------

llvm-svn: 325577
2018-02-20 12:45:43 +00:00
Hans Wennborg
337d926f3b Merging r325511:
------------------------------------------------------------------------
r325511 | labath | 2018-02-19 16:42:48 +0100 (Mon, 19 Feb 2018) | 14 lines

Fix TestStopReplyContainsThreadPcs on 32-bit x86 (pr36013)

Summary:
The issue was that we were parsing the registers into 64-bit integers
and the calling swapByteOrder without regard for the actual size of the
register. This switches the test to use the RegisterValue class which
tracks the register size, and knows how to initialize itself from a
piece of memory (so we don't need to swap byte order ourselves).

Reviewers: eugene, davide

Subscribers: lldb-commits

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

llvm-svn: 325563
2018-02-20 08:49:24 +00:00
Hans Wennborg
d8bd15396c Merging r324722:
------------------------------------------------------------------------
r324722 | labath | 2018-02-09 10:40:03 +0100 (Fri, 09 Feb 2018) | 17 lines

llgs-test: Parse and store register info recieved from lldb-server

Summary:
Right now the test client is not parsing register values correctly,
which is manifesting itself in one test failing on 32-bit architectures
(pr36013). This parses the information from the qRegisterInfo packets
and stores it in the client, which will enable fixing the parsing in a
follow up commit.

I am also adding a new templated SendMessage overload, which enables one
to send a message get a parsed response in a single call.

Reviewers: eugene, davide

Subscribers: lldb-commits

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

llvm-svn: 325562
2018-02-20 08:48:26 +00:00
Hans Wennborg
68344bec97 Merging r324195:
------------------------------------------------------------------------
r324195 | mcrosier | 2018-02-04 16:42:24 +0100 (Sun, 04 Feb 2018) | 12 lines

[LV] Use Demanded Bits and ValueTracking for reduction type-shrinking

The type-shrinking logic in reduction detection, although narrow in scope, is
also rather ad-hoc, which has led to bugs (e.g., PR35734). This patch modifies
the approach to rely on the demanded bits and value tracking analyses, if
available. We currently perform type-shrinking separately for reductions and
other instructions in the loop. Long-term, we should probably think about
computing minimal bit widths in a more complete way for the loops we want to
vectorize.

PR35734
Differential Revision: https://reviews.llvm.org/D42309
------------------------------------------------------------------------

llvm-svn: 325508
2018-02-19 15:24:45 +00:00
Hans Wennborg
6405848242 Merging r324916:
------------------------------------------------------------------------
r324916 | junbuml | 2018-02-12 18:56:55 +0100 (Mon, 12 Feb 2018) | 7 lines

[LICM] update BlockColors after splitting predecessors

Update BlockColors after splitting predecessors. Do not allow splitting
EHPad for sinking when the BlockColors is not empty, so we can
simply assign predecessor's color to the new block.

Fixes PR36184
------------------------------------------------------------------------

llvm-svn: 325507
2018-02-19 15:20:11 +00:00
Hans Wennborg
1092e49011 Merging r325204:
------------------------------------------------------------------------
r325204 | ruiu | 2018-02-15 03:40:58 +0100 (Thu, 15 Feb 2018) | 7 lines

Fix an issue that lld drops symbol versions for -r.

When we are emitting a relocatable output, we should keep the original
symbol name including "@" part. Previously, we drop that part unconditionally
which resulted in dropping versions from symbols.

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

llvm-svn: 325502
2018-02-19 14:20:30 +00:00
Hans Wennborg
a641957c6e Merging r325148:
------------------------------------------------------------------------
r325148 | ctopper | 2018-02-14 19:08:33 +0100 (Wed, 14 Feb 2018) | 7 lines

[InstCombine] Don't fold select(C, Z, binop(select(C, X, Y), W)) -> select(C, Z, binop(Y, W)) if the binop is rem or div.

The select may have been preventing a division by zero or INT_MIN/-1 so removing it might not be safe.

Fixes PR36362.

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

llvm-svn: 325501
2018-02-19 14:16:41 +00:00
Hans Wennborg
4c336031d9 Merging r325375:
------------------------------------------------------------------------
r325375 | rnk | 2018-02-16 20:44:47 +0100 (Fri, 16 Feb 2018) | 11 lines

[MS] Make constexpr static data members implicitly inline

This handles them exactly the same way that we handle const integral
static data members with inline definitions, which is what MSVC does.

As a follow-up, now that we have a way to mark variables inline in the
AST, we should consider marking them implicitly inline there instead of
only treating them as inline in CodeGen. Unfortunately, this breaks a
lot of dllimport test cases, so that is future work for now.

Fixes PR36125.
------------------------------------------------------------------------

llvm-svn: 325500
2018-02-19 14:07:28 +00:00
Hans Wennborg
2dc96ae2f8 Merging r325168:
------------------------------------------------------------------------
r325168 | rksimon | 2018-02-14 21:43:47 +0100 (Wed, 14 Feb 2018) | 1 line

Removed superfluous semicolon to fix -Wpedantic gcc warning. NFCI.
------------------------------------------------------------------------

llvm-svn: 325498
2018-02-19 13:58:07 +00:00
Hans Wennborg
af2b558e6a Merging r324353:
------------------------------------------------------------------------
r324353 | mareko | 2018-02-06 16:17:55 +0100 (Tue, 06 Feb 2018) | 5 lines

AMDGPU: Fix S_BUFFER_LOAD_DWORD_SGPR moveToVALU

Author: Bas Nieuwenhuizen

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

llvm-svn: 325497
2018-02-19 13:55:23 +00:00
Hans Wennborg
08f5a48dc4 Merging r325139:
------------------------------------------------------------------------
r325139 | rafael | 2018-02-14 17:34:27 +0100 (Wed, 14 Feb 2018) | 12 lines

Store defined macros in MCContext.

So that macros defined in inline assembly blocks are available to the
whole file.

This provides a consistent behavior with other assembly directives,
since equations for example are already preserved between inline
assembly blocks.

PR: 36110

Patch by Roger!
------------------------------------------------------------------------

llvm-svn: 325330
2018-02-16 10:11:29 +00:00
Hans Wennborg
e0d9cb45ec Merging r325218:
------------------------------------------------------------------------
r325218 | hahnfeld | 2018-02-15 09:10:22 +0100 (Thu, 15 Feb 2018) | 10 lines

[CMake] Add -fno-experimental-isel for testing

GlobalISel doesn't yet implement blockaddress and falls back to
SelectionDAG. This results in additional branch instruction to
the next basic block which breaks the OMPT tests.
Disable GlobalISel for now when compiling the tests because fixing
them is not easily possible. See http://llvm.org/PR36313 for full
discussion history.

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

llvm-svn: 325230
2018-02-15 11:00:24 +00:00
Hans Wennborg
f9bd38d6f0 Merging r325147:
------------------------------------------------------------------------
r325147 | marshall | 2018-02-14 19:05:25 +0100 (Wed, 14 Feb 2018) | 3 lines

Add a catch for std::length_error for the case where the string can't handle 2GB. (like say 32-bit big-endian)


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

llvm-svn: 325219
2018-02-15 08:22:12 +00:00
Hans Wennborg
e2193b9c6e Revert r319777 for PR36357
llvm-svn: 325114
2018-02-14 10:54:05 +00:00
Hans Wennborg
a4783a3f5c Revert r319778 (and r319911) due to PR36357
llvm-svn: 325112
2018-02-14 10:51:00 +00:00
Hans Wennborg
bcc414370f Revert r320917 for PR36357
llvm-svn: 325111
2018-02-14 10:43:24 +00:00
Hans Wennborg
2375040d91 Merging r324962: (only the first hunk; see PR36375)
------------------------------------------------------------------------
r324962 | kuhar | 2018-02-13 00:37:27 +0100 (Tue, 13 Feb 2018) | 16 lines

[Dominators] Always recalculate postdominators when update yields different roots

Summary:
This patch makes postdominators always recalculate the tree when an update causes to change the tree roots.
As @dmgreen noticed in [[ https://reviews.llvm.org/D41298 | D41298 ]], the previous implementation was not conservative enough and it was possible to end up with a PostDomTree that was different than a freshly computed one.
The patch also compares postdominators with a freshly computed tree at the end of full verification to make sure we don't hit similar issues in the future.

This should (ideally) be also backported to 6.0 before the release, although I don't have any reports of this causing an observable error. It should be safe to do it even if it's late in the release, as the change only makes the current behavior more conservative.

Reviewers: dmgreen, dberlin, davide, brzycki, grosser

Reviewed By: brzycki, grosser

Subscribers: llvm-commits, dmgreen

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

llvm-svn: 325108
2018-02-14 10:16:43 +00:00
Hans Wennborg
34684f1531 Merging r324576:
------------------------------------------------------------------------
r324576 | ctopper | 2018-02-08 08:45:55 +0100 (Thu, 08 Feb 2018) | 20 lines

[X86] Don't emit KTEST instructions unless only the Z flag is being used

Summary:
KTEST has weird flag behavior. The Z flag is set for all bits in the AND of the k-registers being 0, and the C flag is set for all bits being 1. All other flags are cleared.

We currently emit this instruction in EmitTEST and don't check the condition code. This can lead to strange things like using the S flag after a KTEST for a signed compare.

The domain reassignment pass can also transform TEST instructions into KTEST and is not protected against the flag usage either. For now I've disabled this part of the domain reassignment pass. I tried to comment out the checks in the mir test so that we could recover them later, but I couldn't figure out how to get that to work.

This patch moves the KTEST handling into LowerSETCC and now creates a ktest+x86setcc. I've chosen this approach because I'd like to add support for the C flag for all ones in a followup patch. To do that requires that I can rewrite the condition code going in the x86setcc to be different than the original SETCC condition code.

This fixes PR36182. I'll file a PR to fix domain reassignment once this goes in. Should this be merged to 6.0?

Reviewers: spatel, guyblank, RKSimon, zvi

Reviewed By: guyblank

Subscribers: llvm-commits

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

llvm-svn: 325106
2018-02-14 09:44:17 +00:00
Hans Wennborg
c1957e6e1a Merging r324497:
------------------------------------------------------------------------
r324497 | ctopper | 2018-02-07 19:32:15 +0100 (Wed, 07 Feb 2018) | 1 line

[X86] Regenerate test using update_mir_test_checks.py. NFC
------------------------------------------------------------------------

llvm-svn: 325105
2018-02-14 09:41:04 +00:00
Hans Wennborg
e084cf4889 Merging r323998:
------------------------------------------------------------------------
r323998 | rsmith | 2018-02-01 21:01:49 +0100 (Thu, 01 Feb 2018) | 5 lines

PR36157: When injecting an implicit function declaration in C89, find the right
DeclContext rather than injecting it wherever we happen to be.

This avoids creating functions whose DeclContext is a struct or similar.

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

llvm-svn: 325104
2018-02-14 09:06:43 +00:00
Reid Kleckner
5a103280fa Merging r325085:
------------------------------------------------------------------------
r325085 | rnk | 2018-02-13 16:24:29 -0800 (Tue, 13 Feb 2018) | 3 lines

[X86] Remove dead code from retpoline thunk generation

Follow-up to r325049
------------------------------------------------------------------------

llvm-svn: 325086
2018-02-14 00:26:04 +00:00
Reid Kleckner
fb6af5ed0d Merging r325049:
------------------------------------------------------------------------
r325049 | rnk | 2018-02-13 12:47:49 -0800 (Tue, 13 Feb 2018) | 17 lines

[X86] Use EDI for retpoline when no scratch regs are left

Summary:
Instead of solving the hard problem of how to pass the callee to the indirect
jump thunk without a register, just use a CSR. At a call boundary, there's
nothing stopping us from using a CSR to hold the callee as long as we save and
restore it in the prologue.

Also, add tests for this mregparm=3 case. I wrote execution tests for
__llvm_retpoline_push, but they never got committed as lit tests, either
because I never rewrote them or because they got lost in merge conflicts.

Reviewers: chandlerc, dwmw2

Subscribers: javed.absar, kristof.beyls, hiraditya, llvm-commits

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

llvm-svn: 325084
2018-02-14 00:22:20 +00:00
Reid Kleckner
f7882e27b1 Merging r324645:
------------------------------------------------------------------------
r324645 | dwmw2 | 2018-02-08 12:06:05 -0800 (Thu, 08 Feb 2018) | 5 lines

[X86] Support 'V' register operand modifier

This allows the register name to be printed without the leading '%'.
This can be used for emitting calls to the retpoline thunks from inline
asm.
------------------------------------------------------------------------

llvm-svn: 325083
2018-02-14 00:19:26 +00:00
Reid Kleckner
008c81fd1e Merging r324449:
------------------------------------------------------------------------
r324449 | chandlerc | 2018-02-06 22:16:24 -0800 (Tue, 06 Feb 2018) | 15 lines

[x86/retpoline] Make the external thunk names exactly match the names
that happened to end up in GCC.

This is really unfortunate, as the names don't have much rhyme or reason
to them. Originally in the discussions it seemed fine to rely on aliases
to map different names to whatever external thunk code developers wished
to use but there are practical problems with that in the kernel it turns
out. And since we're discovering this practical problems late and since
GCC has already shipped a release with one set of names, we are forced,
yet again, to blindly match what is there.

Somewhat rushing this patch out for the Linux kernel folks to test and
so we can get it patched into our releases.

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

llvm-svn: 325082
2018-02-14 00:18:17 +00:00
Hans Wennborg
cbdaf87ef8 Merging r324746:
------------------------------------------------------------------------
r324746 | arsenm | 2018-02-09 17:57:48 +0100 (Fri, 09 Feb 2018) | 4 lines

AMDGPU: Fix layering issue

Move utility function that depends on codegen.
Fixes build with r324487 reapplied.
------------------------------------------------------------------------

llvm-svn: 325007
2018-02-13 14:42:24 +00:00
Hans Wennborg
4ba2ed47cb Merging r323893 and r323895:
------------------------------------------------------------------------
r323893 | colden | 2018-01-31 18:48:04 +0100 (Wed, 31 Jan 2018) | 11 lines

[LLD][PDB] Implement FIXME: Warn on missing TypeServer PDB rather than error

Summary: Instead of fatal-ing out when missing a type server PDB, insead warn and cache the miss.

Reviewers: rnk, zturner

Reviewed By: rnk

Subscribers: llvm-commits

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

------------------------------------------------------------------------
r323895 | colden | 2018-01-31 19:16:13 +0100 (Wed, 31 Jan 2018) | 1 line

[PDB] Fix test failures due to expected warning not matching actual warning text
------------------------------------------------------------------------

llvm-svn: 325005
2018-02-13 14:01:21 +00:00
Hans Wennborg
396a3082d9 Merging r324772:
------------------------------------------------------------------------
r324772 | thegameg | 2018-02-09 22:47:07 +0100 (Fri, 09 Feb 2018) | 25 lines

[X86][MC] Fix assembling rip-relative addressing + immediate displacements

In the rare case where the input contains rip-relative addressing with
immediate displacements, *and* the instruction ends with an immediate,
we encode the instruction in the wrong way:

movl $12345678, 0x400(%rdi) // all good, no rip-relative addr
movl %eax, 0x400(%rip) // all good, no immediate at the end of the instruction
movl $12345678, 0x400(%rip) // fails, encodes address as 0x3fc(%rip)

Offset is a label:

movl $12345678, foo(%rip)

we want to account for the size of the immediate (in this case,
$12345678, 4 bytes).

Offset is an immediate:

movl $12345678, 0x400(%rip)

we should not account for the size of the immediate, assuming the
immediate offset is what the user wanted.

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

llvm-svn: 324875
2018-02-12 10:14:34 +00:00
Hans Wennborg
6952b01dac Merging r324855:
------------------------------------------------------------------------
r324855 | dim | 2018-02-11 23:31:05 +0100 (Sun, 11 Feb 2018) | 26 lines

Add default C++ ABI libname and include paths for FreeBSD

Summary:
As noted in a discussion about testing the LLVM 6.0.0 release candidates
(with libc++) for FreeBSD, many tests turned out to fail with
"exception_ptr not yet implemented".  This was because libc++ did not
choose the correct C++ ABI library, and therefore it fell back to the
`exception_fallback.ipp` header.

Since FreeBSD 10.x, we have been using libcxxrt as our C++ ABI library,
and its headers have always been installed in /usr/include/c++/v1,
together with the (system) libc++ headers.  (Older versions of FreeBSD
used GNU libsupc++ by default, but these are now unsupported.)

Therefore, if we are building libc++ for FreeBSD, set:
* `LIBCXX_CXX_ABI_LIBNAME` to "libcxxrt"
* `LIBCXX_CXX_ABI_INCLUDE_PATHS` to "/usr/include/c++/v1"
by default.

Reviewers: emaste, EricWF, mclow.lists

Reviewed By: EricWF

Subscribers: mgorny, cfe-commits, krytarowski

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

llvm-svn: 324874
2018-02-12 10:04:36 +00:00
Hans Wennborg
709beeee02 Merging r323041:
------------------------------------------------------------------------
r323041 | dim | 2018-01-20 15:35:05 +0100 (Sat, 20 Jan 2018) | 5 lines

Assume the shared library path variable is LD_LIBRARY_PATH on systems
except Darwin and Windows.  This prevents inserting an environment
variable with an empty name (which is illegal and leads to a Python
exception) on any of the BSDs.

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

llvm-svn: 324872
2018-02-12 09:56:09 +00:00
Hans Wennborg
1f1deb5a9c Merging r323040:
------------------------------------------------------------------------
r323040 | dim | 2018-01-20 15:34:33 +0100 (Sat, 20 Jan 2018) | 5 lines

Assume the shared library path variable is LD_LIBRARY_PATH on systems
except Darwin and Windows.  This prevents inserting an environment
variable with an empty name (which is illegal and leads to a Python
exception) on any of the BSDs.

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

llvm-svn: 324871
2018-02-12 09:54:35 +00:00
Ulrich Weigand
9a78396585 [ReleaseNotes] Add SystemZ target section
llvm-svn: 324726
2018-02-09 10:28:27 +00:00
Hans Wennborg
94750558de Merging r321911:
------------------------------------------------------------------------
r321911 | amccarth | 2018-01-06 00:01:04 +0100 (Sat, 06 Jan 2018) | 9 lines

Re-land "Fix faulty assertion in debug info"

This had been reverted because the new test failed on non-X86 bots.  I moved
the new test to the appropriate subdirectory to correct this.

Differential Revision: https://reviews.llvm.org/D41264
Original submission:  r321122 (which was reverted by r321125)

This reverts commit 3c1639b5703c387a0d8cba2862803b4e68dff436.
------------------------------------------------------------------------

llvm-svn: 324723
2018-02-09 10:04:11 +00:00
Hans Wennborg
86fed00ca5 Merging r324537:
------------------------------------------------------------------------
r324537 | rsmith | 2018-02-07 23:25:16 +0100 (Wed, 07 Feb 2018) | 14 lines

PR36055: fix computation of *-dependence in nested initializer lists.

When we synthesize an implicit inner initializer list when analyzing an outer
initializer list, we add it to the outer list immediately, and then fill in the
inner list. This gives the outer list no chance to update its *-dependence bits
with those of the completed inner list. To fix this, re-add the inner list to
the outer list once it's completed.

Note that we do not recompute the *-dependence bits from scratch when we
complete an outer list; this would give the wrong result for the case where a
designated initializer overwrites a dependent initializer with a non-dependent
one. The resulting list in that case should still be dependent, even though all
traces of the dependence were removed from the semantic form.

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

llvm-svn: 324719
2018-02-09 09:04:00 +00:00
Hans Wennborg
69fdf9a496 Merging r324594:
------------------------------------------------------------------------
r324594 | aivchenk | 2018-02-08 12:15:21 +0100 (Thu, 08 Feb 2018) | 17 lines

Fix for #31362 - ms_abi is implemented incorrectly for values >=16 bytes.

Summary:
This patch is a fix for following issue:
https://bugs.llvm.org/show_bug.cgi?id=31362 The problem was caused by front end
lowering C calling conventions without taking into account calling conventions
enforced by attribute. In this case win64cc was no correctly lowered on targets
other than Windows.

Reviewed By: rnk (Reid Kleckner)

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

Author: belickim <mateusz.belicki@intel.com>



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

llvm-svn: 324718
2018-02-09 09:01:30 +00:00
Hans Wennborg
275fae5078 Merging r324514:
------------------------------------------------------------------------
r324514 | arphaman | 2018-02-07 21:45:39 +0100 (Wed, 07 Feb 2018) | 10 lines

[PR36008] Avoid -Wsign-compare warning for enum constants in
typeof expressions

This commit looks through typeof type at the original expression when diagnosing
-Wsign-compare to avoid an unfriendly diagnostic.

rdar://36588828

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

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

llvm-svn: 324602
2018-02-08 13:19:38 +00:00
Hans Wennborg
f7d393ccb1 Merging r324419:
------------------------------------------------------------------------
r324419 | vsapsai | 2018-02-06 23:39:25 +0100 (Tue, 06 Feb 2018) | 23 lines

[Lex] Fix handling numerical literals ending with ' and signed exponent.

For input `0'e+1` lexer tokenized as numeric constant only `0'e`. Later
NumericLiteralParser skipped 0 and ' as digits and parsed `e+1` as valid
exponent going past the end of the token. Because it didn't mark numeric
literal as having an error, it continued parsing and tried to expandUCNs
with StringRef of length -2.

The fix is not to parse exponent when we reached the end of token.

Discovered by OSS-Fuzz:
https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=4588

rdar://problem/36076719

Reviewers: rsmith, t.p.northover

Reviewed By: rsmith

Subscribers: cfe-commits, jkorous-apple

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

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

llvm-svn: 324579
2018-02-08 08:17:53 +00:00
Hans Wennborg
2adffe13e8 fix sphinx warning
llvm-svn: 324578
2018-02-08 08:13:23 +00:00
Hans Wennborg
5aa8942f65 Merging r324496:
------------------------------------------------------------------------
r324496 | yroux | 2018-02-07 19:27:25 +0100 (Wed, 07 Feb 2018) | 9 lines

[asan] Fix filename size on linux platforms.

This is a a fix for:
https://bugs.llvm.org/show_bug.cgi?id=35996

Use filename limits from system headers to be synchronized with what
LD_PRELOAD can handle.

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

llvm-svn: 324506
2018-02-07 19:51:13 +00:00
Hans Wennborg
79ce1bb135 Merging r324467 and r324468:
------------------------------------------------------------------------
r324467 | atanasyan | 2018-02-07 11:02:49 +0100 (Wed, 07 Feb 2018) | 9 lines

[ELF][MIPS] Ignore incorrect version definition index for _gp_disp symbol

MIPS BFD linker puts _gp_disp symbol into DSO files and assigns zero
version definition index to it. This value means 'unversioned local
symbol' while _gp_disp is a section global symbol. We have to handle
this bug in the LLD because BFD linker is used for building MIPS
toolchain libraries.

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

------------------------------------------------------------------------
r324468 | atanasyan | 2018-02-07 11:14:22 +0100 (Wed, 07 Feb 2018) | 1 line

[ELF][MIPS] Mark the test as required MIPS target support. NFC
------------------------------------------------------------------------

llvm-svn: 324471
2018-02-07 10:50:49 +00:00
Hans Wennborg
43ba9d9f5d Merging r324422:
------------------------------------------------------------------------
r324422 | efriedma | 2018-02-07 00:00:17 +0100 (Wed, 07 Feb 2018) | 16 lines

[LivePhysRegs] Fix handling of return instructions.

See D42509 for the original version of this.

Basically, there are two significant changes to behavior here:

- addLiveOuts always adds all pristine registers (even if a block has
no successors).
- addLiveOuts and addLiveOutsNoPristines always add all callee-saved
registers for return blocks (including conditional return blocks).

I cleaned up the functions a bit to make it clear these properties hold.

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


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

llvm-svn: 324466
2018-02-07 10:01:03 +00:00
Hans Wennborg
0e83d68744 Merging r324439:
------------------------------------------------------------------------
r324439 | compnerd | 2018-02-07 02:55:08 +0100 (Wed, 07 Feb 2018) | 5 lines

AST: support SwiftCC on MS ABI

Microsoft has reserved the identifier 'S' as the swift calling
convention.  Decorate the symbols appropriately.  This enables swift on
Windows.
------------------------------------------------------------------------

llvm-svn: 324460
2018-02-07 08:56:35 +00:00
Hans Wennborg
34c856a87c Merging r324153:
------------------------------------------------------------------------
r324153 | ericwf | 2018-02-02 23:39:59 +0100 (Fri, 02 Feb 2018) | 6 lines

Fix has_unique_object_representation after Clang commit r324134.

Clang previously reported an empty union as having a unique object
representation. This was incorrect and was fixed in a recent Clang commit.

This patch fixes the libc++ tests.
------------------------------------------------------------------------

llvm-svn: 324345
2018-02-06 13:22:33 +00:00
Hans Wennborg
a6dc176b8a Merging r324246:
------------------------------------------------------------------------
r324246 | mzeren-vmw | 2018-02-05 16:59:00 +0100 (Mon, 05 Feb 2018) | 33 lines

[clang-format] Re-land: Fixup #include guard indents after parseFile()

Summary:
When a preprocessor indent closes after the last line of normal code we do not
correctly fixup include guard indents. For example:

  #ifndef HEADER_H
  #define HEADER_H
  #if 1
  int i;
  #  define A 0
  #endif
  #endif

incorrectly reformats to:

  #ifndef HEADER_H
  #define HEADER_H
  #if 1
  int i;
  #    define A 0
  #  endif
  #endif

To resolve this issue we must fixup levels after parseFile(). Delaying
the fixup introduces a new state, so consolidate include guard search
state into an enum.

Reviewers: krasimir, klimek

Subscribers: cfe-commits

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

llvm-svn: 324331
2018-02-06 09:53:34 +00:00
Hans Wennborg
0b67f617c2 Merging r323904:
------------------------------------------------------------------------
r323904 | mzeren-vmw | 2018-01-31 21:05:50 +0100 (Wed, 31 Jan 2018) | 34 lines

[clang-format] Align preprocessor comments with #

Summary:
r312125, which introduced preprocessor indentation, shipped with a known
issue where "indentation of comments immediately before indented
preprocessor lines is toggled on each run". For example these two forms
toggle:

  #ifndef HEADER_H
  #define HEADER_H
  #if 1
  // comment
  #   define A 0
  #endif
  #endif

  #ifndef HEADER_H
  #define HEADER_H
  #if 1
     // comment
  #   define A 0
  #endif
  #endif

This happens because we check vertical alignment against the '#' yet
indent to the level of the 'define'. This patch resolves this issue by
aligning against the '#'.

Reviewers: krasimir, klimek, djasper

Reviewed By: krasimir

Subscribers: cfe-commits
Differential Revision: https://reviews.llvm.org/D42408
------------------------------------------------------------------------

llvm-svn: 324329
2018-02-06 09:51:45 +00:00
Hans Wennborg
d85785aa4b Merging r324234:
------------------------------------------------------------------------
r324234 | kamil | 2018-02-05 14:16:22 +0100 (Mon, 05 Feb 2018) | 29 lines

Fix a crash in *NetBSD::Factory::Launch

Summary:
We cannot call process_up->SetState() inside
the NativeProcessNetBSD::Factory::Launch
function because it triggers a NULL pointer
deference.

The generic code for launching a process in:
GDBRemoteCommunicationServerLLGS::LaunchProcess
sets the m_debugged_process_up pointer after
a successful call to  m_process_factory.Launch().
If we attempt to call process_up->SetState()
inside a platform specific Launch function we
end up dereferencing a NULL pointer in
NativeProcessProtocol::GetCurrentThreadID().

Use the proper call process_up->SetState(,false)
that sets notify_delegates to false.

Sponsored by <The NetBSD Foundation>

Reviewers: labath, joerg

Reviewed By: labath

Subscribers: lldb-commits

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

llvm-svn: 324327
2018-02-06 09:42:38 +00:00
Hans Wennborg
bdb1df16f1 Merging r324251:
------------------------------------------------------------------------
r324251 | kamil | 2018-02-05 18:12:23 +0100 (Mon, 05 Feb 2018) | 14 lines

Sync PlatformNetBSD.cpp with Linux

Summary:
Various changes in logging from log->Printf() to generic LLDB_LOG().

Sponsored by <The NetBSD Foundation>

Reviewers: labath, joerg

Reviewed By: labath

Subscribers: llvm-commits, lldb-commits

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

llvm-svn: 324326
2018-02-06 09:40:58 +00:00
Krzysztof Parzyszek
e337f23726 [Hexagon] Add release notes for 6.0.0
llvm-svn: 324248
2018-02-05 16:21:20 +00:00
Hans Wennborg
2cf6b5957d Merging r324059:
------------------------------------------------------------------------
r324059 | mstorsjo | 2018-02-02 07:22:35 +0100 (Fri, 02 Feb 2018) | 21 lines

[MinGW] Emit typeinfo locally for dllimported classes without key functions

This fixes building Qt as shared libraries with clang in MinGW
mode; previously subclasses of the QObjectData class (in other
DLLs than the base DLL) failed to find the typeinfo symbols
(that neither were emitted in the base DLL nor in the DLL
containing the subclass).

If the virtual destructor in the newly added testcase wouldn't
be pure (or if there'd be another non-pure virtual method),
it'd be a key function and things would work out even before this
change. Make sure to locally emit the typeinfo for these classes
as well.

This matches what GCC does in this specific testcase.

This fixes the root issue that spawned PR35146. (The difference
to GCC that is initially described in that bug still is present
though.)

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

llvm-svn: 324219
2018-02-05 09:59:14 +00:00
Hans Wennborg
4d860d297e Merging r324039: (test case modified to work around r323886 et al.)
------------------------------------------------------------------------
r324039 | matze | 2018-02-02 01:08:19 +0100 (Fri, 02 Feb 2018) | 33 lines

SplitKit: Fix liveness recomputation in some remat cases.

Example situation:
```
BB0:
  %0 = ...
  use %0
  ; ...
  condjump BB1
  jmp BB2

BB1:
  %0 = ...   ; rematerialized def from above (from earlier split step)
  jmp BB2

BB2:
  ; ...
  use %0
```

%0 will have a live interval with 3 value numbers (for the BB0, BB1 and
BB2 parts). Now SplitKit tries and succeeds in rematerializing the value
number in BB2 (This only works because it is a secondary split so
SplitKit is can trace this back to a single original def).

We need to recompute all live ranges affected by a value number that we
rematerialize. The case that we missed before is that when the value
that is rematerialized is at a join (Phi VNI) then we also have to
recompute liveness for the predecessor VNIs.

rdar://35699130

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

llvm-svn: 324218
2018-02-05 09:55:40 +00:00
Hans Wennborg
eb2e0b41c5 Merging r324002:
------------------------------------------------------------------------
r324002 | ctopper | 2018-02-01 21:48:50 +0100 (Thu, 01 Feb 2018) | 7 lines

[DAGCombiner] When folding (insert_subvector undef, (bitcast (extract_subvector N1, Idx)), Idx) -> (bitcast N1) make sure that N1 has the same total size as the original output

We were only checking the element count, but not the total width. This could cause illegal bitcasts to be created if for example the output was 512-bits, but N1 is 256 bits, and the extraction size was 128-bits.

Fixes PR36199

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

llvm-svn: 324216
2018-02-05 09:32:05 +00:00
Hans Wennborg
8be5478f0d Merging r323935:
------------------------------------------------------------------------
r323935 | rsmith | 2018-02-01 01:28:36 +0100 (Thu, 01 Feb 2018) | 5 lines

PR36181: Teach CodeGen to properly ignore requests to emit dependent entities.

Previously, friend function definitions within class templates slipped through
the gaps and caused the MS mangler to assert.

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

llvm-svn: 324215
2018-02-05 09:29:08 +00:00
Hans Wennborg
e71868f85f Merging r324134:
------------------------------------------------------------------------
r324134 | ericwf | 2018-02-02 21:30:39 +0100 (Fri, 02 Feb 2018) | 14 lines

Make __has_unique_object_representations reject empty union types.

Summary:
Clang incorrectly reports empty unions as having a unique object representation. However, this is not correct since `sizeof(EmptyUnion) == 1` AKA it has 8 bits of padding. Therefore it should be treated the same as an empty struct and report `false`.

@erichkeane also suggested this fix should be merged into the 6.0 release branch, so the initial release of `__has_unique_object_representations` is as bug-free as possible. 

Reviewers: erichkeane, rsmith, aaron.ballman, majnemer

Reviewed By: erichkeane

Subscribers: cfe-commits, erichkeane

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

llvm-svn: 324213
2018-02-05 08:56:25 +00:00
Hans Wennborg
3980c8df13 [ReleaseNotes] Add note for the new -fexperimental-isel flag.
Patch by Amara Emerson.

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

llvm-svn: 324212
2018-02-05 08:46:54 +00:00
Hans Wennborg
8ec2d86a22 [ReleaseNotes] Add note for enabling GlobalISel for AArch64 -O0
Patch by Amara Emerson.

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

llvm-svn: 324211
2018-02-05 08:41:01 +00:00
Hans Wennborg
0f84e3ee00 Merging r323908:
------------------------------------------------------------------------
r323908 | mareko | 2018-01-31 21:18:04 +0100 (Wed, 31 Jan 2018) | 7 lines

AMDGPU: Add intrinsics llvm.amdgcn.cvt.{pknorm.i16, pknorm.u16, pk.i16, pk.u16}

Reviewers: arsenm, nhaehnle

Subscribers: kzhuravl, wdng, yaxunl, dstuttard, tpr, t-tye

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

llvm-svn: 324103
2018-02-02 16:24:08 +00:00
Hans Wennborg
9b8da2b903 Merging r324043:
------------------------------------------------------------------------
r324043 | ruiu | 2018-02-02 01:31:05 +0100 (Fri, 02 Feb 2018) | 6 lines

Fix typo: --nopie -> --no-pie.

--nopie was a typo. GNU gold doesn't recognize it. It is also
inconsistent with other options that have --foo and --no-foo.

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

llvm-svn: 324100
2018-02-02 16:01:07 +00:00
Hans Wennborg
27021b8af9 Merging r323643:
------------------------------------------------------------------------
r323643 | jdevlieghere | 2018-01-29 13:10:32 +0100 (Mon, 29 Jan 2018) | 16 lines

[Sparc] Account for bias in stack readjustment

Summary: This was broken long ago in D12208, which failed to account for
the fact that 64-bit SPARC uses a stack bias of 2047, and it is the
*unbiased* value which should be aligned, not the biased one. This was
seen to be an issue with Rust.

Patch by: jrtc27 (James Clarke)

Reviewers: jyknight, venkatra

Reviewed By: jyknight

Subscribers: jacob_hansen, JDevlieghere, fhahn, fedor.sergeev, llvm-commits

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

llvm-svn: 324090
2018-02-02 13:56:46 +00:00
Hans Wennborg
3b724f6325 Merging r323909:
------------------------------------------------------------------------
r323909 | mareko | 2018-01-31 21:18:11 +0100 (Wed, 31 Jan 2018) | 13 lines

AMDGPU: Fold inline offset for loads properly in moveToVALU on GFX9

Summary:
This enables load merging into x2, x4, which is driven by inline offsets.

6500 shaders are affected:
Code Size in affected shaders: -15.14 %

Reviewers: arsenm, nhaehnle

Subscribers: kzhuravl, wdng, yaxunl, dstuttard, tpr, t-tye, llvm-commits

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

llvm-svn: 324089
2018-02-02 13:54:44 +00:00
Hans Wennborg
9f3da91df3 Merging r323907 and r323913:
------------------------------------------------------------------------
r323907 | mareko | 2018-01-31 21:17:52 +0100 (Wed, 31 Jan 2018) | 11 lines

[SeparateConstOffsetFromGEP] Preserve metadata when splitting GEPs

Summary:
!amdgpu.uniform needs to be preserved for AMDGPU, otherwise bad things
happen.

Reviewers: arsenm, nhaehnle, jingyue, broune, majnemer, bjarke.roune, dblaikie

Subscribers: wdng, tpr, llvm-commits

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

------------------------------------------------------------------------
r323913 | mareko | 2018-01-31 21:49:19 +0100 (Wed, 31 Jan 2018) | 1 line

[SeparateConstOffsetFromGEP] Fix up addrspace in the AMDGPU test
------------------------------------------------------------------------

llvm-svn: 324088
2018-02-02 13:50:25 +00:00
Hans Wennborg
87627246a7 Merging r323536:
------------------------------------------------------------------------
r323536 | arichardson | 2018-01-26 16:56:14 +0100 (Fri, 26 Jan 2018) | 11 lines

[MIPS] Don't crash on unsized extern types with -mgpopt

Summary: This fixes an assertion when building the FreeBSD MIPS64 kernel.

Reviewers: atanasyan, sdardis, emaste

Reviewed By: sdardis

Subscribers: krytarowski, llvm-commits

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

llvm-svn: 324087
2018-02-02 13:47:07 +00:00
Hans Wennborg
6291b0d954 Merging r323759:
------------------------------------------------------------------------
r323759 | spatel | 2018-01-30 14:53:59 +0100 (Tue, 30 Jan 2018) | 10 lines

[DSE] make sure memory is not modified before partial store merging (PR36129)

We missed a critical check in D30703. We must make sure that no intermediate 
store is sitting between the stores that we want to merge.

This should fix:
https://bugs.llvm.org/show_bug.cgi?id=36129

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

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

llvm-svn: 324086
2018-02-02 13:44:37 +00:00
Hans Wennborg
7a8cd3ecf9 Merging r323781:
------------------------------------------------------------------------
r323781 | sdardis | 2018-01-30 17:24:10 +0100 (Tue, 30 Jan 2018) | 15 lines

[mips] Fix incorrect sign extension for fpowi libcall

PR36061 showed that during the expansion of ISD::FPOWI, that there
was an incorrect zero extension of the integer argument which for
MIPS64 would then give incorrect results. Address this with the
existing mechanism for correcting sign extensions.

This resolves PR36061.

Thanks to James Cowgill for reporting the issue!

Reviewers: atanasyan, hfinkel

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

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

llvm-svn: 324085
2018-02-02 13:41:04 +00:00
Hans Wennborg
0085276370 Merging r323857:
------------------------------------------------------------------------
r323857 | rogfer01 | 2018-01-31 10:23:43 +0100 (Wed, 31 Jan 2018) | 19 lines

[ARM] Allow the scheduler to clone a node with glue to avoid a copy CPSR ↔ GPR.

In Thumb 1, with the new ADDCARRY / SUBCARRY the scheduler may need to do
copies CPSR ↔ GPR but not all Thumb1 targets implement them.

The schedule can attempt, before attempting a copy, to clone the instructions
but it does not currently do that for nodes with input glue. In this patch we
introduce a target-hook to let the hook decide if a glued machinenode is still
eligible for copying. In this case these are ARM::tADCS and ARM::tSBCS .

As a follow-up of this change we should actually implement the copies for the
Thumb1 targets that do implement them and restrict the hook to the targets that
can't really do such copy as these clones are not ideal.

This change fixes PR35836.

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


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

llvm-svn: 324082
2018-02-02 13:35:26 +00:00
Hans Wennborg
eeabe19e14 Merging r323915:
------------------------------------------------------------------------
r323915 | chandlerc | 2018-01-31 21:56:37 +0100 (Wed, 31 Jan 2018) | 17 lines

[x86] Make the retpoline thunk insertion a machine function pass.

Summary:
This removes the need for a machine module pass using some deeply
questionable hacks. This should address PR36123 which is a case where in
full LTO the memory usage of a machine module pass actually ended up
being significant.

We should revert this on trunk as soon as we understand and fix the
memory usage issue, but we should include this in any backports of
retpolines themselves.

Reviewers: echristo, MatzeB

Subscribers: sanjoy, mcrosier, mehdi_amini, hiraditya, llvm-commits

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

llvm-svn: 324071
2018-02-02 10:55:15 +00:00
Hans Wennborg
7df017020f Merging r323288:
------------------------------------------------------------------------
r323288 | ruiu | 2018-01-24 01:26:57 +0100 (Wed, 24 Jan 2018) | 3 lines

Fix retpoline PLT header size for i386.

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

llvm-svn: 324070
2018-02-02 10:52:13 +00:00
Hans Wennborg
f21e34b3a1 Merging r323155:
------------------------------------------------------------------------
r323155 | chandlerc | 2018-01-22 23:05:25 +0100 (Mon, 22 Jan 2018) | 133 lines

Introduce the "retpoline" x86 mitigation technique for variant #2 of the speculative execution vulnerabilities disclosed today, specifically identified by CVE-2017-5715, "Branch Target Injection", and is one of the two halves to Spectre..

Summary:
First, we need to explain the core of the vulnerability. Note that this
is a very incomplete description, please see the Project Zero blog post
for details:
https://googleprojectzero.blogspot.com/2018/01/reading-privileged-memory-with-side.html

The basis for branch target injection is to direct speculative execution
of the processor to some "gadget" of executable code by poisoning the
prediction of indirect branches with the address of that gadget. The
gadget in turn contains an operation that provides a side channel for
reading data. Most commonly, this will look like a load of secret data
followed by a branch on the loaded value and then a load of some
predictable cache line. The attacker then uses timing of the processors
cache to determine which direction the branch took *in the speculative
execution*, and in turn what one bit of the loaded value was. Due to the
nature of these timing side channels and the branch predictor on Intel
processors, this allows an attacker to leak data only accessible to
a privileged domain (like the kernel) back into an unprivileged domain.

The goal is simple: avoid generating code which contains an indirect
branch that could have its prediction poisoned by an attacker. In many
cases, the compiler can simply use directed conditional branches and
a small search tree. LLVM already has support for lowering switches in
this way and the first step of this patch is to disable jump-table
lowering of switches and introduce a pass to rewrite explicit indirectbr
sequences into a switch over integers.

However, there is no fully general alternative to indirect calls. We
introduce a new construct we call a "retpoline" to implement indirect
calls in a non-speculatable way. It can be thought of loosely as
a trampoline for indirect calls which uses the RET instruction on x86.
Further, we arrange for a specific call->ret sequence which ensures the
processor predicts the return to go to a controlled, known location. The
retpoline then "smashes" the return address pushed onto the stack by the
call with the desired target of the original indirect call. The result
is a predicted return to the next instruction after a call (which can be
used to trap speculative execution within an infinite loop) and an
actual indirect branch to an arbitrary address.

On 64-bit x86 ABIs, this is especially easily done in the compiler by
using a guaranteed scratch register to pass the target into this device.
For 32-bit ABIs there isn't a guaranteed scratch register and so several
different retpoline variants are introduced to use a scratch register if
one is available in the calling convention and to otherwise use direct
stack push/pop sequences to pass the target address.

This "retpoline" mitigation is fully described in the following blog
post: https://support.google.com/faqs/answer/7625886

We also support a target feature that disables emission of the retpoline
thunk by the compiler to allow for custom thunks if users want them.
These are particularly useful in environments like kernels that
routinely do hot-patching on boot and want to hot-patch their thunk to
different code sequences. They can write this custom thunk and use
`-mretpoline-external-thunk` *in addition* to `-mretpoline`. In this
case, on x86-64 thu thunk names must be:
```
  __llvm_external_retpoline_r11
```
or on 32-bit:
```
  __llvm_external_retpoline_eax
  __llvm_external_retpoline_ecx
  __llvm_external_retpoline_edx
  __llvm_external_retpoline_push
```
And the target of the retpoline is passed in the named register, or in
the case of the `push` suffix on the top of the stack via a `pushl`
instruction.

There is one other important source of indirect branches in x86 ELF
binaries: the PLT. These patches also include support for LLD to
generate PLT entries that perform a retpoline-style indirection.

The only other indirect branches remaining that we are aware of are from
precompiled runtimes (such as crt0.o and similar). The ones we have
found are not really attackable, and so we have not focused on them
here, but eventually these runtimes should also be replicated for
retpoline-ed configurations for completeness.

For kernels or other freestanding or fully static executables, the
compiler switch `-mretpoline` is sufficient to fully mitigate this
particular attack. For dynamic executables, you must compile *all*
libraries with `-mretpoline` and additionally link the dynamic
executable and all shared libraries with LLD and pass `-z retpolineplt`
(or use similar functionality from some other linker). We strongly
recommend also using `-z now` as non-lazy binding allows the
retpoline-mitigated PLT to be substantially smaller.

When manually apply similar transformations to `-mretpoline` to the
Linux kernel we observed very small performance hits to applications
running typical workloads, and relatively minor hits (approximately 2%)
even for extremely syscall-heavy applications. This is largely due to
the small number of indirect branches that occur in performance
sensitive paths of the kernel.

When using these patches on statically linked applications, especially
C++ applications, you should expect to see a much more dramatic
performance hit. For microbenchmarks that are switch, indirect-, or
virtual-call heavy we have seen overheads ranging from 10% to 50%.

However, real-world workloads exhibit substantially lower performance
impact. Notably, techniques such as PGO and ThinLTO dramatically reduce
the impact of hot indirect calls (by speculatively promoting them to
direct calls) and allow optimized search trees to be used to lower
switches. If you need to deploy these techniques in C++ applications, we
*strongly* recommend that you ensure all hot call targets are statically
linked (avoiding PLT indirection) and use both PGO and ThinLTO. Well
tuned servers using all of these techniques saw 5% - 10% overhead from
the use of retpoline.

We will add detailed documentation covering these components in
subsequent patches, but wanted to make the core functionality available
as soon as possible. Happy for more code review, but we'd really like to
get these patches landed and backported ASAP for obvious reasons. We're
planning to backport this to both 6.0 and 5.0 release streams and get
a 5.0 release with just this cherry picked ASAP for distros and vendors.

This patch is the work of a number of people over the past month: Eric, Reid,
Rui, and myself. I'm mailing it out as a single commit due to the time
sensitive nature of landing this and the need to backport it. Huge thanks to
everyone who helped out here, and everyone at Intel who helped out in
discussions about how to craft this. Also, credit goes to Paul Turner (at
Google, but not an LLVM contributor) for much of the underlying retpoline
design.

Reviewers: echristo, rnk, ruiu, craig.topper, DavidKreitzer

Subscribers: sanjoy, emaste, mcrosier, mgorny, mehdi_amini, hiraditya, llvm-commits

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

llvm-svn: 324069
2018-02-02 10:50:39 +00:00
Hans Wennborg
64aeffbb5c Merging r323155:
------------------------------------------------------------------------
r323155 | chandlerc | 2018-01-22 23:05:25 +0100 (Mon, 22 Jan 2018) | 133 lines

Introduce the "retpoline" x86 mitigation technique for variant #2 of the speculative execution vulnerabilities disclosed today, specifically identified by CVE-2017-5715, "Branch Target Injection", and is one of the two halves to Spectre..

Summary:
First, we need to explain the core of the vulnerability. Note that this
is a very incomplete description, please see the Project Zero blog post
for details:
https://googleprojectzero.blogspot.com/2018/01/reading-privileged-memory-with-side.html

The basis for branch target injection is to direct speculative execution
of the processor to some "gadget" of executable code by poisoning the
prediction of indirect branches with the address of that gadget. The
gadget in turn contains an operation that provides a side channel for
reading data. Most commonly, this will look like a load of secret data
followed by a branch on the loaded value and then a load of some
predictable cache line. The attacker then uses timing of the processors
cache to determine which direction the branch took *in the speculative
execution*, and in turn what one bit of the loaded value was. Due to the
nature of these timing side channels and the branch predictor on Intel
processors, this allows an attacker to leak data only accessible to
a privileged domain (like the kernel) back into an unprivileged domain.

The goal is simple: avoid generating code which contains an indirect
branch that could have its prediction poisoned by an attacker. In many
cases, the compiler can simply use directed conditional branches and
a small search tree. LLVM already has support for lowering switches in
this way and the first step of this patch is to disable jump-table
lowering of switches and introduce a pass to rewrite explicit indirectbr
sequences into a switch over integers.

However, there is no fully general alternative to indirect calls. We
introduce a new construct we call a "retpoline" to implement indirect
calls in a non-speculatable way. It can be thought of loosely as
a trampoline for indirect calls which uses the RET instruction on x86.
Further, we arrange for a specific call->ret sequence which ensures the
processor predicts the return to go to a controlled, known location. The
retpoline then "smashes" the return address pushed onto the stack by the
call with the desired target of the original indirect call. The result
is a predicted return to the next instruction after a call (which can be
used to trap speculative execution within an infinite loop) and an
actual indirect branch to an arbitrary address.

On 64-bit x86 ABIs, this is especially easily done in the compiler by
using a guaranteed scratch register to pass the target into this device.
For 32-bit ABIs there isn't a guaranteed scratch register and so several
different retpoline variants are introduced to use a scratch register if
one is available in the calling convention and to otherwise use direct
stack push/pop sequences to pass the target address.

This "retpoline" mitigation is fully described in the following blog
post: https://support.google.com/faqs/answer/7625886

We also support a target feature that disables emission of the retpoline
thunk by the compiler to allow for custom thunks if users want them.
These are particularly useful in environments like kernels that
routinely do hot-patching on boot and want to hot-patch their thunk to
different code sequences. They can write this custom thunk and use
`-mretpoline-external-thunk` *in addition* to `-mretpoline`. In this
case, on x86-64 thu thunk names must be:
```
  __llvm_external_retpoline_r11
```
or on 32-bit:
```
  __llvm_external_retpoline_eax
  __llvm_external_retpoline_ecx
  __llvm_external_retpoline_edx
  __llvm_external_retpoline_push
```
And the target of the retpoline is passed in the named register, or in
the case of the `push` suffix on the top of the stack via a `pushl`
instruction.

There is one other important source of indirect branches in x86 ELF
binaries: the PLT. These patches also include support for LLD to
generate PLT entries that perform a retpoline-style indirection.

The only other indirect branches remaining that we are aware of are from
precompiled runtimes (such as crt0.o and similar). The ones we have
found are not really attackable, and so we have not focused on them
here, but eventually these runtimes should also be replicated for
retpoline-ed configurations for completeness.

For kernels or other freestanding or fully static executables, the
compiler switch `-mretpoline` is sufficient to fully mitigate this
particular attack. For dynamic executables, you must compile *all*
libraries with `-mretpoline` and additionally link the dynamic
executable and all shared libraries with LLD and pass `-z retpolineplt`
(or use similar functionality from some other linker). We strongly
recommend also using `-z now` as non-lazy binding allows the
retpoline-mitigated PLT to be substantially smaller.

When manually apply similar transformations to `-mretpoline` to the
Linux kernel we observed very small performance hits to applications
running typical workloads, and relatively minor hits (approximately 2%)
even for extremely syscall-heavy applications. This is largely due to
the small number of indirect branches that occur in performance
sensitive paths of the kernel.

When using these patches on statically linked applications, especially
C++ applications, you should expect to see a much more dramatic
performance hit. For microbenchmarks that are switch, indirect-, or
virtual-call heavy we have seen overheads ranging from 10% to 50%.

However, real-world workloads exhibit substantially lower performance
impact. Notably, techniques such as PGO and ThinLTO dramatically reduce
the impact of hot indirect calls (by speculatively promoting them to
direct calls) and allow optimized search trees to be used to lower
switches. If you need to deploy these techniques in C++ applications, we
*strongly* recommend that you ensure all hot call targets are statically
linked (avoiding PLT indirection) and use both PGO and ThinLTO. Well
tuned servers using all of these techniques saw 5% - 10% overhead from
the use of retpoline.

We will add detailed documentation covering these components in
subsequent patches, but wanted to make the core functionality available
as soon as possible. Happy for more code review, but we'd really like to
get these patches landed and backported ASAP for obvious reasons. We're
planning to backport this to both 6.0 and 5.0 release streams and get
a 5.0 release with just this cherry picked ASAP for distros and vendors.

This patch is the work of a number of people over the past month: Eric, Reid,
Rui, and myself. I'm mailing it out as a single commit due to the time
sensitive nature of landing this and the need to backport it. Huge thanks to
everyone who helped out here, and everyone at Intel who helped out in
discussions about how to craft this. Also, credit goes to Paul Turner (at
Google, but not an LLVM contributor) for much of the underlying retpoline
design.

Reviewers: echristo, rnk, ruiu, craig.topper, DavidKreitzer

Subscribers: sanjoy, emaste, mcrosier, mgorny, mehdi_amini, hiraditya, llvm-commits

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

llvm-svn: 324068
2018-02-02 10:50:16 +00:00
Hans Wennborg
4321822f1d Merging r323155:
------------------------------------------------------------------------
r323155 | chandlerc | 2018-01-22 23:05:25 +0100 (Mon, 22 Jan 2018) | 133 lines

Introduce the "retpoline" x86 mitigation technique for variant #2 of the speculative execution vulnerabilities disclosed today, specifically identified by CVE-2017-5715, "Branch Target Injection", and is one of the two halves to Spectre..

Summary:
First, we need to explain the core of the vulnerability. Note that this
is a very incomplete description, please see the Project Zero blog post
for details:
https://googleprojectzero.blogspot.com/2018/01/reading-privileged-memory-with-side.html

The basis for branch target injection is to direct speculative execution
of the processor to some "gadget" of executable code by poisoning the
prediction of indirect branches with the address of that gadget. The
gadget in turn contains an operation that provides a side channel for
reading data. Most commonly, this will look like a load of secret data
followed by a branch on the loaded value and then a load of some
predictable cache line. The attacker then uses timing of the processors
cache to determine which direction the branch took *in the speculative
execution*, and in turn what one bit of the loaded value was. Due to the
nature of these timing side channels and the branch predictor on Intel
processors, this allows an attacker to leak data only accessible to
a privileged domain (like the kernel) back into an unprivileged domain.

The goal is simple: avoid generating code which contains an indirect
branch that could have its prediction poisoned by an attacker. In many
cases, the compiler can simply use directed conditional branches and
a small search tree. LLVM already has support for lowering switches in
this way and the first step of this patch is to disable jump-table
lowering of switches and introduce a pass to rewrite explicit indirectbr
sequences into a switch over integers.

However, there is no fully general alternative to indirect calls. We
introduce a new construct we call a "retpoline" to implement indirect
calls in a non-speculatable way. It can be thought of loosely as
a trampoline for indirect calls which uses the RET instruction on x86.
Further, we arrange for a specific call->ret sequence which ensures the
processor predicts the return to go to a controlled, known location. The
retpoline then "smashes" the return address pushed onto the stack by the
call with the desired target of the original indirect call. The result
is a predicted return to the next instruction after a call (which can be
used to trap speculative execution within an infinite loop) and an
actual indirect branch to an arbitrary address.

On 64-bit x86 ABIs, this is especially easily done in the compiler by
using a guaranteed scratch register to pass the target into this device.
For 32-bit ABIs there isn't a guaranteed scratch register and so several
different retpoline variants are introduced to use a scratch register if
one is available in the calling convention and to otherwise use direct
stack push/pop sequences to pass the target address.

This "retpoline" mitigation is fully described in the following blog
post: https://support.google.com/faqs/answer/7625886

We also support a target feature that disables emission of the retpoline
thunk by the compiler to allow for custom thunks if users want them.
These are particularly useful in environments like kernels that
routinely do hot-patching on boot and want to hot-patch their thunk to
different code sequences. They can write this custom thunk and use
`-mretpoline-external-thunk` *in addition* to `-mretpoline`. In this
case, on x86-64 thu thunk names must be:
```
  __llvm_external_retpoline_r11
```
or on 32-bit:
```
  __llvm_external_retpoline_eax
  __llvm_external_retpoline_ecx
  __llvm_external_retpoline_edx
  __llvm_external_retpoline_push
```
And the target of the retpoline is passed in the named register, or in
the case of the `push` suffix on the top of the stack via a `pushl`
instruction.

There is one other important source of indirect branches in x86 ELF
binaries: the PLT. These patches also include support for LLD to
generate PLT entries that perform a retpoline-style indirection.

The only other indirect branches remaining that we are aware of are from
precompiled runtimes (such as crt0.o and similar). The ones we have
found are not really attackable, and so we have not focused on them
here, but eventually these runtimes should also be replicated for
retpoline-ed configurations for completeness.

For kernels or other freestanding or fully static executables, the
compiler switch `-mretpoline` is sufficient to fully mitigate this
particular attack. For dynamic executables, you must compile *all*
libraries with `-mretpoline` and additionally link the dynamic
executable and all shared libraries with LLD and pass `-z retpolineplt`
(or use similar functionality from some other linker). We strongly
recommend also using `-z now` as non-lazy binding allows the
retpoline-mitigated PLT to be substantially smaller.

When manually apply similar transformations to `-mretpoline` to the
Linux kernel we observed very small performance hits to applications
running typical workloads, and relatively minor hits (approximately 2%)
even for extremely syscall-heavy applications. This is largely due to
the small number of indirect branches that occur in performance
sensitive paths of the kernel.

When using these patches on statically linked applications, especially
C++ applications, you should expect to see a much more dramatic
performance hit. For microbenchmarks that are switch, indirect-, or
virtual-call heavy we have seen overheads ranging from 10% to 50%.

However, real-world workloads exhibit substantially lower performance
impact. Notably, techniques such as PGO and ThinLTO dramatically reduce
the impact of hot indirect calls (by speculatively promoting them to
direct calls) and allow optimized search trees to be used to lower
switches. If you need to deploy these techniques in C++ applications, we
*strongly* recommend that you ensure all hot call targets are statically
linked (avoiding PLT indirection) and use both PGO and ThinLTO. Well
tuned servers using all of these techniques saw 5% - 10% overhead from
the use of retpoline.

We will add detailed documentation covering these components in
subsequent patches, but wanted to make the core functionality available
as soon as possible. Happy for more code review, but we'd really like to
get these patches landed and backported ASAP for obvious reasons. We're
planning to backport this to both 6.0 and 5.0 release streams and get
a 5.0 release with just this cherry picked ASAP for distros and vendors.

This patch is the work of a number of people over the past month: Eric, Reid,
Rui, and myself. I'm mailing it out as a single commit due to the time
sensitive nature of landing this and the need to backport it. Huge thanks to
everyone who helped out here, and everyone at Intel who helped out in
discussions about how to craft this. Also, credit goes to Paul Turner (at
Google, but not an LLVM contributor) for much of the underlying retpoline
design.

Reviewers: echristo, rnk, ruiu, craig.topper, DavidKreitzer

Subscribers: sanjoy, emaste, mcrosier, mgorny, mehdi_amini, hiraditya, llvm-commits

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

llvm-svn: 324067
2018-02-02 10:49:53 +00:00
Hans Wennborg
7db2a032af releasenotes: fix a version nbr
llvm-svn: 323948
2018-02-01 09:21:21 +00:00
Anastasia Stulova
c4a589b202 [Docs] Added release notes for OpenCL.
Differential Revision: https://reviews.llvm.org/D42307

llvm-svn: 323875
2018-01-31 14:29:12 +00:00
Hans Wennborg
91d53cf313 Merging r323813:
------------------------------------------------------------------------
r323813 | tejohnson | 2018-01-30 21:16:32 +0100 (Tue, 30 Jan 2018) | 14 lines

Teach ValueMapper to use ODR uniqued types when available

Summary:
This is exposed during ThinLTO compilation, when we import an alias by
creating a clone of the aliasee. Without this fix the debug type is
unnecessarily cloned and we get a duplicate, undoing the uniquing.

Fixes PR36089.

Reviewers: mehdi_amini, pcc

Subscribers: eraman, JDevlieghere, llvm-commits

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

llvm-svn: 323854
2018-01-31 09:02:38 +00:00
Hans Wennborg
05bd093d06 Merging r323811:
------------------------------------------------------------------------
r323811 | mstorsjo | 2018-01-30 20:50:58 +0100 (Tue, 30 Jan 2018) | 3 lines

[GlobalISel] Bail out on calls to dllimported functions

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

llvm-svn: 323853
2018-01-31 09:00:28 +00:00
Hans Wennborg
5dd46a0b3a Merging r323810:
------------------------------------------------------------------------
r323810 | mstorsjo | 2018-01-30 20:50:51 +0100 (Tue, 30 Jan 2018) | 3 lines

[AArch64] Properly handle dllimport of variables when using fast-isel

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

llvm-svn: 323852
2018-01-31 08:57:32 +00:00
Hans Wennborg
dc52100849 Merging r322588:
------------------------------------------------------------------------
r322588 | eugenis | 2018-01-16 20:21:45 +0100 (Tue, 16 Jan 2018) | 9 lines

[hwasan] Build runtime library with -fPIC, not -fPIE.

Summary: -fPIE can not be used when building a shared library.

Reviewers: alekseyshl, peter.smith

Subscribers: kubamracek, llvm-commits, mgorny

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

llvm-svn: 323850
2018-01-31 08:51:14 +00:00
Hans Wennborg
3890cc2b5b Merging r323706:
------------------------------------------------------------------------
r323706 | mareko | 2018-01-30 00:19:10 +0100 (Tue, 30 Jan 2018) | 15 lines

AMDGPU: Allow a SGPR for the conditional KILL operand

Patch by: Bas Nieuwenhuizen

Just use the _e64 variant if needed. This should be possible as per

def : Pat <
  (int_amdgcn_kill (i1 (setcc f32:$src, InlineFPImm<f32>:$imm, cond:$cond))),
  (SI_KILL_F32_COND_IMM_PSEUDO $src, (bitcast_fpimm_to_i32 $imm), (cond_as_i32imm $cond))
> ;

I don't think we can get an immediate for the other operand for which we
need the second 32-bit word.

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

llvm-svn: 323772
2018-01-30 15:29:20 +00:00
Hans Wennborg
93fb755683 Merging r323515:
------------------------------------------------------------------------
r323515 | fhahn | 2018-01-26 11:36:50 +0100 (Fri, 26 Jan 2018) | 7 lines

[CallSiteSplitting] Fix infinite loop when recording conditions.

Fix infinite loop when recording conditions by correctly marking basic
blocks as visited.

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

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

llvm-svn: 323771
2018-01-30 15:25:02 +00:00
Hans Wennborg
2a3963f281 Merging r323469:
------------------------------------------------------------------------
r323469 | ctopper | 2018-01-25 22:23:57 +0100 (Thu, 25 Jan 2018) | 3 lines

[X86] Teach Intel syntax InstPrinter to print lock prefixes that have been parsed from the asm parser.

The asm parser puts the lock prefix in the MCInst flags so we need to check that in addition to TSFlags. This matches what the ATT printer does.
------------------------------------------------------------------------

llvm-svn: 323770
2018-01-30 15:22:31 +00:00
Hans Wennborg
f83ec4f24a Merging r323360:
------------------------------------------------------------------------
r323360 | kparzysz | 2018-01-24 19:42:19 +0100 (Wed, 24 Jan 2018) | 2 lines

[Hexagon] Accept lowercase b in -hvx-length=64b and -hvx-length=128b

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

llvm-svn: 323769
2018-01-30 15:16:10 +00:00
Hans Wennborg
dc22c56ced Merging r323013:
------------------------------------------------------------------------
r323013 | petarj | 2018-01-20 01:06:07 +0100 (Sat, 20 Jan 2018) | 18 lines

[TSan][MIPS] Expand sanitizer memory space to lower addresses

MemToShadowImpl() maps lower addresses to a memory space out of sanitizers
range. The simplest example is address 0 which is mapped to 0x2000000000

static const uptr kShadowBeg     = 0x2400000000ull;

but accessing the address during tsan execution will lead to a segmentation
fault.

This patch expands the range used by the sanitizer and ensures that 1/8 of
the maximum valid address in the virtual address spaces is used for shadow
memory.

Patch by Milos Stojanovic.

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

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

llvm-svn: 323767
2018-01-30 15:11:41 +00:00
Hans Wennborg
b5000a56f3 Merging r323243:
------------------------------------------------------------------------
r323243 | psmith | 2018-01-23 20:26:52 +0100 (Tue, 23 Jan 2018) | 10 lines

[ELF] Make --fix-cortex-a53-843419 work on big endian hosts

The reinterpret cast to uint32_t to read the little-endian instructions 
will only work on a little endian system. Use ulittle32_t to always read
little-endian (AArch64 instructions are always little endian).

Fixes PR36056

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

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

llvm-svn: 323750
2018-01-30 11:21:21 +00:00
Hans Wennborg
fcb2c907db Merging r323355:
------------------------------------------------------------------------
r323355 | nha | 2018-01-24 19:02:05 +0100 (Wed, 24 Jan 2018) | 9 lines

Revert r321751, "StructurizeCFG: Fix broken backedge detection"

It causes regressions in various OpenGL test suites.

Keep the test cases introduced by r321751 as XFAIL, and add a test case
for the regression.

Change-Id: I90b4cc354f68cebe5fcef1f2422dc8fe1c6d3514
Bugzilla: https://bugs.llvm.org/show_bug.cgi?id=36015
------------------------------------------------------------------------

llvm-svn: 323749
2018-01-30 11:17:13 +00:00
Hans Wennborg
35916e929c Merging r323710:
------------------------------------------------------------------------
r323710 | qcolombet | 2018-01-30 00:42:37 +0100 (Tue, 30 Jan 2018) | 13 lines

[RAFast] Don't dereference MBB::end

When RAFast sees liveins in on a basic block, it uses that information
to initialize the availability of the registers. The called
method uses an instruction as one of its argument and in the liveins
case, RAFast was dereferencing MBB::begin which can be MBB::end for
empty basic block.

Change the API of definePhysReg to use MachineBasicBlock::iterator
instead of MachineInstr so that we don't dereference an
invalid iterator while making the call.

rdar://problem/36952401
------------------------------------------------------------------------

llvm-svn: 323746
2018-01-30 10:53:45 +00:00
Hans Wennborg
8f2002d701 Merging r323485:
------------------------------------------------------------------------
r323485 | aemerson | 2018-01-26 01:27:22 +0100 (Fri, 26 Jan 2018) | 3 lines

[Driver] Add an -fexperimental-isel driver option to enable/disable GlobalISel.

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

llvm-svn: 323745
2018-01-30 10:43:43 +00:00
Hans Wennborg
55be22de40 Merging r323672: (test-case re-generated)
------------------------------------------------------------------------
r323672 | ctopper | 2018-01-29 18:56:57 +0100 (Mon, 29 Jan 2018) | 5 lines

[X86] Don't create SHRUNKBLEND when the condition is used by the true or false operand of the vselect.

Fixes PR34592.

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

llvm-svn: 323743
2018-01-30 10:30:33 +00:00
Hans Wennborg
b63b0ea5c5 Merging r323582:
------------------------------------------------------------------------
r323582 | aemerson | 2018-01-27 08:07:20 +0100 (Sat, 27 Jan 2018) | 6 lines

[GlobalISel][Legalizer] Convert the FP constants to the right APFloat type for G_FCONSTANT.

We weren't converting the immediate ConstantFP during legalization, which caused
the wrong bit patterns to be emitted for half type FP constants.

Fixes PR36106.
------------------------------------------------------------------------

llvm-svn: 323742
2018-01-30 10:19:03 +00:00
Hans Wennborg
914b6fa85f Merging r322245:
------------------------------------------------------------------------
r322245 | ctopper | 2018-01-11 02:37:59 +0100 (Thu, 11 Jan 2018) | 5 lines

[X86] Make -mavx512f imply -mfma and -mf16c in the frontend like it does in the backend.

Similarly, make -mno-fma and -mno-f16c imply -mno-avx512f.

Withou this  "-mno-sse -mavx512f" ends up with avx512f being enabled in the frontend but disabled in the backend.
------------------------------------------------------------------------

llvm-svn: 323741
2018-01-30 10:15:07 +00:00
Hans Wennborg
7894274a72 Merging r322016:
------------------------------------------------------------------------
r322016 | spatel | 2018-01-08 19:31:13 +0100 (Mon, 08 Jan 2018) | 8 lines

[ValueTracking] remove overzealous assert

The test is derived from a failing fuzz test:
https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=5008

Credit to @rksimon for pointing out the problem.


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

llvm-svn: 323740
2018-01-30 09:57:17 +00:00
Hans Wennborg
613bd84d50 Revert r323738; that was not the one I wanted to merge
llvm-svn: 323739
2018-01-30 09:55:31 +00:00
Hans Wennborg
3a31d30046 Merging r322006:
------------------------------------------------------------------------
r322006 | davide | 2018-01-08 17:34:06 +0100 (Mon, 08 Jan 2018) | 19 lines

[CVP] Replace incoming values from unreachable blocks with undef.

This is an attempt of fixing PR35807.
Due to the non-standard definition of dominance in LLVM, where uses in
unreachable blocks are dominated by anything, you can have, in an
unreachable block:

  %patatino = OP1 %patatino, CONSTANT

When `SimplifyInstruction` receives a PHI where an incoming value is of
the aforementioned form, in some cases, loops indefinitely.

What I propose here instead is keeping track of the incoming values
from unreachable blocks, and replacing them with undef. It fixes this
case, and it seems to be good regardless (even if we can't prove that
the value is constant, as it's coming from an unreachable block, we
can ignore it).

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

llvm-svn: 323738
2018-01-30 09:52:39 +00:00
Hans Wennborg
8a6a0ba9d9 Merging r323331:
------------------------------------------------------------------------
r323331 | spatel | 2018-01-24 16:20:37 +0100 (Wed, 24 Jan 2018) | 21 lines

[ValueTracking] add recursion depth param to matchSelectPattern 

We're getting bug reports:
https://bugs.llvm.org/show_bug.cgi?id=35807
https://bugs.llvm.org/show_bug.cgi?id=35840
https://bugs.llvm.org/show_bug.cgi?id=36045
...where we blow up the stack in value tracking because other passes are sending 
in selects that have an operand that is itself the select.

We don't currently have a reliable way to avoid analyzing dead code that may take 
non-standard forms, so bail out when things go too far.

This mimics the recursion depth limitations in other parts of value tracking.

Unfortunately, this pushes the underlying problems for other passes (jump-threading,
simplifycfg, correlated-propagation) into hiding. If someone wants to uncover those
again, the first draft of this patch on Phab would do that (it would assert rather
than bail out).

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

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

llvm-svn: 323737
2018-01-30 09:48:42 +00:00
Hans Wennborg
3625b1ae96 Merging r322108, r322123 and r322131:
------------------------------------------------------------------------
r322108 | rafael | 2018-01-09 20:29:33 +0100 (Tue, 09 Jan 2018) | 3 lines

Make one of the emitFill methods non virtual. NFC.

This is just preparatory work to fix PR35858.
------------------------------------------------------------------------

------------------------------------------------------------------------
r322123 | rafael | 2018-01-09 22:55:10 +0100 (Tue, 09 Jan 2018) | 3 lines

Don't create MCFillFragment directly.

Instead use higher level APIs that take care of most bookkeeping.
------------------------------------------------------------------------

------------------------------------------------------------------------
r322131 | rafael | 2018-01-09 23:48:37 +0100 (Tue, 09 Jan 2018) | 4 lines

Use a MCExpr for the size of MCFillFragment.

This allows the size to be found during ralaxation. This fixes
pr35858.
------------------------------------------------------------------------

llvm-svn: 323735
2018-01-30 09:31:00 +00:00
Hans Wennborg
51ea8e3de8 Merging r323395, r323396, r323399, r323440, r323449, r323456, and r323625:
------------------------------------------------------------------------
r323395 | rafael | 2018-01-25 02:29:15 +0100 (Thu, 25 Jan 2018) | 1 line

Use lookup instead of find. NFC, just simpler.
------------------------------------------------------------------------

------------------------------------------------------------------------
r323396 | rafael | 2018-01-25 02:36:36 +0100 (Thu, 25 Jan 2018) | 3 lines

Only lookup LMARegion once. NFC.

This is similar to how we handle MemRegion.
------------------------------------------------------------------------

------------------------------------------------------------------------
r323399 | rafael | 2018-01-25 03:18:00 +0100 (Thu, 25 Jan 2018) | 3 lines

Remove MemRegionOffset. NFC.

We can just use a member variable in MemoryRegion.
------------------------------------------------------------------------

------------------------------------------------------------------------
r323440 | rafael | 2018-01-25 17:43:49 +0100 (Thu, 25 Jan 2018) | 1 line

Simplify. NFC.
------------------------------------------------------------------------

------------------------------------------------------------------------
r323449 | rafael | 2018-01-25 18:42:03 +0100 (Thu, 25 Jan 2018) | 9 lines

Improve LMARegion handling.

This fixes the crash reported at PR36083.

The issue is that we were trying to put all the sections in the same
PT_LOAD and crashing trying to write past the end of the file.

This also adds accounting for used space in LMARegion, without it all
3 PT_LOADs would have the same physical address.
------------------------------------------------------------------------

------------------------------------------------------------------------
r323456 | rafael | 2018-01-25 20:02:08 +0100 (Thu, 25 Jan 2018) | 19 lines

Move LMAOffset from the OutputSection to the PhdrEntry. NFC.

If two sections are in the same PT_LOAD, their relatives offsets,
virtual address and physical addresses are all the same.

I initially wanted to have a single global LMAOffset, on the
assumption that every ELF file was in practiced loaded contiguously in
both physical and virtual memory.

Unfortunately that is not the case. The linux kernel has:

  LOAD           0x200000 0xffffffff81000000 0x0000000001000000 0xced000 0xced000 R E 0x200000
  LOAD           0x1000000 0xffffffff81e00000 0x0000000001e00000 0x15f000 0x15f000 RW  0x200000
  LOAD           0x1200000 0x0000000000000000 0x0000000001f5f000 0x01b198 0x01b198 RW  0x200000
  LOAD           0x137b000 0xffffffff81f7b000 0x0000000001f7b000 0x116000 0x1ec000 RWE 0x200000

The delta for all but the third PT_LOAD is the same:
0xffffffff80000000. I think the 3rd one is a hack for implementing per
cpu data, but we can't break that.
------------------------------------------------------------------------

------------------------------------------------------------------------
r323625 | rafael | 2018-01-29 04:44:44 +0100 (Mon, 29 Jan 2018) | 10 lines

Put the header in the first PT_LOAD even if that PT_LOAD has a LMAExpr.

This should fix PR36017.

The root problem is that we were creating a PT_LOAD just for the
header. That was technically valid, but inconvenient: we should not be
making the ELF discontinuous.

The solution is to allow a section with LMAExpr to be added to a
PT_LOAD if that PT_LOAD doesn't already have a LMAExpr.
------------------------------------------------------------------------

llvm-svn: 323733
2018-01-30 09:20:27 +00:00
Hans Wennborg
d8f3048d1c Merging r323384:
------------------------------------------------------------------------
r323384 | aemerson | 2018-01-24 23:40:25 +0100 (Wed, 24 Jan 2018) | 1 line

[GlobalISel] Add a requires: asserts to a test.
------------------------------------------------------------------------

llvm-svn: 323523
2018-01-26 12:03:01 +00:00
Hans Wennborg
aa7377b802 Merging r323369 and r323371:
------------------------------------------------------------------------
r323369 | aemerson | 2018-01-24 20:59:29 +0100 (Wed, 24 Jan 2018) | 4 lines

[GlobalISel] Don't fall back to FastISel.

Apparently checking the pass structure isn't enough to ensure that we don't fall
back to FastISel, as it's set up as part of the SelectionDAGISel.
------------------------------------------------------------------------

------------------------------------------------------------------------
r323371 | aemerson | 2018-01-24 21:35:37 +0100 (Wed, 24 Jan 2018) | 12 lines

[AArch64][GlobalISel] Fall back during AArch64 isel if we have a volatile load.

The tablegen imported patterns for sext(load(a)) don't check for single uses
of the load or delete the original after matching. As a result two loads are
left in the generated code. This particular issue will be fixed by adding
support for a G_SEXTLOAD opcode in future.

There are however other potential issues around this that wouldn't be fixed by
a G_SEXTLOAD, so until we have a proper solution we don't try to handle volatile
loads at all in the AArch64 selector.

Fixes/works around PR36018.
------------------------------------------------------------------------

llvm-svn: 323434
2018-01-25 15:28:01 +00:00
Hans Wennborg
af58022d01 Merging r323315:
------------------------------------------------------------------------
r323315 | mstorsjo | 2018-01-24 11:14:52 +0100 (Wed, 24 Jan 2018) | 9 lines

[builtins] Align addresses to cache lines in __clear_cache for aarch64

This makes sure that the last cache line gets invalidated properly.

This matches the example code at
http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.den0024a/BABJDBHI.html,
and also matches what libgcc does.

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

llvm-svn: 323338
2018-01-24 15:56:18 +00:00
Hans Wennborg
f9b031a47d Merging r322900 and r323307:
------------------------------------------------------------------------
r322900 | mstorsjo | 2018-01-18 22:21:48 +0100 (Thu, 18 Jan 2018) | 6 lines

[test] Actually check the common parts in CodeGen/ARM/global-merge-external.ll. NFC.

Previously, these parts weren't ever checked. The label patterns
need to be extended to match successfully on macho.

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

------------------------------------------------------------------------
r323307 | mstorsjo | 2018-01-24 07:40:04 +0100 (Wed, 24 Jan 2018) | 6 lines

[GlobalMerge] Don't merge dllexport globals

Merging such globals loses the dllexport attribute. Add a test
to check that normal globals still are merged.

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

llvm-svn: 323337
2018-01-24 15:53:46 +00:00
Hans Wennborg
ce35fa3456 Merging r322359, r322421, and r322801:
------------------------------------------------------------------------
r322359 | grimar | 2018-01-12 10:07:35 +0100 (Fri, 12 Jan 2018) | 8 lines

[ELF] - Fix for ld.lld does not accept "AT" syntax for declaring LMA region

AT> lma_region expression allows to specify the memory region
for section load address.

Should fix PR35684.

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

------------------------------------------------------------------------
r322421 | rafael | 2018-01-13 00:26:25 +0100 (Sat, 13 Jan 2018) | 9 lines

Fix incorrect physical address on self-referencing AT command.

When a section placement (AT) command references the section itself,
the physical address of the section in the ELF header was calculated
incorrectly due to alignment happening right after the location
pointer's value was captured.

The problem was diagnosed and the first version of the patch written
by Erick Reyes.
------------------------------------------------------------------------

------------------------------------------------------------------------
r322801 | rafael | 2018-01-18 02:14:57 +0100 (Thu, 18 Jan 2018) | 5 lines

Handle parsing AT(ADDR(.foo-bar)).

The problem we had with it is that anything inside an AT is an
expression, so we failed to parse the section name because of the - in
it.
------------------------------------------------------------------------

llvm-svn: 323336
2018-01-24 15:48:26 +00:00
Hans Wennborg
f49217745b Merging r323190:
------------------------------------------------------------------------
r323190 | rksimon | 2018-01-23 12:39:06 +0100 (Tue, 23 Jan 2018) | 5 lines

[X86][SSE] LowerBUILD_VECTORAsVariablePermute - fix PSHUFB source/index operand ordering

As detailed in rL317463, PSHUFB (like most variable shuffle instructions) uses Op[0] for the source vector and Op[1] for the shuffle index vector, VPERMV works in reverse which is probably where the confusion comes from.

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

llvm-svn: 323335
2018-01-24 15:38:38 +00:00
Hans Wennborg
a06dd8b1e7 Merging r322372 and r322767:
------------------------------------------------------------------------
r322372 | nemanjai | 2018-01-12 15:58:41 +0100 (Fri, 12 Jan 2018) | 10 lines

[PowerPC] Zero-extend the compare operand for ATOMIC_CMP_SWAP

Part of the fix for https://bugs.llvm.org/show_bug.cgi?id=35812.
This patch ensures that the compare operand for the atomic compare and swap
is properly zero-extended to 32 bits if applicable.
A follow-up commit will fix the extension for the SETCC node generated when
expanding an ATOMIC_CMP_SWAP_WITH_SUCCESS. That will complete the bug fix.

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

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

------------------------------------------------------------------------
r322767 | efriedma | 2018-01-17 23:04:36 +0100 (Wed, 17 Jan 2018) | 12 lines

[LegalizeDAG] Fix ATOMIC_CMP_SWAP_WITH_SUCCESS legalization.

The code wasn't zero-extending correctly, so the comparison could
spuriously fail.

Adds some AArch64 tests to cover this case.

Inspired by D41791.

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


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

llvm-svn: 323334
2018-01-24 15:33:33 +00:00
Hans Wennborg
22ca69d152 Merging r323008:
------------------------------------------------------------------------
r323008 | vsapsai | 2018-01-20 00:41:47 +0100 (Sat, 20 Jan 2018) | 32 lines

[Lex] Fix crash on code completion in comment in included file.

This fixes PR32732 by updating CurLexerKind to reflect available lexers.
We were hitting null pointer in Preprocessor::Lex because CurLexerKind
was CLK_Lexer but CurLexer was null. And we set it to null in
Preprocessor::HandleEndOfFile when exiting a file with code completion
point.

To reproduce the crash it is important for a comment to be inside a
class specifier. In this case in Parser::ParseClassSpecifier we improve
error recovery by pushing a semicolon token back into the preprocessor
and later on try to lex a token because we haven't reached the end of
file.

Also clang crashes only on code completion in included file, i.e. when
IncludeMacroStack is not empty. Though we reset CurLexer even if include
stack is empty. The difference is that during pushing back a semicolon
token, preprocessor calls EnterCachingLexMode which decides it is
already in caching mode because various lexers are null and
IncludeMacroStack is not empty. As the result, CurLexerKind remains
CLK_Lexer instead of updating to CLK_CachingLexer.

rdar://problem/34787685

Reviewers: akyrtzi, doug.gregor, arphaman

Reviewed By: arphaman

Subscribers: cfe-commits, kfunk, arphaman, nemanjai, kbarton

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

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

llvm-svn: 323333
2018-01-24 15:28:43 +00:00
Hans Wennborg
e0a493014e ReleaseNotes: mention improved codeview quality
llvm-svn: 323332
2018-01-24 15:24:14 +00:00
Hans Wennborg
b52e723e65 Merging r323221:
------------------------------------------------------------------------
r323221 | rafael | 2018-01-23 17:59:20 +0100 (Tue, 23 Jan 2018) | 3 lines

Don't mark a shared library as needed because of a lazy symbol.

Fixes PR36029.
------------------------------------------------------------------------

llvm-svn: 323327
2018-01-24 14:53:42 +00:00
Kai Nacke
6f21edc12d Add external project LDC to release notes.
LDC, the LLVM-based D compiler, is already ready for LLVM 6.0.0.

llvm-svn: 323186
2018-01-23 11:03:55 +00:00
Jonas Hahnfeld
973f490f11 [ReleaseNotes] Mention OpenMP Tools Interface in runtime library
The OpenMP runtime has no dedicated Release Notes, so add it to Clang's
section about OpenMP.

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

llvm-svn: 323179
2018-01-23 07:50:11 +00:00
Serge Pavlov
f6266a875f [6.0.0 Release] Release notes for configuration files in clang
Differential Revision: https://reviews.llvm.org/D42360

llvm-svn: 323132
2018-01-22 16:44:29 +00:00
Jonas Hahnfeld
b698ef77f8 Merging r323123:
------------------------------------------------------------------------
r323123 | hahnfeld | 2018-01-22 16:27:45 +0100 (Mon, 22 Jan 2018) | 6 lines

[DOCS] Mention OpenMP Tools Interface in runtime library

Also list supported configurations (architectures + operating
systems).

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

llvm-svn: 323128
2018-01-22 15:38:26 +00:00
Hans Wennborg
70281b55d1 Merging r323034:
------------------------------------------------------------------------
r323034 | dmgreen | 2018-01-20 11:29:37 +0100 (Sat, 20 Jan 2018) | 9 lines

[Dominators] Fix some edge cases for PostDomTree updating

These fix some odd cfg cases where batch-updating the post
dom tree fails. Usually around infinite loops and roots
ending up being different.

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


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

llvm-svn: 323121
2018-01-22 15:16:37 +00:00
Hans Wennborg
fbfb221c47 Merging r323039:
------------------------------------------------------------------------
r323039 | kamil | 2018-01-20 15:16:16 +0100 (Sat, 20 Jan 2018) | 13 lines

[compiler-rt] Implement __clear_cache() on OpenBSD/mips64

Summary:
Make __clear_cache() invoke the platform's cache flush function
on OpenBSD/mips64.

Reviewers: krytarowski

Reviewed By: krytarowski

Subscribers: sdardis, dberris, arichardson, krytarowski, llvm-commits, #sanitizers

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

llvm-svn: 323120
2018-01-22 15:08:44 +00:00
Hans Wennborg
d0d681764a Merging r322904 and r322905:
------------------------------------------------------------------------
r322904 | rnk | 2018-01-18 23:55:14 +0100 (Thu, 18 Jan 2018) | 3 lines

[CodeView] Sink complex inline functions to .cpp file, NFC

I'm cleaning up this code before I attempt to fix a line table bug.
------------------------------------------------------------------------

------------------------------------------------------------------------
r322905 | rnk | 2018-01-18 23:55:43 +0100 (Thu, 18 Jan 2018) | 5 lines

[CodeView] Add line numbers for inlined call sites

We did this for inline call site line tables, but we hadn't done it for
regular function line tables yet. This patch copies that logic from
encodeInlineLineTable.
------------------------------------------------------------------------

llvm-svn: 323117
2018-01-22 14:15:37 +00:00
Hans Wennborg
5ef8066d90 Merging r322993:
------------------------------------------------------------------------
r322993 | kuhar | 2018-01-19 22:27:24 +0100 (Fri, 19 Jan 2018) | 16 lines

[Dominators] Visit affected node candidates found at different root levels

Summary:
This patch attempts to fix the DomTree incremental insertion bug found here [[ https://bugs.llvm.org/show_bug.cgi?id=35969 | PR35969 ]] .

When performing an insertion into a piece of unreachable CFG, we may find the same not at different levels. When this happens, the node can turn out to be affected when we find it starting from a node with a lower level in the tree. The level at which we start visitation affects if we consider a node affected or not.

This patch tracks the lowest level at which each node was visited during insertion and allows it to be visited multiple times, if it can cause it to be considered affected.

Reviewers: brzycki, davide, dberlin, grosser

Reviewed By: brzycki

Subscribers: llvm-commits

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

llvm-svn: 323110
2018-01-22 13:01:28 +00:00
Hans Wennborg
5f49ed3c9b Merging r322813:
------------------------------------------------------------------------
r322813 | rtrieu | 2018-01-18 05:28:56 +0100 (Thu, 18 Jan 2018) | 7 lines

Fix Scope::dump()

The dump function for Scope only has 20 out of the 24 flags.  Since it looped
until no flags were left, having an unknown flag lead to an infinite loop.
That loop has been changed to a single pass for each flag, plus an assert to
alert if new flags are added.

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

llvm-svn: 323109
2018-01-22 12:56:41 +00:00
Hans Wennborg
abe75eb4ed Merging r322984:
------------------------------------------------------------------------
r322984 | rtrieu | 2018-01-19 21:46:19 +0100 (Fri, 19 Jan 2018) | 7 lines

Allow BlockDecl in CXXRecord scope to have no access specifier.

Using a BlockDecl in a default member initializer causes it to be attached to
CXXMethodDecl without its access specifier being set.  This prevents a crash
where getAccess is called on this BlockDecl, since that method expects any
Decl in CXXRecord scope to have an access specifier.

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

llvm-svn: 323108
2018-01-22 12:54:39 +00:00
Hans Wennborg
c27bdf55ea Merging r322973:
------------------------------------------------------------------------
r322973 | mgorny | 2018-01-19 18:47:03 +0100 (Fri, 19 Jan 2018) | 7 lines

[cmake] Include LLVM_LIBXML2_ENABLED in LLVMConfig.cmake, PR36006

Include the LLVM_LIBXML2_ENABLED cache variable in LLVMConfig.cmake
in order to make it available for other LLVM packages to query. This
is necessary to fix stand-alone testing of LLD.

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

llvm-svn: 323107
2018-01-22 12:48:06 +00:00
Hans Wennborg
f2be7bed1b Merging r322878:
------------------------------------------------------------------------
r322878 | aemerson | 2018-01-18 20:21:27 +0100 (Thu, 18 Jan 2018) | 5 lines

[AArch64][GlobalISel] Add isel support for global values in the large code model.

Fixes PR35958.

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

llvm-svn: 323103
2018-01-22 11:56:34 +00:00
Jonas Hahnfeld
d576d9d98c [docs] Fix typo in Release Notes
llvm-svn: 323066
2018-01-21 11:43:32 +00:00
Dimitry Andric
fc20aff3eb Merging r322875:
------------------------------------------------------------------------
r322875 | dim | 2018-01-18 19:39:13 +0100 (Thu, 18 Jan 2018) | 9 lines

Add a -no-libcxxabi option to the test-release.sh script.

On FreeBSD, it is currently not possible to build libcxxabi and link
against it, so we have been building releases with -no-libs for quite
some time.

However, libcxx and libunwind should build without problems, so provide
an option to skip just libcxxabi.

------------------------------------------------------------------------
Merging r322879:
------------------------------------------------------------------------
r322879 | dim | 2018-01-18 20:30:30 +0100 (Thu, 18 Jan 2018) | 2 lines

Follow-up to rL322875 by initializing the do_libcxxabi variable properly.

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

llvm-svn: 323038
2018-01-20 12:20:35 +00:00
Dimitry Andric
5aeda7c799 Merging r322869:
------------------------------------------------------------------------
r322869 | dim | 2018-01-18 19:24:22 +0100 (Thu, 18 Jan 2018) | 3 lines

Sprinkle a few <cstdlib> includes, for libomptarget sources using
malloc, free, alloca and getenv.  NFCI.

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

llvm-svn: 323037
2018-01-20 12:17:51 +00:00
Hans Wennborg
04e3399c1a Merging r322053:
------------------------------------------------------------------------
r322053 | echristo | 2018-01-09 03:38:17 +0100 (Tue, 09 Jan 2018) | 1 line

Remove unused function HvxSelector::zerous.
------------------------------------------------------------------------

llvm-svn: 322953
2018-01-19 15:59:49 +00:00
Hans Wennborg
c38b1f37e9 Merging r322901:
------------------------------------------------------------------------
r322901 | nico | 2018-01-18 13:40:27 -0800 (Thu, 18 Jan 2018) | 19 lines

Remove TautologicalInRangeCompare from Extra and TautologicalCompare.

This removes the following (already default-off) warnings from -Wextra:
  -Wtautological-type-limit-compare,
  -Wtautological-unsigned-zero-compare
  -Wtautological-unsigned-enum-zero-compare

On the thread "[cfe-dev] -Wtautological-constant-compare issues", clang
code owners Richard Smith, John McCall, and Reid Kleckner as well as
libc++ code owner Marshall Clow stated that these new warnings are not
yet ready for prime time and shouldn't be part of -Wextra.

Furthermore, Vedant Kumar (Apple), Peter Hosek (Fuchsia), and me (Chromium)
expressed the same concerns (Vedant on that thread, Peter on
https://reviews.llvm.org/D39462, me on https://reviews.llvm.org/D41512).

So remove them from -Wextra, and remove TautologicalInRangeCompare from
TautologicalCompare too until they're usable with real-world code.

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

llvm-svn: 322931
2018-01-19 10:09:28 +00:00
Hans Wennborg
c549b607fd Merging r322644:
------------------------------------------------------------------------
r322644 | d0k | 2018-01-17 05:01:06 -0800 (Wed, 17 Jan 2018) | 7 lines

[X86] Don't mutate shuffle arguments after early-out for AVX512

The match* functions have the annoying behavior of modifying its inputs.
Save and restore the inputs, just in case the early out for AVX512 is
hit. This is still not great and its only a matter of time this kind of
bug happens again, but I couldn't come up with a better pattern without
rewriting significant chunks of this code. Fixes PR35977.
------------------------------------------------------------------------

llvm-svn: 322840
2018-01-18 11:37:05 +00:00
Hans Wennborg
788e5ea143 Merging r322830:
------------------------------------------------------------------------
r322830 | hahnfeld | 2018-01-18 02:58:43 -0800 (Thu, 18 Jan 2018) | 1 line

Add missing headers for Debug builds
------------------------------------------------------------------------

llvm-svn: 322837
2018-01-18 11:19:22 +00:00
Hans Wennborg
cb19914a00 Merging r322724:
------------------------------------------------------------------------
r322724 | ctopper | 2018-01-17 10:46:01 -0800 (Wed, 17 Jan 2018) | 7 lines

[X86] When legalizing (v64i1 select i8, v64i1, v64i1) make sure not to introduce bitcasts to i64 in 32-bit mode

We legalize selects of masks with scalar conditions using a bitcast to an integer type. But if we are in 32-bit mode we can't convert v64i1 to i64. So instead split the v64i1 to v32i1 and concat it back together. Each half will then be legalized by bitcasting to i32 which is fine.

The test case is a little indirect. If we have the v64i1 select in IR it will get legalized by legalize vector ops which has a run of type legalization after it. That type legalization run is able to fix this i64 bitcast. So in order to avoid that we need a build_vector of a splat which legalize vector ops will ignore. Legalize DAG will then turn that into a select via LowerBUILD_VECTORvXi1. And the select will get legalized. In this case there is no type legalizer run to cleanup the bitcast.

This fixes pr35972.
------------------------------------------------------------------------

llvm-svn: 322835
2018-01-18 11:16:33 +00:00
Hans Wennborg
302157538d Merging r322081:
------------------------------------------------------------------------
r322081 | mgorny | 2018-01-09 06:44:04 -0800 (Tue, 09 Jan 2018) | 10 lines

[test] Fix tests to use more portable LLVM_ENABLE_ZLIB

The HAVE_LIBZ variable is not exported by LLVM, and therefore is not
available in stand-alone builds of other tools. Use LLVM_ENABLE_ZLIB
which is the name under which the effective value is exported.

Additional, use llvm_canonicalize_cmake_booleans() to make sure that
a correct (Python-safe) boolean value is passed down to lit.

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

llvm-svn: 322833
2018-01-18 11:13:10 +00:00
Hans Wennborg
ab9a56b7d6 Merging r321932:
------------------------------------------------------------------------
r321932 | mgorny | 2018-01-06 02:20:25 -0800 (Sat, 06 Jan 2018) | 12 lines

[test] Use full PATH lookup for tools

Use full PATH when looking up test tools rather than just llvm tools
directory. r320813 has added a lookup for 'lldb-test' which is part
of LLDB tools rather than LLVM, and therefore is not present
in llvm_tools_dir before LLDB is installed.

While technically we could introduce separate per-directory lookup
logic, there is no real reason not to use the PATH formed earlier here,
and this is what other tools are doing.

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

llvm-svn: 322832
2018-01-18 11:11:43 +00:00
Hans Wennborg
134a2c12ce Merging r322003:
------------------------------------------------------------------------
r322003 | niravd | 2018-01-08 08:21:35 -0800 (Mon, 08 Jan 2018) | 11 lines

[DAG] Teach BaseIndexOffset to correctly handle with indexed operations

BaseIndexOffset address analysis incorrectly ignores offsets folded
into indexed memory operations causing potential errors in alias
analysis of pre-indexed operations.

Reviewers: efriedma, RKSimon, hfinkel, jyknight

Subscribers: hiraditya, javed.absar, llvm-commits

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

llvm-svn: 322693
2018-01-17 17:48:09 +00:00
Hans Wennborg
d68c17bc99 Merging r321751, r321806, and r321878:
------------------------------------------------------------------------
r321751 | arsenm | 2018-01-03 10:45:37 -0800 (Wed, 03 Jan 2018) | 25 lines

StructurizeCFG: Fix broken backedge detection

The work order was changed in r228186 from SCC order
to RPO with an arbitrary sorting function. The sorting
function attempted to move inner loop nodes earlier. This
was was apparently relying on an assumption that every block
in a given loop / the same loop depth would be seen before
visiting another loop. In the broken testcase, a block
outside of the loop was encountered before moving onto
another block in the same loop. The testcase would then
structurize such that one blocks unconditional successor
could never be reached.

Revert to plain RPO for the analysis phase. This fixes
detecting edges as backedges that aren't really.

The processing phase does use another visited set, and
I'm unclear on whether the order there is as important.
An arbitrary order doesn't work, and triggers some infinite
loops. The reversed RPO list seems to work and is closer
to the order that was used before, minus the arbitary
custom sorting.

A few of the changed tests now produce smaller code,
and a few are slightly worse looking.
------------------------------------------------------------------------

------------------------------------------------------------------------
r321806 | arsenm | 2018-01-04 09:23:24 -0800 (Thu, 04 Jan 2018) | 4 lines

StructurizeCFG: xfail one of the testcases from r321751

It fails with -verify-region-info. This seems to be a issue
with RegionInfo itself which existed before.
------------------------------------------------------------------------

------------------------------------------------------------------------
r321878 | arsenm | 2018-01-05 09:51:36 -0800 (Fri, 05 Jan 2018) | 4 lines

RegionInfo: Use report_fatal_error instead of llvm_unreachable

Otherwise when using -verify-region-info in a release build the
error won't be emitted.
------------------------------------------------------------------------

llvm-svn: 322686
2018-01-17 16:33:44 +00:00
Hans Wennborg
b539787af7 Merging r322313:
------------------------------------------------------------------------
r322313 | matze | 2018-01-11 13:57:03 -0800 (Thu, 11 Jan 2018) | 18 lines

PeepholeOptimizer: Do not form PHI with subreg arguments

When replacing a PHI the PeepholeOptimizer currently takes the register
class of the register at the first operand. This however is not correct
if this argument has a subregister index.

As there is currently no API to query the register class resulting from
applying a subregister index to all registers in a class, we can only
abort in these cases and not perform the transformation.

This changes findNextSource() to require the end of all copy chains to
not use a subregister if there is any PHI in the chain. I had to rewrite
the overly complicated inner loop there to have a good place to insert
the new check.

This fixes https://llvm.org/PR33071 (aka rdar://32262041)

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

llvm-svn: 322684
2018-01-17 16:29:10 +00:00
Hans Wennborg
1219711ceb Merging r322246:
------------------------------------------------------------------------
r322246 | ctopper | 2018-01-10 17:38:00 -0800 (Wed, 10 Jan 2018) | 1 line

[X86] Fix vpshrd builtins to require an ICE for their constant argument to match vpshld.
------------------------------------------------------------------------

llvm-svn: 322682
2018-01-17 16:26:58 +00:00
Hans Wennborg
cb494e4e1a Merging r322223:
------------------------------------------------------------------------
r322223 | matze | 2018-01-10 12:49:57 -0800 (Wed, 10 Jan 2018) | 5 lines

TargetLoweringBase: The ios simulator has no bzero function.

Make sure I really get back to the beahvior before my rewrite in r321035
which turned out not to be completely NFC as I changed the behavior for
the ios simulator environment.
------------------------------------------------------------------------

llvm-svn: 322681
2018-01-17 16:24:35 +00:00
Hans Wennborg
95aeee7178 Merging r322106:
------------------------------------------------------------------------
r322106 | abataev | 2018-01-09 11:08:22 -0800 (Tue, 09 Jan 2018) | 11 lines

[COST]Fix PR35865: Fix cost model evaluation for shuffle on X86.

Summary:
If the vector type is transformed to non-vector single type, the compile
may crash trying to get vector information about non-vector type.

Reviewers: RKSimon, spatel, mkuper, hfinkel

Subscribers: llvm-commits

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

llvm-svn: 322680
2018-01-17 16:20:48 +00:00
Hans Wennborg
40aaf37194 Merging r322272:
------------------------------------------------------------------------
r322272 | zvi | 2018-01-11 04:26:52 -0800 (Thu, 11 Jan 2018) | 15 lines

X86: Fix LowerBUILD_VECTORAsVariablePermute for case Src is smaller than Indices

Summary:
As RKSimon suggested in pr35820, in the case that Src is smaller in
bit-size than Indices, need to widen Src to avoid type mismatch.

Fixes pr35820

Reviewers: RKSimon, craig.topper

Reviewed By: RKSimon

Subscribers: llvm-commits

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

llvm-svn: 322679
2018-01-17 16:18:37 +00:00
Hans Wennborg
c1d3b5d666 Merging r321779:
------------------------------------------------------------------------
r321779 | rsmith | 2018-01-03 17:24:17 -0800 (Wed, 03 Jan 2018) | 7 lines

PR35045: Convert injected-class-name to its corresponding simple-template-id
during template argument deduction.

We already did this when the injected-class-name was in P, but missed the case
where it was in A. This (probably) can't happen except in implicit deduction
guides.

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

llvm-svn: 322677
2018-01-17 16:15:39 +00:00
Hans Wennborg
122b42e13e Merging r321777:
------------------------------------------------------------------------
r321777 | rsmith | 2018-01-03 17:02:18 -0800 (Wed, 03 Jan 2018) | 2 lines

PR35028: Retain duplicate alignas attributes in template instantiation.

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

llvm-svn: 322676
2018-01-17 16:13:22 +00:00
Hans Wennborg
0462903e0e Merging r321870, r321872, and r321994:
------------------------------------------------------------------------
r321870 | abataev | 2018-01-05 07:20:40 -0800 (Fri, 05 Jan 2018) | 1 line

[SLP] Update test checks, NFC.
------------------------------------------------------------------------

------------------------------------------------------------------------
r321872 | abataev | 2018-01-05 08:15:17 -0800 (Fri, 05 Jan 2018) | 1 line

[SLP] Update more test checks, NFC.
------------------------------------------------------------------------

------------------------------------------------------------------------
r321994 | abataev | 2018-01-08 06:43:06 -0800 (Mon, 08 Jan 2018) | 13 lines

[SLP] Fix PR35777: Incorrect handling of aggregate values.

Summary:
Fixes the bug with incorrect handling of InsertValue|InsertElement
instrucions in SLP vectorizer. Currently, we may use incorrect
ExtractElement instructions as the operands of the original
InsertValue|InsertElement instructions.

Reviewers: mkuper, hfinkel, RKSimon, spatel

Subscribers: llvm-commits

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

llvm-svn: 322675
2018-01-17 16:04:05 +00:00
Hans Wennborg
cf9e4fbd73 Merging r322473:
------------------------------------------------------------------------
r322473 | a.elovikov | 2018-01-15 02:56:07 -0800 (Mon, 15 Jan 2018) | 23 lines

[LV] Don't call recordVectorLoopValueForInductionCast for newly-created IV from a trunc.

Summary:
This method is supposed to be called for IVs that have casts in their use-def
chains that are completely ignored after vectorization under PSE. However, for
truncates of such IVs the same InductionDescriptor is used during
creation/widening of both original IV based on PHINode and new IV based on
TruncInst.

This leads to unintended second call to recordVectorLoopValueForInductionCast
with a VectorLoopVal set to the newly created IV for a trunc and causes an
assert due to attempt to store new information for already existing entry in the
map. This is wrong and should not be done.

Fixes PR35773.

Reviewers: dorit, Ayal, mssimpso

Reviewed By: dorit

Subscribers: RKSimon, dim, dcaballe, hsaito, llvm-commits, hiraditya

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

llvm-svn: 322673
2018-01-17 15:57:43 +00:00
Hans Wennborg
d461c80298 Merging r321791 and r321862:
------------------------------------------------------------------------
r321791 | sam_parker | 2018-01-04 01:42:27 -0800 (Thu, 04 Jan 2018) | 4 lines

[X86] Codegen test for PR37563

Adding test to ease review of D41628.

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

------------------------------------------------------------------------
r321862 | sam_parker | 2018-01-05 00:47:23 -0800 (Fri, 05 Jan 2018) | 10 lines

[DAGCombine] Fix for PR37563

While searching for loads to be narrowed, equal sized loads were not
added to the list, resulting in anyext loads not being converted to
zext loads.

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

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

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

llvm-svn: 322671
2018-01-17 15:54:25 +00:00
Hans Wennborg
7b7f4c7e4f Merging r321991:
------------------------------------------------------------------------
r321991 | sam_parker | 2018-01-08 05:21:24 -0800 (Mon, 08 Jan 2018) | 9 lines

[DAGCombine] Fix for PR35761

I had falsely assumed that constant operands would be operand(1) of
the bin ops that may need their constant operand to be masked.

Bugzilla: https://bugs.llvm.org/show_bug.cgi?id=35761

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

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

llvm-svn: 322670
2018-01-17 15:50:27 +00:00
Hans Wennborg
c3a8a286c2 Merging r321993:
------------------------------------------------------------------------
r321993 | abataev | 2018-01-08 06:33:11 -0800 (Mon, 08 Jan 2018) | 11 lines

[SLP] Fix PR35628: Count external uses on extra reduction arguments.

Summary:
If the vectorized value is marked as extra reduction argument, its users
are not considered as external users. Patch fixes this.

Reviewers: mkuper, hfinkel, RKSimon, spatel

Subscribers: llvm-commits

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

llvm-svn: 322669
2018-01-17 15:47:38 +00:00
Hans Wennborg
07a1fe8fe7 Merging r321963:
------------------------------------------------------------------------
r321963 | dim | 2018-01-07 08:45:11 -0800 (Sun, 07 Jan 2018) | 51 lines

Add pre-C++11 is_constructible wrappers for 3 arguments

Summary:
After rL319736 for D28253 (which fixes PR28929), gcc cannot compile `<memory>` anymore in pre-C+11 modes, complaining:

```
In file included from /usr/include/c++/v1/memory:648:0,
                 from test.cpp:1:
/usr/include/c++/v1/memory: In static member function 'static std::__1::shared_ptr<_Tp> std::__1::shared_ptr<_Tp>::make_shared(_A0&, _A1&, _A2&)':
/usr/include/c++/v1/memory:4365:5: error: wrong number of template arguments (4, should be at least 1)
     static_assert((is_constructible<_Tp, _A0, _A1, _A2>::value), "Can't construct object in make_shared" );
     ^
In file included from /usr/include/c++/v1/memory:649:0,
                 from test.cpp:1:
/usr/include/c++/v1/type_traits:3198:29: note: provided for 'template<class _Tp, class _A0, class _A1> struct std::__1::is_constructible'
 struct _LIBCPP_TEMPLATE_VIS is_constructible
                             ^~~~~~~~~~~~~~~~
In file included from /usr/include/c++/v1/memory:648:0,
                 from test.cpp:1:
/usr/include/c++/v1/memory:4365:5: error: template argument 1 is invalid
     static_assert((is_constructible<_Tp, _A0, _A1, _A2>::value), "Can't construct object in make_shared" );
     ^
/usr/include/c++/v1/memory: In static member function 'static std::__1::shared_ptr<_Tp> std::__1::shared_ptr<_Tp>::allocate_shared(const _Alloc&, _A0&, _A1&, _A2&)':
/usr/include/c++/v1/memory:4444:5: error: wrong number of template arguments (4, should be at least 1)
     static_assert((is_constructible<_Tp, _A0, _A1, _A2>::value), "Can't construct object in allocate_shared" );
     ^
In file included from /usr/include/c++/v1/memory:649:0,
                 from test.cpp:1:
/usr/include/c++/v1/type_traits:3198:29: note: provided for 'template<class _Tp, class _A0, class _A1> struct std::__1::is_constructible'
 struct _LIBCPP_TEMPLATE_VIS is_constructible
                             ^~~~~~~~~~~~~~~~
In file included from /usr/include/c++/v1/memory:648:0,
                 from test.cpp:1:
/usr/include/c++/v1/memory:4444:5: error: template argument 1 is invalid
     static_assert((is_constructible<_Tp, _A0, _A1, _A2>::value), "Can't construct object in allocate_shared" );
     ^
```

This is also reported in https://bugs.freebsd.org/224946 (FreeBSD is apparently one of the very few projects that regularly builds programs against libc++ with gcc).

The reason is that the static assertions are invoking `is_constructible` with three arguments, while gcc does not have the built-in `is_constructible` feature, and the pre-C++11 `is_constructible` wrappers in `<type_traits>` only provide up to two arguments.

I have added additional wrappers for three arguments, modified the `is_constructible` entry point to take three arguments instead, and added a simple test to is_constructible.pass.cpp.

Reviewers: EricWF, mclow.lists

Reviewed By: EricWF

Subscribers: krytarowski, cfe-commits, emaste

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

llvm-svn: 322659
2018-01-17 14:26:10 +00:00
Hans Wennborg
1e8dfde53e Merging r322623:
------------------------------------------------------------------------
r322623 | avt77 | 2018-01-17 02:12:06 -0800 (Wed, 17 Jan 2018) | 3 lines

Allow usage of X86-prefixes as separate instrs.
Differential Revision: https://reviews.llvm.org/D42102

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

llvm-svn: 322654
2018-01-17 13:56:34 +00:00
Hans Wennborg
d8f5d67c8e Merging r322390:
------------------------------------------------------------------------
r322390 | vsapsai | 2018-01-12 10:54:35 -0800 (Fri, 12 Jan 2018) | 20 lines

[Lex] Avoid out-of-bounds dereference in LexAngledStringLiteral.

Fix makes the loop in LexAngledStringLiteral more like the loops in
LexStringLiteral, LexCharConstant. When we skip a character after
backslash, we need to check if we reached the end of the file instead of
reading the next character unconditionally.

Discovered by OSS-Fuzz:
https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=3832

rdar://problem/35572754

Reviewers: arphaman, kcc, rsmith, dexonsmith

Reviewed By: rsmith, dexonsmith

Subscribers: cfe-commits, rsmith, dexonsmith

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

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

llvm-svn: 322649
2018-01-17 13:24:15 +00:00
Hans Wennborg
8f38b5ed5e Merging r322264:
------------------------------------------------------------------------
r322264 | dim | 2018-01-11 00:03:22 -0800 (Thu, 11 Jan 2018) | 17 lines

Fix thread race between SectionPiece's OutputOff and Live members

Summary:
As reported in bug 35788, rL316280 reintroduces a race between two
members of SectionPiece, which share the same 64 bit memory location.

To fix the race, check the hash before checking the Live member, as
suggested by Rafael.

Reviewers: ruiu, rafael

Reviewed By: ruiu

Subscribers: smeenai, emaste, llvm-commits

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

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

llvm-svn: 322648
2018-01-17 13:14:51 +00:00
Hans Wennborg
5b930ddfed Merging r322041:
------------------------------------------------------------------------
r322041 | ruiu | 2018-01-08 15:12:42 -0800 (Mon, 08 Jan 2018) | 9 lines

Do not use parallelForEach to call maybeCompress().

Currently LLVM's paralellForEach has a problem with reentracy.
That caused https://bugs.llvm.org/show_bug.cgi?id=35788 (lld somtimes
hangs while linking Ruby 2.4) because maybeCompress calls writeTo which
uses paralellForEach.

This patch is to avoid using paralellForEach to call maybeCompress
to workaround the issue.
------------------------------------------------------------------------

llvm-svn: 322647
2018-01-17 13:13:00 +00:00
Hans Wennborg
df84b50eed Merging r322056:
------------------------------------------------------------------------
r322056 | skatkov | 2018-01-08 20:37:06 -0800 (Mon, 08 Jan 2018) | 13 lines

[CGP] Fix Complex addressing mode for offset

If the offset is differ in two addressing mode we can continue only if
ScaleReg is not set due to we will use it as merge of different offsets.

It should fix PR35799 and PR35805.

Reviewers: john.brawn, reames
Reviewed By: reames
Subscribers: llvm-commits
Differential Revision: https://reviews.llvm.org/D41227


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

llvm-svn: 322645
2018-01-17 13:01:33 +00:00
Hans Wennborg
62840bccf2 Merging r322259:
------------------------------------------------------------------------
r322259 | smeenai | 2018-01-10 22:57:01 -0800 (Wed, 10 Jan 2018) | 11 lines

[ELF] Fix SysV hash tables with --no-rosegment

When setting up the chain, we copy over the bucket's previous symbol
index, assuming that this index will be 0 (STN_UNDEF) for an unused
bucket (marking the end of the chain). When linking with --no-rosegment,
however, unused buckets will in fact contain the padding value, and so
the hash table will end up containing invalid chains. Zero out the hash
table section explicitly to avoid this, similar to what's already done
for GNU hash sections.

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

llvm-svn: 322642
2018-01-17 12:58:35 +00:00
Hans Wennborg
c559574c86 Merging r322236:
------------------------------------------------------------------------
r322236 | rsmith | 2018-01-10 15:08:26 -0800 (Wed, 10 Jan 2018) | 3 lines

In C++17, when instantiating an out-of-line definition of an inline static data
member, don't forget to instantiate the initializer too.

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

llvm-svn: 322641
2018-01-17 12:56:35 +00:00
Hans Wennborg
45bdca2f0c Merging r322160:
------------------------------------------------------------------------
r322160 | hahnfeld | 2018-01-10 00:10:23 -0800 (Wed, 10 Jan 2018) | 5 lines

[OMPT] Fix cast and printf of wait_id in lock test

This didn't work on 32 bit platforms.

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

llvm-svn: 322640
2018-01-17 12:52:58 +00:00
Hans Wennborg
1515bfe4fe Merging r322068:
------------------------------------------------------------------------
r322068 | pawosm01 | 2018-01-09 02:54:06 -0800 (Tue, 09 Jan 2018) | 3 lines

Fix type mismatch in omp_control_tool() implementation that makes it run incorrectly on 32-bit machines.

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

llvm-svn: 322639
2018-01-17 12:51:30 +00:00
Hans Wennborg
684dcd3dfe Merging r322350, r322405, r322420, r322593:
------------------------------------------------------------------------
r322350 | rtrieu | 2018-01-11 20:42:27 -0800 (Thu, 11 Jan 2018) | 6 lines

[ODRHash] Don't hash friend functions.

In certain combinations of templated classes and friend functions, the body
of friend functions does not get propagated along with function signature.
Exclude friend functions for hashing to avoid this case.

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

------------------------------------------------------------------------
r322405 | rtrieu | 2018-01-12 13:49:20 -0800 (Fri, 12 Jan 2018) | 2 lines

Disable test for Windows to fix Windows buildbots.

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

------------------------------------------------------------------------
r322420 | rtrieu | 2018-01-12 15:13:33 -0800 (Fri, 12 Jan 2018) | 2 lines

Try to suppress Windows testing again.

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

------------------------------------------------------------------------
r322593 | rtrieu | 2018-01-16 11:53:06 -0800 (Tue, 16 Jan 2018) | 6 lines

Add context to why test was disabled on Windows

test/Modules/odr_hash-Friend.cpp triggers an assertion in MicrosoftMangle.cpp
This has been reported in PR35939


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

llvm-svn: 322632
2018-01-17 11:41:51 +00:00
Hans Wennborg
96ad2f96bf ReleaseNotes: add Zig to External Open Source Projects
Patch by Andrew Kelley!

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

llvm-svn: 322567
2018-01-16 15:50:14 +00:00
Hans Wennborg
60151ce49c Merging r321986:
------------------------------------------------------------------------
r321986 | jhenderson | 2018-01-08 02:17:03 -0800 (Mon, 08 Jan 2018) | 25 lines

[ELF] Compress debug sections after assignAddresses and support custom layout

Previously, in r320472, I moved the calculation of section offsets and sizes
for compressed debug sections into maybeCompress, which happens before
assignAddresses, so that the compression had the required information. However,
I failed to take account of relocations that patch such sections. This had two
effects:

1. A race condition existed when a debug section referred to a different debug
section (see PR35788).
2. References to symbols in non-debug sections would be patched incorrectly.
This is because the addresses of such symbols are not calculated until after
assignAddresses (this was a partial regression caused by r320472, but they
could still have been broken before, in the event that a custom layout was used
in a linker script).

assignAddresses does not need to know about the output section size of
non-allocatable sections, because they do not affect the value of Dot. This
means that there is no longer a reason not to support custom layout of
compressed debug sections, as far as I'm aware. These two points allow for
delaying when maybeCompress can be called, removing the need for the loop I
previously added to calculate the section size, and therefore the race
condition. Furthermore, by delaying, we fix the issues of relocations getting
incorrect symbol values, because they have now all been finalized.

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

llvm-svn: 322565
2018-01-16 15:46:25 +00:00
Hans Wennborg
e2ef189d3f Merging r322018:
------------------------------------------------------------------------
r322018 | abataev | 2018-01-08 11:02:51 -0800 (Mon, 08 Jan 2018) | 9 lines

[OPENMP] Current status of OpenMP support.

Summary: Some info about supported features of OpenMP 4.5-5.0.

Reviewers: hfinkel, rsmith

Subscribers: kkwli0, Hahnfeld, cfe-commits

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

llvm-svn: 322564
2018-01-16 15:43:57 +00:00
Hans Wennborg
0458cc4c08 Merging r321983:
------------------------------------------------------------------------
r321983 | smeenai | 2018-01-07 21:58:36 -0800 (Sun, 07 Jan 2018) | 10 lines

[COFF] Initalize ErrorHandler with CanExitEarly value

Previously, the COFF driver would call exit(1) from the
ErrorHandler in the case of a link error, even if
CanExitEarly=false was specified. Now it initializes
the ErrorHandler in the same way that the ELF driver does.

Patch by Andrew Kelley.

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

llvm-svn: 322563
2018-01-16 15:40:23 +00:00
Hans Wennborg
1a95194553 Merging r321980:
------------------------------------------------------------------------
r321980 | phosek | 2018-01-07 18:23:10 -0800 (Sun, 07 Jan 2018) | 5 lines

[llvm-readobj] Support -needed-libs option for Mach-O files

This implements the -needed-libs option in Mach-O dumper.

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

llvm-svn: 322561
2018-01-16 15:29:26 +00:00
Hans Wennborg
c94ab43288 Merging r321964:
------------------------------------------------------------------------
r321964 | hahnfeld | 2018-01-07 08:54:36 -0800 (Sun, 07 Jan 2018) | 7 lines

Correct types of pointers to doacross_num_done

This field is defined as kmp_int32, so we should use neither
pointers to kmp_int64 nor 64 bit atomic instructions.
(Found while testing on a Raspberry Pi, 32 bit ARM)

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

llvm-svn: 322560
2018-01-16 15:18:22 +00:00
Hans Wennborg
756262deae Merging r321789:
------------------------------------------------------------------------
r321789 | hiraditya | 2018-01-03 23:47:24 -0800 (Wed, 03 Jan 2018) | 8 lines

[GVNHoist] Fix: PR35222 gvn-hoist incorrectly erases load in case of a loop

Reviewers:
    dberlin
    sebpop
    eli.friedman

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

llvm-svn: 322558
2018-01-16 15:00:51 +00:00
Hans Wennborg
7aa59cea55 Merging r321754:
------------------------------------------------------------------------
r321754 | adrian | 2018-01-03 11:10:21 -0800 (Wed, 03 Jan 2018) | 9 lines

-gmodules: Emit debug info for implicit module imports via #include.

When a type is only used as a template parameter and that type is the
only type imported from another #include'd module, no skeleton CU for
that module is generated, so a consumer doesn't know where to find the
type definition. By emitting an import declaration, we can force a
skeleton CU to be generated for each imported module.

rdar://problem/36266156
------------------------------------------------------------------------

llvm-svn: 322557
2018-01-16 14:55:42 +00:00
Hans Wennborg
c0067fe801 Merging r321771:
------------------------------------------------------------------------
r321771 | vedantk | 2018-01-03 15:11:32 -0800 (Wed, 03 Jan 2018) | 21 lines

[CGBuiltin] Handle unsigned mul overflow properly (PR35750)

r320902 fixed the IRGen for some types of checked multiplications. It
did not handle unsigned overflow correctly in the case where the signed
operand is negative (PR35750).

Eli pointed out that on overflow, the result must be equal to the unique
value that is equivalent to the mathematically-correct result modulo two
raised to the k power, where k is the number of bits in the result type.

This patch fixes the specialized IRGen from r320902 accordingly.

Testing: Apart from check-clang, I modified the test harness from
r320902 to validate the results of all multiplications -- not just the
ones which don't overflow:

  https://gist.github.com/vedantk/3eb9c88f82e5c32f2e590555b4af5081

llvm.org/PR35750, rdar://34963321

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

llvm-svn: 322555
2018-01-16 14:49:22 +00:00
Hans Wennborg
e2c7eaa7d6 Merging r321933:
------------------------------------------------------------------------
r321933 | xazax | 2018-01-06 02:51:00 -0800 (Sat, 06 Jan 2018) | 4 lines

[analyzer] Fix some check's output plist not containing the check name

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

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

llvm-svn: 322550
2018-01-16 12:58:37 +00:00
Hans Wennborg
9cf9cacdd0 Merging r322103:
------------------------------------------------------------------------
r322103 | tejohnson | 2018-01-09 10:32:53 -0800 (Tue, 09 Jan 2018) | 25 lines

Fix crash when linking metadata with ODR type uniquing

Summary:
With DebugTypeODRUniquing enabled, during IR linking debug metadata
in the destination module may be reached from the source module.
This means that ConstantAsMetadata nodes (e.g. on DITemplateValueParameter)
may contain a value the destination module. When trying to map such
metadata nodes, we will attempt to map a GV already in the dest module.
linkGlobalValueProto will end up with a source GV that is the same as
the dest GV as well as the new GV. Trying to access the TypeMap for the
source GV type, which is actually a dest GV type, hits an assertion
since it appears that we have mapped into the source module (because the
type is the value not a key into the map).

Detect that we don't need to access the TypeMap in this case, since
there is no need to create a bitcast from the new GV to the source GV
type as they GV are the same.

Fixes PR35722.

Reviewers: mehdi_amini, pcc

Subscribers: probinson, llvm-commits, eraman

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

llvm-svn: 322545
2018-01-16 12:06:38 +00:00
Hans Wennborg
14adc65b3b Merging r322518:
------------------------------------------------------------------------
r322518 | erichkeane | 2018-01-15 13:16:25 -0800 (Mon, 15 Jan 2018) | 4 lines

Revert 319303: Add _Float128 as alias to __float128 to enable compilations on Fedora27/glibc2

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

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

llvm-svn: 322539
2018-01-16 09:21:32 +00:00
Dan Liew
2f568b1b07 [docs] Add JFS as an external project built againt LLVM 6.0.
llvm-svn: 322287
2018-01-11 16:24:04 +00:00
Martin Storsjo
b00187f1ed [docs] Mention SjLj fixes in the release notes
Enabling SjLj on x86 on platforms where it isn't used by default
was partially implemented before 6.0, but didn't actually fully
work until now.

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

llvm-svn: 322059
2018-01-09 07:09:28 +00:00
Hans Wennborg
9dd5a02f47 Merging r321762:
------------------------------------------------------------------------
r321762 | juliehockett | 2018-01-03 14:10:11 -0800 (Wed, 03 Jan 2018) | 8 lines

[clang-tidy] Update fuchsia-overloaded-operator to check for valid loc

Updating fuchsia-overloaded-operator check to not issue warnings for
invalid locations.

Fixes PR35803.

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

llvm-svn: 321800
2018-01-04 14:24:01 +00:00
Martin Storsjo
d20581fb7a [docs] Mention support for Windows/ARM64 in the release notes
Differential Revision: https://reviews.llvm.org/D41711

llvm-svn: 321788
2018-01-04 07:43:41 +00:00
Martin Storsjo
de908acb25 [docs] Add preliminary release notes for LLD 6.0 for COFF
Differential Revision: https://reviews.llvm.org/D41710

llvm-svn: 321787
2018-01-04 07:42:11 +00:00
Hans Wennborg
c3d035b14f Regenerate ClangCommandLineReference.rst
$ bin/clang-tblgen -gen-opt-docs -I../cfe.src/include \
    -I../cfe.src/include/clang/Driver -I../llvm.src/include \
    ../cfe.src/include/clang/Driver/ClangOptionDocs.td \
    -o ../cfe.src/docs/ClangCommandLineReference.rst

llvm-svn: 321745
2018-01-03 17:06:59 +00:00
Hans Wennborg
56e99b09e7 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: 321744
2018-01-03 17:05:59 +00:00
Hans Wennborg
248fdae34d Drop 'svn' suffix from the version number.
llvm-svn: 321742
2018-01-03 16:58:58 +00:00
Hans Wennborg
4c44761f6a Drop 'svn' suffix from the version number.
llvm-svn: 321741
2018-01-03 16:58:46 +00:00
Hans Wennborg
84d446be47 Drop 'svn' suffix from the version number.
llvm-svn: 321740
2018-01-03 16:58:30 +00:00
Hans Wennborg
54da9b04a4 Drop 'svn' suffix from the version number.
llvm-svn: 321739
2018-01-03 16:58:21 +00:00
Hans Wennborg
0c04195745 Creating release_60 branch off revision 321711
llvm-svn: 321724
llvm-svn: 321723
llvm-svn: 321722
llvm-svn: 321721
llvm-svn: 321720
llvm-svn: 321719
llvm-svn: 321718
llvm-svn: 321717
llvm-svn: 321716
llvm-svn: 321714
llvm-svn: 321713
2018-01-03 14:54:55 +00:00
23455 changed files with 806751 additions and 1907020 deletions

View File

@@ -7,7 +7,6 @@ add_subdirectory(clang-tidy-vs)
endif()
add_subdirectory(change-namespace)
add_subdirectory(clang-doc)
add_subdirectory(clang-query)
add_subdirectory(clang-move)
add_subdirectory(clangd)

View File

@@ -4,7 +4,7 @@ LLVM Release License
University of Illinois/NCSA
Open Source License
Copyright (c) 2007-2018 University of Illinois at Urbana-Champaign.
Copyright (c) 2007-2016 University of Illinois at Urbana-Champaign.
All rights reserved.
Developed by:

View File

@@ -478,13 +478,13 @@ void ChangeNamespaceTool::registerMatchers(ast_matchers::MatchFinder *Finder) {
hasAncestor(namespaceDecl(isAnonymous())),
hasAncestor(cxxRecordDecl()))),
hasParent(namespaceDecl()));
Finder->addMatcher(
expr(allOf(hasAncestor(decl().bind("dc")), IsInMovedNs,
unless(hasAncestor(isImplicit())),
anyOf(callExpr(callee(FuncMatcher)).bind("call"),
declRefExpr(to(FuncMatcher.bind("func_decl")))
.bind("func_ref")))),
this);
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()),

View File

@@ -10,7 +10,6 @@ add_clang_library(clangApplyReplacements
clangBasic
clangRewrite
clangToolingCore
clangToolingRefactor
)
include_directories(

View File

@@ -18,7 +18,6 @@
#include "clang/Tooling/Core/Diagnostic.h"
#include "clang/Tooling/Refactoring.h"
#include "clang/Tooling/Refactoring/AtomicChange.h"
#include "llvm/ADT/StringMap.h"
#include "llvm/ADT/StringRef.h"
#include <string>
@@ -30,8 +29,15 @@ namespace clang {
class DiagnosticsEngine;
class Rewriter;
namespace format {
struct FormatStyle;
} // end namespace format
namespace replace {
/// \brief Collection of source ranges.
typedef std::vector<clang::tooling::Range> RangeVector;
/// \brief Collection of TranslationUnitReplacements.
typedef std::vector<clang::tooling::TranslationUnitReplacements> TUReplacements;
@@ -41,10 +47,10 @@ typedef std::vector<std::string> TUReplacementFiles;
/// \brief Collection of TranslationUniDiagnostics.
typedef std::vector<clang::tooling::TranslationUnitDiagnostics> TUDiagnostics;
/// \brief Map mapping file name to a set of AtomicChange targeting that file.
/// \brief Map mapping file name to Replacements targeting that file.
typedef llvm::DenseMap<const clang::FileEntry *,
std::vector<tooling::AtomicChange>>
FileToChangesMap;
std::vector<clang::tooling::Replacement>>
FileToReplacementsMap;
/// \brief Recursively descends through a directory structure rooted at \p
/// Directory and attempts to deserialize *.yaml files as
@@ -71,39 +77,65 @@ std::error_code collectReplacementsFromDirectory(
const llvm::StringRef Directory, TUDiagnostics &TUs,
TUReplacementFiles &TUFiles, clang::DiagnosticsEngine &Diagnostics);
/// \brief Deduplicate, check for conflicts, and extract all Replacements stored
/// in \c TUs. Conflicting replacements are skipped.
/// \brief Deduplicate, check for conflicts, and apply all Replacements stored
/// in \c TUs. If conflicts occur, no Replacements are applied.
///
/// \post For all (key,value) in FileChanges, value[i].getOffset() <=
/// \post For all (key,value) in GroupedReplacements, value[i].getOffset() <=
/// value[i+1].getOffset().
///
/// \param[in] TUs Collection of TranslationUnitReplacements or
/// TranslationUnitDiagnostics to merge, deduplicate, and test for conflicts.
/// \param[out] FileChanges Container grouping all changes by the
/// file they target. Only non conflicting replacements are kept into
/// FileChanges.
/// TranslationUnitDiagnostics to merge,
/// deduplicate, and test for conflicts.
/// \param[out] GroupedReplacements Container grouping all Replacements by the
/// file they target.
/// \param[in] SM SourceManager required for conflict reporting.
///
/// \returns \parblock
/// \li true If all changes were converted successfully.
/// \li true If all changes were applied successfully.
/// \li false If there were conflicts.
bool mergeAndDeduplicate(const TUReplacements &TUs, const TUDiagnostics &TUDs,
FileToChangesMap &FileChanges,
bool mergeAndDeduplicate(const TUReplacements &TUs,
FileToReplacementsMap &GroupedReplacements,
clang::SourceManager &SM);
/// \brief Apply \c AtomicChange on File and rewrite it.
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] File Path of the file where to apply AtomicChange.
/// \param[in] Changes to apply.
/// \param[in] Spec For code cleanup and formatting.
/// \param[in] Diagnostics DiagnosticsEngine used for error output.
/// \param[in] GroupedReplacements Deduplicated and conflict free Replacements
/// to apply.
/// \param[out] Rewrites The results of applying replacements will be applied
/// to this Rewriter.
///
/// \returns The changed code if all changes are applied successfully;
/// otherwise, an llvm::Error carrying llvm::StringError or an error_code.
llvm::Expected<std::string>
applyChanges(StringRef File, const std::vector<tooling::AtomicChange> &Changes,
const tooling::ApplyChangesSpec &Spec,
DiagnosticsEngine &Diagnostics);
/// \returns \parblock
/// \li true If all changes were applied successfully.
/// \li false If a replacement failed to apply.
bool applyReplacements(const FileToReplacementsMap &GroupedReplacements,
clang::Rewriter &Rewrites);
/// \brief Given a collection of Replacements for a single file, produces a list
/// of source ranges that enclose those Replacements.
///
/// \pre Replacements[i].getOffset() <= Replacements[i+1].getOffset().
///
/// \param[in] Replacements Replacements from a single file.
///
/// \returns Collection of source ranges that enclose all given Replacements.
/// One range is created for each replacement.
RangeVector calculateChangedRanges(
const std::vector<clang::tooling::Replacement> &Replacements);
/// \brief Write the contents of \c FileContents to disk. Keys of the map are
/// filenames and values are the new contents for those files.
///
/// \param[in] Rewrites Rewriter containing written files to write to disk.
bool writeFiles(const clang::Rewriter &Rewrites);
/// \brief Delete the replacement files.
///

View File

@@ -80,9 +80,10 @@ std::error_code collectReplacementsFromDirectory(
return ErrorCode;
}
std::error_code collectReplacementsFromDirectory(
const llvm::StringRef Directory, TUDiagnostics &TUs,
TUReplacementFiles &TUFiles, clang::DiagnosticsEngine &Diagnostics) {
std::error_code
collectReplacementsFromDirectory(const llvm::StringRef Directory,
TUDiagnostics &TUs, TUReplacementFiles &TUFiles,
clang::DiagnosticsEngine &Diagnostics) {
using namespace llvm::sys::fs;
using namespace llvm::sys::path;
@@ -124,109 +125,259 @@ std::error_code collectReplacementsFromDirectory(
return ErrorCode;
}
/// \brief Extract replacements from collected TranslationUnitReplacements and
/// TranslationUnitDiagnostics and group them per file.
/// \brief Dumps information for a sequence of conflicting Replacements.
///
/// \param[in] TUs Collection of all found and deserialized
/// TranslationUnitReplacements.
/// \param[in] TUDs Collection of all found and deserialized
/// TranslationUnitDiagnostics.
/// \param[in] SM Used to deduplicate paths.
///
/// \returns A map mapping FileEntry to a set of Replacement targeting that
/// file.
static llvm::DenseMap<const FileEntry *, std::vector<tooling::Replacement>>
groupReplacements(const TUReplacements &TUs, const TUDiagnostics &TUDs,
const clang::SourceManager &SM) {
std::set<StringRef> Warned;
llvm::DenseMap<const FileEntry *, std::vector<tooling::Replacement>>
GroupedReplacements;
/// \param[in] File FileEntry for the file the conflicting Replacements are
/// for.
/// \param[in] ConflictingReplacements List of conflicting Replacements.
/// \param[in] SM SourceManager used for reporting.
static void reportConflict(
const FileEntry *File,
const llvm::ArrayRef<clang::tooling::Replacement> ConflictingReplacements,
SourceManager &SM) {
FileID FID = SM.translateFile(File);
if (FID.isInvalid())
FID = SM.createFileID(File, SourceLocation(), SrcMgr::C_User);
auto AddToGroup = [&](const tooling::Replacement &R) {
// Use the file manager to deduplicate paths. FileEntries are
// automatically canonicalized.
if (const FileEntry *Entry = SM.getFileManager().getFile(R.getFilePath())) {
GroupedReplacements[Entry].push_back(R);
} else if (Warned.insert(R.getFilePath()).second) {
errs() << "Described file '" << R.getFilePath()
<< "' doesn't exist. Ignoring...\n";
// FIXME: Output something a little more user-friendly (e.g. unified diff?)
errs() << "The following changes conflict:\n";
for (const tooling::Replacement &R : ConflictingReplacements) {
if (R.getLength() == 0) {
errs() << " Insert at " << SM.getLineNumber(FID, R.getOffset()) << ":"
<< SM.getColumnNumber(FID, R.getOffset()) << " "
<< R.getReplacementText() << "\n";
} else {
if (R.getReplacementText().empty())
errs() << " Remove ";
else
errs() << " Replace ";
errs() << SM.getLineNumber(FID, R.getOffset()) << ":"
<< SM.getColumnNumber(FID, R.getOffset()) << "-"
<< SM.getLineNumber(FID, R.getOffset() + R.getLength() - 1) << ":"
<< SM.getColumnNumber(FID, R.getOffset() + R.getLength() - 1);
if (R.getReplacementText().empty())
errs() << "\n";
else
errs() << " with \"" << R.getReplacementText() << "\"\n";
}
}
}
// FIXME: Remove this function after changing clang-apply-replacements to use
// Replacements class.
bool applyAllReplacements(const std::vector<tooling::Replacement> &Replaces,
Rewriter &Rewrite) {
bool Result = true;
for (auto I = Replaces.begin(), E = Replaces.end(); I != E; ++I) {
if (I->isApplicable()) {
Result = I->apply(Rewrite) && Result;
} else {
Result = false;
}
}
return Result;
}
// FIXME: moved from libToolingCore. remove this when std::vector<Replacement>
// is replaced with tooling::Replacements class.
static void deduplicate(std::vector<tooling::Replacement> &Replaces,
std::vector<tooling::Range> &Conflicts) {
if (Replaces.empty())
return;
auto LessNoPath = [](const tooling::Replacement &LHS,
const tooling::Replacement &RHS) {
if (LHS.getOffset() != RHS.getOffset())
return LHS.getOffset() < RHS.getOffset();
if (LHS.getLength() != RHS.getLength())
return LHS.getLength() < RHS.getLength();
return LHS.getReplacementText() < RHS.getReplacementText();
};
for (const auto &TU : TUs)
for (const tooling::Replacement &R : TU.Replacements)
AddToGroup(R);
auto EqualNoPath = [](const tooling::Replacement &LHS,
const tooling::Replacement &RHS) {
return LHS.getOffset() == RHS.getOffset() &&
LHS.getLength() == RHS.getLength() &&
LHS.getReplacementText() == RHS.getReplacementText();
};
for (const auto &TU : TUDs)
for (const auto &D : TU.Diagnostics)
for (const auto &Fix : D.Fix)
for (const tooling::Replacement &R : Fix.second)
AddToGroup(R);
// 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());
// Sort replacements per file to keep consistent behavior when
// clang-apply-replacements run on differents machine.
for (auto &FileAndReplacements : GroupedReplacements) {
llvm::sort(FileAndReplacements.second.begin(),
FileAndReplacements.second.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;
}
}
return GroupedReplacements;
if (ConflictLength > 1)
Conflicts.push_back(tooling::Range(ConflictStart, ConflictLength));
}
bool mergeAndDeduplicate(const TUReplacements &TUs, const TUDiagnostics &TUDs,
FileToChangesMap &FileChanges,
clang::SourceManager &SM) {
auto GroupedReplacements = groupReplacements(TUs, TUDs, SM);
bool ConflictDetected = false;
/// \brief Deduplicates and tests for conflicts among the replacements for each
/// file in \c Replacements. Any conflicts found are reported.
///
/// \post Replacements[i].getOffset() <= Replacements[i+1].getOffset().
///
/// \param[in,out] Replacements Container of all replacements grouped by file
/// to be deduplicated and checked for conflicts.
/// \param[in] SM SourceManager required for conflict reporting.
///
/// \returns \parblock
/// \li true if conflicts were detected
/// \li false if no conflicts were detected
static bool deduplicateAndDetectConflicts(FileToReplacementsMap &Replacements,
SourceManager &SM) {
bool conflictsFound = false;
// To report conflicting replacements on corresponding file, all replacements
// are stored into 1 big AtomicChange.
for (const auto &FileAndReplacements : GroupedReplacements) {
for (auto &FileAndReplacements : Replacements) {
const FileEntry *Entry = FileAndReplacements.first;
const SourceLocation BeginLoc =
SM.getLocForStartOfFile(SM.getOrCreateFileID(Entry, SrcMgr::C_User));
tooling::AtomicChange FileChange(Entry->getName(), Entry->getName());
for (const auto &R : FileAndReplacements.second) {
llvm::Error Err =
FileChange.replace(SM, BeginLoc.getLocWithOffset(R.getOffset()),
R.getLength(), R.getReplacementText());
if (Err) {
// FIXME: This will report conflicts by pair using a file+offset format
// which is not so much human readable.
// A first improvement could be to translate offset to line+col. For
// this and without loosing error message some modifications arround
// `tooling::ReplacementError` are need (access to
// `getReplacementErrString`).
// A better strategy could be to add a pretty printer methods for
// conflict reporting. Methods that could be parameterized to report a
// conflict in different format, file+offset, file+line+col, or even
// more human readable using VCS conflict markers.
// For now, printing directly the error reported by `AtomicChange` is
// the easiest solution.
errs() << llvm::toString(std::move(Err)) << "\n";
ConflictDetected = true;
auto &Replacements = FileAndReplacements.second;
assert(Entry != nullptr && "No file entry!");
std::vector<tooling::Range> Conflicts;
deduplicate(FileAndReplacements.second, Conflicts);
if (Conflicts.empty())
continue;
conflictsFound = true;
errs() << "There are conflicting changes to " << Entry->getName() << ":\n";
for (const tooling::Range &Conflict : Conflicts) {
auto ConflictingReplacements = llvm::makeArrayRef(
&Replacements[Conflict.getOffset()], Conflict.getLength());
reportConflict(Entry, ConflictingReplacements, SM);
}
}
return conflictsFound;
}
bool mergeAndDeduplicate(const TUReplacements &TUs,
FileToReplacementsMap &GroupedReplacements,
clang::SourceManager &SM) {
// Group all replacements by target file.
std::set<StringRef> Warned;
for (const auto &TU : TUs) {
for (const tooling::Replacement &R : TU.Replacements) {
// Use the file manager to deduplicate paths. FileEntries are
// automatically canonicalized.
if (const FileEntry *Entry = SM.getFileManager().getFile(R.getFilePath())) {
GroupedReplacements[Entry].push_back(R);
} else if (Warned.insert(R.getFilePath()).second) {
errs() << "Described file '" << R.getFilePath()
<< "' doesn't exist. Ignoring...\n";
}
}
FileChanges.try_emplace(Entry,
std::vector<tooling::AtomicChange>{FileChange});
}
return !ConflictDetected;
// Ask clang to deduplicate and report conflicts.
return !deduplicateAndDetectConflicts(GroupedReplacements, SM);
}
llvm::Expected<std::string>
applyChanges(StringRef File, const std::vector<tooling::AtomicChange> &Changes,
const tooling::ApplyChangesSpec &Spec,
DiagnosticsEngine &Diagnostics) {
FileManager Files((FileSystemOptions()));
SourceManager SM(Diagnostics, Files);
bool mergeAndDeduplicate(const TUDiagnostics &TUs,
FileToReplacementsMap &GroupedReplacements,
clang::SourceManager &SM) {
llvm::ErrorOr<std::unique_ptr<MemoryBuffer>> Buffer =
SM.getFileManager().getBufferForFile(File);
if (!Buffer)
return errorCodeToError(Buffer.getError());
return tooling::applyAtomicChanges(File, Buffer.get()->getBuffer(), Changes,
Spec);
// 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.
if (const FileEntry *Entry = SM.getFileManager().getFile(R.getFilePath())) {
GroupedReplacements[Entry].push_back(R);
} else if (Warned.insert(R.getFilePath()).second) {
errs() << "Described file '" << R.getFilePath()
<< "' doesn't exist. Ignoring...\n";
}
}
}
}
}
// Ask clang to deduplicate and report conflicts.
return !deduplicateAndDetectConflicts(GroupedReplacements, SM);
}
bool applyReplacements(const FileToReplacementsMap &GroupedReplacements,
clang::Rewriter &Rewrites) {
// Apply all changes
//
// FIXME: No longer certain GroupedReplacements is really the best kind of
// data structure for applying replacements. Rewriter certainly doesn't care.
// However, until we nail down the design of ReplacementGroups, might as well
// leave this as is.
for (const auto &FileAndReplacements : GroupedReplacements) {
if (!applyAllReplacements(FileAndReplacements.second, Rewrites))
return false;
}
return true;
}
RangeVector calculateChangedRanges(
const std::vector<clang::tooling::Replacement> &Replaces) {
RangeVector ChangedRanges;
// Generate the new ranges from the replacements.
int Shift = 0;
for (const tooling::Replacement &R : Replaces) {
unsigned Offset = R.getOffset() + Shift;
unsigned Length = R.getReplacementText().size();
Shift += Length - R.getLength();
ChangedRanges.push_back(tooling::Range(Offset, Length));
}
return ChangedRanges;
}
bool writeFiles(const clang::Rewriter &Rewrites) {
for (auto BufferI = Rewrites.buffer_begin(), BufferE = Rewrites.buffer_end();
BufferI != BufferE; ++BufferI) {
StringRef FileName =
Rewrites.getSourceMgr().getFileEntryForID(BufferI->first)->getName();
std::error_code EC;
llvm::raw_fd_ostream FileStream(FileName, EC, llvm::sys::fs::F_Text);
if (EC) {
errs() << "Warning: Could not write to " << EC.message() << "\n";
continue;
}
BufferI->second.write(FileStream);
}
return true;
}
bool deleteReplacementFiles(const TUReplacementFiles &Files,

View File

@@ -2,7 +2,7 @@ set(LLVM_LINK_COMPONENTS
Support
)
add_clang_tool(clang-apply-replacements
add_clang_executable(clang-apply-replacements
ClangApplyReplacementsMain.cpp
)
target_link_libraries(clang-apply-replacements
@@ -12,7 +12,6 @@ target_link_libraries(clang-apply-replacements
clangFormat
clangRewrite
clangToolingCore
clangToolingRefactor
)
install(TARGETS clang-apply-replacements

View File

@@ -86,6 +86,116 @@ static void printVersion(raw_ostream &OS) {
OS << "clang-apply-replacements version " CLANG_VERSION_STRING << "\n";
}
/// \brief Convenience function to get rewritten content for \c Filename from
/// \c Rewrites.
///
/// \pre Replacements[i].getFilePath() == Replacements[i+1].getFilePath().
/// \post Replacements.empty() -> Result.empty()
///
/// \param[in] Replacements Replacements to apply
/// \param[in] Rewrites Rewriter to use to apply replacements.
/// \param[out] Result Contents of the file after applying replacements if
/// replacements were provided.
///
/// \returns \parblock
/// \li true if all replacements were applied successfully.
/// \li false if at least one replacement failed to apply.
static bool
getRewrittenData(const std::vector<tooling::Replacement> &Replacements,
Rewriter &Rewrites, std::string &Result) {
if (Replacements.empty())
return true;
if (!applyAllReplacements(Replacements, Rewrites))
return false;
SourceManager &SM = Rewrites.getSourceMgr();
FileManager &Files = SM.getFileManager();
StringRef FileName = Replacements.begin()->getFilePath();
const clang::FileEntry *Entry = Files.getFile(FileName);
assert(Entry && "Expected an existing file");
FileID ID = SM.translateFile(Entry);
assert(ID.isValid() && "Expected a valid FileID");
const RewriteBuffer *Buffer = Rewrites.getRewriteBufferFor(ID);
Result = std::string(Buffer->begin(), Buffer->end());
return true;
}
/// \brief Apply \c Replacements and return the new file contents.
///
/// \pre Replacements[i].getFilePath() == Replacements[i+1].getFilePath().
/// \post Replacements.empty() -> Result.empty()
///
/// \param[in] Replacements Replacements to apply.
/// \param[out] Result Contents of the file after applying replacements if
/// replacements were provided.
/// \param[in] Diagnostics For diagnostic output.
///
/// \returns \parblock
/// \li true if all replacements applied successfully.
/// \li false if at least one replacement failed to apply.
static bool
applyReplacements(const std::vector<tooling::Replacement> &Replacements,
std::string &Result, DiagnosticsEngine &Diagnostics) {
FileManager Files((FileSystemOptions()));
SourceManager SM(Diagnostics, Files);
Rewriter Rewrites(SM, LangOptions());
return getRewrittenData(Replacements, Rewrites, Result);
}
/// \brief Apply code formatting to all places where replacements were made.
///
/// \pre !Replacements.empty().
/// \pre Replacements[i].getFilePath() == Replacements[i+1].getFilePath().
/// \pre Replacements[i].getOffset() <= Replacements[i+1].getOffset().
///
/// \param[in] Replacements Replacements that were made to the file. Provided
/// to indicate where changes were made.
/// \param[in] FileData The contents of the file \b after \c Replacements have
/// been applied.
/// \param[out] FormattedFileData The contents of the file after reformatting.
/// \param[in] FormatStyle Style to apply.
/// \param[in] Diagnostics For diagnostic output.
///
/// \returns \parblock
/// \li true if reformatting replacements were all successfully
/// applied.
/// \li false if at least one reformatting replacement failed to apply.
static bool
applyFormatting(const std::vector<tooling::Replacement> &Replacements,
const StringRef FileData, std::string &FormattedFileData,
const format::FormatStyle &FormatStyle,
DiagnosticsEngine &Diagnostics) {
assert(!Replacements.empty() && "Need at least one replacement");
RangeVector Ranges = calculateChangedRanges(Replacements);
StringRef FileName = Replacements.begin()->getFilePath();
tooling::Replacements R =
format::reformat(FormatStyle, FileData, Ranges, FileName);
// FIXME: Remove this copy when tooling::Replacements is implemented as a
// vector instead of a set.
std::vector<tooling::Replacement> FormattingReplacements;
std::copy(R.begin(), R.end(), back_inserter(FormattingReplacements));
if (FormattingReplacements.empty()) {
FormattedFileData = FileData;
return true;
}
FileManager Files((FileSystemOptions()));
SourceManager SM(Diagnostics, Files);
SM.overrideFileContents(Files.getFile(FileName),
llvm::MemoryBuffer::getMemBufferCopy(FileData));
Rewriter Rewrites(SM, LangOptions());
return getRewrittenData(FormattingReplacements, Rewrites, FormattedFileData);
}
int main(int argc, char **argv) {
cl::HideUnrelatedOptions(makeArrayRef(VisibleCategories));
@@ -97,13 +207,16 @@ int main(int argc, char **argv) {
IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs()), DiagOpts.get());
// Determine a formatting style from options.
auto FormatStyleOrError =
format::getStyle(FormatStyleOpt, FormatStyleConfig, "LLVM");
if (!FormatStyleOrError) {
llvm::errs() << llvm::toString(FormatStyleOrError.takeError()) << "\n";
return 1;
format::FormatStyle FormatStyle;
if (DoFormat) {
auto FormatStyleOrError =
format::getStyle(FormatStyleOpt, FormatStyleConfig, "LLVM");
if (!FormatStyleOrError) {
llvm::errs() << llvm::toString(FormatStyleOrError.takeError()) << "\n";
return 1;
}
FormatStyle = *FormatStyleOrError;
}
format::FormatStyle FormatStyle = std::move(*FormatStyleOrError);
TUReplacements TURs;
TUReplacementFiles TUFiles;
@@ -131,23 +244,34 @@ int main(int argc, char **argv) {
FileManager Files((FileSystemOptions()));
SourceManager SM(Diagnostics, Files);
FileToChangesMap Changes;
if (!mergeAndDeduplicate(TURs, TUDs, Changes, SM))
FileToReplacementsMap GroupedReplacements;
if (!mergeAndDeduplicate(TURs, GroupedReplacements, SM))
return 1;
if (!mergeAndDeduplicate(TUDs, GroupedReplacements, SM))
return 1;
tooling::ApplyChangesSpec Spec;
Spec.Cleanup = true;
Spec.Style = FormatStyle;
Spec.Format = DoFormat ? tooling::ApplyChangesSpec::kAll
: tooling::ApplyChangesSpec::kNone;
Rewriter ReplacementsRewriter(SM, LangOptions());
for (const auto &FileChange : Changes) {
const FileEntry *Entry = FileChange.first;
StringRef FileName = Entry->getName();
llvm::Expected<std::string> NewFileData =
applyChanges(FileName, FileChange.second, Spec, Diagnostics);
if (!NewFileData) {
errs() << llvm::toString(NewFileData.takeError()) << "\n";
for (const auto &FileAndReplacements : GroupedReplacements) {
// This shouldn't happen but if a file somehow has no replacements skip to
// next file.
if (FileAndReplacements.second.empty())
continue;
std::string NewFileData;
StringRef FileName = FileAndReplacements.first->getName();
if (!applyReplacements(FileAndReplacements.second, NewFileData,
Diagnostics)) {
errs() << "Failed to apply replacements to " << FileName << "\n";
continue;
}
// Apply formatting if requested.
if (DoFormat &&
!applyFormatting(FileAndReplacements.second, NewFileData, NewFileData,
FormatStyle, Diagnostics)) {
errs() << "Failed to apply reformatting replacements for " << FileName
<< "\n";
continue;
}
@@ -158,7 +282,8 @@ int main(int argc, char **argv) {
llvm::errs() << "Could not open " << FileName << " for writing\n";
continue;
}
FileStream << *NewFileData;
FileStream << NewFileData;
}
return 0;

View File

@@ -1,622 +0,0 @@
//===-- BitcodeReader.cpp - ClangDoc Bitcode Reader ------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "BitcodeReader.h"
#include "llvm/ADT/IndexedMap.h"
#include "llvm/ADT/Optional.h"
#include "llvm/Support/raw_ostream.h"
namespace clang {
namespace doc {
using Record = llvm::SmallVector<uint64_t, 1024>;
bool decodeRecord(Record R, llvm::SmallVectorImpl<char> &Field,
llvm::StringRef Blob) {
Field.assign(Blob.begin(), Blob.end());
return true;
}
bool decodeRecord(Record R, SymbolID &Field, llvm::StringRef Blob) {
if (R[0] != BitCodeConstants::USRHashSize)
return false;
// First position in the record is the length of the following array, so we
// copy the following elements to the field.
for (int I = 0, E = R[0]; I < E; ++I)
Field[I] = R[I + 1];
return true;
}
bool decodeRecord(Record R, bool &Field, llvm::StringRef Blob) {
Field = R[0] != 0;
return true;
}
bool decodeRecord(Record R, int &Field, llvm::StringRef Blob) {
if (R[0] > INT_MAX)
return false;
Field = (int)R[0];
return true;
}
bool decodeRecord(Record R, AccessSpecifier &Field, llvm::StringRef Blob) {
switch (R[0]) {
case AS_public:
case AS_private:
case AS_protected:
case AS_none:
Field = (AccessSpecifier)R[0];
return true;
default:
return false;
}
}
bool decodeRecord(Record R, TagTypeKind &Field, llvm::StringRef Blob) {
switch (R[0]) {
case TTK_Struct:
case TTK_Interface:
case TTK_Union:
case TTK_Class:
case TTK_Enum:
Field = (TagTypeKind)R[0];
return true;
default:
return false;
}
}
bool decodeRecord(Record R, llvm::Optional<Location> &Field,
llvm::StringRef Blob) {
if (R[0] > INT_MAX)
return false;
Field.emplace((int)R[0], Blob);
return true;
}
bool decodeRecord(Record R, InfoType &Field, llvm::StringRef Blob) {
switch (auto IT = static_cast<InfoType>(R[0])) {
case InfoType::IT_namespace:
case InfoType::IT_record:
case InfoType::IT_function:
case InfoType::IT_default:
case InfoType::IT_enum:
Field = IT;
return true;
}
return false;
}
bool decodeRecord(Record R, FieldId &Field, llvm::StringRef Blob) {
switch (auto F = static_cast<FieldId>(R[0])) {
case FieldId::F_namespace:
case FieldId::F_parent:
case FieldId::F_vparent:
case FieldId::F_type:
case FieldId::F_default:
Field = F;
return true;
}
return false;
}
bool decodeRecord(Record R, llvm::SmallVectorImpl<llvm::SmallString<16>> &Field,
llvm::StringRef Blob) {
Field.push_back(Blob);
return true;
}
bool decodeRecord(Record R, llvm::SmallVectorImpl<Location> &Field,
llvm::StringRef Blob) {
if (R[0] > INT_MAX)
return false;
Field.emplace_back((int)R[0], Blob);
return true;
}
bool parseRecord(Record R, unsigned ID, llvm::StringRef Blob,
const unsigned VersionNo) {
if (ID == VERSION && R[0] == VersionNo)
return true;
return false;
}
bool parseRecord(Record R, unsigned ID, llvm::StringRef Blob,
NamespaceInfo *I) {
switch (ID) {
case NAMESPACE_USR:
return decodeRecord(R, I->USR, Blob);
case NAMESPACE_NAME:
return decodeRecord(R, I->Name, Blob);
default:
return false;
}
}
bool parseRecord(Record R, unsigned ID, llvm::StringRef Blob, RecordInfo *I) {
switch (ID) {
case RECORD_USR:
return decodeRecord(R, I->USR, Blob);
case RECORD_NAME:
return decodeRecord(R, I->Name, Blob);
case RECORD_DEFLOCATION:
return decodeRecord(R, I->DefLoc, Blob);
case RECORD_LOCATION:
return decodeRecord(R, I->Loc, Blob);
case RECORD_TAG_TYPE:
return decodeRecord(R, I->TagType, Blob);
default:
return false;
}
}
bool parseRecord(Record R, unsigned ID, llvm::StringRef Blob, EnumInfo *I) {
switch (ID) {
case ENUM_USR:
return decodeRecord(R, I->USR, Blob);
case ENUM_NAME:
return decodeRecord(R, I->Name, Blob);
case ENUM_DEFLOCATION:
return decodeRecord(R, I->DefLoc, Blob);
case ENUM_LOCATION:
return decodeRecord(R, I->Loc, Blob);
case ENUM_MEMBER:
return decodeRecord(R, I->Members, Blob);
case ENUM_SCOPED:
return decodeRecord(R, I->Scoped, Blob);
default:
return false;
}
}
bool parseRecord(Record R, unsigned ID, llvm::StringRef Blob, FunctionInfo *I) {
switch (ID) {
case FUNCTION_USR:
return decodeRecord(R, I->USR, Blob);
case FUNCTION_NAME:
return decodeRecord(R, I->Name, Blob);
case FUNCTION_DEFLOCATION:
return decodeRecord(R, I->DefLoc, Blob);
case FUNCTION_LOCATION:
return decodeRecord(R, I->Loc, Blob);
case FUNCTION_ACCESS:
return decodeRecord(R, I->Access, Blob);
case FUNCTION_IS_METHOD:
return decodeRecord(R, I->IsMethod, Blob);
default:
return false;
}
}
bool parseRecord(Record R, unsigned ID, llvm::StringRef Blob, TypeInfo *I) {
return true;
}
bool parseRecord(Record R, unsigned ID, llvm::StringRef Blob,
FieldTypeInfo *I) {
switch (ID) {
case FIELD_TYPE_NAME:
return decodeRecord(R, I->Name, Blob);
default:
return false;
}
}
bool parseRecord(Record R, unsigned ID, llvm::StringRef Blob,
MemberTypeInfo *I) {
switch (ID) {
case MEMBER_TYPE_NAME:
return decodeRecord(R, I->Name, Blob);
case MEMBER_TYPE_ACCESS:
return decodeRecord(R, I->Access, Blob);
default:
return false;
}
}
bool parseRecord(Record R, unsigned ID, llvm::StringRef Blob, CommentInfo *I) {
switch (ID) {
case COMMENT_KIND:
return decodeRecord(R, I->Kind, Blob);
case COMMENT_TEXT:
return decodeRecord(R, I->Text, Blob);
case COMMENT_NAME:
return decodeRecord(R, I->Name, Blob);
case COMMENT_DIRECTION:
return decodeRecord(R, I->Direction, Blob);
case COMMENT_PARAMNAME:
return decodeRecord(R, I->ParamName, Blob);
case COMMENT_CLOSENAME:
return decodeRecord(R, I->CloseName, Blob);
case COMMENT_ATTRKEY:
return decodeRecord(R, I->AttrKeys, Blob);
case COMMENT_ATTRVAL:
return decodeRecord(R, I->AttrValues, Blob);
case COMMENT_ARG:
return decodeRecord(R, I->Args, Blob);
case COMMENT_SELFCLOSING:
return decodeRecord(R, I->SelfClosing, Blob);
case COMMENT_EXPLICIT:
return decodeRecord(R, I->Explicit, Blob);
default:
return false;
}
}
bool parseRecord(Record R, unsigned ID, llvm::StringRef Blob, Reference *I,
FieldId &F) {
switch (ID) {
case REFERENCE_USR:
return decodeRecord(R, I->USR, Blob);
case REFERENCE_NAME:
return decodeRecord(R, I->Name, Blob);
case REFERENCE_TYPE:
return decodeRecord(R, I->RefType, Blob);
case REFERENCE_FIELD:
return decodeRecord(R, F, Blob);
default:
return false;
}
}
template <typename T> CommentInfo *getCommentInfo(T I) {
llvm::errs() << "Cannot have comment subblock.\n";
exit(1);
}
template <> CommentInfo *getCommentInfo(FunctionInfo *I) {
I->Description.emplace_back();
return &I->Description.back();
}
template <> CommentInfo *getCommentInfo(NamespaceInfo *I) {
I->Description.emplace_back();
return &I->Description.back();
}
template <> CommentInfo *getCommentInfo(RecordInfo *I) {
I->Description.emplace_back();
return &I->Description.back();
}
template <> CommentInfo *getCommentInfo(EnumInfo *I) {
I->Description.emplace_back();
return &I->Description.back();
}
template <> CommentInfo *getCommentInfo(CommentInfo *I) {
I->Children.emplace_back(llvm::make_unique<CommentInfo>());
return I->Children.back().get();
}
template <> CommentInfo *getCommentInfo(std::unique_ptr<CommentInfo> &I) {
return getCommentInfo(I.get());
}
template <typename T, typename TTypeInfo>
void addTypeInfo(T I, TTypeInfo &&TI) {
llvm::errs() << "Invalid type for info.\n";
exit(1);
}
template <> void addTypeInfo(RecordInfo *I, MemberTypeInfo &&T) {
I->Members.emplace_back(std::move(T));
}
template <> void addTypeInfo(FunctionInfo *I, TypeInfo &&T) {
I->ReturnType = std::move(T);
}
template <> void addTypeInfo(FunctionInfo *I, FieldTypeInfo &&T) {
I->Params.emplace_back(std::move(T));
}
template <typename T> void addReference(T I, Reference &&R, FieldId F) {
llvm::errs() << "Invalid field type for info.\n";
exit(1);
}
template <> void addReference(TypeInfo *I, Reference &&R, FieldId F) {
switch (F) {
case FieldId::F_type:
I->Type = std::move(R);
break;
default:
llvm::errs() << "Invalid field type for info.\n";
exit(1);
}
}
template <> void addReference(FieldTypeInfo *I, Reference &&R, FieldId F) {
switch (F) {
case FieldId::F_type:
I->Type = std::move(R);
break;
default:
llvm::errs() << "Invalid field type for info.\n";
exit(1);
}
}
template <> void addReference(MemberTypeInfo *I, Reference &&R, FieldId F) {
switch (F) {
case FieldId::F_type:
I->Type = std::move(R);
break;
default:
llvm::errs() << "Invalid field type for info.\n";
exit(1);
}
}
template <> void addReference(EnumInfo *I, Reference &&R, FieldId F) {
switch (F) {
case FieldId::F_namespace:
I->Namespace.emplace_back(std::move(R));
break;
default:
llvm::errs() << "Invalid field type for info.\n";
exit(1);
}
}
template <> void addReference(NamespaceInfo *I, Reference &&R, FieldId F) {
switch (F) {
case FieldId::F_namespace:
I->Namespace.emplace_back(std::move(R));
break;
default:
llvm::errs() << "Invalid field type for info.\n";
exit(1);
}
}
template <> void addReference(FunctionInfo *I, Reference &&R, FieldId F) {
switch (F) {
case FieldId::F_namespace:
I->Namespace.emplace_back(std::move(R));
break;
case FieldId::F_parent:
I->Parent = std::move(R);
break;
default:
llvm::errs() << "Invalid field type for info.\n";
exit(1);
}
}
template <> void addReference(RecordInfo *I, Reference &&R, FieldId F) {
switch (F) {
case FieldId::F_namespace:
I->Namespace.emplace_back(std::move(R));
break;
case FieldId::F_parent:
I->Parents.emplace_back(std::move(R));
break;
case FieldId::F_vparent:
I->VirtualParents.emplace_back(std::move(R));
break;
default:
llvm::errs() << "Invalid field type for info.\n";
exit(1);
}
}
// Read records from bitcode into a given info.
template <typename T> bool ClangDocBitcodeReader::readRecord(unsigned ID, T I) {
Record R;
llvm::StringRef Blob;
unsigned RecID = Stream.readRecord(ID, R, &Blob);
return parseRecord(R, RecID, Blob, I);
}
template <> bool ClangDocBitcodeReader::readRecord(unsigned ID, Reference *I) {
Record R;
llvm::StringRef Blob;
unsigned RecID = Stream.readRecord(ID, R, &Blob);
return parseRecord(R, RecID, Blob, I, CurrentReferenceField);
}
// Read a block of records into a single info.
template <typename T> bool ClangDocBitcodeReader::readBlock(unsigned ID, T I) {
if (Stream.EnterSubBlock(ID))
return false;
while (true) {
unsigned BlockOrCode = 0;
Cursor Res = skipUntilRecordOrBlock(BlockOrCode);
switch (Res) {
case Cursor::BadBlock:
return false;
case Cursor::BlockEnd:
return true;
case Cursor::BlockBegin:
if (readSubBlock(BlockOrCode, I))
continue;
if (!Stream.SkipBlock())
return false;
continue;
case Cursor::Record:
break;
}
if (!readRecord(BlockOrCode, I))
return false;
}
}
template <typename T>
bool ClangDocBitcodeReader::readSubBlock(unsigned ID, T I) {
switch (ID) {
// Blocks can only have Comment, Reference, or TypeInfo subblocks
case BI_COMMENT_BLOCK_ID:
if (readBlock(ID, getCommentInfo(I)))
return true;
return false;
case BI_TYPE_BLOCK_ID: {
TypeInfo TI;
if (readBlock(ID, &TI)) {
addTypeInfo(I, std::move(TI));
return true;
}
return false;
}
case BI_FIELD_TYPE_BLOCK_ID: {
FieldTypeInfo TI;
if (readBlock(ID, &TI)) {
addTypeInfo(I, std::move(TI));
return true;
}
return false;
}
case BI_MEMBER_TYPE_BLOCK_ID: {
MemberTypeInfo TI;
if (readBlock(ID, &TI)) {
addTypeInfo(I, std::move(TI));
return true;
}
return false;
}
case BI_REFERENCE_BLOCK_ID: {
Reference R;
if (readBlock(ID, &R)) {
addReference(I, std::move(R), CurrentReferenceField);
return true;
}
return false;
}
default:
llvm::errs() << "Invalid subblock type.\n";
return false;
}
}
ClangDocBitcodeReader::Cursor
ClangDocBitcodeReader::skipUntilRecordOrBlock(unsigned &BlockOrRecordID) {
BlockOrRecordID = 0;
while (!Stream.AtEndOfStream()) {
unsigned Code = Stream.ReadCode();
switch ((llvm::bitc::FixedAbbrevIDs)Code) {
case llvm::bitc::ENTER_SUBBLOCK:
BlockOrRecordID = Stream.ReadSubBlockID();
return Cursor::BlockBegin;
case llvm::bitc::END_BLOCK:
if (Stream.ReadBlockEnd())
return Cursor::BadBlock;
return Cursor::BlockEnd;
case llvm::bitc::DEFINE_ABBREV:
Stream.ReadAbbrevRecord();
continue;
case llvm::bitc::UNABBREV_RECORD:
return Cursor::BadBlock;
default:
BlockOrRecordID = Code;
return Cursor::Record;
}
}
llvm_unreachable("Premature stream end.");
}
bool ClangDocBitcodeReader::validateStream() {
if (Stream.AtEndOfStream())
return false;
// Sniff for the signature.
if (Stream.Read(8) != BitCodeConstants::Signature[0] ||
Stream.Read(8) != BitCodeConstants::Signature[1] ||
Stream.Read(8) != BitCodeConstants::Signature[2] ||
Stream.Read(8) != BitCodeConstants::Signature[3])
return false;
return true;
}
bool ClangDocBitcodeReader::readBlockInfoBlock() {
BlockInfo = Stream.ReadBlockInfoBlock();
if (!BlockInfo)
return false;
Stream.setBlockInfo(&*BlockInfo);
return true;
}
template <typename T>
std::unique_ptr<Info> ClangDocBitcodeReader::createInfo(unsigned ID) {
std::unique_ptr<Info> I = llvm::make_unique<T>();
if (readBlock(ID, static_cast<T *>(I.get())))
return I;
llvm::errs() << "Error reading from block.\n";
return nullptr;
}
std::unique_ptr<Info> ClangDocBitcodeReader::readBlockToInfo(unsigned ID) {
switch (ID) {
case BI_NAMESPACE_BLOCK_ID:
return createInfo<NamespaceInfo>(ID);
case BI_RECORD_BLOCK_ID:
return createInfo<RecordInfo>(ID);
case BI_ENUM_BLOCK_ID:
return createInfo<EnumInfo>(ID);
case BI_FUNCTION_BLOCK_ID:
return createInfo<FunctionInfo>(ID);
default:
llvm::errs() << "Error reading from block.\n";
return nullptr;
}
}
// Entry point
std::vector<std::unique_ptr<Info>> ClangDocBitcodeReader::readBitcode() {
std::vector<std::unique_ptr<Info>> Infos;
if (!validateStream())
return Infos;
// Read the top level blocks.
while (!Stream.AtEndOfStream()) {
unsigned Code = Stream.ReadCode();
if (Code != llvm::bitc::ENTER_SUBBLOCK)
return Infos;
unsigned ID = Stream.ReadSubBlockID();
switch (ID) {
// NamedType and Comment blocks should not appear at the top level
case BI_TYPE_BLOCK_ID:
case BI_FIELD_TYPE_BLOCK_ID:
case BI_MEMBER_TYPE_BLOCK_ID:
case BI_COMMENT_BLOCK_ID:
case BI_REFERENCE_BLOCK_ID:
llvm::errs() << "Invalid top level block.\n";
return Infos;
case BI_NAMESPACE_BLOCK_ID:
case BI_RECORD_BLOCK_ID:
case BI_ENUM_BLOCK_ID:
case BI_FUNCTION_BLOCK_ID:
if (std::unique_ptr<Info> I = readBlockToInfo(ID)) {
Infos.emplace_back(std::move(I));
}
return Infos;
case BI_VERSION_BLOCK_ID:
if (readBlock(ID, VersionNumber))
continue;
return Infos;
case llvm::bitc::BLOCKINFO_BLOCK_ID:
if (readBlockInfoBlock())
continue;
return Infos;
default:
if (!Stream.SkipBlock())
continue;
}
}
return Infos;
}
} // namespace doc
} // namespace clang

View File

@@ -1,73 +0,0 @@
//===-- BitcodeReader.h - ClangDoc Bitcode Reader --------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file implements a reader for parsing the clang-doc internal
// representation from LLVM bitcode. The reader takes in a stream of bits and
// generates the set of infos that it represents.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_DOC_BITCODEREADER_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_DOC_BITCODEREADER_H
#include "BitcodeWriter.h"
#include "Representation.h"
#include "clang/AST/AST.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/Bitcode/BitstreamReader.h"
namespace clang {
namespace doc {
// Class to read bitstream into an InfoSet collection
class ClangDocBitcodeReader {
public:
ClangDocBitcodeReader(llvm::BitstreamCursor &Stream) : Stream(Stream) {}
// Main entry point, calls readBlock to read each block in the given stream.
std::vector<std::unique_ptr<Info>> readBitcode();
private:
enum class Cursor { BadBlock = 1, Record, BlockEnd, BlockBegin };
// Top level parsing
bool validateStream();
bool readVersion();
bool readBlockInfoBlock();
// Read a block of records into a single Info struct, calls readRecord on each
// record found.
template <typename T> bool readBlock(unsigned ID, T I);
// Step through a block of records to find the next data field.
template <typename T> bool readSubBlock(unsigned ID, T I);
// Read record data into the given Info data field, calling the appropriate
// parseRecord functions to parse and store the data.
template <typename T> bool readRecord(unsigned ID, T I);
// Allocate the relevant type of info and add read data to the object.
template <typename T> std::unique_ptr<Info> createInfo(unsigned ID);
// Helper function to step through blocks to find and dispatch the next record
// or block to be read.
Cursor skipUntilRecordOrBlock(unsigned &BlockOrRecordID);
// Helper function to set up the approriate type of Info.
std::unique_ptr<Info> readBlockToInfo(unsigned ID);
llvm::BitstreamCursor &Stream;
Optional<llvm::BitstreamBlockInfo> BlockInfo;
FieldId CurrentReferenceField;
};
} // namespace doc
} // namespace clang
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_DOC_BITCODEREADER_H

View File

@@ -1,518 +0,0 @@
//===-- BitcodeWriter.cpp - ClangDoc Bitcode Writer ------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "BitcodeWriter.h"
#include "llvm/ADT/IndexedMap.h"
#include <initializer_list>
namespace clang {
namespace doc {
// Empty SymbolID for comparison, so we don't have to construct one every time.
static const SymbolID EmptySID = SymbolID();
// Since id enums are not zero-indexed, we need to transform the given id into
// its associated index.
struct BlockIdToIndexFunctor {
using argument_type = unsigned;
unsigned operator()(unsigned ID) const { return ID - BI_FIRST; }
};
struct RecordIdToIndexFunctor {
using argument_type = unsigned;
unsigned operator()(unsigned ID) const { return ID - RI_FIRST; }
};
using AbbrevDsc = void (*)(std::shared_ptr<llvm::BitCodeAbbrev> &Abbrev);
static void AbbrevGen(std::shared_ptr<llvm::BitCodeAbbrev> &Abbrev,
const std::initializer_list<llvm::BitCodeAbbrevOp> Ops) {
for (const auto &Op : Ops)
Abbrev->Add(Op);
}
static void BoolAbbrev(std::shared_ptr<llvm::BitCodeAbbrev> &Abbrev) {
AbbrevGen(Abbrev,
{// 0. Boolean
llvm::BitCodeAbbrevOp(llvm::BitCodeAbbrevOp::Fixed,
BitCodeConstants::BoolSize)});
}
static void IntAbbrev(std::shared_ptr<llvm::BitCodeAbbrev> &Abbrev) {
AbbrevGen(Abbrev,
{// 0. Fixed-size integer
llvm::BitCodeAbbrevOp(llvm::BitCodeAbbrevOp::Fixed,
BitCodeConstants::IntSize)});
}
static void SymbolIDAbbrev(std::shared_ptr<llvm::BitCodeAbbrev> &Abbrev) {
AbbrevGen(Abbrev,
{// 0. Fixed-size integer (length of the sha1'd USR)
llvm::BitCodeAbbrevOp(llvm::BitCodeAbbrevOp::Fixed,
BitCodeConstants::USRLengthSize),
// 1. Fixed-size array of Char6 (USR)
llvm::BitCodeAbbrevOp(llvm::BitCodeAbbrevOp::Array),
llvm::BitCodeAbbrevOp(llvm::BitCodeAbbrevOp::Fixed,
BitCodeConstants::USRBitLengthSize)});
}
static void StringAbbrev(std::shared_ptr<llvm::BitCodeAbbrev> &Abbrev) {
AbbrevGen(Abbrev,
{// 0. Fixed-size integer (length of the following string)
llvm::BitCodeAbbrevOp(llvm::BitCodeAbbrevOp::Fixed,
BitCodeConstants::StringLengthSize),
// 1. The string blob
llvm::BitCodeAbbrevOp(llvm::BitCodeAbbrevOp::Blob)});
}
// Assumes that the file will not have more than 65535 lines.
static void LocationAbbrev(std::shared_ptr<llvm::BitCodeAbbrev> &Abbrev) {
AbbrevGen(
Abbrev,
{// 0. Fixed-size integer (line number)
llvm::BitCodeAbbrevOp(llvm::BitCodeAbbrevOp::Fixed,
BitCodeConstants::LineNumberSize),
// 1. Fixed-size integer (length of the following string (filename))
llvm::BitCodeAbbrevOp(llvm::BitCodeAbbrevOp::Fixed,
BitCodeConstants::StringLengthSize),
// 2. The string blob
llvm::BitCodeAbbrevOp(llvm::BitCodeAbbrevOp::Blob)});
}
struct RecordIdDsc {
llvm::StringRef Name;
AbbrevDsc Abbrev = nullptr;
RecordIdDsc() = default;
RecordIdDsc(llvm::StringRef Name, AbbrevDsc Abbrev)
: Name(Name), Abbrev(Abbrev) {}
// Is this 'description' valid?
operator bool() const {
return Abbrev != nullptr && Name.data() != nullptr && !Name.empty();
}
};
static const llvm::IndexedMap<llvm::StringRef, BlockIdToIndexFunctor>
BlockIdNameMap = []() {
llvm::IndexedMap<llvm::StringRef, BlockIdToIndexFunctor> BlockIdNameMap;
BlockIdNameMap.resize(BlockIdCount);
// There is no init-list constructor for the IndexedMap, so have to
// improvise
static const std::vector<std::pair<BlockId, const char *const>> Inits = {
{BI_VERSION_BLOCK_ID, "VersionBlock"},
{BI_NAMESPACE_BLOCK_ID, "NamespaceBlock"},
{BI_ENUM_BLOCK_ID, "EnumBlock"},
{BI_TYPE_BLOCK_ID, "TypeBlock"},
{BI_FIELD_TYPE_BLOCK_ID, "FieldTypeBlock"},
{BI_MEMBER_TYPE_BLOCK_ID, "MemberTypeBlock"},
{BI_RECORD_BLOCK_ID, "RecordBlock"},
{BI_FUNCTION_BLOCK_ID, "FunctionBlock"},
{BI_COMMENT_BLOCK_ID, "CommentBlock"},
{BI_REFERENCE_BLOCK_ID, "ReferenceBlock"}};
assert(Inits.size() == BlockIdCount);
for (const auto &Init : Inits)
BlockIdNameMap[Init.first] = Init.second;
assert(BlockIdNameMap.size() == BlockIdCount);
return BlockIdNameMap;
}();
static const llvm::IndexedMap<RecordIdDsc, RecordIdToIndexFunctor>
RecordIdNameMap = []() {
llvm::IndexedMap<RecordIdDsc, RecordIdToIndexFunctor> RecordIdNameMap;
RecordIdNameMap.resize(RecordIdCount);
// There is no init-list constructor for the IndexedMap, so have to
// improvise
static const std::vector<std::pair<RecordId, RecordIdDsc>> Inits = {
{VERSION, {"Version", &IntAbbrev}},
{COMMENT_KIND, {"Kind", &StringAbbrev}},
{COMMENT_TEXT, {"Text", &StringAbbrev}},
{COMMENT_NAME, {"Name", &StringAbbrev}},
{COMMENT_DIRECTION, {"Direction", &StringAbbrev}},
{COMMENT_PARAMNAME, {"ParamName", &StringAbbrev}},
{COMMENT_CLOSENAME, {"CloseName", &StringAbbrev}},
{COMMENT_SELFCLOSING, {"SelfClosing", &BoolAbbrev}},
{COMMENT_EXPLICIT, {"Explicit", &BoolAbbrev}},
{COMMENT_ATTRKEY, {"AttrKey", &StringAbbrev}},
{COMMENT_ATTRVAL, {"AttrVal", &StringAbbrev}},
{COMMENT_ARG, {"Arg", &StringAbbrev}},
{FIELD_TYPE_NAME, {"Name", &StringAbbrev}},
{MEMBER_TYPE_NAME, {"Name", &StringAbbrev}},
{MEMBER_TYPE_ACCESS, {"Access", &IntAbbrev}},
{NAMESPACE_USR, {"USR", &SymbolIDAbbrev}},
{NAMESPACE_NAME, {"Name", &StringAbbrev}},
{ENUM_USR, {"USR", &SymbolIDAbbrev}},
{ENUM_NAME, {"Name", &StringAbbrev}},
{ENUM_DEFLOCATION, {"DefLocation", &LocationAbbrev}},
{ENUM_LOCATION, {"Location", &LocationAbbrev}},
{ENUM_MEMBER, {"Member", &StringAbbrev}},
{ENUM_SCOPED, {"Scoped", &BoolAbbrev}},
{RECORD_USR, {"USR", &SymbolIDAbbrev}},
{RECORD_NAME, {"Name", &StringAbbrev}},
{RECORD_DEFLOCATION, {"DefLocation", &LocationAbbrev}},
{RECORD_LOCATION, {"Location", &LocationAbbrev}},
{RECORD_TAG_TYPE, {"TagType", &IntAbbrev}},
{FUNCTION_USR, {"USR", &SymbolIDAbbrev}},
{FUNCTION_NAME, {"Name", &StringAbbrev}},
{FUNCTION_DEFLOCATION, {"DefLocation", &LocationAbbrev}},
{FUNCTION_LOCATION, {"Location", &LocationAbbrev}},
{FUNCTION_ACCESS, {"Access", &IntAbbrev}},
{FUNCTION_IS_METHOD, {"IsMethod", &BoolAbbrev}},
{REFERENCE_USR, {"USR", &SymbolIDAbbrev}},
{REFERENCE_NAME, {"Name", &StringAbbrev}},
{REFERENCE_TYPE, {"RefType", &IntAbbrev}},
{REFERENCE_FIELD, {"Field", &IntAbbrev}}};
assert(Inits.size() == RecordIdCount);
for (const auto &Init : Inits) {
RecordIdNameMap[Init.first] = Init.second;
assert((Init.second.Name.size() + 1) <= BitCodeConstants::RecordSize);
}
assert(RecordIdNameMap.size() == RecordIdCount);
return RecordIdNameMap;
}();
static const std::vector<std::pair<BlockId, std::vector<RecordId>>>
RecordsByBlock{
// Version Block
{BI_VERSION_BLOCK_ID, {VERSION}},
// Comment Block
{BI_COMMENT_BLOCK_ID,
{COMMENT_KIND, COMMENT_TEXT, COMMENT_NAME, COMMENT_DIRECTION,
COMMENT_PARAMNAME, COMMENT_CLOSENAME, COMMENT_SELFCLOSING,
COMMENT_EXPLICIT, COMMENT_ATTRKEY, COMMENT_ATTRVAL, COMMENT_ARG}},
// Type Block
{BI_TYPE_BLOCK_ID, {}},
// FieldType Block
{BI_FIELD_TYPE_BLOCK_ID, {FIELD_TYPE_NAME}},
// MemberType Block
{BI_MEMBER_TYPE_BLOCK_ID, {MEMBER_TYPE_NAME, MEMBER_TYPE_ACCESS}},
// Enum Block
{BI_ENUM_BLOCK_ID,
{ENUM_USR, ENUM_NAME, ENUM_DEFLOCATION, ENUM_LOCATION, ENUM_MEMBER,
ENUM_SCOPED}},
// Namespace Block
{BI_NAMESPACE_BLOCK_ID, {NAMESPACE_USR, NAMESPACE_NAME}},
// Record Block
{BI_RECORD_BLOCK_ID,
{RECORD_USR, RECORD_NAME, RECORD_DEFLOCATION, RECORD_LOCATION,
RECORD_TAG_TYPE}},
// Function Block
{BI_FUNCTION_BLOCK_ID,
{FUNCTION_USR, FUNCTION_NAME, FUNCTION_DEFLOCATION, FUNCTION_LOCATION,
FUNCTION_ACCESS, FUNCTION_IS_METHOD}},
// Reference Block
{BI_REFERENCE_BLOCK_ID,
{REFERENCE_USR, REFERENCE_NAME, REFERENCE_TYPE, REFERENCE_FIELD}}};
// AbbreviationMap
constexpr char BitCodeConstants::Signature[];
void ClangDocBitcodeWriter::AbbreviationMap::add(RecordId RID,
unsigned AbbrevID) {
assert(RecordIdNameMap[RID] && "Unknown RecordId.");
assert(Abbrevs.find(RID) == Abbrevs.end() && "Abbreviation already added.");
Abbrevs[RID] = AbbrevID;
}
unsigned ClangDocBitcodeWriter::AbbreviationMap::get(RecordId RID) const {
assert(RecordIdNameMap[RID] && "Unknown RecordId.");
assert(Abbrevs.find(RID) != Abbrevs.end() && "Unknown abbreviation.");
return Abbrevs.lookup(RID);
}
// Validation and Overview Blocks
/// \brief Emits the magic number header to check that its the right format,
/// in this case, 'DOCS'.
void ClangDocBitcodeWriter::emitHeader() {
for (char C : BitCodeConstants::Signature)
Stream.Emit((unsigned)C, BitCodeConstants::SignatureBitSize);
}
void ClangDocBitcodeWriter::emitVersionBlock() {
StreamSubBlockGuard Block(Stream, BI_VERSION_BLOCK_ID);
emitRecord(VersionNumber, VERSION);
}
/// \brief Emits a block ID and the block name to the BLOCKINFO block.
void ClangDocBitcodeWriter::emitBlockID(BlockId BID) {
const auto &BlockIdName = BlockIdNameMap[BID];
assert(BlockIdName.data() && BlockIdName.size() && "Unknown BlockId.");
Record.clear();
Record.push_back(BID);
Stream.EmitRecord(llvm::bitc::BLOCKINFO_CODE_SETBID, Record);
Stream.EmitRecord(llvm::bitc::BLOCKINFO_CODE_BLOCKNAME,
ArrayRef<unsigned char>(BlockIdName.bytes_begin(),
BlockIdName.bytes_end()));
}
/// \brief Emits a record name to the BLOCKINFO block.
void ClangDocBitcodeWriter::emitRecordID(RecordId ID) {
assert(RecordIdNameMap[ID] && "Unknown RecordId.");
prepRecordData(ID);
Record.append(RecordIdNameMap[ID].Name.begin(),
RecordIdNameMap[ID].Name.end());
Stream.EmitRecord(llvm::bitc::BLOCKINFO_CODE_SETRECORDNAME, Record);
}
// Abbreviations
void ClangDocBitcodeWriter::emitAbbrev(RecordId ID, BlockId Block) {
assert(RecordIdNameMap[ID] && "Unknown abbreviation.");
auto Abbrev = std::make_shared<llvm::BitCodeAbbrev>();
Abbrev->Add(llvm::BitCodeAbbrevOp(ID));
RecordIdNameMap[ID].Abbrev(Abbrev);
Abbrevs.add(ID, Stream.EmitBlockInfoAbbrev(Block, std::move(Abbrev)));
}
// Records
void ClangDocBitcodeWriter::emitRecord(const SymbolID &Sym, RecordId ID) {
assert(RecordIdNameMap[ID] && "Unknown RecordId.");
assert(RecordIdNameMap[ID].Abbrev == &SymbolIDAbbrev &&
"Abbrev type mismatch.");
if (!prepRecordData(ID, Sym != EmptySID))
return;
assert(Sym.size() == 20);
Record.push_back(Sym.size());
Record.append(Sym.begin(), Sym.end());
Stream.EmitRecordWithAbbrev(Abbrevs.get(ID), Record);
}
void ClangDocBitcodeWriter::emitRecord(llvm::StringRef Str, RecordId ID) {
assert(RecordIdNameMap[ID] && "Unknown RecordId.");
assert(RecordIdNameMap[ID].Abbrev == &StringAbbrev &&
"Abbrev type mismatch.");
if (!prepRecordData(ID, !Str.empty()))
return;
assert(Str.size() < (1U << BitCodeConstants::StringLengthSize));
Record.push_back(Str.size());
Stream.EmitRecordWithBlob(Abbrevs.get(ID), Record, Str);
}
void ClangDocBitcodeWriter::emitRecord(const Location &Loc, RecordId ID) {
assert(RecordIdNameMap[ID] && "Unknown RecordId.");
assert(RecordIdNameMap[ID].Abbrev == &LocationAbbrev &&
"Abbrev type mismatch.");
if (!prepRecordData(ID, true))
return;
// FIXME: Assert that the line number is of the appropriate size.
Record.push_back(Loc.LineNumber);
assert(Loc.Filename.size() < (1U << BitCodeConstants::StringLengthSize));
// Record.push_back(Loc.Filename.size());
// Stream.EmitRecordWithBlob(Abbrevs.get(ID), Record, Loc.Filename);
Record.push_back(4);
Stream.EmitRecordWithBlob(Abbrevs.get(ID), Record, "test");
}
void ClangDocBitcodeWriter::emitRecord(bool Val, RecordId ID) {
assert(RecordIdNameMap[ID] && "Unknown RecordId.");
assert(RecordIdNameMap[ID].Abbrev == &BoolAbbrev && "Abbrev type mismatch.");
if (!prepRecordData(ID, Val))
return;
Record.push_back(Val);
Stream.EmitRecordWithAbbrev(Abbrevs.get(ID), Record);
}
void ClangDocBitcodeWriter::emitRecord(int Val, RecordId ID) {
assert(RecordIdNameMap[ID] && "Unknown RecordId.");
assert(RecordIdNameMap[ID].Abbrev == &IntAbbrev && "Abbrev type mismatch.");
if (!prepRecordData(ID, Val))
return;
// FIXME: Assert that the integer is of the appropriate size.
Record.push_back(Val);
Stream.EmitRecordWithAbbrev(Abbrevs.get(ID), Record);
}
void ClangDocBitcodeWriter::emitRecord(unsigned Val, RecordId ID) {
assert(RecordIdNameMap[ID] && "Unknown RecordId.");
assert(RecordIdNameMap[ID].Abbrev == &IntAbbrev && "Abbrev type mismatch.");
if (!prepRecordData(ID, Val))
return;
assert(Val < (1U << BitCodeConstants::IntSize));
Record.push_back(Val);
Stream.EmitRecordWithAbbrev(Abbrevs.get(ID), Record);
}
bool ClangDocBitcodeWriter::prepRecordData(RecordId ID, bool ShouldEmit) {
assert(RecordIdNameMap[ID] && "Unknown RecordId.");
if (!ShouldEmit)
return false;
Record.clear();
Record.push_back(ID);
return true;
}
// BlockInfo Block
void ClangDocBitcodeWriter::emitBlockInfoBlock() {
Stream.EnterBlockInfoBlock();
for (const auto &Block : RecordsByBlock) {
assert(Block.second.size() < (1U << BitCodeConstants::SubblockIDSize));
emitBlockInfo(Block.first, Block.second);
}
Stream.ExitBlock();
}
void ClangDocBitcodeWriter::emitBlockInfo(BlockId BID,
const std::vector<RecordId> &RIDs) {
assert(RIDs.size() < (1U << BitCodeConstants::SubblockIDSize));
emitBlockID(BID);
for (RecordId RID : RIDs) {
emitRecordID(RID);
emitAbbrev(RID, BID);
}
}
// Block emission
void ClangDocBitcodeWriter::emitBlock(const Reference &R, FieldId Field) {
if (R.USR == EmptySID && R.Name.empty())
return;
StreamSubBlockGuard Block(Stream, BI_REFERENCE_BLOCK_ID);
emitRecord(R.USR, REFERENCE_USR);
emitRecord(R.Name, REFERENCE_NAME);
emitRecord((unsigned)R.RefType, REFERENCE_TYPE);
emitRecord((unsigned)Field, REFERENCE_FIELD);
}
void ClangDocBitcodeWriter::emitBlock(const TypeInfo &T) {
StreamSubBlockGuard Block(Stream, BI_TYPE_BLOCK_ID);
emitBlock(T.Type, FieldId::F_type);
}
void ClangDocBitcodeWriter::emitBlock(const FieldTypeInfo &T) {
StreamSubBlockGuard Block(Stream, BI_FIELD_TYPE_BLOCK_ID);
emitBlock(T.Type, FieldId::F_type);
emitRecord(T.Name, FIELD_TYPE_NAME);
}
void ClangDocBitcodeWriter::emitBlock(const MemberTypeInfo &T) {
StreamSubBlockGuard Block(Stream, BI_MEMBER_TYPE_BLOCK_ID);
emitBlock(T.Type, FieldId::F_type);
emitRecord(T.Name, MEMBER_TYPE_NAME);
emitRecord(T.Access, MEMBER_TYPE_ACCESS);
}
void ClangDocBitcodeWriter::emitBlock(const CommentInfo &I) {
StreamSubBlockGuard Block(Stream, BI_COMMENT_BLOCK_ID);
for (const auto &L : std::vector<std::pair<llvm::StringRef, RecordId>>{
{I.Kind, COMMENT_KIND},
{I.Text, COMMENT_TEXT},
{I.Name, COMMENT_NAME},
{I.Direction, COMMENT_DIRECTION},
{I.ParamName, COMMENT_PARAMNAME},
{I.CloseName, COMMENT_CLOSENAME}})
emitRecord(L.first, L.second);
emitRecord(I.SelfClosing, COMMENT_SELFCLOSING);
emitRecord(I.Explicit, COMMENT_EXPLICIT);
for (const auto &A : I.AttrKeys)
emitRecord(A, COMMENT_ATTRKEY);
for (const auto &A : I.AttrValues)
emitRecord(A, COMMENT_ATTRVAL);
for (const auto &A : I.Args)
emitRecord(A, COMMENT_ARG);
for (const auto &C : I.Children)
emitBlock(*C);
}
void ClangDocBitcodeWriter::emitBlock(const NamespaceInfo &I) {
StreamSubBlockGuard Block(Stream, BI_NAMESPACE_BLOCK_ID);
emitRecord(I.USR, NAMESPACE_USR);
emitRecord(I.Name, NAMESPACE_NAME);
for (const auto &N : I.Namespace)
emitBlock(N, FieldId::F_namespace);
for (const auto &CI : I.Description)
emitBlock(CI);
}
void ClangDocBitcodeWriter::emitBlock(const EnumInfo &I) {
StreamSubBlockGuard Block(Stream, BI_ENUM_BLOCK_ID);
emitRecord(I.USR, ENUM_USR);
emitRecord(I.Name, ENUM_NAME);
for (const auto &N : I.Namespace)
emitBlock(N, FieldId::F_namespace);
for (const auto &CI : I.Description)
emitBlock(CI);
if (I.DefLoc)
emitRecord(I.DefLoc.getValue(), ENUM_DEFLOCATION);
for (const auto &L : I.Loc)
emitRecord(L, ENUM_LOCATION);
emitRecord(I.Scoped, ENUM_SCOPED);
for (const auto &N : I.Members)
emitRecord(N, ENUM_MEMBER);
}
void ClangDocBitcodeWriter::emitBlock(const RecordInfo &I) {
StreamSubBlockGuard Block(Stream, BI_RECORD_BLOCK_ID);
emitRecord(I.USR, RECORD_USR);
emitRecord(I.Name, RECORD_NAME);
for (const auto &N : I.Namespace)
emitBlock(N, FieldId::F_namespace);
for (const auto &CI : I.Description)
emitBlock(CI);
if (I.DefLoc)
emitRecord(I.DefLoc.getValue(), RECORD_DEFLOCATION);
for (const auto &L : I.Loc)
emitRecord(L, RECORD_LOCATION);
emitRecord(I.TagType, RECORD_TAG_TYPE);
for (const auto &N : I.Members)
emitBlock(N);
for (const auto &P : I.Parents)
emitBlock(P, FieldId::F_parent);
for (const auto &P : I.VirtualParents)
emitBlock(P, FieldId::F_vparent);
}
void ClangDocBitcodeWriter::emitBlock(const FunctionInfo &I) {
StreamSubBlockGuard Block(Stream, BI_FUNCTION_BLOCK_ID);
emitRecord(I.USR, FUNCTION_USR);
emitRecord(I.Name, FUNCTION_NAME);
for (const auto &N : I.Namespace)
emitBlock(N, FieldId::F_namespace);
for (const auto &CI : I.Description)
emitBlock(CI);
emitRecord(I.IsMethod, FUNCTION_IS_METHOD);
if (I.DefLoc)
emitRecord(I.DefLoc.getValue(), FUNCTION_DEFLOCATION);
for (const auto &L : I.Loc)
emitRecord(L, FUNCTION_LOCATION);
emitBlock(I.Parent, FieldId::F_parent);
emitBlock(I.ReturnType);
for (const auto &N : I.Params)
emitBlock(N);
}
bool ClangDocBitcodeWriter::dispatchInfoForWrite(Info *I) {
switch (I->IT) {
case InfoType::IT_namespace:
emitBlock(*static_cast<clang::doc::NamespaceInfo *>(I));
break;
case InfoType::IT_record:
emitBlock(*static_cast<clang::doc::RecordInfo *>(I));
break;
case InfoType::IT_enum:
emitBlock(*static_cast<clang::doc::EnumInfo *>(I));
break;
case InfoType::IT_function:
emitBlock(*static_cast<clang::doc::FunctionInfo *>(I));
break;
default:
llvm::errs() << "Unexpected info, unable to write.\n";
return true;
}
return false;
}
} // namespace doc
} // namespace clang

View File

@@ -1,201 +0,0 @@
//===-- BitcodeWriter.h - ClangDoc Bitcode Writer --------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file implements a writer for serializing the clang-doc internal
// representation to LLVM bitcode. The writer takes in a stream and emits the
// generated bitcode to that stream.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_DOC_BITCODEWRITER_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_DOC_BITCODEWRITER_H
#include "Representation.h"
#include "clang/AST/AST.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Bitcode/BitstreamWriter.h"
#include <initializer_list>
#include <vector>
namespace clang {
namespace doc {
// Current version number of clang-doc bitcode.
// Should be bumped when removing or changing BlockIds, RecordIds, or
// BitCodeConstants, though they can be added without breaking it.
static const unsigned VersionNumber = 2;
struct BitCodeConstants {
static constexpr unsigned RecordSize = 32U;
static constexpr unsigned SignatureBitSize = 8U;
static constexpr unsigned SubblockIDSize = 4U;
static constexpr unsigned BoolSize = 1U;
static constexpr unsigned IntSize = 16U;
static constexpr unsigned StringLengthSize = 16U;
static constexpr unsigned FilenameLengthSize = 16U;
static constexpr unsigned LineNumberSize = 16U;
static constexpr unsigned ReferenceTypeSize = 8U;
static constexpr unsigned USRLengthSize = 6U;
static constexpr unsigned USRBitLengthSize = 8U;
static constexpr char Signature[4] = {'D', 'O', 'C', 'S'};
static constexpr int USRHashSize = 20;
};
// New Ids need to be added to both the enum here and the relevant IdNameMap in
// the implementation file.
enum BlockId {
BI_VERSION_BLOCK_ID = llvm::bitc::FIRST_APPLICATION_BLOCKID,
BI_NAMESPACE_BLOCK_ID,
BI_ENUM_BLOCK_ID,
BI_TYPE_BLOCK_ID,
BI_FIELD_TYPE_BLOCK_ID,
BI_MEMBER_TYPE_BLOCK_ID,
BI_RECORD_BLOCK_ID,
BI_FUNCTION_BLOCK_ID,
BI_COMMENT_BLOCK_ID,
BI_REFERENCE_BLOCK_ID,
BI_LAST,
BI_FIRST = BI_VERSION_BLOCK_ID
};
// New Ids need to be added to the enum here, and to the relevant IdNameMap and
// initialization list in the implementation file.
#define INFORECORDS(X) X##_USR, X##_NAME
enum RecordId {
VERSION = 1,
INFORECORDS(FUNCTION),
FUNCTION_DEFLOCATION,
FUNCTION_LOCATION,
FUNCTION_ACCESS,
FUNCTION_IS_METHOD,
COMMENT_KIND,
COMMENT_TEXT,
COMMENT_NAME,
COMMENT_DIRECTION,
COMMENT_PARAMNAME,
COMMENT_CLOSENAME,
COMMENT_SELFCLOSING,
COMMENT_EXPLICIT,
COMMENT_ATTRKEY,
COMMENT_ATTRVAL,
COMMENT_ARG,
FIELD_TYPE_NAME,
MEMBER_TYPE_NAME,
MEMBER_TYPE_ACCESS,
INFORECORDS(NAMESPACE),
INFORECORDS(ENUM),
ENUM_DEFLOCATION,
ENUM_LOCATION,
ENUM_MEMBER,
ENUM_SCOPED,
INFORECORDS(RECORD),
RECORD_DEFLOCATION,
RECORD_LOCATION,
RECORD_TAG_TYPE,
REFERENCE_USR,
REFERENCE_NAME,
REFERENCE_TYPE,
REFERENCE_FIELD,
RI_LAST,
RI_FIRST = VERSION
};
static constexpr unsigned BlockIdCount = BI_LAST - BI_FIRST;
static constexpr unsigned RecordIdCount = RI_LAST - RI_FIRST;
#undef INFORECORDS
// Identifiers for differentiating between subblocks
enum class FieldId { F_default, F_namespace, F_parent, F_vparent, F_type };
class ClangDocBitcodeWriter {
public:
ClangDocBitcodeWriter(llvm::BitstreamWriter &Stream) : Stream(Stream) {
emitHeader();
emitBlockInfoBlock();
emitVersionBlock();
}
// Write a specific info to a bitcode stream.
bool dispatchInfoForWrite(Info *I);
// Block emission of different info types.
void emitBlock(const NamespaceInfo &I);
void emitBlock(const RecordInfo &I);
void emitBlock(const FunctionInfo &I);
void emitBlock(const EnumInfo &I);
void emitBlock(const TypeInfo &B);
void emitBlock(const FieldTypeInfo &B);
void emitBlock(const MemberTypeInfo &B);
void emitBlock(const CommentInfo &B);
void emitBlock(const Reference &B, FieldId F);
private:
class AbbreviationMap {
llvm::DenseMap<unsigned, unsigned> Abbrevs;
public:
AbbreviationMap() : Abbrevs(RecordIdCount) {}
void add(RecordId RID, unsigned AbbrevID);
unsigned get(RecordId RID) const;
};
class StreamSubBlockGuard {
llvm::BitstreamWriter &Stream;
public:
StreamSubBlockGuard(llvm::BitstreamWriter &Stream_, BlockId ID)
: Stream(Stream_) {
// NOTE: SubBlockIDSize could theoretically be calculated on the fly,
// based on the initialization list of records in each block.
Stream.EnterSubblock(ID, BitCodeConstants::SubblockIDSize);
}
StreamSubBlockGuard(const StreamSubBlockGuard &) = delete;
StreamSubBlockGuard &operator=(const StreamSubBlockGuard &) = delete;
~StreamSubBlockGuard() { Stream.ExitBlock(); }
};
// Emission of validation and overview blocks.
void emitHeader();
void emitVersionBlock();
void emitRecordID(RecordId ID);
void emitBlockID(BlockId ID);
void emitBlockInfoBlock();
void emitBlockInfo(BlockId BID, const std::vector<RecordId> &RIDs);
// Emission of individual record types.
void emitRecord(StringRef Str, RecordId ID);
void emitRecord(const SymbolID &Str, RecordId ID);
void emitRecord(const Location &Loc, RecordId ID);
void emitRecord(const Reference &Ref, RecordId ID);
void emitRecord(bool Value, RecordId ID);
void emitRecord(int Value, RecordId ID);
void emitRecord(unsigned Value, RecordId ID);
bool prepRecordData(RecordId ID, bool ShouldEmit = true);
// Emission of appropriate abbreviation type.
void emitAbbrev(RecordId ID, BlockId Block);
// Static size is the maximum length of the block/record names we're pushing
// to this + 1. Longest is currently `MemberTypeBlock` at 15 chars.
SmallVector<uint32_t, BitCodeConstants::RecordSize> Record;
llvm::BitstreamWriter &Stream;
AbbreviationMap Abbrevs;
};
} // namespace doc
} // namespace clang
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_DOC_BITCODEWRITER_H

View File

@@ -1,29 +0,0 @@
set(LLVM_LINK_COMPONENTS
support
BitReader
BitWriter
)
add_clang_library(clangDoc
BitcodeReader.cpp
BitcodeWriter.cpp
ClangDoc.cpp
Generators.cpp
Mapper.cpp
Representation.cpp
Serialize.cpp
YAMLGenerator.cpp
LINK_LIBS
clangAnalysis
clangAST
clangASTMatchers
clangBasic
clangFrontend
clangIndex
clangLex
clangTooling
clangToolingCore
)
add_subdirectory(tool)

View File

@@ -1,62 +0,0 @@
//===-- ClangDoc.cpp - ClangDoc ---------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file implements the main entry point for the clang-doc tool. It runs
// the clang-doc mapper on a given set of source code files using a
// FrontendActionFactory.
//
//===----------------------------------------------------------------------===//
#include "ClangDoc.h"
#include "Mapper.h"
#include "Representation.h"
#include "clang/AST/AST.h"
#include "clang/AST/ASTConsumer.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/RecursiveASTVisitor.h"
#include "clang/Frontend/ASTConsumers.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Frontend/FrontendActions.h"
namespace clang {
namespace doc {
class MapperActionFactory : public tooling::FrontendActionFactory {
public:
MapperActionFactory(ClangDocContext CDCtx) : CDCtx(CDCtx) {}
clang::FrontendAction *create() override;
private:
ClangDocContext CDCtx;
};
clang::FrontendAction *MapperActionFactory::create() {
class ClangDocAction : public clang::ASTFrontendAction {
public:
ClangDocAction(ClangDocContext CDCtx) : CDCtx(CDCtx) {}
std::unique_ptr<clang::ASTConsumer>
CreateASTConsumer(clang::CompilerInstance &Compiler,
llvm::StringRef InFile) override {
return llvm::make_unique<MapASTVisitor>(&Compiler.getASTContext(), CDCtx);
}
private:
ClangDocContext CDCtx;
};
return new ClangDocAction(CDCtx);
}
std::unique_ptr<tooling::FrontendActionFactory>
newMapperActionFactory(ClangDocContext CDCtx) {
return llvm::make_unique<MapperActionFactory>(CDCtx);
}
} // namespace doc
} // namespace clang

View File

@@ -1,34 +0,0 @@
//===-- ClangDoc.h - ClangDoc -----------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file exposes a method to craete the FrontendActionFactory for the
// clang-doc tool. The factory runs the clang-doc mapper on a given set of
// source code files, storing the results key-value pairs in its
// ExecutionContext.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_DOC_CLANGDOC_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_DOC_CLANGDOC_H
#include "Representation.h"
#include "clang/Tooling/Execution.h"
#include "clang/Tooling/StandaloneExecution.h"
#include "clang/Tooling/Tooling.h"
namespace clang {
namespace doc {
std::unique_ptr<tooling::FrontendActionFactory>
newMapperActionFactory(ClangDocContext CDCtx);
} // namespace doc
} // namespace clang
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_DOC_CLANGDOC_H

View File

@@ -1,36 +0,0 @@
//===---- Generator.cpp - Generator Registry ---------------------*- C++-*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "Generators.h"
LLVM_INSTANTIATE_REGISTRY(clang::doc::GeneratorRegistry)
namespace clang {
namespace doc {
llvm::Expected<std::unique_ptr<Generator>>
findGeneratorByName(llvm::StringRef Format) {
for (auto I = GeneratorRegistry::begin(), E = GeneratorRegistry::end();
I != E; ++I) {
if (I->getName() != Format)
continue;
return I->instantiate();
}
return llvm::make_error<llvm::StringError>("Can't find generator: " + Format,
llvm::inconvertibleErrorCode());
}
// This anchor is used to force the linker to link in the generated object file
// and thus register the generators.
extern volatile int YAMLGeneratorAnchorSource;
static int LLVM_ATTRIBUTE_UNUSED YAMLGeneratorAnchorDest =
YAMLGeneratorAnchorSource;
} // namespace doc
} // namespace clang

View File

@@ -1,41 +0,0 @@
//===-- Generators.h - ClangDoc Generator ----------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
// Generator classes for converting declaration information into documentation
// in a specified format.
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_DOC_GENERATOR_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_DOC_GENERATOR_H
#include "Representation.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/Registry.h"
namespace clang {
namespace doc {
// Abstract base class for generators.
// This is expected to be implemented and exposed via the GeneratorRegistry.
class Generator {
public:
virtual ~Generator() = default;
// Write out the decl info in the specified format.
virtual bool generateDocForInfo(Info *I, llvm::raw_ostream &OS) = 0;
};
typedef llvm::Registry<Generator> GeneratorRegistry;
llvm::Expected<std::unique_ptr<Generator>>
findGeneratorByName(llvm::StringRef Format);
} // namespace doc
} // namespace clang
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_DOC_GENERATOR_H

View File

@@ -1,90 +0,0 @@
//===-- Mapper.cpp - ClangDoc Mapper ----------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "Mapper.h"
#include "BitcodeWriter.h"
#include "Serialize.h"
#include "clang/AST/Comment.h"
#include "clang/Index/USRGeneration.h"
#include "llvm/ADT/StringExtras.h"
using clang::comments::FullComment;
namespace clang {
namespace doc {
void MapASTVisitor::HandleTranslationUnit(ASTContext &Context) {
TraverseDecl(Context.getTranslationUnitDecl());
}
template <typename T> bool MapASTVisitor::mapDecl(const T *D) {
// If we're looking a decl not in user files, skip this decl.
if (D->getASTContext().getSourceManager().isInSystemHeader(D->getLocation()))
return true;
llvm::SmallString<128> USR;
// If there is an error generating a USR for the decl, skip this decl.
if (index::generateUSRForDecl(D, USR))
return true;
std::string info = serialize::emitInfo(
D, getComment(D, D->getASTContext()), getLine(D, D->getASTContext()),
getFile(D, D->getASTContext()), CDCtx.PublicOnly);
if (info != "")
CDCtx.ECtx->reportResult(
llvm::toHex(llvm::toStringRef(serialize::hashUSR(USR))), info);
return true;
}
bool MapASTVisitor::VisitNamespaceDecl(const NamespaceDecl *D) {
return mapDecl(D);
}
bool MapASTVisitor::VisitRecordDecl(const RecordDecl *D) { return mapDecl(D); }
bool MapASTVisitor::VisitEnumDecl(const EnumDecl *D) { return mapDecl(D); }
bool MapASTVisitor::VisitCXXMethodDecl(const CXXMethodDecl *D) {
return mapDecl(D);
}
bool MapASTVisitor::VisitFunctionDecl(const FunctionDecl *D) {
// Don't visit CXXMethodDecls twice
if (dyn_cast<CXXMethodDecl>(D))
return true;
return mapDecl(D);
}
comments::FullComment *
MapASTVisitor::getComment(const NamedDecl *D, const ASTContext &Context) const {
RawComment *Comment = Context.getRawCommentForDeclNoCache(D);
// FIXME: Move setAttached to the initial comment parsing.
if (Comment) {
Comment->setAttached();
return Comment->parse(Context, nullptr, D);
}
return nullptr;
}
int MapASTVisitor::getLine(const NamedDecl *D,
const ASTContext &Context) const {
return Context.getSourceManager().getPresumedLoc(D->getLocStart()).getLine();
}
llvm::StringRef MapASTVisitor::getFile(const NamedDecl *D,
const ASTContext &Context) const {
return Context.getSourceManager()
.getPresumedLoc(D->getLocStart())
.getFilename();
}
} // namespace doc
} // namespace clang

View File

@@ -1,58 +0,0 @@
//===-- Mapper.h - ClangDoc Mapper ------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file implements the Mapper piece of the clang-doc tool. It implements
// a RecursiveASTVisitor to look at each declaration and populate the info
// into the internal representation. Each seen declaration is serialized to
// to bitcode and written out to the ExecutionContext as a KV pair where the
// key is the declaration's USR and the value is the serialized bitcode.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_DOC_MAPPER_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_DOC_MAPPER_H
#include "Representation.h"
#include "clang/AST/RecursiveASTVisitor.h"
#include "clang/Tooling/Execution.h"
using namespace clang::comments;
using namespace clang::tooling;
namespace clang {
namespace doc {
class MapASTVisitor : public clang::RecursiveASTVisitor<MapASTVisitor>,
public ASTConsumer {
public:
explicit MapASTVisitor(ASTContext *Ctx, ClangDocContext CDCtx)
: CDCtx(CDCtx) {}
void HandleTranslationUnit(ASTContext &Context) override;
bool VisitNamespaceDecl(const NamespaceDecl *D);
bool VisitRecordDecl(const RecordDecl *D);
bool VisitEnumDecl(const EnumDecl *D);
bool VisitCXXMethodDecl(const CXXMethodDecl *D);
bool VisitFunctionDecl(const FunctionDecl *D);
private:
template <typename T> bool mapDecl(const T *D);
int getLine(const NamedDecl *D, const ASTContext &Context) const;
StringRef getFile(const NamedDecl *D, const ASTContext &Context) const;
comments::FullComment *getComment(const NamedDecl *D,
const ASTContext &Context) const;
ClangDocContext CDCtx;
};
} // namespace doc
} // namespace clang
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_DOC_MAPPER_H

View File

@@ -1,131 +0,0 @@
///===-- Representation.cpp - ClangDoc Representation -----------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file defines the merging of different types of infos. The data in the
// calling Info is preserved during a merge unless that field is empty or
// default. In that case, the data from the parameter Info is used to replace
// the empty or default data.
//
// For most fields, the first decl seen provides the data. Exceptions to this
// include the location and description fields, which are collections of data on
// all decls related to a given definition. All other fields are ignored in new
// decls unless the first seen decl didn't, for whatever reason, incorporate
// data on that field (e.g. a forward declared class wouldn't have information
// on members on the forward declaration, but would have the class name).
//
//===----------------------------------------------------------------------===//
#include "Representation.h"
#include "llvm/Support/Error.h"
namespace clang {
namespace doc {
static const SymbolID EmptySID = SymbolID();
template <typename T>
std::unique_ptr<Info> reduce(std::vector<std::unique_ptr<Info>> &Values) {
std::unique_ptr<Info> Merged = llvm::make_unique<T>();
T *Tmp = static_cast<T *>(Merged.get());
for (auto &I : Values)
Tmp->merge(std::move(*static_cast<T *>(I.get())));
return Merged;
}
// Dispatch function.
llvm::Expected<std::unique_ptr<Info>>
mergeInfos(std::vector<std::unique_ptr<Info>> &Values) {
if (Values.empty())
return llvm::make_error<llvm::StringError>("No info values to merge.\n",
llvm::inconvertibleErrorCode());
switch (Values[0]->IT) {
case InfoType::IT_namespace:
return reduce<NamespaceInfo>(Values);
case InfoType::IT_record:
return reduce<RecordInfo>(Values);
case InfoType::IT_enum:
return reduce<EnumInfo>(Values);
case InfoType::IT_function:
return reduce<FunctionInfo>(Values);
default:
return llvm::make_error<llvm::StringError>("Unexpected info type.\n",
llvm::inconvertibleErrorCode());
}
}
void Info::mergeBase(Info &&Other) {
assert(mergeable(Other));
if (USR == EmptySID)
USR = Other.USR;
if (Name == "")
Name = Other.Name;
if (Namespace.empty())
Namespace = std::move(Other.Namespace);
// Unconditionally extend the description, since each decl may have a comment.
std::move(Other.Description.begin(), Other.Description.end(),
std::back_inserter(Description));
}
bool Info::mergeable(const Info &Other) {
return IT == Other.IT && (USR == EmptySID || USR == Other.USR);
}
void SymbolInfo::merge(SymbolInfo &&Other) {
assert(mergeable(Other));
if (!DefLoc)
DefLoc = std::move(Other.DefLoc);
// Unconditionally extend the list of locations, since we want all of them.
std::move(Other.Loc.begin(), Other.Loc.end(), std::back_inserter(Loc));
mergeBase(std::move(Other));
}
void NamespaceInfo::merge(NamespaceInfo &&Other) {
assert(mergeable(Other));
mergeBase(std::move(Other));
}
void RecordInfo::merge(RecordInfo &&Other) {
assert(mergeable(Other));
if (!TagType)
TagType = Other.TagType;
if (Members.empty())
Members = std::move(Other.Members);
if (Parents.empty())
Parents = std::move(Other.Parents);
if (VirtualParents.empty())
VirtualParents = std::move(Other.VirtualParents);
SymbolInfo::merge(std::move(Other));
}
void EnumInfo::merge(EnumInfo &&Other) {
assert(mergeable(Other));
if (!Scoped)
Scoped = Other.Scoped;
if (Members.empty())
Members = std::move(Other.Members);
SymbolInfo::merge(std::move(Other));
}
void FunctionInfo::merge(FunctionInfo &&Other) {
assert(mergeable(Other));
if (!IsMethod)
IsMethod = Other.IsMethod;
if (!Access)
Access = Other.Access;
if (ReturnType.Type.USR == EmptySID && ReturnType.Type.Name == "")
ReturnType = std::move(Other.ReturnType);
if (Parent.USR == EmptySID && Parent.Name == "")
Parent = std::move(Other.Parent);
if (Params.empty())
Params = std::move(Other.Params);
SymbolInfo::merge(std::move(Other));
}
} // namespace doc
} // namespace clang

View File

@@ -1,251 +0,0 @@
///===-- Representation.h - ClangDoc Representation -------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file defines the internal representations of different declaration
// types for the clang-doc tool.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_DOC_REPRESENTATION_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_DOC_REPRESENTATION_H
#include "clang/AST/Type.h"
#include "clang/Basic/Specifiers.h"
#include "clang/Tooling/StandaloneExecution.h"
#include "llvm/ADT/Optional.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringExtras.h"
#include <array>
#include <string>
namespace clang {
namespace doc {
// SHA1'd hash of a USR.
using SymbolID = std::array<uint8_t, 20>;
struct Info;
enum class InfoType {
IT_default,
IT_namespace,
IT_record,
IT_function,
IT_enum
};
// A representation of a parsed comment.
struct CommentInfo {
CommentInfo() = default;
CommentInfo(CommentInfo &Other) = delete;
CommentInfo(CommentInfo &&Other) = default;
SmallString<16> Kind; // Kind of comment (TextComment, InlineCommandComment,
// HTMLStartTagComment, HTMLEndTagComment,
// BlockCommandComment, ParamCommandComment,
// TParamCommandComment, VerbatimBlockComment,
// VerbatimBlockLineComment, VerbatimLineComment).
SmallString<64> Text; // Text of the comment.
SmallString<16> Name; // Name of the comment (for Verbatim and HTML).
SmallString<8> Direction; // Parameter direction (for (T)ParamCommand).
SmallString<16> ParamName; // Parameter name (for (T)ParamCommand).
SmallString<16> CloseName; // Closing tag name (for VerbatimBlock).
bool SelfClosing = false; // Indicates if tag is self-closing (for HTML).
bool Explicit = false; // Indicates if the direction of a param is explicit
// (for (T)ParamCommand).
llvm::SmallVector<SmallString<16>, 4>
AttrKeys; // List of attribute keys (for HTML).
llvm::SmallVector<SmallString<16>, 4>
AttrValues; // List of attribute values for each key (for HTML).
llvm::SmallVector<SmallString<16>, 4>
Args; // List of arguments to commands (for InlineCommand).
std::vector<std::unique_ptr<CommentInfo>>
Children; // List of child comments for this CommentInfo.
};
struct Reference {
Reference() = default;
Reference(llvm::StringRef Name) : Name(Name) {}
Reference(SymbolID USR, StringRef Name, InfoType IT)
: USR(USR), Name(Name), RefType(IT) {}
bool operator==(const Reference &Other) const {
return std::tie(USR, Name, RefType) ==
std::tie(Other.USR, Other.Name, Other.RefType);
}
SymbolID USR = SymbolID(); // Unique identifer for referenced decl
SmallString<16> Name; // Name of type (possibly unresolved).
InfoType RefType = InfoType::IT_default; // Indicates the type of this
// Reference (namespace, record,
// function, enum, default).
};
// A base struct for TypeInfos
struct TypeInfo {
TypeInfo() = default;
TypeInfo(SymbolID Type, StringRef Field, InfoType IT)
: Type(Type, Field, IT) {}
TypeInfo(llvm::StringRef RefName) : Type(RefName) {}
bool operator==(const TypeInfo &Other) const { return Type == Other.Type; }
Reference Type; // Referenced type in this info.
};
// Info for field types.
struct FieldTypeInfo : public TypeInfo {
FieldTypeInfo() = default;
FieldTypeInfo(SymbolID Type, StringRef Field, InfoType IT,
llvm::StringRef Name)
: TypeInfo(Type, Field, IT), Name(Name) {}
FieldTypeInfo(llvm::StringRef RefName, llvm::StringRef Name)
: TypeInfo(RefName), Name(Name) {}
bool operator==(const FieldTypeInfo &Other) const {
return std::tie(Type, Name) == std::tie(Other.Type, Other.Name);
}
SmallString<16> Name; // Name associated with this info.
};
// Info for member types.
struct MemberTypeInfo : public FieldTypeInfo {
MemberTypeInfo() = default;
MemberTypeInfo(SymbolID Type, StringRef Field, InfoType IT,
llvm::StringRef Name, AccessSpecifier Access)
: FieldTypeInfo(Type, Field, IT, Name), Access(Access) {}
MemberTypeInfo(llvm::StringRef RefName, llvm::StringRef Name,
AccessSpecifier Access)
: FieldTypeInfo(RefName, Name), Access(Access) {}
bool operator==(const MemberTypeInfo &Other) const {
return std::tie(Type, Name, Access) ==
std::tie(Other.Type, Other.Name, Other.Access);
}
AccessSpecifier Access = AccessSpecifier::AS_none; // Access level associated
// with this info (public,
// protected, private,
// none).
};
struct Location {
Location() = default;
Location(int LineNumber, SmallString<16> Filename)
: LineNumber(LineNumber), Filename(std::move(Filename)) {}
bool operator==(const Location &Other) const {
return std::tie(LineNumber, Filename) ==
std::tie(Other.LineNumber, Other.Filename);
}
int LineNumber; // Line number of this Location.
SmallString<32> Filename; // File for this Location.
};
/// A base struct for Infos.
struct Info {
Info() = default;
Info(InfoType IT) : IT(IT) {}
Info(const Info &Other) = delete;
Info(Info &&Other) = default;
SymbolID USR =
SymbolID(); // Unique identifier for the decl described by this Info.
const InfoType IT = InfoType::IT_default; // InfoType of this particular Info.
SmallString<16> Name; // Unqualified name of the decl.
llvm::SmallVector<Reference, 4>
Namespace; // List of parent namespaces for this decl.
std::vector<CommentInfo> Description; // Comment description of this decl.
void mergeBase(Info &&I);
bool mergeable(const Info &Other);
};
// Info for namespaces.
struct NamespaceInfo : public Info {
NamespaceInfo() : Info(InfoType::IT_namespace) {}
void merge(NamespaceInfo &&I);
};
// Info for symbols.
struct SymbolInfo : public Info {
SymbolInfo(InfoType IT) : Info(IT) {}
void merge(SymbolInfo &&I);
llvm::Optional<Location> DefLoc; // Location where this decl is defined.
llvm::SmallVector<Location, 2> Loc; // Locations where this decl is declared.
};
// TODO: Expand to allow for documenting templating and default args.
// Info for functions.
struct FunctionInfo : public SymbolInfo {
FunctionInfo() : SymbolInfo(InfoType::IT_function) {}
void merge(FunctionInfo &&I);
bool IsMethod = false; // Indicates whether this function is a class method.
Reference Parent; // Reference to the parent class decl for this method.
TypeInfo ReturnType; // Info about the return type of this function.
llvm::SmallVector<FieldTypeInfo, 4> Params; // List of parameters.
// Access level for this method (public, private, protected, none).
AccessSpecifier Access = AccessSpecifier::AS_none;
};
// TODO: Expand to allow for documenting templating, inheritance access,
// friend classes
// Info for types.
struct RecordInfo : public SymbolInfo {
RecordInfo() : SymbolInfo(InfoType::IT_record) {}
void merge(RecordInfo &&I);
TagTypeKind TagType = TagTypeKind::TTK_Struct; // Type of this record
// (struct, class, union,
// interface).
llvm::SmallVector<MemberTypeInfo, 4>
Members; // List of info about record members.
llvm::SmallVector<Reference, 4> Parents; // List of base/parent records
// (does not include virtual
// parents).
llvm::SmallVector<Reference, 4>
VirtualParents; // List of virtual base/parent records.
};
// TODO: Expand to allow for documenting templating.
// Info for types.
struct EnumInfo : public SymbolInfo {
EnumInfo() : SymbolInfo(InfoType::IT_enum) {}
void merge(EnumInfo &&I);
bool Scoped =
false; // Indicates whether this enum is scoped (e.g. enum class).
llvm::SmallVector<SmallString<16>, 4> Members; // List of enum members.
};
// TODO: Add functionality to include separate markdown pages.
// A standalone function to call to merge a vector of infos into one.
// This assumes that all infos in the vector are of the same type, and will fail
// if they are different.
llvm::Expected<std::unique_ptr<Info>>
mergeInfos(std::vector<std::unique_ptr<Info>> &Values);
struct ClangDocContext {
tooling::ExecutionContext *ECtx;
bool PublicOnly;
};
} // namespace doc
} // namespace clang
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_DOC_REPRESENTATION_H

View File

@@ -1,368 +0,0 @@
//===-- Serializer.cpp - ClangDoc Serializer --------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "Serialize.h"
#include "BitcodeWriter.h"
#include "clang/AST/Comment.h"
#include "clang/Index/USRGeneration.h"
#include "llvm/ADT/Hashing.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/Support/SHA1.h"
using clang::comments::FullComment;
namespace clang {
namespace doc {
namespace serialize {
SymbolID hashUSR(llvm::StringRef USR) {
return llvm::SHA1::hash(arrayRefFromStringRef(USR));
}
class ClangDocCommentVisitor
: public ConstCommentVisitor<ClangDocCommentVisitor> {
public:
ClangDocCommentVisitor(CommentInfo &CI) : CurrentCI(CI) {}
void parseComment(const comments::Comment *C);
void visitTextComment(const TextComment *C);
void visitInlineCommandComment(const InlineCommandComment *C);
void visitHTMLStartTagComment(const HTMLStartTagComment *C);
void visitHTMLEndTagComment(const HTMLEndTagComment *C);
void visitBlockCommandComment(const BlockCommandComment *C);
void visitParamCommandComment(const ParamCommandComment *C);
void visitTParamCommandComment(const TParamCommandComment *C);
void visitVerbatimBlockComment(const VerbatimBlockComment *C);
void visitVerbatimBlockLineComment(const VerbatimBlockLineComment *C);
void visitVerbatimLineComment(const VerbatimLineComment *C);
private:
std::string getCommandName(unsigned CommandID) const;
bool isWhitespaceOnly(StringRef S) const;
CommentInfo &CurrentCI;
};
void ClangDocCommentVisitor::parseComment(const comments::Comment *C) {
CurrentCI.Kind = C->getCommentKindName();
ConstCommentVisitor<ClangDocCommentVisitor>::visit(C);
for (comments::Comment *Child :
llvm::make_range(C->child_begin(), C->child_end())) {
CurrentCI.Children.emplace_back(llvm::make_unique<CommentInfo>());
ClangDocCommentVisitor Visitor(*CurrentCI.Children.back());
Visitor.parseComment(Child);
}
}
void ClangDocCommentVisitor::visitTextComment(const TextComment *C) {
if (!isWhitespaceOnly(C->getText()))
CurrentCI.Text = C->getText();
}
void ClangDocCommentVisitor::visitInlineCommandComment(
const InlineCommandComment *C) {
CurrentCI.Name = getCommandName(C->getCommandID());
for (unsigned I = 0, E = C->getNumArgs(); I != E; ++I)
CurrentCI.Args.push_back(C->getArgText(I));
}
void ClangDocCommentVisitor::visitHTMLStartTagComment(
const HTMLStartTagComment *C) {
CurrentCI.Name = C->getTagName();
CurrentCI.SelfClosing = C->isSelfClosing();
for (unsigned I = 0, E = C->getNumAttrs(); I < E; ++I) {
const HTMLStartTagComment::Attribute &Attr = C->getAttr(I);
CurrentCI.AttrKeys.push_back(Attr.Name);
CurrentCI.AttrValues.push_back(Attr.Value);
}
}
void ClangDocCommentVisitor::visitHTMLEndTagComment(
const HTMLEndTagComment *C) {
CurrentCI.Name = C->getTagName();
CurrentCI.SelfClosing = true;
}
void ClangDocCommentVisitor::visitBlockCommandComment(
const BlockCommandComment *C) {
CurrentCI.Name = getCommandName(C->getCommandID());
for (unsigned I = 0, E = C->getNumArgs(); I < E; ++I)
CurrentCI.Args.push_back(C->getArgText(I));
}
void ClangDocCommentVisitor::visitParamCommandComment(
const ParamCommandComment *C) {
CurrentCI.Direction =
ParamCommandComment::getDirectionAsString(C->getDirection());
CurrentCI.Explicit = C->isDirectionExplicit();
if (C->hasParamName())
CurrentCI.ParamName = C->getParamNameAsWritten();
}
void ClangDocCommentVisitor::visitTParamCommandComment(
const TParamCommandComment *C) {
if (C->hasParamName())
CurrentCI.ParamName = C->getParamNameAsWritten();
}
void ClangDocCommentVisitor::visitVerbatimBlockComment(
const VerbatimBlockComment *C) {
CurrentCI.Name = getCommandName(C->getCommandID());
CurrentCI.CloseName = C->getCloseName();
}
void ClangDocCommentVisitor::visitVerbatimBlockLineComment(
const VerbatimBlockLineComment *C) {
if (!isWhitespaceOnly(C->getText()))
CurrentCI.Text = C->getText();
}
void ClangDocCommentVisitor::visitVerbatimLineComment(
const VerbatimLineComment *C) {
if (!isWhitespaceOnly(C->getText()))
CurrentCI.Text = C->getText();
}
bool ClangDocCommentVisitor::isWhitespaceOnly(llvm::StringRef S) const {
return std::all_of(S.begin(), S.end(), isspace);
}
std::string ClangDocCommentVisitor::getCommandName(unsigned CommandID) const {
const CommandInfo *Info = CommandTraits::getBuiltinCommandInfo(CommandID);
if (Info)
return Info->Name;
// TODO: Add parsing for \file command.
return "<not a builtin command>";
}
// Serializing functions.
template <typename T> static std::string serialize(T &I) {
SmallString<2048> Buffer;
llvm::BitstreamWriter Stream(Buffer);
ClangDocBitcodeWriter Writer(Stream);
Writer.emitBlock(I);
return Buffer.str().str();
}
static void parseFullComment(const FullComment *C, CommentInfo &CI) {
ClangDocCommentVisitor Visitor(CI);
Visitor.parseComment(C);
}
static SymbolID getUSRForDecl(const Decl *D) {
llvm::SmallString<128> USR;
if (index::generateUSRForDecl(D, USR))
return SymbolID();
return hashUSR(USR);
}
static RecordDecl *getDeclForType(const QualType &T) {
auto *Ty = T->getAs<RecordType>();
if (!Ty)
return nullptr;
return Ty->getDecl()->getDefinition();
}
static bool isPublic(const clang::AccessSpecifier AS,
const clang::Linkage Link) {
if (AS == clang::AccessSpecifier::AS_private)
return false;
else if ((Link == clang::Linkage::ModuleLinkage) ||
(Link == clang::Linkage::ExternalLinkage))
return true;
return false; // otherwise, linkage is some form of internal linkage
}
static void parseFields(RecordInfo &I, const RecordDecl *D, bool PublicOnly) {
for (const FieldDecl *F : D->fields()) {
if (PublicOnly && !isPublic(F->getAccessUnsafe(), F->getLinkageInternal()))
continue;
if (const auto *T = getDeclForType(F->getTypeSourceInfo()->getType())) {
// Use getAccessUnsafe so that we just get the default AS_none if it's not
// valid, as opposed to an assert.
if (const auto *N = dyn_cast<EnumDecl>(T)) {
I.Members.emplace_back(getUSRForDecl(T), N->getNameAsString(),
InfoType::IT_enum, F->getNameAsString(),
N->getAccessUnsafe());
continue;
} else if (const auto *N = dyn_cast<RecordDecl>(T)) {
I.Members.emplace_back(getUSRForDecl(T), N->getNameAsString(),
InfoType::IT_record, F->getNameAsString(),
N->getAccessUnsafe());
continue;
}
}
I.Members.emplace_back(F->getTypeSourceInfo()->getType().getAsString(),
F->getNameAsString(), F->getAccessUnsafe());
}
}
static void parseEnumerators(EnumInfo &I, const EnumDecl *D) {
for (const EnumConstantDecl *E : D->enumerators())
I.Members.emplace_back(E->getNameAsString());
}
static void parseParameters(FunctionInfo &I, const FunctionDecl *D) {
for (const ParmVarDecl *P : D->parameters()) {
if (const auto *T = getDeclForType(P->getOriginalType())) {
if (const auto *N = dyn_cast<EnumDecl>(T)) {
I.Params.emplace_back(getUSRForDecl(N), N->getNameAsString(),
InfoType::IT_enum, P->getNameAsString());
continue;
} else if (const auto *N = dyn_cast<RecordDecl>(T)) {
I.Params.emplace_back(getUSRForDecl(N), N->getNameAsString(),
InfoType::IT_record, P->getNameAsString());
continue;
}
}
I.Params.emplace_back(P->getOriginalType().getAsString(),
P->getNameAsString());
}
}
static void parseBases(RecordInfo &I, const CXXRecordDecl *D) {
for (const CXXBaseSpecifier &B : D->bases()) {
if (B.isVirtual())
continue;
if (const auto *P = getDeclForType(B.getType()))
I.Parents.emplace_back(getUSRForDecl(P), P->getNameAsString(),
InfoType::IT_record);
else
I.Parents.emplace_back(B.getType().getAsString());
}
for (const CXXBaseSpecifier &B : D->vbases()) {
if (const auto *P = getDeclForType(B.getType()))
I.VirtualParents.emplace_back(getUSRForDecl(P), P->getNameAsString(),
InfoType::IT_record);
else
I.VirtualParents.emplace_back(B.getType().getAsString());
}
}
template <typename T>
static void
populateParentNamespaces(llvm::SmallVector<Reference, 4> &Namespaces,
const T *D) {
const auto *DC = dyn_cast<DeclContext>(D);
while ((DC = DC->getParent())) {
if (const auto *N = dyn_cast<NamespaceDecl>(DC))
Namespaces.emplace_back(getUSRForDecl(N), N->getNameAsString(),
InfoType::IT_namespace);
else if (const auto *N = dyn_cast<RecordDecl>(DC))
Namespaces.emplace_back(getUSRForDecl(N), N->getNameAsString(),
InfoType::IT_record);
else if (const auto *N = dyn_cast<FunctionDecl>(DC))
Namespaces.emplace_back(getUSRForDecl(N), N->getNameAsString(),
InfoType::IT_function);
else if (const auto *N = dyn_cast<EnumDecl>(DC))
Namespaces.emplace_back(getUSRForDecl(N), N->getNameAsString(),
InfoType::IT_enum);
}
}
template <typename T>
static void populateInfo(Info &I, const T *D, const FullComment *C) {
I.USR = getUSRForDecl(D);
I.Name = D->getNameAsString();
populateParentNamespaces(I.Namespace, D);
if (C) {
I.Description.emplace_back();
parseFullComment(C, I.Description.back());
}
}
template <typename T>
static void populateSymbolInfo(SymbolInfo &I, const T *D, const FullComment *C,
int LineNumber, StringRef Filename) {
populateInfo(I, D, C);
if (D->isThisDeclarationADefinition())
I.DefLoc.emplace(LineNumber, Filename);
else
I.Loc.emplace_back(LineNumber, Filename);
}
static void populateFunctionInfo(FunctionInfo &I, const FunctionDecl *D,
const FullComment *FC, int LineNumber,
StringRef Filename) {
populateSymbolInfo(I, D, FC, LineNumber, Filename);
if (const auto *T = getDeclForType(D->getReturnType())) {
if (dyn_cast<EnumDecl>(T))
I.ReturnType =
TypeInfo(getUSRForDecl(T), T->getNameAsString(), InfoType::IT_enum);
else if (dyn_cast<RecordDecl>(T))
I.ReturnType =
TypeInfo(getUSRForDecl(T), T->getNameAsString(), InfoType::IT_record);
} else {
I.ReturnType = TypeInfo(D->getReturnType().getAsString());
}
parseParameters(I, D);
}
std::string emitInfo(const NamespaceDecl *D, const FullComment *FC,
int LineNumber, llvm::StringRef File, bool PublicOnly) {
if (PublicOnly && ((D->isAnonymousNamespace()) ||
!isPublic(D->getAccess(), D->getLinkageInternal())))
return "";
NamespaceInfo I;
populateInfo(I, D, FC);
return serialize(I);
}
std::string emitInfo(const RecordDecl *D, const FullComment *FC, int LineNumber,
llvm::StringRef File, bool PublicOnly) {
if (PublicOnly && !isPublic(D->getAccess(), D->getLinkageInternal()))
return "";
RecordInfo I;
populateSymbolInfo(I, D, FC, LineNumber, File);
I.TagType = D->getTagKind();
parseFields(I, D, PublicOnly);
if (const auto *C = dyn_cast<CXXRecordDecl>(D))
parseBases(I, C);
return serialize(I);
}
std::string emitInfo(const FunctionDecl *D, const FullComment *FC,
int LineNumber, llvm::StringRef File, bool PublicOnly) {
if (PublicOnly && !isPublic(D->getAccess(), D->getLinkageInternal()))
return "";
FunctionInfo I;
populateFunctionInfo(I, D, FC, LineNumber, File);
I.Access = clang::AccessSpecifier::AS_none;
return serialize(I);
}
std::string emitInfo(const CXXMethodDecl *D, const FullComment *FC,
int LineNumber, llvm::StringRef File, bool PublicOnly) {
if (PublicOnly && !isPublic(D->getAccess(), D->getLinkageInternal()))
return "";
FunctionInfo I;
populateFunctionInfo(I, D, FC, LineNumber, File);
I.IsMethod = true;
I.Parent = Reference{getUSRForDecl(D->getParent()),
D->getParent()->getNameAsString(), InfoType::IT_record};
I.Access = D->getAccess();
return serialize(I);
}
std::string emitInfo(const EnumDecl *D, const FullComment *FC, int LineNumber,
llvm::StringRef File, bool PublicOnly) {
if (PublicOnly && !isPublic(D->getAccess(), D->getLinkageInternal()))
return "";
EnumInfo I;
populateSymbolInfo(I, D, FC, LineNumber, File);
I.Scoped = D->isScoped();
parseEnumerators(I, D);
return serialize(I);
}
} // namespace serialize
} // namespace doc
} // namespace clang

View File

@@ -1,53 +0,0 @@
//===-- Serializer.h - ClangDoc Serializer ----------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file implements the serializing functions fro the clang-doc tool. Given
// a particular declaration, it collects the appropriate information and returns
// a serialized bitcode string for the declaration.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_DOC_SERIALIZE_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_DOC_SERIALIZE_H
#include "Representation.h"
#include "clang/AST/AST.h"
#include "clang/AST/CommentVisitor.h"
#include <string>
#include <vector>
using namespace clang::comments;
namespace clang {
namespace doc {
namespace serialize {
std::string emitInfo(const NamespaceDecl *D, const FullComment *FC,
int LineNumber, StringRef File, bool PublicOnly);
std::string emitInfo(const RecordDecl *D, const FullComment *FC, int LineNumber,
StringRef File, bool PublicOnly);
std::string emitInfo(const EnumDecl *D, const FullComment *FC, int LineNumber,
StringRef File, bool PublicOnly);
std::string emitInfo(const FunctionDecl *D, const FullComment *FC,
int LineNumber, StringRef File, bool PublicOnly);
std::string emitInfo(const CXXMethodDecl *D, const FullComment *FC,
int LineNumber, StringRef File, bool PublicOnly);
// Function to hash a given USR value for storage.
// As USRs (Unified Symbol Resolution) could be large, especially for functions
// with long type arguments, we use 160-bits SHA1(USR) values to
// guarantee the uniqueness of symbols while using a relatively small amount of
// memory (vs storing USRs directly).
SymbolID hashUSR(llvm::StringRef USR);
} // namespace serialize
} // namespace doc
} // namespace clang
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_DOC_SERIALIZE_H

View File

@@ -1,268 +0,0 @@
//===-- ClangDocYAML.cpp - ClangDoc YAML -----------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
// Implementation of the YAML generator, converting decl info into YAML output.
//===----------------------------------------------------------------------===//
#include "Generators.h"
#include "llvm/Support/YAMLTraits.h"
#include "llvm/Support/raw_ostream.h"
using namespace clang::doc;
LLVM_YAML_IS_SEQUENCE_VECTOR(FieldTypeInfo)
LLVM_YAML_IS_SEQUENCE_VECTOR(MemberTypeInfo)
LLVM_YAML_IS_SEQUENCE_VECTOR(Reference)
LLVM_YAML_IS_SEQUENCE_VECTOR(Location)
LLVM_YAML_IS_SEQUENCE_VECTOR(CommentInfo)
LLVM_YAML_IS_SEQUENCE_VECTOR(std::unique_ptr<CommentInfo>)
LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::SmallString<16>)
namespace llvm {
namespace yaml {
// Enumerations to YAML output.
template <> struct ScalarEnumerationTraits<clang::AccessSpecifier> {
static void enumeration(IO &IO, clang::AccessSpecifier &Value) {
IO.enumCase(Value, "Public", clang::AccessSpecifier::AS_public);
IO.enumCase(Value, "Protected", clang::AccessSpecifier::AS_protected);
IO.enumCase(Value, "Private", clang::AccessSpecifier::AS_private);
IO.enumCase(Value, "None", clang::AccessSpecifier::AS_none);
}
};
template <> struct ScalarEnumerationTraits<clang::TagTypeKind> {
static void enumeration(IO &IO, clang::TagTypeKind &Value) {
IO.enumCase(Value, "Struct", clang::TagTypeKind::TTK_Struct);
IO.enumCase(Value, "Interface", clang::TagTypeKind::TTK_Interface);
IO.enumCase(Value, "Union", clang::TagTypeKind::TTK_Union);
IO.enumCase(Value, "Class", clang::TagTypeKind::TTK_Class);
IO.enumCase(Value, "Enum", clang::TagTypeKind::TTK_Enum);
}
};
template <> struct ScalarEnumerationTraits<InfoType> {
static void enumeration(IO &IO, InfoType &Value) {
IO.enumCase(Value, "Namespace", InfoType::IT_namespace);
IO.enumCase(Value, "Record", InfoType::IT_record);
IO.enumCase(Value, "Function", InfoType::IT_function);
IO.enumCase(Value, "Enum", InfoType::IT_enum);
IO.enumCase(Value, "Default", InfoType::IT_default);
}
};
// Scalars to YAML output.
template <unsigned U> struct ScalarTraits<SmallString<U>> {
static void output(const SmallString<U> &S, void *, llvm::raw_ostream &OS) {
for (const auto &C : S)
OS << C;
}
static StringRef input(StringRef Scalar, void *, SmallString<U> &Value) {
Value.assign(Scalar.begin(), Scalar.end());
return StringRef();
}
static QuotingType mustQuote(StringRef) { return QuotingType::Single; }
};
template <> struct ScalarTraits<std::array<unsigned char, 20>> {
static void output(const std::array<unsigned char, 20> &S, void *,
llvm::raw_ostream &OS) {
OS << toHex(toStringRef(S));
}
static StringRef input(StringRef Scalar, void *,
std::array<unsigned char, 20> &Value) {
if (Scalar.size() != 40)
return "Error: Incorrect scalar size for USR.";
Value = StringToSymbol(Scalar);
return StringRef();
}
static SymbolID StringToSymbol(llvm::StringRef Value) {
SymbolID USR;
std::string HexString = fromHex(Value);
std::copy(HexString.begin(), HexString.end(), USR.begin());
return SymbolID(USR);
}
static QuotingType mustQuote(StringRef) { return QuotingType::Single; }
};
// Helper functions to map infos to YAML.
static void TypeInfoMapping(IO &IO, TypeInfo &I) {
IO.mapOptional("Type", I.Type, Reference());
}
static void FieldTypeInfoMapping(IO &IO, FieldTypeInfo &I) {
TypeInfoMapping(IO, I);
IO.mapOptional("Name", I.Name, SmallString<16>());
}
static void InfoMapping(IO &IO, Info &I) {
IO.mapRequired("USR", I.USR);
IO.mapOptional("Name", I.Name, SmallString<16>());
IO.mapOptional("Namespace", I.Namespace, llvm::SmallVector<Reference, 4>());
IO.mapOptional("Description", I.Description);
}
static void SymbolInfoMapping(IO &IO, SymbolInfo &I) {
InfoMapping(IO, I);
IO.mapOptional("DefLocation", I.DefLoc, Optional<Location>());
IO.mapOptional("Location", I.Loc, llvm::SmallVector<Location, 2>());
}
static void CommentInfoMapping(IO &IO, CommentInfo &I) {
IO.mapOptional("Kind", I.Kind, SmallString<16>());
IO.mapOptional("Text", I.Text, SmallString<64>());
IO.mapOptional("Name", I.Name, SmallString<16>());
IO.mapOptional("Direction", I.Direction, SmallString<8>());
IO.mapOptional("ParamName", I.ParamName, SmallString<16>());
IO.mapOptional("CloseName", I.CloseName, SmallString<16>());
IO.mapOptional("SelfClosing", I.SelfClosing, false);
IO.mapOptional("Explicit", I.Explicit, false);
IO.mapOptional("Args", I.Args, llvm::SmallVector<SmallString<16>, 4>());
IO.mapOptional("AttrKeys", I.AttrKeys,
llvm::SmallVector<SmallString<16>, 4>());
IO.mapOptional("AttrValues", I.AttrValues,
llvm::SmallVector<SmallString<16>, 4>());
IO.mapOptional("Children", I.Children);
}
// Template specialization to YAML traits for Infos.
template <> struct MappingTraits<Location> {
static void mapping(IO &IO, Location &Loc) {
IO.mapOptional("LineNumber", Loc.LineNumber, 0);
IO.mapOptional("Filename", Loc.Filename, SmallString<32>());
}
};
template <> struct MappingTraits<Reference> {
static void mapping(IO &IO, Reference &Ref) {
IO.mapOptional("Type", Ref.RefType, InfoType::IT_default);
IO.mapOptional("Name", Ref.Name, SmallString<16>());
IO.mapOptional("USR", Ref.USR, SymbolID());
}
};
template <> struct MappingTraits<TypeInfo> {
static void mapping(IO &IO, TypeInfo &I) { TypeInfoMapping(IO, I); }
};
template <> struct MappingTraits<FieldTypeInfo> {
static void mapping(IO &IO, FieldTypeInfo &I) {
TypeInfoMapping(IO, I);
IO.mapOptional("Name", I.Name, SmallString<16>());
}
};
template <> struct MappingTraits<MemberTypeInfo> {
static void mapping(IO &IO, MemberTypeInfo &I) {
FieldTypeInfoMapping(IO, I);
IO.mapOptional("Access", I.Access, clang::AccessSpecifier::AS_none);
}
};
template <> struct MappingTraits<NamespaceInfo> {
static void mapping(IO &IO, NamespaceInfo &I) { InfoMapping(IO, I); }
};
template <> struct MappingTraits<RecordInfo> {
static void mapping(IO &IO, RecordInfo &I) {
SymbolInfoMapping(IO, I);
IO.mapOptional("TagType", I.TagType, clang::TagTypeKind::TTK_Struct);
IO.mapOptional("Members", I.Members);
IO.mapOptional("Parents", I.Parents, llvm::SmallVector<Reference, 4>());
IO.mapOptional("VirtualParents", I.VirtualParents,
llvm::SmallVector<Reference, 4>());
}
};
template <> struct MappingTraits<EnumInfo> {
static void mapping(IO &IO, EnumInfo &I) {
SymbolInfoMapping(IO, I);
IO.mapOptional("Scoped", I.Scoped, false);
IO.mapOptional("Members", I.Members);
}
};
template <> struct MappingTraits<FunctionInfo> {
static void mapping(IO &IO, FunctionInfo &I) {
SymbolInfoMapping(IO, I);
IO.mapOptional("IsMethod", I.IsMethod, false);
IO.mapOptional("Parent", I.Parent, Reference());
IO.mapOptional("Params", I.Params);
IO.mapOptional("ReturnType", I.ReturnType);
IO.mapOptional("Access", I.Access, clang::AccessSpecifier::AS_none);
}
};
template <> struct MappingTraits<CommentInfo> {
static void mapping(IO &IO, CommentInfo &I) { CommentInfoMapping(IO, I); }
};
template <> struct MappingTraits<std::unique_ptr<CommentInfo>> {
static void mapping(IO &IO, std::unique_ptr<CommentInfo> &I) {
if (I)
CommentInfoMapping(IO, *I);
}
};
} // end namespace yaml
} // end namespace llvm
namespace clang {
namespace doc {
/// Generator for YAML documentation.
class YAMLGenerator : public Generator {
public:
static const char *Format;
bool generateDocForInfo(Info *I, llvm::raw_ostream &OS) override;
};
const char *YAMLGenerator::Format = "yaml";
bool YAMLGenerator::generateDocForInfo(Info *I, llvm::raw_ostream &OS) {
llvm::yaml::Output InfoYAML(OS);
switch (I->IT) {
case InfoType::IT_namespace:
InfoYAML << *static_cast<clang::doc::NamespaceInfo *>(I);
break;
case InfoType::IT_record:
InfoYAML << *static_cast<clang::doc::RecordInfo *>(I);
break;
case InfoType::IT_enum:
InfoYAML << *static_cast<clang::doc::EnumInfo *>(I);
break;
case InfoType::IT_function:
InfoYAML << *static_cast<clang::doc::FunctionInfo *>(I);
break;
case InfoType::IT_default:
llvm::errs() << "Unexpected info type in index.\n";
return true;
}
return false;
}
static GeneratorRegistry::Add<YAMLGenerator> YAML(YAMLGenerator::Format,
"Generator for YAML output.");
// This anchor is used to force the linker to link in the generated object file
// and thus register the generator.
volatile int YAMLGeneratorAnchorSource = 0;
} // namespace doc
} // namespace clang

View File

@@ -1,200 +0,0 @@
#!/usr/bin/env python3
#
#===- gen_tests.py - clang-doc test generator ----------------*- python -*--===#
#
# The LLVM Compiler Infrastructure
#
# This file is distributed under the University of Illinois Open Source
# License. See LICENSE.TXT for details.
#
#===------------------------------------------------------------------------===#
"""
clang-doc test generator
==========================
Generates tests for clang-doc given a certain set of flags, a prefix for the
test file, and a given clang-doc binary. Please check emitted tests for
accuracy before using.
To generate all current tests:
- Generate mapper tests:
gen_tests.py -flag='--dump-mapper' -flag='--doxygen' -prefix mapper
- Generate reducer tests:
gen_tests.py -flag='--dump-intermediate' -flag='--doxygen' -prefix bc
- Generate yaml tests:
gen_tests.py -flag='--format=yaml' -flag='--doxygen' -prefix yaml
This script was written on/for Linux, and has not been tested on any other
platform and so it may not work.
"""
import argparse
import glob
import os
import shutil
import subprocess
RUN_CLANG_DOC = """
// RUN: clang-doc {0} -p %t %t/test.cpp -output=%t/docs
"""
RUN = """
// RUN: {0} %t/{1} | FileCheck %s --check-prefix CHECK-{2}
"""
CHECK = '// CHECK-{0}: '
CHECK_NEXT = '// CHECK-{0}-NEXT: '
def clear_test_prefix_files(prefix, tests_path):
if os.path.isdir(tests_path):
for root, dirs, files in os.walk(tests_path):
for filename in files:
if filename.startswith(prefix):
os.remove(os.path.join(root, filename))
def copy_to_test_file(test_case_path, test_cases_path):
# Copy file to 'test.cpp' to preserve file-dependent USRs
test_file = os.path.join(test_cases_path, 'test.cpp')
shutil.copyfile(test_case_path, test_file)
return test_file
def run_clang_doc(args, out_dir, test_file):
# Run clang-doc.
current_cmd = [args.clangdoc]
current_cmd.extend(args.flags)
current_cmd.append('--output=' + out_dir)
current_cmd.append(test_file)
print('Running ' + ' '.join(current_cmd))
return_code = subprocess.call(current_cmd)
if return_code:
return 1
return 0
def get_test_case_code(test_case_path, flags):
# Get the test case code
code = ''
with open(test_case_path, 'r') as code_file:
code = code_file.read()
code += RUN_CLANG_DOC.format(flags)
return code
def get_output(root, out_file, case_out_path, flags, checkname, bcanalyzer):
output = ''
run_cmd = ''
if '--dump-mapper' in flags or '--dump-intermediate' in flags:
# Run llvm-bcanalyzer
output = subprocess.check_output(
[bcanalyzer, '--dump',
os.path.join(root, out_file)])
output = output[:output.find('Summary of ')].rstrip()
run_cmd = RUN.format('llvm-bcanalyzer --dump',
os.path.join('docs', 'bc', out_file), checkname)
else:
# Run cat
output = subprocess.check_output(['cat', os.path.join(root, out_file)])
run_cmd = RUN.format(
'cat',
os.path.join('docs', os.path.relpath(root, case_out_path),
out_file), checkname)
# Format output.
output = output.replace('blob data = \'test\'', 'blob data = \'{{.*}}\'')
output = CHECK.format(checkname) + output.rstrip()
output = run_cmd + output.replace('\n',
'\n' + CHECK_NEXT.format(checkname))
return output + '\n'
def main():
parser = argparse.ArgumentParser(description='Generate clang-doc tests.')
parser.add_argument(
'-flag',
action='append',
default=[],
dest='flags',
help='Flags to pass to clang-doc.')
parser.add_argument(
'-prefix',
type=str,
default='',
dest='prefix',
help='Prefix for this test group.')
parser.add_argument(
'-clang-doc-binary',
dest='clangdoc',
metavar="PATH",
default='clang-doc',
help='path to clang-doc binary')
parser.add_argument(
'-llvm-bcanalyzer-binary',
dest='bcanalyzer',
metavar="PATH",
default='llvm-bcanalyzer',
help='path to llvm-bcanalyzer binary')
args = parser.parse_args()
flags = ' '.join(args.flags)
clang_doc_path = os.path.dirname(__file__)
tests_path = os.path.join(clang_doc_path, '..', 'test', 'clang-doc')
test_cases_path = os.path.join(tests_path, 'test_cases')
clear_test_prefix_files(args.prefix, tests_path)
for test_case_path in glob.glob(os.path.join(test_cases_path, '*')):
if test_case_path.endswith(
'compile_flags.txt') or test_case_path.endswith(
'compile_commands.json'):
continue
# Name of this test case
case_name = os.path.basename(test_case_path).split('.')[0]
test_file = copy_to_test_file(test_case_path, test_cases_path)
out_dir = os.path.join(test_cases_path, case_name)
if run_clang_doc(args, out_dir, test_file):
return 1
# Retrieve output and format as FileCheck tests
all_output = ''
num_outputs = 0
for root, dirs, files in os.walk(out_dir):
for out_file in files:
# Make the file check the first 3 letters (there's a very small chance
# that this will collide, but the fix is to simply change the decl name)
usr = os.path.basename(out_file).split('.')
# If the usr is less than 2, this isn't one of the test files.
if len(usr) < 2:
continue
all_output += get_output(root, out_file, out_dir, args.flags,
num_outputs, args.bcanalyzer)
num_outputs += 1
# Add test case code to test
all_output = get_test_case_code(test_case_path,
flags) + '\n' + all_output
# Write to test case file in /test.
test_out_path = os.path.join(
tests_path, args.prefix + '-' + os.path.basename(test_case_path))
with open(test_out_path, 'w+') as o:
o.write(all_output)
# Clean up
shutil.rmtree(out_dir)
os.remove(test_file)
if __name__ == '__main__':
main()

View File

@@ -1,17 +0,0 @@
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/..)
add_clang_executable(clang-doc
ClangDocMain.cpp
)
target_link_libraries(clang-doc
PRIVATE
clangAST
clangASTMatchers
clangBasic
clangFrontend
clangDoc
clangTooling
clangToolingCore
)

View File

@@ -1,253 +0,0 @@
//===-- ClangDocMain.cpp - ClangDoc -----------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This tool for generating C and C++ documenation from source code
// and comments. Generally, it runs a LibTooling FrontendAction on source files,
// mapping each declaration in those files to its USR and serializing relevant
// information into LLVM bitcode. It then runs a pass over the collected
// declaration information, reducing by USR. There is an option to dump this
// intermediate result to bitcode. Finally, it hands the reduced information
// off to a generator, which does the final parsing from the intermediate
// representation to the desired output format.
//
//===----------------------------------------------------------------------===//
#include "BitcodeReader.h"
#include "BitcodeWriter.h"
#include "ClangDoc.h"
#include "Generators.h"
#include "Representation.h"
#include "clang/AST/AST.h"
#include "clang/AST/Decl.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/ASTMatchers/ASTMatchersInternal.h"
#include "clang/Driver/Options.h"
#include "clang/Frontend/FrontendActions.h"
#include "clang/Tooling/CommonOptionsParser.h"
#include "clang/Tooling/Execution.h"
#include "clang/Tooling/StandaloneExecution.h"
#include "clang/Tooling/Tooling.h"
#include "llvm/ADT/APFloat.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/Process.h"
#include "llvm/Support/Signals.h"
#include "llvm/Support/raw_ostream.h"
#include <string>
using namespace clang::ast_matchers;
using namespace clang::tooling;
using namespace clang;
static llvm::cl::extrahelp CommonHelp(CommonOptionsParser::HelpMessage);
static llvm::cl::OptionCategory ClangDocCategory("clang-doc options");
static llvm::cl::opt<std::string>
OutDirectory("output",
llvm::cl::desc("Directory for outputting generated files."),
llvm::cl::init("docs"), llvm::cl::cat(ClangDocCategory));
static llvm::cl::opt<bool>
DumpMapperResult("dump-mapper",
llvm::cl::desc("Dump mapper results to bitcode file."),
llvm::cl::init(false), llvm::cl::cat(ClangDocCategory));
static llvm::cl::opt<bool> DumpIntermediateResult(
"dump-intermediate",
llvm::cl::desc("Dump intermediate results to bitcode file."),
llvm::cl::init(false), llvm::cl::cat(ClangDocCategory));
static llvm::cl::opt<bool>
PublicOnly("public", llvm::cl::desc("Document only public declarations."),
llvm::cl::init(false), llvm::cl::cat(ClangDocCategory));
enum OutputFormatTy {
yaml,
};
static llvm::cl::opt<OutputFormatTy> FormatEnum(
"format", llvm::cl::desc("Format for outputted docs."),
llvm::cl::values(clEnumVal(yaml, "Documentation in YAML format.")),
llvm::cl::init(yaml), llvm::cl::cat(ClangDocCategory));
static llvm::cl::opt<bool> DoxygenOnly(
"doxygen",
llvm::cl::desc("Use only doxygen-style comments to generate docs."),
llvm::cl::init(false), llvm::cl::cat(ClangDocCategory));
bool CreateDirectory(const Twine &DirName, bool ClearDirectory = false) {
std::error_code OK;
llvm::SmallString<128> DocsRootPath;
if (ClearDirectory) {
std::error_code RemoveStatus = llvm::sys::fs::remove_directories(DirName);
if (RemoveStatus != OK) {
llvm::errs() << "Unable to remove existing documentation directory for "
<< DirName << ".\n";
return true;
}
}
std::error_code DirectoryStatus = llvm::sys::fs::create_directories(DirName);
if (DirectoryStatus != OK) {
llvm::errs() << "Unable to create documentation directories.\n";
return true;
}
return false;
}
bool DumpResultToFile(const Twine &DirName, const Twine &FileName,
StringRef Buffer, bool ClearDirectory = false) {
std::error_code OK;
llvm::SmallString<128> IRRootPath;
llvm::sys::path::native(OutDirectory, IRRootPath);
llvm::sys::path::append(IRRootPath, DirName);
if (CreateDirectory(IRRootPath, ClearDirectory))
return true;
llvm::sys::path::append(IRRootPath, FileName);
std::error_code OutErrorInfo;
llvm::raw_fd_ostream OS(IRRootPath, OutErrorInfo, llvm::sys::fs::F_None);
if (OutErrorInfo != OK) {
llvm::errs() << "Error opening documentation file.\n";
return true;
}
OS << Buffer;
OS.close();
return false;
}
llvm::Expected<llvm::SmallString<128>>
getPath(StringRef Root, StringRef Ext, StringRef Name,
llvm::SmallVectorImpl<doc::Reference> &Namespaces) {
std::error_code OK;
llvm::SmallString<128> Path;
llvm::sys::path::native(Root, Path);
for (auto R = Namespaces.rbegin(), E = Namespaces.rend(); R != E; ++R)
llvm::sys::path::append(Path, R->Name);
if (CreateDirectory(Path))
return llvm::make_error<llvm::StringError>("Unable to create directory.\n",
llvm::inconvertibleErrorCode());
llvm::sys::path::append(Path, Name + Ext);
return Path;
}
std::string getFormatString(OutputFormatTy Ty) {
switch (Ty) {
case yaml:
return "yaml";
}
llvm_unreachable("Unknown OutputFormatTy");
}
int main(int argc, const char **argv) {
llvm::sys::PrintStackTraceOnErrorSignal(argv[0]);
std::error_code OK;
// Fail early if an invalid format was provided.
std::string Format = getFormatString(FormatEnum);
auto G = doc::findGeneratorByName(Format);
if (!G) {
llvm::errs() << toString(G.takeError()) << "\n";
return 1;
}
auto Exec = clang::tooling::createExecutorFromCommandLineArgs(
argc, argv, ClangDocCategory);
if (!Exec) {
llvm::errs() << toString(Exec.takeError()) << "\n";
return 1;
}
ArgumentsAdjuster ArgAdjuster;
if (!DoxygenOnly)
ArgAdjuster = combineAdjusters(
getInsertArgumentAdjuster("-fparse-all-comments",
tooling::ArgumentInsertPosition::END),
ArgAdjuster);
// Mapping phase
llvm::outs() << "Mapping decls...\n";
clang::doc::ClangDocContext CDCtx = {Exec->get()->getExecutionContext(),
PublicOnly};
auto Err =
Exec->get()->execute(doc::newMapperActionFactory(CDCtx), ArgAdjuster);
if (Err) {
llvm::errs() << toString(std::move(Err)) << "\n";
return 1;
}
if (DumpMapperResult) {
bool Err = false;
Exec->get()->getToolResults()->forEachResult(
[&](StringRef Key, StringRef Value) {
Err = DumpResultToFile("bc", Key + ".bc", Value);
});
if (Err)
llvm::errs() << "Error dumping map results.\n";
return Err;
}
// Collect values into output by key.
llvm::outs() << "Collecting infos...\n";
llvm::StringMap<std::vector<std::unique_ptr<doc::Info>>> MapOutput;
// In ToolResults, the Key is the hashed USR and the value is the
// bitcode-encoded representation of the Info object.
Exec->get()->getToolResults()->forEachResult([&](StringRef Key,
StringRef Value) {
llvm::BitstreamCursor Stream(Value);
doc::ClangDocBitcodeReader Reader(Stream);
auto Infos = Reader.readBitcode();
for (auto &I : Infos) {
auto R =
MapOutput.try_emplace(Key, std::vector<std::unique_ptr<doc::Info>>());
R.first->second.emplace_back(std::move(I));
}
});
// Reducing and generation phases
llvm::outs() << "Reducing " << MapOutput.size() << " infos...\n";
llvm::StringMap<std::unique_ptr<doc::Info>> ReduceOutput;
for (auto &Group : MapOutput) {
auto Reduced = doc::mergeInfos(Group.getValue());
if (!Reduced)
llvm::errs() << llvm::toString(Reduced.takeError());
if (DumpIntermediateResult) {
SmallString<4096> Buffer;
llvm::BitstreamWriter Stream(Buffer);
doc::ClangDocBitcodeWriter Writer(Stream);
Writer.dispatchInfoForWrite(Reduced.get().get());
if (DumpResultToFile("bc", Group.getKey() + ".bc", Buffer))
llvm::errs() << "Error dumping to bitcode.\n";
continue;
}
// Create the relevant ostream and emit the documentation for this decl.
doc::Info *I = Reduced.get().get();
auto InfoPath = getPath(OutDirectory, "." + Format, I->Name, I->Namespace);
if (!InfoPath) {
llvm::errs() << toString(InfoPath.takeError()) << "\n";
continue;
}
std::error_code FileErr;
llvm::raw_fd_ostream InfoOS(InfoPath.get(), FileErr, llvm::sys::fs::F_None);
if (FileErr != OK) {
llvm::errs() << "Error opening index file: " << FileErr.message() << "\n";
continue;
}
if (G->get()->generateDocForInfo(I, InfoOS))
llvm::errs() << "Unable to generate docs for info.\n";
}
return 0;
}

View File

@@ -61,14 +61,6 @@ AST_MATCHER_P(CXXMethodDecl, ofOutermostEnclosingClass,
return InnerMatcher.matches(*Parent, Finder, Builder);
}
std::string CleanPath(StringRef PathRef) {
llvm::SmallString<128> Path(PathRef);
llvm::sys::path::remove_dots(Path, /*remove_dot_dot=*/true);
// FIXME: figure out why this is necessary.
llvm::sys::path::native(Path);
return Path.str();
}
// 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) {
@@ -80,7 +72,9 @@ std::string MakeAbsolutePath(StringRef CurrentDir, StringRef Path) {
llvm::sys::fs::make_absolute(InitialDirectory, AbsolutePath))
llvm::errs() << "Warning: could not make absolute file: '" << EC.message()
<< '\n';
return CleanPath(std::move(AbsolutePath));
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
@@ -101,15 +95,13 @@ std::string MakeAbsolutePath(const SourceManager &SM, StringRef Path) {
llvm::sys::path::parent_path(AbsolutePath.str()));
if (Dir) {
StringRef DirName = SM.getFileManager().getCanonicalName(Dir);
// FIXME: getCanonicalName might fail to get real path on VFS.
if (llvm::sys::path::is_absolute(DirName)) {
SmallString<128> AbsoluteFilename;
llvm::sys::path::append(AbsoluteFilename, DirName,
llvm::sys::path::filename(AbsolutePath.str()));
return CleanPath(AbsoluteFilename);
}
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 CleanPath(AbsolutePath);
return AbsolutePath.str();
}
// Matches AST nodes that are expanded within the given AbsoluteFilePath.
@@ -139,8 +131,7 @@ public:
clang::CharSourceRange FilenameRange,
const clang::FileEntry * /*File*/,
StringRef SearchPath, StringRef /*RelativePath*/,
const clang::Module * /*Imported*/,
SrcMgr::CharacteristicKind /*FileType*/) override {
const clang::Module * /*Imported*/) override {
if (const auto *FileEntry = SM.getFileEntryForID(SM.getFileID(HashLoc)))
MoveTool->addIncludes(FileName, IsAngled, SearchPath,
FileEntry->getName(), FilenameRange, SM);
@@ -289,10 +280,7 @@ SourceLocation
getLocForEndOfDecl(const clang::Decl *D,
const LangOptions &LangOpts = clang::LangOptions()) {
const auto &SM = D->getASTContext().getSourceManager();
// If the expansion range is a character range, this is the location of
// the first character past the end. Otherwise it's the location of the
// first character in the final token in the range.
auto EndExpansionLoc = SM.getExpansionRange(D->getLocEnd()).getEnd();
auto EndExpansionLoc = SM.getExpansionLoc(D->getLocEnd());
std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(EndExpansionLoc);
// Try to load the file buffer.
bool InvalidTemp = false;
@@ -535,7 +523,6 @@ void ClangMoveTool::registerMatchers(ast_matchers::MatchFinder *Finder) {
auto AllDeclsInHeader = namedDecl(
unless(ForwardClassDecls), unless(namespaceDecl()),
unless(usingDirectiveDecl()), // using namespace decl.
notInMacro(),
InOldHeader,
hasParent(decl(anyOf(namespaceDecl(), translationUnitDecl()))),
hasDeclContext(decl(anyOf(namespaceDecl(), translationUnitDecl()))));
@@ -688,8 +675,8 @@ void ClangMoveTool::run(const ast_matchers::MatchFinder::MatchResult &Result) {
Result.Nodes.getNodeAs<clang::NamedDecl>("helper_decls")) {
MovedDecls.push_back(ND);
HelperDeclarations.push_back(ND);
LLVM_DEBUG(llvm::dbgs() << "Add helper : " << ND->getNameAsString() << " ("
<< ND << ")\n");
DEBUG(llvm::dbgs() << "Add helper : "
<< ND->getNameAsString() << " (" << ND << ")\n");
} else if (const auto *UD =
Result.Nodes.getNodeAs<clang::NamedDecl>("using_decl")) {
MovedDecls.push_back(UD);
@@ -707,28 +694,22 @@ void ClangMoveTool::addIncludes(llvm::StringRef IncludeHeader, bool IsAngled,
const SourceManager &SM) {
SmallVector<char, 128> HeaderWithSearchPath;
llvm::sys::path::append(HeaderWithSearchPath, SearchPath, IncludeHeader);
std::string AbsoluteIncludeHeader =
std::string AbsoluteOldHeader = makeAbsolutePath(Context->Spec.OldHeader);
if (AbsoluteOldHeader ==
MakeAbsolutePath(SM, llvm::StringRef(HeaderWithSearchPath.data(),
HeaderWithSearchPath.size()));
HeaderWithSearchPath.size()))) {
OldHeaderIncludeRange = IncludeFilenameRange;
return;
}
std::string IncludeLine =
IsAngled ? ("#include <" + IncludeHeader + ">\n").str()
: ("#include \"" + IncludeHeader + "\"\n").str();
std::string AbsoluteOldHeader = makeAbsolutePath(Context->Spec.OldHeader);
std::string AbsoluteCurrentFile = MakeAbsolutePath(SM, FileName);
if (AbsoluteOldHeader == AbsoluteCurrentFile) {
// Find old.h includes "old.h".
if (AbsoluteOldHeader == AbsoluteIncludeHeader) {
OldHeaderIncludeRangeInHeader = IncludeFilenameRange;
return;
}
HeaderIncludes.push_back(IncludeLine);
} else if (makeAbsolutePath(Context->Spec.OldCC) == AbsoluteCurrentFile) {
// Find old.cc includes "old.h".
if (AbsoluteOldHeader == AbsoluteIncludeHeader) {
OldHeaderIncludeRangeInCC = IncludeFilenameRange;
return;
}
CCIncludes.push_back(IncludeLine);
}
}
@@ -749,12 +730,12 @@ void ClangMoveTool::removeDeclsInOldFiles() {
// We remove the helper declarations which are not used in the old.cc after
// moving the given declarations.
for (const auto *D : HelperDeclarations) {
LLVM_DEBUG(llvm::dbgs() << "Check helper is used: "
<< D->getNameAsString() << " (" << D << ")\n");
DEBUG(llvm::dbgs() << "Check helper is used: "
<< D->getNameAsString() << " (" << D << ")\n");
if (!UsedDecls.count(HelperDeclRGBuilder::getOutmostClassOrFunDecl(
D->getCanonicalDecl()))) {
LLVM_DEBUG(llvm::dbgs() << "Helper removed in old.cc: "
<< D->getNameAsString() << " (" << D << ")\n");
DEBUG(llvm::dbgs() << "Helper removed in old.cc: "
<< D->getNameAsString() << " (" << D << ")\n");
RemovedDecls.push_back(D);
}
}
@@ -834,8 +815,8 @@ void ClangMoveTool::moveDeclsToNewFiles() {
D->getCanonicalDecl())))
continue;
LLVM_DEBUG(llvm::dbgs() << "Helper used in new.cc: " << D->getNameAsString()
<< " " << D << "\n");
DEBUG(llvm::dbgs() << "Helper used in new.cc: " << D->getNameAsString()
<< " " << D << "\n");
ActualNewCCDecls.push_back(D);
}
@@ -876,18 +857,13 @@ void ClangMoveTool::moveAll(SourceManager &SM, StringRef OldFile,
if (!NewFile.empty()) {
auto AllCode = clang::tooling::Replacements(
clang::tooling::Replacement(NewFile, 0, 0, Code));
auto ReplaceOldInclude = [&](clang::CharSourceRange OldHeaderIncludeRange) {
AllCode = AllCode.merge(clang::tooling::Replacements(
clang::tooling::Replacement(SM, OldHeaderIncludeRange,
'"' + Context->Spec.NewHeader + '"')));
};
// Fix the case where old.h/old.cc includes "old.h", we replace the
// `#include "old.h"` with `#include "new.h"`.
if (Context->Spec.NewCC == NewFile && OldHeaderIncludeRangeInCC.isValid())
ReplaceOldInclude(OldHeaderIncludeRangeInCC);
else if (Context->Spec.NewHeader == NewFile &&
OldHeaderIncludeRangeInHeader.isValid())
ReplaceOldInclude(OldHeaderIncludeRangeInHeader);
// 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);
}
}
@@ -918,9 +894,10 @@ void ClangMoveTool::onEndOfTranslationUnit() {
if (RemovedDecls.empty())
return;
// Ignore symbols that are not supported 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.
// 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:
@@ -945,7 +922,7 @@ void ClangMoveTool::onEndOfTranslationUnit() {
moveAll(SM, Context->Spec.OldCC, Context->Spec.NewCC);
return;
}
LLVM_DEBUG(RGBuilder.getGraph()->dump());
DEBUG(RGBuilder.getGraph()->dump());
moveDeclsToNewFiles();
removeDeclsInOldFiles();
}

View File

@@ -176,11 +176,7 @@ private:
/// 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 OldHeaderIncludeRangeInCC;
/// The source range for the written file name in #include (i.e. "old.h" for
/// #include "old.h") in old.h, including the enclosing quotes or angle
/// brackets.
clang::CharSourceRange OldHeaderIncludeRangeInHeader;
clang::CharSourceRange OldHeaderIncludeRange;
/// Mapping from FilePath to FileID, which can be used in post processes like
/// cleanup around replacements.
llvm::StringMap<FileID> FilePathToFileID;

View File

@@ -116,9 +116,9 @@ void HelperDeclRGBuilder::run(
if (const auto *FuncRef = Result.Nodes.getNodeAs<DeclRefExpr>("func_ref")) {
const auto *DC = Result.Nodes.getNodeAs<Decl>("dc");
assert(DC);
LLVM_DEBUG(llvm::dbgs() << "Find helper function usage: "
<< FuncRef->getDecl()->getNameAsString() << " ("
<< FuncRef->getDecl() << ")\n");
DEBUG(llvm::dbgs() << "Find helper function usage: "
<< FuncRef->getDecl()->getNameAsString() << " ("
<< FuncRef->getDecl() << ")\n");
RG->addEdge(
getOutmostClassOrFunDecl(DC->getCanonicalDecl()),
getOutmostClassOrFunDecl(FuncRef->getDecl()->getCanonicalDecl()));
@@ -126,9 +126,9 @@ void HelperDeclRGBuilder::run(
Result.Nodes.getNodeAs<CXXRecordDecl>("used_class")) {
const auto *DC = Result.Nodes.getNodeAs<Decl>("dc");
assert(DC);
LLVM_DEBUG(llvm::dbgs()
<< "Find helper class usage: " << UsedClass->getNameAsString()
<< " (" << UsedClass << ")\n");
DEBUG(llvm::dbgs() << "Find helper class usage: "
<< UsedClass->getNameAsString() << " (" << UsedClass
<< ")\n");
RG->addEdge(getOutmostClassOrFunDecl(DC->getCanonicalDecl()), UsedClass);
}
}

View File

@@ -30,8 +30,8 @@ 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::CD_CreateAlways, llvm::sys::fs::F_Text))
if (std::error_code ec =
llvm::sys::fs::openFileForWrite(path, fd, llvm::sys::fs::F_Text))
return ec;
return llvm::sys::Process::SafelyCloseFileDescriptor(fd);

View File

@@ -50,35 +50,25 @@ StringRef QueryParser::lexWord() {
// This is the StringSwitch-alike used by lexOrCompleteWord below. See that
// function for details.
template <typename T> struct QueryParser::LexOrCompleteWord {
StringRef Word;
StringSwitch<T> Switch;
QueryParser *P;
StringRef Word;
// Set to the completion point offset in Word, or StringRef::npos if
// completion point not in Word.
size_t WordCompletionPos;
// Lexes a word and stores it in Word. Returns a LexOrCompleteWord<T> object
// that can be used like a llvm::StringSwitch<T>, but adds cases as possible
// completions if the lexed word contains the completion point.
LexOrCompleteWord(QueryParser *P, StringRef &OutWord)
: Word(P->lexWord()), Switch(Word), P(P),
WordCompletionPos(StringRef::npos) {
OutWord = Word;
if (P->CompletionPos && P->CompletionPos <= Word.data() + Word.size()) {
if (P->CompletionPos < Word.data())
WordCompletionPos = 0;
else
WordCompletionPos = P->CompletionPos - Word.data();
}
}
LexOrCompleteWord(QueryParser *P, StringRef Word, size_t WCP)
: Switch(Word), P(P), Word(Word), WordCompletionPos(WCP) {}
LexOrCompleteWord &Case(llvm::StringLiteral CaseStr, const T &Value,
template <unsigned N>
LexOrCompleteWord &Case(const char (&S)[N], const T &Value,
bool IsCompletion = true) {
StringRef CaseStr(S, N - 1);
if (WordCompletionPos == StringRef::npos)
Switch.Case(CaseStr, Value);
else if (CaseStr.size() != 0 && IsCompletion && WordCompletionPos <= CaseStr.size() &&
Switch.Case(S, Value);
else if (N != 1 && IsCompletion && WordCompletionPos <= CaseStr.size() &&
CaseStr.substr(0, WordCompletionPos) ==
Word.substr(0, WordCompletionPos))
P->Completions.push_back(LineEditor::Completion(
@@ -86,12 +76,29 @@ template <typename T> struct QueryParser::LexOrCompleteWord {
return *this;
}
T Default(T Value) { 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
// that can be used like a llvm::StringSwitch<T>, but adds cases as possible
// completions if the lexed word contains the completion point.
template <typename T>
QueryParser::LexOrCompleteWord<T>
QueryParser::lexOrCompleteWord(StringRef &Word) {
Word = lexWord();
size_t WordCompletionPos = StringRef::npos;
if (CompletionPos && CompletionPos <= Word.data() + Word.size()) {
if (CompletionPos < Word.data())
WordCompletionPos = 0;
else
WordCompletionPos = CompletionPos - Word.data();
}
return LexOrCompleteWord<T>(this, Word, WordCompletionPos);
}
QueryRef QueryParser::parseSetBool(bool QuerySession::*Var) {
StringRef ValStr;
unsigned Value = LexOrCompleteWord<unsigned>(this, ValStr)
unsigned Value = lexOrCompleteWord<unsigned>(ValStr)
.Case("false", 0)
.Case("true", 1)
.Default(~0u);
@@ -103,7 +110,7 @@ QueryRef QueryParser::parseSetBool(bool QuerySession::*Var) {
QueryRef QueryParser::parseSetOutputKind() {
StringRef ValStr;
unsigned OutKind = LexOrCompleteWord<unsigned>(this, ValStr)
unsigned OutKind = lexOrCompleteWord<unsigned>(ValStr)
.Case("diag", OK_Diag)
.Case("print", OK_Print)
.Case("dump", OK_Dump)
@@ -159,7 +166,7 @@ QueryRef QueryParser::completeMatcherExpression() {
QueryRef QueryParser::doParse() {
StringRef CommandStr;
ParsedQueryKind QKind = LexOrCompleteWord<ParsedQueryKind>(this, CommandStr)
ParsedQueryKind QKind = lexOrCompleteWord<ParsedQueryKind>(CommandStr)
.Case("", PQK_NoOp)
.Case("help", PQK_Help)
.Case("m", PQK_Match, /*IsCompletion=*/false)
@@ -214,8 +221,7 @@ QueryRef QueryParser::doParse() {
case PQK_Set: {
StringRef VarStr;
ParsedQueryVariable Var = LexOrCompleteWord<ParsedQueryVariable>(this,
VarStr)
ParsedQueryVariable Var = lexOrCompleteWord<ParsedQueryVariable>(VarStr)
.Case("output", PQV_Output)
.Case("bind-root", PQV_BindRoot)
.Default(PQV_Invalid);

View File

@@ -42,6 +42,7 @@ private:
StringRef lexWord();
template <typename T> struct LexOrCompleteWord;
template <typename T> LexOrCompleteWord<T> lexOrCompleteWord(StringRef &Str);
QueryRef parseSetBool(bool QuerySession::*Var);
QueryRef parseSetOutputKind();

View File

@@ -114,6 +114,10 @@ Checks:
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:

View File

@@ -4,7 +4,7 @@ LLVM Release License
University of Illinois/NCSA
Open Source License
Copyright (c) 2007-2018 University of Illinois at Urbana-Champaign.
Copyright (c) 2007-2016 University of Illinois at Urbana-Champaign.
All rights reserved.
Developed by:

View File

@@ -7,7 +7,6 @@ add_clang_library(clangTidy
ClangTidyModule.cpp
ClangTidyDiagnosticConsumer.cpp
ClangTidyOptions.cpp
ClangTidyProfiling.cpp
DEPENDS
ClangSACheckers
@@ -28,7 +27,6 @@ add_clang_library(clangTidy
)
add_subdirectory(android)
add_subdirectory(abseil)
add_subdirectory(boost)
add_subdirectory(bugprone)
add_subdirectory(cert)
@@ -43,8 +41,6 @@ add_subdirectory(mpi)
add_subdirectory(objc)
add_subdirectory(performance)
add_subdirectory(plugin)
add_subdirectory(portability)
add_subdirectory(readability)
add_subdirectory(tool)
add_subdirectory(utils)
add_subdirectory(zircon)

View File

@@ -18,7 +18,6 @@
#include "ClangTidy.h"
#include "ClangTidyDiagnosticConsumer.h"
#include "ClangTidyModuleRegistry.h"
#include "ClangTidyProfiling.h"
#include "clang/AST/ASTConsumer.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/Decl.h"
@@ -90,9 +89,8 @@ private:
class ErrorReporter {
public:
ErrorReporter(ClangTidyContext &Context, bool ApplyFixes,
llvm::IntrusiveRefCntPtr<vfs::FileSystem> BaseFS)
: Files(FileSystemOptions(), BaseFS), DiagOpts(new DiagnosticOptions()),
ErrorReporter(ClangTidyContext &Context, bool ApplyFixes)
: Files(FileSystemOptions()), DiagOpts(new DiagnosticOptions()),
DiagPrinter(new TextDiagnosticPrinter(llvm::outs(), &*DiagOpts)),
Diags(IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs), &*DiagOpts,
DiagPrinter),
@@ -268,17 +266,12 @@ private:
class ClangTidyASTConsumer : public MultiplexConsumer {
public:
ClangTidyASTConsumer(std::vector<std::unique_ptr<ASTConsumer>> Consumers,
std::unique_ptr<ClangTidyProfiling> Profiling,
std::unique_ptr<ast_matchers::MatchFinder> Finder,
std::vector<std::unique_ptr<ClangTidyCheck>> Checks)
: MultiplexConsumer(std::move(Consumers)),
Profiling(std::move(Profiling)), Finder(std::move(Finder)),
: MultiplexConsumer(std::move(Consumers)), Finder(std::move(Finder)),
Checks(std::move(Checks)) {}
private:
// Destructor order matters! Profiling must be destructed last.
// Or at least after Finder.
std::unique_ptr<ClangTidyProfiling> Profiling;
std::unique_ptr<ast_matchers::MatchFinder> Finder;
std::vector<std::unique_ptr<ClangTidyCheck>> Checks;
};
@@ -309,12 +302,11 @@ static void setStaticAnalyzerCheckerOpts(const ClangTidyOptions &Opts,
typedef std::vector<std::pair<std::string, bool>> CheckersList;
static CheckersList getCheckersControlList(ClangTidyContext &Context,
bool IncludeExperimental) {
static CheckersList getCheckersControlList(ClangTidyContext &Context) {
CheckersList List;
const auto &RegisteredCheckers =
AnalyzerOptions::getRegisteredCheckers(IncludeExperimental);
AnalyzerOptions::getRegisteredCheckers(/*IncludeExperimental=*/false);
bool AnalyzerChecksEnabled = false;
for (StringRef CheckName : RegisteredCheckers) {
std::string ClangTidyCheckName((AnalyzerCheckNamePrefix + CheckName).str());
@@ -360,13 +352,8 @@ ClangTidyASTConsumerFactory::CreateASTConsumer(
CheckFactories->createChecks(&Context, Checks);
ast_matchers::MatchFinder::MatchFinderOptions FinderOptions;
std::unique_ptr<ClangTidyProfiling> Profiling;
if (Context.getEnableProfiling()) {
Profiling = llvm::make_unique<ClangTidyProfiling>(
Context.getProfileStorageParams());
FinderOptions.CheckProfiling.emplace(Profiling->Records);
}
if (auto *P = Context.getCheckProfileData())
FinderOptions.CheckProfiling.emplace(P->Records);
std::unique_ptr<ast_matchers::MatchFinder> Finder(
new ast_matchers::MatchFinder(std::move(FinderOptions)));
@@ -381,8 +368,12 @@ ClangTidyASTConsumerFactory::CreateASTConsumer(
Consumers.push_back(Finder->newASTConsumer());
AnalyzerOptionsRef AnalyzerOptions = Compiler.getAnalyzerOpts();
AnalyzerOptions->CheckersControlList =
getCheckersControlList(Context, Context.canEnableAnalyzerAlphaCheckers());
// FIXME: Remove this option once clang's cfg-temporary-dtors option defaults
// to true.
AnalyzerOptions->Config["cfg-temporary-dtors"] =
Context.getOptions().AnalyzeTemporaryDtors ? "true" : "false";
AnalyzerOptions->CheckersControlList = getCheckersControlList(Context);
if (!AnalyzerOptions->CheckersControlList.empty()) {
setStaticAnalyzerCheckerOpts(Context.getOptions(), AnalyzerOptions);
AnalyzerOptions->AnalysisStoreOpt = RegionStoreModel;
@@ -396,8 +387,7 @@ ClangTidyASTConsumerFactory::CreateASTConsumer(
Consumers.push_back(std::move(AnalysisConsumer));
}
return llvm::make_unique<ClangTidyASTConsumer>(
std::move(Consumers), std::move(Profiling), std::move(Finder),
std::move(Checks));
std::move(Consumers), std::move(Finder), std::move(Checks));
}
std::vector<std::string> ClangTidyASTConsumerFactory::getCheckNames() {
@@ -407,8 +397,7 @@ std::vector<std::string> ClangTidyASTConsumerFactory::getCheckNames() {
CheckNames.push_back(CheckFactory.first);
}
for (const auto &AnalyzerCheck : getCheckersControlList(
Context, Context.canEnableAnalyzerAlphaCheckers()))
for (const auto &AnalyzerCheck : getCheckersControlList(Context))
CheckNames.push_back(AnalyzerCheckNamePrefix + AnalyzerCheck.first);
std::sort(CheckNames.begin(), CheckNames.end());
@@ -467,35 +456,26 @@ void OptionsView::store(ClangTidyOptions::OptionMap &Options,
store(Options, LocalName, llvm::itostr(Value));
}
std::vector<std::string>
getCheckNames(const ClangTidyOptions &Options,
bool AllowEnablingAnalyzerAlphaCheckers) {
std::vector<std::string> getCheckNames(const ClangTidyOptions &Options) {
clang::tidy::ClangTidyContext Context(
llvm::make_unique<DefaultOptionsProvider>(ClangTidyGlobalOptions(),
Options),
AllowEnablingAnalyzerAlphaCheckers);
Options));
ClangTidyASTConsumerFactory Factory(Context);
return Factory.getCheckNames();
}
ClangTidyOptions::OptionMap
getCheckOptions(const ClangTidyOptions &Options,
bool AllowEnablingAnalyzerAlphaCheckers) {
ClangTidyOptions::OptionMap getCheckOptions(const ClangTidyOptions &Options) {
clang::tidy::ClangTidyContext Context(
llvm::make_unique<DefaultOptionsProvider>(ClangTidyGlobalOptions(),
Options),
AllowEnablingAnalyzerAlphaCheckers);
Options));
ClangTidyASTConsumerFactory Factory(Context);
return Factory.getCheckOptions();
}
void runClangTidy(clang::tidy::ClangTidyContext &Context,
const CompilationDatabase &Compilations,
ArrayRef<std::string> InputFiles,
llvm::IntrusiveRefCntPtr<vfs::FileSystem> BaseFS,
bool EnableCheckProfile, llvm::StringRef StoreCheckProfile) {
ClangTool Tool(Compilations, InputFiles,
std::make_shared<PCHContainerOperations>(), BaseFS);
ArrayRef<std::string> InputFiles, ProfileData *Profile) {
ClangTool Tool(Compilations, InputFiles);
// Add extra arguments passed by the clang-tidy command-line.
ArgumentsAdjuster PerFileExtraArgumentsInserter =
@@ -533,8 +513,8 @@ void runClangTidy(clang::tidy::ClangTidyContext &Context,
Tool.appendArgumentsAdjuster(PerFileExtraArgumentsInserter);
Tool.appendArgumentsAdjuster(PluginArgumentsRemover);
Context.setEnableProfiling(EnableCheckProfile);
Context.setProfileStoragePrefix(StoreCheckProfile);
if (Profile)
Context.setCheckProfileData(Profile);
ClangTidyDiagnosticConsumer DiagConsumer(Context);
@@ -545,18 +525,6 @@ void runClangTidy(clang::tidy::ClangTidyContext &Context,
ActionFactory(ClangTidyContext &Context) : ConsumerFactory(Context) {}
FrontendAction *create() override { return new Action(&ConsumerFactory); }
bool runInvocation(std::shared_ptr<CompilerInvocation> Invocation,
FileManager *Files,
std::shared_ptr<PCHContainerOperations> PCHContainerOps,
DiagnosticConsumer *DiagConsumer) override {
// Explicitly set ProgramAction to RunAnalysis to make the preprocessor
// define __clang_analyzer__ macro. The frontend analyzer action will not
// be called here.
Invocation->getFrontendOpts().ProgramAction = frontend::RunAnalysis;
return FrontendActionFactory::runInvocation(
Invocation, Files, PCHContainerOps, DiagConsumer);
}
private:
class Action : public ASTFrontendAction {
public:
@@ -578,9 +546,8 @@ void runClangTidy(clang::tidy::ClangTidyContext &Context,
}
void handleErrors(ClangTidyContext &Context, bool Fix,
unsigned &WarningsAsErrorsCount,
llvm::IntrusiveRefCntPtr<vfs::FileSystem> BaseFS) {
ErrorReporter Reporter(Context, Fix, BaseFS);
unsigned &WarningsAsErrorsCount) {
ErrorReporter Reporter(Context, Fix);
vfs::FileSystem &FileSystem =
*Reporter.getSourceManager().getFileManager().getVirtualFileSystem();
auto InitialWorkingDir = FileSystem.getCurrentWorkingDirectory();

View File

@@ -210,8 +210,7 @@ private:
/// \brief Fills the list of check names that are enabled when the provided
/// filters are applied.
std::vector<std::string> getCheckNames(const ClangTidyOptions &Options,
bool AllowEnablingAnalyzerAlphaCheckers);
std::vector<std::string> getCheckNames(const ClangTidyOptions &Options);
/// \brief Returns the effective check-specific options.
///
@@ -219,23 +218,16 @@ std::vector<std::string> getCheckNames(const ClangTidyOptions &Options,
/// effective options from all created checks. The returned set of options
/// includes default check-specific options for all keys not overridden by \p
/// Options.
ClangTidyOptions::OptionMap
getCheckOptions(const ClangTidyOptions &Options,
bool AllowEnablingAnalyzerAlphaCheckers);
ClangTidyOptions::OptionMap getCheckOptions(const ClangTidyOptions &Options);
/// \brief Run a set of clang-tidy checks on a set of files.
///
/// \param EnableCheckProfile If provided, it enables check profile collection
/// in MatchFinder, and will contain the result of the profile.
/// \param StoreCheckProfile If provided, and EnableCheckProfile is true,
/// the profile will not be output to stderr, but will instead be stored
/// as a JSON file in the specified directory.
/// \param Profile if provided, it enables check profile collection in
/// MatchFinder, and will contain the result of the profile.
void runClangTidy(clang::tidy::ClangTidyContext &Context,
const tooling::CompilationDatabase &Compilations,
ArrayRef<std::string> InputFiles,
llvm::IntrusiveRefCntPtr<vfs::FileSystem> BaseFS,
bool EnableCheckProfile = false,
llvm::StringRef StoreCheckProfile = StringRef());
ProfileData *Profile = nullptr);
// FIXME: This interface will need to be significantly extended to be useful.
// FIXME: Implement confidence levels for displaying/fixing errors.
@@ -244,8 +236,7 @@ void runClangTidy(clang::tidy::ClangTidyContext &Context,
/// Errors containing fixes are automatically applied and reformatted. If no
/// clang-format configuration file is found, the given \P FormatStyle is used.
void handleErrors(ClangTidyContext &Context, bool Fix,
unsigned &WarningsAsErrorsCount,
llvm::IntrusiveRefCntPtr<vfs::FileSystem> BaseFS);
unsigned &WarningsAsErrorsCount);
/// \brief Serializes replacements into YAML and writes them to the specified
/// output stream.

View File

@@ -177,11 +177,9 @@ private:
};
ClangTidyContext::ClangTidyContext(
std::unique_ptr<ClangTidyOptionsProvider> OptionsProvider,
bool AllowEnablingAnalyzerAlphaCheckers)
std::unique_ptr<ClangTidyOptionsProvider> OptionsProvider)
: DiagEngine(nullptr), OptionsProvider(std::move(OptionsProvider)),
Profile(false),
AllowEnablingAnalyzerAlphaCheckers(AllowEnablingAnalyzerAlphaCheckers) {
Profile(nullptr) {
// Before the first translation unit we can get errors related to command-line
// parsing, use empty string for the file name in this case.
setCurrentFile("");
@@ -235,19 +233,7 @@ ClangTidyOptions ClangTidyContext::getOptionsForFile(StringRef File) const {
OptionsProvider->getOptions(File));
}
void ClangTidyContext::setEnableProfiling(bool P) { Profile = P; }
void ClangTidyContext::setProfileStoragePrefix(StringRef Prefix) {
ProfilePrefix = Prefix;
}
llvm::Optional<ClangTidyProfiling::StorageParams>
ClangTidyContext::getProfileStorageParams() const {
if (ProfilePrefix.empty())
return llvm::None;
return ClangTidyProfiling::StorageParams(ProfilePrefix, CurrentFile);
}
void ClangTidyContext::setCheckProfileData(ProfileData *P) { Profile = P; }
bool ClangTidyContext::isCheckEnabled(StringRef CheckName) const {
assert(CheckFilter != nullptr);
@@ -388,7 +374,7 @@ static bool LineIsMarkedWithNOLINTinMacro(SourceManager &SM, SourceLocation Loc,
return true;
if (!Loc.isMacroID())
return false;
Loc = SM.getImmediateExpansionRange(Loc).getBegin();
Loc = SM.getImmediateExpansionRange(Loc).first;
}
return false;
}

View File

@@ -11,7 +11,6 @@
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CLANGTIDYDIAGNOSTICCONSUMER_H
#include "ClangTidyOptions.h"
#include "ClangTidyProfiling.h"
#include "clang/Basic/Diagnostic.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Tooling/Core/Diagnostic.h"
@@ -88,6 +87,11 @@ struct ClangTidyStats {
}
};
/// \brief Container for clang-tidy profiling data.
struct ProfileData {
llvm::StringMap<llvm::TimeRecord> Records;
};
/// \brief Every \c ClangTidyCheck reports errors through a \c DiagnosticsEngine
/// provided by this context.
///
@@ -100,8 +104,7 @@ struct ClangTidyStats {
class ClangTidyContext {
public:
/// \brief Initializes \c ClangTidyContext instance.
ClangTidyContext(std::unique_ptr<ClangTidyOptionsProvider> OptionsProvider,
bool AllowEnablingAnalyzerAlphaCheckers = false);
ClangTidyContext(std::unique_ptr<ClangTidyOptionsProvider> OptionsProvider);
~ClangTidyContext();
@@ -166,14 +169,12 @@ public:
/// \brief Clears collected errors.
void clearErrors() { Errors.clear(); }
/// \brief Control profile collection in clang-tidy.
void setEnableProfiling(bool Profile);
bool getEnableProfiling() const { return Profile; }
/// \brief Control storage of profile date.
void setProfileStoragePrefix(StringRef ProfilePrefix);
llvm::Optional<ClangTidyProfiling::StorageParams>
getProfileStorageParams() const;
/// \brief Set the output struct for profile data.
///
/// Setting a non-null pointer here will enable profile collection in
/// clang-tidy.
void setCheckProfileData(ProfileData *Profile);
ProfileData *getCheckProfileData() const { return Profile; }
/// \brief Should be called when starting to process new translation unit.
void setCurrentBuildDirectory(StringRef BuildDirectory) {
@@ -185,12 +186,6 @@ public:
return CurrentBuildDirectory;
}
/// \brief If the experimental alpha checkers from the static analyzer can be
/// enabled.
bool canEnableAnalyzerAlphaCheckers() const {
return AllowEnablingAnalyzerAlphaCheckers;
}
private:
// Calls setDiagnosticsEngine() and storeError().
friend class ClangTidyDiagnosticConsumer;
@@ -221,10 +216,7 @@ private:
llvm::DenseMap<unsigned, std::string> CheckNamesByDiagnosticID;
bool Profile;
std::string ProfilePrefix;
bool AllowEnablingAnalyzerAlphaCheckers;
ProfileData *Profile;
};
/// \brief A diagnostic consumer that turns each \c Diagnostic into a

View File

@@ -83,11 +83,10 @@ template <> struct MappingTraits<ClangTidyOptions> {
static void mapping(IO &IO, ClangTidyOptions &Options) {
MappingNormalization<NOptionMap, ClangTidyOptions::OptionMap> NOpts(
IO, Options.CheckOptions);
bool Ignored = false;
IO.mapOptional("Checks", Options.Checks);
IO.mapOptional("WarningsAsErrors", Options.WarningsAsErrors);
IO.mapOptional("HeaderFilterRegex", Options.HeaderFilterRegex);
IO.mapOptional("AnalyzeTemporaryDtors", Ignored); // legacy compatibility
IO.mapOptional("AnalyzeTemporaryDtors", Options.AnalyzeTemporaryDtors);
IO.mapOptional("FormatStyle", Options.FormatStyle);
IO.mapOptional("User", Options.User);
IO.mapOptional("CheckOptions", NOpts->Options);
@@ -108,6 +107,7 @@ ClangTidyOptions ClangTidyOptions::getDefaults() {
Options.WarningsAsErrors = "";
Options.HeaderFilterRegex = "";
Options.SystemHeaders = false;
Options.AnalyzeTemporaryDtors = false;
Options.FormatStyle = "none";
Options.User = llvm::None;
for (ClangTidyModuleRegistry::iterator I = ClangTidyModuleRegistry::begin(),
@@ -147,6 +147,7 @@ ClangTidyOptions::mergeWith(const ClangTidyOptions &Other) const {
mergeCommaSeparatedLists(Result.WarningsAsErrors, Other.WarningsAsErrors);
overrideValue(Result.HeaderFilterRegex, Other.HeaderFilterRegex);
overrideValue(Result.SystemHeaders, Other.SystemHeaders);
overrideValue(Result.AnalyzeTemporaryDtors, Other.AnalyzeTemporaryDtors);
overrideValue(Result.FormatStyle, Other.FormatStyle);
overrideValue(Result.User, Other.User);
mergeVectors(Result.ExtraArgs, Other.ExtraArgs);
@@ -203,12 +204,9 @@ ConfigOptionsProvider::getRawOptions(llvm::StringRef FileName) {
FileOptionsProvider::FileOptionsProvider(
const ClangTidyGlobalOptions &GlobalOptions,
const ClangTidyOptions &DefaultOptions,
const ClangTidyOptions &OverrideOptions,
llvm::IntrusiveRefCntPtr<vfs::FileSystem> VFS)
const ClangTidyOptions &OverrideOptions)
: DefaultOptionsProvider(GlobalOptions, DefaultOptions),
OverrideOptions(OverrideOptions), FS(std::move(VFS)) {
if (!FS)
FS = vfs::getRealFileSystem();
OverrideOptions(OverrideOptions) {
ConfigHandlers.emplace_back(".clang-tidy", parseConfiguration);
}
@@ -225,22 +223,15 @@ FileOptionsProvider::FileOptionsProvider(
// similar.
std::vector<OptionsSource>
FileOptionsProvider::getRawOptions(StringRef FileName) {
LLVM_DEBUG(llvm::dbgs() << "Getting options for file " << FileName
<< "...\n");
assert(FS && "FS must be set.");
llvm::SmallString<128> AbsoluteFilePath(FileName);
if (FS->makeAbsolute(AbsoluteFilePath))
return {};
DEBUG(llvm::dbgs() << "Getting options for file " << FileName << "...\n");
std::vector<OptionsSource> RawOptions =
DefaultOptionsProvider::getRawOptions(AbsoluteFilePath.str());
DefaultOptionsProvider::getRawOptions(FileName);
OptionsSource CommandLineOptions(OverrideOptions,
OptionsSourceTypeCheckCommandLineOption);
// Look for a suitable configuration file in all parent directories of the
// file. Start with the immediate parent directory and move up.
StringRef Path = llvm::sys::path::parent_path(AbsoluteFilePath.str());
StringRef Path = llvm::sys::path::parent_path(FileName);
for (StringRef CurrentPath = Path; !CurrentPath.empty();
CurrentPath = llvm::sys::path::parent_path(CurrentPath)) {
llvm::Optional<OptionsSource> Result;
@@ -255,8 +246,8 @@ FileOptionsProvider::getRawOptions(StringRef FileName) {
if (Result) {
// Store cached value for all intermediate directories.
while (Path != CurrentPath) {
LLVM_DEBUG(llvm::dbgs()
<< "Caching configuration for path " << Path << ".\n");
DEBUG(llvm::dbgs() << "Caching configuration for path " << Path
<< ".\n");
CachedOptions[Path] = *Result;
Path = llvm::sys::path::parent_path(Path);
}
@@ -283,7 +274,7 @@ FileOptionsProvider::tryReadConfigFile(StringRef Directory) {
for (const ConfigFileHandler &ConfigHandler : ConfigHandlers) {
SmallString<128> ConfigFile(Directory);
llvm::sys::path::append(ConfigFile, ConfigHandler.first);
LLVM_DEBUG(llvm::dbgs() << "Trying " << ConfigFile << "...\n");
DEBUG(llvm::dbgs() << "Trying " << ConfigFile << "...\n");
bool IsFile = false;
// Ignore errors from is_regular_file: we only need to know if we can read

View File

@@ -13,9 +13,7 @@
#include "llvm/ADT/Optional.h"
#include "llvm/ADT/StringMap.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/IntrusiveRefCntPtr.h"
#include "llvm/Support/ErrorOr.h"
#include "clang/Basic/VirtualFileSystem.h"
#include <functional>
#include <map>
#include <string>
@@ -74,6 +72,9 @@ struct ClangTidyOptions {
/// \brief Output warnings from system headers matching \c HeaderFilterRegex.
llvm::Optional<bool> SystemHeaders;
/// \brief Turns on temporary destructor-based analysis.
llvm::Optional<bool> AnalyzeTemporaryDtors;
/// \brief Format code around applied fixes with clang-format using this
/// style.
///
@@ -220,8 +221,7 @@ public:
/// whatever options are read from the configuration file.
FileOptionsProvider(const ClangTidyGlobalOptions &GlobalOptions,
const ClangTidyOptions &DefaultOptions,
const ClangTidyOptions &OverrideOptions,
llvm::IntrusiveRefCntPtr<vfs::FileSystem> FS = nullptr);
const ClangTidyOptions &OverrideOptions);
/// \brief Initializes the \c FileOptionsProvider instance with a custom set
/// of configuration file handlers.
@@ -255,7 +255,6 @@ protected:
llvm::StringMap<OptionsSource> CachedOptions;
ClangTidyOptions OverrideOptions;
ConfigFileHandlers ConfigHandlers;
llvm::IntrusiveRefCntPtr<vfs::FileSystem> FS;
};
/// \brief Parses LineFilter from JSON and stores it to the \p Options.

View File

@@ -1,93 +0,0 @@
//===--- ClangTidyProfiling.cpp - clang-tidy --------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "ClangTidyProfiling.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/YAMLTraits.h"
#include "llvm/Support/raw_ostream.h"
#include <system_error>
#include <utility>
#define DEBUG_TYPE "clang-tidy-profiling"
namespace clang {
namespace tidy {
ClangTidyProfiling::StorageParams::StorageParams(llvm::StringRef ProfilePrefix,
llvm::StringRef SourceFile)
: Timestamp(std::chrono::system_clock::now()), SourceFilename(SourceFile) {
llvm::SmallString<32> TimestampStr;
llvm::raw_svector_ostream OS(TimestampStr);
llvm::format_provider<decltype(Timestamp)>::format(Timestamp, OS,
"%Y%m%d%H%M%S%N");
llvm::SmallString<256> FinalPrefix(ProfilePrefix);
llvm::sys::path::append(FinalPrefix, TimestampStr);
// So the full output name is: /ProfilePrefix/timestamp-inputfilename.json
StoreFilename = llvm::Twine(FinalPrefix + "-" +
llvm::sys::path::filename(SourceFile) + ".json")
.str();
}
void ClangTidyProfiling::printUserFriendlyTable(llvm::raw_ostream &OS) {
TG->print(OS);
OS.flush();
}
void ClangTidyProfiling::printAsJSON(llvm::raw_ostream &OS) {
OS << "{\n";
OS << "\"file\": \"" << Storage->SourceFilename << "\",\n";
OS << "\"timestamp\": \"" << Storage->Timestamp << "\",\n";
OS << "\"profile\": {\n";
TG->printJSONValues(OS, "");
OS << "\n}\n";
OS << "}\n";
OS.flush();
}
void ClangTidyProfiling::storeProfileData() {
assert(Storage.hasValue() && "We should have a filename.");
llvm::SmallString<256> OutputDirectory(Storage->StoreFilename);
llvm::sys::path::remove_filename(OutputDirectory);
if (std::error_code EC = llvm::sys::fs::create_directories(OutputDirectory)) {
llvm::errs() << "Unable to create output directory '" << OutputDirectory
<< "': " << EC.message() << "\n";
return;
}
std::error_code EC;
llvm::raw_fd_ostream OS(Storage->StoreFilename, EC, llvm::sys::fs::F_None);
if (EC) {
llvm::errs() << "Error opening output file '" << Storage->StoreFilename
<< "': " << EC.message() << "\n";
return;
}
printAsJSON(OS);
}
ClangTidyProfiling::ClangTidyProfiling(llvm::Optional<StorageParams> Storage)
: Storage(std::move(Storage)) {}
ClangTidyProfiling::~ClangTidyProfiling() {
TG.emplace("clang-tidy", "clang-tidy checks profiling", Records);
if (!Storage.hasValue())
printUserFriendlyTable(llvm::errs());
else
storeProfileData();
}
} // namespace tidy
} // namespace clang

View File

@@ -1,60 +0,0 @@
//===--- ClangTidyProfiling.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_CLANGTIDYPROFILING_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CLANGTIDYPROFILING_H
#include "llvm/ADT/Optional.h"
#include "llvm/ADT/StringMap.h"
#include "llvm/Support/Chrono.h"
#include "llvm/Support/Timer.h"
#include "llvm/Support/raw_ostream.h"
#include <string>
#include <utility>
#include <vector>
namespace clang {
namespace tidy {
class ClangTidyProfiling {
public:
struct StorageParams {
llvm::sys::TimePoint<> Timestamp;
std::string SourceFilename;
std::string StoreFilename;
StorageParams() = default;
StorageParams(llvm::StringRef ProfilePrefix, llvm::StringRef SourceFile);
};
private:
llvm::Optional<llvm::TimerGroup> TG;
llvm::Optional<StorageParams> Storage;
void printUserFriendlyTable(llvm::raw_ostream &OS);
void printAsJSON(llvm::raw_ostream &OS);
void storeProfileData();
public:
llvm::StringMap<llvm::TimeRecord> Records;
ClangTidyProfiling() = default;
ClangTidyProfiling(llvm::Optional<StorageParams> Storage);
~ClangTidyProfiling();
};
} // end namespace tidy
} // end namespace clang
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CLANGTIDYPROFILING_H

View File

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

View File

@@ -1,14 +0,0 @@
set(LLVM_LINK_COMPONENTS support)
add_clang_library(clangTidyAbseilModule
AbseilTidyModule.cpp
StringFindStartswithCheck.cpp
LINK_LIBS
clangAST
clangASTMatchers
clangBasic
clangLex
clangTidy
clangTidyUtils
)

View File

@@ -1,136 +0,0 @@
//===--- StringFindStartswithCheck.cc - clang-tidy---------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "StringFindStartswithCheck.h"
#include "../utils/OptionsUtils.h"
#include "clang/AST/ASTContext.h"
#include "clang/ASTMatchers/ASTMatchers.h"
#include "clang/Frontend/CompilerInstance.h"
#include <cassert>
using namespace clang::ast_matchers;
namespace clang {
namespace tidy {
namespace abseil {
StringFindStartswithCheck::StringFindStartswithCheck(StringRef Name,
ClangTidyContext *Context)
: ClangTidyCheck(Name, Context),
StringLikeClasses(utils::options::parseStringList(
Options.get("StringLikeClasses", "::std::basic_string"))),
IncludeStyle(utils::IncludeSorter::parseIncludeStyle(
Options.getLocalOrGlobal("IncludeStyle", "llvm"))),
AbseilStringsMatchHeader(
Options.get("AbseilStringsMatchHeader", "absl/strings/match.h")) {}
void StringFindStartswithCheck::registerMatchers(MatchFinder *Finder) {
auto ZeroLiteral = integerLiteral(equals(0));
auto StringClassMatcher = cxxRecordDecl(hasAnyName(SmallVector<StringRef, 4>(
StringLikeClasses.begin(), StringLikeClasses.end())));
auto StringType = hasUnqualifiedDesugaredType(
recordType(hasDeclaration(StringClassMatcher)));
auto StringFind = cxxMemberCallExpr(
// .find()-call on a string...
callee(cxxMethodDecl(hasName("find"))),
on(hasType(StringType)),
// ... with some search expression ...
hasArgument(0, expr().bind("needle")),
// ... and either "0" as second argument or the default argument (also 0).
anyOf(hasArgument(1, ZeroLiteral), hasArgument(1, cxxDefaultArgExpr())));
Finder->addMatcher(
// Match [=!]= with a zero on one side and a string.find on the other.
binaryOperator(
anyOf(hasOperatorName("=="), hasOperatorName("!=")),
hasEitherOperand(ignoringParenImpCasts(ZeroLiteral)),
hasEitherOperand(ignoringParenImpCasts(StringFind.bind("findexpr"))))
.bind("expr"),
this);
}
void StringFindStartswithCheck::check(const MatchFinder::MatchResult &Result) {
const ASTContext &Context = *Result.Context;
const SourceManager &Source = Context.getSourceManager();
// Extract matching (sub)expressions
const auto *ComparisonExpr = Result.Nodes.getNodeAs<BinaryOperator>("expr");
assert(ComparisonExpr != nullptr);
const auto *Needle = Result.Nodes.getNodeAs<Expr>("needle");
assert(Needle != nullptr);
const Expr *Haystack = Result.Nodes.getNodeAs<CXXMemberCallExpr>("findexpr")
->getImplicitObjectArgument();
assert(Haystack != nullptr);
if (ComparisonExpr->getLocStart().isMacroID())
return;
// Get the source code blocks (as characters) for both the string object
// and the search expression
const StringRef NeedleExprCode = Lexer::getSourceText(
CharSourceRange::getTokenRange(Needle->getSourceRange()), Source,
Context.getLangOpts());
const StringRef HaystackExprCode = Lexer::getSourceText(
CharSourceRange::getTokenRange(Haystack->getSourceRange()), Source,
Context.getLangOpts());
// Create the StartsWith string, negating if comparison was "!=".
bool Neg = ComparisonExpr->getOpcodeStr() == "!=";
StringRef StartswithStr;
if (Neg) {
StartswithStr = "!absl::StartsWith";
} else {
StartswithStr = "absl::StartsWith";
}
// Create the warning message and a FixIt hint replacing the original expr.
auto Diagnostic =
diag(ComparisonExpr->getLocStart(),
(StringRef("use ") + StartswithStr + " instead of find() " +
ComparisonExpr->getOpcodeStr() + " 0")
.str());
Diagnostic << FixItHint::CreateReplacement(
ComparisonExpr->getSourceRange(),
(StartswithStr + "(" + HaystackExprCode + ", " + NeedleExprCode + ")")
.str());
// Create a preprocessor #include FixIt hint (CreateIncludeInsertion checks
// whether this already exists).
auto IncludeHint = IncludeInserter->CreateIncludeInsertion(
Source.getFileID(ComparisonExpr->getLocStart()), AbseilStringsMatchHeader,
false);
if (IncludeHint) {
Diagnostic << *IncludeHint;
}
}
void StringFindStartswithCheck::registerPPCallbacks(
CompilerInstance &Compiler) {
IncludeInserter = llvm::make_unique<clang::tidy::utils::IncludeInserter>(
Compiler.getSourceManager(), Compiler.getLangOpts(), IncludeStyle);
Compiler.getPreprocessor().addPPCallbacks(
IncludeInserter->CreatePPCallbacks());
}
void StringFindStartswithCheck::storeOptions(
ClangTidyOptions::OptionMap &Opts) {
Options.store(Opts, "StringLikeClasses",
utils::options::serializeStringList(StringLikeClasses));
Options.store(Opts, "IncludeStyle",
utils::IncludeSorter::toString(IncludeStyle));
Options.store(Opts, "AbseilStringsMatchHeader", AbseilStringsMatchHeader);
}
} // namespace abseil
} // namespace tidy
} // namespace clang

View File

@@ -1,48 +0,0 @@
//===--- StringFindStartswithCheck.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_ABSEIL_STRINGFINDSTARTSWITHCHECK_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_ABSEIL_STRINGFINDSTARTSWITHCHECK_H
#include "../ClangTidy.h"
#include "../utils/IncludeInserter.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include <memory>
#include <string>
#include <vector>
namespace clang {
namespace tidy {
namespace abseil {
// Find string.find(...) == 0 comparisons and suggest replacing with StartsWith.
// FIXME(niko): Add similar check for EndsWith
// FIXME(niko): Add equivalent modernize checks for C++20's std::starts_With
class StringFindStartswithCheck : public ClangTidyCheck {
public:
using ClangTidyCheck::ClangTidyCheck;
StringFindStartswithCheck(StringRef Name, ClangTidyContext *Context);
void registerPPCallbacks(CompilerInstance &Compiler) override;
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
private:
std::unique_ptr<clang::tidy::utils::IncludeInserter> IncludeInserter;
const std::vector<std::string> StringLikeClasses;
const utils::IncludeSorter::IncludeStyle IncludeStyle;
const std::string AbseilStringsMatchHeader;
};
} // namespace abseil
} // namespace tidy
} // namespace clang
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_ABSEIL_STRINGFINDSTARTSWITHCHECK_H

View File

@@ -9,13 +9,11 @@
#
#===------------------------------------------------------------------------===#
from __future__ import print_function
import argparse
import os
import re
import sys
# Adapts the module's CMakelist file. Returns 'True' if it could add a new entry
# and 'False' if the entry already existed.
def adapt_cmake(module_path, check_name_camel):
@@ -31,7 +29,7 @@ def adapt_cmake(module_path, check_name_camel):
return False
print('Updating %s...' % filename)
with open(filename, 'w') as f:
with open(filename, 'wb') as f:
cpp_found = False
file_added = False
for line in lines:
@@ -51,7 +49,7 @@ def write_header(module_path, module, check_name, check_name_camel):
check_name_dashes = module + '-' + check_name
filename = os.path.join(module_path, check_name_camel) + '.h'
print('Creating %s...' % filename)
with open(filename, 'w') as f:
with open(filename, 'wb') as f:
header_guard = ('LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_' + module.upper() + '_'
+ check_name_camel.upper() + '_H')
f.write('//===--- ')
@@ -104,7 +102,7 @@ public:
def write_implementation(module_path, module, check_name_camel):
filename = os.path.join(module_path, check_name_camel) + '.cpp'
print('Creating %s...' % filename)
with open(filename, 'w') as f:
with open(filename, 'wb') as f:
f.write('//===--- ')
f.write(os.path.basename(filename))
f.write(' - clang-tidy')
@@ -153,15 +151,14 @@ void %(check_name)s::check(const MatchFinder::MatchResult &Result) {
# Modifies the module to include the new check.
def adapt_module(module_path, module, check_name, check_name_camel):
modulecpp = list(filter(
lambda p: p.lower() == module.lower() + 'tidymodule.cpp',
os.listdir(module_path)))[0]
modulecpp = filter(lambda p: p.lower() == module.lower() + 'tidymodule.cpp',
os.listdir(module_path))[0]
filename = os.path.join(module_path, modulecpp)
with open(filename, 'r') as f:
lines = f.readlines()
print('Updating %s...' % filename)
with open(filename, 'w') as f:
with open(filename, 'wb') as f:
header_added = False
header_found = False
check_added = False
@@ -201,7 +198,7 @@ def add_release_notes(module_path, module, check_name):
lines = f.readlines()
print('Updating %s...' % filename)
with open(filename, 'w') as f:
with open(filename, 'wb') as f:
note_added = False
header_found = False
@@ -213,8 +210,8 @@ def add_release_notes(module_path, module, check_name):
elif header_found:
if not line.startswith('----'):
f.write("""
- New :doc:`%s
<clang-tidy/checks/%s>` check.
- New `%s
<http://clang.llvm.org/extra/clang-tidy/checks/%s.html>`_ check
FIXME: add release notes.
""" % (check_name_dashes, check_name_dashes))
@@ -224,12 +221,12 @@ def add_release_notes(module_path, module, check_name):
# Adds a test for the check.
def write_test(module_path, module, check_name, test_extension):
def write_test(module_path, module, check_name):
check_name_dashes = module + '-' + check_name
filename = os.path.normpath(os.path.join(module_path, '../../test/clang-tidy',
check_name_dashes + '.' + test_extension))
check_name_dashes + '.cpp'))
print('Creating %s...' % filename)
with open(filename, 'w') as f:
with open(filename, 'wb') as f:
f.write("""// RUN: %%check_clang_tidy %%s %(check_name_dashes)s %%t
// FIXME: Add something that triggers the check here.
@@ -253,8 +250,8 @@ def update_checks_list(clang_tidy_path):
filename = os.path.normpath(os.path.join(docs_dir, 'list.rst'))
with open(filename, 'r') as f:
lines = f.readlines()
doc_files = list(filter(lambda s: s.endswith('.rst') and s != 'list.rst',
os.listdir(docs_dir)))
doc_files = filter(lambda s: s.endswith('.rst') and s != 'list.rst',
os.listdir(docs_dir))
doc_files.sort()
def format_link(doc_file):
@@ -277,7 +274,7 @@ def update_checks_list(clang_tidy_path):
checks = map(format_link, doc_files)
print('Updating %s...' % filename)
with open(filename, 'w') as f:
with open(filename, 'wb') as f:
for line in lines:
f.write(line)
if line.startswith('.. toctree::'):
@@ -291,7 +288,7 @@ def write_docs(module_path, module, check_name):
filename = os.path.normpath(os.path.join(
module_path, '../../docs/clang-tidy/checks/', check_name_dashes + '.rst'))
print('Creating %s...' % filename)
with open(filename, 'w') as f:
with open(filename, 'wb') as f:
f.write(""".. title:: clang-tidy - %(check_name_dashes)s
%(check_name_dashes)s
@@ -303,48 +300,26 @@ FIXME: Describe what patterns does the check detect and why. Give examples.
def main():
language_to_extension = {
'c': 'c',
'c++': 'cpp',
'objc': 'm',
'objc++': 'mm',
}
parser = argparse.ArgumentParser()
parser.add_argument(
'--update-docs',
action='store_true',
help='just update the list of documentation files, then exit')
parser.add_argument(
'--language',
help='language to use for new check (defaults to c++)',
choices=language_to_extension.keys(),
default='c++',
metavar='LANG')
parser.add_argument(
'module',
nargs='?',
help='module directory under which to place the new tidy check (e.g., misc)')
parser.add_argument(
'check',
nargs='?',
help='name of new tidy check to add (e.g. foo-do-the-stuff)')
args = parser.parse_args()
if args.update_docs:
if len(sys.argv) == 2 and sys.argv[1] == '--update-docs':
update_checks_list(os.path.dirname(sys.argv[0]))
return
if not args.module or not args.check:
print('Module and check must be specified.')
parser.print_usage()
if len(sys.argv) != 3:
print """\
Usage: add_new_check.py <module> <check>, e.g.
add_new_check.py misc awesome-functions
Alternatively, run 'add_new_check.py --update-docs' to just update the list of
documentation files."""
return
module = args.module
check_name = args.check
module = sys.argv[1]
check_name = sys.argv[2]
if check_name.startswith(module):
print('Check name "%s" must not start with the module "%s". Exiting.' % (
check_name, module))
print 'Check name "%s" must not start with the module "%s". Exiting.' % (
check_name, module)
return
check_name_camel = ''.join(map(lambda elem: elem.capitalize(),
check_name.split('-'))) + 'Check'
@@ -357,8 +332,7 @@ def main():
write_implementation(module_path, module, check_name_camel)
adapt_module(module_path, module, check_name, check_name_camel)
add_release_notes(module_path, module, check_name)
test_extension = language_to_extension.get(args.language)
write_test(module_path, module, check_name, test_extension)
write_test(module_path, module, check_name)
write_docs(module_path, module, check_name)
update_checks_list(clang_tidy_path)
print('Done. Now it\'s your turn!')

View File

@@ -13,16 +13,15 @@
#include "CloexecAccept4Check.h"
#include "CloexecAcceptCheck.h"
#include "CloexecCreatCheck.h"
#include "CloexecDupCheck.h"
#include "CloexecEpollCreate1Check.h"
#include "CloexecEpollCreateCheck.h"
#include "CloexecDupCheck.h"
#include "CloexecFopenCheck.h"
#include "CloexecInotifyInit1Check.h"
#include "CloexecInotifyInitCheck.h"
#include "CloexecMemfdCreateCheck.h"
#include "CloexecOpenCheck.h"
#include "CloexecSocketCheck.h"
#include "ComparisonInTempFailureRetryCheck.h"
using namespace clang::ast_matchers;
@@ -51,8 +50,6 @@ public:
"android-cloexec-memfd-create");
CheckFactories.registerCheck<CloexecOpenCheck>("android-cloexec-open");
CheckFactories.registerCheck<CloexecSocketCheck>("android-cloexec-socket");
CheckFactories.registerCheck<ComparisonInTempFailureRetryCheck>(
"android-comparison-in-temp-failure-retry");
}
};

View File

@@ -15,7 +15,6 @@ add_clang_library(clangTidyAndroidModule
CloexecMemfdCreateCheck.cpp
CloexecOpenCheck.cpp
CloexecSocketCheck.cpp
ComparisonInTempFailureRetryCheck.cpp
LINK_LIBS
clangAST

View File

@@ -32,7 +32,7 @@ void CloexecAccept4Check::registerMatchers(MatchFinder *Finder) {
}
void CloexecAccept4Check::check(const MatchFinder::MatchResult &Result) {
insertMacroFlag(Result, /*MacroFlag=*/"SOCK_CLOEXEC", /*ArgPos=*/3);
insertMacroFlag(Result, /*MarcoFlag=*/"SOCK_CLOEXEC", /*ArgPos=*/3);
}
} // namespace android

View File

@@ -25,7 +25,7 @@ void CloexecEpollCreate1Check::registerMatchers(MatchFinder *Finder) {
}
void CloexecEpollCreate1Check::check(const MatchFinder::MatchResult &Result) {
insertMacroFlag(Result, /*MacroFlag=*/"EPOLL_CLOEXEC", /*ArgPos=*/0);
insertMacroFlag(Result, /*MarcoFlag=*/"EPOLL_CLOEXEC", /*ArgPos=*/0);
}
} // namespace android

View File

@@ -25,7 +25,7 @@ void CloexecInotifyInit1Check::registerMatchers(MatchFinder *Finder) {
}
void CloexecInotifyInit1Check::check(const MatchFinder::MatchResult &Result) {
insertMacroFlag(Result, /*MacroFlag=*/"IN_CLOEXEC", /*ArgPos=*/0);
insertMacroFlag(Result, /*MarcoFlag=*/"IN_CLOEXEC", /*ArgPos=*/0);
}
} // namespace android

View File

@@ -36,7 +36,7 @@ void CloexecOpenCheck::check(const MatchFinder::MatchResult &Result) {
const auto *FD = Result.Nodes.getNodeAs<FunctionDecl>(FuncDeclBindingStr);
assert(FD->param_size() > 1);
int ArgPos = (FD->param_size() > 2) ? 2 : 1;
insertMacroFlag(Result, /*MacroFlag=*/"O_CLOEXEC", ArgPos);
insertMacroFlag(Result, /*MarcoFlag=*/"O_CLOEXEC", ArgPos);
}
} // namespace android

View File

@@ -27,7 +27,7 @@ void CloexecSocketCheck::registerMatchers(MatchFinder *Finder) {
}
void CloexecSocketCheck::check(const MatchFinder::MatchResult &Result) {
insertMacroFlag(Result, /*MacroFlag=*/"SOCK_CLOEXEC", /*ArgPos=*/1);
insertMacroFlag(Result, /*MarcoFlag=*/"SOCK_CLOEXEC", /*ArgPos=*/1);
}
} // namespace android

View File

@@ -1,84 +0,0 @@
//===--- ComparisonInTempFailureRetryCheck.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 "../utils/Matchers.h"
#include "ComparisonInTempFailureRetryCheck.h"
#include "clang/AST/ASTContext.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/Lex/Lexer.h"
using namespace clang::ast_matchers;
namespace clang {
namespace tidy {
namespace android {
namespace {
AST_MATCHER(BinaryOperator, isRHSATempFailureRetryArg) {
if (!Node.getLocStart().isMacroID())
return false;
const SourceManager &SM = Finder->getASTContext().getSourceManager();
if (!SM.isMacroArgExpansion(Node.getRHS()->IgnoreParenCasts()->getLocStart()))
return false;
const LangOptions &Opts = Finder->getASTContext().getLangOpts();
SourceLocation LocStart = Node.getLocStart();
while (LocStart.isMacroID()) {
SourceLocation Invocation = SM.getImmediateMacroCallerLoc(LocStart);
Token Tok;
if (!Lexer::getRawToken(SM.getSpellingLoc(Invocation), Tok, SM, Opts,
/*IgnoreWhiteSpace=*/true)) {
if (Tok.getKind() == tok::raw_identifier &&
Tok.getRawIdentifier() == "TEMP_FAILURE_RETRY")
return true;
}
LocStart = Invocation;
}
return false;
}
} // namespace
void ComparisonInTempFailureRetryCheck::registerMatchers(MatchFinder *Finder) {
// Both glibc's and Bionic's TEMP_FAILURE_RETRY macros structurally look like:
//
// #define TEMP_FAILURE_RETRY(x) ({ \
// typeof(x) y; \
// do y = (x); \
// while (y == -1 && errno == EINTR); \
// y; \
// })
//
// (glibc uses `long int` instead of `typeof(x)` for the type of y).
//
// It's unclear how to walk up the AST from inside the expansion of `x`, and
// we need to not complain about things like TEMP_FAILURE_RETRY(foo(x == 1)),
// so we just match the assignment of `y = (x)` and inspect `x` from there.
Finder->addMatcher(
binaryOperator(
hasOperatorName("="),
hasRHS(ignoringParenCasts(
binaryOperator(matchers::isComparisonOperator()).bind("binop"))),
isRHSATempFailureRetryArg()),
this);
}
void ComparisonInTempFailureRetryCheck::check(
const MatchFinder::MatchResult &Result) {
const auto &BinOp = *Result.Nodes.getNodeAs<BinaryOperator>("binop");
diag(BinOp.getOperatorLoc(), "top-level comparison in TEMP_FAILURE_RETRY");
// FIXME: FixIts would be nice, but potentially nontrivial when nested macros
// happen, e.g. `TEMP_FAILURE_RETRY(IS_ZERO(foo()))`
}
} // namespace android
} // namespace tidy
} // namespace clang

View File

@@ -1,36 +0,0 @@
//===--- ComparisonInTempFailureRetryCheck.h - clang-tidy--------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_ANDROID_COMPARISONINTEMPFAILURERETRYCHECK_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_ANDROID_COMPARISONINTEMPFAILURERETRYCHECK_H
#include "../ClangTidy.h"
namespace clang {
namespace tidy {
namespace android {
/// Attempts to catch calls to TEMP_FAILURE_RETRY with a top-level comparison
/// operation, like `TEMP_FAILURE_RETRY(read(...) != N)`. In these cases, the
/// comparison should go outside of the TEMP_FAILURE_RETRY.
///
/// TEMP_FAILURE_RETRY is a macro provided by both glibc and Bionic.
class ComparisonInTempFailureRetryCheck : public ClangTidyCheck {
public:
ComparisonInTempFailureRetryCheck(StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context) {}
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
};
} // namespace android
} // namespace tidy
} // namespace clang
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_ANDROID_COMPARISONINTEMPFAILURERETRYCHECK_H

View File

@@ -15,12 +15,10 @@ namespace clang {
namespace tidy {
namespace boost {
namespace {
AST_MATCHER(Type, isStrictlyInteger) {
return Node.isIntegerType() && !Node.isAnyCharacterType() &&
!Node.isBooleanType();
}
} // namespace
void UseToStringCheck::registerMatchers(MatchFinder *Finder) {
if (!getLangOpts().CPlusPlus)

View File

@@ -10,44 +10,21 @@
#include "../ClangTidy.h"
#include "../ClangTidyModule.h"
#include "../ClangTidyModuleRegistry.h"
#include "../cppcoreguidelines/NarrowingConversionsCheck.h"
#include "ArgumentCommentCheck.h"
#include "AssertSideEffectCheck.h"
#include "BoolPointerImplicitConversionCheck.h"
#include "CopyConstructorInitCheck.h"
#include "DanglingHandleCheck.h"
#include "ExceptionEscapeCheck.h"
#include "FoldInitTypeCheck.h"
#include "ForwardDeclarationNamespaceCheck.h"
#include "ForwardingReferenceOverloadCheck.h"
#include "InaccurateEraseCheck.h"
#include "IncorrectRoundingsCheck.h"
#include "IntegerDivisionCheck.h"
#include "LambdaFunctionNameCheck.h"
#include "MacroParenthesesCheck.h"
#include "MacroRepeatedSideEffectsCheck.h"
#include "MisplacedOperatorInStrlenInAllocCheck.h"
#include "MisplacedWideningCastCheck.h"
#include "MoveForwardingReferenceCheck.h"
#include "MultipleStatementMacroCheck.h"
#include "ParentVirtualCallCheck.h"
#include "SizeofContainerCheck.h"
#include "SizeofExpressionCheck.h"
#include "StringConstructorCheck.h"
#include "StringIntegerAssignmentCheck.h"
#include "StringLiteralWithEmbeddedNulCheck.h"
#include "SuspiciousEnumUsageCheck.h"
#include "SuspiciousMemsetUsageCheck.h"
#include "SuspiciousMissingCommaCheck.h"
#include "SuspiciousSemicolonCheck.h"
#include "SuspiciousStringCompareCheck.h"
#include "SwappedArgumentsCheck.h"
#include "TerminatingContinueCheck.h"
#include "ThrowKeywordMissingCheck.h"
#include "UndefinedMemoryManipulationCheck.h"
#include "UndelegatedConstructorCheck.h"
#include "UnusedRaiiCheck.h"
#include "UnusedReturnValueCheck.h"
#include "UseAfterMoveCheck.h"
#include "VirtualNearMissCheck.h"
@@ -68,72 +45,26 @@ public:
"bugprone-copy-constructor-init");
CheckFactories.registerCheck<DanglingHandleCheck>(
"bugprone-dangling-handle");
CheckFactories.registerCheck<ExceptionEscapeCheck>(
"bugprone-exception-escape");
CheckFactories.registerCheck<FoldInitTypeCheck>(
"bugprone-fold-init-type");
CheckFactories.registerCheck<ForwardDeclarationNamespaceCheck>(
"bugprone-forward-declaration-namespace");
CheckFactories.registerCheck<ForwardingReferenceOverloadCheck>(
"bugprone-forwarding-reference-overload");
CheckFactories.registerCheck<InaccurateEraseCheck>(
"bugprone-inaccurate-erase");
CheckFactories.registerCheck<IncorrectRoundingsCheck>(
"bugprone-incorrect-roundings");
CheckFactories.registerCheck<IntegerDivisionCheck>(
"bugprone-integer-division");
CheckFactories.registerCheck<LambdaFunctionNameCheck>(
"bugprone-lambda-function-name");
CheckFactories.registerCheck<MacroParenthesesCheck>(
"bugprone-macro-parentheses");
CheckFactories.registerCheck<MacroRepeatedSideEffectsCheck>(
"bugprone-macro-repeated-side-effects");
CheckFactories.registerCheck<MisplacedOperatorInStrlenInAllocCheck>(
"bugprone-misplaced-operator-in-strlen-in-alloc");
CheckFactories.registerCheck<MisplacedWideningCastCheck>(
"bugprone-misplaced-widening-cast");
CheckFactories.registerCheck<MoveForwardingReferenceCheck>(
"bugprone-move-forwarding-reference");
CheckFactories.registerCheck<MultipleStatementMacroCheck>(
"bugprone-multiple-statement-macro");
CheckFactories.registerCheck<cppcoreguidelines::NarrowingConversionsCheck>(
"bugprone-narrowing-conversions");
CheckFactories.registerCheck<ParentVirtualCallCheck>(
"bugprone-parent-virtual-call");
CheckFactories.registerCheck<SizeofContainerCheck>(
"bugprone-sizeof-container");
CheckFactories.registerCheck<SizeofExpressionCheck>(
"bugprone-sizeof-expression");
CheckFactories.registerCheck<StringConstructorCheck>(
"bugprone-string-constructor");
CheckFactories.registerCheck<StringIntegerAssignmentCheck>(
"bugprone-string-integer-assignment");
CheckFactories.registerCheck<StringLiteralWithEmbeddedNulCheck>(
"bugprone-string-literal-with-embedded-nul");
CheckFactories.registerCheck<SuspiciousEnumUsageCheck>(
"bugprone-suspicious-enum-usage");
CheckFactories.registerCheck<SuspiciousMemsetUsageCheck>(
"bugprone-suspicious-memset-usage");
CheckFactories.registerCheck<SuspiciousMissingCommaCheck>(
"bugprone-suspicious-missing-comma");
CheckFactories.registerCheck<SuspiciousSemicolonCheck>(
"bugprone-suspicious-semicolon");
CheckFactories.registerCheck<SuspiciousStringCompareCheck>(
"bugprone-suspicious-string-compare");
CheckFactories.registerCheck<SwappedArgumentsCheck>(
"bugprone-swapped-arguments");
CheckFactories.registerCheck<TerminatingContinueCheck>(
"bugprone-terminating-continue");
CheckFactories.registerCheck<ThrowKeywordMissingCheck>(
"bugprone-throw-keyword-missing");
CheckFactories.registerCheck<UndefinedMemoryManipulationCheck>(
"bugprone-undefined-memory-manipulation");
CheckFactories.registerCheck<UndelegatedConstructorCheck>(
"bugprone-undelegated-constructor");
CheckFactories.registerCheck<UnusedRaiiCheck>(
"bugprone-unused-raii");
CheckFactories.registerCheck<UnusedReturnValueCheck>(
"bugprone-unused-return-value");
CheckFactories.registerCheck<UseAfterMoveCheck>(
"bugprone-use-after-move");
CheckFactories.registerCheck<VirtualNearMissCheck>(

View File

@@ -7,38 +7,16 @@ add_clang_library(clangTidyBugproneModule
BugproneTidyModule.cpp
CopyConstructorInitCheck.cpp
DanglingHandleCheck.cpp
ExceptionEscapeCheck.cpp
FoldInitTypeCheck.cpp
ForwardDeclarationNamespaceCheck.cpp
ForwardingReferenceOverloadCheck.cpp
InaccurateEraseCheck.cpp
IncorrectRoundingsCheck.cpp
IntegerDivisionCheck.cpp
LambdaFunctionNameCheck.cpp
MacroParenthesesCheck.cpp
MacroRepeatedSideEffectsCheck.cpp
MisplacedOperatorInStrlenInAllocCheck.cpp
MisplacedWideningCastCheck.cpp
MoveForwardingReferenceCheck.cpp
MultipleStatementMacroCheck.cpp
ParentVirtualCallCheck.cpp
SizeofContainerCheck.cpp
SizeofExpressionCheck.cpp
StringConstructorCheck.cpp
StringIntegerAssignmentCheck.cpp
StringLiteralWithEmbeddedNulCheck.cpp
SuspiciousEnumUsageCheck.cpp
SuspiciousMemsetUsageCheck.cpp
SuspiciousMissingCommaCheck.cpp
SuspiciousSemicolonCheck.cpp
SuspiciousStringCompareCheck.cpp
SwappedArgumentsCheck.cpp
TerminatingContinueCheck.cpp
ThrowKeywordMissingCheck.cpp
UndefinedMemoryManipulationCheck.cpp
UndelegatedConstructorCheck.cpp
UnusedRaiiCheck.cpp
UnusedReturnValueCheck.cpp
UseAfterMoveCheck.cpp
VirtualNearMissCheck.cpp
@@ -49,7 +27,6 @@ add_clang_library(clangTidyBugproneModule
clangBasic
clangLex
clangTidy
clangTidyCppCoreGuidelinesModule
clangTidyUtils
clangTooling
)

View File

@@ -25,12 +25,8 @@ namespace {
ast_matchers::internal::BindableMatcher<Stmt>
handleFrom(const ast_matchers::internal::Matcher<RecordDecl> &IsAHandle,
const ast_matchers::internal::Matcher<Expr> &Arg) {
return expr(
anyOf(cxxConstructExpr(hasDeclaration(cxxMethodDecl(ofClass(IsAHandle))),
hasArgument(0, Arg)),
cxxMemberCallExpr(hasType(cxxRecordDecl(IsAHandle)),
callee(memberExpr(member(cxxConversionDecl()))),
on(Arg))));
return cxxConstructExpr(hasDeclaration(cxxMethodDecl(ofClass(IsAHandle))),
hasArgument(0, Arg));
}
ast_matchers::internal::Matcher<Stmt> handleFromTemporaryValue(

View File

@@ -1,214 +0,0 @@
//===--- ExceptionEscapeCheck.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 "ExceptionEscapeCheck.h"
#include "clang/AST/ASTContext.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "llvm/ADT/SmallSet.h"
#include "llvm/ADT/StringSet.h"
using namespace clang::ast_matchers;
namespace {
typedef llvm::SmallVector<const clang::Type *, 8> TypeVec;
} // namespace
namespace clang {
static bool isBaseOf(const Type *DerivedType, const Type *BaseType) {
const auto *DerivedClass = DerivedType->getAsCXXRecordDecl();
const auto *BaseClass = BaseType->getAsCXXRecordDecl();
if (!DerivedClass || !BaseClass)
return false;
return !DerivedClass->forallBases(
[BaseClass](const CXXRecordDecl *Cur) { return Cur != BaseClass; });
}
static const TypeVec
throwsException(const Stmt *St, const TypeVec &Caught,
llvm::SmallSet<const FunctionDecl *, 32> &CallStack);
static const TypeVec
throwsException(const FunctionDecl *Func,
llvm::SmallSet<const FunctionDecl *, 32> &CallStack) {
if (CallStack.count(Func))
return TypeVec();
if (const Stmt *Body = Func->getBody()) {
CallStack.insert(Func);
const TypeVec Result = throwsException(Body, TypeVec(), CallStack);
CallStack.erase(Func);
return Result;
}
TypeVec Result;
if (const auto *FPT = Func->getType()->getAs<FunctionProtoType>()) {
for (const QualType Ex : FPT->exceptions()) {
Result.push_back(Ex.getTypePtr());
}
}
return Result;
}
static const TypeVec
throwsException(const Stmt *St, const TypeVec &Caught,
llvm::SmallSet<const FunctionDecl *, 32> &CallStack) {
TypeVec Results;
if (!St)
return Results;
if (const auto *Throw = dyn_cast<CXXThrowExpr>(St)) {
if (const auto *ThrownExpr = Throw->getSubExpr()) {
const auto *ThrownType =
ThrownExpr->getType()->getUnqualifiedDesugaredType();
if (ThrownType->isReferenceType()) {
ThrownType = ThrownType->castAs<ReferenceType>()
->getPointeeType()
->getUnqualifiedDesugaredType();
}
if (const auto *TD = ThrownType->getAsTagDecl()) {
if (TD->getDeclName().isIdentifier() && TD->getName() == "bad_alloc"
&& TD->isInStdNamespace())
return Results;
}
Results.push_back(ThrownExpr->getType()->getUnqualifiedDesugaredType());
} else {
Results.append(Caught.begin(), Caught.end());
}
} else if (const auto *Try = dyn_cast<CXXTryStmt>(St)) {
TypeVec Uncaught = throwsException(Try->getTryBlock(), Caught, CallStack);
for (unsigned i = 0; i < Try->getNumHandlers(); ++i) {
const CXXCatchStmt *Catch = Try->getHandler(i);
if (!Catch->getExceptionDecl()) {
const TypeVec Rethrown =
throwsException(Catch->getHandlerBlock(), Uncaught, CallStack);
Results.append(Rethrown.begin(), Rethrown.end());
Uncaught.clear();
} else {
const auto *CaughtType =
Catch->getCaughtType()->getUnqualifiedDesugaredType();
if (CaughtType->isReferenceType()) {
CaughtType = CaughtType->castAs<ReferenceType>()
->getPointeeType()
->getUnqualifiedDesugaredType();
}
auto NewEnd =
llvm::remove_if(Uncaught, [&CaughtType](const Type *ThrownType) {
return ThrownType == CaughtType ||
isBaseOf(ThrownType, CaughtType);
});
if (NewEnd != Uncaught.end()) {
Uncaught.erase(NewEnd, Uncaught.end());
const TypeVec Rethrown = throwsException(
Catch->getHandlerBlock(), TypeVec(1, CaughtType), CallStack);
Results.append(Rethrown.begin(), Rethrown.end());
}
}
}
Results.append(Uncaught.begin(), Uncaught.end());
} else if (const auto *Call = dyn_cast<CallExpr>(St)) {
if (const FunctionDecl *Func = Call->getDirectCallee()) {
TypeVec Excs = throwsException(Func, CallStack);
Results.append(Excs.begin(), Excs.end());
}
} else {
for (const Stmt *Child : St->children()) {
TypeVec Excs = throwsException(Child, Caught, CallStack);
Results.append(Excs.begin(), Excs.end());
}
}
return Results;
}
static const TypeVec throwsException(const FunctionDecl *Func) {
llvm::SmallSet<const FunctionDecl *, 32> CallStack;
return throwsException(Func, CallStack);
}
namespace ast_matchers {
AST_MATCHER_P(FunctionDecl, throws, internal::Matcher<Type>, InnerMatcher) {
TypeVec ExceptionList = throwsException(&Node);
auto NewEnd = llvm::remove_if(
ExceptionList, [this, Finder, Builder](const Type *Exception) {
return !InnerMatcher.matches(*Exception, Finder, Builder);
});
ExceptionList.erase(NewEnd, ExceptionList.end());
return ExceptionList.size();
}
AST_MATCHER_P(Type, isIgnored, llvm::StringSet<>, IgnoredExceptions) {
if (const auto *TD = Node.getAsTagDecl()) {
if (TD->getDeclName().isIdentifier())
return IgnoredExceptions.count(TD->getName()) > 0;
}
return false;
}
AST_MATCHER_P(FunctionDecl, isEnabled, llvm::StringSet<>,
FunctionsThatShouldNotThrow) {
return FunctionsThatShouldNotThrow.count(Node.getNameAsString()) > 0;
}
} // namespace ast_matchers
namespace tidy {
namespace bugprone {
ExceptionEscapeCheck::ExceptionEscapeCheck(StringRef Name,
ClangTidyContext *Context)
: ClangTidyCheck(Name, Context), RawFunctionsThatShouldNotThrow(Options.get(
"FunctionsThatShouldNotThrow", "")),
RawIgnoredExceptions(Options.get("IgnoredExceptions", "")) {
llvm::SmallVector<StringRef, 8> FunctionsThatShouldNotThrowVec,
IgnoredExceptionsVec;
StringRef(RawFunctionsThatShouldNotThrow)
.split(FunctionsThatShouldNotThrowVec, ",", -1, false);
FunctionsThatShouldNotThrow.insert(FunctionsThatShouldNotThrowVec.begin(),
FunctionsThatShouldNotThrowVec.end());
StringRef(RawIgnoredExceptions).split(IgnoredExceptionsVec, ",", -1, false);
IgnoredExceptions.insert(IgnoredExceptionsVec.begin(),
IgnoredExceptionsVec.end());
}
void ExceptionEscapeCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
Options.store(Opts, "FunctionsThatShouldNotThrow",
RawFunctionsThatShouldNotThrow);
Options.store(Opts, "IgnoredExceptions", RawIgnoredExceptions);
}
void ExceptionEscapeCheck::registerMatchers(MatchFinder *Finder) {
Finder->addMatcher(
functionDecl(allOf(anyOf(isNoThrow(), cxxDestructorDecl(),
cxxConstructorDecl(isMoveConstructor()),
cxxMethodDecl(isMoveAssignmentOperator()),
hasName("main"), hasName("swap"),
isEnabled(FunctionsThatShouldNotThrow)),
throws(unless(isIgnored(IgnoredExceptions)))))
.bind("thrower"),
this);
}
void ExceptionEscapeCheck::check(const MatchFinder::MatchResult &Result) {
const FunctionDecl *MatchedDecl =
Result.Nodes.getNodeAs<FunctionDecl>("thrower");
if (!MatchedDecl)
return;
// FIXME: We should provide more information about the exact location where
// the exception is thrown, maybe the full path the exception escapes
diag(MatchedDecl->getLocation(), "an exception may be thrown in function %0 "
"which should not throw exceptions") << MatchedDecl;
}
} // namespace bugprone
} // namespace tidy
} // namespace clang

View File

@@ -1,47 +0,0 @@
//===--- ExceptionEscapeCheck.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_BUGPRONE_EXCEPTION_ESCAPE_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_EXCEPTION_ESCAPE_H
#include "../ClangTidy.h"
#include "llvm/ADT/StringSet.h"
namespace clang {
namespace tidy {
namespace bugprone {
/// Finds functions which should not throw exceptions: Destructors, move
/// constructors, move assignment operators, the main() function,
/// swap() functions, functions marked with throw() or noexcept and functions
/// given as option to the checker.
///
/// For the user-facing documentation see:
/// http://clang.llvm.org/extra/clang-tidy/checks/bugprone-exception-escape.html
class ExceptionEscapeCheck : public ClangTidyCheck {
public:
ExceptionEscapeCheck(StringRef Name, ClangTidyContext *Context);
void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
private:
std::string RawFunctionsThatShouldNotThrow;
std::string RawIgnoredExceptions;
llvm::StringSet<> FunctionsThatShouldNotThrow;
llvm::StringSet<> IgnoredExceptions;
};
} // namespace bugprone
} // namespace tidy
} // namespace clang
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_EXCEPTION_ESCAPE_H

View File

@@ -1,71 +0,0 @@
//===--- IncorrectRoundingsCheck.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 "IncorrectRoundingsCheck.h"
#include "clang/AST/DeclBase.h"
#include "clang/AST/Type.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/ASTMatchers/ASTMatchers.h"
#include "clang/Lex/Lexer.h"
using namespace clang::ast_matchers;
namespace clang {
namespace tidy {
namespace bugprone {
namespace {
AST_MATCHER(FloatingLiteral, floatHalf) {
const auto &literal = Node.getValue();
if ((&Node.getSemantics()) == &llvm::APFloat::IEEEsingle())
return literal.convertToFloat() == 0.5f;
if ((&Node.getSemantics()) == &llvm::APFloat::IEEEdouble())
return literal.convertToDouble() == 0.5;
return false;
}
} // namespace
void IncorrectRoundingsCheck::registerMatchers(MatchFinder *MatchFinder) {
// Match a floating literal with value 0.5.
auto FloatHalf = floatLiteral(floatHalf());
// Match a floating point expression.
auto FloatType = expr(hasType(realFloatingPointType()));
// Match a floating literal of 0.5 or a floating literal of 0.5 implicitly.
// cast to floating type.
auto FloatOrCastHalf =
anyOf(FloatHalf,
implicitCastExpr(FloatType, has(ignoringParenImpCasts(FloatHalf))));
// Match if either the LHS or RHS is a floating literal of 0.5 or a floating
// literal of 0.5 and the other is of type double or vice versa.
auto OneSideHalf = anyOf(allOf(hasLHS(FloatOrCastHalf), hasRHS(FloatType)),
allOf(hasRHS(FloatOrCastHalf), hasLHS(FloatType)));
// Find expressions of cast to int of the sum of a floating point expression
// and 0.5.
MatchFinder->addMatcher(
implicitCastExpr(
hasImplicitDestinationType(isInteger()),
ignoringParenCasts(binaryOperator(hasOperatorName("+"), OneSideHalf)))
.bind("CastExpr"),
this);
}
void IncorrectRoundingsCheck::check(const MatchFinder::MatchResult &Result) {
const auto *CastExpr = Result.Nodes.getNodeAs<ImplicitCastExpr>("CastExpr");
diag(CastExpr->getLocStart(),
"casting (double + 0.5) to integer leads to incorrect rounding; "
"consider using lround (#include <cmath>) instead");
}
} // namespace bugprone
} // namespace tidy
} // namespace clang

View File

@@ -1,39 +0,0 @@
//===--- IncorrectRoundingsCheck.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_BUGPRONE_INCORRECTROUNDINGSCHECK_H_
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_INCORRECTROUNDINGSCHECK_H_
#include "../ClangTidy.h"
namespace clang {
namespace tidy {
namespace bugprone {
/// \brief Checks the usage of patterns known to produce incorrect rounding.
/// Programmers often use
/// (int)(double_expression + 0.5)
/// to round the double expression to an integer. The problem with this
/// 1. It is unnecessarily slow.
/// 2. It is incorrect. The number 0.499999975 (smallest representable float
/// number below 0.5) rounds to 1.0. Even worse behavior for negative
/// numbers where both -0.5f and -1.4f both round to 0.0.
class IncorrectRoundingsCheck : public ClangTidyCheck {
public:
IncorrectRoundingsCheck(StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context) {}
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
};
} // namespace bugprone
} // namespace tidy
} // namespace clang
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_INCORRECTROUNDINGSCHECK_H_

View File

@@ -1,31 +0,0 @@
//===--- MacroRepeatedSideEffectsCheck.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_BUGPRONE_MACROREPEATEDSIDEEFFECTSCHECK_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_MACROREPEATEDSIDEEFFECTSCHECK_H
#include "../ClangTidy.h"
namespace clang {
namespace tidy {
namespace bugprone {
/// Checks for repeated argument with side effects in macros.
class MacroRepeatedSideEffectsCheck : public ClangTidyCheck {
public:
MacroRepeatedSideEffectsCheck(StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context) {}
void registerPPCallbacks(CompilerInstance &Compiler) override;
};
} // namespace bugprone
} // namespace tidy
} // namespace clang
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_MACROREPEATEDSIDEEFFECTSCHECK_H

View File

@@ -38,7 +38,7 @@ const Stmt *nextStmt(const MatchFinder::MatchResult &Result, const Stmt *S) {
return nextStmt(Result, Parent);
}
using ExpansionRanges = std::vector<SourceRange>;
using ExpansionRanges = std::vector<std::pair<SourceLocation, SourceLocation>>;
/// \bried Get all the macro expansion ranges related to `Loc`.
///
@@ -47,9 +47,8 @@ ExpansionRanges getExpansionRanges(SourceLocation Loc,
const MatchFinder::MatchResult &Result) {
ExpansionRanges Locs;
while (Loc.isMacroID()) {
Locs.push_back(
Result.SourceManager->getImmediateExpansionRange(Loc).getAsRange());
Loc = Locs.back().getBegin();
Locs.push_back(Result.SourceManager->getImmediateExpansionRange(Loc));
Loc = Locs.back().first;
}
return Locs;
}
@@ -97,9 +96,9 @@ void MultipleStatementMacroCheck::check(
InnerRanges.back() != NextRanges.back())
return;
diag(InnerRanges.back().getBegin(), "multiple statement macro used without "
"braces; some statements will be "
"unconditionally executed");
diag(InnerRanges.back().first, "multiple statement macro used without "
"braces; some statements will be "
"unconditionally executed");
}
} // namespace bugprone

View File

@@ -1,156 +0,0 @@
//===--- ParentVirtualCallCheck.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 "ParentVirtualCallCheck.h"
#include "clang/AST/ASTContext.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/Tooling/FixIt.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallVector.h"
#include <algorithm>
#include <cctype>
using namespace clang::ast_matchers;
namespace clang {
namespace tidy {
namespace bugprone {
using BasesVector = llvm::SmallVector<const CXXRecordDecl *, 5>;
static bool isParentOf(const CXXRecordDecl &Parent,
const CXXRecordDecl &ThisClass) {
if (Parent.getCanonicalDecl() == ThisClass.getCanonicalDecl())
return true;
const CXXRecordDecl *ParentCanonicalDecl = Parent.getCanonicalDecl();
return ThisClass.bases_end() !=
llvm::find_if(ThisClass.bases(), [=](const CXXBaseSpecifier &Base) {
auto *BaseDecl = Base.getType()->getAsCXXRecordDecl();
assert(BaseDecl);
return ParentCanonicalDecl == BaseDecl->getCanonicalDecl();
});
}
static BasesVector getParentsByGrandParent(const CXXRecordDecl &GrandParent,
const CXXRecordDecl &ThisClass,
const CXXMethodDecl &MemberDecl) {
BasesVector Result;
for (const auto &Base : ThisClass.bases()) {
const auto *BaseDecl = Base.getType()->getAsCXXRecordDecl();
const CXXMethodDecl *ActualMemberDecl =
MemberDecl.getCorrespondingMethodInClass(BaseDecl);
if (!ActualMemberDecl)
continue;
// TypePtr is the nearest base class to ThisClass between ThisClass and
// GrandParent, where MemberDecl is overridden. TypePtr is the class the
// check proposes to fix to.
const Type *TypePtr =
ActualMemberDecl->getThisType(ActualMemberDecl->getASTContext())
.getTypePtr();
const CXXRecordDecl *RecordDeclType = TypePtr->getPointeeCXXRecordDecl();
assert(RecordDeclType && "TypePtr is not a pointer to CXXRecordDecl!");
if (RecordDeclType->getCanonicalDecl()->isDerivedFrom(&GrandParent))
Result.emplace_back(RecordDeclType);
}
return Result;
}
static std::string getNameAsString(const NamedDecl *Decl) {
std::string QualName;
llvm::raw_string_ostream OS(QualName);
PrintingPolicy PP(Decl->getASTContext().getPrintingPolicy());
PP.SuppressUnwrittenScope = true;
Decl->printQualifiedName(OS, PP);
return OS.str();
}
// Returns E as written in the source code. Used to handle 'using' and
// 'typedef'ed names of grand-parent classes.
static std::string getExprAsString(const clang::Expr &E,
clang::ASTContext &AC) {
std::string Text = tooling::fixit::getText(E, AC).str();
Text.erase(
llvm::remove_if(
Text,
[](char C) { return std::isspace(static_cast<unsigned char>(C)); }),
Text.end());
return Text;
}
void ParentVirtualCallCheck::registerMatchers(MatchFinder *Finder) {
Finder->addMatcher(
cxxMemberCallExpr(
callee(memberExpr(hasDescendant(implicitCastExpr(
hasImplicitDestinationType(pointsTo(
type(anything()).bind("castToType"))),
hasSourceExpression(cxxThisExpr(hasType(
type(anything()).bind("thisType")))))))
.bind("member")),
callee(cxxMethodDecl(isVirtual()))),
this);
}
void ParentVirtualCallCheck::check(const MatchFinder::MatchResult &Result) {
const auto *Member = Result.Nodes.getNodeAs<MemberExpr>("member");
assert(Member);
if (!Member->getQualifier())
return;
const auto *MemberDecl = cast<CXXMethodDecl>(Member->getMemberDecl());
const auto *ThisTypePtr = Result.Nodes.getNodeAs<PointerType>("thisType");
assert(ThisTypePtr);
const auto *ThisType = ThisTypePtr->getPointeeCXXRecordDecl();
assert(ThisType);
const auto *CastToTypePtr = Result.Nodes.getNodeAs<Type>("castToType");
assert(CastToTypePtr);
const auto *CastToType = CastToTypePtr->getAsCXXRecordDecl();
assert(CastToType);
if (isParentOf(*CastToType, *ThisType))
return;
const BasesVector Parents =
getParentsByGrandParent(*CastToType, *ThisType, *MemberDecl);
if (Parents.empty())
return;
std::string ParentsStr;
ParentsStr.reserve(30 * Parents.size());
for (const CXXRecordDecl *Parent : Parents) {
if (!ParentsStr.empty())
ParentsStr.append(" or ");
ParentsStr.append("'").append(getNameAsString(Parent)).append("'");
}
assert(Member->getQualifierLoc().getSourceRange().getBegin().isValid());
auto Diag = diag(Member->getQualifierLoc().getSourceRange().getBegin(),
"qualified name '%0' refers to a member overridden "
"in subclass%1; did you mean %2?")
<< getExprAsString(*Member, *Result.Context)
<< (Parents.size() > 1 ? "es" : "") << ParentsStr;
// Propose a fix if there's only one parent class...
if (Parents.size() == 1 &&
// ...unless parent class is templated
!isa<ClassTemplateSpecializationDecl>(Parents.front()))
Diag << FixItHint::CreateReplacement(
Member->getQualifierLoc().getSourceRange(),
getNameAsString(Parents.front()) + "::");
}
} // namespace bugprone
} // namespace tidy
} // namespace clang

View File

@@ -1,35 +0,0 @@
//===--- ParentVirtualCallCheck.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_BUGPRONE_PARENTVIRTUALCALLCHECK_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_PARENTVIRTUALCALLCHECK_H
#include "../ClangTidy.h"
namespace clang {
namespace tidy {
namespace bugprone {
/// Finds calls to grand..-parent virtual methods instead of parent's.
///
/// For the user-facing documentation see:
/// http://clang.llvm.org/extra/clang-tidy/checks/bugprone-parent-virtual-call.html
class ParentVirtualCallCheck : public ClangTidyCheck {
public:
ParentVirtualCallCheck(StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context) {}
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
};
} // namespace bugprone
} // namespace tidy
} // namespace clang
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_PARENTVIRTUALCALLCHECK_H

View File

@@ -1,36 +0,0 @@
//===--- SizeofContainerCheck.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_BUGPRONE_SIZEOFCONTAINERCHECK_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_SIZEOFCONTAINERCHECK_H
#include "../ClangTidy.h"
namespace clang {
namespace tidy {
namespace bugprone {
/// Find usages of sizeof on expressions of STL container types. Most likely the
/// user wanted to use `.size()` instead.
///
/// For the user-facing documentation see:
/// http://clang.llvm.org/extra/clang-tidy/checks/bugprone-sizeof-container.html
class SizeofContainerCheck : public ClangTidyCheck {
public:
SizeofContainerCheck(StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context) {}
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
};
} // namespace bugprone
} // namespace tidy
} // namespace clang
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_SIZEOFCONTAINERCHECK_H

View File

@@ -1,41 +0,0 @@
//===--- SizeofExpressionCheck.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_BUGPRONE_SIZEOFEXPRESSIONCHECK_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_SIZEOFEXPRESSIONCHECK_H
#include "../ClangTidy.h"
namespace clang {
namespace tidy {
namespace bugprone {
/// Find suspicious usages of sizeof expression.
///
/// For the user-facing documentation see:
/// http://clang.llvm.org/extra/clang-tidy/checks/bugprone-sizeof-expression.html
class SizeofExpressionCheck : public ClangTidyCheck {
public:
SizeofExpressionCheck(StringRef Name, ClangTidyContext *Context);
void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
private:
const bool WarnOnSizeOfConstant;
const bool WarnOnSizeOfIntegerExpression;
const bool WarnOnSizeOfThis;
const bool WarnOnSizeOfCompareToConstant;
};
} // namespace bugprone
} // namespace tidy
} // namespace clang
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_SIZEOFEXPRESSIONCHECK_H

View File

@@ -18,11 +18,9 @@ namespace clang {
namespace tidy {
namespace bugprone {
namespace {
AST_MATCHER_P(IntegerLiteral, isBiggerThan, unsigned, N) {
return Node.getValue().getZExtValue() > N;
}
} // namespace
StringConstructorCheck::StringConstructorCheck(StringRef Name,
ClangTidyContext *Context)

View File

@@ -1,35 +0,0 @@
//===--- StringLiteralWithEmbeddedNulCheck.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_BUGPRONE_STRINGLITERALWITHEMBEDDEDNULCHECK_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_STRINGLITERALWITHEMBEDDEDNULCHECK_H
#include "../ClangTidy.h"
namespace clang {
namespace tidy {
namespace bugprone {
/// Find suspicious string literals with embedded NUL characters.
///
/// For the user-facing documentation see:
/// http://clang.llvm.org/extra/clang-tidy/checks/bugprone-string-literal-with-embedded-nul.html
class StringLiteralWithEmbeddedNulCheck : public ClangTidyCheck {
public:
StringLiteralWithEmbeddedNulCheck(StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context) {}
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
};
} // namespace bugprone
} // namespace tidy
} // namespace clang
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_STRINGLITERALWITHEMBEDDEDNULCHECK_H

View File

@@ -1,39 +0,0 @@
//===--- SuspiciousEnumUsageCheck.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_BUGPRONE_SUSPICIOUSENUMUSAGECHECK_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_SUSPICIOUSENUMUSAGECHECK_H
#include "../ClangTidy.h"
namespace clang {
namespace tidy {
namespace bugprone {
/// The checker detects various cases when an enum is probably misused (as a
/// bitmask).
/// For the user-facing documentation see:
/// http://clang.llvm.org/extra/clang-tidy/checks/bugprone-suspicious-enum-usage.html
class SuspiciousEnumUsageCheck : public ClangTidyCheck {
public:
SuspiciousEnumUsageCheck(StringRef Name, ClangTidyContext *Context);
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
private:
void checkSuspiciousBitmaskUsage(const Expr*, const EnumDecl*);
const bool StrictMode;
};
} // namespace bugprone
} // namespace tidy
} // namespace clang
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_SUSPICIOUSENUMUSAGECHECK_H

View File

@@ -1,36 +0,0 @@
//===--- SuspiciousSemicolonCheck.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_BUGPRONE_SUSPICIOUSSEMICOLONCHECK_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_SUSPICIOUSSEMICOLONCHECK_H
#include "../ClangTidy.h"
namespace clang {
namespace tidy {
namespace bugprone {
/// This check finds semicolon that modifies the meaning of the program
/// unintendedly.
///
/// For the user-facing documentation see:
/// http://clang.llvm.org/extra/clang-tidy/checks/bugprone-suspicious-semicolon.html
class SuspiciousSemicolonCheck : public ClangTidyCheck {
public:
SuspiciousSemicolonCheck(StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context) {}
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
};
} // namespace bugprone
} // namespace tidy
} // namespace clang
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_SUSPICIOUSSEMICOLONCHECK_H

View File

@@ -1,40 +0,0 @@
//===--- SuspiciousStringCompareCheck.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_BUGPRONE_SUSPICIOUSSTRINGCOMPARECHECK_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_SUSPICIOUSSTRINGCOMPARECHECK_H
#include "../ClangTidy.h"
namespace clang {
namespace tidy {
namespace bugprone {
/// Find suspicious calls to string compare functions.
///
/// For the user-facing documentation see:
/// http://clang.llvm.org/extra/clang-tidy/checks/bugprone-suspicious-string-compare.html
class SuspiciousStringCompareCheck : public ClangTidyCheck {
public:
SuspiciousStringCompareCheck(StringRef Name, ClangTidyContext *Context);
void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
private:
const bool WarnOnImplicitComparison;
const bool WarnOnLogicalNotComparison;
const std::string StringCompareLikeFunctions;
};
} // namespace bugprone
} // namespace tidy
} // namespace clang
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_SUSPICIOUSSTRINGCOMPARECHECK_H

View File

@@ -1,49 +0,0 @@
//===--- TerminatingContinueCheck.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 "TerminatingContinueCheck.h"
#include "clang/AST/ASTContext.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/Lex/Lexer.h"
#include "clang/Tooling/FixIt.h"
using namespace clang::ast_matchers;
namespace clang {
namespace tidy {
namespace bugprone {
void TerminatingContinueCheck::registerMatchers(MatchFinder *Finder) {
const auto doWithFalse =
doStmt(hasCondition(ignoringImpCasts(
anyOf(cxxBoolLiteral(equals(false)), integerLiteral(equals(0)),
cxxNullPtrLiteralExpr(), gnuNullExpr()))),
equalsBoundNode("closestLoop"));
Finder->addMatcher(
continueStmt(hasAncestor(stmt(anyOf(forStmt(), whileStmt(),
cxxForRangeStmt(), doStmt()))
.bind("closestLoop")),
hasAncestor(doWithFalse))
.bind("continue"),
this);
}
void TerminatingContinueCheck::check(const MatchFinder::MatchResult &Result) {
const auto *ContStmt = Result.Nodes.getNodeAs<ContinueStmt>("continue");
auto Diag =
diag(ContStmt->getLocStart(),
"'continue' in loop with false condition is equivalent to 'break'")
<< tooling::fixit::createReplacement(*ContStmt, "break");
}
} // namespace bugprone
} // namespace tidy
} // namespace clang

View File

@@ -1,36 +0,0 @@
//===--- TerminatingContinueCheck.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_BUGPRONE_TERMINATINGCONTINUECHECK_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_TERMINATINGCONTINUECHECK_H
#include "../ClangTidy.h"
namespace clang {
namespace tidy {
namespace bugprone {
/// Checks if a 'continue' statement terminates the loop (i.e. the loop has
/// a condition which always evaluates to false).
///
/// For the user-facing documentation see:
/// http://clang.llvm.org/extra/clang-tidy/checks/bugprone-terminating-continue.html
class TerminatingContinueCheck : public ClangTidyCheck {
public:
TerminatingContinueCheck(StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context) {}
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
};
} // namespace bugprone
} // namespace tidy
} // namespace clang
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_TERMINATINGCONTINUECHECK_H

View File

@@ -1,52 +0,0 @@
//===--- ThrowKeywordMissingCheck.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 "ThrowKeywordMissingCheck.h"
#include "clang/AST/ASTContext.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
using namespace clang::ast_matchers;
namespace clang {
namespace tidy {
namespace bugprone {
void ThrowKeywordMissingCheck::registerMatchers(MatchFinder *Finder) {
if (!getLangOpts().CPlusPlus)
return;
auto CtorInitializerList =
cxxConstructorDecl(hasAnyConstructorInitializer(anything()));
Finder->addMatcher(
expr(anyOf(cxxFunctionalCastExpr(), cxxBindTemporaryExpr(),
cxxTemporaryObjectExpr()),
hasType(cxxRecordDecl(
isSameOrDerivedFrom(matchesName("[Ee]xception|EXCEPTION")))),
unless(anyOf(hasAncestor(stmt(
anyOf(cxxThrowExpr(), callExpr(), returnStmt()))),
hasAncestor(varDecl()),
allOf(hasAncestor(CtorInitializerList),
unless(hasAncestor(cxxCatchStmt()))))))
.bind("temporary-exception-not-thrown"),
this);
}
void ThrowKeywordMissingCheck::check(const MatchFinder::MatchResult &Result) {
const auto *TemporaryExpr =
Result.Nodes.getNodeAs<Expr>("temporary-exception-not-thrown");
diag(TemporaryExpr->getLocStart(), "suspicious exception object created but "
"not thrown; did you mean 'throw %0'?")
<< TemporaryExpr->getType().getBaseTypeIdentifier()->getName();
}
} // namespace bugprone
} // namespace tidy
} // namespace clang

View File

@@ -1,36 +0,0 @@
//===--- ThrowKeywordMissingCheck.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_BUGPRONE_THROWKEYWORDMISSINGCHECK_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_THROWKEYWORDMISSINGCHECK_H
#include "../ClangTidy.h"
namespace clang {
namespace tidy {
namespace bugprone {
/// Emits a warning about temporary objects whose type is (or is derived from) a
/// class that has 'EXCEPTION', 'Exception' or 'exception' in its name.
///
/// For the user-facing documentation see:
/// http://clang.llvm.org/extra/clang-tidy/checks/bugprone-throw-keyword-missing.html
class ThrowKeywordMissingCheck : public ClangTidyCheck {
public:
ThrowKeywordMissingCheck(StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context) {}
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
};
} // namespace bugprone
} // namespace tidy
} // namespace clang
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_THROWKEYWORDMISSINGCHECK_H

View File

@@ -1,84 +0,0 @@
//===--- UndelegatedConstructorCheck.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 "UndelegatedConstructorCheck.h"
#include "clang/AST/ASTContext.h"
#include "clang/Lex/Lexer.h"
using namespace clang::ast_matchers;
namespace clang {
namespace tidy {
namespace bugprone {
namespace {
AST_MATCHER_P(Stmt, ignoringTemporaryExpr,
ast_matchers::internal::Matcher<Stmt>, InnerMatcher) {
const Stmt *E = &Node;
for (;;) {
// Temporaries with non-trivial dtors.
if (const auto *EWC = dyn_cast<ExprWithCleanups>(E))
E = EWC->getSubExpr();
// Temporaries with zero or more than two ctor arguments.
else if (const auto *BTE = dyn_cast<CXXBindTemporaryExpr>(E))
E = BTE->getSubExpr();
// Temporaries with exactly one ctor argument.
else if (const auto *FCE = dyn_cast<CXXFunctionalCastExpr>(E))
E = FCE->getSubExpr();
else
break;
}
return InnerMatcher.matches(*E, Finder, Builder);
}
// Finds a node if it's a base of an already bound node.
AST_MATCHER_P(CXXRecordDecl, baseOfBoundNode, std::string, ID) {
return Builder->removeBindings(
[&](const ast_matchers::internal::BoundNodesMap &Nodes) {
const auto *Derived = Nodes.getNodeAs<CXXRecordDecl>(ID);
return Derived != &Node && !Derived->isDerivedFrom(&Node);
});
}
} // namespace
void UndelegatedConstructorCheck::registerMatchers(MatchFinder *Finder) {
// We look for calls to constructors of the same type in constructors. To do
// this we have to look through a variety of nodes that occur in the path,
// depending on the type's destructor and the number of arguments on the
// constructor call, this is handled by ignoringTemporaryExpr. Ignore template
// instantiations to reduce the number of duplicated warnings.
//
// Only register the matchers for C++11; the functionality currently does not
// provide any benefit to other languages, despite being benign.
if (!getLangOpts().CPlusPlus11)
return;
Finder->addMatcher(
compoundStmt(
hasParent(
cxxConstructorDecl(ofClass(cxxRecordDecl().bind("parent")))),
forEach(ignoringTemporaryExpr(
cxxConstructExpr(hasDeclaration(cxxConstructorDecl(ofClass(
cxxRecordDecl(baseOfBoundNode("parent"))))))
.bind("construct"))),
unless(isInTemplateInstantiation())),
this);
}
void UndelegatedConstructorCheck::check(
const MatchFinder::MatchResult &Result) {
const auto *E = Result.Nodes.getNodeAs<CXXConstructExpr>("construct");
diag(E->getLocStart(), "did you intend to call a delegated constructor? "
"A temporary object is created here instead");
}
} // namespace bugprone
} // namespace tidy
} // namespace clang

View File

@@ -1,36 +0,0 @@
//===--- UndelegatedConstructorCheck.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_MISC_UNDELEGATEDCONSTRUCTOR_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_UNDELEGATEDCONSTRUCTOR_H
#include "../ClangTidy.h"
namespace clang {
namespace tidy {
namespace bugprone {
/// Finds creation of temporary objects in constructors that look like a
/// function call to another constructor of the same class.
///
/// The user most likely meant to use a delegating constructor or base class
/// initializer.
class UndelegatedConstructorCheck : public ClangTidyCheck {
public:
UndelegatedConstructorCheck(StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context) {}
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
};
} // namespace bugprone
} // namespace tidy
} // namespace clang
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_UNDELEGATEDCONSTRUCTOR_H

View File

@@ -1,94 +0,0 @@
//===--- UnusedRaiiCheck.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 "UnusedRaiiCheck.h"
#include "clang/AST/ASTContext.h"
#include "clang/Lex/Lexer.h"
using namespace clang::ast_matchers;
namespace clang {
namespace tidy {
namespace bugprone {
namespace {
AST_MATCHER(CXXRecordDecl, hasNonTrivialDestructor) {
// TODO: If the dtor is there but empty we don't want to warn either.
return Node.hasDefinition() && Node.hasNonTrivialDestructor();
}
} // namespace
void UnusedRaiiCheck::registerMatchers(MatchFinder *Finder) {
// Only register the matchers for C++; the functionality currently does not
// provide any benefit to other languages, despite being benign.
if (!getLangOpts().CPlusPlus)
return;
// Look for temporaries that are constructed in-place and immediately
// destroyed. Look for temporaries created by a functional cast but not for
// those returned from a call.
auto BindTemp =
cxxBindTemporaryExpr(unless(has(ignoringParenImpCasts(callExpr()))))
.bind("temp");
Finder->addMatcher(
exprWithCleanups(unless(isInTemplateInstantiation()),
hasParent(compoundStmt().bind("compound")),
hasType(cxxRecordDecl(hasNonTrivialDestructor())),
anyOf(has(ignoringParenImpCasts(BindTemp)),
has(ignoringParenImpCasts(cxxFunctionalCastExpr(
has(ignoringParenImpCasts(BindTemp)))))))
.bind("expr"),
this);
}
void UnusedRaiiCheck::check(const MatchFinder::MatchResult &Result) {
const auto *E = Result.Nodes.getNodeAs<Expr>("expr");
// We ignore code expanded from macros to reduce the number of false
// positives.
if (E->getLocStart().isMacroID())
return;
// Don't emit a warning for the last statement in the surrounding compund
// statement.
const auto *CS = Result.Nodes.getNodeAs<CompoundStmt>("compound");
if (E == CS->body_back())
return;
// Emit a warning.
auto D = diag(E->getLocStart(), "object destroyed immediately after "
"creation; did you mean to name the object?");
const char *Replacement = " give_me_a_name";
// If this is a default ctor we have to remove the parens or we'll introduce a
// most vexing parse.
const auto *BTE = Result.Nodes.getNodeAs<CXXBindTemporaryExpr>("temp");
if (const auto *TOE = dyn_cast<CXXTemporaryObjectExpr>(BTE->getSubExpr()))
if (TOE->getNumArgs() == 0) {
D << FixItHint::CreateReplacement(
CharSourceRange::getTokenRange(TOE->getParenOrBraceRange()),
Replacement);
return;
}
// Otherwise just suggest adding a name. To find the place to insert the name
// find the first TypeLoc in the children of E, which always points to the
// written type.
auto Matches =
match(expr(hasDescendant(typeLoc().bind("t"))), *E, *Result.Context);
const auto *TL = selectFirst<TypeLoc>("t", Matches);
D << FixItHint::CreateInsertion(
Lexer::getLocForEndOfToken(TL->getLocEnd(), 0, *Result.SourceManager,
getLangOpts()),
Replacement);
}
} // namespace bugprone
} // namespace tidy
} // namespace clang

View File

@@ -1,35 +0,0 @@
//===--- UnusedRaiiCheck.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_BUGPRONE_UNUSEDRAIICHECK_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_UNUSEDRAIICHECK_H
#include "../ClangTidy.h"
namespace clang {
namespace tidy {
namespace bugprone {
/// Finds temporaries that look like RAII objects.
///
/// For the user-facing documentation see:
/// http://clang.llvm.org/extra/clang-tidy/checks/bugprone-unused-raii.html
class UnusedRaiiCheck : public ClangTidyCheck {
public:
UnusedRaiiCheck(StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context) {}
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
};
} // namespace bugprone
} // namespace tidy
} // namespace clang
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_UNUSEDRAIICHECK_H

View File

@@ -1,100 +0,0 @@
//===--- UnusedReturnValueCheck.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 "UnusedReturnValueCheck.h"
#include "../utils/OptionsUtils.h"
#include "clang/AST/ASTContext.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
using namespace clang::ast_matchers;
using namespace clang::ast_matchers::internal;
namespace clang {
namespace tidy {
namespace bugprone {
namespace {
// Matches functions that are instantiated from a class template member function
// matching InnerMatcher. Functions not instantiated from a class template
// member function are matched directly with InnerMatcher.
AST_MATCHER_P(FunctionDecl, isInstantiatedFrom, Matcher<FunctionDecl>,
InnerMatcher) {
FunctionDecl *InstantiatedFrom = Node.getInstantiatedFromMemberFunction();
return InnerMatcher.matches(InstantiatedFrom ? *InstantiatedFrom : Node,
Finder, Builder);
}
} // namespace
UnusedReturnValueCheck::UnusedReturnValueCheck(llvm::StringRef Name,
ClangTidyContext *Context)
: ClangTidyCheck(Name, Context),
CheckedFunctions(Options.get("CheckedFunctions",
"::std::async;"
"::std::launder;"
"::std::remove;"
"::std::remove_if;"
"::std::unique;"
"::std::unique_ptr::release;"
"::std::basic_string::empty;"
"::std::vector::empty")) {}
void UnusedReturnValueCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
Options.store(Opts, "CheckedFunctions", CheckedFunctions);
}
void UnusedReturnValueCheck::registerMatchers(MatchFinder *Finder) {
auto FunVec = utils::options::parseStringList(CheckedFunctions);
auto MatchedCallExpr = expr(ignoringImplicit(ignoringParenImpCasts(
callExpr(callee(functionDecl(
// Don't match void overloads of checked functions.
unless(returns(voidType())),
isInstantiatedFrom(hasAnyName(
std::vector<StringRef>(FunVec.begin(), FunVec.end()))))))
.bind("match"))));
auto UnusedInCompoundStmt =
compoundStmt(forEach(MatchedCallExpr),
// The checker can't currently differentiate between the
// return statement and other statements inside GNU statement
// expressions, so disable the checker inside them to avoid
// false positives.
unless(hasParent(stmtExpr())));
auto UnusedInIfStmt =
ifStmt(eachOf(hasThen(MatchedCallExpr), hasElse(MatchedCallExpr)));
auto UnusedInWhileStmt = whileStmt(hasBody(MatchedCallExpr));
auto UnusedInDoStmt = doStmt(hasBody(MatchedCallExpr));
auto UnusedInForStmt =
forStmt(eachOf(hasLoopInit(MatchedCallExpr),
hasIncrement(MatchedCallExpr), hasBody(MatchedCallExpr)));
auto UnusedInRangeForStmt = cxxForRangeStmt(hasBody(MatchedCallExpr));
auto UnusedInCaseStmt = switchCase(forEach(MatchedCallExpr));
Finder->addMatcher(
stmt(anyOf(UnusedInCompoundStmt, UnusedInIfStmt, UnusedInWhileStmt,
UnusedInDoStmt, UnusedInForStmt, UnusedInRangeForStmt,
UnusedInCaseStmt)),
this);
}
void UnusedReturnValueCheck::check(const MatchFinder::MatchResult &Result) {
if (const auto *Matched = Result.Nodes.getNodeAs<CallExpr>("match")) {
diag(Matched->getLocStart(),
"the value returned by this function should be used")
<< Matched->getSourceRange();
diag(Matched->getLocStart(),
"cast the expression to void to silence this warning",
DiagnosticIDs::Note);
}
}
} // namespace bugprone
} // namespace tidy
} // namespace clang

View File

@@ -1,39 +0,0 @@
//===--- UnusedReturnValueCheck.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_BUGPRONE_UNUSEDRETURNVALUECHECK_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_UNUSEDRETURNVALUECHECK_H
#include "../ClangTidy.h"
#include <string>
namespace clang {
namespace tidy {
namespace bugprone {
/// Detects function calls where the return value is unused.
///
/// For the user-facing documentation see:
/// http://clang.llvm.org/extra/clang-tidy/checks/bugprone-unused-return-value.html
class UnusedReturnValueCheck : public ClangTidyCheck {
public:
UnusedReturnValueCheck(StringRef Name, ClangTidyContext *Context);
void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
private:
std::string CheckedFunctions;
};
} // namespace bugprone
} // namespace tidy
} // namespace clang
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_UNUSEDRETURNVALUECHECK_H

View File

@@ -19,13 +19,11 @@ namespace clang {
namespace tidy {
namespace bugprone {
namespace {
AST_MATCHER(CXXMethodDecl, isStatic) { return Node.isStatic(); }
AST_MATCHER(CXXMethodDecl, isOverloadedOperator) {
return Node.isOverloadedOperator();
}
} // namespace
/// Finds out if the given method overrides some method.
static bool isOverrideMethod(const CXXMethodDecl *MD) {

View File

@@ -21,7 +21,6 @@
#include "FloatLoopCounter.h"
#include "LimitedRandomnessCheck.h"
#include "PostfixOperatorCheck.h"
#include "ProperlySeededRandomGeneratorCheck.h"
#include "SetLongJmpCheck.h"
#include "StaticObjectExceptionCheck.h"
#include "StrToNumCheck.h"
@@ -59,8 +58,6 @@ public:
"cert-err61-cpp");
// MSC
CheckFactories.registerCheck<LimitedRandomnessCheck>("cert-msc50-cpp");
CheckFactories.registerCheck<ProperlySeededRandomGeneratorCheck>(
"cert-msc51-cpp");
// C checkers
// DCL
@@ -75,8 +72,6 @@ public:
CheckFactories.registerCheck<StrToNumCheck>("cert-err34-c");
// MSC
CheckFactories.registerCheck<LimitedRandomnessCheck>("cert-msc30-c");
CheckFactories.registerCheck<ProperlySeededRandomGeneratorCheck>(
"cert-msc32-c");
}
};

View File

@@ -7,7 +7,6 @@ add_clang_library(clangTidyCERTModule
FloatLoopCounter.cpp
LimitedRandomnessCheck.cpp
PostfixOperatorCheck.cpp
ProperlySeededRandomGeneratorCheck.cpp
SetLongJmpCheck.cpp
StaticObjectExceptionCheck.cpp
StrToNumCheck.cpp

View File

@@ -1,124 +0,0 @@
//===--- ProperlySeededRandomGeneratorCheck.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 "ProperlySeededRandomGeneratorCheck.h"
#include "clang/AST/ASTContext.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "llvm/ADT/STLExtras.h"
using namespace clang::ast_matchers;
namespace clang {
namespace tidy {
namespace cert {
ProperlySeededRandomGeneratorCheck::ProperlySeededRandomGeneratorCheck(
StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context),
RawDisallowedSeedTypes(
Options.get("DisallowedSeedTypes", "time_t,std::time_t")) {
StringRef(RawDisallowedSeedTypes).split(DisallowedSeedTypes, ',');
}
void ProperlySeededRandomGeneratorCheck::storeOptions(
ClangTidyOptions::OptionMap &Opts) {
Options.store(Opts, "DisallowedSeedTypes", RawDisallowedSeedTypes);
}
void ProperlySeededRandomGeneratorCheck::registerMatchers(MatchFinder *Finder) {
auto RandomGeneratorEngineDecl = cxxRecordDecl(hasAnyName(
"::std::linear_congruential_engine", "::std::mersenne_twister_engine",
"::std::subtract_with_carry_engine", "::std::discard_block_engine",
"::std::independent_bits_engine", "::std::shuffle_order_engine"));
auto RandomGeneratorEngineTypeMatcher = hasType(hasUnqualifiedDesugaredType(
recordType(hasDeclaration(RandomGeneratorEngineDecl))));
// std::mt19937 engine;
// engine.seed();
// ^
// engine.seed(1);
// ^
// const int x = 1;
// engine.seed(x);
// ^
Finder->addMatcher(
cxxMemberCallExpr(
has(memberExpr(has(declRefExpr(RandomGeneratorEngineTypeMatcher)),
member(hasName("seed")),
unless(hasDescendant(cxxThisExpr())))))
.bind("seed"),
this);
// std::mt19937 engine;
// ^
// std::mt19937 engine(1);
// ^
// const int x = 1;
// std::mt19937 engine(x);
// ^
Finder->addMatcher(
cxxConstructExpr(RandomGeneratorEngineTypeMatcher).bind("ctor"), this);
// srand();
// ^
// const int x = 1;
// srand(x);
// ^
Finder->addMatcher(
callExpr(callee(functionDecl(hasAnyName("::srand", "::std::srand"))))
.bind("srand"),
this);
}
void ProperlySeededRandomGeneratorCheck::check(
const MatchFinder::MatchResult &Result) {
const auto *Ctor = Result.Nodes.getNodeAs<CXXConstructExpr>("ctor");
if (Ctor)
checkSeed(Result, Ctor);
const auto *Func = Result.Nodes.getNodeAs<CXXMemberCallExpr>("seed");
if (Func)
checkSeed(Result, Func);
const auto *Srand = Result.Nodes.getNodeAs<CallExpr>("srand");
if (Srand)
checkSeed(Result, Srand);
}
template <class T>
void ProperlySeededRandomGeneratorCheck::checkSeed(
const MatchFinder::MatchResult &Result, const T *Func) {
if (Func->getNumArgs() == 0 || Func->getArg(0)->isDefaultArgument()) {
diag(Func->getExprLoc(),
"random number generator seeded with a default argument will generate "
"a predictable sequence of values");
return;
}
llvm::APSInt Value;
if (Func->getArg(0)->EvaluateAsInt(Value, *Result.Context)) {
diag(Func->getExprLoc(),
"random number generator seeded with a constant value will generate a "
"predictable sequence of values");
return;
}
const std::string SeedType(
Func->getArg(0)->IgnoreCasts()->getType().getAsString());
if (llvm::find(DisallowedSeedTypes, SeedType) != DisallowedSeedTypes.end()) {
diag(Func->getExprLoc(),
"random number generator seeded with a disallowed source of seed "
"value will generate a predictable sequence of values");
return;
}
}
} // namespace cert
} // namespace tidy
} // namespace clang

View File

@@ -1,47 +0,0 @@
//===--- ProperlySeededRandomGeneratorCheck.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_PROPERLY_SEEDED_RANDOM_GENERATOR_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CERT_PROPERLY_SEEDED_RANDOM_GENERATOR_H
#include "../ClangTidy.h"
#include <string>
namespace clang {
namespace tidy {
namespace cert {
/// Random number generator must be seeded properly.
///
/// A random number generator initialized with default value or a
/// constant expression is a security vulnerability.
///
/// For the user-facing documentation see:
/// http://clang.llvm.org/extra/clang-tidy/checks/cert-properly-seeded-random-generator.html
class ProperlySeededRandomGeneratorCheck : public ClangTidyCheck {
public:
ProperlySeededRandomGeneratorCheck(StringRef Name, ClangTidyContext *Context);
void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
private:
template <class T>
void checkSeed(const ast_matchers::MatchFinder::MatchResult &Result,
const T *Func);
std::string RawDisallowedSeedTypes;
SmallVector<StringRef, 5> DisallowedSeedTypes;
};
} // namespace cert
} // namespace tidy
} // namespace clang
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CERT_PROPERLY_SEEDED_RANDOM_GENERATOR_H

View File

@@ -1,57 +0,0 @@
//===--- AvoidGotoCheck.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 "AvoidGotoCheck.h"
#include "clang/AST/ASTContext.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
using namespace clang::ast_matchers;
namespace clang {
namespace tidy {
namespace cppcoreguidelines {
namespace {
AST_MATCHER(GotoStmt, isForwardJumping) {
return Node.getLocStart() < Node.getLabel()->getLocStart();
}
} // namespace
void AvoidGotoCheck::registerMatchers(MatchFinder *Finder) {
if (!getLangOpts().CPlusPlus)
return;
// TODO: This check does not recognize `IndirectGotoStmt` which is a
// GNU extension. These must be matched separately and an AST matcher
// is currently missing for them.
// Check if the 'goto' is used for control flow other than jumping
// out of a nested loop.
auto Loop = stmt(anyOf(forStmt(), cxxForRangeStmt(), whileStmt(), doStmt()));
auto NestedLoop =
stmt(anyOf(forStmt(hasAncestor(Loop)), cxxForRangeStmt(hasAncestor(Loop)),
whileStmt(hasAncestor(Loop)), doStmt(hasAncestor(Loop))));
Finder->addMatcher(gotoStmt(anyOf(unless(hasAncestor(NestedLoop)),
unless(isForwardJumping())))
.bind("goto"),
this);
}
void AvoidGotoCheck::check(const MatchFinder::MatchResult &Result) {
const auto *Goto = Result.Nodes.getNodeAs<GotoStmt>("goto");
diag(Goto->getGotoLoc(), "avoid using 'goto' for flow control")
<< Goto->getSourceRange();
diag(Goto->getLabel()->getLocStart(), "label defined here",
DiagnosticIDs::Note);
}
} // namespace cppcoreguidelines
} // namespace tidy
} // namespace clang

View File

@@ -1,36 +0,0 @@
//===--- AvoidGotoCheck.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_AVOIDGOTOCHECK_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_AVOIDGOTOCHECK_H
#include "../ClangTidy.h"
namespace clang {
namespace tidy {
namespace cppcoreguidelines {
/// The usage of ``goto`` for control flow is error prone and should be replaced
/// with looping constructs. Only forward jumps in nested loops are accepted.
//
/// For the user-facing documentation see:
/// http://clang.llvm.org/extra/clang-tidy/checks/cppcoreguidelines-avoid-goto.html
class AvoidGotoCheck : public ClangTidyCheck {
public:
AvoidGotoCheck(StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context) {}
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
};
} // namespace cppcoreguidelines
} // namespace tidy
} // namespace clang
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_AVOIDGOTOCHECK_H

View File

@@ -1,10 +1,8 @@
set(LLVM_LINK_COMPONENTS support)
add_clang_library(clangTidyCppCoreGuidelinesModule
AvoidGotoCheck.cpp
CppCoreGuidelinesTidyModule.cpp
InterfacesGlobalInitCheck.cpp
NarrowingConversionsCheck.cpp
NoMallocCheck.cpp
OwningMemoryCheck.cpp
ProBoundsArrayToPointerDecayCheck.cpp

View File

@@ -11,9 +11,7 @@
#include "../ClangTidyModule.h"
#include "../ClangTidyModuleRegistry.h"
#include "../misc/UnconventionalAssignOperatorCheck.h"
#include "AvoidGotoCheck.h"
#include "InterfacesGlobalInitCheck.h"
#include "NarrowingConversionsCheck.h"
#include "NoMallocCheck.h"
#include "OwningMemoryCheck.h"
#include "ProBoundsArrayToPointerDecayCheck.h"
@@ -37,12 +35,8 @@ namespace cppcoreguidelines {
class CppCoreGuidelinesModule : public ClangTidyModule {
public:
void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override {
CheckFactories.registerCheck<AvoidGotoCheck>(
"cppcoreguidelines-avoid-goto");
CheckFactories.registerCheck<InterfacesGlobalInitCheck>(
"cppcoreguidelines-interfaces-global-init");
CheckFactories.registerCheck<NarrowingConversionsCheck>(
"cppcoreguidelines-narrowing-conversions");
CheckFactories.registerCheck<NoMallocCheck>("cppcoreguidelines-no-malloc");
CheckFactories.registerCheck<OwningMemoryCheck>(
"cppcoreguidelines-owning-memory");

View File

@@ -1,70 +0,0 @@
//===--- NarrowingConversionsCheck.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 "NarrowingConversionsCheck.h"
#include "clang/AST/ASTContext.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
using namespace clang::ast_matchers;
namespace clang {
namespace tidy {
namespace cppcoreguidelines {
// FIXME: Check double -> float truncation. Pay attention to casts:
void NarrowingConversionsCheck::registerMatchers(MatchFinder *Finder) {
// ceil() and floor() are guaranteed to return integers, even though the type
// is not integral.
const auto IsCeilFloorCall = callExpr(callee(functionDecl(
hasAnyName("::ceil", "::std::ceil", "::floor", "::std::floor"))));
const auto IsFloatExpr =
expr(hasType(realFloatingPointType()), unless(IsCeilFloorCall));
// casts:
// i = 0.5;
// void f(int); f(0.5);
Finder->addMatcher(implicitCastExpr(hasImplicitDestinationType(isInteger()),
hasSourceExpression(IsFloatExpr),
unless(hasParent(castExpr())),
unless(isInTemplateInstantiation()))
.bind("cast"),
this);
// Binary operators:
// i += 0.5;
Finder->addMatcher(
binaryOperator(isAssignmentOperator(),
// The `=` case generates an implicit cast which is covered
// by the previous matcher.
unless(hasOperatorName("=")),
hasLHS(hasType(isInteger())), hasRHS(IsFloatExpr),
unless(isInTemplateInstantiation()))
.bind("op"),
this);
}
void NarrowingConversionsCheck::check(const MatchFinder::MatchResult &Result) {
if (const auto *Op = Result.Nodes.getNodeAs<BinaryOperator>("op")) {
if (Op->getLocStart().isMacroID())
return;
diag(Op->getOperatorLoc(), "narrowing conversion from %0 to %1")
<< Op->getRHS()->getType() << Op->getLHS()->getType();
return;
}
const auto *Cast = Result.Nodes.getNodeAs<ImplicitCastExpr>("cast");
if (Cast->getLocStart().isMacroID())
return;
diag(Cast->getExprLoc(), "narrowing conversion from %0 to %1")
<< Cast->getSubExpr()->getType() << Cast->getType();
}
} // namespace cppcoreguidelines
} // namespace tidy
} // namespace clang

View File

@@ -1,37 +0,0 @@
//===--- NarrowingConversionsCheck.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_NARROWING_CONVERSIONS_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_NARROWING_CONVERSIONS_H
#include "../ClangTidy.h"
namespace clang {
namespace tidy {
namespace cppcoreguidelines {
/// Checks for narrowing conversions, e.g:
/// int i = 0;
/// i += 0.1;
///
/// For the user-facing documentation see:
/// http://clang.llvm.org/extra/clang-tidy/checks/cppcoreguidelines-narrowing-conversions.html
class NarrowingConversionsCheck : public ClangTidyCheck {
public:
NarrowingConversionsCheck(StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context) {}
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
};
} // namespace cppcoreguidelines
} // namespace tidy
} // namespace clang
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_NARROWING_CONVERSIONS_H

View File

@@ -1,4 +1,4 @@
//===--- NoMallocCheck.cpp - clang-tidy------------------------------------===//
//===--- NoMallocCheck.cpp - clang-tidy------------------------------------===//
//
// The LLVM Compiler Infrastructure
//

View File

@@ -17,7 +17,6 @@ namespace clang {
namespace tidy {
namespace cppcoreguidelines {
namespace {
AST_MATCHER_P(CXXForRangeStmt, hasRangeBeginEndStmt,
ast_matchers::internal::Matcher<DeclStmt>, InnerMatcher) {
for (const DeclStmt *Stmt : {Node.getBeginStmt(), Node.getEndStmt()})
@@ -47,7 +46,6 @@ AST_MATCHER_P(Expr, hasParentIgnoringImpCasts,
return InnerMatcher.matches(*E, Finder, Builder);
}
} // namespace
void ProBoundsArrayToPointerDecayCheck::registerMatchers(MatchFinder *Finder) {
if (!getLangOpts().CPlusPlus)

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