Coverage Report - org.codenarc.rule.concurrency.DoubleCheckedLockingRule
 
Classes in this File Line Coverage Branch Coverage Complexity
DoubleCheckedLockingAstVisitor
94%
29/31
78%
42/54
0
DoubleCheckedLockingRule
100%
1/1
N/A
0
 
 1  
 /*
 2  
  * Copyright 2011 the original author or authors.
 3  
  * 
 4  
  * Licensed under the Apache License, Version 2.0 (the "License");
 5  
  * you may not use this file except in compliance with the License.
 6  
  * You may obtain a copy of the License at
 7  
  * 
 8  
  *      http://www.apache.org/licenses/LICENSE-2.0
 9  
  * 
 10  
  * Unless required by applicable law or agreed to in writing, software
 11  
  * distributed under the License is distributed on an "AS IS" BASIS,
 12  
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 13  
  * See the License for the specific language governing permissions and
 14  
  * limitations under the License.
 15  
  */
 16  
 package org.codenarc.rule.concurrency
 17  
 
 18  
 import org.codehaus.groovy.ast.ASTNode
 19  
 import org.codehaus.groovy.ast.expr.BooleanExpression
 20  
 import org.codehaus.groovy.ast.expr.NotExpression
 21  
 import org.codehaus.groovy.ast.expr.VariableExpression
 22  
 import org.codehaus.groovy.ast.stmt.BlockStatement
 23  
 import org.codehaus.groovy.ast.stmt.IfStatement
 24  
 import org.codehaus.groovy.ast.stmt.SynchronizedStatement
 25  
 import org.codenarc.rule.AbstractAstVisitor
 26  
 import org.codenarc.rule.AbstractAstVisitorRule
 27  
 import org.codenarc.util.AstUtil
 28  
 
 29  
 /**
 30  
  * This rule detects double checked locking, where a 'lock hint' is tested for null before initializing an object within a synchronized block. Double checked locking does not guarantee correctness and is an anti-pattern. 
 31  
  *
 32  
  * @author Hamlet D'Arcy
 33  
  */
 34  16
 class DoubleCheckedLockingRule extends AbstractAstVisitorRule {
 35  
     String name = 'DoubleCheckedLocking'
 36  
     int priority = 2
 37  
     Class astVisitorClass = DoubleCheckedLockingAstVisitor
 38  
 }
 39  
 
 40  91
 class DoubleCheckedLockingAstVisitor extends AbstractAstVisitor {
 41  
     @Override
 42  
     void visitIfElse(IfStatement node) {
 43  
 
 44  138
         addViolationOnDoubleLocking(node)
 45  138
         super.visitIfElse(node)
 46  
     }
 47  
 
 48  
     private addViolationOnDoubleLocking(IfStatement node) {
 49  138
         if (!AstUtil.expressionIsNullCheck(node)) {
 50  123
             return
 51  
         }
 52  15
         SynchronizedStatement syncStatement = getSynchronizedStatement(node.ifBlock)
 53  15
         if (!syncStatement) {
 54  10
             return
 55  
         }
 56  5
         if (!AstUtil.isOneLiner(syncStatement.code)) {
 57  1
             return
 58  
         }
 59  4
         def synchContents = syncStatement.code.statements[0]
 60  4
         if (AstUtil.expressionIsNullCheck(synchContents)) {
 61  4
             def varName1 = getNullCheckVariableName(node.booleanExpression)
 62  4
             def varName2 = getNullCheckVariableName(synchContents.booleanExpression)
 63  4
             if (varName1 == varName2) {
 64  4
                 if (AstUtil.isOneLiner(synchContents.ifBlock) && AstUtil.expressionIsAssignment(synchContents.ifBlock.statements[0], varName2)) {
 65  3
                     addViolation(synchContents.ifBlock.statements[0], "Double checked locking detected for variable ${varName1}. replace with more robust lazy initialization")
 66  
                 }
 67  
             }
 68  
         }
 69  
     }
 70  
 
 71  
     private static SynchronizedStatement getSynchronizedStatement(ASTNode statement) {
 72  15
         if (statement instanceof BlockStatement && statement.statements?.size() == 1) {
 73  13
             if (statement.statements[0] instanceof SynchronizedStatement) {
 74  5
                 return statement.statements[0]
 75  
             }
 76  
         }
 77  10
         null
 78  
     }
 79  
 
 80  
 
 81  
     private static String getNullCheckVariableName(ASTNode node) {
 82  8
         if (!(node instanceof BooleanExpression)) {
 83  0
             return null
 84  
         }
 85  8
         if (AstUtil.isBinaryExpressionType(node.expression, '==')) {
 86  6
             if (AstUtil.isNull(node.expression.leftExpression) && node.expression.rightExpression instanceof VariableExpression) {
 87  2
                 return node.expression.rightExpression.variable
 88  4
             } else if (AstUtil.isNull(node.expression.rightExpression) && node.expression.leftExpression instanceof VariableExpression) {
 89  4
                 return node.expression.leftExpression.variable
 90  
             }
 91  2
         } else if (node.expression instanceof NotExpression && node.expression.expression instanceof VariableExpression) {
 92  2
             return node.expression.expression.variable
 93  
         }
 94  0
         null
 95  
     }
 96  
 
 97  
 }