public RunnerGenerator(GeneratorExecutionContext context, string targetFramework) { Context = context; TargetFramework = targetFramework; context.AnalyzerConfigOptions.GlobalOptions.TryGetValue("build_property.ApplicationId", out var applicationId); context.Log($"ApplicationId: {applicationId}"); ApplicationId = applicationId ?? throw new Exception("ApplicationId needs to be set."); context.AnalyzerConfigOptions.GlobalOptions.TryGetValue("build_property.ApplicationTitle", out var applicationTitle); context.Log($"ApplicationTitle: {applicationTitle}"); ApplicationTitle = applicationTitle ?? "Tests"; context.AnalyzerConfigOptions.GlobalOptions.TryGetValue("build_property.RootNamespace", out var rootNamespace); context.Log($"RootNamespace: {rootNamespace}"); RootNamespace = rootNamespace ?? "TestRunnerNamespace"; ContainsSplashScreen = false; foreach (var file in context.AdditionalFiles) { var options = context.AnalyzerConfigOptions.GetOptions(file); if (options.TryGetValue("build_metadata.AdditionalFiles.IsMauiSplashScreen", out var isMauiSplashScreen) && bool.TryParse(isMauiSplashScreen, out var isSplash) && isSplash) { ContainsSplashScreen = true; break; } } context.Log($"ContainsSplashScreen: {ContainsSplashScreen}"); }
public void Execute(GeneratorExecutionContext context) { if (!context.AnalyzerConfigOptions.GlobalOptions.TryGetValue("build_property.TargetFramework", out var targetFramework)) { return; } context.Log($"TargetFramework: {targetFramework}"); var generator = new RunnerGenerator(context, targetFramework); generator?.Generate(); }
public void Execute(GeneratorExecutionContext context) { // retrieve the populated receiver if (!(context.SyntaxReceiver is RecursionSyntaxReceiver receiver)) { return; } var compilation = context.Compilation; if ("C#" != compilation.Language) { context.ReportUnsupportedLanguageError(compilation.Language); return; } var goodCandidates = receiver.CandidateMethods.Select(methodSyntax => { var methodModel = compilation.GetSemanticModel(methodSyntax.SyntaxTree); var methodSymbol = methodModel.GetDeclaredSymbol(methodSyntax); var attr = methodSymbol.GetAttributes().SingleOrDefault(a => a.AttributeClass.MatchesRuntimeType(TargetAttribute)); var codeGenProps = (attr != null) ? SafeRecursionCodeGenProps.ParseFromAttribute(context, methodSyntax, attr) : null; return(new RecursiveMethodInfo(methodSyntax, methodSymbol, methodModel, codeGenProps)); }) .Where(t => t.CodeGenProps != null) .GroupBy(t => t.CodeGenProps.MutualRecursionId ?? "") //convert null to empty string for GroupBy .ToDictionary(g => g.Key, g => g.ToList()); if (!goodCandidates.TryGetValue("", out var simpleCandidates)) { simpleCandidates = new List <RecursiveMethodInfo>(); } context.Log($"Found {simpleCandidates.Count} method(s) for simple recursion and {goodCandidates.Count - 1} method group(s) for mutual recursion generation."); foreach (var methodInfo in simpleCandidates) { try { var generatedCode = CalculatorClassGenerator.GenerateSimpleSafeRec(context, methodInfo); if (generatedCode != null) { context.AddSource($"{methodInfo.MethodSymbol.ContainingType.Name}_{methodInfo.MethodSymbol.Name}_SafeRecursion.cs", SourceText.From(generatedCode, Encoding.UTF8)); } } catch (Exception e) { context.LogInternalError(methodInfo.MethodSyntax.GetLocation(), $"Processing '{methodInfo.MethodSymbol.ContainingType.Name}' resulted in an internal error '{e}'"); } } foreach (var(recursionId, methodInfos) in goodCandidates) { // skip the simple candidates if (string.IsNullOrEmpty(recursionId)) { continue; } var someMethodInfo = methodInfos.First(); try { var generatedCode = CalculatorClassGenerator.GenerateMutualSafeRec(context, methodInfos); if (generatedCode != null) { context.AddSource($"{someMethodInfo.MethodSymbol.ContainingType.Name}_{recursionId}_SafeRecursion.cs", SourceText.From(generatedCode, Encoding.UTF8)); } } catch (Exception e) { context.LogInternalError(someMethodInfo.MethodSyntax.Parent.GetLocation(), $"Processing '{recursionId}' mutual recursion resulted in an internal error '{e}'"); } } }
internal string GenerateCalculatorMethodBody(TargetMethodsInfo targetMethods) { var code = new CodeBuilder(); // unpack params code.AddHeaderLine("\t\t// unpack params"); code.AddHeaderLine(string.Join("\n", _methodInfo.MethodSyntax.ParameterList.Parameters.Select(parameterSyntax => { var paramSymbol = _methodInfo.MethodModel.GetDeclaredSymbol(parameterSyntax); return($"\t\tvar {paramSymbol.Name} = {ParamsVarName}.{paramSymbol.Name};"); }))); // find all recursive invocations var nodesToReplace = CollectInvocationsWalker.CollectInvocations(_context, _methodInfo, targetMethods); if (nodesToReplace.ContainsCriticalFailure) { _context.Log(_methodInfo.MethodSyntax.GetLocation(), $"An unsupported code was found for '{_methodInfo.MethodSymbol.Name}'. Abort processing."); return(null); } _context.Log(_methodInfo.MethodSyntax.GetLocation(), $"'{_methodInfo.MethodSymbol.Name}':" + $" {nodesToReplace.VoidCalls.Count} void call(s)" + $" {nodesToReplace.Assignments.Count} assignment(s)" + $", {nodesToReplace.DeclarationAndAssignments.Count} var declaration(s)" + $", {nodesToReplace.ReturnsRecursive.Count} recursive call(s) in return(s)" + $", {nodesToReplace.Returns.Count} return(s)"); // process all supported kinds of recursive invocations var allNodesToReplace = nodesToReplace.Returns.Cast <StatementSyntax>() .Concat(nodesToReplace.ReturnsRecursive) .Concat(nodesToReplace.VoidCalls.Select(t => t.containingStatement)) .Concat(nodesToReplace.Assignments.Select((t) => t.containingStatement)) .Concat(nodesToReplace.DeclarationAndAssignments.Select(t => t.containingStatement)); var newRoot = _methodInfo.MethodSyntax.Body.ReplaceNodes(allNodesToReplace, (origNode, curNode) => { // _context.Log("!! " + origNode.Kind() + " " + origNode.ToFullStringTrimmed()); switch (origNode.Kind()) { case SyntaxKind.ReturnStatement: return(ReplaceReturn(targetMethods, (ReturnStatementSyntax)origNode, (ReturnStatementSyntax)curNode)); case SyntaxKind.ExpressionStatement: var origExpressionNode = (ExpressionStatementSyntax)origNode; var expression = origExpressionNode.Expression; switch (expression.Kind()) { case SyntaxKind.InvocationExpression: return(ReplaceVoidCall(targetMethods, origExpressionNode, (ExpressionStatementSyntax)curNode)); case SyntaxKind.SimpleAssignmentExpression: return(ReplaceAssignment(targetMethods, origExpressionNode, (ExpressionStatementSyntax)curNode)); default: throw new AntiSOGenException($"Unexpected expression kind {expression.Kind()} to replace {origNode.ToFullStringTrimmed()}"); } case SyntaxKind.LocalDeclarationStatement: return(ReplaceLocalVarDeclrAssignment(targetMethods, (LocalDeclarationStatementSyntax)origNode, (LocalDeclarationStatementSyntax)curNode)); default: throw new AntiSOGenException($"Unexpected statement kind {origNode.Kind()} to replace {origNode.ToFullStringTrimmed()}"); } }); code.AddHeaderLine("\t\t// method body"); code.AddHeader(newRoot.ToFullString()); return(code.BuildString()); }