NEAL, Uber的开源语言无关检测平台

0
NEAL, Uber的开源语言无关检测平台

每天,Uber的工程师向我们的移动代码库提交数百份提交,每一个新补丁都需要进行彻底的代码审查,以发现可能影响用户体验的bug。

为了使代码评审更容易,我们构建了不完全是Linter (NEAL)这是一种开放源码的语言无关工具,它允许工程师编写自定义的基于语法的规则,从而实现代码审查过程的自动化。在本文中,我们将讨论NEAL的优点,并介绍如何在自己的代码评审中使用它。

引入尼尔

对于Uber移动代码库中更关键部分的审核人员来说,新差异的数量可能是压倒性的,这会降低吞吐量和开发人员的生产力。我们的地理多样性加剧了这些障碍;由于工程团队在全球12个地点,受阻的审查可能导致漫长的周转,使解决问题更加困难。

除了这些日常的考虑之外,还有很多设计模式是我们希望在整个代码库中强制执行或避免的。其中一些已经被现有的工具覆盖了,但许多其他的模式是Uber特有的肋骨架构.到2017年初,我们意识到我们需要投资一个更自动化的代码评审解决方案,它将消除与可靠和有效地大规模增长多语言代码库相关的障碍。

在试用了现有的开源工具之后,我们很快意识到大多数选项都不能满足我们的可扩展性和灵活性需求。虽然有些是为特定的语言构建的,但其他许多只允许定制正则表达式,或要求工程师在想创建新规则时编写程序。我们需要一种解决方案,既能让工程师快速检查代码- - - - - -无论语言- - - - - -并产生新的规则。所以,我们决定从零开始,建立我们自己的。

NEAL允许工程师编写他们自己的规则集来覆盖代码库的任何部分,使代码评审过程更接近于完全自动化。事实上,为了使这个过程更容易,NEAL使用了自己的方法领域特定语言

在Uber,我们广泛使用NEAL:

  • 通过检查模式(例如并发原语和异步测试)来提高测试的可靠性
  • 通过避免导致大量机器代码的语言特性来控制二进制文件大小的增长
  • 对可执行代码实施高级限制,比如哪些代码可以作为Uber核心流程的一部分执行,并确保新加入的插件可以安全关闭

最激动人心的部分是什么?多亏了NEAL,上面提到的规则是由工程师在我们的移动代码库中编写的。

它是如何工作的?

NEAL被设计成可以完全跨三个维度扩展:

  • 语言:抽象语法树(AST)提供程序将语言作为解析器或通过包装现有解析器添加支持。
  • 格式:记者可以轻松添加输出违规行为的新格式。
  • 规则创建:通过NEAL,规则定义了在代码评审期间遇到特定模式时要采取的操作。

我们决定使用基于ast的检测(而不是传统的基于表达式的检测),因为它有助于表达更复杂的模式,例如深度嵌套结构和递归模式。这种方法也不容易出错,因为用户不必解释源代码中无意义的文本,如空白、注释和项顺序(例如,公共静态的vs静态公共的).

默认情况下,NEAL附带了Swift和Python AST提供程序,命令行友好和-友好的格式,没有规则。然而,你可以在我们的GitHub页面

使用尼尔

NEAL并不附带任何规则,所以让我们看看工程师如何在应用程序的代码库中添加一个规则。

假设我们有一个函数,它执行一个非常昂贵的计算(让我们称之为expensiveComputation),我们要确保它不会从任何类的初始化调用,这将导致应用程序意外地失去响应。

一个违规的程序可能是这样的:

/ / test.swift
函数expensiveComputation / * * /

应用程序
初始化
expensiveComputation

首先,我们使用NEAL提取AST以确定在规则中使用哪些节点名:

尼尔——print-asttest.swift
FunctionDeclaration
FunctionName标识符价值 “expensiveComputation”

ClassDeclaration
类名称标识符价值 “应用程序”
ClassBody
InitializerDeclaration
InitializerBody
CallExpression
标识符价值 “expensiveComputation”
参数

(为简洁起见,AST在此缩写)

然后,我们编写一个规则来匹配这个例子,如下所示:

/ / test.rules

//首先我们要给这个规则起个名字
规则NoExpensiveInitializer

//然后我们必须指定我们的目标语言,其次是
//我们想要匹配的第一个节点,在本例中是ClassDeclaration
斯威夫特:: ClassDeclaration
//节点匹配器可以嵌套以深入AST
InitializerDeclaration
//此外,匹配器也可以有谓词
CallExpression,被= = “expensiveComputation”
//一旦我们找到我们想要的模式,我们就可以采取行动
失败 " ' expensivcomputing '不应从类初始化器调用"



现在,如果我们在这段代码上运行NEAL,我们应该会看到如下内容:

尼尔——规则test.rules test.swift
[1 / 1]:分析测试
在test.swift文件中:(NoExpensiveInitializer)

3 |类应用程序{
4 | init() {
5 | expensiveComputation ()
~ | ^
6 |}
7 |}

错误:' expensivcomputing '不应该从类初始化器调用

或者,我们可以创建一个最小配置文件来简化流程,如下所示:


“规则” “test.rules”

一旦发送,我们可以通过简单地运行来达到同样的结果:

尼尔。

由于neal强制的规则是基于语法的,它们只能匹配编写的内容,而不能匹配程序实际执行的内容。例如,在我们上面的例子中,NEAL只会在expensiveComputation直接从初始化器调用,但如果它被传递调用,即初始化器调用x而且x调用expensiveComputation.这可以通过约定和用户提供的注释来实现,但不支持开箱即用的功能。

下一个步骤

为了给其他人自由和可扩展性来设置他们自己的自动代码评审过程的参数,我们把NEAL还给了开源社区。

如果NEAL引起了您的兴趣,您可以访问我们的GitHub回购阅读我们的文档.请与Uber Engineering联系GitHub的问题如果您在开始时需要任何帮助,或者更好的是,希望为项目贡献其他语言!

图片来源,“长颈鹿在路上”,作者康纳·梅尔沃尔德,南卢安瓜国家公园赞比亚, 2009年。

评论