View Javadoc
1   /**
2    * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
3    */
4   package net.sourceforge.pmd.lang.vm.rule.basic;
5   
6   import java.util.HashSet;
7   import java.util.List;
8   import java.util.Set;
9   
10  import net.sourceforge.pmd.lang.vm.ast.ASTBlock;
11  import net.sourceforge.pmd.lang.vm.ast.ASTDirective;
12  import net.sourceforge.pmd.lang.vm.ast.ASTReference;
13  import net.sourceforge.pmd.lang.vm.ast.ASTStringLiteral;
14  import net.sourceforge.pmd.lang.vm.rule.AbstractVmRule;
15  
16  public class UnusedMacroParameterRule extends AbstractVmRule {
17  
18      @Override
19      public Object visit(final ASTDirective node, final Object data) {
20          if ("macro".equals(node.getDirectiveName())) {
21              final Set<String> paramNames = new HashSet<>();
22              final List<ASTReference> params = node.findChildrenOfType(ASTReference.class);
23              for (final ASTReference param : params) {
24                  paramNames.add(param.literal());
25              }
26              final ASTBlock macroBlock = node.getFirstChildOfType(ASTBlock.class);
27              if (macroBlock != null) {
28                  for (final ASTReference referenceInMacro : macroBlock.findDescendantsOfType(ASTReference.class)) {
29                      checkForParameter(paramNames, referenceInMacro.literal());
30                  }
31                  for (final ASTStringLiteral literalInMacro : macroBlock.findDescendantsOfType(ASTStringLiteral.class)) {
32                      final String text = literalInMacro.literal();
33                      checkForParameter(paramNames, text);
34                  }
35              }
36              if (!paramNames.isEmpty()) {
37                  addViolation(data, node, paramNames.toString());
38              }
39          }
40          return super.visit(node, data);
41      }
42  
43      private void checkForParameter(final Set<String> paramNames, final String nameToSearch) {
44          final Set<String> paramsContained = new HashSet<>();
45          for (final String param : paramNames) {
46              if (containsAny(nameToSearch, formatNameVariations(param))) {
47                  paramsContained.add(param);
48              }
49          }
50          paramNames.removeAll(paramsContained);
51      }
52  
53      private boolean containsAny(final String text, final String[] formatNameVariations) {
54          for (final String formattedName : formatNameVariations) {
55              if (text.contains(formattedName)) {
56                  return true;
57              }
58          }
59          return false;
60      }
61  
62      private String[] formatNameVariations(final String param) {
63          final String actualName = param.substring(1);
64          return new String[] {param, "${" + actualName + "}", "${" + actualName + ".", "$!" + actualName,
65                  "$!{" + actualName + ".", "$!{" + actualName + "}"};
66      }
67  }