From e830fbc2a32d62e3077e0179bfa2db5bb1f0b0c1 Mon Sep 17 00:00:00 2001 From: Jacques Bouthillier Date: Tue, 9 Jul 2019 09:46:34 -0400 Subject: [PATCH] use clang-tidy command with task to generate items in problem's view Enable running the `clang-tidy` C/C++ linting present from clang-tidy as a task. With the new updates, linting can be performed across an entire workspace using user specified checks and linting rules. The problem markers collected when executing the task will be recorded and displayed in the `problems-widget`. The result in the "Problems" view is persistent until you re-generates the task. Signed-off-by: Jacques Bouthillier --- packages/cpp/README.md | 60 +++++++++++++++++-- .../cpp/src/browser/cpp-task-provider.spec.ts | 11 +++- packages/cpp/src/browser/cpp-task-provider.ts | 31 +++++++++- 3 files changed, 92 insertions(+), 10 deletions(-) diff --git a/packages/cpp/README.md b/packages/cpp/README.md index bb9a30bbddeb1..f5fe3a62cab67 100644 --- a/packages/cpp/README.md +++ b/packages/cpp/README.md @@ -81,14 +81,14 @@ To get this working, you need to enable clangd's global index using the ## Using the clang-tidy linter -Note: This functionality is available when using clangd 9 and later. +**Note: This functionality is available when using clangd 9 and later.** -You can set the preference 'cpp.clangTidy' to enable the clang-tidy linter included in clangd. When the preference is set, there are two ways to chose which of its built-in checks clang-tidy will use: +You can set the preference 'cpp.clangTidy' to enable the clang-tidy linter included in clangd. When the preference is enabled, there are two ways to choose which of its built-in checks clang-tidy will use: - using the preferences: 'cpp.clangTidyChecks' -- using the file '.clang-tidy' . The file is located in the same folder of the files or a parent folder. +- using the file '.clang-tidy' . This file is located in the same folder of the files or a parent folder. -Note: using the preference checks will supersede the value found in the .clang-tidy file. +**Note**: When the preference setting for "cpp.clangTidyChecks" is set, the configs will be merged with the configuration found in the ".clang-tidy" file. If you want to drop the configs from ".clang-tidy", you'd need to disable it in "cpp.clangTidyChecks" with **"cpp.clangTidyChecks": "-*"**. The syntax used to fill the checks can be found at http://clang.llvm.org/extra/clang-tidy/ @@ -102,6 +102,58 @@ There are two ways to configure clang-tidy's checks: through a Theia preference - for the .clang-tidy file: Checks: "-*,readability-*" - Meaning: disable all list-checks and enable all readability-* checks +### Running clang-tidy as a task + +To be able to run clang-tidy as a task, you need to install the program. + +```bash +UNIX + sudo apt-get install clang-tidy +Note: not all operating system are currently supported with this linter yet. +``` + +``` +The clang-tidy task will read the "checks" and other configuration fields from the ".clang-tidy" file located in the closest parent directory of the source file. +P.S. You do not need to provide a value for each field. + +Those configurations fields can be: + Checks + CheckOptions + FormatStyle + HeaderFilterRegex + User + WarningsAsErrors + +You can run the following command to have an idea of the fields found in file ".clang-tidy" + clang-tidy -dump-config +``` + +In .theia/tasks.json, add the following: + +```json + { + "label": "[Task] clang-tidy", + "type": "shell", + "cwd": "${workspaceFolder}", + "command": "clang-tidy", + "args": [ + "*" + ], + "options": {}, + "problemMatcher": [ + "$clangTidyMatcher" + ] + } +``` + +If you want a description for each task field, see [theia/packages/task/src/browser/task-schema-updater.ts]( https://github.com/theia-ide/theia/blob/531aa3bde8dea7f022ea41beaee3aace65ce54de/packages/task/src/browser/task-schema-updater.ts#L62 ) + +```bash +When there is no compilation database, clang-tidy could run but you may need to be more specific which files to select. One way is to replace the "args" from the "*" to +"**/*.cpp" to only parse files with the "cpp" extension or +"**/*.c" to parse the "c" files extension. +``` + ## License - [Eclipse Public License 2.0](http://www.eclipse.org/legal/epl-2.0/) diff --git a/packages/cpp/src/browser/cpp-task-provider.spec.ts b/packages/cpp/src/browser/cpp-task-provider.spec.ts index 69d398dab2a7d..d41da0d04689a 100644 --- a/packages/cpp/src/browser/cpp-task-provider.spec.ts +++ b/packages/cpp/src/browser/cpp-task-provider.spec.ts @@ -20,9 +20,11 @@ import { TaskResolverRegistry } from '@theia/task/lib/browser/task-contribution' import { CppBuildConfigurationManager, CppBuildConfiguration } from './cpp-build-configurations'; import { Event } from '@theia/core'; import { expect } from 'chai'; -import { TaskConfiguration } from '@theia/task/src/common'; +import { TaskConfiguration } from '@theia/task/lib/common'; import { ProcessTaskConfiguration } from '@theia/task/lib/common/process/task-protocol'; -import { TaskDefinitionRegistry } from '@theia/task/lib/browser'; +import { TaskDefinitionRegistry } from '@theia/task/lib/browser/task-definition-registry'; +import { ProblemMatcherRegistry } from '@theia/task/lib/browser/task-problem-matcher-registry'; +import { ProblemPatternRegistry } from '@theia/task/lib/browser/task-problem-pattern-registry'; // The object under test. let taskProvider: CppTaskProvider; @@ -67,9 +69,12 @@ class MockCppBuildConfigurationManager implements CppBuildConfigurationManager { beforeEach(function () { const container: Container = new Container(); container.bind(CppTaskProvider).toSelf().inSingletonScope(); + container.bind(CppBuildConfigurationManager).to(MockCppBuildConfigurationManager); container.bind(TaskResolverRegistry).toSelf().inSingletonScope(); container.bind(TaskDefinitionRegistry).toSelf().inSingletonScope(); - container.bind(CppBuildConfigurationManager).to(MockCppBuildConfigurationManager); + container.bind(ProblemMatcherRegistry).toSelf().inSingletonScope(); + container.bind(ProblemPatternRegistry).toSelf().inSingletonScope(); + taskProvider = container.get(CppTaskProvider); // Register a task resolver of type 'shell', on which the cpp build tasks diff --git a/packages/cpp/src/browser/cpp-task-provider.ts b/packages/cpp/src/browser/cpp-task-provider.ts index 0c8287e0210a0..e6dbb715d57dd 100644 --- a/packages/cpp/src/browser/cpp-task-provider.ts +++ b/packages/cpp/src/browser/cpp-task-provider.ts @@ -17,10 +17,12 @@ import parseArgv = require('string-argv'); import { inject, injectable, postConstruct } from 'inversify'; import { ProcessTaskConfiguration } from '@theia/task/lib/common/process/task-protocol'; -import { TaskDefinitionRegistry } from '@theia/task/lib/browser'; import { TaskContribution, TaskProvider, TaskProviderRegistry, TaskResolver, TaskResolverRegistry } from '@theia/task/lib/browser/task-contribution'; import { CppBuildConfigurationManager, CppBuildConfiguration } from './cpp-build-configurations'; -import { ContributedTaskConfiguration, TaskConfiguration, } from '@theia/task/lib/common/task-protocol'; +import { ContributedTaskConfiguration, TaskConfiguration } from '@theia/task/lib/common/task-protocol'; +import { TaskDefinitionRegistry } from '@theia/task/lib/browser/task-definition-registry'; +import { ProblemMatcherRegistry } from '@theia/task/lib/browser/task-problem-matcher-registry'; +import { ProblemPatternRegistry } from '@theia/task/lib/browser/task-problem-pattern-registry'; /** * Data required to define a C/C++ build task the user could run. @@ -38,10 +40,33 @@ export class CppTaskProvider implements TaskContribution, TaskProvider, TaskReso @inject(TaskResolverRegistry) protected readonly taskResolverRegistry: TaskResolverRegistry; @inject(TaskDefinitionRegistry) protected readonly taskDefinitionRegistry: TaskDefinitionRegistry; @inject(CppBuildConfigurationManager) protected readonly cppBuildConfigurationManager: CppBuildConfigurationManager; + @inject(ProblemMatcherRegistry) protected readonly problemMatcherRegistry: ProblemMatcherRegistry; + @inject(ProblemPatternRegistry) protected readonly problemPatternRegistry: ProblemPatternRegistry; @postConstruct() protected init(): void { this.registerTaskDefinition(); + this.problemPatternRegistry.register({ + 'name': 'clangTidyPattern', + 'regexp': '^(.+):(\\d+):(\\d+):\\s+(error|warning|info|note):\\s+(.+?)\\s+\\[(.+)\\]$', + 'file': 1, + 'line': 2, + 'character': 3, + 'severity': 4, + 'message': 5, + 'code': 6 + }); + this.problemMatcherRegistry.register({ + 'name': 'clangTidyMatcher', + 'label': 'Clang-tidy problems', + 'owner': 'clang-tidy', + 'source': 'clang-tidy-task', + 'applyTo': 'alldocuments', + 'fileLocation': [ + 'absolute' + ], + 'pattern': 'clangTidyPattern' + }); } registerProviders(registry: TaskProviderRegistry) { @@ -77,7 +102,7 @@ export class CppTaskProvider implements TaskContribution, TaskProvider, TaskReso type: 'shell', command, args, - options: { cwd: task.config.directory } + cwd: task.config.directory, }; return resolver.resolveTask(resolvedTask); }