@@ -6,12 +6,14 @@ use ruff_diagnostics::{AlwaysFixableViolation, FixAvailability, Violation};
6
6
use ruff_diagnostics:: { Diagnostic , Edit , Fix } ;
7
7
use ruff_macros:: { derive_message_formats, violation} ;
8
8
use ruff_python_ast:: helpers:: { is_const_false, is_const_true} ;
9
+ use ruff_python_ast:: name:: QualifiedName ;
9
10
use ruff_python_ast:: stmt_if:: elif_else_range;
10
11
use ruff_python_ast:: visitor:: Visitor ;
11
12
use ruff_python_ast:: whitespace:: indentation;
12
13
use ruff_python_ast:: { self as ast, Decorator , ElifElseClause , Expr , Stmt } ;
13
14
use ruff_python_codegen:: Stylist ;
14
15
use ruff_python_index:: Indexer ;
16
+ use ruff_python_semantic:: analyze:: visibility:: is_property;
15
17
use ruff_python_semantic:: SemanticModel ;
16
18
use ruff_python_trivia:: { is_python_whitespace, SimpleTokenKind , SimpleTokenizer } ;
17
19
use ruff_source_file:: Locator ;
@@ -373,11 +375,20 @@ fn unnecessary_return_none(checker: &mut Checker, decorator_list: &[Decorator],
373
375
continue ;
374
376
}
375
377
376
- // Skip properties and other stdlib property-like decorators.
377
- if decorator_list
378
+ let extra_property_decorators = checker
379
+ . settings
380
+ . pydocstyle
381
+ . property_decorators
378
382
. iter ( )
379
- . any ( |decorator| is_property_like_decorator ( decorator, checker. semantic ( ) ) )
380
- {
383
+ . map ( |decorator| QualifiedName :: from_dotted_name ( decorator) )
384
+ . collect :: < Vec < QualifiedName > > ( ) ;
385
+
386
+ // Skip property functions
387
+ if is_property (
388
+ decorator_list,
389
+ & extra_property_decorators,
390
+ checker. semantic ( ) ,
391
+ ) {
381
392
return ;
382
393
}
383
394
@@ -390,25 +401,6 @@ fn unnecessary_return_none(checker: &mut Checker, decorator_list: &[Decorator],
390
401
}
391
402
}
392
403
393
- /// Determine whether `decorator` is `@property`,
394
- /// or another stdlib decorator similar to `@property`.
395
- ///
396
- /// TODO: be more principled here once we have type inference;
397
- /// hardcoding these is a little hacky.
398
- fn is_property_like_decorator ( decorator : & Decorator , semantic : & SemanticModel ) -> bool {
399
- semantic
400
- . resolve_qualified_name ( & decorator. expression )
401
- . is_some_and ( |qualified_name| {
402
- matches ! (
403
- qualified_name. segments( ) ,
404
- [ "" | "builtins" | "enum" , "property" ]
405
- | [ "functools" , "cached_property" ]
406
- | [ "abc" , "abstractproperty" ]
407
- | [ "types" , "DynamicClassAttribute" ]
408
- )
409
- } )
410
- }
411
-
412
404
/// RET502
413
405
fn implicit_return_value ( checker : & mut Checker , stack : & Stack ) {
414
406
for stmt in & stack. returns {
0 commit comments