很难为已经过了几十年的语言开发了一个新的静态分析工具 - 如果您的工具标志代码已达到十几年的工具标记代码,请在您的用户思想中脱颖而出。
一些相对较新的静态分析工具通过权威的一种论证来克服了这一点。 TypeScript的作者可以说“我设计了着名的编程语言,当我说你的代码写得不好时相信我”。
然而,我设计了零编程语言,因此在构建PHP的静态分析工具时,我必须更容易。
诗篇有一个特征I' m调用流动敏感的条件分析。我不相信任何类似的工具雇用它。自写这个Paul Khuong已经指着键入的球拍作为现代系统,也可以执行此分析,其中它更适当地命名为键入。
本文将展示PSALM的分析如何帮助我们避免在此代码片段上进行假阳性:
<?phpclass a {public function isvalid():bool {return(bool)rand(0,1); }公共功能索赔():void {}}函数需要(?$ a):void {$ valid_a = $ a&&& $ a-> isvalid(); if($ vigw_a){$ a-> soomeAction(); }}
具有PHP或JavaScript的动态类型语言的任何类型推断的静态分析工具需要执行流动敏感的类型分析,这意味着该工具了解如何在给定该函数的语句和表达式的函数中更改变量的数据类型。
在分析给定的一组陈述期间的各个点,此类工具会以相应的类型保留变量名称的地图,更新其遍历代码' s抽象语法树。
这允许这些工具推断执行此代码永远不会导致NULL参考:
<?phpfunction getstringordfault(?string $ a):string {if($ a === null){return'默认&#39 ;; }返回$ a;}
PSALM看到条件表达,并产生由一系列条款组成的公式(以连续正常形式)。如果有条件为$ a === null转换为公式($ a为null)。该配方代表了我们所知道的所有事情在IF块内。
从此公式中,我们生成可以恢复到当前定义的类型的断言列表。为此,我们在我们的公式中查找只包含单个变量的公式中的所有条款。在这种情况下,那是我们之前找到的($ a为null)断言的子句。我们提取了该子句,然后使用A,String |使用现有已知类型将其与NULL进行重新调整,导致我们将$ a的类型设置为null。
PSALM看到IF块始终返回,只要我们生成的公式才能评估为true,因此在if块ksalm之后知道公式必须评估为false。因此,在IF块之后,对公式的否定是真实的。
如在步骤2中,它寻找包含单个变量的子句,该变量可以转换为类型分配。在这里,我们使用($ a是!null)子句,并使用a为$ a与字符串类型进行调和它,以向我们提供类型字符串。
虽然流动敏感类型分析保持变量和各自的类型的地图,但流动敏感的条件分析添加了一个条件列表,它知道在代码中的每个点处都是如此。
让我们向上面的功能介绍一些额外的复杂性,看看流动敏感的条件分析是有用的:
<?phpfunction getstringsordfault(?string $ a,?string $ b):string {if($ a === null& $ b === null){return"默认&#34 ;; }如果($ a!== null){返回$ a; }返回$ b; }
诗篇分析了第一个条件$ a === null&& $ b === null并生成公式($ a为null)&& ($ b为null)。
然后,诗篇寻找包含单个变量断言的子句,看到两个 - ($ a为null),并且($ b为null)。诗篇尽职尽责地将$ a和$ b分配到IF条件内(尽管从未使用过这些分配)。
由于IF块总是在输入时返回,因此如果条件必须是假的,所以我们计算否定!(($ a为null)&&($ b为null))获得($ a是!null || $ b是!null)。这是一个单个条款,但由于它包含两个变量,我们无法立即推断任何类型。在此点工具,只需执行熄灭公式的流动敏感类型分析丢弃,因为公式无法直接转换为类型分配。但诗篇保留了此信息,存储为它所知的条件集。
现在它遇到了一个新的如果条件$ a!== null。它派生公式($ a是!null)和if块内部,它段落地确定$ a有the type字符串
在第二秒之后,如果条件诗篇,则知道($ a为null)。它将该公式结合在前生成的公式($ a是!null || $ b是!null)。 ($ a为null)&& ($ a是!null || $ b是!null)简化($ b是!null)。这为我们提供了一个单个变量的子句,该变量允许我们确定$ b具有类型字符串。刚执行流动敏感类型分析的工具在此发出虚假正面警告。
知道有点关于流动敏感的条件分析,让我们重新审视第一例:
<?phpclass a {public function isvalid():bool {return(bool)rand(0,1); }公共功能索赔():void {}}函数需要(?$ a):void {$ valid_a = $ a&&& $ a-> isvalid(); if($ vigw_a){$ a-> soomeAction(); }}
每当诗篇遇到右侧是和/或操作的作业时:
它将新条件介绍到其确实如此的条件列表:
在普通的英语中,“它是$ valid_a是falsy的案例,或者是$ a不是伪造的案例”。
如果条件Psalm衍生公式($ valid_a是!falsy)。现在诗篇知道如果块内部的公式持有:
($ valid_a是!falsy)&& ($ vigw_a是falsy || $ a是!falsy)&& ($ vapbal_a是falsy ||<额外的东西>) 这告诉PSALM在IF病情内部不是伪造的,这允许工具避免其他工具未命中的假阳性。 执行流动敏感的条件分析增加了PSALM整体运行时的小(约6%)的平均增加。 有许多病理病例可以创造非常大的公式(诗篇在20,000条术语中最大化,然后在试图简化公式之前)。 <?phpfunction checkvalue(string $ a):void {if($ a!=="你好"){if($ a ==="你好"){//一些 代码 } }} 最后,在那里维护一个没有似乎存在于PSALM之外的系统的成本。 我希望这篇文章可能会改变!