@Override public ProgramState checkPostStatement(CheckerContext context, Tree syntaxNode) { if (syntaxNode.is(Tree.Kind.SWITCH_STATEMENT) && context.getConstraintManager().isNull(context.getState(), context.getState().peekValue())) { context.reportIssue(syntaxNode, this, "NullPointerException might be thrown as '" + SyntaxTreeNameFinder.getName(syntaxNode) + "' is nullable here"); context.createSink(); return context.getState(); } List<ProgramState> programStates = setNullConstraint(context, syntaxNode); for (ProgramState programState : programStates) { context.addTransition(programState); } return context.getState(); }
private ProgramState handleMinMaxInvocation(CheckerContext context, MethodInvocationTree syntaxNode) { if (!MIN_MAX_MATCHER.matches(syntaxNode)) { return context.getState(); } ProgramState programState = context.getState(); ProgramState psBeforeInvocation = context.getNode().programState; List<SymbolicValue> args = psBeforeInvocation.peekValues(2); List<ConstraintsByDomain> constraintsByArgs = args.stream().map(programState::getConstraints).collect(Collectors.toList()); checkRangeInconsistencies(context, syntaxNode, constraintsByArgs); return context.getState(); }
PreStatementVisitor(CheckerContext context) { super(context.getState()); this.context = context; this.constraintManager = context.getConstraintManager(); }
@Override public ProgramState checkPostStatement(CheckerContext context, Tree syntaxNode) { if (syntaxNode.is(Tree.Kind.SWITCH_STATEMENT, Tree.Kind.THROW_STATEMENT) && context.getConstraintManager().isNull(context.getState(), context.getState().peekValue())) { NullDereferenceIssue issue = new NullDereferenceIssue(context.getNode(), context.getState().peekValue(), syntaxNode); detectedIssues.peek().add(issue); context.createSink(); return context.getState(); } List<ProgramState> programStates = setNullConstraint(context, syntaxNode); for (ProgramState programState : programStates) { context.addTransition(programState); } return context.getState(); }
@Override public void checkEndOfExecutionPath(CheckerContext context, ConstraintManager constraintManager) { final List<ObjectConstraint> constraints = context.getState().getFieldConstraints(Status.LOCKED); for (ObjectConstraint constraint : constraints) { Tree syntaxNode = constraint.syntaxNode(); context.reportIssue(syntaxNode, this, "Unlock \"" + syntaxNode.toString() + "\" along all executions paths of this method."); } } }
@Override public void checkEndOfExecution(CheckerContext context) { for (Tree condition : Sets.difference(evaluatedToFalse, evaluatedToTrue)) { context.reportIssue(condition, this, "Change this condition so that it does not always evaluate to \"false\""); } for (Tree condition : Sets.difference(evaluatedToTrue, evaluatedToFalse)) { context.reportIssue(condition, this, "Change this condition so that it does not always evaluate to \"true\""); } }
private static boolean isUndefinedOrNull(CheckerContext context, Symbol symbol) { ProgramState programState = context.getState(); SymbolicValue value = programState.getValue(symbol); return value == null; }
private ProgramState handleMethodInvocation(CheckerContext context, MethodInvocationTree mit) { ProgramState programState = context.getState(); programState = removeConstraintOnArgs(programState, mit.arguments().size()); SymbolicValue invocationTarget = invocationTarget(programState, mit); if ((isIntermediateOperation(mit) || isTerminalOperation(mit)) && isPipelineConsumed(programState, invocationTarget)) { reportIssue(mit, "Refactor this code so that this consumed stream pipeline is not reused.", flow(invocationTarget, context.getNode())); return null; } if (isIntermediateOperation(mit)) { // intermediate operations return same stream pipeline, so we reuse SV context.getConstraintManager().setValueFactory(() -> invocationTarget); return Iterables.getOnlyElement(invocationTarget.setConstraint(programState, StreamPipelineConstraint.NOT_CONSUMED)); } if (isTerminalOperation(mit)) { return Iterables.getOnlyElement(invocationTarget.setConstraint(programState, StreamPipelineConstraint.CONSUMED)); } if (mit.symbol().isUnknown()) { // lambdas used in pipelines are sometimes not resolved properly, this is to shutdown the noise programState = programState.removeConstraintsOnDomain(invocationTarget, StreamPipelineConstraint.class); } return programState; }
private void reportIssue(Tree tree, SymbolicValue denominator, Symbol denominatorSymbol) { ExpressionTree expression = getDenominator(tree); String operation = tree.is(Tree.Kind.REMAINDER, Tree.Kind.REMAINDER_ASSIGNMENT) ? "modulation" : "division"; String expressionName = expression.is(Tree.Kind.IDENTIFIER) ? ("\"" + ((IdentifierTree) expression).name() + "\"") : "this expression"; List<Class<? extends Constraint>> domains = Collections.singletonList(ZeroConstraint.class); Set<Flow> flows = FlowComputation.flow(context.getNode(), denominator, domains, denominatorSymbol).stream() .filter(f -> !f.isEmpty()) .map(f -> Flow.builder() .add(new JavaFileScannerContext.Location("Division by zero.", tree)) .addAll(f) .build()) .collect(Collectors.toSet()); context.reportIssue(expression, DivisionByZeroCheck.this, "Make sure " + expressionName + " can't be zero before doing this " + operation + ".", flows); }
private ProgramState checkConstraint(ProgramState programState, CheckerContext context, Tree syntaxNode, SymbolicValue currentVal) { ObjectConstraint constraint = programState.getConstraint(currentVal, ObjectConstraint.class); if (constraint != null && constraint.isNull()) { NullDereferenceIssue issue = new NullDereferenceIssue(context.getNode(), currentVal, syntaxNode); detectedIssues.peek().add(issue); // we reported the issue and stopped the exploration, but we still need to create a yield for x-procedural calls context.addExceptionalYield(currentVal, programState, JAVA_LANG_NPE, this); return null; } constraint = programState.getConstraint(currentVal, ObjectConstraint.class); if (constraint == null) { // a NPE will be triggered if the current value would have been null context.addExceptionalYield(currentVal, programState.addConstraint(currentVal, ObjectConstraint.NULL), JAVA_LANG_NPE, this); // We dereferenced the target value for the member select, so we can assume it is not null when not already known return programState.addConstraint(currentVal, ObjectConstraint.NOT_NULL); } return programState; }
@Override public void checkEndOfExecution(CheckerContext context) { context.alwaysTrueOrFalseExpressions().alwaysTrue().forEach(tree -> { Tree statementParent = firstStatementParent(tree); if (statementParent != null && statementParent.is(Tree.Kind.WHILE_STATEMENT)) { checkLoopWithAlwaysTrueCondition(context, statementParent); } }); contexts.pop(); }
@Override public void checkEndOfExecutionPath(CheckerContext context, ConstraintManager constraintManager) { EXCEPTIONAL_YIELD_CHECKER.reportOnExceptionalYield(context.getNode(), this); } }
private void handleDivide(Tree tree, SymbolicValue leftOp, SymbolicValue rightOp, Symbol rightOpSymbol) { if (isZero(rightOp)) { context.addExceptionalYield(rightOp, programState, "java.lang.ArithmeticException", DivisionByZeroCheck.this); reportIssue(tree, rightOp, rightOpSymbol); // interrupt exploration programState = null; } else if (isZero(leftOp)) { reuseSymbolicValue(leftOp); } else if (isNonZero(leftOp) && isNonZero(rightOp)) { // result of 'integer' can be zero or non-zero, depending of operands (for instance: '1 / 2 == 0') deferConstraint(null); } else if (hasNoConstraint(rightOp)) { ProgramState exceptionalState = programState .addConstraint(rightOp, ZeroConstraint.ZERO) // FIXME SONARJAVA-2125 - we should not have to add the NOT_NULL constraint for primitive types .addConstraint(rightOp, ObjectConstraint.NOT_NULL); context.addExceptionalYield(rightOp, exceptionalState, "java.lang.ArithmeticException", DivisionByZeroCheck.this); programState = programState.addConstraintTransitively(rightOp, ZeroConstraint.NON_ZERO); } }
@Override public ProgramState checkPostStatement(CheckerContext context, Tree syntaxNode) { if (syntaxNode.is(Tree.Kind.SWITCH_STATEMENT, Tree.Kind.THROW_STATEMENT) && context.getConstraintManager().isNull(context.getState(), context.getState().peekValue())) { NullDereferenceIssue issue = new NullDereferenceIssue(context.getNode(), context.getState().peekValue(), syntaxNode); detectedIssues.peek().add(issue); context.createSink(); return context.getState(); } List<ProgramState> programStates = setNullConstraint(context, syntaxNode); for (ProgramState programState : programStates) { context.addTransition(programState); } return context.getState(); }
private PreStatementVisitor(SECheck check, CheckerContext context) { super(context.getState()); this.context = context; this.constraintManager = context.getConstraintManager(); this.check = check; }
private ProgramState checkMemberSelect(CheckerContext context, MemberSelectExpressionTree syntaxNode, SymbolicValue currentVal) { final ProgramState programState = context.getState(); if ("class".equals(syntaxNode.identifier().name())) { // expression ClassName.class won't raise NPE. return programState; } Constraint constraint = programState.getConstraint(currentVal); if (constraint != null && constraint.isNull()) { List<JavaFileScannerContext.Location> secondary = new ArrayList<>(); if(((ObjectConstraint) constraint).syntaxNode() != null ) { secondary.add(new JavaFileScannerContext.Location("", ((ObjectConstraint) constraint).syntaxNode())); } context.reportIssue(syntaxNode, this, "NullPointerException might be thrown as '" + SyntaxTreeNameFinder.getName(syntaxNode) + "' is nullable here", secondary); return null; } SymbolicValue targetValue = programState.peekValue(); constraint = programState.getConstraint(targetValue); if (constraint == null) { // We dereferenced the target value for the member select, so we can assume it is not null when not already known return programState.addConstraint(targetValue, ObjectConstraint.NOT_NULL); } return programState; }
protected void reportIssue(Tree tree, String message, Object... parameters) { context.reportIssue(tree, NonNullSetToNullCheck.this, MessageFormat.format(message, parameters)); } }
@Override public ProgramState checkPreStatement(CheckerContext context, Tree syntaxNode) { if (syntaxNode.is(Tree.Kind.METHOD_INVOCATION)) { methodInvocations.peek().add((MethodInvocationTree) syntaxNode); } // No operation on state, just monitoring return context.getState(); }
private ProgramState handleMethodInvocation(CheckerContext context, MethodInvocationTree mit) { ProgramState programState = context.getState(); programState = removeConstraintOnArgs(programState, mit.arguments().size()); SymbolicValue invocationTarget = invocationTarget(programState, mit); if ((isIntermediateOperation(mit) || isTerminalOperation(mit)) && isPipelineConsumed(programState, invocationTarget)) { reportIssue(mit, "Refactor this code so that this consumed stream pipeline is not reused.", flow(invocationTarget, context.getNode())); return null; } if (isIntermediateOperation(mit)) { // intermediate operations return same stream pipeline, so we reuse SV context.getConstraintManager().setValueFactory(() -> invocationTarget); return Iterables.getOnlyElement(invocationTarget.setConstraint(programState, StreamPipelineConstraint.NOT_CONSUMED)); } if (isTerminalOperation(mit)) { return Iterables.getOnlyElement(invocationTarget.setConstraint(programState, StreamPipelineConstraint.CONSUMED)); } if (mit.symbol().isUnknown()) { // lambdas used in pipelines are sometimes not resolved properly, this is to shutdown the noise programState = programState.removeConstraintsOnDomain(invocationTarget, StreamPipelineConstraint.class); } return programState; }
private void reportIssue(Tree tree, SymbolicValue denominator, Symbol denominatorSymbol) { ExpressionTree expression = getDenominator(tree); String operation = tree.is(Tree.Kind.REMAINDER, Tree.Kind.REMAINDER_ASSIGNMENT) ? "modulation" : "division"; String expressionName = expression.is(Tree.Kind.IDENTIFIER) ? ("\"" + ((IdentifierTree) expression).name() + "\"") : "this expression"; List<Class<? extends Constraint>> domains = Collections.singletonList(ZeroConstraint.class); Set<Flow> flows = FlowComputation.flow(context.getNode(), denominator, domains, denominatorSymbol).stream() .filter(f -> !f.isEmpty()) .map(f -> Flow.builder() .add(new JavaFileScannerContext.Location("Division by zero.", tree)) .addAll(f) .build()) .collect(Collectors.toSet()); context.reportIssue(expression, DivisionByZeroCheck.this, "Make sure " + expressionName + " can't be zero before doing this " + operation + ".", flows); }