@@ -14,6 +14,7 @@ sealed class InterfaceBuilder(
1414 IRoslynSymbols roslynSymbols ,
1515 ITypes types ,
1616 IArguments arguments ,
17+ ILocationProvider locationProvider ,
1718 Func < IBuilder < GeneratedInterfaceDetails , Lines > > interfaceCodeBuilderFactory )
1819 : IInterfaceBuilder
1920{
@@ -35,14 +36,16 @@ sealed class InterfaceBuilder(
3536 globalNamespaceStyle : FullyQualifiedDisplayFormat . GlobalNamespaceStyle ,
3637 miscellaneousOptions : FullyQualifiedDisplayFormat . MiscellaneousOptions ) ;
3738
38- public ImmutableArray < GeneratedInterfaceSource > BuildInterfacesFor (
39+ public GeneratedInterfacesResult BuildInterfacesFor (
3940 SemanticModel semanticModel ,
4041 ITypeSymbol typeSymbol ,
4142 ClassDeclarationSyntax classSyntax )
4243 {
4344 if ( typeSymbol is not INamedTypeSymbol namedTypeSymbol )
4445 {
45- return ImmutableArray < GeneratedInterfaceSource > . Empty ;
46+ return new GeneratedInterfacesResult (
47+ ImmutableArray < GeneratedInterfaceSource > . Empty ,
48+ ImmutableArray < GeneratedInterfaceWarning > . Empty ) ;
4649 }
4750
4851 var generateInterfaceAttributeFullName = $ "{ Names . GlobalNamespacePrefix } { Names . GenerateInterfaceAttributeFullName } ";
@@ -53,6 +56,8 @@ public ImmutableArray<GeneratedInterfaceSource> BuildInterfacesFor(
5356 var settingsByInterface = new Dictionary < InterfaceKey , ( bool AsInternal , bool HasClassAttribute ) > ( ) ;
5457 var selectiveInterfaces = new HashSet < InterfaceKey > ( ) ;
5558 var selectedMembersByInterface = new Dictionary < InterfaceKey , HashSet < ISymbol > > ( ) ;
59+ var selectiveInterfaceLocations = new Dictionary < InterfaceKey , Location > ( ) ;
60+ var warnings = new List < GeneratedInterfaceWarning > ( ) ;
5661
5762 var classGenerateAttributes = typeSymbol . GetAttributes ( )
5863 . Where ( x => x . AttributeClass ? . ToDisplayString ( SymbolDisplayFormat . FullyQualifiedFormat ) == generateInterfaceAttributeFullName )
@@ -76,12 +81,15 @@ public ImmutableArray<GeneratedInterfaceSource> BuildInterfacesFor(
7681 settingsByInterface [ key ] = ( existing . AsInternal , true ) ;
7782 }
7883
79- var members = roslynSymbols . GetAllMembers ( typeSymbol )
84+ var allMembers = roslynSymbols . GetAllMembers ( typeSymbol )
85+ . Where ( x => x . Kind is SymbolKind . Method or SymbolKind . Property or SymbolKind . Event )
86+ . ToList ( ) ;
87+ var eligibleMembers = allMembers
8088 . Where ( x => x . DeclaredAccessibility == Accessibility . Public )
8189 . Where ( x => ! x . IsStatic )
8290 . ToList ( ) ;
8391
84- foreach ( var member in members )
92+ foreach ( var member in allMembers )
8593 {
8694 var generationAttributes = member . GetAttributes ( )
8795 . Where ( x => x . AttributeClass ? . ToDisplayString ( SymbolDisplayFormat . FullyQualifiedFormat ) == generateInterfaceAttributeFullName )
@@ -101,12 +109,38 @@ public ImmutableArray<GeneratedInterfaceSource> BuildInterfacesFor(
101109 defaultAsInternal ) ;
102110 var key = new InterfaceKey ( settings . namespaceName , settings . interfaceName ) ;
103111 selectiveInterfaces . Add ( key ) ;
112+ if ( ! selectiveInterfaceLocations . ContainsKey ( key ) )
113+ {
114+ selectiveInterfaceLocations . Add ( key , GetLocation ( member , generationAttribute ) ) ;
115+ }
104116
105117 if ( ! settingsByInterface . TryGetValue ( key , out var existing ) )
106118 {
107119 settingsByInterface [ key ] = ( settings . asInternal , false ) ;
108120 }
109121
122+ var memberName = member . ToDisplayString ( SymbolDisplayFormat . CSharpErrorMessageFormat ) ;
123+ var interfaceDisplayName = GetInterfaceDisplayName ( key ) ;
124+ if ( member . IsStatic )
125+ {
126+ warnings . Add ( new GeneratedInterfaceWarning (
127+ LogId . WarningGenerateInterfaceOnStaticMember ,
128+ string . Format ( Strings . Warning_Template_GenerateInterfaceOnStaticMember , memberName , interfaceDisplayName ) ,
129+ nameof ( Strings . Warning_Template_GenerateInterfaceOnStaticMember ) ,
130+ GetLocation ( member , generationAttribute ) ) ) ;
131+ continue ;
132+ }
133+
134+ if ( member . DeclaredAccessibility != Accessibility . Public )
135+ {
136+ warnings . Add ( new GeneratedInterfaceWarning (
137+ LogId . WarningGenerateInterfaceOnNonPublicMember ,
138+ string . Format ( Strings . Warning_Template_GenerateInterfaceOnNonPublicMember , memberName , interfaceDisplayName ) ,
139+ nameof ( Strings . Warning_Template_GenerateInterfaceOnNonPublicMember ) ,
140+ GetLocation ( member , generationAttribute ) ) ) ;
141+ continue ;
142+ }
143+
110144 if ( HasIgnoreAttribute ( member ) )
111145 {
112146 continue ;
@@ -124,7 +158,9 @@ public ImmutableArray<GeneratedInterfaceSource> BuildInterfacesFor(
124158
125159 if ( settingsByInterface . Count == 0 )
126160 {
127- return ImmutableArray < GeneratedInterfaceSource > . Empty ;
161+ return new GeneratedInterfacesResult (
162+ ImmutableArray < GeneratedInterfaceSource > . Empty ,
163+ warnings . ToImmutableArray ( ) ) ;
128164 }
129165
130166 var nullableContextEnabled = semanticModel . Compilation . Options . NullableContextOptions != NullableContextOptions . Disable ;
@@ -135,11 +171,21 @@ public ImmutableArray<GeneratedInterfaceSource> BuildInterfacesFor(
135171 var settings = pair . Value ;
136172 var interfaceMembers = selectiveInterfaces . Contains ( key )
137173 ? selectedMembersByInterface . TryGetValue ( key , out var selectedMembers )
138- ? members . Where ( selectedMembers . Contains ) . ToList ( )
174+ ? eligibleMembers . Where ( selectedMembers . Contains ) . ToList ( )
139175 : [ ]
140176 : settings . HasClassAttribute
141- ? members . Where ( x => ! HasIgnoreAttribute ( x ) ) . ToList ( )
177+ ? eligibleMembers . Where ( x => ! HasIgnoreAttribute ( x ) ) . ToList ( )
142178 : [ ] ;
179+ if ( selectiveInterfaces . Contains ( key )
180+ && interfaceMembers . Count == 0
181+ && selectiveInterfaceLocations . TryGetValue ( key , out var selectiveLocation ) )
182+ {
183+ warnings . Add ( new GeneratedInterfaceWarning (
184+ LogId . WarningGenerateInterfaceSelectiveEmpty ,
185+ string . Format ( Strings . Warning_Template_GenerateInterfaceSelectiveEmpty , GetInterfaceDisplayName ( key ) ) ,
186+ nameof ( Strings . Warning_Template_GenerateInterfaceSelectiveEmpty ) ,
187+ selectiveLocation ) ) ;
188+ }
143189
144190 var symbolDetails = new GeneratedInterfaceDetails ( semanticModel , key . NamespaceName , key . InterfaceName , settings . AsInternal )
145191 {
@@ -156,7 +202,7 @@ public ImmutableArray<GeneratedInterfaceSource> BuildInterfacesFor(
156202 interfaceCodeBuilderFactory ( ) . Build ( symbolDetails ) ) ) ;
157203 }
158204
159- return generatedSources . ToImmutable ( ) ;
205+ return new GeneratedInterfacesResult ( generatedSources . ToImmutable ( ) , warnings . ToImmutableArray ( ) ) ;
160206 }
161207
162208 private ImmutableArray < MethodInfo > GetMethods ( SemanticModel semanticModel , List < ISymbol > members , bool nullableContextEnabled )
@@ -403,4 +449,19 @@ private static T GetArgValue<T>(SemanticModel semanticModel, IReadOnlyList<Attri
403449 return value is { HasValue : true , Value : T typedValue } ? typedValue : defaultValue ;
404450 }
405451
452+ private Location GetLocation ( ISymbol member , AttributeData attribute )
453+ {
454+ if ( attribute . ApplicationSyntaxReference ? . GetSyntax ( ) is AttributeSyntax attributeSyntax )
455+ {
456+ return locationProvider . GetLocation ( attributeSyntax ) ;
457+ }
458+
459+ return member . Locations . FirstOrDefault ( ) ?? Location . None ;
460+ }
461+
462+ private static string GetInterfaceDisplayName ( InterfaceKey key ) =>
463+ string . IsNullOrWhiteSpace ( key . NamespaceName )
464+ ? key . InterfaceName
465+ : $ "{ key . NamespaceName } .{ key . InterfaceName } ";
466+
406467}
0 commit comments