private void AnalyzeExpression(SyntaxNodeAnalysisContext context, ConcurrentDictionary <int, Diagnostic> reports) { var parentKind = context.Node.Parent.Kind(); // will be handled at higher level if (parentKind == SyntaxKind.SimpleMemberAccessExpression || parentKind == SyntaxKind.QualifiedName) { return; } var target = GetTargetOfNode(context.Node, context.SemanticModel); if (target == null) { return; } var platform = Analyzer.GetPlatformForSymbol(target); // Some quick escapes if (platform.Kind == PlatformKind.Unchecked) { return; } if (platform.Kind == PlatformKind.Uwp && platform.Version == Analyzer.N2SDKVersion) { return; } // Is this expression inside a method/constructor/property that claims to be specific? var containingBlock = context.Node.FirstAncestorOrSelf <BlockSyntax>(); // for constructors and methods MemberDeclarationSyntax containingMember = containingBlock?.FirstAncestorOrSelf <BaseMethodDeclarationSyntax>(); if (containingBlock == null || containingBlock?.Parent is AccessorDeclarationSyntax) { containingMember = context.Node.FirstAncestorOrSelf <PropertyDeclarationSyntax>(); } // Is this invocation properly guarded? See readme.md for explanations. if (IsProperlyGuarded(context.Node, context.SemanticModel)) { return; } if (containingBlock != null) { foreach (var ret in containingBlock.DescendantNodes().OfType <ReturnStatementSyntax>()) { if (IsProperlyGuarded(ret, context.SemanticModel)) { return; } } } // We'll report only a single diagnostic per line, the first. var loc = context.Node.GetLocation(); if (!loc.IsInSource) { return; } var line = loc.GetLineSpan().StartLinePosition.Line; if (reports.TryGetValue(line, out var diagnostic) && diagnostic.Location.SourceSpan.Start <= loc.SourceSpan.Start) { return; } diagnostic = Diagnostic.Create(platform.Kind == PlatformKind.Uwp ? Analyzer.VersionRule : Analyzer.PlatformRule, loc); reports[line] = diagnostic; context.ReportDiagnostic(diagnostic); }
/// <summary> /// returns instance of <see cref="HowToGuard"/> for <see cref="ISymbol"/> /// </summary> /// <param name="target">instance of <see cref="ISymbol"/></param> /// <returns>instance of <see cref="HowToGuard"/></returns> public static HowToGuard GetGuardForSymbol(ISymbol target) { var plat = Analyzer.GetPlatformForSymbol(target); switch (plat.Kind) { case PlatformKind.ExtensionSDK: return(new HowToGuard() { TypeToCheck = target.Kind == SymbolKind.NamedType ? target.ToDisplayString() : target.ContainingType.ToDisplayString(), KindOfCheck = "IsTypePresent" }); case PlatformKind.Uwp: if (target.Kind == SymbolKind.NamedType) { return(new HowToGuard() { TypeToCheck = target.ToDisplayString(), KindOfCheck = "IsTypePresent" }); } else { var g = new HowToGuard { TypeToCheck = target.ContainingType.ToDisplayString() }; var d0 = Analyzer.GetUniversalApiAdditions(Analyzer.N0DifferencesRes); var d1 = Analyzer.GetUniversalApiAdditions(Analyzer.N1DifferencesRes); if (!d0.TryGetValue(g.TypeToCheck, out List <NewMember> newMembers)) { d1.TryGetValue(g.TypeToCheck, out newMembers); } if (newMembers == null) { throw new InvalidOperationException("oops! expected this UWP version API to be in the dictionary of new things"); } g.MemberToCheck = target.Name; if (target.Kind == SymbolKind.Field) { // the only fields in WinRT are enum fields g.KindOfCheck = "IsEnumNamedValuePresent"; } else if (target.Kind == SymbolKind.Event) { g.KindOfCheck = "IsEventPresent"; } else if (target.Kind == SymbolKind.Property) { // TODO: if SDK starts introducing additional accessors on properties, we'll have to change this g.KindOfCheck = "IsPropertyPresent"; } else if (target.Kind == SymbolKind.Method) { g.KindOfCheck = "IsMethodPresent"; if (target.Kind == SymbolKind.Method && plat.ByParameterCount) { g.ParameterCountToCheck = (target as IMethodSymbol).Parameters.Length; } } return(g); } default: throw new InvalidOperationException("oops! don't know why I was asked to check something that's fine"); } }