diff --git a/infer/documentation/issues/ASSIGN_POINTER_WARNING.md b/infer/documentation/issues/ASSIGN_POINTER_WARNING.md new file mode 100644 index 000000000..08bcf0d43 --- /dev/null +++ b/infer/documentation/issues/ASSIGN_POINTER_WARNING.md @@ -0,0 +1,4 @@ +This check fires when a pointer to an Obj-C object is tagged with an `assign` +property (similar to the `-Warc-unsafe-retained-assign` compiler flag). Not +holding a strong reference to the object makes it easy to accidentally create +and use a dangling pointer. diff --git a/infer/documentation/issues/BAD_POINTER_COMPARISON.md b/infer/documentation/issues/BAD_POINTER_COMPARISON.md new file mode 100644 index 000000000..dd61f11d7 --- /dev/null +++ b/infer/documentation/issues/BAD_POINTER_COMPARISON.md @@ -0,0 +1,14 @@ +Infer reports these warnings in Objective-C when a boxed primitive type such as +`NSNumber *` is coerced to a boolean in a comparison. For example, consider the +code + +```objectivec +void foo(NSNumber * n) { + if (n) ... +``` + +The branch in the above code will be taken when the pointer `n` is non-`nil`, +but the programmer might have actually wanted the branch to be taken when the +integer pointed to by `n` is nonzero (e.g., she may have meant to call an +accessor like `[n intValue]` instead). Infer will ask the programmer explicitly +compare `n` to `nil` or call an accessor to clarify her intention. diff --git a/infer/documentation/issues/CAPTURED_STRONG_SELF.md b/infer/documentation/issues/CAPTURED_STRONG_SELF.md new file mode 100644 index 000000000..a262deac7 --- /dev/null +++ b/infer/documentation/issues/CAPTURED_STRONG_SELF.md @@ -0,0 +1,8 @@ +This will happen in one of two cases generally: + +1. One uses `weakSelf` but forgot to declare it weak first. +2. One is using `strongSelf`, declared in a block, in another (inside) block. + This changes the delicate balance of the `weakSelf`/`strongSelf` use in the + first block. The retain cycle is avoided there because `strongSelf` is a + local variable to the block. If `strongSelf` is used in the inside block, + then it's not a local variable anymore, but a captured variable. diff --git a/infer/documentation/issues/CHECKERS_FRAGMENT_RETAINS_VIEW.md b/infer/documentation/issues/CHECKERS_FRAGMENT_RETAINS_VIEW.md new file mode 100644 index 000000000..54616822f --- /dev/null +++ b/infer/documentation/issues/CHECKERS_FRAGMENT_RETAINS_VIEW.md @@ -0,0 +1,9 @@ +This error type is Android-specific. It fires when a `Fragment` type fails to +nullify one or more of its declared `View` fields in `onDestroyView`. In +performance-sensitive applications, a `Fragment` should initialize all `View`'s +in `onCreateView` and nullify them in `onDestroyView`. If a `Fragment` is placed +on the back stack and fails to nullify a `View` in `onDestroyView`, it will +retain a useless reference to that `View` that will not be cleaned up until the +`Fragment` is resumed or destroyed. + +Action: Nullify the `View` in question in `onDestroyView`. diff --git a/infer/documentation/issues/CHECKERS_IMMUTABLE_CAST.md b/infer/documentation/issues/CHECKERS_IMMUTABLE_CAST.md new file mode 100644 index 000000000..e21684fb2 --- /dev/null +++ b/infer/documentation/issues/CHECKERS_IMMUTABLE_CAST.md @@ -0,0 +1,15 @@ +This error type is reported in Java. It fires when an immutable collection is +returned from a method whose type is mutable. + +```java + public List getSomeList() { + ImmutableList l = foo(...); + return l; + } +``` + +This can lead to a runtime error if users of `getSomeList` try to modify the +list e.g. by adding elements. + +Action: you can change the return type to be immutable, or make a copy of the +collection so that it can be modified. diff --git a/infer/documentation/issues/COMPONENT_FACTORY_FUNCTION.md b/infer/documentation/issues/COMPONENT_FACTORY_FUNCTION.md new file mode 100644 index 000000000..e69de29bb diff --git a/infer/documentation/issues/COMPONENT_INITIALIZER_WITH_SIDE_EFFECTS.md b/infer/documentation/issues/COMPONENT_INITIALIZER_WITH_SIDE_EFFECTS.md new file mode 100644 index 000000000..e69de29bb diff --git a/infer/documentation/issues/COMPONENT_WITH_MULTIPLE_FACTORY_METHODS.md b/infer/documentation/issues/COMPONENT_WITH_MULTIPLE_FACTORY_METHODS.md new file mode 100644 index 000000000..e69de29bb diff --git a/infer/documentation/issues/COMPONENT_WITH_UNCONVENTIONAL_SUPERCLASS.md b/infer/documentation/issues/COMPONENT_WITH_UNCONVENTIONAL_SUPERCLASS.md new file mode 100644 index 000000000..7852627d4 --- /dev/null +++ b/infer/documentation/issues/COMPONENT_WITH_UNCONVENTIONAL_SUPERCLASS.md @@ -0,0 +1 @@ +[Doc in ComponentKit page](http://componentkit.org/docs/never-subclass-components) diff --git a/infer/documentation/issues/CXX_REFERENCE_CAPTURED_IN_OBJC_BLOCK.md b/infer/documentation/issues/CXX_REFERENCE_CAPTURED_IN_OBJC_BLOCK.md new file mode 100644 index 000000000..ca290fd3e --- /dev/null +++ b/infer/documentation/issues/CXX_REFERENCE_CAPTURED_IN_OBJC_BLOCK.md @@ -0,0 +1,14 @@ +With this check, Infer detects C++ references captured in a block. Doing this is +almost always wrong. The reason is that C++ references are not managed pointers +(like ARC pointers) and so the referent is likely to be gone by the time the +block gets executed. One solution is to do a local copy of the reference and +pass that to the block. Example: + +```c +(int &) v; +... +const int copied_v = v; +^{ +// use copied_v not v +}; +``` diff --git a/infer/documentation/issues/DEADLOCK.md b/infer/documentation/issues/DEADLOCK.md new file mode 100644 index 000000000..f693b5c4f --- /dev/null +++ b/infer/documentation/issues/DEADLOCK.md @@ -0,0 +1,60 @@ +This error is currently reported in Java. A deadlock occurs when two distinct +threads try to acquire two locks in reverse orders. The following code +illustrates a textbook example. Of course, in real deadlocks, the lock +acquisitions may be separated by deeply nested call chains. + +```java + public void lockAThenB() { + synchronized(lockA) { + synchronized(lockB) { + // do something with both resources + } + } + } + + public void lockBThenA() { + synchronized(lockB) { + synchronized(lockA) { + // do something with both resources + } + } + } +``` + +The standard solution to a deadlock is to fix an order of lock acquisition and +adhere to that order in all cases. Another solution may be to shrink the +critical sections (i.e., the code executing under lock) to the minimum required. + +Old-style containers such as `Vector` are synchronized on the object monitor, +which means that deadlocks can occur even without explicit synchronisation on +both threads. For instance: + +```java + public void lockAThenAddToVector() { + synchronized(lockA) { + vector.add(object); + } + } + + public void lockVectorThenA() { + synchronized(vector) { + synchronized(lockA) { + // do something with both resources + } + } + } +``` + +Infer has support for detecting these deadlocks too. + +To suppress reports of deadlocks in a method `m()` use the +`@SuppressLint("DEADLOCK")` annotation, as follows: + +```java + import android.annotation.SuppressLint; + + @SuppressLint("DEADLOCK") + public void m() { + ... + } +``` diff --git a/infer/documentation/issues/DEAD_STORE.md b/infer/documentation/issues/DEAD_STORE.md new file mode 100644 index 000000000..981453cfa --- /dev/null +++ b/infer/documentation/issues/DEAD_STORE.md @@ -0,0 +1,2 @@ +This error is reported in C++. It fires when the value assigned to a variables +is never used (e.g., `int i = 1; i = 2; return i;`). diff --git a/infer/documentation/issues/DIRECT_ATOMIC_PROPERTY_ACCESS.md b/infer/documentation/issues/DIRECT_ATOMIC_PROPERTY_ACCESS.md new file mode 100644 index 000000000..fc0a7df95 --- /dev/null +++ b/infer/documentation/issues/DIRECT_ATOMIC_PROPERTY_ACCESS.md @@ -0,0 +1,5 @@ +This check warns you when you are accessing an atomic property directly with an +ivar. This makes the atomic property not atomic anymore. So potentially you may +get a race condition. + +To fix the problem you need to access properties with their getter or setter. diff --git a/infer/documentation/issues/DISCOURAGED_WEAK_PROPERTY_CUSTOM_SETTER.md b/infer/documentation/issues/DISCOURAGED_WEAK_PROPERTY_CUSTOM_SETTER.md new file mode 100644 index 000000000..ecfed164d --- /dev/null +++ b/infer/documentation/issues/DISCOURAGED_WEAK_PROPERTY_CUSTOM_SETTER.md @@ -0,0 +1,64 @@ +This check warns you when you have a custom setter for a weak property. When +compiled with Automatic Reference Counting (ARC, `-fobj-arc`) ARC may set the +property to `nil` without invoking the setter, for example: + +```objectivec +#import + +@interface Employee : NSObject { + NSString* _name; + __weak Employee* _manager; +} +-(id)initWithName:(NSString*)name; +@property(atomic, weak) Employee* manager; +-(void)report; +@end + +@implementation Employee + +-(id)initWithName:(NSString*)name { + _name = name; + return self; +} + +-(NSString*)description { + return _name; +} + +-(void)report { + NSLog(@"I work for %@", _manager); +} + +-(Employee*)manager { + return _manager; +} + +// DON'T do this; ARC will not call this when setting _manager to nil. +-(void)setManager:(Employee*)newManager { + NSLog(@"Meet the new boss..."); + _manager = newManager; +} + +@end + +int main(int argc, char *argv[]) +{ + Employee* bob = [[Employee alloc] initWithName:@"Bob"]; + Employee* sue = [[Employee alloc] initWithName:@"Sue"]; + bob.manager = sue; + [bob report]; + sue = nil; + [bob report]; + return 0; +} +``` + +This prints: + +``` +Meet the new boss... +I work for Sue +I work for (null) +``` + +Note that the custom setter was only invoked once. diff --git a/infer/documentation/issues/EMPTY_VECTOR_ACCESS.md b/infer/documentation/issues/EMPTY_VECTOR_ACCESS.md new file mode 100644 index 000000000..d88ad32b0 --- /dev/null +++ b/infer/documentation/issues/EMPTY_VECTOR_ACCESS.md @@ -0,0 +1,12 @@ +This error type is reported only in C++, in versions >= C++11. + +The code is trying to access an element of a vector that Infer believes to be +empty. Such an access will cause undefined behavior at runtime. + +```c++ +#include +int foo(){ + const std::vector vec; + return vec[0]; // Empty vector access reported here +} +``` diff --git a/infer/documentation/issues/ERADICATE_CONDITION_REDUNDANT.md b/infer/documentation/issues/ERADICATE_CONDITION_REDUNDANT.md new file mode 100644 index 000000000..2b39866e0 --- /dev/null +++ b/infer/documentation/issues/ERADICATE_CONDITION_REDUNDANT.md @@ -0,0 +1,22 @@ +This report is inactive by default. Condition (x != null) or (x == null) when x +cannot be null: the first condition is always true and the second is always +false + +Example: + +```java +class C { + void m() { + String s = new String("abc"); + if (s != null) { + int n = s.length(); + } + } +} +``` + +Action: Make sure that the annotations are correct, as the condition is +considered redundant based on the existing annotations. In particular, check the +annotation of any input parameters and fields of the current method, as well as +the annotations of any method called directly by the current method, if +relevant. If the annotations are correct, you can remove the redundant case. diff --git a/infer/documentation/issues/ERADICATE_FIELD_NOT_INITIALIZED.md b/infer/documentation/issues/ERADICATE_FIELD_NOT_INITIALIZED.md new file mode 100644 index 000000000..09c95b1fa --- /dev/null +++ b/infer/documentation/issues/ERADICATE_FIELD_NOT_INITIALIZED.md @@ -0,0 +1,17 @@ +The constructor does not initialize a field f which is not annotated with +@Nullable + +Example: + +```java +class C { + String f; + + C () { // field f not initialized and not annotated @Nullable + } +} +``` + +Action: The preferred action is to initialize the field with a value that is not +null. If, by design, null is a valid value for the field, then it should be +annotated with @Nullable. diff --git a/infer/documentation/issues/ERADICATE_FIELD_NOT_NULLABLE.md b/infer/documentation/issues/ERADICATE_FIELD_NOT_NULLABLE.md new file mode 100644 index 000000000..657108c9d --- /dev/null +++ b/infer/documentation/issues/ERADICATE_FIELD_NOT_NULLABLE.md @@ -0,0 +1,20 @@ +An assignment x.f = v where v could be null and field f is not annotated with +@Nullable. + +Example: + +```java +class C { + String f; + + void foo(@Nullable String s) { + f = s; + } +} +``` + +Action: The preferred action is to ensure that a null value is never stored in +the field, by changing the code or changing annotations. If this cannot be done, +add a @Nullable annotation to the field. This annotation might trigger more +warnings in other code that uses the field, as that code must now deal with null +values. diff --git a/infer/documentation/issues/ERADICATE_INCONSISTENT_SUBCLASS_PARAMETER_ANNOTATION.md b/infer/documentation/issues/ERADICATE_INCONSISTENT_SUBCLASS_PARAMETER_ANNOTATION.md new file mode 100644 index 000000000..399927c35 --- /dev/null +++ b/infer/documentation/issues/ERADICATE_INCONSISTENT_SUBCLASS_PARAMETER_ANNOTATION.md @@ -0,0 +1,40 @@ +A parameter of the overridden method is missing a @Nullable annotation present in the superclass. + +Action: choose a consistent annotation based on the desired invariant. + +Example: + +```java +class A { + + int len(@Nullable String s) { + if (s != null) { + return s.length(); + } else { + return 0; + } + } +} + +class B extends A { + + int len(String s) { // @Nullable missing. + return s.length(); + } +} +``` + +A consistent use of @Nullable on parameters across subtyping should prevent runtime issue like in: + +```java +public class Main { + + String s; + + int foo() { + A a = new B(); + return a.len(s); + } +} +``` + diff --git a/infer/documentation/issues/ERADICATE_INCONSISTENT_SUBCLASS_RETURN_ANNOTATION.md b/infer/documentation/issues/ERADICATE_INCONSISTENT_SUBCLASS_RETURN_ANNOTATION.md new file mode 100644 index 000000000..001126dfa --- /dev/null +++ b/infer/documentation/issues/ERADICATE_INCONSISTENT_SUBCLASS_RETURN_ANNOTATION.md @@ -0,0 +1,39 @@ +The return type of the overridden method is annotated @Nullable, but the +corresponding method in the superclass is not. + +Action: choose a consistent annotation based on the desired invariant. + +Example: + +```java +class A { + String create() { + return new String("abc"); + } +} + +class B extends A { + @Nullable String create() { // Inconsistent @Nullable annotation. + return null; + } +} +``` + +A consistent use of `@Nullable` on the return type across subtyping should prevent +runtime issue like in: + +```java +class Main { + + int foo(A a) { + String s = a.create(); + return s.length(); + } + + void main(String[] args) { + A a = new B(); + foo(a); + } + +} +``` diff --git a/infer/documentation/issues/ERADICATE_PARAMETER_NOT_NULLABLE.md b/infer/documentation/issues/ERADICATE_PARAMETER_NOT_NULLABLE.md new file mode 100644 index 000000000..610bbda01 --- /dev/null +++ b/infer/documentation/issues/ERADICATE_PARAMETER_NOT_NULLABLE.md @@ -0,0 +1,22 @@ +Method call x.m(..., v, ...) where v can be null and the corresponding parameter +in method m is not annotated with @Nullable + +Example: + +```java +class C { + void m(C x) { + String s = x.toString() + } + + void test(@Nullable C x) { + m(x); + } +} +``` + +Action: The preferred action is to ensure that a null value is never passed to +the method, by changing the code or changing annotations. If this cannot be +done, add a @Nullable annotation to the relevant parameter in the method +declaration. This annotation might trigger more warnings in the implementation +of method m, as that code must now deal with null values. diff --git a/infer/documentation/issues/ERADICATE_RETURN_NOT_NULLABLE.md b/infer/documentation/issues/ERADICATE_RETURN_NOT_NULLABLE.md new file mode 100644 index 000000000..4555db7c5 --- /dev/null +++ b/infer/documentation/issues/ERADICATE_RETURN_NOT_NULLABLE.md @@ -0,0 +1,18 @@ +Method m can return null, but the method's return type is not annotated with +@Nullable + +Example: + +```java +class C { + String m() { + return null; + } +} +``` + +Action: The preferred action is to ensure that a null value is never returned by +the method, by changing the code or changing annotations. If this cannot be +done, add a @Nullable annotation to the the method declaration. This annotation +might trigger more warnings in the callers of method m, as the callers must now +deal with null values. diff --git a/infer/documentation/issues/ERADICATE_RETURN_OVER_ANNOTATED.md b/infer/documentation/issues/ERADICATE_RETURN_OVER_ANNOTATED.md new file mode 100644 index 000000000..0f0311fb6 --- /dev/null +++ b/infer/documentation/issues/ERADICATE_RETURN_OVER_ANNOTATED.md @@ -0,0 +1,20 @@ +This report is inactive by default. Method m is annotated with @Nullable but the +method cannot return null + +Example: + +```java +class C { + @Nullable String m() { + String s = new String("abc"); + return s; + } +} +``` + +Action: Make sure that the annotations are correct, as the return annotation is +considered redundant based on the existing annotations. In particular, check the +annotation of any input parameters and fields of the current method, as well as +the annotations of any method called directly by the current method, if +relevant. If the annotations are correct, you can remove the @Nullable +annotation. diff --git a/infer/documentation/issues/GLOBAL_VARIABLE_INITIALIZED_WITH_FUNCTION_OR_METHOD_CALL.md b/infer/documentation/issues/GLOBAL_VARIABLE_INITIALIZED_WITH_FUNCTION_OR_METHOD_CALL.md new file mode 100644 index 000000000..7842f0202 --- /dev/null +++ b/infer/documentation/issues/GLOBAL_VARIABLE_INITIALIZED_WITH_FUNCTION_OR_METHOD_CALL.md @@ -0,0 +1,4 @@ +This checker warns you when the initialization of global variable contain a +method or function call. The warning wants to make you aware that some functions +are expensive. As the global variables are initialized before main() is called, +these initializations can slow down the start-up time of an app. diff --git a/infer/documentation/issues/INTERFACE_NOT_THREAD_SAFE.md b/infer/documentation/issues/INTERFACE_NOT_THREAD_SAFE.md new file mode 100644 index 000000000..bec452ec6 --- /dev/null +++ b/infer/documentation/issues/INTERFACE_NOT_THREAD_SAFE.md @@ -0,0 +1,6 @@ +This error indicates that you have invoked an interface method not annotated +with `@ThreadSafe` from a thread-safe context (e.g., code that uses locks or is +marked `@ThreadSafe`). The fix is to add the `@ThreadSafe` annotation to the +interface or to the interface method. For background on why these annotations +are needed, see the detailed explanation +[here](racerd#interface-not-thread-safe). diff --git a/infer/documentation/issues/IVAR_NOT_NULL_CHECKED.md b/infer/documentation/issues/IVAR_NOT_NULL_CHECKED.md new file mode 100644 index 000000000..370a75fbc --- /dev/null +++ b/infer/documentation/issues/IVAR_NOT_NULL_CHECKED.md @@ -0,0 +1,14 @@ +This error type is only reported in Objective-C. This is similar to Null +dereference, but Infer hasn't found a whole trace where the error can happen, +but only found that a null dereference can happen if an instance variable of a +parameter is `nil`. For example: + +```objectivec + -(int) foo { + B b* = [self->_a foo]; // sending a message with receiver nil returns nil + return b->x; // dereferencing b, potential NPE if you pass nil as the argument a. + } +``` + +Possible solutions are adding a check for `nil`, or making sure that the method +is not called with `nil`. diff --git a/infer/documentation/issues/LOCK_CONSISTENCY_VIOLATION.md b/infer/documentation/issues/LOCK_CONSISTENCY_VIOLATION.md new file mode 100644 index 000000000..ab32d00af --- /dev/null +++ b/infer/documentation/issues/LOCK_CONSISTENCY_VIOLATION.md @@ -0,0 +1,20 @@ +This is a C++ and Objective C error reported whenever: + +- A class contains a member `lock` used for synchronization (most often a + `std::mutex`). +- It has a public method which writes to some member `x` while holding `lock`. +- It has a public method which reads `x` without holding `lock`. + +The above may happen through a chain of calls. Above, `x` may also be a +container (an array, a vector, etc). + +### Fixing Lock Consistency Violation reports + +- Avoid the offending access (most often the read). Of course, this may not be + possible. +- Use synchronization to protect the read, by using the same lock protecting the + corresponding write. +- Make the method doing the read access private. This should silence the + warning, since Infer looks for a pair of non-private methods. Objective-C: + Infer considers a method as private if it's not exported in the header-file + interface. diff --git a/infer/documentation/issues/MEMORY_LEAK.md b/infer/documentation/issues/MEMORY_LEAK.md new file mode 100644 index 000000000..abec84e6b --- /dev/null +++ b/infer/documentation/issues/MEMORY_LEAK.md @@ -0,0 +1,24 @@ +### Memory leak in C + +This error type is only reported in C and Objective-C code. In Java we do not +report memory leaks because it is a garbage collected language. + +In C, Infer reports memory leaks when objects are created with `malloc` and not +freed. For example: + +```c +-(void) memory_leak_bug { + struct Person *p = malloc(sizeof(struct Person)); +} +``` + +### Memory leak in Objective-C + +Additionally, in Objective-C, Infer reports memory leaks that happen when +objects from Core Foundation or Core Graphics don't get released. + +```objectivec +-(void) memory_leak_bug_cf { + CGPathRef shadowPath = CGPathCreateWithRect(self.inputView.bounds, NULL); //object created and not released. +} +``` diff --git a/infer/documentation/issues/MIXED_SELF_WEAKSELF.md b/infer/documentation/issues/MIXED_SELF_WEAKSELF.md new file mode 100644 index 000000000..df0442745 --- /dev/null +++ b/infer/documentation/issues/MIXED_SELF_WEAKSELF.md @@ -0,0 +1,4 @@ +This happens when an Objective-C block captures both `self` and `weakSelf`, a +weak pointer to `self`. Possibly the developer meant to capture only `weakSelf` +to avoid a retain cycle, but made a typo and used `self` as well in the block, +instead of `strongSelf`. In this case, this could cause a retain cycle. diff --git a/infer/documentation/issues/MULTIPLE_WEAKSELF.md b/infer/documentation/issues/MULTIPLE_WEAKSELF.md new file mode 100644 index 000000000..eb19264bd --- /dev/null +++ b/infer/documentation/issues/MULTIPLE_WEAKSELF.md @@ -0,0 +1,5 @@ +An Objective-C block uses `weakSelf` more than once. This could lead to +unexpected behaviour. Even if `weakSelf` is not nil in the first use, it could +be nil in the following uses since the object that `weakSelf` points to could be +freed anytime. One should assign it to a strong pointer first, and then use it +in the block. diff --git a/infer/documentation/issues/MUTABLE_LOCAL_VARIABLE_IN_COMPONENT_FILE.md b/infer/documentation/issues/MUTABLE_LOCAL_VARIABLE_IN_COMPONENT_FILE.md new file mode 100644 index 000000000..36907ca1d --- /dev/null +++ b/infer/documentation/issues/MUTABLE_LOCAL_VARIABLE_IN_COMPONENT_FILE.md @@ -0,0 +1 @@ +[Doc in ComponentKit page](http://componentkit.org/docs/avoid-local-variables) diff --git a/infer/documentation/issues/PARAMETER_NOT_NULL_CHECKED.md b/infer/documentation/issues/PARAMETER_NOT_NULL_CHECKED.md new file mode 100644 index 000000000..eaf3ffeb9 --- /dev/null +++ b/infer/documentation/issues/PARAMETER_NOT_NULL_CHECKED.md @@ -0,0 +1,24 @@ +This error type is reported only in Objective-C. It is similar to Null +dereference, but Infer hasn't found a whole trace where the error can happen, +but only found that a null dereference can happen if you call a method with nil +as an argument. Therefore it is only a warning. For example: + +```objectivec + -(int) foo:(A* a) { + B b* = [a foo]; // sending a message with receiver nil returns nil + return b->x; // dereferencing b, potential NPE if you pass nil as the argument a. + } +``` + +or when the parameter is a block: + +```objectivec + -(void) foo:(void (^)(BOOL))block { + block(YES); // calling a nil block will cause a crash. + } +``` + +Possible solutions are adding a check for `nil`, or making sure that the method +is not called with `nil`. When an argument will never be `nil`, you can add the +annotation `nonnull` to the argument's type, to tell Infer (and the type +system), that the argument won't be `nil`. This will silence the warning. diff --git a/infer/documentation/issues/POINTER_TO_CONST_OBJC_CLASS.md b/infer/documentation/issues/POINTER_TO_CONST_OBJC_CLASS.md new file mode 100644 index 000000000..8d63f54f0 --- /dev/null +++ b/infer/documentation/issues/POINTER_TO_CONST_OBJC_CLASS.md @@ -0,0 +1,4 @@ +In Objective-C, `const Class *` represents a mutable pointer pointing to an +Objective-C class where the ivars cannot be changed. More useful is +`Class *const` instead, meaning the destination of the pointer cannot be +changed. diff --git a/infer/documentation/issues/PREMATURE_NIL_TERMINATION_ARGUMENT.md b/infer/documentation/issues/PREMATURE_NIL_TERMINATION_ARGUMENT.md new file mode 100644 index 000000000..b8cd556d6 --- /dev/null +++ b/infer/documentation/issues/PREMATURE_NIL_TERMINATION_ARGUMENT.md @@ -0,0 +1,15 @@ +This error type is reported in C and Objective-C. In many variadic methods, +`nil` is used to signify the end of the list of input objects. This is similar +to nil-termination of C strings. If one of the arguments that is not the last +argument to the method is `nil` as well, Infer reports an error because that may +lead to unexpected behavior. + +An example of such variadic methods is +[arrayWithObjects](https://developer.apple.com/library/prerelease/ios/documentation/Cocoa/Reference/Foundation/Classes/NSArray_Class/index.html#//apple_ref/occ/clm/NSArray/arrayWithObjects) + +```objectivec + NSArray *foo = [NSArray arrayWithObjects: @"aaa", str, @"bbb", nil]; +``` + +In this example, if `str` is `nil` then an array `@[@"aaa"]` of size 1 will be +created, and not an array `@[@"aaa", str, @"bbb"]` of size 3 as expected. diff --git a/infer/documentation/issues/REGISTERED_OBSERVER_BEING_DEALLOCATED.md b/infer/documentation/issues/REGISTERED_OBSERVER_BEING_DEALLOCATED.md new file mode 100644 index 000000000..2a540f68d --- /dev/null +++ b/infer/documentation/issues/REGISTERED_OBSERVER_BEING_DEALLOCATED.md @@ -0,0 +1,5 @@ +Objects register with a notification center to receive notifications. This check +warns you when an object is registered as observer of a NSNotificationCenter but +it is never unregistered. This is problematic as if the object is not +unregistered the notification center can still send notification even after the +object has been deallocated. In that case we would get a crash. diff --git a/infer/documentation/issues/RESOURCE_LEAK.md b/infer/documentation/issues/RESOURCE_LEAK.md new file mode 100644 index 000000000..394ba5407 --- /dev/null +++ b/infer/documentation/issues/RESOURCE_LEAK.md @@ -0,0 +1,273 @@ +Infer reports resource leaks in C, Objective-C and Java. In general, resources +are entities such as files, sockets, connections, etc, that need to be closed +after being used. + +### Resource leak in C + +This is an example of a resource leak in C code: + +```c +-(void) resource_leak_bug { + FILE *fp; + fp=fopen("c:\\test.txt", "r"); // file opened and not closed. +} +``` + +### Resource leak in Java + +For the remaining of this section, we will consider examples of resource leaks +in Java code. + +TIP: A common source of bugs is exceptions skipping past close() +statements. That is the first thing to look for if INFER reports a potential +resource leak. + +### Basics and Standard Idiom + +Some objects in Java, the resources, are supposed to be closed when you +stop using them, and failure to close is a resource leak. Resources +include input streams, output streams, readers, writers, sockets, http +connections, cursors, and json parsers. + +The standard idiom is + +```java + // Standard idiom + Allocate resource + try { + do some stuff + } finally { + close resource + } +``` + +or more for example, + +```java + // Standard Idiom + public static void foo () throws IOException{ + FileOutputStream fos = new FileOutputStream(new File("whatever.txt")); + try { + fos.write(7); + } finally { + fos.close(); + } + } +``` + +and you should use the standard idiom for the most part, when you don't want to +return the resource to the surrounding context. + +Sometimes people just leave out close(), and that is a bug, but more typically +exceptional paths are the root of the problem, as in + +```java + // leak because of exception + public static void foo () throws IOException { + FileOutputStream fos = new FileOutputStream(new File("whatever.txt")); + fos.write(7); //DOH! What if exception? + fos.close(); + } +``` + +where an exception in fos.write will cause execution to skip past the close() +statement. + +#### Multiple Resources Bugs + +We can deal with multiple resources correctly and simply just by nesting the +standard idiom. + +```java + // Two Resources nested + public static void foo() throws IOException { + FileInputStream fis = new FileInputStream(new File("whatever.txt")); + try { + FileOutputStream fos = new FileOutputStream(new File("everwhat.txt")); + try { + fos.write(fis.read()); + } finally { + fos.close(); + } + } finally { + fis.close(); + } + } +``` + +Bugs often occur when using multiple resources in other ways because of +exceptions in close() methods. For example, + +```java + // Classic Two Resources Bug + public static void foo() throws IOException { + FileInputStream fis = null; + FileOutputStream fos = null; + try { + fis = new FileInputStream(new File("whatever.txt")); + fos = new FileOutputStream(new File("everwhat.txt")); + fos.write(fis.read()); + } finally { + if (fis!=null) fis.close(); + if (fos!=null) fos.close(); + } + } +``` + +Here, if there is an exception in the call to fis.close() execution will skip +past fos.close(); a leak. + +Another way, besides the standard idiom, to deal with this problem is to swallow +exceptions. + +```java + // Two Resources Fix 1 + public static void foo() throws IOException { + FileInputStream fis = null; + FileOutputStream fos = null; + try { + fis = new FileInputStream(new File("whatever.txt")); + fos = new FileOutputStream(new File("everwhat.txt")); + fos.write(fis.read()); + } finally { + try { + if (fis!=null) fis.close(); + } catch (Exception e) {}; // Exception swallowing + if (fos!=null) fos.close(); + } + } +``` + +You can also swallow the exception on the output stream. Some people prefer not +to swallow output stream exceptions, and also flush before closing. +http://code.google.com/p/guava-libraries/issues/detail?id=1118 + +Notice that the nested standard idiom does not need the checks for null, which +are in there in this case to protect against the case when one of the +allocations throws an exception, in which case one would get a +NullPointerException. + +### Nested_Allocations + +When a resource allocation is included as an argument to a constructor, if the +constructor fails it can leave an an unreachable resource that no one can close. + +For example gzipOutputStream = new GZIPOutputStream(new FileOutputStream(out)); +is bad in case the outer constructor, GZIPOutputStream, throws an exception. In +that case, no one will have a hold of the FileOutputStream and so no one will be +able to close it. + +In such a case you need to move the allocation the FileOutputStream out of the +nested position and name it, so you are able to close if anything goes wrong +during execution of the GZIPOutputStream constructor. + +Here are resources that can throw exceptions i their constructor(s). + +- ObjectInputStream , ObjectOutputStream, PipedInputStream, PipedOutputStream, + PipedReader, PipedWriter, JarInputStream, JarOutputStream, GZIPInputStream, + GZIPOutputStream , ZipFile all throw IOException +- PrintStream throws UnsupportedEncodingException + +The constructors for FileInputStream, FileOutputStream and RandomAccessFile +throw FileNotFoundException, but these cases are not problematic in the sense +that their arguments are not resources and so they do not cause the nested +resource leak. + +### Allocation of JSonParser and Cursor resources + +Some resources are created inside libraries instead of by "new". + +Cursor is an interface, the actual resources are something like SQLiteCursor. +So, every time you call a function that returns a Cursor object, there is an +allocation. + +For instance, in the functions from SQLiteDatabase query(…) and rawQuery(…) +allocate a cursor resource. For SQLiteQueryBuilder, ContentProviderClient, +ContentResolver. MediaStore and DownloadManager it is only query(…) Cursor +objects cursor created by these functions need to be closed (i.e., +cursor.close()). + +Similarly, JsonParser is an abstract class, and create a resource in functions +from the class JsonFactory createParser(byte[] data) createParser(byte[] data, +int offset, int len) createParser(String content) createParser(URL url) +createParser(File f) JsonParser objects js created by these functions need to be +closed (jp.close()). On the other hand . JasonParsers gotten from +createParser(InputStream in) and createParser(Reader r) give you JsonParsers +that don’t need to be closed. This is because they receive the resource from +somewhere that will maintain the responsibility to close it. + +### Escaping resources and exceptions + +Sometimes you want to return a resource to the outside, in which case you should +not close it, but you still need to be careful of exceptions in case control +skips past the return leaving no one to close. Here is a simple example of a +positive use of escaping resources. + +```java + // An escaping resource, shouldn't close + public BugReportAttachment createAttachment(File reportDirectory, String fileName) + throws FileNotFoundException { + File file = new File(reportDirectory, fileName); + OutputStream stream = new FileOutputStream(file); + return new BugReportAttachment(Uri.fromFile(file), stream); + } +``` + +In this case it is intended that an object that wraps `stream` is passed to the +caller of `createAttachment`. You should certainly not close stream here, +because it is being passed to the outside. + +But for escaping resources like this you still need to be careful of exceptions. +For example, in + +```java + // An escaping resource, and a leak + public BugReportAttachment createAttachment(File reportDirectory, String fileName) + throws FileNotFoundException { + File file = new File(reportDirectory, fileName); + OutputStream stream = new FileOutputStream(file); + stream.write(7); + return new BugReportAttachment(Uri.fromFile(file), stream); + } +``` + +if stream.write(7) throws an exception, then no one will have a hold of stream, +and no one will be able to close it; a leak. + +### Java 7's try-with-resources + +**(For use with Java 7 only)** + +Clearly, accounting for the ramifications of all the exceptional cases is +complicated, and there is a better way in Java 7. + +```java + // Two Resources Fix 2; via try-with-resources + public static void foo() throws IOException { + try ( + FileInputStream fis = new FileInputStream(new File("whatever.txt")); + FileOutputStream fos = new FileOutputStream(new File("everwhat.txt")) + ) { + fos.write(fis.read()); + } + } +``` + +All the complicated exceptional cases above are (apparently) covered by this +construct, and the result is much simpler. + +So, if you are trying to fix a potential leak in code with multiples resources +you can go ahead and try to understand whether the potential leak is real. Or, +if the code is complex and it is hard to figure out, it would be perfectly +legitimate to simply convert the code over to try-with-resources if you have +access to Java 7, so as to save yourself some brain-cycles. You will also end up +with cleaner code. + +If try-with-resources is so great you should always use it. But you +shouldn't… Try-with-resources gives resources static scoping, and works via a +stack discipline. Sometimes, you want a resource to persist beyond scope, as in +the escaping example above. In an escaping example maybe you could refactor lots +of code so that try-with-resources applies, and maybe you cannot in a sensible +way. This just illustrates that, though you might hear people say that +try-with-resources "solves" the resource problem, it does not. It is very +useful, but you cannot use it blindly when you see a resource-allocation site. diff --git a/infer/documentation/issues/RETAIN_CYCLE.md b/infer/documentation/issues/RETAIN_CYCLE.md new file mode 100644 index 000000000..242f129e5 --- /dev/null +++ b/infer/documentation/issues/RETAIN_CYCLE.md @@ -0,0 +1,30 @@ +A retain cycle is a situation when object A retains object B, and object B +retains object A at the same time. Here is an example: + +```objectivec +@class Child; +@interface Parent : NSObject { + Child *child; // Instance variables are implicitly __strong +} +@end +@interface Child : NSObject { + Parent *parent; +} +@end +``` + +You can fix a retain cycle in ARC by using \_\_weak variables or weak properties +for your "back links", i.e. links to direct or indirect parents in an object +hierarchy: + +```objectivec +@class Child; +@interface Parent : NSObject { + Child *child; +} +@end +@interface Child : NSObject { + __weak Parent *parent; +} +@end +``` diff --git a/infer/documentation/issues/STARVATION.md b/infer/documentation/issues/STARVATION.md new file mode 100644 index 000000000..15339baa1 --- /dev/null +++ b/infer/documentation/issues/STARVATION.md @@ -0,0 +1,55 @@ +This error is reported in Java, and specifically on Android. These reports are +triggered when a method that runs on the UI thread may block, thus potentially +leading to an Application Not Responding error. + +Infer considers a method as running on the UI thread whenever: + +- The method, one of its overrides, its class, or an ancestral class, is + annotated with `@UiThread`. +- The method, or one of its overrides is annotated with `@OnEvent`, `@OnClick`, + etc. +- The method or its callees call a `Litho.ThreadUtils` method such as + `assertMainThread`. + +The issue is reported when a method deemed to run on the UI thread + +- Makes a method call which may block. +- Takes a lock, and another thread takes the same lock, and before releasing it, + makes a call that may block. + +Calls that may block are considered: + +- Certain I/O calls. +- Two way `Binder.transact` calls. +- Certain OS calls. +- `Future` or `AsyncTask` calls to `get` without timeouts, or with too large + timeouts. + +To suppress starvation reports in a method `m()` use the +`@SuppressLint("STARVATION")` annotation, as follows: + +```java + import android.annotation.SuppressLint; + + @SuppressLint("STARVATION") + public void m() { + ... + } +``` + +To signal to Infer that a method does not perform any blocking calls, despite +appearences, you can use the `@NonBlocking` annotation: + +```java + import com.facebook.infer.annotation.NonBlocking; + + @NonBlocking + public void m() { + ... + } +``` + +This instructs Infer to filter out any potentially blocking calls in `m()` +(also, transitively), and thus any other method can expect no starvation reports +due to a call to `m()`. You will need to set up your class path appropriately to +include the JAR files in `infer/annotations` for this annotation to work. diff --git a/infer/documentation/issues/STRICT_MODE_VIOLATION.md b/infer/documentation/issues/STRICT_MODE_VIOLATION.md new file mode 100644 index 000000000..63f2b5d97 --- /dev/null +++ b/infer/documentation/issues/STRICT_MODE_VIOLATION.md @@ -0,0 +1,9 @@ +Android has a feature called +[strict mode](https://developer.android.com/reference/android/os/StrictMode), +which if enabled, will flag the occasions where the main thread makes a call +that results in disk I/O, waiting on a network socket, etc. The analysis +catching starvation errors and deadlocks (the `--starvation` analysis) has the +ability to statically detect such violations. + +To suppress this warning, it's enough to annotate the offending method with +`@SuppressLint("STRICT_MODE_VIOLATION")`. diff --git a/infer/documentation/issues/STRONG_DELEGATE_WARNING.md b/infer/documentation/issues/STRONG_DELEGATE_WARNING.md new file mode 100644 index 000000000..fe9e04191 --- /dev/null +++ b/infer/documentation/issues/STRONG_DELEGATE_WARNING.md @@ -0,0 +1,3 @@ +This check warns you when you have a property called delegate or variations +thereof which is declared strong. The idea is that delegates should generally be +weak, otherwise this may cause retain cycles. diff --git a/infer/documentation/issues/STRONG_SELF_NOT_CHECKED.md b/infer/documentation/issues/STRONG_SELF_NOT_CHECKED.md new file mode 100644 index 000000000..23d6432cb --- /dev/null +++ b/infer/documentation/issues/STRONG_SELF_NOT_CHECKED.md @@ -0,0 +1,12 @@ +When a block captures `weakSelf` in the following pattern: + +``` +__weak __typeof(self) weakSelf = self; + int (^my_block)() = ^() { + __strong __typeof(weakSelf) strongSelf = weakSelf; + int y = strongSelf->x; +``` + +the variable `strongSelf` should be checked for `null` before being used, +otherwise this could cause a crash because the weak pointer `weakSelf` could be +`null`. diff --git a/infer/documentation/issues/THREAD_SAFETY_VIOLATION.md b/infer/documentation/issues/THREAD_SAFETY_VIOLATION.md new file mode 100644 index 000000000..7b008c486 --- /dev/null +++ b/infer/documentation/issues/THREAD_SAFETY_VIOLATION.md @@ -0,0 +1,94 @@ +This warning indicates a potential data race in Java. The analyser is called +RacerD and this section gives brief but a mostly complete description of its +features. See the [RacerD page](/docs/racerd) for more in-depth information and +examples. + +NB this warning **is not related to @GuardedBy** and not issued by the same +analysis. + +### Thread-safety: What is a data race + +Here a data race is a pair of accesses to the same member field such that: + +- at least one is a write, and, +- at least one occurs without any lock synchronization, and, +- the two accesses occur on threads (if known) which can run in parallel. + +### Thread-safety: Potential fixes + +- Synchronizing the accesses (using the `synchronized` keyword, thread-exclusion + such as atomic objects, `volatile` etc). +- Making an offending method private -- this will exclude it from being checked + at the top level, though it will be checked if called by a public method which + may itself, e.g., hold a lock when calling it. +- Putting the two accesses on the same thread, e.g., by using `@MainThread` or + `@ThreadConfined`. + +### Thread-safety: Conditions checked before reporting + +The class and method are not marked `@ThreadSafe(enableChecks = false)`, and, + +- The method is declared `synchronized`, or employs (non-transitively) locking, + or, +- The class is not marked `@NotThreadSafe`, and, + - The class/method is marked `@ThreadSafe,` or one of the configured synonyms + in `.inferconfig`, or, + - A parent class, or an override method are marked with the above annotations. + +NB currently RacerD **does not take into account `@GuardedBy`**. + +### Thread-safety: Thread annotations recognized by RacerD + +These class and method annotations imply the method is on the main thread: +`@MainThread`, `@UiThread` + +These method annotations imply the method is on the main thread: `@OnBind`, +`@OnEvent`, `@OnMount`, `@OnUnbind`, `@OnUnmount` + +Both classes of annotations work through the inheritance tree (i.e. if a parent +class or method is marked with one of these annotations, so is the child class / +method override). + +In addition to these, RacerD recognizes many lifecycle methods as necessarily +running on the main thread, eg `Fragment.onCreate` etc. + +Finally, the thread status of being on the main thread propagates backwards +through the call graph (ie if `foo` calls `bar` and `bar` is marked `@UiThtread` +then `foo` is automatically considered on the main thread too). Calling +`assertMainThread`, `assertOnUiThread`, `checkOnMainThread` has the same effect. + +NB RacerD currently **does not recognize `@WorkerThread`, `@BinderThread` or +`@AnyThread`**. + +### Thread-safety: Other annotations and what they do + +These annotations can be found at `com.facebook.infer.annotation.*`. + +- `@Functional` This is a method annotation indicating the method always returns + the same value. When a method `foo` is annotated `@Functional`, RacerD will + ignore any writes of the return value of `foo`. For example, in + `this.x = foo()`, the write to `this.x` is ignored. The reasoning is that if + the method returns the same value whenever it's called, any data race on + `this.x` is benign, if that is the only write. + +- `@ThreadConfined` This is a class/method/field annotation which takes a single + parameter which can be `UI`, `ANY` or a user chosen string. It indicates to + RacerD a thread identifier for the class/method/field. Thus, + `@ThreadConfined(UI)` is equivalent to `@UiThread`, and `@ThreadConfined(ANY)` + is equivalent to not having the annotation at all, for classes and methods. + When this annotation is applied to a field it instructs Infer to assume + (without checking) that all accesses to that field are made on the same thread + (and can, therefore, not race by definition). The intention is that RacerD + uses that to detect exclusion between accesses occurring on the same thread. + However, only the UI thread is supported at this time, and any user provided + value is considered equal to `UI`. + +- `@VisibleForTesting` A method annotation making Infer consider the method as + effectively `private`. This means it will not be checked for races against + other non-private methods of the class, but only if called by one. + +- `@ReturnsOwnership` A method annotation indicating that the method returns a + freshly owned object. Accesses to the returned value will not be considered + for data races, as the object is in-effect unique and not accessible yet from + other threads. The main utility of this annotation is in interfaces, where + Infer cannot look up the implementation and decide for itself. diff --git a/infer/documentation/issues/UNAVAILABLE_API_IN_SUPPORTED_IOS_SDK.md b/infer/documentation/issues/UNAVAILABLE_API_IN_SUPPORTED_IOS_SDK.md new file mode 100644 index 000000000..b803fd85d --- /dev/null +++ b/infer/documentation/issues/UNAVAILABLE_API_IN_SUPPORTED_IOS_SDK.md @@ -0,0 +1,20 @@ +This checks warns you when you are using an API (constant, method call, etc.) +that is only defined in a version higher than the version that you support. To +enable this check, pass to Infer the option +`--iphoneos-target-sdk-version version`. Calling an undefined API will lead to a +crash in the app. To fix this, you can choose a different API or use it inside +an if, as in: + +```objectivec +if ([UIFont respondsToSelector:@selector(systemFontOfSize:weight:)]) { + font = [UIFont systemFontOfSize:size weight:0]; +} +``` + +or + +```objectivec +if (kCFCoreFoundationVersionNumber >= kCFCoreFoundationVersionNumber_iOS_9_0) { + font = [UIFont systemFontOfSize:size weight:0]; +} +``` diff --git a/infer/documentation/issues/WEAK_SELF_IN_NO_ESCAPE_BLOCK.md b/infer/documentation/issues/WEAK_SELF_IN_NO_ESCAPE_BLOCK.md new file mode 100644 index 000000000..e98aa9761 --- /dev/null +++ b/infer/documentation/issues/WEAK_SELF_IN_NO_ESCAPE_BLOCK.md @@ -0,0 +1,4 @@ +In many methods that take a block as an argument, the block position is +annotated with NS_NOESCAPE to mark that the block passed to this method won't be +leaving the current scope. In those cases, there is no need to use `weakSelf` to +avoid the block to capture `self`. This issue type flags this case. diff --git a/infer/lib/linter_rules/linters.al b/infer/lib/linter_rules/linters.al index 596b5190e..1cf25c73a 100644 --- a/infer/lib/linter_rules/linters.al +++ b/infer/lib/linter_rules/linters.al @@ -7,124 +7,124 @@ // a property declared atomic should not be accessed directly via its ivar DEFINE-CHECKER DIRECT_ATOMIC_PROPERTY_ACCESS = { - SET report_when = - WHEN - ((NOT context_in_synchronized_block()) AND is_ivar_atomic()) - AND NOT is_method_property_accessor_of_ivar() - AND NOT is_objc_constructor() - AND NOT is_objc_dealloc() - HOLDS-IN-NODE ObjCIvarRefExpr; - - SET message = "Direct access to ivar %ivar_name% of an atomic property"; - SET suggestion = "Accessing an ivar of an atomic property makes the property nonatomic."; - SET severity = "WARNING"; + SET report_when = + WHEN + ((NOT context_in_synchronized_block()) AND is_ivar_atomic()) + AND NOT is_method_property_accessor_of_ivar() + AND NOT is_objc_constructor() + AND NOT is_objc_dealloc() + HOLDS-IN-NODE ObjCIvarRefExpr; + + SET message = "Direct access to ivar %ivar_name% of an atomic property"; + SET suggestion = "Accessing an ivar of an atomic property makes the property nonatomic."; + SET severity = "WARNING"; }; // ASSIGN_POINTER_WARNING: // a property with a pointer type should not be declared `assign` DEFINE-CHECKER ASSIGN_POINTER_WARNING = { - SET report_when = - WHEN - is_assign_property() AND is_property_pointer_type() + SET report_when = + WHEN + is_assign_property() AND is_property_pointer_type() HOLDS-IN-NODE ObjCPropertyDecl; - SET message = "Property %decl_name% is a pointer type marked with the `assign` attribute"; - SET suggestion = "Use a different attribute like `strong` or `weak`."; - SET severity = "WARNING"; + SET message = "Property %decl_name% is a pointer type marked with the `assign` attribute"; + SET suggestion = "Use a different attribute like `strong` or `weak`."; + SET severity = "WARNING"; }; // BAD_POINTER_COMPARISON: // Fires whenever a NSNumber is dangerously coerced to a boolean in a comparison DEFINE-CHECKER BAD_POINTER_COMPARISON = { - LET bool_op = - is_binop_with_kind("LAnd") OR is_binop_with_kind("LOr") - OR is_unop_with_kind("LNot") OR is_unop_with_kind("LNot"); + LET bool_op = + is_binop_with_kind("LAnd") OR is_binop_with_kind("LOr") + OR is_unop_with_kind("LNot") OR is_unop_with_kind("LNot"); LET comparison_with_integral = - ( is_binop_with_kind("EQ") OR is_binop_with_kind("NE") - OR is_binop_with_kind("GT") OR is_binop_with_kind("GE") - OR is_binop_with_kind("LT") OR is_binop_with_kind("LE")) - AND - ( (is_node("ImplicitCastExpr") AND has_type("NSNumber *") - AND has_cast_kind("IntegralToPointer") - ) HOLDS-NEXT); + ( is_binop_with_kind("EQ") OR is_binop_with_kind("NE") + OR is_binop_with_kind("GT") OR is_binop_with_kind("GE") + OR is_binop_with_kind("LT") OR is_binop_with_kind("LE")) + AND + ( (is_node("ImplicitCastExpr") AND has_type("NSNumber *") + AND has_cast_kind("IntegralToPointer") + ) HOLDS-NEXT); - LET root_is_stmt_expecting_bool = - is_node("IfStmt") OR is_node("ForStmt") OR is_node("WhileStmt"); + LET root_is_stmt_expecting_bool = + is_node("IfStmt") OR is_node("ForStmt") OR is_node("WhileStmt"); - LET use_num_as_bool = - (bool_op OR root_is_stmt_expecting_bool) AND (has_type("NSNumber *") HOLDS-NEXT); + LET use_num_as_bool = + (bool_op OR root_is_stmt_expecting_bool) AND (has_type("NSNumber *") HOLDS-NEXT); - LET bad_conditional = - is_node("ConditionalOperator") AND (has_type("NSNumber *") HOLDS-NEXT WITH-TRANSITION Cond); + LET bad_conditional = + is_node("ConditionalOperator") AND (has_type("NSNumber *") HOLDS-NEXT WITH-TRANSITION Cond); - SET report_when = - use_num_as_bool OR comparison_with_integral OR bad_conditional; + SET report_when = + use_num_as_bool OR comparison_with_integral OR bad_conditional; SET message = "Implicitly checking whether NSNumber pointer is nil or comparing to integral value"; - SET suggestion = - "Did you mean to use/compare against the unboxed value instead? Please either explicitly compare the NSNumber instance to nil, or use one of the NSNumber accessors before the comparison."; + SET suggestion = + "Did you mean to use/compare against the unboxed value instead? Please either explicitly compare the NSNumber instance to nil, or use one of the NSNumber accessors before the comparison."; }; DEFINE-CHECKER REGISTERED_OBSERVER_BEING_DEALLOCATED = { - LET exists_method_calling_addObserver = - call_method("addObserver:selector:name:object:") HOLDS-EVENTUALLY; + LET exists_method_calling_addObserver = + call_method("addObserver:selector:name:object:") HOLDS-EVENTUALLY; - LET exists_method_calling_addObserverForName = - call_method("addObserverForName:object:queue:usingBlock:") HOLDS-EVENTUALLY; + LET exists_method_calling_addObserverForName = + call_method("addObserverForName:object:queue:usingBlock:") HOLDS-EVENTUALLY; - LET add_observer = - exists_method_calling_addObserver OR exists_method_calling_addObserverForName; + LET add_observer = + exists_method_calling_addObserver OR exists_method_calling_addObserverForName; - LET eventually_addObserver = - IN-NODE ObjCMethodDecl WITH-TRANSITION Body - (add_observer) - HOLDS-EVENTUALLY; + LET eventually_addObserver = + IN-NODE ObjCMethodDecl WITH-TRANSITION Body + (add_observer) + HOLDS-EVENTUALLY; - LET exists_method_calling_removeObserver = - call_method("removeObserver:") HOLDS-EVENTUALLY; + LET exists_method_calling_removeObserver = + call_method("removeObserver:") HOLDS-EVENTUALLY; - LET exists_method_calling_removeObserverName = - call_method("removeObserver:name:object:") HOLDS-EVENTUALLY; + LET exists_method_calling_removeObserverName = + call_method("removeObserver:name:object:") HOLDS-EVENTUALLY; - LET remove_observer = - exists_method_calling_removeObserver OR exists_method_calling_removeObserverName; + LET remove_observer = + exists_method_calling_removeObserver OR exists_method_calling_removeObserverName; - LET remove_observer_in_block = - IN-NODE BlockDecl WITH-TRANSITION Body - (remove_observer) - HOLDS-EVENTUALLY; + LET remove_observer_in_block = + IN-NODE BlockDecl WITH-TRANSITION Body + (remove_observer) + HOLDS-EVENTUALLY; - LET remove_observer1 = - remove_observer OR remove_observer_in_block; + LET remove_observer1 = + remove_observer OR remove_observer_in_block; - LET remove_observer_in_method = - IN-NODE ObjCMethodDecl WITH-TRANSITION Body - (remove_observer1) - HOLDS-EVENTUALLY; + LET remove_observer_in_method = + IN-NODE ObjCMethodDecl WITH-TRANSITION Body + (remove_observer1) + HOLDS-EVENTUALLY; - LET eventually_removeObserver = - IN-NODE ObjCImplementationDecl, ObjCProtocolDecl WITH-TRANSITION Any - (remove_observer_in_method OR - remove_observer_in_method HOLDS-IN-SOME-SUPERCLASS-OF ObjCImplementationDecl) - HOLDS-EVENTUALLY; + LET eventually_removeObserver = + IN-NODE ObjCImplementationDecl, ObjCProtocolDecl WITH-TRANSITION Any + (remove_observer_in_method OR + remove_observer_in_method HOLDS-IN-SOME-SUPERCLASS-OF ObjCImplementationDecl) + HOLDS-EVENTUALLY; - SET report_when = - WHEN - NOT (eventually_addObserver IMPLIES eventually_removeObserver) AND - NOT iphoneos_target_sdk_version_greater_or_equal("9.0") //this is not needed after iOS SDK 9.0 - HOLDS-IN-NODE ObjCImplementationDecl, ObjCProtocolDecl; + SET report_when = + WHEN + NOT (eventually_addObserver IMPLIES eventually_removeObserver) AND + NOT iphoneos_target_sdk_version_greater_or_equal("9.0") //this is not needed after iOS SDK 9.0 + HOLDS-IN-NODE ObjCImplementationDecl, ObjCProtocolDecl; - SET message = - "Object self is registered in a notification center but not being removed before deallocation"; + SET message = + "Object self is registered in a notification center but not being removed before deallocation"; - SET suggestion = - "Consider removing the object from the notification center before its deallocation."; + SET suggestion = + "Consider removing the object from the notification center before its deallocation."; }; @@ -132,14 +132,14 @@ DEFINE-CHECKER STRONG_DELEGATE_WARNING = { LET name_contains_delegate = declaration_has_name(REGEXP("[dD]elegate")); LET name_does_not_contain_delegates = - NOT declaration_has_name(REGEXP("[dD]elegates")); + NOT declaration_has_name(REGEXP("[dD]elegates")); LET name_does_not_contains_queue = - NOT declaration_has_name(REGEXP("[qQ]ueue")); + NOT declaration_has_name(REGEXP("[qQ]ueue")); SET report_when = - WHEN - name_contains_delegate AND name_does_not_contain_delegates AND name_does_not_contains_queue AND is_strong_property() - HOLDS-IN-NODE ObjCPropertyDecl; + WHEN + name_contains_delegate AND name_does_not_contain_delegates AND name_does_not_contains_queue AND is_strong_property() + HOLDS-IN-NODE ObjCPropertyDecl; SET message = "Property or ivar %decl_name% declared strong"; SET suggestion = "In general delegates should be declared weak or assign."; @@ -151,22 +151,22 @@ DEFINE-CHECKER GLOBAL_VARIABLE_INITIALIZED_WITH_FUNCTION_OR_METHOD_CALL = { LET is_global_but_not_const_variable = is_objc_extension() AND is_global_var() AND (NOT is_const_expr_var()) AND (NOT is_init_integral_constant_expr()); - LET makes_an_expensive_call = - (is_node("CallExpr") AND NOT call_function("CGPointMake")) - OR is_node("CXXTemporaryObjectExpr") + LET makes_an_expensive_call = + (is_node("CallExpr") AND NOT call_function("CGPointMake")) + OR is_node("CXXTemporaryObjectExpr") OR is_node("CXXMemberCallExpr") OR is_node("CXXOperatorCallExpr") OR is_node("ObjCMessageExpr"); LET is_initialized_with_expensive_call = IN-NODE VarDecl WITH-TRANSITION InitExpr - (makes_an_expensive_call HOLDS-EVENTUALLY) - HOLDS-EVENTUALLY; + (makes_an_expensive_call HOLDS-EVENTUALLY) + HOLDS-EVENTUALLY; SET report_when = - WHEN - (is_global_but_not_const_variable AND is_initialized_with_expensive_call) - HOLDS-IN-NODE VarDecl; + WHEN + (is_global_but_not_const_variable AND is_initialized_with_expensive_call) + HOLDS-IN-NODE VarDecl; SET message = "Global variable %decl_name% is initialized using a function or method call"; @@ -177,75 +177,75 @@ DEFINE-CHECKER GLOBAL_VARIABLE_INITIALIZED_WITH_FUNCTION_OR_METHOD_CALL = { DEFINE-CHECKER CXX_REFERENCE_CAPTURED_IN_OBJC_BLOCK = { - // The current node is block definition capturing a C++ reference - LET capture_reference = - WHEN - ((is_node("BlockDecl") AND captures_cxx_references()) HOLDS-NEXT) - HOLDS-IN-NODE BlockExpr; + // The current node is block definition capturing a C++ reference + LET capture_reference = + WHEN + ((is_node("BlockDecl") AND captures_cxx_references()) HOLDS-NEXT) + HOLDS-IN-NODE BlockExpr; - // At some point we encounter a block definition capturing a C++ reference - LET block_definition_capture_reference = - capture_reference HOLDS-EVENTUALLY; + // At some point we encounter a block definition capturing a C++ reference + LET block_definition_capture_reference = + capture_reference HOLDS-EVENTUALLY; - // A variable definition initialized with a block capturing a C++ reference - LET variable_initialized_with_block = - IN-NODE VarDecl WITH-TRANSITION InitExpr - (block_definition_capture_reference) - HOLDS-EVENTUALLY; + // A variable definition initialized with a block capturing a C++ reference + LET variable_initialized_with_block = + IN-NODE VarDecl WITH-TRANSITION InitExpr + (block_definition_capture_reference) + HOLDS-EVENTUALLY; - // Reference to a variable initialized with a capturing block - LET variable_block_definition = - IN-NODE DeclRefExpr WITH-TRANSITION PointerToDecl - (variable_initialized_with_block) - HOLDS-EVENTUALLY; + // Reference to a variable initialized with a capturing block + LET variable_block_definition = + IN-NODE DeclRefExpr WITH-TRANSITION PointerToDecl + (variable_initialized_with_block) + HOLDS-EVENTUALLY; - // Report when a function that does not have NoEscapeAttribute call a block or a variable definied with - // a block capturing a C++ ref - SET report_when = - WHEN - (NOT has_no_escape_attribute) AND (block_definition_capture_reference OR variable_block_definition) - HOLDS-IN-NODE CallExpr; + // Report when a function that does not have NoEscapeAttribute call a block or a variable definied with + // a block capturing a C++ ref + SET report_when = + WHEN + (NOT has_no_escape_attribute) AND (block_definition_capture_reference OR variable_block_definition) + HOLDS-IN-NODE CallExpr; - SET message = - "C++ Reference variable(s) %cxx_ref_captured_in_block% captured by Objective-C block"; + SET message = + "C++ Reference variable(s) %cxx_ref_captured_in_block% captured by Objective-C block"; - SET suggestion = "This will very likely cause a crash because C++ References are unmanaged and may be invalid by the time the block executes."; + SET suggestion = "This will very likely cause a crash because C++ References are unmanaged and may be invalid by the time the block executes."; SET severity = "ERROR"; - SET mode = "ON"; - }; + SET mode = "ON"; +}; - // If the declaration has availability attributes, check that it's compatible with - // the iphoneos_target_sdk_version - DEFINE-CHECKER UNAVAILABLE_API_IN_SUPPORTED_IOS_SDK = { +// If the declaration has availability attributes, check that it's compatible with +// the iphoneos_target_sdk_version +DEFINE-CHECKER UNAVAILABLE_API_IN_SUPPORTED_IOS_SDK = { - SET report_when = - WHEN HOLDS-NEXT WITH-TRANSITION PointerToDecl - (decl_unavailable_in_supported_ios_sdk() AND - NOT within_responds_to_selector_block()) - HOLDS-IN-NODE DeclRefExpr, ObjCMessageExpr; + SET report_when = + WHEN HOLDS-NEXT WITH-TRANSITION PointerToDecl + (decl_unavailable_in_supported_ios_sdk() AND + NOT within_responds_to_selector_block()) + HOLDS-IN-NODE DeclRefExpr, ObjCMessageExpr; - SET message = - "%decl_ref_or_selector_name% is not available in the required iOS SDK version %iphoneos_target_sdk_version% (only available from version %available_ios_sdk%)"; + SET message = + "%decl_ref_or_selector_name% is not available in the required iOS SDK version %iphoneos_target_sdk_version% (only available from version %available_ios_sdk%)"; SET name = "Unavailable API In Supported iOS SDK"; - SET suggestion = "This could cause a crash."; - SET severity = "ERROR"; - }; - - DEFINE-CHECKER UNAVAILABLE_CLASS_IN_SUPPORTED_IOS_SDK = { - SET report_when = - WHEN ((class_unavailable_in_supported_ios_sdk()) AND - NOT within_available_class_block() AND - (call_class_method("alloc") OR - call_class_method("new"))) - HOLDS-IN-NODE ObjCMessageExpr; - - SET message = - "The receiver %receiver_method_call% of %name% is not available in the required iOS SDK version %iphoneos_target_sdk_version% (only available from version %class_available_ios_sdk%)"; - SET name = "Unavailable API In Supported iOS SDK"; - SET severity = "ERROR"; - SET mode = "ON"; - }; + SET suggestion = "This could cause a crash."; + SET severity = "ERROR"; +}; + +DEFINE-CHECKER UNAVAILABLE_CLASS_IN_SUPPORTED_IOS_SDK = { + SET report_when = + WHEN ((class_unavailable_in_supported_ios_sdk()) AND + NOT within_available_class_block() AND + (call_class_method("alloc") OR + call_class_method("new"))) + HOLDS-IN-NODE ObjCMessageExpr; + + SET message = + "The receiver %receiver_method_call% of %name% is not available in the required iOS SDK version %iphoneos_target_sdk_version% (only available from version %class_available_ios_sdk%)"; + SET name = "Unavailable API In Supported iOS SDK"; + SET severity = "ERROR"; + SET mode = "ON"; +}; DEFINE-CHECKER POINTER_TO_INTEGRAL_IMPLICIT_CAST = { @@ -253,7 +253,7 @@ DEFINE-CHECKER POINTER_TO_INTEGRAL_IMPLICIT_CAST = { WHEN has_cast_kind("PointerToIntegral") HOLDS-IN-NODE ImplicitCastExpr; SET message = "Implicit conversion from %child_type% to %type% in usage of %name%"; - SET doc_url = "https://clang.llvm.org/docs/DiagnosticsReference.html#wint-conversion"; + SET doc_url = "https://clang.llvm.org/docs/DiagnosticsReference.html#wint-conversion"; }; DEFINE-CHECKER POINTER_TO_CONST_OBJC_CLASS = { @@ -263,7 +263,7 @@ DEFINE-CHECKER POINTER_TO_CONST_OBJC_CLASS = { it represents a mutable pointer pointing to an Objective-C class where the ivars cannot be changed."; SET suggestion = "Consider using `%class_name% *const` instead, meaning - the destination of the pointer cannot be changed."; + the destination of the pointer cannot be changed."; SET severity = "WARNING"; SET mode = "ON"; }; @@ -288,14 +288,14 @@ DEFINE-CHECKER DISCOURAGED_WEAK_PROPERTY_CUSTOM_SETTER = { DEFINE-CHECKER WRONG_SCOPE_FOR_DISPATCH_ONCE_T = { SET report_when = - WHEN - NOT (is_global_var() OR is_static_local_var()) AND - has_type("dispatch_once_t") - HOLDS-IN-NODE VarDecl; - - SET message = "Variables of type dispatch_once_t must have global or static scope. The result of using this type with automatic or dynamic allocation is undefined."; - SET severity = "WARNING"; - SET mode = "ON"; + WHEN + NOT (is_global_var() OR is_static_local_var()) AND + has_type("dispatch_once_t") + HOLDS-IN-NODE VarDecl; + + SET message = "Variables of type dispatch_once_t must have global or static scope. The result of using this type with automatic or dynamic allocation is undefined."; + SET severity = "WARNING"; + SET mode = "ON"; }; @@ -309,8 +309,8 @@ DEFINE-CHECKER UNSAFE_CALL_TO_OPTIONAL_METHOD = { HOLDS-IN-NODE ObjCMessageExpr; SET message = "This is a call to an `@optional` protocol method. Calling it without checking if its implemented can lead to crashes at run time."; - SET suggestion = "Please make sure to test the method is implemented by first calling `if ([object respondsToSelector:@selector(%decl_name%)]) ...` "; - SET severity = "ERROR"; - SET mode = "ON"; + SET suggestion = "Please make sure to test the method is implemented by first calling `if ([object respondsToSelector:@selector(%decl_name%)]) ...` "; + SET severity = "ERROR"; + SET mode = "ON"; }; diff --git a/infer/man/man1/infer-full.txt b/infer/man/man1/infer-full.txt index fd3db8a6e..950c5f15c 100644 --- a/infer/man/man1/infer-full.txt +++ b/infer/man/man1/infer-full.txt @@ -360,9 +360,11 @@ OPTIONS ARRAY_OUT_OF_BOUNDS_L1 (disabled by default), ARRAY_OUT_OF_BOUNDS_L2 (disabled by default), ARRAY_OUT_OF_BOUNDS_L3 (disabled by default), + ASSIGN_POINTER_WARNING (enabled by default), Abduction_case_not_implemented (enabled by default), Array_of_pointsto (enabled by default), Assert_failure (enabled by default), + BAD_POINTER_COMPARISON (enabled by default), BIABDUCTION_ANALYSIS_STOPS (disabled by default), BIABD_CONDITION_ALWAYS_FALSE (disabled by default), BIABD_CONDITION_ALWAYS_TRUE (disabled by default), @@ -401,6 +403,7 @@ OPTIONS CONSTANT_ADDRESS_DEREFERENCE (disabled by default), CREATE_INTENT_FROM_URI (enabled by default), CROSS_SITE_SCRIPTING (enabled by default), + CXX_REFERENCE_CAPTURED_IN_OBJC_BLOCK (enabled by default), Cannot_star (enabled by default), DANGLING_POINTER_DEREFERENCE (disabled by default), DANGLING_POINTER_DEREFERENCE_MAYBE (disabled by default), @@ -409,6 +412,8 @@ OPTIONS DEALLOCATE_STACK_VARIABLE (enabled by default), DEALLOCATE_STATIC_MEMORY (enabled by default), DEALLOCATION_MISMATCH (enabled by default), + DIRECT_ATOMIC_PROPERTY_ACCESS (enabled by default), + DISCOURAGED_WEAK_PROPERTY_CUSTOM_SETTER (enabled by default), DIVIDE_BY_ZERO (disabled by default), DO_NOT_REPORT (enabled by default), EMPTY_VECTOR_ACCESS (enabled by default), @@ -483,12 +488,14 @@ OPTIONS NULL_TEST_AFTER_DEREFERENCE (disabled by default), PARAMETER_NOT_NULL_CHECKED (enabled by default), POINTER_SIZE_MISMATCH (enabled by default), + POINTER_TO_CONST_OBJC_CLASS (enabled by default), PRECONDITION_NOT_FOUND (enabled by default), PRECONDITION_NOT_MET (enabled by default), PREMATURE_NIL_TERMINATION_ARGUMENT (enabled by default), PULSE_MEMORY_LEAK (disabled by default), PURE_FUNCTION (enabled by default), QUANDARY_TAINT_ERROR (enabled by default), + REGISTERED_OBSERVER_BEING_DEALLOCATED (enabled by default), RESOURCE_LEAK (enabled by default), RETAIN_CYCLE (enabled by default), SHELL_INJECTION (enabled by default), @@ -501,12 +508,14 @@ OPTIONS STARVATION (enabled by default), STATIC_INITIALIZATION_ORDER_FIASCO (enabled by default), STRICT_MODE_VIOLATION (enabled by default), + STRONG_DELEGATE_WARNING (enabled by default), STRONG_SELF_NOT_CHECKED (enabled by default), Symexec_memory_error (enabled by default), THREAD_SAFETY_VIOLATION (enabled by default), TOPL_ERROR (enabled by default), UNARY_MINUS_APPLIED_TO_UNSIGNED_EXPRESSION (disabled by default), + UNAVAILABLE_API_IN_SUPPORTED_IOS_SDK (enabled by default), UNINITIALIZED_VALUE (enabled by default), UNREACHABLE_CODE (enabled by default), UNTRUSTED_BUFFER_ACCESS (disabled by default), diff --git a/infer/man/man1/infer-report.txt b/infer/man/man1/infer-report.txt index 339a6e0d0..bb0dbed12 100644 --- a/infer/man/man1/infer-report.txt +++ b/infer/man/man1/infer-report.txt @@ -84,9 +84,11 @@ OPTIONS ARRAY_OUT_OF_BOUNDS_L1 (disabled by default), ARRAY_OUT_OF_BOUNDS_L2 (disabled by default), ARRAY_OUT_OF_BOUNDS_L3 (disabled by default), + ASSIGN_POINTER_WARNING (enabled by default), Abduction_case_not_implemented (enabled by default), Array_of_pointsto (enabled by default), Assert_failure (enabled by default), + BAD_POINTER_COMPARISON (enabled by default), BIABDUCTION_ANALYSIS_STOPS (disabled by default), BIABD_CONDITION_ALWAYS_FALSE (disabled by default), BIABD_CONDITION_ALWAYS_TRUE (disabled by default), @@ -125,6 +127,7 @@ OPTIONS CONSTANT_ADDRESS_DEREFERENCE (disabled by default), CREATE_INTENT_FROM_URI (enabled by default), CROSS_SITE_SCRIPTING (enabled by default), + CXX_REFERENCE_CAPTURED_IN_OBJC_BLOCK (enabled by default), Cannot_star (enabled by default), DANGLING_POINTER_DEREFERENCE (disabled by default), DANGLING_POINTER_DEREFERENCE_MAYBE (disabled by default), @@ -133,6 +136,8 @@ OPTIONS DEALLOCATE_STACK_VARIABLE (enabled by default), DEALLOCATE_STATIC_MEMORY (enabled by default), DEALLOCATION_MISMATCH (enabled by default), + DIRECT_ATOMIC_PROPERTY_ACCESS (enabled by default), + DISCOURAGED_WEAK_PROPERTY_CUSTOM_SETTER (enabled by default), DIVIDE_BY_ZERO (disabled by default), DO_NOT_REPORT (enabled by default), EMPTY_VECTOR_ACCESS (enabled by default), @@ -207,12 +212,14 @@ OPTIONS NULL_TEST_AFTER_DEREFERENCE (disabled by default), PARAMETER_NOT_NULL_CHECKED (enabled by default), POINTER_SIZE_MISMATCH (enabled by default), + POINTER_TO_CONST_OBJC_CLASS (enabled by default), PRECONDITION_NOT_FOUND (enabled by default), PRECONDITION_NOT_MET (enabled by default), PREMATURE_NIL_TERMINATION_ARGUMENT (enabled by default), PULSE_MEMORY_LEAK (disabled by default), PURE_FUNCTION (enabled by default), QUANDARY_TAINT_ERROR (enabled by default), + REGISTERED_OBSERVER_BEING_DEALLOCATED (enabled by default), RESOURCE_LEAK (enabled by default), RETAIN_CYCLE (enabled by default), SHELL_INJECTION (enabled by default), @@ -225,12 +232,14 @@ OPTIONS STARVATION (enabled by default), STATIC_INITIALIZATION_ORDER_FIASCO (enabled by default), STRICT_MODE_VIOLATION (enabled by default), + STRONG_DELEGATE_WARNING (enabled by default), STRONG_SELF_NOT_CHECKED (enabled by default), Symexec_memory_error (enabled by default), THREAD_SAFETY_VIOLATION (enabled by default), TOPL_ERROR (enabled by default), UNARY_MINUS_APPLIED_TO_UNSIGNED_EXPRESSION (disabled by default), + UNAVAILABLE_API_IN_SUPPORTED_IOS_SDK (enabled by default), UNINITIALIZED_VALUE (enabled by default), UNREACHABLE_CODE (enabled by default), UNTRUSTED_BUFFER_ACCESS (disabled by default), diff --git a/infer/man/man1/infer.txt b/infer/man/man1/infer.txt index 88c5c4926..508acbf1d 100644 --- a/infer/man/man1/infer.txt +++ b/infer/man/man1/infer.txt @@ -360,9 +360,11 @@ OPTIONS ARRAY_OUT_OF_BOUNDS_L1 (disabled by default), ARRAY_OUT_OF_BOUNDS_L2 (disabled by default), ARRAY_OUT_OF_BOUNDS_L3 (disabled by default), + ASSIGN_POINTER_WARNING (enabled by default), Abduction_case_not_implemented (enabled by default), Array_of_pointsto (enabled by default), Assert_failure (enabled by default), + BAD_POINTER_COMPARISON (enabled by default), BIABDUCTION_ANALYSIS_STOPS (disabled by default), BIABD_CONDITION_ALWAYS_FALSE (disabled by default), BIABD_CONDITION_ALWAYS_TRUE (disabled by default), @@ -401,6 +403,7 @@ OPTIONS CONSTANT_ADDRESS_DEREFERENCE (disabled by default), CREATE_INTENT_FROM_URI (enabled by default), CROSS_SITE_SCRIPTING (enabled by default), + CXX_REFERENCE_CAPTURED_IN_OBJC_BLOCK (enabled by default), Cannot_star (enabled by default), DANGLING_POINTER_DEREFERENCE (disabled by default), DANGLING_POINTER_DEREFERENCE_MAYBE (disabled by default), @@ -409,6 +412,8 @@ OPTIONS DEALLOCATE_STACK_VARIABLE (enabled by default), DEALLOCATE_STATIC_MEMORY (enabled by default), DEALLOCATION_MISMATCH (enabled by default), + DIRECT_ATOMIC_PROPERTY_ACCESS (enabled by default), + DISCOURAGED_WEAK_PROPERTY_CUSTOM_SETTER (enabled by default), DIVIDE_BY_ZERO (disabled by default), DO_NOT_REPORT (enabled by default), EMPTY_VECTOR_ACCESS (enabled by default), @@ -483,12 +488,14 @@ OPTIONS NULL_TEST_AFTER_DEREFERENCE (disabled by default), PARAMETER_NOT_NULL_CHECKED (enabled by default), POINTER_SIZE_MISMATCH (enabled by default), + POINTER_TO_CONST_OBJC_CLASS (enabled by default), PRECONDITION_NOT_FOUND (enabled by default), PRECONDITION_NOT_MET (enabled by default), PREMATURE_NIL_TERMINATION_ARGUMENT (enabled by default), PULSE_MEMORY_LEAK (disabled by default), PURE_FUNCTION (enabled by default), QUANDARY_TAINT_ERROR (enabled by default), + REGISTERED_OBSERVER_BEING_DEALLOCATED (enabled by default), RESOURCE_LEAK (enabled by default), RETAIN_CYCLE (enabled by default), SHELL_INJECTION (enabled by default), @@ -501,12 +508,14 @@ OPTIONS STARVATION (enabled by default), STATIC_INITIALIZATION_ORDER_FIASCO (enabled by default), STRICT_MODE_VIOLATION (enabled by default), + STRONG_DELEGATE_WARNING (enabled by default), STRONG_SELF_NOT_CHECKED (enabled by default), Symexec_memory_error (enabled by default), THREAD_SAFETY_VIOLATION (enabled by default), TOPL_ERROR (enabled by default), UNARY_MINUS_APPLIED_TO_UNSIGNED_EXPRESSION (disabled by default), + UNAVAILABLE_API_IN_SUPPORTED_IOS_SDK (enabled by default), UNINITIALIZED_VALUE (enabled by default), UNREACHABLE_CODE (enabled by default), UNTRUSTED_BUFFER_ACCESS (disabled by default), diff --git a/infer/src/base/IssueType.ml b/infer/src/base/IssueType.ml index a05ba13e1..05ab139bd 100644 --- a/infer/src/base/IssueType.ml +++ b/infer/src/base/IssueType.ml @@ -221,8 +221,18 @@ let assert_failure = register_from_string ~visibility:Developer ~id:"Assert_failure" Error Biabduction +let _assign_pointer_warning = + register_from_string ~id:"ASSIGN_POINTER_WARNING" Warning Linters + ~user_documentation:[%blob "../../documentation/issues/ASSIGN_POINTER_WARNING.md"] + + let bad_footprint = register_from_string ~visibility:Developer ~id:"Bad_footprint" Error Biabduction +let _bad_pointer_comparison = + register_from_string ~id:"BAD_POINTER_COMPARISON" Warning Linters + ~user_documentation:[%blob "../../documentation/issues/BAD_POINTER_COMPARISON.md"] + + let biabduction_analysis_stops = register_from_string ~visibility:Developer ~enabled:false ~id:"BIABDUCTION_ANALYSIS_STOPS" Warning Biabduction @@ -280,6 +290,7 @@ let cannot_star = register_from_string ~visibility:Developer ~id:"Cannot_star" E let captured_strong_self = register_from_string ~id:"CAPTURED_STRONG_SELF" ~hum:"Captured strongSelf" Error SelfInBlock + ~user_documentation:[%blob "../../documentation/issues/CAPTURED_STRONG_SELF.md"] let checkers_allocates_memory = @@ -305,10 +316,12 @@ let checkers_expensive_overrides_unexpensive = let checkers_fragment_retain_view = register_from_string ~id:"CHECKERS_FRAGMENT_RETAINS_VIEW" ~hum:"Fragment Retains View" Warning FragmentRetainsView + ~user_documentation:[%blob "../../documentation/issues/CHECKERS_FRAGMENT_RETAINS_VIEW.md"] let checkers_immutable_cast = register_from_string ~id:"CHECKERS_IMMUTABLE_CAST" Warning ImmutableCast + ~user_documentation:[%blob "../../documentation/issues/CHECKERS_IMMUTABLE_CAST.md"] let checkers_printf_args = register_from_string ~id:"CHECKERS_PRINTF_ARGS" Error PrintfArgs @@ -322,6 +335,7 @@ let class_load = register_from_string ~id:"CLASS_LOAD" Warning ClassLoads let component_factory_function = register_from_string ~id:"COMPONENT_FACTORY_FUNCTION" Advice Linters + ~user_documentation:[%blob "../../documentation/issues/COMPONENT_FACTORY_FUNCTION.md"] let component_file_cyclomatic_complexity = @@ -334,14 +348,20 @@ let component_file_line_count = let component_initializer_with_side_effects = register_from_string ~id:"COMPONENT_INITIALIZER_WITH_SIDE_EFFECTS" Advice Linters + ~user_documentation: + [%blob "../../documentation/issues/COMPONENT_INITIALIZER_WITH_SIDE_EFFECTS.md"] let component_with_multiple_factory_methods = register_from_string ~id:"COMPONENT_WITH_MULTIPLE_FACTORY_METHODS" Advice Linters + ~user_documentation: + [%blob "../../documentation/issues/COMPONENT_WITH_MULTIPLE_FACTORY_METHODS.md"] let component_with_unconventional_superclass = register_from_string ~id:"COMPONENT_WITH_UNCONVENTIONAL_SUPERCLASS" Advice Linters + ~user_documentation: + [%blob "../../documentation/issues/COMPONENT_WITH_UNCONVENTIONAL_SUPERCLASS.md"] let condition_always_false = @@ -360,6 +380,11 @@ let create_intent_from_uri = register_from_string ~id:"CREATE_INTENT_FROM_URI" E let cross_site_scripting = register_from_string ~id:"CROSS_SITE_SCRIPTING" Error Quandary +let _cxx_reference_captured_in_objc_block = + register_from_string ~id:"CXX_REFERENCE_CAPTURED_IN_OBJC_BLOCK" Warning Linters + ~user_documentation:[%blob "../../documentation/issues/CXX_REFERENCE_CAPTURED_IN_OBJC_BLOCK.md"] + + let dangling_pointer_dereference = register_from_string ~enabled:false ~id:"DANGLING_POINTER_DEREFERENCE" Error Biabduction @@ -369,9 +394,15 @@ let dangling_pointer_dereference_maybe = Warning Biabduction -let dead_store = register_from_string ~id:"DEAD_STORE" Error Liveness +let dead_store = + register_from_string ~id:"DEAD_STORE" Error Liveness + ~user_documentation:[%blob "../../documentation/issues/DEAD_STORE.md"] + + +let deadlock = + register_from_string ~id:"DEADLOCK" Error Starvation + ~user_documentation:[%blob "../../documentation/issues/DEADLOCK.md"] -let deadlock = register_from_string ~id:"DEADLOCK" Error Starvation let deallocate_stack_variable = register_from_string ~id:"DEALLOCATE_STACK_VARIABLE" Error Biabduction @@ -381,11 +412,25 @@ let deallocate_static_memory = register_from_string ~id:"DEALLOCATE_STATIC_MEMOR let deallocation_mismatch = register_from_string ~id:"DEALLOCATION_MISMATCH" Error Biabduction +let _direct_atomic_property_access = + register_from_string ~id:"DIRECT_ATOMIC_PROPERTY_ACCESS" Warning Linters + ~user_documentation:[%blob "../../documentation/issues/DIRECT_ATOMIC_PROPERTY_ACCESS.md"] + + +let _discouraged_weak_property_custom_setter = + register_from_string ~id:"DISCOURAGED_WEAK_PROPERTY_CUSTOM_SETTER" Warning Linters + ~user_documentation: + [%blob "../../documentation/issues/DISCOURAGED_WEAK_PROPERTY_CUSTOM_SETTER.md"] + + let divide_by_zero = register_from_string ~enabled:false ~id:"DIVIDE_BY_ZERO" Error Biabduction let do_not_report = register_from_string ~id:"DO_NOT_REPORT" Error Quandary -let empty_vector_access = register_from_string ~id:"EMPTY_VECTOR_ACCESS" Error Biabduction +let empty_vector_access = + register_from_string ~id:"EMPTY_VECTOR_ACCESS" Error Biabduction + ~user_documentation:[%blob "../../documentation/issues/EMPTY_VECTOR_ACCESS.md"] + (* Condition redundant is a very non-precise issue. Depending on the origin of what is compared with null, this can have a lot of reasons to be actually nullable. @@ -395,6 +440,7 @@ let empty_vector_access = register_from_string ~id:"EMPTY_VECTOR_ACCESS" Error B let eradicate_condition_redundant = register_from_string ~id:"ERADICATE_CONDITION_REDUNDANT" ~hum:"Condition Redundant" Advice Eradicate + ~user_documentation:[%blob "../../documentation/issues/ERADICATE_CONDITION_REDUNDANT.md"] (* TODO(T54070503) remove condition redundant nonnull *) @@ -406,11 +452,13 @@ let _ = let eradicate_field_not_initialized = register_from_string ~id:"ERADICATE_FIELD_NOT_INITIALIZED" ~hum:"Field Not Initialized" Warning Eradicate + ~user_documentation:[%blob "../../documentation/issues/ERADICATE_FIELD_NOT_INITIALIZED.md"] let eradicate_field_not_nullable = register_from_string ~id:"ERADICATE_FIELD_NOT_NULLABLE" ~hum:"Field Not Nullable" Warning Eradicate + ~user_documentation:[%blob "../../documentation/issues/ERADICATE_FIELD_NOT_NULLABLE.md"] (* Very non-precise issue. Should be actually turned off unless for experimental purposes. *) @@ -422,11 +470,15 @@ let eradicate_field_over_annotated = let eradicate_inconsistent_subclass_parameter_annotation = register_from_string ~id:"ERADICATE_INCONSISTENT_SUBCLASS_PARAMETER_ANNOTATION" ~hum:"Inconsistent Subclass Parameter Annotation" Warning Eradicate + ~user_documentation: + [%blob "../../documentation/issues/ERADICATE_INCONSISTENT_SUBCLASS_PARAMETER_ANNOTATION.md"] let eradicate_inconsistent_subclass_return_annotation = register_from_string ~id:"ERADICATE_INCONSISTENT_SUBCLASS_RETURN_ANNOTATION" ~hum:"Inconsistent Subclass Return Annotation" Warning Eradicate + ~user_documentation: + [%blob "../../documentation/issues/ERADICATE_INCONSISTENT_SUBCLASS_RETURN_ANNOTATION.md"] let eradicate_redundant_nested_class_annotation = @@ -447,17 +499,20 @@ let eradicate_nullable_dereference = let eradicate_parameter_not_nullable = register_from_string ~id:"ERADICATE_PARAMETER_NOT_NULLABLE" ~hum:"Parameter Not Nullable" Warning Eradicate + ~user_documentation:[%blob "../../documentation/issues/ERADICATE_PARAMETER_NOT_NULLABLE.md"] let eradicate_return_not_nullable = register_from_string ~id:"ERADICATE_RETURN_NOT_NULLABLE" ~hum:"Return Not Nullable" Warning Eradicate + ~user_documentation:[%blob "../../documentation/issues/ERADICATE_RETURN_NOT_NULLABLE.md"] (* Very non-precise issue. Should be actually turned off unless for experimental purposes. *) let eradicate_return_over_annotated = register_from_string ~id:"ERADICATE_RETURN_OVER_ANNOTATED" ~hum:"Return Over Annotated" Advice Eradicate + ~user_documentation:[%blob "../../documentation/issues/ERADICATE_RETURN_OVER_ANNOTATED.md"] let eradicate_unchecked_usage_in_nullsafe = @@ -504,12 +559,18 @@ let exposed_insecure_intent_handling = let failure_exe = register_from_string ~visibility:Silent ~id:"Failure_exe" Info Biabduction -let field_not_null_checked = register_from_string ~id:"IVAR_NOT_NULL_CHECKED" Warning Biabduction +let field_not_null_checked = + register_from_string ~id:"IVAR_NOT_NULL_CHECKED" Warning Biabduction + ~user_documentation:[%blob "../../documentation/issues/IVAR_NOT_NULL_CHECKED.md"] + (* from AL default linters *) let _global_variable_initialized_with_function_or_method_call = register_from_string ~enabled:false ~id:"GLOBAL_VARIABLE_INITIALIZED_WITH_FUNCTION_OR_METHOD_CALL" Warning Linters + ~user_documentation: + [%blob + "../../documentation/issues/GLOBAL_VARIABLE_INITIALIZED_WITH_FUNCTION_OR_METHOD_CALL.md"] let guardedby_violation_racerd = @@ -568,7 +629,10 @@ let integer_overflow_u5 = register_from_string ~enabled:false ~id:"INTEGER_OVERFLOW_U5" Error BufferOverrunChecker -let interface_not_thread_safe = register_from_string Warning ~id:"INTERFACE_NOT_THREAD_SAFE" RacerD +let interface_not_thread_safe = + register_from_string Warning ~id:"INTERFACE_NOT_THREAD_SAFE" RacerD + ~user_documentation:[%blob "../../documentation/issues/INTERFACE_NOT_THREAD_SAFE.md"] + let internal_error = register_from_string ~visibility:Developer ~id:"Internal_error" Error Biabduction @@ -595,6 +659,7 @@ let leak_unknown_origin = let lock_consistency_violation = register_from_string Warning ~id:"LOCK_CONSISTENCY_VIOLATION" RacerD + ~user_documentation:[%blob "../../documentation/issues/LOCK_CONSISTENCY_VIOLATION.md"] let lockless_violation = register_from_string ~id:"LOCKLESS_VIOLATION" Error Starvation @@ -605,7 +670,10 @@ let expensive_loop_invariant_call = register_from_string ~id:"EXPENSIVE_LOOP_INVARIANT_CALL" Error LoopHoisting -let memory_leak = register_from_string ~id:"MEMORY_LEAK" Error Biabduction +let memory_leak = + register_from_string ~id:"MEMORY_LEAK" Error Biabduction + ~user_documentation:[%blob "../../documentation/issues/MEMORY_LEAK.md"] + let missing_fld = register_from_string ~visibility:Developer ~id:"Missing_fld" ~hum:"Missing Field" Error @@ -618,14 +686,18 @@ let missing_required_prop = let mixed_self_weakself = register_from_string ~id:"MIXED_SELF_WEAKSELF" ~hum:"Mixed Self WeakSelf" Error SelfInBlock + ~user_documentation:[%blob "../../documentation/issues/MIXED_SELF_WEAKSELF.md"] let multiple_weakself = register_from_string ~id:"MULTIPLE_WEAKSELF" ~hum:"Multiple WeakSelf Use" Error SelfInBlock + ~user_documentation:[%blob "../../documentation/issues/MULTIPLE_WEAKSELF.md"] let mutable_local_variable_in_component_file = register_from_string ~id:"MUTABLE_LOCAL_VARIABLE_IN_COMPONENT_FILE" Advice Linters + ~user_documentation: + [%blob "../../documentation/issues/MUTABLE_LOCAL_VARIABLE_IN_COMPONENT_FILE.md"] let null_dereference = @@ -641,10 +713,16 @@ let nullptr_dereference = register_from_string ~enabled:false ~id:"NULLPTR_DEREF let parameter_not_null_checked = register_from_string ~id:"PARAMETER_NOT_NULL_CHECKED" Warning Biabduction + ~user_documentation:[%blob "../../documentation/issues/PARAMETER_NOT_NULL_CHECKED.md"] let pointer_size_mismatch = register_from_string ~id:"POINTER_SIZE_MISMATCH" Error Biabduction +let _pointer_to_const_objc_class = + register_from_string ~id:"POINTER_TO_CONST_OBJC_CLASS" Warning Linters + ~user_documentation:[%blob "../../documentation/issues/POINTER_TO_CONST_OBJC_CLASS.md"] + + let precondition_not_found = register_from_string ~visibility:Developer ~id:"PRECONDITION_NOT_FOUND" Error Biabduction @@ -655,6 +733,7 @@ let precondition_not_met = let premature_nil_termination = register_from_string ~id:"PREMATURE_NIL_TERMINATION_ARGUMENT" Warning Biabduction + ~user_documentation:[%blob "../../documentation/issues/PREMATURE_NIL_TERMINATION_ARGUMENT.md"] let pulse_memory_leak = register_from_string ~enabled:false ~id:"PULSE_MEMORY_LEAK" Error Pulse @@ -665,9 +744,21 @@ let quandary_taint_error = register_from_string ~hum:"Taint Error" ~id:"QUANDARY_TAINT_ERROR" Error Quandary -let resource_leak = register_from_string ~id:"RESOURCE_LEAK" Error Biabduction +let _registered_observer_being_deallocated = + register_from_string ~id:"REGISTERED_OBSERVER_BEING_DEALLOCATED" Warning Linters + ~user_documentation: + [%blob "../../documentation/issues/REGISTERED_OBSERVER_BEING_DEALLOCATED.md"] + + +let resource_leak = + register_from_string ~id:"RESOURCE_LEAK" Error Biabduction + ~user_documentation:[%blob "../../documentation/issues/RESOURCE_LEAK.md"] + + +let retain_cycle = + register_from_string ~enabled:true ~id:"RETAIN_CYCLE" Error Biabduction + ~user_documentation:[%blob "../../documentation/issues/RETAIN_CYCLE.md"] -let retain_cycle = register_from_string ~enabled:true ~id:"RETAIN_CYCLE" Error Biabduction let skip_function = register_from_string ~visibility:Developer ~enabled:false ~id:"SKIP_FUNCTION" Info Biabduction @@ -689,7 +780,10 @@ let stack_variable_address_escape = register_from_string ~id:"STACK_VARIABLE_ADDRESS_ESCAPE" Error Pulse -let starvation = register_from_string ~id:"STARVATION" ~hum:"UI Thread Starvation" Error Starvation +let starvation = + register_from_string ~id:"STARVATION" ~hum:"UI Thread Starvation" Error Starvation + ~user_documentation:[%blob "../../documentation/issues/STARVATION.md"] + let static_initialization_order_fiasco = register_from_string ~id:"STATIC_INITIALIZATION_ORDER_FIASCO" Error SIOF @@ -698,10 +792,17 @@ let static_initialization_order_fiasco = let strict_mode_violation = register_from_string ~id:"STRICT_MODE_VIOLATION" ~hum:"Strict Mode Violation" Error Starvation + ~user_documentation:[%blob "../../documentation/issues/STRICT_MODE_VIOLATION.md"] + + +let _strong_delegate_warning = + register_from_string ~id:"STRONG_DELEGATE_WARNING" Warning Linters + ~user_documentation:[%blob "../../documentation/issues/STRONG_DELEGATE_WARNING.md"] let strong_self_not_checked = register_from_string ~id:"STRONG_SELF_NOT_CHECKED" ~hum:"StrongSelf Not Checked" Error SelfInBlock + ~user_documentation:[%blob "../../documentation/issues/STRONG_SELF_NOT_CHECKED.md"] let symexec_memory_error = @@ -709,7 +810,10 @@ let symexec_memory_error = ~hum:"Symbolic Execution Memory Error" Error Biabduction -let thread_safety_violation = register_from_string Warning ~id:"THREAD_SAFETY_VIOLATION" RacerD +let thread_safety_violation = + register_from_string Warning ~id:"THREAD_SAFETY_VIOLATION" RacerD + ~user_documentation:[%blob "../../documentation/issues/THREAD_SAFETY_VIOLATION.md"] + let complexity_increase ~kind ~is_on_ui_thread = register_from_cost_string ~kind ~is_on_ui_thread "%s_COMPLEXITY_INCREASE" @@ -722,6 +826,11 @@ let unary_minus_applied_to_unsigned_expression = Biabduction +let _unavailable_api_in_supported_ios_sdk = + register_from_string ~id:"UNAVAILABLE_API_IN_SUPPORTED_IOS_SDK" Warning Linters + ~user_documentation:[%blob "../../documentation/issues/UNAVAILABLE_API_IN_SUPPORTED_IOS_SDK.md"] + + let uninitialized_value = register_from_string ~id:"UNINITIALIZED_VALUE" Error Uninit let unreachable_code_after = register_from_string ~id:"UNREACHABLE_CODE" Error BufferOverrunChecker @@ -768,6 +877,7 @@ let vector_invalidation = register_from_string ~id:"VECTOR_INVALIDATION" Error P let weak_self_in_noescape_block = register_from_string ~id:"WEAK_SELF_IN_NO_ESCAPE_BLOCK" Error SelfInBlock + ~user_documentation:[%blob "../../documentation/issues/WEAK_SELF_IN_NO_ESCAPE_BLOCK.md"] let wrong_argument_number = diff --git a/website/docs/03-eradicate-warnings.md b/website/docs/03-eradicate-warnings.md index b54fca270..00a6f49f9 100644 --- a/website/docs/03-eradicate-warnings.md +++ b/website/docs/03-eradicate-warnings.md @@ -211,7 +211,7 @@ class B extends A { A consistent use of @Nullable on the return type across subtyping should prevent runtime issue like in: -````java +```java class Main { int foo(A a) { @@ -269,4 +269,3 @@ public class Main { } } ``` -```` diff --git a/website/versioned_docs/version-0.17.0/03-eradicate-warnings.md b/website/versioned_docs/version-0.17.0/03-eradicate-warnings.md index b54fca270..5f9b877f9 100644 --- a/website/versioned_docs/version-0.17.0/03-eradicate-warnings.md +++ b/website/versioned_docs/version-0.17.0/03-eradicate-warnings.md @@ -211,7 +211,7 @@ class B extends A { A consistent use of @Nullable on the return type across subtyping should prevent runtime issue like in: -````java +```java class Main { int foo(A a) { @@ -269,4 +269,4 @@ public class Main { } } ``` -```` +