diff --git a/clang-tools-extra/clang-doc/Generators.cpp b/clang-tools-extra/clang-doc/Generators.cpp index ec7133466f2e9..3b7dcf93411af 100644 --- a/clang-tools-extra/clang-doc/Generators.cpp +++ b/clang-tools-extra/clang-doc/Generators.cpp @@ -27,20 +27,6 @@ findGeneratorByName(llvm::StringRef Format) { // Enum conversion -std::string getAccess(AccessSpecifier AS) { - switch (AS) { - case AccessSpecifier::AS_public: - return "public"; - case AccessSpecifier::AS_protected: - return "protected"; - case AccessSpecifier::AS_private: - return "private"; - case AccessSpecifier::AS_none: - return {}; - } - llvm_unreachable("Unknown AccessSpecifier"); -} - std::string getTagType(TagTypeKind AS) { switch (AS) { case TagTypeKind::TTK_Class: diff --git a/clang-tools-extra/clang-doc/Generators.h b/clang-tools-extra/clang-doc/Generators.h index 799d503b10231..89c6b34c43844 100644 --- a/clang-tools-extra/clang-doc/Generators.h +++ b/clang-tools-extra/clang-doc/Generators.h @@ -42,8 +42,6 @@ typedef llvm::Registry GeneratorRegistry; llvm::Expected> findGeneratorByName(llvm::StringRef Format); -std::string getAccess(AccessSpecifier AS); - std::string getTagType(TagTypeKind AS); } // namespace doc diff --git a/clang-tools-extra/clang-doc/HTMLGenerator.cpp b/clang-tools-extra/clang-doc/HTMLGenerator.cpp index dc569e2a482c7..49ff36a02be7f 100644 --- a/clang-tools-extra/clang-doc/HTMLGenerator.cpp +++ b/clang-tools-extra/clang-doc/HTMLGenerator.cpp @@ -402,7 +402,7 @@ genRecordMembersBlock(const llvm::SmallVector &Members, Out.emplace_back(std::make_unique(HTMLTag::TAG_UL)); auto &ULBody = Out.back(); for (const auto &M : Members) { - std::string Access = getAccess(M.Access); + std::string Access = getAccessSpelling(M.Access).str(); if (Access != "") Access = Access + " "; auto LIBody = std::make_unique(HTMLTag::TAG_LI); @@ -679,7 +679,7 @@ genHTML(const FunctionInfo &I, const ClangDocContext &CDCtx, Out.emplace_back(std::make_unique(HTMLTag::TAG_P)); auto &FunctionHeader = Out.back(); - std::string Access = getAccess(I.Access); + std::string Access = getAccessSpelling(I.Access).str(); if (Access != "") FunctionHeader->Children.emplace_back( std::make_unique(Access + " ")); diff --git a/clang-tools-extra/clang-doc/MDGenerator.cpp b/clang-tools-extra/clang-doc/MDGenerator.cpp index 9ad71e435a70c..58c2de96b298c 100644 --- a/clang-tools-extra/clang-doc/MDGenerator.cpp +++ b/clang-tools-extra/clang-doc/MDGenerator.cpp @@ -157,7 +157,7 @@ static void genMarkdown(const ClangDocContext &CDCtx, const FunctionInfo &I, First = false; } writeHeader(I.Name, 3, OS); - std::string Access = getAccess(I.Access); + std::string Access = getAccessSpelling(I.Access).str(); if (Access != "") writeLine(genItalic(Access + " " + I.ReturnType.Type.Name + " " + I.Name + "(" + Stream.str() + ")"), @@ -250,7 +250,7 @@ static void genMarkdown(const ClangDocContext &CDCtx, const RecordInfo &I, if (!I.Members.empty()) { writeHeader("Members", 2, OS); for (const auto &Member : I.Members) { - std::string Access = getAccess(Member.Access); + std::string Access = getAccessSpelling(Member.Access).str(); if (Access != "") writeLine(Access + " " + Member.Type.Name + " " + Member.Name, OS); else diff --git a/clang-tools-extra/clang-query/tool/ClangQuery.cpp b/clang-tools-extra/clang-query/tool/ClangQuery.cpp index 5cfa0acf9120a..0c471def2e140 100644 --- a/clang-tools-extra/clang-query/tool/ClangQuery.cpp +++ b/clang-tools-extra/clang-query/tool/ClangQuery.cpp @@ -35,6 +35,7 @@ #include "llvm/Support/CommandLine.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/Signals.h" +#include "llvm/Support/WithColor.h" #include #include @@ -86,7 +87,14 @@ bool runCommandsInFile(const char *ExeName, std::string const &FileName, int main(int argc, const char **argv) { llvm::sys::PrintStackTraceOnErrorSignal(argv[0]); - CommonOptionsParser OptionsParser(argc, argv, ClangQueryCategory); + llvm::Expected OptionsParser = + CommonOptionsParser::create(argc, argv, ClangQueryCategory, + llvm::cl::OneOrMore); + + if (!OptionsParser) { + llvm::WithColor::error() << llvm::toString(OptionsParser.takeError()); + return 1; + } if (!Commands.empty() && !CommandFiles.empty()) { llvm::errs() << argv[0] << ": cannot specify both -c and -f\n"; @@ -99,8 +107,8 @@ int main(int argc, const char **argv) { return 1; } - ClangTool Tool(OptionsParser.getCompilations(), - OptionsParser.getSourcePathList()); + ClangTool Tool(OptionsParser->getCompilations(), + OptionsParser->getSourcePathList()); std::vector> ASTs; int Status = Tool.buildASTs(ASTs); int ASTStatus = 0; diff --git a/clang-tools-extra/clang-tidy/abseil/AbseilTidyModule.cpp b/clang-tools-extra/clang-tidy/abseil/AbseilTidyModule.cpp index c70ef9007fbd1..7d592d7e3e559 100644 --- a/clang-tools-extra/clang-tidy/abseil/AbseilTidyModule.cpp +++ b/clang-tools-extra/clang-tidy/abseil/AbseilTidyModule.cpp @@ -21,8 +21,9 @@ #include "NoInternalDependenciesCheck.h" #include "NoNamespaceCheck.h" #include "RedundantStrcatCallsCheck.h" -#include "StringFindStartswithCheck.h" #include "StrCatAppendCheck.h" +#include "StringFindStartswithCheck.h" +#include "StringFindStrContainsCheck.h" #include "TimeComparisonCheck.h" #include "TimeSubtractionCheck.h" #include "UpgradeDurationConversionsCheck.h" @@ -61,6 +62,8 @@ class AbseilModule : public ClangTidyModule { "abseil-str-cat-append"); CheckFactories.registerCheck( "abseil-string-find-startswith"); + CheckFactories.registerCheck( + "abseil-string-find-str-contains"); CheckFactories.registerCheck( "abseil-time-comparison"); CheckFactories.registerCheck( diff --git a/clang-tools-extra/clang-tidy/abseil/CMakeLists.txt b/clang-tools-extra/clang-tidy/abseil/CMakeLists.txt index bd8865ef92690..5926717c6c0a6 100644 --- a/clang-tools-extra/clang-tidy/abseil/CMakeLists.txt +++ b/clang-tools-extra/clang-tidy/abseil/CMakeLists.txt @@ -20,6 +20,7 @@ add_clang_library(clangTidyAbseilModule RedundantStrcatCallsCheck.cpp StrCatAppendCheck.cpp StringFindStartswithCheck.cpp + StringFindStrContainsCheck.cpp TimeComparisonCheck.cpp TimeSubtractionCheck.cpp UpgradeDurationConversionsCheck.cpp @@ -32,4 +33,5 @@ add_clang_library(clangTidyAbseilModule clangTidy clangTidyUtils clangTooling + clangTransformer ) diff --git a/clang-tools-extra/clang-tidy/abseil/StringFindStrContainsCheck.cpp b/clang-tools-extra/clang-tidy/abseil/StringFindStrContainsCheck.cpp new file mode 100644 index 0000000000000..f60ce20007661 --- /dev/null +++ b/clang-tools-extra/clang-tidy/abseil/StringFindStrContainsCheck.cpp @@ -0,0 +1,110 @@ +//===--- StringFindStrContainsCheck.cc - 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 "StringFindStrContainsCheck.h" + +#include "../utils/OptionsUtils.h" +#include "clang/AST/ASTContext.h" +#include "clang/ASTMatchers/ASTMatchers.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Tooling/Transformer/RewriteRule.h" +#include "clang/Tooling/Transformer/Stencil.h" + +using namespace clang::ast_matchers; + +namespace clang { +namespace tidy { +namespace abseil { + +using ::clang::transformer::applyFirst; +using ::clang::transformer::cat; +using ::clang::transformer::change; +using ::clang::transformer::makeRule; +using ::clang::transformer::node; + +static const char DefaultStringLikeClasses[] = "::std::basic_string;" + "::std::basic_string_view;" + "::absl::string_view"; +static const char DefaultAbseilStringsMatchHeader[] = "absl/strings/match.h"; + +static llvm::Optional +MakeRule(const LangOptions &LangOpts, + const ClangTidyCheck::OptionsView &Options) { + // Parse options. + // + // FIXME(tdl-g): These options are being parsed redundantly with the + // constructor because TransformerClangTidyCheck forces us to provide MakeRule + // before "this" is fully constructed, but StoreOptions requires us to store + // the parsed options in "this". We need to fix TransformerClangTidyCheck and + // then we can clean this up. + const std::vector StringLikeClassNames = + utils::options::parseStringList( + Options.get("StringLikeClasses", DefaultStringLikeClasses)); + const std::string AbseilStringsMatchHeader = + Options.get("AbseilStringsMatchHeader", DefaultAbseilStringsMatchHeader); + + auto StringLikeClass = cxxRecordDecl(hasAnyName(SmallVector( + StringLikeClassNames.begin(), StringLikeClassNames.end()))); + auto StringType = + hasUnqualifiedDesugaredType(recordType(hasDeclaration(StringLikeClass))); + auto CharStarType = + hasUnqualifiedDesugaredType(pointerType(pointee(isAnyCharacter()))); + auto StringNpos = declRefExpr( + to(varDecl(hasName("npos"), hasDeclContext(StringLikeClass)))); + auto StringFind = cxxMemberCallExpr( + callee(cxxMethodDecl( + hasName("find"), + hasParameter(0, parmVarDecl(anyOf(hasType(StringType), + hasType(CharStarType)))))), + on(hasType(StringType)), hasArgument(0, expr().bind("parameter_to_find")), + anyOf(hasArgument(1, integerLiteral(equals(0))), + hasArgument(1, cxxDefaultArgExpr())), + onImplicitObjectArgument(expr().bind("string_being_searched"))); + + tooling::RewriteRule rule = applyFirst( + {makeRule(binaryOperator(hasOperatorName("=="), + hasOperands(ignoringParenImpCasts(StringNpos), + ignoringParenImpCasts(StringFind))), + change(cat("!absl::StrContains(", node("string_being_searched"), + ", ", node("parameter_to_find"), ")")), + cat("use !absl::StrContains instead of find() == npos")), + makeRule(binaryOperator(hasOperatorName("!="), + hasOperands(ignoringParenImpCasts(StringNpos), + ignoringParenImpCasts(StringFind))), + change(cat("absl::StrContains(", node("string_being_searched"), + ", ", node("parameter_to_find"), ")")), + cat("use absl::StrContains instead of find() != npos"))}); + addInclude(rule, AbseilStringsMatchHeader); + return rule; +} + +StringFindStrContainsCheck::StringFindStrContainsCheck( + StringRef Name, ClangTidyContext *Context) + : TransformerClangTidyCheck(&MakeRule, Name, Context), + StringLikeClassesOption(utils::options::parseStringList( + Options.get("StringLikeClasses", DefaultStringLikeClasses))), + AbseilStringsMatchHeaderOption(Options.get( + "AbseilStringsMatchHeader", DefaultAbseilStringsMatchHeader)) {} + +bool StringFindStrContainsCheck::isLanguageVersionSupported( + const LangOptions &LangOpts) const { + return LangOpts.CPlusPlus11; +} + +void StringFindStrContainsCheck::storeOptions( + ClangTidyOptions::OptionMap &Opts) { + TransformerClangTidyCheck::storeOptions(Opts); + Options.store(Opts, "StringLikeClasses", + utils::options::serializeStringList(StringLikeClassesOption)); + Options.store(Opts, "AbseilStringsMatchHeader", + AbseilStringsMatchHeaderOption); +} + +} // namespace abseil +} // namespace tidy +} // namespace clang diff --git a/clang-tools-extra/clang-tidy/abseil/StringFindStrContainsCheck.h b/clang-tools-extra/clang-tidy/abseil/StringFindStrContainsCheck.h new file mode 100644 index 0000000000000..351cc3784a96e --- /dev/null +++ b/clang-tools-extra/clang-tidy/abseil/StringFindStrContainsCheck.h @@ -0,0 +1,39 @@ +//===--- StringFindStrContainsCheck.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_ABSEIL_STRINGFINDSTRCONTAINSCHECK_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_ABSEIL_STRINGFINDSTRCONTAINSCHECK_H + +#include "../ClangTidy.h" +#include "../utils/TransformerClangTidyCheck.h" + +namespace clang { +namespace tidy { +namespace abseil { + +/// Finds s.find(...) == string::npos comparisons (for various string-like +/// types) and suggests replacing with absl::StrContains. +/// +/// For the user-facing documentation see: +/// http://clang.llvm.org/extra/clang-tidy/checks/abseil-string-find-str-contains.html +class StringFindStrContainsCheck : public utils::TransformerClangTidyCheck { +public: + StringFindStrContainsCheck(StringRef Name, ClangTidyContext *Context); + bool isLanguageVersionSupported(const LangOptions &LangOpts) const override; + void storeOptions(ClangTidyOptions::OptionMap &Opts) override; + +private: + const std::vector StringLikeClassesOption; + const std::string AbseilStringsMatchHeaderOption; +}; + +} // namespace abseil +} // namespace tidy +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_ABSEIL_STRINGFINDSTRCONTAINSCHECK_H diff --git a/clang-tools-extra/clang-tidy/misc/NonPrivateMemberVariablesInClassesCheck.cpp b/clang-tools-extra/clang-tidy/misc/NonPrivateMemberVariablesInClassesCheck.cpp index 12fb7d8a7ae8c..5b69885097427 100644 --- a/clang-tools-extra/clang-tidy/misc/NonPrivateMemberVariablesInClassesCheck.cpp +++ b/clang-tools-extra/clang-tidy/misc/NonPrivateMemberVariablesInClassesCheck.cpp @@ -59,8 +59,9 @@ void NonPrivateMemberVariablesInClassesCheck::registerMatchers( // If we are ok with public fields, then we only want to complain about // protected fields, else we want to complain about all non-private fields. // We can ignore public member variables in structs/classes, in unions. - auto InterestingField = fieldDecl( - IgnorePublicMemberVariables ? isProtected() : unless(isPrivate())); + auto InterestingField = IgnorePublicMemberVariables + ? fieldDecl(isProtected()) + : fieldDecl(unless(isPrivate())); // We only want the records that not only contain the mutable data (non-static // member variables), but also have some logic (non-static, non-implicit diff --git a/clang-tools-extra/clang-tidy/tool/ClangTidyMain.cpp b/clang-tools-extra/clang-tidy/tool/ClangTidyMain.cpp index 665d100268344..aca16b0d6d819 100644 --- a/clang-tools-extra/clang-tidy/tool/ClangTidyMain.cpp +++ b/clang-tools-extra/clang-tidy/tool/ClangTidyMain.cpp @@ -23,6 +23,7 @@ #include "llvm/Support/Process.h" #include "llvm/Support/Signals.h" #include "llvm/Support/TargetSelect.h" +#include "llvm/Support/WithColor.h" using namespace clang::ast_matchers; using namespace clang::driver; @@ -333,8 +334,14 @@ getVfsFromFile(const std::string &OverlayFile, int clangTidyMain(int argc, const char **argv) { llvm::InitLLVM X(argc, argv); - CommonOptionsParser OptionsParser(argc, argv, ClangTidyCategory, - cl::ZeroOrMore); + llvm::Expected OptionsParser = + CommonOptionsParser::create(argc, argv, ClangTidyCategory, + cl::ZeroOrMore); + if (!OptionsParser) { + llvm::WithColor::error() << llvm::toString(OptionsParser.takeError()); + return 1; + } + llvm::IntrusiveRefCntPtr BaseFS( new vfs::OverlayFileSystem(vfs::getRealFileSystem())); @@ -365,7 +372,7 @@ int clangTidyMain(int argc, const char **argv) { SmallString<256> ProfilePrefix = MakeAbsolute(StoreCheckProfile); StringRef FileName("dummy"); - auto PathList = OptionsParser.getSourcePathList(); + auto PathList = OptionsParser->getSourcePathList(); if (!PathList.empty()) { FileName = PathList.front(); } @@ -433,7 +440,7 @@ int clangTidyMain(int argc, const char **argv) { ClangTidyContext Context(std::move(OwningOptionsProvider), AllowEnablingAnalyzerAlphaCheckers); std::vector Errors = - runClangTidy(Context, OptionsParser.getCompilations(), PathList, BaseFS, + runClangTidy(Context, OptionsParser->getCompilations(), PathList, BaseFS, EnableCheckProfile, ProfilePrefix); bool FoundErrors = llvm::find_if(Errors, [](const ClangTidyError &E) { return E.DiagLevel == ClangTidyError::Error; diff --git a/clang-tools-extra/clang-tidy/utils/RenamerClangTidyCheck.cpp b/clang-tools-extra/clang-tidy/utils/RenamerClangTidyCheck.cpp index dd05b3a45c0d2..3301ba6343c7b 100644 --- a/clang-tools-extra/clang-tidy/utils/RenamerClangTidyCheck.cpp +++ b/clang-tools-extra/clang-tidy/utils/RenamerClangTidyCheck.cpp @@ -73,6 +73,14 @@ class RenamerClangTidyCheckPPCallbacks : public PPCallbacks { /// MacroDefined calls checkMacro for macros in the main file void MacroDefined(const Token &MacroNameTok, const MacroDirective *MD) override { + if (MD->getMacroInfo()->isBuiltinMacro()) + return; + if (PP->getSourceManager().isWrittenInBuiltinFile( + MacroNameTok.getLocation())) + return; + if (PP->getSourceManager().isWrittenInCommandLineFile( + MacroNameTok.getLocation())) + return; Check->checkMacro(PP->getSourceManager(), MacroNameTok, MD->getMacroInfo()); } diff --git a/clang-tools-extra/clangd/ClangdServer.cpp b/clang-tools-extra/clangd/ClangdServer.cpp index 910c591c1f1ec..044b37944b6ce 100644 --- a/clang-tools-extra/clangd/ClangdServer.cpp +++ b/clang-tools-extra/clangd/ClangdServer.cpp @@ -114,6 +114,7 @@ ClangdServer::Options ClangdServer::optsForTest() { Opts.StorePreamblesInMemory = true; Opts.AsyncThreadsCount = 4; // Consistent! Opts.TheiaSemanticHighlighting = true; + Opts.AsyncPreambleBuilds = true; return Opts; } @@ -123,6 +124,7 @@ ClangdServer::Options::operator TUScheduler::Options() const { Opts.RetentionPolicy = RetentionPolicy; Opts.StorePreamblesInMemory = StorePreamblesInMemory; Opts.UpdateDebounce = UpdateDebounce; + Opts.AsyncPreambleBuilds = AsyncPreambleBuilds; return Opts; } diff --git a/clang-tools-extra/clangd/ClangdServer.h b/clang-tools-extra/clangd/ClangdServer.h index 68344eb8f51e7..2c477faecedd1 100644 --- a/clang-tools-extra/clangd/ClangdServer.h +++ b/clang-tools-extra/clangd/ClangdServer.h @@ -97,6 +97,9 @@ class ClangdServer { /// Cached preambles are potentially large. If false, store them on disk. bool StorePreamblesInMemory = true; + /// Reuse even stale preambles, and rebuild them in the background. + /// This improves latency at the cost of accuracy. + bool AsyncPreambleBuilds = false; /// If true, ClangdServer builds a dynamic in-memory index for symbols in /// opened files and uses the index to augment code completion results. diff --git a/clang-tools-extra/clangd/CodeComplete.cpp b/clang-tools-extra/clangd/CodeComplete.cpp index f2bdadb0ad9dd..d2ee41f36cf4e 100644 --- a/clang-tools-extra/clangd/CodeComplete.cpp +++ b/clang-tools-extra/clangd/CodeComplete.cpp @@ -1031,7 +1031,7 @@ struct SemaCompleteInput { PathRef FileName; const tooling::CompileCommand &Command; const PreambleData &Preamble; - const PreamblePatch &Patch; + llvm::Optional Patch; llvm::StringRef Contents; size_t Offset; llvm::IntrusiveRefCntPtr VFS; @@ -1105,7 +1105,8 @@ bool semaCodeComplete(std::unique_ptr Consumer, PreambleBounds PreambleRegion = ComputePreambleBounds(*CI->getLangOpts(), ContentsBuffer.get(), 0); bool CompletingInPreamble = PreambleRegion.Size > Input.Offset; - Input.Patch.apply(*CI); + if (Input.Patch) + Input.Patch->apply(*CI); // NOTE: we must call BeginSourceFile after prepareCompilerInstance. Otherwise // the remapped buffers do not get freed. auto Clang = prepareCompilerInstance( @@ -1767,7 +1768,8 @@ codeComplete(PathRef FileName, const tooling::CompileCommand &Command, : std::move(Flow).run({FileName, Command, *Preamble, // We want to serve code completions with // low latency, so don't bother patching. - PreamblePatch(), Contents, *Offset, VFS}); + /*PreamblePatch=*/llvm::None, Contents, + *Offset, VFS}); } SignatureHelp signatureHelp(PathRef FileName, @@ -1792,10 +1794,11 @@ SignatureHelp signatureHelp(PathRef FileName, PI.CompileCommand = Command; PI.Contents = Contents.str(); PI.FS = std::move(VFS); - auto PP = PreamblePatch::create(FileName, PI, Preamble); semaCodeComplete( std::make_unique(Options, Index, Result), Options, - {FileName, Command, Preamble, PP, Contents, *Offset, std::move(PI.FS)}); + {FileName, Command, Preamble, + PreamblePatch::create(FileName, PI, Preamble), Contents, *Offset, + std::move(PI.FS)}); return Result; } diff --git a/clang-tools-extra/clangd/Headers.cpp b/clang-tools-extra/clangd/Headers.cpp index 2b9f8feb7db86..876645cd6a503 100644 --- a/clang-tools-extra/clangd/Headers.cpp +++ b/clang-tools-extra/clangd/Headers.cpp @@ -8,6 +8,7 @@ #include "Headers.h" #include "Compiler.h" +#include "Preamble.h" #include "SourceCode.h" #include "support/Logger.h" #include "clang/Basic/SourceLocation.h" @@ -23,11 +24,6 @@ namespace clang { namespace clangd { namespace { -bool isMainFile(llvm::StringRef FileName, const SourceManager &SM) { - auto FE = SM.getFileManager().getFile(FileName); - return FE && *FE == SM.getFileEntryForID(SM.getMainFileID()); -} - class RecordHeaders : public PPCallbacks { public: RecordHeaders(const SourceManager &SM, IncludeStructure *Out) @@ -44,17 +40,8 @@ class RecordHeaders : public PPCallbacks { SrcMgr::CharacteristicKind FileKind) override { auto MainFID = SM.getMainFileID(); // If an include is part of the preamble patch, translate #line directives. - if (InBuiltinFile) { - auto Presumed = SM.getPresumedLoc(HashLoc); - // Presumed locations will have an invalid file id when #line directive - // changes the filename. - if (Presumed.getFileID().isInvalid() && - isMainFile(Presumed.getFilename(), SM)) { - // Now we'll hit the case below. - HashLoc = SM.translateLineCol(MainFID, Presumed.getLine(), - Presumed.getColumn()); - } - } + if (InBuiltinFile) + HashLoc = translatePreamblePatchLocation(HashLoc, SM); // Record main-file inclusions (including those mapped from the preamble // patch). diff --git a/clang-tools-extra/clangd/Hover.cpp b/clang-tools-extra/clangd/Hover.cpp index 3d0430b57931d..1cc564be5cf53 100644 --- a/clang-tools-extra/clangd/Hover.cpp +++ b/clang-tools-extra/clangd/Hover.cpp @@ -376,8 +376,7 @@ llvm::Optional fieldName(const Expr *E) { const auto *ME = llvm::dyn_cast(E->IgnoreCasts()); if (!ME || !llvm::isa(ME->getBase()->IgnoreCasts())) return llvm::None; - const auto *Field = - llvm::dyn_cast(ME->getMemberDecl()); + const auto *Field = llvm::dyn_cast(ME->getMemberDecl()); if (!Field || !Field->getDeclName().isIdentifier()) return llvm::None; return Field->getDeclName().getAsIdentifierInfo()->getName(); @@ -468,6 +467,7 @@ HoverInfo getHoverContents(const NamedDecl *D, const SymbolIndex *Index) { HoverInfo HI; const ASTContext &Ctx = D->getASTContext(); + HI.AccessSpecifier = getAccessSpelling(D->getAccess()).str(); HI.NamespaceScope = getNamespaceScope(D); if (!HI.NamespaceScope->empty()) HI.NamespaceScope->append("::"); @@ -555,7 +555,14 @@ HoverInfo getHoverContents(const DefinedMacro &Macro, ParsedAST &AST) { // Try to get the full definition, not just the name SourceLocation StartLoc = Macro.Info->getDefinitionLoc(); SourceLocation EndLoc = Macro.Info->getDefinitionEndLoc(); - if (EndLoc.isValid()) { + // Ensure that EndLoc is a valid offset. For example it might come from + // preamble, and source file might've changed, in such a scenario EndLoc still + // stays valid, but getLocForEndOfToken will fail as it is no longer a valid + // offset. + // Note that this check is just to ensure there's text data inside the range. + // It will still succeed even when the data inside the range is irrelevant to + // macro definition. + if (SM.getPresumedLoc(EndLoc, /*UseLineDirectives=*/false).isValid()) { EndLoc = Lexer::getLocForEndOfToken(EndLoc, 0, SM, AST.getLangOpts()); bool Invalid; StringRef Buffer = SM.getBufferData(SM.getFileID(StartLoc), &Invalid); @@ -835,9 +842,12 @@ markup::Document HoverInfo::present() const { ScopeComment = "// In namespace " + llvm::StringRef(*NamespaceScope).rtrim(':').str() + '\n'; } + std::string DefinitionWithAccess = !AccessSpecifier.empty() + ? AccessSpecifier + ": " + Definition + : Definition; // Note that we don't print anything for global namespace, to not annoy // non-c++ projects or projects that are not making use of namespaces. - Output.addCodeBlock(ScopeComment + Definition); + Output.addCodeBlock(ScopeComment + DefinitionWithAccess); } return Output; } @@ -869,20 +879,20 @@ llvm::Optional getBacktickQuoteRange(llvm::StringRef Line, if (!Suffix.empty() && !AfterEndChars.contains(Suffix.front())) return llvm::None; - return Line.slice(Offset, Next+1); + return Line.slice(Offset, Next + 1); } void parseDocumentationLine(llvm::StringRef Line, markup::Paragraph &Out) { // Probably this is appendText(Line), but scan for something interesting. for (unsigned I = 0; I < Line.size(); ++I) { switch (Line[I]) { - case '`': - if (auto Range = getBacktickQuoteRange(Line, I)) { - Out.appendText(Line.substr(0, I)); - Out.appendCode(Range->trim("`"), /*Preserve=*/true); - return parseDocumentationLine(Line.substr(I+Range->size()), Out); - } - break; + case '`': + if (auto Range = getBacktickQuoteRange(Line, I)) { + Out.appendText(Line.substr(0, I)); + Out.appendCode(Range->trim("`"), /*Preserve=*/true); + return parseDocumentationLine(Line.substr(I + Range->size()), Out); + } + break; } } Out.appendText(Line).appendSpace(); diff --git a/clang-tools-extra/clangd/Hover.h b/clang-tools-extra/clangd/Hover.h index 931e1c2363a45..b712d844e33d0 100644 --- a/clang-tools-extra/clangd/Hover.h +++ b/clang-tools-extra/clangd/Hover.h @@ -59,6 +59,9 @@ struct HoverInfo { /// Source code containing the definition of the symbol. std::string Definition; + /// Access specifier for declarations inside class/struct/unions, empty for + /// others. + std::string AccessSpecifier; /// Pretty-printed variable type. /// Set only for variables. llvm::Optional Type; diff --git a/clang-tools-extra/clangd/ParsedAST.cpp b/clang-tools-extra/clangd/ParsedAST.cpp index e7678f3d69e99..082c7cae00219 100644 --- a/clang-tools-extra/clangd/ParsedAST.cpp +++ b/clang-tools-extra/clangd/ParsedAST.cpp @@ -14,6 +14,7 @@ #include "Diagnostics.h" #include "Headers.h" #include "IncludeFixer.h" +#include "Preamble.h" #include "SourceCode.h" #include "index/CanonicalIncludes.h" #include "index/Index.h" @@ -48,6 +49,7 @@ #include "llvm/Support/raw_ostream.h" #include #include +#include // Force the linker to link in Clang-tidy modules. // clangd doesn't support the static analyzer. @@ -268,6 +270,11 @@ ParsedAST::build(llvm::StringRef Filename, const ParseInputs &Inputs, StoreDiags ASTDiags; + llvm::Optional Patch; + if (Preamble) { + Patch = PreamblePatch::create(Filename, Inputs, *Preamble); + Patch->apply(*CI); + } auto Clang = prepareCompilerInstance( std::move(CI), PreamblePCH, llvm::MemoryBuffer::getMemBufferCopy(Inputs.Contents, Filename), VFS, @@ -369,12 +376,14 @@ ParsedAST::build(llvm::StringRef Filename, const ParseInputs &Inputs, Clang->setExternalSemaSource(FixIncludes->unresolvedNameRecorder()); } - // Copy over the includes from the preamble, then combine with the - // non-preamble includes below. - auto Includes = Preamble ? Preamble->Includes : IncludeStructure{}; - // Replay the preamble includes so that clang-tidy checks can see them. - if (Preamble) + IncludeStructure Includes; + // If we are using a preamble, copy existing includes. + if (Preamble) { + Includes = Preamble->Includes; + Includes.MainFileIncludes = Patch->preambleIncludes(); + // Replay the preamble includes so that clang-tidy checks can see them. ReplayPreamble::attach(Includes, *Clang, Preamble->Preamble.getBounds()); + } // Important: collectIncludeStructure is registered *after* ReplayPreamble! // Otherwise we would collect the replayed includes again... // (We can't *just* use the replayed includes, they don't have Resolved path). @@ -542,5 +551,10 @@ ParsedAST::ParsedAST(llvm::StringRef Version, assert(this->Action); } +llvm::Optional ParsedAST::preambleVersion() const { + if (!Preamble) + return llvm::None; + return llvm::StringRef(Preamble->Version); +} } // namespace clangd } // namespace clang diff --git a/clang-tools-extra/clangd/ParsedAST.h b/clang-tools-extra/clangd/ParsedAST.h index d90f77f9263b3..c01f1fa0e6d8d 100644 --- a/clang-tools-extra/clangd/ParsedAST.h +++ b/clang-tools-extra/clangd/ParsedAST.h @@ -33,6 +33,9 @@ #include "clang/Tooling/CompilationDatabase.h" #include "clang/Tooling/Syntax/Tokens.h" #include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/None.h" +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/StringRef.h" #include #include #include @@ -102,6 +105,10 @@ class ParsedAST { /// Returns the version of the ParseInputs this AST was built from. llvm::StringRef version() const { return Version; } + /// Returns the version of the ParseInputs used to build Preamble part of this + /// AST. Might be None if no Preamble is used. + llvm::Optional preambleVersion() const; + private: ParsedAST(llvm::StringRef Version, std::shared_ptr Preamble, diff --git a/clang-tools-extra/clangd/Preamble.cpp b/clang-tools-extra/clangd/Preamble.cpp index 9d9c5eff8c682..d02685f4bf46d 100644 --- a/clang-tools-extra/clangd/Preamble.cpp +++ b/clang-tools-extra/clangd/Preamble.cpp @@ -15,6 +15,7 @@ #include "clang/Basic/Diagnostic.h" #include "clang/Basic/LangOptions.h" #include "clang/Basic/SourceLocation.h" +#include "clang/Basic/SourceManager.h" #include "clang/Basic/TokenKinds.h" #include "clang/Frontend/CompilerInvocation.h" #include "clang/Frontend/FrontendActions.h" @@ -24,6 +25,8 @@ #include "clang/Lex/PreprocessorOptions.h" #include "clang/Tooling/CompilationDatabase.h" #include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/DenseSet.h" #include "llvm/ADT/IntrusiveRefCntPtr.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallString.h" @@ -47,6 +50,7 @@ namespace clang { namespace clangd { namespace { +constexpr llvm::StringLiteral PreamblePatchHeaderName = "__preamble_patch__.h"; bool compileCommandsAreEqual(const tooling::CompileCommand &LHS, const tooling::CompileCommand &RHS) { @@ -104,32 +108,110 @@ class CppFilePreambleCallbacks : public PreambleCallbacks { const SourceManager *SourceMgr = nullptr; }; -// Runs preprocessor over preamble section. -class PreambleOnlyAction : public PreprocessorFrontendAction { -protected: - void ExecuteAction() override { - Preprocessor &PP = getCompilerInstance().getPreprocessor(); - auto &SM = PP.getSourceManager(); - PP.EnterMainSourceFile(); - auto Bounds = ComputePreambleBounds(getCompilerInstance().getLangOpts(), - SM.getBuffer(SM.getMainFileID()), 0); - Token Tok; - do { - PP.Lex(Tok); - assert(SM.isInMainFile(Tok.getLocation())); - } while (Tok.isNot(tok::eof) && - SM.getDecomposedLoc(Tok.getLocation()).second < Bounds.Size); +// Represents directives other than includes, where basic textual information is +// enough. +struct TextualPPDirective { + unsigned DirectiveLine; + // Full text that's representing the directive, including the `#`. + std::string Text; + + bool operator==(const TextualPPDirective &RHS) const { + return std::tie(DirectiveLine, Text) == + std::tie(RHS.DirectiveLine, RHS.Text); } }; -/// Gets the includes in the preamble section of the file by running -/// preprocessor over \p Contents. Returned includes do not contain resolved -/// paths. \p VFS and \p Cmd is used to build the compiler invocation, which -/// might stat/read files. -llvm::Expected> -scanPreambleIncludes(llvm::StringRef Contents, - llvm::IntrusiveRefCntPtr VFS, - const tooling::CompileCommand &Cmd) { +// Formats a PP directive consisting of Prefix (e.g. "#define ") and Body ("X +// 10"). The formatting is copied so that the tokens in Body have PresumedLocs +// with correct columns and lines. +std::string spellDirective(llvm::StringRef Prefix, + CharSourceRange DirectiveRange, + const LangOptions &LangOpts, const SourceManager &SM, + unsigned &DirectiveLine) { + std::string SpelledDirective; + llvm::raw_string_ostream OS(SpelledDirective); + OS << Prefix; + + // Make sure DirectiveRange is a char range and doesn't contain macro ids. + DirectiveRange = SM.getExpansionRange(DirectiveRange); + if (DirectiveRange.isTokenRange()) { + DirectiveRange.setEnd( + Lexer::getLocForEndOfToken(DirectiveRange.getEnd(), 0, SM, LangOpts)); + } + + auto DecompLoc = SM.getDecomposedLoc(DirectiveRange.getBegin()); + DirectiveLine = SM.getLineNumber(DecompLoc.first, DecompLoc.second); + auto TargetColumn = SM.getColumnNumber(DecompLoc.first, DecompLoc.second) - 1; + + // Pad with spaces before DirectiveRange to make sure it will be on right + // column when patched. + if (Prefix.size() <= TargetColumn) { + // There is enough space for Prefix and space before directive, use it. + // We try to squeeze the Prefix into the same line whenever we can, as + // putting onto a separate line won't work at the beginning of the file. + OS << std::string(TargetColumn - Prefix.size(), ' '); + } else { + // Prefix was longer than the space we had. We produce e.g.: + // #line N-1 + // #define \ + // X 10 + OS << "\\\n" << std::string(TargetColumn, ' '); + // Decrement because we put an additional line break before + // DirectiveRange.begin(). + --DirectiveLine; + } + OS << toSourceCode(SM, DirectiveRange.getAsRange()); + return OS.str(); +} + +// Collects #define directives inside the main file. +struct DirectiveCollector : public PPCallbacks { + DirectiveCollector(const Preprocessor &PP, + std::vector &TextualDirectives) + : LangOpts(PP.getLangOpts()), SM(PP.getSourceManager()), + TextualDirectives(TextualDirectives) {} + + void FileChanged(SourceLocation Loc, FileChangeReason Reason, + SrcMgr::CharacteristicKind FileType, + FileID PrevFID) override { + InMainFile = SM.isWrittenInMainFile(Loc); + } + + void MacroDefined(const Token &MacroNameTok, + const MacroDirective *MD) override { + if (!InMainFile) + return; + TextualDirectives.emplace_back(); + TextualPPDirective &TD = TextualDirectives.back(); + + const auto *MI = MD->getMacroInfo(); + TD.Text = + spellDirective("#define ", + CharSourceRange::getTokenRange( + MI->getDefinitionLoc(), MI->getDefinitionEndLoc()), + LangOpts, SM, TD.DirectiveLine); + } + +private: + bool InMainFile = true; + const LangOptions &LangOpts; + const SourceManager &SM; + std::vector &TextualDirectives; +}; + +struct ScannedPreamble { + std::vector Includes; + std::vector TextualDirectives; +}; + +/// Scans the preprocessor directives in the preamble section of the file by +/// running preprocessor over \p Contents. Returned includes do not contain +/// resolved paths. \p VFS and \p Cmd is used to build the compiler invocation, +/// which might stat/read files. +llvm::Expected +scanPreamble(llvm::StringRef Contents, + llvm::IntrusiveRefCntPtr VFS, + const tooling::CompileCommand &Cmd) { // Build and run Preprocessor over the preamble. ParseInputs PI; PI.Contents = Contents.str(); @@ -142,8 +224,15 @@ scanPreambleIncludes(llvm::StringRef Contents, "failed to create compiler invocation"); CI->getDiagnosticOpts().IgnoreWarnings = true; auto ContentsBuffer = llvm::MemoryBuffer::getMemBuffer(Contents); + // This means we're scanning (though not preprocessing) the preamble section + // twice. However, it's important to precisely follow the preamble bounds used + // elsewhere. + auto Bounds = + ComputePreambleBounds(*CI->getLangOpts(), ContentsBuffer.get(), 0); + auto PreambleContents = + llvm::MemoryBuffer::getMemBufferCopy(Contents.substr(0, Bounds.Size)); auto Clang = prepareCompilerInstance( - std::move(CI), nullptr, std::move(ContentsBuffer), + std::move(CI), nullptr, std::move(PreambleContents), // Provide an empty FS to prevent preprocessor from performing IO. This // also implies missing resolved paths for includes. new llvm::vfs::InMemoryFileSystem, IgnoreDiags); @@ -152,18 +241,22 @@ scanPreambleIncludes(llvm::StringRef Contents, "compiler instance had no inputs"); // We are only interested in main file includes. Clang->getPreprocessorOpts().SingleFileParseMode = true; - PreambleOnlyAction Action; + PreprocessOnlyAction Action; if (!Action.BeginSourceFile(*Clang, Clang->getFrontendOpts().Inputs[0])) return llvm::createStringError(llvm::inconvertibleErrorCode(), "failed BeginSourceFile"); + const auto &SM = Clang->getSourceManager(); Preprocessor &PP = Clang->getPreprocessor(); IncludeStructure Includes; + PP.addPPCallbacks(collectIncludeStructureCallback(SM, &Includes)); + ScannedPreamble SP; PP.addPPCallbacks( - collectIncludeStructureCallback(Clang->getSourceManager(), &Includes)); + std::make_unique(PP, SP.TextualDirectives)); if (llvm::Error Err = Action.Execute()) return std::move(Err); Action.EndSourceFile(); - return Includes.MainFileIncludes; + SP.Includes = std::move(Includes.MainFileIncludes); + return SP; } const char *spellingForIncDirective(tok::PPKeywordKind IncludeDirective) { @@ -179,6 +272,13 @@ const char *spellingForIncDirective(tok::PPKeywordKind IncludeDirective) { } llvm_unreachable("not an include directive"); } + +// Checks whether \p FileName is a valid spelling of main file. +bool isMainFile(llvm::StringRef FileName, const SourceManager &SM) { + auto FE = SM.getFileManager().getFile(FileName); + return FE && *FE == SM.getFileEntryForID(SM.getMainFileID()); +} + } // namespace PreambleData::PreambleData(const ParseInputs &Inputs, @@ -285,7 +385,10 @@ void escapeBackslashAndQuotes(llvm::StringRef Text, llvm::raw_ostream &OS) { PreamblePatch PreamblePatch::create(llvm::StringRef FileName, const ParseInputs &Modified, const PreambleData &Baseline) { - // First scan the include directives in Baseline and Modified. These will be + trace::Span Tracer("CreatePreamblePatch"); + SPAN_ATTACH(Tracer, "File", FileName); + assert(llvm::sys::path::is_absolute(FileName) && "relative FileName!"); + // First scan preprocessor directives in Baseline and Modified. These will be // used to figure out newly added directives in Modified. Scanning can fail, // the code just bails out and creates an empty patch in such cases, as: // - If scanning for Baseline fails, no knowledge of existing includes hence @@ -293,41 +396,37 @@ PreamblePatch PreamblePatch::create(llvm::StringRef FileName, // whole preamble, which is terribly slow. // - If scanning for Modified fails, cannot figure out newly added ones so // there's nothing to do but generate an empty patch. - auto BaselineIncludes = scanPreambleIncludes( + auto BaselineScan = scanPreamble( // Contents needs to be null-terminated. Baseline.Preamble.getContents().str(), Baseline.StatCache->getConsumingFS(Modified.FS), Modified.CompileCommand); - if (!BaselineIncludes) { - elog("Failed to scan includes for baseline of {0}: {1}", FileName, - BaselineIncludes.takeError()); - return {}; + if (!BaselineScan) { + elog("Failed to scan baseline of {0}: {1}", FileName, + BaselineScan.takeError()); + return PreamblePatch::unmodified(Baseline); } - auto ModifiedIncludes = scanPreambleIncludes( + auto ModifiedScan = scanPreamble( Modified.Contents, Baseline.StatCache->getConsumingFS(Modified.FS), Modified.CompileCommand); - if (!ModifiedIncludes) { - elog("Failed to scan includes for modified contents of {0}: {1}", FileName, - ModifiedIncludes.takeError()); - return {}; + if (!ModifiedScan) { + elog("Failed to scan modified contents of {0}: {1}", FileName, + ModifiedScan.takeError()); + return PreamblePatch::unmodified(Baseline); } - // No patch needed if includes are equal. - if (*BaselineIncludes == *ModifiedIncludes) - return {}; + + bool IncludesChanged = BaselineScan->Includes != ModifiedScan->Includes; + bool DirectivesChanged = + BaselineScan->TextualDirectives != ModifiedScan->TextualDirectives; + if (!IncludesChanged && !DirectivesChanged) + return PreamblePatch::unmodified(Baseline); PreamblePatch PP; // This shouldn't coincide with any real file name. llvm::SmallString<128> PatchName; llvm::sys::path::append(PatchName, llvm::sys::path::parent_path(FileName), - "__preamble_patch__.h"); + PreamblePatchHeaderName); PP.PatchFileName = PatchName.str().str(); - // We are only interested in newly added includes, record the ones in Baseline - // for exclusion. - llvm::DenseSet> - ExistingIncludes; - for (const auto &Inc : *BaselineIncludes) - ExistingIncludes.insert({Inc.Directive, Inc.Written}); - // Calculate extra includes that needs to be inserted. llvm::raw_string_ostream Patch(PP.PatchContents); // Set default filename for subsequent #line directives Patch << "#line 0 \""; @@ -335,19 +434,57 @@ PreamblePatch PreamblePatch::create(llvm::StringRef FileName, // might lead to problems on windows especially. escapeBackslashAndQuotes(FileName, Patch); Patch << "\"\n"; - for (const auto &Inc : *ModifiedIncludes) { - if (ExistingIncludes.count({Inc.Directive, Inc.Written})) - continue; - // Include is new in the modified preamble. Inject it into the patch and use - // #line to set the presumed location to where it is spelled. - auto LineCol = offsetToClangLineColumn(Modified.Contents, Inc.HashOffset); - Patch << llvm::formatv("#line {0}\n", LineCol.first); - Patch << llvm::formatv("#{0} {1}\n", spellingForIncDirective(Inc.Directive), - Inc.Written); + + if (IncludesChanged) { + // We are only interested in newly added includes, record the ones in + // Baseline for exclusion. + llvm::DenseMap, + /*Resolved=*/llvm::StringRef> + ExistingIncludes; + for (const auto &Inc : Baseline.Includes.MainFileIncludes) + ExistingIncludes[{Inc.Directive, Inc.Written}] = Inc.Resolved; + // There might be includes coming from disabled regions, record these for + // exclusion too. note that we don't have resolved paths for those. + for (const auto &Inc : BaselineScan->Includes) + ExistingIncludes.try_emplace({Inc.Directive, Inc.Written}); + // Calculate extra includes that needs to be inserted. + for (auto &Inc : ModifiedScan->Includes) { + auto It = ExistingIncludes.find({Inc.Directive, Inc.Written}); + // Include already present in the baseline preamble. Set resolved path and + // put into preamble includes. + if (It != ExistingIncludes.end()) { + Inc.Resolved = It->second.str(); + PP.PreambleIncludes.push_back(Inc); + continue; + } + // Include is new in the modified preamble. Inject it into the patch and + // use #line to set the presumed location to where it is spelled. + auto LineCol = offsetToClangLineColumn(Modified.Contents, Inc.HashOffset); + Patch << llvm::formatv("#line {0}\n", LineCol.first); + Patch << llvm::formatv( + "#{0} {1}\n", spellingForIncDirective(Inc.Directive), Inc.Written); + } } - Patch.flush(); - // FIXME: Handle more directives, e.g. define/undef. + if (DirectivesChanged) { + // We need to patch all the directives, since they are order dependent. e.g: + // #define BAR(X) NEW(X) // Newly introduced in Modified + // #define BAR(X) OLD(X) // Exists in the Baseline + // + // If we've patched only the first directive, the macro definition would've + // been wrong for the rest of the file, since patch is applied after the + // baseline preamble. + // + // Note that we deliberately ignore conditional directives and undefs to + // reduce complexity. The former might cause problems because scanning is + // imprecise and might pick directives from disabled regions. + for (const auto &TD : ModifiedScan->TextualDirectives) { + Patch << "#line " << TD.DirectiveLine << '\n'; + Patch << TD.Text << '\n'; + } + } + dlog("Created preamble patch: {0}", Patch.str()); + Patch.flush(); return PP; } @@ -367,5 +504,34 @@ void PreamblePatch::apply(CompilerInvocation &CI) const { PPOpts.Includes.push_back(PatchFileName); } +std::vector PreamblePatch::preambleIncludes() const { + return PreambleIncludes; +} + +PreamblePatch PreamblePatch::unmodified(const PreambleData &Preamble) { + PreamblePatch PP; + PP.PreambleIncludes = Preamble.Includes.MainFileIncludes; + return PP; +} + +SourceLocation translatePreamblePatchLocation(SourceLocation Loc, + const SourceManager &SM) { + auto DefFile = SM.getFileID(Loc); + if (auto *FE = SM.getFileEntryForID(DefFile)) { + auto IncludeLoc = SM.getIncludeLoc(DefFile); + // Preamble patch is included inside the builtin file. + if (IncludeLoc.isValid() && SM.isWrittenInBuiltinFile(IncludeLoc) && + FE->getName().endswith(PreamblePatchHeaderName)) { + auto Presumed = SM.getPresumedLoc(Loc); + // Check that line directive is pointing at main file. + if (Presumed.isValid() && Presumed.getFileID().isInvalid() && + isMainFile(Presumed.getFilename(), SM)) { + Loc = SM.translateLineCol(SM.getMainFileID(), Presumed.getLine(), + Presumed.getColumn()); + } + } + } + return Loc; +} } // namespace clangd } // namespace clang diff --git a/clang-tools-extra/clangd/Preamble.h b/clang-tools-extra/clangd/Preamble.h index f2e1ba98f3326..35c168573b90a 100644 --- a/clang-tools-extra/clangd/Preamble.h +++ b/clang-tools-extra/clangd/Preamble.h @@ -91,12 +91,14 @@ bool isPreambleCompatible(const PreambleData &Preamble, const CompilerInvocation &CI); /// Stores information required to parse a TU using a (possibly stale) Baseline -/// preamble. Updates compiler invocation to approximately reflect additions to -/// the preamble section of Modified contents, e.g. new include directives. +/// preamble. Later on this information can be injected into the main file by +/// updating compiler invocation with \c apply. This injected section +/// approximately reflects additions to the preamble in Modified contents, e.g. +/// new include directives. class PreamblePatch { public: - // With an empty patch, the preamble is used verbatim. - PreamblePatch() = default; + /// \p Preamble is used verbatim. + static PreamblePatch unmodified(const PreambleData &Preamble); /// Builds a patch that contains new PP directives introduced to the preamble /// section of \p Modified compared to \p Baseline. /// FIXME: This only handles include directives, we should at least handle @@ -109,11 +111,31 @@ class PreamblePatch { /// \p CI that contains new directives calculated in create. void apply(CompilerInvocation &CI) const; + /// Returns #include directives from the \c Modified preamble that were + /// resolved using the \c Baseline preamble. This covers the new locations of + /// inclusions that were moved around, but not inclusions of new files. Those + /// will be recorded when parsing the main file: the includes in the injected + /// section will be resolved back to their spelled positions in the main file + /// using the presumed-location mechanism. + std::vector preambleIncludes() const; + + /// Returns textual patch contents. + llvm::StringRef text() const { return PatchContents; } + private: + PreamblePatch() = default; std::string PatchContents; std::string PatchFileName; + /// Includes that are present in both \p Baseline and \p Modified. Used for + /// patching includes of baseline preamble. + std::vector PreambleIncludes; }; +/// Translates locations inside preamble patch to their main-file equivalent +/// using presumed locations. Returns \p Loc if it isn't inside preamble patch. +SourceLocation translatePreamblePatchLocation(SourceLocation Loc, + const SourceManager &SM); + } // namespace clangd } // namespace clang diff --git a/clang-tools-extra/clangd/SourceCode.cpp b/clang-tools-extra/clangd/SourceCode.cpp index 0d08bf8e0a1ae..f6f9371d436fe 100644 --- a/clang-tools-extra/clangd/SourceCode.cpp +++ b/clang-tools-extra/clangd/SourceCode.cpp @@ -8,6 +8,7 @@ #include "SourceCode.h" #include "FuzzyMatch.h" +#include "Preamble.h" #include "Protocol.h" #include "refactor/Tweak.h" #include "support/Context.h" @@ -961,7 +962,9 @@ llvm::Optional locateMacroAt(const syntax::Token &SpelledTok, Loc = Loc.getLocWithOffset(-1); MacroDefinition MacroDef = PP.getMacroDefinitionAtLoc(IdentifierInfo, Loc); if (auto *MI = MacroDef.getMacroInfo()) - return DefinedMacro{IdentifierInfo->getName(), MI}; + return DefinedMacro{ + IdentifierInfo->getName(), MI, + translatePreamblePatchLocation(MI->getDefinitionLoc(), SM)}; return None; } diff --git a/clang-tools-extra/clangd/SourceCode.h b/clang-tools-extra/clangd/SourceCode.h index c00cc17ac9bcf..73fd9398b71d5 100644 --- a/clang-tools-extra/clangd/SourceCode.h +++ b/clang-tools-extra/clangd/SourceCode.h @@ -292,6 +292,10 @@ EligibleRegion getEligiblePoints(llvm::StringRef Code, struct DefinedMacro { llvm::StringRef Name; const MacroInfo *Info; + /// Location of the identifier that names the macro. + /// Unlike Info->Location, this translates preamble-patch locations to + /// main-file locations. + SourceLocation NameLoc; }; /// Gets the macro referenced by \p SpelledTok. It must be a spelled token /// aligned to the beginning of an identifier. diff --git a/clang-tools-extra/clangd/TUScheduler.cpp b/clang-tools-extra/clangd/TUScheduler.cpp index ee6d52188934a..b53daa251b035 100644 --- a/clang-tools-extra/clangd/TUScheduler.cpp +++ b/clang-tools-extra/clangd/TUScheduler.cpp @@ -239,10 +239,14 @@ class PreambleThread { return; } { - std::lock_guard Lock(Mutex); - // If shutdown is issued, don't bother building. - if (Done) - return; + std::unique_lock Lock(Mutex); + // If NextReq was requested with WantDiagnostics::Yes we cannot just drop + // that on the floor. Block until we start building it. This won't + // dead-lock as we are blocking the caller thread, while builds continue + // on preamble thread. + ReqCV.wait(Lock, [this] { + return !NextReq || NextReq->WantDiags != WantDiagnostics::Yes; + }); NextReq = std::move(Req); } // Let the worker thread know there's a request, notify_one is safe as there @@ -359,8 +363,7 @@ class ASTWorker { friend class ASTWorkerHandle; ASTWorker(PathRef FileName, const GlobalCompilationDatabase &CDB, TUScheduler::ASTCache &LRUCache, Semaphore &Barrier, bool RunSync, - DebouncePolicy UpdateDebounce, bool StorePreamblesInMemory, - ParsingCallbacks &Callbacks); + const TUScheduler::Options &Opts, ParsingCallbacks &Callbacks); public: /// Create a new ASTWorker and return a handle to it. @@ -368,11 +371,12 @@ class ASTWorker { /// is null, all requests will be processed on the calling thread /// synchronously instead. \p Barrier is acquired when processing each /// request, it is used to limit the number of actively running threads. - static ASTWorkerHandle - create(PathRef FileName, const GlobalCompilationDatabase &CDB, - TUScheduler::ASTCache &IdleASTs, AsyncTaskRunner *Tasks, - Semaphore &Barrier, DebouncePolicy UpdateDebounce, - bool StorePreamblesInMemory, ParsingCallbacks &Callbacks); + static ASTWorkerHandle create(PathRef FileName, + const GlobalCompilationDatabase &CDB, + TUScheduler::ASTCache &IdleASTs, + AsyncTaskRunner *Tasks, Semaphore &Barrier, + const TUScheduler::Options &Opts, + ParsingCallbacks &Callbacks); ~ASTWorker(); void update(ParseInputs Inputs, WantDiagnostics); @@ -476,6 +480,7 @@ class ASTWorker { std::queue PreambleRequests; /* GUARDED_BY(Mutex) */ llvm::Optional CurrentRequest; /* GUARDED_BY(Mutex) */ mutable std::condition_variable RequestsCV; + Notification ReceivedPreamble; /// Guards the callback that publishes results of AST-related computations /// (diagnostics, highlightings) and file statuses. std::mutex PublishMu; @@ -535,14 +540,14 @@ class ASTWorkerHandle { std::shared_ptr Worker; }; -ASTWorkerHandle -ASTWorker::create(PathRef FileName, const GlobalCompilationDatabase &CDB, - TUScheduler::ASTCache &IdleASTs, AsyncTaskRunner *Tasks, - Semaphore &Barrier, DebouncePolicy UpdateDebounce, - bool StorePreamblesInMemory, ParsingCallbacks &Callbacks) { - std::shared_ptr Worker( - new ASTWorker(FileName, CDB, IdleASTs, Barrier, /*RunSync=*/!Tasks, - UpdateDebounce, StorePreamblesInMemory, Callbacks)); +ASTWorkerHandle ASTWorker::create(PathRef FileName, + const GlobalCompilationDatabase &CDB, + TUScheduler::ASTCache &IdleASTs, + AsyncTaskRunner *Tasks, Semaphore &Barrier, + const TUScheduler::Options &Opts, + ParsingCallbacks &Callbacks) { + std::shared_ptr Worker(new ASTWorker( + FileName, CDB, IdleASTs, Barrier, /*RunSync=*/!Tasks, Opts, Callbacks)); if (Tasks) { Tasks->runAsync("ASTWorker:" + llvm::sys::path::filename(FileName), [Worker]() { Worker->run(); }); @@ -555,15 +560,13 @@ ASTWorker::create(PathRef FileName, const GlobalCompilationDatabase &CDB, ASTWorker::ASTWorker(PathRef FileName, const GlobalCompilationDatabase &CDB, TUScheduler::ASTCache &LRUCache, Semaphore &Barrier, - bool RunSync, DebouncePolicy UpdateDebounce, - bool StorePreamblesInMemory, ParsingCallbacks &Callbacks) - : IdleASTs(LRUCache), RunSync(RunSync), UpdateDebounce(UpdateDebounce), + bool RunSync, const TUScheduler::Options &Opts, + ParsingCallbacks &Callbacks) + : IdleASTs(LRUCache), RunSync(RunSync), UpdateDebounce(Opts.UpdateDebounce), FileName(FileName), CDB(CDB), Callbacks(Callbacks), Barrier(Barrier), Done(false), Status(FileName, Callbacks), - PreamblePeer(FileName, Callbacks, StorePreamblesInMemory, - // FIXME: Run PreamblePeer asynchronously once ast patching - // is available. - /*RunSync=*/true, Status, *this) { + PreamblePeer(FileName, Callbacks, Opts.StorePreamblesInMemory, + RunSync || !Opts.AsyncPreambleBuilds, Status, *this) { // Set a fallback command because compile command can be accessed before // `Inputs` is initialized. Other fields are only used after initialization // from client inputs. @@ -648,6 +651,12 @@ void ASTWorker::update(ParseInputs Inputs, WantDiagnostics WantDiags) { PreamblePeer.update(std::move(Invocation), std::move(Inputs), std::move(CompilerInvocationDiags), WantDiags); + // Block until first preamble is ready, as patching an empty preamble would + // imply rebuilding it from scratch. + // This isn't the natural place to block, rather where the preamble would be + // consumed. But that's too late, we'd be running on the worker thread with + // the PreambleTask scheduled and so we'd deadlock. + ReceivedPreamble.wait(); return; }; startTask(TaskName, std::move(Task), WantDiags, TUScheduler::NoInvalidation); @@ -771,6 +780,7 @@ void ASTWorker::updatePreamble(std::unique_ptr CI, }; if (RunSync) { Task(); + ReceivedPreamble.notify(); return; } { @@ -779,6 +789,7 @@ void ASTWorker::updatePreamble(std::unique_ptr CI, steady_clock::now(), Context::current().clone(), llvm::None, TUScheduler::NoInvalidation, nullptr}); } + ReceivedPreamble.notify(); RequestsCV.notify_all(); } @@ -915,6 +926,7 @@ void ASTWorker::stop() { Done = true; } // We are no longer going to build any preambles, let the waiters know that. + ReceivedPreamble.notify(); BuiltFirstPreamble.notify(); PreamblePeer.stop(); Status.stop(); @@ -1117,10 +1129,24 @@ bool ASTWorker::shouldSkipHeadLocked() const { } bool ASTWorker::blockUntilIdle(Deadline Timeout) const { - std::unique_lock Lock(Mutex); - return wait(Lock, RequestsCV, Timeout, [&] { - return PreambleRequests.empty() && Requests.empty() && !CurrentRequest; - }); + auto WaitUntilASTWorkerIsIdle = [&] { + std::unique_lock Lock(Mutex); + return wait(Lock, RequestsCV, Timeout, [&] { + return PreambleRequests.empty() && Requests.empty() && !CurrentRequest; + }); + }; + // Make sure ASTWorker has processed all requests, which might issue new + // updates to PreamblePeer. + WaitUntilASTWorkerIsIdle(); + // Now that ASTWorker processed all requests, ensure PreamblePeer has served + // all update requests. This might create new PreambleRequests for the + // ASTWorker. + PreamblePeer.blockUntilIdle(Timeout); + assert(Requests.empty() && + "No new normal tasks can be scheduled concurrently with " + "blockUntilIdle(): ASTWorker isn't threadsafe"); + // Finally make sure ASTWorker has processed all of the preamble updates. + return WaitUntilASTWorkerIsIdle(); } // Render a TUAction to a user-facing string representation. @@ -1178,13 +1204,12 @@ struct TUScheduler::FileData { TUScheduler::TUScheduler(const GlobalCompilationDatabase &CDB, const Options &Opts, std::unique_ptr Callbacks) - : CDB(CDB), StorePreamblesInMemory(Opts.StorePreamblesInMemory), + : CDB(CDB), Opts(Opts), Callbacks(Callbacks ? move(Callbacks) : std::make_unique()), Barrier(Opts.AsyncThreadsCount), IdleASTs( - std::make_unique(Opts.RetentionPolicy.MaxRetainedASTs)), - UpdateDebounce(Opts.UpdateDebounce) { + std::make_unique(Opts.RetentionPolicy.MaxRetainedASTs)) { if (0 < Opts.AsyncThreadsCount) { PreambleTasks.emplace(); WorkerThreads.emplace(); @@ -1218,10 +1243,10 @@ bool TUScheduler::update(PathRef File, ParseInputs Inputs, bool NewFile = FD == nullptr; if (!FD) { // Create a new worker to process the AST-related tasks. - ASTWorkerHandle Worker = ASTWorker::create( - File, CDB, *IdleASTs, - WorkerThreads ? WorkerThreads.getPointer() : nullptr, Barrier, - UpdateDebounce, StorePreamblesInMemory, *Callbacks); + ASTWorkerHandle Worker = + ASTWorker::create(File, CDB, *IdleASTs, + WorkerThreads ? WorkerThreads.getPointer() : nullptr, + Barrier, Opts, *Callbacks); FD = std::unique_ptr( new FileData{Inputs.Contents, std::move(Worker)}); } else { diff --git a/clang-tools-extra/clangd/TUScheduler.h b/clang-tools-extra/clangd/TUScheduler.h index f24a777d5836f..fb32c3b2edff4 100644 --- a/clang-tools-extra/clangd/TUScheduler.h +++ b/clang-tools-extra/clangd/TUScheduler.h @@ -191,6 +191,10 @@ class TUScheduler { /// Determines when to keep idle ASTs in memory for future use. ASTRetentionPolicy RetentionPolicy; + + /// Whether to run PreamblePeer asynchronously. + /// No-op if AsyncThreadsCount is 0. + bool AsyncPreambleBuilds = false; }; TUScheduler(const GlobalCompilationDatabase &CDB, const Options &Opts, @@ -301,7 +305,7 @@ class TUScheduler { private: const GlobalCompilationDatabase &CDB; - const bool StorePreamblesInMemory; + const Options Opts; std::unique_ptr Callbacks; // not nullptr Semaphore Barrier; llvm::StringMap> Files; @@ -310,7 +314,6 @@ class TUScheduler { // asynchronously. llvm::Optional PreambleTasks; llvm::Optional WorkerThreads; - DebouncePolicy UpdateDebounce; }; } // namespace clangd diff --git a/clang-tools-extra/clangd/XRefs.cpp b/clang-tools-extra/clangd/XRefs.cpp index 1d82763b6a3cf..df2c7a7c72285 100644 --- a/clang-tools-extra/clangd/XRefs.cpp +++ b/clang-tools-extra/clangd/XRefs.cpp @@ -27,8 +27,12 @@ #include "clang/AST/Attrs.inc" #include "clang/AST/Decl.h" #include "clang/AST/DeclCXX.h" +#include "clang/AST/DeclObjC.h" #include "clang/AST/DeclTemplate.h" #include "clang/AST/ExprCXX.h" +#include "clang/AST/RecursiveASTVisitor.h" +#include "clang/AST/Stmt.h" +#include "clang/AST/StmtCXX.h" #include "clang/AST/Type.h" #include "clang/Basic/CharInfo.h" #include "clang/Basic/LLVM.h" @@ -45,6 +49,7 @@ #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/None.h" #include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/ScopeExit.h" #include "llvm/ADT/SmallSet.h" #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringRef.h" @@ -206,8 +211,8 @@ llvm::Optional locateMacroReferent(const syntax::Token &TouchedIdentifier, ParsedAST &AST, llvm::StringRef MainFilePath) { if (auto M = locateMacroAt(TouchedIdentifier, AST.getPreprocessor())) { - if (auto Loc = makeLocation(AST.getASTContext(), - M->Info->getDefinitionLoc(), MainFilePath)) { + if (auto Loc = + makeLocation(AST.getASTContext(), M->NameLoc, MainFilePath)) { LocatedSymbol Macro; Macro.Name = std::string(M->Name); Macro.PreferredDeclaration = *Loc; @@ -438,8 +443,11 @@ locateSymbolTextually(const SpelledWord &Word, ParsedAST &AST, ScoredResults.push_back({Score, std::move(Located)}); }); - if (TooMany) + if (TooMany) { + vlog("Heuristic index lookup for {0} returned too many candidates, ignored", + Word.Text); return {}; + } llvm::sort(ScoredResults, [](const ScoredLocatedSymbol &A, const ScoredLocatedSymbol &B) { @@ -448,6 +456,10 @@ locateSymbolTextually(const SpelledWord &Word, ParsedAST &AST, std::vector Results; for (auto &Res : std::move(ScoredResults)) Results.push_back(std::move(Res.second)); + if (Results.empty()) + vlog("No heuristic index definition for {0}", Word.Text); + else + log("Found definition heuristically in index for {0}", Word.Text); return Results; } @@ -570,13 +582,22 @@ std::vector locateSymbolAt(ParsedAST &AST, Position Pos, // Is the same word nearby a real identifier that might refer to something? if (const syntax::Token *NearbyIdent = findNearbyIdentifier(*Word, AST.getTokens())) { - if (auto Macro = locateMacroReferent(*NearbyIdent, AST, *MainFilePath)) + if (auto Macro = locateMacroReferent(*NearbyIdent, AST, *MainFilePath)) { + log("Found macro definition heuristically using nearby identifier {0}", + Word->Text); return {*std::move(Macro)}; + } ASTResults = locateASTReferent(NearbyIdent->location(), NearbyIdent, AST, *MainFilePath, Index, /*NodeKind=*/nullptr); - if (!ASTResults.empty()) + if (!ASTResults.empty()) { + log("Found definition heuristically using nearby identifier {0}", + NearbyIdent->text(SM)); return ASTResults; + } else { + vlog("No definition found using nearby identifier {0} at {1}", + Word->Text, Word->Location.printToString(SM)); + } } // No nearby word, or it didn't refer to anything either. Try the index. auto TextualResults = @@ -696,35 +717,304 @@ findRefs(const std::vector &Decls, ParsedAST &AST) { return std::move(RefFinder).take(); } +const Stmt *getFunctionBody(DynTypedNode N) { + if (const auto *FD = N.get()) + return FD->getBody(); + if (const auto *FD = N.get()) + return FD->getBody(); + if (const auto *FD = N.get()) + return FD->getBody(); + if (const auto *FD = N.get()) + return FD->getBody(); + return nullptr; +} + +const Stmt *getLoopBody(DynTypedNode N) { + if (const auto *LS = N.get()) + return LS->getBody(); + if (const auto *LS = N.get()) + return LS->getBody(); + if (const auto *LS = N.get()) + return LS->getBody(); + if (const auto *LS = N.get()) + return LS->getBody(); + return nullptr; +} + +// AST traversal to highlight control flow statements under some root. +// Once we hit further control flow we prune the tree (or at least restrict +// what we highlight) so we capture e.g. breaks from the outer loop only. +class FindControlFlow : public RecursiveASTVisitor { + // Types of control-flow statements we might highlight. + enum Target { + Break = 1, + Continue = 2, + Return = 4, + Case = 8, + Throw = 16, + Goto = 32, + All = Break | Continue | Return | Case | Throw | Goto, + }; + int Ignore = 0; // bitmask of Target - what are we *not* highlighting? + SourceRange Bounds; // Half-open, restricts reported targets. + std::vector &Result; + const SourceManager &SM; + + // Masks out targets for a traversal into D. + // Traverses the subtree using Delegate() if any targets remain. + template + bool filterAndTraverse(DynTypedNode D, const Func &Delegate) { + auto RestoreIgnore = llvm::make_scope_exit( + [OldIgnore(Ignore), this] { Ignore = OldIgnore; }); + if (getFunctionBody(D)) + Ignore = All; + else if (getLoopBody(D)) + Ignore |= Continue | Break; + else if (D.get()) + Ignore |= Break | Case; + // Prune tree if we're not looking for anything. + return (Ignore == All) ? true : Delegate(); + } + + void found(Target T, SourceLocation Loc) { + if (T & Ignore) + return; + if (SM.isBeforeInTranslationUnit(Loc, Bounds.getBegin()) || + SM.isBeforeInTranslationUnit(Bounds.getEnd(), Loc)) + return; + Result.push_back(Loc); + } + +public: + FindControlFlow(SourceRange Bounds, std::vector &Result, + const SourceManager &SM) + : Bounds(Bounds), Result(Result), SM(SM) {} + + // When traversing function or loops, limit targets to those that still + // refer to the original root. + bool TraverseDecl(Decl *D) { + return !D || filterAndTraverse(DynTypedNode::create(*D), [&] { + return RecursiveASTVisitor::TraverseDecl(D); + }); + } + bool TraverseStmt(Stmt *S) { + return !S || filterAndTraverse(DynTypedNode::create(*S), [&] { + return RecursiveASTVisitor::TraverseStmt(S); + }); + } + + // Add leaves that we found and want. + bool VisitReturnStmt(ReturnStmt *R) { + found(Return, R->getReturnLoc()); + return true; + } + bool VisitBreakStmt(BreakStmt *B) { + found(Break, B->getBreakLoc()); + return true; + } + bool VisitContinueStmt(ContinueStmt *C) { + found(Continue, C->getContinueLoc()); + return true; + } + bool VisitSwitchCase(SwitchCase *C) { + found(Case, C->getKeywordLoc()); + return true; + } + bool VisitCXXThrowExpr(CXXThrowExpr *T) { + found(Throw, T->getThrowLoc()); + return true; + } + bool VisitGotoStmt(GotoStmt *G) { + // Goto is interesting if its target is outside the root. + if (const auto *LD = G->getLabel()) { + if (SM.isBeforeInTranslationUnit(LD->getLocation(), Bounds.getBegin()) || + SM.isBeforeInTranslationUnit(Bounds.getEnd(), LD->getLocation())) + found(Goto, G->getGotoLoc()); + } + return true; + } +}; + +// Given a location within a switch statement, return the half-open range that +// covers the case it's contained in. +// We treat `case X: case Y: ...` as one case, and assume no other fallthrough. +SourceRange findCaseBounds(const SwitchStmt &Switch, SourceLocation Loc, + const SourceManager &SM) { + // Cases are not stored in order, sort them first. + // (In fact they seem to be stored in reverse order, don't rely on this) + std::vector Cases; + for (const SwitchCase *Case = Switch.getSwitchCaseList(); Case; + Case = Case->getNextSwitchCase()) + Cases.push_back(Case); + llvm::sort(Cases, [&](const SwitchCase *L, const SwitchCase *R) { + return SM.isBeforeInTranslationUnit(L->getKeywordLoc(), R->getKeywordLoc()); + }); + + // Find the first case after the target location, the end of our range. + auto CaseAfter = llvm::partition_point(Cases, [&](const SwitchCase *C) { + return !SM.isBeforeInTranslationUnit(Loc, C->getKeywordLoc()); + }); + SourceLocation End = CaseAfter == Cases.end() ? Switch.getEndLoc() + : (*CaseAfter)->getKeywordLoc(); + + // Our target can be before the first case - cases are optional! + if (CaseAfter == Cases.begin()) + return SourceRange(Switch.getBeginLoc(), End); + // The start of our range is usually the previous case, but... + auto CaseBefore = std::prev(CaseAfter); + // ... rewind CaseBefore to the first in a `case A: case B: ...` sequence. + while (CaseBefore != Cases.begin() && + (*std::prev(CaseBefore))->getSubStmt() == *CaseBefore) + --CaseBefore; + return SourceRange((*CaseBefore)->getKeywordLoc(), End); +} + +// Returns the locations of control flow statements related to N. e.g.: +// for => branches: break/continue/return/throw +// break => controlling loop (forwhile/do), and its related control flow +// return => all returns/throws from the same function +// When an inner block is selected, we include branches bound to outer blocks +// as these are exits from the inner block. e.g. return in a for loop. +// FIXME: We don't analyze catch blocks, throw is treated the same as return. +std::vector relatedControlFlow(const SelectionTree::Node &N) { + const SourceManager &SM = + N.getDeclContext().getParentASTContext().getSourceManager(); + std::vector Result; + + // First, check if we're at a node that can resolve to a root. + enum class Cur { None, Break, Continue, Return, Case, Throw } Cursor; + if (N.ASTNode.get()) { + Cursor = Cur::Break; + } else if (N.ASTNode.get()) { + Cursor = Cur::Continue; + } else if (N.ASTNode.get()) { + Cursor = Cur::Return; + } else if (N.ASTNode.get()) { + Cursor = Cur::Throw; + } else if (N.ASTNode.get()) { + Cursor = Cur::Case; + } else if (const GotoStmt *GS = N.ASTNode.get()) { + // We don't know what root to associate with, but highlight the goto/label. + Result.push_back(GS->getGotoLoc()); + if (const auto *LD = GS->getLabel()) + Result.push_back(LD->getLocation()); + Cursor = Cur::None; + } else { + Cursor = Cur::None; + } + + const Stmt *Root = nullptr; // Loop or function body to traverse. + SourceRange Bounds; + // Look up the tree for a root (or just at this node if we didn't find a leaf) + for (const auto *P = &N; P; P = P->Parent) { + // return associates with enclosing function + if (const Stmt *FunctionBody = getFunctionBody(P->ASTNode)) { + if (Cursor == Cur::Return || Cursor == Cur::Throw) { + Root = FunctionBody; + } + break; // other leaves don't cross functions. + } + // break/continue associate with enclosing loop. + if (const Stmt *LoopBody = getLoopBody(P->ASTNode)) { + if (Cursor == Cur::None || Cursor == Cur::Break || + Cursor == Cur::Continue) { + Root = LoopBody; + // Highlight the loop keyword itself. + // FIXME: for do-while, this only covers the `do`.. + Result.push_back(P->ASTNode.getSourceRange().getBegin()); + break; + } + } + // For switches, users think of case statements as control flow blocks. + // We highlight only occurrences surrounded by the same case. + // We don't detect fallthrough (other than 'case X, case Y'). + if (const auto *SS = P->ASTNode.get()) { + if (Cursor == Cur::Break || Cursor == Cur::Case) { + Result.push_back(SS->getSwitchLoc()); // Highlight the switch. + Root = SS->getBody(); + // Limit to enclosing case, if there is one. + Bounds = findCaseBounds(*SS, N.ASTNode.getSourceRange().getBegin(), SM); + break; + } + } + // If we didn't start at some interesting node, we're done. + if (Cursor == Cur::None) + break; + } + if (Root) { + if (!Bounds.isValid()) + Bounds = Root->getSourceRange(); + FindControlFlow(Bounds, Result, SM).TraverseStmt(const_cast(Root)); + } + return Result; +} + +DocumentHighlight toHighlight(const ReferenceFinder::Reference &Ref, + const SourceManager &SM) { + DocumentHighlight DH; + DH.range = Ref.range(SM); + if (Ref.Role & index::SymbolRoleSet(index::SymbolRole::Write)) + DH.kind = DocumentHighlightKind::Write; + else if (Ref.Role & index::SymbolRoleSet(index::SymbolRole::Read)) + DH.kind = DocumentHighlightKind::Read; + else + DH.kind = DocumentHighlightKind::Text; + return DH; +} + +llvm::Optional toHighlight(SourceLocation Loc, + const syntax::TokenBuffer &TB) { + Loc = TB.sourceManager().getFileLoc(Loc); + if (const auto *Tok = TB.spelledTokenAt(Loc)) { + DocumentHighlight Result; + Result.range = halfOpenToRange( + TB.sourceManager(), + CharSourceRange::getCharRange(Tok->location(), Tok->endLocation())); + return Result; + } + return llvm::None; +} + } // namespace std::vector findDocumentHighlights(ParsedAST &AST, Position Pos) { const SourceManager &SM = AST.getSourceManager(); // FIXME: show references to macro within file? - DeclRelationSet Relations = - DeclRelation::TemplatePattern | DeclRelation::Alias; auto CurLoc = sourceLocationInMainFile(SM, Pos); if (!CurLoc) { llvm::consumeError(CurLoc.takeError()); return {}; } - auto References = findRefs(getDeclAtPosition(AST, *CurLoc, Relations), AST); - - // FIXME: we may get multiple DocumentHighlights with the same location and - // different kinds, deduplicate them. std::vector Result; - for (const auto &Ref : References) { - DocumentHighlight DH; - DH.range = Ref.range(SM); - if (Ref.Role & index::SymbolRoleSet(index::SymbolRole::Write)) - DH.kind = DocumentHighlightKind::Write; - else if (Ref.Role & index::SymbolRoleSet(index::SymbolRole::Read)) - DH.kind = DocumentHighlightKind::Read; - else - DH.kind = DocumentHighlightKind::Text; - Result.push_back(std::move(DH)); - } + auto TryTree = [&](SelectionTree ST) { + if (const SelectionTree::Node *N = ST.commonAncestor()) { + DeclRelationSet Relations = + DeclRelation::TemplatePattern | DeclRelation::Alias; + auto Decls = targetDecl(N->ASTNode, Relations); + if (!Decls.empty()) { + auto Refs = findRefs({Decls.begin(), Decls.end()}, AST); + // FIXME: we may get multiple DocumentHighlights with the same location + // and different kinds, deduplicate them. + for (const auto &Ref : findRefs({Decls.begin(), Decls.end()}, AST)) + Result.push_back(toHighlight(Ref, SM)); + return true; + } + auto ControlFlow = relatedControlFlow(*N); + if (!ControlFlow.empty()) { + for (SourceLocation Loc : ControlFlow) + if (auto Highlight = toHighlight(Loc, AST.getTokens())) + Result.push_back(std::move(*Highlight)); + return true; + } + } + return false; + }; + + unsigned Offset = + AST.getSourceManager().getDecomposedSpellingLoc(*CurLoc).second; + SelectionTree::createEach(AST.getASTContext(), AST.getTokens(), Offset, + Offset, TryTree); return Result; } diff --git a/clang-tools-extra/clangd/index/IndexAction.cpp b/clang-tools-extra/clangd/index/IndexAction.cpp index 9f294d4ab9252..aa65008b51c00 100644 --- a/clang-tools-extra/clangd/index/IndexAction.cpp +++ b/clang-tools-extra/clangd/index/IndexAction.cpp @@ -132,11 +132,19 @@ class IndexAction : public ASTFrontendAction { std::function RefsCallback, std::function RelationsCallback, std::function IncludeGraphCallback) - : SymbolsCallback(SymbolsCallback), - RefsCallback(RefsCallback), RelationsCallback(RelationsCallback), + : SymbolsCallback(SymbolsCallback), RefsCallback(RefsCallback), + RelationsCallback(RelationsCallback), IncludeGraphCallback(IncludeGraphCallback), Collector(C), Includes(std::move(Includes)), Opts(Opts), - PragmaHandler(collectIWYUHeaderMaps(this->Includes.get())) {} + PragmaHandler(collectIWYUHeaderMaps(this->Includes.get())) { + this->Opts.ShouldTraverseDecl = [this](const Decl *D) { + auto &SM = D->getASTContext().getSourceManager(); + auto FID = SM.getFileID(SM.getExpansionLoc(D->getLocation())); + if (!FID.isValid()) + return true; + return Collector->shouldIndexFile(FID); + }; + } std::unique_ptr CreateASTConsumer(CompilerInstance &CI, llvm::StringRef InFile) override { @@ -146,15 +154,8 @@ class IndexAction : public ASTFrontendAction { CI.getPreprocessor().addPPCallbacks( std::make_unique(CI.getSourceManager(), IG)); - return index::createIndexingASTConsumer( - Collector, Opts, CI.getPreprocessorPtr(), - /*ShouldSkipFunctionBody=*/[this](const Decl *D) { - auto &SM = D->getASTContext().getSourceManager(); - auto FID = SM.getFileID(SM.getExpansionLoc(D->getLocation())); - if (!FID.isValid()) - return false; - return !Collector->shouldIndexFile(FID); - }); + return index::createIndexingASTConsumer(Collector, Opts, + CI.getPreprocessorPtr()); } bool BeginInvocation(CompilerInstance &CI) override { diff --git a/clang-tools-extra/clangd/refactor/tweaks/DefineOutline.cpp b/clang-tools-extra/clangd/refactor/tweaks/DefineOutline.cpp index 405ff90a5945c..63a5ba6cb9988 100644 --- a/clang-tools-extra/clangd/refactor/tweaks/DefineOutline.cpp +++ b/clang-tools-extra/clangd/refactor/tweaks/DefineOutline.cpp @@ -317,18 +317,16 @@ SourceRange getDeletionRange(const FunctionDecl *FD, const syntax::TokenBuffer &TokBuf) { auto DeletionRange = FD->getBody()->getSourceRange(); if (auto *CD = llvm::dyn_cast(FD)) { - const auto &SM = TokBuf.sourceManager(); // AST doesn't contain the location for ":" in ctor initializers. Therefore // we find it by finding the first ":" before the first ctor initializer. SourceLocation InitStart; // Find the first initializer. for (const auto *CInit : CD->inits()) { - // We don't care about in-class initializers. - if (CInit->isInClassMemberInitializer()) + // SourceOrder is -1 for implicit initializers. + if (CInit->getSourceOrder() != 0) continue; - if (InitStart.isInvalid() || - SM.isBeforeInTranslationUnit(CInit->getSourceLocation(), InitStart)) - InitStart = CInit->getSourceLocation(); + InitStart = CInit->getSourceLocation(); + break; } if (InitStart.isValid()) { auto Toks = TokBuf.expandedTokens(CD->getSourceRange()); diff --git a/clang-tools-extra/clangd/tool/ClangdMain.cpp b/clang-tools-extra/clangd/tool/ClangdMain.cpp index 031f57f954cb2..eec3a830f6e77 100644 --- a/clang-tools-extra/clangd/tool/ClangdMain.cpp +++ b/clang-tools-extra/clangd/tool/ClangdMain.cpp @@ -274,11 +274,8 @@ list TweakList{ opt CrossFileRename{ "cross-file-rename", cat(Features), - desc("Enable cross-file rename feature. Note that this feature is " - "experimental and may lead to broken code or incomplete rename " - "results"), - init(false), - Hidden, + desc("Enable cross-file rename feature."), + init(true), }; opt RecoveryAST{ @@ -420,6 +417,15 @@ opt PrettyPrint{ init(false), }; +opt AsyncPreamble{ + "async-preamble", + cat(Misc), + desc("Reuse even stale preambles, and rebuild them in the background. This " + "improves latency at the cost of accuracy."), + init(ClangdServer::Options().AsyncPreambleBuilds), + Hidden, +}; + /// Supports a test URI scheme with relaxed constraints for lit tests. /// The path in a test URI will be combined with a platform-specific fake /// directory to form an absolute path. For example, test:///a.cpp is resolved diff --git a/clang-tools-extra/clangd/unittests/ClangdTests.cpp b/clang-tools-extra/clangd/unittests/ClangdTests.cpp index 81075ff1bbe73..46164015a3ce0 100644 --- a/clang-tools-extra/clangd/unittests/ClangdTests.cpp +++ b/clang-tools-extra/clangd/unittests/ClangdTests.cpp @@ -211,18 +211,18 @@ int b = a; FS.Files[FooCpp] = SourceContents; Server.addDocument(FooCpp, SourceContents); - auto DumpParse1 = dumpASTWithoutMemoryLocs(Server, FooCpp); ASSERT_TRUE(Server.blockUntilIdleForTest()) << "Waiting for diagnostics"; + auto DumpParse1 = dumpASTWithoutMemoryLocs(Server, FooCpp); EXPECT_FALSE(DiagConsumer.hadErrorInLastDiags()); Server.addDocument(FooCpp, ""); - auto DumpParseEmpty = dumpASTWithoutMemoryLocs(Server, FooCpp); ASSERT_TRUE(Server.blockUntilIdleForTest()) << "Waiting for diagnostics"; + auto DumpParseEmpty = dumpASTWithoutMemoryLocs(Server, FooCpp); EXPECT_FALSE(DiagConsumer.hadErrorInLastDiags()); Server.addDocument(FooCpp, SourceContents); - auto DumpParse2 = dumpASTWithoutMemoryLocs(Server, FooCpp); ASSERT_TRUE(Server.blockUntilIdleForTest()) << "Waiting for diagnostics"; + auto DumpParse2 = dumpASTWithoutMemoryLocs(Server, FooCpp); EXPECT_FALSE(DiagConsumer.hadErrorInLastDiags()); EXPECT_EQ(DumpParse1, DumpParse2); @@ -247,20 +247,20 @@ int b = a; FS.Files[FooCpp] = SourceContents; Server.addDocument(FooCpp, SourceContents); - auto DumpParse1 = dumpASTWithoutMemoryLocs(Server, FooCpp); ASSERT_TRUE(Server.blockUntilIdleForTest()) << "Waiting for diagnostics"; + auto DumpParse1 = dumpASTWithoutMemoryLocs(Server, FooCpp); EXPECT_FALSE(DiagConsumer.hadErrorInLastDiags()); FS.Files[FooH] = ""; Server.addDocument(FooCpp, SourceContents); - auto DumpParseDifferent = dumpASTWithoutMemoryLocs(Server, FooCpp); ASSERT_TRUE(Server.blockUntilIdleForTest()) << "Waiting for diagnostics"; + auto DumpParseDifferent = dumpASTWithoutMemoryLocs(Server, FooCpp); EXPECT_TRUE(DiagConsumer.hadErrorInLastDiags()); FS.Files[FooH] = "int a;"; Server.addDocument(FooCpp, SourceContents); - auto DumpParse2 = dumpASTWithoutMemoryLocs(Server, FooCpp); ASSERT_TRUE(Server.blockUntilIdleForTest()) << "Waiting for diagnostics"; + auto DumpParse2 = dumpASTWithoutMemoryLocs(Server, FooCpp); EXPECT_FALSE(DiagConsumer.hadErrorInLastDiags()); EXPECT_EQ(DumpParse1, DumpParse2); diff --git a/clang-tools-extra/clangd/unittests/FindSymbolsTests.cpp b/clang-tools-extra/clangd/unittests/FindSymbolsTests.cpp index f9343458841a7..2b014ac0a69b4 100644 --- a/clang-tools-extra/clangd/unittests/FindSymbolsTests.cpp +++ b/clang-tools-extra/clangd/unittests/FindSymbolsTests.cpp @@ -666,7 +666,8 @@ TEST_F(DocumentSymbolsTest, UsingDirectives) { } TEST_F(DocumentSymbolsTest, TempSpecs) { - addFile("foo.cpp", R"cpp( + std::string FilePath = testPath("foo.cpp"); + addFile(FilePath, R"cpp( template class Foo {}; template class Foo {}; template <> class Foo {}; @@ -674,7 +675,7 @@ TEST_F(DocumentSymbolsTest, TempSpecs) { )cpp"); // Foo is higher ranked because of exact name match. EXPECT_THAT( - getSymbols("foo.cpp"), + getSymbols(FilePath), UnorderedElementsAre( AllOf(WithName("Foo"), WithKind(SymbolKind::Class)), AllOf(WithName("Foo"), WithKind(SymbolKind::Class)), @@ -683,7 +684,8 @@ TEST_F(DocumentSymbolsTest, TempSpecs) { } TEST_F(DocumentSymbolsTest, Qualifiers) { - addFile("foo.cpp", R"cpp( + std::string FilePath = testPath("foo.cpp"); + addFile(FilePath, R"cpp( namespace foo { namespace bar { struct Cls; @@ -706,7 +708,7 @@ TEST_F(DocumentSymbolsTest, Qualifiers) { )cpp"); // All the qualifiers should be preserved exactly as written. - EXPECT_THAT(getSymbols("foo.cpp"), + EXPECT_THAT(getSymbols(FilePath), UnorderedElementsAre( WithName("foo"), WithName("foo::bar::Cls"), WithName("foo::bar::func1"), WithName("::foo::bar::func2"), @@ -715,7 +717,8 @@ TEST_F(DocumentSymbolsTest, Qualifiers) { } TEST_F(DocumentSymbolsTest, QualifiersWithTemplateArgs) { - addFile("foo.cpp", R"cpp( + std::string FilePath = testPath("foo.cpp"); + addFile(FilePath, R"cpp( template class Foo; template <> @@ -738,7 +741,7 @@ TEST_F(DocumentSymbolsTest, QualifiersWithTemplateArgs) { int Foo_type::method3() { return 30; } )cpp"); EXPECT_THAT( - getSymbols("foo.cpp"), + getSymbols(FilePath), UnorderedElementsAre(WithName("Foo"), WithName("Foo"), WithName("int_type"), WithName("Foo::method1"), diff --git a/clang-tools-extra/clangd/unittests/HeadersTests.cpp b/clang-tools-extra/clangd/unittests/HeadersTests.cpp index 2de0b5ca92a70..07ab835bdb673 100644 --- a/clang-tools-extra/clangd/unittests/HeadersTests.cpp +++ b/clang-tools-extra/clangd/unittests/HeadersTests.cpp @@ -306,7 +306,7 @@ TEST(Headers, NoHeaderSearchInfo) { } TEST_F(HeadersTest, PresumedLocations) { - std::string HeaderFile = "implicit_include.h"; + std::string HeaderFile = "__preamble_patch__.h"; // Line map inclusion back to main file. std::string HeaderContents = @@ -317,7 +317,7 @@ TEST_F(HeadersTest, PresumedLocations) { FS.Files[HeaderFile] = HeaderContents; // Including through non-builtin file has no effects. - FS.Files[MainFile] = "#include \"implicit_include.h\"\n\n"; + FS.Files[MainFile] = "#include \"__preamble_patch__.h\"\n\n"; EXPECT_THAT(collectIncludes().MainFileIncludes, Not(Contains(Written("")))); diff --git a/clang-tools-extra/clangd/unittests/HoverTests.cpp b/clang-tools-extra/clangd/unittests/HoverTests.cpp index e5ff0ee364d83..dc818ea661938 100644 --- a/clang-tools-extra/clangd/unittests/HoverTests.cpp +++ b/clang-tools-extra/clangd/unittests/HoverTests.cpp @@ -12,6 +12,7 @@ #include "TestIndex.h" #include "TestTU.h" #include "index/MemIndex.h" +#include "clang/Basic/Specifiers.h" #include "clang/Index/IndexSymbol.h" #include "llvm/ADT/None.h" #include "llvm/ADT/StringRef.h" @@ -79,6 +80,7 @@ TEST(Hover, Structured) { HI.Type = "char"; HI.Offset = 0; HI.Size = 1; + HI.AccessSpecifier = "public"; }}, // Local to class method. {R"cpp( @@ -115,6 +117,7 @@ TEST(Hover, Structured) { HI.Type = "char"; HI.Offset = 0; HI.Size = 1; + HI.AccessSpecifier = "public"; }}, // Struct definition shows size. {R"cpp( @@ -344,6 +347,7 @@ class Foo {})cpp"; HI.Kind = index::SymbolKind::Constructor; HI.Definition = "X()"; HI.Parameters.emplace(); + HI.AccessSpecifier = "public"; }}, {"class X { [[^~]]X(); };", // FIXME: Should be [[~X]]() [](HoverInfo &HI) { @@ -353,6 +357,7 @@ class Foo {})cpp"; HI.Kind = index::SymbolKind::Destructor; HI.Definition = "~X()"; HI.Parameters.emplace(); + HI.AccessSpecifier = "private"; }}, {"class X { [[op^erator]] int(); };", [](HoverInfo &HI) { @@ -362,6 +367,7 @@ class Foo {})cpp"; HI.Kind = index::SymbolKind::ConversionFunction; HI.Definition = "operator int()"; HI.Parameters.emplace(); + HI.AccessSpecifier = "private"; }}, {"class X { operator [[^X]](); };", [](HoverInfo &HI) { @@ -494,6 +500,7 @@ class Foo {})cpp"; HI.NamespaceScope = ""; HI.LocalScope = "Add<1, 2>::"; HI.Value = "3"; + HI.AccessSpecifier = "public"; }}, {R"cpp( constexpr int answer() { return 40 + 2; } @@ -606,6 +613,7 @@ class Foo {})cpp"; HI.Definition = "typename T = int"; HI.LocalScope = "foo::"; HI.Type = "typename"; + HI.AccessSpecifier = "public"; }}, {// TemplateTemplate Type Parameter R"cpp( @@ -618,6 +626,7 @@ class Foo {})cpp"; HI.Definition = "template class T"; HI.LocalScope = "foo::"; HI.Type = "template class"; + HI.AccessSpecifier = "public"; }}, {// NonType Template Parameter R"cpp( @@ -630,6 +639,7 @@ class Foo {})cpp"; HI.Definition = "int T = 5"; HI.LocalScope = "foo::"; HI.Type = "int"; + HI.AccessSpecifier = "public"; }}, {// Getter @@ -646,6 +656,7 @@ class Foo {})cpp"; HI.Type = "float ()"; HI.ReturnType = "float"; HI.Parameters.emplace(); + HI.AccessSpecifier = "public"; }}, {// Setter R"cpp( @@ -664,6 +675,7 @@ class Foo {})cpp"; HI.Parameters->emplace_back(); HI.Parameters->back().Type = "float"; HI.Parameters->back().Name = "v"; + HI.AccessSpecifier = "public"; }}, {// Setter (builder) R"cpp( @@ -682,6 +694,7 @@ class Foo {})cpp"; HI.Parameters->emplace_back(); HI.Parameters->back().Type = "float"; HI.Parameters->back().Name = "v"; + HI.AccessSpecifier = "public"; }}, }; for (const auto &Case : Cases) { @@ -715,6 +728,7 @@ class Foo {})cpp"; EXPECT_EQ(H->Value, Expected.Value); EXPECT_EQ(H->Size, Expected.Size); EXPECT_EQ(H->Offset, Expected.Offset); + EXPECT_EQ(H->AccessSpecifier, Expected.AccessSpecifier); } } @@ -1964,7 +1978,51 @@ Size: 4 bytes // In test::Bar def)", }, - }; + { + [](HoverInfo &HI) { + HI.Kind = index::SymbolKind::Field; + HI.AccessSpecifier = "public"; + HI.Name = "foo"; + HI.LocalScope = "test::Bar::"; + HI.Definition = "def"; + }, + R"(field foo + +// In test::Bar +public: def)", + }, + { + [](HoverInfo &HI) { + HI.Definition = "int method()"; + HI.AccessSpecifier = "protected"; + HI.Kind = index::SymbolKind::InstanceMethod; + HI.NamespaceScope = ""; + HI.LocalScope = "cls::"; + HI.Name = "method"; + HI.Parameters.emplace(); + HI.ReturnType = "int"; + HI.Type = "int ()"; + }, + R"(instance-method method + +→ int + +// In cls +protected: int method())", + }, + { + [](HoverInfo &HI) { + HI.Kind = index::SymbolKind::Union; + HI.AccessSpecifier = "private"; + HI.Name = "foo"; + HI.NamespaceScope = "ns1::"; + HI.Definition = "union foo {}"; + }, + R"(union foo + +// In namespace ns1 +private: union foo {})", + }}; for (const auto &C : Cases) { HoverInfo HI; diff --git a/clang-tools-extra/clangd/unittests/IndexActionTests.cpp b/clang-tools-extra/clangd/unittests/IndexActionTests.cpp index 6441d019c7e18..31e1bc573290f 100644 --- a/clang-tools-extra/clangd/unittests/IndexActionTests.cpp +++ b/clang-tools-extra/clangd/unittests/IndexActionTests.cpp @@ -19,6 +19,7 @@ namespace { using ::testing::AllOf; using ::testing::ElementsAre; +using ::testing::EndsWith; using ::testing::Not; using ::testing::Pair; using ::testing::UnorderedElementsAre; @@ -75,8 +76,7 @@ class IndexActionTest : public ::testing::Test { new FileManager(FileSystemOptions(), InMemoryFileSystem)); auto Action = createStaticIndexingAction( - SymbolCollector::Options(), - [&](SymbolSlab S) { IndexFile.Symbols = std::move(S); }, + Opts, [&](SymbolSlab S) { IndexFile.Symbols = std::move(S); }, [&](RefSlab R) { IndexFile.Refs = std::move(R); }, [&](RelationSlab R) { IndexFile.Relations = std::move(R); }, [&](IncludeGraph IG) { IndexFile.Sources = std::move(IG); }); @@ -99,11 +99,12 @@ class IndexActionTest : public ::testing::Test { void addFile(llvm::StringRef Path, llvm::StringRef Content) { InMemoryFileSystem->addFile(Path, 0, - llvm::MemoryBuffer::getMemBuffer(Content)); + llvm::MemoryBuffer::getMemBufferCopy(Content)); FilePaths.push_back(std::string(Path)); } protected: + SymbolCollector::Options Opts; std::vector FilePaths; llvm::IntrusiveRefCntPtr InMemoryFileSystem; }; @@ -250,6 +251,36 @@ TEST_F(IndexActionTest, NoWarnings) { EXPECT_THAT(*IndexFile.Symbols, ElementsAre(HasName("foo"), HasName("bar"))); } +TEST_F(IndexActionTest, SkipFiles) { + std::string MainFilePath = testPath("main.cpp"); + addFile(MainFilePath, R"cpp( + // clang-format off + #include "good.h" + #include "bad.h" + // clang-format on + )cpp"); + addFile(testPath("good.h"), R"cpp( + struct S { int s; }; + void f1() { S f; } + auto unskippable1() { return S(); } + )cpp"); + addFile(testPath("bad.h"), R"cpp( + struct T { S t; }; + void f2() { S f; } + auto unskippable2() { return S(); } + )cpp"); + Opts.FileFilter = [](const SourceManager &SM, FileID F) { + return !SM.getFileEntryForID(F)->getName().endswith("bad.h"); + }; + IndexFileIn IndexFile = runIndexingAction(MainFilePath, {"-std=c++14"}); + EXPECT_THAT(*IndexFile.Symbols, + UnorderedElementsAre(HasName("S"), HasName("s"), HasName("f1"), + HasName("unskippable1"))); + for (const auto &Pair : *IndexFile.Refs) + for (const auto &Ref : Pair.second) + EXPECT_THAT(Ref.Location.FileURI, EndsWith("good.h")); +} + } // namespace } // namespace clangd } // namespace clang diff --git a/clang-tools-extra/clangd/unittests/ParsedASTTests.cpp b/clang-tools-extra/clangd/unittests/ParsedASTTests.cpp index a2bc996be4f14..d86f741d9e72e 100644 --- a/clang-tools-extra/clangd/unittests/ParsedASTTests.cpp +++ b/clang-tools-extra/clangd/unittests/ParsedASTTests.cpp @@ -17,7 +17,9 @@ #include "Annotations.h" #include "Compiler.h" #include "Diagnostics.h" +#include "Headers.h" #include "ParsedAST.h" +#include "Preamble.h" #include "SourceCode.h" #include "TestFS.h" #include "TestTU.h" @@ -28,6 +30,7 @@ #include "clang/Lex/PPCallbacks.h" #include "clang/Lex/Token.h" #include "clang/Tooling/Syntax/Tokens.h" +#include "llvm/ADT/STLExtras.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/ScopedPrinter.h" #include "gmock/gmock-matchers.h" @@ -82,6 +85,13 @@ MATCHER_P(RangeIs, R, "") { return arg.beginOffset() == R.Begin && arg.endOffset() == R.End; } +MATCHER(EqInc, "") { + Inclusion Actual = testing::get<0>(arg); + Inclusion Expected = testing::get<1>(arg); + return std::tie(Actual.HashLine, Actual.Written) == + std::tie(Expected.HashLine, Expected.Written); +} + TEST(ParsedASTTest, TopLevelDecls) { TestTU TU; TU.HeaderCode = R"( @@ -431,6 +441,105 @@ TEST(ParsedASTTest, ReplayPreambleForTidyCheckers) { } } +TEST(ParsedASTTest, PatchesAdditionalIncludes) { + llvm::StringLiteral ModifiedContents = R"cpp( + #include "baz.h" + #include "foo.h" + #include "sub/aux.h" + void bar() { + foo(); + baz(); + aux(); + })cpp"; + // Build expected ast with symbols coming from headers. + TestTU TU; + TU.Filename = "foo.cpp"; + TU.AdditionalFiles["foo.h"] = "void foo();"; + TU.AdditionalFiles["sub/baz.h"] = "void baz();"; + TU.AdditionalFiles["sub/aux.h"] = "void aux();"; + TU.ExtraArgs = {"-I" + testPath("sub")}; + TU.Code = ModifiedContents.str(); + auto ExpectedAST = TU.build(); + + // Build preamble with no includes. + TU.Code = ""; + StoreDiags Diags; + auto Inputs = TU.inputs(); + auto CI = buildCompilerInvocation(Inputs, Diags); + auto EmptyPreamble = + buildPreamble(testPath("foo.cpp"), *CI, Inputs, true, nullptr); + ASSERT_TRUE(EmptyPreamble); + EXPECT_THAT(EmptyPreamble->Includes.MainFileIncludes, testing::IsEmpty()); + + // Now build an AST using empty preamble and ensure patched includes worked. + TU.Code = ModifiedContents.str(); + Inputs = TU.inputs(); + auto PatchedAST = ParsedAST::build(testPath("foo.cpp"), Inputs, std::move(CI), + {}, EmptyPreamble); + ASSERT_TRUE(PatchedAST); + ASSERT_TRUE(PatchedAST->getDiagnostics().empty()); + + // Ensure source location information is correct, including resolved paths. + EXPECT_THAT(PatchedAST->getIncludeStructure().MainFileIncludes, + testing::Pointwise( + EqInc(), ExpectedAST.getIncludeStructure().MainFileIncludes)); + auto StringMapToVector = [](const llvm::StringMap SM) { + std::vector> Res; + for (const auto &E : SM) + Res.push_back({E.first().str(), E.second}); + llvm::sort(Res); + return Res; + }; + // Ensure file proximity signals are correct. + EXPECT_EQ(StringMapToVector(PatchedAST->getIncludeStructure().includeDepth( + testPath("foo.cpp"))), + StringMapToVector(ExpectedAST.getIncludeStructure().includeDepth( + testPath("foo.cpp")))); +} + +TEST(ParsedASTTest, PatchesDeletedIncludes) { + TestTU TU; + TU.Filename = "foo.cpp"; + TU.Code = ""; + auto ExpectedAST = TU.build(); + + // Build preamble with no includes. + TU.Code = R"cpp(#include )cpp"; + StoreDiags Diags; + auto Inputs = TU.inputs(); + auto CI = buildCompilerInvocation(Inputs, Diags); + auto BaselinePreamble = + buildPreamble(testPath("foo.cpp"), *CI, Inputs, true, nullptr); + ASSERT_TRUE(BaselinePreamble); + EXPECT_THAT(BaselinePreamble->Includes.MainFileIncludes, + ElementsAre(testing::Field(&Inclusion::Written, ""))); + + // Now build an AST using additional includes and check that locations are + // correctly parsed. + TU.Code = ""; + Inputs = TU.inputs(); + auto PatchedAST = ParsedAST::build(testPath("foo.cpp"), Inputs, std::move(CI), + {}, BaselinePreamble); + ASSERT_TRUE(PatchedAST); + + // Ensure source location information is correct. + EXPECT_THAT(PatchedAST->getIncludeStructure().MainFileIncludes, + testing::Pointwise( + EqInc(), ExpectedAST.getIncludeStructure().MainFileIncludes)); + auto StringMapToVector = [](const llvm::StringMap SM) { + std::vector> Res; + for (const auto &E : SM) + Res.push_back({E.first().str(), E.second}); + llvm::sort(Res); + return Res; + }; + // Ensure file proximity signals are correct. + EXPECT_EQ(StringMapToVector(PatchedAST->getIncludeStructure().includeDepth( + testPath("foo.cpp"))), + StringMapToVector(ExpectedAST.getIncludeStructure().includeDepth( + testPath("foo.cpp")))); +} + } // namespace } // namespace clangd } // namespace clang diff --git a/clang-tools-extra/clangd/unittests/PreambleTests.cpp b/clang-tools-extra/clangd/unittests/PreambleTests.cpp index c1801980b1d50..87a68ceea048a 100644 --- a/clang-tools-extra/clangd/unittests/PreambleTests.cpp +++ b/clang-tools-extra/clangd/unittests/PreambleTests.cpp @@ -8,73 +8,72 @@ #include "Annotations.h" #include "Compiler.h" +#include "Headers.h" +#include "Hover.h" #include "Preamble.h" +#include "SourceCode.h" #include "TestFS.h" #include "TestTU.h" +#include "XRefs.h" +#include "clang/Format/Format.h" #include "clang/Frontend/PrecompiledPreamble.h" #include "clang/Lex/PreprocessorOptions.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringRef.h" +#include "llvm/Support/Error.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/VirtualFileSystem.h" #include "gmock/gmock.h" #include "gtest/gtest.h" #include +#include #include #include +using testing::Contains; using testing::Field; +using testing::Matcher; +using testing::MatchesRegex; namespace clang { namespace clangd { namespace { +MATCHER_P2(Distance, File, D, "") { + return arg.first() == File && arg.second == D; +} + // Builds a preamble for BaselineContents, patches it for ModifiedContents and // returns the includes in the patch. IncludeStructure collectPatchedIncludes(llvm::StringRef ModifiedContents, llvm::StringRef BaselineContents, llvm::StringRef MainFileName = "main.cpp") { - std::string MainFile = testPath(MainFileName); - ParseInputs PI; - PI.FS = new llvm::vfs::InMemoryFileSystem; - MockCompilationDatabase CDB; + auto TU = TestTU::withCode(BaselineContents); + TU.Filename = MainFileName.str(); // ms-compatibility changes meaning of #import, make sure it is turned off. - CDB.ExtraClangFlags.push_back("-fno-ms-compatibility"); - PI.CompileCommand = CDB.getCompileCommand(MainFile).getValue(); - // Create invocation - IgnoreDiagnostics Diags; - auto CI = buildCompilerInvocation(PI, Diags); - assert(CI && "failed to create compiler invocation"); - // Build baseline preamble. - PI.Contents = BaselineContents.str(); - PI.Version = "baseline preamble"; - auto BaselinePreamble = buildPreamble(MainFile, *CI, PI, true, nullptr); - assert(BaselinePreamble && "failed to build baseline preamble"); + TU.ExtraArgs = {"-fno-ms-compatibility"}; + auto BaselinePreamble = TU.preamble(); // Create the patch. - PI.Contents = ModifiedContents.str(); - PI.Version = "modified contents"; - auto PP = PreamblePatch::create(MainFile, PI, *BaselinePreamble); + TU.Code = ModifiedContents.str(); + auto PI = TU.inputs(); + auto PP = PreamblePatch::create(testPath(TU.Filename), PI, *BaselinePreamble); // Collect patch contents. + IgnoreDiagnostics Diags; + auto CI = buildCompilerInvocation(PI, Diags); PP.apply(*CI); - llvm::StringRef PatchContents; - for (const auto &Rempaped : CI->getPreprocessorOpts().RemappedFileBuffers) { - if (Rempaped.first == testPath("__preamble_patch__.h")) { - PatchContents = Rempaped.second->getBuffer(); - break; - } - } - // Run preprocessor over the modified contents with patched Invocation to and - // BaselinePreamble to collect includes in the patch. We trim the input to - // only preamble section to not collect includes in the mainfile. + // Run preprocessor over the modified contents with patched Invocation. We + // provide a preamble and trim contents to ensure only the implicit header + // introduced by the patch is parsed and nothing else. + // We don't run PP directly over the patch cotents to test production + // behaviour. auto Bounds = Lexer::ComputePreamble(ModifiedContents, *CI->getLangOpts()); auto Clang = prepareCompilerInstance(std::move(CI), &BaselinePreamble->Preamble, llvm::MemoryBuffer::getMemBufferCopy( ModifiedContents.slice(0, Bounds.Size).str()), PI.FS, Diags); - Clang->getPreprocessorOpts().ImplicitPCHInclude.clear(); PreprocessOnlyAction Action; if (!Action.BeginSourceFile(*Clang, Clang->getFrontendOpts().Inputs[0])) { ADD_FAILURE() << "failed begin source file"; @@ -118,6 +117,11 @@ TEST(PreamblePatchTest, IncludeParsing) { ^#include "a.h" #include )cpp", + // Directive is not part of preamble if it is not the token immediately + // followed by the hash (#). + R"cpp( + ^#include "a.h" + #/**/include )cpp", }; for (const auto Case : Cases) { @@ -158,6 +162,328 @@ TEST(PreamblePatchTest, MainFileIsEscaped) { EXPECT_THAT(Includes, ElementsAre(AllOf(Field(&Inclusion::Written, ""), Field(&Inclusion::HashLine, 0)))); } + +TEST(PreamblePatchTest, PatchesPreambleIncludes) { + IgnoreDiagnostics Diags; + auto TU = TestTU::withCode(R"cpp( + #include "a.h" + #include "c.h" + )cpp"); + TU.AdditionalFiles["a.h"] = "#include \"b.h\""; + TU.AdditionalFiles["b.h"] = ""; + TU.AdditionalFiles["c.h"] = ""; + auto PI = TU.inputs(); + auto BaselinePreamble = buildPreamble( + TU.Filename, *buildCompilerInvocation(PI, Diags), PI, true, nullptr); + // We drop c.h from modified and add a new header. Since the latter is patched + // we should only get a.h in preamble includes. + TU.Code = R"cpp( + #include "a.h" + #include "b.h" + )cpp"; + auto PP = PreamblePatch::create(testPath(TU.Filename), TU.inputs(), + *BaselinePreamble); + // Only a.h should exists in the preamble, as c.h has been dropped and b.h was + // newly introduced. + EXPECT_THAT(PP.preambleIncludes(), + ElementsAre(AllOf(Field(&Inclusion::Written, "\"a.h\""), + Field(&Inclusion::Resolved, testPath("a.h"))))); +} + +llvm::Optional createPatchedAST(llvm::StringRef Baseline, + llvm::StringRef Modified) { + auto BaselinePreamble = TestTU::withCode(Baseline).preamble(); + if (!BaselinePreamble) { + ADD_FAILURE() << "Failed to build baseline preamble"; + return llvm::None; + } + + IgnoreDiagnostics Diags; + auto TU = TestTU::withCode(Modified); + auto CI = buildCompilerInvocation(TU.inputs(), Diags); + if (!CI) { + ADD_FAILURE() << "Failed to build compiler invocation"; + return llvm::None; + } + return ParsedAST::build(testPath(TU.Filename), TU.inputs(), std::move(CI), {}, + BaselinePreamble); +} + +std::string getPreamblePatch(llvm::StringRef Baseline, + llvm::StringRef Modified) { + auto BaselinePreamble = TestTU::withCode(Baseline).preamble(); + if (!BaselinePreamble) { + ADD_FAILURE() << "Failed to build baseline preamble"; + return ""; + } + auto TU = TestTU::withCode(Modified); + return PreamblePatch::create(testPath("main.cpp"), TU.inputs(), + *BaselinePreamble) + .text() + .str(); +} + +TEST(PreamblePatchTest, Define) { + // BAR should be defined while parsing the AST. + struct { + llvm::StringLiteral Contents; + llvm::StringLiteral ExpectedPatch; + } Cases[] = { + { + R"cpp( + #define BAR + [[BAR]])cpp", + R"cpp(#line 0 ".*main.cpp" +#line 2 +#define BAR +)cpp", + }, + // multiline macro + { + R"cpp( + #define BAR \ + + [[BAR]])cpp", + R"cpp(#line 0 ".*main.cpp" +#line 2 +#define BAR +)cpp", + }, + // multiline macro + { + R"cpp( + #define \ + BAR + [[BAR]])cpp", + R"cpp(#line 0 ".*main.cpp" +#line 3 +#define BAR +)cpp", + }, + }; + + for (const auto &Case : Cases) { + SCOPED_TRACE(Case.Contents); + Annotations Modified(Case.Contents); + EXPECT_THAT(getPreamblePatch("", Modified.code()), + MatchesRegex(Case.ExpectedPatch.str())); + + auto AST = createPatchedAST("", Modified.code()); + ASSERT_TRUE(AST); + EXPECT_THAT(AST->getDiagnostics(), + Not(Contains(Field(&Diag::Range, Modified.range())))); + } +} + +TEST(PreamblePatchTest, OrderingPreserved) { + llvm::StringLiteral Baseline = "#define BAR(X) X"; + Annotations Modified(R"cpp( + #define BAR(X, Y) X Y + #define BAR(X) X + [[BAR]](int y); + )cpp"); + + llvm::StringLiteral ExpectedPatch(R"cpp(#line 0 ".*main.cpp" +#line 2 +#define BAR\(X, Y\) X Y +#line 3 +#define BAR\(X\) X +)cpp"); + EXPECT_THAT(getPreamblePatch(Baseline, Modified.code()), + MatchesRegex(ExpectedPatch.str())); + + auto AST = createPatchedAST(Baseline, Modified.code()); + ASSERT_TRUE(AST); + EXPECT_THAT(AST->getDiagnostics(), + Not(Contains(Field(&Diag::Range, Modified.range())))); +} + +TEST(PreamblePatchTest, LocateMacroAtWorks) { + struct { + llvm::StringLiteral Baseline; + llvm::StringLiteral Modified; + } Cases[] = { + // Addition of new directive + { + "", + R"cpp( + #define $def^FOO + $use^FOO)cpp", + }, + // Available inside preamble section + { + "", + R"cpp( + #define $def^FOO + #undef $use^FOO)cpp", + }, + // Available after undef, as we don't patch those + { + "", + R"cpp( + #define $def^FOO + #undef FOO + $use^FOO)cpp", + }, + // Identifier on a different line + { + "", + R"cpp( + #define \ + $def^FOO + $use^FOO)cpp", + }, + // In presence of comment tokens + { + "", + R"cpp( + #\ + define /* FOO */\ + /* FOO */ $def^FOO + $use^FOO)cpp", + }, + // Moved around + { + "#define FOO", + R"cpp( + #define BAR + #define $def^FOO + $use^FOO)cpp", + }, + }; + for (const auto &Case : Cases) { + SCOPED_TRACE(Case.Modified); + llvm::Annotations Modified(Case.Modified); + auto AST = createPatchedAST(Case.Baseline, Modified.code()); + ASSERT_TRUE(AST); + + const auto &SM = AST->getSourceManager(); + auto *MacroTok = AST->getTokens().spelledTokenAt( + SM.getComposedLoc(SM.getMainFileID(), Modified.point("use"))); + ASSERT_TRUE(MacroTok); + + auto FoundMacro = locateMacroAt(*MacroTok, AST->getPreprocessor()); + ASSERT_TRUE(FoundMacro); + EXPECT_THAT(FoundMacro->Name, "FOO"); + + auto MacroLoc = FoundMacro->NameLoc; + EXPECT_EQ(SM.getFileID(MacroLoc), SM.getMainFileID()); + EXPECT_EQ(SM.getFileOffset(MacroLoc), Modified.point("def")); + } +} + +TEST(PreamblePatchTest, LocateMacroAtDeletion) { + { + // We don't patch deleted define directives, make sure we don't crash. + llvm::StringLiteral Baseline = "#define FOO"; + llvm::Annotations Modified("^FOO"); + + auto AST = createPatchedAST(Baseline, Modified.code()); + ASSERT_TRUE(AST); + + const auto &SM = AST->getSourceManager(); + auto *MacroTok = AST->getTokens().spelledTokenAt( + SM.getComposedLoc(SM.getMainFileID(), Modified.point())); + ASSERT_TRUE(MacroTok); + + auto FoundMacro = locateMacroAt(*MacroTok, AST->getPreprocessor()); + ASSERT_TRUE(FoundMacro); + EXPECT_THAT(FoundMacro->Name, "FOO"); + auto HI = + getHover(*AST, offsetToPosition(Modified.code(), Modified.point()), + format::getLLVMStyle(), nullptr); + ASSERT_TRUE(HI); + EXPECT_THAT(HI->Definition, testing::IsEmpty()); + } + + { + // Offset is valid, but underlying text is different. + llvm::StringLiteral Baseline = "#define FOO"; + Annotations Modified(R"cpp(#define BAR + ^FOO")cpp"); + + auto AST = createPatchedAST(Baseline, Modified.code()); + ASSERT_TRUE(AST); + + auto HI = getHover(*AST, Modified.point(), format::getLLVMStyle(), nullptr); + ASSERT_TRUE(HI); + EXPECT_THAT(HI->Definition, "#define BAR"); + } +} + +TEST(PreamblePatchTest, RefsToMacros) { + struct { + llvm::StringLiteral Baseline; + llvm::StringLiteral Modified; + } Cases[] = { + // Newly added + { + "", + R"cpp( + #define ^FOO + ^[[FOO]])cpp", + }, + // Moved around + { + "#define FOO", + R"cpp( + #define BAR + #define ^FOO + ^[[FOO]])cpp", + }, + // Ref in preamble section + { + "", + R"cpp( + #define ^FOO + #undef ^FOO)cpp", + }, + }; + + for (const auto &Case : Cases) { + Annotations Modified(Case.Modified); + auto AST = createPatchedAST("", Modified.code()); + ASSERT_TRUE(AST); + + const auto &SM = AST->getSourceManager(); + std::vector> ExpectedLocations; + for (const auto &R : Modified.ranges()) + ExpectedLocations.push_back(Field(&Location::range, R)); + + for (const auto &P : Modified.points()) { + auto *MacroTok = AST->getTokens().spelledTokenAt(SM.getComposedLoc( + SM.getMainFileID(), + llvm::cantFail(positionToOffset(Modified.code(), P)))); + ASSERT_TRUE(MacroTok); + EXPECT_THAT(findReferences(*AST, P, 0).References, + testing::ElementsAreArray(ExpectedLocations)); + } + } +} + +TEST(TranslatePreamblePatchLocation, Simple) { + auto TU = TestTU::withHeaderCode(R"cpp( + #line 3 "main.cpp" + int foo();)cpp"); + // Presumed line/col needs to be valid in the main file. + TU.Code = R"cpp(// line 1 + // line 2 + // line 3 + // line 4)cpp"; + TU.Filename = "main.cpp"; + TU.HeaderFilename = "__preamble_patch__.h"; + TU.ImplicitHeaderGuard = false; + + auto AST = TU.build(); + auto &SM = AST.getSourceManager(); + auto &ND = findDecl(AST, "foo"); + EXPECT_NE(SM.getFileID(ND.getLocation()), SM.getMainFileID()); + + auto TranslatedLoc = translatePreamblePatchLocation(ND.getLocation(), SM); + auto DecompLoc = SM.getDecomposedLoc(TranslatedLoc); + EXPECT_EQ(DecompLoc.first, SM.getMainFileID()); + EXPECT_EQ(SM.getLineNumber(DecompLoc.first, DecompLoc.second), 3U); +} } // namespace } // namespace clangd } // namespace clang diff --git a/clang-tools-extra/clangd/unittests/TUSchedulerTests.cpp b/clang-tools-extra/clangd/unittests/TUSchedulerTests.cpp index 9e6ad6c9b6e1e..8c8520aae620c 100644 --- a/clang-tools-extra/clangd/unittests/TUSchedulerTests.cpp +++ b/clang-tools-extra/clangd/unittests/TUSchedulerTests.cpp @@ -21,14 +21,20 @@ #include "support/Threading.h" #include "clang/Basic/DiagnosticDriver.h" #include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/FunctionExtras.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/ScopeExit.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/ADT/StringRef.h" #include "gmock/gmock.h" #include "gtest/gtest.h" #include #include #include #include +#include +#include #include namespace clang { @@ -407,6 +413,7 @@ TEST_F(TUSchedulerTests, ManyUpdates) { int TotalASTReads = 0; int TotalPreambleReads = 0; int TotalUpdates = 0; + llvm::StringMap LatestDiagVersion; // Run TUScheduler and collect some stats. { @@ -441,15 +448,23 @@ TEST_F(TUSchedulerTests, ManyUpdates) { auto Inputs = getInputs(File, Contents.str()); { WithContextValue WithNonce(NonceKey, ++Nonce); - Inputs.Version = std::to_string(Nonce); + Inputs.Version = std::to_string(UpdateI); updateWithDiags( S, File, Inputs, WantDiagnostics::Auto, - [File, Nonce, &Mut, &TotalUpdates](std::vector) { + [File, Nonce, Version(Inputs.Version), &Mut, &TotalUpdates, + &LatestDiagVersion](std::vector) { EXPECT_THAT(Context::current().get(NonceKey), Pointee(Nonce)); std::lock_guard Lock(Mut); ++TotalUpdates; EXPECT_EQ(File, *TUScheduler::getFileBeingProcessedInContext()); + // Make sure Diags are for a newer version. + auto It = LatestDiagVersion.try_emplace(File, -1); + const int PrevVersion = It.first->second; + int CurVersion; + ASSERT_TRUE(llvm::to_integer(Version, CurVersion, 10)); + EXPECT_LT(PrevVersion, CurVersion); + It.first->getValue() = CurVersion; }); } { @@ -494,7 +509,13 @@ TEST_F(TUSchedulerTests, ManyUpdates) { } // TUScheduler destructor waits for all operations to finish. std::lock_guard Lock(Mut); - EXPECT_EQ(TotalUpdates, FilesCount * UpdatesPerFile); + // Updates might get coalesced in preamble thread and result in dropping + // diagnostics for intermediate snapshots. + EXPECT_GE(TotalUpdates, FilesCount); + EXPECT_LE(TotalUpdates, FilesCount * UpdatesPerFile); + // We should receive diags for last update. + for (const auto &Entry : LatestDiagVersion) + EXPECT_EQ(Entry.second, UpdatesPerFile - 1); EXPECT_EQ(TotalASTReads, FilesCount * UpdatesPerFile); EXPECT_EQ(TotalPreambleReads, FilesCount * UpdatesPerFile); } @@ -972,6 +993,57 @@ TEST(DebouncePolicy, Compute) { EXPECT_NEAR(25, Compute({}), 0.01) << "no history -> max"; } +TEST_F(TUSchedulerTests, AsyncPreambleThread) { + // Blocks preamble thread while building preamble with \p BlockVersion until + // \p N is notified. + class BlockPreambleThread : public ParsingCallbacks { + public: + BlockPreambleThread(llvm::StringRef BlockVersion, Notification &N) + : BlockVersion(BlockVersion), N(N) {} + void onPreambleAST(PathRef Path, llvm::StringRef Version, ASTContext &Ctx, + std::shared_ptr PP, + const CanonicalIncludes &) override { + if (Version == BlockVersion) + N.wait(); + } + + private: + llvm::StringRef BlockVersion; + Notification &N; + }; + + static constexpr llvm::StringLiteral InputsV0 = "v0"; + static constexpr llvm::StringLiteral InputsV1 = "v1"; + Notification Ready; + TUScheduler S(CDB, optsForTest(), + std::make_unique(InputsV1, Ready)); + + Path File = testPath("foo.cpp"); + auto PI = getInputs(File, ""); + PI.Version = InputsV0.str(); + S.update(File, PI, WantDiagnostics::Auto); + S.blockUntilIdle(timeoutSeconds(10)); + + // Block preamble builds. + PI.Version = InputsV1.str(); + // Issue second update which will block preamble thread. + S.update(File, PI, WantDiagnostics::Auto); + + Notification RunASTAction; + // Issue an AST read, which shouldn't be blocked and see latest version of the + // file. + S.runWithAST("test", File, [&](Expected AST) { + ASSERT_TRUE(bool(AST)); + // Make sure preamble is built with stale inputs, but AST was built using + // new ones. + EXPECT_THAT(AST->AST.preambleVersion(), InputsV0); + EXPECT_THAT(AST->Inputs.Version, InputsV1.str()); + RunASTAction.notify(); + }); + RunASTAction.wait(); + Ready.notify(); +} + } // namespace } // namespace clangd } // namespace clang diff --git a/clang-tools-extra/clangd/unittests/TestTU.cpp b/clang-tools-extra/clangd/unittests/TestTU.cpp index 824c4cc8ff143..b4781425943c6 100644 --- a/clang-tools-extra/clangd/unittests/TestTU.cpp +++ b/clang-tools-extra/clangd/unittests/TestTU.cpp @@ -66,14 +66,24 @@ ParseInputs TestTU::inputs() const { return Inputs; } +std::shared_ptr TestTU::preamble() const { + auto Inputs = inputs(); + IgnoreDiagnostics Diags; + auto CI = buildCompilerInvocation(Inputs, Diags); + assert(CI && "Failed to build compilation invocation."); + return clang::clangd::buildPreamble(testPath(Filename), *CI, Inputs, + /*StoreInMemory=*/true, + /*PreambleCallback=*/nullptr); +} + ParsedAST TestTU::build() const { auto Inputs = inputs(); StoreDiags Diags; auto CI = buildCompilerInvocation(Inputs, Diags); assert(CI && "Failed to build compilation invocation."); - auto Preamble = - buildPreamble(testPath(Filename), *CI, Inputs, - /*StoreInMemory=*/true, /*PreambleCallback=*/nullptr); + auto Preamble = clang::clangd::buildPreamble(testPath(Filename), *CI, Inputs, + /*StoreInMemory=*/true, + /*PreambleCallback=*/nullptr); auto AST = ParsedAST::build(testPath(Filename), Inputs, std::move(CI), Diags.take(), Preamble); if (!AST.hasValue()) { diff --git a/clang-tools-extra/clangd/unittests/TestTU.h b/clang-tools-extra/clangd/unittests/TestTU.h index 2be294f78e7ec..57017f4a91753 100644 --- a/clang-tools-extra/clangd/unittests/TestTU.h +++ b/clang-tools-extra/clangd/unittests/TestTU.h @@ -68,6 +68,7 @@ struct TestTU { // By default, build() will report Error diagnostics as GTest errors. // Suppress this behavior by adding an 'error-ok' comment to the code. ParsedAST build() const; + std::shared_ptr preamble() const; ParseInputs inputs() const; SymbolSlab headerSymbols() const; RefSlab headerRefs() const; diff --git a/clang-tools-extra/clangd/unittests/TweakTests.cpp b/clang-tools-extra/clangd/unittests/TweakTests.cpp index b0a941dae5d2c..319d9e088c2d8 100644 --- a/clang-tools-extra/clangd/unittests/TweakTests.cpp +++ b/clang-tools-extra/clangd/unittests/TweakTests.cpp @@ -2059,21 +2059,57 @@ TEST_F(DefineOutlineTest, ApplyTest) { "void foo(int x, int y = 5, int = 2, int (*foo)(int) = nullptr) ;", "void foo(int x, int y , int , int (*foo)(int) ) {}", }, - // Ctor initializers. + // Constructors + { + R"cpp( + class Foo {public: Foo(); Foo(int);}; + class Bar { + Ba^r() {} + Bar(int x) : f1(x) {} + Foo f1; + Foo f2 = 2; + };)cpp", + R"cpp( + class Foo {public: Foo(); Foo(int);}; + class Bar { + Bar() ; + Bar(int x) : f1(x) {} + Foo f1; + Foo f2 = 2; + };)cpp", + "Bar::Bar() {}\n", + }, + // Ctor with initializer. + { + R"cpp( + class Foo {public: Foo(); Foo(int);}; + class Bar { + Bar() {} + B^ar(int x) : f1(x), f2(3) {} + Foo f1; + Foo f2 = 2; + };)cpp", + R"cpp( + class Foo {public: Foo(); Foo(int);}; + class Bar { + Bar() {} + Bar(int x) ; + Foo f1; + Foo f2 = 2; + };)cpp", + "Bar::Bar(int x) : f1(x), f2(3) {}\n", + }, + // Ctor initializer with attribute. { R"cpp( class Foo { - int y = 2; F^oo(int z) __attribute__((weak)) : bar(2){} int bar; - int z = 2; };)cpp", R"cpp( class Foo { - int y = 2; Foo(int z) __attribute__((weak)) ; int bar; - int z = 2; };)cpp", "Foo::Foo(int z) __attribute__((weak)) : bar(2){}\n", }, diff --git a/clang-tools-extra/clangd/unittests/XRefsTests.cpp b/clang-tools-extra/clangd/unittests/XRefsTests.cpp index 77e863895f803..e260285a179cf 100644 --- a/clang-tools-extra/clangd/unittests/XRefsTests.cpp +++ b/clang-tools-extra/clangd/unittests/XRefsTests.cpp @@ -116,6 +116,143 @@ TEST(HighlightsTest, All) { } } +TEST(HighlightsTest, ControlFlow) { + const char *Tests[] = { + R"cpp( + // Highlight same-function returns. + int fib(unsigned n) { + if (n <= 1) [[ret^urn]] 1; + [[return]] fib(n - 1) + fib(n - 2); + + // Returns from other functions not highlighted. + auto Lambda = [] { return; }; + class LocalClass { void x() { return; } }; + } + )cpp", + + R"cpp( + #define FAIL() return false + #define DO(x) { x; } + bool foo(int n) { + if (n < 0) [[FAIL]](); + DO([[re^turn]] true) + } + )cpp", + + R"cpp( + // Highlight loop control flow + int magic() { + int counter = 0; + [[^for]] (char c : "fruit loops!") { + if (c == ' ') [[continue]]; + counter += c; + if (c == '!') [[break]]; + if (c == '?') [[return]] -1; + } + return counter; + } + )cpp", + + R"cpp( + // Highlight loop and same-loop control flow + void nonsense() { + [[while]] (true) { + if (false) [[bre^ak]]; + switch (1) break; + [[continue]]; + } + } + )cpp", + + R"cpp( + // Highlight switch for break (but not other breaks). + void describe(unsigned n) { + [[switch]](n) { + case 0: + break; + [[default]]: + [[^break]]; + } + } + )cpp", + + R"cpp( + // Highlight case and exits for switch-break (but not other cases). + void describe(unsigned n) { + [[switch]](n) { + case 0: + break; + [[case]] 1: + [[default]]: + [[return]]; + [[^break]]; + } + } + )cpp", + + R"cpp( + // Highlight exits and switch for case + void describe(unsigned n) { + [[switch]](n) { + case 0: + break; + [[case]] 1: + [[d^efault]]: + [[return]]; + [[break]]; + } + } + )cpp", + + R"cpp( + // Highlight nothing for switch. + void describe(unsigned n) { + s^witch(n) { + case 0: + break; + case 1: + default: + return; + break; + } + } + )cpp", + + R"cpp( + // FIXME: match exception type against catch blocks + int catchy() { + try { // wrong: highlight try with matching catch + try { // correct: has no matching catch + [[thr^ow]] "oh no!"; + } catch (int) { } // correct: catch doesn't match type + [[return]] -1; // correct: exits the matching catch + } catch (const char*) { } // wrong: highlight matching catch + [[return]] 42; // wrong: throw doesn't exit function + } + )cpp", + + R"cpp( + // Loop highlights goto exiting the loop, but not jumping within it. + void jumpy() { + [[wh^ile]](1) { + up: + if (0) [[goto]] out; + goto up; + } + out: return; + } + )cpp", + }; + for (const char *Test : Tests) { + Annotations T(Test); + auto TU = TestTU::withCode(T.code()); + TU.ExtraArgs.push_back("-fexceptions"); // FIXME: stop testing on PS4. + auto AST = TU.build(); + EXPECT_THAT(findDocumentHighlights(AST, T.point()), HighlightsFrom(T)) + << Test; + } +} + MATCHER_P3(Sym, Name, Decl, DefOrNone, "") { llvm::Optional Def = DefOrNone; if (Name != arg.Name) { diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst index 1827dfe913380..e6583c17978b0 100644 --- a/clang-tools-extra/docs/ReleaseNotes.rst +++ b/clang-tools-extra/docs/ReleaseNotes.rst @@ -75,6 +75,13 @@ New module New checks ^^^^^^^^^^ + +- New :doc:`abseil-string-find-str-contains + ` check. + + Finds ``s.find(...) == string::npos`` comparisons (for various string-like types) + and suggests replacing with ``absl::StrContains()``. + - New :doc:`cppcoreguidelines-avoid-non-const-global-variables ` check. Finds non-const global variables as described in check I.2 of C++ Core diff --git a/clang-tools-extra/docs/clang-tidy/checks/abseil-string-find-str-contains.rst b/clang-tools-extra/docs/clang-tidy/checks/abseil-string-find-str-contains.rst new file mode 100644 index 0000000000000..4cf99d5877a95 --- /dev/null +++ b/clang-tools-extra/docs/clang-tidy/checks/abseil-string-find-str-contains.rst @@ -0,0 +1,52 @@ +.. title:: clang-tidy - abseil-string-find-str-contains + +abseil-string-find-str-contains +=============================== + +Finds ``s.find(...) == string::npos`` comparisons (for various string-like types) +and suggests replacing with ``absl::StrContains()``. + +This improves readability and reduces the likelihood of accidentally mixing +``find()`` and ``npos`` from different string-like types. + +By default, "string-like types" includes ``::std::basic_string``, +``::std::basic_string_view``, and ``::absl::string_view``. See the +StringLikeClasses option to change this. + +.. code-block:: c++ + + std::string s = "..."; + if (s.find("Hello World") == std::string::npos) { /* do something */ } + + absl::string_view a = "..."; + if (absl::string_view::npos != a.find("Hello World")) { /* do something */ } + +becomes + +.. code-block:: c++ + + std::string s = "..."; + if (!absl::StrContains(s, "Hello World")) { /* do something */ } + + absl::string_view a = "..."; + if (absl::StrContains(a, "Hello World")) { /* do something */ } + + +Options +------- + +.. option:: StringLikeClasses + + Semicolon-separated list of names of string-like classes. By default includes + ``::std::basic_string``, ``::std::basic_string_view``, and + ``::absl::string_view``. + +.. option:: IncludeStyle + + A string specifying which include-style is used, `llvm` or `google`. Default + is `llvm`. + +.. option:: AbseilStringsMatchHeader + + The location of Abseil's ``strings/match.h``. Defaults to + ``absl/strings/match.h``. diff --git a/clang-tools-extra/docs/clang-tidy/checks/list.rst b/clang-tools-extra/docs/clang-tidy/checks/list.rst index b4d09d5267262..6d5f8fcbb05a5 100644 --- a/clang-tools-extra/docs/clang-tidy/checks/list.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/list.rst @@ -26,6 +26,7 @@ Clang-Tidy Checks `abseil-redundant-strcat-calls `_, "Yes" `abseil-str-cat-append `_, "Yes" `abseil-string-find-startswith `_, "Yes" + `abseil-string-find-str-contains `_, "Yes" `abseil-time-comparison `_, "Yes" `abseil-time-subtraction `_, "Yes" `abseil-upgrade-duration-conversions `_, "Yes" diff --git a/clang-tools-extra/test/clang-query/invalid-command-line.cpp b/clang-tools-extra/test/clang-query/invalid-command-line.cpp new file mode 100644 index 0000000000000..901aad8c1f237 --- /dev/null +++ b/clang-tools-extra/test/clang-query/invalid-command-line.cpp @@ -0,0 +1,4 @@ +// RUN: not clang-query --invalid-arg 2>&1 | FileCheck %s + +// CHECK: error: [CommonOptionsParser]: clang-query{{(\.exe)?}}: Unknown command line argument '--invalid-arg'. Try: 'clang-query{{(\.exe)?}} --help' +// CHECK-NEXT: clang-query{{(\.exe)?}}: Did you mean '--extra-arg'? diff --git a/clang-tools-extra/test/clang-tidy/checkers/abseil-string-find-str-contains.cpp b/clang-tools-extra/test/clang-tidy/checkers/abseil-string-find-str-contains.cpp new file mode 100644 index 0000000000000..871c830b81cf8 --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/abseil-string-find-str-contains.cpp @@ -0,0 +1,290 @@ +// RUN: %check_clang_tidy %s abseil-string-find-str-contains %t -- \ +// RUN: -config="{CheckOptions: []}" + +using size_t = decltype(sizeof(int)); + +namespace std { + +// Lightweight standin for std::string. +template +class basic_string { +public: + basic_string(); + basic_string(const basic_string &); + basic_string(const C *); + ~basic_string(); + int find(basic_string s, int pos = 0); + int find(const C *s, int pos = 0); + int find(char c, int pos = 0); + static constexpr size_t npos = -1; +}; +typedef basic_string string; + +// Lightweight standin for std::string_view. +template +class basic_string_view { +public: + basic_string_view(); + basic_string_view(const basic_string_view &); + basic_string_view(const C *); + ~basic_string_view(); + int find(basic_string_view s, int pos = 0); + int find(const C *s, int pos = 0); + int find(char c, int pos = 0); + static constexpr size_t npos = -1; +}; +typedef basic_string_view string_view; + +} // namespace std + +namespace absl { + +// Lightweight standin for absl::string_view. +class string_view { +public: + string_view(); + string_view(const string_view &); + string_view(const char *); + ~string_view(); + int find(string_view s, int pos = 0); + int find(const char *s, int pos = 0); + int find(char c, int pos = 0); + static constexpr size_t npos = -1; +}; + +} // namespace absl + +// Functions that take and return our various string-like types. +std::string foo_ss(std::string); +std::string_view foo_ssv(std::string_view); +absl::string_view foo_asv(absl::string_view); +std::string bar_ss(); +std::string_view bar_ssv(); +absl::string_view bar_asv(); + +// Confirms that find==npos and find!=npos work for each supported type, when +// npos comes from the correct type. +void basic_tests() { + std::string ss; + ss.find("a") == std::string::npos; + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use !absl::StrContains instead of find() == npos + // CHECK-FIXES: {{^[[:space:]]*}}!absl::StrContains(ss, "a");{{$}} + + ss.find("a") != std::string::npos; + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use absl::StrContains instead of find() != npos + // CHECK-FIXES: {{^[[:space:]]*}}absl::StrContains(ss, "a");{{$}} + + std::string::npos != ss.find("a"); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use absl::StrContains instead of + // CHECK-FIXES: {{^[[:space:]]*}}absl::StrContains(ss, "a");{{$}} + + std::string_view ssv; + ssv.find("a") == std::string_view::npos; + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use !absl::StrContains instead of + // CHECK-FIXES: {{^[[:space:]]*}}!absl::StrContains(ssv, "a");{{$}} + + ssv.find("a") != std::string_view::npos; + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use absl::StrContains instead of + // CHECK-FIXES: {{^[[:space:]]*}}absl::StrContains(ssv, "a");{{$}} + + std::string_view::npos != ssv.find("a"); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use absl::StrContains instead of + // CHECK-FIXES: {{^[[:space:]]*}}absl::StrContains(ssv, "a");{{$}} + + absl::string_view asv; + asv.find("a") == absl::string_view::npos; + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use !absl::StrContains instead of + // CHECK-FIXES: {{^[[:space:]]*}}!absl::StrContains(asv, "a");{{$}} + + asv.find("a") != absl::string_view::npos; + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use absl::StrContains instead of + // CHECK-FIXES: {{^[[:space:]]*}}absl::StrContains(asv, "a");{{$}} + + absl::string_view::npos != asv.find("a"); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use absl::StrContains instead of + // CHECK-FIXES: {{^[[:space:]]*}}absl::StrContains(asv, "a");{{$}} +} + +// Confirms that it works even if you mix-and-match the type for find and for +// npos. (One of the reasons for this checker is to clean up cases that +// accidentally mix-and-match like this. absl::StrContains is less +// error-prone.) +void mismatched_npos() { + std::string ss; + ss.find("a") == std::string_view::npos; + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use !absl::StrContains instead of + // CHECK-FIXES: {{^[[:space:]]*}}!absl::StrContains(ss, "a");{{$}} + + ss.find("a") != absl::string_view::npos; + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use absl::StrContains instead of + // CHECK-FIXES: {{^[[:space:]]*}}absl::StrContains(ss, "a");{{$}} + + std::string_view ssv; + ssv.find("a") == absl::string_view::npos; + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use !absl::StrContains instead of + // CHECK-FIXES: {{^[[:space:]]*}}!absl::StrContains(ssv, "a");{{$}} + + ssv.find("a") != std::string::npos; + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use absl::StrContains instead of + // CHECK-FIXES: {{^[[:space:]]*}}absl::StrContains(ssv, "a");{{$}} + + absl::string_view asv; + asv.find("a") == std::string::npos; + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use !absl::StrContains instead of + // CHECK-FIXES: {{^[[:space:]]*}}!absl::StrContains(asv, "a");{{$}} + + asv.find("a") != std::string_view::npos; + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use absl::StrContains instead of + // CHECK-FIXES: {{^[[:space:]]*}}absl::StrContains(asv, "a");{{$}} +} + +// Confirms that it works even when the needle or the haystack are more +// complicated expressions. +void subexpression_tests() { + std::string ss, ss2; + foo_ss(ss).find(ss2) == std::string::npos; + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use !absl::StrContains instead of + // CHECK-FIXES: {{^[[:space:]]*}}!absl::StrContains(foo_ss(ss), ss2);{{$}} + + ss.find(foo_ss(ss2)) != std::string::npos; + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use absl::StrContains instead of + // CHECK-FIXES: {{^[[:space:]]*}}absl::StrContains(ss, foo_ss(ss2));{{$}} + + foo_ss(bar_ss()).find(foo_ss(ss2)) != std::string::npos; + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use absl::StrContains instead of + // CHECK-FIXES: {{^[[:space:]]*}}absl::StrContains(foo_ss(bar_ss()), foo_ss(ss2));{{$}} + + std::string_view ssv, ssv2; + foo_ssv(ssv).find(ssv2) == std::string_view::npos; + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use !absl::StrContains instead of + // CHECK-FIXES: {{^[[:space:]]*}}!absl::StrContains(foo_ssv(ssv), ssv2);{{$}} + + ssv.find(foo_ssv(ssv2)) != std::string_view::npos; + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use absl::StrContains instead of + // CHECK-FIXES: {{^[[:space:]]*}}absl::StrContains(ssv, foo_ssv(ssv2));{{$}} + + foo_ssv(bar_ssv()).find(foo_ssv(ssv2)) != std::string_view::npos; + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use absl::StrContains instead of + // CHECK-FIXES: {{^[[:space:]]*}}absl::StrContains(foo_ssv(bar_ssv()), foo_ssv(ssv2));{{$}} + + absl::string_view asv, asv2; + foo_asv(asv).find(asv2) == absl::string_view::npos; + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use !absl::StrContains instead of + // CHECK-FIXES: {{^[[:space:]]*}}!absl::StrContains(foo_asv(asv), asv2);{{$}} + + asv.find(foo_asv(asv2)) != absl::string_view::npos; + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use absl::StrContains instead of + // CHECK-FIXES: {{^[[:space:]]*}}absl::StrContains(asv, foo_asv(asv2));{{$}} + + foo_asv(bar_asv()).find(foo_asv(asv2)) != absl::string_view::npos; + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use absl::StrContains instead of + // CHECK-FIXES: {{^[[:space:]]*}}absl::StrContains(foo_asv(bar_asv()), foo_asv(asv2));{{$}} +} + +// Confirms that it works with string literal, char* and const char* parameters. +void string_literal_and_char_ptr_tests() { + char *c = nullptr; + const char *cc = nullptr; + + std::string ss; + ss.find("c") == std::string::npos; + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use !absl::StrContains instead of + // CHECK-FIXES: {{^[[:space:]]*}}!absl::StrContains(ss, "c");{{$}} + + ss.find(c) == std::string::npos; + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use !absl::StrContains instead of + // CHECK-FIXES: {{^[[:space:]]*}}!absl::StrContains(ss, c);{{$}} + + ss.find(cc) == std::string::npos; + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use !absl::StrContains instead of + // CHECK-FIXES: {{^[[:space:]]*}}!absl::StrContains(ss, cc);{{$}} + + std::string_view ssv; + ssv.find("c") == std::string_view::npos; + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use !absl::StrContains instead of + // CHECK-FIXES: {{^[[:space:]]*}}!absl::StrContains(ssv, "c");{{$}} + + ssv.find(c) == std::string_view::npos; + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use !absl::StrContains instead of + // CHECK-FIXES: {{^[[:space:]]*}}!absl::StrContains(ssv, c);{{$}} + + ssv.find(cc) == std::string_view::npos; + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use !absl::StrContains instead of + // CHECK-FIXES: {{^[[:space:]]*}}!absl::StrContains(ssv, cc);{{$}} + + absl::string_view asv; + asv.find("c") == absl::string_view::npos; + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use !absl::StrContains instead of + // CHECK-FIXES: {{^[[:space:]]*}}!absl::StrContains(asv, "c");{{$}} + + asv.find(c) == absl::string_view::npos; + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use !absl::StrContains instead of + // CHECK-FIXES: {{^[[:space:]]*}}!absl::StrContains(asv, c);{{$}} + + asv.find(cc) == absl::string_view::npos; + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use !absl::StrContains instead of + // CHECK-FIXES: {{^[[:space:]]*}}!absl::StrContains(asv, cc);{{$}} +} + +// Confirms that it does *not* match when the parameter to find() is a char, +// because absl::StrContains is not implemented for char. +void no_char_param_tests() { + std::string ss; + ss.find('c') == std::string::npos; + + std::string_view ssv; + ssv.find('c') == std::string_view::npos; + + absl::string_view asv; + asv.find('c') == absl::string_view::npos; +} + +#define COMPARE_MACRO(x, y) ((x) == (y)) +#define FIND_MACRO(x, y) ((x).find(y)) +#define FIND_COMPARE_MACRO(x, y, z) ((x).find(y) == (z)) + +// Confirms that it does not match when a macro is involved. +void no_macros() { + std::string s; + COMPARE_MACRO(s.find("a"), std::string::npos); + FIND_MACRO(s, "a") == std::string::npos; + FIND_COMPARE_MACRO(s, "a", std::string::npos); +} + +// Confirms that it does not match when the pos parameter is non-zero. +void no_nonzero_pos() { + std::string ss; + ss.find("a", 1) == std::string::npos; + + std::string_view ssv; + ssv.find("a", 2) == std::string_view::npos; + + absl::string_view asv; + asv.find("a", 3) == std::string_view::npos; +} + +// Confirms that it does not match when it's compared to something other than +// npos, even if the value is the same as npos. +void no_non_npos() { + std::string ss; + ss.find("a") == 0; + ss.find("a") == 1; + ss.find("a") == -1; + + std::string_view ssv; + ssv.find("a") == 0; + ssv.find("a") == 1; + ssv.find("a") == -1; + + absl::string_view asv; + asv.find("a") == 0; + asv.find("a") == 1; + asv.find("a") == -1; +} + +// Confirms that it does not match if the two operands are the same. +void no_symmetric_operands() { + std::string ss; + ss.find("a") == ss.find("a"); + std::string::npos == std::string::npos; +} diff --git a/clang-tools-extra/test/clang-tidy/checkers/readability-identifier-naming.cpp b/clang-tools-extra/test/clang-tidy/checkers/readability-identifier-naming.cpp index 7983bb30ca649..1bb435e02eb55 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/readability-identifier-naming.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/readability-identifier-naming.cpp @@ -80,7 +80,7 @@ // RUN: {key: readability-identifier-naming.LocalPointerPrefix, value: 'l_'}, \ // RUN: {key: readability-identifier-naming.LocalConstantPointerCase, value: CamelCase}, \ // RUN: {key: readability-identifier-naming.LocalConstantPointerPrefix, value: 'lc_'}, \ -// RUN: ]}' -- -fno-delayed-template-parsing \ +// RUN: ]}' -- -fno-delayed-template-parsing -Dbad_macro \ // RUN: -I%S/Inputs/readability-identifier-naming \ // RUN: -isystem %S/Inputs/readability-identifier-naming/system diff --git a/clang-tools-extra/test/clang-tidy/infrastructure/invalid-command-line.cpp b/clang-tools-extra/test/clang-tidy/infrastructure/invalid-command-line.cpp new file mode 100644 index 0000000000000..be84a08818957 --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/infrastructure/invalid-command-line.cpp @@ -0,0 +1,4 @@ +// RUN: not clang-tidy --invalid-arg 2>&1 | FileCheck %s + +// CHECK: error: [CommonOptionsParser]: clang-tidy{{(\.exe)?}}: Unknown command line argument '--invalid-arg'. Try: 'clang-tidy{{(\.exe)?}} --help' +// CHECK-NEXT: clang-tidy{{(\.exe)?}}: Did you mean '--extra-arg'? diff --git a/clang/CMakeLists.txt b/clang/CMakeLists.txt index 6607a0f5e60db..23998bcbde46b 100644 --- a/clang/CMakeLists.txt +++ b/clang/CMakeLists.txt @@ -714,6 +714,7 @@ if (CLANG_ENABLE_BOOTSTRAP) CLANG_VERSION_MAJOR CLANG_VERSION_MINOR CLANG_VERSION_PATCHLEVEL + CLANG_VENDOR LLVM_VERSION_SUFFIX LLVM_BINUTILS_INCDIR CLANG_REPOSITORY_STRING diff --git a/clang/cmake/caches/CrossWinToARMLinux.cmake b/clang/cmake/caches/CrossWinToARMLinux.cmake index c01c31ae5a722..3d1e961ada8d0 100644 --- a/clang/cmake/caches/CrossWinToARMLinux.cmake +++ b/clang/cmake/caches/CrossWinToARMLinux.cmake @@ -89,9 +89,6 @@ set(LIBCXXABI_LINK_TESTS_WITH_SHARED_LIBCXX OFF CACHE BOOL "") set(LIBCXX_LINK_TESTS_WITH_SHARED_LIBCXXABI OFF CACHE BOOL "") set(LIBCXX_LINK_TESTS_WITH_SHARED_LIBCXX OFF CACHE BOOL "") -# FIXME: Remove this when https://reviews.llvm.org/D78200 is merged. -set(LIBCXX_ENABLE_FILESYSTEM OFF CACHE BOOL "") - set(LIBCXX_USE_COMPILER_RT ON CACHE BOOL "") set(LIBCXX_TARGET_TRIPLE "${CMAKE_C_COMPILER_TARGET}" CACHE STRING "") set(LIBCXX_SYSROOT "${DEFAULT_SYSROOT}" CACHE STRING "") diff --git a/clang/docs/ClangFormat.rst b/clang/docs/ClangFormat.rst index 51ecabe927b51..cc1ef38dbb9df 100644 --- a/clang/docs/ClangFormat.rst +++ b/clang/docs/ClangFormat.rst @@ -240,3 +240,8 @@ In an SVN client, you can do: The option `-U0` will create a diff without context lines (the script would format those as well). + +Current State of Clang Format for LLVM +====================================== + +The following table :doc:`ClangFormattedStatus` shows the current status of clang-formatting for the entire LLVM source tree. diff --git a/clang/docs/ClangFormattedStatus.rst b/clang/docs/ClangFormattedStatus.rst new file mode 100644 index 0000000000000..458bce762e799 --- /dev/null +++ b/clang/docs/ClangFormattedStatus.rst @@ -0,0 +1,6406 @@ +.. raw:: html + + + +.. role:: none +.. role:: part +.. role:: good +.. role:: total + +====================== +Clang Formatted Status +====================== + +:doc:`ClangFormattedStatus` describes the state of LLVM source +tree in terms of conformance to :doc:`ClangFormat` as of: May 29, 2020 17:04:26 (`dac21fd29cd `_). + + +.. list-table:: LLVM Clang-Format Status + :widths: 50 25 25 25 25 + :header-rows: 1 + + * - Directory + - Total Files + - Formatted Files + - Unformatted Files + - % Complete + * - clang/bindings/python/tests/cindex/INPUTS + - `5` + - `3` + - `2` + - :part:`60%` + * - clang/docs/analyzer/checkers + - `2` + - `0` + - `2` + - :none:`0%` + * - clang/examples/AnnotateFunctions + - `1` + - `0` + - `1` + - :none:`0%` + * - clang/examples/Attribute + - `1` + - `0` + - `1` + - :none:`0%` + * - clang/examples/clang-interpreter + - `1` + - `0` + - `1` + - :none:`0%` + * - clang/examples/PrintFunctionNames + - `1` + - `0` + - `1` + - :none:`0%` + * - clang/include/clang/Analysis + - `14` + - `4` + - `10` + - :part:`28%` + * - clang/include/clang/Analysis/Analyses + - `14` + - `2` + - `12` + - :part:`14%` + * - clang/include/clang/Analysis/DomainSpecific + - `2` + - `0` + - `2` + - :none:`0%` + * - clang/include/clang/Analysis/FlowSensitive + - `2` + - `1` + - `1` + - :part:`50%` + * - clang/include/clang/Analysis/Support + - `1` + - `0` + - `1` + - :none:`0%` + * - clang/include/clang/ARCMigrate + - `3` + - `0` + - `3` + - :none:`0%` + * - clang/include/clang/AST + - `113` + - `21` + - `92` + - :part:`18%` + * - clang/include/clang/ASTMatchers + - `5` + - `1` + - `4` + - :part:`20%` + * - clang/include/clang/ASTMatchers/Dynamic + - `4` + - `1` + - `3` + - :part:`25%` + * - clang/include/clang/Basic + - `76` + - `24` + - `52` + - :part:`31%` + * - clang/include/clang/CodeGen + - `9` + - `0` + - `9` + - :none:`0%` + * - clang/include/clang/CrossTU + - `2` + - `1` + - `1` + - :part:`50%` + * - clang/include/clang/DirectoryWatcher + - `1` + - `1` + - `0` + - :good:`100%` + * - clang/include/clang/Driver + - `17` + - `4` + - `13` + - :part:`23%` + * - clang/include/clang/Edit + - `5` + - `1` + - `4` + - :part:`20%` + * - clang/include/clang/Format + - `1` + - `1` + - `0` + - :good:`100%` + * - clang/include/clang/Frontend + - `28` + - `7` + - `21` + - :part:`25%` + * - clang/include/clang/FrontendTool + - `1` + - `0` + - `1` + - :none:`0%` + * - clang/include/clang/Index + - `7` + - `2` + - `5` + - :part:`28%` + * - clang/include/clang/Lex + - `29` + - `4` + - `25` + - :part:`13%` + * - clang/include/clang/Parse + - `5` + - `2` + - `3` + - :part:`40%` + * - clang/include/clang/Rewrite/Core + - `6` + - `0` + - `6` + - :none:`0%` + * - clang/include/clang/Rewrite/Frontend + - `4` + - `0` + - `4` + - :none:`0%` + * - clang/include/clang/Sema + - `32` + - `3` + - `29` + - :part:`9%` + * - clang/include/clang/Serialization + - `14` + - `2` + - `12` + - :part:`14%` + * - clang/include/clang/StaticAnalyzer/Checkers + - `4` + - `1` + - `3` + - :part:`25%` + * - clang/include/clang/StaticAnalyzer/Core + - `5` + - `1` + - `4` + - :part:`20%` + * - clang/include/clang/StaticAnalyzer/Core/BugReporter + - `4` + - `1` + - `3` + - :part:`25%` + * - clang/include/clang/StaticAnalyzer/Core/PathSensitive + - `36` + - `9` + - `27` + - :part:`25%` + * - clang/include/clang/StaticAnalyzer/Frontend + - `5` + - `3` + - `2` + - :part:`60%` + * - clang/include/clang/Tooling + - `16` + - `9` + - `7` + - :part:`56%` + * - clang/include/clang/Tooling/ASTDiff + - `2` + - `2` + - `0` + - :good:`100%` + * - clang/include/clang/Tooling/Core + - `3` + - `1` + - `2` + - :part:`33%` + * - clang/include/clang/Tooling/DependencyScanning + - `5` + - `4` + - `1` + - :part:`80%` + * - clang/include/clang/Tooling/Inclusions + - `2` + - `0` + - `2` + - :none:`0%` + * - clang/include/clang/Tooling/Refactoring + - `14` + - `12` + - `2` + - :part:`85%` + * - clang/include/clang/Tooling/Refactoring/Extract + - `2` + - `1` + - `1` + - :part:`50%` + * - clang/include/clang/Tooling/Refactoring/Rename + - `6` + - `5` + - `1` + - :part:`83%` + * - clang/include/clang/Tooling/Syntax + - `5` + - `5` + - `0` + - :good:`100%` + * - clang/include/clang/Tooling/Transformer + - `7` + - `6` + - `1` + - :part:`85%` + * - clang/include/clang-c + - `9` + - `3` + - `6` + - :part:`33%` + * - clang/INPUTS + - `2` + - `0` + - `2` + - :none:`0%` + * - clang/lib/Analysis + - `25` + - `2` + - `23` + - :part:`8%` + * - clang/lib/Analysis/plugins/CheckerDependencyHandling + - `1` + - `1` + - `0` + - :good:`100%` + * - clang/lib/Analysis/plugins/CheckerOptionHandling + - `1` + - `0` + - `1` + - :none:`0%` + * - clang/lib/Analysis/plugins/SampleAnalyzer + - `1` + - `1` + - `0` + - :good:`100%` + * - clang/lib/ARCMigrate + - `22` + - `0` + - `22` + - :none:`0%` + * - clang/lib/AST + - `80` + - `2` + - `78` + - :part:`2%` + * - clang/lib/AST/Interp + - `44` + - `19` + - `25` + - :part:`43%` + * - clang/lib/ASTMatchers + - `3` + - `0` + - `3` + - :none:`0%` + * - clang/lib/ASTMatchers/Dynamic + - `6` + - `1` + - `5` + - :part:`16%` + * - clang/lib/Basic + - `32` + - `6` + - `26` + - :part:`18%` + * - clang/lib/Basic/Targets + - `46` + - `21` + - `25` + - :part:`45%` + * - clang/lib/CodeGen + - `87` + - `9` + - `78` + - :part:`10%` + * - clang/lib/CrossTU + - `1` + - `0` + - `1` + - :none:`0%` + * - clang/lib/DirectoryWatcher + - `2` + - `2` + - `0` + - :good:`100%` + * - clang/lib/DirectoryWatcher/default + - `1` + - `0` + - `1` + - :none:`0%` + * - clang/lib/DirectoryWatcher/linux + - `1` + - `0` + - `1` + - :none:`0%` + * - clang/lib/DirectoryWatcher/mac + - `1` + - `0` + - `1` + - :none:`0%` + * - clang/lib/DirectoryWatcher/windows + - `1` + - `0` + - `1` + - :none:`0%` + * - clang/lib/Driver + - `16` + - `2` + - `14` + - :part:`12%` + * - clang/lib/Driver/ToolChains + - `80` + - `24` + - `56` + - :part:`30%` + * - clang/lib/Driver/ToolChains/Arch + - `16` + - `2` + - `14` + - :part:`12%` + * - clang/lib/Edit + - `3` + - `0` + - `3` + - :none:`0%` + * - clang/lib/Format + - `29` + - `29` + - `0` + - :good:`100%` + * - clang/lib/Frontend + - `33` + - `4` + - `29` + - :part:`12%` + * - clang/lib/Frontend/Rewrite + - `8` + - `0` + - `8` + - :none:`0%` + * - clang/lib/FrontendTool + - `1` + - `0` + - `1` + - :none:`0%` + * - clang/lib/Headers + - `126` + - `10` + - `116` + - :part:`7%` + * - clang/lib/Headers/openmp_wrappers + - `3` + - `3` + - `0` + - :good:`100%` + * - clang/lib/Headers/ppc_wrappers + - `7` + - `2` + - `5` + - :part:`28%` + * - clang/lib/Index + - `12` + - `2` + - `10` + - :part:`16%` + * - clang/lib/Lex + - `23` + - `1` + - `22` + - :part:`4%` + * - clang/lib/Parse + - `15` + - `0` + - `15` + - :none:`0%` + * - clang/lib/Rewrite + - `5` + - `0` + - `5` + - :none:`0%` + * - clang/lib/Sema + - `54` + - `3` + - `51` + - :part:`5%` + * - clang/lib/Serialization + - `17` + - `1` + - `16` + - :part:`5%` + * - clang/lib/StaticAnalyzer/Checkers + - `115` + - `13` + - `102` + - :part:`11%` + * - clang/lib/StaticAnalyzer/Checkers/cert + - `1` + - `1` + - `0` + - :good:`100%` + * - clang/lib/StaticAnalyzer/Checkers/MPI-Checker + - `6` + - `0` + - `6` + - :none:`0%` + * - clang/lib/StaticAnalyzer/Checkers/RetainCountChecker + - `4` + - `0` + - `4` + - :none:`0%` + * - clang/lib/StaticAnalyzer/Checkers/UninitializedObject + - `3` + - `1` + - `2` + - :part:`33%` + * - clang/lib/StaticAnalyzer/Checkers/WebKit + - `6` + - `6` + - `0` + - :good:`100%` + * - clang/lib/StaticAnalyzer/Core + - `46` + - `8` + - `38` + - :part:`17%` + * - clang/lib/StaticAnalyzer/Frontend + - `8` + - `3` + - `5` + - :part:`37%` + * - clang/lib/Tooling + - `15` + - `6` + - `9` + - :part:`40%` + * - clang/lib/Tooling/ASTDiff + - `1` + - `0` + - `1` + - :none:`0%` + * - clang/lib/Tooling/Core + - `3` + - `1` + - `2` + - :part:`33%` + * - clang/lib/Tooling/DependencyScanning + - `5` + - `2` + - `3` + - :part:`40%` + * - clang/lib/Tooling/Inclusions + - `2` + - `2` + - `0` + - :good:`100%` + * - clang/lib/Tooling/Refactoring + - `4` + - `2` + - `2` + - :part:`50%` + * - clang/lib/Tooling/Refactoring/Extract + - `2` + - `1` + - `1` + - :part:`50%` + * - clang/lib/Tooling/Refactoring/Rename + - `5` + - `2` + - `3` + - :part:`40%` + * - clang/lib/Tooling/Syntax + - `7` + - `6` + - `1` + - :part:`85%` + * - clang/lib/Tooling/Transformer + - `6` + - `3` + - `3` + - :part:`50%` + * - clang/tools/arcmt-test + - `1` + - `0` + - `1` + - :none:`0%` + * - clang/tools/c-index-test + - `1` + - `0` + - `1` + - :none:`0%` + * - clang/tools/clang-check + - `1` + - `0` + - `1` + - :none:`0%` + * - clang/tools/clang-diff + - `1` + - `0` + - `1` + - :none:`0%` + * - clang/tools/clang-extdef-mapping + - `1` + - `0` + - `1` + - :none:`0%` + * - clang/tools/clang-format + - `1` + - `1` + - `0` + - :good:`100%` + * - clang/tools/clang-format/fuzzer + - `1` + - `0` + - `1` + - :none:`0%` + * - clang/tools/clang-fuzzer + - `6` + - `4` + - `2` + - :part:`66%` + * - clang/tools/clang-fuzzer/fuzzer-initialize + - `2` + - `0` + - `2` + - :none:`0%` + * - clang/tools/clang-fuzzer/handle-cxx + - `2` + - `0` + - `2` + - :none:`0%` + * - clang/tools/clang-fuzzer/handle-llvm + - `3` + - `1` + - `2` + - :part:`33%` + * - clang/tools/clang-fuzzer/proto-to-cxx + - `5` + - `0` + - `5` + - :none:`0%` + * - clang/tools/clang-fuzzer/proto-to-llvm + - `3` + - `0` + - `3` + - :none:`0%` + * - clang/tools/clang-import-test + - `1` + - `0` + - `1` + - :none:`0%` + * - clang/tools/clang-offload-bundler + - `1` + - `0` + - `1` + - :none:`0%` + * - clang/tools/clang-offload-wrapper + - `1` + - `1` + - `0` + - :good:`100%` + * - clang/tools/clang-refactor + - `4` + - `4` + - `0` + - :good:`100%` + * - clang/tools/clang-rename + - `1` + - `1` + - `0` + - :good:`100%` + * - clang/tools/clang-scan-deps + - `1` + - `0` + - `1` + - :none:`0%` + * - clang/tools/clang-shlib + - `1` + - `1` + - `0` + - :good:`100%` + * - clang/tools/diagtool + - `9` + - `0` + - `9` + - :none:`0%` + * - clang/tools/driver + - `4` + - `1` + - `3` + - :part:`25%` + * - clang/tools/libclang + - `34` + - `6` + - `28` + - :part:`17%` + * - clang/tools/scan-build-py/tests/functional/src/include + - `1` + - `1` + - `0` + - :good:`100%` + * - clang/unittests/Analysis + - `5` + - `2` + - `3` + - :part:`40%` + * - clang/unittests/AST + - `29` + - `7` + - `22` + - :part:`24%` + * - clang/unittests/ASTMatchers + - `6` + - `0` + - `6` + - :none:`0%` + * - clang/unittests/ASTMatchers/Dynamic + - `3` + - `0` + - `3` + - :none:`0%` + * - clang/unittests/Basic + - `5` + - `1` + - `4` + - :part:`20%` + * - clang/unittests/CodeGen + - `5` + - `0` + - `5` + - :none:`0%` + * - clang/unittests/CrossTU + - `1` + - `0` + - `1` + - :none:`0%` + * - clang/unittests/DirectoryWatcher + - `1` + - `0` + - `1` + - :none:`0%` + * - clang/unittests/Driver + - `5` + - `1` + - `4` + - :part:`20%` + * - clang/unittests/Format + - `18` + - `18` + - `0` + - :good:`100%` + * - clang/unittests/Frontend + - `8` + - `4` + - `4` + - :part:`50%` + * - clang/unittests/Index + - `1` + - `1` + - `0` + - :good:`100%` + * - clang/unittests/Lex + - `6` + - `1` + - `5` + - :part:`16%` + * - clang/unittests/libclang + - `2` + - `0` + - `2` + - :none:`0%` + * - clang/unittests/libclang/CrashTests + - `1` + - `1` + - `0` + - :good:`100%` + * - clang/unittests/Rename + - `6` + - `0` + - `6` + - :none:`0%` + * - clang/unittests/Rewrite + - `2` + - `1` + - `1` + - :part:`50%` + * - clang/unittests/Sema + - `3` + - `2` + - `1` + - :part:`66%` + * - clang/unittests/Serialization + - `1` + - `1` + - `0` + - :good:`100%` + * - clang/unittests/StaticAnalyzer + - `9` + - `4` + - `5` + - :part:`44%` + * - clang/unittests/Tooling + - `29` + - `7` + - `22` + - :part:`24%` + * - clang/unittests/Tooling/RecursiveASTVisitorTests + - `23` + - `9` + - `14` + - :part:`39%` + * - clang/unittests/Tooling/Syntax + - `2` + - `1` + - `1` + - :part:`50%` + * - clang/utils/perf-training/cxx + - `1` + - `0` + - `1` + - :none:`0%` + * - clang/utils/TableGen + - `20` + - `2` + - `18` + - :part:`10%` + * - clang-tools-extra/clang-apply-replacements/include/clang-apply-replacements/Tooling + - `1` + - `1` + - `0` + - :good:`100%` + * - clang-tools-extra/clang-apply-replacements/lib/Tooling + - `1` + - `1` + - `0` + - :good:`100%` + * - clang-tools-extra/clang-apply-replacements/tool + - `1` + - `1` + - `0` + - :good:`100%` + * - clang-tools-extra/clang-change-namespace + - `2` + - `0` + - `2` + - :none:`0%` + * - clang-tools-extra/clang-change-namespace/tool + - `1` + - `0` + - `1` + - :none:`0%` + * - clang-tools-extra/clang-doc + - `17` + - `16` + - `1` + - :part:`94%` + * - clang-tools-extra/clang-doc/tool + - `1` + - `1` + - `0` + - :good:`100%` + * - clang-tools-extra/clang-include-fixer + - `13` + - `7` + - `6` + - :part:`53%` + * - clang-tools-extra/clang-include-fixer/find-all-symbols + - `17` + - `13` + - `4` + - :part:`76%` + * - clang-tools-extra/clang-include-fixer/find-all-symbols/tool + - `1` + - `0` + - `1` + - :none:`0%` + * - clang-tools-extra/clang-include-fixer/plugin + - `1` + - `1` + - `0` + - :good:`100%` + * - clang-tools-extra/clang-include-fixer/tool + - `1` + - `0` + - `1` + - :none:`0%` + * - clang-tools-extra/clang-move + - `4` + - `1` + - `3` + - :part:`25%` + * - clang-tools-extra/clang-move/tool + - `1` + - `1` + - `0` + - :good:`100%` + * - clang-tools-extra/clang-query + - `5` + - `3` + - `2` + - :part:`60%` + * - clang-tools-extra/clang-query/tool + - `1` + - `1` + - `0` + - :good:`100%` + * - clang-tools-extra/clang-reorder-fields + - `2` + - `1` + - `1` + - :part:`50%` + * - clang-tools-extra/clang-reorder-fields/tool + - `1` + - `0` + - `1` + - :none:`0%` + * - clang-tools-extra/clang-tidy + - `18` + - `12` + - `6` + - :part:`66%` + * - clang-tools-extra/clang-tidy/abseil + - `40` + - `28` + - `12` + - :part:`70%` + * - clang-tools-extra/clang-tidy/android + - `33` + - `23` + - `10` + - :part:`69%` + * - clang-tools-extra/clang-tidy/boost + - `3` + - `3` + - `0` + - :good:`100%` + * - clang-tools-extra/clang-tidy/bugprone + - `105` + - `84` + - `21` + - :part:`80%` + * - clang-tools-extra/clang-tidy/cert + - `29` + - `27` + - `2` + - :part:`93%` + * - clang-tools-extra/clang-tidy/cppcoreguidelines + - `41` + - `38` + - `3` + - :part:`92%` + * - clang-tools-extra/clang-tidy/darwin + - `5` + - `2` + - `3` + - :part:`40%` + * - clang-tools-extra/clang-tidy/fuchsia + - `15` + - `9` + - `6` + - :part:`60%` + * - clang-tools-extra/clang-tidy/google + - `35` + - `23` + - `12` + - :part:`65%` + * - clang-tools-extra/clang-tidy/hicpp + - `9` + - `6` + - `3` + - :part:`66%` + * - clang-tools-extra/clang-tidy/linuxkernel + - `3` + - `2` + - `1` + - :part:`66%` + * - clang-tools-extra/clang-tidy/llvm + - `11` + - `10` + - `1` + - :part:`90%` + * - clang-tools-extra/clang-tidy/llvmlibc + - `7` + - `7` + - `0` + - :good:`100%` + * - clang-tools-extra/clang-tidy/misc + - `29` + - `25` + - `4` + - :part:`86%` + * - clang-tools-extra/clang-tidy/modernize + - `65` + - `43` + - `22` + - :part:`66%` + * - clang-tools-extra/clang-tidy/mpi + - `5` + - `4` + - `1` + - :part:`80%` + * - clang-tools-extra/clang-tidy/objc + - `15` + - `10` + - `5` + - :part:`66%` + * - clang-tools-extra/clang-tidy/openmp + - `5` + - `5` + - `0` + - :good:`100%` + * - clang-tools-extra/clang-tidy/performance + - `29` + - `24` + - `5` + - :part:`82%` + * - clang-tools-extra/clang-tidy/plugin + - `1` + - `1` + - `0` + - :good:`100%` + * - clang-tools-extra/clang-tidy/portability + - `5` + - `3` + - `2` + - :part:`60%` + * - clang-tools-extra/clang-tidy/readability + - `75` + - `61` + - `14` + - :part:`81%` + * - clang-tools-extra/clang-tidy/tool + - `3` + - `2` + - `1` + - :part:`66%` + * - clang-tools-extra/clang-tidy/utils + - `33` + - `26` + - `7` + - :part:`78%` + * - clang-tools-extra/clang-tidy/zircon + - `3` + - `3` + - `0` + - :good:`100%` + * - clang-tools-extra/clangd + - `73` + - `57` + - `16` + - :part:`78%` + * - clang-tools-extra/clangd/benchmarks + - `1` + - `1` + - `0` + - :good:`100%` + * - clang-tools-extra/clangd/fuzzer + - `2` + - `2` + - `0` + - :good:`100%` + * - clang-tools-extra/clangd/index + - `37` + - `34` + - `3` + - :part:`91%` + * - clang-tools-extra/clangd/index/dex + - `9` + - `8` + - `1` + - :part:`88%` + * - clang-tools-extra/clangd/index/dex/dexp + - `1` + - `1` + - `0` + - :good:`100%` + * - clang-tools-extra/clangd/index/remote + - `2` + - `2` + - `0` + - :good:`100%` + * - clang-tools-extra/clangd/index/remote/marshalling + - `2` + - `2` + - `0` + - :good:`100%` + * - clang-tools-extra/clangd/index/remote/server + - `1` + - `1` + - `0` + - :good:`100%` + * - clang-tools-extra/clangd/index/remote/unimplemented + - `1` + - `1` + - `0` + - :good:`100%` + * - clang-tools-extra/clangd/indexer + - `1` + - `1` + - `0` + - :good:`100%` + * - clang-tools-extra/clangd/refactor + - `4` + - `4` + - `0` + - :good:`100%` + * - clang-tools-extra/clangd/refactor/tweaks + - `13` + - `10` + - `3` + - :part:`76%` + * - clang-tools-extra/clangd/support + - `18` + - `18` + - `0` + - :good:`100%` + * - clang-tools-extra/clangd/tool + - `1` + - `1` + - `0` + - :good:`100%` + * - clang-tools-extra/clangd/unittests + - `63` + - `51` + - `12` + - :part:`80%` + * - clang-tools-extra/clangd/unittests/remote + - `1` + - `1` + - `0` + - :good:`100%` + * - clang-tools-extra/clangd/unittests/support + - `8` + - `8` + - `0` + - :good:`100%` + * - clang-tools-extra/clangd/unittests/xpc + - `1` + - `1` + - `0` + - :good:`100%` + * - clang-tools-extra/clangd/xpc + - `3` + - `3` + - `0` + - :good:`100%` + * - clang-tools-extra/clangd/xpc/framework + - `1` + - `1` + - `0` + - :good:`100%` + * - clang-tools-extra/clangd/xpc/test-client + - `1` + - `1` + - `0` + - :good:`100%` + * - clang-tools-extra/modularize + - `9` + - `1` + - `8` + - :part:`11%` + * - clang-tools-extra/pp-trace + - `3` + - `1` + - `2` + - :part:`33%` + * - clang-tools-extra/tool-template + - `1` + - `1` + - `0` + - :good:`100%` + * - clang-tools-extra/unittests/clang-apply-replacements + - `1` + - `1` + - `0` + - :good:`100%` + * - clang-tools-extra/unittests/clang-change-namespace + - `1` + - `0` + - `1` + - :none:`0%` + * - clang-tools-extra/unittests/clang-doc + - `9` + - `9` + - `0` + - :good:`100%` + * - clang-tools-extra/unittests/clang-include-fixer + - `2` + - `0` + - `2` + - :none:`0%` + * - clang-tools-extra/unittests/clang-include-fixer/find-all-symbols + - `1` + - `0` + - `1` + - :none:`0%` + * - clang-tools-extra/unittests/clang-move + - `1` + - `0` + - `1` + - :none:`0%` + * - clang-tools-extra/unittests/clang-query + - `2` + - `0` + - `2` + - :none:`0%` + * - clang-tools-extra/unittests/clang-tidy + - `14` + - `6` + - `8` + - :part:`42%` + * - clang-tools-extra/unittests/include/common + - `1` + - `0` + - `1` + - :none:`0%` + * - compiler-rt/include/fuzzer + - `1` + - `0` + - `1` + - :none:`0%` + * - compiler-rt/include/sanitizer + - `14` + - `1` + - `13` + - :part:`7%` + * - compiler-rt/include/xray + - `3` + - `2` + - `1` + - :part:`66%` + * - compiler-rt/lib/asan + - `59` + - `3` + - `56` + - :part:`5%` + * - compiler-rt/lib/asan/tests + - `17` + - `1` + - `16` + - :part:`5%` + * - compiler-rt/lib/BlocksRuntime + - `2` + - `0` + - `2` + - :none:`0%` + * - compiler-rt/lib/builtins + - `11` + - `9` + - `2` + - :part:`81%` + * - compiler-rt/lib/builtins/arm + - `1` + - `0` + - `1` + - :none:`0%` + * - compiler-rt/lib/builtins/ppc + - `1` + - `1` + - `0` + - :good:`100%` + * - compiler-rt/lib/cfi + - `1` + - `0` + - `1` + - :none:`0%` + * - compiler-rt/lib/dfsan + - `5` + - `0` + - `5` + - :none:`0%` + * - compiler-rt/lib/fuzzer + - `43` + - `5` + - `38` + - :part:`11%` + * - compiler-rt/lib/fuzzer/afl + - `1` + - `0` + - `1` + - :none:`0%` + * - compiler-rt/lib/fuzzer/dataflow + - `3` + - `0` + - `3` + - :none:`0%` + * - compiler-rt/lib/fuzzer/tests + - `2` + - `1` + - `1` + - :part:`50%` + * - compiler-rt/lib/gwp_asan + - `14` + - `13` + - `1` + - :part:`92%` + * - compiler-rt/lib/gwp_asan/optional + - `7` + - `7` + - `0` + - :good:`100%` + * - compiler-rt/lib/gwp_asan/platform_specific + - `4` + - `4` + - `0` + - :good:`100%` + * - compiler-rt/lib/gwp_asan/tests + - `14` + - `14` + - `0` + - :good:`100%` + * - compiler-rt/lib/gwp_asan/tests/optional + - `1` + - `1` + - `0` + - :good:`100%` + * - compiler-rt/lib/hwasan + - `25` + - `5` + - `20` + - :part:`20%` + * - compiler-rt/lib/interception + - `8` + - `1` + - `7` + - :part:`12%` + * - compiler-rt/lib/interception/tests + - `3` + - `1` + - `2` + - :part:`33%` + * - compiler-rt/lib/lsan + - `20` + - `7` + - `13` + - :part:`35%` + * - compiler-rt/lib/msan + - `18` + - `4` + - `14` + - :part:`22%` + * - compiler-rt/lib/msan/tests + - `4` + - `0` + - `4` + - :none:`0%` + * - compiler-rt/lib/profile + - `6` + - `0` + - `6` + - :none:`0%` + * - compiler-rt/lib/safestack + - `3` + - `1` + - `2` + - :part:`33%` + * - compiler-rt/lib/sanitizer_common + - `159` + - `23` + - `136` + - :part:`14%` + * - compiler-rt/lib/sanitizer_common/symbolizer + - `2` + - `1` + - `1` + - :part:`50%` + * - compiler-rt/lib/sanitizer_common/tests + - `37` + - `1` + - `36` + - :part:`2%` + * - compiler-rt/lib/scudo + - `20` + - `0` + - `20` + - :none:`0%` + * - compiler-rt/lib/scudo/standalone + - `46` + - `42` + - `4` + - :part:`91%` + * - compiler-rt/lib/scudo/standalone/benchmarks + - `1` + - `1` + - `0` + - :good:`100%` + * - compiler-rt/lib/scudo/standalone/fuzz + - `1` + - `0` + - `1` + - :none:`0%` + * - compiler-rt/lib/scudo/standalone/include/scudo + - `1` + - `1` + - `0` + - :good:`100%` + * - compiler-rt/lib/scudo/standalone/tests + - `23` + - `23` + - `0` + - :good:`100%` + * - compiler-rt/lib/scudo/standalone/tools + - `1` + - `0` + - `1` + - :none:`0%` + * - compiler-rt/lib/stats + - `3` + - `0` + - `3` + - :none:`0%` + * - compiler-rt/lib/tsan/benchmarks + - `6` + - `0` + - `6` + - :none:`0%` + * - compiler-rt/lib/tsan/dd + - `3` + - `0` + - `3` + - :none:`0%` + * - compiler-rt/lib/tsan/go + - `1` + - `0` + - `1` + - :none:`0%` + * - compiler-rt/lib/tsan/rtl + - `62` + - `10` + - `52` + - :part:`16%` + * - compiler-rt/lib/tsan/tests/rtl + - `10` + - `1` + - `9` + - :part:`10%` + * - compiler-rt/lib/tsan/tests/unit + - `10` + - `0` + - `10` + - :none:`0%` + * - compiler-rt/lib/ubsan + - `27` + - `7` + - `20` + - :part:`25%` + * - compiler-rt/lib/ubsan_minimal + - `1` + - `0` + - `1` + - :none:`0%` + * - compiler-rt/lib/xray + - `39` + - `30` + - `9` + - :part:`76%` + * - compiler-rt/lib/xray/tests/unit + - `10` + - `8` + - `2` + - :part:`80%` + * - compiler-rt/tools/gwp_asan + - `1` + - `1` + - `0` + - :good:`100%` + * - debuginfo-tests/dexter/feature_tests/commands/penalty + - `6` + - `0` + - `6` + - :none:`0%` + * - debuginfo-tests/dexter/feature_tests/commands/perfect + - `5` + - `0` + - `5` + - :none:`0%` + * - debuginfo-tests/dexter/feature_tests/commands/perfect/expect_step_kind + - `5` + - `0` + - `5` + - :none:`0%` + * - debuginfo-tests/dexter/feature_tests/subtools + - `1` + - `0` + - `1` + - :none:`0%` + * - debuginfo-tests/dexter/feature_tests/subtools/clang-opt-bisect + - `1` + - `0` + - `1` + - :none:`0%` + * - debuginfo-tests/dexter-tests + - `8` + - `3` + - `5` + - :part:`37%` + * - debuginfo-tests/llgdb-tests + - `7` + - `0` + - `7` + - :none:`0%` + * - debuginfo-tests/llvm-prettyprinters/gdb + - `1` + - `1` + - `0` + - :good:`100%` + * - flang/include/flang + - `1` + - `1` + - `0` + - :good:`100%` + * - flang/include/flang/Common + - `19` + - `19` + - `0` + - :good:`100%` + * - flang/include/flang/Decimal + - `2` + - `2` + - `0` + - :good:`100%` + * - flang/include/flang/Evaluate + - `21` + - `21` + - `0` + - :good:`100%` + * - flang/include/flang/Lower + - `1` + - `1` + - `0` + - :good:`100%` + * - flang/include/flang/Optimizer/CodeGen + - `1` + - `1` + - `0` + - :good:`100%` + * - flang/include/flang/Optimizer/Dialect + - `5` + - `5` + - `0` + - :good:`100%` + * - flang/include/flang/Optimizer/Support + - `2` + - `2` + - `0` + - :good:`100%` + * - flang/include/flang/Parser + - `17` + - `17` + - `0` + - :good:`100%` + * - flang/include/flang/Semantics + - `8` + - `8` + - `0` + - :good:`100%` + * - flang/lib/Common + - `4` + - `4` + - `0` + - :good:`100%` + * - flang/lib/Decimal + - `3` + - `3` + - `0` + - :good:`100%` + * - flang/lib/Evaluate + - `30` + - `30` + - `0` + - :good:`100%` + * - flang/lib/Lower + - `1` + - `1` + - `0` + - :good:`100%` + * - flang/lib/Optimizer/Dialect + - `4` + - `4` + - `0` + - :good:`100%` + * - flang/lib/Optimizer/Support + - `2` + - `2` + - `0` + - :good:`100%` + * - flang/lib/Parser + - `34` + - `34` + - `0` + - :good:`100%` + * - flang/lib/Semantics + - `66` + - `65` + - `1` + - :part:`98%` + * - flang/module + - `1` + - `0` + - `1` + - :none:`0%` + * - flang/runtime + - `56` + - `56` + - `0` + - :good:`100%` + * - flang/tools/f18 + - `2` + - `2` + - `0` + - :good:`100%` + * - flang/tools/f18-parse-demo + - `2` + - `2` + - `0` + - :good:`100%` + * - flang/tools/tco + - `1` + - `1` + - `0` + - :good:`100%` + * - flang/unittests/Decimal + - `2` + - `2` + - `0` + - :good:`100%` + * - flang/unittests/Evaluate + - `15` + - `15` + - `0` + - :good:`100%` + * - flang/unittests/Runtime + - `7` + - `7` + - `0` + - :good:`100%` + * - libc/AOR_v20.02/math + - `4` + - `1` + - `3` + - :part:`25%` + * - libc/AOR_v20.02/math/include + - `1` + - `0` + - `1` + - :none:`0%` + * - libc/AOR_v20.02/networking + - `1` + - `0` + - `1` + - :none:`0%` + * - libc/AOR_v20.02/networking/include + - `1` + - `0` + - `1` + - :none:`0%` + * - libc/AOR_v20.02/string + - `1` + - `0` + - `1` + - :none:`0%` + * - libc/AOR_v20.02/string/include + - `1` + - `0` + - `1` + - :none:`0%` + * - libc/fuzzing/string + - `1` + - `0` + - `1` + - :none:`0%` + * - libc/include + - `4` + - `4` + - `0` + - :good:`100%` + * - libc/loader/linux/x86_64 + - `1` + - `1` + - `0` + - :good:`100%` + * - libc/src/assert + - `2` + - `0` + - `2` + - :none:`0%` + * - libc/src/errno + - `2` + - `2` + - `0` + - :good:`100%` + * - libc/src/math + - `23` + - `21` + - `2` + - :part:`91%` + * - libc/src/signal + - `8` + - `8` + - `0` + - :good:`100%` + * - libc/src/signal/linux + - `10` + - `10` + - `0` + - :good:`100%` + * - libc/src/stdio + - `3` + - `3` + - `0` + - :good:`100%` + * - libc/src/stdlib + - `3` + - `3` + - `0` + - :good:`100%` + * - libc/src/stdlib/linux + - `1` + - `1` + - `0` + - :good:`100%` + * - libc/src/string + - `11` + - `10` + - `1` + - :part:`90%` + * - libc/src/string/memory_utils + - `3` + - `3` + - `0` + - :good:`100%` + * - libc/src/string/x86 + - `1` + - `1` + - `0` + - :good:`100%` + * - libc/src/sys/mman + - `2` + - `2` + - `0` + - :good:`100%` + * - libc/src/sys/mman/linux + - `2` + - `2` + - `0` + - :good:`100%` + * - libc/src/threads + - `6` + - `6` + - `0` + - :good:`100%` + * - libc/src/threads/linux + - `7` + - `7` + - `0` + - :good:`100%` + * - libc/src/unistd + - `1` + - `1` + - `0` + - :good:`100%` + * - libc/src/unistd/linux + - `1` + - `1` + - `0` + - :good:`100%` + * - libc/utils/benchmarks + - `14` + - `14` + - `0` + - :good:`100%` + * - libc/utils/CPP + - `5` + - `5` + - `0` + - :good:`100%` + * - libc/utils/FPUtil + - `3` + - `3` + - `0` + - :good:`100%` + * - libc/utils/HdrGen + - `9` + - `9` + - `0` + - :good:`100%` + * - libc/utils/MPFRWrapper + - `3` + - `2` + - `1` + - :part:`66%` + * - libc/utils/testutils + - `6` + - `6` + - `0` + - :good:`100%` + * - libc/utils/UnitTest + - `3` + - `3` + - `0` + - :good:`100%` + * - libclc/generic/include + - `2` + - `1` + - `1` + - :part:`50%` + * - libclc/generic/include/clc + - `6` + - `2` + - `4` + - :part:`33%` + * - libclc/generic/include/clc/async + - `4` + - `4` + - `0` + - :good:`100%` + * - libclc/generic/include/clc/atomic + - `11` + - `7` + - `4` + - :part:`63%` + * - libclc/generic/include/clc/cl_khr_global_int32_base_atomics + - `6` + - `5` + - `1` + - :part:`83%` + * - libclc/generic/include/clc/cl_khr_global_int32_extended_atomics + - `5` + - `5` + - `0` + - :good:`100%` + * - libclc/generic/include/clc/cl_khr_int64_base_atomics + - `6` + - `3` + - `3` + - :part:`50%` + * - libclc/generic/include/clc/cl_khr_int64_extended_atomics + - `5` + - `5` + - `0` + - :good:`100%` + * - libclc/generic/include/clc/cl_khr_local_int32_base_atomics + - `6` + - `5` + - `1` + - :part:`83%` + * - libclc/generic/include/clc/cl_khr_local_int32_extended_atomics + - `5` + - `5` + - `0` + - :good:`100%` + * - libclc/generic/include/clc/common + - `6` + - `6` + - `0` + - :good:`100%` + * - libclc/generic/include/clc/explicit_fence + - `1` + - `1` + - `0` + - :good:`100%` + * - libclc/generic/include/clc/float + - `1` + - `0` + - `1` + - :none:`0%` + * - libclc/generic/include/clc/geometric + - `8` + - `8` + - `0` + - :good:`100%` + * - libclc/generic/include/clc/image + - `2` + - `0` + - `2` + - :none:`0%` + * - libclc/generic/include/clc/integer + - `16` + - `13` + - `3` + - :part:`81%` + * - libclc/generic/include/clc/math + - `95` + - `92` + - `3` + - :part:`96%` + * - libclc/generic/include/clc/misc + - `2` + - `0` + - `2` + - :none:`0%` + * - libclc/generic/include/clc/relational + - `18` + - `12` + - `6` + - :part:`66%` + * - libclc/generic/include/clc/shared + - `5` + - `3` + - `2` + - :part:`60%` + * - libclc/generic/include/clc/synchronization + - `2` + - `2` + - `0` + - :good:`100%` + * - libclc/generic/include/clc/workitem + - `8` + - `8` + - `0` + - :good:`100%` + * - libclc/generic/include/integer + - `1` + - `1` + - `0` + - :good:`100%` + * - libclc/generic/include/math + - `15` + - `15` + - `0` + - :good:`100%` + * - libclc/generic/lib + - `1` + - `0` + - `1` + - :none:`0%` + * - libclc/generic/lib/math + - `8` + - `1` + - `7` + - :part:`12%` + * - libclc/generic/lib/relational + - `1` + - `0` + - `1` + - :none:`0%` + * - libclc/utils + - `1` + - `0` + - `1` + - :none:`0%` + * - libcxx/benchmarks + - `16` + - `1` + - `15` + - :part:`6%` + * - libcxx/fuzzing + - `4` + - `0` + - `4` + - :none:`0%` + * - libcxx/include + - `21` + - `0` + - `21` + - :none:`0%` + * - libcxx/include/support/android + - `1` + - `0` + - `1` + - :none:`0%` + * - libcxx/include/support/fuchsia + - `1` + - `1` + - `0` + - :good:`100%` + * - libcxx/include/support/ibm + - `4` + - `1` + - `3` + - :part:`25%` + * - libcxx/include/support/musl + - `1` + - `0` + - `1` + - :none:`0%` + * - libcxx/include/support/newlib + - `1` + - `0` + - `1` + - :none:`0%` + * - libcxx/include/support/solaris + - `3` + - `2` + - `1` + - :part:`66%` + * - libcxx/include/support/win32 + - `2` + - `0` + - `2` + - :none:`0%` + * - libcxx/include/support/xlocale + - `3` + - `0` + - `3` + - :none:`0%` + * - libcxx/src + - `35` + - `1` + - `34` + - :part:`2%` + * - libcxx/src/experimental + - `1` + - `0` + - `1` + - :none:`0%` + * - libcxx/src/filesystem + - `4` + - `2` + - `2` + - :part:`50%` + * - libcxx/src/include + - `4` + - `2` + - `2` + - :part:`50%` + * - libcxx/src/support/solaris + - `1` + - `0` + - `1` + - :none:`0%` + * - libcxx/src/support/win32 + - `3` + - `0` + - `3` + - :none:`0%` + * - libcxx/utils/google-benchmark/cmake + - `5` + - `1` + - `4` + - :part:`20%` + * - libcxx/utils/google-benchmark/include/benchmark + - `1` + - `0` + - `1` + - :none:`0%` + * - libcxx/utils/google-benchmark/src + - `20` + - `16` + - `4` + - :part:`80%` + * - libcxxabi/fuzz + - `1` + - `0` + - `1` + - :none:`0%` + * - libcxxabi/include + - `2` + - `0` + - `2` + - :none:`0%` + * - libcxxabi/src + - `26` + - `1` + - `25` + - :part:`3%` + * - libcxxabi/src/demangle + - `4` + - `2` + - `2` + - :part:`50%` + * - libcxxabi/src/include + - `2` + - `0` + - `2` + - :none:`0%` + * - libunwind/include + - `3` + - `0` + - `3` + - :none:`0%` + * - libunwind/include/mach-o + - `1` + - `0` + - `1` + - :none:`0%` + * - libunwind/src + - `9` + - `0` + - `9` + - :none:`0%` + * - lld/COFF + - `33` + - `10` + - `23` + - :part:`30%` + * - lld/Common + - `10` + - `9` + - `1` + - :part:`90%` + * - lld/ELF + - `48` + - `26` + - `22` + - :part:`54%` + * - lld/ELF/Arch + - `14` + - `7` + - `7` + - :part:`50%` + * - lld/include/lld/Common + - `12` + - `6` + - `6` + - :part:`50%` + * - lld/include/lld/Core + - `20` + - `4` + - `16` + - :part:`20%` + * - lld/include/lld/ReaderWriter + - `2` + - `0` + - `2` + - :none:`0%` + * - lld/lib/Core + - `8` + - `2` + - `6` + - :part:`25%` + * - lld/lib/Driver + - `1` + - `0` + - `1` + - :none:`0%` + * - lld/lib/ReaderWriter + - `1` + - `0` + - `1` + - :none:`0%` + * - lld/lib/ReaderWriter/MachO + - `30` + - `1` + - `29` + - :part:`3%` + * - lld/lib/ReaderWriter/YAML + - `1` + - `0` + - `1` + - :none:`0%` + * - lld/MachO + - `25` + - `25` + - `0` + - :good:`100%` + * - lld/MachO/Arch + - `1` + - `1` + - `0` + - :good:`100%` + * - lld/MinGW + - `1` + - `1` + - `0` + - :good:`100%` + * - lld/tools/lld + - `1` + - `1` + - `0` + - :good:`100%` + * - lld/unittests/DriverTests + - `1` + - `0` + - `1` + - :none:`0%` + * - lld/unittests/MachOTests + - `4` + - `0` + - `4` + - :none:`0%` + * - lld/wasm + - `27` + - `14` + - `13` + - :part:`51%` + * - lldb/examples/darwin/heap_find/heap + - `1` + - `1` + - `0` + - :good:`100%` + * - lldb/examples/functions + - `1` + - `0` + - `1` + - :none:`0%` + * - lldb/examples/interposing/darwin/fd_interposing + - `1` + - `0` + - `1` + - :none:`0%` + * - lldb/examples/lookup + - `1` + - `0` + - `1` + - :none:`0%` + * - lldb/examples/plugins/commands + - `1` + - `1` + - `0` + - :good:`100%` + * - lldb/examples/synthetic/bitfield + - `1` + - `1` + - `0` + - :good:`100%` + * - lldb/include/lldb + - `12` + - `7` + - `5` + - :part:`58%` + * - lldb/include/lldb/API + - `71` + - `59` + - `12` + - :part:`83%` + * - lldb/include/lldb/Breakpoint + - `24` + - `10` + - `14` + - :part:`41%` + * - lldb/include/lldb/Core + - `57` + - `31` + - `26` + - :part:`54%` + * - lldb/include/lldb/DataFormatters + - `18` + - `9` + - `9` + - :part:`50%` + * - lldb/include/lldb/Expression + - `17` + - `6` + - `11` + - :part:`35%` + * - lldb/include/lldb/Host + - `40` + - `20` + - `20` + - :part:`50%` + * - lldb/include/lldb/Host/android + - `1` + - `1` + - `0` + - :good:`100%` + * - lldb/include/lldb/Host/common + - `8` + - `2` + - `6` + - :part:`25%` + * - lldb/include/lldb/Host/freebsd + - `1` + - `0` + - `1` + - :none:`0%` + * - lldb/include/lldb/Host/linux + - `5` + - `3` + - `2` + - :part:`60%` + * - lldb/include/lldb/Host/macosx + - `2` + - `0` + - `2` + - :none:`0%` + * - lldb/include/lldb/Host/netbsd + - `1` + - `0` + - `1` + - :none:`0%` + * - lldb/include/lldb/Host/openbsd + - `1` + - `0` + - `1` + - :none:`0%` + * - lldb/include/lldb/Host/posix + - `9` + - `7` + - `2` + - :part:`77%` + * - lldb/include/lldb/Host/windows + - `11` + - `5` + - `6` + - :part:`45%` + * - lldb/include/lldb/Initialization + - `3` + - `1` + - `2` + - :part:`33%` + * - lldb/include/lldb/Interpreter + - `47` + - `38` + - `9` + - :part:`80%` + * - lldb/include/lldb/Symbol + - `36` + - `16` + - `20` + - :part:`44%` + * - lldb/include/lldb/Target + - `66` + - `37` + - `29` + - :part:`56%` + * - lldb/include/lldb/Utility + - `58` + - `36` + - `22` + - :part:`62%` + * - lldb/source + - `1` + - `1` + - `0` + - :good:`100%` + * - lldb/source/API + - `75` + - `8` + - `67` + - :part:`10%` + * - lldb/source/Breakpoint + - `24` + - `6` + - `18` + - :part:`25%` + * - lldb/source/Commands + - `56` + - `48` + - `8` + - :part:`85%` + * - lldb/source/Core + - `45` + - `24` + - `21` + - :part:`53%` + * - lldb/source/DataFormatters + - `16` + - `2` + - `14` + - :part:`12%` + * - lldb/source/Expression + - `13` + - `4` + - `9` + - :part:`30%` + * - lldb/source/Host/android + - `2` + - `2` + - `0` + - :good:`100%` + * - lldb/source/Host/common + - `32` + - `17` + - `15` + - :part:`53%` + * - lldb/source/Host/freebsd + - `2` + - `2` + - `0` + - :good:`100%` + * - lldb/source/Host/linux + - `5` + - `3` + - `2` + - :part:`60%` + * - lldb/source/Host/macosx/cfcpp + - `14` + - `12` + - `2` + - :part:`85%` + * - lldb/source/Host/netbsd + - `2` + - `0` + - `2` + - :none:`0%` + * - lldb/source/Host/openbsd + - `2` + - `1` + - `1` + - :part:`50%` + * - lldb/source/Host/posix + - `9` + - `5` + - `4` + - :part:`55%` + * - lldb/source/Host/windows + - `12` + - `5` + - `7` + - :part:`41%` + * - lldb/source/Initialization + - `3` + - `3` + - `0` + - :good:`100%` + * - lldb/source/Interpreter + - `46` + - `25` + - `21` + - :part:`54%` + * - lldb/source/Plugins/ABI/AArch64 + - `6` + - `2` + - `4` + - :part:`33%` + * - lldb/source/Plugins/ABI/ARC + - `2` + - `0` + - `2` + - :none:`0%` + * - lldb/source/Plugins/ABI/ARM + - `6` + - `4` + - `2` + - :part:`66%` + * - lldb/source/Plugins/ABI/Hexagon + - `2` + - `0` + - `2` + - :none:`0%` + * - lldb/source/Plugins/ABI/Mips + - `6` + - `2` + - `4` + - :part:`33%` + * - lldb/source/Plugins/ABI/PowerPC + - `6` + - `3` + - `3` + - :part:`50%` + * - lldb/source/Plugins/ABI/SystemZ + - `2` + - `0` + - `2` + - :none:`0%` + * - lldb/source/Plugins/ABI/X86 + - `11` + - `4` + - `7` + - :part:`36%` + * - lldb/source/Plugins/Architecture/Arm + - `2` + - `1` + - `1` + - :part:`50%` + * - lldb/source/Plugins/Architecture/Mips + - `2` + - `0` + - `2` + - :none:`0%` + * - lldb/source/Plugins/Architecture/PPC64 + - `2` + - `2` + - `0` + - :good:`100%` + * - lldb/source/Plugins/Disassembler/LLVMC + - `2` + - `1` + - `1` + - :part:`50%` + * - lldb/source/Plugins/DynamicLoader/Darwin-Kernel + - `2` + - `0` + - `2` + - :none:`0%` + * - lldb/source/Plugins/DynamicLoader/Hexagon-DYLD + - `4` + - `4` + - `0` + - :good:`100%` + * - lldb/source/Plugins/DynamicLoader/MacOSX-DYLD + - `6` + - `3` + - `3` + - :part:`50%` + * - lldb/source/Plugins/DynamicLoader/POSIX-DYLD + - `4` + - `2` + - `2` + - :part:`50%` + * - lldb/source/Plugins/DynamicLoader/Static + - `2` + - `1` + - `1` + - :part:`50%` + * - lldb/source/Plugins/DynamicLoader/wasm-DYLD + - `2` + - `2` + - `0` + - :good:`100%` + * - lldb/source/Plugins/DynamicLoader/Windows-DYLD + - `2` + - `1` + - `1` + - :part:`50%` + * - lldb/source/Plugins/ExpressionParser/Clang + - `51` + - `26` + - `25` + - :part:`50%` + * - lldb/source/Plugins/Instruction/ARM + - `4` + - `2` + - `2` + - :part:`50%` + * - lldb/source/Plugins/Instruction/ARM64 + - `2` + - `2` + - `0` + - :good:`100%` + * - lldb/source/Plugins/Instruction/MIPS + - `2` + - `0` + - `2` + - :none:`0%` + * - lldb/source/Plugins/Instruction/MIPS64 + - `2` + - `1` + - `1` + - :part:`50%` + * - lldb/source/Plugins/Instruction/PPC64 + - `2` + - `2` + - `0` + - :good:`100%` + * - lldb/source/Plugins/InstrumentationRuntime/ASan + - `2` + - `2` + - `0` + - :good:`100%` + * - lldb/source/Plugins/InstrumentationRuntime/MainThreadChecker + - `2` + - `2` + - `0` + - :good:`100%` + * - lldb/source/Plugins/InstrumentationRuntime/TSan + - `2` + - `2` + - `0` + - :good:`100%` + * - lldb/source/Plugins/InstrumentationRuntime/UBSan + - `2` + - `2` + - `0` + - :good:`100%` + * - lldb/source/Plugins/JITLoader/GDB + - `2` + - `1` + - `1` + - :part:`50%` + * - lldb/source/Plugins/Language/ClangCommon + - `2` + - `2` + - `0` + - :good:`100%` + * - lldb/source/Plugins/Language/CPlusPlus + - `29` + - `17` + - `12` + - :part:`58%` + * - lldb/source/Plugins/Language/ObjC + - `20` + - `13` + - `7` + - :part:`65%` + * - lldb/source/Plugins/Language/ObjCPlusPlus + - `2` + - `2` + - `0` + - :good:`100%` + * - lldb/source/Plugins/LanguageRuntime/CPlusPlus + - `2` + - `0` + - `2` + - :none:`0%` + * - lldb/source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABI + - `2` + - `0` + - `2` + - :none:`0%` + * - lldb/source/Plugins/LanguageRuntime/ObjC + - `2` + - `1` + - `1` + - :part:`50%` + * - lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime + - `16` + - `4` + - `12` + - :part:`25%` + * - lldb/source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime + - `8` + - `3` + - `5` + - :part:`37%` + * - lldb/source/Plugins/MemoryHistory/asan + - `2` + - `2` + - `0` + - :good:`100%` + * - lldb/source/Plugins/ObjectContainer/BSD-Archive + - `2` + - `0` + - `2` + - :none:`0%` + * - lldb/source/Plugins/ObjectContainer/Universal-Mach-O + - `2` + - `2` + - `0` + - :good:`100%` + * - lldb/source/Plugins/ObjectFile/Breakpad + - `4` + - `3` + - `1` + - :part:`75%` + * - lldb/source/Plugins/ObjectFile/ELF + - `4` + - `1` + - `3` + - :part:`25%` + * - lldb/source/Plugins/ObjectFile/JIT + - `2` + - `0` + - `2` + - :none:`0%` + * - lldb/source/Plugins/ObjectFile/Mach-O + - `2` + - `0` + - `2` + - :none:`0%` + * - lldb/source/Plugins/ObjectFile/PECOFF + - `6` + - `3` + - `3` + - :part:`50%` + * - lldb/source/Plugins/ObjectFile/wasm + - `2` + - `2` + - `0` + - :good:`100%` + * - lldb/source/Plugins/OperatingSystem/Python + - `2` + - `2` + - `0` + - :good:`100%` + * - lldb/source/Plugins/Platform/Android + - `6` + - `3` + - `3` + - :part:`50%` + * - lldb/source/Plugins/Platform/FreeBSD + - `2` + - `1` + - `1` + - :part:`50%` + * - lldb/source/Plugins/Platform/gdb-server + - `2` + - `2` + - `0` + - :good:`100%` + * - lldb/source/Plugins/Platform/Linux + - `2` + - `1` + - `1` + - :part:`50%` + * - lldb/source/Plugins/Platform/MacOSX + - `24` + - `8` + - `16` + - :part:`33%` + * - lldb/source/Plugins/Platform/MacOSX/objcxx + - `1` + - `1` + - `0` + - :good:`100%` + * - lldb/source/Plugins/Platform/NetBSD + - `2` + - `1` + - `1` + - :part:`50%` + * - lldb/source/Plugins/Platform/OpenBSD + - `2` + - `1` + - `1` + - :part:`50%` + * - lldb/source/Plugins/Platform/POSIX + - `2` + - `0` + - `2` + - :none:`0%` + * - lldb/source/Plugins/Platform/Windows + - `2` + - `2` + - `0` + - :good:`100%` + * - lldb/source/Plugins/Process/elf-core + - `20` + - `18` + - `2` + - :part:`90%` + * - lldb/source/Plugins/Process/FreeBSD + - `19` + - `11` + - `8` + - :part:`57%` + * - lldb/source/Plugins/Process/gdb-remote + - `26` + - `16` + - `10` + - :part:`61%` + * - lldb/source/Plugins/Process/Linux + - `24` + - `12` + - `12` + - :part:`50%` + * - lldb/source/Plugins/Process/mach-core + - `4` + - `3` + - `1` + - :part:`75%` + * - lldb/source/Plugins/Process/MacOSX-Kernel + - `16` + - `13` + - `3` + - :part:`81%` + * - lldb/source/Plugins/Process/minidump + - `17` + - `10` + - `7` + - :part:`58%` + * - lldb/source/Plugins/Process/NetBSD + - `8` + - `3` + - `5` + - :part:`37%` + * - lldb/source/Plugins/Process/POSIX + - `8` + - `5` + - `3` + - :part:`62%` + * - lldb/source/Plugins/Process/Utility + - `127` + - `87` + - `40` + - :part:`68%` + * - lldb/source/Plugins/Process/Windows/Common + - `34` + - `23` + - `11` + - :part:`67%` + * - lldb/source/Plugins/Process/Windows/Common/arm + - `2` + - `1` + - `1` + - :part:`50%` + * - lldb/source/Plugins/Process/Windows/Common/arm64 + - `2` + - `1` + - `1` + - :part:`50%` + * - lldb/source/Plugins/Process/Windows/Common/x64 + - `2` + - `0` + - `2` + - :none:`0%` + * - lldb/source/Plugins/Process/Windows/Common/x86 + - `2` + - `0` + - `2` + - :none:`0%` + * - lldb/source/Plugins/ScriptInterpreter/Lua + - `4` + - `4` + - `0` + - :good:`100%` + * - lldb/source/Plugins/ScriptInterpreter/None + - `2` + - `2` + - `0` + - :good:`100%` + * - lldb/source/Plugins/ScriptInterpreter/Python + - `8` + - `3` + - `5` + - :part:`37%` + * - lldb/source/Plugins/StructuredData/DarwinLog + - `2` + - `0` + - `2` + - :none:`0%` + * - lldb/source/Plugins/SymbolFile/Breakpad + - `2` + - `0` + - `2` + - :none:`0%` + * - lldb/source/Plugins/SymbolFile/DWARF + - `65` + - `35` + - `30` + - :part:`53%` + * - lldb/source/Plugins/SymbolFile/NativePDB + - `20` + - `12` + - `8` + - :part:`60%` + * - lldb/source/Plugins/SymbolFile/PDB + - `6` + - `4` + - `2` + - :part:`66%` + * - lldb/source/Plugins/SymbolFile/Symtab + - `2` + - `2` + - `0` + - :good:`100%` + * - lldb/source/Plugins/SymbolVendor/ELF + - `2` + - `2` + - `0` + - :good:`100%` + * - lldb/source/Plugins/SymbolVendor/MacOSX + - `2` + - `2` + - `0` + - :good:`100%` + * - lldb/source/Plugins/SymbolVendor/wasm + - `2` + - `2` + - `0` + - :good:`100%` + * - lldb/source/Plugins/SystemRuntime/MacOSX + - `10` + - `1` + - `9` + - :part:`10%` + * - lldb/source/Plugins/TypeSystem/Clang + - `2` + - `0` + - `2` + - :none:`0%` + * - lldb/source/Plugins/UnwindAssembly/InstEmulation + - `2` + - `1` + - `1` + - :part:`50%` + * - lldb/source/Plugins/UnwindAssembly/x86 + - `4` + - `2` + - `2` + - :part:`50%` + * - lldb/source/Symbol + - `32` + - `18` + - `14` + - :part:`56%` + * - lldb/source/Target + - `61` + - `28` + - `33` + - :part:`45%` + * - lldb/source/Utility + - `54` + - `41` + - `13` + - :part:`75%` + * - lldb/tools/argdumper + - `1` + - `1` + - `0` + - :good:`100%` + * - lldb/tools/darwin-debug + - `1` + - `1` + - `0` + - :good:`100%` + * - lldb/tools/debugserver/source + - `49` + - `38` + - `11` + - :part:`77%` + * - lldb/tools/debugserver/source/MacOSX + - `24` + - `16` + - `8` + - :part:`66%` + * - lldb/tools/debugserver/source/MacOSX/arm + - `2` + - `1` + - `1` + - :part:`50%` + * - lldb/tools/debugserver/source/MacOSX/arm64 + - `2` + - `1` + - `1` + - :part:`50%` + * - lldb/tools/debugserver/source/MacOSX/DarwinLog + - `20` + - `18` + - `2` + - :part:`90%` + * - lldb/tools/debugserver/source/MacOSX/i386 + - `3` + - `1` + - `2` + - :part:`33%` + * - lldb/tools/debugserver/source/MacOSX/x86_64 + - `3` + - `1` + - `2` + - :part:`33%` + * - lldb/tools/driver + - `4` + - `4` + - `0` + - :good:`100%` + * - lldb/tools/intel-features + - `1` + - `1` + - `0` + - :good:`100%` + * - lldb/tools/intel-features/intel-mpx + - `2` + - `2` + - `0` + - :good:`100%` + * - lldb/tools/intel-features/intel-pt + - `6` + - `6` + - `0` + - :good:`100%` + * - lldb/tools/lldb-instr + - `1` + - `1` + - `0` + - :good:`100%` + * - lldb/tools/lldb-server + - `9` + - `4` + - `5` + - :part:`44%` + * - lldb/tools/lldb-test + - `5` + - `3` + - `2` + - :part:`60%` + * - lldb/tools/lldb-vscode + - `19` + - `12` + - `7` + - :part:`63%` + * - lldb/unittests + - `1` + - `1` + - `0` + - :good:`100%` + * - lldb/unittests/API + - `1` + - `1` + - `0` + - :good:`100%` + * - lldb/unittests/Breakpoint + - `1` + - `1` + - `0` + - :good:`100%` + * - lldb/unittests/Core + - `6` + - `5` + - `1` + - :part:`83%` + * - lldb/unittests/DataFormatter + - `2` + - `2` + - `0` + - :good:`100%` + * - lldb/unittests/debugserver + - `3` + - `2` + - `1` + - :part:`66%` + * - lldb/unittests/Disassembler + - `2` + - `0` + - `2` + - :none:`0%` + * - lldb/unittests/Editline + - `1` + - `1` + - `0` + - :good:`100%` + * - lldb/unittests/Expression + - `5` + - `3` + - `2` + - :part:`60%` + * - lldb/unittests/Host + - `13` + - `10` + - `3` + - :part:`76%` + * - lldb/unittests/Host/linux + - `2` + - `2` + - `0` + - :good:`100%` + * - lldb/unittests/Interpreter + - `2` + - `0` + - `2` + - :none:`0%` + * - lldb/unittests/Language/CPlusPlus + - `1` + - `0` + - `1` + - :none:`0%` + * - lldb/unittests/Language/Highlighting + - `1` + - `1` + - `0` + - :good:`100%` + * - lldb/unittests/ObjectFile/Breakpad + - `1` + - `1` + - `0` + - :good:`100%` + * - lldb/unittests/ObjectFile/ELF + - `1` + - `0` + - `1` + - :none:`0%` + * - lldb/unittests/ObjectFile/PECOFF + - `1` + - `0` + - `1` + - :none:`0%` + * - lldb/unittests/Platform + - `1` + - `1` + - `0` + - :good:`100%` + * - lldb/unittests/Platform/Android + - `1` + - `0` + - `1` + - :none:`0%` + * - lldb/unittests/Process/gdb-remote + - `6` + - `5` + - `1` + - :part:`83%` + * - lldb/unittests/Process/Linux + - `1` + - `0` + - `1` + - :none:`0%` + * - lldb/unittests/Process/minidump + - `2` + - `0` + - `2` + - :none:`0%` + * - lldb/unittests/Process/minidump/Inputs + - `1` + - `1` + - `0` + - :good:`100%` + * - lldb/unittests/Process/POSIX + - `1` + - `1` + - `0` + - :good:`100%` + * - lldb/unittests/ScriptInterpreter/Lua + - `2` + - `2` + - `0` + - :good:`100%` + * - lldb/unittests/ScriptInterpreter/Python + - `3` + - `1` + - `2` + - :part:`33%` + * - lldb/unittests/Signals + - `1` + - `1` + - `0` + - :good:`100%` + * - lldb/unittests/Symbol + - `7` + - `4` + - `3` + - :part:`57%` + * - lldb/unittests/SymbolFile/DWARF + - `3` + - `0` + - `3` + - :none:`0%` + * - lldb/unittests/SymbolFile/DWARF/Inputs + - `1` + - `1` + - `0` + - :good:`100%` + * - lldb/unittests/SymbolFile/NativePDB + - `1` + - `1` + - `0` + - :good:`100%` + * - lldb/unittests/SymbolFile/PDB + - `1` + - `0` + - `1` + - :none:`0%` + * - lldb/unittests/SymbolFile/PDB/Inputs + - `5` + - `5` + - `0` + - :good:`100%` + * - lldb/unittests/Target + - `7` + - `3` + - `4` + - :part:`42%` + * - lldb/unittests/TestingSupport + - `5` + - `4` + - `1` + - :part:`80%` + * - lldb/unittests/TestingSupport/Host + - `1` + - `1` + - `0` + - :good:`100%` + * - lldb/unittests/TestingSupport/Symbol + - `3` + - `3` + - `0` + - :good:`100%` + * - lldb/unittests/tools/lldb-server/inferior + - `2` + - `0` + - `2` + - :none:`0%` + * - lldb/unittests/tools/lldb-server/tests + - `8` + - `1` + - `7` + - :part:`12%` + * - lldb/unittests/UnwindAssembly/ARM64 + - `1` + - `0` + - `1` + - :none:`0%` + * - lldb/unittests/UnwindAssembly/PPC64 + - `1` + - `1` + - `0` + - :good:`100%` + * - lldb/unittests/UnwindAssembly/x86 + - `1` + - `0` + - `1` + - :none:`0%` + * - lldb/unittests/Utility + - `44` + - `31` + - `13` + - :part:`70%` + * - lldb/utils/lit-cpuid + - `1` + - `0` + - `1` + - :none:`0%` + * - lldb/utils/TableGen + - `6` + - `6` + - `0` + - :good:`100%` + * - llvm/benchmarks + - `1` + - `0` + - `1` + - :none:`0%` + * - llvm/bindings/go/llvm + - `6` + - `3` + - `3` + - :part:`50%` + * - llvm/cmake + - `1` + - `1` + - `0` + - :good:`100%` + * - llvm/examples/BrainF + - `3` + - `0` + - `3` + - :none:`0%` + * - llvm/examples/Bye + - `1` + - `1` + - `0` + - :good:`100%` + * - llvm/examples/ExceptionDemo + - `1` + - `0` + - `1` + - :none:`0%` + * - llvm/examples/Fibonacci + - `1` + - `0` + - `1` + - :none:`0%` + * - llvm/examples/HowToUseJIT + - `1` + - `0` + - `1` + - :none:`0%` + * - llvm/examples/HowToUseLLJIT + - `1` + - `1` + - `0` + - :good:`100%` + * - llvm/examples/IRTransforms + - `4` + - `4` + - `0` + - :good:`100%` + * - llvm/examples/Kaleidoscope/BuildingAJIT/Chapter1 + - `2` + - `1` + - `1` + - :part:`50%` + * - llvm/examples/Kaleidoscope/BuildingAJIT/Chapter2 + - `2` + - `1` + - `1` + - :part:`50%` + * - llvm/examples/Kaleidoscope/BuildingAJIT/Chapter3 + - `2` + - `0` + - `2` + - :none:`0%` + * - llvm/examples/Kaleidoscope/BuildingAJIT/Chapter4 + - `2` + - `0` + - `2` + - :none:`0%` + * - llvm/examples/Kaleidoscope/BuildingAJIT/Chapter5 + - `3` + - `0` + - `3` + - :none:`0%` + * - llvm/examples/Kaleidoscope/BuildingAJIT/Chapter5/Server + - `1` + - `0` + - `1` + - :none:`0%` + * - llvm/examples/Kaleidoscope/Chapter2 + - `1` + - `0` + - `1` + - :none:`0%` + * - llvm/examples/Kaleidoscope/Chapter3 + - `1` + - `0` + - `1` + - :none:`0%` + * - llvm/examples/Kaleidoscope/Chapter4 + - `1` + - `0` + - `1` + - :none:`0%` + * - llvm/examples/Kaleidoscope/Chapter5 + - `1` + - `0` + - `1` + - :none:`0%` + * - llvm/examples/Kaleidoscope/Chapter6 + - `1` + - `0` + - `1` + - :none:`0%` + * - llvm/examples/Kaleidoscope/Chapter7 + - `1` + - `0` + - `1` + - :none:`0%` + * - llvm/examples/Kaleidoscope/Chapter8 + - `1` + - `0` + - `1` + - :none:`0%` + * - llvm/examples/Kaleidoscope/Chapter9 + - `1` + - `0` + - `1` + - :none:`0%` + * - llvm/examples/Kaleidoscope/include + - `1` + - `1` + - `0` + - :good:`100%` + * - llvm/examples/Kaleidoscope/MCJIT/cached + - `2` + - `0` + - `2` + - :none:`0%` + * - llvm/examples/Kaleidoscope/MCJIT/complete + - `1` + - `0` + - `1` + - :none:`0%` + * - llvm/examples/Kaleidoscope/MCJIT/initial + - `1` + - `0` + - `1` + - :none:`0%` + * - llvm/examples/Kaleidoscope/MCJIT/lazy + - `2` + - `0` + - `2` + - :none:`0%` + * - llvm/examples/ModuleMaker + - `1` + - `0` + - `1` + - :none:`0%` + * - llvm/examples/OrcV2Examples + - `1` + - `1` + - `0` + - :good:`100%` + * - llvm/examples/OrcV2Examples/LLJITDumpObjects + - `1` + - `1` + - `0` + - :good:`100%` + * - llvm/examples/OrcV2Examples/LLJITWithCustomObjectLinkingLayer + - `1` + - `1` + - `0` + - :good:`100%` + * - llvm/examples/OrcV2Examples/LLJITWithGDBRegistrationListener + - `1` + - `1` + - `0` + - :good:`100%` + * - llvm/examples/OrcV2Examples/LLJITWithInitializers + - `1` + - `1` + - `0` + - :good:`100%` + * - llvm/examples/OrcV2Examples/LLJITWithLazyReexports + - `1` + - `1` + - `0` + - :good:`100%` + * - llvm/examples/OrcV2Examples/LLJITWithObjectCache + - `1` + - `1` + - `0` + - :good:`100%` + * - llvm/examples/OrcV2Examples/LLJITWithObjectLinkingLayerPlugin + - `1` + - `1` + - `0` + - :good:`100%` + * - llvm/examples/ParallelJIT + - `1` + - `0` + - `1` + - :none:`0%` + * - llvm/examples/SpeculativeJIT + - `1` + - `0` + - `1` + - :none:`0%` + * - llvm/examples/ThinLtoJIT + - `9` + - `8` + - `1` + - :part:`88%` + * - llvm/include/llvm + - `8` + - `2` + - `6` + - :part:`25%` + * - llvm/include/llvm/ADT + - `84` + - `24` + - `60` + - :part:`28%` + * - llvm/include/llvm/Analysis + - `107` + - `29` + - `78` + - :part:`27%` + * - llvm/include/llvm/Analysis/ML + - `1` + - `1` + - `0` + - :good:`100%` + * - llvm/include/llvm/Analysis/Utils + - `1` + - `0` + - `1` + - :none:`0%` + * - llvm/include/llvm/AsmParser + - `2` + - `1` + - `1` + - :part:`50%` + * - llvm/include/llvm/BinaryFormat + - `13` + - `9` + - `4` + - :part:`69%` + * - llvm/include/llvm/Bitcode + - `5` + - `1` + - `4` + - :part:`20%` + * - llvm/include/llvm/Bitstream + - `3` + - `0` + - `3` + - :none:`0%` + * - llvm/include/llvm/CodeGen + - `140` + - `32` + - `108` + - :part:`22%` + * - llvm/include/llvm/CodeGen/GlobalISel + - `28` + - `12` + - `16` + - :part:`42%` + * - llvm/include/llvm/CodeGen/MIRParser + - `2` + - `1` + - `1` + - :part:`50%` + * - llvm/include/llvm/CodeGen/PBQP + - `5` + - `1` + - `4` + - :part:`20%` + * - llvm/include/llvm/DebugInfo + - `1` + - `0` + - `1` + - :none:`0%` + * - llvm/include/llvm/DebugInfo/CodeView + - `57` + - `40` + - `17` + - :part:`70%` + * - llvm/include/llvm/DebugInfo/DWARF + - `32` + - `17` + - `15` + - :part:`53%` + * - llvm/include/llvm/DebugInfo/GSYM + - `14` + - `2` + - `12` + - :part:`14%` + * - llvm/include/llvm/DebugInfo/MSF + - `5` + - `4` + - `1` + - :part:`80%` + * - llvm/include/llvm/DebugInfo/PDB + - `50` + - `7` + - `43` + - :part:`14%` + * - llvm/include/llvm/DebugInfo/PDB/DIA + - `20` + - `9` + - `11` + - :part:`45%` + * - llvm/include/llvm/DebugInfo/PDB/Native + - `49` + - `31` + - `18` + - :part:`63%` + * - llvm/include/llvm/DebugInfo/Symbolize + - `3` + - `0` + - `3` + - :none:`0%` + * - llvm/include/llvm/Demangle + - `7` + - `3` + - `4` + - :part:`42%` + * - llvm/include/llvm/DWARFLinker + - `4` + - `4` + - `0` + - :good:`100%` + * - llvm/include/llvm/ExecutionEngine + - `14` + - `3` + - `11` + - :part:`21%` + * - llvm/include/llvm/ExecutionEngine/JITLink + - `8` + - `5` + - `3` + - :part:`62%` + * - llvm/include/llvm/ExecutionEngine/Orc + - `32` + - `11` + - `21` + - :part:`34%` + * - llvm/include/llvm/ExecutionEngine/Orc/RPC + - `3` + - `0` + - `3` + - :none:`0%` + * - llvm/include/llvm/Frontend/OpenMP + - `3` + - `3` + - `0` + - :good:`100%` + * - llvm/include/llvm/FuzzMutate + - `6` + - `0` + - `6` + - :none:`0%` + * - llvm/include/llvm/IR + - `84` + - `15` + - `69` + - :part:`17%` + * - llvm/include/llvm/IRReader + - `1` + - `0` + - `1` + - :none:`0%` + * - llvm/include/llvm/LineEditor + - `1` + - `0` + - `1` + - :none:`0%` + * - llvm/include/llvm/Linker + - `2` + - `0` + - `2` + - :none:`0%` + * - llvm/include/llvm/LTO + - `5` + - `2` + - `3` + - :part:`40%` + * - llvm/include/llvm/LTO/legacy + - `4` + - `0` + - `4` + - :none:`0%` + * - llvm/include/llvm/MC + - `69` + - `17` + - `52` + - :part:`24%` + * - llvm/include/llvm/MC/MCDisassembler + - `4` + - `1` + - `3` + - :part:`25%` + * - llvm/include/llvm/MC/MCParser + - `8` + - `3` + - `5` + - :part:`37%` + * - llvm/include/llvm/MCA + - `8` + - `8` + - `0` + - :good:`100%` + * - llvm/include/llvm/MCA/HardwareUnits + - `6` + - `4` + - `2` + - :part:`66%` + * - llvm/include/llvm/MCA/Stages + - `7` + - `6` + - `1` + - :part:`85%` + * - llvm/include/llvm/Object + - `30` + - `10` + - `20` + - :part:`33%` + * - llvm/include/llvm/ObjectYAML + - `15` + - `13` + - `2` + - :part:`86%` + * - llvm/include/llvm/Option + - `5` + - `1` + - `4` + - :part:`20%` + * - llvm/include/llvm/Passes + - `3` + - `1` + - `2` + - :part:`33%` + * - llvm/include/llvm/ProfileData + - `8` + - `4` + - `4` + - :part:`50%` + * - llvm/include/llvm/ProfileData/Coverage + - `3` + - `2` + - `1` + - :part:`66%` + * - llvm/include/llvm/Remarks + - `11` + - `10` + - `1` + - :part:`90%` + * - llvm/include/llvm/Support + - `168` + - `49` + - `119` + - :part:`29%` + * - llvm/include/llvm/Support/Solaris/sys + - `1` + - `1` + - `0` + - :good:`100%` + * - llvm/include/llvm/Support/Windows + - `1` + - `0` + - `1` + - :none:`0%` + * - llvm/include/llvm/TableGen + - `7` + - `1` + - `6` + - :part:`14%` + * - llvm/include/llvm/Target + - `5` + - `1` + - `4` + - :part:`20%` + * - llvm/include/llvm/Testing/Support + - `3` + - `2` + - `1` + - :part:`66%` + * - llvm/include/llvm/TextAPI/ELF + - `2` + - `0` + - `2` + - :none:`0%` + * - llvm/include/llvm/TextAPI/MachO + - `9` + - `8` + - `1` + - :part:`88%` + * - llvm/include/llvm/ToolDrivers/llvm-dlltool + - `1` + - `1` + - `0` + - :good:`100%` + * - llvm/include/llvm/ToolDrivers/llvm-lib + - `1` + - `0` + - `1` + - :none:`0%` + * - llvm/include/llvm/Transforms + - `8` + - `2` + - `6` + - :part:`25%` + * - llvm/include/llvm/Transforms/AggressiveInstCombine + - `1` + - `0` + - `1` + - :none:`0%` + * - llvm/include/llvm/Transforms/Coroutines + - `4` + - `4` + - `0` + - :good:`100%` + * - llvm/include/llvm/Transforms/InstCombine + - `2` + - `0` + - `2` + - :none:`0%` + * - llvm/include/llvm/Transforms/Instrumentation + - `14` + - `8` + - `6` + - :part:`57%` + * - llvm/include/llvm/Transforms/IPO + - `29` + - `19` + - `10` + - :part:`65%` + * - llvm/include/llvm/Transforms/Scalar + - `61` + - `32` + - `29` + - :part:`52%` + * - llvm/include/llvm/Transforms/Utils + - `57` + - `26` + - `31` + - :part:`45%` + * - llvm/include/llvm/Transforms/Vectorize + - `5` + - `1` + - `4` + - :part:`20%` + * - llvm/include/llvm/WindowsManifest + - `1` + - `1` + - `0` + - :good:`100%` + * - llvm/include/llvm/WindowsResource + - `3` + - `1` + - `2` + - :part:`33%` + * - llvm/include/llvm/XRay + - `17` + - `14` + - `3` + - :part:`82%` + * - llvm/include/llvm-c + - `26` + - `11` + - `15` + - :part:`42%` + * - llvm/include/llvm-c/Transforms + - `8` + - `2` + - `6` + - :part:`25%` + * - llvm/lib/Analysis + - `104` + - `28` + - `76` + - :part:`26%` + * - llvm/lib/Analysis/ML + - `1` + - `1` + - `0` + - :good:`100%` + * - llvm/lib/AsmParser + - `6` + - `2` + - `4` + - :part:`33%` + * - llvm/lib/BinaryFormat + - `11` + - `7` + - `4` + - :part:`63%` + * - llvm/lib/Bitcode/Reader + - `7` + - `2` + - `5` + - :part:`28%` + * - llvm/lib/Bitcode/Writer + - `5` + - `0` + - `5` + - :none:`0%` + * - llvm/lib/Bitstream/Reader + - `1` + - `0` + - `1` + - :none:`0%` + * - llvm/lib/CodeGen + - `198` + - `33` + - `165` + - :part:`16%` + * - llvm/lib/CodeGen/AsmPrinter + - `42` + - `14` + - `28` + - :part:`33%` + * - llvm/lib/CodeGen/GlobalISel + - `24` + - `8` + - `16` + - :part:`33%` + * - llvm/lib/CodeGen/MIRParser + - `4` + - `1` + - `3` + - :part:`25%` + * - llvm/lib/CodeGen/SelectionDAG + - `31` + - `2` + - `29` + - :part:`6%` + * - llvm/lib/DebugInfo/CodeView + - `40` + - `25` + - `15` + - :part:`62%` + * - llvm/lib/DebugInfo/DWARF + - `28` + - `6` + - `22` + - :part:`21%` + * - llvm/lib/DebugInfo/GSYM + - `11` + - `1` + - `10` + - :part:`9%` + * - llvm/lib/DebugInfo/MSF + - `4` + - `4` + - `0` + - :good:`100%` + * - llvm/lib/DebugInfo/PDB + - `40` + - `34` + - `6` + - :part:`85%` + * - llvm/lib/DebugInfo/PDB/DIA + - `18` + - `15` + - `3` + - :part:`83%` + * - llvm/lib/DebugInfo/PDB/Native + - `45` + - `33` + - `12` + - :part:`73%` + * - llvm/lib/DebugInfo/Symbolize + - `4` + - `1` + - `3` + - :part:`25%` + * - llvm/lib/Demangle + - `4` + - `2` + - `2` + - :part:`50%` + * - llvm/lib/DWARFLinker + - `4` + - `3` + - `1` + - :part:`75%` + * - llvm/lib/ExecutionEngine + - `5` + - `1` + - `4` + - :part:`20%` + * - llvm/lib/ExecutionEngine/IntelJITEvents + - `5` + - `0` + - `5` + - :none:`0%` + * - llvm/lib/ExecutionEngine/Interpreter + - `4` + - `0` + - `4` + - :none:`0%` + * - llvm/lib/ExecutionEngine/JITLink + - `14` + - `9` + - `5` + - :part:`64%` + * - llvm/lib/ExecutionEngine/MCJIT + - `2` + - `0` + - `2` + - :none:`0%` + * - llvm/lib/ExecutionEngine/OProfileJIT + - `2` + - `0` + - `2` + - :none:`0%` + * - llvm/lib/ExecutionEngine/Orc + - `28` + - `15` + - `13` + - :part:`53%` + * - llvm/lib/ExecutionEngine/OrcError + - `2` + - `0` + - `2` + - :none:`0%` + * - llvm/lib/ExecutionEngine/PerfJITEvents + - `1` + - `0` + - `1` + - :none:`0%` + * - llvm/lib/ExecutionEngine/RuntimeDyld + - `12` + - `1` + - `11` + - :part:`8%` + * - llvm/lib/ExecutionEngine/RuntimeDyld/Targets + - `10` + - `1` + - `9` + - :part:`10%` + * - llvm/lib/Extensions + - `1` + - `1` + - `0` + - :good:`100%` + * - llvm/lib/Frontend/OpenMP + - `3` + - `3` + - `0` + - :good:`100%` + * - llvm/lib/FuzzMutate + - `5` + - `2` + - `3` + - :part:`40%` + * - llvm/lib/IR + - `61` + - `8` + - `53` + - :part:`13%` + * - llvm/lib/IRReader + - `1` + - `0` + - `1` + - :none:`0%` + * - llvm/lib/LineEditor + - `1` + - `0` + - `1` + - :none:`0%` + * - llvm/lib/Linker + - `3` + - `0` + - `3` + - :none:`0%` + * - llvm/lib/LTO + - `8` + - `1` + - `7` + - :part:`12%` + * - llvm/lib/MC + - `62` + - `20` + - `42` + - :part:`32%` + * - llvm/lib/MC/MCDisassembler + - `6` + - `4` + - `2` + - :part:`66%` + * - llvm/lib/MC/MCParser + - `12` + - `1` + - `11` + - :part:`8%` + * - llvm/lib/MCA + - `7` + - `3` + - `4` + - :part:`42%` + * - llvm/lib/MCA/HardwareUnits + - `6` + - `3` + - `3` + - :part:`50%` + * - llvm/lib/MCA/Stages + - `7` + - `6` + - `1` + - :part:`85%` + * - llvm/lib/Object + - `29` + - `10` + - `19` + - :part:`34%` + * - llvm/lib/ObjectYAML + - `22` + - `11` + - `11` + - :part:`50%` + * - llvm/lib/Option + - `4` + - `0` + - `4` + - :none:`0%` + * - llvm/lib/Passes + - `3` + - `2` + - `1` + - :part:`66%` + * - llvm/lib/ProfileData + - `8` + - `2` + - `6` + - :part:`25%` + * - llvm/lib/ProfileData/Coverage + - `3` + - `0` + - `3` + - :none:`0%` + * - llvm/lib/Remarks + - `13` + - `10` + - `3` + - :part:`76%` + * - llvm/lib/Support + - `130` + - `42` + - `88` + - :part:`32%` + * - llvm/lib/Support/Unix + - `1` + - `0` + - `1` + - :none:`0%` + * - llvm/lib/TableGen + - `11` + - `1` + - `10` + - :part:`9%` + * - llvm/lib/Target + - `5` + - `0` + - `5` + - :none:`0%` + * - llvm/lib/Target/AArch64 + - `67` + - `8` + - `59` + - :part:`11%` + * - llvm/lib/Target/AArch64/AsmParser + - `1` + - `0` + - `1` + - :none:`0%` + * - llvm/lib/Target/AArch64/Disassembler + - `4` + - `1` + - `3` + - :part:`25%` + * - llvm/lib/Target/AArch64/MCTargetDesc + - `21` + - `6` + - `15` + - :part:`28%` + * - llvm/lib/Target/AArch64/TargetInfo + - `2` + - `1` + - `1` + - :part:`50%` + * - llvm/lib/Target/AArch64/Utils + - `2` + - `0` + - `2` + - :none:`0%` + * - llvm/lib/Target/AMDGPU + - `145` + - `11` + - `134` + - :part:`7%` + * - llvm/lib/Target/AMDGPU/AsmParser + - `1` + - `0` + - `1` + - :none:`0%` + * - llvm/lib/Target/AMDGPU/Disassembler + - `2` + - `0` + - `2` + - :none:`0%` + * - llvm/lib/Target/AMDGPU/MCTargetDesc + - `18` + - `3` + - `15` + - :part:`16%` + * - llvm/lib/Target/AMDGPU/TargetInfo + - `2` + - `1` + - `1` + - :part:`50%` + * - llvm/lib/Target/AMDGPU/Utils + - `9` + - `2` + - `7` + - :part:`22%` + * - llvm/lib/Target/ARC + - `24` + - `19` + - `5` + - :part:`79%` + * - llvm/lib/Target/ARC/Disassembler + - `1` + - `0` + - `1` + - :none:`0%` + * - llvm/lib/Target/ARC/MCTargetDesc + - `7` + - `6` + - `1` + - :part:`85%` + * - llvm/lib/Target/ARC/TargetInfo + - `2` + - `2` + - `0` + - :good:`100%` + * - llvm/lib/Target/ARM + - `71` + - `7` + - `64` + - :part:`9%` + * - llvm/lib/Target/ARM/AsmParser + - `1` + - `0` + - `1` + - :none:`0%` + * - llvm/lib/Target/ARM/Disassembler + - `1` + - `0` + - `1` + - :none:`0%` + * - llvm/lib/Target/ARM/MCTargetDesc + - `26` + - `2` + - `24` + - :part:`7%` + * - llvm/lib/Target/ARM/TargetInfo + - `2` + - `2` + - `0` + - :good:`100%` + * - llvm/lib/Target/ARM/Utils + - `2` + - `0` + - `2` + - :none:`0%` + * - llvm/lib/Target/AVR + - `23` + - `4` + - `19` + - :part:`17%` + * - llvm/lib/Target/AVR/AsmParser + - `1` + - `0` + - `1` + - :none:`0%` + * - llvm/lib/Target/AVR/Disassembler + - `1` + - `0` + - `1` + - :none:`0%` + * - llvm/lib/Target/AVR/MCTargetDesc + - `20` + - `6` + - `14` + - :part:`30%` + * - llvm/lib/Target/AVR/TargetInfo + - `2` + - `1` + - `1` + - :part:`50%` + * - llvm/lib/Target/BPF + - `28` + - `5` + - `23` + - :part:`17%` + * - llvm/lib/Target/BPF/AsmParser + - `1` + - `0` + - `1` + - :none:`0%` + * - llvm/lib/Target/BPF/Disassembler + - `1` + - `0` + - `1` + - :none:`0%` + * - llvm/lib/Target/BPF/MCTargetDesc + - `8` + - `1` + - `7` + - :part:`12%` + * - llvm/lib/Target/BPF/TargetInfo + - `2` + - `1` + - `1` + - :part:`50%` + * - llvm/lib/Target/Hexagon + - `77` + - `2` + - `75` + - :part:`2%` + * - llvm/lib/Target/Hexagon/AsmParser + - `1` + - `0` + - `1` + - :none:`0%` + * - llvm/lib/Target/Hexagon/Disassembler + - `1` + - `0` + - `1` + - :none:`0%` + * - llvm/lib/Target/Hexagon/MCTargetDesc + - `26` + - `6` + - `20` + - :part:`23%` + * - llvm/lib/Target/Hexagon/TargetInfo + - `2` + - `1` + - `1` + - :part:`50%` + * - llvm/lib/Target/Lanai + - `28` + - `19` + - `9` + - :part:`67%` + * - llvm/lib/Target/Lanai/AsmParser + - `1` + - `0` + - `1` + - :none:`0%` + * - llvm/lib/Target/Lanai/Disassembler + - `2` + - `2` + - `0` + - :good:`100%` + * - llvm/lib/Target/Lanai/MCTargetDesc + - `13` + - `12` + - `1` + - :part:`92%` + * - llvm/lib/Target/Lanai/TargetInfo + - `2` + - `2` + - `0` + - :good:`100%` + * - llvm/lib/Target/Mips + - `69` + - `12` + - `57` + - :part:`17%` + * - llvm/lib/Target/Mips/AsmParser + - `1` + - `0` + - `1` + - :none:`0%` + * - llvm/lib/Target/Mips/Disassembler + - `1` + - `0` + - `1` + - :none:`0%` + * - llvm/lib/Target/Mips/MCTargetDesc + - `25` + - `6` + - `19` + - :part:`24%` + * - llvm/lib/Target/Mips/TargetInfo + - `2` + - `2` + - `0` + - :good:`100%` + * - llvm/lib/Target/MSP430 + - `20` + - `0` + - `20` + - :none:`0%` + * - llvm/lib/Target/MSP430/AsmParser + - `1` + - `0` + - `1` + - :none:`0%` + * - llvm/lib/Target/MSP430/Disassembler + - `1` + - `0` + - `1` + - :none:`0%` + * - llvm/lib/Target/MSP430/MCTargetDesc + - `11` + - `3` + - `8` + - :part:`27%` + * - llvm/lib/Target/MSP430/TargetInfo + - `2` + - `2` + - `0` + - :good:`100%` + * - llvm/lib/Target/NVPTX + - `42` + - `7` + - `35` + - :part:`16%` + * - llvm/lib/Target/NVPTX/MCTargetDesc + - `9` + - `5` + - `4` + - :part:`55%` + * - llvm/lib/Target/NVPTX/TargetInfo + - `2` + - `2` + - `0` + - :good:`100%` + * - llvm/lib/Target/PowerPC + - `53` + - `2` + - `51` + - :part:`3%` + * - llvm/lib/Target/PowerPC/AsmParser + - `1` + - `0` + - `1` + - :none:`0%` + * - llvm/lib/Target/PowerPC/Disassembler + - `1` + - `0` + - `1` + - :none:`0%` + * - llvm/lib/Target/PowerPC/MCTargetDesc + - `18` + - `2` + - `16` + - :part:`11%` + * - llvm/lib/Target/PowerPC/TargetInfo + - `2` + - `2` + - `0` + - :good:`100%` + * - llvm/lib/Target/RISCV + - `31` + - `13` + - `18` + - :part:`41%` + * - llvm/lib/Target/RISCV/AsmParser + - `1` + - `0` + - `1` + - :none:`0%` + * - llvm/lib/Target/RISCV/Disassembler + - `1` + - `0` + - `1` + - :none:`0%` + * - llvm/lib/Target/RISCV/MCTargetDesc + - `17` + - `8` + - `9` + - :part:`47%` + * - llvm/lib/Target/RISCV/TargetInfo + - `2` + - `2` + - `0` + - :good:`100%` + * - llvm/lib/Target/RISCV/Utils + - `4` + - `3` + - `1` + - :part:`75%` + * - llvm/lib/Target/Sparc + - `23` + - `2` + - `21` + - :part:`8%` + * - llvm/lib/Target/Sparc/AsmParser + - `1` + - `0` + - `1` + - :none:`0%` + * - llvm/lib/Target/Sparc/Disassembler + - `1` + - `0` + - `1` + - :none:`0%` + * - llvm/lib/Target/Sparc/MCTargetDesc + - `14` + - `4` + - `10` + - :part:`28%` + * - llvm/lib/Target/Sparc/TargetInfo + - `2` + - `2` + - `0` + - :good:`100%` + * - llvm/lib/Target/SystemZ + - `40` + - `3` + - `37` + - :part:`7%` + * - llvm/lib/Target/SystemZ/AsmParser + - `1` + - `0` + - `1` + - :none:`0%` + * - llvm/lib/Target/SystemZ/Disassembler + - `1` + - `0` + - `1` + - :none:`0%` + * - llvm/lib/Target/SystemZ/MCTargetDesc + - `10` + - `4` + - `6` + - :part:`40%` + * - llvm/lib/Target/SystemZ/TargetInfo + - `2` + - `2` + - `0` + - :good:`100%` + * - llvm/lib/Target/VE + - `19` + - `15` + - `4` + - :part:`78%` + * - llvm/lib/Target/VE/AsmParser + - `1` + - `1` + - `0` + - :good:`100%` + * - llvm/lib/Target/VE/MCTargetDesc + - `14` + - `13` + - `1` + - :part:`92%` + * - llvm/lib/Target/VE/TargetInfo + - `2` + - `1` + - `1` + - :part:`50%` + * - llvm/lib/Target/WebAssembly + - `57` + - `41` + - `16` + - :part:`71%` + * - llvm/lib/Target/WebAssembly/AsmParser + - `1` + - `0` + - `1` + - :none:`0%` + * - llvm/lib/Target/WebAssembly/Disassembler + - `1` + - `1` + - `0` + - :good:`100%` + * - llvm/lib/Target/WebAssembly/MCTargetDesc + - `12` + - `8` + - `4` + - :part:`66%` + * - llvm/lib/Target/WebAssembly/TargetInfo + - `2` + - `2` + - `0` + - :good:`100%` + * - llvm/lib/Target/X86 + - `75` + - `12` + - `63` + - :part:`16%` + * - llvm/lib/Target/X86/AsmParser + - `3` + - `0` + - `3` + - :none:`0%` + * - llvm/lib/Target/X86/Disassembler + - `2` + - `0` + - `2` + - :none:`0%` + * - llvm/lib/Target/X86/MCTargetDesc + - `25` + - `6` + - `19` + - :part:`24%` + * - llvm/lib/Target/X86/TargetInfo + - `2` + - `1` + - `1` + - :part:`50%` + * - llvm/lib/Target/XCore + - `27` + - `2` + - `25` + - :part:`7%` + * - llvm/lib/Target/XCore/Disassembler + - `1` + - `0` + - `1` + - :none:`0%` + * - llvm/lib/Target/XCore/MCTargetDesc + - `6` + - `3` + - `3` + - :part:`50%` + * - llvm/lib/Target/XCore/TargetInfo + - `2` + - `1` + - `1` + - :part:`50%` + * - llvm/lib/Testing/Support + - `3` + - `3` + - `0` + - :good:`100%` + * - llvm/lib/TextAPI/ELF + - `2` + - `1` + - `1` + - :part:`50%` + * - llvm/lib/TextAPI/MachO + - `11` + - `8` + - `3` + - :part:`72%` + * - llvm/lib/ToolDrivers/llvm-dlltool + - `1` + - `0` + - `1` + - :none:`0%` + * - llvm/lib/ToolDrivers/llvm-lib + - `1` + - `0` + - `1` + - :none:`0%` + * - llvm/lib/Transforms/AggressiveInstCombine + - `3` + - `0` + - `3` + - :none:`0%` + * - llvm/lib/Transforms/CFGuard + - `1` + - `1` + - `0` + - :good:`100%` + * - llvm/lib/Transforms/Coroutines + - `8` + - `0` + - `8` + - :none:`0%` + * - llvm/lib/Transforms/Hello + - `1` + - `0` + - `1` + - :none:`0%` + * - llvm/lib/Transforms/InstCombine + - `16` + - `1` + - `15` + - :part:`6%` + * - llvm/lib/Transforms/Instrumentation + - `21` + - `2` + - `19` + - :part:`9%` + * - llvm/lib/Transforms/IPO + - `39` + - `4` + - `35` + - :part:`10%` + * - llvm/lib/Transforms/ObjCARC + - `15` + - `3` + - `12` + - :part:`20%` + * - llvm/lib/Transforms/Scalar + - `75` + - `10` + - `65` + - :part:`13%` + * - llvm/lib/Transforms/Utils + - `72` + - `13` + - `59` + - :part:`18%` + * - llvm/lib/Transforms/Vectorize + - `22` + - `14` + - `8` + - :part:`63%` + * - llvm/lib/WindowsManifest + - `1` + - `1` + - `0` + - :good:`100%` + * - llvm/lib/XRay + - `14` + - `12` + - `2` + - :part:`85%` + * - llvm/tools/bugpoint + - `12` + - `1` + - `11` + - :part:`8%` + * - llvm/tools/bugpoint-passes + - `1` + - `0` + - `1` + - :none:`0%` + * - llvm/tools/dsymutil + - `18` + - `15` + - `3` + - :part:`83%` + * - llvm/tools/gold + - `1` + - `0` + - `1` + - :none:`0%` + * - llvm/tools/llc + - `1` + - `0` + - `1` + - :none:`0%` + * - llvm/tools/lli + - `2` + - `0` + - `2` + - :none:`0%` + * - llvm/tools/lli/ChildTarget + - `1` + - `1` + - `0` + - :good:`100%` + * - llvm/tools/llvm-ar + - `1` + - `0` + - `1` + - :none:`0%` + * - llvm/tools/llvm-as + - `1` + - `0` + - `1` + - :none:`0%` + * - llvm/tools/llvm-as-fuzzer + - `1` + - `0` + - `1` + - :none:`0%` + * - llvm/tools/llvm-bcanalyzer + - `1` + - `1` + - `0` + - :good:`100%` + * - llvm/tools/llvm-c-test + - `2` + - `0` + - `2` + - :none:`0%` + * - llvm/tools/llvm-cat + - `1` + - `0` + - `1` + - :none:`0%` + * - llvm/tools/llvm-cfi-verify + - `1` + - `0` + - `1` + - :none:`0%` + * - llvm/tools/llvm-cfi-verify/lib + - `4` + - `1` + - `3` + - :part:`25%` + * - llvm/tools/llvm-config + - `1` + - `0` + - `1` + - :none:`0%` + * - llvm/tools/llvm-cov + - `23` + - `12` + - `11` + - :part:`52%` + * - llvm/tools/llvm-cvtres + - `1` + - `0` + - `1` + - :none:`0%` + * - llvm/tools/llvm-cxxdump + - `4` + - `2` + - `2` + - :part:`50%` + * - llvm/tools/llvm-cxxfilt + - `1` + - `0` + - `1` + - :none:`0%` + * - llvm/tools/llvm-cxxmap + - `1` + - `0` + - `1` + - :none:`0%` + * - llvm/tools/llvm-diff + - `7` + - `0` + - `7` + - :none:`0%` + * - llvm/tools/llvm-dis + - `1` + - `0` + - `1` + - :none:`0%` + * - llvm/tools/llvm-dwarfdump + - `4` + - `2` + - `2` + - :part:`50%` + * - llvm/tools/llvm-dwarfdump/fuzzer + - `1` + - `0` + - `1` + - :none:`0%` + * - llvm/tools/llvm-dwp + - `4` + - `1` + - `3` + - :part:`25%` + * - llvm/tools/llvm-elfabi + - `5` + - `2` + - `3` + - :part:`40%` + * - llvm/tools/llvm-exegesis + - `1` + - `1` + - `0` + - :good:`100%` + * - llvm/tools/llvm-exegesis/lib + - `44` + - `34` + - `10` + - :part:`77%` + * - llvm/tools/llvm-exegesis/lib/AArch64 + - `1` + - `1` + - `0` + - :good:`100%` + * - llvm/tools/llvm-exegesis/lib/Mips + - `1` + - `0` + - `1` + - :none:`0%` + * - llvm/tools/llvm-exegesis/lib/PowerPC + - `1` + - `1` + - `0` + - :good:`100%` + * - llvm/tools/llvm-exegesis/lib/X86 + - `1` + - `1` + - `0` + - :good:`100%` + * - llvm/tools/llvm-extract + - `1` + - `0` + - `1` + - :none:`0%` + * - llvm/tools/llvm-gsymutil + - `1` + - `0` + - `1` + - :none:`0%` + * - llvm/tools/llvm-ifs + - `1` + - `1` + - `0` + - :good:`100%` + * - llvm/tools/llvm-isel-fuzzer + - `2` + - `1` + - `1` + - :part:`50%` + * - llvm/tools/llvm-itanium-demangle-fuzzer + - `2` + - `1` + - `1` + - :part:`50%` + * - llvm/tools/llvm-jitlink + - `4` + - `2` + - `2` + - :part:`50%` + * - llvm/tools/llvm-jitlistener + - `1` + - `0` + - `1` + - :none:`0%` + * - llvm/tools/llvm-link + - `1` + - `0` + - `1` + - :none:`0%` + * - llvm/tools/llvm-lipo + - `1` + - `0` + - `1` + - :none:`0%` + * - llvm/tools/llvm-lto + - `1` + - `0` + - `1` + - :none:`0%` + * - llvm/tools/llvm-lto2 + - `1` + - `0` + - `1` + - :none:`0%` + * - llvm/tools/llvm-mc + - `3` + - `1` + - `2` + - :part:`33%` + * - llvm/tools/llvm-mc-assemble-fuzzer + - `1` + - `0` + - `1` + - :none:`0%` + * - llvm/tools/llvm-mc-disassemble-fuzzer + - `1` + - `0` + - `1` + - :none:`0%` + * - llvm/tools/llvm-mca + - `7` + - `6` + - `1` + - :part:`85%` + * - llvm/tools/llvm-mca/Views + - `20` + - `15` + - `5` + - :part:`75%` + * - llvm/tools/llvm-microsoft-demangle-fuzzer + - `2` + - `2` + - `0` + - :good:`100%` + * - llvm/tools/llvm-ml + - `3` + - `1` + - `2` + - :part:`33%` + * - llvm/tools/llvm-modextract + - `1` + - `1` + - `0` + - :good:`100%` + * - llvm/tools/llvm-mt + - `1` + - `0` + - `1` + - :none:`0%` + * - llvm/tools/llvm-nm + - `1` + - `0` + - `1` + - :none:`0%` + * - llvm/tools/llvm-objcopy + - `6` + - `4` + - `2` + - :part:`66%` + * - llvm/tools/llvm-objcopy/COFF + - `8` + - `7` + - `1` + - :part:`87%` + * - llvm/tools/llvm-objcopy/ELF + - `6` + - `3` + - `3` + - :part:`50%` + * - llvm/tools/llvm-objcopy/MachO + - `10` + - `10` + - `0` + - :good:`100%` + * - llvm/tools/llvm-objcopy/wasm + - `8` + - `8` + - `0` + - :good:`100%` + * - llvm/tools/llvm-objdump + - `12` + - `8` + - `4` + - :part:`66%` + * - llvm/tools/llvm-opt-fuzzer + - `2` + - `0` + - `2` + - :none:`0%` + * - llvm/tools/llvm-opt-report + - `1` + - `0` + - `1` + - :none:`0%` + * - llvm/tools/llvm-pdbutil + - `47` + - `16` + - `31` + - :part:`34%` + * - llvm/tools/llvm-profdata + - `1` + - `0` + - `1` + - :none:`0%` + * - llvm/tools/llvm-rc + - `12` + - `7` + - `5` + - :part:`58%` + * - llvm/tools/llvm-readobj + - `21` + - `3` + - `18` + - :part:`14%` + * - llvm/tools/llvm-reduce + - `4` + - `2` + - `2` + - :part:`50%` + * - llvm/tools/llvm-reduce/deltas + - `14` + - `8` + - `6` + - :part:`57%` + * - llvm/tools/llvm-rtdyld + - `1` + - `0` + - `1` + - :none:`0%` + * - llvm/tools/llvm-shlib + - `1` + - `1` + - `0` + - :good:`100%` + * - llvm/tools/llvm-size + - `1` + - `0` + - `1` + - :none:`0%` + * - llvm/tools/llvm-special-case-list-fuzzer + - `2` + - `2` + - `0` + - :good:`100%` + * - llvm/tools/llvm-split + - `1` + - `0` + - `1` + - :none:`0%` + * - llvm/tools/llvm-stress + - `1` + - `0` + - `1` + - :none:`0%` + * - llvm/tools/llvm-strings + - `1` + - `0` + - `1` + - :none:`0%` + * - llvm/tools/llvm-symbolizer + - `1` + - `0` + - `1` + - :none:`0%` + * - llvm/tools/llvm-undname + - `1` + - `1` + - `0` + - :good:`100%` + * - llvm/tools/llvm-xray + - `19` + - `16` + - `3` + - :part:`84%` + * - llvm/tools/llvm-yaml-numeric-parser-fuzzer + - `2` + - `2` + - `0` + - :good:`100%` + * - llvm/tools/lto + - `2` + - `1` + - `1` + - :part:`50%` + * - llvm/tools/obj2yaml + - `11` + - `4` + - `7` + - :part:`36%` + * - llvm/tools/opt + - `10` + - `2` + - `8` + - :part:`20%` + * - llvm/tools/remarks-shlib + - `1` + - `0` + - `1` + - :none:`0%` + * - llvm/tools/sancov + - `1` + - `0` + - `1` + - :none:`0%` + * - llvm/tools/sanstats + - `1` + - `1` + - `0` + - :good:`100%` + * - llvm/tools/verify-uselistorder + - `1` + - `0` + - `1` + - :none:`0%` + * - llvm/tools/vfabi-demangle-fuzzer + - `1` + - `1` + - `0` + - :good:`100%` + * - llvm/tools/yaml2obj + - `1` + - `1` + - `0` + - :good:`100%` + * - llvm/unittests/ADT + - `74` + - `29` + - `45` + - :part:`39%` + * - llvm/unittests/Analysis + - `33` + - `10` + - `23` + - :part:`30%` + * - llvm/unittests/Analysis/ML + - `1` + - `1` + - `0` + - :good:`100%` + * - llvm/unittests/AsmParser + - `1` + - `1` + - `0` + - :good:`100%` + * - llvm/unittests/BinaryFormat + - `6` + - `5` + - `1` + - :part:`83%` + * - llvm/unittests/Bitcode + - `2` + - `1` + - `1` + - :part:`50%` + * - llvm/unittests/Bitstream + - `2` + - `1` + - `1` + - :part:`50%` + * - llvm/unittests/CodeGen + - `10` + - `2` + - `8` + - :part:`20%` + * - llvm/unittests/CodeGen/GlobalISel + - `10` + - `1` + - `9` + - :part:`10%` + * - llvm/unittests/DebugInfo/CodeView + - `3` + - `1` + - `2` + - :part:`33%` + * - llvm/unittests/DebugInfo/DWARF + - `13` + - `8` + - `5` + - :part:`61%` + * - llvm/unittests/DebugInfo/GSYM + - `1` + - `0` + - `1` + - :none:`0%` + * - llvm/unittests/DebugInfo/MSF + - `3` + - `2` + - `1` + - :part:`66%` + * - llvm/unittests/DebugInfo/PDB + - `5` + - `3` + - `2` + - :part:`60%` + * - llvm/unittests/DebugInfo/PDB/Inputs + - `1` + - `1` + - `0` + - :good:`100%` + * - llvm/unittests/Demangle + - `3` + - `2` + - `1` + - :part:`66%` + * - llvm/unittests/ExecutionEngine + - `1` + - `0` + - `1` + - :none:`0%` + * - llvm/unittests/ExecutionEngine/JITLink + - `1` + - `1` + - `0` + - :good:`100%` + * - llvm/unittests/ExecutionEngine/MCJIT + - `7` + - `0` + - `7` + - :none:`0%` + * - llvm/unittests/ExecutionEngine/Orc + - `20` + - `4` + - `16` + - :part:`20%` + * - llvm/unittests/Frontend + - `2` + - `1` + - `1` + - :part:`50%` + * - llvm/unittests/FuzzMutate + - `4` + - `0` + - `4` + - :none:`0%` + * - llvm/unittests/IR + - `35` + - `7` + - `28` + - :part:`20%` + * - llvm/unittests/LineEditor + - `1` + - `0` + - `1` + - :none:`0%` + * - llvm/unittests/Linker + - `1` + - `0` + - `1` + - :none:`0%` + * - llvm/unittests/MC + - `5` + - `3` + - `2` + - :part:`60%` + * - llvm/unittests/MC/AMDGPU + - `1` + - `1` + - `0` + - :good:`100%` + * - llvm/unittests/MI + - `1` + - `0` + - `1` + - :none:`0%` + * - llvm/unittests/Object + - `7` + - `6` + - `1` + - :part:`85%` + * - llvm/unittests/ObjectYAML + - `4` + - `2` + - `2` + - :part:`50%` + * - llvm/unittests/Option + - `1` + - `0` + - `1` + - :none:`0%` + * - llvm/unittests/Passes + - `3` + - `3` + - `0` + - :good:`100%` + * - llvm/unittests/ProfileData + - `3` + - `0` + - `3` + - :none:`0%` + * - llvm/unittests/Remarks + - `8` + - `5` + - `3` + - :part:`62%` + * - llvm/unittests/Support + - `86` + - `21` + - `65` + - :part:`24%` + * - llvm/unittests/Support/DynamicLibrary + - `4` + - `0` + - `4` + - :none:`0%` + * - llvm/unittests/TableGen + - `2` + - `0` + - `2` + - :none:`0%` + * - llvm/unittests/Target/AArch64 + - `2` + - `1` + - `1` + - :part:`50%` + * - llvm/unittests/Target/AMDGPU + - `1` + - `1` + - `0` + - :good:`100%` + * - llvm/unittests/Target/ARM + - `1` + - `0` + - `1` + - :none:`0%` + * - llvm/unittests/Target/PowerPC + - `1` + - `1` + - `0` + - :good:`100%` + * - llvm/unittests/Target/WebAssembly + - `1` + - `0` + - `1` + - :none:`0%` + * - llvm/unittests/Target/X86 + - `1` + - `0` + - `1` + - :none:`0%` + * - llvm/unittests/TextAPI + - `6` + - `3` + - `3` + - :part:`50%` + * - llvm/unittests/tools/llvm-cfi-verify + - `2` + - `1` + - `1` + - :part:`50%` + * - llvm/unittests/tools/llvm-exegesis + - `5` + - `4` + - `1` + - :part:`80%` + * - llvm/unittests/tools/llvm-exegesis/AArch64 + - `1` + - `1` + - `0` + - :good:`100%` + * - llvm/unittests/tools/llvm-exegesis/ARM + - `1` + - `1` + - `0` + - :good:`100%` + * - llvm/unittests/tools/llvm-exegesis/Common + - `1` + - `1` + - `0` + - :good:`100%` + * - llvm/unittests/tools/llvm-exegesis/Mips + - `5` + - `4` + - `1` + - :part:`80%` + * - llvm/unittests/tools/llvm-exegesis/PowerPC + - `2` + - `0` + - `2` + - :none:`0%` + * - llvm/unittests/tools/llvm-exegesis/X86 + - `9` + - `8` + - `1` + - :part:`88%` + * - llvm/unittests/Transforms/IPO + - `2` + - `0` + - `2` + - :none:`0%` + * - llvm/unittests/Transforms/Scalar + - `2` + - `0` + - `2` + - :none:`0%` + * - llvm/unittests/Transforms/Utils + - `17` + - `7` + - `10` + - :part:`41%` + * - llvm/unittests/Transforms/Vectorize + - `7` + - `7` + - `0` + - :good:`100%` + * - llvm/unittests/XRay + - `8` + - `7` + - `1` + - :part:`87%` + * - llvm/utils/benchmark/cmake + - `5` + - `3` + - `2` + - :part:`60%` + * - llvm/utils/benchmark/include/benchmark + - `1` + - `0` + - `1` + - :none:`0%` + * - llvm/utils/benchmark/src + - `19` + - `0` + - `19` + - :none:`0%` + * - llvm/utils/FileCheck + - `1` + - `0` + - `1` + - :none:`0%` + * - llvm/utils/fpcmp + - `1` + - `0` + - `1` + - :none:`0%` + * - llvm/utils/KillTheDoctor + - `1` + - `0` + - `1` + - :none:`0%` + * - llvm/utils/not + - `1` + - `1` + - `0` + - :good:`100%` + * - llvm/utils/PerfectShuffle + - `1` + - `0` + - `1` + - :none:`0%` + * - llvm/utils/TableGen + - `74` + - `8` + - `66` + - :part:`10%` + * - llvm/utils/TableGen/GlobalISel + - `17` + - `8` + - `9` + - :part:`47%` + * - llvm/utils/unittest/googlemock/include/gmock + - `11` + - `0` + - `11` + - :none:`0%` + * - llvm/utils/unittest/googlemock/include/gmock/internal + - `3` + - `0` + - `3` + - :none:`0%` + * - llvm/utils/unittest/googlemock/include/gmock/internal/custom + - `3` + - `0` + - `3` + - :none:`0%` + * - llvm/utils/unittest/googletest/include/gtest + - `10` + - `0` + - `10` + - :none:`0%` + * - llvm/utils/unittest/googletest/include/gtest/internal + - `11` + - `0` + - `11` + - :none:`0%` + * - llvm/utils/unittest/googletest/include/gtest/internal/custom + - `4` + - `0` + - `4` + - :none:`0%` + * - llvm/utils/unittest/googletest/src + - `1` + - `0` + - `1` + - :none:`0%` + * - llvm/utils/unittest/UnitTestMain + - `1` + - `0` + - `1` + - :none:`0%` + * - llvm/utils/yaml-bench + - `1` + - `0` + - `1` + - :none:`0%` + * - mlir/examples/standalone/include/Standalone + - `2` + - `2` + - `0` + - :good:`100%` + * - mlir/examples/standalone/lib/Standalone + - `2` + - `2` + - `0` + - :good:`100%` + * - mlir/examples/standalone/standalone-opt + - `1` + - `1` + - `0` + - :good:`100%` + * - mlir/examples/standalone/standalone-translate + - `1` + - `1` + - `0` + - :good:`100%` + * - mlir/examples/toy/Ch1 + - `1` + - `1` + - `0` + - :good:`100%` + * - mlir/examples/toy/Ch1/include/toy + - `3` + - `3` + - `0` + - :good:`100%` + * - mlir/examples/toy/Ch1/parser + - `1` + - `0` + - `1` + - :none:`0%` + * - mlir/examples/toy/Ch2 + - `1` + - `1` + - `0` + - :good:`100%` + * - mlir/examples/toy/Ch2/include/toy + - `5` + - `5` + - `0` + - :good:`100%` + * - mlir/examples/toy/Ch2/mlir + - `2` + - `2` + - `0` + - :good:`100%` + * - mlir/examples/toy/Ch2/parser + - `1` + - `0` + - `1` + - :none:`0%` + * - mlir/examples/toy/Ch3 + - `1` + - `1` + - `0` + - :good:`100%` + * - mlir/examples/toy/Ch3/include/toy + - `5` + - `5` + - `0` + - :good:`100%` + * - mlir/examples/toy/Ch3/mlir + - `3` + - `3` + - `0` + - :good:`100%` + * - mlir/examples/toy/Ch3/parser + - `1` + - `0` + - `1` + - :none:`0%` + * - mlir/examples/toy/Ch4 + - `1` + - `1` + - `0` + - :good:`100%` + * - mlir/examples/toy/Ch4/include/toy + - `7` + - `7` + - `0` + - :good:`100%` + * - mlir/examples/toy/Ch4/mlir + - `4` + - `4` + - `0` + - :good:`100%` + * - mlir/examples/toy/Ch4/parser + - `1` + - `0` + - `1` + - :none:`0%` + * - mlir/examples/toy/Ch5 + - `1` + - `1` + - `0` + - :good:`100%` + * - mlir/examples/toy/Ch5/include/toy + - `7` + - `7` + - `0` + - :good:`100%` + * - mlir/examples/toy/Ch5/mlir + - `5` + - `4` + - `1` + - :part:`80%` + * - mlir/examples/toy/Ch5/parser + - `1` + - `0` + - `1` + - :none:`0%` + * - mlir/examples/toy/Ch6 + - `1` + - `1` + - `0` + - :good:`100%` + * - mlir/examples/toy/Ch6/include/toy + - `7` + - `7` + - `0` + - :good:`100%` + * - mlir/examples/toy/Ch6/mlir + - `6` + - `5` + - `1` + - :part:`83%` + * - mlir/examples/toy/Ch6/parser + - `1` + - `0` + - `1` + - :none:`0%` + * - mlir/examples/toy/Ch7 + - `1` + - `1` + - `0` + - :good:`100%` + * - mlir/examples/toy/Ch7/include/toy + - `7` + - `7` + - `0` + - :good:`100%` + * - mlir/examples/toy/Ch7/mlir + - `6` + - `5` + - `1` + - :part:`83%` + * - mlir/examples/toy/Ch7/parser + - `1` + - `0` + - `1` + - :none:`0%` + * - mlir/include/mlir + - `5` + - `5` + - `0` + - :good:`100%` + * - mlir/include/mlir/Analysis + - `8` + - `7` + - `1` + - :part:`87%` + * - mlir/include/mlir/Conversion/AffineToStandard + - `1` + - `1` + - `0` + - :good:`100%` + * - mlir/include/mlir/Conversion/AVX512ToLLVM + - `1` + - `0` + - `1` + - :none:`0%` + * - mlir/include/mlir/Conversion/GPUCommon + - `1` + - `1` + - `0` + - :good:`100%` + * - mlir/include/mlir/Conversion/GPUToNVVM + - `1` + - `0` + - `1` + - :none:`0%` + * - mlir/include/mlir/Conversion/GPUToROCDL + - `1` + - `1` + - `0` + - :good:`100%` + * - mlir/include/mlir/Conversion/GPUToSPIRV + - `2` + - `1` + - `1` + - :part:`50%` + * - mlir/include/mlir/Conversion/GPUToVulkan + - `1` + - `0` + - `1` + - :none:`0%` + * - mlir/include/mlir/Conversion/LinalgToLLVM + - `1` + - `0` + - `1` + - :none:`0%` + * - mlir/include/mlir/Conversion/LinalgToSPIRV + - `2` + - `2` + - `0` + - :good:`100%` + * - mlir/include/mlir/Conversion/LinalgToStandard + - `1` + - `1` + - `0` + - :good:`100%` + * - mlir/include/mlir/Conversion/SCFToGPU + - `2` + - `2` + - `0` + - :good:`100%` + * - mlir/include/mlir/Conversion/SCFToStandard + - `1` + - `1` + - `0` + - :good:`100%` + * - mlir/include/mlir/Conversion/StandardToLLVM + - `2` + - `1` + - `1` + - :part:`50%` + * - mlir/include/mlir/Conversion/StandardToSPIRV + - `2` + - `2` + - `0` + - :good:`100%` + * - mlir/include/mlir/Conversion/VectorToLLVM + - `1` + - `0` + - `1` + - :none:`0%` + * - mlir/include/mlir/Conversion/VectorToSCF + - `1` + - `1` + - `0` + - :good:`100%` + * - mlir/include/mlir/Dialect + - `2` + - `2` + - `0` + - :good:`100%` + * - mlir/include/mlir/Dialect/Affine + - `2` + - `1` + - `1` + - :part:`50%` + * - mlir/include/mlir/Dialect/Affine/EDSC + - `2` + - `2` + - `0` + - :good:`100%` + * - mlir/include/mlir/Dialect/Affine/IR + - `3` + - `3` + - `0` + - :good:`100%` + * - mlir/include/mlir/Dialect/AVX512 + - `1` + - `1` + - `0` + - :good:`100%` + * - mlir/include/mlir/Dialect/GPU + - `5` + - `4` + - `1` + - :part:`80%` + * - mlir/include/mlir/Dialect/Linalg + - `1` + - `0` + - `1` + - :none:`0%` + * - mlir/include/mlir/Dialect/Linalg/Analysis + - `1` + - `1` + - `0` + - :good:`100%` + * - mlir/include/mlir/Dialect/Linalg/EDSC + - `3` + - `2` + - `1` + - :part:`66%` + * - mlir/include/mlir/Dialect/Linalg/IR + - `3` + - `2` + - `1` + - :part:`66%` + * - mlir/include/mlir/Dialect/Linalg/Transforms + - `1` + - `1` + - `0` + - :good:`100%` + * - mlir/include/mlir/Dialect/Linalg/Utils + - `1` + - `1` + - `0` + - :good:`100%` + * - mlir/include/mlir/Dialect/LLVMIR + - `4` + - `4` + - `0` + - :good:`100%` + * - mlir/include/mlir/Dialect/LLVMIR/Transforms + - `1` + - `1` + - `0` + - :good:`100%` + * - mlir/include/mlir/Dialect/OpenMP + - `1` + - `1` + - `0` + - :good:`100%` + * - mlir/include/mlir/Dialect/Quant + - `6` + - `5` + - `1` + - :part:`83%` + * - mlir/include/mlir/Dialect/SCF + - `3` + - `3` + - `0` + - :good:`100%` + * - mlir/include/mlir/Dialect/SCF/EDSC + - `2` + - `2` + - `0` + - :good:`100%` + * - mlir/include/mlir/Dialect/SDBM + - `3` + - `2` + - `1` + - :part:`66%` + * - mlir/include/mlir/Dialect/Shape/IR + - `1` + - `1` + - `0` + - :good:`100%` + * - mlir/include/mlir/Dialect/SPIRV + - `11` + - `11` + - `0` + - :good:`100%` + * - mlir/include/mlir/Dialect/StandardOps/EDSC + - `2` + - `2` + - `0` + - :good:`100%` + * - mlir/include/mlir/Dialect/StandardOps/IR + - `1` + - `1` + - `0` + - :good:`100%` + * - mlir/include/mlir/Dialect/StandardOps/Transforms + - `2` + - `2` + - `0` + - :good:`100%` + * - mlir/include/mlir/Dialect/Utils + - `1` + - `1` + - `0` + - :good:`100%` + * - mlir/include/mlir/Dialect/Vector + - `3` + - `3` + - `0` + - :good:`100%` + * - mlir/include/mlir/Dialect/Vector/EDSC + - `2` + - `2` + - `0` + - :good:`100%` + * - mlir/include/mlir/EDSC + - `1` + - `1` + - `0` + - :good:`100%` + * - mlir/include/mlir/ExecutionEngine + - `5` + - `2` + - `3` + - :part:`40%` + * - mlir/include/mlir/Interfaces + - `7` + - `6` + - `1` + - :part:`85%` + * - mlir/include/mlir/IR + - `42` + - `9` + - `33` + - :part:`21%` + * - mlir/include/mlir/Pass + - `6` + - `0` + - `6` + - :none:`0%` + * - mlir/include/mlir/Support + - `9` + - `5` + - `4` + - :part:`55%` + * - mlir/include/mlir/TableGen + - `18` + - `17` + - `1` + - :part:`94%` + * - mlir/include/mlir/Target + - `3` + - `3` + - `0` + - :good:`100%` + * - mlir/include/mlir/Target/LLVMIR + - `1` + - `1` + - `0` + - :good:`100%` + * - mlir/include/mlir/Transforms + - `12` + - `7` + - `5` + - :part:`58%` + * - mlir/include/mlir-c + - `1` + - `1` + - `0` + - :good:`100%` + * - mlir/lib/Analysis + - `8` + - `7` + - `1` + - :part:`87%` + * - mlir/lib/Conversion + - `1` + - `1` + - `0` + - :good:`100%` + * - mlir/lib/Conversion/AffineToStandard + - `1` + - `0` + - `1` + - :none:`0%` + * - mlir/lib/Conversion/AVX512ToLLVM + - `1` + - `1` + - `0` + - :good:`100%` + * - mlir/lib/Conversion/GPUCommon + - `5` + - `5` + - `0` + - :good:`100%` + * - mlir/lib/Conversion/GPUToNVVM + - `1` + - `0` + - `1` + - :none:`0%` + * - mlir/lib/Conversion/GPUToROCDL + - `1` + - `1` + - `0` + - :good:`100%` + * - mlir/lib/Conversion/GPUToSPIRV + - `2` + - `2` + - `0` + - :good:`100%` + * - mlir/lib/Conversion/GPUToVulkan + - `2` + - `2` + - `0` + - :good:`100%` + * - mlir/lib/Conversion/LinalgToLLVM + - `1` + - `1` + - `0` + - :good:`100%` + * - mlir/lib/Conversion/LinalgToSPIRV + - `2` + - `2` + - `0` + - :good:`100%` + * - mlir/lib/Conversion/LinalgToStandard + - `1` + - `1` + - `0` + - :good:`100%` + * - mlir/lib/Conversion/SCFToGPU + - `2` + - `2` + - `0` + - :good:`100%` + * - mlir/lib/Conversion/SCFToStandard + - `1` + - `1` + - `0` + - :good:`100%` + * - mlir/lib/Conversion/StandardToLLVM + - `1` + - `1` + - `0` + - :good:`100%` + * - mlir/lib/Conversion/StandardToSPIRV + - `3` + - `3` + - `0` + - :good:`100%` + * - mlir/lib/Conversion/VectorToLLVM + - `1` + - `1` + - `0` + - :good:`100%` + * - mlir/lib/Conversion/VectorToSCF + - `1` + - `1` + - `0` + - :good:`100%` + * - mlir/lib/Dialect + - `1` + - `1` + - `0` + - :good:`100%` + * - mlir/lib/Dialect/Affine/EDSC + - `1` + - `1` + - `0` + - :good:`100%` + * - mlir/lib/Dialect/Affine/IR + - `3` + - `3` + - `0` + - :good:`100%` + * - mlir/lib/Dialect/Affine/Transforms + - `8` + - `8` + - `0` + - :good:`100%` + * - mlir/lib/Dialect/Affine/Utils + - `1` + - `1` + - `0` + - :good:`100%` + * - mlir/lib/Dialect/AVX512/IR + - `1` + - `0` + - `1` + - :none:`0%` + * - mlir/lib/Dialect/GPU/IR + - `1` + - `0` + - `1` + - :none:`0%` + * - mlir/lib/Dialect/GPU/Transforms + - `5` + - `4` + - `1` + - :part:`80%` + * - mlir/lib/Dialect/Linalg/Analysis + - `1` + - `0` + - `1` + - :none:`0%` + * - mlir/lib/Dialect/Linalg/EDSC + - `1` + - `1` + - `0` + - :good:`100%` + * - mlir/lib/Dialect/Linalg/IR + - `2` + - `2` + - `0` + - :good:`100%` + * - mlir/lib/Dialect/Linalg/Transforms + - `10` + - `9` + - `1` + - :part:`90%` + * - mlir/lib/Dialect/Linalg/Utils + - `1` + - `1` + - `0` + - :good:`100%` + * - mlir/lib/Dialect/LLVMIR/IR + - `4` + - `1` + - `3` + - :part:`25%` + * - mlir/lib/Dialect/LLVMIR/Transforms + - `2` + - `2` + - `0` + - :good:`100%` + * - mlir/lib/Dialect/OpenMP/IR + - `1` + - `1` + - `0` + - :good:`100%` + * - mlir/lib/Dialect/Quant/IR + - `4` + - `4` + - `0` + - :good:`100%` + * - mlir/lib/Dialect/Quant/Transforms + - `3` + - `3` + - `0` + - :good:`100%` + * - mlir/lib/Dialect/Quant/Utils + - `3` + - `3` + - `0` + - :good:`100%` + * - mlir/lib/Dialect/SCF + - `1` + - `1` + - `0` + - :good:`100%` + * - mlir/lib/Dialect/SCF/EDSC + - `1` + - `1` + - `0` + - :good:`100%` + * - mlir/lib/Dialect/SCF/Transforms + - `4` + - `4` + - `0` + - :good:`100%` + * - mlir/lib/Dialect/SDBM + - `4` + - `4` + - `0` + - :good:`100%` + * - mlir/lib/Dialect/Shape/IR + - `1` + - `1` + - `0` + - :good:`100%` + * - mlir/lib/Dialect/SPIRV + - `8` + - `5` + - `3` + - :part:`62%` + * - mlir/lib/Dialect/SPIRV/Serialization + - `4` + - `2` + - `2` + - :part:`50%` + * - mlir/lib/Dialect/SPIRV/Transforms + - `4` + - `4` + - `0` + - :good:`100%` + * - mlir/lib/Dialect/StandardOps/EDSC + - `2` + - `2` + - `0` + - :good:`100%` + * - mlir/lib/Dialect/StandardOps/IR + - `1` + - `1` + - `0` + - :good:`100%` + * - mlir/lib/Dialect/StandardOps/Transforms + - `3` + - `3` + - `0` + - :good:`100%` + * - mlir/lib/Dialect/Vector + - `3` + - `2` + - `1` + - :part:`66%` + * - mlir/lib/Dialect/Vector/EDSC + - `1` + - `1` + - `0` + - :good:`100%` + * - mlir/lib/EDSC + - `2` + - `2` + - `0` + - :good:`100%` + * - mlir/lib/ExecutionEngine + - `5` + - `5` + - `0` + - :good:`100%` + * - mlir/lib/Interfaces + - `7` + - `7` + - `0` + - :good:`100%` + * - mlir/lib/IR + - `32` + - `32` + - `0` + - :good:`100%` + * - mlir/lib/Parser + - `5` + - `4` + - `1` + - :part:`80%` + * - mlir/lib/Pass + - `7` + - `6` + - `1` + - :part:`85%` + * - mlir/lib/Support + - `4` + - `4` + - `0` + - :good:`100%` + * - mlir/lib/TableGen + - `16` + - `16` + - `0` + - :good:`100%` + * - mlir/lib/Target/LLVMIR + - `8` + - `8` + - `0` + - :good:`100%` + * - mlir/lib/Transforms + - `19` + - `17` + - `2` + - :part:`89%` + * - mlir/lib/Transforms/Utils + - `7` + - `7` + - `0` + - :good:`100%` + * - mlir/lib/Translation + - `1` + - `1` + - `0` + - :good:`100%` + * - mlir/tools/mlir-cpu-runner + - `1` + - `1` + - `0` + - :good:`100%` + * - mlir/tools/mlir-cuda-runner + - `2` + - `2` + - `0` + - :good:`100%` + * - mlir/tools/mlir-linalg-ods-gen + - `1` + - `1` + - `0` + - :good:`100%` + * - mlir/tools/mlir-opt + - `1` + - `0` + - `1` + - :none:`0%` + * - mlir/tools/mlir-shlib + - `1` + - `1` + - `0` + - :good:`100%` + * - mlir/tools/mlir-tblgen + - `16` + - `14` + - `2` + - :part:`87%` + * - mlir/tools/mlir-translate + - `1` + - `1` + - `0` + - :good:`100%` + * - mlir/tools/mlir-vulkan-runner + - `4` + - `4` + - `0` + - :good:`100%` + * - mlir/unittests/Dialect + - `1` + - `1` + - `0` + - :good:`100%` + * - mlir/unittests/Dialect/Quant + - `1` + - `1` + - `0` + - :good:`100%` + * - mlir/unittests/Dialect/SPIRV + - `2` + - `2` + - `0` + - :good:`100%` + * - mlir/unittests/IR + - `3` + - `3` + - `0` + - :good:`100%` + * - mlir/unittests/Pass + - `1` + - `1` + - `0` + - :good:`100%` + * - mlir/unittests/SDBM + - `1` + - `1` + - `0` + - :good:`100%` + * - mlir/unittests/TableGen + - `3` + - `3` + - `0` + - :good:`100%` + * - openmp/libomptarget/deviceRTLs + - `1` + - `0` + - `1` + - :none:`0%` + * - openmp/libomptarget/deviceRTLs/amdgcn/src + - `3` + - `3` + - `0` + - :good:`100%` + * - openmp/libomptarget/deviceRTLs/common + - `8` + - `4` + - `4` + - :part:`50%` + * - openmp/libomptarget/deviceRTLs/nvptx/src + - `2` + - `1` + - `1` + - :part:`50%` + * - openmp/libomptarget/include + - `2` + - `1` + - `1` + - :part:`50%` + * - openmp/libomptarget/plugins/cuda/src + - `1` + - `0` + - `1` + - :none:`0%` + * - openmp/libomptarget/plugins/generic-elf-64bit/src + - `1` + - `0` + - `1` + - :none:`0%` + * - openmp/libomptarget/plugins/ve/src + - `1` + - `0` + - `1` + - :none:`0%` + * - openmp/libomptarget/src + - `8` + - `0` + - `8` + - :none:`0%` + * - openmp/runtime/doc/doxygen + - `1` + - `0` + - `1` + - :none:`0%` + * - openmp/runtime/src + - `74` + - `37` + - `37` + - :part:`50%` + * - openmp/runtime/src/thirdparty/ittnotify + - `6` + - `0` + - `6` + - :none:`0%` + * - openmp/runtime/src/thirdparty/ittnotify/legacy + - `1` + - `0` + - `1` + - :none:`0%` + * - openmp/tools/archer + - `1` + - `0` + - `1` + - :none:`0%` + * - openmp/tools/archer/tests/ompt + - `1` + - `1` + - `0` + - :good:`100%` + * - parallel-libs/acxxel + - `6` + - `4` + - `2` + - :part:`66%` + * - parallel-libs/acxxel/examples + - `1` + - `1` + - `0` + - :good:`100%` + * - parallel-libs/acxxel/tests + - `5` + - `4` + - `1` + - :part:`80%` + * - polly/include/polly + - `22` + - `22` + - `0` + - :good:`100%` + * - polly/include/polly/CodeGen + - `14` + - `14` + - `0` + - :good:`100%` + * - polly/include/polly/Support + - `11` + - `11` + - `0` + - :good:`100%` + * - polly/lib/Analysis + - `9` + - `9` + - `0` + - :good:`100%` + * - polly/lib/CodeGen + - `15` + - `15` + - `0` + - :good:`100%` + * - polly/lib/Exchange + - `1` + - `1` + - `0` + - :good:`100%` + * - polly/lib/External/isl + - `67` + - `1` + - `66` + - :part:`1%` + * - polly/lib/External/isl/imath + - `3` + - `0` + - `3` + - :none:`0%` + * - polly/lib/External/isl/imath_wrap + - `4` + - `0` + - `4` + - :none:`0%` + * - polly/lib/External/isl/include/isl + - `62` + - `8` + - `54` + - :part:`12%` + * - polly/lib/External/isl/interface + - `5` + - `1` + - `4` + - :part:`20%` + * - polly/lib/External/pet/include + - `1` + - `0` + - `1` + - :none:`0%` + * - polly/lib/External/ppcg + - `17` + - `0` + - `17` + - :none:`0%` + * - polly/lib/Plugin + - `1` + - `1` + - `0` + - :good:`100%` + * - polly/lib/Support + - `10` + - `10` + - `0` + - :good:`100%` + * - polly/lib/Transform + - `14` + - `14` + - `0` + - :good:`100%` + * - polly/tools/GPURuntime + - `1` + - `1` + - `0` + - :good:`100%` + * - polly/unittests/DeLICM + - `1` + - `1` + - `0` + - :good:`100%` + * - polly/unittests/Flatten + - `1` + - `1` + - `0` + - :good:`100%` + * - polly/unittests/Isl + - `1` + - `1` + - `0` + - :good:`100%` + * - polly/unittests/ScheduleOptimizer + - `1` + - `1` + - `0` + - :good:`100%` + * - polly/unittests/ScopPassManager + - `1` + - `1` + - `0` + - :good:`100%` + * - polly/unittests/Support + - `1` + - `1` + - `0` + - :good:`100%` + * - pstl/include/pstl/internal + - `22` + - `18` + - `4` + - :part:`81%` + * - Total + - :total:`13035` + - :total:`5791` + - :total:`7244` + - :total:`44%` diff --git a/clang/docs/CommandGuide/clang.rst b/clang/docs/CommandGuide/clang.rst index 6947450beb43d..de0e0eda90974 100644 --- a/clang/docs/CommandGuide/clang.rst +++ b/clang/docs/CommandGuide/clang.rst @@ -246,7 +246,9 @@ Language Selection and Mode Options .. option:: -ffreestanding Indicate that the file should be compiled for a freestanding, not a hosted, - environment. + environment. Note that it is assumed that a freestanding environment will + additionally provide `memcpy`, `memmove`, `memset` and `memcmp` + implementations, as these are needed for efficient codegen for many programs. .. option:: -fno-builtin diff --git a/clang/docs/LibASTMatchersReference.html b/clang/docs/LibASTMatchersReference.html index f57352389e4ce..bb5e4984fcdf4 100644 --- a/clang/docs/LibASTMatchersReference.html +++ b/clang/docs/LibASTMatchersReference.html @@ -2175,6 +2175,75 @@

Narrowing Matchers

+Matcher<CXXBaseSpecifier>isPrivate +
Matches private C++ declarations and C++ base specifers that specify private
+inheritance.
+
+Examples:
+  class C {
+  public:    int a;
+  protected: int b;
+  private:   int c; // fieldDecl(isPrivate()) matches 'c'
+  };
+
+  struct Base {};
+  struct Derived1 : private Base {}; // matches 'Base'
+  class Derived2 : Base {}; // matches 'Base'
+
+ + +Matcher<CXXBaseSpecifier>isProtected +
Matches protected C++ declarations and C++ base specifers that specify
+protected inheritance.
+
+Examples:
+  class C {
+  public:    int a;
+  protected: int b; // fieldDecl(isProtected()) matches 'b'
+  private:   int c;
+  };
+
+  class Base {};
+  class Derived : protected Base {}; // matches 'Base'
+
+ + +Matcher<CXXBaseSpecifier>isPublic +
Matches public C++ declarations and C++ base specifers that specify public
+inheritance.
+
+Examples:
+  class C {
+  public:    int a; // fieldDecl(isPublic()) matches 'a'
+  protected: int b;
+  private:   int c;
+  };
+
+  class Base {};
+  class Derived1 : public Base {}; // matches 'Base'
+  struct Derived2 : Base {}; // matches 'Base'
+
+ + +Matcher<CXXBaseSpecifier>isVirtual +
Matches declarations of virtual methods and C++ base specifers that specify
+virtual inheritance.
+
+Example:
+  class A {
+   public:
+    virtual void x(); // matches x
+  };
+
+Example:
+  class Base {};
+  class DirectlyDerived : virtual Base {}; // matches Base
+  class IndirectlyDerived : DirectlyDerived, Base {}; // matches Base
+
+Usable as: Matcher<CXXMethodDecl>, Matcher<CXXBaseSpecifier>
+
+ + Matcher<CXXBoolLiteralExpr>equalsbool Value

 
@@ -2562,14 +2631,21 @@ 

Narrowing Matchers

Matcher<CXXMethodDecl>isVirtual -
Matches if the given method declaration is virtual.
+
Matches declarations of virtual methods and C++ base specifers that specify
+virtual inheritance.
 
-Given
+Example:
   class A {
    public:
-    virtual void x();
+    virtual void x(); // matches x
   };
-  matches A::x
+
+Example:
+  class Base {};
+  class DirectlyDerived : virtual Base {}; // matches Base
+  class IndirectlyDerived : DirectlyDerived, Base {}; // matches Base
+
+Usable as: Matcher<CXXMethodDecl>, Matcher<CXXBaseSpecifier>
 
@@ -3012,44 +3088,52 @@

Narrowing Matchers

Matcher<Decl>isPrivate -
Matches private C++ declarations.
+
Matches private C++ declarations and C++ base specifers that specify private
+inheritance.
 
-Given
+Examples:
   class C {
   public:    int a;
   protected: int b;
-  private:   int c;
+  private:   int c; // fieldDecl(isPrivate()) matches 'c'
   };
-fieldDecl(isPrivate())
-  matches 'int c;'
+
+  struct Base {};
+  struct Derived1 : private Base {}; // matches 'Base'
+  class Derived2 : Base {}; // matches 'Base'
 
Matcher<Decl>isProtected -
Matches protected C++ declarations.
+
Matches protected C++ declarations and C++ base specifers that specify
+protected inheritance.
 
-Given
+Examples:
   class C {
   public:    int a;
-  protected: int b;
+  protected: int b; // fieldDecl(isProtected()) matches 'b'
   private:   int c;
   };
-fieldDecl(isProtected())
-  matches 'int b;'
+
+  class Base {};
+  class Derived : protected Base {}; // matches 'Base'
 
Matcher<Decl>isPublic -
Matches public C++ declarations.
+
Matches public C++ declarations and C++ base specifers that specify public
+inheritance.
 
-Given
+Examples:
   class C {
-  public:    int a;
+  public:    int a; // fieldDecl(isPublic()) matches 'a'
   protected: int b;
   private:   int c;
   };
-fieldDecl(isPublic())
-  matches 'int a;'
+
+  class Base {};
+  class Derived1 : public Base {}; // matches 'Base'
+  struct Derived2 : Base {}; // matches 'Base'
 
@@ -4671,6 +4755,23 @@

Narrowing Matchers

Usable as: Matcher<FunctionDecl>, Matcher<VarDecl>, Matcher<CXXRecordDecl>
+ +Matcher<clang::ParmVarDecl>isAtPositionunsigned N +
Matches the ParmVarDecl nodes that are at the N'th position in the parameter
+list. The parameter list could be that of either a block, function, or
+objc-method.
+
+
+Given
+
+void f(int a, int b, int c) {
+}
+
+``parmVarDecl(isAtPosition(0))`` matches ``int a``.
+
+``parmVarDecl(isAtPosition(1))`` matches ``int b``.
+
+ @@ -5118,6 +5219,33 @@

AST Traversal Matchers

+Matcher<CXXBaseSpecifier>hasTypeMatcher<Decl> InnerMatcher +
Overloaded to match the declaration of the expression's or value
+declaration's type.
+
+In case of a value declaration (for example a variable declaration),
+this resolves one layer of indirection. For example, in the value
+declaration "X x;", cxxRecordDecl(hasName("X")) matches the declaration of
+X, while varDecl(hasType(cxxRecordDecl(hasName("X")))) matches the
+declaration of x.
+
+Example matches x (matcher = expr(hasType(cxxRecordDecl(hasName("X")))))
+            and z (matcher = varDecl(hasType(cxxRecordDecl(hasName("X")))))
+            and friend class X (matcher = friendDecl(hasType("X"))
+ class X {};
+ void y(X &x) { x; X z; }
+ class Y { friend class X; };
+
+Example matches class Derived
+(matcher = cxxRecordDecl(hasAnyBase(hasType(cxxRecordDecl(hasName("Base"))))))
+class Base {};
+class Derived : Base {};
+
+Usable as: Matcher<Expr>, Matcher<FriendDecl>, Matcher<ValueDecl>,
+Matcher<CXXBaseSpecifier>
+
+ + Matcher<CXXConstructExpr>forEachArgumentWithParamMatcher<Expr> ArgMatcher, Matcher<ParmVarDecl> ParamMatcher
Matches all arguments and their respective ParmVarDecl.
 
@@ -5501,6 +5629,21 @@ 

AST Traversal Matchers

+Matcher<CXXRecordDecl>hasAnyBaseMatcher<CXXBaseSpecifier> BaseSpecMatcher +
Matches C++ classes that have a direct or indirect base matching BaseSpecMatcher.
+
+Example matches DirectlyDerived, IndirectlyDerived (BaseSpecMatcher ==
+hasType(cxxRecordDecl(hasName("SpecialBase")))) class Foo;
+  class Bar : Foo {};
+  class Baz : Bar {};
+  class SpecialBase;
+  class DirectlyDerived : SpecialBase {};  // directly derived
+  class IndirectlyDerived : DirectlyDerived {};  // indirectly derived
+
+FIXME: Refactor this and isDerivedFrom to reuse implementation.
+
+ + Matcher<CXXRecordDecl>hasMethodMatcher<CXXMethodDecl> InnerMatcher
Matches the first method of a class or struct that satisfies InnerMatcher.
 
@@ -6058,7 +6201,13 @@ 

AST Traversal Matchers

void y(X &x) { x; X z; } class Y { friend class X; }; -Usable as: Matcher<Expr>, Matcher<ValueDecl> +Example matches class Derived +(matcher = cxxRecordDecl(hasAnyBase(hasType(cxxRecordDecl(hasName("Base")))))) +class Base {}; +class Derived : Base {}; + +Usable as: Matcher<Expr>, Matcher<FriendDecl>, Matcher<ValueDecl>, +Matcher<CXXBaseSpecifier>
@@ -6272,7 +6421,13 @@

AST Traversal Matchers

void y(X &x) { x; X z; } class Y { friend class X; }; -Usable as: Matcher<Expr>, Matcher<ValueDecl> +Example matches class Derived +(matcher = cxxRecordDecl(hasAnyBase(hasType(cxxRecordDecl(hasName("Base")))))) +class Base {}; +class Derived : Base {}; + +Usable as: Matcher<Expr>, Matcher<FriendDecl>, Matcher<ValueDecl>, +Matcher<CXXBaseSpecifier>
@@ -7660,7 +7815,13 @@

AST Traversal Matchers

void y(X &x) { x; X z; } class Y { friend class X; }; -Usable as: Matcher<Expr>, Matcher<ValueDecl> +Example matches class Derived +(matcher = cxxRecordDecl(hasAnyBase(hasType(cxxRecordDecl(hasName("Base")))))) +class Base {}; +class Derived : Base {}; + +Usable as: Matcher<Expr>, Matcher<FriendDecl>, Matcher<ValueDecl>, +Matcher<CXXBaseSpecifier>
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index c38ff0e367902..8f9dc81ec0b30 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -82,6 +82,10 @@ Non-comprehensive list of changes in this release linker. If the user links the program with the ``clang`` or ``clang-cl`` drivers, the driver will pass this flag for them. +- Clang's profile files generated through ``-fprofile-instr-generate`` are using + a fixed hashing algorithm that prevents some collision when loading + out-of-date profile informations. Clang can still read old profile files. + New Compiler Flags ------------------ @@ -255,6 +259,36 @@ AST Matchers uses ``IgnoreUnlessSpelledInSource`` by default. The mode can be changed using ``set traversal AsIs`` in the ``clang-query`` environment. + As this change requires downstream tools which use AST Matchers to adapt + to the new default, a porting guide may be useful for downstream tools + needing to adapt. + + Note that although there are many steps below, only the first is + non-optional. The steps are intentionally extemely granular to facilitate + understanding of the guide itself. It is reasonable to do some of the + steps at the same time if you understand the guide: + + 1. Use ``(your ASTContext instance).getParentMapContext().setTraversalKind(TK_AsIs)`` + to restore the previous behavior for your tool. All further steps in + this porting guide are optional. + 2. Wrap your existing matcher expressions with ``traverse(TK_AsIs, ...)`` + before passing them to ``ASTMatchFinder::addMatcher``. + 3. Remove ``(your ASTContext instance).getParentMapContext().setTraversalKind(TK_AsIs)`` + from your tool so that the default behavior of your tool matches the + default behavior of upstream clang. This is made possible by wrapping + your matchers in ``traverse(TK_AsIs, ...)`` from step (2). + 4. Audit your matcher expressions and remove ``traverse(TK_AsIs, ...)`` + where not needed. + 5. Audit your matcher expressions and remove calls to ``ignoring*()`` + matchers where not needed. + 6. Audit your matcher expressions and consider whether the matcher is + better using the ``TK_AsIs`` mode or if it can be better expressed in + the default mode. For example, some matchers explicitly match + ``has(implicitCastExpr(has(...)))``. Such matchers are sometimes + written by author who were unaware of the existence of the + ``ignoring*()`` matchers. + + clang-format ------------ diff --git a/clang/docs/analyzer/checkers.rst b/clang/docs/analyzer/checkers.rst index dcf1f28994de4..c977dde8c52ff 100644 --- a/clang/docs/analyzer/checkers.rst +++ b/clang/docs/analyzer/checkers.rst @@ -1403,6 +1403,24 @@ Ref-counted types hold their ref-countable data by a raw pointer and allow impli struct Derived : RefCntblBase { }; // warn +.. _webkit-WebKitNoUncountedMemberChecker: + +webkit.WebKitNoUncountedMemberChecker +"""""""""""""""""""""""""""""""""""" +Raw pointers and references to uncounted types can't be used as class members. Only ref-counted types are allowed. + +.. code-block:: cpp + struct RefCntbl { + void ref() {} + void deref() {} + }; + + struct Foo { + RefCntbl * ptr; // warn + RefCntbl & ptr; // warn + // ... + }; + .. _alpha-checkers: Experimental Checkers diff --git a/clang/docs/index.rst b/clang/docs/index.rst index 493f736f2be4f..c49312861baa8 100644 --- a/clang/docs/index.rst +++ b/clang/docs/index.rst @@ -77,6 +77,7 @@ Using Clang Tools ClangCheck ClangFormat ClangFormatStyleOptions + ClangFormattedStatus Design Documents ================ diff --git a/clang/docs/tools/generate_formatted_state.py b/clang/docs/tools/generate_formatted_state.py new file mode 100755 index 0000000000000..1b620a84ac0b4 --- /dev/null +++ b/clang/docs/tools/generate_formatted_state.py @@ -0,0 +1,146 @@ +#!/usr/bin/env python +# A tool to parse creates a document outlining how clang formatted the +# LLVM project is. + +import sys +import os +import subprocess +from datetime import datetime + + +def get_git_revision_short_hash(): + return subprocess.check_output(['git', 'rev-parse', '--short', 'HEAD'] + ).decode(sys.stdout.encoding).strip() + + +def get_style(count, passed): + if passed == count: + return ":good:" + elif passed != 0: + return ":part:" + else: + return ":none:" + + +TOP_DIR = os.path.join(os.path.dirname(__file__), '../../..') +CLANG_DIR = os.path.join(os.path.dirname(__file__), '../..') +DOC_FILE = os.path.join(CLANG_DIR, 'docs/ClangFormattedStatus.rst') + +rootdir = TOP_DIR + +skipped_dirs = [".git", "test"] +suffixes = (".cpp", ".h") + +rst_prefix = """\ +.. raw:: html + + + +.. role:: none +.. role:: part +.. role:: good +.. role:: total + +====================== +Clang Formatted Status +====================== + +:doc:`ClangFormattedStatus` describes the state of LLVM source +tree in terms of conformance to :doc:`ClangFormat` as of: {today} (`{sha} `_). + + +.. list-table:: LLVM Clang-Format Status + :widths: 50 25 25 25 25 + :header-rows: 1\n + * - Directory + - Total Files + - Formatted Files + - Unformatted Files + - % Complete +""" + +table_row = """\ + * - {path} + - {style}`{count}` + - {style}`{passes}` + - {style}`{fails}` + - {style2}`{percent}%` +""" + +with open(DOC_FILE, 'wb') as output: + sha = get_git_revision_short_hash() + today = datetime.now().strftime("%B %d, %Y %H:%M:%S") + output.write(bytes(rst_prefix.format(today=today, + sha=sha).encode("utf-8"))) + + total_files_count = 0 + total_files_pass = 0 + total_files_fail = 0 + for root, subdirs, files in os.walk(rootdir): + for subdir in subdirs: + if any(sd == subdir for sd in skipped_dirs): + subdirs.remove(subdir) + + path = os.path.relpath(root, TOP_DIR) + path = path.replace('\\', '/') + + head, _ = os.path.split(root) + while head: + head, _ = os.path.split(head) + + file_count = 0 + file_pass = 0 + file_fail = 0 + for filename in files: + file_path = os.path.join(root, filename) + ext = os.path.splitext(file_path)[-1].lower() + if not ext.endswith(suffixes): + continue + + file_count += 1 + + args = ["clang-format", "-n", file_path] + cmd = subprocess.Popen(args, stderr=subprocess.PIPE) + stdout, err = cmd.communicate() + + relpath = os.path.relpath(file_path, TOP_DIR) + relpath = relpath.replace('\\', '/') + if err.decode(sys.stdout.encoding).find(': warning:') > 0: + print(relpath, ":", "FAIL") + file_fail += 1 + else: + print(relpath, ":", "PASS") + file_pass += 1 + + total_files_count += file_count + total_files_pass += file_pass + total_files_fail += file_fail + + if file_count > 0: + percent = (int(100.0 * (float(file_pass)/float(file_count)))) + style = get_style(file_count, file_pass) + output.write(bytes(table_row.format(path=path, + count=file_count, + passes=file_pass, + fails=file_fail, + percent=str(percent), style="", + style2=style).encode("utf-8"))) + output.flush() + + print("----\n") + print(path, file_count, file_pass, file_fail, percent) + print("----\n") + + total_percent = (float(total_files_pass)/float(total_files_count)) + percent_str = str(int(100.0 * total_percent)) + output.write(bytes(table_row.format(path="Total", + count=total_files_count, + passes=total_files_pass, + fails=total_files_fail, + percent=percent_str, style=":total:", + style2=":total:").encode("utf-8"))) diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h index 509ada3c96962..a5bb9a34c2fb3 100644 --- a/clang/include/clang/AST/ASTContext.h +++ b/clang/include/clang/AST/ASTContext.h @@ -657,7 +657,7 @@ class ASTContext : public RefCountedBase { /// getRealTypeForBitwidth - /// sets floating point QualTy according to specified bitwidth. /// Returns empty type if there is no appropriate target types. - QualType getRealTypeForBitwidth(unsigned DestWidth) const; + QualType getRealTypeForBitwidth(unsigned DestWidth, bool ExplicitIEEE) const; bool AtomicUsesUnsupportedLibcall(const AtomicExpr *E) const; diff --git a/clang/include/clang/AST/ASTTypeTraits.h b/clang/include/clang/AST/ASTTypeTraits.h index cd80e9bc38084..67fa4ab1b6a4f 100644 --- a/clang/include/clang/AST/ASTTypeTraits.h +++ b/clang/include/clang/AST/ASTTypeTraits.h @@ -16,6 +16,7 @@ #define LLVM_CLANG_AST_ASTTYPETRAITS_H #include "clang/AST/ASTFwd.h" +#include "clang/AST/DeclCXX.h" #include "clang/AST/NestedNameSpecifier.h" #include "clang/AST/TemplateBase.h" #include "clang/AST/TypeLoc.h" @@ -136,6 +137,7 @@ class ASTNodeKind { NKI_QualType, NKI_TypeLoc, NKI_LastKindWithoutPointerIdentity = NKI_TypeLoc, + NKI_CXXBaseSpecifier, NKI_CXXCtorInitializer, NKI_NestedNameSpecifier, NKI_Decl, @@ -198,6 +200,7 @@ KIND_TO_KIND_ID(Decl) KIND_TO_KIND_ID(Stmt) KIND_TO_KIND_ID(Type) KIND_TO_KIND_ID(OMPClause) +KIND_TO_KIND_ID(CXXBaseSpecifier) #define DECL(DERIVED, BASE) KIND_TO_KIND_ID(DERIVED##Decl) #include "clang/AST/DeclNodes.inc" #define STMT(DERIVED, BASE) KIND_TO_KIND_ID(DERIVED) @@ -510,6 +513,10 @@ template <> struct DynTypedNode::BaseConverter< TypeLoc, void> : public ValueConverter {}; +template <> +struct DynTypedNode::BaseConverter + : public PtrConverter {}; + // The only operation we allow on unsupported types is \c get. // This allows to conveniently use \c DynTypedNode when having an arbitrary // AST node that is not supported, but prevents misuse - a user cannot create diff --git a/clang/include/clang/AST/Decl.h b/clang/include/clang/AST/Decl.h index 25dfccceb698c..bda30cb5c9ab5 100644 --- a/clang/include/clang/AST/Decl.h +++ b/clang/include/clang/AST/Decl.h @@ -2126,19 +2126,17 @@ class FunctionDecl : public DeclaratorDecl, bool isTrivialForCall() const { return FunctionDeclBits.IsTrivialForCall; } void setTrivialForCall(bool IT) { FunctionDeclBits.IsTrivialForCall = IT; } - /// Whether this function is defaulted per C++0x. Only valid for - /// special member functions. + /// Whether this function is defaulted. Valid for e.g. + /// special member functions, defaulted comparisions (not methods!). bool isDefaulted() const { return FunctionDeclBits.IsDefaulted; } void setDefaulted(bool D = true) { FunctionDeclBits.IsDefaulted = D; } - /// Whether this function is explicitly defaulted per C++0x. Only valid - /// for special member functions. + /// Whether this function is explicitly defaulted. bool isExplicitlyDefaulted() const { return FunctionDeclBits.IsExplicitlyDefaulted; } - /// State that this function is explicitly defaulted per C++0x. Only valid - /// for special member functions. + /// State that this function is explicitly defaulted. void setExplicitlyDefaulted(bool ED = true) { FunctionDeclBits.IsExplicitlyDefaulted = ED; } diff --git a/clang/include/clang/AST/Expr.h b/clang/include/clang/AST/Expr.h index 0ca4941789e75..deca0b82c4e33 100644 --- a/clang/include/clang/AST/Expr.h +++ b/clang/include/clang/AST/Expr.h @@ -232,6 +232,11 @@ class Expr : public ValueStmt { /// a problem with a generic expression. SourceLocation getExprLoc() const LLVM_READONLY; + /// Determine whether an lvalue-to-rvalue conversion should implicitly be + /// applied to this expression if it appears as a discarded-value expression + /// in C++11 onwards. This applies to certain forms of volatile glvalues. + bool isReadIfDiscardedInCPlusPlus11() const; + /// isUnusedResultAWarning - Return true if this immediate expression should /// be warned about if the result is unused. If so, fill in expr, location, /// and ranges with expr to warn on and source locations/ranges appropriate diff --git a/clang/include/clang/AST/OpenMPClause.h b/clang/include/clang/AST/OpenMPClause.h index 54e83f4619800..91e4d011a3e96 100644 --- a/clang/include/clang/AST/OpenMPClause.h +++ b/clang/include/clang/AST/OpenMPClause.h @@ -6597,6 +6597,110 @@ class OMPUseDevicePtrClause final } }; +/// This represents clause 'use_device_addr' in the '#pragma omp ...' +/// directives. +/// +/// \code +/// #pragma omp target data use_device_addr(a,b) +/// \endcode +/// In this example directive '#pragma omp target data' has clause +/// 'use_device_addr' with the variables 'a' and 'b'. +class OMPUseDeviceAddrClause final + : public OMPMappableExprListClause, + private llvm::TrailingObjects< + OMPUseDeviceAddrClause, Expr *, ValueDecl *, unsigned, + OMPClauseMappableExprCommon::MappableComponent> { + friend class OMPClauseReader; + friend OMPMappableExprListClause; + friend OMPVarListClause; + friend TrailingObjects; + + /// Build clause with number of variables \a NumVars. + /// + /// \param Locs Locations needed to build a mappable clause. It includes 1) + /// StartLoc: starting location of the clause (the clause keyword); 2) + /// LParenLoc: location of '('; 3) EndLoc: ending location of the clause. + /// \param Sizes All required sizes to build a mappable clause. It includes 1) + /// NumVars: number of expressions listed in this clause; 2) + /// NumUniqueDeclarations: number of unique base declarations in this clause; + /// 3) NumComponentLists: number of component lists in this clause; and 4) + /// NumComponents: total number of expression components in the clause. + explicit OMPUseDeviceAddrClause(const OMPVarListLocTy &Locs, + const OMPMappableExprListSizeTy &Sizes) + : OMPMappableExprListClause(llvm::omp::OMPC_use_device_addr, Locs, + Sizes) {} + + /// Build an empty clause. + /// + /// \param Sizes All required sizes to build a mappable clause. It includes 1) + /// NumVars: number of expressions listed in this clause; 2) + /// NumUniqueDeclarations: number of unique base declarations in this clause; + /// 3) NumComponentLists: number of component lists in this clause; and 4) + /// NumComponents: total number of expression components in the clause. + explicit OMPUseDeviceAddrClause(const OMPMappableExprListSizeTy &Sizes) + : OMPMappableExprListClause(llvm::omp::OMPC_use_device_addr, + OMPVarListLocTy(), Sizes) {} + + /// Define the sizes of each trailing object array except the last one. This + /// is required for TrailingObjects to work properly. + size_t numTrailingObjects(OverloadToken) const { + return varlist_size(); + } + size_t numTrailingObjects(OverloadToken) const { + return getUniqueDeclarationsNum(); + } + size_t numTrailingObjects(OverloadToken) const { + return getUniqueDeclarationsNum() + getTotalComponentListNum(); + } + +public: + /// Creates clause with a list of variables \a Vars. + /// + /// \param C AST context. + /// \param Locs Locations needed to build a mappable clause. It includes 1) + /// StartLoc: starting location of the clause (the clause keyword); 2) + /// LParenLoc: location of '('; 3) EndLoc: ending location of the clause. + /// \param Vars The original expression used in the clause. + /// \param Declarations Declarations used in the clause. + /// \param ComponentLists Component lists used in the clause. + static OMPUseDeviceAddrClause * + Create(const ASTContext &C, const OMPVarListLocTy &Locs, + ArrayRef Vars, ArrayRef Declarations, + MappableExprComponentListsRef ComponentLists); + + /// Creates an empty clause with the place for \a NumVars variables. + /// + /// \param C AST context. + /// \param Sizes All required sizes to build a mappable clause. It includes 1) + /// NumVars: number of expressions listed in this clause; 2) + /// NumUniqueDeclarations: number of unique base declarations in this clause; + /// 3) NumComponentLists: number of component lists in this clause; and 4) + /// NumComponents: total number of expression components in the clause. + static OMPUseDeviceAddrClause * + CreateEmpty(const ASTContext &C, const OMPMappableExprListSizeTy &Sizes); + + child_range children() { + return child_range(reinterpret_cast(varlist_begin()), + reinterpret_cast(varlist_end())); + } + + const_child_range children() const { + auto Children = const_cast(this)->children(); + return const_child_range(Children.begin(), Children.end()); + } + + child_range used_children() { + return child_range(child_iterator(), child_iterator()); + } + const_child_range used_children() const { + return const_child_range(const_child_iterator(), const_child_iterator()); + } + + static bool classof(const OMPClause *T) { + return T->getClauseKind() == llvm::omp::OMPC_use_device_addr; + } +}; + /// This represents clause 'is_device_ptr' in the '#pragma omp ...' /// directives. /// diff --git a/clang/include/clang/AST/RecursiveASTVisitor.h b/clang/include/clang/AST/RecursiveASTVisitor.h index a264d1cf24b23..83ff49e405020 100644 --- a/clang/include/clang/AST/RecursiveASTVisitor.h +++ b/clang/include/clang/AST/RecursiveASTVisitor.h @@ -2103,11 +2103,11 @@ bool RecursiveASTVisitor::TraverseFunctionHelper(FunctionDecl *D) { } } - bool VisitBody = D->isThisDeclarationADefinition(); - // If a method is set to default outside the class definition the compiler - // generates the method body and adds it to the AST. - if (const auto *MD = dyn_cast(D)) - VisitBody &= !MD->isDefaulted() || getDerived().shouldVisitImplicitCode(); + bool VisitBody = + D->isThisDeclarationADefinition() && + // Don't visit the function body if the function definition is generated + // by clang. + (!D->isDefaulted() || getDerived().shouldVisitImplicitCode()); if (VisitBody) { TRY_TO(TraverseStmt(D->getBody())); // Function body. @@ -3521,6 +3521,13 @@ bool RecursiveASTVisitor::VisitOMPUseDevicePtrClause( return true; } +template +bool RecursiveASTVisitor::VisitOMPUseDeviceAddrClause( + OMPUseDeviceAddrClause *C) { + TRY_TO(VisitOMPClauseList(C)); + return true; +} + template bool RecursiveASTVisitor::VisitOMPIsDevicePtrClause( OMPIsDevicePtrClause *C) { diff --git a/clang/include/clang/AST/Type.h b/clang/include/clang/AST/Type.h index 85ee1e9b99cf1..a193000aeaf7e 100644 --- a/clang/include/clang/AST/Type.h +++ b/clang/include/clang/AST/Type.h @@ -2055,7 +2055,8 @@ class alignas(8) Type : public ExtQualsTypeCommonBase { bool isComplexIntegerType() const; // GCC _Complex integer type. bool isVectorType() const; // GCC vector type. bool isExtVectorType() const; // Extended vector type. - bool isConstantMatrixType() const; // Matrix type. + bool isMatrixType() const; // Matrix type. + bool isConstantMatrixType() const; // Constant matrix type. bool isDependentAddressSpaceType() const; // value-dependent address space qualifier bool isObjCObjectPointerType() const; // pointer to ObjC object bool isObjCRetainableType() const; // ObjC object or block pointer @@ -6749,6 +6750,10 @@ inline bool Type::isExtVectorType() const { return isa(CanonicalType); } +inline bool Type::isMatrixType() const { + return isa(CanonicalType); +} + inline bool Type::isConstantMatrixType() const { return isa(CanonicalType); } diff --git a/clang/include/clang/ASTMatchers/ASTMatchers.h b/clang/include/clang/ASTMatchers/ASTMatchers.h index a750747c9aa3f..6dbcc01442ed6 100644 --- a/clang/include/clang/ASTMatchers/ASTMatchers.h +++ b/clang/include/clang/ASTMatchers/ASTMatchers.h @@ -47,6 +47,7 @@ #include "clang/AST/ASTContext.h" #include "clang/AST/ASTTypeTraits.h" #include "clang/AST/Attr.h" +#include "clang/AST/CXXInheritance.h" #include "clang/AST/Decl.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/DeclFriend.h" @@ -548,52 +549,72 @@ extern const internal::VariadicDynCastAllOfMatcher templateTypeParmDecl; -/// Matches public C++ declarations. +/// Matches public C++ declarations and C++ base specifers that specify public +/// inheritance. /// -/// Given +/// Examples: /// \code /// class C { -/// public: int a; +/// public: int a; // fieldDecl(isPublic()) matches 'a' /// protected: int b; /// private: int c; /// }; /// \endcode -/// fieldDecl(isPublic()) -/// matches 'int a;' -AST_MATCHER(Decl, isPublic) { - return Node.getAccess() == AS_public; +/// +/// \code +/// class Base {}; +/// class Derived1 : public Base {}; // matches 'Base' +/// struct Derived2 : Base {}; // matches 'Base' +/// \endcode +AST_POLYMORPHIC_MATCHER(isPublic, + AST_POLYMORPHIC_SUPPORTED_TYPES(Decl, + CXXBaseSpecifier)) { + return getAccessSpecifier(Node) == AS_public; } -/// Matches protected C++ declarations. +/// Matches protected C++ declarations and C++ base specifers that specify +/// protected inheritance. /// -/// Given +/// Examples: /// \code /// class C { /// public: int a; -/// protected: int b; +/// protected: int b; // fieldDecl(isProtected()) matches 'b' /// private: int c; /// }; /// \endcode -/// fieldDecl(isProtected()) -/// matches 'int b;' -AST_MATCHER(Decl, isProtected) { - return Node.getAccess() == AS_protected; +/// +/// \code +/// class Base {}; +/// class Derived : protected Base {}; // matches 'Base' +/// \endcode +AST_POLYMORPHIC_MATCHER(isProtected, + AST_POLYMORPHIC_SUPPORTED_TYPES(Decl, + CXXBaseSpecifier)) { + return getAccessSpecifier(Node) == AS_protected; } -/// Matches private C++ declarations. +/// Matches private C++ declarations and C++ base specifers that specify private +/// inheritance. /// -/// Given +/// Examples: /// \code /// class C { /// public: int a; /// protected: int b; -/// private: int c; +/// private: int c; // fieldDecl(isPrivate()) matches 'c' /// }; /// \endcode -/// fieldDecl(isPrivate()) -/// matches 'int c;' -AST_MATCHER(Decl, isPrivate) { - return Node.getAccess() == AS_private; +/// +/// \code +/// struct Base {}; +/// struct Derived1 : private Base {}; // matches 'Base' +/// class Derived2 : Base {}; // matches 'Base' +/// \endcode +AST_POLYMORPHIC_MATCHER(isPrivate, + AST_POLYMORPHIC_SUPPORTED_TYPES(Decl, + CXXBaseSpecifier)) { + return getAccessSpecifier(Node) == AS_private; } /// Matches non-static data members that are bit-fields. @@ -2839,6 +2860,26 @@ AST_POLYMORPHIC_MATCHER_P_OVERLOAD( return Matcher(M).matches(*InterfaceDecl, Finder, Builder); } +/// Matches C++ classes that have a direct or indirect base matching \p +/// BaseSpecMatcher. +/// +/// Example: +/// matcher hasAnyBase(hasType(cxxRecordDecl(hasName("SpecialBase"))))) +/// \code +/// class Foo; +/// class Bar : Foo {}; +/// class Baz : Bar {}; +/// class SpecialBase; +/// class Proxy : SpecialBase {}; // matches Proxy +/// class IndirectlyDerived : Proxy {}; //matches IndirectlyDerived +/// \endcode +/// +// FIXME: Refactor this and isDerivedFrom to reuse implementation. +AST_MATCHER_P(CXXRecordDecl, hasAnyBase, internal::Matcher, + BaseSpecMatcher) { + return internal::matchesAnyBase(Node, BaseSpecMatcher, Finder, Builder); +} + /// Similar to \c isDerivedFrom(), but also matches classes that directly /// match \c Base. AST_POLYMORPHIC_MATCHER_P_OVERLOAD( @@ -3469,9 +3510,19 @@ AST_POLYMORPHIC_MATCHER_P_OVERLOAD( /// class Y { friend class X; }; /// \endcode /// -/// Usable as: Matcher, Matcher +/// Example matches class Derived +/// (matcher = cxxRecordDecl(hasAnyBase(hasType(cxxRecordDecl(hasName("Base")))))) +/// \code +/// class Base {}; +/// class Derived : Base {}; +/// \endcode +/// +/// Usable as: Matcher, Matcher, Matcher, +/// Matcher AST_POLYMORPHIC_MATCHER_P_OVERLOAD( - hasType, AST_POLYMORPHIC_SUPPORTED_TYPES(Expr, FriendDecl, ValueDecl), + hasType, + AST_POLYMORPHIC_SUPPORTED_TYPES(Expr, FriendDecl, ValueDecl, + CXXBaseSpecifier), internal::Matcher, InnerMatcher, 1) { QualType QT = internal::getUnderlyingType(Node); if (!QT.isNull()) @@ -4257,6 +4308,34 @@ AST_POLYMORPHIC_MATCHER_P2(forEachArgumentWithParam, return Matched; } +/// Matches the ParmVarDecl nodes that are at the N'th position in the parameter +/// list. The parameter list could be that of either a block, function, or +/// objc-method. +/// +/// +/// Given +/// +/// \code +/// void f(int a, int b, int c) { +/// } +/// \endcode +/// +/// ``parmVarDecl(isAtPosition(0))`` matches ``int a``. +/// +/// ``parmVarDecl(isAtPosition(1))`` matches ``int b``. +AST_MATCHER_P(clang::ParmVarDecl, isAtPosition, unsigned, N) { + const clang::DeclContext *Context = Node.getParentFunctionOrMethod(); + + if (const auto *Decl = dyn_cast_or_null(Context)) + return N < Decl->param_size() && Decl->getParamDecl(N) == &Node; + if (const auto *Decl = dyn_cast_or_null(Context)) + return N < Decl->param_size() && Decl->getParamDecl(N) == &Node; + if (const auto *Decl = dyn_cast_or_null(Context)) + return N < Decl->param_size() && Decl->getParamDecl(N) == &Node; + + return false; +} + /// Matches any parameter of a function or an ObjC method declaration or a /// block. /// @@ -5149,17 +5228,28 @@ AST_MATCHER_P(CXXMethodDecl, forEachOverridden, return Matched; } -/// Matches if the given method declaration is virtual. +/// Matches declarations of virtual methods and C++ base specifers that specify +/// virtual inheritance. /// -/// Given +/// Example: /// \code /// class A { /// public: -/// virtual void x(); +/// virtual void x(); // matches x /// }; /// \endcode -/// matches A::x -AST_MATCHER(CXXMethodDecl, isVirtual) { +/// +/// Example: +/// \code +/// class Base {}; +/// class DirectlyDerived : virtual Base {}; // matches Base +/// class IndirectlyDerived : DirectlyDerived, Base {}; // matches Base +/// \endcode +/// +/// Usable as: Matcher, Matcher +AST_POLYMORPHIC_MATCHER(isVirtual, + AST_POLYMORPHIC_SUPPORTED_TYPES(CXXMethodDecl, + CXXBaseSpecifier)) { return Node.isVirtual(); } diff --git a/clang/include/clang/ASTMatchers/ASTMatchersInternal.h b/clang/include/clang/ASTMatchers/ASTMatchersInternal.h index e363bdd9ae9cf..fc41407ba2961 100644 --- a/clang/include/clang/ASTMatchers/ASTMatchersInternal.h +++ b/clang/include/clang/ASTMatchers/ASTMatchersInternal.h @@ -130,6 +130,9 @@ inline QualType getUnderlyingType(const FriendDecl &Node) { return TSI->getType(); return QualType(); } +inline QualType getUnderlyingType(const CXXBaseSpecifier &Node) { + return Node.getType(); +} /// Unifies obtaining the FunctionProtoType pointer from both /// FunctionProtoType and FunctionDecl nodes.. @@ -142,6 +145,15 @@ inline const FunctionProtoType *getFunctionProtoType(const FunctionDecl &Node) { return Node.getType()->getAs(); } +/// Unifies obtaining the access specifier from Decl and CXXBaseSpecifier nodes. +inline clang::AccessSpecifier getAccessSpecifier(const Decl &Node) { + return Node.getAccess(); +} + +inline clang::AccessSpecifier getAccessSpecifier(const CXXBaseSpecifier &Node) { + return Node.getAccessSpecifier(); +} + /// Internal version of BoundNodes. Holds all the bound nodes. class BoundNodesMap { public: @@ -395,6 +407,12 @@ class DynTypedMatcher { /// restricts the node types for \p Kind. DynTypedMatcher dynCastTo(const ASTNodeKind Kind) const; + /// Return a matcher that that points to the same implementation, but sets the + /// traversal kind. + /// + /// If the traversal kind is already set, then \c TK overrides it. + DynTypedMatcher withTraversalKind(TraversalKind TK); + /// Returns true if the matcher matches the given \c DynNode. bool matches(const DynTypedNode &DynNode, ASTMatchFinder *Finder, BoundNodesTreeBuilder *Builder) const; @@ -458,6 +476,14 @@ class DynTypedMatcher { /// If it is not compatible, then this matcher will never match anything. template Matcher unconditionalConvertTo() const; + /// Returns the \c TraversalKind respected by calls to `match()`, if any. + /// + /// Most matchers will not have a traversal kind set, instead relying on the + /// surrounding context. For those, \c llvm::None is returned. + llvm::Optional getTraversalKind() const { + return Implementation->TraversalKind(); + } + private: DynTypedMatcher(ASTNodeKind SupportedKind, ASTNodeKind RestrictKind, IntrusiveRefCntPtr Implementation) @@ -475,19 +501,6 @@ class DynTypedMatcher { IntrusiveRefCntPtr Implementation; }; -/// Wrapper base class for a wrapping matcher. -/// -/// This is just a container for a DynTypedMatcher that can be used as a base -/// class for another matcher. -template -class WrapperMatcherInterface : public MatcherInterface { -protected: - explicit WrapperMatcherInterface(DynTypedMatcher &&InnerMatcher) - : InnerMatcher(std::move(InnerMatcher)) {} - - const DynTypedMatcher InnerMatcher; -}; - /// Wrapper of a MatcherInterface *that allows copying. /// /// A Matcher can be used anywhere a Matcher is @@ -558,10 +571,12 @@ class Matcher { /// does only matches in the absence of qualifiers, or not, i.e. simply /// ignores any qualifiers. template - class TypeToQualType : public WrapperMatcherInterface { + class TypeToQualType : public MatcherInterface { + const DynTypedMatcher InnerMatcher; + public: TypeToQualType(const Matcher &InnerMatcher) - : TypeToQualType::WrapperMatcherInterface(InnerMatcher) {} + : InnerMatcher(InnerMatcher) {} bool matches(const QualType &Node, ASTMatchFinder *Finder, BoundNodesTreeBuilder *Builder) const override { @@ -750,13 +765,15 @@ Matcher hasAnySelectorFunc( /// Type argument DeclMatcherT is required by PolymorphicMatcherWithParam1 but /// not actually used. template -class HasDeclarationMatcher : public WrapperMatcherInterface { +class HasDeclarationMatcher : public MatcherInterface { static_assert(std::is_same>::value, "instantiated with wrong types"); + const DynTypedMatcher InnerMatcher; + public: explicit HasDeclarationMatcher(const Matcher &InnerMatcher) - : HasDeclarationMatcher::WrapperMatcherInterface(InnerMatcher) {} + : InnerMatcher(InnerMatcher) {} bool matches(const T &Node, ASTMatchFinder *Finder, BoundNodesTreeBuilder *Builder) const override { @@ -1167,14 +1184,14 @@ struct ArgumentAdaptingMatcherFunc { } }; -template -class TraversalMatcher : public WrapperMatcherInterface { +template class TraversalMatcher : public MatcherInterface { + const DynTypedMatcher InnerMatcher; clang::TraversalKind Traversal; public: - explicit TraversalMatcher(clang::TraversalKind TK, const Matcher &ChildMatcher) - : TraversalMatcher::WrapperMatcherInterface(ChildMatcher), Traversal(TK) { - } + explicit TraversalMatcher(clang::TraversalKind TK, + const Matcher &InnerMatcher) + : InnerMatcher(InnerMatcher), Traversal(TK) {} bool matches(const T &Node, ASTMatchFinder *Finder, BoundNodesTreeBuilder *Builder) const override { @@ -1323,10 +1340,12 @@ class BindableMatcher : public Matcher { /// /// ChildT must be an AST base type. template -class HasMatcher : public WrapperMatcherInterface { +class HasMatcher : public MatcherInterface { + const DynTypedMatcher InnerMatcher; + public: - explicit HasMatcher(const Matcher &ChildMatcher) - : HasMatcher::WrapperMatcherInterface(ChildMatcher) {} + explicit HasMatcher(const Matcher &InnerMatcher) + : InnerMatcher(InnerMatcher) {} bool matches(const T &Node, ASTMatchFinder *Finder, BoundNodesTreeBuilder *Builder) const override { @@ -1342,16 +1361,18 @@ class HasMatcher : public WrapperMatcherInterface { /// As opposed to the HasMatcher, the ForEachMatcher will produce a match /// for each child that matches. template -class ForEachMatcher : public WrapperMatcherInterface { +class ForEachMatcher : public MatcherInterface { static_assert(IsBaseType::value, "for each only accepts base type matcher"); - public: - explicit ForEachMatcher(const Matcher &ChildMatcher) - : ForEachMatcher::WrapperMatcherInterface(ChildMatcher) {} + const DynTypedMatcher InnerMatcher; + +public: + explicit ForEachMatcher(const Matcher &InnerMatcher) + : InnerMatcher(InnerMatcher) {} - bool matches(const T& Node, ASTMatchFinder* Finder, - BoundNodesTreeBuilder* Builder) const override { + bool matches(const T &Node, ASTMatchFinder *Finder, + BoundNodesTreeBuilder *Builder) const override { return Finder->matchesChildOf( Node, this->InnerMatcher, Builder, TraversalKind::TK_IgnoreImplicitCastsAndParentheses, @@ -1455,17 +1476,19 @@ BindableMatcher makeDynCastAllOfComposite( /// /// DescendantT must be an AST base type. template -class HasDescendantMatcher : public WrapperMatcherInterface { +class HasDescendantMatcher : public MatcherInterface { static_assert(IsBaseType::value, "has descendant only accepts base type matcher"); + const DynTypedMatcher DescendantMatcher; + public: explicit HasDescendantMatcher(const Matcher &DescendantMatcher) - : HasDescendantMatcher::WrapperMatcherInterface(DescendantMatcher) {} + : DescendantMatcher(DescendantMatcher) {} bool matches(const T &Node, ASTMatchFinder *Finder, BoundNodesTreeBuilder *Builder) const override { - return Finder->matchesDescendantOf(Node, this->InnerMatcher, Builder, + return Finder->matchesDescendantOf(Node, this->DescendantMatcher, Builder, ASTMatchFinder::BK_First); } }; @@ -1475,17 +1498,19 @@ class HasDescendantMatcher : public WrapperMatcherInterface { /// /// \c ParentT must be an AST base type. template -class HasParentMatcher : public WrapperMatcherInterface { +class HasParentMatcher : public MatcherInterface { static_assert(IsBaseType::value, "has parent only accepts base type matcher"); + const DynTypedMatcher ParentMatcher; + public: explicit HasParentMatcher(const Matcher &ParentMatcher) - : HasParentMatcher::WrapperMatcherInterface(ParentMatcher) {} + : ParentMatcher(ParentMatcher) {} bool matches(const T &Node, ASTMatchFinder *Finder, BoundNodesTreeBuilder *Builder) const override { - return Finder->matchesAncestorOf(Node, this->InnerMatcher, Builder, + return Finder->matchesAncestorOf(Node, this->ParentMatcher, Builder, ASTMatchFinder::AMM_ParentOnly); } }; @@ -1495,17 +1520,19 @@ class HasParentMatcher : public WrapperMatcherInterface { /// /// \c AncestorT must be an AST base type. template -class HasAncestorMatcher : public WrapperMatcherInterface { +class HasAncestorMatcher : public MatcherInterface { static_assert(IsBaseType::value, "has ancestor only accepts base type matcher"); + const DynTypedMatcher AncestorMatcher; + public: explicit HasAncestorMatcher(const Matcher &AncestorMatcher) - : HasAncestorMatcher::WrapperMatcherInterface(AncestorMatcher) {} + : AncestorMatcher(AncestorMatcher) {} bool matches(const T &Node, ASTMatchFinder *Finder, BoundNodesTreeBuilder *Builder) const override { - return Finder->matchesAncestorOf(Node, this->InnerMatcher, Builder, + return Finder->matchesAncestorOf(Node, this->AncestorMatcher, Builder, ASTMatchFinder::AMM_All); } }; @@ -1517,18 +1544,20 @@ class HasAncestorMatcher : public WrapperMatcherInterface { /// As opposed to HasDescendantMatcher, ForEachDescendantMatcher will match /// for each descendant node that matches instead of only for the first. template -class ForEachDescendantMatcher : public WrapperMatcherInterface { +class ForEachDescendantMatcher : public MatcherInterface { static_assert(IsBaseType::value, "for each descendant only accepts base type matcher"); + const DynTypedMatcher DescendantMatcher; + public: explicit ForEachDescendantMatcher( const Matcher &DescendantMatcher) - : ForEachDescendantMatcher::WrapperMatcherInterface(DescendantMatcher) {} + : DescendantMatcher(DescendantMatcher) {} bool matches(const T &Node, ASTMatchFinder *Finder, BoundNodesTreeBuilder *Builder) const override { - return Finder->matchesDescendantOf(Node, this->InnerMatcher, Builder, + return Finder->matchesDescendantOf(Node, this->DescendantMatcher, Builder, ASTMatchFinder::BK_All); } }; @@ -1621,10 +1650,12 @@ class VariadicAllOfMatcher /// Matches nodes of type \c TLoc for which the inner /// \c Matcher matches. template -class LocMatcher : public WrapperMatcherInterface { +class LocMatcher : public MatcherInterface { + const DynTypedMatcher InnerMatcher; + public: explicit LocMatcher(const Matcher &InnerMatcher) - : LocMatcher::WrapperMatcherInterface(InnerMatcher) {} + : InnerMatcher(InnerMatcher) {} bool matches(const TLoc &Node, ASTMatchFinder *Finder, BoundNodesTreeBuilder *Builder) const override { @@ -1643,10 +1674,12 @@ class LocMatcher : public WrapperMatcherInterface { /// \c QualType. /// /// Used to implement the \c loc() matcher. -class TypeLocTypeMatcher : public WrapperMatcherInterface { +class TypeLocTypeMatcher : public MatcherInterface { + const DynTypedMatcher InnerMatcher; + public: explicit TypeLocTypeMatcher(const Matcher &InnerMatcher) - : TypeLocTypeMatcher::WrapperMatcherInterface(InnerMatcher) {} + : InnerMatcher(InnerMatcher) {} bool matches(const TypeLoc &Node, ASTMatchFinder *Finder, BoundNodesTreeBuilder *Builder) const override { @@ -1660,13 +1693,13 @@ class TypeLocTypeMatcher : public WrapperMatcherInterface { /// Matches nodes of type \c T for which the inner matcher matches on a /// another node of type \c T that can be reached using a given traverse /// function. -template -class TypeTraverseMatcher : public WrapperMatcherInterface { +template class TypeTraverseMatcher : public MatcherInterface { + const DynTypedMatcher InnerMatcher; + public: explicit TypeTraverseMatcher(const Matcher &InnerMatcher, QualType (T::*TraverseFunction)() const) - : TypeTraverseMatcher::WrapperMatcherInterface(InnerMatcher), - TraverseFunction(TraverseFunction) {} + : InnerMatcher(InnerMatcher), TraverseFunction(TraverseFunction) {} bool matches(const T &Node, ASTMatchFinder *Finder, BoundNodesTreeBuilder *Builder) const override { @@ -1685,12 +1718,13 @@ class TypeTraverseMatcher : public WrapperMatcherInterface { /// matcher matches on a another node of type \c T that can be reached using a /// given traverse function. template -class TypeLocTraverseMatcher : public WrapperMatcherInterface { +class TypeLocTraverseMatcher : public MatcherInterface { + const DynTypedMatcher InnerMatcher; + public: explicit TypeLocTraverseMatcher(const Matcher &InnerMatcher, TypeLoc (T::*TraverseFunction)() const) - : TypeLocTraverseMatcher::WrapperMatcherInterface(InnerMatcher), - TraverseFunction(TraverseFunction) {} + : InnerMatcher(InnerMatcher), TraverseFunction(TraverseFunction) {} bool matches(const T &Node, ASTMatchFinder *Finder, BoundNodesTreeBuilder *Builder) const override { @@ -1907,6 +1941,13 @@ using HasOverloadOpNameMatcher = PolymorphicMatcherWithParam1< HasOverloadOpNameMatcher hasAnyOverloadedOperatorNameFunc(ArrayRef NameRefs); +/// Returns true if \p Node has a base specifier matching \p BaseSpec. +/// +/// A class is not considered to be derived from itself. +bool matchesAnyBase(const CXXRecordDecl &Node, + const Matcher &BaseSpecMatcher, + ASTMatchFinder *Finder, BoundNodesTreeBuilder *Builder); + } // namespace internal } // namespace ast_matchers diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td index 8f238f3a93a83..053b3eb4fd386 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -261,7 +261,6 @@ class VariadicEnumArgument values, class Spelling { string Name = name; string Variety = variety; - bit KnownToGCC; } class GNU : Spelling; @@ -281,11 +280,11 @@ class Pragma : Spelling { string Namespace = namespace; } -// The GCC spelling implies GNU and CXX11<"gnu", name> and also sets -// KnownToGCC to 1. This spelling should be used for any GCC-compatible +// The GCC spelling implies GNU, CXX11<"gnu", name>, and optionally, +// C2x<"gnu", name>. This spelling should be used for any GCC-compatible // attributes. -class GCC : Spelling { - let KnownToGCC = 1; +class GCC : Spelling { + bit AllowInC = allowInC; } // The Clang spelling implies GNU, CXX11<"clang", name>, and optionally, @@ -615,7 +614,7 @@ class IgnoredAttr : Attr { // def AbiTag : Attr { - let Spellings = [GCC<"abi_tag">]; + let Spellings = [GCC<"abi_tag", /*AllowInC*/0>]; let Args = [VariadicStringArgument<"Tags">]; let Subjects = SubjectList<[Struct, Var, Function, Namespace], ErrorDiag>; let MeaningfulToClassTemplateDefinition = 1; @@ -2563,7 +2562,7 @@ def WorkGroupSizeHint : InheritableAttr { } def InitPriority : InheritableAttr { - let Spellings = [GCC<"init_priority">]; + let Spellings = [GCC<"init_priority", /*AllowInC*/0>]; let Args = [UnsignedArgument<"Priority">]; let Subjects = SubjectList<[Var], ErrorDiag>; let Documentation = [Undocumented]; diff --git a/clang/include/clang/Basic/Builtins.def b/clang/include/clang/Basic/Builtins.def index 035942fcc5707..f398737cb32e1 100644 --- a/clang/include/clang/Basic/Builtins.def +++ b/clang/include/clang/Basic/Builtins.def @@ -323,6 +323,9 @@ BUILTIN(__builtin_truncf, "ff", "Fnc") BUILTIN(__builtin_truncl, "LdLd", "Fnc") BUILTIN(__builtin_truncf16, "hh", "Fnc") +// Access to floating point environment +BUILTIN(__builtin_flt_rounds, "i", "n") + // C99 complex builtins BUILTIN(__builtin_cabs, "dXd", "Fne") BUILTIN(__builtin_cabsf, "fXf", "Fne") @@ -517,7 +520,6 @@ BUILTIN(__builtin_return_address, "v*IUi", "n") BUILTIN(__builtin_extract_return_addr, "v*v*", "n") BUILTIN(__builtin_frame_address, "v*IUi", "n") BUILTIN(__builtin___clear_cache, "vc*c*", "n") -BUILTIN(__builtin_flt_rounds, "i", "nc") BUILTIN(__builtin_setjmp, "iv**", "j") BUILTIN(__builtin_longjmp, "vv**i", "r") BUILTIN(__builtin_unwind_init, "v", "") diff --git a/clang/include/clang/Basic/BuiltinsAMDGPU.def b/clang/include/clang/Basic/BuiltinsAMDGPU.def index 5633ccd5d744c..28379142b05ad 100644 --- a/clang/include/clang/Basic/BuiltinsAMDGPU.def +++ b/clang/include/clang/Basic/BuiltinsAMDGPU.def @@ -44,6 +44,7 @@ BUILTIN(__builtin_amdgcn_mbcnt_lo, "UiUiUi", "nc") // Instruction builtins. //===----------------------------------------------------------------------===// BUILTIN(__builtin_amdgcn_s_getreg, "UiIi", "n") +BUILTIN(__builtin_amdgcn_s_setreg, "vIiUi", "n") BUILTIN(__builtin_amdgcn_s_getpc, "LUi", "n") BUILTIN(__builtin_amdgcn_s_waitcnt, "vIi", "n") BUILTIN(__builtin_amdgcn_s_sendmsg, "vIiUi", "n") diff --git a/clang/include/clang/Basic/DiagnosticLexKinds.td b/clang/include/clang/Basic/DiagnosticLexKinds.td index ef90bdf84c8ab..fa07e9ae76c85 100644 --- a/clang/include/clang/Basic/DiagnosticLexKinds.td +++ b/clang/include/clang/Basic/DiagnosticLexKinds.td @@ -175,7 +175,7 @@ def ext_unknown_escape : ExtWarn<"unknown escape sequence '\\%0'">, def err_invalid_digit : Error< "invalid digit '%0' in %select{decimal|octal|binary}1 constant">; def err_invalid_suffix_constant : Error< - "invalid suffix '%0' on %select{integer|floating}1 constant">; + "invalid suffix '%0' on %select{integer|floating|fixed-point}1 constant">; def warn_cxx11_compat_digit_separator : Warning< "digit separators are incompatible with C++ standards before C++14">, InGroup, DefaultIgnore; diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 5984ede3fcbb8..93722c301d03c 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -10245,8 +10245,8 @@ def err_omp_invariant_or_linear_dependency : Error< "expected loop invariant expression or ' * %0 + ' kind of expression">; def err_omp_wrong_dependency_iterator_type : Error< "expected an integer or a pointer type of the outer loop counter '%0' for non-rectangular nests">; -def err_omp_unsupported_type : Error < - "host requires %0 bit size %1 type support, but device '%2' does not support it">; +def err_device_unsupported_type : Error < + "%0 requires %1 bit size %2 type support, but device '%3' does not support it">; def err_omp_lambda_capture_in_declare_target_not_to : Error< "variable captured in declare target region must appear in a to clause">; def err_omp_device_type_mismatch : Error< diff --git a/clang/include/clang/Basic/LangOptions.def b/clang/include/clang/Basic/LangOptions.def index c091f343e9861..bb2d12b8398ca 100644 --- a/clang/include/clang/Basic/LangOptions.def +++ b/clang/include/clang/Basic/LangOptions.def @@ -306,6 +306,7 @@ ENUM_LANGOPT(TypeVisibilityMode, Visibility, 3, DefaultVisibility, LANGOPT(SetVisibilityForExternDecls, 1, 0, "apply global symbol visibility to external declarations without an explicit visibility") BENIGN_LANGOPT(SemanticInterposition , 1, 0, "semantic interposition") +BENIGN_LANGOPT(ExplicitNoSemanticInterposition, 1, 0, "explicitly no semantic interposition") ENUM_LANGOPT(StackProtector, StackProtectorMode, 2, SSPOff, "stack protector mode") ENUM_LANGOPT(TrivialAutoVarInit, TrivialAutoVarInitKind, 2, TrivialAutoVarInitKind::Uninitialized, diff --git a/clang/include/clang/Basic/Specifiers.h b/clang/include/clang/Basic/Specifiers.h index e6c2cb39566ce..2c80dd4fa8103 100644 --- a/clang/include/clang/Basic/Specifiers.h +++ b/clang/include/clang/Basic/Specifiers.h @@ -365,6 +365,20 @@ namespace clang { }; llvm::StringRef getParameterABISpelling(ParameterABI kind); + + inline llvm::StringRef getAccessSpelling(AccessSpecifier AS) { + switch (AS) { + case AccessSpecifier::AS_public: + return "public"; + case AccessSpecifier::AS_protected: + return "protected"; + case AccessSpecifier::AS_private: + return "private"; + case AccessSpecifier::AS_none: + return {}; + } + llvm_unreachable("Unknown AccessSpecifier"); + } } // end namespace clang #endif // LLVM_CLANG_BASIC_SPECIFIERS_H diff --git a/clang/include/clang/Basic/TargetInfo.h b/clang/include/clang/Basic/TargetInfo.h index 910a4d6846aaa..0a5379225caf3 100644 --- a/clang/include/clang/Basic/TargetInfo.h +++ b/clang/include/clang/Basic/TargetInfo.h @@ -368,8 +368,13 @@ class TargetInfo : public virtual TransferrableTargetInfo, virtual IntType getLeastIntTypeByWidth(unsigned BitWidth, bool IsSigned) const; - /// Return floating point type with specified width. - RealType getRealTypeByWidth(unsigned BitWidth) const; + /// Return floating point type with specified width. On PPC, there are + /// three possible types for 128-bit floating point: "PPC double-double", + /// IEEE 754R quad precision, and "long double" (which under the covers + /// is represented as one of those two). At this time, there is no support + /// for an explicit "PPC double-double" type (i.e. __ibm128) so we only + /// need to differentiate between "long double" and IEEE quad precision. + RealType getRealTypeByWidth(unsigned BitWidth, bool ExplicitIEEE) const; /// Return the alignment (in bits) of the specified integer type enum. /// diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td index ea802d57b7576..1076a461c064e 100644 --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -3373,11 +3373,9 @@ def Z_reserved_lib_cckext : Flag<["-"], "Z-reserved-lib-cckext">, Flags<[LinkerInput, NoArgumentUnused, Unsupported]>, Group; // Ignored options -// FIXME: multiclasess produce suffixes, not prefixes. This is fine for now -// since it is only used in ignored options. multiclass BooleanFFlag { - def _f : Flag<["-"], "f"#name>; - def _fno : Flag<["-"], "fno-"#name>; + def f#NAME : Flag<["-"], "f"#name>; + def fno_#NAME : Flag<["-"], "fno-"#name>; } defm : BooleanFFlag<"keep-inline-functions">, Group; @@ -3428,7 +3426,7 @@ defm ipa_cp : BooleanFFlag<"ipa-cp">, Group; defm ivopts : BooleanFFlag<"ivopts">, Group; def fsemantic_interposition : Flag<["-"], "fsemantic-interposition">, Group, Flags<[CC1Option]>; -def fno_semantic_interposition: Flag<["-"], "fno-semantic-interposition">, Group; +def fno_semantic_interposition: Flag<["-"], "fno-semantic-interposition">, Group, Flags<[CC1Option]>; defm non_call_exceptions : BooleanFFlag<"non-call-exceptions">, Group; defm peel_loops : BooleanFFlag<"peel-loops">, Group; defm permissive : BooleanFFlag<"permissive">, Group; diff --git a/clang/include/clang/Index/IndexingAction.h b/clang/include/clang/Index/IndexingAction.h index 9ed2a018f1617..4baa2d5e72603 100644 --- a/clang/include/clang/Index/IndexingAction.h +++ b/clang/include/clang/Index/IndexingAction.h @@ -30,22 +30,21 @@ namespace serialization { } namespace index { - class IndexDataConsumer; +class IndexDataConsumer; /// Creates an ASTConsumer that indexes all symbols (macros and AST decls). +std::unique_ptr +createIndexingASTConsumer(std::shared_ptr DataConsumer, + const IndexingOptions &Opts, + std::shared_ptr PP); + std::unique_ptr createIndexingASTConsumer( std::shared_ptr DataConsumer, const IndexingOptions &Opts, std::shared_ptr PP, + // Prefer to set Opts.ShouldTraverseDecl and use the above overload. + // This version is only needed if used to *track* function body parsing. std::function ShouldSkipFunctionBody); -inline std::unique_ptr createIndexingASTConsumer( - std::shared_ptr DataConsumer, - const IndexingOptions &Opts, std::shared_ptr PP) { - return createIndexingASTConsumer( - std::move(DataConsumer), Opts, std::move(PP), - /*ShouldSkipFunctionBody=*/[](const Decl *) { return false; }); -} - /// Creates a frontend action that indexes all symbols (macros and AST decls). std::unique_ptr createIndexingAction(std::shared_ptr DataConsumer, diff --git a/clang/include/clang/Index/IndexingOptions.h b/clang/include/clang/Index/IndexingOptions.h index bbfd6e4a72c62..9f5c03d1b3b94 100644 --- a/clang/include/clang/Index/IndexingOptions.h +++ b/clang/include/clang/Index/IndexingOptions.h @@ -14,6 +14,7 @@ #include namespace clang { +class Decl; namespace index { struct IndexingOptions { @@ -34,6 +35,12 @@ struct IndexingOptions { // Has no effect if IndexFunctionLocals are false. bool IndexParametersInDeclarations = false; bool IndexTemplateParameters = false; + + // If set, skip indexing inside some declarations for performance. + // This prevents traversal, so skipping a struct means its declaration an + // members won't be indexed, but references elsewhere to that struct will be. + // Currently this is only checked for top-level declarations. + std::function ShouldTraverseDecl; }; } // namespace index diff --git a/clang/include/clang/Lex/LiteralSupport.h b/clang/include/clang/Lex/LiteralSupport.h index b9d64c24a00bd..6829771b28308 100644 --- a/clang/include/clang/Lex/LiteralSupport.h +++ b/clang/include/clang/Lex/LiteralSupport.h @@ -71,7 +71,9 @@ class NumericLiteralParser { bool isFract : 1; // 1.0hr/r/lr/uhr/ur/ulr bool isAccum : 1; // 1.0hk/k/lk/uhk/uk/ulk - bool isFixedPointLiteral() const { return saw_fixed_point_suffix; } + bool isFixedPointLiteral() const { + return (saw_period || saw_exponent) && saw_fixed_point_suffix; + } bool isIntegerLiteral() const { return !saw_period && !saw_exponent && !isFixedPointLiteral(); diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 6233e982506f7..c1297cf19b688 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -3525,7 +3525,8 @@ class Sema final { /// Check the enable_if expressions on the given function. Returns the first /// failing attribute, or NULL if they were all successful. - EnableIfAttr *CheckEnableIf(FunctionDecl *Function, ArrayRef Args, + EnableIfAttr *CheckEnableIf(FunctionDecl *Function, SourceLocation CallLoc, + ArrayRef Args, bool MissingImplicitThis = false); /// Find the failed Boolean condition within a given Boolean @@ -10037,10 +10038,6 @@ class Sema final { /// Pop OpenMP function region for non-capturing function. void popOpenMPFunctionRegion(const sema::FunctionScopeInfo *OldFSI); - /// Check if the expression is allowed to be used in expressions for the - /// OpenMP devices. - void checkOpenMPDeviceExpr(const Expr *E); - /// Checks if a type or a declaration is disabled due to the owning extension /// being disabled, and emits diagnostic messages if it is disabled. /// \param D type or declaration to be checked. @@ -10946,6 +10943,9 @@ class Sema final { /// Called on well-formed 'use_device_ptr' clause. OMPClause *ActOnOpenMPUseDevicePtrClause(ArrayRef VarList, const OMPVarListLocTy &Locs); + /// Called on well-formed 'use_device_addr' clause. + OMPClause *ActOnOpenMPUseDeviceAddrClause(ArrayRef VarList, + const OMPVarListLocTy &Locs); /// Called on well-formed 'is_device_ptr' clause. OMPClause *ActOnOpenMPIsDevicePtrClause(ArrayRef VarList, const OMPVarListLocTy &Locs); @@ -11375,6 +11375,11 @@ class Sema final { QualType CheckVectorLogicalOperands(ExprResult &LHS, ExprResult &RHS, SourceLocation Loc); + /// Type checking for matrix binary operators. + QualType CheckMatrixElementwiseOperands(ExprResult &LHS, ExprResult &RHS, + SourceLocation Loc, + bool IsCompAssign); + bool areLaxCompatibleVectorTypes(QualType srcType, QualType destType); bool isLaxVectorConversion(QualType srcType, QualType destType); @@ -11820,6 +11825,10 @@ class Sema final { DeviceDiagBuilder targetDiag(SourceLocation Loc, unsigned DiagID); + /// Check if the expression is allowed to be used in expressions for the + /// offloading devices. + void checkDeviceDecl(const ValueDecl *D, SourceLocation Loc); + enum CUDAFunctionTarget { CFT_Device, CFT_Global, @@ -12624,10 +12633,11 @@ class Sema final { /// /// Example usage: /// - /// Variables with thread storage duration are not allowed to be used in SYCL - /// device code - /// if (getLangOpts().SYCLIsDevice) - /// SYCLDiagIfDeviceCode(Loc, diag::err_thread_unsupported); + /// Diagnose __float128 type usage only from SYCL device code if the current + /// target doesn't support it + /// if (!S.Context.getTargetInfo().hasFloat128Type() && + /// S.getLangOpts().SYCLIsDevice) + /// SYCLDiagIfDeviceCode(Loc, diag::err_type_unsupported) << "__float128"; DeviceDiagBuilder SYCLDiagIfDeviceCode(SourceLocation Loc, unsigned DiagID); /// Check whether we're allowed to call Callee from the current context. diff --git a/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td b/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td index f0ad8326929e5..2d69d8f344209 100644 --- a/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td +++ b/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td @@ -556,13 +556,13 @@ def NewDeleteChecker : Checker<"NewDelete">, def NewDeleteLeaksChecker : Checker<"NewDeleteLeaks">, HelpText<"Check for memory leaks. Traces memory managed by new/delete.">, - Dependencies<[NewDeleteChecker]>, + Dependencies<[DynamicMemoryModeling]>, Documentation; def PlacementNewChecker : Checker<"PlacementNew">, HelpText<"Check if default placement new is provided with pointers to " "sufficient storage capacity">, - Dependencies<[NewDeleteChecker]>, + Dependencies<[DynamicMemoryModeling]>, Documentation; def CXXSelfAssignmentChecker : Checker<"SelfAssignment">, @@ -1094,15 +1094,6 @@ def NSErrorChecker : Checker<"NSError">, def RetainCountChecker : Checker<"RetainCount">, HelpText<"Check for leaks and improper reference count management">, CheckerOptions<[ - CmdLineOption, CmdLineOption, HelpText<"Check for any ref-countable base class having virtual destructor.">, Documentation; + +def WebKitNoUncountedMemberChecker : Checker<"WebKitNoUncountedMemberChecker">, + HelpText<"Check for no uncounted member variables.">, + Documentation; } // end webkit diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/BasicValueFactory.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/BasicValueFactory.h index ac218bc070e9a..a001c0dc70308 100644 --- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/BasicValueFactory.h +++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/BasicValueFactory.h @@ -157,6 +157,10 @@ class BasicValueFactory { const llvm::APSInt &Convert(QualType T, const llvm::APSInt &From) { APSIntType TargetType = getAPSIntType(T); + return Convert(TargetType, From); + } + + const llvm::APSInt &Convert(APSIntType TargetType, const llvm::APSInt &From) { if (TargetType == APSIntType(From)) return From; @@ -177,11 +181,19 @@ class BasicValueFactory { } const llvm::APSInt &getMaxValue(QualType T) { - return getValue(getAPSIntType(T).getMaxValue()); + return getMaxValue(getAPSIntType(T)); } const llvm::APSInt &getMinValue(QualType T) { - return getValue(getAPSIntType(T).getMinValue()); + return getMinValue(getAPSIntType(T)); + } + + const llvm::APSInt &getMaxValue(APSIntType T) { + return getValue(T.getMaxValue()); + } + + const llvm::APSInt &getMinValue(APSIntType T) { + return getValue(T.getMinValue()); } const llvm::APSInt &Add1(const llvm::APSInt &V) { diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ConstraintManager.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ConstraintManager.h index 935b2bb7b937d..335536b6a3106 100644 --- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ConstraintManager.h +++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ConstraintManager.h @@ -32,7 +32,7 @@ namespace clang { namespace ento { class ProgramStateManager; -class SubEngine; +class ExprEngine; class SymbolReaper; class ConditionTruthVal { @@ -193,10 +193,11 @@ class ConstraintManager { std::unique_ptr CreateRangeConstraintManager(ProgramStateManager &statemgr, - SubEngine *subengine); + ExprEngine *exprengine); std::unique_ptr -CreateZ3ConstraintManager(ProgramStateManager &statemgr, SubEngine *subengine); +CreateZ3ConstraintManager(ProgramStateManager &statemgr, + ExprEngine *exprengine); } // namespace ento } // namespace clang diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h index 278193ef99ede..2aca2c99ef4fd 100644 --- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h +++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h @@ -41,7 +41,7 @@ class LabelDecl; namespace ento { class FunctionSummariesTy; -class SubEngine; +class ExprEngine; //===----------------------------------------------------------------------===// /// CoreEngine - Implements the core logic of the graph-reachability @@ -69,7 +69,7 @@ class CoreEngine { std::vector>; private: - SubEngine &SubEng; + ExprEngine &ExprEng; /// G - The simulation graph. Each node is a (location,state) pair. mutable ExplodedGraph G; @@ -129,7 +129,7 @@ class CoreEngine { public: /// Construct a CoreEngine object to analyze the provided CFG. - CoreEngine(SubEngine &subengine, + CoreEngine(ExprEngine &exprengine, FunctionSummariesTy *FS, AnalyzerOptions &Opts); diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/DynamicSize.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/DynamicSize.h index b48914c53d82f..398f9b6ac33a4 100644 --- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/DynamicSize.h +++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/DynamicSize.h @@ -32,6 +32,21 @@ DefinedOrUnknownSVal getDynamicElementCount(ProgramStateRef State, SValBuilder &SVB, QualType ElementTy); +/// Get the dynamic size for a symbolic value that represents a buffer. If +/// there is an offsetting to the underlying buffer we consider that too. +/// Returns with an SVal that represents the size, this is Unknown if the +/// engine cannot deduce the size. +/// E.g. +/// char buf[3]; +/// (buf); // size is 3 +/// (buf + 1); // size is 2 +/// (buf + 3); // size is 0 +/// (buf + 4); // size is -1 +/// +/// char *bufptr; +/// (bufptr) // size is unknown +SVal getDynamicSizeWithOffset(ProgramStateRef State, const SVal &BufV); + } // namespace ento } // namespace clang diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h index c66c54116a0c6..3611979c61911 100644 --- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h +++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h @@ -21,6 +21,7 @@ #include "clang/Analysis/DomainSpecific/ObjCNoReturn.h" #include "clang/Analysis/ProgramPoint.h" #include "clang/Basic/LLVM.h" +#include "clang/StaticAnalyzer/Core/CheckerManager.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitors.h" #include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" @@ -29,9 +30,9 @@ #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState_Fwd.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/Store.h" #include "clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h" #include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/SubEngine.h" #include "clang/StaticAnalyzer/Core/PathSensitive/WorkList.h" #include "llvm/ADT/ArrayRef.h" #include @@ -42,6 +43,8 @@ namespace clang { class AnalysisDeclContextManager; class AnalyzerOptions; class ASTContext; +class CFGBlock; +class CFGElement; class ConstructionContext; class CXXBindTemporaryExpr; class CXXCatchStmt; @@ -72,16 +75,29 @@ class CrossTranslationUnitContext; namespace ento { +class AnalysisManager; class BasicValueFactory; +class BlockCounter; +class BranchNodeBuilder; class CallEvent; class CheckerManager; class ConstraintManager; class CXXTempObjectRegion; +class EndOfFunctionNodeBuilder; +class ExplodedNodeSet; +class ExplodedNode; +class IndirectGotoNodeBuilder; class MemRegion; +struct NodeBuilderContext; +class NodeBuilderWithSinks; +class ProgramState; +class ProgramStateManager; class RegionAndSymbolInvalidationTraits; class SymbolManager; +class SwitchNodeBuilder; -class ExprEngine : public SubEngine { +class ExprEngine { + void anchor(); public: /// The modes of inlining, which override the default analysis-wide settings. enum InliningModes { @@ -161,7 +177,7 @@ class ExprEngine : public SubEngine { SetOfConstDecls *VisitedCalleesIn, FunctionSummariesTy *FS, InliningModes HowToInlineIn); - ~ExprEngine() override = default; + virtual ~ExprEngine() = default; /// Returns true if there is still simulation state on the worklist. bool ExecuteWorkList(const LocationContext *L, unsigned Steps = 150000) { @@ -181,7 +197,7 @@ class ExprEngine : public SubEngine { /// getContext - Return the ASTContext associated with this analysis. ASTContext &getContext() const { return AMgr.getASTContext(); } - AnalysisManager &getAnalysisManager() override { return AMgr; } + AnalysisManager &getAnalysisManager() { return AMgr; } AnalysisDeclContextManager &getAnalysisDeclContextManager() { return AMgr.getAnalysisDeclContextManager(); @@ -196,7 +212,7 @@ class ExprEngine : public SubEngine { BugReporter &getBugReporter() { return BR; } cross_tu::CrossTranslationUnitContext * - getCrossTranslationUnitContext() override { + getCrossTranslationUnitContext() { return &CTU; } @@ -232,7 +248,7 @@ class ExprEngine : public SubEngine { /// getInitialState - Return the initial state used for the root vertex /// in the ExplodedGraph. - ProgramStateRef getInitialState(const LocationContext *InitLoc) override; + ProgramStateRef getInitialState(const LocationContext *InitLoc); ExplodedGraph &getGraph() { return G; } const ExplodedGraph &getGraph() const { return G; } @@ -270,7 +286,7 @@ class ExprEngine : public SubEngine { /// processCFGElement - Called by CoreEngine. Used to generate new successor /// nodes by processing the 'effects' of a CFG element. void processCFGElement(const CFGElement E, ExplodedNode *Pred, - unsigned StmtIdx, NodeBuilderContext *Ctx) override; + unsigned StmtIdx, NodeBuilderContext *Ctx); void ProcessStmt(const Stmt *S, ExplodedNode *Pred); @@ -296,7 +312,7 @@ class ExprEngine : public SubEngine { /// Called by CoreEngine when processing the entrance of a CFGBlock. void processCFGBlockEntrance(const BlockEdge &L, NodeBuilderWithSinks &nodeBuilder, - ExplodedNode *Pred) override; + ExplodedNode *Pred); /// ProcessBranch - Called by CoreEngine. Used to generate successor /// nodes by processing the 'effects' of a branch condition. @@ -305,7 +321,7 @@ class ExprEngine : public SubEngine { ExplodedNode *Pred, ExplodedNodeSet &Dst, const CFGBlock *DstT, - const CFGBlock *DstF) override; + const CFGBlock *DstF); /// Called by CoreEngine. /// Used to generate successor nodes for temporary destructors depending @@ -314,7 +330,7 @@ class ExprEngine : public SubEngine { NodeBuilderContext &BldCtx, ExplodedNode *Pred, ExplodedNodeSet &Dst, const CFGBlock *DstT, - const CFGBlock *DstF) override; + const CFGBlock *DstF); /// Called by CoreEngine. Used to processing branching behavior /// at static initializers. @@ -323,27 +339,27 @@ class ExprEngine : public SubEngine { ExplodedNode *Pred, ExplodedNodeSet &Dst, const CFGBlock *DstT, - const CFGBlock *DstF) override; + const CFGBlock *DstF); /// processIndirectGoto - Called by CoreEngine. Used to generate successor /// nodes by processing the 'effects' of a computed goto jump. - void processIndirectGoto(IndirectGotoNodeBuilder& builder) override; + void processIndirectGoto(IndirectGotoNodeBuilder& builder); /// ProcessSwitch - Called by CoreEngine. Used to generate successor /// nodes by processing the 'effects' of a switch statement. - void processSwitch(SwitchNodeBuilder& builder) override; + void processSwitch(SwitchNodeBuilder& builder); /// Called by CoreEngine. Used to notify checkers that processing a /// function has begun. Called for both inlined and and top-level functions. void processBeginOfFunction(NodeBuilderContext &BC, ExplodedNode *Pred, ExplodedNodeSet &Dst, - const BlockEdge &L) override; + const BlockEdge &L); /// Called by CoreEngine. Used to notify checkers that processing a /// function has ended. Called for both inlined and and top-level functions. void processEndOfFunction(NodeBuilderContext& BC, ExplodedNode *Pred, - const ReturnStmt *RS = nullptr) override; + const ReturnStmt *RS = nullptr); /// Remove dead bindings/symbols before exiting a function. void removeDeadOnEndOfFunction(NodeBuilderContext& BC, @@ -352,19 +368,19 @@ class ExprEngine : public SubEngine { /// Generate the entry node of the callee. void processCallEnter(NodeBuilderContext& BC, CallEnter CE, - ExplodedNode *Pred) override; + ExplodedNode *Pred); /// Generate the sequence of nodes that simulate the call exit and the post /// visit for CallExpr. - void processCallExit(ExplodedNode *Pred) override; + void processCallExit(ExplodedNode *Pred); /// Called by CoreEngine when the analysis worklist has terminated. - void processEndWorklist() override; + void processEndWorklist(); /// evalAssume - Callback function invoked by the ConstraintManager when /// making assumptions about state values. ProgramStateRef processAssume(ProgramStateRef state, SVal cond, - bool assumption) override; + bool assumption); /// processRegionChanges - Called by ProgramStateManager whenever a change is made /// to the store. Used to update checkers that track region values. @@ -374,14 +390,21 @@ class ExprEngine : public SubEngine { ArrayRef ExplicitRegions, ArrayRef Regions, const LocationContext *LCtx, - const CallEvent *Call) override; + const CallEvent *Call); + + inline ProgramStateRef + processRegionChange(ProgramStateRef state, + const MemRegion* MR, + const LocationContext *LCtx) { + return processRegionChanges(state, nullptr, MR, MR, LCtx, nullptr); + } /// printJson - Called by ProgramStateManager to print checker-specific data. void printJson(raw_ostream &Out, ProgramStateRef State, const LocationContext *LCtx, const char *NL, - unsigned int Space, bool IsDot) const override; + unsigned int Space, bool IsDot) const; - ProgramStateManager &getStateManager() override { return StateMgr; } + ProgramStateManager &getStateManager() { return StateMgr; } StoreManager &getStoreManager() { return StateMgr.getStoreManager(); } @@ -608,23 +631,11 @@ class ExprEngine : public SubEngine { const ConstructionContextItem &Item, const LocationContext *LC); -protected: - /// evalBind - Handle the semantics of binding a value to a specific location. - /// This method is used by evalStore, VisitDeclStmt, and others. - void evalBind(ExplodedNodeSet &Dst, const Stmt *StoreE, ExplodedNode *Pred, - SVal location, SVal Val, bool atDeclInit = false, - const ProgramPoint *PP = nullptr); - /// Call PointerEscape callback when a value escapes as a result of bind. ProgramStateRef processPointerEscapedOnBind( ProgramStateRef State, ArrayRef> LocAndVals, const LocationContext *LCtx, PointerEscapeKind Kind, - const CallEvent *Call) override; - - ProgramStateRef - processPointerEscapedOnBind(ProgramStateRef State, - SVal Loc, SVal Val, - const LocationContext *LCtx); + const CallEvent *Call); /// Call PointerEscape callback when a value escapes as a result of /// region invalidation. @@ -634,7 +645,19 @@ class ExprEngine : public SubEngine { const InvalidatedSymbols *Invalidated, ArrayRef ExplicitRegions, const CallEvent *Call, - RegionAndSymbolInvalidationTraits &ITraits) override; + RegionAndSymbolInvalidationTraits &ITraits); + +private: + /// evalBind - Handle the semantics of binding a value to a specific location. + /// This method is used by evalStore, VisitDeclStmt, and others. + void evalBind(ExplodedNodeSet &Dst, const Stmt *StoreE, ExplodedNode *Pred, + SVal location, SVal Val, bool atDeclInit = false, + const ProgramPoint *PP = nullptr); + + ProgramStateRef + processPointerEscapedOnBind(ProgramStateRef State, + SVal Loc, SVal Val, + const LocationContext *LCtx); /// A simple wrapper when you only need to notify checkers of pointer-escape /// of some values. diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h index ecb61bffe3d95..a0d7db6dd860c 100644 --- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h +++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h @@ -39,7 +39,7 @@ class CallEvent; class CallEventManager; typedef std::unique_ptr(*ConstraintManagerCreator)( - ProgramStateManager &, SubEngine *); + ProgramStateManager &, ExprEngine *); typedef std::unique_ptr(*StoreManagerCreator)( ProgramStateManager &); @@ -460,8 +460,8 @@ class ProgramStateManager { friend class ProgramState; friend void ProgramStateRelease(const ProgramState *state); private: - /// Eng - The SubEngine that owns this state manager. - SubEngine *Eng; /* Can be null. */ + /// Eng - The ExprEngine that owns this state manager. + ExprEngine *Eng; /* Can be null. */ EnvironmentManager EnvMgr; std::unique_ptr StoreMgr; @@ -493,7 +493,7 @@ class ProgramStateManager { StoreManagerCreator CreateStoreManager, ConstraintManagerCreator CreateConstraintManager, llvm::BumpPtrAllocator& alloc, - SubEngine *subeng); + ExprEngine *expreng); ~ProgramStateManager(); @@ -534,7 +534,7 @@ class ProgramStateManager { StoreManager &getStoreManager() { return *StoreMgr; } ConstraintManager &getConstraintManager() { return *ConstraintMgr; } - SubEngine &getOwningEngine() { return *Eng; } + ExprEngine &getOwningEngine() { return *Eng; } ProgramStateRef removeDeadBindingsFromEnvironmentAndStore(ProgramStateRef St, diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/RangedConstraintManager.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/RangedConstraintManager.h index a9ca3451d8f3e..a42eebd7d4e81 100644 --- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/RangedConstraintManager.h +++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/RangedConstraintManager.h @@ -30,6 +30,10 @@ class Range : public std::pair { : std::pair(&from, &to) { assert(from <= to); } + + Range(const llvm::APSInt &point) + : std::pair(&point, &point) {} + bool Includes(const llvm::APSInt &v) const { return *first <= v && v <= *second; } @@ -89,6 +93,9 @@ class RangeSet { RangeSet(Factory &F, const llvm::APSInt &from, const llvm::APSInt &to) : ranges(F.add(F.getEmptySet(), Range(from, to))) {} + /// Construct a new RangeSet representing the given point as a range. + RangeSet(Factory &F, const llvm::APSInt &point) : RangeSet(F, point, point) {} + /// Profile - Generates a hash profile of this RangeSet for use /// by FoldingSet. void Profile(llvm::FoldingSetNodeID &ID) const { ranges.Profile(ID); } @@ -100,14 +107,17 @@ class RangeSet { return ranges.isSingleton() ? ranges.begin()->getConcreteValue() : nullptr; } + /// Get a minimal value covered by the ranges in the set + const llvm::APSInt &getMinValue() const; + /// Get a maximal value covered by the ranges in the set + const llvm::APSInt &getMaxValue() const; + private: void IntersectInRange(BasicValueFactory &BV, Factory &F, const llvm::APSInt &Lower, const llvm::APSInt &Upper, PrimRangeSet &newRanges, PrimRangeSet::iterator &i, PrimRangeSet::iterator &e) const; - const llvm::APSInt &getMinValue() const; - bool pin(llvm::APSInt &Lower, llvm::APSInt &Upper) const; public: @@ -124,7 +134,6 @@ class RangeSet { } }; - class ConstraintRange {}; using ConstraintRangeTy = llvm::ImmutableMap; @@ -137,8 +146,8 @@ struct ProgramStateTrait class RangedConstraintManager : public SimpleConstraintManager { public: - RangedConstraintManager(SubEngine *SE, SValBuilder &SB) - : SimpleConstraintManager(SE, SB) {} + RangedConstraintManager(ExprEngine *EE, SValBuilder &SB) + : SimpleConstraintManager(EE, SB) {} ~RangedConstraintManager() override; diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SMTConstraintManager.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SMTConstraintManager.h index 294a45b214d7e..6a0f5f10874e3 100644 --- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SMTConstraintManager.h +++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SMTConstraintManager.h @@ -31,8 +31,9 @@ class SMTConstraintManager : public clang::ento::SimpleConstraintManager { mutable llvm::SMTSolverRef Solver = llvm::CreateZ3Solver(); public: - SMTConstraintManager(clang::ento::SubEngine *SE, clang::ento::SValBuilder &SB) - : SimpleConstraintManager(SE, SB) {} + SMTConstraintManager(clang::ento::ExprEngine *EE, + clang::ento::SValBuilder &SB) + : SimpleConstraintManager(EE, SB) {} virtual ~SMTConstraintManager() = default; //===------------------------------------------------------------------===// diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SimpleConstraintManager.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SimpleConstraintManager.h index 6bf5e94afdbb6..87e927f5b4800 100644 --- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SimpleConstraintManager.h +++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SimpleConstraintManager.h @@ -21,12 +21,12 @@ namespace clang { namespace ento { class SimpleConstraintManager : public ConstraintManager { - SubEngine *SU; + ExprEngine *EE; SValBuilder &SVB; public: - SimpleConstraintManager(SubEngine *subengine, SValBuilder &SB) - : SU(subengine), SVB(SB) {} + SimpleConstraintManager(ExprEngine *exprengine, SValBuilder &SB) + : EE(exprengine), SVB(SB) {} ~SimpleConstraintManager() override; diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SubEngine.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SubEngine.h deleted file mode 100644 index a7f3c28d4373a..0000000000000 --- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SubEngine.h +++ /dev/null @@ -1,178 +0,0 @@ -//== SubEngine.h - Interface of the subengine of CoreEngine --------*- C++ -*-// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -// -// This file defines the interface of a subengine of the CoreEngine. -// -//===----------------------------------------------------------------------===// -#ifndef LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_SUBENGINE_H -#define LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_SUBENGINE_H - -#include "clang/Analysis/ProgramPoint.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/Store.h" -#include "clang/StaticAnalyzer/Core/CheckerManager.h" - -namespace clang { - -class CFGBlock; -class CFGElement; -class LocationContext; -class Stmt; - -namespace cross_tu { -class CrossTranslationUnitContext; -} - -namespace ento { - -struct NodeBuilderContext; -class AnalysisManager; -class ExplodedNodeSet; -class ExplodedNode; -class ProgramState; -class ProgramStateManager; -class BlockCounter; -class BranchNodeBuilder; -class IndirectGotoNodeBuilder; -class SwitchNodeBuilder; -class EndOfFunctionNodeBuilder; -class NodeBuilderWithSinks; -class MemRegion; - -class SubEngine { - virtual void anchor(); -public: - virtual ~SubEngine() {} - - virtual ProgramStateRef getInitialState(const LocationContext *InitLoc) = 0; - - virtual AnalysisManager &getAnalysisManager() = 0; - - virtual cross_tu::CrossTranslationUnitContext * - getCrossTranslationUnitContext() = 0; - - virtual ProgramStateManager &getStateManager() = 0; - - /// Called by CoreEngine. Used to generate new successor - /// nodes by processing the 'effects' of a block-level statement. - virtual void processCFGElement(const CFGElement E, ExplodedNode* Pred, - unsigned StmtIdx, NodeBuilderContext *Ctx)=0; - - /// Called by CoreEngine when it starts processing a CFGBlock. The - /// SubEngine is expected to populate dstNodes with new nodes representing - /// updated analysis state, or generate no nodes at all if it doesn't. - virtual void processCFGBlockEntrance(const BlockEdge &L, - NodeBuilderWithSinks &nodeBuilder, - ExplodedNode *Pred) = 0; - - /// Called by CoreEngine. Used to generate successor - /// nodes by processing the 'effects' of a branch condition. - virtual void processBranch(const Stmt *Condition, - NodeBuilderContext& BuilderCtx, - ExplodedNode *Pred, - ExplodedNodeSet &Dst, - const CFGBlock *DstT, - const CFGBlock *DstF) = 0; - - /// Called by CoreEngine. - /// Used to generate successor nodes for temporary destructors depending - /// on whether the corresponding constructor was visited. - virtual void processCleanupTemporaryBranch(const CXXBindTemporaryExpr *BTE, - NodeBuilderContext &BldCtx, - ExplodedNode *Pred, - ExplodedNodeSet &Dst, - const CFGBlock *DstT, - const CFGBlock *DstF) = 0; - - /// Called by CoreEngine. Used to processing branching behavior - /// at static initializers. - virtual void processStaticInitializer(const DeclStmt *DS, - NodeBuilderContext& BuilderCtx, - ExplodedNode *Pred, - ExplodedNodeSet &Dst, - const CFGBlock *DstT, - const CFGBlock *DstF) = 0; - - /// Called by CoreEngine. Used to generate successor - /// nodes by processing the 'effects' of a computed goto jump. - virtual void processIndirectGoto(IndirectGotoNodeBuilder& builder) = 0; - - /// Called by CoreEngine. Used to generate successor - /// nodes by processing the 'effects' of a switch statement. - virtual void processSwitch(SwitchNodeBuilder& builder) = 0; - - /// Called by CoreEngine. Used to notify checkers that processing a - /// function has begun. Called for both inlined and and top-level functions. - virtual void processBeginOfFunction(NodeBuilderContext &BC, - ExplodedNode *Pred, - ExplodedNodeSet &Dst, - const BlockEdge &L) = 0; - - /// Called by CoreEngine. Used to notify checkers that processing a - /// function has ended. Called for both inlined and and top-level functions. - virtual void processEndOfFunction(NodeBuilderContext& BC, - ExplodedNode *Pred, - const ReturnStmt *RS = nullptr) = 0; - - // Generate the entry node of the callee. - virtual void processCallEnter(NodeBuilderContext& BC, CallEnter CE, - ExplodedNode *Pred) = 0; - - // Generate the first post callsite node. - virtual void processCallExit(ExplodedNode *Pred) = 0; - - /// Called by ConstraintManager. Used to call checker-specific - /// logic for handling assumptions on symbolic values. - virtual ProgramStateRef processAssume(ProgramStateRef state, - SVal cond, bool assumption) = 0; - - /// processRegionChanges - Called by ProgramStateManager whenever a change is - /// made to the store. Used to update checkers that track region values. - virtual ProgramStateRef - processRegionChanges(ProgramStateRef state, - const InvalidatedSymbols *invalidated, - ArrayRef ExplicitRegions, - ArrayRef Regions, - const LocationContext *LCtx, - const CallEvent *Call) = 0; - - - inline ProgramStateRef - processRegionChange(ProgramStateRef state, - const MemRegion* MR, - const LocationContext *LCtx) { - return processRegionChanges(state, nullptr, MR, MR, LCtx, nullptr); - } - - virtual ProgramStateRef processPointerEscapedOnBind( - ProgramStateRef State, ArrayRef> LocAndVals, - const LocationContext *LCtx, PointerEscapeKind Kind, - const CallEvent *Call) = 0; - - virtual ProgramStateRef - notifyCheckersOfPointerEscape(ProgramStateRef State, - const InvalidatedSymbols *Invalidated, - ArrayRef ExplicitRegions, - const CallEvent *Call, - RegionAndSymbolInvalidationTraits &HTraits) = 0; - - /// printJson - Called by ProgramStateManager to print checker-specific data. - virtual void printJson(raw_ostream &Out, ProgramStateRef State, - const LocationContext *LCtx, const char *NL, - unsigned int Space, bool IsDot) const = 0; - - /// Called by CoreEngine when the analysis worklist is either empty or the - // maximum number of analysis steps have been reached. - virtual void processEndWorklist() = 0; -}; - -} // end GR namespace - -} // end clang namespace - -#endif diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h index 2c505995bee0a..390ced8c29f8f 100644 --- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h +++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h @@ -326,136 +326,83 @@ class BinarySymExpr : public SymExpr { Kind k = SE->getKind(); return k >= BEGIN_BINARYSYMEXPRS && k <= END_BINARYSYMEXPRS; } -}; - -/// Represents a symbolic expression like 'x' + 3. -class SymIntExpr : public BinarySymExpr { - const SymExpr *LHS; - const llvm::APSInt& RHS; -public: - SymIntExpr(const SymExpr *lhs, BinaryOperator::Opcode op, - const llvm::APSInt &rhs, QualType t) - : BinarySymExpr(SymIntExprKind, op, t), LHS(lhs), RHS(rhs) { - assert(lhs); +protected: + static unsigned computeOperandComplexity(const SymExpr *Value) { + return Value->computeComplexity(); } - - void dumpToStream(raw_ostream &os) const override; - - const SymExpr *getLHS() const { return LHS; } - const llvm::APSInt &getRHS() const { return RHS; } - - unsigned computeComplexity() const override { - if (Complexity == 0) - Complexity = 1 + LHS->computeComplexity(); - return Complexity; + static unsigned computeOperandComplexity(const llvm::APSInt &Value) { + return 1; } - static void Profile(llvm::FoldingSetNodeID& ID, const SymExpr *lhs, - BinaryOperator::Opcode op, const llvm::APSInt& rhs, - QualType t) { - ID.AddInteger((unsigned) SymIntExprKind); - ID.AddPointer(lhs); - ID.AddInteger(op); - ID.AddPointer(&rhs); - ID.Add(t); + static const llvm::APSInt *getPointer(const llvm::APSInt &Value) { + return &Value; } + static const SymExpr *getPointer(const SymExpr *Value) { return Value; } - void Profile(llvm::FoldingSetNodeID& ID) override { - Profile(ID, LHS, getOpcode(), RHS, getType()); - } - - // Implement isa support. - static bool classof(const SymExpr *SE) { - return SE->getKind() == SymIntExprKind; - } + static void dumpToStreamImpl(raw_ostream &os, const SymExpr *Value); + static void dumpToStreamImpl(raw_ostream &os, const llvm::APSInt &Value); + static void dumpToStreamImpl(raw_ostream &os, BinaryOperator::Opcode op); }; -/// Represents a symbolic expression like 3 - 'x'. -class IntSymExpr : public BinarySymExpr { - const llvm::APSInt& LHS; - const SymExpr *RHS; +/// Template implementation for all binary symbolic expressions +template +class BinarySymExprImpl : public BinarySymExpr { + LHSTYPE LHS; + RHSTYPE RHS; public: - IntSymExpr(const llvm::APSInt &lhs, BinaryOperator::Opcode op, - const SymExpr *rhs, QualType t) - : BinarySymExpr(IntSymExprKind, op, t), LHS(lhs), RHS(rhs) { - assert(rhs); + BinarySymExprImpl(LHSTYPE lhs, BinaryOperator::Opcode op, RHSTYPE rhs, + QualType t) + : BinarySymExpr(ClassKind, op, t), LHS(lhs), RHS(rhs) { + assert(getPointer(lhs)); + assert(getPointer(rhs)); } - void dumpToStream(raw_ostream &os) const override; + void dumpToStream(raw_ostream &os) const override { + dumpToStreamImpl(os, LHS); + dumpToStreamImpl(os, getOpcode()); + dumpToStreamImpl(os, RHS); + } - const SymExpr *getRHS() const { return RHS; } - const llvm::APSInt &getLHS() const { return LHS; } + LHSTYPE getLHS() const { return LHS; } + RHSTYPE getRHS() const { return RHS; } unsigned computeComplexity() const override { if (Complexity == 0) - Complexity = 1 + RHS->computeComplexity(); + Complexity = + computeOperandComplexity(RHS) + computeOperandComplexity(LHS); return Complexity; } - static void Profile(llvm::FoldingSetNodeID& ID, const llvm::APSInt& lhs, - BinaryOperator::Opcode op, const SymExpr *rhs, - QualType t) { - ID.AddInteger((unsigned) IntSymExprKind); - ID.AddPointer(&lhs); + static void Profile(llvm::FoldingSetNodeID &ID, LHSTYPE lhs, + BinaryOperator::Opcode op, RHSTYPE rhs, QualType t) { + ID.AddInteger((unsigned)ClassKind); + ID.AddPointer(getPointer(lhs)); ID.AddInteger(op); - ID.AddPointer(rhs); + ID.AddPointer(getPointer(rhs)); ID.Add(t); } - void Profile(llvm::FoldingSetNodeID& ID) override { + void Profile(llvm::FoldingSetNodeID &ID) override { Profile(ID, LHS, getOpcode(), RHS, getType()); } // Implement isa support. - static bool classof(const SymExpr *SE) { - return SE->getKind() == IntSymExprKind; - } + static bool classof(const SymExpr *SE) { return SE->getKind() == ClassKind; } }; -/// Represents a symbolic expression like 'x' + 'y'. -class SymSymExpr : public BinarySymExpr { - const SymExpr *LHS; - const SymExpr *RHS; - -public: - SymSymExpr(const SymExpr *lhs, BinaryOperator::Opcode op, const SymExpr *rhs, - QualType t) - : BinarySymExpr(SymSymExprKind, op, t), LHS(lhs), RHS(rhs) { - assert(lhs); - assert(rhs); - } - - const SymExpr *getLHS() const { return LHS; } - const SymExpr *getRHS() const { return RHS; } - - void dumpToStream(raw_ostream &os) const override; - - unsigned computeComplexity() const override { - if (Complexity == 0) - Complexity = RHS->computeComplexity() + LHS->computeComplexity(); - return Complexity; - } - - static void Profile(llvm::FoldingSetNodeID& ID, const SymExpr *lhs, - BinaryOperator::Opcode op, const SymExpr *rhs, QualType t) { - ID.AddInteger((unsigned) SymSymExprKind); - ID.AddPointer(lhs); - ID.AddInteger(op); - ID.AddPointer(rhs); - ID.Add(t); - } +/// Represents a symbolic expression like 'x' + 3. +using SymIntExpr = BinarySymExprImpl; - void Profile(llvm::FoldingSetNodeID& ID) override { - Profile(ID, LHS, getOpcode(), RHS, getType()); - } +/// Represents a symbolic expression like 3 - 'x'. +using IntSymExpr = BinarySymExprImpl; - // Implement isa support. - static bool classof(const SymExpr *SE) { - return SE->getKind() == SymSymExprKind; - } -}; +/// Represents a symbolic expression like 'x' + 'y'. +using SymSymExpr = BinarySymExprImpl; class SymbolManager { using DataSetTy = llvm::FoldingSet; diff --git a/clang/include/clang/StaticAnalyzer/Frontend/CheckerRegistry.h b/clang/include/clang/StaticAnalyzer/Frontend/CheckerRegistry.h index 4e98ba2e10d23..c3494d0ebeefd 100644 --- a/clang/include/clang/StaticAnalyzer/Frontend/CheckerRegistry.h +++ b/clang/include/clang/StaticAnalyzer/Frontend/CheckerRegistry.h @@ -13,6 +13,7 @@ #include "llvm/ADT/SetVector.h" #include "llvm/ADT/StringMap.h" #include "llvm/ADT/StringRef.h" +#include "llvm/Support/raw_ostream.h" #include #include @@ -133,6 +134,9 @@ class CheckerRegistry { DevelopmentStatus == "released") && "Invalid development status!"); } + + LLVM_DUMP_METHOD void dump() const { dumpToStream(llvm::errs()); } + LLVM_DUMP_METHOD void dumpToStream(llvm::raw_ostream &Out) const; }; using CmdLineOptionList = llvm::SmallVector; @@ -189,6 +193,9 @@ class CheckerRegistry { // Used for lower_bound. explicit CheckerInfo(StringRef FullName) : FullName(FullName) {} + + LLVM_DUMP_METHOD void dump() const { dumpToStream(llvm::errs()); } + LLVM_DUMP_METHOD void dumpToStream(llvm::raw_ostream &Out) const; }; using StateFromCmdLine = CheckerInfo::StateFromCmdLine; @@ -206,6 +213,9 @@ class CheckerRegistry { } explicit PackageInfo(StringRef FullName) : FullName(FullName) {} + + LLVM_DUMP_METHOD void dump() const { dumpToStream(llvm::errs()); } + LLVM_DUMP_METHOD void dumpToStream(llvm::raw_ostream &Out) const; }; using PackageInfoList = llvm::SmallVector; diff --git a/clang/include/clang/Testing/CommandLineArgs.h b/clang/include/clang/Testing/CommandLineArgs.h new file mode 100644 index 0000000000000..0d2267f63ac54 --- /dev/null +++ b/clang/include/clang/Testing/CommandLineArgs.h @@ -0,0 +1,37 @@ +//===--- CommandLineArgs.h ------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file defines language options for Clang unittests. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TESTING_COMMANDLINEARGS_H +#define LLVM_CLANG_TESTING_COMMANDLINEARGS_H + +#include +#include + +namespace clang { + +enum TestLanguage { + Lang_C, + Lang_C89, + Lang_CXX, + Lang_CXX11, + Lang_CXX14, + Lang_CXX17, + Lang_CXX2a, + Lang_OpenCL, + Lang_OBJCXX +}; + +std::vector getCommandLineArgsForTesting(TestLanguage Lang); + +} // end namespace clang + +#endif diff --git a/clang/include/clang/Tooling/Syntax/Nodes.h b/clang/include/clang/Tooling/Syntax/Nodes.h index f4d482bb848c6..e240becbf883a 100644 --- a/clang/include/clang/Tooling/Syntax/Nodes.h +++ b/clang/include/clang/Tooling/Syntax/Nodes.h @@ -40,6 +40,9 @@ enum class NodeKind : uint16_t { // Expressions. UnknownExpression, + PrefixUnaryOperatorExpression, + PostfixUnaryOperatorExpression, + BinaryOperatorExpression, // Statements. UnknownStatement, @@ -104,6 +107,11 @@ enum class NodeRole : uint8_t { BodyStatement, // Roles specific to particular node kinds. + UnaryOperatorExpression_operatorToken, + UnaryOperatorExpression_operand, + BinaryOperatorExpression_leftHandSide, + BinaryOperatorExpression_operatorToken, + BinaryOperatorExpression_rightHandSide, CaseStatement_value, IfStatement_thenStatement, IfStatement_elseKeyword, @@ -158,6 +166,68 @@ class UnknownExpression final : public Expression { } }; +/// An abstract class for prefix and postfix unary operators. +class UnaryOperatorExpression : public Expression { +public: + UnaryOperatorExpression(NodeKind K) : Expression(K) {} + static bool classof(const Node *N) { + return N->kind() == NodeKind::PrefixUnaryOperatorExpression || + N->kind() == NodeKind::PostfixUnaryOperatorExpression; + } + syntax::Leaf *operatorToken(); + syntax::Expression *operand(); +}; + +/// +/// +/// For example: +/// +a -b +/// !c not c +/// ~d compl d +/// *e &f +/// ++h --h +/// __real i __imag i +class PrefixUnaryOperatorExpression final : public UnaryOperatorExpression { +public: + PrefixUnaryOperatorExpression() + : UnaryOperatorExpression(NodeKind::PrefixUnaryOperatorExpression) {} + static bool classof(const Node *N) { + return N->kind() == NodeKind::PrefixUnaryOperatorExpression; + } +}; + +/// +/// +/// For example: +/// a++ +/// b-- +class PostfixUnaryOperatorExpression final : public UnaryOperatorExpression { +public: + PostfixUnaryOperatorExpression() + : UnaryOperatorExpression(NodeKind::PostfixUnaryOperatorExpression) {} + static bool classof(const Node *N) { + return N->kind() == NodeKind::PostfixUnaryOperatorExpression; + } +}; + +/// +/// +/// For example: +/// a + b +/// a bitor 1 +/// a |= b +/// a and_eq b +class BinaryOperatorExpression final : public Expression { +public: + BinaryOperatorExpression() : Expression(NodeKind::BinaryOperatorExpression) {} + static bool classof(const Node *N) { + return N->kind() == NodeKind::BinaryOperatorExpression; + } + syntax::Expression *lhs(); + syntax::Leaf *operatorToken(); + syntax::Expression *rhs(); +}; + /// An abstract node for C++ statements, e.g. 'while', 'if', etc. /// FIXME: add accessors for semicolon of statements that have it. class Statement : public Tree { diff --git a/clang/include/clang/Tooling/Transformer/RewriteRule.h b/clang/include/clang/Tooling/Transformer/RewriteRule.h index 4a5e5556cdff0..0a961ccc475df 100644 --- a/clang/include/clang/Tooling/Transformer/RewriteRule.h +++ b/clang/include/clang/Tooling/Transformer/RewriteRule.h @@ -273,11 +273,13 @@ namespace detail { /// supports mixing matchers of different kinds. ast_matchers::internal::DynTypedMatcher buildMatcher(const RewriteRule &Rule); -/// Builds a set of matchers that cover the rule (one for each distinct node -/// matcher base kind: Stmt, Decl, etc.). Node-matchers for `QualType` and -/// `Type` are not permitted, since such nodes carry no source location -/// information and are therefore not relevant for rewriting. If any such -/// matchers are included, will return an empty vector. +/// Builds a set of matchers that cover the rule. +/// +/// One matcher is built for each distinct node matcher base kind: Stmt, Decl, +/// etc. Node-matchers for `QualType` and `Type` are not permitted, since such +/// nodes carry no source location information and are therefore not relevant +/// for rewriting. If any such matchers are included, will return an empty +/// vector. std::vector buildMatchers(const RewriteRule &Rule); diff --git a/clang/include/clang/module.modulemap b/clang/include/clang/module.modulemap index 15f891c153401..7549ff2e3bcd6 100644 --- a/clang/include/clang/module.modulemap +++ b/clang/include/clang/module.modulemap @@ -152,6 +152,12 @@ module Clang_StaticAnalyzer_Frontend { module * { export * } } +module Clang_Testing { + requires cplusplus + umbrella "Testing" + module * { export * } +} + module Clang_Tooling { requires cplusplus umbrella "Tooling" module * { export * } // FIXME: Exclude these headers to avoid pulling all of the AST matchers diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp index d2330f8e4f46f..18a695e33536a 100644 --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -10662,8 +10662,10 @@ QualType ASTContext::getIntTypeForBitwidth(unsigned DestWidth, /// getRealTypeForBitwidth - /// sets floating point QualTy according to specified bitwidth. /// Returns empty type if there is no appropriate target types. -QualType ASTContext::getRealTypeForBitwidth(unsigned DestWidth) const { - TargetInfo::RealType Ty = getTargetInfo().getRealTypeByWidth(DestWidth); +QualType ASTContext::getRealTypeForBitwidth(unsigned DestWidth, + bool ExplicitIEEE) const { + TargetInfo::RealType Ty = + getTargetInfo().getRealTypeByWidth(DestWidth, ExplicitIEEE); switch (Ty) { case TargetInfo::Float: return FloatTy; diff --git a/clang/lib/AST/ASTTypeTraits.cpp b/clang/lib/AST/ASTTypeTraits.cpp index 6404edd79679a..37e81e5813eca 100644 --- a/clang/lib/AST/ASTTypeTraits.cpp +++ b/clang/lib/AST/ASTTypeTraits.cpp @@ -27,6 +27,7 @@ const ASTNodeKind::KindInfo ASTNodeKind::AllKindInfo[] = { { NKI_None, "NestedNameSpecifierLoc" }, { NKI_None, "QualType" }, { NKI_None, "TypeLoc" }, + { NKI_None, "CXXBaseSpecifier" }, { NKI_None, "CXXCtorInitializer" }, { NKI_None, "NestedNameSpecifier" }, { NKI_None, "Decl" }, diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp index 3adbb7410c61f..a3edb63ab34f5 100644 --- a/clang/lib/AST/Decl.cpp +++ b/clang/lib/AST/Decl.cpp @@ -3227,6 +3227,15 @@ unsigned FunctionDecl::getBuiltinID(bool ConsiderWrapperFunctions) const { !(BuiltinID == Builtin::BIprintf || BuiltinID == Builtin::BImalloc)) return 0; + // As AMDGCN implementation of OpenMP does not have a device-side standard + // library, none of the predefined library functions except printf and malloc + // should be treated as a builtin i.e. 0 should be returned for them. + if (Context.getTargetInfo().getTriple().isAMDGCN() && + Context.getLangOpts().OpenMPIsDevice && + Context.BuiltinInfo.isPredefinedLibFunction(BuiltinID) && + !(BuiltinID == Builtin::BIprintf || BuiltinID == Builtin::BImalloc)) + return 0; + return BuiltinID; } diff --git a/clang/lib/AST/DeclPrinter.cpp b/clang/lib/AST/DeclPrinter.cpp index 27c61832fcf64..85143d4b968e1 100644 --- a/clang/lib/AST/DeclPrinter.cpp +++ b/clang/lib/AST/DeclPrinter.cpp @@ -289,12 +289,10 @@ void DeclPrinter::ProcessDeclGroup(SmallVectorImpl& Decls) { } void DeclPrinter::Print(AccessSpecifier AS) { - switch(AS) { - case AS_none: llvm_unreachable("No access specifier!"); - case AS_public: Out << "public"; break; - case AS_protected: Out << "protected"; break; - case AS_private: Out << "private"; break; - } + const auto AccessSpelling = getAccessSpelling(AS); + if (AccessSpelling.empty()) + llvm_unreachable("No access specifier!"); + Out << AccessSpelling; } void DeclPrinter::PrintConstructorInitializers(CXXConstructorDecl *CDecl, diff --git a/clang/lib/AST/Expr.cpp b/clang/lib/AST/Expr.cpp index 4c175fff64217..feb0517204c4b 100644 --- a/clang/lib/AST/Expr.cpp +++ b/clang/lib/AST/Expr.cpp @@ -2267,6 +2267,64 @@ Stmt *BlockExpr::getBody() { // Generic Expression Routines //===----------------------------------------------------------------------===// +bool Expr::isReadIfDiscardedInCPlusPlus11() const { + // In C++11, discarded-value expressions of a certain form are special, + // according to [expr]p10: + // The lvalue-to-rvalue conversion (4.1) is applied only if the + // expression is an lvalue of volatile-qualified type and it has + // one of the following forms: + if (!isGLValue() || !getType().isVolatileQualified()) + return false; + + const Expr *E = IgnoreParens(); + + // - id-expression (5.1.1), + if (isa(E)) + return true; + + // - subscripting (5.2.1), + if (isa(E)) + return true; + + // - class member access (5.2.5), + if (isa(E)) + return true; + + // - indirection (5.3.1), + if (auto *UO = dyn_cast(E)) + if (UO->getOpcode() == UO_Deref) + return true; + + if (auto *BO = dyn_cast(E)) { + // - pointer-to-member operation (5.5), + if (BO->isPtrMemOp()) + return true; + + // - comma expression (5.18) where the right operand is one of the above. + if (BO->getOpcode() == BO_Comma) + return BO->getRHS()->isReadIfDiscardedInCPlusPlus11(); + } + + // - conditional expression (5.16) where both the second and the third + // operands are one of the above, or + if (auto *CO = dyn_cast(E)) + return CO->getTrueExpr()->isReadIfDiscardedInCPlusPlus11() && + CO->getFalseExpr()->isReadIfDiscardedInCPlusPlus11(); + // The related edge case of "*x ?: *x". + if (auto *BCO = + dyn_cast(E)) { + if (auto *OVE = dyn_cast(BCO->getTrueExpr())) + return OVE->getSourceExpr()->isReadIfDiscardedInCPlusPlus11() && + BCO->getFalseExpr()->isReadIfDiscardedInCPlusPlus11(); + } + + // Objective-C++ extensions to the rule. + if (isa(E) || isa(E)) + return true; + + return false; +} + /// isUnusedResultAWarning - Return true if this immediate expression should /// be warned about if the result is unused. If so, fill in Loc and Ranges /// with location to warn on and the source range[s] to report with the @@ -2555,20 +2613,31 @@ bool Expr::isUnusedResultAWarning(const Expr *&WarnE, SourceLocation &Loc, } case CXXFunctionalCastExprClass: case CStyleCastExprClass: { - // Ignore an explicit cast to void unless the operand is a non-trivial - // volatile lvalue. + // Ignore an explicit cast to void, except in C++98 if the operand is a + // volatile glvalue for which we would trigger an implicit read in any + // other language mode. (Such an implicit read always happens as part of + // the lvalue conversion in C, and happens in C++ for expressions of all + // forms where it seems likely the user intended to trigger a volatile + // load.) const CastExpr *CE = cast(this); + const Expr *SubE = CE->getSubExpr()->IgnoreParens(); if (CE->getCastKind() == CK_ToVoid) { - if (CE->getSubExpr()->isGLValue() && - CE->getSubExpr()->getType().isVolatileQualified()) { - const DeclRefExpr *DRE = - dyn_cast(CE->getSubExpr()->IgnoreParens()); - if (!(DRE && isa(DRE->getDecl()) && - cast(DRE->getDecl())->hasLocalStorage()) && - !isa(CE->getSubExpr()->IgnoreParens())) { - return CE->getSubExpr()->isUnusedResultAWarning(WarnE, Loc, - R1, R2, Ctx); - } + if (Ctx.getLangOpts().CPlusPlus && !Ctx.getLangOpts().CPlusPlus11 && + SubE->isReadIfDiscardedInCPlusPlus11()) { + // Suppress the "unused value" warning for idiomatic usage of + // '(void)var;' used to suppress "unused variable" warnings. + if (auto *DRE = dyn_cast(SubE)) + if (auto *VD = dyn_cast(DRE->getDecl())) + if (!VD->isExternallyVisible()) + return false; + + // The lvalue-to-rvalue conversion would have no effect for an array. + // It's implausible that the programmer expected this to result in a + // volatile array load, so don't warn. + if (SubE->getType()->isArrayType()) + return false; + + return SubE->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx); } return false; } diff --git a/clang/lib/AST/JSONNodeDumper.cpp b/clang/lib/AST/JSONNodeDumper.cpp index 91281fb44bfa9..8edfed673ce21 100644 --- a/clang/lib/AST/JSONNodeDumper.cpp +++ b/clang/lib/AST/JSONNodeDumper.cpp @@ -1,5 +1,6 @@ #include "clang/AST/JSONNodeDumper.h" #include "clang/Basic/SourceManager.h" +#include "clang/Basic/Specifiers.h" #include "clang/Lex/Lexer.h" #include "llvm/ADT/StringSwitch.h" @@ -465,13 +466,10 @@ JSONNodeDumper::createCXXRecordDefinitionData(const CXXRecordDecl *RD) { #undef FIELD2 std::string JSONNodeDumper::createAccessSpecifier(AccessSpecifier AS) { - switch (AS) { - case AS_none: return "none"; - case AS_private: return "private"; - case AS_protected: return "protected"; - case AS_public: return "public"; - } - llvm_unreachable("Unknown access specifier"); + const auto AccessSpelling = getAccessSpelling(AS); + if (AccessSpelling.empty()) + return "none"; + return AccessSpelling.str(); } llvm::json::Object diff --git a/clang/lib/AST/OpenMPClause.cpp b/clang/lib/AST/OpenMPClause.cpp index 14c4c78e5f39f..fa1c80fc6bbf9 100644 --- a/clang/lib/AST/OpenMPClause.cpp +++ b/clang/lib/AST/OpenMPClause.cpp @@ -136,6 +136,7 @@ const OMPClauseWithPreInit *OMPClauseWithPreInit::get(const OMPClause *C) { case OMPC_to: case OMPC_from: case OMPC_use_device_ptr: + case OMPC_use_device_addr: case OMPC_is_device_ptr: case OMPC_unified_address: case OMPC_unified_shared_memory: @@ -227,6 +228,7 @@ const OMPClauseWithPostUpdate *OMPClauseWithPostUpdate::get(const OMPClause *C) case OMPC_to: case OMPC_from: case OMPC_use_device_ptr: + case OMPC_use_device_addr: case OMPC_is_device_ptr: case OMPC_unified_address: case OMPC_unified_shared_memory: @@ -1198,6 +1200,53 @@ OMPUseDevicePtrClause::CreateEmpty(const ASTContext &C, return new (Mem) OMPUseDevicePtrClause(Sizes); } +OMPUseDeviceAddrClause * +OMPUseDeviceAddrClause::Create(const ASTContext &C, const OMPVarListLocTy &Locs, + ArrayRef Vars, + ArrayRef Declarations, + MappableExprComponentListsRef ComponentLists) { + OMPMappableExprListSizeTy Sizes; + Sizes.NumVars = Vars.size(); + Sizes.NumUniqueDeclarations = getUniqueDeclarationsTotalNumber(Declarations); + Sizes.NumComponentLists = ComponentLists.size(); + Sizes.NumComponents = getComponentsTotalNumber(ComponentLists); + + // We need to allocate: + // 3 x NumVars x Expr* - we have an original list expression for each clause + // list entry and an equal number of private copies and inits. + // NumUniqueDeclarations x ValueDecl* - unique base declarations associated + // with each component list. + // (NumUniqueDeclarations + NumComponentLists) x unsigned - we specify the + // number of lists for each unique declaration and the size of each component + // list. + // NumComponents x MappableComponent - the total of all the components in all + // the lists. + void *Mem = C.Allocate( + totalSizeToAlloc( + Sizes.NumVars, Sizes.NumUniqueDeclarations, + Sizes.NumUniqueDeclarations + Sizes.NumComponentLists, + Sizes.NumComponents)); + + auto *Clause = new (Mem) OMPUseDeviceAddrClause(Locs, Sizes); + + Clause->setVarRefs(Vars); + Clause->setClauseInfo(Declarations, ComponentLists); + return Clause; +} + +OMPUseDeviceAddrClause * +OMPUseDeviceAddrClause::CreateEmpty(const ASTContext &C, + const OMPMappableExprListSizeTy &Sizes) { + void *Mem = C.Allocate( + totalSizeToAlloc( + Sizes.NumVars, Sizes.NumUniqueDeclarations, + Sizes.NumUniqueDeclarations + Sizes.NumComponentLists, + Sizes.NumComponents)); + return new (Mem) OMPUseDeviceAddrClause(Sizes); +} + OMPIsDevicePtrClause * OMPIsDevicePtrClause::Create(const ASTContext &C, const OMPVarListLocTy &Locs, ArrayRef Vars, @@ -1934,6 +1983,15 @@ void OMPClausePrinter::VisitOMPUseDevicePtrClause(OMPUseDevicePtrClause *Node) { } } +void OMPClausePrinter::VisitOMPUseDeviceAddrClause( + OMPUseDeviceAddrClause *Node) { + if (!Node->varlist_empty()) { + OS << "use_device_addr"; + VisitOMPClauseList(Node, '('); + OS << ")"; + } +} + void OMPClausePrinter::VisitOMPIsDevicePtrClause(OMPIsDevicePtrClause *Node) { if (!Node->varlist_empty()) { OS << "is_device_ptr"; diff --git a/clang/lib/AST/StmtProfile.cpp b/clang/lib/AST/StmtProfile.cpp index bd2eeb699e65e..e573c045cb7ab 100644 --- a/clang/lib/AST/StmtProfile.cpp +++ b/clang/lib/AST/StmtProfile.cpp @@ -784,6 +784,10 @@ void OMPClauseProfiler::VisitOMPUseDevicePtrClause( const OMPUseDevicePtrClause *C) { VisitOMPClauseList(C); } +void OMPClauseProfiler::VisitOMPUseDeviceAddrClause( + const OMPUseDeviceAddrClause *C) { + VisitOMPClauseList(C); +} void OMPClauseProfiler::VisitOMPIsDevicePtrClause( const OMPIsDevicePtrClause *C) { VisitOMPClauseList(C); diff --git a/clang/lib/AST/TextNodeDumper.cpp b/clang/lib/AST/TextNodeDumper.cpp index 9dbe557075391..1b640a8cbe40e 100644 --- a/clang/lib/AST/TextNodeDumper.cpp +++ b/clang/lib/AST/TextNodeDumper.cpp @@ -17,6 +17,7 @@ #include "clang/AST/LocInfoType.h" #include "clang/Basic/Module.h" #include "clang/Basic/SourceManager.h" +#include "clang/Basic/Specifiers.h" using namespace clang; @@ -436,19 +437,10 @@ void TextNodeDumper::dumpName(const NamedDecl *ND) { } void TextNodeDumper::dumpAccessSpecifier(AccessSpecifier AS) { - switch (AS) { - case AS_none: - break; - case AS_public: - OS << "public"; - break; - case AS_protected: - OS << "protected"; - break; - case AS_private: - OS << "private"; - break; - } + const auto AccessSpelling = getAccessSpelling(AS); + if (AccessSpelling.empty()) + return; + OS << AccessSpelling; } void TextNodeDumper::dumpCleanupObject( diff --git a/clang/lib/ASTMatchers/ASTMatchersInternal.cpp b/clang/lib/ASTMatchers/ASTMatchersInternal.cpp index 40bd439f79fae..9b69734d075d3 100644 --- a/clang/lib/ASTMatchers/ASTMatchersInternal.cpp +++ b/clang/lib/ASTMatchers/ASTMatchersInternal.cpp @@ -68,6 +68,30 @@ bool OptionallyVariadicOperator(const DynTypedNode &DynNode, BoundNodesTreeBuilder *Builder, ArrayRef InnerMatchers); +bool matchesAnyBase(const CXXRecordDecl &Node, + const Matcher &BaseSpecMatcher, + ASTMatchFinder *Finder, BoundNodesTreeBuilder *Builder) { + if (!Node.hasDefinition()) + return false; + + CXXBasePaths Paths; + Paths.setOrigin(&Node); + + const auto basePredicate = + [Finder, Builder, &BaseSpecMatcher](const CXXBaseSpecifier *BaseSpec, + CXXBasePath &IgnoredParam) { + BoundNodesTreeBuilder Result(*Builder); + if (BaseSpecMatcher.matches(*BaseSpec, Finder, Builder)) { + *Builder = std::move(Result); + return true; + } + return false; + }; + + return Node.lookupInBases(basePredicate, Paths, + /*LookupInDependent =*/true); +} + void BoundNodesTreeBuilder::visitMatches(Visitor *ResultVisitor) { if (Bindings.empty()) Bindings.push_back(BoundNodesMap()); @@ -136,6 +160,31 @@ class TrueMatcherImpl : public DynMatcherInterface { } }; +/// A matcher that specifies a particular \c TraversalKind. +/// +/// The kind provided to the constructor overrides any kind that may be +/// specified by the `InnerMatcher`. +class DynTraversalMatcherImpl : public DynMatcherInterface { +public: + explicit DynTraversalMatcherImpl( + clang::TraversalKind TK, + IntrusiveRefCntPtr InnerMatcher) + : TK(TK), InnerMatcher(std::move(InnerMatcher)) {} + + bool dynMatches(const DynTypedNode &DynNode, ASTMatchFinder *Finder, + BoundNodesTreeBuilder *Builder) const override { + return this->InnerMatcher->dynMatches(DynNode, Finder, Builder); + } + + llvm::Optional TraversalKind() const override { + return TK; + } + +private: + clang::TraversalKind TK; + IntrusiveRefCntPtr InnerMatcher; +}; + } // namespace static llvm::ManagedStatic TrueMatcherInstance; @@ -204,6 +253,14 @@ DynTypedMatcher::constructRestrictedWrapper(const DynTypedMatcher &InnerMatcher, return Copy; } +DynTypedMatcher +DynTypedMatcher::withTraversalKind(ast_type_traits::TraversalKind TK) { + auto Copy = *this; + Copy.Implementation = + new DynTraversalMatcherImpl(TK, std::move(Copy.Implementation)); + return Copy; +} + DynTypedMatcher DynTypedMatcher::trueMatcher(ASTNodeKind NodeKind) { return DynTypedMatcher(NodeKind, NodeKind, &*TrueMatcherInstance); } diff --git a/clang/lib/ASTMatchers/Dynamic/Registry.cpp b/clang/lib/ASTMatchers/Dynamic/Registry.cpp index 0a7d09e55c885..950a56f795516 100644 --- a/clang/lib/ASTMatchers/Dynamic/Registry.cpp +++ b/clang/lib/ASTMatchers/Dynamic/Registry.cpp @@ -219,6 +219,7 @@ RegistryMaps::RegistryMaps() { REGISTER_MATCHER(floatLiteral); REGISTER_MATCHER(forEach); REGISTER_MATCHER(forEachArgumentWithParam); + REGISTER_MATCHER(isAtPosition); REGISTER_MATCHER(forEachConstructorInitializer); REGISTER_MATCHER(forEachDescendant); REGISTER_MATCHER(forEachOverridden); @@ -236,6 +237,7 @@ RegistryMaps::RegistryMaps() { REGISTER_MATCHER(has); REGISTER_MATCHER(hasAncestor); REGISTER_MATCHER(hasAnyArgument); + REGISTER_MATCHER(hasAnyBase); REGISTER_MATCHER(hasAnyClause); REGISTER_MATCHER(hasAnyConstructorInitializer); REGISTER_MATCHER(hasAnyDeclaration); diff --git a/clang/lib/Basic/OpenMPKinds.cpp b/clang/lib/Basic/OpenMPKinds.cpp index 8dddb66fa322a..a000e4dee3b85 100644 --- a/clang/lib/Basic/OpenMPKinds.cpp +++ b/clang/lib/Basic/OpenMPKinds.cpp @@ -163,6 +163,7 @@ unsigned clang::getOpenMPSimpleClauseType(OpenMPClauseKind Kind, case OMPC_hint: case OMPC_uniform: case OMPC_use_device_ptr: + case OMPC_use_device_addr: case OMPC_is_device_ptr: case OMPC_unified_address: case OMPC_unified_shared_memory: @@ -411,6 +412,7 @@ const char *clang::getOpenMPSimpleClauseTypeName(OpenMPClauseKind Kind, case OMPC_hint: case OMPC_uniform: case OMPC_use_device_ptr: + case OMPC_use_device_addr: case OMPC_is_device_ptr: case OMPC_unified_address: case OMPC_unified_shared_memory: diff --git a/clang/lib/Basic/TargetInfo.cpp b/clang/lib/Basic/TargetInfo.cpp index 2f1e044bb106d..a3c8da5885b8e 100644 --- a/clang/lib/Basic/TargetInfo.cpp +++ b/clang/lib/Basic/TargetInfo.cpp @@ -265,7 +265,8 @@ TargetInfo::IntType TargetInfo::getLeastIntTypeByWidth(unsigned BitWidth, return NoInt; } -TargetInfo::RealType TargetInfo::getRealTypeByWidth(unsigned BitWidth) const { +TargetInfo::RealType TargetInfo::getRealTypeByWidth(unsigned BitWidth, + bool ExplicitIEEE) const { if (getFloatWidth() == BitWidth) return Float; if (getDoubleWidth() == BitWidth) @@ -277,6 +278,10 @@ TargetInfo::RealType TargetInfo::getRealTypeByWidth(unsigned BitWidth) const { return LongDouble; break; case 128: + // The caller explicitly asked for an IEEE compliant type but we still + // have to check if the target supports it. + if (ExplicitIEEE) + return hasFloat128Type() ? Float128 : NoFloat; if (&getLongDoubleFormat() == &llvm::APFloat::PPCDoubleDouble() || &getLongDoubleFormat() == &llvm::APFloat::IEEEquad()) return LongDouble; diff --git a/clang/lib/Basic/Targets/AMDGPU.h b/clang/lib/Basic/Targets/AMDGPU.h index d0e88e223e955..6c9060aa3f7bf 100644 --- a/clang/lib/Basic/Targets/AMDGPU.h +++ b/clang/lib/Basic/Targets/AMDGPU.h @@ -131,6 +131,11 @@ class LLVM_LIBRARY_VISIBILITY AMDGPUTargetInfo final : public TargetInfo { }); StringRef S(Name); + if (S == "A") { + Info.setRequiresImmediate(); + return true; + } + bool HasLeftParen = false; if (S.front() == '{') { HasLeftParen = true; diff --git a/clang/lib/Basic/Targets/PPC.cpp b/clang/lib/Basic/Targets/PPC.cpp index 81c13a8104e8a..ad34c287b5188 100644 --- a/clang/lib/Basic/Targets/PPC.cpp +++ b/clang/lib/Basic/Targets/PPC.cpp @@ -151,6 +151,8 @@ void PPCTargetInfo::getTargetDefines(const LangOptions &Opts, Builder.defineMacro("_ARCH_PWR8"); if (ArchDefs & ArchDefinePwr9) Builder.defineMacro("_ARCH_PWR9"); + if (ArchDefs & ArchDefinePwr10) + Builder.defineMacro("_ARCH_PWR10"); if (ArchDefs & ArchDefineA2) Builder.defineMacro("_ARCH_A2"); if (ArchDefs & ArchDefineA2q) { @@ -313,10 +315,17 @@ bool PPCTargetInfo::initFeatureMap( .Case("e500", true) .Default(false); - // Future CPU should include all of the features of Power 9 as well as any + // Power10 includes all the same features as Power9 plus any features specific + // to the Power10 core. + if (CPU == "pwr10" || CPU == "power10") { + initFeatureMap(Features, Diags, "pwr9", FeaturesVec); + addP10SpecificFeatures(Features); + } + + // Future CPU should include all of the features of Power 10 as well as any // additional features (yet to be determined) specific to it. if (CPU == "future") { - initFeatureMap(Features, Diags, "pwr9", FeaturesVec); + initFeatureMap(Features, Diags, "pwr10", FeaturesVec); addFutureSpecificFeatures(Features); } @@ -333,6 +342,13 @@ bool PPCTargetInfo::initFeatureMap( return TargetInfo::initFeatureMap(Features, Diags, CPU, FeaturesVec); } +// Add any Power10 specific features. +void PPCTargetInfo::addP10SpecificFeatures( + llvm::StringMap &Features) const { + Features["htm"] = false; // HTM was removed for P10. + return; +} + // Add features specific to the "Future" CPU. void PPCTargetInfo::addFutureSpecificFeatures( llvm::StringMap &Features) const { @@ -463,18 +479,17 @@ ArrayRef PPCTargetInfo::getGCCAddlRegNames() const { } static constexpr llvm::StringLiteral ValidCPUNames[] = { - {"generic"}, {"440"}, {"450"}, {"601"}, {"602"}, - {"603"}, {"603e"}, {"603ev"}, {"604"}, {"604e"}, - {"620"}, {"630"}, {"g3"}, {"7400"}, {"g4"}, - {"7450"}, {"g4+"}, {"750"}, {"8548"}, {"970"}, - {"g5"}, {"a2"}, {"a2q"}, {"e500"}, {"e500mc"}, - {"e5500"}, {"power3"}, {"pwr3"}, {"power4"}, {"pwr4"}, - {"power5"}, {"pwr5"}, {"power5x"}, {"pwr5x"}, {"power6"}, - {"pwr6"}, {"power6x"}, {"pwr6x"}, {"power7"}, {"pwr7"}, - {"power8"}, {"pwr8"}, {"power9"}, {"pwr9"}, {"powerpc"}, - {"ppc"}, {"powerpc64"}, {"ppc64"}, {"powerpc64le"}, {"ppc64le"}, - {"future"} -}; + {"generic"}, {"440"}, {"450"}, {"601"}, {"602"}, + {"603"}, {"603e"}, {"603ev"}, {"604"}, {"604e"}, + {"620"}, {"630"}, {"g3"}, {"7400"}, {"g4"}, + {"7450"}, {"g4+"}, {"750"}, {"8548"}, {"970"}, + {"g5"}, {"a2"}, {"a2q"}, {"e500"}, {"e500mc"}, + {"e5500"}, {"power3"}, {"pwr3"}, {"power4"}, {"pwr4"}, + {"power5"}, {"pwr5"}, {"power5x"}, {"pwr5x"}, {"power6"}, + {"pwr6"}, {"power6x"}, {"pwr6x"}, {"power7"}, {"pwr7"}, + {"power8"}, {"pwr8"}, {"power9"}, {"pwr9"}, {"power10"}, + {"pwr10"}, {"powerpc"}, {"ppc"}, {"powerpc64"}, {"ppc64"}, + {"powerpc64le"}, {"ppc64le"}, {"future"}}; bool PPCTargetInfo::isValidCPUName(StringRef Name) const { return llvm::find(ValidCPUNames, Name) != std::end(ValidCPUNames); diff --git a/clang/lib/Basic/Targets/PPC.h b/clang/lib/Basic/Targets/PPC.h index 7c19a96a99c74..691fa5fdcc6d1 100644 --- a/clang/lib/Basic/Targets/PPC.h +++ b/clang/lib/Basic/Targets/PPC.h @@ -43,13 +43,13 @@ class LLVM_LIBRARY_VISIBILITY PPCTargetInfo : public TargetInfo { ArchDefinePwr7 = 1 << 11, ArchDefinePwr8 = 1 << 12, ArchDefinePwr9 = 1 << 13, - ArchDefineFuture = 1 << 14, - ArchDefineA2 = 1 << 15, - ArchDefineA2q = 1 << 16, - ArchDefineE500 = 1 << 17 + ArchDefinePwr10 = 1 << 14, + ArchDefineFuture = 1 << 15, + ArchDefineA2 = 1 << 16, + ArchDefineA2q = 1 << 17, + ArchDefineE500 = 1 << 18 } ArchDefineTypes; - ArchDefineTypes ArchDefs = ArchDefineNone; static const Builtin::Info BuiltinInfo[]; static const char *const GCCRegNames[]; @@ -119,20 +119,20 @@ class LLVM_LIBRARY_VISIBILITY PPCTargetInfo : public TargetInfo { .Case("a2q", ArchDefineName | ArchDefineA2 | ArchDefineA2q) .Cases("power3", "pwr3", ArchDefinePpcgr) .Cases("power4", "pwr4", - ArchDefinePwr4 | ArchDefinePpcgr | ArchDefinePpcsq) + ArchDefinePwr4 | ArchDefinePpcgr | ArchDefinePpcsq) .Cases("power5", "pwr5", - ArchDefinePwr5 | ArchDefinePwr4 | ArchDefinePpcgr | - ArchDefinePpcsq) + ArchDefinePwr5 | ArchDefinePwr4 | ArchDefinePpcgr | + ArchDefinePpcsq) .Cases("power5x", "pwr5x", - ArchDefinePwr5x | ArchDefinePwr5 | ArchDefinePwr4 | - ArchDefinePpcgr | ArchDefinePpcsq) + ArchDefinePwr5x | ArchDefinePwr5 | ArchDefinePwr4 | + ArchDefinePpcgr | ArchDefinePpcsq) .Cases("power6", "pwr6", - ArchDefinePwr6 | ArchDefinePwr5x | ArchDefinePwr5 | - ArchDefinePwr4 | ArchDefinePpcgr | ArchDefinePpcsq) + ArchDefinePwr6 | ArchDefinePwr5x | ArchDefinePwr5 | + ArchDefinePwr4 | ArchDefinePpcgr | ArchDefinePpcsq) .Cases("power6x", "pwr6x", - ArchDefinePwr6x | ArchDefinePwr6 | ArchDefinePwr5x | - ArchDefinePwr5 | ArchDefinePwr4 | ArchDefinePpcgr | - ArchDefinePpcsq) + ArchDefinePwr6x | ArchDefinePwr6 | ArchDefinePwr5x | + ArchDefinePwr5 | ArchDefinePwr4 | ArchDefinePpcgr | + ArchDefinePpcsq) .Cases("power7", "pwr7", ArchDefinePwr7 | ArchDefinePwr6 | ArchDefinePwr5x | ArchDefinePwr5 | ArchDefinePwr4 | ArchDefinePpcgr | @@ -146,11 +146,16 @@ class LLVM_LIBRARY_VISIBILITY PPCTargetInfo : public TargetInfo { ArchDefinePwr9 | ArchDefinePwr8 | ArchDefinePwr7 | ArchDefinePwr6 | ArchDefinePwr5x | ArchDefinePwr5 | ArchDefinePwr4 | ArchDefinePpcgr | ArchDefinePpcsq) + .Cases("power10", "pwr10", + ArchDefinePwr10 | ArchDefinePwr9 | ArchDefinePwr8 | + ArchDefinePwr7 | ArchDefinePwr6 | ArchDefinePwr5x | + ArchDefinePwr5 | ArchDefinePwr4 | ArchDefinePpcgr | + ArchDefinePpcsq) .Case("future", - ArchDefineFuture | ArchDefinePwr9 | ArchDefinePwr8 | - ArchDefinePwr7 | ArchDefinePwr6 | ArchDefinePwr5x | - ArchDefinePwr5 | ArchDefinePwr4 | ArchDefinePpcgr | - ArchDefinePpcsq) + ArchDefineFuture | ArchDefinePwr10 | ArchDefinePwr9 | + ArchDefinePwr8 | ArchDefinePwr7 | ArchDefinePwr6 | + ArchDefinePwr5x | ArchDefinePwr5 | ArchDefinePwr4 | + ArchDefinePpcgr | ArchDefinePpcsq) .Cases("8548", "e500", ArchDefineE500) .Default(ArchDefineNone); } @@ -171,6 +176,7 @@ class LLVM_LIBRARY_VISIBILITY PPCTargetInfo : public TargetInfo { StringRef CPU, const std::vector &FeaturesVec) const override; + void addP10SpecificFeatures(llvm::StringMap &Features) const; void addFutureSpecificFeatures(llvm::StringMap &Features) const; bool handleTargetFeatures(std::vector &Features, diff --git a/clang/lib/CMakeLists.txt b/clang/lib/CMakeLists.txt index 0c03f5972b093..c2b6a5a4d5d4f 100644 --- a/clang/lib/CMakeLists.txt +++ b/clang/lib/CMakeLists.txt @@ -24,3 +24,4 @@ if(CLANG_ENABLE_STATIC_ANALYZER) add_subdirectory(StaticAnalyzer) endif() add_subdirectory(Format) +add_subdirectory(Testing) diff --git a/clang/lib/CodeGen/BackendUtil.cpp b/clang/lib/CodeGen/BackendUtil.cpp index 15aa99268213a..5329db7bed518 100644 --- a/clang/lib/CodeGen/BackendUtil.cpp +++ b/clang/lib/CodeGen/BackendUtil.cpp @@ -32,6 +32,7 @@ #include "llvm/IR/LegacyPassManager.h" #include "llvm/IR/Module.h" #include "llvm/IR/ModuleSummaryIndex.h" +#include "llvm/IR/PassManager.h" #include "llvm/IR/Verifier.h" #include "llvm/LTO/LTOBackend.h" #include "llvm/MC/MCAsmInfo.h" @@ -359,7 +360,7 @@ static void addDataFlowSanitizerPass(const PassManagerBuilder &Builder, static void addMemTagOptimizationPasses(const PassManagerBuilder &Builder, legacy::PassManagerBase &PM) { - PM.add(createStackSafetyGlobalInfoWrapperPass(/*SetMetadata=*/true)); + PM.add(createStackSafetyGlobalInfoWrapperPass()); } static TargetLibraryInfoImpl *createTLII(llvm::Triple &TargetTriple, @@ -1043,6 +1044,15 @@ static void addSanitizersAtO0(ModulePassManager &MPM, const Triple &TargetTriple, const LangOptions &LangOpts, const CodeGenOptions &CodeGenOpts) { + if (CodeGenOpts.SanitizeCoverageType || + CodeGenOpts.SanitizeCoverageIndirectCalls || + CodeGenOpts.SanitizeCoverageTraceCmp) { + auto SancovOpts = getSancovOptsFromCGOpts(CodeGenOpts); + MPM.addPass(ModuleSanitizerCoveragePass( + SancovOpts, CodeGenOpts.SanitizeCoverageWhitelistFiles, + CodeGenOpts.SanitizeCoverageBlacklistFiles)); + } + auto ASanPass = [&](SanitizerMask Mask, bool CompileKernel) { MPM.addPass(RequireAnalysisPass()); bool Recover = CodeGenOpts.SanitizeRecover.has(Mask); @@ -1291,6 +1301,20 @@ void EmitAssemblyHelper::EmitAssemblyWithNewPassManager( [](FunctionPassManager &FPM, PassBuilder::OptimizationLevel Level) { FPM.addPass(BoundsCheckingPass()); }); + + if (CodeGenOpts.SanitizeCoverageType || + CodeGenOpts.SanitizeCoverageIndirectCalls || + CodeGenOpts.SanitizeCoverageTraceCmp) { + PB.registerOptimizerLastEPCallback( + [this](ModulePassManager &MPM, + PassBuilder::OptimizationLevel Level) { + auto SancovOpts = getSancovOptsFromCGOpts(CodeGenOpts); + MPM.addPass(ModuleSanitizerCoveragePass( + SancovOpts, CodeGenOpts.SanitizeCoverageWhitelistFiles, + CodeGenOpts.SanitizeCoverageBlacklistFiles)); + }); + } + if (LangOpts.Sanitize.has(SanitizerKind::Memory)) { int TrackOrigins = CodeGenOpts.SanitizeMemoryTrackOrigins; bool Recover = CodeGenOpts.SanitizeRecover.has(SanitizerKind::Memory); @@ -1299,17 +1323,19 @@ void EmitAssemblyHelper::EmitAssemblyWithNewPassManager( MPM.addPass(MemorySanitizerPass({TrackOrigins, Recover, false})); }); PB.registerOptimizerLastEPCallback( - [TrackOrigins, Recover](FunctionPassManager &FPM, + [TrackOrigins, Recover](ModulePassManager &MPM, PassBuilder::OptimizationLevel Level) { - FPM.addPass(MemorySanitizerPass({TrackOrigins, Recover, false})); + MPM.addPass(createModuleToFunctionPassAdaptor( + MemorySanitizerPass({TrackOrigins, Recover, false}))); }); } if (LangOpts.Sanitize.has(SanitizerKind::Thread)) { PB.registerPipelineStartEPCallback( [](ModulePassManager &MPM) { MPM.addPass(ThreadSanitizerPass()); }); PB.registerOptimizerLastEPCallback( - [](FunctionPassManager &FPM, PassBuilder::OptimizationLevel Level) { - FPM.addPass(ThreadSanitizerPass()); + [](ModulePassManager &MPM, PassBuilder::OptimizationLevel Level) { + MPM.addPass( + createModuleToFunctionPassAdaptor(ThreadSanitizerPass())); }); } if (LangOpts.Sanitize.has(SanitizerKind::Address)) { @@ -1320,10 +1346,11 @@ void EmitAssemblyHelper::EmitAssemblyWithNewPassManager( bool Recover = CodeGenOpts.SanitizeRecover.has(SanitizerKind::Address); bool UseAfterScope = CodeGenOpts.SanitizeAddressUseAfterScope; PB.registerOptimizerLastEPCallback( - [Recover, UseAfterScope](FunctionPassManager &FPM, + [Recover, UseAfterScope](ModulePassManager &MPM, PassBuilder::OptimizationLevel Level) { - FPM.addPass(AddressSanitizerPass( - /*CompileKernel=*/false, Recover, UseAfterScope)); + MPM.addPass( + createModuleToFunctionPassAdaptor(AddressSanitizerPass( + /*CompileKernel=*/false, Recover, UseAfterScope))); }); bool ModuleUseAfterScope = asanUseGlobalsGC(TargetTriple, CodeGenOpts); bool UseOdrIndicator = CodeGenOpts.SanitizeAddressUseOdrIndicator; @@ -1367,15 +1394,6 @@ void EmitAssemblyHelper::EmitAssemblyWithNewPassManager( } } - if (CodeGenOpts.SanitizeCoverageType || - CodeGenOpts.SanitizeCoverageIndirectCalls || - CodeGenOpts.SanitizeCoverageTraceCmp) { - auto SancovOpts = getSancovOptsFromCGOpts(CodeGenOpts); - MPM.addPass(ModuleSanitizerCoveragePass( - SancovOpts, CodeGenOpts.SanitizeCoverageWhitelistFiles, - CodeGenOpts.SanitizeCoverageBlacklistFiles)); - } - if (LangOpts.Sanitize.has(SanitizerKind::HWAddress)) { bool Recover = CodeGenOpts.SanitizeRecover.has(SanitizerKind::HWAddress); MPM.addPass(HWAddressSanitizerPass( diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp index 5612364bfef80..e51f32e2442e6 100644 --- a/clang/lib/CodeGen/CGBuiltin.cpp +++ b/clang/lib/CodeGen/CGBuiltin.cpp @@ -1271,6 +1271,8 @@ llvm::Function *CodeGenFunction::generateBuiltinOSLogHelperFunction( FunctionDecl *FD = FunctionDecl::Create( Ctx, Ctx.getTranslationUnitDecl(), SourceLocation(), SourceLocation(), II, FuncionTy, nullptr, SC_PrivateExtern, false, false); + // Avoid generating debug location info for the function. + FD->setImplicit(); StartFunction(FD, ReturnTy, Fn, FI, Args); @@ -4490,29 +4492,29 @@ static llvm::VectorType *GetNeonType(CodeGenFunction *CGF, switch (TypeFlags.getEltType()) { case NeonTypeFlags::Int8: case NeonTypeFlags::Poly8: - return llvm::VectorType::get(CGF->Int8Ty, V1Ty ? 1 : (8 << IsQuad)); + return llvm::FixedVectorType::get(CGF->Int8Ty, V1Ty ? 1 : (8 << IsQuad)); case NeonTypeFlags::Int16: case NeonTypeFlags::Poly16: - return llvm::VectorType::get(CGF->Int16Ty, V1Ty ? 1 : (4 << IsQuad)); + return llvm::FixedVectorType::get(CGF->Int16Ty, V1Ty ? 1 : (4 << IsQuad)); case NeonTypeFlags::Float16: if (HasLegalHalfType) - return llvm::VectorType::get(CGF->HalfTy, V1Ty ? 1 : (4 << IsQuad)); + return llvm::FixedVectorType::get(CGF->HalfTy, V1Ty ? 1 : (4 << IsQuad)); else - return llvm::VectorType::get(CGF->Int16Ty, V1Ty ? 1 : (4 << IsQuad)); + return llvm::FixedVectorType::get(CGF->Int16Ty, V1Ty ? 1 : (4 << IsQuad)); case NeonTypeFlags::Int32: - return llvm::VectorType::get(CGF->Int32Ty, V1Ty ? 1 : (2 << IsQuad)); + return llvm::FixedVectorType::get(CGF->Int32Ty, V1Ty ? 1 : (2 << IsQuad)); case NeonTypeFlags::Int64: case NeonTypeFlags::Poly64: - return llvm::VectorType::get(CGF->Int64Ty, V1Ty ? 1 : (1 << IsQuad)); + return llvm::FixedVectorType::get(CGF->Int64Ty, V1Ty ? 1 : (1 << IsQuad)); case NeonTypeFlags::Poly128: // FIXME: i128 and f128 doesn't get fully support in Clang and llvm. // There is a lot of i128 and f128 API missing. // so we use v16i8 to represent poly128 and get pattern matched. - return llvm::VectorType::get(CGF->Int8Ty, 16); + return llvm::FixedVectorType::get(CGF->Int8Ty, 16); case NeonTypeFlags::Float32: - return llvm::VectorType::get(CGF->FloatTy, V1Ty ? 1 : (2 << IsQuad)); + return llvm::FixedVectorType::get(CGF->FloatTy, V1Ty ? 1 : (2 << IsQuad)); case NeonTypeFlags::Float64: - return llvm::VectorType::get(CGF->DoubleTy, V1Ty ? 1 : (1 << IsQuad)); + return llvm::FixedVectorType::get(CGF->DoubleTy, V1Ty ? 1 : (1 << IsQuad)); } llvm_unreachable("Unknown vector element type!"); } @@ -4522,11 +4524,11 @@ static llvm::VectorType *GetFloatNeonType(CodeGenFunction *CGF, int IsQuad = IntTypeFlags.isQuad(); switch (IntTypeFlags.getEltType()) { case NeonTypeFlags::Int16: - return llvm::VectorType::get(CGF->HalfTy, (4 << IsQuad)); + return llvm::FixedVectorType::get(CGF->HalfTy, (4 << IsQuad)); case NeonTypeFlags::Int32: - return llvm::VectorType::get(CGF->FloatTy, (2 << IsQuad)); + return llvm::FixedVectorType::get(CGF->FloatTy, (2 << IsQuad)); case NeonTypeFlags::Int64: - return llvm::VectorType::get(CGF->DoubleTy, (1 << IsQuad)); + return llvm::FixedVectorType::get(CGF->DoubleTy, (1 << IsQuad)); default: llvm_unreachable("Type can't be converted to floating-point!"); } @@ -5407,7 +5409,7 @@ Function *CodeGenFunction::LookupNeonLLVMIntrinsic(unsigned IntrinsicID, if (Modifier & AddRetType) { llvm::Type *Ty = ConvertType(E->getCallReturnType(getContext())); if (Modifier & VectorizeRetType) - Ty = llvm::VectorType::get( + Ty = llvm::FixedVectorType::get( Ty, VectorSize ? VectorSize / Ty->getPrimitiveSizeInBits() : 1); Tys.push_back(Ty); @@ -5416,7 +5418,7 @@ Function *CodeGenFunction::LookupNeonLLVMIntrinsic(unsigned IntrinsicID, // Arguments. if (Modifier & VectorizeArgTypes) { int Elts = VectorSize ? VectorSize / ArgType->getPrimitiveSizeInBits() : 1; - ArgType = llvm::VectorType::get(ArgType, Elts); + ArgType = llvm::FixedVectorType::get(ArgType, Elts); } if (Modifier & (Add1ArgType | Add2ArgTypes)) @@ -5590,7 +5592,7 @@ Value *CodeGenFunction::EmitCommonNeonBuiltinExpr( Ty = HalfTy; break; } - llvm::Type *VecFlt = llvm::VectorType::get(Ty, VTy->getNumElements()); + auto *VecFlt = llvm::FixedVectorType::get(Ty, VTy->getNumElements()); llvm::Type *Tys[] = { VTy, VecFlt }; Function *F = CGM.getIntrinsic(LLVMIntrinsic, Tys); return EmitNeonCall(F, Ops, NameHint); @@ -5850,8 +5852,8 @@ Value *CodeGenFunction::EmitCommonNeonBuiltinExpr( unsigned EltBits = VTy->getElementType()->getPrimitiveSizeInBits(); llvm::Type *EltTy = llvm::IntegerType::get(getLLVMContext(), EltBits / 2); - llvm::Type *NarrowTy = - llvm::VectorType::get(EltTy, VTy->getNumElements() * 2); + auto *NarrowTy = + llvm::FixedVectorType::get(EltTy, VTy->getNumElements() * 2); llvm::Type *Tys[2] = { Ty, NarrowTy }; return EmitNeonCall(CGM.getIntrinsic(Int, Tys), Ops, NameHint); } @@ -5860,8 +5862,8 @@ Value *CodeGenFunction::EmitCommonNeonBuiltinExpr( // The source operand type has twice as many elements of half the size. unsigned EltBits = VTy->getElementType()->getPrimitiveSizeInBits(); llvm::Type *EltTy = llvm::IntegerType::get(getLLVMContext(), EltBits / 2); - llvm::Type *NarrowTy = - llvm::VectorType::get(EltTy, VTy->getNumElements() * 2); + auto *NarrowTy = + llvm::FixedVectorType::get(EltTy, VTy->getNumElements() * 2); llvm::Type *Tys[2] = { Ty, NarrowTy }; return EmitNeonCall(CGM.getIntrinsic(Int, Tys), Ops, "vpaddl"); } @@ -5880,8 +5882,8 @@ Value *CodeGenFunction::EmitCommonNeonBuiltinExpr( auto *RTy = cast(Ty); if (BuiltinID == NEON::BI__builtin_neon_vqdmulhq_lane_v || BuiltinID == NEON::BI__builtin_neon_vqrdmulhq_lane_v) - RTy = llvm::VectorType::get(RTy->getElementType(), - RTy->getNumElements() * 2); + RTy = llvm::FixedVectorType::get(RTy->getElementType(), + RTy->getNumElements() * 2); llvm::Type *Tys[2] = { RTy, GetNeonType(this, NeonTypeFlags(Type.getEltType(), false, /*isQuad*/ false))}; @@ -6068,57 +6070,57 @@ Value *CodeGenFunction::EmitCommonNeonBuiltinExpr( } case NEON::BI__builtin_neon_vdot_v: case NEON::BI__builtin_neon_vdotq_v: { - llvm::Type *InputTy = - llvm::VectorType::get(Int8Ty, Ty->getPrimitiveSizeInBits() / 8); + auto *InputTy = + llvm::FixedVectorType::get(Int8Ty, Ty->getPrimitiveSizeInBits() / 8); llvm::Type *Tys[2] = { Ty, InputTy }; Int = Usgn ? LLVMIntrinsic : AltLLVMIntrinsic; return EmitNeonCall(CGM.getIntrinsic(Int, Tys), Ops, "vdot"); } case NEON::BI__builtin_neon_vfmlal_low_v: case NEON::BI__builtin_neon_vfmlalq_low_v: { - llvm::Type *InputTy = - llvm::VectorType::get(HalfTy, Ty->getPrimitiveSizeInBits() / 16); + auto *InputTy = + llvm::FixedVectorType::get(HalfTy, Ty->getPrimitiveSizeInBits() / 16); llvm::Type *Tys[2] = { Ty, InputTy }; return EmitNeonCall(CGM.getIntrinsic(Int, Tys), Ops, "vfmlal_low"); } case NEON::BI__builtin_neon_vfmlsl_low_v: case NEON::BI__builtin_neon_vfmlslq_low_v: { - llvm::Type *InputTy = - llvm::VectorType::get(HalfTy, Ty->getPrimitiveSizeInBits() / 16); + auto *InputTy = + llvm::FixedVectorType::get(HalfTy, Ty->getPrimitiveSizeInBits() / 16); llvm::Type *Tys[2] = { Ty, InputTy }; return EmitNeonCall(CGM.getIntrinsic(Int, Tys), Ops, "vfmlsl_low"); } case NEON::BI__builtin_neon_vfmlal_high_v: case NEON::BI__builtin_neon_vfmlalq_high_v: { - llvm::Type *InputTy = - llvm::VectorType::get(HalfTy, Ty->getPrimitiveSizeInBits() / 16); + auto *InputTy = + llvm::FixedVectorType::get(HalfTy, Ty->getPrimitiveSizeInBits() / 16); llvm::Type *Tys[2] = { Ty, InputTy }; return EmitNeonCall(CGM.getIntrinsic(Int, Tys), Ops, "vfmlal_high"); } case NEON::BI__builtin_neon_vfmlsl_high_v: case NEON::BI__builtin_neon_vfmlslq_high_v: { - llvm::Type *InputTy = - llvm::VectorType::get(HalfTy, Ty->getPrimitiveSizeInBits() / 16); + auto *InputTy = + llvm::FixedVectorType::get(HalfTy, Ty->getPrimitiveSizeInBits() / 16); llvm::Type *Tys[2] = { Ty, InputTy }; return EmitNeonCall(CGM.getIntrinsic(Int, Tys), Ops, "vfmlsl_high"); } case NEON::BI__builtin_neon_vmmlaq_v: { - llvm::Type *InputTy = - llvm::VectorType::get(Int8Ty, Ty->getPrimitiveSizeInBits() / 8); + auto *InputTy = + llvm::FixedVectorType::get(Int8Ty, Ty->getPrimitiveSizeInBits() / 8); llvm::Type *Tys[2] = { Ty, InputTy }; Int = Usgn ? LLVMIntrinsic : AltLLVMIntrinsic; return EmitNeonCall(CGM.getIntrinsic(Int, Tys), Ops, "vmmla"); } case NEON::BI__builtin_neon_vusmmlaq_v: { - llvm::Type *InputTy = - llvm::VectorType::get(Int8Ty, Ty->getPrimitiveSizeInBits() / 8); + auto *InputTy = + llvm::FixedVectorType::get(Int8Ty, Ty->getPrimitiveSizeInBits() / 8); llvm::Type *Tys[2] = { Ty, InputTy }; return EmitNeonCall(CGM.getIntrinsic(Int, Tys), Ops, "vusmmla"); } case NEON::BI__builtin_neon_vusdot_v: case NEON::BI__builtin_neon_vusdotq_v: { - llvm::Type *InputTy = - llvm::VectorType::get(Int8Ty, Ty->getPrimitiveSizeInBits() / 8); + auto *InputTy = + llvm::FixedVectorType::get(Int8Ty, Ty->getPrimitiveSizeInBits() / 8); llvm::Type *Tys[2] = { Ty, InputTy }; return EmitNeonCall(CGM.getIntrinsic(Int, Tys), Ops, "vusdot"); } @@ -7007,7 +7009,7 @@ Value *CodeGenFunction::EmitARMBuiltinExpr(unsigned BuiltinID, Value *SV = llvm::ConstantVector::get(ConstantInt::get(Int32Ty, 1-Lane)); Ops[1] = Builder.CreateShuffleVector(Ops[1], Ops[1], SV); // Load the value as a one-element vector. - Ty = llvm::VectorType::get(VTy->getElementType(), 1); + Ty = llvm::FixedVectorType::get(VTy->getElementType(), 1); llvm::Type *Tys[] = {Ty, Int8PtrTy}; Function *F = CGM.getIntrinsic(Intrinsic::arm_neon_vld1, Tys); Value *Align = getAlignmentValue32(PtrOp0); @@ -7501,7 +7503,7 @@ static Value *EmitAArch64TblBuiltinExpr(CodeGenFunction &CGF, unsigned BuiltinID } Value *CodeGenFunction::vectorWrapScalar16(Value *Op) { - llvm::Type *VTy = llvm::VectorType::get(Int16Ty, 4); + auto *VTy = llvm::FixedVectorType::get(Int16Ty, 4); Op = Builder.CreateBitCast(Op, Int16Ty); Value *V = UndefValue::get(VTy); llvm::Constant *CI = ConstantInt::get(SizeTy, 0); @@ -8871,7 +8873,7 @@ Value *CodeGenFunction::EmitAArch64BuiltinExpr(unsigned BuiltinID, return EmitNeonCall(CGM.getIntrinsic(Int, Tys), Ops, "fcvth_n"); } case NEON::BI__builtin_neon_vpaddd_s64: { - llvm::Type *Ty = llvm::VectorType::get(Int64Ty, 2); + auto *Ty = llvm::FixedVectorType::get(Int64Ty, 2); Value *Vec = EmitScalarExpr(E->getArg(0)); // The vector is v2f64, so make sure it's bitcast to that. Vec = Builder.CreateBitCast(Vec, Ty, "v2i64"); @@ -8883,8 +8885,7 @@ Value *CodeGenFunction::EmitAArch64BuiltinExpr(unsigned BuiltinID, return Builder.CreateAdd(Op0, Op1, "vpaddd"); } case NEON::BI__builtin_neon_vpaddd_f64: { - llvm::Type *Ty = - llvm::VectorType::get(DoubleTy, 2); + auto *Ty = llvm::FixedVectorType::get(DoubleTy, 2); Value *Vec = EmitScalarExpr(E->getArg(0)); // The vector is v2f64, so make sure it's bitcast to that. Vec = Builder.CreateBitCast(Vec, Ty, "v2f64"); @@ -8896,8 +8897,7 @@ Value *CodeGenFunction::EmitAArch64BuiltinExpr(unsigned BuiltinID, return Builder.CreateFAdd(Op0, Op1, "vpaddd"); } case NEON::BI__builtin_neon_vpadds_f32: { - llvm::Type *Ty = - llvm::VectorType::get(FloatTy, 2); + auto *Ty = llvm::FixedVectorType::get(FloatTy, 2); Value *Vec = EmitScalarExpr(E->getArg(0)); // The vector is v2f32, so make sure it's bitcast to that. Vec = Builder.CreateBitCast(Vec, Ty, "v2f32"); @@ -9070,87 +9070,95 @@ Value *CodeGenFunction::EmitAArch64BuiltinExpr(unsigned BuiltinID, return Builder.CreateInsertElement(Ops[1], Ops[0], Ops[2], "vset_lane"); case NEON::BI__builtin_neon_vset_lane_f64: // The vector type needs a cast for the v1f64 variant. - Ops[1] = Builder.CreateBitCast(Ops[1], - llvm::VectorType::get(DoubleTy, 1)); + Ops[1] = + Builder.CreateBitCast(Ops[1], llvm::FixedVectorType::get(DoubleTy, 1)); Ops.push_back(EmitScalarExpr(E->getArg(2))); return Builder.CreateInsertElement(Ops[1], Ops[0], Ops[2], "vset_lane"); case NEON::BI__builtin_neon_vsetq_lane_f64: // The vector type needs a cast for the v2f64 variant. - Ops[1] = Builder.CreateBitCast(Ops[1], - llvm::VectorType::get(DoubleTy, 2)); + Ops[1] = + Builder.CreateBitCast(Ops[1], llvm::FixedVectorType::get(DoubleTy, 2)); Ops.push_back(EmitScalarExpr(E->getArg(2))); return Builder.CreateInsertElement(Ops[1], Ops[0], Ops[2], "vset_lane"); case NEON::BI__builtin_neon_vget_lane_i8: case NEON::BI__builtin_neon_vdupb_lane_i8: - Ops[0] = Builder.CreateBitCast(Ops[0], llvm::VectorType::get(Int8Ty, 8)); + Ops[0] = + Builder.CreateBitCast(Ops[0], llvm::FixedVectorType::get(Int8Ty, 8)); return Builder.CreateExtractElement(Ops[0], EmitScalarExpr(E->getArg(1)), "vget_lane"); case NEON::BI__builtin_neon_vgetq_lane_i8: case NEON::BI__builtin_neon_vdupb_laneq_i8: - Ops[0] = Builder.CreateBitCast(Ops[0], llvm::VectorType::get(Int8Ty, 16)); + Ops[0] = + Builder.CreateBitCast(Ops[0], llvm::FixedVectorType::get(Int8Ty, 16)); return Builder.CreateExtractElement(Ops[0], EmitScalarExpr(E->getArg(1)), "vgetq_lane"); case NEON::BI__builtin_neon_vget_lane_i16: case NEON::BI__builtin_neon_vduph_lane_i16: - Ops[0] = Builder.CreateBitCast(Ops[0], llvm::VectorType::get(Int16Ty, 4)); + Ops[0] = + Builder.CreateBitCast(Ops[0], llvm::FixedVectorType::get(Int16Ty, 4)); return Builder.CreateExtractElement(Ops[0], EmitScalarExpr(E->getArg(1)), "vget_lane"); case NEON::BI__builtin_neon_vgetq_lane_i16: case NEON::BI__builtin_neon_vduph_laneq_i16: - Ops[0] = Builder.CreateBitCast(Ops[0], llvm::VectorType::get(Int16Ty, 8)); + Ops[0] = + Builder.CreateBitCast(Ops[0], llvm::FixedVectorType::get(Int16Ty, 8)); return Builder.CreateExtractElement(Ops[0], EmitScalarExpr(E->getArg(1)), "vgetq_lane"); case NEON::BI__builtin_neon_vget_lane_i32: case NEON::BI__builtin_neon_vdups_lane_i32: - Ops[0] = Builder.CreateBitCast(Ops[0], llvm::VectorType::get(Int32Ty, 2)); + Ops[0] = + Builder.CreateBitCast(Ops[0], llvm::FixedVectorType::get(Int32Ty, 2)); return Builder.CreateExtractElement(Ops[0], EmitScalarExpr(E->getArg(1)), "vget_lane"); case NEON::BI__builtin_neon_vdups_lane_f32: - Ops[0] = Builder.CreateBitCast(Ops[0], - llvm::VectorType::get(FloatTy, 2)); + Ops[0] = + Builder.CreateBitCast(Ops[0], llvm::FixedVectorType::get(FloatTy, 2)); return Builder.CreateExtractElement(Ops[0], EmitScalarExpr(E->getArg(1)), "vdups_lane"); case NEON::BI__builtin_neon_vgetq_lane_i32: case NEON::BI__builtin_neon_vdups_laneq_i32: - Ops[0] = Builder.CreateBitCast(Ops[0], llvm::VectorType::get(Int32Ty, 4)); + Ops[0] = + Builder.CreateBitCast(Ops[0], llvm::FixedVectorType::get(Int32Ty, 4)); return Builder.CreateExtractElement(Ops[0], EmitScalarExpr(E->getArg(1)), "vgetq_lane"); case NEON::BI__builtin_neon_vget_lane_i64: case NEON::BI__builtin_neon_vdupd_lane_i64: - Ops[0] = Builder.CreateBitCast(Ops[0], llvm::VectorType::get(Int64Ty, 1)); + Ops[0] = + Builder.CreateBitCast(Ops[0], llvm::FixedVectorType::get(Int64Ty, 1)); return Builder.CreateExtractElement(Ops[0], EmitScalarExpr(E->getArg(1)), "vget_lane"); case NEON::BI__builtin_neon_vdupd_lane_f64: - Ops[0] = Builder.CreateBitCast(Ops[0], - llvm::VectorType::get(DoubleTy, 1)); + Ops[0] = + Builder.CreateBitCast(Ops[0], llvm::FixedVectorType::get(DoubleTy, 1)); return Builder.CreateExtractElement(Ops[0], EmitScalarExpr(E->getArg(1)), "vdupd_lane"); case NEON::BI__builtin_neon_vgetq_lane_i64: case NEON::BI__builtin_neon_vdupd_laneq_i64: - Ops[0] = Builder.CreateBitCast(Ops[0], llvm::VectorType::get(Int64Ty, 2)); + Ops[0] = + Builder.CreateBitCast(Ops[0], llvm::FixedVectorType::get(Int64Ty, 2)); return Builder.CreateExtractElement(Ops[0], EmitScalarExpr(E->getArg(1)), "vgetq_lane"); case NEON::BI__builtin_neon_vget_lane_f32: - Ops[0] = Builder.CreateBitCast(Ops[0], - llvm::VectorType::get(FloatTy, 2)); + Ops[0] = + Builder.CreateBitCast(Ops[0], llvm::FixedVectorType::get(FloatTy, 2)); return Builder.CreateExtractElement(Ops[0], EmitScalarExpr(E->getArg(1)), "vget_lane"); case NEON::BI__builtin_neon_vget_lane_f64: - Ops[0] = Builder.CreateBitCast(Ops[0], - llvm::VectorType::get(DoubleTy, 1)); + Ops[0] = + Builder.CreateBitCast(Ops[0], llvm::FixedVectorType::get(DoubleTy, 1)); return Builder.CreateExtractElement(Ops[0], EmitScalarExpr(E->getArg(1)), "vget_lane"); case NEON::BI__builtin_neon_vgetq_lane_f32: case NEON::BI__builtin_neon_vdups_laneq_f32: - Ops[0] = Builder.CreateBitCast(Ops[0], - llvm::VectorType::get(FloatTy, 4)); + Ops[0] = + Builder.CreateBitCast(Ops[0], llvm::FixedVectorType::get(FloatTy, 4)); return Builder.CreateExtractElement(Ops[0], EmitScalarExpr(E->getArg(1)), "vgetq_lane"); case NEON::BI__builtin_neon_vgetq_lane_f64: case NEON::BI__builtin_neon_vdupd_laneq_f64: - Ops[0] = Builder.CreateBitCast(Ops[0], - llvm::VectorType::get(DoubleTy, 2)); + Ops[0] = + Builder.CreateBitCast(Ops[0], llvm::FixedVectorType::get(DoubleTy, 2)); return Builder.CreateExtractElement(Ops[0], EmitScalarExpr(E->getArg(1)), "vgetq_lane"); case NEON::BI__builtin_neon_vaddh_f16: @@ -9191,7 +9199,7 @@ Value *CodeGenFunction::EmitAArch64BuiltinExpr(unsigned BuiltinID, SmallVector ProductOps; ProductOps.push_back(vectorWrapScalar16(Ops[1])); ProductOps.push_back(vectorWrapScalar16(EmitScalarExpr(E->getArg(2)))); - llvm::Type *VTy = llvm::VectorType::get(Int32Ty, 4); + auto *VTy = llvm::FixedVectorType::get(Int32Ty, 4); Ops[1] = EmitNeonCall(CGM.getIntrinsic(Intrinsic::aarch64_neon_sqdmull, VTy), ProductOps, "vqdmlXl"); Constant *CI = ConstantInt::get(SizeTy, 0); @@ -9288,7 +9296,7 @@ Value *CodeGenFunction::EmitAArch64BuiltinExpr(unsigned BuiltinID, SmallVector ProductOps; ProductOps.push_back(vectorWrapScalar16(Ops[1])); ProductOps.push_back(vectorWrapScalar16(Ops[2])); - llvm::Type *VTy = llvm::VectorType::get(Int32Ty, 4); + auto *VTy = llvm::FixedVectorType::get(Int32Ty, 4); Ops[1] = EmitNeonCall(CGM.getIntrinsic(Intrinsic::aarch64_neon_sqdmull, VTy), ProductOps, "vqdmlXl"); Constant *CI = ConstantInt::get(SizeTy, 0); @@ -9536,9 +9544,10 @@ Value *CodeGenFunction::EmitAArch64BuiltinExpr(unsigned BuiltinID, Ops[2] = Addend; // Now adjust things to handle the lane access. - llvm::Type *SourceTy = BuiltinID == NEON::BI__builtin_neon_vfmaq_lane_v ? - llvm::VectorType::get(VTy->getElementType(), VTy->getNumElements() / 2) : - VTy; + auto *SourceTy = BuiltinID == NEON::BI__builtin_neon_vfmaq_lane_v + ? llvm::FixedVectorType::get(VTy->getElementType(), + VTy->getNumElements() / 2) + : VTy; llvm::Constant *cst = cast(Ops[3]); Value *SV = llvm::ConstantVector::getSplat(VTy->getElementCount(), cst); Ops[1] = Builder.CreateBitCast(Ops[1], SourceTy); @@ -9568,8 +9577,8 @@ Value *CodeGenFunction::EmitAArch64BuiltinExpr(unsigned BuiltinID, Ops[0] = Builder.CreateBitCast(Ops[0], Ty); Ops[1] = Builder.CreateBitCast(Ops[1], Ty); - llvm::Type *STy = llvm::VectorType::get(VTy->getElementType(), - VTy->getNumElements() * 2); + auto *STy = llvm::FixedVectorType::get(VTy->getElementType(), + VTy->getNumElements() * 2); Ops[2] = Builder.CreateBitCast(Ops[2], STy); Value *SV = llvm::ConstantVector::getSplat(VTy->getElementCount(), cast(Ops[3])); @@ -9640,8 +9649,8 @@ Value *CodeGenFunction::EmitAArch64BuiltinExpr(unsigned BuiltinID, unsigned ArgElts = VTy->getNumElements(); llvm::IntegerType *EltTy = cast(VTy->getElementType()); unsigned BitWidth = EltTy->getBitWidth(); - llvm::Type *ArgTy = llvm::VectorType::get( - llvm::IntegerType::get(getLLVMContext(), BitWidth/2), 2*ArgElts); + auto *ArgTy = llvm::FixedVectorType::get( + llvm::IntegerType::get(getLLVMContext(), BitWidth / 2), 2 * ArgElts); llvm::Type* Tys[2] = { VTy, ArgTy }; Int = usgn ? Intrinsic::aarch64_neon_uaddlp : Intrinsic::aarch64_neon_saddlp; SmallVector TmpOps; @@ -9972,7 +9981,7 @@ Value *CodeGenFunction::EmitAArch64BuiltinExpr(unsigned BuiltinID, case NEON::BI__builtin_neon_vaddv_s8: { Int = usgn ? Intrinsic::aarch64_neon_uaddv : Intrinsic::aarch64_neon_saddv; Ty = Int32Ty; - VTy = llvm::VectorType::get(Int8Ty, 8); + VTy = llvm::FixedVectorType::get(Int8Ty, 8); llvm::Type *Tys[2] = { Ty, VTy }; Ops.push_back(EmitScalarExpr(E->getArg(0))); Ops[0] = EmitNeonCall(CGM.getIntrinsic(Int, Tys), Ops, "vaddv"); @@ -9984,7 +9993,7 @@ Value *CodeGenFunction::EmitAArch64BuiltinExpr(unsigned BuiltinID, case NEON::BI__builtin_neon_vaddv_s16: { Int = usgn ? Intrinsic::aarch64_neon_uaddv : Intrinsic::aarch64_neon_saddv; Ty = Int32Ty; - VTy = llvm::VectorType::get(Int16Ty, 4); + VTy = llvm::FixedVectorType::get(Int16Ty, 4); llvm::Type *Tys[2] = { Ty, VTy }; Ops.push_back(EmitScalarExpr(E->getArg(0))); Ops[0] = EmitNeonCall(CGM.getIntrinsic(Int, Tys), Ops, "vaddv"); @@ -9996,7 +10005,7 @@ Value *CodeGenFunction::EmitAArch64BuiltinExpr(unsigned BuiltinID, case NEON::BI__builtin_neon_vaddvq_s8: { Int = usgn ? Intrinsic::aarch64_neon_uaddv : Intrinsic::aarch64_neon_saddv; Ty = Int32Ty; - VTy = llvm::VectorType::get(Int8Ty, 16); + VTy = llvm::FixedVectorType::get(Int8Ty, 16); llvm::Type *Tys[2] = { Ty, VTy }; Ops.push_back(EmitScalarExpr(E->getArg(0))); Ops[0] = EmitNeonCall(CGM.getIntrinsic(Int, Tys), Ops, "vaddv"); @@ -10008,7 +10017,7 @@ Value *CodeGenFunction::EmitAArch64BuiltinExpr(unsigned BuiltinID, case NEON::BI__builtin_neon_vaddvq_s16: { Int = usgn ? Intrinsic::aarch64_neon_uaddv : Intrinsic::aarch64_neon_saddv; Ty = Int32Ty; - VTy = llvm::VectorType::get(Int16Ty, 8); + VTy = llvm::FixedVectorType::get(Int16Ty, 8); llvm::Type *Tys[2] = { Ty, VTy }; Ops.push_back(EmitScalarExpr(E->getArg(0))); Ops[0] = EmitNeonCall(CGM.getIntrinsic(Int, Tys), Ops, "vaddv"); @@ -10017,7 +10026,7 @@ Value *CodeGenFunction::EmitAArch64BuiltinExpr(unsigned BuiltinID, case NEON::BI__builtin_neon_vmaxv_u8: { Int = Intrinsic::aarch64_neon_umaxv; Ty = Int32Ty; - VTy = llvm::VectorType::get(Int8Ty, 8); + VTy = llvm::FixedVectorType::get(Int8Ty, 8); llvm::Type *Tys[2] = { Ty, VTy }; Ops.push_back(EmitScalarExpr(E->getArg(0))); Ops[0] = EmitNeonCall(CGM.getIntrinsic(Int, Tys), Ops, "vmaxv"); @@ -10026,7 +10035,7 @@ Value *CodeGenFunction::EmitAArch64BuiltinExpr(unsigned BuiltinID, case NEON::BI__builtin_neon_vmaxv_u16: { Int = Intrinsic::aarch64_neon_umaxv; Ty = Int32Ty; - VTy = llvm::VectorType::get(Int16Ty, 4); + VTy = llvm::FixedVectorType::get(Int16Ty, 4); llvm::Type *Tys[2] = { Ty, VTy }; Ops.push_back(EmitScalarExpr(E->getArg(0))); Ops[0] = EmitNeonCall(CGM.getIntrinsic(Int, Tys), Ops, "vmaxv"); @@ -10035,7 +10044,7 @@ Value *CodeGenFunction::EmitAArch64BuiltinExpr(unsigned BuiltinID, case NEON::BI__builtin_neon_vmaxvq_u8: { Int = Intrinsic::aarch64_neon_umaxv; Ty = Int32Ty; - VTy = llvm::VectorType::get(Int8Ty, 16); + VTy = llvm::FixedVectorType::get(Int8Ty, 16); llvm::Type *Tys[2] = { Ty, VTy }; Ops.push_back(EmitScalarExpr(E->getArg(0))); Ops[0] = EmitNeonCall(CGM.getIntrinsic(Int, Tys), Ops, "vmaxv"); @@ -10044,7 +10053,7 @@ Value *CodeGenFunction::EmitAArch64BuiltinExpr(unsigned BuiltinID, case NEON::BI__builtin_neon_vmaxvq_u16: { Int = Intrinsic::aarch64_neon_umaxv; Ty = Int32Ty; - VTy = llvm::VectorType::get(Int16Ty, 8); + VTy = llvm::FixedVectorType::get(Int16Ty, 8); llvm::Type *Tys[2] = { Ty, VTy }; Ops.push_back(EmitScalarExpr(E->getArg(0))); Ops[0] = EmitNeonCall(CGM.getIntrinsic(Int, Tys), Ops, "vmaxv"); @@ -10053,7 +10062,7 @@ Value *CodeGenFunction::EmitAArch64BuiltinExpr(unsigned BuiltinID, case NEON::BI__builtin_neon_vmaxv_s8: { Int = Intrinsic::aarch64_neon_smaxv; Ty = Int32Ty; - VTy = llvm::VectorType::get(Int8Ty, 8); + VTy = llvm::FixedVectorType::get(Int8Ty, 8); llvm::Type *Tys[2] = { Ty, VTy }; Ops.push_back(EmitScalarExpr(E->getArg(0))); Ops[0] = EmitNeonCall(CGM.getIntrinsic(Int, Tys), Ops, "vmaxv"); @@ -10062,7 +10071,7 @@ Value *CodeGenFunction::EmitAArch64BuiltinExpr(unsigned BuiltinID, case NEON::BI__builtin_neon_vmaxv_s16: { Int = Intrinsic::aarch64_neon_smaxv; Ty = Int32Ty; - VTy = llvm::VectorType::get(Int16Ty, 4); + VTy = llvm::FixedVectorType::get(Int16Ty, 4); llvm::Type *Tys[2] = { Ty, VTy }; Ops.push_back(EmitScalarExpr(E->getArg(0))); Ops[0] = EmitNeonCall(CGM.getIntrinsic(Int, Tys), Ops, "vmaxv"); @@ -10071,7 +10080,7 @@ Value *CodeGenFunction::EmitAArch64BuiltinExpr(unsigned BuiltinID, case NEON::BI__builtin_neon_vmaxvq_s8: { Int = Intrinsic::aarch64_neon_smaxv; Ty = Int32Ty; - VTy = llvm::VectorType::get(Int8Ty, 16); + VTy = llvm::FixedVectorType::get(Int8Ty, 16); llvm::Type *Tys[2] = { Ty, VTy }; Ops.push_back(EmitScalarExpr(E->getArg(0))); Ops[0] = EmitNeonCall(CGM.getIntrinsic(Int, Tys), Ops, "vmaxv"); @@ -10080,7 +10089,7 @@ Value *CodeGenFunction::EmitAArch64BuiltinExpr(unsigned BuiltinID, case NEON::BI__builtin_neon_vmaxvq_s16: { Int = Intrinsic::aarch64_neon_smaxv; Ty = Int32Ty; - VTy = llvm::VectorType::get(Int16Ty, 8); + VTy = llvm::FixedVectorType::get(Int16Ty, 8); llvm::Type *Tys[2] = { Ty, VTy }; Ops.push_back(EmitScalarExpr(E->getArg(0))); Ops[0] = EmitNeonCall(CGM.getIntrinsic(Int, Tys), Ops, "vmaxv"); @@ -10089,7 +10098,7 @@ Value *CodeGenFunction::EmitAArch64BuiltinExpr(unsigned BuiltinID, case NEON::BI__builtin_neon_vmaxv_f16: { Int = Intrinsic::aarch64_neon_fmaxv; Ty = HalfTy; - VTy = llvm::VectorType::get(HalfTy, 4); + VTy = llvm::FixedVectorType::get(HalfTy, 4); llvm::Type *Tys[2] = { Ty, VTy }; Ops.push_back(EmitScalarExpr(E->getArg(0))); Ops[0] = EmitNeonCall(CGM.getIntrinsic(Int, Tys), Ops, "vmaxv"); @@ -10098,7 +10107,7 @@ Value *CodeGenFunction::EmitAArch64BuiltinExpr(unsigned BuiltinID, case NEON::BI__builtin_neon_vmaxvq_f16: { Int = Intrinsic::aarch64_neon_fmaxv; Ty = HalfTy; - VTy = llvm::VectorType::get(HalfTy, 8); + VTy = llvm::FixedVectorType::get(HalfTy, 8); llvm::Type *Tys[2] = { Ty, VTy }; Ops.push_back(EmitScalarExpr(E->getArg(0))); Ops[0] = EmitNeonCall(CGM.getIntrinsic(Int, Tys), Ops, "vmaxv"); @@ -10107,7 +10116,7 @@ Value *CodeGenFunction::EmitAArch64BuiltinExpr(unsigned BuiltinID, case NEON::BI__builtin_neon_vminv_u8: { Int = Intrinsic::aarch64_neon_uminv; Ty = Int32Ty; - VTy = llvm::VectorType::get(Int8Ty, 8); + VTy = llvm::FixedVectorType::get(Int8Ty, 8); llvm::Type *Tys[2] = { Ty, VTy }; Ops.push_back(EmitScalarExpr(E->getArg(0))); Ops[0] = EmitNeonCall(CGM.getIntrinsic(Int, Tys), Ops, "vminv"); @@ -10116,7 +10125,7 @@ Value *CodeGenFunction::EmitAArch64BuiltinExpr(unsigned BuiltinID, case NEON::BI__builtin_neon_vminv_u16: { Int = Intrinsic::aarch64_neon_uminv; Ty = Int32Ty; - VTy = llvm::VectorType::get(Int16Ty, 4); + VTy = llvm::FixedVectorType::get(Int16Ty, 4); llvm::Type *Tys[2] = { Ty, VTy }; Ops.push_back(EmitScalarExpr(E->getArg(0))); Ops[0] = EmitNeonCall(CGM.getIntrinsic(Int, Tys), Ops, "vminv"); @@ -10125,7 +10134,7 @@ Value *CodeGenFunction::EmitAArch64BuiltinExpr(unsigned BuiltinID, case NEON::BI__builtin_neon_vminvq_u8: { Int = Intrinsic::aarch64_neon_uminv; Ty = Int32Ty; - VTy = llvm::VectorType::get(Int8Ty, 16); + VTy = llvm::FixedVectorType::get(Int8Ty, 16); llvm::Type *Tys[2] = { Ty, VTy }; Ops.push_back(EmitScalarExpr(E->getArg(0))); Ops[0] = EmitNeonCall(CGM.getIntrinsic(Int, Tys), Ops, "vminv"); @@ -10134,7 +10143,7 @@ Value *CodeGenFunction::EmitAArch64BuiltinExpr(unsigned BuiltinID, case NEON::BI__builtin_neon_vminvq_u16: { Int = Intrinsic::aarch64_neon_uminv; Ty = Int32Ty; - VTy = llvm::VectorType::get(Int16Ty, 8); + VTy = llvm::FixedVectorType::get(Int16Ty, 8); llvm::Type *Tys[2] = { Ty, VTy }; Ops.push_back(EmitScalarExpr(E->getArg(0))); Ops[0] = EmitNeonCall(CGM.getIntrinsic(Int, Tys), Ops, "vminv"); @@ -10143,7 +10152,7 @@ Value *CodeGenFunction::EmitAArch64BuiltinExpr(unsigned BuiltinID, case NEON::BI__builtin_neon_vminv_s8: { Int = Intrinsic::aarch64_neon_sminv; Ty = Int32Ty; - VTy = llvm::VectorType::get(Int8Ty, 8); + VTy = llvm::FixedVectorType::get(Int8Ty, 8); llvm::Type *Tys[2] = { Ty, VTy }; Ops.push_back(EmitScalarExpr(E->getArg(0))); Ops[0] = EmitNeonCall(CGM.getIntrinsic(Int, Tys), Ops, "vminv"); @@ -10152,7 +10161,7 @@ Value *CodeGenFunction::EmitAArch64BuiltinExpr(unsigned BuiltinID, case NEON::BI__builtin_neon_vminv_s16: { Int = Intrinsic::aarch64_neon_sminv; Ty = Int32Ty; - VTy = llvm::VectorType::get(Int16Ty, 4); + VTy = llvm::FixedVectorType::get(Int16Ty, 4); llvm::Type *Tys[2] = { Ty, VTy }; Ops.push_back(EmitScalarExpr(E->getArg(0))); Ops[0] = EmitNeonCall(CGM.getIntrinsic(Int, Tys), Ops, "vminv"); @@ -10161,7 +10170,7 @@ Value *CodeGenFunction::EmitAArch64BuiltinExpr(unsigned BuiltinID, case NEON::BI__builtin_neon_vminvq_s8: { Int = Intrinsic::aarch64_neon_sminv; Ty = Int32Ty; - VTy = llvm::VectorType::get(Int8Ty, 16); + VTy = llvm::FixedVectorType::get(Int8Ty, 16); llvm::Type *Tys[2] = { Ty, VTy }; Ops.push_back(EmitScalarExpr(E->getArg(0))); Ops[0] = EmitNeonCall(CGM.getIntrinsic(Int, Tys), Ops, "vminv"); @@ -10170,7 +10179,7 @@ Value *CodeGenFunction::EmitAArch64BuiltinExpr(unsigned BuiltinID, case NEON::BI__builtin_neon_vminvq_s16: { Int = Intrinsic::aarch64_neon_sminv; Ty = Int32Ty; - VTy = llvm::VectorType::get(Int16Ty, 8); + VTy = llvm::FixedVectorType::get(Int16Ty, 8); llvm::Type *Tys[2] = { Ty, VTy }; Ops.push_back(EmitScalarExpr(E->getArg(0))); Ops[0] = EmitNeonCall(CGM.getIntrinsic(Int, Tys), Ops, "vminv"); @@ -10179,7 +10188,7 @@ Value *CodeGenFunction::EmitAArch64BuiltinExpr(unsigned BuiltinID, case NEON::BI__builtin_neon_vminv_f16: { Int = Intrinsic::aarch64_neon_fminv; Ty = HalfTy; - VTy = llvm::VectorType::get(HalfTy, 4); + VTy = llvm::FixedVectorType::get(HalfTy, 4); llvm::Type *Tys[2] = { Ty, VTy }; Ops.push_back(EmitScalarExpr(E->getArg(0))); Ops[0] = EmitNeonCall(CGM.getIntrinsic(Int, Tys), Ops, "vminv"); @@ -10188,7 +10197,7 @@ Value *CodeGenFunction::EmitAArch64BuiltinExpr(unsigned BuiltinID, case NEON::BI__builtin_neon_vminvq_f16: { Int = Intrinsic::aarch64_neon_fminv; Ty = HalfTy; - VTy = llvm::VectorType::get(HalfTy, 8); + VTy = llvm::FixedVectorType::get(HalfTy, 8); llvm::Type *Tys[2] = { Ty, VTy }; Ops.push_back(EmitScalarExpr(E->getArg(0))); Ops[0] = EmitNeonCall(CGM.getIntrinsic(Int, Tys), Ops, "vminv"); @@ -10197,7 +10206,7 @@ Value *CodeGenFunction::EmitAArch64BuiltinExpr(unsigned BuiltinID, case NEON::BI__builtin_neon_vmaxnmv_f16: { Int = Intrinsic::aarch64_neon_fmaxnmv; Ty = HalfTy; - VTy = llvm::VectorType::get(HalfTy, 4); + VTy = llvm::FixedVectorType::get(HalfTy, 4); llvm::Type *Tys[2] = { Ty, VTy }; Ops.push_back(EmitScalarExpr(E->getArg(0))); Ops[0] = EmitNeonCall(CGM.getIntrinsic(Int, Tys), Ops, "vmaxnmv"); @@ -10206,7 +10215,7 @@ Value *CodeGenFunction::EmitAArch64BuiltinExpr(unsigned BuiltinID, case NEON::BI__builtin_neon_vmaxnmvq_f16: { Int = Intrinsic::aarch64_neon_fmaxnmv; Ty = HalfTy; - VTy = llvm::VectorType::get(HalfTy, 8); + VTy = llvm::FixedVectorType::get(HalfTy, 8); llvm::Type *Tys[2] = { Ty, VTy }; Ops.push_back(EmitScalarExpr(E->getArg(0))); Ops[0] = EmitNeonCall(CGM.getIntrinsic(Int, Tys), Ops, "vmaxnmv"); @@ -10215,7 +10224,7 @@ Value *CodeGenFunction::EmitAArch64BuiltinExpr(unsigned BuiltinID, case NEON::BI__builtin_neon_vminnmv_f16: { Int = Intrinsic::aarch64_neon_fminnmv; Ty = HalfTy; - VTy = llvm::VectorType::get(HalfTy, 4); + VTy = llvm::FixedVectorType::get(HalfTy, 4); llvm::Type *Tys[2] = { Ty, VTy }; Ops.push_back(EmitScalarExpr(E->getArg(0))); Ops[0] = EmitNeonCall(CGM.getIntrinsic(Int, Tys), Ops, "vminnmv"); @@ -10224,7 +10233,7 @@ Value *CodeGenFunction::EmitAArch64BuiltinExpr(unsigned BuiltinID, case NEON::BI__builtin_neon_vminnmvq_f16: { Int = Intrinsic::aarch64_neon_fminnmv; Ty = HalfTy; - VTy = llvm::VectorType::get(HalfTy, 8); + VTy = llvm::FixedVectorType::get(HalfTy, 8); llvm::Type *Tys[2] = { Ty, VTy }; Ops.push_back(EmitScalarExpr(E->getArg(0))); Ops[0] = EmitNeonCall(CGM.getIntrinsic(Int, Tys), Ops, "vminnmv"); @@ -10238,7 +10247,7 @@ Value *CodeGenFunction::EmitAArch64BuiltinExpr(unsigned BuiltinID, case NEON::BI__builtin_neon_vaddlv_u8: { Int = Intrinsic::aarch64_neon_uaddlv; Ty = Int32Ty; - VTy = llvm::VectorType::get(Int8Ty, 8); + VTy = llvm::FixedVectorType::get(Int8Ty, 8); llvm::Type *Tys[2] = { Ty, VTy }; Ops.push_back(EmitScalarExpr(E->getArg(0))); Ops[0] = EmitNeonCall(CGM.getIntrinsic(Int, Tys), Ops, "vaddlv"); @@ -10247,7 +10256,7 @@ Value *CodeGenFunction::EmitAArch64BuiltinExpr(unsigned BuiltinID, case NEON::BI__builtin_neon_vaddlv_u16: { Int = Intrinsic::aarch64_neon_uaddlv; Ty = Int32Ty; - VTy = llvm::VectorType::get(Int16Ty, 4); + VTy = llvm::FixedVectorType::get(Int16Ty, 4); llvm::Type *Tys[2] = { Ty, VTy }; Ops.push_back(EmitScalarExpr(E->getArg(0))); return EmitNeonCall(CGM.getIntrinsic(Int, Tys), Ops, "vaddlv"); @@ -10255,7 +10264,7 @@ Value *CodeGenFunction::EmitAArch64BuiltinExpr(unsigned BuiltinID, case NEON::BI__builtin_neon_vaddlvq_u8: { Int = Intrinsic::aarch64_neon_uaddlv; Ty = Int32Ty; - VTy = llvm::VectorType::get(Int8Ty, 16); + VTy = llvm::FixedVectorType::get(Int8Ty, 16); llvm::Type *Tys[2] = { Ty, VTy }; Ops.push_back(EmitScalarExpr(E->getArg(0))); Ops[0] = EmitNeonCall(CGM.getIntrinsic(Int, Tys), Ops, "vaddlv"); @@ -10264,7 +10273,7 @@ Value *CodeGenFunction::EmitAArch64BuiltinExpr(unsigned BuiltinID, case NEON::BI__builtin_neon_vaddlvq_u16: { Int = Intrinsic::aarch64_neon_uaddlv; Ty = Int32Ty; - VTy = llvm::VectorType::get(Int16Ty, 8); + VTy = llvm::FixedVectorType::get(Int16Ty, 8); llvm::Type *Tys[2] = { Ty, VTy }; Ops.push_back(EmitScalarExpr(E->getArg(0))); return EmitNeonCall(CGM.getIntrinsic(Int, Tys), Ops, "vaddlv"); @@ -10272,7 +10281,7 @@ Value *CodeGenFunction::EmitAArch64BuiltinExpr(unsigned BuiltinID, case NEON::BI__builtin_neon_vaddlv_s8: { Int = Intrinsic::aarch64_neon_saddlv; Ty = Int32Ty; - VTy = llvm::VectorType::get(Int8Ty, 8); + VTy = llvm::FixedVectorType::get(Int8Ty, 8); llvm::Type *Tys[2] = { Ty, VTy }; Ops.push_back(EmitScalarExpr(E->getArg(0))); Ops[0] = EmitNeonCall(CGM.getIntrinsic(Int, Tys), Ops, "vaddlv"); @@ -10281,7 +10290,7 @@ Value *CodeGenFunction::EmitAArch64BuiltinExpr(unsigned BuiltinID, case NEON::BI__builtin_neon_vaddlv_s16: { Int = Intrinsic::aarch64_neon_saddlv; Ty = Int32Ty; - VTy = llvm::VectorType::get(Int16Ty, 4); + VTy = llvm::FixedVectorType::get(Int16Ty, 4); llvm::Type *Tys[2] = { Ty, VTy }; Ops.push_back(EmitScalarExpr(E->getArg(0))); return EmitNeonCall(CGM.getIntrinsic(Int, Tys), Ops, "vaddlv"); @@ -10289,7 +10298,7 @@ Value *CodeGenFunction::EmitAArch64BuiltinExpr(unsigned BuiltinID, case NEON::BI__builtin_neon_vaddlvq_s8: { Int = Intrinsic::aarch64_neon_saddlv; Ty = Int32Ty; - VTy = llvm::VectorType::get(Int8Ty, 16); + VTy = llvm::FixedVectorType::get(Int8Ty, 16); llvm::Type *Tys[2] = { Ty, VTy }; Ops.push_back(EmitScalarExpr(E->getArg(0))); Ops[0] = EmitNeonCall(CGM.getIntrinsic(Int, Tys), Ops, "vaddlv"); @@ -10298,7 +10307,7 @@ Value *CodeGenFunction::EmitAArch64BuiltinExpr(unsigned BuiltinID, case NEON::BI__builtin_neon_vaddlvq_s16: { Int = Intrinsic::aarch64_neon_saddlv; Ty = Int32Ty; - VTy = llvm::VectorType::get(Int16Ty, 8); + VTy = llvm::FixedVectorType::get(Int16Ty, 8); llvm::Type *Tys[2] = { Ty, VTy }; Ops.push_back(EmitScalarExpr(E->getArg(0))); return EmitNeonCall(CGM.getIntrinsic(Int, Tys), Ops, "vaddlv"); @@ -10758,8 +10767,8 @@ BuildVector(ArrayRef Ops) { } // Otherwise, insertelement the values to build the vector. - Value *Result = - llvm::UndefValue::get(llvm::VectorType::get(Ops[0]->getType(), Ops.size())); + Value *Result = llvm::UndefValue::get( + llvm::FixedVectorType::get(Ops[0]->getType(), Ops.size())); for (unsigned i = 0, e = Ops.size(); i != e; ++i) Result = Builder.CreateInsertElement(Result, Ops[i], Builder.getInt32(i)); @@ -10771,8 +10780,9 @@ BuildVector(ArrayRef Ops) { static Value *getMaskVecValue(CodeGenFunction &CGF, Value *Mask, unsigned NumElts) { - llvm::VectorType *MaskTy = llvm::VectorType::get(CGF.Builder.getInt1Ty(), - cast(Mask->getType())->getBitWidth()); + auto *MaskTy = llvm::FixedVectorType::get( + CGF.Builder.getInt1Ty(), + cast(Mask->getType())->getBitWidth()); Value *MaskVec = CGF.Builder.CreateBitCast(Mask, MaskTy); // If we have less than 8 elements, then the starting mask was an i8 and @@ -10950,9 +10960,8 @@ static Value *EmitX86ScalarSelect(CodeGenFunction &CGF, if (C->isAllOnesValue()) return Op0; - llvm::VectorType *MaskTy = - llvm::VectorType::get(CGF.Builder.getInt1Ty(), - Mask->getType()->getIntegerBitWidth()); + auto *MaskTy = llvm::FixedVectorType::get( + CGF.Builder.getInt1Ty(), Mask->getType()->getIntegerBitWidth()); Mask = CGF.Builder.CreateBitCast(Mask, MaskTy); Mask = CGF.Builder.CreateExtractElement(Mask, (uint64_t)0); return CGF.Builder.CreateSelect(Mask, Op0, Op1); @@ -10991,10 +11000,10 @@ static Value *EmitX86MaskedCompare(CodeGenFunction &CGF, unsigned CC, if (CC == 3) { Cmp = Constant::getNullValue( - llvm::VectorType::get(CGF.Builder.getInt1Ty(), NumElts)); + llvm::FixedVectorType::get(CGF.Builder.getInt1Ty(), NumElts)); } else if (CC == 7) { Cmp = Constant::getAllOnesValue( - llvm::VectorType::get(CGF.Builder.getInt1Ty(), NumElts)); + llvm::FixedVectorType::get(CGF.Builder.getInt1Ty(), NumElts)); } else { ICmpInst::Predicate Pred; switch (CC) { @@ -11208,8 +11217,8 @@ static Value *EmitX86Muldq(CodeGenFunction &CGF, bool IsSigned, ArrayRef Ops) { llvm::Type *Ty = Ops[0]->getType(); // Arguments have a vXi32 type so cast to vXi64. - Ty = llvm::VectorType::get(CGF.Int64Ty, - Ty->getPrimitiveSizeInBits() / 64); + Ty = llvm::FixedVectorType::get(CGF.Int64Ty, + Ty->getPrimitiveSizeInBits() / 64); Value *LHS = CGF.Builder.CreateBitCast(Ops[0], Ty); Value *RHS = CGF.Builder.CreateBitCast(Ops[1], Ty); @@ -11310,7 +11319,7 @@ static Value *EmitX86CvtF16ToFloatExpr(CodeGenFunction &CGF, } // Bitcast from vXi16 to vXf16. - llvm::Type *HalfTy = llvm::VectorType::get( + auto *HalfTy = llvm::FixedVectorType::get( llvm::Type::getHalfTy(CGF.getLLVMContext()), NumDstElts); Src = CGF.Builder.CreateBitCast(Src, HalfTy); @@ -12575,7 +12584,7 @@ Value *CodeGenFunction::EmitX86BuiltinExpr(unsigned BuiltinID, } } - llvm::Type *VecTy = llvm::VectorType::get(Int8Ty, NumElts); + auto *VecTy = llvm::FixedVectorType::get(Int8Ty, NumElts); Value *Cast = Builder.CreateBitCast(Ops[0], VecTy, "cast"); Value *Zero = llvm::Constant::getNullValue(VecTy); Value *SV = Builder.CreateShuffleVector(Zero, Cast, @@ -12605,7 +12614,7 @@ Value *CodeGenFunction::EmitX86BuiltinExpr(unsigned BuiltinID, } } - llvm::Type *VecTy = llvm::VectorType::get(Int8Ty, NumElts); + auto *VecTy = llvm::FixedVectorType::get(Int8Ty, NumElts); Value *Cast = Builder.CreateBitCast(Ops[0], VecTy, "cast"); Value *Zero = llvm::Constant::getNullValue(VecTy); Value *SV = Builder.CreateShuffleVector(Cast, Zero, @@ -14083,11 +14092,13 @@ Value *CodeGenFunction::EmitPPCBuiltinExpr(unsigned BuiltinID, // Need to cast the second argument from a vector of unsigned int to a // vector of long long. - Ops[1] = Builder.CreateBitCast(Ops[1], llvm::VectorType::get(Int64Ty, 2)); + Ops[1] = + Builder.CreateBitCast(Ops[1], llvm::FixedVectorType::get(Int64Ty, 2)); if (getTarget().isLittleEndian()) { // Reverse the double words in the vector we will extract from. - Ops[0] = Builder.CreateBitCast(Ops[0], llvm::VectorType::get(Int64Ty, 2)); + Ops[0] = + Builder.CreateBitCast(Ops[0], llvm::FixedVectorType::get(Int64Ty, 2)); Ops[0] = Builder.CreateShuffleVector(Ops[0], Ops[0], ArrayRef{1, 0}); // Reverse the index. @@ -14095,7 +14106,8 @@ Value *CodeGenFunction::EmitPPCBuiltinExpr(unsigned BuiltinID, } // Intrinsic expects the first arg to be a vector of int. - Ops[0] = Builder.CreateBitCast(Ops[0], llvm::VectorType::get(Int32Ty, 4)); + Ops[0] = + Builder.CreateBitCast(Ops[0], llvm::FixedVectorType::get(Int32Ty, 4)); Ops[2] = ConstantInt::getSigned(Int32Ty, Index); return Builder.CreateCall(F, Ops); } @@ -14104,7 +14116,8 @@ Value *CodeGenFunction::EmitPPCBuiltinExpr(unsigned BuiltinID, llvm::Function *F = CGM.getIntrinsic(Intrinsic::ppc_vsx_xxextractuw); // Intrinsic expects the first argument to be a vector of doublewords. - Ops[0] = Builder.CreateBitCast(Ops[0], llvm::VectorType::get(Int64Ty, 2)); + Ops[0] = + Builder.CreateBitCast(Ops[0], llvm::FixedVectorType::get(Int64Ty, 2)); // The second argument is a compile time constant int that needs to // be clamped to the range [0, 12]. @@ -14136,8 +14149,10 @@ Value *CodeGenFunction::EmitPPCBuiltinExpr(unsigned BuiltinID, assert(ArgCI && "Third arg must be constant integer!"); unsigned Index = ArgCI->getZExtValue(); - Ops[0] = Builder.CreateBitCast(Ops[0], llvm::VectorType::get(Int64Ty, 2)); - Ops[1] = Builder.CreateBitCast(Ops[1], llvm::VectorType::get(Int64Ty, 2)); + Ops[0] = + Builder.CreateBitCast(Ops[0], llvm::FixedVectorType::get(Int64Ty, 2)); + Ops[1] = + Builder.CreateBitCast(Ops[1], llvm::FixedVectorType::get(Int64Ty, 2)); // Account for endianness by treating this as just a shuffle. So we use the // same indices for both LE and BE in order to produce expected results in @@ -14157,8 +14172,10 @@ Value *CodeGenFunction::EmitPPCBuiltinExpr(unsigned BuiltinID, ConstantInt *ArgCI = dyn_cast(Ops[2]); assert(ArgCI && "Third argument must be a compile time constant"); unsigned Index = ArgCI->getZExtValue() & 0x3; - Ops[0] = Builder.CreateBitCast(Ops[0], llvm::VectorType::get(Int32Ty, 4)); - Ops[1] = Builder.CreateBitCast(Ops[1], llvm::VectorType::get(Int32Ty, 4)); + Ops[0] = + Builder.CreateBitCast(Ops[0], llvm::FixedVectorType::get(Int32Ty, 4)); + Ops[1] = + Builder.CreateBitCast(Ops[1], llvm::FixedVectorType::get(Int32Ty, 4)); // Create a shuffle mask int ElemIdx0; @@ -14192,7 +14209,7 @@ Value *CodeGenFunction::EmitPPCBuiltinExpr(unsigned BuiltinID, case PPC::BI__builtin_pack_vector_int128: { bool isLittleEndian = getTarget().isLittleEndian(); Value *UndefValue = - llvm::UndefValue::get(llvm::VectorType::get(Ops[0]->getType(), 2)); + llvm::UndefValue::get(llvm::FixedVectorType::get(Ops[0]->getType(), 2)); Value *Res = Builder.CreateInsertElement( UndefValue, Ops[0], (uint64_t)(isLittleEndian ? 1 : 0)); Res = Builder.CreateInsertElement(Res, Ops[1], @@ -14203,7 +14220,7 @@ Value *CodeGenFunction::EmitPPCBuiltinExpr(unsigned BuiltinID, case PPC::BI__builtin_unpack_vector_int128: { ConstantInt *Index = cast(Ops[1]); Value *Unpacked = Builder.CreateBitCast( - Ops[0], llvm::VectorType::get(ConvertType(E->getType()), 2)); + Ops[0], llvm::FixedVectorType::get(ConvertType(E->getType()), 2)); if (getTarget().isLittleEndian()) Index = ConstantInt::get(Index->getType(), 1 - Index->getZExtValue()); diff --git a/clang/lib/CodeGen/CGDebugInfo.cpp b/clang/lib/CodeGen/CGDebugInfo.cpp index 7ec792ca0e1f4..5be8e77c0b497 100644 --- a/clang/lib/CodeGen/CGDebugInfo.cpp +++ b/clang/lib/CodeGen/CGDebugInfo.cpp @@ -236,6 +236,10 @@ PrintingPolicy CGDebugInfo::getPrintingPolicy() const { if (CGM.getCodeGenOpts().EmitCodeView) { PP.MSVCFormatting = true; PP.SplitTemplateClosers = true; + } else { + // For DWARF, printing rules are underspecified. + // SplitTemplateClosers yields better interop with GCC and GDB (PR46052). + PP.SplitTemplateClosers = true; } // Apply -fdebug-prefix-map. @@ -2732,9 +2736,17 @@ llvm::DIType *CGDebugInfo::CreateType(const VectorType *Ty, QualType QTy(Ty, 0); auto SizeExpr = SizeExprCache.find(QTy); if (SizeExpr != SizeExprCache.end()) - Subscript = DBuilder.getOrCreateSubrange(0, SizeExpr->getSecond()); - else - Subscript = DBuilder.getOrCreateSubrange(0, Count ? Count : -1); + Subscript = DBuilder.getOrCreateSubrange( + SizeExpr->getSecond() /*count*/, nullptr /*lowerBound*/, + nullptr /*upperBound*/, nullptr /*stride*/); + else { + auto *CountNode = + llvm::ConstantAsMetadata::get(llvm::ConstantInt::getSigned( + llvm::Type::getInt64Ty(CGM.getLLVMContext()), Count ? Count : -1)); + Subscript = DBuilder.getOrCreateSubrange( + CountNode /*count*/, nullptr /*lowerBound*/, nullptr /*upperBound*/, + nullptr /*stride*/); + } llvm::DINodeArray SubscriptArray = DBuilder.getOrCreateArray(Subscript); uint64_t Size = CGM.getContext().getTypeSize(Ty); @@ -2754,8 +2766,18 @@ llvm::DIType *CGDebugInfo::CreateType(const ConstantMatrixType *Ty, // Create ranges for both dimensions. llvm::SmallVector Subscripts; - Subscripts.push_back(DBuilder.getOrCreateSubrange(0, Ty->getNumColumns())); - Subscripts.push_back(DBuilder.getOrCreateSubrange(0, Ty->getNumRows())); + auto *ColumnCountNode = + llvm::ConstantAsMetadata::get(llvm::ConstantInt::getSigned( + llvm::Type::getInt64Ty(CGM.getLLVMContext()), Ty->getNumColumns())); + auto *RowCountNode = + llvm::ConstantAsMetadata::get(llvm::ConstantInt::getSigned( + llvm::Type::getInt64Ty(CGM.getLLVMContext()), Ty->getNumRows())); + Subscripts.push_back(DBuilder.getOrCreateSubrange( + ColumnCountNode /*count*/, nullptr /*lowerBound*/, nullptr /*upperBound*/, + nullptr /*stride*/)); + Subscripts.push_back(DBuilder.getOrCreateSubrange( + RowCountNode /*count*/, nullptr /*lowerBound*/, nullptr /*upperBound*/, + nullptr /*stride*/)); llvm::DINodeArray SubscriptArray = DBuilder.getOrCreateArray(Subscripts); return DBuilder.createArrayType(Size, Align, ElementTy, SubscriptArray); } @@ -2810,10 +2832,17 @@ llvm::DIType *CGDebugInfo::CreateType(const ArrayType *Ty, llvm::DIFile *Unit) { auto SizeNode = SizeExprCache.find(EltTy); if (SizeNode != SizeExprCache.end()) - Subscripts.push_back( - DBuilder.getOrCreateSubrange(0, SizeNode->getSecond())); - else - Subscripts.push_back(DBuilder.getOrCreateSubrange(0, Count)); + Subscripts.push_back(DBuilder.getOrCreateSubrange( + SizeNode->getSecond() /*count*/, nullptr /*lowerBound*/, + nullptr /*upperBound*/, nullptr /*stride*/)); + else { + auto *CountNode = + llvm::ConstantAsMetadata::get(llvm::ConstantInt::getSigned( + llvm::Type::getInt64Ty(CGM.getLLVMContext()), Count)); + Subscripts.push_back(DBuilder.getOrCreateSubrange( + CountNode /*count*/, nullptr /*lowerBound*/, nullptr /*upperBound*/, + nullptr /*stride*/)); + } EltTy = Ty->getElementType(); } @@ -3871,7 +3900,7 @@ void CGDebugInfo::EmitFunctionDecl(GlobalDecl GD, SourceLocation Loc, if (IsDeclForCallSite) Fn->setSubprogram(SP); - DBuilder.retainType(SP); + DBuilder.finalizeSubprogram(SP); } void CGDebugInfo::EmitFuncDeclForCallSite(llvm::CallBase *CallOrInvoke, diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp index 4e567f9f06ddc..635986d28ef4f 100644 --- a/clang/lib/CodeGen/CGExpr.cpp +++ b/clang/lib/CodeGen/CGExpr.cpp @@ -151,8 +151,8 @@ Address CodeGenFunction::CreateMemTemp(QualType Ty, CharUnits Align, if (Ty->isConstantMatrixType()) { auto *ArrayTy = cast(Result.getType()->getElementType()); - auto *VectorTy = llvm::VectorType::get(ArrayTy->getElementType(), - ArrayTy->getNumElements()); + auto *VectorTy = llvm::FixedVectorType::get(ArrayTy->getElementType(), + ArrayTy->getNumElements()); Result = Address( Builder.CreateBitCast(Result.getPointer(), VectorTy->getPointerTo()), @@ -1677,8 +1677,7 @@ llvm::Value *CodeGenFunction::EmitLoadOfScalar(Address Addr, bool Volatile, if (VTy->getNumElements() == 3) { // Bitcast to vec4 type. - llvm::VectorType *vec4Ty = - llvm::VectorType::get(VTy->getElementType(), 4); + auto *vec4Ty = llvm::FixedVectorType::get(VTy->getElementType(), 4); Address Cast = Builder.CreateElementBitCast(Addr, vec4Ty, "castToVec4"); // Now load value. llvm::Value *V = Builder.CreateLoad(Cast, Volatile, "loadVec4"); @@ -1749,8 +1748,8 @@ static Address MaybeConvertMatrixAddress(Address Addr, CodeGenFunction &CGF, auto *ArrayTy = dyn_cast( cast(Addr.getPointer()->getType())->getElementType()); if (ArrayTy && IsVector) { - auto *VectorTy = llvm::VectorType::get(ArrayTy->getElementType(), - ArrayTy->getNumElements()); + auto *VectorTy = llvm::FixedVectorType::get(ArrayTy->getElementType(), + ArrayTy->getNumElements()); return Address(CGF.Builder.CreateElementBitCast(Addr, VectorTy)); } @@ -1794,7 +1793,7 @@ void CodeGenFunction::EmitStoreOfScalar(llvm::Value *Value, Address Addr, Value = Builder.CreateShuffleVector(Value, llvm::UndefValue::get(VecTy), ArrayRef{0, 1, 2, -1}, "extractVec"); - SrcTy = llvm::VectorType::get(VecTy->getElementType(), 4); + SrcTy = llvm::FixedVectorType::get(VecTy->getElementType(), 4); } if (Addr.getElementType() != SrcTy) { Addr = Builder.CreateElementBitCast(Addr, SrcTy, "storetmp"); diff --git a/clang/lib/CodeGen/CGExprScalar.cpp b/clang/lib/CodeGen/CGExprScalar.cpp index faeced0296715..e73a1405a6f37 100644 --- a/clang/lib/CodeGen/CGExprScalar.cpp +++ b/clang/lib/CodeGen/CGExprScalar.cpp @@ -37,6 +37,7 @@ #include "llvm/IR/GlobalVariable.h" #include "llvm/IR/Intrinsics.h" #include "llvm/IR/IntrinsicsPowerPC.h" +#include "llvm/IR/MatrixBuilder.h" #include "llvm/IR/Module.h" #include @@ -1667,8 +1668,8 @@ Value *ScalarExprEmitter::VisitShuffleVectorExpr(ShuffleVectorExpr *E) { // n = extract mask i // x = extract val n // newv = insert newv, x, i - llvm::VectorType *RTy = llvm::VectorType::get(LTy->getElementType(), - MTy->getNumElements()); + auto *RTy = llvm::FixedVectorType::get(LTy->getElementType(), + MTy->getNumElements()); Value* NewV = llvm::UndefValue::get(RTy); for (unsigned i = 0, e = MTy->getNumElements(); i != e; ++i) { Value *IIndx = llvm::ConstantInt::get(CGF.SizeTy, i); @@ -3606,6 +3607,11 @@ Value *ScalarExprEmitter::EmitAdd(const BinOpInfo &op) { } } + if (op.Ty->isConstantMatrixType()) { + llvm::MatrixBuilder MB(Builder); + return MB.CreateAdd(op.LHS, op.RHS); + } + if (op.Ty->isUnsignedIntegerType() && CGF.SanOpts.has(SanitizerKind::UnsignedIntegerOverflow) && !CanElideOverflowCheck(CGF.getContext(), op)) @@ -3790,6 +3796,11 @@ Value *ScalarExprEmitter::EmitSub(const BinOpInfo &op) { } } + if (op.Ty->isConstantMatrixType()) { + llvm::MatrixBuilder MB(Builder); + return MB.CreateSub(op.LHS, op.RHS); + } + if (op.Ty->isUnsignedIntegerType() && CGF.SanOpts.has(SanitizerKind::UnsignedIntegerOverflow) && !CanElideOverflowCheck(CGF.getContext(), op)) @@ -4524,10 +4535,8 @@ VisitAbstractConditionalOperator(const AbstractConditionalOperator *E) { llvm::Value *zeroVec = llvm::Constant::getNullValue(vecTy); llvm::Value *TestMSB = Builder.CreateICmpSLT(CondV, zeroVec); - llvm::Value *tmp = Builder.CreateSExt(TestMSB, - llvm::VectorType::get(elemType, - numElem), - "sext"); + llvm::Value *tmp = Builder.CreateSExt( + TestMSB, llvm::FixedVectorType::get(elemType, numElem), "sext"); llvm::Value *tmp2 = Builder.CreateNot(tmp); // Cast float to int to perform ANDs if necessary. @@ -4763,7 +4772,7 @@ Value *ScalarExprEmitter::VisitAsTypeExpr(AsTypeExpr *E) { // get a vec3. if (NumElementsSrc != 3 && NumElementsDst == 3) { if (!CGF.CGM.getCodeGenOpts().PreserveVec3Type) { - auto Vec4Ty = llvm::VectorType::get( + auto *Vec4Ty = llvm::FixedVectorType::get( cast(DstTy)->getElementType(), 4); Src = createCastsForTypeOfSameSize(Builder, CGF.CGM.getDataLayout(), Src, Vec4Ty); diff --git a/clang/lib/CodeGen/CGStmtOpenMP.cpp b/clang/lib/CodeGen/CGStmtOpenMP.cpp index d12aa65af0bae..ae4e3400fcbc4 100644 --- a/clang/lib/CodeGen/CGStmtOpenMP.cpp +++ b/clang/lib/CodeGen/CGStmtOpenMP.cpp @@ -4730,6 +4730,7 @@ static void emitOMPAtomicExpr(CodeGenFunction &CGF, OpenMPClauseKind Kind, case OMPC_to: case OMPC_from: case OMPC_use_device_ptr: + case OMPC_use_device_addr: case OMPC_is_device_ptr: case OMPC_unified_address: case OMPC_unified_shared_memory: diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp index d3135ccfb0adf..775fdd42edae8 100644 --- a/clang/lib/CodeGen/CodeGenModule.cpp +++ b/clang/lib/CodeGen/CodeGenModule.cpp @@ -509,6 +509,9 @@ void CodeGenModule::Release() { if (Context.getLangOpts().SemanticInterposition) // Require various optimization to respect semantic interposition. getModule().setSemanticInterposition(1); + else if (Context.getLangOpts().ExplicitNoSemanticInterposition) + // Allow dso_local on applicable targets. + getModule().setSemanticInterposition(0); if (CodeGenOpts.EmitCodeView) { // Indicate that we want CodeView in the metadata. @@ -540,7 +543,7 @@ void CodeGenModule::Release() { "StrictVTablePointersRequirement", llvm::MDNode::get(VMContext, Ops)); } - if (DebugInfo) + if (getModuleDebugInfo()) // We support a single version in the linked module. The LLVM // parser will drop debug info with a different version number // (and warn about it, too). @@ -685,8 +688,8 @@ void CodeGenModule::Release() { if (getCodeGenOpts().EmitGcovArcs || getCodeGenOpts().EmitGcovNotes) EmitCoverageFile(); - if (DebugInfo) - DebugInfo->finalize(); + if (CGDebugInfo *DI = getModuleDebugInfo()) + DI->finalize(); if (getCodeGenOpts().EmitVersionIdentMetadata) EmitVersionIdentMetadata(); @@ -4342,17 +4345,24 @@ void CodeGenModule::EmitGlobalVarDefinition(const VarDecl *D, GV->setAlignment(getContext().getDeclAlign(D).getAsAlign()); - // On Darwin, if the normal linkage of a C++ thread_local variable is - // LinkOnce or Weak, we keep the normal linkage to prevent multiple - // copies within a linkage unit; otherwise, the backing variable has - // internal linkage and all accesses should just be calls to the - // Itanium-specified entry point, which has the normal linkage of the - // variable. This is to preserve the ability to change the implementation - // behind the scenes. - if (!D->isStaticLocal() && D->getTLSKind() == VarDecl::TLS_Dynamic && + // On Darwin, unlike other Itanium C++ ABI platforms, the thread-wrapper + // function is only defined alongside the variable, not also alongside + // callers. Normally, all accesses to a thread_local go through the + // thread-wrapper in order to ensure initialization has occurred, underlying + // variable will never be used other than the thread-wrapper, so it can be + // converted to internal linkage. + // + // However, if the variable has the 'constinit' attribute, it _can_ be + // referenced directly, without calling the thread-wrapper, so the linkage + // must not be changed. + // + // Additionally, if the variable isn't plain external linkage, e.g. if it's + // weak or linkonce, the de-duplication semantics are important to preserve, + // so we don't change the linkage. + if (D->getTLSKind() == VarDecl::TLS_Dynamic && + Linkage == llvm::GlobalValue::ExternalLinkage && Context.getTargetInfo().getTriple().isOSDarwin() && - !llvm::GlobalVariable::isLinkOnceLinkage(Linkage) && - !llvm::GlobalVariable::isWeakLinkage(Linkage)) + !D->hasAttr()) Linkage = llvm::GlobalValue::InternalLinkage; GV->setLinkage(Linkage); @@ -5566,17 +5576,17 @@ void CodeGenModule::EmitTopLevelDecl(Decl *D) { break; case Decl::ClassTemplateSpecialization: { const auto *Spec = cast(D); - if (DebugInfo && - Spec->getSpecializationKind() == TSK_ExplicitInstantiationDefinition && - Spec->hasDefinition()) - DebugInfo->completeTemplateDefinition(*Spec); + if (CGDebugInfo *DI = getModuleDebugInfo()) + if (Spec->getSpecializationKind() == + TSK_ExplicitInstantiationDefinition && + Spec->hasDefinition()) + DI->completeTemplateDefinition(*Spec); } LLVM_FALLTHROUGH; case Decl::CXXRecord: - if (DebugInfo) { + if (CGDebugInfo *DI = getModuleDebugInfo()) if (auto *ES = D->getASTContext().getExternalSource()) if (ES->hasExternalDefinitions(D) == ExternalASTSource::EK_Never) - DebugInfo->completeUnusedClass(cast(*D)); - } + DI->completeUnusedClass(cast(*D)); // Emit any static data members, they may be definitions. for (auto *I : cast(D)->decls()) if (isa(I) || isa(I)) @@ -5597,15 +5607,15 @@ void CodeGenModule::EmitTopLevelDecl(Decl *D) { case Decl::Using: // using X; [C++] if (CGDebugInfo *DI = getModuleDebugInfo()) DI->EmitUsingDecl(cast(*D)); - return; + break; case Decl::NamespaceAlias: if (CGDebugInfo *DI = getModuleDebugInfo()) DI->EmitNamespaceAlias(cast(*D)); - return; + break; case Decl::UsingDirective: // using namespace X; [C++] if (CGDebugInfo *DI = getModuleDebugInfo()) DI->EmitUsingDirective(cast(*D)); - return; + break; case Decl::CXXConstructor: getCXXABI().EmitCXXConstructors(cast(D)); break; @@ -5788,10 +5798,10 @@ void CodeGenModule::AddDeferredUnusedCoverageMapping(Decl *D) { case Decl::CXXConstructor: case Decl::CXXDestructor: { if (!cast(D)->doesThisDeclarationHaveABody()) - return; + break; SourceManager &SM = getContext().getSourceManager(); if (LimitedCoverage && SM.getMainFileID() != SM.getFileID(D->getBeginLoc())) - return; + break; auto I = DeferredEmptyCoverageMappingDecls.find(D); if (I == DeferredEmptyCoverageMappingDecls.end()) DeferredEmptyCoverageMappingDecls[D] = true; diff --git a/clang/lib/CodeGen/CodeGenPGO.cpp b/clang/lib/CodeGen/CodeGenPGO.cpp index 3c91a04d54642..e810f608ab787 100644 --- a/clang/lib/CodeGen/CodeGenPGO.cpp +++ b/clang/lib/CodeGen/CodeGenPGO.cpp @@ -52,9 +52,10 @@ void CodeGenPGO::setFuncName(llvm::Function *Fn) { enum PGOHashVersion : unsigned { PGO_HASH_V1, PGO_HASH_V2, + PGO_HASH_V3, // Keep this set to the latest hash version. - PGO_HASH_LATEST = PGO_HASH_V2 + PGO_HASH_LATEST = PGO_HASH_V3 }; namespace { @@ -122,7 +123,7 @@ class PGOHash { BinaryOperatorGE, BinaryOperatorEQ, BinaryOperatorNE, - // The preceding values are available with PGO_HASH_V2. + // The preceding values are available since PGO_HASH_V2. // Keep this last. It's for the static assert that follows. LastHashType @@ -144,7 +145,9 @@ static PGOHashVersion getPGOHashVersion(llvm::IndexedInstrProfReader *PGOReader, CodeGenModule &CGM) { if (PGOReader->getVersion() <= 4) return PGO_HASH_V1; - return PGO_HASH_V2; + if (PGOReader->getVersion() <= 5) + return PGO_HASH_V2; + return PGO_HASH_V3; } /// A RecursiveASTVisitor that fills a map of statements to PGO counters. @@ -288,7 +291,7 @@ struct MapRegionCounters : public RecursiveASTVisitor { return PGOHash::BinaryOperatorLAnd; if (BO->getOpcode() == BO_LOr) return PGOHash::BinaryOperatorLOr; - if (HashVersion == PGO_HASH_V2) { + if (HashVersion >= PGO_HASH_V2) { switch (BO->getOpcode()) { default: break; @@ -310,7 +313,7 @@ struct MapRegionCounters : public RecursiveASTVisitor { } } - if (HashVersion == PGO_HASH_V2) { + if (HashVersion >= PGO_HASH_V2) { switch (S->getStmtClass()) { default: break; @@ -747,13 +750,21 @@ uint64_t PGOHash::finalize() { return Working; // Check for remaining work in Working. - if (Working) - MD5.update(Working); + if (Working) { + // Keep the buggy behavior from v1 and v2 for backward-compatibility. This + // is buggy because it converts a uint64_t into an array of uint8_t. + if (HashVersion < PGO_HASH_V3) { + MD5.update({(uint8_t)Working}); + } else { + using namespace llvm::support; + uint64_t Swapped = endian::byte_swap(Working); + MD5.update(llvm::makeArrayRef((uint8_t *)&Swapped, sizeof(Swapped))); + } + } // Finalize the MD5 and return the hash. llvm::MD5::MD5Result Result; MD5.final(Result); - using namespace llvm::support; return Result.low(); } diff --git a/clang/lib/CodeGen/CodeGenTypes.cpp b/clang/lib/CodeGen/CodeGenTypes.cpp index 64dc24f4e54a4..8d32a72d0edc2 100644 --- a/clang/lib/CodeGen/CodeGenTypes.cpp +++ b/clang/lib/CodeGen/CodeGenTypes.cpp @@ -663,14 +663,15 @@ llvm::Type *CodeGenTypes::ConvertType(QualType T) { case Type::ExtVector: case Type::Vector: { const VectorType *VT = cast(Ty); - ResultType = llvm::VectorType::get(ConvertType(VT->getElementType()), - VT->getNumElements()); + ResultType = llvm::FixedVectorType::get(ConvertType(VT->getElementType()), + VT->getNumElements()); break; } case Type::ConstantMatrix: { const ConstantMatrixType *MT = cast(Ty); - ResultType = llvm::VectorType::get(ConvertType(MT->getElementType()), - MT->getNumRows() * MT->getNumColumns()); + ResultType = + llvm::FixedVectorType::get(ConvertType(MT->getElementType()), + MT->getNumRows() * MT->getNumColumns()); break; } case Type::FunctionNoProto: diff --git a/clang/lib/CodeGen/SwiftCallingConv.cpp b/clang/lib/CodeGen/SwiftCallingConv.cpp index 8bce93b71c0c2..3d7421ac2e16c 100644 --- a/clang/lib/CodeGen/SwiftCallingConv.cpp +++ b/clang/lib/CodeGen/SwiftCallingConv.cpp @@ -694,7 +694,7 @@ swiftcall::splitLegalVectorType(CodeGenModule &CGM, CharUnits vectorSize, // Try to split the vector type in half. if (numElts >= 4 && isPowerOf2(numElts)) { if (isLegalVectorType(CGM, vectorSize / 2, eltTy, numElts / 2)) - return {llvm::VectorType::get(eltTy, numElts / 2), 2}; + return {llvm::FixedVectorType::get(eltTy, numElts / 2), 2}; } return {eltTy, numElts}; @@ -747,7 +747,8 @@ void swiftcall::legalizeVectorType(CodeGenModule &CGM, CharUnits origVectorSize, // Add the right number of vectors of this size. auto numVecs = numElts >> logCandidateNumElts; - components.append(numVecs, llvm::VectorType::get(eltTy, candidateNumElts)); + components.append(numVecs, + llvm::FixedVectorType::get(eltTy, candidateNumElts)); numElts -= (numVecs << logCandidateNumElts); if (numElts == 0) return; @@ -757,7 +758,7 @@ void swiftcall::legalizeVectorType(CodeGenModule &CGM, CharUnits origVectorSize, // This only needs to be separately checked if it's not a power of 2. if (numElts > 2 && !isPowerOf2(numElts) && isLegalVectorType(CGM, eltSize * numElts, eltTy, numElts)) { - components.push_back(llvm::VectorType::get(eltTy, numElts)); + components.push_back(llvm::FixedVectorType::get(eltTy, numElts)); return; } diff --git a/clang/lib/CodeGen/TargetInfo.cpp b/clang/lib/CodeGen/TargetInfo.cpp index 771ed6c4a0e6a..e4210257c9b0f 100644 --- a/clang/lib/CodeGen/TargetInfo.cpp +++ b/clang/lib/CodeGen/TargetInfo.cpp @@ -1478,8 +1478,8 @@ ABIArgInfo X86_32ABIInfo::classifyReturnType(QualType RetTy, // registers and we need to make sure to pick a type the LLVM // backend will like. if (Size == 128) - return ABIArgInfo::getDirect(llvm::VectorType::get( - llvm::Type::getInt64Ty(getVMContext()), 2)); + return ABIArgInfo::getDirect(llvm::FixedVectorType::get( + llvm::Type::getInt64Ty(getVMContext()), 2)); // Always return in register if it fits in a general purpose // register, or if it is 64 bits and has a single element. @@ -3122,8 +3122,8 @@ llvm::Type *X86_64ABIInfo::GetByteVectorType(QualType Ty) const { cast(IRType)->getElementType()->isIntegerTy(128)) { // Use a vXi64 vector. uint64_t Size = getContext().getTypeSize(Ty); - return llvm::VectorType::get(llvm::Type::getInt64Ty(getVMContext()), - Size / 64); + return llvm::FixedVectorType::get(llvm::Type::getInt64Ty(getVMContext()), + Size / 64); } return IRType; @@ -3138,8 +3138,8 @@ llvm::Type *X86_64ABIInfo::GetByteVectorType(QualType Ty) const { // Return a LLVM IR vector type based on the size of 'Ty'. - return llvm::VectorType::get(llvm::Type::getDoubleTy(getVMContext()), - Size / 64); + return llvm::FixedVectorType::get(llvm::Type::getDoubleTy(getVMContext()), + Size / 64); } /// BitsContainNoUserData - Return true if the specified [start,end) bit range @@ -3273,7 +3273,8 @@ GetSSETypeAtOffset(llvm::Type *IRType, unsigned IROffset, // case. if (ContainsFloatAtOffset(IRType, IROffset, getDataLayout()) && ContainsFloatAtOffset(IRType, IROffset+4, getDataLayout())) - return llvm::VectorType::get(llvm::Type::getFloatTy(getVMContext()), 2); + return llvm::FixedVectorType::get(llvm::Type::getFloatTy(getVMContext()), + 2); return llvm::Type::getDoubleTy(getVMContext()); } @@ -4140,8 +4141,8 @@ ABIArgInfo WinX86_64ABIInfo::classify(QualType Ty, unsigned &FreeSSERegs, // Mingw64 GCC returns i128 in XMM0. Coerce to v2i64 to handle that. // Clang matches them for compatibility. - return ABIArgInfo::getDirect( - llvm::VectorType::get(llvm::Type::getInt64Ty(getVMContext()), 2)); + return ABIArgInfo::getDirect(llvm::FixedVectorType::get( + llvm::Type::getInt64Ty(getVMContext()), 2)); default: break; @@ -5478,13 +5479,13 @@ ABIArgInfo AArch64ABIInfo::classifyArgumentType(QualType Ty) const { return ABIArgInfo::getDirect(ResType); } if (Size == 64) { - llvm::Type *ResType = - llvm::VectorType::get(llvm::Type::getInt32Ty(getVMContext()), 2); + auto *ResType = + llvm::FixedVectorType::get(llvm::Type::getInt32Ty(getVMContext()), 2); return ABIArgInfo::getDirect(ResType); } if (Size == 128) { - llvm::Type *ResType = - llvm::VectorType::get(llvm::Type::getInt32Ty(getVMContext()), 4); + auto *ResType = + llvm::FixedVectorType::get(llvm::Type::getInt32Ty(getVMContext()), 4); return ABIArgInfo::getDirect(ResType); } return getNaturalAlignIndirect(Ty, /*ByVal=*/false); @@ -6209,7 +6210,7 @@ ABIArgInfo ARMABIInfo::coerceIllegalVector(QualType Ty) const { return ABIArgInfo::getDirect(ResType); } if (Size == 64 || Size == 128) { - llvm::Type *ResType = llvm::VectorType::get( + auto *ResType = llvm::FixedVectorType::get( llvm::Type::getInt32Ty(getVMContext()), Size / 32); return ABIArgInfo::getDirect(ResType); } @@ -6225,7 +6226,7 @@ ABIArgInfo ARMABIInfo::classifyHomogeneousAggregate(QualType Ty, // FP16 vectors should be converted to integer vectors if (!getTarget().hasLegalHalfType() && containsAnyFP16Vectors(Ty)) { uint64_t Size = getContext().getTypeSize(VT); - llvm::Type *NewVecTy = llvm::VectorType::get( + auto *NewVecTy = llvm::FixedVectorType::get( llvm::Type::getInt32Ty(getVMContext()), Size / 32); llvm::Type *Ty = llvm::ArrayType::get(NewVecTy, Members); return ABIArgInfo::getDirect(Ty, 0, nullptr, false); diff --git a/clang/lib/Driver/SanitizerArgs.cpp b/clang/lib/Driver/SanitizerArgs.cpp index bc186fa5a5982..6281991ebf04e 100644 --- a/clang/lib/Driver/SanitizerArgs.cpp +++ b/clang/lib/Driver/SanitizerArgs.cpp @@ -14,10 +14,10 @@ #include "clang/Driver/ToolChain.h" #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringSwitch.h" -#include "llvm/Support/FileSystem.h" #include "llvm/Support/Path.h" #include "llvm/Support/SpecialCaseList.h" #include "llvm/Support/TargetParser.h" +#include "llvm/Support/VirtualFileSystem.h" #include using namespace clang; @@ -43,11 +43,12 @@ static const SanitizerMask SupportsCoverage = SanitizerKind::KernelAddress | SanitizerKind::KernelHWAddress | SanitizerKind::MemTag | SanitizerKind::Memory | SanitizerKind::KernelMemory | SanitizerKind::Leak | - SanitizerKind::Undefined | SanitizerKind::Integer | + SanitizerKind::Undefined | SanitizerKind::Integer | SanitizerKind::Bounds | SanitizerKind::ImplicitConversion | SanitizerKind::Nullability | SanitizerKind::DataFlow | SanitizerKind::Fuzzer | SanitizerKind::FuzzerNoLink | SanitizerKind::FloatDivideByZero | - SanitizerKind::SafeStack | SanitizerKind::ShadowCallStack; + SanitizerKind::SafeStack | SanitizerKind::ShadowCallStack | + SanitizerKind::Thread; static const SanitizerMask RecoverableByDefault = SanitizerKind::Undefined | SanitizerKind::Integer | SanitizerKind::ImplicitConversion | SanitizerKind::Nullability | @@ -488,8 +489,7 @@ SanitizerArgs::SanitizerArgs(const ToolChain &TC, << lastArgumentForMask(D, Args, Kinds & NeedsLTO) << "-flto"; } - if ((Kinds & SanitizerKind::ShadowCallStack) && - TC.getTriple().getArch() == llvm::Triple::aarch64 && + if ((Kinds & SanitizerKind::ShadowCallStack) && TC.getTriple().isAArch64() && !llvm::AArch64::isX18ReservedByDefault(TC.getTriple()) && !Args.hasArg(options::OPT_ffixed_x18)) { D.Diag(diag::err_drv_argument_only_allowed_with) diff --git a/clang/lib/Driver/ToolChain.cpp b/clang/lib/Driver/ToolChain.cpp index 3da4f8f107f91..db405316653cc 100644 --- a/clang/lib/Driver/ToolChain.cpp +++ b/clang/lib/Driver/ToolChain.cpp @@ -230,9 +230,12 @@ ToolChain::getTargetAndModeFromProgramName(StringRef PN) { StringRef ToolChain::getDefaultUniversalArchName() const { // In universal driver terms, the arch name accepted by -arch isn't exactly // the same as the ones that appear in the triple. Roughly speaking, this is - // an inverse of the darwin::getArchTypeForDarwinArchName() function, but the - // only interesting special case is powerpc. + // an inverse of the darwin::getArchTypeForDarwinArchName() function. switch (Triple.getArch()) { + case llvm::Triple::aarch64: + return "arm64"; + case llvm::Triple::aarch64_32: + return "arm64_32"; case llvm::Triple::ppc: return "ppc"; case llvm::Triple::ppc64: @@ -1033,15 +1036,12 @@ SanitizerMask ToolChain::getSupportedSanitizers() const { if (getTriple().getArch() == llvm::Triple::x86 || getTriple().getArch() == llvm::Triple::x86_64 || getTriple().getArch() == llvm::Triple::arm || - getTriple().getArch() == llvm::Triple::aarch64 || getTriple().getArch() == llvm::Triple::wasm32 || - getTriple().getArch() == llvm::Triple::wasm64) + getTriple().getArch() == llvm::Triple::wasm64 || getTriple().isAArch64()) Res |= SanitizerKind::CFIICall; - if (getTriple().getArch() == llvm::Triple::x86_64 || - getTriple().getArch() == llvm::Triple::aarch64) + if (getTriple().getArch() == llvm::Triple::x86_64 || getTriple().isAArch64()) Res |= SanitizerKind::ShadowCallStack; - if (getTriple().getArch() == llvm::Triple::aarch64 || - getTriple().getArch() == llvm::Triple::aarch64_be) + if (getTriple().isAArch64()) Res |= SanitizerKind::MemTag; return Res; } diff --git a/clang/lib/Driver/ToolChains/AIX.cpp b/clang/lib/Driver/ToolChains/AIX.cpp index 6fbff61f76565..df2e30da32a8c 100644 --- a/clang/lib/Driver/ToolChains/AIX.cpp +++ b/clang/lib/Driver/ToolChains/AIX.cpp @@ -81,6 +81,7 @@ void aix::Linker::ConstructJob(Compilation &C, const JobAction &JA, const InputInfoList &Inputs, const ArgList &Args, const char *LinkingOutput) const { const AIX &ToolChain = static_cast(getToolChain()); + const Driver &D = ToolChain.getDriver(); ArgStringList CmdArgs; const bool IsArch32Bit = ToolChain.getTriple().isArch32Bit(); @@ -129,6 +130,12 @@ void aix::Linker::ConstructJob(Compilation &C, const JobAction &JA, Args.MakeArgString(ToolChain.GetFilePath(getCrt0Basename()))); } + // Collect all static constructor and destructor functions in CXX mode. This + // has to come before AddLinkerInputs as the implied option needs to precede + // any other '-bcdtors' settings or '-bnocdtors' that '-Wl' might forward. + if (D.CCCIsCXX()) + CmdArgs.push_back("-bcdtors:all:0:s"); + // Specify linker input file(s). AddLinkerInputs(ToolChain, Inputs, Args, CmdArgs, JA); diff --git a/clang/lib/Driver/ToolChains/Arch/PPC.cpp b/clang/lib/Driver/ToolChains/Arch/PPC.cpp index e5130a9485de7..144e276a6bd87 100644 --- a/clang/lib/Driver/ToolChains/Arch/PPC.cpp +++ b/clang/lib/Driver/ToolChains/Arch/PPC.cpp @@ -70,6 +70,7 @@ std::string ppc::getPPCTargetCPU(const ArgList &Args) { .Case("power7", "pwr7") .Case("power8", "pwr8") .Case("power9", "pwr9") + .Case("power10", "pwr10") .Case("future", "future") .Case("pwr3", "pwr3") .Case("pwr4", "pwr4") @@ -80,6 +81,7 @@ std::string ppc::getPPCTargetCPU(const ArgList &Args) { .Case("pwr7", "pwr7") .Case("pwr8", "pwr8") .Case("pwr9", "pwr9") + .Case("pwr10", "pwr10") .Case("powerpc", "ppc") .Case("powerpc64", "ppc64") .Case("powerpc64le", "ppc64le") @@ -91,14 +93,16 @@ std::string ppc::getPPCTargetCPU(const ArgList &Args) { const char *ppc::getPPCAsmModeForCPU(StringRef Name) { return llvm::StringSwitch(Name) - .Case("pwr7", "-mpower7") - .Case("power7", "-mpower7") - .Case("pwr8", "-mpower8") - .Case("power8", "-mpower8") - .Case("ppc64le", "-mpower8") - .Case("pwr9", "-mpower9") - .Case("power9", "-mpower9") - .Default("-many"); + .Case("pwr7", "-mpower7") + .Case("power7", "-mpower7") + .Case("pwr8", "-mpower8") + .Case("power8", "-mpower8") + .Case("ppc64le", "-mpower8") + .Case("pwr9", "-mpower9") + .Case("power9", "-mpower9") + .Case("pwr10", "-mpower10") + .Case("power10", "-mpower10") + .Default("-many"); } void ppc::getPPCTargetFeatures(const Driver &D, const llvm::Triple &Triple, diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp index 7b6088f48f911..6f43a2b05a1b7 100644 --- a/clang/lib/Driver/ToolChains/Clang.cpp +++ b/clang/lib/Driver/ToolChains/Clang.cpp @@ -2069,57 +2069,10 @@ void Clang::AddSystemZTargetArgs(const ArgList &Args, } } -static void addX86AlignBranchArgs(const Driver &D, const ArgList &Args, - ArgStringList &CmdArgs) { - if (Args.hasArg(options::OPT_mbranches_within_32B_boundaries)) { - CmdArgs.push_back("-mllvm"); - CmdArgs.push_back("-x86-branches-within-32B-boundaries"); - } - if (const Arg *A = Args.getLastArg(options::OPT_malign_branch_boundary_EQ)) { - StringRef Value = A->getValue(); - unsigned Boundary; - if (Value.getAsInteger(10, Boundary) || Boundary < 16 || - !llvm::isPowerOf2_64(Boundary)) { - D.Diag(diag::err_drv_invalid_argument_to_option) - << Value << A->getOption().getName(); - } else { - CmdArgs.push_back("-mllvm"); - CmdArgs.push_back( - Args.MakeArgString("-x86-align-branch-boundary=" + Twine(Boundary))); - } - } - if (const Arg *A = Args.getLastArg(options::OPT_malign_branch_EQ)) { - std::string AlignBranch; - for (StringRef T : A->getValues()) { - if (T != "fused" && T != "jcc" && T != "jmp" && T != "call" && - T != "ret" && T != "indirect") - D.Diag(diag::err_drv_invalid_malign_branch_EQ) - << T << "fused, jcc, jmp, call, ret, indirect"; - if (!AlignBranch.empty()) - AlignBranch += '+'; - AlignBranch += T; - } - CmdArgs.push_back("-mllvm"); - CmdArgs.push_back(Args.MakeArgString("-x86-align-branch=" + AlignBranch)); - } - if (const Arg *A = Args.getLastArg(options::OPT_mpad_max_prefix_size_EQ)) { - StringRef Value = A->getValue(); - unsigned PrefixSize; - if (Value.getAsInteger(10, PrefixSize)) { - D.Diag(diag::err_drv_invalid_argument_to_option) - << Value << A->getOption().getName(); - } else { - CmdArgs.push_back("-mllvm"); - CmdArgs.push_back( - Args.MakeArgString("-x86-pad-max-prefix-size=" + Twine(PrefixSize))); - } - } -} - void Clang::AddX86TargetArgs(const ArgList &Args, ArgStringList &CmdArgs) const { const Driver &D = getToolChain().getDriver(); - addX86AlignBranchArgs(D, Args, CmdArgs); + addX86AlignBranchArgs(D, Args, CmdArgs, /*IsLTO=*/false); if (!Args.hasFlag(options::OPT_mred_zone, options::OPT_mno_red_zone, true) || Args.hasArg(options::OPT_mkernel) || @@ -4569,10 +4522,12 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, CmdArgs.push_back(A->getValue()); } - if (Args.hasFlag(options::OPT_fsemantic_interposition, - options::OPT_fno_semantic_interposition, false) && - RelocationModel != llvm::Reloc::Static && !IsPIE) - CmdArgs.push_back("-fsemantic-interposition"); + // The default is -fno-semantic-interposition. We render it just because we + // require explicit -fno-semantic-interposition to infer dso_local. + if (Arg *A = Args.getLastArg(options::OPT_fsemantic_interposition, + options::OPT_fno_semantic_interposition)) + if (RelocationModel != llvm::Reloc::Static && !IsPIE) + A->render(Args, CmdArgs); CmdArgs.push_back("-mthread-model"); if (Arg *A = Args.getLastArg(options::OPT_mthread_model)) { @@ -6920,7 +6875,8 @@ void ClangAs::AddMIPSTargetArgs(const ArgList &Args, void ClangAs::AddX86TargetArgs(const ArgList &Args, ArgStringList &CmdArgs) const { - addX86AlignBranchArgs(getToolChain().getDriver(), Args, CmdArgs); + addX86AlignBranchArgs(getToolChain().getDriver(), Args, CmdArgs, + /*IsLTO=*/false); if (Arg *A = Args.getLastArg(options::OPT_masm_EQ)) { StringRef Value = A->getValue(); diff --git a/clang/lib/Driver/ToolChains/CommonArgs.cpp b/clang/lib/Driver/ToolChains/CommonArgs.cpp index b4c50fc2c72c2..e18557df72c6b 100644 --- a/clang/lib/Driver/ToolChains/CommonArgs.cpp +++ b/clang/lib/Driver/ToolChains/CommonArgs.cpp @@ -366,6 +366,7 @@ void tools::addLTOOptions(const ToolChain &ToolChain, const ArgList &Args, ArgStringList &CmdArgs, const InputInfo &Output, const InputInfo &Input, bool IsThinLTO) { const char *Linker = Args.MakeArgString(ToolChain.GetLinkerPath()); + const Driver &D = ToolChain.getDriver(); if (llvm::sys::path::filename(Linker) != "ld.lld" && llvm::sys::path::stem(Linker) != "ld.lld") { // Tell the linker to load the plugin. This has to come before @@ -382,10 +383,9 @@ void tools::addLTOOptions(const ToolChain &ToolChain, const ArgList &Args, #endif SmallString<1024> Plugin; - llvm::sys::path::native(Twine(ToolChain.getDriver().Dir) + - "/../lib" CLANG_LIBDIR_SUFFIX "/LLVMgold" + - Suffix, - Plugin); + llvm::sys::path::native( + Twine(D.Dir) + "/../lib" CLANG_LIBDIR_SUFFIX "/LLVMgold" + Suffix, + Plugin); CmdArgs.push_back(Args.MakeArgString(Plugin)); } @@ -425,7 +425,7 @@ void tools::addLTOOptions(const ToolChain &ToolChain, const ArgList &Args, if (IsThinLTO) CmdArgs.push_back("-plugin-opt=thinlto"); - StringRef Parallelism = getLTOParallelism(Args, ToolChain.getDriver()); + StringRef Parallelism = getLTOParallelism(Args, D); if (!Parallelism.empty()) CmdArgs.push_back( Args.MakeArgString("-plugin-opt=jobs=" + Twine(Parallelism))); @@ -457,7 +457,7 @@ void tools::addLTOOptions(const ToolChain &ToolChain, const ArgList &Args, if (Arg *A = getLastProfileSampleUseArg(Args)) { StringRef FName = A->getValue(); if (!llvm::sys::fs::exists(FName)) - ToolChain.getDriver().Diag(diag::err_drv_no_such_file) << FName; + D.Diag(diag::err_drv_no_such_file) << FName; else CmdArgs.push_back( Args.MakeArgString(Twine("-plugin-opt=sample-profile=") + FName)); @@ -500,11 +500,12 @@ void tools::addLTOOptions(const ToolChain &ToolChain, const ArgList &Args, } // Setup statistics file output. - SmallString<128> StatsFile = - getStatsFileName(Args, Output, Input, ToolChain.getDriver()); + SmallString<128> StatsFile = getStatsFileName(Args, Output, Input, D); if (!StatsFile.empty()) CmdArgs.push_back( Args.MakeArgString(Twine("-plugin-opt=stats-file=") + StatsFile)); + + addX86AlignBranchArgs(D, Args, CmdArgs, /*IsLTO=*/true); } void tools::addArchSpecificRPath(const ToolChain &TC, const ArgList &Args, @@ -1242,7 +1243,14 @@ static void AddUnwindLibrary(const ToolChain &TC, const Driver &D, case ToolChain::UNW_CompilerRT: if (LGT == LibGccType::StaticLibGcc) CmdArgs.push_back("-l:libunwind.a"); - else + else if (TC.getTriple().isOSCygMing()) { + if (LGT == LibGccType::SharedLibGcc) + CmdArgs.push_back("-l:libunwind.dll.a"); + else + // Let the linker choose between libunwind.dll.a and libunwind.a + // depending on what's available, and depending on the -static flag + CmdArgs.push_back("-lunwind"); + } else CmdArgs.push_back("-l:libunwind.so"); break; } @@ -1431,3 +1439,53 @@ void tools::addMultilibFlag(bool Enabled, const char *const Flag, Multilib::flags_list &Flags) { Flags.push_back(std::string(Enabled ? "+" : "-") + Flag); } + +void tools::addX86AlignBranchArgs(const Driver &D, const ArgList &Args, + ArgStringList &CmdArgs, bool IsLTO) { + auto addArg = [&, IsLTO](const Twine &Arg) { + if (IsLTO) { + CmdArgs.push_back(Args.MakeArgString("-plugin-opt=" + Arg)); + } else { + CmdArgs.push_back("-mllvm"); + CmdArgs.push_back(Args.MakeArgString(Arg)); + } + }; + + if (Args.hasArg(options::OPT_mbranches_within_32B_boundaries)) { + addArg(Twine("-x86-branches-within-32B-boundaries")); + } + if (const Arg *A = Args.getLastArg(options::OPT_malign_branch_boundary_EQ)) { + StringRef Value = A->getValue(); + unsigned Boundary; + if (Value.getAsInteger(10, Boundary) || Boundary < 16 || + !llvm::isPowerOf2_64(Boundary)) { + D.Diag(diag::err_drv_invalid_argument_to_option) + << Value << A->getOption().getName(); + } else { + addArg("-x86-align-branch-boundary=" + Twine(Boundary)); + } + } + if (const Arg *A = Args.getLastArg(options::OPT_malign_branch_EQ)) { + std::string AlignBranch; + for (StringRef T : A->getValues()) { + if (T != "fused" && T != "jcc" && T != "jmp" && T != "call" && + T != "ret" && T != "indirect") + D.Diag(diag::err_drv_invalid_malign_branch_EQ) + << T << "fused, jcc, jmp, call, ret, indirect"; + if (!AlignBranch.empty()) + AlignBranch += '+'; + AlignBranch += T; + } + addArg("-x86-align-branch=" + Twine(AlignBranch)); + } + if (const Arg *A = Args.getLastArg(options::OPT_mpad_max_prefix_size_EQ)) { + StringRef Value = A->getValue(); + unsigned PrefixSize; + if (Value.getAsInteger(10, PrefixSize)) { + D.Diag(diag::err_drv_invalid_argument_to_option) + << Value << A->getOption().getName(); + } else { + addArg("-x86-pad-max-prefix-size=" + Twine(PrefixSize)); + } + } +} diff --git a/clang/lib/Driver/ToolChains/CommonArgs.h b/clang/lib/Driver/ToolChains/CommonArgs.h index c94b2b828c9b8..58bc92c9b7569 100644 --- a/clang/lib/Driver/ToolChains/CommonArgs.h +++ b/clang/lib/Driver/ToolChains/CommonArgs.h @@ -127,6 +127,8 @@ SmallString<128> getStatsFileName(const llvm::opt::ArgList &Args, void addMultilibFlag(bool Enabled, const char *const Flag, Multilib::flags_list &Flags); +void addX86AlignBranchArgs(const Driver &D, const llvm::opt::ArgList &Args, + llvm::opt::ArgStringList &CmdArgs, bool IsLTO); } // end namespace tools } // end namespace driver } // end namespace clang diff --git a/clang/lib/Driver/ToolChains/Gnu.cpp b/clang/lib/Driver/ToolChains/Gnu.cpp index e981b1d80eac5..bb861a0477dec 100644 --- a/clang/lib/Driver/ToolChains/Gnu.cpp +++ b/clang/lib/Driver/ToolChains/Gnu.cpp @@ -486,10 +486,9 @@ void tools::gnutools::Linker::ConstructJob(Compilation &C, const JobAction &JA, CmdArgs.push_back("-export-dynamic"); if (!Args.hasArg(options::OPT_shared) && !IsStaticPIE) { - const std::string Loader = - D.DyldPrefix + ToolChain.getDynamicLinker(Args); CmdArgs.push_back("-dynamic-linker"); - CmdArgs.push_back(Args.MakeArgString(Loader)); + CmdArgs.push_back(Args.MakeArgString(Twine(D.DyldPrefix) + + ToolChain.getDynamicLinker(Args))); } } diff --git a/clang/lib/Driver/ToolChains/Linux.cpp b/clang/lib/Driver/ToolChains/Linux.cpp index 64efedece6ea9..1c67fffa4fdab 100644 --- a/clang/lib/Driver/ToolChains/Linux.cpp +++ b/clang/lib/Driver/ToolChains/Linux.cpp @@ -879,13 +879,9 @@ SanitizerMask Linux::getSupportedSanitizers() const { void Linux::addProfileRTLibs(const llvm::opt::ArgList &Args, llvm::opt::ArgStringList &CmdArgs) const { - bool Profile = needsProfileRT(Args); - if (!Profile && !needsGCovInstrumentation(Args)) - return; - // Add linker option -u__llvm_profile_runtime to cause runtime // initialization module to be linked in. - if (Profile) + if (needsProfileRT(Args)) CmdArgs.push_back(Args.MakeArgString( Twine("-u", llvm::getInstrProfRuntimeHookVarName()))); ToolChain::addProfileRTLibs(Args, CmdArgs); diff --git a/clang/lib/Driver/XRayArgs.cpp b/clang/lib/Driver/XRayArgs.cpp index 54c15685d3898..f233267b49846 100644 --- a/clang/lib/Driver/XRayArgs.cpp +++ b/clang/lib/Driver/XRayArgs.cpp @@ -13,10 +13,10 @@ #include "clang/Driver/ToolChain.h" #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringSwitch.h" -#include "llvm/Support/FileSystem.h" #include "llvm/Support/Path.h" #include "llvm/Support/ScopedPrinter.h" #include "llvm/Support/SpecialCaseList.h" +#include "llvm/Support/VirtualFileSystem.h" using namespace clang; using namespace clang::driver; diff --git a/clang/lib/Format/NamespaceEndCommentsFixer.cpp b/clang/lib/Format/NamespaceEndCommentsFixer.cpp index 92707150fcdba..97de45bd19659 100644 --- a/clang/lib/Format/NamespaceEndCommentsFixer.cpp +++ b/clang/lib/Format/NamespaceEndCommentsFixer.cpp @@ -205,6 +205,23 @@ std::pair NamespaceEndCommentsFixer::analyze( const SourceManager &SourceMgr = Env.getSourceManager(); AffectedRangeMgr.computeAffectedLines(AnnotatedLines); tooling::Replacements Fixes; + + // Spin through the lines and ensure we have balanced braces. + int Braces = 0; + for (size_t I = 0, E = AnnotatedLines.size(); I != E; ++I) { + FormatToken *Tok = AnnotatedLines[I]->First; + while (Tok) { + Braces += Tok->is(tok::l_brace) ? 1 : Tok->is(tok::r_brace) ? -1 : 0; + Tok = Tok->Next; + } + } + // Don't attempt to comment unbalanced braces or this can + // lead to comments being placed on the closing brace which isn't + // the matching brace of the namespace. (occurs during incomplete editing). + if (Braces != 0) { + return {Fixes, 0}; + } + std::string AllNamespaceNames = ""; size_t StartLineIndex = SIZE_MAX; StringRef NamespaceTokenText; diff --git a/clang/lib/Format/UnwrappedLineParser.cpp b/clang/lib/Format/UnwrappedLineParser.cpp index 03b6e0c9ef744..b8da2c23b55ac 100644 --- a/clang/lib/Format/UnwrappedLineParser.cpp +++ b/clang/lib/Format/UnwrappedLineParser.cpp @@ -1962,7 +1962,7 @@ void UnwrappedLineParser::parseIfThenElse() { if (FormatTok->Tok.is(tok::l_paren)) parseParens(); // handle [[likely]] / [[unlikely]] - if (FormatTok->is(tok::l_square)) + if (FormatTok->is(tok::l_square) && tryToParseSimpleAttribute()) parseSquare(); bool NeedsUnwrappedLine = false; if (FormatTok->Tok.is(tok::l_brace)) { @@ -1981,7 +1981,7 @@ void UnwrappedLineParser::parseIfThenElse() { if (FormatTok->Tok.is(tok::kw_else)) { nextToken(); // handle [[likely]] / [[unlikely]] - if (FormatTok->is(tok::l_square)) + if (FormatTok->Tok.is(tok::l_square) && tryToParseSimpleAttribute()) parseSquare(); if (FormatTok->Tok.is(tok::l_brace)) { CompoundStatementIndenter Indenter(this, Style, Line->Level); @@ -2343,6 +2343,51 @@ bool UnwrappedLineParser::parseEnum() { // "} n, m;" will end up in one unwrapped line. } +namespace { +// A class used to set and restore the Token position when peeking +// ahead in the token source. +class ScopedTokenPosition { + unsigned StoredPosition; + FormatTokenSource *Tokens; + +public: + ScopedTokenPosition(FormatTokenSource *Tokens) : Tokens(Tokens) { + assert(Tokens && "Tokens expected to not be null"); + StoredPosition = Tokens->getPosition(); + } + + ~ScopedTokenPosition() { Tokens->setPosition(StoredPosition); } +}; +} // namespace + +// Look to see if we have [[ by looking ahead, if +// its not then rewind to the original position. +bool UnwrappedLineParser::tryToParseSimpleAttribute() { + ScopedTokenPosition AutoPosition(Tokens); + FormatToken *Tok = Tokens->getNextToken(); + // We already read the first [ check for the second. + if (Tok && !Tok->is(tok::l_square)) { + return false; + } + // Double check that the attribute is just something + // fairly simple. + while (Tok) { + if (Tok->is(tok::r_square)) { + break; + } + Tok = Tokens->getNextToken(); + } + Tok = Tokens->getNextToken(); + if (Tok && !Tok->is(tok::r_square)) { + return false; + } + Tok = Tokens->getNextToken(); + if (Tok && Tok->is(tok::semi)) { + return false; + } + return true; +} + void UnwrappedLineParser::parseJavaEnumBody() { // Determine whether the enum is simple, i.e. does not have a semicolon or // constants with class bodies. Simple enums can be formatted like braced diff --git a/clang/lib/Format/UnwrappedLineParser.h b/clang/lib/Format/UnwrappedLineParser.h index 8d4118ab6dc7d..8b3aa4c84edba 100644 --- a/clang/lib/Format/UnwrappedLineParser.h +++ b/clang/lib/Format/UnwrappedLineParser.h @@ -134,6 +134,7 @@ class UnwrappedLineParser { bool tryToParseLambdaIntroducer(); bool tryToParsePropertyAccessor(); void tryToParseJSFunction(); + bool tryToParseSimpleAttribute(); void addUnwrappedLine(); bool eof() const; // LevelDifference is the difference of levels after and before the current diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp index f178c5a237978..82eb1dd88d685 100644 --- a/clang/lib/Frontend/CompilerInvocation.cpp +++ b/clang/lib/Frontend/CompilerInvocation.cpp @@ -3066,6 +3066,9 @@ static void ParseLangArgs(LangOptions &Opts, ArgList &Args, InputKind IK, } Opts.SemanticInterposition = Args.hasArg(OPT_fsemantic_interposition); + // An explicit -fno-semantic-interposition infers dso_local. + Opts.ExplicitNoSemanticInterposition = + Args.hasArg(OPT_fno_semantic_interposition); // -mrtd option if (Arg *A = Args.getLastArg(OPT_mrtd)) { @@ -3123,7 +3126,8 @@ static void ParseLangArgs(LangOptions &Opts, ArgList &Args, InputKind IK, // Set the flag to prevent the implementation from emitting device exception // handling code for those requiring so. - if ((Opts.OpenMPIsDevice && T.isNVPTX()) || Opts.OpenCLCPlusPlus) { + if ((Opts.OpenMPIsDevice && (T.isNVPTX() || T.isAMDGCN())) || + Opts.OpenCLCPlusPlus) { Opts.Exceptions = 0; Opts.CXXExceptions = 0; } @@ -3157,6 +3161,7 @@ static void ParseLangArgs(LangOptions &Opts, ArgList &Args, InputKind IK, TT.getArch() == llvm::Triple::ppc64le || TT.getArch() == llvm::Triple::nvptx || TT.getArch() == llvm::Triple::nvptx64 || + TT.getArch() == llvm::Triple::amdgcn || TT.getArch() == llvm::Triple::x86 || TT.getArch() == llvm::Triple::x86_64)) Diags.Report(diag::err_drv_invalid_omp_target) << A->getValue(i); @@ -3174,13 +3179,13 @@ static void ParseLangArgs(LangOptions &Opts, ArgList &Args, InputKind IK, << Opts.OMPHostIRFile; } - // Set CUDA mode for OpenMP target NVPTX if specified in options - Opts.OpenMPCUDAMode = Opts.OpenMPIsDevice && T.isNVPTX() && + // Set CUDA mode for OpenMP target NVPTX/AMDGCN if specified in options + Opts.OpenMPCUDAMode = Opts.OpenMPIsDevice && (T.isNVPTX() || T.isAMDGCN()) && Args.hasArg(options::OPT_fopenmp_cuda_mode); - // Set CUDA mode for OpenMP target NVPTX if specified in options + // Set CUDA mode for OpenMP target NVPTX/AMDGCN if specified in options Opts.OpenMPCUDAForceFullRuntime = - Opts.OpenMPIsDevice && T.isNVPTX() && + Opts.OpenMPIsDevice && (T.isNVPTX() || T.isAMDGCN()) && Args.hasArg(options::OPT_fopenmp_cuda_force_full_runtime); // Record whether the __DEPRECATED define was requested. diff --git a/clang/lib/Index/IndexDecl.cpp b/clang/lib/Index/IndexDecl.cpp index 68160bc59eb6a..2ba323e635753 100644 --- a/clang/lib/Index/IndexDecl.cpp +++ b/clang/lib/Index/IndexDecl.cpp @@ -765,6 +765,9 @@ bool IndexingContext::indexTopLevelDecl(const Decl *D) { if (isa(D)) return true; // Wait for the objc container. + if (IndexOpts.ShouldTraverseDecl && !IndexOpts.ShouldTraverseDecl(D)) + return true; // skip + return indexDecl(D); } diff --git a/clang/lib/Index/IndexingAction.cpp b/clang/lib/Index/IndexingAction.cpp index 4f402135672c3..e698c07133a9c 100644 --- a/clang/lib/Index/IndexingAction.cpp +++ b/clang/lib/Index/IndexingAction.cpp @@ -131,6 +131,21 @@ std::unique_ptr index::createIndexingASTConsumer( ShouldSkipFunctionBody); } +std::unique_ptr clang::index::createIndexingASTConsumer( + std::shared_ptr DataConsumer, + const IndexingOptions &Opts, std::shared_ptr PP) { + std::function ShouldSkipFunctionBody = [](const Decl *) { + return false; + }; + if (Opts.ShouldTraverseDecl) + ShouldSkipFunctionBody = + [ShouldTraverseDecl(Opts.ShouldTraverseDecl)](const Decl *D) { + return !ShouldTraverseDecl(D); + }; + return createIndexingASTConsumer(std::move(DataConsumer), Opts, std::move(PP), + std::move(ShouldSkipFunctionBody)); +} + std::unique_ptr index::createIndexingAction(std::shared_ptr DataConsumer, const IndexingOptions &Opts) { diff --git a/clang/lib/Lex/LiteralSupport.cpp b/clang/lib/Lex/LiteralSupport.cpp index 2b1add4d9b987..f44614b4bec46 100644 --- a/clang/lib/Lex/LiteralSupport.cpp +++ b/clang/lib/Lex/LiteralSupport.cpp @@ -583,6 +583,7 @@ NumericLiteralParser::NumericLiteralParser(StringRef TokSpelling, // Parse the suffix. At this point we can classify whether we have an FP or // integer constant. + bool isFixedPointConstant = isFixedPointLiteral(); bool isFPConstant = isFloatingLiteral(); // Loop over all of the characters of the suffix. If we see something bad, @@ -737,7 +738,8 @@ NumericLiteralParser::NumericLiteralParser(StringRef TokSpelling, // Report an error if there are any. PP.Diag(PP.AdvanceToTokenCharacter(TokLoc, SuffixBegin - ThisTokBegin), diag::err_invalid_suffix_constant) - << StringRef(SuffixBegin, ThisTokEnd - SuffixBegin) << isFPConstant; + << StringRef(SuffixBegin, ThisTokEnd - SuffixBegin) + << (isFixedPointConstant ? 2 : isFPConstant); hadError = true; } } diff --git a/clang/lib/Parse/ParseOpenMP.cpp b/clang/lib/Parse/ParseOpenMP.cpp index bd40e6b991a5d..5161c7d06cdab 100644 --- a/clang/lib/Parse/ParseOpenMP.cpp +++ b/clang/lib/Parse/ParseOpenMP.cpp @@ -2497,7 +2497,7 @@ OMPClause *Parser::ParseOpenMPUsesAllocatorClause(OpenMPDirectiveKind DKind) { /// in_reduction-clause | allocator-clause | allocate-clause | /// acq_rel-clause | acquire-clause | release-clause | relaxed-clause | /// depobj-clause | destroy-clause | detach-clause | inclusive-clause | -/// exclusive-clause | uses_allocators-clause +/// exclusive-clause | uses_allocators-clause | use_device_addr-clause /// OMPClause *Parser::ParseOpenMPClause(OpenMPDirectiveKind DKind, OpenMPClauseKind CKind, bool FirstClause) { @@ -2663,6 +2663,7 @@ OMPClause *Parser::ParseOpenMPClause(OpenMPDirectiveKind DKind, case OMPC_to: case OMPC_from: case OMPC_use_device_ptr: + case OMPC_use_device_addr: case OMPC_is_device_ptr: case OMPC_allocate: case OMPC_nontemporal: @@ -3581,6 +3582,8 @@ bool Parser::ParseOpenMPVarList(OpenMPDirectiveKind DKind, /// 'from' '(' [ mapper '(' mapper-identifier ')' ':' ] list ')' /// use_device_ptr-clause: /// 'use_device_ptr' '(' list ')' +/// use_device_addr-clause: +/// 'use_device_addr' '(' list ')' /// is_device_ptr-clause: /// 'is_device_ptr' '(' list ')' /// allocate-clause: diff --git a/clang/lib/Sema/DeclSpec.cpp b/clang/lib/Sema/DeclSpec.cpp index 276e35a3497e6..834e2533342d4 100644 --- a/clang/lib/Sema/DeclSpec.cpp +++ b/clang/lib/Sema/DeclSpec.cpp @@ -1269,7 +1269,8 @@ void DeclSpec::Finish(Sema &S, const PrintingPolicy &Policy) { // Note that this intentionally doesn't include _Complex _Bool. if (!S.getLangOpts().CPlusPlus) S.Diag(TSTLoc, diag::ext_integer_complex); - } else if (TypeSpecType != TST_float && TypeSpecType != TST_double) { + } else if (TypeSpecType != TST_float && TypeSpecType != TST_double && + TypeSpecType != TST_float128) { S.Diag(TSCLoc, diag::err_invalid_complex_spec) << getSpecifierName((TST)TypeSpecType, Policy); TypeSpecComplex = TSC_unspecified; diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp index 28a304e6d0ef2..6b6b5a239dbf0 100644 --- a/clang/lib/Sema/Sema.cpp +++ b/clang/lib/Sema/Sema.cpp @@ -1731,6 +1731,56 @@ Sema::DeviceDiagBuilder Sema::targetDiag(SourceLocation Loc, unsigned DiagID) { getCurFunctionDecl(), *this); } +void Sema::checkDeviceDecl(const ValueDecl *D, SourceLocation Loc) { + if (isUnevaluatedContext()) + return; + + Decl *C = cast(getCurLexicalContext()); + + // Memcpy operations for structs containing a member with unsupported type + // are ok, though. + if (const auto *MD = dyn_cast(C)) { + if ((MD->isCopyAssignmentOperator() || MD->isMoveAssignmentOperator()) && + MD->isTrivial()) + return; + + if (const auto *Ctor = dyn_cast(MD)) + if (Ctor->isCopyOrMoveConstructor() && Ctor->isTrivial()) + return; + } + + auto CheckType = [&](QualType Ty) { + if (Ty->isDependentType()) + return; + + auto IsSYCLDeviceCuda = getLangOpts().SYCLIsDevice && + Context.getTargetInfo().getTriple().isNVPTX(); + if ((Ty->isFloat16Type() && !Context.getTargetInfo().hasFloat16Type() && + // Disable check for SYCL CUDA BE until FP16 support is properly + // reported there (issue#1799) + !IsSYCLDeviceCuda) || + ((Ty->isFloat128Type() || + (Ty->isRealFloatingType() && Context.getTypeSize(Ty) == 128)) && + !Context.getTargetInfo().hasFloat128Type()) || + (Ty->isIntegerType() && Context.getTypeSize(Ty) == 128 && + !Context.getTargetInfo().hasInt128Type())) { + targetDiag(Loc, diag::err_device_unsupported_type) + << D << static_cast(Context.getTypeSize(Ty)) << Ty + << Context.getTargetInfo().getTriple().str(); + targetDiag(D->getLocation(), diag::note_defined_here) << D; + } + }; + + QualType Ty = D->getType(); + CheckType(Ty); + + if (const auto *FPTy = dyn_cast(Ty)) { + for (const auto &ParamTy : FPTy->param_types()) + CheckType(ParamTy); + CheckType(FPTy->getReturnType()); + } +} + /// Looks through the macro-expansion chain for the given /// location, looking for a macro expansion with the given name. /// If one is found, returns true and sets the location to that diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index d76b234aba18b..b3944d3560754 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -6884,18 +6884,34 @@ NamedDecl *Sema::ActOnVariableDeclarator( if (SC == SC_Static && CurContext->isRecord()) { if (const CXXRecordDecl *RD = dyn_cast(DC)) { - // C++ [class.static.data]p2: - // A static data member shall not be a direct member of an unnamed - // or local class - // FIXME: or of a (possibly indirectly) nested class thereof. - if (RD->isLocalClass()) { + // Walk up the enclosing DeclContexts to check for any that are + // incompatible with static data members. + const DeclContext *FunctionOrMethod = nullptr; + const CXXRecordDecl *AnonStruct = nullptr; + for (DeclContext *Ctxt = DC; Ctxt; Ctxt = Ctxt->getParent()) { + if (Ctxt->isFunctionOrMethod()) { + FunctionOrMethod = Ctxt; + break; + } + const CXXRecordDecl *ParentDecl = dyn_cast(Ctxt); + if (ParentDecl && !ParentDecl->getDeclName()) { + AnonStruct = ParentDecl; + break; + } + } + if (FunctionOrMethod) { + // C++ [class.static.data]p5: A local class shall not have static data + // members. Diag(D.getIdentifierLoc(), diag::err_static_data_member_not_allowed_in_local_class) << Name << RD->getDeclName() << RD->getTagKind(); - } else if (!RD->getDeclName()) { + } else if (AnonStruct) { + // C++ [class.static.data]p4: Unnamed classes and classes contained + // directly or indirectly within unnamed classes shall not contain + // static data members. Diag(D.getIdentifierLoc(), diag::err_static_data_member_not_allowed_in_anon_struct) - << Name << RD->getTagKind(); + << Name << AnonStruct->getTagKind(); Invalid = true; } else if (RD->isUnion()) { // C++98 [class.union]p1: If a union contains a static data member, @@ -18119,8 +18135,8 @@ Decl *Sema::getObjCDeclContext() const { Sema::FunctionEmissionStatus Sema::getEmissionStatus(FunctionDecl *FD, bool Final) { - // Due to SYCL functions are template we check if they have appropriate - // attribute prior to checking if it is a template + // SYCL functions can be template, so we check if they have appropriate + // attribute prior to checking if it is a template. if (LangOpts.SYCLIsDevice && (FD->hasAttr() || FD->hasAttr())) return FunctionEmissionStatus::Emitted; diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp index 61a51918befd4..08dfc66729f96 100644 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -4097,7 +4097,8 @@ bool Sema::checkMSInheritanceAttrOnDefinition( /// parseModeAttrArg - Parses attribute mode string and returns parsed type /// attribute. static void parseModeAttrArg(Sema &S, StringRef Str, unsigned &DestWidth, - bool &IntegerMode, bool &ComplexMode) { + bool &IntegerMode, bool &ComplexMode, + bool &ExplicitIEEE) { IntegerMode = true; ComplexMode = false; switch (Str.size()) { @@ -4118,7 +4119,12 @@ static void parseModeAttrArg(Sema &S, StringRef Str, unsigned &DestWidth, case 'X': DestWidth = 96; break; + case 'K': // KFmode - IEEE quad precision (__float128) + ExplicitIEEE = true; + DestWidth = Str[1] == 'I' ? 0 : 128; + break; case 'T': + ExplicitIEEE = false; DestWidth = 128; break; } @@ -4179,6 +4185,7 @@ void Sema::AddModeAttr(Decl *D, const AttributeCommonInfo &CI, unsigned DestWidth = 0; bool IntegerMode = true; bool ComplexMode = false; + bool ExplicitIEEE = false; llvm::APInt VectorSize(64, 0); if (Str.size() >= 4 && Str[0] == 'V') { // Minimal length of vector mode is 4: 'V' + NUMBER(>=1) + TYPE(>=2). @@ -4191,7 +4198,7 @@ void Sema::AddModeAttr(Decl *D, const AttributeCommonInfo &CI, !Str.substr(1, VectorStringLength).getAsInteger(10, VectorSize) && VectorSize.isPowerOf2()) { parseModeAttrArg(*this, Str.substr(VectorStringLength + 1), DestWidth, - IntegerMode, ComplexMode); + IntegerMode, ComplexMode, ExplicitIEEE); // Avoid duplicate warning from template instantiation. if (!InInstantiation) Diag(AttrLoc, diag::warn_vector_mode_deprecated); @@ -4201,7 +4208,8 @@ void Sema::AddModeAttr(Decl *D, const AttributeCommonInfo &CI, } if (!VectorSize) - parseModeAttrArg(*this, Str, DestWidth, IntegerMode, ComplexMode); + parseModeAttrArg(*this, Str, DestWidth, IntegerMode, ComplexMode, + ExplicitIEEE); // FIXME: Sync this with InitializePredefinedMacros; we need to match int8_t // and friends, at least with glibc. @@ -4267,7 +4275,7 @@ void Sema::AddModeAttr(Decl *D, const AttributeCommonInfo &CI, NewElemTy = Context.getIntTypeForBitwidth(DestWidth, OldElemTy->isSignedIntegerType()); else - NewElemTy = Context.getRealTypeForBitwidth(DestWidth); + NewElemTy = Context.getRealTypeForBitwidth(DestWidth, ExplicitIEEE); if (NewElemTy.isNull()) { Diag(AttrLoc, diag::err_machine_mode) << 1 /*Unsupported*/ << Name; diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index f954f1043d8e2..4f61ebdb828e0 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -370,6 +370,10 @@ bool Sema::DiagnoseUseOfDecl(NamedDecl *D, ArrayRef Locs, diagnoseUseOfInternalDeclInInlineFunction(*this, D, Loc); + if (LangOpts.SYCLIsDevice || (LangOpts.OpenMP && LangOpts.OpenMPIsDevice)) + if (const auto *VD = dyn_cast(D)) + checkDeviceDecl(VD, Loc); + if (isa(D) && isa(D->getDeclContext()) && !isUnevaluatedContext()) { // C++ [expr.prim.req.nested] p3 @@ -6078,7 +6082,8 @@ static void checkDirectCallValidity(Sema &S, const Expr *Fn, if (Callee->getMinRequiredArguments() > ArgExprs.size()) return; - if (const EnableIfAttr *Attr = S.CheckEnableIf(Callee, ArgExprs, true)) { + if (const EnableIfAttr *Attr = + S.CheckEnableIf(Callee, Fn->getBeginLoc(), ArgExprs, true)) { S.Diag(Fn->getBeginLoc(), isa(Callee) ? diag::err_ovl_no_viable_member_function_in_call @@ -10253,6 +10258,11 @@ QualType Sema::CheckAdditionOperands(ExprResult &LHS, ExprResult &RHS, return compType; } + if (LHS.get()->getType()->isConstantMatrixType() || + RHS.get()->getType()->isConstantMatrixType()) { + return CheckMatrixElementwiseOperands(LHS, RHS, Loc, CompLHSTy); + } + QualType compType = UsualArithmeticConversions( LHS, RHS, Loc, CompLHSTy ? ACK_CompAssign : ACK_Arithmetic); if (LHS.isInvalid() || RHS.isInvalid()) @@ -10348,6 +10358,11 @@ QualType Sema::CheckSubtractionOperands(ExprResult &LHS, ExprResult &RHS, return compType; } + if (LHS.get()->getType()->isConstantMatrixType() || + RHS.get()->getType()->isConstantMatrixType()) { + return CheckMatrixElementwiseOperands(LHS, RHS, Loc, CompLHSTy); + } + QualType compType = UsualArithmeticConversions( LHS, RHS, Loc, CompLHSTy ? ACK_CompAssign : ACK_Arithmetic); if (LHS.isInvalid() || RHS.isInvalid()) @@ -11943,6 +11958,63 @@ QualType Sema::CheckVectorLogicalOperands(ExprResult &LHS, ExprResult &RHS, return GetSignedVectorType(LHS.get()->getType()); } +static bool tryConvertScalarToMatrixElementTy(Sema &S, QualType ElementType, + ExprResult *Scalar) { + InitializedEntity Entity = + InitializedEntity::InitializeTemporary(ElementType); + InitializationKind Kind = InitializationKind::CreateCopy( + Scalar->get()->getBeginLoc(), SourceLocation()); + Expr *Arg = Scalar->get(); + InitializationSequence InitSeq(S, Entity, Kind, Arg); + *Scalar = InitSeq.Perform(S, Entity, Kind, Arg); + return !Scalar->isInvalid(); +} + +QualType Sema::CheckMatrixElementwiseOperands(ExprResult &LHS, ExprResult &RHS, + SourceLocation Loc, + bool IsCompAssign) { + if (!IsCompAssign) { + LHS = DefaultFunctionArrayLvalueConversion(LHS.get()); + if (LHS.isInvalid()) + return QualType(); + } + RHS = DefaultFunctionArrayLvalueConversion(RHS.get()); + if (RHS.isInvalid()) + return QualType(); + + // For conversion purposes, we ignore any qualifiers. + // For example, "const float" and "float" are equivalent. + QualType LHSType = LHS.get()->getType().getUnqualifiedType(); + QualType RHSType = RHS.get()->getType().getUnqualifiedType(); + + const MatrixType *LHSMatType = LHSType->getAs(); + const MatrixType *RHSMatType = RHSType->getAs(); + assert((LHSMatType || RHSMatType) && "At least one operand must be a matrix"); + + if (Context.hasSameType(LHSType, RHSType)) + return LHSType; + + // Type conversion may change LHS/RHS. Keep copies to the original results, in + // case we have to return InvalidOperands. + ExprResult OriginalLHS = LHS; + ExprResult OriginalRHS = RHS; + if (LHSMatType && !RHSMatType) { + if (tryConvertScalarToMatrixElementTy(*this, LHSMatType->getElementType(), + &RHS)) + return LHSType; + return InvalidOperands(Loc, OriginalLHS, OriginalRHS); + } + + if (!LHSMatType && RHSMatType) { + if (tryConvertScalarToMatrixElementTy(*this, RHSMatType->getElementType(), + &LHS)) + return RHSType; + return InvalidOperands(Loc, OriginalLHS, OriginalRHS); + } + + return InvalidOperands(Loc, LHS, RHS); +} + inline QualType Sema::CheckBitwiseOperands(ExprResult &LHS, ExprResult &RHS, SourceLocation Loc, BinaryOperatorKind Opc) { @@ -13528,14 +13600,6 @@ ExprResult Sema::CreateBuiltinBinOp(SourceLocation OpLoc, } } - // Diagnose operations on the unsupported types for OpenMP device compilation. - if (getLangOpts().OpenMP && getLangOpts().OpenMPIsDevice) { - if (Opc != BO_Assign && Opc != BO_Comma) { - checkOpenMPDeviceExpr(LHSExpr); - checkOpenMPDeviceExpr(RHSExpr); - } - } - switch (Opc) { case BO_Assign: ResultTy = CheckAssignmentOperands(LHS.get(), RHS, OpLoc, QualType()); @@ -14148,12 +14212,6 @@ ExprResult Sema::CreateBuiltinUnaryOp(SourceLocation OpLoc, << Input.get()->getSourceRange()); } } - // Diagnose operations on the unsupported types for OpenMP device compilation. - if (getLangOpts().OpenMP && getLangOpts().OpenMPIsDevice) { - if (UnaryOperator::isIncrementDecrementOp(Opc) || - UnaryOperator::isArithmeticOp(Opc)) - checkOpenMPDeviceExpr(InputExpr); - } switch (Opc) { case UO_PreInc: @@ -16414,6 +16472,9 @@ void Sema::MarkFunctionReferenced(SourceLocation Loc, FunctionDecl *Func, if (getLangOpts().SYCLIsDevice) checkSYCLDeviceFunction(Loc, Func); + if (getLangOpts().SYCLIsDevice) + checkSYCLDeviceFunction(Loc, Func); + // If we need a definition, try to create one. if (NeedDefinition && !Func->getBody()) { runWithSufficientStackSpace(Loc, [&] { diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp index f7e77d9661791..e26e6321bdf22 100644 --- a/clang/lib/Sema/SemaExprCXX.cpp +++ b/clang/lib/Sema/SemaExprCXX.cpp @@ -7669,61 +7669,6 @@ ExprResult Sema::ActOnNoexceptExpr(SourceLocation KeyLoc, SourceLocation, return BuildCXXNoexceptExpr(KeyLoc, Operand, RParen); } -static bool IsSpecialDiscardedValue(Expr *E) { - // In C++11, discarded-value expressions of a certain form are special, - // according to [expr]p10: - // The lvalue-to-rvalue conversion (4.1) is applied only if the - // expression is an lvalue of volatile-qualified type and it has - // one of the following forms: - E = E->IgnoreParens(); - - // - id-expression (5.1.1), - if (isa(E)) - return true; - - // - subscripting (5.2.1), - if (isa(E)) - return true; - - // - class member access (5.2.5), - if (isa(E)) - return true; - - // - indirection (5.3.1), - if (UnaryOperator *UO = dyn_cast(E)) - if (UO->getOpcode() == UO_Deref) - return true; - - if (BinaryOperator *BO = dyn_cast(E)) { - // - pointer-to-member operation (5.5), - if (BO->isPtrMemOp()) - return true; - - // - comma expression (5.18) where the right operand is one of the above. - if (BO->getOpcode() == BO_Comma) - return IsSpecialDiscardedValue(BO->getRHS()); - } - - // - conditional expression (5.16) where both the second and the third - // operands are one of the above, or - if (ConditionalOperator *CO = dyn_cast(E)) - return IsSpecialDiscardedValue(CO->getTrueExpr()) && - IsSpecialDiscardedValue(CO->getFalseExpr()); - // The related edge case of "*x ?: *x". - if (BinaryConditionalOperator *BCO = - dyn_cast(E)) { - if (OpaqueValueExpr *OVE = dyn_cast(BCO->getTrueExpr())) - return IsSpecialDiscardedValue(OVE->getSourceExpr()) && - IsSpecialDiscardedValue(BCO->getFalseExpr()); - } - - // Objective-C++ extensions to the rule. - if (isa(E) || isa(E)) - return true; - - return false; -} - /// Perform the conversions required for an expression used in a /// context that ignores the result. ExprResult Sema::IgnoredValueConversions(Expr *E) { @@ -7748,23 +7693,20 @@ ExprResult Sema::IgnoredValueConversions(Expr *E) { return E; } - if (getLangOpts().CPlusPlus) { + if (getLangOpts().CPlusPlus) { // The C++11 standard defines the notion of a discarded-value expression; // normally, we don't need to do anything to handle it, but if it is a // volatile lvalue with a special form, we perform an lvalue-to-rvalue // conversion. - if (getLangOpts().CPlusPlus11 && E->isGLValue() && - E->getType().isVolatileQualified()) { - if (IsSpecialDiscardedValue(E)) { - ExprResult Res = DefaultLvalueConversion(E); - if (Res.isInvalid()) - return E; - E = Res.get(); - } else { - // Per C++2a [expr.ass]p5, a volatile assignment is not deprecated if - // it occurs as a discarded-value expression. - CheckUnusedVolatileAssignment(E); - } + if (getLangOpts().CPlusPlus11 && E->isReadIfDiscardedInCPlusPlus11()) { + ExprResult Res = DefaultLvalueConversion(E); + if (Res.isInvalid()) + return E; + E = Res.get(); + } else { + // Per C++2a [expr.ass]p5, a volatile assignment is not deprecated if + // it occurs as a discarded-value expression. + CheckUnusedVolatileAssignment(E); } // C++1z: diff --git a/clang/lib/Sema/SemaOpenMP.cpp b/clang/lib/Sema/SemaOpenMP.cpp index e556969a786ab..17b585862639d 100644 --- a/clang/lib/Sema/SemaOpenMP.cpp +++ b/clang/lib/Sema/SemaOpenMP.cpp @@ -1832,23 +1832,28 @@ Sema::DeviceDiagBuilder Sema::diagIfOpenMPDeviceCode(SourceLocation Loc, unsigned DiagID) { assert(LangOpts.OpenMP && LangOpts.OpenMPIsDevice && "Expected OpenMP device compilation."); - FunctionEmissionStatus FES = getEmissionStatus(getCurFunctionDecl()); + + FunctionDecl *FD = getCurFunctionDecl(); DeviceDiagBuilder::Kind Kind = DeviceDiagBuilder::K_Nop; - switch (FES) { - case FunctionEmissionStatus::Emitted: - Kind = DeviceDiagBuilder::K_Immediate; - break; - case FunctionEmissionStatus::Unknown: - Kind = isOpenMPDeviceDelayedContext(*this) ? DeviceDiagBuilder::K_Deferred - : DeviceDiagBuilder::K_Immediate; - break; - case FunctionEmissionStatus::TemplateDiscarded: - case FunctionEmissionStatus::OMPDiscarded: - Kind = DeviceDiagBuilder::K_Nop; - break; - case FunctionEmissionStatus::CUDADiscarded: - llvm_unreachable("CUDADiscarded unexpected in OpenMP device compilation"); - break; + if (FD) { + FunctionEmissionStatus FES = getEmissionStatus(FD); + switch (FES) { + case FunctionEmissionStatus::Emitted: + Kind = DeviceDiagBuilder::K_Immediate; + break; + case FunctionEmissionStatus::Unknown: + Kind = isOpenMPDeviceDelayedContext(*this) + ? DeviceDiagBuilder::K_Deferred + : DeviceDiagBuilder::K_Immediate; + break; + case FunctionEmissionStatus::TemplateDiscarded: + case FunctionEmissionStatus::OMPDiscarded: + Kind = DeviceDiagBuilder::K_Nop; + break; + case FunctionEmissionStatus::CUDADiscarded: + llvm_unreachable("CUDADiscarded unexpected in OpenMP device compilation"); + break; + } } return DeviceDiagBuilder(Kind, Loc, DiagID, getCurFunctionDecl(), *this); @@ -1877,21 +1882,6 @@ Sema::DeviceDiagBuilder Sema::diagIfOpenMPHostCode(SourceLocation Loc, return DeviceDiagBuilder(Kind, Loc, DiagID, getCurFunctionDecl(), *this); } -void Sema::checkOpenMPDeviceExpr(const Expr *E) { - assert(getLangOpts().OpenMP && getLangOpts().OpenMPIsDevice && - "OpenMP device compilation mode is expected."); - QualType Ty = E->getType(); - if ((Ty->isFloat16Type() && !Context.getTargetInfo().hasFloat16Type()) || - ((Ty->isFloat128Type() || - (Ty->isRealFloatingType() && Context.getTypeSize(Ty) == 128)) && - !Context.getTargetInfo().hasFloat128Type()) || - (Ty->isIntegerType() && Context.getTypeSize(Ty) == 128 && - !Context.getTargetInfo().hasInt128Type())) - targetDiag(E->getExprLoc(), diag::err_omp_unsupported_type) - << static_cast(Context.getTypeSize(Ty)) << Ty - << Context.getTargetInfo().getTriple().str() << E->getSourceRange(); -} - static OpenMPDefaultmapClauseKind getVariableCategoryFromDecl(const LangOptions &LO, const ValueDecl *VD) { if (LO.OpenMP <= 45) { @@ -5408,6 +5398,7 @@ StmtResult Sema::ActOnOpenMPExecutableDirective( case OMPC_to: case OMPC_from: case OMPC_use_device_ptr: + case OMPC_use_device_addr: case OMPC_is_device_ptr: case OMPC_nontemporal: case OMPC_order: @@ -10165,12 +10156,18 @@ StmtResult Sema::ActOnOpenMPTargetDataDirective(ArrayRef Clauses, assert(isa(AStmt) && "Captured statement expected"); - // OpenMP [2.10.1, Restrictions, p. 97] - // At least one map clause must appear on the directive. - if (!hasClauses(Clauses, OMPC_map, OMPC_use_device_ptr)) { + // OpenMP [2.12.2, target data Construct, Restrictions] + // At least one map, use_device_addr or use_device_ptr clause must appear on + // the directive. + if (!hasClauses(Clauses, OMPC_map, OMPC_use_device_ptr) && + (LangOpts.OpenMP < 50 || !hasClauses(Clauses, OMPC_use_device_addr))) { + StringRef Expected; + if (LangOpts.OpenMP < 50) + Expected = "'map' or 'use_device_ptr'"; + else + Expected = "'map', 'use_device_ptr', or 'use_device_addr'"; Diag(StartLoc, diag::err_omp_no_clause_for_directive) - << "'map' or 'use_device_ptr'" - << getOpenMPDirectiveName(OMPD_target_data); + << Expected << getOpenMPDirectiveName(OMPD_target_data); return StmtError(); } @@ -11535,6 +11532,7 @@ OMPClause *Sema::ActOnOpenMPSingleExprClause(OpenMPClauseKind Kind, Expr *Expr, case OMPC_to: case OMPC_from: case OMPC_use_device_ptr: + case OMPC_use_device_addr: case OMPC_is_device_ptr: case OMPC_unified_address: case OMPC_unified_shared_memory: @@ -12289,6 +12287,7 @@ static OpenMPDirectiveKind getOpenMPCaptureRegionForClause( case OMPC_to: case OMPC_from: case OMPC_use_device_ptr: + case OMPC_use_device_addr: case OMPC_is_device_ptr: case OMPC_unified_address: case OMPC_unified_shared_memory: @@ -12731,6 +12730,7 @@ OMPClause *Sema::ActOnOpenMPSimpleClause( case OMPC_to: case OMPC_from: case OMPC_use_device_ptr: + case OMPC_use_device_addr: case OMPC_is_device_ptr: case OMPC_unified_address: case OMPC_unified_shared_memory: @@ -12956,6 +12956,7 @@ OMPClause *Sema::ActOnOpenMPSingleExprWithArgClause( case OMPC_to: case OMPC_from: case OMPC_use_device_ptr: + case OMPC_use_device_addr: case OMPC_is_device_ptr: case OMPC_unified_address: case OMPC_unified_shared_memory: @@ -13195,6 +13196,7 @@ OMPClause *Sema::ActOnOpenMPClause(OpenMPClauseKind Kind, case OMPC_to: case OMPC_from: case OMPC_use_device_ptr: + case OMPC_use_device_addr: case OMPC_is_device_ptr: case OMPC_atomic_default_mem_order: case OMPC_device_type: @@ -13406,6 +13408,9 @@ OMPClause *Sema::ActOnOpenMPVarListClause( case OMPC_use_device_ptr: Res = ActOnOpenMPUseDevicePtrClause(VarList, Locs); break; + case OMPC_use_device_addr: + Res = ActOnOpenMPUseDeviceAddrClause(VarList, Locs); + break; case OMPC_is_device_ptr: Res = ActOnOpenMPIsDevicePtrClause(VarList, Locs); break; @@ -18389,6 +18394,54 @@ OMPClause *Sema::ActOnOpenMPUseDevicePtrClause(ArrayRef VarList, MVLI.VarBaseDeclarations, MVLI.VarComponents); } +OMPClause *Sema::ActOnOpenMPUseDeviceAddrClause(ArrayRef VarList, + const OMPVarListLocTy &Locs) { + MappableVarListInfo MVLI(VarList); + + for (Expr *RefExpr : VarList) { + assert(RefExpr && "NULL expr in OpenMP use_device_addr clause."); + SourceLocation ELoc; + SourceRange ERange; + Expr *SimpleRefExpr = RefExpr; + auto Res = getPrivateItem(*this, SimpleRefExpr, ELoc, ERange, + /*AllowArraySection=*/true); + if (Res.second) { + // It will be analyzed later. + MVLI.ProcessedVarList.push_back(RefExpr); + } + ValueDecl *D = Res.first; + if (!D) + continue; + auto *VD = dyn_cast(D); + + // If required, build a capture to implement the privatization initialized + // with the current list item value. + DeclRefExpr *Ref = nullptr; + if (!VD) + Ref = buildCapture(*this, D, SimpleRefExpr, /*WithInit=*/true); + MVLI.ProcessedVarList.push_back(VD ? RefExpr->IgnoreParens() : Ref); + + // We need to add a data sharing attribute for this variable to make sure it + // is correctly captured. A variable that shows up in a use_device_addr has + // similar properties of a first private variable. + DSAStack->addDSA(D, RefExpr->IgnoreParens(), OMPC_firstprivate, Ref); + + // Create a mappable component for the list item. List items in this clause + // only need a component. + MVLI.VarBaseDeclarations.push_back(D); + MVLI.VarComponents.emplace_back(); + MVLI.VarComponents.back().push_back( + OMPClauseMappableExprCommon::MappableComponent(SimpleRefExpr, D)); + } + + if (MVLI.ProcessedVarList.empty()) + return nullptr; + + return OMPUseDeviceAddrClause::Create(Context, Locs, MVLI.ProcessedVarList, + MVLI.VarBaseDeclarations, + MVLI.VarComponents); +} + OMPClause *Sema::ActOnOpenMPIsDevicePtrClause(ArrayRef VarList, const OMPVarListLocTy &Locs) { MappableVarListInfo MVLI(VarList); diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp index 1b00b2b18572b..1aef43614d99e 100644 --- a/clang/lib/Sema/SemaOverload.cpp +++ b/clang/lib/Sema/SemaOverload.cpp @@ -6356,7 +6356,8 @@ void Sema::AddOverloadCandidate( } } - if (EnableIfAttr *FailedAttr = CheckEnableIf(Function, Args)) { + if (EnableIfAttr *FailedAttr = + CheckEnableIf(Function, CandidateSet.getLocation(), Args)) { Candidate.Viable = false; Candidate.FailureKind = ovl_fail_enable_if; Candidate.DeductionFailure.Data = FailedAttr; @@ -6462,11 +6463,10 @@ Sema::SelectBestMethod(Selector Sel, MultiExprArg Args, bool IsInstance, return nullptr; } -static bool -convertArgsForAvailabilityChecks(Sema &S, FunctionDecl *Function, Expr *ThisArg, - ArrayRef Args, Sema::SFINAETrap &Trap, - bool MissingImplicitThis, Expr *&ConvertedThis, - SmallVectorImpl &ConvertedArgs) { +static bool convertArgsForAvailabilityChecks( + Sema &S, FunctionDecl *Function, Expr *ThisArg, SourceLocation CallLoc, + ArrayRef Args, Sema::SFINAETrap &Trap, bool MissingImplicitThis, + Expr *&ConvertedThis, SmallVectorImpl &ConvertedArgs) { if (ThisArg) { CXXMethodDecl *Method = cast(Function); assert(!isa(Method) && @@ -6511,17 +6511,7 @@ convertArgsForAvailabilityChecks(Sema &S, FunctionDecl *Function, Expr *ThisArg, if (!Function->isVariadic() && Args.size() < Function->getNumParams()) { for (unsigned i = Args.size(), e = Function->getNumParams(); i != e; ++i) { ParmVarDecl *P = Function->getParamDecl(i); - Expr *DefArg = P->hasUninstantiatedDefaultArg() - ? P->getUninstantiatedDefaultArg() - : P->getDefaultArg(); - // This can only happen in code completion, i.e. when PartialOverloading - // is true. - if (!DefArg) - return false; - ExprResult R = - S.PerformCopyInitialization(InitializedEntity::InitializeParameter( - S.Context, Function->getParamDecl(i)), - SourceLocation(), DefArg); + ExprResult R = S.BuildCXXDefaultArgExpr(CallLoc, Function, P); if (R.isInvalid()) return false; ConvertedArgs.push_back(R.get()); @@ -6533,7 +6523,9 @@ convertArgsForAvailabilityChecks(Sema &S, FunctionDecl *Function, Expr *ThisArg, return true; } -EnableIfAttr *Sema::CheckEnableIf(FunctionDecl *Function, ArrayRef Args, +EnableIfAttr *Sema::CheckEnableIf(FunctionDecl *Function, + SourceLocation CallLoc, + ArrayRef Args, bool MissingImplicitThis) { auto EnableIfAttrs = Function->specific_attrs(); if (EnableIfAttrs.begin() == EnableIfAttrs.end()) @@ -6544,7 +6536,7 @@ EnableIfAttr *Sema::CheckEnableIf(FunctionDecl *Function, ArrayRef Args, // FIXME: We should look into making enable_if late-parsed. Expr *DiscardedThis; if (!convertArgsForAvailabilityChecks( - *this, Function, /*ThisArg=*/nullptr, Args, Trap, + *this, Function, /*ThisArg=*/nullptr, CallLoc, Args, Trap, /*MissingImplicitThis=*/true, DiscardedThis, ConvertedArgs)) return *EnableIfAttrs.begin(); @@ -6874,7 +6866,8 @@ Sema::AddMethodCandidate(CXXMethodDecl *Method, DeclAccessPair FoundDecl, } } - if (EnableIfAttr *FailedAttr = CheckEnableIf(Method, Args, true)) { + if (EnableIfAttr *FailedAttr = + CheckEnableIf(Method, CandidateSet.getLocation(), Args, true)) { Candidate.Viable = false; Candidate.FailureKind = ovl_fail_enable_if; Candidate.DeductionFailure.Data = FailedAttr; @@ -7327,7 +7320,8 @@ void Sema::AddConversionCandidate( "Can only end up with a standard conversion sequence or failure"); } - if (EnableIfAttr *FailedAttr = CheckEnableIf(Conversion, None)) { + if (EnableIfAttr *FailedAttr = + CheckEnableIf(Conversion, CandidateSet.getLocation(), None)) { Candidate.Viable = false; Candidate.FailureKind = ovl_fail_enable_if; Candidate.DeductionFailure.Data = FailedAttr; @@ -7497,7 +7491,8 @@ void Sema::AddSurrogateCandidate(CXXConversionDecl *Conversion, } } - if (EnableIfAttr *FailedAttr = CheckEnableIf(Conversion, None)) { + if (EnableIfAttr *FailedAttr = + CheckEnableIf(Conversion, CandidateSet.getLocation(), None)) { Candidate.Viable = false; Candidate.FailureKind = ovl_fail_enable_if; Candidate.DeductionFailure.Data = FailedAttr; @@ -7687,6 +7682,10 @@ class BuiltinCandidateTypeSet { /// candidates. TypeSet VectorTypes; + /// The set of matrix types that will be used in the built-in + /// candidates. + TypeSet MatrixTypes; + /// A flag indicating non-record types are viable candidates bool HasNonRecordTypes; @@ -7747,6 +7746,11 @@ class BuiltinCandidateTypeSet { iterator vector_begin() { return VectorTypes.begin(); } iterator vector_end() { return VectorTypes.end(); } + llvm::iterator_range matrix_types() { return MatrixTypes; } + iterator matrix_begin() { return MatrixTypes.begin(); } + iterator matrix_end() { return MatrixTypes.end(); } + + bool containsMatrixType(QualType Ty) const { return MatrixTypes.count(Ty); } bool hasNonRecordTypes() { return HasNonRecordTypes; } bool hasArithmeticOrEnumeralTypes() { return HasArithmeticOrEnumeralTypes; } bool hasNullPtrType() const { return HasNullPtrType; } @@ -7921,6 +7925,11 @@ BuiltinCandidateTypeSet::AddTypesConvertedFrom(QualType Ty, // extension. HasArithmeticOrEnumeralTypes = true; VectorTypes.insert(Ty); + } else if (Ty->isMatrixType()) { + // Similar to vector types, we treat vector types as arithmetic types in + // many contexts as an extension. + HasArithmeticOrEnumeralTypes = true; + MatrixTypes.insert(Ty); } else if (Ty->isNullPtrType()) { HasNullPtrType = true; } else if (AllowUserConversions && TyRec) { @@ -8149,6 +8158,13 @@ class BuiltinOperatorOverloadBuilder { } + /// Helper to add an overload candidate for a binary builtin with types \p L + /// and \p R. + void AddCandidate(QualType L, QualType R) { + QualType LandR[2] = {L, R}; + S.AddBuiltinCandidate(LandR, Args, CandidateSet); + } + public: BuiltinOperatorOverloadBuilder( Sema &S, ArrayRef Args, @@ -8567,6 +8583,27 @@ class BuiltinOperatorOverloadBuilder { } } + /// Add binary operator overloads for each candidate matrix type M1, M2: + /// * (M1, M1) -> M1 + /// * (M1, M1.getElementType()) -> M1 + /// * (M2.getElementType(), M2) -> M2 + /// * (M2, M2) -> M2 // Only if M2 is not part of CandidateTypes[0]. + void addMatrixBinaryArithmeticOverloads() { + if (!HasArithmeticOrEnumeralCandidateType) + return; + + for (QualType M1 : CandidateTypes[0].matrix_types()) { + AddCandidate(M1, cast(M1)->getElementType()); + AddCandidate(M1, M1); + } + + for (QualType M2 : CandidateTypes[1].matrix_types()) { + AddCandidate(cast(M2)->getElementType(), M2); + if (!CandidateTypes[0].containsMatrixType(M2)) + AddCandidate(M2, M2); + } + } + // C++2a [over.built]p14: // // For every integral type T there exists a candidate operator function @@ -9140,6 +9177,7 @@ void Sema::AddBuiltinOperatorCandidates(OverloadedOperatorKind Op, } else { OpBuilder.addBinaryPlusOrMinusPointerOverloads(Op); OpBuilder.addGenericBinaryArithmeticOverloads(); + OpBuilder.addMatrixBinaryArithmeticOverloads(); } break; @@ -14130,7 +14168,8 @@ Sema::BuildCallToMemberFunction(Scope *S, Expr *MemExprE, // resolution process, we still need to handle the enable_if attribute. Do // that here, so it will not hide previous -- and more relevant -- errors. if (auto *MemE = dyn_cast(NakedMemExpr)) { - if (const EnableIfAttr *Attr = CheckEnableIf(Method, Args, true)) { + if (const EnableIfAttr *Attr = + CheckEnableIf(Method, LParenLoc, Args, true)) { Diag(MemE->getMemberLoc(), diag::err_ovl_no_viable_member_function_in_call) << Method << Method->getSourceRange(); diff --git a/clang/lib/Sema/SemaStmt.cpp b/clang/lib/Sema/SemaStmt.cpp index 9b048caf9cadd..a913f561e454b 100644 --- a/clang/lib/Sema/SemaStmt.cpp +++ b/clang/lib/Sema/SemaStmt.cpp @@ -3309,6 +3309,7 @@ Sema::ActOnCapScopeReturnStmt(SourceLocation ReturnLoc, Expr *RetValExp) { assert(AT && "lost auto type from lambda return type"); if (DeduceFunctionTypeFromReturnExpr(FD, ReturnLoc, RetValExp, AT)) { FD->setInvalidDecl(); + // FIXME: preserve the ill-formed return expression. return StmtError(); } CurCap->ReturnType = FnRetType = FD->getReturnType(); diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp index 19f8248db6bfd..877020ed4dcf9 100644 --- a/clang/lib/Sema/SemaTemplateDeduction.cpp +++ b/clang/lib/Sema/SemaTemplateDeduction.cpp @@ -4655,6 +4655,8 @@ Sema::DeduceAutoResult Sema::DeduceAutoType(TypeLoc Type, Expr *&Init, QualType &Result, Optional DependentDeductionDepth, bool IgnoreConstraints) { + if (Init->containsErrors()) + return DAR_FailedAlreadyDiagnosed; if (Init->getType()->isNonOverloadPlaceholderType()) { ExprResult NonPlaceholder = CheckPlaceholderExpr(Init); if (NonPlaceholder.isInvalid()) diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h index 5a3dab4a525c3..85b7885b7817c 100644 --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -2040,6 +2040,15 @@ class TreeTransform { return getSema().ActOnOpenMPUseDevicePtrClause(VarList, Locs); } + /// Build a new OpenMP 'use_device_addr' clause. + /// + /// By default, performs semantic analysis to build the new OpenMP clause. + /// Subclasses may override this routine to provide different behavior. + OMPClause *RebuildOMPUseDeviceAddrClause(ArrayRef VarList, + const OMPVarListLocTy &Locs) { + return getSema().ActOnOpenMPUseDeviceAddrClause(VarList, Locs); + } + /// Build a new OpenMP 'is_device_ptr' clause. /// /// By default, performs semantic analysis to build the new OpenMP clause. @@ -9747,6 +9756,21 @@ OMPClause *TreeTransform::TransformOMPUseDevicePtrClause( return getDerived().RebuildOMPUseDevicePtrClause(Vars, Locs); } +template +OMPClause *TreeTransform::TransformOMPUseDeviceAddrClause( + OMPUseDeviceAddrClause *C) { + llvm::SmallVector Vars; + Vars.reserve(C->varlist_size()); + for (auto *VE : C->varlists()) { + ExprResult EVar = getDerived().TransformExpr(cast(VE)); + if (EVar.isInvalid()) + return nullptr; + Vars.push_back(EVar.get()); + } + OMPVarListLocTy Locs(C->getBeginLoc(), C->getLParenLoc(), C->getEndLoc()); + return getDerived().RebuildOMPUseDeviceAddrClause(Vars, Locs); +} + template OMPClause * TreeTransform::TransformOMPIsDevicePtrClause(OMPIsDevicePtrClause *C) { diff --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp index 16bcb18f4e68e..a5a1276253c7c 100644 --- a/clang/lib/Serialization/ASTReader.cpp +++ b/clang/lib/Serialization/ASTReader.cpp @@ -11918,6 +11918,15 @@ OMPClause *OMPClauseReader::readClause() { C = OMPUseDevicePtrClause::CreateEmpty(Context, Sizes); break; } + case llvm::omp::OMPC_use_device_addr: { + OMPMappableExprListSizeTy Sizes; + Sizes.NumVars = Record.readInt(); + Sizes.NumUniqueDeclarations = Record.readInt(); + Sizes.NumComponentLists = Record.readInt(); + Sizes.NumComponents = Record.readInt(); + C = OMPUseDeviceAddrClause::CreateEmpty(Context, Sizes); + break; + } case llvm::omp::OMPC_is_device_ptr: { OMPMappableExprListSizeTy Sizes; Sizes.NumVars = Record.readInt(); @@ -12704,6 +12713,48 @@ void OMPClauseReader::VisitOMPUseDevicePtrClause(OMPUseDevicePtrClause *C) { C->setComponents(Components, ListSizes); } +void OMPClauseReader::VisitOMPUseDeviceAddrClause(OMPUseDeviceAddrClause *C) { + C->setLParenLoc(Record.readSourceLocation()); + auto NumVars = C->varlist_size(); + auto UniqueDecls = C->getUniqueDeclarationsNum(); + auto TotalLists = C->getTotalComponentListNum(); + auto TotalComponents = C->getTotalComponentsNum(); + + SmallVector Vars; + Vars.reserve(NumVars); + for (unsigned i = 0; i != NumVars; ++i) + Vars.push_back(Record.readSubExpr()); + C->setVarRefs(Vars); + + SmallVector Decls; + Decls.reserve(UniqueDecls); + for (unsigned i = 0; i < UniqueDecls; ++i) + Decls.push_back(Record.readDeclAs()); + C->setUniqueDecls(Decls); + + SmallVector ListsPerDecl; + ListsPerDecl.reserve(UniqueDecls); + for (unsigned i = 0; i < UniqueDecls; ++i) + ListsPerDecl.push_back(Record.readInt()); + C->setDeclNumLists(ListsPerDecl); + + SmallVector ListSizes; + ListSizes.reserve(TotalLists); + for (unsigned i = 0; i < TotalLists; ++i) + ListSizes.push_back(Record.readInt()); + C->setComponentListSizes(ListSizes); + + SmallVector Components; + Components.reserve(TotalComponents); + for (unsigned i = 0; i < TotalComponents; ++i) { + Expr *AssociatedExpr = Record.readSubExpr(); + auto *AssociatedDecl = Record.readDeclAs(); + Components.push_back(OMPClauseMappableExprCommon::MappableComponent( + AssociatedExpr, AssociatedDecl)); + } + C->setComponents(Components, ListSizes); +} + void OMPClauseReader::VisitOMPIsDevicePtrClause(OMPIsDevicePtrClause *C) { C->setLParenLoc(Record.readSourceLocation()); auto NumVars = C->varlist_size(); diff --git a/clang/lib/Serialization/ASTWriter.cpp b/clang/lib/Serialization/ASTWriter.cpp index 1e3adb588da29..9d81e137f0bb8 100644 --- a/clang/lib/Serialization/ASTWriter.cpp +++ b/clang/lib/Serialization/ASTWriter.cpp @@ -6625,6 +6625,26 @@ void OMPClauseWriter::VisitOMPUseDevicePtrClause(OMPUseDevicePtrClause *C) { } } +void OMPClauseWriter::VisitOMPUseDeviceAddrClause(OMPUseDeviceAddrClause *C) { + Record.push_back(C->varlist_size()); + Record.push_back(C->getUniqueDeclarationsNum()); + Record.push_back(C->getTotalComponentListNum()); + Record.push_back(C->getTotalComponentsNum()); + Record.AddSourceLocation(C->getLParenLoc()); + for (auto *E : C->varlists()) + Record.AddStmt(E); + for (auto *D : C->all_decls()) + Record.AddDeclRef(D); + for (auto N : C->all_num_lists()) + Record.push_back(N); + for (auto N : C->all_lists_sizes()) + Record.push_back(N); + for (auto &M : C->all_components()) { + Record.AddStmt(M.getAssociatedExpression()); + Record.AddDeclRef(M.getAssociatedDeclaration()); + } +} + void OMPClauseWriter::VisitOMPIsDevicePtrClause(OMPIsDevicePtrClause *C) { Record.push_back(C->varlist_size()); Record.push_back(C->getUniqueDeclarationsNum()); diff --git a/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt b/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt index 4f885fadf4158..b3dc7a9f63212 100644 --- a/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt +++ b/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt @@ -121,6 +121,7 @@ add_clang_library(clangStaticAnalyzerCheckers VLASizeChecker.cpp ValistChecker.cpp VirtualCallChecker.cpp + WebKit/NoUncountedMembersChecker.cpp WebKit/PtrTypesSemantics.cpp WebKit/RefCntblBaseVirtualDtorChecker.cpp diff --git a/clang/lib/StaticAnalyzer/Checkers/CheckPlacementNew.cpp b/clang/lib/StaticAnalyzer/Checkers/CheckPlacementNew.cpp index fec9fb59b2eb5..dc9cd717be9e9 100644 --- a/clang/lib/StaticAnalyzer/Checkers/CheckPlacementNew.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/CheckPlacementNew.cpp @@ -63,28 +63,8 @@ class PlacementNewChecker : public Checker> { SVal PlacementNewChecker::getExtentSizeOfPlace(const CXXNewExpr *NE, CheckerContext &C) const { - ProgramStateRef State = C.getState(); const Expr *Place = NE->getPlacementArg(0); - - const MemRegion *MRegion = C.getSVal(Place).getAsRegion(); - if (!MRegion) - return UnknownVal(); - RegionOffset Offset = MRegion->getAsOffset(); - if (Offset.hasSymbolicOffset()) - return UnknownVal(); - const MemRegion *BaseRegion = MRegion->getBaseRegion(); - if (!BaseRegion) - return UnknownVal(); - - SValBuilder &SvalBuilder = C.getSValBuilder(); - NonLoc OffsetInBytes = SvalBuilder.makeArrayIndex( - Offset.getOffset() / C.getASTContext().getCharWidth()); - DefinedOrUnknownSVal ExtentInBytes = - getDynamicSize(State, BaseRegion, SvalBuilder); - - return SvalBuilder.evalBinOp(State, BinaryOperator::Opcode::BO_Sub, - ExtentInBytes, OffsetInBytes, - SvalBuilder.getArrayIndexType()); + return getDynamicSizeWithOffset(C.getState(), C.getSVal(Place)); } SVal PlacementNewChecker::getExtentSizeOfNewTarget(const CXXNewExpr *NE, diff --git a/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp index a7c62a7e8046f..fa69bc253fbd0 100644 --- a/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp @@ -684,41 +684,42 @@ class MallocChecker static bool SummarizeValue(raw_ostream &os, SVal V); static bool SummarizeRegion(raw_ostream &os, const MemRegion *MR); - void ReportBadFree(CheckerContext &C, SVal ArgVal, SourceRange Range, - const Expr *DeallocExpr, AllocationFamily Family) const; + void HandleNonHeapDealloc(CheckerContext &C, SVal ArgVal, SourceRange Range, + const Expr *DeallocExpr, + AllocationFamily Family) const; - void ReportFreeAlloca(CheckerContext &C, SVal ArgVal, + void HandleFreeAlloca(CheckerContext &C, SVal ArgVal, SourceRange Range) const; - void ReportMismatchedDealloc(CheckerContext &C, SourceRange Range, + void HandleMismatchedDealloc(CheckerContext &C, SourceRange Range, const Expr *DeallocExpr, const RefState *RS, SymbolRef Sym, bool OwnershipTransferred) const; - void ReportOffsetFree(CheckerContext &C, SVal ArgVal, SourceRange Range, + void HandleOffsetFree(CheckerContext &C, SVal ArgVal, SourceRange Range, const Expr *DeallocExpr, AllocationFamily Family, const Expr *AllocExpr = nullptr) const; - void ReportUseAfterFree(CheckerContext &C, SourceRange Range, + void HandleUseAfterFree(CheckerContext &C, SourceRange Range, SymbolRef Sym) const; - void ReportDoubleFree(CheckerContext &C, SourceRange Range, bool Released, + void HandleDoubleFree(CheckerContext &C, SourceRange Range, bool Released, SymbolRef Sym, SymbolRef PrevSym) const; - void ReportDoubleDelete(CheckerContext &C, SymbolRef Sym) const; + void HandleDoubleDelete(CheckerContext &C, SymbolRef Sym) const; - void ReportUseZeroAllocated(CheckerContext &C, SourceRange Range, - SymbolRef Sym) const; + void HandleUseZeroAlloc(CheckerContext &C, SourceRange Range, + SymbolRef Sym) const; - void ReportFunctionPointerFree(CheckerContext &C, SVal ArgVal, - SourceRange Range, const Expr *FreeExpr, - AllocationFamily Family) const; + void HandleFunctionPtrFree(CheckerContext &C, SVal ArgVal, SourceRange Range, + const Expr *FreeExpr, + AllocationFamily Family) const; /// Find the location of the allocation for Sym on the path leading to the /// exploded node N. static LeakInfo getAllocationSite(const ExplodedNode *N, SymbolRef Sym, CheckerContext &C); - void reportLeak(SymbolRef Sym, ExplodedNode *N, CheckerContext &C) const; + void HandleLeak(SymbolRef Sym, ExplodedNode *N, CheckerContext &C) const; /// Test if value in ArgVal equals to value in macro `ZERO_SIZE_PTR`. bool isArgZERO_SIZE_PTR(ProgramStateRef State, CheckerContext &C, @@ -1743,6 +1744,15 @@ ProgramStateRef MallocChecker::FreeMemAux( const MemRegion *R = ArgVal.getAsRegion(); const Expr *ParentExpr = Call.getOriginExpr(); + // NOTE: We detected a bug, but the checker under whose name we would emit the + // error could be disabled. Generally speaking, the MallocChecker family is an + // integral part of the Static Analyzer, and disabling any part of it should + // only be done under exceptional circumstances, such as frequent false + // positives. If this is the case, we can reasonably believe that there are + // serious faults in our understanding of the source code, and even if we + // don't emit an warning, we should terminate further analysis with a sink + // node. + // Nonlocs can't be freed, of course. // Non-region locations (labels and fixed addresses) also shouldn't be freed. if (!R) { @@ -1752,7 +1762,8 @@ ProgramStateRef MallocChecker::FreeMemAux( // zero-sized memory block which is allowed to be freed, despite not being a // null pointer. if (Family != AF_Malloc || !isArgZERO_SIZE_PTR(State, C, ArgVal)) - ReportBadFree(C, ArgVal, ArgExpr->getSourceRange(), ParentExpr, Family); + HandleNonHeapDealloc(C, ArgVal, ArgExpr->getSourceRange(), ParentExpr, + Family); return nullptr; } @@ -1760,7 +1771,8 @@ ProgramStateRef MallocChecker::FreeMemAux( // Blocks might show up as heap data, but should not be free()d if (isa(R)) { - ReportBadFree(C, ArgVal, ArgExpr->getSourceRange(), ParentExpr, Family); + HandleNonHeapDealloc(C, ArgVal, ArgExpr->getSourceRange(), ParentExpr, + Family); return nullptr; } @@ -1778,9 +1790,10 @@ ProgramStateRef MallocChecker::FreeMemAux( // False negatives are better than false positives. if (isa(R)) - ReportFreeAlloca(C, ArgVal, ArgExpr->getSourceRange()); + HandleFreeAlloca(C, ArgVal, ArgExpr->getSourceRange()); else - ReportBadFree(C, ArgVal, ArgExpr->getSourceRange(), ParentExpr, Family); + HandleNonHeapDealloc(C, ArgVal, ArgExpr->getSourceRange(), ParentExpr, + Family); return nullptr; } @@ -1802,14 +1815,14 @@ ProgramStateRef MallocChecker::FreeMemAux( // Memory returned by alloca() shouldn't be freed. if (RsBase->getAllocationFamily() == AF_Alloca) { - ReportFreeAlloca(C, ArgVal, ArgExpr->getSourceRange()); + HandleFreeAlloca(C, ArgVal, ArgExpr->getSourceRange()); return nullptr; } // Check for double free first. if ((RsBase->isReleased() || RsBase->isRelinquished()) && !didPreviousFreeFail(State, SymBase, PreviousRetStatusSymbol)) { - ReportDoubleFree(C, ParentExpr->getSourceRange(), RsBase->isReleased(), + HandleDoubleFree(C, ParentExpr->getSourceRange(), RsBase->isReleased(), SymBase, PreviousRetStatusSymbol); return nullptr; @@ -1821,8 +1834,8 @@ ProgramStateRef MallocChecker::FreeMemAux( // Check if an expected deallocation function matches the real one. bool DeallocMatchesAlloc = RsBase->getAllocationFamily() == Family; if (!DeallocMatchesAlloc) { - ReportMismatchedDealloc(C, ArgExpr->getSourceRange(), - ParentExpr, RsBase, SymBase, Hold); + HandleMismatchedDealloc(C, ArgExpr->getSourceRange(), ParentExpr, + RsBase, SymBase, Hold); return nullptr; } @@ -1833,7 +1846,7 @@ ProgramStateRef MallocChecker::FreeMemAux( !Offset.hasSymbolicOffset() && Offset.getOffset() != 0) { const Expr *AllocExpr = cast(RsBase->getStmt()); - ReportOffsetFree(C, ArgVal, ArgExpr->getSourceRange(), ParentExpr, + HandleOffsetFree(C, ArgVal, ArgExpr->getSourceRange(), ParentExpr, Family, AllocExpr); return nullptr; } @@ -1841,8 +1854,8 @@ ProgramStateRef MallocChecker::FreeMemAux( } if (SymBase->getType()->isFunctionPointerType()) { - ReportFunctionPointerFree(C, ArgVal, ArgExpr->getSourceRange(), ParentExpr, - Family); + HandleFunctionPtrFree(C, ArgVal, ArgExpr->getSourceRange(), ParentExpr, + Family); return nullptr; } @@ -2009,13 +2022,15 @@ bool MallocChecker::SummarizeRegion(raw_ostream &os, } } -void MallocChecker::ReportBadFree(CheckerContext &C, SVal ArgVal, - SourceRange Range, const Expr *DeallocExpr, - AllocationFamily Family) const { +void MallocChecker::HandleNonHeapDealloc(CheckerContext &C, SVal ArgVal, + SourceRange Range, + const Expr *DeallocExpr, + AllocationFamily Family) const { - if (!ChecksEnabled[CK_MallocChecker] && - !ChecksEnabled[CK_NewDeleteChecker]) + if (!ChecksEnabled[CK_MallocChecker] && !ChecksEnabled[CK_NewDeleteChecker]) { + C.addSink(); return; + } Optional CheckKind = getCheckIfTracked(Family); if (!CheckKind.hasValue()) @@ -2055,7 +2070,7 @@ void MallocChecker::ReportBadFree(CheckerContext &C, SVal ArgVal, } } -void MallocChecker::ReportFreeAlloca(CheckerContext &C, SVal ArgVal, +void MallocChecker::HandleFreeAlloca(CheckerContext &C, SVal ArgVal, SourceRange Range) const { Optional CheckKind; @@ -2064,8 +2079,10 @@ void MallocChecker::ReportFreeAlloca(CheckerContext &C, SVal ArgVal, CheckKind = CK_MallocChecker; else if (ChecksEnabled[CK_MismatchedDeallocatorChecker]) CheckKind = CK_MismatchedDeallocatorChecker; - else + else { + C.addSink(); return; + } if (ExplodedNode *N = C.generateErrorNode()) { if (!BT_FreeAlloca[*CheckKind]) @@ -2081,15 +2098,16 @@ void MallocChecker::ReportFreeAlloca(CheckerContext &C, SVal ArgVal, } } -void MallocChecker::ReportMismatchedDealloc(CheckerContext &C, +void MallocChecker::HandleMismatchedDealloc(CheckerContext &C, SourceRange Range, const Expr *DeallocExpr, - const RefState *RS, - SymbolRef Sym, + const RefState *RS, SymbolRef Sym, bool OwnershipTransferred) const { - if (!ChecksEnabled[CK_MismatchedDeallocatorChecker]) + if (!ChecksEnabled[CK_MismatchedDeallocatorChecker]) { + C.addSink(); return; + } if (ExplodedNode *N = C.generateErrorNode()) { if (!BT_MismatchedDealloc) @@ -2137,14 +2155,15 @@ void MallocChecker::ReportMismatchedDealloc(CheckerContext &C, } } -void MallocChecker::ReportOffsetFree(CheckerContext &C, SVal ArgVal, +void MallocChecker::HandleOffsetFree(CheckerContext &C, SVal ArgVal, SourceRange Range, const Expr *DeallocExpr, AllocationFamily Family, const Expr *AllocExpr) const { - if (!ChecksEnabled[CK_MallocChecker] && - !ChecksEnabled[CK_NewDeleteChecker]) + if (!ChecksEnabled[CK_MallocChecker] && !ChecksEnabled[CK_NewDeleteChecker]) { + C.addSink(); return; + } Optional CheckKind = getCheckIfTracked(Family); if (!CheckKind.hasValue()) @@ -2194,13 +2213,14 @@ void MallocChecker::ReportOffsetFree(CheckerContext &C, SVal ArgVal, C.emitReport(std::move(R)); } -void MallocChecker::ReportUseAfterFree(CheckerContext &C, SourceRange Range, +void MallocChecker::HandleUseAfterFree(CheckerContext &C, SourceRange Range, SymbolRef Sym) const { - if (!ChecksEnabled[CK_MallocChecker] && - !ChecksEnabled[CK_NewDeleteChecker] && - !ChecksEnabled[CK_InnerPointerChecker]) + if (!ChecksEnabled[CK_MallocChecker] && !ChecksEnabled[CK_NewDeleteChecker] && + !ChecksEnabled[CK_InnerPointerChecker]) { + C.addSink(); return; + } Optional CheckKind = getCheckIfTracked(C, Sym); if (!CheckKind.hasValue()) @@ -2232,13 +2252,14 @@ void MallocChecker::ReportUseAfterFree(CheckerContext &C, SourceRange Range, } } -void MallocChecker::ReportDoubleFree(CheckerContext &C, SourceRange Range, +void MallocChecker::HandleDoubleFree(CheckerContext &C, SourceRange Range, bool Released, SymbolRef Sym, SymbolRef PrevSym) const { - if (!ChecksEnabled[CK_MallocChecker] && - !ChecksEnabled[CK_NewDeleteChecker]) + if (!ChecksEnabled[CK_MallocChecker] && !ChecksEnabled[CK_NewDeleteChecker]) { + C.addSink(); return; + } Optional CheckKind = getCheckIfTracked(C, Sym); if (!CheckKind.hasValue()) @@ -2263,10 +2284,12 @@ void MallocChecker::ReportDoubleFree(CheckerContext &C, SourceRange Range, } } -void MallocChecker::ReportDoubleDelete(CheckerContext &C, SymbolRef Sym) const { +void MallocChecker::HandleDoubleDelete(CheckerContext &C, SymbolRef Sym) const { - if (!ChecksEnabled[CK_NewDeleteChecker]) + if (!ChecksEnabled[CK_NewDeleteChecker]) { + C.addSink(); return; + } Optional CheckKind = getCheckIfTracked(C, Sym); if (!CheckKind.hasValue()) @@ -2287,13 +2310,13 @@ void MallocChecker::ReportDoubleDelete(CheckerContext &C, SymbolRef Sym) const { } } -void MallocChecker::ReportUseZeroAllocated(CheckerContext &C, - SourceRange Range, - SymbolRef Sym) const { +void MallocChecker::HandleUseZeroAlloc(CheckerContext &C, SourceRange Range, + SymbolRef Sym) const { - if (!ChecksEnabled[CK_MallocChecker] && - !ChecksEnabled[CK_NewDeleteChecker]) + if (!ChecksEnabled[CK_MallocChecker] && !ChecksEnabled[CK_NewDeleteChecker]) { + C.addSink(); return; + } Optional CheckKind = getCheckIfTracked(C, Sym); @@ -2318,12 +2341,14 @@ void MallocChecker::ReportUseZeroAllocated(CheckerContext &C, } } -void MallocChecker::ReportFunctionPointerFree(CheckerContext &C, SVal ArgVal, - SourceRange Range, - const Expr *FreeExpr, - AllocationFamily Family) const { - if (!ChecksEnabled[CK_MallocChecker]) +void MallocChecker::HandleFunctionPtrFree(CheckerContext &C, SVal ArgVal, + SourceRange Range, + const Expr *FreeExpr, + AllocationFamily Family) const { + if (!ChecksEnabled[CK_MallocChecker]) { + C.addSink(); return; + } Optional CheckKind = getCheckIfTracked(Family); if (!CheckKind.hasValue()) @@ -2521,7 +2546,7 @@ MallocChecker::LeakInfo MallocChecker::getAllocationSite(const ExplodedNode *N, return LeakInfo(AllocNode, ReferenceRegion); } -void MallocChecker::reportLeak(SymbolRef Sym, ExplodedNode *N, +void MallocChecker::HandleLeak(SymbolRef Sym, ExplodedNode *N, CheckerContext &C) const { if (!ChecksEnabled[CK_MallocChecker] && @@ -2637,7 +2662,7 @@ void MallocChecker::checkDeadSymbols(SymbolReaper &SymReaper, if (N) { for (SmallVectorImpl::iterator I = Errors.begin(), E = Errors.end(); I != E; ++I) { - reportLeak(*I, N, C); + HandleLeak(*I, N, C); } } } @@ -2822,7 +2847,7 @@ bool MallocChecker::checkUseAfterFree(SymbolRef Sym, CheckerContext &C, const Stmt *S) const { if (isReleased(Sym, C)) { - ReportUseAfterFree(C, S->getSourceRange(), Sym); + HandleUseAfterFree(C, S->getSourceRange(), Sym); return true; } @@ -2835,17 +2860,17 @@ void MallocChecker::checkUseZeroAllocated(SymbolRef Sym, CheckerContext &C, if (const RefState *RS = C.getState()->get(Sym)) { if (RS->isAllocatedOfSizeZero()) - ReportUseZeroAllocated(C, RS->getStmt()->getSourceRange(), Sym); + HandleUseZeroAlloc(C, RS->getStmt()->getSourceRange(), Sym); } else if (C.getState()->contains(Sym)) { - ReportUseZeroAllocated(C, S->getSourceRange(), Sym); + HandleUseZeroAlloc(C, S->getSourceRange(), Sym); } } bool MallocChecker::checkDoubleDelete(SymbolRef Sym, CheckerContext &C) const { if (isReleased(Sym, C)) { - ReportDoubleDelete(C, Sym); + HandleDoubleDelete(C, Sym); return true; } return false; diff --git a/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.cpp index 4bf9beb365f66..3f3267ff93916 100644 --- a/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.cpp @@ -12,12 +12,12 @@ //===----------------------------------------------------------------------===// #include "RetainCountChecker.h" +#include "clang/StaticAnalyzer/Core/Checker.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" using namespace clang; using namespace ento; using namespace retaincountchecker; -using llvm::StrInStrNoCase; REGISTER_MAP_WITH_PROGRAMSTATE(RefBindings, SymbolRef, RefVal) @@ -701,7 +701,7 @@ void RetainCountChecker::checkSummary(const RetainSummary &Summ, for (ProgramStateRef St : Out) { if (DeallocSent) { - C.addTransition(St, C.getPredecessor(), &DeallocSentTag); + C.addTransition(St, C.getPredecessor(), &getDeallocSentTag()); } else { C.addTransition(St); } @@ -844,13 +844,13 @@ RetainCountChecker::errorKindToBugKind(RefVal::Kind ErrorKind, SymbolRef Sym) const { switch (ErrorKind) { case RefVal::ErrorUseAfterRelease: - return useAfterRelease; + return *UseAfterRelease; case RefVal::ErrorReleaseNotOwned: - return releaseNotOwned; + return *ReleaseNotOwned; case RefVal::ErrorDeallocNotOwned: if (Sym->getType()->getPointeeCXXRecordDecl()) - return freeNotOwned; - return deallocNotOwned; + return *FreeNotOwned; + return *DeallocNotOwned; default: llvm_unreachable("Unhandled error."); } @@ -946,7 +946,7 @@ bool RetainCountChecker::evalCall(const CallEvent &Call, // Assume that output is zero on the other branch. NullOutputState = NullOutputState->BindExpr( CE, LCtx, C.getSValBuilder().makeNull(), /*Invalidate=*/false); - C.addTransition(NullOutputState, &CastFailTag); + C.addTransition(NullOutputState, &getCastFailTag()); // And on the original branch assume that both input and // output are non-zero. @@ -1095,7 +1095,7 @@ ExplodedNode * RetainCountChecker::checkReturnWithRetEffect(const ReturnStmt *S, if (N) { const LangOptions &LOpts = C.getASTContext().getLangOpts(); auto R = - std::make_unique(leakAtReturn, LOpts, N, Sym, C); + std::make_unique(*LeakAtReturn, LOpts, N, Sym, C); C.emitReport(std::move(R)); } return N; @@ -1120,7 +1120,7 @@ ExplodedNode * RetainCountChecker::checkReturnWithRetEffect(const ReturnStmt *S, ExplodedNode *N = C.addTransition(state, Pred, &ReturnNotOwnedTag); if (N) { auto R = std::make_unique( - returnNotOwnedForOwned, C.getASTContext().getLangOpts(), N, Sym); + *ReturnNotOwnedForOwned, C.getASTContext().getLangOpts(), N, Sym); C.emitReport(std::move(R)); } return N; @@ -1273,8 +1273,8 @@ RetainCountChecker::handleAutoreleaseCounts(ProgramStateRef state, os << "has a +" << V.getCount() << " retain count"; const LangOptions &LOpts = Ctx.getASTContext().getLangOpts(); - auto R = std::make_unique(overAutorelease, LOpts, N, Sym, - os.str()); + auto R = std::make_unique(*OverAutorelease, LOpts, N, Sym, + os.str()); Ctx.emitReport(std::move(R)); } @@ -1320,7 +1320,7 @@ RetainCountChecker::processLeaks(ProgramStateRef state, if (N) { for (SymbolRef L : Leaked) { - const RefCountBug &BT = Pred ? leakWithinFunction : leakAtReturn; + const RefCountBug &BT = Pred ? *LeakWithinFunction : *LeakAtReturn; Ctx.emitReport(std::make_unique(BT, LOpts, N, L, Ctx)); } } @@ -1473,34 +1473,39 @@ void RetainCountChecker::printState(raw_ostream &Out, ProgramStateRef State, // Checker registration. //===----------------------------------------------------------------------===// +std::unique_ptr RetainCountChecker::DeallocSentTag; +std::unique_ptr RetainCountChecker::CastFailTag; + void ento::registerRetainCountBase(CheckerManager &Mgr) { - Mgr.registerChecker(); + auto *Chk = Mgr.registerChecker(); + Chk->DeallocSentTag = + std::make_unique(Chk, "DeallocSent"); + Chk->CastFailTag = + std::make_unique(Chk, "DynamicCastFail"); } bool ento::shouldRegisterRetainCountBase(const CheckerManager &mgr) { return true; } - -// FIXME: remove this, hack for backwards compatibility: -// it should be possible to enable the NS/CF retain count checker as -// osx.cocoa.RetainCount, and it should be possible to disable -// osx.OSObjectRetainCount using osx.cocoa.RetainCount:CheckOSObject=false. -static bool getOption(const AnalyzerOptions &Options, - StringRef Postfix, - StringRef Value) { - auto I = Options.Config.find( - (StringRef("osx.cocoa.RetainCount:") + Postfix).str()); - if (I != Options.Config.end()) - return I->getValue() == Value; - return false; -} - void ento::registerRetainCountChecker(CheckerManager &Mgr) { auto *Chk = Mgr.getChecker(); Chk->TrackObjCAndCFObjects = true; - Chk->TrackNSCFStartParam = getOption(Mgr.getAnalyzerOptions(), - "TrackNSCFStartParam", - "true"); + Chk->TrackNSCFStartParam = Mgr.getAnalyzerOptions().getCheckerBooleanOption( + Mgr.getCurrentCheckerName(), "TrackNSCFStartParam"); + +#define INIT_BUGTYPE(KIND) \ + Chk->KIND = std::make_unique(Mgr.getCurrentCheckerName(), \ + RefCountBug::KIND); + // TODO: Ideally, we should have a checker for each of these bug types. + INIT_BUGTYPE(UseAfterRelease) + INIT_BUGTYPE(ReleaseNotOwned) + INIT_BUGTYPE(DeallocNotOwned) + INIT_BUGTYPE(FreeNotOwned) + INIT_BUGTYPE(OverAutorelease) + INIT_BUGTYPE(ReturnNotOwnedForOwned) + INIT_BUGTYPE(LeakWithinFunction) + INIT_BUGTYPE(LeakAtReturn) +#undef INIT_BUGTYPE } bool ento::shouldRegisterRetainCountChecker(const CheckerManager &mgr) { @@ -1509,10 +1514,30 @@ bool ento::shouldRegisterRetainCountChecker(const CheckerManager &mgr) { void ento::registerOSObjectRetainCountChecker(CheckerManager &Mgr) { auto *Chk = Mgr.getChecker(); - if (!getOption(Mgr.getAnalyzerOptions(), - "CheckOSObject", - "false")) - Chk->TrackOSObjects = true; + Chk->TrackOSObjects = true; + + // FIXME: We want bug reports to always have the same checker name associated + // with them, yet here, if RetainCountChecker is disabled but + // OSObjectRetainCountChecker is enabled, the checker names will be different. + // This hack will make it so that the checker name depends on which checker is + // enabled rather than on the registration order. + // For the most part, we want **non-hidden checkers** to be associated with + // diagnostics, and **hidden checker options** with the fine-tuning of + // modeling. Following this logic, OSObjectRetainCountChecker should be the + // latter, but we can't just remove it for backward compatibility reasons. +#define LAZY_INIT_BUGTYPE(KIND) \ + if (!Chk->KIND) \ + Chk->KIND = std::make_unique(Mgr.getCurrentCheckerName(), \ + RefCountBug::KIND); + LAZY_INIT_BUGTYPE(UseAfterRelease) + LAZY_INIT_BUGTYPE(ReleaseNotOwned) + LAZY_INIT_BUGTYPE(DeallocNotOwned) + LAZY_INIT_BUGTYPE(FreeNotOwned) + LAZY_INIT_BUGTYPE(OverAutorelease) + LAZY_INIT_BUGTYPE(ReturnNotOwnedForOwned) + LAZY_INIT_BUGTYPE(LeakWithinFunction) + LAZY_INIT_BUGTYPE(LeakAtReturn) +#undef LAZY_INIT_BUGTYPE } bool ento::shouldRegisterOSObjectRetainCountChecker(const CheckerManager &mgr) { diff --git a/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.h b/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.h index dd79bbef321c3..223e28c2c5b86 100644 --- a/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.h +++ b/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.h @@ -251,20 +251,20 @@ class RetainCountChecker eval::Assume, eval::Call > { - RefCountBug useAfterRelease{this, RefCountBug::UseAfterRelease}; - RefCountBug releaseNotOwned{this, RefCountBug::ReleaseNotOwned}; - RefCountBug deallocNotOwned{this, RefCountBug::DeallocNotOwned}; - RefCountBug freeNotOwned{this, RefCountBug::FreeNotOwned}; - RefCountBug overAutorelease{this, RefCountBug::OverAutorelease}; - RefCountBug returnNotOwnedForOwned{this, RefCountBug::ReturnNotOwnedForOwned}; - RefCountBug leakWithinFunction{this, RefCountBug::LeakWithinFunction}; - RefCountBug leakAtReturn{this, RefCountBug::LeakAtReturn}; - - CheckerProgramPointTag DeallocSentTag{this, "DeallocSent"}; - CheckerProgramPointTag CastFailTag{this, "DynamicCastFail"}; +public: + std::unique_ptr UseAfterRelease; + std::unique_ptr ReleaseNotOwned; + std::unique_ptr DeallocNotOwned; + std::unique_ptr FreeNotOwned; + std::unique_ptr OverAutorelease; + std::unique_ptr ReturnNotOwnedForOwned; + std::unique_ptr LeakWithinFunction; + std::unique_ptr LeakAtReturn; mutable std::unique_ptr Summaries; -public: + + static std::unique_ptr DeallocSentTag; + static std::unique_ptr CastFailTag; /// Track Objective-C and CoreFoundation objects. bool TrackObjCAndCFObjects = false; @@ -360,13 +360,11 @@ class RetainCountChecker CheckerContext &Ctx, ExplodedNode *Pred = nullptr) const; - const CheckerProgramPointTag &getDeallocSentTag() const { - return DeallocSentTag; + static const CheckerProgramPointTag &getDeallocSentTag() { + return *DeallocSentTag; } - const CheckerProgramPointTag &getCastFailTag() const { - return CastFailTag; - } + static const CheckerProgramPointTag &getCastFailTag() { return *CastFailTag; } private: /// Perform the necessary checks and state adjustments at the end of the diff --git a/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp b/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp index cfad47626354a..1d8ed90f7590c 100644 --- a/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp @@ -18,7 +18,7 @@ using namespace clang; using namespace ento; using namespace retaincountchecker; -StringRef RefCountBug::bugTypeToName(RefCountBug::RefCountBugType BT) { +StringRef RefCountBug::bugTypeToName(RefCountBug::RefCountBugKind BT) { switch (BT) { case UseAfterRelease: return "Use-after-release"; @@ -37,7 +37,7 @@ StringRef RefCountBug::bugTypeToName(RefCountBug::RefCountBugType BT) { case LeakAtReturn: return "Leak of returned object"; } - llvm_unreachable("Unknown RefCountBugType"); + llvm_unreachable("Unknown RefCountBugKind"); } StringRef RefCountBug::getDescription() const { @@ -60,13 +60,14 @@ StringRef RefCountBug::getDescription() const { case LeakAtReturn: return ""; } - llvm_unreachable("Unknown RefCountBugType"); + llvm_unreachable("Unknown RefCountBugKind"); } -RefCountBug::RefCountBug(const CheckerBase *Checker, RefCountBugType BT) +RefCountBug::RefCountBug(CheckerNameRef Checker, RefCountBugKind BT) : BugType(Checker, bugTypeToName(BT), categories::MemoryRefCount, - /*SuppressOnSink=*/BT == LeakWithinFunction || BT == LeakAtReturn), - BT(BT), Checker(Checker) {} + /*SuppressOnSink=*/BT == LeakWithinFunction || + BT == LeakAtReturn), + BT(BT) {} static bool isNumericLiteralExpression(const Expr *E) { // FIXME: This set of cases was copied from SemaExprObjC. @@ -453,8 +454,6 @@ RefCountReportVisitor::VisitNode(const ExplodedNode *N, BugReporterContext &BRC, PathSensitiveBugReport &BR) { const auto &BT = static_cast(BR.getBugType()); - const auto *Checker = - static_cast(BT.getChecker()); bool IsFreeUnowned = BT.getBugType() == RefCountBug::FreeNotOwned || BT.getBugType() == RefCountBug::DeallocNotOwned; @@ -545,11 +544,11 @@ RefCountReportVisitor::VisitNode(const ExplodedNode *N, BugReporterContext &BRC, const ProgramPointTag *Tag = N->getLocation().getTag(); - if (Tag == &Checker->getCastFailTag()) { + if (Tag == &RetainCountChecker::getCastFailTag()) { os << "Assuming dynamic cast returns null due to type mismatch"; } - if (Tag == &Checker->getDeallocSentTag()) { + if (Tag == &RetainCountChecker::getDeallocSentTag()) { // We only have summaries attached to nodes after evaluating CallExpr and // ObjCMessageExprs. const Stmt *S = N->getLocation().castAs().getStmt(); diff --git a/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.h b/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.h index e9e2777540548..286a8ae2ef7d7 100644 --- a/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.h +++ b/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.h @@ -26,7 +26,7 @@ namespace retaincountchecker { class RefCountBug : public BugType { public: - enum RefCountBugType { + enum RefCountBugKind { UseAfterRelease, ReleaseNotOwned, DeallocNotOwned, @@ -36,21 +36,14 @@ class RefCountBug : public BugType { LeakWithinFunction, LeakAtReturn, }; - RefCountBug(const CheckerBase *checker, RefCountBugType BT); + RefCountBug(CheckerNameRef Checker, RefCountBugKind BT); StringRef getDescription() const; - RefCountBugType getBugType() const { - return BT; - } - - const CheckerBase *getChecker() const { - return Checker; - } + RefCountBugKind getBugType() const { return BT; } private: - RefCountBugType BT; - const CheckerBase *Checker; - static StringRef bugTypeToName(RefCountBugType BT); + RefCountBugKind BT; + static StringRef bugTypeToName(RefCountBugKind BT); }; class RefCountReport : public PathSensitiveBugReport { diff --git a/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp index aefcad3745968..6feae56502f1f 100644 --- a/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp @@ -56,6 +56,7 @@ #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerHelpers.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicSize.h" using namespace clang; using namespace clang::ento; @@ -63,10 +64,8 @@ using namespace clang::ento; namespace { class StdLibraryFunctionsChecker : public Checker { - /// Below is a series of typedefs necessary to define function specs. - /// We avoid nesting types here because each additional qualifier - /// would need to be repeated in every function spec. - struct Summary; + + class Summary; /// Specify how much the analyzer engine should entrust modeling this function /// to us. If he doesn't, he performs additional invalidations. @@ -108,14 +107,32 @@ class StdLibraryFunctionsChecker /// Apply the effects of the constraint on the given program state. If null /// is returned then the constraint is not feasible. virtual ProgramStateRef apply(ProgramStateRef State, const CallEvent &Call, - const Summary &Summary) const = 0; + const Summary &Summary, + CheckerContext &C) const = 0; virtual ValueConstraintPtr negate() const { llvm_unreachable("Not implemented"); }; + + // Check whether the constraint is malformed or not. It is malformed if the + // specified argument has a mismatch with the given FunctionDecl (e.g. the + // arg number is out-of-range of the function's argument list). + bool checkValidity(const FunctionDecl *FD) const { + const bool ValidArg = ArgN == Ret || ArgN < FD->getNumParams(); + assert(ValidArg && "Arg out of range!"); + if (!ValidArg) + return false; + // Subclasses may further refine the validation. + return checkSpecificValidity(FD); + } ArgNo getArgNo() const { return ArgN; } protected: ArgNo ArgN; // Argument to which we apply the constraint. + + /// Do polymorphic sanity check on the constraint. + virtual bool checkSpecificValidity(const FunctionDecl *FD) const { + return true; + } }; /// Given a range, should the argument stay inside or outside this range? @@ -143,7 +160,8 @@ class StdLibraryFunctionsChecker const Summary &Summary) const; public: ProgramStateRef apply(ProgramStateRef State, const CallEvent &Call, - const Summary &Summary) const override { + const Summary &Summary, + CheckerContext &C) const override { switch (Kind) { case OutOfRange: return applyAsOutOfRange(State, Call, Summary); @@ -165,6 +183,14 @@ class StdLibraryFunctionsChecker } return std::make_shared(Tmp); } + + bool checkSpecificValidity(const FunctionDecl *FD) const override { + const bool ValidArg = + getArgType(FD, ArgN)->isIntegralType(FD->getASTContext()); + assert(ValidArg && + "This constraint should be applied on an integral type"); + return ValidArg; + } }; class ComparisonConstraint : public ValueConstraint { @@ -178,7 +204,8 @@ class StdLibraryFunctionsChecker ArgNo getOtherArgNo() const { return OtherArgN; } BinaryOperator::Opcode getOpcode() const { return Opcode; } ProgramStateRef apply(ProgramStateRef State, const CallEvent &Call, - const Summary &Summary) const override; + const Summary &Summary, + CheckerContext &C) const override; }; class NotNullConstraint : public ValueConstraint { @@ -188,7 +215,8 @@ class StdLibraryFunctionsChecker public: ProgramStateRef apply(ProgramStateRef State, const CallEvent &Call, - const Summary &Summary) const override { + const Summary &Summary, + CheckerContext &C) const override { SVal V = getArgSVal(Call, getArgNo()); if (V.isUndef()) return State; @@ -205,17 +233,127 @@ class StdLibraryFunctionsChecker Tmp.CannotBeNull = !this->CannotBeNull; return std::make_shared(Tmp); } + + bool checkSpecificValidity(const FunctionDecl *FD) const override { + const bool ValidArg = getArgType(FD, ArgN)->isPointerType(); + assert(ValidArg && + "This constraint should be applied only on a pointer type"); + return ValidArg; + } + }; + + // Represents a buffer argument with an additional size argument. + // E.g. the first two arguments here: + // ctime_s(char *buffer, rsize_t bufsz, const time_t *time); + // Another example: + // size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream); + // // Here, ptr is the buffer, and its minimum size is `size * nmemb`. + class BufferSizeConstraint : public ValueConstraint { + // The argument which holds the size of the buffer. + ArgNo SizeArgN; + // The argument which is a multiplier to size. This is set in case of + // `fread` like functions where the size is computed as a multiplication of + // two arguments. + llvm::Optional SizeMultiplierArgN; + // The operator we use in apply. This is negated in negate(). + BinaryOperator::Opcode Op = BO_LE; + + public: + BufferSizeConstraint(ArgNo Buffer, ArgNo BufSize) + : ValueConstraint(Buffer), SizeArgN(BufSize) {} + + BufferSizeConstraint(ArgNo Buffer, ArgNo BufSize, ArgNo BufSizeMultiplier) + : ValueConstraint(Buffer), SizeArgN(BufSize), + SizeMultiplierArgN(BufSizeMultiplier) {} + + ProgramStateRef apply(ProgramStateRef State, const CallEvent &Call, + const Summary &Summary, + CheckerContext &C) const override { + SValBuilder &SvalBuilder = C.getSValBuilder(); + // The buffer argument. + SVal BufV = getArgSVal(Call, getArgNo()); + // The size argument. + SVal SizeV = getArgSVal(Call, SizeArgN); + // Multiply with another argument if given. + if (SizeMultiplierArgN) { + SVal SizeMulV = getArgSVal(Call, *SizeMultiplierArgN); + SizeV = SvalBuilder.evalBinOp(State, BO_Mul, SizeV, SizeMulV, + Summary.getArgType(SizeArgN)); + } + // The dynamic size of the buffer argument, got from the analyzer engine. + SVal BufDynSize = getDynamicSizeWithOffset(State, BufV); + + SVal Feasible = SvalBuilder.evalBinOp(State, Op, SizeV, BufDynSize, + SvalBuilder.getContext().BoolTy); + if (auto F = Feasible.getAs()) + return State->assume(*F, true); + + // We can get here only if the size argument or the dynamic size is + // undefined. But the dynamic size should never be undefined, only + // unknown. So, here, the size of the argument is undefined, i.e. we + // cannot apply the constraint. Actually, other checkers like + // CallAndMessage should catch this situation earlier, because we call a + // function with an uninitialized argument. + llvm_unreachable("Size argument or the dynamic size is Undefined"); + } + + ValueConstraintPtr negate() const override { + BufferSizeConstraint Tmp(*this); + Tmp.Op = BinaryOperator::negateComparisonOp(Op); + return std::make_shared(Tmp); + } }; /// The complete list of constraints that defines a single branch. typedef std::vector ConstraintSet; using ArgTypes = std::vector; + + // A placeholder type, we use it whenever we do not care about the concrete + // type in a Signature. + const QualType Irrelevant{}; + bool static isIrrelevant(QualType T) { return T.isNull(); } + + // The signature of a function we want to describe with a summary. This is a + // concessive signature, meaning there may be irrelevant types in the + // signature which we do not check against a function with concrete types. + struct Signature { + const ArgTypes ArgTys; + const QualType RetTy; + Signature(ArgTypes ArgTys, QualType RetTy) : ArgTys(ArgTys), RetTy(RetTy) { + assertRetTypeSuitableForSignature(RetTy); + for (size_t I = 0, E = ArgTys.size(); I != E; ++I) { + QualType ArgTy = ArgTys[I]; + assertArgTypeSuitableForSignature(ArgTy); + } + } + bool matches(const FunctionDecl *FD) const; + + private: + static void assertArgTypeSuitableForSignature(QualType T) { + assert((T.isNull() || !T->isVoidType()) && + "We should have no void types in the spec"); + assert((T.isNull() || T.isCanonical()) && + "We should only have canonical types in the spec"); + } + static void assertRetTypeSuitableForSignature(QualType T) { + assert((T.isNull() || T.isCanonical()) && + "We should only have canonical types in the spec"); + } + }; + + static QualType getArgType(const FunctionDecl *FD, ArgNo ArgN) { + assert(FD && "Function must be set"); + QualType T = (ArgN == Ret) + ? FD->getReturnType().getCanonicalType() + : FD->getParamDecl(ArgN)->getType().getCanonicalType(); + return T; + } + using Cases = std::vector; - /// Includes information about - /// * function prototype (which is necessary to - /// ensure we're modeling the right function and casting values properly), + /// A summary includes information about + /// * function prototype (signature) /// * approach to invalidation, /// * a list of branches - a list of list of ranges - /// A branch represents a path in the exploded graph of a function (which @@ -232,15 +370,28 @@ class StdLibraryFunctionsChecker /// * a list of argument constraints, that must be true on every branch. /// If these constraints are not satisfied that means a fatal error /// usually resulting in undefined behaviour. - struct Summary { - const ArgTypes ArgTys; - const QualType RetTy; + /// + /// Application of a summary: + /// The signature and argument constraints together contain information + /// about which functions are handled by the summary. The signature can use + /// "wildcards", i.e. Irrelevant types. Irrelevant type of a parameter in + /// a signature means that type is not compared to the type of the parameter + /// in the found FunctionDecl. Argument constraints may specify additional + /// rules for the given parameter's type, those rules are checked once the + /// signature is matched. + class Summary { + const Signature Sign; const InvalidationKind InvalidationKd; Cases CaseConstraints; ConstraintSet ArgConstraints; + // The function to which the summary applies. This is set after lookup and + // match to the signature. + const FunctionDecl *FD = nullptr; + + public: Summary(ArgTypes ArgTys, QualType RetTy, InvalidationKind InvalidationKd) - : ArgTys(ArgTys), RetTy(RetTy), InvalidationKd(InvalidationKd) {} + : Sign(ArgTys, RetTy), InvalidationKd(InvalidationKd) {} Summary &Case(ConstraintSet&& CS) { CaseConstraints.push_back(std::move(CS)); @@ -251,24 +402,38 @@ class StdLibraryFunctionsChecker return *this; } - private: - static void assertTypeSuitableForSummary(QualType T) { - assert(!T->isVoidType() && - "We should have had no significant void types in the spec"); - assert(T.isCanonical() && - "We should only have canonical types in the spec"); - } + InvalidationKind getInvalidationKd() const { return InvalidationKd; } + const Cases &getCaseConstraints() const { return CaseConstraints; } + const ConstraintSet &getArgConstraints() const { return ArgConstraints; } - public: QualType getArgType(ArgNo ArgN) const { - QualType T = (ArgN == Ret) ? RetTy : ArgTys[ArgN]; - assertTypeSuitableForSummary(T); - return T; + return StdLibraryFunctionsChecker::getArgType(FD, ArgN); } - /// Try our best to figure out if the summary's signature matches - /// *the* library function to which this specification applies. - bool matchesSignature(const FunctionDecl *FD) const; + // Returns true if the summary should be applied to the given function. + // And if yes then store the function declaration. + bool matchesAndSet(const FunctionDecl *FD) { + bool Result = Sign.matches(FD) && validateByConstraints(FD); + if (Result) { + assert(!this->FD && "FD must not be set more than once"); + this->FD = FD; + } + return Result; + } + + private: + // Once we know the exact type of the function then do sanity check on all + // the given constraints. + bool validateByConstraints(const FunctionDecl *FD) const { + for (const ConstraintSet &Case : CaseConstraints) + for (const ValueConstraintPtr &Constraint : Case) + if (!Constraint->checkValidity(FD)) + return false; + for (const ValueConstraintPtr &Constraint : ArgConstraints) + if (!Constraint->checkValidity(FD)) + return false; + return true; + } }; // The map of all functions supported by the checker. It is initialized @@ -278,11 +443,6 @@ class StdLibraryFunctionsChecker mutable std::unique_ptr BT_InvalidArg; - // Auxiliary functions to support ArgNo within all structures - // in a unified manner. - static QualType getArgType(const Summary &Summary, ArgNo ArgN) { - return Summary.getArgType(ArgN); - } static SVal getArgSVal(const CallEvent &Call, ArgNo ArgN) { return ArgN == Ret ? Call.getReturnValue() : Call.getArgSVal(ArgN); } @@ -339,7 +499,7 @@ ProgramStateRef StdLibraryFunctionsChecker::RangeConstraint::applyAsOutOfRange( SValBuilder &SVB = Mgr.getSValBuilder(); BasicValueFactory &BVF = SVB.getBasicValueFactory(); ConstraintManager &CM = Mgr.getConstraintManager(); - QualType T = getArgType(Summary, getArgNo()); + QualType T = Summary.getArgType(getArgNo()); SVal V = getArgSVal(Call, getArgNo()); if (auto N = V.getAs()) { @@ -366,7 +526,7 @@ ProgramStateRef StdLibraryFunctionsChecker::RangeConstraint::applyAsWithinRange( SValBuilder &SVB = Mgr.getSValBuilder(); BasicValueFactory &BVF = SVB.getBasicValueFactory(); ConstraintManager &CM = Mgr.getConstraintManager(); - QualType T = getArgType(Summary, getArgNo()); + QualType T = Summary.getArgType(getArgNo()); SVal V = getArgSVal(Call, getArgNo()); // "WithinRange R" is treated as "outside [T_MIN, T_MAX] \ R". @@ -416,19 +576,19 @@ ProgramStateRef StdLibraryFunctionsChecker::RangeConstraint::applyAsWithinRange( } ProgramStateRef StdLibraryFunctionsChecker::ComparisonConstraint::apply( - ProgramStateRef State, const CallEvent &Call, - const Summary &Summary) const { + ProgramStateRef State, const CallEvent &Call, const Summary &Summary, + CheckerContext &C) const { ProgramStateManager &Mgr = State->getStateManager(); SValBuilder &SVB = Mgr.getSValBuilder(); QualType CondT = SVB.getConditionType(); - QualType T = getArgType(Summary, getArgNo()); + QualType T = Summary.getArgType(getArgNo()); SVal V = getArgSVal(Call, getArgNo()); BinaryOperator::Opcode Op = getOpcode(); ArgNo OtherArg = getOtherArgNo(); SVal OtherV = getArgSVal(Call, OtherArg); - QualType OtherT = getArgType(Summary, OtherArg); + QualType OtherT = Summary.getArgType(OtherArg); // Note: we avoid integral promotion for comparison. OtherV = SVB.evalCast(OtherV, T, OtherT); if (auto CompV = SVB.evalBinOp(State, Op, V, OtherV, CondT) @@ -447,9 +607,10 @@ void StdLibraryFunctionsChecker::checkPreCall(const CallEvent &Call, ProgramStateRef State = C.getState(); ProgramStateRef NewState = State; - for (const ValueConstraintPtr& VC : Summary.ArgConstraints) { - ProgramStateRef SuccessSt = VC->apply(NewState, Call, Summary); - ProgramStateRef FailureSt = VC->negate()->apply(NewState, Call, Summary); + for (const ValueConstraintPtr &Constraint : Summary.getArgConstraints()) { + ProgramStateRef SuccessSt = Constraint->apply(NewState, Call, Summary, C); + ProgramStateRef FailureSt = + Constraint->negate()->apply(NewState, Call, Summary, C); // The argument constraint is not satisfied. if (FailureSt && !SuccessSt) { if (ExplodedNode *N = C.generateErrorNode(NewState)) @@ -479,10 +640,10 @@ void StdLibraryFunctionsChecker::checkPostCall(const CallEvent &Call, ProgramStateRef State = C.getState(); // Apply case/branch specifications. - for (const auto &VRS : Summary.CaseConstraints) { + for (const ConstraintSet &Case : Summary.getCaseConstraints()) { ProgramStateRef NewState = State; - for (const auto &VR: VRS) { - NewState = VR->apply(NewState, Call, Summary); + for (const ValueConstraintPtr &Constraint : Case) { + NewState = Constraint->apply(NewState, Call, Summary, C); if (!NewState) break; } @@ -499,7 +660,7 @@ bool StdLibraryFunctionsChecker::evalCall(const CallEvent &Call, return false; const Summary &Summary = *FoundSummary; - switch (Summary.InvalidationKd) { + switch (Summary.getInvalidationKd()) { case EvalCallAsPure: { ProgramStateRef State = C.getState(); const LocationContext *LC = C.getLocationContext(); @@ -518,27 +679,23 @@ bool StdLibraryFunctionsChecker::evalCall(const CallEvent &Call, llvm_unreachable("Unknown invalidation kind!"); } -bool StdLibraryFunctionsChecker::Summary::matchesSignature( +bool StdLibraryFunctionsChecker::Signature::matches( const FunctionDecl *FD) const { // Check number of arguments: if (FD->param_size() != ArgTys.size()) return false; - // Check return type if relevant: - if (!RetTy.isNull() && RetTy != FD->getReturnType().getCanonicalType()) - return false; + // Check return type. + if (!isIrrelevant(RetTy)) + if (RetTy != FD->getReturnType().getCanonicalType()) + return false; - // Check argument types when relevant: + // Check argument types. for (size_t I = 0, E = ArgTys.size(); I != E; ++I) { - QualType FormalT = ArgTys[I]; - // Null type marks irrelevant arguments. - if (FormalT.isNull()) + QualType ArgTy = ArgTys[I]; + if (isIrrelevant(ArgTy)) continue; - - assertTypeSuitableForSummary(FormalT); - - QualType ActualT = FD->getParamDecl(I)->getType().getCanonicalType(); - if (ActualT != FormalT) + if (ArgTy != FD->getParamDecl(I)->getType().getCanonicalType()) return false; } @@ -568,6 +725,26 @@ StdLibraryFunctionsChecker::findFunctionSummary(const CallEvent &Call, return findFunctionSummary(FD, C); } +llvm::Optional lookupType(StringRef Name, const ASTContext &ACtx) { + IdentifierInfo &II = ACtx.Idents.get(Name); + auto LookupRes = ACtx.getTranslationUnitDecl()->lookup(&II); + if (LookupRes.size() == 0) + return None; + + // Prioritze typedef declarations. + // This is needed in case of C struct typedefs. E.g.: + // typedef struct FILE FILE; + // In this case, we have a RecordDecl 'struct FILE' with the name 'FILE' and + // we have a TypedefDecl with the name 'FILE'. + for (Decl *D : LookupRes) { + if (auto *TD = dyn_cast(D)) + return ACtx.getTypeDeclType(TD).getCanonicalType(); + } + assert(LookupRes.size() == 1 && "Type identifier should be unique"); + auto *D = cast(LookupRes.front()); + return ACtx.getTypeDeclType(D).getCanonicalType(); +} + void StdLibraryFunctionsChecker::initFunctionSummaries( CheckerContext &C) const { if (!FunctionSummaryMap.empty()) @@ -584,21 +761,22 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( // of function summary for common cases (eg. ssize_t could be int or long // or long long, so three summary variants would be enough). // Of course, function variants are also useful for C++ overloads. - const QualType - Irrelevant{}; // A placeholder, whenever we do not care about the type. const QualType IntTy = ACtx.IntTy; const QualType LongTy = ACtx.LongTy; const QualType LongLongTy = ACtx.LongLongTy; const QualType SizeTy = ACtx.getSizeType(); const QualType VoidPtrTy = ACtx.VoidPtrTy; // void * const QualType VoidPtrRestrictTy = - ACtx.getRestrictType(VoidPtrTy); // void *restrict + ACtx.getLangOpts().C99 ? ACtx.getRestrictType(VoidPtrTy) // void *restrict + : VoidPtrTy; const QualType ConstVoidPtrTy = ACtx.getPointerType(ACtx.VoidTy.withConst()); // const void * const QualType ConstCharPtrTy = ACtx.getPointerType(ACtx.CharTy.withConst()); // const char * const QualType ConstVoidPtrRestrictTy = - ACtx.getRestrictType(ConstVoidPtrTy); // const void *restrict + ACtx.getLangOpts().C99 + ? ACtx.getRestrictType(ConstVoidPtrTy) // const void *restrict + : ConstVoidPtrTy; const RangeInt IntMax = BVF.getMaxValue(IntTy).getLimitedValue(); const RangeInt LongMax = BVF.getMaxValue(LongTy).getLimitedValue(); @@ -635,14 +813,14 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( // Add a summary to a FunctionDecl found by lookup. The lookup is performed // by the given Name, and in the global scope. The summary will be attached // to the found FunctionDecl only if the signatures match. - void operator()(StringRef Name, const Summary &S) { + void operator()(StringRef Name, Summary S) { IdentifierInfo &II = ACtx.Idents.get(Name); auto LookupRes = ACtx.getTranslationUnitDecl()->lookup(&II); if (LookupRes.size() == 0) return; for (Decl *D : LookupRes) { if (auto *FD = dyn_cast(D)) { - if (S.matchesSignature(FD)) { + if (S.matchesAndSet(FD)) { auto Res = Map.insert({FD->getCanonicalDecl(), S}); assert(Res.second && "Function already has a summary set!"); (void)Res; @@ -694,6 +872,9 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( IntRangeVector Ranges) { return std::make_shared(ArgN, Kind, Ranges); }; + auto BufferSize = [](auto... Args) { + return std::make_shared(Args...); + }; struct { auto operator()(RangeKind Kind, IntRangeVector Ranges) { return std::make_shared(Ret, Kind, Ranges); @@ -713,10 +894,20 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( return std::make_shared(ArgN); }; + Optional FileTy = lookupType("FILE", ACtx); + Optional FilePtrTy, FilePtrRestrictTy; + if (FileTy) { + // FILE * + FilePtrTy = ACtx.getPointerType(*FileTy); + // FILE *restrict + FilePtrRestrictTy = + ACtx.getLangOpts().C99 ? ACtx.getRestrictType(*FilePtrTy) : *FilePtrTy; + } + using RetType = QualType; // Templates for summaries that are reused by many functions. auto Getc = [&]() { - return Summary(ArgTypes{Irrelevant}, RetType{IntTy}, NoEvalCall) + return Summary(ArgTypes{*FilePtrTy}, RetType{IntTy}, NoEvalCall) .Case({ReturnValueCondition(WithinRange, {{EOFv, EOFv}, {0, UCharRangeMax}})}); }; @@ -727,17 +918,18 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( ReturnValueCondition(WithinRange, Range(-1, Max))}); }; auto Fread = [&]() { - return Summary(ArgTypes{VoidPtrRestrictTy, Irrelevant, SizeTy, Irrelevant}, - RetType{SizeTy}, NoEvalCall) + return Summary( + ArgTypes{VoidPtrRestrictTy, SizeTy, SizeTy, *FilePtrRestrictTy}, + RetType{SizeTy}, NoEvalCall) .Case({ ReturnValueCondition(LessThanOrEq, ArgNo(2)), }) .ArgConstraint(NotNull(ArgNo(0))); }; auto Fwrite = [&]() { - return Summary( - ArgTypes{ConstVoidPtrRestrictTy, Irrelevant, SizeTy, Irrelevant}, - RetType{SizeTy}, NoEvalCall) + return Summary(ArgTypes{ConstVoidPtrRestrictTy, SizeTy, SizeTy, + *FilePtrRestrictTy}, + RetType{SizeTy}, NoEvalCall) .Case({ ReturnValueCondition(LessThanOrEq, ArgNo(2)), }) @@ -884,23 +1076,33 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( ReturnValueCondition(WithinRange, SingleValue(0))})); // The getc() family of functions that returns either a char or an EOF. - addToFunctionSummaryMap("getc", Getc()); - addToFunctionSummaryMap("fgetc", Getc()); + if (FilePtrTy) { + addToFunctionSummaryMap("getc", Getc()); + addToFunctionSummaryMap("fgetc", Getc()); + } addToFunctionSummaryMap( "getchar", Summary(ArgTypes{}, RetType{IntTy}, NoEvalCall) .Case({ReturnValueCondition( WithinRange, {{EOFv, EOFv}, {0, UCharRangeMax}})})); // read()-like functions that never return more than buffer size. + if (FilePtrRestrictTy) { + addToFunctionSummaryMap("fread", Fread()); + addToFunctionSummaryMap("fwrite", Fwrite()); + } + // We are not sure how ssize_t is defined on every platform, so we // provide three variants that should cover common cases. + // FIXME these are actually defined by POSIX and not by the C standard, we + // should handle them together with the rest of the POSIX functions. addToFunctionSummaryMap("read", {Read(IntTy, IntMax), Read(LongTy, LongMax), Read(LongLongTy, LongLongMax)}); addToFunctionSummaryMap("write", {Read(IntTy, IntMax), Read(LongTy, LongMax), Read(LongLongTy, LongLongMax)}); - addToFunctionSummaryMap("fread", Fread()); - addToFunctionSummaryMap("fwrite", Fwrite()); + // getline()-like functions either fail or read at least the delimiter. + // FIXME these are actually defined by POSIX and not by the C standard, we + // should handle them together with the rest of the POSIX functions. addToFunctionSummaryMap("getline", {Getline(IntTy, IntMax), Getline(LongTy, LongMax), Getline(LongLongTy, LongLongMax)}); @@ -929,6 +1131,18 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( RetType{IntTy}, EvalCallAsPure) .ArgConstraint(NotNull(ArgNo(0))) .ArgConstraint(NotNull(ArgNo(1)))); + addToFunctionSummaryMap( + "__buf_size_arg_constraint", + Summary(ArgTypes{ConstVoidPtrTy, SizeTy}, RetType{IntTy}, + EvalCallAsPure) + .ArgConstraint( + BufferSize(/*Buffer=*/ArgNo(0), /*BufSize=*/ArgNo(1)))); + addToFunctionSummaryMap( + "__buf_size_arg_constraint_mul", + Summary(ArgTypes{ConstVoidPtrTy, SizeTy, SizeTy}, RetType{IntTy}, + EvalCallAsPure) + .ArgConstraint(BufferSize(/*Buffer=*/ArgNo(0), /*BufSize=*/ArgNo(1), + /*BufSizeMultiplier=*/ArgNo(2)))); } } diff --git a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp index 2e90be4350a03..63ebfaf90dc82 100644 --- a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp @@ -92,7 +92,27 @@ struct StreamState { /// State of the error flags. /// Ignored in non-opened stream state but must be NoError. - StreamErrorState ErrorState; + StreamErrorState const ErrorState; + + /// Indicate if the file has an "indeterminate file position indicator". + /// This can be set at a failing read or write or seek operation. + /// If it is set no more read or write is allowed. + /// This value is not dependent on the stream error flags: + /// The error flag may be cleared with `clearerr` but the file position + /// remains still indeterminate. + /// This value applies to all error states in ErrorState except FEOF. + /// An EOF+indeterminate state is the same as EOF state. + bool const FilePositionIndeterminate = false; + + StreamState(const FnDescription *L, KindTy S, const StreamErrorState &ES, + bool IsFilePositionIndeterminate) + : LastOperation(L), State(S), ErrorState(ES), + FilePositionIndeterminate(IsFilePositionIndeterminate) { + assert((!ES.isFEof() || !IsFilePositionIndeterminate) && + "FilePositionIndeterminate should be false in FEof case."); + assert((State == Opened || ErrorState.isNoError()) && + "ErrorState should be None in non-opened stream state."); + } bool isOpened() const { return State == Opened; } bool isClosed() const { return State == Closed; } @@ -102,24 +122,27 @@ struct StreamState { // In not opened state error state should always NoError, so comparison // here is no problem. return LastOperation == X.LastOperation && State == X.State && - ErrorState == X.ErrorState; + ErrorState == X.ErrorState && + FilePositionIndeterminate == X.FilePositionIndeterminate; } static StreamState getOpened(const FnDescription *L, - const StreamErrorState &ES = {}) { - return StreamState{L, Opened, ES}; + const StreamErrorState &ES = ErrorNone, + bool IsFilePositionIndeterminate = false) { + return StreamState{L, Opened, ES, IsFilePositionIndeterminate}; } static StreamState getClosed(const FnDescription *L) { - return StreamState{L, Closed, {}}; + return StreamState{L, Closed, {}, false}; } static StreamState getOpenFailed(const FnDescription *L) { - return StreamState{L, OpenFailed, {}}; + return StreamState{L, OpenFailed, {}, false}; } void Profile(llvm::FoldingSetNodeID &ID) const { ID.AddPointer(LastOperation); ID.AddInteger(State); ID.AddInteger(ErrorState); + ID.AddBoolean(FilePositionIndeterminate); } }; @@ -173,7 +196,8 @@ ProgramStateRef bindInt(uint64_t Value, ProgramStateRef State, class StreamChecker : public Checker { mutable std::unique_ptr BT_nullfp, BT_illegalwhence, - BT_UseAfterClose, BT_UseAfterOpenFailed, BT_ResourceLeak, BT_StreamEof; + BT_UseAfterClose, BT_UseAfterOpenFailed, BT_ResourceLeak, BT_StreamEof, + BT_IndeterminatePosition; public: void checkPreCall(const CallEvent &Call, CheckerContext &C) const; @@ -279,6 +303,16 @@ class StreamChecker ProgramStateRef ensureStreamOpened(SVal StreamVal, CheckerContext &C, ProgramStateRef State) const; + /// Check that the stream has not an invalid ("indeterminate") file position, + /// generate warning for it. + /// (EOF is not an invalid position.) + /// The returned state can be nullptr if a fatal error was generated. + /// It can return non-null state if the stream has not an invalid position or + /// there is execution path with non-invalid position. + ProgramStateRef + ensureNoFilePositionIndeterminate(SVal StreamVal, CheckerContext &C, + ProgramStateRef State) const; + /// Check the legality of the 'whence' argument of 'fseek'. /// Generate error and return nullptr if it is found to be illegal. /// Otherwise returns the state. @@ -447,6 +481,9 @@ void StreamChecker::preFread(const FnDescription *Desc, const CallEvent &Call, if (!State) return; State = ensureStreamOpened(StreamVal, C, State); + if (!State) + return; + State = ensureNoFilePositionIndeterminate(StreamVal, C, State); if (!State) return; @@ -468,6 +505,9 @@ void StreamChecker::preFwrite(const FnDescription *Desc, const CallEvent &Call, if (!State) return; State = ensureStreamOpened(StreamVal, C, State); + if (!State) + return; + State = ensureNoFilePositionIndeterminate(StreamVal, C, State); if (!State) return; @@ -548,7 +588,9 @@ void StreamChecker::evalFreadFwrite(const FnDescription *Desc, NewES = (SS->ErrorState == ErrorFEof) ? ErrorFEof : ErrorFEof | ErrorFError; else NewES = ErrorFError; - StreamState NewState = StreamState::getOpened(Desc, NewES); + // If a (non-EOF) error occurs, the resulting value of the file position + // indicator for the stream is indeterminate. + StreamState NewState = StreamState::getOpened(Desc, NewES, !NewES.isFEof()); StateFailed = StateFailed->set(StreamSym, NewState); C.addTransition(StateFailed); } @@ -601,9 +643,11 @@ void StreamChecker::evalFseek(const FnDescription *Desc, const CallEvent &Call, StateNotFailed->set(StreamSym, StreamState::getOpened(Desc)); // We get error. // It is possible that fseek fails but sets none of the error flags. + // If fseek failed, assume that the file position becomes indeterminate in any + // case. StateFailed = StateFailed->set( StreamSym, - StreamState::getOpened(Desc, ErrorNone | ErrorFEof | ErrorFError)); + StreamState::getOpened(Desc, ErrorNone | ErrorFEof | ErrorFError, true)); C.addTransition(StateNotFailed); C.addTransition(StateFailed); @@ -623,7 +667,10 @@ void StreamChecker::evalClearerr(const FnDescription *Desc, assertStreamStateOpened(SS); - State = State->set(StreamSym, StreamState::getOpened(Desc)); + // FilePositionIndeterminate is not cleared. + State = State->set( + StreamSym, + StreamState::getOpened(Desc, ErrorNone, SS->FilePositionIndeterminate)); C.addTransition(State); } @@ -651,7 +698,9 @@ void StreamChecker::evalFeofFerror(const FnDescription *Desc, // From now on it is the only one error state. ProgramStateRef TrueState = bindAndAssumeTrue(State, C, CE); C.addTransition(TrueState->set( - StreamSym, StreamState::getOpened(Desc, ErrorKind))); + StreamSym, StreamState::getOpened(Desc, ErrorKind, + SS->FilePositionIndeterminate && + !ErrorKind.isFEof()))); } if (StreamErrorState NewES = SS->ErrorState & (~ErrorKind)) { // Execution path(s) with ErrorKind not set. @@ -659,7 +708,9 @@ void StreamChecker::evalFeofFerror(const FnDescription *Desc, // New error state is everything before minus ErrorKind. ProgramStateRef FalseState = bindInt(0, State, C, CE); C.addTransition(FalseState->set( - StreamSym, StreamState::getOpened(Desc, NewES))); + StreamSym, + StreamState::getOpened( + Desc, NewES, SS->FilePositionIndeterminate && !NewES.isFEof()))); } } @@ -767,6 +818,55 @@ ProgramStateRef StreamChecker::ensureStreamOpened(SVal StreamVal, return State; } +ProgramStateRef StreamChecker::ensureNoFilePositionIndeterminate( + SVal StreamVal, CheckerContext &C, ProgramStateRef State) const { + SymbolRef Sym = StreamVal.getAsSymbol(); + if (!Sym) + return State; + + const StreamState *SS = State->get(Sym); + if (!SS) + return State; + + assert(SS->isOpened() && "First ensure that stream is opened."); + + if (SS->FilePositionIndeterminate) { + if (!BT_IndeterminatePosition) + BT_IndeterminatePosition.reset( + new BuiltinBug(this, "Invalid stream state", + "File position of the stream might be 'indeterminate' " + "after a failed operation. " + "Can cause undefined behavior.")); + + if (SS->ErrorState & ErrorFEof) { + // The error is unknown but may be FEOF. + // Continue analysis with the FEOF error state. + // Report warning because the other possible error states. + ExplodedNode *N = C.generateNonFatalErrorNode(State); + if (!N) + return nullptr; + + C.emitReport(std::make_unique( + *BT_IndeterminatePosition, BT_IndeterminatePosition->getDescription(), + N)); + return State->set( + Sym, StreamState::getOpened(SS->LastOperation, ErrorFEof, false)); + } + + // Known or unknown error state without FEOF possible. + // Stop analysis, report error. + ExplodedNode *N = C.generateErrorNode(State); + if (N) + C.emitReport(std::make_unique( + *BT_IndeterminatePosition, BT_IndeterminatePosition->getDescription(), + N)); + + return nullptr; + } + + return State; +} + ProgramStateRef StreamChecker::ensureFseekWhenceCorrect(SVal WhenceVal, CheckerContext &C, ProgramStateRef State) const { diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/DiagOutputUtils.h b/clang/lib/StaticAnalyzer/Checkers/WebKit/DiagOutputUtils.h index 4979b8ffc2b20..781a8d746001f 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/DiagOutputUtils.h +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/DiagOutputUtils.h @@ -23,6 +23,14 @@ void printQuotedQualifiedName(llvm::raw_ostream &Os, Os << "'"; } +template +void printQuotedName(llvm::raw_ostream &Os, const NamedDeclDerivedT &D) { + Os << "'"; + D->getNameForDiagnostic(Os, D->getASTContext().getPrintingPolicy(), + /*Qualified=*/false); + Os << "'"; +} + } // namespace clang #endif diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/NoUncountedMembersChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/NoUncountedMembersChecker.cpp new file mode 100644 index 0000000000000..89caf602a17e5 --- /dev/null +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/NoUncountedMembersChecker.cpp @@ -0,0 +1,150 @@ +//=======- NoUncountedMembersChecker.cpp -------------------------*- C++ -*-==// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "ASTUtils.h" +#include "DiagOutputUtils.h" +#include "PtrTypesSemantics.h" +#include "clang/AST/CXXInheritance.h" +#include "clang/AST/Decl.h" +#include "clang/AST/DeclCXX.h" +#include "clang/AST/RecursiveASTVisitor.h" +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "llvm/ADT/DenseSet.h" +#include "llvm/Support/Casting.h" + +using namespace clang; +using namespace ento; + +namespace { + +class NoUncountedMemberChecker + : public Checker> { +private: + BugType Bug; + mutable BugReporter *BR; + +public: + NoUncountedMemberChecker() + : Bug(this, + "Member variable is a raw-poiner/reference to reference-countable " + "type", + "WebKit coding guidelines") {} + + void checkASTDecl(const TranslationUnitDecl *TUD, AnalysisManager &MGR, + BugReporter &BRArg) const { + BR = &BRArg; + + // The calls to checkAST* from AnalysisConsumer don't + // visit template instantiations or lambda classes. We + // want to visit those, so we make our own RecursiveASTVisitor. + struct LocalVisitor : public RecursiveASTVisitor { + const NoUncountedMemberChecker *Checker; + explicit LocalVisitor(const NoUncountedMemberChecker *Checker) + : Checker(Checker) { + assert(Checker); + } + + bool shouldVisitTemplateInstantiations() const { return true; } + bool shouldVisitImplicitCode() const { return false; } + + bool VisitRecordDecl(const RecordDecl *RD) { + Checker->visitRecordDecl(RD); + return true; + } + }; + + LocalVisitor visitor(this); + visitor.TraverseDecl(const_cast(TUD)); + } + + void visitRecordDecl(const RecordDecl *RD) const { + if (shouldSkipDecl(RD)) + return; + + for (auto Member : RD->fields()) { + const Type *MemberType = Member->getType().getTypePtrOrNull(); + if (!MemberType) + continue; + + if (auto *MemberCXXRD = MemberType->getPointeeCXXRecordDecl()) { + if (isRefCountable(MemberCXXRD)) + reportBug(Member, MemberType, MemberCXXRD, RD); + } + } + } + + bool shouldSkipDecl(const RecordDecl *RD) const { + if (!RD->isThisDeclarationADefinition()) + return true; + + if (RD->isImplicit()) + return true; + + if (RD->isLambda()) + return true; + + // If the construct doesn't have a source file, then it's not something + // we want to diagnose. + const auto RDLocation = RD->getLocation(); + if (!RDLocation.isValid()) + return true; + + const auto Kind = RD->getTagKind(); + // FIMXE: Should we check union members too? + if (Kind != TTK_Struct && Kind != TTK_Class) + return true; + + // Ignore CXXRecords that come from system headers. + if (BR->getSourceManager().isInSystemHeader(RDLocation)) + return true; + + // Ref-counted smartpointers actually have raw-pointer to uncounted type as + // a member but we trust them to handle it correctly. + return isRefCounted(llvm::dyn_cast_or_null(RD)); + } + + void reportBug(const FieldDecl *Member, const Type *MemberType, + const CXXRecordDecl *MemberCXXRD, + const RecordDecl *ClassCXXRD) const { + assert(Member); + assert(MemberType); + assert(MemberCXXRD); + + SmallString<100> Buf; + llvm::raw_svector_ostream Os(Buf); + + Os << "Member variable "; + printQuotedName(Os, Member); + Os << " in "; + printQuotedQualifiedName(Os, ClassCXXRD); + Os << " is a " + << (isa(MemberType) ? "raw pointer" : "reference") + << " to ref-countable type "; + printQuotedQualifiedName(Os, MemberCXXRD); + Os << "; member variables must be ref-counted."; + + PathDiagnosticLocation BSLoc(Member->getSourceRange().getBegin(), + BR->getSourceManager()); + auto Report = std::make_unique(Bug, Os.str(), BSLoc); + Report->addRange(Member->getSourceRange()); + BR->emitReport(std::move(Report)); + } +}; +} // namespace + +void ento::registerWebKitNoUncountedMemberChecker(CheckerManager &Mgr) { + Mgr.registerChecker(); +} + +bool ento::shouldRegisterWebKitNoUncountedMemberChecker( + const CheckerManager &Mgr) { + return true; +} diff --git a/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp b/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp index 3b2e5cd28e437..ad79f7cb9359f 100644 --- a/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp +++ b/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp @@ -45,7 +45,6 @@ #include "clang/StaticAnalyzer/Core/PathSensitive/SMTConv.h" #include "clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h" #include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/SubEngine.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/None.h" #include "llvm/ADT/Optional.h" diff --git a/clang/lib/StaticAnalyzer/Core/CMakeLists.txt b/clang/lib/StaticAnalyzer/Core/CMakeLists.txt index 057cdd4bb18ab..233ffaf799568 100644 --- a/clang/lib/StaticAnalyzer/Core/CMakeLists.txt +++ b/clang/lib/StaticAnalyzer/Core/CMakeLists.txt @@ -44,7 +44,6 @@ add_clang_library(clangStaticAnalyzerCore SimpleSValBuilder.cpp SMTConstraintManager.cpp Store.cpp - SubEngine.cpp SValBuilder.cpp SVals.cpp SymbolManager.cpp diff --git a/clang/lib/StaticAnalyzer/Core/CallEvent.cpp b/clang/lib/StaticAnalyzer/Core/CallEvent.cpp index 1ea7c26dc76b0..fb728ac9e4f5a 100644 --- a/clang/lib/StaticAnalyzer/Core/CallEvent.cpp +++ b/clang/lib/StaticAnalyzer/Core/CallEvent.cpp @@ -564,7 +564,7 @@ RuntimeDefinition AnyFunctionCall::getRuntimeDefinition() const { return RuntimeDefinition(Decl); } - SubEngine &Engine = getState()->getStateManager().getOwningEngine(); + ExprEngine &Engine = getState()->getStateManager().getOwningEngine(); AnalyzerOptions &Opts = Engine.getAnalysisManager().options; // Try to get CTU definition only if CTUDir is provided. diff --git a/clang/lib/StaticAnalyzer/Core/CoreEngine.cpp b/clang/lib/StaticAnalyzer/Core/CoreEngine.cpp index 5a49b18aecf12..70deb13a8e1ae 100644 --- a/clang/lib/StaticAnalyzer/Core/CoreEngine.cpp +++ b/clang/lib/StaticAnalyzer/Core/CoreEngine.cpp @@ -23,8 +23,8 @@ #include "clang/StaticAnalyzer/Core/AnalyzerOptions.h" #include "clang/StaticAnalyzer/Core/PathSensitive/BlockCounter.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" #include "clang/StaticAnalyzer/Core/PathSensitive/FunctionSummary.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/SubEngine.h" #include "clang/StaticAnalyzer/Core/PathSensitive/WorkList.h" #include "llvm/ADT/Optional.h" #include "llvm/ADT/STLExtras.h" @@ -52,8 +52,7 @@ STATISTIC(NumPathsExplored, // Core analysis engine. //===----------------------------------------------------------------------===// -static std::unique_ptr generateWorkList(AnalyzerOptions &Opts, - SubEngine &subengine) { +static std::unique_ptr generateWorkList(AnalyzerOptions &Opts) { switch (Opts.getExplorationStrategy()) { case ExplorationStrategyKind::DFS: return WorkList::makeDFS(); @@ -71,9 +70,9 @@ static std::unique_ptr generateWorkList(AnalyzerOptions &Opts, llvm_unreachable("Unknown AnalyzerOptions::ExplorationStrategyKind"); } -CoreEngine::CoreEngine(SubEngine &subengine, FunctionSummariesTy *FS, +CoreEngine::CoreEngine(ExprEngine &exprengine, FunctionSummariesTy *FS, AnalyzerOptions &Opts) - : SubEng(subengine), WList(generateWorkList(Opts, subengine)), + : ExprEng(exprengine), WList(generateWorkList(Opts)), BCounterFactory(G.getAllocator()), FunctionSummaries(FS) {} /// ExecuteWorkList - Run the worklist algorithm for a maximum number of steps. @@ -104,7 +103,7 @@ bool CoreEngine::ExecuteWorkList(const LocationContext *L, unsigned Steps, WList->setBlockCounter(BCounterFactory.GetEmptyCounter()); if (!InitState) - InitState = SubEng.getInitialState(L); + InitState = ExprEng.getInitialState(L); bool IsNew; ExplodedNode *Node = G.getNode(StartLoc, InitState, false, &IsNew); @@ -113,7 +112,7 @@ bool CoreEngine::ExecuteWorkList(const LocationContext *L, unsigned Steps, NodeBuilderContext BuilderCtx(*this, StartLoc.getDst(), Node); ExplodedNodeSet DstBegin; - SubEng.processBeginOfFunction(BuilderCtx, Node, DstBegin, StartLoc); + ExprEng.processBeginOfFunction(BuilderCtx, Node, DstBegin, StartLoc); enqueue(DstBegin); } @@ -147,7 +146,7 @@ bool CoreEngine::ExecuteWorkList(const LocationContext *L, unsigned Steps, dispatchWorkItem(Node, Node->getLocation(), WU); } - SubEng.processEndWorklist(); + ExprEng.processEndWorklist(); return WList->hasWork(); } @@ -172,7 +171,7 @@ void CoreEngine::dispatchWorkItem(ExplodedNode* Pred, ProgramPoint Loc, break; case ProgramPoint::CallExitBeginKind: - SubEng.processCallExit(Pred); + ExprEng.processCallExit(Pred); break; case ProgramPoint::EpsilonKind: { @@ -253,17 +252,17 @@ void CoreEngine::HandleBlockEdge(const BlockEdge &L, ExplodedNode *Pred) { } // Process the final state transition. - SubEng.processEndOfFunction(BuilderCtx, Pred, RS); + ExprEng.processEndOfFunction(BuilderCtx, Pred, RS); // This path is done. Don't enqueue any more nodes. return; } - // Call into the SubEngine to process entering the CFGBlock. + // Call into the ExprEngine to process entering the CFGBlock. ExplodedNodeSet dstNodes; BlockEntrance BE(Blk, Pred->getLocationContext()); NodeBuilderWithSinks nodeBuilder(Pred, dstNodes, BuilderCtx, BE); - SubEng.processCFGBlockEntrance(L, nodeBuilder, Pred); + ExprEng.processCFGBlockEntrance(L, nodeBuilder, Pred); // Auto-generate a node. if (!nodeBuilder.hasGeneratedNodes()) { @@ -287,7 +286,7 @@ void CoreEngine::HandleBlockEntrance(const BlockEntrance &L, // Process the entrance of the block. if (Optional E = L.getFirstElement()) { NodeBuilderContext Ctx(*this, L.getBlock(), Pred); - SubEng.processCFGElement(*E, Pred, 0, &Ctx); + ExprEng.processCFGElement(*E, Pred, 0, &Ctx); } else HandleBlockExit(L.getBlock(), Pred); @@ -367,7 +366,7 @@ void CoreEngine::HandleBlockExit(const CFGBlock * B, ExplodedNode *Pred) { builder(Pred, B, cast(Term)->getTarget(), *(B->succ_begin()), this); - SubEng.processIndirectGoto(builder); + ExprEng.processIndirectGoto(builder); return; } @@ -378,7 +377,7 @@ void CoreEngine::HandleBlockExit(const CFGBlock * B, ExplodedNode *Pred) { // 'element' variable to a value. // (2) in a terminator, which represents the branch. // - // For (1), subengines will bind a value (i.e., 0 or 1) indicating + // For (1), ExprEngine will bind a value (i.e., 0 or 1) indicating // whether or not collection contains any more elements. We cannot // just test to see if the element is nil because a container can // contain nil elements. @@ -389,7 +388,7 @@ void CoreEngine::HandleBlockExit(const CFGBlock * B, ExplodedNode *Pred) { SwitchNodeBuilder builder(Pred, B, cast(Term)->getCond(), this); - SubEng.processSwitch(builder); + ExprEng.processSwitch(builder); return; } @@ -418,7 +417,7 @@ void CoreEngine::HandleBlockExit(const CFGBlock * B, ExplodedNode *Pred) { void CoreEngine::HandleCallEnter(const CallEnter &CE, ExplodedNode *Pred) { NodeBuilderContext BuilderCtx(*this, CE.getEntry(), Pred); - SubEng.processCallEnter(BuilderCtx, CE, Pred); + ExprEng.processCallEnter(BuilderCtx, CE, Pred); } void CoreEngine::HandleBranch(const Stmt *Cond, const Stmt *Term, @@ -426,7 +425,7 @@ void CoreEngine::HandleBranch(const Stmt *Cond, const Stmt *Term, assert(B->succ_size() == 2); NodeBuilderContext Ctx(*this, B, Pred); ExplodedNodeSet Dst; - SubEng.processBranch(Cond, Ctx, Pred, Dst, *(B->succ_begin()), + ExprEng.processBranch(Cond, Ctx, Pred, Dst, *(B->succ_begin()), *(B->succ_begin() + 1)); // Enqueue the new frontier onto the worklist. enqueue(Dst); @@ -438,7 +437,7 @@ void CoreEngine::HandleCleanupTemporaryBranch(const CXXBindTemporaryExpr *BTE, assert(B->succ_size() == 2); NodeBuilderContext Ctx(*this, B, Pred); ExplodedNodeSet Dst; - SubEng.processCleanupTemporaryBranch(BTE, Ctx, Pred, Dst, *(B->succ_begin()), + ExprEng.processCleanupTemporaryBranch(BTE, Ctx, Pred, Dst, *(B->succ_begin()), *(B->succ_begin() + 1)); // Enqueue the new frontier onto the worklist. enqueue(Dst); @@ -449,7 +448,7 @@ void CoreEngine::HandleStaticInit(const DeclStmt *DS, const CFGBlock *B, assert(B->succ_size() == 2); NodeBuilderContext Ctx(*this, B, Pred); ExplodedNodeSet Dst; - SubEng.processStaticInitializer(DS, Ctx, Pred, Dst, + ExprEng.processStaticInitializer(DS, Ctx, Pred, Dst, *(B->succ_begin()), *(B->succ_begin()+1)); // Enqueue the new frontier onto the worklist. enqueue(Dst); @@ -464,7 +463,7 @@ void CoreEngine::HandlePostStmt(const CFGBlock *B, unsigned StmtIdx, HandleBlockExit(B, Pred); else { NodeBuilderContext Ctx(*this, B, Pred); - SubEng.processCFGElement((*B)[StmtIdx], Pred, StmtIdx, &Ctx); + ExprEng.processCFGElement((*B)[StmtIdx], Pred, StmtIdx, &Ctx); } } diff --git a/clang/lib/StaticAnalyzer/Core/DynamicSize.cpp b/clang/lib/StaticAnalyzer/Core/DynamicSize.cpp index f90c29c52f0fb..8b2172db445ce 100644 --- a/clang/lib/StaticAnalyzer/Core/DynamicSize.cpp +++ b/clang/lib/StaticAnalyzer/Core/DynamicSize.cpp @@ -44,5 +44,28 @@ DefinedOrUnknownSVal getDynamicElementCount(ProgramStateRef State, return DivisionV.castAs(); } +SVal getDynamicSizeWithOffset(ProgramStateRef State, const SVal &BufV) { + SValBuilder &SvalBuilder = State->getStateManager().getSValBuilder(); + const MemRegion *MRegion = BufV.getAsRegion(); + if (!MRegion) + return UnknownVal(); + RegionOffset Offset = MRegion->getAsOffset(); + if (Offset.hasSymbolicOffset()) + return UnknownVal(); + const MemRegion *BaseRegion = MRegion->getBaseRegion(); + if (!BaseRegion) + return UnknownVal(); + + NonLoc OffsetInBytes = SvalBuilder.makeArrayIndex( + Offset.getOffset() / + MRegion->getMemRegionManager().getContext().getCharWidth()); + DefinedOrUnknownSVal ExtentInBytes = + getDynamicSize(State, BaseRegion, SvalBuilder); + + return SvalBuilder.evalBinOp(State, BinaryOperator::Opcode::BO_Sub, + ExtentInBytes, OffsetInBytes, + SvalBuilder.getArrayIndexType()); +} + } // namespace ento } // namespace clang diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp index 36b930faf2d02..6fce27bc95569 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -3206,3 +3206,5 @@ void *ProgramStateTrait::GDMIndex() { static int index = 0; return &index; } + +void ExprEngine::anchor() { } diff --git a/clang/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp b/clang/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp index 184fdcfb3d4b7..bc7c41d039c4d 100644 --- a/clang/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp +++ b/clang/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp @@ -1070,8 +1070,13 @@ StringRef HTMLDiagnostics::generateKeyboardNavigationJavascript() {