| //===---------------- SemaCodeComplete.cpp - Code Completion ----*- C++ -*-===// |
| // |
| // The LLVM Compiler Infrastructure |
| // |
| // This file is distributed under the University of Illinois Open Source |
| // License. See LICENSE.TXT for details. |
| // |
| //===----------------------------------------------------------------------===// |
| // |
| // This file defines the code-completion semantic actions. |
| // |
| //===----------------------------------------------------------------------===// |
| #include "clang/AST/DeclCXX.h" |
| #include "clang/AST/DeclObjC.h" |
| #include "clang/AST/ExprCXX.h" |
| #include "clang/AST/ExprObjC.h" |
| #include "clang/AST/QualTypeNames.h" |
| #include "clang/Basic/CharInfo.h" |
| #include "clang/Lex/HeaderSearch.h" |
| #include "clang/Lex/MacroInfo.h" |
| #include "clang/Lex/Preprocessor.h" |
| #include "clang/Sema/CodeCompleteConsumer.h" |
| #include "clang/Sema/Lookup.h" |
| #include "clang/Sema/Overload.h" |
| #include "clang/Sema/Scope.h" |
| #include "clang/Sema/ScopeInfo.h" |
| #include "clang/Sema/SemaInternal.h" |
| #include "llvm/ADT/DenseSet.h" |
| #include "llvm/ADT/SmallBitVector.h" |
| #include "llvm/ADT/SmallPtrSet.h" |
| #include "llvm/ADT/SmallString.h" |
| #include "llvm/ADT/StringExtras.h" |
| #include "llvm/ADT/StringSwitch.h" |
| #include "llvm/ADT/Twine.h" |
| #include <list> |
| #include <map> |
| #include <vector> |
| |
| using namespace clang; |
| using namespace sema; |
| |
| namespace { |
| /// \brief A container of code-completion results. |
| class ResultBuilder { |
| public: |
| /// \brief The type of a name-lookup filter, which can be provided to the |
| /// name-lookup routines to specify which declarations should be included in |
| /// the result set (when it returns true) and which declarations should be |
| /// filtered out (returns false). |
| typedef bool (ResultBuilder::*LookupFilter)(const NamedDecl *) const; |
| |
| typedef CodeCompletionResult Result; |
| |
| private: |
| /// \brief The actual results we have found. |
| std::vector<Result> Results; |
| |
| /// \brief A record of all of the declarations we have found and placed |
| /// into the result set, used to ensure that no declaration ever gets into |
| /// the result set twice. |
| llvm::SmallPtrSet<const Decl*, 16> AllDeclsFound; |
| |
| typedef std::pair<const NamedDecl *, unsigned> DeclIndexPair; |
| |
| /// \brief An entry in the shadow map, which is optimized to store |
| /// a single (declaration, index) mapping (the common case) but |
| /// can also store a list of (declaration, index) mappings. |
| class ShadowMapEntry { |
| typedef SmallVector<DeclIndexPair, 4> DeclIndexPairVector; |
| |
| /// \brief Contains either the solitary NamedDecl * or a vector |
| /// of (declaration, index) pairs. |
| llvm::PointerUnion<const NamedDecl *, DeclIndexPairVector*> DeclOrVector; |
| |
| /// \brief When the entry contains a single declaration, this is |
| /// the index associated with that entry. |
| unsigned SingleDeclIndex; |
| |
| public: |
| ShadowMapEntry() : DeclOrVector(), SingleDeclIndex(0) { } |
| |
| void Add(const NamedDecl *ND, unsigned Index) { |
| if (DeclOrVector.isNull()) { |
| // 0 - > 1 elements: just set the single element information. |
| DeclOrVector = ND; |
| SingleDeclIndex = Index; |
| return; |
| } |
| |
| if (const NamedDecl *PrevND = |
| DeclOrVector.dyn_cast<const NamedDecl *>()) { |
| // 1 -> 2 elements: create the vector of results and push in the |
| // existing declaration. |
| DeclIndexPairVector *Vec = new DeclIndexPairVector; |
| Vec->push_back(DeclIndexPair(PrevND, SingleDeclIndex)); |
| DeclOrVector = Vec; |
| } |
| |
| // Add the new element to the end of the vector. |
| DeclOrVector.get<DeclIndexPairVector*>()->push_back( |
| DeclIndexPair(ND, Index)); |
| } |
| |
| void Destroy() { |
| if (DeclIndexPairVector *Vec |
| = DeclOrVector.dyn_cast<DeclIndexPairVector *>()) { |
| delete Vec; |
| DeclOrVector = ((NamedDecl *)nullptr); |
| } |
| } |
| |
| // Iteration. |
| class iterator; |
| iterator begin() const; |
| iterator end() const; |
| }; |
| |
| /// \brief A mapping from declaration names to the declarations that have |
| /// this name within a particular scope and their index within the list of |
| /// results. |
| typedef llvm::DenseMap<DeclarationName, ShadowMapEntry> ShadowMap; |
| |
| /// \brief The semantic analysis object for which results are being |
| /// produced. |
| Sema &SemaRef; |
| |
| /// \brief The allocator used to allocate new code-completion strings. |
| CodeCompletionAllocator &Allocator; |
| |
| CodeCompletionTUInfo &CCTUInfo; |
| |
| /// \brief If non-NULL, a filter function used to remove any code-completion |
| /// results that are not desirable. |
| LookupFilter Filter; |
| |
| /// \brief Whether we should allow declarations as |
| /// nested-name-specifiers that would otherwise be filtered out. |
| bool AllowNestedNameSpecifiers; |
| |
| /// \brief If set, the type that we would prefer our resulting value |
| /// declarations to have. |
| /// |
| /// Closely matching the preferred type gives a boost to a result's |
| /// priority. |
| CanQualType PreferredType; |
| |
| /// \brief A list of shadow maps, which is used to model name hiding at |
| /// different levels of, e.g., the inheritance hierarchy. |
| std::list<ShadowMap> ShadowMaps; |
| |
| /// \brief If we're potentially referring to a C++ member function, the set |
| /// of qualifiers applied to the object type. |
| Qualifiers ObjectTypeQualifiers; |
| |
| /// \brief Whether the \p ObjectTypeQualifiers field is active. |
| bool HasObjectTypeQualifiers; |
| |
| /// \brief The selector that we prefer. |
| Selector PreferredSelector; |
| |
| /// \brief The completion context in which we are gathering results. |
| CodeCompletionContext CompletionContext; |
| |
| /// \brief If we are in an instance method definition, the \@implementation |
| /// object. |
| ObjCImplementationDecl *ObjCImplementation; |
| |
| void AdjustResultPriorityForDecl(Result &R); |
| |
| void MaybeAddConstructorResults(Result R); |
| |
| public: |
| explicit ResultBuilder(Sema &SemaRef, CodeCompletionAllocator &Allocator, |
| CodeCompletionTUInfo &CCTUInfo, |
| const CodeCompletionContext &CompletionContext, |
| LookupFilter Filter = nullptr) |
| : SemaRef(SemaRef), Allocator(Allocator), CCTUInfo(CCTUInfo), |
| Filter(Filter), |
| AllowNestedNameSpecifiers(false), HasObjectTypeQualifiers(false), |
| CompletionContext(CompletionContext), |
| ObjCImplementation(nullptr) |
| { |
| // If this is an Objective-C instance method definition, dig out the |
| // corresponding implementation. |
| switch (CompletionContext.getKind()) { |
| case CodeCompletionContext::CCC_Expression: |
| case CodeCompletionContext::CCC_ObjCMessageReceiver: |
| case CodeCompletionContext::CCC_ParenthesizedExpression: |
| case CodeCompletionContext::CCC_Statement: |
| case CodeCompletionContext::CCC_Recovery: |
| if (ObjCMethodDecl *Method = SemaRef.getCurMethodDecl()) |
| if (Method->isInstanceMethod()) |
| if (ObjCInterfaceDecl *Interface = Method->getClassInterface()) |
| ObjCImplementation = Interface->getImplementation(); |
| break; |
| |
| default: |
| break; |
| } |
| } |
| |
| /// \brief Determine the priority for a reference to the given declaration. |
| unsigned getBasePriority(const NamedDecl *D); |
| |
| /// \brief Whether we should include code patterns in the completion |
| /// results. |
| bool includeCodePatterns() const { |
| return SemaRef.CodeCompleter && |
| SemaRef.CodeCompleter->includeCodePatterns(); |
| } |
| |
| /// \brief Set the filter used for code-completion results. |
| void setFilter(LookupFilter Filter) { |
| this->Filter = Filter; |
| } |
| |
| Result *data() { return Results.empty()? nullptr : &Results.front(); } |
| unsigned size() const { return Results.size(); } |
| bool empty() const { return Results.empty(); } |
| |
| /// \brief Specify the preferred type. |
| void setPreferredType(QualType T) { |
| PreferredType = SemaRef.Context.getCanonicalType(T); |
| } |
| |
| /// \brief Set the cv-qualifiers on the object type, for us in filtering |
| /// calls to member functions. |
| /// |
| /// When there are qualifiers in this set, they will be used to filter |
| /// out member functions that aren't available (because there will be a |
| /// cv-qualifier mismatch) or prefer functions with an exact qualifier |
| /// match. |
| void setObjectTypeQualifiers(Qualifiers Quals) { |
| ObjectTypeQualifiers = Quals; |
| HasObjectTypeQualifiers = true; |
| } |
| |
| /// \brief Set the preferred selector. |
| /// |
| /// When an Objective-C method declaration result is added, and that |
| /// method's selector matches this preferred selector, we give that method |
| /// a slight priority boost. |
| void setPreferredSelector(Selector Sel) { |
| PreferredSelector = Sel; |
| } |
| |
| /// \brief Retrieve the code-completion context for which results are |
| /// being collected. |
| const CodeCompletionContext &getCompletionContext() const { |
| return CompletionContext; |
| } |
| |
| /// \brief Specify whether nested-name-specifiers are allowed. |
| void allowNestedNameSpecifiers(bool Allow = true) { |
| AllowNestedNameSpecifiers = Allow; |
| } |
| |
| /// \brief Return the semantic analysis object for which we are collecting |
| /// code completion results. |
| Sema &getSema() const { return SemaRef; } |
| |
| /// \brief Retrieve the allocator used to allocate code completion strings. |
| CodeCompletionAllocator &getAllocator() const { return Allocator; } |
| |
| CodeCompletionTUInfo &getCodeCompletionTUInfo() const { return CCTUInfo; } |
| |
| /// \brief Determine whether the given declaration is at all interesting |
| /// as a code-completion result. |
| /// |
| /// \param ND the declaration that we are inspecting. |
| /// |
| /// \param AsNestedNameSpecifier will be set true if this declaration is |
| /// only interesting when it is a nested-name-specifier. |
| bool isInterestingDecl(const NamedDecl *ND, |
| bool &AsNestedNameSpecifier) const; |
| |
| /// \brief Check whether the result is hidden by the Hiding declaration. |
| /// |
| /// \returns true if the result is hidden and cannot be found, false if |
| /// the hidden result could still be found. When false, \p R may be |
| /// modified to describe how the result can be found (e.g., via extra |
| /// qualification). |
| bool CheckHiddenResult(Result &R, DeclContext *CurContext, |
| const NamedDecl *Hiding); |
| |
| /// \brief Add a new result to this result set (if it isn't already in one |
| /// of the shadow maps), or replace an existing result (for, e.g., a |
| /// redeclaration). |
| /// |
| /// \param R the result to add (if it is unique). |
| /// |
| /// \param CurContext the context in which this result will be named. |
| void MaybeAddResult(Result R, DeclContext *CurContext = nullptr); |
| |
| /// \brief Add a new result to this result set, where we already know |
| /// the hiding declaration (if any). |
| /// |
| /// \param R the result to add (if it is unique). |
| /// |
| /// \param CurContext the context in which this result will be named. |
| /// |
| /// \param Hiding the declaration that hides the result. |
| /// |
| /// \param InBaseClass whether the result was found in a base |
| /// class of the searched context. |
| void AddResult(Result R, DeclContext *CurContext, NamedDecl *Hiding, |
| bool InBaseClass); |
| |
| /// \brief Add a new non-declaration result to this result set. |
| void AddResult(Result R); |
| |
| /// \brief Enter into a new scope. |
| void EnterNewScope(); |
| |
| /// \brief Exit from the current scope. |
| void ExitScope(); |
| |
| /// \brief Ignore this declaration, if it is seen again. |
| void Ignore(const Decl *D) { AllDeclsFound.insert(D->getCanonicalDecl()); } |
| |
| /// \name Name lookup predicates |
| /// |
| /// These predicates can be passed to the name lookup functions to filter the |
| /// results of name lookup. All of the predicates have the same type, so that |
| /// |
| //@{ |
| bool IsOrdinaryName(const NamedDecl *ND) const; |
| bool IsOrdinaryNonTypeName(const NamedDecl *ND) const; |
| bool IsIntegralConstantValue(const NamedDecl *ND) const; |
| bool IsOrdinaryNonValueName(const NamedDecl *ND) const; |
| bool IsNestedNameSpecifier(const NamedDecl *ND) const; |
| bool IsEnum(const NamedDecl *ND) const; |
| bool IsClassOrStruct(const NamedDecl *ND) const; |
| bool IsUnion(const NamedDecl *ND) const; |
| bool IsNamespace(const NamedDecl *ND) const; |
| bool IsNamespaceOrAlias(const NamedDecl *ND) const; |
| bool IsType(const NamedDecl *ND) const; |
| bool IsMember(const NamedDecl *ND) const; |
| bool IsObjCIvar(const NamedDecl *ND) const; |
| bool IsObjCMessageReceiver(const NamedDecl *ND) const; |
| bool IsObjCMessageReceiverOrLambdaCapture(const NamedDecl *ND) const; |
| bool IsObjCCollection(const NamedDecl *ND) const; |
| bool IsImpossibleToSatisfy(const NamedDecl *ND) const; |
| //@} |
| }; |
| } |
| |
| class ResultBuilder::ShadowMapEntry::iterator { |
| llvm::PointerUnion<const NamedDecl *, const DeclIndexPair *> DeclOrIterator; |
| unsigned SingleDeclIndex; |
| |
| public: |
| typedef DeclIndexPair value_type; |
| typedef value_type reference; |
| typedef std::ptrdiff_t difference_type; |
| typedef std::input_iterator_tag iterator_category; |
| |
| class pointer { |
| DeclIndexPair Value; |
| |
| public: |
| pointer(const DeclIndexPair &Value) : Value(Value) { } |
| |
| const DeclIndexPair *operator->() const { |
| return &Value; |
| } |
| }; |
| |
| iterator() : DeclOrIterator((NamedDecl *)nullptr), SingleDeclIndex(0) {} |
| |
| iterator(const NamedDecl *SingleDecl, unsigned Index) |
| : DeclOrIterator(SingleDecl), SingleDeclIndex(Index) { } |
| |
| iterator(const DeclIndexPair *Iterator) |
| : DeclOrIterator(Iterator), SingleDeclIndex(0) { } |
| |
| iterator &operator++() { |
| if (DeclOrIterator.is<const NamedDecl *>()) { |
| DeclOrIterator = (NamedDecl *)nullptr; |
| SingleDeclIndex = 0; |
| return *this; |
| } |
| |
| const DeclIndexPair *I = DeclOrIterator.get<const DeclIndexPair*>(); |
| ++I; |
| DeclOrIterator = I; |
| return *this; |
| } |
| |
| /*iterator operator++(int) { |
| iterator tmp(*this); |
| ++(*this); |
| return tmp; |
| }*/ |
| |
| reference operator*() const { |
| if (const NamedDecl *ND = DeclOrIterator.dyn_cast<const NamedDecl *>()) |
| return reference(ND, SingleDeclIndex); |
| |
| return *DeclOrIterator.get<const DeclIndexPair*>(); |
| } |
| |
| pointer operator->() const { |
| return pointer(**this); |
| } |
| |
| friend bool operator==(const iterator &X, const iterator &Y) { |
| return X.DeclOrIterator.getOpaqueValue() |
| == Y.DeclOrIterator.getOpaqueValue() && |
| X.SingleDeclIndex == Y.SingleDeclIndex; |
| } |
| |
| friend bool operator!=(const iterator &X, const iterator &Y) { |
| return !(X == Y); |
| } |
| }; |
| |
| ResultBuilder::ShadowMapEntry::iterator |
| ResultBuilder::ShadowMapEntry::begin() const { |
| if (DeclOrVector.isNull()) |
| return iterator(); |
| |
| if (const NamedDecl *ND = DeclOrVector.dyn_cast<const NamedDecl *>()) |
| return iterator(ND, SingleDeclIndex); |
| |
| return iterator(DeclOrVector.get<DeclIndexPairVector *>()->begin()); |
| } |
| |
| ResultBuilder::ShadowMapEntry::iterator |
| ResultBuilder::ShadowMapEntry::end() const { |
| if (DeclOrVector.is<const NamedDecl *>() || DeclOrVector.isNull()) |
| return iterator(); |
| |
| return iterator(DeclOrVector.get<DeclIndexPairVector *>()->end()); |
| } |
| |
| /// \brief Compute the qualification required to get from the current context |
| /// (\p CurContext) to the target context (\p TargetContext). |
| /// |
| /// \param Context the AST context in which the qualification will be used. |
| /// |
| /// \param CurContext the context where an entity is being named, which is |
| /// typically based on the current scope. |
| /// |
| /// \param TargetContext the context in which the named entity actually |
| /// resides. |
| /// |
| /// \returns a nested name specifier that refers into the target context, or |
| /// NULL if no qualification is needed. |
| static NestedNameSpecifier * |
| getRequiredQualification(ASTContext &Context, |
| const DeclContext *CurContext, |
| const DeclContext *TargetContext) { |
| SmallVector<const DeclContext *, 4> TargetParents; |
| |
| for (const DeclContext *CommonAncestor = TargetContext; |
| CommonAncestor && !CommonAncestor->Encloses(CurContext); |
| CommonAncestor = CommonAncestor->getLookupParent()) { |
| if (CommonAncestor->isTransparentContext() || |
| CommonAncestor->isFunctionOrMethod()) |
| continue; |
| |
| TargetParents.push_back(CommonAncestor); |
| } |
| |
| NestedNameSpecifier *Result = nullptr; |
| while (!TargetParents.empty()) { |
| const DeclContext *Parent = TargetParents.pop_back_val(); |
| |
| if (const NamespaceDecl *Namespace = dyn_cast<NamespaceDecl>(Parent)) { |
| if (!Namespace->getIdentifier()) |
| continue; |
| |
| Result = NestedNameSpecifier::Create(Context, Result, Namespace); |
| } |
| else if (const TagDecl *TD = dyn_cast<TagDecl>(Parent)) |
| Result = NestedNameSpecifier::Create(Context, Result, |
| false, |
| Context.getTypeDeclType(TD).getTypePtr()); |
| } |
| return Result; |
| } |
| |
| /// Determine whether \p Id is a name reserved for the implementation (C99 |
| /// 7.1.3, C++ [lib.global.names]). |
| static bool isReservedName(const IdentifierInfo *Id, |
| bool doubleUnderscoreOnly = false) { |
| if (Id->getLength() < 2) |
| return false; |
| const char *Name = Id->getNameStart(); |
| return Name[0] == '_' && |
| (Name[1] == '_' || (Name[1] >= 'A' && Name[1] <= 'Z' && |
| !doubleUnderscoreOnly)); |
| } |
| |
| // Some declarations have reserved names that we don't want to ever show. |
| // Filter out names reserved for the implementation if they come from a |
| // system header. |
| static bool shouldIgnoreDueToReservedName(const NamedDecl *ND, Sema &SemaRef) { |
| const IdentifierInfo *Id = ND->getIdentifier(); |
| if (!Id) |
| return false; |
| |
| // Ignore reserved names for compiler provided decls. |
| if (isReservedName(Id) && ND->getLocation().isInvalid()) |
| return true; |
| |
| // For system headers ignore only double-underscore names. |
| // This allows for system headers providing private symbols with a single |
| // underscore. |
| if (isReservedName(Id, /*doubleUnderscoreOnly=*/true) && |
| SemaRef.SourceMgr.isInSystemHeader( |
| SemaRef.SourceMgr.getSpellingLoc(ND->getLocation()))) |
| return true; |
| |
| return false; |
| } |
| |
| bool ResultBuilder::isInterestingDecl(const NamedDecl *ND, |
| bool &AsNestedNameSpecifier) const { |
| AsNestedNameSpecifier = false; |
| |
| auto *Named = ND; |
| ND = ND->getUnderlyingDecl(); |
| |
| // Skip unnamed entities. |
| if (!ND->getDeclName()) |
| return false; |
| |
| // Friend declarations and declarations introduced due to friends are never |
| // added as results. |
| if (ND->getFriendObjectKind() == Decl::FOK_Undeclared) |
| return false; |
| |
| // Class template (partial) specializations are never added as results. |
| if (isa<ClassTemplateSpecializationDecl>(ND) || |
| isa<ClassTemplatePartialSpecializationDecl>(ND)) |
| return false; |
| |
| // Using declarations themselves are never added as results. |
| if (isa<UsingDecl>(ND)) |
| return false; |
| |
| if (shouldIgnoreDueToReservedName(ND, SemaRef)) |
| return false; |
| |
| if (Filter == &ResultBuilder::IsNestedNameSpecifier || |
| (isa<NamespaceDecl>(ND) && |
| Filter != &ResultBuilder::IsNamespace && |
| Filter != &ResultBuilder::IsNamespaceOrAlias && |
| Filter != nullptr)) |
| AsNestedNameSpecifier = true; |
| |
| // Filter out any unwanted results. |
| if (Filter && !(this->*Filter)(Named)) { |
| // Check whether it is interesting as a nested-name-specifier. |
| if (AllowNestedNameSpecifiers && SemaRef.getLangOpts().CPlusPlus && |
| IsNestedNameSpecifier(ND) && |
| (Filter != &ResultBuilder::IsMember || |
| (isa<CXXRecordDecl>(ND) && |
| cast<CXXRecordDecl>(ND)->isInjectedClassName()))) { |
| AsNestedNameSpecifier = true; |
| return true; |
| } |
| |
| return false; |
| } |
| // ... then it must be interesting! |
| return true; |
| } |
| |
| bool ResultBuilder::CheckHiddenResult(Result &R, DeclContext *CurContext, |
| const NamedDecl *Hiding) { |
| // In C, there is no way to refer to a hidden name. |
| // FIXME: This isn't true; we can find a tag name hidden by an ordinary |
| // name if we introduce the tag type. |
| if (!SemaRef.getLangOpts().CPlusPlus) |
| return true; |
| |
| const DeclContext *HiddenCtx = |
| R.Declaration->getDeclContext()->getRedeclContext(); |
| |
| // There is no way to qualify a name declared in a function or method. |
| if (HiddenCtx->isFunctionOrMethod()) |
| return true; |
| |
| if (HiddenCtx == Hiding->getDeclContext()->getRedeclContext()) |
| return true; |
| |
| // We can refer to the result with the appropriate qualification. Do it. |
| R.Hidden = true; |
| R.QualifierIsInformative = false; |
| |
| if (!R.Qualifier) |
| R.Qualifier = getRequiredQualification(SemaRef.Context, |
| CurContext, |
| R.Declaration->getDeclContext()); |
| return false; |
| } |
| |
| /// \brief A simplified classification of types used to determine whether two |
| /// types are "similar enough" when adjusting priorities. |
| SimplifiedTypeClass clang::getSimplifiedTypeClass(CanQualType T) { |
| switch (T->getTypeClass()) { |
| case Type::Builtin: |
| switch (cast<BuiltinType>(T)->getKind()) { |
| case BuiltinType::Void: |
| return STC_Void; |
| |
| case BuiltinType::NullPtr: |
| return STC_Pointer; |
| |
| case BuiltinType::Overload: |
| case BuiltinType::Dependent: |
| return STC_Other; |
| |
| case BuiltinType::ObjCId: |
| case BuiltinType::ObjCClass: |
| case BuiltinType::ObjCSel: |
| return STC_ObjectiveC; |
| |
| default: |
| return STC_Arithmetic; |
| } |
| |
| case Type::Complex: |
| return STC_Arithmetic; |
| |
| case Type::Pointer: |
| return STC_Pointer; |
| |
| case Type::BlockPointer: |
| return STC_Block; |
| |
| case Type::LValueReference: |
| case Type::RValueReference: |
| return getSimplifiedTypeClass(T->getAs<ReferenceType>()->getPointeeType()); |
| |
| case Type::ConstantArray: |
| case Type::IncompleteArray: |
| case Type::VariableArray: |
| case Type::DependentSizedArray: |
| return STC_Array; |
| |
| case Type::DependentSizedExtVector: |
| case Type::Vector: |
| case Type::ExtVector: |
| return STC_Arithmetic; |
| |
| case Type::FunctionProto: |
| case Type::FunctionNoProto: |
| return STC_Function; |
| |
| case Type::Record: |
| return STC_Record; |
| |
| case Type::Enum: |
| return STC_Arithmetic; |
| |
| case Type::ObjCObject: |
| case Type::ObjCInterface: |
| case Type::ObjCObjectPointer: |
| return STC_ObjectiveC; |
| |
| default: |
| return STC_Other; |
| } |
| } |
| |
| /// \brief Get the type that a given expression will have if this declaration |
| /// is used as an expression in its "typical" code-completion form. |
| QualType clang::getDeclUsageType(ASTContext &C, const NamedDecl *ND) { |
| ND = cast<NamedDecl>(ND->getUnderlyingDecl()); |
| |
| if (const TypeDecl *Type = dyn_cast<TypeDecl>(ND)) |
| return C.getTypeDeclType(Type); |
| if (const ObjCInterfaceDecl *Iface = dyn_cast<ObjCInterfaceDecl>(ND)) |
| return C.getObjCInterfaceType(Iface); |
| |
| QualType T; |
| if (const FunctionDecl *Function = ND->getAsFunction()) |
| T = Function->getCallResultType(); |
| else if (const ObjCMethodDecl *Method = dyn_cast<ObjCMethodDecl>(ND)) |
| T = Method->getSendResultType(); |
| else if (const EnumConstantDecl *Enumerator = dyn_cast<EnumConstantDecl>(ND)) |
| T = C.getTypeDeclType(cast<EnumDecl>(Enumerator->getDeclContext())); |
| else if (const ObjCPropertyDecl *Property = dyn_cast<ObjCPropertyDecl>(ND)) |
| T = Property->getType(); |
| else if (const ValueDecl *Value = dyn_cast<ValueDecl>(ND)) |
| T = Value->getType(); |
| else |
| return QualType(); |
| |
| // Dig through references, function pointers, and block pointers to |
| // get down to the likely type of an expression when the entity is |
| // used. |
| do { |
| if (const ReferenceType *Ref = T->getAs<ReferenceType>()) { |
| T = Ref->getPointeeType(); |
| continue; |
| } |
| |
| if (const PointerType *Pointer = T->getAs<PointerType>()) { |
| if (Pointer->getPointeeType()->isFunctionType()) { |
| T = Pointer->getPointeeType(); |
| continue; |
| } |
| |
| break; |
| } |
| |
| if (const BlockPointerType *Block = T->getAs<BlockPointerType>()) { |
| T = Block->getPointeeType(); |
| continue; |
| } |
| |
| if (const FunctionType *Function = T->getAs<FunctionType>()) { |
| T = Function->getReturnType(); |
| continue; |
| } |
| |
| break; |
| } while (true); |
| |
| return T; |
| } |
| |
| unsigned ResultBuilder::getBasePriority(const NamedDecl *ND) { |
| if (!ND) |
| return CCP_Unlikely; |
| |
| // Context-based decisions. |
| const DeclContext *LexicalDC = ND->getLexicalDeclContext(); |
| if (LexicalDC->isFunctionOrMethod()) { |
| // _cmd is relatively rare |
| if (const ImplicitParamDecl *ImplicitParam = |
| dyn_cast<ImplicitParamDecl>(ND)) |
| if (ImplicitParam->getIdentifier() && |
| ImplicitParam->getIdentifier()->isStr("_cmd")) |
| return CCP_ObjC_cmd; |
| |
| return CCP_LocalDeclaration; |
| } |
| |
| const DeclContext *DC = ND->getDeclContext()->getRedeclContext(); |
| if (DC->isRecord() || isa<ObjCContainerDecl>(DC)) { |
| // Explicit destructor calls are very rare. |
| if (isa<CXXDestructorDecl>(ND)) |
| return CCP_Unlikely; |
| // Explicit operator and conversion function calls are also very rare. |
| auto DeclNameKind = ND->getDeclName().getNameKind(); |
| if (DeclNameKind == DeclarationName::CXXOperatorName || |
| DeclNameKind == DeclarationName::CXXLiteralOperatorName || |
| DeclNameKind == DeclarationName::CXXConversionFunctionName) |
| return CCP_Unlikely; |
| return CCP_MemberDeclaration; |
| } |
| |
| // Content-based decisions. |
| if (isa<EnumConstantDecl>(ND)) |
| return CCP_Constant; |
| |
| // Use CCP_Type for type declarations unless we're in a statement, Objective-C |
| // message receiver, or parenthesized expression context. There, it's as |
| // likely that the user will want to write a type as other declarations. |
| if ((isa<TypeDecl>(ND) || isa<ObjCInterfaceDecl>(ND)) && |
| !(CompletionContext.getKind() == CodeCompletionContext::CCC_Statement || |
| CompletionContext.getKind() |
| == CodeCompletionContext::CCC_ObjCMessageReceiver || |
| CompletionContext.getKind() |
| == CodeCompletionContext::CCC_ParenthesizedExpression)) |
| return CCP_Type; |
| |
| return CCP_Declaration; |
| } |
| |
| void ResultBuilder::AdjustResultPriorityForDecl(Result &R) { |
| // If this is an Objective-C method declaration whose selector matches our |
| // preferred selector, give it a priority boost. |
| if (!PreferredSelector.isNull()) |
| if (const ObjCMethodDecl *Method = dyn_cast<ObjCMethodDecl>(R.Declaration)) |
| if (PreferredSelector == Method->getSelector()) |
| R.Priority += CCD_SelectorMatch; |
| |
| // If we have a preferred type, adjust the priority for results with exactly- |
| // matching or nearly-matching types. |
| if (!PreferredType.isNull()) { |
| QualType T = getDeclUsageType(SemaRef.Context, R.Declaration); |
| if (!T.isNull()) { |
| CanQualType TC = SemaRef.Context.getCanonicalType(T); |
| // Check for exactly-matching types (modulo qualifiers). |
| if (SemaRef.Context.hasSameUnqualifiedType(PreferredType, TC)) |
| R.Priority /= CCF_ExactTypeMatch; |
| // Check for nearly-matching types, based on classification of each. |
| else if ((getSimplifiedTypeClass(PreferredType) |
| == getSimplifiedTypeClass(TC)) && |
| !(PreferredType->isEnumeralType() && TC->isEnumeralType())) |
| R.Priority /= CCF_SimilarTypeMatch; |
| } |
| } |
| } |
| |
| void ResultBuilder::MaybeAddConstructorResults(Result R) { |
| if (!SemaRef.getLangOpts().CPlusPlus || !R.Declaration || |
| !CompletionContext.wantConstructorResults()) |
| return; |
| |
| ASTContext &Context = SemaRef.Context; |
| const NamedDecl *D = R.Declaration; |
| const CXXRecordDecl *Record = nullptr; |
| if (const ClassTemplateDecl *ClassTemplate = dyn_cast<ClassTemplateDecl>(D)) |
| Record = ClassTemplate->getTemplatedDecl(); |
| else if ((Record = dyn_cast<CXXRecordDecl>(D))) { |
| // Skip specializations and partial specializations. |
| if (isa<ClassTemplateSpecializationDecl>(Record)) |
| return; |
| } else { |
| // There are no constructors here. |
| return; |
| } |
| |
| Record = Record->getDefinition(); |
| if (!Record) |
| return; |
| |
| |
| QualType RecordTy = Context.getTypeDeclType(Record); |
| DeclarationName ConstructorName |
| = Context.DeclarationNames.getCXXConstructorName( |
| Context.getCanonicalType(RecordTy)); |
| DeclContext::lookup_result Ctors = Record->lookup(ConstructorName); |
| for (DeclContext::lookup_iterator I = Ctors.begin(), |
| E = Ctors.end(); |
| I != E; ++I) { |
| R.Declaration = *I; |
| R.CursorKind = getCursorKindForDecl(R.Declaration); |
| Results.push_back(R); |
| } |
| } |
| |
| void ResultBuilder::MaybeAddResult(Result R, DeclContext *CurContext) { |
| assert(!ShadowMaps.empty() && "Must enter into a results scope"); |
| |
| if (R.Kind != Result::RK_Declaration) { |
| // For non-declaration results, just add the result. |
| Results.push_back(R); |
| return; |
| } |
| |
| // Look through using declarations. |
| if (const UsingShadowDecl *Using = |
| dyn_cast<UsingShadowDecl>(R.Declaration)) { |
| MaybeAddResult(Result(Using->getTargetDecl(), |
| getBasePriority(Using->getTargetDecl()), |
| R.Qualifier), |
| CurContext); |
| return; |
| } |
| |
| const Decl *CanonDecl = R.Declaration->getCanonicalDecl(); |
| unsigned IDNS = CanonDecl->getIdentifierNamespace(); |
| |
| bool AsNestedNameSpecifier = false; |
| if (!isInterestingDecl(R.Declaration, AsNestedNameSpecifier)) |
| return; |
| |
| // C++ constructors are never found by name lookup. |
| if (isa<CXXConstructorDecl>(R.Declaration)) |
| return; |
| |
| ShadowMap &SMap = ShadowMaps.back(); |
| ShadowMapEntry::iterator I, IEnd; |
| ShadowMap::iterator NamePos = SMap.find(R.Declaration->getDeclName()); |
| if (NamePos != SMap.end()) { |
| I = NamePos->second.begin(); |
| IEnd = NamePos->second.end(); |
| } |
| |
| for (; I != IEnd; ++I) { |
| const NamedDecl *ND = I->first; |
| unsigned Index = I->second; |
| if (ND->getCanonicalDecl() == CanonDecl) { |
| // This is a redeclaration. Always pick the newer declaration. |
| Results[Index].Declaration = R.Declaration; |
| |
| // We're done. |
| return; |
| } |
| } |
| |
| // This is a new declaration in this scope. However, check whether this |
| // declaration name is hidden by a similarly-named declaration in an outer |
| // scope. |
| std::list<ShadowMap>::iterator SM, SMEnd = ShadowMaps.end(); |
| --SMEnd; |
| for (SM = ShadowMaps.begin(); SM != SMEnd; ++SM) { |
| ShadowMapEntry::iterator I, IEnd; |
| ShadowMap::iterator NamePos = SM->find(R.Declaration->getDeclName()); |
| if (NamePos != SM->end()) { |
| I = NamePos->second.begin(); |
| IEnd = NamePos->second.end(); |
| } |
| for (; I != IEnd; ++I) { |
| // A tag declaration does not hide a non-tag declaration. |
| if (I->first->hasTagIdentifierNamespace() && |
| (IDNS & (Decl::IDNS_Member | Decl::IDNS_Ordinary | |
| Decl::IDNS_LocalExtern | Decl::IDNS_ObjCProtocol))) |
| continue; |
| |
| // Protocols are in distinct namespaces from everything else. |
| if (((I->first->getIdentifierNamespace() & Decl::IDNS_ObjCProtocol) |
| || (IDNS & Decl::IDNS_ObjCProtocol)) && |
| I->first->getIdentifierNamespace() != IDNS) |
| continue; |
| |
| // The newly-added result is hidden by an entry in the shadow map. |
| if (CheckHiddenResult(R, CurContext, I->first)) |
| return; |
| |
| break; |
| } |
| } |
| |
| // Make sure that any given declaration only shows up in the result set once. |
| if (!AllDeclsFound.insert(CanonDecl).second) |
| return; |
| |
| // If the filter is for nested-name-specifiers, then this result starts a |
| // nested-name-specifier. |
| if (AsNestedNameSpecifier) { |
| R.StartsNestedNameSpecifier = true; |
| R.Priority = CCP_NestedNameSpecifier; |
| } else |
| AdjustResultPriorityForDecl(R); |
| |
| // If this result is supposed to have an informative qualifier, add one. |
| if (R.QualifierIsInformative && !R.Qualifier && |
| !R.StartsNestedNameSpecifier) { |
| const DeclContext *Ctx = R.Declaration->getDeclContext(); |
| if (const NamespaceDecl *Namespace = dyn_cast<NamespaceDecl>(Ctx)) |
| R.Qualifier = NestedNameSpecifier::Create(SemaRef.Context, nullptr, |
| Namespace); |
| else if (const TagDecl *Tag = dyn_cast<TagDecl>(Ctx)) |
| R.Qualifier = NestedNameSpecifier::Create(SemaRef.Context, nullptr, |
| false, SemaRef.Context.getTypeDeclType(Tag).getTypePtr()); |
| else |
| R.QualifierIsInformative = false; |
| } |
| |
| // Insert this result into the set of results and into the current shadow |
| // map. |
| SMap[R.Declaration->getDeclName()].Add(R.Declaration, Results.size()); |
| Results.push_back(R); |
| |
| if (!AsNestedNameSpecifier) |
| MaybeAddConstructorResults(R); |
| } |
| |
| void ResultBuilder::AddResult(Result R, DeclContext *CurContext, |
| NamedDecl *Hiding, bool InBaseClass = false) { |
| if (R.Kind != Result::RK_Declaration) { |
| // For non-declaration results, just add the result. |
| Results.push_back(R); |
| return; |
| } |
| |
| // Look through using declarations. |
| if (const UsingShadowDecl *Using = dyn_cast<UsingShadowDecl>(R.Declaration)) { |
| AddResult(Result(Using->getTargetDecl(), |
| getBasePriority(Using->getTargetDecl()), |
| R.Qualifier), |
| CurContext, Hiding); |
| return; |
| } |
| |
| bool AsNestedNameSpecifier = false; |
| if (!isInterestingDecl(R.Declaration, AsNestedNameSpecifier)) |
| return; |
| |
| // C++ constructors are never found by name lookup. |
| if (isa<CXXConstructorDecl>(R.Declaration)) |
| return; |
| |
| if (Hiding && CheckHiddenResult(R, CurContext, Hiding)) |
| return; |
| |
| // Make sure that any given declaration only shows up in the result set once. |
| if (!AllDeclsFound.insert(R.Declaration->getCanonicalDecl()).second) |
| return; |
| |
| // If the filter is for nested-name-specifiers, then this result starts a |
| // nested-name-specifier. |
| if (AsNestedNameSpecifier) { |
| R.StartsNestedNameSpecifier = true; |
| R.Priority = CCP_NestedNameSpecifier; |
| } |
| else if (Filter == &ResultBuilder::IsMember && !R.Qualifier && InBaseClass && |
| isa<CXXRecordDecl>(R.Declaration->getDeclContext() |
| ->getRedeclContext())) |
| R.QualifierIsInformative = true; |
| |
| // If this result is supposed to have an informative qualifier, add one. |
| if (R.QualifierIsInformative && !R.Qualifier && |
| !R.StartsNestedNameSpecifier) { |
| const DeclContext *Ctx = R.Declaration->getDeclContext(); |
| if (const NamespaceDecl *Namespace = dyn_cast<NamespaceDecl>(Ctx)) |
| R.Qualifier = NestedNameSpecifier::Create(SemaRef.Context, nullptr, |
| Namespace); |
| else if (const TagDecl *Tag = dyn_cast<TagDecl>(Ctx)) |
| R.Qualifier = NestedNameSpecifier::Create(SemaRef.Context, nullptr, false, |
| SemaRef.Context.getTypeDeclType(Tag).getTypePtr()); |
| else |
| R.QualifierIsInformative = false; |
| } |
| |
| // Adjust the priority if this result comes from a base class. |
| if (InBaseClass) |
| R.Priority += CCD_InBaseClass; |
| |
| AdjustResultPriorityForDecl(R); |
| |
| if (HasObjectTypeQualifiers) |
| if (const CXXMethodDecl *Method = dyn_cast<CXXMethodDecl>(R.Declaration)) |
| if (Method->isInstance()) { |
| Qualifiers MethodQuals |
| = Qualifiers::fromCVRMask(Method->getTypeQualifiers()); |
| if (ObjectTypeQualifiers == MethodQuals) |
| R.Priority += CCD_ObjectQualifierMatch; |
| else if (ObjectTypeQualifiers - MethodQuals) { |
| // The method cannot be invoked, because doing so would drop |
| // qualifiers. |
| return; |
| } |
| } |
| |
| // Insert this result into the set of results. |
| Results.push_back(R); |
| |
| if (!AsNestedNameSpecifier) |
| MaybeAddConstructorResults(R); |
| } |
| |
| void ResultBuilder::AddResult(Result R) { |
| assert(R.Kind != Result::RK_Declaration && |
| "Declaration results need more context"); |
| Results.push_back(R); |
| } |
| |
| /// \brief Enter into a new scope. |
| void ResultBuilder::EnterNewScope() { ShadowMaps.emplace_back(); } |
| |
| /// \brief Exit from the current scope. |
| void ResultBuilder::ExitScope() { |
| for (ShadowMap::iterator E = ShadowMaps.back().begin(), |
| EEnd = ShadowMaps.back().end(); |
| E != EEnd; |
| ++E) |
| E->second.Destroy(); |
| |
| ShadowMaps.pop_back(); |
| } |
| |
| /// \brief Determines whether this given declaration will be found by |
| /// ordinary name lookup. |
| bool ResultBuilder::IsOrdinaryName(const NamedDecl *ND) const { |
| ND = cast<NamedDecl>(ND->getUnderlyingDecl()); |
| |
| // If name lookup finds a local extern declaration, then we are in a |
| // context where it behaves like an ordinary name. |
| unsigned IDNS = Decl::IDNS_Ordinary | Decl::IDNS_LocalExtern; |
| if (SemaRef.getLangOpts().CPlusPlus) |
| IDNS |= Decl::IDNS_Tag | Decl::IDNS_Namespace | Decl::IDNS_Member; |
| else if (SemaRef.getLangOpts().ObjC1) { |
| if (isa<ObjCIvarDecl>(ND)) |
| return true; |
| } |
| |
| return ND->getIdentifierNamespace() & IDNS; |
| } |
| |
| /// \brief Determines whether this given declaration will be found by |
| /// ordinary name lookup but is not a type name. |
| bool ResultBuilder::IsOrdinaryNonTypeName(const NamedDecl *ND) const { |
| ND = cast<NamedDecl>(ND->getUnderlyingDecl()); |
| if (isa<TypeDecl>(ND)) |
| return false; |
| // Objective-C interfaces names are not filtered by this method because they |
| // can be used in a class property expression. We can still filter out |
| // @class declarations though. |
| if (const auto *ID = dyn_cast<ObjCInterfaceDecl>(ND)) { |
| if (!ID->getDefinition()) |
| return false; |
| } |
| |
| unsigned IDNS = Decl::IDNS_Ordinary | Decl::IDNS_LocalExtern; |
| if (SemaRef.getLangOpts().CPlusPlus) |
| IDNS |= Decl::IDNS_Tag | Decl::IDNS_Namespace | Decl::IDNS_Member; |
| else if (SemaRef.getLangOpts().ObjC1) { |
| if (isa<ObjCIvarDecl>(ND)) |
| return true; |
| } |
| |
| return ND->getIdentifierNamespace() & IDNS; |
| } |
| |
| bool ResultBuilder::IsIntegralConstantValue(const NamedDecl *ND) const { |
| if (!IsOrdinaryNonTypeName(ND)) |
| return 0; |
| |
| if (const ValueDecl *VD = dyn_cast<ValueDecl>(ND->getUnderlyingDecl())) |
| if (VD->getType()->isIntegralOrEnumerationType()) |
| return true; |
| |
| return false; |
| } |
| |
| /// \brief Determines whether this given declaration will be found by |
| /// ordinary name lookup. |
| bool ResultBuilder::IsOrdinaryNonValueName(const NamedDecl *ND) const { |
| ND = cast<NamedDecl>(ND->getUnderlyingDecl()); |
| |
| unsigned IDNS = Decl::IDNS_Ordinary | Decl::IDNS_LocalExtern; |
| if (SemaRef.getLangOpts().CPlusPlus) |
| IDNS |= Decl::IDNS_Tag | Decl::IDNS_Namespace; |
| |
| return (ND->getIdentifierNamespace() & IDNS) && |
| !isa<ValueDecl>(ND) && !isa<FunctionTemplateDecl>(ND) && |
| !isa<ObjCPropertyDecl>(ND); |
| } |
| |
| /// \brief Determines whether the given declaration is suitable as the |
| /// start of a C++ nested-name-specifier, e.g., a class or namespace. |
| bool ResultBuilder::IsNestedNameSpecifier(const NamedDecl *ND) const { |
| // Allow us to find class templates, too. |
| if (const ClassTemplateDecl *ClassTemplate = dyn_cast<ClassTemplateDecl>(ND)) |
| ND = ClassTemplate->getTemplatedDecl(); |
| |
| return SemaRef.isAcceptableNestedNameSpecifier(ND); |
| } |
| |
| /// \brief Determines whether the given declaration is an enumeration. |
| bool ResultBuilder::IsEnum(const NamedDecl *ND) const { |
| return isa<EnumDecl>(ND); |
| } |
| |
| /// \brief Determines whether the given declaration is a class or struct. |
| bool ResultBuilder::IsClassOrStruct(const NamedDecl *ND) const { |
| // Allow us to find class templates, too. |
| if (const ClassTemplateDecl *ClassTemplate = dyn_cast<ClassTemplateDecl>(ND)) |
| ND = ClassTemplate->getTemplatedDecl(); |
| |
| // For purposes of this check, interfaces match too. |
| if (const RecordDecl *RD = dyn_cast<RecordDecl>(ND)) |
| return RD->getTagKind() == TTK_Class || |
| RD->getTagKind() == TTK_Struct || |
| RD->getTagKind() == TTK_Interface; |
| |
| return false; |
| } |
| |
| /// \brief Determines whether the given declaration is a union. |
| bool ResultBuilder::IsUnion(const NamedDecl *ND) const { |
| // Allow us to find class templates, too. |
| if (const ClassTemplateDecl *ClassTemplate = dyn_cast<ClassTemplateDecl>(ND)) |
| ND = ClassTemplate->getTemplatedDecl(); |
| |
| if (const RecordDecl *RD = dyn_cast<RecordDecl>(ND)) |
| return RD->getTagKind() == TTK_Union; |
| |
| return false; |
| } |
| |
| /// \brief Determines whether the given declaration is a namespace. |
| bool ResultBuilder::IsNamespace(const NamedDecl *ND) const { |
| return isa<NamespaceDecl>(ND); |
| } |
| |
| /// \brief Determines whether the given declaration is a namespace or |
| /// namespace alias. |
| bool ResultBuilder::IsNamespaceOrAlias(const NamedDecl *ND) const { |
| return isa<NamespaceDecl>(ND->getUnderlyingDecl()); |
| } |
| |
| /// \brief Determines whether the given declaration is a type. |
| bool ResultBuilder::IsType(const NamedDecl *ND) const { |
| ND = ND->getUnderlyingDecl(); |
| return isa<TypeDecl>(ND) || isa<ObjCInterfaceDecl>(ND); |
| } |
| |
| /// \brief Determines which members of a class should be visible via |
| /// "." or "->". Only value declarations, nested name specifiers, and |
| /// using declarations thereof should show up. |
| bool ResultBuilder::IsMember(const NamedDecl *ND) const { |
| ND = ND->getUnderlyingDecl(); |
| return isa<ValueDecl>(ND) || isa<FunctionTemplateDecl>(ND) || |
| isa<ObjCPropertyDecl>(ND); |
| } |
| |
| static bool isObjCReceiverType(ASTContext &C, QualType T) { |
| T = C.getCanonicalType(T); |
| switch (T->getTypeClass()) { |
| case Type::ObjCObject: |
| case Type::ObjCInterface: |
| case Type::ObjCObjectPointer: |
| return true; |
| |
| case Type::Builtin: |
| switch (cast<BuiltinType>(T)->getKind()) { |
| case BuiltinType::ObjCId: |
| case BuiltinType::ObjCClass: |
| case BuiltinType::ObjCSel: |
| return true; |
| |
| default: |
| break; |
| } |
| return false; |
| |
| default: |
| break; |
| } |
| |
| if (!C.getLangOpts().CPlusPlus) |
| return false; |
| |
| // FIXME: We could perform more analysis here to determine whether a |
| // particular class type has any conversions to Objective-C types. For now, |
| // just accept all class types. |
| return T->isDependentType() || T->isRecordType(); |
| } |
| |
| bool ResultBuilder::IsObjCMessageReceiver(const NamedDecl *ND) const { |
| QualType T = getDeclUsageType(SemaRef.Context, ND); |
| if (T.isNull()) |
| return false; |
| |
| T = SemaRef.Context.getBaseElementType(T); |
| return isObjCReceiverType(SemaRef.Context, T); |
| } |
| |
| bool ResultBuilder::IsObjCMessageReceiverOrLambdaCapture(const NamedDecl *ND) const { |
| if (IsObjCMessageReceiver(ND)) |
| return true; |
| |
| const VarDecl *Var = dyn_cast<VarDecl>(ND); |
| if (!Var) |
| return false; |
| |
| return Var->hasLocalStorage() && !Var->hasAttr<BlocksAttr>(); |
| } |
| |
| bool ResultBuilder::IsObjCCollection(const NamedDecl *ND) const { |
| if ((SemaRef.getLangOpts().CPlusPlus && !IsOrdinaryName(ND)) || |
| (!SemaRef.getLangOpts().CPlusPlus && !IsOrdinaryNonTypeName(ND))) |
| return false; |
| |
| QualType T = getDeclUsageType(SemaRef.Context, ND); |
| if (T.isNull()) |
| return false; |
| |
| T = SemaRef.Context.getBaseElementType(T); |
| return T->isObjCObjectType() || T->isObjCObjectPointerType() || |
| T->isObjCIdType() || |
| (SemaRef.getLangOpts().CPlusPlus && T->isRecordType()); |
| } |
| |
| bool ResultBuilder::IsImpossibleToSatisfy(const NamedDecl *ND) const { |
| return false; |
| } |
| |
| /// \brief Determines whether the given declaration is an Objective-C |
| /// instance variable. |
| bool ResultBuilder::IsObjCIvar(const NamedDecl *ND) const { |
| return isa<ObjCIvarDecl>(ND); |
| } |
| |
| namespace { |
| /// \brief Visible declaration consumer that adds a code-completion result |
| /// for each visible declaration. |
| class CodeCompletionDeclConsumer : public VisibleDeclConsumer { |
| ResultBuilder &Results; |
| DeclContext *CurContext; |
| |
| public: |
| CodeCompletionDeclConsumer(ResultBuilder &Results, DeclContext *CurContext) |
| : Results(Results), CurContext(CurContext) { } |
| |
| void FoundDecl(NamedDecl *ND, NamedDecl *Hiding, DeclContext *Ctx, |
| bool InBaseClass) override { |
| bool Accessible = true; |
| if (Ctx) |
| Accessible = Results.getSema().IsSimplyAccessible(ND, Ctx); |
| |
| ResultBuilder::Result Result(ND, Results.getBasePriority(ND), nullptr, |
| false, Accessible); |
| Results.AddResult(Result, CurContext, Hiding, InBaseClass); |
| } |
| }; |
| } |
| |
| /// \brief Add type specifiers for the current language as keyword results. |
| static void AddTypeSpecifierResults(const LangOptions &LangOpts, |
| ResultBuilder &Results) { |
| typedef CodeCompletionResult Result; |
| Results.AddResult(Result("short", CCP_Type)); |
| Results.AddResult(Result("long", CCP_Type)); |
| Results.AddResult(Result("signed", CCP_Type)); |
| Results.AddResult(Result("unsigned", CCP_Type)); |
| Results.AddResult(Result("void", CCP_Type)); |
| Results.AddResult(Result("char", CCP_Type)); |
| Results.AddResult(Result("int", CCP_Type)); |
| Results.AddResult(Result("float", CCP_Type)); |
| Results.AddResult(Result("double", CCP_Type)); |
| Results.AddResult(Result("enum", CCP_Type)); |
| Results.AddResult(Result("struct", CCP_Type)); |
| Results.AddResult(Result("union", CCP_Type)); |
| Results.AddResult(Result("const", CCP_Type)); |
| Results.AddResult(Result("volatile", CCP_Type)); |
| |
| if (LangOpts.C99) { |
| // C99-specific |
| Results.AddResult(Result("_Complex", CCP_Type)); |
| Results.AddResult(Result("_Imaginary", CCP_Type)); |
| Results.AddResult(Result("_Bool", CCP_Type)); |
| Results.AddResult(Result("restrict", CCP_Type)); |
| } |
| |
| CodeCompletionBuilder Builder(Results.getAllocator(), |
| Results.getCodeCompletionTUInfo()); |
| if (LangOpts.CPlusPlus) { |
| // C++-specific |
| Results.AddResult(Result("bool", CCP_Type + |
| (LangOpts.ObjC1? CCD_bool_in_ObjC : 0))); |
| Results.AddResult(Result("class", CCP_Type)); |
| Results.AddResult(Result("wchar_t", CCP_Type)); |
| |
| // typename qualified-id |
| Builder.AddTypedTextChunk("typename"); |
| Builder.AddChunk(CodeCompletionString::CK_HorizontalSpace); |
| Builder.AddPlaceholderChunk("qualifier"); |
| Builder.AddTextChunk("::"); |
| Builder.AddPlaceholderChunk("name"); |
| Results.AddResult(Result(Builder.TakeString())); |
| |
| if (LangOpts.CPlusPlus11) { |
| Results.AddResult(Result("auto", CCP_Type)); |
| Results.AddResult(Result("char16_t", CCP_Type)); |
| Results.AddResult(Result("char32_t", CCP_Type)); |
| |
| Builder.AddTypedTextChunk("decltype"); |
| Builder.AddChunk(CodeCompletionString::CK_LeftParen); |
| Builder.AddPlaceholderChunk("expression"); |
| Builder.AddChunk(CodeCompletionString::CK_RightParen); |
| Results.AddResult(Result(Builder.TakeString())); |
| } |
| } else |
| Results.AddResult(Result("__auto_type", CCP_Type)); |
| |
| // GNU extensions |
| if (LangOpts.GNUMode) { |
| // FIXME: Enable when we actually support decimal floating point. |
| // Results.AddResult(Result("_Decimal32")); |
| // Results.AddResult(Result("_Decimal64")); |
| // Results.AddResult(Result("_Decimal128")); |
| |
| Builder.AddTypedTextChunk("typeof"); |
| Builder.AddChunk(CodeCompletionString::CK_HorizontalSpace); |
| Builder.AddPlaceholderChunk("expression"); |
| Results.AddResult(Result(Builder.TakeString())); |
| |
| Builder.AddTypedTextChunk("typeof"); |
| Builder.AddChunk(CodeCompletionString::CK_LeftParen); |
| Builder.AddPlaceholderChunk("type"); |
| Builder.AddChunk(CodeCompletionString::CK_RightParen); |
| Results.AddResult(Result(Builder.TakeString())); |
| } |
| |
| // Nullability |
| Results.AddResult(Result("_Nonnull", CCP_Type)); |
| Results.AddResult(Result("_Null_unspecified", CCP_Type)); |
| Results.AddResult(Result("_Nullable", CCP_Type)); |
| } |
| |
| static void AddStorageSpecifiers(Sema::ParserCompletionContext CCC, |
| const LangOptions &LangOpts, |
| ResultBuilder &Results) { |
| typedef CodeCompletionResult Result; |
| // Note: we don't suggest either "auto" or "register", because both |
| // are pointless as storage specifiers. Elsewhere, we suggest "auto" |
| // 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, |
| const LangOptions &LangOpts, |
| ResultBuilder &Results) { |
| typedef CodeCompletionResult Result; |
| switch (CCC) { |
| case Sema::PCC_Class: |
| case Sema::PCC_MemberTemplate: |
| if (LangOpts.CPlusPlus) { |
| Results.AddResult(Result("explicit")); |
| Results.AddResult(Result("friend")); |
| Results.AddResult(Result("mutable")); |
| Results.AddResult(Result("virtual")); |
| } |
| LLVM_FALLTHROUGH; |
| |
| case Sema::PCC_ObjCInterface: |
| case Sema::PCC_ObjCImplementation: |
| case Sema::PCC_Namespace: |
| case Sema::PCC_Template: |
| if (LangOpts.CPlusPlus || LangOpts.C99) |
| Results.AddResult(Result("inline")); |
| break; |
| |
| case Sema::PCC_ObjCInstanceVariableList: |
| case Sema::PCC_Expression: |
| case Sema::PCC_Statement: |
| case Sema::PCC_ForInit: |
| case Sema::PCC_Condition: |
| case Sema::PCC_RecoveryInFunction: |
| case Sema::PCC_Type: |
| case Sema::PCC_ParenthesizedExpression: |
| case Sema::PCC_LocalDeclarationSpecifiers: |
| break; |
| } |
| } |
| |
| static void AddObjCExpressionResults(ResultBuilder &Results, bool NeedAt); |
| static void AddObjCStatementResults(ResultBuilder &Results, bool NeedAt); |
| static void AddObjCVisibilityResults(const LangOptions &LangOpts, |
| ResultBuilder &Results, |
| bool NeedAt); |
| static void AddObjCImplementationResults(const LangOptions &LangOpts, |
| ResultBuilder &Results, |
| bool NeedAt); |
| static void AddObjCInterfaceResults(const LangOptions &LangOpts, |
| ResultBuilder &Results, |
| bool NeedAt); |
| static void AddObjCTopLevelResults(ResultBuilder &Results, bool NeedAt); |
| |
| static void AddTypedefResult(ResultBuilder &Results) { |
| CodeCompletionBuilder Builder(Results.getAllocator(), |
| Results.getCodeCompletionTUInfo()); |
| Builder.AddTypedTextChunk("typedef"); |
| Builder.AddChunk(CodeCompletionString::CK_HorizontalSpace); |
| Builder.AddPlaceholderChunk("type"); |
| Builder.AddChunk(CodeCompletionString::CK_HorizontalSpace); |
| Builder.AddPlaceholderChunk("name"); |
| Results.AddResult(CodeCompletionResult(Builder.TakeString())); |
| } |
| |
| static bool WantTypesInContext(Sema::ParserCompletionContext CCC, |
| const LangOptions &LangOpts) { |
| switch (CCC) { |
| case Sema::PCC_Namespace: |
| case Sema::PCC_Class: |
| case Sema::PCC_ObjCInstanceVariableList: |
| case Sema::PCC_Template: |
| case Sema::PCC_MemberTemplate: |
| case Sema::PCC_Statement: |
| case Sema::PCC_RecoveryInFunction: |
| case Sema::PCC_Type: |
| case Sema::PCC_ParenthesizedExpression: |
| case Sema::PCC_LocalDeclarationSpecifiers: |
| return true; |
| |
| case Sema::PCC_Expression: |
| case Sema::PCC_Condition: |
| return LangOpts.CPlusPlus; |
| |
| case Sema::PCC_ObjCInterface: |
| case Sema::PCC_ObjCImplementation: |
| return false; |
| |
| case Sema::PCC_ForInit: |
| return LangOpts.CPlusPlus || LangOpts.ObjC1 || LangOpts.C99; |
| } |
| |
| llvm_unreachable("Invalid ParserCompletionContext!"); |
| } |
| |
| static PrintingPolicy getCompletionPrintingPolicy(const ASTContext &Context, |
| const Preprocessor &PP) { |
| PrintingPolicy Policy = Sema::getPrintingPolicy(Context, PP); |
| Policy.AnonymousTagLocations = false; |
| Policy.SuppressStrongLifetime = true; |
| Policy.SuppressUnwrittenScope = true; |
| Policy.SuppressScope = true; |
| return Policy; |
| } |
| |
| /// \brief Retrieve a printing policy suitable for code completion. |
| static PrintingPolicy getCompletionPrintingPolicy(Sema &S) { |
| return getCompletionPrintingPolicy(S.Context, S.PP); |
| } |
| |
| /// \brief Retrieve the string representation of the given type as a string |
| /// that has the appropriate lifetime for code completion. |
| /// |
| /// This routine provides a fast path where we provide constant strings for |
| /// common type names. |
| static const char *GetCompletionTypeString(QualType T, |
| ASTContext &Context, |
| const PrintingPolicy &Policy, |
| CodeCompletionAllocator &Allocator) { |
| if (!T.getLocalQualifiers()) { |
| // Built-in type names are constant strings. |
| if (const BuiltinType *BT = dyn_cast<BuiltinType>(T)) |
| return BT->getNameAsCString(Policy); |
| |
| // Anonymous tag types are constant strings. |
| if (const TagType *TagT = dyn_cast<TagType>(T)) |
| if (TagDecl *Tag = TagT->getDecl()) |
| if (!Tag->hasNameForLinkage()) { |
| switch (Tag->getTagKind()) { |
| case TTK_Struct: return "struct <anonymous>"; |
| case TTK_Interface: return "__interface <anonymous>"; |
| case TTK_Class: return "class <anonymous>"; |
| case TTK_Union: return "union <anonymous>"; |
| case TTK_Enum: return "enum <anonymous>"; |
| } |
| } |
| } |
| |
| // Slow path: format the type as a string. |
| std::string Result; |
| T.getAsStringInternal(Result, Policy); |
| return Allocator.CopyString(Result); |
| } |
| |
| /// \brief Add a completion for "this", if we're in a member function. |
| static void addThisCompletion(Sema &S, ResultBuilder &Results) { |
| QualType ThisTy = S.getCurrentThisType(); |
| if (ThisTy.isNull()) |
| return; |
| |
| CodeCompletionAllocator &Allocator = Results.getAllocator(); |
| CodeCompletionBuilder Builder(Allocator, Results.getCodeCompletionTUInfo()); |
| PrintingPolicy Policy = getCompletionPrintingPolicy(S); |
| Builder.AddResultTypeChunk(GetCompletionTypeString(ThisTy, |
| S.Context, |
| Policy, |
| Allocator)); |
| Builder.AddTypedTextChunk("this"); |
| 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, |
| Sema &SemaRef, |
| ResultBuilder &Results) { |
| CodeCompletionAllocator &Allocator = Results.getAllocator(); |
| CodeCompletionBuilder Builder(Allocator, Results.getCodeCompletionTUInfo()); |
| |
| typedef CodeCompletionResult Result; |
| switch (CCC) { |
| case Sema::PCC_Namespace: |
| if (SemaRef.getLangOpts().CPlusPlus) { |
| if (Results.includeCodePatterns()) { |
| // namespace <identifier> { declarations } |
| Builder.AddTypedTextChunk("namespace"); |
| Builder.AddChunk(CodeCompletionString::CK_HorizontalSpace); |
| Builder.AddPlaceholderChunk("identifier"); |
| Builder.AddChunk(CodeCompletionString::CK_LeftBrace); |
| Builder.AddPlaceholderChunk("declarations"); |
| Builder.AddChunk(CodeCompletionString::CK_VerticalSpace); |
| Builder.AddChunk(CodeCompletionString::CK_RightBrace); |
| Results.AddResult(Result(Builder.TakeString())); |
| } |
| |
| // namespace identifier = identifier ; |
| Builder.AddTypedTextChunk("namespace"); |
| Builder.AddChunk(CodeCompletionString::CK_HorizontalSpace); |
| Builder.AddPlaceholderChunk("name"); |
| Builder.AddChunk(CodeCompletionString::CK_Equal); |
| Builder.AddPlaceholderChunk("namespace"); |
| Results.AddResult(Result(Builder.TakeString())); |
| |
| // Using directives |
| Builder.AddTypedTextChunk("using"); |
| Builder.AddChunk(CodeCompletionString::CK_HorizontalSpace); |
| Builder.AddTextChunk("namespace"); |
| Builder.AddChunk(CodeCompletionString::CK_HorizontalSpace); |
| Builder.AddPlaceholderChunk("identifier"); |
| Results.AddResult(Result(Builder.TakeString())); |
| |
| // asm(string-literal) |
| Builder.AddTypedTextChunk("asm"); |
| Builder.AddChunk(CodeCompletionString::CK_LeftParen); |
| Builder.AddPlaceholderChunk("string-literal"); |
| Builder.AddChunk(CodeCompletionString::CK_RightParen); |
| Results.AddResult(Result(Builder.TakeString())); |
| |
| if (Results.includeCodePatterns()) { |
| // Explicit template instantiation |
| Builder.AddTypedTextChunk("template"); |
| Builder.AddChunk(CodeCompletionString::CK_HorizontalSpace); |
| Builder.AddPlaceholderChunk("declaration"); |
| Results.AddResult(Result(Builder.TakeString())); |
| } |
| } |
| |
| if (SemaRef.getLangOpts().ObjC1) |
| AddObjCTopLevelResults(Results, true); |
| |
| AddTypedefResult(Results); |
| LLVM_FALLTHROUGH; |
| |
| case Sema::PCC_Class: |
| if (SemaRef.getLangOpts().CPlusPlus) { |
| // Using declaration |
| Builder.AddTypedTextChunk("using"); |
| Builder.AddChunk(CodeCompletionString::CK_HorizontalSpace); |
| Builder.AddPlaceholderChunk("qualifier"); |
| Builder.AddTextChunk("::"); |
| Builder.AddPlaceholderChunk("name"); |
| Results.AddResult(Result(Builder.TakeString())); |
| |
| // using typename qualifier::name (only in a dependent context) |
| if (SemaRef.CurContext->isDependentContext()) { |
| Builder.AddTypedTextChunk("using"); |
| Builder.AddChunk(CodeCompletionString::CK_HorizontalSpace); |
| Builder.AddTextChunk("typename"); |
| Builder.AddChunk(CodeCompletionString::CK_HorizontalSpace); |
| Builder.AddPlaceholderChunk("qualifier"); |
| Builder.AddTextChunk("::"); |
| Builder.AddPlaceholderChunk("name"); |
| Results.AddResult(Result(Builder.TakeString())); |
| } |
| |
| AddStaticAssertResult(Builder, Results, SemaRef.getLangOpts()); |
| |
| if (CCC == Sema::PCC_Class) { |
| AddTypedefResult(Results); |
| |
| bool IsNotInheritanceScope = |
| !(S->getFlags() & Scope::ClassInheritanceScope); |
| // public: |
| Builder.AddTypedTextChunk("public"); |
| if (IsNotInheritanceScope && Results.includeCodePatterns()) |
| Builder.AddChunk(CodeCompletionString::CK_Colon); |
| Results.AddResult(Result(Builder.TakeString())); |
| |
| // protected: |
| Builder.AddTypedTextChunk("protected"); |
| if (IsNotInheritanceScope && Results.includeCodePatterns()) |
| Builder.AddChunk(CodeCompletionString::CK_Colon); |
| Results.AddResult(Result(Builder.TakeString())); |
| |
| // private: |
| Builder.AddTypedTextChunk("private"); |
| if (IsNotInheritanceScope && Results.includeCodePatterns()) |
| Builder.AddChunk(CodeCompletionString::CK_Colon); |
| Results.AddResult(Result(Builder.TakeString())); |
| } |
| } |
| LLVM_FALLTHROUGH; |
| |
| case Sema::PCC_Template: |
| case Sema::PCC_MemberTemplate: |
| if (SemaRef.getLangOpts().CPlusPlus && Results.includeCodePatterns()) { |
| // template < parameters > |
| Builder.AddTypedTextChunk("template"); |
| Builder.AddChunk(CodeCompletionString::CK_LeftAngle); |
| Builder.AddPlaceholderChunk("parameters"); |
| Builder.AddChunk(CodeCompletionString::CK_RightAngle); |
| Results.AddResult(Result(Builder.TakeString())); |
| } |
| |
| AddStorageSpecifiers(CCC, SemaRef.getLangOpts(), Results); |
| AddFunctionSpecifiers(CCC, SemaRef.getLangOpts(), Results); |
| break; |
| |
| case Sema::PCC_ObjCInterface: |
| AddObjCInterfaceResults(SemaRef.getLangOpts(), Results, true); |
| AddStorageSpecifiers(CCC, SemaRef.getLangOpts(), Results); |
| AddFunctionSpecifiers(CCC, SemaRef.getLangOpts(), Results); |
| break; |
| |
| case Sema::PCC_ObjCImplementation: |
| AddObjCImplementationResults(SemaRef.getLangOpts(), Results, true); |
| AddStorageSpecifiers(CCC, SemaRef.getLangOpts(), Results); |
| AddFunctionSpecifiers(CCC, SemaRef.getLangOpts(), Results); |
| break; |
| |
| case Sema::PCC_ObjCInstanceVariableList: |
| AddObjCVisibilityResults(SemaRef.getLangOpts(), Results, true); |
| break; |
| |
| case Sema::PCC_RecoveryInFunction: |
| case Sema::PCC_Statement: { |
| AddTypedefResult(Results); |
| |
| if (SemaRef.getLangOpts().CPlusPlus && Results.includeCodePatterns() && |
| SemaRef.getLangOpts().CXXExceptions) { |
| Builder.AddTypedTextChunk("try"); |
| Builder.AddChunk(CodeCompletionString::CK_LeftBrace); |
| Builder.AddPlaceholderChunk("statements"); |
| Builder.AddChunk(CodeCompletionString::CK_VerticalSpace); |
| Builder.AddChunk(CodeCompletionString::CK_RightBrace); |
| Builder.AddTextChunk("catch"); |
| Builder.AddChunk(CodeCompletionString::CK_LeftParen); |
| Builder.AddPlaceholderChunk("declaration"); |
| Builder.AddChunk(CodeCompletionString::CK_RightParen); |
| Builder.AddChunk(CodeCompletionString::CK_LeftBrace); |
| Builder.AddPlaceholderChunk("statements"); |
| Builder.AddChunk(CodeCompletionString::CK_VerticalSpace); |
| Builder.AddChunk(CodeCompletionString::CK_RightBrace); |
| Results.AddResult(Result(Builder.TakeString())); |
| } |
| if (SemaRef.getLangOpts().ObjC1) |
| AddObjCStatementResults(Results, true); |
| |
| if (Results.includeCodePatterns()) { |
| // if (condition) { statements } |
| Builder.AddTypedTextChunk("if"); |
| Builder.AddChunk(CodeCompletionString::CK_LeftParen); |
| if (SemaRef.getLangOpts().CPlusPlus) |
| Builder.AddPlaceholderChunk("condition"); |
| else |
| Builder.AddPlaceholderChunk("expression"); |
| Builder.AddChunk(CodeCompletionString::CK_RightParen); |
| Builder.AddChunk(CodeCompletionString::CK_LeftBrace); |
| Builder.AddPlaceholderChunk("statements"); |
| Builder.AddChunk(CodeCompletionString::CK_VerticalSpace); |
| Builder.AddChunk(CodeCompletionString::CK_RightBrace); |
| Results.AddResult(Result(Builder.TakeString())); |
| |
| // switch (condition) { } |
| Builder.AddTypedTextChunk("switch"); |
| Builder.AddChunk(CodeCompletionString::CK_LeftParen); |
| if (SemaRef.getLangOpts().CPlusPlus) |
| Builder.AddPlaceholderChunk("condition"); |
| else |
| Builder.AddPlaceholderChunk("expression"); |
| Builder.AddChunk(CodeCompletionString::CK_RightParen); |
| Builder.AddChunk(CodeCompletionString::CK_LeftBrace); |
| Builder.AddChunk(CodeCompletionString::CK_VerticalSpace); |
| Builder.AddChunk(CodeCompletionString::CK_RightBrace); |
| Results.AddResult(Result(Builder.TakeString())); |
| } |
| |
| // Switch-specific statements. |
| if (!SemaRef.getCurFunction()->SwitchStack.empty()) { |
| // case expression: |
| Builder.AddTypedTextChunk("case"); |
| Builder.AddChunk(CodeCompletionString::CK_HorizontalSpace); |
| Builder.AddPlaceholderChunk("expression"); |
| Builder.AddChunk(CodeCompletionString::CK_Colon); |
| Results.AddResult(Result(Builder.TakeString())); |
| |
| // default: |
| Builder.AddTypedTextChunk("default"); |
| Builder.AddChunk(CodeCompletionString::CK_Colon); |
| Results.AddResult(Result(Builder.TakeString())); |
| } |
| |
| if (Results.includeCodePatterns()) { |
| /// while (condition) { statements } |
| Builder.AddTypedTextChunk("while"); |
| Builder.AddChunk(CodeCompletionString::CK_LeftParen); |
| if (SemaRef.getLangOpts().CPlusPlus) |
| Builder.AddPlaceholderChunk("condition"); |
| else |
| Builder.AddPlaceholderChunk("expression"); |
| Builder.AddChunk(CodeCompletionString::CK_RightParen); |
| Builder.AddChunk(CodeCompletionString::CK_LeftBrace); |
| Builder.AddPlaceholderChunk("statements"); |
| Builder.AddChunk(CodeCompletionString::CK_VerticalSpace); |
| Builder.AddChunk(CodeCompletionString::CK_RightBrace); |
| Results.AddResult(Result(Builder.TakeString())); |
| |
| // do { statements } while ( expression ); |
| Builder.AddTypedTextChunk("do"); |
| Builder.AddChunk(CodeCompletionString::CK_LeftBrace); |
| Builder.AddPlaceholderChunk("statements"); |
| Builder.AddChunk(CodeCompletionString::CK_VerticalSpace); |
| Builder.AddChunk(CodeCompletionString::CK_RightBrace); |
| Builder.AddTextChunk("while"); |
| Builder.AddChunk(CodeCompletionString::CK_LeftParen); |
| Builder.AddPlaceholderChunk("expression"); |
| Builder.AddChunk(CodeCompletionString::CK_RightParen); |
| Results.AddResult(Result(Builder.TakeString())); |
| |
| // for ( for-init-statement ; condition ; expression ) { statements } |
| Builder.AddTypedTextChunk("for"); |
| Builder.AddChunk(CodeCompletionString::CK_LeftParen); |
| if (SemaRef.getLangOpts().CPlusPlus || SemaRef.getLangOpts().C99) |
| Builder.AddPlaceholderChunk("init-statement"); |
| else |
| Builder.AddPlaceholderChunk("init-expression"); |
| Builder.AddChunk(CodeCompletionString::CK_SemiColon); |
| Builder.AddPlaceholderChunk("condition"); |
| Builder.AddChunk(CodeCompletionString::CK_SemiColon); |
| Builder.AddPlaceholderChunk("inc-expression"); |
| Builder.AddChunk(CodeCompletionString::CK_RightParen); |
| Builder.AddChunk(CodeCompletionString::CK_LeftBrace); |
| Builder.AddChunk(CodeCompletionString::CK_VerticalSpace); |
| Builder.AddPlaceholderChunk("statements"); |
| Builder.AddChunk(CodeCompletionString::CK_VerticalSpace); |
| Builder.AddChunk(CodeCompletionString::CK_RightBrace); |
| Results.AddResult(Result(Builder.TakeString())); |
| } |
| |
| if (S->getContinueParent()) { |
| // continue ; |
| Builder.AddTypedTextChunk("continue"); |
| Results.AddResult(Result(Builder.TakeString())); |
| } |
| |
| if (S->getBreakParent()) { |
| // break ; |
| Builder.AddTypedTextChunk("break"); |
| Results.AddResult(Result(Builder.TakeString())); |
| } |
| |
| // "return expression ;" or "return ;", depending on whether we |
| // know the function is void or not. |
| bool isVoid = false; |
| if (FunctionDecl *Function = dyn_cast<FunctionDecl>(SemaRef.CurContext)) |
| isVoid = Function->getReturnType()->isVoidType(); |
| else if (ObjCMethodDecl *Method |
| = dyn_cast<ObjCMethodDecl>(SemaRef.CurContext)) |
| isVoid = Method->getReturnType()->isVoidType(); |
| else if (SemaRef.getCurBlock() && |
| !SemaRef.getCurBlock()->ReturnType.isNull()) |
| isVoid = SemaRef.getCurBlock()->ReturnType->isVoidType(); |
| Builder.AddTypedTextChunk("return"); |
| if (!isVoid) { |
| Builder.AddChunk(CodeCompletionString::CK_HorizontalSpace); |
| Builder.AddPlaceholderChunk("expression"); |
| } |
| Results.AddResult(Result(Builder.TakeString())); |
| |
| // goto identifier ; |
| Builder.AddTypedTextChunk("goto"); |
| Builder.AddChunk(CodeCompletionString::CK_HorizontalSpace); |
| Builder.AddPlaceholderChunk("label"); |
| Results.AddResult(Result(Builder.TakeString())); |
| |
| // Using directives |
| Builder.AddTypedTextChunk("using"); |
| Builder.AddChunk(CodeCompletionString::CK_HorizontalSpace); |
| Builder.AddTextChunk("namespace"); |
| Builder.AddChunk(CodeCompletionString::CK_HorizontalSpace); |
| Builder.AddPlaceholderChunk("identifier"); |
| Results.AddResult(Result(Builder.TakeString())); |
| |
| AddStaticAssertResult(Builder, Results, SemaRef.getLangOpts()); |
| } |
| LLVM_FALLTHROUGH; |
| |
| // Fall through (for statement expressions). |
| case Sema::PCC_ForInit: |
| case Sema::PCC_Condition: |
| AddStorageSpecifiers(CCC, SemaRef.getLangOpts(), Results); |
| // Fall through: conditions and statements can have expressions. |
| LLVM_FALLTHROUGH; |
| |
| case Sema::PCC_ParenthesizedExpression: |
| if (SemaRef.getLangOpts().ObjCAutoRefCount && |
| CCC == Sema::PCC_ParenthesizedExpression) { |
| // (__bridge <type>)<expression> |
| Builder.AddTypedTextChunk("__bridge"); |
| Builder.AddChunk(CodeCompletionString::CK_HorizontalSpace); |
| Builder.AddPlaceholderChunk("type"); |
| Builder.AddChunk(CodeCompletionString::CK_RightParen); |
| Builder.AddPlaceholderChunk("expression"); |
| Results.AddResult(Result(Builder.TakeString())); |
| |
| // (__bridge_transfer <Objective-C type>)<expression> |
| Builder.AddTypedTextChunk("__bridge_transfer"); |
| Builder.AddChunk(CodeCompletionString::CK_HorizontalSpace); |
| Builder.AddPlaceholderChunk("Objective-C type"); |
| Builder.AddChunk(CodeCompletionString::CK_RightParen); |
| Builder.AddPlaceholderChunk("expression"); |
| Results.AddResult(Result(Builder.TakeString())); |
| |
| // (__bridge_retained <CF type>)<expression> |
| Builder.AddTypedTextChunk("__bridge_retained"); |
| Builder.AddChunk(CodeCompletionString::CK_HorizontalSpace); |
| Builder.AddPlaceholderChunk("CF type"); |
| Builder.AddChunk(CodeCompletionString::CK_RightParen); |
| Builder.AddPlaceholderChunk("expression"); |
| Results.AddResult(Result(Builder.TakeString())); |
| } |
| // Fall through |
| LLVM_FALLTHROUGH; |
| |
| case Sema::PCC_Expression: { |
| if (SemaRef.getLangOpts().CPlusPlus) { |
| // 'this', if we're in a non-static member function. |
| addThisCompletion(SemaRef, Results); |
| |
| // true |
| Builder.AddResultTypeChunk("bool"); |
| Builder.AddTypedTextChunk("true"); |
| Results.AddResult(Result(Builder.TakeString())); |
| |
| // false |
| Builder.AddResultTypeChunk("bool"); |
| Builder.AddTypedTextChunk("false"); |
| Results.AddResult(Result(Builder.TakeString())); |
| |
| if (SemaRef.getLangOpts().RTTI) { |
| // dynamic_cast < type-id > ( expression ) |
| Builder.AddTypedTextChunk("dynamic_cast"); |
| Builder.AddChunk(CodeCompletionString::CK_LeftAngle); |
| Builder.AddPlaceholderChunk("type"); |
| Builder.AddChunk(CodeCompletionString::CK_RightAngle); |
| Builder.AddChunk(CodeCompletionString::CK_LeftParen); |
| Builder.AddPlaceholderChunk("expression"); |
| Builder.AddChunk(CodeCompletionString::CK_RightParen); |
| Results.AddResult(Result(Builder.TakeString())); |
| } |
| |
| // static_cast < type-id > ( expression ) |
| Builder.AddTypedTextChunk("static_cast"); |
| Builder.AddChunk(CodeCompletionString::CK_LeftAngle); |
| Builder.AddPlaceholderChunk("type"); |
| Builder.AddChunk(CodeCompletionString::CK_RightAngle); |
| Builder.AddChunk(CodeCompletionString::CK_LeftParen); |
| Builder.AddPlaceholderChunk("expression"); |
| Builder.AddChunk(CodeCompletionString::CK_RightParen); |
| Results.AddResult(Result(Builder.TakeString())); |
| |
| // reinterpret_cast < type-id > ( expression ) |
| Builder.AddTypedTextChunk("reinterpret_cast"); |
| Builder.AddChunk(CodeCompletionString::CK_LeftAngle); |
| Builder.AddPlaceholderChunk("type"); |
| Builder.AddChunk(CodeCompletionString::CK_RightAngle); |
| Builder.AddChunk(CodeCompletionString::CK_LeftParen); |
| Builder.AddPlaceholderChunk("expression"); |
| Builder.AddChunk(CodeCompletionString::CK_RightParen); |
| Results.AddResult(Result(Builder.TakeString())); |
| |
| // const_cast < type-id > ( expression ) |
| Builder.AddTypedTextChunk("const_cast"); |
| Builder.AddChunk(CodeCompletionString::CK_LeftAngle); |
| Builder.AddPlaceholderChunk("type"); |
| Builder.AddChunk(CodeCompletionString::CK_RightAngle); |
| Builder.AddChunk(CodeCompletionString::CK_LeftParen); |
| Builder.AddPlaceholderChunk("expression"); |
| Builder.AddChunk(CodeCompletionString::CK_RightParen); |
| Results.AddResult(Result(Builder.TakeString())); |
| |
| if (SemaRef.getLangOpts().RTTI) { |
| // typeid ( expression-or-type ) |
| Builder.AddResultTypeChunk("std::type_info"); |
| Builder.AddTypedTextChunk("typeid"); |
| Builder.AddChunk(CodeCompletionString::CK_LeftParen); |
| Builder.AddPlaceholderChunk("expression-or-type"); |
| Builder.AddChunk(CodeCompletionString::CK_RightParen); |
| Results.AddResult(Result(Builder.TakeString())); |
| } |
| |
| // new T ( ... ) |
| Builder.AddTypedTextChunk("new"); |
| Builder.AddChunk(CodeCompletionString::CK_HorizontalSpace); |
| Builder.AddPlaceholderChunk("type"); |
| Builder.AddChunk(CodeCompletionString::CK_LeftParen); |
| Builder.AddPlaceholderChunk("expressions"); |
| Builder.AddChunk(CodeCompletionString::CK_RightParen); |
| Results.AddResult(Result(Builder.TakeString())); |
| |
| // new T [ ] ( ... ) |
| Builder.AddTypedTextChunk("new"); |
| Builder.AddChunk(CodeCompletionString::CK_HorizontalSpace); |
| Builder.AddPlaceholderChunk("type"); |
| Builder.AddChunk(CodeCompletionString::CK_LeftBracket); |
| Builder.AddPlaceholderChunk("size"); |
| Builder.AddChunk(CodeCompletionString::CK_RightBracket); |
| Builder.AddChunk(CodeCompletionString::CK_LeftParen); |
| Builder.AddPlaceholderChunk("expressions"); |
| Builder.AddChunk(CodeCompletionString::CK_RightParen); |
| Results.AddResult(Result(Builder.TakeString())); |
| |
| // delete expression |
| Builder.AddResultTypeChunk("void"); |
| Builder.AddTypedTextChunk("delete"); |
| Builder.AddChunk(CodeCompletionString::CK_HorizontalSpace); |
| Builder.AddPlaceholderChunk("expression"); |
| Results.AddResult(Result(Builder.TakeString())); |
| |
| // delete [] expression |
| Builder.AddResultTypeChunk("void"); |
| Builder.AddTypedTextChunk("delete"); |
| Builder.AddChunk(CodeCompletionString::CK_HorizontalSpace); |
| Builder.AddChunk(CodeCompletionString::CK_LeftBracket); |
| Builder.AddChunk(CodeCompletionString::CK_RightBracket); |
| Builder.AddChunk(CodeCompletionString::CK_HorizontalSpace); |
| Builder.AddPlaceholderChunk("expression"); |
| Results.AddResult(Result(Builder.TakeString())); |
| |
| if (SemaRef.getLangOpts().CXXExceptions) { |
| // throw expression |
| Builder.AddResultTypeChunk("void"); |
| Builder.AddTypedTextChunk("throw"); |
| Builder.AddChunk(CodeCompletionString::CK_HorizontalSpace); |
| Builder.AddPlaceholderChunk("expression"); |
| Results.AddResult(Result(Builder.TakeString())); |
| } |
| |
| // FIXME: Rethrow? |
| |
| if (SemaRef.getLangOpts().CPlusPlus11) { |
| // nullptr |
| Builder.AddResultTypeChunk("std::nullptr_t"); |
| Builder.AddTypedTextChunk("nullptr"); |
| Results.AddResult(Result(Builder.TakeString())); |
| |
| // alignof |
| Builder.AddResultTypeChunk("size_t"); |
| Builder.AddTypedTextChunk("alignof"); |
| Builder.AddChunk(CodeCompletionString::CK_LeftParen); |
| Builder.AddPlaceholderChunk("type"); |
| Builder.AddChunk(CodeCompletionString::CK_RightParen); |
| Results.AddResult(Result(Builder.TakeString())); |
| |
| // noexcept |
| Builder.AddResultTypeChunk("bool"); |
| Builder.AddTypedTextChunk("noexcept"); |
| Builder.AddChunk(CodeCompletionString::CK_LeftParen); |
| Builder.AddPlaceholderChunk("expression"); |
| Builder.AddChunk(CodeCompletionString::CK_RightParen); |
| Results.AddResult(Result(Builder.TakeString())); |
| |
| // sizeof... expression |
| Builder.AddResultTypeChunk("size_t"); |
| Builder.AddTypedTextChunk("sizeof..."); |
| Builder.AddChunk(CodeCompletionString::CK_LeftParen); |
| Builder.AddPlaceholderChunk("parameter-pack"); |
| Builder.AddChunk(CodeCompletionString::CK_RightParen); |
| Results.AddResult(Result(Builder.TakeString())); |
| } |
| } |
| |
| if (SemaRef.getLangOpts().ObjC1) { |
| // Add "super", if we're in an Objective-C class with a superclass. |
| if (ObjCMethodDecl *Method = SemaRef.getCurMethodDecl()) { |
| // The interface can be NULL. |
| if (ObjCInterfaceDecl *ID = Method->getClassInterface()) |
| if (ID->getSuperClass()) { |
| std::string SuperType; |
| SuperType = ID->getSuperClass()->getNameAsString(); |
| if (Method->isInstanceMethod()) |
| SuperType += " *"; |
| |
| Builder.AddResultTypeChunk(Allocator.CopyString(SuperType)); |
| Builder.AddTypedTextChunk("super"); |
| Results.AddResult(Result(Builder.TakeString())); |
| } |
| } |
| |
| AddObjCExpressionResults(Results, true); |
| } |
| |
| if (SemaRef.getLangOpts().C11) { |
| // _Alignof |
| Builder.AddResultTypeChunk("size_t"); |
| if (SemaRef.PP.isMacroDefined("alignof")) |
| Builder.AddTypedTextChunk("alignof"); |
| else |
| Builder.AddTypedTextChunk("_Alignof"); |
| Builder.AddChunk(CodeCompletionString::CK_LeftParen); |
| Builder.AddPlaceholderChunk("type"); |
| Builder.AddChunk(CodeCompletionString::CK_RightParen); |
| Results.AddResult(Result(Builder.TakeString())); |
| } |
| |
| // sizeof expression |
| Builder.AddResultTypeChunk("size_t"); |
| Builder.AddTypedTextChunk("sizeof"); |
| Builder.AddChunk(CodeCompletionString::CK_LeftParen); |
| Builder.AddPlaceholderChunk("expression-or-type"); |
| Builder.AddChunk(CodeCompletionString::CK_RightParen); |
| Results.AddResult(Result(Builder.TakeString())); |
| break; |
| } |
| |
| case Sema::PCC_Type: |
| case Sema::PCC_LocalDeclarationSpecifiers: |
| break; |
| } |
| |
| if (WantTypesInContext(CCC, SemaRef.getLangOpts())) |
| AddTypeSpecifierResults(SemaRef.getLangOpts(), Results); |
| |
| if (SemaRef.getLangOpts().CPlusPlus && CCC != Sema::PCC_Type) |
| Results.AddResult(Result("operator")); |
| } |
| |
| /// \brief If the given declaration has an associated type, add it as a result |
| /// type chunk. |
| static void AddResultTypeChunk(ASTContext &Context, |
| const PrintingPolicy &Policy, |
| const NamedDecl *ND, |
| QualType BaseType, |
| CodeCompletionBuilder &Result) { |
| if (!ND) |
| return; |
| |
| // Skip constructors and conversion functions, which have their return types |
| // built into their names. |
| if (isa<CXXConstructorDecl>(ND) || isa<CXXConversionDecl>(ND)) |
| return; |
| |
| // Determine the type of the declaration (if it has a type). |
| QualType T; |
| if (const FunctionDecl *Function = ND->getAsFunction()) |
| T = Function->getReturnType(); |
| else if (const ObjCMethodDecl *Method = dyn_cast<ObjCMethodDecl>(ND)) { |
| if (!BaseType.isNull()) |
| T = Method->getSendResultType(BaseType); |
| else |
| T = Method->getReturnType(); |
| } else if (const EnumConstantDecl *Enumerator = dyn_cast<EnumConstantDecl>(ND)) { |
| T = Context.getTypeDeclType(cast<TypeDecl>(Enumerator->getDeclContext())); |
| T = clang::TypeName::getFullyQualifiedType(T, Context); |
| } else if (isa<UnresolvedUsingValueDecl>(ND)) { |
| /* Do nothing: ignore unresolved using declarations*/ |
| } else if (const ObjCIvarDecl *Ivar = dyn_cast<ObjCIvarDecl>(ND)) { |
| if (!BaseType.isNull()) |
| T = Ivar->getUsageType(BaseType); |
| else |
| T = Ivar->getType(); |
| } else if (const ValueDecl *Value = dyn_cast<ValueDecl>(ND)) { |
| T = Value->getType(); |
| } else if (const ObjCPropertyDecl *Property = dyn_cast<ObjCPropertyDecl>(ND)) { |
| if (!BaseType.isNull()) |
| T = Property->getUsageType(BaseType); |
| else |
| T = Property->getType(); |
| } |
| |
| if (T.isNull() || Context.hasSameType(T, Context.DependentTy)) |
| return; |
| |
| Result.AddResultTypeChunk(GetCompletionTypeString(T, Context, Policy, |
| Result.getAllocator())); |
| } |
| |
| static void MaybeAddSentinel(Preprocessor &PP, |
| const NamedDecl *FunctionOrMethod, |
| CodeCompletionBuilder &Result) { |
| if (SentinelAttr *Sentinel = FunctionOrMethod->getAttr<SentinelAttr>()) |
| if (Sentinel->getSentinel() == 0) { |
| if (PP.getLangOpts().ObjC1 && PP.isMacroDefined("nil")) |
| Result.AddTextChunk(", nil"); |
| else if (PP.isMacroDefined("NULL")) |
| Result.AddTextChunk(", NULL"); |
| else |
| Result.AddTextChunk(", (void*)0"); |
| } |
| } |
| |
| static std::string formatObjCParamQualifiers(unsigned ObjCQuals, |
| QualType &Type) { |
| std::string Result; |
| if (ObjCQuals & Decl::OBJC_TQ_In) |
| Result += "in "; |
| else if (ObjCQuals & Decl::OBJC_TQ_Inout) |
| Result += "inout "; |
| else if (ObjCQuals & Decl::OBJC_TQ_Out) |
| Result += "out "; |
| if (ObjCQuals & Decl::OBJC_TQ_Bycopy) |
| Result += "bycopy "; |
| else if (ObjCQuals & Decl::OBJC_TQ_Byref) |
| Result += "byref "; |
| if (ObjCQuals & Decl::OBJC_TQ_Oneway) |
| Result += "oneway "; |
| if (ObjCQuals & Decl::OBJC_TQ_CSNullability) { |
| if (auto nullability = AttributedType::stripOuterNullability(Type)) { |
| switch (*nullability) { |
| case NullabilityKind::NonNull: |
| Result += "nonnull "; |
| break; |
| |
| case NullabilityKind::Nullable: |
| Result += "nullable "; |
| break; |
| |
| case NullabilityKind::Unspecified: |
| Result += "null_unspecified "; |
| break; |
| } |
| } |
| } |
| return Result; |
| } |
| |
| /// \brief Tries to find the most appropriate type location for an Objective-C |
| /// block placeholder. |
| /// |
| /// This function ignores things like typedefs and qualifiers in order to |
| /// present the most relevant and accurate block placeholders in code completion |
| /// results. |
| static void findTypeLocationForBlockDecl(const TypeSourceInfo *TSInfo, |
| FunctionTypeLoc &Block, |
| FunctionProtoTypeLoc &BlockProto, |
| bool SuppressBlock = false) { |
| if (!TSInfo) |
| return; |
| TypeLoc TL = TSInfo->getTypeLoc().getUnqualifiedLoc(); |
| while (true) { |
| // Look through typedefs. |
| if (!SuppressBlock) { |
| if (TypedefTypeLoc TypedefTL = TL.getAs<TypedefTypeLoc>()) { |
| if (TypeSourceInfo *InnerTSInfo = |
| TypedefTL.getTypedefNameDecl()->getTypeSourceInfo()) { |
| TL = InnerTSInfo->getTypeLoc().getUnqualifiedLoc(); |
| continue; |
| } |
| } |
| |
| // Look through qualified types |
| if (QualifiedTypeLoc QualifiedTL = TL.getAs<QualifiedTypeLoc>()) { |
| TL = QualifiedTL.getUnqualifiedLoc(); |
| continue; |
| } |
| |
| if (AttributedTypeLoc AttrTL = TL.getAs<AttributedTypeLoc>()) { |
| TL = AttrTL.getModifiedLoc(); |
| continue; |
| } |
| } |
| |
| // Try to get the function prototype behind the block pointer type, |
| // then we're done. |
| if (BlockPointerTypeLoc BlockPtr = TL.getAs<BlockPointerTypeLoc>()) { |
| TL = BlockPtr.getPointeeLoc().IgnoreParens(); |
| Block = TL.getAs<FunctionTypeLoc>(); |
| BlockProto = TL.getAs<FunctionProtoTypeLoc>(); |
| } |
| break; |
| } |
| } |
| |
| static std::string |
| formatBlockPlaceholder(const PrintingPolicy &Policy, const NamedDecl *BlockDecl, |
| FunctionTypeLoc &Block, FunctionProtoTypeLoc &BlockProto, |
| bool SuppressBlockName = false, |
| bool SuppressBlock = false, |
| Optional<ArrayRef<QualType>> ObjCSubsts = None); |
| |
| static std::string FormatFunctionParameter(const PrintingPolicy &Policy, |
| const ParmVarDecl *Param, |
| bool SuppressName = false, |
| bool SuppressBlock = false, |
| Optional<ArrayRef<QualType>> ObjCSubsts = None) { |
| bool ObjCMethodParam = isa<ObjCMethodDecl>(Param->getDeclContext()); |
| if (Param->getType()->isDependentType() || |
| !Param->getType()->isBlockPointerType()) { |
| // The argument for a dependent or non-block parameter is a placeholder |
| // containing that parameter's type. |
| std::string Result; |
| |
| if (Param->getIdentifier() && !ObjCMethodParam && !SuppressName) |
| Result = Param->getIdentifier()->getName(); |
| |
| QualType Type = Param->getType(); |
| if (ObjCSubsts) |
| Type = Type.substObjCTypeArgs(Param->getASTContext(), *ObjCSubsts, |
| ObjCSubstitutionContext::Parameter); |
| if (ObjCMethodParam) { |
| Result = "(" + formatObjCParamQualifiers(Param->getObjCDeclQualifier(), |
| Type); |
| Result += Type.getAsString(Policy) + ")"; |
| if (Param->getIdentifier() && !SuppressName) |
| Result += Param->getIdentifier()->getName(); |
| } else { |
| Type.getAsStringInternal(Result, Policy); |
| } |
| return Result; |
| } |
| |
| // The argument for a block pointer parameter is a block literal with |
| // the appropriate type. |
| FunctionTypeLoc Block; |
| FunctionProtoTypeLoc BlockProto; |
| findTypeLocationForBlockDecl(Param->getTypeSourceInfo(), Block, BlockProto, |
| SuppressBlock); |
| // Try to retrieve the block type information from the property if this is a |
| // parameter in a setter. |
| if (!Block && ObjCMethodParam && |
| cast<ObjCMethodDecl>(Param->getDeclContext())->isPropertyAccessor()) { |
| if (const auto *PD = cast<ObjCMethodDecl>(Param->getDeclContext()) |
| ->findPropertyDecl(/*CheckOverrides=*/false)) |
| findTypeLocationForBlockDecl(PD->getTypeSourceInfo(), Block, BlockProto, |
| SuppressBlock); |
| } |
| |
| if (!Block) { |
| // We were unable to find a FunctionProtoTypeLoc with parameter names |
| // for the block; just use the parameter type as a placeholder. |
| std::string Result; |
| if (!ObjCMethodParam && Param->getIdentifier()) |
| Result = Param->getIdentifier()->getName(); |
| |
| QualType Type = Param->getType().getUnqualifiedType(); |
| |
| if (ObjCMethodParam) { |
| Result = Type.getAsString(Policy); |
| std::string Quals = |
| formatObjCParamQualifiers(Param->getObjCDeclQualifier(), Type); |
| if (!Quals.empty()) |
| Result = "(" + Quals + " " + Result + ")"; |
| if (Result.back() != ')') |
| Result += " "; |
| if (Param->getIdentifier()) |
| Result += Param->getIdentifier()->getName(); |
| } else { |
| Type.getAsStringInternal(Result, Policy); |
| } |
| |
| return Result; |
| } |
| |
| // We have the function prototype behind the block pointer type, as it was |
| // written in the source. |
| return formatBlockPlaceholder(Policy, Param, Block, BlockProto, |
| /*SuppressBlockName=*/false, SuppressBlock, |
| ObjCSubsts); |
| } |
| |
| /// \brief Returns a placeholder string that corresponds to an Objective-C block |
| /// declaration. |
| /// |
| /// \param BlockDecl A declaration with an Objective-C block type. |
| /// |
| /// \param Block The most relevant type location for that block type. |
| /// |
| /// \param SuppressBlockName Determines wether or not the name of the block |
| /// declaration is included in the resulting string. |
| static std::string |
| formatBlockPlaceholder(const PrintingPolicy &Policy, const NamedDecl *BlockDecl, |
| FunctionTypeLoc &Block, FunctionProtoTypeLoc &BlockProto, |
| bool SuppressBlockName, bool SuppressBlock, |
| Optional<ArrayRef<QualType>> ObjCSubsts) { |
| std::string Result; |
| QualType ResultType = Block.getTypePtr()->getReturnType(); |
| if (ObjCSubsts) |
| ResultType = |
| ResultType.substObjCTypeArgs(BlockDecl->getASTContext(), *ObjCSubsts, |
| ObjCSubstitutionContext::Result); |
| if (!ResultType->isVoidType() || SuppressBlock) |
| ResultType.getAsStringInternal(Result, Policy); |
| |
| // Format the parameter list. |
| std::string Params; |
| if (!BlockProto || Block.getNumParams() == 0) { |
| if (BlockProto && BlockProto.getTypePtr()->isVariadic()) |
| Params = "(...)"; |
| else |
| Params = "(void)"; |
| } else { |
| Params += "("; |
| for (unsigned I = 0, N = Block.getNumParams(); I != N; ++I) { |
| if (I) |
| Params += ", "; |
| Params += FormatFunctionParameter(Policy, Block.getParam(I), |
| /*SuppressName=*/false, |
| /*SuppressBlock=*/true, ObjCSubsts); |
| |
| if (I == N - 1 && BlockProto.getTypePtr()->isVariadic()) |
| Params += ", ..."; |
| } |
| Params += ")"; |
| } |
| |
| if (SuppressBlock) { |
| // Format as a parameter. |
| Result = Result + " (^"; |
| if (!SuppressBlockName && BlockDecl->getIdentifier()) |
| Result += BlockDecl->getIdentifier()->getName(); |
| Result += ")"; |
| Result += Params; |
| } else { |
| // Format as a block literal argument. |
| Result = '^' + Result; |
| Result += Params; |
| |
| if (!SuppressBlockName && BlockDecl->getIdentifier()) |
| Result += BlockDecl->getIdentifier()->getName(); |
| } |
| |
| return Result; |
| } |
| |
| static std::string GetDefaultValueString(const ParmVarDecl *Param, |
| const SourceManager &SM, |
| const LangOptions &LangOpts) { |
| const SourceRange SrcRange = Param->getDefaultArgRange(); |
| CharSourceRange CharSrcRange = CharSourceRange::getTokenRange(SrcRange); |
| bool Invalid = CharSrcRange.isInvalid(); |
| if (Invalid) |
| return ""; |
| StringRef srcText = Lexer::getSourceText(CharSrcRange, SM, LangOpts, &Invalid); |
| if (Invalid) |
| return ""; |
| |
| if (srcText.empty() || srcText == "=") { |
| // Lexer can't determine the value. |
| // This happens if the code is incorrect (for example class is forward declared). |
| return ""; |
| } |
| std::string DefValue(srcText.str()); |
| // FIXME: remove this check if the Lexer::getSourceText value is fixed and |
| // this value always has (or always does not have) '=' in front of it |
| if (DefValue.at(0) != '=') { |
| // If we don't have '=' in front of value. |
| // Lexer returns built-in types values without '=' and user-defined types values with it. |
| return " = " + DefValue; |
| } |
| return " " + DefValue; |
| } |
| |
| /// \brief Add function parameter chunks to the given code completion string. |
| static void AddFunctionParameterChunks(Preprocessor &PP, |
| const PrintingPolicy &Policy, |
| const FunctionDecl *Function, |
| CodeCompletionBuilder &Result, |
| unsigned Start = 0, |
| bool InOptional = false) { |
| bool FirstParameter = true; |
| |
| for (unsigned P = Start, N = Function->getNumParams(); P != N; ++P) { |
| const ParmVarDecl *Param = Function->getParamDecl(P); |
| |
| if (Param->hasDefaultArg() && !InOptional) { |
| // When we see an optional default argument, put that argument and |
| // the remaining default arguments into a new, optional string. |
| CodeCompletionBuilder Opt(Result.getAllocator(), |
| Result.getCodeCompletionTUInfo()); |
| if (!FirstParameter) |
| Opt.AddChunk(CodeCompletionString::CK_Comma); |
| AddFunctionParameterChunks(PP, Policy, Function, Opt, P, true); |
| Result.AddOptionalChunk(Opt.TakeString()); |
| break; |
| } |
| |
| if (FirstParameter) |
| FirstParameter = false; |
| else |
| Result.AddChunk(CodeCompletionString::CK_Comma); |
| |
| InOptional = false; |
| |
| // Format the placeholder string. |
| std::string PlaceholderStr = FormatFunctionParameter(Policy, Param); |
| if (Param->hasDefaultArg()) |
| PlaceholderStr += GetDefaultValueString(Param, PP.getSourceManager(), PP.getLangOpts()); |
| |
| if (Function->isVariadic() && P == N - 1) |
| PlaceholderStr += ", ..."; |
| |
| // Add the placeholder string. |
| Result.AddPlaceholderChunk( |
| Result.getAllocator().CopyString(PlaceholderStr)); |
| } |
| |
| if (const FunctionProtoType *Proto |
| = Function->getType()->getAs<FunctionProtoType>()) |
| if (Proto->isVariadic()) { |
| if (Proto->getNumParams() == 0) |
| Result.AddPlaceholderChunk("..."); |
| |
| MaybeAddSentinel(PP, Function, Result); |
| } |
| } |
| |
| /// \brief Add template parameter chunks to the given code completion string. |
| static void AddTemplateParameterChunks(ASTContext &Context, |
| const PrintingPolicy &Policy, |
| const TemplateDecl *Template, |
| CodeCompletionBuilder &Result, |
| unsigned MaxParameters = 0, |
| unsigned Start = 0, |
| bool InDefaultArg = false) { |
| bool FirstParameter = true; |
| |
| // Prefer to take the template parameter names from the first declaration of |
| // the template. |
| Template = cast<TemplateDecl>(Template->getCanonicalDecl()); |
| |
| TemplateParameterList *Params = Template->getTemplateParameters(); |
| TemplateParameterList::iterator PEnd = Params->end(); |
| if (MaxParameters) |
| PEnd = Params->begin() + MaxParameters; |
| for (TemplateParameterList::iterator P = Params->begin() + Start; |
| P != PEnd; ++P) { |
| bool HasDefaultArg = false; |
| std::string PlaceholderStr; |
| if (TemplateTypeParmDecl *TTP = dyn_cast<TemplateTypeParmDecl>(*P)) { |
| if (TTP->wasDeclaredWithTypename()) |
| PlaceholderStr = "typename"; |
| else |
| PlaceholderStr = "class"; |
| |
| if (TTP->getIdentifier()) { |
| PlaceholderStr += ' '; |
| PlaceholderStr += TTP->getIdentifier()->getName(); |
| } |
| |
| HasDefaultArg = TTP->hasDefaultArgument(); |
| } else if (NonTypeTemplateParmDecl *NTTP |
| = dyn_cast<NonTypeTemplateParmDecl>(*P)) { |
| if (NTTP->getIdentifier()) |
| PlaceholderStr = NTTP->getIdentifier()->getName(); |
| NTTP->getType().getAsStringInternal(PlaceholderStr, Policy); |
| HasDefaultArg = NTTP->hasDefaultArgument(); |
| } else { |
| assert(isa<TemplateTemplateParmDecl>(*P)); |
| TemplateTemplateParmDecl *TTP = cast<TemplateTemplateParmDecl>(*P); |
| |
| // Since putting the template argument list into the placeholder would |
| // be very, very long, we just use an abbreviation. |
| PlaceholderStr = "template<...> class"; |
| if (TTP->getIdentifier()) { |
| PlaceholderStr += ' '; |
| PlaceholderStr += TTP->getIdentifier()->getName(); |
| } |
| |
| HasDefaultArg = TTP->hasDefaultArgument(); |
| } |
| |
| if (HasDefaultArg && !InDefaultArg) { |
| // When we see an optional default argument, put that argument and |
| // the remaining default arguments into a new, optional string. |
| CodeCompletionBuilder Opt(Result.getAllocator(), |
| Result.getCodeCompletionTUInfo()); |
| if (!FirstParameter) |
| Opt.AddChunk(CodeCompletionString::CK_Comma); |
| AddTemplateParameterChunks(Context, Policy, Template, Opt, MaxParameters, |
| P - Params->begin(), true); |
| Result.AddOptionalChunk(Opt.TakeString()); |
| break; |
| } |
| |
| InDefaultArg = false; |
| |
| if (FirstParameter) |
| FirstParameter = false; |
| else |
| Result.AddChunk(CodeCompletionString::CK_Comma); |
| |
| // Add the placeholder string. |
| Result.AddPlaceholderChunk( |
| Result.getAllocator().CopyString(PlaceholderStr)); |
| } |
| } |
| |
| /// \brief Add a qualifier to the given code-completion string, if the |
| /// provided nested-name-specifier is non-NULL. |
| static void |
| AddQualifierToCompletionString(CodeCompletionBuilder &Result, |
| NestedNameSpecifier *Qualifier, |
| bool QualifierIsInformative, |
| ASTContext &Context, |
| const PrintingPolicy &Policy) { |
| if (!Qualifier) |
| return; |
| |
| std::string PrintedNNS; |
| { |
| llvm::raw_string_ostream OS(PrintedNNS); |
| Qualifier->print(OS, Policy); |
| } |
| if (QualifierIsInformative) |
| Result.AddInformativeChunk(Result.getAllocator().CopyString(PrintedNNS)); |
| else |
| Result.AddTextChunk(Result.getAllocator().CopyString(PrintedNNS)); |
| } |
| |
| static void |
| AddFunctionTypeQualsToCompletionString(CodeCompletionBuilder &Result, |
| const FunctionDecl *Function) { |
| const FunctionProtoType *Proto |
| = Function->getType()->getAs<FunctionProtoType>(); |
| if (!Proto || !Proto->getTypeQuals()) |
| return; |
| |
| // FIXME: Add ref-qualifier! |
| |
| // Handle single qualifiers without copying |
| if (Proto->getTypeQuals() == Qualifiers::Const) { |
| Result.AddInformativeChunk(" const"); |
| return; |
| } |
| |
| if (Proto->getTypeQuals() == Qualifiers::Volatile) { |
| Result.AddInformativeChunk(" volatile"); |
| return; |
| } |
| |
| if (Proto->getTypeQuals() == Qualifiers::Restrict) { |
| Result.AddInformativeChunk(" restrict"); |
| return; |
| } |
| |
| // Handle multiple qualifiers. |
| std::string QualsStr; |
| if (Proto->isConst()) |
| QualsStr += " const"; |
| if (Proto->isVolatile()) |
| QualsStr += " volatile"; |
| if (Proto->isRestrict()) |
| QualsStr += " restrict"; |
| Result.AddInformativeChunk(Result.getAllocator().CopyString(QualsStr)); |
| } |
| |
| /// \brief Add the name of the given declaration |
| static void AddTypedNameChunk(ASTContext &Context, const PrintingPolicy &Policy, |
| const NamedDecl *ND, |
| CodeCompletionBuilder &Result) { |
| DeclarationName Name = ND->getDeclName(); |
| if (!Name) |
| return; |
| |
| switch (Name.getNameKind()) { |
| case DeclarationName::CXXOperatorName: { |
| const char *OperatorName = nullptr; |
| switch (Name.getCXXOverloadedOperator()) { |
| case OO_None: |
| case OO_Conditional: |
| case NUM_OVERLOADED_OPERATORS: |
| OperatorName = "operator"; |
| break; |
| |
| #define OVERLOADED_OPERATOR(Name,Spelling,Token,Unary,Binary,MemberOnly) \ |
| case OO_##Name: OperatorName = "operator" Spelling; break; |
| #define OVERLOADED_OPERATOR_MULTI(Name,Spelling,Unary,Binary,MemberOnly) |
| #include "clang/Basic/OperatorKinds.def" |
| |
| case OO_New: OperatorName = "operator new"; break; |
| case OO_Delete: OperatorName = "operator delete"; break; |
| case OO_Array_New: OperatorName = "operator new[]"; break; |
| case OO_Array_Delete: OperatorName = "operator delete[]"; break; |
| case OO_Call: OperatorName = "operator()"; break; |
| case OO_Subscript: OperatorName = "operator[]"; break; |
| } |
| Result.AddTypedTextChunk(OperatorName); |
| break; |
| } |
| |
| case DeclarationName::Identifier: |
| case DeclarationName::CXXConversionFunctionName: |
| case DeclarationName::CXXDestructorName: |
| case DeclarationName::CXXLiteralOperatorName: |
| Result.AddTypedTextChunk( |
| Result.getAllocator().CopyString(ND->getNameAsString())); |
| break; |
| |
| case DeclarationName::CXXDeductionGuideName: |
| case DeclarationName::CXXUsingDirective: |
| case DeclarationName::ObjCZeroArgSelector: |
| case DeclarationName::ObjCOneArgSelector: |
| case DeclarationName::ObjCMultiArgSelector: |
| break; |
| |
| case DeclarationName::CXXConstructorName: { |
| CXXRecordDecl *Record = nullptr; |
| QualType Ty = Name.getCXXNameType(); |
| if (const RecordType *RecordTy = Ty->getAs<RecordType>()) |
| Record = cast<CXXRecordDecl>(RecordTy->getDecl()); |
| else if (const InjectedClassNameType *InjectedTy |
| = Ty->getAs<InjectedClassNameType>()) |
| Record = InjectedTy->getDecl(); |
| else { |
| Result.AddTypedTextChunk( |
| Result.getAllocator().CopyString(ND->getNameAsString())); |
| break; |
| } |
| |
| Result.AddTypedTextChunk( |
| Result.getAllocator().CopyString(Record->getNameAsString())); |
| if (ClassTemplateDecl *Template = Record->getDescribedClassTemplate()) { |
| Result.AddChunk(CodeCompletionString::CK_LeftAngle); |
| AddTemplateParameterChunks(Context, Policy, Template, Result); |
| Result.AddChunk(CodeCompletionString::CK_RightAngle); |
| } |
| break; |
| |