From 71e6088be40164f0f191dd3b666926b097b61778 Mon Sep 17 00:00:00 2001 From: Alex Lorenz Date: Tue, 9 Mar 2021 13:18:19 -0800 Subject: [PATCH 1/2] [clang][ObjC] allow the use of NSAttributedString * return type with format_arg attribute This is useful for APIs that want to produce an attributed NSString as a result of some formatting API call. --- clang/lib/Sema/SemaDeclAttr.cpp | 8 ++++++-- clang/test/SemaObjC/format-arg-attribute.m | 3 +++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp index 9e67e1365f717..a3812efad7e9f 100644 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -153,7 +153,8 @@ static bool isInstanceMethod(const Decl *D) { return false; } -static inline bool isNSStringType(QualType T, ASTContext &Ctx) { +static inline bool isNSStringType(QualType T, ASTContext &Ctx, + bool AllowNSAttributedString = false) { const auto *PT = T->getAs(); if (!PT) return false; @@ -164,6 +165,9 @@ static inline bool isNSStringType(QualType T, ASTContext &Ctx) { IdentifierInfo* ClsName = Cls->getIdentifier(); + if (AllowNSAttributedString && + ClsName == &Ctx.Idents.get("NSAttributedString")) + return true; // FIXME: Should we walk the chain of classes? return ClsName == &Ctx.Idents.get("NSString") || ClsName == &Ctx.Idents.get("NSMutableString"); @@ -3328,7 +3332,7 @@ static void handleFormatArgAttr(Sema &S, Decl *D, const ParsedAttr &AL) { return; } Ty = getFunctionOrMethodResultType(D); - if (!isNSStringType(Ty, S.Context) && + if (!isNSStringType(Ty, S.Context, /*AllowNSAttributedString=*/true) && !isCFStringType(Ty, S.Context) && (!Ty->isPointerType() || !Ty->castAs()->getPointeeType()->isCharType())) { diff --git a/clang/test/SemaObjC/format-arg-attribute.m b/clang/test/SemaObjC/format-arg-attribute.m index 67c9c2e3d4c97..ac81bdc21dc16 100644 --- a/clang/test/SemaObjC/format-arg-attribute.m +++ b/clang/test/SemaObjC/format-arg-attribute.m @@ -1,6 +1,7 @@ // RUN: %clang_cc1 -verify -fsyntax-only %s @class NSString; +@class NSAttributedString; extern NSString *fa2 (const NSString *) __attribute__((format_arg(1))); extern NSString *fa3 (NSString *) __attribute__((format_arg(1))); @@ -25,3 +26,5 @@ extern int fi3 (const NSString *) __attribute__((format_arg(1))); // expected-error {{function does not return NSString}} extern NSString *fi4 (const NSString *) __attribute__((format_arg(1))); extern NSString *fi5 (const NSString *) __attribute__((format_arg(1))); + +extern NSAttributedString *fattrs (const NSString *) __attribute__((format_arg(1))); From dd3ac30d719c1bc4515039ba3a6b119c7245a0f0 Mon Sep 17 00:00:00 2001 From: Alex Lorenz Date: Tue, 9 Mar 2021 14:42:07 -0800 Subject: [PATCH 2/2] [clang][driver] Support Darwin SDK names with an optional prefix in their name rdar://74017977 --- clang/lib/Driver/ToolChains/Darwin.cpp | 52 +++++++++++++++------- clang/test/Driver/darwin-sdk-with-prefix.c | 10 +++++ 2 files changed, 45 insertions(+), 17 deletions(-) create mode 100644 clang/test/Driver/darwin-sdk-with-prefix.c diff --git a/clang/lib/Driver/ToolChains/Darwin.cpp b/clang/lib/Driver/ToolChains/Darwin.cpp index d10c6009e397a..80c3ca4566ddb 100644 --- a/clang/lib/Driver/ToolChains/Darwin.cpp +++ b/clang/lib/Driver/ToolChains/Darwin.cpp @@ -1690,6 +1690,15 @@ getDeploymentTargetFromEnvironmentVariables(const Driver &TheDriver, return None; } +/// Returns the SDK name without the optional prefix that ends with a '.' or an +/// empty string otherwise. +static StringRef dropSDKNamePrefix(StringRef SDKName) { + size_t PrefixPos = SDKName.find('.'); + if (PrefixPos == StringRef::npos) + return ""; + return SDKName.substr(PrefixPos + 1); +} + /// Tries to infer the deployment target from the SDK specified by -isysroot /// (or SDKROOT). Uses the version specified in the SDKSettings.json file if /// it's available. @@ -1719,22 +1728,29 @@ inferDeploymentTargetFromSDK(DerivedArgList &Args, if (Version.empty()) return None; - if (SDK.startswith("iPhoneOS") || SDK.startswith("iPhoneSimulator")) - return DarwinPlatform::createFromSDK( - Darwin::IPhoneOS, Version, - /*IsSimulator=*/SDK.startswith("iPhoneSimulator")); - else if (SDK.startswith("MacOSX")) - return DarwinPlatform::createFromSDK(Darwin::MacOS, - getSystemOrSDKMacOSVersion(Version)); - else if (SDK.startswith("WatchOS") || SDK.startswith("WatchSimulator")) - return DarwinPlatform::createFromSDK( - Darwin::WatchOS, Version, - /*IsSimulator=*/SDK.startswith("WatchSimulator")); - else if (SDK.startswith("AppleTVOS") || SDK.startswith("AppleTVSimulator")) - return DarwinPlatform::createFromSDK( - Darwin::TvOS, Version, - /*IsSimulator=*/SDK.startswith("AppleTVSimulator")); - return None; + auto CreatePlatformFromSDKName = + [&](StringRef SDK) -> Optional { + if (SDK.startswith("iPhoneOS") || SDK.startswith("iPhoneSimulator")) + return DarwinPlatform::createFromSDK( + Darwin::IPhoneOS, Version, + /*IsSimulator=*/SDK.startswith("iPhoneSimulator")); + else if (SDK.startswith("MacOSX")) + return DarwinPlatform::createFromSDK(Darwin::MacOS, + getSystemOrSDKMacOSVersion(Version)); + else if (SDK.startswith("WatchOS") || SDK.startswith("WatchSimulator")) + return DarwinPlatform::createFromSDK( + Darwin::WatchOS, Version, + /*IsSimulator=*/SDK.startswith("WatchSimulator")); + else if (SDK.startswith("AppleTVOS") || SDK.startswith("AppleTVSimulator")) + return DarwinPlatform::createFromSDK( + Darwin::TvOS, Version, + /*IsSimulator=*/SDK.startswith("AppleTVSimulator")); + return None; + }; + if (auto Result = CreatePlatformFromSDKName(SDK)) + return Result; + // The SDK can be an SDK variant with a name like `.`. + return CreatePlatformFromSDKName(dropSDKNamePrefix(SDK)); } std::string getOSVersion(llvm::Triple::OSType OS, const llvm::Triple &Triple, @@ -2000,7 +2016,9 @@ void Darwin::AddDeploymentTarget(DerivedArgList &Args) const { StringRef SDKName = SDK.slice(0, StartVer); // Don't warn about the macabi SDK. // FIXME: Can we warn here? - if (!SDKName.startswith(getPlatformFamily()) && Environment != MacABI) + if (!SDKName.startswith(getPlatformFamily()) && + !dropSDKNamePrefix(SDKName).startswith(getPlatformFamily()) + && Environment != MacABI) getDriver().Diag(diag::warn_incompatible_sysroot) << SDKName << getPlatformFamily(); } diff --git a/clang/test/Driver/darwin-sdk-with-prefix.c b/clang/test/Driver/darwin-sdk-with-prefix.c new file mode 100644 index 0000000000000..7619ded56b65a --- /dev/null +++ b/clang/test/Driver/darwin-sdk-with-prefix.c @@ -0,0 +1,10 @@ +// RUN: rm -rf %t.dir +// RUN: mkdir -p %t.dir + +// RUN: rm -rf %t.dir/prefix.iPhoneOS12.0.0.sdk +// RUN: mkdir -p %t.dir/prefix.iPhoneOS12.0.0.sdk +// RUN: %clang -c -isysroot %t.dir/prefix.iPhoneOS12.0.0.sdk -target arm64-apple-darwin %s -### 2>&1 | FileCheck %s +// RUN: env SDKROOT=%t.dir/prefix.iPhoneOS12.0.0.sdk %clang -c -target arm64-apple-darwin %s -### 2>&1 | FileCheck %s +// +// CHECK-NOT: warning: using sysroot for +// CHECK: "-triple" "arm64-apple-ios12.0.0"