This commit is contained in:
İsmail Yılmaz 2026-08-10 10:40:51 +05:30 committed by GitHub
commit a6cabeb488
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
20 changed files with 1425 additions and 2 deletions

View file

@ -0,0 +1,173 @@
#include "ClangTidy.h"
static String sExeFilePath;
String ClangTidy::GetConfigFilePath() const
{
return ConfigFile(AppendFileName("clangtidy", IdeGetCurrentMainPackage() + "-clangtidy.json"));
}
Value ClangTidy::LoadConfig()
{
String path = GetConfigFilePath();
if(!FileExists(path))
return Null;
return ParseJSON(LoadFile(path));
}
void ClangTidy::SaveConfig(const Value& cfg)
{
String path = GetConfigFilePath();
RealizePath(path);
SaveChangedFile(path, cfg);
}
bool ClangTidy::Exists() const
{
static bool b = false;
ONCELOCK
{
#ifdef flagWIN32
// FIXME: Check path.
constexpr const char *exe = "C:\\Program Files\\LLVM\\bin\\clang-tidy.exe";
b = FileExists(exe);
if(b) sExeFilePath << "\"" << exe << "\"";
#else
b = Sys("which clang-tidy", sExeFilePath) == 0;
if(b) sExeFilePath = TrimRight(sExeFilePath);
else sExeFilePath.Clear();
#endif
}
return b && TheIde();
}
void ClangTidy::Settings()
{
ClangTidyConfigDlg dlg(*this);
dlg.Load();
if(dlg.ExecuteOK())
dlg.Save();
}
Vector<String> ClangTidy::ResolveProject(const String& ccjpath)
{
Vector<String> files;
Value ccj = ParseJSON(LoadFile(ccjpath));
for(int i = 0; i < ccj.GetCount(); i++)
files.Add(ccj[i]["file"]);
return files;
}
Vector<String> ClangTidy::ResolvePackage(const String& ccjpath, const Vector<String>& paths)
{
Vector<String> files;
Value ccj = ParseJSON(LoadFile(ccjpath));
for(int i = 0; i < ccj.GetCount(); i++) {
String file = ccj[i]["file"];
// Length-check prevents prefix collisions (e.g. /upp/Core vs /upp/Core2)
for(const String& p : paths) {
int pl = p.GetLength();
if(file.StartsWith(p) && (file.GetLength() == pl || file[pl] == '/' || file[pl] == '\\')) {
files.Add(file);
break;
}
}
}
return files;
}
Vector<String> ClangTidy::ResolveFiles(Scope sc, const String& ccjpath, const Vector<String>& paths)
{
bool hasccj = FileExists(ccjpath);
switch(sc) {
case Scope::File:
return clone(paths);
case Scope::Project:
return hasccj ? ResolveProject(ccjpath) : clone(paths);
case Scope::Package:
return hasccj ? ResolvePackage(ccjpath, paths) : clone(paths);
}
return clone(paths);
}
String ClangTidy::MakeCmdLine(Scope sc, Vector<String>& paths)
{
Value v = LoadConfig()["ClangTidy"];
// Is there a better way to obtain compile_commands.json file?
String ccjdir = GetTempPath();
String ccjpath = AppendFileName(ccjdir, "compile_commands.json");
if(MakeBuild *mb = dynamic_cast<MakeBuild *>(TheIdeContext()))
mb->SaveCCJ(ccjpath, false);
String path;
for(const String& f : ResolveFiles(sc, ccjpath, paths))
path << "\"" << f << "\" ";
if(IsNull(v))
return sExeFilePath + " --checks=* " + path;
String checks = Nvl(v["checks"], "*");
String extraargs = Nvl(v["extra_args"], "");
String standard = Nvl(v["standard"], "c++14");
String s;
s << sExeFilePath << " "
<< "--checks=\"" << checks << "\" "
<< "--quiet ";
if(FileExists(ccjpath))
s << "-p=\"" << ccjdir << "\" ";
return s + extraargs + (extraargs.GetCount() ? " " : "") + path
+ (FileExists(ccjpath) ? "" : "-- -std=" + standard);
}
void ClangTidy::OnResults(const String& results)
{
// FIXME: This is fragile, but not all versions of clang have JSON output...
RegExp r("([A-Za-z]?:?[/\\\\][^:]+\\.[ch]pp):(\\d+):(\\d+): (\\w+): (.+?)(\\s+\\[[\\w,.-]+\\])?$");
for(const String& line : Split(results, '\n', false)) {
if(!r.Match(line))
continue;
Vector<String> v = r.GetStrings();
if(v.GetCount() < 5)
continue;
String severity = v.At(3);
if(severity == "note")
continue;
Ide::ListLineInfo e;
e.file = v.At(0);
e.lineno = StrInt(v.At(1));
e.linepos = StrInt(v.At(2));
e.kind = severity == "error" ? 0 : 1;
e.message = v.At(4) + TrimLeft(v.At(5));
Image img = decode(
severity,
"error", LinterImg::error(),
"warning", LinterImg::warning(),
LinterImg::warning()
);
Color paper = HighlightSetup::GetHlStyle(e.kind == 0 ? HighlightSetup::PAPER_ERROR
: HighlightSetup::PAPER_WARNING).color;
int linecy;
AttrText txt(TheIde()->FormatErrorLine(e.message, linecy));
txt.NormalPaper(paper);
txt.SetImage(img);
ArrayCtrl& error = TheIde()->error;
error.Add(e.file, e.lineno, txt, RawToValue(e));
error.SetLineCy(error.GetCount() - 1, linecy);
}
}
INITIALIZER(ClangTidy)
{
RegisterLinterModule(Single<ClangTidy>("ClangTidy"));
}

View file

@ -0,0 +1,39 @@
#ifndef _ClangTidy_h_
#define _ClangTidy_h_
#include "Linter.h"
#define LAYOUTFILE <ide/Linter/ClangTidyConfig.lay>
#include <CtrlCore/lay.h>
class ClangTidy : public Linter {
public:
ClangTidy(const String& name) : Linter(name) {}
virtual String GetConfigFilePath() const final;
virtual Value LoadConfig() final;
virtual void SaveConfig(const Value& cfg) final;
virtual bool Exists() const final;
virtual void Settings() final;
private:
Vector<String> ResolveProject(const String& ccjpath);
Vector<String> ResolvePackage(const String& ccjpath, const Vector<String>& paths);
Vector<String> ResolveFiles (Scope sc, const String& ccjpath, const Vector<String>& paths);
virtual String MakeCmdLine(Scope sc, Vector<String>& paths) final;
virtual void OnResults(const String& results) final;
};
INITIALIZE(ClangTidy);
class ClangTidyConfigDlg : public WithClangTidyConfigLayout<TopWindow>, public Linter::Config {
public:
ClangTidyConfigDlg(Linter& l);
virtual void Load() final;
virtual void Save() final;
virtual void Reset() final;
};
#endif

View file

@ -0,0 +1,19 @@
#include "Linter.h"
ClangTidyConfigDlg::ClangTidyConfigDlg(Linter& l)
: Linter::Config(l)
{
}
void ClangTidyConfigDlg::Load()
{
}
void ClangTidyConfigDlg::Save()
{
}
void ClangTidyConfigDlg::Reset()
{
}

View file

@ -0,0 +1,3 @@
LAYOUT(ClangTidyConfigLayout, 400, 200)
END_LAYOUT

22
uppsrc/ide/Linter/Copying Normal file
View file

@ -0,0 +1,22 @@
Copyright (c) 1998, 2023, The U++ Project
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted
provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of
conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of
conditions and the following disclaimer in the documentation and/or other materials provided
with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

View file

