From: Douglas Gregor Date: Thu, 26 Aug 2010 16:36:48 +0000 (+0000) Subject: When code-completing a potential call to a C++ non-static member X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=3cdee121daa13403335094ce0e181b9911c2124c;p=clang When code-completing a potential call to a C++ non-static member function, take into account the qualifiers on the object argument (e.g., what will become "this"), filtering around uncallable member functions and giving a slight priority boost to those with exactly-matching qualifiers. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@112193 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/AST/ASTContext.h b/include/clang/AST/ASTContext.h index fdf2d52bbb..20c8d1f41d 100644 --- a/include/clang/AST/ASTContext.h +++ b/include/clang/AST/ASTContext.h @@ -1073,8 +1073,6 @@ public: bool UnwrapSimilarPointerTypes(QualType &T1, QualType &T2); - /// \brief Retrieves the "canonical" declaration of - /// \brief Retrieves the "canonical" nested name specifier for a /// given nested name specifier. /// diff --git a/include/clang/AST/Type.h b/include/clang/AST/Type.h index 5a69932051..819d7d24a5 100644 --- a/include/clang/AST/Type.h +++ b/include/clang/AST/Type.h @@ -289,7 +289,18 @@ public: L += R; return L; } + + Qualifiers &operator-=(Qualifiers R) { + Mask = Mask & ~(R.Mask); + return *this; + } + /// \brief Compute the difference between two qualifier sets. + friend Qualifiers operator-(Qualifiers L, Qualifiers R) { + L -= R; + return L; + } + std::string getAsString() const; std::string getAsString(const PrintingPolicy &Policy) const { std::string Buffer; diff --git a/include/clang/Sema/CodeCompleteConsumer.h b/include/clang/Sema/CodeCompleteConsumer.h index 55bd576f96..27cda88cb2 100644 --- a/include/clang/Sema/CodeCompleteConsumer.h +++ b/include/clang/Sema/CodeCompleteConsumer.h @@ -55,7 +55,7 @@ enum { CCP_Unlikely = 80 }; -/// \brief Priority value deltas that are applied to code-completion results +/// \brief Priority value deltas that are added to code-completion results /// based on the context of the result. enum { /// \brief The result is in a base class. @@ -64,7 +64,10 @@ enum { /// /// Since everything converts to "void", we don't give as drastic an /// adjustment for matching void. - CCD_VoidMatch = -5 + CCD_VoidMatch = -5, + /// \brief The result is a C++ non-static member function whose qualifiers + /// exactly match the object type on which the member function can be called. + CCD_ObjectQualifierMatch = -1 }; /// \brief Priority value factors by which we will divide or multiply the diff --git a/lib/Sema/SemaCodeComplete.cpp b/lib/Sema/SemaCodeComplete.cpp index b75a7d05bb..392152630d 100644 --- a/lib/Sema/SemaCodeComplete.cpp +++ b/lib/Sema/SemaCodeComplete.cpp @@ -136,11 +136,19 @@ namespace { /// different levels of, e.g., the inheritance hierarchy. std::list 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; + void AdjustResultPriorityForPreferredType(Result &R); public: explicit ResultBuilder(Sema &SemaRef, LookupFilter Filter = 0) - : SemaRef(SemaRef), Filter(Filter), AllowNestedNameSpecifiers(false) { } + : SemaRef(SemaRef), Filter(Filter), AllowNestedNameSpecifiers(false), + HasObjectTypeQualifiers(false) { } /// \brief Whether we should include code patterns in the completion /// results. @@ -167,6 +175,18 @@ namespace { 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 Specify whether nested-name-specifiers are allowed. void allowNestedNameSpecifiers(bool Allow = true) { AllowNestedNameSpecifiers = Allow; @@ -770,6 +790,20 @@ void ResultBuilder::AddResult(Result R, DeclContext *CurContext, if (!PreferredType.isNull()) AdjustResultPriorityForPreferredType(R); + if (HasObjectTypeQualifiers) + if (CXXMethodDecl *Method = dyn_cast(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); } @@ -2364,6 +2398,13 @@ void Sema::CodeCompleteOrdinaryName(Scope *S, break; } + // If we are in a C++ non-static member function, check the qualifiers on + // the member function to filter/prioritize the results list. + if (CXXMethodDecl *CurMethod = dyn_cast(CurContext)) + if (CurMethod->isInstance()) + Results.setObjectTypeQualifiers( + Qualifiers::fromCVRMask(CurMethod->getTypeQualifiers())); + CodeCompletionDeclConsumer Consumer(Results, CurContext); LookupVisibleDecls(S, LookupOrdinaryName, Consumer, CodeCompleter->includeGlobals()); @@ -2566,7 +2607,7 @@ void Sema::CodeCompleteMemberReferenceExpr(Scope *S, ExprTy *BaseE, if (const PointerType *Ptr = BaseType->getAs()) BaseType = Ptr->getPointeeType(); else if (BaseType->isObjCObjectPointerType()) - /*Do nothing*/ ; + /*Do nothing*/ ; else return; } @@ -2574,6 +2615,10 @@ void Sema::CodeCompleteMemberReferenceExpr(Scope *S, ExprTy *BaseE, ResultBuilder Results(*this, &ResultBuilder::IsMember); Results.EnterNewScope(); if (const RecordType *Record = BaseType->getAs()) { + // Indicate that we are performing a member access, and the cv-qualifiers + // for the base object type. + Results.setObjectTypeQualifiers(BaseType.getQualifiers()); + // Access to a C/C++ class, struct, or union. Results.allowNestedNameSpecifiers(); CodeCompletionDeclConsumer Consumer(Results, CurContext); diff --git a/test/Index/complete-memfunc-cvquals.cpp b/test/Index/complete-memfunc-cvquals.cpp new file mode 100644 index 0000000000..08acdd18c1 --- /dev/null +++ b/test/Index/complete-memfunc-cvquals.cpp @@ -0,0 +1,78 @@ +// The run lines are below, because this test is line- and +// column-number sensitive. +struct Foo { + void babble() const volatile; + void bar(); + void baz() const; + void bingo() volatile; + void theend() const volatile; +}; + +template +struct smart_ptr { + T *operator->(); + const T* operator->() const; +}; + +void text(Foo f, Foo *fp, const Foo &fc, const Foo *fcp, + smart_ptr sf, const smart_ptr &sfc, Foo volatile *fvp) { + f.bar(); + fp->bar(); + fc.baz(); + fcp->baz(); + sf->bar(); + sfc->baz(); + fvp->babble(); +} + +void Foo::bar() { + +} + +void Foo::baz() const { + +} + +void Foo::bingo() volatile { + +} + +// Check member access expressions. +// RUN: c-index-test -code-completion-at=%s:19:5 %s | FileCheck -check-prefix=CHECK-NOQUALS %s +// RUN: c-index-test -code-completion-at=%s:20:7 %s | FileCheck -check-prefix=CHECK-NOQUALS %s +// RUN: c-index-test -code-completion-at=%s:23:7 %s | FileCheck -check-prefix=CHECK-NOQUALS %s +// CHECK-NOQUALS: FunctionDecl:{ResultType void}{TypedText babble}{LeftParen (}{RightParen )}{Informative const volatile} (20) +// CHECK-NOQUALS: FunctionDecl:{ResultType void}{TypedText bar}{LeftParen (}{RightParen )} (19) +// CHECK-NOQUALS: FunctionDecl:{ResultType void}{TypedText baz}{LeftParen (}{RightParen )}{Informative const} (20) +// CHECK-NOQUALS: FunctionDecl:{ResultType void}{TypedText bingo}{LeftParen (}{RightParen )}{Informative volatile} (20) +// RUN: c-index-test -code-completion-at=%s:21:6 %s | FileCheck -check-prefix=CHECK-CONST %s +// RUN: c-index-test -code-completion-at=%s:22:8 %s | FileCheck -check-prefix=CHECK-CONST %s +// RUN: c-index-test -code-completion-at=%s:24:8 %s | FileCheck -check-prefix=CHECK-CONST %s +// CHECK-CONST: FunctionDecl:{ResultType void}{TypedText babble}{LeftParen (}{RightParen )}{Informative const volatile} (20) +// CHECK-CONST-NOT: bar +// CHECK-CONST: FunctionDecl:{ResultType void}{TypedText baz}{LeftParen (}{RightParen )}{Informative const} (19) +// CHECK-CONST-NOT: bingo +// CHECK-CONST: theend +// RUN: c-index-test -code-completion-at=%s:25:8 %s | FileCheck -check-prefix=CHECK-VOLATILE %s +// CHECK-VOLATILE: FunctionDecl:{ResultType void}{TypedText babble}{LeftParen (}{RightParen )}{Informative const volatile} (20) +// CHECK-VOLATILE-NOT: baz +// CHECK-VOLATILE: FunctionDecl:{ResultType void}{TypedText bingo}{LeftParen (}{RightParen )}{Informative volatile} (19) + +// Check implicit member access expressions. +// RUN: c-index-test -code-completion-at=%s:29:2 %s | FileCheck -check-prefix=CHECK-IMPLICIT-NOQUALS %s +// CHECK-IMPLICIT-NOQUALS: FunctionDecl:{ResultType void}{TypedText babble}{LeftParen (}{RightParen )}{Informative const volatile} (15) +// CHECK-IMPLICIT-NOQUALS: FunctionDecl:{ResultType void}{TypedText bar}{LeftParen (}{RightParen )} (14) +// CHECK-IMPLICIT-NOQUALS: FunctionDecl:{ResultType void}{TypedText baz}{LeftParen (}{RightParen )}{Informative const} (15) +// CHECK-IMPLICIT-NOQUALS: FunctionDecl:{ResultType void}{TypedText bingo}{LeftParen (}{RightParen )}{Informative volatile} (15) + +// RUN: c-index-test -code-completion-at=%s:33:1 %s | FileCheck -check-prefix=CHECK-IMPLICIT-CONST %s +// CHECK-IMPLICIT-CONST: FunctionDecl:{ResultType void}{TypedText babble}{LeftParen (}{RightParen )}{Informative const volatile} (15) +// CHECK-IMPLICIT-CONST-NOT: bar +// CHECK-IMPLICIT-CONST: FunctionDecl:{ResultType void}{TypedText baz}{LeftParen (}{RightParen )}{Informative const} (14) +// CHECK-IMPLICIT-CONST-NOT: bingo +// CHECK-IMPLICIT-CONST: theend + +// RUN: c-index-test -code-completion-at=%s:37:1 %s | FileCheck -check-prefix=CHECK-IMPLICIT-VOLATILE %s +// CHECK-IMPLICIT-VOLATILE: FunctionDecl:{ResultType void}{TypedText babble}{LeftParen (}{RightParen )}{Informative const volatile} (15) +// CHECK-IMPLICIT-VOLATILE-NOT: baz +// CHECK-IMPLICIT-VOLATILE: FunctionDecl:{ResultType void}{TypedText bingo}{LeftParen (}{RightParen )}{Informative volatile} (14)