当前位置:首页 > 文章列表 > Golang > Go教程 > Go scanner.ErrorList.RemoveMultiples 怎样让每行只保留一条诊断

Go scanner.ErrorList.RemoveMultiples 怎样让每行只保留一条诊断

来源:17golang原创 2026-11-05 21:51:44 0浏览 收藏

做 Go 源码扫描器时,同一行可能同时产生多个诊断:列号不同、消息不同,但对编辑器或命令行摘要来说,逐条展示往往太吵。scanner.ErrorList.RemoveMultiples 正好提供了一个收敛展示列表的入口:它先排序,再按文件名和行号只保留每行遇到的第一条诊断。

官方文档:https://pkg.go.dev/go/scanner#ErrorList.RemoveMultiples

RemoveMultiples 会直接整理当前 ErrorList:排序依据包含文件名、行号、列号和消息,去重键只看文件名与行号。要保留完整诊断,就在调用前复制一份列表,把原始列表用于详细报告,把整理后的列表用于摘要。

这个方法到底保留哪一条错误

ErrorList 是 []*scanner.Error。每个错误带有 token.Position 和消息。标准库的排序先比较文件名,再比较行号、列号,最后比较消息;因此同一文件同一行中,列号更靠前的错误会排在前面。

随后 RemoveMultiples 只比较当前错误和上一条已保留错误的文件名、行号。如果两者相同,当前错误就被跳过;如果文件名或行号变化,当前错误就成为该行的首条代表。

ErrorList 中的错误按文件名行号列号消息排序并按同文件同一行保留首条诊断的静态关系图
图1:说明图,展示 ErrorList 的排序字段、按行去重条件和保留首条诊断之间的静态关系;它不是运行截图。
func compactDiagnostics(list scanner.ErrorList) scanner.ErrorList {
	// RemoveMultiples 会先排序,再让每个文件的每一行只保留一条诊断。
	list.RemoveMultiples()
	return list
}

这里返回的是同一个切片变量整理后的结果,不会创建一组新的 scanner.Error 对象。调用方如果只需要摘要,这种写法很直接;如果后面还要输出所有错误,就不要对唯一的原始列表直接调用。

把完整诊断和摘要诊断分开

最实用的做法是准备两份切片:一份保存全部指针,另一份作为展示副本。下面的复制是浅复制,复制的是指针切片;RemoveMultiples 只会重排和缩短切片,不会改写每个 Error 的位置或消息,所以适合这个场景。

func splitDiagnostics(list scanner.ErrorList) (scanner.ErrorList, scanner.ErrorList) {
	// 复制切片头,保留完整列表供详细报告使用。
	all := append(scanner.ErrorList(nil), list...)
	// 再复制一份,只让摘要副本承担排序和按行去重。
	summary := append(scanner.ErrorList(nil), list...)
	summary.RemoveMultiples()
	return all, summary
}

复制要发生在整理之前。若先对 list 调用 RemoveMultiples,原切片长度已经缩短,再复制就只能得到摘要,无法恢复被移除的指针顺序。

从 Scanner 收集错误时保留位置

scanner.Scanner 可以通过 Init 接收一个错误处理函数。处理函数拿到的是错误位置和消息,收集时应把这两个值加入 ErrorList,否则后面的按文件、按行归并就没有可靠依据。

func scanSource(filename string, src []byte) scanner.ErrorList {
	var list scanner.ErrorList
	fset := token.NewFileSet()
	file := fset.AddFile(filename, fset.Base(), len(src))
	var s scanner.Scanner
	s.Init(file, src, func(pos token.Position, msg string) {
		// 位置和消息一起保存,后续才能按文件和行号整理。
		list.Add(pos, msg)
	}, 0)

	for {
		_, tok, _ := s.Scan()
		if tok == token.EOF {
			break
		}
	}
	return list
}

扫描器在遇到语法错误后仍可能继续返回可用 token,所以不要只根据是否出现非法 token 判断扫描是否成功;错误处理函数收到的诊断数量才是更直接的收集结果。

同一行多条错误时为什么是“第一条”

“第一条”不是指错误最早被扫描到的那一条,而是排序完成后的第一条。排序同时使用文件名、行号、列号和消息,因此同一文件同一行通常会优先保留列号更小的诊断。这个规则适合做稳定摘要,但不等于错误重要性排序。