@ -0,0 +1,167 @@
#include "CppCheck.h"
static String sExeFilePath;
static bool sVerboseMode = false;
String CppCheck::GetConfigFilePath() const
{
return ConfigFile(AppendFileName("cppcheck", IdeGetCurrentMainPackage() + "-cppcheck.json"));
}
Value CppCheck::LoadConfig()
{
String path = GetConfigFilePath();
if(!FileExists(path))
return Null;
return ParseJSON(LoadFile(path));
}
void CppCheck::SaveConfig(const Value& cfg)
{
String path = GetConfigFilePath();
RealizePath(path);
SaveChangedFile(path, cfg);
}
bool CppCheck::Exists() const
{
static bool b = false;
ONCELOCK {
#ifdef flagWIN32
constexpr const char *exe = "C:\\Program Files\\CppCheck\\cppcheck.exe";
b = FileExists(exe);
if(b) sExeFilePath << "\"" << exe << "\"";
#else
b = Sys("which cppcheck", sExeFilePath) == 0;
if(b) sExeFilePath = TrimRight(sExeFilePath);
else sExeFilePath.Clear();
#endif
}
return b && TheIde();
}
void CppCheck::Settings()
{
CppCheckConfigDlg dlg(static_cast<Linter&>(*this));
dlg.Load();
if(dlg.ExecuteOK())
dlg.Save();
}
String CppCheck::MakeCmdLine(Scope sc, Vector<String>& paths)
{
String path;
for(const String& s : paths)
path << "\"" << s << "\" ";
Value v = LoadConfig()["CppCheck"];
if(IsNull(v))
return sExeFilePath
+ " --language=c++ --std=c++17 --platform=native"
" --enable=all --xml -i *.tpp " + path;
int depth = v["depth"];
int jobs = v["jobs"];
String opts = v["cmdline_options"];
Vector<String> severity;
for(const Value& q : v["severity"])
if(!IsNull(q))
severity.Add() = q;
String s;
s << sExeFilePath << " "
<< "--force "
<< "--xml "
<< "--language=" << Nvl(v["language"], "c++") << " "
<< "--platform=" << Nvl(v["platform"], "native") << " "
<< "--std=" << Nvl(v["standard"], "c++17") << " "
<< "--max-ctu-depth=" << AsString(clamp(depth, 1, 6)) << " "
<< "-j " << AsString(clamp(jobs, 1, 1024)) << " ";
if(severity.GetCount())
s << "--enable=" << Join(severity, ",", true) << " ";
for(const String& q : v["libraries"])
if(FileExists(q))
s << "--library=\"" << q << "\" ";
for(const String& q : v["addons"])
if(FileExists(q))
s << "--plugin=\"" << q << "\" ";
sVerboseMode = v["verbose_mode"];
// Ensure clean spacing between user options and paths
return s + opts + (opts.GetCount() ? " " : "") + path;
}
void CppCheck::OnResults(const String& results)
{
DecodeXML(ParseXML(results)["results"]);
}
void CppCheck::DecodeXML(const XmlNode& results)
{
if(results.IsTag("results")) {
DecodeXML(results["errors"]);
}
else
if(results.IsTag("errors")) {
for(const XmlNode& node : results) {
if(!node.IsTag("error"))
continue;
const XmlNode& loc = node["location"];
String severity = node.Attr("severity");
Ide::ListLineInfo e;
e.file = loc.Attr("file");
e.lineno = StrInt(loc.Attr("line"));
e.linepos = StrInt(loc.Attr("column"));
e.kind = severity == "error" ? 0 : 1;
Image img = decode(
severity,
"warning", LinterImg::warning(),
"style", LinterImg::style(),
"performance", LinterImg::performance(),
"portability", LinterImg::portability(),
"information", LinterImg::information(),
"unusedFunction", LinterImg::unusedfunction(),
"missingInclude", LinterImg::missinginclude(),
LinterImg::error()
);
Color paper = HighlightSetup::GetHlStyle(e.kind == 0 ? HighlightSetup::PAPER_ERROR
: HighlightSetup::PAPER_WARNING).color;
e.message = node.Attr("msg");
int linecy;
AttrText txt(TheIde()->FormatErrorLine(e.message, linecy));
txt.NormalPaper(paper);
txt.SetImage(img);
ArrayCtrl& error = TheIde()->error;
error.Add(e.file, e.lineno, txt, RawToValue(e));
ValueArray notes;
if(sVerboseMode) {
e.message = node.Attr("verbose");
if(e.message.GetCount())
notes.Add(RawToValue(e));
}
if(node.Attr("inconclusive") == "true") {
e.message = "[Note that this is an inconclusive result!]";
notes.Add(RawToValue(e));
}
error.Set(error.GetCount() - 1, "NOTES", notes);
error.SetLineCy(error.GetCount() - 1, linecy);
}
}
}
INITIALIZER(CppCheck)
{
RegisterLinterModule(Single<CppCheck>("CppCheck"));
}

View file

@ -0,0 +1,45 @@
#ifndef _ide_Linter_CppCheck_h_
#define _ide_Linter_CppCheck_h_
#include "Linter.h"
#define LAYOUTFILE <ide/Linter/CppCheckConfig.lay>
#include <CtrlCore/lay.h>
class CppCheck final : public Linter {
public:
CppCheck(const String& name) : Linter(name) {}
String GetConfigFilePath() const final;
Value LoadConfig() final;
void SaveConfig(const Value& cfg) final;
bool Exists() const final;
void Settings() final;
protected:
String MakeCmdLine(Scope sc, Vector<String>& paths) final;
void OnResults(const String& results) final;
private:
void DecodeXML(const XmlNode& results);
};
INITIALIZE(CppCheck);
struct CppCheckConfigDlg final : Linter::Config, WithCppCheckConfigLayout<TopWindow> {
CppCheckConfigDlg(Linter& l);
void Load() final;
void Save() final;
void Reset() final;
struct Pane : WithCppCheckConfigPaneLayout<ParentCtrl> {
Pane();
void SetData(const Value& data) final;
Value GetData() const final;
void Load(const String& path, const String& ext);
} libs, addons;
};
#endif

View file

@ -0,0 +1,20 @@
LAYOUT(CppCheckSetupLayout, 436, 384)
ITEM(Upp::DropList, language, HCenterPosZ(64, 32).TopPosZ(20, 19))
ITEM(Upp::DropList, standard, HCenterPosZ(64, 32).TopPosZ(44, 19))
ITEM(Upp::DropList, platform, HCenterPosZ(64, 32).TopPosZ(68, 19))
ITEM(Upp::DropList, depth, HCenterPosZ(64, 32).TopPosZ(92, 19))
ITEM(Upp::EditInt, jobs, Min(1).Max(6).HCenterPosZ(64, 32).TopPosZ(120, 19))
ITEM(Upp::Option, style, SetLabel(t_("Style")).HCenterPosZ(160, 0).TopPosZ(188, 16))
ITEM(Upp::Option, performance, SetLabel(t_("Performance")).HCenterPosZ(160, 0).TopPosZ(208, 16))
ITEM(Upp::Option, portability, SetLabel(t_("Portability")).HCenterPosZ(160, 0).TopPosZ(228, 16))
ITEM(Upp::Option, information, SetLabel(t_("Information")).HCenterPosZ(160, 0).TopPosZ(248, 16))
ITEM(Upp::Option, unusedfunction, SetLabel(t_("Unused functions")).HCenterPosZ(160, 0).TopPosZ(268, 16))
ITEM(Upp::Option, missinginclude, SetLabel(t_("Missing Includes")).HCenterPosZ(160, 0).TopPosZ(288, 16))
ITEM(Upp::LabelBox, dv___11, SetLabel(t_("Additional Checks")).SetFont(Serif()).HCenterPosZ(188, 0).TopPosZ(160, 160))
ITEM(Upp::Label, dv___12, SetLabel(t_("Threads")).HCenterPosZ(56, -36).TopPosZ(120, 19))
ITEM(Upp::Label, dv___13, SetLabel(t_("Language")).HCenterPosZ(56, -36).TopPosZ(20, 19))
ITEM(Upp::Label, dv___14, SetLabel(t_("Standard")).HCenterPosZ(56, -36).TopPosZ(44, 19))
ITEM(Upp::Label, dv___15, SetLabel(t_("Depth")).HCenterPosZ(56, -36).TopPosZ(92, 19))
ITEM(Upp::Label, dv___16, SetLabel(t_("Platform")).HCenterPosZ(56, -36).TopPosZ(68, 19))
END_LAYOUT

View file

