2
2
// The .NET Foundation licenses this file to you under the MIT license.
3
3
4
4
using System ;
5
+ using System . Diagnostics ;
5
6
using System . Collections . Generic ;
6
7
using System . Linq ;
7
8
using System . Threading ;
@@ -61,19 +62,8 @@ public IReadOnlyList<LoggerClass> GetLogClasses(IEnumerable<ClassDeclarationSynt
61
62
return Array . Empty < LoggerClass > ( ) ;
62
63
}
63
64
64
- INamedTypeSymbol enumerableSymbol = _compilation . GetTypeByMetadataName ( "System.Collections.IEnumerable" ) ;
65
- if ( enumerableSymbol == null )
66
- {
67
- Diag ( DiagnosticDescriptors . MissingRequiredType , null , "System.Collections.IEnumerable" ) ;
68
- return Array . Empty < LoggerClass > ( ) ;
69
- }
70
-
71
- INamedTypeSymbol stringSymbol = _compilation . GetTypeByMetadataName ( "System.String" ) ;
72
- if ( stringSymbol == null )
73
- {
74
- Diag ( DiagnosticDescriptors . MissingRequiredType , null , "System.String" ) ;
75
- return Array . Empty < LoggerClass > ( ) ;
76
- }
65
+ INamedTypeSymbol enumerableSymbol = _compilation . GetSpecialType ( SpecialType . System_Collections_IEnumerable ) ;
66
+ INamedTypeSymbol stringSymbol = _compilation . GetSpecialType ( SpecialType . System_String ) ;
77
67
78
68
var results = new List < LoggerClass > ( ) ;
79
69
var ids = new HashSet < int > ( ) ;
@@ -102,29 +92,29 @@ public IReadOnlyList<LoggerClass> GetLogClasses(IEnumerable<ClassDeclarationSynt
102
92
continue ;
103
93
}
104
94
95
+ sm ??= _compilation . GetSemanticModel ( classDec . SyntaxTree ) ;
96
+
105
97
foreach ( AttributeListSyntax mal in method . AttributeLists )
106
98
{
107
99
foreach ( AttributeSyntax ma in mal . Attributes )
108
100
{
109
- sm ??= _compilation . GetSemanticModel ( classDec . SyntaxTree ) ;
110
-
111
101
IMethodSymbol attrCtorSymbol = sm . GetSymbolInfo ( ma , _cancellationToken ) . Symbol as IMethodSymbol ;
112
102
if ( attrCtorSymbol == null || ! loggerMessageAttribute . Equals ( attrCtorSymbol . ContainingType , SymbolEqualityComparer . Default ) )
113
103
{
114
104
// badly formed attribute definition, or not the right attribute
115
105
continue ;
116
106
}
117
107
118
- ( int eventId , int ? level , string ? message , string ? eventName ) = ExtractAttributeValues ( ma . ArgumentList ! , sm ) ;
108
+ ( int eventId , int ? level , string message , string ? eventName ) = ExtractAttributeValues ( ma . ArgumentList ! , sm ) ;
119
109
120
110
IMethodSymbol ? methodSymbol = sm . GetDeclaredSymbol ( method , _cancellationToken ) ;
121
111
if ( methodSymbol != null )
122
112
{
123
113
var lm = new LoggerMethod
124
114
{
125
- Name = method . Identifier . ToString ( ) ,
115
+ Name = methodSymbol . Name ,
126
116
Level = level ,
127
- Message = message ?? string . Empty ,
117
+ Message = message ,
128
118
EventId = eventId ,
129
119
EventName = eventName ,
130
120
IsExtensionMethod = methodSymbol . IsExtensionMethod ,
@@ -142,7 +132,7 @@ public IReadOnlyList<LoggerClass> GetLogClasses(IEnumerable<ClassDeclarationSynt
142
132
keepMethod = false ;
143
133
}
144
134
145
- if ( sm . GetTypeInfo ( method . ReturnType ! ) . Type ! . SpecialType != SpecialType . System_Void )
135
+ if ( ! methodSymbol . ReturnsVoid )
146
136
{
147
137
// logging methods must return void
148
138
Diag ( DiagnosticDescriptors . LoggingMethodMustReturnVoid , method . ReturnType . GetLocation ( ) ) ;
@@ -208,38 +198,36 @@ public IReadOnlyList<LoggerClass> GetLogClasses(IEnumerable<ClassDeclarationSynt
208
198
bool foundLogger = false ;
209
199
bool foundException = false ;
210
200
bool foundLogLevel = level != null ;
211
- foreach ( ParameterSyntax p in method . ParameterList . Parameters )
201
+ foreach ( IParameterSymbol paramSymbol in methodSymbol . Parameters )
212
202
{
213
- string paramName = p . Identifier . ToString ( ) ;
203
+ string paramName = paramSymbol . Name ;
214
204
if ( string . IsNullOrWhiteSpace ( paramName ) )
215
205
{
216
206
// semantic problem, just bail quietly
217
207
keepMethod = false ;
218
208
break ;
219
209
}
220
210
221
- IParameterSymbol ? declSymbol = sm . GetDeclaredSymbol ( p ) ;
222
- ITypeSymbol paramSymbol = declSymbol ! . Type ;
223
- if ( paramSymbol is IErrorTypeSymbol )
211
+ ITypeSymbol paramTypeSymbol = paramSymbol ! . Type ;
212
+ if ( paramTypeSymbol is IErrorTypeSymbol )
224
213
{
225
214
// semantic problem, just bail quietly
226
215
keepMethod = false ;
227
216
break ;
228
217
}
229
218
230
- IParameterSymbol declaredType = sm . GetDeclaredSymbol ( p ) ;
231
- string typeName = declaredType ! . Type . ToDisplayString (
219
+ string typeName = paramTypeSymbol . ToDisplayString (
232
220
SymbolDisplayFormat . FullyQualifiedFormat . WithMiscellaneousOptions (
233
221
SymbolDisplayMiscellaneousOptions . IncludeNullableReferenceTypeModifier ) ) ;
234
222
235
223
var lp = new LoggerParameter
236
224
{
237
225
Name = paramName ,
238
226
Type = typeName ,
239
- IsLogger = ! foundLogger && IsBaseOrIdentity ( paramSymbol ! , loggerSymbol ) ,
240
- IsException = ! foundException && IsBaseOrIdentity ( paramSymbol ! , exceptionSymbol ) ,
241
- IsLogLevel = ! foundLogLevel && IsBaseOrIdentity ( paramSymbol ! , logLevelSymbol ) ,
242
- IsEnumerable = IsBaseOrIdentity ( paramSymbol ! , enumerableSymbol ) && ! IsBaseOrIdentity ( paramSymbol ! , stringSymbol ) ,
227
+ IsLogger = ! foundLogger && IsBaseOrIdentity ( paramTypeSymbol ! , loggerSymbol ) ,
228
+ IsException = ! foundException && IsBaseOrIdentity ( paramTypeSymbol ! , exceptionSymbol ) ,
229
+ IsLogLevel = ! foundLogLevel && IsBaseOrIdentity ( paramTypeSymbol ! , logLevelSymbol ) ,
230
+ IsEnumerable = IsBaseOrIdentity ( paramTypeSymbol ! , enumerableSymbol ) && ! IsBaseOrIdentity ( paramTypeSymbol ! , stringSymbol ) ,
243
231
} ;
244
232
245
233
foundLogger |= lp . IsLogger ;
@@ -248,30 +236,30 @@ public IReadOnlyList<LoggerClass> GetLogClasses(IEnumerable<ClassDeclarationSynt
248
236
249
237
if ( lp . IsLogger && lm . TemplateMap . ContainsKey ( paramName ) )
250
238
{
251
- Diag ( DiagnosticDescriptors . ShouldntMentionLoggerInMessage , p . Identifier . GetLocation ( ) , paramName ) ;
239
+ Diag ( DiagnosticDescriptors . ShouldntMentionLoggerInMessage , paramSymbol . Locations [ 0 ] , paramName ) ;
252
240
}
253
241
else if ( lp . IsException && lm . TemplateMap . ContainsKey ( paramName ) )
254
242
{
255
- Diag ( DiagnosticDescriptors . ShouldntMentionExceptionInMessage , p . Identifier . GetLocation ( ) , paramName ) ;
243
+ Diag ( DiagnosticDescriptors . ShouldntMentionExceptionInMessage , paramSymbol . Locations [ 0 ] , paramName ) ;
256
244
}
257
245
else if ( lp . IsLogLevel && lm . TemplateMap . ContainsKey ( paramName ) )
258
246
{
259
- Diag ( DiagnosticDescriptors . ShouldntMentionLogLevelInMessage , p . Identifier . GetLocation ( ) , paramName ) ;
247
+ Diag ( DiagnosticDescriptors . ShouldntMentionLogLevelInMessage , paramSymbol . Locations [ 0 ] , paramName ) ;
260
248
}
261
249
else if ( lp . IsLogLevel && level != null && ! lm . TemplateMap . ContainsKey ( paramName ) )
262
250
{
263
- Diag ( DiagnosticDescriptors . ArgumentHasNoCorrespondingTemplate , p . Identifier . GetLocation ( ) , paramName ) ;
251
+ Diag ( DiagnosticDescriptors . ArgumentHasNoCorrespondingTemplate , paramSymbol . Locations [ 0 ] , paramName ) ;
264
252
}
265
253
else if ( lp . IsTemplateParameter && ! lm . TemplateMap . ContainsKey ( paramName ) )
266
254
{
267
- Diag ( DiagnosticDescriptors . ArgumentHasNoCorrespondingTemplate , p . Identifier . GetLocation ( ) , paramName ) ;
255
+ Diag ( DiagnosticDescriptors . ArgumentHasNoCorrespondingTemplate , paramSymbol . Locations [ 0 ] , paramName ) ;
268
256
}
269
257
270
258
if ( paramName [ 0 ] == '_' )
271
259
{
272
260
// can't have logging method parameter names that start with _ since that can lead to conflicting symbol names
273
261
// because all generated symbols start with _
274
- Diag ( DiagnosticDescriptors . InvalidLoggingMethodParameterName , p . Identifier . GetLocation ( ) ) ;
262
+ Diag ( DiagnosticDescriptors . InvalidLoggingMethodParameterName , paramSymbol . Locations [ 0 ] ) ;
275
263
}
276
264
277
265
lm . AllParameters . Add ( lp ) ;
@@ -427,88 +415,32 @@ public IReadOnlyList<LoggerClass> GetLogClasses(IEnumerable<ClassDeclarationSynt
427
415
return ( loggerField , false ) ;
428
416
}
429
417
430
- private ( int eventId , int ? level , string ? message , string ? eventName ) ExtractAttributeValues ( AttributeArgumentListSyntax args , SemanticModel sm )
418
+ private ( int eventId , int ? level , string message , string ? eventName ) ExtractAttributeValues ( AttributeArgumentListSyntax args , SemanticModel sm )
431
419
{
432
- // two constructor arg shapes:
433
- //
434
- // (eventId, level, message)
435
- // (eventId, message)
436
-
437
420
int eventId = 0 ;
438
421
int ? level = null ;
439
422
string ? eventName = null ;
440
- string ? message = null ;
441
- int numPositional = 0 ;
423
+ string message = string . Empty ;
442
424
foreach ( AttributeArgumentSyntax a in args . Arguments )
443
425
{
444
- if ( a . NameEquals != null )
445
- {
446
- switch ( a . NameEquals . Name . ToString ( ) )
447
- {
448
- case "EventId" :
449
- eventId = ( int ) sm . GetConstantValue ( a . Expression , _cancellationToken ) . Value ! ;
450
- break ;
451
- case "EventName" :
452
- eventName = sm . GetConstantValue ( a . Expression , _cancellationToken ) . ToString ( ) ;
453
- break ;
454
- case "Level" :
455
- level = ( int ) sm . GetConstantValue ( a . Expression , _cancellationToken ) . Value ! ;
456
- break ;
457
- case "Message" :
458
- message = sm . GetConstantValue ( a . Expression , _cancellationToken ) . ToString ( ) ;
459
- break ;
460
- }
461
- }
462
- else if ( a . NameColon != null )
426
+ // argument syntax takes parameters. e.g. EventId = 0
427
+ Debug . Assert ( a . NameEquals != null ) ;
428
+ switch ( a . NameEquals . Name . ToString ( ) )
463
429
{
464
- switch ( a . NameColon . Name . ToString ( ) )
465
- {
466
- case "eventId" :
467
- eventId = ( int ) sm . GetConstantValue ( a . Expression , _cancellationToken ) . Value ! ;
468
- break ;
469
-
470
- case "level" :
471
- level = ( int ) sm . GetConstantValue ( a . Expression , _cancellationToken ) . Value ! ;
472
- break ;
473
-
474
- case "message" :
475
- message = sm . GetConstantValue ( a . Expression , _cancellationToken ) . ToString ( ) ;
476
- break ;
477
- }
478
- }
479
- else
480
- {
481
- switch ( numPositional )
482
- {
483
- // event id
484
- case 0 :
485
- eventId = ( int ) sm . GetConstantValue ( a . Expression , _cancellationToken ) . Value ! ;
486
- break ;
487
-
488
- // log level or message
489
- case 1 :
490
- object o = sm . GetConstantValue ( a . Expression , _cancellationToken ) . Value ! ;
491
- if ( o is int l )
492
- {
493
- level = l ;
494
- }
495
- else
496
- {
497
- message = sm . GetConstantValue ( a . Expression , _cancellationToken ) . ToString ( ) ;
498
- }
499
-
500
- break ;
501
-
502
- // message
503
- case 2 :
504
- message = sm . GetConstantValue ( a . Expression , _cancellationToken ) . ToString ( ) ;
505
- break ;
506
- }
507
-
508
- numPositional ++ ;
430
+ case "EventId" :
431
+ eventId = ( int ) sm . GetConstantValue ( a . Expression , _cancellationToken ) . Value ! ;
432
+ break ;
433
+ case "EventName" :
434
+ eventName = sm . GetConstantValue ( a . Expression , _cancellationToken ) . ToString ( ) ;
435
+ break ;
436
+ case "Level" :
437
+ level = ( int ) sm . GetConstantValue ( a . Expression , _cancellationToken ) . Value ! ;
438
+ break ;
439
+ case "Message" :
440
+ message = sm . GetConstantValue ( a . Expression , _cancellationToken ) . ToString ( ) ;
441
+ break ;
509
442
}
510
443
}
511
-
512
444
return ( eventId , level , message , eventName ) ;
513
445
}
514
446
@@ -528,7 +460,7 @@ private bool IsBaseOrIdentity(ITypeSymbol source, ITypeSymbol dest)
528
460
/// <summary>
529
461
/// Finds the template arguments contained in the message string.
530
462
/// </summary>
531
- private static void ExtractTemplates ( string ? message , IDictionary < string , string > templateMap , IList < string > templateList )
463
+ private static void ExtractTemplates ( string ? message , IDictionary < string , string > templateMap , ICollection < string > templateList )
532
464
{
533
465
if ( string . IsNullOrEmpty ( message ) )
534
466
{
@@ -613,6 +545,21 @@ private static int FindIndexOfAny(string message, char[] chars, int startIndex,
613
545
int findIndex = message . IndexOfAny ( chars , startIndex , endIndex - startIndex ) ;
614
546
return findIndex == - 1 ? endIndex : findIndex ;
615
547
}
548
+
549
+ private string GetStringExpression ( SemanticModel sm , SyntaxNode expr )
550
+ {
551
+ Optional < object ? > optional = sm . GetConstantValue ( expr , _cancellationToken ) ;
552
+ if ( optional . HasValue )
553
+ {
554
+ object o = optional . Value ;
555
+ if ( o != null )
556
+ {
557
+ return o . ToString ( ) ;
558
+ }
559
+ }
560
+
561
+ return string . Empty ;
562
+ }
616
563
}
617
564
618
565
/// <summary>
@@ -642,7 +589,7 @@ internal class LoggerMethod
642
589
public string ? EventName ;
643
590
public bool IsExtensionMethod ;
644
591
public string Modifiers = string . Empty ;
645
- public string LoggerField ;
592
+ public string LoggerField = string . Empty ;
646
593
}
647
594
648
595
/// <summary>
0 commit comments