SonarQubeのプラグインを使用してPascalABC.NETプロジェクトを確認します:SonarC#およびPVS-Studio

写真30





昨年11月、SonarQube用のPVS-Studioプラグインの開発と使用に関する記事がブログに公開されました。 実際のプロジェクトでプラグインをテストするリクエストをいただいたお客様や関心のあるユーザーから多くのフィードバックを受け取りました。 この問題への関心は衰えていないため、C#プロジェクトPascalABC.NETでテストすることにしました。 また、SonarQubeには独自の静的C#コードアナライザーSonarC#が含まれていることを忘れないでください。 写真を完成させるために、調査とSonarC#を実施します。 この作業の目的は、アナライザーを比較することではなく、アナライザーとSonarQubeサービスとの相互作用の主な機能を示すことです。 PVS-Studioはエラーや潜在的な脆弱性を検索するための特別なツールであり、SonarQubeは多数のパラメーター(コードの複製、コーディング標準への準拠、コードカバレッジ)によってコードの品質を評価するサービスであるため、アナライザーの直接比較は完全には正しくありません単体テスト、コードのエラーの可能性、コードのコメントの密度、技術的な負債など



はじめに



SonarQubeプラットフォームとPVS-Studioアナライザーとの統合について説明している記事の資料をよく理解しておくことをお勧めします。



次に、研究プロジェクトについて少し説明します。 PascalABC.NETは、PascalABC.NET、C#、Visual Basic.NET、F#、IronPythonでプログラムを作成するためのWeb環境だけでなく、独自の開発環境を含む新世代のPascalプログラミング言語です。 このプロジェクトはC#で開発され、フリーライセンスLGPLv3の下で配布されました。 プロジェクトサイト 。 ソースコードは、GitHubのリポジトリからダウンロードできます。



PascalABC.NETソリューションには、拡張子が「.cs」の2628個のファイルが含まれており、約752千行のコード( SourceMonitorユーティリティを使用して取得したメトリック)が含まれています。 したがって、プロジェクトは私たちの研究目的に非常に適しています。



SonarC#



前述したように、 SonarQubeサービスには、とりわけ静的C#コードアナライザーが含まれています。 私たちの場合のように、開いているプロジェクトをサイトに追加して分析するには、いくつかの簡単な手順で十分です。



SonarQube Webサイトに登録するためにGitHubアカウントを使用しました。 次に、クイックスタート手順を使用しまし 。 PascalABC.NETプロジェクトのアカウントへのリンク、一意の組織キーの取得、ローカルコンピューターでのセットアップなど、セットアッププロセス全体で約15分かかりました。 プロジェクトの分析にはさらに10分かかりました。 その後、 結果はSonarQube Webサイトにアップロードされ、誰でも見ることができます。



SonarQubeは、PascalABC.NETコードで発生する可能性のあるエラーについて3636の警告を発行しました。



写真1







これらのうち、8個のブロッカー(即時削除が必要)、64個のクリティカル、1742個の重要、および1822個の非重要。 情報メッセージは発行されませんでした。 受け取った警告に精通し、興味深いエラーを見つけ、誤検知の割合を理解してみましょう。 これを行うには、SonarQubeサービスで提供されるさまざまなディメンションで便利なフィルタリングツールを使用します。 ブロックアラートから始めましょう。



ブロッカー



写真32







ご覧のとおり、2つのルールに対してブロッキング警告が発行されます:無限再帰とIDisposableリソースのクリア。 ブロッカー警告の1つの例を次に示します。



写真5







Instanceプロパティのgetセクションで、 instanceの代わりにInstanceが誤って返され、無限再帰が生成されます。



他のすべてのブロッカーレベルの警告もエラーです。



クリティカル



写真33







クリティカルレベルでは、無効な型キャストルールに対して64個の警告が発行されました。 次の警告のいずれかを検討してください。



写真8







コードと実装のリストを検討した結果、アナライザーに同意します。現時点では、 IBaseScopeインターフェイスとIComparableインターフェイスの両方 一度に実装する単一の型は実際には存在せず、その結果、 boxItem.ItemがIComparableチェックは常にfalseになります 。 ただし、この場合のエラーについては説明しません。最初に、このようなチェックが存在することで、型(IComparable)のboxItem.Itemをキャストしようとしたときに例外が発生しなくなるためです。 次に、たとえば、特定のdllをいつでもソリューションに接続できます 。この場合IBaseScope インターフェイスIComparableインターフェイスの両方を実装する型が宣言されます 。 おそらく開発者はこれを頼りにして、チェック後にのみ型変換を実現していました。 私の意見では、考慮される警告はマイナーセクションのメッセージとして分類されるべきであり、実行にとって重要ではありません。また、重大レベルでの存在は誤検知と見なされるべきです。



