NumeRe v1.1.4
NumeRe: Framework für Numerische Rechnungen
codeanalyzer.cpp
Go to the documentation of this file.
1/*****************************************************************************
2 NumeRe: Framework fuer Numerische Rechnungen
3 Copyright (C) 2019 Erik Haenel et al.
4
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with m_editor program. If not, see <http://www.gnu.org/licenses/>.
17******************************************************************************/
18
19#include "codeanalyzer.hpp"
20#include "editor.h"
21#include "searchcontroller.hpp"
22#include "../../common/Options.h"
23#include "../../kernel/core/ui/language.hpp"
24#include "../../kernel/core/procedure/includer.hpp"
25
26#define ANNOTATION_NOTE wxSTC_NSCR_PROCEDURE_COMMANDS+1
27#define ANNOTATION_WARN wxSTC_NSCR_PROCEDURE_COMMANDS+2
28#define ANNOTATION_ERROR wxSTC_NSCR_PROCEDURE_COMMANDS+3
29
30#define HIGHLIGHT_ANNOTATION 12
31
32extern Language _guilang;
33using namespace std;
34
35static void replaceDocStrings(std::string& sStr)
36{
37 sStr.replace(sStr.find_first_of(" ("), std::string::npos, "()");
38}
39
46CodeAnalyzer::CodeAnalyzer(NumeReEditor* parent, Options* opts) : m_editor(parent), m_options(opts), m_nCurPos(0), m_nCurrentLine(0), m_hasProcedureDefinition(false)
47{
48 m_STRING_FUNCS = _guilang.getList("PARSERFUNCS_LISTFUNC_FUNC_*_[STRING]");
49 m_STRING_METHODS = _guilang.getList("PARSERFUNCS_LISTFUNC_METHOD_*_[STRING]");
50 m_MATOP_FUNCS = _guilang.getList("PARSERFUNCS_LISTFUNC_FUNC_*_[MAT]");
51 m_DRAW_FUNCS = _guilang.getList("PARSERFUNCS_LISTFUNC_FUNC_*_[DRAW]");
52
53 std::for_each(m_STRING_FUNCS.begin(), m_STRING_FUNCS.end(), replaceDocStrings);
54 std::for_each(m_STRING_METHODS.begin(), m_STRING_METHODS.end(), replaceDocStrings);
55 std::for_each(m_MATOP_FUNCS.begin(), m_MATOP_FUNCS.end(), replaceDocStrings);
56 std::for_each(m_DRAW_FUNCS.begin(), m_DRAW_FUNCS.end(), replaceDocStrings);
57
58 m_sNote = _guilang.get("GUI_ANALYZER_NOTE");
59 m_sWarn = _guilang.get("GUI_ANALYZER_WARN");
60 m_sError = _guilang.get("GUI_ANALYZER_ERROR");
61}
62
63
74{
75 if (!m_editor || !m_options)
76 return;
77
78 // Clear all annotations
79 m_editor->AnnotationClearAll();
81
82 // Clear the corresponding indicators
83 m_editor->SetIndicatorCurrent(HIGHLIGHT_ANNOTATION);
84 m_editor->IndicatorClearRange(0, m_editor->GetLastPosition());
85 m_editor->IndicatorSetStyle(HIGHLIGHT_ANNOTATION, wxSTC_INDIC_ROUNDBOX);
86 m_editor->IndicatorSetForeground(HIGHLIGHT_ANNOTATION, wxColor(0, 0, 255));
87
88 // Ensure that the correct file type is used and that the setting is active
94 return;
95
96 // Determine the annotation style
97 m_editor->AnnotationSetVisible(wxSTC_ANNOTATION_BOXED);
98
99 m_nCurrentLine = -1;
101
102 bool isContinuedLine = false;
103 bool isAlreadyMeasured = false;
104 bool isSuppressed = false;
105
106 m_currentMode.clear();
107 m_sCurrentLine.clear();
108 m_sStyles.clear();
109 string sFirstLine = "";
110 string sFirstStyles = "";
111 int nFirstLine = -1;
112
113 AnnotationCount AnnotCount;
114
115 // Go through the whole file
116 for (m_nCurPos = 0; m_nCurPos < m_editor->GetLastPosition(); m_nCurPos++)
117 {
118 // Ignore comments
121 continue;
122
123 // It's a new line? Then finalize the contents of the last line
124 // and display the contents as annotation
125 if (m_nCurrentLine < m_editor->LineFromPosition(m_nCurPos))
126 {
127 // Get the line's contents
128 string sLine = m_editor->GetLine(m_nCurrentLine).ToStdString();
129 StripSpaces(sLine);
130
131 // catch constant expressions
133 && sLine.length()
134 && sLine.find_first_not_of("\n\r\t") != string::npos
135 && sLine.find_first_not_of("0123456789eE+-*/.,;^(){} \t\r\n") == string::npos)
136 AnnotCount += addToAnnotation(_guilang.get("GUI_ANALYZER_TEMPLATE", sLine.substr(0, sLine.find_last_not_of("0123456789eE+-*/.,;^()")), m_sWarn, _guilang.get("GUI_ANALYZER_CONSTEXPR")), ANNOTATION_WARN);
137
138 // Handle line continuations
139 if (sLine.find("\\\\") != string::npos)
140 isContinuedLine = true;
141 else
142 {
143 isContinuedLine = false;
145 m_currentMode.clear();
146 }
147
148 // Find the summary line for the current file and push the
149 // current lines contents, if m_editor is the first line,
150 // otherwise simply add the current lines contents to the
151 // current line
152 if (nFirstLine < 0 && m_nCurrentLine >= 0)
153 {
154 sFirstLine = m_sCurrentLine;
155 sFirstStyles = m_sStyles;
156 nFirstLine = m_nCurrentLine;
157 }
158 else if (m_sCurrentLine.length())
159 {
160 // Write the annotion to the current line
161 m_editor->AnnotationSetText(m_nCurrentLine, m_sCurrentLine);
162 m_editor->AnnotationSetStyles(m_nCurrentLine, m_sStyles);
163 }
164
165 m_nCurrentLine = m_editor->LineFromPosition(m_nCurPos);
166
167 // Get the new line for finding the trailing semicolon
168 sLine = m_editor->GetLine(m_nCurrentLine).ToStdString();
169
170 // Ensure that there's no trailing comment
171 if (sLine.rfind("##") != string::npos)
172 sLine.erase(sLine.rfind("##"));
173
174 // Remove also block comments
175 size_t nBlockStart = 0;
176
177 while ((nBlockStart = sLine.find("#*")) != string::npos)
178 {
179 if (sLine.find("*#", nBlockStart+2) == string::npos)
180 {
181 sLine.erase(nBlockStart);
182 break;
183 }
184 else
185 {
186 sLine.erase(nBlockStart, sLine.find("*#", nBlockStart+2)+2 - nBlockStart);
187 }
188 }
189
190 // Find the last visible character
191 size_t lastVisibleChar = sLine.find_last_not_of(" \t\r\n");
192
193 // Determine, whether it is a semicolon or a line continuation
194 if (lastVisibleChar != string::npos
195 && (sLine[lastVisibleChar] == ';' || (lastVisibleChar > 0 && sLine.substr(lastVisibleChar-1, 2) == "\\\\")))
196 isSuppressed = true;
197 else
198 isSuppressed = false;
199
200 m_sCurrentLine.clear();
201 m_sStyles.clear();
202 }
203
204 // Get code metrics for scripts if not already done
205 if (m_editor->m_fileType == FILE_NSCR && !isAlreadyMeasured)
206 {
207 string sLine = m_editor->GetLine(m_nCurrentLine).ToStdString();
208 StripSpaces(sLine);
209 if (sLine.length() && sLine.find_first_not_of(" \n\r\t") != string::npos)
210 {
211 string sSyntaxElement = m_editor->GetFilenameString().ToStdString();
212 isAlreadyMeasured = true;
213
214 // Calculate the code metrics:
215 // Complexity
216 int nCyclomaticComplexity = calculateCyclomaticComplexity(m_nCurrentLine, m_editor->LineFromPosition(m_editor->GetLastPosition()));
217
218 // LinesOfcode
219 int nLinesOfCode = calculateLinesOfCode(m_nCurrentLine, m_editor->LineFromPosition(m_editor->GetLastPosition()));
220
221 // Number of comments
222 int nNumberOfComments = countNumberOfComments(m_nCurrentLine, m_editor->LineFromPosition(m_editor->GetLastPosition()));
223
224 // comment density
225 double dCommentDensity = (double)nNumberOfComments / (double)nLinesOfCode;
226
227 // Compare the metrics with the contants and issue a note or a warning
228 if (m_options->GetAnalyzerOption(Options::COMPLEXITY) && nCyclomaticComplexity > MAXCOMPLEXITYWARN)
229 AnnotCount += addToAnnotation(_guilang.get("GUI_ANALYZER_TEMPLATE", sSyntaxElement, m_sWarn, _guilang.get("GUI_ANALYZER_HIGHCOMPLEXITY", toString(nCyclomaticComplexity))), ANNOTATION_WARN);
231 AnnotCount += addToAnnotation(_guilang.get("GUI_ANALYZER_TEMPLATE", sSyntaxElement, m_sNote, _guilang.get("GUI_ANALYZER_HIGHCOMPLEXITY", toString(nCyclomaticComplexity))), ANNOTATION_NOTE);
233 AnnotCount += addToAnnotation(_guilang.get("GUI_ANALYZER_TEMPLATE", sSyntaxElement, m_sNote, _guilang.get("GUI_ANALYZER_MANYLINES", toString(nLinesOfCode))), ANNOTATION_NOTE);
234
236 AnnotCount += addToAnnotation(_guilang.get("GUI_ANALYZER_TEMPLATE", sSyntaxElement, m_sNote, _guilang.get("GUI_ANALYZER_LOWCOMMENTDENSITY", toString(dCommentDensity * 100.0, 3))), ANNOTATION_NOTE);
237
239 AnnotCount += addToAnnotation(_guilang.get("GUI_ANALYZER_TEMPLATE", sSyntaxElement, m_sNote, _guilang.get("GUI_ANALYZER_HIGHCOMMENTDENSITY", toString(dCommentDensity * 100.0, 3))), ANNOTATION_NOTE);
240 }
241 }
242
243 // Handle the different style types
245 {
246 if (!isSuppressed)
247 {
248 string sWord = m_editor->GetTextRange(m_editor->WordStartPosition(m_nCurPos, true), m_editor->WordEndPosition(m_nCurPos, true)).ToStdString();
249
250 // the commands "matop" or "mtrxop" are needing semicolons
251 // and are the first element in a command line
252 if (sWord != "matop" && sWord != "mtrxop")
253 isSuppressed = true;
254 }
255 // Handle commands
256 AnnotCount += analyseCommands();
257 }
260 && m_editor->GetStyleAt(m_nCurPos) == wxSTC_NSCR_METHOD))
261 {
263 {
264 string sWord = m_editor->GetTextRange(m_editor->WordStartPosition(m_nCurPos, true), m_editor->WordEndPosition(m_nCurPos, true)).ToStdString();
265 AnnotCount += addToAnnotation(_guilang.get("GUI_ANALYZER_TEMPLATE", highlightFoundOccurence(sWord + "()", m_editor->WordStartPosition(m_nCurPos, true), sWord.length()), m_sNote, _guilang.get("GUI_ANALYZER_SUPPRESS_OUTPUT")), ANNOTATION_NOTE);
266 isSuppressed = true;
267 }
268 // Handle standard functions
269 AnnotCount += analyseFunctions(isContinuedLine);
270 }
272 {
273 // Handle NumeRe procedure calls (NumeRe only)
274 AnnotCount += analyseProcedures();
275 }
278 && m_editor->GetCharAt(m_nCurPos) != ' '
279 && m_editor->GetCharAt(m_nCurPos) != '\t'
280 && m_editor->GetCharAt(m_nCurPos) != '\r'
281 && m_editor->GetCharAt(m_nCurPos) != '\n')
282 {
284 {
285 string sWord = m_editor->GetTextRange(m_editor->WordStartPosition(m_nCurPos, true), m_editor->WordEndPosition(m_nCurPos, true)).ToStdString();
286 AnnotCount += addToAnnotation(_guilang.get("GUI_ANALYZER_TEMPLATE", highlightFoundOccurence(sWord, m_editor->WordStartPosition(m_nCurPos, true), sWord.length()), m_sNote, _guilang.get("GUI_ANALYZER_SUPPRESS_OUTPUT")), ANNOTATION_NOTE);
287 isSuppressed = true;
288 }
289 // Handle identifiers (like variable names)
290 AnnotCount += analyseIdentifiers();
291 }
293 {
294 if (m_options->GetAnalyzerOption(Options::RESULT_SUPPRESSION) && m_editor->GetCharAt(m_nCurPos) == '=' && !isSuppressed)
295 {
296 AnnotCount += addToAnnotation(_guilang.get("GUI_ANALYZER_TEMPLATE", highlightFoundOccurence("=", m_nCurPos, 1), m_sNote, _guilang.get("GUI_ANALYZER_SUPPRESS_OUTPUT")), ANNOTATION_NOTE);
297 isSuppressed = true;
298 }
299 // Handle special operators
300 AnnotCount += analyseOperators();
301 }
303 {
304 // Handle numbers
305 AnnotCount += analyseNumbers();
306 }
308 && m_editor->GetStyleAt(m_nCurPos) == wxSTC_NSCR_PREDEFS)
309 {
310 AnnotCount += analysePreDefs();
311 }
312 }
313
314 // Clear the annotation and style cache
315 m_sCurrentLine.clear();
316 m_sStyles.clear();
317
318 // Write the summary lines
319 if (AnnotCount.nNotes)
320 addToAnnotation(_guilang.get("GUI_ANALYZER_NOTE_TOTAL", toString(AnnotCount.nNotes)), ANNOTATION_NOTE);
321
322 if (AnnotCount.nWarnings)
323 addToAnnotation(_guilang.get("GUI_ANALYZER_WARN_TOTAL", toString(AnnotCount.nWarnings)), ANNOTATION_WARN);
324
325 if (AnnotCount.nErrors)
326 addToAnnotation(_guilang.get("GUI_ANALYZER_ERROR_TOTAL", toString(AnnotCount.nErrors)), ANNOTATION_ERROR);
327
328 // Append the summary line to the first line (if it is not empty)
329 if (m_sCurrentLine.length() && sFirstLine.length())
330 {
331 m_sCurrentLine += "\n" + sFirstLine;
332 m_sStyles += m_sStyles.back() + sFirstStyles;
333 }
334 else if (sFirstLine.length())
335 {
336 m_sCurrentLine = sFirstLine;
337 m_sStyles = sFirstStyles;
338 }
339
340 // Write the first line if it is not empty
341 if (m_sCurrentLine.length())
342 {
343 m_editor->AnnotationSetText(nFirstLine, m_sCurrentLine);
344 m_editor->AnnotationSetStyles(nFirstLine, m_sStyles);
345 }
346}
347
348
357{
358 AnnotationCount AnnotCount;
359
360 bool canContinue = false;
361 int wordstart = m_editor->WordStartPosition(m_nCurPos, true);
362 int wordend = m_editor->WordEndPosition(m_nCurPos, true);
363
364 // Get the current syntax element
365 string sSyntaxElement = m_editor->GetTextRange(wordstart, wordend).ToStdString();
366
367 // add a message to "throw"
368 if (sSyntaxElement == "throw")
369 {
370 for (int j = wordend; j < m_editor->GetLineEndPosition(m_nCurrentLine); j++)
371 {
372 if (m_editor->GetStyleAt(j) == wxSTC_NSCR_STRING
373 || m_editor->GetStyleAt(j) == wxSTC_NSCR_STRING_PARSER
374 || m_editor->GetStyleAt(j) == wxSTC_NSCR_CLUSTER
375 || m_editor->GetStyleAt(j) == wxSTC_NSCR_IDENTIFIER)
376 {
377 canContinue = true;
378 break;
379 }
380 }
381
382 // Was a message found?
383 if (!canContinue)
384 {
385 AnnotCount += addToAnnotation(_guilang.get("GUI_ANALYZER_TEMPLATE", highlightFoundOccurence(sSyntaxElement, wordstart, 5), m_sNote, _guilang.get("GUI_ANALYZER_THROW_ADDMESSAGE")), ANNOTATION_NOTE);
386 }
387 }
388
389 // check the namespace command
390 if (sSyntaxElement == "namespace")
391 {
392 string sArgs = m_editor->GetTextRange(wordend, m_editor->GetLineEndPosition(m_nCurrentLine)).ToStdString();
393 while (sArgs.back() == '\r' || sArgs.back() == '\n')
394 sArgs.pop_back();
395 StripSpaces(sArgs);
396
397 // Is there an explicit namespace name? If no, warn the user
398 if (!sArgs.length())
399 {
400 AnnotCount += addToAnnotation(_guilang.get("GUI_ANALYZER_TEMPLATE", highlightFoundOccurence(sSyntaxElement, wordstart, sSyntaxElement.length()), m_sWarn, _guilang.get("GUI_ANALYZER_NAMESPACE_ALWAYSMAIN")), ANNOTATION_WARN);
401 }
402 m_nCurPos = wordend;
403
404 // Advance the character pointer and return the number of gathered annotations
405 while (m_editor->GetCharAt(m_nCurPos) != ';' && m_editor->GetCharAt(m_nCurPos) != '\r' && m_editor->GetCharAt(m_nCurPos) != '\n')
406 m_nCurPos++;
407 return AnnotCount;
408 }
409
410 // The progress command needs extra runtime (2-4 times). Inform the user about m_editor issue
411 if (m_options->GetAnalyzerOption(Options::PROGRESS_RUNTIME) && sSyntaxElement == "progress")
412 {
413 AnnotCount += addToAnnotation(_guilang.get("GUI_ANALYZER_TEMPLATE", highlightFoundOccurence(sSyntaxElement, wordstart, sSyntaxElement.length()), m_sNote, _guilang.get("GUI_ANALYZER_PROGRESS_RUNTIME")), ANNOTATION_NOTE);
414 }
415
416 // The install or the start commands are not allowed in scripts and procedures
417 if (sSyntaxElement == "install" || sSyntaxElement == "uninstall" || sSyntaxElement == "start")
418 {
419 AnnotCount += addToAnnotation(_guilang.get("GUI_ANALYZER_TEMPLATE", highlightFoundOccurence(sSyntaxElement, wordstart, sSyntaxElement.length()), m_sError, _guilang.get("GUI_ANALYZER_NOTALLOWED")), ANNOTATION_ERROR);
420 }
421
422 // Store mode-specific commands
423 if (sSyntaxElement == "matop" || sSyntaxElement == "mtrxop")
424 m_currentMode = "matop";
425 else if (sSyntaxElement == "draw" || sSyntaxElement == "draw3d")
426 m_currentMode = "draw";
427
428 // Handle the memory clearance commands
429 if (sSyntaxElement == "clear" || sSyntaxElement == "delete" || sSyntaxElement == "remove")
430 {
431 // some caches may not be removec
432 if (sSyntaxElement == "remove" && m_editor->GetStyleAt(m_editor->WordStartPosition(wordend + 1, true)) == wxSTC_NSCR_PREDEFS)
433 AnnotCount += addToAnnotation(_guilang.get("GUI_ANALYZER_TEMPLATE", highlightFoundOccurence(sSyntaxElement, wordstart, sSyntaxElement.length()), m_sError, _guilang.get("GUI_ANALYZER_CANNOTREMOVEPREDEFS")), ANNOTATION_ERROR);
434
435 // Get everything after the clearance command
436 string sArgs = m_editor->GetTextRange(wordend, m_editor->GetLineEndPosition(m_nCurrentLine)).ToStdString();
437 while (sArgs.back() == '\r' || sArgs.back() == '\n' || sArgs.back() == ';')
438 sArgs.pop_back();
439
440 // Inform the user that he should append "ignore" as parameter
441 if (!findParameter(sArgs, "ignore")
442 && !findParameter(sArgs, "i")
443 && (sSyntaxElement != "remove" || m_editor->GetStyleAt(m_editor->WordStartPosition(wordend + 1, true)) != wxSTC_NSCR_CUSTOM_FUNCTION))
444 AnnotCount += addToAnnotation(_guilang.get("GUI_ANALYZER_TEMPLATE", highlightFoundOccurence(sSyntaxElement, wordstart, sSyntaxElement.length()), m_sNote, _guilang.get("GUI_ANALYZER_APPENDIGNORE")), ANNOTATION_NOTE);
445 }
446
447 // check, whether the current command do have an expression
448 // Ignore some special commands, which do not need an expression
449 if (sSyntaxElement != "hline"
450 && sSyntaxElement != "continue"
451 && sSyntaxElement != "break"
452 && sSyntaxElement != "else"
453 && m_editor->isBlockEnd(sSyntaxElement) == wxNOT_FOUND
454 && sSyntaxElement != "about"
455 && sSyntaxElement != "abort"
456 && sSyntaxElement != "compose"
457 && sSyntaxElement != "layout"
458 && sSyntaxElement != "group"
459 && sSyntaxElement != "help"
460 && sSyntaxElement != "quit"
461 && sSyntaxElement != "return"
462 && sSyntaxElement != "subplot"
463 && sSyntaxElement != "try"
464 && sSyntaxElement != "catch"
465 && sSyntaxElement != "otherwise"
466 && sSyntaxElement != "throw"
467 && sSyntaxElement != "rethrow"
468 && sSyntaxElement != "namespace" //warning
469 )
470 {
471 canContinue = false;
472
473 // Get everything in the current line after the command
474 string sArgs = m_editor->GetTextRange(wordend, m_editor->GetLineEndPosition(m_nCurrentLine)).ToStdString();
475 while (sArgs.back() == '\r' || sArgs.back() == '\n')
476 sArgs.pop_back();
477 StripSpaces(sArgs);
478
479 // If the line after the command is empty
480 if (!sArgs.length())
481 {
482 // is used as a parameter (legacy)
483 for (int j = wordstart; j >= m_editor->PositionFromLine(m_nCurrentLine); j--)
484 {
485 if (m_editor->GetCharAt(j) == '-')
486 canContinue = true;
487 }
488
489 // No expression found and not used as a parameter?
490 if (!canContinue)
491 AnnotCount += addToAnnotation(_guilang.get("GUI_ANALYZER_TEMPLATE", highlightFoundOccurence(sSyntaxElement, wordstart, sSyntaxElement.length()), m_sError, _guilang.get("GUI_ANALYZER_EMPTYEXPRESSION")), ANNOTATION_ERROR);
492 }
493 }
494
495 // There are some command, which will return values.
496 // Check, whether there results are stored into a variable
497 if (sSyntaxElement == "zeroes"
498 || sSyntaxElement == "extrema"
499 || sSyntaxElement == "integrate"
500 || sSyntaxElement == "load"
501 || sSyntaxElement == "reload"
502 || sSyntaxElement == "append"
503 || sSyntaxElement == "imread"
504 || sSyntaxElement == "eval"
505 || sSyntaxElement == "taylor"
506 || sSyntaxElement == "get"
507 || sSyntaxElement == "read"
508 || sSyntaxElement == "pulse"
509 || sSyntaxElement == "diff")
510 {
511 canContinue = false;
512
513 // Try to find an assignment operator
514 for (int j = m_editor->PositionFromLine(m_nCurrentLine); j < wordstart; j++)
515 {
516 if (m_editor->GetCharAt(j) == '=')
517 {
518 canContinue = true;
519 break;
520 }
521 }
522
523 // Was an assignment operator found?
525 AnnotCount += addToAnnotation(_guilang.get("GUI_ANALYZER_TEMPLATE", highlightFoundOccurence(sSyntaxElement, wordstart, sSyntaxElement.length()), m_sWarn, _guilang.get("GUI_ANALYZER_ASSIGNTOVARIABLE", sSyntaxElement)), ANNOTATION_WARN);
526 }
527
528 // Examine the if, elseif and while commands
529 if (sSyntaxElement == "if" || sSyntaxElement == "elseif" || sSyntaxElement == "while" || sSyntaxElement == "switch")
530 {
531 for (int j = wordend; j < m_editor->GetLineEndPosition(m_nCurrentLine); j++)
532 {
533 // Only examine elements in the first parenthesis for NumeRe syntax.
534 // Ignore the parenthesis in the MATLAB case
535 if (m_editor->GetCharAt(j) == '(' || m_editor->m_fileType == FILE_MATLAB)
536 {
537 int nPos;
538
539 // If the first character is a parenthesis
540 if (m_editor->GetCharAt(j) == '(')
541 {
542 nPos = m_editor->BraceMatch(j);
543 if (nPos < 0)
544 {
545 AnnotCount += addToAnnotation(_guilang.get("GUI_ANALYZER_TEMPLATE", highlightFoundOccurence(sSyntaxElement, j, 1), m_sError, _guilang.get("GUI_ANALYZER_MISSINGPARENTHESIS")), ANNOTATION_ERROR);
546 break;
547 }
548 }
549 else
550 nPos = m_editor->GetLineEndPosition(m_nCurrentLine);
551
552 // Get the argument
553 string sArgument = m_editor->GetTextRange(j + 1, nPos).ToStdString();
554 StripSpaces(sArgument);
555
556 // Is the argument available?
557 if (!sArgument.length())
558 {
559 AnnotCount += addToAnnotation(_guilang.get("GUI_ANALYZER_TEMPLATE", highlightFoundOccurence(sSyntaxElement, j, 2), m_sError, _guilang.get("GUI_ANALYZER_MISSINGARGUMENT")), ANNOTATION_ERROR);
560 break;
561 }
562
563 // Is it a constant?
564 if (sArgument == "true" || (sArgument.find_first_not_of("1234567890.") == string::npos && sArgument != "0"))
565 {
566 if (sSyntaxElement == "while")
567 AnnotCount += addToAnnotation(_guilang.get("GUI_ANALYZER_TEMPLATE", highlightFoundOccurence(sSyntaxElement, j+1, sArgument.length()), m_sWarn, _guilang.get("GUI_ANALYZER_WHILE_ALWAYSTRUE")), ANNOTATION_WARN);
568 else if (sSyntaxElement == "switch")
569 AnnotCount += addToAnnotation(_guilang.get("GUI_ANALYZER_TEMPLATE", highlightFoundOccurence(sSyntaxElement, j+1, sArgument.length()), m_sWarn, _guilang.get("GUI_ANALYZER_SWITCH_CONSTANT")), ANNOTATION_WARN);
570 else
571 AnnotCount += addToAnnotation(_guilang.get("GUI_ANALYZER_TEMPLATE", highlightFoundOccurence(sSyntaxElement, j+1, sArgument.length()), m_sWarn, _guilang.get("GUI_ANALYZER_IF_ALWAYSTRUE")), ANNOTATION_WARN);
572 }
573 else if (sArgument == "false" || sArgument == "0")
574 {
575 if (sSyntaxElement == "while")
576 AnnotCount += addToAnnotation(_guilang.get("GUI_ANALYZER_TEMPLATE", highlightFoundOccurence(sSyntaxElement, j+1, sArgument.length()), m_sWarn, _guilang.get("GUI_ANALYZER_WHILE_ALWAYSFALSE")), ANNOTATION_WARN);
577 else if (sSyntaxElement == "switch")
578 AnnotCount += addToAnnotation(_guilang.get("GUI_ANALYZER_TEMPLATE", highlightFoundOccurence(sSyntaxElement, j+1, sArgument.length()), m_sWarn, _guilang.get("GUI_ANALYZER_SWITCH_CONSTANT")), ANNOTATION_WARN);
579 else
580 AnnotCount += addToAnnotation(_guilang.get("GUI_ANALYZER_TEMPLATE", highlightFoundOccurence(sSyntaxElement, j+1, sArgument.length()), m_sWarn, _guilang.get("GUI_ANALYZER_IF_ALWAYSFALSE")), ANNOTATION_WARN);
581 }
582 else if (containsAssignment(sArgument))
583 {
584 // Does it contain an assignment? Warn the user as m_editor is probably not intendet
585 AnnotCount += addToAnnotation(_guilang.get("GUI_ANALYZER_TEMPLATE", highlightFoundOccurence(sSyntaxElement, j+1, sArgument.length()), m_sWarn, _guilang.get("GUI_ANALYZER_ASSIGNMENTINARGUMENT")), ANNOTATION_WARN);
586 }
587 break;
588 }
589 }
590
591 // There's an faster, inline if-else operator in NumeRe
592 // Propose that, if the current if-else block is quite short
593 if (sSyntaxElement == "if" && m_editor->m_fileType != FILE_MATLAB)
594 {
595 vector<int> vBlock = m_editor->BlockMatch(m_nCurPos);
596
597 // Was the end of the current block found?
598 if (vBlock.back() != wxSTC_INVALID_POSITION)
599 {
600 // Check the length of the current block
601 if (m_editor->LineFromPosition(vBlock.back()) - m_nCurrentLine < 5)
602 {
603 canContinue = false;
604
605 // Ensure that no commands except of further ifs and elses are used inside of the found block
606 for (int pos = wordend; pos <= vBlock.back(); pos++)
607 {
608 if (m_editor->GetStyleAt(pos) == wxSTC_NSCR_COMMAND
609 && m_editor->GetTextRange(m_editor->WordStartPosition(pos, true), m_editor->WordEndPosition(pos, true)) != "if"
610 && m_editor->GetTextRange(m_editor->WordStartPosition(pos, true), m_editor->WordEndPosition(pos, true)) != "else"
611 && m_editor->GetTextRange(m_editor->WordStartPosition(pos, true), m_editor->WordEndPosition(pos, true)) != "endif")
612 {
613 // Other command found
614 canContinue = true;
615 break;
616 }
617 else
618 {
619 // jump over other elements
620 pos = m_editor->WordEndPosition(pos, true);
621 }
622 }
623
624 // Was an other command found?
625 if (m_options->GetAnalyzerOption(Options::INLINE_IF) && !canContinue)
626 {
627 AnnotCount += addToAnnotation(_guilang.get("GUI_ANALYZER_TEMPLATE", highlightFoundOccurence(sSyntaxElement, wordstart, sSyntaxElement.length()), m_sNote, _guilang.get("GUI_ANALYZER_USEINLINEIF")), ANNOTATION_NOTE);
628 }
629 }
630 }
631 }
632
633 // Ensure that a switch does contain at least one case
634 if (sSyntaxElement == "switch")
635 {
636 vector<int> vBlock = m_editor->BlockMatch(m_nCurPos);
637
638 // Examine each match
639 for (size_t i = 1; i < vBlock.size(); i++)
640 {
641 // Invalid position -> missing end. Already handled elsewhere
642 if (vBlock[i] == wxSTC_INVALID_POSITION)
643 break;
644
645 // Examine the command
646 if (m_editor->GetTextRange(vBlock[i], m_editor->WordEndPosition(vBlock[i], true)) == "case")
647 {
648 // Contains a case: everything is alright
649 break;
650 }
651 else if (m_editor->GetTextRange(vBlock[i], m_editor->WordEndPosition(vBlock[i], true)) == "default")
652 {
653 // Only a default statement?
654 AnnotCount += addToAnnotation(_guilang.get("GUI_ANALYZER_TEMPLATE", highlightFoundOccurence(sSyntaxElement, wordstart, sSyntaxElement.length()), m_sWarn, _guilang.get("GUI_ANALYZER_SWITCH_ONLY_DEFAULT")), ANNOTATION_WARN);
655 break;
656 }
657 else if (m_editor->GetTextRange(vBlock[i], m_editor->WordEndPosition(vBlock[i], true)) == "endswitch")
658 {
659 // Neither a case nor a default statement?
660 AnnotCount += addToAnnotation(_guilang.get("GUI_ANALYZER_TEMPLATE", highlightFoundOccurence(sSyntaxElement, wordstart, sSyntaxElement.length()), m_sError, _guilang.get("GUI_ANALYZER_SWITCH_MISSING_CASE")), ANNOTATION_ERROR);
661 break;
662 }
663 }
664 }
665 }
666
667 // Check for fallthroughs and warn the user,
668 // if one was found. Also check the appended value.
669 if (sSyntaxElement == "case" && m_editor->m_fileType != FILE_MATLAB)
670 {
671 vector<int> vBlock = m_editor->BlockMatch(m_nCurPos);
672
673 // Search the correct position in the
674 // whole block
675 for (size_t i = 0; i < vBlock.size(); i++)
676 {
677 // Is this the correct position?
678 if (vBlock[i] == wordstart)
679 {
680 if (vBlock[i+1] != wxSTC_INVALID_POSITION
681 && m_editor->GetTextRange(vBlock[i+1], m_editor->WordEndPosition(vBlock[i+1], true)) != "endswitch")
682 {
683 // Search all occurences of the "break"
684 // command between the two statements
685 vector<int> vMatches = m_editor->m_search->FindAll("break", wxSTC_NSCR_COMMAND, wordend, vBlock[i+1], false);
686
687 // Search also for "return"s as an alternative
688 if (!vMatches.size())
689 vMatches = m_editor->m_search->FindAll("return", wxSTC_NSCR_COMMAND, wordend, vBlock[i+1], false);
690
691 // Ensure that there's one "break" or "return"
692 // statement
694 {
695 AnnotCount += addToAnnotation(_guilang.get("GUI_ANALYZER_TEMPLATE", highlightFoundOccurence(sSyntaxElement, wordstart, sSyntaxElement.length()), m_sWarn, _guilang.get("GUI_ANALYZER_SWITCH_MISSING_BREAK")), ANNOTATION_WARN);
696 }
697 }
698
699 break;
700 }
701 }
702
703 // Check the value of the case
704 wxString sLine = m_editor->GetTextRange(wordend, m_editor->GetLineEndPosition(m_nCurrentLine));
705
706 // Does the value contain something, which is not a whitespace
707 // and not a comment?
708 if (sLine.find_first_not_of(":\r\n#* ") == string::npos || sLine.find(':') == string::npos)
709 {
710 AnnotCount += addToAnnotation(_guilang.get("GUI_ANALYZER_TEMPLATE", highlightFoundOccurence(sSyntaxElement, wordstart, sSyntaxElement.length()), m_sError, _guilang.get("GUI_ANALYZER_SWITCH_MISSING_VALUE")), ANNOTATION_ERROR);
711 }
712
713 }
714
715 // Examine the for command
716 // Only used for NumeRe syntax
717 if (sSyntaxElement == "for" && m_editor->m_fileType != FILE_MATLAB)
718 {
719 // Go through the current line
720 for (int j = wordend; j < m_editor->GetLineEndPosition(m_nCurrentLine); j++)
721 {
722 // If the current character is an opening parenthesis
723 if (m_editor->GetCharAt(j) == '(')
724 {
725 // Examine the argument
726 int nPos = m_editor->BraceMatch(j);
727 size_t nOpPos = std::string::npos;
728
729 if (nPos < 0)
730 {
731 // Missing parenthesis
732 AnnotCount += addToAnnotation(_guilang.get("GUI_ANALYZER_TEMPLATE",
733 highlightFoundOccurence(sSyntaxElement, j, 1),
734 m_sError, _guilang.get("GUI_ANALYZER_MISSINGPARENTHESIS")), ANNOTATION_ERROR);
735 break;
736 }
737
738 // Get the argument from the parenthesis
739 string sArgument = m_editor->GetTextRange(j + 1, nPos).ToStdString();
740 StripSpaces(sArgument);
741
742
743 // Argument is empty?
744 if (!sArgument.length())
745 {
746 AnnotCount += addToAnnotation(_guilang.get("GUI_ANALYZER_TEMPLATE",
747 highlightFoundOccurence(sSyntaxElement, j, 2),
748 m_sError, _guilang.get("GUI_ANALYZER_MISSINGARGUMENT")), ANNOTATION_ERROR);
749 break;
750 }
751
752 // Important parts of the argument are missing?
753 if ((nOpPos = sArgument.find("->")) != std::string::npos)
754 {
755 // Range-based for loop
756 if (nOpPos >= sArgument.length()-2
757 || (!isalpha(sArgument.front()) && sArgument.front() != '_' && sArgument.front() != '{'))
758 {
759 AnnotCount += addToAnnotation(_guilang.get("GUI_ANALYZER_TEMPLATE",
760 highlightFoundOccurence(sSyntaxElement, j+1, sArgument.length()),
761 m_sError,
762 _guilang.get("GUI_ANALYZER_FOR_INTERVALERROR")), ANNOTATION_ERROR);
763 }
764 }
765 else
766 {
767 nOpPos = sArgument.find('=');
768
769 // Normal for loop
770 if (nOpPos >= sArgument.length()-1
771 || (!isalpha(sArgument.front()) && sArgument.front() != '_'))
772 {
773 AnnotCount += addToAnnotation(_guilang.get("GUI_ANALYZER_TEMPLATE",
774 highlightFoundOccurence(sSyntaxElement, j+1, sArgument.length()),
775 m_sError,
776 _guilang.get("GUI_ANALYZER_FOR_INTERVALERROR")), ANNOTATION_ERROR);
777 }
778 }
779
780 // Store the for index variable in the list of known
781 // local variables
782 if (m_editor->m_fileType == FILE_NPRC && nOpPos != std::string::npos)
783 {
784 nOpPos += j+1;
785
786 for (int i = j+1; i < (int)nOpPos; i++)
787 {
788 if (m_editor->GetStyleAt(i) == wxSTC_NPRC_IDENTIFIER
789 || m_editor->GetStyleAt(i) == wxSTC_NPRC_CUSTOM_FUNCTION
790 || m_editor->GetStyleAt(i) == wxSTC_NPRC_CLUSTER)
791 {
792 // Store it and break directly
793 m_vLocalVariables.push_back(pair<string,int>(m_editor->GetTextRange(m_editor->WordStartPosition(i, true),
794 m_editor->WordEndPosition(i, true)).ToStdString(),
795 m_editor->GetStyleAt(i)));
796
797 i = m_editor->WordEndPosition(i, true);
798 }
799 }
800 }
801
802 break;
803 }
804 }
805 }
806
807 // Examine the current usage of the local variable declarators
808 // Esp. ensure that the declared variables are used
810 && (sSyntaxElement == "var" || sSyntaxElement == "str" || sSyntaxElement == "tab" || sSyntaxElement == "cst"))
811 {
812 // Handle the special case "list -var"
813 if (sSyntaxElement == "var"
814 && m_editor->GetTextRange(m_editor->PositionFromLine(m_nCurrentLine), m_editor->GetLineEndPosition(m_nCurrentLine)).find("list") < (size_t)(wordstart - m_editor->PositionFromLine(m_nCurrentLine)))
815 {
816 m_nCurPos = wordend;
817 return AnnotCount;
818 }
819
820 // Get the next line
821 int nNextLineStartPosition = m_editor->GetLineEndPosition(m_nCurrentLine) + 1;
822
823 // Find the end of the current procedure
824 int nProcedureEndPosition = m_editor->FindText(nNextLineStartPosition,
825 m_editor->GetLastPosition(),
826 "endprocedure",
827 wxSTC_FIND_MATCHCASE | wxSTC_FIND_WHOLEWORD);
828
829 int nStyle = wxSTC_NSCR_IDENTIFIER;
830
831 if (sSyntaxElement == "tab")
832 nStyle = wxSTC_NSCR_CUSTOM_FUNCTION;
833 else if (sSyntaxElement == "cst")
834 nStyle = wxSTC_NSCR_CLUSTER;
835
836 // extract the arguments and strip the spaces
837 string sArgs = m_editor->GetTextRange(wordend, m_editor->GetLineEndPosition(m_nCurrentLine)).ToStdString();
838
839 while (sArgs.back() == '\r' || sArgs.back() == '\n')
840 sArgs.pop_back();
841
842 StripSpaces(sArgs);
843
844 // Ensure that the end of the procedure is available
845 if (nProcedureEndPosition == -1)
846 {
847 nProcedureEndPosition = m_editor->GetLastPosition();
848 AnnotCount += addToAnnotation(_guilang.get("GUI_ANALYZER_TEMPLATE",
849 highlightFoundOccurence(sSyntaxElement, wordstart, sSyntaxElement.length()),
850 m_sError,
851 _guilang.get("GUI_ANALYZER_MISSINGENDPROCEDURE")), ANNOTATION_ERROR);
852 }
853
854 // If there are variables available
855 if (sArgs.length())
856 {
857 string currentArg;
858
859 // Extract variable by variable
860 while (getNextArgument(sArgs, false).length())
861 {
862 currentArg = getNextArgument(sArgs, true);
863
864 // remove assignments and parentheses and strip the spaces
865 if (currentArg.find('=') != string::npos)
866 currentArg.erase(currentArg.find('='));
867
868 if (currentArg.find_first_of("({;") != string::npos)
869 currentArg.erase(currentArg.find_first_of("({;"));
870
871 StripSpaces(currentArg);
872
873 if (!currentArg.length())
874 continue;
875
876 // Will this variable be overwritten by a declare?
877 if (nStyle == wxSTC_NPRC_IDENTIFIER && m_symdefs.isSymbol(currentArg))
878 AnnotCount += addToAnnotation(_guilang.get("GUI_ANALYZER_TEMPLATE",
879 currentArg,
880 m_sError,
881 _guilang.get("GUI_ANALYZER_DECLAREOVERWRITES", currentArg)), ANNOTATION_ERROR);
882
883 // Does this variable has a fitting type?
884 char cType = getVariableType(currentArg);
885 std::string sChars;
886
887 if (sSyntaxElement == "str")
888 sChars = "s";
889 else if (sSyntaxElement == "var")
890 sChars = "nfdbxyzt";
891
893 && cType != '\0'
894 && sChars.find(cType) == std::string::npos)
895 AnnotCount += addToAnnotation(_guilang.get("GUI_ANALYZER_TEMPLATE",
896 currentArg,
897 m_sWarn,
898 _guilang.get("GUI_ANALYZER_MISLEADING_TYPE", currentArg)), ANNOTATION_WARN);
899
900 m_vLocalVariables.push_back(pair<string,int>(currentArg, nStyle));
901
902 // Try to find the variable in the remaining code
904 && !m_editor->m_search->FindAll(currentArg, nStyle, nNextLineStartPosition, nProcedureEndPosition, false).size())
905 {
906 // No variable found
907 AnnotCount += addToAnnotation(_guilang.get("GUI_ANALYZER_TEMPLATE",
908 highlightFoundOccurence(currentArg, m_editor->FindText(wordstart, nProcedureEndPosition, currentArg, wxSTC_FIND_MATCHCASE | wxSTC_FIND_WHOLEWORD), currentArg.length()),
909 m_sWarn,
910 _guilang.get("GUI_ANALYZER_UNUSEDVARIABLE", currentArg)), ANNOTATION_WARN);
911 }
912 }
913 }
914 else // No varibles are available
915 AnnotCount += addToAnnotation(_guilang.get("GUI_ANALYZER_TEMPLATE",
916 highlightFoundOccurence(sSyntaxElement, wordstart, sSyntaxElement.length()),
917 m_sError,
918 _guilang.get("GUI_ANALYZER_NOVARIABLES")), ANNOTATION_ERROR);
919 }
920
921 // Examine definitions
922 if (sSyntaxElement == "define"
923 || sSyntaxElement == "ifndefined"
924 || sSyntaxElement == "lclfunc"
925 || sSyntaxElement == "def"
926 || sSyntaxElement == "ifndef")
927 {
928 // According Scintilla docu this shall return the start of the next word
929 int definitionStart = m_editor->WordEndPosition(wordend, false);
930 int definitionEnd = m_editor->WordEndPosition(definitionStart, true);
931 std::string sDefinitionName = m_editor->GetTextRange(definitionStart, definitionEnd).ToStdString();
932
933 if (m_editor->GetStyleAt(definitionStart) == wxSTC_NSCR_FUNCTION)
934 {
935 AnnotCount += addToAnnotation(_guilang.get("GUI_ANALYZER_TEMPLATE",
936 highlightFoundOccurence(sDefinitionName, definitionStart, sDefinitionName.length()),
937 m_sError,
938 _guilang.get("GUI_ANALYZER_STDFUNC_REDEF", sDefinitionName)), ANNOTATION_ERROR);
939 }
940 else if (m_editor->m_fileType == FILE_NPRC
942 {
943 // Use the definition names as local variables
944 m_vLocalVariables.push_back(pair<string,int>(sDefinitionName, wxSTC_NSCR_CUSTOM_FUNCTION));
945 }
946
947 // increment the position variable to the last position
948 // in the current line, so that we may jump over the definition
949 // in the following
950 m_nCurPos = m_editor->GetLineEndPosition(m_editor->LineFromPosition(m_nCurPos));
951 return AnnotCount;
952 }
953
954 // Examine symbol declarations
955 if (sSyntaxElement == SYMDEF_COMMAND)
956 {
957 // extract the declaration and strip the spaces
958 std::string sDefinition = m_editor->GetTextRange(wordend, m_editor->GetLineEndPosition(m_nCurrentLine)).ToStdString();
959
960 while (sDefinition.back() == '\r' || sDefinition.back() == '\n')
961 sDefinition.pop_back();
962
963 m_symdefs.createSymbol(sDefinition);
964
965 // increment the position variable to the last position
966 // in the current line, so that we may jump over the definition
967 // in the following
968 m_nCurPos = m_editor->GetLineEndPosition(m_editor->LineFromPosition(m_nCurPos));
969 return AnnotCount;
970 }
971
972 // Handle includes by resolving them and loading all definitions and declares
973 // if they are imported in this file
974 if (sSyntaxElement == "include")
975 {
976 try
977 {
978 // Creating this instance might fail
979 Includer incl(m_editor->GetTextRange(wordstart, m_editor->GetLineEndPosition(m_nCurrentLine)).ToStdString(),
980 m_editor->GetFileName().GetPath().ToStdString());
981
982 // Go through all included lines in this file
983 while (incl.is_open())
984 {
985 std::string sLine = incl.getNextLine();
986 Match _mMatch = findCommand(sLine);
987
988 // Is it a declare?
989 if (_mMatch.sString == SYMDEF_COMMAND)
990 m_symdefs.createSymbol(sLine.substr(_mMatch.nPos + _mMatch.sString.length()));
991
992 // Is it a define?
993 if (_mMatch.sString == "define"
994 || _mMatch.sString == "ifndefined"
995 || _mMatch.sString == "lclfunc"
996 || _mMatch.sString == "def"
997 || _mMatch.sString == "ifndef")
998 {
999 std::string sDefinition = sLine.substr(_mMatch.nPos + _mMatch.sString.length());
1000 StripSpaces(sDefinition);
1001
1002 // If there is a declaration available
1003 if (sDefinition.length())
1004 {
1005 // remove assignments and parentheses and strip the spaces
1006 if (sDefinition.find('=') != string::npos)
1007 sDefinition.erase(sDefinition.find('='));
1008
1009 if (sDefinition.find_first_of("({") != string::npos)
1010 sDefinition.erase(sDefinition.find_first_of("({"));
1011
1012 StripSpaces(sDefinition);
1013
1014 m_vLocalVariables.push_back(pair<string,int>(sDefinition, wxSTC_NSCR_CUSTOM_FUNCTION));
1015 }
1016 }
1017 }
1018 }
1019 catch (SyntaxError& e)
1020 {
1021 AnnotCount += addToAnnotation(_guilang.get("GUI_ANALYZER_TEMPLATE", sSyntaxElement, m_sError,
1022 _guilang.get("ERR_NR_2022_0_INCLUDE_NOT_EXIST", e.getToken())), ANNOTATION_ERROR);
1023 }
1024
1025 m_nCurPos = m_editor->GetLineEndPosition(m_editor->LineFromPosition(m_nCurPos));
1026 return AnnotCount;
1027 }
1028
1029 // Examine the procedure / MATLAB function starting at m_editor position
1030 // This includes esp. the calculation of the standard coding metrics
1031 if ((m_editor->m_fileType == FILE_NPRC && sSyntaxElement == "procedure")
1032 || (m_editor->m_fileType == FILE_MATLAB && sSyntaxElement == "function"))
1033 {
1034 // Use the block match function, which is capable of doing both: NumeRe and MATLAB syntax
1035 vector<int> vBlock = m_editor->BlockMatch(m_nCurPos);
1037
1038 // If the current file is a procedure file, then decode the
1039 // argument list and store it in the list of known local
1040 // variables
1042 {
1043 int nArgumentParensStart = m_editor->FindText(m_nCurPos, m_editor->GetLineEndPosition(m_nCurrentLine), "(");
1044 int nArgumentParensEnd = m_editor->BraceMatch(nArgumentParensStart);
1045
1046 if (nArgumentParensStart != -1 && nArgumentParensEnd != -1)
1047 {
1048 // Decode the list
1049 for (int i = nArgumentParensStart+1; i < nArgumentParensEnd; i++)
1050 {
1051 if (m_editor->GetStyleAt(i) == wxSTC_NPRC_IDENTIFIER
1052 || m_editor->GetStyleAt(i) == wxSTC_NPRC_CUSTOM_FUNCTION
1053 || m_editor->GetStyleAt(i) == wxSTC_NPRC_CLUSTER)
1054 {
1055 wxString sArg = m_editor->GetTextRange(m_editor->WordStartPosition(i, true), m_editor->WordEndPosition(i, true));
1056
1057 // Will this argument be overwritten by a declare?
1058 if (m_editor->GetStyleAt(i) == wxSTC_NPRC_IDENTIFIER && m_symdefs.isSymbol(sArg.ToStdString()))
1059 AnnotCount += addToAnnotation(_guilang.get("GUI_ANALYZER_TEMPLATE", sArg.ToStdString(), m_sError, _guilang.get("GUI_ANALYZER_DECLAREOVERWRITES", sArg.ToStdString())), ANNOTATION_ERROR);
1060
1061 m_vLocalVariables.push_back(pair<string,int>(sArg.ToStdString(), m_editor->GetStyleAt(i)));
1062 i = m_editor->WordEndPosition(i, true);
1063 }
1064 }
1065 }
1066 }
1067
1068 if (vBlock.back() != wxSTC_INVALID_POSITION)
1069 {
1070 int nProcedureEnd = vBlock.back();
1071
1072 // This is only needed for NumeRe procedures
1074 {
1075 // check the name of the procedure - is there a naming procedure?
1076 int nNamingProcedure = m_editor->m_search->FindNamingProcedure();
1077
1078 if (nNamingProcedure == wxNOT_FOUND)
1079 {
1080 AnnotCount += addToAnnotation(_guilang.get("GUI_ANALYZER_TEMPLATE", sSyntaxElement, m_sError, _guilang.get("GUI_ANALYZER_NONAMINGPROCEDURE")), ANNOTATION_ERROR);
1081 }
1083 {
1084 AnnotCount += addToAnnotation(_guilang.get("GUI_ANALYZER_TEMPLATE", sSyntaxElement, m_sWarn, _guilang.get("GUI_ANALYZER_THISFILEPROCEDURE")), ANNOTATION_WARN);
1085 }
1086 }
1087 // Calculate the code metrics:
1088 // Complexity
1089 int nCyclomaticComplexity = calculateCyclomaticComplexity(m_nCurrentLine, m_editor->LineFromPosition(nProcedureEnd));
1090
1091 // LinesOfCode
1092 int nLinesOfCode = calculateLinesOfCode(m_nCurrentLine, m_editor->LineFromPosition(nProcedureEnd)) - 2;
1093
1094 // Number of comments
1095 int nNumberOfComments = countNumberOfComments(m_nCurrentLine, m_editor->LineFromPosition(nProcedureEnd));
1096
1097 // Comment density
1098 double dCommentDensity = (double)nNumberOfComments / (double)nLinesOfCode;
1099
1100 // Compare the metrics with the contants and issue a note or a warning
1101 if (m_options->GetAnalyzerOption(Options::PROCEDURE_LENGTH) && nLinesOfCode < 3)
1102 AnnotCount += addToAnnotation(_guilang.get("GUI_ANALYZER_TEMPLATE", sSyntaxElement, m_sWarn, _guilang.get("GUI_ANALYZER_INLINING")), ANNOTATION_WARN);
1103
1104 if (m_options->GetAnalyzerOption(Options::COMPLEXITY) && nCyclomaticComplexity > MAXCOMPLEXITYWARN)
1105 AnnotCount += addToAnnotation(_guilang.get("GUI_ANALYZER_TEMPLATE", sSyntaxElement, m_sWarn, _guilang.get("GUI_ANALYZER_HIGHCOMPLEXITY", toString(nCyclomaticComplexity))), ANNOTATION_WARN);
1107 AnnotCount += addToAnnotation(_guilang.get("GUI_ANALYZER_TEMPLATE", sSyntaxElement, m_sNote, _guilang.get("GUI_ANALYZER_HIGHCOMPLEXITY", toString(nCyclomaticComplexity))), ANNOTATION_NOTE);
1108
1110 AnnotCount += addToAnnotation(_guilang.get("GUI_ANALYZER_TEMPLATE", sSyntaxElement, m_sNote, _guilang.get("GUI_ANALYZER_MANYLINES", toString(nLinesOfCode))), ANNOTATION_NOTE);
1111
1113 AnnotCount += addToAnnotation(_guilang.get("GUI_ANALYZER_TEMPLATE", sSyntaxElement, m_sNote, _guilang.get("GUI_ANALYZER_LOWCOMMENTDENSITY", toString(dCommentDensity * 100.0, 3))), ANNOTATION_NOTE);
1114
1116 AnnotCount += addToAnnotation(_guilang.get("GUI_ANALYZER_TEMPLATE", sSyntaxElement, m_sNote, _guilang.get("GUI_ANALYZER_HIGHCOMMENTDENSITY", toString(dCommentDensity * 100.0, 3))), ANNOTATION_NOTE);
1117 }
1118 }
1119
1120 // Handle the "return" command in procedures (not needed in MATLAB)
1121 if (m_editor->m_fileType == FILE_NPRC && sSyntaxElement == "return")
1122 {
1123 // Try to find the end of the current procedure
1124 int nProcedureEnd = m_editor->FindText(m_nCurPos, m_editor->GetLastPosition(), "endprocedure", wxSTC_FIND_MATCHCASE | wxSTC_FIND_WHOLEWORD);
1125
1126 // Get the argument of the return command and strip the spaces
1127 string sArgs = m_editor->GetTextRange(wordend, m_editor->GetLineEndPosition(m_nCurrentLine)).ToStdString();
1128 while (sArgs.back() == '\r' || sArgs.back() == '\n')
1129 sArgs.pop_back();
1130 StripSpaces(sArgs);
1131
1132 // Ensure that the end of the procedure was found
1133 if (nProcedureEnd == -1)
1134 {
1135 AnnotCount += addToAnnotation(_guilang.get("GUI_ANALYZER_TEMPLATE", highlightFoundOccurence(sSyntaxElement, wordstart, sSyntaxElement.length()), m_sError, _guilang.get("GUI_ANALYZER_MISSINGENDPROCEDURE")), ANNOTATION_ERROR);
1136 }
1137
1138 // Examine the argument
1139 if (sArgs.length())
1140 {
1141 // Inform the user to add an semicolon to the arguments, if he uses something else than "void"
1142 if (m_options->GetAnalyzerOption(Options::RESULT_SUPPRESSION) && sArgs.back() != ';' && sArgs != "void")
1143 AnnotCount += addToAnnotation(_guilang.get("GUI_ANALYZER_TEMPLATE", highlightFoundOccurence(sSyntaxElement, wordend + 1, sArgs.length()), m_sNote, _guilang.get("GUI_ANALYZER_RETURN_ADDSEMICOLON")), ANNOTATION_NOTE);
1144 }
1145 else
1146 {
1147 // Inform the user that the return value will always be "true", if he doesn't append a value
1148 AnnotCount += addToAnnotation(_guilang.get("GUI_ANALYZER_TEMPLATE", highlightFoundOccurence(sSyntaxElement, wordstart, sSyntaxElement.length()), m_sNote, _guilang.get("GUI_ANALYZER_RETURN_ALWAYSTRUE")), ANNOTATION_NOTE);
1149 }
1150 }
1151
1152 // Handle blocks with their corresponding end
1153 if (m_editor->isBlockStart(sSyntaxElement, true) != wxNOT_FOUND || m_editor->isBlockEnd(sSyntaxElement) != wxNOT_FOUND)
1154 {
1155 // Try to find the matching block parts
1156 vector<int> vMatch = m_editor->BlockMatch(m_nCurPos);
1157
1158 if (vMatch.size() > 1)
1159 {
1160 // If there's an invalid position, m_editor means that the current block is unfinished
1161 if (vMatch.front() == wxSTC_INVALID_POSITION || vMatch.back() == wxSTC_INVALID_POSITION)
1162 AnnotCount += addToAnnotation(_guilang.get("GUI_ANALYZER_TEMPLATE", highlightFoundOccurence(sSyntaxElement, wordstart, sSyntaxElement.length()), m_sError, _guilang.get("GUI_ANALYZER_UNFINISHEDBLOCK")), ANNOTATION_ERROR);
1163 }
1164
1165 // Clear the list of local variables
1166 if (sSyntaxElement == "endprocedure")
1167 m_vLocalVariables.clear();
1168
1169 // Remove the last declared local variable
1170 if (sSyntaxElement == "endfor" && m_editor->m_fileType == FILE_NPRC && m_vLocalVariables.size())
1171 m_vLocalVariables.pop_back();
1172 }
1173 m_nCurPos = wordend;
1174
1175 // Return the counted annotations
1176 return AnnotCount;
1177}
1178
1179
1189{
1190 AnnotationCount AnnotCount;
1191
1192 bool canContinue = false;
1193 int wordstart = m_editor->WordStartPosition(m_nCurPos, true);
1194 int wordend = m_editor->WordEndPosition(m_nCurPos, true);
1195
1196 // Get the corresponding syntax element
1197 string sSyntaxElement = m_editor->GetTextRange(wordstart, wordend).ToStdString();
1198
1199 // Handle method (modifier) calls, also appends a pair of parentheses if needed
1200 if ((m_editor->m_fileType == FILE_NSCR || m_editor->m_fileType == FILE_NPRC) && m_editor->GetStyleAt(m_nCurPos) == wxSTC_NSCR_METHOD)
1201 {
1202 if (std::find(m_STRING_METHODS.begin(), m_STRING_METHODS.end(), sSyntaxElement + "()") != m_STRING_METHODS.end() && m_currentMode == "matop")
1203 AnnotCount += addToAnnotation(_guilang.get("GUI_ANALYZER_TEMPLATE", highlightFoundOccurence(sSyntaxElement + "()", wordstart, wordend-wordstart), m_sError, _guilang.get("GUI_ANALYZER_STRINGFUNCTION", sSyntaxElement + "()")), ANNOTATION_ERROR);
1204
1205 // ignore modifiers, i.e. method without parentheses
1206 static string sMODIFIER = ",len,cols,lines,rows,grid,avg,std,min,max,med,sum,prd,cnt,num,norm,and,or,xor,name,size,minpos,maxpos,description,";
1207
1208 if (sMODIFIER.find("," + sSyntaxElement + ",") == string::npos)
1209 sSyntaxElement += "()";
1210
1211 sSyntaxElement.insert(0, "VAR.");
1212 }
1213 else
1214 sSyntaxElement += "()";
1215
1216 // Is the current function called without a target variable?
1217 if (m_options->GetAnalyzerOption(Options::RESULT_ASSIGNMENT) && m_editor->PositionFromLine(m_nCurrentLine) == wordstart && !isContinuedLine && sSyntaxElement != "sleep()")
1218 {
1219 // The function is called at the first position without a target variable
1220 AnnotCount += addToAnnotation(_guilang.get("GUI_ANALYZER_TEMPLATE", highlightFoundOccurence(sSyntaxElement, wordstart, wordend-wordstart), m_sWarn, _guilang.get("GUI_ANALYZER_ASSIGNTOVARIABLE", sSyntaxElement)), ANNOTATION_WARN);
1221 }
1222 else if (sSyntaxElement != "sleep()")
1223 {
1224 // Try to find a assignment operator before the function
1225 // Other possibilities are commands and procedure calls
1226 for (int j = m_editor->PositionFromLine(m_nCurrentLine); j < wordstart; j++)
1227 {
1229 {
1230 canContinue = true;
1231 break;
1232 }
1233 }
1234
1235 // Was an operator or a command found?
1236 if (m_options->GetAnalyzerOption(Options::RESULT_ASSIGNMENT) && !canContinue && !isContinuedLine)
1237 AnnotCount += addToAnnotation(_guilang.get("GUI_ANALYZER_TEMPLATE", highlightFoundOccurence(sSyntaxElement, wordstart, wordend-wordstart), m_sWarn, _guilang.get("GUI_ANALYZER_ASSIGNTOVARIABLE", sSyntaxElement)), ANNOTATION_WARN);
1238 }
1239
1240 // There's a missing parenthesis?
1241 if (m_editor->BraceMatch(wordend) < wordend && sSyntaxElement.find('(') != string::npos)
1242 {
1243 // MATLAB doesn't require a parenthesis pair for empty arguments.
1244 // However, issue a warning as it is good practice to visually distinguish between variables and functions
1246 AnnotCount += addToAnnotation(_guilang.get("GUI_ANALYZER_TEMPLATE", highlightFoundOccurence(sSyntaxElement, wordstart, wordend-wordstart), m_sWarn, _guilang.get("GUI_ANALYZER_MISSINGPARENTHESIS")), ANNOTATION_WARN);
1247 else
1248 AnnotCount += addToAnnotation(_guilang.get("GUI_ANALYZER_TEMPLATE", highlightFoundOccurence(sSyntaxElement, wordstart, wordend-wordstart), m_sError, _guilang.get("GUI_ANALYZER_MISSINGPARENTHESIS")), ANNOTATION_ERROR);
1249 }
1250 else if (sSyntaxElement != "time()"
1251 && sSyntaxElement != "clock()"
1252 && sSyntaxElement != "version()"
1253 && sSyntaxElement != "getlasterror()"
1254 && sSyntaxElement != "getversioninfo()"
1255 && sSyntaxElement.find('(') != string::npos)
1256 {
1257 // Check for missing arguments
1258 int nPos = m_editor->BraceMatch(wordend);
1259 string sArgument = m_editor->GetTextRange(wordend + 1, nPos).ToStdString();
1260 StripSpaces(sArgument);
1261 if (!sArgument.length())
1262 {
1263 AnnotCount += addToAnnotation(_guilang.get("GUI_ANALYZER_TEMPLATE", highlightFoundOccurence(sSyntaxElement, wordend, 2), m_sError, _guilang.get("GUI_ANALYZER_MISSINGARGUMENT")), ANNOTATION_ERROR);
1264 }
1265 }
1266
1267
1268 // Examine mode-specific functions
1269 if (std::find(m_MATOP_FUNCS.begin(), m_MATOP_FUNCS.end(), sSyntaxElement) != m_MATOP_FUNCS.end() && m_currentMode != "matop")
1270 AnnotCount += addToAnnotation(_guilang.get("GUI_ANALYZER_TEMPLATE", highlightFoundOccurence(sSyntaxElement, wordstart, wordend-wordstart), m_sError, _guilang.get("GUI_ANALYZER_MATOPFUNCTION", sSyntaxElement)), ANNOTATION_ERROR);
1271
1272 if (std::find(m_STRING_FUNCS.begin(), m_STRING_FUNCS.end(), sSyntaxElement) != m_STRING_FUNCS.end() && m_currentMode == "matop")
1273 AnnotCount += addToAnnotation(_guilang.get("GUI_ANALYZER_TEMPLATE", highlightFoundOccurence(sSyntaxElement, wordstart, wordend-wordstart), m_sError, _guilang.get("GUI_ANALYZER_STRINGFUNCTION", sSyntaxElement)), ANNOTATION_ERROR);
1274
1275 if (std::find(m_DRAW_FUNCS.begin(), m_DRAW_FUNCS.end(), sSyntaxElement) != m_DRAW_FUNCS.end() && m_currentMode != "draw")
1276 AnnotCount += addToAnnotation(_guilang.get("GUI_ANALYZER_TEMPLATE", highlightFoundOccurence(sSyntaxElement, wordstart, wordend-wordstart), m_sError, _guilang.get("GUI_ANALYZER_DRAWFUNCTION", sSyntaxElement)), ANNOTATION_ERROR);
1277
1278
1279 m_nCurPos = wordend;
1280
1281 // return the counted number of gathered annotations
1282 return AnnotCount;
1283}
1284
1285
1294{
1295 AnnotationCount AnnotCount;
1296
1297 int nProcStart = m_nCurPos;
1298
1299 // Try to find the current procedure call
1300 string sSyntaxElement = m_editor->m_search->FindMarkedProcedure(m_nCurPos).ToStdString();
1301 if (!sSyntaxElement.length())
1302 return AnnotCount;
1303
1304 // Advance the character pointer until the style type changes
1306 m_nCurPos++;
1307
1308 // Validate the procedure name for unwanted characters
1309 for (size_t i = 0; i < sSyntaxElement.length(); i++)
1310 {
1311 if (!isalnum(sSyntaxElement[i])
1312 && sSyntaxElement[i] != '$'
1313 && sSyntaxElement[i] != '/'
1314 && sSyntaxElement[i] != ':'
1315 && sSyntaxElement[i] != '_'
1316 && sSyntaxElement[i] != '\''
1317 && sSyntaxElement[i] != '('
1318 && sSyntaxElement[i] != ')'
1319 && sSyntaxElement[i] != '~')
1320 {
1321 AnnotCount += addToAnnotation(_guilang.get("GUI_ANALYZER_TEMPLATE", highlightFoundOccurence(sSyntaxElement, nProcStart, m_nCurPos-nProcStart+1), m_sError, _guilang.get("GUI_ANALYZER_PROCEDUREINVALIDCHARS", sSyntaxElement)), ANNOTATION_ERROR);
1322 break;
1323 }
1324 }
1325
1326 // Try to find the correspondung procedure definition
1327 if (!m_editor->m_search->FindProcedureDefinition().length())
1328 {
1329 // Procedure definition was not found
1330 AnnotCount += addToAnnotation(_guilang.get("GUI_ANALYZER_TEMPLATE", highlightFoundOccurence(sSyntaxElement, nProcStart, m_nCurPos-nProcStart+1), m_sError, _guilang.get("GUI_ANALYZER_PROCEDURENOTFOUND", sSyntaxElement)), ANNOTATION_ERROR);
1331 }
1332
1333 // return the number of gathered annotations
1334 return AnnotCount;
1335}
1336
1337
1346{
1347 AnnotationCount AnnotCount;
1348
1349 int wordstart = m_editor->WordStartPosition(m_nCurPos, true);
1350 int wordend = m_editor->WordEndPosition(m_nCurPos, true);
1351
1352 // Shift the word end position, if the following character is a dot
1353 if (m_editor->GetCharAt(wordend) == '.' && m_editor->GetStyleAt(wordend + 1) != wxSTC_NSCR_METHOD)
1354 wordend = m_editor->WordEndPosition(wordend + 1, true);
1355
1356 // Get the corresponding syntax element
1357 std::string sSyntaxElement = m_editor->GetTextRange(wordstart, wordend).ToStdString();
1358
1359 // Warn about global variables
1361 {
1362 bool bOK = false;
1363
1364 // Try to find the current identifier in the list
1365 // of known local variables
1366 for (size_t i = 0; i < m_vLocalVariables.size(); i++)
1367 {
1368 if (m_vLocalVariables[i].first == sSyntaxElement && m_vLocalVariables[i].second == m_editor->GetStyleAt(m_nCurPos))
1369 {
1370 bOK = true;
1371 break;
1372 }
1373 }
1374
1375 // Check in the symbol declarations
1376 if (!bOK)
1377 bOK = m_symdefs.isSymbol(sSyntaxElement);
1378
1379 // nothing found
1380 if (!bOK)
1381 {
1382 wxString currentline = m_editor->GetLine(m_nCurrentLine);
1383
1384 // Ignore y* variables from odesolve
1385 if (currentline.substr(currentline.find_first_not_of(" \t"), 9) != "odesolve " || !(sSyntaxElement[0] == 'y' && sSyntaxElement.length() > 1 && sSyntaxElement.find_first_not_of("0123456789", 1) == string::npos))
1386 AnnotCount += addToAnnotation(_guilang.get("GUI_ANALYZER_TEMPLATE", highlightFoundOccurence(sSyntaxElement, wordstart, sSyntaxElement.length()), m_sWarn, _guilang.get("GUI_ANALYZER_GLOBALVARIABLE", sSyntaxElement)), ANNOTATION_WARN);
1387 }
1388 }
1389
1390 // Return, if the current identifier is a data object
1392 {
1395 && m_editor->GetStyleAt(wordstart) == wxSTC_NSCR_CUSTOM_FUNCTION
1396 && m_editor->GetCharAt(wordstart-1) != '&'
1397 && m_editor->GetTextRange(wordend, wordend+3) != "()&")
1398 {
1399 AnnotCount += addToAnnotation(_guilang.get("GUI_ANALYZER_TEMPLATE", highlightFoundOccurence(sSyntaxElement, wordstart, sSyntaxElement.length()), m_sWarn, _guilang.get("GUI_ANALYZER_WARN_TABLE_REFERENCE", sSyntaxElement)), ANNOTATION_WARN);
1400 }
1401
1402 m_nCurPos = wordend;
1403 return AnnotCount;
1404 }
1405
1406 // Handle very short variable names
1407 if (sSyntaxElement.length() < 4 && sSyntaxElement.length() > 1 && sSyntaxElement.find_first_not_of("\r\n") != string::npos && sSyntaxElement.find('.') == string::npos)
1408 {
1409 // Too short
1410 if (m_options->GetAnalyzerOption(Options::VARIABLE_LENGTH) && !(sSyntaxElement.length() == 2 && ((sSyntaxElement[1] >= '0' && sSyntaxElement[1] <= '9') || sSyntaxElement[0] == 'd')))
1411 AnnotCount += addToAnnotation(_guilang.get("GUI_ANALYZER_TEMPLATE", highlightFoundOccurence(sSyntaxElement, wordstart, sSyntaxElement.length()), m_sNote, _guilang.get("GUI_ANALYZER_VARNAMETOOSHORT", sSyntaxElement)), ANNOTATION_NOTE);
1412 }
1413
1414 // Handle the variable's names: are they following guidelines?
1415 if (sSyntaxElement.length() > 2 && sSyntaxElement.find_first_not_of("\r\n") != string::npos && sSyntaxElement.find('.') == string::npos)
1416 {
1417 size_t shift = 0;
1418
1419 // We want to start the procedures arguments with an underscore (not possible in MATLAB)
1420 while (sSyntaxElement[shift] == '_')
1421 shift++;
1422
1423 // Because function names definitions are not highlighted different in MATLAB code, we leave the function
1424 // at m_editor position
1425 if (m_editor->m_fileType == FILE_MATLAB && m_hasProcedureDefinition && m_editor->GetCharAt(wordend) == '(')
1426 {
1427 m_nCurPos = wordend;
1428 return AnnotCount;
1429 }
1430
1431 if (getVariableType(sSyntaxElement) == '\0')
1432 {
1433 // var not type-oriented
1434 // Add and underscore to indicate the procedures arguments
1437 && !shift
1439 AnnotCount += addToAnnotation(_guilang.get("GUI_ANALYZER_TEMPLATE",
1440 highlightFoundOccurence(sSyntaxElement, wordstart, sSyntaxElement.length()),
1441 m_sNote,
1442 _guilang.get("GUI_ANALYZER_INDICATEARGUMENT")), ANNOTATION_NOTE);
1443
1444 // variable should begin with lowercase letter indicate its type
1446 AnnotCount += addToAnnotation(_guilang.get("GUI_ANALYZER_TEMPLATE",
1447 highlightFoundOccurence(sSyntaxElement, wordstart, sSyntaxElement.length()),
1448 m_sNote,
1449 _guilang.get("GUI_ANALYZER_VARNOTTYPEORIENTED", sSyntaxElement)), ANNOTATION_NOTE);
1450 }
1453 && !shift
1455 {
1456 // Add and underscore to indicate the procedures arguments
1457 AnnotCount += addToAnnotation(_guilang.get("GUI_ANALYZER_TEMPLATE",
1458 highlightFoundOccurence(sSyntaxElement, wordstart, sSyntaxElement.length()),
1459 m_sNote,
1460 _guilang.get("GUI_ANALYZER_INDICATEARGUMENT")), ANNOTATION_NOTE);
1461 }
1462 }
1463
1464 m_nCurPos = wordend;
1465
1466 // Return the gathered number of annotations
1467 return AnnotCount;
1468}
1469
1470
1479{
1480 AnnotationCount AnnotCount;
1481
1482 // If the current operator is a parenthesis, try to find the matching one
1483 if (m_editor->GetCharAt(m_nCurPos) == '(' || m_editor->GetCharAt(m_nCurPos) == '[' || m_editor->GetCharAt(m_nCurPos) == '{'
1484 || m_editor->GetCharAt(m_nCurPos) == ')' || m_editor->GetCharAt(m_nCurPos) == ']' || m_editor->GetCharAt(m_nCurPos) == '}')
1485 {
1486 int nPos = m_editor->BraceMatch(m_nCurPos);
1487 if (nPos < 0)
1488 {
1489 AnnotCount += addToAnnotation(_guilang.get("GUI_ANALYZER_TEMPLATE", highlightFoundOccurence(string(1, m_editor->GetCharAt(m_nCurPos)), m_nCurPos, 1), m_sError, _guilang.get("GUI_ANALYZER_MISSINGPARENTHESIS")), ANNOTATION_ERROR);
1490 }
1491 }
1492
1493 return AnnotCount;
1494}
1495
1496
1505{
1506 AnnotationCount AnnotCount;
1507 int nNumberStart = m_nCurPos;
1508 int nLineStartPos = m_editor->PositionFromLine(m_nCurrentLine);
1509
1510 // Advance until the style of the next character is not a number any more
1512 m_nCurPos++;
1513
1515 return AnnotCount;
1516
1517 // Get the number
1518 string sCurrentNumber = m_editor->GetTextRange(nNumberStart, m_nCurPos + 1).ToStdString();
1519
1520 // Go inversely through the line and try to find an assignment operator
1521 for (int i = nNumberStart; i >= nLineStartPos; i--)
1522 {
1523 // If the current character is a operator and the previous one is not
1525 {
1526 // Is an assignment -> no magic number
1527 if (m_editor->GetCharAt(i) == '='
1528 && m_editor->GetCharAt(i - 1) != '<'
1529 && m_editor->GetCharAt(i - 1) != '>'
1530 && m_editor->GetCharAt(i - 1) != '!'
1531 && m_editor->GetCharAt(i - 1) != '~'
1532 && m_editor->GetCharAt(i - 1) != '=')
1533 break;
1534
1535 // All other operators are indicating the current number as magic number
1536 AnnotCount += addToAnnotation(_guilang.get("GUI_ANALYZER_TEMPLATE", highlightFoundOccurence(sCurrentNumber, nNumberStart, sCurrentNumber.length()), m_sWarn, _guilang.get("GUI_ANALYZER_MAGICNUMBER")), ANNOTATION_WARN);
1537 break;
1538 }
1539 }
1540
1541 // Return the number of gathered annotations
1542 return AnnotCount;
1543}
1544
1545
1554{
1555 AnnotationCount AnnotCount;
1556
1557 int wordstart = m_editor->WordStartPosition(m_nCurPos, true);
1558 int wordend = m_editor->WordEndPosition(m_nCurPos, true);
1559
1560 // Get the corresponding syntax element
1561 std::string sSyntaxElement = m_editor->GetTextRange(wordstart, wordend).ToStdString();
1562
1563 int contextPoint = 0;
1564
1565 for (int i = m_nCurPos; i > m_editor->PositionFromLine(m_nCurrentLine); i--)
1566 {
1567 if ((m_editor->GetCharAt(i) == '(' || m_editor->GetCharAt(i) == '{')
1568 && (m_editor->BraceMatch(i) >= m_nCurPos || m_editor->BraceMatch(i) == -1) // either no brace (yet) or the brace further right
1569 && (m_editor->GetStyleAt(i - 1) == wxSTC_NSCR_CLUSTER
1570 || m_editor->GetStyleAt(i - 1) == wxSTC_NSCR_CUSTOM_FUNCTION
1571 || m_editor->GetStyleAt(i - 1) == wxSTC_NSCR_PREDEFS)) // table() or data()
1572 {
1573 contextPoint = i;
1574 break;
1575 }
1576 }
1577
1578 if (!contextPoint)
1579 {
1580 m_nCurPos = wordend;
1581
1582 // No context point means used outside of data elements,
1583 // i.e. also a misuse
1584 if (sSyntaxElement == "nlen"
1585 || sSyntaxElement == "nrows"
1586 || sSyntaxElement == "ncols"
1587 || sSyntaxElement == "nlines")
1588 {
1589 AnnotCount += addToAnnotation(_guilang.get("GUI_ANALYZER_TEMPLATE",
1590 highlightFoundOccurence(sSyntaxElement, wordstart, sSyntaxElement.length()),
1591 m_sWarn,
1592 _guilang.get("GUI_ANALYZER_DIMVAR_MISUSE", sSyntaxElement)),
1594 }
1595
1596 return AnnotCount;
1597 }
1598
1599 // Check dimensionality variables and their corresponding
1600 // positions in the data element accesses
1601 if (sSyntaxElement == "nlen")
1602 {
1603 // Should only be used in clusters
1604 if (m_editor->GetStyleAt(contextPoint - 1) != wxSTC_NSCR_CLUSTER)
1605 AnnotCount += addToAnnotation(_guilang.get("GUI_ANALYZER_TEMPLATE",
1606 highlightFoundOccurence(sSyntaxElement, wordstart, sSyntaxElement.length()),
1607 m_sWarn,
1608 _guilang.get("GUI_ANALYZER_DIMVAR_MISUSE", sSyntaxElement)),
1610 }
1611 else if (sSyntaxElement == "nrows"
1612 || sSyntaxElement == "ncols"
1613 || sSyntaxElement == "nlines")
1614 {
1615 // Should only be used in tables. Additionally,
1616 // their order might be mixed up
1617 if (m_editor->GetStyleAt(contextPoint - 1) != wxSTC_NSCR_CUSTOM_FUNCTION
1618 && m_editor->GetStyleAt(contextPoint - 1) != wxSTC_NSCR_PREDEFS)
1619 AnnotCount += addToAnnotation(_guilang.get("GUI_ANALYZER_TEMPLATE",
1620 highlightFoundOccurence(sSyntaxElement, wordstart, sSyntaxElement.length()),
1621 m_sWarn,
1622 _guilang.get("GUI_ANALYZER_DIMVAR_MISUSE", sSyntaxElement)),
1624
1625 // Now, check for mixed up orientations
1626 int contextEnd = m_editor->BraceMatch(contextPoint);
1627
1628 if (contextEnd == wxNOT_FOUND)
1629 contextEnd = m_editor->GetLineEndPosition(m_nCurrentLine);
1630
1631 // Get the context content and split it up into
1632 // the single arguments
1633 std::string sContext = m_editor->GetTextRange(contextPoint+1, contextEnd).ToStdString();
1635
1636 // Check, whether the dimension vars can be found in the
1637 // opposite dimension
1638 if ((sSyntaxElement == "ncols" && findVariableInExpression(args[0], sSyntaxElement, 0u) != std::string::npos)
1639 || (sSyntaxElement != "ncols" && findVariableInExpression(args[1], sSyntaxElement, 0u) != std::string::npos))
1640 AnnotCount += addToAnnotation(_guilang.get("GUI_ANALYZER_TEMPLATE",
1641 highlightFoundOccurence(sSyntaxElement, wordstart, sSyntaxElement.length()),
1642 m_sWarn,
1643 _guilang.get("GUI_ANALYZER_DIMVAR_MIXUP", sSyntaxElement)),
1645 }
1646
1647 m_nCurPos = wordend;
1648
1649 // Return the gathered number of annotations
1650 return AnnotCount;
1651}
1652
1653
1662AnnotationCount CodeAnalyzer::addToAnnotation(const string& sMessage, int nStyle)
1663{
1664 AnnotationCount annoCount;
1665
1666 // Ensure that the options allow the current type of
1667 // annotation
1669 return annoCount;
1670
1672 return annoCount;
1673
1675 return annoCount;
1676
1677 int chartoadd = 0;
1678 // Do not show the same message multiple times
1679 if (m_sCurrentLine.find(sMessage) != string::npos
1680 && (!m_sCurrentLine.find(sMessage) || m_sCurrentLine[m_sCurrentLine.find(sMessage) - 1] == '\n'))
1681 return annoCount;
1682
1683 if (m_sCurrentLine.length())
1684 {
1685 m_sCurrentLine += "\n";
1686 chartoadd++;
1687 }
1688
1689 m_sCurrentLine += sMessage;
1690 chartoadd += m_editor->countUmlauts(sMessage);
1691
1692 m_sStyles.append(sMessage.length() + chartoadd, nStyle);
1693
1694 // Increment the total counter
1695 if (nStyle == ANNOTATION_NOTE)
1696 annoCount.nNotes++;
1697 else if (nStyle == ANNOTATION_WARN)
1698 annoCount.nWarnings++;
1699 else if (nStyle == ANNOTATION_ERROR)
1700 annoCount.nErrors++;
1701
1702 return annoCount;
1703}
1704
1705
1716bool CodeAnalyzer::containsAssignment(const string& sCurrentLine)
1717{
1718 if (sCurrentLine.find('=') == string::npos)
1719 return false;
1720
1721 // Go through the line and check, whether there's
1722 // a single equal sign
1723 for (size_t i = 1; i < sCurrentLine.length() - 1; i++)
1724 {
1725 if (sCurrentLine[i] == '='
1726 && sCurrentLine[i - 1] != '<'
1727 && sCurrentLine[i - 1] != '>'
1728 && sCurrentLine[i - 1] != '!'
1729 && sCurrentLine[i - 1] != '='
1730 && sCurrentLine[i + 1] != '='
1731 && !isInQuotes(sCurrentLine, i))
1732 return true;
1733 }
1734
1735 return false;
1736}
1737
1738
1750string CodeAnalyzer::highlightFoundOccurence(const string& sElement, int nPos, int nLength)
1751{
1752 m_editor->SetIndicatorCurrent(HIGHLIGHT_ANNOTATION);
1753 m_editor->IndicatorFillRange(nPos, nLength);
1754 return sElement;
1755}
1756
1757
1769int CodeAnalyzer::calculateCyclomaticComplexity(int startline, int endline)
1770{
1771 int nCycComplx = 1;
1772
1773 // Go through the block of code indicated by the
1774 // starting and endling line
1775 for (int i = m_editor->PositionFromLine(startline); i < m_editor->GetLineEndPosition(endline); i++)
1776 {
1777 // Examine command occurences, which may be
1778 // flow control statements
1780 {
1781 int wordstart = m_editor->WordStartPosition(i, true);
1782 int wordend = m_editor->WordEndPosition(i, true);
1783
1784 if (m_editor->GetTextRange(wordstart, wordend) == "if"
1785 || m_editor->GetTextRange(wordstart, wordend) == "elseif"
1786 || m_editor->GetTextRange(wordstart, wordend) == "while"
1787 || m_editor->GetTextRange(wordstart, wordend) == "case"
1788 || m_editor->GetTextRange(wordstart, wordend) == "try"
1789 || m_editor->GetTextRange(wordstart, wordend) == "for")
1790 nCycComplx++;
1791
1792 i = wordend;
1793 }
1794
1795 // Examine standard function calls, which may be
1796 // logical functions
1798 {
1799 int wordstart = m_editor->WordStartPosition(i, true);
1800 int wordend = m_editor->WordEndPosition(i, true);
1801
1802 if (m_editor->GetTextRange(wordstart, wordend) == "and"
1803 || m_editor->GetTextRange(wordstart, wordend) == "or"
1804 || m_editor->GetTextRange(wordstart, wordend) == "xor")
1805 nCycComplx++;
1806
1807 i = wordend;
1808 }
1809
1810 // Examine operaters, which may be logical operators
1812 {
1813 int j = i;
1814
1816 j++;
1817
1818 if (m_editor->GetTextRange(i, j) == "&"
1819 || m_editor->GetTextRange(i, j) == "&&"
1820 || m_editor->GetTextRange(i, j) == "|"
1821 || m_editor->GetTextRange(i, j) == "||"
1822 || m_editor->GetTextRange(i, j) == "|||")
1823 nCycComplx++;
1824
1825 i = j;
1826 }
1827 }
1828
1829 return nCycComplx;
1830}
1831
1832
1844int CodeAnalyzer::calculateLinesOfCode(int startline, int endline)
1845{
1846 int nLinesOfCode = 0;
1847 string currentline;
1848
1849 // Go through the block of code indicated by the
1850 // starting and endling line
1851 for (int i = startline; i <= endline; i++)
1852 {
1853 currentline = m_editor->GetLine(i).ToStdString();
1854
1855 // Ignore line comments
1856 if (currentline.find("##") != string::npos)
1857 currentline.erase(currentline.find("##"));
1858
1859 // Check, whether the line contains at least a single
1860 // non-whitespace character
1861 if (currentline.find_first_not_of(" \t\r\n") != string::npos)
1862 {
1863 // Check for block comments
1864 for (size_t j = m_editor->PositionFromLine(i); j < currentline.length() + m_editor->PositionFromLine(i); j++)
1865 {
1868 && m_editor->GetCharAt(j) != ' '
1869 && m_editor->GetCharAt(j) != '\t'
1870 && m_editor->GetCharAt(j) != '\r'
1871 && m_editor->GetCharAt(j) != '\n')
1872 {
1873 nLinesOfCode++;
1874 break;
1875 }
1876 }
1877 }
1878 }
1879
1880 return nLinesOfCode;
1881}
1882
1883
1895int CodeAnalyzer::countNumberOfComments(int startline, int endline)
1896{
1897 int nComments = 0;
1898
1899 // Go through the block of code indicated by the
1900 // starting and endling line
1901 for (int i = m_editor->PositionFromLine(startline); i < m_editor->GetLineEndPosition(endline); i++)
1902 {
1903 // Count every line comment and block comment
1905 {
1906 nComments++;
1907
1908 // Find the end of the current block comment
1909 for (int j = i; j < m_editor->GetLineEndPosition(endline); j++)
1910 {
1912 {
1913 i = j;
1914 break;
1915 }
1916
1917 // Count the lines contained in the block comment
1918 if (j > i + 1 && m_editor->PositionFromLine(m_editor->LineFromPosition(j)) == j)
1919 nComments++;
1920 }
1921 }
1922 }
1923
1924 return nComments;
1925}
1926
1927
1939char CodeAnalyzer::getVariableType(const std::string& sVarName)
1940{
1941 // Check some special cases first
1942 if (sVarName.length() <= 2
1943 || sVarName.find_first_not_of("\r\n") == std::string::npos
1944 || sVarName.find('.') != std::string::npos)
1945 return '\0';
1946
1947 size_t shift = 0;
1948
1949 // We want to start the procedures arguments with an underscore (not possible in MATLAB)
1950 while (sVarName[shift] == '_')
1951 shift++;
1952
1953 // numerical/int string float standard vars (x,y,z,t)
1954 static std::string sFirstChars = "nsfbdxyzt";
1955
1956 if (sVarName.length() > shift+1
1957 && (std::isupper(sVarName[shift+1]) || sVarName[shift+1] == '_')
1958 && sFirstChars.find(sVarName[shift]) != std::string::npos)
1959 return sVarName[shift];
1960
1961 return '\0';
1962}
1963
1964
1973static bool isNumericType(char c)
1974{
1975 return c == 'n' || c == 'd' || c == 'f' || c == 't';
1976}
1977
1978
1988void CodeAnalyzer::changeVariableType(std::string& sVarName, char type)
1989{
1990 char currentType = getVariableType(sVarName);
1991
1992 // floats and integers are not so sharply separated
1993 if (currentType == type
1994 || (isNumericType(currentType) && isNumericType(type)))
1995 return;
1996
1997 size_t shift = 0;
1998
1999 // We want to start the procedures arguments with an underscore (not possible in MATLAB)
2000 while (sVarName[shift] == '_')
2001 shift++;
2002
2003 // Now create the fixed variable name
2004 if (currentType) // Has already a type
2005 sVarName[shift] = type;
2006 else
2007 {
2008 if (!std::isupper(sVarName[shift]))
2009 sVarName[shift] = std::toupper(sVarName[shift]);
2010
2011 sVarName.insert(shift, 1, type);
2012 }
2013}
2014
2015
int calculateCyclomaticComplexity(int startline, int endline)
Calculates the cyclomatic complexity between both lines.
std::string m_sCurrentLine
const int MAXLINESOFCODE
bool m_hasProcedureDefinition
std::string m_sStyles
AnnotationCount analyseNumbers()
Analyses occurences of numerical literals.
AnnotationCount analyseFunctions(bool isContinuedLine)
Analyses calls to standard functions.
int calculateLinesOfCode(int startline, int endline)
Calculates the lines of code between both lines.
const double MINCOMMENTDENSITY
AnnotationCount analysePreDefs()
Analyses occurences of special predefined variables.
std::vector< std::pair< std::string, int > > m_vLocalVariables
std::vector< std::string > m_STRING_METHODS
Options * m_options
std::string m_sNote
const int MAXCOMPLEXITYWARN
std::string m_currentMode
void run()
Starts the code analysis. Requires a pointer to the editor and to the options.
AnnotationCount analyseCommands()
Analyses commands in the code.
AnnotationCount analyseOperators()
Analyses occurences of operators.
bool containsAssignment(const std::string &sCurrentLine)
Checks for assignments.
std::string m_sWarn
const int MAXCOMPLEXITYNOTIFY
const double MAXCOMMENTDENSITY
std::string m_sError
std::vector< std::string > m_STRING_FUNCS
std::vector< std::string > m_MATOP_FUNCS
SymDefManager m_symdefs
static void changeVariableType(std::string &sVarName, char type)
Change the variable's type to the passed type.
AnnotationCount analyseProcedures()
Analyses calls to procedures.
AnnotationCount addToAnnotation(const std::string &sMessage, int nStyle)
Adds the passed sMessage with the style to the internal cache.
std::vector< std::string > m_DRAW_FUNCS
CodeAnalyzer(NumeReEditor *parent, Options *opts)
NumeReEditor * m_editor
AnnotationCount analyseIdentifiers()
Analyses occurences of identifiers.
std::string highlightFoundOccurence(const std::string &sElement, int nPos, int nLength)
Highlights the occurence in the editor.
int countNumberOfComments(int startline, int endline)
Counts the number of comment lines between both lines.
static char getVariableType(const std::string &sVarName)
Returns the leading character, which indicates the type of this variable or a null character,...
This class extends the std::vector for endlessness.
Definition: structures.hpp:838
This class represents a file, which can be included into other files using the @ syntax.
Definition: includer.hpp:32
bool is_open() const
Determine, if the internal included file is open and valid.
Definition: includer.cpp:285
std::string getNextLine()
Return the next line of the included string.
Definition: includer.cpp:168
This class handles the internal language system and returns the language strings of the selected lang...
Definition: language.hpp:38
std::vector< std::string > getList(const std::string &sMessageScheme) const
This member function returns a vector of language strings matching to the passed identifier containin...
Definition: language.cpp:349
std::string get(const std::string &sMessage, const std::vector< std::string > &vTokens) const
This member function returns the language string for the passed language identifier and replaces all ...
Definition: language.cpp:292
The class of the editor window.
Definition: editor.h:53
bool isStyleType(StyleType _type, int nPos)
Determine the syntax style type at the selected position.
Definition: editor.cpp:8249
wxString GetFilenameString()
Definition: editor.cpp:4314
int isBlockStart(const wxString &sWord, bool allowIntermediate=false)
This function returns true, if a passed word corresponds to a control flow statement block start.
Definition: editor.cpp:7109
bool getEditorSetting(EditorSettings _setting)
Returns true, if the selected setting is active.
Definition: editor.cpp:2819
SearchController * m_search
Definition: editor.h:393
wxFileName GetFileName()
Definition: editor.cpp:4328
@ SETTING_USEANALYZER
Definition: editor.h:72
std::vector< int > BlockMatch(int nPos)
Finds all matching flow control statements.
Definition: editor.cpp:2994
FileFilterType m_fileType
Definition: editor.h:446
@ STYLE_NUMBER
Definition: editor.h:331
@ STYLE_OPERATOR
Definition: editor.h:327
@ STYLE_DATAOBJECT
Definition: editor.h:330
@ STYLE_COMMAND
Definition: editor.h:324
@ STYLE_IDENTIFIER
Definition: editor.h:329
@ STYLE_PROCEDURE
Definition: editor.h:328
@ STYLE_COMMENT_LINE
Definition: editor.h:320
@ STYLE_COMMENT_BLOCK
Definition: editor.h:321
@ STYLE_FUNCTION
Definition: editor.h:325
int isBlockEnd(const wxString &sWord)
This function returns true, if a passed word corresponds to a control flow statement block end.
Definition: editor.cpp:7152
int countUmlauts(const std::string &sStr)
Counts the german umlauts in the current string.
Definition: editor.cpp:8401
This class implements an interface of the internal Settings object adapted to be usable from the GUI.
Definition: Options.h:178
int GetAnalyzerOption(AnalyzerOptions opt) const
Return the value of the selected static code analyzer option.
Definition: options.cpp:256
@ THISFILE_NAMESPACE
Definition: Options.h:328
@ PROGRESS_RUNTIME
Definition: Options.h:329
@ ARGUMENT_UNDERSCORE
Definition: Options.h:324
@ SWITCH_FALLTHROUGH
Definition: Options.h:330
@ COMMENT_DENSITY
Definition: Options.h:313
@ TYPE_ORIENTATION
Definition: Options.h:322
@ LINES_OF_CODE
Definition: Options.h:314
@ VARIABLE_LENGTH
Definition: Options.h:325
@ MAGIC_NUMBERS
Definition: Options.h:316
@ UNUSED_VARIABLES
Definition: Options.h:326
@ INLINE_IF
Definition: Options.h:318
@ USE_WARNINGS
Definition: Options.h:311
@ CONSTANT_EXPRESSION
Definition: Options.h:319
@ USE_ERRORS
Definition: Options.h:312
@ USE_NOTES
Definition: Options.h:310
@ COMPLEXITY
Definition: Options.h:315
@ RESULT_ASSIGNMENT
Definition: Options.h:321
@ GLOBAL_VARIABLES
Definition: Options.h:331
@ MISLEADING_TYPE
Definition: Options.h:323
@ ALWAYS_SHOW_METRICS
Definition: Options.h:317
@ RESULT_SUPPRESSION
Definition: Options.h:320
@ PROCEDURE_LENGTH
Definition: Options.h:327
std::vector< int > FindAll(const wxString &sSymbol, int nStyle, int nStartPos=0, int nEndPos=-1, bool bSearchInComments=false)
Finds all occurences of a code symbol considering the style.
int FindNamingProcedure()
Searches the file for the naming procedure.
wxString FindMarkedProcedure(int charpos, bool ignoreDefinitions=true)
Extracts the procedure call at the selected position.
wxString FindProcedureDefinition()
Searches the definition below the cursor.
SettingsValue & getSetting(const std::string &value)
Returns a reference to the setting value, which corresponds to the passed string. Throws an exception...
Definition: settings.hpp:711
bool & active()
Returns a reference to a boolean value type setting.
Definition: settings.hpp:556
void clear()
Remove all file-static constant declarations.
Definition: symdef.cpp:33
bool isSymbol(const std::string &sSymbol) const
Check, whether the passed string is a defined symbol (will be used by the static code analyzer).
Definition: symdef.cpp:117
void createSymbol(const std::string &sCommandLine)
Create one or more new file-static constant declarations for the current file.
Definition: symdef.cpp:72
Common exception class for all exceptions thrown in NumeRe.
Definition: error.hpp:32
std::string getToken() const
Returns the error token containing additional information about the error.
Definition: error.hpp:438
#define ANNOTATION_ERROR
Language _guilang
static bool isNumericType(char c)
Checks, whether the passed type is a numeric type.
static void replaceDocStrings(std::string &sStr)
#define ANNOTATION_WARN
#define HIGHLIGHT_ANNOTATION
#define ANNOTATION_NOTE
@ FILE_CPP
@ FILE_NPRC
@ FILE_NSCR
@ FILE_MATLAB
size_t findVariableInExpression(const std::string &sExpr, const std::string &sVarName, size_t nPosStart)
This function searches for the selected variable in the passed string and returns the position of the...
void StripSpaces(std::string &)
Removes leading and trailing white spaces and tabulator characters.
int findParameter(const std::string &sCmd, const std::string &sParam, const char cFollowing)
This function searches the passed parameter in the passed command string. If something is found,...
Definition: tools.cpp:113
#define SETTING_B_TABLEREFS
Definition: settings.hpp:45
std::string getNextArgument(std::string &sArgList, bool bCut)
Definition: tools.cpp:2294
Stores the number of annotations for displaying a summary.
Structure for the findCommand function.
unsigned int nPos
std::string sString
std::string toString(int)
Converts an integer to a string without the Settings bloat.
#define SYMDEF_COMMAND
Definition: symdef.hpp:21
Match findCommand(StringView sCmd, const std::string &sCommand)
This function is very important for the command handler.
Definition: tools.cpp:1275
bool isInQuotes(StringView sExpr, unsigned int nPos, bool bIgnoreVarParser)
Checks, whether the position in the passed expression is part of a string literal.
Definition: tools.cpp:1672
EndlessVector< StringView > getAllArguments(StringView sArgList)
Splits up the complete argument list and returns them as an EndlessVector.
Definition: tools.cpp:2346