SonarLint

1,629 阅读5分钟

SonarLint介绍

关于SonarLint的介绍可以看一下这篇文章——Sonarlint插件详解

本文主要记录一些在开发中常见的SonarLint的issue。首先介绍一下issue的等级和状态。

SonarLint的Issue等级&状态
  • 等级
    • BLOCKER(致命):会影响应用程序的缺陷:内存泄漏,未关闭的JDBC连接…必须立刻修复的代码
    • CRITICAL(关键 ):可能会影响应用程序的缺陷或者是安全性缺陷:空的catch块,sql注入,…必须立刻查看代码
    • MAJOR(主要):可能会影响开发者效率的质量缺陷:未覆盖的代码,重复块,未使用的参数….
    • MINOR(微小):可能会影响开发者效率的质量缺陷:每行不能太长,“switch”语句应该至少有三个条件,….
    • INFO(未知):既不是缺陷也不是质量问题,只是一个发现。
  • 状态
    • Bug:窃听器(可靠性),避免错误和未定义的行为
    • Vulnerability:漏洞(安全),避免破坏或攻击入,…必须立刻查看代码
    • Code Smell:代码嗅觉(可维护性),简化代码更新,并提高开发人员的速度

工具介绍

开发工具: IDEA

扫描工具: IDEA的SonarLint插件

开发语言: Java

开发环境: JDK1.8