如果业务认为某种消息更重要,不能直接依赖 RemoveMultiples 选出它。可以先自行过滤、调整消息或构造业务排序后的列表,再决定是否调用标准库方法;也可以保留完整列表,在展示层按优先级选择代表项。

注意没有有效位置的 Error

按行去重的前提是 Position.Filename 和 Position.Line 能表达一个稳定位置。没有文件名或行号的错误不适合和源码行诊断混在同一摘要里,最好单独放到“全局错误”区域,或在进入 RemoveMultiples 前单独保留。

尤其不要把“没有位置”误读成“所有未定位错误属于同一源码行”。该方法的去重判断就是文件名加行号,调用方应先决定无位置错误的展示策略。

RemoveMultiples 通过指针接收者在原始 ErrorList 前部覆盖元素并截短为保留结果的静态说明图
图2:结构图,展示指针接收者、原始切片、按文件名加行号判重、前部覆盖和 [0:i] 结果边界;它不是运行结果截图。

原地整理带来的调用边界

RemoveMultiples 使用指针接收者,是因为它既要排序元素,又要把切片重新切到保留数量。整理后的列表通常复用原来的底层数组;如果程序还持有同一切片的其他视图,就不要把这些视图当成独立的“原始快照”。最稳妥的做法仍是先复制,再分别传给详细报告和摘要输出。

目标推荐做法需要注意
输出所有诊断直接遍历原始 ErrorList不要先调用 RemoveMultiples
生成一行一条摘要对副本调用 RemoveMultiples代表项由排序规则决定
自定义重要性先做业务过滤或自定义选择不要把标准排序当成优先级排序
处理无位置错误单独展示或提前归类按行去重缺少稳定位置依据

最后记住这几个判断

  • RemoveMultiples 的去重键是文件名和行号,不是消息文本,也不是列号。
  • 它会先排序,所以“第一条”由位置排序决定,不代表最重要或最早发生。
  • 方法直接修改当前 ErrorList 的切片顺序和长度;需要完整错误时先复制。
  • 收集 Scanner 错误时要保存 token.Position,否则无法得到稳定的按行摘要。
  • 无有效位置的错误应有单独展示策略,不要强行当作源码行诊断。

相关问题

RemoveMultiples 会删除原始 Error 对象吗?它主要重排指针并缩短切片,不会把每个 Error 的消息和位置字段改写;但原切片的顺序和长度会变化。

同一行想保留最严重的错误怎么办?不要直接依赖标准库的排序结果,先按业务规则筛选代表项,再把完整列表留作详细输出。

只调用 Sort 能不能达到同样效果?不能。Sort 只排序,RemoveMultiples 才会按文件名和行号压缩重复行。

为什么要复制两份 ErrorList?因为摘要操作会改变切片长度和顺序,先复制可以同时保留详细诊断与简洁展示。

版本声明
本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
Go archive/zip.Reader 怎样保留错误信息Go archive/zip.Reader 怎样保留错误信息
上一篇
Go archive/zip.Reader 怎样保留错误信息
Go compress/flate.Writer 怎样处理可选字段
下一篇
Go compress/flate.Writer 怎样处理可选字段
查看更多
最新文章
查看更多
课程推荐
  • 前端进阶之JavaScript设计模式
    前端进阶之JavaScript设计模式
    设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
    543次学习
  • GO语言核心编程课程
    GO语言核心编程课程
    本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
    516次学习
  • 简单聊聊mysql8与网络通信
    简单聊聊mysql8与网络通信
    如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
    500次学习
  • JavaScript正则表达式基础与实战
    JavaScript正则表达式基础与实战
    在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
    487次学习
  • 从零制作响应式网站—Grid布局
    从零制作响应式网站—Grid布局
    本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
    485次学习
查看更多
AI推荐
  • PubMedQA数据集详解:生物医学问答基准、功能与应用指南
    PubMedQA
    深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
    899次使用
  • H2O EvalGPT:开源LLM大模型评估与排行榜工具
    H2O EvalGPT
    H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
    906次使用
  • LMArena是什么?伯克利AI模型评估平台使用指南与功能解析
    LMArena
    LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
    986次使用
  • 斯坦福HELM:大语言模型Holistic Evaluation整体评估框架详解
    HELM
    深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
    975次使用
  • MMBench详解:多模态大模型基准测试、功能特点与使用指南
    MMBench
    MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
    800次使用
微信登录更方便
  • 密码登录
  • 注册账号
登录即同意 用户协议 和 隐私政策
返回登录
  • 重置密码