From 8f4d399c99a577013e0f1f03dbbaca710e906f59 Mon Sep 17 00:00:00 2001 From: Alex Lorenz Date: Mon, 13 Feb 2017 23:19:40 +0000 Subject: [PATCH] [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 --- clang/include/clang/Parse/Parser.h | 8 +- clang/include/clang/Sema/Sema.h | 2 + clang/lib/Parse/ParseDecl.cpp | 18 ++-- clang/lib/Parse/ParseDeclCXX.cpp | 6 +- clang/lib/Sema/SemaCodeComplete.cpp | 89 ++++++++++++++++--- clang/test/CodeCompletion/keywords.cpp | 79 ++++++++++++++++ .../CodeCompletion/ordinary-name-cxx11.cpp | 9 ++ 7 files changed, 188 insertions(+), 23 deletions(-) create mode 100644 clang/test/CodeCompletion/keywords.cpp diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h index f39c176b58a6..bf032992dfeb 100644 --- a/clang/include/clang/Parse/Parser.h +++ b/clang/include/clang/Parse/Parser.h @@ -2385,10 +2385,10 @@ private: AR_DeclspecAttributesParsed }; - void ParseTypeQualifierListOpt(DeclSpec &DS, - unsigned AttrReqs = AR_AllAttributesParsed, - bool AtomicAllowed = true, - bool IdentifierRequired = false); + void ParseTypeQualifierListOpt( + DeclSpec &DS, unsigned AttrReqs = AR_AllAttributesParsed, + bool AtomicAllowed = true, bool IdentifierRequired = false, + Optional> CodeCompletionHandler = None); void ParseDirectDeclarator(Declarator &D); void ParseDecompositionDeclarator(Declarator &D); void ParseParenDeclarator(Declarator &D); diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 2fda45f55e4a..8ee53b209f15 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -9820,6 +9820,8 @@ public: void CodeCompletePostfixExpression(Scope *S, ExprResult LHS); void CodeCompleteTag(Scope *S, unsigned TagSpec); void CodeCompleteTypeQualifiers(DeclSpec &DS); + void CodeCompleteFunctionQualifiers(DeclSpec &DS, Declarator &D, + const VirtSpecifiers *VS = nullptr); void CodeCompleteBracketDeclarator(Scope *S); void CodeCompleteCase(Scope *S); void CodeCompleteCall(Scope *S, Expr *Fn, ArrayRef Args); diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp index d224a77d8424..7ffafe2dab73 100644 --- a/clang/lib/Parse/ParseDecl.cpp +++ b/clang/lib/Parse/ParseDecl.cpp @@ -4802,9 +4802,10 @@ bool Parser::isConstructorDeclarator(bool IsUnqualified, bool DeductionGuide) { /// [ only if AttReqs & AR_CXX11AttributesParsed ] /// Note: vendor can be GNU, MS, etc and can be explicitly controlled via /// AttrRequirements bitmask values. -void Parser::ParseTypeQualifierListOpt(DeclSpec &DS, unsigned AttrReqs, - bool AtomicAllowed, - bool IdentifierRequired) { +void Parser::ParseTypeQualifierListOpt( + DeclSpec &DS, unsigned AttrReqs, bool AtomicAllowed, + bool IdentifierRequired, + Optional> CodeCompletionHandler) { if (getLangOpts().CPlusPlus11 && (AttrReqs & AR_CXX11AttributesParsed) && isCXX11AttributeSpecifier()) { ParsedAttributesWithRange attrs(AttrFactory); @@ -4822,7 +4823,10 @@ void Parser::ParseTypeQualifierListOpt(DeclSpec &DS, unsigned AttrReqs, switch (Tok.getKind()) { case tok::code_completion: - Actions.CodeCompleteTypeQualifiers(DS); + if (CodeCompletionHandler) + (*CodeCompletionHandler)(); + else + Actions.CodeCompleteTypeQualifiers(DS); return cutOffParsing(); case tok::kw_const: @@ -5748,7 +5752,11 @@ void Parser::ParseFunctionDeclarator(Declarator &D, // Parse cv-qualifier-seq[opt]. ParseTypeQualifierListOpt(DS, AR_NoAttributesParsed, - /*AtomicAllowed*/ false); + /*AtomicAllowed*/ false, + /*IdentifierRequired=*/false, + llvm::function_ref([&]() { + Actions.CodeCompleteFunctionQualifiers(DS, D); + })); if (!DS.getSourceRange().getEnd().isInvalid()) { EndLoc = DS.getSourceRange().getEnd(); ConstQualifierLoc = DS.getConstSpecLoc(); diff --git a/clang/lib/Parse/ParseDeclCXX.cpp b/clang/lib/Parse/ParseDeclCXX.cpp index 8d89f4093749..c3ede1b7f527 100644 --- a/clang/lib/Parse/ParseDeclCXX.cpp +++ b/clang/lib/Parse/ParseDeclCXX.cpp @@ -2285,7 +2285,11 @@ void Parser::MaybeParseAndDiagnoseDeclSpecAfterCXX11VirtSpecifierSeq( // GNU-style and C++11 attributes are not allowed here, but they will be // handled by the caller. Diagnose everything else. - ParseTypeQualifierListOpt(DS, AR_NoAttributesParsed, false); + ParseTypeQualifierListOpt( + DS, AR_NoAttributesParsed, false, + /*IdentifierRequired=*/false, llvm::function_ref([&]() { + Actions.CodeCompleteFunctionQualifiers(DS, D, &VS); + })); D.ExtendWithDeclSpec(DS); if (D.isFunctionDeclarator()) { diff --git a/clang/lib/Sema/SemaCodeComplete.cpp b/clang/lib/Sema/SemaCodeComplete.cpp index 5f65df2ef375..0056b1138d8e 100644 --- a/clang/lib/Sema/SemaCodeComplete.cpp +++ b/clang/lib/Sema/SemaCodeComplete.cpp @@ -1370,6 +1370,21 @@ static void AddStorageSpecifiers(Sema::ParserCompletionContext CCC, // in C++0x as a type specifier. Results.AddResult(Result("extern")); 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, @@ -1527,6 +1542,21 @@ static void addThisCompletion(Sema &S, ResultBuilder &Results) { 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. static void AddOrdinaryNameResults(Sema::ParserCompletionContext CCC, Scope *S, @@ -1611,6 +1641,8 @@ static void AddOrdinaryNameResults(Sema::ParserCompletionContext CCC, Results.AddResult(Result(Builder.TakeString())); } + AddStaticAssertResult(Builder, Results, SemaRef.getLangOpts()); + if (CCC == Sema::PCC_Class) { AddTypedefResult(Results); @@ -1824,6 +1856,8 @@ static void AddOrdinaryNameResults(Sema::ParserCompletionContext CCC, Builder.AddChunk(CodeCompletionString::CK_HorizontalSpace); Builder.AddPlaceholderChunk("identifier"); Results.AddResult(Result(Builder.TakeString())); + + AddStaticAssertResult(Builder, Results, SemaRef.getLangOpts()); } // Fall through (for statement expressions). @@ -3483,6 +3517,11 @@ void Sema::CodeCompleteDeclSpec(Scope *S, DeclSpec &DS, Results.AddResult(Result("restrict")); if (getLangOpts().CPlusPlus) { + if (getLangOpts().CPlusPlus11 && + (DS.getTypeSpecType() == DeclSpec::TST_class || + DS.getTypeSpecType() == DeclSpec::TST_struct)) + Results.AddResult("final"); + if (AllowNonIdentifiers) { Results.AddResult(Result("operator")); } @@ -4013,30 +4052,54 @@ void Sema::CodeCompleteTag(Scope *S, unsigned TagSpec) { 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) { ResultBuilder Results(*this, CodeCompleter->getAllocator(), CodeCompleter->getCodeCompletionTUInfo(), CodeCompletionContext::CCC_TypeQualifiers); Results.EnterNewScope(); - if (!(DS.getTypeQualifiers() & DeclSpec::TQ_const)) - 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"); + AddTypeQualifierResults(DS, Results, LangOpts); Results.ExitScope(); HandleCodeCompleteResults(this, CodeCompleter, Results.getCompletionContext(), 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) { CodeCompleteExpression(S, QualType(getASTContext().getSizeType())); } diff --git a/clang/test/CodeCompletion/keywords.cpp b/clang/test/CodeCompletion/keywords.cpp new file mode 100644 index 000000000000..6e5824c2f183 --- /dev/null +++ b/clang/test/CodeCompletion/keywords.cpp @@ -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 diff --git a/clang/test/CodeCompletion/ordinary-name-cxx11.cpp b/clang/test/CodeCompletion/ordinary-name-cxx11.cpp index 8e6f38322384..34c3bf96a9d5 100644 --- a/clang/test/CodeCompletion/ordinary-name-cxx11.cpp +++ b/clang/test/CodeCompletion/ordinary-name-cxx11.cpp @@ -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...(<#parameter-pack#>) // 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: struct // CHECK-CC1-NEXT: COMPLETION: Pattern : switch(<#condition#>){ // CHECK-CC1: COMPLETION: t : t + // CHECK-CC1-NEXT: COMPLETION: thread_local // CHECK-CC1-NEXT: COMPLETION: Pattern : [#void#]throw <#expression#> // CHECK-CC1-NEXT: COMPLETION: Pattern : [#bool#]true // CHECK-CC1-NEXT: COMPLETION: Pattern : try{<#statements#> @@ -72,6 +74,7 @@ void foo() { // CHECK-CC2-NEXT: COMPLETION: char32 // CHECK-CC2-NEXT: COMPLETION: class // CHECK-CC2-NEXT: COMPLETION: const + // CHECK-CC2-NEXT: COMPLETION: constexpr // CHECK-CC2-NEXT: COMPLETION: Pattern : decltype(<#expression#>) // CHECK-CC2-NEXT: COMPLETION: double // CHECK-CC2-NEXT: COMPLETION: enum @@ -86,10 +89,12 @@ void foo() { // CHECK-CC2-NEXT: COMPLETION: short // CHECK-CC2-NEXT: COMPLETION: signed // CHECK-CC2-NEXT: COMPLETION: static + // CHECK-CC2-NEXT: COMPLETION: Pattern : static_assert(<#expression#>, <#message#>) // CHECK-CC2-NEXT: COMPLETION: struct // CHECK-CC2-NEXT: COMPLETION: t : t // CHECK-CC2-NEXT: COMPLETION: Pattern : template <#declaration#> // CHECK-CC2-NEXT: COMPLETION: Pattern : template<<#parameters#>> + // CHECK-CC2-NEXT: COMPLETION: thread_local // CHECK-CC2-NEXT: COMPLETION: TYPEDEF : TYPEDEF // CHECK-CC2-NEXT: COMPLETION: Pattern : typedef <#type#> <#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: class // CHECK-CC3-NEXT: COMPLETION: const + // CHECK-CC3-NEXT: COMPLETION: constexpr // CHECK-CC3-NEXT: COMPLETION: Pattern : decltype(<#expression#>) // CHECK-CC3-NEXT: COMPLETION: double // CHECK-CC3-NEXT: COMPLETION: enum @@ -129,8 +135,10 @@ void foo() { // CHECK-CC3-NEXT: COMPLETION: short // CHECK-CC3-NEXT: COMPLETION: signed // CHECK-CC3-NEXT: COMPLETION: static + // CHECK-CC3-NEXT: COMPLETION: Pattern : static_assert(<#expression#>, <#message#>) // CHECK-CC3-NEXT: COMPLETION: struct // 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 : typename <#qualifier#>::<#name#> // 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...(<#parameter-pack#>) // 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: struct // CHECK-NO-RTTI-NEXT: COMPLETION: Pattern : switch(<#condition#>){