diff --git a/clang/include/clang/Sema/ParsedAttr.h b/clang/include/clang/Sema/ParsedAttr.h --- a/clang/include/clang/Sema/ParsedAttr.h +++ b/clang/include/clang/Sema/ParsedAttr.h @@ -67,6 +67,8 @@ const char *NormalizedFullName; }; ArrayRef Spellings; + // The names of the known arguments of this attribute. + ArrayRef ArgNames; ParsedAttrInfo(AttributeCommonInfo::Kind AttrKind = AttributeCommonInfo::NoSemaHandlerAttribute) diff --git a/clang/lib/Sema/SemaCodeComplete.cpp b/clang/lib/Sema/SemaCodeComplete.cpp --- a/clang/lib/Sema/SemaCodeComplete.cpp +++ b/clang/lib/Sema/SemaCodeComplete.cpp @@ -4423,33 +4423,59 @@ Scope = ""; } + auto Add = [&](llvm::StringRef Scope, llvm::StringRef Name, + bool Underscores) { + CodeCompletionBuilder Builder(Results.getAllocator(), + Results.getCodeCompletionTUInfo()); + llvm::SmallString<32> Text; + if (!Scope.empty()) { + Text.append(Scope); + Text.append("::"); + } + if (Underscores) + Text.append("__"); + Text.append(Name); + if (Underscores) + Text.append("__"); + Builder.AddTypedTextChunk(Results.getAllocator().CopyString(Text)); + + if (!A.ArgNames.empty()) { + Builder.AddChunk(CodeCompletionString::CK_LeftParen, "("); + bool First = true; + for (const char *Arg : A.ArgNames) { + if (!First) + Builder.AddChunk(CodeCompletionString::CK_Comma, ", "); + First = false; + Builder.AddPlaceholderChunk(Arg); + } + Builder.AddChunk(CodeCompletionString::CK_RightParen, ")"); + } + + Results.AddResult(Builder.TakeString()); + }; + // Generate the non-underscore-guarded result. // Note this is (a suffix of) the NormalizedFullName, no need to copy. // If an underscore-guarded scope was specified, only the // underscore-guarded attribute name is relevant. if (!InScopeUnderscore) - Results.AddResult(Scope.empty() ? Name.data() : S.NormalizedFullName); + Add(Scope, Name, /*Underscores=*/false); // Generate the underscore-guarded version, for syntaxes that support it. // We skip this if the scope was already spelled and not guarded, or // we must spell it and can't guard it. if (!(InScope && !InScopeUnderscore) && SyntaxSupportsGuards) { llvm::SmallString<32> Guarded; - if (!Scope.empty()) { + if (Scope.empty()) { + Add(Scope, Name, /*Underscores=*/true); + } else { const char *GuardedScope = underscoreAttrScope(Scope); if (!GuardedScope) continue; - Guarded.append(GuardedScope); - Guarded.append("::"); + Add(GuardedScope, Name, /*Underscores=*/true); } - Guarded.append("__"); - Guarded.append(Name); - Guarded.append("__"); - Results.AddResult( - CodeCompletionResult(Results.getAllocator().CopyString(Guarded))); } - // FIXME: include the list of arg names (not currently exposed). // It may be nice to include the Kind so we can look up the docs later. } }; diff --git a/clang/test/CodeCompletion/attr.cpp b/clang/test/CodeCompletion/attr.cpp --- a/clang/test/CodeCompletion/attr.cpp +++ b/clang/test/CodeCompletion/attr.cpp @@ -1,81 +1,83 @@ int a [[gnu::used]]; // RUN: %clang_cc1 -code-completion-at=%s:1:9 %s | FileCheck --check-prefix=STD %s -// STD: COMPLETION: __carries_dependency__ -// STD-NOT: COMPLETION: __convergent__ -// STD: COMPLETION: __gnu__::__used__ -// STD-NOT: COMPLETION: __gnu__::used -// STD-NOT: COMPLETION: __used__ -// STD: COMPLETION: _Clang::__convergent__ -// STD: COMPLETION: carries_dependency -// STD-NOT: COMPLETION: clang::called_once -// STD: COMPLETION: clang::convergent -// STD-NOT: COMPLETION: convergent -// STD-NOT: COMPLETION: gnu::__used__ -// STD: COMPLETION: gnu::used -// STD-NOT: COMPLETION: used +// STD: COMPLETION: Pattern : __carries_dependency__ +// STD-NOT: COMPLETION: Pattern : __convergent__ +// STD: COMPLETION: Pattern : __gnu__::__used__ +// STD-NOT: COMPLETION: Pattern : __gnu__::used +// STD-NOT: COMPLETION: Pattern : __used__ +// STD: COMPLETION: Pattern : _Clang::__convergent__ +// STD: COMPLETION: Pattern : carries_dependency +// STD-NOT: COMPLETION: Pattern : clang::called_once +// STD: COMPLETION: Pattern : clang::convergent +// STD-NOT: COMPLETION: Pattern : convergent +// STD-NOT: COMPLETION: Pattern : gnu::__used__ +// STD: COMPLETION: Pattern : gnu::abi_tag(<#Tags...#>) +// STD: COMPLETION: Pattern : gnu::alias(<#Aliasee#>) +// STD: COMPLETION: Pattern : gnu::used +// STD-NOT: COMPLETION: Pattern : used // RUN: %clang_cc1 -code-completion-at=%s:1:9 -xobjective-c++ %s | FileCheck --check-prefix=STD-OBJC %s -// STD-OBJC: COMPLETION: clang::called_once +// STD-OBJC: COMPLETION: Pattern : clang::called_once // RUN: %clang_cc1 -code-completion-at=%s:1:14 %s | FileCheck --check-prefix=STD-NS %s -// STD-NS-NOT: COMPLETION: __used__ -// STD-NS-NOT: COMPLETION: carries_dependency -// STD-NS-NOT: COMPLETION: clang::convergent -// STD-NS-NOT: COMPLETION: convergent -// STD-NS-NOT: COMPLETION: gnu::used -// STD-NS: COMPLETION: used +// STD-NS-NOT: COMPLETION: Pattern : __used__ +// STD-NS-NOT: COMPLETION: Pattern : carries_dependency +// STD-NS-NOT: COMPLETION: Pattern : clang::convergent +// STD-NS-NOT: COMPLETION: Pattern : convergent +// STD-NS-NOT: COMPLETION: Pattern : gnu::used +// STD-NS: COMPLETION: Pattern : used int b [[__gnu__::used]]; -// RUN: %clang_cc1 -code-completion-at=%s:25:18 %s | FileCheck --check-prefix=STD-NSU %s -// STD-NSU: COMPLETION: __used__ -// STD-NSU-NOT: COMPLETION: used +// RUN: %clang_cc1 -code-completion-at=%s:27:18 %s | FileCheck --check-prefix=STD-NSU %s +// STD-NSU: COMPLETION: Pattern : __used__ +// STD-NSU-NOT: COMPLETION: Pattern : used int c [[using gnu: used]]; -// RUN: %clang_cc1 -code-completion-at=%s:30:15 %s | FileCheck --check-prefix=STD-USING %s +// RUN: %clang_cc1 -code-completion-at=%s:32:15 %s | FileCheck --check-prefix=STD-USING %s // STD-USING: COMPLETION: __gnu__ // STD-USING: COMPLETION: _Clang -// STD-USING-NOT: COMPLETION: carries_dependency +// STD-USING-NOT: COMPLETION: Pattern : carries_dependency // STD-USING: COMPLETION: clang -// STD-USING-NOT: COMPLETION: clang:: -// STD-USING-NOT: COMPLETION: gnu:: +// STD-USING-NOT: COMPLETION: Pattern : clang:: +// STD-USING-NOT: COMPLETION: Pattern : gnu:: // STD-USING: COMPLETION: gnu -// RUN: %clang_cc1 -code-completion-at=%s:30:20 %s | FileCheck --check-prefix=STD-NS %s +// RUN: %clang_cc1 -code-completion-at=%s:32:20 %s | FileCheck --check-prefix=STD-NS %s int d __attribute__((used)); -// RUN: %clang_cc1 -code-completion-at=%s:41:22 %s | FileCheck --check-prefix=GNU %s -// GNU: COMPLETION: __carries_dependency__ -// GNU: COMPLETION: __convergent__ -// GNU-NOT: COMPLETION: __gnu__::__used__ -// GNU: COMPLETION: __used__ -// GNU-NOT: COMPLETION: _Clang::__convergent__ -// GNU: COMPLETION: carries_dependency -// GNU-NOT: COMPLETION: clang::convergent -// GNU: COMPLETION: convergent -// GNU-NOT: COMPLETION: gnu::used -// GNU: COMPLETION: used +// RUN: %clang_cc1 -code-completion-at=%s:43:22 %s | FileCheck --check-prefix=GNU %s +// GNU: COMPLETION: Pattern : __carries_dependency__ +// GNU: COMPLETION: Pattern : __convergent__ +// GNU-NOT: COMPLETION: Pattern : __gnu__::__used__ +// GNU: COMPLETION: Pattern : __used__ +// GNU-NOT: COMPLETION: Pattern : _Clang::__convergent__ +// GNU: COMPLETION: Pattern : carries_dependency +// GNU-NOT: COMPLETION: Pattern : clang::convergent +// GNU: COMPLETION: Pattern : convergent +// GNU-NOT: COMPLETION: Pattern : gnu::used +// GNU: COMPLETION: Pattern : used #pragma clang attribute push (__attribute__((internal_linkage)), apply_to=variable) int e; #pragma clang attribute pop -// RUN: %clang_cc1 -code-completion-at=%s:54:46 %s | FileCheck --check-prefix=PRAGMA %s -// PRAGMA: internal_linkage +// RUN: %clang_cc1 -code-completion-at=%s:56:46 %s | FileCheck --check-prefix=PRAGMA %s +// PRAGMA: COMPLETION: Pattern : internal_linkage #ifdef MS_EXT int __declspec(thread) f; -// RUN: %clang_cc1 -fms-extensions -DMS_EXT -code-completion-at=%s:61:16 %s | FileCheck --check-prefix=DS %s -// DS-NOT: COMPLETION: __convergent__ -// DS-NOT: COMPLETION: __used__ -// DS-NOT: COMPLETION: clang::convergent -// DS-NOT: COMPLETION: convergent -// DS: COMPLETION: thread -// DS-NOT: COMPLETION: used -// DS: COMPLETION: uuid +// RUN: %clang_cc1 -fms-extensions -DMS_EXT -code-completion-at=%s:63:16 %s | FileCheck --check-prefix=DS %s +// DS-NOT: COMPLETION: Pattern : __convergent__ +// DS-NOT: COMPLETION: Pattern : __used__ +// DS-NOT: COMPLETION: Pattern : clang::convergent +// DS-NOT: COMPLETION: Pattern : convergent +// DS: COMPLETION: Pattern : thread +// DS-NOT: COMPLETION: Pattern : used +// DS: COMPLETION: Pattern : uuid [uuid("123e4567-e89b-12d3-a456-426614174000")] struct g; -// RUN: %clang_cc1 -fms-extensions -DMS_EXT -code-completion-at=%s:71:2 %s | FileCheck --check-prefix=MS %s -// MS-NOT: COMPLETION: __uuid__ -// MS-NOT: COMPLETION: clang::convergent -// MS-NOT: COMPLETION: convergent -// MS-NOT: COMPLETION: thread -// MS-NOT: COMPLETION: used -// MS: COMPLETION: uuid +// RUN: %clang_cc1 -fms-extensions -DMS_EXT -code-completion-at=%s:73:2 %s | FileCheck --check-prefix=MS %s +// MS-NOT: COMPLETION: Pattern : __uuid__ +// MS-NOT: COMPLETION: Pattern : clang::convergent +// MS-NOT: COMPLETION: Pattern : convergent +// MS-NOT: COMPLETION: Pattern : thread +// MS-NOT: COMPLETION: Pattern : used +// MS: COMPLETION: Pattern : uuid #endif // MS_EXT void foo() { @@ -83,9 +85,9 @@ {} } // FIXME: support for omp attributes would be nice. -// RUN: %clang_cc1 -fopenmp -code-completion-at=%s:82:5 %s | FileCheck --check-prefix=OMP-NS --allow-empty %s -// OMP-NS-NOT: omp -// RUN: %clang_cc1 -fopenmp -code-completion-at=%s:82:10 %s | FileCheck --check-prefix=OMP-ATTR --allow-empty %s -// OMP-ATTR-NOT: sequence -// RUN: %clang_cc1 -fopenmp -code-completion-at=%s:82:19 %s | FileCheck --check-prefix=OMP-NESTED --allow-empty %s -// OMP-NESTED-NOT: directive +// RUN: %clang_cc1 -fopenmp -code-completion-at=%s:84:5 %s | FileCheck --check-prefix=OMP-NS --allow-empty %s +// OMP-NS-NOT: COMPLETION: omp +// RUN: %clang_cc1 -fopenmp -code-completion-at=%s:84:10 %s | FileCheck --check-prefix=OMP-ATTR --allow-empty %s +// OMP-ATTR-NOT: COMPLETION: Pattern : sequence +// RUN: %clang_cc1 -fopenmp -code-completion-at=%s:84:19 %s | FileCheck --check-prefix=OMP-NESTED --allow-empty %s +// OMP-NESTED-NOT: COMPLETION: Pattern : directive diff --git a/clang/utils/TableGen/ClangAttrEmitter.cpp b/clang/utils/TableGen/ClangAttrEmitter.cpp --- a/clang/utils/TableGen/ClangAttrEmitter.cpp +++ b/clang/utils/TableGen/ClangAttrEmitter.cpp @@ -3959,6 +3959,27 @@ } OS << "};\n"; } + + std::vector ArgNames; + for (const auto &Arg : Attr.getValueAsListOfDefs("Args")) { + bool UnusedUnset; + if (Arg->getValueAsBitOrUnset("Fake", UnusedUnset)) + continue; + ArgNames.push_back(Arg->getValueAsString("Name").str()); + for (const auto &Class : Arg->getSuperClasses()) { + if (Class.first->getName().startswith("Variadic")) { + ArgNames.back().append("..."); + break; + } + } + } + if (!ArgNames.empty()) { + OS << "static constexpr const char *" << I->first << "ArgNames[] = {\n"; + for (const auto &N : ArgNames) + OS << '"' << N << "\","; + OS << "};\n"; + } + OS << "struct ParsedAttrInfo" << I->first << " final : public ParsedAttrInfo {\n"; OS << " ParsedAttrInfo" << I->first << "() {\n"; @@ -3980,6 +4001,8 @@ OS << PragmaAttributeSupport.isAttributedSupported(*I->second) << ";\n"; if (!Spellings.empty()) OS << " Spellings = " << I->first << "Spellings;\n"; + if (!ArgNames.empty()) + OS << " ArgNames = " << I->first << "ArgNames;\n"; OS << " }\n"; GenerateAppertainsTo(Attr, OS); GenerateMutualExclusionsChecks(Attr, Records, OS, MergeDeclOS, MergeStmtOS);