[CodeCompletion] Provide placeholders for known attribute arguments
authorSam McCall <sam.mccall@gmail.com>
Fri, 13 Aug 2021 08:17:59 +0000 (10:17 +0200)
committerSam McCall <sam.mccall@gmail.com>
Thu, 19 Aug 2021 12:03:41 +0000 (14:03 +0200)
Completion now looks more like function/member completion:

  used
  alias(Aliasee)
  abi_tag(Tags...)

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

clang/include/clang/Sema/ParsedAttr.h
clang/lib/Sema/SemaCodeComplete.cpp
clang/test/CodeCompletion/attr.cpp
clang/utils/TableGen/ClangAttrEmitter.cpp

index 408032c..64a0788 100644 (file)
@@ -67,6 +67,8 @@ struct ParsedAttrInfo {
     const char *NormalizedFullName;
   };
   ArrayRef<Spelling> Spellings;
+  // The names of the known arguments of this attribute.
+  ArrayRef<const char *> ArgNames;
 
   ParsedAttrInfo(AttributeCommonInfo::Kind AttrKind =
                      AttributeCommonInfo::NoSemaHandlerAttribute)
index 445ab48..8c551a7 100644 (file)
@@ -4423,33 +4423,59 @@ void Sema::CodeCompleteAttribute(AttributeCommonInfo::Syntax Syntax,
         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.
     }
   };
index 782e5a1..c3f76e7 100644 (file)
@@ -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 @@ void foo() {
   {}
 }
 // 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
index e5ac084..342bbb3 100644 (file)
@@ -3959,6 +3959,27 @@ void EmitClangAttrParsedAttrImpl(RecordKeeper &Records, raw_ostream &OS) {
       }
       OS << "};\n";
     }
+
+    std::vector<std::string> 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 @@ void EmitClangAttrParsedAttrImpl(RecordKeeper &Records, raw_ostream &OS) {
     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);