diff options
Diffstat (limited to 'clang/lib/Sema/SemaCodeComplete.cpp')
-rw-r--r-- | clang/lib/Sema/SemaCodeComplete.cpp | 127 |
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) |