Common.cs 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362
  1. using System.Linq;
  2. using Microsoft.CodeAnalysis;
  3. using Microsoft.CodeAnalysis.CSharp.Syntax;
  4. using Microsoft.CodeAnalysis.Diagnostics;
  5. namespace Godot.SourceGenerators
  6. {
  7. public static class Common
  8. {
  9. public static void ReportNonPartialGodotScriptClass(
  10. GeneratorExecutionContext context,
  11. ClassDeclarationSyntax cds, INamedTypeSymbol symbol
  12. )
  13. {
  14. string message =
  15. "Missing partial modifier on declaration of type '" +
  16. $"{symbol.FullQualifiedNameOmitGlobal()}' which is a subclass of '{GodotClasses.Object}'";
  17. string description = $"{message}. Subclasses of '{GodotClasses.Object}' " +
  18. "must be declared with the partial modifier.";
  19. context.ReportDiagnostic(Diagnostic.Create(
  20. new DiagnosticDescriptor(id: "GD0001",
  21. title: message,
  22. messageFormat: message,
  23. category: "Usage",
  24. DiagnosticSeverity.Error,
  25. isEnabledByDefault: true,
  26. description),
  27. cds.GetLocation(),
  28. cds.SyntaxTree.FilePath));
  29. }
  30. public static void ReportNonPartialGodotScriptOuterClass(
  31. GeneratorExecutionContext context,
  32. TypeDeclarationSyntax outerTypeDeclSyntax
  33. )
  34. {
  35. var outerSymbol = context.Compilation
  36. .GetSemanticModel(outerTypeDeclSyntax.SyntaxTree)
  37. .GetDeclaredSymbol(outerTypeDeclSyntax);
  38. string fullQualifiedName = outerSymbol is INamedTypeSymbol namedTypeSymbol ?
  39. namedTypeSymbol.FullQualifiedNameOmitGlobal() :
  40. "type not found";
  41. string message =
  42. $"Missing partial modifier on declaration of type '{fullQualifiedName}', " +
  43. $"which contains one or more subclasses of '{GodotClasses.Object}'";
  44. string description = $"{message}. Subclasses of '{GodotClasses.Object}' and their " +
  45. "containing types must be declared with the partial modifier.";
  46. context.ReportDiagnostic(Diagnostic.Create(
  47. new DiagnosticDescriptor(id: "GD0002",
  48. title: message,
  49. messageFormat: message,
  50. category: "Usage",
  51. DiagnosticSeverity.Error,
  52. isEnabledByDefault: true,
  53. description),
  54. outerTypeDeclSyntax.GetLocation(),
  55. outerTypeDeclSyntax.SyntaxTree.FilePath));
  56. }
  57. public static void ReportExportedMemberIsStatic(
  58. GeneratorExecutionContext context,
  59. ISymbol exportedMemberSymbol
  60. )
  61. {
  62. var locations = exportedMemberSymbol.Locations;
  63. var location = locations.FirstOrDefault(l => l.SourceTree != null) ?? locations.FirstOrDefault();
  64. bool isField = exportedMemberSymbol is IFieldSymbol;
  65. string message = $"Attempted to export static {(isField ? "field" : "property")}: " +
  66. $"'{exportedMemberSymbol.ToDisplayString()}'";
  67. string description = $"{message}. Only instance fields and properties can be exported." +
  68. " Remove the 'static' modifier or the '[Export]' attribute.";
  69. context.ReportDiagnostic(Diagnostic.Create(
  70. new DiagnosticDescriptor(id: "GD0101",
  71. title: message,
  72. messageFormat: message,
  73. category: "Usage",
  74. DiagnosticSeverity.Error,
  75. isEnabledByDefault: true,
  76. description),
  77. location,
  78. location?.SourceTree?.FilePath));
  79. }
  80. public static void ReportExportedMemberTypeNotSupported(
  81. GeneratorExecutionContext context,
  82. ISymbol exportedMemberSymbol
  83. )
  84. {
  85. var locations = exportedMemberSymbol.Locations;
  86. var location = locations.FirstOrDefault(l => l.SourceTree != null) ?? locations.FirstOrDefault();
  87. bool isField = exportedMemberSymbol is IFieldSymbol;
  88. string message = $"The type of the exported {(isField ? "field" : "property")} " +
  89. $"is not supported: '{exportedMemberSymbol.ToDisplayString()}'";
  90. string description = $"{message}. Use a supported type or remove the '[Export]' attribute.";
  91. context.ReportDiagnostic(Diagnostic.Create(
  92. new DiagnosticDescriptor(id: "GD0102",
  93. title: message,
  94. messageFormat: message,
  95. category: "Usage",
  96. DiagnosticSeverity.Error,
  97. isEnabledByDefault: true,
  98. description),
  99. location,
  100. location?.SourceTree?.FilePath));
  101. }
  102. public static void ReportExportedMemberIsReadOnly(
  103. GeneratorExecutionContext context,
  104. ISymbol exportedMemberSymbol
  105. )
  106. {
  107. var locations = exportedMemberSymbol.Locations;
  108. var location = locations.FirstOrDefault(l => l.SourceTree != null) ?? locations.FirstOrDefault();
  109. bool isField = exportedMemberSymbol is IFieldSymbol;
  110. string message = $"The exported {(isField ? "field" : "property")} " +
  111. $"is read-only: '{exportedMemberSymbol.ToDisplayString()}'";
  112. string description = isField ?
  113. $"{message}. Exported fields cannot be read-only." :
  114. $"{message}. Exported properties must be writable.";
  115. context.ReportDiagnostic(Diagnostic.Create(
  116. new DiagnosticDescriptor(id: "GD0103",
  117. title: message,
  118. messageFormat: message,
  119. category: "Usage",
  120. DiagnosticSeverity.Error,
  121. isEnabledByDefault: true,
  122. description),
  123. location,
  124. location?.SourceTree?.FilePath));
  125. }
  126. public static void ReportExportedMemberIsWriteOnly(
  127. GeneratorExecutionContext context,
  128. ISymbol exportedMemberSymbol
  129. )
  130. {
  131. var locations = exportedMemberSymbol.Locations;
  132. var location = locations.FirstOrDefault(l => l.SourceTree != null) ?? locations.FirstOrDefault();
  133. string message = $"The exported property is write-only: '{exportedMemberSymbol.ToDisplayString()}'";
  134. string description = $"{message}. Exported properties must be readable.";
  135. context.ReportDiagnostic(Diagnostic.Create(
  136. new DiagnosticDescriptor(id: "GD0104",
  137. title: message,
  138. messageFormat: message,
  139. category: "Usage",
  140. DiagnosticSeverity.Error,
  141. isEnabledByDefault: true,
  142. description),
  143. location,
  144. location?.SourceTree?.FilePath));
  145. }
  146. public static void ReportExportedMemberIsIndexer(
  147. GeneratorExecutionContext context,
  148. ISymbol exportedMemberSymbol
  149. )
  150. {
  151. var locations = exportedMemberSymbol.Locations;
  152. var location = locations.FirstOrDefault(l => l.SourceTree != null) ?? locations.FirstOrDefault();
  153. string message = $"Attempted to export indexer property: " +
  154. $"'{exportedMemberSymbol.ToDisplayString()}'";
  155. string description = $"{message}. Indexer properties can't be exported." +
  156. " Remove the '[Export]' attribute.";
  157. context.ReportDiagnostic(Diagnostic.Create(
  158. new DiagnosticDescriptor(id: "GD0105",
  159. title: message,
  160. messageFormat: message,
  161. category: "Usage",
  162. DiagnosticSeverity.Error,
  163. isEnabledByDefault: true,
  164. description),
  165. location,
  166. location?.SourceTree?.FilePath));
  167. }
  168. public static void ReportSignalDelegateMissingSuffix(
  169. GeneratorExecutionContext context,
  170. INamedTypeSymbol delegateSymbol)
  171. {
  172. var locations = delegateSymbol.Locations;
  173. var location = locations.FirstOrDefault(l => l.SourceTree != null) ?? locations.FirstOrDefault();
  174. string message = "The name of the delegate must end with 'EventHandler': " +
  175. delegateSymbol.ToDisplayString() +
  176. $". Did you mean '{delegateSymbol.Name}EventHandler'?";
  177. string description = $"{message}. Rename the delegate accordingly or remove the '[Signal]' attribute.";
  178. context.ReportDiagnostic(Diagnostic.Create(
  179. new DiagnosticDescriptor(id: "GD0201",
  180. title: message,
  181. messageFormat: message,
  182. category: "Usage",
  183. DiagnosticSeverity.Error,
  184. isEnabledByDefault: true,
  185. description),
  186. location,
  187. location?.SourceTree?.FilePath));
  188. }
  189. public static void ReportSignalParameterTypeNotSupported(
  190. GeneratorExecutionContext context,
  191. IParameterSymbol parameterSymbol)
  192. {
  193. var locations = parameterSymbol.Locations;
  194. var location = locations.FirstOrDefault(l => l.SourceTree != null) ?? locations.FirstOrDefault();
  195. string message = "The parameter of the delegate signature of the signal " +
  196. $"is not supported: '{parameterSymbol.ToDisplayString()}'";
  197. string description = $"{message}. Use supported types only or remove the '[Signal]' attribute.";
  198. context.ReportDiagnostic(Diagnostic.Create(
  199. new DiagnosticDescriptor(id: "GD0202",
  200. title: message,
  201. messageFormat: message,
  202. category: "Usage",
  203. DiagnosticSeverity.Error,
  204. isEnabledByDefault: true,
  205. description),
  206. location,
  207. location?.SourceTree?.FilePath));
  208. }
  209. public static void ReportSignalDelegateSignatureMustReturnVoid(
  210. GeneratorExecutionContext context,
  211. INamedTypeSymbol delegateSymbol)
  212. {
  213. var locations = delegateSymbol.Locations;
  214. var location = locations.FirstOrDefault(l => l.SourceTree != null) ?? locations.FirstOrDefault();
  215. string message = "The delegate signature of the signal " +
  216. $"must return void: '{delegateSymbol.ToDisplayString()}'";
  217. string description = $"{message}. Return void or remove the '[Signal]' attribute.";
  218. context.ReportDiagnostic(Diagnostic.Create(
  219. new DiagnosticDescriptor(id: "GD0203",
  220. title: message,
  221. messageFormat: message,
  222. category: "Usage",
  223. DiagnosticSeverity.Error,
  224. isEnabledByDefault: true,
  225. description),
  226. location,
  227. location?.SourceTree?.FilePath));
  228. }
  229. public static readonly DiagnosticDescriptor GenericTypeArgumentMustBeVariantRule =
  230. new DiagnosticDescriptor(id: "GD0301",
  231. title: "The generic type argument must be a Variant compatible type",
  232. messageFormat: "The generic type argument must be a Variant compatible type: {0}",
  233. category: "Usage",
  234. DiagnosticSeverity.Error,
  235. isEnabledByDefault: true,
  236. "The generic type argument must be a Variant compatible type. Use a Variant compatible type as the generic type argument.");
  237. public static void ReportGenericTypeArgumentMustBeVariant(
  238. SyntaxNodeAnalysisContext context,
  239. SyntaxNode typeArgumentSyntax,
  240. ISymbol typeArgumentSymbol)
  241. {
  242. string message = "The generic type argument " +
  243. $"must be a Variant compatible type: '{typeArgumentSymbol.ToDisplayString()}'";
  244. string description = $"{message}. Use a Variant compatible type as the generic type argument.";
  245. context.ReportDiagnostic(Diagnostic.Create(
  246. new DiagnosticDescriptor(id: "GD0301",
  247. title: message,
  248. messageFormat: message,
  249. category: "Usage",
  250. DiagnosticSeverity.Error,
  251. isEnabledByDefault: true,
  252. description),
  253. typeArgumentSyntax.GetLocation(),
  254. typeArgumentSyntax.SyntaxTree.FilePath));
  255. }
  256. public static readonly DiagnosticDescriptor GenericTypeParameterMustBeVariantAnnotatedRule =
  257. new DiagnosticDescriptor(id: "GD0302",
  258. title: "The generic type parameter must be annotated with the MustBeVariant attribute",
  259. messageFormat: "The generic type argument must be a Variant type: {0}",
  260. category: "Usage",
  261. DiagnosticSeverity.Error,
  262. isEnabledByDefault: true,
  263. "The generic type argument must be a Variant type. Use a Variant type as the generic type argument.");
  264. public static void ReportGenericTypeParameterMustBeVariantAnnotated(
  265. SyntaxNodeAnalysisContext context,
  266. SyntaxNode typeArgumentSyntax,
  267. ISymbol typeArgumentSymbol)
  268. {
  269. string message = "The generic type parameter must be annotated with the MustBeVariant attribute";
  270. string description = $"{message}. Add the MustBeVariant attribute to the generic type parameter.";
  271. context.ReportDiagnostic(Diagnostic.Create(
  272. new DiagnosticDescriptor(id: "GD0302",
  273. title: message,
  274. messageFormat: message,
  275. category: "Usage",
  276. DiagnosticSeverity.Error,
  277. isEnabledByDefault: true,
  278. description),
  279. typeArgumentSyntax.GetLocation(),
  280. typeArgumentSyntax.SyntaxTree.FilePath));
  281. }
  282. public static readonly DiagnosticDescriptor TypeArgumentParentSymbolUnhandledRule =
  283. new DiagnosticDescriptor(id: "GD0303",
  284. title: "The generic type parameter must be annotated with the MustBeVariant attribute",
  285. messageFormat: "The generic type argument must be a Variant type: {0}",
  286. category: "Usage",
  287. DiagnosticSeverity.Error,
  288. isEnabledByDefault: true,
  289. "The generic type argument must be a Variant type. Use a Variant type as the generic type argument.");
  290. public static void ReportTypeArgumentParentSymbolUnhandled(
  291. SyntaxNodeAnalysisContext context,
  292. SyntaxNode typeArgumentSyntax,
  293. ISymbol parentSymbol)
  294. {
  295. string message = $"Symbol '{parentSymbol.ToDisplayString()}' parent of a type argument " +
  296. "that must be Variant compatible was not handled.";
  297. string description = $"{message}. Handle type arguments that are children of the unhandled symbol type.";
  298. context.ReportDiagnostic(Diagnostic.Create(
  299. new DiagnosticDescriptor(id: "GD0303",
  300. title: message,
  301. messageFormat: message,
  302. category: "Usage",
  303. DiagnosticSeverity.Error,
  304. isEnabledByDefault: true,
  305. description),
  306. typeArgumentSyntax.GetLocation(),
  307. typeArgumentSyntax.SyntaxTree.FilePath));
  308. }
  309. }
  310. }