-
Notifications
You must be signed in to change notification settings - Fork 163
ECJ Annotation‐based Null Analysis
How is annotation-based null analysis implemented in ECJ?
When using declaration annotations (i.e., not for target TYPE_USE), the semantics of null annotations will be encoded in bindings of the affected element using bitset tagBits. This happens for:
- locals
- fields
- methods, meaning: their return type
A method, however, has no binding for the parameters it declares. MethodBinding.parameters only holds the parameter-types.
For that reason, such information is encoded in MethodBinding.parameterFlowBits.
During resolution we compute the specified or derived nullness into each ReferenceBinding, which applies cloning to distinguish type bindings that differ only in annotations.
Different kinds of AST nodes are handled by different utility methods:
NullAnnotationMatching.checkAssignment() handles various forms of assignment contexts:
- Assignment
- LocalDeclaration
- FieldDeclaration
- ForeachStatement
- The resources of a TryStatement are implicitly handled as those are encoded as LocalDeclaration
Statement.analyseArguments() handles this invocation contexts:
- MessageSend
- AllocationExpression
- QualifiedAllocationExpression
- ExplicitContructorCall
Statement.checkAgainstNullTypeAnnotation() is used for partially unclear purpose in:
- ArrayReference -- ??
- ArrayInitializer -- actually a kind of assignment context
- ReturnStatement -- actually a kind of assignment context
Each of the above methods is able to drill into complex expressions, to analyse all result expressions in:
- ConditionalExpression
- SwitchExpression
These expressions can also be freely nested. They transparently propagate the enclosing expression context.
Expression.checkNPE() is used to check nullness of receivers or similar expressions of:
- MessageSend
- FieldReference
- ArrayReference
- ReferenceExpression
- ForeachStatement -- for its collection, as the receiver of the
iterator()call - SwitchStatement -- if null-hostile for its switch expression (subject to conversions like unboxing)
- SynchronizedStatement -- for its expression which is required to be nonnull by the vm
- ThrowStatement -- cannot throw null
- CastExpression -- really?
- OperatorExpressions: UnaryExpression, BinaryExpression, CombinedBinaryExpression -- why?
- CompoundAssignment -- for its lhs, why?
What about QualifiedAllocationExpression?
Expression.checkNPEbyUnboxing() checks expression for unboxing of a value that is not null-safe:
- Invocation arguments of
- MessageSend
- ExplicitConstructorCall
- AllocationExpression
- QualifiedAllocationExpression
- Dimensions / index of
- ArrayAllocationExpression
- ArrayReference
- Single expresion of
- CastExpression
- Assignment
- CompoundAssignment
- AssertStatement
- ReturnStatement
- YieldStatement
- Operands of
- ConditionalExpression
- OR_OR_Expression
- AND_AND_Expression
- check if EQUAL_EQUAL_Expression is safe, does it ever unbox?
- Condition of
- DoStatement
- ForStatement
- IfStatement
- WhileStatement
- Initialization of
- LocalDeclaration
- FieldDeclaration
T.B.C.