Common.cs 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388
  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.GodotObject}'";
  17. string description = $"{message}. Subclasses of '{GodotClasses.GodotObject}' " +
  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.GodotObject}'";
  44. string description = $"{message}. Subclasses of '{GodotClasses.GodotObject}' 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 ReportExportedMemberIsExplicitInterfaceImplementation(
  169. GeneratorExecutionContext context,
  170. ISymbol exportedMemberSymbol
  171. )
  172. {
  173. var locations = exportedMemberSymbol.Locations;
  174. var location = locations.FirstOrDefault(l => l.SourceTree != null) ?? locations.FirstOrDefault();
  175. string message = $"Attempted to export explicit interface property implementation: " +
  176. $"'{exportedMemberSymbol.ToDisplayString()}'";
  177. string description = $"{message}. Explicit interface implementations can't be exported." +
  178. " Remove the '[Export]' attribute.";
  179. context.ReportDiagnostic(Diagnostic.Create(
  180. new DiagnosticDescriptor(id: "GD0106",
  181. title: message,
  182. messageFormat: message,
  183. category: "Usage",
  184. DiagnosticSeverity.Error,
  185. isEnabledByDefault: true,
  186. description),
  187. location,
  188. location?.SourceTree?.FilePath));
  189. }
  190. public static void ReportSignalDelegateMissingSuffix(
  191. GeneratorExecutionContext context,
  192. INamedTypeSymbol delegateSymbol)
  193. {
  194. var locations = delegateSymbol.Locations;
  195. var location = locations.FirstOrDefault(l => l.SourceTree != null) ?? locations.FirstOrDefault();
  196. string message = "The name of the delegate must end with 'EventHandler': " +
  197. delegateSymbol.ToDisplayString() +
  198. $". Did you mean '{delegateSymbol.Name}EventHandler'?";
  199. string description = $"{message}. Rename the delegate accordingly or remove the '[Signal]' attribute.";
  200. context.ReportDiagnostic(Diagnostic.Create(
  201. new DiagnosticDescriptor(id: "GD0201",
  202. title: message,
  203. messageFormat: message,
  204. category: "Usage",
  205. DiagnosticSeverity.Error,
  206. isEnabledByDefault: true,
  207. description),
  208. location,
  209. location?.SourceTree?.FilePath));
  210. }
  211. public static void ReportSignalParameterTypeNotSupported(
  212. GeneratorExecutionContext context,
  213. IParameterSymbol parameterSymbol)
  214. {
  215. var locations = parameterSymbol.Locations;
  216. var location = locations.FirstOrDefault(l => l.SourceTree != null) ?? locations.FirstOrDefault();
  217. string message = "The parameter of the delegate signature of the signal " +
  218. $"is not supported: '{parameterSymbol.ToDisplayString()}'";
  219. string description = $"{message}. Use supported types only or remove the '[Signal]' attribute.";
  220. context.ReportDiagnostic(Diagnostic.Create(
  221. new DiagnosticDescriptor(id: "GD0202",
  222. title: message,
  223. messageFormat: message,
  224. category: "Usage",
  225. DiagnosticSeverity.Error,
  226. isEnabledByDefault: true,
  227. description),
  228. location,
  229. location?.SourceTree?.FilePath));
  230. }
  231. public static void ReportSignalDelegateSignatureMustReturnVoid(
  232. GeneratorExecutionContext context,
  233. INamedTypeSymbol delegateSymbol)
  234. {
  235. var locations = delegateSymbol.Locations;
  236. var location = locations.FirstOrDefault(l => l.SourceTree != null) ?? locations.FirstOrDefault();
  237. string message = "The delegate signature of the signal " +
  238. $"must return void: '{delegateSymbol.ToDisplayString()}'";
  239. string description = $"{message}. Return void or remove the '[Signal]' attribute.";
  240. context.ReportDiagnostic(Diagnostic.Create(
  241. new DiagnosticDescriptor(id: "GD0203",
  242. title: message,
  243. messageFormat: message,
  244. category: "Usage",
  245. DiagnosticSeverity.Error,
  246. isEnabledByDefault: true,
  247. description),
  248. location,
  249. location?.SourceTree?.FilePath));
  250. }
  251. public static readonly DiagnosticDescriptor GenericTypeArgumentMustBeVariantRule =
  252. new DiagnosticDescriptor(id: "GD0301",
  253. title: "The generic type argument must be a Variant compatible type",
  254. messageFormat: "The generic type argument must be a Variant compatible type: {0}",
  255. category: "Usage",
  256. DiagnosticSeverity.Error,
  257. isEnabledByDefault: true,
  258. "The generic type argument must be a Variant compatible type. Use a Variant compatible type as the generic type argument.");
  259. public static void ReportGenericTypeArgumentMustBeVariant(
  260. SyntaxNodeAnalysisContext context,
  261. SyntaxNode typeArgumentSyntax,
  262. ISymbol typeArgumentSymbol)
  263. {
  264. string message = "The generic type argument " +
  265. $"must be a Variant compatible type: '{typeArgumentSymbol.ToDisplayString()}'";
  266. string description = $"{message}. Use a Variant compatible type as the generic type argument.";
  267. context.ReportDiagnostic(Diagnostic.Create(
  268. new DiagnosticDescriptor(id: "GD0301",
  269. title: message,
  270. messageFormat: message,
  271. category: "Usage",
  272. DiagnosticSeverity.Error,
  273. isEnabledByDefault: true,
  274. description),
  275. typeArgumentSyntax.GetLocation(),
  276. typeArgumentSyntax.SyntaxTree.FilePath));
  277. }
  278. public static readonly DiagnosticDescriptor GenericTypeParameterMustBeVariantAnnotatedRule =
  279. new DiagnosticDescriptor(id: "GD0302",
  280. title: "The generic type parameter must be annotated with the MustBeVariant attribute",
  281. messageFormat: "The generic type argument must be a Variant type: {0}",
  282. category: "Usage",
  283. DiagnosticSeverity.Error,
  284. isEnabledByDefault: true,
  285. "The generic type argument must be a Variant type. Use a Variant type as the generic type argument.");
  286. public static void ReportGenericTypeParameterMustBeVariantAnnotated(
  287. SyntaxNodeAnalysisContext context,
  288. SyntaxNode typeArgumentSyntax,
  289. ISymbol typeArgumentSymbol)
  290. {
  291. string message = "The generic type parameter must be annotated with the MustBeVariant attribute";
  292. string description = $"{message}. Add the MustBeVariant attribute to the generic type parameter.";
  293. context.ReportDiagnostic(Diagnostic.Create(
  294. new DiagnosticDescriptor(id: "GD0302",
  295. title: message,
  296. messageFormat: message,
  297. category: "Usage",
  298. DiagnosticSeverity.Error,
  299. isEnabledByDefault: true,
  300. description),
  301. typeArgumentSyntax.GetLocation(),
  302. typeArgumentSyntax.SyntaxTree.FilePath));
  303. }
  304. public static readonly DiagnosticDescriptor TypeArgumentParentSymbolUnhandledRule =
  305. new DiagnosticDescriptor(id: "GD0303",
  306. title: "The generic type parameter must be annotated with the MustBeVariant attribute",
  307. messageFormat: "The generic type argument must be a Variant type: {0}",
  308. category: "Usage",
  309. DiagnosticSeverity.Error,
  310. isEnabledByDefault: true,
  311. "The generic type argument must be a Variant type. Use a Variant type as the generic type argument.");
  312. public static void ReportTypeArgumentParentSymbolUnhandled(
  313. SyntaxNodeAnalysisContext context,
  314. SyntaxNode typeArgumentSyntax,
  315. ISymbol parentSymbol)
  316. {
  317. string message = $"Symbol '{parentSymbol.ToDisplayString()}' parent of a type argument " +
  318. "that must be Variant compatible was not handled.";
  319. string description = $"{message}. Handle type arguments that are children of the unhandled symbol type.";
  320. context.ReportDiagnostic(Diagnostic.Create(
  321. new DiagnosticDescriptor(id: "GD0303",
  322. title: message,
  323. messageFormat: message,
  324. category: "Usage",
  325. DiagnosticSeverity.Error,
  326. isEnabledByDefault: true,
  327. description),
  328. typeArgumentSyntax.GetLocation(),
  329. typeArgumentSyntax.SyntaxTree.FilePath));
  330. }
  331. }
  332. }