{"action":"created","comment":{"url":"https://api.github.com/repos/dotnet/codeformatter/pulls/comments/28284034","id":28284034,"diff_hunk":"@@ -0,0 +1,527 @@\n+// Copyright (c) Microsoft. All rights reserved.\n+// Licensed under the MIT license. See LICENSE file in the project root for full license information.\n+\n+using System;\n+using System.Collections.Concurrent;\n+using System.Collections.Generic;\n+using System.Linq;\n+using System.Reflection;\n+using System.Threading;\n+using System.Threading.Tasks;\n+\n+using Microsoft.CodeAnalysis;\n+using Microsoft.CodeAnalysis.CSharp;\n+using Microsoft.CodeAnalysis.CSharp.Syntax;\n+\n+namespace Microsoft.DotNet.CodeFormatting.Rules\n+{\n+    /// <summary>\n+    /// Mark any fields that can provably be marked as readonly.\n+    /// </summary>\n+    [GlobalSemanticRuleOrder(GlobalSemanticRuleOrder.MarkReadonlyFieldsRule)]\n+    internal sealed class MarkReadonlyFieldsRule : IGlobalSemanticFormattingRule\n+    {\n+        private readonly SemaphoreSlim _processUsagesLock = new SemaphoreSlim(1, 1);\n+        private ConcurrentDictionary<IFieldSymbol, bool> _unwrittenWritableFields;\n+\n+        public bool SupportsLanguage(string languageName)\n+        {\n+            return languageName == LanguageNames.CSharp;\n+        }\n+\n+        public async Task<Solution> ProcessAsync(\n+            Document document,\n+            SyntaxNode syntaxRoot,\n+            CancellationToken cancellationToken)\n+        {\n+            if (_unwrittenWritableFields == null)\n+            {\n+                using (await SemaphoreLock.GetAsync(_processUsagesLock))\n+                {\n+                    // A global analysis must be run before we can do any actual processing, because a field might\n+                    // be written in a different file than it is declared (even private ones may be split between\n+                    // partial classes).\n+\n+                    // It's also quite expensive, which is why it's being done inside the lock, so\n+                    // that the entire solution is not processed for each input file individually\n+                    if (_unwrittenWritableFields == null)\n+                    {\n+                        List<Document> allDocuments =\n+                            document.Project.Solution.Projects.SelectMany(p => p.Documents).ToList();\n+                        HashSet<IFieldSymbol>[] fields = await Task.WhenAll(\n+                            allDocuments\n+                                .AsParallel()\n+                                .Select(\n+                                    doc => WritableFieldScanner.Scan(doc, cancellationToken)));\n+\n+                        var writableFields = new ConcurrentDictionary<IFieldSymbol, bool>(\n+                            fields.SelectMany(s => s).Select(f => new KeyValuePair<IFieldSymbol, bool>(f, true)));\n+\n+                        await Task.WhenAll(\n+                            allDocuments.AsParallel()\n+                                .Select(\n+                                    doc => WriteUsagesScanner.RemoveWrittenFields(\n+                                        doc,\n+                                        writableFields,\n+                                        cancellationToken)));\n+\n+                        _unwrittenWritableFields = writableFields;\n+                    }\n+                }\n+            }\n+\n+            SyntaxNode root = await document.GetSyntaxRootAsync(cancellationToken);\n+            var application = new ReadonlyRewriter(\n+                _unwrittenWritableFields,\n+                await document.GetSemanticModelAsync(cancellationToken));\n+            return document.Project.Solution.WithDocumentSyntaxRoot(document.Id, application.Visit(root));\n+        }\n+\n+        /// <summary>\n+        /// This is the first walker, which looks for fields that are valid to transform to readonly.\n+        /// It returns any private or internal fields that are not already marked readonly, and returns a hash set\n+        /// of them. Internal fields are only considered if the \"InternalsVisibleTo\" is a reference to something\n+        /// in the same solution, since it's possible to analyse the global usages of it. Otherwise there is an\n+        /// assembly we don't have access to that can see that field, so we have to treat is as public.\n+        /// </summary>\n+        private sealed class WritableFieldScanner : CSharpSyntaxWalker\n+        {\n+            private static readonly HashSet<string> SerializingFieldAttributes = new HashSet<string>\n+            {\n+                \"System.ComponentModel.Composition.ImportAttribute\",\n+                \"System.ComponentModel.Composition.ImportManyAttribute\",\n+            };\n+\n+            private readonly HashSet<IFieldSymbol> _fields = new HashSet<IFieldSymbol>();\n+            private readonly ISymbol _internalsVisibleToAttribute;\n+            private readonly SemanticModel _model;\n+            private readonly Solution _solution;\n+\n+            private readonly Dictionary<IAssemblySymbol, bool> _visibleOutsideAsembly =\n+                new Dictionary<IAssemblySymbol, bool>();\n+\n+            private WritableFieldScanner(SemanticModel model, Solution solution)\n+            {\n+                _model = model;\n+                _solution = solution;\n+                _internalsVisibleToAttribute =\n+                    model.Compilation.GetTypeByMetadataName(\n+                        \"System.Runtime.CompilerServices.InternalsVisibleToAttribute\");\n+            }\n+\n+            public static async Task<HashSet<IFieldSymbol>> Scan(\n+                Document document,\n+                CancellationToken cancellationToken)\n+            {\n+                var scanner = new WritableFieldScanner(\n+                    await document.GetSemanticModelAsync(cancellationToken),\n+                    document.Project.Solution);\n+                scanner.Visit(await document.GetSyntaxRootAsync(cancellationToken));\n+                return scanner._fields;\n+            }\n+\n+            public override void VisitFieldDeclaration(FieldDeclarationSyntax node)\n+            {\n+                var fieldSymbol = (IFieldSymbol)_model.GetDeclaredSymbol(node.Declaration.Variables[0]);\n+\n+                if (fieldSymbol.IsReadOnly || fieldSymbol.IsConst || fieldSymbol.IsExtern)\n+                {\n+                    return;\n+                }\n+\n+                if (IsSymbolVisibleOutsideSolution(fieldSymbol, _internalsVisibleToAttribute, _solution))\n+                {\n+                    return;\n+                }\n+\n+                if (IsFieldSerializableByAttributes(fieldSymbol))\n+                {\n+                    return;\n+                }\n+\n+                _fields.Add(fieldSymbol);\n+            }\n+\n+            private bool IsSymbolVisibleOutsideSolution(\n+                ISymbol symbol,\n+                ISymbol internalsVisibleToAttribute,\n+                Solution solution)\n+            {\n+                Accessibility accessibility = symbol.DeclaredAccessibility;\n+\n+                if (accessibility == Accessibility.NotApplicable)\n+                {\n+                    if (symbol.Kind == SymbolKind.Field)\n+                    {\n+                        accessibility = Accessibility.Private;\n+                    }\n+                    else\n+                    {\n+                        accessibility = Accessibility.Internal;\n+                    }\n+                }\n+\n+                if (accessibility == Accessibility.Public || accessibility == Accessibility.Protected)\n+                {\n+                    if (symbol.ContainingType != null)\n+                    {\n+                        // a public symbol in a non-visible class isn't visible\n+                        return IsSymbolVisibleOutsideSolution(\n+                            symbol.ContainingType,\n+                            internalsVisibleToAttribute,\n+                            solution);\n+                    }\n+\n+                    // They are public, we are going to skip them.\n+                    return true;\n+                }\n+\n+                if (accessibility > Accessibility.Private)\n+                {\n+                    bool visibleOutsideSolution = IsVisibleOutsideSolution(\n+                        symbol,\n+                        internalsVisibleToAttribute,\n+                        solution);\n+\n+                    if (visibleOutsideSolution)\n+                    {\n+                        if (symbol.ContainingType != null)\n+                        {\n+                            // a visible symbol in a non-visible class isn't visible\n+                            return IsSymbolVisibleOutsideSolution(\n+                                symbol.ContainingType,\n+                                internalsVisibleToAttribute,\n+                                solution);\n+                        }\n+\n+                        return true;\n+                    }\n+                }\n+\n+                return false;\n+            }\n+\n+            private bool IsVisibleOutsideSolution(\n+                ISymbol field,\n+                ISymbol internalsVisibleToAttribute,\n+                Solution solution)\n+            {\n+                bool isVisible;\n+                IAssemblySymbol assembly = field.ContainingAssembly;\n+                if (_visibleOutsideAsembly.TryGetValue(assembly, out isVisible))\n+                {\n+                    return isVisible;\n+                }\n+\n+                foreach (AttributeData internalsVisibleInstance in assembly.GetAttributes()\n+                    .Where(a => Equals(a.AttributeClass, internalsVisibleToAttribute)))\n+                {\n+                    if (internalsVisibleInstance.ConstructorArguments.Length != 1)\n+                    {\n+                        // Unexpected number of agruments, isn't really the correct type.\n+                        continue;\n+                    }\n+\n+                    TypedConstant assemblyNameArgument = internalsVisibleInstance.ConstructorArguments[0];\n+                    if (assemblyNameArgument.Kind != TypedConstantKind.Primitive)\n+                    {\n+                        // The first argument wasn't a primitave value, isn't really the correct type\n+                        continue;\n+                    }\n+\n+                    string assemblyName = assemblyNameArgument.Value as string;\n+                    if (String.IsNullOrEmpty(assemblyName))\n+                    {\n+                        // The first argument wasn't a string, isn't really the correct type\n+                        continue;\n+                    }\n+\n+                    if (!solution.Projects.Any(p => ProjectHasName(p, assemblyName)))","path":"src/Microsoft.DotNet.CodeFormatting/Rules/MarkReadonlyFieldsRule.cs","position":239,"original_position":239,"commit_id":"011326bf944ec310dbae54e1a93cde4ee275b473","original_commit_id":"011326bf944ec310dbae54e1a93cde4ee275b473","user":{"login":"ChadNedzlek","id":11790765,"avatar_url":"https://avatars.githubusercontent.com/u/11790765?v=3","gravatar_id":"","url":"https://api.github.com/users/ChadNedzlek","html_url":"https://github.com/ChadNedzlek","followers_url":"https://api.github.com/users/ChadNedzlek/followers","following_url":"https://api.github.com/users/ChadNedzlek/following{/other_user}","gists_url":"https://api.github.com/users/ChadNedzlek/gists{/gist_id}","starred_url":"https://api.github.com/users/ChadNedzlek/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ChadNedzlek/subscriptions","organizations_url":"https://api.github.com/users/ChadNedzlek/orgs","repos_url":"https://api.github.com/users/ChadNedzlek/repos","events_url":"https://api.github.com/users/ChadNedzlek/events{/privacy}","received_events_url":"https://api.github.com/users/ChadNedzlek/received_events","type":"User","site_admin":false},"body":"I worry that anything that has unit tests is going to expose their internals to the test assembly, which would disable readonly for any of the internal fields.  Running on the solution that contained both of them would solve the problem.  Running on the solution will mark more things (correctly) as readonly than running it only on the project.\r\n\r\nHowever, it looks like most of the framework doesn't actually expose many of it's internals, so I'll remove the solution level check for now.  I'll still have the commit in my fork if we want to add it back later.","created_at":"2015-04-13T22:03:49Z","updated_at":"2015-04-13T22:03:49Z","html_url":"https://github.com/dotnet/codeformatter/pull/109#discussion_r28284034","pull_request_url":"https://api.github.com/repos/dotnet/codeformatter/pulls/109","_links":{"self":{"href":"https://api.github.com/repos/dotnet/codeformatter/pulls/comments/28284034"},"html":{"href":"https://github.com/dotnet/codeformatter/pull/109#discussion_r28284034"},"pull_request":{"href":"https://api.github.com/repos/dotnet/codeformatter/pulls/109"}}},"pull_request":{"url":"https://api.github.com/repos/dotnet/codeformatter/pulls/109","id":33184343,"html_url":"https://github.com/dotnet/codeformatter/pull/109","diff_url":"https://github.com/dotnet/codeformatter/pull/109.diff","patch_url":"https://github.com/dotnet/codeformatter/pull/109.patch","issue_url":"https://api.github.com/repos/dotnet/codeformatter/issues/109","number":109,"state":"open","locked":false,"title":"Readonly field formatter","user":{"login":"ChadNedzlek","id":11790765,"avatar_url":"https://avatars.githubusercontent.com/u/11790765?v=3","gravatar_id":"","url":"https://api.github.com/users/ChadNedzlek","html_url":"https://github.com/ChadNedzlek","followers_url":"https://api.github.com/users/ChadNedzlek/followers","following_url":"https://api.github.com/users/ChadNedzlek/following{/other_user}","gists_url":"https://api.github.com/users/ChadNedzlek/gists{/gist_id}","starred_url":"https://api.github.com/users/ChadNedzlek/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ChadNedzlek/subscriptions","organizations_url":"https://api.github.com/users/ChadNedzlek/orgs","repos_url":"https://api.github.com/users/ChadNedzlek/repos","events_url":"https://api.github.com/users/ChadNedzlek/events{/privacy}","received_events_url":"https://api.github.com/users/ChadNedzlek/received_events","type":"User","site_admin":false},"body":"Add the readonly keyword to fields that are provably never written\r\nto. Only mark private and internal fields, and the following\r\nare considered \"writing\" to a field:\r\n\r\n- Assignments (_field = 5)\r\n- Compound assignments (_field += 5)\r\n- ref/out calls (Method(ref _field))\r\n- ref of parent to extern method. While not technically required\r\n  the fields can possibly change (even when marked readonly)\r\n- internal fields that are exposed via InternalsVisibleTo an\r\n  assembly that is not in the input project\r\n\r\nFix #104","created_at":"2015-04-13T17:24:39Z","updated_at":"2015-04-13T22:03:49Z","closed_at":null,"merged_at":null,"merge_commit_sha":"fe55e181d45149a236aae4ae35900bf85fbf904b","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/dotnet/codeformatter/pulls/109/commits","review_comments_url":"https://api.github.com/repos/dotnet/codeformatter/pulls/109/comments","review_comment_url":"https://api.github.com/repos/dotnet/codeformatter/pulls/comments{/number}","comments_url":"https://api.github.com/repos/dotnet/codeformatter/issues/109/comments","statuses_url":"https://api.github.com/repos/dotnet/codeformatter/statuses/011326bf944ec310dbae54e1a93cde4ee275b473","head":{"label":"ChadNedzlek:readonly-field-submit","ref":"readonly-field-submit","sha":"011326bf944ec310dbae54e1a93cde4ee275b473","user":{"login":"ChadNedzlek","id":11790765,"avatar_url":"https://avatars.githubusercontent.com/u/11790765?v=3","gravatar_id":"","url":"https://api.github.com/users/ChadNedzlek","html_url":"https://github.com/ChadNedzlek","followers_url":"https://api.github.com/users/ChadNedzlek/followers","following_url":"https://api.github.com/users/ChadNedzlek/following{/other_user}","gists_url":"https://api.github.com/users/ChadNedzlek/gists{/gist_id}","starred_url":"https://api.github.com/users/ChadNedzlek/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ChadNedzlek/subscriptions","organizations_url":"https://api.github.com/users/ChadNedzlek/orgs","repos_url":"https://api.github.com/users/ChadNedzlek/repos","events_url":"https://api.github.com/users/ChadNedzlek/events{/privacy}","received_events_url":"https://api.github.com/users/ChadNedzlek/received_events","type":"User","site_admin":false},"repo":{"id":33525606,"name":"codeformatter","full_name":"ChadNedzlek/codeformatter","owner":{"login":"ChadNedzlek","id":11790765,"avatar_url":"https://avatars.githubusercontent.com/u/11790765?v=3","gravatar_id":"","url":"https://api.github.com/users/ChadNedzlek","html_url":"https://github.com/ChadNedzlek","followers_url":"https://api.github.com/users/ChadNedzlek/followers","following_url":"https://api.github.com/users/ChadNedzlek/following{/other_user}","gists_url":"https://api.github.com/users/ChadNedzlek/gists{/gist_id}","starred_url":"https://api.github.com/users/ChadNedzlek/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ChadNedzlek/subscriptions","organizations_url":"https://api.github.com/users/ChadNedzlek/orgs","repos_url":"https://api.github.com/users/ChadNedzlek/repos","events_url":"https://api.github.com/users/ChadNedzlek/events{/privacy}","received_events_url":"https://api.github.com/users/ChadNedzlek/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/ChadNedzlek/codeformatter","description":"Tool that uses Roslyn to automatically rewrite the source to follow our coding styles","fork":true,"url":"https://api.github.com/repos/ChadNedzlek/codeformatter","forks_url":"https://api.github.com/repos/ChadNedzlek/codeformatter/forks","keys_url":"https://api.github.com/repos/ChadNedzlek/codeformatter/keys{/key_id}","collaborators_url":"https://api.github.com/repos/ChadNedzlek/codeformatter/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/ChadNedzlek/codeformatter/teams","hooks_url":"https://api.github.com/repos/ChadNedzlek/codeformatter/hooks","issue_events_url":"https://api.github.com/repos/ChadNedzlek/codeformatter/issues/events{/number}","events_url":"https://api.github.com/repos/ChadNedzlek/codeformatter/events","assignees_url":"https://api.github.com/repos/ChadNedzlek/codeformatter/assignees{/user}","branches_url":"https://api.github.com/repos/ChadNedzlek/codeformatter/branches{/branch}","tags_url":"https://api.github.com/repos/ChadNedzlek/codeformatter/tags","blobs_url":"https://api.github.com/repos/ChadNedzlek/codeformatter/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/ChadNedzlek/codeformatter/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/ChadNedzlek/codeformatter/git/refs{/sha}","trees_url":"https://api.github.com/repos/ChadNedzlek/codeformatter/git/trees{/sha}","statuses_url":"https://api.github.com/repos/ChadNedzlek/codeformatter/statuses/{sha}","languages_url":"https://api.github.com/repos/ChadNedzlek/codeformatter/languages","stargazers_url":"https://api.github.com/repos/ChadNedzlek/codeformatter/stargazers","contributors_url":"https://api.github.com/repos/ChadNedzlek/codeformatter/contributors","subscribers_url":"https://api.github.com/repos/ChadNedzlek/codeformatter/subscribers","subscription_url":"https://api.github.com/repos/ChadNedzlek/codeformatter/subscription","commits_url":"https://api.github.com/repos/ChadNedzlek/codeformatter/commits{/sha}","git_commits_url":"https://api.github.com/repos/ChadNedzlek/codeformatter/git/commits{/sha}","comments_url":"https://api.github.com/repos/ChadNedzlek/codeformatter/comments{/number}","issue_comment_url":"https://api.github.com/repos/ChadNedzlek/codeformatter/issues/comments{/number}","contents_url":"https://api.github.com/repos/ChadNedzlek/codeformatter/contents/{+path}","compare_url":"https://api.github.com/repos/ChadNedzlek/codeformatter/compare/{base}...{head}","merges_url":"https://api.github.com/repos/ChadNedzlek/codeformatter/merges","archive_url":"https://api.github.com/repos/ChadNedzlek/codeformatter/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/ChadNedzlek/codeformatter/downloads","issues_url":"https://api.github.com/repos/ChadNedzlek/codeformatter/issues{/number}","pulls_url":"https://api.github.com/repos/ChadNedzlek/codeformatter/pulls{/number}","milestones_url":"https://api.github.com/repos/ChadNedzlek/codeformatter/milestones{/number}","notifications_url":"https://api.github.com/repos/ChadNedzlek/codeformatter/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/ChadNedzlek/codeformatter/labels{/name}","releases_url":"https://api.github.com/repos/ChadNedzlek/codeformatter/releases{/id}","created_at":"2015-04-07T06:13:34Z","updated_at":"2015-04-09T01:31:54Z","pushed_at":"2015-04-13T21:42:51Z","git_url":"git://github.com/ChadNedzlek/codeformatter.git","ssh_url":"git@github.com:ChadNedzlek/codeformatter.git","clone_url":"https://github.com/ChadNedzlek/codeformatter.git","svn_url":"https://github.com/ChadNedzlek/codeformatter","homepage":"","size":612,"stargazers_count":0,"watchers_count":0,"language":"C#","has_issues":true,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"dotnet:master","ref":"master","sha":"0ac40694d26ae8afd653a794cbe9fd56ef189a7b","user":{"login":"dotnet","id":9141961,"avatar_url":"https://avatars.githubusercontent.com/u/9141961?v=3","gravatar_id":"","url":"https://api.github.com/users/dotnet","html_url":"https://github.com/dotnet","followers_url":"https://api.github.com/users/dotnet/followers","following_url":"https://api.github.com/users/dotnet/following{/other_user}","gists_url":"https://api.github.com/users/dotnet/gists{/gist_id}","starred_url":"https://api.github.com/users/dotnet/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/dotnet/subscriptions","organizations_url":"https://api.github.com/users/dotnet/orgs","repos_url":"https://api.github.com/users/dotnet/repos","events_url":"https://api.github.com/users/dotnet/events{/privacy}","received_events_url":"https://api.github.com/users/dotnet/received_events","type":"Organization","site_admin":false},"repo":{"id":28896487,"name":"codeformatter","full_name":"dotnet/codeformatter","owner":{"login":"dotnet","id":9141961,"avatar_url":"https://avatars.githubusercontent.com/u/9141961?v=3","gravatar_id":"","url":"https://api.github.com/users/dotnet","html_url":"https://github.com/dotnet","followers_url":"https://api.github.com/users/dotnet/followers","following_url":"https://api.github.com/users/dotnet/following{/other_user}","gists_url":"https://api.github.com/users/dotnet/gists{/gist_id}","starred_url":"https://api.github.com/users/dotnet/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/dotnet/subscriptions","organizations_url":"https://api.github.com/users/dotnet/orgs","repos_url":"https://api.github.com/users/dotnet/repos","events_url":"https://api.github.com/users/dotnet/events{/privacy}","received_events_url":"https://api.github.com/users/dotnet/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/dotnet/codeformatter","description":"Tool that uses Roslyn to automatically rewrite the source to follow our coding styles","fork":false,"url":"https://api.github.com/repos/dotnet/codeformatter","forks_url":"https://api.github.com/repos/dotnet/codeformatter/forks","keys_url":"https://api.github.com/repos/dotnet/codeformatter/keys{/key_id}","collaborators_url":"https://api.github.com/repos/dotnet/codeformatter/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/dotnet/codeformatter/teams","hooks_url":"https://api.github.com/repos/dotnet/codeformatter/hooks","issue_events_url":"https://api.github.com/repos/dotnet/codeformatter/issues/events{/number}","events_url":"https://api.github.com/repos/dotnet/codeformatter/events","assignees_url":"https://api.github.com/repos/dotnet/codeformatter/assignees{/user}","branches_url":"https://api.github.com/repos/dotnet/codeformatter/branches{/branch}","tags_url":"https://api.github.com/repos/dotnet/codeformatter/tags","blobs_url":"https://api.github.com/repos/dotnet/codeformatter/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/dotnet/codeformatter/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/dotnet/codeformatter/git/refs{/sha}","trees_url":"https://api.github.com/repos/dotnet/codeformatter/git/trees{/sha}","statuses_url":"https://api.github.com/repos/dotnet/codeformatter/statuses/{sha}","languages_url":"https://api.github.com/repos/dotnet/codeformatter/languages","stargazers_url":"https://api.github.com/repos/dotnet/codeformatter/stargazers","contributors_url":"https://api.github.com/repos/dotnet/codeformatter/contributors","subscribers_url":"https://api.github.com/repos/dotnet/codeformatter/subscribers","subscription_url":"https://api.github.com/repos/dotnet/codeformatter/subscription","commits_url":"https://api.github.com/repos/dotnet/codeformatter/commits{/sha}","git_commits_url":"https://api.github.com/repos/dotnet/codeformatter/git/commits{/sha}","comments_url":"https://api.github.com/repos/dotnet/codeformatter/comments{/number}","issue_comment_url":"https://api.github.com/repos/dotnet/codeformatter/issues/comments{/number}","contents_url":"https://api.github.com/repos/dotnet/codeformatter/contents/{+path}","compare_url":"https://api.github.com/repos/dotnet/codeformatter/compare/{base}...{head}","merges_url":"https://api.github.com/repos/dotnet/codeformatter/merges","archive_url":"https://api.github.com/repos/dotnet/codeformatter/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/dotnet/codeformatter/downloads","issues_url":"https://api.github.com/repos/dotnet/codeformatter/issues{/number}","pulls_url":"https://api.github.com/repos/dotnet/codeformatter/pulls{/number}","milestones_url":"https://api.github.com/repos/dotnet/codeformatter/milestones{/number}","notifications_url":"https://api.github.com/repos/dotnet/codeformatter/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/dotnet/codeformatter/labels{/name}","releases_url":"https://api.github.com/repos/dotnet/codeformatter/releases{/id}","created_at":"2015-01-07T03:28:44Z","updated_at":"2015-04-11T21:10:01Z","pushed_at":"2015-04-13T21:42:51Z","git_url":"git://github.com/dotnet/codeformatter.git","ssh_url":"git@github.com:dotnet/codeformatter.git","clone_url":"https://github.com/dotnet/codeformatter.git","svn_url":"https://github.com/dotnet/codeformatter","homepage":"","size":1776,"stargazers_count":360,"watchers_count":360,"language":"C#","has_issues":true,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":50,"mirror_url":null,"open_issues_count":35,"forks":50,"open_issues":35,"watchers":360,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/dotnet/codeformatter/pulls/109"},"html":{"href":"https://github.com/dotnet/codeformatter/pull/109"},"issue":{"href":"https://api.github.com/repos/dotnet/codeformatter/issues/109"},"comments":{"href":"https://api.github.com/repos/dotnet/codeformatter/issues/109/comments"},"review_comments":{"href":"https://api.github.com/repos/dotnet/codeformatter/pulls/109/comments"},"review_comment":{"href":"https://api.github.com/repos/dotnet/codeformatter/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/dotnet/codeformatter/pulls/109/commits"},"statuses":{"href":"https://api.github.com/repos/dotnet/codeformatter/statuses/011326bf944ec310dbae54e1a93cde4ee275b473"}}}}