@ -0,0 +1,205 @@
#include "CppCheck.h"
CppCheckConfigDlg::CppCheckConfigDlg(Linter& l)
: Linter::Config(l)
{
CtrlLayoutOKCancel(*this, "CppCheck Settings");
for(const Value& v : { "c", "c++"})
language.Add(v);
for(const Value& v : { "c89", "c99", "c11", "c++03", "c++11", "c++14", "c++17", "c++20"})
standard.Add(v);
for(const Value& v : { "native", "unix32", "unix64", "win32A", "win32W", "win64"})
platform.Add(v);
for(int i = 0; i < 6; i++)
depth.Add(i);
CtrlLayout(libs);
CtrlLayout(addons);
libs.dirpath.WhenAction = [this] { libs.Load(SelectDirectory(), "*.cfg"); };
addons.dirpath.WhenAction = [this] { addons.Load(SelectDirectory(), "*.py"); };
tabs.Add(libs.SizePos(), "Libraries");
tabs.Add(addons.SizePos(), "Addons");
defaults.WhenAction = [this] { Reset(); };
Reset();
}
void CppCheckConfigDlg::Reset()
{
language.SetIndex(1);
standard.SetIndex(5);
platform.SetIndex(0);
depth.SetIndex(1);
jobs.MinMax(1, CPU_Cores()) <<= CPU_Cores();
warning = false;
style = false;
performance = false;
portability = false;
information = false;
unusedfunction = false;
missinginclude = false;
verbose = false;
options <<= "-isrc.tpp -isrcdoc.tpp";
#ifdef flagWIN32
constexpr const char *deflibrarypath = "C:\\Program Files\\CppCheck\\cfg";
constexpr const char *defpluginspath = "C:\\Program Files\\CppCheck\\addons";
#else
constexpr const char *deflibrarypath = "/usr/share/cppcheck/cfg";
constexpr const char *defpluginspath = "/usr/share/cppcheck/addons";
#endif
libs.Load(deflibrarypath, "*.cfg");
addons.Load(defpluginspath, "*.py");
Title("CppCheck Configuration [" + IdeGetCurrentMainPackage() + "]");
}
void CppCheckConfigDlg::Load()
{
Reset();
try
{
Value v = linter.LoadConfig()["CppCheck"];
if(IsNull(v))
return;
auto LoadList = [this, &v](DropList& lst, const String& id, const Value& def)
{
int i = lst.FindValue(v[id]);
lst.SetIndex(i >= 0 ? i : lst.FindValue(def));
};
LoadList(language, "language", "c++");
LoadList(standard, "standard", "c++14");
LoadList(platform, "platform", "native");
LoadList(depth, "depth", 2);
jobs <<= clamp((int) v["jobs"], 1, INT_MAX);
options <<= v["cmdline_options"];
for(const Value& q : v["severity"]) {
if(q == "warning")
warning = true;
else
if(q == "style")
style = true;
else
if(q == "performance")
performance = true;
else
if(q == "portability")
portability = true;
else
if(q == "information")
information = true;
else
if(q == "unusedFunction")
unusedfunction = true;
else
if(q == "missingInclude") // FIXED TYPO: Was "missingInglude"
missinginclude = true;
}
libs <<= v["libraries"];
addons <<= v["addons"];
verbose <<= v["verbose_mode"];
}
catch(...)
{
Reset();
}
}
void CppCheckConfigDlg::Save()
{
JsonArray jl, jq;
for(const String& s : ~libs) jl << s;
for(const String& s : ~addons) jq << s;
JsonArray ja;
if(~warning) ja << "warning";
if(~style) ja << "style";
if(~performance) ja << "performance";
if(~portability) ja << "portability";
if(~information) ja << "information";
if(~unusedfunction) ja << "unusedFunction";
if(~missinginclude) ja << "missingInclude";
Json j;
j("language", language.GetValue());
j("standard", standard.GetValue());
j("platform", platform.GetValue());
j("depth", depth.GetValue());
j("jobs", ~jobs);
j("severity", ja);
j("libraries", jl);
j("addons", jq);
j("cmdline_options", ~options);
j("verbose_mode", ~verbose);
linter.SaveConfig(Json("CppCheck", j).ToString());
}
CppCheckConfigDlg::Pane::Pane()
{
struct NameDisplay : Display
{
void Paint(Draw& w, const Rect& r, const Value& q, Color ink, Color paper, dword style) const override
{
StdDisplay().Paint(w, r, GetFileTitle(q.To<String>()), ink, paper, style);
};
};
list.AddColumn("Enable").Ctrls<Option>();
list.AddColumn("Name").SetDisplay(Single<NameDisplay>());;
list.ColumnWidths("20 300");
dirpath.NullText("Select a valid library path");
}
void CppCheckConfigDlg::Pane::SetData(const Value& data)
{
if(IsValueArray(data)) {
for(const Value& q : data) {
int i = list.Find(q, 1);
if(i >= 0)
list.Set(i, 0, true);
}
}
}
Value CppCheckConfigDlg::Pane::GetData() const
{
ValueArray va;
for(int i = 0; i < list.GetCount(); i++)
if(list.Get(i, 0) == true)
va << list.Get(i, 1);
return va;
}
void CppCheckConfigDlg::Pane::Load(const String& path, const String& ext)
{
if(path.IsEmpty())
return;
if(!DirectoryExists(path)) {
dirpath <<= Null;
return;
}
list.Clear();
for(const FindFile& f : FindFile(AppendFileName(path, ext)))
list.Add(false, f.GetPath());
dirpath <<= path;
list.Enable(list.GetCount());
}

View file

@ -0,0 +1,34 @@
LAYOUT(CppCheckConfigLayout, 620, 408)
ITEM(Upp::DropList, language, LeftPosZ(79, 64).TopPosZ(12, 19))
ITEM(Upp::DropList, standard, LeftPosZ(79, 64).TopPosZ(36, 19))
ITEM(Upp::DropList, platform, LeftPosZ(79, 64).TopPosZ(60, 19))
ITEM(Upp::DropList, depth, LeftPosZ(79, 64).TopPosZ(84, 19))
ITEM(Upp::EditInt, jobs, Min(1).Max(6).LeftPosZ(79, 64).TopPosZ(108, 19))
ITEM(Upp::Option, warning, SetLabel(t_("Warnings")).LeftPosZ(16, 124).TopPosZ(176, 16))
ITEM(Upp::Option, style, SetLabel(t_("Style")).LeftPosZ(16, 124).TopPosZ(196, 16))
ITEM(Upp::Option, performance, SetLabel(t_("Performance")).LeftPosZ(16, 124).TopPosZ(216, 16))
ITEM(Upp::Option, portability, SetLabel(t_("Portability")).LeftPosZ(16, 124).TopPosZ(236, 16))
ITEM(Upp::Option, information, SetLabel(t_("Information")).LeftPosZ(16, 124).TopPosZ(256, 16))
ITEM(Upp::Option, unusedfunction, SetLabel(t_("Unused functions")).LeftPosZ(16, 124).TopPosZ(276, 16))
ITEM(Upp::Option, missinginclude, SetLabel(t_("Missing Includes")).LeftPosZ(16, 124).TopPosZ(296, 16))
ITEM(Upp::TabCtrl, tabs, HSizePosZ(164, 8).VSizePosZ(12, 84))
ITEM(Upp::EditString, options, HSizePosZ(116, 8).BottomPosZ(53, 19))
ITEM(Upp::Button, defaults, SetLabel(t_("Restore defaults")).LeftPosZ(8, 116).BottomPosZ(8, 24))
ITEM(Upp::Option, verbose, SetLabel(t_("Be verbose")).LeftPosZ(144, 124).BottomPosZ(12, 16))
ITEM(Upp::Button, cancel, SetLabel(t_("Cancel")).RightPosZ(128, 116).BottomPosZ(8, 24))
ITEM(Upp::Button, ok, SetLabel(t_("OK")).RightPosZ(8, 116).BottomPosZ(8, 24))
ITEM(Upp::Label, dv___18, SetLabel(t_("Threads")).LeftPosZ(16, 56).TopPosZ(108, 19))
ITEM(Upp::Label, dv___19, SetLabel(t_("Language")).LeftPosZ(16, 56).TopPosZ(12, 19))
ITEM(Upp::Label, dv___20, SetLabel(t_("Standard")).LeftPosZ(16, 56).TopPosZ(36, 19))
ITEM(Upp::Label, dv___21, SetLabel(t_("Max depth")).LeftPosZ(16, 56).TopPosZ(84, 19))
ITEM(Upp::Label, dv___22, SetLabel(t_("Platform")).LeftPosZ(16, 56).TopPosZ(60, 19))
ITEM(Upp::Label, dv___23, SetLabel(t_("Additional checks")).LeftPosZ(16, 124).TopPosZ(148, 16))
ITEM(Upp::Label, dv___24, SetLabel(t_("Additional options")).LeftPosZ(16, 96).BottomPosZ(53, 19))
END_LAYOUT
LAYOUT(CppCheckConfigPaneLayout, 408, 264)
ITEM(Upp::ArrayCtrl, list, AutoHideSb(true).Header(false).AskRemove(false).VertGrid(false).HorzGrid(false).NoCursor(true).HSizePosZ(4, 0).VSizePosZ(28, 0))
ITEM(Upp::Label, dv___1, SetLabel(t_("Path")).LeftPosZ(4, 36).TopPosZ(4, 19))
ITEM(Upp::DataPusher, dirpath, HSizePosZ(44, 0).TopPosZ(4, 19))
END_LAYOUT

