From 15021003e58634d63c173680b6c2d2b53fe1adf7 Mon Sep 17 00:00:00 2001 From: Daniel Boyd Date: Fri, 3 Apr 2026 13:17:36 -0500 Subject: [PATCH 01/12] Add phase 4B collections nullability annotations --- Headers/Foundation/NSArray.h | 50 ++++++++++++++++------------ Headers/Foundation/NSDictionary.h | 54 +++++++++++++++++++------------ Headers/Foundation/NSSet.h | 22 +++++++++---- 3 files changed, 77 insertions(+), 49 deletions(-) diff --git a/Headers/Foundation/NSArray.h b/Headers/Foundation/NSArray.h index 9f32070779..c34213181b 100644 --- a/Headers/Foundation/NSArray.h +++ b/Headers/Foundation/NSArray.h @@ -56,19 +56,22 @@ enum typedef NSUInteger NSBinarySearchingOptions; #endif +NS_ASSUME_NONNULL_BEGIN + GS_EXPORT_CLASS @interface GS_GENERIC_CLASS(NSArray, __covariant ElementT) : NSObject + (instancetype) array; + (instancetype) arrayWithArray: (GS_GENERIC_CLASS(NSArray, ElementT) *)array; -+ (instancetype) arrayWithContentsOfFile: (NSString*)file; ++ (instancetype _Nullable) arrayWithContentsOfFile: (NSString*)file; #if OS_API_VERSION(GS_API_MACOSX, GS_API_LATEST) -+ (instancetype) arrayWithContentsOfURL: (NSURL*)aURL; ++ (instancetype _Nullable) arrayWithContentsOfURL: (NSURL*)aURL; #endif + (instancetype) arrayWithObject: (id)anObject; + (instancetype) arrayWithObjects: (id)firstObject, ...; -+ (instancetype) arrayWithObjects: (const id[])objects count: (NSUInteger)count; ++ (instancetype) arrayWithObjects: (const id _Nonnull [])objects + count: (NSUInteger)count; - (GS_GENERIC_CLASS(NSArray, ElementT) *) arrayByAddingObject: (GS_GENERIC_TYPE(ElementT))anObject; @@ -80,8 +83,10 @@ GS_EXPORT_CLASS * Returns the number of elements contained in the receiver. */ - (NSUInteger) count; -- (void) getObjects: (__unsafe_unretained GS_GENERIC_TYPE(ElementT)[])aBuffer; -- (void) getObjects: (__unsafe_unretained GS_GENERIC_TYPE(ElementT)[])aBuffer +- (void) getObjects: + (__unsafe_unretained GS_GENERIC_TYPE(ElementT) _Nonnull [])aBuffer; +- (void) getObjects: + (__unsafe_unretained GS_GENERIC_TYPE(ElementT) _Nonnull [])aBuffer range: (NSRange)aRange; - (NSUInteger) indexOfObject: (GS_GENERIC_TYPE(ElementT))anObject; - (NSUInteger) indexOfObject: (GS_GENERIC_TYPE(ElementT))anObject @@ -95,9 +100,9 @@ GS_EXPORT_CLASS - (instancetype) initWithArray: (GS_GENERIC_CLASS(NSArray, ElementT)*)array copyItems: (BOOL)shouldCopy; #endif -- (instancetype) initWithContentsOfFile: (NSString*)file; +- (instancetype _Nullable) initWithContentsOfFile: (NSString*)file; #if OS_API_VERSION(GS_API_MACOSX, GS_API_LATEST) -- (instancetype) initWithContentsOfURL: (NSURL*)aURL; +- (instancetype _Nullable) initWithContentsOfURL: (NSURL*)aURL; #endif - (instancetype) initWithObjects: (GS_GENERIC_TYPE(ElementT)) firstObject, ...; @@ -108,11 +113,12 @@ GS_EXPORT_CLASS * and needs to be re-implemented in subclasses in order to have all * other initialisers work. */ -- (instancetype) initWithObjects: (const GS_GENERIC_TYPE(ElementT)[])objects +- (instancetype) initWithObjects: + (const GS_GENERIC_TYPE(ElementT) _Nonnull [])objects count: (NSUInteger)count; -- (GS_GENERIC_TYPE(ElementT)) lastObject; +- (GS_GENERIC_TYPE(ElementT) _Nullable) lastObject; #if OS_API_VERSION(MAC_OS_X_VERSION_10_6, GS_API_LATEST) -- (GS_GENERIC_TYPE(ElementT)) firstObject; +- (GS_GENERIC_TYPE(ElementT) _Nullable) firstObject; #endif /** @@ -129,27 +135,27 @@ GS_EXPORT_CLASS (NSIndexSet *)indexes; #endif -- (GS_GENERIC_TYPE(ElementT)) firstObjectCommonWithArray: +- (GS_GENERIC_TYPE(ElementT) _Nullable) firstObjectCommonWithArray: (GS_GENERIC_CLASS(NSArray, ElementT) *)otherArray; - (BOOL) isEqualToArray: (NSArray*)otherArray; #if OS_API_VERSION(GS_API_OPENSTEP, GS_API_MACOSX) - (void) makeObjectsPerform: (SEL)aSelector; -- (void) makeObjectsPerform: (SEL)aSelector withObject: (id)argument; +- (void) makeObjectsPerform: (SEL)aSelector withObject: (id _Nullable)argument; #endif #if OS_API_VERSION(GS_API_MACOSX, GS_API_LATEST) - (void) makeObjectsPerformSelector: (SEL)aSelector; -- (void) makeObjectsPerformSelector: (SEL)aSelector withObject: (id)arg; +- (void) makeObjectsPerformSelector: (SEL)aSelector withObject: (id _Nullable)arg; #endif -- (NSData*) sortedArrayHint; +- (NSData *_Nullable) sortedArrayHint; - (GS_GENERIC_CLASS(NSArray, ElementT)*) sortedArrayUsingFunction: (NSComparisonResult (*)(id, id, void*))comparator - context: (void*)context; + context: (void *_Nullable)context; - (GS_GENERIC_CLASS(NSArray, ElementT)*) sortedArrayUsingFunction: (NSComparisonResult (*)(id, id, void*))comparator - context: (void*)context - hint: (NSData*)hint; + context: (void *_Nullable)context + hint: (NSData *_Nullable)hint; - (GS_GENERIC_CLASS(NSArray, ElementT)*) sortedArrayUsingSelector: (SEL)comparator; - (GS_GENERIC_CLASS(NSArray, ElementT)*) subarrayWithRange: (NSRange)aRange; @@ -162,15 +168,15 @@ GS_EXPORT_CLASS - (GS_GENERIC_CLASS(NSEnumerator, ElementT)*) reverseObjectEnumerator; - (NSString*) description; -- (NSString*) descriptionWithLocale: (id)locale; -- (NSString*) descriptionWithLocale: (id)locale +- (NSString*) descriptionWithLocale: (id _Nullable)locale; +- (NSString*) descriptionWithLocale: (id _Nullable)locale indent: (NSUInteger)level; - (BOOL) writeToFile: (NSString*)path atomically: (BOOL)useAuxiliaryFile; #if OS_API_VERSION(GS_API_MACOSX, GS_API_LATEST) - (BOOL) writeToURL: (NSURL*)url atomically: (BOOL)useAuxiliaryFile; - (id) valueForKey: (NSString*)key; -- (void) setValue: (id)value forKey: (NSString*)key; +- (void) setValue: (id _Nullable)value forKey: (NSString*)key; #endif #if OS_API_VERSION(MAC_OS_X_VERSION_10_6, GS_API_LATEST) @@ -389,7 +395,7 @@ GS_EXPORT_CLASS - (void) sortUsingFunction: (NSComparisonResult (*)(GS_GENERIC_TYPE(ElementT), GS_GENERIC_TYPE(ElementT),void*))compare - context: (void*)context; + context: (void *_Nullable)context; - (void) sortUsingSelector: (SEL)comparator; @@ -415,6 +421,8 @@ atIndexedSubscript: (NSUInteger)anIndex; #endif @end +NS_ASSUME_NONNULL_END + #if defined(__cplusplus) } #endif diff --git a/Headers/Foundation/NSDictionary.h b/Headers/Foundation/NSDictionary.h index 9cdbbf9fe8..18f816818f 100644 --- a/Headers/Foundation/NSDictionary.h +++ b/Headers/Foundation/NSDictionary.h @@ -36,34 +36,38 @@ extern "C" { @class GS_GENERIC_CLASS(NSSet, ElementT); @class NSString, NSURL; +NS_ASSUME_NONNULL_BEGIN + GS_EXPORT_CLASS @interface GS_GENERIC_CLASS(NSDictionary, __covariant KeyT:id, __covariant ValT) : NSObject + (instancetype) dictionary; -+ (instancetype) dictionaryWithContentsOfFile: (NSString*)path; ++ (instancetype _Nullable) dictionaryWithContentsOfFile: (NSString*)path; #if OS_API_VERSION(GS_API_MACOSX, GS_API_LATEST) -+ (instancetype) dictionaryWithContentsOfURL: (NSURL*)aURL; ++ (instancetype _Nullable) dictionaryWithContentsOfURL: (NSURL*)aURL; #endif + (instancetype) dictionaryWithDictionary: (NSDictionary*)otherDictionary; + (instancetype) dictionaryWithObject: (GS_GENERIC_TYPE(ValT))object forKey: (GS_GENERIC_TYPE(KeyT))key; + (instancetype) dictionaryWithObjects: (GS_GENERIC_CLASS(NSArray,ValT)*)objects forKeys: (GS_GENERIC_CLASS(NSArray,KeyT)*)keys; -+ (instancetype) dictionaryWithObjects: (const GS_GENERIC_TYPE(ValT)[])objects - forKeys: (const GS_GENERIC_TYPE_F(KeyT,id)[])keys ++ (instancetype) dictionaryWithObjects: + (const GS_GENERIC_TYPE(ValT) _Nonnull [])objects + forKeys: + (const GS_GENERIC_TYPE_F(KeyT,id) _Nonnull [])keys count: (NSUInteger)count; + (instancetype) dictionaryWithObjectsAndKeys: (id)firstObject, ...; - (GS_GENERIC_CLASS(NSArray,KeyT)*) allKeys; -- (GS_GENERIC_CLASS(NSArray,KeyT)*) allKeysForObject: - (GS_GENERIC_TYPE(ValT))anObject; +- (GS_GENERIC_CLASS(NSArray,KeyT)* _Nullable) allKeysForObject: + (GS_GENERIC_TYPE(ValT) _Nullable)anObject; - (GS_GENERIC_CLASS(NSArray,ValT)*) allValues; - (NSUInteger) count; // Primitive - (NSString*) description; - (NSString*) descriptionInStringsFileFormat; -- (NSString*) descriptionWithLocale: (id)locale; -- (NSString*) descriptionWithLocale: (id)locale +- (NSString*) descriptionWithLocale: (id _Nullable)locale; +- (NSString*) descriptionWithLocale: (id _Nullable)locale indent: (NSUInteger)level; #if OS_API_VERSION(MAC_OS_X_VERSION_10_6, GS_API_LATEST) @@ -74,13 +78,15 @@ DEFINE_BLOCK_TYPE(GSKeysAndObjectsEnumeratorBlock, void, GS_GENERIC_TYPE_F(KeyT, usingBlock: (GSKeysAndObjectsEnumeratorBlock)aBlock; #endif -- (void) getObjects: (__unsafe_unretained GS_GENERIC_TYPE(ValT)[])objects - andKeys: (__unsafe_unretained GS_GENERIC_TYPE_F(KeyT,id)[])keys; +- (void) getObjects: + (__unsafe_unretained GS_GENERIC_TYPE(ValT) _Nullable [])objects + andKeys: + (__unsafe_unretained GS_GENERIC_TYPE_F(KeyT,id) _Nullable [])keys; - (instancetype) init; -- (instancetype) initWithContentsOfFile: (NSString*)path; +- (instancetype _Nullable) initWithContentsOfFile: (NSString*)path; #if OS_API_VERSION(GS_API_MACOSX, GS_API_LATEST) -- (instancetype) initWithContentsOfURL: (NSURL*)aURL; +- (instancetype _Nullable) initWithContentsOfURL: (NSURL*)aURL; #endif - (instancetype) initWithDictionary: @@ -90,8 +96,9 @@ DEFINE_BLOCK_TYPE(GSKeysAndObjectsEnumeratorBlock, void, GS_GENERIC_TYPE_F(KeyT, - (id) initWithObjects: (GS_GENERIC_CLASS(NSArray,KeyT)*)objects forKeys: (GS_GENERIC_CLASS(NSArray,ValT)*)keys; - (id) initWithObjectsAndKeys: (GS_GENERIC_TYPE(ValT))firstObject, ...; -- (id) initWithObjects: (const GS_GENERIC_TYPE(ValT)[])objects - forKeys: (const GS_GENERIC_TYPE_F(KeyT,id)[])keys +- (id) initWithObjects: (const GS_GENERIC_TYPE(ValT) _Nonnull [])objects + forKeys: + (const GS_GENERIC_TYPE_F(KeyT,id) _Nonnull [])keys count: (NSUInteger)count; // Primitive - (BOOL) isEqualToDictionary: (GS_GENERIC_CLASS(NSDictionary,KeyT, ValT)*)other; @@ -110,14 +117,14 @@ DEFINE_BLOCK_TYPE(GSKeysAndObjectsPredicateBlock, BOOL, GS_GENERIC_TYPE_F(KeyT,i - (GS_GENERIC_CLASS(NSArray,ValT)*) keysSortedByValueUsingComparator:(NSComparator)cmptr; - (GS_GENERIC_CLASS(NSArray,ValT)*) keysSortedByValueWithOptions:(NSSortOptions)opts usingComparator:(NSComparator)cmptr; - (GS_GENERIC_CLASS(NSEnumerator,ValT)*) objectEnumerator; // Primitive -- (GS_GENERIC_TYPE(ValT)) objectForKey: +- (GS_GENERIC_TYPE(ValT) _Nullable) objectForKey: (GS_GENERIC_TYPE(KeyT))aKey; // Primitive - (GS_GENERIC_CLASS(NSArray,ValT)*) objectsForKeys: (GS_GENERIC_CLASS(NSArray,KeyT)*)keys notFoundMarker: (GS_GENERIC_TYPE(ValT))marker; #if OS_API_VERSION(GS_API_MACOSX, GS_API_LATEST) -- (GS_GENERIC_TYPE(ValT)) valueForKey: (NSString*)key; +- (GS_GENERIC_TYPE(ValT) _Nullable) valueForKey: (NSString*)key; #endif - (BOOL) writeToFile: (NSString*)path atomically: (BOOL)useAuxiliaryFile; @@ -128,7 +135,7 @@ DEFINE_BLOCK_TYPE(GSKeysAndObjectsPredicateBlock, BOOL, GS_GENERIC_TYPE_F(KeyT,i /** * Method called by array subscripting. */ -- (GS_GENERIC_TYPE(ValT)) objectForKeyedSubscript: +- (GS_GENERIC_TYPE(ValT) _Nullable) objectForKeyedSubscript: (GS_GENERIC_TYPE(KeyT))aKey; @end @@ -153,18 +160,23 @@ GS_EXPORT_CLASS - (void) setDictionary: (GS_GENERIC_CLASS(NSDictionary, KeyT, ValT)*)otherDictionary; #if OS_API_VERSION(GS_API_MACOSX, GS_API_LATEST) -- (void) setValue: (GS_GENERIC_TYPE(ValT))value forKey: (NSString*)key; -- (void) takeStoredValue: (GS_GENERIC_TYPE(ValT))value forKey: (NSString*)key; -- (void) takeValue: (GS_GENERIC_TYPE(ValT))value forKey: (NSString*)key; +- (void) setValue: (GS_GENERIC_TYPE(ValT) _Nullable)value + forKey: (NSString*)key; +- (void) takeStoredValue: (GS_GENERIC_TYPE(ValT) _Nullable)value + forKey: (NSString*)key; +- (void) takeValue: (GS_GENERIC_TYPE(ValT) _Nullable)value + forKey: (NSString*)key; #endif /** * Method called by array subscripting. */ -- (void) setObject: (GS_GENERIC_TYPE(ValT))anObject +- (void) setObject: (GS_GENERIC_TYPE(ValT) _Nullable)anObject forKeyedSubscript: (GS_GENERIC_TYPE(KeyT))aKey; @end +NS_ASSUME_NONNULL_END + #if defined(__cplusplus) } #endif diff --git a/Headers/Foundation/NSSet.h b/Headers/Foundation/NSSet.h index 674d407a43..5325c1388a 100644 --- a/Headers/Foundation/NSSet.h +++ b/Headers/Foundation/NSSet.h @@ -42,6 +42,8 @@ extern "C" { @class GS_GENERIC_CLASS(NSDictionary, KeyT:id, ValT); @class NSString; +NS_ASSUME_NONNULL_BEGIN + GS_EXPORT_CLASS @interface GS_GENERIC_CLASS(NSSet, __covariant ElementT) : NSObject Date: Fri, 3 Apr 2026 13:11:40 -0500 Subject: [PATCH 02/12] Add phase 4B core Foundation nullability annotations Add narrow nullability annotations for the first Phase 4B core Foundation tranche without touching collection headers, module maps, or non-core surfaces. Keep the remaining judgment-call annotations conservative: - NSString legacy C-string decoding entry points stay nullable where decoding can return nil even though NULL C string inputs still raise. - NSURL initWithScheme:host:path: stays nullable because it funnels through initWithString:relativeToURL:, which can still fail for constructed non-ASCII URL text. - NSData bytes and mutableBytes stay nullable because zero-length concrete storage may legitimately expose a NULL backing pointer. --- Headers/Foundation/NSData.h | 66 +++++------ Headers/Foundation/NSError.h | 20 ++-- Headers/Foundation/NSJSONSerialization.h | 22 ++-- Headers/Foundation/NSObjCRuntime.h | 26 ++++- Headers/Foundation/NSObject.h | 50 +++++---- Headers/Foundation/NSString.h | 134 ++++++++++++----------- Headers/Foundation/NSURL.h | 76 ++++++------- 7 files changed, 217 insertions(+), 177 deletions(-) diff --git a/Headers/Foundation/NSData.h b/Headers/Foundation/NSData.h index 4c1210cc7e..d881bba486 100644 --- a/Headers/Foundation/NSData.h +++ b/Headers/Foundation/NSData.h @@ -98,37 +98,39 @@ enum { DEFINE_BLOCK_TYPE(GSDataDeallocatorBlock, void, void*, NSUInteger); #endif +NS_ASSUME_NONNULL_BEGIN + GS_EXPORT_CLASS @interface NSData : NSObject // Allocating and Initializing a Data Object + (instancetype) data; -+ (instancetype) dataWithBytes: (const void*)bytes ++ (instancetype) dataWithBytes: (const void *_Nullable)bytes length: (NSUInteger)length; -+ (instancetype) dataWithBytesNoCopy: (void*)bytes ++ (instancetype) dataWithBytesNoCopy: (void *_Nullable)bytes length: (NSUInteger)length; #if OS_API_VERSION(GS_API_MACOSX, GS_API_LATEST) -+ (instancetype) dataWithBytesNoCopy: (void*)aBuffer ++ (instancetype) dataWithBytesNoCopy: (void *_Nullable)aBuffer length: (NSUInteger)bufferSize freeWhenDone: (BOOL)shouldFree; #endif -+ (instancetype) dataWithContentsOfFile: (NSString *)path - options: (NSDataReadingOptions)readOptionsMask - error: (NSError **)errorPtr; -+ (instancetype) dataWithContentsOfFile: (NSString*)path; -+ (instancetype) dataWithContentsOfMappedFile: (NSString*)path; ++ (instancetype _Nullable) dataWithContentsOfFile: (NSString *)path + options: (NSDataReadingOptions)readOptionsMask + error: (NSError **)errorPtr; ++ (instancetype _Nullable) dataWithContentsOfFile: (NSString*)path; ++ (instancetype _Nullable) dataWithContentsOfMappedFile: (NSString*)path; #if OS_API_VERSION(GS_API_MACOSX, GS_API_LATEST) -+ (instancetype) dataWithContentsOfURL: (NSURL *)url - options: (NSDataReadingOptions)readOptionsMask - error: (NSError **)errorPtr; -+ (instancetype) dataWithContentsOfURL: (NSURL*)url; ++ (instancetype _Nullable) dataWithContentsOfURL: (NSURL *)url + options: (NSDataReadingOptions)readOptionsMask + error: (NSError **)errorPtr; ++ (instancetype _Nullable) dataWithContentsOfURL: (NSURL*)url; #endif + (instancetype) dataWithData: (NSData*)data; #if OS_API_VERSION(MAC_OS_X_VERSION_10_9,GS_API_LATEST) -- (instancetype) initWithBase64EncodedData: (NSData*)base64Data +- (instancetype _Nullable) initWithBase64EncodedData: (NSData*)base64Data options: (NSDataBase64DecodingOptions)options; -- (instancetype) initWithBase64EncodedString: (NSString*)base64String +- (instancetype _Nullable) initWithBase64EncodedString: (NSString*)base64String options: (NSDataBase64DecodingOptions)options; /** * @@ -141,31 +143,31 @@ GS_EXPORT_CLASS length: (NSUInteger)length deallocator: (GSDataDeallocatorBlock)deallocBlock; #endif -- (instancetype) initWithBytes: (const void*)aBuffer +- (instancetype) initWithBytes: (const void *_Nullable)aBuffer length: (NSUInteger)bufferSize; -- (instancetype) initWithBytesNoCopy: (void*)aBuffer +- (instancetype) initWithBytesNoCopy: (void *_Nullable)aBuffer length: (NSUInteger)bufferSize; #if OS_API_VERSION(GS_API_MACOSX, GS_API_LATEST) -- (instancetype) initWithBytesNoCopy: (void*)aBuffer +- (instancetype) initWithBytesNoCopy: (void *_Nullable)aBuffer length: (NSUInteger)bufferSize freeWhenDone: (BOOL)shouldFree; #endif -- (instancetype) initWithContentsOfFile: (NSString*)path; -- (instancetype) initWithContentsOfFile: (NSString *) path - options: (NSDataReadingOptions) readOptionsMask - error: (NSError **) errorPtr; -- (instancetype) initWithContentsOfMappedFile: (NSString*)path; +- (instancetype _Nullable) initWithContentsOfFile: (NSString*)path; +- (instancetype _Nullable) initWithContentsOfFile: (NSString *) path + options: (NSDataReadingOptions) readOptionsMask + error: (NSError **) errorPtr; +- (instancetype _Nullable) initWithContentsOfMappedFile: (NSString*)path; #if OS_API_VERSION(GS_API_MACOSX, GS_API_LATEST) -- (instancetype) initWithContentsOfURL: (NSURL*)url; -- (instancetype) initWithContentsOfURL: (NSURL *)url - options: (NSDataReadingOptions)readOptionsMask - error: (NSError **)errorPtr; +- (instancetype _Nullable) initWithContentsOfURL: (NSURL*)url; +- (instancetype _Nullable) initWithContentsOfURL: (NSURL *)url + options: (NSDataReadingOptions)readOptionsMask + error: (NSError **)errorPtr; #endif - (instancetype) initWithData: (NSData*)data; // Accessing Data -- (const void*) bytes; +- (const void *_Nullable) bytes; - (NSString*) description; - (void) getBytes: (void*)buffer; - (void) getBytes: (void*)buffer @@ -355,21 +357,21 @@ GS_EXPORT_CLASS - (void) increaseLengthBy: (NSUInteger)extraLength; - (void) setLength: (NSUInteger)size; -- (void*) mutableBytes; +- (void *_Nullable) mutableBytes; // Appending Data -- (void) appendBytes: (const void*)aBuffer +- (void) appendBytes: (const void *_Nullable)aBuffer length: (NSUInteger)bufferSize; - (void) appendData: (NSData*)other; // Modifying Data - (void) replaceBytesInRange: (NSRange)aRange - withBytes: (const void*)bytes; + withBytes: (const void *_Nullable)bytes; #if OS_API_VERSION(GS_API_MACOSX, GS_API_LATEST) - (void) replaceBytesInRange: (NSRange)aRange - withBytes: (const void*)bytes + withBytes: (const void *_Nullable)bytes length: (NSUInteger)length; #endif - (void) resetBytesInRange: (NSRange)aRange; @@ -392,6 +394,8 @@ GS_EXPORT_CLASS @end +NS_ASSUME_NONNULL_END + #if OS_API_VERSION(GS_API_NONE, GS_API_NONE) @interface NSMutableData (GNUstepExtensions) diff --git a/Headers/Foundation/NSError.h b/Headers/Foundation/NSError.h index df3ce5a31a..21459d68d0 100644 --- a/Headers/Foundation/NSError.h +++ b/Headers/Foundation/NSError.h @@ -37,6 +37,8 @@ extern "C" { @class NSArray, NSDictionary, NSString; +NS_ASSUME_NONNULL_BEGIN + typedef NSString* NSErrorDomain; /** @@ -135,7 +137,7 @@ GS_EXPORT_CLASS @private int _code; NSString *_domain; - NSDictionary *_userInfo; + NSDictionary *_Nullable _userInfo; #endif #if GS_NONFRAGILE #else @@ -154,7 +156,7 @@ GS_EXPORT_CLASS */ + (instancetype) errorWithDomain: (NSErrorDomain)aDomain code: (NSInteger)aCode - userInfo: (NSDictionary*)aDictionary; + userInfo: (NSDictionary *_Nullable)aDictionary; /** * Return the error code ... which is not globally unique, just unique for @@ -173,7 +175,7 @@ GS_EXPORT_CLASS */ - (instancetype) initWithDomain: (NSErrorDomain)aDomain code: (NSInteger)aCode - userInfo: (NSDictionary*)aDictionary; + userInfo: (NSDictionary *_Nullable)aDictionary; /** * Return a human readable description for the error.
@@ -191,7 +193,7 @@ GS_EXPORT_CLASS * The default implementation uses the value from the user info dictionary * if it is available, otherwise it returns nil. */ -- (NSString *) localizedFailureReason; +- (NSString *_Nullable) localizedFailureReason; /** * Returns an array of strings to be used as titles of buttons in an @@ -200,7 +202,7 @@ GS_EXPORT_CLASS * The default implementation uses the value from the user info dictionary * if it is available, otherwise it returns nil. */ -- (NSArray *) localizedRecoveryOptions; +- (NSArray *_Nullable) localizedRecoveryOptions; /** * Returns a string used as the secondary text in an alert panel, @@ -209,14 +211,14 @@ GS_EXPORT_CLASS * The default implementation uses the value from the user info dictionary * if it is available, otherwise it returns nil. */ -- (NSString *) localizedRecoverySuggestion; +- (NSString *_Nullable) localizedRecoverySuggestion; /** * Not yet useful in GNUstep.
* The default implementation uses the value from the user info dictionary * if it is available, otherwise it returns nil. */ -- (id) recoveryAttempter; +- (id _Nullable) recoveryAttempter; #endif /** @@ -227,9 +229,11 @@ GS_EXPORT_CLASS * NSError instance if an error is available describing any * underlying problem.
*/ -- (NSDictionary*) userInfo; +- (NSDictionary *_Nullable) userInfo; @end +NS_ASSUME_NONNULL_END + #if defined(__cplusplus) } #endif diff --git a/Headers/Foundation/NSJSONSerialization.h b/Headers/Foundation/NSJSONSerialization.h index 354ed6d623..d86debeae0 100644 --- a/Headers/Foundation/NSJSONSerialization.h +++ b/Headers/Foundation/NSJSONSerialization.h @@ -28,6 +28,8 @@ @class NSInputStream; @class NSOutputStream; +NS_ASSUME_NONNULL_BEGIN + enum { /** @@ -87,18 +89,20 @@ typedef NSUInteger NSJSONReadingOptions; */ GS_EXPORT_CLASS @interface NSJSONSerialization : NSObject -+ (NSData*) dataWithJSONObject: (id)obj - options: (NSJSONWritingOptions)opt - error: (NSError **)error; ++ (NSData *_Nullable) dataWithJSONObject: (id)obj + options: (NSJSONWritingOptions)opt + error: (NSError **)error; + (BOOL) isValidJSONObject: (id)obj; -+ (id) JSONObjectWithData: (NSData *)data - options: (NSJSONReadingOptions)opt - error: (NSError **)error; -+ (id) JSONObjectWithStream: (NSInputStream *)stream - options: (NSJSONReadingOptions)opt - error: (NSError **)error; ++ (id _Nullable) JSONObjectWithData: (NSData *)data + options: (NSJSONReadingOptions)opt + error: (NSError **)error; ++ (id _Nullable) JSONObjectWithStream: (NSInputStream *)stream + options: (NSJSONReadingOptions)opt + error: (NSError **)error; + (NSInteger) writeJSONObject: (id)obj toStream: (NSOutputStream *)stream options: (NSJSONWritingOptions)opt error: (NSError **)error; @end + +NS_ASSUME_NONNULL_END diff --git a/Headers/Foundation/NSObjCRuntime.h b/Headers/Foundation/NSObjCRuntime.h index b350baef3b..2ac67ce601 100644 --- a/Headers/Foundation/NSObjCRuntime.h +++ b/Headers/Foundation/NSObjCRuntime.h @@ -184,6 +184,16 @@ extern "C" { # define NS_SWIFT_NOTHROW #endif +/* + * Import a declaration with a specific Swift name when the compiler + * supports the attribute. + */ +#if __has_attribute(swift_name) +# define NS_SWIFT_NAME(_name) __attribute__((swift_name(#_name))) +#else +# define NS_SWIFT_NAME(_name) +#endif + /* * Backwards compatibility macro for instance type. */ @@ -243,17 +253,21 @@ typedef NS_OPTIONS(NSUInteger, NSSortOptions) #import +NS_ASSUME_NONNULL_BEGIN + #if OS_API_VERSION(MAC_OS_X_VERSION_10_5,GS_API_LATEST) -GS_EXPORT NSString *NSStringFromProtocol(Protocol *aProtocol); -GS_EXPORT Protocol *NSProtocolFromString(NSString *aProtocolName); +GS_EXPORT NSString *_Nullable NSStringFromProtocol(Protocol *_Nullable aProtocol); +GS_EXPORT Protocol *_Nullable NSProtocolFromString(NSString *aProtocolName); #endif GS_EXPORT SEL NSSelectorFromString(NSString *aSelectorName); -GS_EXPORT NSString *NSStringFromSelector(SEL aSelector); +GS_EXPORT NSString *_Nullable NSStringFromSelector(SEL aSelector); GS_EXPORT SEL NSSelectorFromString(NSString *aSelectorName); -GS_EXPORT Class NSClassFromString(NSString *aClassName); -GS_EXPORT NSString *NSStringFromClass(Class aClass); +GS_EXPORT Class _Nullable NSClassFromString(NSString *aClassName); +GS_EXPORT NSString *_Nullable NSStringFromClass(Class _Nullable aClass); GS_EXPORT const char *NSGetSizeAndAlignment(const char *typePtr, - NSUInteger *sizep, NSUInteger *alignp); + NSUInteger *_Nullable sizep, NSUInteger *_Nullable alignp); + +NS_ASSUME_NONNULL_END #if OS_API_VERSION(GS_API_NONE, GS_API_NONE) /* Logging */ diff --git a/Headers/Foundation/NSObject.h b/Headers/Foundation/NSObject.h index 9361669bb5..e017b0654a 100644 --- a/Headers/Foundation/NSObject.h +++ b/Headers/Foundation/NSObject.h @@ -48,6 +48,8 @@ extern "C" { @class NSInvocation; @class Protocol; +NS_ASSUME_NONNULL_BEGIN + /** * The NSObject protocol describes a minimal set of methods that all * objects are expected to support. You should be able to send any @@ -71,7 +73,7 @@ extern "C" { * superclass of the receiver's class, ignoring proxies, then use * "class_getSuperclass(object_getClass())". */ -- (Class) superclass; +- (Class _Nullable) superclass; /** * Returns whether the receiver is equal to the argument. Defining equality is @@ -90,7 +92,7 @@ extern "C" { * If two objects are equal, then they must have the same hash value, however * equal hash values do not imply equality. */ -- (BOOL) isEqual: (id)anObject; +- (BOOL) isEqual: (id _Nullable)anObject; /** * Returns YES if the receiver is an instance of the class, an instance of the @@ -141,24 +143,24 @@ extern "C" { * Performs the specified selector. The selector must correspond to a method * that takes no arguments. */ -- (id) performSelector: (SEL)aSelector; +- (id _Nullable) performSelector: (SEL)aSelector; /** * Performs the specified selector, with the object as the argument. This * method does not perform any automatic unboxing, so the selector must * correspond to a method that takes one object argument. */ -- (id) performSelector: (SEL)aSelector - withObject: (id)anObject; +- (id _Nullable) performSelector: (SEL)aSelector + withObject: (id _Nullable)anObject; /** * Performs the specified selector, with the objects as the arguments. This * method does not perform any automatic unboxing, so the selector must * correspond to a method that takes two object arguments. */ -- (id) performSelector: (SEL)aSelector - withObject: (id)object1 - withObject: (id)object2; +- (id _Nullable) performSelector: (SEL)aSelector + withObject: (id _Nullable)object1 + withObject: (id _Nullable)object2; /** * Returns YES if the object can respond to messages with the specified @@ -223,7 +225,7 @@ extern "C" { /** * Returns the zone of the object. */ -- (NSZone*) zone NS_AUTOMATED_REFCOUNT_UNAVAILABLE; +- (NSZone *_Nullable) zone NS_AUTOMATED_REFCOUNT_UNAVAILABLE; @end /** @@ -246,7 +248,7 @@ extern "C" { * new copy, or are themselves copied, or whether some other mechanism * entirely is used. */ -- (id) copyWithZone: (NSZone*)zone; +- (id) copyWithZone: (NSZone *_Nullable)zone; @end /** @@ -268,7 +270,7 @@ extern "C" { * new copy, or are themselves copied, or whether some other mechanism * entirely is used. */ -- (id) mutableCopyWithZone: (NSZone*)zone; +- (id) mutableCopyWithZone: (NSZone *_Nullable)zone; @end /** @@ -325,7 +327,7 @@ GS_EXPORT_CLASS GS_ROOT_CLASS - (NSString*) className; #endif -+ (id) allocWithZone: (NSZone*)z; ++ (id) allocWithZone: (NSZone *_Nullable)z; + (id) alloc; + (Class) class; @@ -375,8 +377,8 @@ GS_EXPORT_CLASS GS_ROOT_CLASS * of +initialize. */ + (void) initialize; -+ (IMP) instanceMethodForSelector: (SEL)aSelector; -+ (NSMethodSignature*) instanceMethodSignatureForSelector: (SEL)aSelector; ++ (IMP _Nullable) instanceMethodForSelector: (SEL)aSelector; ++ (NSMethodSignature *_Nullable) instanceMethodSignatureForSelector: (SEL)aSelector; + (BOOL) instancesRespondToSelector: (SEL)aSelector; + (BOOL) isSubclassOfClass: (Class)aClass; + (id) new; @@ -392,12 +394,12 @@ GS_EXPORT_CLASS GS_ROOT_CLASS - (void) doesNotRecognizeSelector: (SEL)aSelector; - (void) forwardInvocation: (NSInvocation*)anInvocation; - (id) init; -- (IMP) methodForSelector: (SEL)aSelector; -- (NSMethodSignature*) methodSignatureForSelector: (SEL)aSelector; +- (IMP _Nullable) methodForSelector: (SEL)aSelector; +- (NSMethodSignature *_Nullable) methodSignatureForSelector: (SEL)aSelector; - (id) mutableCopy; - (id) replacementObjectForArchiver: (NSArchiver*)anArchiver; - (id) replacementObjectForCoder: (NSCoder*)anEncoder; -- (Class) superclass; +- (Class _Nullable) superclass; #if OS_API_VERSION(MAC_OS_X_VERSION_10_5, GS_API_LATEST) /** * This method will be called when attempting to send a message a class that @@ -450,11 +452,13 @@ GS_EXPORT_CLASS GS_ROOT_CLASS * runtime, you must also implement -forwardInvocation: with equivalent * semantics. This will be considerably slower, but more portable. */ -- (id) forwardingTargetForSelector: (SEL)aSelector; +- (id _Nullable) forwardingTargetForSelector: (SEL)aSelector; #endif @end +NS_ASSUME_NONNULL_END + /** * Used to allocate memory to hold an object, and initialise the * class of the object to be aClass etc. The allocated memory will @@ -520,6 +524,8 @@ NSIncrementExtraRefCount(id anObject); * Declares some methods for sending messages to self after a fixed delay. * (These methods are in OpenStep and OS X.) */ +NS_ASSUME_NONNULL_BEGIN + @interface NSObject (TimedPerformers) /** @@ -537,13 +543,13 @@ NSIncrementExtraRefCount(id anObject); */ + (void) cancelPreviousPerformRequestsWithTarget: (id)obj selector: (SEL)s - object: (id)arg; + object: (id _Nullable)arg; /** * Sets given message to be sent to this instance after given delay, * in any run loop mode. See [NSRunLoop]. */ - (void) performSelector: (SEL)s - withObject: (id)arg + withObject: (id _Nullable)arg afterDelay: (NSTimeInterval)seconds; /** @@ -551,7 +557,7 @@ NSIncrementExtraRefCount(id anObject); * in given run loop modes. See [NSRunLoop]. */ - (void) performSelector: (SEL)s - withObject: (id)arg + withObject: (id _Nullable)arg afterDelay: (NSTimeInterval)seconds inModes: (NSArray*)modes; @end @@ -591,6 +597,8 @@ NSIncrementExtraRefCount(id anObject); */ - (BOOL) isContentDiscarded; @end + +NS_ASSUME_NONNULL_END #endif #if defined(__cplusplus) } diff --git a/Headers/Foundation/NSString.h b/Headers/Foundation/NSString.h index e0f842ecbd..6a03c1c29e 100644 --- a/Headers/Foundation/NSString.h +++ b/Headers/Foundation/NSString.h @@ -513,6 +513,8 @@ DEFINE_BLOCK_TYPE(GSNSStringLineEnumerationBlock, void, NSString *line, BOOL *st *

