示例#1
0
        private static bool UnaccountedForFieldExists(LazinatorPairInformation lazinatorPairInformation, out List <FieldDeclarationSyntax> problemFields)
        {
            var fields = lazinatorPairInformation.LazinatorObjectLocationsExcludingCodeBehind.Select(x =>
            {
                bool success = x.SourceTree.TryGetRoot(out SyntaxNode root);
                if (success)
                {
                    return(root);
                }
                return(null);
            }
                                                                                                     )
                         .Where(x => x != null)
                         .SelectMany(x => x.DescendantNodes().OfType <FieldDeclarationSyntax>());
            var fieldsWithoutNonserializedAttribute = fields
                                                      .Where(x => !x.Modifiers.Any(y => y.Kind() == SyntaxKind.StaticKeyword || y.Kind() == SyntaxKind.ConstKeyword))
                                                      .Where(x => !x.AttributeLists.Any(
                                                                 y => y.Attributes.Any(
                                                                     z => (z.Name as IdentifierNameSyntax)?.Identifier.Text == "NonSerialized"
                                                                     )
                                                                 )
                                                             )
                                                      .ToList();

            if (fieldsWithoutNonserializedAttribute.Any())
            {
                problemFields = fieldsWithoutNonserializedAttribute;
                return(true);
            }
            problemFields = null;
            return(false);
        }
示例#2
0
 private void RememberLazinatorPair(LazinatorPairInformation lazinatorPairInfo)
 {
     if (lazinatorPairInfo != null)
     {
         Location key = lazinatorPairInfo.PrimaryLocation;
         CompilationInformation.AddOrUpdate(key, lazinatorPairInfo, (k, v) => lazinatorPairInfo);
     }
 }
示例#3
0
        private List <Diagnostic> GetExtraFileDiagnosticsToReport(LazinatorPairInformation lazinatorPairInfo)
        {
            List <Diagnostic> diagnosticsToReturn = new List <Diagnostic>();
            List <Location>   additionalLocations = GetAdditionalLocations(lazinatorPairInfo);

            additionalLocations.AddRange(lazinatorPairInfo.IncorrectCodeBehindLocations);
            diagnosticsToReturn.Add(Diagnostic.Create(LazinatorCodeAnalyzer.ExtraFileRule, lazinatorPairInfo.PrimaryLocation, additionalLocations, lazinatorPairInfo.GetSourceFileDictionary(_configPath, _configString)));
            return(diagnosticsToReturn);
        }
示例#4
0
        private static List <Location> GetAdditionalLocations(LazinatorPairInformation lazinatorPairInfo)
        {
            var additionalLocations = new List <Location>();

            if (lazinatorPairInfo.CodeBehindLocation != null)
            {
                additionalLocations.Add(lazinatorPairInfo.CodeBehindLocation);
            }
            additionalLocations.AddRange(lazinatorPairInfo.LazinatorObjectLocationsExcludingCodeBehind);
            return(additionalLocations);
        }
示例#5
0
        private Diagnostic GetDiagnosticForGeneratable(LazinatorPairInformation lazinatorPairInfo, bool needsGeneration, Location locationOfImplementingType, Microsoft.CodeAnalysis.Text.TextSpan?textSpan, bool suppressRegenerate)
        {
            Location interfaceSpecificationLocation = Location.Create(
                locationOfImplementingType.SourceTree,
                textSpan.Value);
            List <Location> additionalLocations = GetAdditionalLocations(lazinatorPairInfo);

            if (suppressRegenerate && !needsGeneration)
            {
                return(null);
            }
            var diagnostic = Diagnostic.Create(needsGeneration ? LazinatorCodeAnalyzer.OutOfDateRule : LazinatorCodeAnalyzer.OptionalRegenerationRule, interfaceSpecificationLocation, additionalLocations, lazinatorPairInfo.GetSourceFileDictionary(_configPath, _configString));

            return(diagnostic);
        }
