[CodeCompletion] Code complete the missing C++11 keywords

This commit adds context sensitive code completion support for the C++11
keywords that currently don't have completion results.

The following keywords are supported by this patch:

alignas
constexpr
static_assert
noexcept (as a function/method qualifier)
thread_local

The following special identifiers are also supported:

final (as a method qualifier or class qualifier)
override

rdar://29219185

Differential Revision: https://reviews.llvm.org/D28286

llvm-svn: 295001
This commit is contained in:
Alex Lorenz 2017-02-13 23:19:40 +00:00
parent 5be2e8415c
commit 8f4d399c99
7 changed files with 188 additions and 23 deletions

View File

@ -2385,10 +2385,10 @@ private:
AR_DeclspecAttributesParsed AR_DeclspecAttributesParsed
}; };
void ParseTypeQualifierListOpt(DeclSpec &DS, void ParseTypeQualifierListOpt(
unsigned AttrReqs = AR_AllAttributesParsed, DeclSpec &DS, unsigned AttrReqs = AR_AllAttributesParsed,
bool AtomicAllowed = true, bool AtomicAllowed = true, bool IdentifierRequired = false,
bool IdentifierRequired = false); Optional<llvm::function_ref<void()>> CodeCompletionHandler = None);
void ParseDirectDeclarator(Declarator &D); void ParseDirectDeclarator(Declarator &D);
void ParseDecompositionDeclarator(Declarator &D); void ParseDecompositionDeclarator(Declarator &D);
void ParseParenDeclarator(Declarator &D); void ParseParenDeclarator(Declarator &D);

View File

@ -9820,6 +9820,8 @@ public:
void CodeCompletePostfixExpression(Scope *S, ExprResult LHS); void CodeCompletePostfixExpression(Scope *S, ExprResult LHS);
void CodeCompleteTag(Scope *S, unsigned TagSpec); void CodeCompleteTag(Scope *S, unsigned TagSpec);
void CodeCompleteTypeQualifiers(DeclSpec &DS); void CodeCompleteTypeQualifiers(DeclSpec &DS);
void CodeCompleteFunctionQualifiers(DeclSpec &DS, Declarator &D,
const VirtSpecifiers *VS = nullptr);
void CodeCompleteBracketDeclarator(Scope *S); void CodeCompleteBracketDeclarator(Scope *S);
void CodeCompleteCase(Scope *S); void CodeCompleteCase(Scope *S);
void CodeCompleteCall(Scope *S, Expr *Fn, ArrayRef<Expr *> Args); void CodeCompleteCall(Scope *S, Expr *Fn, ArrayRef<Expr *> Args);

View File

