KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > modules > ruby > SemanticAnalysis


1 /*
2  * The contents of this file are subject to the terms of the Common Development
3  * and Distribution License (the License). You may not use this file except in
4  * compliance with the License.
5  *
6  * You can obtain a copy of the License at http://www.netbeans.org/cddl.html
7  * or http://www.netbeans.org/cddl.txt.
8  *
9  * When distributing Covered Code, include this CDDL Header Notice in each file
10  * and include the License file at http://www.netbeans.org/cddl.txt.
11  * If applicable, add the following below the CDDL Header, with the fields
12  * enclosed by brackets [] replaced by your own identifying information:
13  * "Portions Copyrighted [year] [name of copyright owner]"
14  *
15  * The Original Software is NetBeans. The Initial Developer of the Original
16  * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
17  * Microsystems, Inc. All Rights Reserved.
18  */

19 package org.netbeans.modules.ruby;
20
21 import java.util.ArrayList JavaDoc;
22 import java.util.HashMap JavaDoc;
23 import java.util.List JavaDoc;
24 import java.util.Map JavaDoc;
25
26 import org.jruby.ast.AliasNode;
27 import org.jruby.ast.ArgsNode;
28 import org.jruby.ast.ArgumentNode;
29 import org.jruby.ast.DAsgnNode;
30 import org.jruby.ast.DVarNode;
31 import org.jruby.ast.DefnNode;
32 import org.jruby.ast.DefsNode;
33 import org.jruby.ast.FCallNode;
34 import org.jruby.ast.ListNode;
35 import org.jruby.ast.LocalAsgnNode;
36 import org.jruby.ast.LocalVarNode;
37 import org.jruby.ast.Node;
38 import org.jruby.ast.VCallNode;
39 import org.netbeans.api.gsf.ColoringAttributes;
40 import org.netbeans.api.gsf.CompilationInfo;
41 import org.netbeans.api.gsf.OffsetRange;
42 import org.netbeans.api.gsf.SemanticAnalyzer;
43
44
45 /**
46  * Walk through the JRuby AST and note interesting things
47  * @todo Use the org.jruby.ast.visitor.NodeVisitor interface
48  * @todo Do mixins and includes trip up my unused private method detection code?
49  * @todo Treat toplevel methods as private?
50  * @todo Show unused highlighting for unused class variables:
51  * private_class_method
52  * See section 7.8 in http://www.rubycentral.com/faq/rubyfaq-7.html
53  * @author Tor Norbye
54  */