示例#6
0
        private static void AssessGenerationFeasibility(LazinatorPairInformation lazinatorPairInfo, out bool couldBeGenerated, out bool needsGeneration)
        {
            couldBeGenerated = true;
            needsGeneration  = lazinatorPairInfo.CodeBehindLocation == null;
            if (!needsGeneration)
            {
                var success = lazinatorPairInfo.CodeBehindLocation.SourceTree.TryGetRoot(out SyntaxNode root);
                if (success)
                {
                    SyntaxTrivia possibleComment = root.DescendantTrivia().FirstOrDefault();
                    if (possibleComment.IsKind(SyntaxKind.SingleLineCommentTrivia))
                    {
                        string commentContent = possibleComment.ToString().Substring(2);
                        bool   parse          = Guid.TryParse(commentContent, out Guid codeBehindGuid);
                        if (parse)
                        {
                            var interfaceLocations = lazinatorPairInfo.LazinatorInterface.Locations;
                            if (interfaceLocations.Count() != 1)
                            {
                                couldBeGenerated = false;
                            }
                            else
                            {
                                var hash = LazinatorCompilation.GetHashForInterface(lazinatorPairInfo.LazinatorInterface, lazinatorPairInfo.LazinatorObject);

                                if (hash != codeBehindGuid)
                                {
                                    needsGeneration = true;
                                }
                            }
                        }
                        else
                        {
                            needsGeneration = true;
                        }
                    }
                    else
                    {
                        needsGeneration = true;
                    }
                }
                else
                {
                    needsGeneration = true;
                }
            }
        }
示例#7
0
        public List <Diagnostic> GetDiagnosticsToReport(LazinatorPairInformation lazinatorPairInfo, bool suppressRegenerate)
        {
            bool couldBeGenerated, needsGeneration;

            AssessGenerationFeasibility(lazinatorPairInfo, out couldBeGenerated, out needsGeneration);
            if (needsGeneration || couldBeGenerated)
            {
                var locationOfImplementingType = lazinatorPairInfo.PrimaryLocation;
                var implementingTypeRoot       = locationOfImplementingType.SourceTree.GetRoot();
                TypeDeclarationSyntax implementingTypeSyntaxNode = (TypeDeclarationSyntax)implementingTypeRoot.FindNode(locationOfImplementingType.SourceSpan);
                Microsoft.CodeAnalysis.Text.TextSpan?textSpan    =
                    implementingTypeSyntaxNode.BaseList.Types
                    .FirstOrDefault(x =>
                                    (x.Type as IdentifierNameSyntax)?.Identifier.Text.Contains(lazinatorPairInfo.LazinatorInterface.Name)
                                    ??
                                    (x.Type as GenericNameSyntax)?.Identifier.Text.Contains(lazinatorPairInfo.LazinatorInterface.Name)
                                    ??
                                    false
                                    )?.Span;
                if (textSpan == null)
                {
                    return(null);
                }
                else
                {
                    List <Diagnostic> diagnosticsToReturn;
                    if (UnaccountedForFieldExists(lazinatorPairInfo, out var problemFields))
                    {
                        diagnosticsToReturn = problemFields.Select(x => Diagnostic.Create(LazinatorCodeAnalyzer.UnaccountedForFieldRule, x.GetLocation())).ToList();
                    }
                    else
                    {
                        diagnosticsToReturn = new List <Diagnostic>();
                    }
                    Diagnostic diagnosticForGeneratable = GetDiagnosticForGeneratable(lazinatorPairInfo, needsGeneration, locationOfImplementingType, textSpan, suppressRegenerate);
                    if (diagnosticForGeneratable != null)
                    {
                        diagnosticsToReturn.Add(diagnosticForGeneratable);
                    }
                    return(diagnosticsToReturn);
                }
            }
            else
            {
                return(null);
            }
        }
示例#8
0
        public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context)
        {
            var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false);

            var diagnostic = context.Diagnostics
                             .Where(x => FixableDiagnosticIds.Contains(x.Id))
                             .FirstOrDefault();

            if (diagnostic == null)
            {
                return;
            }
            LazinatorPairInformation lazinatorPairInfo = new LazinatorPairInformation(
                await context.Document.GetSemanticModelAsync(), diagnostic.Properties, diagnostic.AdditionalLocations);

            var diagnosticSpan = diagnostic.Location.SourceSpan;

            // Find the type declaration identified by the diagnostic.
            if (diagnostic.Id == LazinatorCodeAnalyzer.Lazin001 || diagnostic.Id == LazinatorCodeAnalyzer.Lazin002)
            {
                var syntaxToken = root.FindToken(diagnosticSpan.Start);
                var declaration = syntaxToken.Parent.AncestorsAndSelf().OfType <TypeDeclarationSyntax>().FirstOrDefault();

                // Register a code action that will invoke the fix.
                string title = diagnostic.Id == LazinatorCodeAnalyzer.Lazin001 ? Lazin001Title : Lazin002Title;
                if (declaration != null)
                {
                    context.RegisterCodeFix(
                        CodeAction.Create(
                            title: title,
                            createChangedSolution: c => FixGenerateLazinatorCodeBehind(context.Document, declaration, lazinatorPairInfo, c),
                            equivalenceKey: title),
                        diagnostic);
                }
            }

            if (diagnostic.Id == LazinatorCodeAnalyzer.Lazin004)
            {
                string title = Lazin004Title;
                context.RegisterCodeFix(
                    CodeAction.Create(
                        title: title,
                        createChangedSolution: c => DeleteExtraFiles(context.Document.Project.Solution, lazinatorPairInfo.IncorrectCodeBehindLocations, c),
                        equivalenceKey: title),
                    diagnostic);
            }
        }
