박우진

change code

Showing 1000 changed files with 611 additions and 1737 deletions

Too many changes to show.

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

{
"files.associations": {
"algorithm": "cpp",
"typeinfo": "cpp",
"cmath": "cpp",
"cstdlib": "cpp",
"limits": "cpp",
"vector": "cpp",
"__bit_reference": "cpp",
"__config": "cpp",
"__debug": "cpp",
"__functional_base": "cpp",
"__hash_table": "cpp",
"__locale": "cpp",
"__node_handle": "cpp",
"__nullptr": "cpp",
"__split_buffer": "cpp",
"__threading_support": "cpp",
"__tuple": "cpp",
"array": "cpp",
"atomic": "cpp",
"bit": "cpp",
"bitset": "cpp",
"cctype": "cpp",
"chrono": "cpp",
"cstdarg": "cpp",
"cstddef": "cpp",
"cstdint": "cpp",
"cstdio": "cpp",
"cstring": "cpp",
"ctime": "cpp",
"cwchar": "cpp",
"exception": "cpp",
"functional": "cpp",
"initializer_list": "cpp",
"ios": "cpp",
"iosfwd": "cpp",
"iostream": "cpp",
"istream": "cpp",
"iterator": "cpp",
"locale": "cpp",
"memory": "cpp",
"mutex": "cpp",
"new": "cpp",
"numeric": "cpp",
"optional": "cpp",
"ostream": "cpp",
"random": "cpp",
"ratio": "cpp",
"set": "cpp",
"sstream": "cpp",
"stdexcept": "cpp",
"streambuf": "cpp",
"string": "cpp",
"string_view": "cpp",
"system_error": "cpp",
"tuple": "cpp",
"type_traits": "cpp",
"unordered_map": "cpp",
"utility": "cpp",
"*.tcc": "cpp",
"memory_resource": "cpp",
"clocale": "cpp",
"condition_variable": "cpp",
"cwctype": "cpp",
"deque": "cpp",
"list": "cpp",
"fstream": "cpp",
"shared_mutex": "cpp",
"thread": "cpp",
"mock_vector": "cpp",
"*.inc": "cpp",
"filesystem": "cpp",
"*.def": "cpp",
"queue": "cpp",
"stack": "cpp"
}
}
\ No newline at end of file
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)
......
......@@ -99,7 +99,7 @@ CreateSymbolInfo(const NamedDecl *ND, const SourceManager &SM,
SourceLocation Loc = SM.getExpansionLoc(ND->getLocation());
if (!Loc.isValid()) {
llvm::errs() << "Declaration " << ND->getDeclName() << "("
llvm::errs() << "Declaration " << ND->getNameAsString() << "("
<< ND->getDeclKindName()
<< ") has invalid declaration location.";
return llvm::None;
......
......@@ -116,7 +116,7 @@ void HelperDeclRGBuilder::run(
const auto *DC = Result.Nodes.getNodeAs<Decl>("dc");
assert(DC);
LLVM_DEBUG(llvm::dbgs() << "Find helper function usage: "
<< FuncRef->getDecl()->getDeclName() << " ("
<< FuncRef->getDecl()->getNameAsString() << " ("
<< FuncRef->getDecl() << ")\n");
RG->addEdge(
getOutmostClassOrFunDecl(DC->getCanonicalDecl()),
......@@ -126,7 +126,7 @@ void HelperDeclRGBuilder::run(
const auto *DC = Result.Nodes.getNodeAs<Decl>("dc");
assert(DC);
LLVM_DEBUG(llvm::dbgs()
<< "Find helper class usage: " << UsedClass->getDeclName()
<< "Find helper class usage: " << UsedClass->getNameAsString()
<< " (" << UsedClass << ")\n");
RG->addEdge(getOutmostClassOrFunDecl(DC->getCanonicalDecl()), UsedClass);
}
......
......@@ -675,8 +675,8 @@ void ClangMoveTool::run(const ast_matchers::MatchFinder::MatchResult &Result) {
Result.Nodes.getNodeAs<NamedDecl>("helper_decls")) {
MovedDecls.push_back(ND);
HelperDeclarations.push_back(ND);
LLVM_DEBUG(llvm::dbgs()
<< "Add helper : " << ND->getDeclName() << " (" << ND << ")\n");
LLVM_DEBUG(llvm::dbgs() << "Add helper : " << ND->getNameAsString() << " ("
<< ND << ")\n");
} else if (const auto *UD = Result.Nodes.getNodeAs<NamedDecl>("using_decl")) {
MovedDecls.push_back(UD);
}
......@@ -735,12 +735,12 @@ void ClangMoveTool::removeDeclsInOldFiles() {
// We remove the helper declarations which are not used in the old.cc after
// moving the given declarations.
for (const auto *D : HelperDeclarations) {
LLVM_DEBUG(llvm::dbgs() << "Check helper is used: " << D->getDeclName()
<< " (" << D << ")\n");
LLVM_DEBUG(llvm::dbgs() << "Check helper is used: "
<< D->getNameAsString() << " (" << D << ")\n");
if (!UsedDecls.count(HelperDeclRGBuilder::getOutmostClassOrFunDecl(
D->getCanonicalDecl()))) {
LLVM_DEBUG(llvm::dbgs() << "Helper removed in old.cc: "
<< D->getDeclName() << " (" << D << ")\n");
<< D->getNameAsString() << " (" << D << ")\n");
RemovedDecls.push_back(D);
}
}
......@@ -820,7 +820,7 @@ void ClangMoveTool::moveDeclsToNewFiles() {
D->getCanonicalDecl())))
continue;
LLVM_DEBUG(llvm::dbgs() << "Helper used in new.cc: " << D->getDeclName()
LLVM_DEBUG(llvm::dbgs() << "Helper used in new.cc: " << D->getNameAsString()
<< " " << D << "\n");
ActualNewCCDecls.push_back(D);
}
......
......@@ -110,33 +110,31 @@ int main(int argc, const char **argv) {
ClangTool Tool(OptionsParser->getCompilations(),
OptionsParser->getSourcePathList());
std::vector<std::unique_ptr<ASTUnit>> ASTs;
int Status = Tool.buildASTs(ASTs);
int ASTStatus = 0;
switch (Tool.buildASTs(ASTs)) {
case 0:
break;
case 1: // Building ASTs failed.
if (Status == 1) {
// Building ASTs failed.
return 1;
case 2:
} else if (Status == 2) {
ASTStatus |= 1;
llvm::errs() << "Failed to build AST for some of the files, "
<< "results may be incomplete."
<< "\n";
break;
default:
llvm_unreachable("Unexpected status returned");
} else {
assert(Status == 0 && "Unexpected status returned");
}
QuerySession QS(ASTs);
if (!Commands.empty()) {
for (auto &Command : Commands) {
QueryRef Q = QueryParser::parse(Command, QS);
for (auto I = Commands.begin(), E = Commands.end(); I != E; ++I) {
QueryRef Q = QueryParser::parse(*I, QS);
if (!Q->run(llvm::outs(), QS))
return 1;
}
} else if (!CommandFiles.empty()) {
for (auto &CommandFile : CommandFiles) {
if (runCommandsInFile(argv[0], CommandFile, QS))
for (auto I = CommandFiles.begin(), E = CommandFiles.end(); I != E; ++I) {
if (runCommandsInFile(argv[0], *I, QS))
return 1;
}
} else {
......
......@@ -3,11 +3,6 @@ set(LLVM_LINK_COMPONENTS
Support
)
configure_file(
${CMAKE_CURRENT_SOURCE_DIR}/clang-tidy-config.h.cmake
${CMAKE_CURRENT_BINARY_DIR}/clang-tidy-config.h)
include_directories(BEFORE ${CMAKE_CURRENT_BINARY_DIR})
add_clang_library(clangTidy
ClangTidy.cpp
ClangTidyCheck.cpp
......@@ -39,7 +34,7 @@ clang_target_link_libraries(clangTidy
clangToolingCore
)
if(CLANG_TIDY_ENABLE_STATIC_ANALYZER)
if(CLANG_ENABLE_STATIC_ANALYZER)
clang_target_link_libraries(clangTidy
PRIVATE
clangStaticAnalyzerCore
......@@ -51,7 +46,6 @@ endif()
# If you add a check, also add it to ClangTidyForceLinker.h in this directory.
add_subdirectory(android)
add_subdirectory(abseil)
add_subdirectory(altera)
add_subdirectory(boost)
add_subdirectory(bugprone)
add_subdirectory(cert)
......@@ -65,7 +59,7 @@ add_subdirectory(llvm)
add_subdirectory(llvmlibc)
add_subdirectory(misc)
add_subdirectory(modernize)
if(CLANG_TIDY_ENABLE_STATIC_ANALYZER)
if(CLANG_ENABLE_STATIC_ANALYZER)
add_subdirectory(mpi)
endif()
add_subdirectory(objc)
......@@ -77,7 +71,6 @@ add_subdirectory(zircon)
set(ALL_CLANG_TIDY_CHECKS
clangTidyAndroidModule
clangTidyAbseilModule
clangTidyAlteraModule
clangTidyBoostModule
clangTidyBugproneModule
clangTidyCERTModule
......@@ -98,7 +91,7 @@ set(ALL_CLANG_TIDY_CHECKS
clangTidyReadabilityModule
clangTidyZirconModule
)
if(CLANG_TIDY_ENABLE_STATIC_ANALYZER)
if(CLANG_ENABLE_STATIC_ANALYZER)
list(APPEND ALL_CLANG_TIDY_CHECKS clangTidyMPIModule)
endif()
set(ALL_CLANG_TIDY_CHECKS ${ALL_CLANG_TIDY_CHECKS} PARENT_SCOPE)
......
......@@ -20,11 +20,11 @@
#include "ClangTidyModuleRegistry.h"
#include "ClangTidyProfiling.h"
#include "ExpandModularHeadersPPCallbacks.h"
#include "clang-tidy-config.h"
#include "clang/AST/ASTConsumer.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/Decl.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/Config/config.h"
#include "clang/Format/Format.h"
#include "clang/Frontend/ASTConsumers.h"
#include "clang/Frontend/CompilerInstance.h"
......@@ -47,10 +47,10 @@
#include <algorithm>
#include <utility>
#if CLANG_TIDY_ENABLE_STATIC_ANALYZER
#if CLANG_ENABLE_STATIC_ANALYZER
#include "clang/Analysis/PathDiagnostic.h"
#include "clang/StaticAnalyzer/Frontend/AnalysisConsumer.h"
#endif // CLANG_TIDY_ENABLE_STATIC_ANALYZER
#endif // CLANG_ENABLE_STATIC_ANALYZER
using namespace clang::ast_matchers;
using namespace clang::driver;
......@@ -63,7 +63,7 @@ namespace clang {
namespace tidy {
namespace {
#if CLANG_TIDY_ENABLE_STATIC_ANALYZER
#if CLANG_ENABLE_STATIC_ANALYZER
static const char *AnalyzerCheckNamePrefix = "clang-analyzer-";
class AnalyzerDiagnosticConsumer : public ento::PathDiagnosticConsumer {
......@@ -95,7 +95,7 @@ public:
private:
ClangTidyContext &Context;
};
#endif // CLANG_TIDY_ENABLE_STATIC_ANALYZER
#endif // CLANG_ENABLE_STATIC_ANALYZER
class ErrorReporter {
public:
......@@ -324,16 +324,17 @@ ClangTidyASTConsumerFactory::ClangTidyASTConsumerFactory(
}
}
#if CLANG_TIDY_ENABLE_STATIC_ANALYZER
#if CLANG_ENABLE_STATIC_ANALYZER
static void setStaticAnalyzerCheckerOpts(const ClangTidyOptions &Opts,
AnalyzerOptionsRef AnalyzerOptions) {
StringRef AnalyzerPrefix(AnalyzerCheckNamePrefix);
for (const auto &Opt : Opts.CheckOptions) {
StringRef OptName(Opt.getKey());
if (!OptName.consume_front(AnalyzerPrefix))
StringRef OptName(Opt.first);
if (!OptName.startswith(AnalyzerPrefix))
continue;
// Analyzer options are always local options so we can ignore priority.
AnalyzerOptions->Config[OptName] = Opt.getValue().Value;
AnalyzerOptions->Config[OptName.substr(AnalyzerPrefix.size())] =
Opt.second.Value;
}
}
......@@ -369,7 +370,7 @@ static CheckersList getAnalyzerCheckersAndPackages(ClangTidyContext &Context,
}
return List;
}
#endif // CLANG_TIDY_ENABLE_STATIC_ANALYZER
#endif // CLANG_ENABLE_STATIC_ANALYZER
std::unique_ptr<clang::ASTConsumer>
ClangTidyASTConsumerFactory::CreateASTConsumer(
......@@ -424,7 +425,7 @@ ClangTidyASTConsumerFactory::CreateASTConsumer(
if (!Checks.empty())
Consumers.push_back(Finder->newASTConsumer());
#if CLANG_TIDY_ENABLE_STATIC_ANALYZER
#if CLANG_ENABLE_STATIC_ANALYZER
AnalyzerOptionsRef AnalyzerOptions = Compiler.getAnalyzerOpts();
AnalyzerOptions->CheckersAndPackages = getAnalyzerCheckersAndPackages(
Context, Context.canEnableAnalyzerAlphaCheckers());
......@@ -440,7 +441,7 @@ ClangTidyASTConsumerFactory::CreateASTConsumer(
new AnalyzerDiagnosticConsumer(Context));
Consumers.push_back(std::move(AnalysisConsumer));
}
#endif // CLANG_TIDY_ENABLE_STATIC_ANALYZER
#endif // CLANG_ENABLE_STATIC_ANALYZER
return std::make_unique<ClangTidyASTConsumer>(
std::move(Consumers), std::move(Profiling), std::move(Finder),
std::move(Checks));
......@@ -449,15 +450,15 @@ ClangTidyASTConsumerFactory::CreateASTConsumer(
std::vector<std::string> ClangTidyASTConsumerFactory::getCheckNames() {
std::vector<std::string> CheckNames;
for (const auto &CheckFactory : *CheckFactories) {
if (Context.isCheckEnabled(CheckFactory.getKey()))
CheckNames.emplace_back(CheckFactory.getKey());
if (Context.isCheckEnabled(CheckFactory.first))
CheckNames.push_back(CheckFactory.first);
}
#if CLANG_TIDY_ENABLE_STATIC_ANALYZER
#if CLANG_ENABLE_STATIC_ANALYZER
for (const auto &AnalyzerCheck : getAnalyzerCheckersAndPackages(
Context, Context.canEnableAnalyzerAlphaCheckers()))
CheckNames.push_back(AnalyzerCheckNamePrefix + AnalyzerCheck.first);
#endif // CLANG_TIDY_ENABLE_STATIC_ANALYZER
#endif // CLANG_ENABLE_STATIC_ANALYZER
llvm::sort(CheckNames);
return CheckNames;
......
......@@ -10,7 +10,6 @@
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/WithColor.h"
#include "llvm/Support/raw_ostream.h"
namespace clang {
......@@ -73,7 +72,7 @@ llvm::Expected<std::string>
ClangTidyCheck::OptionsView::get(StringRef LocalName) const {
const auto &Iter = CheckOptions.find(NamePrefix + LocalName.str());
if (Iter != CheckOptions.end())
return Iter->getValue().Value;
return Iter->second.Value;
return llvm::make_error<MissingOptionError>((NamePrefix + LocalName).str());
}
......@@ -86,7 +85,7 @@ findPriorityOption(const ClangTidyOptions::OptionMap &Options, StringRef NamePre
return IterGlobal;
if (IterGlobal == Options.end())
return IterLocal;
if (IterLocal->getValue().Priority >= IterGlobal->getValue().Priority)
if (IterLocal->second.Priority >= IterGlobal->second.Priority)
return IterLocal;
return IterGlobal;
}
......@@ -95,7 +94,7 @@ llvm::Expected<std::string>
ClangTidyCheck::OptionsView::getLocalOrGlobal(StringRef LocalName) const {
auto Iter = findPriorityOption(CheckOptions, NamePrefix, LocalName);
if (Iter != CheckOptions.end())
return Iter->getValue().Value;
return Iter->second.Value;
return llvm::make_error<MissingOptionError>((NamePrefix + LocalName).str());
}
......@@ -127,7 +126,7 @@ bool ClangTidyCheck::OptionsView::get<bool>(StringRef LocalName,
llvm::Expected<bool> ValueOr = get<bool>(LocalName);
if (ValueOr)
return *ValueOr;
logIfOptionParsingError(ValueOr.takeError());
logErrToStdErr(ValueOr.takeError());
return Default;
}
......@@ -136,7 +135,7 @@ llvm::Expected<bool>
ClangTidyCheck::OptionsView::getLocalOrGlobal<bool>(StringRef LocalName) const {
auto Iter = findPriorityOption(CheckOptions, NamePrefix, LocalName);
if (Iter != CheckOptions.end())
return getAsBool(Iter->getValue().Value, Iter->getKey());
return getAsBool(Iter->second.Value, Iter->first);
return llvm::make_error<MissingOptionError>((NamePrefix + LocalName).str());
}
......@@ -146,7 +145,7 @@ bool ClangTidyCheck::OptionsView::getLocalOrGlobal<bool>(StringRef LocalName,
llvm::Expected<bool> ValueOr = getLocalOrGlobal<bool>(LocalName);
if (ValueOr)
return *ValueOr;
logIfOptionParsingError(ValueOr.takeError());
logErrToStdErr(ValueOr.takeError());
return Default;
}
......@@ -169,16 +168,17 @@ void ClangTidyCheck::OptionsView::store<bool>(
store(Options, LocalName, Value ? StringRef("true") : StringRef("false"));
}
llvm::Expected<int64_t> ClangTidyCheck::OptionsView::getEnumInt(
StringRef LocalName, ArrayRef<NameAndValue> Mapping, bool CheckGlobal,
bool IgnoreCase) const {
llvm::Expected<int64_t>
ClangTidyCheck::OptionsView::getEnumInt(StringRef LocalName,
ArrayRef<NameAndValue> Mapping,
bool CheckGlobal, bool IgnoreCase) {
auto Iter = CheckGlobal
? findPriorityOption(CheckOptions, NamePrefix, LocalName)
: CheckOptions.find((NamePrefix + LocalName).str());
if (Iter == CheckOptions.end())
return llvm::make_error<MissingOptionError>((NamePrefix + LocalName).str());
StringRef Value = Iter->getValue().Value;
StringRef Value = Iter->second.Value;
StringRef Closest;
unsigned EditDistance = -1;
for (const auto &NameAndEnum : Mapping) {
......@@ -200,38 +200,18 @@ llvm::Expected<int64_t> ClangTidyCheck::OptionsView::getEnumInt(
}
if (EditDistance < 3)
return llvm::make_error<UnparseableEnumOptionError>(
Iter->getKey().str(), Iter->getValue().Value, Closest.str());
return llvm::make_error<UnparseableEnumOptionError>(Iter->getKey().str(),
Iter->getValue().Value);
Iter->first, Iter->second.Value, std::string(Closest));
return llvm::make_error<UnparseableEnumOptionError>(Iter->first,
Iter->second.Value);
}
void ClangTidyCheck::OptionsView::logIfOptionParsingError(llvm::Error &&Err) {
if (auto RemainingErrors =
llvm::handleErrors(std::move(Err), [](const MissingOptionError &) {}))
llvm::logAllUnhandledErrors(std::move(RemainingErrors),
llvm::WithColor::warning());
void ClangTidyCheck::OptionsView::logErrToStdErr(llvm::Error &&Err) {
llvm::logAllUnhandledErrors(
llvm::handleErrors(std::move(Err),
[](const MissingOptionError &) -> llvm::Error {
return llvm::Error::success();
}),
llvm::errs(), "warning: ");
}
template <>
Optional<std::string> ClangTidyCheck::OptionsView::getOptional<std::string>(
StringRef LocalName) const {
if (auto ValueOr = get(LocalName))
return *ValueOr;
else
consumeError(ValueOr.takeError());
return llvm::None;
}
template <>
Optional<std::string>
ClangTidyCheck::OptionsView::getOptionalLocalOrGlobal<std::string>(
StringRef LocalName) const {
if (auto ValueOr = getLocalOrGlobal(LocalName))
return *ValueOr;
else
consumeError(ValueOr.takeError());
return llvm::None;
}
} // namespace tidy
} // namespace clang
......
......@@ -268,7 +268,7 @@ public:
if (llvm::Expected<T> ValueOr = get<T>(LocalName))
return *ValueOr;
else
logIfOptionParsingError(ValueOr.takeError());
logErrToStdErr(ValueOr.takeError());
return Default;
}
......@@ -314,7 +314,7 @@ public:
if (llvm::Expected<T> ValueOr = getLocalOrGlobal<T>(LocalName))
return *ValueOr;
else
logIfOptionParsingError(ValueOr.takeError());
logErrToStdErr(ValueOr.takeError());
return Default;
}
......@@ -330,7 +330,7 @@ public:
/// supply the mapping required to convert between ``T`` and a string.
template <typename T>
std::enable_if_t<std::is_enum<T>::value, llvm::Expected<T>>
get(StringRef LocalName, bool IgnoreCase = false) const {
get(StringRef LocalName, bool IgnoreCase = false) {
if (llvm::Expected<int64_t> ValueOr =
getEnumInt(LocalName, typeEraseMapping<T>(), false, IgnoreCase))
return static_cast<T>(*ValueOr);
......@@ -349,11 +349,11 @@ public:
/// supply the mapping required to convert between ``T`` and a string.
template <typename T>
std::enable_if_t<std::is_enum<T>::value, T>
get(StringRef LocalName, T Default, bool IgnoreCase = false) const {
get(StringRef LocalName, T Default, bool IgnoreCase = false) {
if (auto ValueOr = get<T>(LocalName, IgnoreCase))
return *ValueOr;
else
logIfOptionParsingError(ValueOr.takeError());
logErrToStdErr(ValueOr.takeError());
return Default;
}
......@@ -370,7 +370,8 @@ public:
/// supply the mapping required to convert between ``T`` and a string.
template <typename T>
std::enable_if_t<std::is_enum<T>::value, llvm::Expected<T>>
getLocalOrGlobal(StringRef LocalName, bool IgnoreCase = false) const {
getLocalOrGlobal(StringRef LocalName,
bool IgnoreCase = false) {
if (llvm::Expected<int64_t> ValueOr =
getEnumInt(LocalName, typeEraseMapping<T>(), true, IgnoreCase))
return static_cast<T>(*ValueOr);
......@@ -390,40 +391,14 @@ public:
/// supply the mapping required to convert between ``T`` and a string.
template <typename T>
std::enable_if_t<std::is_enum<T>::value, T>
getLocalOrGlobal(StringRef LocalName, T Default,
bool IgnoreCase = false) const {
getLocalOrGlobal(StringRef LocalName, T Default, bool IgnoreCase = false) {
if (auto ValueOr = getLocalOrGlobal<T>(LocalName, IgnoreCase))
return *ValueOr;
else
logIfOptionParsingError(ValueOr.takeError());
logErrToStdErr(ValueOr.takeError());
return Default;
}
/// Returns the value for the option \p LocalName represented as a ``T``.
/// If the option is missing returns None, if the option can't be parsed
/// as a ``T``, log that to stderr and return None.
template <typename T = std::string>
llvm::Optional<T> getOptional(StringRef LocalName) const {
if (auto ValueOr = get<T>(LocalName))
return *ValueOr;
else
logIfOptionParsingError(ValueOr.takeError());
return llvm::None;
}
/// Returns the value for the local or global option \p LocalName
/// represented as a ``T``.
/// If the option is missing returns None, if the
/// option can't be parsed as a ``T``, log that to stderr and return None.
template <typename T = std::string>
llvm::Optional<T> getOptionalLocalOrGlobal(StringRef LocalName) const {
if (auto ValueOr = getLocalOrGlobal<T>(LocalName))
return *ValueOr;
else
logIfOptionParsingError(ValueOr.takeError());
return llvm::None;
}
/// Stores an option with the check-local name \p LocalName with
/// string value \p Value to \p Options.
void store(ClangTidyOptions::OptionMap &Options, StringRef LocalName,
......@@ -445,8 +420,7 @@ public:
/// supply the mapping required to convert between ``T`` and a string.
template <typename T>
std::enable_if_t<std::is_enum<T>::value>
store(ClangTidyOptions::OptionMap &Options, StringRef LocalName,
T Value) const {
store(ClangTidyOptions::OptionMap &Options, StringRef LocalName, T Value) {
ArrayRef<std::pair<T, StringRef>> Mapping =
OptionEnumMapping<T>::getEnumMapping();
auto Iter = llvm::find_if(
......@@ -462,11 +436,11 @@ public:
llvm::Expected<int64_t> getEnumInt(StringRef LocalName,
ArrayRef<NameAndValue> Mapping,
bool CheckGlobal, bool IgnoreCase) const;
bool CheckGlobal, bool IgnoreCase);
template <typename T>
std::enable_if_t<std::is_enum<T>::value, std::vector<NameAndValue>>
typeEraseMapping() const {
typeEraseMapping() {
ArrayRef<std::pair<T, StringRef>> Mapping =
OptionEnumMapping<T>::getEnumMapping();
std::vector<NameAndValue> Result;
......@@ -481,8 +455,7 @@ public:
void storeInt(ClangTidyOptions::OptionMap &Options, StringRef LocalName,
int64_t Value) const;
/// Logs an Error to stderr if a \p Err is not a MissingOptionError.
static void logIfOptionParsingError(llvm::Error &&Err);
static void logErrToStdErr(llvm::Error &&Err);
std::string NamePrefix;
const ClangTidyOptions::OptionMap &CheckOptions;
......@@ -550,19 +523,6 @@ void ClangTidyCheck::OptionsView::store<bool>(
ClangTidyOptions::OptionMap &Options, StringRef LocalName,
bool Value) const;
/// Returns the value for the option \p LocalName.
/// If the option is missing returns None.
template <>
Optional<std::string> ClangTidyCheck::OptionsView::getOptional<std::string>(
StringRef LocalName) const;
/// Returns the value for the local or global option \p LocalName.
/// If the option is missing returns None.
template <>
Optional<std::string>
ClangTidyCheck::OptionsView::getOptionalLocalOrGlobal<std::string>(
StringRef LocalName) const;
} // namespace tidy
} // namespace clang
......
......@@ -31,7 +31,6 @@
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/StringMap.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/FormatVariadic.h"
#include "llvm/Support/Regex.h"
#include <tuple>
......@@ -591,7 +590,6 @@ void ClangTidyDiagnosticConsumer::removeIncompatibleErrors() {
// An event can be either the begin or the end of an interval.
enum EventType {
ET_Begin = 1,
ET_Insert = 0,
ET_End = -1,
};
......@@ -623,17 +621,10 @@ void ClangTidyDiagnosticConsumer::removeIncompatibleErrors() {
// one will be processed before, disallowing the second one, and the
// end point of the first one will also be processed before,
// disallowing the first one.
switch (Type) {
case ET_Begin:
if (Type == ET_Begin)
Priority = std::make_tuple(Begin, Type, -End, -ErrorSize, ErrorId);
break;
case ET_Insert:
Priority = std::make_tuple(Begin, Type, -End, ErrorSize, ErrorId);
break;
case ET_End:
else
Priority = std::make_tuple(End, Type, -Begin, ErrorSize, ErrorId);
break;
}
}
bool operator<(const Event &Other) const {
......@@ -671,19 +662,19 @@ void ClangTidyDiagnosticConsumer::removeIncompatibleErrors() {
}
// Build events from error intervals.
llvm::StringMap<std::vector<Event>> FileEvents;
std::map<std::string, std::vector<Event>> FileEvents;
for (unsigned I = 0; I < ErrorFixes.size(); ++I) {
for (const auto &FileAndReplace : *ErrorFixes[I].second) {
for (const auto &Replace : FileAndReplace.second) {
unsigned Begin = Replace.getOffset();
unsigned End = Begin + Replace.getLength();
auto &Events = FileEvents[Replace.getFilePath()];
if (Begin == End) {
Events.emplace_back(Begin, End, Event::ET_Insert, I, Sizes[I]);
} else {
Events.emplace_back(Begin, End, Event::ET_Begin, I, Sizes[I]);
Events.emplace_back(Begin, End, Event::ET_End, I, Sizes[I]);
}
const std::string &FilePath = std::string(Replace.getFilePath());
// FIXME: Handle empty intervals, such as those from insertions.
if (Begin == End)
continue;
auto &Events = FileEvents[FilePath];
Events.emplace_back(Begin, End, Event::ET_Begin, I, Sizes[I]);
Events.emplace_back(Begin, End, Event::ET_End, I, Sizes[I]);
}
}
}
......@@ -695,20 +686,14 @@ void ClangTidyDiagnosticConsumer::removeIncompatibleErrors() {
llvm::sort(Events);
int OpenIntervals = 0;
for (const auto &Event : Events) {
switch (Event.Type) {
case Event::ET_Begin:
if (OpenIntervals++ != 0)
Apply[Event.ErrorId] = false;
break;
case Event::ET_Insert:
if (OpenIntervals != 0)
Apply[Event.ErrorId] = false;
break;
case Event::ET_End:
if (--OpenIntervals != 0)
Apply[Event.ErrorId] = false;
break;
}
if (Event.Type == Event::ET_End)
--OpenIntervals;
// This has to be checked after removing the interval from the count if it
// is an end event, or before adding it if it is a begin event.
if (OpenIntervals != 0)
Apply[Event.ErrorId] = false;
if (Event.Type == Event::ET_Begin)
++OpenIntervals;
}
assert(OpenIntervals == 0 && "Amount of begin/end points doesn't match");
}
......@@ -744,7 +729,7 @@ struct EqualClangTidyError {
std::vector<ClangTidyError> ClangTidyDiagnosticConsumer::take() {
finalizeLastError();
llvm::stable_sort(Errors, LessClangTidyError());
llvm::sort(Errors, LessClangTidyError());
Errors.erase(std::unique(Errors.begin(), Errors.end(), EqualClangTidyError()),
Errors.end());
if (RemoveIncompatibleErrors)
......
......@@ -9,7 +9,7 @@
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CLANGTIDYFORCELINKER_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CLANGTIDYFORCELINKER_H
#include "clang-tidy-config.h"
#include "clang/Config/config.h"
#include "llvm/Support/Compiler.h"
namespace clang {
......@@ -20,11 +20,6 @@ extern volatile int AbseilModuleAnchorSource;
static int LLVM_ATTRIBUTE_UNUSED AbseilModuleAnchorDestination =
AbseilModuleAnchorSource;
// This anchor is used to force the linker to link the AlteraModule.
extern volatile int AlteraModuleAnchorSource;
static int LLVM_ATTRIBUTE_UNUSED AlteraModuleAnchorDestination =
AlteraModuleAnchorSource;
// This anchor is used to force the linker to link the AndroidModule.
extern volatile int AndroidModuleAnchorSource;
static int LLVM_ATTRIBUTE_UNUSED AndroidModuleAnchorDestination =
......@@ -95,7 +90,7 @@ extern volatile int ModernizeModuleAnchorSource;
static int LLVM_ATTRIBUTE_UNUSED ModernizeModuleAnchorDestination =
ModernizeModuleAnchorSource;
#if CLANG_TIDY_ENABLE_STATIC_ANALYZER && \
#if CLANG_ENABLE_STATIC_ANALYZER && \
!defined(CLANG_TIDY_DISABLE_STATIC_ANALYZER_CHECKS)
// This anchor is used to force the linker to link the MPIModule.
extern volatile int MPIModuleAnchorSource;
......
......@@ -18,15 +18,15 @@ namespace tidy {
void ClangTidyCheckFactories::registerCheckFactory(StringRef Name,
CheckFactory Factory) {
Factories.insert_or_assign(Name, std::move(Factory));
Factories[std::string(Name)] = std::move(Factory);
}
std::vector<std::unique_ptr<ClangTidyCheck>>
ClangTidyCheckFactories::createChecks(ClangTidyContext *Context) {
std::vector<std::unique_ptr<ClangTidyCheck>> Checks;
for (const auto &Factory : Factories) {
if (Context->isCheckEnabled(Factory.getKey()))
Checks.emplace_back(Factory.getValue()(Factory.getKey(), Context));
if (Context->isCheckEnabled(Factory.first))
Checks.emplace_back(Factory.second(Factory.first, Context));
}
return Checks;
}
......
......@@ -69,7 +69,7 @@ public:
std::vector<std::unique_ptr<ClangTidyCheck>>
createChecks(ClangTidyContext *Context);
typedef llvm::StringMap<CheckFactory> FactoryMap;
typedef std::map<std::string, CheckFactory> FactoryMap;
FactoryMap::const_iterator begin() const { return Factories.begin(); }
FactoryMap::const_iterator end() const { return Factories.end(); }
bool empty() const { return Factories.empty(); }
......
......@@ -70,7 +70,7 @@ struct NOptionMap {
NOptionMap(IO &, const ClangTidyOptions::OptionMap &OptionMap) {
Options.reserve(OptionMap.size());
for (const auto &KeyValue : OptionMap)
Options.emplace_back(std::string(KeyValue.getKey()), KeyValue.getValue().Value);
Options.emplace_back(KeyValue.first, KeyValue.second.Value);
}
ClangTidyOptions::OptionMap denormalize(IO &) {
ClangTidyOptions::OptionMap Map;
......@@ -114,9 +114,11 @@ ClangTidyOptions ClangTidyOptions::getDefaults() {
Options.SystemHeaders = false;
Options.FormatStyle = "none";
Options.User = llvm::None;
unsigned Priority = 0;
for (const ClangTidyModuleRegistry::entry &Module :
ClangTidyModuleRegistry::entries())
Options = Options.mergeWith(Module.instantiate()->getModuleOptions(), 0);
Options =
Options.mergeWith(Module.instantiate()->getModuleOptions(), ++Priority);
return Options;
}
......@@ -157,10 +159,8 @@ ClangTidyOptions ClangTidyOptions::mergeWith(const ClangTidyOptions &Other,
mergeVectors(Result.ExtraArgsBefore, Other.ExtraArgsBefore);
for (const auto &KeyValue : Other.CheckOptions) {
Result.CheckOptions.insert_or_assign(
KeyValue.getKey(),
ClangTidyValue(KeyValue.getValue().Value,
KeyValue.getValue().Priority + Priority));
Result.CheckOptions[KeyValue.first] = ClangTidyValue(
KeyValue.second.Value, KeyValue.second.Priority + Priority);
}
return Result;
......
......@@ -16,6 +16,7 @@
#include "llvm/Support/ErrorOr.h"
#include "llvm/Support/VirtualFileSystem.h"
#include <functional>
#include <map>
#include <string>
#include <system_error>
#include <utility>
......@@ -107,7 +108,7 @@ struct ClangTidyOptions {
unsigned Priority;
};
typedef std::pair<std::string, std::string> StringPair;
typedef llvm::StringMap<ClangTidyValue> OptionMap;
typedef std::map<std::string, ClangTidyValue> OptionMap;
/// Key-value mapping used to store check-specific options.
OptionMap CheckOptions;
......
......@@ -47,18 +47,14 @@ AST_POLYMORPHIC_MATCHER(
if (PrefixPosition == StringRef::npos)
return false;
Path = Path.drop_front(PrefixPosition + AbslPrefix.size());
static const char *AbseilLibraries[] = {"algorithm", "base",
"container", "debugging",
"flags", "hash",
"iterator", "memory",
"meta", "numeric",
"random", "status",
"strings", "synchronization",
"time", "types",
"utility"};
return llvm::any_of(AbseilLibraries, [&](const char *Library) {
return Path.startswith(Library);
});
static const char *AbseilLibraries[] = {
"algorithm", "base", "container", "debugging", "flags",
"hash", "iterator", "memory", "meta", "numeric",
"random", "strings", "synchronization", "time", "types",
"utility"};
return std::any_of(
std::begin(AbseilLibraries), std::end(AbseilLibraries),
[&](const char *Library) { return Path.startswith(Library); });
}
} // namespace ast_matchers
......
......@@ -26,8 +26,8 @@ StringFindStartswithCheck::StringFindStartswithCheck(StringRef Name,
: ClangTidyCheck(Name, Context),
StringLikeClasses(utils::options::parseStringList(
Options.get("StringLikeClasses", "::std::basic_string"))),
IncludeInserter(Options.getLocalOrGlobal("IncludeStyle",
utils::IncludeSorter::IS_LLVM)),
IncludeStyle(Options.getLocalOrGlobal("IncludeStyle",
utils::IncludeSorter::IS_LLVM)),
AbseilStringsMatchHeader(
Options.get("AbseilStringsMatchHeader", "absl/strings/match.h")) {}
......@@ -105,21 +105,23 @@ void StringFindStartswithCheck::check(const MatchFinder::MatchResult &Result) {
// Create a preprocessor #include FixIt hint (CreateIncludeInsertion checks
// whether this already exists).
Diagnostic << IncludeInserter.createIncludeInsertion(
Source.getFileID(ComparisonExpr->getBeginLoc()),
AbseilStringsMatchHeader);
Diagnostic << IncludeInserter->CreateIncludeInsertion(
Source.getFileID(ComparisonExpr->getBeginLoc()), AbseilStringsMatchHeader,
false);
}
void StringFindStartswithCheck::registerPPCallbacks(
const SourceManager &SM, Preprocessor *PP, Preprocessor *ModuleExpanderPP) {
IncludeInserter.registerPreprocessor(PP);
IncludeInserter = std::make_unique<utils::IncludeInserter>(SM, getLangOpts(),
IncludeStyle);
PP->addPPCallbacks(IncludeInserter->CreatePPCallbacks());
}
void StringFindStartswithCheck::storeOptions(
ClangTidyOptions::OptionMap &Opts) {
Options.store(Opts, "StringLikeClasses",
utils::options::serializeStringList(StringLikeClasses));
Options.store(Opts, "IncludeStyle", IncludeInserter.getStyle());
Options.store(Opts, "IncludeStyle", IncludeStyle);
Options.store(Opts, "AbseilStringsMatchHeader", AbseilStringsMatchHeader);
}
......
......@@ -35,8 +35,9 @@ public:
void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
private:
std::unique_ptr<clang::tidy::utils::IncludeInserter> IncludeInserter;
const std::vector<std::string> StringLikeClasses;
utils::IncludeInserter IncludeInserter;
const utils::IncludeSorter::IncludeStyle IncludeStyle;
const std::string AbseilStringsMatchHeader;
};
......
......@@ -136,7 +136,7 @@ void %(check_name)s::registerMatchers(MatchFinder *Finder) {
void %(check_name)s::check(const MatchFinder::MatchResult &Result) {
// FIXME: Add callback implementation.
const auto *MatchedDecl = Result.Nodes.getNodeAs<FunctionDecl>("x");
if (!MatchedDecl->getIdentifier() || MatchedDecl->getName().startswith("awesome_"))
if (MatchedDecl->getName().startswith("awesome_"))
return;
diag(MatchedDecl->getLocation(), "function %%0 is insufficiently awesome")
<< MatchedDecl;
......
//===--- AlteraTidyModule.cpp - clang-tidy --------------------------------===//
//
// 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 "../ClangTidy.h"
#include "../ClangTidyModule.h"
#include "../ClangTidyModuleRegistry.h"
#include "StructPackAlignCheck.h"
using namespace clang::ast_matchers;
namespace clang {
namespace tidy {
namespace altera {
class AlteraModule : public ClangTidyModule {
public:
void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override {
CheckFactories.registerCheck<StructPackAlignCheck>(
"altera-struct-pack-align");
}
};
} // namespace altera
// Register the AlteraTidyModule using this statically initialized variable.
static ClangTidyModuleRegistry::Add<altera::AlteraModule>
X("altera-module", "Adds Altera FPGA OpenCL lint checks.");
// This anchor is used to force the linker to link in the generated object file
// and thus register the AlteraModule.
volatile int AlteraModuleAnchorSource = 0;
} // namespace tidy
} // namespace clang
set(LLVM_LINK_COMPONENTS
FrontendOpenMP
support
)
add_clang_library(clangTidyAlteraModule
AlteraTidyModule.cpp
StructPackAlignCheck.cpp
LINK_LIBS
clangTidy
clangTidyUtils
)
clang_target_link_libraries(clangTidyAlteraModule
PRIVATE
clangAnalysis
clangAST
clangASTMatchers
clangBasic
clangLex
)
//===--- StructPackAlignCheck.cpp - clang-tidy ----------------------------===//
//
// 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 "StructPackAlignCheck.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/RecordLayout.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include <math.h>
#include <sstream>
using namespace clang::ast_matchers;
namespace clang {
namespace tidy {
namespace altera {
void StructPackAlignCheck::registerMatchers(MatchFinder *Finder) {
Finder->addMatcher(recordDecl(isStruct(), isDefinition(),
unless(isExpansionInSystemHeader()))
.bind("struct"),
this);
}
CharUnits
StructPackAlignCheck::computeRecommendedAlignment(CharUnits MinByteSize) {
CharUnits NewAlign = CharUnits::fromQuantity(1);
if (!MinByteSize.isPowerOfTwo()) {
int MSB = (int)MinByteSize.getQuantity();
for (; MSB > 0; MSB /= 2) {
NewAlign = NewAlign.alignTo(
CharUnits::fromQuantity(((int)NewAlign.getQuantity()) * 2));
// Abort if the computed alignment meets the maximum configured alignment.
if (NewAlign.getQuantity() >= MaxConfiguredAlignment)
break;
}
} else {
NewAlign = MinByteSize;
}
return NewAlign;
}
void StructPackAlignCheck::check(const MatchFinder::MatchResult &Result) {
const auto *Struct = Result.Nodes.getNodeAs<RecordDecl>("struct");
// Do not trigger on templated struct declarations because the packing and
// alignment requirements are unknown.
if (Struct->isTemplated())
return;
// Get sizing info for the struct.
llvm::SmallVector<std::pair<unsigned int, unsigned int>, 10> FieldSizes;
unsigned int TotalBitSize = 0;
for (const FieldDecl *StructField : Struct->fields()) {
// For each StructField, record how big it is (in bits).
// Would be good to use a pair of <offset, size> to advise a better
// packing order.
unsigned int StructFieldWidth =
(unsigned int)Result.Context
->getTypeInfo(StructField->getType().getTypePtr())
.Width;
FieldSizes.emplace_back(StructFieldWidth, StructField->getFieldIndex());
// FIXME: Recommend a reorganization of the struct (sort by StructField
// size, largest to smallest).
TotalBitSize += StructFieldWidth;
}
uint64_t CharSize = Result.Context->getCharWidth();
CharUnits CurrSize = Result.Context->getASTRecordLayout(Struct).getSize();
CharUnits MinByteSize =
CharUnits::fromQuantity(ceil((float)TotalBitSize / CharSize));
CharUnits MaxAlign = CharUnits::fromQuantity(
ceil((float)Struct->getMaxAlignment() / CharSize));
CharUnits CurrAlign =
Result.Context->getASTRecordLayout(Struct).getAlignment();
CharUnits NewAlign = computeRecommendedAlignment(MinByteSize);
bool IsPacked = Struct->hasAttr<PackedAttr>();
bool NeedsPacking = (MinByteSize < CurrSize) && (MaxAlign != NewAlign) &&
(CurrSize != NewAlign);
bool NeedsAlignment = CurrAlign.getQuantity() != NewAlign.getQuantity();
if (!NeedsAlignment && !NeedsPacking)
return;
// If it's using much more space than it needs, suggest packing.
// (Do not suggest packing if it is currently explicitly aligned to what the
// minimum byte size would suggest as the new alignment.)
if (NeedsPacking && !IsPacked) {
diag(Struct->getLocation(),
"accessing fields in struct %0 is inefficient due to padding; only "
"needs %1 bytes but is using %2 bytes")
<< Struct << (int)MinByteSize.getQuantity()
<< (int)CurrSize.getQuantity()
<< FixItHint::CreateInsertion(Struct->getEndLoc().getLocWithOffset(1),
" __attribute__((packed))");
diag(Struct->getLocation(),
"use \"__attribute__((packed))\" to reduce the amount of padding "
"applied to struct %0",
DiagnosticIDs::Note)
<< Struct;
}
FixItHint FixIt;
AlignedAttr *Attribute = Struct->getAttr<AlignedAttr>();
std::string NewAlignQuantity = std::to_string((int)NewAlign.getQuantity());
if (Attribute) {
std::ostringstream FixItString;
FixItString << "aligned(" << NewAlignQuantity << ")";
FixIt =
FixItHint::CreateReplacement(Attribute->getRange(), FixItString.str());
} else {
std::ostringstream FixItString;
FixItString << " __attribute__((aligned(" << NewAlignQuantity << ")))";
FixIt = FixItHint::CreateInsertion(Struct->getEndLoc().getLocWithOffset(1),
FixItString.str());
}
// And suggest the minimum power-of-two alignment for the struct as a whole
// (with and without packing).
if (NeedsAlignment) {
diag(Struct->getLocation(),
"accessing fields in struct %0 is inefficient due to poor alignment; "
"currently aligned to %1 bytes, but recommended alignment is %2 bytes")
<< Struct << (int)CurrAlign.getQuantity() << NewAlignQuantity << FixIt;
diag(Struct->getLocation(),
"use \"__attribute__((aligned(%0)))\" to align struct %1 to %0 bytes",
DiagnosticIDs::Note)
<< NewAlignQuantity << Struct;
}
}
void StructPackAlignCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
Options.store(Opts, "MaxConfiguredAlignment", MaxConfiguredAlignment);
}
} // namespace altera
} // namespace tidy
} // namespace clang
//===--- StructPackAlignCheck.h - clang-tidy --------------------*- 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_CLANG_TIDY_ALTERA_STRUCTPACKALIGNCHECK_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_ALTERA_STRUCTPACKALIGNCHECK_H
#include "../ClangTidyCheck.h"
namespace clang {
namespace tidy {
namespace altera {
/// Finds structs that are inefficiently packed or aligned, and recommends
/// packing and/or aligning of said structs as needed.
///
/// For the user-facing documentation see:
/// http://clang.llvm.org/extra/clang-tidy/checks/altera-struct-pack-align.html
class StructPackAlignCheck : public ClangTidyCheck {
public:
StructPackAlignCheck(StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context),
MaxConfiguredAlignment(Options.get("MaxConfiguredAlignment", 128)) {}
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
private:
const unsigned MaxConfiguredAlignment;
CharUnits computeRecommendedAlignment(CharUnits MinByteSize);
};
} // namespace altera
} // namespace tidy
} // namespace clang
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_ALTERA_STRUCTPACKALIGNCHECK_H
......@@ -18,17 +18,32 @@ namespace clang {
namespace tidy {
namespace android {
ComparisonInTempFailureRetryCheck::ComparisonInTempFailureRetryCheck(
StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context),
RawRetryList(Options.get("RetryMacros", "TEMP_FAILURE_RETRY")) {
StringRef(RawRetryList).split(RetryMacros, ",", -1, false);
}
namespace {
AST_MATCHER(BinaryOperator, isRHSATempFailureRetryArg) {
if (!Node.getBeginLoc().isMacroID())
return false;
const SourceManager &SM = Finder->getASTContext().getSourceManager();
if (!SM.isMacroArgExpansion(Node.getRHS()->IgnoreParenCasts()->getBeginLoc()))
return false;
const LangOptions &Opts = Finder->getASTContext().getLangOpts();
SourceLocation LocStart = Node.getBeginLoc();
while (LocStart.isMacroID()) {
SourceLocation Invocation = SM.getImmediateMacroCallerLoc(LocStart);
Token Tok;
if (!Lexer::getRawToken(SM.getSpellingLoc(Invocation), Tok, SM, Opts,
/*IgnoreWhiteSpace=*/true)) {
if (Tok.getKind() == tok::raw_identifier &&
Tok.getRawIdentifier() == "TEMP_FAILURE_RETRY")
return true;
}
void ComparisonInTempFailureRetryCheck::storeOptions(
ClangTidyOptions::OptionMap &Opts) {
Options.store(Opts, "RetryMacros", RawRetryList);
LocStart = Invocation;
}
return false;
}
} // namespace
void ComparisonInTempFailureRetryCheck::registerMatchers(MatchFinder *Finder) {
// Both glibc's and Bionic's TEMP_FAILURE_RETRY macros structurally look like:
......@@ -48,43 +63,15 @@ void ComparisonInTempFailureRetryCheck::registerMatchers(MatchFinder *Finder) {
Finder->addMatcher(
binaryOperator(hasOperatorName("="),
hasRHS(ignoringParenCasts(
binaryOperator(isComparisonOperator()).bind("inner"))))
.bind("outer"),
binaryOperator(isComparisonOperator()).bind("binop"))),
isRHSATempFailureRetryArg()),
this);
}
void ComparisonInTempFailureRetryCheck::check(
const MatchFinder::MatchResult &Result) {
StringRef RetryMacroName;
const auto &Node = *Result.Nodes.getNodeAs<BinaryOperator>("outer");
if (!Node.getBeginLoc().isMacroID())
return;
const SourceManager &SM = *Result.SourceManager;
if (!SM.isMacroArgExpansion(Node.getRHS()->IgnoreParenCasts()->getBeginLoc()))
return;
const LangOptions &Opts = Result.Context->getLangOpts();
SourceLocation LocStart = Node.getBeginLoc();
while (LocStart.isMacroID()) {
SourceLocation Invocation = SM.getImmediateMacroCallerLoc(LocStart);
Token Tok;
if (!Lexer::getRawToken(SM.getSpellingLoc(Invocation), Tok, SM, Opts,
/*IgnoreWhiteSpace=*/true)) {
if (Tok.getKind() == tok::raw_identifier &&
llvm::is_contained(RetryMacros, Tok.getRawIdentifier())) {
RetryMacroName = Tok.getRawIdentifier();
break;
}
}
LocStart = Invocation;
}
if (RetryMacroName.empty())
return;
const auto &Inner = *Result.Nodes.getNodeAs<BinaryOperator>("inner");
diag(Inner.getOperatorLoc(), "top-level comparison in %0") << RetryMacroName;
const auto &BinOp = *Result.Nodes.getNodeAs<BinaryOperator>("binop");
diag(BinOp.getOperatorLoc(), "top-level comparison in TEMP_FAILURE_RETRY");
// FIXME: FixIts would be nice, but potentially nontrivial when nested macros
// happen, e.g. `TEMP_FAILURE_RETRY(IS_ZERO(foo()))`
......
......@@ -10,9 +10,6 @@
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_ANDROID_COMPARISONINTEMPFAILURERETRYCHECK_H
#include "../ClangTidyCheck.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringRef.h"
#include <string>
namespace clang {
namespace tidy {
......@@ -25,14 +22,10 @@ namespace android {
/// TEMP_FAILURE_RETRY is a macro provided by both glibc and Bionic.
class ComparisonInTempFailureRetryCheck : public ClangTidyCheck {
public:
ComparisonInTempFailureRetryCheck(StringRef Name, ClangTidyContext *Context);
void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
ComparisonInTempFailureRetryCheck(StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context) {}
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
private:
const std::string RawRetryList;
SmallVector<StringRef, 5> RetryMacros;
};
} // namespace android
......
......@@ -30,8 +30,7 @@ static Preprocessor *PP;
void BadSignalToKillThreadCheck::check(const MatchFinder::MatchResult &Result) {
const auto IsSigterm = [](const auto &KeyValue) -> bool {
return KeyValue.first->getName() == "SIGTERM" &&
KeyValue.first->hasMacroDefinition();
return KeyValue.first->getName() == "SIGTERM";
};
const auto TryExpandAsInteger =
[](Preprocessor::macro_iterator It) -> Optional<unsigned> {
......@@ -39,8 +38,6 @@ void BadSignalToKillThreadCheck::check(const MatchFinder::MatchResult &Result) {
return llvm::None;
const MacroInfo *MI = PP->getMacroInfo(It->first);
const Token &T = MI->tokens().back();
if (!T.isLiteral() || !T.getLiteralData())
return llvm::None;
StringRef ValueStr = StringRef(T.getLiteralData(), T.getLength());
llvm::APInt IntValue;
......
......@@ -20,68 +20,53 @@ void BoolPointerImplicitConversionCheck::registerMatchers(MatchFinder *Finder) {
Finder->addMatcher(
traverse(
ast_type_traits::TK_AsIs,
ifStmt(
hasCondition(findAll(implicitCastExpr(
unless(hasParent(unaryOperator(hasOperatorName("!")))),
hasSourceExpression(expr(
hasType(pointerType(pointee(booleanType()))),
ignoringParenImpCasts(anyOf(declRefExpr().bind("expr"),
memberExpr().bind("expr"))))),
hasCastKind(CK_PointerToBoolean)))),
unless(isInTemplateInstantiation()))
ifStmt(hasCondition(findAll(implicitCastExpr(
unless(hasParent(unaryOperator(hasOperatorName("!")))),
hasSourceExpression(expr(
hasType(pointerType(pointee(booleanType()))),
ignoringParenImpCasts(declRefExpr().bind("expr")))),
hasCastKind(CK_PointerToBoolean)))),
unless(isInTemplateInstantiation()))
.bind("if")),
this);
}
static void checkImpl(const MatchFinder::MatchResult &Result, const Expr *Ref,
const IfStmt *If,
const ast_matchers::internal::Matcher<Expr> &RefMatcher,
ClangTidyCheck &Check) {
void BoolPointerImplicitConversionCheck::check(
const MatchFinder::MatchResult &Result) {
auto *If = Result.Nodes.getNodeAs<IfStmt>("if");
auto *Var = Result.Nodes.getNodeAs<DeclRefExpr>("expr");
// Ignore macros.
if (Ref->getBeginLoc().isMacroID())
if (Var->getBeginLoc().isMacroID())
return;
// Only allow variable accesses and member exprs for now, no function calls.
// Only allow variable accesses for now, no function calls or member exprs.
// Check that we don't dereference the variable anywhere within the if. This
// avoids false positives for checks of the pointer for nullptr before it is
// dereferenced. If there is a dereferencing operator on this variable don't
// emit a diagnostic. Also ignore array subscripts.
if (!match(findAll(unaryOperator(hasOperatorName("*"),
hasUnaryOperand(RefMatcher))),
const Decl *D = Var->getDecl();
auto DeclRef = ignoringParenImpCasts(declRefExpr(to(equalsNode(D))));
if (!match(findAll(
unaryOperator(hasOperatorName("*"), hasUnaryOperand(DeclRef))),
*If, *Result.Context)
.empty() ||
!match(findAll(arraySubscriptExpr(hasBase(RefMatcher))), *If,
!match(findAll(arraySubscriptExpr(hasBase(DeclRef))), *If,
*Result.Context)
.empty() ||
// FIXME: We should still warn if the paremater is implicitly converted to
// bool.
!match(
findAll(callExpr(hasAnyArgument(ignoringParenImpCasts(RefMatcher)))),
*If, *Result.Context)
!match(findAll(callExpr(hasAnyArgument(ignoringParenImpCasts(DeclRef)))),
*If, *Result.Context)
.empty() ||
!match(
findAll(cxxDeleteExpr(has(ignoringParenImpCasts(expr(RefMatcher))))),
*If, *Result.Context)
!match(findAll(cxxDeleteExpr(has(ignoringParenImpCasts(expr(DeclRef))))),
*If, *Result.Context)
.empty())
return;
Check.diag(Ref->getBeginLoc(),
"dubious check of 'bool *' against 'nullptr', did "
"you mean to dereference it?")
<< FixItHint::CreateInsertion(Ref->getBeginLoc(), "*");
}
void BoolPointerImplicitConversionCheck::check(
const MatchFinder::MatchResult &Result) {
const auto *If = Result.Nodes.getNodeAs<IfStmt>("if");
if (const auto *E = Result.Nodes.getNodeAs<Expr>("expr")) {
const Decl *D = isa<DeclRefExpr>(E) ? cast<DeclRefExpr>(E)->getDecl()
: cast<MemberExpr>(E)->getMemberDecl();
const auto M =
ignoringParenImpCasts(anyOf(declRefExpr(to(equalsNode(D))),
memberExpr(hasDeclaration(equalsNode(D)))));
checkImpl(Result, E, If, M, *this);
}
diag(Var->getBeginLoc(), "dubious check of 'bool *' against 'nullptr', did "
"you mean to dereference it?")
<< FixItHint::CreateInsertion(Var->getBeginLoc(), "*");
}
} // namespace bugprone
......
......@@ -38,7 +38,6 @@
#include "NotNullTerminatedResultCheck.h"
#include "ParentVirtualCallCheck.h"
#include "PosixReturnCheck.h"
#include "RedundantBranchConditionCheck.h"
#include "ReservedIdentifierCheck.h"
#include "SignedCharMisuseCheck.h"
#include "SizeofContainerCheck.h"
......@@ -120,8 +119,6 @@ public:
"bugprone-move-forwarding-reference");
CheckFactories.registerCheck<MultipleStatementMacroCheck>(
"bugprone-multiple-statement-macro");
CheckFactories.registerCheck<RedundantBranchConditionCheck>(
"bugprone-redundant-branch-condition");
CheckFactories.registerCheck<cppcoreguidelines::NarrowingConversionsCheck>(
"bugprone-narrowing-conversions");
CheckFactories.registerCheck<NoEscapeCheck>("bugprone-no-escape");
......
......@@ -33,7 +33,6 @@ add_clang_library(clangTidyBugproneModule
NotNullTerminatedResultCheck.cpp
ParentVirtualCallCheck.cpp
PosixReturnCheck.cpp
RedundantBranchConditionCheck.cpp
ReservedIdentifierCheck.cpp
SignedCharMisuseCheck.cpp
SizeofContainerCheck.cpp
......
......@@ -77,9 +77,9 @@ void MisplacedPointerArithmeticInAllocCheck::check(
CallName = "operator new[]";
} else {
const auto *CtrE = New->getConstructExpr();
if (!CtrE || !CtrE->getArg(CtrE->getNumArgs() - 1)
->getType()
->isIntegralOrEnumerationType())
if (!CtrE->getArg(CtrE->getNumArgs() - 1)
->getType()
->isIntegralOrEnumerationType())
return;
CallName = "operator new";
}
......
......@@ -802,16 +802,11 @@ void NotNullTerminatedResultCheck::check(
while (It != PP->macro_end() && !AreSafeFunctionsWanted.hasValue()) {
if (It->first->getName() == "__STDC_WANT_LIB_EXT1__") {
const auto *MI = PP->getMacroInfo(It->first);
// PP->getMacroInfo() returns nullptr if macro has no definition.
if (MI) {
const auto &T = MI->tokens().back();
if (T.isLiteral() && T.getLiteralData()) {
StringRef ValueStr = StringRef(T.getLiteralData(), T.getLength());
llvm::APInt IntValue;
ValueStr.getAsInteger(10, IntValue);
AreSafeFunctionsWanted = IntValue.getZExtValue();
}
}
const auto &T = MI->tokens().back();
StringRef ValueStr = StringRef(T.getLiteralData(), T.getLength());
llvm::APInt IntValue;
ValueStr.getAsInteger(10, IntValue);
AreSafeFunctionsWanted = IntValue.getZExtValue();
}
++It;
......
//===--- RedundantBranchConditionCheck.cpp - clang-tidy -------------------------===//
//
// 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 "RedundantBranchConditionCheck.h"
#include "../utils/Aliasing.h"
#include "clang/AST/ASTContext.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/Analysis/Analyses/ExprMutationAnalyzer.h"
#include "clang/Lex/Lexer.h"
using namespace clang::ast_matchers;
using clang::tidy::utils::hasPtrOrReferenceInFunc;
namespace clang {
namespace tidy {
namespace bugprone {
static const char CondVarStr[] = "cond_var";
static const char OuterIfStr[] = "outer_if";
static const char InnerIfStr[] = "inner_if";
static const char FuncStr[] = "func";
/// Returns whether `Var` is changed in `S` before `NextS`.
static bool isChangedBefore(const Stmt *S, const Stmt *NextS,
const VarDecl *Var, ASTContext *Context) {
ExprMutationAnalyzer MutAn(*S, *Context);
const auto &SM = Context->getSourceManager();
const Stmt *MutS = MutAn.findMutation(Var);
return MutS &&
SM.isBeforeInTranslationUnit(MutS->getEndLoc(), NextS->getBeginLoc());
}
void RedundantBranchConditionCheck::registerMatchers(MatchFinder *Finder) {
const auto ImmutableVar =
varDecl(anyOf(parmVarDecl(), hasLocalStorage()), hasType(isInteger()),
unless(hasType(isVolatileQualified())))
.bind(CondVarStr);
Finder->addMatcher(
ifStmt(
hasCondition(ignoringParenImpCasts(anyOf(
declRefExpr(hasDeclaration(ImmutableVar)),
binaryOperator(hasOperatorName("&&"),
hasEitherOperand(ignoringParenImpCasts(declRefExpr(
hasDeclaration(ImmutableVar)))))))),
hasThen(hasDescendant(
ifStmt(hasCondition(ignoringParenImpCasts(
anyOf(declRefExpr(hasDeclaration(
varDecl(equalsBoundNode(CondVarStr)))),
binaryOperator(
hasAnyOperatorName("&&", "||"),
hasEitherOperand(ignoringParenImpCasts(
declRefExpr(hasDeclaration(varDecl(
equalsBoundNode(CondVarStr)))))))))))
.bind(InnerIfStr))),
forFunction(functionDecl().bind(FuncStr)))
.bind(OuterIfStr),
this);
// FIXME: Handle longer conjunctive and disjunctive clauses.
}
void RedundantBranchConditionCheck::check(const MatchFinder::MatchResult &Result) {
const auto *OuterIf = Result.Nodes.getNodeAs<IfStmt>(OuterIfStr);
const auto *InnerIf = Result.Nodes.getNodeAs<IfStmt>(InnerIfStr);
const auto *CondVar = Result.Nodes.getNodeAs<VarDecl>(CondVarStr);
const auto *Func = Result.Nodes.getNodeAs<FunctionDecl>(FuncStr);
// If the variable has an alias then it can be changed by that alias as well.
// FIXME: could potentially support tracking pointers and references in the
// future to improve catching true positives through aliases.
if (hasPtrOrReferenceInFunc(Func, CondVar))
return;
if (isChangedBefore(OuterIf->getThen(), InnerIf, CondVar, Result.Context))
return;
auto Diag = diag(InnerIf->getBeginLoc(), "redundant condition %0") << CondVar;
// For standalone condition variables and for "or" binary operations we simply
// remove the inner `if`.
const auto *BinOpCond = dyn_cast<BinaryOperator>(InnerIf->getCond());
if (isa<DeclRefExpr>(InnerIf->getCond()->IgnoreParenImpCasts()) ||
(BinOpCond && BinOpCond->getOpcode() == BO_LOr)) {
SourceLocation IfBegin = InnerIf->getBeginLoc();
const Stmt *Body = InnerIf->getThen();
const Expr *OtherSide = nullptr;
if (BinOpCond) {
const auto *LeftDRE =
dyn_cast<DeclRefExpr>(BinOpCond->getLHS()->IgnoreParenImpCasts());
if (LeftDRE && LeftDRE->getDecl() == CondVar)
OtherSide = BinOpCond->getRHS();
else
OtherSide = BinOpCond->getLHS();
}
SourceLocation IfEnd = Body->getBeginLoc().getLocWithOffset(-1);
// For compound statements also remove the left brace.
if (isa<CompoundStmt>(Body))
IfEnd = Body->getBeginLoc();
// If the other side has side effects then keep it.
if (OtherSide && OtherSide->HasSideEffects(*Result.Context)) {
SourceLocation BeforeOtherSide =
OtherSide->getBeginLoc().getLocWithOffset(-1);
SourceLocation AfterOtherSide =
Lexer::findNextToken(OtherSide->getEndLoc(), *Result.SourceManager,
getLangOpts())
->getLocation();
Diag << FixItHint::CreateRemoval(
CharSourceRange::getTokenRange(IfBegin, BeforeOtherSide))
<< FixItHint::CreateInsertion(AfterOtherSide, ";")
<< FixItHint::CreateRemoval(
CharSourceRange::getTokenRange(AfterOtherSide, IfEnd));
} else {
Diag << FixItHint::CreateRemoval(
CharSourceRange::getTokenRange(IfBegin, IfEnd));
}
// For comound statements also remove the right brace at the end.
if (isa<CompoundStmt>(Body))
Diag << FixItHint::CreateRemoval(
CharSourceRange::getTokenRange(Body->getEndLoc(), Body->getEndLoc()));
// For "and" binary operations we remove the "and" operation with the
// condition variable from the inner if.
} else {
const auto *CondOp = cast<BinaryOperator>(InnerIf->getCond());
const auto *LeftDRE =
dyn_cast<DeclRefExpr>(CondOp->getLHS()->IgnoreParenImpCasts());
if (LeftDRE && LeftDRE->getDecl() == CondVar) {
SourceLocation BeforeRHS =
CondOp->getRHS()->getBeginLoc().getLocWithOffset(-1);
Diag << FixItHint::CreateRemoval(CharSourceRange::getTokenRange(
CondOp->getLHS()->getBeginLoc(), BeforeRHS));
} else {
SourceLocation AfterLHS =
Lexer::findNextToken(CondOp->getLHS()->getEndLoc(),
*Result.SourceManager, getLangOpts())
->getLocation();
Diag << FixItHint::CreateRemoval(CharSourceRange::getTokenRange(
AfterLHS, CondOp->getRHS()->getEndLoc()));
}
}
}
} // namespace bugprone
} // namespace tidy
} // namespace clang
//===--- RedundantBranchConditionCheck.h - clang-tidy -----------*- 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_CLANG_TIDY_BUGPRONE_REDUNDANTBRANCHCONDITIONCHECK_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_REDUNDANTBRANCHCONDITIONCHECK_H
#include "../ClangTidyCheck.h"
namespace clang {
namespace tidy {
namespace bugprone {
/// Finds condition variables in nested `if` statements that were also checked
/// in the outer `if` statement and were not changed.
///
/// For the user-facing documentation see:
/// http://clang.llvm.org/extra/clang-tidy/checks/bugprone-redundant-branch-condition.html
class RedundantBranchConditionCheck : public ClangTidyCheck {
public:
RedundantBranchConditionCheck(StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context) {}
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
};
} // namespace bugprone
} // namespace tidy
} // namespace clang
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_REDUNDANTBRANCHCONDITIONCHECK_H
/* This generated file is for internal use. Do not include it from headers. */
#ifdef CLANG_TIDY_CONFIG_H
#error clang-tidy-config.h can only be included once
#else
#define CLANG_TIDY_CONFIG_H
#cmakedefine01 CLANG_TIDY_ENABLE_STATIC_ANALYZER
#endif
......@@ -13,7 +13,6 @@ add_clang_library(clangTidyCppCoreGuidelinesModule
NarrowingConversionsCheck.cpp
NoMallocCheck.cpp
OwningMemoryCheck.cpp
PreferMemberInitializerCheck.cpp
ProBoundsArrayToPointerDecayCheck.cpp
ProBoundsConstantArrayIndexCheck.cpp
ProBoundsPointerArithmeticCheck.cpp
......
......@@ -22,7 +22,6 @@
#include "NarrowingConversionsCheck.h"
#include "NoMallocCheck.h"
#include "OwningMemoryCheck.h"
#include "PreferMemberInitializerCheck.h"
#include "ProBoundsArrayToPointerDecayCheck.h"
#include "ProBoundsConstantArrayIndexCheck.h"
#include "ProBoundsPointerArithmeticCheck.h"
......@@ -67,8 +66,6 @@ public:
"cppcoreguidelines-non-private-member-variables-in-classes");
CheckFactories.registerCheck<OwningMemoryCheck>(
"cppcoreguidelines-owning-memory");
CheckFactories.registerCheck<PreferMemberInitializerCheck>(
"cppcoreguidelines-prefer-member-initializer");
CheckFactories.registerCheck<ProBoundsArrayToPointerDecayCheck>(
"cppcoreguidelines-pro-bounds-array-to-pointer-decay");
CheckFactories.registerCheck<ProBoundsConstantArrayIndexCheck>(
......
......@@ -26,12 +26,12 @@ AST_MATCHER(VarDecl, isLocalVarDecl) { return Node.isLocalVarDecl(); }
InitVariablesCheck::InitVariablesCheck(StringRef Name,
ClangTidyContext *Context)
: ClangTidyCheck(Name, Context),
IncludeInserter(Options.getLocalOrGlobal("IncludeStyle",
utils::IncludeSorter::IS_LLVM)),
MathHeader(Options.get("MathHeader", "<math.h>")) {}
IncludeStyle(Options.getLocalOrGlobal("IncludeStyle",
utils::IncludeSorter::IS_LLVM)),
MathHeader(Options.get("MathHeader", "math.h")) {}
void InitVariablesCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
Options.store(Opts, "IncludeStyle", IncludeInserter.getStyle());
Options.store(Opts, "IncludeStyle", IncludeStyle);
Options.store(Opts, "MathHeader", MathHeader);
}
......@@ -51,7 +51,9 @@ void InitVariablesCheck::registerMatchers(MatchFinder *Finder) {
void InitVariablesCheck::registerPPCallbacks(const SourceManager &SM,
Preprocessor *PP,
Preprocessor *ModuleExpanderPP) {
IncludeInserter.registerPreprocessor(PP);
IncludeInserter =
std::make_unique<utils::IncludeInserter>(SM, getLangOpts(), IncludeStyle);
PP->addPPCallbacks(IncludeInserter->CreatePPCallbacks());
}
void InitVariablesCheck::check(const MatchFinder::MatchResult &Result) {
......@@ -102,8 +104,8 @@ void InitVariablesCheck::check(const MatchFinder::MatchResult &Result) {
MatchedDecl->getName().size()),
InitializationString);
if (AddMathInclude) {
Diagnostic << IncludeInserter.createIncludeInsertion(
Source.getFileID(MatchedDecl->getBeginLoc()), MathHeader);
Diagnostic << IncludeInserter->CreateIncludeInsertion(
Source.getFileID(MatchedDecl->getBeginLoc()), MathHeader, false);
}
}
}
......
......@@ -31,7 +31,8 @@ public:
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
private:
utils::IncludeInserter IncludeInserter;
std::unique_ptr<clang::tidy::utils::IncludeInserter> IncludeInserter;
const utils::IncludeSorter::IncludeStyle IncludeStyle;
const std::string MathHeader;
};
......
......@@ -79,8 +79,9 @@ static QualType getUnqualifiedType(const Expr &E) {
}
static APValue getConstantExprValue(const ASTContext &Ctx, const Expr &E) {
if (auto IntegerConstant = E.getIntegerConstantExpr(Ctx))
return APValue(*IntegerConstant);
llvm::APSInt IntegerConstant;
if (E.isIntegerConstantExpr(IntegerConstant, Ctx))
return APValue(IntegerConstant);
APValue Constant;
if (Ctx.getLangOpts().CPlusPlus && E.isCXX11ConstantExpr(Ctx, &Constant))
return Constant;
......
//===--- PreferMemberInitializerCheck.cpp - clang-tidy -------------------===//
//
// 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 "PreferMemberInitializerCheck.h"
#include "clang/AST/ASTContext.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/Lex/Lexer.h"
using namespace clang::ast_matchers;
namespace clang {
namespace tidy {
namespace cppcoreguidelines {
static bool isControlStatement(const Stmt *S) {
return isa<IfStmt, SwitchStmt, ForStmt, WhileStmt, DoStmt, ReturnStmt,
GotoStmt, CXXTryStmt, CXXThrowExpr>(S);
}
static bool isNoReturnCallStatement(const Stmt *S) {
const auto *Call = dyn_cast<CallExpr>(S);
if (!Call)
return false;
const FunctionDecl *Func = Call->getDirectCallee();
if (!Func)
return false;
return Func->isNoReturn();
}
static bool isLiteral(const Expr *E) {
return isa<StringLiteral, CharacterLiteral, IntegerLiteral, FloatingLiteral,
CXXBoolLiteralExpr, CXXNullPtrLiteralExpr>(E);
}
static bool isUnaryExprOfLiteral(const Expr *E) {
if (const auto *UnOp = dyn_cast<UnaryOperator>(E))
return isLiteral(UnOp->getSubExpr());
return false;
}
static bool shouldBeDefaultMemberInitializer(const Expr *Value) {
if (isLiteral(Value) || isUnaryExprOfLiteral(Value))
return true;
if (const auto *DRE = dyn_cast<DeclRefExpr>(Value))
return isa<EnumConstantDecl>(DRE->getDecl());
return false;
}
static const std::pair<const FieldDecl *, const Expr *>
isAssignmentToMemberOf(const RecordDecl *Rec, const Stmt *S) {
if (const auto *BO = dyn_cast<BinaryOperator>(S)) {
if (BO->getOpcode() != BO_Assign)
return std::make_pair(nullptr, nullptr);
const auto *ME = dyn_cast<MemberExpr>(BO->getLHS()->IgnoreParenImpCasts());
if (!ME)
return std::make_pair(nullptr, nullptr);
const auto *Field = dyn_cast<FieldDecl>(ME->getMemberDecl());
if (!Field)
return std::make_pair(nullptr, nullptr);
if (isa<CXXThisExpr>(ME->getBase()))
return std::make_pair(Field, BO->getRHS()->IgnoreParenImpCasts());
} else if (const auto *COCE = dyn_cast<CXXOperatorCallExpr>(S)) {
if (COCE->getOperator() != OO_Equal)
return std::make_pair(nullptr, nullptr);
const auto *ME =
dyn_cast<MemberExpr>(COCE->getArg(0)->IgnoreParenImpCasts());
if (!ME)
return std::make_pair(nullptr, nullptr);
const auto *Field = dyn_cast<FieldDecl>(ME->getMemberDecl());
if (!Field)
return std::make_pair(nullptr, nullptr);
if (isa<CXXThisExpr>(ME->getBase()))
return std::make_pair(Field, COCE->getArg(1)->IgnoreParenImpCasts());
}
return std::make_pair(nullptr, nullptr);
}
PreferMemberInitializerCheck::PreferMemberInitializerCheck(
StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context),
IsUseDefaultMemberInitEnabled(
Context->isCheckEnabled("modernize-use-default-member-init")),
UseAssignment(OptionsView("modernize-use-default-member-init",
Context->getOptions().CheckOptions)
.get("UseAssignment", false)) {}
void PreferMemberInitializerCheck::storeOptions(
ClangTidyOptions::OptionMap &Opts) {
Options.store(Opts, "UseAssignment", UseAssignment);
}
void PreferMemberInitializerCheck::registerMatchers(MatchFinder *Finder) {
Finder->addMatcher(
cxxConstructorDecl(hasBody(compoundStmt()), unless(isInstantiated()))
.bind("ctor"),
this);
}
void PreferMemberInitializerCheck::check(
const MatchFinder::MatchResult &Result) {
const auto *Ctor = Result.Nodes.getNodeAs<CXXConstructorDecl>("ctor");
const auto *Body = cast<CompoundStmt>(Ctor->getBody());
const CXXRecordDecl *Class = Ctor->getParent();
SourceLocation InsertPos;
bool FirstToCtorInits = true;
for (const Stmt *S : Body->body()) {
if (S->getBeginLoc().isMacroID()) {
StringRef MacroName =
Lexer::getImmediateMacroName(S->getBeginLoc(), *Result.SourceManager,
getLangOpts());
if (MacroName.contains_lower("assert"))
return;
}
if (isControlStatement(S))
return;
if (isNoReturnCallStatement(S))
return;
if (const auto *CondOp = dyn_cast<ConditionalOperator>(S)) {
if (isNoReturnCallStatement(CondOp->getLHS()) ||
isNoReturnCallStatement(CondOp->getRHS()))
return;
}
const FieldDecl *Field;
const Expr *InitValue;
std::tie(Field, InitValue) = isAssignmentToMemberOf(Class, S);
if (Field) {
if (IsUseDefaultMemberInitEnabled && getLangOpts().CPlusPlus11 &&
Ctor->isDefaultConstructor() &&
(getLangOpts().CPlusPlus20 || !Field->isBitField()) &&
(!isa<RecordDecl>(Class->getDeclContext()) ||
!cast<RecordDecl>(Class->getDeclContext())->isUnion()) &&
shouldBeDefaultMemberInitializer(InitValue)) {
auto Diag =
diag(S->getBeginLoc(), "%0 should be initialized in an in-class"
" default member initializer")
<< Field;
SourceLocation FieldEnd =
Lexer::getLocForEndOfToken(Field->getSourceRange().getEnd(), 0,
*Result.SourceManager, getLangOpts());
Diag << FixItHint::CreateInsertion(FieldEnd,
UseAssignment ? " = " : "{")
<< FixItHint::CreateInsertionFromRange(
FieldEnd,
CharSourceRange(InitValue->getSourceRange(), true))
<< FixItHint::CreateInsertion(FieldEnd, UseAssignment ? "" : "}");
SourceLocation SemiColonEnd =
Lexer::findNextToken(S->getEndLoc(), *Result.SourceManager,
getLangOpts())
->getEndLoc();
CharSourceRange StmtRange =
CharSourceRange::getCharRange(S->getBeginLoc(), SemiColonEnd);
Diag << FixItHint::CreateRemoval(StmtRange);
} else {
auto Diag =
diag(S->getBeginLoc(), "%0 should be initialized in a member"
" initializer of the constructor")
<< Field;
bool AddComma = false;
if (!Ctor->getNumCtorInitializers() && FirstToCtorInits) {
SourceLocation BodyPos = Ctor->getBody()->getBeginLoc();
SourceLocation NextPos = Ctor->getBeginLoc();
do {
InsertPos = NextPos;
NextPos = Lexer::findNextToken(NextPos, *Result.SourceManager,
getLangOpts())
->getLocation();
} while (NextPos != BodyPos);
InsertPos = Lexer::getLocForEndOfToken(
InsertPos, 0, *Result.SourceManager, getLangOpts());
Diag << FixItHint::CreateInsertion(InsertPos, " : ");
} else {
bool Found = false;
for (const auto *Init : Ctor->inits()) {
if (Init->isMemberInitializer()) {
if (Result.SourceManager->isBeforeInTranslationUnit(
Field->getLocation(), Init->getMember()->getLocation())) {
InsertPos = Init->getSourceLocation();
Found = true;
break;
}
}
}
if (!Found) {
if (Ctor->getNumCtorInitializers()) {
InsertPos = Lexer::getLocForEndOfToken(
(*Ctor->init_rbegin())->getSourceRange().getEnd(), 0,
*Result.SourceManager, getLangOpts());
}
Diag << FixItHint::CreateInsertion(InsertPos, ", ");
} else {
AddComma = true;
}
}
Diag << FixItHint::CreateInsertion(InsertPos, Field->getName())
<< FixItHint::CreateInsertion(InsertPos, "(")
<< FixItHint::CreateInsertionFromRange(
InsertPos,
CharSourceRange(InitValue->getSourceRange(), true))
<< FixItHint::CreateInsertion(InsertPos, ")");
if (AddComma)
Diag << FixItHint::CreateInsertion(InsertPos, ", ");
SourceLocation SemiColonEnd =
Lexer::findNextToken(S->getEndLoc(), *Result.SourceManager,
getLangOpts())
->getEndLoc();
CharSourceRange StmtRange =
CharSourceRange::getCharRange(S->getBeginLoc(), SemiColonEnd);
Diag << FixItHint::CreateRemoval(StmtRange);
FirstToCtorInits = false;
}
}
}
}
} // namespace cppcoreguidelines
} // namespace tidy
} // namespace clang
//===--- PreferMemberInitializerCheck.h - clang-tidy ------------*- 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_CLANG_TIDY_CPPCOREGUIDELINES_PREFERMEMBERINITIALIZERCHECK_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_PREFERMEMBERINITIALIZERCHECK_H
#include "../ClangTidyCheck.h"
namespace clang {
namespace tidy {
namespace cppcoreguidelines {
/// Finds member initializations in the constructor body which can be placed
/// into the initialization list instead.
///
/// For the user-facing documentation see:
/// http://clang.llvm.org/extra/clang-tidy/checks/cppcoreguidelines-prefer-member-initializer.html
class PreferMemberInitializerCheck : public ClangTidyCheck {
public:
PreferMemberInitializerCheck(StringRef Name, ClangTidyContext *Context);
bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
return LangOpts.CPlusPlus;
}
void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
const bool IsUseDefaultMemberInitEnabled;
const bool UseAssignment;
};
} // namespace cppcoreguidelines
} // namespace tidy
} // namespace clang
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_PREFERMEMBERINITIALIZERCHECK_H
......@@ -21,18 +21,20 @@ namespace cppcoreguidelines {
ProBoundsConstantArrayIndexCheck::ProBoundsConstantArrayIndexCheck(
StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context), GslHeader(Options.get("GslHeader", "")),
Inserter(Options.getLocalOrGlobal("IncludeStyle",
utils::IncludeSorter::IS_LLVM)) {}
IncludeStyle(Options.getLocalOrGlobal("IncludeStyle",
utils::IncludeSorter::IS_LLVM)) {}
void ProBoundsConstantArrayIndexCheck::storeOptions(
ClangTidyOptions::OptionMap &Opts) {
Options.store(Opts, "GslHeader", GslHeader);
Options.store(Opts, "IncludeStyle", Inserter.getStyle());
Options.store(Opts, "IncludeStyle", IncludeStyle);
}
void ProBoundsConstantArrayIndexCheck::registerPPCallbacks(
const SourceManager &SM, Preprocessor *PP, Preprocessor *ModuleExpanderPP) {
Inserter.registerPreprocessor(PP);
Inserter = std::make_unique<utils::IncludeInserter>(SM, getLangOpts(),
IncludeStyle);
PP->addPPCallbacks(Inserter->CreatePPCallbacks());
}
void ProBoundsConstantArrayIndexCheck::registerMatchers(MatchFinder *Finder) {
......@@ -63,9 +65,9 @@ void ProBoundsConstantArrayIndexCheck::check(
if (IndexExpr->isValueDependent())
return; // We check in the specialization.
Optional<llvm::APSInt> Index =
IndexExpr->getIntegerConstantExpr(*Result.Context);
if (!Index) {
llvm::APSInt Index;
if (!IndexExpr->isIntegerConstantExpr(Index, *Result.Context, nullptr,
/*isEvaluated=*/true)) {
SourceRange BaseRange;
if (const auto *ArraySubscriptE = dyn_cast<ArraySubscriptExpr>(Matched))
BaseRange = ArraySubscriptE->getBase()->getSourceRange();
......@@ -85,7 +87,9 @@ void ProBoundsConstantArrayIndexCheck::check(
IndexRange.getBegin().getLocWithOffset(-1)),
", ")
<< FixItHint::CreateReplacement(Matched->getEndLoc(), ")")
<< Inserter.createMainFileIncludeInsertion(GslHeader);
<< Inserter->CreateIncludeInsertion(
Result.SourceManager->getMainFileID(), GslHeader,
/*IsAngled=*/false);
}
return;
}
......@@ -97,9 +101,9 @@ void ProBoundsConstantArrayIndexCheck::check(
if (!StdArrayDecl)
return;
if (Index->isSigned() && Index->isNegative()) {
if (Index.isSigned() && Index.isNegative()) {
diag(Matched->getExprLoc(), "std::array<> index %0 is negative")
<< Index->toString(10);
<< Index.toString(10);
return;
}
......@@ -114,11 +118,11 @@ void ProBoundsConstantArrayIndexCheck::check(
// Get uint64_t values, because different bitwidths would lead to an assertion
// in APInt::uge.
if (Index->getZExtValue() >= ArraySize.getZExtValue()) {
if (Index.getZExtValue() >= ArraySize.getZExtValue()) {
diag(Matched->getExprLoc(),
"std::array<> index %0 is past the end of the array "
"(which contains %1 elements)")
<< Index->toString(10) << ArraySize.toString(10, false);
<< Index.toString(10) << ArraySize.toString(10, false);
}
}
......
......@@ -23,7 +23,8 @@ namespace cppcoreguidelines {
/// http://clang.llvm.org/extra/clang-tidy/checks/cppcoreguidelines-pro-bounds-constant-array-index.html
class ProBoundsConstantArrayIndexCheck : public ClangTidyCheck {
const std::string GslHeader;
utils::IncludeInserter Inserter;
const utils::IncludeSorter::IncludeStyle IncludeStyle;
std::unique_ptr<utils::IncludeInserter> Inserter;
public:
ProBoundsConstantArrayIndexCheck(StringRef Name, ClangTidyContext *Context);
......
......@@ -500,13 +500,7 @@ static bool retrieveIntegerConstantExpr(const MatchFinder::MatchResult &Result,
const Expr *&ConstExpr) {
std::string CstId = (Id + "-const").str();
ConstExpr = Result.Nodes.getNodeAs<Expr>(CstId);
if (!ConstExpr)
return false;
Optional<llvm::APSInt> R = ConstExpr->getIntegerConstantExpr(*Result.Context);
if (!R)
return false;
Value = *R;
return true;
return ConstExpr && ConstExpr->isIntegerConstantExpr(Value, *Result.Context);
}
// Overloaded `retrieveIntegerConstantExpr` for compatibility.
......@@ -679,7 +673,7 @@ static bool retrieveRelationalIntegerConstantExpr(
if (const auto *Arg = OverloadedOperatorExpr->getArg(1)) {
if (!Arg->isValueDependent() &&
!Arg->isIntegerConstantExpr(*Result.Context))
!Arg->isIntegerConstantExpr(Value, *Result.Context))
return false;
}
Symbol = OverloadedOperatorExpr->getArg(0);
......@@ -1271,23 +1265,21 @@ void RedundantExpressionCheck::check(const MatchFinder::MatchResult &Result) {
"left-right-shift-confusion")) {
const auto *ShiftingConst = Result.Nodes.getNodeAs<Expr>("shift-const");
assert(ShiftingConst && "Expr* 'ShiftingConst' is nullptr!");
Optional<llvm::APSInt> ShiftingValue =
ShiftingConst->getIntegerConstantExpr(*Result.Context);
APSInt ShiftingValue;
if (!ShiftingValue)
if (!ShiftingConst->isIntegerConstantExpr(ShiftingValue, *Result.Context))
return;
const auto *AndConst = Result.Nodes.getNodeAs<Expr>("and-const");
assert(AndConst && "Expr* 'AndCont' is nullptr!");
Optional<llvm::APSInt> AndValue =
AndConst->getIntegerConstantExpr(*Result.Context);
if (!AndValue)
APSInt AndValue;
if (!AndConst->isIntegerConstantExpr(AndValue, *Result.Context))
return;
// If ShiftingConst is shifted left with more bits than the position of the
// leftmost 1 in the bit representation of AndValue, AndConstant is
// ineffective.
if (AndValue->getActiveBits() > *ShiftingValue)
if (AndValue.getActiveBits() > ShiftingValue)
return;
auto Diag = diag(BinaryAndExpr->getOperatorLoc(),
......
......@@ -438,12 +438,11 @@ static bool arrayMatchesBoundExpr(ASTContext *Context,
Context->getAsConstantArrayType(ArrayType);
if (!ConstType)
return false;
Optional<llvm::APSInt> ConditionSize =
ConditionExpr->getIntegerConstantExpr(*Context);
if (!ConditionSize)
llvm::APSInt ConditionSize;
if (!ConditionExpr->isIntegerConstantExpr(ConditionSize, *Context))
return false;
llvm::APSInt ArraySize(ConstType->getSize());
return llvm::APSInt::isSameValue(*ConditionSize, ArraySize);
return llvm::APSInt::isSameValue(ConditionSize, ArraySize);
}
ForLoopIndexUseVisitor::ForLoopIndexUseVisitor(ASTContext *Context,
......
......@@ -19,6 +19,7 @@ namespace modernize {
namespace {
constexpr char StdMemoryHeader[] = "memory";
constexpr char ConstructorCall[] = "constructorCall";
constexpr char ResetCall[] = "resetCall";
constexpr char NewExpression[] = "newExpression";
......@@ -43,16 +44,16 @@ const char MakeSmartPtrCheck::PointerType[] = "pointerType";
MakeSmartPtrCheck::MakeSmartPtrCheck(StringRef Name, ClangTidyContext *Context,
StringRef MakeSmartPtrFunctionName)
: ClangTidyCheck(Name, Context),
Inserter(Options.getLocalOrGlobal("IncludeStyle",
utils::IncludeSorter::IS_LLVM)),
IncludeStyle(Options.getLocalOrGlobal("IncludeStyle",
utils::IncludeSorter::IS_LLVM)),
MakeSmartPtrFunctionHeader(
Options.get("MakeSmartPtrFunctionHeader", "<memory>")),
Options.get("MakeSmartPtrFunctionHeader", StdMemoryHeader)),
MakeSmartPtrFunctionName(
Options.get("MakeSmartPtrFunction", MakeSmartPtrFunctionName)),
IgnoreMacros(Options.getLocalOrGlobal("IgnoreMacros", true)) {}
void MakeSmartPtrCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
Options.store(Opts, "IncludeStyle", Inserter.getStyle());
Options.store(Opts, "IncludeStyle", IncludeStyle);
Options.store(Opts, "MakeSmartPtrFunctionHeader", MakeSmartPtrFunctionHeader);
Options.store(Opts, "MakeSmartPtrFunction", MakeSmartPtrFunctionName);
Options.store(Opts, "IgnoreMacros", IgnoreMacros);
......@@ -66,7 +67,9 @@ bool MakeSmartPtrCheck::isLanguageVersionSupported(
void MakeSmartPtrCheck::registerPPCallbacks(const SourceManager &SM,
Preprocessor *PP,
Preprocessor *ModuleExpanderPP) {
Inserter.registerPreprocessor(PP);
Inserter = std::make_unique<utils::IncludeInserter>(SM, getLangOpts(),
IncludeStyle);
PP->addPPCallbacks(Inserter->CreatePPCallbacks());
}
void MakeSmartPtrCheck::registerMatchers(ast_matchers::MatchFinder *Finder) {
......@@ -429,7 +432,9 @@ void MakeSmartPtrCheck::insertHeader(DiagnosticBuilder &Diag, FileID FD) {
if (MakeSmartPtrFunctionHeader.empty()) {
return;
}
Diag << Inserter.createIncludeInsertion(FD, MakeSmartPtrFunctionHeader);
Diag << Inserter->CreateIncludeInsertion(
FD, MakeSmartPtrFunctionHeader,
/*IsAngled=*/MakeSmartPtrFunctionHeader == StdMemoryHeader);
}
} // namespace modernize
......
......@@ -46,7 +46,8 @@ protected:
static const char PointerType[];
private:
utils::IncludeInserter Inserter;
std::unique_ptr<utils::IncludeInserter> Inserter;
const utils::IncludeSorter::IncludeStyle IncludeStyle;
const std::string MakeSmartPtrFunctionHeader;
const std::string MakeSmartPtrFunctionName;
const bool IgnoreMacros;
......
......@@ -120,12 +120,12 @@ collectParamDecls(const CXXConstructorDecl *Ctor,
PassByValueCheck::PassByValueCheck(StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context),
Inserter(Options.getLocalOrGlobal("IncludeStyle",
utils::IncludeSorter::IS_LLVM)),
IncludeStyle(Options.getLocalOrGlobal("IncludeStyle",
utils::IncludeSorter::IS_LLVM)),
ValuesOnly(Options.get("ValuesOnly", false)) {}
void PassByValueCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
Options.store(Opts, "IncludeStyle", Inserter.getStyle());
Options.store(Opts, "IncludeStyle", IncludeStyle);
Options.store(Opts, "ValuesOnly", ValuesOnly);
}
......@@ -167,7 +167,9 @@ void PassByValueCheck::registerMatchers(MatchFinder *Finder) {
void PassByValueCheck::registerPPCallbacks(const SourceManager &SM,
Preprocessor *PP,
Preprocessor *ModuleExpanderPP) {
Inserter.registerPreprocessor(PP);
Inserter = std::make_unique<utils::IncludeInserter>(SM, getLangOpts(),
IncludeStyle);
PP->addPPCallbacks(Inserter->CreatePPCallbacks());
}
void PassByValueCheck::check(const MatchFinder::MatchResult &Result) {
......@@ -214,9 +216,10 @@ void PassByValueCheck::check(const MatchFinder::MatchResult &Result) {
Diag << FixItHint::CreateInsertion(Initializer->getRParenLoc(), ")")
<< FixItHint::CreateInsertion(
Initializer->getLParenLoc().getLocWithOffset(1), "std::move(")
<< Inserter.createIncludeInsertion(
<< Inserter->CreateIncludeInsertion(
Result.SourceManager->getFileID(Initializer->getSourceLocation()),
"<utility>");
"utility",
/*IsAngled=*/true);
}
} // namespace modernize
......
......@@ -31,7 +31,8 @@ public:
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
private:
utils::IncludeInserter Inserter;
std::unique_ptr<utils::IncludeInserter> Inserter;
const utils::IncludeSorter::IncludeStyle IncludeStyle;
const bool ValuesOnly;
};
......
......@@ -74,11 +74,11 @@ AST_MATCHER(Decl, isFromStdNamespace) {
ReplaceAutoPtrCheck::ReplaceAutoPtrCheck(StringRef Name,
ClangTidyContext *Context)
: ClangTidyCheck(Name, Context),
Inserter(Options.getLocalOrGlobal("IncludeStyle",
utils::IncludeSorter::IS_LLVM)) {}
IncludeStyle(Options.getLocalOrGlobal("IncludeStyle",
utils::IncludeSorter::IS_LLVM)) {}
void ReplaceAutoPtrCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
Options.store(Opts, "IncludeStyle", Inserter.getStyle());
Options.store(Opts, "IncludeStyle", IncludeStyle);
}
void ReplaceAutoPtrCheck::registerMatchers(MatchFinder *Finder) {
......@@ -131,7 +131,9 @@ void ReplaceAutoPtrCheck::registerMatchers(MatchFinder *Finder) {
void ReplaceAutoPtrCheck::registerPPCallbacks(const SourceManager &SM,
Preprocessor *PP,
Preprocessor *ModuleExpanderPP) {
Inserter.registerPreprocessor(PP);
Inserter = std::make_unique<utils::IncludeInserter>(SM, getLangOpts(),
IncludeStyle);
PP->addPPCallbacks(Inserter->CreatePPCallbacks());
}
void ReplaceAutoPtrCheck::check(const MatchFinder::MatchResult &Result) {
......@@ -144,10 +146,12 @@ void ReplaceAutoPtrCheck::check(const MatchFinder::MatchResult &Result) {
if (Range.isInvalid())
return;
auto Diag = diag(Range.getBegin(), "use std::move to transfer ownership")
<< FixItHint::CreateInsertion(Range.getBegin(), "std::move(")
<< FixItHint::CreateInsertion(Range.getEnd(), ")")
<< Inserter.createMainFileIncludeInsertion("<utility>");
auto Diag =
diag(Range.getBegin(), "use std::move to transfer ownership")
<< FixItHint::CreateInsertion(Range.getBegin(), "std::move(")
<< FixItHint::CreateInsertion(Range.getEnd(), ")")
<< Inserter->CreateIncludeInsertion(SM.getMainFileID(), "utility",
/*IsAngled=*/true);
return;
}
......
......@@ -53,7 +53,8 @@ public:
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
private:
utils::IncludeInserter Inserter;
std::unique_ptr<utils::IncludeInserter> Inserter;
const utils::IncludeSorter::IncludeStyle IncludeStyle;
};
} // namespace modernize
......
......@@ -23,9 +23,8 @@ namespace modernize {
ReplaceRandomShuffleCheck::ReplaceRandomShuffleCheck(StringRef Name,
ClangTidyContext *Context)
: ClangTidyCheck(Name, Context),
IncludeInserter(Options.getLocalOrGlobal("IncludeStyle",
utils::IncludeSorter::IS_LLVM)) {
}
IncludeStyle(Options.getLocalOrGlobal("IncludeStyle",
utils::IncludeSorter::IS_LLVM)) {}
void ReplaceRandomShuffleCheck::registerMatchers(MatchFinder *Finder) {
const auto Begin = hasArgument(0, expr());
......@@ -45,12 +44,14 @@ void ReplaceRandomShuffleCheck::registerMatchers(MatchFinder *Finder) {
void ReplaceRandomShuffleCheck::registerPPCallbacks(
const SourceManager &SM, Preprocessor *PP, Preprocessor *ModuleExpanderPP) {
IncludeInserter.registerPreprocessor(PP);
IncludeInserter = std::make_unique<utils::IncludeInserter>(SM, getLangOpts(),
IncludeStyle);
PP->addPPCallbacks(IncludeInserter->CreatePPCallbacks());
}
void ReplaceRandomShuffleCheck::storeOptions(
ClangTidyOptions::OptionMap &Opts) {
Options.store(Opts, "IncludeStyle", IncludeInserter.getStyle());
Options.store(Opts, "IncludeStyle", IncludeStyle);
}
void ReplaceRandomShuffleCheck::check(const MatchFinder::MatchResult &Result) {
......@@ -91,10 +92,10 @@ void ReplaceRandomShuffleCheck::check(const MatchFinder::MatchResult &Result) {
Diag << FixItHint::CreateRemoval(MatchedDecl->getSourceRange());
Diag << FixItHint::CreateInsertion(MatchedDecl->getBeginLoc(), NewName);
Diag << IncludeInserter.createIncludeInsertion(
Diag << IncludeInserter->CreateIncludeInsertion(
Result.Context->getSourceManager().getFileID(
MatchedCallExpr->getBeginLoc()),
"<random>");
"random", /*IsAngled=*/true);
}
} // namespace modernize
......
......@@ -34,7 +34,8 @@ public:
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
private:
utils::IncludeInserter IncludeInserter;
std::unique_ptr<utils::IncludeInserter> IncludeInserter;
const utils::IncludeSorter::IncludeStyle IncludeStyle;
};
} // namespace modernize
......
......@@ -119,11 +119,16 @@ AST_MATCHER_P(QualType, isSugarFor, Matcher<QualType>, SugarMatcher) {
/// \endcode
///
/// namedDecl(hasStdIteratorName()) matches \c I and \c CI.
Matcher<NamedDecl> hasStdIteratorName() {
static const StringRef IteratorNames[] = {"iterator", "reverse_iterator",
"const_iterator",
"const_reverse_iterator"};
return hasAnyName(IteratorNames);
AST_MATCHER(NamedDecl, hasStdIteratorName) {
static const char *const IteratorNames[] = {"iterator", "reverse_iterator",
"const_iterator",
"const_reverse_iterator"};
for (const char *Name : IteratorNames) {
if (hasName(Name).matches(Node, Finder, Builder))
return true;
}
return false;
}
/// Matches named declarations that have one of the standard container
......@@ -138,21 +143,26 @@ Matcher<NamedDecl> hasStdIteratorName() {
///
/// recordDecl(hasStdContainerName()) matches \c vector and \c forward_list
/// but not \c my_vec.
Matcher<NamedDecl> hasStdContainerName() {
static StringRef ContainerNames[] = {"array", "deque",
"forward_list", "list",
"vector",
AST_MATCHER(NamedDecl, hasStdContainerName) {
static const char *const ContainerNames[] = {
"array", "deque",
"forward_list", "list",
"vector",
"map", "multimap",
"set", "multiset",
"map", "multimap",
"set", "multiset",
"unordered_map", "unordered_multimap",
"unordered_set", "unordered_multiset",
"unordered_map", "unordered_multimap",
"unordered_set", "unordered_multiset",
"queue", "priority_queue",
"stack"};
"queue", "priority_queue",
"stack"};
return hasAnyName(ContainerNames);
for (const char *Name : ContainerNames) {
if (hasName(Name).matches(Node, Finder, Builder))
return true;
}
return false;
}
/// Matches declarations whose declaration context is the C++ standard library
......@@ -338,7 +348,7 @@ void UseAutoCheck::replaceIterators(const DeclStmt *D, ASTContext *Context) {
// Drill down to the as-written initializer.
const Expr *E = (*Construct->arg_begin())->IgnoreParenImpCasts();
if (E != E->IgnoreConversionOperatorSingleStep()) {
if (E != E->IgnoreConversionOperator()) {
// We hit a conversion operator. Early-out now as they imply an implicit
// conversion from a different type. Could also mean an explicit
// conversion from the same type but that's pretty rare.
......
......@@ -36,12 +36,12 @@ void UseEqualsDeleteCheck::registerMatchers(MatchFinder *Finder) {
Finder->addMatcher(
cxxMethodDecl(
PrivateSpecialFn,
unless(anyOf(hasAnyBody(stmt()), isDefaulted(), isDeleted(),
unless(anyOf(hasBody(stmt()), isDefaulted(), isDeleted(),
ast_matchers::isTemplateInstantiation(),
// Ensure that all methods except private special member
// functions are defined.
hasParent(cxxRecordDecl(hasMethod(unless(
anyOf(PrivateSpecialFn, hasAnyBody(stmt()), isPure(),
anyOf(PrivateSpecialFn, hasBody(stmt()), isPure(),
isDefaulted(), isDeleted()))))))))
.bind(SpecialFunction),
this);
......
......@@ -77,16 +77,13 @@ void UseNoexceptCheck::check(const MatchFinder::MatchResult &Result) {
.getExceptionSpecRange();
}
assert(FnTy && "FunctionProtoType is null.");
if (isUnresolvedExceptionSpec(FnTy->getExceptionSpecType()))
return;
assert(Range.isValid() && "Exception Source Range is invalid.");
CharSourceRange CRange = Lexer::makeFileCharRange(
CharSourceRange::getTokenRange(Range), *Result.SourceManager,
Result.Context->getLangOpts());
assert(FnTy && "FunctionProtoType is null.");
bool IsNoThrow = FnTy->isNothrow();
StringRef ReplacementStr =
IsNoThrow
......
......@@ -66,11 +66,6 @@ public:
->getName()))
return false;
break;
case TypeLoc::Typedef:
if (VisitUnqualName(
TL.getAs<TypedefTypeLoc>().getTypePtr()->getDecl()->getName()))
return false;
break;
default:
break;
}
......@@ -266,8 +261,8 @@ static bool hasAnyNestedLocalQualifiers(QualType Type) {
}
SourceRange UseTrailingReturnTypeCheck::findReturnTypeAndCVSourceRange(
const FunctionDecl &F, const TypeLoc &ReturnLoc, const ASTContext &Ctx,
const SourceManager &SM, const LangOptions &LangOpts) {
const FunctionDecl &F, const ASTContext &Ctx, const SourceManager &SM,
const LangOptions &LangOpts) {
// We start with the range of the return type and expand to neighboring
// qualifiers (const, volatile and restrict).
......@@ -279,35 +274,6 @@ SourceRange UseTrailingReturnTypeCheck::findReturnTypeAndCVSourceRange(
return {};
}
// If the return type is a constrained 'auto' or 'decltype(auto)', we need to
// include the tokens after the concept. Unfortunately, the source range of an
// AutoTypeLoc, if it is constrained, does not include the 'auto' or
// 'decltype(auto)'. If the return type is a plain 'decltype(...)', the
// source range only contains the first 'decltype' token.
auto ATL = ReturnLoc.getAs<AutoTypeLoc>();
if ((ATL && (ATL.isConstrained() ||
ATL.getAutoKeyword() == AutoTypeKeyword::DecltypeAuto)) ||
ReturnLoc.getAs<DecltypeTypeLoc>()) {
SourceLocation End =
expandIfMacroId(ReturnLoc.getSourceRange().getEnd(), SM);
SourceLocation BeginNameF = expandIfMacroId(F.getLocation(), SM);
// Extend the ReturnTypeRange until the last token before the function
// name.
std::pair<FileID, unsigned> Loc = SM.getDecomposedLoc(End);
StringRef File = SM.getBufferData(Loc.first);
const char *TokenBegin = File.data() + Loc.second;
Lexer Lexer(SM.getLocForStartOfFile(Loc.first), LangOpts, File.begin(),
TokenBegin, File.end());
Token T;
SourceLocation LastTLoc = End;
while (!Lexer.LexFromRawLexer(T) &&
SM.isBeforeInTranslationUnit(T.getLocation(), BeginNameF)) {
LastTLoc = T.getLocation();
}
ReturnTypeRange.setEnd(LastTLoc);
}
// If the return type has no local qualifiers, it's source range is accurate.
if (!hasAnyNestedLocalQualifiers(F.getReturnType()))
return ReturnTypeRange;
......@@ -351,7 +317,7 @@ SourceRange UseTrailingReturnTypeCheck::findReturnTypeAndCVSourceRange(
return ReturnTypeRange;
}
void UseTrailingReturnTypeCheck::keepSpecifiers(
bool UseTrailingReturnTypeCheck::keepSpecifiers(
std::string &ReturnType, std::string &Auto, SourceRange ReturnTypeCVRange,
const FunctionDecl &F, const FriendDecl *Fr, const ASTContext &Ctx,
const SourceManager &SM, const LangOptions &LangOpts) {
......@@ -361,14 +327,14 @@ void UseTrailingReturnTypeCheck::keepSpecifiers(
if (!F.isConstexpr() && !F.isInlineSpecified() &&
F.getStorageClass() != SC_Extern && F.getStorageClass() != SC_Static &&
!Fr && !(M && M->isVirtualAsWritten()))
return;
return true;
// Tokenize return type. If it contains macros which contain a mix of
// qualifiers, specifiers and types, give up.
llvm::Optional<SmallVector<ClassifiedToken, 8>> MaybeTokens =
classifyTokensBeforeFunctionName(F, Ctx, SM, LangOpts);
if (!MaybeTokens)
return;
return false;
// Find specifiers, remove them from the return type, add them to 'auto'.
unsigned int ReturnTypeBeginOffset =
......@@ -401,12 +367,14 @@ void UseTrailingReturnTypeCheck::keepSpecifiers(
ReturnType.erase(TOffsetInRT, TLengthWithWS);
DeletedChars += TLengthWithWS;
}
return true;
}
void UseTrailingReturnTypeCheck::registerMatchers(MatchFinder *Finder) {
auto F = functionDecl(
unless(anyOf(hasTrailingReturn(), returns(voidType()),
cxxConversionDecl(), cxxMethodDecl(isImplicit()))))
auto F = functionDecl(unless(anyOf(hasTrailingReturn(), returns(voidType()),
returns(autoType()), cxxConversionDecl(),
cxxMethodDecl(isImplicit()))))
.bind("Func");
Finder->addMatcher(F, this);
......@@ -429,17 +397,11 @@ void UseTrailingReturnTypeCheck::check(const MatchFinder::MatchResult &Result) {
if (F->getLocation().isInvalid())
return;
// Skip functions which return just 'auto'.
const auto *AT = F->getDeclaredReturnType()->getAs<AutoType>();
if (AT != nullptr && !AT->isConstrained() &&
AT->getKeyword() == AutoTypeKeyword::Auto &&
!hasAnyNestedLocalQualifiers(F->getDeclaredReturnType()))
return;
// TODO: implement those
if (F->getDeclaredReturnType()->isFunctionPointerType() ||
F->getDeclaredReturnType()->isMemberFunctionPointerType() ||
F->getDeclaredReturnType()->isMemberPointerType()) {
F->getDeclaredReturnType()->isMemberPointerType() ||
F->getDeclaredReturnType()->getAs<DecltypeType>() != nullptr) {
diag(F->getLocation(), Message);
return;
}
......@@ -473,7 +435,7 @@ void UseTrailingReturnTypeCheck::check(const MatchFinder::MatchResult &Result) {
// discards user formatting and order of const, volatile, type, whitespace,
// space before & ... .
SourceRange ReturnTypeCVRange =
findReturnTypeAndCVSourceRange(*F, FTL.getReturnLoc(), Ctx, SM, LangOpts);
findReturnTypeAndCVSourceRange(*F, Ctx, SM, LangOpts);
if (ReturnTypeCVRange.isInvalid())
return;
......
......@@ -50,11 +50,10 @@ private:
const SourceManager &SM,
const LangOptions &LangOpts);
SourceRange findReturnTypeAndCVSourceRange(const FunctionDecl &F,
const TypeLoc &ReturnLoc,
const ASTContext &Ctx,
const SourceManager &SM,
const LangOptions &LangOpts);
void keepSpecifiers(std::string &ReturnType, std::string &Auto,
bool keepSpecifiers(std::string &ReturnType, std::string &Auto,
SourceRange ReturnTypeCVRange, const FunctionDecl &F,
const FriendDecl *Fr, const ASTContext &Ctx,
const SourceManager &SM, const LangOptions &LangOpts);
......
......@@ -23,8 +23,8 @@ namespace performance {
MoveConstructorInitCheck::MoveConstructorInitCheck(StringRef Name,
ClangTidyContext *Context)
: ClangTidyCheck(Name, Context),
Inserter(Options.getLocalOrGlobal("IncludeStyle",
utils::IncludeSorter::IS_LLVM)) {}
IncludeStyle(Options.getLocalOrGlobal("IncludeStyle",
utils::IncludeSorter::IS_LLVM)) {}
void MoveConstructorInitCheck::registerMatchers(MatchFinder *Finder) {
Finder->addMatcher(
......@@ -90,11 +90,13 @@ void MoveConstructorInitCheck::check(const MatchFinder::MatchResult &Result) {
void MoveConstructorInitCheck::registerPPCallbacks(
const SourceManager &SM, Preprocessor *PP, Preprocessor *ModuleExpanderPP) {
Inserter.registerPreprocessor(PP);
Inserter = std::make_unique<utils::IncludeInserter>(SM, getLangOpts(),
IncludeStyle);
PP->addPPCallbacks(Inserter->CreatePPCallbacks());
}
void MoveConstructorInitCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
Options.store(Opts, "IncludeStyle", Inserter.getStyle());
Options.store(Opts, "IncludeStyle", IncludeStyle);
}
} // namespace performance
......
......@@ -36,7 +36,8 @@ public:
void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
private:
utils::IncludeInserter Inserter;
std::unique_ptr<utils::IncludeInserter> Inserter;
const utils::IncludeSorter::IncludeStyle IncludeStyle;
};
} // namespace performance
......
......@@ -31,18 +31,19 @@ AST_MATCHER_P(Type, isBuiltinType, BuiltinType::Kind, Kind) {
TypePromotionInMathFnCheck::TypePromotionInMathFnCheck(
StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context),
IncludeInserter(Options.getLocalOrGlobal("IncludeStyle",
utils::IncludeSorter::IS_LLVM)) {
}
IncludeStyle(Options.getLocalOrGlobal("IncludeStyle",
utils::IncludeSorter::IS_LLVM)) {}
void TypePromotionInMathFnCheck::registerPPCallbacks(
const SourceManager &SM, Preprocessor *PP, Preprocessor *ModuleExpanderPP) {
IncludeInserter.registerPreprocessor(PP);
IncludeInserter = std::make_unique<utils::IncludeInserter>(SM, getLangOpts(),
IncludeStyle);
PP->addPPCallbacks(IncludeInserter->CreatePPCallbacks());
}
void TypePromotionInMathFnCheck::storeOptions(
ClangTidyOptions::OptionMap &Opts) {
Options.store(Opts, "IncludeStyle", IncludeInserter.getStyle());
Options.store(Opts, "IncludeStyle", IncludeStyle);
}
void TypePromotionInMathFnCheck::registerMatchers(MatchFinder *Finder) {
......@@ -190,9 +191,9 @@ void TypePromotionInMathFnCheck::check(const MatchFinder::MatchResult &Result) {
// <math.h>, because the functions we're suggesting moving away from are all
// declared in <math.h>.
if (FnInCmath)
Diag << IncludeInserter.createIncludeInsertion(
Diag << IncludeInserter->CreateIncludeInsertion(
Result.Context->getSourceManager().getFileID(Call->getBeginLoc()),
"<cmath>");
"cmath", /*IsAngled=*/true);
}
} // namespace performance
......
......@@ -36,7 +36,8 @@ public:
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
private:
utils::IncludeInserter IncludeInserter;
std::unique_ptr<utils::IncludeInserter> IncludeInserter;
const utils::IncludeSorter::IncludeStyle IncludeStyle;
};
} // namespace performance
......
......@@ -54,8 +54,7 @@ void UnnecessaryCopyInitialization::registerMatchers(MatchFinder *Finder) {
on(declRefExpr(to(varDecl().bind("objectArg")))));
auto ConstRefReturningFunctionCall =
callExpr(callee(functionDecl(returns(ConstReference))),
unless(callee(cxxMethodDecl())))
.bind("initFunctionCall");
unless(callee(cxxMethodDecl())));
auto localVarCopiedFrom = [this](const internal::Matcher<Expr> &CopyCtorArg) {
return compoundStmt(
......@@ -97,8 +96,6 @@ void UnnecessaryCopyInitialization::check(
const auto *ObjectArg = Result.Nodes.getNodeAs<VarDecl>("objectArg");
const auto *BlockStmt = Result.Nodes.getNodeAs<Stmt>("blockStmt");
const auto *CtorCall = Result.Nodes.getNodeAs<CXXConstructExpr>("ctorCall");
const auto *InitFunctionCall =
Result.Nodes.getNodeAs<CallExpr>("initFunctionCall");
TraversalKindScope RAII(*Result.Context, ast_type_traits::TK_AsIs);
......@@ -116,11 +113,6 @@ void UnnecessaryCopyInitialization::check(
return;
if (OldVar == nullptr) {
// Only allow initialization of a const reference from a free function if it
// has no arguments. Otherwise it could return an alias to one of its
// arguments and the arguments need to be checked for const use as well.
if (InitFunctionCall != nullptr && InitFunctionCall->getNumArgs() > 0)
return;
handleCopyFromMethodReturn(*NewVar, *BlockStmt, IssueFix, ObjectArg,
*Result.Context);
} else {
......
......@@ -68,8 +68,8 @@ bool isExplicitTemplateSpecialization(const FunctionDecl &Function) {
UnnecessaryValueParamCheck::UnnecessaryValueParamCheck(
StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context),
Inserter(Options.getLocalOrGlobal("IncludeStyle",
utils::IncludeSorter::IS_LLVM)),
IncludeStyle(Options.getLocalOrGlobal("IncludeStyle",
utils::IncludeSorter::IS_LLVM)),
AllowedTypes(
utils::options::parseStringList(Options.get("AllowedTypes", ""))) {}
......@@ -173,12 +173,14 @@ void UnnecessaryValueParamCheck::check(const MatchFinder::MatchResult &Result) {
void UnnecessaryValueParamCheck::registerPPCallbacks(
const SourceManager &SM, Preprocessor *PP, Preprocessor *ModuleExpanderPP) {
Inserter.registerPreprocessor(PP);
Inserter = std::make_unique<utils::IncludeInserter>(SM, getLangOpts(),
IncludeStyle);
PP->addPPCallbacks(Inserter->CreatePPCallbacks());
}
void UnnecessaryValueParamCheck::storeOptions(
ClangTidyOptions::OptionMap &Opts) {
Options.store(Opts, "IncludeStyle", Inserter.getStyle());
Options.store(Opts, "IncludeStyle", IncludeStyle);
Options.store(Opts, "AllowedTypes",
utils::options::serializeStringList(AllowedTypes));
}
......@@ -202,8 +204,9 @@ void UnnecessaryValueParamCheck::handleMoveFix(const ParmVarDecl &Var,
Context.getLangOpts());
Diag << FixItHint::CreateInsertion(CopyArgument.getBeginLoc(), "std::move(")
<< FixItHint::CreateInsertion(EndLoc, ")")
<< Inserter.createIncludeInsertion(
SM.getFileID(CopyArgument.getBeginLoc()), "<utility>");
<< Inserter->CreateIncludeInsertion(
SM.getFileID(CopyArgument.getBeginLoc()), "utility",
/*IsAngled=*/true);
}
} // namespace performance
......
......@@ -41,7 +41,8 @@ private:
llvm::DenseMap<const FunctionDecl *, FunctionParmMutationAnalyzer>
MutationAnalyzers;
utils::IncludeInserter Inserter;
std::unique_ptr<utils::IncludeInserter> Inserter;
const utils::IncludeSorter::IncludeStyle IncludeStyle;
const std::vector<std::string> AllowedTypes;
};
......
......@@ -12,7 +12,6 @@ add_clang_library(clangTidyReadabilityModule
DeleteNullPointerCheck.cpp
DeletedDefaultCheck.cpp
ElseAfterReturnCheck.cpp
FunctionCognitiveComplexityCheck.cpp
FunctionSizeCheck.cpp
IdentifierNamingCheck.cpp
ImplicitBoolConversionCheck.cpp
......
//===--- FunctionCognitiveComplexityCheck.h - clang-tidy --------*- 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_CLANG_TIDY_READABILITY_FUNCTIONCOGNITIVECOMPLEXITYCHECK_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_FUNCTIONCOGNITIVECOMPLEXITYCHECK_H
#include "../ClangTidyCheck.h"
namespace clang {
namespace tidy {
namespace readability {
/// Checks function Cognitive Complexity metric.
///
/// There is only one configuration option:
///
/// * `Threshold` - flag functions with Cognitive Complexity exceeding
/// this number. The default is `25`.
///
/// For the user-facing documentation see:
/// http://clang.llvm.org/extra/clang-tidy/checks/readability-function-cognitive-complexity.html
class FunctionCognitiveComplexityCheck : public ClangTidyCheck {
public:
FunctionCognitiveComplexityCheck(StringRef Name, ClangTidyContext *Context);
void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
private:
const unsigned Threshold;
};
} // namespace readability
} // namespace tidy
} // namespace clang
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_FUNCTIONCOGNITIVECOMPLEXITYCHECK_H
......@@ -10,7 +10,6 @@
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_IDENTIFIERNAMINGCHECK_H
#include "../utils/RenamerClangTidyCheck.h"
#include "llvm/ADT/Optional.h"
namespace clang {
class MacroInfo;
......@@ -70,17 +69,7 @@ private:
DiagInfo GetDiagInfo(const NamingCheckId &ID,
const NamingCheckFailure &Failure) const override;
ArrayRef<llvm::Optional<NamingStyle>>
getStyleForFile(StringRef FileName) const;
/// Stores the style options as a vector, indexed by the specified \ref
/// StyleKind, for a given directory.
mutable llvm::StringMap<std::vector<llvm::Optional<NamingStyle>>>
NamingStylesCache;
ArrayRef<llvm::Optional<NamingStyle>> MainFileStyle;
ClangTidyContext *const Context;
const std::string CheckName;
const bool GetConfigPerFile;
std::vector<llvm::Optional<NamingStyle>> NamingStyles;
const bool IgnoreFailedSplit;
const bool IgnoreMainLikeFunctions;
};
......
......@@ -17,7 +17,6 @@
#include "DeleteNullPointerCheck.h"
#include "DeletedDefaultCheck.h"
#include "ElseAfterReturnCheck.h"
#include "FunctionCognitiveComplexityCheck.h"
#include "FunctionSizeCheck.h"
#include "IdentifierNamingCheck.h"
#include "ImplicitBoolConversionCheck.h"
......@@ -71,8 +70,6 @@ public:
"readability-deleted-default");
CheckFactories.registerCheck<ElseAfterReturnCheck>(
"readability-else-after-return");
CheckFactories.registerCheck<FunctionCognitiveComplexityCheck>(
"readability-function-cognitive-complexity");
CheckFactories.registerCheck<FunctionSizeCheck>(
"readability-function-size");
CheckFactories.registerCheck<IdentifierNamingCheck>(
......
......@@ -58,28 +58,16 @@ const char SimplifyConditionDiagnostic[] =
const char SimplifyConditionalReturnDiagnostic[] =
"redundant boolean literal in conditional return statement";
const Expr *getBoolLiteral(const MatchFinder::MatchResult &Result,
StringRef Id) {
if (const Expr *Literal = Result.Nodes.getNodeAs<CXXBoolLiteralExpr>(Id))
return Literal->getBeginLoc().isMacroID() ? nullptr : Literal;
if (const auto *Negated = Result.Nodes.getNodeAs<UnaryOperator>(Id)) {
if (Negated->getOpcode() == UO_LNot &&
isa<CXXBoolLiteralExpr>(Negated->getSubExpr()))
return Negated->getBeginLoc().isMacroID() ? nullptr : Negated;
}
return nullptr;
}
internal::BindableMatcher<Stmt> literalOrNegatedBool(bool Value) {
return expr(anyOf(cxxBoolLiteral(equals(Value)),
unaryOperator(hasUnaryOperand(ignoringParenImpCasts(
cxxBoolLiteral(equals(!Value)))),
hasOperatorName("!"))));
const CXXBoolLiteralExpr *getBoolLiteral(const MatchFinder::MatchResult &Result,
StringRef Id) {
const auto *Literal = Result.Nodes.getNodeAs<CXXBoolLiteralExpr>(Id);
return (Literal && Literal->getBeginLoc().isMacroID()) ? nullptr : Literal;
}
internal::Matcher<Stmt> returnsBool(bool Value, StringRef Id = "ignored") {
auto SimpleReturnsBool = returnStmt(has(literalOrNegatedBool(Value).bind(Id)))
.bind("returns-bool");
auto SimpleReturnsBool =
returnStmt(has(cxxBoolLiteral(equals(Value)).bind(Id)))
.bind("returns-bool");
return anyOf(SimpleReturnsBool,
compoundStmt(statementCountIs(1), has(SimpleReturnsBool)));
}
......@@ -205,7 +193,7 @@ std::string compareExpressionToZero(const MatchFinder::MatchResult &Result,
std::string replacementExpression(const MatchFinder::MatchResult &Result,
bool Negated, const Expr *E) {
E = E->IgnoreParenBaseCasts();
E = E->ignoreParenBaseCasts();
if (const auto *EC = dyn_cast<ExprWithCleanups>(E))
E = EC->getSubExpr();
......@@ -281,25 +269,16 @@ std::string replacementExpression(const MatchFinder::MatchResult &Result,
return asBool(getText(Result, *E), NeedsStaticCast);
}
const Expr *stmtReturnsBool(const ReturnStmt *Ret, bool Negated) {
const CXXBoolLiteralExpr *stmtReturnsBool(const ReturnStmt *Ret, bool Negated) {
if (const auto *Bool = dyn_cast<CXXBoolLiteralExpr>(Ret->getRetValue())) {
if (Bool->getValue() == !Negated)
return Bool;
}
if (const auto *Unary = dyn_cast<UnaryOperator>(Ret->getRetValue())) {
if (Unary->getOpcode() == UO_LNot) {
if (const auto *Bool =
dyn_cast<CXXBoolLiteralExpr>(Unary->getSubExpr())) {
if (Bool->getValue() == Negated)
return Bool;
}
}
}
return nullptr;
}
const Expr *stmtReturnsBool(const IfStmt *IfRet, bool Negated) {
const CXXBoolLiteralExpr *stmtReturnsBool(const IfStmt *IfRet, bool Negated) {
if (IfRet->getElse() != nullptr)
return nullptr;
......@@ -444,7 +423,7 @@ void SimplifyBooleanExprCheck::matchBoolCondition(MatchFinder *Finder,
StringRef BooleanId) {
Finder->addMatcher(
ifStmt(unless(isInTemplateInstantiation()),
hasCondition(literalOrNegatedBool(Value).bind(BooleanId)))
hasCondition(cxxBoolLiteral(equals(Value)).bind(BooleanId)))
.bind(IfStmtId),
this);
}
......@@ -454,8 +433,8 @@ void SimplifyBooleanExprCheck::matchTernaryResult(MatchFinder *Finder,
StringRef TernaryId) {
Finder->addMatcher(
conditionalOperator(unless(isInTemplateInstantiation()),
hasTrueExpression(literalOrNegatedBool(Value)),
hasFalseExpression(literalOrNegatedBool(!Value)))
hasTrueExpression(cxxBoolLiteral(equals(Value))),
hasFalseExpression(cxxBoolLiteral(equals(!Value))))
.bind(TernaryId),
this);
}
......@@ -486,12 +465,12 @@ void SimplifyBooleanExprCheck::matchIfAssignsBool(MatchFinder *Finder,
auto SimpleThen =
binaryOperator(hasOperatorName("="), hasLHS(anyOf(VarAssign, MemAssign)),
hasLHS(expr().bind(IfAssignVariableId)),
hasRHS(literalOrNegatedBool(Value).bind(IfAssignLocId)));
hasRHS(cxxBoolLiteral(equals(Value)).bind(IfAssignLocId)));
auto Then = anyOf(SimpleThen, compoundStmt(statementCountIs(1),
hasAnySubstatement(SimpleThen)));
auto SimpleElse =
binaryOperator(hasOperatorName("="), hasLHS(anyOf(VarRef, MemRef)),
hasRHS(literalOrNegatedBool(!Value)));
hasRHS(cxxBoolLiteral(equals(!Value))));
auto Else = anyOf(SimpleElse, compoundStmt(statementCountIs(1),
hasAnySubstatement(SimpleElse)));
if (ChainedConditionalAssignment)
......@@ -516,7 +495,7 @@ void SimplifyBooleanExprCheck::matchCompoundIfReturnsBool(MatchFinder *Finder,
hasAnySubstatement(
ifStmt(hasThen(returnsBool(Value)), unless(hasElse(stmt())))),
hasAnySubstatement(returnStmt(has(ignoringParenImpCasts(
literalOrNegatedBool(!Value))))
cxxBoolLiteral(equals(!Value)))))
.bind(CompoundReturnId)))
.bind(Id),
this);
......@@ -550,10 +529,10 @@ void SimplifyBooleanExprCheck::registerMatchers(MatchFinder *Finder) {
void SimplifyBooleanExprCheck::check(const MatchFinder::MatchResult &Result) {
if (Result.Nodes.getNodeAs<TranslationUnitDecl>("top"))
Visitor(this, Result).TraverseAST(*Result.Context);
else if (const Expr *TrueConditionRemoved =
else if (const CXXBoolLiteralExpr *TrueConditionRemoved =
getBoolLiteral(Result, ConditionThenStmtId))
replaceWithThenStatement(Result, TrueConditionRemoved);
else if (const Expr *FalseConditionRemoved =
else if (const CXXBoolLiteralExpr *FalseConditionRemoved =
getBoolLiteral(Result, ConditionElseStmtId))
replaceWithElseStatement(Result, FalseConditionRemoved);
else if (const auto *Ternary =
......@@ -595,7 +574,8 @@ void SimplifyBooleanExprCheck::issueDiag(
}
void SimplifyBooleanExprCheck::replaceWithThenStatement(
const MatchFinder::MatchResult &Result, const Expr *TrueConditionRemoved) {
const MatchFinder::MatchResult &Result,
const CXXBoolLiteralExpr *TrueConditionRemoved) {
const auto *IfStatement = Result.Nodes.getNodeAs<IfStmt>(IfStmtId);
issueDiag(Result, TrueConditionRemoved->getBeginLoc(),
SimplifyConditionDiagnostic, IfStatement->getSourceRange(),
......@@ -603,7 +583,8 @@ void SimplifyBooleanExprCheck::replaceWithThenStatement(
}
void SimplifyBooleanExprCheck::replaceWithElseStatement(
const MatchFinder::MatchResult &Result, const Expr *FalseConditionRemoved) {
const MatchFinder::MatchResult &Result,
const CXXBoolLiteralExpr *FalseConditionRemoved) {
const auto *IfStatement = Result.Nodes.getNodeAs<IfStmt>(IfStmtId);
const Stmt *ElseStatement = IfStatement->getElse();
issueDiag(Result, FalseConditionRemoved->getBeginLoc(),
......@@ -650,7 +631,7 @@ void SimplifyBooleanExprCheck::replaceCompoundReturnWithCondition(
for (++After; After != Compound->body_end() && *Current != Ret;
++Current, ++After) {
if (const auto *If = dyn_cast<IfStmt>(*Current)) {
if (const Expr *Lit = stmtReturnsBool(If, Negated)) {
if (const CXXBoolLiteralExpr *Lit = stmtReturnsBool(If, Negated)) {
if (*After == Ret) {
if (!ChainedConditionalReturn && BeforeIf)
continue;
......
......@@ -51,11 +51,11 @@ private:
void
replaceWithThenStatement(const ast_matchers::MatchFinder::MatchResult &Result,
const Expr *BoolLiteral);
const CXXBoolLiteralExpr *BoolLiteral);
void
replaceWithElseStatement(const ast_matchers::MatchFinder::MatchResult &Result,
const Expr *FalseConditionRemoved);
const CXXBoolLiteralExpr *FalseConditionRemoved);
void
replaceWithCondition(const ast_matchers::MatchFinder::MatchResult &Result,
......
......@@ -123,7 +123,12 @@ public:
// Emit warnings for headers that are missing guards.
checkGuardlessHeaders();
clearAllState();
// Clear all state.
Macros.clear();
Files.clear();
Ifndefs.clear();
EndIfs.clear();
}
bool wouldFixEndifComment(StringRef FileName, SourceLocation EndIf,
......@@ -250,13 +255,6 @@ public:
}
private:
void clearAllState() {
Macros.clear();
Files.clear();
Ifndefs.clear();
EndIfs.clear();
}
std::vector<std::pair<Token, const MacroInfo *>> Macros;
llvm::StringMap<const FileEntry *> Files;
std::map<const IdentifierInfo *, std::pair<SourceLocation, SourceLocation>>
......
......@@ -7,8 +7,6 @@
//===----------------------------------------------------------------------===//
#include "IncludeInserter.h"
#include "clang/Lex/PPCallbacks.h"
#include "clang/Lex/Preprocessor.h"
#include "clang/Lex/Token.h"
namespace clang {
......@@ -28,7 +26,7 @@ public:
StringRef /*SearchPath*/, StringRef /*RelativePath*/,
const Module * /*ImportedModule*/,
SrcMgr::CharacteristicKind /*FileType*/) override {
Inserter->addInclude(FileNameRef, IsAngled, HashLocation,
Inserter->AddInclude(FileNameRef, IsAngled, HashLocation,
IncludeToken.getEndLoc());
}
......@@ -36,76 +34,45 @@ private:
IncludeInserter *Inserter;
};
IncludeInserter::IncludeInserter(IncludeSorter::IncludeStyle Style)
: Style(Style) {}
IncludeInserter::IncludeInserter(const SourceManager &SourceMgr,
const LangOptions &LangOpts,
IncludeSorter::IncludeStyle Style)
: SourceMgr(SourceMgr), Style(Style) {}
void IncludeInserter::registerPreprocessor(Preprocessor *PP) {
assert(PP && "PP shouldn't be null");
SourceMgr = &PP->getSourceManager();
IncludeInserter::~IncludeInserter() {}
// If this gets registered multiple times, clear the maps
if (!IncludeSorterByFile.empty())
IncludeSorterByFile.clear();
if (!InsertedHeaders.empty())
InsertedHeaders.clear();
PP->addPPCallbacks(std::make_unique<IncludeInserterCallback>(this));
std::unique_ptr<PPCallbacks> IncludeInserter::CreatePPCallbacks() {
return std::make_unique<IncludeInserterCallback>(this);
}
IncludeSorter &IncludeInserter::getOrCreate(FileID FileID) {
assert(SourceMgr && "SourceMgr shouldn't be null; did you remember to call "
"registerPreprocessor()?");
// std::unique_ptr is cheap to construct, so force a construction now to save
// the lookup needed if we were to insert into the map.
std::unique_ptr<IncludeSorter> &Entry = IncludeSorterByFile[FileID];
if (!Entry) {
// If it wasn't found, Entry will be default constructed to nullptr.
Entry = std::make_unique<IncludeSorter>(
SourceMgr, FileID,
SourceMgr->getFilename(SourceMgr->getLocForStartOfFile(FileID)), Style);
&SourceMgr, FileID,
SourceMgr.getFilename(SourceMgr.getLocForStartOfFile(FileID)), Style);
}
return *Entry;
}
llvm::Optional<FixItHint>
IncludeInserter::createIncludeInsertion(FileID FileID, StringRef Header,
IncludeInserter::CreateIncludeInsertion(FileID FileID, StringRef Header,
bool IsAngled) {
// We assume the same Header will never be included both angled and not
// angled.
if (!InsertedHeaders[FileID].insert(Header).second)
if (!InsertedHeaders[FileID].insert(std::string(Header)).second)
return llvm::None;
return getOrCreate(FileID).CreateIncludeInsertion(Header, IsAngled);
}
llvm::Optional<FixItHint>
IncludeInserter::createIncludeInsertion(FileID FileID, llvm::StringRef Header) {
bool IsAngled = Header.consume_front("<");
if (IsAngled != Header.consume_back(">"))
return llvm::None;
return createIncludeInsertion(FileID, Header, IsAngled);
}
llvm::Optional<FixItHint>
IncludeInserter::createMainFileIncludeInsertion(StringRef Header,
bool IsAngled) {
assert(SourceMgr && "SourceMgr shouldn't be null; did you remember to call "
"registerPreprocessor()?");
return createIncludeInsertion(SourceMgr->getMainFileID(), Header, IsAngled);
}
llvm::Optional<FixItHint>
IncludeInserter::createMainFileIncludeInsertion(StringRef Header) {
assert(SourceMgr && "SourceMgr shouldn't be null; did you remember to call "
"registerPreprocessor()?");
return createIncludeInsertion(SourceMgr->getMainFileID(), Header);
}
void IncludeInserter::addInclude(StringRef FileName, bool IsAngled,
void IncludeInserter::AddInclude(StringRef FileName, bool IsAngled,
SourceLocation HashLocation,
SourceLocation EndLocation) {
assert(SourceMgr && "SourceMgr shouldn't be null; did you remember to call "
"registerPreprocessor()?");
FileID FileID = SourceMgr->getFileID(HashLocation);
FileID FileID = SourceMgr.getFileID(HashLocation);
getOrCreate(FileID).AddInclude(FileName, IsAngled, HashLocation, EndLocation);
}
......
......@@ -11,11 +11,13 @@
#include "IncludeSorter.h"
#include "clang/Basic/Diagnostic.h"
#include "llvm/ADT/StringSet.h"
#include "clang/Basic/LangOptions.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Lex/PPCallbacks.h"
#include <memory>
#include <string>
namespace clang {
class Preprocessor;
namespace tidy {
namespace utils {
......@@ -24,17 +26,16 @@ namespace utils {
///
/// ``IncludeInserter`` can be used in clang-tidy checks in the following way:
/// \code
/// #include "../ClangTidyCheck.h"
/// #include "../utils/IncludeInserter.h"
///
/// namespace clang {
/// namespace tidy {
/// #include "clang/Frontend/CompilerInstance.h"
///
/// class MyCheck : public ClangTidyCheck {
/// public:
/// void registerPPCallbacks(const SourceManager &SM, Preprocessor *PP,
/// Preprocessor *ModuleExpanderPP) override {
/// Inserter.registerPreprocessor();
/// Inserter = std::make_unique<IncludeInserter>(
/// SM, getLangOpts(), utils::IncludeSorter::IS_Google);
/// PP->addPPCallbacks(Inserter->CreatePPCallbacks());
/// }
///
/// void registerMatchers(ast_matchers::MatchFinder* Finder) override { ... }
......@@ -42,73 +43,39 @@ namespace utils {
/// void check(
/// const ast_matchers::MatchFinder::MatchResult& Result) override {
/// ...
/// Inserter.createMainFileIncludeInsertion("path/to/Header.h",
/// /*IsAngled=*/false);
/// Inserter->CreateIncludeInsertion(
/// Result.SourceManager->getMainFileID(), "path/to/Header.h",
/// /*IsAngled=*/false);
/// ...
/// }
///
/// private:
/// utils::IncludeInserter Inserter{utils::IncludeSorter::IS_Google};
/// std::unique_ptr<clang::tidy::utils::IncludeInserter> Inserter;
/// };
/// } // namespace tidy
/// } // namespace clang
/// \endcode
class IncludeInserter {
public:
/// Initializes the IncludeInserter using the IncludeStyle \p Style.
/// In most cases the \p Style will be retrieved from the ClangTidyOptions
/// using \code
/// Options.getLocalOrGlobal("IncludeStyle", <DefaultStyle>)
/// \endcode
explicit IncludeInserter(IncludeSorter::IncludeStyle Style);
/// Registers this with the Preprocessor \p PP, must be called before this
/// class is used.
void registerPreprocessor(Preprocessor *PP);
/// Creates a \p Header inclusion directive fixit in the File \p FileID.
/// Returns ``llvm::None`` on error or if the inclusion directive already
/// exists.
/// FIXME: This should be removed once the clients are migrated to the
/// overload without the ``IsAngled`` parameter.
llvm::Optional<FixItHint>
createIncludeInsertion(FileID FileID, llvm::StringRef Header, bool IsAngled);
IncludeInserter(const SourceManager &SourceMgr, const LangOptions &LangOpts,
IncludeSorter::IncludeStyle Style);
~IncludeInserter();
/// Creates a \p Header inclusion directive fixit in the File \p FileID.
/// When \p Header is enclosed in angle brackets, uses angle brackets in the
/// inclusion directive, otherwise uses quotes.
/// Returns ``llvm::None`` on error or if the inclusion directive already
/// exists.
llvm::Optional<FixItHint> createIncludeInsertion(FileID FileID,
llvm::StringRef Header);
/// Create ``PPCallbacks`` for registration with the compiler's preprocessor.
std::unique_ptr<PPCallbacks> CreatePPCallbacks();
/// Creates a \p Header inclusion directive fixit in the main file.
/// Returns``llvm::None`` on error or if the inclusion directive already
/// exists.
/// FIXME: This should be removed once the clients are migrated to the
/// overload without the ``IsAngled`` parameter.
/// Creates a \p Header inclusion directive fixit. Returns ``llvm::None`` on
/// error or if inclusion directive already exists.
llvm::Optional<FixItHint>
createMainFileIncludeInsertion(llvm::StringRef Header, bool IsAngled);
/// Creates a \p Header inclusion directive fixit in the main file.
/// When \p Header is enclosed in angle brackets, uses angle brackets in the
/// inclusion directive, otherwise uses quotes.
/// Returns``llvm::None`` on error or if the inclusion directive already
/// exists.
llvm::Optional<FixItHint>
createMainFileIncludeInsertion(llvm::StringRef Header);
IncludeSorter::IncludeStyle getStyle() const { return Style; }
CreateIncludeInsertion(FileID FileID, llvm::StringRef Header, bool IsAngled);
private:
void addInclude(StringRef FileName, bool IsAngled,
void AddInclude(StringRef FileName, bool IsAngled,
SourceLocation HashLocation, SourceLocation EndLocation);
IncludeSorter &getOrCreate(FileID FileID);
llvm::DenseMap<FileID, std::unique_ptr<IncludeSorter>> IncludeSorterByFile;
llvm::DenseMap<FileID, llvm::StringSet<>> InsertedHeaders;
const SourceManager *SourceMgr{nullptr};
llvm::DenseMap<FileID, std::set<std::string>> InsertedHeaders;
const SourceManager &SourceMgr;
const IncludeSorter::IncludeStyle Style;
friend class IncludeInserterCallback;
};
......
......@@ -32,8 +32,8 @@ TransformerClangTidyCheck::TransformerClangTidyCheck(
MakeRule,
StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context), Rule(MakeRule(getLangOpts(), Options)),
Inserter(
Options.getLocalOrGlobal("IncludeStyle", IncludeSorter::IS_LLVM)) {
IncludeStyle(Options.getLocalOrGlobal("IncludeStyle",
IncludeSorter::IS_LLVM)) {
if (Rule)
assert(llvm::all_of(Rule->Cases, hasExplanation) &&
"clang-tidy checks must have an explanation by default;"
......@@ -44,8 +44,8 @@ TransformerClangTidyCheck::TransformerClangTidyCheck(RewriteRule R,
StringRef Name,
ClangTidyContext *Context)
: ClangTidyCheck(Name, Context), Rule(std::move(R)),
Inserter(
Options.getLocalOrGlobal("IncludeStyle", IncludeSorter::IS_LLVM)) {
IncludeStyle(Options.getLocalOrGlobal("IncludeStyle",
IncludeSorter::IS_LLVM)) {
assert(llvm::all_of(Rule->Cases, hasExplanation) &&
"clang-tidy checks must have an explanation by default;"
" explicitly provide an empty explanation if none is desired");
......@@ -53,7 +53,15 @@ TransformerClangTidyCheck::TransformerClangTidyCheck(RewriteRule R,
void TransformerClangTidyCheck::registerPPCallbacks(
const SourceManager &SM, Preprocessor *PP, Preprocessor *ModuleExpanderPP) {
Inserter.registerPreprocessor(PP);
// Only allocate and register the IncludeInsert when some `Case` will add
// includes.
if (Rule && llvm::any_of(Rule->Cases, [](const RewriteRule::Case &C) {
return !C.AddedIncludes.empty();
})) {
Inserter =
std::make_unique<IncludeInserter>(SM, getLangOpts(), IncludeStyle);
PP->addPPCallbacks(Inserter->CreatePPCallbacks());
}
}
void TransformerClangTidyCheck::registerMatchers(
......@@ -91,24 +99,18 @@ void TransformerClangTidyCheck::check(
// Associate the diagnostic with the location of the first change.
DiagnosticBuilder Diag = diag((*Edits)[0].Range.getBegin(), *Explanation);
for (const auto &T : *Edits)
switch (T.Kind) {
case transformer::EditKind::Range:
Diag << FixItHint::CreateReplacement(T.Range, T.Replacement);
break;
case transformer::EditKind::AddInclude: {
StringRef FileName = T.Replacement;
bool IsAngled = FileName.startswith("<") && FileName.endswith(">");
Diag << Inserter.createMainFileIncludeInsertion(
IsAngled ? FileName.substr(1, FileName.size() - 2) : FileName,
IsAngled);
break;
}
}
Diag << FixItHint::CreateReplacement(T.Range, T.Replacement);
for (const auto &I : Case.AddedIncludes) {
Diag << Inserter->CreateIncludeInsertion(
Result.SourceManager->getMainFileID(), I.first,
/*IsAngled=*/I.second == transformer::IncludeFormat::Angled);
}
}
void TransformerClangTidyCheck::storeOptions(
ClangTidyOptions::OptionMap &Opts) {
Options.store(Opts, "IncludeStyle", Inserter.getStyle());
Options.store(Opts, "IncludeStyle", IncludeStyle);
}
} // namespace utils
......
......@@ -70,7 +70,8 @@ public:
private:
Optional<transformer::RewriteRule> Rule;
IncludeInserter Inserter;
const IncludeSorter::IncludeStyle IncludeStyle;
std::unique_ptr<IncludeInserter> Inserter;
};
} // namespace utils
......
......@@ -15,7 +15,6 @@ if (NOT DEFINED CLANGD_BUILD_XPC)
endif ()
llvm_canonicalize_cmake_booleans(CLANGD_BUILD_XPC)
llvm_canonicalize_cmake_booleans(CLANGD_ENABLE_REMOTE)
configure_file(
${CMAKE_CURRENT_SOURCE_DIR}/Features.inc.in
......@@ -28,15 +27,6 @@ set(LLVM_LINK_COMPONENTS
FrontendOpenMP
Option
)
include(${CMAKE_CURRENT_SOURCE_DIR}/quality/CompletionModel.cmake)
gen_decision_forest(${CMAKE_CURRENT_SOURCE_DIR}/quality/model CompletionModel clang::clangd::Example)
if(MSVC AND NOT CLANG_CL)
set_source_files_properties(CompileCommands.cpp PROPERTIES COMPILE_FLAGS -wd4130) # disables C4130: logical operation on address of string constant
endif()
include_directories(BEFORE "${CMAKE_CURRENT_BINARY_DIR}/../clang-tidy")
add_clang_library(clangDaemon
AST.cpp
......@@ -80,7 +70,6 @@ add_clang_library(clangDaemon
TUScheduler.cpp
URI.cpp
XRefs.cpp
${CMAKE_CURRENT_BINARY_DIR}/CompletionModel.cpp
index/Background.cpp
index/BackgroundIndexLoader.cpp
......@@ -121,11 +110,6 @@ add_clang_library(clangDaemon
omp_gen
)
# Include generated CompletionModel headers.
target_include_directories(clangDaemon PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}>
)
clang_target_link_libraries(clangDaemon
PRIVATE
clangAST
......
......@@ -36,24 +36,17 @@ class SymbolIndex;
/// The server also supports $/cancelRequest (MessageHandler provides this).
class ClangdLSPServer : private ClangdServer::Callbacks {
public:
struct Options : ClangdServer::Options {
/// Look for compilation databases, rather than using compile commands
/// set via LSP (extensions) only.
bool UseDirBasedCDB = true;
/// A fixed directory to search for a compilation database in.
/// If not set, we search upward from the source file.
llvm::Optional<Path> CompileCommandsDir;
/// The offset-encoding to use, or None to negotiate it over LSP.
llvm::Optional<OffsetEncoding> Encoding;
/// Per-feature options. Generally ClangdServer lets these vary
/// per-request, but LSP allows limited/no customizations.
clangd::CodeCompleteOptions CodeComplete;
clangd::RenameOptions Rename;
};
/// If \p CompileCommandsDir has a value, compile_commands.json will be
/// loaded only from \p CompileCommandsDir. Otherwise, clangd will look
/// for compile_commands.json in all parent directories of each file.
/// If UseDirBasedCDB is false, compile commands are not read from disk.
// FIXME: Clean up signature around CDBs.
ClangdLSPServer(Transport &Transp, const ThreadsafeFS &TFS,
const ClangdLSPServer::Options &Opts);
const clangd::CodeCompleteOptions &CCOpts,
const clangd::RenameOptions &RenameOpts,
llvm::Optional<Path> CompileCommandsDir, bool UseDirBasedCDB,
llvm::Optional<OffsetEncoding> ForcedOffsetEncoding,
const ClangdServer::Options &Opts);
/// The destructor blocks on any outstanding background tasks.
~ClangdLSPServer();
......@@ -187,39 +180,22 @@ private:
std::unique_ptr<MessageHandler> MsgHandler;
std::mutex TranspWriter;
template <typename T>
static Expected<T> parse(const llvm::json::Value &Raw,
llvm::StringRef PayloadName,
llvm::StringRef PayloadKind) {
T Result;
llvm::json::Path::Root Root;
if (!fromJSON(Raw, Result, Root)) {
elog("Failed to decode {0} {1}: {2}", PayloadName, PayloadKind,
Root.getError());
// Dump the relevant parts of the broken message.
std::string Context;
llvm::raw_string_ostream OS(Context);
Root.printErrorContext(Raw, OS);
vlog("{0}", OS.str());
// Report the error (e.g. to the client).
return llvm::make_error<LSPError>(
llvm::formatv("failed to decode {0} {1}: {2}", PayloadName,
PayloadKind, fmt_consume(Root.getError())),
ErrorCode::InvalidParams);
}
return std::move(Result);
}
template <typename Response>
void call(StringRef Method, llvm::json::Value Params, Callback<Response> CB) {
// Wrap the callback with LSP conversion and error-handling.
auto HandleReply =
[CB = std::move(CB), Ctx = Context::current().clone(),
Method = Method.str()](
[CB = std::move(CB), Ctx = Context::current().clone()](
llvm::Expected<llvm::json::Value> RawResponse) mutable {
if (!RawResponse)
return CB(RawResponse.takeError());
CB(parse<Response>(*RawResponse, Method, "response"));
Response Rsp;
if (!RawResponse) {
CB(RawResponse.takeError());
} else if (fromJSON(*RawResponse, Rsp)) {
CB(std::move(Rsp));
} else {
elog("Failed to decode {0} response", *RawResponse);
CB(llvm::make_error<LSPError>("failed to decode response",
ErrorCode::InvalidParams));
}
};
callRaw(Method, std::move(Params), std::move(HandleReply));
}
......@@ -234,6 +210,10 @@ private:
}
const ThreadsafeFS &TFS;
/// Options used for code completion
clangd::CodeCompleteOptions CCOpts;
/// Options used for rename.
clangd::RenameOptions RenameOpts;
/// Options used for diagnostics.
ClangdDiagnosticOptions DiagOpts;
/// The supported kinds of the client.
......@@ -271,11 +251,14 @@ private:
// Store of the current versions of the open documents.
DraftStore DraftMgr;
Options Opts;
// The CDB is created by the "initialize" LSP method.
bool UseDirBasedCDB; // FIXME: make this a capability.
llvm::Optional<Path> CompileCommandsDir; // FIXME: merge with capability?
std::unique_ptr<GlobalCompilationDatabase> BaseCDB;
// CDB is BaseCDB plus any commands overridden via LSP extensions.
llvm::Optional<OverlayCDB> CDB;
ClangdServer::Options ClangdServerOpts;
llvm::Optional<OffsetEncoding> NegotiatedOffsetEncoding;
// The ClangdServer is created by the "initialize" LSP method.
llvm::Optional<ClangdServer> Server;
};
......
......@@ -111,9 +111,6 @@ public:
/// on background threads. The index is stored in the project root.
bool BackgroundIndex = false;
/// Store refs to main-file symbols in the index.
bool CollectMainFileRefs = false;
/// If set, use this index to augment code completion results.
SymbolIndex *StaticIndex = nullptr;
......@@ -131,7 +128,7 @@ public:
bool BuildRecoveryAST = true;
/// If true, turn on the `-frecovery-ast-type` clang flag.
bool PreserveRecoveryASTType = true;
bool PreserveRecoveryASTType = false;
/// Clangd's workspace root. Relevant for "workspace" operations not bound
/// to a particular file.
......@@ -273,12 +270,9 @@ public:
StringRef TriggerText, Callback<std::vector<TextEdit>> CB);
/// Test the validity of a rename operation.
///
/// The returned result describes edits in the main-file only (all
/// occurrences of the renamed symbol are simply deleted.
void prepareRename(PathRef File, Position Pos,
const RenameOptions &RenameOpts,
Callback<RenameResult> CB);
Callback<llvm::Optional<Range>> CB);
/// Rename all occurrences of the symbol at the \p Pos in \p File to
/// \p NewName.
......@@ -286,12 +280,12 @@ public:
/// embedders could use this method to get all occurrences of the symbol (e.g.
/// highlighting them in prepare stage).
void rename(PathRef File, Position Pos, llvm::StringRef NewName,
const RenameOptions &Opts, Callback<RenameResult> CB);
const RenameOptions &Opts, Callback<FileEdits> CB);
struct TweakRef {
std::string ID; /// ID to pass for applyTweak.
std::string Title; /// A single-line message to show in the UI.
llvm::StringLiteral Kind;
Tweak::Intent Intent;
};
/// Enumerate the code tweaks available to the user at a specified point.
void enumerateTweaks(PathRef File, Range Sel,
......@@ -301,6 +295,10 @@ public:
void applyTweak(PathRef File, Range Sel, StringRef ID,
Callback<Tweak::Effect> CB);
/// Only for testing purposes.
/// Waits until all requests to worker thread are finished and dumps AST for
/// \p File. \p File must be in the list of added documents.
void dumpAST(PathRef File, llvm::unique_function<void(std::string)> Callback);
/// Called when an event occurs for a watched file in the workspace.
void onFileEvent(const DidChangeWatchedFilesParams &Params);
......@@ -319,13 +317,6 @@ public:
void semanticHighlights(PathRef File,
Callback<std::vector<HighlightingToken>>);
/// Runs an arbitrary action that has access to the AST of the specified file.
/// The action will execute on one of ClangdServer's internal threads.
/// The AST is only valid for the duration of the callback.
/// As with other actions, the file must have been opened.
void customAction(PathRef File, llvm::StringRef Name,
Callback<InputsAndAST> Action);
/// Returns estimated memory usage and other statistics for each of the
/// currently open files.
/// Overall memory usage of clangd may be significantly more than reported
......
......@@ -333,7 +333,8 @@ struct CodeCompletionBuilder {
return ResolvedInserted.takeError();
auto Spelled = Includes.calculateIncludePath(*ResolvedInserted, FileName);
if (!Spelled)
return error("Header not on include path");
return llvm::createStringError(llvm::inconvertibleErrorCode(),
"Header not on include path");
return std::make_pair(
std::move(*Spelled),
Includes.shouldInsertInclude(*ResolvedDeclaring, *ResolvedInserted));
......@@ -1616,52 +1617,13 @@ private:
llvm::Optional<float> fuzzyScore(const CompletionCandidate &C) {
// Macros can be very spammy, so we only support prefix completion.
if (((C.SemaResult &&
C.SemaResult->Kind == CodeCompletionResult::RK_Macro) ||
(C.IndexResult &&
C.IndexResult->SymInfo.Kind == index::SymbolKind::Macro)) &&
// We won't end up with underfull index results, as macros are sema-only.
if (C.SemaResult && C.SemaResult->Kind == CodeCompletionResult::RK_Macro &&
!C.Name.startswith_lower(Filter->pattern()))
return None;
return Filter->match(C.Name);
}
CodeCompletion::Scores
evaluateCompletion(const SymbolQualitySignals &Quality,
const SymbolRelevanceSignals &Relevance) {
using RM = CodeCompleteOptions::CodeCompletionRankingModel;
CodeCompletion::Scores Scores;
switch (Opts.RankingModel) {
case RM::Heuristics:
Scores.Quality = Quality.evaluateHeuristics();
Scores.Relevance = Relevance.evaluateHeuristics();
Scores.Total =
evaluateSymbolAndRelevance(Scores.Quality, Scores.Relevance);
// NameMatch is in fact a multiplier on total score, so rescoring is
// sound.
Scores.ExcludingName = Relevance.NameMatch
? Scores.Total / Relevance.NameMatch
: Scores.Quality;
return Scores;
case RM::DecisionForest:
Scores.Quality = 0;
Scores.Relevance = 0;
// Exponentiating DecisionForest prediction makes the score of each tree a
// multiplciative boost (like NameMatch). This allows us to weigh the
// prediciton score and NameMatch appropriately.
Scores.ExcludingName = pow(Opts.DecisionForestBase,
evaluateDecisionForest(Quality, Relevance));
// NeedsFixIts is not part of the DecisionForest as generating training
// data that needs fixits is not-feasible.
if (Relevance.NeedsFixIts)
Scores.ExcludingName *= 0.5;
// NameMatch should be a multiplier on total score to support rescoring.
Scores.Total = Relevance.NameMatch * Scores.ExcludingName;
return Scores;
}
llvm_unreachable("Unhandled CodeCompletion ranking model.");
}
// Scores a candidate and adds it to the TopN structure.
void addCandidate(TopN<ScoredBundle, ScoredBundleGreater> &Candidates,
CompletionCandidate::Bundle Bundle) {
......@@ -1669,7 +1631,6 @@ private:
SymbolRelevanceSignals Relevance;
Relevance.Context = CCContextKind;
Relevance.Name = Bundle.front().Name;
Relevance.FilterLength = HeuristicPrefix.Name.size();
Relevance.Query = SymbolRelevanceSignals::CodeComplete;
Relevance.FileProximityMatch = FileProximity.getPointer();
if (ScopeProximity)
......@@ -1718,7 +1679,15 @@ private:
}
}
CodeCompletion::Scores Scores = evaluateCompletion(Quality, Relevance);
CodeCompletion::Scores Scores;
Scores.Quality = Quality.evaluate();
Scores.Relevance = Relevance.evaluate();
Scores.Total = evaluateSymbolAndRelevance(Scores.Quality, Scores.Relevance);
// NameMatch is in fact a multiplier on total score, so rescoring is sound.
Scores.ExcludingName = Relevance.NameMatch
? Scores.Total / Relevance.NameMatch
: Scores.Quality;
if (Opts.RecordCCResult)
Opts.RecordCCResult(toCodeCompletion(Bundle), Quality, Relevance,
Scores.Total);
......
......@@ -147,22 +147,6 @@ struct CodeCompleteOptions {
std::function<void(const CodeCompletion &, const SymbolQualitySignals &,
const SymbolRelevanceSignals &, float Score)>
RecordCCResult;
/// Model to use for ranking code completion candidates.
enum CodeCompletionRankingModel {
Heuristics,
DecisionForest,
} RankingModel = Heuristics;
/// Weight for combining NameMatch and Prediction of DecisionForest.
/// CompletionScore is NameMatch * pow(Base, Prediction).
/// The optimal value of Base largely depends on the semantics of the model
/// and prediction score (e.g. algorithm used during training, number of
/// trees, etc.). Usually if the range of Prediciton is [-20, 20] then a Base
/// in [1.2, 1.7] works fine.
/// Semantics: E.g. the completion score reduces by 50% if the Prediciton
/// score is reduced by 2.6 points for Base = 1.3.
float DecisionForestBase = 1.3f;
};
// Semi-structured representation of a code-complete suggestion for our C++ API.
......
......@@ -12,7 +12,6 @@
#include "clang/AST/RawCommentList.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Sema/CodeCompleteConsumer.h"
#include "llvm/Support/JSON.h"
#include <limits>
#include <utility>
......@@ -87,12 +86,7 @@ std::string getDeclComment(const ASTContext &Ctx, const NamedDecl &Decl) {
assert(!Ctx.getSourceManager().isLoadedSourceLocation(RC->getBeginLoc()));
std::string Doc =
RC->getFormattedText(Ctx.getSourceManager(), Ctx.getDiagnostics());
if (!looksLikeDocComment(Doc))
return "";
// Clang requires source to be UTF-8, but doesn't enforce this in comments.
if (!llvm::json::isUTF8(Doc))
Doc = llvm::json::fixUTF8(Doc);
return Doc;
return looksLikeDocComment(Doc) ? Doc : "";
}
void getSignature(const CodeCompletionString &CCS, std::string *Signature,
......
......@@ -271,7 +271,6 @@ std::pair<unsigned, unsigned> getArgCount(const llvm::opt::Option &Opt) {
case Option::RemainingArgsJoinedClass:
return {Rest, Rest};
}
llvm_unreachable("Unhandled option kind");
}
// Flag-parsing mode, which affects which flags are available.
......@@ -322,7 +321,7 @@ unsigned char getModes(const llvm::opt::Option &Opt) {
}
}
return Result;
}
};
} // namespace
......@@ -476,7 +475,7 @@ void ArgStripper::process(std::vector<std::string> &Args) const {
bool WasXclang = false;
while (Read < Args.size()) {
unsigned ArgCount = 0;
if (matchingRule(Args[Read], CurrentMode, ArgCount)) {
if (const Rule *R = matchingRule(Args[Read], CurrentMode, ArgCount)) {
// Delete it and its args.
if (WasXclang) {
assert(Write > 0);
......
......@@ -78,8 +78,6 @@ buildCompilerInvocation(const ParseInputs &Inputs, clang::DiagnosticConsumer &D,
CI->getPreprocessorOpts().PCHThroughHeader.clear();
CI->getPreprocessorOpts().PCHWithHdrStop = false;
CI->getPreprocessorOpts().PCHWithHdrStopCreate = false;
// Don't crash on `#pragma clang __debug parser_crash`
CI->getPreprocessorOpts().DisablePragmaDebugCrash = true;
// Recovery expression currently only works for C++.
if (CI->getLangOpts()->CPlusPlus) {
......
......@@ -62,14 +62,6 @@ struct Config {
/// Whether this TU should be indexed.
BackgroundPolicy Background = BackgroundPolicy::Build;
} Index;
/// Style of the codebase.
struct {
// Namespaces that should always be fully qualified, meaning no "using"
// declarations, always spell out the whole name (with or without leading
// ::). All nested namespaces are affected as well.
std::vector<std::string> FullyQualifiedNamespaces;
} Style;
};
} // namespace clangd
......
......@@ -208,25 +208,6 @@ struct FragmentCompiler {
}
}
void compile(Fragment::StyleBlock &&F) {
if (!F.FullyQualifiedNamespaces.empty()) {
std::vector<std::string> FullyQualifiedNamespaces;
for (auto &N : F.FullyQualifiedNamespaces) {
// Normalize the data by dropping both leading and trailing ::
StringRef Namespace(*N);
Namespace.consume_front("::");
Namespace.consume_back("::");
FullyQualifiedNamespaces.push_back(Namespace.str());
}
Out.Apply.push_back([FullyQualifiedNamespaces(
std::move(FullyQualifiedNamespaces))](Config &C) {
C.Style.FullyQualifiedNamespaces.insert(
C.Style.FullyQualifiedNamespaces.begin(),
FullyQualifiedNamespaces.begin(), FullyQualifiedNamespaces.end());
});
}
}
constexpr static llvm::SourceMgr::DiagKind Error = llvm::SourceMgr::DK_Error;
constexpr static llvm::SourceMgr::DiagKind Warning =
llvm::SourceMgr::DK_Warning;
......
......@@ -161,16 +161,6 @@ struct Fragment {
llvm::Optional<Located<std::string>> Background;
};
IndexBlock Index;
// Describes the style of the codebase, beyond formatting.
struct StyleBlock {
// Namespaces that should always be fully qualified, meaning no "using"
// declarations, always spell out the whole name (with or without leading
// ::). All nested namespaces are affected as well.
// Affects availability of the AddUsing tweak.
std::vector<Located<std::string>> FullyQualifiedNamespaces;
};
StyleBlock Style;
};
} // namespace config
......
......@@ -209,11 +209,7 @@ Provider::combine(std::vector<const Provider *> Providers) {
};
auto Result = std::make_unique<CombinedProvider>();
Result->Providers = std::move(Providers);
// FIXME: This is a workaround for a bug in older versions of clang (< 3.9)
// The constructor that is supposed to allow for Derived to Base
// conversion does not work. Remove this if we drop support for such
// configurations.
return std::unique_ptr<Provider>(Result.release());
return Result;
}
Config Provider::getConfig(const Params &P, DiagnosticCallback DC) const {
......
......@@ -39,7 +39,6 @@ public:
Dict.handle("If", [&](Node &N) { parse(F.If, N); });
Dict.handle("CompileFlags", [&](Node &N) { parse(F.CompileFlags, N); });
Dict.handle("Index", [&](Node &N) { parse(F.Index, N); });
Dict.handle("Style", [&](Node &N) { parse(F.Style, N); });
Dict.parse(N);
return !(N.failed() || HadError);
}
......@@ -73,15 +72,6 @@ private:
Dict.parse(N);
}
void parse(Fragment::StyleBlock &F, Node &N) {
DictParser Dict("Style", this);
Dict.handle("FullyQualifiedNamespaces", [&](Node &N) {
if (auto Values = scalarValues(N))
F.FullyQualifiedNamespaces = std::move(*Values);
});
Dict.parse(N);
}
void parse(Fragment::IndexBlock &F, Node &N) {
DictParser Dict("Index", this);
Dict.handle("Background",
......
......@@ -411,8 +411,6 @@ void toLSPDiags(
Main.codeActions.emplace();
for (const auto &Fix : D.Fixes)
Main.codeActions->push_back(toCodeAction(Fix, File));
if (Main.codeActions->size() == 1)
Main.codeActions->front().isPreferred = true;
}
if (Opts.SendDiagnosticCategory && !D.Category.empty())
Main.category = D.Category;
......@@ -520,17 +518,13 @@ std::vector<Diag> StoreDiags::take(const clang::tidy::ClangTidyContext *Tidy) {
}
void StoreDiags::BeginSourceFile(const LangOptions &Opts,
const Preprocessor *PP) {
const Preprocessor *) {
LangOpts = Opts;
if (PP) {
OrigSrcMgr = &PP->getSourceManager();
}
}
void StoreDiags::EndSourceFile() {
flushLastDiag();
LangOpts = None;
OrigSrcMgr = nullptr;
}
/// Sanitizes a piece for presenting it in a synthesized fix message. Ensures
......@@ -566,16 +560,6 @@ static void fillNonLocationData(DiagnosticsEngine::Level DiagLevel,
void StoreDiags::HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,
const clang::Diagnostic &Info) {
// If the diagnostic was generated for a different SourceManager, skip it.
// This happens when a module is imported and needs to be implicitly built.
// The compilation of that module will use the same StoreDiags, but different
// SourceManager.
if (OrigSrcMgr && Info.hasSourceManager() &&
OrigSrcMgr != &Info.getSourceManager()) {
IgnoreDiagnostics::log(DiagLevel, Info);
return;
}
DiagnosticConsumer::HandleDiagnostic(DiagLevel, Info);
bool OriginallyError =
Info.getDiags()->getDiagnosticIDs()->isDefaultMappingAsError(
......
......@@ -122,8 +122,7 @@ public:
// The ClangTidyContext populates Source and Name for clang-tidy diagnostics.
std::vector<Diag> take(const clang::tidy::ClangTidyContext *Tidy = nullptr);
void BeginSourceFile(const LangOptions &Opts,
const Preprocessor *PP) override;
void BeginSourceFile(const LangOptions &Opts, const Preprocessor *) override;
void EndSourceFile() override;
void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,
const clang::Diagnostic &Info) override;
......@@ -149,7 +148,6 @@ private:
llvm::Optional<Diag> LastDiag;
llvm::Optional<FullSourceLoc> LastDiagLoc; // Valid only when LastDiag is set.
bool LastDiagOriginallyError = false; // Valid only when LastDiag is set.
SourceManager *OrigSrcMgr = nullptr;
llvm::DenseSet<std::pair<unsigned, unsigned>> IncludedErrorLocations;
bool LastPrimaryDiagnosticWasSuppressed = false;
......
......@@ -64,9 +64,9 @@ llvm::Expected<DraftStore::Draft> DraftStore::updateDraft(
auto EntryIt = Drafts.find(File);
if (EntryIt == Drafts.end()) {
return error(llvm::errc::invalid_argument,
"Trying to do incremental update on non-added document: {0}",
File);
return llvm::make_error<llvm::StringError>(
"Trying to do incremental update on non-added document: " + File,
llvm::errc::invalid_argument);
}
Draft &D = EntryIt->second;
std::string Contents = EntryIt->second.Contents;
......@@ -89,9 +89,11 @@ llvm::Expected<DraftStore::Draft> DraftStore::updateDraft(
return EndIndex.takeError();
if (*EndIndex < *StartIndex)
return error(llvm::errc::invalid_argument,
"Range's end position ({0}) is before start position ({1})",
End, Start);
return llvm::make_error<llvm::StringError>(
llvm::formatv(
"Range's end position ({0}) is before start position ({1})", End,
Start),
llvm::errc::invalid_argument);
// Since the range length between two LSP positions is dependent on the
// contents of the buffer we compute the range length between the start and
......@@ -104,10 +106,11 @@ llvm::Expected<DraftStore::Draft> DraftStore::updateDraft(
lspLength(Contents.substr(*StartIndex, *EndIndex - *StartIndex));
if (Change.rangeLength && ComputedRangeLength != *Change.rangeLength)
return error(llvm::errc::invalid_argument,
"Change's rangeLength ({0}) doesn't match the "
"computed range length ({1}).",
*Change.rangeLength, ComputedRangeLength);
return llvm::make_error<llvm::StringError>(
llvm::formatv("Change's rangeLength ({0}) doesn't match the "
"computed range length ({1}).",
*Change.rangeLength, ComputedRangeLength),
llvm::errc::invalid_argument);
std::string NewContents;
NewContents.reserve(*StartIndex + Change.text.length() +
......
#define CLANGD_BUILD_XPC @CLANGD_BUILD_XPC@
#define CLANGD_ENABLE_REMOTE @CLANGD_ENABLE_REMOTE@
......
......@@ -43,9 +43,12 @@ struct ScoredSymbolGreater {
llvm::Expected<Location> indexToLSPLocation(const SymbolLocation &Loc,
llvm::StringRef TUPath) {
auto Path = URI::resolve(Loc.FileURI, TUPath);
if (!Path)
return error("Could not resolve path for file '{0}': {1}", Loc.FileURI,
Path.takeError());
if (!Path) {
return llvm::make_error<llvm::StringError>(
llvm::formatv("Could not resolve path for file '{0}': {1}", Loc.FileURI,
llvm::toString(Path.takeError())),
llvm::inconvertibleErrorCode());
}
Location L;
L.uri = URIForFile::canonicalize(*Path, TUPath);
Position Start, End;
......@@ -116,8 +119,8 @@ getWorkspaceSymbols(llvm::StringRef Query, int Limit,
return;
}
Relevance.merge(Sym);
auto Score = evaluateSymbolAndRelevance(Quality.evaluateHeuristics(),
Relevance.evaluateHeuristics());
auto Score =
evaluateSymbolAndRelevance(Quality.evaluate(), Relevance.evaluate());
dlog("FindSymbols: {0}{1} = {2}\n{3}{4}\n", Sym.Scope, Sym.Name, Score,
Quality, Relevance);
......@@ -185,7 +188,7 @@ public:
}
private:
enum class VisitKind { No, OnlyDecl, OnlyChildren, DeclAndChildren };
enum class VisitKind { No, OnlyDecl, DeclAndChildren };
void traverseDecl(Decl *D, std::vector<DocumentSymbol> &Results) {
if (auto *Templ = llvm::dyn_cast<TemplateDecl>(D)) {
......@@ -193,25 +196,18 @@ private:
if (auto *TD = Templ->getTemplatedDecl())
D = TD;
}
VisitKind Visit = shouldVisit(D);
auto *ND = llvm::dyn_cast<NamedDecl>(D);
if (!ND)
return;
VisitKind Visit = shouldVisit(ND);
if (Visit == VisitKind::No)
return;
if (Visit == VisitKind::OnlyChildren)
return traverseChildren(D, Results);
auto *ND = llvm::cast<NamedDecl>(D);
auto Sym = declToSym(AST.getASTContext(), *ND);
llvm::Optional<DocumentSymbol> Sym = declToSym(AST.getASTContext(), *ND);
if (!Sym)
return;
if (Visit == VisitKind::DeclAndChildren)
traverseChildren(D, Sym->children);
Results.push_back(std::move(*Sym));
if (Visit == VisitKind::OnlyDecl)
return;
assert(Visit == VisitKind::DeclAndChildren && "Unexpected VisitKind");
traverseChildren(ND, Results.back().children);
}
void traverseChildren(Decl *D, std::vector<DocumentSymbol> &Results) {
......@@ -222,16 +218,10 @@ private:
traverseDecl(C, Results);
}
VisitKind shouldVisit(Decl *D) {
VisitKind shouldVisit(NamedDecl *D) {
if (D->isImplicit())
return VisitKind::No;
if (llvm::isa<LinkageSpecDecl>(D) || llvm::isa<ExportDecl>(D))
return VisitKind::OnlyChildren;
if (!llvm::isa<NamedDecl>(D))
return VisitKind::No;
if (auto Func = llvm::dyn_cast<FunctionDecl>(D)) {
// Some functions are implicit template instantiations, those should be
// ignored.
......
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 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.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff 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 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.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff 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.