55 public class SemanticAnalysis implements SemanticAnalyzer {
56     private boolean cancelled;
57     private Map JavaDoc<OffsetRange, ColoringAttributes> semanticHighlights;
58
59     public SemanticAnalysis() {
60     }
61
62     public Map JavaDoc<OffsetRange, ColoringAttributes> getHighlights() {
63         return semanticHighlights;
64     }
65
66     protected final synchronized boolean isCancelled() {
67         return cancelled;
68     }
69
70     protected final synchronized void resume() {
71         cancelled = false;
72     }
73
74     public final synchronized void cancel() {
75         cancelled = true;
76     }
77
78     public void run(CompilationInfo info) {
79         resume();
80
81         if (isCancelled()) {
82             return;
83         }
84
85         Node root = AstUtilities.getRoot(info);
86
87         if (root == null) {
88             return;
89         }
90
91         Map JavaDoc<OffsetRange, ColoringAttributes> highlights =
92             new HashMap JavaDoc<OffsetRange, ColoringAttributes>(100);
93
94         AstPath path = new AstPath();
95         path.descend(root);
96         annotate(root, highlights, path, null);
97         path.ascend();
98
99         if (isCancelled()) {
100             return;
101         }
102
103         if (highlights.size() > 0) {
104             this.semanticHighlights = highlights;
105         } else {
106             this.semanticHighlights = null;
107         }
108     }
109
110     /** Find unused local and dynamic variables */
111     @SuppressWarnings JavaDoc("unchecked")
112     private void annotate(Node node, Map JavaDoc<OffsetRange, ColoringAttributes> highlights, AstPath path,
113         List JavaDoc<String JavaDoc> parameters) {
114         // Silly hack for now
115
//if (node instanceof DAsgnNode) {
116
if (node instanceof LocalAsgnNode) {
117             LocalAsgnNode lasgn = (LocalAsgnNode)node;
118             Node method = AstUtilities.findLocalScope(node, path);
119
120             boolean isUsed = isUsedInMethod(method, lasgn.getName());
121
122             if (!isUsed) {
123                 OffsetRange range = AstUtilities.getLValueRange(lasgn);
124                 highlights.put(range, ColoringAttributes.UNUSED);
125             } else if (parameters != null) {
126                 String JavaDoc name = ((LocalAsgnNode)node).getName();
127
128                 if (parameters.contains(name)) {
129                     OffsetRange range = AstUtilities.getRange(node);
130                     // Adjust end offset to only include the left hand size
131
range = new OffsetRange(range.getStart(), range.getStart() + name.length());
132                     highlights.put(range, ColoringAttributes.PARAMETER);
133                 }
134             }
135         } else if (node instanceof DAsgnNode) {
136             DAsgnNode dasgn = (DAsgnNode)node;
137
138             Node method = AstUtilities.findLocalScope(node, path);
139
140             boolean isUsed = isUsedInMethod(method, dasgn.getName());
141
142             if (!isUsed) {
143                 OffsetRange range = AstUtilities.getLValueRange(dasgn);
144                 highlights.put(range, ColoringAttributes.UNUSED);
145             }
146         } else if (node instanceof DefnNode || node instanceof DefsNode) {
147             parameters = AstUtilities.getDefArgs(node);
148
149             if ((parameters != null) && (parameters.size() > 0)) {
150                 List JavaDoc<String JavaDoc> unused = new ArrayList JavaDoc();
151
152                 for (String JavaDoc parameter : parameters) {
153                     boolean isUsed = isUsedInMethod(node, parameter);
154
155                     if (!isUsed) {
156                         unused.add(parameter);
157                     }
158                 }
159
160                 if (unused.size() > 0) {
161                     annotateUnusedParameters(node, highlights, unused);
162                     parameters.removeAll(unused);
163                 }
164
165                 if (parameters != null) {
166                     if (parameters.size() == 0) {
167                         parameters = null;
168                     } else {
169                         annotateParameters(node, highlights, parameters);
170                     }
171                 }
172             }
173
174             highlightMethodName(node, highlights);
175         } else if (node instanceof LocalVarNode) {
176             if (parameters != null) {
177                 if (parameters.contains(((LocalVarNode)node).getName())) {
178                     OffsetRange range = AstUtilities.getRange(node);
179                     highlights.put(range, ColoringAttributes.PARAMETER);
180                 }
181             }
182         } else if (node instanceof FCallNode ||
183                 node instanceof VCallNode /*|| node instanceof CallNode*/) {
184             // CallNode seems overly aggressive - it will show all operators for example
185
OffsetRange range = AstUtilities.getCallRange(node);
186             highlights.put(range, ColoringAttributes.METHOD);
187         }
188
189         List JavaDoc<Node> list = node.childNodes();
190
191         for (Node child : list) {
192             path.descend(child);
193             annotate(child, highlights, path, parameters);
194             path.ascend();
195         }
196     }
197
198     @SuppressWarnings JavaDoc("unchecked")
199     private void annotateParameters(Node node, Map JavaDoc<OffsetRange, ColoringAttributes> highlights,
200         List JavaDoc<String JavaDoc> usedParameterNames) {
201         List JavaDoc<Node> nodes = (List JavaDoc<Node>)node.childNodes();
202
203         for (Node c : nodes) {
204             if (c instanceof ArgsNode) {
205                 ArgsNode an = (ArgsNode)c;
206
207                 if (an.getArgsCount() > 0) {
208                     List JavaDoc<Node> args = (List JavaDoc<Node>)an.childNodes();
209
210                     for (Node arg : args) {
211                         if (arg instanceof ListNode) {
212                             List JavaDoc<Node> args2 = (List JavaDoc<Node>)arg.childNodes();
213
214                             for (Node arg2 : args2) {
215                                 if (arg2 instanceof ArgumentNode) {
216                                     if (usedParameterNames.contains(((ArgumentNode)arg2).getName())) {
217                                         OffsetRange range = AstUtilities.getRange(arg2);
218                                         highlights.put(range, ColoringAttributes.PARAMETER);
219                                     }
220                                 } else if (arg2 instanceof LocalAsgnNode) {
221                                     if (usedParameterNames.contains(((LocalAsgnNode)arg2).getName())) {
222                                         OffsetRange range = AstUtilities.getRange(arg2);
223                                         highlights.put(range, ColoringAttributes.PARAMETER);
224                                     }
225                                 }
226                             }
227                         }
228                     }
229                 }
230             }
231         }
232     }
233
234     @SuppressWarnings JavaDoc("unchecked")
235     private void annotateUnusedParameters(Node node,
236         Map JavaDoc<OffsetRange, ColoringAttributes> highlights, List JavaDoc<String JavaDoc> names) {
237         List JavaDoc<Node> nodes = (List JavaDoc<Node>)node.childNodes();
238
239         for (Node c : nodes) {
240             if (c instanceof ArgsNode) {
241                 ArgsNode an = (ArgsNode)c;
242
243                 if (an.getArgsCount() > 0) {
244                     List JavaDoc<Node> args = (List JavaDoc<Node>)an.childNodes();
245
246                     for (Node arg : args) {
247                         if (arg instanceof ListNode) {
248                             List JavaDoc<Node> args2 = (List JavaDoc<Node>)arg.childNodes();
249
250                             for (Node arg2 : args2) {
251                                 if (arg2 instanceof ArgumentNode) {
252                                     if (names.contains(((ArgumentNode)arg2).getName())) {
253                                         OffsetRange range = AstUtilities.getRange(arg2);
254                                         highlights.put(range, ColoringAttributes.UNUSED);
255                                     }
256                                 } else if (arg2 instanceof LocalAsgnNode) {
257                                     if (names.contains(((LocalAsgnNode)arg2).getName())) {
258                                         OffsetRange range = AstUtilities.getRange(arg2);
259                                         highlights.put(range, ColoringAttributes.UNUSED);
260                                     }
261                                 }
262                             }
263                         }
264                     }
265                 }
266             }
267         }
268     }
269
270     @SuppressWarnings JavaDoc("unchecked")
271     private boolean isUsedInMethod(Node node, String JavaDoc targetName) {
272         if (node instanceof LocalVarNode) {
273             String JavaDoc name = ((LocalVarNode)node).getName();
274
275             if (targetName.equals(name)) {
276                 return true;
277             }
278         } else if (node instanceof DVarNode) {
279             if (targetName.equals(((DVarNode)node).getName())) {
280                 return true;
281             }
282         } else if (node instanceof AliasNode) {
283             AliasNode an = (AliasNode)node;
284
285             if (an.getOldName().equals(targetName)) {
286                 return true;
287             }
288         }
289
290         List JavaDoc<Node> list = node.childNodes();
291
292         for (Node child : list) {
293             // The "outer" foo here is unused - we shouldn't
294
// recurse into method bodies when doing unused detection
295
// foo = 1; def bar; foo = 2; print foo; end;
296
if (child instanceof DefnNode || child instanceof DefsNode) {
297                 continue;
298             }
299
300             boolean used = isUsedInMethod(child, targetName);
301
302             if (used) {
303                 return true;
304             }
305         }
306
307         return false;
308     }
309
310     @SuppressWarnings JavaDoc("unchecked")
311     private void highlightMethodName(Node node, Map JavaDoc<OffsetRange, ColoringAttributes> highlights) {
312         OffsetRange range = AstUtilities.getFunctionNameRange(node);
313
314         if (range != OffsetRange.NONE) {
315             if (!highlights.containsKey(range)) { // Don't block out already annotated private methods
316
highlights.put(range, ColoringAttributes.METHOD);
317             }
318         }
319     }
320 }
321
Popular Tags