박우진

Add llvm-project Codes

Showing 1000 changed files with 4873 additions and 0 deletions

Too many changes to show.

To preserve performance only 1000 of 1000+ files are displayed.

1 +BasedOnStyle: LLVM
1 +Checks: '-*,clang-diagnostic-*,llvm-*,misc-*,-misc-unused-parameters,-misc-non-private-member-variables-in-classes,readability-identifier-naming'
2 +CheckOptions:
3 + - key: readability-identifier-naming.ClassCase
4 + value: CamelCase
5 + - key: readability-identifier-naming.EnumCase
6 + value: CamelCase
7 + - key: readability-identifier-naming.FunctionCase
8 + value: camelBack
9 + - key: readability-identifier-naming.MemberCase
10 + value: CamelCase
11 + - key: readability-identifier-naming.ParameterCase
12 + value: CamelCase
13 + - key: readability-identifier-naming.UnionCase
14 + value: CamelCase
15 + - key: readability-identifier-naming.VariableCase
16 + value: CamelCase
17 + - key: readability-identifier-naming.IgnoreMainLikeFunctions
18 + value: 1
19 +
1 +# Contributing to LLVM
2 +
3 +Thank you for your interest in contributing to LLVM! There are many ways to
4 +contribute, and we appreciate all contributions.
5 +
6 +To get started with contributing, please take a look at the
7 +[Contributing to LLVM](https://llvm.org/docs/Contributing.html) guide. It
8 +describes how to get involved, raise issues and submit patches. Please note
9 +that at the moment the LLVM project does not use either Github pull requests
10 +or Github issues.
1 +# The LLVM Compiler Infrastructure
2 +
3 +This directory and its sub-directories contain source code for LLVM,
4 +a toolkit for the construction of highly optimized compilers,
5 +optimizers, and run-time environments.
6 +
7 +The README briefly describes how to get started with building LLVM.
8 +For more information on how to contribute to the LLVM project, please
9 +take a look at the
10 +[Contributing to LLVM](https://llvm.org/docs/Contributing.html) guide.
11 +
12 +## Getting Started with the LLVM System
13 +
14 +Taken from https://llvm.org/docs/GettingStarted.html.
15 +
16 +### Overview
17 +
18 +Welcome to the LLVM project!
19 +
20 +The LLVM project has multiple components. The core of the project is
21 +itself called "LLVM". This contains all of the tools, libraries, and header
22 +files needed to process intermediate representations and converts it into
23 +object files. Tools include an assembler, disassembler, bitcode analyzer, and
24 +bitcode optimizer. It also contains basic regression tests.
25 +
26 +C-like languages use the [Clang](http://clang.llvm.org/) front end. This
27 +component compiles C, C++, Objective-C, and Objective-C++ code into LLVM bitcode
28 +-- and from there into object files, using LLVM.
29 +
30 +Other components include:
31 +the [libc++ C++ standard library](https://libcxx.llvm.org),
32 +the [LLD linker](https://lld.llvm.org), and more.
33 +
34 +### Getting the Source Code and Building LLVM
35 +
36 +The LLVM Getting Started documentation may be out of date. The [Clang
37 +Getting Started](http://clang.llvm.org/get_started.html) page might have more
38 +accurate information.
39 +
40 +This is an example work-flow and configuration to get and build the LLVM source:
41 +
42 +1. Checkout LLVM (including related sub-projects like Clang):
43 +
44 + * ``git clone https://github.com/llvm/llvm-project.git``
45 +
46 + * Or, on windows, ``git clone --config core.autocrlf=false
47 + https://github.com/llvm/llvm-project.git``
48 +
49 +2. Configure and build LLVM and Clang:
50 +
51 + * ``cd llvm-project``
52 +
53 + * ``mkdir build``
54 +
55 + * ``cd build``
56 +
57 + * ``cmake -G <generator> [options] ../llvm``
58 +
59 + Some common build system generators are:
60 +
61 + * ``Ninja`` --- for generating [Ninja](https://ninja-build.org)
62 + build files. Most llvm developers use Ninja.
63 + * ``Unix Makefiles`` --- for generating make-compatible parallel makefiles.
64 + * ``Visual Studio`` --- for generating Visual Studio projects and
65 + solutions.
66 + * ``Xcode`` --- for generating Xcode projects.
67 +
68 + Some Common options:
69 +
70 + * ``-DLLVM_ENABLE_PROJECTS='...'`` --- semicolon-separated list of the LLVM
71 + sub-projects you'd like to additionally build. Can include any of: clang,
72 + clang-tools-extra, libcxx, libcxxabi, libunwind, lldb, compiler-rt, lld,
73 + polly, or debuginfo-tests.
74 +
75 + For example, to build LLVM, Clang, libcxx, and libcxxabi, use
76 + ``-DLLVM_ENABLE_PROJECTS="clang;libcxx;libcxxabi"``.
77 +
78 + * ``-DCMAKE_INSTALL_PREFIX=directory`` --- Specify for *directory* the full
79 + path name of where you want the LLVM tools and libraries to be installed
80 + (default ``/usr/local``).
81 +
82 + * ``-DCMAKE_BUILD_TYPE=type`` --- Valid options for *type* are Debug,
83 + Release, RelWithDebInfo, and MinSizeRel. Default is Debug.
84 +
85 + * ``-DLLVM_ENABLE_ASSERTIONS=On`` --- Compile with assertion checks enabled
86 + (default is Yes for Debug builds, No for all other build types).
87 +
88 + * ``cmake --build . [-- [options] <target>]`` or your build system specified above
89 + directly.
90 +
91 + * The default target (i.e. ``ninja`` or ``make``) will build all of LLVM.
92 +
93 + * The ``check-all`` target (i.e. ``ninja check-all``) will run the
94 + regression tests to ensure everything is in working order.
95 +
96 + * CMake will generate targets for each tool and library, and most
97 + LLVM sub-projects generate their own ``check-<project>`` target.
98 +
99 + * Running a serial build will be **slow**. To improve speed, try running a
100 + parallel build. That's done by default in Ninja; for ``make``, use the option
101 + ``-j NNN``, where ``NNN`` is the number of parallel jobs, e.g. the number of
102 + CPUs you have.
103 +
104 + * For more information see [CMake](https://llvm.org/docs/CMake.html)
105 +
106 +Consult the
107 +[Getting Started with LLVM](https://llvm.org/docs/GettingStarted.html#getting-started-with-llvm)
108 +page for detailed information on configuring and compiling LLVM. You can visit
109 +[Directory Layout](https://llvm.org/docs/GettingStarted.html#directory-layout)
110 +to learn about the layout of the source code tree.
1 +#==============================================================================#
2 +# This file specifies intentionally untracked files that git should ignore.
3 +# See: http://www.kernel.org/pub/software/scm/git/docs/gitignore.html
4 +#
5 +# This file is intentionally different from the output of `git svn show-ignore`,
6 +# as most of those are useless.
7 +#==============================================================================#
8 +
9 +#==============================================================================#
10 +# File extensions to be ignored anywhere in the tree.
11 +#==============================================================================#
12 +# Temp files created by most text editors.
13 +*~
14 +# Merge files created by git.
15 +*.orig
16 +# Byte compiled python modules.
17 +*.pyc
18 +# vim swap files
19 +.*.swp
20 +.sw?
21 +
22 +#==============================================================================#
23 +# Explicit files to ignore (only matches one).
24 +#==============================================================================#
25 +cscope.files
26 +cscope.out
27 +.clang_complete
28 +
29 +#==============================================================================#
30 +# Directories to ignore (do not add trailing '/'s, they skip symlinks).
31 +#==============================================================================#
32 +docs/_build
1 +include(CMakeDependentOption)
2 +
3 +option(CLANG_TIDY_ENABLE_STATIC_ANALYZER
4 + "Include static analyzer checks in clang-tidy" ON)
5 +
6 +add_subdirectory(clang-apply-replacements)
7 +add_subdirectory(clang-reorder-fields)
8 +add_subdirectory(modularize)
9 +add_subdirectory(clang-tidy)
10 +
11 +add_subdirectory(clang-change-namespace)
12 +add_subdirectory(clang-doc)
13 +add_subdirectory(clang-include-fixer)
14 +add_subdirectory(clang-move)
15 +add_subdirectory(clang-query)
16 +add_subdirectory(pp-trace)
17 +add_subdirectory(tool-template)
18 +
19 +# Add the common testsuite after all the tools.
20 +if(CLANG_INCLUDE_TESTS)
21 +add_subdirectory(test)
22 +add_subdirectory(unittests)
23 +endif()
24 +
25 +option(CLANG_TOOLS_EXTRA_INCLUDE_DOCS "Generate build targets for the Clang Extra Tools docs."
26 + ${LLVM_INCLUDE_DOCS})
27 +if( CLANG_TOOLS_EXTRA_INCLUDE_DOCS )
28 + add_subdirectory(docs)
29 +endif()
30 +
31 +# clangd has its own CMake tree. It requires threads.
32 +CMAKE_DEPENDENT_OPTION(CLANG_ENABLE_CLANGD "Build clangd language server" ON
33 + "LLVM_ENABLE_THREADS" OFF)
34 +if (CLANG_ENABLE_CLANGD)
35 + add_subdirectory(clangd)
36 +endif()
1 +This file is a list of the people responsible for ensuring that patches for a
2 +particular tool are reviewed, either by themself or by someone else. They are
3 +also the gatekeepers for their part of Clang, with the final word on what goes
4 +in or not.
5 +
6 +The list is sorted by surname and formatted to allow easy grepping and
7 +beautification by scripts. The fields are: name (N), email (E), web-address
8 +(W), PGP key ID and fingerprint (P), description (D), and snail-mail address
9 +(S).
10 +
11 +N: Aaron Ballman
12 +E: aaron@aaronballman.com
13 +D: clang-query
14 +
15 +N: Manuel Klimek
16 +E: klimek@google.com
17 +D: clang-rename, all parts of clang-tools-extra not covered by someone else
18 +
19 +N: Alexander Kornienko
20 +E: alexfh@google.com
21 +D: clang-tidy
22 +
23 +N: Julie Hockett
24 +E: juliehockett@google.com
25 +D: clang-doc
26 +
27 +N: Sam McCall
28 +E: sammccall@google.com
29 +D: clangd
This diff is collapsed. Click to expand it.
1 +//===----------------------------------------------------------------------===//
2 +// Clang Tools repository
3 +//===----------------------------------------------------------------------===//
4 +
5 +Welcome to the repository of extra Clang Tools. This repository holds tools
6 +that are developed as part of the LLVM compiler infrastructure project and the
7 +Clang frontend. These tools are kept in a separate "extra" repository to
8 +allow lighter weight checkouts of the core Clang codebase.
9 +
10 +This repository is only intended to be checked out inside of a full LLVM+Clang
11 +tree, and in the 'tools/extra' subdirectory of the Clang checkout.
12 +
13 +All discussion regarding Clang, Clang-based tools, and code in this repository
14 +should be held using the standard Clang mailing lists:
15 + http://lists.llvm.org/mailman/listinfo/cfe-dev
16 +
17 +Code review for this tree should take place on the standard Clang patch and
18 +commit lists:
19 + http://lists.llvm.org/mailman/listinfo/cfe-commits
20 +
21 +If you find a bug in these tools, please file it in the LLVM bug tracker:
22 + http://llvm.org/bugs/
1 +set(LLVM_LINK_COMPONENTS
2 + Support
3 + )
4 +
5 +add_clang_library(clangApplyReplacements
6 + lib/Tooling/ApplyReplacements.cpp
7 +)
8 +
9 +clang_target_link_libraries(clangApplyReplacements
10 + PRIVATE
11 + clangAST
12 + clangBasic
13 + clangRewrite
14 + clangToolingCore
15 + clangToolingRefactoring
16 + )
17 +
18 +include_directories(
19 + ${CMAKE_CURRENT_SOURCE_DIR}
20 + include
21 + )
22 +add_subdirectory(tool)
1 +//===-- ApplyReplacements.h - Deduplicate and apply replacements -- C++ -*-===//
2 +//
3 +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 +// See https://llvm.org/LICENSE.txt for license information.
5 +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 +//
7 +//===----------------------------------------------------------------------===//
8 +///
9 +/// \file
10 +/// This file provides the interface for deduplicating, detecting
11 +/// conflicts in, and applying collections of Replacements.
12 +///
13 +//===----------------------------------------------------------------------===//
14 +
15 +#ifndef LLVM_CLANG_APPLYREPLACEMENTS_H
16 +#define LLVM_CLANG_APPLYREPLACEMENTS_H
17 +
18 +#include "clang/Tooling/Core/Diagnostic.h"
19 +#include "clang/Tooling/Refactoring.h"
20 +#include "clang/Tooling/Refactoring/AtomicChange.h"
21 +#include "llvm/ADT/StringMap.h"
22 +#include "llvm/ADT/StringRef.h"
23 +#include <string>
24 +#include <system_error>
25 +#include <vector>
26 +
27 +namespace clang {
28 +
29 +class DiagnosticsEngine;
30 +class Rewriter;
31 +
32 +namespace replace {
33 +
34 +/// Collection of TranslationUnitReplacements.
35 +typedef std::vector<clang::tooling::TranslationUnitReplacements> TUReplacements;
36 +
37 +/// Collection of TranslationUnitReplacement files.
38 +typedef std::vector<std::string> TUReplacementFiles;
39 +
40 +/// Collection of TranslationUniDiagnostics.
41 +typedef std::vector<clang::tooling::TranslationUnitDiagnostics> TUDiagnostics;
42 +
43 +/// Map mapping file name to a set of AtomicChange targeting that file.
44 +typedef llvm::DenseMap<const clang::FileEntry *,
45 + std::vector<tooling::AtomicChange>>
46 + FileToChangesMap;
47 +
48 +/// Recursively descends through a directory structure rooted at \p
49 +/// Directory and attempts to deserialize *.yaml files as
50 +/// TranslationUnitReplacements. All docs that successfully deserialize are
51 +/// added to \p TUs.
52 +///
53 +/// Directories starting with '.' are ignored during traversal.
54 +///
55 +/// \param[in] Directory Directory to begin search for serialized
56 +/// TranslationUnitReplacements.
57 +/// \param[out] TUs Collection of all found and deserialized
58 +/// TranslationUnitReplacements or TranslationUnitDiagnostics.
59 +/// \param[out] TUFiles Collection of all TranslationUnitReplacement files
60 +/// found in \c Directory.
61 +/// \param[in] Diagnostics DiagnosticsEngine used for error output.
62 +///
63 +/// \returns An error_code indicating success or failure in navigating the
64 +/// directory structure.
65 +std::error_code collectReplacementsFromDirectory(
66 + const llvm::StringRef Directory, TUReplacements &TUs,
67 + TUReplacementFiles &TUFiles, clang::DiagnosticsEngine &Diagnostics);
68 +
69 +std::error_code collectReplacementsFromDirectory(
70 + const llvm::StringRef Directory, TUDiagnostics &TUs,
71 + TUReplacementFiles &TUFiles, clang::DiagnosticsEngine &Diagnostics);
72 +
73 +/// Deduplicate, check for conflicts, and extract all Replacements stored
74 +/// in \c TUs. Conflicting replacements are skipped.
75 +///
76 +/// \post For all (key,value) in FileChanges, value[i].getOffset() <=
77 +/// value[i+1].getOffset().
78 +///
79 +/// \param[in] TUs Collection of TranslationUnitReplacements or
80 +/// TranslationUnitDiagnostics to merge, deduplicate, and test for conflicts.
81 +/// \param[out] FileChanges Container grouping all changes by the
82 +/// file they target. Only non conflicting replacements are kept into
83 +/// FileChanges.
84 +/// \param[in] SM SourceManager required for conflict reporting.
85 +///
86 +/// \returns \parblock
87 +/// \li true If all changes were converted successfully.
88 +/// \li false If there were conflicts.
89 +bool mergeAndDeduplicate(const TUReplacements &TUs, const TUDiagnostics &TUDs,
90 + FileToChangesMap &FileChanges,
91 + clang::SourceManager &SM);
92 +
93 +/// Apply \c AtomicChange on File and rewrite it.
94 +///
95 +/// \param[in] File Path of the file where to apply AtomicChange.
96 +/// \param[in] Changes to apply.
97 +/// \param[in] Spec For code cleanup and formatting.
98 +/// \param[in] Diagnostics DiagnosticsEngine used for error output.
99 +///
100 +/// \returns The changed code if all changes are applied successfully;
101 +/// otherwise, an llvm::Error carrying llvm::StringError or an error_code.
102 +llvm::Expected<std::string>
103 +applyChanges(StringRef File, const std::vector<tooling::AtomicChange> &Changes,
104 + const tooling::ApplyChangesSpec &Spec,
105 + DiagnosticsEngine &Diagnostics);
106 +
107 +/// Delete the replacement files.
108 +///
109 +/// \param[in] Files Replacement files to delete.
110 +/// \param[in] Diagnostics DiagnosticsEngine used for error output.
111 +///
112 +/// \returns \parblock
113 +/// \li true If all files have been deleted successfully.
114 +/// \li false If at least one or more failures occur when deleting
115 +/// files.
116 +bool deleteReplacementFiles(const TUReplacementFiles &Files,
117 + clang::DiagnosticsEngine &Diagnostics);
118 +
119 +} // end namespace replace
120 +} // end namespace clang
121 +
122 +#endif // LLVM_CLANG_APPLYREPLACEMENTS_H
1 +//===-- ApplyReplacements.cpp - Apply and deduplicate replacements --------===//
2 +//
3 +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 +// See https://llvm.org/LICENSE.txt for license information.
5 +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 +//
7 +//===----------------------------------------------------------------------===//
8 +///
9 +/// \file
10 +/// This file provides the implementation for deduplicating, detecting
11 +/// conflicts in, and applying collections of Replacements.
12 +///
13 +/// FIXME: Use Diagnostics for output instead of llvm::errs().
14 +///
15 +//===----------------------------------------------------------------------===//
16 +#include "clang-apply-replacements/Tooling/ApplyReplacements.h"
17 +#include "clang/Basic/LangOptions.h"
18 +#include "clang/Basic/SourceManager.h"
19 +#include "clang/Format/Format.h"
20 +#include "clang/Lex/Lexer.h"
21 +#include "clang/Rewrite/Core/Rewriter.h"
22 +#include "clang/Tooling/Core/Diagnostic.h"
23 +#include "clang/Tooling/DiagnosticsYaml.h"
24 +#include "clang/Tooling/ReplacementsYaml.h"
25 +#include "llvm/ADT/ArrayRef.h"
26 +#include "llvm/Support/FileSystem.h"
27 +#include "llvm/Support/MemoryBuffer.h"
28 +#include "llvm/Support/Path.h"
29 +#include "llvm/Support/raw_ostream.h"
30 +
31 +using namespace llvm;
32 +using namespace clang;
33 +
34 +static void eatDiagnostics(const SMDiagnostic &, void *) {}
35 +
36 +namespace clang {
37 +namespace replace {
38 +
39 +std::error_code collectReplacementsFromDirectory(
40 + const llvm::StringRef Directory, TUReplacements &TUs,
41 + TUReplacementFiles &TUFiles, clang::DiagnosticsEngine &Diagnostics) {
42 + using namespace llvm::sys::fs;
43 + using namespace llvm::sys::path;
44 +
45 + std::error_code ErrorCode;
46 +
47 + for (recursive_directory_iterator I(Directory, ErrorCode), E;
48 + I != E && !ErrorCode; I.increment(ErrorCode)) {
49 + if (filename(I->path())[0] == '.') {
50 + // Indicate not to descend into directories beginning with '.'
51 + I.no_push();
52 + continue;
53 + }
54 +
55 + if (extension(I->path()) != ".yaml")
56 + continue;
57 +
58 + TUFiles.push_back(I->path());
59 +
60 + ErrorOr<std::unique_ptr<MemoryBuffer>> Out =
61 + MemoryBuffer::getFile(I->path());
62 + if (std::error_code BufferError = Out.getError()) {
63 + errs() << "Error reading " << I->path() << ": " << BufferError.message()
64 + << "\n";
65 + continue;
66 + }
67 +
68 + yaml::Input YIn(Out.get()->getBuffer(), nullptr, &eatDiagnostics);
69 + tooling::TranslationUnitReplacements TU;
70 + YIn >> TU;
71 + if (YIn.error()) {
72 + // File doesn't appear to be a header change description. Ignore it.
73 + continue;
74 + }
75 +
76 + // Only keep files that properly parse.
77 + TUs.push_back(TU);
78 + }
79 +
80 + return ErrorCode;
81 +}
82 +
83 +std::error_code collectReplacementsFromDirectory(
84 + const llvm::StringRef Directory, TUDiagnostics &TUs,
85 + TUReplacementFiles &TUFiles, clang::DiagnosticsEngine &Diagnostics) {
86 + using namespace llvm::sys::fs;
87 + using namespace llvm::sys::path;
88 +
89 + std::error_code ErrorCode;
90 +
91 + for (recursive_directory_iterator I(Directory, ErrorCode), E;
92 + I != E && !ErrorCode; I.increment(ErrorCode)) {
93 + if (filename(I->path())[0] == '.') {
94 + // Indicate not to descend into directories beginning with '.'
95 + I.no_push();
96 + continue;
97 + }
98 +
99 + if (extension(I->path()) != ".yaml")
100 + continue;
101 +
102 + TUFiles.push_back(I->path());
103 +
104 + ErrorOr<std::unique_ptr<MemoryBuffer>> Out =
105 + MemoryBuffer::getFile(I->path());
106 + if (std::error_code BufferError = Out.getError()) {
107 + errs() << "Error reading " << I->path() << ": " << BufferError.message()
108 + << "\n";
109 + continue;
110 + }
111 +
112 + yaml::Input YIn(Out.get()->getBuffer(), nullptr, &eatDiagnostics);
113 + tooling::TranslationUnitDiagnostics TU;
114 + YIn >> TU;
115 + if (YIn.error()) {
116 + // File doesn't appear to be a header change description. Ignore it.
117 + continue;
118 + }
119 +
120 + // Only keep files that properly parse.
121 + TUs.push_back(TU);
122 + }
123 +
124 + return ErrorCode;
125 +}
126 +
127 +/// Extract replacements from collected TranslationUnitReplacements and
128 +/// TranslationUnitDiagnostics and group them per file. Identical replacements
129 +/// from diagnostics are deduplicated.
130 +///
131 +/// \param[in] TUs Collection of all found and deserialized
132 +/// TranslationUnitReplacements.
133 +/// \param[in] TUDs Collection of all found and deserialized
134 +/// TranslationUnitDiagnostics.
135 +/// \param[in] SM Used to deduplicate paths.
136 +///
137 +/// \returns A map mapping FileEntry to a set of Replacement targeting that
138 +/// file.
139 +static llvm::DenseMap<const FileEntry *, std::vector<tooling::Replacement>>
140 +groupReplacements(const TUReplacements &TUs, const TUDiagnostics &TUDs,
141 + const clang::SourceManager &SM) {
142 + std::set<StringRef> Warned;
143 + llvm::DenseMap<const FileEntry *, std::vector<tooling::Replacement>>
144 + GroupedReplacements;
145 +
146 + // Deduplicate identical replacements in diagnostics unless they are from the
147 + // same TU.
148 + // FIXME: Find an efficient way to deduplicate on diagnostics level.
149 + llvm::DenseMap<const FileEntry *,
150 + std::map<tooling::Replacement,
151 + const tooling::TranslationUnitDiagnostics *>>
152 + DiagReplacements;
153 +
154 + auto AddToGroup = [&](const tooling::Replacement &R,
155 + const tooling::TranslationUnitDiagnostics *SourceTU) {
156 + // Use the file manager to deduplicate paths. FileEntries are
157 + // automatically canonicalized.
158 + if (auto Entry = SM.getFileManager().getFile(R.getFilePath())) {
159 + if (SourceTU) {
160 + auto &Replaces = DiagReplacements[*Entry];
161 + auto It = Replaces.find(R);
162 + if (It == Replaces.end())
163 + Replaces.emplace(R, SourceTU);
164 + else if (It->second != SourceTU)
165 + // This replacement is a duplicate of one suggested by another TU.
166 + return;
167 + }
168 + GroupedReplacements[*Entry].push_back(R);
169 + } else if (Warned.insert(R.getFilePath()).second) {
170 + errs() << "Described file '" << R.getFilePath()
171 + << "' doesn't exist. Ignoring...\n";
172 + }
173 + };
174 +
175 + for (const auto &TU : TUs)
176 + for (const tooling::Replacement &R : TU.Replacements)
177 + AddToGroup(R, nullptr);
178 +
179 + for (const auto &TU : TUDs)
180 + for (const auto &D : TU.Diagnostics)
181 + if (const auto *ChoosenFix = tooling::selectFirstFix(D)) {
182 + for (const auto &Fix : *ChoosenFix)
183 + for (const tooling::Replacement &R : Fix.second)
184 + AddToGroup(R, &TU);
185 + }
186 +
187 + // Sort replacements per file to keep consistent behavior when
188 + // clang-apply-replacements run on differents machine.
189 + for (auto &FileAndReplacements : GroupedReplacements) {
190 + llvm::sort(FileAndReplacements.second.begin(),
191 + FileAndReplacements.second.end());
192 + }
193 +
194 + return GroupedReplacements;
195 +}
196 +
197 +bool mergeAndDeduplicate(const TUReplacements &TUs, const TUDiagnostics &TUDs,
198 + FileToChangesMap &FileChanges,
199 + clang::SourceManager &SM) {
200 + auto GroupedReplacements = groupReplacements(TUs, TUDs, SM);
201 + bool ConflictDetected = false;
202 +
203 + // To report conflicting replacements on corresponding file, all replacements
204 + // are stored into 1 big AtomicChange.
205 + for (const auto &FileAndReplacements : GroupedReplacements) {
206 + const FileEntry *Entry = FileAndReplacements.first;
207 + const SourceLocation BeginLoc =
208 + SM.getLocForStartOfFile(SM.getOrCreateFileID(Entry, SrcMgr::C_User));
209 + tooling::AtomicChange FileChange(Entry->getName(), Entry->getName());
210 + for (const auto &R : FileAndReplacements.second) {
211 + llvm::Error Err =
212 + FileChange.replace(SM, BeginLoc.getLocWithOffset(R.getOffset()),
213 + R.getLength(), R.getReplacementText());
214 + if (Err) {
215 + // FIXME: This will report conflicts by pair using a file+offset format
216 + // which is not so much human readable.
217 + // A first improvement could be to translate offset to line+col. For
218 + // this and without loosing error message some modifications around
219 + // `tooling::ReplacementError` are need (access to
220 + // `getReplacementErrString`).
221 + // A better strategy could be to add a pretty printer methods for
222 + // conflict reporting. Methods that could be parameterized to report a
223 + // conflict in different format, file+offset, file+line+col, or even
224 + // more human readable using VCS conflict markers.
225 + // For now, printing directly the error reported by `AtomicChange` is
226 + // the easiest solution.
227 + errs() << llvm::toString(std::move(Err)) << "\n";
228 + ConflictDetected = true;
229 + }
230 + }
231 + FileChanges.try_emplace(Entry,
232 + std::vector<tooling::AtomicChange>{FileChange});
233 + }
234 +
235 + return !ConflictDetected;
236 +}
237 +
238 +llvm::Expected<std::string>
239 +applyChanges(StringRef File, const std::vector<tooling::AtomicChange> &Changes,
240 + const tooling::ApplyChangesSpec &Spec,
241 + DiagnosticsEngine &Diagnostics) {
242 + FileManager Files((FileSystemOptions()));
243 + SourceManager SM(Diagnostics, Files);
244 +
245 + llvm::ErrorOr<std::unique_ptr<MemoryBuffer>> Buffer =
246 + SM.getFileManager().getBufferForFile(File);
247 + if (!Buffer)
248 + return errorCodeToError(Buffer.getError());
249 + return tooling::applyAtomicChanges(File, Buffer.get()->getBuffer(), Changes,
250 + Spec);
251 +}
252 +
253 +bool deleteReplacementFiles(const TUReplacementFiles &Files,
254 + clang::DiagnosticsEngine &Diagnostics) {
255 + bool Success = true;
256 + for (const auto &Filename : Files) {
257 + std::error_code Error = llvm::sys::fs::remove(Filename);
258 + if (Error) {
259 + Success = false;
260 + // FIXME: Use Diagnostics for outputting errors.
261 + errs() << "Error deleting file: " << Filename << "\n";
262 + errs() << Error.message() << "\n";
263 + errs() << "Please delete the file manually\n";
264 + }
265 + }
266 + return Success;
267 +}
268 +
269 +} // end namespace replace
270 +} // end namespace clang
1 +set(LLVM_LINK_COMPONENTS
2 + Support
3 + )
4 +
5 +add_clang_tool(clang-apply-replacements
6 + ClangApplyReplacementsMain.cpp
7 + )
8 +clang_target_link_libraries(clang-apply-replacements
9 + PRIVATE
10 + clangBasic
11 + clangFormat
12 + clangRewrite
13 + clangToolingCore
14 + clangToolingRefactoring
15 + )
16 +target_link_libraries(clang-apply-replacements
17 + PRIVATE
18 + clangApplyReplacements
19 + )
1 +//===-- ClangApplyReplacementsMain.cpp - Main file for the tool -----------===//
2 +//
3 +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 +// See https://llvm.org/LICENSE.txt for license information.
5 +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 +//
7 +//===----------------------------------------------------------------------===//
8 +///
9 +/// \file
10 +/// This file provides the main function for the
11 +/// clang-apply-replacements tool.
12 +///
13 +//===----------------------------------------------------------------------===//
14 +
15 +#include "clang-apply-replacements/Tooling/ApplyReplacements.h"
16 +#include "clang/Basic/Diagnostic.h"
17 +#include "clang/Basic/DiagnosticOptions.h"
18 +#include "clang/Basic/SourceManager.h"
19 +#include "clang/Basic/Version.h"
20 +#include "clang/Format/Format.h"
21 +#include "clang/Rewrite/Core/Rewriter.h"
22 +#include "llvm/ADT/STLExtras.h"
23 +#include "llvm/ADT/StringSet.h"
24 +#include "llvm/Support/CommandLine.h"
25 +
26 +using namespace llvm;
27 +using namespace clang;
28 +using namespace clang::replace;
29 +
30 +static cl::opt<std::string> Directory(cl::Positional, cl::Required,
31 + cl::desc("<Search Root Directory>"));
32 +
33 +static cl::OptionCategory ReplacementCategory("Replacement Options");
34 +static cl::OptionCategory FormattingCategory("Formatting Options");
35 +
36 +const cl::OptionCategory *VisibleCategories[] = {&ReplacementCategory,
37 + &FormattingCategory};
38 +
39 +static cl::opt<bool> RemoveTUReplacementFiles(
40 + "remove-change-desc-files",
41 + cl::desc("Remove the change description files regardless of successful\n"
42 + "merging/replacing."),
43 + cl::init(false), cl::cat(ReplacementCategory));
44 +
45 +static cl::opt<bool> DoFormat(
46 + "format",
47 + cl::desc("Enable formatting of code changed by applying replacements.\n"
48 + "Use -style to choose formatting style.\n"),
49 + cl::cat(FormattingCategory));
50 +
51 +// FIXME: Consider making the default behaviour for finding a style
52 +// configuration file to start the search anew for every file being changed to
53 +// handle situations where the style is different for different parts of a
54 +// project.
55 +
56 +static cl::opt<std::string> FormatStyleConfig(
57 + "style-config",
58 + cl::desc("Path to a directory containing a .clang-format file\n"
59 + "describing a formatting style to use for formatting\n"
60 + "code when -style=file.\n"),
61 + cl::init(""), cl::cat(FormattingCategory));
62 +
63 +static cl::opt<std::string>
64 + FormatStyleOpt("style", cl::desc(format::StyleOptionHelpDescription),
65 + cl::init("LLVM"), cl::cat(FormattingCategory));
66 +
67 +namespace {
68 +// Helper object to remove the TUReplacement and TUDiagnostic (triggered by
69 +// "remove-change-desc-files" command line option) when exiting current scope.
70 +class ScopedFileRemover {
71 +public:
72 + ScopedFileRemover(const TUReplacementFiles &Files,
73 + clang::DiagnosticsEngine &Diagnostics)
74 + : TURFiles(Files), Diag(Diagnostics) {}
75 +
76 + ~ScopedFileRemover() { deleteReplacementFiles(TURFiles, Diag); }
77 +
78 +private:
79 + const TUReplacementFiles &TURFiles;
80 + clang::DiagnosticsEngine &Diag;
81 +};
82 +} // namespace
83 +
84 +static void printVersion(raw_ostream &OS) {
85 + OS << "clang-apply-replacements version " CLANG_VERSION_STRING << "\n";
86 +}
87 +
88 +int main(int argc, char **argv) {
89 + cl::HideUnrelatedOptions(makeArrayRef(VisibleCategories));
90 +
91 + cl::SetVersionPrinter(printVersion);
92 + cl::ParseCommandLineOptions(argc, argv);
93 +
94 + IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts(new DiagnosticOptions());
95 + DiagnosticsEngine Diagnostics(
96 + IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs()), DiagOpts.get());
97 +
98 + // Determine a formatting style from options.
99 + auto FormatStyleOrError = format::getStyle(FormatStyleOpt, FormatStyleConfig,
100 + format::DefaultFallbackStyle);
101 + if (!FormatStyleOrError) {
102 + llvm::errs() << llvm::toString(FormatStyleOrError.takeError()) << "\n";
103 + return 1;
104 + }
105 + format::FormatStyle FormatStyle = std::move(*FormatStyleOrError);
106 +
107 + TUReplacements TURs;
108 + TUReplacementFiles TUFiles;
109 +
110 + std::error_code ErrorCode =
111 + collectReplacementsFromDirectory(Directory, TURs, TUFiles, Diagnostics);
112 +
113 + TUDiagnostics TUDs;
114 + TUFiles.clear();
115 + ErrorCode =
116 + collectReplacementsFromDirectory(Directory, TUDs, TUFiles, Diagnostics);
117 +
118 + if (ErrorCode) {
119 + errs() << "Trouble iterating over directory '" << Directory
120 + << "': " << ErrorCode.message() << "\n";
121 + return 1;
122 + }
123 +
124 + // Remove the TUReplacementFiles (triggered by "remove-change-desc-files"
125 + // command line option) when exiting main().
126 + std::unique_ptr<ScopedFileRemover> Remover;
127 + if (RemoveTUReplacementFiles)
128 + Remover.reset(new ScopedFileRemover(TUFiles, Diagnostics));
129 +
130 + FileManager Files((FileSystemOptions()));
131 + SourceManager SM(Diagnostics, Files);
132 +
133 + FileToChangesMap Changes;
134 + if (!mergeAndDeduplicate(TURs, TUDs, Changes, SM))
135 + return 1;
136 +
137 + tooling::ApplyChangesSpec Spec;
138 + Spec.Cleanup = true;
139 + Spec.Style = FormatStyle;
140 + Spec.Format = DoFormat ? tooling::ApplyChangesSpec::kAll
141 + : tooling::ApplyChangesSpec::kNone;
142 +
143 + for (const auto &FileChange : Changes) {
144 + const FileEntry *Entry = FileChange.first;
145 + StringRef FileName = Entry->getName();
146 + llvm::Expected<std::string> NewFileData =
147 + applyChanges(FileName, FileChange.second, Spec, Diagnostics);
148 + if (!NewFileData) {
149 + errs() << llvm::toString(NewFileData.takeError()) << "\n";
150 + continue;
151 + }
152 +
153 + // Write new file to disk
154 + std::error_code EC;
155 + llvm::raw_fd_ostream FileStream(FileName, EC, llvm::sys::fs::OF_None);
156 + if (EC) {
157 + llvm::errs() << "Could not open " << FileName << " for writing\n";
158 + continue;
159 + }
160 + FileStream << *NewFileData;
161 + }
162 +
163 + return 0;
164 +}
1 +set(LLVM_LINK_COMPONENTS
2 + FrontendOpenMP
3 + Support
4 + )
5 +
6 +add_clang_library(clangChangeNamespace
7 + ChangeNamespace.cpp
8 +
9 + DEPENDS
10 + omp_gen
11 +)
12 +
13 +clang_target_link_libraries(clangChangeNamespace
14 + PRIVATE
15 + clangAST
16 + clangASTMatchers
17 + clangBasic
18 + clangFormat
19 + clangFrontend
20 + clangLex
21 + clangSerialization
22 + clangTooling
23 + clangToolingCore
24 + )
25 +
26 +add_subdirectory(tool)
1 +//===-- ChangeNamespace.h -- Change namespace ------------------*- C++ -*-===//
2 +//
3 +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 +// See https://llvm.org/LICENSE.txt for license information.
5 +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 +//
7 +//===----------------------------------------------------------------------===//
8 +
9 +#ifndef LLVM_CLANG_TOOLS_EXTRA_CHANGE_NAMESPACE_CHANGENAMESPACE_H
10 +#define LLVM_CLANG_TOOLS_EXTRA_CHANGE_NAMESPACE_CHANGENAMESPACE_H
11 +
12 +#include "clang/ASTMatchers/ASTMatchFinder.h"
13 +#include "clang/Format/Format.h"
14 +#include "clang/Tooling/Core/Replacement.h"
15 +#include "llvm/Support/Regex.h"
16 +#include <string>
17 +
18 +namespace clang {
19 +namespace change_namespace {
20 +
21 +// This tool can be used to change the surrounding namespaces of class/function
22 +// definitions. Classes/functions in the moved namespace will have new
23 +// namespaces while references to symbols (e.g. types, functions) which are not
24 +// defined in the changed namespace will be correctly qualified by prepending
25 +// namespace specifiers before them.
26 +// This will try to add shortest namespace specifiers possible. When a symbol
27 +// reference needs to be fully-qualified, this adds a "::" prefix to the
28 +// namespace specifiers unless the new namespace is the global namespace.
29 +// For classes, only classes that are declared/defined in the given namespace in
30 +// specified files will be moved: forward declarations will remain in the old
31 +// namespace.
32 +// For example, changing "a" to "x":
33 +// Old code:
34 +// namespace a {
35 +// class FWD;
36 +// class A { FWD *fwd; }
37 +// } // a
38 +// New code:
39 +// namespace a {
40 +// class FWD;
41 +// } // a
42 +// namespace x {
43 +// class A { ::a::FWD *fwd; }
44 +// } // x
45 +// FIXME: support moving typedef, enums across namespaces.
46 +class ChangeNamespaceTool : public ast_matchers::MatchFinder::MatchCallback {
47 +public:
48 + // Moves code in the old namespace `OldNs` to the new namespace `NewNs` in
49 + // files matching `FilePattern`.
50 + ChangeNamespaceTool(
51 + llvm::StringRef OldNs, llvm::StringRef NewNs, llvm::StringRef FilePattern,
52 + llvm::ArrayRef<std::string> AllowedSymbolPatterns,
53 + std::map<std::string, tooling::Replacements> *FileToReplacements,
54 + llvm::StringRef FallbackStyle = "LLVM");
55 +
56 + void registerMatchers(ast_matchers::MatchFinder *Finder);
57 +
58 + void run(const ast_matchers::MatchFinder::MatchResult &Result) override;
59 +
60 + // Moves the changed code in old namespaces but leaves class forward
61 + // declarations behind.
62 + void onEndOfTranslationUnit() override;
63 +
64 +private:
65 + void moveOldNamespace(const ast_matchers::MatchFinder::MatchResult &Result,
66 + const NamespaceDecl *NsDecl);
67 +
68 + void moveClassForwardDeclaration(
69 + const ast_matchers::MatchFinder::MatchResult &Result,
70 + const NamedDecl *FwdDecl);
71 +
72 + void replaceQualifiedSymbolInDeclContext(
73 + const ast_matchers::MatchFinder::MatchResult &Result,
74 + const DeclContext *DeclContext, SourceLocation Start, SourceLocation End,
75 + const NamedDecl *FromDecl);
76 +
77 + void fixTypeLoc(const ast_matchers::MatchFinder::MatchResult &Result,
78 + SourceLocation Start, SourceLocation End, TypeLoc Type);
79 +
80 + void fixUsingShadowDecl(const ast_matchers::MatchFinder::MatchResult &Result,
81 + const UsingDecl *UsingDeclaration);
82 +
83 + void fixDeclRefExpr(const ast_matchers::MatchFinder::MatchResult &Result,
84 + const DeclContext *UseContext, const NamedDecl *From,
85 + const DeclRefExpr *Ref);
86 +
87 + // Information about moving an old namespace.
88 + struct MoveNamespace {
89 + // The start offset of the namespace block being moved in the original
90 + // code.
91 + unsigned Offset;
92 + // The length of the namespace block in the original code.
93 + unsigned Length;
94 + // The offset at which the new namespace block will be inserted in the
95 + // original code.
96 + unsigned InsertionOffset;
97 + // The file in which the namespace is declared.
98 + FileID FID;
99 + SourceManager *SourceMgr;
100 + };
101 +
102 + // Information about inserting a class forward declaration.
103 + struct InsertForwardDeclaration {
104 + // The offset at while the forward declaration will be inserted in the
105 + // original code.
106 + unsigned InsertionOffset;
107 + // The code to be inserted.
108 + std::string ForwardDeclText;
109 + };
110 +
111 + std::string FallbackStyle;
112 + // In match callbacks, this contains replacements for replacing `typeLoc`s in
113 + // and deleting forward declarations in the moved namespace blocks.
114 + // In `onEndOfTranslationUnit` callback, the previous added replacements are
115 + // applied (on the moved namespace blocks), and then changed code in old
116 + // namespaces re moved to new namespaces, and previously deleted forward
117 + // declarations are inserted back to old namespaces, from which they are
118 + // deleted.
119 + std::map<std::string, tooling::Replacements> &FileToReplacements;
120 + // A fully qualified name of the old namespace without "::" prefix, e.g.
121 + // "a::b::c".
122 + std::string OldNamespace;
123 + // A fully qualified name of the new namespace without "::" prefix, e.g.
124 + // "x::y::z".
125 + std::string NewNamespace;
126 + // The longest suffix in the old namespace that does not overlap the new
127 + // namespace.
128 + // For example, if `OldNamespace` is "a::b::c" and `NewNamespace` is
129 + // "a::x::y", then `DiffOldNamespace` will be "b::c".
130 + std::string DiffOldNamespace;
131 + // The longest suffix in the new namespace that does not overlap the old
132 + // namespace.
133 + // For example, if `OldNamespace` is "a::b::c" and `NewNamespace` is
134 + // "a::x::y", then `DiffNewNamespace` will be "x::y".
135 + std::string DiffNewNamespace;
136 + // A regex pattern that matches files to be processed.
137 + std::string FilePattern;
138 + llvm::Regex FilePatternRE;
139 + // Information about moved namespaces grouped by file.
140 + // Since we are modifying code in old namespaces (e.g. add namespace
141 + // specifiers) as well as moving them, we store information about namespaces
142 + // to be moved and only move them after all modifications are finished (i.e.
143 + // in `onEndOfTranslationUnit`).
144 + std::map<std::string, std::vector<MoveNamespace>> MoveNamespaces;
145 + // Information about forward declaration insertions grouped by files.
146 + // A class forward declaration is not moved, so it will be deleted from the
147 + // moved code block and inserted back into the old namespace. The insertion
148 + // will be done after removing the code from the old namespace and before
149 + // inserting it to the new namespace.
150 + std::map<std::string, std::vector<InsertForwardDeclaration>> InsertFwdDecls;
151 + // Records all using declarations, which can be used to shorten namespace
152 + // specifiers.
153 + llvm::SmallPtrSet<const UsingDecl *, 8> UsingDecls;
154 + // Records all using namespace declarations, which can be used to shorten
155 + // namespace specifiers.
156 + llvm::SmallPtrSet<const UsingDirectiveDecl *, 8> UsingNamespaceDecls;
157 + // Records all namespace alias declarations, which can be used to shorten
158 + // namespace specifiers.
159 + llvm::SmallPtrSet<const NamespaceAliasDecl *, 8> NamespaceAliasDecls;
160 + // TypeLocs of CXXCtorInitializer. Types of CXXCtorInitializers do not need to
161 + // be fixed.
162 + llvm::SmallVector<TypeLoc, 8> BaseCtorInitializerTypeLocs;
163 + // Since a DeclRefExpr for a function call can be matched twice (one as
164 + // CallExpr and one as DeclRefExpr), we record all DeclRefExpr's that have
165 + // been processed so that we don't handle them twice.
166 + llvm::SmallPtrSet<const clang::DeclRefExpr*, 16> ProcessedFuncRefs;
167 + // Patterns of symbol names whose references are not expected to be updated
168 + // when changing namespaces around them.
169 + std::vector<llvm::Regex> AllowedSymbolRegexes;
170 +};
171 +
172 +} // namespace change_namespace
173 +} // namespace clang
174 +
175 +#endif // LLVM_CLANG_TOOLS_EXTRA_CHANGE_NAMESPACE_CHANGENAMESPACE_H
1 +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/..)
2 +
3 +set(LLVM_LINK_COMPONENTS
4 + FrontendOpenMP
5 + Support
6 + )
7 +
8 +add_clang_tool(clang-change-namespace
9 + ClangChangeNamespace.cpp
10 + )
11 +clang_target_link_libraries(clang-change-namespace
12 + PRIVATE
13 + clangAST
14 + clangASTMatchers
15 + clangBasic
16 + clangFormat
17 + clangFrontend
18 + clangRewrite
19 + clangSerialization
20 + clangTooling
21 + clangToolingCore
22 + )
23 +target_link_libraries(clang-change-namespace
24 + PRIVATE
25 + clangChangeNamespace
26 + )
1 +//===-- ClangChangeNamespace.cpp - Standalone change namespace ------------===//
2 +//
3 +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 +// See https://llvm.org/LICENSE.txt for license information.
5 +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 +//
7 +//===----------------------------------------------------------------------===//
8 +// This tool can be used to change the surrounding namespaces of class/function
9 +// definitions.
10 +//
11 +// Example: test.cc
12 +// namespace na {
13 +// class X {};
14 +// namespace nb {
15 +// class Y { X x; };
16 +// } // namespace nb
17 +// } // namespace na
18 +// To move the definition of class Y from namespace "na::nb" to "x::y", run:
19 +// clang-change-namespace --old_namespace "na::nb" \
20 +// --new_namespace "x::y" --file_pattern "test.cc" test.cc --
21 +// Output:
22 +// namespace na {
23 +// class X {};
24 +// } // namespace na
25 +// namespace x {
26 +// namespace y {
27 +// class Y { na::X x; };
28 +// } // namespace y
29 +// } // namespace x
30 +
31 +#include "ChangeNamespace.h"
32 +#include "clang/ASTMatchers/ASTMatchFinder.h"
33 +#include "clang/Frontend/FrontendActions.h"
34 +#include "clang/Frontend/TextDiagnosticPrinter.h"
35 +#include "clang/Rewrite/Core/Rewriter.h"
36 +#include "clang/Tooling/CommonOptionsParser.h"
37 +#include "clang/Tooling/Refactoring.h"
38 +#include "clang/Tooling/Tooling.h"
39 +#include "llvm/Support/CommandLine.h"
40 +#include "llvm/Support/Signals.h"
41 +#include "llvm/Support/YAMLTraits.h"
42 +
43 +using namespace clang;
44 +using namespace llvm;
45 +
46 +namespace {
47 +
48 +cl::OptionCategory ChangeNamespaceCategory("Change namespace.");
49 +
50 +cl::opt<std::string> OldNamespace("old_namespace", cl::Required,
51 + cl::desc("Old namespace."),
52 + cl::cat(ChangeNamespaceCategory));
53 +
54 +cl::opt<std::string> NewNamespace("new_namespace", cl::Required,
55 + cl::desc("New namespace."),
56 + cl::cat(ChangeNamespaceCategory));
57 +
58 +cl::opt<std::string> FilePattern(
59 + "file_pattern", cl::Required,
60 + cl::desc("Only rename namespaces in files that match the given pattern."),
61 + cl::cat(ChangeNamespaceCategory));
62 +
63 +cl::opt<bool> Inplace("i", cl::desc("Inplace edit <file>s, if specified."),
64 + cl::cat(ChangeNamespaceCategory));
65 +
66 +cl::opt<bool>
67 + DumpYAML("dump_result",
68 + cl::desc("Dump new file contents in YAML, if specified."),
69 + cl::cat(ChangeNamespaceCategory));
70 +
71 +cl::opt<std::string> Style("style",
72 + cl::desc("The style name used for reformatting."),
73 + cl::init("LLVM"), cl::cat(ChangeNamespaceCategory));
74 +
75 +cl::opt<std::string> AllowedFile(
76 + "allowed_file",
77 + cl::desc("A file containing regexes of symbol names that are not expected "
78 + "to be updated when changing namespaces around them."),
79 + cl::init(""), cl::cat(ChangeNamespaceCategory));
80 +
81 +llvm::ErrorOr<std::vector<std::string>> GetAllowedSymbolPatterns() {
82 + std::vector<std::string> Patterns;
83 + if (AllowedFile.empty())
84 + return Patterns;
85 +
86 + llvm::SmallVector<StringRef, 8> Lines;
87 + llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> File =
88 + llvm::MemoryBuffer::getFile(AllowedFile);
89 + if (!File)
90 + return File.getError();
91 + llvm::StringRef Content = File.get()->getBuffer();
92 + Content.split(Lines, '\n', /*MaxSplit=*/-1, /*KeepEmpty=*/false);
93 + for (auto Line : Lines)
94 + Patterns.push_back(std::string(Line.trim()));
95 + return Patterns;
96 +}
97 +
98 +} // anonymous namespace
99 +
100 +int main(int argc, const char **argv) {
101 + llvm::sys::PrintStackTraceOnErrorSignal(argv[0]);
102 + tooling::CommonOptionsParser OptionsParser(argc, argv,
103 + ChangeNamespaceCategory);
104 + const auto &Files = OptionsParser.getSourcePathList();
105 + tooling::RefactoringTool Tool(OptionsParser.getCompilations(), Files);
106 + llvm::ErrorOr<std::vector<std::string>> AllowedPatterns =
107 + GetAllowedSymbolPatterns();
108 + if (!AllowedPatterns) {
109 + llvm::errs() << "Failed to open allow file " << AllowedFile << ". "
110 + << AllowedPatterns.getError().message() << "\n";
111 + return 1;
112 + }
113 + change_namespace::ChangeNamespaceTool NamespaceTool(
114 + OldNamespace, NewNamespace, FilePattern, *AllowedPatterns,
115 + &Tool.getReplacements(), Style);
116 + ast_matchers::MatchFinder Finder;
117 + NamespaceTool.registerMatchers(&Finder);
118 + std::unique_ptr<tooling::FrontendActionFactory> Factory =
119 + tooling::newFrontendActionFactory(&Finder);
120 +
121 + if (int Result = Tool.run(Factory.get()))
122 + return Result;
123 + LangOptions DefaultLangOptions;
124 + IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions();
125 + clang::TextDiagnosticPrinter DiagnosticPrinter(errs(), &*DiagOpts);
126 + DiagnosticsEngine Diagnostics(
127 + IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs()), &*DiagOpts,
128 + &DiagnosticPrinter, false);
129 + auto &FileMgr = Tool.getFiles();
130 + SourceManager Sources(Diagnostics, FileMgr);
131 + Rewriter Rewrite(Sources, DefaultLangOptions);
132 +
133 + if (!formatAndApplyAllReplacements(Tool.getReplacements(), Rewrite, Style)) {
134 + llvm::errs() << "Failed applying all replacements.\n";
135 + return 1;
136 + }
137 + if (Inplace)
138 + return Rewrite.overwriteChangedFiles();
139 +
140 + std::set<llvm::StringRef> ChangedFiles;
141 + for (const auto &it : Tool.getReplacements())
142 + ChangedFiles.insert(it.first);
143 +
144 + if (DumpYAML) {
145 + auto WriteToYAML = [&](llvm::raw_ostream &OS) {
146 + OS << "[\n";
147 + for (auto I = ChangedFiles.begin(), E = ChangedFiles.end(); I != E; ++I) {
148 + OS << " {\n";
149 + OS << " \"FilePath\": \"" << *I << "\",\n";
150 + const auto Entry = FileMgr.getFile(*I);
151 + auto ID = Sources.getOrCreateFileID(*Entry, SrcMgr::C_User);
152 + std::string Content;
153 + llvm::raw_string_ostream ContentStream(Content);
154 + Rewrite.getEditBuffer(ID).write(ContentStream);
155 + OS << " \"SourceText\": \""
156 + << llvm::yaml::escape(ContentStream.str()) << "\"\n";
157 + OS << " }";
158 + if (I != std::prev(E))
159 + OS << ",\n";
160 + }
161 + OS << "\n]\n";
162 + };
163 + WriteToYAML(llvm::outs());
164 + return 0;
165 + }
166 +
167 + for (const auto &File : ChangedFiles) {
168 + const auto Entry = FileMgr.getFile(File);
169 +
170 + auto ID = Sources.getOrCreateFileID(*Entry, SrcMgr::C_User);
171 + outs() << "============== " << File << " ==============\n";
172 + Rewrite.getEditBuffer(ID).write(llvm::outs());
173 + outs() << "\n============================================\n";
174 + }
175 +
176 + return 0;
177 +}
This diff is collapsed. Click to expand it.
1 +//===-- BitcodeReader.h - ClangDoc Bitcode Reader --------------*- C++ -*-===//
2 +//
3 +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 +// See https://llvm.org/LICENSE.txt for license information.
5 +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 +//
7 +//===----------------------------------------------------------------------===//
8 +//
9 +// This file implements a reader for parsing the clang-doc internal
10 +// representation from LLVM bitcode. The reader takes in a stream of bits and
11 +// generates the set of infos that it represents.
12 +//
13 +//===----------------------------------------------------------------------===//
14 +
15 +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_DOC_BITCODEREADER_H
16 +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_DOC_BITCODEREADER_H
17 +
18 +#include "BitcodeWriter.h"
19 +#include "Representation.h"
20 +#include "clang/AST/AST.h"
21 +#include "llvm/ADT/Optional.h"
22 +#include "llvm/ADT/SmallVector.h"
23 +#include "llvm/Bitstream/BitstreamReader.h"
24 +#include "llvm/Support/Error.h"
25 +
26 +namespace clang {
27 +namespace doc {
28 +
29 +// Class to read bitstream into an InfoSet collection
30 +class ClangDocBitcodeReader {
31 +public:
32 + ClangDocBitcodeReader(llvm::BitstreamCursor &Stream) : Stream(Stream) {}
33 +
34 + // Main entry point, calls readBlock to read each block in the given stream.
35 + llvm::Expected<std::vector<std::unique_ptr<Info>>> readBitcode();
36 +
37 +private:
38 + enum class Cursor { BadBlock = 1, Record, BlockEnd, BlockBegin };
39 +
40 + // Top level parsing
41 + llvm::Error validateStream();
42 + llvm::Error readVersion();
43 + llvm::Error readBlockInfoBlock();
44 +
45 + // Read a block of records into a single Info struct, calls readRecord on each
46 + // record found.
47 + template <typename T> llvm::Error readBlock(unsigned ID, T I);
48 +
49 + // Step through a block of records to find the next data field.
50 + template <typename T> llvm::Error readSubBlock(unsigned ID, T I);
51 +
52 + // Read record data into the given Info data field, calling the appropriate
53 + // parseRecord functions to parse and store the data.
54 + template <typename T> llvm::Error readRecord(unsigned ID, T I);
55 +
56 + // Allocate the relevant type of info and add read data to the object.
57 + template <typename T>
58 + llvm::Expected<std::unique_ptr<Info>> createInfo(unsigned ID);
59 +
60 + // Helper function to step through blocks to find and dispatch the next record
61 + // or block to be read.
62 + Cursor skipUntilRecordOrBlock(unsigned &BlockOrRecordID);
63 +
64 + // Helper function to set up the appropriate type of Info.
65 + llvm::Expected<std::unique_ptr<Info>> readBlockToInfo(unsigned ID);
66 +
67 + llvm::BitstreamCursor &Stream;
68 + Optional<llvm::BitstreamBlockInfo> BlockInfo;
69 + FieldId CurrentReferenceField;
70 +};
71 +
72 +} // namespace doc
73 +} // namespace clang
74 +
75 +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_DOC_BITCODEREADER_H
This diff is collapsed. Click to expand it.
1 +//===-- BitcodeWriter.h - ClangDoc Bitcode Writer --------------*- C++ -*-===//
2 +//
3 +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 +// See https://llvm.org/LICENSE.txt for license information.
5 +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 +//
7 +//===----------------------------------------------------------------------===//
8 +//
9 +// This file implements a writer for serializing the clang-doc internal
10 +// representation to LLVM bitcode. The writer takes in a stream and emits the
11 +// generated bitcode to that stream.
12 +//
13 +//===----------------------------------------------------------------------===//
14 +
15 +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_DOC_BITCODEWRITER_H
16 +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_DOC_BITCODEWRITER_H
17 +
18 +#include "Representation.h"
19 +#include "clang/AST/AST.h"
20 +#include "llvm/ADT/DenseMap.h"
21 +#include "llvm/ADT/SmallVector.h"
22 +#include "llvm/ADT/StringRef.h"
23 +#include "llvm/Bitstream/BitstreamWriter.h"
24 +#include <initializer_list>
25 +#include <vector>
26 +
27 +namespace clang {
28 +namespace doc {
29 +
30 +// Current version number of clang-doc bitcode.
31 +// Should be bumped when removing or changing BlockIds, RecordIds, or
32 +// BitCodeConstants, though they can be added without breaking it.
33 +static const unsigned VersionNumber = 3;
34 +
35 +struct BitCodeConstants {
36 + static constexpr unsigned RecordSize = 32U;
37 + static constexpr unsigned SignatureBitSize = 8U;
38 + static constexpr unsigned SubblockIDSize = 4U;
39 + static constexpr unsigned BoolSize = 1U;
40 + static constexpr unsigned IntSize = 16U;
41 + static constexpr unsigned StringLengthSize = 16U;
42 + static constexpr unsigned FilenameLengthSize = 16U;
43 + static constexpr unsigned LineNumberSize = 32U;
44 + static constexpr unsigned ReferenceTypeSize = 8U;
45 + static constexpr unsigned USRLengthSize = 6U;
46 + static constexpr unsigned USRBitLengthSize = 8U;
47 + static constexpr unsigned char Signature[4] = {'D', 'O', 'C', 'S'};
48 + static constexpr int USRHashSize = 20;
49 +};
50 +
51 +// New Ids need to be added to both the enum here and the relevant IdNameMap in
52 +// the implementation file.
53 +enum BlockId {
54 + BI_VERSION_BLOCK_ID = llvm::bitc::FIRST_APPLICATION_BLOCKID,
55 + BI_NAMESPACE_BLOCK_ID,
56 + BI_ENUM_BLOCK_ID,
57 + BI_TYPE_BLOCK_ID,
58 + BI_FIELD_TYPE_BLOCK_ID,
59 + BI_MEMBER_TYPE_BLOCK_ID,
60 + BI_RECORD_BLOCK_ID,
61 + BI_BASE_RECORD_BLOCK_ID,
62 + BI_FUNCTION_BLOCK_ID,
63 + BI_COMMENT_BLOCK_ID,
64 + BI_REFERENCE_BLOCK_ID,
65 + BI_LAST,
66 + BI_FIRST = BI_VERSION_BLOCK_ID
67 +};
68 +
69 +// New Ids need to be added to the enum here, and to the relevant IdNameMap and
70 +// initialization list in the implementation file.
71 +enum RecordId {
72 + VERSION = 1,
73 + FUNCTION_USR,
74 + FUNCTION_NAME,
75 + FUNCTION_DEFLOCATION,
76 + FUNCTION_LOCATION,
77 + FUNCTION_ACCESS,
78 + FUNCTION_IS_METHOD,
79 + COMMENT_KIND,
80 + COMMENT_TEXT,
81 + COMMENT_NAME,
82 + COMMENT_DIRECTION,
83 + COMMENT_PARAMNAME,
84 + COMMENT_CLOSENAME,
85 + COMMENT_SELFCLOSING,
86 + COMMENT_EXPLICIT,
87 + COMMENT_ATTRKEY,
88 + COMMENT_ATTRVAL,
89 + COMMENT_ARG,
90 + FIELD_TYPE_NAME,
91 + MEMBER_TYPE_NAME,
92 + MEMBER_TYPE_ACCESS,
93 + NAMESPACE_USR,
94 + NAMESPACE_NAME,
95 + NAMESPACE_PATH,
96 + ENUM_USR,
97 + ENUM_NAME,
98 + ENUM_DEFLOCATION,
99 + ENUM_LOCATION,
100 + ENUM_MEMBER,
101 + ENUM_SCOPED,
102 + RECORD_USR,
103 + RECORD_NAME,
104 + RECORD_PATH,
105 + RECORD_DEFLOCATION,
106 + RECORD_LOCATION,
107 + RECORD_TAG_TYPE,
108 + RECORD_IS_TYPE_DEF,
109 + BASE_RECORD_USR,
110 + BASE_RECORD_NAME,
111 + BASE_RECORD_PATH,
112 + BASE_RECORD_TAG_TYPE,
113 + BASE_RECORD_IS_VIRTUAL,
114 + BASE_RECORD_ACCESS,
115 + BASE_RECORD_IS_PARENT,
116 + REFERENCE_USR,
117 + REFERENCE_NAME,
118 + REFERENCE_TYPE,
119 + REFERENCE_PATH,
120 + REFERENCE_IS_IN_GLOBAL_NAMESPACE,
121 + REFERENCE_FIELD,
122 + RI_LAST,
123 + RI_FIRST = VERSION
124 +};
125 +
126 +static constexpr unsigned BlockIdCount = BI_LAST - BI_FIRST;
127 +static constexpr unsigned RecordIdCount = RI_LAST - RI_FIRST;
128 +
129 +// Identifiers for differentiating between subblocks
130 +enum class FieldId {
131 + F_default,
132 + F_namespace,
133 + F_parent,
134 + F_vparent,
135 + F_type,
136 + F_child_namespace,
137 + F_child_record
138 +};
139 +
140 +class ClangDocBitcodeWriter {
141 +public:
142 + ClangDocBitcodeWriter(llvm::BitstreamWriter &Stream) : Stream(Stream) {
143 + emitHeader();
144 + emitBlockInfoBlock();
145 + emitVersionBlock();
146 + }
147 +
148 + // Write a specific info to a bitcode stream.
149 + bool dispatchInfoForWrite(Info *I);
150 +
151 + // Block emission of different info types.
152 + void emitBlock(const NamespaceInfo &I);
153 + void emitBlock(const RecordInfo &I);
154 + void emitBlock(const BaseRecordInfo &I);
155 + void emitBlock(const FunctionInfo &I);
156 + void emitBlock(const EnumInfo &I);
157 + void emitBlock(const TypeInfo &B);
158 + void emitBlock(const FieldTypeInfo &B);
159 + void emitBlock(const MemberTypeInfo &B);
160 + void emitBlock(const CommentInfo &B);
161 + void emitBlock(const Reference &B, FieldId F);
162 +
163 +private:
164 + class AbbreviationMap {
165 + llvm::DenseMap<unsigned, unsigned> Abbrevs;
166 +
167 + public:
168 + AbbreviationMap() : Abbrevs(RecordIdCount) {}
169 +
170 + void add(RecordId RID, unsigned AbbrevID);
171 + unsigned get(RecordId RID) const;
172 + };
173 +
174 + class StreamSubBlockGuard {
175 + llvm::BitstreamWriter &Stream;
176 +
177 + public:
178 + StreamSubBlockGuard(llvm::BitstreamWriter &Stream_, BlockId ID)
179 + : Stream(Stream_) {
180 + // NOTE: SubBlockIDSize could theoretically be calculated on the fly,
181 + // based on the initialization list of records in each block.
182 + Stream.EnterSubblock(ID, BitCodeConstants::SubblockIDSize);
183 + }
184 +
185 + StreamSubBlockGuard(const StreamSubBlockGuard &) = delete;
186 + StreamSubBlockGuard &operator=(const StreamSubBlockGuard &) = delete;
187 +
188 + ~StreamSubBlockGuard() { Stream.ExitBlock(); }
189 + };
190 +
191 + // Emission of validation and overview blocks.
192 + void emitHeader();
193 + void emitVersionBlock();
194 + void emitRecordID(RecordId ID);
195 + void emitBlockID(BlockId ID);
196 + void emitBlockInfoBlock();
197 + void emitBlockInfo(BlockId BID, const std::vector<RecordId> &RIDs);
198 +
199 + // Emission of individual record types.
200 + void emitRecord(StringRef Str, RecordId ID);
201 + void emitRecord(const SymbolID &Str, RecordId ID);
202 + void emitRecord(const Location &Loc, RecordId ID);
203 + void emitRecord(const Reference &Ref, RecordId ID);
204 + void emitRecord(bool Value, RecordId ID);
205 + void emitRecord(int Value, RecordId ID);
206 + void emitRecord(unsigned Value, RecordId ID);
207 + bool prepRecordData(RecordId ID, bool ShouldEmit = true);
208 +
209 + // Emission of appropriate abbreviation type.
210 + void emitAbbrev(RecordId ID, BlockId Block);
211 +
212 + // Static size is the maximum length of the block/record names we're pushing
213 + // to this + 1. Longest is currently `MemberTypeBlock` at 15 chars.
214 + SmallVector<uint32_t, BitCodeConstants::RecordSize> Record;
215 + llvm::BitstreamWriter &Stream;
216 + AbbreviationMap Abbrevs;
217 +};
218 +
219 +} // namespace doc
220 +} // namespace clang
221 +
222 +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_DOC_BITCODEWRITER_H
1 +set(LLVM_LINK_COMPONENTS
2 + support
3 + BitstreamReader
4 + FrontendOpenMP
5 + )
6 +
7 +add_clang_library(clangDoc
8 + BitcodeReader.cpp
9 + BitcodeWriter.cpp
10 + ClangDoc.cpp
11 + Generators.cpp
12 + HTMLGenerator.cpp
13 + Mapper.cpp
14 + MDGenerator.cpp
15 + Representation.cpp
16 + Serialize.cpp
17 + YAMLGenerator.cpp
18 +
19 + DEPENDS
20 + omp_gen
21 +)
22 +
23 +clang_target_link_libraries(clangDoc
24 + PRIVATE
25 + clangAnalysis
26 + clangAST
27 + clangASTMatchers
28 + clangBasic
29 + clangFrontend
30 + clangIndex
31 + clangLex
32 + clangTooling
33 + clangToolingCore
34 + )
35 +
36 +add_subdirectory(tool)
1 +//===-- ClangDoc.cpp - ClangDoc ---------------------------------*- C++ -*-===//
2 +//
3 +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 +// See https://llvm.org/LICENSE.txt for license information.
5 +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 +//
7 +//===----------------------------------------------------------------------===//
8 +//
9 +// This file implements the main entry point for the clang-doc tool. It runs
10 +// the clang-doc mapper on a given set of source code files using a
11 +// FrontendActionFactory.
12 +//
13 +//===----------------------------------------------------------------------===//
14 +
15 +#include "ClangDoc.h"
16 +#include "Mapper.h"
17 +#include "Representation.h"
18 +#include "clang/AST/AST.h"
19 +#include "clang/AST/ASTConsumer.h"
20 +#include "clang/AST/ASTContext.h"
21 +#include "clang/AST/RecursiveASTVisitor.h"
22 +#include "clang/Frontend/ASTConsumers.h"
23 +#include "clang/Frontend/CompilerInstance.h"
24 +#include "clang/Frontend/FrontendActions.h"
25 +
26 +namespace clang {
27 +namespace doc {
28 +
29 +class MapperActionFactory : public tooling::FrontendActionFactory {
30 +public:
31 + MapperActionFactory(ClangDocContext CDCtx) : CDCtx(CDCtx) {}
32 + std::unique_ptr<FrontendAction> create() override;
33 +
34 +private:
35 + ClangDocContext CDCtx;
36 +};
37 +
38 +std::unique_ptr<FrontendAction> MapperActionFactory::create() {
39 + class ClangDocAction : public clang::ASTFrontendAction {
40 + public:
41 + ClangDocAction(ClangDocContext CDCtx) : CDCtx(CDCtx) {}
42 +
43 + std::unique_ptr<clang::ASTConsumer>
44 + CreateASTConsumer(clang::CompilerInstance &Compiler,
45 + llvm::StringRef InFile) override {
46 + return std::make_unique<MapASTVisitor>(&Compiler.getASTContext(), CDCtx);
47 + }
48 +
49 + private:
50 + ClangDocContext CDCtx;
51 + };
52 + return std::make_unique<ClangDocAction>(CDCtx);
53 +}
54 +
55 +std::unique_ptr<tooling::FrontendActionFactory>
56 +newMapperActionFactory(ClangDocContext CDCtx) {
57 + return std::make_unique<MapperActionFactory>(CDCtx);
58 +}
59 +
60 +} // namespace doc
61 +} // namespace clang
1 +//===-- ClangDoc.h - ClangDoc -----------------------------------*- C++ -*-===//
2 +//
3 +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 +// See https://llvm.org/LICENSE.txt for license information.
5 +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 +//
7 +//===----------------------------------------------------------------------===//
8 +//
9 +// This file exposes a method to craete the FrontendActionFactory for the
10 +// clang-doc tool. The factory runs the clang-doc mapper on a given set of
11 +// source code files, storing the results key-value pairs in its
12 +// ExecutionContext.
13 +//
14 +//===----------------------------------------------------------------------===//
15 +
16 +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_DOC_CLANGDOC_H
17 +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_DOC_CLANGDOC_H
18 +
19 +#include "Representation.h"
20 +#include "clang/Tooling/Execution.h"
21 +#include "clang/Tooling/StandaloneExecution.h"
22 +#include "clang/Tooling/Tooling.h"
23 +
24 +namespace clang {
25 +namespace doc {
26 +
27 +std::unique_ptr<tooling::FrontendActionFactory>
28 +newMapperActionFactory(ClangDocContext CDCtx);
29 +
30 +} // namespace doc
31 +} // namespace clang
32 +
33 +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_DOC_CLANGDOC_H
1 +//===-- Generators.cpp - Generator Registry ----------------------*- C++-*-===//
2 +//
3 +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 +// See https://llvm.org/LICENSE.txt for license information.
5 +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 +//
7 +//===----------------------------------------------------------------------===//
8 +
9 +#include "Generators.h"
10 +
11 +LLVM_INSTANTIATE_REGISTRY(clang::doc::GeneratorRegistry)
12 +
13 +namespace clang {
14 +namespace doc {
15 +
16 +llvm::Expected<std::unique_ptr<Generator>>
17 +findGeneratorByName(llvm::StringRef Format) {
18 + for (const auto &Generator : GeneratorRegistry::entries()) {
19 + if (Generator.getName() != Format)
20 + continue;
21 + return Generator.instantiate();
22 + }
23 + return createStringError(llvm::inconvertibleErrorCode(),
24 + "can't find generator: " + Format);
25 +}
26 +
27 +// Enum conversion
28 +
29 +std::string getTagType(TagTypeKind AS) {
30 + switch (AS) {
31 + case TagTypeKind::TTK_Class:
32 + return "class";
33 + case TagTypeKind::TTK_Union:
34 + return "union";
35 + case TagTypeKind::TTK_Interface:
36 + return "interface";
37 + case TagTypeKind::TTK_Struct:
38 + return "struct";
39 + case TagTypeKind::TTK_Enum:
40 + return "enum";
41 + }
42 + llvm_unreachable("Unknown TagTypeKind");
43 +}
44 +
45 +llvm::Error Generator::createResources(ClangDocContext &CDCtx) {
46 + return llvm::Error::success();
47 +}
48 +
49 +// A function to add a reference to Info in Idx.
50 +// Given an Info X with the following namespaces: [B,A]; a reference to X will
51 +// be added in the children of a reference to B, which should be also a child of
52 +// a reference to A, where A is a child of Idx.
53 +// Idx
54 +// |-- A
55 +// |--B
56 +// |--X
57 +// If the references to the namespaces do not exist, they will be created. If
58 +// the references already exist, the same one will be used.
59 +void Generator::addInfoToIndex(Index &Idx, const doc::Info *Info) {
60 + // Index pointer that will be moving through Idx until the first parent
61 + // namespace of Info (where the reference has to be inserted) is found.
62 + Index *I = &Idx;
63 + // The Namespace vector includes the upper-most namespace at the end so the
64 + // loop will start from the end to find each of the namespaces.
65 + for (const auto &R : llvm::reverse(Info->Namespace)) {
66 + // Look for the current namespace in the children of the index I is
67 + // pointing.
68 + auto It = std::find(I->Children.begin(), I->Children.end(), R.USR);
69 + if (It != I->Children.end()) {
70 + // If it is found, just change I to point the namespace reference found.
71 + I = &*It;
72 + } else {
73 + // If it is not found a new reference is created
74 + I->Children.emplace_back(R.USR, R.Name, R.RefType, R.Path);
75 + // I is updated with the reference of the new namespace reference
76 + I = &I->Children.back();
77 + }
78 + }
79 + // Look for Info in the vector where it is supposed to be; it could already
80 + // exist if it is a parent namespace of an Info already passed to this
81 + // function.
82 + auto It = std::find(I->Children.begin(), I->Children.end(), Info->USR);
83 + if (It == I->Children.end()) {
84 + // If it is not in the vector it is inserted
85 + I->Children.emplace_back(Info->USR, Info->extractName(), Info->IT,
86 + Info->Path);
87 + } else {
88 + // If it not in the vector we only check if Path and Name are not empty
89 + // because if the Info was included by a namespace it may not have those
90 + // values.
91 + if (It->Path.empty())
92 + It->Path = Info->Path;
93 + if (It->Name.empty())
94 + It->Name = Info->extractName();
95 + }
96 +}
97 +
98 +// This anchor is used to force the linker to link in the generated object file
99 +// and thus register the generators.
100 +extern volatile int YAMLGeneratorAnchorSource;
101 +extern volatile int MDGeneratorAnchorSource;
102 +extern volatile int HTMLGeneratorAnchorSource;
103 +static int LLVM_ATTRIBUTE_UNUSED YAMLGeneratorAnchorDest =
104 + YAMLGeneratorAnchorSource;
105 +static int LLVM_ATTRIBUTE_UNUSED MDGeneratorAnchorDest =
106 + MDGeneratorAnchorSource;
107 +static int LLVM_ATTRIBUTE_UNUSED HTMLGeneratorAnchorDest =
108 + HTMLGeneratorAnchorSource;
109 +
110 +} // namespace doc
111 +} // namespace clang
1 +//===-- Generators.h - ClangDoc Generator ----------------------*- C++ -*-===//
2 +//
3 +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 +// See https://llvm.org/LICENSE.txt for license information.
5 +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 +//
7 +//===----------------------------------------------------------------------===//
8 +// Generator classes for converting declaration information into documentation
9 +// in a specified format.
10 +//===----------------------------------------------------------------------===//
11 +
12 +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_DOC_GENERATOR_H
13 +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_DOC_GENERATOR_H
14 +
15 +#include "Representation.h"
16 +#include "llvm/Support/Error.h"
17 +#include "llvm/Support/Registry.h"
18 +
19 +namespace clang {
20 +namespace doc {
21 +
22 +// Abstract base class for generators.
23 +// This is expected to be implemented and exposed via the GeneratorRegistry.
24 +class Generator {
25 +public:
26 + virtual ~Generator() = default;
27 +
28 + // Write out the decl info in the specified format.
29 + virtual llvm::Error generateDocForInfo(Info *I, llvm::raw_ostream &OS,
30 + const ClangDocContext &CDCtx) = 0;
31 + // This function writes a file with the index previously constructed.
32 + // It can be overwritten by any of the inherited generators.
33 + // If the override method wants to run this it should call
34 + // Generator::createResources(CDCtx);
35 + virtual llvm::Error createResources(ClangDocContext &CDCtx);
36 +
37 + static void addInfoToIndex(Index &Idx, const doc::Info *Info);
38 +};
39 +
40 +typedef llvm::Registry<Generator> GeneratorRegistry;
41 +
42 +llvm::Expected<std::unique_ptr<Generator>>
43 +findGeneratorByName(llvm::StringRef Format);
44 +
45 +std::string getTagType(TagTypeKind AS);
46 +
47 +} // namespace doc
48 +} // namespace clang
49 +
50 +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_DOC_GENERATOR_H
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
1 +//===-- Mapper.cpp - ClangDoc Mapper ----------------------------*- C++ -*-===//
2 +//
3 +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 +// See https://llvm.org/LICENSE.txt for license information.
5 +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 +//
7 +//===----------------------------------------------------------------------===//
8 +
9 +#include "Mapper.h"
10 +#include "BitcodeWriter.h"
11 +#include "Serialize.h"
12 +#include "clang/AST/Comment.h"
13 +#include "clang/Index/USRGeneration.h"
14 +#include "llvm/ADT/StringExtras.h"
15 +#include "llvm/Support/Error.h"
16 +
17 +using clang::comments::FullComment;
18 +
19 +namespace clang {
20 +namespace doc {
21 +
22 +void MapASTVisitor::HandleTranslationUnit(ASTContext &Context) {
23 + TraverseDecl(Context.getTranslationUnitDecl());
24 +}
25 +
26 +template <typename T> bool MapASTVisitor::mapDecl(const T *D) {
27 + // If we're looking a decl not in user files, skip this decl.
28 + if (D->getASTContext().getSourceManager().isInSystemHeader(D->getLocation()))
29 + return true;
30 +
31 + // Skip function-internal decls.
32 + if (D->getParentFunctionOrMethod())
33 + return true;
34 +
35 + llvm::SmallString<128> USR;
36 + // If there is an error generating a USR for the decl, skip this decl.
37 + if (index::generateUSRForDecl(D, USR))
38 + return true;
39 + bool IsFileInRootDir;
40 + llvm::SmallString<128> File =
41 + getFile(D, D->getASTContext(), CDCtx.SourceRoot, IsFileInRootDir);
42 + auto I = serialize::emitInfo(D, getComment(D, D->getASTContext()),
43 + getLine(D, D->getASTContext()), File,
44 + IsFileInRootDir, CDCtx.PublicOnly);
45 +
46 + // A null in place of I indicates that the serializer is skipping this decl
47 + // for some reason (e.g. we're only reporting public decls).
48 + if (I.first)
49 + CDCtx.ECtx->reportResult(llvm::toHex(llvm::toStringRef(I.first->USR)),
50 + serialize::serialize(I.first));
51 + if (I.second)
52 + CDCtx.ECtx->reportResult(llvm::toHex(llvm::toStringRef(I.second->USR)),
53 + serialize::serialize(I.second));
54 + return true;
55 +}
56 +
57 +bool MapASTVisitor::VisitNamespaceDecl(const NamespaceDecl *D) {
58 + return mapDecl(D);
59 +}
60 +
61 +bool MapASTVisitor::VisitRecordDecl(const RecordDecl *D) { return mapDecl(D); }
62 +
63 +bool MapASTVisitor::VisitEnumDecl(const EnumDecl *D) { return mapDecl(D); }
64 +
65 +bool MapASTVisitor::VisitCXXMethodDecl(const CXXMethodDecl *D) {
66 + return mapDecl(D);
67 +}
68 +
69 +bool MapASTVisitor::VisitFunctionDecl(const FunctionDecl *D) {
70 + // Don't visit CXXMethodDecls twice
71 + if (dyn_cast<CXXMethodDecl>(D))
72 + return true;
73 + return mapDecl(D);
74 +}
75 +
76 +comments::FullComment *
77 +MapASTVisitor::getComment(const NamedDecl *D, const ASTContext &Context) const {
78 + RawComment *Comment = Context.getRawCommentForDeclNoCache(D);
79 + // FIXME: Move setAttached to the initial comment parsing.
80 + if (Comment) {
81 + Comment->setAttached();
82 + return Comment->parse(Context, nullptr, D);
83 + }
84 + return nullptr;
85 +}
86 +
87 +int MapASTVisitor::getLine(const NamedDecl *D,
88 + const ASTContext &Context) const {
89 + return Context.getSourceManager().getPresumedLoc(D->getBeginLoc()).getLine();
90 +}
91 +
92 +llvm::SmallString<128> MapASTVisitor::getFile(const NamedDecl *D,
93 + const ASTContext &Context,
94 + llvm::StringRef RootDir,
95 + bool &IsFileInRootDir) const {
96 + llvm::SmallString<128> File(Context.getSourceManager()
97 + .getPresumedLoc(D->getBeginLoc())
98 + .getFilename());
99 + IsFileInRootDir = false;
100 + if (RootDir.empty() || !File.startswith(RootDir))
101 + return File;
102 + IsFileInRootDir = true;
103 + llvm::SmallString<128> Prefix(RootDir);
104 + // replace_path_prefix removes the exact prefix provided. The result of
105 + // calling that function on ("A/B/C.c", "A/B", "") would be "/C.c", which
106 + // starts with a / that is not needed. This is why we fix Prefix so it always
107 + // ends with a / and the result has the desired format.
108 + if (!llvm::sys::path::is_separator(Prefix.back()))
109 + Prefix += llvm::sys::path::get_separator();
110 + llvm::sys::path::replace_path_prefix(File, Prefix, "");
111 + return File;
112 +}
113 +
114 +} // namespace doc
115 +} // namespace clang
1 +//===-- Mapper.h - ClangDoc Mapper ------------------------------*- C++ -*-===//
2 +//
3 +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 +// See https://llvm.org/LICENSE.txt for license information.
5 +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 +//
7 +//===----------------------------------------------------------------------===//
8 +//
9 +// This file implements the Mapper piece of the clang-doc tool. It implements
10 +// a RecursiveASTVisitor to look at each declaration and populate the info
11 +// into the internal representation. Each seen declaration is serialized to
12 +// to bitcode and written out to the ExecutionContext as a KV pair where the
13 +// key is the declaration's USR and the value is the serialized bitcode.
14 +//
15 +//===----------------------------------------------------------------------===//
16 +
17 +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_DOC_MAPPER_H
18 +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_DOC_MAPPER_H
19 +
20 +#include "Representation.h"
21 +#include "clang/AST/RecursiveASTVisitor.h"
22 +#include "clang/Tooling/Execution.h"
23 +
24 +using namespace clang::comments;
25 +using namespace clang::tooling;
26 +
27 +namespace clang {
28 +namespace doc {
29 +
30 +class MapASTVisitor : public clang::RecursiveASTVisitor<MapASTVisitor>,
31 + public ASTConsumer {
32 +public:
33 + explicit MapASTVisitor(ASTContext *Ctx, ClangDocContext CDCtx)
34 + : CDCtx(CDCtx) {}
35 +
36 + void HandleTranslationUnit(ASTContext &Context) override;
37 + bool VisitNamespaceDecl(const NamespaceDecl *D);
38 + bool VisitRecordDecl(const RecordDecl *D);
39 + bool VisitEnumDecl(const EnumDecl *D);
40 + bool VisitCXXMethodDecl(const CXXMethodDecl *D);
41 + bool VisitFunctionDecl(const FunctionDecl *D);
42 +
43 +private:
44 + template <typename T> bool mapDecl(const T *D);
45 +
46 + int getLine(const NamedDecl *D, const ASTContext &Context) const;
47 + llvm::SmallString<128> getFile(const NamedDecl *D, const ASTContext &Context,
48 + StringRef RootDir,
49 + bool &IsFileInRootDir) const;
50 + comments::FullComment *getComment(const NamedDecl *D,
51 + const ASTContext &Context) const;
52 +
53 + ClangDocContext CDCtx;
54 +};
55 +
56 +} // namespace doc
57 +} // namespace clang
58 +
59 +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_DOC_MAPPER_H
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
1 +//===-- Serializer.h - ClangDoc Serializer ----------------------*- C++ -*-===//
2 +//
3 +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 +// See https://llvm.org/LICENSE.txt for license information.
5 +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 +//
7 +//===----------------------------------------------------------------------===//
8 +//
9 +// This file implements the serializing functions fro the clang-doc tool. Given
10 +// a particular declaration, it collects the appropriate information and returns
11 +// a serialized bitcode string for the declaration.
12 +//
13 +//===----------------------------------------------------------------------===//
14 +
15 +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_DOC_SERIALIZE_H
16 +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_DOC_SERIALIZE_H
17 +
18 +#include "Representation.h"
19 +#include "clang/AST/AST.h"
20 +#include "clang/AST/CommentVisitor.h"
21 +#include <string>
22 +#include <vector>
23 +
24 +using namespace clang::comments;
25 +
26 +namespace clang {
27 +namespace doc {
28 +namespace serialize {
29 +
30 +// The first element will contain the relevant information about the declaration
31 +// passed as parameter.
32 +// The second element will contain the relevant information about the
33 +// declaration's parent, it can be a NamespaceInfo or RecordInfo.
34 +// Both elements can be nullptrs if the declaration shouldn't be handled.
35 +// When the declaration is handled, the first element will be a nullptr for
36 +// EnumDecl, FunctionDecl and CXXMethodDecl; they are only returned wrapped in
37 +// its parent scope. For NamespaceDecl and RecordDecl both elements are not
38 +// nullptr.
39 +std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
40 +emitInfo(const NamespaceDecl *D, const FullComment *FC, int LineNumber,
41 + StringRef File, bool IsFileInRootDir, bool PublicOnly);
42 +std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
43 +emitInfo(const RecordDecl *D, const FullComment *FC, int LineNumber,
44 + StringRef File, bool IsFileInRootDir, bool PublicOnly);
45 +std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
46 +emitInfo(const EnumDecl *D, const FullComment *FC, int LineNumber,
47 + StringRef File, bool IsFileInRootDir, bool PublicOnly);
48 +std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
49 +emitInfo(const FunctionDecl *D, const FullComment *FC, int LineNumber,
50 + StringRef File, bool IsFileInRootDir, bool PublicOnly);
51 +std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
52 +emitInfo(const CXXMethodDecl *D, const FullComment *FC, int LineNumber,
53 + StringRef File, bool IsFileInRootDir, bool PublicOnly);
54 +
55 +// Function to hash a given USR value for storage.
56 +// As USRs (Unified Symbol Resolution) could be large, especially for functions
57 +// with long type arguments, we use 160-bits SHA1(USR) values to
58 +// guarantee the uniqueness of symbols while using a relatively small amount of
59 +// memory (vs storing USRs directly).
60 +SymbolID hashUSR(llvm::StringRef USR);
61 +
62 +std::string serialize(std::unique_ptr<Info> &I);
63 +
64 +} // namespace serialize
65 +} // namespace doc
66 +} // namespace clang
67 +
68 +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_DOC_SERIALIZE_H
This diff is collapsed. Click to expand it.
1 +// Append using posix-style a file name or directory to Base
2 +function append(Base, New) {
3 + if (!New)
4 + return Base;
5 + if (Base)
6 + Base += "/";
7 + Base += New;
8 + return Base;
9 +}
10 +
11 +// Get relative path to access FilePath from CurrentDirectory
12 +function computeRelativePath(FilePath, CurrentDirectory) {
13 + var Path = FilePath;
14 + while (Path) {
15 + if (CurrentDirectory == Path)
16 + return FilePath.substring(Path.length + 1);
17 + Path = Path.substring(0, Path.lastIndexOf("/"));
18 + }
19 +
20 + var Dir = CurrentDirectory;
21 + var Result = "";
22 + while (Dir) {
23 + if (Dir == FilePath)
24 + break;
25 + Dir = Dir.substring(0, Dir.lastIndexOf("/"));
26 + Result = append(Result, "..")
27 + }
28 + Result = append(Result, FilePath.substring(Dir.length))
29 + return Result;
30 +}
31 +
32 +function genLink(Ref, CurrentDirectory) {
33 + var Path = computeRelativePath(Ref.Path, CurrentDirectory);
34 + if (Ref.RefType == "namespace")
35 + Path = append(Path, "index.html");
36 + else
37 + Path = append(Path, Ref.Name + ".html")
38 +
39 + ANode = document.createElement("a");
40 + ANode.setAttribute("href", Path);
41 + var TextNode = document.createTextNode(Ref.Name);
42 + ANode.appendChild(TextNode);
43 + return ANode;
44 +}
45 +
46 +function genHTMLOfIndex(Index, CurrentDirectory, IsOutermostList) {
47 + // Out will store the HTML elements that Index requires to be generated
48 + var Out = [];
49 + if (Index.Name) {
50 + var SpanNode = document.createElement("span");
51 + var TextNode = document.createTextNode(Index.Name);
52 + SpanNode.appendChild(genLink(Index, CurrentDirectory));
53 + Out.push(SpanNode);
54 + }
55 + if (Index.Children.length == 0)
56 + return Out;
57 + // Only the outermost list should use ol, the others should use ul
58 + var ListNodeName = IsOutermostList ? "ol" : "ul";
59 + var ListNode = document.createElement(ListNodeName);
60 + for (Child of Index.Children) {
61 + var LiNode = document.createElement("li");
62 + ChildNodes = genHTMLOfIndex(Child, CurrentDirectory, false);
63 + for (Node of ChildNodes)
64 + LiNode.appendChild(Node);
65 + ListNode.appendChild(LiNode);
66 + }
67 + Out.push(ListNode);
68 + return Out;
69 +}
70 +
71 +function createIndex(Index) {
72 + // Get the DOM element where the index will be created
73 + var IndexDiv = document.getElementById("sidebar-left");
74 + // Get the relative path of this file
75 + CurrentDirectory = IndexDiv.getAttribute("path");
76 + var IndexNodes = genHTMLOfIndex(Index, CurrentDirectory, true);
77 + for (Node of IndexNodes)
78 + IndexDiv.appendChild(Node);
79 +}
80 +
81 +// Runs after DOM loads
82 +document.addEventListener("DOMContentLoaded", function() {
83 + // JsonIndex is a variable from another file that contains the index
84 + // in JSON format
85 + var Index = JSON.parse(JsonIndex);
86 + createIndex(Index);
87 +});
1 +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/..)
2 +
3 +add_clang_tool(clang-doc
4 + ClangDocMain.cpp
5 + )
6 +
7 +clang_target_link_libraries(clang-doc
8 + PRIVATE
9 + clangAST
10 + clangASTMatchers
11 + clangBasic
12 + clangFrontend
13 + clangTooling
14 + clangToolingCore
15 + )
16 +target_link_libraries(clang-doc
17 + PRIVATE
18 + clangDoc
19 + )
20 +
21 +install(FILES ../assets/clang-doc-default-stylesheet.css
22 + DESTINATION share/clang
23 + COMPONENT clang-doc)
24 +
25 +install(FILES ../assets/index.js
26 + DESTINATION share/clang
27 + COMPONENT clang-doc)
This diff is collapsed. Click to expand it.
1 +set(LLVM_LINK_COMPONENTS
2 + support
3 + )
4 +
5 +add_clang_library(clangIncludeFixer
6 + IncludeFixer.cpp
7 + IncludeFixerContext.cpp
8 + InMemorySymbolIndex.cpp
9 + FuzzySymbolIndex.cpp
10 + SymbolIndexManager.cpp
11 + YamlSymbolIndex.cpp
12 +
13 + LINK_LIBS
14 + findAllSymbols
15 +
16 + DEPENDS
17 + omp_gen
18 + )
19 +
20 +clang_target_link_libraries(clangIncludeFixer
21 + PRIVATE
22 + clangAST
23 + clangBasic
24 + clangFormat
25 + clangFrontend
26 + clangLex
27 + clangParse
28 + clangSema
29 + clangSerialization
30 + clangTooling
31 + clangToolingCore
32 + )
33 +
34 +add_subdirectory(plugin)
35 +add_subdirectory(tool)
36 +add_subdirectory(find-all-symbols)
1 +//===--- FuzzySymbolIndex.cpp - Lookup symbols for autocomplete -*- C++ -*-===//
2 +//
3 +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 +// See https://llvm.org/LICENSE.txt for license information.
5 +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 +//
7 +//===----------------------------------------------------------------------===//
8 +#include "FuzzySymbolIndex.h"
9 +#include "llvm/Support/Regex.h"
10 +
11 +using clang::find_all_symbols::SymbolAndSignals;
12 +using llvm::StringRef;
13 +
14 +namespace clang {
15 +namespace include_fixer {
16 +namespace {
17 +
18 +class MemSymbolIndex : public FuzzySymbolIndex {
19 +public:
20 + MemSymbolIndex(std::vector<SymbolAndSignals> Symbols) {
21 + for (auto &Symbol : Symbols) {
22 + auto Tokens = tokenize(Symbol.Symbol.getName());
23 + this->Symbols.emplace_back(
24 + StringRef(llvm::join(Tokens.begin(), Tokens.end(), " ")),
25 + std::move(Symbol));
26 + }
27 + }
28 +
29 + std::vector<SymbolAndSignals> search(StringRef Query) override {
30 + auto Tokens = tokenize(Query);
31 + llvm::Regex Pattern("^" + queryRegexp(Tokens));
32 + std::vector<SymbolAndSignals> Results;
33 + for (const Entry &E : Symbols)
34 + if (Pattern.match(E.first))
35 + Results.push_back(E.second);
36 + return Results;
37 + }
38 +
39 +private:
40 + using Entry = std::pair<llvm::SmallString<32>, SymbolAndSignals>;
41 + std::vector<Entry> Symbols;
42 +};
43 +
44 +// Helpers for tokenize state machine.
45 +enum TokenizeState {
46 + EMPTY, // No pending characters.
47 + ONE_BIG, // Read one uppercase letter, could be WORD or Word.
48 + BIG_WORD, // Reading an uppercase WORD.
49 + SMALL_WORD, // Reading a lowercase word.
50 + NUMBER // Reading a number.
51 +};
52 +
53 +enum CharType { UPPER, LOWER, DIGIT, MISC };
54 +CharType classify(char c) {
55 + if (isupper(c))
56 + return UPPER;
57 + if (islower(c))
58 + return LOWER;
59 + if (isdigit(c))
60 + return DIGIT;
61 + return MISC;
62 +}
63 +
64 +} // namespace
65 +
66 +std::vector<std::string> FuzzySymbolIndex::tokenize(StringRef Text) {
67 + std::vector<std::string> Result;
68 + // State describes the treatment of text from Start to I.
69 + // Once text is Flush()ed into Result, we're done with it and advance Start.
70 + TokenizeState State = EMPTY;
71 + size_t Start = 0;
72 + auto Flush = [&](size_t End) {
73 + if (State != EMPTY) {
74 + Result.push_back(Text.substr(Start, End - Start).lower());
75 + State = EMPTY;
76 + }
77 + Start = End;
78 + };
79 + for (size_t I = 0; I < Text.size(); ++I) {
80 + CharType Type = classify(Text[I]);
81 + if (Type == MISC)
82 + Flush(I);
83 + else if (Type == LOWER)
84 + switch (State) {
85 + case BIG_WORD:
86 + Flush(I - 1); // FOOBar: first token is FOO, not FOOB.
87 + LLVM_FALLTHROUGH;
88 + case ONE_BIG:
89 + State = SMALL_WORD;
90 + LLVM_FALLTHROUGH;
91 + case SMALL_WORD:
92 + break;
93 + default:
94 + Flush(I);
95 + State = SMALL_WORD;
96 + }
97 + else if (Type == UPPER)
98 + switch (State) {
99 + case ONE_BIG:
100 + State = BIG_WORD;
101 + LLVM_FALLTHROUGH;
102 + case BIG_WORD:
103 + break;
104 + default:
105 + Flush(I);
106 + State = ONE_BIG;
107 + }
108 + else if (Type == DIGIT && State != NUMBER) {
109 + Flush(I);
110 + State = NUMBER;
111 + }
112 + }
113 + Flush(Text.size());
114 + return Result;
115 +}
116 +
117 +std::string
118 +FuzzySymbolIndex::queryRegexp(const std::vector<std::string> &Tokens) {
119 + std::string Result;
120 + for (size_t I = 0; I < Tokens.size(); ++I) {
121 + if (I)
122 + Result.append("[[:alnum:]]* ");
123 + for (size_t J = 0; J < Tokens[I].size(); ++J) {
124 + if (J)
125 + Result.append("([[:alnum:]]* )?");
126 + Result.push_back(Tokens[I][J]);
127 + }
128 + }
129 + return Result;
130 +}
131 +
132 +llvm::Expected<std::unique_ptr<FuzzySymbolIndex>>
133 +FuzzySymbolIndex::createFromYAML(StringRef FilePath) {
134 + auto Buffer = llvm::MemoryBuffer::getFile(FilePath);
135 + if (!Buffer)
136 + return llvm::errorCodeToError(Buffer.getError());
137 + return std::make_unique<MemSymbolIndex>(
138 + find_all_symbols::ReadSymbolInfosFromYAML(Buffer.get()->getBuffer()));
139 +}
140 +
141 +} // namespace include_fixer
142 +} // namespace clang
1 +//===--- FuzzySymbolIndex.h - Lookup symbols for autocomplete ---*- C++ -*-===//
2 +//
3 +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 +// See https://llvm.org/LICENSE.txt for license information.
5 +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 +//
7 +//===----------------------------------------------------------------------===//
8 +
9 +#ifndef LLVM_CLANG_TOOLS_EXTRA_INCLUDE_FIXER_FUZZY_SYMBOL_INDEX_H
10 +#define LLVM_CLANG_TOOLS_EXTRA_INCLUDE_FIXER_FUZZY_SYMBOL_INDEX_H
11 +
12 +#include "SymbolIndex.h"
13 +#include "find-all-symbols/SymbolInfo.h"
14 +#include "llvm/ADT/SmallString.h"
15 +#include "llvm/ADT/StringRef.h"
16 +#include "llvm/Support/Error.h"
17 +#include <string>
18 +#include <vector>
19 +
20 +namespace clang {
21 +namespace include_fixer {
22 +
23 +// A FuzzySymbolIndex retrieves top-level symbols matching a query string.
24 +//
25 +// It refines the contract of SymbolIndex::search to do fuzzy matching:
26 +// - symbol names are tokenized: "unique ptr", "string ref".
27 +// - query must match prefixes of symbol tokens: [upt]
28 +// - if the query has multiple tokens, splits must match: [StR], not [STr].
29 +// Helpers for tokenization and regex matching are provided.
30 +//
31 +// Implementations may choose to truncate results, refuse short queries, etc.
32 +class FuzzySymbolIndex : public SymbolIndex {
33 +public:
34 + // Loads the specified clang-include-fixer database and returns an index serving it.
35 + static llvm::Expected<std::unique_ptr<FuzzySymbolIndex>>
36 + createFromYAML(llvm::StringRef File);
37 +
38 + // Helpers for implementing indexes:
39 +
40 + // Transforms a symbol name or query into a sequence of tokens.
41 + // - URLHandlerCallback --> [url, handler, callback]
42 + // - snake_case11 --> [snake, case, 11]
43 + // - _WTF$ --> [wtf]
44 + static std::vector<std::string> tokenize(llvm::StringRef Text);
45 +
46 + // Transforms query tokens into an unanchored regexp to match symbol tokens.
47 + // - [fe f] --> /f(\w* )?e\w* f/, matches [fee fie foe].
48 + static std::string queryRegexp(const std::vector<std::string> &Tokens);
49 +};
50 +
51 +} // namespace include_fixer
52 +} // namespace clang
53 +
54 +#endif // LLVM_CLANG_TOOLS_EXTRA_INCLUDE_FIXER_FUZZY_SYMBOL_INDEX_H
1 +//===-- InMemorySymbolIndex.cpp--------------------------------------------===//
2 +//
3 +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 +// See https://llvm.org/LICENSE.txt for license information.
5 +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 +//
7 +//===----------------------------------------------------------------------===//
8 +
9 +#include "InMemorySymbolIndex.h"
10 +
11 +using clang::find_all_symbols::SymbolAndSignals;
12 +
13 +namespace clang {
14 +namespace include_fixer {
15 +
16 +InMemorySymbolIndex::InMemorySymbolIndex(
17 + const std::vector<SymbolAndSignals> &Symbols) {
18 + for (const auto &Symbol : Symbols)
19 + LookupTable[std::string(Symbol.Symbol.getName())].push_back(Symbol);
20 +}
21 +
22 +std::vector<SymbolAndSignals>
23 +InMemorySymbolIndex::search(llvm::StringRef Identifier) {
24 + auto I = LookupTable.find(std::string(Identifier));
25 + if (I != LookupTable.end())
26 + return I->second;
27 + return {};
28 +}
29 +
30 +} // namespace include_fixer
31 +} // namespace clang
1 +//===-- InMemorySymbolIndex.h -----------------------------------*- C++ -*-===//
2 +//
3 +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 +// See https://llvm.org/LICENSE.txt for license information.
5 +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 +//
7 +//===----------------------------------------------------------------------===//
8 +//
9 +#ifndef LLVM_CLANG_TOOLS_EXTRA_INCLUDE_FIXER_INMEMORYSYMBOLINDEX_H
10 +#define LLVM_CLANG_TOOLS_EXTRA_INCLUDE_FIXER_INMEMORYSYMBOLINDEX_H
11 +
12 +#include "SymbolIndex.h"
13 +#include <map>
14 +#include <string>
15 +#include <vector>
16 +
17 +namespace clang {
18 +namespace include_fixer {
19 +
20 +/// Xref database with fixed content.
21 +class InMemorySymbolIndex : public SymbolIndex {
22 +public:
23 + InMemorySymbolIndex(
24 + const std::vector<find_all_symbols::SymbolAndSignals> &Symbols);
25 +
26 + std::vector<find_all_symbols::SymbolAndSignals>
27 + search(llvm::StringRef Identifier) override;
28 +
29 +private:
30 + std::map<std::string, std::vector<find_all_symbols::SymbolAndSignals>>
31 + LookupTable;
32 +};
33 +
34 +} // namespace include_fixer
35 +} // namespace clang
36 +
37 +#endif // LLVM_CLANG_TOOLS_EXTRA_INCLUDE_FIXER_INMEMORYSYMBOLINDEX_H
1 +//===-- IncludeFixer.h - Include inserter -----------------------*- C++ -*-===//
2 +//
3 +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 +// See https://llvm.org/LICENSE.txt for license information.
5 +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 +//
7 +//===----------------------------------------------------------------------===//
8 +
9 +#ifndef LLVM_CLANG_TOOLS_EXTRA_INCLUDE_FIXER_INCLUDEFIXER_H
10 +#define LLVM_CLANG_TOOLS_EXTRA_INCLUDE_FIXER_INCLUDEFIXER_H
11 +
12 +#include "IncludeFixerContext.h"
13 +#include "SymbolIndexManager.h"
14 +#include "clang/Format/Format.h"
15 +#include "clang/Sema/ExternalSemaSource.h"
16 +#include "clang/Tooling/Core/Replacement.h"
17 +#include "clang/Tooling/Tooling.h"
18 +#include <memory>
19 +#include <vector>
20 +
21 +namespace clang {
22 +
23 +class CompilerInvocation;
24 +class DiagnosticConsumer;
25 +class FileManager;
26 +class PCHContainerOperations;
27 +
28 +namespace include_fixer {
29 +
30 +class IncludeFixerActionFactory : public clang::tooling::ToolAction {
31 +public:
32 + /// \param SymbolIndexMgr A source for matching symbols to header files.
33 + /// \param Contexts The contexts for the symbols being queried.
34 + /// \param StyleName Fallback style for reformatting.
35 + /// \param MinimizeIncludePaths whether inserted include paths are optimized.
36 + IncludeFixerActionFactory(SymbolIndexManager &SymbolIndexMgr,
37 + std::vector<IncludeFixerContext> &Contexts,
38 + StringRef StyleName,
39 + bool MinimizeIncludePaths = true);
40 +
41 + ~IncludeFixerActionFactory() override;
42 +
43 + bool
44 + runInvocation(std::shared_ptr<clang::CompilerInvocation> Invocation,
45 + clang::FileManager *Files,
46 + std::shared_ptr<clang::PCHContainerOperations> PCHContainerOps,
47 + clang::DiagnosticConsumer *Diagnostics) override;
48 +
49 +private:
50 + /// The client to use to find cross-references.
51 + SymbolIndexManager &SymbolIndexMgr;
52 +
53 + /// Multiple contexts for files being processed.
54 + std::vector<IncludeFixerContext> &Contexts;
55 +
56 + /// Whether inserted include paths should be optimized.
57 + bool MinimizeIncludePaths;
58 +
59 + /// The fallback format style for formatting after insertion if no
60 + /// clang-format config file was found.
61 + std::string FallbackStyle;
62 +};
63 +
64 +/// Create replacements, which are generated by clang-format, for the
65 +/// missing header and missing qualifiers insertions. The function uses the
66 +/// first header for insertion.
67 +///
68 +/// \param Code The source code.
69 +/// \param Context The context which contains all information for creating
70 +/// clang-include-fixer replacements.
71 +/// \param Style clang-format style being used.
72 +/// \param AddQualifiers Whether we should add qualifiers to all instances of
73 +/// an unidentified symbol.
74 +///
75 +/// \return Formatted replacements for inserting, sorting headers and adding
76 +/// qualifiers on success; otherwise, an llvm::Error carrying llvm::StringError
77 +/// is returned.
78 +llvm::Expected<tooling::Replacements> createIncludeFixerReplacements(
79 + StringRef Code, const IncludeFixerContext &Context,
80 + const format::FormatStyle &Style = format::getLLVMStyle(),
81 + bool AddQualifiers = true);
82 +
83 +/// Handles callbacks from sema, does the include lookup and turns it into an
84 +/// IncludeFixerContext.
85 +class IncludeFixerSemaSource : public clang::ExternalSemaSource {
86 +public:
87 + explicit IncludeFixerSemaSource(SymbolIndexManager &SymbolIndexMgr,
88 + bool MinimizeIncludePaths,
89 + bool GenerateDiagnostics)
90 + : SymbolIndexMgr(SymbolIndexMgr),
91 + MinimizeIncludePaths(MinimizeIncludePaths),
92 + GenerateDiagnostics(GenerateDiagnostics) {}
93 +
94 + void setCompilerInstance(CompilerInstance *CI) { this->CI = CI; }
95 + void setFilePath(StringRef FilePath) {
96 + this->FilePath = std::string(FilePath);
97 + }
98 +
99 + /// Callback for incomplete types. If we encounter a forward declaration we
100 + /// have the fully qualified name ready. Just query that.
101 + bool MaybeDiagnoseMissingCompleteType(clang::SourceLocation Loc,
102 + clang::QualType T) override;
103 +
104 + /// Callback for unknown identifiers. Try to piece together as much
105 + /// qualification as we can get and do a query.
106 + clang::TypoCorrection CorrectTypo(const DeclarationNameInfo &Typo,
107 + int LookupKind, Scope *S, CXXScopeSpec *SS,
108 + CorrectionCandidateCallback &CCC,
109 + DeclContext *MemberContext,
110 + bool EnteringContext,
111 + const ObjCObjectPointerType *OPT) override;
112 +
113 + /// Get the minimal include for a given path.
114 + std::string minimizeInclude(StringRef Include,
115 + const clang::SourceManager &SourceManager,
116 + clang::HeaderSearch &HeaderSearch) const;
117 +
118 + /// Get the include fixer context for the queried symbol.
119 + IncludeFixerContext getIncludeFixerContext(
120 + const clang::SourceManager &SourceManager,
121 + clang::HeaderSearch &HeaderSearch,
122 + ArrayRef<find_all_symbols::SymbolInfo> MatchedSymbols) const;
123 +
124 + /// Get the global matched symbols.
125 + ArrayRef<find_all_symbols::SymbolInfo> getMatchedSymbols() const {
126 + return MatchedSymbols;
127 + }
128 +
129 +private:
130 + /// Query the database for a given identifier.
131 + std::vector<find_all_symbols::SymbolInfo>
132 + query(StringRef Query, StringRef ScopedQualifiers, tooling::Range Range);
133 +
134 + CompilerInstance *CI;
135 +
136 + /// The client to use to find cross-references.
137 + SymbolIndexManager &SymbolIndexMgr;
138 +
139 + /// The information of the symbols being queried.
140 + std::vector<IncludeFixerContext::QuerySymbolInfo> QuerySymbolInfos;
141 +
142 + /// All symbol candidates which match QuerySymbol. We only include the first
143 + /// discovered identifier to avoid getting caught in results from error
144 + /// recovery.
145 + std::vector<find_all_symbols::SymbolInfo> MatchedSymbols;
146 +
147 + /// The file path to the file being processed.
148 + std::string FilePath;
149 +
150 + /// Whether we should use the smallest possible include path.
151 + bool MinimizeIncludePaths = true;
152 +
153 + /// Whether we should generate diagnostics with fixits for missing symbols.
154 + bool GenerateDiagnostics = false;
155 +};
156 +} // namespace include_fixer
157 +} // namespace clang
158 +
159 +#endif // LLVM_CLANG_TOOLS_EXTRA_INCLUDE_FIXER_INCLUDEFIXER_H
1 +//===-- IncludeFixerContext.cpp - Include fixer context ---------*- C++ -*-===//
2 +//
3 +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 +// See https://llvm.org/LICENSE.txt for license information.
5 +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 +//
7 +//===----------------------------------------------------------------------===//
8 +
9 +#include "IncludeFixerContext.h"
10 +#include <algorithm>
11 +
12 +namespace clang {
13 +namespace include_fixer {
14 +
15 +namespace {
16 +
17 +// Splits a multiply qualified names (e.g. a::b::c).
18 +llvm::SmallVector<llvm::StringRef, 8>
19 +SplitQualifiers(llvm::StringRef StringQualifiers) {
20 + llvm::SmallVector<llvm::StringRef, 8> Qualifiers;
21 + StringQualifiers.split(Qualifiers, "::");
22 + return Qualifiers;
23 +}
24 +
25 +std::string createQualifiedNameForReplacement(
26 + llvm::StringRef RawSymbolName,
27 + llvm::StringRef SymbolScopedQualifiersName,
28 + const find_all_symbols::SymbolInfo &MatchedSymbol) {
29 + // No need to add missing qualifiers if SymbolIdentifier has a global scope
30 + // operator "::".
31 + if (RawSymbolName.startswith("::"))
32 + return std::string(RawSymbolName);
33 +
34 + std::string QualifiedName = MatchedSymbol.getQualifiedName();
35 +
36 + // For nested classes, the qualified name constructed from database misses
37 + // some stripped qualifiers, because when we search a symbol in database,
38 + // we strip qualifiers from the end until we find a result. So append the
39 + // missing stripped qualifiers here.
40 + //
41 + // Get stripped qualifiers.
42 + auto SymbolQualifiers = SplitQualifiers(RawSymbolName);
43 + std::string StrippedQualifiers;
44 + while (!SymbolQualifiers.empty() &&
45 + !llvm::StringRef(QualifiedName).endswith(SymbolQualifiers.back())) {
46 + StrippedQualifiers =
47 + "::" + SymbolQualifiers.back().str() + StrippedQualifiers;
48 + SymbolQualifiers.pop_back();
49 + }
50 + // Append the missing stripped qualifiers.
51 + std::string FullyQualifiedName = QualifiedName + StrippedQualifiers;
52 +
53 + // Try to find and skip the common prefix qualifiers.
54 + auto FullySymbolQualifiers = SplitQualifiers(FullyQualifiedName);
55 + auto ScopedQualifiers = SplitQualifiers(SymbolScopedQualifiersName);
56 + auto FullySymbolQualifiersIter = FullySymbolQualifiers.begin();
57 + auto SymbolScopedQualifiersIter = ScopedQualifiers.begin();
58 + while (FullySymbolQualifiersIter != FullySymbolQualifiers.end() &&
59 + SymbolScopedQualifiersIter != ScopedQualifiers.end()) {
60 + if (*FullySymbolQualifiersIter != *SymbolScopedQualifiersIter)
61 + break;
62 + ++FullySymbolQualifiersIter;
63 + ++SymbolScopedQualifiersIter;
64 + }
65 + std::string Result;
66 + for (; FullySymbolQualifiersIter != FullySymbolQualifiers.end();
67 + ++FullySymbolQualifiersIter) {
68 + if (!Result.empty())
69 + Result += "::";
70 + Result += *FullySymbolQualifiersIter;
71 + }
72 + return Result;
73 +}
74 +
75 +} // anonymous namespace
76 +
77 +IncludeFixerContext::IncludeFixerContext(
78 + StringRef FilePath, std::vector<QuerySymbolInfo> QuerySymbols,
79 + std::vector<find_all_symbols::SymbolInfo> Symbols)
80 + : FilePath(FilePath), QuerySymbolInfos(std::move(QuerySymbols)),
81 + MatchedSymbols(std::move(Symbols)) {
82 + // Remove replicated QuerySymbolInfos with the same range.
83 + //
84 + // QuerySymbolInfos may contain replicated elements. Because CorrectTypo
85 + // callback doesn't always work as we expected. In somecases, it will be
86 + // triggered at the same position or unidentified symbol multiple times.
87 + std::sort(QuerySymbolInfos.begin(), QuerySymbolInfos.end(),
88 + [&](const QuerySymbolInfo &A, const QuerySymbolInfo &B) {
89 + return std::make_pair(A.Range.getOffset(), A.Range.getLength()) <
90 + std::make_pair(B.Range.getOffset(), B.Range.getLength());
91 + });
92 + QuerySymbolInfos.erase(
93 + std::unique(QuerySymbolInfos.begin(), QuerySymbolInfos.end(),
94 + [](const QuerySymbolInfo &A, const QuerySymbolInfo &B) {
95 + return A.Range == B.Range;
96 + }),
97 + QuerySymbolInfos.end());
98 + for (const auto &Symbol : MatchedSymbols) {
99 + HeaderInfos.push_back(
100 + {Symbol.getFilePath().str(),
101 + createQualifiedNameForReplacement(
102 + QuerySymbolInfos.front().RawIdentifier,
103 + QuerySymbolInfos.front().ScopedQualifiers, Symbol)});
104 + }
105 + // Deduplicate header infos.
106 + HeaderInfos.erase(std::unique(HeaderInfos.begin(), HeaderInfos.end(),
107 + [](const HeaderInfo &A, const HeaderInfo &B) {
108 + return A.Header == B.Header &&
109 + A.QualifiedName == B.QualifiedName;
110 + }),
111 + HeaderInfos.end());
112 +}
113 +
114 +} // include_fixer
115 +} // clang
1 +//===-- IncludeFixerContext.h - Include fixer context -----------*- C++ -*-===//
2 +//
3 +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 +// See https://llvm.org/LICENSE.txt for license information.
5 +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 +//
7 +//===----------------------------------------------------------------------===//
8 +
9 +#ifndef LLVM_CLANG_TOOLS_EXTRA_INCLUDE_FIXER_INCLUDEFIXERCONTEXT_H
10 +#define LLVM_CLANG_TOOLS_EXTRA_INCLUDE_FIXER_INCLUDEFIXERCONTEXT_H
11 +
12 +#include "find-all-symbols/SymbolInfo.h"
13 +#include "clang/Tooling/Core/Replacement.h"
14 +#include <string>
15 +#include <vector>
16 +
17 +namespace clang {
18 +namespace include_fixer {
19 +
20 +/// A context for a file being processed. It includes all query
21 +/// information, e.g. symbols being queried in database, all header candidates.
22 +class IncludeFixerContext {
23 +public:
24 + struct HeaderInfo {
25 + /// The header where QualifiedName comes from.
26 + std::string Header;
27 + /// A symbol name with completed namespace qualifiers which will
28 + /// replace the original symbol.
29 + std::string QualifiedName;
30 + };
31 +
32 + struct QuerySymbolInfo {
33 + /// The raw symbol name being queried in database. This name might
34 + /// miss some namespace qualifiers, and will be replaced by a fully
35 + /// qualified one.
36 + std::string RawIdentifier;
37 +
38 + /// The qualifiers of the scope in which SymbolIdentifier lookup
39 + /// occurs. It is represented as a sequence of names and scope resolution
40 + /// operators ::, ending with a scope resolution operator (e.g. a::b::).
41 + /// Empty if SymbolIdentifier is not in a specific scope.
42 + std::string ScopedQualifiers;
43 +
44 + /// The replacement range of RawIdentifier.
45 + tooling::Range Range;
46 + };
47 +
48 + IncludeFixerContext() = default;
49 + IncludeFixerContext(StringRef FilePath,
50 + std::vector<QuerySymbolInfo> QuerySymbols,
51 + std::vector<find_all_symbols::SymbolInfo> Symbols);
52 +
53 + /// Get symbol name.
54 + llvm::StringRef getSymbolIdentifier() const {
55 + return QuerySymbolInfos.front().RawIdentifier;
56 + }
57 +
58 + /// Get replacement range of the symbol.
59 + tooling::Range getSymbolRange() const {
60 + return QuerySymbolInfos.front().Range;
61 + }
62 +
63 + /// Get the file path to the file being processed.
64 + StringRef getFilePath() const { return FilePath; }
65 +
66 + /// Get header information.
67 + const std::vector<HeaderInfo> &getHeaderInfos() const { return HeaderInfos; }
68 +
69 + /// Get information of symbols being querid.
70 + const std::vector<QuerySymbolInfo> &getQuerySymbolInfos() const {
71 + return QuerySymbolInfos;
72 + }
73 +
74 +private:
75 + friend struct llvm::yaml::MappingTraits<IncludeFixerContext>;
76 +
77 + /// The file path to the file being processed.
78 + std::string FilePath;
79 +
80 + /// All instances of an unidentified symbol being queried.
81 + std::vector<QuerySymbolInfo> QuerySymbolInfos;
82 +
83 + /// The symbol candidates which match SymbolIdentifier. The symbols are
84 + /// sorted in a descending order based on the popularity info in SymbolInfo.
85 + std::vector<find_all_symbols::SymbolInfo> MatchedSymbols;
86 +
87 + /// The header information.
88 + std::vector<HeaderInfo> HeaderInfos;
89 +};
90 +
91 +} // namespace include_fixer
92 +} // namespace clang
93 +
94 +#endif // LLVM_CLANG_TOOLS_EXTRA_INCLUDE_FIXER_INCLUDEFIXERCONTEXT_H
1 +//===-- SymbolIndex.h - Interface for symbol-header matching ----*- C++ -*-===//
2 +//
3 +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 +// See https://llvm.org/LICENSE.txt for license information.
5 +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 +//
7 +//===----------------------------------------------------------------------===//
8 +
9 +#ifndef LLVM_CLANG_TOOLS_EXTRA_INCLUDE_FIXER_SYMBOLINDEX_H
10 +#define LLVM_CLANG_TOOLS_EXTRA_INCLUDE_FIXER_SYMBOLINDEX_H
11 +
12 +#include "find-all-symbols/SymbolInfo.h"
13 +#include "llvm/ADT/StringRef.h"
14 +#include <vector>
15 +
16 +namespace clang {
17 +namespace include_fixer {
18 +
19 +/// This class provides an interface for finding all `SymbolInfo`s corresponding
20 +/// to a symbol name from a symbol database.
21 +class SymbolIndex {
22 +public:
23 + virtual ~SymbolIndex() = default;
24 +
25 + /// Search for all `SymbolInfo`s corresponding to an identifier.
26 + /// \param Identifier The unqualified identifier being searched for.
27 + /// \returns A list of `SymbolInfo` candidates.
28 + // FIXME: Expose the type name so we can also insert using declarations (or
29 + // fix the usage)
30 + virtual std::vector<find_all_symbols::SymbolAndSignals>
31 + search(llvm::StringRef Identifier) = 0;
32 +};
33 +
34 +} // namespace include_fixer
35 +} // namespace clang
36 +
37 +#endif // LLVM_CLANG_TOOLS_EXTRA_INCLUDE_FIXER_SYMBOLINDEX_H
1 +//===-- SymbolIndexManager.cpp - Managing multiple SymbolIndices-*- C++ -*-===//
2 +//
3 +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 +// See https://llvm.org/LICENSE.txt for license information.
5 +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 +//
7 +//===----------------------------------------------------------------------===//
8 +
9 +#include "SymbolIndexManager.h"
10 +#include "find-all-symbols/SymbolInfo.h"
11 +#include "llvm/ADT/DenseMap.h"
12 +#include "llvm/ADT/SmallVector.h"
13 +#include "llvm/Support/Debug.h"
14 +#include "llvm/Support/Path.h"
15 +
16 +#define DEBUG_TYPE "clang-include-fixer"
17 +
18 +namespace clang {
19 +namespace include_fixer {
20 +
21 +using find_all_symbols::SymbolInfo;
22 +using find_all_symbols::SymbolAndSignals;
23 +
24 +// Calculate a score based on whether we think the given header is closely
25 +// related to the given source file.
26 +static double similarityScore(llvm::StringRef FileName,
27 + llvm::StringRef Header) {
28 + // Compute the maximum number of common path segments between Header and
29 + // a suffix of FileName.
30 + // We do not do a full longest common substring computation, as Header
31 + // specifies the path we would directly #include, so we assume it is rooted
32 + // relatively to a subproject of the repository.
33 + int MaxSegments = 1;
34 + for (auto FileI = llvm::sys::path::begin(FileName),
35 + FileE = llvm::sys::path::end(FileName);
36 + FileI != FileE; ++FileI) {
37 + int Segments = 0;
38 + for (auto HeaderI = llvm::sys::path::begin(Header),
39 + HeaderE = llvm::sys::path::end(Header), I = FileI;
40 + HeaderI != HeaderE && *I == *HeaderI && I != FileE; ++I, ++HeaderI) {
41 + ++Segments;
42 + }
43 + MaxSegments = std::max(Segments, MaxSegments);
44 + }
45 + return MaxSegments;
46 +}
47 +
48 +static void rank(std::vector<SymbolAndSignals> &Symbols,
49 + llvm::StringRef FileName) {
50 + llvm::DenseMap<llvm::StringRef, double> Score;
51 + for (const auto &Symbol : Symbols) {
52 + // Calculate a score from the similarity of the header the symbol is in
53 + // with the current file and the popularity of the symbol.
54 + double NewScore = similarityScore(FileName, Symbol.Symbol.getFilePath()) *
55 + (1.0 + std::log2(1 + Symbol.Signals.Seen));
56 + double &S = Score[Symbol.Symbol.getFilePath()];
57 + S = std::max(S, NewScore);
58 + }
59 + // Sort by the gathered scores. Use file name as a tie breaker so we can
60 + // deduplicate.
61 + std::sort(Symbols.begin(), Symbols.end(),
62 + [&](const SymbolAndSignals &A, const SymbolAndSignals &B) {
63 + auto AS = Score[A.Symbol.getFilePath()];
64 + auto BS = Score[B.Symbol.getFilePath()];
65 + if (AS != BS)
66 + return AS > BS;
67 + return A.Symbol.getFilePath() < B.Symbol.getFilePath();
68 + });
69 +}
70 +
71 +std::vector<find_all_symbols::SymbolInfo>
72 +SymbolIndexManager::search(llvm::StringRef Identifier,
73 + bool IsNestedSearch,
74 + llvm::StringRef FileName) const {
75 + // The identifier may be fully qualified, so split it and get all the context
76 + // names.
77 + llvm::SmallVector<llvm::StringRef, 8> Names;
78 + Identifier.split(Names, "::");
79 +
80 + bool IsFullyQualified = false;
81 + if (Identifier.startswith("::")) {
82 + Names.erase(Names.begin()); // Drop first (empty) element.
83 + IsFullyQualified = true;
84 + }
85 +
86 + // As long as we don't find a result keep stripping name parts from the end.
87 + // This is to support nested classes which aren't recorded in the database.
88 + // Eventually we will either hit a class (namespaces aren't in the database
89 + // either) and can report that result.
90 + bool TookPrefix = false;
91 + std::vector<SymbolAndSignals> MatchedSymbols;
92 + do {
93 + std::vector<SymbolAndSignals> Symbols;
94 + for (const auto &DB : SymbolIndices) {
95 + auto Res = DB.get()->search(Names.back());
96 + Symbols.insert(Symbols.end(), Res.begin(), Res.end());
97 + }
98 +
99 + LLVM_DEBUG(llvm::dbgs() << "Searching " << Names.back() << "... got "
100 + << Symbols.size() << " results...\n");
101 +
102 + for (auto &SymAndSig : Symbols) {
103 + const SymbolInfo &Symbol = SymAndSig.Symbol;
104 + // Match the identifier name without qualifier.
105 + bool IsMatched = true;
106 + auto SymbolContext = Symbol.getContexts().begin();
107 + auto IdentiferContext = Names.rbegin() + 1; // Skip identifier name.
108 + // Match the remaining context names.
109 + while (IdentiferContext != Names.rend() &&
110 + SymbolContext != Symbol.getContexts().end()) {
111 + if (SymbolContext->second == *IdentiferContext) {
112 + ++IdentiferContext;
113 + ++SymbolContext;
114 + } else if (SymbolContext->first ==
115 + find_all_symbols::SymbolInfo::ContextType::EnumDecl) {
116 + // Skip non-scoped enum context.
117 + ++SymbolContext;
118 + } else {
119 + IsMatched = false;
120 + break;
121 + }
122 + }
123 +
124 + // If the name was qualified we only want to add results if we evaluated
125 + // all contexts.
126 + if (IsFullyQualified)
127 + IsMatched &= (SymbolContext == Symbol.getContexts().end());
128 +
129 + // FIXME: Support full match. At this point, we only find symbols in
130 + // database which end with the same contexts with the identifier.
131 + if (IsMatched && IdentiferContext == Names.rend()) {
132 + // If we're in a situation where we took a prefix but the thing we
133 + // found couldn't possibly have a nested member ignore it.
134 + if (TookPrefix &&
135 + (Symbol.getSymbolKind() == SymbolInfo::SymbolKind::Function ||
136 + Symbol.getSymbolKind() == SymbolInfo::SymbolKind::Variable ||
137 + Symbol.getSymbolKind() ==
138 + SymbolInfo::SymbolKind::EnumConstantDecl ||
139 + Symbol.getSymbolKind() == SymbolInfo::SymbolKind::Macro))
140 + continue;
141 +
142 + MatchedSymbols.push_back(std::move(SymAndSig));
143 + }
144 + }
145 + Names.pop_back();
146 + TookPrefix = true;
147 + } while (MatchedSymbols.empty() && !Names.empty() && IsNestedSearch);
148 +
149 + rank(MatchedSymbols, FileName);
150 + // Strip signals, they are no longer needed.
151 + std::vector<SymbolInfo> Res;
152 + for (auto &SymAndSig : MatchedSymbols)
153 + Res.push_back(std::move(SymAndSig.Symbol));
154 + return Res;
155 +}
156 +
157 +} // namespace include_fixer
158 +} // namespace clang
1 +//===-- SymbolIndexManager.h - Managing multiple SymbolIndices --*- C++ -*-===//
2 +//
3 +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 +// See https://llvm.org/LICENSE.txt for license information.
5 +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 +//
7 +//===----------------------------------------------------------------------===//
8 +
9 +#ifndef LLVM_CLANG_TOOLS_EXTRA_INCLUDE_FIXER_SYMBOLINDEXMANAGER_H
10 +#define LLVM_CLANG_TOOLS_EXTRA_INCLUDE_FIXER_SYMBOLINDEXMANAGER_H
11 +
12 +#include "SymbolIndex.h"
13 +#include "find-all-symbols/SymbolInfo.h"
14 +#include "llvm/ADT/StringRef.h"
15 +
16 +#ifdef _MSC_VER
17 +// Disable warnings from ppltasks.h transitively included by <future>.
18 +#pragma warning(push)
19 +#pragma warning(disable:4530)
20 +#endif
21 +
22 +#include <future>
23 +
24 +#ifdef _MSC_VER
25 +#pragma warning(pop)
26 +#endif
27 +
28 +namespace clang {
29 +namespace include_fixer {
30 +
31 +/// This class provides an interface for finding the header files corresponding
32 +/// to an identifier in the source code from multiple symbol databases.
33 +class SymbolIndexManager {
34 +public:
35 + void addSymbolIndex(std::function<std::unique_ptr<SymbolIndex>()> F) {
36 +#if LLVM_ENABLE_THREADS
37 + auto Strategy = std::launch::async;
38 +#else
39 + auto Strategy = std::launch::deferred;
40 +#endif
41 + SymbolIndices.push_back(std::async(Strategy, F));
42 + }
43 +
44 + /// Search for header files to be included for an identifier.
45 + /// \param Identifier The identifier being searched for. May or may not be
46 + /// fully qualified.
47 + /// \param IsNestedSearch Whether searching nested classes. If true, the
48 + /// method tries to strip identifier name parts from the end until it
49 + /// finds the corresponding candidates in database (e.g for identifier
50 + /// "b::foo", the method will try to find "b" if it fails to find
51 + /// "b::foo").
52 + ///
53 + /// \returns A list of symbol candidates.
54 + std::vector<find_all_symbols::SymbolInfo>
55 + search(llvm::StringRef Identifier, bool IsNestedSearch = true,
56 + llvm::StringRef FileName = "") const;
57 +
58 +private:
59 + std::vector<std::shared_future<std::unique_ptr<SymbolIndex>>> SymbolIndices;
60 +};
61 +
62 +} // namespace include_fixer
63 +} // namespace clang
64 +
65 +#endif
1 +//===-- YamlSymbolIndex.cpp -----------------------------------------------===//
2 +//
3 +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 +// See https://llvm.org/LICENSE.txt for license information.
5 +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 +//
7 +//===----------------------------------------------------------------------===//
8 +
9 +#include "YamlSymbolIndex.h"
10 +#include "llvm/ADT/SmallVector.h"
11 +#include "llvm/Support/Errc.h"
12 +#include "llvm/Support/FileSystem.h"
13 +#include "llvm/Support/MemoryBuffer.h"
14 +#include "llvm/Support/Path.h"
15 +#include <string>
16 +#include <vector>
17 +
18 +using clang::find_all_symbols::SymbolInfo;
19 +using clang::find_all_symbols::SymbolAndSignals;
20 +
21 +namespace clang {
22 +namespace include_fixer {
23 +
24 +llvm::ErrorOr<std::unique_ptr<YamlSymbolIndex>>
25 +YamlSymbolIndex::createFromFile(llvm::StringRef FilePath) {
26 + auto Buffer = llvm::MemoryBuffer::getFile(FilePath);
27 + if (!Buffer)
28 + return Buffer.getError();
29 +
30 + return std::unique_ptr<YamlSymbolIndex>(new YamlSymbolIndex(
31 + find_all_symbols::ReadSymbolInfosFromYAML(Buffer.get()->getBuffer())));
32 +}
33 +
34 +llvm::ErrorOr<std::unique_ptr<YamlSymbolIndex>>
35 +YamlSymbolIndex::createFromDirectory(llvm::StringRef Directory,
36 + llvm::StringRef Name) {
37 + // Walk upwards from Directory, looking for files.
38 + for (llvm::SmallString<128> PathStorage = Directory; !Directory.empty();
39 + Directory = llvm::sys::path::parent_path(Directory)) {
40 + assert(Directory.size() <= PathStorage.size());
41 + PathStorage.resize(Directory.size()); // Shrink to parent.
42 + llvm::sys::path::append(PathStorage, Name);
43 + if (auto DB = createFromFile(PathStorage))
44 + return DB;
45 + }
46 + return llvm::make_error_code(llvm::errc::no_such_file_or_directory);
47 +}
48 +
49 +std::vector<SymbolAndSignals>
50 +YamlSymbolIndex::search(llvm::StringRef Identifier) {
51 + std::vector<SymbolAndSignals> Results;
52 + for (const auto &Symbol : Symbols) {
53 + if (Symbol.Symbol.getName() == Identifier)
54 + Results.push_back(Symbol);
55 + }
56 + return Results;
57 +}
58 +
59 +} // namespace include_fixer
60 +} // namespace clang
1 +//===-- YamlSymbolIndex.h ---------------------------------------*- C++ -*-===//
2 +//
3 +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 +// See https://llvm.org/LICENSE.txt for license information.
5 +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 +//
7 +//===----------------------------------------------------------------------===//
8 +
9 +#ifndef LLVM_CLANG_TOOLS_EXTRA_INCLUDE_FIXER_YAMLSYMBOLINDEX_H
10 +#define LLVM_CLANG_TOOLS_EXTRA_INCLUDE_FIXER_YAMLSYMBOLINDEX_H
11 +
12 +#include "SymbolIndex.h"
13 +#include "find-all-symbols/SymbolInfo.h"
14 +#include "llvm/Support/ErrorOr.h"
15 +#include <map>
16 +#include <vector>
17 +
18 +namespace clang {
19 +namespace include_fixer {
20 +
21 +/// Yaml format database.
22 +class YamlSymbolIndex : public SymbolIndex {
23 +public:
24 + /// Create a new Yaml db from a file.
25 + static llvm::ErrorOr<std::unique_ptr<YamlSymbolIndex>>
26 + createFromFile(llvm::StringRef FilePath);
27 + /// Look for a file called \c Name in \c Directory and all parent directories.
28 + static llvm::ErrorOr<std::unique_ptr<YamlSymbolIndex>>
29 + createFromDirectory(llvm::StringRef Directory, llvm::StringRef Name);
30 +
31 + std::vector<find_all_symbols::SymbolAndSignals>
32 + search(llvm::StringRef Identifier) override;
33 +
34 +private:
35 + explicit YamlSymbolIndex(
36 + std::vector<find_all_symbols::SymbolAndSignals> Symbols)
37 + : Symbols(std::move(Symbols)) {}
38 +
39 + std::vector<find_all_symbols::SymbolAndSignals> Symbols;
40 +};
41 +
42 +} // namespace include_fixer
43 +} // namespace clang
44 +
45 +#endif // LLVM_CLANG_TOOLS_EXTRA_INCLUDE_FIXER_YAMLSYMBOLINDEX_H
1 +set(LLVM_LINK_COMPONENTS
2 + Support
3 + FrontendOpenMP
4 + )
5 +
6 +add_clang_library(findAllSymbols
7 + FindAllSymbols.cpp
8 + FindAllSymbolsAction.cpp
9 + FindAllMacros.cpp
10 + HeaderMapCollector.cpp
11 + PathConfig.cpp
12 + PragmaCommentHandler.cpp
13 + STLPostfixHeaderMap.cpp
14 + SymbolInfo.cpp
15 +
16 + DEPENDS
17 + omp_gen
18 + )
19 +
20 +clang_target_link_libraries(findAllSymbols
21 + PRIVATE
22 + clangAST
23 + clangASTMatchers
24 + clangBasic
25 + clangFrontend
26 + clangLex
27 + clangTooling
28 + )
29 +
30 +add_subdirectory(tool)
1 +//===-- FindAllMacros.cpp - find all macros ---------------------*- C++ -*-===//
2 +//
3 +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 +// See https://llvm.org/LICENSE.txt for license information.
5 +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 +//
7 +//===----------------------------------------------------------------------===//
8 +
9 +#include "FindAllMacros.h"
10 +#include "HeaderMapCollector.h"
11 +#include "PathConfig.h"
12 +#include "SymbolInfo.h"
13 +#include "clang/Basic/FileManager.h"
14 +#include "clang/Basic/IdentifierTable.h"
15 +#include "clang/Basic/SourceManager.h"
16 +#include "clang/Lex/MacroInfo.h"
17 +#include "clang/Lex/Token.h"
18 +#include "llvm/Support/Path.h"
19 +
20 +namespace clang {
21 +namespace find_all_symbols {
22 +
23 +llvm::Optional<SymbolInfo>
24 +FindAllMacros::CreateMacroSymbol(const Token &MacroNameTok,
25 + const MacroInfo *info) {
26 + std::string FilePath =
27 + getIncludePath(*SM, info->getDefinitionLoc(), Collector);
28 + if (FilePath.empty())
29 + return llvm::None;
30 + return SymbolInfo(MacroNameTok.getIdentifierInfo()->getName(),
31 + SymbolInfo::SymbolKind::Macro, FilePath, {});
32 +}
33 +
34 +void FindAllMacros::MacroDefined(const Token &MacroNameTok,
35 + const MacroDirective *MD) {
36 + if (auto Symbol = CreateMacroSymbol(MacroNameTok, MD->getMacroInfo()))
37 + ++FileSymbols[*Symbol].Seen;
38 +}
39 +
40 +void FindAllMacros::MacroUsed(const Token &Name, const MacroDefinition &MD) {
41 + if (!MD || !SM->isInMainFile(SM->getExpansionLoc(Name.getLocation())))
42 + return;
43 + if (auto Symbol = CreateMacroSymbol(Name, MD.getMacroInfo()))
44 + ++FileSymbols[*Symbol].Used;
45 +}
46 +
47 +void FindAllMacros::MacroExpands(const Token &MacroNameTok,
48 + const MacroDefinition &MD, SourceRange Range,
49 + const MacroArgs *Args) {
50 + MacroUsed(MacroNameTok, MD);
51 +}
52 +
53 +void FindAllMacros::Ifdef(SourceLocation Loc, const Token &MacroNameTok,
54 + const MacroDefinition &MD) {
55 + MacroUsed(MacroNameTok, MD);
56 +}
57 +
58 +void FindAllMacros::Ifndef(SourceLocation Loc, const Token &MacroNameTok,
59 + const MacroDefinition &MD) {
60 + MacroUsed(MacroNameTok, MD);
61 +}
62 +
63 +void FindAllMacros::EndOfMainFile() {
64 + Reporter->reportSymbols(SM->getFileEntryForID(SM->getMainFileID())->getName(),
65 + FileSymbols);
66 + FileSymbols.clear();
67 +}
68 +
69 +} // namespace find_all_symbols
70 +} // namespace clang
1 +//===-- FindAllMacros.h - find all macros -----------------------*- C++ -*-===//
2 +//
3 +//
4 +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
5 +// See https://llvm.org/LICENSE.txt for license information.
6 +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 +//
8 +//===----------------------------------------------------------------------===//
9 +
10 +#ifndef LLVM_CLANG_TOOLS_EXTRA_FIND_ALL_SYMBOLS_FIND_ALL_MACROS_H
11 +#define LLVM_CLANG_TOOLS_EXTRA_FIND_ALL_SYMBOLS_FIND_ALL_MACROS_H
12 +
13 +#include "SymbolInfo.h"
14 +#include "SymbolReporter.h"
15 +#include "clang/Lex/PPCallbacks.h"
16 +
17 +namespace clang {
18 +class MacroInfo;
19 +namespace find_all_symbols {
20 +
21 +class HeaderMapCollector;
22 +
23 +/// A preprocessor that collects all macro symbols.
24 +/// The contexts of a macro will be ignored since they are not available during
25 +/// preprocessing period.
26 +class FindAllMacros : public clang::PPCallbacks {
27 +public:
28 + explicit FindAllMacros(SymbolReporter *Reporter, SourceManager *SM,
29 + HeaderMapCollector *Collector = nullptr)
30 + : Reporter(Reporter), SM(SM), Collector(Collector) {}
31 +
32 + void MacroDefined(const Token &MacroNameTok,
33 + const MacroDirective *MD) override;
34 +
35 + void MacroExpands(const Token &MacroNameTok, const MacroDefinition &MD,
36 + SourceRange Range, const MacroArgs *Args) override;
37 +
38 + void Ifdef(SourceLocation Loc, const Token &MacroNameTok,
39 + const MacroDefinition &MD) override;
40 +
41 + void Ifndef(SourceLocation Loc, const Token &MacroNameTok,
42 + const MacroDefinition &MD) override;
43 +
44 + void EndOfMainFile() override;
45 +
46 +private:
47 + llvm::Optional<SymbolInfo> CreateMacroSymbol(const Token &MacroNameTok,
48 + const MacroInfo *MD);
49 + // Not a callback, just a common path for all usage types.
50 + void MacroUsed(const Token &Name, const MacroDefinition &MD);
51 +
52 + SymbolInfo::SignalMap FileSymbols;
53 + // Reporter for SymbolInfo.
54 + SymbolReporter *const Reporter;
55 + SourceManager *const SM;
56 + // A remapping header file collector allowing clients to include a different
57 + // header.
58 + HeaderMapCollector *const Collector;
59 +};
60 +
61 +} // namespace find_all_symbols
62 +} // namespace clang
63 +
64 +#endif // LLVM_CLANG_TOOLS_EXTRA_FIND_ALL_SYMBOLS_FIND_ALL_MACROS_H
1 +//===-- FindAllSymbols.h - find all symbols----------------------*- C++ -*-===//
2 +//
3 +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 +// See https://llvm.org/LICENSE.txt for license information.
5 +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 +//
7 +//===----------------------------------------------------------------------===//
8 +
9 +#ifndef LLVM_CLANG_TOOLS_EXTRA_FIND_ALL_SYMBOLS_SYMBOL_MATCHER_H
10 +#define LLVM_CLANG_TOOLS_EXTRA_FIND_ALL_SYMBOLS_SYMBOL_MATCHER_H
11 +
12 +#include "SymbolInfo.h"
13 +#include "SymbolReporter.h"
14 +#include "clang/ASTMatchers/ASTMatchFinder.h"
15 +#include <string>
16 +
17 +namespace clang {
18 +namespace find_all_symbols {
19 +
20 +class HeaderMapCollector;
21 +
22 +/// FindAllSymbols collects all classes, free standing functions and
23 +/// global variables with some extra information such as the path of the header
24 +/// file, the namespaces they are contained in, the type of variables and the
25 +/// parameter types of functions.
26 +///
27 +/// NOTE:
28 +/// - Symbols declared in main files are not collected since they can not be
29 +/// included.
30 +/// - Member functions are not collected because accessing them must go
31 +/// through the class. #include fixer only needs the class name to find
32 +/// headers.
33 +///
34 +class FindAllSymbols : public ast_matchers::MatchFinder::MatchCallback {
35 +public:
36 + explicit FindAllSymbols(SymbolReporter *Reporter,
37 + HeaderMapCollector *Collector = nullptr)
38 + : Reporter(Reporter), Collector(Collector) {}
39 +
40 + void registerMatchers(ast_matchers::MatchFinder *MatchFinder);
41 +
42 + void run(const ast_matchers::MatchFinder::MatchResult &result) override;
43 +
44 +protected:
45 + void onEndOfTranslationUnit() override;
46 +
47 +private:
48 + // Current source file being processed, filled by first symbol found.
49 + std::string Filename;
50 + // Findings for the current source file, flushed on onEndOfTranslationUnit.
51 + SymbolInfo::SignalMap FileSymbols;
52 + // Reporter for SymbolInfo.
53 + SymbolReporter *const Reporter;
54 + // A remapping header file collector allowing clients include a different
55 + // header.
56 + HeaderMapCollector *const Collector;
57 +};
58 +
59 +} // namespace find_all_symbols
60 +} // namespace clang
61 +
62 +#endif // LLVM_CLANG_TOOLS_EXTRA_FIND_ALL_SYMBOLS_SYMBOL_MATCHER_H
1 +//===-- FindAllSymbolsAction.cpp - find all symbols action --------*- C++ -*-===//
2 +//
3 +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 +// See https://llvm.org/LICENSE.txt for license information.
5 +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 +//
7 +//===----------------------------------------------------------------------===//
8 +
9 +#include "FindAllSymbolsAction.h"
10 +#include "FindAllMacros.h"
11 +#include "clang/Lex/PPCallbacks.h"
12 +#include "clang/Lex/Preprocessor.h"
13 +#include "llvm/ADT/STLExtras.h"
14 +
15 +namespace clang {
16 +namespace find_all_symbols {
17 +
18 +FindAllSymbolsAction::FindAllSymbolsAction(
19 + SymbolReporter *Reporter,
20 + const HeaderMapCollector::RegexHeaderMap *RegexHeaderMap)
21 + : Reporter(Reporter), Collector(RegexHeaderMap), Handler(&Collector),
22 + Matcher(Reporter, &Collector) {
23 + Matcher.registerMatchers(&MatchFinder);
24 +}
25 +
26 +std::unique_ptr<ASTConsumer>
27 +FindAllSymbolsAction::CreateASTConsumer(CompilerInstance &Compiler,
28 + StringRef InFile) {
29 + Compiler.getPreprocessor().addCommentHandler(&Handler);
30 + Compiler.getPreprocessor().addPPCallbacks(std::make_unique<FindAllMacros>(
31 + Reporter, &Compiler.getSourceManager(), &Collector));
32 + return MatchFinder.newASTConsumer();
33 +}
34 +
35 +} // namespace find_all_symbols
36 +} // namespace clang
1 +//===-- FindAllSymbolsAction.h - find all symbols action --------*- C++ -*-===//
2 +//
3 +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 +// See https://llvm.org/LICENSE.txt for license information.
5 +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 +//
7 +//===----------------------------------------------------------------------===//
8 +
9 +#ifndef LLVM_CLANG_TOOLS_EXTRA_FIND_ALL_SYMBOLS_FIND_ALL_SYMBOLS_ACTION_H
10 +#define LLVM_CLANG_TOOLS_EXTRA_FIND_ALL_SYMBOLS_FIND_ALL_SYMBOLS_ACTION_H
11 +
12 +#include "FindAllSymbols.h"
13 +#include "HeaderMapCollector.h"
14 +#include "PragmaCommentHandler.h"
15 +#include "clang/ASTMatchers/ASTMatchFinder.h"
16 +#include "clang/Frontend/CompilerInstance.h"
17 +#include "clang/Frontend/FrontendAction.h"
18 +#include "clang/Tooling/Tooling.h"
19 +#include "llvm/ADT/StringRef.h"
20 +#include <memory>
21 +
22 +namespace clang {
23 +namespace find_all_symbols {
24 +
25 +class FindAllSymbolsAction : public clang::ASTFrontendAction {
26 +public:
27 + explicit FindAllSymbolsAction(
28 + SymbolReporter *Reporter,
29 + const HeaderMapCollector::RegexHeaderMap *RegexHeaderMap = nullptr);
30 +
31 + std::unique_ptr<clang::ASTConsumer>
32 + CreateASTConsumer(clang::CompilerInstance &Compiler,
33 + StringRef InFile) override;
34 +
35 +private:
36 + SymbolReporter *const Reporter;
37 + clang::ast_matchers::MatchFinder MatchFinder;
38 + HeaderMapCollector Collector;
39 + PragmaCommentHandler Handler;
40 + FindAllSymbols Matcher;
41 +};
42 +
43 +class FindAllSymbolsActionFactory : public tooling::FrontendActionFactory {
44 +public:
45 + FindAllSymbolsActionFactory(
46 + SymbolReporter *Reporter,
47 + const HeaderMapCollector::RegexHeaderMap *RegexHeaderMap = nullptr)
48 + : Reporter(Reporter), RegexHeaderMap(RegexHeaderMap) {}
49 +
50 + std::unique_ptr<FrontendAction> create() override {
51 + return std::make_unique<FindAllSymbolsAction>(Reporter, RegexHeaderMap);
52 + }
53 +
54 +private:
55 + SymbolReporter *const Reporter;
56 + const HeaderMapCollector::RegexHeaderMap *const RegexHeaderMap;
57 +};
58 +
59 +} // namespace find_all_symbols
60 +} // namespace clang
61 +
62 +#endif // LLVM_CLANG_TOOLS_EXTRA_FIND_ALL_SYMBOLS_FIND_ALL_SYMBOLS_ACTION_H
1 +//===-- HeaderMapCoolector.h - find all symbols------------------*- C++ -*-===//
2 +//
3 +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 +// See https://llvm.org/LICENSE.txt for license information.
5 +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 +//
7 +//===----------------------------------------------------------------------===//
8 +
9 +#include "HeaderMapCollector.h"
10 +#include "llvm/Support/Regex.h"
11 +
12 +namespace clang {
13 +namespace find_all_symbols {
14 +
15 +HeaderMapCollector::HeaderMapCollector(
16 + const RegexHeaderMap *RegexHeaderMappingTable) {
17 + assert(RegexHeaderMappingTable);
18 + this->RegexHeaderMappingTable.reserve(RegexHeaderMappingTable->size());
19 + for (const auto &Entry : *RegexHeaderMappingTable) {
20 + this->RegexHeaderMappingTable.emplace_back(llvm::Regex(Entry.first),
21 + Entry.second);
22 + }
23 +}
24 +
25 +llvm::StringRef
26 +HeaderMapCollector::getMappedHeader(llvm::StringRef Header) const {
27 + auto Iter = HeaderMappingTable.find(Header);
28 + if (Iter != HeaderMappingTable.end())
29 + return Iter->second;
30 + // If there is no complete header name mapping for this header, check the
31 + // regex header mapping.
32 + for (auto &Entry : RegexHeaderMappingTable) {
33 +#ifndef NDEBUG
34 + std::string Dummy;
35 + assert(Entry.first.isValid(Dummy) && "Regex should never be invalid!");
36 +#endif
37 + if (Entry.first.match(Header))
38 + return Entry.second;
39 + }
40 + return Header;
41 +}
42 +
43 +} // namespace find_all_symbols
44 +} // namespace clang
1 +//===-- HeaderMapCoolector.h - find all symbols------------------*- C++ -*-===//
2 +//
3 +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 +// See https://llvm.org/LICENSE.txt for license information.
5 +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 +//
7 +//===----------------------------------------------------------------------===//
8 +
9 +#ifndef LLVM_CLANG_TOOLS_EXTRA_FIND_ALL_SYMBOLS_HEADER_MAP_COLLECTOR_H
10 +#define LLVM_CLANG_TOOLS_EXTRA_FIND_ALL_SYMBOLS_HEADER_MAP_COLLECTOR_H
11 +
12 +#include "llvm/ADT/StringMap.h"
13 +#include "llvm/Support/Regex.h"
14 +#include <string>
15 +#include <vector>
16 +
17 +namespace clang {
18 +namespace find_all_symbols {
19 +
20 +/// HeaderMappCollector collects all remapping header files. This maps
21 +/// complete header names or header name regex patterns to header names.
22 +class HeaderMapCollector {
23 +public:
24 + typedef llvm::StringMap<std::string> HeaderMap;
25 + typedef std::vector<std::pair<const char *, const char *>> RegexHeaderMap;
26 +
27 + HeaderMapCollector() = default;
28 + explicit HeaderMapCollector(const RegexHeaderMap *RegexHeaderMappingTable);
29 +
30 + void addHeaderMapping(llvm::StringRef OrignalHeaderPath,
31 + llvm::StringRef MappingHeaderPath) {
32 + HeaderMappingTable[OrignalHeaderPath] = std::string(MappingHeaderPath);
33 + };
34 +
35 + /// Check if there is a mapping from \p Header or a regex pattern that matches
36 + /// it to another header name.
37 + /// \param Header A header name.
38 + /// \return \p Header itself if there is no mapping for it; otherwise, return
39 + /// a mapped header name.
40 + llvm::StringRef getMappedHeader(llvm::StringRef Header) const;
41 +
42 +private:
43 + /// A string-to-string map saving the mapping relationship.
44 + HeaderMap HeaderMappingTable;
45 +
46 + // A map from header patterns to header names.
47 + // The header names are not owned. This is only threadsafe because the regexes
48 + // never fail.
49 + mutable std::vector<std::pair<llvm::Regex, const char *>>
50 + RegexHeaderMappingTable;
51 +};
52 +
53 +} // namespace find_all_symbols
54 +} // namespace clang
55 +
56 +#endif // LLVM_CLANG_TOOLS_EXTRA_FIND_ALL_SYMBOLS_HEADER_MAP_COLLECTOR_H
1 +//===-- PathConfig.cpp - Process paths of symbols ---------------*- C++ -*-===//
2 +//
3 +//
4 +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
5 +// See https://llvm.org/LICENSE.txt for license information.
6 +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 +//
8 +//===----------------------------------------------------------------------===//
9 +
10 +#include "PathConfig.h"
11 +#include "llvm/ADT/SmallString.h"
12 +#include "llvm/Support/Path.h"
13 +
14 +namespace clang {
15 +namespace find_all_symbols {
16 +
17 +std::string getIncludePath(const SourceManager &SM, SourceLocation Loc,
18 + const HeaderMapCollector *Collector) {
19 + llvm::StringRef FilePath;
20 + // Walk up the include stack to skip .inc files.
21 + while (true) {
22 + if (!Loc.isValid() || SM.isInMainFile(Loc))
23 + return "";
24 + FilePath = SM.getFilename(Loc);
25 + if (FilePath.empty())
26 + return "";
27 + if (!FilePath.endswith(".inc"))
28 + break;
29 + FileID ID = SM.getFileID(Loc);
30 + Loc = SM.getIncludeLoc(ID);
31 + }
32 +
33 + if (Collector)
34 + FilePath = Collector->getMappedHeader(FilePath);
35 + SmallString<256> CleanedFilePath = FilePath;
36 + llvm::sys::path::remove_dots(CleanedFilePath, /*remove_dot_dot=*/false);
37 +
38 + return std::string(CleanedFilePath.str());
39 +}
40 +
41 +} // namespace find_all_symbols
42 +} // namespace clang
1 +//===-- PathConfig.h - Process paths of symbols -----------------*- C++ -*-===//
2 +//
3 +//
4 +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
5 +// See https://llvm.org/LICENSE.txt for license information.
6 +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 +//
8 +//===----------------------------------------------------------------------===//
9 +
10 +#ifndef LLVM_CLANG_TOOLS_EXTRA_FIND_ALL_SYMBOLS_PATH_CONFIG_H
11 +#define LLVM_CLANG_TOOLS_EXTRA_FIND_ALL_SYMBOLS_PATH_CONFIG_H
12 +
13 +#include "HeaderMapCollector.h"
14 +#include "clang/Basic/SourceManager.h"
15 +#include <string>
16 +
17 +namespace clang {
18 +namespace find_all_symbols {
19 +
20 +/// This calculates the include path for \p Loc.
21 +///
22 +/// \param SM SourceManager.
23 +/// \param Loc A SourceLocation.
24 +/// \param Collector An optional header mapping collector.
25 +///
26 +/// \return The file path (or mapped file path if Collector is provided) of the
27 +/// header that includes \p Loc. If \p Loc comes from .inc header file, \p Loc
28 +/// is set to the location from which the .inc header file is included. If \p
29 +/// Loc is invalid or comes from a main file, this returns an empty string.
30 +std::string getIncludePath(const SourceManager &SM, SourceLocation Loc,
31 + const HeaderMapCollector *Collector = nullptr);
32 +
33 +} // namespace find_all_symbols
34 +} // namespace clang
35 +
36 +#endif // LLVM_CLANG_TOOLS_EXTRA_FIND_ALL_SYMBOLS_PATH_CONFIG_H
1 +//===-- PragmaCommentHandler.cpp - find all symbols -----------------------===//
2 +//
3 +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 +// See https://llvm.org/LICENSE.txt for license information.
5 +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 +//
7 +//===----------------------------------------------------------------------===//
8 +
9 +#include "PragmaCommentHandler.h"
10 +#include "FindAllSymbols.h"
11 +#include "HeaderMapCollector.h"
12 +#include "clang/Lex/Preprocessor.h"
13 +#include "llvm/Support/Regex.h"
14 +
15 +namespace clang {
16 +namespace find_all_symbols {
17 +namespace {
18 +const char IWYUPragma[] = "// IWYU pragma: private, include ";
19 +} // namespace
20 +
21 +bool PragmaCommentHandler::HandleComment(Preprocessor &PP, SourceRange Range) {
22 + StringRef Text =
23 + Lexer::getSourceText(CharSourceRange::getCharRange(Range),
24 + PP.getSourceManager(), PP.getLangOpts());
25 + size_t Pos = Text.find(IWYUPragma);
26 + if (Pos == StringRef::npos)
27 + return false;
28 + StringRef RemappingFilePath = Text.substr(Pos + std::strlen(IWYUPragma));
29 + Collector->addHeaderMapping(
30 + PP.getSourceManager().getFilename(Range.getBegin()),
31 + RemappingFilePath.trim("\"<>"));
32 + return false;
33 +}
34 +
35 +} // namespace find_all_symbols
36 +} // namespace clang
1 +//===-- PragmaCommentHandler.h - find all symbols----------------*- C++ -*-===//
2 +//
3 +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 +// See https://llvm.org/LICENSE.txt for license information.
5 +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 +//
7 +//===----------------------------------------------------------------------===//
8 +
9 +#ifndef LLVM_CLANG_TOOLS_EXTRA_FIND_ALL_SYMBOLS_PRAGMA_COMMENT_HANDLER_H
10 +#define LLVM_CLANG_TOOLS_EXTRA_FIND_ALL_SYMBOLS_PRAGMA_COMMENT_HANDLER_H
11 +
12 +#include "clang/Basic/SourceLocation.h"
13 +#include "clang/Lex/Preprocessor.h"
14 +#include <map>
15 +
16 +namespace clang {
17 +namespace find_all_symbols {
18 +
19 +class HeaderMapCollector;
20 +
21 +/// PragmaCommentHandler parses pragma comment on include files to
22 +/// determine when we should include a different header from the header that
23 +/// directly defines a symbol.
24 +///
25 +/// Currently it only supports IWYU private pragma:
26 +/// https://github.com/include-what-you-use/include-what-you-use/blob/master/docs/IWYUPragmas.md#iwyu-pragma-private
27 +class PragmaCommentHandler : public clang::CommentHandler {
28 +public:
29 + PragmaCommentHandler(HeaderMapCollector *Collector) : Collector(Collector) {}
30 +
31 + bool HandleComment(Preprocessor &PP, SourceRange Range) override;
32 +
33 +private:
34 + HeaderMapCollector *const Collector;
35 +};
36 +
37 +} // namespace find_all_symbols
38 +} // namespace clang
39 +
40 +#endif // LLVM_CLANG_TOOLS_EXTRA_FIND_ALL_SYMBOLS_PRAGMA_COMMENT_HANDLER_H
1 +//===-- STLPostfixHeaderMap.h - hardcoded header map for STL ----*- C++ -*-===//
2 +//
3 +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 +// See https://llvm.org/LICENSE.txt for license information.
5 +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 +//
7 +//===----------------------------------------------------------------------===//
8 +
9 +#ifndef LLVM_CLANG_TOOLS_EXTRA_FIND_ALL_SYMBOLS_TOOL_STL_POSTFIX_HEADER_MAP_H
10 +#define LLVM_CLANG_TOOLS_EXTRA_FIND_ALL_SYMBOLS_TOOL_STL_POSTFIX_HEADER_MAP_H
11 +
12 +#include "HeaderMapCollector.h"
13 +
14 +namespace clang {
15 +namespace find_all_symbols {
16 +
17 +const HeaderMapCollector::RegexHeaderMap *getSTLPostfixHeaderMap();
18 +
19 +} // namespace find_all_symbols
20 +} // namespace clang
21 +
22 +#endif // LLVM_CLANG_TOOLS_EXTRA_FIND_ALL_SYMBOLS_TOOL_STL_POSTFIX_HEADER_MAP_H
1 +//===-- SymbolInfo.cpp - Symbol Info ----------------------------*- C++ -*-===//
2 +//
3 +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 +// See https://llvm.org/LICENSE.txt for license information.
5 +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 +//
7 +//===----------------------------------------------------------------------===//
8 +
9 +#include "SymbolInfo.h"
10 +#include "llvm/Support/CommandLine.h"
11 +#include "llvm/Support/FileSystem.h"
12 +#include "llvm/Support/YAMLTraits.h"
13 +#include "llvm/Support/raw_ostream.h"
14 +
15 +using llvm::yaml::MappingTraits;
16 +using llvm::yaml::IO;
17 +using llvm::yaml::Input;
18 +using ContextType = clang::find_all_symbols::SymbolInfo::ContextType;
19 +using clang::find_all_symbols::SymbolInfo;
20 +using clang::find_all_symbols::SymbolAndSignals;
21 +using SymbolKind = clang::find_all_symbols::SymbolInfo::SymbolKind;
22 +
23 +LLVM_YAML_IS_DOCUMENT_LIST_VECTOR(SymbolAndSignals)
24 +LLVM_YAML_IS_SEQUENCE_VECTOR(SymbolInfo::Context)
25 +
26 +namespace llvm {
27 +namespace yaml {
28 +template <> struct MappingTraits<SymbolAndSignals> {
29 + static void mapping(IO &io, SymbolAndSignals &Symbol) {
30 + io.mapRequired("Name", Symbol.Symbol.Name);
31 + io.mapRequired("Contexts", Symbol.Symbol.Contexts);
32 + io.mapRequired("FilePath", Symbol.Symbol.FilePath);
33 + io.mapRequired("Type", Symbol.Symbol.Type);
34 + io.mapRequired("Seen", Symbol.Signals.Seen);
35 + io.mapRequired("Used", Symbol.Signals.Used);
36 + }
37 +};
38 +
39 +template <> struct ScalarEnumerationTraits<ContextType> {
40 + static void enumeration(IO &io, ContextType &value) {
41 + io.enumCase(value, "Record", ContextType::Record);
42 + io.enumCase(value, "Namespace", ContextType::Namespace);
43 + io.enumCase(value, "EnumDecl", ContextType::EnumDecl);
44 + }
45 +};
46 +
47 +template <> struct ScalarEnumerationTraits<SymbolKind> {
48 + static void enumeration(IO &io, SymbolKind &value) {
49 + io.enumCase(value, "Variable", SymbolKind::Variable);
50 + io.enumCase(value, "Function", SymbolKind::Function);
51 + io.enumCase(value, "Class", SymbolKind::Class);
52 + io.enumCase(value, "TypedefName", SymbolKind::TypedefName);
53 + io.enumCase(value, "EnumDecl", SymbolKind::EnumDecl);
54 + io.enumCase(value, "EnumConstantDecl", SymbolKind::EnumConstantDecl);
55 + io.enumCase(value, "Macro", SymbolKind::Macro);
56 + io.enumCase(value, "Unknown", SymbolKind::Unknown);
57 + }
58 +};
59 +
60 +template <> struct MappingTraits<SymbolInfo::Context> {
61 + static void mapping(IO &io, SymbolInfo::Context &Context) {
62 + io.mapRequired("ContextType", Context.first);
63 + io.mapRequired("ContextName", Context.second);
64 + }
65 +};
66 +
67 +} // namespace yaml
68 +} // namespace llvm
69 +
70 +namespace clang {
71 +namespace find_all_symbols {
72 +
73 +SymbolInfo::SymbolInfo(llvm::StringRef Name, SymbolKind Type,
74 + llvm::StringRef FilePath,
75 + const std::vector<Context> &Contexts)
76 + : Name(Name), Type(Type), FilePath(FilePath), Contexts(Contexts) {}
77 +
78 +bool SymbolInfo::operator==(const SymbolInfo &Symbol) const {
79 + return std::tie(Name, Type, FilePath, Contexts) ==
80 + std::tie(Symbol.Name, Symbol.Type, Symbol.FilePath, Symbol.Contexts);
81 +}
82 +
83 +bool SymbolInfo::operator<(const SymbolInfo &Symbol) const {
84 + return std::tie(Name, Type, FilePath, Contexts) <
85 + std::tie(Symbol.Name, Symbol.Type, Symbol.FilePath, Symbol.Contexts);
86 +}
87 +
88 +std::string SymbolInfo::getQualifiedName() const {
89 + std::string QualifiedName = Name;
90 + for (const auto &Context : Contexts) {
91 + if (Context.first == ContextType::EnumDecl)
92 + continue;
93 + QualifiedName = Context.second + "::" + QualifiedName;
94 + }
95 + return QualifiedName;
96 +}
97 +
98 +SymbolInfo::Signals &SymbolInfo::Signals::operator+=(const Signals &RHS) {
99 + Seen += RHS.Seen;
100 + Used += RHS.Used;
101 + return *this;
102 +}
103 +
104 +SymbolInfo::Signals SymbolInfo::Signals::operator+(const Signals &RHS) const {
105 + Signals Result = *this;
106 + Result += RHS;
107 + return Result;
108 +}
109 +
110 +bool SymbolInfo::Signals::operator==(const Signals &RHS) const {
111 + return std::tie(Seen, Used) == std::tie(RHS.Seen, RHS.Used);
112 +}
113 +
114 +bool SymbolAndSignals::operator==(const SymbolAndSignals& RHS) const {
115 + return std::tie(Symbol, Signals) == std::tie(RHS.Symbol, RHS.Signals);
116 +}
117 +
118 +bool WriteSymbolInfosToStream(llvm::raw_ostream &OS,
119 + const SymbolInfo::SignalMap &Symbols) {
120 + llvm::yaml::Output yout(OS);
121 + for (const auto &Symbol : Symbols) {
122 + SymbolAndSignals S{Symbol.first, Symbol.second};
123 + yout << S;
124 + }
125 + return true;
126 +}
127 +
128 +std::vector<SymbolAndSignals> ReadSymbolInfosFromYAML(llvm::StringRef Yaml) {
129 + std::vector<SymbolAndSignals> Symbols;
130 + llvm::yaml::Input yin(Yaml);
131 + yin >> Symbols;
132 + return Symbols;
133 +}
134 +
135 +} // namespace find_all_symbols
136 +} // namespace clang
1 +//===-- SymbolInfo.h - Symbol Info ------------------------------*- C++ -*-===//
2 +//
3 +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 +// See https://llvm.org/LICENSE.txt for license information.
5 +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 +//
7 +//===----------------------------------------------------------------------===//
8 +
9 +#ifndef LLVM_CLANG_TOOLS_EXTRA_INCLUDE_FIXER_FIND_ALL_SYMBOLS_SYMBOLINFO_H
10 +#define LLVM_CLANG_TOOLS_EXTRA_INCLUDE_FIXER_FIND_ALL_SYMBOLS_SYMBOLINFO_H
11 +
12 +#include "llvm/ADT/Optional.h"
13 +#include "llvm/ADT/StringRef.h"
14 +#include "llvm/Support/YAMLTraits.h"
15 +#include "llvm/Support/raw_ostream.h"
16 +#include <set>
17 +#include <string>
18 +#include <vector>
19 +
20 +namespace clang {
21 +namespace find_all_symbols {
22 +/// Describes a named symbol from a header.
23 +/// Symbols with the same qualified name and type (e.g. function overloads)
24 +/// that appear in the same header are represented by a single SymbolInfo.
25 +///
26 +/// TODO: keep track of instances, e.g. overload locations and signatures.
27 +class SymbolInfo {
28 +public:
29 + /// The SymbolInfo Type.
30 + enum class SymbolKind {
31 + Function,
32 + Class,
33 + Variable,
34 + TypedefName,
35 + EnumDecl,
36 + EnumConstantDecl,
37 + Macro,
38 + Unknown,
39 + };
40 +
41 + /// The Context Type.
42 + enum class ContextType {
43 + Namespace, // Symbols declared in a namespace.
44 + Record, // Symbols declared in a class.
45 + EnumDecl, // Enum constants declared in a enum declaration.
46 + };
47 +
48 + /// A pair of <ContextType, ContextName>.
49 + typedef std::pair<ContextType, std::string> Context;
50 +
51 + // Signals are signals gathered by observing how a symbol is used.
52 + // These are used to rank results.
53 + struct Signals {
54 + Signals() {}
55 + Signals(unsigned Seen, unsigned Used) : Seen(Seen), Used(Used) {}
56 +
57 + // Number of times this symbol was visible to a TU.
58 + unsigned Seen = 0;
59 +
60 + // Number of times this symbol was referenced a TU's main file.
61 + unsigned Used = 0;
62 +
63 + Signals &operator+=(const Signals &RHS);
64 + Signals operator+(const Signals &RHS) const;
65 + bool operator==(const Signals &RHS) const;
66 + };
67 +
68 + using SignalMap = std::map<SymbolInfo, Signals>;
69 +
70 + // The default constructor is required by YAML traits in
71 + // LLVM_YAML_IS_DOCUMENT_LIST_VECTOR.
72 + SymbolInfo() : Type(SymbolKind::Unknown) {}
73 +
74 + SymbolInfo(llvm::StringRef Name, SymbolKind Type, llvm::StringRef FilePath,
75 + const std::vector<Context> &Contexts);
76 +
77 + void SetFilePath(llvm::StringRef Path) { FilePath = std::string(Path); }
78 +
79 + /// Get symbol name.
80 + llvm::StringRef getName() const { return Name; }
81 +
82 + /// Get the fully-qualified symbol name.
83 + std::string getQualifiedName() const;
84 +
85 + /// Get symbol type.
86 + SymbolKind getSymbolKind() const { return Type; }
87 +
88 + /// Get a relative file path where symbol comes from.
89 + llvm::StringRef getFilePath() const { return FilePath; }
90 +
91 + /// Get symbol contexts.
92 + const std::vector<SymbolInfo::Context> &getContexts() const {
93 + return Contexts;
94 + }
95 +
96 + bool operator<(const SymbolInfo &Symbol) const;
97 +
98 + bool operator==(const SymbolInfo &Symbol) const;
99 +
100 +private:
101 + friend struct llvm::yaml::MappingTraits<struct SymbolAndSignals>;
102 +
103 + /// Identifier name.
104 + std::string Name;
105 +
106 + /// Symbol type.
107 + SymbolKind Type;
108 +
109 + /// The file path where the symbol comes from. It's a relative file
110 + /// path based on the build directory.
111 + std::string FilePath;
112 +
113 + /// Contains information about symbol contexts. Context information is
114 + /// stored from the inner-most level to outer-most level.
115 + ///
116 + /// For example, if a symbol 'x' is declared as:
117 + /// namespace na { namespace nb { class A { int x; } } }
118 + /// The contexts would be { {RECORD, "A"}, {NAMESPACE, "nb"}, {NAMESPACE,
119 + /// "na"} }.
120 + /// The name of an anonymous namespace is "".
121 + ///
122 + /// If the symbol is declared in `TranslationUnitDecl`, it has no context.
123 + std::vector<Context> Contexts;
124 +};
125 +
126 +struct SymbolAndSignals {
127 + SymbolInfo Symbol;
128 + SymbolInfo::Signals Signals;
129 + bool operator==(const SymbolAndSignals& RHS) const;
130 +};
131 +
132 +/// Write SymbolInfos to a stream (YAML format).
133 +bool WriteSymbolInfosToStream(llvm::raw_ostream &OS,
134 + const SymbolInfo::SignalMap &Symbols);
135 +
136 +/// Read SymbolInfos from a YAML document.
137 +std::vector<SymbolAndSignals> ReadSymbolInfosFromYAML(llvm::StringRef Yaml);
138 +
139 +} // namespace find_all_symbols
140 +} // namespace clang
141 +
142 +#endif // LLVM_CLANG_TOOLS_EXTRA_INCLUDE_FIXER_FIND_ALL_SYMBOLS_SYMBOLINFO_H
1 +//===--- SymbolReporter.h - Symbol Reporter ---------------------*- C++ -*-===//
2 +//
3 +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 +// See https://llvm.org/LICENSE.txt for license information.
5 +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 +//
7 +//===----------------------------------------------------------------------===//
8 +
9 +#ifndef LLVM_CLANG_TOOLS_EXTRA_FIND_ALL_SYMBOLS_SYMBOL_REPORTER_H
10 +#define LLVM_CLANG_TOOLS_EXTRA_FIND_ALL_SYMBOLS_SYMBOL_REPORTER_H
11 +
12 +#include "SymbolInfo.h"
13 +
14 +namespace clang {
15 +namespace find_all_symbols {
16 +
17 +/// An interface for classes that collect symbols.
18 +class SymbolReporter {
19 +public:
20 + virtual ~SymbolReporter() = default;
21 +
22 + virtual void reportSymbols(llvm::StringRef FileName,
23 + const SymbolInfo::SignalMap &Symbols) = 0;
24 +};
25 +
26 +} // namespace find_all_symbols
27 +} // namespace clang
28 +
29 +#endif // LLVM_CLANG_TOOLS_EXTRA_FIND_ALL_SYMBOLS_SYMBOL_REPORTER_H
1 +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/..)
2 +
3 +add_clang_tool(find-all-symbols
4 + FindAllSymbolsMain.cpp
5 + )
6 +
7 +clang_target_link_libraries(find-all-symbols
8 + PRIVATE
9 + clangAST
10 + clangASTMatchers
11 + clangBasic
12 + clangFrontend
13 + clangLex
14 + clangSerialization
15 + clangTooling
16 + )
17 +target_link_libraries(find-all-symbols
18 + PRIVATE
19 + findAllSymbols
20 + )
21 +
22 +install(PROGRAMS run-find-all-symbols.py
23 + DESTINATION share/clang
24 + COMPONENT find-all-symbols)
1 +//===-- FindAllSymbolsMain.cpp - find all symbols tool ----------*- C++ -*-===//
2 +//
3 +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 +// See https://llvm.org/LICENSE.txt for license information.
5 +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 +//
7 +//===----------------------------------------------------------------------===//
8 +
9 +#include "FindAllSymbolsAction.h"
10 +#include "STLPostfixHeaderMap.h"
11 +#include "SymbolInfo.h"
12 +#include "SymbolReporter.h"
13 +#include "clang/ASTMatchers/ASTMatchFinder.h"
14 +#include "clang/ASTMatchers/ASTMatchers.h"
15 +#include "clang/Frontend/CompilerInstance.h"
16 +#include "clang/Frontend/FrontendActions.h"
17 +#include "clang/Lex/Preprocessor.h"
18 +#include "clang/Tooling/CommonOptionsParser.h"
19 +#include "clang/Tooling/Tooling.h"
20 +#include "llvm/ADT/ArrayRef.h"
21 +#include "llvm/ADT/SmallString.h"
22 +#include "llvm/ADT/StringRef.h"
23 +#include "llvm/Support/CommandLine.h"
24 +#include "llvm/Support/FileSystem.h"
25 +#include "llvm/Support/MemoryBuffer.h"
26 +#include "llvm/Support/Path.h"
27 +#include "llvm/Support/ThreadPool.h"
28 +#include "llvm/Support/raw_ostream.h"
29 +#include <map>
30 +#include <mutex>
31 +#include <set>
32 +#include <string>
33 +#include <system_error>
34 +#include <vector>
35 +
36 +using namespace clang::tooling;
37 +using namespace llvm;
38 +using SymbolInfo = clang::find_all_symbols::SymbolInfo;
39 +
40 +// Apply a custom category to all command-line options so that they are the
41 +// only ones displayed.
42 +static cl::OptionCategory FindAllSymbolsCategory("find_all_symbols options");
43 +
44 +// CommonOptionsParser declares HelpMessage with a description of the common
45 +// command-line options related to the compilation database and input files.
46 +// It's nice to have this help message in all tools.
47 +static cl::extrahelp CommonHelp(CommonOptionsParser::HelpMessage);
48 +
49 +// A help message for this specific tool can be added afterwards.
50 +static cl::extrahelp MoreHelp("\nMore help text...");
51 +
52 +static cl::opt<std::string> OutputDir("output-dir", cl::desc(R"(
53 +The output directory for saving the results.)"),
54 + cl::init("."),
55 + cl::cat(FindAllSymbolsCategory));
56 +
57 +static cl::opt<std::string> MergeDir("merge-dir", cl::desc(R"(
58 +The directory for merging symbols.)"),
59 + cl::init(""),
60 + cl::cat(FindAllSymbolsCategory));
61 +namespace clang {
62 +namespace find_all_symbols {
63 +
64 +class YamlReporter : public SymbolReporter {
65 +public:
66 + void reportSymbols(StringRef FileName,
67 + const SymbolInfo::SignalMap &Symbols) override {
68 + int FD;
69 + SmallString<128> ResultPath;
70 + llvm::sys::fs::createUniqueFile(
71 + OutputDir + "/" + llvm::sys::path::filename(FileName) + "-%%%%%%.yaml",
72 + FD, ResultPath);
73 + llvm::raw_fd_ostream OS(FD, /*shouldClose=*/true);
74 + WriteSymbolInfosToStream(OS, Symbols);
75 + }
76 +};
77 +
78 +bool Merge(llvm::StringRef MergeDir, llvm::StringRef OutputFile) {
79 + std::error_code EC;
80 + SymbolInfo::SignalMap Symbols;
81 + std::mutex SymbolMutex;
82 + auto AddSymbols = [&](ArrayRef<SymbolAndSignals> NewSymbols) {
83 + // Synchronize set accesses.
84 + std::unique_lock<std::mutex> LockGuard(SymbolMutex);
85 + for (const auto &Symbol : NewSymbols) {
86 + Symbols[Symbol.Symbol] += Symbol.Signals;
87 + }
88 + };
89 +
90 + // Load all symbol files in MergeDir.
91 + {
92 + llvm::ThreadPool Pool;
93 + for (llvm::sys::fs::directory_iterator Dir(MergeDir, EC), DirEnd;
94 + Dir != DirEnd && !EC; Dir.increment(EC)) {
95 + // Parse YAML files in parallel.
96 + Pool.async(
97 + [&AddSymbols](std::string Path) {
98 + auto Buffer = llvm::MemoryBuffer::getFile(Path);
99 + if (!Buffer) {
100 + llvm::errs() << "Can't open " << Path << "\n";
101 + return;
102 + }
103 + std::vector<SymbolAndSignals> Symbols =
104 + ReadSymbolInfosFromYAML(Buffer.get()->getBuffer());
105 + for (auto &Symbol : Symbols) {
106 + // Only count one occurrence per file, to avoid spam.
107 + Symbol.Signals.Seen = std::min(Symbol.Signals.Seen, 1u);
108 + Symbol.Signals.Used = std::min(Symbol.Signals.Used, 1u);
109 + }
110 + // FIXME: Merge without creating such a heavy contention point.
111 + AddSymbols(Symbols);
112 + },
113 + Dir->path());
114 + }
115 + }
116 +
117 + llvm::raw_fd_ostream OS(OutputFile, EC, llvm::sys::fs::OF_None);
118 + if (EC) {
119 + llvm::errs() << "Can't open '" << OutputFile << "': " << EC.message()
120 + << '\n';
121 + return false;
122 + }
123 + WriteSymbolInfosToStream(OS, Symbols);
124 + return true;
125 +}
126 +
127 +} // namespace clang
128 +} // namespace find_all_symbols
129 +
130 +int main(int argc, const char **argv) {
131 + CommonOptionsParser OptionsParser(argc, argv, FindAllSymbolsCategory);
132 + ClangTool Tool(OptionsParser.getCompilations(),
133 + OptionsParser.getSourcePathList());
134 +
135 + std::vector<std::string> sources = OptionsParser.getSourcePathList();
136 + if (sources.empty()) {
137 + llvm::errs() << "Must specify at least one one source file.\n";
138 + return 1;
139 + }
140 + if (!MergeDir.empty()) {
141 + clang::find_all_symbols::Merge(MergeDir, sources[0]);
142 + return 0;
143 + }
144 +
145 + clang::find_all_symbols::YamlReporter Reporter;
146 +
147 + auto Factory =
148 + std::make_unique<clang::find_all_symbols::FindAllSymbolsActionFactory>(
149 + &Reporter, clang::find_all_symbols::getSTLPostfixHeaderMap());
150 + return Tool.run(Factory.get());
151 +}
1 +#!/usr/bin/env python
2 +#
3 +#=- run-find-all-symbols.py - Parallel find-all-symbols runner -*- python -*-=#
4 +#
5 +# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
6 +# See https://llvm.org/LICENSE.txt for license information.
7 +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
8 +#
9 +#===------------------------------------------------------------------------===#
10 +
11 +"""
12 +Parallel find-all-symbols runner
13 +================================
14 +
15 +Runs find-all-symbols over all files in a compilation database.
16 +
17 +Example invocations.
18 +- Run find-all-symbols on all files in the current working directory.
19 + run-find-all-symbols.py <source-file>
20 +
21 +Compilation database setup:
22 +http://clang.llvm.org/docs/HowToSetupToolingForLLVM.html
23 +"""
24 +
25 +import argparse
26 +import json
27 +import multiprocessing
28 +import os
29 +import Queue
30 +import shutil
31 +import subprocess
32 +import sys
33 +import tempfile
34 +import threading
35 +
36 +
37 +def find_compilation_database(path):
38 + """Adjusts the directory until a compilation database is found."""
39 + result = './'
40 + while not os.path.isfile(os.path.join(result, path)):
41 + if os.path.realpath(result) == '/':
42 + print 'Error: could not find compilation database.'
43 + sys.exit(1)
44 + result += '../'
45 + return os.path.realpath(result)
46 +
47 +
48 +def MergeSymbols(directory, args):
49 + """Merge all symbol files (yaml) in a given directory into a single file."""
50 + invocation = [args.binary, '-merge-dir='+directory, args.saving_path]
51 + subprocess.call(invocation)
52 + print 'Merge is finished. Saving results in ' + args.saving_path
53 +
54 +
55 +def run_find_all_symbols(args, tmpdir, build_path, queue):
56 + """Takes filenames out of queue and runs find-all-symbols on them."""
57 + while True:
58 + name = queue.get()
59 + invocation = [args.binary, name, '-output-dir='+tmpdir, '-p='+build_path]
60 + sys.stdout.write(' '.join(invocation) + '\n')
61 + subprocess.call(invocation)
62 + queue.task_done()
63 +
64 +
65 +def main():
66 + parser = argparse.ArgumentParser(description='Runs find-all-symbols over all'
67 + 'files in a compilation database.')
68 + parser.add_argument('-binary', metavar='PATH',
69 + default='./bin/find-all-symbols',
70 + help='path to find-all-symbols binary')
71 + parser.add_argument('-j', type=int, default=0,
72 + help='number of instances to be run in parallel.')
73 + parser.add_argument('-p', dest='build_path',
74 + help='path used to read a compilation database.')
75 + parser.add_argument('-saving-path', default='./find_all_symbols_db.yaml',
76 + help='result saving path')
77 + args = parser.parse_args()
78 +
79 + db_path = 'compile_commands.json'
80 +
81 + if args.build_path is not None:
82 + build_path = args.build_path
83 + else:
84 + build_path = find_compilation_database(db_path)
85 +
86 + tmpdir = tempfile.mkdtemp()
87 +
88 + # Load the database and extract all files.
89 + database = json.load(open(os.path.join(build_path, db_path)))
90 + files = [entry['file'] for entry in database]
91 +
92 + # Filter out .rc files on Windows. CMake includes them for some reason.
93 + files = [f for f in files if not f.endswith('.rc')]
94 +
95 + max_task = args.j
96 + if max_task == 0:
97 + max_task = multiprocessing.cpu_count()
98 +
99 + try:
100 + # Spin up a bunch of tidy-launching threads.
101 + queue = Queue.Queue(max_task)
102 + for _ in range(max_task):
103 + t = threading.Thread(target=run_find_all_symbols,
104 + args=(args, tmpdir, build_path, queue))
105 + t.daemon = True
106 + t.start()
107 +
108 + # Fill the queue with files.
109 + for name in files:
110 + queue.put(name)
111 +
112 + # Wait for all threads to be done.
113 + queue.join()
114 +
115 + MergeSymbols(tmpdir, args)
116 +
117 +
118 + except KeyboardInterrupt:
119 + # This is a sad hack. Unfortunately subprocess goes
120 + # bonkers with ctrl-c and we start forking merrily.
121 + print '\nCtrl-C detected, goodbye.'
122 + os.kill(0, 9)
123 +
124 +
125 +if __name__ == '__main__':
126 + main()
1 +add_clang_library(clangIncludeFixerPlugin
2 + IncludeFixerPlugin.cpp
3 +
4 + LINK_LIBS
5 + clangAST
6 + clangBasic
7 + clangFrontend
8 + clangIncludeFixer
9 + clangParse
10 + clangSema
11 + clangTooling
12 + ${LLVM_PTHREAD_LIB}
13 +
14 + DEPENDS
15 + omp_gen
16 + )
1 +//===- IncludeFixerPlugin.cpp - clang-include-fixer as a clang plugin -----===//
2 +//
3 +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 +// See https://llvm.org/LICENSE.txt for license information.
5 +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 +//
7 +//===----------------------------------------------------------------------===//
8 +
9 +#include "../IncludeFixer.h"
10 +#include "../YamlSymbolIndex.h"
11 +#include "clang/Frontend/CompilerInstance.h"
12 +#include "clang/Frontend/FrontendPluginRegistry.h"
13 +#include "clang/Parse/ParseAST.h"
14 +#include "clang/Sema/Sema.h"
15 +#include "llvm/Support/Path.h"
16 +
17 +namespace clang {
18 +namespace include_fixer {
19 +
20 +/// The core include fixer plugin action. This just provides the AST consumer
21 +/// and command line flag parsing for using include fixer as a clang plugin.
22 +class ClangIncludeFixerPluginAction : public PluginASTAction {
23 + /// ASTConsumer to keep the symbol index alive. We don't really need an
24 + /// ASTConsumer for this plugin (everything is funneled on the side through
25 + /// Sema) but we have to keep the symbol index alive until sema is done.
26 + struct ASTConsumerManagerWrapper : public ASTConsumer {
27 + ASTConsumerManagerWrapper(std::shared_ptr<SymbolIndexManager> SIM)
28 + : SymbolIndexMgr(std::move(SIM)) {}
29 + std::shared_ptr<SymbolIndexManager> SymbolIndexMgr;
30 + };
31 +
32 +public:
33 + explicit ClangIncludeFixerPluginAction()
34 + : SymbolIndexMgr(std::make_shared<SymbolIndexManager>()),
35 + SemaSource(new IncludeFixerSemaSource(*SymbolIndexMgr,
36 + /*MinimizeIncludePaths=*/true,
37 + /*GenerateDiagnostics=*/true)) {}
38 +
39 + std::unique_ptr<clang::ASTConsumer>
40 + CreateASTConsumer(clang::CompilerInstance &CI, StringRef InFile) override {
41 + CI.setExternalSemaSource(SemaSource);
42 + SemaSource->setFilePath(InFile);
43 + SemaSource->setCompilerInstance(&CI);
44 + return std::make_unique<ASTConsumerManagerWrapper>(SymbolIndexMgr);
45 + }
46 +
47 + void ExecuteAction() override {} // Do nothing.
48 +
49 + bool ParseArgs(const CompilerInstance &CI,
50 + const std::vector<std::string> &Args) override {
51 + StringRef DB = "yaml";
52 + StringRef Input;
53 +
54 + // Parse the extra command line args.
55 + // FIXME: This is very limited at the moment.
56 + for (StringRef Arg : Args) {
57 + if (Arg.startswith("-db="))
58 + DB = Arg.substr(strlen("-db="));
59 + else if (Arg.startswith("-input="))
60 + Input = Arg.substr(strlen("-input="));
61 + }
62 +
63 + std::string InputFile =
64 + std::string(CI.getFrontendOpts().Inputs[0].getFile());
65 + auto CreateYamlIdx = [=]() -> std::unique_ptr<include_fixer::SymbolIndex> {
66 + llvm::ErrorOr<std::unique_ptr<include_fixer::YamlSymbolIndex>> SymbolIdx(
67 + nullptr);
68 + if (DB == "yaml") {
69 + if (!Input.empty()) {
70 + SymbolIdx = include_fixer::YamlSymbolIndex::createFromFile(Input);
71 + } else {
72 + // If we don't have any input file, look in the directory of the first
73 + // file and its parents.
74 + SmallString<128> AbsolutePath(tooling::getAbsolutePath(InputFile));
75 + StringRef Directory = llvm::sys::path::parent_path(AbsolutePath);
76 + SymbolIdx = include_fixer::YamlSymbolIndex::createFromDirectory(
77 + Directory, "find_all_symbols_db.yaml");
78 + }
79 + }
80 + return std::move(*SymbolIdx);
81 + };
82 +
83 + SymbolIndexMgr->addSymbolIndex(std::move(CreateYamlIdx));
84 + return true;
85 + }
86 +
87 +private:
88 + std::shared_ptr<SymbolIndexManager> SymbolIndexMgr;
89 + IntrusiveRefCntPtr<IncludeFixerSemaSource> SemaSource;
90 +};
91 +} // namespace include_fixer
92 +} // namespace clang
93 +
94 +// This anchor is used to force the linker to link in the generated object file
95 +// and thus register the include fixer plugin.
96 +volatile int ClangIncludeFixerPluginAnchorSource = 0;
97 +
98 +static clang::FrontendPluginRegistry::Add<
99 + clang::include_fixer::ClangIncludeFixerPluginAction>
100 + X("clang-include-fixer", "clang-include-fixer");
1 +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/..)
2 +
3 +add_clang_tool(clang-include-fixer
4 + ClangIncludeFixer.cpp
5 + )
6 +
7 +clang_target_link_libraries(clang-include-fixer
8 + PRIVATE
9 + clangBasic
10 + clangFormat
11 + clangFrontend
12 + clangRewrite
13 + clangSerialization
14 + clangTooling
15 + clangToolingCore
16 + )
17 +target_link_libraries(clang-include-fixer
18 + PRIVATE
19 + clangIncludeFixer
20 + findAllSymbols
21 + )
22 +
23 +install(PROGRAMS clang-include-fixer.el
24 + DESTINATION share/clang
25 + COMPONENT clang-include-fixer)
26 +install(PROGRAMS clang-include-fixer.py
27 + DESTINATION share/clang
28 + COMPONENT clang-include-fixer)
1 +;;; clang-include-fixer-test.el --- unit tests for clang-include-fixer.el -*- lexical-binding: t; -*-
2 +
3 +;;; Commentary:
4 +
5 +;; Unit tests for clang-include-fixer.el.
6 +
7 +;;; Code:
8 +
9 +(require 'clang-include-fixer)
10 +
11 +(require 'cc-mode)
12 +(require 'ert)
13 +
14 +(ert-deftest clang-include-fixer--insert-line ()
15 + "Unit test for `clang-include-fixer--insert-line'."
16 + (with-temp-buffer
17 + (insert "aa\nab\nac\nad\n")
18 + (let ((from (current-buffer)))
19 + (with-temp-buffer
20 + (insert "aa\nac\nad\n")
21 + (let ((to (current-buffer)))
22 + (should (clang-include-fixer--insert-line from to))
23 + (should (equal (buffer-string) "aa\nab\nac\nad\n")))))
24 + (should (equal (buffer-string) "aa\nab\nac\nad\n"))))
25 +
26 +(ert-deftest clang-include-fixer--insert-line-diff-on-empty-line ()
27 + "Unit test for `clang-include-fixer--insert-line'."
28 + (with-temp-buffer
29 + (insert "aa\nab\n\nac\nad\n")
30 + (let ((from (current-buffer)))
31 + (with-temp-buffer
32 + (insert "aa\n\nac\nad\n")
33 + (let ((to (current-buffer)))
34 + (should (clang-include-fixer--insert-line from to))
35 + (should (equal (buffer-string) "aa\nab\n\nac\nad\n")))))
36 + (should (equal (buffer-string) "aa\nab\n\nac\nad\n"))))
37 +
38 +(ert-deftest clang-include-fixer--symbol-at-point ()
39 + "Unit test for `clang-include-fixer--symbol-at-point'."
40 + (with-temp-buffer
41 + (insert "a+bbb::cc")
42 + (c++-mode)
43 + (goto-char (point-min))
44 + (should (equal (clang-include-fixer--symbol-at-point) "a"))
45 + (forward-char)
46 + ;; Emacs treats the character immediately following a symbol as part of the
47 + ;; symbol.
48 + (should (equal (clang-include-fixer--symbol-at-point) "a"))
49 + (forward-char)
50 + (should (equal (clang-include-fixer--symbol-at-point) "bbb::cc"))
51 + (goto-char (point-max))
52 + (should (equal (clang-include-fixer--symbol-at-point) "bbb::cc"))))
53 +
54 +(ert-deftest clang-include-fixer--highlight ()
55 + (with-temp-buffer
56 + (insert "util::Status foo;\n")
57 + (setq buffer-file-coding-system 'utf-8-unix)
58 + (should (equal nil (clang-include-fixer--highlight
59 + '((Range . ((Offset . 0) (Length . 0)))))))
60 + (let ((overlay (clang-include-fixer--highlight
61 + '((Range . ((Offset . 1) (Length . 12)))))))
62 + (should (equal 2 (overlay-start overlay)))
63 + (should (equal 14 (overlay-end overlay))))))
64 +
65 +;;; clang-include-fixer-test.el ends here
1 +# This file is a minimal clang-include-fixer vim-integration. To install:
2 +# - Change 'binary' if clang-include-fixer is not on the path (see below).
3 +# - Add to your .vimrc:
4 +#
5 +# noremap <leader>cf :pyf path/to/llvm/source/tools/clang/tools/extra/clang-include-fixer/tool/clang-include-fixer.py<cr>
6 +#
7 +# This enables clang-include-fixer for NORMAL and VISUAL mode. Change
8 +# "<leader>cf" to another binding if you need clang-include-fixer on a
9 +# different key.
10 +#
11 +# To set up clang-include-fixer, see
12 +# http://clang.llvm.org/extra/clang-include-fixer.html
13 +#
14 +# With this integration you can press the bound key and clang-include-fixer will
15 +# be run on the current buffer.
16 +#
17 +# It operates on the current, potentially unsaved buffer and does not create
18 +# or save any files. To revert a fix, just undo.
19 +
20 +from __future__ import print_function
21 +import argparse
22 +import difflib
23 +import json
24 +import re
25 +import subprocess
26 +import vim
27 +
28 +# set g:clang_include_fixer_path to the path to clang-include-fixer if it is not
29 +# on the path.
30 +# Change this to the full path if clang-include-fixer is not on the path.
31 +binary = 'clang-include-fixer'
32 +if vim.eval('exists("g:clang_include_fixer_path")') == "1":
33 + binary = vim.eval('g:clang_include_fixer_path')
34 +
35 +maximum_suggested_headers = 3
36 +if vim.eval('exists("g:clang_include_fixer_maximum_suggested_headers")') == "1":
37 + maximum_suggested_headers = max(
38 + 1,
39 + vim.eval('g:clang_include_fixer_maximum_suggested_headers'))
40 +
41 +increment_num = 5
42 +if vim.eval('exists("g:clang_include_fixer_increment_num")') == "1":
43 + increment_num = max(
44 + 1,
45 + vim.eval('g:clang_include_fixer_increment_num'))
46 +
47 +jump_to_include = False
48 +if vim.eval('exists("g:clang_include_fixer_jump_to_include")') == "1":
49 + jump_to_include = vim.eval('g:clang_include_fixer_jump_to_include') != "0"
50 +
51 +query_mode = False
52 +if vim.eval('exists("g:clang_include_fixer_query_mode")') == "1":
53 + query_mode = vim.eval('g:clang_include_fixer_query_mode') != "0"
54 +
55 +
56 +def GetUserSelection(message, headers, maximum_suggested_headers):
57 + eval_message = message + '\n'
58 + for idx, header in enumerate(headers[0:maximum_suggested_headers]):
59 + eval_message += "({0}). {1}\n".format(idx + 1, header)
60 + eval_message += "Enter (q) to quit;"
61 + if maximum_suggested_headers < len(headers):
62 + eval_message += " (m) to show {0} more candidates.".format(
63 + min(increment_num, len(headers) - maximum_suggested_headers))
64 +
65 + eval_message += "\nSelect (default 1): "
66 + res = vim.eval("input('{0}')".format(eval_message))
67 + if res == '':
68 + # choose the top ranked header by default
69 + idx = 1
70 + elif res == 'q':
71 + raise Exception(' Insertion cancelled...')
72 + elif res == 'm':
73 + return GetUserSelection(message,
74 + headers, maximum_suggested_headers + increment_num)
75 + else:
76 + try:
77 + idx = int(res)
78 + if idx <= 0 or idx > len(headers):
79 + raise Exception()
80 + except Exception:
81 + # Show a new prompt on invalid option instead of aborting so that users
82 + # don't need to wait for another clang-include-fixer run.
83 + print("Invalid option: {}".format(res), file=sys.stderr)
84 + return GetUserSelection(message, headers, maximum_suggested_headers)
85 + return headers[idx - 1]
86 +
87 +
88 +def execute(command, text):
89 + # Avoid flashing a cmd prompt on Windows.
90 + startupinfo = None
91 + if sys.platform.startswith('win32'):
92 + startupinfo = subprocess.STARTUPINFO()
93 + startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
94 + startupinfo.wShowWindow = subprocess.SW_HIDE
95 +
96 + p = subprocess.Popen(command,
97 + stdout=subprocess.PIPE, stderr=subprocess.PIPE,
98 + stdin=subprocess.PIPE, startupinfo=startupinfo)
99 + return p.communicate(input=text.encode('utf-8'))
100 +
101 +
102 +def InsertHeaderToVimBuffer(header, text):
103 + command = [binary, "-stdin", "-insert-header=" + json.dumps(header),
104 + vim.current.buffer.name]
105 + stdout, stderr = execute(command, text)
106 + if stderr:
107 + raise Exception(stderr)
108 + if stdout:
109 + lines = stdout.splitlines()
110 + sequence = difflib.SequenceMatcher(None, vim.current.buffer, lines)
111 + line_num = None
112 + for op in reversed(sequence.get_opcodes()):
113 + if op[0] != 'equal':
114 + vim.current.buffer[op[1]:op[2]] = lines[op[3]:op[4]]
115 + if op[0] == 'insert':
116 + # line_num in vim is 1-based.
117 + line_num = op[1] + 1
118 +
119 + if jump_to_include and line_num:
120 + vim.current.window.cursor = (line_num, 0)
121 +
122 +
123 +# The vim internal implementation (expand("cword"/"cWORD")) doesn't support
124 +# our use case very well, we re-implement our own one.
125 +def get_symbol_under_cursor():
126 + line = vim.eval("line(\".\")")
127 + # column number in vim is 1-based.
128 + col = int(vim.eval("col(\".\")")) - 1
129 + line_text = vim.eval("getline({0})".format(line))
130 + if len(line_text) == 0: return ""
131 + symbol_pos_begin = col
132 + p = re.compile('[a-zA-Z0-9:_]')
133 + while symbol_pos_begin >= 0 and p.match(line_text[symbol_pos_begin]):
134 + symbol_pos_begin -= 1
135 +
136 + symbol_pos_end = col
137 + while symbol_pos_end < len(line_text) and p.match(line_text[symbol_pos_end]):
138 + symbol_pos_end += 1
139 + return line_text[symbol_pos_begin+1:symbol_pos_end]
140 +
141 +
142 +def main():
143 + parser = argparse.ArgumentParser(
144 + description='Vim integration for clang-include-fixer')
145 + parser.add_argument('-db', default='yaml',
146 + help='clang-include-fixer input format.')
147 + parser.add_argument('-input', default='',
148 + help='String to initialize the database.')
149 + # Don't throw exception when parsing unknown arguments to make the script
150 + # work in neovim.
151 + # Neovim (at least v0.2.1) somehow mangles the sys.argv in a weird way: it
152 + # will pass additional arguments (e.g. "-c script_host.py") to sys.argv,
153 + # which makes the script fail.
154 + args, _ = parser.parse_known_args()
155 +
156 + # Get the current text.
157 + buf = vim.current.buffer
158 + text = '\n'.join(buf)
159 +
160 + if query_mode:
161 + symbol = get_symbol_under_cursor()
162 + if len(symbol) == 0:
163 + print("Skip querying empty symbol.")
164 + return
165 + command = [binary, "-stdin", "-query-symbol="+get_symbol_under_cursor(),
166 + "-db=" + args.db, "-input=" + args.input,
167 + vim.current.buffer.name]
168 + else:
169 + # Run command to get all headers.
170 + command = [binary, "-stdin", "-output-headers", "-db=" + args.db,
171 + "-input=" + args.input, vim.current.buffer.name]
172 + stdout, stderr = execute(command, text)
173 + if stderr:
174 + print("Error while running clang-include-fixer: {}".format(stderr),
175 + file=sys.stderr)
176 + return
177 +
178 + include_fixer_context = json.loads(stdout)
179 + query_symbol_infos = include_fixer_context["QuerySymbolInfos"]
180 + if not query_symbol_infos:
181 + print("The file is fine, no need to add a header.")
182 + return
183 + symbol = query_symbol_infos[0]["RawIdentifier"]
184 + # The header_infos is already sorted by clang-include-fixer.
185 + header_infos = include_fixer_context["HeaderInfos"]
186 + # Deduplicate headers while keeping the order, so that the same header would
187 + # not be suggested twice.
188 + unique_headers = []
189 + seen = set()
190 + for header_info in header_infos:
191 + header = header_info["Header"]
192 + if header not in seen:
193 + seen.add(header)
194 + unique_headers.append(header)
195 +
196 + if not unique_headers:
197 + print("Couldn't find a header for {0}.".format(symbol))
198 + return
199 +
200 + try:
201 + selected = unique_headers[0]
202 + inserted_header_infos = header_infos
203 + if len(unique_headers) > 1:
204 + selected = GetUserSelection(
205 + "choose a header file for {0}.".format(symbol),
206 + unique_headers, maximum_suggested_headers)
207 + inserted_header_infos = [
208 + header for header in header_infos if header["Header"] == selected]
209 + include_fixer_context["HeaderInfos"] = inserted_header_infos
210 +
211 + InsertHeaderToVimBuffer(include_fixer_context, text)
212 + print("Added #include {0} for {1}.".format(selected, symbol))
213 + except Exception as error:
214 + print(error, file=sys.stderr)
215 + return
216 +
217 +
218 +if __name__ == '__main__':
219 + main()
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff could not be displayed because it is too large.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.