aboutsummaryrefslogtreecommitdiff
path: root/clang/lib/Sema/SemaCodeComplete.cpp
diff options
context:
space:
mode:
authorSam McCall <sam.mccall@gmail.com>2021-08-07 17:36:26 +0200
committerSam McCall <sam.mccall@gmail.com>2021-08-12 23:49:10 +0200
commitece4e920852185b332c2f7ba027e0c4a2972855a (patch)
tree7218f9b46468b3ffe46d95194c569091461c23d7 /clang/lib/Sema/SemaCodeComplete.cpp
parent3b99acbff2504d9f056c6bb76974286322e60382 (diff)
downloadllvm-ece4e920852185b332c2f7ba027e0c4a2972855a.zip
llvm-ece4e920852185b332c2f7ba027e0c4a2972855a.tar.gz
llvm-ece4e920852185b332c2f7ba027e0c4a2972855a.tar.bz2
[CodeComplete] Basic code completion for attribute names.
Only the bare name is completed, with no args. For args to be useful we need arg names. These *are* in the tablegen but not currently emitted in usable form, so left this as future work. C++11, C2x, GNU, declspec, MS syntax is supported, with the appropriate spellings of attributes suggested. `#pragma clang attribute` is supported but not terribly useful as we only reach completion if parens are balanced (i.e. the line is not truncated) There's no filtering of which attributes might make sense in this grammatical context (e.g. attached to a function). In code-completion context this is hard to do, and will only work in few cases :-( There's also no filtering by langopts: this is because currently the only way of checking is to try to produce diagnostics, which requires a valid ParsedAttr which is hard to get. This should be fairly simple to fix but requires some tablegen changes to expose the logic without the side-effect. Differential Revision: https://reviews.llvm.org/D107696
Diffstat (limited to 'clang/lib/Sema/SemaCodeComplete.cpp')
-rw-r--r--clang/lib/Sema/SemaCodeComplete.cpp127
1 files changed, 127 insertions, 0 deletions
diff --git a/clang/lib/Sema/SemaCodeComplete.cpp b/clang/lib/Sema/SemaCodeComplete.cpp
index e03b671..e8e8fb2 100644
--- a/clang/lib/Sema/SemaCodeComplete.cpp
+++ b/clang/lib/Sema/SemaCodeComplete.cpp
@@ -23,6 +23,7 @@
#include "clang/AST/QualTypeNames.h"
#include "clang/AST/RecursiveASTVisitor.h"
#include "clang/AST/Type.h"
+#include "clang/Basic/AttributeCommonInfo.h"
#include "clang/Basic/CharInfo.h"
#include "clang/Basic/OperatorKinds.h"
#include "clang/Basic/Specifiers.h"
@@ -34,6 +35,7 @@
#include "clang/Sema/Designator.h"
#include "clang/Sema/Lookup.h"
#include "clang/Sema/Overload.h"
+#include "clang/Sema/ParsedAttr.h"
#include "clang/Sema/Scope.h"
#include "clang/Sema/ScopeInfo.h"
#include "clang/Sema/Sema.h"
@@ -4335,6 +4337,131 @@ void Sema::CodeCompleteDeclSpec(Scope *S, DeclSpec &DS,
Results.data(), Results.size());
}
+static const char *underscoreAttrScope(llvm::StringRef Scope) {
+ if (Scope == "clang")
+ return "_Clang";
+ if (Scope == "gnu")
+ return "__gnu__";
+ return nullptr;
+}
+
+static const char *noUnderscoreAttrScope(llvm::StringRef Scope) {
+ if (Scope == "_Clang")
+ return "clang";
+ if (Scope == "__gnu__")
+ return "gnu";
+ return nullptr;
+}
+
+void Sema::CodeCompleteAttribute(AttributeCommonInfo::Syntax Syntax,
+ AttributeCompletion Completion,
+ const IdentifierInfo *InScope) {
+ if (Completion == AttributeCompletion::None)
+ return;
+ ResultBuilder Results(*this, CodeCompleter->getAllocator(),
+ CodeCompleter->getCodeCompletionTUInfo(),
+ CodeCompletionContext::CCC_Attribute);
+
+ // We're going to iterate over the normalized spellings of the attribute.
+ // These don't include "underscore guarding": the normalized spelling is
+ // clang::foo but you can also write _Clang::__foo__.
+ //
+ // (Clang supports a mix like clang::__foo__ but we won't suggest it: either
+ // you care about clashing with macros or you don't).
+ //
+ // So if we're already in a scope, we determine its canonical spellings
+ // (for comparison with normalized attr spelling) and remember whether it was
+ // underscore-guarded (so we know how to spell contained attributes).
+ llvm::StringRef InScopeName;
+ bool InScopeUnderscore = false;
+ if (InScope) {
+ InScopeName = InScope->getName();
+ if (const char *NoUnderscore = noUnderscoreAttrScope(InScopeName)) {
+ InScopeName = NoUnderscore;
+ InScopeUnderscore = true;
+ }
+ }
+ bool SyntaxSupportsGuards = Syntax == AttributeCommonInfo::AS_GNU ||
+ Syntax == AttributeCommonInfo::AS_CXX11 ||
+ Syntax == AttributeCommonInfo::AS_C2x;
+
+ llvm::DenseSet<llvm::StringRef> FoundScopes;
+ auto AddCompletions = [&](const ParsedAttrInfo &A) {
+ if (A.IsTargetSpecific && !A.existsInTarget(Context.getTargetInfo()))
+ return;
+ // FIXME: filter by langopts (diagLangOpts method requires a ParsedAttr)
+ for (const auto &S : A.Spellings) {
+ if (S.Syntax != Syntax)
+ continue;
+ llvm::StringRef Name = S.NormalizedFullName;
+ llvm::StringRef Scope;
+ if ((Syntax == AttributeCommonInfo::AS_CXX11 ||
+ Syntax == AttributeCommonInfo::AS_C2x)) {
+ std::tie(Scope, Name) = Name.split("::");
+ if (Name.empty()) // oops, unscoped
+ std::swap(Name, Scope);
+ }
+
+ // Do we just want a list of scopes rather than attributes?
+ if (Completion == AttributeCompletion::Scope) {
+ // Make sure to emit each scope only once.
+ if (!Scope.empty() && FoundScopes.insert(Scope).second) {
+ Results.AddResult(
+ CodeCompletionResult(Results.getAllocator().CopyString(Scope)));
+ // Include alternate form (__gnu__ instead of gnu).
+ if (const char *Scope2 = underscoreAttrScope(Scope))
+ Results.AddResult(CodeCompletionResult(Scope2));
+ }
+ continue;
+ }
+
+ // If a scope was specified, it must match but we don't need to print it.
+ if (!InScopeName.empty()) {
+ if (Scope != InScopeName)
+ continue;
+ Scope = "";
+ }
+
+ // 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);
+
+ // 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()) {
+ const char *GuardedScope = underscoreAttrScope(Scope);
+ if (!GuardedScope)
+ continue;
+ Guarded.append(GuardedScope);
+ Guarded.append("::");
+ }
+ 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.
+ }
+ };
+
+ for (const auto *A : ParsedAttrInfo::getAllBuiltin())
+ AddCompletions(*A);
+ for (const auto &Entry : ParsedAttrInfoRegistry::entries())
+ AddCompletions(*Entry.instantiate());
+
+ HandleCodeCompleteResults(this, CodeCompleter, Results.getCompletionContext(),
+ Results.data(), Results.size());
+}
+
struct Sema::CodeCompleteExpressionData {
CodeCompleteExpressionData(QualType PreferredType = QualType(),
bool IsParenthesized = false)