残りの63個の警告は、考慮された警告に類似しています。



メジャー



このレベルでは、15種類の診断に対して1742年に多くの警告が発行されました。



写真9







実際のエラーを見つけてアナライザーの機能を評価するために、警告のリストを見ていきましょう。



一般的な例外は決してスローされるべきではありません



ルールは、 throwを使用して汎用例外のスローを報告します。 PascalABC.NETプロジェクトコードには、634個の同様の構成体が見つかりました。 大部分の形式は次のとおりです。



写真10







また、開発者によって意図的に残された、コード内の「スタブ」に類似した構造(600以上)が多数あります。



写真11







もちろん、一般的な例外をスローするのは「悪い形式」です。 それにもかかわらず、これは決して間違いではないようです。 さらに、コード作成者がそのような数で意図的に作成した可能性は低いです。 はい、PascalABC.NETプロジェクトでの例外処理は明らかに一律ではありません。 それにもかかわらず、マイナーセクションまたはアナライザの誤検知でさえ、同じタイプのこれらすべての634の警告の場所。



ところで、これはSonarC#とPVS-Studioアナライザーの違いの良い例です。 SonarC#はコード内の「臭い」を指し、これらの警告を発行するのに絶対に正しい。 プロジェクトの品質を判断できます。 PVS-Studioアナライザの開発者である私たちの観点からは、これらは誤検知です。エラーとセキュリティの欠陥の検索に焦点を合わせているからです。



デッドストアは削除する必要があります



また、変数が割り当て間で使用されない場合、変数の再割り当てに関する618個の警告の非常に大きなグループがあります。 ここでは次のパターンが優先されます。



写真12







変数は宣言中に初期化され、保存された値を使用したことがないため、新しい値が割り当てられます。 もちろん、これを行うべきではありません。 リソースの節約についての質問と、別の間違いやタイプミスの疑いがあります。 しかし実際には、これらの構造はいずれも間違いではありません。 繰り返しますが、このような警告がすべて重要度の高いエラーセクションに配置された理由は不明です。 私の意見では、これはすべて誤検知です。



フォームのいくつかの絶対に明確な誤検知警告があります。



写真13







この場合、アナライザの推奨事項に従うと、プログラムのロジックに違反します。



したがって、検討中のグループからの618個の警告の中から、単一の実際のエラーを見つけることはできませんでした。



浮動小数点数の等価性をテストしないでください



151比較されたオペランドの一方または両方が実数型である比較構造に対して警告が発行されました。 実際、このような比較はしばしば誤った結果をもたらしますが、これは実際の変数をメモリに保存する特性に関連しており、たとえばコンパイラの設定によって異なる場合があります。 このような設計は、問題なく非常に長い間機能します。 この場合、それぞれのケースで、そのようなコードの誤りを判断する必要があります。 たとえば、比較された値が数学的計算の結果である場合、これらの値の直接的な比較は通常誤りです。 2つの実定数を比較する場合、おそらくこれは意味のある方法で行われ、エラーは発生しません。



PascalABC.NETコードでは、主に次の実際の変数との比較パターンに遭遇しました。



写真4







比較は2つの実変数と整数型変数を持つ実変数の両方で行われることに注意してください。 もちろん、そのようなコードは完全に安全ではありません。比較された値がどのように取得されたかは不明です。 ここで明らかな間違いについて話す価値はありますか? 明確な答えを出すことは困難です。 ただし、コードにはおそらくいくつかの改良が必要です。



ちなみに、PVS-Studioアナライザーは、このような疑わしい比較についても警告しますが、これらの診断は信頼性レベルが低く、研究のために推奨されていません。



また、アナライザーによって発行される警告には、次のような明らかな誤報があります。



写真15







この場合、 byte型の2つの変数比較されます。 左右の変数はbyte_const_nodeです

public class byte_const_node : concrete_constant<byte>, SemanticTree.IByteConstantNode { public byte_const_node(byte value, location loc) : base(value, loc) { } .... } public abstract class concrete_constant<ConstantType> : constant_node { private ConstantType _constant_value; public concrete_constant(ConstantType value, location loc) : base(compiled_type_node.get_type_node(typeof(ConstantType)), loc) { _constant_value = value; } .... public ConstantType constant_value { get { return _constant_value; } .... } .... } .... }
      
      





