From 86b4268153c9e903065f23f533b67393a39f9503 Mon Sep 17 00:00:00 2001 From: Douglas Gregor Date: Fri, 19 Jun 2015 18:27:52 +0000 Subject: [PATCH] Code completion for nullability type specifiers. Another part of rdar://problem/18868820. llvm-svn: 240159 --- clang/lib/AST/DeclPrinter.cpp | 16 +------ clang/lib/Sema/SemaCodeComplete.cpp | 71 ++++++++++++++++++++++++------ clang/test/Index/complete-method-decls.m | 13 ++++++ clang/test/Index/complete-objc-message.m | 11 +++++ clang/test/Index/complete-property-flags.m | 12 +++++ clang/test/Index/complete-stmt.c | 2 + 6 files changed, 98 insertions(+), 27 deletions(-) diff --git a/clang/lib/AST/DeclPrinter.cpp b/clang/lib/AST/DeclPrinter.cpp index d6c03169..c3ce476 100644 --- a/clang/lib/AST/DeclPrinter.cpp +++ b/clang/lib/AST/DeclPrinter.cpp @@ -937,18 +937,6 @@ void DeclPrinter::VisitClassTemplateDecl(ClassTemplateDecl *D) { // Objective-C declarations //---------------------------------------------------------------------------- -/// Strip off the top-level nullability annotation, if it's there. -static Optional stripOuterNullability(QualType &T) { - if (auto attributed = dyn_cast(T.getTypePtr())) { - if (auto nullability = attributed->getImmediateNullability()) { - T = attributed->getModifiedType(); - return nullability; - } - } - - return None; - } - void DeclPrinter::PrintObjCMethodType(ASTContext &Ctx, Decl::ObjCDeclQualifier Quals, QualType T) { @@ -966,7 +954,7 @@ void DeclPrinter::PrintObjCMethodType(ASTContext &Ctx, if (Quals & Decl::ObjCDeclQualifier::OBJC_TQ_Oneway) Out << "oneway "; if (Quals & Decl::ObjCDeclQualifier::OBJC_TQ_CSNullability) { - if (auto nullability = stripOuterNullability(T)) { + if (auto nullability = AttributedType::stripOuterNullability(T)) { Out << getNullabilitySpelling(*nullability).substr(2) << ' '; } } @@ -1212,7 +1200,7 @@ void DeclPrinter::VisitObjCPropertyDecl(ObjCPropertyDecl *PDecl) { if (PDecl->getPropertyAttributes() & ObjCPropertyDecl::OBJC_PR_nullability) { - if (auto nullability = stripOuterNullability(T)) { + if (auto nullability = AttributedType::stripOuterNullability(T)) { if (*nullability == NullabilityKind::Unspecified && (PDecl->getPropertyAttributes() & ObjCPropertyDecl::OBJC_PR_null_resettable)) { diff --git a/clang/lib/Sema/SemaCodeComplete.cpp b/clang/lib/Sema/SemaCodeComplete.cpp index fd97809..ebb6bbc 100644 --- a/clang/lib/Sema/SemaCodeComplete.cpp +++ b/clang/lib/Sema/SemaCodeComplete.cpp @@ -1341,6 +1341,11 @@ static void AddTypeSpecifierResults(const LangOptions &LangOpts, 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, @@ -2097,7 +2102,8 @@ static void MaybeAddSentinel(Preprocessor &PP, } } -static std::string formatObjCParamQualifiers(unsigned ObjCQuals) { +static std::string formatObjCParamQualifiers(unsigned ObjCQuals, + QualType &Type) { std::string Result; if (ObjCQuals & Decl::OBJC_TQ_In) Result += "in "; @@ -2111,6 +2117,23 @@ static std::string formatObjCParamQualifiers(unsigned ObjCQuals) { 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; } @@ -2128,13 +2151,15 @@ static std::string FormatFunctionParameter(const PrintingPolicy &Policy, if (Param->getIdentifier() && !ObjCMethodParam && !SuppressName) Result = Param->getIdentifier()->getName(); - Param->getType().getAsStringInternal(Result, Policy); - + QualType Type = Param->getType(); if (ObjCMethodParam) { - Result = "(" + formatObjCParamQualifiers(Param->getObjCDeclQualifier()) - + Result + ")"; + Result = "(" + formatObjCParamQualifiers(Param->getObjCDeclQualifier(), + Type); + Result += Type.getAsString(Policy) + ")"; if (Param->getIdentifier() && !SuppressName) Result += Param->getIdentifier()->getName(); + } else { + Type.getAsStringInternal(Result, Policy); } return Result; } @@ -2182,13 +2207,16 @@ static std::string FormatFunctionParameter(const PrintingPolicy &Policy, if (!ObjCMethodParam && Param->getIdentifier()) Result = Param->getIdentifier()->getName(); - Param->getType().getUnqualifiedType().getAsStringInternal(Result, Policy); + QualType Type = Param->getType().getUnqualifiedType(); if (ObjCMethodParam) { - Result = "(" + formatObjCParamQualifiers(Param->getObjCDeclQualifier()) - + Result + ")"; + Result = "(" + formatObjCParamQualifiers(Param->getObjCDeclQualifier(), + Type); + Result += Type.getAsString(Policy) + Result + ")"; if (Param->getIdentifier()) Result += Param->getIdentifier()->getName(); + } else { + Type.getAsStringInternal(Result, Policy); } return Result; @@ -2762,9 +2790,10 @@ CodeCompletionResult::CreateCodeCompletionString(ASTContext &Ctx, if ((*P)->getType()->isBlockPointerType() && !DeclaringEntity) Arg = FormatFunctionParameter(Policy, *P, true); else { - (*P)->getType().getAsStringInternal(Arg, Policy); - Arg = "(" + formatObjCParamQualifiers((*P)->getObjCDeclQualifier()) - + Arg + ")"; + QualType Type = (*P)->getType(); + Arg = "(" + formatObjCParamQualifiers((*P)->getObjCDeclQualifier(), + Type); + Arg += Type.getAsString(Policy) + ")"; if (IdentifierInfo *II = (*P)->getIdentifier()) if (DeclaringEntity || AllParametersAreInformative) Arg += II->getName(); @@ -4858,6 +4887,12 @@ void Sema::CodeCompleteObjCPropertyFlags(Scope *S, ObjCDeclSpec &ODS) { Getter.AddPlaceholderChunk("method"); Results.AddResult(CodeCompletionResult(Getter.TakeString())); } + if (!ObjCPropertyFlagConflicts(Attributes, ObjCDeclSpec::DQ_PR_nullability)) { + Results.AddResult(CodeCompletionResult("nonnull")); + Results.AddResult(CodeCompletionResult("nullable")); + Results.AddResult(CodeCompletionResult("null_unspecified")); + Results.AddResult(CodeCompletionResult("null_resettable")); + } Results.ExitScope(); HandleCodeCompleteResults(this, CodeCompleter, CodeCompletionContext::CCC_Other, @@ -5107,6 +5142,11 @@ void Sema::CodeCompleteObjCPassingType(Scope *S, ObjCDeclSpec &DS, Results.AddResult("byref"); Results.AddResult("oneway"); } + if ((DS.getObjCDeclQualifier() & ObjCDeclSpec::DQ_CSNullability) == 0) { + Results.AddResult("nonnull"); + Results.AddResult("nullable"); + Results.AddResult("null_unspecified"); + } // If we're completing the return type of an Objective-C method and the // identifier IBAction refers to a macro, provide a completion item for @@ -6279,7 +6319,7 @@ static void AddObjCPassingTypeChunk(QualType Type, const PrintingPolicy &Policy, CodeCompletionBuilder &Builder) { Builder.AddChunk(CodeCompletionString::CK_LeftParen); - std::string Quals = formatObjCParamQualifiers(ObjCDeclQuals); + std::string Quals = formatObjCParamQualifiers(ObjCDeclQuals, Type); if (!Quals.empty()) Builder.AddTextChunk(Builder.getAllocator().CopyString(Quals)); Builder.AddTextChunk(GetCompletionTypeString(Type, Context, Policy, @@ -7018,7 +7058,12 @@ void Sema::CodeCompleteObjCMethodDecl(Scope *S, break; // Add the parameter type. - AddObjCPassingTypeChunk((*P)->getOriginalType(), + QualType ParamType; + if ((*P)->getObjCDeclQualifier() & Decl::OBJC_TQ_CSNullability) + ParamType = (*P)->getType(); + else + ParamType = (*P)->getOriginalType(); + AddObjCPassingTypeChunk(ParamType, (*P)->getObjCDeclQualifier(), Context, Policy, Builder); diff --git a/clang/test/Index/complete-method-decls.m b/clang/test/Index/complete-method-decls.m index eceaa83..0e3780b 100644 --- a/clang/test/Index/complete-method-decls.m +++ b/clang/test/Index/complete-method-decls.m @@ -82,6 +82,14 @@ typedef A *MyObjectRef; @end @implementation I1 +-(void)foo {} +@end + +@interface I2 +-(nonnull I2 *)produceI2:(nullable I2 *)i2; +@end + +@implementation I2 - @end @@ -153,6 +161,8 @@ typedef A *MyObjectRef; // CHECK-CCF: NotImplemented:{TypedText byref} (40) // CHECK-CCF: NotImplemented:{TypedText in} (40) // CHECK-CCF: NotImplemented:{TypedText inout} (40) +// CHECK-CCF: NotImplemented:{TypedText nonnull} (40) +// CHECK-CCF: NotImplemented:{TypedText nullable} (40) // CHECK-CCF: NotImplemented:{TypedText oneway} (40) // CHECK-CCF: NotImplemented:{TypedText out} (40) // CHECK-CCF: NotImplemented:{TypedText unsigned} (50) @@ -201,3 +211,6 @@ typedef A *MyObjectRef; // FIXME: It should be "MyObject *"" // CHECK-CLASSTY: ObjCInstanceMethodDecl:{LeftParen (}{Text A *}{RightParen )}{TypedText meth2} // CHECK-CLASSTY: ObjCInstanceMethodDecl:{LeftParen (}{Text MyObjectRef}{RightParen )}{TypedText meth3} + +// RUN: c-index-test -code-completion-at=%s:93:2 %s | FileCheck -check-prefix=CHECK-NULLABILITY %s +// CHECK-NULLABILITY: ObjCInstanceMethodDecl:{LeftParen (}{Text nonnull }{Text I2 *}{RightParen )}{TypedText produceI2}{TypedText :}{LeftParen (}{Text nullable }{Text I2 *}{RightParen )}{Text i2} (40) diff --git a/clang/test/Index/complete-objc-message.m b/clang/test/Index/complete-objc-message.m index 5a72005..a62b491 100644 --- a/clang/test/Index/complete-objc-message.m +++ b/clang/test/Index/complete-objc-message.m @@ -189,6 +189,14 @@ void test_DO(DO *d, A* a) { [d method:a aout:&a]; } +@interface Nullability +- (nonnull A *)method:(nullable A *)param; +@end + +void test_Nullability(Nullability *n, A* a) { + [n method: a]; +} + // RUN: c-index-test -code-completion-at=%s:23:19 %s | FileCheck -check-prefix=CHECK-CC1 %s // CHECK-CC1: {TypedText categoryClassMethod} (35) // CHECK-CC1: {TypedText classMethod1:}{Placeholder (id)}{HorizontalSpace }{TypedText withKeyword:}{Placeholder (int)} (35) @@ -335,3 +343,6 @@ void test_DO(DO *d, A* a) { // RUN: c-index-test -code-completion-at=%s:189:6 %s | FileCheck -check-prefix=CHECK-DISTRIB-OBJECTS %s // CHECK-DISTRIB-OBJECTS: ObjCInstanceMethodDecl:{ResultType void}{TypedText method:}{Placeholder (in bycopy A *)}{HorizontalSpace }{TypedText result:}{Placeholder (out byref A **)} (35) + +// RUN: c-index-test -code-completion-at=%s:197:6 %s | FileCheck -check-prefix=CHECK-NULLABLE %s +// CHECK-NULLABLE: ObjCInstanceMethodDecl:{ResultType A * __nonnull}{TypedText method:}{Placeholder (nullable A *)} diff --git a/clang/test/Index/complete-property-flags.m b/clang/test/Index/complete-property-flags.m index 13ec1e7..9e3fc1a 100644 --- a/clang/test/Index/complete-property-flags.m +++ b/clang/test/Index/complete-property-flags.m @@ -13,6 +13,10 @@ // CHECK-CC1-NEXT: {TypedText copy} // CHECK-CC1-NEXT: {TypedText getter}{Text =}{Placeholder method} // CHECK-CC1-NEXT: {TypedText nonatomic} +// CHECK-CC1: {TypedText nonnull} +// CHECK-CC1-NEXT: {TypedText null_resettable} +// CHECK-CC1-NEXT: {TypedText null_unspecified} +// CHECK-CC1-NEXT: {TypedText nullable} // CHECK-CC1-NEXT: {TypedText readonly} // CHECK-CC1-NEXT: {TypedText readwrite} // CHECK-CC1-NEXT: {TypedText retain} @@ -27,6 +31,10 @@ // CHECK-CC1-ARC-NEXT: {TypedText copy} // CHECK-CC1-ARC-NEXT: {TypedText getter}{Text =}{Placeholder method} // CHECK-CC1-ARC-NEXT: {TypedText nonatomic} +// CHECK-CC1-ARC-NEXT: {TypedText nonnull} +// CHECK-CC1-ARC-NEXT: {TypedText null_resettable} +// CHECK-CC1-ARC-NEXT: {TypedText null_unspecified} +// CHECK-CC1-ARC-NEXT: {TypedText nullable} // CHECK-CC1-ARC-NEXT: {TypedText readonly} // CHECK-CC1-ARC-NEXT: {TypedText readwrite} // CHECK-CC1-ARC-NEXT: {TypedText retain} @@ -38,6 +46,10 @@ // RUN: c-index-test -code-completion-at=%s:8:18 %s | FileCheck -check-prefix=CHECK-CC2 %s // CHECK-CC2: {TypedText getter}{Text =}{Placeholder method} // CHECK-CC2-NEXT: {TypedText nonatomic} +// CHECK-CC2-NEXT: {TypedText nonnull} +// CHECK-CC2-NEXT: {TypedText null_resettable} +// CHECK-CC2-NEXT: {TypedText null_unspecified} +// CHECK-CC2-NEXT: {TypedText nullable} // CHECK-CC2-NEXT: {TypedText readonly} // CHECK-CC2-NEXT: {TypedText readwrite} // CHECK-CC2-NEXT: {TypedText setter}{Text =}{Placeholder method} diff --git a/clang/test/Index/complete-stmt.c b/clang/test/Index/complete-stmt.c index 3d31ca2..8bbfe2d 100644 --- a/clang/test/Index/complete-stmt.c +++ b/clang/test/Index/complete-stmt.c @@ -16,6 +16,8 @@ void f(int x) { // CHECK-IF-ELSE-SIMPLE: NotImplemented:{TypedText else}{HorizontalSpace }{Text if}{HorizontalSpace }{LeftParen (}{Placeholder expression}{RightParen )} (40) // RUN: c-index-test -code-completion-at=%s:6:1 %s | FileCheck -check-prefix=CHECK-STMT %s +// CHECK-STMT: NotImplemented:{TypedText __nonnull} (50) +// CHECK-STMT: NotImplemented:{TypedText __nullable} (50) // CHECK-STMT: NotImplemented:{TypedText char} (50) // CHECK-STMT: NotImplemented:{TypedText const} (50) // CHECK-STMT: NotImplemented:{TypedText double} (50) -- 2.7.4