View file

@ -0,0 +1,248 @@
#include "Linter.h"
#define IMAGECLASS LinterImg
#define IMAGEFILE <ide/Linter/Linter.iml>
#include <Draw/iml_source.h>
#define KEYGROUPNAME "Linter"
#define KEYNAMESPACE LinterKeys
#define KEYFILE <ide/Linter/Linter.key>
#include <CtrlLib/key_source.h>
using namespace LinterKeys;
static String sActiveModuleName;
static Vector<Linter*>& sLM()
{
static Vector<Linter*> m;
return m;
}
void RegisterLinterModule(Linter& linter_module)
{
sLM().Add(&linter_module);
}
int GetLinterModuleCount()
{
return sLM().GetCount();
}
Linter& GetLinterModule(int i)
{
ASSERT(i >= 0 && i < GetLinterModuleCount());
return *sLM()[i];
}
Linter* GetActiveLinterModulePtr()
{
for(Linter *p : sLM())
if(p->Exists() && p->GetName() == sActiveModuleName)
return p;
for(Linter *p : sLM())
if(p->Exists()) {
sActiveModuleName = p->GetName();
return p;
}
return nullptr;
}
bool Linter::CanCheck() const
{
return !running
&& TheIde()
&& TheIde()->idestate == Ide::EDITING
&& !IdeIsDebugLock();
}
String Linter::GetFileName() const
{
return Nvl(TheIde()->GetActiveFileName(), Upp::GetFileName(TheIde()->editfile));
}
String Linter::GetFilePath() const
{
return Nvl(TheIde()->GetActiveFilePath(), TheIde()->editfile);
}
String Linter::GetPackageName() const
{
return TheIde()->GetActivePackage();
}
String Linter::GetPackagePath() const
{
return TheIde()->GetActivePackageDir();
}
void Linter::CheckFile()
{
if(!Exists())
return;
Vector<String> paths = { GetFilePath() };
DoCheck(Scope::File, paths);
}
void Linter::CheckPackage()
{
if(!Exists())
return;
Vector<String> paths = { GetPackagePath() };
DoCheck(Scope::Package, paths);
}
void Linter::CheckProject()
{
if(!Exists())
return;
Vector<String> paths;
const Workspace& wspc = GetIdeWorkspace();
for(int i = 0; i < wspc.GetCount(); i++)
paths.Add() = PackageDirectory(wspc[i]);
DoCheck(Scope::Project, paths);
}
void Linter::SysCmd(const String& cmd, const String& text, Stream& fs)
{
MakeBuild *mb = dynamic_cast<MakeBuild *>(TheIdeContext());
if(!mb)
throw Exc("Cannot get TheIDE context");
Host host;
mb->CreateHost(host, false, false);
LocalProcess p;
if(!host.StartProcess(p, ~cmd))
throw Exc("Cannot start linter process");
Progress pi;
pi.Title("Linter");
pi.SetText(text);
for(;;) {
String out = p.Get();
if(p.IsRunning()) {
if(!IsNull(out))
fs.Put(out);
}
else {
if(out.IsVoid()) {
p.Kill();
break;
}
else
fs.Put(out);
}
if(pi.StepCanceled()) {
pi.Close();
p.Kill();
throw Exc("User break.");
}
IdeProcessEvents();
}
}
void Linter::DoCheck(Scope sc, Vector<String>& paths)
{
if(running)
return;
running = true;
Ide *ide = TheIde();
String tmp = GetTempFileName();
try {
FileOut fo(tmp);
if(!fo)
throw Exc("Unable to open temporary file");
ide->ConsoleClear();
ide->ShowConsole();
ide->PutConsole("Running linter..");
String text = "Analyzing " + (paths.GetCount() == 1 ? Upp::GetFileName(paths[0]) : "all packages");
SysCmd(MakeCmdLine(sc, paths), text, fo);
fo.Close();
ide->Sync();
ide->PutConsole("Parsing linter output..");
String rawresults = LoadFile(tmp);
DeleteFile(tmp);
ide->ClearErrorsPane();
OnResults(rawresults);
ide->PutConsole("done");
if(ide->error.GetCount()) {
ide->BeepMuteExclamation();
ide->SetBottom(Ide::BERRORS);
}
else
ide->BeepMuteInformation();
}
catch(const Exc& e) {
ide->PutConsole(e);
ide->BeepMuteExclamation();
if(FileExists(tmp))
DeleteFile(tmp);
}
running = false;
}
void sListMenu(Linter& l, Bar& menu)
{
auto list = [&l](Bar& menu) {
Vector<int> ndx = FindAll(sLM(), [](const Linter *p) { return p->Exists(); });
for(int i : ndx) {
const Linter& q = GetLinterModule(i);
menu.Add(q.GetName(), [&q]() { sActiveModuleName = q.GetName(); })
.Radio(q.GetName() == l.GetName());
}
menu.Separator();
menu.Add("Configure " + l.GetName(), [&l]() { l.Settings(); })
.Key(AK_CONFIGURE);
};
menu.Sub("Static analyzers", list);
}
void sFileMenu(Linter& l, String name, Bar& menu)
{
menu.Add(l.CanCheck(), "Analyze " + name, [&l]() { l.CheckFile(); })
.Key(AK_CHECKFILE);
}
void sPackageMenu(Linter& l, String name, Bar& menu)
{
menu.Add(l.CanCheck(), "Analyze package " + name, [&l]() { l.CheckPackage(); })
.Key(AK_CHECKPACKAGE);
}
void Linter::StdMenu(Bar& menu)
{
Linter *p = GetActiveLinterModulePtr();
if(!p)
return;
sListMenu(*p, menu);
sFileMenu(*p, p->GetFileName(), menu);
sPackageMenu(*p, p->GetPackageName(), menu);
menu.Add(p->CanCheck(), "Analyze all..", [p]() { p->CheckProject(); })
.Key(AK_CHECKALL);
menu.Separator();
}
void Linter::FileMenu(Bar& menu)
{
Linter *p = GetActiveLinterModulePtr();
if(p) sFileMenu(*p, p->GetFileName(), menu);
}
void Linter::PackageMenu(Bar& menu)
{
Linter *p = GetActiveLinterModulePtr();
if(p) sPackageMenu(*p, p->GetPackageName(), menu);
}
INITIALIZER(Linter)
{
RegisterGlobalSerialize("Linters", [](Stream& s) {
int version = 0;
s / version;
s % sActiveModuleName;
});
}

View file

@ -0,0 +1,82 @@
#ifndef TheIde_Linter_h
#define TheIde_Linter_h
#include <Core/Core.h>
#include <CtrlLib/CtrlLib.h>
#include <ide/Common/Common.h>
#include <ide/Core/Core.h>
#include <ide/ide.h>
#define IMAGECLASS LinterImg
#define IMAGEFILE <ide/Linter/Linter.iml>
#include <Draw/iml_header.h>
// Base class for command-line driven linter modules (static analyzers)
class Linter {
public:
Linter(const String& name) : module_name(name) {}
virtual ~Linter() {}
virtual String GetConfigFilePath() const = 0;
virtual Value LoadConfig() = 0;
virtual void SaveConfig(const Value& cfg) = 0;
virtual bool Exists() const = 0;
virtual void Settings() = 0;
String GetName() const { return module_name; }
bool CanCheck() const;
void CheckFile();
void CheckPackage();
void CheckProject();
static void StdMenu(Bar& menu);
static void FileMenu(Bar& menu);
static void PackageMenu(Bar& menu);
class Config {
public:
Config(Linter& l) : linter(l) {}
virtual void Load() = 0;
virtual void Save() = 0;
virtual void Reset() = 0;
protected:
Linter& linter;
};
protected:
enum class Scope { File, Package, Project };
virtual String MakeCmdLine(Scope sc, Vector<String>& paths) = 0;
virtual void OnResults(const String& results) = 0;
String GetFileName() const;
String GetFilePath() const;
String GetPackageName() const;
String GetPackagePath() const;
private:
void SysCmd(const String& cmd, const String& text, Stream& fs);
void DoCheck(Scope sc, Vector<String>& paths);
String module_name;
bool running = false;
};
INITIALIZE(Linter)
void RegisterLinterModule(Linter& linter_module);
int GetLinterModuleCount();
Linter& GetLinterModule(int i);
Linter* GetActiveLinterModulePtr();
// Available static analyzer modules.
#include "CppCheck.h"
#include "ClangTidy.h"
#define KEYGROUPNAME "Linter"
#define KEYNAMESPACE LinterKeys
#define KEYFILE <ide/Linter/Linter.key>
#include <CtrlLib/key_header.h>
#endif