この警告グループは、メジャーセクションに合理的に配置されていると思います。 ただし、すべての警告がエラーを検出したとは考えません。 いずれの場合も、コードの作成者が決定を下す必要があります。



複数行のブロックは中括弧で囲む必要があります



プログラム実行ロジックに影響する潜在的なフォーマットエラーを含む、108個の警告のグループ。 ここで私は非常に疑わしい構造を見つけました。 例:



写真16







このフラグメントでは、おそらく括弧が欠落しています。 いずれの場合でも、開発者はコードをフォーマットして、プログラムのロジックをよりよく理解する必要があります。



別の同様の警告:



写真17







エラーはありませんが、コードは乱雑に見えます。 リファクタリングが必要です。



原則として、このグループからの警告はすべてケースで発行されましたが、実際のエラーは明らかになりませんでした。



NULLポインターを逆参照しないでください



nullリンクによるアクセスの可能性に関する75の警告。 このブロックでは、興味深いエラーが見つかりました。



写真18







実際、コードの初期段階では、 returned_scope変数は使用前に常にnullがチェックされますが、この場合は忘れていました。

 public override void visit(....) { .... if (returned_scope != null && ....) { .... } else if (returned_scope != null) { .... } returned_scope.declaringUnit = entry_scope; // <= .... }
      
      





別の同様のエラー:



写真19







最初のケースでは、変数piは使用前にnull チェックされますが、次にpi.CompilationUnitを呼び出すときに、これを行うのを忘れます。



警告ブロックには、それほど明白ではないエラーのほか、誤検知が含まれています。 ここで実際のエラーを見つける割合を85%と評価します。 非常に良い結果。



条件は無条件に「true」または「false」に評価されるべきではありません



プログラムのロジックに関係なく実行可能な条件に関する警告のブロック。 見つかった典型的なエラー:



写真20







著者が改善する必要のある奇妙なコード。 重大な間違いが行われた可能性があります。



一般に、グループにはこのようなエラーの約70%が含まれます。



プロパティゲッターから例外をスローしないでください



プロパティのgetセクションで例外をスローしないでください。必要に応じて、プロパティの代わりにメソッドを使用してください。 このグループには、このような警告が46個含まれています。 それらの大部分は、開発者が意図的にまたは忘却から残した「スタブ」です。



写真21







リファクタリングを必要とする非常に正しい構造もありません:



写真22







ただし、これらの警告はエラーとは見なしません。 それらをマイナーセクションに帰属させる方が合理的だと思います。



コンストラクターで静的フィールドを更新しないでください



コンストラクターで静的フィールドを更新する危険性についての診断:クラスのすべてのインスタンスでフィールドが再初期化されるため、プログラムの動作に一貫性がなくなる可能性があります。 合計で、PascalABC.NETアナライザーはこのような警告を26個生成しました。 私はそれらの間で本当の間違いを見つけませんでした。 以下に、発見されたコードスニペットの例をいくつか示します。



写真23







毎回、クラスの新しいインスタンスへのリンクが_instance静的変数に書き込まれます。 変数の名前から判断すると、それが意図されたものです。



写真24







parsers_loadedフラグは、クラスの少なくとも1つのインスタンスが既に作成されていることを通知します。 犯罪者はいません。



「=」の代わりに「= +」を使用しないでください



興味深い診断は、演算子 "-="の代わりに、たとえば "=-"が誤って使用されたことです。 アナライザーは、9つの警告を発行しました。 残念ながら、それらはすべて誤検知です。 変数宣言である構文に対して6つの警告が発行されました。原則として、演算子「-= "または" + = "を使用することはできません」:



写真25







残りの3つの警告は、コードの作成者がスペースを使用してコードをフォーマットすることを明らかに好まないという事実によるものです。



写真26







関連する「if / else if」ステートメントは同じ条件にしないでください



ifブロックとelse ifブロックの条件が同じコードフラグメントに対して5つの警告が発行されました。 多くの場合、このようなコードはすでにエラーが発生しているか、エラーの可能性が含まれています。 今回のケースでは、5つの警告のうち4つに条件の単純な複製と実行ブロックが含まれていました。これはもちろん疑わしいですが、重大なエラーではありません。 1つの警告はより興味深いものです。



写真27







最初のifブロックの条件の一部がコメント化される前は、次のifブロックの条件とは異なりました。 また、次の場合 、この2番目の実行ユニットに注意してください。空の場合。 演算子は「;」のみです。 非常に奇妙で疑わしいコード。



ブールコンテキストでは短絡ロジックを使用する必要があります



たとえば、診断は、 boolなどの式に&&の代わりに演算子を誤って使用する可能性があることを警告します。 合計5つの疑わしい構造が見つかりました。 エラーが含まれていない場合もありますが、それらはすべて何らかの形で注意が必要です。 それらの1つの例を次に示します。



写真28







この場合、「|」演算子を使用していると断言することはできません 誤ったのは、内部で複雑なロジックを持つプロパティが右側でチェックされるためです。 おそらく、開発者は両方の条件が常にチェックされるように努めました。



例外を明示的に再スローしないでください



例外スタックの損失に関する診断。 アナライザーは、同じタイプの4つの警告を次の形式で発行しました。



写真29







もちろん、これを行うべきではありません。 アプリケーションのさらなるデバッグは困難です。 しかし、これらすべての警告はそれほど重要ではありません。 私の意見では、マイナーセクションでの彼らの位置。



変数を自己割り当てしないでください



変数自体への値の割り当てに関する3つの警告。 見つかったコードフラグメントの1つの例を次に示します。



写真3







奇妙で明らかに誤りのあるコード。 visitNode宣言は次のとおりです。

 protected bool visitNode = true;
      
      





この警告グループには2つのエラーがあります。



二項演算子の両側で同じ式を使用しないでください



診断は、同一の部分式がある条件を検索します。 2つの疑わしい構造が見つかりました。 それらのいずれにも明らかなエラーはありませんが、おそらくコードの見た目と動作が異なるはずです。 警告の1つの例:



写真7







奇妙なコード。 2番目のチェックを置き換えるのを忘れている可能性があります。



「ToString()」メソッドはnullを返さない



メジャーセクションの最後の警告グループ。 ToString()メソッドのオーバーロードは正しく実装されていません。 2つの警告が発行されましたが、どちらもエラーです。 それらの1つの例:



写真14







オーバーロードされたToString()メソッドからnullを返すのは正しくありません string.Emptyを使用する必要があります。



マイナー



ここでは、1822件の警告が発行されました。 このレベルは重要ではないため、ここで本当に興味深いエラーを見つけることはまずありません。 また、通常、このレベルでは多くの誤検知が記録されます。 したがって、この研究ではマイナーレベルの警告は考慮しません。



SonarC#アナライザーのテスト結果



まとめると、一般的に、アナライザーはブロッカー、クリティカル、およびメジャーレベルで実際のエラーを発見したと言えます(1814の警告に対して268のエラーまたは非常に疑わしい構造をカウントしました)。 それにもかかわらず、誤検知の割合は非常に大きく、85%以上に達します。 これにより、結果の分析が大幅に複雑になります。



SonarQube用のPVS-Studioプラグイン



SonarQubeのPVS-Studioアナライザーの統合セクションは、弊社Webサイトのドキュメントセクションで説明されています。 統合をゼロからセットアップするのに約15分かかりました。 プロジェクトをチェックして、結果をローカルのSonarQubeサーバーにアップロードすると、同じ量がかかりました。



PVS-Studioは、PascalABC.NETコードの検証中に1039個の警告を発行しました。 これらのうち、クリティカルレベルの156個の警告、541-メジャーレベルの警告、342-マイナーレベルの警告。



写真31







軽度の警​​告は考慮されません。これは、通常、誤検知の割合が高いためです。



クリティカルレベルの診断アラートの配布:



写真34







メジャーレベルでの診断警告の分布:



写真35







クリティカルおよびメジャーのレベルで697個の警告を分析した結果、そのうち204個が誤検知に起因していることがわかりました。 これは、重要度の第1および第2レベルでの警告の総数の約29%に相当します。 したがって、PascalABC.NETプロジェクトの実際のエラーと疑わしい構造を検出する割合は71%です。 コードの行数(KLOC)に関して、これはKLOCあたり0.66エラーです。 見つかった最も興味深いエラーを見てみましょう。 便宜上、診断ルール番号の昇順のエラーを引用します。



コピーペースト



V3001 「||」の左と右に同一の副次式「token.Kind == openBracketToken」があります。 演算子。 ICSharpCode.SharpDevelop NRefactoryInsightWindowHandler.cs 66



 readonly int eofToken, commaToken, openParensToken, closeParensToken, openBracketToken, closeBracketToken, openBracesToken, closeBracesToken, statementEndToken; public void InitializeOpenedInsightWindow(....) { .... if (token.Kind == openParensToken || token.Kind == openBracketToken || token.Kind == openBracketToken) { // <= bracketCount++; } .... }
      
      





ifブロック条件では、 token.Kind == openBracketTokenの等価性二重チェックされます。 クラスで宣言されたフィールドの中で、 openBracesTokenと非常によく似た名前のフィールドを見つけることができます。 おそらく、このフィールドは条件にありませんでした。 この場合、修正されたコードのバージョンは次のようになります。



 public void InitializeOpenedInsightWindow(....) { .... if (token.Kind == openParensToken || token.Kind == openBracketToken || token.Kind == openBracesToken) { bracketCount++; } .... }
      
      





コード内の同様のエラー:





不注意



V3003 「if(A){...} else if(A){...}」パターンの使用が検出されました。 論理エラーが存在する可能性があります。 行を確認してください:597、631。ParserTools SyntaxTreeComparer.cs 597



 public void CompareInternal(....) { .... if (left is ident) CompareInternal(left as ident, right as ident); .... else if (left is int64_const) CompareInternal(left as int64_const, right as int64_const); .... else if (left is int64_const) CompareInternal(left as int64_const, right as int64_const); .... }
      
      





指定されたコードフラグメントには、実際には約30の類似したチェックが含まれており、そのうち2つは完全に同一です。 おそらくここにはエラーはなく、不注意のためにコードが単純に複製されています。 しかし、開発者によると、チェックの1つは異なって見える可能性があります。 この場合、深刻な論理エラーに対処しています。



同様のエラー:





Copy-Paste v2.0



V3004 「then」ステートメントは「else」ステートメントと同等です。 VisualPascalABCNET CodeCompletionWindow.cs 204



 public void HandleMouseWheel(....) { .... if (System.Windows.Forms.SystemInformation.MouseWheelScrollLines > 0) { newValue = this.vScrollBar.Value - (control.TextEditorProperties.MouseWheelScrollDown ? 1 : -1) * multiplier; } else { newValue = this.vScrollBar.Value - (control.TextEditorProperties.MouseWheelScrollDown ? 1 : -1) * multiplier; } .... }
      
      





ifブロックの両方のブランチには、同一の部分式が含まれています。 この場合、このフラグメントの正しいバージョンについて結論付けることは困難ですが、指定された形式では、コードは期待どおりに機能しません。



コード内の同様のエラー:





これら20個のエラーのうち最初の10個だけを引用しました。



変数はそれ自体に割り当てられます。



V3005 「miGenerateRealization.Visible」変数はそれ自体に割り当てられます。 VisualPascalABCNET OptionsManager.cs 342



 public void UpdateUserOptions() { .... tsViewIntellisensePanel.Visible = tssmIntellisence.Visible = tsGotoDefinition.Visible = tsGotoRealization.Visible = tsFindAllReferences.Visible = miGenerateRealization.Visible = miGenerateRealization.Visible = cmGenerateRealization.Visible = cmsCodeCompletion.Visible = cmFindAllReferences.Visible = cmGotoDefinition.Visible = cmGotoRealization.Visible = UserOptions.AllowCodeCompletion; }
      
      





変数miGenerateRealization.Visibleは、割り当て中に同じ値を2回取得します。 おそらく、余分な割り当てが誤って追加された可能性があります。 ただし、 miGenerateRealization.Visible変数のいずれかではなく、初期化されていない他の変数が存在する場合あります。



別の同様のエラーが見つかりました:



V3005 「visitNode」変数はそれ自体に割り当てられます。 SyntaxVisitors SimplePrettyPrinterVisitor.cs 106



再割り当て



V3008 「codeCompileUnit」変数には、値が連続して2回割り当てられます。 おそらくこれは間違いです。 チェック行:126、124。VisualPascalABCNET CodeDomHostLoader.cs 126



 CodeCompileUnit codeCompileUnit = null; private DesignSurface Designer; .... protected override CodeCompileUnit Parse() { .... CodeCompileUnit ccu = null; DesignSurface ds = new DesignSurface(); .... ccu = cg.GetCodeCompileUnit(idh); .... codeCompileUnit = ccu; Designer = ds; codeCompileUnit = ccu; // <= .... }
      
      





コードから、 codeCompileUnit変数同じ値に再割り当てするための論理的な説明はまったくないことがわかります。



コード内の同様のエラー:





メソッドの結果は常に同じです



V3009このメソッドが常に「false」という同じ値を返すのは奇妙です。 NETGenerator NETGenerator.cs 5434



 private bool BeginOnForNode(IStatementNode value) { //if (value is IForNode) return true; IStatementsListNode stats = value as IStatementsListNode; if (stats == null) return false; if (stats.statements.Length == 0) return false; //if (stats.statements[0] is IForNode) return true; return false; }
      
      





おそらくここでは、リファクタリング時の不注意に対処しています。 以前のコードでは、 trueを返すコードブロックがありました。 ただし、これらはコメント化され、メソッドは、作業の結果に関係なくfalseを返します



コード内の同様のエラー:





不注意



V3010関数 'OrderBy'の戻り値を使用する必要があります。 ICSharpCode.SharpDevelop RefactoringService.cs 86



 static IEnumerable<ITreeNode<IClass>> FindDerivedClassesTree(....) { .... var result = new List<TreeNode<IClass>>(); .... result.OrderBy(node => node.Content.FullyQualifiedName); // <= return result; }
      
      





結果リストのソート結果はどこにも保存されません。 指定されたフラグメントの修正バージョン:



 static IEnumerable<ITreeNode<IClass>> FindDerivedClassesTree(....) { .... var result = new List<TreeNode<IClass>>(); .... return result.OrderBy(node => node.Content.FullyQualifiedName); }
      
      





そして別のそのようなエラー:



V3010関数「ToString」の戻り値を使用する必要があります。 CodeCompletion SymTable.cs 2145



ロジックの問題



V3018アプリケーションのロジックの検査を検討してください。 「else」キーワードが欠落している可能性があります。 VisualPascalABCNET InsightWindow.cs 145



 public void HandleMouseWheel(MouseEventArgs e) { .... if (e.Delta > 0) { if (control.TextEditorProperties.MouseWheelScrollDown) { CurrentData = (CurrentData + 1) % DataProvider.InsightDataCount; } else { CurrentData = (CurrentData + DataProvider.InsightDataCount - 1) % DataProvider.InsightDataCount; } } if (e.Delta < 0) { // <= if (control.TextEditorProperties.MouseWheelScrollDown) { CurrentData = (CurrentData + DataProvider.InsightDataCount - 1) % DataProvider.InsightDataCount; } else { CurrentData = (CurrentData + 1) % DataProvider.InsightDataCount; } } .... }
      
      





if条件(e.Delta <0)に注意してください。 コードのフォーマット方法とプログラムロジックに基づいて、結論は次のとおりです。おそらくelseキーワードが欠落している可能性があります。 それでも、この設計の特徴について正確な答えを出すことができるのは著者だけです。



as演算子を使用する場合の古典的なエラー



V3019 「as」キーワードを使用した型変換後に、誤った変数がnullと比較される可能性があります。 変数「baseScope」、「this.baseScope」を確認します。 CodeCompletion SymTable.cs 3497



 public TypeScope(...., SymScope baseScope) { .... this.baseScope = baseScope as TypeScope; .... if (baseScope == null) { .... } .... }
      
      





baseScope引数をTypeScope型にキャストした後 誤っnull チェックされるthis.baseScopeフィールド nullではなく、 baseScope引数です。 コードの修正バージョン:



 public TypeScope(...., SymScope baseScope) { .... this.baseScope = baseScope as TypeScope; .... if (this.baseScope == null) { .... } .... }
      
      





コード内の同様のエラー:





不正確なコード



V3022式 't == null'は常にtrueです。 VisualPascalABCNET Debugger.cs 141



 public static Type GetTypeForStatic(string name) { Type t = stand_types[name] as Type; if (t != null) return t; if (t == null) // <= foreach (string s in ns_ht.Keys) { .... } t = PascalABCCompiler.NetHelper.NetHelper.FindType(name); .... }
      
      





エラーはありませんが、プログラムは乱雑に見えます。



コード内の同様の構造:





45以上の警告のうち最初の10個だけを警告しました。



冗長なチェックまたはエラー?



V3030定期的なチェック。 「upperScopeWhereVarsAreCaptured!= Scope」条件は、383行目で既に確認されています。TreeConverter CapturedVariablesSubstitutionClassGenerator.cs 391



 private void VisitCapturedVar(....) { .... if (upperScopeWhereVarsAreCaptured != scope) { .... if (upperScopeWhereVarsAreCaptured != scope) { .... } .... } .... }
      
      





通常、このような構成はエラーではありませんが、チェックの1つに別の条件が含まれている可能性があります。



コード内の同様のエラー:





奇妙なフォーマット



V3033この「else」ブランチは、前の「if」ステートメントに適用する必要がある可能性があります。 TreeConverter syntax_tree_visitor.cs 14894



 public override void visit(....) { .... if (_var_def_statement.inital_value != null) if (is_event) AddError(....); else { .... } .... }
      
      





プログラムのロジックによれば、 elseキーワードはif(is_event)条件ブロックを参照します。 ただし、コードは、まったく異なる印象が作成されるようにフォーマットされています。 括弧{}を使用すると、おそらく問題が解決するでしょう。



タイプミス



V3038 'enum_consts [i]'引数が 'Compare'メソッドに数回渡されました。 代わりに他の引数を渡す必要があります。 CodeCompletion SymTable.cs 2206



 private List<string> enum_consts = new List<string>(); public override bool IsEqual(SymScope ts) { EnumScope es = ts as EnumScope; if (es == null) return false; if (enum_consts.Count != es.enum_consts.Count) return false; for (int i = 0; i < es.enum_consts.Count; i++) if (string.Compare(enum_consts[i], this.enum_consts[i], true) != 0) // <= return false; return true; }
      
      





残念ながら、 IsEqualメソッドにローカル変数enum_constsの宣言が含まれていません。 for enum_consts . IsEqual :



 public override bool IsEqual(SymScope ts) { .... for (int i = 0; i < es.enum_consts.Count; i++) if (string.Compare(enum_consts[i], es.enum_consts[i], true) != 0) .... }
      
      





v2.0



V3043 The code's operational logic does not correspond with its formatting. ステートメントは右側にインデントされますが、常に実行されます。 中括弧が欠落している可能性があります。 VBNETParser LanguageInformation.cs 1002



 public override string FindExpression(....) { .... switch (ch) { .... case '(': if (kav.Count == 0) { .... } else sb.Insert(0, ch); punkt_sym = true; break; } .... }
      
      





punkt_sym = true kav.Count == 0 . , , , kav.Count != 0 .



:



V3043 The code's operational logic does not correspond with its formatting. ステートメントは右側にインデントされますが、常に実行されます。 中括弧が欠落している可能性があります。 ICSharpCode.SharpDevelop AbstractConsolePad.cs 159







V3052 The original exception object 'e' was swallowed. 元の例外のスタックが失われる可能性があります。 NETGenerator NETGenerator.cs 925



 public void ConvertFromTree(....) { .... try { .... } catch (System.Runtime.InteropServices.COMException e) { throw new TreeConverter.SaveAssemblyError(e.Message); } .... }
      
      





COMException . , , SaveAssemblyError , :



 public class SaveAssemblyError : CompilationError { .... public SaveAssemblyError(string text) { _text = text; } .... }
      
      





, — . , , .



:









V3053 An excessive expression. Examine the substrings 'reduction' and 'reduction('. TreeConverter OpenMP.cs 267



 private void ProcessClauses(string Text, ....) { .... if (....) { .... } else if (AllowReduction && (Text.StartsWith("reduction") || Text.StartsWith("reduction("))) { .... } .... }
      
      





«reduction(» , «reduction» .







V3070 Uninitialized variable 'event_add_method_prefix' is used when initializing the 'event_add_method_nameformat' variable. TreeConverter compiler_string_consts.cs 313



 public static class compiler_string_consts { .... public static string event_add_method_nameformat = event_add_method_prefix + "{0}"; .... public static string event_add_method_prefix = "add_"; .... }
      
      





, event_add_method_nameformat "{0}" , «add_{0}» . :



 public static class compiler_string_consts { .... public static string event_add_method_prefix = "add_"; .... public static string event_add_method_nameformat = event_add_method_prefix + "{0}"; .... }
      
      





:



V3070 Uninitialized variable 'event_remove_method_prefix' is used when initializing the 'event_remove_method_nameformat' variable. TreeConverter compiler_string_consts.cs 314



:



V3080 nullの逆参照の可能性。 Consider inspecting 'tc'. CodeCompletion CodeCompletionPCUReader.cs 736



 private TypeScope GetTemplateInstance() { TypeScope tc = null;//GetTemplateClassReference(); int params_count = br.ReadInt32(); for (int i = 0; i < params_count; i++) { tc.AddGenericInstanciation(GetTypeReference()); // <= } return tc; }
      
      





, tc GetTemplateClassReference() . , — null . , for . , , GetTemplateInstance() . , .



:





:



V3095 The 'VisualEnvironmentCompiler.RemoteCompiler' object was used before it was verified against null. Check lines: 52, 54. CompilerController CompilerControllerPlugin.cs 52



 public CompilerController_VisualPascalABCPlugin(....) { .... VisualEnvironmentCompiler.RemoteCompiler.InternalDebug.RunOnMono = CompilerInformation.cbRunMono.Checked; .... if (VisualEnvironmentCompiler.RemoteCompiler != null) .... }
      
      





null . :



 public CompilerController_VisualPascalABCPlugin(....) { .... if (VisualEnvironmentCompiler.RemoteCompiler != null) { VisualEnvironmentCompiler.RemoteCompiler. InternalDebug.RunOnMono = CompilerInformation.cbRunMono.Checked; .... } }
      
      





:





10 40.



: x2



V3110 Possible infinite recursion inside 'SetRange' method. TreeConverter SymbolInfoArrayList.cs 439



V3110 Possible infinite recursion inside 'SetRange' method. TreeConverter SymbolInfoArrayList.cs 444



 public void SetRange(int index,SymbolInfo[] tnarr) { SetRange(index,tnarr); } public void SetRange(int index,SymbolInfoArrayList tnarl) { SetRange(index,tnarl); }
      
      





, . . . .



:





Equals



V3115 Passing 'null' to 'Equals' method should not result in 'NullReferenceException'. ICSharpCode.SharpDevelop ServiceReferenceMapFile.cs 31



 public override bool Equals(object obj) { var rhs = obj as ServiceReferenceMapFile; return FileName == rhs.FileName; // <= }
      
      





. null rhs . , null obj :



 public override bool Equals(object obj) { if (obj == null || !(obj is ServiceReferenceMapFile)) return false; var rhs = obj as ServiceReferenceMapFile; return FileName == rhs.FileName; }
      
      









V3125 The 'resources' object was used after it was verified against null. Check lines: 215, 211. VisualPascalABCNET DesignerResourceService.cs 215



 public System.Resources.IResourceReader GetResourceReader(System.Globalization.CultureInfo info) { .... if (resources != null && resources.ContainsKey(info.Name)) { resourceStorage = resources[info.Name]; } else { resourceStorage = new ResourceStorage(); resources[info.Name] = resourceStorage; // <= } .... }
      
      





null resources , , else . . :



 public System.Resources.IResourceReader GetResourceReader(System.Globalization.CultureInfo info) { .... if (resources != null) { if (resources.ContainsKey(info.Name)) { resourceStorage = resources[info.Name]; } else { resourceStorage = new ResourceStorage(); resources[info.Name] = resourceStorage; } } .... }
      
      





:





10 80 (!).







V3128 The 'dockPanel' field is used before it is initialized in constructor. ICSharpCode.SharpDevelop SearchResultsPad.cs 49



 .... DockPanel dockPanel; .... public SearchResultsPad() { .... defaultToolbarItems = ToolBarService. CreateToolBarItems(dockPanel, ....); // <= foreach (object toolBarItem in defaultToolbarItems) { toolBar.Items.Add(toolBarItem); } .... dockPanel = new DockPanel { Children = { toolBar, contentPlaceholder } }; .... }
      
      





dockPanel SearchResultsPad , . , CreateToolBarItems null , , , null. , , defaultToolbarItems .



統計



. SonarC# PVS-Studio . SonarC# . , «» . PVS-Studio , . , , :





PascalABC.NET ( Blocker, Critical Major):



写真6







, . SonarC# , , . . , PVS-Studio . , .



おわりに



, , PVS-Studio SonarC# SonarQube . . SonarQube . , .



SonarQube .



PVS-Studio SonarQube . — Enterprise . SonarQube, PVS-Studio .



PVS-Studioをダウンロードして試してください: http : //www.viva64.com/en/pvs-studio/



PVS-Studio . デモバージョンの制限を削除する場合は、PVS-Studioの包括的な研究の一時的なライセンスを取得するために私たちに書き込むこともできます。





この記事を英語圏の聴衆と共有したい場合は、翻訳へのリンクを使用してください:Sergey Khrenov。 Analysis of PascalABC.NET using SonarQube plugins: SonarC# and PVS-Studio



記事を読んで質問がありますか?
多くの場合、記事には同じ質問が寄せられます。 ここで回答を収集しました: PVS-Studioバージョン2015に関する記事の読者からの質問への回答 。 リストをご覧ください。



All Articles