/// <summary> /// Creates a new syntax tree from a syntax node. /// </summary> public static SyntaxTree Create( CSharpSyntaxNode root, CSharpParseOptions options = null, string path = "", Encoding encoding = null, ImmutableDictionary <string, ReportDiagnostic> diagnosticOptions = null, bool?isGeneratedCode = null) { if (root == null) { throw new ArgumentNullException(nameof(root)); } var directives = root.Kind() == SyntaxKind.CompilationUnit ? ((CompilationUnitSyntax)root).GetConditionalDirectivesStack() : InternalSyntax.DirectiveStack.Empty; bool isGenerated = isGeneratedCode ?? GeneratedCodeUtilities.IsGeneratedCode( path, root, isComment: trivia => trivia.Kind() == SyntaxKind.SingleLineCommentTrivia || trivia.Kind() == SyntaxKind.MultiLineCommentTrivia); return(new ParsedSyntaxTree( textOpt: null, encodingOpt: encoding, checksumAlgorithm: SourceHashAlgorithm.Sha1, path: path, options: options ?? CSharpParseOptions.Default, root: root, directives: directives, diagnosticOptions, isGenerated, cloneRoot: true)); }
public override SyntaxTree CreateSyntaxTree(string filePath, ParseOptions options, Encoding encoding, SyntaxNode root, AnalyzerConfigOptionsResult analyzerConfigOptionsResult) { options ??= GetDefaultParseOptions(); var isUserConfiguredGeneratedCode = GeneratedCodeUtilities.GetIsGeneratedCodeFromOptions(analyzerConfigOptionsResult.AnalyzerOptions); return(CSharpSyntaxTree.Create((CSharpSyntaxNode)root, (CSharpParseOptions)options, filePath, encoding, analyzerConfigOptionsResult.TreeOptions, isUserConfiguredGeneratedCode)); }
public bool IsGeneratedCode(Document document, CancellationToken cancellationToken) { var syntaxTree = document.GetSyntaxTreeSynchronously(cancellationToken); var syntaxFacts = document.GetLanguageService <ISyntaxFactsService>(); return(GeneratedCodeUtilities.IsGeneratedCode( syntaxTree, t => syntaxFacts.IsRegularComment(t) || syntaxFacts.IsDocumentationComment(t), cancellationToken)); }
private static async Task <Solution> FormatFilesInProjectAsync(ILogger logger, Project project, ICodingConventionsManager codingConventionsManager, EditorConfigOptionsApplier optionsApplier, CancellationToken cancellationToken) { var isCommentTrivia = project.Language == LanguageNames.CSharp ? IsCSharpCommentTrivia : IsVisualBasicCommentTrivia; var formattedDocuments = new List <(DocumentId documentId, Task <SourceText> formatTask)>(); foreach (var documentId in project.DocumentIds) { var document = project.Solution.GetDocument(documentId); if (!document.SupportsSyntaxTree) { continue; } var formatTask = Task.Run(async() => { var syntaxTree = await document.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false); if (GeneratedCodeUtilities.IsGeneratedCode(syntaxTree, isCommentTrivia, cancellationToken)) { return(null); } logger.LogTrace(Resources.Formatting_code_file_0, Path.GetFileName(document.FilePath)); OptionSet documentOptions = await document.GetOptionsAsync(cancellationToken).ConfigureAwait(false); var codingConventionsContext = await codingConventionsManager.GetConventionContextAsync(document.FilePath, cancellationToken).ConfigureAwait(false); if (codingConventionsContext?.CurrentConventions != null) { documentOptions = optionsApplier.ApplyConventions(documentOptions, codingConventionsContext.CurrentConventions, project.Language); } var formattedDocument = await Formatter.FormatAsync(document, documentOptions, cancellationToken).ConfigureAwait(false); return(await formattedDocument.GetTextAsync(cancellationToken).ConfigureAwait(false)); }, cancellationToken); formattedDocuments.Add((documentId, formatTask)); } var formattedSolution = project.Solution; foreach (var(documentId, formatTask) in formattedDocuments) { var text = await formatTask.ConfigureAwait(false); if (text is null) { continue; } formattedSolution = formattedSolution.WithDocumentText(documentId, text); } return(formattedSolution); }
public override SyntaxTree ParseSyntaxTree(string filePath, ParseOptions options, SourceText text, AnalyzerConfigOptionsResult?analyzerConfigOptionsResult, CancellationToken cancellationToken) { options ??= GetDefaultParseOptions(); var isUserConfiguredGeneratedCode = analyzerConfigOptionsResult.HasValue ? GeneratedCodeUtilities.GetIsGeneratedCodeFromOptions(analyzerConfigOptionsResult.Value.AnalyzerOptions) : null; return(SyntaxFactory.ParseSyntaxTree(text, options, filePath, analyzerConfigOptionsResult?.TreeOptions, isUserConfiguredGeneratedCode, cancellationToken: cancellationToken)); }
public Options(AnalyzerConfigOptionsResult?result) { if (result is AnalyzerConfigOptionsResult r) { DiagnosticOptions = r.TreeOptions; IsGenerated = GeneratedCodeUtilities.GetIsGeneratedCodeFromOptions(r.AnalyzerOptions); } else { DiagnosticOptions = SyntaxTree.EmptyDiagnosticOptions; IsGenerated = null; } }
private void AnalyzeSemanticModel(SemanticModelAnalysisContext context) { var tree = context.SemanticModel.SyntaxTree; var cancellationToken = context.CancellationToken; if (!(context.Options is WorkspaceAnalyzerOptions workspaceOptions)) { return; } var language = context.SemanticModel.Compilation.Language; var service = workspaceOptions.Services.GetLanguageServices(language) .GetService <IUnnecessaryImportsService>(); var unnecessaryImports = service.GetUnnecessaryImports(context.SemanticModel, cancellationToken); if (unnecessaryImports.Any()) { // The IUnnecessaryImportsService will return individual import pieces that // need to be removed. For example, it will return individual import-clauses // from VB. However, we want to mark the entire import statement if we are // going to remove all the clause. Defer to our subclass to stitch this up // for us appropriately. unnecessaryImports = MergeImports(unnecessaryImports); EnsureClassificationIdDescriptors(); var fadeOut = workspaceOptions.Services.Workspace.Options.GetOption(FadingOptions.FadeOutUnusedImports, language); DiagnosticDescriptor descriptor; var syntaxFacts = workspaceOptions.Services.GetLanguageServices(language).GetRequiredService <ISyntaxFactsService>(); if (GeneratedCodeUtilities.IsGeneratedCode(tree, t => syntaxFacts.IsRegularComment(t) || syntaxFacts.IsDocumentationComment(t), context.CancellationToken)) { descriptor = fadeOut ? _unnecessaryGeneratedCodeClassificationIdDescriptor : _generatedCodeClassificationIdDescriptor; } else { descriptor = fadeOut ? _unnecessaryClassificationIdDescriptor : _classificationIdDescriptor; } var getLastTokenFunc = GetLastTokenDelegateForContiguousSpans(); var contiguousSpans = unnecessaryImports.GetContiguousSpans(getLastTokenFunc); var diagnostics = CreateClassificationDiagnostics(contiguousSpans, tree, descriptor, cancellationToken).Concat( CreateFixableDiagnostics(unnecessaryImports, tree, cancellationToken)); foreach (var diagnostic in diagnostics) { context.ReportDiagnostic(diagnostic); } } }
internal bool IsGeneratedCode() { if (_lazyIsGeneratedCode == ThreeState.Unknown) { // Create the generated code status on demand bool isGenerated = GeneratedCodeUtilities.IsGeneratedCode( this, isComment: trivia => trivia.Kind() == SyntaxKind.SingleLineCommentTrivia || trivia.Kind() == SyntaxKind.MultiLineCommentTrivia, cancellationToken: default); _lazyIsGeneratedCode = isGenerated.ToThreeState(); } return(_lazyIsGeneratedCode == ThreeState.True); }
internal bool IsGeneratedCode() { if (_lazyIsGeneratedCode == -1) { // Create the generated code status on demand bool isGenerated = GeneratedCodeUtilities.IsGeneratedCode( this, isComment: trivia => trivia.Kind() == SyntaxKind.SingleLineCommentTrivia || trivia.Kind() == SyntaxKind.MultiLineCommentTrivia, cancellationToken: default); Interlocked.CompareExchange(ref _lazyIsGeneratedCode, isGenerated ? 1 : 0, -1); } return(_lazyIsGeneratedCode == 1); }
private static bool IsGeneratedCode(SyntaxTree syntaxTree, Document document, CancellationToken cancellationToken) { // First check if user has configured "generated_code = true | false" in .editorconfig var analyzerOptions = document.Project.AnalyzerOptions.AnalyzerConfigOptionsProvider.GetOptions(syntaxTree); var isUserConfiguredGeneratedCode = GeneratedCodeUtilities.GetIsGeneratedCodeFromOptions(analyzerOptions); if (isUserConfiguredGeneratedCode.HasValue) { return(isUserConfiguredGeneratedCode.Value); } // Otherwise, fallback to generated code heuristic. var syntaxFacts = document.GetLanguageService <ISyntaxFactsService>(); return(GeneratedCodeUtilities.IsGeneratedCode( syntaxTree, t => syntaxFacts.IsRegularComment(t) || syntaxFacts.IsDocumentationComment(t), cancellationToken)); }
private void AnalyzeSemanticModel(SemanticModelAnalysisContext context) { var tree = context.SemanticModel.SyntaxTree; var cancellationToken = context.CancellationToken; var language = context.SemanticModel.Language; var unnecessaryImports = UnnecessaryImportsProvider.GetUnnecessaryImports(context.SemanticModel, cancellationToken); if (unnecessaryImports.Any()) { // The IUnnecessaryImportsService will return individual import pieces that // need to be removed. For example, it will return individual import-clauses // from VB. However, we want to mark the entire import statement if we are // going to remove all the clause. Defer to our subclass to stitch this up // for us appropriately. unnecessaryImports = MergeImports(unnecessaryImports); EnsureClassificationIdDescriptors(); var fadeOut = ShouldFade(context.Options, tree, language, cancellationToken); DiagnosticDescriptor descriptor; if (GeneratedCodeUtilities.IsGeneratedCode(tree, IsRegularCommentOrDocComment, cancellationToken)) { descriptor = fadeOut ? _unnecessaryGeneratedCodeClassificationIdDescriptor : _generatedCodeClassificationIdDescriptor; } else { descriptor = fadeOut ? _unnecessaryClassificationIdDescriptor : _classificationIdDescriptor; } var getLastTokenFunc = GetLastTokenDelegateForContiguousSpans(); var contiguousSpans = unnecessaryImports.GetContiguousSpans(getLastTokenFunc); var diagnostics = CreateClassificationDiagnostics(contiguousSpans, tree, descriptor, cancellationToken).Concat( CreateFixableDiagnostics(unnecessaryImports, tree, cancellationToken)); foreach (var diagnostic in diagnostics) { context.ReportDiagnostic(diagnostic); } }
private static SyntaxTree ParseFile( CSharpParseOptions parseOptions, CSharpParseOptions scriptParseOptions, SourceText content, CommandLineSourceFile file, AnalyzerConfigOptionsResult?analyzerConfigOptionsResult) { ImmutableDictionary <string, ReportDiagnostic> diagnosticOptions; bool?isUserConfiguredGeneratedCode; if (analyzerConfigOptionsResult.HasValue) { diagnosticOptions = analyzerConfigOptionsResult.Value.TreeOptions; isUserConfiguredGeneratedCode = GeneratedCodeUtilities.GetIsGeneratedCodeFromOptions(analyzerConfigOptionsResult.Value.AnalyzerOptions); } else { diagnosticOptions = null; isUserConfiguredGeneratedCode = null; } var tree = SyntaxFactory.ParseSyntaxTree( content, file.IsScript ? scriptParseOptions : parseOptions, file.Path, diagnosticOptions, isUserConfiguredGeneratedCode); // prepopulate line tables. // we will need line tables anyways and it is better to not wait until we are in emit // where things run sequentially. bool isHiddenDummy; tree.GetMappedLineSpanAndVisibility(default(TextSpan), out isHiddenDummy); return(tree); }
/// <summary> /// Returns true if this <paramref name="syntaxTree"/> is obtained from generated code. /// </summary> public static bool IsGeneratedCode(this SyntaxTree syntaxTree) { // TODO: check explicit user definitions return(GeneratedCodeUtilities.IsGeneratedCode(syntaxTree, SyntaxTriviaExtensions.IsRegularOrDocComment, cancellationToken: default)); }
private static async Task <(Solution solution, int filesFormatted)> FormatFilesInProjectAsync(ILogger logger, Project project, ICodingConventionsManager codingConventionsManager, EditorConfigOptionsApplier optionsApplier, string[] filesToFormat, CancellationToken cancellationToken) { var isCommentTrivia = project.Language == LanguageNames.CSharp ? IsCSharpCommentTrivia : IsVisualBasicCommentTrivia; var formattedDocuments = new List <(DocumentId documentId, Task <SourceText> formatTask)>(); foreach (var documentId in project.DocumentIds) { var document = project.Solution.GetDocument(documentId); if (!document.SupportsSyntaxTree) { continue; } if (filesToFormat != null) { var fileInArgumentList = filesToFormat.Any(relativePath => document.FilePath.EndsWith(relativePath, StringComparison.OrdinalIgnoreCase)); if (!fileInArgumentList) { continue; } } var formatTask = Task.Run(async() => { var syntaxTree = await document.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false); if (GeneratedCodeUtilities.IsGeneratedCode(syntaxTree, isCommentTrivia, cancellationToken)) { return(null); } logger.LogTrace(Resources.Formatting_code_file_0, Path.GetFileName(document.FilePath)); OptionSet documentOptions = await document.GetOptionsAsync(cancellationToken).ConfigureAwait(false); var codingConventionsContext = await codingConventionsManager.GetConventionContextAsync(document.FilePath, cancellationToken).ConfigureAwait(false); if (codingConventionsContext?.CurrentConventions != null) { documentOptions = optionsApplier.ApplyConventions(documentOptions, codingConventionsContext.CurrentConventions, project.Language); } var formattedDocument = await Formatter.FormatAsync(document, documentOptions, cancellationToken).ConfigureAwait(false); var formattedSourceText = await formattedDocument.GetTextAsync(cancellationToken).ConfigureAwait(false); if (formattedSourceText.ContentEquals(await document.GetTextAsync(cancellationToken).ConfigureAwait(false))) { // Avoid touching files that didn't actually change return(null); } return(formattedSourceText); }, cancellationToken); formattedDocuments.Add((documentId, formatTask)); } var formattedSolution = project.Solution; var filesFormatted = 0; foreach (var(documentId, formatTask) in formattedDocuments) { var text = await formatTask.ConfigureAwait(false); if (text is null) { continue; } filesFormatted++; formattedSolution = formattedSolution.WithDocumentText(documentId, text); } return(formattedSolution, filesFormatted); }
internal static async Task <(int, ImmutableArray <DocumentId>)> DetermineFormattableFilesAsync( Solution solution, string projectPath, FormatOptions formatOptions, ILogger logger, CancellationToken cancellationToken) { var totalFileCount = solution.Projects.Sum(project => project.DocumentIds.Count); var projectFileCount = 0; var documentsCoveredByEditorConfig = ImmutableArray.CreateBuilder <DocumentId>(totalFileCount); var documentsNotCoveredByEditorConfig = ImmutableArray.CreateBuilder <DocumentId>(totalFileCount); var addedFilePaths = new HashSet <string>(totalFileCount); foreach (var project in solution.Projects) { if (project?.FilePath is null) { continue; } // If a project is used as a workspace, then ignore other referenced projects. if (!string.IsNullOrEmpty(projectPath) && !project.FilePath.Equals(projectPath, StringComparison.OrdinalIgnoreCase)) { logger.LogDebug(Resources.Skipping_referenced_project_0, project.Name); continue; } // Ignore unsupported project types. if (project.Language != LanguageNames.CSharp && project.Language != LanguageNames.VisualBasic) { logger.LogWarning(Resources.Could_not_format_0_Format_currently_supports_only_CSharp_and_Visual_Basic_projects, project.FilePath); continue; } projectFileCount += project.DocumentIds.Count; foreach (var document in project.Documents) { // If we've already added this document, either via a link or multi-targeted framework, then ignore. if (document?.FilePath is null || addedFilePaths.Contains(document.FilePath)) { continue; } addedFilePaths.Add(document.FilePath); var isFileIncluded = formatOptions.WorkspaceType == WorkspaceType.Folder || (formatOptions.FileMatcher.HasMatches(document.FilePath) && File.Exists(document.FilePath)); if (!isFileIncluded || !document.SupportsSyntaxTree) { continue; } var syntaxTree = await document.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false); if (syntaxTree is null) { throw new Exception($"Unable to get a syntax tree for '{document.Name}'"); } if (!formatOptions.IncludeGeneratedFiles && await GeneratedCodeUtilities.IsGeneratedCodeAsync(syntaxTree, cancellationToken).ConfigureAwait(false)) { continue; } // Track files covered by an editorconfig separately from those not covered. var analyzerConfigOptions = document.Project.AnalyzerOptions.AnalyzerConfigOptionsProvider.GetOptions(syntaxTree); if (analyzerConfigOptions != null) { if (formatOptions.IncludeGeneratedFiles || GeneratedCodeUtilities.GetIsGeneratedCodeFromOptions(analyzerConfigOptions) != true) { documentsCoveredByEditorConfig.Add(document.Id); } } else { documentsNotCoveredByEditorConfig.Add(document.Id); } } } // Initially we would format all documents in a workspace, even if some files weren't covered by an // .editorconfig and would have defaults applied. This behavior was an early requested change since // users were surprised to have files not specified by the .editorconfig modified. The assumption is // that users without an .editorconfig still wanted formatting (they did run a formatter after all), // so we run on all files with defaults. // If no files are covered by an editorconfig, then return them all. Otherwise only return // files that are covered by an editorconfig. return(documentsCoveredByEditorConfig.Count == 0 ? (projectFileCount, documentsNotCoveredByEditorConfig.ToImmutable()) : (projectFileCount, documentsCoveredByEditorConfig.ToImmutable())); }
private static async Task <(Solution solution, int filesFormatted)> FormatFilesInProjectAsync(Project project, ICodingConventionsManager codingConventionsManager, EditorConfigOptionsApplier optionsApplier, ImmutableHashSet <string> filesToFormat, ILogger logger, CancellationToken cancellationToken) { var formattedDocuments = new List <(DocumentId documentId, Task <SourceText> formatTask)>(); foreach (var documentId in project.DocumentIds) { var document = project.Solution.GetDocument(documentId); if (!document.SupportsSyntaxTree) { continue; } if (!filesToFormat.IsEmpty && !filesToFormat.Contains(document.FilePath)) { continue; } var formatTask = Task.Run(async() => { if (await GeneratedCodeUtilities.IsGeneratedCodeAsync(document, cancellationToken)) { return(null); } logger.LogTrace(Resources.Formatting_code_file_0, Path.GetFileName(document.FilePath)); OptionSet documentOptions = await document.GetOptionsAsync(cancellationToken).ConfigureAwait(false); var codingConventionsContext = await codingConventionsManager.GetConventionContextAsync(document.FilePath, cancellationToken).ConfigureAwait(false); if (codingConventionsContext?.CurrentConventions != null) { documentOptions = optionsApplier.ApplyConventions(documentOptions, codingConventionsContext.CurrentConventions, project.Language); } var formattedDocument = await Formatter.FormatAsync(document, documentOptions, cancellationToken).ConfigureAwait(false); var formattedSourceText = await formattedDocument.GetTextAsync(cancellationToken).ConfigureAwait(false); if (formattedSourceText.ContentEquals(await document.GetTextAsync(cancellationToken).ConfigureAwait(false))) { // Avoid touching files that didn't actually change return(null); } logger.LogInformation(Resources.Formatted_code_file_0, Path.GetFileName(document.FilePath)); return(formattedSourceText); }, cancellationToken); formattedDocuments.Add((documentId, formatTask)); } var formattedSolution = project.Solution; var filesFormatted = 0; foreach (var(documentId, formatTask) in formattedDocuments) { var text = await formatTask.ConfigureAwait(false); if (text is null) { continue; } filesFormatted++; formattedSolution = formattedSolution.WithDocumentText(documentId, text); } return(formattedSolution, filesFormatted); }