View file

@ -0,0 +1,124 @@
PREMULTIPLIED
IMAGE_ID(warning)
IMAGE_ID(style)
IMAGE_ID(performance)
IMAGE_ID(portability)
IMAGE_ID(unusedfunction)
IMAGE_ID(missinginclude)
IMAGE_ID(information)
IMAGE_ID(error)
IMAGE_BEGIN_DATA
IMAGE_DATA(120,156,229,151,7,84,148,87,218,199,41,9,74,130,88,18,140,70,143,13,93,133,181,36,198,196,253,146,88,130,38,26)
IMAGE_DATA(137,6,197,8,34,136,130,10,40,77,5,69,116,16,41,26,176,81,68,81,17,1,165,27,16,1,5,81,16,134,94,164)
IMAGE_DATA(13,117,128,1,198,97,20,1,5,134,34,200,252,247,222,87,103,86,93,53,100,179,238,249,190,111,159,115,158,195,204,189)
IMAGE_DATA(239,239,62,245,62,239,192,30,33,51,66,230,93,139,172,172,140,60,219,239,115,78,236,137,191,178,233,231,63,202,187,89)
IMAGE_DATA(170,250,180,115,92,241,184,216,9,6,63,170,152,253,17,118,184,146,252,168,218,232,249,29,3,125,237,232,123,84,6,206)
IMAGE_DATA(229,207,90,135,127,40,55,106,176,252,101,39,181,184,142,202,83,16,63,237,132,184,79,136,199,165,39,225,186,117,220,233)
IMAGE_DATA(193,176,234,147,21,63,107,138,255,106,0,3,79,240,228,193,41,244,61,244,39,103,180,160,46,116,214,83,181,9,67,62)
IMAGE_DATA(251,61,62,213,119,22,71,196,11,39,120,43,202,47,45,100,244,105,23,7,237,21,65,8,63,48,137,253,54,86,107,225)
IMAGE_DATA(40,189,214,236,157,128,248,9,68,53,27,81,22,180,144,81,81,149,62,196,253,173,16,36,153,66,243,75,37,189,215,177)
IMAGE_DATA(138,67,100,149,56,193,159,183,244,52,103,99,160,175,9,162,106,3,148,5,46,96,180,163,66,7,79,59,179,32,226,223)
IMAGE_DATA(65,218,145,137,247,20,21,100,148,94,229,207,238,153,26,241,184,228,24,177,211,140,222,166,83,132,215,71,109,204,178,231)
IMAGE_DATA(252,58,116,113,245,49,208,91,7,225,29,22,172,53,149,143,188,200,78,28,163,48,181,33,106,110,191,184,191,19,253,29)
IMAGE_DATA(108,116,215,232,51,124,55,111,11,30,230,233,160,163,252,23,116,85,173,69,175,240,20,250,69,124,228,57,169,60,25,55)
IMAGE_DATA(82,118,170,132,143,119,159,145,219,94,113,129,228,76,136,94,193,81,116,115,245,8,191,225,31,254,151,175,69,87,229,26)
IMAGE_DATA(136,202,181,240,180,187,18,130,148,95,113,98,237,208,56,202,106,204,85,214,188,159,98,68,106,45,34,189,18,143,174,106)
IMAGE_DATA(93,70,59,43,137,221,10,93,194,234,146,252,233,16,126,53,68,21,171,208,93,109,196,196,88,226,189,20,127,155,32,163)
IMAGE_DATA(153,123,78,157,223,197,79,102,120,113,255,35,162,109,207,244,233,163,103,42,93,107,125,174,45,100,189,3,15,239,70,34)
IMAGE_DATA(72,87,182,242,164,249,248,160,250,208,89,224,93,254,43,106,3,213,80,227,63,29,213,231,167,161,202,87,21,229,222,147)
IMAGE_DATA(80,122,108,28,10,93,71,35,223,97,36,114,246,14,67,230,78,69,176,205,21,144,106,42,15,187,69,50,65,191,215,79)
IMAGE_DATA(191,39,169,255,137,97,240,170,200,203,202,40,230,248,45,105,206,62,182,98,32,219,99,21,174,187,78,229,141,248,80,102)
IMAGE_DATA(236,96,249,170,48,45,84,6,111,71,79,67,14,122,249,121,200,245,209,128,179,238,164,168,193,176,247,110,206,129,136,19)
IMAGE_DATA(138,222,134,12,60,225,231,51,218,195,189,141,44,183,31,7,6,195,246,182,178,209,219,24,134,218,40,115,136,10,131,25)
IMAGE_DATA(165,124,138,155,102,207,96,216,167,162,88,210,204,166,232,44,180,66,209,133,245,168,141,220,141,28,207,159,49,104,182,102)
IMAGE_DATA(19,105,228,165,232,107,59,129,166,56,3,84,157,252,230,157,177,41,30,211,255,101,54,209,237,47,232,104,74,254,195,236)
IMAGE_DATA(71,74,239,77,162,108,67,222,85,100,135,155,252,97,187,222,22,19,208,209,144,141,62,97,49,226,89,243,192,62,251,57)
IMAGE_DATA(106,210,45,80,232,183,2,89,206,243,222,202,74,248,39,164,167,90,34,54,227,190,239,98,164,153,168,32,98,211,104,196)
IMAGE_DATA(237,153,249,187,44,149,245,26,163,192,241,51,64,87,97,8,90,10,195,145,122,116,249,160,89,42,186,139,63,194,70,13)
IMAGE_DATA(21,248,153,77,99,56,7,131,25,191,13,150,253,179,114,103,48,195,64,83,83,211,216,199,199,39,197,219,219,59,229,135)
IMAGE_DATA(31,126,48,30,204,193,19,38,76,80,27,58,116,168,178,172,172,172,220,245,235,215,155,5,2,1,238,221,187,7,63,63)
IMAGE_DATA(191,102,186,166,160,160,160,60,102,204,24,181,215,177,7,15,30,60,159,159,159,63,112,227,198,13,225,136,17,35,198,70)
IMAGE_DATA(69,69,9,235,234,234,80,91,91,11,127,127,127,161,178,178,242,216,115,231,206,9,131,130,130,6,140,141,141,207,191,200)
IMAGE_DATA(110,218,180,105,127,77,77,13,90,91,91,193,229,114,65,108,139,110,223,190,45,46,47,47,7,135,195,65,116,116,180,56)
IMAGE_DATA(48,48,80,148,151,151,7,161,80,8,98,3,139,22,45,218,47,225,13,13,13,89,108,54,91,76,207,160,207,87,85,85)
IMAGE_DATA(49,231,72,120,250,189,164,164,4,148,207,206,206,134,173,173,173,120,222,188,121,172,23,125,48,49,49,113,165,207,146,24)
IMAGE_DATA(144,147,147,131,155,55,111,210,184,65,236,34,45,45,13,153,153,153,72,77,77,101,108,47,91,182,204,85,194,5,4,4)
IMAGE_DATA(20,68,70,70,242,99,99,99,219,42,43,43,81,80,80,128,91,183,110,97,235,214,173,153,227,199,143,95,74,114,186,212)
IMAGE_DATA(202,202,42,147,248,199,156,67,247,73,174,218,236,236,236,248,196,102,1,89,27,120,240,224,1,227,43,181,123,247,238,93)
IMAGE_DATA(4,7,7,99,210,164,73,75,37,54,166,77,155,182,244,202,149,43,72,79,79,103,252,162,190,208,188,110,223,190,125,128)
IMAGE_DATA(228,105,160,177,177,145,137,143,174,211,243,99,98,98,48,117,234,84,41,79,63,135,133,133,49,246,169,255,119,238,220,97)
IMAGE_DATA(158,39,121,31,112,118,118,46,56,112,224,0,159,244,74,91,97,97,33,115,6,205,129,189,189,125,38,181,75,89,234,63)
IMAGE_DATA(141,157,198,149,152,152,8,61,61,189,54,210,95,124,82,131,2,137,13,178,230,154,145,145,193,228,136,254,205,202,202,66)
IMAGE_DATA(68,68,4,66,67,67,153,181,228,228,100,196,199,199,51,107,95,124,241,133,171,204,11,162,165,165,197,34,49,139,203,202)
IMAGE_DATA(202,24,150,214,169,184,184,152,201,7,61,135,250,67,215,73,172,204,30,137,91,60,103,206,28,105,253,136,47,251,169,111)
IMAGE_DATA(180,223,66,66,66,168,239,34,23,23,23,49,205,23,141,217,204,204,76,188,109,219,54,17,221,163,113,59,58,58,210,252)
IMAGE_DATA(236,127,209,135,85,171,86,157,183,180,180,28,208,209,209,17,146,59,48,214,198,198,70,72,109,81,251,116,237,253,247,223)
IMAGE_DATA(31,187,114,229,74,225,138,21,43,6,212,213,213,95,234,95,137,144,30,87,147,147,147,83,38,31,229,246,236,217,211,76)
IMAGE_DATA(253,189,118,237,26,72,124,205,116,141,238,41,42,42,190,246,254,188,42,211,167,79,55,214,208,208,72,249,246,219,111,83)
IMAGE_DATA(72,31,189,241,254,254,211,48,160,77,40,105,124,154,68,218,80,185,185,185,76,32,36,161,98,82,96,38,49,180,9,146)
IMAGE_DATA(146,146,232,101,195,213,171,87,97,109,109,29,74,121,90,132,71,143,218,193,107,224,131,87,223,8,30,175,17,220,26,30)
IMAGE_DATA(57,171,132,185,96,124,126,19,106,234,26,80,67,214,232,58,151,91,135,150,150,54,166,105,41,79,19,76,185,149,187,66)
IMAGE_DATA(25,181,118,143,193,173,59,25,98,30,143,135,230,230,102,228,228,22,34,35,51,15,236,244,92,164,178,179,73,115,100,161)
IMAGE_DATA(178,170,134,14,9,20,21,21,61,41,45,45,165,127,153,162,211,203,74,47,78,74,74,10,211,248,180,129,168,175,180,137)
IMAGE_DATA(168,61,111,239,83,240,244,244,66,121,5,23,190,190,190,160,44,227,59,239,153,223,117,188,6,212,82,95,107,235,165,190)
IMAGE_DATA(86,17,125,216,210,202,216,75,78,201,64,210,109,54,202,202,171,201,57,158,76,174,40,183,110,255,85,162,49,208,57,16)
IMAGE_DATA(3,93,214,53,232,58,196,98,253,193,56,232,57,198,51,49,85,84,214,48,246,142,31,63,14,119,119,119,112,202,170,112)
IMAGE_DATA(244,232,81,38,207,212,38,229,76,79,164,99,187,71,38,118,120,101,195,226,84,46,172,78,231,195,218,247,46,195,151,151)
IMAGE_DATA(115,25,123,137,73,169,72,72,188,67,252,174,0,105,88,166,201,105,110,117,89,177,216,225,153,5,11,239,28,88,250,228)
IMAGE_DATA(193,250,76,1,118,158,45,196,238,243,197,12,47,177,119,57,59,0,235,194,181,80,76,120,7,7,7,166,166,180,46,212)
IMAGE_DATA(87,202,89,61,231,118,17,206,198,175,4,182,254,28,134,47,229,60,179,167,17,248,13,230,159,154,143,162,226,114,144,129)
IMAGE_DATA(196,228,152,230,136,198,73,125,245,140,170,132,103,116,213,115,173,198,222,128,114,134,151,216,139,141,75,66,76,108,18,10)
IMAGE_DATA(139,56,216,189,123,55,226,226,226,200,96,170,151,214,254,77,202,41,171,102,236,197,92,187,137,232,152,68,220,45,228,192)
IMAGE_DATA(194,194,2,228,165,129,198,198,123,40,43,171,64,73,105,25,10,238,22,51,125,146,70,250,36,59,167,128,228,151,12,103)
IMAGE_DATA(162,229,229,149,196,158,13,162,8,27,21,125,131,60,87,10,83,83,83,232,235,235,31,39,151,59,159,12,180,252,125,251)
IMAGE_DATA(246,21,121,120,120,136,105,157,201,139,7,78,78,78,98,26,243,225,35,71,224,122,248,8,46,6,4,49,28,85,129,224)
IMAGE_DATA(62,182,108,217,242,210,219,127,245,234,213,190,94,94,94,226,212,180,116,198,63,22,139,37,38,3,68,76,106,46,38,195)
IMAGE_DATA(129,222,23,236,216,177,3,100,136,131,188,208,64,158,175,144,176,31,124,240,129,10,97,73,93,203,152,187,64,123,143,71)
IMAGE_DATA(250,130,12,82,177,185,185,57,29,72,116,40,228,190,229,254,203,147,120,242,105,142,73,28,144,216,91,187,118,237,219,152)
IMAGE_DATA(193,253,50,248,47,151,225,127,134,85,219,232,158,169,121,58,255,177,100,65,203,191,232,177,250,102,247,204,65,156,59,106)
IMAGE_DATA(198,6,247,204,93,215,31,226,107,219,223,234,36,139,139,28,163,235,246,36,181,128,238,209,103,94,7,190,167,60,86,247)
IMAGE_DATA(23,191,138,158,157,241,205,248,218,134,178,114,227,164,155,178,114,227,190,182,143,170,163,123,63,159,41,235,145,87,26,163)
IMAGE_DATA(251,10,174,176,194,43,175,125,95,66,11,86,95,172,235,151,145,31,242,197,63,25,32,107,116,207,246,218,3,124,239,154)
IMAGE_DATA(213,78,153,151,182,201,153,63,249,148,246,108,143,184,143,121,102,225,47,219,39,159,191,178,10,175,163,123,63,158,44,233)
IMAGE_DATA(145,83,252,228,85,251,210,248,167,104,186,100,110,189,36,192,60,227,80,105,252,95,90,134,215,153,134,9,65,247,222,20)
IMAGE_DATA(255,11,50,124,226,247,135,50,23,217,177,165,249,215,112,202,124,252,156,253,51,117,253,127,41,255,7,134,1,200,196,117)
IMAGE_DATA(196,155,191,255,111,229,233,115,18,117,196,155,191,191,73,28,49,56,125,87,254,255,89,254,191,61,254,127,23,255,159,147)
IMAGE_DATA(119,58,12,134,140,24,171,58,249,123,99,214,2,251,56,238,130,67,41,157,11,28,111,119,206,183,137,228,142,91,100,192)
IMAGE_DATA(122,79,121,180,234,219,88,149,153,223,105,47,62,148,208,180,61,160,8,1,41,245,136,202,224,51,122,241,22,15,70,103)
IMAGE_DATA(178,49,219,50,164,105,216,212,191,105,191,142,253,120,230,98,109,13,215,76,209,177,107,85,72,40,104,194,133,155,245,112)
IMAGE_DATA(141,172,128,83,120,57,60,99,171,16,153,214,128,195,81,28,204,54,10,22,13,155,248,229,75,103,40,12,255,100,202,130)
IMAGE_DATA(253,137,2,202,198,230,54,129,69,152,125,193,197,8,75,111,68,24,187,1,123,130,138,176,247,114,49,130,110,215,194,245)
IMAGE_DATA(74,17,102,234,158,22,200,43,142,154,34,225,39,44,217,236,96,234,87,128,184,60,33,236,131,57,228,167,113,17,108,252)
IMAGE_DATA(11,209,213,219,207,232,78,255,60,236,188,144,135,189,129,5,8,190,195,195,6,247,68,168,168,253,228,32,225,231,219,71)
IMAGE_DATA(113,47,222,170,135,119,44,23,182,1,228,39,186,63,249,169,238,151,135,75,41,181,8,34,186,227,108,198,51,245,77,39)
IMAGE_DATA(49,21,227,204,205,74,168,255,120,152,43,225,191,114,76,238,190,154,115,15,246,151,138,136,157,124,152,159,203,34,207,102)
IMAGE_DATA(128,138,152,168,217,105,54,76,79,165,18,77,131,133,47,27,145,25,60,204,214,58,218,45,225,231,216,38,116,71,103,241)
IMAGE_DATA(153,56,169,157,237,103,210,9,147,14,78,125,27,68,61,253,216,230,145,130,173,39,147,177,133,168,153,87,50,34,50,9)
IMAGE_DATA(191,210,77,202,171,155,7,114,253,110,113,225,246,91,25,204,125,179,96,230,195,134,137,119,42,74,121,173,132,239,131,241)
IMAGE_DATA(209,36,24,147,152,141,220,19,96,119,33,3,222,9,37,80,95,98,47,245,127,244,252,117,14,27,61,83,16,150,86,15)
IMAGE_DATA(243,211,25,48,243,76,67,66,94,3,158,14,136,25,255,139,106,154,177,209,53,142,104,44,206,198,115,200,191,99,81,24)
IMAGE_DATA(173,170,33,205,159,252,7,31,77,153,185,241,180,224,80,68,33,130,110,85,99,155,103,50,140,142,221,132,161,219,13,108)
IMAGE_DATA(116,137,133,129,115,12,244,157,175,226,120,68,1,169,107,38,102,254,176,95,32,175,160,44,173,31,21,165,113,115,181,103)
IMAGE_DATA(174,244,21,177,66,243,112,49,169,18,123,253,216,196,223,235,216,116,56,14,187,78,39,227,76,108,49,246,94,202,192,236)
IMAGE_DATA(101,246,162,97,42,51,94,219,131,74,159,204,209,158,189,220,189,73,199,241,26,188,111,148,33,132,205,37,90,13,143,184)
IMAGE_DATA(18,172,177,143,196,236,37,118,77,111,98,165,177,12,25,161,170,50,117,41,139,230,103,214,242,131,157,179,150,177,58,213)
IMAGE_DATA(151,216,112,85,84,23,178,136,207,111,189,63,255,138,188,211,97,48,121,228,48,213,125,139,230,178,202,44,117,184,205,246)
IMAGE_DATA(134,157,247,237,12,58,11,205,214,112,109,190,158,197,154,56,252,195,183,6,163,165,54,89,155,103,163,223,212,230,108,9)
IMAGE_DATA(81,136,23,122,227,2,24,237,12,241,196,3,150,41,56,219,126,110,90,161,250,233,107,147,249,179,218,36,237,86,251,77)
IMAGE_DATA(162,246,51,46,232,191,29,142,222,75,238,232,118,219,193,104,111,144,27,250,110,133,161,197,235,32,170,183,173,20,45,159)
IMAGE_DATA(60,230,165,51,38,142,80,154,82,187,107,189,224,241,25,103,134,237,114,50,102,84,34,157,14,155,209,121,96,19,158,36)
IMAGE_DATA(134,160,249,248,126,100,233,126,39,24,175,52,84,218,76,123,23,204,113,104,57,100,254,15,246,144,17,68,142,70,82,190)
IMAGE_DATA(221,222,16,143,237,12,241,104,143,1,122,227,131,81,107,109,0,211,153,19,165,205,92,104,162,197,165,49,246,6,254,202)
IMAGE_DATA(112,18,123,18,161,92,219,110,125,180,88,111,64,187,183,19,90,124,221,16,183,98,158,244,50,241,173,215,117,247,94,15)
IMAGE_DATA(132,200,197,132,225,36,246,36,66,185,135,150,122,104,54,95,143,7,86,134,104,15,59,143,12,173,175,164,151,153,103,177)
IMAGE_DATA(166,187,39,54,0,29,142,91,165,126,82,123,125,85,165,12,79,185,251,102,186,16,154,232,64,104,110,128,246,224,179,72)
IMAGE_DATA(213,156,43,229,217,250,223,115,31,95,60,1,209,89,23,169,159,212,94,251,5,15,116,70,135,48,92,211,214,117,16,24)
IMAGE_DATA(253,130,135,110,44,8,61,93,17,177,80,93,234,191,197,220,105,14,245,187,55,161,39,246,178,212,79,106,79,34,148,227)
IMAGE_DATA(111,94,139,70,67,109,116,132,95,68,241,102,45,108,156,172,34,205,31,173,69,218,186,5,130,166,95,237,208,117,245,146)
IMAGE_DATA(212,94,171,151,27,169,249,175,12,215,96,176,134,97,107,89,86,184,178,96,154,96,204,144,247,94,26,6,75,199,127,172)
IMAGE_DATA(157,189,246,27,17,223,197,22,162,43,129,104,57,122,16,247,76,244,193,223,182,1,205,71,88,36,103,132,61,96,133,248)
IMAGE_DATA(197,127,17,125,251,241,135,175,237,65,141,79,71,106,39,44,255,188,137,179,101,13,132,30,36,23,1,62,104,189,232,3)
IMAGE_DATA(193,9,103,220,53,248,9,225,255,163,218,244,205,71,175,103,37,242,169,226,251,170,134,170,163,89,97,11,166,115,111,124)
IMAGE_DATA(55,163,243,198,162,25,157,151,230,79,230,234,77,24,201,26,51,68,254,223,62,12,254,14,78,22,173,4,0,0,0,0)
IMAGE_END_DATA(3584, 8)

