【问题标题】:Delphi: how to automatically remove unused vars ("Variable 'x' is declared but never used" hint)Delphi:如何自动删除未使用的变量(“变量'x'已声明但从未使用”提示)
【发布时间】:2009-02-10 11:11:20
【问题描述】:

是否有任何工具(最好是免费软件)可以分析 Pascal/Delphi 语法并自动删除未使用的变量?

在我的例子中,我正在使用一个非常大的 Delphi 代码库,编译器提示报告了超过一千个“变量 'x' 已声明但从未使用”的情况。

我需要花费数小时才能手动删除它们,而且我可能会出错,但正确的工具应该能够安全且自动地执行此操作。

我在网上搜索了但没有找到...这里有人知道这样的工具吗?

谢谢...

马克·布拉福德

【问题讨论】:

    标签: delphi syntax


    【解决方案1】:

    我明白你的意思,并且完全同意这样的工具在处理遗留代码时会很有用。不幸的是,我不知道任何现有的工具(我应该在这里添加免费软件工具,静态分析工具当然应该能够轻松完成,但我不知道任何免费的静态代码分析工具)能够做到这一点.

    但我想你可以在几分钟内轻松编写出这样的工具。一个带有备忘录和按钮的小 GUI 就足够了。然后只需将编译器提示复制到备忘录中并按下按钮。然后该工具解析每一行。它可以很容易地检查该行是否包含您正在寻找的提示,并且每个这样的行具有相同的结构,因此解析应该相对容易。然后它可以提取文件名和行号,打开文件并删除变量声明。如果在一行中声明多个变量,这可能有点棘手,但我认为这是可行的。

    与自己删除所有变量声明的任务相比,我不知道这是否对您来说太过分了。但是我很想看到这样的工具,所以随意写吧:)

    希望至少有一点帮助。

    好吧,我真的看不出这里有什么问题。解析部分:

    function ParseHint (const HintText : String; out HintInfo : THintInfo) : Boolean;
    var
      I, J     : Integer;
      HintName : String;
    begin
      Result := False;
      for I := 1 to Length (HintText) do
      begin
        if (HintText [I] = '(') then
        begin
          J := I + 1;
          while (HintText [J] <> ')') do Inc (J);
          HintInfo.LineNumber := StrToInt (MidStr (HintText, I+1, J-(I+1)));
          HintInfo.SourceFile := MidStr (HintText, 12, I-12);
          HintName := MidStr (HintText, J+3, 5);
          if (HintName <> 'H2164') then Exit (False);
        end;
        if (HintText [I] = '''') then
        begin
          J := I + 1;
          while (HintText [J] <> '''') do Inc (J);
          HintInfo.VarName := MidStr (HintText, I+1, J-(I+1));
          Exit (True);
        end;
      end;
    end;
    

    嗯,读取源文件应该很容易,所以唯一剩下的部分就是从它的声明行中删除变量。我们可以简单地搜索该行中出现的 HintInfo.VarName 并检查出现前后的字符是否不是字母而只有''、'、'或':'。如果是这种情况,我们可以将其删除。这涵盖了所有这些情况:

    var UnusedVar : Integer;
    var
      UnusedVar,
      AnotherVar : Integer;
    var
      UnusedVar, AnotherVar : Integer;
    

    如果我错了或者我忘记了任何情况,请告诉我,但我认为这会起作用,并且会解决使用编译器生成的提示从 delphi 源文件中删除未使用变量的问题。

    【讨论】:

    • 几分钟将是一个很大的误解,因为这不是一个小问题。
    • 好吧,也许您可​​以指出您认为的问题是什么?明确一点:我不是在谈论查找未使用的变量,而是解释 Delphi 编译器提示。
    • 好的,解释提示应该没那么难。但这不是问题的主要部分。请看下面我的回答:
    • 您的方法很好,正如我所说,我很高兴看到这样的工具,但我不明白为什么我的解决方案不能解决“问题的主要部分”。为什么解析源代码而不使用编译器输出?它不是那么通用,但它更容易并且解决了手头的问题。
    • 只是解决了部分问题。另请参阅 gabr 对我的回答的评论。
    【解决方案2】:

    解决方案很简单,但需要这些时间来确保您不会犯错。首先,您可以使用 Alt-F8 一个接一个地浏览每个报告(而 Alt-F7 可以后退)。这使得定位它们变得非常容易。光标为您放在行上。然后只需按两次“/”键即可将其注释掉。不要删除它,评论它。这样,如果您犯了错误,您就不会丢失任何信息。变量的存在和它的数据类型仍然被记录下来。您可以稍后再整理。

    对此有一个警告:条件编译可能会在以不同方式构建时使某些变量未使用。如果发生这种情况,那么只需再次取消注释变量,并将条件也放在声明周围。

    【讨论】:

    • 感谢有关检查条件定义的观点,这肯定会不时发生。
    • 感谢 Alt-F8 Alt-F7 快捷键。这将节省我一些时间。 +1
    • 我通常会删除未使用的变量、源代码控制或代码历史记录以从错误中恢复。将它们注释掉仅意味着您需要在其他时间返回并删除这些 cmets。
    【解决方案3】:

    您确定不应该使用这些变量吗?我知道编译器发现它们现在没有被使用,但这是正确的,也许应该使用其中的许多,但是开发人员使用 x2 而不是 x1,例如复制和粘贴?

    虽然您可能想删除所有未经审查的变量,但我不会这么仓促,它们可能表明您希望修复代码中的错误。

    例子:

    procedure PlotPixelAtCenter(rect: Rectangle)
    var
        x, y: Integer;
    begin
        x := (rect.Left + rect.Right) div 2;
        x := (rect.Top + rect.Bottom) div 2; // <-- bug here, should be y :=
        PlotPixel(x, y);
    end;
    

    在此示例中,您将收到有关未使用变量的错误,但这是一个潜伏的错误。当然,在这个例子中,错误应该很容易找到,因为绘图可能会关闭,但其他类似的错误可能更难发现。

    【讨论】:

    • > 我知道编译器发现它们现在没有被使用,但这是正确的,有什么例子吗?
    • 好点。我不希望一个工具只是盲目地删除所有变量,而是逐步完成每个变量,例如全局搜索并在每个操作之前用确认替换,所以我可以看看是否涉及 {$ifdef} ,或类似您在上面显示的内容。
    【解决方案4】:

    如果没有这样的工具并且你有一些耐心,我正在构建一个Delphi分析和修复工具。删除未使用的符号也在列表中。这是一个低优先级项目,所以我无法估计它何时准备就绪。

    只是为了解释为什么这不是一项简单的任务:

    1. 阅读源代码
    2. 创建一个模型,其中包含每个符号使用的足够信息。
    3. 标记所有未使用的符号。
    4. 重写源代码,去掉不需要的符号。

    任务 1 和 2 很难(幸运的是我已经完成了)。 Delphi 语言相当复杂。而且您需要所有语言元素才能重新创建源代码。

    任务 3 很简单。只需标记所有未使用的符号。但要注意单元接口部分中的符号。它们可能不被使用,但以后需要(或其他项目)。

    任务 4 取决于。

    方法 A 使用中间格式(例如字符串列表),然后您可以使用模型查找每个未使用符号的声明(自下而上,否则您可能会更改行号)。你删除所有不需要的。如果它是列表中的最后一个 var,请不要忘记删除 var 关键字!

    方法 B 完全重写源文件。在这种情况下,您必须保留所有不太有趣的 cmets(但我的模型也需要这样做)。您只需从模型中删除未使用的符号并重写它。 始终确保创建备份,因为这可能会导致灾难。

    【讨论】:

    • 并且有可能在一行中声明了多个变量,这再次使删除变得复杂。或者 var 声明可以分成两行(第一行是'a',第二行是': integer')。我同意——很难为这个问题写一个稳定的解决方案。
    • 并且代码的某些部分可能依赖于编译器指令({$ifdef ... }),但相应的变量可能不是,因此编译器可以将一些变量检测为未使用,尽管它们与其他编译器指令一起使用
    • 我期待在准备好后检查您的软件...它将是一个独立的应用程序还是集成到 IDE 中的东西?
    猜你喜欢
    • 1970-01-01
    • 2011-09-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-03-11
    • 1970-01-01
    • 2010-12-15
    相关资源
    最近更新 更多