29 changes: 29 additions & 0 deletions .github/workflows/release-merge-pr.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
name: Release Backport - Merge Pull Request

on:
pull_request:
types: [closed]

jobs:
update-linked-issue:
runs-on: ubuntu-latest
#if: github.event.pull_request.merged

steps:
- name: Install PyGithub
run: pip install PyGithub

- name: Update Isssue
shell: python
run: |
import github
import re
context = github.Github('${{ github.token }}')
repo = context.get_repo('${{ github.repository}}')
pull = repo.get_pull(${{ github.event.pull_request.number }})
for comment in pull.get_issue_comments():
m = re.match('resolves #([0-9]+)', comment.body)
if not m:
continue
issue.repo.get_issue(int(m.group(1)))
issue.edit(state='closed')
62 changes: 62 additions & 0 deletions .github/workflows/release-tasks.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
name: Release Task

on:
push:
tags:
# The regex support here is limited, so just match everything that starts with llvmorg- and filter later.
- 'llvmorg-*'

jobs:
release-tasks:
runs-on: ubuntu-latest
steps:

- name: Install Dependencies
run: |
sudo apt-get install doxygen python3-sphinx python3-recommonmark ninja-build graphviz texlive-font-utils python3-github
pip3 install --user sphinx-markdown-tables
- name: Checkout LLVM
uses: actions/checkout@v1
with:
fetch-depth: 1

# This step will only succeed if the tag name is valid, so it safe to
# assume a valid tag name in all steps after this one.
- name: Create Release
run: |
./llvm/utils/release/./github-upload-release.py --token ${{ github.token }} --tag ${{ github.ref_name }} create
- name: Get Release Name
id: release
run: |
name=`echo "${{ github.ref_name }}" | sed 's/llvmorg-//g'`
echo "::set-output name=name::$name"
- name: Build Documentation
env:
RELEASE: "${{ steps.release.output.name }}"
run: |
bash llvm/utils/release/build-docs.sh -srcdir llvm -release $RELEASE
./llvm/utils/release/./github-upload-release.py --token ${{ github.token }} --release $RELEASE upload --files *doxygen*.tar.xz
- name: Clone www-releases
if: ${{ !contains(steps.release.outputs.name, 'rc') }}
uses: actions/checkout@v1
with:
repository: ${{ github.repository_owner }}/www-releases
ref: main
fetch-depth: 0
path: www-releases

- name: Upload Release Notes
env:
RELEASE: "${{ steps.release.output.name }}"
run: |
./github-automation.py --token ${{ github.token }} setup-llvmbot-git
mkdir -p ../www-releases/$RELEASE
mv ./docs-build/html-export/* ../www-releases/$RELEASE
cd ../www-releases
git add $RELEASE
git commit -a -m "Add $RELEASE documentation"
git push https://${{ secrets.WWW_RELEASES_TOKEN }}@github.com/${{ github.repository_owner }}/www-releases main:main
Empty file.
Empty file.
3 changes: 2 additions & 1 deletion clang-tools-extra/clang-tidy/bugprone/BranchCloneCheck.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,8 @@ namespace bugprone {

void BranchCloneCheck::registerMatchers(MatchFinder *Finder) {
Finder->addMatcher(
ifStmt(stmt().bind("if"),
ifStmt(unless(allOf(isConstexpr(), isInTemplateInstantiation())),
stmt().bind("if"),
hasParent(stmt(unless(ifStmt(hasElse(equalsBoundNode("if")))))),
hasElse(stmt().bind("else"))),
this);
Expand Down
36 changes: 32 additions & 4 deletions clang-tools-extra/clang-tidy/bugprone/InfiniteLoopCheck.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,15 @@ static std::string getCondVarNames(const Stmt *Cond) {
return Result;
}

static bool isKnownFalse(const Expr &Cond, const ASTContext &Ctx) {
if (Cond.isValueDependent())
return false;
bool Result = false;
if (Cond.EvaluateAsBooleanCondition(Result, Ctx))
return !Result;
return false;
}

void InfiniteLoopCheck::registerMatchers(MatchFinder *Finder) {
const auto LoopCondition = allOf(
hasCondition(
Expand All @@ -170,17 +179,36 @@ void InfiniteLoopCheck::check(const MatchFinder::MatchResult &Result) {
const auto *LoopStmt = Result.Nodes.getNodeAs<Stmt>("loop-stmt");
const auto *Func = Result.Nodes.getNodeAs<FunctionDecl>("func");

if (isKnownFalse(*Cond, *Result.Context))
return;

bool ShouldHaveConditionVariables = true;
if (const auto *While = dyn_cast<WhileStmt>(LoopStmt)) {
if (const VarDecl *LoopVarDecl = While->getConditionVariable()) {
if (const Expr *Init = LoopVarDecl->getInit()) {
ShouldHaveConditionVariables = false;
Cond = Init;
}
}
}

if (isAtLeastOneCondVarChanged(Func, LoopStmt, Cond, Result.Context))
return;

std::string CondVarNames = getCondVarNames(Cond);
if (CondVarNames.empty())
if (ShouldHaveConditionVariables && CondVarNames.empty())
return;

diag(LoopStmt->getBeginLoc(),
"this loop is infinite; none of its condition variables (%0)"
" are updated in the loop body")
if (CondVarNames.empty()) {
diag(LoopStmt->getBeginLoc(),
"this loop is infinite; it does not check any variables in the"
" condition");
} else {
diag(LoopStmt->getBeginLoc(),
"this loop is infinite; none of its condition variables (%0)"
" are updated in the loop body")
<< CondVarNames;
}
}

} // namespace bugprone
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ static void replaceMoveWithForward(const UnresolvedLookupExpr *Callee,

if (CallRange.isValid()) {
const std::string TypeName =
TypeParmDecl->getIdentifier()
(TypeParmDecl->getIdentifier() && !TypeParmDecl->isImplicit())
? TypeParmDecl->getName().str()
: (llvm::Twine("decltype(") + ParmVar->getName() + ")").str();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,15 @@ InitVariablesCheck::InitVariablesCheck(StringRef Name,
MathHeader(Options.get("MathHeader", "math.h")) {}

void InitVariablesCheck::registerMatchers(MatchFinder *Finder) {
Finder->addMatcher(varDecl(unless(hasInitializer(anything())),
unless(isInstantiated()), isLocalVarDecl(),
unless(isStaticLocal()), isDefinition())
.bind("vardecl"),
this);
std::string BadDecl = "badDecl";
Finder->addMatcher(
varDecl(unless(hasInitializer(anything())), unless(isInstantiated()),
isLocalVarDecl(), unless(isStaticLocal()), isDefinition(),
optionally(hasParent(declStmt(hasParent(
cxxForRangeStmt(hasLoopVariable(varDecl().bind(BadDecl))))))),
unless(equalsBoundNode(BadDecl)))
.bind("vardecl"),
this);
}

void InitVariablesCheck::registerPPCallbacks(const SourceManager &SM,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,10 @@ void BracesAroundStatementsCheck::storeOptions(
}

void BracesAroundStatementsCheck::registerMatchers(MatchFinder *Finder) {
Finder->addMatcher(ifStmt().bind("if"), this);
Finder->addMatcher(
ifStmt(unless(allOf(isConstexpr(), isInTemplateInstantiation())))
.bind("if"),
this);
Finder->addMatcher(whileStmt().bind("while"), this);
Finder->addMatcher(doStmt().bind("do"), this);
Finder->addMatcher(forStmt().bind("for"), this);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ static const char WarningMessage[] = "do not use 'else' after '%0'";
static const char WarnOnUnfixableStr[] = "WarnOnUnfixable";

const DeclRefExpr *findUsage(const Stmt *Node, int64_t DeclIdentifier) {
if (!Node)
return nullptr;
if (const auto *DeclRef = dyn_cast<DeclRefExpr>(Node)) {
if (DeclRef->getDecl()->getID() == DeclIdentifier) {
return DeclRef;
Expand All @@ -44,6 +46,8 @@ const DeclRefExpr *findUsage(const Stmt *Node, int64_t DeclIdentifier) {
const DeclRefExpr *
findUsageRange(const Stmt *Node,
const llvm::iterator_range<int64_t *> &DeclIdentifiers) {
if (!Node)
return nullptr;
if (const auto *DeclRef = dyn_cast<DeclRefExpr>(Node)) {
if (llvm::is_contained(DeclIdentifiers, DeclRef->getDecl()->getID())) {
return DeclRef;
Expand Down
2 changes: 2 additions & 0 deletions clang-tools-extra/clangd/CodeComplete.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1842,6 +1842,8 @@ CompletionItem CodeCompletion::render(const CodeCompleteOptions &Opts) const {
if (InsertInclude && InsertInclude->Insertion)
LSP.additionalTextEdits.push_back(*InsertInclude->Insertion);

LSP.score = Score.ExcludingName;

return LSP;
}

Expand Down
17 changes: 8 additions & 9 deletions clang-tools-extra/clangd/FSProvider.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,10 @@ namespace clangd {

namespace {
/// Always opens files in the underlying filesystem as "volatile", meaning they
/// won't be memory-mapped. This avoid locking the files on Windows.
/// won't be memory-mapped. Memory-mapping isn't desirable for clangd:
/// - edits to the underlying files change contents MemoryBuffers owned by
// SourceManager, breaking its invariants and leading to crashes
/// - it locks files on windows, preventing edits
class VolatileFileSystem : public llvm::vfs::ProxyFileSystem {
public:
explicit VolatileFileSystem(llvm::IntrusiveRefCntPtr<FileSystem> FS)
Expand All @@ -34,7 +37,7 @@ class VolatileFileSystem : public llvm::vfs::ProxyFileSystem {
if (!File)
return File;
// Try to guess preamble files, they can be memory-mapped even on Windows as
// clangd has exclusive access to those.
// clangd has exclusive access to those and nothing else should touch them.
llvm::StringRef FileName = llvm::sys::path::filename(Path);
if (FileName.startswith("preamble-") && FileName.endswith(".pch"))
return File;
Expand Down Expand Up @@ -70,15 +73,11 @@ class VolatileFileSystem : public llvm::vfs::ProxyFileSystem {

llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem>
clang::clangd::RealFileSystemProvider::getFileSystem() const {
// Avoid using memory-mapped files on Windows, they cause file locking issues.
// FIXME: Try to use a similar approach in Sema instead of relying on
// propagation of the 'isVolatile' flag through all layers.
#ifdef _WIN32
// Avoid using memory-mapped files.
// FIXME: Try to use a similar approach in Sema instead of relying on
// propagation of the 'isVolatile' flag through all layers.
return new VolatileFileSystem(
llvm::vfs::createPhysicalFileSystem().release());
#else
return llvm::vfs::createPhysicalFileSystem().release();
#endif
}
} // namespace clangd
} // namespace clang
1 change: 0 additions & 1 deletion clang-tools-extra/clangd/FSProvider.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ class FileSystemProvider {

class RealFileSystemProvider : public FileSystemProvider {
public:
// FIXME: returns the single real FS instance, which is not threadsafe.
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem>
getFileSystem() const override;
};
Expand Down
81 changes: 49 additions & 32 deletions clang-tools-extra/clangd/FindTarget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
#include "llvm/Support/Compiler.h"
#include "llvm/Support/raw_ostream.h"
#include <utility>
#include <vector>

namespace clang {
namespace clangd {
Expand Down Expand Up @@ -134,6 +135,35 @@ const Type *getPointeeType(const Type *T) {
return FirstArg.getAsType().getTypePtrOrNull();
}

const NamedDecl *getTemplatePattern(const NamedDecl *D) {
if (const CXXRecordDecl *CRD = dyn_cast<CXXRecordDecl>(D)) {
return CRD->getTemplateInstantiationPattern();
} else if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) {
return FD->getTemplateInstantiationPattern();
} else if (auto *VD = dyn_cast<VarDecl>(D)) {
// Hmm: getTIP returns its arg if it's not an instantiation?!
VarDecl *T = VD->getTemplateInstantiationPattern();
return (T == D) ? nullptr : T;
} else if (const auto *ED = dyn_cast<EnumDecl>(D)) {
return ED->getInstantiatedFromMemberEnum();
} else if (isa<FieldDecl>(D) || isa<TypedefNameDecl>(D)) {
if (const auto *Parent = llvm::dyn_cast<NamedDecl>(D->getDeclContext()))
if (const DeclContext *ParentPat =
dyn_cast_or_null<DeclContext>(getTemplatePattern(Parent)))
for (const NamedDecl *BaseND : ParentPat->lookup(D->getDeclName()))
if (!BaseND->isImplicit() && BaseND->getKind() == D->getKind())
return BaseND;
} else if (const auto *ECD = dyn_cast<EnumConstantDecl>(D)) {
if (const auto *ED = dyn_cast<EnumDecl>(ECD->getDeclContext())) {
if (const EnumDecl *Pattern = ED->getInstantiatedFromMemberEnum()) {
for (const NamedDecl *BaseECD : Pattern->lookup(ECD->getDeclName()))
return BaseECD;
}
}
}
return nullptr;
}

// TargetFinder locates the entities that an AST node refers to.
//
// Typically this is (possibly) one declaration and (possibly) one type, but
Expand Down Expand Up @@ -167,37 +197,12 @@ const Type *getPointeeType(const Type *T) {
struct TargetFinder {
using RelSet = DeclRelationSet;
using Rel = DeclRelation;
llvm::SmallDenseMap<const NamedDecl *, RelSet> Decls;
RelSet Flags;

static const NamedDecl *getTemplatePattern(const NamedDecl *D) {
if (const CXXRecordDecl *CRD = dyn_cast<CXXRecordDecl>(D)) {
return CRD->getTemplateInstantiationPattern();
} else if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) {
return FD->getTemplateInstantiationPattern();
} else if (auto *VD = dyn_cast<VarDecl>(D)) {
// Hmm: getTIP returns its arg if it's not an instantiation?!
VarDecl *T = VD->getTemplateInstantiationPattern();
return (T == D) ? nullptr : T;
} else if (const auto *ED = dyn_cast<EnumDecl>(D)) {
return ED->getInstantiatedFromMemberEnum();
} else if (isa<FieldDecl>(D) || isa<TypedefNameDecl>(D)) {
if (const auto *Parent = llvm::dyn_cast<NamedDecl>(D->getDeclContext()))
if (const DeclContext *ParentPat =
dyn_cast_or_null<DeclContext>(getTemplatePattern(Parent)))
for (const NamedDecl *BaseND : ParentPat->lookup(D->getDeclName()))
if (!BaseND->isImplicit() && BaseND->getKind() == D->getKind())
return BaseND;
} else if (const auto *ECD = dyn_cast<EnumConstantDecl>(D)) {
if (const auto *ED = dyn_cast<EnumDecl>(ECD->getDeclContext())) {
if (const EnumDecl *Pattern = ED->getInstantiatedFromMemberEnum()) {
for (const NamedDecl *BaseECD : Pattern->lookup(ECD->getDeclName()))
return BaseECD;
}
}
}
return nullptr;
}
private:
llvm::SmallDenseMap<const NamedDecl *,
std::pair<RelSet, /*InsertionOrder*/ size_t>>
Decls;
RelSet Flags;