View file

@ -0,0 +1,4 @@
KEY(CHECKFILE, "Analyze source file" , 0)
KEY(CHECKPACKAGE, "Analyze package" , 0)
KEY(CHECKALL, "Analyze all" , 0)
KEY(CONFIGURE, "Configure linter", 0)

View file

@ -0,0 +1,28 @@
description "TheIDE, static analyzer modules.\377B0,0,0";
uses
ide/Core,
ide/Common;
file
Linter.h,
Linter.cpp,
CppCheck readonly separator,
CppCheck.h,
CppCheck.cpp,
CppCheckConfig.cpp,
CppCheckConfig.lay,
Clang-Tidy readonly separator,
ClangTidy.h,
ClangTidy.cpp,
ClangTidyConfig.cpp,
ClangTidyConfig.lay,
Resources readonly separator,
Linter.key,
Linter.iml,
Docs readonly separator,
src.tpp,
Info readonly separator,
Copying,
Todo.txt;

View file

@ -0,0 +1,33 @@
DONE
+ Linter package: Check file, package, project (all) commands.
+ Linter package: TheIDE linter keyboard shortcuts for check file, package, and project commands.
+ Linter package: Redesigned as a framework to utilize multiple command-line-driven static analysis tools.
+ Linter package: Added initial API docs for implmenting linter modules.
+ Linter package: Linter package is now a compile-time option, using the LINTER flag.
+ Linter package: Footprint in TheIDE's source code is further narrowed down.
+ Linter package: A module registration mechanism is implemented.
+ Linter package: Added a mechanism to switch between available linter modules on-the-fly.
+ CppCheck module: Ability to select severity message types.
+ CppCheck module: Core configuration settings (language, standard, platform, etc.)
+ CppCheck module: Added Windows cppcheck executable detection.
+ CppCheck module: Added library file(s) support.
+ CppCheck module: Added the ability to pass additional command line options.
+ CppCheck module: Re-implemented as a linter module.
+ CppCheck module: Can now show verbose messages.
+ CppCheck module: Can now show inconclusive results.
+ CppCheck module: Can now filter out non C/C++ files and directories (*.tpp, *.log, etc).
+ CppCheck module: Allows per-project configuration file.
+ ClangTidy module: Check file, package, project (all) command.
WIP:
- Linter package: Add a clang-tidy module.
TODO:
- CppCheck module: Add configurable build dir path (to speed-up analysis).
- CppCheck module: Add CLANG backend switch to CppCheck module.

