13 changes: 13 additions & 0 deletions clang-tools-extra/clangd/ClangdLSPServer.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,13 @@
#include "Protocol.h"
#include "Transport.h"
#include "support/Context.h"
#include "support/MemoryTree.h"
#include "support/Path.h"
#include "clang/Tooling/Core/Replacement.h"
#include "llvm/ADT/Optional.h"
#include "llvm/ADT/StringSet.h"
#include "llvm/Support/JSON.h"
#include <chrono>
#include <memory>

namespace clang {
Expand Down Expand Up @@ -67,6 +69,9 @@ class ClangdLSPServer : private ClangdServer::Callbacks {
/// \return Whether we shut down cleanly with a 'shutdown' -> 'exit' sequence.
bool run();

/// Profiles resource-usage.
void profile(MemoryTree &MT) const;

private:
// Implement ClangdServer::Callbacks.
void onDiagnosticsReady(PathRef File, llvm::StringRef Version,
Expand Down Expand Up @@ -160,6 +165,14 @@ class ClangdLSPServer : private ClangdServer::Callbacks {
/// Sends a "publishDiagnostics" notification to the LSP client.
void publishDiagnostics(const PublishDiagnosticsParams &);

/// Runs profiling and exports memory usage metrics if tracing is enabled and
/// profiling hasn't happened recently.
void maybeExportMemoryProfile();

/// Timepoint until which profiling is off. It is used to throttle profiling
/// requests.
std::chrono::steady_clock::time_point NextProfileTime;

/// Since initialization of CDBs and ClangdServer is done lazily, the
/// following context captures the one used while creating ClangdLSPServer and
/// passes it to above mentioned object instances to make sure they share the
Expand Down
9 changes: 9 additions & 0 deletions clang-tools-extra/clangd/ClangdServer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
#include "refactor/Tweak.h"
#include "support/Logger.h"
#include "support/Markup.h"
#include "support/MemoryTree.h"
#include "support/ThreadsafeFS.h"
#include "support/Trace.h"
#include "clang/Format/Format.h"
Expand Down Expand Up @@ -218,6 +219,7 @@ ClangdServer::ClangdServer(const GlobalCompilationDatabase &CDB,
BGOpts.ContextProvider = [this](PathRef P) {
return createProcessingContext(P);
};
BGOpts.CollectMainFileRefs = Opts.CollectMainFileRefs;
BackgroundIdx = std::make_unique<BackgroundIndex>(
TFS, CDB,
BackgroundIndexStorage::createDiskBackedStorageFactory(
Expand Down Expand Up @@ -826,5 +828,12 @@ ClangdServer::blockUntilIdleForTest(llvm::Optional<double> TimeoutSeconds) {
BackgroundIdx->blockUntilIdleForTest(TimeoutSeconds));
}

void ClangdServer::profile(MemoryTree &MT) const {
if (DynamicIdx)
DynamicIdx->profile(MT.child("dynamic_index"));
if (BackgroundIdx)
BackgroundIdx->profile(MT.child("background_index"));
WorkScheduler.profile(MT.child("tuscheduler"));
}
} // namespace clangd
} // namespace clang
14 changes: 10 additions & 4 deletions clang-tools-extra/clangd/ClangdServer.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
#include "refactor/Tweak.h"
#include "support/Cancellation.h"
#include "support/Function.h"
#include "support/MemoryTree.h"
#include "support/ThreadsafeFS.h"
#include "clang/Tooling/CompilationDatabase.h"
#include "clang/Tooling/Core/Replacement.h"
Expand Down Expand Up @@ -127,11 +128,13 @@ class ClangdServer {
/// enabled.
ClangTidyOptionsBuilder GetClangTidyOptions;

/// If true, turn on the `-frecovery-ast` clang flag.
bool BuildRecoveryAST = true;
/// If true, force -frecovery-ast flag.
/// If false, respect the value in clang.
bool BuildRecoveryAST = false;

/// If true, turn on the `-frecovery-ast-type` clang flag.
bool PreserveRecoveryASTType = true;
/// If true, force -frecovery-ast-type flag.
/// If false, respect the value in clang.
bool PreserveRecoveryASTType = false;

/// Clangd's workspace root. Relevant for "workspace" operations not bound
/// to a particular file.
Expand Down Expand Up @@ -337,6 +340,9 @@ class ClangdServer {
LLVM_NODISCARD bool
blockUntilIdleForTest(llvm::Optional<double> TimeoutSeconds = 10);

/// Builds a nested representation of memory used by components.
void profile(MemoryTree &MT) const;

private:
void formatCode(PathRef File, llvm::StringRef Code,
ArrayRef<tooling::Range> Ranges,
Expand Down
10 changes: 5 additions & 5 deletions clang-tools-extra/clangd/Compiler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -81,11 +81,11 @@ buildCompilerInvocation(const ParseInputs &Inputs, clang::DiagnosticConsumer &D,
// Don't crash on `#pragma clang __debug parser_crash`
CI->getPreprocessorOpts().DisablePragmaDebugCrash = true;

// Recovery expression currently only works for C++.
if (CI->getLangOpts()->CPlusPlus) {
CI->getLangOpts()->RecoveryAST = Inputs.Opts.BuildRecoveryAST;
CI->getLangOpts()->RecoveryASTType = Inputs.Opts.PreserveRecoveryASTType;
}
if (Inputs.Opts.BuildRecoveryAST)
CI->getLangOpts()->RecoveryAST = true;
if (Inputs.Opts.PreserveRecoveryASTType)
CI->getLangOpts()->RecoveryASTType = true;

return CI;
}

Expand Down
36 changes: 28 additions & 8 deletions clang-tools-extra/clangd/FindSymbols.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -171,18 +171,13 @@ namespace {
llvm::Optional<DocumentSymbol> declToSym(ASTContext &Ctx, const NamedDecl &ND) {
auto &SM = Ctx.getSourceManager();

SourceLocation NameLoc = nameLocation(ND, SM);
SourceLocation BeginLoc = SM.getSpellingLoc(SM.getFileLoc(ND.getBeginLoc()));
SourceLocation EndLoc = SM.getSpellingLoc(SM.getFileLoc(ND.getEndLoc()));
const auto SymbolRange =
toHalfOpenFileRange(SM, Ctx.getLangOpts(), {BeginLoc, EndLoc});
if (!SymbolRange)
return llvm::None;

Position NameBegin = sourceLocToPosition(SM, NameLoc);
Position NameEnd = sourceLocToPosition(
SM, Lexer::getLocForEndOfToken(NameLoc, 0, SM, Ctx.getLangOpts()));

index::SymbolInfo SymInfo = index::getSymbolInfo(&ND);
// FIXME: this is not classifying constructors, destructors and operators
// correctly (they're all "methods").
Expand All @@ -194,10 +189,35 @@ llvm::Optional<DocumentSymbol> declToSym(ASTContext &Ctx, const NamedDecl &ND) {
SI.deprecated = ND.isDeprecated();
SI.range = Range{sourceLocToPosition(SM, SymbolRange->getBegin()),
sourceLocToPosition(SM, SymbolRange->getEnd())};
SI.selectionRange = Range{NameBegin, NameEnd};

SourceLocation NameLoc = ND.getLocation();
SourceLocation FallbackNameLoc;
if (NameLoc.isMacroID()) {
if (isSpelledInSource(NameLoc, SM)) {
// Prefer the spelling loc, but save the expansion loc as a fallback.
FallbackNameLoc = SM.getExpansionLoc(NameLoc);
NameLoc = SM.getSpellingLoc(NameLoc);
} else {
NameLoc = SM.getExpansionLoc(NameLoc);
}
}
auto ComputeSelectionRange = [&](SourceLocation L) -> Range {
Position NameBegin = sourceLocToPosition(SM, L);
Position NameEnd = sourceLocToPosition(
SM, Lexer::getLocForEndOfToken(L, 0, SM, Ctx.getLangOpts()));
return Range{NameBegin, NameEnd};
};

SI.selectionRange = ComputeSelectionRange(NameLoc);
if (!SI.range.contains(SI.selectionRange) && FallbackNameLoc.isValid()) {
// 'selectionRange' must be contained in 'range'. In cases where clang
// reports unrelated ranges, we first try falling back to the expansion
// loc for the selection range.
SI.selectionRange = ComputeSelectionRange(FallbackNameLoc);
}
if (!SI.range.contains(SI.selectionRange)) {
// 'selectionRange' must be contained in 'range', so in cases where clang
// reports unrelated ranges we need to reconcile somehow.
// If the containment relationship still doesn't hold, throw away
// 'range' and use 'selectionRange' for both.
SI.range = SI.selectionRange;
}
return SI;
Expand Down
66 changes: 55 additions & 11 deletions clang-tools-extra/clangd/FindTarget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,10 @@ const auto StaticFilter = [](const NamedDecl *D) {
return !D->isCXXInstanceMember();
};
const auto ValueFilter = [](const NamedDecl *D) { return isa<ValueDecl>(D); };
const auto TypeFilter = [](const NamedDecl *D) { return isa<TypeDecl>(D); };
const auto TemplateFilter = [](const NamedDecl *D) {
return isa<TemplateDecl>(D);
};

// Given the type T of a dependent expression that appears of the LHS of a
// "->", heuristically find a corresponding pointee type in whose scope we
Expand Down Expand Up @@ -219,19 +223,45 @@ std::vector<const NamedDecl *> resolveExprToDecls(const Expr *E) {
return {};
}

// Try to heuristically resolve the type of a possibly-dependent expression `E`.
const Type *resolveExprToType(const Expr *E) {
std::vector<const NamedDecl *> Decls = resolveExprToDecls(E);
const Type *resolveDeclsToType(const std::vector<const NamedDecl *> &Decls) {
if (Decls.size() != 1) // Names an overload set -- just bail.
return nullptr;
if (const auto *TD = dyn_cast<TypeDecl>(Decls[0])) {
return TD->getTypeForDecl();
} else if (const auto *VD = dyn_cast<ValueDecl>(Decls[0])) {
}
if (const auto *VD = dyn_cast<ValueDecl>(Decls[0])) {
return VD->getType().getTypePtrOrNull();
}
return nullptr;
}

// Try to heuristically resolve the type of a possibly-dependent expression `E`.
const Type *resolveExprToType(const Expr *E) {
return resolveDeclsToType(resolveExprToDecls(E));
}

// Try to heuristically resolve the type of a possibly-dependent nested name
// specifier.
const Type *resolveNestedNameSpecifierToType(const NestedNameSpecifier *NNS) {
if (!NNS)
return nullptr;

switch (NNS->getKind()) {
case NestedNameSpecifier::TypeSpec:
case NestedNameSpecifier::TypeSpecWithTemplate:
return NNS->getAsType();
case NestedNameSpecifier::Identifier: {
return resolveDeclsToType(getMembersReferencedViaDependentName(
resolveNestedNameSpecifierToType(NNS->getPrefix()),
[&](const ASTContext &) { return NNS->getAsIdentifier(); },
TypeFilter));
}
default:
break;
}
return nullptr;
}

const NamedDecl *getTemplatePattern(const NamedDecl *D) {
if (const CXXRecordDecl *CRD = dyn_cast<CXXRecordDecl>(D)) {
if (const auto *Result = CRD->getTemplateInstantiationPattern())
Expand Down Expand Up @@ -291,10 +321,8 @@ const NamedDecl *getTemplatePattern(const NamedDecl *D) {
// and both are lossy. We must know upfront what the caller ultimately wants.
//
// FIXME: improve common dependent scope using name lookup in primary templates.
// We currently handle DependentScopeDeclRefExpr and
// CXXDependentScopeMemberExpr, but some other constructs remain to be handled:
// - DependentTemplateSpecializationType,
// - DependentNameType
// We currently handle several dependent constructs, but some others remain to
// be handled:
// - UnresolvedUsingTypenameDecl
struct TargetFinder {
using RelSet = DeclRelationSet;
Expand Down Expand Up @@ -536,6 +564,23 @@ struct TargetFinder {
if (auto *TD = DTST->getTemplateName().getAsTemplateDecl())
Outer.add(TD->getTemplatedDecl(), Flags | Rel::TemplatePattern);
}
void VisitDependentNameType(const DependentNameType *DNT) {
for (const NamedDecl *ND : getMembersReferencedViaDependentName(
resolveNestedNameSpecifierToType(DNT->getQualifier()),
[DNT](ASTContext &) { return DNT->getIdentifier(); },
TypeFilter)) {
Outer.add(ND, Flags);
}
}
void VisitDependentTemplateSpecializationType(
const DependentTemplateSpecializationType *DTST) {
for (const NamedDecl *ND : getMembersReferencedViaDependentName(
resolveNestedNameSpecifierToType(DTST->getQualifier()),
[DTST](ASTContext &) { return DTST->getIdentifier(); },
TemplateFilter)) {
Outer.add(ND, Flags);
}
}
void VisitTypedefType(const TypedefType *TT) {
Outer.add(TT->getDecl(), Flags);
}
Expand Down Expand Up @@ -591,17 +636,16 @@ struct TargetFinder {
return;
debug(*NNS, Flags);
switch (NNS->getKind()) {
case NestedNameSpecifier::Identifier:
return;
case NestedNameSpecifier::Namespace:
add(NNS->getAsNamespace(), Flags);
return;
case NestedNameSpecifier::NamespaceAlias:
add(NNS->getAsNamespaceAlias(), Flags);
return;
case NestedNameSpecifier::Identifier:
case NestedNameSpecifier::TypeSpec:
case NestedNameSpecifier::TypeSpecWithTemplate:
add(QualType(NNS->getAsType(), 0), Flags);
add(QualType(resolveNestedNameSpecifierToType(NNS), 0), Flags);
return;
case NestedNameSpecifier::Global:
// This should be TUDecl, but we can't get a pointer to it!
Expand Down
2 changes: 1 addition & 1 deletion clang-tools-extra/clangd/ParsedAST.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ class ReplayPreamble : private PPCallbacks {
SrcMgr::CharacteristicKind Kind, FileID PrevFID) override {
// It'd be nice if there was a better way to identify built-in headers...
if (Reason == FileChangeReason::ExitFile &&
SM.getBuffer(PrevFID)->getBufferIdentifier() == "<built-in>")
SM.getBufferOrFake(PrevFID).getBufferIdentifier() == "<built-in>")
replay();
}

Expand Down
67 changes: 24 additions & 43 deletions clang-tools-extra/clangd/Protocol.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -484,12 +484,10 @@ bool fromJSON(const llvm::json::Value &Params, DidSaveTextDocumentParams &R,
bool fromJSON(const llvm::json::Value &Params, DidChangeTextDocumentParams &R,
llvm::json::Path P) {
llvm::json::ObjectMapper O(Params, P);
if (!O)
return false;
O.map("forceRebuild", R.forceRebuild); // Optional clangd extension.
return O.map("textDocument", R.textDocument) &&
return O && O.map("textDocument", R.textDocument) &&
O.map("contentChanges", R.contentChanges) &&
O.map("wantDiagnostics", R.wantDiagnostics);
O.map("wantDiagnostics", R.wantDiagnostics) &&
O.mapOptional("forceRebuild", R.forceRebuild);
}

bool fromJSON(const llvm::json::Value &E, FileChangeType &Out,
Expand Down Expand Up @@ -578,12 +576,10 @@ llvm::json::Value toJSON(const Diagnostic &D) {
bool fromJSON(const llvm::json::Value &Params, Diagnostic &R,
llvm::json::Path P) {
llvm::json::ObjectMapper O(Params, P);
if (!O || !O.map("range", R.range) || !O.map("message", R.message))
return false;
O.map("severity", R.severity);
O.map("category", R.category);
O.map("code", R.code);
O.map("source", R.source);
return O && O.map("range", R.range) && O.map("message", R.message) &&
O.mapOptional("severity", R.severity) &&
O.mapOptional("category", R.category) &&
O.mapOptional("code", R.code) && O.mapOptional("source", R.source);
return true;
}

Expand Down Expand Up @@ -800,10 +796,8 @@ llvm::json::Value toJSON(const ApplyWorkspaceEditParams &Params) {
bool fromJSON(const llvm::json::Value &Response, ApplyWorkspaceEditResponse &R,
llvm::json::Path P) {
llvm::json::ObjectMapper O(Response, P);
if (!O || !O.map("applied", R.applied))
return false;
O.map("failureReason", R.failureReason);
return true;
return O && O.map("applied", R.applied) &&
O.map("failureReason", R.failureReason);
}

bool fromJSON(const llvm::json::Value &Params, TextDocumentPositionParams &R,
Expand All @@ -816,16 +810,11 @@ bool fromJSON(const llvm::json::Value &Params, TextDocumentPositionParams &R,
bool fromJSON(const llvm::json::Value &Params, CompletionContext &R,
llvm::json::Path P) {
llvm::json::ObjectMapper O(Params, P);
if (!O)
return false;

int TriggerKind;
if (!O.map("triggerKind", TriggerKind))
if (!O || !O.map("triggerKind", TriggerKind) ||
!O.mapOptional("triggerCharacter", R.triggerCharacter))
return false;
R.triggerKind = static_cast<CompletionTriggerKind>(TriggerKind);

if (auto *TC = Params.getAsObject()->get("triggerCharacter"))
return fromJSON(*TC, R.triggerCharacter, P.field("triggerCharacter"));
return true;
}

Expand Down Expand Up @@ -1126,8 +1115,8 @@ bool fromJSON(const llvm::json::Value &Params, ConfigurationSettings &S,
llvm::json::ObjectMapper O(Params, P);
if (!O)
return true; // 'any' type in LSP.
O.map("compilationDatabaseChanges", S.compilationDatabaseChanges);
return true;
return O.mapOptional("compilationDatabaseChanges",
S.compilationDatabaseChanges);
}

bool fromJSON(const llvm::json::Value &Params, InitializationOptions &Opts,
Expand All @@ -1136,11 +1125,10 @@ bool fromJSON(const llvm::json::Value &Params, InitializationOptions &Opts,
if (!O)
return true; // 'any' type in LSP.

fromJSON(Params, Opts.ConfigSettings, P);
O.map("compilationDatabasePath", Opts.compilationDatabasePath);
O.map("fallbackFlags", Opts.fallbackFlags);
O.map("clangdFileStatus", Opts.FileStatus);
return true;
return fromJSON(Params, Opts.ConfigSettings, P) &&
O.map("compilationDatabasePath", Opts.compilationDatabasePath) &&
O.mapOptional("fallbackFlags", Opts.fallbackFlags) &&
O.mapOptional("clangdFileStatus", Opts.FileStatus);
}

bool fromJSON(const llvm::json::Value &E, TypeHierarchyDirection &Out,
Expand Down Expand Up @@ -1193,20 +1181,13 @@ bool fromJSON(const llvm::json::Value &Params, TypeHierarchyItem &I,
llvm::json::ObjectMapper O(Params, P);

// Required fields.
if (!(O && O.map("name", I.name) && O.map("kind", I.kind) &&
O.map("uri", I.uri) && O.map("range", I.range) &&
O.map("selectionRange", I.selectionRange))) {
return false;
}

// Optional fields.
O.map("detail", I.detail);
O.map("deprecated", I.deprecated);
O.map("parents", I.parents);
O.map("children", I.children);
O.map("data", I.data);

return true;
return O && O.map("name", I.name) && O.map("kind", I.kind) &&
O.map("uri", I.uri) && O.map("range", I.range) &&
O.map("selectionRange", I.selectionRange) &&
O.mapOptional("detail", I.detail) &&
O.mapOptional("deprecated", I.deprecated) &&
O.mapOptional("parents", I.parents) &&
O.mapOptional("children", I.children) && O.mapOptional("data", I.data);
}

bool fromJSON(const llvm::json::Value &Params,
Expand Down
2 changes: 1 addition & 1 deletion clang-tools-extra/clangd/SemanticHighlighting.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,7 @@ class HighlightingsBuilder {
TokRef = TokRef.drop_front(Conflicting.size());
}
const auto &SM = AST.getSourceManager();
StringRef MainCode = SM.getBuffer(SM.getMainFileID())->getBuffer();
StringRef MainCode = SM.getBufferOrFake(SM.getMainFileID()).getBuffer();

// Merge token stream with "inactive line" markers.
std::vector<HighlightingToken> WithInactiveLines;
Expand Down
7 changes: 3 additions & 4 deletions clang-tools-extra/clangd/SourceCode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -445,9 +445,8 @@ llvm::Optional<SourceRange> toHalfOpenFileRange(const SourceManager &SM,

llvm::StringRef toSourceCode(const SourceManager &SM, SourceRange R) {
assert(isValidFileRange(SM, R));
bool Invalid = false;
auto *Buf = SM.getBuffer(SM.getFileID(R.getBegin()), &Invalid);
assert(!Invalid);
auto Buf = SM.getBufferOrNone(SM.getFileID(R.getBegin()));
assert(Buf);

size_t BeginOffset = SM.getFileOffset(R.getBegin());
size_t EndOffset = SM.getFileOffset(R.getEnd());
Expand All @@ -456,7 +455,7 @@ llvm::StringRef toSourceCode(const SourceManager &SM, SourceRange R) {

llvm::Expected<SourceLocation> sourceLocationInMainFile(const SourceManager &SM,
Position P) {
llvm::StringRef Code = SM.getBuffer(SM.getMainFileID())->getBuffer();
llvm::StringRef Code = SM.getBufferOrFake(SM.getMainFileID()).getBuffer();
auto Offset =
positionToOffset(Code, P, /*AllowColumnBeyondLineLength=*/false);
if (!Offset)
Expand Down
14 changes: 12 additions & 2 deletions clang-tools-extra/clangd/TUScheduler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@
#include "support/Cancellation.h"
#include "support/Context.h"
#include "support/Logger.h"
#include "support/MemoryTree.h"
#include "support/Path.h"
#include "support/Threading.h"
#include "support/Trace.h"
Expand Down Expand Up @@ -932,9 +933,9 @@ TUScheduler::FileStats ASTWorker::stats() const {
// Note that we don't report the size of ASTs currently used for processing
// the in-flight requests. We used this information for debugging purposes
// only, so this should be fine.
Result.UsedBytes = IdleASTs.getUsedBytes(this);
Result.UsedBytesAST = IdleASTs.getUsedBytes(this);
if (auto Preamble = getPossiblyStalePreamble())
Result.UsedBytes += Preamble->Preamble.getSize();
Result.UsedBytesPreamble = Preamble->Preamble.getSize();
return Result;
}

Expand Down Expand Up @@ -1429,5 +1430,14 @@ DebouncePolicy DebouncePolicy::fixed(clock::duration T) {
return P;
}

void TUScheduler::profile(MemoryTree &MT) const {
for (const auto &Elem : fileStats()) {
MT.detail(Elem.first())
.child("preamble")
.addUsage(Opts.StorePreamblesInMemory ? Elem.second.UsedBytesPreamble
: 0);
MT.detail(Elem.first()).child("ast").addUsage(Elem.second.UsedBytesAST);
}
}
} // namespace clangd
} // namespace clang
6 changes: 5 additions & 1 deletion clang-tools-extra/clangd/TUScheduler.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#include "GlobalCompilationDatabase.h"
#include "index/CanonicalIncludes.h"
#include "support/Function.h"
#include "support/MemoryTree.h"
#include "support/Path.h"
#include "support/Threading.h"
#include "llvm/ADT/Optional.h"
Expand Down Expand Up @@ -207,7 +208,8 @@ class TUScheduler {
~TUScheduler();

struct FileStats {
std::size_t UsedBytes = 0;
std::size_t UsedBytesAST = 0;
std::size_t UsedBytesPreamble = 0;
unsigned PreambleBuilds = 0;
unsigned ASTBuilds = 0;
};
Expand Down Expand Up @@ -311,6 +313,8 @@ class TUScheduler {
// FIXME: move to ClangdServer via createProcessingContext.
static llvm::Optional<llvm::StringRef> getFileBeingProcessedInContext();

void profile(MemoryTree &MT) const;

private:
const GlobalCompilationDatabase &CDB;
Options Opts;
Expand Down
6 changes: 6 additions & 0 deletions clang-tools-extra/clangd/index/Background.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include "URI.h"
#include "index/BackgroundIndexLoader.h"
#include "index/FileIndex.h"
#include "index/Index.h"
#include "index/IndexAction.h"
#include "index/MemIndex.h"
#include "index/Ref.h"
Expand Down Expand Up @@ -414,5 +415,10 @@ BackgroundIndex::loadProject(std::vector<std::string> MainFiles) {
return {TUsToIndex.begin(), TUsToIndex.end()};
}

void BackgroundIndex::profile(MemoryTree &MT) const {
IndexedSymbols.profile(MT.child("symbols"));
// We don't want to mix memory used by index and symbols, so call base class.
MT.child("index").addUsage(SwapIndex::estimateMemoryUsage());
}
} // namespace clangd
} // namespace clang
4 changes: 4 additions & 0 deletions clang-tools-extra/clangd/index/Background.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,11 @@
#include "index/Index.h"
#include "index/Serialization.h"
#include "support/Context.h"
#include "support/MemoryTree.h"
#include "support/Path.h"
#include "support/Threading.h"
#include "support/ThreadsafeFS.h"
#include "support/Trace.h"
#include "clang/Tooling/CompilationDatabase.h"
#include "llvm/ADT/StringMap.h"
#include "llvm/Support/Threading.h"
Expand Down Expand Up @@ -172,6 +174,8 @@ class BackgroundIndex : public SwapIndex {
return Queue.blockUntilIdleForTest(TimeoutSeconds);
}

void profile(MemoryTree &MT) const;

private:
/// Represents the state of a single file when indexing was performed.
struct ShardVersion {
Expand Down
30 changes: 30 additions & 0 deletions clang-tools-extra/clangd/index/FileIndex.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
#include "index/SymbolOrigin.h"
#include "index/dex/Dex.h"
#include "support/Logger.h"
#include "support/MemoryTree.h"
#include "support/Path.h"
#include "clang/AST/ASTContext.h"
#include "clang/Index/IndexingAction.h"
Expand Down Expand Up @@ -388,6 +389,25 @@ FileSymbols::buildIndex(IndexType Type, DuplicateHandling DuplicateHandle,
llvm_unreachable("Unknown clangd::IndexType");
}

void FileSymbols::profile(MemoryTree &MT) const {
std::lock_guard<std::mutex> Lock(Mutex);
for (const auto &SymSlab : SymbolsSnapshot) {
MT.detail(SymSlab.first())
.child("symbols")
.addUsage(SymSlab.second->bytes());
}
for (const auto &RefSlab : RefsSnapshot) {
MT.detail(RefSlab.first())
.child("references")
.addUsage(RefSlab.second.Slab->bytes());
}
for (const auto &RelSlab : RelationsSnapshot) {
MT.detail(RelSlab.first())
.child("relations")
.addUsage(RelSlab.second->bytes());
}
}

FileIndex::FileIndex(bool UseDex, bool CollectMainFileRefs)
: MergedIndex(&MainFileIndex, &PreambleIndex), UseDex(UseDex),
CollectMainFileRefs(CollectMainFileRefs),
Expand Down Expand Up @@ -457,5 +477,15 @@ void FileIndex::updateMain(PathRef Path, ParsedAST &AST) {
}
}

void FileIndex::profile(MemoryTree &MT) const {
PreambleSymbols.profile(MT.child("preamble").child("symbols"));
MT.child("preamble")
.child("index")
.addUsage(PreambleIndex.estimateMemoryUsage());
MainFileSymbols.profile(MT.child("main_file").child("symbols"));
MT.child("main_file")
.child("index")
.addUsage(MainFileIndex.estimateMemoryUsage());
}
} // namespace clangd
} // namespace clang
5 changes: 5 additions & 0 deletions clang-tools-extra/clangd/index/FileIndex.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include "index/Relation.h"
#include "index/Serialization.h"
#include "index/Symbol.h"
#include "support/MemoryTree.h"
#include "support/Path.h"
#include "clang/Lex/Preprocessor.h"
#include "clang/Tooling/CompilationDatabase.h"
Expand Down Expand Up @@ -87,6 +88,8 @@ class FileSymbols {
DuplicateHandling DuplicateHandle = DuplicateHandling::PickOne,
size_t *Version = nullptr);

void profile(MemoryTree &MT) const;

private:
struct RefSlabAndCountReferences {
std::shared_ptr<RefSlab> Slab;
Expand Down Expand Up @@ -116,6 +119,8 @@ class FileIndex : public MergedIndex {
/// `indexMainDecls`.
void updateMain(PathRef Path, ParsedAST &AST);

void profile(MemoryTree &MT) const;

private:
bool UseDex; // FIXME: this should be always on.
bool CollectMainFileRefs;
Expand Down
18 changes: 14 additions & 4 deletions clang-tools-extra/clangd/refactor/tweaks/ExtractVariable.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -382,17 +382,27 @@ bool eligibleForExtraction(const SelectionTree::Node *N) {
if (BinOp.parse(*N) && BinaryOperator::isAssignmentOp(BinOp.Kind))
return false;

const SelectionTree::Node &OuterImplicit = N->outerImplicit();
const auto *Parent = OuterImplicit.Parent;
if (!Parent)
return false;
// We don't want to extract expressions used as statements, that would leave
// a `dummy;` around that has no effect.
// Unfortunately because the AST doesn't have ExprStmt, we have to check in
// this roundabout way.
const SelectionTree::Node &OuterImplicit = N->outerImplicit();
if (!OuterImplicit.Parent ||
childExprIsStmt(OuterImplicit.Parent->ASTNode.get<Stmt>(),
if (childExprIsStmt(Parent->ASTNode.get<Stmt>(),
OuterImplicit.ASTNode.get<Expr>()))
return false;

// FIXME: ban extracting the RHS of an assignment: `a = [[foo()]]`
// Disable extraction of full RHS on assignment operations, e.g:
// auto x = [[RHS_EXPR]];
// This would just result in duplicating the code.
if (const auto *BO = Parent->ASTNode.get<BinaryOperator>()) {
if (BO->isAssignmentOp() &&
BO->getRHS() == OuterImplicit.ASTNode.get<Expr>())
return false;
}

return true;
}

Expand Down
1 change: 1 addition & 0 deletions clang-tools-extra/clangd/support/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ add_clang_library(clangdSupport
Context.cpp
Logger.cpp
Markup.cpp
MemoryTree.cpp
Shutdown.cpp
Threading.cpp
ThreadsafeFS.cpp
Expand Down
51 changes: 51 additions & 0 deletions clang-tools-extra/clangd/support/MemoryTree.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
#include "support/MemoryTree.h"
#include "Trace.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/StringRef.h"
#include <cstddef>

namespace clang {
namespace clangd {

namespace {

size_t traverseTree(const MemoryTree &MT, std::string &ComponentName,
const trace::Metric &Out) {
size_t OriginalLen = ComponentName.size();
if (!ComponentName.empty())
ComponentName += '.';
size_t Total = MT.self();
for (const auto &Entry : MT.children()) {
ComponentName += Entry.first;
Total += traverseTree(Entry.getSecond(), ComponentName, Out);
ComponentName.resize(OriginalLen + 1);
}
ComponentName.resize(OriginalLen);
Out.record(Total, ComponentName);
return Total;
}
} // namespace

MemoryTree &MemoryTree::createChild(llvm::StringRef Name) {
auto &Child = Children.try_emplace(Name, DetailAlloc).first->getSecond();
return Child;
}

const llvm::DenseMap<llvm::StringRef, MemoryTree> &
MemoryTree::children() const {
return Children;
}

size_t MemoryTree::total() const {
size_t Total = Size;
for (const auto &Entry : Children)
Total += Entry.getSecond().total();
return Total;
}

void record(const MemoryTree &MT, std::string RootName,
const trace::Metric &Out) {
traverseTree(MT, RootName, Out);
}
} // namespace clangd
} // namespace clang
92 changes: 92 additions & 0 deletions clang-tools-extra/clangd/support/MemoryTree.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
//===--- MemoryTree.h - A special tree for components and sizes -*- 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_CLANGD_SUPPORT_MEMORYTREE_H_
#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_SUPPORT_MEMORYTREE_H_

#include "Trace.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/Allocator.h"
#include "llvm/Support/StringSaver.h"
#include <cstddef>
#include <string>
#include <vector>

namespace clang {
namespace clangd {

/// A tree that can be used to represent memory usage of nested components while
/// preserving the hierarchy.
/// Edges have associated names. An edge that might not be interesting to all
/// traversers or costly to copy (e.g. file names) can be marked as "detail".
/// Tree construction allows chosing between a detailed and brief mode, in brief
/// mode all "detail" edges are ignored and tree is constructed without any
/// string copies.
struct MemoryTree {
public:
/// If Alloc is nullptr, tree is in brief mode and will ignore detail edges.
MemoryTree(llvm::BumpPtrAllocator *DetailAlloc = nullptr)
: DetailAlloc(DetailAlloc) {}

/// No copy of the \p Name.
/// Note that returned pointers are invalidated with subsequent calls to
/// child/detail.
MemoryTree &child(llvm::StringLiteral Name) { return createChild(Name); }

MemoryTree(const MemoryTree &) = delete;
MemoryTree &operator=(const MemoryTree &) = delete;

MemoryTree(MemoryTree &&) = default;
MemoryTree &operator=(MemoryTree &&) = default;

/// Makes a copy of the \p Name in detailed mode, returns current node
/// otherwise.
/// Note that returned pointers are invalidated with subsequent calls to
/// child/detail.
MemoryTree &detail(llvm::StringRef Name) {
return DetailAlloc ? createChild(Name.copy(*DetailAlloc)) : *this;
}

/// Increases size of current node by \p Increment.
void addUsage(size_t Increment) { Size += Increment; }

/// Returns edges to direct children of this node.
const llvm::DenseMap<llvm::StringRef, MemoryTree> &children() const;

/// Returns total number of bytes used by this sub-tree. Performs a traversal.
size_t total() const;

/// Returns total number of bytes used by this node only.
size_t self() const { return Size; }

private:
/// Adds a child with an edge labeled as \p Name. Multiple calls to this
/// function returns the same node.
MemoryTree &createChild(llvm::StringRef Name);

/// Allocator to use for detailed edge names.
llvm::BumpPtrAllocator *DetailAlloc = nullptr;

/// Bytes owned by this component specifically.
size_t Size = 0;

/// Edges from current node to its children. Keys are the labels for edges.
llvm::DenseMap<llvm::StringRef, MemoryTree> Children;
};

/// Records total memory usage of each node under \p Out. Labels are edges on
/// the path joined with ".", starting with \p RootName.
void record(const MemoryTree &MT, std::string RootName,
const trace::Metric &Out);

} // namespace clangd
} // namespace clang

#endif
50 changes: 32 additions & 18 deletions clang-tools-extra/clangd/support/Trace.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,12 @@ class JSONTracer : public EventTracer {

// We stash a Span object in the context. It will record the start/end,
// and this also allows us to look up the parent Span's information.
Context beginSpan(llvm::StringRef Name, llvm::json::Object *Args) override {
return Context::current().derive(
SpanKey, std::make_unique<JSONSpan>(this, Name, Args));
Context beginSpan(
llvm::StringRef Name,
llvm::function_ref<void(llvm::json::Object *)> AttachDetails) override {
auto JS = std::make_unique<JSONSpan>(this, Name);
AttachDetails(&JS->Args);
return Context::current().derive(SpanKey, std::move(JS));
}

// Trace viewer requires each thread to properly stack events.
Expand Down Expand Up @@ -85,9 +88,9 @@ class JSONTracer : public EventTracer {
private:
class JSONSpan {
public:
JSONSpan(JSONTracer *Tracer, llvm::StringRef Name, llvm::json::Object *Args)
JSONSpan(JSONTracer *Tracer, llvm::StringRef Name)
: StartTime(Tracer->timestamp()), EndTime(0), Name(Name),
TID(llvm::get_threadid()), Tracer(Tracer), Args(Args) {
TID(llvm::get_threadid()), Tracer(Tracer) {
// ~JSONSpan() may run in a different thread, so we need to capture now.
Tracer->captureThreadMetadata();

Expand Down Expand Up @@ -125,14 +128,16 @@ class JSONTracer : public EventTracer {
// Finally, record the event (ending at EndTime, not timestamp())!
Tracer->jsonEvent("X",
llvm::json::Object{{"name", std::move(Name)},
{"args", std::move(*Args)},
{"args", std::move(Args)},
{"dur", EndTime - StartTime}},
TID, StartTime);
}

// May be called by any thread.
void markEnded() { EndTime = Tracer->timestamp(); }

llvm::json::Object Args;

private:
static int64_t nextID() {
static std::atomic<int64_t> Next = {0};
Expand All @@ -144,7 +149,6 @@ class JSONTracer : public EventTracer {
std::string Name;
uint64_t TID;
JSONTracer *Tracer;
llvm::json::Object *Args;
};
static Key<std::unique_ptr<JSONSpan>> SpanKey;

Expand Down Expand Up @@ -277,12 +281,13 @@ void log(const llvm::Twine &Message) {
T->instant("Log", llvm::json::Object{{"Message", Message.str()}});
}

// Returned context owns Args.
static Context makeSpanContext(llvm::Twine Name, llvm::json::Object *Args,
const Metric &LatencyMetric) {
bool enabled() { return T != nullptr; }

// The JSON object is event args (owned by context), if the tracer wants them.
static std::pair<Context, llvm::json::Object *>
makeSpanContext(llvm::Twine Name, const Metric &LatencyMetric) {
if (!T)
return Context::current().clone();
WithContextValue WithArgs{std::unique_ptr<llvm::json::Object>(Args)};
return std::make_pair(Context::current().clone(), nullptr);
llvm::Optional<WithContextValue> WithLatency;
using Clock = std::chrono::high_resolution_clock;
WithLatency.emplace(llvm::make_scope_exit(
Expand All @@ -293,9 +298,15 @@ static Context makeSpanContext(llvm::Twine Name, llvm::json::Object *Args,
.count(),
Name);
}));
return T->beginSpan(Name.isSingleStringRef() ? Name.getSingleStringRef()
: llvm::StringRef(Name.str()),
Args);
llvm::json::Object *Args = nullptr;
Context Ctx = T->beginSpan(
Name.isSingleStringRef() ? Name.getSingleStringRef()
: llvm::StringRef(Name.str()),
[&](llvm::json::Object *A) {
assert(A && A->empty() && "Invalid AttachDetails() placeholder!");
Args = A;
});
return std::make_pair(std::move(Ctx), Args);
}

// Fallback metric that measures latencies for spans without an explicit latency
Expand All @@ -307,8 +318,9 @@ constexpr Metric SpanLatency("span_latency", Metric::Distribution, "span_name");
// beginSpan() context is destroyed, when the tracing engine will consume them.
Span::Span(llvm::Twine Name) : Span(Name, SpanLatency) {}
Span::Span(llvm::Twine Name, const Metric &LatencyMetric)
: Args(T ? new llvm::json::Object() : nullptr),
RestoreCtx(makeSpanContext(Name, Args, LatencyMetric)) {}
: Span(makeSpanContext(Name, LatencyMetric)) {}
Span::Span(std::pair<Context, llvm::json::Object *> Pair)
: Args(Pair.second), RestoreCtx(std::move(Pair.first)) {}

Span::~Span() {
if (T)
Expand All @@ -323,7 +335,9 @@ void Metric::record(double Value, llvm::StringRef Label) const {
T->record(*this, Value, Label);
}

Context EventTracer::beginSpan(llvm::StringRef Name, llvm::json::Object *Args) {
Context EventTracer::beginSpan(
llvm::StringRef Name,
llvm::function_ref<void(llvm::json::Object *)> AttachDetails) {
return Context::current().clone();
}
} // namespace trace
Expand Down
14 changes: 12 additions & 2 deletions clang-tools-extra/clangd/support/Trace.h
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,13 @@ class EventTracer {
/// Returns a derived context that will be destroyed when the event ends.
/// Usually implementations will store an object in the returned context
/// whose destructor records the end of the event.
/// The args are *Args, only complete when the event ends.
virtual Context beginSpan(llvm::StringRef Name, llvm::json::Object *Args);
/// The tracer may capture event details provided in SPAN_ATTACH() calls.
/// In this case it should call AttachDetails(), and pass in an empty Object
/// to hold them. This Object should be owned by the context, and the data
/// will be complete by the time the context is destroyed.
virtual Context
beginSpan(llvm::StringRef Name,
llvm::function_ref<void(llvm::json::Object *)> AttachDetails);
// Called when a Span is destroyed (it may still be active on other threads).
// beginSpan() and endSpan() will always form a proper stack on each thread.
// The Context returned by beginSpan is active, but Args is not ready.
Expand Down Expand Up @@ -123,6 +128,9 @@ std::unique_ptr<EventTracer> createCSVMetricTracer(llvm::raw_ostream &OS);
/// Records a single instant event, associated with the current thread.
void log(const llvm::Twine &Name);

/// Returns true if there is an active tracer.
bool enabled();

/// Records an event whose duration is the lifetime of the Span object.
/// This lifetime is extended when the span's context is reused.
///
Expand All @@ -146,6 +154,8 @@ class Span {
llvm::json::Object *const Args;

private:
// Awkward constructor works around constant initialization.
Span(std::pair<Context, llvm::json::Object *>);
WithContext RestoreCtx;
};

Expand Down
2 changes: 1 addition & 1 deletion clang-tools-extra/clangd/tool/ClangdMain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -310,7 +310,7 @@ opt<bool> CrossFileRename{
opt<bool> RecoveryAST{
"recovery-ast",
cat(Features),
desc("Preserve expressions in AST for broken code (C++ only)."),
desc("Preserve expressions in AST for broken code."),
init(ClangdServer::Options().BuildRecoveryAST),
};

Expand Down
14 changes: 14 additions & 0 deletions clang-tools-extra/clangd/unittests/BackgroundIndexTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ using ::testing::AllOf;
using ::testing::Contains;
using ::testing::ElementsAre;
using ::testing::Not;
using ::testing::Pair;
using ::testing::UnorderedElementsAre;

namespace clang {
Expand Down Expand Up @@ -916,5 +917,18 @@ TEST(BackgroundQueueTest, Progress) {
EXPECT_EQ(S.LastIdle, 2000u);
}

TEST(BackgroundIndex, Profile) {
MockFS FS;
MockCompilationDatabase CDB;
BackgroundIndex Idx(FS, CDB, [](llvm::StringRef) { return nullptr; },
/*Opts=*/{});

llvm::BumpPtrAllocator Alloc;
MemoryTree MT(&Alloc);
Idx.profile(MT);
ASSERT_THAT(MT.children(),
UnorderedElementsAre(Pair("symbols", _), Pair("index", _)));
}

} // namespace clangd
} // namespace clang
1 change: 1 addition & 0 deletions clang-tools-extra/clangd/unittests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ add_unittest(ClangdUnitTests ClangdTests
support/ContextTests.cpp
support/FunctionTests.cpp
support/MarkupTests.cpp
support/MemoryTreeTests.cpp
support/ThreadingTests.cpp
support/TestTracer.cpp
support/TraceTests.cpp
Expand Down
22 changes: 21 additions & 1 deletion clang-tools-extra/clangd/unittests/ClangdTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#include "TestFS.h"
#include "TestTU.h"
#include "URI.h"
#include "support/MemoryTree.h"
#include "support/Path.h"
#include "support/Threading.h"
#include "clang/Config/config.h"
Expand All @@ -27,6 +28,7 @@
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringMap.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/Allocator.h"
#include "llvm/Support/Errc.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/Regex.h"
Expand All @@ -48,6 +50,7 @@ namespace clangd {
namespace {

using ::testing::AllOf;
using ::testing::Contains;
using ::testing::ElementsAre;
using ::testing::Field;
using ::testing::Gt;
Expand Down Expand Up @@ -565,7 +568,9 @@ int hello;
}

MATCHER_P4(Stats, Name, UsesMemory, PreambleBuilds, ASTBuilds, "") {
return arg.first() == Name && (arg.second.UsedBytes != 0) == UsesMemory &&
return arg.first() == Name &&
(arg.second.UsedBytesAST + arg.second.UsedBytesPreamble != 0) ==
UsesMemory &&
std::tie(arg.second.PreambleBuilds, ASTBuilds) ==
std::tie(PreambleBuilds, ASTBuilds);
}
Expand Down Expand Up @@ -1234,6 +1239,21 @@ TEST(ClangdServer, TidyOverrideTest) {
EXPECT_FALSE(DiagConsumer.HadDiagsInLastCallback);
}

TEST(ClangdServer, MemoryUsageTest) {
MockFS FS;
MockCompilationDatabase CDB;
ClangdServer Server(CDB, FS, ClangdServer::optsForTest());

auto FooCpp = testPath("foo.cpp");
Server.addDocument(FooCpp, "");
ASSERT_TRUE(Server.blockUntilIdleForTest());

llvm::BumpPtrAllocator Alloc;
MemoryTree MT(&Alloc);
Server.profile(MT);
ASSERT_TRUE(MT.children().count("tuscheduler"));
EXPECT_TRUE(MT.child("tuscheduler").children().count(FooCpp));
}
} // namespace
} // namespace clangd
} // namespace clang
4 changes: 0 additions & 4 deletions clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -111,8 +111,6 @@ CodeCompleteResult completions(const TestTU &TU, Position Point,

MockFS FS;
auto Inputs = TU.inputs(FS);
Inputs.Opts.BuildRecoveryAST = true;
Inputs.Opts.PreserveRecoveryASTType = true;
IgnoreDiagnostics Diags;
auto CI = buildCompilerInvocation(Inputs, Diags);
if (!CI) {
Expand Down Expand Up @@ -1100,8 +1098,6 @@ SignatureHelp signatures(llvm::StringRef Text, Position Point,
MockFS FS;
auto Inputs = TU.inputs(FS);
Inputs.Index = Index.get();
Inputs.Opts.BuildRecoveryAST = true;
Inputs.Opts.PreserveRecoveryASTType = true;
IgnoreDiagnostics Diags;
auto CI = buildCompilerInvocation(Inputs, Diags);
if (!CI) {
Expand Down
56 changes: 56 additions & 0 deletions clang-tools-extra/clangd/unittests/FileIndexTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,20 +22,25 @@
#include "index/Relation.h"
#include "index/Serialization.h"
#include "index/Symbol.h"
#include "index/SymbolID.h"
#include "support/Threading.h"
#include "clang/Frontend/CompilerInvocation.h"
#include "clang/Frontend/Utils.h"
#include "clang/Index/IndexSymbol.h"
#include "clang/Lex/Preprocessor.h"
#include "clang/Tooling/CompilationDatabase.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/Support/Allocator.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include <utility>
#include <vector>

using ::testing::_;
using ::testing::AllOf;
using ::testing::Contains;
using ::testing::ElementsAre;
using ::testing::Gt;
using ::testing::IsEmpty;
using ::testing::Pair;
using ::testing::UnorderedElementsAre;
Expand Down Expand Up @@ -88,6 +93,13 @@ std::unique_ptr<RefSlab> refSlab(const SymbolID &ID, const char *Path) {
return std::make_unique<RefSlab>(std::move(Slab).build());
}

std::unique_ptr<RelationSlab> relSlab(llvm::ArrayRef<const Relation> Rels) {
RelationSlab::Builder RelBuilder;
for (auto &Rel : Rels)
RelBuilder.insert(Rel);
return std::make_unique<RelationSlab>(std::move(RelBuilder).build());
}

TEST(FileSymbolsTest, UpdateAndGet) {
FileSymbols FS;
EXPECT_THAT(runFuzzyFind(*FS.buildIndex(IndexType::Light), ""), IsEmpty());
Expand Down Expand Up @@ -643,6 +655,50 @@ TEST(FileShardedIndexTest, Sharding) {
EXPECT_TRUE(Shard->Cmd.hasValue());
}
}

TEST(FileIndexTest, Profile) {
FileIndex FI;

auto FileName = testPath("foo.cpp");
auto AST = TestTU::withHeaderCode("int a;").build();
FI.updateMain(FileName, AST);
FI.updatePreamble(FileName, "v1", AST.getASTContext(),
AST.getPreprocessorPtr(), AST.getCanonicalIncludes());

llvm::BumpPtrAllocator Alloc;
MemoryTree MT(&Alloc);
FI.profile(MT);
ASSERT_THAT(MT.children(),
UnorderedElementsAre(Pair("preamble", _), Pair("main_file", _)));

ASSERT_THAT(MT.child("preamble").children(),
UnorderedElementsAre(Pair("index", _), Pair("symbols", _)));
ASSERT_THAT(MT.child("main_file").children(),
UnorderedElementsAre(Pair("index", _), Pair("symbols", _)));

ASSERT_THAT(MT.child("preamble").child("index").total(), Gt(0U));
ASSERT_THAT(MT.child("main_file").child("index").total(), Gt(0U));
}

TEST(FileSymbolsTest, Profile) {
FileSymbols FS;
FS.update("f1", numSlab(1, 2), nullptr, nullptr, false);
FS.update("f2", nullptr, refSlab(SymbolID("1"), "f1"), nullptr, false);
FS.update("f3", nullptr, nullptr,
relSlab({{SymbolID("1"), RelationKind::BaseOf, SymbolID("2")}}),
false);
llvm::BumpPtrAllocator Alloc;
MemoryTree MT(&Alloc);
FS.profile(MT);
ASSERT_THAT(MT.children(), UnorderedElementsAre(Pair("f1", _), Pair("f2", _),
Pair("f3", _)));
EXPECT_THAT(MT.child("f1").children(), ElementsAre(Pair("symbols", _)));
EXPECT_THAT(MT.child("f1").total(), Gt(0U));
EXPECT_THAT(MT.child("f2").children(), ElementsAre(Pair("references", _)));
EXPECT_THAT(MT.child("f2").total(), Gt(0U));
EXPECT_THAT(MT.child("f3").children(), ElementsAre(Pair("relations", _)));
EXPECT_THAT(MT.child("f3").total(), Gt(0U));
}
} // namespace
} // namespace clangd
} // namespace clang
18 changes: 13 additions & 5 deletions clang-tools-extra/clangd/unittests/FindSymbolsTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -639,19 +639,27 @@ TEST(DocumentSymbols, FromMacro) {
#define FF(name) \
class name##_Test {};
$expansion[[FF]](abc);
$expansion1[[FF]](abc);
#define FF2() \
class $spelling[[Test]] {};
class Test {};
FF2();
$expansion2[[FF2]]();
#define FF3() \
void waldo()
$fullDef[[FF3() {
int var = 42;
}]]
)");
TU.Code = Main.code().str();
EXPECT_THAT(
getSymbols(TU.build()),
ElementsAre(
AllOf(WithName("abc_Test"), SymNameRange(Main.range("expansion"))),
AllOf(WithName("Test"), SymNameRange(Main.range("spelling")))));
AllOf(WithName("abc_Test"), SymNameRange(Main.range("expansion1"))),
AllOf(WithName("Test"), SymNameRange(Main.range("expansion2"))),
AllOf(WithName("waldo"), SymRange(Main.range("fullDef")))));
}

TEST(DocumentSymbols, FuncTemplates) {
Expand Down
59 changes: 59 additions & 0 deletions clang-tools-extra/clangd/unittests/FindTargetTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,17 @@ TEST_F(TargetDeclTest, Exprs) {
EXPECT_DECLS("LabelStmt", "label:");
}

TEST_F(TargetDeclTest, RecoveryForC) {
Flags = {"-xc", "-Xclang", "-frecovery-ast"};
Code = R"cpp(
// error-ok: testing behavior on broken code
// int f();
int f(int);
int x = [[f]]();
)cpp";
EXPECT_DECLS("DeclRefExpr", "int f(int)");
}

TEST_F(TargetDeclTest, Recovery) {
Code = R"cpp(
// error-ok: testing behavior on broken code
Expand Down Expand Up @@ -728,6 +739,54 @@ TEST_F(TargetDeclTest, DependentExprs) {
"template <typename T> T convert() const");
}

TEST_F(TargetDeclTest, DependentTypes) {
Flags = {"-fno-delayed-template-parsing"};

// Heuristic resolution of dependent type name
Code = R"cpp(
template <typename>
struct A { struct B {}; };

template <typename T>
void foo(typename A<T>::[[B]]);
)cpp";
EXPECT_DECLS("DependentNameTypeLoc", "struct B");

// Heuristic resolution of dependent type name which doesn't get a TypeLoc
Code = R"cpp(
template <typename>
struct A { struct B { struct C {}; }; };

template <typename T>
void foo(typename A<T>::[[B]]::C);
)cpp";
EXPECT_DECLS("NestedNameSpecifierLoc", "struct B");

// Heuristic resolution of dependent type name whose qualifier is also
// dependent
Code = R"cpp(
template <typename>
struct A { struct B { struct C {}; }; };

template <typename T>
void foo(typename A<T>::B::[[C]]);
)cpp";
EXPECT_DECLS("DependentNameTypeLoc", "struct C");

// Heuristic resolution of dependent template name
Code = R"cpp(
template <typename>
struct A {
template <typename> struct B {};
};

template <typename T>
void foo(typename A<T>::template [[B]]<int>);
)cpp";
EXPECT_DECLS("DependentTemplateSpecializationTypeLoc",
"template <typename> struct B");
}

TEST_F(TargetDeclTest, ObjC) {
Flags = {"-xobjective-c"};
Code = R"cpp(
Expand Down
6 changes: 4 additions & 2 deletions clang-tools-extra/clangd/unittests/ParsedASTTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,8 @@ MATCHER_P(DeclNamed, Name, "") {

MATCHER_P(DeclKind, Kind, "") {
if (NamedDecl *ND = dyn_cast<NamedDecl>(arg))
return ND->getDeclKindName() == Kind;
if (ND->getDeclKindName() == Kind)
return true;
if (auto *Stream = result_listener->stream()) {
llvm::raw_os_ostream OS(*Stream);
arg->dump(OS);
Expand Down Expand Up @@ -103,7 +104,8 @@ MATCHER(EqInc, "") {
std::tie(Expected.HashLine, Expected.Written);
}

TEST(ParsedASTTest, TopLevelDecls) {
// FIXME: figure out why it fails on clang-ppc64le-rhel buildbot.
TEST(ParsedASTTest, DISABLED_TopLevelDecls) {
TestTU TU;
TU.HeaderCode = R"(
int header1();
Expand Down
2 changes: 0 additions & 2 deletions clang-tools-extra/clangd/unittests/TestTU.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,6 @@ ParseInputs TestTU::inputs(MockFS &FS) const {
FS.OverlayRealFileSystemForModules = true;
Inputs.TFS = &FS;
Inputs.Opts = ParseOptions();
Inputs.Opts.BuildRecoveryAST = true;
Inputs.Opts.PreserveRecoveryASTType = true;
Inputs.Opts.ClangTidyOpts.Checks = ClangTidyChecks;
Inputs.Opts.ClangTidyOpts.WarningsAsErrors = ClangTidyWarningsAsErrors;
Inputs.Index = ExternalIndex;
Expand Down
180 changes: 90 additions & 90 deletions clang-tools-extra/clangd/unittests/TweakTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -215,26 +215,26 @@ TEST_F(ExtractVariableTest, Test) {
int x = [[1]], y = [[a + 1]], a = [[1]], z = a + 1;
// if without else
if([[1]])
a = [[1]];
a = [[1]] + 1;
// if with else
if(a < [[3]])
if(a == [[4]])
a = [[5]];
a = [[5]] + 1;
else
a = [[5]];
a = [[5]] + 1;
else if (a < [[4]])
a = [[4]];
a = [[4]] + 1;
else
a = [[5]];
a = [[5]] + 1;
// for loop
for(a = [[1]]; a > [[[[3]] + [[4]]]]; a++)
a = [[2]];
for(a = [[1]] + 1; a > [[[[3]] + [[4]]]]; a++)
a = [[2]] + 1;
// while
while(a < [[1]])
a = [[1]];
a = [[1]] + 1;
// do while
do
a = [[1]];
a = [[1]] + 1;
while(a < [[3]]);
}
)cpp";
Expand Down Expand Up @@ -291,6 +291,7 @@ TEST_F(ExtractVariableTest, Test) {
xyz([[a *= 5]]);
// Variable DeclRefExpr
a = [[b]];
a = [[xyz()]];
// statement expression
[[xyz()]];
while (a)
Expand All @@ -304,172 +305,171 @@ TEST_F(ExtractVariableTest, Test) {
EXPECT_UNAVAILABLE(UnavailableCases);

// vector of pairs of input and output strings
const std::vector<std::pair<std::string, std::string>>
InputOutputs = {
// extraction from variable declaration/assignment
{R"cpp(void varDecl() {
const std::vector<std::pair<std::string, std::string>> InputOutputs = {
// extraction from variable declaration/assignment
{R"cpp(void varDecl() {
int a = 5 * (4 + (3 [[- 1)]]);
})cpp",
R"cpp(void varDecl() {
R"cpp(void varDecl() {
auto dummy = (3 - 1); int a = 5 * (4 + dummy);
})cpp"},
// FIXME: extraction from switch case
/*{R"cpp(void f(int a) {
if(1)
while(a < 1)
switch (1) {
case 1:
a = [[1 + 2]];
break;
default:
break;
}
})cpp",
R"cpp(void f(int a) {
auto dummy = 1 + 2; if(1)
while(a < 1)
switch (1) {
case 1:
a = dummy;
break;
default:
break;
}
})cpp"},*/
// Macros
{R"cpp(#define PLUS(x) x++
// FIXME: extraction from switch case
/*{R"cpp(void f(int a) {
if(1)
while(a < 1)
switch (1) {
case 1:
a = [[1 + 2]];
break;
default:
break;
}
})cpp",
R"cpp(void f(int a) {
auto dummy = 1 + 2; if(1)
while(a < 1)
switch (1) {
case 1:
a = dummy;
break;
default:
break;
}
})cpp"},*/
// Macros
{R"cpp(#define PLUS(x) x++
void f(int a) {
int y = PLUS([[1+a]]);
})cpp",
/*FIXME: It should be extracted like this.
R"cpp(#define PLUS(x) x++
void f(int a) {
auto dummy = 1+a; int y = PLUS(dummy);
})cpp"},*/
R"cpp(#define PLUS(x) x++
/*FIXME: It should be extracted like this.
R"cpp(#define PLUS(x) x++
void f(int a) {
auto dummy = 1+a; int y = PLUS(dummy);
})cpp"},*/
R"cpp(#define PLUS(x) x++
void f(int a) {
auto dummy = PLUS(1+a); int y = dummy;
})cpp"},
// ensure InsertionPoint isn't inside a macro
{R"cpp(#define LOOP(x) while (1) {a = x;}
// ensure InsertionPoint isn't inside a macro
{R"cpp(#define LOOP(x) while (1) {a = x;}
void f(int a) {
if(1)
LOOP(5 + [[3]])
})cpp",
R"cpp(#define LOOP(x) while (1) {a = x;}
R"cpp(#define LOOP(x) while (1) {a = x;}
void f(int a) {
auto dummy = 3; if(1)
LOOP(5 + dummy)
})cpp"},
{R"cpp(#define LOOP(x) do {x;} while(1);
{R"cpp(#define LOOP(x) do {x;} while(1);
void f(int a) {
if(1)
LOOP(5 + [[3]])
})cpp",
R"cpp(#define LOOP(x) do {x;} while(1);
R"cpp(#define LOOP(x) do {x;} while(1);
void f(int a) {
auto dummy = 3; if(1)
LOOP(5 + dummy)
})cpp"},
// attribute testing
{R"cpp(void f(int a) {
[ [gsl::suppress("type")] ] for (;;) a = [[1]];
// attribute testing
{R"cpp(void f(int a) {
[ [gsl::suppress("type")] ] for (;;) a = [[1]] + 1;
})cpp",
R"cpp(void f(int a) {
auto dummy = 1; [ [gsl::suppress("type")] ] for (;;) a = dummy;
R"cpp(void f(int a) {
auto dummy = 1; [ [gsl::suppress("type")] ] for (;;) a = dummy + 1;
})cpp"},
// MemberExpr
{R"cpp(class T {
// MemberExpr
{R"cpp(class T {
T f() {
return [[T().f()]].f();
}
};)cpp",
R"cpp(class T {
R"cpp(class T {
T f() {
auto dummy = T().f(); return dummy.f();
}
};)cpp"},
// Function DeclRefExpr
{R"cpp(int f() {
// Function DeclRefExpr
{R"cpp(int f() {
return [[f]]();
})cpp",
R"cpp(int f() {
R"cpp(int f() {
auto dummy = f(); return dummy;
})cpp"},
// FIXME: Wrong result for \[\[clang::uninitialized\]\] int b = [[1]];
// since the attr is inside the DeclStmt and the bounds of
// DeclStmt don't cover the attribute.
// FIXME: Wrong result for \[\[clang::uninitialized\]\] int b = [[1]];
// since the attr is inside the DeclStmt and the bounds of
// DeclStmt don't cover the attribute.

// Binary subexpressions
{R"cpp(void f() {
// Binary subexpressions
{R"cpp(void f() {
int x = 1 + [[2 + 3 + 4]] + 5;
})cpp",
R"cpp(void f() {
R"cpp(void f() {
auto dummy = 2 + 3 + 4; int x = 1 + dummy + 5;
})cpp"},
{R"cpp(void f() {
{R"cpp(void f() {
int x = [[1 + 2 + 3]] + 4 + 5;
})cpp",
R"cpp(void f() {
R"cpp(void f() {
auto dummy = 1 + 2 + 3; int x = dummy + 4 + 5;
})cpp"},
{R"cpp(void f() {
{R"cpp(void f() {
int x = 1 + 2 + [[3 + 4 + 5]];
})cpp",
R"cpp(void f() {
R"cpp(void f() {
auto dummy = 3 + 4 + 5; int x = 1 + 2 + dummy;
})cpp"},
// Non-associative operations have no special support
{R"cpp(void f() {
// Non-associative operations have no special support
{R"cpp(void f() {
int x = 1 - [[2 - 3 - 4]] - 5;
})cpp",
R"cpp(void f() {
R"cpp(void f() {
auto dummy = 1 - 2 - 3 - 4; int x = dummy - 5;
})cpp"},
// A mix of associative operators isn't associative.
{R"cpp(void f() {
// A mix of associative operators isn't associative.
{R"cpp(void f() {
int x = 0 + 1 * [[2 + 3]] * 4 + 5;
})cpp",
R"cpp(void f() {
R"cpp(void f() {
auto dummy = 1 * 2 + 3 * 4; int x = 0 + dummy + 5;
})cpp"},
// Overloaded operators are supported, we assume associativity
// as if they were built-in.
{R"cpp(struct S {
// Overloaded operators are supported, we assume associativity
// as if they were built-in.
{R"cpp(struct S {
S(int);
};
S operator+(S, S);

void f() {
S x = S(1) + [[S(2) + S(3) + S(4)]] + S(5);
})cpp",
R"cpp(struct S {
R"cpp(struct S {
S(int);
};
S operator+(S, S);

void f() {
auto dummy = S(2) + S(3) + S(4); S x = S(1) + dummy + S(5);
})cpp"},
// Don't try to analyze across macro boundaries
// FIXME: it'd be nice to do this someday (in a safe way)
{R"cpp(#define ECHO(X) X
// Don't try to analyze across macro boundaries
// FIXME: it'd be nice to do this someday (in a safe way)
{R"cpp(#define ECHO(X) X
void f() {
int x = 1 + [[ECHO(2 + 3) + 4]] + 5;
})cpp",
R"cpp(#define ECHO(X) X
R"cpp(#define ECHO(X) X
void f() {
auto dummy = 1 + ECHO(2 + 3) + 4; int x = dummy + 5;
})cpp"},
{R"cpp(#define ECHO(X) X
{R"cpp(#define ECHO(X) X
void f() {
int x = 1 + [[ECHO(2) + ECHO(3) + 4]] + 5;
})cpp",
R"cpp(#define ECHO(X) X
R"cpp(#define ECHO(X) X
void f() {
auto dummy = 1 + ECHO(2) + ECHO(3) + 4; int x = dummy + 5;
})cpp"},
};
};
for (const auto &IO : InputOutputs) {
EXPECT_EQ(IO.second, apply(IO.first)) << IO.first;
}
Expand Down Expand Up @@ -720,7 +720,7 @@ TEST_F(ExtractFunctionTest, ControlFlow) {

TEST_F(ExtractFunctionTest, ExistingReturnStatement) {
Context = File;
const char* Before = R"cpp(
const char *Before = R"cpp(
bool lucky(int N);
int getNum(bool Superstitious, int Min, int Max) {
if (Superstitious) [[{
Expand All @@ -735,7 +735,7 @@ TEST_F(ExtractFunctionTest, ExistingReturnStatement) {
)cpp";
// FIXME: min/max should be by value.
// FIXME: avoid emitting redundant braces
const char* After = R"cpp(
const char *After = R"cpp(
bool lucky(int N);
int extracted(int &Min, int &Max) {
{
Expand Down
121 changes: 121 additions & 0 deletions clang-tools-extra/clangd/unittests/support/MemoryTreeTests.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
//===-- MemoryTreeTests.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 "support/MemoryTree.h"
#include "support/TestTracer.h"
#include "support/Trace.h"
#include "llvm/Support/Allocator.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include <ostream>

namespace clang {
namespace clangd {
namespace {
using testing::Contains;
using testing::ElementsAre;
using testing::IsEmpty;
using testing::UnorderedElementsAre;

MATCHER_P2(WithNameAndSize, Name, Size, "") {
return arg.first == Name &&
arg.getSecond().total() == static_cast<size_t>(Size);
}

TEST(MemoryTree, Basics) {
MemoryTree MT;
EXPECT_EQ(MT.total(), 0U);
EXPECT_THAT(MT.children(), IsEmpty());

MT.addUsage(42);
EXPECT_EQ(MT.total(), 42U);
EXPECT_THAT(MT.children(), IsEmpty());

MT.child("leaf").addUsage(1);
EXPECT_EQ(MT.total(), 43U);
EXPECT_THAT(MT.children(), UnorderedElementsAre(WithNameAndSize("leaf", 1)));

// child should be idempotent.
MT.child("leaf").addUsage(1);
EXPECT_EQ(MT.total(), 44U);
EXPECT_THAT(MT.children(), UnorderedElementsAre(WithNameAndSize("leaf", 2)));
}

TEST(MemoryTree, DetailedNodesWithoutDetails) {
MemoryTree MT;
MT.detail("should_be_ignored").addUsage(2);
EXPECT_THAT(MT.children(), IsEmpty());
EXPECT_EQ(MT.total(), 2U);

// Make sure children from details are merged.
MT.detail("first_detail").child("leaf").addUsage(1);
MT.detail("second_detail").child("leaf").addUsage(1);
EXPECT_THAT(MT.children(), Contains(WithNameAndSize("leaf", 2)));
}

TEST(MemoryTree, DetailedNodesWithDetails) {
llvm::BumpPtrAllocator Alloc;
MemoryTree MT(&Alloc);

{
auto &Detail = MT.detail("first_detail");
Detail.child("leaf").addUsage(1);
EXPECT_THAT(MT.children(), Contains(WithNameAndSize("first_detail", 1)));
EXPECT_THAT(Detail.children(), Contains(WithNameAndSize("leaf", 1)));
}

{
auto &Detail = MT.detail("second_detail");
Detail.child("leaf").addUsage(1);
EXPECT_THAT(MT.children(), Contains(WithNameAndSize("second_detail", 1)));
EXPECT_THAT(Detail.children(), Contains(WithNameAndSize("leaf", 1)));
}
}

TEST(MemoryTree, Record) {
trace::TestTracer Tracer;
static constexpr llvm::StringLiteral MetricName = "memory_usage";
static constexpr trace::Metric OutMetric(MetricName, trace::Metric::Value,
"component_name");
auto AddNodes = [](MemoryTree Root) {
Root.child("leaf").addUsage(1);

{
auto &Detail = Root.detail("detail");
Detail.addUsage(1);
Detail.child("leaf").addUsage(1);
auto &Child = Detail.child("child");
Child.addUsage(1);
Child.child("leaf").addUsage(1);
}

{
auto &Child = Root.child("child");
Child.addUsage(1);
Child.child("leaf").addUsage(1);
}
return Root;
};

llvm::BumpPtrAllocator Alloc;
record(AddNodes(MemoryTree(&Alloc)), "root", OutMetric);
EXPECT_THAT(Tracer.takeMetric(MetricName, "root"), ElementsAre(7));
EXPECT_THAT(Tracer.takeMetric(MetricName, "root.leaf"), ElementsAre(1));
EXPECT_THAT(Tracer.takeMetric(MetricName, "root.detail"), ElementsAre(4));
EXPECT_THAT(Tracer.takeMetric(MetricName, "root.detail.leaf"),
ElementsAre(1));
EXPECT_THAT(Tracer.takeMetric(MetricName, "root.detail.child"),
ElementsAre(2));
EXPECT_THAT(Tracer.takeMetric(MetricName, "root.detail.child.leaf"),
ElementsAre(1));
EXPECT_THAT(Tracer.takeMetric(MetricName, "root.child"), ElementsAre(2));
EXPECT_THAT(Tracer.takeMetric(MetricName, "root.child.leaf"), ElementsAre(1));
}
} // namespace
} // namespace clangd
} // namespace clang
5 changes: 5 additions & 0 deletions clang-tools-extra/clangd/unittests/support/TraceTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,11 @@ TEST_F(CSVMetricsTracerTest, Escaping) {
StartsWith("d,dist,\"a\nb\",1"), ""));
}

TEST_F(CSVMetricsTracerTest, IgnoresArgs) {
trace::Span Tracer("Foo");
EXPECT_EQ(nullptr, Tracer.Args);
}

} // namespace
} // namespace clangd
} // namespace clang
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,10 @@ misc-unused-alias-decls


Finds unused namespace alias declarations.

.. code-block:: c++

namespace my_namespace {
class C {};
}
namespace unused_alias = ::my_namespace;
15 changes: 8 additions & 7 deletions clang-tools-extra/modularize/PreprocessorTracker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -312,10 +312,10 @@ static std::string getSourceString(clang::Preprocessor &PP,
// Retrieve source line from file image given a location.
static std::string getSourceLine(clang::Preprocessor &PP,
clang::SourceLocation Loc) {
const llvm::MemoryBuffer *MemBuffer =
PP.getSourceManager().getBuffer(PP.getSourceManager().getFileID(Loc));
const char *Buffer = MemBuffer->getBufferStart();
const char *BufferEnd = MemBuffer->getBufferEnd();
llvm::MemoryBufferRef MemBuffer = PP.getSourceManager().getBufferOrFake(
PP.getSourceManager().getFileID(Loc));
const char *Buffer = MemBuffer.getBufferStart();
const char *BufferEnd = MemBuffer.getBufferEnd();
const char *BeginPtr = PP.getSourceManager().getCharacterData(Loc);
const char *EndPtr = BeginPtr;
while (BeginPtr > Buffer) {
Expand All @@ -338,9 +338,10 @@ static std::string getSourceLine(clang::Preprocessor &PP,
// Retrieve source line from file image given a file ID and line number.
static std::string getSourceLine(clang::Preprocessor &PP, clang::FileID FileID,
int Line) {
const llvm::MemoryBuffer *MemBuffer = PP.getSourceManager().getBuffer(FileID);
const char *Buffer = MemBuffer->getBufferStart();
const char *BufferEnd = MemBuffer->getBufferEnd();
llvm::MemoryBufferRef MemBuffer =
PP.getSourceManager().getBufferOrFake(FileID);
const char *Buffer = MemBuffer.getBufferStart();
const char *BufferEnd = MemBuffer.getBufferEnd();
const char *BeginPtr = Buffer;
const char *EndPtr = BufferEnd;
int LineCounter = 1;
Expand Down
226 changes: 175 additions & 51 deletions clang-tools-extra/unittests/clang-tidy/IncludeInserterTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,10 @@ namespace {

class IncludeInserterCheckBase : public ClangTidyCheck {
public:
IncludeInserterCheckBase(StringRef CheckName, ClangTidyContext *Context)
: ClangTidyCheck(CheckName, Context) {}
IncludeInserterCheckBase(StringRef CheckName, ClangTidyContext *Context,
utils::IncludeSorter::IncludeStyle Style =
utils::IncludeSorter::IS_Google)
: ClangTidyCheck(CheckName, Context), Inserter(Style) {}

void registerPPCallbacks(const SourceManager &SM, Preprocessor *PP,
Preprocessor *ModuleExpanderPP) override {
Expand All @@ -50,7 +52,7 @@ class IncludeInserterCheckBase : public ClangTidyCheck {

virtual std::vector<StringRef> headersToInclude() const = 0;

utils::IncludeInserter Inserter{utils::IncludeSorter::IS_Google};
utils::IncludeInserter Inserter;
};

class NonSystemHeaderInserterCheck : public IncludeInserterCheckBase {
Expand Down Expand Up @@ -111,6 +113,42 @@ class InvalidIncludeInserterCheck : public IncludeInserterCheckBase {
}
};

class ObjCEarlyInAlphabetHeaderInserterCheck : public IncludeInserterCheckBase {
public:
ObjCEarlyInAlphabetHeaderInserterCheck(StringRef CheckName,
ClangTidyContext *Context)
: IncludeInserterCheckBase(CheckName, Context,
utils::IncludeSorter::IS_Google_ObjC) {}

std::vector<StringRef> headersToInclude() const override {
return {"a/header.h"};
}
};

class ObjCCategoryHeaderInserterCheck : public IncludeInserterCheckBase {
public:
ObjCCategoryHeaderInserterCheck(StringRef CheckName,
ClangTidyContext *Context)
: IncludeInserterCheckBase(CheckName, Context,
utils::IncludeSorter::IS_Google_ObjC) {}

std::vector<StringRef> headersToInclude() const override {
return {"clang_tidy/tests/insert_includes_test_header+foo.h"};
}
};

class ObjCGeneratedHeaderInserterCheck : public IncludeInserterCheckBase {
public:
ObjCGeneratedHeaderInserterCheck(StringRef CheckName,
ClangTidyContext *Context)
: IncludeInserterCheckBase(CheckName, Context,
utils::IncludeSorter::IS_Google_ObjC) {}

std::vector<StringRef> headersToInclude() const override {
return {"clang_tidy/tests/generated_file.proto.h"};
}
};

template <typename Check>
std::string runCheckOnCode(StringRef Code, StringRef Filename) {
std::vector<ClangTidyError> Errors;
Expand All @@ -120,12 +158,20 @@ std::string runCheckOnCode(StringRef Code, StringRef Filename) {
{"clang_tidy/tests/"
"insert_includes_test_header.h",
"\n"},
// ObjC category.
{"clang_tidy/tests/"
"insert_includes_test_header+foo.h",
"\n"},
// Non system headers
{"a/header.h", "\n"},
{"path/to/a/header.h", "\n"},
{"path/to/z/header.h", "\n"},
{"path/to/header.h", "\n"},
{"path/to/header2.h", "\n"},
// Generated headers
{"clang_tidy/tests/"
"generated_file.proto.h",
"\n"},
// Fake system headers.
{"stdlib.h", "\n"},
{"unistd.h", "\n"},
Expand Down Expand Up @@ -160,9 +206,9 @@ void foo() {
int a = 0;
})";

EXPECT_EQ(PostCode, runCheckOnCode<NonSystemHeaderInserterCheck>(
PreCode, "clang_tidy/tests/"
"insert_includes_test_input2.cc"));
EXPECT_EQ(PostCode,
runCheckOnCode<NonSystemHeaderInserterCheck>(
PreCode, "clang_tidy/tests/insert_includes_test_input2.cc"));
}

TEST(IncludeInserterTest, InsertMultipleIncludesAndDeduplicate) {
Expand Down Expand Up @@ -191,9 +237,9 @@ void foo() {
int a = 0;
})";

EXPECT_EQ(PostCode, runCheckOnCode<MultipleHeaderInserterCheck>(
PreCode, "clang_tidy/tests/"
"insert_includes_test_input2.cc"));
EXPECT_EQ(PostCode,
runCheckOnCode<MultipleHeaderInserterCheck>(
PreCode, "clang_tidy/tests/insert_includes_test_input2.cc"));
}

TEST(IncludeInserterTest, InsertBeforeFirstNonSystemInclude) {
Expand Down Expand Up @@ -221,9 +267,9 @@ void foo() {
int a = 0;
})";

EXPECT_EQ(PostCode, runCheckOnCode<NonSystemHeaderInserterCheck>(
PreCode, "clang_tidy/tests/"
"insert_includes_test_input2.cc"));
EXPECT_EQ(PostCode,
runCheckOnCode<NonSystemHeaderInserterCheck>(
PreCode, "clang_tidy/tests/insert_includes_test_input2.cc"));
}

TEST(IncludeInserterTest, InsertBetweenNonSystemIncludes) {
Expand Down Expand Up @@ -253,9 +299,9 @@ void foo() {
int a = 0;
})";

EXPECT_EQ(PostCode, runCheckOnCode<NonSystemHeaderInserterCheck>(
PreCode, "clang_tidy/tests/"
"insert_includes_test_input2.cc"));
EXPECT_EQ(PostCode,
runCheckOnCode<NonSystemHeaderInserterCheck>(
PreCode, "clang_tidy/tests/insert_includes_test_input2.cc"));
}

TEST(IncludeInserterTest, NonSystemIncludeAlreadyIncluded) {
Expand All @@ -272,9 +318,9 @@ TEST(IncludeInserterTest, NonSystemIncludeAlreadyIncluded) {
void foo() {
int a = 0;
})";
EXPECT_EQ(PreCode, runCheckOnCode<NonSystemHeaderInserterCheck>(
PreCode, "clang_tidy/tests/"
"insert_includes_test_input2.cc"));
EXPECT_EQ(PreCode,
runCheckOnCode<NonSystemHeaderInserterCheck>(
PreCode, "clang_tidy/tests/insert_includes_test_input2.cc"));
}

TEST(IncludeInserterTest, InsertNonSystemIncludeAfterLastCXXSystemInclude) {
Expand All @@ -299,9 +345,9 @@ void foo() {
int a = 0;
})";

EXPECT_EQ(PostCode, runCheckOnCode<NonSystemHeaderInserterCheck>(
PreCode, "clang_tidy/tests/"
"insert_includes_test_header.cc"));
EXPECT_EQ(PostCode,
runCheckOnCode<NonSystemHeaderInserterCheck>(
PreCode, "clang_tidy/tests/insert_includes_test_header.cc"));
}

TEST(IncludeInserterTest, InsertNonSystemIncludeAfterMainFileInclude) {
Expand All @@ -320,9 +366,9 @@ void foo() {
int a = 0;
})";

EXPECT_EQ(PostCode, runCheckOnCode<NonSystemHeaderInserterCheck>(
PreCode, "clang_tidy/tests/"
"insert_includes_test_header.cc"));
EXPECT_EQ(PostCode,
runCheckOnCode<NonSystemHeaderInserterCheck>(
PreCode, "clang_tidy/tests/insert_includes_test_header.cc"));
}

TEST(IncludeInserterTest, InsertCXXSystemIncludeAfterLastCXXSystemInclude) {
Expand Down Expand Up @@ -350,9 +396,9 @@ void foo() {
int a = 0;
})";

EXPECT_EQ(PostCode, runCheckOnCode<CXXSystemIncludeInserterCheck>(
PreCode, "clang_tidy/tests/"
"insert_includes_test_header.cc"));
EXPECT_EQ(PostCode,
runCheckOnCode<CXXSystemIncludeInserterCheck>(
PreCode, "clang_tidy/tests/insert_includes_test_header.cc"));
}

TEST(IncludeInserterTest, InsertCXXSystemIncludeBeforeFirstCXXSystemInclude) {
Expand All @@ -378,9 +424,9 @@ void foo() {
int a = 0;
})";

EXPECT_EQ(PostCode, runCheckOnCode<CXXSystemIncludeInserterCheck>(
PreCode, "clang_tidy/tests/"
"insert_includes_test_header.cc"));
EXPECT_EQ(PostCode,
runCheckOnCode<CXXSystemIncludeInserterCheck>(
PreCode, "clang_tidy/tests/insert_includes_test_header.cc"));
}

TEST(IncludeInserterTest, InsertCXXSystemIncludeBetweenCXXSystemIncludes) {
Expand Down Expand Up @@ -408,9 +454,9 @@ void foo() {
int a = 0;
})";

EXPECT_EQ(PostCode, runCheckOnCode<CXXSystemIncludeInserterCheck>(
PreCode, "clang_tidy/tests/"
"insert_includes_test_header.cc"));
EXPECT_EQ(PostCode,
runCheckOnCode<CXXSystemIncludeInserterCheck>(
PreCode, "clang_tidy/tests/insert_includes_test_header.cc"));
}

TEST(IncludeInserterTest, InsertCXXSystemIncludeAfterMainFileInclude) {
Expand All @@ -433,9 +479,9 @@ void foo() {
int a = 0;
})";

EXPECT_EQ(PostCode, runCheckOnCode<CXXSystemIncludeInserterCheck>(
PreCode, "clang_tidy/tests/"
"insert_includes_test_header.cc"));
EXPECT_EQ(PostCode,
runCheckOnCode<CXXSystemIncludeInserterCheck>(
PreCode, "clang_tidy/tests/insert_includes_test_header.cc"));
}

TEST(IncludeInserterTest, InsertCXXSystemIncludeAfterCSystemInclude) {
Expand All @@ -462,9 +508,9 @@ void foo() {
int a = 0;
})";

EXPECT_EQ(PostCode, runCheckOnCode<CXXSystemIncludeInserterCheck>(
PreCode, "clang_tidy/tests/"
"insert_includes_test_header.cc"));
EXPECT_EQ(PostCode,
runCheckOnCode<CXXSystemIncludeInserterCheck>(
PreCode, "clang_tidy/tests/insert_includes_test_header.cc"));
}

TEST(IncludeInserterTest, InsertCXXSystemIncludeBeforeNonSystemInclude) {
Expand All @@ -483,9 +529,10 @@ void foo() {
int a = 0;
})";

EXPECT_EQ(PostCode, runCheckOnCode<CXXSystemIncludeInserterCheck>(
PreCode, "devtools/cymbal/clang_tidy/tests/"
"insert_includes_test_header.cc"));
EXPECT_EQ(
PostCode,
runCheckOnCode<CXXSystemIncludeInserterCheck>(
PreCode, "repo/clang_tidy/tests/insert_includes_test_header.cc"));
}

TEST(IncludeInserterTest, InsertCSystemIncludeBeforeCXXSystemInclude) {
Expand All @@ -508,9 +555,10 @@ void foo() {
int a = 0;
})";

EXPECT_EQ(PostCode, runCheckOnCode<CSystemIncludeInserterCheck>(
PreCode, "devtools/cymbal/clang_tidy/tests/"
"insert_includes_test_header.cc"));
EXPECT_EQ(
PostCode,
runCheckOnCode<CSystemIncludeInserterCheck>(
PreCode, "repo/clang_tidy/tests/insert_includes_test_header.cc"));
}

TEST(IncludeInserterTest, InsertIncludeIfThereWasNoneBefore) {
Expand All @@ -525,9 +573,10 @@ void foo() {
int a = 0;
})";

EXPECT_EQ(PostCode, runCheckOnCode<CXXSystemIncludeInserterCheck>(
PreCode, "devtools/cymbal/clang_tidy/tests/"
"insert_includes_test_header.cc"));
EXPECT_EQ(
PostCode,
runCheckOnCode<CXXSystemIncludeInserterCheck>(
PreCode, "repo/clang_tidy/tests/insert_includes_test_header.cc"));
}

TEST(IncludeInserterTest, DontInsertDuplicateIncludeEvenIfMiscategorized) {
Expand Down Expand Up @@ -630,9 +679,84 @@ void foo() {
int a = 0;
})";

EXPECT_EQ(PostCode, runCheckOnCode<InvalidIncludeInserterCheck>(
PreCode, "clang_tidy/tests/"
"insert_includes_test_header.cc"));
EXPECT_EQ(PostCode,
runCheckOnCode<InvalidIncludeInserterCheck>(
PreCode, "clang_tidy/tests/insert_includes_test_header.cc"));
}

TEST(IncludeInserterTest, InsertHeaderObjectiveC) {
const char *PreCode = R"(
#import "clang_tidy/tests/insert_includes_test_header.h"

void foo() {
int a = 0;
})";
const char *PostCode = R"(
#import "clang_tidy/tests/insert_includes_test_header.h"

#import "a/header.h"

void foo() {
int a = 0;
})";

EXPECT_EQ(
PostCode,
runCheckOnCode<ObjCEarlyInAlphabetHeaderInserterCheck>(
PreCode, "repo/clang_tidy/tests/insert_includes_test_header.mm"));
}

TEST(IncludeInserterTest, InsertCategoryHeaderObjectiveC) {
const char *PreCode = R"(
#import "clang_tidy/tests/insert_includes_test_header.h"

void foo() {
int a = 0;
})";
const char *PostCode = R"(
#import "clang_tidy/tests/insert_includes_test_header.h"
#import "clang_tidy/tests/insert_includes_test_header+foo.h"

void foo() {
int a = 0;
})";

EXPECT_EQ(
PostCode,
runCheckOnCode<ObjCCategoryHeaderInserterCheck>(
PreCode, "repo/clang_tidy/tests/insert_includes_test_header.mm"));
}

TEST(IncludeInserterTest, InsertGeneratedHeaderObjectiveC) {
const char *PreCode = R"(
#import "clang_tidy/tests/insert_includes_test_header.h"

#include <list>
#include <map>

#include "path/to/a/header.h"

void foo() {
int a = 0;
})";
const char *PostCode = R"(
#import "clang_tidy/tests/insert_includes_test_header.h"

#include <list>
#include <map>

#include "path/to/a/header.h"

#import "clang_tidy/tests/generated_file.proto.h"

void foo() {
int a = 0;
})";

EXPECT_EQ(
PostCode,
runCheckOnCode<ObjCGeneratedHeaderInserterCheck>(
PreCode, "repo/clang_tidy/tests/insert_includes_test_header.mm"));
}

} // anonymous namespace
Expand Down
2 changes: 1 addition & 1 deletion clang/cmake/caches/Fuchsia-stage2.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ if(APPLE)
set(LIBCXX_ABI_VERSION 2 CACHE STRING "")
set(DARWIN_ios_ARCHS armv7;armv7s;arm64 CACHE STRING "")
set(DARWIN_iossim_ARCHS i386;x86_64 CACHE STRING "")
set(DARWIN_osx_ARCHS x86_64 CACHE STRING "")
set(DARWIN_osx_ARCHS arm64;x86_64 CACHE STRING "")
set(SANITIZER_MIN_OSX_VERSION 10.7 CACHE STRING "")
endif()

Expand Down
10 changes: 6 additions & 4 deletions clang/docs/ClangCommandLineReference.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2655,10 +2655,6 @@ Align selected branches (fused, jcc, jmp) within 32-byte boundary

.. option:: -mcmodel=<arg>, -mcmodel=medany (equivalent to -mcmodel=medium), -mcmodel=medlow (equivalent to -mcmodel=small)

.. option:: -mcode-object-v3, -mno-code-object-v3

Enable code object v3 (AMDGPU only)

.. option:: -mconsole<arg>

.. program:: clang1
Expand Down Expand Up @@ -2939,6 +2935,10 @@ Specify the size in bits of an SVE vector register. Defaults to the vector lengt

AMDGPU
------
.. option:: -mcode-object-v3, -mno-code-object-v3

Legacy option to specify code object v3 (AMDGPU only)

.. option:: -mcumode, -mno-cumode

CU wavefront execution mode is used (AMDGPU only)
Expand Down Expand Up @@ -3261,6 +3261,8 @@ X86

.. option:: -mgfni, -mno-gfni

.. option:: -mhreset, -mno-hreset

.. option:: -minvpcid, -mno-invpcid

.. option:: -mkl, -mno-kl
Expand Down
5 changes: 4 additions & 1 deletion clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,10 @@ X86 Support in Clang
- The x86 intrinsics ``__rorb``, ``__rorw``, ``__rord``, ``__rorq`, ``_rotr``,
``_rotwr`` and ``_lrotr`` may now be used within constant expressions.

- Support for -march=sapphirerapids was added.
- Support for ``-march=sapphirerapids`` was added.

- Support for ``-march=x86-64-v[234]`` has been added.
See :doc:`UsersManual` for details about these micro-architecture levels.

- The -mtune command line option is no longer ignored for X86. This can be used
to request microarchitectural optimizations independent on -march. -march=<cpu>
Expand Down
5 changes: 5 additions & 0 deletions clang/docs/ThinLTO.rst
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,11 @@ be reduced to ``N`` via:
- lld-link:
``/opt:lldltojobs=N``

Other possible values for ``N`` are:
- 0: Use one thread per physical core (default)
- 1: Use a single thread only (disable multi-threading)
- all: Use one thread per logical core (uses all hyper-threads)

Incremental
-----------
.. _incremental:
Expand Down
9 changes: 9 additions & 0 deletions clang/docs/UsersManual.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3201,6 +3201,15 @@ and the ABI remains 32-bit but the assembler emits instructions
appropriate for a CPU running in 16-bit mode, with address-size and
operand-size prefixes to enable 32-bit addressing and operations.

Several micro-architecture levels as specified by the x86-64 psABI are defined.
They are cumulative in the sense that features from previous levels are
implicitly included in later levels.

- ``-march=x86-64``: CMOV, CMPXCHG8B, FPU, FXSR, MMX, FXSR, SCE, SSE, SSE2
- ``-march=x86-64-v2``: (close to Nehalem) CMPXCHG16B, LAHF-SAHF, POPCNT, SSE3, SSE4.1, SSE4.2, SSSE3
- ``-march=x86-64-v3``: (close to Haswell) AVX, AVX2, BMI1, BMI2, F16C, FMA, LZCNT, MOVBE, XSAVE
- ``-march=x86-64-v4``: AVX512F, AVX512BW, AVX512CD, AVX512DQ, AVX512VL

ARM
^^^

Expand Down
11 changes: 6 additions & 5 deletions clang/include/clang/AST/APValue.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,10 @@
#include "llvm/ADT/APFixedPoint.h"
#include "llvm/ADT/APFloat.h"
#include "llvm/ADT/APSInt.h"
#include "llvm/ADT/FoldingSet.h"
#include "llvm/ADT/PointerIntPair.h"
#include "llvm/ADT/PointerUnion.h"
#include "llvm/ADT/FoldingSet.h"
#include "llvm/Support/AlignOf.h"

namespace clang {
class AddrLabelExpr;
Expand Down Expand Up @@ -150,7 +151,7 @@ class APValue {
static LValueBase getDynamicAlloc(DynamicAllocLValue LV, QualType Type);
static LValueBase getTypeInfo(TypeInfoLValue LV, QualType TypeInfo);

void profile(llvm::FoldingSetNodeID &ID) const;
void Profile(llvm::FoldingSetNodeID &ID) const;

template <class T>
bool is() const { return Ptr.is<T>(); }
Expand Down Expand Up @@ -218,7 +219,7 @@ class APValue {
}
uint64_t getAsArrayIndex() const { return Value; }

void profile(llvm::FoldingSetNodeID &ID) const;
void Profile(llvm::FoldingSetNodeID &ID) const;

friend bool operator==(LValuePathEntry A, LValuePathEntry B) {
return A.Value == B.Value;
Expand Down Expand Up @@ -362,10 +363,10 @@ class APValue {
/// Swaps the contents of this and the given APValue.
void swap(APValue &RHS);

/// Profile this value. There is no guarantee that values of different
/// profile this value. There is no guarantee that values of different
/// types will not produce the same profiled value, so the type should
/// typically also be profiled if it's not implied by the context.
void profile(llvm::FoldingSetNodeID &ID) const;
void Profile(llvm::FoldingSetNodeID &ID) const;

ValueKind getKind() const { return Kind; }

Expand Down
16 changes: 13 additions & 3 deletions clang/include/clang/AST/ASTContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,16 @@ struct TypeInfo {
: Width(Width), Align(Align), AlignIsRequired(AlignIsRequired) {}
};

struct TypeInfoChars {
CharUnits Width;
CharUnits Align;
bool AlignIsRequired : 1;

TypeInfoChars() : AlignIsRequired(false) {}
TypeInfoChars(CharUnits Width, CharUnits Align, bool AlignIsRequired)
: Width(Width), Align(Align), AlignIsRequired(AlignIsRequired) {}
};

/// Holds long-lived AST nodes (such as types and decls) that can be
/// referred to throughout the semantic analysis of a file.
class ASTContext : public RefCountedBase<ASTContext> {
Expand Down Expand Up @@ -2169,10 +2179,10 @@ class ASTContext : public RefCountedBase<ASTContext> {

// getTypeInfoDataSizeInChars - Return the size of a type, in chars. If the
// type is a record, its data size is returned.
std::pair<CharUnits, CharUnits> getTypeInfoDataSizeInChars(QualType T) const;
TypeInfoChars getTypeInfoDataSizeInChars(QualType T) const;

std::pair<CharUnits, CharUnits> getTypeInfoInChars(const Type *T) const;
std::pair<CharUnits, CharUnits> getTypeInfoInChars(QualType T) const;
TypeInfoChars getTypeInfoInChars(const Type *T) const;
TypeInfoChars getTypeInfoInChars(QualType T) const;

/// Determine if the alignment the type has was required using an
/// alignment attribute.
Expand Down
5 changes: 3 additions & 2 deletions clang/include/clang/AST/CommentSema.h
Original file line number Diff line number Diff line change
Expand Up @@ -238,8 +238,9 @@ class Sema {

/// Returns index of a function parameter with the name closest to a given
/// typo.
unsigned correctTypoInParmVarReference(StringRef Typo,
ArrayRef<const ParmVarDecl *> ParamVars);
static unsigned
correctTypoInParmVarReference(StringRef Typo,
ArrayRef<const ParmVarDecl *> ParamVars);

bool resolveTParamReference(StringRef Name,
const TemplateParameterList *TemplateParameters,
Expand Down
4 changes: 2 additions & 2 deletions clang/include/clang/AST/DeclTemplate.h
Original file line number Diff line number Diff line change
Expand Up @@ -2266,7 +2266,7 @@ class ClassTemplateDecl : public RedeclarableTemplateDecl {
/// Retrieve the set of partial specializations of this class
/// template.
llvm::FoldingSetVector<ClassTemplatePartialSpecializationDecl> &
getPartialSpecializations();
getPartialSpecializations() const;

ClassTemplateDecl(ASTContext &C, DeclContext *DC, SourceLocation L,
DeclarationName Name, TemplateParameterList *Params,
Expand Down Expand Up @@ -2363,7 +2363,7 @@ class ClassTemplateDecl : public RedeclarableTemplateDecl {

/// Retrieve the partial specializations as an ordered list.
void getPartialSpecializations(
SmallVectorImpl<ClassTemplatePartialSpecializationDecl *> &PS);
SmallVectorImpl<ClassTemplatePartialSpecializationDecl *> &PS) const;

/// Find a class template partial specialization with the given
/// type T.
Expand Down
8 changes: 8 additions & 0 deletions clang/include/clang/AST/OperationKinds.def
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,14 @@ CAST_OPERATION(IntegralToBoolean)
/// float f = i;
CAST_OPERATION(IntegralToFloating)

/// CK_FloatingToFixedPoint - Floating to fixed point.
/// _Accum a = f;
CAST_OPERATION(FloatingToFixedPoint)

/// CK_FixedPointToFloating - Fixed point to floating.
/// (float) 2.5k
CAST_OPERATION(FixedPointToFloating)

/// CK_FixedPointCast - Fixed point to fixed point.
/// (_Accum) 0.5r
CAST_OPERATION(FixedPointCast)
Expand Down
4 changes: 4 additions & 0 deletions clang/include/clang/AST/RecordLayout.h
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,8 @@ class ASTRecordLayout {
/// getBaseClassOffset - Get the offset, in chars, for the given base class.
CharUnits getBaseClassOffset(const CXXRecordDecl *Base) const {
assert(CXXInfo && "Record layout does not have C++ specific info!");

Base = Base->getDefinition();
assert(CXXInfo->BaseOffsets.count(Base) && "Did not find base!");

return CXXInfo->BaseOffsets[Base];
Expand All @@ -256,6 +258,8 @@ class ASTRecordLayout {
/// getVBaseClassOffset - Get the offset, in chars, for the given base class.
CharUnits getVBaseClassOffset(const CXXRecordDecl *VBase) const {
assert(CXXInfo && "Record layout does not have C++ specific info!");

VBase = VBase->getDefinition();
assert(CXXInfo->VBaseOffsets.count(VBase) && "Did not find base!");

return CXXInfo->VBaseOffsets[VBase].VBaseOffset;
Expand Down
15 changes: 13 additions & 2 deletions clang/include/clang/Basic/BuiltinsWebAssembly.def
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,8 @@ TARGET_BUILTIN(__builtin_wasm_max_u_i32x4, "V4UiV4UiV4Ui", "nc", "simd128")
TARGET_BUILTIN(__builtin_wasm_avgr_u_i8x16, "V16UcV16UcV16Uc", "nc", "simd128")
TARGET_BUILTIN(__builtin_wasm_avgr_u_i16x8, "V8UsV8UsV8Us", "nc", "simd128")

TARGET_BUILTIN(__builtin_wasm_popcnt_i8x16, "V16ScV16Sc", "nc", "simd128")

TARGET_BUILTIN(__builtin_wasm_q15mulr_saturate_s_i8x16, "V8sV8sV8s", "nc", "simd128")

TARGET_BUILTIN(__builtin_wasm_bitselect, "V4iV4iV4iV4i", "nc", "simd128")
Expand Down Expand Up @@ -171,8 +173,17 @@ TARGET_BUILTIN(__builtin_wasm_narrow_u_i8x16_i16x8, "V16UcV8UsV8Us", "nc", "simd
TARGET_BUILTIN(__builtin_wasm_narrow_s_i16x8_i32x4, "V8sV4iV4i", "nc", "simd128")
TARGET_BUILTIN(__builtin_wasm_narrow_u_i16x8_i32x4, "V8UsV4UiV4Ui", "nc", "simd128")

TARGET_BUILTIN(__builtin_wasm_load32_zero, "V4ii*", "nU", "simd128")
TARGET_BUILTIN(__builtin_wasm_load64_zero, "V2LLiLLi*", "nU", "simd128")
TARGET_BUILTIN(__builtin_wasm_load32_zero, "V4ii*", "n", "simd128")
TARGET_BUILTIN(__builtin_wasm_load64_zero, "V2LLiLLi*", "n", "simd128")

TARGET_BUILTIN(__builtin_wasm_load8_lane, "V16ScSc*V16ScIi", "n", "simd128")
TARGET_BUILTIN(__builtin_wasm_load16_lane, "V8ss*V8sIi", "n", "simd128")
TARGET_BUILTIN(__builtin_wasm_load32_lane, "V4ii*V4iIi", "n", "simd128")
TARGET_BUILTIN(__builtin_wasm_load64_lane, "V2LLiLLi*V2LLiIi", "n", "simd128")
TARGET_BUILTIN(__builtin_wasm_store8_lane, "vSc*V16ScIi", "n", "simd128")
TARGET_BUILTIN(__builtin_wasm_store16_lane, "vs*V8sIi", "n", "simd128")
TARGET_BUILTIN(__builtin_wasm_store32_lane, "vi*V4iIi", "n", "simd128")
TARGET_BUILTIN(__builtin_wasm_store64_lane, "vLLi*V2LLiIi", "n", "simd128")

#undef BUILTIN
#undef TARGET_BUILTIN
18 changes: 18 additions & 0 deletions clang/include/clang/Basic/BuiltinsX86.def
Original file line number Diff line number Diff line change
Expand Up @@ -1871,6 +1871,24 @@ TARGET_BUILTIN(__builtin_ia32_selectpd_512, "V8dUcV8dV8d", "ncV:512:", "avx512f"
TARGET_BUILTIN(__builtin_ia32_selectss_128, "V4fUcV4fV4f", "ncV:128:", "avx512f")
TARGET_BUILTIN(__builtin_ia32_selectsd_128, "V2dUcV2dV2d", "ncV:128:", "avx512f")

// generic reduction intrinsics
TARGET_BUILTIN(__builtin_ia32_reduce_add_d512, "iV16i", "ncV:512:", "avx512f")
TARGET_BUILTIN(__builtin_ia32_reduce_add_q512, "OiV8Oi", "ncV:512:", "avx512f")
TARGET_BUILTIN(__builtin_ia32_reduce_and_d512, "iV16i", "ncV:512:", "avx512f")
TARGET_BUILTIN(__builtin_ia32_reduce_and_q512, "OiV8Oi", "ncV:512:", "avx512f")
TARGET_BUILTIN(__builtin_ia32_reduce_mul_d512, "iV16i", "ncV:512:", "avx512f")
TARGET_BUILTIN(__builtin_ia32_reduce_mul_q512, "OiV8Oi", "ncV:512:", "avx512f")
TARGET_BUILTIN(__builtin_ia32_reduce_or_d512, "iV16i", "ncV:512:", "avx512f")
TARGET_BUILTIN(__builtin_ia32_reduce_or_q512, "OiV8Oi", "ncV:512:", "avx512f")
TARGET_BUILTIN(__builtin_ia32_reduce_smax_d512, "iV16i", "ncV:512:", "avx512f")
TARGET_BUILTIN(__builtin_ia32_reduce_smax_q512, "OiV8Oi", "ncV:512:", "avx512f")
TARGET_BUILTIN(__builtin_ia32_reduce_smin_d512, "iV16i", "ncV:512:", "avx512f")
TARGET_BUILTIN(__builtin_ia32_reduce_smin_q512, "OiV8Oi", "ncV:512:", "avx512f")
TARGET_BUILTIN(__builtin_ia32_reduce_umax_d512, "iV16i", "ncV:512:", "avx512f")
TARGET_BUILTIN(__builtin_ia32_reduce_umax_q512, "OiV8Oi", "ncV:512:", "avx512f")
TARGET_BUILTIN(__builtin_ia32_reduce_umin_d512, "iV16i", "ncV:512:", "avx512f")
TARGET_BUILTIN(__builtin_ia32_reduce_umin_q512, "OiV8Oi", "ncV:512:", "avx512f")

// MONITORX/MWAITX
TARGET_BUILTIN(__builtin_ia32_monitorx, "vvC*UiUi", "n", "mwaitx")
TARGET_BUILTIN(__builtin_ia32_mwaitx, "vUiUiUi", "n", "mwaitx")
Expand Down
6 changes: 5 additions & 1 deletion clang/include/clang/Basic/CodeGenOptions.def
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@ CODEGENOPT(IncrementalLinkerCompatible, 1, 0) ///< Emit an object file which can
///< linker.
CODEGENOPT(MergeAllConstants , 1, 1) ///< Merge identical constants.
CODEGENOPT(MergeFunctions , 1, 0) ///< Set when -fmerge-functions is enabled.
CODEGENOPT(SplitColdCode , 1, 0) ///< Set when -fsplit-cold-code is enabled.
CODEGENOPT(MemProf , 1, 0) ///< Set when -fmemory-profile is enabled.
CODEGENOPT(MSVolatile , 1, 0) ///< Set when /volatile:ms is enabled.
CODEGENOPT(NoCommon , 1, 0) ///< Set when -fno-common or C++ is enabled.
Expand Down Expand Up @@ -401,12 +402,15 @@ CODEGENOPT(Addrsig, 1, 0)
/// Whether to emit unused static constants.
CODEGENOPT(KeepStaticConsts, 1, 0)

/// Whether to not follow the AAPCS that enforce at least one read before storing to a volatile bitfield
/// Whether to follow the AAPCS enforcing at least one read before storing to a volatile bitfield
CODEGENOPT(ForceAAPCSBitfieldLoad, 1, 0)

/// Assume that by-value parameters do not alias any other values.
CODEGENOPT(PassByValueIsNoAlias, 1, 0)

/// Whether to not follow the AAPCS that enforces volatile bit-field access width to be
/// according to the field declaring type width.
CODEGENOPT(AAPCSBitfieldWidth, 1, 1)

#undef CODEGENOPT
#undef ENUM_CODEGENOPT
Expand Down
1 change: 1 addition & 0 deletions clang/include/clang/Basic/Cuda.h
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ enum class CudaArch {
GFX1012,
GFX1030,
GFX1031,
GFX1032,
LAST,
};

Expand Down
4 changes: 4 additions & 0 deletions clang/include/clang/Basic/DiagnosticFrontendKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,10 @@ def warn_alias_with_section : Warning<
"as the %select{aliasee|resolver}2">,
InGroup<IgnoredAttributes>;

def warn_fe_ignored_opt_split_cold_code : Warning<
"'%0' has no effect when %select{optimizing for minimum size|optimizations are disabled}1">,
InGroup<IgnoredAttributes>;

let CategoryName = "Instrumentation Issue" in {
def warn_profile_data_out_of_date : Warning<
"profile data may be out of date: of %0 function%s0, %1 %plural{1:has|:have}1"
Expand Down
2 changes: 1 addition & 1 deletion clang/include/clang/Basic/LangOptions.def
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,7 @@ LANGOPT(CUDAHostDeviceConstexpr, 1, 1, "treating unattributed constexpr function
LANGOPT(CUDADeviceApproxTranscendentals, 1, 0, "using approximate transcendental functions")
LANGOPT(GPURelocatableDeviceCode, 1, 0, "generate relocatable device code")
LANGOPT(GPUAllowDeviceInit, 1, 0, "allowing device side global init functions for HIP")
LANGOPT(GPUMaxThreadsPerBlock, 32, 1024, "default max threads per block for kernel launch bounds for HIP")
LANGOPT(GPUMaxThreadsPerBlock, 32, 256, "default max threads per block for kernel launch bounds for HIP")

LANGOPT(SYCL , 1, 0, "SYCL")
LANGOPT(SYCLIsDevice , 1, 0, "Generate code for SYCL device")
Expand Down
77 changes: 59 additions & 18 deletions clang/include/clang/Basic/SourceManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -184,13 +184,21 @@ namespace SrcMgr {
///
/// \param Loc If specified, is the location that invalid file diagnostics
/// will be emitted at.
llvm::Optional<llvm::MemoryBufferRef>
getBufferOrNone(DiagnosticsEngine &Diag, FileManager &FM,
SourceLocation Loc = SourceLocation()) const;

private:
/// Returns pointer to memory buffer.
///
/// \param Invalid If non-NULL, will be set \c true if an error occurred.
const llvm::MemoryBuffer *getBuffer(DiagnosticsEngine &Diag,
FileManager &FM,
SourceLocation Loc = SourceLocation(),
bool *Invalid = nullptr) const;
/// TODO: SourceManager needs access to this for now, but once that's done
/// we should remove this API.
const llvm::MemoryBuffer *getBufferPointer(DiagnosticsEngine &Diag,
FileManager &FM,
SourceLocation Loc) const;
friend class clang::SourceManager;

public:
/// Returns the size of the content encapsulated by this
/// ContentCache.
///
Expand Down Expand Up @@ -958,10 +966,38 @@ class SourceManager : public RefCountedBase<SourceManager> {
// FileID manipulation methods.
//===--------------------------------------------------------------------===//

/// Return the buffer for the specified FileID.
///
/// If there is an error opening this buffer the first time, return None.
llvm::Optional<llvm::MemoryBufferRef>
getBufferOrNone(FileID FID, SourceLocation Loc = SourceLocation()) const {
bool MyInvalid = false;
const SrcMgr::SLocEntry &Entry = getSLocEntry(FID, &MyInvalid);
if (MyInvalid || !Entry.isFile())
return None;

return Entry.getFile().getContentCache()->getBufferOrNone(
Diag, getFileManager(), Loc);
}

/// Return the buffer for the specified FileID.
///
/// If there is an error opening this buffer the first time, this
/// manufactures a temporary buffer and returns it.
llvm::MemoryBufferRef
getBufferOrFake(FileID FID, SourceLocation Loc = SourceLocation()) const {
if (auto B = getBufferOrNone(FID, Loc))
return *B;
return getFakeBufferForRecovery()->getMemBufferRef();
}

/// Return the buffer for the specified FileID.
///
/// If there is an error opening this buffer the first time, this
/// manufactures a temporary buffer and returns a non-empty error string.
///
/// TODO: Update users of Invalid to call getBufferOrNone and change return
/// type to MemoryBufferRef.
const llvm::MemoryBuffer *getBuffer(FileID FID, SourceLocation Loc,
bool *Invalid = nullptr) const {
bool MyInvalid = false;
Expand All @@ -973,23 +1009,16 @@ class SourceManager : public RefCountedBase<SourceManager> {
return getFakeBufferForRecovery();
}

return Entry.getFile().getContentCache()->getBuffer(Diag, getFileManager(),
Loc, Invalid);
auto *B = Entry.getFile().getContentCache()->getBufferPointer(
Diag, getFileManager(), Loc);
if (Invalid)
*Invalid = !B;
return B ? B : getFakeBufferForRecovery();
}

const llvm::MemoryBuffer *getBuffer(FileID FID,
bool *Invalid = nullptr) const {
bool MyInvalid = false;
const SrcMgr::SLocEntry &Entry = getSLocEntry(FID, &MyInvalid);
if (MyInvalid || !Entry.isFile()) {
if (Invalid)
*Invalid = true;

return getFakeBufferForRecovery();
}

return Entry.getFile().getContentCache()->getBuffer(
Diag, getFileManager(), SourceLocation(), Invalid);
return getBuffer(FID, SourceLocation(), Invalid);
}

/// Returns the FileEntry record for the provided FileID.
Expand Down Expand Up @@ -1024,6 +1053,18 @@ class SourceManager : public RefCountedBase<SourceManager> {
/// \param Invalid If non-NULL, will be set true if an error occurred.
StringRef getBufferData(FileID FID, bool *Invalid = nullptr) const;

/// Return a StringRef to the source buffer data for the
/// specified FileID, returning None if invalid.
///
/// \param FID The file ID whose contents will be returned.
llvm::Optional<StringRef> getBufferDataOrNone(FileID FID) const;

/// Return a StringRef to the source buffer data for the
/// specified FileID, returning None if it's not yet loaded.
///
/// \param FID The file ID whose contents will be returned.
llvm::Optional<StringRef> getBufferDataIfLoaded(FileID FID) const;

/// Get the number of FileIDs (files and macros) that were created
/// during preprocessing of \p FID, including it.
unsigned getNumCreatedFIDsForFileID(FileID FID) const {
Expand Down
1 change: 1 addition & 0 deletions clang/include/clang/Basic/TargetCXXABI.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#ifndef LLVM_CLANG_BASIC_TARGETCXXABI_H
#define LLVM_CLANG_BASIC_TARGETCXXABI_H

#include "clang/Basic/LLVM.h"
#include "llvm/Support/ErrorHandling.h"

namespace clang {
Expand Down
Loading