박우진

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.

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