protected Program RewriteFile( string relativePath, Address addrBase, Func <IServiceProvider, IProcessorArchitecture, IPlatform> mkPlatform) { var sc = new ServiceContainer(); PopulateServiceContainer(sc); var arch = new X86ArchitectureReal(sc, "x86-real-16", new Dictionary <string, object>()); ILoader ldr = new Loader(sc); var program = ldr.AssembleExecutable( ImageLocation.FromUri(FileUnitTester.MapTestPath(relativePath)), new X86TextAssembler(arch), null, addrBase); program.Platform = mkPlatform(sc, program.Architecture); var ep = ImageSymbol.Procedure(arch, program.SegmentMap.BaseAddress); var project = new Project { Programs = { program } }; var scan = new Scanner( program, project.LoadedMetadata, new DynamicLinker(project, program, eventListener), sc); scan.EnqueueImageSymbol(ep, true); scan.ScanImage(); var dynamicLinker = new DynamicLinker(project, program, eventListener); var dfa = new DataFlowAnalysis(program, dynamicLinker, sc); dfa.AnalyzeProgram(); return(program); }
private void Build(Program prog) { DataFlowAnalysis dfa = new DataFlowAnalysis(prog, new FakeDecompilerEventListener()); dfa.UntangleProcedures(); foreach (Procedure proc in prog.Procedures.Values) { Aliases alias = new Aliases(proc, prog.Architecture); alias.Transform(); var gr = proc.CreateBlockDominatorGraph(); SsaTransform sst = new SsaTransform(dfa.ProgramDataFlow, proc, gr); SsaState ssa = sst.SsaState; ConditionCodeEliminator cce = new ConditionCodeEliminator(ssa.Identifiers, prog.Platform); cce.Transform(); DeadCode.Eliminate(proc, ssa); var vp = new ValuePropagator(prog.Architecture, ssa.Identifiers, proc); vp.Transform(); DeadCode.Eliminate(proc, ssa); Coalescer coa = new Coalescer(proc, ssa); coa.Transform(); DeadCode.Eliminate(proc, ssa); LiveCopyInserter lci = new LiveCopyInserter(proc, ssa.Identifiers); lci.Transform(); WebBuilder web = new WebBuilder(proc, ssa.Identifiers, new Dictionary <Identifier, LinearInductionVariable>()); web.Transform(); ssa.ConvertBack(false); } }
protected void DumpProcedureFlows(Program program, DataFlowAnalysis dfa, RegisterLiveness live, TextWriter w) { foreach (Procedure proc in program.Procedures.Values) { w.WriteLine("// {0} /////////////////////", proc.Name); ProcedureFlow flow = dfa.ProgramDataFlow[proc]; DataFlow.EmitRegisters(program.Architecture, "\tLiveOut: ", flow.grfLiveOut, flow.LiveOut, w); w.WriteLine(); DataFlow.EmitRegisters(program.Architecture, "\tMayUseIn: ", flow.grfMayUse, flow.MayUse, w); w.WriteLine(); DataFlow.EmitRegisters(program.Architecture, "\tBypassIn: ", flow.grfMayUse, flow.ByPass, w); w.WriteLine(); DataFlow.EmitRegisters(program.Architecture, "\tTrashed: ", flow.grfTrashed, flow.TrashedRegisters, w); w.WriteLine(); DataFlow.EmitRegisters(program.Architecture, "\tPreserved:", flow.grfPreserved, flow.PreservedRegisters, w); w.WriteLine(); w.WriteLine("// {0}", proc.Name); proc.Signature.Emit(proc.Name, FunctionType.EmitFlags.None, new TextFormatter(w)); w.WriteLine(); foreach (Block block in proc.SortBlocksByName()) { if (live != null) { var bFlow = dfa.ProgramDataFlow[block]; bFlow.WriteBefore(program.Architecture, w); block.Write(w); bFlow.WriteAfter(program.Architecture, w); w.WriteLine(); } else { block.Write(w); } } } }
public void AnalysisOnArrayAccess() { var source = @" public class Sample { public void Foo() { int[] outerArray = new int[10] { 0, 1, 2, 3, 4, 0, 1, 2, 3, 4}; for (int index = 0; index < 10; index++) { int[] innerArray = new int[10] { 0, 1, 2, 3, 4, 0, 1, 2, 3, 4 }; index = index + 2; outerArray[index - 1] = 5; } } }"; var tree = CSharpSyntaxTree.ParseText(source); SemanticModel model = GetSemanticModel(tree); var forStatement = tree.GetRoot().DescendantNodes().OfType <ForStatementSyntax>().Single(); DataFlowAnalysis result = model.AnalyzeDataFlow(forStatement); Trace.WriteLine(source); DumpSymbolArray("Always Assigned", result.AlwaysAssigned); DumpSymbolArray("Written Outside", result.WrittenOutside); DumpSymbolArray("Written Inside", result.WrittenInside); DumpSymbolArray("Read Inside", result.ReadInside); DumpSymbolArray("Variables Declared", result.VariablesDeclared); void DumpSymbolArray(string prefix, ImmutableArray <ISymbol> symbols) { foreach (var symbol in symbols) { Trace.WriteLine($"{prefix}: {symbol}"); } } }
public void AnalyzeRegionDataFlow() { TestCode testCode = new TestCode(@" class C { public void F(int x) { int a; /*start*/ int b; int x, y = 1; { var z = ""a""; } /*end*/ int c; } }"); StatementSyntax firstStatement, lastStatement; testCode.GetStatementsBetweenMarkers(out firstStatement, out lastStatement); DataFlowAnalysis regionDataFlowAnalysis = testCode.SemanticModel.AnalyzeDataFlow(firstStatement, lastStatement); Assert.AreEqual("b,x,y,z", string.Join(",", regionDataFlowAnalysis .VariablesDeclared .Select(symbol => symbol.Name))); }
private void PerformTest(FileUnitTester fut) { DataFlowAnalysis dfa = new DataFlowAnalysis(program, null, new FakeDecompilerEventListener()); dfa.UntangleProcedures(); foreach (Procedure proc in program.Procedures.Values) { Aliases alias = new Aliases(proc, program.Architecture); alias.Transform(); SsaTransform sst = new SsaTransform(dfa.ProgramDataFlow, proc, null, proc.CreateBlockDominatorGraph()); SsaState ssa = sst.SsaState; proc.Write(false, fut.TextWriter); fut.TextWriter.WriteLine(); OutParameterTransformer opt = new OutParameterTransformer(proc, ssa.Identifiers); opt.Transform(); DeadCode.Eliminate(proc, ssa); proc.Write(false, fut.TextWriter); fut.TextWriter.WriteLine("===================="); } }
public void Dfa2_FactorialReg() { pb = new ProgramBuilder(); pb.Add("fact", m => { var sp = m.Register(m.Architecture.StackRegister); var r1 = m.Register(1); var r2 = m.Register(2); var r3 = m.Register(3); var cc = m.Flags(0xF, "cc"); m.Assign(sp, m.Frame.FramePointer); m.Assign(r2, r1); m.Assign(r1, 1); m.Assign(cc, m.Cond(m.ISub(r2, r1))); m.BranchIf(m.Test(ConditionCode.LE, cc), "done"); m.Assign(sp, m.ISub(sp, 4)); m.Store(sp, r2); m.Assign(r1, m.ISub(r2, r1)); m.Call("fact", 0); m.Assign(r2, m.LoadDw(sp)); m.Assign(sp, m.IAdd(sp, 4)); m.Assign(r1, m.IMul(r1, r2)); m.Label("done"); m.Return(); }); var dfa = new DataFlowAnalysis(pb.BuildProgram(), new FakeDecompilerEventListener()); dfa.UntangleProcedures2(); var sExp = @"@@@"; AssertProgram(sExp, pb); }
public static bool IsLocalVariable(this SemanticModel semanticModel, MethodDeclarationSyntax containingMethod, string?variableName) { semanticModel.ThrowOnNull(nameof(semanticModel)); containingMethod.ThrowOnNull(nameof(containingMethod)); if (variableName.IsNullOrWhiteSpace()) { return(false); } if (containingMethod.Body == null) { return(false); } DataFlowAnalysis dataFlowAnalysis = semanticModel.AnalyzeDataFlow(containingMethod.Body); if (dataFlowAnalysis == null || !dataFlowAnalysis.Succeeded) { return(false); } return(dataFlowAnalysis.VariablesDeclared.Any(var => var.Name == variableName)); }
private Program RewriteProgram() { var project = new Project { Programs = { program } }; var scan = new Scanner( program, new Dictionary <Address, ProcedureSignature>(), new ImportResolver(project), new FakeDecompilerEventListener()); foreach (EntryPoint ep in program.EntryPoints) { scan.EnqueueEntryPoint(ep); } scan.ScanImage(); DecompilerEventListener eventListener = new FakeDecompilerEventListener(); DataFlowAnalysis da = new DataFlowAnalysis(program, eventListener); da.AnalyzeProgram(); return(program); }
protected override void RunTest(Program program, TextWriter writer) { DataFlowAnalysis dfa = new DataFlowAnalysis(program, null, new FakeDecompilerEventListener()); dfa.UntangleProcedures(); foreach (Procedure proc in program.Procedures.Values) { Aliases alias = new Aliases(proc, program.Architecture); alias.Transform(); SsaTransform sst = new SsaTransform( dfa.ProgramDataFlow, proc, null, proc.CreateBlockDominatorGraph(), program.Platform.CreateImplicitArgumentRegisters()); SsaState ssa = sst.SsaState; ConditionCodeEliminator cce = new ConditionCodeEliminator(ssa, program.Platform); cce.Transform(); DeadCode.Eliminate(proc, ssa); ssa.Write(writer); proc.Write(false, writer); } }
protected void RunHexTest(string hexFile, string outputFile) { var svc = new ServiceContainer(); var cfg = new FakeDecompilerConfiguration(); svc.AddService <IConfigurationService>(cfg); ILoader ldr = new Loader(svc); var imgLoader = new DchexLoader(FileUnitTester.MapTestPath(hexFile), svc, null); var img = imgLoader.Load(null); var program = new Program(img.Image, img.Image.CreateImageMap(), img.Architecture, img.Platform); var project = new Project { Programs = { program } }; var ep = new EntryPoint(program.Image.BaseAddress, program.Architecture.CreateProcessorState()); var scan = new Scanner(program, new Dictionary <Address, ProcedureSignature>(), new ImportResolver(project), new FakeDecompilerEventListener()); scan.EnqueueEntryPoint(ep); scan.ScanImage(); var dfa = new DataFlowAnalysis(program, new FakeDecompilerEventListener()); dfa.AnalyzeProgram(); RunTest(program, outputFile); }
protected void DumpProcedureFlows(Program program, DataFlowAnalysis dfa, TextWriter w) { foreach (Procedure proc in program.Procedures.Values) { w.WriteLine("// {0} /////////////////////", proc.Name); ProcedureFlow flow = dfa.ProgramDataFlow[proc]; DataFlow.EmitRegisters(program.Architecture, "\tLiveOut: ", flow.grfLiveOut, flow.BitsLiveOut, w); w.WriteLine(); DataFlow.EmitRegisterValues("\tBitsUsed: ", flow.BitsUsed, w); w.WriteLine(); DataFlow.EmitRegisters(program.Architecture, "\tTrashed: ", flow.grfTrashed, flow.Trashed, w); w.WriteLine(); DataFlow.EmitRegisters(program.Architecture, "\tPreserved:", flow.grfPreserved, flow.Preserved, w); w.WriteLine(); w.WriteLine("// {0}", proc.Name); proc.Signature.Emit(proc.Name, FunctionType.EmitFlags.None, new TextFormatter(w)); w.WriteLine(); foreach (Block block in proc.SortBlocksByName()) { block.Write(w); } } }
private void RunTest(string sExp, Program program) { var sc = new ServiceContainer(); sc.AddService(eventListener.Object); var dfa = new DataFlowAnalysis( program, import.Object, sc); var ssts = dfa.RewriteProceduresToSsa(); var uvr = new UnusedOutValuesRemover( program, ssts.Select(sst => sst.SsaState), dfa.ProgramDataFlow, import.Object, eventListener.Object); uvr.Transform(); var sb = new StringWriter(); foreach (var proc in program.Procedures.Values) { proc.Write(false, sb); sb.WriteLine("==="); } var sActual = sb.ToString(); if (sExp != sActual) { Console.WriteLine(sActual); Assert.AreEqual(sExp, sActual); } }
protected Program RewriteFile(string relativePath, Address addrBase) { sc = new ServiceContainer(); var config = new FakeDecompilerConfiguration(); var eventListener = new FakeDecompilerEventListener(); sc.AddService <IConfigurationService>(config); sc.AddService <IDecompiledFileService>(new FakeDecompiledFileService()); sc.AddService <DecompilerEventListener>(eventListener); sc.AddService <IFileSystemService>(new FileSystemServiceImpl()); var arch = new X86ArchitectureReal("x86-real-16"); ILoader ldr = new Loader(sc); var program = ldr.AssembleExecutable( FileUnitTester.MapTestPath(relativePath), new X86TextAssembler(sc, arch), addrBase); program.Platform = new DefaultPlatform(sc, program.Architecture); var ep = ImageSymbol.Procedure(arch, program.SegmentMap.BaseAddress); var project = new Project { Programs = { program } }; var scan = new Scanner( program, new ImportResolver(project, program, eventListener), sc); scan.EnqueueImageSymbol(ep, true); scan.ScanImage(); var importResolver = new ImportResolver(project, program, eventListener); var dfa = new DataFlowAnalysis(program, importResolver, eventListener); dfa.AnalyzeProgram(); return(program); }
public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) { if (!Settings.IsCodeFixEnabled(CodeFixIdentifiers.AssignDefaultValueToOutParameter)) { return; } SyntaxNode root = await context.GetSyntaxRootAsync().ConfigureAwait(false); SyntaxNode node = root .FindNode(context.Span, getInnermostNodeForTie: true)? .FirstAncestorOrSelf(f => f.IsKind(SyntaxKind.MethodDeclaration, SyntaxKind.LocalFunctionStatement) || f is StatementSyntax); Debug.Assert(node != null, $"{nameof(node)} is null"); if (node == null) { return; } foreach (Diagnostic diagnostic in context.Diagnostics) { switch (diagnostic.Id) { case CompilerDiagnosticIdentifiers.OutParameterMustBeAssignedToBeforeControlLeavesCurrentMethod: { SyntaxNode bodyOrExpressionBody = null; StatementSyntax statement = null; if (!node.IsKind(SyntaxKind.LocalFunctionStatement)) { statement = node as StatementSyntax; } if (statement != null) { node = node.FirstAncestor(f => f.IsKind(SyntaxKind.MethodDeclaration, SyntaxKind.LocalFunctionStatement)); } bodyOrExpressionBody = GetBodyOrExpressionBody(node); SemanticModel semanticModel = await context.Document.GetSemanticModelAsync().ConfigureAwait(false); var methodSymbol = (IMethodSymbol)semanticModel.GetDeclaredSymbol(node); DataFlowAnalysis dataFlowAnalysis = AnalyzeDataFlow(bodyOrExpressionBody, semanticModel); // Flow analysis APIs do not work with local functions: https://github.com/dotnet/roslyn/issues/14214 if (!dataFlowAnalysis.Succeeded) { break; } ImmutableArray <ISymbol> alwaysAssigned = dataFlowAnalysis.AlwaysAssigned; foreach (IParameterSymbol parameter in methodSymbol.Parameters) { if (parameter.RefKind == RefKind.Out && !alwaysAssigned.Contains(parameter)) { CodeAction codeAction = CodeAction.Create( $"Assign default value to parameter '{parameter.Name}'", cancellationToken => RefactorAsync(context.Document, node, statement, bodyOrExpressionBody, parameter, semanticModel, cancellationToken), GetEquivalenceKey(diagnostic)); context.RegisterCodeFix(codeAction, diagnostic); } } break; } } } }
internal static string CalcCondExpFuncOptions(DataFlowAnalysis dataFlow, ITypeSymbol typeSym, List <string> inputs, List <string> outputs, bool needFuncInfo) { string returnType = ClassInfo.GetFullName(typeSym); string returnTypeKind = "TypeKind." + typeSym.TypeKind; bool returnTypeIsExtern = !SymbolTable.Instance.IsCs2DslSymbol(typeSym); var sb = new StringBuilder(); sb.AppendFormat("needfuncinfo({0}), rettype(return, {1}, {2}, 0, {3})", needFuncInfo ? "true" : "false", returnType, returnTypeKind, returnTypeIsExtern ? "true" : "false"); foreach (var v in dataFlow.DataFlowsOut) { sb.Append(", "); int refOrOut = 2; if (inputs.Contains(v.Name)) { refOrOut = 1; } var psym = v as IParameterSymbol; var lsym = v as ILocalSymbol; if (null != psym) { var type = ClassInfo.GetFullName(psym.Type); var typeKind = psym.Type.TypeKind.ToString(); bool isExtern = !SymbolTable.Instance.IsCs2DslSymbol(psym.Type); if (string.IsNullOrEmpty(type)) { type = "null"; } sb.AppendFormat("rettype({0}, {1}, {2}, {3}, {4})", v.Name, type, typeKind, refOrOut, isExtern ? "true" : "false"); } else if (null != lsym) { var type = ClassInfo.GetFullName(lsym.Type); var typeKind = lsym.Type.TypeKind.ToString(); bool isExtern = !SymbolTable.Instance.IsCs2DslSymbol(lsym.Type); if (string.IsNullOrEmpty(type)) { type = "null"; } sb.AppendFormat("rettype({0}, {1}, {2}, {3}, {4})", v.Name, type, typeKind, refOrOut, isExtern ? "true" : "false"); } } foreach (var v in dataFlow.DataFlowsIn) { if (v.Name == "this") { continue; } sb.Append(", "); int refOrOut = 0; if (outputs.Contains(v.Name)) { refOrOut = 1; } var psym = v as IParameterSymbol; var lsym = v as ILocalSymbol; if (null != psym) { var type = ClassInfo.GetFullName(psym.Type); var typeKind = psym.Type.TypeKind.ToString(); bool isExtern = !SymbolTable.Instance.IsCs2DslSymbol(psym.Type); if (string.IsNullOrEmpty(type)) { type = "null"; } sb.AppendFormat("paramtype({0}, {1}, {2}, {3}, {4})", v.Name, type, typeKind, refOrOut, isExtern ? "true" : "false"); } else if (null != lsym) { var type = ClassInfo.GetFullName(lsym.Type); var typeKind = lsym.Type.TypeKind.ToString(); bool isExtern = !SymbolTable.Instance.IsCs2DslSymbol(lsym.Type); if (string.IsNullOrEmpty(type)) { type = "null"; } sb.AppendFormat("paramtype({0}, {1}, {2}, {3}, {4})", v.Name, type, typeKind, refOrOut, isExtern ? "true" : "false"); } } foreach (var v in dataFlow.DataFlowsOut) { sb.Append(", "); int refOrOut = 2; if (inputs.Contains(v.Name)) { refOrOut = 1; } var psym = v as IParameterSymbol; var lsym = v as ILocalSymbol; if (null != psym) { var type = ClassInfo.GetFullName(psym.Type); var typeKind = psym.Type.TypeKind.ToString(); bool isExtern = !SymbolTable.Instance.IsCs2DslSymbol(psym.Type); if (string.IsNullOrEmpty(type)) { type = "null"; } sb.AppendFormat("paramtype({0}, {1}, {2}, {3}, {4})", v.Name, type, typeKind, refOrOut, isExtern ? "true" : "false"); } else if (null != lsym) { var type = ClassInfo.GetFullName(lsym.Type); var typeKind = lsym.Type.TypeKind.ToString(); bool isExtern = !SymbolTable.Instance.IsCs2DslSymbol(lsym.Type); if (string.IsNullOrEmpty(type)) { type = "null"; } sb.AppendFormat("paramtype({0}, {1}, {2}, {3}, {4})", v.Name, type, typeKind, refOrOut, isExtern ? "true" : "false"); } } return(sb.ToString()); }
internal string CalcTryUsingFuncOptions(DataFlowAnalysis dataFlow, ControlFlowAnalysis ctrlFlow, List <string> inputs, List <string> outputs) { string returnType = "System.Int32"; string returnTypeKind = "TypeKind.Struct"; bool returnTypeIsExtern = false; var sb = new StringBuilder(); sb.AppendFormat("needfuncinfo({0}), rettype(return, {1}, {2}, 0, true)", NeedFuncInfo ? "true" : "false", returnType, returnTypeKind); if (ctrlFlow.ReturnStatements.Length > 0 && !string.IsNullOrEmpty(ReturnVarName)) { returnType = ClassInfo.GetFullName(SemanticInfo.ReturnType); if (string.IsNullOrEmpty(returnType)) { returnType = "null"; } returnTypeKind = "TypeKind." + SemanticInfo.ReturnType.TypeKind.ToString(); returnTypeIsExtern = !SymbolTable.Instance.IsCs2DslSymbol(SemanticInfo.ReturnType); sb.Append(", "); sb.AppendFormat("rettype({0}, {1}, {2}, 1, {3})", ReturnVarName, returnType, returnTypeKind, returnTypeIsExtern ? "true" : "false"); } foreach (var v in dataFlow.DataFlowsOut) { sb.Append(", "); int refOrOut = 2; if (inputs.Contains(v.Name)) { refOrOut = 1; } var psym = v as IParameterSymbol; var lsym = v as ILocalSymbol; if (null != psym) { var type = ClassInfo.GetFullName(psym.Type); var typeKind = psym.Type.TypeKind.ToString(); bool isExtern = !SymbolTable.Instance.IsCs2DslSymbol(psym.Type); if (string.IsNullOrEmpty(type)) { type = "null"; } sb.AppendFormat("rettype({0}, {1}, {2}, {3}, {4})", v.Name, type, typeKind, refOrOut, isExtern ? "true" : "false"); } else if (null != lsym) { var type = ClassInfo.GetFullName(lsym.Type); var typeKind = lsym.Type.TypeKind.ToString(); bool isExtern = !SymbolTable.Instance.IsCs2DslSymbol(lsym.Type); if (string.IsNullOrEmpty(type)) { type = "null"; } sb.AppendFormat("rettype({0}, {1}, {2}, {3}, {4})", v.Name, type, typeKind, refOrOut, isExtern ? "true" : "false"); } } if (ctrlFlow.ReturnStatements.Length > 0 && !string.IsNullOrEmpty(ReturnVarName)) { sb.Append(", "); sb.AppendFormat("paramtype({0}, {1}, {2}, 1, {3})", ReturnVarName, returnType, returnTypeKind, returnTypeIsExtern ? "true" : "false"); } foreach (var v in dataFlow.DataFlowsIn) { if (v.Name == "this") { continue; } sb.Append(", "); int refOrOut = 0; if (outputs.Contains(v.Name)) { refOrOut = 1; } var psym = v as IParameterSymbol; var lsym = v as ILocalSymbol; if (null != psym) { var type = ClassInfo.GetFullName(psym.Type); var typeKind = psym.Type.TypeKind.ToString(); bool isExtern = !SymbolTable.Instance.IsCs2DslSymbol(psym.Type); if (string.IsNullOrEmpty(type)) { type = "null"; } sb.AppendFormat("paramtype({0}, {1}, {2}, {3}, {4})", v.Name, type, typeKind, refOrOut, isExtern ? "true" : "false"); } else if (null != lsym) { var type = ClassInfo.GetFullName(lsym.Type); var typeKind = lsym.Type.TypeKind.ToString(); bool isExtern = !SymbolTable.Instance.IsCs2DslSymbol(lsym.Type); if (string.IsNullOrEmpty(type)) { type = "null"; } sb.AppendFormat("paramtype({0}, {1}, {2}, {3}, {4})", v.Name, type, typeKind, refOrOut, isExtern ? "true" : "false"); } } foreach (var v in dataFlow.DataFlowsOut) { sb.Append(", "); int refOrOut = 2; if (inputs.Contains(v.Name)) { refOrOut = 1; } var psym = v as IParameterSymbol; var lsym = v as ILocalSymbol; if (null != psym) { var type = ClassInfo.GetFullName(psym.Type); var typeKind = psym.Type.TypeKind.ToString(); bool isExtern = !SymbolTable.Instance.IsCs2DslSymbol(psym.Type); if (string.IsNullOrEmpty(type)) { type = "null"; } sb.AppendFormat("paramtype({0}, {1}, {2}, {3}, {4})", v.Name, type, typeKind, refOrOut, isExtern ? "true" : "false"); } else if (null != lsym) { var type = ClassInfo.GetFullName(lsym.Type); var typeKind = lsym.Type.TypeKind.ToString(); bool isExtern = !SymbolTable.Instance.IsCs2DslSymbol(lsym.Type); if (string.IsNullOrEmpty(type)) { type = "null"; } sb.AppendFormat("paramtype({0}, {1}, {2}, {3}, {4})", v.Name, type, typeKind, refOrOut, isExtern ? "true" : "false"); } } return(sb.ToString()); }
public override void Initialize(AnalysisContext context) { context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); context.RegisterSyntaxNodeAction(c => ExamineNode(c), _targetNodeTypes); void ExamineNode(SyntaxNodeAnalysisContext nodeContext) { if (TryGetSymbols(nodeContext.Node, nodeContext.SemanticModel, out IMethodSymbol methodSymbol, out IParameterSymbol parameterSymbol)) { if (HasNoCaptureAttribute(methodSymbol) || HasNoCaptureAttribute(parameterSymbol)) { IEnumerable <ISymbol> capturedSymbols = GetCapturedSymbols(); if (capturedSymbols.Any()) { Diagnostic diagnostic = Diagnostic.Create ( _descriptor, nodeContext.Node.GetLocation(), methodSymbol.Name, parameterSymbol.Name, String.Join(", ", capturedSymbols.Select(s => s.Name)) ); nodeContext.ReportDiagnostic(diagnostic); } } bool HasNoCaptureAttribute(ISymbol symbol) => symbol.GetAttributes().Any(a => a.AttributeClass.Name == _noCaptureAttributeName); IEnumerable <ISymbol> GetCapturedSymbols() { DataFlowAnalysis dataFlow = nodeContext.SemanticModel.AnalyzeDataFlow(nodeContext.Node); ImmutableArray <ISymbol> capturedSymbols = dataFlow.Captured.IntersectWith(dataFlow.ReadInside); if (capturedSymbols.IsEmpty) { return(Enumerable.Empty <ISymbol>()); } IEnumerable <ISymbol> childDeclared = nodeContext .Node .DescendantNodesAndSelf(descendIntoChildren: _ => true, descendIntoTrivia: false) .Where(child => _targetNodeTypes.Contains(child.Kind())) .Select(child => nodeContext.SemanticModel.AnalyzeDataFlow(child)) .Where(dataFlow => dataFlow.Succeeded) .Select(dataFlow => dataFlow.VariablesDeclared) .Where(declared => declared.Length > 0) .SelectMany(x => x) ; return(capturedSymbols .Except(childDeclared) ); } } } bool TryGetSymbols(SyntaxNode node, SemanticModel semanticModel, out IMethodSymbol method, out IParameterSymbol parameter) { if ( node.Parent is ArgumentSyntax argument && argument.Parent is ArgumentListSyntax argumentList && argumentList.Parent is InvocationExpressionSyntax invocation && semanticModel.GetSingleSymbol(invocation.Expression) is IMethodSymbol methodSymbol ) { method = methodSymbol; parameter = argument.NameColon is NameColonSyntax nameColon ? GetParameterByName() : GetParameterByOrder(); return(true); IParameterSymbol GetParameterByName() => methodSymbol.Parameters.First(p => p.Name == nameColon.Name.Identifier.Text); IParameterSymbol GetParameterByOrder() { int argumentIndex = argumentList.Arguments.IndexOf(argument); /// params[] method invocations might have more arguments than parameters. /// If they do, all extra arguments are actually passed to the last parameter. int parameterIndex = argumentIndex < methodSymbol.Parameters.Length ? argumentIndex : methodSymbol.Parameters.Length - 1; return(methodSymbol.Parameters[parameterIndex]); } }
private void VisitMethods(SyntaxNodeAnalysisContext ctx) { if (!(ctx.Node is CSharpSyntax.ClassDeclarationSyntax node)) { return; } var classSymbol = CSharp.CSharpExtensions.GetDeclaredSymbol(ctx.SemanticModel, node); if (classSymbol == null || !classSymbol.IsDerivedFrom(ControllerNames)) { return; } var methodsWithParameters = node.DescendantNodesAndSelf() .OfType <CSharpSyntax.MethodDeclarationSyntax>() .Where(method => method.ParameterList.Parameters.Any()) .Where(method => method.Modifiers .Any(x => x.IsKind(CSharp.SyntaxKind.PublicKeyword))) .Where(method => ctx.SemanticModel .GetSymbolInfo(method.ReturnType) .Symbol ?.IsType("System.String") == true); foreach (CSharpSyntax.MethodDeclarationSyntax method in methodsWithParameters) { SyntaxList <CSharpSyntax.StatementSyntax> methodStatements = method.Body.Statements; var methodInvocations = method.DescendantNodes() .OfType <CSharpSyntax.InvocationExpressionSyntax>() .ToArray(); if (!methodStatements.Any()) { continue; } DataFlowAnalysis flow = ctx.SemanticModel.AnalyzeDataFlow(methodStatements.First(), methodStatements.Last()); // Returns from the Data Flow Analysis of input data // Dangerous data is: Data passed as a parameter that is also returned as is by the method var inputVariables = flow.DataFlowsIn.Union(flow.VariablesDeclared.Except(flow.AlwaysAssigned)) .Union(flow.WrittenInside) .Intersect(flow.WrittenOutside) .ToArray(); if (!inputVariables.Any()) { continue; } foreach (ISymbol inputVariable in inputVariables) { bool inputVariableIsEncoded = false; foreach (CSharpSyntax.InvocationExpressionSyntax methodInvocation in methodInvocations) { var arguments = methodInvocation.ArgumentList.Arguments; if (!arguments.Any()) { continue; } if (arguments.First().ToString().Contains(inputVariable.Name)) { inputVariableIsEncoded = true; } } if (!inputVariableIsEncoded) { ctx.ReportDiagnostic(Diagnostic.Create(Rule, inputVariable.Locations[0])); break; } } } }
private static bool IsParameterUsed([NotNull] IParameterSymbol parameter, [NotNull] DataFlowAnalysis dataFlowAnalysis) { return(dataFlowAnalysis.ReadInside.Contains(parameter) || dataFlowAnalysis.WrittenInside.Contains(parameter) || dataFlowAnalysis.Captured.Contains(parameter)); }
/// <remarks>Unfortunately the roslyn UnassignedVariablesWalker and all useful collections created from it are internal only /// Other attempts using DataFlowsIn on each reference showed that "DataFlowsIn" even from an uninitialized variable (at least in the case of ints) /// https://github.com/dotnet/roslyn/blob/007022c37c6d21ee100728954bd75113e0dfe4bd/src/Compilers/VisualBasic/Portable/Analysis/FlowAnalysis/UnassignedVariablesWalker.vb#L15 /// It'd be possible to see the result of the diagnostic analysis, but that would miss out value types, which don't cause a warning in VB /// PERF: Assume we'll only be passed one type of data flow analysis (VisualBasicDataFlowAnalysis) /// </remarks> public static IEnumerable <ISymbol> GetVbUnassignedVariables(this DataFlowAnalysis methodFlow) => GetCachedReflectedPropertyDelegate(methodFlow, "UnassignedVariables", ref _vbUnassignedVariables).Cast <ISymbol>();
private void GenerateVariableInfoMap( bool bestEffort, SemanticModel model, DataFlowAnalysis dataFlowAnalysisData, Dictionary <ISymbol, List <SyntaxToken> > symbolMap, out IDictionary <ISymbol, VariableInfo> variableInfoMap, out List <ISymbol> failedVariables) { Contract.ThrowIfNull(model); Contract.ThrowIfNull(dataFlowAnalysisData); variableInfoMap = new Dictionary <ISymbol, VariableInfo>(); failedVariables = new List <ISymbol>(); // create map of each data var capturedMap = new HashSet <ISymbol>(dataFlowAnalysisData.Captured); var dataFlowInMap = new HashSet <ISymbol>(dataFlowAnalysisData.DataFlowsIn); var dataFlowOutMap = new HashSet <ISymbol>(dataFlowAnalysisData.DataFlowsOut); var alwaysAssignedMap = new HashSet <ISymbol>(dataFlowAnalysisData.AlwaysAssigned); var variableDeclaredMap = new HashSet <ISymbol>(dataFlowAnalysisData.VariablesDeclared); var readInsideMap = new HashSet <ISymbol>(dataFlowAnalysisData.ReadInside); var writtenInsideMap = new HashSet <ISymbol>(dataFlowAnalysisData.WrittenInside); var readOutsideMap = new HashSet <ISymbol>(dataFlowAnalysisData.ReadOutside); var writtenOutsideMap = new HashSet <ISymbol>(dataFlowAnalysisData.WrittenOutside); var unsafeAddressTakenMap = new HashSet <ISymbol>(dataFlowAnalysisData.UnsafeAddressTaken); // gather all meaningful symbols for the span. var candidates = new HashSet <ISymbol>(readInsideMap); candidates.UnionWith(writtenInsideMap); candidates.UnionWith(variableDeclaredMap); foreach (var symbol in candidates) { if (symbol.IsThisParameter() || IsInteractiveSynthesizedParameter(symbol)) { continue; } var captured = capturedMap.Contains(symbol); var dataFlowIn = dataFlowInMap.Contains(symbol); var dataFlowOut = dataFlowOutMap.Contains(symbol); var alwaysAssigned = alwaysAssignedMap.Contains(symbol); var variableDeclared = variableDeclaredMap.Contains(symbol); var readInside = readInsideMap.Contains(symbol); var writtenInside = writtenInsideMap.Contains(symbol); var readOutside = readOutsideMap.Contains(symbol); var writtenOutside = writtenOutsideMap.Contains(symbol); var unsafeAddressTaken = unsafeAddressTakenMap.Contains(symbol); // if it is static local, make sure it is not defined inside if (symbol.IsStatic) { dataFlowIn = dataFlowIn && !variableDeclared; } // make sure readoutside is true when dataflowout is true (bug #3790) // when a variable is only used inside of loop, a situation where dataflowout == true and readOutside == false // can happen. but for extract method's point of view, this is not an information that would affect output. // so, here we adjust flags to follow predefined assumption. readOutside = readOutside || dataFlowOut; // make sure data flow out is true when declared inside/written inside/read outside/not written outside are true (bug #6277) dataFlowOut = dataFlowOut || (variableDeclared && writtenInside && readOutside && !writtenOutside); // variable that is declared inside but never referenced outside. just ignore it and move to next one. if (variableDeclared && !dataFlowOut && !readOutside && !writtenOutside) { continue; } // parameter defined inside of the selection (such as lambda parameter) will be ignored (bug # 10964) if (symbol is IParameterSymbol && variableDeclared) { continue; } var type = GetSymbolType(model, symbol); if (type == null) { continue; } // If the variable doesn't have a name, it is invalid. if (symbol.Name.IsEmpty()) { continue; } if (!TryGetVariableStyle( bestEffort, symbolMap, symbol, model, type, captured, dataFlowIn, dataFlowOut, alwaysAssigned, variableDeclared, readInside, writtenInside, readOutside, writtenOutside, unsafeAddressTaken, out var variableStyle)) { Contract.ThrowIfTrue(bestEffort, "Should never fail if bestEffort is true"); failedVariables.Add(symbol); continue; } AddVariableToMap(variableInfoMap, symbol, CreateFromSymbol(model.Compilation, symbol, type, variableStyle, variableDeclared)); } }
private static void AnalyzeSetProcessDelegateMethod(SyntaxNodeAnalysisContext syntaxContext, PXContext pxContext) { var setDelegateInvocation = syntaxContext.Node as InvocationExpressionSyntax; if (!CheckIfDiagnosticIsValid(setDelegateInvocation, syntaxContext, pxContext)) { return; } DataFlowAnalysis dfa = null; switch (setDelegateInvocation.ArgumentList.Arguments[0].Expression) { case IdentifierNameSyntax ins: ISymbol identifierSymbol = syntaxContext.SemanticModel.GetSymbolInfo(ins, syntaxContext.CancellationToken).Symbol; if (identifierSymbol != null && !identifierSymbol.IsStatic) { syntaxContext.ReportDiagnosticWithSuppressionCheck( Diagnostic.Create( Descriptors.PX1008_LongOperationDelegateClosures, setDelegateInvocation.GetLocation()), pxContext.CodeAnalysisSettings); } return; case MemberAccessExpressionSyntax memberAccess when memberAccess.Expression is ElementAccessExpressionSyntax arrayIndexAccess: AnalyzeMemberAccessExpressions(arrayIndexAccess.Expression, syntaxContext, pxContext); return; case MemberAccessExpressionSyntax memberAccess: AnalyzeMemberAccessExpressions(memberAccess.Expression, syntaxContext, pxContext); return; case ConditionalAccessExpressionSyntax conditionalAccess when conditionalAccess.Expression is ElementAccessExpressionSyntax arrayIndexAccess: AnalyzeMemberAccessExpressions(arrayIndexAccess.Expression, syntaxContext, pxContext); return; case ConditionalAccessExpressionSyntax conditionalAccess: AnalyzeMemberAccessExpressions(conditionalAccess.Expression, syntaxContext, pxContext); return; case AnonymousMethodExpressionSyntax anonMethodNode: dfa = syntaxContext.SemanticModel.AnalyzeDataFlow(anonMethodNode); break; case LambdaExpressionSyntax lambdaNode: dfa = syntaxContext.SemanticModel.AnalyzeDataFlow(lambdaNode); break; } if (dfa != null && dfa.DataFlowsIn.OfType <IParameterSymbol>().Any(p => p.IsThis)) { syntaxContext.ReportDiagnosticWithSuppressionCheck( Diagnostic.Create( Descriptors.PX1008_LongOperationDelegateClosures, setDelegateInvocation.GetLocation()), pxContext.CodeAnalysisSettings); } }
private void AnalyzeNode(SyntaxNodeAnalysisContext context) { var localDeclaration = (LocalDeclarationStatementSyntax)context.Node; // make sure the declaration isn't already const: if (localDeclaration.Modifiers.Any(SyntaxKind.ConstKeyword)) { return; } TypeSyntax variableTypeName = localDeclaration.Declaration.Type; ITypeSymbol variableType = context.SemanticModel.GetTypeInfo(variableTypeName, context.CancellationToken).ConvertedType; // Ensure that all variables in the local declaration have initializers that // are assigned with constant values. foreach (VariableDeclaratorSyntax variable in localDeclaration.Declaration.Variables) { EqualsValueClauseSyntax initializer = variable.Initializer; if (initializer == null) { return; } Optional <object> constantValue = context.SemanticModel.GetConstantValue(initializer.Value, context.CancellationToken); if (!constantValue.HasValue) { return; } // Ensure that the initializer value can be converted to the type of the // local declaration without a user-defined conversion. Conversion conversion = context.SemanticModel.ClassifyConversion(initializer.Value, variableType); if (!conversion.Exists || conversion.IsUserDefined) { return; } // Special cases: // * If the constant value is a string, the type of the local declaration // must be System.String. // * If the constant value is null, the type of the local declaration must // be a reference type. if (constantValue.Value is string) { if (variableType.SpecialType != SpecialType.System_String) { return; } } else if (variableType.IsReferenceType && constantValue.Value != null) { return; } } // Perform data flow analysis on the local declaration. DataFlowAnalysis dataFlowAnalysis = context.SemanticModel.AnalyzeDataFlow(localDeclaration); foreach (VariableDeclaratorSyntax variable in localDeclaration.Declaration.Variables) { // Retrieve the local symbol for each variable in the local declaration // and ensure that it is not written outside of the data flow analysis region. ISymbol variableSymbol = context.SemanticModel.GetDeclaredSymbol(variable, context.CancellationToken); if (dataFlowAnalysis.WrittenOutside.Contains(variableSymbol)) { return; } } context.ReportDiagnostic(Diagnostic.Create(Rule, context.Node.GetLocation())); }
private static void Analyze( SyntaxNodeAnalysisContext context, SyntaxNode declaration, ParameterListSyntax parameterList, CSharpSyntaxNode bodyOrExpressionBody) { if (parameterList == null) { return; } if (bodyOrExpressionBody == null) { return; } if (!parameterList.Parameters.Any()) { return; } SemanticModel semanticModel = context.SemanticModel; CancellationToken cancellationToken = context.CancellationToken; var methodSymbol = (IMethodSymbol)semanticModel.GetDeclaredSymbol(declaration, cancellationToken); SyntaxWalker walker = null; foreach (IParameterSymbol parameter in methodSymbol.Parameters) { cancellationToken.ThrowIfCancellationRequested(); ITypeSymbol type = parameter.Type; if (type.Kind == SymbolKind.ErrorType) { continue; } if (CSharpFacts.IsSimpleType(type.SpecialType)) { continue; } if (!type.IsReadOnlyStruct()) { if (parameter.RefKind == RefKind.In && type.TypeKind == TypeKind.Struct) { var parameterSyntax = (ParameterSyntax)parameter.GetSyntax(cancellationToken); Debug.Assert(parameterSyntax.Modifiers.Contains(SyntaxKind.InKeyword), ""); DiagnosticHelpers.ReportDiagnostic(context, DiagnosticDescriptors.DoNotPassNonReadOnlyStructByReadOnlyReference, parameterSyntax.Identifier); } continue; } if (parameter.RefKind != RefKind.None) { continue; } if (walker == null) { if (methodSymbol.ImplementsInterfaceMember(allInterfaces: true)) { break; } walker = SyntaxWalker.GetInstance(); } else if (walker.Parameters.ContainsKey(parameter.Name)) { walker.Parameters.Clear(); break; } walker.Parameters.Add(parameter.Name, parameter); } if (walker == null) { return; } if (walker.Parameters.Count > 0) { walker.SemanticModel = semanticModel; walker.CancellationToken = cancellationToken; if (bodyOrExpressionBody.IsKind(SyntaxKind.Block)) { walker.VisitBlock((BlockSyntax)bodyOrExpressionBody); } else { walker.VisitArrowExpressionClause((ArrowExpressionClauseSyntax)bodyOrExpressionBody); } if (walker.Parameters.Count > 0) { DataFlowAnalysis analysis = (bodyOrExpressionBody.IsKind(SyntaxKind.Block)) ? semanticModel.AnalyzeDataFlow((BlockSyntax)bodyOrExpressionBody) : semanticModel.AnalyzeDataFlow(((ArrowExpressionClauseSyntax)bodyOrExpressionBody).Expression); bool?isReferencedAsMethodGroup = null; foreach (KeyValuePair <string, IParameterSymbol> kvp in walker.Parameters) { var isAssigned = false; foreach (ISymbol assignedSymbol in analysis.AlwaysAssigned) { if (SymbolEqualityComparer.Default.Equals(assignedSymbol, kvp.Value)) { isAssigned = true; break; } } if (isAssigned) { continue; } if (isReferencedAsMethodGroup ??= IsReferencedAsMethodGroup()) { break; } if (kvp.Value.GetSyntaxOrDefault(cancellationToken) is ParameterSyntax parameter) { DiagnosticHelpers.ReportDiagnostic( context, DiagnosticDescriptors.MakeParameterRefReadOnly, parameter.Identifier); } } } } SyntaxWalker.Free(walker); bool IsReferencedAsMethodGroup() { switch (declaration.Kind()) { case SyntaxKind.MethodDeclaration: return(MethodReferencedAsMethodGroupWalker.IsReferencedAsMethodGroup(declaration.Parent, methodSymbol, semanticModel, cancellationToken)); case SyntaxKind.LocalFunctionStatement: return(MethodReferencedAsMethodGroupWalker.IsReferencedAsMethodGroup(declaration.FirstAncestor <MemberDeclarationSyntax>(), methodSymbol, semanticModel, cancellationToken)); default: return(false); } } }
public void Setup() { this.CSignature = null; this.dfa = null; base.platform = null; }
public override async Task RegisterCodeFixesAsync(CodeFixContext context) { Diagnostic diagnostic = context.Diagnostics[0]; if (!Settings.IsEnabled(diagnostic.Id, CodeFixIdentifiers.AssignDefaultValueToOutParameter)) { return; } SyntaxNode root = await context.GetSyntaxRootAsync().ConfigureAwait(false); if (!TryFindFirstAncestorOrSelf( root, context.Span, out SyntaxNode node, predicate: f => f.IsKind(SyntaxKind.MethodDeclaration) || f is StatementSyntax)) { return; } StatementSyntax statement = null; if (!node.IsKind(SyntaxKind.MethodDeclaration, SyntaxKind.LocalFunctionStatement)) { statement = (StatementSyntax)node; node = node.FirstAncestor(f => f.IsKind(SyntaxKind.MethodDeclaration, SyntaxKind.LocalFunctionStatement)); Debug.Assert(node != null, "Containing method or local function not found."); if (node == null) { return; } } SyntaxNode bodyOrExpressionBody = GetBodyOrExpressionBody(node); if (bodyOrExpressionBody == null) { return; } if (bodyOrExpressionBody is BlockSyntax body && body.ContainsYield()) { return; } SemanticModel semanticModel = await context.Document.GetSemanticModelAsync().ConfigureAwait(false); DataFlowAnalysis dataFlowAnalysis = AnalyzeDataFlow(bodyOrExpressionBody, semanticModel); // Flow analysis APIs do not work with local functions: https://github.com/dotnet/roslyn/issues/14214 if (!dataFlowAnalysis.Succeeded) { return; } var methodSymbol = (IMethodSymbol)semanticModel.GetDeclaredSymbol(node); ImmutableArray <IParameterSymbol> parameters = methodSymbol.Parameters; ImmutableArray <ISymbol> alwaysAssigned = dataFlowAnalysis.AlwaysAssigned; IParameterSymbol singleParameter = null; var isAny = false; foreach (IParameterSymbol parameter in parameters) { if (parameter.RefKind == RefKind.Out && !alwaysAssigned.Contains(parameter)) { if (singleParameter == null) { singleParameter = parameter; isAny = true; } else { singleParameter = null; break; } } } Debug.Assert(isAny, "No unassigned 'out' parameter found."); if (!isAny) { return; } CodeAction codeAction = CodeAction.Create( (singleParameter != null) ? $"Assign default value to parameter '{singleParameter.Name}'" : "Assign default value to parameters", cancellationToken => RefactorAsync(context.Document, node, statement, bodyOrExpressionBody, parameters, alwaysAssigned, semanticModel, cancellationToken), base.GetEquivalenceKey(diagnostic)); context.RegisterCodeFix(codeAction, diagnostic); }
public void Dfa2_StackArgs_Nested() { pb = new ProgramBuilder(); pb.Add("main", m => { var r1 = m.Register("r1"); var sp = m.Frame.EnsureRegister(m.Architecture.StackRegister); m.Assign(sp, m.Frame.FramePointer); m.Assign(sp, m.ISub(sp, 4)); m.MStore(sp, m.Mem32(m.Ptr32(0x123400))); m.Call("level1", 4); m.Assign(sp, m.IAdd(sp, 4)); m.MStore(m.Ptr32(0x123404), r1); m.Return(); }); pb.Add("level1", m => { var r1 = m.Register("r1"); var sp = m.Frame.EnsureRegister(m.Architecture.StackRegister); m.Assign(sp, m.Frame.FramePointer); m.Assign(r1, m.Mem32(m.IAdd(sp, 4))); m.Assign(sp, m.ISub(sp, 4)); m.MStore(sp, r1); m.Call("level2", 4); m.Assign(sp, m.IAdd(sp, 4)); m.Return(); }); pb.Add("level2", m => { var r1 = m.Register("r1"); var r2 = m.Register("r2"); var sp = m.Frame.EnsureRegister(m.Architecture.StackRegister); m.Assign(sp, m.Frame.FramePointer); m.Assign(r1, m.Mem32(m.IAdd(sp, 4))); m.Assign(r1, m.IAdd(r1, 1)); m.Return(); }); var program = pb.BuildProgram(); var dfa = new DataFlowAnalysis(program, dynamicLinker.Object, sc); dfa.AnalyzeProgram(); var sExp = #region Expected @"// main // Return size: 0 void main() main_entry: // succ: l1 l1: Mem8[0x00123404<p32>:word32] = level1(Mem0[0x00123400<p32>:word32]) return // succ: main_exit main_exit: === // level1 // Return size: 0 word32 level1(word32 dwArg04) level1_entry: // succ: l1 l1: return level2(dwArg04) // succ: level1_exit level1_exit: === // level2 // Return size: 0 word32 level2(word32 dwArg04) level2_entry: // succ: l1 l1: return dwArg04 + 1<32> // succ: level2_exit level2_exit: "; #endregion AssertProgram(sExp, pb.Program); }
private void GetLocalAndType(DataFlowAnalysis df, List<ISymbol> list) { foreach (var v in df.VariablesDeclared) { list.Add((Symbol)v); var local = v as LocalSymbol; if (local != null && (local.Type.Kind == SymbolKind.ArrayType || local.Type.Kind == SymbolKind.PointerType)) { list.Add(local.Type); } } }
private void ProcessEmitVertex(InvocationExpressionSyntax node) { var model = GetModel(node); DataFlowAnalysis analysis = null; if (_lastVertexEmit is ExpressionSyntax expression) { analysis = model.AnalyzeDataFlow(expression); } else if (_lastVertexEmit is StatementSyntax lastStatement) { var emitStatement = node.Ancestors().OfType <StatementSyntax>().FirstOrDefault(); if (lastStatement.Parent != emitStatement.Parent) { lastStatement = emitStatement.Parent.ChildNodes().Cast <StatementSyntax>().First(); } analysis = model.AnalyzeDataFlow(lastStatement, emitStatement); _lastVertexEmit = emitStatement; } var emit = model.GetSymbolInfo(node.ArgumentList.Arguments.Last().Expression); if (!(emit.Symbol is IFieldSymbol emitParam)) { throw new ShaderGenerationException("EmitVertex argument must be a field"); } var emitAttribute = emit.Symbol.GetAttributes().FirstOrDefault(a => a.AttributeClass.Name.Contains("EmitVertex")); if (emitAttribute == null) { throw new ShaderGenerationException("EmitVertex arguments must be marked with EmitVertexAttribute"); } int streamId = 0; if (node.ArgumentList.Arguments.Count > 1) { streamId = (int)model.GetConstantValue(node.ArgumentList.Arguments.First()).Value; } var assignments = analysis.AlwaysAssigned.Concat(_writeFields); if (!_emittedData.TryGetValue(streamId, out var emissions)) { emissions = new HashSet <ISymbol>(assignments.OfType <IFieldSymbol>(), SymbolEqualityComparer.Default); _emittedData.Add(streamId, emissions); if (assignments.Contains(emitParam)) { emissions.Add(emitParam); } } if (analysis != null) { foreach (var emission in emissions) { if (!assignments.Contains(emission) && !assignments.Contains(emission.ContainingType)) { var type = (emission as IFieldSymbol)?.Type ?? (emission as IParameterSymbol)?.Type; if (type == null || !type.GetMembers().OfType <IFieldSymbol>().All(assignments.Contains)) { throw new ShaderGenerationException($"{emission.Name} must be fully assigned before every call to EmitVertex"); } } } foreach (var assignment in assignments.OfType <IFieldSymbol>().Append <ISymbol>(emitParam).Where(a => !emissions.Contains(a))) { var type = (assignment as IFieldSymbol)?.Type ?? (assignment as IParameterSymbol)?.Type; if (type == null || !type.GetMembers().OfType <IFieldSymbol>().All(emissions.Contains)) { throw new ShaderGenerationException($"{assignment.Name} must be fully assigned before every call to EmitVertex"); } } } _writeFields.Clear(); }
protected void VisitMethods(SyntaxNodeAnalysisContext ctx) { if (!(ctx.Node is VBSyntax.ClassBlockSyntax node)) { return; } var classSymbol = VB.VisualBasicExtensions.GetDeclaredSymbol(ctx.SemanticModel, node); if (classSymbol == null || !classSymbol.IsDerivedFrom("Microsoft.AspNetCore.Mvc.Controller", "System.Web.Mvc.Controller")) { return; } var methodsWithParameters = node.DescendantNodesAndSelf() .OfType <VBSyntax.MethodBlockSyntax>() .Where(method => method.SubOrFunctionStatement.ParameterList.Parameters.Any()) .Where(method => method .SubOrFunctionStatement .Modifiers.Any(x => x.IsKind(VB.SyntaxKind.PublicKeyword))) .Where(method => { var retType = method.SubOrFunctionStatement.AsClause?.Type; if (retType == null) { return(false); } return(ctx.SemanticModel .GetSymbolInfo(retType) .Symbol ?.IsType("System.String") == true); }); foreach (VBSyntax.MethodBlockSyntax method in methodsWithParameters) { SyntaxList <VBSyntax.StatementSyntax> methodStatements = method.Statements; var methodInvocations = method.DescendantNodes() .OfType <VBSyntax.InvocationExpressionSyntax>() .ToArray(); if (!methodStatements.Any()) { continue; } DataFlowAnalysis flow = ctx.SemanticModel.AnalyzeDataFlow(methodStatements.First(), methodStatements.Last()); // Returns from the Data Flow Analysis of sensible data // Sensible data is: Data passed as a parameter that is also returned as is by the method var sensibleVariables = flow.DataFlowsIn.Union(flow.VariablesDeclared.Except(flow.AlwaysAssigned)) .Union(flow.WrittenInside) .Intersect(flow.WrittenOutside) .ToArray(); if (!sensibleVariables.Any()) { continue; } foreach (ISymbol sensibleVariable in sensibleVariables) { bool sensibleVariableIsEncoded = false; foreach (VBSyntax.InvocationExpressionSyntax methodInvocation in methodInvocations) { var arguments = methodInvocation.ArgumentList.Arguments; if (!arguments.Any()) { continue; } if (arguments.First().ToString().Contains(sensibleVariable.Name)) { sensibleVariableIsEncoded = true; } } if (!sensibleVariableIsEncoded) { ctx.ReportDiagnostic(Diagnostic.Create(Rule, method.GetLocation())); } } } }