@@ -2,12 +2,17 @@ use rustc_ast::join_path_syms;
22use rustc_attr_ir:: AttributeKind ;
33use rustc_attr_ir:: lint:: { LintCheck , LintCheckKind } ;
44use rustc_attr_ir:: target:: { AssocCtxt , MethodKind , Target } ;
5- use rustc_lint_defs:: builtin:: UNUSED_ATTRIBUTES ;
6- use rustc_span:: { Symbol , sym} ;
5+ use rustc_lint_defs:: builtin:: { RENAMED_AND_REMOVED_LINTS , UNKNOWN_LINTS , UNUSED_ATTRIBUTES } ;
6+ use rustc_lint_defs:: { CheckLintNameResult , LintId } ;
7+ use rustc_span:: { DUMMY_SP , Ident , Span , Symbol , sym} ;
78use thin_vec:: ThinVec ;
89
910use crate :: attributes:: { AcceptMapping , AttributeParser , AttributeStability } ;
1011use crate :: context:: { AcceptContext , ExpectStringLiteral , FinalizeContext } ;
12+ use crate :: diagnostics:: {
13+ DeprecatedLintName , RemovedLint , RenamedLint , RenamedLintSuggestion , UnknownLint ,
14+ UnknownLintSuggestion , UnknownToolInScopedLint ,
15+ } ;
1116use crate :: parser:: { ArgParser , MetaItemOrLitParser } ;
1217use crate :: target_checking:: AllowedTargets ;
1318use crate :: target_checking:: Policy :: { Allow , Warn } ;
@@ -69,9 +74,18 @@ impl LintParser {
6974 ) ,
7075 } ;
7176
77+ let lint_name_normalized = if let Some ( ( _lint_ids, new_lint_name) ) =
78+ Self :: check_lint_name ( lint_name, tool, p. span ( ) , cx)
79+ {
80+ new_lint_name
81+ } else {
82+ // Discard lint attributes with unknown lints in them
83+ continue ;
84+ } ;
85+
7286 self . lints . push ( LintCheck {
7387 tool,
74- lint_name ,
88+ lint_name_normalized ,
7589 lint_span : p. span ( ) ,
7690 kind,
7791 attr_id,
@@ -130,6 +144,142 @@ impl LintParser {
130144 }
131145 }
132146 }
147+
148+ fn check_lint_name < ' a > (
149+ lint_name : Symbol ,
150+ tool : Option < Ident > ,
151+ lint_span : Span ,
152+ cx : & mut AcceptContext < ' _ , ' a > ,
153+ ) -> Option < ( & ' a [ LintId ] , Symbol ) > {
154+ let lint_result = cx. sess . lint_store . as_ref ( ) . unwrap ( ) . check_lint_name (
155+ lint_name. as_str ( ) ,
156+ tool. map ( |tool| tool. name ) ,
157+ cx. lint_tools . unwrap ( ) ,
158+ ) ;
159+
160+ // FIXME(164898) Lint attributes are parsed twice, once on the AST and once on the HIR
161+ // Both times emit diagnostics, for historic reasons
162+ // We only want to emit these lints once, on the HIR.
163+ // On the AST `target_span` will be `DUMMY_SP`, which we use to detect which instance we are.
164+ // #164898 will fix this, after that PR this check can be removed
165+ let should_lint = cx. target_span != DUMMY_SP ;
166+
167+ let ( ids, name) = match lint_result {
168+ CheckLintNameResult :: Ok ( ids) => ( ids, lint_name) ,
169+
170+ CheckLintNameResult :: Tool ( ids, new_lint_name) => {
171+ let name = match new_lint_name {
172+ None => {
173+ let complete_name = & format ! ( "{}::{lint_name}" , tool. unwrap( ) . name) ;
174+ Symbol :: intern ( complete_name)
175+ }
176+ Some ( new_lint_name) => {
177+ let symbol = Symbol :: intern ( & new_lint_name) ;
178+ if should_lint {
179+ cx. emit_lint (
180+ RENAMED_AND_REMOVED_LINTS ,
181+ DeprecatedLintName {
182+ name : lint_name. to_string ( ) ,
183+ suggestion : lint_span,
184+ replace : new_lint_name,
185+ } ,
186+ lint_span,
187+ ) ;
188+ }
189+ symbol
190+ }
191+ } ;
192+ ( ids, name)
193+ }
194+
195+ CheckLintNameResult :: MissingTool => {
196+ // If `MissingTool` is returned, then either the lint does not
197+ // exist in the tool or the code was not compiled with the tool and
198+ // therefore the lint was never added to the `LintStore`. To detect
199+ // this is the responsibility of the lint tool.
200+ return None ;
201+ }
202+
203+ CheckLintNameResult :: NoTool => {
204+ if should_lint {
205+ cx. emit_err ( UnknownToolInScopedLint {
206+ span : Some ( tool. unwrap ( ) . span ) ,
207+ tool_name : tool. unwrap ( ) . name ,
208+ lint_name : lint_name. to_string ( ) ,
209+ is_nightly_build : cx. sess . is_nightly_build ( ) ,
210+ } ) ;
211+ }
212+ return None ;
213+ }
214+
215+ CheckLintNameResult :: Renamed ( ref replace) => {
216+ if should_lint {
217+ let suggestion = RenamedLintSuggestion :: WithSpan {
218+ suggestion : lint_span,
219+ replace : replace. clone ( ) ,
220+ } ;
221+ let name = tool
222+ . map ( |Ident { name, .. } | format ! ( "{name}::{lint_name}" ) )
223+ . unwrap_or_else ( || lint_name. to_string ( ) ) ;
224+ cx. emit_lint (
225+ RENAMED_AND_REMOVED_LINTS ,
226+ RenamedLint { name, replace : replace. clone ( ) , suggestion } ,
227+ lint_span,
228+ ) ;
229+ }
230+
231+ // If this lint was renamed, apply the new lint instead of ignoring the
232+ // attribute. Ignore any errors or warnings that happen because the new
233+ // name is inaccurate.
234+ // NOTE: `new_name` already includes the tool name, so we don't
235+ // have to add it again.
236+ let CheckLintNameResult :: Ok ( ids) = cx
237+ . sess
238+ . lint_store
239+ . as_ref ( )
240+ . unwrap ( )
241+ . check_lint_name ( replace, None , cx. lint_tools . unwrap ( ) )
242+ else {
243+ panic ! ( "renamed lint does not exist: {replace}" ) ;
244+ } ;
245+
246+ ( ids, Symbol :: intern ( & replace) )
247+ }
248+
249+ CheckLintNameResult :: Removed ( ref reason) => {
250+ if should_lint {
251+ let name = if let Some ( Ident { name, .. } ) = tool {
252+ format ! ( "{name}::{lint_name}" )
253+ } else {
254+ lint_name. to_string ( )
255+ } ;
256+ cx. emit_lint (
257+ RENAMED_AND_REMOVED_LINTS ,
258+ RemovedLint { name, reason : reason. clone ( ) } ,
259+ lint_span,
260+ ) ;
261+ }
262+ return None ;
263+ }
264+
265+ CheckLintNameResult :: NoLint ( suggestion) => {
266+ if should_lint {
267+ let name = tool
268+ . map ( |Ident { name, .. } | format ! ( "{name}::{lint_name}" ) )
269+ . unwrap_or_else ( || lint_name. to_string ( ) ) ;
270+ let suggestion =
271+ suggestion. map ( |( replace, from_rustc) | UnknownLintSuggestion :: WithSpan {
272+ suggestion : lint_span,
273+ replace,
274+ from_rustc,
275+ } ) ;
276+ cx. emit_lint ( UNKNOWN_LINTS , UnknownLint { name, suggestion } , lint_span) ;
277+ }
278+ return None ;
279+ }
280+ } ;
281+ Some ( ( ids, name) )
282+ }
133283}
134284
135285impl AttributeParser for LintParser {
0 commit comments