View file

@ -0,0 +1,168 @@
topic "Linter";
[i448;a25;kKO9;2 $$1,0#37138531426314131252341829483380:class]
[l288;2 $$2,2#27521748481378242620020725143825:desc]
[0 $$3,0#96390100711032703541132217272105:end]
[H6;0 $$4,0#05600065144404261032431302351956:begin]
[i448;a25;kKO9;2 $$5,0#37138531426314131252341829483370:item]
[l288;a4;*@5;1 $$6,6#70004532496200323422659154056402:requirement]
[l288;i1121;b17;O9;~~~.1408;2 $$7,0#10431211400427159095818037425705:param]
[i448;b42;O9;2 $$8,8#61672508125594000341940100500538:tparam]
[b42;2 $$9,9#13035079074754324216151401829390:normal]
[2 $$0,0#00000000000000000000000000000000:Default]
[{_}
[ {{10000@(113.42.0) [s0;%% [*@7;4 Linter]]}}&]
[s3; &]
[s1;:Linter: [@(0.0.255) class ][*3 Linter]&]
[s2;%% The base class of TheIDE`'s static analyzer tools framework.
This class provides a generic interface for integrating command`-line
driven static analysis tools in TheIDE.&]
[s3;%% &]
[s0;%% &]
[ {{10000F(128)G(128)@1 [s0;%% [* Enumerators]]}}&]
[s0;%% &]
[s0; enum_[@(0.0.255) class ][* Scope]&]
[s2;b17;a17;%% Constants used to indicate the scope of the analysis.&]
[s7;i1120;a17;:Ctrl`:`:CENTER:%% [%-*C@3 File]-|Denotes a single file
analysis.&]
[s7;i1120;a17;:Ctrl`:`:LEFT:%% [%-*C@3 Package]-|Denotes a single package
analysis&]
[s7;i1120;a17;:Ctrl`:`:RIGHT:%% [%-*C@3 Project]-|Denotes a complete
(all packages `+ files) analysis.&]
[s0;%% &]
[ {{10000F(128)G(128)@1 [s0;%% [* Constructor detail]]}}&]
[s3; &]
[s5;:Linter`:`:Linter`(const Upp`:`:String`&`): [* Linter]([@(0.0.255) const]
String[@(0.0.255) `&] [*@3 name])&]
[s2;%% Constructor. Deriving class must provide a valid [%-*@3 name]
for the module (e.g. `"CppCheck`").&]
[s3; &]
[s4; &]
[s5;:Linter`:`:`~`(`): [* `~Linter]()&]
[s2;%% Default destructor.&]
[s3; &]
[ {{10000F(128)G(128)@1 [s0;%% [* Public Method List]]}}&]
[s3; &]
[s5;:Linter`:`:GetConfigFilePath`(`)const: [@(0.0.255) virtual] String
[* GetConfigFilePath]() [@(0.0.255) const ]`= 0&]
[s2;%% This method should return the path of the linter module`'s
configuration file. .&]
[s3; &]
[s4; &]
[s5;:Linter`:`:LoadConfig`(`): [@(0.0.255) virtual] Value [* LoadConfig]()
`= 0&]
[s2;%% This method should load the linter module`'s configuration.&]
[s3; &]
[s4; &]
[s5;:Linter`:`:SaveConfig`(const Upp`:`:Value`&`): [@(0.0.255) virtual]
[@(0.0.255) void] [* SaveConfig]([@(0.0.255) const] Value[@(0.0.255) `&]
[*@3 cfg])&]
[s2;%% This method should save the linter module`'s configuration
to file.&]
[s3; &]
[s4; &]
[s5;:Linter`:`:Exists`(`)const: [@(0.0.255) virtual] [@(0.0.255) bool]
[* Exists]() [@(0.0.255) const ]`= 0&]
[s2;%% This method should return true if the linter backend (e.g.
cppcheck, clang`-tidy) is present on the system. .&]
[s3; &]
[s4; &]
[s5;:Linter`:`:Settings`(`): [@(0.0.255) virtual] [@(0.0.255) void] [* Settings]()
`= 0&]
[s2;%% This method should open up the linter`'s configuration window.&]
[s3; &]
[s4; &]
[s5;:Linter`:`:GetName`(`)const: String [* GetName]() [@(0.0.255) const]&]
[s2;%% Returns the name of the linter module.&]
[s3; &]
[s4; &]
[s5;:Linter`:`:CanCheck`(`)const: [@(0.0.255) bool] [* CanCheck]() [@(0.0.255) const]&]
[s2;%% Returns true if TheIDE is in editing state and is not in
debug lock mode.&]
[s3; &]
[s4; &]
[s5;:Linter`:`:CheckFile`(`): [@(0.0.255) void] [* CheckFile]()&]
[s2;%% Checks and analyzes the current file.&]
[s3; &]
[s4; &]
[s5;:Linter`:`:CheckPackage`(`): [@(0.0.255) void] [* CheckPackage]()&]
[s2;%% Checks and analyzes the current package.&]
[s3; &]
[s4; &]
[s5;:Linter`:`:CheckProject`(`): [@(0.0.255) void] [* CheckProject]()&]
[s2;%% Checks and analyzes the whole project.&]
[s3; &]
[s4; &]
[s5;:Linter`:`:StdMenu`(Upp`:`:Bar`&`): [@(0.0.255) static] [@(0.0.255) void]
[* StdMenu](Bar[@(0.0.255) `&] [*@3 menu])&]
[s2;%% Provides a standard linter menu for TheIDE.&]
[s3; &]
[s4; &]
[s5;:Linter`:`:FileMenu`(Upp`:`:Bar`&`): [@(0.0.255) static] [@(0.0.255) void]
[* FileMenu](Bar[@(0.0.255) `&] [*@3 menu])&]
[s2;%% Provides a linter menu for individual TheIDE c/c`+`+ files&]
[s3;~~~256;%% &]
[s4; &]
[s5;:Linter`:`:PackageMenu`(Upp`:`:Bar`&`): [@(0.0.255) static] [@(0.0.255) void]
[* PackageMenu](Bar[@(0.0.255) `&] [*@3 menu])&]
[s2;%% Provides a linter menu for individual TheIDE packages.&]
[s3; &]
[ {{10000F(128)G(128)@1 [s0;%% [* Protected Method List]]}}&]
[s3; &]
[s5;:Linter`:`:MakeCmdLine`(Linter`:`:Scope`,Vector`&`): [@(0.0.255) virtual]
String [* MakeCmdLine]([@(0.0.255) enum] Scope [*@3 sc], Vector<String>[@(0.0.255) `&]
[*@3 paths]) `= 0&]
[s2;%% The deriving module must define this function to create and
return a [/ complete ]command line to be executed by TheIDE. [%-*@3 paths]
contain the path of one or more files to be analyzed, depending
on the range of the given operation (file, package, project).
[%-*@3 sc] indicates the scope of the operation.&]
[s3; &]
[s4; &]
[s5;:Linter`:`:OnResults`(const Upp`:`:String`&`): [@(0.0.255) virtual]
[@(0.0.255) void] [* OnResults]([@(0.0.255) const] String[@(0.0.255) `&]
[*@3 results]) `= 0&]
[s2;%% This method will deliver the [%-*@3 results] of the static analysis
to the deriving module. The content of the input data depends
on the underlying static analysis tool.&]
[s3; &]
[s4; &]
[s5;:Linter`:`:GetFileName`(`)const: String [* GetFileName]() [@(0.0.255) const]&]
[s2;%% Convenience method. Returns the name of TheIDE`'s active file.&]
[s3; &]
[s4; &]
[s5;:Linter`:`:GetFilePath`(`)const: String [* GetFilePath]() [@(0.0.255) const]&]
[s2;%% Convenience method. Return the path of TheIDE`'s active file.&]
[s3; &]
[s4; &]
[s5;:Linter`:`:GetPackageName`(`)const: String [* GetPackageName]()
[@(0.0.255) const]&]
[s2;%% Convenience method. Returns the name of TheIDE`'s active package.&]
[s3; &]
[s4; &]
[s5;:Linter`:`:GetPackagePath`(`)const: String [* GetPackagePath]()
[@(0.0.255) const]&]
[s2;%% Convenience method. Returns the path of TheIDE`'s active package.&]
[s3; &]
[ {{10000F(128)G(128)@1 [s0;%% [* Function List]]}}&]
[s3; &]
[s5;:RegisterLinterModule`(Linter`&`): [@(0.0.255) void] [* RegisterLinterModule](Linter[@(0.0.255) `&
] [*@3 linter`_module])&]
[s2;%% Registers a new [%-*@3 linter`_module] to TheIDE`'s static analyzers
list.&]
[s3; &]
[s4; &]
[s5;:GetLinterModuleCount`(`): [@(0.0.255) int] [* GetLinterModuleCount]()&]
[s2;%% Returns the number of registered linter modules.&]
[s3; &]
[s4; &]
[s5;:GetLinterModule`(int`): Linter[@(0.0.255) `&] [* GetLinterModule]([@(0.0.255) int]
[*@3 i])&]
[s2;%% Returns a reference to the linter module at index [%-*@3 i].
&]
[s3; &]
[s4; &]
[s5;:GetActiveLinterModulePtr`(`): Linter [@(0.0.255) `*][* GetActiveLinterModulePtr]()&]
[s2;%% Returns a pointer to the active (selected) linter module.
Returns [%-@(0.0.255) nullptr] if there is no registered linter
module&]
[s3; ]]

View file

@ -21,6 +21,10 @@
#include <ide/Android/Android.h>
#include <plugin/md/Markdown.h>
#ifdef flagLINTER
#include <ide/Linter/Linter.h>
#endif
#include "About.h"
#include "MethodsCtrls.h"

View file

@ -20,7 +20,8 @@ uses
Report,
Core/SSL,
plugin/md,
ide/clang;
ide/clang,
ide/Linter;
file
IDE readonly separator,
@ -136,7 +137,8 @@ mainconfig
"" = "GUI HEAPLOG",
"" = "GUI X11",
"" = "GUI WAYLAND",
"" = "GUI DEBUGCODE";
"" = "GUI DEBUGCODE",
"" = "GUI LINTER";
custom() "",
"ASDFASDFASDF",

View file

@ -859,6 +859,9 @@ void Ide::DebugMenu(Bar& menu)
.Help("Build application & run in valgring");
#endif
menu.Separator();
#ifdef flagLINTER // Experimental static analyzer tools support.
Linter::StdMenu(menu);
#endif
}
}
if(menu.IsMenuBar()) {