示例#9
0
        private LazinatorPairInformation GetLazinatorPairInfo(Compilation compilation, INamedTypeSymbol lazinatorObjectType, INamedTypeSymbol namedInterfaceType)
        {
            var locationsExcludingCodeBehind =
                lazinatorObjectType.Locations
                .Where(x => !x.SourceTree.FilePath.EndsWith(GetGeneratedCodeFileExtension()))
                .ToList();
            var codeBehindLocations = lazinatorObjectType.Locations
                                      .Where(x => x.SourceTree.FilePath.EndsWith(GetGeneratedCodeFileExtension()))
                                      .ToList();
            // Complication: we should exclude code behind locations that are just for partial classes.
            // Similarly, if this is a partial class, we should exclude code behind locations for the main class.
            // We can figure this out by looking at the last partial class declared in the syntax tree and seeing if it matches.
            HashSet <Location> excludedCodeBehindLocations = null;

            foreach (Location location in codeBehindLocations)
            {
                var              lastClassNode = location.SourceTree.GetRoot().DescendantNodes().Where(x => x is ClassDeclarationSyntax or StructDeclarationSyntax or RecordDeclarationSyntax).Select(x => (TypeDeclarationSyntax)x).LastOrDefault();
                SemanticModel    semanticModel = compilation.GetSemanticModel(location.SourceTree);
                INamedTypeSymbol mainClass     = lastClassNode == null ? null : semanticModel.GetDeclaredSymbol(lastClassNode);
                if (!SymbolEqualityComparer.Default.Equals(mainClass, lazinatorObjectType))
                {
                    if (excludedCodeBehindLocations == null)
                    {
                        excludedCodeBehindLocations = new HashSet <Location>();
                    }
                    excludedCodeBehindLocations.Add(location);
                }
            }
            // Now, make a list of all non-excluded locations
            List <Location> allLocations = lazinatorObjectType.Locations.Where(x => excludedCodeBehindLocations == null || !excludedCodeBehindLocations.Contains(x)).ToList();

            var primaryLocation = locationsExcludingCodeBehind
                                  .FirstOrDefault();

            if (primaryLocation != null)
            {
                LazinatorPairInformation lazinatorPairInfo = new LazinatorPairInformation();
                lazinatorPairInfo.LazinatorObject    = lazinatorObjectType;
                lazinatorPairInfo.LazinatorInterface = namedInterfaceType;
                (lazinatorPairInfo.CodeBehindLocation, lazinatorPairInfo.IncorrectCodeBehindLocations, lazinatorPairInfo.LazinatorObjectLocationsExcludingCodeBehind) = LazinatorPairInformation.CategorizeLocations(Config, lazinatorObjectType, allLocations);
                return(lazinatorPairInfo);
            }

            return(null);
        }
