diff --git a/sonar-keyware-plugins-cxx/src/main/java/com/keyware/sonar/cxx/SubscriptionAstVisitor.java b/sonar-keyware-plugins-cxx/src/main/java/com/keyware/sonar/cxx/SubscriptionAstVisitor.java new file mode 100644 index 0000000..5a69849 --- /dev/null +++ b/sonar-keyware-plugins-cxx/src/main/java/com/keyware/sonar/cxx/SubscriptionAstVisitor.java @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2023 - 2024. KeyWare.Co.Ltd All rights reserved. + * 项目名称:C++ 信息安全性设计准则 + * 项目描述:用于检查C++源代码的安全性设计准则的Sonarqube插件 + * 版权说明:本软件属北京关键科技股份有限公司所有,在未获得北京关键科技股份有限公司正式授权情况下,任何企业和个人,不能获取、阅读、安装、传播本软件涉及的任何受知识产权保护的内容。 + */ +package com.keyware.sonar.cxx; + +import com.sonar.cxx.sslr.api.AstNode; +import com.sonar.cxx.sslr.api.AstNodeType; +import com.sonar.cxx.sslr.api.Grammar; +import org.sonar.cxx.squidbridge.checks.SquidCheck; + +import java.util.List; + +/** + * 可自由订阅的语法树节点访问器 + * + * @author GuoXin + * @date 2024/1/13 + */ +public abstract class SubscriptionAstVisitor extends SquidCheck { + /** + * 构造函数需要传入初代访问器 + * + * @param checker 初代规则检查器 + */ + public SubscriptionAstVisitor(SquidCheck checker) { + setContext(checker.getContext()); + visitNodeTypes().forEach(this::subscribeTo); + } + + /** + * 对指定的节点进行访问操作 + * + * @param node 需要访问的节点 + */ + public void accept(AstNode node) { + this.visit(node); + } + + /** + * 获取该访问器所需要访问的节点类型列表 + * + * @return 节点类型列表 + */ + public abstract List visitNodeTypes(); + + /** + * 对指定的节点进行访问 + * + * @param node 待访问的节点 + */ + private void visit(AstNode node) { + boolean isSubscribed = isSubscribed(node); + if (isSubscribed) { + visitNode(node); + } + visitChildren(node); + if (isSubscribed) { + leaveNode(node); + } + } + + /** + * 对指定节点的子节点进行访问 + * + * @param node 待访问的节点 + */ + private void visitChildren(AstNode node) { + if (node.hasChildren()) { + for (AstNode next : node.getChildren()) { + if (next != null) { + visit(next); + } + } + } + } + + /** + * 判断指定节点是否被订阅 + * + * @param tree 待判断的节点 + * @return true表示被订阅,false表示未订阅 + */ + private boolean isSubscribed(AstNode tree) { + return getAstNodeTypesToVisit().contains(tree.getType()); + } + + + /** + * 创建由给定 AST 节点导致的文件级问题 + * + * @param message 描述违规的消息,可以格式化(参见 java.text.MessageFormat) + * @param messageParameters 可选消息参数(请参阅 java.text.MessageFormat) + */ + protected void reportIssue(String message, Object... messageParameters) { + getContext().createFileViolation(this, message, messageParameters); + } + + /** + * 创建由给定 AST 节点导致的行级问题 + * + * @param node 导致问题的 AST 节点 + * @param message 描述违规的消息,可以格式化(参见 java.text.MessageFormat) + * @param messageParameters 可选消息参数(请参阅 java.text.MessageFormat) + */ + protected void reportIssue(AstNode node, String message, Object... messageParameters) { + getContext().createLineViolation(this, message, node, messageParameters); + } +} + diff --git a/sonar-keyware-plugins-cxx/src/main/java/com/keyware/sonar/cxx/rules/checkers/ABCVarNameChecker.java b/sonar-keyware-plugins-cxx/src/main/java/com/keyware/sonar/cxx/rules/checkers/ABCVarNameChecker.java index 20adf10..f591304 100644 --- a/sonar-keyware-plugins-cxx/src/main/java/com/keyware/sonar/cxx/rules/checkers/ABCVarNameChecker.java +++ b/sonar-keyware-plugins-cxx/src/main/java/com/keyware/sonar/cxx/rules/checkers/ABCVarNameChecker.java @@ -6,7 +6,9 @@ */ package com.keyware.sonar.cxx.rules.checkers; +import com.keyware.sonar.cxx.SubscriptionAstVisitor; import com.sonar.cxx.sslr.api.AstNode; +import com.sonar.cxx.sslr.api.AstNodeType; import com.sonar.cxx.sslr.api.Grammar; import org.sonar.check.Priority; import org.sonar.check.Rule; @@ -15,6 +17,9 @@ import org.sonar.cxx.squidbridge.annotations.ActivatedByDefault; import org.sonar.cxx.squidbridge.annotations.SqaleConstantRemediation; import org.sonar.cxx.squidbridge.checks.SquidCheck; +import javax.annotation.Nonnull; +import java.util.List; + import static com.sonar.cxx.sslr.api.GenericTokenType.IDENTIFIER; /** @@ -33,24 +38,47 @@ public class ABCVarNameChecker extends SquidCheck { */ @Override public void init() { - // 订阅要检查AST节点类型,用于在visitNode方法中检查该类型节点 + // 指定当前访问器需要访问的节点类型,functionDefinition(方法声明)节点类型 this.subscribeTo( - CxxGrammarImpl.declaratorId + CxxGrammarImpl.functionDefinition ); } /** - * 检查AST节点 + * 访问AST节点 * - * @param astNode 要处理的AST节点,该节点类型为通过subscribeTo方法订阅的类型 + * @param node 要处理的AST节点,该节点类型为通过subscribeTo方法订阅的类型 */ @Override - public void visitNode(AstNode astNode) { - var idNode = astNode.getLastChild(IDENTIFIER); - if (idNode != null) { - String identifier = idNode.getTokenValue(); - if ("ABC".equals(identifier)) { - getContext().createLineViolation(this, "变量名称不可以是ABC", idNode); + public void visitNode(@Nonnull AstNode node) { + var visitor = new SimpleDeclarationVisitor(this); + visitor.accept(node); + } + + /** + * 自定义一个AST节点访问器,这里用于访问simpleDeclaration(简单声明)节点 + */ + static class SimpleDeclarationVisitor extends SubscriptionAstVisitor { + + public SimpleDeclarationVisitor(SquidCheck checker) { + super(checker); + } + + @Override + public List visitNodeTypes() { + // 指定当前访问器需要访问的节点类型,这里指定了simpleDeclaration(简单声明)节点类型 + return List.of(CxxGrammarImpl.simpleDeclaration); + } + + @Override + public void visitNode(AstNode node) { + // 获取第一个后代 + var idNode = node.getFirstDescendant(IDENTIFIER); + if (idNode != null) { + String identifier = idNode.getTokenValue(); + if ("ABC".equals(identifier)) { + reportIssue(idNode, "变量名称不可以是ABC"); + } } } }