*/ +NS_ASSUME_NONNULL_BEGIN + GS_EXPORT_CLASS @interface NSString :NSObject @@ -520,51 +522,51 @@ GS_EXPORT_CLASS + (instancetype) stringWithCharacters: (const unichar*)chars length: (NSUInteger)length; #if OS_API_VERSION(MAC_OS_X_VERSION_10_4,GS_API_LATEST) && GS_API_VERSION( 10200,GS_API_LATEST) -+ (instancetype) stringWithCString: (const char*)byteString - encoding: (NSStringEncoding)encoding; ++ (instancetype _Nullable) stringWithCString: (const char*)byteString + encoding: (NSStringEncoding)encoding; #endif -+ (instancetype) stringWithCString: (const char*)byteString - length: (NSUInteger)length; -+ (instancetype) stringWithCString: (const char*)byteString; ++ (instancetype _Nullable) stringWithCString: (const char*)byteString + length: (NSUInteger)length; ++ (instancetype _Nullable) stringWithCString: (const char*)byteString; + (instancetype) stringWithFormat: (NSString*)format, ... NS_FORMAT_FUNCTION(1,2); -+ (instancetype) stringWithContentsOfFile: (NSString *)path; ++ (instancetype _Nullable) stringWithContentsOfFile: (NSString *)path; // Initializing Newly Allocated Strings - (instancetype) init; #if OS_API_VERSION(MAC_OS_X_VERSION_10_4,GS_API_LATEST) && GS_API_VERSION( 10200,GS_API_LATEST) -- (instancetype) initWithBytes: (const void*)bytes - length: (NSUInteger)length - encoding: (NSStringEncoding)encoding; -- (instancetype) initWithBytesNoCopy: (void*)bytes - length: (NSUInteger)length - encoding: (NSStringEncoding)encoding - freeWhenDone: (BOOL)flag; +- (instancetype _Nullable) initWithBytes: (const void*)bytes + length: (NSUInteger)length + encoding: (NSStringEncoding)encoding; +- (instancetype _Nullable) initWithBytesNoCopy: (void*)bytes + length: (NSUInteger)length + encoding: (NSStringEncoding)encoding + freeWhenDone: (BOOL)flag; #endif #if OS_API_VERSION(MAC_OS_X_VERSION_10_4,GS_API_LATEST) -+ (instancetype) stringWithContentsOfFile: (NSString*)path - usedEncoding: (NSStringEncoding*)enc - error: (NSError**)error; -- (instancetype) initWithContentsOfFile: (NSString*)path - usedEncoding: (NSStringEncoding*)enc - error: (NSError**)error; -+ (instancetype) stringWithContentsOfFile: (NSString*)path - encoding: (NSStringEncoding)enc - error: (NSError**)error; -- (instancetype) initWithContentsOfFile: (NSString*)path - encoding: (NSStringEncoding)enc - error: (NSError**)error; -+ (instancetype) stringWithContentsOfURL: (NSURL*)url - usedEncoding: (NSStringEncoding*)enc - error: (NSError**)error; -- (instancetype) initWithContentsOfURL: (NSURL*)url - usedEncoding: (NSStringEncoding*)enc - error: (NSError**)error; -+ (instancetype) stringWithContentsOfURL: (NSURL*)url - encoding: (NSStringEncoding)enc - error: (NSError**)error; -- (instancetype) initWithContentsOfURL: (NSURL*)url - encoding: (NSStringEncoding)enc - error: (NSError**)error; ++ (instancetype _Nullable) stringWithContentsOfFile: (NSString*)path + usedEncoding: (NSStringEncoding *_Nullable)enc + error: (NSError**)error; +- (instancetype _Nullable) initWithContentsOfFile: (NSString*)path + usedEncoding: (NSStringEncoding *_Nullable)enc + error: (NSError**)error; ++ (instancetype _Nullable) stringWithContentsOfFile: (NSString*)path + encoding: (NSStringEncoding)enc + error: (NSError**)error; +- (instancetype _Nullable) initWithContentsOfFile: (NSString*)path + encoding: (NSStringEncoding)enc + error: (NSError**)error; ++ (instancetype _Nullable) stringWithContentsOfURL: (NSURL*)url + usedEncoding: (NSStringEncoding *_Nullable)enc + error: (NSError**)error; +- (instancetype _Nullable) initWithContentsOfURL: (NSURL*)url + usedEncoding: (NSStringEncoding *_Nullable)enc + error: (NSError**)error; ++ (instancetype _Nullable) stringWithContentsOfURL: (NSURL*)url + encoding: (NSStringEncoding)enc + error: (NSError**)error; +- (instancetype _Nullable) initWithContentsOfURL: (NSURL*)url + encoding: (NSStringEncoding)enc + error: (NSError**)error; - (BOOL) writeToFile: (NSString*)path atomically: (BOOL)atomically encoding: (NSStringEncoding)enc @@ -592,16 +594,16 @@ GS_EXPORT_CLASS - (instancetype) initWithCStringNoCopy: (char*)byteString length: (NSUInteger)length freeWhenDone: (BOOL)flag; -- (instancetype) initWithCString: (const char*)byteString - length: (NSUInteger)length; -- (instancetype) initWithCString: (const char*)byteString; +- (instancetype _Nullable) initWithCString: (const char*)byteString + length: (NSUInteger)length; +- (instancetype _Nullable) initWithCString: (const char*)byteString; - (instancetype) initWithString: (NSString*)string; - (instancetype) initWithFormat: (NSString*)format, ... NS_FORMAT_FUNCTION(1,2); - (instancetype) initWithFormat: (NSString*)format arguments: (va_list)argList NS_FORMAT_FUNCTION(1,0); -- (instancetype) initWithData: (NSData*)data - encoding: (NSStringEncoding)encoding; -- (instancetype) initWithContentsOfFile: (NSString*)path; +- (instancetype _Nullable) initWithData: (NSData*)data + encoding: (NSStringEncoding)encoding; +- (instancetype _Nullable) initWithContentsOfFile: (NSString*)path; // Getting a String's Length - (NSUInteger) length; @@ -658,8 +660,8 @@ GS_EXPORT_CLASS #endif // Converting String Contents into a Property List -- (id) propertyList; -- (NSDictionary*) propertyListFromStringsFileFormat; +- (id _Nullable) propertyList; +- (NSDictionary *_Nullable) propertyListFromStringsFileFormat; // Identifying and Comparing Strings - (NSComparisonResult) compare: (NSString*)aString; @@ -670,7 +672,7 @@ GS_EXPORT_CLASS range: (NSRange)aRange; - (BOOL) hasPrefix: (NSString*)aString; - (BOOL) hasSuffix: (NSString*)aString; -- (BOOL) isEqual: (id)anObject; +- (BOOL) isEqual: (id _Nullable)anObject; - (BOOL) isEqualToString: (NSString*)aString; - (NSUInteger) hash; @@ -692,8 +694,8 @@ GS_EXPORT_CLASS - (BOOL) getCString: (char*)buffer maxLength: (NSUInteger)maxLength encoding: (NSStringEncoding)encoding; -- (instancetype) initWithCString: (const char*)byteString - encoding: (NSStringEncoding)encoding; +- (instancetype _Nullable) initWithCString: (const char*)byteString + encoding: (NSStringEncoding)encoding; - (NSUInteger) lengthOfBytesUsingEncoding: (NSStringEncoding)encoding; - (NSUInteger) maximumLengthOfBytesUsingEncoding: (NSStringEncoding)encoding; #endif @@ -714,13 +716,13 @@ GS_EXPORT_CLASS // Working With Encodings - (BOOL) canBeConvertedToEncoding: (NSStringEncoding)encoding; -- (NSData*) dataUsingEncoding: (NSStringEncoding)encoding; +- (NSData *_Nullable) dataUsingEncoding: (NSStringEncoding)encoding; /** Conversion to an encoding where byte order matters but is not specified * (NSUnicodeStringEncoding, NSUTF16StringEncoding, NSUTF32StringEncoding) * produces data with a Byte Order Marker (BOM) at the start of the data. */ -- (NSData*) dataUsingEncoding: (NSStringEncoding)encoding - allowLossyConversion: (BOOL)flag; +- (NSData *_Nullable) dataUsingEncoding: (NSStringEncoding)encoding + allowLossyConversion: (BOOL)flag; + (NSStringEncoding) defaultCStringEncoding; - (NSString*) description; - (NSStringEncoding) fastestEncoding; @@ -736,9 +738,9 @@ GS_EXPORT_CLASS * completions. Returns 0 if no match found, else a positive number that is * only accurate if outputArray was non-nil. */ -- (NSUInteger) completePathIntoString: (NSString**)outputName +- (NSUInteger) completePathIntoString: (NSString * _Nullable * _Nonnull)outputName caseSensitive: (BOOL)flag - matchesIntoArray: (NSArray**)outputArray + matchesIntoArray: (NSArray * _Nullable * _Nullable)outputArray filterTypes: (NSArray*)filterTypes; /** @@ -752,7 +754,7 @@ GS_EXPORT_CLASS * This method uses [NSFileManager-fileSystemRepresentationWithPath:] to * perform the conversion. */ -- (const GSNativeChar*) fileSystemRepresentation; +- (const GSNativeChar *_Nullable) fileSystemRepresentation; /** * Converts the receiver to a C string path using the character encoding @@ -1003,15 +1005,15 @@ GS_EXPORT_CLASS NS_FORMAT_FUNCTION(1,2); + (instancetype) stringWithString: (NSString*)aString; -+ (instancetype) stringWithContentsOfURL: (NSURL*)url; -+ (instancetype) stringWithUTF8String: (const char*)bytes; ++ (instancetype _Nullable) stringWithContentsOfURL: (NSURL*)url; ++ (instancetype _Nullable) stringWithUTF8String: (const char*)bytes; - (instancetype) initWithFormat: (NSString*)format locale: (NSDictionary*)locale, ... NS_FORMAT_FUNCTION(1,3); - (instancetype) initWithFormat: (NSString*)format locale: (NSDictionary*)locale arguments: (va_list)argList NS_FORMAT_FUNCTION(1,0); -- (instancetype) initWithUTF8String: (const char *)bytes; -- (instancetype) initWithContentsOfURL: (NSURL*)url; +- (instancetype _Nullable) initWithUTF8String: (const char *)bytes; +- (instancetype _Nullable) initWithContentsOfURL: (NSURL*)url; - (NSString*) substringWithRange: (NSRange)aRange; - (NSComparisonResult) caseInsensitiveCompare: (NSString*)aString; - (NSComparisonResult) compare: (NSString*)string @@ -1033,18 +1035,18 @@ GS_EXPORT_CLASS forRange: (NSRange)aRange; - (NSRange) lineRangeForRange: (NSRange)aRange; - (const char*) lossyCString; -- (NSString*) stringByAddingPercentEscapesUsingEncoding: (NSStringEncoding)e; +- (NSString *_Nullable) stringByAddingPercentEscapesUsingEncoding: (NSStringEncoding)e; - (NSString*) stringByPaddingToLength: (NSUInteger)newLength withString: (NSString*)padString startingAtIndex: (NSUInteger)padIndex; -- (NSString*) stringByReplacingPercentEscapesUsingEncoding: (NSStringEncoding)e; +- (NSString *_Nullable) stringByReplacingPercentEscapesUsingEncoding: (NSStringEncoding)e; - (NSString*) stringByTrimmingCharactersInSet: (NSCharacterSet*)aSet; - (const char *)UTF8String; #endif #if OS_API_VERSION(MAC_OS_X_VERSION_10_9,GS_API_LATEST) - (NSString *) stringByAddingPercentEncodingWithAllowedCharacters: (NSCharacterSet *)aSet; -- (NSString *) stringByRemovingPercentEncoding; +- (NSString *_Nullable) stringByRemovingPercentEncoding; #endif #if OS_API_VERSION(MAC_OS_X_VERSION_10_3,GS_API_LATEST) @@ -1111,11 +1113,11 @@ GS_EXPORT_CLASS + (instancetype) string; + (instancetype) stringWithCharacters: (const unichar*)characters length: (NSUInteger)length; -+ (instancetype) stringWithCString: (const char*)byteString - length: (NSUInteger)length; -+ (instancetype) stringWithCString: (const char*)byteString; ++ (instancetype _Nullable) stringWithCString: (const char*)byteString + length: (NSUInteger)length; ++ (instancetype _Nullable) stringWithCString: (const char*)byteString; + (instancetype) stringWithFormat: (NSString*)format, ... NS_FORMAT_FUNCTION(1,2); -+ (instancetype) stringWithContentsOfFile: (NSString*)path; ++ (instancetype _Nullable) stringWithContentsOfFile: (NSString*)path; + (NSMutableString*) stringWithCapacity: (NSUInteger)capacity; // Initializing Newly Allocated Strings @@ -1136,6 +1138,8 @@ GS_EXPORT_CLASS @end +NS_ASSUME_NONNULL_END + #ifdef __OBJC_GNUSTEP_RUNTIME_ABI__ # if __OBJC_GNUSTEP_RUNTIME_ABI__ >= 20 # define GNUSTEP_NEW_STRING_ABI diff --git a/Headers/Foundation/NSURL.h b/Headers/Foundation/NSURL.h index cc13f9b155..df77c70379 100644 --- a/Headers/Foundation/NSURL.h +++ b/Headers/Foundation/NSURL.h @@ -40,6 +40,8 @@ extern "C" { @class NSDictionary; @class NSArray; +NS_ASSUME_NONNULL_BEGIN + /** * URL scheme constant for use with [NSURL-initWithScheme:host:path:]. */ @@ -73,9 +75,9 @@ GS_EXPORT_CLASS #if GS_EXPOSE(NSURL) @private NSString *_urlString; - NSURL *_baseURL; - void *_clients; - void *_data; + NSURL *_Nullable _baseURL; + void *_Nullable _clients; + void *_Nullable _data; #endif } @@ -100,12 +102,12 @@ GS_EXPORT_CLASS #if OS_API_VERSION(MAC_OS_X_VERSION_10_11, GS_API_LATEST) + (instancetype) fileURLWithPath: (NSString *)aPath isDirectory: (BOOL)isDir - relativeToURL: (NSURL *)baseURL; + relativeToURL: (NSURL *_Nullable)baseURL; /** Create and return a file URL with the supplied path, relative to a base URL. */ + (instancetype) fileURLWithPath: (NSString *)aPath - relativeToURL: (NSURL *)baseURL; + relativeToURL: (NSURL *_Nullable)baseURL; #endif /** @@ -114,12 +116,12 @@ GS_EXPORT_CLASS * conforming to the description (in RFC2396) of an absolute URL.
* Calls -initWithString: */ -+ (instancetype) URLWithString: (NSString*)aUrlString; ++ (instancetype _Nullable) URLWithString: (NSString*)aUrlString; #if OS_API_VERSION(MAC_OS_X_VERSION_10_10, GS_API_LATEST) -+ (instancetype) URLByResolvingAliasFileAtURL: (NSURL*)url - options: (NSURLBookmarkResolutionOptions)options - error: (NSError**)error; ++ (instancetype _Nullable) URLByResolvingAliasFileAtURL: (NSURL*)url + options: (NSURLBookmarkResolutionOptions)options + error: (NSError**)error; #endif /** @@ -128,8 +130,8 @@ GS_EXPORT_CLASS * conforming to the description (in RFC2396) of a relative URL.
* Calls -initWithString:relativeToURL: */ -+ (instancetype) URLWithString: (NSString*)aUrlString - relativeToURL: (NSURL*)aBaseUrl; ++ (instancetype _Nullable) URLWithString: (NSString*)aUrlString + relativeToURL: (NSURL *_Nullable)aBaseUrl; /** * Initialise as a file URL with the specified path (which must @@ -167,7 +169,7 @@ GS_EXPORT_CLASS * Calls -initWithScheme:host:path: */ - (instancetype) initFileURLWithPath: (NSString *)aPath - relativeToURL: (NSURL *)baseURL; + relativeToURL: (NSURL *_Nullable)baseURL; /** * Initialise as a file URL with the specified path (which must @@ -180,7 +182,7 @@ GS_EXPORT_CLASS */ - (instancetype) initFileURLWithPath: (NSString *)aPath isDirectory: (BOOL)isDir - relativeToURL: (NSURL *)baseURL; + relativeToURL: (NSURL *_Nullable)baseURL; #endif /** @@ -194,15 +196,15 @@ GS_EXPORT_CLASS * Permits the 'aHost' part to contain 'username:password@host:port' or * 'host:port' in addition to a simple host name or address. */ -- (instancetype) initWithScheme: (NSString*)aScheme - host: (NSString*)aHost - path: (NSString*)aPath; +- (instancetype _Nullable) initWithScheme: (NSString*)aScheme + host: (NSString *_Nullable)aHost + path: (NSString*)aPath; /** * Initialise as an absolute URL.
* Calls -initWithString:relativeToURL: */ -- (instancetype) initWithString: (NSString*)aUrlString; +- (instancetype _Nullable) initWithString: (NSString*)aUrlString; /** * Initialised using aUrlString and aBaseUrl. The value of aBaseUrl @@ -212,8 +214,8 @@ GS_EXPORT_CLASS * Parses an empty string as an empty path.
* If the string cannot be parsed the method returns nil. */ -- (instancetype) initWithString: (NSString*)aUrlString - relativeToURL: (NSURL*)aBaseUrl; +- (instancetype _Nullable) initWithString: (NSString*)aUrlString + relativeToURL: (NSURL *_Nullable)aBaseUrl; #if GS_HAS_DECLARED_PROPERTIES @property (readonly, getter=isFileURL) BOOL fileURL; @@ -236,7 +238,7 @@ GS_EXPORT_CLASS * If the receiver is a relative URL, returns its base URL.
* Otherwise, returns nil. */ -- (NSURL*) baseURL; +- (NSURL *_Nullable) baseURL; #if OS_API_VERSION(MAC_OS_X_VERSION_10_6,GS_API_LATEST) /** Attempts to load from the specified URL and provides an error @@ -252,7 +254,7 @@ GS_EXPORT_CLASS * The fragment is everything in the original URL string after a '#'
* File URLs do not have fragments. */ -- (NSString*) fragment; +- (NSString *_Nullable) fragment; /** * Returns the host portion of the receiver or nil if there is no @@ -262,7 +264,7 @@ GS_EXPORT_CLASS * Returns IPv6 addresses without the enclosing square brackets * required (by RFC2732) in URL strings. */ -- (NSString*) host; +- (NSString *_Nullable) host; #if OS_API_VERSION(MAC_OS_X_VERSION_10_6,GS_API_LATEST) /** Returns the last (rightmost) path component of the receiver. @@ -292,7 +294,7 @@ GS_EXPORT_CLASS * background load operation to operate! *