示例#10
0
        public static async Task <Solution> AttemptFixGenerateLazinatorCodeBehind(Document originalDocument, LazinatorPairInformation lazinatorPairInformation, CancellationToken cancellationToken)
        {
            var             originalSolution = originalDocument.Project.Solution;
            LazinatorConfig config           = lazinatorPairInformation.LoadLazinatorConfig();

            var semanticModel = await originalDocument.GetSemanticModelAsync(cancellationToken);

            LazinatorCompilation generator = null;

            if (!RecycleLazinatorCompilation || _LastLazinatorCompilation == null)
            {
                generator = new LazinatorCompilation(semanticModel.Compilation, lazinatorPairInformation.LazinatorObject.Name, lazinatorPairInformation.LazinatorObject.GetFullMetadataName(), config);
                if (RecycleLazinatorCompilation)
                {
                    _LastLazinatorCompilation = generator;
                }
            }
            else
            {
                generator = _LastLazinatorCompilation;
                generator.Initialize(semanticModel.Compilation, lazinatorPairInformation.LazinatorObject.Name, lazinatorPairInformation.LazinatorObject.GetFullMetadataName());
            }
            var d          = new ObjectDescription(generator.ImplementingTypeSymbol, generator, originalDocument.FilePath);
            var codeBehind = d.GetCodeBehind();

            string fileExtension      = config?.GeneratedCodeFileExtension ?? ".laz.cs";
            var    project            = originalDocument.Project;
            string codeBehindFilePath = null;
            string codeBehindName     = null;

            string[] codeBehindFolders      = null;
            bool     useFullyQualifiedNames = (config?.UseFullyQualifiedNames ?? false) || generator.ImplementingTypeSymbol.ContainingType != null || generator.ImplementingTypeSymbol.IsGenericType;

            codeBehindName = RoslynHelpers.GetEncodableVersionOfIdentifier(generator.ImplementingTypeSymbol, useFullyQualifiedNames) + fileExtension;

            string[] GetFolders(string fileName)
            {
                return(fileName.Split('\\', '/').Where(x => !x.Contains(".cs") && !x.Contains(".csproj")).ToArray());
            }

            if (config?.GeneratedCodePath == null)
            { // use short form of name in same location as original code
                codeBehindFilePath = originalDocument.FilePath;
                codeBehindFolders  = GetFolders(codeBehindFilePath).Skip(GetFolders(originalDocument.Project.FilePath).Count()).ToArray();
            }
            else
            { // we have a config file specifying a common directory
                codeBehindFilePath = config.GeneratedCodePath;
                if (!codeBehindFilePath.EndsWith("\\"))
                {
                    codeBehindFilePath += "\\";
                }
                codeBehindFolders = config.RelativeGeneratedCodePath.Split('\\', '/');
            }
            codeBehindFilePath = System.IO.Path.GetDirectoryName(codeBehindFilePath);
            while (codeBehindFilePath.EndsWith(".cs"))
            {
                var lastSlash = codeBehindFilePath.IndexOfAny(new char[] { '\\', '/' });
                if (lastSlash >= 0)
                {
                    codeBehindFilePath = codeBehindFilePath.Substring(0, lastSlash);
                }
            }
            while (codeBehindFilePath.EndsWith("\\"))
            {
                codeBehindFilePath = codeBehindFilePath.Substring(0, codeBehindFilePath.Length - 1);
            }
            codeBehindFilePath += "\\" + codeBehindName;

            Solution revisedSolution;

            if (lazinatorPairInformation.CodeBehindLocation == null)
            { // The file does not already exist
              // codeBehindFilePath = System.IO.Path.GetDirectoryName(codeBehindFilePath); // omit file name
                Document documentToAdd = project.AddDocument(codeBehindName, codeBehind, codeBehindFolders, codeBehindFilePath);
                //if (config.GeneratedCodePath != null)
                //    documentToAdd = documentToAdd.WithFolders(codeBehindFolders);
                revisedSolution = documentToAdd.Project.Solution;
            }
            else
            { // the file does already exist
                var currentDocumentWithCode = originalSolution.GetDocument(lazinatorPairInformation.CodeBehindLocation.SourceTree);
                var replacementDocument     = currentDocumentWithCode.WithText(SourceText.From(codeBehind));
                revisedSolution = originalSolution.WithDocumentText(currentDocumentWithCode.Id, SourceText.From(codeBehind));
            }
            revisedSolution = await AddAnnotationToIndicateSuccess(revisedSolution, true);

            return(revisedSolution);
        }
示例#11
0
        public static async Task <Solution> FixGenerateLazinatorCodeBehind(Document originalDocument, TypeDeclarationSyntax enclosingType, LazinatorPairInformation lazinatorPairInformation, CancellationToken cancellationToken)
        {
            if (!(enclosingType is ClassDeclarationSyntax or StructDeclarationSyntax or RecordDeclarationSyntax))
            {
                throw new LazinatorCodeGenException("Could not fix. Attribute is not in a class or struct.");
            }

            try
            {
                if (originalDocument == null)
                {
                    return(null);
                }
                Solution revisedSolution = await AttemptFixGenerateLazinatorCodeBehind(originalDocument, lazinatorPairInformation, cancellationToken);

                return(revisedSolution);
            }
            catch (LazinatorCodeGenException e)
            {
                Solution revisedSolution = await InsertLazinatorCodeGenerationError(originalDocument, enclosingType, e, cancellationToken);

                return(revisedSolution);
            }
        }