private async Task <Document> MakeParameterAsync(Document document, VariableDeclaratorSyntax typeDecl, CancellationToken cancellationToken) { var root = await document.GetSyntaxRootAsync(cancellationToken); string varKeyword = string.Empty, varName = string.Empty; //Predefined variable type (string, int, etc...) var preDefType = typeDecl.Parent.ChildNodes().OfType <PredefinedTypeSyntax>().FirstOrDefault(); if (preDefType != null) { varKeyword = preDefType.Keyword.ToString(); var varDecl = typeDecl.Parent.ChildNodes().OfType <VariableDeclaratorSyntax>().FirstOrDefault(); varName = varDecl?.Identifier.ToString(); } else //var { var identName = typeDecl.Parent.ChildNodes().OfType <IdentifierNameSyntax>().FirstOrDefault(); //Access the semantic model to determine actual type var model = document.GetSemanticModelAsync().Result; var type = model.GetTypeInfo(identName).Type; varKeyword = type.ToMinimalDisplayString(model, typeDecl.SpanStart); var varDecl = typeDecl.Parent.ChildNodes().OfType <VariableDeclaratorSyntax>().FirstOrDefault(); varName = varDecl?.Identifier.ToString(); } MethodDeclarationSyntax mds = typeDecl.Ancestors().OfType <MethodDeclarationSyntax>().FirstOrDefault(); //Add the existing and new parameters ParameterSyntax ps = SyntaxFactory.Parameter(SyntaxFactory.Identifier(string.Concat(varKeyword, " ", varName))); var newList = SyntaxFactory.ParameterList(SyntaxFactory.SeparatedList <ParameterSyntax>().AddRange( mds.ParameterList.ChildNodes().OfType <ParameterSyntax>()).Add(ps)); var methodNode = typeDecl.Ancestors().OfType <MethodDeclarationSyntax>().FirstOrDefault(); var variableNode = typeDecl.Parent.Parent; var variableParentNode = typeDecl.Parent.Parent.Parent; //Track the nodes we'll be using var newRoot = root.TrackNodes(methodNode, variableNode, variableParentNode); //Remode/replace the variable declaration var trackedVariableParentNode = newRoot.GetCurrentNode(variableParentNode); var trackedVariableNode = newRoot.GetCurrentNode(variableNode); var newVariableParentNode = trackedVariableParentNode.RemoveNode(trackedVariableNode, SyntaxRemoveOptions.KeepNoTrivia); newRoot = newRoot.ReplaceNode(trackedVariableParentNode, newVariableParentNode); //Replace the method parameters var trackedMethodNode = newRoot.GetCurrentNode(methodNode); var newMethodNode = trackedMethodNode.ReplaceNode(trackedMethodNode.ParameterList, newList); newRoot = newRoot.ReplaceNode(trackedMethodNode, newMethodNode); return(document.WithSyntaxRoot(newRoot)); }
public override void VisitVariableDeclarator(VariableDeclaratorSyntax node) { if (node.Ancestors().Any(x => x is FunctionDefinitionSyntax)) CreateTag(node.Identifier, _classificationService.LocalVariableIdentifier); else if (node.Ancestors().Any(x => x is TypeDefinitionSyntax)) CreateTag(node.Identifier, _classificationService.FieldIdentifier); else CreateTag(node.Identifier, _classificationService.GlobalVariableIdentifier); base.VisitVariableDeclarator(node); }
private async Task<Document> MakeParameterAsync(Document document, VariableDeclaratorSyntax typeDecl, CancellationToken cancellationToken) { var root = await document.GetSyntaxRootAsync(cancellationToken); string varKeyword = string.Empty, varName = string.Empty; //Predefined variable type (string, int, etc...) var preDefType = typeDecl.Parent.ChildNodes().OfType<PredefinedTypeSyntax>().FirstOrDefault(); if (preDefType != null) { varKeyword = preDefType.Keyword.ToString(); var varDecl = typeDecl.Parent.ChildNodes().OfType<VariableDeclaratorSyntax>().FirstOrDefault(); varName = varDecl?.Identifier.ToString(); } else //var { var identName = typeDecl.Parent.ChildNodes().OfType<IdentifierNameSyntax>().FirstOrDefault(); //Access the semantic model to determine actual type var model = document.GetSemanticModelAsync().Result; var type = model.GetTypeInfo(identName).Type; varKeyword = type.ToMinimalDisplayString(model, typeDecl.SpanStart); var varDecl = typeDecl.Parent.ChildNodes().OfType<VariableDeclaratorSyntax>().FirstOrDefault(); varName = varDecl?.Identifier.ToString(); } MethodDeclarationSyntax mds = typeDecl.Ancestors().OfType<MethodDeclarationSyntax>().FirstOrDefault(); //Add the existing and new parameters ParameterSyntax ps = SyntaxFactory.Parameter(SyntaxFactory.Identifier(string.Concat(varKeyword, " ", varName))); var newList = SyntaxFactory.ParameterList(SyntaxFactory.SeparatedList<ParameterSyntax>().AddRange( mds.ParameterList.ChildNodes().OfType<ParameterSyntax>()).Add(ps)); var methodNode = typeDecl.Ancestors().OfType<MethodDeclarationSyntax>().FirstOrDefault(); var variableNode = typeDecl.Parent.Parent; var variableParentNode = typeDecl.Parent.Parent.Parent; //Track the nodes we'll be using var newRoot = root.TrackNodes(methodNode, variableNode, variableParentNode); //Remode/replace the variable declaration var trackedVariableParentNode = newRoot.GetCurrentNode(variableParentNode); var trackedVariableNode = newRoot.GetCurrentNode(variableNode); var newVariableParentNode = trackedVariableParentNode.RemoveNode(trackedVariableNode, SyntaxRemoveOptions.KeepNoTrivia); newRoot = newRoot.ReplaceNode(trackedVariableParentNode, newVariableParentNode); //Replace the method parameters var trackedMethodNode = newRoot.GetCurrentNode(methodNode); var newMethodNode = trackedMethodNode.ReplaceNode(trackedMethodNode.ParameterList, newList); newRoot = newRoot.ReplaceNode(trackedMethodNode, newMethodNode); return document.WithSyntaxRoot(newRoot); }
private static bool HasMutableUsagesInMethod(VariableDeclaratorSyntax parameter, ISymbol parameterSymbol, SemanticModel semanticModel) { var methodSyntax = parameter?.Ancestors()?.FirstOrDefault(IsMethodLike); if (methodSyntax == null) { return(false); } return(methodSyntax .DescendantNodes() .OfType <IdentifierNameSyntax>() .Where(MatchesIdentifier) .Any(IsMutatingUse)); bool IsMethodLike(SyntaxNode arg) => arg is BaseMethodDeclarationSyntax || arg is IndexerDeclarationSyntax || arg is AccessorDeclarationSyntax; bool MatchesIdentifier(IdentifierNameSyntax id) { var symbol = semanticModel.GetSymbolInfo(id).Symbol; return(Equals(parameterSymbol, symbol)); } }
public override void VisitVariableDeclarator(VariableDeclaratorSyntax node) { if (node.Ancestors().Any(x => x is FunctionDefinitionSyntax)) { CreateTag(node.Identifier, _classificationService.LocalVariableIdentifier); } else if (node.Ancestors().Any(x => x is TypeDefinitionSyntax)) { CreateTag(node.Identifier, _classificationService.FieldIdentifier); } else { CreateTag(node.Identifier, _classificationService.GlobalVariableIdentifier); } base.VisitVariableDeclarator(node); }
private static Diag GetDiagnostic(VariableDeclaratorSyntax syntax, ContractCategory contractCategory) { var fieldDeclarationSyntax = syntax?.Ancestors().OfType <FieldDeclarationSyntax>().FirstOrDefault(); if (fieldDeclarationSyntax?.AttributeLists.ContainsNotNullAttribute() != false) { return(null); } return(new Diag(fieldDeclarationSyntax.GetLocation(), syntax.ToString(), contractCategory)); }
private static bool HasMutableUsagesInMethod(VariableDeclaratorSyntax variable, ISymbol variableSymbol, SemanticModel semanticModel) { var methodSyntax = variable?.Ancestors()?.FirstOrDefault(IsMethodLike); if (methodSyntax == null) { return(false); } return(methodSyntax .DescendantNodes() .OfType <IdentifierNameSyntax>() .Where(MatchesIdentifier) .Any(IsMutatingUse));
private static CompilationUnitSyntax AddInvariant(CompilationUnitSyntax root, SemanticModel semanticModel, [NotNull] VariableDeclaratorSyntax variableSyntax) { var classDeclaration = variableSyntax.Ancestors().OfType <ClassDeclarationSyntax>().FirstOrDefault(); var invariantMethod = classDeclaration?.ChildNodes() .OfType <MethodDeclarationSyntax>() .FirstOrDefault(m => m.AttributeLists.ContainsAttribute("ContractInvariantMethod")); if (invariantMethod == null) { return(root); } var statements = new [] { SyntaxFactory.ParseStatement($" Contract.Invariant({variableSyntax.Identifier.Text} != null);\r\n") }; return(root.ReplaceNode(invariantMethod, invariantMethod.AddBodyStatements(statements))); }
public override SyntaxNode VisitVariableDeclarator(VariableDeclaratorSyntax node) { var id = node.Identifier.ValueText; if (node.Ancestors().Any(anc => anc is FieldDeclarationSyntax)) { return(base.VisitVariableDeclarator(node)); } if (!identities.ContainsKey(id)) { identities[id] = 1; return(base.VisitVariableDeclarator(node)); } ++identities[id]; return(base.VisitVariableDeclarator(node.WithIdentifier(Identifier(id)))); }
private static IEnumerable <ConstructorDeclarationSyntax> GetConstructors(VariableDeclaratorSyntax fieldVariable) { var classDeclaration = fieldVariable.Ancestors().OfType <ClassDeclarationSyntax>().SingleOrDefault(); if (classDeclaration == null) { return(new List <ConstructorDeclarationSyntax>()); } var constructors = classDeclaration.DescendantNodes().OfType <ConstructorDeclarationSyntax>(); var relevantConstructors = constructors.Where(x => x.Body .DescendantNodes() .OfType <IdentifierNameSyntax>() .All(identifier => identifier.Identifier.Text != fieldVariable.Identifier.Text)); return(relevantConstructors); }
private void Analyze(SyntaxNodeAnalysisContext context) { if (!IsCaseWeUnderstand(context.Node)) { return; } // Collect some items we'll use repeatedly if (context.Node.Parent != null) { VariableDeclaratorSyntax variableDeclaratorSyntax = context.Node.Parent.Parent as VariableDeclaratorSyntax; if (variableDeclaratorSyntax != null) { BlockSyntax blockOfInterest = variableDeclaratorSyntax.Ancestors().OfType <BlockSyntax>().First(); var model = context.SemanticModel; ISymbol ourSymbol = model.GetDeclaredSymbol(variableDeclaratorSyntax); // Identify where y is first used (ie., MemberAccessExpression) IdentifierNameSyntax identifierNameSyntax = GetFirstMemberAccess(ourSymbol, model, blockOfInterest); if (identifierNameSyntax == null) { return; } if (!SameBlock(blockOfInterest, identifierNameSyntax)) { return; } // Evaluate the code between (ie after) "y = x as yType" and "y.Foo" (ie before) to see if y is read (ie checked for null) or written to (ie rendering our check moot) (StatementSyntax firstStatementOfAnalysis, int firstStatementOfAnalysisIndex) = GetStatement(blockOfInterest, variableDeclaratorSyntax, offset: 1); (StatementSyntax lastStatementOfAnalysis, int lastStatementOfAnalysisIndex) = GetStatement(blockOfInterest, identifierNameSyntax, offset: -1); if (lastStatementOfAnalysisIndex < firstStatementOfAnalysisIndex) { // There's nothing to analyze; they immediately used the symbol after the 'as' // Before reporting an error, note common possibility that the situation could be: // string y = obj as string; // if (y != null && y.ToString() == @"") // Ie there's nothing to analyze between the statements, but within the statement exists a check if (firstStatementOfAnalysis is IfStatementSyntax ifStatementSyntax) { if (ifStatementSyntax.Condition.ToString().Contains(@"null")) { // There's an "if" statement with a likely null check of some kind in some order. Don't be too picky, just let it go to minimize risk of a false positive return; } } if (firstStatementOfAnalysis is WhileStatementSyntax whileStatementSyntax) { if (whileStatementSyntax.Condition.ToString().Contains(@"null")) { // There's an "if" statement with a likely null check of some kind in some order. Don't be too picky, just let it go to minimize risk of a false positive return; } } Report(context, identifierNameSyntax); return; } bool ourSymbolIsReadOrWritten = false; DataFlowAnalysis result = model.AnalyzeDataFlow(firstStatementOfAnalysis, lastStatementOfAnalysis); if (result != null) { foreach (ISymbol assignedValue in result.ReadInside) { if (SymbolEqualityComparer.Default.Equals(assignedValue, ourSymbol)) { // We shouldn't just be checking that we read our symbol; we should really see if it's checked for null ourSymbolIsReadOrWritten = true; break; } } foreach (ISymbol assignedValue in result.WrittenInside) { if (SymbolEqualityComparer.Default.Equals(assignedValue, ourSymbol)) { ourSymbolIsReadOrWritten = true; break; } } } if (!ourSymbolIsReadOrWritten) { Report(context, identifierNameSyntax); return; } } } }