View Javadoc
1   /**
2    * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
3    */
4   package net.sourceforge.pmd.lang.java.dfa;
5   
6   import java.util.ArrayList;
7   import java.util.HashSet;
8   import java.util.List;
9   import java.util.Map;
10  import java.util.Set;
11  
12  import net.sourceforge.pmd.lang.ast.Node;
13  import net.sourceforge.pmd.lang.dfa.DataFlowNode;
14  import net.sourceforge.pmd.lang.dfa.StartOrEndDataFlowNode;
15  import net.sourceforge.pmd.lang.dfa.VariableAccess;
16  import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceBodyDeclaration;
17  import net.sourceforge.pmd.lang.java.ast.ASTConstructorDeclaration;
18  import net.sourceforge.pmd.lang.java.ast.ASTFormalParameter;
19  import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration;
20  import net.sourceforge.pmd.lang.java.ast.ASTVariableInitializer;
21  import net.sourceforge.pmd.lang.java.ast.JavaNode;
22  import net.sourceforge.pmd.lang.java.ast.JavaParserVisitorAdapter;
23  import net.sourceforge.pmd.lang.java.symboltable.JavaNameOccurrence;
24  import net.sourceforge.pmd.lang.java.symboltable.VariableNameDeclaration;
25  import net.sourceforge.pmd.lang.symboltable.NameOccurrence;
26  
27  /**
28   *         Searches for special nodes and computes based on the sequence, the type of
29   *         access of a variable.
30   * @since Created on 14.07.2004
31   * @author raik, Sven Jacob
32   */
33  public class VariableAccessVisitor extends JavaParserVisitorAdapter {
34  
35      public void compute(ASTMethodDeclaration node) {
36  	if (node.jjtGetParent() instanceof ASTClassOrInterfaceBodyDeclaration) {
37  	    this.computeNow(node);
38  	}
39      }
40  
41      public void compute(ASTConstructorDeclaration node) {
42  	this.computeNow(node);
43      }
44  
45      private void computeNow(Node node) {
46  	DataFlowNode inode = node.getDataFlowNode();
47  
48  	List<VariableAccess> undefinitions = markUsages(inode);
49  
50  	// all variables are first in state undefinition
51  	DataFlowNode firstINode = inode.getFlow().get(0);
52  	firstINode.setVariableAccess(undefinitions);
53  
54  	// all variables are getting undefined when leaving scope
55  	DataFlowNode lastINode = inode.getFlow().get(inode.getFlow().size() - 1);
56  	lastINode.setVariableAccess(undefinitions);
57      }
58  
59      private List<VariableAccess> markUsages(DataFlowNode inode) {
60  	// undefinitions was once a field... seems like it works fine as a local
61  	List<VariableAccess> undefinitions = new ArrayList<>();
62  	Set<Map<VariableNameDeclaration, List<NameOccurrence>>> variableDeclarations = collectDeclarations(inode);
63  	for (Map<VariableNameDeclaration, List<NameOccurrence>> declarations : variableDeclarations) {
64  	    for (Map.Entry<VariableNameDeclaration, List<NameOccurrence>> entry : declarations.entrySet()) {
65          VariableNameDeclaration vnd = entry.getKey();
66  
67  		if (vnd.getAccessNodeParent() instanceof ASTFormalParameter) {
68  		    // no definition/undefinition/references for parameters
69  		    continue;
70  		} else if (vnd.getAccessNodeParent().getFirstDescendantOfType(ASTVariableInitializer.class) != null) {
71  		    // add definition for initialized variables
72  		    addVariableAccess(vnd.getNode(), new VariableAccess(VariableAccess.DEFINITION, vnd.getImage()),
73  			    inode.getFlow());
74  		}
75  		undefinitions.add(new VariableAccess(VariableAccess.UNDEFINITION, vnd.getImage()));
76  
77  		for (NameOccurrence occurrence : entry.getValue()) {
78  		    addAccess((JavaNameOccurrence)occurrence, inode);
79  		}
80  	    }
81  	}
82  	return undefinitions;
83      }
84  
85      private Set<Map<VariableNameDeclaration, List<NameOccurrence>>> collectDeclarations(DataFlowNode inode) {
86  	Set<Map<VariableNameDeclaration, List<NameOccurrence>>> decls = new HashSet<>();
87  	Map<VariableNameDeclaration, List<NameOccurrence>> varDecls;
88  	for (int i = 0; i < inode.getFlow().size(); i++) {
89  	    DataFlowNode n = inode.getFlow().get(i);
90  	    if (n instanceof StartOrEndDataFlowNode) {
91  		continue;
92  	    }
93  	    varDecls = ((JavaNode)n.getNode()).getScope().getDeclarations(VariableNameDeclaration.class);
94  	    if (!decls.contains(varDecls)) {
95  		decls.add(varDecls);
96  	    }
97  	}
98  	return decls;
99      }
100 
101     private void addAccess(JavaNameOccurrence occurrence, DataFlowNode inode) {
102 	if (occurrence.isOnLeftHandSide()) {
103 	    this.addVariableAccess(occurrence.getLocation(), new VariableAccess(VariableAccess.DEFINITION, occurrence
104 		    .getImage()), inode.getFlow());
105 	} else if (occurrence.isOnRightHandSide() || !occurrence.isOnLeftHandSide() && !occurrence.isOnRightHandSide()) {
106 	    this.addVariableAccess(occurrence.getLocation(), new VariableAccess(VariableAccess.REFERENCING, occurrence
107 		    .getImage()), inode.getFlow());
108 	}
109     }
110 
111     /**
112      * Adds a VariableAccess to a dataflow node.
113      * @param node location of the access of a variable
114      * @param va variable access to add
115      * @param flow dataflownodes that can contain the node.
116      */
117     private void addVariableAccess(Node node, VariableAccess va, List<DataFlowNode> flow) {
118 	// backwards to find the right inode (not a method declaration)
119 	for (int i = flow.size() - 1; i > 0; i--) {
120 	    DataFlowNode inode = flow.get(i);
121 	    if (inode.getNode() == null) {
122 		continue;
123 	    }
124 
125 	    List<? extends Node> children = inode.getNode().findDescendantsOfType(node.getClass());
126 	    for (Node n : children) {
127 		if (node.equals(n)) {
128 		    List<VariableAccess> v = new ArrayList<>();
129 		    v.add(va);
130 		    inode.setVariableAccess(v);
131 		    return;
132 		}
133 	    }
134 	}
135     }
136 
137 }