@@ -21,6 +21,13 @@ import SwiftSyntax
2121///         `internal`, as that is the default access level) have the explicit access level removed.
2222@_spi ( Rules)  
2323public  final  class  NoAccessLevelOnExtensionDeclaration :  SyntaxFormatRule  { 
24+   /// The access level keyword that was attached to the current extension, or nil if we are not
25+   /// inside an extension.
26+   private  var  accessKeyword :  Keyword ? =  nil 
27+ 
28+   /// Findings propagated up to the extension visitor from any members that were rewritten.
29+   private  var  notesFromRewrittenMembers :  [ Finding . Note ]  =  [ ] 
30+ 
2431  public  override func  visit( _ node:  ExtensionDeclSyntax )  ->  DeclSyntax  { 
2532    guard 
2633      let  accessKeyword =  node. modifiers. accessLevelModifier, 
@@ -29,80 +36,169 @@ public final class NoAccessLevelOnExtensionDeclaration: SyntaxFormatRule {
2936      return  DeclSyntax ( node) 
3037    } 
3138
32-     var  result  =  node
39+     let  keywordToAdd :  Keyword ? 
40+     let  message :  Finding . Message 
3341
3442    switch  keyword { 
35-     // Public, private, fileprivate, or package keywords need to be moved to members
3643    case  . public,  . private,  . fileprivate,  . package : 
37-       // The effective access level of the members of a `private` extension is `fileprivate`, so
38-       // we have to update the keyword to ensure that the result is correct.
39-       var  accessKeywordToAdd  =  accessKeyword
40-       let  message :  Finding . Message 
44+       // These access level modifiers need to be moved to members. Additionally, `private` is a
45+       // special case, because the *effective* access level for a top-level private extension is
46+       // `fileprivate`, so we need to preserve that when we apply it to the members.
4147      if  keyword ==  . private { 
42-         accessKeywordToAdd . name . tokenKind  =  . keyword ( . fileprivate) 
48+         keywordToAdd  =  . fileprivate
4349        message =  . moveAccessKeywordAndMakeFileprivate( keyword:  accessKeyword. name. text) 
4450      }  else  { 
51+         keywordToAdd =  keyword
4552        message =  . moveAccessKeyword( keyword:  accessKeyword. name. text) 
4653      } 
4754
48-       let  ( newMembers,  notes)  = 
49-         addMemberAccessKeyword ( accessKeywordToAdd,  toMembersIn:  node. memberBlock) 
50-       diagnose ( message,  on:  accessKeyword,  notes:  notes) 
51- 
52-       result. modifiers. remove ( anyOf:  [ keyword] ) 
53-       result. extensionKeyword. leadingTrivia =  accessKeyword. leadingTrivia
54-       result. memberBlock. members =  newMembers
55-       return  DeclSyntax ( result) 
56- 
57-     // Internal keyword redundant, delete
5855    case  . internal: 
59-       diagnose ( . removeRedundantAccessKeyword,  on:  accessKeyword) 
60- 
61-       result. modifiers. remove ( anyOf:  [ keyword] ) 
62-       result. extensionKeyword. leadingTrivia =  accessKeyword. leadingTrivia
63-       return  DeclSyntax ( result) 
56+       // If the access level keyword was `internal`, then it's redundant and we can just remove it.
57+       // We don't need to modify the members at all in this case.
58+       message =  . removeRedundantAccessKeyword
59+       keywordToAdd =  nil 
6460
6561    default : 
66-       break 
62+       // For anything else, just return the extension and its members unchanged.
63+       return  DeclSyntax ( node) 
64+     } 
65+ 
66+     // Set the keyword that should be applied to nested decls when we visit them, if any. Since
67+     // extensions can't nest, we don't have to worry about maintaining a stack here.
68+     self . accessKeyword =  keywordToAdd
69+     self . notesFromRewrittenMembers =  [ ] 
70+     defer  {  self . accessKeyword =  nil  } 
71+ 
72+     var  result :  ExtensionDeclSyntax 
73+     if  keywordToAdd !=  nil  { 
74+       // Visit the children if we need to add a keyword to the extension members.
75+       result =  super. visit ( node) . as ( ExtensionDeclSyntax . self) !
76+     }  else  { 
77+       // We don't need to visit the children in this case.
78+       result =  node
6779    } 
6880
81+     // Finally, emit the finding (which includes notes from any rewritten members) and remove the
82+     // access level keyword from the extension itself.
83+     diagnose ( message,  on:  accessKeyword,  notes:  self . notesFromRewrittenMembers) 
84+     result. modifiers. remove ( anyOf:  [ keyword] ) 
85+     result. extensionKeyword. leadingTrivia =  accessKeyword. leadingTrivia
6986    return  DeclSyntax ( result) 
7087  } 
7188
72-   // Adds given keyword to all members in declaration block
73-   private  func  addMemberAccessKeyword( 
74-     _ modifier:  DeclModifierSyntax , 
75-     toMembersIn memberBlock:  MemberBlockSyntax 
76-   )  ->  ( MemberBlockItemListSyntax ,  [ Finding . Note ] )  { 
77-     var  newMembers :  [ MemberBlockItemSyntax ]  =  [ ] 
78-     var  notes :  [ Finding . Note ]  =  [ ] 
79- 
80-     for  memberItem  in  memberBlock. members { 
81-       let  decl  =  memberItem. decl
82-       guard 
83-         let  modifiers =  decl. asProtocol ( WithModifiersSyntax . self) ? . modifiers, 
84-         modifiers. accessLevelModifier ==  nil 
85-       else  { 
86-         newMembers. append ( memberItem) 
87-         continue 
88-       } 
89+   public  override func  visit( _ node:  ActorDeclSyntax )  ->  DeclSyntax  { 
90+     return  applyingAccessModifierIfNone ( to:  node) 
91+   } 
92+ 
93+   public  override func  visit( _ node:  ClassDeclSyntax )  ->  DeclSyntax  { 
94+     return  applyingAccessModifierIfNone ( to:  node) 
95+   } 
96+ 
97+   public  override func  visit( _ node:  EnumDeclSyntax )  ->  DeclSyntax  { 
98+     return  applyingAccessModifierIfNone ( to:  node) 
99+   } 
100+ 
101+   public  override func  visit( _ node:  FunctionDeclSyntax )  ->  DeclSyntax  { 
102+     return  applyingAccessModifierIfNone ( to:  node) 
103+   } 
104+ 
105+   public  override func  visit( _ node:  InitializerDeclSyntax )  ->  DeclSyntax  { 
106+     return  applyingAccessModifierIfNone ( to:  node) 
107+   } 
108+ 
109+   public  override func  visit( _ node:  StructDeclSyntax )  ->  DeclSyntax  { 
110+     return  applyingAccessModifierIfNone ( to:  node) 
111+   } 
112+ 
113+   public  override func  visit( _ node:  SubscriptDeclSyntax )  ->  DeclSyntax  { 
114+     return  applyingAccessModifierIfNone ( to:  node) 
115+   } 
116+ 
117+   public  override func  visit( _ node:  TypeAliasDeclSyntax )  ->  DeclSyntax  { 
118+     return  applyingAccessModifierIfNone ( to:  node) 
119+   } 
89120
90-       // Create a note associated with each declaration that needs to have an access level modifier
91-       // added to it.
92-       notes. append ( 
93-         Finding . Note ( 
94-           message:  . addModifierToExtensionMember( keyword:  modifier. name. text) , 
95-           location: 
96-             Finding . Location ( decl. startLocation ( converter:  context. sourceLocationConverter) ) 
97-         ) 
121+   public  override func  visit( _ node:  VariableDeclSyntax )  ->  DeclSyntax  { 
122+     return  applyingAccessModifierIfNone ( to:  node) 
123+   } 
124+ 
125+   /// Adds `modifier` to `decl` if it doesn't already have an explicit access level modifier and
126+   /// returns the new declaration.
127+   ///
128+   /// If `decl` already has an access level modifier, it is returned unchanged.
129+   private  func  applyingAccessModifierIfNone( to decl:  some  DeclSyntaxProtocol )  ->  DeclSyntax  { 
130+     // Only go further if we are applying an access level keyword and if the decl is one that
131+     // allows modifiers but doesn't already have an access level modifier.
132+     guard 
133+       let  accessKeyword, 
134+       let  modifiers =  decl. asProtocol ( WithModifiersSyntax . self) ? . modifiers, 
135+       modifiers. accessLevelModifier ==  nil 
136+     else  { 
137+       return  DeclSyntax ( decl) 
138+     } 
139+ 
140+     // Create a note associated with each declaration that needs to have an access level modifier
141+     // added to it.
142+     self . notesFromRewrittenMembers. append ( 
143+       Finding . Note ( 
144+         message:  . addModifierToExtensionMember( keyword:  TokenSyntax . keyword ( accessKeyword) . text) , 
145+         location: 
146+           Finding . Location ( decl. startLocation ( converter:  context. sourceLocationConverter) ) 
98147      ) 
148+     ) 
99149
100-       var  newItem  =  memberItem
101-       newItem. decl =  applyingAccessModifierIfNone ( modifier,  to:  decl) 
102-       newMembers. append ( newItem) 
150+     switch  Syntax ( decl) . as ( SyntaxEnum . self)  { 
151+     case  . actorDecl( let  actorDecl) : 
152+       return  applyingAccessModifierIfNone ( accessKeyword,  to:  actorDecl,  declKeywordKeyPath:  \. actorKeyword) 
153+     case  . classDecl( let  classDecl) : 
154+       return  applyingAccessModifierIfNone ( accessKeyword,  to:  classDecl,  declKeywordKeyPath:  \. classKeyword) 
155+     case  . enumDecl( let  enumDecl) : 
156+       return  applyingAccessModifierIfNone ( accessKeyword,  to:  enumDecl,  declKeywordKeyPath:  \. enumKeyword) 
157+     case  . initializerDecl( let  initDecl) : 
158+       return  applyingAccessModifierIfNone ( accessKeyword,  to:  initDecl,  declKeywordKeyPath:  \. initKeyword) 
159+     case  . functionDecl( let  funcDecl) : 
160+       return  applyingAccessModifierIfNone ( accessKeyword,  to:  funcDecl,  declKeywordKeyPath:  \. funcKeyword) 
161+     case  . structDecl( let  structDecl) : 
162+       return  applyingAccessModifierIfNone ( accessKeyword,  to:  structDecl,  declKeywordKeyPath:  \. structKeyword) 
163+     case  . subscriptDecl( let  subscriptDecl) : 
164+       return  applyingAccessModifierIfNone ( accessKeyword,  to:  subscriptDecl,  declKeywordKeyPath:  \. subscriptKeyword) 
165+     case  . typeAliasDecl( let  typeAliasDecl) : 
166+       return  applyingAccessModifierIfNone ( accessKeyword,  to:  typeAliasDecl,  declKeywordKeyPath:  \. typealiasKeyword) 
167+     case  . variableDecl( let  varDecl) : 
168+       return  applyingAccessModifierIfNone ( accessKeyword,  to:  varDecl,  declKeywordKeyPath:  \. bindingSpecifier) 
169+     default : 
170+       return  DeclSyntax ( decl) 
171+     } 
172+   } 
173+ 
174+   private  func  applyingAccessModifierIfNone< Decl:  DeclSyntaxProtocol  &  WithModifiersSyntax > ( 
175+     _ modifier:  Keyword , 
176+     to decl:  Decl , 
177+     declKeywordKeyPath:  WritableKeyPath < Decl ,  TokenSyntax > 
178+   )  ->  DeclSyntax  { 
179+     // If there's already an access modifier among the modifier list, bail out.
180+     guard  decl. modifiers. accessLevelModifier ==  nil  else  {  return  DeclSyntax ( decl)  } 
181+ 
182+     var  result  =  decl
183+     var  modifier  =  DeclModifierSyntax ( name:  . keyword( modifier) ) 
184+     modifier. trailingTrivia =  [ . spaces( 1 ) ] 
185+ 
186+     guard  var  firstModifier =  decl. modifiers. first else  { 
187+       // If there are no modifiers at all, add the one being requested, moving the leading trivia
188+       // from the decl keyword to that modifier (to preserve leading comments, newlines, etc.).
189+       modifier. leadingTrivia =  decl [ keyPath:  declKeywordKeyPath] . leadingTrivia
190+       result [ keyPath:  declKeywordKeyPath] . leadingTrivia =  [ ] 
191+       result. modifiers =  . init( [ modifier] ) 
192+       return  DeclSyntax ( result) 
103193    } 
104194
105-     return  ( MemberBlockItemListSyntax ( newMembers) ,  notes) 
195+     // Otherwise, insert the modifier at the front of the modifier list, moving the (original) first
196+     // modifier's leading trivia to the new one (to preserve leading comments, newlines, etc.).
197+     modifier. leadingTrivia =  firstModifier. leadingTrivia
198+     firstModifier. leadingTrivia =  [ ] 
199+     result. modifiers [ result. modifiers. startIndex]  =  firstModifier
200+     result. modifiers. insert ( modifier,  at:  result. modifiers. startIndex) 
201+     return  DeclSyntax ( result) 
106202  } 
107203} 
108204
@@ -122,81 +218,3 @@ extension Finding.Message {
122218    " add ' \( keyword) ' access modifier to this declaration " 
123219  } 
124220} 
125- 
126- /// Adds `modifier` to `decl` if it doesn't already have an explicit access level modifier and
127- /// returns the new declaration.
128- ///
129- /// If `decl` already has an access level modifier, it is returned unchanged.
130- private  func  applyingAccessModifierIfNone( 
131-   _ modifier:  DeclModifierSyntax , 
132-   to decl:  DeclSyntax 
133- )  ->  DeclSyntax  { 
134-   switch  Syntax ( decl) . as ( SyntaxEnum . self)  { 
135-   case  . actorDecl( let  actorDecl) : 
136-     return  applyingAccessModifierIfNone ( modifier,  to:  actorDecl,  declKeywordKeyPath:  \. actorKeyword) 
137-   case  . classDecl( let  classDecl) : 
138-     return  applyingAccessModifierIfNone ( modifier,  to:  classDecl,  declKeywordKeyPath:  \. classKeyword) 
139-   case  . enumDecl( let  enumDecl) : 
140-     return  applyingAccessModifierIfNone ( modifier,  to:  enumDecl,  declKeywordKeyPath:  \. enumKeyword) 
141-   case  . initializerDecl( let  initDecl) : 
142-     return  applyingAccessModifierIfNone ( modifier,  to:  initDecl,  declKeywordKeyPath:  \. initKeyword) 
143-   case  . functionDecl( let  funcDecl) : 
144-     return  applyingAccessModifierIfNone ( modifier,  to:  funcDecl,  declKeywordKeyPath:  \. funcKeyword) 
145-   case  . structDecl( let  structDecl) : 
146-     return  applyingAccessModifierIfNone ( 
147-       modifier, 
148-       to:  structDecl, 
149-       declKeywordKeyPath:  \. structKeyword
150-     ) 
151-   case  . subscriptDecl( let  subscriptDecl) : 
152-     return  applyingAccessModifierIfNone ( 
153-       modifier, 
154-       to:  subscriptDecl, 
155-       declKeywordKeyPath:  \. subscriptKeyword
156-     ) 
157-   case  . typeAliasDecl( let  typeAliasDecl) : 
158-     return  applyingAccessModifierIfNone ( 
159-       modifier, 
160-       to:  typeAliasDecl, 
161-       declKeywordKeyPath:  \. typealiasKeyword
162-     ) 
163-   case  . variableDecl( let  varDecl) : 
164-     return  applyingAccessModifierIfNone ( 
165-       modifier, 
166-       to:  varDecl, 
167-       declKeywordKeyPath:  \. bindingSpecifier
168-     ) 
169-   default : 
170-     return  decl
171-   } 
172- } 
173- 
174- private  func  applyingAccessModifierIfNone< Decl:  DeclSyntaxProtocol  &  WithModifiersSyntax > ( 
175-   _ modifier:  DeclModifierSyntax , 
176-   to decl:  Decl , 
177-   declKeywordKeyPath:  WritableKeyPath < Decl ,  TokenSyntax > 
178- )  ->  DeclSyntax  { 
179-   // If there's already an access modifier among the modifier list, bail out.
180-   guard  decl. modifiers. accessLevelModifier ==  nil  else  {  return  DeclSyntax ( decl)  } 
181- 
182-   var  result  =  decl
183-   var  modifier  =  modifier
184-   modifier. trailingTrivia =  [ . spaces( 1 ) ] 
185- 
186-   guard  var  firstModifier =  decl. modifiers. first else  { 
187-     // If there are no modifiers at all, add the one being requested, moving the leading trivia
188-     // from the decl keyword to that modifier (to preserve leading comments, newlines, etc.).
189-     modifier. leadingTrivia =  decl [ keyPath:  declKeywordKeyPath] . leadingTrivia
190-     result [ keyPath:  declKeywordKeyPath] . leadingTrivia =  [ ] 
191-     result. modifiers =  . init( [ modifier] ) 
192-     return  DeclSyntax ( result) 
193-   } 
194- 
195-   // Otherwise, insert the modifier at the front of the modifier list, moving the (original) first
196-   // modifier's leading trivia to the new one (to preserve leading comments, newlines, etc.).
197-   modifier. leadingTrivia =  firstModifier. leadingTrivia
198-   firstModifier. leadingTrivia =  [ ] 
199-   result. modifiers [ result. modifiers. startIndex]  =  firstModifier
200-   result. modifiers. insert ( modifier,  at:  result. modifiers. startIndex) 
201-   return  DeclSyntax ( result) 
202- } 
0 commit comments