template <typename T> void debug(T &Node, RelSet Flags) {
dlog("visit [{0}] {1}", Flags,
Expand All @@ -207,10 +212,22 @@ struct TargetFinder {
void report(const NamedDecl *D, RelSet Flags) {
dlog("--> [{0}] {1}", Flags,
nodeToString(ast_type_traits::DynTypedNode::create(*D)));
Decls[D] |= Flags;
auto It = Decls.try_emplace(D, std::make_pair(Flags, Decls.size()));
// If already exists, update the flags.
if (!It.second)
It.first->second.first |= Flags;
}

public:
llvm::SmallVector<std::pair<const NamedDecl *, RelSet>, 1> takeDecls() const {
using ValTy = std::pair<const NamedDecl *, RelSet>;
llvm::SmallVector<ValTy, 1> Result;
Result.resize(Decls.size());
for (const auto &Elem : Decls)
Result[Elem.second.second] = {Elem.first, Elem.second.first};
return Result;
}

void add(const Decl *Dcl, RelSet Flags) {
const NamedDecl *D = llvm::dyn_cast<NamedDecl>(Dcl);
if (!D)
Expand Down Expand Up @@ -485,7 +502,7 @@ allTargetDecls(const ast_type_traits::DynTypedNode &N) {
else if (const CXXCtorInitializer *CCI = N.get<CXXCtorInitializer>())
Finder.add(CCI, Flags);

return {Finder.Decls.begin(), Finder.Decls.end()};
return Finder.takeDecls();
}

llvm::SmallVector<const NamedDecl *, 1>
Expand Down
49 changes: 43 additions & 6 deletions clang-tools-extra/clangd/FormattedString.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include "llvm/Support/FormatVariadic.h"
#include "llvm/Support/raw_ostream.h"
#include <cstddef>
#include <iterator>
#include <memory>
#include <string>
#include <vector>
Expand Down Expand Up @@ -117,16 +118,52 @@ std::string renderBlocks(llvm::ArrayRef<std::unique_ptr<Block>> Children,
void (Block::*RenderFunc)(llvm::raw_ostream &) const) {
std::string R;
llvm::raw_string_ostream OS(R);
for (auto &C : Children)

// Trim rulers.
Children = Children.drop_while(
[](const std::unique_ptr<Block> &C) { return C->isRuler(); });
auto Last = llvm::find_if(
llvm::reverse(Children),
[](const std::unique_ptr<Block> &C) { return !C->isRuler(); });
Children = Children.drop_back(Children.end() - Last.base());

bool LastBlockWasRuler = true;
for (const auto &C : Children) {
if (C->isRuler() && LastBlockWasRuler)
continue;
LastBlockWasRuler = C->isRuler();
((*C).*RenderFunc)(OS);
return llvm::StringRef(OS.str()).trim().str();
}

// Get rid of redundant empty lines introduced in plaintext while imitating
// padding in markdown.
std::string AdjustedResult;
llvm::StringRef TrimmedText(OS.str());
TrimmedText = TrimmedText.trim();

llvm::copy_if(TrimmedText, std::back_inserter(AdjustedResult),
[&TrimmedText](const char &C) {
return !llvm::StringRef(TrimmedText.data(),
&C - TrimmedText.data() + 1)
// We allow at most two newlines.
.endswith("\n\n\n");
});

return AdjustedResult;
}

// Puts a vertical space between blocks inside a document.
class Spacer : public Block {
// Seperates two blocks with extra spacing. Note that it might render strangely
// in vscode if the trailing block is a codeblock, see
// https://github.com/microsoft/vscode/issues/88416 for details.
class Ruler : public Block {
public:
void renderMarkdown(llvm::raw_ostream &OS) const override { OS << '\n'; }
void renderMarkdown(llvm::raw_ostream &OS) const override {
// Note that we need an extra new line before the ruler, otherwise we might
// make previous block a title instead of introducing a ruler.
OS << "\n---\n";
}
void renderPlainText(llvm::raw_ostream &OS) const override { OS << '\n'; }
bool isRuler() const override { return true; }
};

class CodeBlock : public Block {
Expand Down Expand Up @@ -272,7 +309,7 @@ Paragraph &Document::addParagraph() {
return *static_cast<Paragraph *>(Children.back().get());
}

void Document::addSpacer() { Children.push_back(std::make_unique<Spacer>()); }
void Document::addRuler() { Children.push_back(std::make_unique<Ruler>()); }

void Document::addCodeBlock(std::string Code, std::string Language) {
Children.emplace_back(
Expand Down
5 changes: 3 additions & 2 deletions clang-tools-extra/clangd/FormattedString.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ class Block {
std::string asMarkdown() const;
std::string asPlainText() const;

virtual bool isRuler() const { return false; }
virtual ~Block() = default;
};

Expand Down Expand Up @@ -82,8 +83,8 @@ class Document {
public:
/// Adds a semantical block that will be separate from others.
Paragraph &addParagraph();
/// Inserts a vertical space into the document.
void addSpacer();
/// Inserts a horizontal separator to the document.
void addRuler();
/// Adds a block of code. This translates to a ``` block in markdown. In plain
/// text representation, the code block will be surrounded by newlines.
void addCodeBlock(std::string Code, std::string Language = "cpp");
Expand Down
186 changes: 132 additions & 54 deletions clang-tools-extra/clangd/Hover.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include "FindTarget.h"
#include "FormattedString.h"
#include "Logger.h"
#include "ParsedAST.h"
#include "Selection.h"
#include "SourceCode.h"
#include "index/SymbolCollector.h"
Expand All @@ -21,13 +22,20 @@
#include "clang/AST/Decl.h"
#include "clang/AST/DeclBase.h"
#include "clang/AST/DeclTemplate.h"
#include "clang/AST/Expr.h"
#include "clang/AST/ExprCXX.h"
#include "clang/AST/PrettyPrinter.h"
#include "clang/AST/Type.h"
#include "clang/Basic/Specifiers.h"
#include "clang/Index/IndexSymbol.h"
#include "llvm/ADT/None.h"
#include "llvm/ADT/Optional.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/raw_ostream.h"
#include <string>

Expand Down Expand Up @@ -116,6 +124,15 @@ void printParams(llvm::raw_ostream &OS,
}
}

std::string printType(QualType QT, const PrintingPolicy &Policy) {
// TypePrinter doesn't resolve decltypes, so resolve them here.
// FIXME: This doesn't handle composite types that contain a decltype in them.
// We should rather have a printing policy for that.
while (const auto *DT = QT->getAs<DecltypeType>())
QT = DT->getUnderlyingType();
return QT.getAsString(Policy);
}

std::vector<HoverInfo::Param>
fetchTemplateParameters(const TemplateParameterList *Params,
const PrintingPolicy &PP) {
Expand All @@ -124,8 +141,7 @@ fetchTemplateParameters(const TemplateParameterList *Params,

for (const Decl *Param : *Params) {
HoverInfo::Param P;
P.Type.emplace();
if (const auto TTP = dyn_cast<TemplateTypeParmDecl>(Param)) {
if (const auto *TTP = dyn_cast<TemplateTypeParmDecl>(Param)) {
P.Type = TTP->wasDeclaredWithTypename() ? "typename" : "class";
if (TTP->isParameterPack())
*P.Type += "...";
Expand All @@ -134,21 +150,21 @@ fetchTemplateParameters(const TemplateParameterList *Params,
P.Name = TTP->getNameAsString();
if (TTP->hasDefaultArgument())
P.Default = TTP->getDefaultArgument().getAsString(PP);
} else if (const auto NTTP = dyn_cast<NonTypeTemplateParmDecl>(Param)) {
} else if (const auto *NTTP = dyn_cast<NonTypeTemplateParmDecl>(Param)) {
if (IdentifierInfo *II = NTTP->getIdentifier())
P.Name = II->getName().str();

llvm::raw_string_ostream Out(*P.Type);
NTTP->getType().print(Out, PP);
P.Type = printType(NTTP->getType(), PP);
if (NTTP->isParameterPack())
Out << "...";
*P.Type += "...";

if (NTTP->hasDefaultArgument()) {
P.Default.emplace();
llvm::raw_string_ostream Out(*P.Default);
NTTP->getDefaultArgument()->printPretty(Out, nullptr, PP);
}
} else if (const auto TTPD = dyn_cast<TemplateTemplateParmDecl>(Param)) {
} else if (const auto *TTPD = dyn_cast<TemplateTemplateParmDecl>(Param)) {
P.Type.emplace();
llvm::raw_string_ostream OS(*P.Type);
OS << "template <";
printParams(OS,
Expand Down Expand Up @@ -190,15 +206,23 @@ const FunctionDecl *getUnderlyingFunction(const Decl *D) {
// Returns the decl that should be used for querying comments, either from index
// or AST.
const NamedDecl *getDeclForComment(const NamedDecl *D) {
if (auto *CTSD = llvm::dyn_cast<ClassTemplateSpecializationDecl>(D))
if (!CTSD->isExplicitInstantiationOrSpecialization())
return CTSD->getTemplateInstantiationPattern();
if (auto *VTSD = llvm::dyn_cast<VarTemplateSpecializationDecl>(D))
if (!VTSD->isExplicitInstantiationOrSpecialization())
return VTSD->getTemplateInstantiationPattern();
if (auto *FD = D->getAsFunction())
if (FD->isTemplateInstantiation())
return FD->getTemplateInstantiationPattern();
if (const auto *TSD = llvm::dyn_cast<ClassTemplateSpecializationDecl>(D)) {
// Template may not be instantiated e.g. if the type didn't need to be
// complete; fallback to primary template.
if (TSD->getTemplateSpecializationKind() == TSK_Undeclared)
return TSD->getSpecializedTemplate();
if (const auto *TIP = TSD->getTemplateInstantiationPattern())
return TIP;
}
if (const auto *TSD = llvm::dyn_cast<VarTemplateSpecializationDecl>(D)) {
if (TSD->getTemplateSpecializationKind() == TSK_Undeclared)
return TSD->getSpecializedTemplate();
if (const auto *TIP = TSD->getTemplateInstantiationPattern())
return TIP;
}
if (const auto *FD = D->getAsFunction())
if (const auto *TIP = FD->getTemplateInstantiationPattern())
return TIP;
return D;
}

Expand Down Expand Up @@ -234,7 +258,7 @@ void fillFunctionTypeAndParams(HoverInfo &HI, const Decl *D,
HI.Parameters->emplace_back();
auto &P = HI.Parameters->back();
if (!PVD->getType().isNull()) {
P.Type = PVD->getType().getAsString(Policy);
P.Type = printType(PVD->getType(), Policy);
} else {
std::string Param;
llvm::raw_string_ostream OS(Param);
Expand All @@ -251,20 +275,19 @@ void fillFunctionTypeAndParams(HoverInfo &HI, const Decl *D,
}
}

if (const auto *CCD = llvm::dyn_cast<CXXConstructorDecl>(FD)) {
// Constructor's "return type" is the class type.
HI.ReturnType = declaredType(CCD->getParent()).getAsString(Policy);
// Don't provide any type for the constructor itself.
} else if (llvm::isa<CXXDestructorDecl>(FD)) {
HI.ReturnType = "void";
} else {
HI.ReturnType = FD->getReturnType().getAsString(Policy);
// We don't want any type info, if name already contains it. This is true for
// constructors/destructors and conversion operators.
const auto NK = FD->getDeclName().getNameKind();
if (NK == DeclarationName::CXXConstructorName ||
NK == DeclarationName::CXXDestructorName ||
NK == DeclarationName::CXXConversionFunctionName)
return;

QualType FunctionType = FD->getType();
if (const VarDecl *VD = llvm::dyn_cast<VarDecl>(D)) // Lambdas
FunctionType = VD->getType().getDesugaredType(D->getASTContext());
HI.Type = FunctionType.getAsString(Policy);
}
HI.ReturnType = printType(FD->getReturnType(), Policy);
QualType QT = FD->getType();
if (const VarDecl *VD = llvm::dyn_cast<VarDecl>(D)) // Lambdas
QT = VD->getType().getDesugaredType(D->getASTContext());
HI.Type = printType(QT, Policy);
// FIXME: handle variadics.
}

Expand Down Expand Up @@ -335,7 +358,7 @@ HoverInfo getHoverContents(const NamedDecl *D, const SymbolIndex *Index) {
fetchTemplateParameters(TD->getTemplateParameters(), Policy);
D = TD;
} else if (const FunctionDecl *FD = D->getAsFunction()) {
if (const auto FTD = FD->getDescribedTemplate()) {
if (const auto *FTD = FD->getDescribedTemplate()) {
HI.TemplateParameters =
fetchTemplateParameters(FTD->getTemplateParameters(), Policy);
D = FTD;
Expand All @@ -346,7 +369,7 @@ HoverInfo getHoverContents(const NamedDecl *D, const SymbolIndex *Index) {
if (const FunctionDecl *FD = getUnderlyingFunction(D))
fillFunctionTypeAndParams(HI, D, FD, Policy);
else if (const auto *VD = dyn_cast<ValueDecl>(D))
HI.Type = VD->getType().getAsString(Policy);
HI.Type = printType(VD->getType(), Policy);

// Fill in value with evaluated initializer if possible.
if (const auto *Var = dyn_cast<VarDecl>(D)) {
Expand Down Expand Up @@ -410,6 +433,51 @@ HoverInfo getHoverContents(const DefinedMacro &Macro, ParsedAST &AST) {
}
return HI;
}

bool isLiteral(const Expr *E) {
// Unfortunately there's no common base Literal classes inherits from
// (apart from Expr), therefore this is a nasty blacklist.
return llvm::isa<CharacterLiteral>(E) || llvm::isa<CompoundLiteralExpr>(E) ||
llvm::isa<CXXBoolLiteralExpr>(E) ||
llvm::isa<CXXNullPtrLiteralExpr>(E) ||
llvm::isa<FixedPointLiteral>(E) || llvm::isa<FloatingLiteral>(E) ||
llvm::isa<ImaginaryLiteral>(E) || llvm::isa<IntegerLiteral>(E) ||
llvm::isa<StringLiteral>(E) || llvm::isa<UserDefinedLiteral>(E);
}

llvm::StringLiteral getNameForExpr(const Expr *E) {
// FIXME: Come up with names for `special` expressions.
//
// It's an known issue for GCC5, https://godbolt.org/z/Z_tbgi. Work around
// that by using explicit conversion constructor.
//
// TODO: Once GCC5 is fully retired and not the minimal requirement as stated
// in `GettingStarted`, please remove the explicit conversion constructor.
return llvm::StringLiteral("expression");
}

// Generates hover info for evaluatable expressions.
// FIXME: Support hover for literals (esp user-defined)
llvm::Optional<HoverInfo> getHoverContents(const Expr *E, ParsedAST &AST) {
// There's not much value in hovering over "42" and getting a hover card
// saying "42 is an int", similar for other literals.
if (isLiteral(E))
return llvm::None;

HoverInfo HI;
// For expressions we currently print the type and the value, iff it is
// evaluatable.
if (auto Val = printExprValue(E, AST.getASTContext())) {
auto Policy =
printingPolicyForDecls(AST.getASTContext().getPrintingPolicy());
Policy.SuppressTagKeyword = true;
HI.Type = printType(E->getType(), Policy);
HI.Value = *Val;
HI.Name = getNameForExpr(E);
return HI;
}
return llvm::None;
}
} // namespace

llvm::Optional<HoverInfo> getHover(ParsedAST &AST, Position Pos,
Expand Down Expand Up @@ -439,11 +507,11 @@ llvm::Optional<HoverInfo> getHover(ParsedAST &AST, Position Pos,
// Look for a close enclosing expression to show the value of.
if (!HI->Value)
HI->Value = printExprValue(N, AST.getASTContext());
} else if (const Expr *E = N->ASTNode.get<Expr>()) {
HI = getHoverContents(E, AST);
}
// FIXME: support hovers for other nodes?
// - certain expressions (sizeof etc)
// - built-in types
// - literals (esp user-defined)
}
}

Expand All @@ -464,37 +532,46 @@ llvm::Optional<HoverInfo> getHover(ParsedAST &AST, Position Pos,
markup::Document HoverInfo::present() const {
markup::Document Output;
// Header contains a text of the form:
// variable `var` : `int`
// variable `var`
//
// class `X`
//
// function `foo` → `int`
// function `foo`
//
// expression
//
// Note that we are making use of a level-3 heading because VSCode renders
// level 1 and 2 headers in a huge font, see
// https://github.com/microsoft/vscode/issues/88417 for details.
markup::Paragraph &Header = Output.addHeading(3);
Header.appendText(index::getSymbolKindString(Kind));
if (Kind != index::SymbolKind::Unknown)
Header.appendText(index::getSymbolKindString(Kind));
assert(!Name.empty() && "hover triggered on a nameless symbol");
Header.appendCode(Name);
if (ReturnType) {
Header.appendText("→");
Header.appendCode(*ReturnType);
} else if (Type) {
Header.appendText(":");
Header.appendCode(*Type);
}

// For functions we display signature in a list form, e.g.:
// - `bool param1`
// - `int param2 = 5`
if (Parameters && !Parameters->empty()) {
markup::BulletList &L = Output.addBulletList();
for (const auto &Param : *Parameters) {
std::string Buffer;
llvm::raw_string_ostream OS(Buffer);
OS << Param;
L.addItem().addParagraph().appendCode(std::move(OS.str()));
// Put a linebreak after header to increase readability.
Output.addRuler();
// Print Types on their own lines to reduce chances of getting line-wrapped by
// editor, as they might be long.
if (ReturnType) {
// For functions we display signature in a list form, e.g.:
// → `x`
// Parameters:
// - `bool param1`
// - `int param2 = 5`
Output.addParagraph().appendText("→").appendCode(*ReturnType);
if (Parameters && !Parameters->empty()) {
Output.addParagraph().appendText("Parameters:");
markup::BulletList &L = Output.addBulletList();
for (const auto &Param : *Parameters) {
std::string Buffer;
llvm::raw_string_ostream OS(Buffer);
OS << Param;
L.addItem().addParagraph().appendCode(std::move(OS.str()));
}
}
} else if (Type) {
Output.addParagraph().appendText("Type: ").appendCode(*Type);
}

if (Value) {
Expand All @@ -507,6 +584,7 @@ markup::Document HoverInfo::present() const {
Output.addParagraph().appendText(Documentation);

if (!Definition.empty()) {
Output.addRuler();
std::string ScopeComment;
// Drop trailing "::".
if (!LocalScope.empty()) {
Expand Down
1 change: 1 addition & 0 deletions clang-tools-extra/clangd/Protocol.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -826,6 +826,7 @@ llvm::json::Value toJSON(const CompletionItem &CI) {
Result["additionalTextEdits"] = llvm::json::Array(CI.additionalTextEdits);
if (CI.deprecated)
Result["deprecated"] = CI.deprecated;
Result["score"] = CI.score;
return std::move(Result);
}

Expand Down
7 changes: 7 additions & 0 deletions clang-tools-extra/clangd/Protocol.h
Original file line number Diff line number Diff line change
Expand Up @@ -995,6 +995,13 @@ struct CompletionItem {
/// Indicates if this item is deprecated.
bool deprecated = false;

/// This is Clangd extension.
/// The score that Clangd calculates to rank completion items. This score can
/// be used to adjust the ranking on the client side.
/// NOTE: This excludes fuzzy matching score which is typically calculated on
/// the client side.
float score = 0.f;

// TODO(krasimir): The following optional fields defined by the language
// server protocol are unsupported:
//
Expand Down
1 change: 1 addition & 0 deletions clang-tools-extra/clangd/Shutdown.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include "Shutdown.h"

#include <atomic>
#include <cstdlib>
#include <thread>

namespace clang {
Expand Down
67 changes: 63 additions & 4 deletions clang-tools-extra/clangd/refactor/tweaks/DefineOutline.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include "SourceCode.h"
#include "refactor/Tweak.h"
#include "clang/AST/ASTTypeTraits.h"
#include "clang/AST/Attr.h"
#include "clang/AST/Decl.h"
#include "clang/AST/DeclBase.h"
#include "clang/AST/DeclCXX.h"
Expand Down Expand Up @@ -155,7 +156,7 @@ getFunctionSourceCode(const FunctionDecl *FD, llvm::StringRef TargetNamespace,
"define outline: couldn't find a context for target");

llvm::Error Errors = llvm::Error::success();
tooling::Replacements QualifierInsertions;
tooling::Replacements DeclarationCleanups;

// Finds the first unqualified name in function return type and name, then
// qualifies those to be valid in TargetContext.
Expand All @@ -180,7 +181,7 @@ getFunctionSourceCode(const FunctionDecl *FD, llvm::StringRef TargetNamespace,
const NamedDecl *ND = Ref.Targets.front();
const std::string Qualifier = getQualification(
AST, *TargetContext, SM.getLocForStartOfFile(SM.getMainFileID()), ND);
if (auto Err = QualifierInsertions.add(
if (auto Err = DeclarationCleanups.add(
tooling::Replacement(SM, Ref.NameLoc, 0, Qualifier)))
Errors = llvm::joinErrors(std::move(Errors), std::move(Err));
});
Expand All @@ -205,14 +206,72 @@ getFunctionSourceCode(const FunctionDecl *FD, llvm::StringRef TargetNamespace,
assert(Tok != Tokens.rend());
DelRange.setBegin(Tok->location());
if (auto Err =
QualifierInsertions.add(tooling::Replacement(SM, DelRange, "")))
DeclarationCleanups.add(tooling::Replacement(SM, DelRange, "")))
Errors = llvm::joinErrors(std::move(Errors), std::move(Err));
}
}

auto DelAttr = [&](const Attr *A) {
if (!A)
return;
auto AttrTokens =
TokBuf.spelledForExpanded(TokBuf.expandedTokens(A->getRange()));
assert(A->getLocation().isValid());
if (!AttrTokens || AttrTokens->empty()) {
Errors = llvm::joinErrors(
std::move(Errors),
llvm::createStringError(
llvm::inconvertibleErrorCode(),
llvm::StringRef("define outline: Can't move out of line as "
"function has a macro `") +
A->getSpelling() + "` specifier."));
return;
}
CharSourceRange DelRange =
syntax::Token::range(SM, AttrTokens->front(), AttrTokens->back())
.toCharRange(SM);
if (auto Err =
DeclarationCleanups.add(tooling::Replacement(SM, DelRange, "")))
Errors = llvm::joinErrors(std::move(Errors), std::move(Err));
};

DelAttr(FD->getAttr<OverrideAttr>());
DelAttr(FD->getAttr<FinalAttr>());

if (FD->isVirtualAsWritten()) {
SourceRange SpecRange{FD->getBeginLoc(), FD->getLocation()};
bool HasErrors = true;

// Clang allows duplicating virtual specifiers so check for multiple
// occurances.
for (const auto &Tok : TokBuf.expandedTokens(SpecRange)) {
if (Tok.kind() != tok::kw_virtual)
continue;
auto Spelling = TokBuf.spelledForExpanded(llvm::makeArrayRef(Tok));
if (!Spelling) {
HasErrors = true;
break;
}
HasErrors = false;
CharSourceRange DelRange =
syntax::Token::range(SM, Spelling->front(), Spelling->back())
.toCharRange(SM);
if (auto Err =
DeclarationCleanups.add(tooling::Replacement(SM, DelRange, "")))
Errors = llvm::joinErrors(std::move(Errors), std::move(Err));
}
if (HasErrors) {
Errors = llvm::joinErrors(
std::move(Errors),
llvm::createStringError(llvm::inconvertibleErrorCode(),
"define outline: Can't move out of line as "
"function has a macro `virtual` specifier."));
}
}

if (Errors)
return std::move(Errors);
return getFunctionSourceAfterReplacements(FD, QualifierInsertions);
return getFunctionSourceAfterReplacements(FD, DeclarationCleanups);
}

struct InsertionPoint {
Expand Down
3 changes: 3 additions & 0 deletions clang-tools-extra/clangd/test/completion-auto-trigger.test
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
# CHECK-NEXT: "insertTextFormat": 1,
# CHECK-NEXT: "kind": 5,
# CHECK-NEXT: "label": " size",
# CHECK-NEXT: "score": {{[0-9]+.[0-9]+}},
# CHECK-NEXT: "sortText": "{{.*}}size",
# CHECK-NEXT: "textEdit": {
# CHECK-NEXT: "newText": "size",
Expand All @@ -46,6 +47,7 @@
# CHECK-NEXT: "insertTextFormat": 1,
# CHECK-NEXT: "kind": 10,
# CHECK-NEXT: "label": " default_capacity",
# CHECK-NEXT: "score": {{[0-9]+.[0-9]+}},
# CHECK-NEXT: "sortText": "{{.*}}default_capacity",
# CHECK-NEXT: "textEdit": {
# CHECK-NEXT: "newText": "default_capacity",
Expand Down Expand Up @@ -86,6 +88,7 @@
# CHECK-NEXT: "insertTextFormat": 1,
# CHECK-NEXT: "kind": 6,
# CHECK-NEXT: "label": " ns_member",
# CHECK-NEXT: "score": {{[0-9]+.[0-9]+}},
# CHECK-NEXT: "sortText": "{{.*}}ns_member",
# CHECK-NEXT: "textEdit": {
# CHECK-NEXT: "newText": "ns_member",
Expand Down
1 change: 1 addition & 0 deletions clang-tools-extra/clangd/test/completion-snippets.test
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
# CHECK-NEXT: "insertTextFormat": 2,
# CHECK-NEXT: "kind": 3,
# CHECK-NEXT: "label": " func_with_args(int a, int b)",
# CHECK-NEXT: "score": {{[0-9]+.[0-9]+}},
# CHECK-NEXT: "sortText": "{{.*}}func_with_args"
# CHECK-NEXT: "textEdit": {
# CHECK-NEXT: "newText": "func_with_args(${1:int a}, ${2:int b})",
Expand Down
2 changes: 2 additions & 0 deletions clang-tools-extra/clangd/test/completion.test
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
# CHECK-NEXT: "insertTextFormat": 1,
# CHECK-NEXT: "kind": 5,
# CHECK-NEXT: "label": " a",
# CHECK-NEXT: "score": {{[0-9]+.[0-9]+}},
# CHECK-NEXT: "sortText": "{{.*}}a"
# CHECK-NEXT: "textEdit": {
# CHECK-NEXT: "newText": "a",
Expand Down Expand Up @@ -50,6 +51,7 @@
# CHECK-NEXT: "insertTextFormat": 1,
# CHECK-NEXT: "kind": 5,
# CHECK-NEXT: "label": " b",
# CHECK-NEXT: "score": {{[0-9]+.[0-9]+}},
# CHECK-NEXT: "sortText": "{{.*}}b"
# CHECK-NEXT: "textEdit": {
# CHECK-NEXT: "newText": "b",
Expand Down
2 changes: 1 addition & 1 deletion clang-tools-extra/clangd/test/hover.test
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
# CHECK-NEXT: "result": {
# CHECK-NEXT: "contents": {
# CHECK-NEXT: "kind": "plaintext",
# CHECK-NEXT: "value": "function foo → void\n\nvoid foo()"
# CHECK-NEXT: "value": "function foo\n\n→ void\n\nvoid foo()"
# CHECK-NEXT: },
# CHECK-NEXT: "range": {
# CHECK-NEXT: "end": {
Expand Down
3 changes: 3 additions & 0 deletions clang-tools-extra/clangd/test/protocol.test
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ Content-Length: 146
# CHECK-NEXT: "insertTextFormat": 1,
# CHECK-NEXT: "kind": 5,
# CHECK-NEXT: "label": " a",
# CHECK-NEXT: "score": {{[0-9]+.[0-9]+}},
# CHECK-NEXT: "sortText": "{{.*}}"
# CHECK: ]
# CHECK-NEXT: }
Expand Down Expand Up @@ -68,6 +69,7 @@ Content-Length: 146
# CHECK-NEXT: "insertTextFormat": 1,
# CHECK-NEXT: "kind": 5,
# CHECK-NEXT: "label": " a",
# CHECK-NEXT: "score": {{[0-9]+.[0-9]+}},
# CHECK-NEXT: "sortText": "{{.*}}"
# CHECK: ]
# CHECK-NEXT: }
Expand Down Expand Up @@ -97,6 +99,7 @@ Content-Length: 146
# CHECK-NEXT: "insertTextFormat": 1,
# CHECK-NEXT: "kind": 5,
# CHECK-NEXT: "label": " a",
# CHECK-NEXT: "score": {{[0-9]+.[0-9]+}},
# CHECK-NEXT: "sortText": "{{.*}}"
# CHECK: ]
# CHECK-NEXT: }
Expand Down
2 changes: 2 additions & 0 deletions clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1643,6 +1643,7 @@ TEST(CompletionTest, Render) {
Include.Header = "\"foo.h\"";
C.Kind = CompletionItemKind::Method;
C.Score.Total = 1.0;
C.Score.ExcludingName = .5;
C.Origin = SymbolOrigin::AST | SymbolOrigin::Static;

CodeCompleteOptions Opts;
Expand All @@ -1660,6 +1661,7 @@ TEST(CompletionTest, Render) {
EXPECT_THAT(R.additionalTextEdits, IsEmpty());
EXPECT_EQ(R.sortText, sortText(1.0, "x"));
EXPECT_FALSE(R.deprecated);
EXPECT_EQ(R.score, .5f);

Opts.EnableSnippets = true;
R = C.render(Opts);
Expand Down
34 changes: 27 additions & 7 deletions clang-tools-extra/clangd/unittests/FormattedStringTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -136,13 +136,37 @@ bar)pt";
EXPECT_EQ(D.asPlainText(), ExpectedText);
}

TEST(Document, Spacer) {
TEST(Document, Ruler) {
Document D;
D.addParagraph().appendText("foo");
D.addSpacer();
D.addRuler();

// Ruler followed by paragraph.
D.addParagraph().appendText("bar");
EXPECT_EQ(D.asMarkdown(), "foo \n\nbar");
EXPECT_EQ(D.asMarkdown(), "foo \n\n---\nbar");
EXPECT_EQ(D.asPlainText(), "foo\n\nbar");

D = Document();
D.addParagraph().appendText("foo");
D.addRuler();
D.addCodeBlock("bar");
// Ruler followed by a codeblock.
EXPECT_EQ(D.asMarkdown(), "foo \n\n---\n```cpp\nbar\n```");
EXPECT_EQ(D.asPlainText(), "foo\n\nbar");

// Ruler followed by another ruler
D = Document();
D.addParagraph().appendText("foo");
D.addRuler();
D.addRuler();
EXPECT_EQ(D.asMarkdown(), "foo");
EXPECT_EQ(D.asPlainText(), "foo");

// Multiple rulers between blocks
D.addRuler();
D.addParagraph().appendText("foo");
EXPECT_EQ(D.asMarkdown(), "foo \n\n---\nfoo");
EXPECT_EQ(D.asPlainText(), "foo\n\nfoo");
}

TEST(Document, Heading) {
Expand Down Expand Up @@ -182,15 +206,11 @@ foo
foo
```)md";
EXPECT_EQ(D.asMarkdown(), ExpectedMarkdown);
// FIXME: we shouldn't have 2 empty lines in between. A solution might be
// having a `verticalMargin` method for blocks, and let container insert new
// lines according to that before/after blocks.
ExpectedPlainText =
R"pt(foo
bar
baz
foo)pt";
EXPECT_EQ(D.asPlainText(), ExpectedPlainText);
}
Expand Down
164 changes: 157 additions & 7 deletions clang-tools-extra/clangd/unittests/HoverTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -327,7 +327,6 @@ class Foo {})cpp";
HI.Name = "X";
HI.LocalScope = "X<T *>::"; // FIXME: X<T *, void>::
HI.Kind = index::SymbolKind::Constructor;
HI.ReturnType = "X<T *>";
HI.Definition = "X()";
HI.Parameters.emplace();
}},
Expand All @@ -337,10 +336,18 @@ class Foo {})cpp";
HI.Name = "~X";
HI.LocalScope = "X::";
HI.Kind = index::SymbolKind::Destructor;
HI.ReturnType = "void";
HI.Definition = "~X()";
HI.Parameters.emplace();
}},
{"class X { operator [[in^t]](); };",
[](HoverInfo &HI) {
HI.NamespaceScope = "";
HI.Name = "operator int";
HI.LocalScope = "X::";
HI.Kind = index::SymbolKind::ConversionFunction;
HI.Definition = "operator int()";
HI.Parameters.emplace();
}},

// auto on lambda
{R"cpp(
Expand Down Expand Up @@ -547,14 +554,38 @@ class Foo {})cpp";
HI.Name = "Foo<X>";
HI.Kind = index::SymbolKind::Class;
}},
{// Falls back to primary template, when the type is not instantiated.
R"cpp(
// comment from primary
template <typename T> class Foo {};
// comment from specialization
template <typename T> class Foo<T*> {};
void foo() {
[[Fo^o]]<int*> *x = nullptr;
}
)cpp",
[](HoverInfo &HI) {
HI.Name = "Foo<int *>";
HI.Kind = index::SymbolKind::Class;
HI.NamespaceScope = "";
HI.Definition = "template <> class Foo<int *>";
// FIXME: Maybe force instantiation to make use of real template
// pattern.
HI.Documentation = "comment from primary";
}},
};
for (const auto &Case : Cases) {
SCOPED_TRACE(Case.Code);

Annotations T(Case.Code);
TestTU TU = TestTU::withCode(T.code());
TU.ExtraArgs.push_back("-std=c++17");
// FIXME: This is no longer necessary, as the default behavior is no delayed
// parsing in the triplet below.
TU.ExtraArgs.push_back("-fno-delayed-template-parsing");
// Types might be different depending on the target triplet, we chose a
// fixed one to make sure tests passes on different platform.
TU.ExtraArgs.push_back("--target=x86_64-pc-linux-gnu");
auto AST = TU.build();
ASSERT_TRUE(AST.getDiagnostics().empty());

Expand Down Expand Up @@ -601,6 +632,19 @@ TEST(Hover, NoHover) {
R"cpp(// non-named decls don't get hover. Don't crash!
^static_assert(1, "");
)cpp",
R"cpp(// non-evaluatable expr
template <typename T> void foo() {
(void)[[size^of]](T);
})cpp",
// literals
"auto x = t^rue;",
"auto x = '^A';",
"auto x = ^(int){42};",
"auto x = ^42.;",
"auto x = ^42.0i;",
"auto x = ^42;",
"auto x = ^nullptr;",
"auto x = ^\"asdf\";",
};

for (const auto &Test : Tests) {
Expand Down Expand Up @@ -1501,6 +1545,76 @@ TEST(Hover, All) {
HI.Name = "cls<cls<cls<int> > >";
HI.Documentation = "type of nested templates.";
}},
{
R"cpp(// type with decltype
int a;
decltype(a) [[b^]] = a;)cpp",
[](HoverInfo &HI) {
HI.Definition = "decltype(a) b = a";
HI.Kind = index::SymbolKind::Variable;
HI.NamespaceScope = "";
HI.Name = "b";
HI.Type = "int";
}},
{
R"cpp(// type with decltype
int a;
decltype(a) c;
decltype(c) [[b^]] = a;)cpp",
[](HoverInfo &HI) {
HI.Definition = "decltype(c) b = a";
HI.Kind = index::SymbolKind::Variable;
HI.NamespaceScope = "";
HI.Name = "b";
HI.Type = "int";
}},
{
R"cpp(// type with decltype
int a;
const decltype(a) [[b^]] = a;)cpp",
[](HoverInfo &HI) {
HI.Definition = "const decltype(a) b = a";
HI.Kind = index::SymbolKind::Variable;
HI.NamespaceScope = "";
HI.Name = "b";
HI.Type = "int";
}},
{
R"cpp(// type with decltype
int a;
auto [[f^oo]](decltype(a) x) -> decltype(a) { return 0; })cpp",
[](HoverInfo &HI) {
HI.Definition = "auto foo(decltype(a) x) -> decltype(a)";
HI.Kind = index::SymbolKind::Function;
HI.NamespaceScope = "";
HI.Name = "foo";
// FIXME: Handle composite types with decltype with a printing
// policy.
HI.Type = "auto (decltype(a)) -> decltype(a)";
HI.ReturnType = "int";
HI.Parameters = {
{std::string("int"), std::string("x"), llvm::None}};
}},
{
R"cpp(// sizeof expr
void foo() {
(void)[[size^of]](char);
})cpp",
[](HoverInfo &HI) {
HI.Name = "expression";
HI.Type = "unsigned long";
HI.Value = "1";
}},
{
R"cpp(// alignof expr
void foo() {
(void)[[align^of]](char);
})cpp",
[](HoverInfo &HI) {
HI.Name = "expression";
HI.Type = "unsigned long";
HI.Value = "1";
}},
};

// Create a tiny index, so tests above can verify documentation is fetched.
Expand All @@ -1518,6 +1632,9 @@ TEST(Hover, All) {
TestTU TU = TestTU::withCode(T.code());
TU.ExtraArgs.push_back("-std=c++17");
TU.ExtraArgs.push_back("-Wno-gnu-designator");
// Types might be different depending on the target triplet, we chose a
// fixed one to make sure tests passes on different platform.
TU.ExtraArgs.push_back("--target=x86_64-pc-linux-gnu");
auto AST = TU.build();
for (const auto &D : AST.getDiagnostics())
ADD_FAILURE() << D;
Expand All @@ -1529,6 +1646,7 @@ TEST(Hover, All) {
Expected.SymRange = T.range();
Case.ExpectedBuilder(Expected);

SCOPED_TRACE(H->present().asPlainText());
EXPECT_EQ(H->NamespaceScope, Expected.NamespaceScope);
EXPECT_EQ(H->LocalScope, Expected.LocalScope);
EXPECT_EQ(H->Name, Expected.Name);
Expand Down Expand Up @@ -1642,7 +1760,7 @@ TEST(Hover, Present) {
HI.Kind = index::SymbolKind::Unknown;
HI.Name = "X";
},
R"(<unknown> X)",
R"(X)",
},
{
[](HoverInfo &HI) {
Expand All @@ -1666,6 +1784,7 @@ TEST(Hover, Present) {
HI.NamespaceScope.emplace();
},
R"(class foo
documentation
template <typename T, typename C = bool> class Foo {})",
Expand All @@ -1688,7 +1807,10 @@ template <typename T, typename C = bool> class Foo {})",
HI.NamespaceScope = "ns::";
HI.Definition = "ret_type foo(params) {}";
},
R"(function foo → ret_type
R"(function foo
→ ret_type
Parameters:
-
- type
- type foo
Expand All @@ -1706,7 +1828,9 @@ ret_type foo(params) {})",
HI.Type = "type";
HI.Definition = "def";
},
R"(variable foo : type
R"(variable foo
Type: type
Value = value
// In test::bar
Expand All @@ -1727,11 +1851,37 @@ TEST(Hover, PresentHeadings) {
HoverInfo HI;
HI.Kind = index::SymbolKind::Variable;
HI.Name = "foo";
HI.Type = "type";

EXPECT_EQ(HI.present().asMarkdown(), "### variable `foo` \\: `type`");
EXPECT_EQ(HI.present().asMarkdown(), "### variable `foo`");
}

// This is a separate test as rulers behave differently in markdown vs
// plaintext.
TEST(Hover, PresentRulers) {
HoverInfo HI;
HI.Kind = index::SymbolKind::Variable;
HI.Name = "foo";
HI.Value = "val";
HI.Definition = "def";

llvm::StringRef ExpectedMarkdown = R"md(### variable `foo`
---
Value \= `val`
---
```cpp
def
```)md";
EXPECT_EQ(HI.present().asMarkdown(), ExpectedMarkdown);

llvm::StringRef ExpectedPlaintext = R"pt(variable foo
Value = val
def)pt";
EXPECT_EQ(HI.present().asPlainText(), ExpectedPlaintext);
}
} // namespace
} // namespace clangd
} // namespace clang
161 changes: 161 additions & 0 deletions clang-tools-extra/clangd/unittests/TweakTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2065,6 +2065,80 @@ TEST_F(DefineOutlineTest, ApplyTest) {
};)cpp",
"Foo::Foo(int z) __attribute__((weak)) : bar(2){}\n",
},
// Virt specifiers.
{
R"cpp(
struct A {
virtual void f^oo() {}
};)cpp",
R"cpp(
struct A {
virtual void foo() ;
};)cpp",
" void A::foo() {}\n",
},
{
R"cpp(
struct A {
virtual virtual void virtual f^oo() {}
};)cpp",
R"cpp(
struct A {
virtual virtual void virtual foo() ;
};)cpp",
" void A::foo() {}\n",
},
{
R"cpp(
struct A {
virtual void foo() = 0;
};
struct B : A {
void fo^o() override {}
};)cpp",
R"cpp(
struct A {
virtual void foo() = 0;
};
struct B : A {
void foo() override ;
};)cpp",
"void B::foo() {}\n",
},
{
R"cpp(
struct A {
virtual void foo() = 0;
};
struct B : A {
void fo^o() final {}
};)cpp",
R"cpp(
struct A {
virtual void foo() = 0;
};
struct B : A {
void foo() final ;
};)cpp",
"void B::foo() {}\n",
},
{
R"cpp(
struct A {
virtual void foo() = 0;
};
struct B : A {
void fo^o() final override {}
};)cpp",
R"cpp(
struct A {
virtual void foo() = 0;
};
struct B : A {
void foo() final override ;
};)cpp",
"void B::foo() {}\n",
},
};
for (const auto &Case : Cases) {
SCOPED_TRACE(Case.Test);
Expand All @@ -2078,6 +2152,8 @@ TEST_F(DefineOutlineTest, HandleMacros) {
llvm::StringMap<std::string> EditedFiles;
ExtraFiles["Test.cpp"] = "";
FileName = "Test.hpp";
ExtraArgs.push_back("-DVIRTUAL=virtual");
ExtraArgs.push_back("-DOVER=override");

struct {
llvm::StringRef Test;
Expand Down Expand Up @@ -2115,6 +2191,48 @@ TEST_F(DefineOutlineTest, HandleMacros) {
#define TARGET foo
void TARGET();)cpp",
"void TARGET(){ return; }"},
{R"cpp(#define VIRT virtual
struct A {
VIRT void f^oo() {}
};)cpp",
R"cpp(#define VIRT virtual
struct A {
VIRT void foo() ;
};)cpp",
" void A::foo() {}\n"},
{R"cpp(
struct A {
VIRTUAL void f^oo() {}
};)cpp",
R"cpp(
struct A {
VIRTUAL void foo() ;
};)cpp",
" void A::foo() {}\n"},
{R"cpp(
struct A {
virtual void foo() = 0;
};
struct B : A {
void fo^o() OVER {}
};)cpp",
R"cpp(
struct A {
virtual void foo() = 0;
};
struct B : A {
void foo() OVER ;
};)cpp",
"void B::foo() {}\n"},
{R"cpp(#define STUPID_MACRO(X) virtual
struct A {
STUPID_MACRO(sizeof sizeof int) void f^oo() {}
};)cpp",
R"cpp(#define STUPID_MACRO(X) virtual
struct A {
STUPID_MACRO(sizeof sizeof int) void foo() ;
};)cpp",
" void A::foo() {}\n"},
};
for (const auto &Case : Cases) {
SCOPED_TRACE(Case.Test);
Expand Down Expand Up @@ -2226,6 +2344,49 @@ TEST_F(DefineOutlineTest, QualifyFunctionName) {
<< Case.TestHeader;
}
}

TEST_F(DefineOutlineTest, FailsMacroSpecifier) {
FileName = "Test.hpp";
ExtraFiles["Test.cpp"] = "";
ExtraArgs.push_back("-DFINALOVER=final override");

std::pair<StringRef, StringRef> Cases[] = {
{
R"cpp(
#define VIRT virtual void
struct A {
VIRT fo^o() {}
};)cpp",
"fail: define outline: Can't move out of line as function has a "
"macro `virtual` specifier."},
{
R"cpp(
#define OVERFINAL final override
struct A {
virtual void foo() {}
};
struct B : A {
void fo^o() OVERFINAL {}
};)cpp",
"fail: define outline: Can't move out of line as function has a "
"macro `override` specifier.\ndefine outline: Can't move out of line "
"as function has a macro `final` specifier."},
{
R"cpp(
struct A {
virtual void foo() {}
};
struct B : A {
void fo^o() FINALOVER {}
};)cpp",
"fail: define outline: Can't move out of line as function has a "
"macro `override` specifier.\ndefine outline: Can't move out of line "
"as function has a macro `final` specifier."},
};
for (const auto &Case : Cases) {
EXPECT_EQ(apply(Case.first), Case.second);
}
}
} // namespace
} // namespace clangd
} // namespace clang
99 changes: 50 additions & 49 deletions clang-tools-extra/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
@@ -1,19 +1,13 @@
====================================================
Extra Clang Tools 10.0.0 (In-Progress) Release Notes
====================================================
======================================
Extra Clang Tools 10.0.0 Release Notes
======================================

.. contents::
:local:
:depth: 3

Written by the `LLVM Team <https://llvm.org/>`_

.. warning::

These are in-progress notes for the upcoming Extra Clang Tools 10 release.
Release notes for previous releases can be found on
`the Download Page <https://releases.llvm.org/download.html>`_.

Introduction
============

Expand All @@ -27,42 +21,68 @@ For more information about Clang or LLVM, including information about
the latest release, please see the `Clang Web Site <https://clang.llvm.org>`_ or
the `LLVM Web Site <https://llvm.org>`_.

Note that if you are reading this file from a Subversion checkout or the
main Clang web page, this document applies to the *next* release, not
the current one. To see the release notes for a specific release, please
see the `releases page <https://llvm.org/releases/>`_.

What's New in Extra Clang Tools 10.0.0?
=======================================

Some of the major new features and improvements to Extra Clang Tools are listed
here. Generic improvements to Extra Clang Tools as a whole or to its underlying
infrastructure are described first, followed by tool-specific sections.

Major New Features
------------------

...

Improvements to clangd
----------------------

The improvements are...
- clangd documentation is now found at https://clangd.llvm.org/

Improvements to clang-doc
-------------------------
- Go-to-definition, hover, find-references etc use a new mechanism to identify
what is under the cursor, which is (hopefully) more consistent and accurate.

- :doc:`clang-doc <clang-doc>` now generates documentation in HTML format.
- clangd should be able to reliably locate the standard library/SDK on macOS.

- Shutdown more cleanly on receiving a signal. In particular temporary PCH files
should be cleaned up.

- Find references now works on macros.

- clangd can be more easily used remotely or in a docker container.

The ``--path-mappings`` flag translates between local and remote paths.

- Experimental support for renaming across files (behind the
``--cross-file-rename`` flag).

- Hover now exposes more information, including the type of symbols and the
value of constant expressions.

- Go to definition now works in dependent code in more cases, by assuming the
primary template is used.

- Better recovery and reporting when the compile command for a file can't be
fully parsed.

- Switch header/source (an extension) now uses index information in addition
to filename heuristics, and is much more robust.

Improvements to clang-query
---------------------------
- Semantic selection (expand/contract selection) is supported.

The improvements are...
- Semantic highlighting is more robust, highlights more types of tokens, and
as an extension provides information about inactive preprocessor regions.

Improvements to clang-rename
----------------------------
- Code completion results now include an extension field ``score``.

The improvements are...
This allows clients to incorporate clangd quality signals when re-ranking code
completion after client-side fuzzy-matching.

- New refactorings:
define function out-of-line, define function in-line, extract function,
remove using namespace directive, localize Objective-C string.

- Bug fixes and performance improvements :-)

Improvements to clang-doc
-------------------------

- :doc:`clang-doc <clang-doc>` now generates documentation in HTML format.

Improvements to clang-tidy
--------------------------
Expand All @@ -73,7 +93,7 @@ New checks
- New :doc:`bugprone-bad-signal-to-kill-thread
<clang-tidy/checks/bugprone-bad-signal-to-kill-thread>` check.

Finds ``pthread_kill`` function calls when a thread is terminated by
Finds ``pthread_kill`` function calls when a thread is terminated by
raising ``SIGTERM`` signal.

- New :doc:`bugprone-dynamic-static-initializers
Expand Down Expand Up @@ -168,7 +188,7 @@ New checks
- New :doc:`readability-qualified-auto
<clang-tidy/checks/readability-qualified-auto>` check.

Adds pointer and ``const`` qualifications to ``auto``-typed variables
Adds pointer and ``const`` qualifications to ``auto``-typed variables
that are deduced to pointers and ``const`` pointers.

- New :doc:`readability-redundant-access-specifiers
Expand Down Expand Up @@ -253,25 +273,6 @@ Renamed checks
- The 'objc-avoid-spinlock' check was renamed to :doc:`darwin-avoid-spinlock
<clang-tidy/checks/darwin-avoid-spinlock>`

Improvements to include-fixer
-----------------------------

The improvements are...

Improvements to clang-include-fixer
-----------------------------------

The improvements are...

Improvements to modularize
--------------------------

The improvements are...

Improvements to pp-trace
------------------------

The improvements are...

Clang-tidy visual studio plugin
-------------------------------
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// RUN: %check_clang_tidy %s bugprone-branch-clone %t -- -- -std=c++17

void handle(int);

template <unsigned Index>
void shouldFail() {
if constexpr (Index == 0) {
// CHECK-MESSAGES: :[[@LINE-1]]:29: warning: repeated branch in conditional chain [bugprone-branch-clone]
handle(0);
} else if constexpr (Index == 1) {
handle(1);
} else {
handle(0);
}
}

template <unsigned Index>
void shouldPass() {
if constexpr (Index == 0) {
handle(0);
} else if constexpr (Index == 1) {
handle(1);
} else {
handle(2);
}
}

void shouldFailNonTemplate() {
constexpr unsigned Index = 1;
if constexpr (Index == 0) {
// CHECK-MESSAGES: :[[@LINE-1]]:29: warning: repeated branch in conditional chain [bugprone-branch-clone]
handle(0);
} else if constexpr (Index == 1) {
handle(1);
} else {
handle(0);
}
}

void shouldPassNonTemplate() {
constexpr unsigned Index = 1;
if constexpr (Index == 0) {
handle(0);
} else if constexpr (Index == 1) {
handle(1);
} else {
handle(2);
}
}

void run() {
shouldFail<0>();
shouldFail<1>();
shouldFail<2>();
shouldPass<0>();
shouldPass<1>();
shouldPass<2>();
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,20 @@ void simple_infinite_loop1() {
int i = 0;
int j = 0;
while (i < 10) {
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this loop is infinite; none of its condition variables (i) are updated in the loop body [bugprone-infinite-loop]
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this loop is infinite; none of its condition variables (i) are updated in the loop body [bugprone-infinite-loop]
j++;
}

while (int k = 10) {
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this loop is infinite; it does not check any variables in the condition [bugprone-infinite-loop]
j--;
}

while (int k = 10) {
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this loop is infinite; it does not check any variables in the condition [bugprone-infinite-loop]
k--;
}

do {
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this loop is infinite; none of its condition variables (i) are updated in the loop body [bugprone-infinite-loop]
j++;
Expand All @@ -27,6 +37,16 @@ void simple_infinite_loop2() {
j++;
}

while (int k = Limit) {
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this loop is infinite; none of its condition variables (Limit) are updated in the loop body [bugprone-infinite-loop]
j--;
}

while (int k = Limit) {
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this loop is infinite; none of its condition variables (Limit) are updated in the loop body [bugprone-infinite-loop]
k--;
}

do {
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this loop is infinite; none of its condition variables (i, Limit) are updated in the loop body [bugprone-infinite-loop]
j++;
Expand All @@ -44,6 +64,22 @@ void simple_not_infinite1() {
// Not an error since 'Limit' is updated.
Limit--;
}

while (Limit--) {
// Not an error since 'Limit' is updated.
i++;
}

while (int k = Limit) {
// Not an error since 'Limit' is updated.
Limit--;
}

while (int k = Limit--) {
// Not an error since 'Limit' is updated.
i++;
}

do {
Limit--;
} while (i < Limit);
Expand Down Expand Up @@ -318,3 +354,12 @@ void lambda_capture() {
(*p)++;
} while (i < Limit);
}

void evaluatable(bool CondVar) {
for (; false && CondVar;) {
}
while (false && CondVar) {
}
do {
} while (false && CondVar);
}
Original file line number Diff line number Diff line change
Expand Up @@ -78,3 +78,9 @@ void init_unit_tests() {
int parens(42);
int braces{42};
}

template <typename RANGE>
void f(RANGE r) {
for (char c : r) {
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// RUN: %check_clang_tidy %s readability-braces-around-statements %t -- -- -std=c++17

void handle(bool);

template <bool branch>
void shouldFail() {
if constexpr (branch)
// CHECK-MESSAGES: :[[@LINE-1]]:24: warning: statement should be inside braces [readability-braces-around-statements]
handle(true);
else
// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: statement should be inside braces [readability-braces-around-statements]
handle(false);
}

template <bool branch>
void shouldPass() {
if constexpr (branch) {
handle(true);
} else {
handle(false);
}
}

void shouldFailNonTemplate() {
constexpr bool branch = false;
if constexpr (branch)
// CHECK-MESSAGES: :[[@LINE-1]]:24: warning: statement should be inside braces [readability-braces-around-statements]
handle(true);
else
// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: statement should be inside braces [readability-braces-around-statements]
handle(false);
}

void shouldPass() {
constexpr bool branch = false;
if constexpr (branch) {
handle(true);
} else {
handle(false);
}
}

void run() {
shouldFail<true>();
shouldFail<false>();
shouldPass<true>();
shouldPass<false>();
}
Original file line number Diff line number Diff line change
Expand Up @@ -213,3 +213,16 @@ int lifeTimeExtensionTests(int a) {
return b;
}
}

void test_B44745() {
// This is the actual minimum test case for the crash in bug 44745. We aren't
// too worried about the warning or fix here, more we don't want a crash.
// CHECK-MESSAGES: :[[@LINE+3]]:5: warning: do not use 'else' after 'return' [readability-else-after-return]
if (auto X = false) {
return;
} else {
for (;;) {
}
}
return;
}
6,186 changes: 6,180 additions & 6 deletions clang/docs/AttributeReference.rst

Large diffs are not rendered by default.

600 changes: 490 additions & 110 deletions clang/docs/ClangCommandLineReference.rst

Large diffs are not rendered by default.

11 changes: 10 additions & 1 deletion clang/docs/CommandGuide/clang.rst
Original file line number Diff line number Diff line change
Expand Up @@ -278,9 +278,18 @@ Language Selection and Mode Options
Make all string literals default to writable. This disables uniquing of
strings and other optimizations.

.. option:: -flax-vector-conversions
.. option:: -flax-vector-conversions, -flax-vector-conversions=<kind>, -fno-lax-vector-conversions

Allow loose type checking rules for implicit vector conversions.
Possible values of <kind>:

- ``none``: allow no implicit conversions between vectors
- ``integer``: allow implicit bitcasts between integer vectors of the same
overall bit-width
- ``all``: allow implicit bitcasts between any vectors of the same
overall bit-width

<kind> defaults to ``integer`` if unspecified.

.. option:: -fblocks

Expand Down
3,764 changes: 2,923 additions & 841 deletions clang/docs/DiagnosticsReference.rst

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion clang/docs/LanguageExtensions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ of ``cxx_rvalue_references``.
``__has_cpp_attribute``
-----------------------

This function-like macro is available in C++2a by default, and is provided as an
This function-like macro is available in C++20 by default, and is provided as an
extension in earlier language standards. It takes a single argument that is the
name of a double-square-bracket-style attribute. The argument can either be a
single identifier or a scoped identifier. If the attribute is supported, a
Expand Down
400 changes: 276 additions & 124 deletions clang/docs/ReleaseNotes.rst

Large diffs are not rendered by default.

153 changes: 101 additions & 52 deletions clang/docs/UsersManual.rst

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion clang/examples/clang-interpreter/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,8 @@ class SimpleJIT {
MangleAndInterner Mangle{ES, DL};
JITDylib &MainJD{ES.createJITDylib("<main>")};
RTDyldObjectLinkingLayer ObjectLayer{ES, createMemMgr};
IRCompileLayer CompileLayer{ES, ObjectLayer, SimpleCompiler(*TM)};
IRCompileLayer CompileLayer{ES, ObjectLayer,
std::make_unique<SimpleCompiler>(*TM)};

static std::unique_ptr<SectionMemoryManager> createMemMgr() {
return std::make_unique<SectionMemoryManager>();
Expand Down
31 changes: 25 additions & 6 deletions clang/include/clang/AST/ASTConcept.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,25 @@
#include <utility>
namespace clang {
class ConceptDecl;
class ConceptSpecializationExpr;

/// The result of a constraint satisfaction check, containing the necessary
/// information to diagnose an unsatisfied constraint.
class ConstraintSatisfaction : public llvm::FoldingSetNode {
// The template-like entity that 'owns' the constraint checked here (can be a
// constrained entity or a concept).
const NamedDecl *ConstraintOwner = nullptr;
llvm::SmallVector<TemplateArgument, 4> TemplateArgs;

public:

ConstraintSatisfaction() = default;

ConstraintSatisfaction(const NamedDecl *ConstraintOwner,
ArrayRef<TemplateArgument> TemplateArgs) :
ConstraintOwner(ConstraintOwner), TemplateArgs(TemplateArgs.begin(),
TemplateArgs.end()) { }

/// \brief The result of a constraint satisfaction check, containing the
/// necessary information to diagnose an unsatisfied constraint.
struct ConstraintSatisfaction {
using SubstitutionDiagnostic = std::pair<SourceLocation, StringRef>;
using Detail = llvm::PointerUnion<Expr *, SubstitutionDiagnostic *>;

Expand All @@ -37,9 +52,13 @@ struct ConstraintSatisfaction {
/// invalid expression.
llvm::SmallVector<std::pair<const Expr *, Detail>, 4> Details;

// This can leak if used in an AST node, use ASTConstraintSatisfaction
// instead.
void *operator new(size_t bytes, ASTContext &C) = delete;
void Profile(llvm::FoldingSetNodeID &ID, const ASTContext &C) {
Profile(ID, C, ConstraintOwner, TemplateArgs);
}

static void Profile(llvm::FoldingSetNodeID &ID, const ASTContext &C,
const NamedDecl *ConstraintOwner,
ArrayRef<TemplateArgument> TemplateArgs);
};

/// Pairs of unsatisfied atomic constraint expressions along with the
Expand Down
7 changes: 5 additions & 2 deletions clang/include/clang/AST/ASTContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ class AtomicExpr;
class BlockExpr;
class BuiltinTemplateDecl;
class CharUnits;
class ConceptDecl;
class CXXABI;
class CXXConstructorDecl;
class CXXMethodDecl;
Expand Down Expand Up @@ -211,7 +212,7 @@ class ASTContext : public RefCountedBase<ASTContext> {
mutable llvm::FoldingSet<ObjCObjectPointerType> ObjCObjectPointerTypes;
mutable llvm::FoldingSet<DependentUnaryTransformType>
DependentUnaryTransformTypes;
mutable llvm::FoldingSet<AutoType> AutoTypes;
mutable llvm::ContextualFoldingSet<AutoType, ASTContext&> AutoTypes;
mutable llvm::FoldingSet<DeducedTemplateSpecializationType>
DeducedTemplateSpecializationTypes;
mutable llvm::FoldingSet<AtomicType> AtomicTypes;
Expand Down Expand Up @@ -1542,7 +1543,9 @@ class ASTContext : public RefCountedBase<ASTContext> {

/// C++11 deduced auto type.
QualType getAutoType(QualType DeducedType, AutoTypeKeyword Keyword,
bool IsDependent, bool IsPack = false) const;
bool IsDependent, bool IsPack = false,
ConceptDecl *TypeConstraintConcept = nullptr,
ArrayRef<TemplateArgument> TypeConstraintArgs ={}) const;

/// C++11 deduction pattern for 'auto' type.
QualType getAutoDeductType() const;
Expand Down
4 changes: 2 additions & 2 deletions clang/include/clang/AST/ASTNodeTraverser.h
Original file line number Diff line number Diff line change
Expand Up @@ -548,8 +548,8 @@ class ASTNodeTraverser
}

void VisitNonTypeTemplateParmDecl(const NonTypeTemplateParmDecl *D) {
if (const auto *TC = D->getPlaceholderTypeConstraint())
Visit(TC->getImmediatelyDeclaredConstraint());
if (const auto *E = D->getPlaceholderTypeConstraint())
Visit(E);
if (D->hasDefaultArgument())
Visit(D->getDefaultArgument(), SourceRange(),
D->getDefaultArgStorage().getInheritedFrom(),
Expand Down
31 changes: 31 additions & 0 deletions clang/include/clang/AST/DeclCXX.h
Original file line number Diff line number Diff line change
Expand Up @@ -1893,6 +1893,37 @@ class CXXDeductionGuideDecl : public FunctionDecl {
static bool classofKind(Kind K) { return K == CXXDeductionGuide; }
};

/// \brief Represents the body of a requires-expression.
///
/// This decl exists merely to serve as the DeclContext for the local
/// parameters of the requires expression as well as other declarations inside
/// it.
///
/// \code
/// template<typename T> requires requires (T t) { {t++} -> regular; }
/// \endcode
///
/// In this example, a RequiresExpr object will be generated for the expression,
/// and a RequiresExprBodyDecl will be created to hold the parameter t and the
/// template argument list imposed by the compound requirement.
class RequiresExprBodyDecl : public Decl, public DeclContext {
RequiresExprBodyDecl(ASTContext &C, DeclContext *DC, SourceLocation StartLoc)
: Decl(RequiresExprBody, DC, StartLoc), DeclContext(RequiresExprBody) {}

public:
friend class ASTDeclReader;
friend class ASTDeclWriter;

static RequiresExprBodyDecl *Create(ASTContext &C, DeclContext *DC,
SourceLocation StartLoc);

static RequiresExprBodyDecl *CreateDeserialized(ASTContext &C, unsigned ID);

// Implement isa/cast/dyncast/etc.
static bool classof(const Decl *D) { return classofKind(D->getKind()); }
static bool classofKind(Kind K) { return K == RequiresExprBody; }
};

/// Represents a static or instance method of a struct/union/class.
///
/// In the terminology of the C++ Standard, these are the (static and
Expand Down
43 changes: 29 additions & 14 deletions clang/include/clang/AST/DeclTemplate.h
Original file line number Diff line number Diff line change
Expand Up @@ -1102,6 +1102,17 @@ class FunctionTemplateDecl : public RedeclarableTemplateDecl {
/// template.
ArrayRef<TemplateArgument> getInjectedTemplateArgs();

/// Return whether this function template is an abbreviated function template,
/// e.g. `void foo(auto x)` or `template<typename T> void foo(auto x)`
bool isAbbreviated() const {
// Since the invented template parameters generated from 'auto' parameters
// are either appended to the end of the explicit template parameter list or
// form a new template paramter list, we can simply observe the last
// parameter to determine if such a thing happened.
const TemplateParameterList *TPL = getTemplateParameters();
return TPL->getParam(TPL->size() - 1)->isImplicit();
}

/// Merge \p Prev with our RedeclarableTemplateDecl::Common.
void mergePrevDecl(FunctionTemplateDecl *Prev);

Expand Down Expand Up @@ -1215,7 +1226,6 @@ class TemplateTypeParmDecl final : public TypeDecl,
bool ParameterPack,
bool HasTypeConstraint = false,
Optional<unsigned> NumExpanded = None);

static TemplateTypeParmDecl *CreateDeserialized(const ASTContext &C,
unsigned ID);
static TemplateTypeParmDecl *CreateDeserialized(const ASTContext &C,
Expand Down Expand Up @@ -1374,7 +1384,8 @@ class NonTypeTemplateParmDecl final
: public DeclaratorDecl,
protected TemplateParmPosition,
private llvm::TrailingObjects<NonTypeTemplateParmDecl,
std::pair<QualType, TypeSourceInfo *>> {
std::pair<QualType, TypeSourceInfo *>,
Expr *> {
friend class ASTDeclReader;
friend TrailingObjects;

Expand Down Expand Up @@ -1429,10 +1440,12 @@ class NonTypeTemplateParmDecl final
ArrayRef<TypeSourceInfo *> ExpandedTInfos);

static NonTypeTemplateParmDecl *CreateDeserialized(ASTContext &C,
unsigned ID);
unsigned ID,
bool HasTypeConstraint);
static NonTypeTemplateParmDecl *CreateDeserialized(ASTContext &C,
unsigned ID,
unsigned NumExpandedTypes);
unsigned NumExpandedTypes,
bool HasTypeConstraint);

using TemplateParmPosition::getDepth;
using TemplateParmPosition::setDepth;
Expand Down Expand Up @@ -1543,20 +1556,22 @@ class NonTypeTemplateParmDecl final
return TypesAndInfos[I].second;
}

/// Return the type-constraint in the placeholder type of this non-type
/// Return the constraint introduced by the placeholder type of this non-type
/// template parameter (if any).
TypeConstraint *getPlaceholderTypeConstraint() const {
// TODO: Concepts: Implement once we have actual placeholders with type
// constraints.
return nullptr;
Expr *getPlaceholderTypeConstraint() const {
return hasPlaceholderTypeConstraint() ? *getTrailingObjects<Expr *>() :
nullptr;
}

void setPlaceholderTypeConstraint(Expr *E) {
*getTrailingObjects<Expr *>() = E;
}

/// Determine whether this non-type template parameter's type has a
/// placeholder with a type-constraint.
bool hasPlaceholderTypeConstraint() const {
// TODO: Concepts: Implement once we have actual placeholders with type
// constraints.
return false;
auto *AT = getType()->getContainedAutoType();
return AT && AT->isConstrained();
}

/// \brief Get the associated-constraints of this template parameter.
Expand All @@ -1566,8 +1581,8 @@ class NonTypeTemplateParmDecl final
/// Use this instead of getPlaceholderImmediatelyDeclaredConstraint for
/// concepts APIs that accept an ArrayRef of constraint expressions.
void getAssociatedConstraints(llvm::SmallVectorImpl<const Expr *> &AC) const {
if (TypeConstraint *TC = getPlaceholderTypeConstraint())
AC.push_back(TC->getImmediatelyDeclaredConstraint());
if (Expr *E = getPlaceholderTypeConstraint())
AC.push_back(E);
}

// Implement isa/cast/dyncast/etc.
Expand Down
22 changes: 14 additions & 8 deletions clang/include/clang/AST/Expr.h
Original file line number Diff line number Diff line change
Expand Up @@ -3955,14 +3955,18 @@ class StmtExpr : public Expr {
Stmt *SubStmt;
SourceLocation LParenLoc, RParenLoc;
public:
// FIXME: Does type-dependence need to be computed differently?
// FIXME: Do we need to compute instantiation instantiation-dependence for
// statements? (ugh!)
StmtExpr(CompoundStmt *substmt, QualType T,
SourceLocation lp, SourceLocation rp) :
Expr(StmtExprClass, T, VK_RValue, OK_Ordinary,
T->isDependentType(), false, false, false),
SubStmt(substmt), LParenLoc(lp), RParenLoc(rp) { }
StmtExpr(CompoundStmt *SubStmt, QualType T, SourceLocation LParenLoc,
SourceLocation RParenLoc, unsigned TemplateDepth)
: // We treat a statement-expression in a dependent context as
// always being value- and instantiation-dependent. This matches the
// behavior of lambda-expressions and GCC.
Expr(StmtExprClass, T, VK_RValue, OK_Ordinary, T->isDependentType(),
TemplateDepth != 0, TemplateDepth != 0, false),
SubStmt(SubStmt), LParenLoc(LParenLoc), RParenLoc(RParenLoc) {
// FIXME: A templated statement expression should have an associated
// DeclContext so that nested declarations always have a dependent context.
StmtExprBits.TemplateDepth = TemplateDepth;
}

/// Build an empty statement expression.
explicit StmtExpr(EmptyShell Empty) : Expr(StmtExprClass, Empty) { }
Expand All @@ -3979,6 +3983,8 @@ class StmtExpr : public Expr {
SourceLocation getRParenLoc() const { return RParenLoc; }
void setRParenLoc(SourceLocation L) { RParenLoc = L; }

unsigned getTemplateDepth() const { return StmtExprBits.TemplateDepth; }

static bool classof(const Stmt *T) {
return T->getStmtClass() == StmtExprClass;
}
Expand Down
94 changes: 0 additions & 94 deletions clang/include/clang/AST/ExprCXX.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
#ifndef LLVM_CLANG_AST_EXPRCXX_H
#define LLVM_CLANG_AST_EXPRCXX_H

#include "clang/AST/ASTConcept.h"
#include "clang/AST/Decl.h"
#include "clang/AST/DeclBase.h"
#include "clang/AST/DeclCXX.h"
Expand Down Expand Up @@ -4836,99 +4835,6 @@ class BuiltinBitCastExpr final
}
};

/// \brief Represents the specialization of a concept - evaluates to a prvalue
/// of type bool.
///
/// According to C++2a [expr.prim.id]p3 an id-expression that denotes the
/// specialization of a concept results in a prvalue of type bool.
class ConceptSpecializationExpr final : public Expr, public ConceptReference,
private llvm::TrailingObjects<ConceptSpecializationExpr,
TemplateArgument> {
friend class ASTStmtReader;
friend TrailingObjects;
public:
using SubstitutionDiagnostic = std::pair<SourceLocation, std::string>;

protected:
/// \brief The number of template arguments in the tail-allocated list of
/// converted template arguments.
unsigned NumTemplateArgs;

/// \brief Information about the satisfaction of the named concept with the
/// given arguments. If this expression is value dependent, this is to be
/// ignored.
ASTConstraintSatisfaction *Satisfaction;

ConceptSpecializationExpr(const ASTContext &C, NestedNameSpecifierLoc NNS,
SourceLocation TemplateKWLoc,
DeclarationNameInfo ConceptNameInfo,
NamedDecl *FoundDecl, ConceptDecl *NamedConcept,
const ASTTemplateArgumentListInfo *ArgsAsWritten,
ArrayRef<TemplateArgument> ConvertedArgs,
const ConstraintSatisfaction *Satisfaction);

ConceptSpecializationExpr(EmptyShell Empty, unsigned NumTemplateArgs);

public:

static ConceptSpecializationExpr *
Create(const ASTContext &C, NestedNameSpecifierLoc NNS,
SourceLocation TemplateKWLoc, DeclarationNameInfo ConceptNameInfo,
NamedDecl *FoundDecl, ConceptDecl *NamedConcept,
const ASTTemplateArgumentListInfo *ArgsAsWritten,
ArrayRef<TemplateArgument> ConvertedArgs,
const ConstraintSatisfaction *Satisfaction);

static ConceptSpecializationExpr *
Create(ASTContext &C, EmptyShell Empty, unsigned NumTemplateArgs);

ArrayRef<TemplateArgument> getTemplateArguments() const {
return ArrayRef<TemplateArgument>(getTrailingObjects<TemplateArgument>(),
NumTemplateArgs);
}

/// \brief Set new template arguments for this concept specialization.
void setTemplateArguments(ArrayRef<TemplateArgument> Converted);

/// \brief Whether or not the concept with the given arguments was satisfied
/// when the expression was created.
/// The expression must not be dependent.
bool isSatisfied() const {
assert(!isValueDependent()
&& "isSatisfied called on a dependent ConceptSpecializationExpr");
return Satisfaction->IsSatisfied;
}

/// \brief Get elaborated satisfaction info about the template arguments'
/// satisfaction of the named concept.
/// The expression must not be dependent.
const ASTConstraintSatisfaction &getSatisfaction() const {
assert(!isValueDependent()
&& "getSatisfaction called on dependent ConceptSpecializationExpr");
return *Satisfaction;
}

static bool classof(const Stmt *T) {
return T->getStmtClass() == ConceptSpecializationExprClass;
}

SourceLocation getBeginLoc() const LLVM_READONLY {
return ConceptName.getBeginLoc();
}

SourceLocation getEndLoc() const LLVM_READONLY {
return ArgsAsWritten->RAngleLoc;
}

// Iterators
child_range children() {
return child_range(child_iterator(), child_iterator());
}
const_child_range children() const {
return const_child_range(const_child_iterator(), const_child_iterator());
}
};

} // namespace clang

#endif // LLVM_CLANG_AST_EXPRCXX_H
553 changes: 553 additions & 0 deletions clang/include/clang/AST/ExprConcepts.h

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions clang/include/clang/AST/PropertiesBase.td
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,8 @@ def DeclRef : RefPropertyType<"Decl"> { let ConstWhenWriting = 1; }
SubclassPropertyType<"TagDecl", DeclRef>;
def TemplateDeclRef :
SubclassPropertyType<"TemplateDecl", DeclRef>;
def ConceptDeclRef :
SubclassPropertyType<"ConceptDecl", DeclRef>;
def TemplateTypeParmDeclRef :
SubclassPropertyType<"TemplateTypeParmDecl", DeclRef>;
def TemplateTemplateParmDeclRef :
Expand Down
39 changes: 38 additions & 1 deletion clang/include/clang/AST/RecursiveASTVisitor.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include "clang/AST/DeclOpenMP.h"
#include "clang/AST/DeclTemplate.h"
#include "clang/AST/Expr.h"
#include "clang/AST/ExprConcepts.h"
#include "clang/AST/ExprCXX.h"
#include "clang/AST/ExprObjC.h"
#include "clang/AST/ExprOpenMP.h"
Expand Down Expand Up @@ -1039,7 +1040,13 @@ DEF_TRAVERSE_TYPE(UnaryTransformType, {
TRY_TO(TraverseType(T->getUnderlyingType()));
})

DEF_TRAVERSE_TYPE(AutoType, { TRY_TO(TraverseType(T->getDeducedType())); })
DEF_TRAVERSE_TYPE(AutoType, {
TRY_TO(TraverseType(T->getDeducedType()));
if (T->isConstrained()) {
TRY_TO(TraverseDecl(T->getTypeConstraintConcept()));
TRY_TO(TraverseTemplateArguments(T->getArgs(), T->getNumArgs()));
}
})
DEF_TRAVERSE_TYPE(DeducedTemplateSpecializationType, {
TRY_TO(TraverseTemplateName(T->getTemplateName()));
TRY_TO(TraverseType(T->getDeducedType()));
Expand Down Expand Up @@ -1286,6 +1293,12 @@ DEF_TRAVERSE_TYPELOC(UnaryTransformType, {

DEF_TRAVERSE_TYPELOC(AutoType, {
TRY_TO(TraverseType(TL.getTypePtr()->getDeducedType()));
if (TL.isConstrained()) {
TRY_TO(TraverseNestedNameSpecifierLoc(TL.getNestedNameSpecifierLoc()));
TRY_TO(TraverseDeclarationNameInfo(TL.getConceptNameInfo()));
for (unsigned I = 0, E = TL.getNumArgs(); I != E; ++I)
TRY_TO(TraverseTemplateArgumentLoc(TL.getArgLoc(I)));
}
})

DEF_TRAVERSE_TYPELOC(DeducedTemplateSpecializationType, {
Expand Down Expand Up @@ -2138,6 +2151,8 @@ DEF_TRAVERSE_DECL(ParmVarDecl, {
TRY_TO(TraverseStmt(D->getDefaultArg()));
})

DEF_TRAVERSE_DECL(RequiresExprBodyDecl, {})

#undef DEF_TRAVERSE_DECL

// ----------------- Stmt traversal -----------------
Expand Down Expand Up @@ -2709,6 +2724,28 @@ DEF_TRAVERSE_STMT(ConceptSpecializationExpr, {
TRY_TO(TraverseConceptReference(*S));
})

DEF_TRAVERSE_STMT(RequiresExpr, {
TRY_TO(TraverseDecl(S->getBody()));
for (ParmVarDecl *Parm : S->getLocalParameters())
TRY_TO(TraverseDecl(Parm));
for (concepts::Requirement *Req : S->getRequirements())
if (auto *TypeReq = dyn_cast<concepts::TypeRequirement>(Req)) {
if (!TypeReq->isSubstitutionFailure())
TRY_TO(TraverseTypeLoc(TypeReq->getType()->getTypeLoc()));
} else if (auto *ExprReq = dyn_cast<concepts::ExprRequirement>(Req)) {
if (!ExprReq->isExprSubstitutionFailure())
TRY_TO(TraverseStmt(ExprReq->getExpr()));
auto &RetReq = ExprReq->getReturnTypeRequirement();
if (RetReq.isTypeConstraint())
TRY_TO(TraverseTemplateParameterListHelper(
RetReq.getTypeConstraintTemplateParameterList()));
} else {
auto *NestedReq = cast<concepts::NestedRequirement>(Req);
if (!NestedReq->isSubstitutionFailure())
TRY_TO(TraverseStmt(NestedReq->getConstraintExpr()));
}
})

// These literals (all of them) do not need any action.
DEF_TRAVERSE_STMT(IntegerLiteral, {})
DEF_TRAVERSE_STMT(FixedPointLiteral, {})
Expand Down
27 changes: 27 additions & 0 deletions clang/include/clang/AST/Stmt.h
Original file line number Diff line number Diff line change
Expand Up @@ -588,6 +588,18 @@ class alignas(void *) Stmt {
unsigned Kind : 2;
};

class StmtExprBitfields {
friend class ASTStmtReader;
friend class StmtExpr;

unsigned : NumExprBits;

/// The number of levels of template parameters enclosing this statement
/// expression. Used to determine if a statement expression remains
/// dependent after instantiation.
unsigned TemplateDepth;
};

//===--- C++ Expression bitfields classes ---===//

class CXXOperatorCallExprBitfields {
Expand Down Expand Up @@ -910,6 +922,17 @@ class alignas(void *) Stmt {
SourceLocation NameLoc;
};

class RequiresExprBitfields {
friend class ASTStmtReader;
friend class ASTStmtWriter;
friend class RequiresExpr;

unsigned : NumExprBits;

unsigned IsSatisfied : 1;
SourceLocation RequiresKWLoc;
};

//===--- C++ Coroutines TS bitfields classes ---===//

class CoawaitExprBitfields {
Expand Down Expand Up @@ -985,6 +1008,9 @@ class alignas(void *) Stmt {
PseudoObjectExprBitfields PseudoObjectExprBits;
SourceLocExprBitfields SourceLocExprBits;

// GNU Extensions.
StmtExprBitfields StmtExprBits;

// C++ Expressions
CXXOperatorCallExprBitfields CXXOperatorCallExprBits;
CXXRewrittenBinaryOperatorBitfields CXXRewrittenBinaryOperatorBits;
Expand All @@ -1008,6 +1034,7 @@ class alignas(void *) Stmt {
UnresolvedMemberExprBitfields UnresolvedMemberExprBits;
CXXNoexceptExprBitfields CXXNoexceptExprBits;
SubstNonTypeTemplateParmExprBitfields SubstNonTypeTemplateParmExprBits;
RequiresExprBitfields RequiresExprBits;

// C++ Coroutines TS expressions
CoawaitExprBitfields CoawaitBits;
Expand Down
1 change: 1 addition & 0 deletions clang/include/clang/AST/StmtVisitor.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#ifndef LLVM_CLANG_AST_STMTVISITOR_H
#define LLVM_CLANG_AST_STMTVISITOR_H

#include "clang/AST/ExprConcepts.h"
#include "clang/AST/ExprCXX.h"
#include "clang/AST/ExprObjC.h"
#include "clang/AST/ExprOpenMP.h"
Expand Down
7 changes: 6 additions & 1 deletion clang/include/clang/AST/TemplateBase.h
Original file line number Diff line number Diff line change
Expand Up @@ -637,7 +637,7 @@ struct ASTTemplateArgumentListInfo final
}

static const ASTTemplateArgumentListInfo *
Create(ASTContext &C, const TemplateArgumentListInfo &List);
Create(const ASTContext &C, const TemplateArgumentListInfo &List);
};

/// Represents an explicit template argument list in C++, e.g.,
Expand Down Expand Up @@ -702,6 +702,11 @@ inline const TemplateArgument &
return getArgs()[Idx];
}

inline const TemplateArgument &AutoType::getArg(unsigned Idx) const {
assert(Idx < getNumArgs() && "Template argument out of range");
return getArgs()[Idx];
}

} // namespace clang

#endif // LLVM_CLANG_AST_TEMPLATEBASE_H
74 changes: 56 additions & 18 deletions clang/include/clang/AST/Type.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ namespace clang {

class ExtQuals;
class QualType;
class ConceptDecl;
class TagDecl;
class Type;

Expand Down Expand Up @@ -1683,6 +1684,15 @@ class alignas(8) Type : public ExtQualsTypeCommonBase {
/// Was this placeholder type spelled as 'auto', 'decltype(auto)',
/// or '__auto_type'? AutoTypeKeyword value.
unsigned Keyword : 2;

/// The number of template arguments in the type-constraints, which is
/// expected to be able to hold at least 1024 according to [implimits].
/// However as this limit is somewhat easy to hit with template
/// metaprogramming we'd prefer to keep it as large as possible.
/// At the moment it has been left as a non-bitfield since this type
/// safely fits in 64 bits as an unsigned, so there is no reason to
/// introduce the performance impact of a bitfield.
unsigned NumArgs;
};

class SubstTemplateTypeParmPackTypeBitfields {
Expand Down Expand Up @@ -4814,8 +4824,7 @@ class SubstTemplateTypeParmPackType : public Type, public llvm::FoldingSetNode {

/// Common base class for placeholders for types that get replaced by
/// placeholder type deduction: C++11 auto, C++14 decltype(auto), C++17 deduced
/// class template types, and (eventually) constrained type names from the C++
/// Concepts TS.
/// class template types, and constrained type names.
///
/// These types are usually a placeholder for a deduced type. However, before
/// the initializer is attached, or (usually) if the initializer is
Expand Down Expand Up @@ -4860,18 +4869,50 @@ class DeducedType : public Type {
}
};

/// Represents a C++11 auto or C++14 decltype(auto) type.
class AutoType : public DeducedType, public llvm::FoldingSetNode {
/// Represents a C++11 auto or C++14 decltype(auto) type, possibly constrained
/// by a type-constraint.
class alignas(8) AutoType : public DeducedType, public llvm::FoldingSetNode {
friend class ASTContext; // ASTContext creates these

ConceptDecl *TypeConstraintConcept;

AutoType(QualType DeducedAsType, AutoTypeKeyword Keyword,
bool IsDeducedAsDependent, bool IsDeducedAsPack)
: DeducedType(Auto, DeducedAsType, IsDeducedAsDependent,
IsDeducedAsDependent, IsDeducedAsPack) {
AutoTypeBits.Keyword = (unsigned)Keyword;
bool IsDeducedAsDependent, bool IsDeducedAsPack, ConceptDecl *CD,
ArrayRef<TemplateArgument> TypeConstraintArgs);

const TemplateArgument *getArgBuffer() const {
return reinterpret_cast<const TemplateArgument*>(this+1);
}

TemplateArgument *getArgBuffer() {
return reinterpret_cast<TemplateArgument*>(this+1);
}

public:
/// Retrieve the template arguments.
const TemplateArgument *getArgs() const {
return getArgBuffer();
}

/// Retrieve the number of template arguments.
unsigned getNumArgs() const {
return AutoTypeBits.NumArgs;
}

const TemplateArgument &getArg(unsigned Idx) const; // in TemplateBase.h

ArrayRef<TemplateArgument> getTypeConstraintArguments() const {
return {getArgs(), getNumArgs()};
}

ConceptDecl *getTypeConstraintConcept() const {
return TypeConstraintConcept;
}

bool isConstrained() const {
return TypeConstraintConcept != nullptr;
}

bool isDecltypeAuto() const {
return getKeyword() == AutoTypeKeyword::DecltypeAuto;
}
Expand All @@ -4880,18 +4921,15 @@ class AutoType : public DeducedType, public llvm::FoldingSetNode {
return (AutoTypeKeyword)AutoTypeBits.Keyword;
}

void Profile(llvm::FoldingSetNodeID &ID) {
Profile(ID, getDeducedType(), getKeyword(), isDependentType(),
containsUnexpandedParameterPack());
void Profile(llvm::FoldingSetNodeID &ID, const ASTContext &Context) {
Profile(ID, Context, getDeducedType(), getKeyword(), isDependentType(),
getTypeConstraintConcept(), getTypeConstraintArguments());
}

static void Profile(llvm::FoldingSetNodeID &ID, QualType Deduced,
AutoTypeKeyword Keyword, bool IsDependent, bool IsPack) {
ID.AddPointer(Deduced.getAsOpaquePtr());
ID.AddInteger((unsigned)Keyword);
ID.AddBoolean(IsDependent);
ID.AddBoolean(IsPack);
}
static void Profile(llvm::FoldingSetNodeID &ID, const ASTContext &Context,
QualType Deduced, AutoTypeKeyword Keyword,
bool IsDependent, ConceptDecl *CD,
ArrayRef<TemplateArgument> Arguments);

static bool classof(const Type *T) {
return T->getTypeClass() == Auto;
Expand Down
138 changes: 137 additions & 1 deletion clang/include/clang/AST/TypeLoc.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#ifndef LLVM_CLANG_AST_TYPELOC_H
#define LLVM_CLANG_AST_TYPELOC_H

#include "clang/AST/DeclarationName.h"
#include "clang/AST/NestedNameSpecifier.h"
#include "clang/AST/TemplateBase.h"
#include "clang/AST/Type.h"
Expand All @@ -34,6 +35,7 @@ namespace clang {
class Attr;
class ASTContext;
class CXXRecordDecl;
class ConceptDecl;
class Expr;
class ObjCInterfaceDecl;
class ObjCProtocolDecl;
Expand Down Expand Up @@ -181,6 +183,11 @@ class TypeLoc {
/// AttributedTypeLoc, for those type attributes that behave as qualifiers
TypeLoc findExplicitQualifierLoc() const;

/// Get the typeloc of an AutoType whose type will be deduced for a variable
/// with an initializer of this type. This looks through declarators like
/// pointer types, but not through decltype or typedefs.
AutoTypeLoc getContainedAutoTypeLoc() const;

/// Initializes this to state that every location in this
/// type is the given location.
///
Expand Down Expand Up @@ -1923,8 +1930,137 @@ class DeducedTypeLoc
: public InheritingConcreteTypeLoc<TypeSpecTypeLoc, DeducedTypeLoc,
DeducedType> {};

struct AutoTypeLocInfo : TypeSpecLocInfo {
NestedNameSpecifierLoc NestedNameSpec;
SourceLocation TemplateKWLoc;
SourceLocation ConceptNameLoc;
NamedDecl *FoundDecl;
SourceLocation LAngleLoc;
SourceLocation RAngleLoc;
};

class AutoTypeLoc
: public InheritingConcreteTypeLoc<DeducedTypeLoc, AutoTypeLoc, AutoType> {
: public ConcreteTypeLoc<DeducedTypeLoc,
AutoTypeLoc,
AutoType,
AutoTypeLocInfo> {
public:
AutoTypeKeyword getAutoKeyword() const {
return getTypePtr()->getKeyword();
}

bool isConstrained() const {
return getTypePtr()->isConstrained();
}

const NestedNameSpecifierLoc &getNestedNameSpecifierLoc() const {
return getLocalData()->NestedNameSpec;
}

void setNestedNameSpecifierLoc(NestedNameSpecifierLoc NNS) {
getLocalData()->NestedNameSpec = NNS;
}

SourceLocation getTemplateKWLoc() const {
return getLocalData()->TemplateKWLoc;
}

void setTemplateKWLoc(SourceLocation Loc) {
getLocalData()->TemplateKWLoc = Loc;
}

SourceLocation getConceptNameLoc() const {
return getLocalData()->ConceptNameLoc;
}

void setConceptNameLoc(SourceLocation Loc) {
getLocalData()->ConceptNameLoc = Loc;
}

NamedDecl *getFoundDecl() const {
return getLocalData()->FoundDecl;
}

void setFoundDecl(NamedDecl *D) {
getLocalData()->FoundDecl = D;
}

ConceptDecl *getNamedConcept() const {
return getTypePtr()->getTypeConstraintConcept();
}

DeclarationNameInfo getConceptNameInfo() const;

bool hasExplicitTemplateArgs() const {
return getLocalData()->LAngleLoc.isValid();
}

SourceLocation getLAngleLoc() const {
return this->getLocalData()->LAngleLoc;
}

void setLAngleLoc(SourceLocation Loc) {
this->getLocalData()->LAngleLoc = Loc;
}

SourceLocation getRAngleLoc() const {
return this->getLocalData()->RAngleLoc;
}

void setRAngleLoc(SourceLocation Loc) {
this->getLocalData()->RAngleLoc = Loc;
}

unsigned getNumArgs() const {
return getTypePtr()->getNumArgs();
}

void setArgLocInfo(unsigned i, TemplateArgumentLocInfo AI) {
getArgInfos()[i] = AI;
}

TemplateArgumentLocInfo getArgLocInfo(unsigned i) const {
return getArgInfos()[i];
}

TemplateArgumentLoc getArgLoc(unsigned i) const {
return TemplateArgumentLoc(getTypePtr()->getTypeConstraintArguments()[i],
getArgLocInfo(i));
}

SourceRange getLocalSourceRange() const {
return{
isConstrained()
? (getNestedNameSpecifierLoc()
? getNestedNameSpecifierLoc().getBeginLoc()
: (getTemplateKWLoc().isValid()
? getTemplateKWLoc()
: getConceptNameLoc()))
: getNameLoc(),
getNameLoc()
};
}

void copy(AutoTypeLoc Loc) {
unsigned size = getFullDataSize();
assert(size == Loc.getFullDataSize());
memcpy(Data, Loc.Data, size);
}

void initializeLocal(ASTContext &Context, SourceLocation Loc);

unsigned getExtraLocalDataSize() const {
return getNumArgs() * sizeof(TemplateArgumentLocInfo);
}

unsigned getExtraLocalDataAlignment() const {
return alignof(TemplateArgumentLocInfo);
}

private:
TemplateArgumentLocInfo *getArgInfos() const {
return static_cast<TemplateArgumentLocInfo*>(getExtraLocalData());
}
};

class DeducedTemplateSpecializationTypeLoc
Expand Down
11 changes: 10 additions & 1 deletion clang/include/clang/AST/TypeProperties.td
Original file line number Diff line number Diff line change
Expand Up @@ -395,6 +395,13 @@ let Class = AutoType in {
def : Property<"keyword", AutoTypeKeyword> {
let Read = [{ node->getKeyword() }];
}
def : Property<"typeConstraintConcept", Optional<ConceptDeclRef>> {
let Read = [{ makeOptionalFromPointer(
const_cast<const ConceptDecl*>(node->getTypeConstraintConcept())) }];
}
def : Property<"typeConstraintArguments", Array<TemplateArgument>> {
let Read = [{ node->getTypeConstraintArguments() }];
}
// FIXME: better enumerated value
// Only really required when the deduced type is null
def : Property<"dependence", UInt32> {
Expand All @@ -406,7 +413,9 @@ let Class = AutoType in {
def : Creator<[{
return ctx.getAutoType(makeNullableFromOptional(deducedType), keyword,
/*isDependentWithoutDeducedType*/ dependence > 0,
/*isPackWithoutDeducedType*/ dependence > 1);
/*isPackWithoutDeducedType*/ dependence > 1,
makePointerFromOptional(typeConstraintConcept),
typeConstraintArguments);
}]>;
}

Expand Down
2 changes: 0 additions & 2 deletions clang/include/clang/Basic/AttrDocs.td
Original file line number Diff line number Diff line change
Expand Up @@ -3995,8 +3995,6 @@ def PatchableFunctionEntryDocs : Documentation {
before the function entry and N-M NOPs after the function entry. This attribute
takes precedence over the command line option ``-fpatchable-function-entry=N,M``.
``M`` defaults to 0 if omitted.

Currently, only M=0 is supported.
}];
}

Expand Down
3 changes: 3 additions & 0 deletions clang/include/clang/Basic/Builtins.def
Original file line number Diff line number Diff line change
Expand Up @@ -788,6 +788,9 @@ BUILTIN(__builtin_abort, "v", "Fnr")
BUILTIN(__builtin_index, "c*cC*i", "Fn")
BUILTIN(__builtin_rindex, "c*cC*i", "Fn")

// ignored glibc builtin, see https://sourceware.org/bugzilla/show_bug.cgi?id=25399
BUILTIN(__warn_memset_zero_len, "v", "nU")

// Microsoft builtins. These are only active with -fms-extensions.
LANGBUILTIN(_alloca, "v*z", "n", ALL_MS_LANGUAGES)
LANGBUILTIN(__annotation, "wC*.","n", ALL_MS_LANGUAGES)
Expand Down
1 change: 1 addition & 0 deletions clang/include/clang/Basic/CodeGenOptions.def
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ CODEGENOPT(XRayAlwaysEmitTypedEvents , 1, 0)
VALUE_CODEGENOPT(XRayInstructionThreshold , 32, 200)

VALUE_CODEGENOPT(PatchableFunctionEntryCount , 32, 0) ///< Number of NOPs at function entry
VALUE_CODEGENOPT(PatchableFunctionEntryOffset , 32, 0)

CODEGENOPT(InstrumentForProfiling , 1, 0) ///< Set when -pg is enabled.
CODEGENOPT(CallFEntry , 1, 0) ///< Set when -mfentry is enabled.
Expand Down
3 changes: 2 additions & 1 deletion clang/include/clang/Basic/Cuda.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

namespace llvm {
class StringRef;
class Twine;
class VersionTuple;
} // namespace llvm

Expand All @@ -30,7 +31,7 @@ enum class CudaVersion {
};
const char *CudaVersionToString(CudaVersion V);
// Input is "Major.Minor"
CudaVersion CudaStringToVersion(llvm::StringRef S);
CudaVersion CudaStringToVersion(const llvm::Twine &S);

enum class CudaArch {
UNKNOWN,
Expand Down
1 change: 1 addition & 0 deletions clang/include/clang/Basic/DeclNodes.td
Original file line number Diff line number Diff line change
Expand Up @@ -100,5 +100,6 @@ def OMPThreadPrivate : DeclNode<Decl>;
def OMPAllocate : DeclNode<Decl>;
def OMPRequires : DeclNode<Decl>;
def Empty : DeclNode<Decl>;
def RequiresExprBody : DeclNode<Decl>, DeclContext;
def LifetimeExtendedTemporary : DeclNode<Decl>;

Loading