*/ -- (void) loadResourceDataNotifyingClient: (id)client +- (void) loadResourceDataNotifyingClient: (id _Nullable)client usingCache: (BOOL)shouldUseCache; /** @@ -302,7 +304,7 @@ GS_EXPORT_CLASS * but before the query.
* File URLs do not have parameters. */ -- (NSString*) parameterString; +- (NSString *_Nullable) parameterString; /** * Returns the password portion of the receiver or nil if there is no @@ -312,7 +314,7 @@ GS_EXPORT_CLASS * NB. because of its security implications it is recommended that you * do not use URLs with users and passwords unless necessary. */ -- (NSString*) password; +- (NSString *_Nullable) password; /** * Returns the path portion of the receiver.
@@ -346,13 +348,13 @@ GS_EXPORT_CLASS * Percent escape sequences in the user string are translated in GNUstep * but this appears to be broken in MacOS-X. */ -- (NSNumber*) port; +- (NSNumber *_Nullable) port; /** * Asks a URL handle to return the property for the specified key and * returns the result. */ -- (id) propertyForKey: (NSString*)propertyKey; +- (id _Nullable) propertyForKey: (NSString*)propertyKey; /** * Returns the query portion of the receiver or nil if there is no @@ -361,14 +363,14 @@ GS_EXPORT_CLASS * but before the fragment.
* File URLs do not have queries. */ -- (NSString*) query; +- (NSString *_Nullable) query; /** * Returns the path of the receiver, without taking any base URL into account. * If the receiver is an absolute URL, -relativePath is the same as -path.
* Returns nil if there is no path specified for the URL. */ -- (NSString*) relativePath; +- (NSString *_Nullable) relativePath; /** * Returns the relative portion of the URL string. If the receiver is not @@ -382,7 +384,7 @@ GS_EXPORT_CLASS * an existing NSURLHandle can be used to provide the data, or if it should * be refetched. */ -- (NSData*) resourceDataUsingCache: (BOOL)shouldUseCache; +- (NSData *_Nullable) resourceDataUsingCache: (BOOL)shouldUseCache; /** * Returns the resource specifier of the URL ... the part which lies @@ -393,7 +395,7 @@ GS_EXPORT_CLASS /** * Returns the scheme of the receiver. */ -- (NSString*) scheme; +- (NSString *_Nullable) scheme; /** * Calls [NSURLHandle-writeProperty:forKey:] to set the named property. @@ -463,11 +465,11 @@ GS_EXPORT_CLASS #if OS_API_VERSION(MAC_OS_X_VERSION_10_6, GS_API_LATEST) - (BOOL) isFileReferenceURL; -- (NSURL *) fileReferenceURL; +- (NSURL *_Nullable) fileReferenceURL; -- (NSURL *) filePathURL; +- (NSURL *_Nullable) filePathURL; -- (BOOL) getResourceValue: (id*)value +- (BOOL) getResourceValue: (id _Nullable * _Nullable)value forKey: (NSString *)key error: (NSError**)error; #endif @@ -487,7 +489,7 @@ GS_EXPORT_CLASS * NB. because of its security implications it is recommended that you * do not use URLs with users and passwords unless necessary. */ -- (NSString*) user; +- (NSString *_Nullable) user; @end @@ -518,6 +520,8 @@ GS_EXPORT_CLASS resourceDidFailLoadingWithReason: (NSString*)reason; @end +NS_ASSUME_NONNULL_END + /** URL Resource Value Constants **/ #if OS_API_VERSION(MAC_OS_X_VERSION_10_6, GS_API_LATEST) GS_EXPORT NSString* const NSURLNameKey; @@ -794,5 +798,3 @@ GS_NSURLComponents_IVARS; #endif /* __NSURL_h_GNUSTEP_BASE_INCLUDE */ - - From 5bc8a5d565719af5d4c7f1fbe04271b0029da07b Mon Sep 17 00:00:00 2001 From: Daniel Boyd Date: Fri, 3 Apr 2026 14:23:43 -0500 Subject: [PATCH 03/12] Add Foundation module map for Swift import --- Headers/module.modulemap | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 Headers/module.modulemap diff --git a/Headers/module.modulemap b/Headers/module.modulemap new file mode 100644 index 0000000000..7f498d055a --- /dev/null +++ b/Headers/module.modulemap @@ -0,0 +1,10 @@ +module Foundation [system] { + header "Foundation/Foundation.h" + + textual header "GNUstepBase/GSVersionMacros.h" + textual header "GNUstepBase/GSConfig.h" + textual header "GNUstepBase/GNUstep.h" + textual header "GNUstepBase/GSBlocks.h" + + export * +} From 97dd53793bbb515e8e1a5135f62daa73c13467c8 Mon Sep 17 00:00:00 2001 From: Daniel Boyd Date: Fri, 17 Apr 2026 15:15:15 -0500 Subject: [PATCH 04/12] Tighten NSData and NSURL Swift nullability --- Headers/Foundation/NSData.h | 8 ++++---- Headers/Foundation/NSURL.h | 8 ++++++-- Source/NSURL.m | 5 +++++ 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/Headers/Foundation/NSData.h b/Headers/Foundation/NSData.h index d881bba486..d60f554bec 100644 --- a/Headers/Foundation/NSData.h +++ b/Headers/Foundation/NSData.h @@ -106,8 +106,8 @@ GS_EXPORT_CLASS // Allocating and Initializing a Data Object + (instancetype) data; -+ (instancetype) dataWithBytes: (const void *_Nullable)bytes - length: (NSUInteger)length; ++ (nonnull instancetype) dataWithBytes: (const void *_Nullable)bytes + length: (NSUInteger)length; + (instancetype) dataWithBytesNoCopy: (void *_Nullable)bytes length: (NSUInteger)length; #if OS_API_VERSION(GS_API_MACOSX, GS_API_LATEST) @@ -143,8 +143,8 @@ GS_EXPORT_CLASS length: (NSUInteger)length deallocator: (GSDataDeallocatorBlock)deallocBlock; #endif -- (instancetype) initWithBytes: (const void *_Nullable)aBuffer - length: (NSUInteger)bufferSize; +- (nonnull instancetype) initWithBytes: (const void *_Nullable)aBuffer + length: (NSUInteger)bufferSize; - (instancetype) initWithBytesNoCopy: (void *_Nullable)aBuffer length: (NSUInteger)bufferSize; #if OS_API_VERSION(GS_API_MACOSX, GS_API_LATEST) diff --git a/Headers/Foundation/NSURL.h b/Headers/Foundation/NSURL.h index df77c70379..d314a162d5 100644 --- a/Headers/Foundation/NSURL.h +++ b/Headers/Foundation/NSURL.h @@ -80,7 +80,12 @@ GS_EXPORT_CLASS void *_Nullable _data; #endif } - + +/** + * Initialize an empty URL object. + */ +- (nonnull instancetype) init; + /** * Create and return a file URL with the supplied path.
* The value of aPath must be a valid filesystem path.
@@ -797,4 +802,3 @@ GS_NSURLComponents_IVARS; #endif /* __NSURL_h_GNUSTEP_BASE_INCLUDE */ - diff --git a/Source/NSURL.m b/Source/NSURL.m index b97b7a960b..be1db02c95 100644 --- a/Source/NSURL.m +++ b/Source/NSURL.m @@ -727,6 +727,11 @@ + (id) URLByResolvingAliasFileAtURL: (NSURL*)url return nil; } +- (id) init +{ + return [self initWithString: @""]; +} + - (id) initFileURLWithPath: (NSString *)aPath { /* isDirectory flag will be overwritten if a directory exists. */ From 89fd3ce54ae0b351834d962a5efe6c5f46fc0fc5 Mon Sep 17 00:00:00 2001 From: Daniel Boyd Date: Fri, 17 Apr 2026 15:27:34 -0500 Subject: [PATCH 05/12] Tighten retained NSError out parameter nullability --- Headers/Foundation/NSData.h | 8 ++++---- Headers/Foundation/NSJSONSerialization.h | 8 ++++---- Headers/Foundation/NSPropertyList.h | 8 ++++---- Headers/Foundation/NSRegularExpression.h | 8 ++++---- Headers/Foundation/NSURL.h | 3 +-- 5 files changed, 17 insertions(+), 18 deletions(-) diff --git a/Headers/Foundation/NSData.h b/Headers/Foundation/NSData.h index d60f554bec..5e73dc7e5f 100644 --- a/Headers/Foundation/NSData.h +++ b/Headers/Foundation/NSData.h @@ -117,13 +117,13 @@ GS_EXPORT_CLASS #endif + (instancetype _Nullable) dataWithContentsOfFile: (NSString *)path options: (NSDataReadingOptions)readOptionsMask - error: (NSError **)errorPtr; + error: (NSError *_Nullable *_Nullable)errorPtr; + (instancetype _Nullable) dataWithContentsOfFile: (NSString*)path; + (instancetype _Nullable) dataWithContentsOfMappedFile: (NSString*)path; #if OS_API_VERSION(GS_API_MACOSX, GS_API_LATEST) + (instancetype _Nullable) dataWithContentsOfURL: (NSURL *)url options: (NSDataReadingOptions)readOptionsMask - error: (NSError **)errorPtr; + error: (NSError *_Nullable *_Nullable)errorPtr; + (instancetype _Nullable) dataWithContentsOfURL: (NSURL*)url; #endif + (instancetype) dataWithData: (NSData*)data; @@ -155,13 +155,13 @@ GS_EXPORT_CLASS - (instancetype _Nullable) initWithContentsOfFile: (NSString*)path; - (instancetype _Nullable) initWithContentsOfFile: (NSString *) path options: (NSDataReadingOptions) readOptionsMask - error: (NSError **) errorPtr; + error: (NSError *_Nullable *_Nullable) errorPtr; - (instancetype _Nullable) initWithContentsOfMappedFile: (NSString*)path; #if OS_API_VERSION(GS_API_MACOSX, GS_API_LATEST) - (instancetype _Nullable) initWithContentsOfURL: (NSURL*)url; - (instancetype _Nullable) initWithContentsOfURL: (NSURL *)url options: (NSDataReadingOptions)readOptionsMask - error: (NSError **)errorPtr; + error: (NSError *_Nullable *_Nullable)errorPtr; #endif - (instancetype) initWithData: (NSData*)data; diff --git a/Headers/Foundation/NSJSONSerialization.h b/Headers/Foundation/NSJSONSerialization.h index d86debeae0..0feb5e6c67 100644 --- a/Headers/Foundation/NSJSONSerialization.h +++ b/Headers/Foundation/NSJSONSerialization.h @@ -91,18 +91,18 @@ GS_EXPORT_CLASS @interface NSJSONSerialization : NSObject + (NSData *_Nullable) dataWithJSONObject: (id)obj options: (NSJSONWritingOptions)opt - error: (NSError **)error; + error: (NSError *_Nullable *_Nullable)error; + (BOOL) isValidJSONObject: (id)obj; + (id _Nullable) JSONObjectWithData: (NSData *)data options: (NSJSONReadingOptions)opt - error: (NSError **)error; + error: (NSError *_Nullable *_Nullable)error; + (id _Nullable) JSONObjectWithStream: (NSInputStream *)stream options: (NSJSONReadingOptions)opt - error: (NSError **)error; + error: (NSError *_Nullable *_Nullable)error; + (NSInteger) writeJSONObject: (id)obj toStream: (NSOutputStream *)stream options: (NSJSONWritingOptions)opt - error: (NSError **)error; + error: (NSError *_Nullable *_Nullable)error; @end NS_ASSUME_NONNULL_END diff --git a/Headers/Foundation/NSPropertyList.h b/Headers/Foundation/NSPropertyList.h index 5dddbd3126..65b7aeae27 100644 --- a/Headers/Foundation/NSPropertyList.h +++ b/Headers/Foundation/NSPropertyList.h @@ -269,20 +269,20 @@ GS_EXPORT_CLASS + (NSData *) dataWithPropertyList: (id)aPropertyList format: (NSPropertyListFormat)aFormat options: (NSPropertyListWriteOptions)anOption - error: (out NSError**)error; + error: (out NSError *_Nullable *_Nullable)error; + (id) propertyListWithData: (NSData*)data options: (NSPropertyListReadOptions)anOption format: (NSPropertyListFormat*)aFormat - error: (out NSError**)error; + error: (out NSError *_Nullable *_Nullable)error; + (id) propertyListWithStream: (NSInputStream*)stream options: (NSPropertyListReadOptions)anOption format: (NSPropertyListFormat*)aFormat - error: (out NSError**)error; + error: (out NSError *_Nullable *_Nullable)error; + (NSInteger) writePropertyList: (id)aPropertyList toStream: (NSOutputStream*)stream format: (NSPropertyListFormat)aFormat options: (NSPropertyListWriteOptions)anOption - error: (out NSError**)error; + error: (out NSError *_Nullable *_Nullable)error; #endif @end diff --git a/Headers/Foundation/NSRegularExpression.h b/Headers/Foundation/NSRegularExpression.h index bd9ef23dc5..b6233b3fcb 100644 --- a/Headers/Foundation/NSRegularExpression.h +++ b/Headers/Foundation/NSRegularExpression.h @@ -118,16 +118,16 @@ GS_EXPORT_CLASS #if GS_USE_ICU || GS_UNSAFE_REGEX + (NSRegularExpression*) regularExpressionWithPattern: (NSString*)aPattern options: (NSRegularExpressionOptions)opts - error: (NSError**)e; + error: (NSError *_Nullable *_Nullable)e; - (id) initWithPattern: (NSString*)aPattern options: (NSRegularExpressionOptions)opts - error: (NSError**)e; + error: (NSError *_Nullable *_Nullable)e; + (NSRegularExpression*) regularExpressionWithPattern: (NSString*)aPattern options: (NSRegularExpressionOptions)opts - error: (NSError**)e; + error: (NSError *_Nullable *_Nullable)e; - (id) initWithPattern: (NSString*)aPattern options: (NSRegularExpressionOptions)opts - error: (NSError**)e; + error: (NSError *_Nullable *_Nullable)e; - (NSString*) pattern; #if GS_API_VERSION( 13100, GS_API_LATEST) /** In the GNUstep implementation this method is the fundametal primitive diff --git a/Headers/Foundation/NSURL.h b/Headers/Foundation/NSURL.h index d314a162d5..9a0d750c6b 100644 --- a/Headers/Foundation/NSURL.h +++ b/Headers/Foundation/NSURL.h @@ -250,7 +250,7 @@ GS_EXPORT_CLASS * response if the data is unrachable.
* Returns YES on success, NO on failure. */ -- (BOOL) checkResourceIsReachableAndReturnError: (NSError **)error; +- (BOOL) checkResourceIsReachableAndReturnError: (NSError *_Nullable *_Nullable)error; #endif /** @@ -801,4 +801,3 @@ GS_NSURLComponents_IVARS; #endif /* GS_API_MACOSX */ #endif /* __NSURL_h_GNUSTEP_BASE_INCLUDE */ - From 37dddc8e4931a03d6fa653ab29943cd1493454a7 Mon Sep 17 00:00:00 2001 From: Daniel Boyd Date: Mon, 27 Apr 2026 13:52:03 -0500 Subject: [PATCH 06/12] Tighten NSFileManager retained NSError annotations --- Headers/Foundation/NSFileManager.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Headers/Foundation/NSFileManager.h b/Headers/Foundation/NSFileManager.h index b6b6f0efa9..13e136d5fa 100644 --- a/Headers/Foundation/NSFileManager.h +++ b/Headers/Foundation/NSFileManager.h @@ -310,7 +310,7 @@ GS_EXPORT_CLASS */ - (BOOL) createSymbolicLinkAtPath: (NSString*)path withDestinationPath: (NSString*)destPath - error: (NSError**)error; + error: (NSError *_Nullable *_Nullable)error; - (BOOL) setAttributes:(NSDictionary *)attributes ofItemAtPath:(NSString *)path error:(NSError **)error; #endif @@ -391,7 +391,7 @@ GS_EXPORT_CLASS - (NSArray*) contentsOfDirectoryAtPath: (NSString*)path error: (NSError**)error; - (NSDictionary*) attributesOfFileSystemForPath: (NSString*)path - error: (NSError**)error; + error: (NSError *_Nullable *_Nullable)error; #endif - (BOOL) copyPath: (NSString*)source From f863f6c565ec15fcf98b8c8ed1b9eebb3193c661 Mon Sep 17 00:00:00 2001 From: Daniel Boyd Date: Mon, 27 Apr 2026 14:20:11 -0500 Subject: [PATCH 07/12] Implement basic file URL resource values --- Source/NSURL.m | 61 +++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 60 insertions(+), 1 deletion(-) diff --git a/Source/NSURL.m b/Source/NSURL.m index be1db02c95..f14ca0908e 100644 --- a/Source/NSURL.m +++ b/Source/NSURL.m @@ -1744,7 +1744,66 @@ - (BOOL) getResourceValue: (id*)value forKey: (NSString *)key error: (NSError**)error { - // TODO: unimplemented + if (value != 0) + { + *value = nil; + } + + if (NO == [self isFileURL]) + { + return NO; + } + + if (nil == key) + { + return NO; + } + + if ([key isEqualToString: NSURLNameKey]) + { + if (value != 0) + { + *value = [self lastPathComponent]; + } + return YES; + } + else if ([key isEqualToString: NSURLIsDirectoryKey] + || [key isEqualToString: NSURLFileSizeKey]) + { + NSDictionary *attributes; + NSString *path = [self path]; + + if (nil == path) + { + return NO; + } + + attributes = [[NSFileManager defaultManager] fileAttributesAtPath: path + traverseLink: YES]; + if (nil == attributes) + { + return NO; + } + + if ([key isEqualToString: NSURLIsDirectoryKey]) + { + if (value != 0) + { + NSString *fileType = [attributes objectForKey: NSFileType]; + + *value = [NSNumber numberWithBool: + [fileType isEqualToString: NSFileTypeDirectory]]; + } + return YES; + } + + if (value != 0) + { + *value = [attributes objectForKey: NSFileSize]; + } + return (*value != nil); + } + return NO; } From 1bb56920d5d17c1680d331c43bab0b1f1f81e676 Mon Sep 17 00:00:00 2001 From: Daniel Boyd Date: Mon, 27 Apr 2026 15:08:29 -0500 Subject: [PATCH 08/12] Widen file URL resource values --- Source/NSURL.m | 91 ++++++++++++++++++++++++-- Tests/base/NSURL/resourceValues.m | 103 ++++++++++++++++++++++++++++++ 2 files changed, 189 insertions(+), 5 deletions(-) create mode 100644 Tests/base/NSURL/resourceValues.m diff --git a/Source/NSURL.m b/Source/NSURL.m index f14ca0908e..0a413bd077 100644 --- a/Source/NSURL.m +++ b/Source/NSURL.m @@ -1767,11 +1767,20 @@ - (BOOL) getResourceValue: (id*)value } return YES; } - else if ([key isEqualToString: NSURLIsDirectoryKey] + else if ([key isEqualToString: NSURLIsRegularFileKey] + || [key isEqualToString: NSURLIsDirectoryKey] + || [key isEqualToString: NSURLIsSymbolicLinkKey] + || [key isEqualToString: NSURLIsPackageKey] + || [key isEqualToString: NSURLIsHiddenKey] + || [key isEqualToString: NSURLCreationDateKey] + || [key isEqualToString: NSURLContentAccessDateKey] + || [key isEqualToString: NSURLContentModificationDateKey] + || [key isEqualToString: NSURLAttributeModificationDateKey] || [key isEqualToString: NSURLFileSizeKey]) { NSDictionary *attributes; NSString *path = [self path]; + NSString *fileType; if (nil == path) { @@ -1779,23 +1788,95 @@ - (BOOL) getResourceValue: (id*)value } attributes = [[NSFileManager defaultManager] fileAttributesAtPath: path - traverseLink: YES]; + traverseLink: NO]; if (nil == attributes) { return NO; } - if ([key isEqualToString: NSURLIsDirectoryKey]) + fileType = [attributes objectForKey: NSFileType]; + if ([key isEqualToString: NSURLIsRegularFileKey]) + { + if (value != 0) + { + *value = [NSNumber numberWithBool: + [fileType isEqualToString: NSFileTypeRegular]]; + } + return YES; + } + else if ([key isEqualToString: NSURLIsDirectoryKey]) { if (value != 0) { - NSString *fileType = [attributes objectForKey: NSFileType]; - *value = [NSNumber numberWithBool: [fileType isEqualToString: NSFileTypeDirectory]]; } return YES; } + else if ([key isEqualToString: NSURLIsSymbolicLinkKey]) + { + if (value != 0) + { + *value = [NSNumber numberWithBool: + [fileType isEqualToString: NSFileTypeSymbolicLink]]; + } + return YES; + } + else if ([key isEqualToString: NSURLIsPackageKey]) + { + if (value != 0) + { + NSString *extension = [[path pathExtension] lowercaseString]; + + *value = [NSNumber numberWithBool: + [extension isEqualToString: @"app"] + || [extension isEqualToString: @"bundle"] + || [extension isEqualToString: @"framework"] + || [extension isEqualToString: @"plugin"]]; + } + return YES; + } + else if ([key isEqualToString: NSURLIsHiddenKey]) + { + if (value != 0) + { + *value = [NSNumber numberWithBool: + [[path lastPathComponent] hasPrefix: @"."]]; + } + return YES; + } + else if ([key isEqualToString: NSURLCreationDateKey]) + { + if (value != 0) + { + *value = [attributes objectForKey: NSFileCreationDate]; + } + return (*value != nil); + } + else if ([key isEqualToString: NSURLContentAccessDateKey]) + { + if (value != 0) + { + *value = [attributes objectForKey: NSFileModificationDate]; + } + return (*value != nil); + } + else if ([key isEqualToString: NSURLContentModificationDateKey]) + { + if (value != 0) + { + *value = [attributes objectForKey: NSFileModificationDate]; + } + return (*value != nil); + } + else if ([key isEqualToString: NSURLAttributeModificationDateKey]) + { + if (value != 0) + { + *value = [attributes objectForKey: NSFileModificationDate]; + } + return (*value != nil); + } if (value != 0) { diff --git a/Tests/base/NSURL/resourceValues.m b/Tests/base/NSURL/resourceValues.m new file mode 100644 index 0000000000..9e78919cd9 --- /dev/null +++ b/Tests/base/NSURL/resourceValues.m @@ -0,0 +1,103 @@ +#import +#import "Testing.h" +#import "ObjectTesting.h" + +#if !defined(_WIN32) +#include +#endif + +static BOOL +getValue(NSURL *url, NSString *key, id *value) +{ + NSError *error = nil; + + return [url getResourceValue: value forKey: key error: &error]; +} + +int +main() +{ + NSAutoreleasePool *arp = [NSAutoreleasePool new]; + NSFileManager *fm = [NSFileManager defaultManager]; + NSString *root = [NSTemporaryDirectory() + stringByAppendingPathComponent: @"NSURLResourceValuesTest"]; + NSString *filePath = [root stringByAppendingPathComponent: @"file.txt"]; + NSString *hiddenPath = [root stringByAppendingPathComponent: @".hidden"]; + NSString *dirPath = [root stringByAppendingPathComponent: @"Sample.app"]; + NSString *linkPath = [root stringByAppendingPathComponent: @"link.txt"]; + NSURL *fileURL; + NSURL *hiddenURL; + NSURL *dirURL; + NSURL *linkURL; + id value = nil; + + START_SET("NSURL resource values"); + + [fm removeFileAtPath: root handler: nil]; + PASS([fm createDirectoryAtPath: root attributes: nil], + "created resource value test directory"); + PASS([@"hello" writeToFile: filePath atomically: YES], + "created regular file"); + PASS([@"secret" writeToFile: hiddenPath atomically: YES], + "created hidden file"); + PASS([fm createDirectoryAtPath: dirPath attributes: nil], + "created package directory"); +#if !defined(_WIN32) + PASS(0 == symlink([filePath fileSystemRepresentation], + [linkPath fileSystemRepresentation]), "created symbolic link"); +#endif + + fileURL = [NSURL fileURLWithPath: filePath]; + hiddenURL = [NSURL fileURLWithPath: hiddenPath]; + dirURL = [NSURL fileURLWithPath: dirPath]; + linkURL = [NSURL fileURLWithPath: linkPath]; + + PASS(getValue(fileURL, NSURLNameKey, &value), "NSURLNameKey succeeds"); + PASS_EQUAL(value, @"file.txt", "NSURLNameKey returns last path component"); + + PASS(getValue(fileURL, NSURLIsRegularFileKey, &value), + "NSURLIsRegularFileKey succeeds"); + PASS_EQUAL(value, [NSNumber numberWithBool: YES], + "regular file reports regular"); + PASS(getValue(fileURL, NSURLIsDirectoryKey, &value), + "NSURLIsDirectoryKey succeeds"); + PASS_EQUAL(value, [NSNumber numberWithBool: NO], + "regular file is not a directory"); + PASS(getValue(fileURL, NSURLFileSizeKey, &value), + "NSURLFileSizeKey succeeds"); + PASS_EQUAL(value, [NSNumber numberWithUnsignedLongLong: 5], + "file size is reported"); + + PASS(getValue(dirURL, NSURLIsDirectoryKey, &value), + "directory NSURLIsDirectoryKey succeeds"); + PASS_EQUAL(value, [NSNumber numberWithBool: YES], + "directory reports directory"); + PASS(getValue(dirURL, NSURLIsPackageKey, &value), + "NSURLIsPackageKey succeeds"); + PASS_EQUAL(value, [NSNumber numberWithBool: YES], + ".app directory reports package"); + + PASS(getValue(hiddenURL, NSURLIsHiddenKey, &value), + "NSURLIsHiddenKey succeeds"); + PASS_EQUAL(value, [NSNumber numberWithBool: YES], + "dot file reports hidden"); + +#if !defined(_WIN32) + PASS(getValue(linkURL, NSURLIsSymbolicLinkKey, &value), + "NSURLIsSymbolicLinkKey succeeds"); + PASS_EQUAL(value, [NSNumber numberWithBool: YES], + "symbolic link reports symbolic link"); +#endif + + PASS(getValue(fileURL, NSURLContentModificationDateKey, &value), + "NSURLContentModificationDateKey succeeds"); + PASS([value isKindOfClass: [NSDate class]], + "modification date is an NSDate"); + + [fm removeFileAtPath: root handler: nil]; + + END_SET("NSURL resource values"); + + DESTROY(arp); + return 0; +} From 086fbfbffe9a1bcf349d006d917901d8c1fc2d6b Mon Sep 17 00:00:00 2001 From: Daniel Boyd Date: Mon, 27 Apr 2026 15:33:19 -0500 Subject: [PATCH 09/12] Skip package descendants in URL enumerators --- Headers/Foundation/NSFileManager.h | 1 + Source/NSFileManager.m | 27 ++++++++++++++++++++++++++ Tests/base/NSFileManager/general.m | 31 ++++++++++++++++++++++++++++++ 3 files changed, 59 insertions(+) diff --git a/Headers/Foundation/NSFileManager.h b/Headers/Foundation/NSFileManager.h index 13e136d5fa..4128ab56a0 100644 --- a/Headers/Foundation/NSFileManager.h +++ b/Headers/Foundation/NSFileManager.h @@ -590,6 +590,7 @@ GS_EXPORT_CLASS BOOL justContents: 1; BOOL skipHidden: 1; BOOL currentIsDir: 1; + BOOL skipPackages: 1; } _flags; #endif #if GS_NONFRAGILE diff --git a/Source/NSFileManager.m b/Source/NSFileManager.m index 3de1b0f8e0..27004b32e2 100644 --- a/Source/NSFileManager.m +++ b/Source/NSFileManager.m @@ -194,6 +194,7 @@ - (id) initWithDirectoryPath: (NSString*)path followSymlinks: (BOOL)follow justContents: (BOOL)justContents skipHidden: (BOOL)skipHidden + skipPackages: (BOOL)skipPackages errorHandler: (GSDirEnumErrorHandler) handler for: (NSFileManager*)mgr; @@ -958,6 +959,7 @@ - (NSDirectoryEnumerator*) enumeratorAtURL: (NSURL*)url NSString *path; BOOL shouldRecurse; BOOL shouldSkipHidden; + BOOL shouldSkipPackages; [self _setLastError: nil code: 0]; @@ -986,6 +988,16 @@ - (NSDirectoryEnumerator*) enumeratorAtURL: (NSURL*)url { shouldSkipHidden = NO; } + + if ((mask & NSDirectoryEnumerationSkipsPackageDescendants) + == NSDirectoryEnumerationSkipsPackageDescendants) + { + shouldSkipPackages = YES; + } + else + { + shouldSkipPackages = NO; + } direnum = [[GSURLEnumerator alloc] initWithDirectoryPath: path @@ -993,6 +1005,7 @@ - (NSDirectoryEnumerator*) enumeratorAtURL: (NSURL*)url followSymlinks: NO justContents: NO skipHidden: shouldSkipHidden + skipPackages: shouldSkipPackages errorHandler: handler for: self]; @@ -2752,6 +2765,7 @@ - (id) initWithDirectoryPath: (NSString*)path followSymlinks: (BOOL)follow justContents: (BOOL)justContents skipHidden: (BOOL)skipHidden + skipPackages: (BOOL)skipPackages errorHandler: (GSDirEnumErrorHandler) handler for: (NSFileManager*)mgr { @@ -2770,6 +2784,7 @@ - (id) initWithDirectoryPath: (NSString*)path _flags.isFollowing = follow ? 1 : 0; _flags.justContents = justContents ? 1 : 0; _flags.skipHidden = skipHidden ? 1 : 0; + _flags.skipPackages = skipPackages ? 1 : 0; _errorHandler = handler; _topPath = [[NSString alloc] initWithString: path]; @@ -2830,6 +2845,7 @@ - (id) initWithDirectoryPath: (NSString*)path followSymlinks: follow justContents: justContents skipHidden: NO + skipPackages: NO errorHandler: NULL for: mgr]; } @@ -3041,6 +3057,17 @@ - (id) nextObject if (S_IFDIR == (S_IFMT & statbuf.st_mode)) { _DIR *dir_pointer; + NSString *extension; + + extension = [[_currentFilePath pathExtension] lowercaseString]; + if (_flags.skipPackages + && ([extension isEqualToString: @"app"] + || [extension isEqualToString: @"bundle"] + || [extension isEqualToString: @"framework"] + || [extension isEqualToString: @"plugin"])) + { + break; + } dir_pointer = _OPENDIR([_mgr fileSystemRepresentationWithPath: diff --git a/Tests/base/NSFileManager/general.m b/Tests/base/NSFileManager/general.m index 3431849452..c86a2d1242 100644 --- a/Tests/base/NSFileManager/general.m +++ b/Tests/base/NSFileManager/general.m @@ -287,6 +287,12 @@ int main() PASS([mgr createDirectoryAtPath: @"subdir" attributes: nil], "NSFileManager can create a subdirectory"); + PASS([mgr createDirectoryAtPath: @"Sample.app" attributes: nil], + "NSFileManager can create a package directory"); + PASS([mgr createFileAtPath: @"Sample.app/Info.plist" + contents: [@"{}" dataUsingEncoding: NSASCIIStringEncoding] + attributes: nil], + "NSFileManager can create a package leaf"); { NSURL *u; @@ -310,6 +316,31 @@ int main() PASS(2 == found, "URL enumerator finds expected file and subdirectory") } + { + NSURL *u; + NSDirectoryEnumerator *e; + BOOL foundPackage = NO; + BOOL foundPackageLeaf = NO; + + e = [mgr enumeratorAtURL: [NSURL fileURLWithPath: @"."] + includingPropertiesForKeys: nil + options: NSDirectoryEnumerationSkipsPackageDescendants + errorHandler: nil]; + + while (nil != (u = [e nextObject])) + { + NSString *c = [[u path] lastPathComponent]; + + if ([c isEqualToString: @"Sample.app"]) + foundPackage = YES; + if ([c isEqualToString: @"Info.plist"]) + foundPackageLeaf = YES; + } + PASS(foundPackage, "URL enumerator lists package directory") + PASS(!foundPackageLeaf, + "URL enumerator skips package descendants when requested") + } + PASS([mgr changeCurrentDirectoryPath: @"subdir"], "NSFileManager can move into subdir"); From bda5a3d90cc7c72e526f50043b8cb60b3cbf9beb Mon Sep 17 00:00:00 2001 From: Daniel Boyd Date: Thu, 30 Apr 2026 16:14:55 -0500 Subject: [PATCH 10/12] Tighten NSFileHandle retained NSError annotations --- Headers/Foundation/NSFileHandle.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Headers/Foundation/NSFileHandle.h b/Headers/Foundation/NSFileHandle.h index c7ff19e030..1df77e1df2 100644 --- a/Headers/Foundation/NSFileHandle.h +++ b/Headers/Foundation/NSFileHandle.h @@ -55,11 +55,11 @@ GS_EXPORT_CLASS #if OS_API_VERSION(MAC_OS_X_VERSION_10_6, GS_API_LATEST) + (instancetype) fileHandleForReadingFromURL: (NSURL*)url - error: (NSError**)error; + error: (NSError *_Nullable *_Nullable)error; + (instancetype) fileHandleForWritingToURL: (NSURL*)url - error: (NSError**)error; + error: (NSError *_Nullable *_Nullable)error; + (instancetype) fileHandleForUpdatingURL: (NSURL*)url - error: (NSError**)error; + error: (NSError *_Nullable *_Nullable)error; #endif - (id) initWithFileDescriptor: (int)desc; From 498a344d17f8355955f3c7a3d9239487d7d84538 Mon Sep 17 00:00:00 2001 From: Daniel Boyd Date: Mon, 4 May 2026 13:57:03 -0500 Subject: [PATCH 11/12] Return NSError for invalid keyed unarchive data --- Source/NSKeyedUnarchiver.m | 39 ++++++++++++++++++++++++++-- Tests/base/NSKeyedArchiver/general.m | 18 +++++++++++++ 2 files changed, 55 insertions(+), 2 deletions(-) diff --git a/Source/NSKeyedUnarchiver.m b/Source/NSKeyedUnarchiver.m index 70b60e0a50..92a7cc1bd5 100644 --- a/Source/NSKeyedUnarchiver.m +++ b/Source/NSKeyedUnarchiver.m @@ -27,7 +27,9 @@ #import "Foundation/NSAutoreleasePool.h" #import "Foundation/NSData.h" #import "Foundation/NSDictionary.h" +#import "Foundation/NSError.h" #import "Foundation/NSException.h" +#import "Foundation/FoundationErrors.h" #import "Foundation/NSMapTable.h" #import "Foundation/NSNull.h" #import "Foundation/NSValue.h" @@ -55,6 +57,25 @@ @implementation NilMarker static NSMapTable *globalClassMap = 0; +static NSError * +GSNSErrorFromUnarchiverException(NSException *exception) +{ + NSDictionary *info; + NSString *reason = [exception reason]; + + if (reason == nil) + { + reason = @"The archive data could not be decoded."; + } + info = [NSDictionary dictionaryWithObjectsAndKeys: + reason, NSLocalizedDescriptionKey, + [exception name], @"NSExceptionName", + nil]; + return [NSError errorWithDomain: NSCocoaErrorDomain + code: NSFileReadCorruptFileError + userInfo: info]; +} + #define GETVAL \ id o; \ \ @@ -387,8 +408,23 @@ + (id) unarchivedObjectOfClasses: (GS_GENERIC_CLASS(NSSet,Class)*)classes fromData: (NSData*)data error: (NSError**)error { + id object = nil; + /* FIXME: implement proper secure coding support */ - return [self unarchiveObjectWithData: data]; + NS_DURING + { + object = RETAIN([self unarchiveObjectWithData: data]); + } + NS_HANDLER + { + if (error != 0) + { + *error = GSNSErrorFromUnarchiverException(localException); + } + DESTROY(object); + } + NS_ENDHANDLER + return AUTORELEASE(object); } + (NSArray*) unarchivedArrayOfObjectsOfClass: (Class)cls @@ -1007,4 +1043,3 @@ + (Class) classForKeyedUnarchiver return self; } @end - diff --git a/Tests/base/NSKeyedArchiver/general.m b/Tests/base/NSKeyedArchiver/general.m index c7d5d3efea..304864723b 100644 --- a/Tests/base/NSKeyedArchiver/general.m +++ b/Tests/base/NSKeyedArchiver/general.m @@ -3,6 +3,7 @@ #import #import #import +#import #import #import #import @@ -21,6 +22,7 @@ int main() NSArray *a; NSURL *u; NSMutableSet *ms; + NSError *error = nil; NSKeyedArchiver *archiver = nil; NSKeyedUnarchiver *unarchiver = nil; @@ -67,6 +69,22 @@ int main() PASS((a != nil && [a isKindOfClass:[NSArray class]] && [a isEqual:vals2]), "unarchiveObjectWithFile: seems ok"); + data1 = [NSData dataWithBytes: "nope" length: 4]; + error = nil; + a = [NSKeyedUnarchiver unarchivedObjectOfClass: [NSString class] + fromData: data1 + error: &error]; + PASS((a == nil && error != nil), + "unarchivedObjectOfClass:fromData:error: reports invalid data as NSError"); + + error = nil; + a = [NSKeyedUnarchiver unarchivedObjectOfClasses: + [NSSet setWithObject: [NSString class]] + fromData: data1 + error: &error]; + PASS((a == nil && error != nil), + "unarchivedObjectOfClasses:fromData:error: reports invalid data as NSError"); + // encode data2 = [NSMutableData dataWithCapacity: 10240]; archiver = AUTORELEASE([[NSKeyedArchiver alloc] From 09bfdd78b40f00407782ccc0434d492a4bcdb15e Mon Sep 17 00:00:00 2001 From: Daniel Boyd Date: Mon, 4 May 2026 14:25:27 -0500 Subject: [PATCH 12/12] Tighten NSKeyedArchiver retained NSError annotations --- Headers/Foundation/NSKeyedArchiver.h | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/Headers/Foundation/NSKeyedArchiver.h b/Headers/Foundation/NSKeyedArchiver.h index ea5856650e..cbbd8febf1 100644 --- a/Headers/Foundation/NSKeyedArchiver.h +++ b/Headers/Foundation/NSKeyedArchiver.h @@ -98,7 +98,7 @@ GS_EXPORT_CLASS */ + (NSData *) archivedDataWithRootObject: (id)anObject requiringSecureCoding: (BOOL)requiresSecureCoding - error: (NSError **)error; + error: (NSError *_Nullable *_Nullable)error; #endif /** @@ -315,11 +315,11 @@ GS_EXPORT_CLASS + (id) unarchivedObjectOfClass: (Class)cls fromData: (NSData*)data - error: (NSError**)error; + error: (NSError *_Nullable *_Nullable)error; + (id) unarchivedObjectOfClasses: (GS_GENERIC_CLASS(NSSet,Class)*)classes fromData: (NSData*)data - error: (NSError**)error; + error: (NSError *_Nullable *_Nullable)error; #endif @@ -327,21 +327,21 @@ GS_EXPORT_CLASS + (NSArray*) unarchivedArrayOfObjectsOfClass: (Class)cls fromData: (NSData*)data - error: (NSError**)error; + error: (NSError *_Nullable *_Nullable)error; + (NSArray*) unarchivedArrayOfObjectsOfClasses: (GS_GENERIC_CLASS(NSSet,Class)*)classes fromData: (NSData*)data - error: (NSError**)error; + error: (NSError *_Nullable *_Nullable)error; + (NSDictionary*) unarchivedDictionaryWithKeysOfClass: (Class)keyCls objectsOfClass: (Class)valueCls fromData: (NSData*)data - error: (NSError**)error; + error: (NSError *_Nullable *_Nullable)error; + (NSDictionary*) unarchivedDictionaryWithKeysOfClasses: (GS_GENERIC_CLASS(NSSet,Class)*)keyClasses objectsOfClasses: (GS_GENERIC_CLASS(NSSet,Class)*)valueClasses fromData: (NSData*)data - error: (NSError**)error; + error: (NSError *_Nullable *_Nullable)error; #endif @@ -668,4 +668,3 @@ willReplaceObject: (id)anObject #endif /* GS_API_MACOSX */ #endif /* __NSKeyedArchiver_h_GNUSTEP_BASE_INCLUDE */ -