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 fdf2d52..20c8d1f 100644
--- a/include/clang/AST/ASTContext.h
+++ b/include/clang/AST/ASTContext.h
@@ -1073,8 +1073,6 @@
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 5a69932..819d7d2 100644
--- a/include/clang/AST/Type.h
+++ b/include/clang/AST/Type.h
@@ -289,7 +289,18 @@
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 55bd576..27cda88 100644
--- a/include/clang/Sema/CodeCompleteConsumer.h
+++ b/include/clang/Sema/CodeCompleteConsumer.h
@@ -55,7 +55,7 @@
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 @@
///
/// 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 b75a7d0..3921526 100644
--- a/lib/Sema/SemaCodeComplete.cpp
+++ b/lib/Sema/SemaCodeComplete.cpp
@@ -136,11 +136,19 @@
/// 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;
+
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 @@
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 @@
if (!PreferredType.isNull())
AdjustResultPriorityForPreferredType(R);
+ if (HasObjectTypeQualifiers)
+ if (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);
}
@@ -2364,6 +2398,13 @@
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<CXXMethodDecl>(CurContext))
+ if (CurMethod->isInstance())
+ Results.setObjectTypeQualifiers(
+ Qualifiers::fromCVRMask(CurMethod->getTypeQualifiers()));
+
CodeCompletionDeclConsumer Consumer(Results, CurContext);
LookupVisibleDecls(S, LookupOrdinaryName, Consumer,
CodeCompleter->includeGlobals());
@@ -2566,7 +2607,7 @@
if (const PointerType *Ptr = BaseType->getAs<PointerType>())
BaseType = Ptr->getPointeeType();
else if (BaseType->isObjCObjectPointerType())
- /*Do nothing*/ ;
+ /*Do nothing*/ ;
else
return;
}
@@ -2574,6 +2615,10 @@
ResultBuilder Results(*this, &ResultBuilder::IsMember);
Results.EnterNewScope();
if (const RecordType *Record = BaseType->getAs<RecordType>()) {
+ // 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 0000000..08acdd1
--- /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<typename T>
+struct smart_ptr {
+ T *operator->();
+ const T* operator->() const;
+};
+
+void text(Foo f, Foo *fp, const Foo &fc, const Foo *fcp,
+ smart_ptr<Foo> sf, const smart_ptr<Foo> &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)