SonarLint相关Issue(持续更新...)

  • BLOCKER

  • CRITICAL

    1. Cognitive Complexity of methods should not be too high

      解释: 方法的认知复杂度不应该太高

      状态: Code Smell

      案例: 略(列出来了代码太多了)

      解读: 这个问题指的是一个方法体里面的逻辑太复杂了,你写的方法里每一个if,else,for都会统计一次,每个方法的统计值不能超过15

      解决方法:把里面判断或者循环进行抽象,变成一个独立方法再进行调用即可

    2. "String#replace" should be preferred to "String#replaceAll"

      解释: “String#replace”应该优先于“String#replaceAll”,字符串的replace()替换方法应该优先于replaceAll()方法

      状态: Code smell

      案例: 略

      解读: replaceAll()底层实现会调用匹配方法,判断第一个参数是不是正则表达式,如果不是,replace()和replaceAll()所做的事情完全相同

      解决方法: 将replaceAll()方法替换成为replace()

    3. String literals should not be duplicated

      解释: 字符串文字不应重复

      状态: Code smell

      案例:

      Map<String,Object> map = HashMap();
      if(0 == code){
          map.put("str",object1);
      } else if(1 == code){
          map.put("str",object2);
      } else {
          map.put("str",object3);
      }
      

      解读: 一个方法内,相同的字符串被重复使用而没有定义变量的时候,就会提示该问题,因为相同的字符串直接使用,没有定义变量的话,重构代码时候可能会出现更新遗漏

      解决方法: 将重复的字符串定义为一个变量

      Map<String,Object> map = HashMap<>();
      String key = "str";
      if(0 == code){
          map.put(key,object1);
      } else if(1 == code){
          map.put(key,object2);
      } else {
          map.put(key,object3);
      }
      
    4. "static" base class members should not be accessed via derived types

      解释: 不应通过派生类型访问“静态”基类成员

      状态: Code smell

      案例:

      PageInfo<updateRecord> pageInfo = PageHelper.startPage(request.getPageNum(), request.getPageSize()).doSelectPageInfo(() -> updateRecordMapper.selectByExample(updateRecordExample));
      

      解读: 这里的PageHelper是一个派生类,不应该通过这个类去访问基类(PageMethod)的方法

      解决方法: 使用基类访问基类的方法

      PageInfo<updateRecord> pageInfo = PageMethod.startPage(request.getPageNum(), request.getPageSize()).doSelectPageInfo(() -> updateRecordMapper.selectByExample(updateRecordExample));
      
  • MAJOR

    1. Null pointers should not be dereferenced

      解释: 被引用的值不应该为空

      状态: Bug

      案例:

      Map<String, Object> map = new Hash<>(8);
      Group ruleGroup = groupMapper.selectByPrimaryKey(groupId);
      map.put("groupId", groupId);
      map.put("groupName",[1]null != ruleGroup ? ruleGroup.getGroupName() : "");
      map.put("groupCreateTime", [2]ruleGroup.getCreateTime());
      

      解读: 这个Group对象是从数据库中查询出来的,可能为空

      解决方法: 可以先设置初始值,再进行判空赋值

      Map<String, Object> map = new Hash<>(8);
      Group ruleGroup = groupMapper.selectByPrimaryKey(groupId);
      String groupName = "";
      Date groupCreateTime = null;
      if (null != ruleGroup) {
          groupName = ruleGroup.getGroupName();
          groupCreateTime = ruleGroup.getCreateTime();
      }
      map.put("groupName", groupName);
      map.put("groupCreateTime", groupCreateTime);
      
    2. Assignments should not be made from within sub-expressions

      解释:不应从子表达式中进行赋值

      状态: Code smell

      案例:

      boolean flag;
      if ([1]flag = !this.formalCheckRequirement(totalValues, target)) {
          ...
      }
      

      解读: [1]处创建对象flag之后,在子表达式中赋值,导致提示该问题

      解决方法: 创建值的时候初始化,避免在子表达式中进行赋值

      boolean flag = !this.formalCheckRequirement(totalValues, target);
      if (flag) {
          ...
      }
      
    3. Unused "private" methods should be removed

      解读: 未使用的私有方法应该移除

      状态: Code smell

      案例: 略(这个没啥举例的,代码里面有没有用到的private修饰的方法删除即可)

      解读: 从不执行的私有方法是死代码,应该删除的不必要的、无效的代码

      解决方法: 将代码中没有使用到的private方法删除即可

    4. Sections of code should not be commented out

      解读: 不应注释掉代码段

      状态: Code smell

      案例: 略(这个没啥举例的,代码里面有注释的代码段检查是否会使用,不使用的话直接删除)

      解读: 不应该注释代码段,会是代码膨胀降低可读性。应该删除未使用的代码,有需要的话可以从源代码控制记录中检索

      解决方法: 将注释的代码段删除或者重新使用

    5. "java.nio.Files#delete" should be preferred

      解释:“java.nio.Files#delete”应该是首选(相比于File.delete())

      状态: Code smell

      案例:

      File resumeZipFile = File.createTempFile("prefix","suffix");
      [1]resumeZipFile.delete();
      

      解读: [1]处需要删除一个创建的临时文件,使用java.io.File#delete删除,但是不需要其他的操作,这个时候应该优先使用java.nio.Files#delete

      解决方法: 使用java.nio.Files.delete()

      File resumeZipFile = File.createTempFile("prefix","suffix");
      Files.delete(resumeZipFile.toPath());
      
    6. Collapsible "if" statements should be merged

      解释:应合并可折叠的“if”语句

      状态: Code smell

      案例:

      if(null != object) {
          if(1 == object.code) {
              ...
          }
      }
      

      解读: 可以折叠的if语句应该合并在一起,这样能够提高代码可读性

      解决方法:

      if(null != object && 1 == object.code) {
          ...
      }
      
    7. Synchronized classes Vector, Hashtable, Stack and StringBuffer should not be used

      解释:不应使用同步类 Vector、Hashtable、Stack 和 StringBuffer

      状态: Code smell

      案例:

      Stack<String> resultStack = new Stack<>();
      Vector<String> vector = new Vector<>();
      Hashtable<String,Object> hashtable = new Hashtable();
      StringBuffer stringBuilder = new StringBuffer();
      

      解读: 当你在代码中使用Stack这些同步类的时候就会提示该问题,因为使用同步类会导致性能下降

      解决方法: 可以使用相关的不同步类进行替换

      Deque<String> resultStack = new Deque<>();
      ArrayList<String> vector = new ArrayList<>();
      HashMap<String,Object> hashtable = new HashMap();
      StringBuilder stringBuilder = new StringBuilder();
      
    8. Methods should not have too many parameters

      解释:方法不应该有太多参数

      状态: Code smell

      案例:

      public void doSomething(int param1, int param2, int param3, String param4, long param5) {
      ...
      }
      

      解读: 一个方法不应该有太多的参数,如果必须要这么多参数的话,可以使用一个结构包装参数

      解决方法:

      public Class Param{
          int param1,
          int param2,
          int param3,
          int param4,
          int param5,
      }
      
      public void doSomething(Param param) {
      ...
      }
      
  • MINOR

    1. Math operands should be cast before assignment

      解释: 数学操作数应在赋值之前强制转换

      状态:Bug

      案例:

      int addNum = 5;
      LocalDate localStartDate = LocalDate.parse("2022-02-01");
      localStartDate.plusDays([1]addNum + 1);
      

      解读:localStartDate.plusDays()方法参数要求是long类型,而[1]处直接将表达式作为参数传递,可能会引起类型转换错误

      解决方法: 提前进行类型转换

       int addNum = 5;
       long addDayNum = (long) addNum + 1
      LocalDate localStartDate = LocalDate.parse("2022-02-01");
      localStartDate.plusDays(addDayNum);
      
    2. Boxed "Boolean" should be avoided in boolean expressions

      解释: 布尔表达式中应避免使用封装类型的“布尔”

      状态: Code smell

      案例:

      public Boolean judgment() {...}
      
      if([1]judgment()){...}
      

      解读: [1]处调用方法作为判断条件,获取到的返回值是Boolean,Boolean可能为null,有可能导致NPE异常

      解决方法: 使用equals进行比较,避免NEP异常

      if(Boolean.FALSE.equals(judgmentDate(startDate, endDate))){...}
      
    3. Collection.isEmpty() should be used to test for emptiness

      解释: 应该使用Collection.isEmpty()来判断是否为空

      状态: Code smell

      案例:

      List<String> list = new ArrayList<>(8);
      if([1]s1.size() != 0) {...}
      

      解读: [1]处应该使用Collection.isEmpty()来进行判空,不应该使用size != 0,Sonarlint给的解释是因为使用Collection.isEmpty()可读性更好和性能更高

      解决方法:

      List<String> list = new ArrayList<>(8);
      if(!s1.isEmpty) {...}
      
    4. Local variable and method parameter names should comply with a naming convention

      解释: 局部变量和方法参数名称应符合命名约定

      状态: Code smell

      案例: 略(这个就是命名不规范引起的,只要修改命名就行了)

      解读: 无

      解决方法: 按照规范命名

    5. Declarations should use Java collection interfaces such as "List" rather than specific implementation classes such as "LinkedList"

      解释: 声明应该使用诸如“List”之类的Java集合接口而不是诸如“LinkedList”之类的特定实现类

      状态: Code smell

      案例:

      public HashMap<String, String> get(){...}
      

      解读: 这个方法的返回类型是HashMap,是Map的实现类,应该使用Map

      解决方法:

      public Map<String, String> get(){...}
      
    6. Return of boolean expressions should not be wrapped into an "if-then-else" statement

      解释: 布尔表达式的返回不应包含在“if-then-else”语句中

      状态: Code smell

      案例:

      if (option.equals(TargetEnum.Option.LESS_EQUAL.getCode())) {
          return true;
      }
      

      解读: 判断表达式之后返回boolean值,有点类似于脱裤子放屁了,当然这里这是只是举例

      解决方法:

      return option.equals(TargetEnum.Option.LESS_EQUAL.getCode());
      
    7. Loops should not contain more than a single "break" or "continue" statement

      解释:循环不应包含多个“break”或“continue”语句

      状态: Code smell

      案例:

      for (int i = 1; i <= 10; i++) {     // Noncompliant - 2 continue - one might be tempted to add some logic in between
        if (i % 2 == 0) {
          continue;
        }
      
        if (i % 3 == 0) {
          continue;
        }
      
        System.out.println("i = " + i);
      }
      

      解读: 将多个continue合并为一处

      解决方法:

      for (int i = 1; i <= 10; i++) {     // Noncompliant - 2 continue - one might be tempted to add some logic in between
        if (i % 2 == 0 || i % 3 == 0) {
          continue;
        }
        
        System.out.println("i = " + i);
      }
      
    8. Lambdas should be replaced with method references

      解释:应将 Lambda 替换为方法引用

      状态: Code smell

      案例:

      List<GroupEmployee> groupEmployees = new ArrayList();
      employeeIds = groupEmployees.stream().map(groupEmployee -> groupEmployee.getCompanyEmployeeId()).collect(Collectors.toList());
      

      解读: 将多个continue合并为一处

      解决方法: 使用::(方法引用)替换->(Lambda)

      List<GroupEmployee> groupEmployees = new ArrayList();
      employeeIds = groupEmployees.stream().map(groupEmployee::groupEmployee.getCompanyEmployeeId()).collect(Collectors.toList());
      
  • INFO

    1. Track uses of "TODO" tags

      解释: 跟踪“TODO”标签的使用

      状态: Code smell

      案例:

      void doSomething{
          //TODO
      }
      

      解读: 当代码中有TODO标签的时候,就会有这个规则的出现,做一个提示作用

      解决方法: 实现相关代码,去掉TODO