@ -4802,9 +4802,10 @@ bool Parser::isConstructorDeclarator(bool IsUnqualified, bool DeductionGuide) {
/// [ only if AttReqs & AR_CXX11AttributesParsed ] /// [ only if AttReqs & AR_CXX11AttributesParsed ]
/// Note: vendor can be GNU, MS, etc and can be explicitly controlled via /// Note: vendor can be GNU, MS, etc and can be explicitly controlled via
/// AttrRequirements bitmask values. /// AttrRequirements bitmask values.
void Parser::ParseTypeQualifierListOpt(DeclSpec &DS, unsigned AttrReqs, void Parser::ParseTypeQualifierListOpt(
bool AtomicAllowed, DeclSpec &DS, unsigned AttrReqs, bool AtomicAllowed,
bool IdentifierRequired) { bool IdentifierRequired,
Optional<llvm::function_ref<void()>> CodeCompletionHandler) {
if (getLangOpts().CPlusPlus11 && (AttrReqs & AR_CXX11AttributesParsed) && if (getLangOpts().CPlusPlus11 && (AttrReqs & AR_CXX11AttributesParsed) &&
isCXX11AttributeSpecifier()) { isCXX11AttributeSpecifier()) {
ParsedAttributesWithRange attrs(AttrFactory); ParsedAttributesWithRange attrs(AttrFactory);
@ -4822,7 +4823,10 @@ void Parser::ParseTypeQualifierListOpt(DeclSpec &DS, unsigned AttrReqs,
switch (Tok.getKind()) { switch (Tok.getKind()) {
case tok::code_completion: case tok::code_completion:
Actions.CodeCompleteTypeQualifiers(DS); if (CodeCompletionHandler)
(*CodeCompletionHandler)();
else
Actions.CodeCompleteTypeQualifiers(DS);
return cutOffParsing(); return cutOffParsing();
case tok::kw_const: case tok::kw_const:
@ -5748,7 +5752,11 @@ void Parser::ParseFunctionDeclarator(Declarator &D,
// Parse cv-qualifier-seq[opt]. // Parse cv-qualifier-seq[opt].
ParseTypeQualifierListOpt(DS, AR_NoAttributesParsed, ParseTypeQualifierListOpt(DS, AR_NoAttributesParsed,
/*AtomicAllowed*/ false); /*AtomicAllowed*/ false,
/*IdentifierRequired=*/false,
llvm::function_ref<void()>([&]() {
Actions.CodeCompleteFunctionQualifiers(DS, D);
}));
if (!DS.getSourceRange().getEnd().isInvalid()) { if (!DS.getSourceRange().getEnd().isInvalid()) {
EndLoc = DS.getSourceRange().getEnd(); EndLoc = DS.getSourceRange().getEnd();
ConstQualifierLoc = DS.getConstSpecLoc(); ConstQualifierLoc = DS.getConstSpecLoc();

View File

@ -2285,7 +2285,11 @@ void Parser::MaybeParseAndDiagnoseDeclSpecAfterCXX11VirtSpecifierSeq(
// GNU-style and C++11 attributes are not allowed here, but they will be // GNU-style and C++11 attributes are not allowed here, but they will be
// handled by the caller. Diagnose everything else. // handled by the caller. Diagnose everything else.
ParseTypeQualifierListOpt(DS, AR_NoAttributesParsed, false); ParseTypeQualifierListOpt(
DS, AR_NoAttributesParsed, false,
/*IdentifierRequired=*/false, llvm::function_ref<void()>([&]() {
Actions.CodeCompleteFunctionQualifiers(DS, D, &VS);
}));
D.ExtendWithDeclSpec(DS); D.ExtendWithDeclSpec(DS);
if (D.isFunctionDeclarator()) { if (D.isFunctionDeclarator()) {

View File

@ -1370,6 +1370,21 @@ static void AddStorageSpecifiers(Sema::ParserCompletionContext CCC,
// in C++0x as a type specifier. // in C++0x as a type specifier.
Results.AddResult(Result("extern")); Results.AddResult(Result("extern"));
Results.AddResult(Result("static")); Results.AddResult(Result("static"));
if (LangOpts.CPlusPlus11) {
CodeCompletionAllocator &Allocator = Results.getAllocator();
CodeCompletionBuilder Builder(Allocator, Results.getCodeCompletionTUInfo());
// alignas
Builder.AddTypedTextChunk("alignas");
Builder.AddChunk(CodeCompletionString::CK_LeftParen);
Builder.AddPlaceholderChunk("expression");
Builder.AddChunk(CodeCompletionString::CK_RightParen);
Results.AddResult(Result(Builder.TakeString()));
Results.AddResult(Result("constexpr"));
Results.AddResult(Result("thread_local"));
}
} }
static void AddFunctionSpecifiers(Sema::ParserCompletionContext CCC, static void AddFunctionSpecifiers(Sema::ParserCompletionContext CCC,
@ -1527,6 +1542,21 @@ static void addThisCompletion(Sema &S, ResultBuilder &Results) {
Results.AddResult(CodeCompletionResult(Builder.TakeString())); Results.AddResult(CodeCompletionResult(Builder.TakeString()));
} }
static void AddStaticAssertResult(CodeCompletionBuilder &Builder,
ResultBuilder &Results,
const LangOptions &LangOpts) {
if (!LangOpts.CPlusPlus11)
return;
Builder.AddTypedTextChunk("static_assert");
Builder.AddChunk(CodeCompletionString::CK_LeftParen);
Builder.AddPlaceholderChunk("expression");
Builder.AddChunk(CodeCompletionString::CK_Comma);
Builder.AddPlaceholderChunk("message");
Builder.AddChunk(CodeCompletionString::CK_RightParen);
Results.AddResult(CodeCompletionResult(Builder.TakeString()));
}
/// \brief Add language constructs that show up for "ordinary" names. /// \brief Add language constructs that show up for "ordinary" names.
static void AddOrdinaryNameResults(Sema::ParserCompletionContext CCC, static void AddOrdinaryNameResults(Sema::ParserCompletionContext CCC,
Scope *S, Scope *S,
@ -1611,6 +1641,8 @@ static void AddOrdinaryNameResults(Sema::ParserCompletionContext CCC,
Results.AddResult(Result(Builder.TakeString())); Results.AddResult(Result(Builder.TakeString()));
} }
AddStaticAssertResult(Builder, Results, SemaRef.getLangOpts());
if (CCC == Sema::PCC_Class) { if (CCC == Sema::PCC_Class) {
AddTypedefResult(Results); AddTypedefResult(Results);
@ -1824,6 +1856,8 @@ static void AddOrdinaryNameResults(Sema::ParserCompletionContext CCC,
Builder.AddChunk(CodeCompletionString::CK_HorizontalSpace); Builder.AddChunk(CodeCompletionString::CK_HorizontalSpace);
Builder.AddPlaceholderChunk("identifier"); Builder.AddPlaceholderChunk("identifier");
Results.AddResult(Result(Builder.TakeString())); Results.AddResult(Result(Builder.TakeString()));
AddStaticAssertResult(Builder, Results, SemaRef.getLangOpts());
} }
// Fall through (for statement expressions). // Fall through (for statement expressions).
@ -3483,6 +3517,11 @@ void Sema::CodeCompleteDeclSpec(Scope *S, DeclSpec &DS,
Results.AddResult(Result("restrict")); Results.AddResult(Result("restrict"));
if (getLangOpts().CPlusPlus) { if (getLangOpts().CPlusPlus) {
if (getLangOpts().CPlusPlus11 &&
(DS.getTypeSpecType() == DeclSpec::TST_class ||
DS.getTypeSpecType() == DeclSpec::TST_struct))
Results.AddResult("final");
if (AllowNonIdentifiers) { if (AllowNonIdentifiers) {
Results.AddResult(Result("operator")); Results.AddResult(Result("operator"));
} }
@ -4013,30 +4052,54 @@ void Sema::CodeCompleteTag(Scope *S, unsigned TagSpec) {
Results.data(),Results.size()); Results.data(),Results.size());
} }
static void AddTypeQualifierResults(DeclSpec &DS, ResultBuilder &Results,
const LangOptions &LangOpts) {
if (!(DS.getTypeQualifiers() & DeclSpec::TQ_const))
Results.AddResult("const");
if (!(DS.getTypeQualifiers() & DeclSpec::TQ_volatile))
Results.AddResult("volatile");
if (LangOpts.C99 && !(DS.getTypeQualifiers() & DeclSpec::TQ_restrict))
Results.AddResult("restrict");
if (LangOpts.C11 && !(DS.getTypeQualifiers() & DeclSpec::TQ_atomic))
Results.AddResult("_Atomic");
if (LangOpts.MSVCCompat && !(DS.getTypeQualifiers() & DeclSpec::TQ_unaligned))
Results.AddResult("__unaligned");
}
void Sema::CodeCompleteTypeQualifiers(DeclSpec &DS) { void Sema::CodeCompleteTypeQualifiers(DeclSpec &DS) {
ResultBuilder Results(*this, CodeCompleter->getAllocator(), ResultBuilder Results(*this, CodeCompleter->getAllocator(),
CodeCompleter->getCodeCompletionTUInfo(), CodeCompleter->getCodeCompletionTUInfo(),
CodeCompletionContext::CCC_TypeQualifiers); CodeCompletionContext::CCC_TypeQualifiers);
Results.EnterNewScope(); Results.EnterNewScope();
if (!(DS.getTypeQualifiers() & DeclSpec::TQ_const)) AddTypeQualifierResults(DS, Results, LangOpts);
Results.AddResult("const");
if (!(DS.getTypeQualifiers() & DeclSpec::TQ_volatile))
Results.AddResult("volatile");
if (getLangOpts().C99 &&
!(DS.getTypeQualifiers() & DeclSpec::TQ_restrict))
Results.AddResult("restrict");
if (getLangOpts().C11 &&
!(DS.getTypeQualifiers() & DeclSpec::TQ_atomic))
Results.AddResult("_Atomic");
if (getLangOpts().MSVCCompat &&
!(DS.getTypeQualifiers() & DeclSpec::TQ_unaligned))
Results.AddResult("__unaligned");
Results.ExitScope(); Results.ExitScope();
HandleCodeCompleteResults(this, CodeCompleter, HandleCodeCompleteResults(this, CodeCompleter,
Results.getCompletionContext(), Results.getCompletionContext(),
Results.data(), Results.size()); Results.data(), Results.size());
} }
void Sema::CodeCompleteFunctionQualifiers(DeclSpec &DS, Declarator &D,
const VirtSpecifiers *VS) {
ResultBuilder Results(*this, CodeCompleter->getAllocator(),
CodeCompleter->getCodeCompletionTUInfo(),
CodeCompletionContext::CCC_TypeQualifiers);
Results.EnterNewScope();
AddTypeQualifierResults(DS, Results, LangOpts);
if (LangOpts.CPlusPlus11) {
Results.AddResult("noexcept");
if (D.getContext() == Declarator::MemberContext && !D.isCtorOrDtor() &&
!D.isStaticMember()) {
if (!VS || !VS->isFinalSpecified())
Results.AddResult("final");
if (!VS || !VS->isOverrideSpecified())
Results.AddResult("override");
}
}
Results.ExitScope();
HandleCodeCompleteResults(this, CodeCompleter, Results.getCompletionContext(),
Results.data(), Results.size());
}
void Sema::CodeCompleteBracketDeclarator(Scope *S) { void Sema::CodeCompleteBracketDeclarator(Scope *S) {
CodeCompleteExpression(S, QualType(getASTContext().getSizeType())); CodeCompleteExpression(S, QualType(getASTContext().getSizeType()));
} }

View File

@ -0,0 +1,79 @@
int function(int x) {
return x + 1;
}
int variable = 0;
class Class {
public:
Class() { }
int method(int x) {
return x + 1;
}
virtual void virtualMethod() {
}
static void staticMethod() {
}
static int staticVar;
};
class SubClass : public Class {
void virtualMethod() override final {
}
};
struct Struct {
};
// RUN: %clang_cc1 -std=c++11 -code-completion-at=%s:1:1 %s | FileCheck --check-prefix=CHECK-TOP-LEVEL %s
// RUN: %clang_cc1 -std=c++11 -code-completion-at=%s:5:1 %s | FileCheck --check-prefix=CHECK-TOP-LEVEL %s
// RUN: %clang_cc1 -std=c++11 -code-completion-at=%s:11:1 %s | FileCheck --check-prefix=CHECK-TOP-LEVEL %s
// CHECK-TOP-LEVEL: alignas(<#expression#>)
// CHECK-TOP-LEVEL: constexpr
// CHECK-TOP-LEVEL: static_assert(<#expression#>, <#message#>)
// CHECK-TOP-LEVEL: thread_local
// CHECK-TOP-LEVEL-NOT: final
// CHECK-TOP-LEVEL-NOT: noexcept
// RUN: %clang_cc1 -std=c++11 -code-completion-at=%s:1:14 %s | FileCheck --check-prefix=CHECK-PARAM %s
// CHECK-PARAM-NOT: alignas
// CHECK-PARAM-NOT: constexpr
// CHECK-PARAM-NOT: final
// CHECK-PARAM-NOT: thread_local
// RUN: %clang_cc1 -std=c++11 -code-completion-at=%s:21:10 %s | FileCheck --check-prefix=CHECK-STATICVAR1 %s
// CHECK-STATICVAR1: constexpr
// CHECK-STATICVAR1: thread_local
// RUN: %clang_cc1 -std=c++11 -code-completion-at=%s:7:13 %s | FileCheck --check-prefix=CHECK-CLASS-QUALIFIER %s
// RUN: %clang_cc1 -std=c++11 -code-completion-at=%s:24:16 %s | FileCheck --check-prefix=CHECK-CLASS-QUALIFIER %s
// RUN: %clang_cc1 -std=c++11 -code-completion-at=%s:29:15 %s | FileCheck --check-prefix=CHECK-CLASS-QUALIFIER %s
// CHECK-CLASS-QUALIFIER: final
// RUN: %clang_cc1 -std=c++11 -code-completion-at=%s:1:21 %s | FileCheck --check-prefix=CHECK-FUNCTION-QUALIFIER %s
// RUN: %clang_cc1 -std=c++11 -code-completion-at=%s:9:11 %s | FileCheck --check-prefix=CHECK-FUNCTION-QUALIFIER %s
// RUN: %clang_cc1 -std=c++11 -code-completion-at=%s:18:30 %s | FileCheck --check-prefix=CHECK-FUNCTION-QUALIFIER %s
// CHECK-FUNCTION-QUALIFIER: noexcept
// CHECK-FUNCTION-QUALIFIER-NOT: final
// CHECK-FUNCTION-QUALIFIER-NOT: override
// RUN: %clang_cc1 -std=c++11 -code-completion-at=%s:11:21 %s | FileCheck --check-prefix=CHECK-METHOD-QUALIFIER %s
// RUN: %clang_cc1 -std=c++11 -code-completion-at=%s:15:32 %s | FileCheck --check-prefix=CHECK-METHOD-QUALIFIER %s
// RUN: %clang_cc1 -std=c++11 -code-completion-at=%s:25:24 %s | FileCheck --check-prefix=CHECK-METHOD-QUALIFIER %s
// CHECK-METHOD-QUALIFIER: final
// CHECK-METHOD-QUALIFIER: noexcept
// CHECK-METHOD-QUALIFIER: override
// RUN: %clang_cc1 -std=c++11 -code-completion-at=%s:25:33 %s | FileCheck --check-prefix=CHECK-OVERRIDE-SPECIFIED %s
// CHECK-OVERRIDE-SPECIFIED: final
// CHECK-OVERRIDE-SPECIFIED: noexcept
// CHECK-OVERRIDE-SPECIFIED-NOT: override
// RUN: %clang_cc1 -std=c++11 -code-completion-at=%s:25:39 %s | FileCheck --check-prefix=CHECK-OVERRIDE-FINAL-SPECIFIED %s
// CHECK-OVERRIDE-FINAL-SPECIFIED: noexcept
// CHECK-OVERRIDE-FINAL-SPECIFIED-NOT: final
// CHECK-OVERRIDE-FINAL-SPECIFIED-NOT: override

View File

@ -39,10 +39,12 @@ void foo() {
// CHECK-CC1-NEXT: COMPLETION: Pattern : [#size_t#]sizeof(<#expression-or-type#>) // CHECK-CC1-NEXT: COMPLETION: Pattern : [#size_t#]sizeof(<#expression-or-type#>)
// CHECK-CC1-NEXT: COMPLETION: Pattern : [#size_t#]sizeof...(<#parameter-pack#>) // CHECK-CC1-NEXT: COMPLETION: Pattern : [#size_t#]sizeof...(<#parameter-pack#>)
// CHECK-CC1-NEXT: COMPLETION: static // CHECK-CC1-NEXT: COMPLETION: static
// CHECK-CC1-NEXT: COMPLETION: Pattern : static_assert(<#expression#>, <#message#>)
// CHECK-CC1-NEXT: COMPLETION: Pattern : static_cast<<#type#>>(<#expression#>) // CHECK-CC1-NEXT: COMPLETION: Pattern : static_cast<<#type#>>(<#expression#>)
// CHECK-CC1-NEXT: COMPLETION: struct // CHECK-CC1-NEXT: COMPLETION: struct
// CHECK-CC1-NEXT: COMPLETION: Pattern : switch(<#condition#>){ // CHECK-CC1-NEXT: COMPLETION: Pattern : switch(<#condition#>){
// CHECK-CC1: COMPLETION: t : t // CHECK-CC1: COMPLETION: t : t
// CHECK-CC1-NEXT: COMPLETION: thread_local
// CHECK-CC1-NEXT: COMPLETION: Pattern : [#void#]throw <#expression#> // CHECK-CC1-NEXT: COMPLETION: Pattern : [#void#]throw <#expression#>
// CHECK-CC1-NEXT: COMPLETION: Pattern : [#bool#]true // CHECK-CC1-NEXT: COMPLETION: Pattern : [#bool#]true
// CHECK-CC1-NEXT: COMPLETION: Pattern : try{<#statements#> // CHECK-CC1-NEXT: COMPLETION: Pattern : try{<#statements#>
@ -72,6 +74,7 @@ void foo() {
// CHECK-CC2-NEXT: COMPLETION: char32 // CHECK-CC2-NEXT: COMPLETION: char32
// CHECK-CC2-NEXT: COMPLETION: class // CHECK-CC2-NEXT: COMPLETION: class
// CHECK-CC2-NEXT: COMPLETION: const // CHECK-CC2-NEXT: COMPLETION: const
// CHECK-CC2-NEXT: COMPLETION: constexpr
// CHECK-CC2-NEXT: COMPLETION: Pattern : decltype(<#expression#>) // CHECK-CC2-NEXT: COMPLETION: Pattern : decltype(<#expression#>)
// CHECK-CC2-NEXT: COMPLETION: double // CHECK-CC2-NEXT: COMPLETION: double
// CHECK-CC2-NEXT: COMPLETION: enum // CHECK-CC2-NEXT: COMPLETION: enum
@ -86,10 +89,12 @@ void foo() {
// CHECK-CC2-NEXT: COMPLETION: short // CHECK-CC2-NEXT: COMPLETION: short
// CHECK-CC2-NEXT: COMPLETION: signed // CHECK-CC2-NEXT: COMPLETION: signed
// CHECK-CC2-NEXT: COMPLETION: static // CHECK-CC2-NEXT: COMPLETION: static
// CHECK-CC2-NEXT: COMPLETION: Pattern : static_assert(<#expression#>, <#message#>)
// CHECK-CC2-NEXT: COMPLETION: struct // CHECK-CC2-NEXT: COMPLETION: struct
// CHECK-CC2-NEXT: COMPLETION: t : t // CHECK-CC2-NEXT: COMPLETION: t : t
// CHECK-CC2-NEXT: COMPLETION: Pattern : template <#declaration#> // CHECK-CC2-NEXT: COMPLETION: Pattern : template <#declaration#>
// CHECK-CC2-NEXT: COMPLETION: Pattern : template<<#parameters#>> // CHECK-CC2-NEXT: COMPLETION: Pattern : template<<#parameters#>>
// CHECK-CC2-NEXT: COMPLETION: thread_local
// CHECK-CC2-NEXT: COMPLETION: TYPEDEF : TYPEDEF // CHECK-CC2-NEXT: COMPLETION: TYPEDEF : TYPEDEF
// CHECK-CC2-NEXT: COMPLETION: Pattern : typedef <#type#> <#name#> // CHECK-CC2-NEXT: COMPLETION: Pattern : typedef <#type#> <#name#>
// CHECK-CC2-NEXT: COMPLETION: Pattern : typename <#qualifier#>::<#name#> // CHECK-CC2-NEXT: COMPLETION: Pattern : typename <#qualifier#>::<#name#>
@ -111,6 +116,7 @@ void foo() {
// CHECK-CC3-NEXT: COMPLETION: char32_t // CHECK-CC3-NEXT: COMPLETION: char32_t
// CHECK-CC3-NEXT: COMPLETION: class // CHECK-CC3-NEXT: COMPLETION: class
// CHECK-CC3-NEXT: COMPLETION: const // CHECK-CC3-NEXT: COMPLETION: const
// CHECK-CC3-NEXT: COMPLETION: constexpr
// CHECK-CC3-NEXT: COMPLETION: Pattern : decltype(<#expression#>) // CHECK-CC3-NEXT: COMPLETION: Pattern : decltype(<#expression#>)
// CHECK-CC3-NEXT: COMPLETION: double // CHECK-CC3-NEXT: COMPLETION: double
// CHECK-CC3-NEXT: COMPLETION: enum // CHECK-CC3-NEXT: COMPLETION: enum
@ -129,8 +135,10 @@ void foo() {
// CHECK-CC3-NEXT: COMPLETION: short // CHECK-CC3-NEXT: COMPLETION: short
// CHECK-CC3-NEXT: COMPLETION: signed // CHECK-CC3-NEXT: COMPLETION: signed
// CHECK-CC3-NEXT: COMPLETION: static // CHECK-CC3-NEXT: COMPLETION: static
// CHECK-CC3-NEXT: COMPLETION: Pattern : static_assert(<#expression#>, <#message#>)
// CHECK-CC3-NEXT: COMPLETION: struct // CHECK-CC3-NEXT: COMPLETION: struct
// CHECK-CC3-NEXT: COMPLETION: Pattern : template<<#parameters#>> // CHECK-CC3-NEXT: COMPLETION: Pattern : template<<#parameters#>>
// CHECK-CC3-NEXT: COMPLETION: thread_local
// CHECK-CC3-NEXT: COMPLETION: Pattern : typedef <#type#> <#name#> // CHECK-CC3-NEXT: COMPLETION: Pattern : typedef <#type#> <#name#>
// CHECK-CC3-NEXT: COMPLETION: Pattern : typename <#qualifier#>::<#name#> // CHECK-CC3-NEXT: COMPLETION: Pattern : typename <#qualifier#>::<#name#>
// CHECK-CC3-NEXT: COMPLETION: Pattern : typeof <#expression#> // CHECK-CC3-NEXT: COMPLETION: Pattern : typeof <#expression#>
@ -227,6 +235,7 @@ void foo() {
// CHECK-NO-RTTI-NEXT: COMPLETION: Pattern : [#size_t#]sizeof(<#expression-or-type#>) // CHECK-NO-RTTI-NEXT: COMPLETION: Pattern : [#size_t#]sizeof(<#expression-or-type#>)
// CHECK-NO-RTTI-NEXT: COMPLETION: Pattern : [#size_t#]sizeof...(<#parameter-pack#>) // CHECK-NO-RTTI-NEXT: COMPLETION: Pattern : [#size_t#]sizeof...(<#parameter-pack#>)
// CHECK-NO-RTTI-NEXT: COMPLETION: static // CHECK-NO-RTTI-NEXT: COMPLETION: static
// CHECK-NO-RTTI-NEXT: COMPLETION: Pattern : static_assert(<#expression#>, <#message#>)
// CHECK-NO-RTTI-NEXT: COMPLETION: Pattern : static_cast<<#type#>>(<#expression#>) // CHECK-NO-RTTI-NEXT: COMPLETION: Pattern : static_cast<<#type#>>(<#expression#>)
// CHECK-NO-RTTI-NEXT: COMPLETION: struct // CHECK-NO-RTTI-NEXT: COMPLETION: struct
// CHECK-NO-RTTI-NEXT: COMPLETION: Pattern : switch(<#condition#>){ // CHECK-NO-RTTI-NEXT: COMPLETION: Pattern : switch(<#condition#>){