From 600e0c7f08bf0dece6c9dc8f1c8de680c96c62a4 Mon Sep 17 00:00:00 2001 From: Johannes Mey <johannes.mey@tu-dresden.de> Date: Tue, 17 Dec 2019 15:47:05 +0100 Subject: [PATCH] temporarily disable the nullable derereference analysis --- .../jastadd/NullableDereferenceAnalysis.jrag | 1008 ++++++++--------- reusablecfg/src/main/jastadd/PrintCfg.jrag | 2 +- .../src/main/jastadd/PrintCfgTest.jrag | 2 +- .../reusablecfg/NullableDereferenceTest.java | 216 ++-- 4 files changed, 616 insertions(+), 612 deletions(-) diff --git a/reusablecfg/src/main/jastadd/NullableDereferenceAnalysis.jrag b/reusablecfg/src/main/jastadd/NullableDereferenceAnalysis.jrag index db9afd1..6f0d36b 100644 --- a/reusablecfg/src/main/jastadd/NullableDereferenceAnalysis.jrag +++ b/reusablecfg/src/main/jastadd/NullableDereferenceAnalysis.jrag @@ -1,504 +1,504 @@ -/** - * Copyright 2015 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * Adds an analysis that checks for dereferences of a parameter declared nullable. - * - * <p>When a method or constructor parameter is annotated with javax.annotation.Nullable, - * we check that all dereferences of that parameter are guarded by a null-check. The - * analysis is control-flow sensitive in that it will recognize if all control flow paths - * to the dereference are effectively guarded by a null check, for example: {@code - * if (p == null) return; - * p.x(); // Guarded by null check above. - * } - * - * <p>The analysis is not intraprocedural, so in order to avoid false positives - * where a method call guards against nullness the analyzer assumes that - * calling a method with an argument x results in an exception if x is null and - * thus works like an effective null guard for x. - * - * <p>To find potential null dereferences on nullable parameters, the analysis does a forward CFG - * traversal from the entry-point of the method. Note that this is only done whenever a - * {@code @Nullable} parameter has been encountered in the method. The traversal explores all paths - * from the method entry until it finds null guard statements such as {@code if (p != null)}. The - * branches that are protected from nullness get pruned in the search and the search continues on - * other branches. The search is performed by a {@code NullDereferenceLocator}, which implements the - * {@code CfgSearch} interface. This visitor is invoked for each node in the CFG search, and it - * decides if the search will continue from that node, or if the current edge should be skipped - * (i.e., pruned). - * - * <p>In order to find the position in the CFG where a potential null dereference occurs, CFG marker - * nodes are inserted. This is done by adding a synthesized non-terminal attribute (NTA) for - * a CfgMarker on Dot (which represents Java dot expressions). This marker node appears - * in the CFG as "nullable access" because it is inserted whenever a nullable variable is - * dereferenced. - * - * <p>Dataflow analysis is not used, so in order to analyze a parameter it is required to be - * effectively final, i.e. it is not assigned anywhere in side the body of the method/constructor. - */ -aspect NullableDereferenceAnalysis { - - // Give ParameterDeclaration access to the inherited compilationUnit attribute. - inh CompilationUnit ParameterDeclaration.compilationUnit(); - - ParameterDeclaration contributes nullableDereferenceFinding() - when nullableDereferenceFinding() != null - to CompilationUnit.findings() - for compilationUnit(); - - /** - * Generate a NullableDereference finding for this dot expression, - * if no finding should be reported this attribute returns {@code null}. - */ - syn lazy ExtendJFinding ParameterDeclaration.nullableDereferenceFinding() { - if (!getModifiers().hasNullableAnnotation()) { - return null; - } - if (!isFinal() && !isEffectivelyFinal()) { - // Do not analyze non-effectively final parameters. - return null; - } - Expr location = findNullableDereference(this); - if (location == null) { - return null; - } - ExtendJFinding finding = location.finding("NullableDereference", String.format( - "Dereferencing %s, which was declared @Nullable.", name())); - if (compilationUnit().fromSource()) { - ASTNode modifierLocation = nullableModifierLocation(); - int line = getLine(modifierLocation.getStart()); - int startCol = getColumn(modifierLocation.getStart()); - int endCol = getColumn(modifierLocation.getEnd()); - if (startCol < endCol && line == getLine(modifierLocation.getEnd())) { - try { - InputStream data = compilationUnit().getClassSource().openInputStream(); - java.util.Scanner scanner = new java.util.Scanner(data); - for (int i = 1; i < line && scanner.hasNextLine(); ++i) { - scanner.nextLine(); - } - if (scanner.hasNextLine()) { - String text = scanner.nextLine(); - finding.addFix("Remove the @Nullable annotation.", - line, line, - text.substring(0,startCol-1) + text.substring(endCol+1) + "\n"); - } - } catch (IOException e) { - // Failed to unparse the current line. - // This is not a serious problem; we just don't give a fix suggestion. - } - } - } - return finding; - } - - // Exclude variable arity parameters from Nullable dereference analysis. - // When a variable arity parameter is annotated @Nullable, that will most likely be intended as a - // @Nullable annotation for the individual parameters, not the containing argument array. - eq VariableArityParameterDeclaration.nullableDereferenceFinding() = null; - - /** - * Find the location node for the javax.annotation.Nullable annotation in the modifier list. - * Returns {@code null} if the location of the modifier was not found. - */ - syn ASTNode ParameterDeclaration.nullableModifierLocation() = - getModifiers().nullableModifierLocation(); - - syn ASTNode Modifiers.nullableModifierLocation() { - for (Modifier modifier : getModifierList()) { - if (modifier.isAnnotation("javax.annotation", "Nullable")) { - return modifier.locationNode(); - } - } - return null; - } - - /** - * Find a location, not necessarily the first location, in the host method/constructor where the - * parameter is accessed without a null guard. - */ - inh Expr ParameterDeclaration.findNullableDereference(Variable var); - - eq Program.getChild().findNullableDereference(Variable var) = null; - eq BodyDecl.getChild().findNullableDereference(Variable var) = null; - - eq MethodDecl.getParameter().findNullableDereference(Variable var) { - if (!hasBlock()) { - return null; - } - CfgNode cfgNode = entry().bfs(new NullDereferenceLocator(var)); - return cfgNode == null ? null : cfgNode.receiverExpr(); - } - - eq ConstructorDecl.getParameter().findNullableDereference(Variable var) { - CfgNode cfgNode = entry().bfs(new NullDereferenceLocator(var)); - return cfgNode == null ? null : cfgNode.receiverExpr(); - } - - /** - * A CFG visitor that searches in the forward CFG for a nullable dereference. - * - * <p>The search stops at parts of the search tree guarded by a null check - * on the receiver variable. - */ - class NullDereferenceLocator implements CfgVisitor { - private final Variable var; - - public NullDereferenceLocator(Variable var) { - this.var = var; - } - - @Override public SearchAction processEdge(CfgNode pred, CfgNode succ) { - if (pred.isNullGuard(var, succ)) { - return SearchAction.SKIP; - } - Expr receiver = succ.receiverExpr(); - if (receiver != null && receiver.isVariable(var) && !receiver.hasNullGuard(var)) { - return SearchAction.SUCCESSOR_MATCH; - } - return SearchAction.CONTINUE; - } - } - - /** - * Returns the receiver expression if the CFG node is the child of a dereference expression. - * Returns {@code null} otherwise. - */ - inh Expr CfgNode.receiverExpr(); - eq Program.getChild().receiverExpr() = null; - eq BodyDecl.getChild().receiverExpr() = null; - eq BodyDecl.exit().receiverExpr() = null; - eq TryStmt.tryEntryMarker().receiverExpr() = null; - eq BreakStmt.marker().receiverExpr() = null; - eq ContinueStmt.marker().receiverExpr() = null; - eq ReturnStmt.marker().receiverExpr() = null; - eq MethodAccess.exceptionNode().receiverExpr() = null; - eq MethodAccess.call().receiverExpr() = - hasPrevExpr() - ? prevExpr() - : null; - eq ThrowStmt.exceptionNode().receiverExpr() = null; - eq TryStmt.exceptionNode().receiverExpr() = null; - eq ConditionalExpr.branch().receiverExpr() = null; - eq ConditionalExpr.thenEndMarker().receiverExpr() = null; - eq ConditionalExpr.elseEndMarker().receiverExpr() = null; - eq IfStmt.branch().receiverExpr() = null; - eq IfStmt.thenEndMarker().receiverExpr() = null; - eq IfStmt.elseEndMarker().receiverExpr() = null; - eq ForStmt.branch().receiverExpr() = null; - eq EnhancedForStmt.branch().receiverExpr() = null; - eq WhileStmt.branch().receiverExpr() = null; - eq DoStmt.branch().receiverExpr() = null; - eq SwitchStmt.branch().receiverExpr() = null; - eq LambdaBody.exit().receiverExpr() = null; - eq Dot.nullableDereferenceMarker().receiverExpr() = getLeft(); - - /** Marker node used to find location of a nullable dereference in the CFG. */ - syn nta CfgMarker Dot.nullableDereferenceMarker() = new CfgMarker(); - - /** Insert nullable dereference marker in the CFG. */ - refine SimpleCFG - eq Dot.getLeft().follow() = - getRight().isMethodAccess() - ? refined() - : nullableDereferenceMarker(); - - eq Dot.nullableDereferenceMarker().succ() = Collections.singleton(getRight().entry()); - - syn boolean CfgNode.isNullGuard(Variable var, CfgNode succ) = false; - - /** - * We assume that calling a method with the variable var as an argument - * results in an exception thrown by the method call if var is null. This is - * not true for many methods, but it should reduce the false positive rate - * for the NullableDereference analyzer. - */ - eq CfgMethodCall.isNullGuard(Variable var, CfgNode succ) { - if (succ instanceof CfgException) { - return false; - } - MethodAccess access = methodAccess(); - for (Expr arg : access.getArgList()) { - if (arg.isVariable(var)) { - return true; - } - } - return false; - } - - /** Check if this branch has a null-guarding condition. */ - eq CfgBranch.isNullGuard(Variable var, CfgNode succ) = inNullGuard(var, succ); - - inh boolean CfgBranch.inNullGuard(Variable var, CfgNode succ); - - eq IfStmt.branch().inNullGuard(Variable var, CfgNode succ) = - succ == getThen().entry() - ? getCondition().isNonNullWhenTrue(var) - : getCondition().isNonNullWhenFalse(var); - - eq ConditionalExpr.branch().inNullGuard(Variable var, CfgNode succ) = - succ == getTrueExpr().entry() - ? getCondition().isNonNullWhenTrue(var) - : getCondition().isNonNullWhenFalse(var); - - eq ForStmt.branch().inNullGuard(Variable var, CfgNode succ) = - succ == getStmt().entry() - ? getCondition().isNonNullWhenTrue(var) - : getCondition().isNonNullWhenFalse(var); - - eq WhileStmt.branch().inNullGuard(Variable var, CfgNode succ) = - succ == getStmt().entry() - ? getCondition().isNonNullWhenTrue(var) - : getCondition().isNonNullWhenFalse(var); - - eq EnhancedForStmt.branch().inNullGuard(Variable var, CfgNode succ) = false; - eq DoStmt.branch().inNullGuard(Variable var, CfgNode succ) = false; - eq SwitchStmt.branch().inNullGuard(Variable var, CfgNode succ) = false; - - /** Returns {@code true} if this set of modifiers includes {@code javax.annotation.Nullable}. */ - syn boolean Modifiers.hasNullableAnnotation() = hasAnnotation("javax.annotation", "Nullable"); - - /** Return {@code true} if this expression is guarded by a != null check for var. */ - inh boolean Expr.hasNullGuard(Variable var); - eq Program.getChild().hasNullGuard(Variable var) = false; - eq IfStmt.getThen().hasNullGuard(Variable var) = getCondition().isNonNullWhenTrue(var); - eq IfStmt.getElse().hasNullGuard(Variable var) = getCondition().isNonNullWhenFalse(var); - eq WhileStmt.getStmt().hasNullGuard(Variable var) = getCondition().isNonNullWhenTrue(var); - eq ForStmt.getStmt().hasNullGuard(Variable var) = getCondition().isNonNullWhenTrue(var); - eq ConditionalExpr.getTrueExpr().hasNullGuard(Variable var) = - getCondition().isNonNullWhenTrue(var) || hasNullGuard(var); - eq ConditionalExpr.getFalseExpr().hasNullGuard(Variable var) = - getCondition().isNonNullWhenFalse(var) || hasNullGuard(var); - eq AndLogicalExpr.getRightOperand().hasNullGuard(Variable var) = - getLeftOperand().isNonNullWhenTrue(var) || hasNullGuard(var); - eq AndBitwiseExpr.getRightOperand().hasNullGuard(Variable var) = - getLeftOperand().isNonNullWhenTrue(var) || hasNullGuard(var); - eq OrLogicalExpr.getRightOperand().hasNullGuard(Variable var) = - getLeftOperand().isNonNullWhenFalse(var) || hasNullGuard(var); - - /** @return {@code true} if the variable var is null when this expression is true. */ - syn boolean Expr.isNullWhenTrue(Variable var) = false; - - eq NEExpr.isNullWhenTrue(Variable var) = - getLeftOperand().isTrue() && getRightOperand().isNullWhenFalse(var) - || getRightOperand().isTrue() && getLeftOperand().isNullWhenFalse(var) - || getLeftOperand().isFalse() && getRightOperand().isNullWhenTrue(var) - || getRightOperand().isFalse() && getLeftOperand().isNullWhenTrue(var); - - eq EQExpr.isNullWhenTrue(Variable var) = - getLeftOperand().isNull() && getRightOperand().varDecl() == var - || getRightOperand().isNull() && getLeftOperand().varDecl() == var - || getLeftOperand().isTrue() && getRightOperand().isNullWhenTrue(var) - || getRightOperand().isTrue() && getLeftOperand().isNullWhenTrue(var) - || getLeftOperand().isFalse() && getRightOperand().isNullWhenFalse(var) - || getRightOperand().isFalse() && getLeftOperand().isNullWhenFalse(var); - - eq LogNotExpr.isNullWhenTrue(Variable var) = getOperand().isNullWhenFalse(var); - - eq ParExpr.isNullWhenTrue(Variable var) = getExpr().isNullWhenTrue(var); - - eq AndLogicalExpr.isNullWhenTrue(Variable var) = - getLeftOperand().isNullWhenTrue(var) || getRightOperand().isNullWhenTrue(var); - - eq AndBitwiseExpr.isNullWhenTrue(Variable var) = - getLeftOperand().isNullWhenTrue(var) || getRightOperand().isNullWhenTrue(var); - - eq OrLogicalExpr.isNullWhenTrue(Variable var) = - getLeftOperand().isFalse() && getRightOperand().isNullWhenTrue(var) - || getRightOperand().isFalse() && getLeftOperand().isNullWhenTrue(var); - - eq Dot.isNullWhenTrue(Variable var) = - !getLeft().isVariable(var) && getRight().isNullWhenTrue(var); - - // Assume that a method call to X.isNull_(var) is equivalent to a null test on var. - eq MethodAccess.isNullWhenTrue(Variable var) = - name().startsWith("isNull") && getNumArg() == 1 && getArg(0).isVariable(var); - - eq VarAccess.isNullWhenTrue(Variable var) = decl().isNullWhenTrue(var); - - syn boolean Variable.isNullWhenTrue(Variable var); - eq EnumConstant.isNullWhenTrue(Variable var) = false; - eq ParameterDeclaration.isNullWhenTrue(Variable var) = false; - eq FieldDeclarator.isNullWhenTrue(Variable var) = false; - eq CatchParameterDeclaration.isNullWhenTrue(Variable var) = false; - eq InferredParameterDeclaration.isNullWhenTrue(Variable var) = false; - eq VariableDeclarator.isNullWhenTrue(Variable var) = - type().isBoolean() && hasInit() && isEffectivelyFinal() - ? getInit().isNullWhenTrue(var) - : false; - - /** @return {@code true} if the variable var is null when this expression is false. */ - syn boolean Expr.isNullWhenFalse(Variable var) = false; - - eq NEExpr.isNullWhenFalse(Variable var) = - getLeftOperand().isNull() && getRightOperand().varDecl() == var - || getRightOperand().isNull() && getLeftOperand().varDecl() == var - || getLeftOperand().isTrue() && getRightOperand().isNullWhenTrue(var) - || getRightOperand().isTrue() && getLeftOperand().isNullWhenTrue(var) - || getLeftOperand().isFalse() && getRightOperand().isNullWhenFalse(var) - || getRightOperand().isFalse() && getLeftOperand().isNullWhenFalse(var); - - eq EQExpr.isNullWhenFalse(Variable var) = - getLeftOperand().isTrue() && getRightOperand().isNullWhenFalse(var) - || getRightOperand().isTrue() && getLeftOperand().isNullWhenFalse(var) - || getLeftOperand().isFalse() && getRightOperand().isNullWhenTrue(var) - || getRightOperand().isFalse() && getLeftOperand().isNullWhenTrue(var); - - eq LogNotExpr.isNullWhenFalse(Variable var) = getOperand().isNullWhenTrue(var); - - eq ParExpr.isNullWhenFalse(Variable var) = getExpr().isNullWhenFalse(var); - - eq AndLogicalExpr.isNullWhenFalse(Variable var) = - getLeftOperand().isTrue() && getRightOperand().isNullWhenFalse(var) - || getRightOperand().isTrue() && getLeftOperand().isNullWhenFalse(var); - - eq AndBitwiseExpr.isNullWhenFalse(Variable var) = - getLeftOperand().isTrue() && getRightOperand().isNullWhenFalse(var) - || getRightOperand().isTrue() && getLeftOperand().isNullWhenFalse(var); - - eq OrLogicalExpr.isNullWhenFalse(Variable var) = - getLeftOperand().isNullWhenFalse(var) && getRightOperand().isNullWhenFalse(var); - - eq Dot.isNullWhenFalse(Variable var) = - !getLeft().isVariable(var) && getRight().isNullWhenFalse(var); - - // Assume that a method call to X.isNo{t,n}Null_(var) is equivalent to a non-null test on var. - eq MethodAccess.isNullWhenFalse(Variable var) = - (name().startsWith("isNotNull") || name().startsWith("isNonNull")) - && getNumArg() == 1 && getArg(0).isVariable(var); - - eq VarAccess.isNullWhenFalse(Variable var) = decl().isNullWhenFalse(var); - - syn boolean Variable.isNullWhenFalse(Variable var); - eq EnumConstant.isNullWhenFalse(Variable var) = false; - eq ParameterDeclaration.isNullWhenFalse(Variable var) = false; - eq FieldDeclarator.isNullWhenFalse(Variable var) = false; - eq CatchParameterDeclaration.isNullWhenFalse(Variable var) = false; - eq InferredParameterDeclaration.isNullWhenFalse(Variable var) = false; - eq VariableDeclarator.isNullWhenFalse(Variable var) = - type().isBoolean() && hasInit() && isEffectivelyFinal() - ? getInit().isNullWhenFalse(var) - : false; - - /** @return {@code true} if the variable var is non-null when this expression is true. */ - syn boolean Expr.isNonNullWhenTrue(Variable var) = false; - - eq NEExpr.isNonNullWhenTrue(Variable var) = - getLeftOperand().isNull() && getRightOperand().varDecl() == var - || getRightOperand().isNull() && getLeftOperand().varDecl() == var - || getLeftOperand().isTrue() && getRightOperand().isNonNullWhenFalse(var) - || getRightOperand().isTrue() && getLeftOperand().isNonNullWhenFalse(var) - || getLeftOperand().isFalse() && getRightOperand().isNonNullWhenTrue(var) - || getRightOperand().isFalse() && getLeftOperand().isNonNullWhenTrue(var); - - eq EQExpr.isNonNullWhenTrue(Variable var) = - getLeftOperand().isTrue() && getRightOperand().isNonNullWhenTrue(var) - || getRightOperand().isTrue() && getLeftOperand().isNonNullWhenTrue(var) - || getLeftOperand().isFalse() && getRightOperand().isNonNullWhenFalse(var) - || getRightOperand().isFalse() && getLeftOperand().isNonNullWhenFalse(var); - - eq LogNotExpr.isNonNullWhenTrue(Variable var) = getOperand().isNullWhenTrue(var); - - eq ParExpr.isNonNullWhenTrue(Variable var) = getExpr().isNonNullWhenTrue(var); - - eq AndLogicalExpr.isNonNullWhenTrue(Variable var) = - getLeftOperand().isNonNullWhenTrue(var) || getRightOperand().isNonNullWhenTrue(var); - - eq AndBitwiseExpr.isNonNullWhenTrue(Variable var) = - getLeftOperand().isNonNullWhenTrue(var) || getRightOperand().isNonNullWhenTrue(var); - - eq OrLogicalExpr.isNonNullWhenTrue(Variable var) = - getLeftOperand().isFalse() && getRightOperand().isNonNullWhenTrue(var) - || getRightOperand().isFalse() && getLeftOperand().isNonNullWhenTrue(var); - - eq Dot.isNonNullWhenTrue(Variable var) = - !getLeft().isVariable(var) && getRight().isNonNullWhenTrue(var); - - // Assume that a method call to X.isNo{t,n}Null_(var) is equivalent to a non-null test on var. - eq MethodAccess.isNonNullWhenTrue(Variable var) = - (name().startsWith("isNotNull") || name().startsWith("isNonNull")) - && getNumArg() == 1 && getArg(0).isVariable(var); - - eq VarAccess.isNonNullWhenTrue(Variable var) = decl().isNonNullWhenTrue(var); - - syn boolean Variable.isNonNullWhenTrue(Variable var); - eq EnumConstant.isNonNullWhenTrue(Variable var) = false; - eq ParameterDeclaration.isNonNullWhenTrue(Variable var) = false; - eq FieldDeclarator.isNonNullWhenTrue(Variable var) = false; - eq CatchParameterDeclaration.isNonNullWhenTrue(Variable var) = false; - eq InferredParameterDeclaration.isNonNullWhenTrue(Variable var) = false; - eq VariableDeclarator.isNonNullWhenTrue(Variable var) = - type().isBoolean() && hasInit() && isEffectivelyFinal() - ? getInit().isNonNullWhenTrue(var) - : false; - - // An instanceof check guards against the variable being null. - eq InstanceOfExpr.isNonNullWhenTrue(Variable var) = getExpr().isVariable(var); - - /** @return {@code true} if the variable var is non-null when this expression is false. */ - syn boolean Expr.isNonNullWhenFalse(Variable var) = false; - - eq NEExpr.isNonNullWhenFalse(Variable var) = - getLeftOperand().isTrue() && getRightOperand().isNonNullWhenTrue(var) - || getRightOperand().isTrue() && getLeftOperand().isNonNullWhenTrue(var) - || getLeftOperand().isFalse() && getRightOperand().isNonNullWhenFalse(var) - || getRightOperand().isFalse() && getLeftOperand().isNonNullWhenFalse(var); - - eq EQExpr.isNonNullWhenFalse(Variable var) = - getLeftOperand().isNull() && getRightOperand().varDecl() == var - || getRightOperand().isNull() && getLeftOperand().varDecl() == var - || getLeftOperand().isTrue() && getRightOperand().isNonNullWhenFalse(var) - || getRightOperand().isTrue() && getLeftOperand().isNonNullWhenFalse(var) - || getLeftOperand().isFalse() && getRightOperand().isNonNullWhenTrue(var) - || getRightOperand().isFalse() && getLeftOperand().isNonNullWhenTrue(var); - - eq LogNotExpr.isNonNullWhenFalse(Variable var) = getOperand().isNonNullWhenTrue(var); - - eq ParExpr.isNonNullWhenFalse(Variable var) = getExpr().isNonNullWhenFalse(var); - - eq AndLogicalExpr.isNonNullWhenFalse(Variable var) = - getLeftOperand().isTrue() && getRightOperand().isNonNullWhenFalse(var) - || getRightOperand().isTrue() && getLeftOperand().isNonNullWhenFalse(var); - - eq AndBitwiseExpr.isNonNullWhenFalse(Variable var) = - getLeftOperand().isTrue() && getRightOperand().isNonNullWhenFalse(var) - || getRightOperand().isTrue() && getLeftOperand().isNonNullWhenFalse(var); - - eq OrLogicalExpr.isNonNullWhenFalse(Variable var) = - getLeftOperand().isNonNullWhenFalse(var) || getRightOperand().isNonNullWhenFalse(var); - - eq Dot.isNonNullWhenFalse(Variable var) = - !getLeft().isVariable(var) && getRight().isNonNullWhenFalse(var); - - // Assume that a method call to X.isNull_(var) is equivalent to a null test on var. - eq MethodAccess.isNonNullWhenFalse(Variable var) = - name().startsWith("isNull") && getNumArg() == 1 && getArg(0).isVariable(var); - - eq VarAccess.isNonNullWhenFalse(Variable var) = decl().isNonNullWhenFalse(var); - - syn boolean Variable.isNonNullWhenFalse(Variable var); - eq EnumConstant.isNonNullWhenFalse(Variable var) = false; - eq ParameterDeclaration.isNonNullWhenFalse(Variable var) = false; - eq FieldDeclarator.isNonNullWhenFalse(Variable var) = false; - eq CatchParameterDeclaration.isNonNullWhenFalse(Variable var) = false; - eq InferredParameterDeclaration.isNonNullWhenFalse(Variable var) = false; - eq VariableDeclarator.isNonNullWhenFalse(Variable var) = - type().isBoolean() && hasInit() && isEffectivelyFinal() - ? getInit().isNonNullWhenFalse(var) - : false; - - syn boolean Expr.isNull() = type().isNull(); - eq NullLiteral.isNull() = true; -} +///** +// * Copyright 2015 Google Inc. All Rights Reserved. +// * +// * Licensed under the Apache License, Version 2.0 (the "License"); +// * you may not use this file except in compliance with the License. +// * You may obtain a copy of the License at +// * +// * http://www.apache.org/licenses/LICENSE-2.0 +// * +// * Unless required by applicable law or agreed to in writing, software +// * distributed under the License is distributed on an "AS IS" BASIS, +// * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// * See the License for the specific language governing permissions and +// * limitations under the License. +// */ +// +///** +// * Adds an analysis that checks for dereferences of a parameter declared nullable. +// * +// * <p>When a method or constructor parameter is annotated with javax.annotation.Nullable, +// * we check that all dereferences of that parameter are guarded by a null-check. The +// * analysis is control-flow sensitive in that it will recognize if all control flow paths +// * to the dereference are effectively guarded by a null check, for example: {@code +// * if (p == null) return; +// * p.x(); // Guarded by null check above. +// * } +// * +// * <p>The analysis is not intraprocedural, so in order to avoid false positives +// * where a method call guards against nullness the analyzer assumes that +// * calling a method with an argument x results in an exception if x is null and +// * thus works like an effective null guard for x. +// * +// * <p>To find potential null dereferences on nullable parameters, the analysis does a forward CFG +// * traversal from the entry-point of the method. Note that this is only done whenever a +// * {@code @Nullable} parameter has been encountered in the method. The traversal explores all paths +// * from the method entry until it finds null guard statements such as {@code if (p != null)}. The +// * branches that are protected from nullness get pruned in the search and the search continues on +// * other branches. The search is performed by a {@code NullDereferenceLocator}, which implements the +// * {@code CfgSearch} interface. This visitor is invoked for each node in the CFG search, and it +// * decides if the search will continue from that node, or if the current edge should be skipped +// * (i.e., pruned). +// * +// * <p>In order to find the position in the CFG where a potential null dereference occurs, CFG marker +// * nodes are inserted. This is done by adding a synthesized non-terminal attribute (NTA) for +// * a CfgMarker on Dot (which represents Java dot expressions). This marker node appears +// * in the CFG as "nullable access" because it is inserted whenever a nullable variable is +// * dereferenced. +// * +// * <p>Dataflow analysis is not used, so in order to analyze a parameter it is required to be +// * effectively final, i.e. it is not assigned anywhere in side the body of the method/constructor. +// */ +//aspect NullableDereferenceAnalysis { +// +// // Give ParameterDeclaration access to the inherited compilationUnit attribute. +// inh CompilationUnit ParameterDeclaration.compilationUnit(); +// +// ParameterDeclaration contributes nullableDereferenceFinding() +// when nullableDereferenceFinding() != null +// to CompilationUnit.findings() +// for compilationUnit(); +// +// /** +// * Generate a NullableDereference finding for this dot expression, +// * if no finding should be reported this attribute returns {@code null}. +// */ +// syn lazy ExtendJFinding ParameterDeclaration.nullableDereferenceFinding() { +// if (!getModifiers().hasNullableAnnotation()) { +// return null; +// } +// if (!isFinal() && !isEffectivelyFinal()) { +// // Do not analyze non-effectively final parameters. +// return null; +// } +// Expr location = findNullableDereference(this); +// if (location == null) { +// return null; +// } +// ExtendJFinding finding = location.finding("NullableDereference", String.format( +// "Dereferencing %s, which was declared @Nullable.", name())); +// if (compilationUnit().fromSource()) { +// ASTNode modifierLocation = nullableModifierLocation(); +// int line = getLine(modifierLocation.getStart()); +// int startCol = getColumn(modifierLocation.getStart()); +// int endCol = getColumn(modifierLocation.getEnd()); +// if (startCol < endCol && line == getLine(modifierLocation.getEnd())) { +// try { +// InputStream data = compilationUnit().getClassSource().openInputStream(); +// java.util.Scanner scanner = new java.util.Scanner(data); +// for (int i = 1; i < line && scanner.hasNextLine(); ++i) { +// scanner.nextLine(); +// } +// if (scanner.hasNextLine()) { +// String text = scanner.nextLine(); +// finding.addFix("Remove the @Nullable annotation.", +// line, line, +// text.substring(0,startCol-1) + text.substring(endCol+1) + "\n"); +// } +// } catch (IOException e) { +// // Failed to unparse the current line. +// // This is not a serious problem; we just don't give a fix suggestion. +// } +// } +// } +// return finding; +// } +// +// // Exclude variable arity parameters from Nullable dereference analysis. +// // When a variable arity parameter is annotated @Nullable, that will most likely be intended as a +// // @Nullable annotation for the individual parameters, not the containing argument array. +// eq VariableArityParameterDeclaration.nullableDereferenceFinding() = null; +// +// /** +// * Find the location node for the javax.annotation.Nullable annotation in the modifier list. +// * Returns {@code null} if the location of the modifier was not found. +// */ +// syn ASTNode ParameterDeclaration.nullableModifierLocation() = +// getModifiers().nullableModifierLocation(); +// +// syn ASTNode Modifiers.nullableModifierLocation() { +// for (Modifier modifier : getModifierList()) { +// if (modifier.isAnnotation("javax.annotation", "Nullable")) { +// return modifier.locationNode(); +// } +// } +// return null; +// } +// +// /** +// * Find a location, not necessarily the first location, in the host method/constructor where the +// * parameter is accessed without a null guard. +// */ +// inh Expr ParameterDeclaration.findNullableDereference(Variable var); +// +// eq Program.getChild().findNullableDereference(Variable var) = null; +// eq BodyDecl.getChild().findNullableDereference(Variable var) = null; +// +// eq MethodDecl.getParameter().findNullableDereference(Variable var) { +// if (!hasBlock()) { +// return null; +// } +// CfgNode cfgNode = entry().bfs(new NullDereferenceLocator(var)); +// return cfgNode == null ? null : cfgNode.receiverExpr(); +// } +// +// eq ConstructorDecl.getParameter().findNullableDereference(Variable var) { +// CfgNode cfgNode = entry().bfs(new NullDereferenceLocator(var)); +// return cfgNode == null ? null : cfgNode.receiverExpr(); +// } +// +// /** +// * A CFG visitor that searches in the forward CFG for a nullable dereference. +// * +// * <p>The search stops at parts of the search tree guarded by a null check +// * on the receiver variable. +// */ +// class NullDereferenceLocator implements CfgVisitor { +// private final Variable var; +// +// public NullDereferenceLocator(Variable var) { +// this.var = var; +// } +// +// @Override public SearchAction processEdge(CfgNode pred, CfgNode succ) { +// if (pred.isNullGuard(var, succ)) { +// return SearchAction.SKIP; +// } +// Expr receiver = succ.receiverExpr(); +// if (receiver != null && receiver.isVariable(var) && !receiver.hasNullGuard(var)) { +// return SearchAction.SUCCESSOR_MATCH; +// } +// return SearchAction.CONTINUE; +// } +// } +// +// /** +// * Returns the receiver expression if the CFG node is the child of a dereference expression. +// * Returns {@code null} otherwise. +// */ +// inh Expr CfgNode.receiverExpr(); +// eq Program.getChild().receiverExpr() = null; +// eq BodyDecl.getChild().receiverExpr() = null; +// eq BodyDecl.exit().receiverExpr() = null; +// eq TryStmt.tryEntryMarker().receiverExpr() = null; +// eq BreakStmt.marker().receiverExpr() = null; +// eq ContinueStmt.marker().receiverExpr() = null; +// eq ReturnStmt.marker().receiverExpr() = null; +// eq MethodAccess.exceptionNode().receiverExpr() = null; +// eq MethodAccess.call().receiverExpr() = +// hasPrevExpr() +// ? prevExpr() +// : null; +// eq ThrowStmt.exceptionNode().receiverExpr() = null; +// eq TryStmt.exceptionNode().receiverExpr() = null; +// eq ConditionalExpr.branch().receiverExpr() = null; +// eq ConditionalExpr.thenEndMarker().receiverExpr() = null; +// eq ConditionalExpr.elseEndMarker().receiverExpr() = null; +// eq IfStmt.branch().receiverExpr() = null; +// eq IfStmt.thenEndMarker().receiverExpr() = null; +// eq IfStmt.elseEndMarker().receiverExpr() = null; +// eq ForStmt.branch().receiverExpr() = null; +// eq EnhancedForStmt.branch().receiverExpr() = null; +// eq WhileStmt.branch().receiverExpr() = null; +// eq DoStmt.branch().receiverExpr() = null; +// eq SwitchStmt.branch().receiverExpr() = null; +// eq LambdaBody.exit().receiverExpr() = null; +// eq Dot.nullableDereferenceMarker().receiverExpr() = getLeft(); +// +// /** Marker node used to find location of a nullable dereference in the CFG. */ +// syn nta CfgMarker Dot.nullableDereferenceMarker() = new CfgMarker(); +// +// /** Insert nullable dereference marker in the CFG. */ +// refine SimpleCFG +// eq Dot.getLeft().follow() = +// getRight().isMethodAccess() +// ? refined() +// : nullableDereferenceMarker(); +// +// eq Dot.nullableDereferenceMarker().succ() = Collections.singleton(getRight().entry()); +// +// syn boolean CfgNode.isNullGuard(Variable var, CfgNode succ) = false; +// +// /** +// * We assume that calling a method with the variable var as an argument +// * results in an exception thrown by the method call if var is null. This is +// * not true for many methods, but it should reduce the false positive rate +// * for the NullableDereference analyzer. +// */ +// eq CfgMethodCall.isNullGuard(Variable var, CfgNode succ) { +// if (succ instanceof CfgException) { +// return false; +// } +// MethodAccess access = methodAccess(); +// for (Expr arg : access.getArgList()) { +// if (arg.isVariable(var)) { +// return true; +// } +// } +// return false; +// } +// +// /** Check if this branch has a null-guarding condition. */ +// eq CfgBranch.isNullGuard(Variable var, CfgNode succ) = inNullGuard(var, succ); +// +// inh boolean CfgBranch.inNullGuard(Variable var, CfgNode succ); +// +// eq IfStmt.branch().inNullGuard(Variable var, CfgNode succ) = +// succ == getThen().entry() +// ? getCondition().isNonNullWhenTrue(var) +// : getCondition().isNonNullWhenFalse(var); +// +// eq ConditionalExpr.branch().inNullGuard(Variable var, CfgNode succ) = +// succ == getTrueExpr().entry() +// ? getCondition().isNonNullWhenTrue(var) +// : getCondition().isNonNullWhenFalse(var); +// +// eq ForStmt.branch().inNullGuard(Variable var, CfgNode succ) = +// succ == getStmt().entry() +// ? getCondition().isNonNullWhenTrue(var) +// : getCondition().isNonNullWhenFalse(var); +// +// eq WhileStmt.branch().inNullGuard(Variable var, CfgNode succ) = +// succ == getStmt().entry() +// ? getCondition().isNonNullWhenTrue(var) +// : getCondition().isNonNullWhenFalse(var); +// +// eq EnhancedForStmt.branch().inNullGuard(Variable var, CfgNode succ) = false; +// eq DoStmt.branch().inNullGuard(Variable var, CfgNode succ) = false; +// eq SwitchStmt.branch().inNullGuard(Variable var, CfgNode succ) = false; +// +// /** Returns {@code true} if this set of modifiers includes {@code javax.annotation.Nullable}. */ +// syn boolean Modifiers.hasNullableAnnotation() = hasAnnotation("javax.annotation", "Nullable"); +// +// /** Return {@code true} if this expression is guarded by a != null check for var. */ +// inh boolean Expr.hasNullGuard(Variable var); +// eq Program.getChild().hasNullGuard(Variable var) = false; +// eq IfStmt.getThen().hasNullGuard(Variable var) = getCondition().isNonNullWhenTrue(var); +// eq IfStmt.getElse().hasNullGuard(Variable var) = getCondition().isNonNullWhenFalse(var); +// eq WhileStmt.getStmt().hasNullGuard(Variable var) = getCondition().isNonNullWhenTrue(var); +// eq ForStmt.getStmt().hasNullGuard(Variable var) = getCondition().isNonNullWhenTrue(var); +// eq ConditionalExpr.getTrueExpr().hasNullGuard(Variable var) = +// getCondition().isNonNullWhenTrue(var) || hasNullGuard(var); +// eq ConditionalExpr.getFalseExpr().hasNullGuard(Variable var) = +// getCondition().isNonNullWhenFalse(var) || hasNullGuard(var); +// eq AndLogicalExpr.getRightOperand().hasNullGuard(Variable var) = +// getLeftOperand().isNonNullWhenTrue(var) || hasNullGuard(var); +// eq AndBitwiseExpr.getRightOperand().hasNullGuard(Variable var) = +// getLeftOperand().isNonNullWhenTrue(var) || hasNullGuard(var); +// eq OrLogicalExpr.getRightOperand().hasNullGuard(Variable var) = +// getLeftOperand().isNonNullWhenFalse(var) || hasNullGuard(var); +// +// /** @return {@code true} if the variable var is null when this expression is true. */ +// syn boolean Expr.isNullWhenTrue(Variable var) = false; +// +// eq NEExpr.isNullWhenTrue(Variable var) = +// getLeftOperand().isTrue() && getRightOperand().isNullWhenFalse(var) +// || getRightOperand().isTrue() && getLeftOperand().isNullWhenFalse(var) +// || getLeftOperand().isFalse() && getRightOperand().isNullWhenTrue(var) +// || getRightOperand().isFalse() && getLeftOperand().isNullWhenTrue(var); +// +// eq EQExpr.isNullWhenTrue(Variable var) = +// getLeftOperand().isNull() && getRightOperand().varDecl() == var +// || getRightOperand().isNull() && getLeftOperand().varDecl() == var +// || getLeftOperand().isTrue() && getRightOperand().isNullWhenTrue(var) +// || getRightOperand().isTrue() && getLeftOperand().isNullWhenTrue(var) +// || getLeftOperand().isFalse() && getRightOperand().isNullWhenFalse(var) +// || getRightOperand().isFalse() && getLeftOperand().isNullWhenFalse(var); +// +// eq LogNotExpr.isNullWhenTrue(Variable var) = getOperand().isNullWhenFalse(var); +// +// eq ParExpr.isNullWhenTrue(Variable var) = getExpr().isNullWhenTrue(var); +// +// eq AndLogicalExpr.isNullWhenTrue(Variable var) = +// getLeftOperand().isNullWhenTrue(var) || getRightOperand().isNullWhenTrue(var); +// +// eq AndBitwiseExpr.isNullWhenTrue(Variable var) = +// getLeftOperand().isNullWhenTrue(var) || getRightOperand().isNullWhenTrue(var); +// +// eq OrLogicalExpr.isNullWhenTrue(Variable var) = +// getLeftOperand().isFalse() && getRightOperand().isNullWhenTrue(var) +// || getRightOperand().isFalse() && getLeftOperand().isNullWhenTrue(var); +// +// eq Dot.isNullWhenTrue(Variable var) = +// !getLeft().isVariable(var) && getRight().isNullWhenTrue(var); +// +// // Assume that a method call to X.isNull_(var) is equivalent to a null test on var. +// eq MethodAccess.isNullWhenTrue(Variable var) = +// name().startsWith("isNull") && getNumArg() == 1 && getArg(0).isVariable(var); +// +// eq VarAccess.isNullWhenTrue(Variable var) = decl().isNullWhenTrue(var); +// +// syn boolean Variable.isNullWhenTrue(Variable var); +// eq EnumConstant.isNullWhenTrue(Variable var) = false; +// eq ParameterDeclaration.isNullWhenTrue(Variable var) = false; +// eq FieldDeclarator.isNullWhenTrue(Variable var) = false; +// eq CatchParameterDeclaration.isNullWhenTrue(Variable var) = false; +// eq InferredParameterDeclaration.isNullWhenTrue(Variable var) = false; +// eq VariableDeclarator.isNullWhenTrue(Variable var) = +// type().isBoolean() && hasInit() && isEffectivelyFinal() +// ? getInit().isNullWhenTrue(var) +// : false; +// +// /** @return {@code true} if the variable var is null when this expression is false. */ +// syn boolean Expr.isNullWhenFalse(Variable var) = false; +// +// eq NEExpr.isNullWhenFalse(Variable var) = +// getLeftOperand().isNull() && getRightOperand().varDecl() == var +// || getRightOperand().isNull() && getLeftOperand().varDecl() == var +// || getLeftOperand().isTrue() && getRightOperand().isNullWhenTrue(var) +// || getRightOperand().isTrue() && getLeftOperand().isNullWhenTrue(var) +// || getLeftOperand().isFalse() && getRightOperand().isNullWhenFalse(var) +// || getRightOperand().isFalse() && getLeftOperand().isNullWhenFalse(var); +// +// eq EQExpr.isNullWhenFalse(Variable var) = +// getLeftOperand().isTrue() && getRightOperand().isNullWhenFalse(var) +// || getRightOperand().isTrue() && getLeftOperand().isNullWhenFalse(var) +// || getLeftOperand().isFalse() && getRightOperand().isNullWhenTrue(var) +// || getRightOperand().isFalse() && getLeftOperand().isNullWhenTrue(var); +// +// eq LogNotExpr.isNullWhenFalse(Variable var) = getOperand().isNullWhenTrue(var); +// +// eq ParExpr.isNullWhenFalse(Variable var) = getExpr().isNullWhenFalse(var); +// +// eq AndLogicalExpr.isNullWhenFalse(Variable var) = +// getLeftOperand().isTrue() && getRightOperand().isNullWhenFalse(var) +// || getRightOperand().isTrue() && getLeftOperand().isNullWhenFalse(var); +// +// eq AndBitwiseExpr.isNullWhenFalse(Variable var) = +// getLeftOperand().isTrue() && getRightOperand().isNullWhenFalse(var) +// || getRightOperand().isTrue() && getLeftOperand().isNullWhenFalse(var); +// +// eq OrLogicalExpr.isNullWhenFalse(Variable var) = +// getLeftOperand().isNullWhenFalse(var) && getRightOperand().isNullWhenFalse(var); +// +// eq Dot.isNullWhenFalse(Variable var) = +// !getLeft().isVariable(var) && getRight().isNullWhenFalse(var); +// +// // Assume that a method call to X.isNo{t,n}Null_(var) is equivalent to a non-null test on var. +// eq MethodAccess.isNullWhenFalse(Variable var) = +// (name().startsWith("isNotNull") || name().startsWith("isNonNull")) +// && getNumArg() == 1 && getArg(0).isVariable(var); +// +// eq VarAccess.isNullWhenFalse(Variable var) = decl().isNullWhenFalse(var); +// +// syn boolean Variable.isNullWhenFalse(Variable var); +// eq EnumConstant.isNullWhenFalse(Variable var) = false; +// eq ParameterDeclaration.isNullWhenFalse(Variable var) = false; +// eq FieldDeclarator.isNullWhenFalse(Variable var) = false; +// eq CatchParameterDeclaration.isNullWhenFalse(Variable var) = false; +// eq InferredParameterDeclaration.isNullWhenFalse(Variable var) = false; +// eq VariableDeclarator.isNullWhenFalse(Variable var) = +// type().isBoolean() && hasInit() && isEffectivelyFinal() +// ? getInit().isNullWhenFalse(var) +// : false; +// +// /** @return {@code true} if the variable var is non-null when this expression is true. */ +// syn boolean Expr.isNonNullWhenTrue(Variable var) = false; +// +// eq NEExpr.isNonNullWhenTrue(Variable var) = +// getLeftOperand().isNull() && getRightOperand().varDecl() == var +// || getRightOperand().isNull() && getLeftOperand().varDecl() == var +// || getLeftOperand().isTrue() && getRightOperand().isNonNullWhenFalse(var) +// || getRightOperand().isTrue() && getLeftOperand().isNonNullWhenFalse(var) +// || getLeftOperand().isFalse() && getRightOperand().isNonNullWhenTrue(var) +// || getRightOperand().isFalse() && getLeftOperand().isNonNullWhenTrue(var); +// +// eq EQExpr.isNonNullWhenTrue(Variable var) = +// getLeftOperand().isTrue() && getRightOperand().isNonNullWhenTrue(var) +// || getRightOperand().isTrue() && getLeftOperand().isNonNullWhenTrue(var) +// || getLeftOperand().isFalse() && getRightOperand().isNonNullWhenFalse(var) +// || getRightOperand().isFalse() && getLeftOperand().isNonNullWhenFalse(var); +// +// eq LogNotExpr.isNonNullWhenTrue(Variable var) = getOperand().isNullWhenTrue(var); +// +// eq ParExpr.isNonNullWhenTrue(Variable var) = getExpr().isNonNullWhenTrue(var); +// +// eq AndLogicalExpr.isNonNullWhenTrue(Variable var) = +// getLeftOperand().isNonNullWhenTrue(var) || getRightOperand().isNonNullWhenTrue(var); +// +// eq AndBitwiseExpr.isNonNullWhenTrue(Variable var) = +// getLeftOperand().isNonNullWhenTrue(var) || getRightOperand().isNonNullWhenTrue(var); +// +// eq OrLogicalExpr.isNonNullWhenTrue(Variable var) = +// getLeftOperand().isFalse() && getRightOperand().isNonNullWhenTrue(var) +// || getRightOperand().isFalse() && getLeftOperand().isNonNullWhenTrue(var); +// +// eq Dot.isNonNullWhenTrue(Variable var) = +// !getLeft().isVariable(var) && getRight().isNonNullWhenTrue(var); +// +// // Assume that a method call to X.isNo{t,n}Null_(var) is equivalent to a non-null test on var. +// eq MethodAccess.isNonNullWhenTrue(Variable var) = +// (name().startsWith("isNotNull") || name().startsWith("isNonNull")) +// && getNumArg() == 1 && getArg(0).isVariable(var); +// +// eq VarAccess.isNonNullWhenTrue(Variable var) = decl().isNonNullWhenTrue(var); +// +// syn boolean Variable.isNonNullWhenTrue(Variable var); +// eq EnumConstant.isNonNullWhenTrue(Variable var) = false; +// eq ParameterDeclaration.isNonNullWhenTrue(Variable var) = false; +// eq FieldDeclarator.isNonNullWhenTrue(Variable var) = false; +// eq CatchParameterDeclaration.isNonNullWhenTrue(Variable var) = false; +// eq InferredParameterDeclaration.isNonNullWhenTrue(Variable var) = false; +// eq VariableDeclarator.isNonNullWhenTrue(Variable var) = +// type().isBoolean() && hasInit() && isEffectivelyFinal() +// ? getInit().isNonNullWhenTrue(var) +// : false; +// +// // An instanceof check guards against the variable being null. +// eq InstanceOfExpr.isNonNullWhenTrue(Variable var) = getExpr().isVariable(var); +// +// /** @return {@code true} if the variable var is non-null when this expression is false. */ +// syn boolean Expr.isNonNullWhenFalse(Variable var) = false; +// +// eq NEExpr.isNonNullWhenFalse(Variable var) = +// getLeftOperand().isTrue() && getRightOperand().isNonNullWhenTrue(var) +// || getRightOperand().isTrue() && getLeftOperand().isNonNullWhenTrue(var) +// || getLeftOperand().isFalse() && getRightOperand().isNonNullWhenFalse(var) +// || getRightOperand().isFalse() && getLeftOperand().isNonNullWhenFalse(var); +// +// eq EQExpr.isNonNullWhenFalse(Variable var) = +// getLeftOperand().isNull() && getRightOperand().varDecl() == var +// || getRightOperand().isNull() && getLeftOperand().varDecl() == var +// || getLeftOperand().isTrue() && getRightOperand().isNonNullWhenFalse(var) +// || getRightOperand().isTrue() && getLeftOperand().isNonNullWhenFalse(var) +// || getLeftOperand().isFalse() && getRightOperand().isNonNullWhenTrue(var) +// || getRightOperand().isFalse() && getLeftOperand().isNonNullWhenTrue(var); +// +// eq LogNotExpr.isNonNullWhenFalse(Variable var) = getOperand().isNonNullWhenTrue(var); +// +// eq ParExpr.isNonNullWhenFalse(Variable var) = getExpr().isNonNullWhenFalse(var); +// +// eq AndLogicalExpr.isNonNullWhenFalse(Variable var) = +// getLeftOperand().isTrue() && getRightOperand().isNonNullWhenFalse(var) +// || getRightOperand().isTrue() && getLeftOperand().isNonNullWhenFalse(var); +// +// eq AndBitwiseExpr.isNonNullWhenFalse(Variable var) = +// getLeftOperand().isTrue() && getRightOperand().isNonNullWhenFalse(var) +// || getRightOperand().isTrue() && getLeftOperand().isNonNullWhenFalse(var); +// +// eq OrLogicalExpr.isNonNullWhenFalse(Variable var) = +// getLeftOperand().isNonNullWhenFalse(var) || getRightOperand().isNonNullWhenFalse(var); +// +// eq Dot.isNonNullWhenFalse(Variable var) = +// !getLeft().isVariable(var) && getRight().isNonNullWhenFalse(var); +// +// // Assume that a method call to X.isNull_(var) is equivalent to a null test on var. +// eq MethodAccess.isNonNullWhenFalse(Variable var) = +// name().startsWith("isNull") && getNumArg() == 1 && getArg(0).isVariable(var); +// +// eq VarAccess.isNonNullWhenFalse(Variable var) = decl().isNonNullWhenFalse(var); +// +// syn boolean Variable.isNonNullWhenFalse(Variable var); +// eq EnumConstant.isNonNullWhenFalse(Variable var) = false; +// eq ParameterDeclaration.isNonNullWhenFalse(Variable var) = false; +// eq FieldDeclarator.isNonNullWhenFalse(Variable var) = false; +// eq CatchParameterDeclaration.isNonNullWhenFalse(Variable var) = false; +// eq InferredParameterDeclaration.isNonNullWhenFalse(Variable var) = false; +// eq VariableDeclarator.isNonNullWhenFalse(Variable var) = +// type().isBoolean() && hasInit() && isEffectivelyFinal() +// ? getInit().isNonNullWhenFalse(var) +// : false; +// +// syn boolean Expr.isNull() = type().isNull(); +// eq NullLiteral.isNull() = true; +//} diff --git a/reusablecfg/src/main/jastadd/PrintCfg.jrag b/reusablecfg/src/main/jastadd/PrintCfg.jrag index 35b7f87..45651f3 100644 --- a/reusablecfg/src/main/jastadd/PrintCfg.jrag +++ b/reusablecfg/src/main/jastadd/PrintCfg.jrag @@ -137,7 +137,7 @@ aspect PrintCfg { eq ReturnStmt.marker().markerName() = "return"; eq TryStmt.tryEntryMarker().markerName() = "try"; eq Program.getChild().markerName() = "marker"; - eq Dot.nullableDereferenceMarker().markerName() = "nullable access"; +// eq Dot.nullableDereferenceMarker().markerName() = "nullable access"; eq ForStmt.loopEndMarker().markerName() = "for-end"; eq EnhancedForStmt.loopEndMarker().markerName() = "for-end"; eq WhileStmt.loopEndMarker().markerName() = "while-end"; diff --git a/reusablecfg/src/main/jastadd/PrintCfgTest.jrag b/reusablecfg/src/main/jastadd/PrintCfgTest.jrag index cc881f8..d8f8c36 100644 --- a/reusablecfg/src/main/jastadd/PrintCfgTest.jrag +++ b/reusablecfg/src/main/jastadd/PrintCfgTest.jrag @@ -171,7 +171,7 @@ aspect PrintCfgTest { eq ReturnStmt.marker().markerVarName() = "returnMarker"; eq TryStmt.tryEntryMarker().markerVarName() = "tryEntry"; eq Program.getChild().markerVarName() = "marker"; - eq Dot.nullableDereferenceMarker().markerVarName() = "nullable"; +// eq Dot.nullableDereferenceMarker().markerVarName() = "nullable"; eq ForStmt.loopEndMarker().markerVarName() = "forEnd"; eq EnhancedForStmt.loopEndMarker().markerVarName() = "forEnd"; eq WhileStmt.loopEndMarker().markerVarName() = "whileEnd"; diff --git a/reusablecfg/src/test/java/de/tudresden/inf/st/reusablecfg/NullableDereferenceTest.java b/reusablecfg/src/test/java/de/tudresden/inf/st/reusablecfg/NullableDereferenceTest.java index e5b1420..db5a630 100644 --- a/reusablecfg/src/test/java/de/tudresden/inf/st/reusablecfg/NullableDereferenceTest.java +++ b/reusablecfg/src/test/java/de/tudresden/inf/st/reusablecfg/NullableDereferenceTest.java @@ -36,110 +36,114 @@ import java.util.Collection; @RunWith(JUnit4.class) public class NullableDereferenceTest { - @Test public void suggestedFixEndsWithNewline() { - CompilationUnit unit = StmtCfgTest.parseFile("NullableNullGuard01", - Program.ANALYZER_TYPE_FILTER); - Collection<ExtendJFinding> findings = unit.findings(); - assertThat(findings).isNotEmpty(); - ExtendJFinding finding = findings.iterator().next(); - assertThat(finding.fixes).hasSize(1); - assertThat(finding.fixes.iterator().next().newText).endsWith("\n"); - } - - @Test public void nullGuards01() { - Collection<String> findings = StmtCfgTest.findings("NullableNullGuard01"); - assertThat(findings).containsExactly( - "../simplecfg/testdata/NullableNullGuard01.javax:42:25: Dereferencing p, which was declared @Nullable.", - "../simplecfg/testdata/NullableNullGuard01.javax:49:12: Dereferencing p, which was declared @Nullable.", - "../simplecfg/testdata/NullableNullGuard01.javax:62:12: Dereferencing p, which was declared @Nullable.", - "../simplecfg/testdata/NullableNullGuard01.javax:93:12: Dereferencing q, which was declared @Nullable." - ); - } - - @Test public void nullGuards02() { - Collection<String> findings = StmtCfgTest.findings("NullableNullGuard02"); - assertThat(findings).containsExactly( - "../simplecfg/testdata/NullableNullGuard02.javax:49:7: Dereferencing p, which was declared @Nullable.", - "../simplecfg/testdata/NullableNullGuard02.javax:54:7: Dereferencing p, which was declared @Nullable." - ); - } - - @Test public void nullGuards03() { - Collection<Integer> lines = StmtCfgTest.findingLines("NullableNullGuard03", - Program.ANALYZER_TYPE_FILTER); - assertThat(lines).containsExactly(28, 34, 41, 48, 113, 119); - } - - @Test public void methodNullGuard01() { - Collection<String> findings = StmtCfgTest.findings("NullableMethodNullGuard01"); - assertThat(findings).isEmpty(); - } - - @Test public void dataflow01() { - Collection<String> findings = StmtCfgTest.findings("NullableDataflow01"); - assertThat(findings).containsExactly( - "../simplecfg/testdata/NullableDataflow01.javax:27:7: Dereferencing p, which was declared @Nullable.", - "../simplecfg/testdata/NullableDataflow01.javax:35:7: Dereferencing p, which was declared @Nullable." - ); - } - - @Test public void instanceOf() { - Collection<String> findings = StmtCfgTest.findings("NullableInstanceOf"); - assertThat(findings).isEmpty(); - } - - @Test public void variableArity() { - Collection<String> findings = StmtCfgTest.findings("NullableVariableArity"); - assertThat(findings).isEmpty(); - } - - @Test public void nullableDereference01() { - Collection<String> findings = StmtCfgTest.findings("NullableDereference01"); - assertThat(findings).containsExactly( - "../simplecfg/testdata/NullableDereference01.javax:27:12: Dereferencing p, which was declared @Nullable.", - "../simplecfg/testdata/NullableDereference01.javax:31:12: Dereferencing p, which was declared @Nullable." - ); - } - - /** Test false positive for GitHub issue #10. */ - @Test public void issue10() { - Collection<String> findings = StmtCfgTest.findings("NullableDereferenceIssue10"); - assertThat(findings).containsExactly( - "../simplecfg/testdata/NullableDereferenceIssue10.javax:34:31: Dereferencing y, which was declared @Nullable." - ); - } - - @Test public void issue11() { - Collection<String> findings = StmtCfgTest.findings("NullableDereferenceIssue11"); - assertThat(findings).isEmpty(); - } - - @Test public void issue12() { - Collection<String> findings = StmtCfgTest.findings("NullableDereferenceIssue12"); - assertThat(findings).isEmpty(); - } - - @Test public void eqExpr() { - Collection<String> findings = StmtCfgTest.findings("NullableDereferenceEqExpr"); - assertThat(findings).isEmpty(); - } - - @Test public void neExpr() { - Collection<String> findings = StmtCfgTest.findings("NullableDereferenceNeExpr"); - assertThat(findings).isEmpty(); - } - - @Test public void methodCall() { - Collection<String> findings = StmtCfgTest.findings("NullableDereferenceMethodCall"); - assertThat(findings).containsExactly( - "../simplecfg/testdata/NullableDereferenceMethodCall.javax:41:16: " - + "Dereferencing p, which was declared @Nullable."); - } - - @Test public void issue13() { - Collection<String> findings = StmtCfgTest.findings("NullableDereferenceIssue13"); - assertThat(findings).containsExactly( - "../simplecfg/testdata/NullableDereferenceIssue13.javax:33:7: " - + "Dereferencing obj, which was declared @Nullable."); - } + @Test public void test() { + // this is a dummy test to appease intellij + } + +// @Test public void suggestedFixEndsWithNewline() { +// CompilationUnit unit = StmtCfgTest.parseFile("NullableNullGuard01", +// Program.ANALYZER_TYPE_FILTER); +// Collection<ExtendJFinding> findings = unit.findings(); +// assertThat(findings).isNotEmpty(); +// ExtendJFinding finding = findings.iterator().next(); +// assertThat(finding.fixes).hasSize(1); +// assertThat(finding.fixes.iterator().next().newText).endsWith("\n"); +// } +// +// @Test public void nullGuards01() { +// Collection<String> findings = StmtCfgTest.findings("NullableNullGuard01"); +// assertThat(findings).containsExactly( +// "../simplecfg/testdata/NullableNullGuard01.javax:42:25: Dereferencing p, which was declared @Nullable.", +// "../simplecfg/testdata/NullableNullGuard01.javax:49:12: Dereferencing p, which was declared @Nullable.", +// "../simplecfg/testdata/NullableNullGuard01.javax:62:12: Dereferencing p, which was declared @Nullable.", +// "../simplecfg/testdata/NullableNullGuard01.javax:93:12: Dereferencing q, which was declared @Nullable." +// ); +// } +// +// @Test public void nullGuards02() { +// Collection<String> findings = StmtCfgTest.findings("NullableNullGuard02"); +// assertThat(findings).containsExactly( +// "../simplecfg/testdata/NullableNullGuard02.javax:49:7: Dereferencing p, which was declared @Nullable.", +// "../simplecfg/testdata/NullableNullGuard02.javax:54:7: Dereferencing p, which was declared @Nullable." +// ); +// } +// +// @Test public void nullGuards03() { +// Collection<Integer> lines = StmtCfgTest.findingLines("NullableNullGuard03", +// Program.ANALYZER_TYPE_FILTER); +// assertThat(lines).containsExactly(28, 34, 41, 48, 113, 119); +// } +// +// @Test public void methodNullGuard01() { +// Collection<String> findings = StmtCfgTest.findings("NullableMethodNullGuard01"); +// assertThat(findings).isEmpty(); +// } +// +// @Test public void dataflow01() { +// Collection<String> findings = StmtCfgTest.findings("NullableDataflow01"); +// assertThat(findings).containsExactly( +// "../simplecfg/testdata/NullableDataflow01.javax:27:7: Dereferencing p, which was declared @Nullable.", +// "../simplecfg/testdata/NullableDataflow01.javax:35:7: Dereferencing p, which was declared @Nullable." +// ); +// } +// +// @Test public void instanceOf() { +// Collection<String> findings = StmtCfgTest.findings("NullableInstanceOf"); +// assertThat(findings).isEmpty(); +// } +// +// @Test public void variableArity() { +// Collection<String> findings = StmtCfgTest.findings("NullableVariableArity"); +// assertThat(findings).isEmpty(); +// } +// +// @Test public void nullableDereference01() { +// Collection<String> findings = StmtCfgTest.findings("NullableDereference01"); +// assertThat(findings).containsExactly( +// "../simplecfg/testdata/NullableDereference01.javax:27:12: Dereferencing p, which was declared @Nullable.", +// "../simplecfg/testdata/NullableDereference01.javax:31:12: Dereferencing p, which was declared @Nullable." +// ); +// } +// +// /** Test false positive for GitHub issue #10. */ +// @Test public void issue10() { +// Collection<String> findings = StmtCfgTest.findings("NullableDereferenceIssue10"); +// assertThat(findings).containsExactly( +// "../simplecfg/testdata/NullableDereferenceIssue10.javax:34:31: Dereferencing y, which was declared @Nullable." +// ); +// } +// +// @Test public void issue11() { +// Collection<String> findings = StmtCfgTest.findings("NullableDereferenceIssue11"); +// assertThat(findings).isEmpty(); +// } +// +// @Test public void issue12() { +// Collection<String> findings = StmtCfgTest.findings("NullableDereferenceIssue12"); +// assertThat(findings).isEmpty(); +// } +// +// @Test public void eqExpr() { +// Collection<String> findings = StmtCfgTest.findings("NullableDereferenceEqExpr"); +// assertThat(findings).isEmpty(); +// } +// +// @Test public void neExpr() { +// Collection<String> findings = StmtCfgTest.findings("NullableDereferenceNeExpr"); +// assertThat(findings).isEmpty(); +// } +// +// @Test public void methodCall() { +// Collection<String> findings = StmtCfgTest.findings("NullableDereferenceMethodCall"); +// assertThat(findings).containsExactly( +// "../simplecfg/testdata/NullableDereferenceMethodCall.javax:41:16: " +// + "Dereferencing p, which was declared @Nullable."); +// } +// +// @Test public void issue13() { +// Collection<String> findings = StmtCfgTest.findings("NullableDereferenceIssue13"); +// assertThat(findings).containsExactly( +// "../simplecfg/testdata/NullableDereferenceIssue13.javax:33:7: " +// + "Dereferencing obj, which was declared @Nullable."); +// } } -- GitLab